Fix view column source detection

This commit is contained in:
Ruslan Talpa
2016-02-29 13:10:33 +02:00
parent 62af792add
commit 40a30d7b02
2 changed files with 89 additions and 61 deletions
+79 -51
View File
@@ -556,69 +556,97 @@ allSynonyms :: [Column] -> H.Query () [(Column,Column)]
allSynonyms cols = allSynonyms cols =
H.statement sql HE.unit (decodeSynonyms cols) True H.statement sql HE.unit (decodeSynonyms cols) True
where where
-- query explanation at https://gist.github.com/ruslantalpa/2eab8c930a65e8043d8f
sql = [q| sql = [q|
WITH synonyms AS ( WITH view_columns AS (
/* SELECT
-- CTE to replace the view from information_schema because the information in it depended on the logged in role c.oid AS view_oid,
-- notice the commented line nc.nspname::information_schema.sql_identifier AS view_schema,
*/ c.relname::information_schema.sql_identifier AS view_name,
WITH view_column_usage AS ( a.attname::information_schema.sql_identifier AS column_name
FROM pg_attribute a
JOIN (pg_class c JOIN pg_namespace nc ON c.relnamespace = nc.oid) ON a.attrelid = c.oid
WHERE
NOT pg_is_other_temp_schema(nc.oid)
AND a.attnum > 0
AND NOT a.attisdropped
AND (c.relkind = 'v'::"char")
AND nc.nspname NOT IN ('information_schema', 'pg_catalog')
),
view_column_usage AS (
SELECT DISTINCT SELECT DISTINCT
CAST(current_database() AS character varying) AS view_catalog, v.oid as view_oid,
CAST(nv.nspname AS character varying) AS view_schema, nv.nspname::information_schema.sql_identifier AS view_schema,
CAST(v.relname AS character varying) AS view_name, v.relname::information_schema.sql_identifier AS view_name,
CAST(current_database() AS character varying) AS table_catalog, nt.nspname::information_schema.sql_identifier AS table_schema,
CAST(nt.nspname AS character varying) AS table_schema, t.relname::information_schema.sql_identifier AS table_name,
CAST(t.relname AS character varying) AS table_name, a.attname::information_schema.sql_identifier AS column_name,
CAST(a.attname AS character varying) AS column_name pg_get_viewdef(v.oid)::information_schema.character_data AS view_definition
FROM pg_namespace nv, pg_class v, pg_depend dv, FROM
pg_depend dt, pg_class t, pg_namespace nt, pg_namespace nv,
pg_class v,
pg_depend dv,
pg_depend dt,
pg_class t,
pg_namespace nt,
pg_attribute a pg_attribute a
WHERE nv.oid = v.relnamespace WHERE
AND v.relkind = 'v' nv.oid = v.relnamespace
AND nv.nspname not in ('information_schema', 'pg_catalog')
AND v.relkind = 'v'::"char"
AND v.oid = dv.refobjid AND v.oid = dv.refobjid
AND dv.refclassid = 'pg_catalog.pg_class'::regclass AND dv.refclassid = 'pg_class'::regclass::oid
AND dv.classid = 'pg_catalog.pg_rewrite'::regclass AND dv.classid = 'pg_rewrite'::regclass::oid
AND dv.deptype = 'i' AND dv.deptype = 'i'::"char"
AND dv.objid = dt.objid AND dv.objid = dt.objid
AND dv.refobjid <> dt.refobjid AND dv.refobjid <> dt.refobjid
AND dt.classid = 'pg_catalog.pg_rewrite'::regclass AND dt.classid = 'pg_rewrite'::regclass::oid
AND dt.refclassid = 'pg_catalog.pg_class'::regclass AND dt.refclassid = 'pg_class'::regclass::oid
AND dt.refobjid = t.oid AND dt.refobjid = t.oid
AND t.relnamespace = nt.oid AND t.relnamespace = nt.oid
AND t.relkind IN ('r', 'v', 'f') AND (t.relkind = ANY (ARRAY['r'::"char", 'v'::"char", 'f'::"char"]))
AND t.oid = a.attrelid AND t.oid = a.attrelid
AND dt.refobjsubid = a.attnum AND dt.refobjsubid = a.attnum
/*--AND pg_has_role(t.relowner, 'USAGE')*/ ),
)
candidates AS (
(
SELECT SELECT
vcu.table_schema AS src_table_schema, vcu.*,
vcu.table_name AS src_table_name, (REGEXP_MATCHES(
vcu.column_name AS src_column_name, CONCAT('SELECT ', SPLIT_PART(vcu.view_definition, 'SELECT', 2)),
view.schemaname AS syn_table_schema, CONCAT('SELECT.*?((',vcu.table_name,')|(\w+))\.(', vcu.column_name, ')(\sAS\s(")?([^"]+)\6)?.*?FROM.*?',vcu.table_schema,'\.(\2|',vcu.table_name,'\s+(AS\s)?\3)'),
view.viewname AS syn_table_name, 'ns'
view.definition AS view_definition ))[7] AS view_column_name
FROM FROM view_column_usage AS vcu
pg_catalog.pg_views AS view, )
view_column_usage AS vcu UNION
(
SELECT
vcu.*,
(REGEXP_MATCHES(
CONCAT('SELECT ', SPLIT_PART(vcu.view_definition, 'SELECT', 2)),
CONCAT('SELECT.*?((',vcu.table_name,')|(\w+))\.(', vcu.column_name, ')(\sAS\s(")?([^"]+)\6)?.*?FROM.*?',vcu.table_schema,'\.(\2|',vcu.table_name,'\s+(AS\s)?\3)'),
'ns'
))[4] AS view_column_name
FROM view_column_usage AS vcu
)
)
SELECT
c.table_schema,
c.table_name,
c.column_name AS table_column_name,
c.view_schema,
c.view_name,
c.view_column_name
FROM view_columns AS vc, candidates AS c
WHERE WHERE
view.schemaname = vcu.view_schema AND vc.view_oid = c.view_oid AND
view.viewname = vcu.view_name AND vc.column_name = c.view_column_name
view.schemaname NOT IN ('pg_catalog', 'information_schema') ORDER BY c.view_schema, c.view_name, c.table_name, c.view_column_name
/*--AND (SELECT COUNT(*) FROM information_schema.view_table_usage WHERE view_schema = view.schemaname AND view_name = view.viewname) = 1*/ |]
)
SELECT
src_table_schema, src_table_name, src_column_name,
syn_table_schema, syn_table_name,
(regexp_matches(view_definition, CONCAT('\.(', src_column_name, ')(?=,|$)'), 'gn'))[1] AS syn_column_name
FROM synonyms
UNION (
SELECT
src_table_schema, src_table_name, src_column_name,
syn_table_schema, syn_table_name,
(regexp_matches(view_definition, CONCAT('\.', src_column_name, '\sAS\s("?)(.+?)\1(,|$)'), 'gn'))[2] AS syn_column_name /* " <- for syntax highlighting */
FROM synonyms
) |]
synonymFromRow :: [Column] -> (Text,Text,Text,Text,Text,Text) -> Maybe (Column,Column) synonymFromRow :: [Column] -> (Text,Text,Text,Text,Text,Text) -> Maybe (Column,Column)
synonymFromRow allCols (s1,t1,c1,s2,t2,c2) = (,) <$> col1 <*> col2 synonymFromRow allCols (s1,t1,c1,s2,t2,c2) = (,) <$> col1 <*> col2
+1 -1
View File
@@ -9,7 +9,7 @@ extra-deps:
- postgresql-error-codes-1 - postgresql-error-codes-1
- postgresql-binary-0.8.1 - postgresql-binary-0.8.1
ghc-options: ghc-options:
postgrest: -O1 -Werror -Wall -fwarn-monomorphism-restriction -fwarn-missing-exported-sigs -fwarn-identities postgrest: -O2 -Werror -Wall -fwarn-monomorphism-restriction -fwarn-missing-exported-sigs -fwarn-identities
packages: packages:
- '.' - '.'