Fonctions de documentation des bases de données PostgreSQL. Deuxième partie

Il s'agit de la deuxième partie de l'article qui décrit les fonctions définies par l'utilisateur pour travailler avec les répertoires système: pg_class, pg_attribute, pg_constraints, etc.

Cette partie de l'article traite des fonctions qui renvoient les caractéristiques des contraintes et des index .

La première moitié de l'article fournit des commentaires sur la mise en œuvre des fonctions. Le second est le code source des fonctions. Pour les lecteurs qui ne sont intéressés que par les textes sources, nous suggérons de passer immédiatement à l' annexe .

Voir aussi
Fonctions de documentation des bases de données PostgreSQL. Première partie ;
Fonctions de documentation des bases de données PostgreSQL. Troisième partie .
Fonctions de documentation des bases de données PostgreSQL. La fin (quatrième partie) .

admtf_Table_Constraintes liste des restrictions de table de base de données et leurs caractéristiques



La fonction admtf_Table_Constraintes renvoie une liste des contraintes (CONSTRAINT) de la table de base de données et leurs caractéristiques. Le code source peut être consulté et téléchargé ici , et voici une version d'une fonction qui n'utilise pas de curseur .



En tant que paramètres, la fonction prend le nom de la table source ( a_TableName ) et le nom du schéma dans lequel la table est créée ( a_SchemaName ).

La description d'une contrainte particulière est une combinaison de l'enregistrement dans pg_class la décrivant comme une relation physique et l'enregistrement dans pg_constraint contenant des données sur les caractéristiques spécifiques de la contrainte.




le code source de l'opérateur sur la figure
SELECT tbl.OID,con.conname,con.contype,con.conkey,reftbl.OID, reftbl.relname,con.confkey,con.consrc FROM pg_constraint con INNER JOIN pg_namespace nsp ON con.connamespace=nsp.oid LEFT OUTER JOIN pg_class tbl ON con.conrelid=tbl.oid LEFT OUTER JOIN pg_class reftbl ON con.confrelid=reftbl.oid WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableOID) ORDER BY con.contype DESC,con.conname; 


Les données principales (nom et type de restriction) sont extraites de l' entrée du répertoire pg_constraint . Les caractéristiques de chaque contrainte sont extraites du même catalogue, qui sont présentées sous forme de tables OID ( conrelid , confrelid ) ou de tableaux de numéros de série d'attributs ( conkey , confkey ) participant à la contrainte.



La fonction renvoie des caractéristiques de contrainte sous la forme de noms et d'attributs de table. Dans ce cas, les noms de table sont extraits de l' entrée de catalogue pg_class par identifiant (OID), et les noms d' attribut des entrées de catalogue pg_attribute par l'identifiant de table et le numéro de série d'attribut. Parce que Étant donné que les numéros de série sont stockés dans le répertoire principal sous la forme d'un tableau (liste), des listes de noms d'attributs sont formées à l'intérieur de la fonction à l'aide d'une boucle.


La fonction renvoie une caractéristique spéciale - la règle de vérification des valeurs des champs dans les entrées de table (restriction CHECK). Cette caractéristique est stockée sous forme de valeur de texte dans le champ consrc du répertoire pg_constraint .


Tableau 7. Résultat de l'exécution de la fonction admtf_Table_Constraintes ('public', 'Street').

La version texte du tableau de la figure
Le titreTapezAttributs de table sourceLe nom de la table externeAttributs d'une table externeRègle de validation
xpkstreetpwcrccode, localityid, streetid
fk_street_localityfwcrccode, localityidlocalitéwcrccode, localityid
fk_street_streettypefstreettypeacrmstreettypestreettypeacrm
ck_street_streetnamecnom de rue((nom de rue) :: text! ~ * '[az]' :: text)
ck_street_streettypeacrmcstreettypeacrm((streettypeacrm) :: bpchar! ~ * '[az]' :: texte)



Version sans curseur


Je prévois des questions et commentaires sur l'utilisation du curseur dans la version principale de la fonction.


