Search code examples
sqlpostgresqlsql-viewdatabase-metadata

Query to get dependant view definition and details for given table column


select 'CREATE VIEW ' || views || E' AS \n'
       || pg_get_viewdef(views, true) as view_definition
  from (
    select distinct(r.ev_class::regclass) as "views",
           pg_class.relname as table_name,
           pg_attribute.attname as column_name,
           pg_class.dependent_view as rel_obj_name 
      from pg_depend d join pg_rewrite r on r.oid = d.objid 
     where refclassid = 'pg_class'::regclass
       and refobjid = 'DBNAME.TABLENAME'::regclass
       and classid = 'pg_rewrite'::regclass
       and pg_get_viewdef(r.ev_class, true) ~ 'COLUMNNAME') as x;

I want to run this query and extract viewdefinition(Dependant) on a table and column and other details.

Currently it gives below error:

ERROR:  missing FROM-clause entry for table "pg_class"
LINE 3: ...(select distinct(r.ev_class::regclass) as "views",pg_class.r...
SQL state: 42P01
Character: 162

Metadata tables used in the above query are:

                   Table "pg_catalog.pg_class"
       Column        |   Type    | Collation | Nullable | Default
---------------------+-----------+-----------+----------+---------
 relname             | name      |           | not null |
 relnamespace        | oid       |           | not null |
 reltype             | oid       |           | not null |
 reloftype           | oid       |           | not null |
 relowner            | oid       |           | not null |
 relam               | oid       |           | not null |
 relfilenode         | oid       |           | not null |
 reltablespace       | oid       |           | not null |
 relpages            | integer   |           | not null |
 reltuples           | real      |           | not null |
 relallvisible       | integer   |           | not null |
 reltoastrelid       | oid       |           | not null |
 relhasindex         | boolean   |           | not null |
 relisshared         | boolean   |           | not null |
 relpersistence      | "char"    |           | not null |
 relkind             | "char"    |           | not null |
 relnatts            | smallint  |           | not null |
 relchecks           | smallint  |           | not null |
 relhasoids          | boolean   |           | not null |
 relhaspkey          | boolean   |           | not null |
 relhasrules         | boolean   |           | not null |
 relhastriggers      | boolean   |           | not null |
 relhassubclass      | boolean   |           | not null |
 relrowsecurity      | boolean   |           | not null |
 relforcerowsecurity | boolean   |           | not null |
 relispopulated      | boolean   |           | not null |
 relreplident        | "char"    |           | not null |
 relfrozenxid        | xid       |           | not null |
 relminmxid          | xid       |           | not null |
 relacl              | aclitem[] |           |          |
 reloptions          | text[]    |           |          |


             Table "pg_catalog.pg_depend"
  Column    |  Type   | Collation | Nullable | Default
------------+---------+-----------+----------+---------
classid     | oid     |           | not null |
objid       | oid     |           | not null |
objsubid    | integer |           | not null |
refclassid  | oid     |           | not null |
refobjid    | oid     |           | not null |
refobjsubid | integer |           | not null |
deptype     | "char"  |           | not null |

               Table "pg_catalog.pg_rewrite"
   Column   |     Type     | Collation | Nullable | Default
------------+--------------+-----------+----------+---------
 rulename   | name         |           | not null |
 ev_class   | oid          |           | not null |
 ev_type    | "char"       |           | not null |
 ev_enabled | "char"       |           | not null |
 is_instead | boolean      |           | not null |
 ev_qual    | pg_node_tree |           |          |
 ev_action  | pg_node_tree |           |          

Query 1:

SELECT dependent_ns.nspname as dependent_schema
, dependent_view.relname as dependent_view 
, source_ns.nspname as source_schema
, source_table.relname as source_table
, pg_attribute.attname as column_name
FROM pg_depend 
JOIN pg_rewrite ON pg_depend.objid = pg_rewrite.oid 
JOIN pg_class as dependent_view ON pg_rewrite.ev_class = dependent_view.oid 
JOIN pg_class as source_table ON pg_depend.refobjid = source_table.oid 
JOIN pg_attribute ON pg_depend.refobjid = pg_attribute.attrelid 
    AND pg_depend.refobjsubid = pg_attribute.attnum 
JOIN pg_namespace dependent_ns ON dependent_ns.oid = dependent_view.relnamespace
JOIN pg_namespace source_ns ON source_ns.oid = source_table.relnamespace
WHERE 
source_ns.nspname = 'SCHEMANAME'
AND source_table.relname = 'TABLENAME'
AND pg_attribute.attnum > 0 
AND pg_attribute.attname = 'COLUMNNAME'
ORDER BY 1,2;

Query 2:

select 'CREATE VIEW ' || views || E' AS \n'
       || pg_get_viewdef(views, true) as view_definition
     from (select distinct(r.ev_class::regclass) as "views" 
          from pg_depend d join pg_rewrite r on r.oid = d.objid 
         where refclassid = 'pg_class'::regclass
           and refobjid = 'SCHEMANAME.TABLENAME'::regclass
           and classid = 'pg_rewrite'::regclass
           and pg_get_viewdef(r.ev_class, true) ~ 'columnname') as x;

How to merge these two queries to get names and view definition from a single query?


Solution

  • I think this is what you are looking for:

    SELECT v.relnamespace::regnamespace AS dependent_schema
         , v.relname                    AS dependent_table
         , t.relnamespace::regnamespace AS source_schema
         , t.relname                    AS source_table
         , a.attname                    AS column_name
         , format(E'CREATE VIEW %s AS\n%s'
                , v.oid::regclass, pg_get_viewdef(r.ev_class, true)) AS view_definition
    FROM   pg_class     t
    JOIN   pg_attribute a ON a.attrelid = t.oid
    JOIN   pg_depend    d ON d.refobjid = a.attrelid
                         AND d.refobjsubid = a.attnum
    JOIN   pg_rewrite   r ON r.oid = d.objid
    JOIN   pg_class     v ON v.oid = r.ev_class
    WHERE  t.oid = 'SCHEMANAME.TABLENAME'::regclass -- table name HERE
    AND    a.attname = 'COLUMNAME'                  -- column name HERE
    AND    NOT attisdropped                         -- no dropped cols
    AND    attnum > 0                               -- no system cols
    AND    d.classid = 'pg_rewrite'::regclass       -- it's a view
    AND    r.ev_type = '1'                          -- only SELECT
    AND    r.is_instead;                            -- INSTEAD rule
    

    Simpler, faster and safer than either of your queries. But this is still building on implementation details that may change in the next major release. So don't rely on it.

    The input ('SCHEMANAME.TABLENAME') optionally includes the schema name. Postgres defaults to the search_path for unqualified names.

    The view definition uses v.oid::regclass, which schema-qualifies the view name depending on the current search_path. Adapt to your needs.

    Related: