Functions for documenting PostgreSQL databases. Part one

During the work with PostgreSQL, more than a hundred functions have been accumulated for working with system directories: pg_class, pg_attribute, pg_constraints, etc.


What to do with them? They are used relatively rarely. Include in any project? Krasnoyarsk customer will not pay for such “nonsense”. And yet, what if they are useful to someone other than the author. And I decided to lay them out like read books in an open closet for those who want it.

Someone wants to use them in their work. And someone will be interested in a different experience from working with system catalogs.

But in order not to turn the publication into a boring enumeration, it is not known why the functions created, decided to dwell on those that can be combined with a common goal. Therefore, the functions that are used to issue an extended list of characteristics of an arbitrary database table are selected.

The advanced list of characteristics of the database table is returned by the function admtf_Table_ComplexFeatures , which in this article will be called the head function. Thus, the article will be limited to consideration of the functions that are called in the process of performing the head function.

The first half of the article contains comments on the implementation of functions. In the second, the source code of the functions. For those readers who are interested only in the source code, we suggest that you go straight to the Appendix .

see also
Functions for documenting PostgreSQL databases. Part Two ;
Functions for documenting PostgreSQL databases. Part Three
Functions for documenting PostgreSQL databases. The end (part four) .




What are the advanced features in question?


In order to get an idea of ​​what is meant by the extended characteristics of the database table, we begin by considering the following list of characteristics. The list contains the characteristics of the Street database table, returned by the function admtf_Table_ComplexFeatures ( 'public' , 'street' ) .

The table below contains an abbreviated list of the characteristics of the Street table. A full set of characteristics of this table is given in the Additional Materials of Appendix 2.

Table 1. Extended specifications of the Street table.

Text version of the table in the figure
CategoryNoTitleCommenttype ofBase type? not NULL
tbl0streetList of streets in settlements
attonewcrccodeCountry codewcrccodesmallintt
att2localityidID of the settlementlocalityidintegert
att3streetidID street of the villagestreetidsmallintt
attfourstreettypeacrmStreet type acronymstreettypeacrmcharacter (8)f
attfivestreetnameStreet namestreettypeacrmvarchar (150)t
pk0xpkstreetPrimary key table street
pkattonewcrccodeCountry codewcrccodesmallintt
fk01onefk_street_localityForeign key table
fk022fk_street_streettypeForeign key table
idx01onexie1streetIndex on the type and name of the street settlements
idx022xie2streetIndex on the name of the street settlements
idx033xie3streetIndex of street names of all settlements
idx04fourxpkstreetIndex unique (primary key) of the street table


Looking at the dislike of this set of letters and numbers, you can see that we are talking about the usual characteristics of a database table:



The uniqueness of each record from the list of characteristics is provided by the values ​​of the “category” fields and the sequence number (“No.”) of the characteristics.



Table 2. Categories of characteristics of tables.

Text version of the table in the figure
AcronymPurpose
tbTable specifications
attTable attribute characteristics
seqSequence characteristics
pkPrimary Key Characteristics
pkAttCharacteristics of the primary key attribute
fk99Foreign key characteristics
fk99attForeign Key Attribute Characteristics
fk99rtblCharacteristics of the table referenced by the foreign key
fk99rattAttribute characteristics of the table referenced by the foreign key
Idx99Index Features
Idx99attIndex Attribute Characteristics
Inhtbl99Characteristics of the generated table


Category value is necessary in order to distinguish different groups of characteristics from each other. A sequence number, in order to distinguish the characteristics within the group.


Several foreign keys (FOREIGN KEY) and indexes can be declared in the database table. Therefore, the category value for these characteristics and their descendants contains a sequence number. For example, an entry with the key “Category” = idx02att and “No.” = 1 indicates the first attribute of the 2nd index.


In the above list of categories, the location of the sequence number is indicated by '99'.


Remark 1



source code operator in the figure
SELECT * FROM admtf_Table_ComplexFeatures('pg_catalog','pg_class'); 