Je ne répondrai pas - il n'y a pas de camarades pour le goût et la couleur. Mais je vais donner une version de la fonction sans curseur. La version de l'implémentation de la fonction sans utiliser le curseur peut être consultée et téléchargée ici .


La principale difficulté est d'organiser la jointure (JOIN) des tables par les valeurs situées dans le type d'attribut du tableau de l'une d'entre elles. Dans ce cas, ces tableaux sont conkey et confkey .



 SELECT c.conname,c.contype,c.conkey::SMALLINT[], GENERATE_SUBSCRIPTS(c.conkey, 1) as No FROM pg_constraint c WHERE c.conname='fk_street_locality' ORDER BY No; 

Pour résoudre un tel problème, PostgrSQL contient des fonctions qui renvoient une table de valeurs de pointeurs vers des éléments de tableau. Dans notre cas, la fonction generate_subscripts sera utilisée. Non seulement il génère de nombreux pointeurs vers la position du tableau qui lui est transmise en tant que paramètre, mais il transforme également un enregistrement contenant le tableau en plusieurs par le nombre d'éléments du tableau. Chaque enregistrement d'une telle table contient une valeur unique - la position du tableau.



Tableau 8. Propagation de la chaîne d'origine à l'aide de generate_subscripts .

Nom de restrictionTapezTableau de numéros d'attributPointeur vers une position de tableau
fk_street_localityf{1,2}1
fk_street_localityf{1,2}2



le code source de l'opérateur sur la figure
 SELECT con.conname AS ConstraintName,con.contype::VARCHAR(2) AS ConstraintType, STRING_AGG(attr.attname, ', 'ORDER BY con.No) AS r_ConstraintKeyNames, reftbl.relname AS RefTableName, STRING_AGG(rattr.attname,', 'ORDER BY con.No) AS r_RefTableKeyNames, con.consrc AS ConstraintSource FROM (SELECT c.oid, c.conrelid,c.confrelid,c.conname,c.contype, c.conkey::SMALLINT[],c.consrc, c.confkey::SMALLINT[], generate_subscripts(c.conkey, 1) as No FROM pg_constraint c) con INNER JOIN pg_class tbl ON con.conrelid=tbl.oid INNER JOIN pg_attribute attr ON attr.attrelid=tbl.oid AND attr.attnum=con.conkey[con.No] INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid LEFT OUTER JOIN pg_class reftbl ON con.confrelid=reftbl.oid LEFT OUTER JOIN pg_attribute rattr ON rattr.attrelid=reftbl.oid AND rattr.attnum=con.confkey[con.No] WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableName) GROUP BY con.conname,con.contype,reftbl.relname,con.consrc ORDER BY con.contype DESC,con.conname; 


Une telle table peut être connectée au catalogue d' attributs pg_attribute en en extrayant les noms d'attribut sous la condition attr.attrelid = tbl.oid AND attr.attnum = con.conkey [con.No] .
Il reste maintenant à supprimer les enregistrements inutiles en regroupant les enregistrements et à créer une chaîne à partir des noms des attributs.


La création d'une ligne s'effectue à l'aide de la fonction d'agrégation STRING_AGG , dans laquelle vous devez spécifier l'option de tri (ORDER BY), sinon l'ordre des attributs peut ne pas être conforme à l'ordre de déclaration des attributs dans l'index.



Le temps d'exécution des deux versions des fonctions a coïncidé. Il a fallu 20 ms pour sortir les données dans le tableau des résultats.


Admtf_Table_Indexes, fonction liste des index de table de base de données et leurs caractéristiques



La fonction admtf_Table_Indexes renvoie une liste d'index (INDEX) de la table de base de données et leurs caractéristiques. Le code source peut être consulté et téléchargé ici , et voici une version d'une fonction qui n'utilise pas de curseur .


En tant que paramètres, la fonction prend le nom de la table source ( a_TableName ) et le nom du schéma dans lequel la table est créée ( a_SchemaName ).

