Funções para documentar bancos de dados PostgreSQL. Parte dois

Esta é a segunda parte do artigo que descreve funções definidas pelo usuário para trabalhar com diretórios do sistema: pg_class, pg_attribute, pg_constraints etc.

Esta parte do artigo discute funções que retornam as características de restrições e índices .

A primeira metade do artigo fornece comentários sobre a implementação de funções. O segundo é o código fonte das funções. Para os leitores que estão interessados ​​apenas nos textos originais, sugerimos seguir imediatamente para o Apêndice .

Veja também
Funções para documentar bancos de dados PostgreSQL. Parte um ;
Funções para documentar bancos de dados PostgreSQL. Parte três .
Funções para documentar bancos de dados PostgreSQL. O fim (parte quatro) .

admtf_Table_Constraintes lista de restrições da tabela do banco de dados e suas características



A função admtf_Table_Constraintes retorna uma lista de restrições (CONSTRAINT) da tabela do banco de dados e suas características. O código-fonte pode ser visualizado e baixado aqui , e aqui está uma versão de uma função que não usa um cursor .



Como parâmetros, a função assume o nome da tabela de origem ( a_TableName ) e o nome do esquema no qual a tabela é criada ( a_SchemaName ).

A descrição de uma restrição específica é uma combinação do registro em pg_class descrevendo-o como uma relação física e o registro em pg_constraint contendo dados sobre as características específicas da restrição.




o código fonte do operador na figura
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; 


Os dados principais (nome e tipo de restrição) são recuperados da entrada do diretório pg_constraint . As características de cada restrição são extraídas do mesmo catálogo, que são apresentadas na forma de tabelas OID ( conrelid , confrelid ) ou matrizes de números de série de atributos ( conkey , confkey ) que participam da restrição.



A função retorna características de restrição na forma de nomes e atributos de tabela. Nesse caso, os nomes da tabela são extraídos da entrada do catálogo pg_class pelo identificador (OID) e os nomes dos atributos das entradas do catálogo pg_attribute pelo identificador da tabela e número de série do atributo. Porque Como os números de série são armazenados no diretório principal na forma de uma matriz (lista), as listas de nomes de atributos são formadas dentro da função usando um loop.


A função retorna uma característica especial - a regra para verificar os valores dos campos nas entradas da tabela (restrição CHECK). Essa característica é armazenada como um valor de texto no campo consrc do diretório pg_constraint .


Tabela 7. O resultado da execução da função admtf_Table_Constraintes ('public', 'Street').

A versão em texto da tabela na figura
TítuloTipoAtributos da tabela de origemO nome da tabela externaAtributos de uma tabela externaRegra de validação
xpkstreetpwcrccode, localityid, streetid
fk_street_localityfwcrccode, localityidlocalidadewcrccode, localityid
fk_street_streettypefstreettypeacrmstreettypestreettypeacrm
ck_street_streetnamecnome da rua((nome da rua) :: texto! ~ * '[az]' :: texto)
ck_street_streettypeacrmcstreettypeacrm((streettypeacrm) :: bpchar! ~ * '[az]' :: texto)



Versão sem cursor


Prevejo perguntas e comentários sobre o uso do cursor na versão principal da função.


Não vou responder - não há camaradas pelo gosto e cor. Mas darei uma versão da função sem um cursor. A versão da implementação da função sem usar o cursor pode ser visualizada e baixada aqui .


A principal dificuldade é organizar a junção (JOIN) das tabelas pelos valores localizados no tipo de atributo da matriz de uma delas. Tais matrizes neste caso são conkey e 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; 

Para resolver esse problema, o PostgrSQL contém funções que retornam uma tabela de valores de ponteiros para elementos da matriz. No nosso caso, a função generate_subscripts será usada. Além de gerar muitos ponteiros para a posição da matriz passada como parâmetro, ele também transforma um registro que contém a matriz em vários pelo número de elementos na matriz. Cada registro dessa tabela contém um valor exclusivo - a posição da matriz.



Tabela 8. Propagando a sequência original usando generate_subscripts .

Nome da restriçãoTipoMatriz de números de atributoPonteiro para uma posição de matriz
fk_street_localityf{1,2}1
fk_street_localityf{1,2}2



o código fonte do operador na figura
 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; 


Essa tabela pode ser conectada ao catálogo de atributos pg_attribute extraindo nomes de atributos sob a condição attr.attrelid = tbl.oid AND attr.attnum = con.conkey [con.No] .
Agora resta remover registros desnecessários agrupando registros e criar uma sequência a partir dos nomes dos atributos.


