WIP: converting DbStructure

This commit is contained in:
Joe Nelson
2016-01-24 18:09:18 -08:00
parent 2d5210464a
commit 4b515c5df4
+298 -258
View File
@@ -27,9 +27,10 @@ import qualified Hasql.Session as H
import PostgREST.Types import PostgREST.Types
import GHC.Exts (groupWith) import GHC.Exts (groupWith)
import GHC.Int (Int32)
import Prelude import Prelude
getDbStructure :: Schema -> H.Session DbStructure getDbStructure :: Schema -> H.Query () DbStructure
getDbStructure schema = do getDbStructure schema = do
tabs <- allTables tabs <- allTables
cols <- allColumns tabs cols <- allColumns tabs
@@ -60,6 +61,48 @@ decodeTables =
tblRow = Table <$> HD.value HD.text <*> HD.value HD.text tblRow = Table <$> HD.value HD.text <*> HD.value HD.text
<*> HD.value HD.bool <*> HD.value HD.bool
decodeColumns :: [Table] -> HD.Result [Column]
decodeColumns tables =
mapMaybe (columnFromRow tables) <$> HD.rowsList colRow
where
colRow =
(,,,,,,,,,,)
<$> HD.value HD.text <*> HD.value HD.text
<*> HD.value HD.text <*> HD.value HD.int4
<*> HD.value HD.bool <*> HD.value HD.text
<*> HD.value HD.bool
<*> HD.nullableValue HD.int4
<*> HD.nullableValue HD.int4
<*> HD.nullableValue HD.text
<*> HD.nullableValue HD.text
decodeRelations :: [Table] -> [Column] -> HD.Result [Relation]
decodeRelations tables cols =
mapMaybe (relationFromRow tables cols) <$> HD.rowsList relRow
where
relRow = (,,,,,)
<$> HD.value HD.text
<*> HD.value HD.text
<*> HD.value (HD.array $ HD.arrayValue HD.text)
<*> HD.value HD.text
<*> HD.value HD.text
<*> HD.value (HD.array $ HD.arrayValue HD.text)
decodePks :: [Table] -> HD.Result [PrimaryKey]
decodePks tables =
mapMaybe (pkFromRow tables) <$> HD.rowsList pkRow
where
pkRow = (,,) <$> HD.value HD.text <*> HD.value HD.text <*> HD.value HD.text
decodeSynonyms :: [Column] -> HD.Result [(Column,Column)]
decodeSynonyms cols =
mapMaybe (synonymFromRow cols) <$> HD.rowsList synRow
where
synRow = (,,,,,)
<$> HD.value HD.text <*> HD.value HD.text
<*> HD.value HD.text <*> HD.value HD.text
<*> HD.value HD.text <*> HD.value HD.text
doesProcExist :: H.Query QualifiedIdentifier Bool doesProcExist :: H.Query QualifiedIdentifier Bool
doesProcExist = doesProcExist =
H.statement sql encodeQi (HD.singleRow (HD.value HD.bool)) True H.statement sql encodeQi (HD.singleRow (HD.value HD.bool)) True
@@ -209,151 +252,148 @@ allTables =
GROUP BY table_schema, table_name, insertable GROUP BY table_schema, table_name, insertable
ORDER BY table_schema, table_name |] ORDER BY table_schema, table_name |]
tableFromRow :: (Text, Text, Bool) -> Table allColumns :: [Table] -> H.Query () [Column]
tableFromRow (s, n, i) = Table s n i
allColumns :: [Table] -> H.Session [Column]
allColumns tabs = do allColumns tabs = do
cols <- H.listEx $ [H.stmt| H.statement sql HE.unit (decodeColumns tabs) True
SELECT DISTINCT where
info.table_schema AS schema, sql = [q|
info.table_name AS table_name, SELECT DISTINCT
info.column_name AS name, info.table_schema AS schema,
info.ordinal_position AS position, info.table_name AS table_name,
info.is_nullable::boolean AS nullable, info.column_name AS name,
info.data_type AS col_type, info.ordinal_position AS position,
info.is_updatable::boolean AS updatable, info.is_nullable::boolean AS nullable,
info.character_maximum_length AS max_len, info.data_type AS col_type,
info.numeric_precision AS precision, info.is_updatable::boolean AS updatable,
info.column_default AS default_value, info.character_maximum_length AS max_len,
array_to_string(enum_info.vals, ',') AS enum info.numeric_precision AS precision,
FROM ( info.column_default AS default_value,
/* array_to_string(enum_info.vals, ',') AS enum
-- CTE based on information_schema.columns to remove the owner filter FROM (
*/ /*
WITH columns AS ( -- CTE based on information_schema.columns to remove the owner filter
SELECT current_database()::information_schema.sql_identifier AS table_catalog, */
nc.nspname::information_schema.sql_identifier AS table_schema, WITH columns AS (
c.relname::information_schema.sql_identifier AS table_name, SELECT current_database()::information_schema.sql_identifier AS table_catalog,
a.attname::information_schema.sql_identifier AS column_name, nc.nspname::information_schema.sql_identifier AS table_schema,
a.attnum::information_schema.cardinal_number AS ordinal_position, c.relname::information_schema.sql_identifier AS table_name,
pg_get_expr(ad.adbin, ad.adrelid)::information_schema.character_data AS column_default, a.attname::information_schema.sql_identifier AS column_name,
CASE a.attnum::information_schema.cardinal_number AS ordinal_position,
WHEN a.attnotnull OR t.typtype = 'd'::"char" AND t.typnotnull THEN 'NO'::text pg_get_expr(ad.adbin, ad.adrelid)::information_schema.character_data AS column_default,
ELSE 'YES'::text CASE
END::information_schema.yes_or_no AS is_nullable, WHEN a.attnotnull OR t.typtype = 'd'::"char" AND t.typnotnull THEN 'NO'::text
CASE ELSE 'YES'::text
WHEN t.typtype = 'd'::"char" THEN END::information_schema.yes_or_no AS is_nullable,
CASE CASE
WHEN bt.typelem <> 0::oid AND bt.typlen = (-1) THEN 'ARRAY'::text WHEN t.typtype = 'd'::"char" THEN
WHEN nbt.nspname = 'pg_catalog'::name THEN format_type(t.typbasetype, NULL::integer) CASE
ELSE 'USER-DEFINED'::text WHEN bt.typelem <> 0::oid AND bt.typlen = (-1) THEN 'ARRAY'::text
END WHEN nbt.nspname = 'pg_catalog'::name THEN format_type(t.typbasetype, NULL::integer)
ELSE ELSE 'USER-DEFINED'::text
CASE END
WHEN t.typelem <> 0::oid AND t.typlen = (-1) THEN 'ARRAY'::text ELSE
WHEN nt.nspname = 'pg_catalog'::name THEN format_type(a.atttypid, NULL::integer) CASE
ELSE 'USER-DEFINED'::text WHEN t.typelem <> 0::oid AND t.typlen = (-1) THEN 'ARRAY'::text
END WHEN nt.nspname = 'pg_catalog'::name THEN format_type(a.atttypid, NULL::integer)
END::information_schema.character_data AS data_type, ELSE 'USER-DEFINED'::text
information_schema._pg_char_max_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS character_maximum_length, END
information_schema._pg_char_octet_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS character_octet_length, END::information_schema.character_data AS data_type,
information_schema._pg_numeric_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS numeric_precision, information_schema._pg_char_max_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS character_maximum_length,
information_schema._pg_numeric_precision_radix(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS numeric_precision_radix, information_schema._pg_char_octet_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS character_octet_length,
information_schema._pg_numeric_scale(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS numeric_scale, information_schema._pg_numeric_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS numeric_precision,
information_schema._pg_datetime_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS datetime_precision, information_schema._pg_numeric_precision_radix(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS numeric_precision_radix,
information_schema._pg_interval_type(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.character_data AS interval_type, information_schema._pg_numeric_scale(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS numeric_scale,
NULL::integer::information_schema.cardinal_number AS interval_precision, information_schema._pg_datetime_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS datetime_precision,
NULL::character varying::information_schema.sql_identifier AS character_set_catalog, information_schema._pg_interval_type(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.character_data AS interval_type,
NULL::character varying::information_schema.sql_identifier AS character_set_schema, NULL::integer::information_schema.cardinal_number AS interval_precision,
NULL::character varying::information_schema.sql_identifier AS character_set_name, NULL::character varying::information_schema.sql_identifier AS character_set_catalog,
CASE NULL::character varying::information_schema.sql_identifier AS character_set_schema,
WHEN nco.nspname IS NOT NULL THEN current_database() NULL::character varying::information_schema.sql_identifier AS character_set_name,
ELSE NULL::name CASE
END::information_schema.sql_identifier AS collation_catalog, WHEN nco.nspname IS NOT NULL THEN current_database()
nco.nspname::information_schema.sql_identifier AS collation_schema, ELSE NULL::name
co.collname::information_schema.sql_identifier AS collation_name, END::information_schema.sql_identifier AS collation_catalog,
CASE nco.nspname::information_schema.sql_identifier AS collation_schema,
WHEN t.typtype = 'd'::"char" THEN current_database() co.collname::information_schema.sql_identifier AS collation_name,
ELSE NULL::name CASE
END::information_schema.sql_identifier AS domain_catalog, WHEN t.typtype = 'd'::"char" THEN current_database()
CASE ELSE NULL::name
WHEN t.typtype = 'd'::"char" THEN nt.nspname END::information_schema.sql_identifier AS domain_catalog,
ELSE NULL::name CASE
END::information_schema.sql_identifier AS domain_schema, WHEN t.typtype = 'd'::"char" THEN nt.nspname
CASE ELSE NULL::name
WHEN t.typtype = 'd'::"char" THEN t.typname END::information_schema.sql_identifier AS domain_schema,
ELSE NULL::name CASE
END::information_schema.sql_identifier AS domain_name, WHEN t.typtype = 'd'::"char" THEN t.typname
current_database()::information_schema.sql_identifier AS udt_catalog, ELSE NULL::name
COALESCE(nbt.nspname, nt.nspname)::information_schema.sql_identifier AS udt_schema, END::information_schema.sql_identifier AS domain_name,
COALESCE(bt.typname, t.typname)::information_schema.sql_identifier AS udt_name, current_database()::information_schema.sql_identifier AS udt_catalog,
NULL::character varying::information_schema.sql_identifier AS scope_catalog, COALESCE(nbt.nspname, nt.nspname)::information_schema.sql_identifier AS udt_schema,
NULL::character varying::information_schema.sql_identifier AS scope_schema, COALESCE(bt.typname, t.typname)::information_schema.sql_identifier AS udt_name,
NULL::character varying::information_schema.sql_identifier AS scope_name, NULL::character varying::information_schema.sql_identifier AS scope_catalog,
NULL::integer::information_schema.cardinal_number AS maximum_cardinality, NULL::character varying::information_schema.sql_identifier AS scope_schema,
a.attnum::information_schema.sql_identifier AS dtd_identifier, NULL::character varying::information_schema.sql_identifier AS scope_name,
'NO'::character varying::information_schema.yes_or_no AS is_self_referencing, NULL::integer::information_schema.cardinal_number AS maximum_cardinality,
'NO'::character varying::information_schema.yes_or_no AS is_identity, a.attnum::information_schema.sql_identifier AS dtd_identifier,
NULL::character varying::information_schema.character_data AS identity_generation, 'NO'::character varying::information_schema.yes_or_no AS is_self_referencing,
NULL::character varying::information_schema.character_data AS identity_start, 'NO'::character varying::information_schema.yes_or_no AS is_identity,
NULL::character varying::information_schema.character_data AS identity_increment, NULL::character varying::information_schema.character_data AS identity_generation,
NULL::character varying::information_schema.character_data AS identity_maximum, NULL::character varying::information_schema.character_data AS identity_start,
NULL::character varying::information_schema.character_data AS identity_minimum, NULL::character varying::information_schema.character_data AS identity_increment,
NULL::character varying::information_schema.yes_or_no AS identity_cycle, NULL::character varying::information_schema.character_data AS identity_maximum,
'NEVER'::character varying::information_schema.character_data AS is_generated, NULL::character varying::information_schema.character_data AS identity_minimum,
NULL::character varying::information_schema.character_data AS generation_expression, NULL::character varying::information_schema.yes_or_no AS identity_cycle,
CASE 'NEVER'::character varying::information_schema.character_data AS is_generated,
WHEN c.relkind = 'r'::"char" OR (c.relkind = ANY (ARRAY['v'::"char", 'f'::"char"])) AND pg_column_is_updatable(c.oid::regclass, a.attnum, false) THEN 'YES'::text NULL::character varying::information_schema.character_data AS generation_expression,
ELSE 'NO'::text CASE
END::information_schema.yes_or_no AS is_updatable WHEN c.relkind = 'r'::"char" OR (c.relkind = ANY (ARRAY['v'::"char", 'f'::"char"])) AND pg_column_is_updatable(c.oid::regclass, a.attnum, false) THEN 'YES'::text
FROM pg_attribute a ELSE 'NO'::text
LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum END::information_schema.yes_or_no AS is_updatable
JOIN (pg_class c FROM pg_attribute a
JOIN pg_namespace nc ON c.relnamespace = nc.oid) ON a.attrelid = c.oid LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum
JOIN (pg_type t JOIN (pg_class c
JOIN pg_namespace nt ON t.typnamespace = nt.oid) ON a.atttypid = t.oid JOIN pg_namespace nc ON c.relnamespace = nc.oid) ON a.attrelid = c.oid
LEFT JOIN (pg_type bt JOIN (pg_type t
JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid) ON t.typtype = 'd'::"char" AND t.typbasetype = bt.oid JOIN pg_namespace nt ON t.typnamespace = nt.oid) ON a.atttypid = t.oid
LEFT JOIN (pg_collation co LEFT JOIN (pg_type bt
JOIN pg_namespace nco ON co.collnamespace = nco.oid) ON a.attcollation = co.oid AND (nco.nspname <> 'pg_catalog'::name OR co.collname <> 'default'::name) JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid) ON t.typtype = 'd'::"char" AND t.typbasetype = bt.oid
WHERE NOT pg_is_other_temp_schema(nc.oid) AND a.attnum > 0 AND NOT a.attisdropped AND (c.relkind = ANY (ARRAY['r'::"char", 'v'::"char", 'f'::"char"])) LEFT JOIN (pg_collation co
/*--AND (pg_has_role(c.relowner, 'USAGE'::text) OR has_column_privilege(c.oid, a.attnum, 'SELECT, INSERT, UPDATE, REFERENCES'::text))*/ JOIN pg_namespace nco ON co.collnamespace = nco.oid) ON a.attcollation = co.oid AND (nco.nspname <> 'pg_catalog'::name OR co.collname <> 'default'::name)
) WHERE NOT pg_is_other_temp_schema(nc.oid) AND a.attnum > 0 AND NOT a.attisdropped AND (c.relkind = ANY (ARRAY['r'::"char", 'v'::"char", 'f'::"char"]))
SELECT /*--AND (pg_has_role(c.relowner, 'USAGE'::text) OR has_column_privilege(c.oid, a.attnum, 'SELECT, INSERT, UPDATE, REFERENCES'::text))*/
table_schema, )
table_name, SELECT
column_name, table_schema,
ordinal_position, table_name,
is_nullable, column_name,
data_type, ordinal_position,
is_updatable, is_nullable,
character_maximum_length, data_type,
numeric_precision, is_updatable,
column_default, character_maximum_length,
udt_name numeric_precision,
/*-- FROM information_schema.columns*/ column_default,
FROM columns udt_name
WHERE table_schema NOT IN ('pg_catalog', 'information_schema') /*-- FROM information_schema.columns*/
) AS info FROM columns
LEFT OUTER JOIN ( WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
SELECT ) AS info
n.nspname AS s, LEFT OUTER JOIN (
t.typname AS n, SELECT
array_agg(e.enumlabel ORDER BY e.enumsortorder) AS vals n.nspname AS s,
FROM pg_type t t.typname AS n,
JOIN pg_enum e ON t.oid = e.enumtypid array_agg(e.enumlabel ORDER BY e.enumsortorder) AS vals
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace FROM pg_type t
GROUP BY s,n JOIN pg_enum e ON t.oid = e.enumtypid
) AS enum_info ON (info.udt_name = enum_info.n) JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
ORDER BY schema, position GROUP BY s,n
|] ) AS enum_info ON (info.udt_name = enum_info.n)
return $ mapMaybe (columnFromRow tabs) cols ORDER BY schema, position |]
columnFromRow :: [Table] -> columnFromRow :: [Table] ->
(Text, Text, Text, (Text, Text, Text,
Int, Bool, Text, Int32, Bool, Text,
Bool, Maybe Int, Maybe Int, Bool, Maybe Int32, Maybe Int32,
Maybe Text, Maybe Text) Maybe Text, Maybe Text)
-> Maybe Column -> Maybe Column
columnFromRow tabs (s, t, n, pos, nul, typ, u, l, p, d, e) = buildColumn <$> table columnFromRow tabs (s, t, n, pos, nul, typ, u, l, p, d, e) = buildColumn <$> table
@@ -363,9 +403,11 @@ columnFromRow tabs (s, t, n, pos, nul, typ, u, l, p, d, e) = buildColumn <$> tab
parseEnum :: Maybe Text -> [Text] parseEnum :: Maybe Text -> [Text]
parseEnum str = fromMaybe [] $ split (==',') <$> str parseEnum str = fromMaybe [] $ split (==',') <$> str
allRelations :: [Table] -> [Column] -> H.Session [Relation] allRelations :: [Table] -> [Column] -> H.Query () [Relation]
allRelations tabs cols = do allRelations tabs cols = do
rels <- H.listEx $ [H.stmt| H.statement sql HE.unit (decodeRelations tabs cols) True
where
sql = [q|
SELECT ns1.nspname AS table_schema, SELECT ns1.nspname AS table_schema,
tab.relname AS table_name, tab.relname AS table_name,
column_info.cols AS columns, column_info.cols AS columns,
@@ -389,9 +431,7 @@ allRelations tabs cols = do
LATERAL (SELECT * FROM pg_class WHERE pg_class.oid = confrelid) AS other, LATERAL (SELECT * FROM pg_class WHERE pg_class.oid = confrelid) AS other,
LATERAL (SELECT * FROM pg_namespace WHERE pg_namespace.oid = other.relnamespace) AS ns2 LATERAL (SELECT * FROM pg_namespace WHERE pg_namespace.oid = other.relnamespace) AS ns2
WHERE confrelid != 0 WHERE confrelid != 0
ORDER BY (conrelid, column_info.nums) ORDER BY (conrelid, column_info.nums) |]
|]
return $ mapMaybe (relationFromRow tabs cols) rels
relationFromRow :: [Table] -> [Column] -> (Text, Text, [Text], Text, Text, [Text]) -> Maybe Relation relationFromRow :: [Table] -> [Column] -> (Text, Text, [Text], Text, Text, [Text]) -> Maybe Relation
relationFromRow allTabs allCols (rs, rt, rcs, frs, frt, frcs) = relationFromRow allTabs allCols (rs, rt, rcs, frs, frt, frcs) =
@@ -404,119 +444,121 @@ relationFromRow allTabs allCols (rs, rt, rcs, frs, frt, frcs) =
cols = mapM (findCol rs rt) rcs cols = mapM (findCol rs rt) rcs
colsF = mapM (findCol frs frt) frcs colsF = mapM (findCol frs frt) frcs
allPrimaryKeys :: [Table] -> H.Session [PrimaryKey] allPrimaryKeys :: [Table] -> H.Query () [PrimaryKey]
allPrimaryKeys tabs = do allPrimaryKeys tabs = do
pks <- H.listEx $ [H.stmt| H.statement sql HE.unit (decodePks tabs) True
/* where
-- CTE to replace information_schema.table_constraints to remove owner limit sql = [q|
*/ /*
WITH tc AS ( -- CTE to replace information_schema.table_constraints to remove owner limit
SELECT current_database()::information_schema.sql_identifier AS constraint_catalog, */
nc.nspname::information_schema.sql_identifier AS constraint_schema, WITH tc AS (
c.conname::information_schema.sql_identifier AS constraint_name, SELECT current_database()::information_schema.sql_identifier AS constraint_catalog,
current_database()::information_schema.sql_identifier AS table_catalog, nc.nspname::information_schema.sql_identifier AS constraint_schema,
nr.nspname::information_schema.sql_identifier AS table_schema, c.conname::information_schema.sql_identifier AS constraint_name,
r.relname::information_schema.sql_identifier AS table_name, current_database()::information_schema.sql_identifier AS table_catalog,
CASE c.contype nr.nspname::information_schema.sql_identifier AS table_schema,
WHEN 'c'::"char" THEN 'CHECK'::text r.relname::information_schema.sql_identifier AS table_name,
WHEN 'f'::"char" THEN 'FOREIGN KEY'::text CASE c.contype
WHEN 'p'::"char" THEN 'PRIMARY KEY'::text WHEN 'c'::"char" THEN 'CHECK'::text
WHEN 'u'::"char" THEN 'UNIQUE'::text WHEN 'f'::"char" THEN 'FOREIGN KEY'::text
ELSE NULL::text WHEN 'p'::"char" THEN 'PRIMARY KEY'::text
END::information_schema.character_data AS constraint_type, WHEN 'u'::"char" THEN 'UNIQUE'::text
CASE ELSE NULL::text
WHEN c.condeferrable THEN 'YES'::text END::information_schema.character_data AS constraint_type,
ELSE 'NO'::text CASE
END::information_schema.yes_or_no AS is_deferrable, WHEN c.condeferrable THEN 'YES'::text
CASE ELSE 'NO'::text
WHEN c.condeferred THEN 'YES'::text END::information_schema.yes_or_no AS is_deferrable,
ELSE 'NO'::text CASE
END::information_schema.yes_or_no AS initially_deferred WHEN c.condeferred THEN 'YES'::text
FROM pg_namespace nc, ELSE 'NO'::text
pg_namespace nr, END::information_schema.yes_or_no AS initially_deferred
pg_constraint c, FROM pg_namespace nc,
pg_class r pg_namespace nr,
WHERE nc.oid = c.connamespace AND nr.oid = r.relnamespace AND c.conrelid = r.oid AND (c.contype <> ALL (ARRAY['t'::"char", 'x'::"char"])) AND r.relkind = 'r'::"char" AND NOT pg_is_other_temp_schema(nr.oid) pg_constraint c,
/*--AND (pg_has_role(r.relowner, 'USAGE'::text) OR has_table_privilege(r.oid, 'INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER'::text) OR has_any_column_privilege(r.oid, 'INSERT, UPDATE, REFERENCES'::text))*/ pg_class r
UNION ALL WHERE nc.oid = c.connamespace AND nr.oid = r.relnamespace AND c.conrelid = r.oid AND (c.contype <> ALL (ARRAY['t'::"char", 'x'::"char"])) AND r.relkind = 'r'::"char" AND NOT pg_is_other_temp_schema(nr.oid)
SELECT current_database()::information_schema.sql_identifier AS constraint_catalog, /*--AND (pg_has_role(r.relowner, 'USAGE'::text) OR has_table_privilege(r.oid, 'INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER'::text) OR has_any_column_privilege(r.oid, 'INSERT, UPDATE, REFERENCES'::text))*/
nr.nspname::information_schema.sql_identifier AS constraint_schema, UNION ALL
(((((nr.oid::text || '_'::text) || r.oid::text) || '_'::text) || a.attnum::text) || '_not_null'::text)::information_schema.sql_identifier AS constraint_name, SELECT current_database()::information_schema.sql_identifier AS constraint_catalog,
current_database()::information_schema.sql_identifier AS table_catalog, nr.nspname::information_schema.sql_identifier AS constraint_schema,
nr.nspname::information_schema.sql_identifier AS table_schema, (((((nr.oid::text || '_'::text) || r.oid::text) || '_'::text) || a.attnum::text) || '_not_null'::text)::information_schema.sql_identifier AS constraint_name,
r.relname::information_schema.sql_identifier AS table_name, current_database()::information_schema.sql_identifier AS table_catalog,
'CHECK'::character varying::information_schema.character_data AS constraint_type, nr.nspname::information_schema.sql_identifier AS table_schema,
'NO'::character varying::information_schema.yes_or_no AS is_deferrable, r.relname::information_schema.sql_identifier AS table_name,
'NO'::character varying::information_schema.yes_or_no AS initially_deferred 'CHECK'::character varying::information_schema.character_data AS constraint_type,
FROM pg_namespace nr, 'NO'::character varying::information_schema.yes_or_no AS is_deferrable,
pg_class r, 'NO'::character varying::information_schema.yes_or_no AS initially_deferred
pg_attribute a FROM pg_namespace nr,
WHERE nr.oid = r.relnamespace AND r.oid = a.attrelid AND a.attnotnull AND a.attnum > 0 AND NOT a.attisdropped AND r.relkind = 'r'::"char" AND NOT pg_is_other_temp_schema(nr.oid) pg_class r,
/*--AND (pg_has_role(r.relowner, 'USAGE'::text) OR has_table_privilege(r.oid, 'INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER'::text) OR has_any_column_privilege(r.oid, 'INSERT, UPDATE, REFERENCES'::text))*/ pg_attribute a
), WHERE nr.oid = r.relnamespace AND r.oid = a.attrelid AND a.attnotnull AND a.attnum > 0 AND NOT a.attisdropped AND r.relkind = 'r'::"char" AND NOT pg_is_other_temp_schema(nr.oid)
/* /*--AND (pg_has_role(r.relowner, 'USAGE'::text) OR has_table_privilege(r.oid, 'INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER'::text) OR has_any_column_privilege(r.oid, 'INSERT, UPDATE, REFERENCES'::text))*/
-- CTE to replace information_schema.key_column_usage to remove owner limit ),
*/ /*
kc AS ( -- CTE to replace information_schema.key_column_usage to remove owner limit
SELECT current_database()::information_schema.sql_identifier AS constraint_catalog, */
ss.nc_nspname::information_schema.sql_identifier AS constraint_schema, kc AS (
ss.conname::information_schema.sql_identifier AS constraint_name, SELECT current_database()::information_schema.sql_identifier AS constraint_catalog,
current_database()::information_schema.sql_identifier AS table_catalog, ss.nc_nspname::information_schema.sql_identifier AS constraint_schema,
ss.nr_nspname::information_schema.sql_identifier AS table_schema, ss.conname::information_schema.sql_identifier AS constraint_name,
ss.relname::information_schema.sql_identifier AS table_name, current_database()::information_schema.sql_identifier AS table_catalog,
a.attname::information_schema.sql_identifier AS column_name, ss.nr_nspname::information_schema.sql_identifier AS table_schema,
(ss.x).n::information_schema.cardinal_number AS ordinal_position, ss.relname::information_schema.sql_identifier AS table_name,
CASE a.attname::information_schema.sql_identifier AS column_name,
WHEN ss.contype = 'f'::"char" THEN information_schema._pg_index_position(ss.conindid, ss.confkey[(ss.x).n]) (ss.x).n::information_schema.cardinal_number AS ordinal_position,
ELSE NULL::integer CASE
END::information_schema.cardinal_number AS position_in_unique_constraint WHEN ss.contype = 'f'::"char" THEN information_schema._pg_index_position(ss.conindid, ss.confkey[(ss.x).n])
FROM pg_attribute a, ELSE NULL::integer
( SELECT r.oid AS roid, END::information_schema.cardinal_number AS position_in_unique_constraint
r.relname, FROM pg_attribute a,
r.relowner, ( SELECT r.oid AS roid,
nc.nspname AS nc_nspname, r.relname,
nr.nspname AS nr_nspname, r.relowner,
c.oid AS coid, nc.nspname AS nc_nspname,
c.conname, nr.nspname AS nr_nspname,
c.contype, c.oid AS coid,
c.conindid, c.conname,
c.confkey, c.contype,
c.confrelid, c.conindid,
information_schema._pg_expandarray(c.conkey) AS x c.confkey,
FROM pg_namespace nr, c.confrelid,
pg_class r, information_schema._pg_expandarray(c.conkey) AS x
pg_namespace nc, FROM pg_namespace nr,
pg_constraint c pg_class r,
WHERE nr.oid = r.relnamespace AND r.oid = c.conrelid AND nc.oid = c.connamespace AND (c.contype = ANY (ARRAY['p'::"char", 'u'::"char", 'f'::"char"])) AND r.relkind = 'r'::"char" AND NOT pg_is_other_temp_schema(nr.oid)) ss pg_namespace nc,
WHERE ss.roid = a.attrelid AND a.attnum = (ss.x).x AND NOT a.attisdropped pg_constraint c
/*--AND (pg_has_role(ss.relowner, 'USAGE'::text) OR has_column_privilege(ss.roid, a.attnum, 'SELECT, INSERT, UPDATE, REFERENCES'::text))*/ WHERE nr.oid = r.relnamespace AND r.oid = c.conrelid AND nc.oid = c.connamespace AND (c.contype = ANY (ARRAY['p'::"char", 'u'::"char", 'f'::"char"])) AND r.relkind = 'r'::"char" AND NOT pg_is_other_temp_schema(nr.oid)) ss
) WHERE ss.roid = a.attrelid AND a.attnum = (ss.x).x AND NOT a.attisdropped
SELECT /*--AND (pg_has_role(ss.relowner, 'USAGE'::text) OR has_column_privilege(ss.roid, a.attnum, 'SELECT, INSERT, UPDATE, REFERENCES'::text))*/
kc.table_schema, )
kc.table_name, SELECT
kc.column_name kc.table_schema,
FROM kc.table_name,
/* kc.column_name
--information_schema.table_constraints tc, FROM
--information_schema.key_column_usage kc /*
*/ --information_schema.table_constraints tc,
tc, kc --information_schema.key_column_usage kc
WHERE */
tc.constraint_type = 'PRIMARY KEY' AND tc, kc
kc.table_name = tc.table_name AND WHERE
kc.table_schema = tc.table_schema AND tc.constraint_type = 'PRIMARY KEY' AND
kc.constraint_name = tc.constraint_name AND kc.table_name = tc.table_name AND
kc.table_schema NOT IN ('pg_catalog', 'information_schema') kc.table_schema = tc.table_schema AND
|] kc.constraint_name = tc.constraint_name AND
return $ mapMaybe (pkFromRow tabs) pks kc.table_schema NOT IN ('pg_catalog', 'information_schema') |]
pkFromRow :: [Table] -> (Schema, Text, Text) -> Maybe PrimaryKey pkFromRow :: [Table] -> (Schema, Text, Text) -> Maybe PrimaryKey
pkFromRow tabs (s, t, n) = PrimaryKey <$> table <*> pure n pkFromRow tabs (s, t, n) = PrimaryKey <$> table <*> pure n
where table = find (\tbl -> tableSchema tbl == s && tableName tbl == t) tabs where table = find (\tbl -> tableSchema tbl == s && tableName tbl == t) tabs
allSynonyms :: [Column] -> H.Session [(Column,Column)] allSynonyms :: [Column] -> H.Query () [(Column,Column)]
allSynonyms allCols = do allSynonyms cols = do
syns <- H.listEx $ [H.stmt| H.statement sql HE.unit (decodeSynonyms cols) True
where
sql = [q|
WITH synonyms AS ( WITH synonyms AS (
/* /*
-- CTE to replace the view from information_schema because the information in it depended on the logged in role -- CTE to replace the view from information_schema because the information in it depended on the logged in role
@@ -578,9 +620,7 @@ allSynonyms allCols = do
syn_table_schema, syn_table_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 */ (regexp_matches(view_definition, CONCAT('\.', src_column_name, '\sAS\s("?)(.+?)\1(,|$)'), 'gn'))[2] AS syn_column_name /* " <- for syntax highlighting */
FROM synonyms FROM synonyms
) ) |]
|]
return $ mapMaybe (synonymFromRow allCols) syns
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