The article provides examples of the characteristics of the tables, which are briefly described in the auxiliary diagram created specifically to demonstrate the capabilities of the functions. But the reader, having created this or that function in his database, can use the names of his diagrams and tables as parameters. Moreover, as a parameter, for example, the pg_class directory can be used, although in this case a limited number of characteristics are displayed .

The end of the remark.



Head function structure



Fig. 1. Functions that the head function calls.

Table 3. Assignment of functions.

Text version of the table in the figure
NoTitlePurpose
oneadmtf_Table_FeaturesThe function returns a list of database table characteristics.
2admtf_Table_AttributesThe function returns a list of database table attributes and their characteristics.
3admtf_Table_ConstraintesThe function returns a list of database table constraints and their characteristics.
fouradmtf_Table_IndexesThe function returns a list of database table indexes and their characteristics.
fiveadmtf_Table_InheritanceChildrensThe function returns a list of tables generated (IHERITS) from the original database table.
6admtf_Table_SequencesThe function returns a list of sequences (SEQUENCE) on which the table depends.
7admtf_PrimaryKey_ComplexFeaturesThe function returns a complete (extended) list of the primary key characteristics (PRIMARY KEY) of the database table.
eightadmtf_ForeignKey_ComplexFeaturesThe function returns a complete (extended) list of foreign key characteristics (FOREIGN KEY) of the database table.
9admtf_Index_ComplexFeaturesThe function returns a complete (extended) list of database table index characteristics.
tenadmtf_Table_ComplexFeaturesThe function returns a complete (extended) list of characteristics of the database table.


Remark 2.


The function descriptions will be located in the order listed above. The reason is that the article will have to be divided into several parts. And the functions located in this order can be used regardless of the fact that some of them will be described only in the following parts of the publication.


The end of the remark.



Function admtf_Table_Features list of database table characteristics




The function admtf_Table_Features returns a list of the characteristics of the actual database table. Source code can be viewed and downloaded here.


As parameters, the function takes the name of the source table (a_TableName ) and the name of the schema within which the table is created ( a_SchemaName ).


The function extracts the main data from the pg_class directory entry , which contains, in addition to records about tables, records about sequences, views, materialized views, and composite types. Therefore, the condition relkind = 'r' is used to select tables.



 SELECT tbl.relname,dsc.description,tbl.relnatts::INTEGER,tbl.relchecks::INTEGER, tbl.relhaspkey,tbl.relhasindex,tbl.relhassubclass,tbl.reltuples::INTEGER FROM pg_class tbl INNER JOIN pg_namespace nspc ON tbl.relnamespace = nspc.oid LEFT OUTER JOIN pg_description dsc ON tbl.oid=dsc.objoid AND dsc.objsubid=0 WHERE LOWER( nspc.nspname)=LOWER(a_SchemaName) AND tbl.relkind='r' AND LOWER(tbl.relname) =LOWER(a_TableName); 

Additionally, the function accesses the data of the pg_namespace and pg_description directories . The first one contains the names of database schemas, and the second one contains comments to all database objects.


It is important to pay attention to the condition objsubid = 0 . It defines the comment to the table, since the value of the objoid field is the same for both the table and its attributes. The comment to the attribute of the table is contained in the record in which objsubid matches the number of this attribute.



Table 4. Result of execution of the function admtf_Table_Features ('public', 'Street').

TitleCommentNumber of attributesNumber of CHECK restrictions? is there a primary key? indexes declared? are there any descendantsNumber of records in the table
streetList of streets in settlements220ttf20150

Note 3


Note the number of attributes of the street table. It is significantly different from the number of attributes specified in the auxiliary scheme.


Table 5. Additional attributes of the Street table.


attnameatttypidattnumNote
cmin29-fourSystem attribute
xmin28-3System attribute
ctid27-oneSystem attribute
wcrccode795369oneValid attribute
localityid7953522Valid attribute
streetid7953643Valid attribute
streettypeacrm1919168fourValid attribute
streetname1043fiveValid attribute
........ pg.dropped.6 ........06Remote attribute
........ pg.dropped.7 ........07Remote attribute

The fact is that PostgreSQL, in addition to the basic attributes, additionally takes into account several system attributes, and even remote attributes.