le code source de l'opérateur sur la figure
 SELECT tbl.oid,inxcls.relname,inxam.amname,inx.indisunique,inx.indisprimary, inx.indkey::SMALLINT[],inx.indoption::SMALLINT[],inxam.amcanorder FROM pg_index inx INNER JOIN pg_class inxcls ON inx.indexrelid=inxcls.oid INNER JOIN pg_namespace inxnsp ON inxcls.relnamespace=inxnsp.oid INNER JOIN pg_am inxam ON inxcls.relam=inxam.oid INNER JOIN pg_class tbl ON inx.indrelid=tbl.oid INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableOID) ORDER BY inxam.amname, inxcls.relname; 


La description d'un seul index est une combinaison d'un enregistrement dans pg_class le décrivant comme une relation physique et d'un enregistrement dans pg_index contenant des données sur les caractéristiques spécifiques de l'index. De plus, les informations sur les méthodes d'accès aux index sont stockées dans le répertoire système pg_am.


 CASE inxam.amcanorder WHEN true THEN CASE inx.indoption[inx.No] & 1 WHEN 1 THEN ' DESC' ELSE ' ASC' END ELSE '' END; 

Un attribut d'unicité d'index ( indisunique ), un signe que l'index est construit conformément à la description de la clé primaire ( indisprimary ), ainsi que des tableaux de numéros de série des attributs de table, basés sur les valeurs dont l'index ( indkey ) et les signes de l'ordre de tri des valeurs d'attribut sont extraits de l' entrée de catalogue pg_index, sont extraits dans l'index ( indoption ).


À partir de l'entrée de catalogue décrivant la méthode d'accès de l'index pg_am , l'attribut de l'adéquation des données triées incluses dans l'index ( amcanorder ) et le nom ou le type de la méthode d'accès à l'index ( amname ) sont extraits .

En d'autres termes, l'attribut amcanorder indique s'il est possible d'établir un ordre de tri pour les valeurs des attributs inclus dans l'index. Si amcanorder = true , l'ordre de tri peut être spécifié, sinon non. Sur la même figure, la signification des valeurs du tableau d' indoption est visible - si le bit droit de la forme binaire de la valeur contient 1B, alors la valeur de l'attribut correspondant est triée par ordre décroissant, sinon - par ordre croissant.


Des listes de noms d'attributs inclus dans l'index, ainsi que des signes de classement des valeurs d'attribut, sont formées à l'intérieur de la fonction à l'aide d'un cycle.



Tableau 9. Résultat de l'exécution de la fonction admtf_Table_Indexes ('public', 'Street').


La version texte du tableau de la figure
Nom d'indexLa méthode? Unique? clé primaireAttributs dans l'index
xie1streetbtreeffwcrccode ASC, localityid ASC, streettypeacrm ASC, streetname ASC
xie2streebtreeffwcrccode ASC, localityid ASC, streetname ASC
xie3streetbtreeffnom de rue ASC
xie9streetbtreeffwcrccode ASC, localityid ASC, nom de rue DESC
xpkstreetbtreettwcrccode ASC, localityid ASC, streetid ASC
xts1streetGinffstreettsvector
xts2streetGinffstreettsvector


Version sans curseur


L'approche pour créer une version d'une fonction sans curseur est exactement la même que celle décrite dans la section précédente:


  • reproduction d'enregistrements à l'aide de generate_subscripts;
  • regroupement ultérieur des enregistrements;
  • Création d'une liste d'attributs d'index à l'aide de la fonction STRING_AGG avec l'option ORDER BY.