A criação de uma linha é realizada usando a função de agregação STRING_AGG , na qual você deve especificar a opção de classificação (ORDER BY), caso contrário, a ordem dos atributos pode não estar de acordo com a ordem de declaração dos atributos no índice.



O tempo de execução de ambas as versões das funções coincidiu. Foram necessários 20 ms para gerar os dados na tabela de resultados.


Lista de funções Admtf_Table_Indexes dos índices da tabela do banco de dados e suas características



A função admtf_Table_Indexes retorna uma lista de índices (INDEX) da tabela do banco de dados e suas características. O código-fonte pode ser visualizado e baixado aqui , e aqui está uma versão de uma função que não usa um cursor .


Como parâmetros, a função assume o nome da tabela de origem ( a_TableName ) e o nome do esquema no qual a tabela é criada ( a_SchemaName ).

o código fonte do operador na figura
 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; 


A descrição de um único índice é uma combinação de um registro em pg_class descrevendo-o como uma relação física e um registro em pg_index contendo dados sobre as características específicas do índice. Além disso, informações sobre métodos de acesso ao índice são armazenadas no diretório do sistema pg_am.


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

Um atributo de exclusividade do índice ( indisunique ), um sinal de que o índice é construído de acordo com a descrição da chave primária ( indisprimary ), bem como matrizes de números de série dos atributos da tabela, com base nos valores dos quais o índice ( indkey ) e os sinais da ordem de classificação dos valores dos atributos são extraídos da entrada do catálogo pg_index. no índice ( introdução ).


Na entrada do catálogo que descreve o método de acesso do índice pg_am, são extraídos o atributo de adequação dos dados incluídos no índice para classificação ( amcanorder ) e o nome ou o tipo do método de acesso do índice ( amname ).

Em outras palavras, o atributo amcanorder indica se é possível estabelecer uma ordem de classificação para os valores dos atributos incluídos no índice. Se amcanorder = true , a ordem de classificação pode ser especificada, caso contrário não. Na mesma figura, o significado dos valores da matriz de opções é visível - se o bit direito da forma binária do valor contiver 1B, o valor do atributo correspondente será classificado em ordem decrescente, caso contrário - em ordem crescente.


Listas de nomes de atributos incluídos no índice, bem como sinais da ordem dos valores dos atributos, são formados dentro da função usando um ciclo.



Tabela 9. O resultado da execução da função admtf_Table_Indexes ('public', 'Street').


A versão em texto da tabela na figura
Nome do índiceMétodo? Único? chave primáriaAtributos no índice
xie1streetbtreeffASC wcrccode, ASC localityid, ASC streettypeacrm, ASC nome da rua
xie2streebtreeffASC wcrccode, ASC localityid, nome da rua ASC
xie3streetbtreeffnome da rua ASC
xie9streetbtreeffwcrccode ASC, localityid ASC, nome da rua DESC
xpkstreetbtreettwcrccode ASC, localityid ASC, streetid ASC
xts1streetgimffstreettsvector
xts2streetgimffstreettsvector


Versão sem cursor


A abordagem para criar uma versão de uma função sem cursor é exatamente a mesma descrita na seção anterior:


  • reprodução de registros usando generate_subscripts;
  • agrupamento subsequente de registros;
  • Criando uma lista de atributos de índice usando a função STRING_AGG com a opção ORDER BY.


o código fonte do operador na figura
 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; 



O tempo de execução de ambas as versões das funções coincidiu, levei 20 ms para gerar os dados na tabela de resultados.


Portanto, não vou mais produzir versões de funções, pois quem quiser pode refazê-los ao seu gosto ou entrar em contato comigo, enviarei uma versão modificada gratuitamente .

Veja também a primeira , terceira e quarta partes do artigo.



APÊNDICE 1. Scripts



Criando a função admtf_Table_Constraintes


Comentários sobre o código fonte da função podem ser encontrados aqui.
código fonte da função
 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); 



Criando uma versão do admtf_Table_Constraintes sem um cursor


Comentários sobre o código fonte da função podem ser encontrados aqui.
código fonte da função
 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); 



Criando a função admtf_Table_Indexes


Comentários sobre o código fonte da função podem ser encontrados aqui.
código fonte da função
 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)); 



Criando uma versão do admtf_Table_Indexes sem um cursor


.
 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)); 



PostgreSQL. ;
PostgreSQL. .
PostgreSQL. ( ) .

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


All Articles