End note



The function admtf_Table_Attributes a list of database table attributes and their characteristics


The function admtf_Table_Attributes returns a list of attributes of a database table. Source code can be viewed and downloaded here.


As parameters, the function takes the name of the source table ( a_TableName ) and the name of the schema within which the table is created ( a_SchemaName ).


The main data function extracts from the directory entry pg_attribute and pg_type . The first contains entries with data about the attributes of tables, views, materialized views, composite types, and even functions. The second is about the characteristics of attribute types.


Perhaps some explanation is required of the way in which the function defines the user and base types.


A table attribute is declared with a user-defined type if in the corresponding directory entry pg_type the field typbasetype is greater than 0. Otherwise, the attribute is of the base type. Therefore, the pg_type directory participates twice in the FROM clause. In the first directory entry, the presence of a user-defined type is determined; if not defined ( typbasetype = 0 ), then the value of the base type is formed from this entry. Otherwise, the base type is determined from the entry for which btyp.OID = typ.typbasetype.


Directly the string with the base type is formed using the function of the system catalog FORMAT_TYPE (type_oid, typemod) . The first parameter of which is the base type OID entries. The second parameter is the value of the modifier for types that contain size. For example, VARCHAR (100) or NUMERIC (4,2), DECIMAL (4,2). The value of the typemod parameter is taken from typ.typtypmod if the attribute has a custom type, otherwise from attr.atttypmod , i.e. directly from the attribute record.



source code operator in the figure
 SELECT attr.attnum, attr.attname::VARCHAR(100), CASE WHEN COALESCE(typ.typbasetype,0)>0 THEN typ.typname::VARCHAR(100) ELSE '' END, FORMAT_TYPE(COALESCE(NULLIF(typ.typbasetype,0),typ.oid), COALESCE(NULLIF(typ.typtypmod,-1),attr.atttypmod))::VARCHAR(256), attr.attnotnull, dsc.description FROM pg_attribute attr INNER JOIN pg_class tbl ON tbl.oid = attr.attrelid INNER JOIN pg_namespace nspc ON tbl.relnamespace = nspc.oid INNER JOIN pg_type typ ON attr.atttypid=typ.oid LEFT OUTER JOIN pg_description dsc ON dsc.objoid=attr.attrelid AND dsc.objsubid=attr.attnum WHERE LOWER( nspc.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname) =LOWER(a_TableName) AND tbl.relkind='r' AND attr.attnum>0 AND attr.atttypID>0 ORDER BY tbl.relname,attr.attnum; 


Additionally, the function accesses the data of the pg_class , pg_namespace and pg_description directories . The first and second directories are used to search for attributes by schema and database table names.

The third directory is used to retrieve a comment on a table attribute.


The comment to the attribute of the table is in the record where the dsc.objoid contains the OID of the source table, and dsc.objsubid is the ordinal number of the attribute in the table, i.e. attr.attnum .


In order for the function not to return system and deleted attributes, the clause WHERE is set to attr.attnum> 0 AND attr.atttypID> 0 .



Table 6. Result of the execution of the function admtf_Table_Attributes ('public', 'Street').

NoTitleCustom typeBase type? not NULLComment
onewcrccodewcrccodesmallinttCountry code
2localityidlocalityidintegertID of the settlement
3streetidstreetidsmallinttID street of the village
fourstreettypeacrmstreettypeacrmcharacter (8)fStreet type acronym
fivestreetnamevarchar (150)tStreet name of the settlement

Function version using the regclass alias for the oid type


Object identifiers (OIDs) in PostgreSQL have the same type OID, which is currently implemented as an unsigned four-byte integer. But due to the presence of aliases of this type, an integer can be represented as an object name. And vice versa - convert the object name to an integer of type OID.