le code source de l'opérateur sur la figure
 SELECT inxcls.relname AS r_IndexName ,inxam.amname AS r_IndexType, inx.indisunique AS r_isUnique,inx.indisprimary AS r_isPrimary, STRING_AGG(attr.attname|| CASE inxam.amcanorder WHEN true THEN CASE inx.indoption[inx.No] & 1 WHEN 1 THEN ' DESC' ELSE ' ASC' END ELSE '' END, c_Delimiter ORDER BY inx.No) FROM (SELECT i.indrelid, i.indexrelid,i.indisunique,i.indisprimary, i.indkey::SMALLINT[],i.indoption::SMALLINT[], generate_subscripts(i.indkey, 1) as No FROM pg_index i) inx INNER JOIN pg_class inxcls ON inx.indexrelid=inxcls.oid INNER JOIN pg_am inxam ON inxcls.relam=inxam.oid INNER JOIN pg_class tbl ON inx.indrelid=tbl.oid INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid INNER JOIN pg_attribute attr ON attr.attrelid=tbl.OID AND attr.attnum=inx.indkey[inx.No] WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableName) GROUP BY inxcls.relname,inxam.amname,inx.indisunique,inx.indisprimary ORDER BY inxcls.relname; 



Le temps d'exécution des deux versions des fonctions a coïncidé, il m'a fallu 20 ms pour sortir les données dans le tableau de résultats.


Par conséquent, je ne produirai plus de versions de fonctions, comme ceux qui le souhaitent peuvent les refaire à leur guise ou me contacter, j'enverrai gratuitement une version modifiée .

Voir également les première , troisième et quatrième parties de l'article.



ANNEXE 1. Scripts



Création de la fonction admtf_Table_Constraintes


Vous trouverez des commentaires sur le code source de la fonction ici.
code source de la fonction
 BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Constraintes (a_SchemaName NAME, a_TableName NAME); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Constraintes (a_SchemaName name default 'public', /*     */ a_TableName name default NULL /*   */ ) RETURNS TABLE (r_ConstraintName NAME,r_ConstraintType NAME,r_ConstraintKeyNames Text,r_RefTableName NAME,r_RefTableKeyNames Text,r_ConstraintSource Text) AS $BODY$ DECLARE v_Scale INTEGER; /*   */ v_ConstraintRec RECORD; /*   */ v_TableOID INTEGER; /* OID */ v_ConstraintOID INTEGER; /* OID */ v_ConstraintKeyNos SMALLINT[]; /* */ v_ConstraintName name; /*   */ v_ConstraintType name; /*     */ v_isUnique BOOLEAN; /*   */ v_isPrimary BOOLEAN; /*      Primary KEY */ v_AttributeNum INTEGER; /*   */ v_AttributeName name; /*  */ v_ConstraintKeyNames TEXT; /*     */ v_RefTableOID INTEGER; /* OID ,     */ v_RefTableName name;/*  ,     */ v_RefTableKeyNos SMALLINT[]; /* */ v_RefTableKeyNames TEXT; /*     ,    */ v_ConstraintSource TEXT; /*     CHECK*/ c_Delimiter CONSTANT VARCHAR(2):=','; --****************************************************************************************************** BEGIN FOR v_ConstraintRec IN SELECT con.oid AS ConstraintOID ,tbl.OID AS TableOID, con.conname AS ConstraintName, con.contype AS ConstraintType, con.conkey AS ConstraintKeyNos, reftbl.OID AS RefTableOID, reftbl.relname AS RefTableName, con.confkey AS RefTableKeyNos, con.consrc AS ConstraintSource FROM pg_constraint con INNER JOIN pg_class tbl ON con.conrelid=tbl.oid INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid LEFT OUTER JOIN pg_class reftbl ON con.confrelid=reftbl.oid WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableName) ORDER BY con.contype DESC,con.conname LOOP v_ConstraintOID:=v_ConstraintRec.ConstraintOID; v_TableOID:=v_ConstraintRec.TableOID; v_ConstraintName:=v_ConstraintRec.ConstraintName; v_ConstraintType:=v_ConstraintRec.ConstraintType; v_ConstraintKeyNos:=v_ConstraintRec.ConstraintKeyNos; v_RefTableOID:=v_ConstraintRec.RefTableOID; v_RefTableName:=v_ConstraintRec.RefTableName; v_RefTableKeyNos:=v_ConstraintRec.RefTableKeyNos; v_ConstraintSource:=v_ConstraintRec.ConstraintSource; v_ConstraintKeyNames:=''; FOREACH v_AttributeNum IN ARRAY v_ConstraintKeyNos LOOP SELECT INTO v_AttributeName attr.attname::VARCHAR(100) AS r_AttributeName FROM pg_attribute attr WHERE attr.attrelid=v_TableOID AND attr.attnum=v_AttributeNum; v_ConstraintKeyNames:=v_ConstraintKeyNames|| CASE WHEN v_ConstraintKeyNames='' THEN '' ELSE c_Delimiter END ||v_AttributeName; END LOOP; v_RefTableKeyNames:=''; IF v_RefTableKeyNos IS NOT NULL THEN FOREACH v_AttributeNum IN ARRAY v_RefTableKeyNos LOOP SELECT INTO v_AttributeName attr.attname::VARCHAR(100) AS r_AttributeName FROM pg_attribute attr WHERE attr.attrelid=v_RefTableOID AND attr.attnum=v_AttributeNum; v_RefTableKeyNames:=v_RefTableKeyNames|| CASE WHEN v_RefTableKeyNames='' THEN '' ELSE c_Delimiter END ||v_AttributeName; END LOOP; END IF; RETURN QUERY SELECT v_ConstraintName,v_ConstraintType,v_ConstraintKeyNames, v_RefTableName,v_RefTableKeyNames, v_ConstraintSource; END LOOP; RETURN; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Constraintes(a_SchemaName NAME, a_TableName NAME) IS '    '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Constraintes (a_SchemaName VARCHAR(256), a_TableName VARCHAR(256)); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Constraintes (a_SchemaName VARCHAR(256) default 'public', /*     */ a_TableName VARCHAR(256) default NULL /*   */ ) RETURNS TABLE (r_ConstraintName VARCHAR(256),r_ConstraintType VARCHAR(256),r_ConstraintKeyNames Text,r_RefTableName VARCHAR(256),r_RefTableKeyNames Text,r_ConstraintSource Text) AS $BODY$ DECLARE --****************************************************************************************************** BEGIN RETURN QUERY SELECT tc.r_ConstraintName::VARCHAR(256), tc.r_ConstraintType::VARCHAR(256),tc.r_ConstraintKeyNames::TEXT, tc.r_RefTableName::VARCHAR(256),tc.r_RefTableKeyNames::TEXT, tc.r_ConstraintSource::TEXT FROM admtf_Table_Constraintes(a_SchemaName::NAME,a_TableName::NAME) tc; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Constraintes(a_SchemaName VARCHAR(256), a_TableName VARCHAR(256)) IS '    '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; SELECT * FROM admtf_Table_Constraintes('public'::VARCHAR(256),'Street'::VARCHAR(256)); SELECT * FROM admtf_Table_Constraintes('public'::name,'Street'::name); 



