|
| 1 | +{-# LANGUAGE QuasiQuotes, FlexibleInstances #-} |
| 2 | + |
| 3 | +module View where |
| 4 | + |
| 5 | +import Str(str) |
| 6 | +import Acl |
| 7 | +import Util |
| 8 | +import Console |
| 9 | +import Diff |
| 10 | + |
| 11 | +tblList = [str| |
| 12 | +SELECT n.nspname AS "Schema", c.relname AS "Name", d.description AS "Comment", |
| 13 | + relacl AS "ACLs" |
| 14 | +FROM pg_catalog.pg_namespace n |
| 15 | + JOIN pg_catalog.pg_class c ON c.relnamespace = n.oid |
| 16 | + LEFT JOIN pg_catalog.pg_description d ON (c.oid = d.objoid AND d.objsubid = 0) |
| 17 | + -- LEFT JOIN pg_catalog.pg_class dc ON (d.classoid=dc.oid AND dc.relname='pg_class') |
| 18 | + -- LEFT JOIN pg_catalog.pg_namespace dn ON (dn.oid=dc.relnamespace AND dn.nspname='pg_catalog') |
| 19 | +WHERE n.nspname IN ('account','document') |
| 20 | + AND c.relkind = 'r' |
| 21 | + AND n.nspname !~ '^pg_' |
| 22 | + AND n.nspname <> 'information_schema' |
| 23 | +ORDER BY 1, 2 |
| 24 | +|] |
| 25 | + |
| 26 | +tblColumns = [str| |
| 27 | +SELECT * FROM (SELECT n.nspname,c.relname,a.attname,a.atttypid,a.attnotnull OR (t.typtype = 'd' AND t.typnotnull) AS attnotnull,a.atttypmod,a.attlen,row_number() OVER (PARTITION BY a.attrelid ORDER BY a.attnum) AS attnum, pg_catalog.pg_get_expr(def.adbin, def.adrelid) AS adsrc,dsc.description,t.typbasetype,t.typtype FROM pg_catalog.pg_namespace n JOIN pg_catalog.pg_class c ON (c.relnamespace = n.oid) JOIN pg_catalog.pg_attribute a ON (a.attrelid=c.oid) JOIN pg_catalog.pg_type t ON (a.atttypid = t.oid) LEFT JOIN pg_catalog.pg_attrdef def ON (a.attrelid=def.adrelid AND a.attnum = def.adnum) LEFT JOIN pg_catalog.pg_description dsc ON (c.oid=dsc.objoid AND a.attnum = dsc.objsubid) LEFT JOIN pg_catalog.pg_class dc ON (dc.oid=dsc.classoid AND dc.relname='pg_class') LEFT JOIN pg_catalog.pg_namespace dn ON (dc.relnamespace=dn.oid AND dn.nspname='pg_catalog') |
| 28 | +WHERE a.attnum > 0 AND NOT a.attisdropped AND n.nspname LIKE 'account' AND c.relname LIKE 'user_table') c WHERE true ORDER BY nspname,c.relname,attnum |
| 29 | +|] |
| 30 | + |
| 31 | + |
| 32 | + |
| 33 | +tblIndices2 = [str| |
| 34 | +SELECT NULL AS TABLE_CAT, n.nspname AS TABLE_SCHEM, ct.relname AS TABLE_NAME, NOT i.indisunique AS NON_UNIQUE, NULL AS INDEX_QUALIFIER, ci.relname AS INDEX_NAME, CASE i.indisclustered WHEN true THEN 1 ELSE CASE am.amname WHEN 'hash' THEN 2 ELSE 3 END END AS TYPE, (i.keys).n AS ORDINAL_POSITION, pg_catalog.pg_get_indexdef(ci.oid, (i.keys).n, false) AS COLUMN_NAME, CASE am.amcanorder WHEN true THEN CASE i.indoption[(i.keys).n - 1] & 1 WHEN 1 THEN 'D' ELSE 'A' END ELSE NULL END AS ASC_OR_DESC, ci.reltuples AS CARDINALITY, ci.relpages AS PAGES, pg_catalog.pg_get_expr(i.indpred, i.indrelid) AS FILTER_CONDITION FROM pg_catalog.pg_class ct JOIN pg_catalog.pg_namespace n ON (ct.relnamespace = n.oid) JOIN (SELECT i.indexrelid, i.indrelid, i.indoption, i.indisunique, i.indisclustered, |
| 35 | +i.indpred, i.indexprs, information_schema._pg_expandarray(i.indkey) AS keys FROM pg_catalog.pg_index i) i ON (ct.oid = i.indrelid) JOIN pg_catalog.pg_class ci ON (ci.oid = i.indexrelid) JOIN pg_catalog.pg_am am ON (ci.relam = am.oid) WHERE true AND n.nspname = 'account' AND ct.relname = 'user_table' ORDER BY NON_UNIQUE, TYPE, INDEX_NAME, ORDINAL_POSITION |
| 36 | +|] |
| 37 | + |
| 38 | +tblIndices = [str| |
| 39 | +select ind.indisclustered, ind.indexrelid, ind.indisprimary, cls.relname from pg_catalog.pg_index ind, pg_catalog.pg_class tab, pg_catalog.pg_namespace sch, pg_catalog.pg_class cls where ind.indrelid = tab.oid and cls.oid = ind.indexrelid and tab.relnamespace = sch.oid and tab.relname = $1 and sch.nspname = $2 |
| 40 | +|] |
| 41 | + |
| 42 | +tblConstraints = [str| |
| 43 | +SELECT cons.conname, cons.conkey |
| 44 | +FROM pg_catalog.pg_constraint cons, pg_catalog.pg_class tab, pg_catalog.pg_namespace sch |
| 45 | +WHERE cons.contype = 'u' and cons.conrelid = tab.oid and tab.relnamespace = sch.oid |
| 46 | + AND tab.relname = $1 and sch.nspname = $2 |
| 47 | +|] |
| 48 | + |
| 49 | +tblKeysx = [str| |
| 50 | +SELECT NULL AS TABLE_CAT, n.nspname AS TABLE_SCHEM, ct.relname AS TABLE_NAME, a.attname AS COLUMN_NAME, (i.keys).n AS KEY_SEQ, ci.relname AS PK_NAME FROM pg_catalog.pg_class ct JOIN pg_catalog.pg_attribute a ON (ct.oid = a.attrelid) JOIN pg_catalog.pg_namespace n ON (ct.relnamespace = n.oid) JOIN (SELECT i.indexrelid, i.indrelid, i.indisprimary, information_schema._pg_expandarray(i.indkey) AS keys FROM pg_catalog.pg_index i) i ON (a.attnum = (i.keys).x AND a.attrelid = i.indrelid) JOIN pg_catalog.pg_class ci ON (ci.oid = i.indexrelid) WHERE true AND n.nspname = 'account' AND ct.relname = 'user_table' AND i.indisprimary ORDER BY table_name, pk_name, key_seq |
| 51 | +|] |
| 52 | + |
| 53 | +tblKeys = [str| |
| 54 | +SELECT NULL::text AS PKTABLE_CAT, pkn.nspname AS PKTABLE_SCHEM, pkc.relname AS PKTABLE_NAME, pka.attname AS PKCOLUMN_NAME, NULL::text AS FKTABLE_CAT, fkn.nspname AS FKTABLE_SCHEM, fkc.relname AS FKTABLE_NAME, fka.attname AS FKCOLUMN_NAME, pos.n AS KEY_SEQ, CASE con.confupdtype WHEN 'c' THEN 0 WHEN 'n' THEN 2 WHEN 'd' THEN 4 WHEN 'r' THEN 1 WHEN 'a' THEN 3 ELSE NULL END AS UPDATE_RULE, CASE con.confdeltype WHEN 'c' THEN 0 WHEN 'n' THEN 2 WHEN 'd' THEN 4 WHEN 'r' THEN 1 WHEN 'a' THEN 3 ELSE NULL END AS DELETE_RULE, con.conname AS FK_NAME, pkic.relname AS PK_NAME, CASE WHEN con.condeferrable AND con.condeferred THEN 5 WHEN con.condeferrable THEN 6 ELSE 7 END AS DEFERRABILITY FROM pg_catalog.pg_namespace pkn, pg_catalog.pg_class pkc, pg_catalog.pg_attribute pka, pg_catalog.pg_namespace fkn, pg_catalog.pg_class fkc, pg_catalog.pg_attribute fka, |
| 55 | +pg_catalog.pg_constraint con, pg_catalog.generate_series(1, 32) pos(n), pg_catalog.pg_depend dep, pg_catalog.pg_class pkic WHERE pkn.oid = pkc.relnamespace AND pkc.oid = pka.attrelid AND pka.attnum = con.confkey[pos.n] AND con.confrelid = pkc.oid AND fkn.oid = fkc.relnamespace AND fkc.oid = fka.attrelid AND fka.attnum = con.conkey[pos.n] AND con.conrelid = fkc.oid AND con.contype = 'f' AND con.oid = dep.objid AND pkic.oid = dep.refobjid AND pkic.relkind = 'i' AND dep.classid = 'pg_constraint'::regclass::oid AND dep.refclassid = 'pg_class'::regclass::oid AND fkn.nspname = 'account' AND fkc.relname = 'user_table' ORDER BY pkn.nspname,pkc.relname,pos.n |
| 56 | +|] |
| 57 | + |
| 58 | + |
| 59 | +{- |
| 60 | +viewList = [str| |
| 61 | +SELECT n.nspname AS "Schema", c.relname AS "Name", -- d.description AS "Comment", |
| 62 | + pg_get_viewdef(c.oid) AS definition, |
| 63 | + relacl AS "ACLs" |
| 64 | +FROM pg_catalog.pg_namespace n |
| 65 | + JOIN pg_catalog.pg_class c ON c.relnamespace = n.oid |
| 66 | + LEFT JOIN pg_catalog.pg_description d ON (c.oid = d.objoid AND d.objsubid = 0) |
| 67 | +WHERE n.nspname IN (select * from unnest(current_schemas(false))) |
| 68 | + AND c.relkind = 'v' |
| 69 | + AND n.nspname !~ '^pg_' |
| 70 | + AND n.nspname <> 'information_schema' |
| 71 | +ORDER BY 1, 2 |
| 72 | +|] |
| 73 | +
|
| 74 | +viewColumns = [str| |
| 75 | +SELECT n.nspname as "Schema",c.relname AS "View",a.attname AS "Column",a.atttypid AS "Type", |
| 76 | + a.attnotnull OR (t.typtype = 'd' AND t.typnotnull) AS attnotnull, |
| 77 | + a.atttypmod,a.attlen,row_number() OVER (PARTITION BY a.attrelid ORDER BY a.attnum) AS attnum, |
| 78 | + pg_catalog.pg_get_expr(def.adbin, def.adrelid) AS adsrc, |
| 79 | + dsc.description,t.typbasetype,t.typtype |
| 80 | +FROM pg_catalog.pg_namespace n |
| 81 | + JOIN pg_catalog.pg_class c ON (c.relnamespace = n.oid) |
| 82 | + JOIN pg_catalog.pg_attribute a ON (a.attrelid=c.oid) |
| 83 | + JOIN pg_catalog.pg_type t ON (a.atttypid = t.oid) |
| 84 | + LEFT JOIN pg_catalog.pg_attrdef def ON (a.attrelid=def.adrelid AND a.attnum = def.adnum) |
| 85 | + LEFT JOIN pg_catalog.pg_description dsc ON (c.oid=dsc.objoid AND a.attnum = dsc.objsubid) |
| 86 | + LEFT JOIN pg_catalog.pg_class dc ON (dc.oid=dsc.classoid AND dc.relname='pg_class') |
| 87 | + LEFT JOIN pg_catalog.pg_namespace dn ON (dc.relnamespace=dn.oid AND dn.nspname='pg_catalog') |
| 88 | +WHERE a.attnum > 0 AND NOT a.attisdropped |
| 89 | + AND n.nspname IN (select * from unnest(current_schemas(false))) |
| 90 | +ORDER BY 1,2,3 |
| 91 | +|] |
| 92 | +
|
| 93 | +viewTriggers = [str| |
| 94 | +SELECT n.nspname as "Schema", c.relname AS "View", t.tgname AS "Name", t.tgenabled = 'O' AS enabled, |
| 95 | + -- pg_get_triggerdef(trig.oid) as source |
| 96 | + concat (np.nspname, '.', p.proname) AS procedure |
| 97 | +FROM pg_catalog.pg_trigger t |
| 98 | +JOIN pg_catalog.pg_class c ON t.tgrelid = c.oid |
| 99 | +JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid |
| 100 | +JOIN pg_catalog.pg_proc p ON t.tgfoid = p.oid |
| 101 | +JOIN pg_catalog.pg_namespace np ON p.pronamespace = np.oid |
| 102 | +WHERE t.tgconstraint = 0 AND n.nspname IN (select * from unnest(current_schemas(false))) |
| 103 | +ORDER BY 1,2,3 |
| 104 | +|] |
| 105 | +
|
| 106 | +viewRules = [str| |
| 107 | +SELECT n.nspname as "Schema", c.relname AS "View", r.rulename AS "Name", pg_get_ruledef(r.oid) AS definition |
| 108 | +FROM pg_rewrite r |
| 109 | +JOIN pg_class c ON c.oid = r.ev_class |
| 110 | +JOIN pg_namespace n ON c.relnamespace = n.oid |
| 111 | +WHERE n.nspname IN (select * from unnest(current_schemas(false))) AND c.relkind = 'v' |
| 112 | +ORDER BY 1,2,3 |
| 113 | +|] |
| 114 | +-} |
| 115 | + |
| 116 | +data DbView = DbView { schema :: String, name :: String, definition :: String, acl :: [Acl] } |
| 117 | + deriving(Show) |
| 118 | +mkdbv (a:b:c:d:_) = DbView a b c (cvtacl d) |
| 119 | + |
| 120 | +instance Show (Comparison DbView) where |
| 121 | + show (Equal x) = concat [sok, showView x, treset] |
| 122 | + show (LeftOnly a) = concat [azure, [charLeftArrow]," ", showView a, treset] |
| 123 | + show (RightOnly a) = concat [peach, [charRightArrow], " ", showView a, treset] |
| 124 | + show (Unequal a b) = concat [nok, showView a, treset, |
| 125 | + -- if (acl a /= acl b) then concat[ setAttr bold, "\n acls: " , treset, map show $ dbCompare a b] else "", |
| 126 | + showAclDiffs (acl a) (acl b), |
| 127 | + if (compareIgnoringWhiteSpace (definition a) (definition b)) then "" |
| 128 | + else concat [setAttr bold,"\n definition differences: \n", treset, concatMap show $ diff (definition a) (definition b)] |
| 129 | + ] |
| 130 | + |
| 131 | +instance Comparable DbView where |
| 132 | + objCmp a b = |
| 133 | + if (acl a == acl b && compareIgnoringWhiteSpace (definition a) (definition b)) then Equal a |
| 134 | + else Unequal a b |
| 135 | + |
| 136 | +compareViews (get1, get2) = do |
| 137 | + aa <- get1 viewList |
| 138 | + -- aac <- get1 viewColumns |
| 139 | + -- aat <- get1 viewTriggers |
| 140 | + -- aar <- get1 viewRules |
| 141 | + |
| 142 | + bb <- get2 viewList |
| 143 | + -- bbc <- get2 viewColumns |
| 144 | + -- bbt <- get2 viewTriggers |
| 145 | + -- bbr <- get2 viewRules |
| 146 | + |
| 147 | + let a = map (mkdbv . (map gs)) aa |
| 148 | + let b = map (mkdbv . (map gs)) bb |
| 149 | + |
| 150 | + let cc = dbCompare a b |
| 151 | + let cnt = dcount iseq cc |
| 152 | + putStr $ if (fst cnt > 0) then sok ++ (show $ fst cnt) ++ " matches, " else "" |
| 153 | + putStrLn $ if (snd cnt > 0) then concat [setColor dullRed,show $ snd cnt," differences"] else concat [sok,"no differences"] |
| 154 | + putStr $ treset |
| 155 | + return $ filter (not . iseq) cc |
| 156 | + |
| 157 | +showView x = (schema x) ++ "." ++ (name x) |
| 158 | + |
| 159 | +instance Ord DbView where |
| 160 | + compare a b = let hd p = map ($ p) [schema, name] in compare (hd a) (hd b) |
| 161 | + |
| 162 | +instance Eq DbView where |
| 163 | + (==) a b = EQ == compare a b |
0 commit comments