As an example, take a look at the following SELECT statement . It unusually retrieves the names of the attribute table and the names of its types — instead of referring to the corresponding catalog fields with the names of these characteristics, use:



 SELECT attr.attname,attr.attrelid::regclass, attr.atttypid::regtype, typ.typbasetype::regtype,attr.attrelid::regclass::name, attr.atttypid::regtype::name,typ.typbasetype::regtype::name FROM pg_attribute attr INNER JOIN pg_type typ ON attr.atttypid=typ.oid WHERE attr.attrelid=('public'||'.'||'Street')::regclass AND attr.attnum>0 AND attr.atttypID>0 ORDER BY attr.attnum; 

Below is the result of this query.



In the list of output values ​​of the SELECT statement before conversion using aliases of the OID type, all values ​​except the attribute name are numeric, but the result is the names of the table and attribute types. The types of output values ​​can be seen in the second row of the table header.


In addition, in the WHERE clause of the statement is the condition attr.attrelid = ('public' || '.' || 'Street') :: regclass , on the left side of which is a numeric value, and in the right part - a string value, which is converted to a numeric using the alias regclass .



source code operator in the figure
 SELECT attr.attnum, attr.attname::VARCHAR(100), CASE WHEN COALESCE(typ.typbasetype,0)>0 THEN typ.typname::VARCHAR(100) ELSE '' END, FORMAT_TYPE(COALESCE(NULLIF(typ.typbasetype,0),typ.oid), COALESCE(NULLIF(typ.typtypmod,-1),attr.atttypmod))::VARCHAR(256), attr.attnotnull, dsc.description FROM pg_attribute attr INNER JOIN pg_type typ ON attr.atttypid=typ.oid LEFT OUTER JOIN pg_description dsc ON dsc.objoid=attr.attrelid AND dsc.objsubid=attr.attnum WHERE attr.attrelid=( a_SchemaName ||'.'|| a_TableName)::regclass AND attr.attnum>0 AND attr.atttypID>0 ORDER BY attr.attnum; 


Using the regclass alias, you can remove the connection from two directories from the main statement. But such an improvement had almost no effect on the performance of the function - in both versions, the function is executed in 11 ms. Perhaps due to the fact that there are few attributes in the test table.
Remark 4

A serious disadvantage of the condition in the form of attr.attrelid = (a_SchemaName || '.' || a_TableName) :: regclass is manifested in the case when there is a schema and / or table with an unusual name in the database. For example, “My Scheme” and / or “My Table” . Such values ​​need to be transferred, enclosed in double quotes, or use the QUOTE_IDENT function, otherwise the function will end with a system error.



source code operator in the figure
 /*       «   »*/ SELECT attr.attname,attr.attrelid::regclass, attr.atttypid::regtype,typ.typbasetype::regtype, attr.attrelid::regclass::name, attr.atttypid::regtype::name,typ.typbasetype::regtype::name FROM pg_attribute attr INNER JOIN pg_type typ ON attr.atttypid=typ.oid WHERE attr.attrelid=(' '||'.'||' ')::regclass AND attr.attnum>0 AND attr.atttypID>0 ORDER BY attr.attnum; /*       */ SELECT attr.attname,attr.attrelid::regclass, attr.atttypid::regtype,typ.typbasetype::regtype, attr.attrelid::regclass::name, attr.atttypid::regtype::name,typ.typbasetype::regtype::name FROM pg_attribute attr INNER JOIN pg_type typ ON attr.atttypid=typ.oid WHERE attr.attrelid=('" "'||'.'||'" "')::regclass AND attr.attnum>0 AND attr.atttypID>0 ORDER BY attr.attnum; 


Therefore, I prefer to use conditions in the form LOWER (nspc.nspname) = LOWER (a_SchemaName) AND LOWER (tbl.relname) = LOWER (a_TableName) , which does not lead to system errors.

End note

APPENDIX 1. Scripts


Create function admtf_Table_Features