Création d'une version d'admtf_Table_Constraintes sans curseur


Vous trouverez des commentaires sur le code source de la fonction ici.
code source de la fonction
 BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Constraintes (a_SchemaName NAME, a_TableName NAME); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Constraintes (a_SchemaName name default 'public', /*     */ a_TableName name default NULL /*   */ ) RETURNS TABLE (r_ConstraintName NAME,r_ConstraintType NAME,r_ConstraintKeyNames Text,r_RefTableName NAME,r_RefTableKeyNames Text,r_ConstraintSource Text) AS $BODY$ DECLARE v_Scale INTEGER; /*   */ v_ConstraintRec RECORD; /*   */ v_TableOID INTEGER; /* OID */ v_ConstraintOID INTEGER; /* OID */ v_ConstraintKeyNos SMALLINT[]; /* */ v_ConstraintName name; /*   */ v_ConstraintType name; /*     */ v_isUnique BOOLEAN; /*   */ v_isPrimary BOOLEAN;/*      Primary KEY */ v_AttributeNum INTEGER; /*   */ v_AttributeName name; /*  */ v_ConstraintKeyNames TEXT; /*     */ v_RefTableOID INTEGER; /* OID ,     */ v_RefTableName name;/*  ,     */ v_RefTableKeyNos SMALLINT[]; /* */ v_RefTableKeyNames TEXT;/*     ,    */ v_ConstraintSource TEXT; /*     CHECK*/ c_Delimiter CONSTANT VARCHAR(2):=','; --****************************************************************************************************** BEGIN FOR v_ConstraintRec IN SELECT con.oid AS ConstraintOID , tbl.OID AS TableOID, con.conname AS ConstraintName, con.contype AS ConstraintType, con.conkey AS ConstraintKeyNos, reftbl.OID AS RefTableOID, reftbl.relname AS RefTableName, con.confkey AS RefTableKeyNos, con.consrc AS ConstraintSource FROM pg_constraint con INNER JOIN pg_class tbl ON con.conrelid=tbl.oid INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid LEFT OUTER JOIN pg_class reftbl ON con.confrelid=reftbl.oid WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableName) ORDER BY con.contype DESC,con.conname LOOP v_ConstraintOID:=v_ConstraintRec.ConstraintOID; v_TableOID:=v_ConstraintRec.TableOID; v_ConstraintName:=v_ConstraintRec.ConstraintName; v_ConstraintType:=v_ConstraintRec.ConstraintType; v_ConstraintKeyNos:=v_ConstraintRec.ConstraintKeyNos; v_RefTableOID:=v_ConstraintRec.RefTableOID; v_RefTableName:=v_ConstraintRec.RefTableName; v_RefTableKeyNos:=v_ConstraintRec.RefTableKeyNos; v_ConstraintSource:=v_ConstraintRec.ConstraintSource; v_ConstraintKeyNames:=''; FOREACH v_AttributeNum IN ARRAY v_ConstraintKeyNos LOOP SELECT INTO v_AttributeName attr.attname::VARCHAR(100) AS r_AttributeName FROM pg_attribute attr WHERE attr.attrelid=v_TableOID AND attr.attnum=v_AttributeNum; v_ConstraintKeyNames:=v_ConstraintKeyNames|| CASE WHEN v_ConstraintKeyNames='' THEN '' ELSE c_Delimiter END ||v_AttributeName; END LOOP; v_RefTableKeyNames:=''; IF v_RefTableKeyNos IS NOT NULL THEN FOREACH v_AttributeNum IN ARRAY v_RefTableKeyNos LOOP SELECT INTO v_AttributeName attr.attname::VARCHAR(100) AS r_AttributeName FROM pg_attribute attr WHERE attr.attrelid=v_RefTableOID AND attr.attnum=v_AttributeNum; v_RefTableKeyNames:=v_RefTableKeyNames|| CASE WHEN v_RefTableKeyNames='' THEN '' ELSE c_Delimiter END ||v_AttributeName; END LOOP; END IF; RETURN QUERY SELECT v_ConstraintName,v_ConstraintType,v_ConstraintKeyNames, v_RefTableName,v_RefTableKeyNames, v_ConstraintSource; END LOOP; RETURN; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Constraintes(a_SchemaName NAME, a_TableName NAME) IS '    '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Constraintes (a_SchemaName VARCHAR(256), a_TableName VARCHAR(256)); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Constraintes (a_SchemaName VARCHAR(256) default 'public', /*     */ a_TableName VARCHAR(256) default NULL /*   */ ) RETURNS TABLE (r_ConstraintName VARCHAR(256),r_ConstraintType VARCHAR(256),r_ConstraintKeyNames Text,r_RefTableName VARCHAR(256),r_RefTableKeyNames Text,r_ConstraintSource Text) AS $BODY$ DECLARE --****************************************************************************************************** BEGIN RETURN QUERY SELECT tc.r_ConstraintName::VARCHAR(256), tc.r_ConstraintType::VARCHAR(256), tc.r_ConstraintKeyNames::TEXT, tc.r_RefTableName::VARCHAR(256), tc.r_RefTableKeyNames::TEXT, tc.r_ConstraintSource::TEXT FROM admtf_Table_Constraintes(a_SchemaName::NAME,a_TableName::NAME) tc; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Constraintes(a_SchemaName VARCHAR(256), a_TableName VARCHAR(256)) IS '    '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; SELECT * FROM admtf_Table_Constraintes('public'::VARCHAR(256),'Street'::VARCHAR(256)); SELECT * FROM admtf_Table_Constraintes('public'::name,'Street'::name); 