Comments on the source code of the function can be found here.
function source code
 BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Features (a_SchemaName NAME,a_TableName NAME); /********************************************************************************************************/ /*     ,   */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Features (a_SchemaName NAME default 'public', /*     */ a_TableName NAME default NULL /*   */ ) RETURNS TABLE (rs_TableName NAME,rs_TableDescription TEXT,rs_NumberOfAttribute INTEGER,rs_NumberOfChecks INTEGER,rs_hasPKey BOOLEAN,rs_hasIndex BOOLEAN,rs_hasSubClass BOOLEAN,rs_NumberOfRow INTEGER) AS $BODY$ DECLARE c_TableKind CONSTANT CHAR:='r'; v_TableOID OID; /*   */ v_TableName NAME; /*   */ v_TableDescription TEXT; /*   */ v_TableNumberOfRowCalc INTEGER; /*     */ --****************************************************************************************************** BEGIN SELECT INTO rs_TableName,rs_TableDescription,rs_NumberOfAttribute, rs_NumberOfChecks,rs_hasPKey,rs_hasIndex,rs_hasSubClass, rs_NumberOfRow tbl.relname,dsc.description,tbl.relnatts::INTEGER,tbl.relchecks::INTEGER, tbl.relhaspkey,tbl.relhasindex,tbl.relhassubclass,tbl.reltuples::INTEGER FROM pg_class tbl INNER JOIN pg_namespace nspc ON tbl.relnamespace = nspc.oid LEFT OUTER JOIN pg_Description dsc ON tbl.oid=dsc.objoid AND dsc.objsubid=0 WHERE nspc.nspname=LOWER(a_SchemaName) AND tbl.relkind=c_TableKind AND tbl.relname =LOWER(a_TableName); EXECUTE 'SELECT count(*) FROM ' ||LOWER(a_SchemaName) ||'.'||quote_ident(LOWER(a_TableName)) INTO v_TableNumberOfRowCalc; RETURN QUERY SELECT rs_TableName,rs_TableDescription,rs_NumberOfAttribute, rs_NumberOfChecks,rs_hasPKey,rs_hasIndex, rs_hasSubClass,v_TableNumberOfRowCalc AS rs_NumberOfRow; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Features(a_SchemaName NAME,a_TableName NAME) IS '   ,  '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Features (a_SchemaName VARCHAR(256),a_TableName VARCHAR(256)); /********************************************************************************************************/ /*     ,   */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Features (a_SchemaName VARCHAR(256) default 'public', /*     */ a_TableName VARCHAR(256) default NULL /*   */ ) RETURNS TABLE (rs_TableName VARCHAR(256),rs_TableDescription TEXT,rs_NumberOfAttribute INTEGER,rs_NumberOfChecks INTEGER,rs_hasPKey BOOLEAN,rs_hasIndex BOOLEAN,rs_hasSubClass BOOLEAN,rs_NumberOfRow INTEGER) AS $BODY$ DECLARE c_TableKind CONSTANT CHAR:='r'; v_TableOID OID; /*   */ v_TableName VARCHAR(256); /*   */ v_TableDescription TEXT; /*   */ v_TableNumberOfRowCalc INTEGER; /*     */ --****************************************************************************************************** BEGIN RETURN QUERY SELECT tf.rs_TableName::VARCHAR(256), tf.rs_TableDescription::TEXT, tf.rs_NumberOfAttribute::INTEGER, tf.rs_NumberOfChecks::INTEGER, tf.rs_hasPKey::BOOLEAN, tf.rs_hasIndex::BOOLEAN, tf.rs_hasSubClass::BOOLEAN, tf.rs_NumberOfRow::INTEGER FROM admtf_Table_Features(a_SchemaName::NAME,a_TableName::NAME) tf; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Features(a_SchemaName VARCHAR(256),a_TableName VARCHAR(256)) IS '   ,  '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; SELECt * FROM admtf_Table_Features('public'::VARCHAR(256),'Street'::VARCHAR(256)); SELECt * FROM admtf_Table_Features('public':: NAME,'Street'::NAME); 



Create function admtf_Table_Attributes


Comments on the source code of the function can be found here.
function source code
 BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Attributes (a_SchemaName NAME,a_TableName NAME); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Attributes (a_SchemaName NAME default 'public', /*     */ a_TableName NAME default NULL /*   */ ) RETURNS TABLE (r_AttributeNumber SMALLINT,r_AttributeName NAME,r_UserTypeName NAME,r_TypeName NAME,r_isNotNULL BOOLEAN, r_Description Text) AS $BODY$ DECLARE c_TableKind CONSTANT CHAR:='r'; v_Scale INTEGER; /*   */ --****************************************************************************************************** BEGIN RETURN QUERY SELECT attr.attnum AS r_AttributeNumber, attr.attname::NAME AS r_AttributeName, CASE WHEN COALESCE(typ.typbasetype,0)>0 THEN typ.typname::NAME ELSE ''::NAME END AS r_UserTypeName, FORMAT_TYPE(COALESCE(NULLIF(typ.typbasetype,0),typ.oid), COALESCE(NULLIF(typ.typtypmod,-1),attr.atttypmod))::NAME AS r_TypeName, attr.attnotnull AS r_isNotNULL, dsc.description AS r_Description FROM pg_attribute attr INNER JOIN pg_class tbl ON tbl.oid = attr.attrelid INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid LEFT OUTER JOIN pg_type typ ON attr.atttypid=typ.oid LEFT OUTER JOIN pg_type btyp ON typ.typbasetype=btyp.oid LEFT OUTER JOIN pg_description dsc ON dsc.objoid=attr.attrelid AND dsc.objsubid=attr.attnum WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableName) AND tbl.relkind=c_TableKind AND attr.attnum>0 AND attr.atttypID>0 ORDER BY tbl.relname,attr.attnum; RETURN; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Attributes(a_SchemaName NAME,a_TableName NAME) IS '   '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Attributes (a_SchemaName VARCHAR(256),a_TableName VARCHAR(256)); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Attributes (a_SchemaName VARCHAR(256) default 'public', /*     */ a_TableName VARCHAR(256) default NULL /*   */ ) RETURNS TABLE (r_AttributeNumber SMALLINT,r_AttributeName VARCHAR(256),r_UserTypeName VARCHAR(256),r_TypeName VARCHAR(256),r_isNotNULL BOOLEAN, r_Description Text) AS $BODY$ DECLARE c_TableKind CONSTANT CHAR:='r'; v_Scale INTEGER; /*   */ --****************************************************************************************************** BEGIN RETURN QUERY SELECT ta.r_AttributeNumber::SMALLINT, ta.r_AttributeName::VARCHAR(256), ta.r_UserTypeName::VARCHAR(256), ta.r_TypeName::VARCHAR(256), ta.r_isNotNULL::BOOLEAN, ta.r_Description::TEXT FROM admtf_Table_Attributes(a_SchemaName::NAME,a_TableName::NAME) ta; RETURN; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Attributes(a_SchemaName VARCHAR(256),a_TableName VARCHAR(256)) IS '   '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; SELECT * FROM admtf_Table_Attributes('public'::VARCHAR(256),'Street'::VARCHAR(256)); SELECT * FROM admtf_Table_Attributes('public'::NAME,'Street'::NAME); 


Creating the function admtf_Table_Attributes using the regclass alias