Création de la fonction admtf_Table_Indexes


Vous trouverez des commentaires sur le code source de la fonction ici.
code source de la fonction
 BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Indexes (a_SchemaName NAME, a_TableName NAME); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Indexes (a_SchemaName NAME default 'public', /*     */ a_TableName NAME default NULL /*   */ ) RETURNS TABLE (r_IndexName NAME,r_IndexType NAME,r_isUnique BOOLEAN,r_isPrimary BOOLEAN, r_IndexKeyNames Text) AS $BODY$ DECLARE c_IndexKind CONSTANT CHAR:='i'; v_IndexRec RECORD; /*   */ v_Scale INTEGER; /*   */ v_TableOID INTEGER; /* OID */ v_IndexOID INTEGER; /* OID */ v_IndexKeyNos SMALLINT[]; /* */ v_IndexName NAME; /*   */ v_IndexAMName NAME; /*    ( ) */ v_isUnique BOOLEAN; /*   */ v_isPrimary BOOLEAN; /*      Primary KEY */ v_AttributeNum INTEGER; /*   */ v_AttributeName NAME; /*  */ v_IndexKeyNames TEXT; /*     */ c_Delimiter CONSTANT VARCHAR(2):=','; --****************************************************************************************************** BEGIN FOR v_IndexRec IN SELECT inxcls.oid AS IndexOID,tbl.oid AS TableOID, inxcls.relname AS IndexName,inxam.amname AS IndexAMName, inx.indisunique AS isUnique,inx.indisprimary isPrimary, inx.indkey::SMALLINT[] AS IndexKeyNos FROM pg_index inx INNER JOIN pg_class inxcls ON inx.indexrelid=inxcls.oid INNER JOIN pg_am inxam ON inxcls.relam=inxam.oid INNER JOIN pg_class tbl ON inx.indrelid=tbl.oid INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND inxcls.relkind=c_IndexKind AND tbl.relname=LOWER(a_TableName) ORDER BY inxam.amname, inxcls.relname LOOP v_IndexOID:=v_IndexRec.IndexOID; v_TableOID:=v_IndexRec.TableOID; v_IndexName:=v_IndexRec.IndexName; v_IndexAMName:=v_IndexRec.IndexAMName; v_isUnique:=v_IndexRec.isUnique; v_isPrimary:=v_IndexRec.isPrimary; v_IndexKeyNos:=v_IndexRec.IndexKeyNos; v_IndexKeyNames:=''; FOREACH v_AttributeNum IN ARRAY v_IndexKeyNos LOOP SELECT INTO v_AttributeName attr.attname::VARCHAR(100) AS r_AttributeName FROM pg_attribute attr WHERE attr.attrelid=v_TableOID AND attr.attnum=v_AttributeNum; v_IndexKeyNames:=v_IndexKeyNames|| CASE WHEN v_IndexKeyNames='' THEN '' ELSE c_Delimiter||' ' END || v_AttributeName; END LOOP; RETURN QUERY SELECT v_IndexName,v_IndexAMName,v_isUnique, v_isPrimary,v_IndexKeyNames; END LOOP; RETURN; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Indexes(a_SchemaName NAME, a_TableName NAME) IS '    '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Indexes (a_SchemaName VARCHAR(256), a_TableName VARCHAR(256)); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Indexes (a_SchemaName VARCHAR(256) default 'public', /*     */ a_TableName VARCHAR(256) default NULL /*   */ ) RETURNS TABLE (r_IndexName VARCHAR(256),r_IndexType VARCHAR(256),r_isUnique BOOLEAN,r_isPrimary BOOLEAN, r_IndexKeyNames TEXT) AS $BODY$ DECLARE --****************************************************************************************************** BEGIN RETURN QUERY SELECT ti.r_IndexName::VARCHAR(256), ti.r_IndexType::VARCHAR(256), ti.r_isUnique::BOOLEAN, ti.r_isPrimary::BOOLEAN, ti.r_IndexKeyNames::TEXT FROM admtf_Table_Indexes(a_SchemaName::NAME,a_TableName::NAME) ti; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Indexes(a_SchemaName VARCHAR(256), a_TableName VARCHAR(256)) IS '    '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; SELECT * FROM admtf_Table_Indexes('public'::NAME,'Street'::NAME); SELECT * FROM admtf_Table_Indexes('public'::VARCHAR(256),'Street'::VARCHAR(256)); 



Création d'une version d'admtf_Table_Indexes sans curseur


.
 BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Indexes (a_SchemaName NAME, a_TableName NAME); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Indexes (a_SchemaName NAME default 'public', /*     */ a_TableName NAME default NULL /*   */ ) RETURNS TABLE (r_IndexName NAME,r_IndexType NAME,r_isUnique BOOLEAN,r_isPrimary BOOLEAN, r_IndexKeyNames Text) AS $BODY$ DECLARE c_IndexKind CONSTANT CHAR:='i'; c_Delimiter CONSTANT VARCHAR(2):=', '; --****************************************************************************************************** BEGIN RETURN QUERY SELECT inxcls.relname AS r_IndexName, inxam.amname AS r_IndexType, inx.indisunique AS r_isUnique, inx.indisprimary r_isPrimary, STRING_AGG(attr.attname||CASE inxam.amcanorder WHEN true THEN CASE inx.indoption[inx.No] & 1 WHEN 1 THEN ' DESC' ELSE ' ASC' END ELSE '' END, c_Delimiter ORDER BY inx.No) FROM (SELECT i.indrelid, i.indexrelid,i.indisunique, i.indisprimary,i.indkey::SMALLINT[], i.indoption::SMALLINT[], generate_subscripts(i.indkey, 1) as No FROM pg_index i) inx INNER JOIN pg_class inxcls ON inx.indexrelid=inxcls.oid INNER JOIN pg_am inxam ON inxcls.relam=inxam.oid INNER JOIN pg_class tbl ON inx.indrelid=tbl.oid INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid INNER JOIN pg_attribute attr ON attr.attrelid=tbl.OID AND attr.attnum=inx.indkey[inx.No] WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND inxcls.relkind=c_IndexKind AND tbl.relname=LOWER(a_TableName) GROUP BY inxcls.relname,inxam.amname,inx.indisunique,inx.indisprimary ORDER BY inxcls.relname; RETURN; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Indexes(a_SchemaName NAME, a_TableName NAME) IS '    '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Indexes (a_SchemaName VARCHAR(256), a_TableName VARCHAR(256)); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Indexes (a_SchemaName VARCHAR(256) default 'public', /*    */ a_TableName VARCHAR(256) default NULL /*   */ ) RETURNS TABLE (r_IndexName VARCHAR(256),r_IndexType VARCHAR(256),r_isUnique BOOLEAN,r_isPrimary BOOLEAN, r_IndexKeyNames TEXT) AS $BODY$ DECLARE --****************************************************************************************************** BEGIN RETURN QUERY SELECT ti.r_IndexName::VARCHAR(256), ti.r_IndexType::VARCHAR(256), ti.r_isUnique::BOOLEAN, ti.r_isPrimary::BOOLEAN, ti.r_IndexKeyNames::TEXT FROM admtf_Table_Indexes(a_SchemaName::NAME,a_TableName::NAME) ti; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Indexes(a_SchemaName VARCHAR(256), a_TableName VARCHAR(256)) IS '    '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; SELECT * FROM admtf_Table_Indexes('public'::NAME,'Street'::NAME); SELECT * FROM admtf_Table_Indexes('public'::VARCHAR(256),'Street'::VARCHAR(256)); 


Voir aussi


PostgreSQL. ;
PostgreSQL. .
PostgreSQL. ( ) .

Source: https://habr.com/ru/post/fr415897/


All Articles