Comments on the source code of the function can be found here.
function source code
 BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Attributes (a_SchemaName NAME,a_TableName NAME); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Attributes (a_SchemaName NAME default 'public', /*     */ a_TableName NAME default NULL /*   */ ) RETURNS TABLE (r_AttributeNumber SMALLINT,r_AttributeName NAME,r_UserTypeName NAME,r_TypeName NAME,r_isNotNULL BOOLEAN, r_Description Text) AS $BODY$ DECLARE c_TableKind CONSTANT CHAR:='r'; v_Scale INTEGER; /*   */ --****************************************************************************************************** BEGIN RETURN QUERY SELECT attr.attnum AS r_AttributeNumber, attr.attname::NAME AS r_AttributeName, CASE WHEN COALESCE(typ.typbasetype,0)>0 THEN typ.typname::NAME ELSE ''::NAME END AS r_UserTypeName, FORMAT_TYPE(COALESCE(NULLIF(typ.typbasetype,0),typ.oid), COALESCE(NULLIF(typ.typtypmod,-1),attr.atttypmod))::NAME AS r_TypeName, attr.attnotnull AS r_isNotNULL, dsc.description AS r_Description FROM pg_attribute attr INNER JOIN pg_type typ ON attr.atttypid=typ.oid LEFT OUTER JOIN pg_type btyp ON typ.typbasetype=btyp.oid LEFT OUTER JOIN pg_description dsc ON dsc.objoid=attr.attrelid AND dsc.objsubid=attr.attnum WHERE attr.attrelid=(LOWER(a_SchemaName)||'.'|| LOWER(a_TableName))::regclass AND attr.attnum>0 AND attr.atttypID>0 ORDER BY attr.attnum; RETURN; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Attributes(a_SchemaName NAME,a_TableName NAME) IS '   '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Attributes (a_SchemaName VARCHAR(256),a_TableName VARCHAR(256)); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Attributes (a_SchemaName VARCHAR(256) default 'public', /*     */ a_TableName VARCHAR(256) default NULL /*   */ ) RETURNS TABLE (r_AttributeNumber SMALLINT,r_AttributeName VARCHAR(256),r_UserTypeName VARCHAR(256),r_TypeName VARCHAR(256),r_isNotNULL BOOLEAN, r_Description Text) AS $BODY$ DECLARE c_TableKind CONSTANT CHAR:='r'; v_Scale INTEGER; /*   */ --****************************************************************************************************** BEGIN RETURN QUERY SELECT ta.r_AttributeNumber::SMALLINT, ta.r_AttributeName::VARCHAR(256), ta.r_UserTypeName::VARCHAR(256), ta.r_TypeName::VARCHAR(256), ta.r_isNotNULL::BOOLEAN, ta.r_Description::TEXT FROM admtf_Table_Attributes(a_SchemaName::NAME,a_TableName::NAME) ta; RETURN; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Attributes(a_SchemaName VARCHAR(256),a_TableName VARCHAR(256)) IS '   '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; SELECT * FROM admtf_Table_Attributes('public'::VARCHAR(256),'Street'::VARCHAR(256)); SELECT * FROM admtf_Table_Attributes('public'::NAME,'Street'::NAME); 



APPENDIX 2. Additional materials


Auxiliary database schema








Extended specifications of the Street table


.
1. Street ().

CategoryNoTitleCommenttype of? not NULL
tbl0street
attonewcrccodeCountry codewcrccodesmallintt
att2localityidID of the settlementlocalityidintegert
att3streetidstreetidsmallintt
attfourstreettypeacrmstreettypeacrmcharacter(8)f
attfivestreetnamestreettypeacrmvarchar(150)t
pk0xpkstreetPrimary key table street
pkattonewcrccodeCountry codewcrccodesmallintt
pkatt2localityidID of the settlementlocalityidintegert
pkatt3streetidstreetidsmallintt
fk01onefk_street_locality
fk01attonewcrccodeCountry codewcrccodesmallintt
fk01att2localityidID of the settlementlocalityidintegert
fk01rtbl0locality
fk01rattonewcrccodeCountry codewcrccodesmallintt
fk01ratt2localityidID of the settlementlocalityidintegert
fk022fk_street_streettype
fk02attonestreettypeacrmstreettypeacrmcharacter(8)f
fk02rtbl0streettype
fk02rattonestreettypeacrmstreettypeacrmcharacter(8)t
idx01onexie1street
idx01attonewcrccodeCountry codewcrccodesmallintt
idx01att2localityidID of the settlementlocalityidintegert
idx01att3streettypeacrmstreettypeacrmcharacter(8)f
idx01attfourstreetnamevarchar(150)t
idx022xie2street
idx02attonewcrccodeCountry codewcrccodesmallint
idx02att2localityidID of the settlementlocalityidintegert
idx02att3streetnamevarchar(150)t
idx033xie3street
idx03attonestreetnamevarchar(150)t
idx04fourxpkstreet( ) street
idx04attonewcrccodeCountry codewcrccodesmallintt
idx04att2localityidID of the settlementlocalityidintegert
idx04att3streetidstreetidsmallintt


see also


Functions for documenting PostgreSQL databases. Part Two ;
Functions for documenting PostgreSQL databases. Part Three
Functions for documenting PostgreSQL databases. The end (part four) .

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


All Articles