neon 8.4.4 → 8.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +11 -11
  2. package/dist/_chunks/{env-4WGRW2nw.js → env-BNm69Qh8.js} +2 -5
  3. package/dist/commands/config.js +11 -28
  4. package/dist/commands/dev.js +3 -3
  5. package/dist/commands/env.js +2 -3
  6. package/dist/commands/index.js +0 -2
  7. package/dist/commands/init.js +1 -2
  8. package/dist/commands/manifest.js +2 -10
  9. package/dist/commands/realtime.js +2 -1
  10. package/dist/commands/subscribe.js +24 -43
  11. package/dist/credential_io.js +1 -2
  12. package/dist/dev/env.js +1 -2
  13. package/dist/index.js +2 -2
  14. package/dist/init/run.js +1 -2
  15. package/dist/plugins/run.js +2 -1
  16. package/dist/psql/command/cmd_io.js +1 -2
  17. package/dist/psql/command/cmd_pipeline.js +1 -2
  18. package/dist/psql/core/common.js +1 -2
  19. package/dist/psql/core/startup.js +1 -2
  20. package/dist/psql/index.js +1 -2
  21. package/dist/psql/types/index.js +1 -2
  22. package/dist/skills/catalog.js +5 -0
  23. package/dist/utils/hooks.js +0 -2
  24. package/dist/utils/staging.js +1 -14
  25. package/{LICENSE → dist/vendor/postgres-meta/LICENSE} +0 -1
  26. package/dist/vendor/postgres-meta/constants.js +14 -0
  27. package/dist/vendor/postgres-meta/lib/PostgresMeta.js +49 -0
  28. package/dist/vendor/postgres-meta/lib/PostgresMetaColumnPrivileges.js +85 -0
  29. package/dist/vendor/postgres-meta/lib/PostgresMetaColumns.js +235 -0
  30. package/dist/vendor/postgres-meta/lib/PostgresMetaConfig.js +16 -0
  31. package/dist/vendor/postgres-meta/lib/PostgresMetaExtensions.js +79 -0
  32. package/dist/vendor/postgres-meta/lib/PostgresMetaForeignTables.js +81 -0
  33. package/dist/vendor/postgres-meta/lib/PostgresMetaFunctions.js +173 -0
  34. package/dist/vendor/postgres-meta/lib/PostgresMetaIndexes.js +42 -0
  35. package/dist/vendor/postgres-meta/lib/PostgresMetaMaterializedViews.js +84 -0
  36. package/dist/vendor/postgres-meta/lib/PostgresMetaPolicies.js +120 -0
  37. package/dist/vendor/postgres-meta/lib/PostgresMetaPublications.js +193 -0
  38. package/dist/vendor/postgres-meta/lib/PostgresMetaRelationships.js +87 -0
  39. package/dist/vendor/postgres-meta/lib/PostgresMetaRoles.js +203 -0
  40. package/dist/vendor/postgres-meta/lib/PostgresMetaSchemas.js +105 -0
  41. package/dist/vendor/postgres-meta/lib/PostgresMetaTablePrivileges.js +91 -0
  42. package/dist/vendor/postgres-meta/lib/PostgresMetaTables.js +183 -0
  43. package/dist/vendor/postgres-meta/lib/PostgresMetaTriggers.js +156 -0
  44. package/dist/vendor/postgres-meta/lib/PostgresMetaTypes.js +22 -0
  45. package/dist/vendor/postgres-meta/lib/PostgresMetaVersion.js +20 -0
  46. package/dist/vendor/postgres-meta/lib/PostgresMetaViews.js +83 -0
  47. package/dist/vendor/postgres-meta/lib/constants.js +8 -0
  48. package/dist/vendor/postgres-meta/lib/db.js +147 -0
  49. package/dist/vendor/postgres-meta/lib/generators.js +106 -0
  50. package/dist/vendor/postgres-meta/lib/helpers.js +26 -0
  51. package/dist/vendor/postgres-meta/lib/index.js +1 -0
  52. package/dist/vendor/postgres-meta/lib/sql/column_privileges.sql.js +154 -0
  53. package/dist/vendor/postgres-meta/lib/sql/columns.sql.js +123 -0
  54. package/dist/vendor/postgres-meta/lib/sql/common.js +1 -0
  55. package/dist/vendor/postgres-meta/lib/sql/config.sql.js +32 -0
  56. package/dist/vendor/postgres-meta/lib/sql/extensions.sql.js +20 -0
  57. package/dist/vendor/postgres-meta/lib/sql/foreign_tables.sql.js +20 -0
  58. package/dist/vendor/postgres-meta/lib/sql/functions.sql.js +141 -0
  59. package/dist/vendor/postgres-meta/lib/sql/indexes.sql.js +51 -0
  60. package/dist/vendor/postgres-meta/lib/sql/materialized_views.sql.js +21 -0
  61. package/dist/vendor/postgres-meta/lib/sql/policies.sql.js +53 -0
  62. package/dist/vendor/postgres-meta/lib/sql/publications.sql.js +46 -0
  63. package/dist/vendor/postgres-meta/lib/sql/roles.sql.js +40 -0
  64. package/dist/vendor/postgres-meta/lib/sql/schemas.sql.js +26 -0
  65. package/dist/vendor/postgres-meta/lib/sql/table.sql.js +109 -0
  66. package/dist/vendor/postgres-meta/lib/sql/table_privileges.sql.js +85 -0
  67. package/dist/vendor/postgres-meta/lib/sql/table_relationships.sql.js +52 -0
  68. package/dist/vendor/postgres-meta/lib/sql/triggers.sql.js +64 -0
  69. package/dist/vendor/postgres-meta/lib/sql/types.sql.js +65 -0
  70. package/dist/vendor/postgres-meta/lib/sql/version.sql.js +15 -0
  71. package/dist/vendor/postgres-meta/lib/sql/views.sql.js +22 -0
  72. package/dist/vendor/postgres-meta/lib/sql/views_key_dependencies.sql.js +198 -0
  73. package/dist/vendor/postgres-meta/lib/types.js +1 -0
  74. package/dist/vendor/postgres-meta/templates/typescript.js +608 -0
  75. package/package.json +16 -8
@@ -0,0 +1,40 @@
1
+ //#region src/vendor/postgres-meta/lib/sql/roles.sql.ts
2
+ const ROLES_SQL = (props) => `
3
+ -- TODO: Consider using pg_authid vs. pg_roles for unencrypted password field
4
+ SELECT
5
+ oid :: int8 AS id,
6
+ rolname AS name,
7
+ rolsuper AS is_superuser,
8
+ rolcreatedb AS can_create_db,
9
+ rolcreaterole AS can_create_role,
10
+ rolinherit AS inherit_role,
11
+ rolcanlogin AS can_login,
12
+ rolreplication AS is_replication_role,
13
+ rolbypassrls AS can_bypass_rls,
14
+ (
15
+ SELECT
16
+ COUNT(*)
17
+ FROM
18
+ pg_stat_activity
19
+ WHERE
20
+ pg_roles.rolname = pg_stat_activity.usename
21
+ ) AS active_connections,
22
+ CASE WHEN rolconnlimit = -1 THEN current_setting('max_connections') :: int8
23
+ ELSE rolconnlimit
24
+ END AS connection_limit,
25
+ rolpassword AS password,
26
+ rolvaliduntil AS valid_until,
27
+ rolconfig AS config
28
+ FROM
29
+ pg_roles
30
+ WHERE
31
+ ${props.idsFilter ? `oid ${props.idsFilter}` : "true"}
32
+ -- All default/predefined roles start with pg_: https://www.postgresql.org/docs/15/predefined-roles.html
33
+ -- The pg_ prefix is also reserved.
34
+ ${!props.includeDefaultRoles ? `AND NOT pg_catalog.starts_with(rolname, 'pg_')` : ""}
35
+ ${props.nameFilter ? `AND rolname ${props.nameFilter}` : ""}
36
+ ${props.limit ? `limit ${props.limit}` : ""}
37
+ ${props.offset ? `offset ${props.offset}` : ""}
38
+ `;
39
+ //#endregion
40
+ export { ROLES_SQL };
@@ -0,0 +1,26 @@
1
+ //#region src/vendor/postgres-meta/lib/sql/schemas.sql.ts
2
+ const SCHEMAS_SQL = (props) => `
3
+ -- Adapted from information_schema.schemata
4
+ select
5
+ n.oid::int8 as id,
6
+ n.nspname as name,
7
+ u.rolname as owner
8
+ from
9
+ pg_namespace n,
10
+ pg_roles u
11
+ where
12
+ n.nspowner = u.oid
13
+ ${props.idsFilter ? `and n.oid ${props.idsFilter}` : ""}
14
+ ${props.nameFilter ? `and n.nspname ${props.nameFilter}` : ""}
15
+ ${!props.includeSystemSchemas ? `and not pg_catalog.starts_with(n.nspname, 'pg_')` : ""}
16
+ and (
17
+ pg_has_role(n.nspowner, 'USAGE')
18
+ or has_schema_privilege(n.oid, 'CREATE, USAGE')
19
+ )
20
+ and not pg_catalog.starts_with(n.nspname, 'pg_temp_')
21
+ and not pg_catalog.starts_with(n.nspname, 'pg_toast_temp_')
22
+ ${props.limit ? `limit ${props.limit}` : ""}
23
+ ${props.offset ? `offset ${props.offset}` : ""}
24
+ `;
25
+ //#endregion
26
+ export { SCHEMAS_SQL };
@@ -0,0 +1,109 @@
1
+ //#region src/vendor/postgres-meta/lib/sql/table.sql.ts
2
+ const TABLES_SQL = (props) => `
3
+ SELECT
4
+ c.oid :: int8 AS id,
5
+ nc.nspname AS schema,
6
+ c.relname AS name,
7
+ c.relrowsecurity AS rls_enabled,
8
+ c.relforcerowsecurity AS rls_forced,
9
+ CASE
10
+ WHEN c.relreplident = 'd' THEN 'DEFAULT'
11
+ WHEN c.relreplident = 'i' THEN 'INDEX'
12
+ WHEN c.relreplident = 'f' THEN 'FULL'
13
+ ELSE 'NOTHING'
14
+ END AS replica_identity,
15
+ pg_total_relation_size(format('%I.%I', nc.nspname, c.relname)) :: int8 AS bytes,
16
+ pg_size_pretty(
17
+ pg_total_relation_size(format('%I.%I', nc.nspname, c.relname))
18
+ ) AS size,
19
+ pg_stat_get_live_tuples(c.oid) AS live_rows_estimate,
20
+ pg_stat_get_dead_tuples(c.oid) AS dead_rows_estimate,
21
+ obj_description(c.oid) AS comment,
22
+ coalesce(pk.primary_keys, '[]') as primary_keys,
23
+ coalesce(
24
+ jsonb_agg(relationships) filter (where relationships is not null),
25
+ '[]'
26
+ ) as relationships
27
+ FROM
28
+ pg_namespace nc
29
+ JOIN pg_class c ON nc.oid = c.relnamespace
30
+ left join (
31
+ select
32
+ c.oid::int8 as table_id,
33
+ jsonb_agg(
34
+ jsonb_build_object(
35
+ 'table_id', c.oid::int8,
36
+ 'schema', n.nspname,
37
+ 'table_name', c.relname,
38
+ 'name', a.attname
39
+ )
40
+ order by array_position(i.indkey, a.attnum)
41
+ ) as primary_keys
42
+ from
43
+ pg_index i
44
+ join pg_class c on i.indrelid = c.oid
45
+ join pg_namespace n on c.relnamespace = n.oid
46
+ join pg_attribute a on a.attrelid = c.oid and a.attnum = any(i.indkey)
47
+ where
48
+ ${props.schemaFilter ? `n.nspname ${props.schemaFilter} AND` : ""}
49
+ ${props.tableIdentifierFilter ? `n.nspname || '.' || c.relname ${props.tableIdentifierFilter} AND` : ""}
50
+ i.indisprimary
51
+ group by c.oid
52
+ ) as pk
53
+ on pk.table_id = c.oid
54
+ left join (
55
+ select
56
+ c.oid :: int8 as id,
57
+ c.conname as constraint_name,
58
+ nsa.nspname as source_schema,
59
+ csa.relname as source_table_name,
60
+ sa.attname as source_column_name,
61
+ nta.nspname as target_table_schema,
62
+ cta.relname as target_table_name,
63
+ ta.attname as target_column_name
64
+ from
65
+ pg_constraint c
66
+ join (
67
+ pg_attribute sa
68
+ join pg_class csa on sa.attrelid = csa.oid
69
+ join pg_namespace nsa on csa.relnamespace = nsa.oid
70
+ ) on sa.attrelid = c.conrelid and sa.attnum = any (c.conkey)
71
+ join (
72
+ pg_attribute ta
73
+ join pg_class cta on ta.attrelid = cta.oid
74
+ join pg_namespace nta on cta.relnamespace = nta.oid
75
+ ) on ta.attrelid = c.confrelid and ta.attnum = any (c.confkey)
76
+ where
77
+ ${props.schemaFilter ? `nsa.nspname ${props.schemaFilter} OR nta.nspname ${props.schemaFilter} AND` : ""}
78
+ ${props.tableIdentifierFilter ? `(nsa.nspname || '.' || csa.relname) ${props.tableIdentifierFilter} OR (nta.nspname || '.' || cta.relname) ${props.tableIdentifierFilter} AND` : ""}
79
+ c.contype = 'f'
80
+ ) as relationships
81
+ on (relationships.source_schema = nc.nspname and relationships.source_table_name = c.relname)
82
+ or (relationships.target_table_schema = nc.nspname and relationships.target_table_name = c.relname)
83
+ WHERE
84
+ ${props.schemaFilter ? `nc.nspname ${props.schemaFilter} AND` : ""}
85
+ ${props.idsFilter ? `c.oid ${props.idsFilter} AND` : ""}
86
+ ${props.tableIdentifierFilter ? `nc.nspname || '.' || c.relname ${props.tableIdentifierFilter} AND` : ""}
87
+ c.relkind IN ('r', 'p')
88
+ AND NOT pg_is_other_temp_schema(nc.oid)
89
+ AND (
90
+ pg_has_role(c.relowner, 'USAGE')
91
+ OR has_table_privilege(
92
+ c.oid,
93
+ 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER'
94
+ )
95
+ OR has_any_column_privilege(c.oid, 'SELECT, INSERT, UPDATE, REFERENCES')
96
+ )
97
+ group by
98
+ c.oid,
99
+ c.relname,
100
+ c.relrowsecurity,
101
+ c.relforcerowsecurity,
102
+ c.relreplident,
103
+ nc.nspname,
104
+ pk.primary_keys
105
+ ${props.limit ? `limit ${props.limit}` : ""}
106
+ ${props.offset ? `offset ${props.offset}` : ""}
107
+ `;
108
+ //#endregion
109
+ export { TABLES_SQL };
@@ -0,0 +1,85 @@
1
+ //#region src/vendor/postgres-meta/lib/sql/table_privileges.sql.ts
2
+ const TABLE_PRIVILEGES_SQL = (props) => `
3
+ -- Despite the name \`table_privileges\`, this includes other kinds of relations:
4
+ -- views, matviews, etc. "Relation privileges" just doesn't roll off the tongue.
5
+ --
6
+ -- For each relation, get its relacl in a jsonb format,
7
+ -- e.g.
8
+ --
9
+ -- '{postgres=arwdDxt/postgres}'
10
+ --
11
+ -- becomes
12
+ --
13
+ -- [
14
+ -- {
15
+ -- "grantee": "postgres",
16
+ -- "grantor": "postgres",
17
+ -- "is_grantable": false,
18
+ -- "privilege_type": "INSERT"
19
+ -- },
20
+ -- ...
21
+ -- ]
22
+ select
23
+ c.oid as relation_id,
24
+ nc.nspname as schema,
25
+ c.relname as name,
26
+ case
27
+ when c.relkind = 'r' then 'table'
28
+ when c.relkind = 'v' then 'view'
29
+ when c.relkind = 'm' then 'materialized_view'
30
+ when c.relkind = 'f' then 'foreign_table'
31
+ when c.relkind = 'p' then 'partitioned_table'
32
+ end as kind,
33
+ coalesce(
34
+ jsonb_agg(
35
+ jsonb_build_object(
36
+ 'grantor', grantor.rolname,
37
+ 'grantee', grantee.rolname,
38
+ 'privilege_type', _priv.privilege_type,
39
+ 'is_grantable', _priv.is_grantable
40
+ )
41
+ ) filter (where _priv is not null),
42
+ '[]'
43
+ ) as privileges
44
+ from pg_class c
45
+ join pg_namespace as nc
46
+ on nc.oid = c.relnamespace
47
+ left join lateral (
48
+ select grantor, grantee, privilege_type, is_grantable
49
+ from aclexplode(coalesce(c.relacl, acldefault('r', c.relowner)))
50
+ ) as _priv on true
51
+ left join pg_roles as grantor
52
+ on grantor.oid = _priv.grantor
53
+ left join (
54
+ select
55
+ pg_roles.oid,
56
+ pg_roles.rolname
57
+ from pg_roles
58
+ union all
59
+ select
60
+ (0)::oid as oid, 'PUBLIC'
61
+ ) as grantee (oid, rolname)
62
+ on grantee.oid = _priv.grantee
63
+ where c.relkind in ('r', 'v', 'm', 'f', 'p')
64
+ ${props.schemaFilter ? `and nc.nspname ${props.schemaFilter}` : ""}
65
+ ${props.idsFilter ? `and c.oid ${props.idsFilter}` : ""}
66
+ ${props.nameIdentifierFilter ? `and (nc.nspname || '.' || c.relname) ${props.nameIdentifierFilter}` : ""}
67
+ and not pg_is_other_temp_schema(c.relnamespace)
68
+ and (
69
+ pg_has_role(c.relowner, 'USAGE')
70
+ or has_table_privilege(
71
+ c.oid,
72
+ 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER, MAINTAIN'
73
+ )
74
+ or has_any_column_privilege(c.oid, 'SELECT, INSERT, UPDATE, REFERENCES')
75
+ )
76
+ group by
77
+ c.oid,
78
+ nc.nspname,
79
+ c.relname,
80
+ c.relkind
81
+ ${props.limit ? `limit ${props.limit}` : ""}
82
+ ${props.offset ? `offset ${props.offset}` : ""}
83
+ `;
84
+ //#endregion
85
+ export { TABLE_PRIVILEGES_SQL };
@@ -0,0 +1,52 @@
1
+ //#region src/vendor/postgres-meta/lib/sql/table_relationships.sql.ts
2
+ const TABLE_RELATIONSHIPS_SQL = (props) => `
3
+ -- Adapted from
4
+ -- https://github.com/PostgREST/postgrest/blob/f9f0f79fa914ac00c11fbf7f4c558e14821e67e2/src/PostgREST/SchemaCache.hs#L722
5
+ WITH
6
+ pks_uniques_cols AS (
7
+ SELECT
8
+ connamespace,
9
+ conrelid,
10
+ jsonb_agg(column_info.cols) as cols
11
+ FROM pg_constraint
12
+ JOIN lateral (
13
+ SELECT array_agg(cols.attname order by cols.attnum) as cols
14
+ FROM ( select unnest(conkey) as col) _
15
+ JOIN pg_attribute cols on cols.attrelid = conrelid and cols.attnum = col
16
+ ) column_info ON TRUE
17
+ WHERE
18
+ contype IN ('p', 'u') and
19
+ connamespace::regnamespace::text <> 'pg_catalog'
20
+ ${props.schemaFilter ? `and connamespace::regnamespace::text ${props.schemaFilter}` : ""}
21
+ GROUP BY connamespace, conrelid
22
+ )
23
+ SELECT
24
+ traint.conname AS foreign_key_name,
25
+ ns1.nspname AS schema,
26
+ tab.relname AS relation,
27
+ column_info.cols AS columns,
28
+ ns2.nspname AS referenced_schema,
29
+ other.relname AS referenced_relation,
30
+ column_info.refs AS referenced_columns,
31
+ (column_info.cols IN (SELECT * FROM jsonb_array_elements(pks_uqs.cols))) AS is_one_to_one
32
+ FROM pg_constraint traint
33
+ JOIN LATERAL (
34
+ SELECT
35
+ jsonb_agg(cols.attname order by ord) AS cols,
36
+ jsonb_agg(refs.attname order by ord) AS refs
37
+ FROM unnest(traint.conkey, traint.confkey) WITH ORDINALITY AS _(col, ref, ord)
38
+ JOIN pg_attribute cols ON cols.attrelid = traint.conrelid AND cols.attnum = col
39
+ JOIN pg_attribute refs ON refs.attrelid = traint.confrelid AND refs.attnum = ref
40
+ WHERE ${props.schemaFilter ? `traint.connamespace::regnamespace::text ${props.schemaFilter}` : "true"}
41
+ ) AS column_info ON TRUE
42
+ JOIN pg_namespace ns1 ON ns1.oid = traint.connamespace
43
+ JOIN pg_class tab ON tab.oid = traint.conrelid
44
+ JOIN pg_class other ON other.oid = traint.confrelid
45
+ JOIN pg_namespace ns2 ON ns2.oid = other.relnamespace
46
+ LEFT JOIN pks_uniques_cols pks_uqs ON pks_uqs.connamespace = traint.connamespace AND pks_uqs.conrelid = traint.conrelid
47
+ WHERE traint.contype = 'f'
48
+ AND traint.conparentid = 0
49
+ ${props.schemaFilter ? `and ns1.nspname ${props.schemaFilter}` : ""}
50
+ `;
51
+ //#endregion
52
+ export { TABLE_RELATIONSHIPS_SQL };
@@ -0,0 +1,64 @@
1
+ //#region src/vendor/postgres-meta/lib/sql/triggers.sql.ts
2
+ const TRIGGERS_SQL = (props) => `
3
+ SELECT
4
+ pg_t.oid AS id,
5
+ pg_t.tgrelid AS table_id,
6
+ CASE
7
+ WHEN pg_t.tgenabled = 'D' THEN 'DISABLED'
8
+ WHEN pg_t.tgenabled = 'O' THEN 'ORIGIN'
9
+ WHEN pg_t.tgenabled = 'R' THEN 'REPLICA'
10
+ WHEN pg_t.tgenabled = 'A' THEN 'ALWAYS'
11
+ END AS enabled_mode,
12
+ (
13
+ STRING_TO_ARRAY(
14
+ ENCODE(pg_t.tgargs, 'escape'), '\\000'
15
+ )
16
+ )[:pg_t.tgnargs] AS function_args,
17
+ is_t.trigger_name AS name,
18
+ is_t.event_object_table AS table,
19
+ is_t.event_object_schema AS schema,
20
+ is_t.action_condition AS condition,
21
+ is_t.action_orientation AS orientation,
22
+ is_t.action_timing AS activation,
23
+ ARRAY_AGG(is_t.event_manipulation)::text[] AS events,
24
+ pg_p.proname AS function_name,
25
+ pg_n.nspname AS function_schema
26
+ FROM
27
+ pg_trigger AS pg_t
28
+ JOIN
29
+ pg_class AS pg_c
30
+ ON pg_t.tgrelid = pg_c.oid
31
+ JOIN pg_namespace AS table_ns
32
+ ON pg_c.relnamespace = table_ns.oid
33
+ JOIN information_schema.triggers AS is_t
34
+ ON is_t.trigger_name = pg_t.tgname
35
+ AND pg_c.relname = is_t.event_object_table
36
+ AND pg_c.relnamespace = (quote_ident(is_t.event_object_schema))::regnamespace
37
+ JOIN pg_proc AS pg_p
38
+ ON pg_t.tgfoid = pg_p.oid
39
+ JOIN pg_namespace AS pg_n
40
+ ON pg_p.pronamespace = pg_n.oid
41
+ WHERE
42
+ ${props.schemaFilter ? `table_ns.nspname ${props.schemaFilter}` : "true"}
43
+ ${props.tableNameFilter ? `AND pg_c.relname ${props.tableNameFilter}` : ""}
44
+ ${props.nameFilter ? `AND is_t.trigger_name ${props.nameFilter}` : ""}
45
+ ${props.idsFilter ? `AND pg_t.oid ${props.idsFilter}` : ""}
46
+ GROUP BY
47
+ pg_t.oid,
48
+ pg_t.tgrelid,
49
+ pg_t.tgenabled,
50
+ pg_t.tgargs,
51
+ pg_t.tgnargs,
52
+ is_t.trigger_name,
53
+ is_t.event_object_table,
54
+ is_t.event_object_schema,
55
+ is_t.action_condition,
56
+ is_t.action_orientation,
57
+ is_t.action_timing,
58
+ pg_p.proname,
59
+ pg_n.nspname
60
+ ${props.limit ? `limit ${props.limit}` : ""}
61
+ ${props.offset ? `offset ${props.offset}` : ""}
62
+ `;
63
+ //#endregion
64
+ export { TRIGGERS_SQL };
@@ -0,0 +1,65 @@
1
+ //#region src/vendor/postgres-meta/lib/sql/types.sql.ts
2
+ const TYPES_SQL = (props) => `
3
+ select
4
+ t.oid::int8 as id,
5
+ t.typname as name,
6
+ n.nspname as schema,
7
+ format_type (t.oid, null) as format,
8
+ coalesce(t_enums.enums, '[]') as enums,
9
+ coalesce(t_attributes.attributes, '[]') as attributes,
10
+ obj_description (t.oid, 'pg_type') as comment,
11
+ nullif(t.typrelid::int8, 0) as type_relation_id
12
+ from
13
+ pg_type t
14
+ left join pg_namespace n on n.oid = t.typnamespace
15
+ left join (
16
+ select
17
+ enumtypid,
18
+ jsonb_agg(enumlabel order by enumsortorder) as enums
19
+ from
20
+ pg_enum
21
+ group by
22
+ enumtypid
23
+ ) as t_enums on t_enums.enumtypid = t.oid
24
+ left join (
25
+ select
26
+ oid,
27
+ jsonb_agg(
28
+ jsonb_build_object('name', a.attname, 'type_id', a.atttypid::int8)
29
+ order by a.attnum asc
30
+ ) as attributes
31
+ from
32
+ pg_class c
33
+ join pg_attribute a on a.attrelid = c.oid
34
+ where
35
+ c.relkind = 'c' and not a.attisdropped
36
+ group by
37
+ c.oid
38
+ ) as t_attributes on t_attributes.oid = t.typrelid
39
+ where
40
+ (
41
+ t.typrelid = 0
42
+ or (
43
+ select
44
+ c.relkind ${props.includeTableTypes ? `in ('c', 'r', 'v', 'm', 'p')` : `= 'c'`}
45
+ from
46
+ pg_class c
47
+ where
48
+ c.oid = t.typrelid
49
+ )
50
+ )
51
+ ${!props.includeArrayTypes ? `and not exists (
52
+ select
53
+ from
54
+ pg_type el
55
+ where
56
+ el.oid = t.typelem
57
+ and el.typarray = t.oid
58
+ )` : ""}
59
+ ${props.schemaFilter ? `and n.nspname ${props.schemaFilter}` : ""}
60
+ ${props.idsFilter ? `and t.oid ${props.idsFilter}` : ""}
61
+ ${props.limit ? `limit ${props.limit}` : ""}
62
+ ${props.offset ? `offset ${props.offset}` : ""}
63
+ `;
64
+ //#endregion
65
+ export { TYPES_SQL };
@@ -0,0 +1,15 @@
1
+ //#region src/vendor/postgres-meta/lib/sql/version.sql.ts
2
+ const VERSION_SQL = () => `
3
+ SELECT
4
+ version(),
5
+ current_setting('server_version_num') :: int8 AS version_number,
6
+ (
7
+ SELECT
8
+ COUNT(*) AS active_connections
9
+ FROM
10
+ pg_stat_activity
11
+ ) AS active_connections,
12
+ current_setting('max_connections') :: int8 AS max_connections
13
+ `;
14
+ //#endregion
15
+ export { VERSION_SQL };
@@ -0,0 +1,22 @@
1
+ //#region src/vendor/postgres-meta/lib/sql/views.sql.ts
2
+ const VIEWS_SQL = (props) => `
3
+ SELECT
4
+ c.oid :: int8 AS id,
5
+ n.nspname AS schema,
6
+ c.relname AS name,
7
+ -- See definition of information_schema.views
8
+ (pg_relation_is_updatable(c.oid, false) & 20) = 20 AS is_updatable,
9
+ obj_description(c.oid) AS comment
10
+ FROM
11
+ pg_class c
12
+ JOIN pg_namespace n ON n.oid = c.relnamespace
13
+ WHERE
14
+ ${props.schemaFilter ? `n.nspname ${props.schemaFilter} AND` : ""}
15
+ ${props.idsFilter ? `c.oid ${props.idsFilter} AND` : ""}
16
+ ${props.viewIdentifierFilter ? `(n.nspname || '.' || c.relname) ${props.viewIdentifierFilter} AND` : ""}
17
+ c.relkind = 'v'
18
+ ${props.limit ? `limit ${props.limit}` : ""}
19
+ ${props.offset ? `offset ${props.offset}` : ""}
20
+ `;
21
+ //#endregion
22
+ export { VIEWS_SQL };
@@ -0,0 +1,198 @@
1
+ //#region src/vendor/postgres-meta/lib/sql/views_key_dependencies.sql.ts
2
+ const VIEWS_KEY_DEPENDENCIES_SQL = (props) => `
3
+ -- Adapted from
4
+ -- https://github.com/PostgREST/postgrest/blob/f9f0f79fa914ac00c11fbf7f4c558e14821e67e2/src/PostgREST/SchemaCache.hs#L820
5
+ with recursive
6
+ pks_fks as (
7
+ -- pk + fk referencing col
8
+ select
9
+ contype::text as contype,
10
+ conname,
11
+ array_length(conkey, 1) as ncol,
12
+ conrelid as resorigtbl,
13
+ col as resorigcol,
14
+ ord
15
+ from pg_constraint
16
+ left join lateral unnest(conkey) with ordinality as _(col, ord) on true
17
+ where contype IN ('p', 'f')
18
+ union
19
+ -- fk referenced col
20
+ select
21
+ concat(contype, '_ref') as contype,
22
+ conname,
23
+ array_length(confkey, 1) as ncol,
24
+ confrelid,
25
+ col,
26
+ ord
27
+ from pg_constraint
28
+ left join lateral unnest(confkey) with ordinality as _(col, ord) on true
29
+ where contype='f'
30
+ ${props.schemaFilter ? `and connamespace::regnamespace::text ${props.schemaFilter}` : ""}
31
+ ),
32
+ views as (
33
+ select
34
+ c.oid as view_id,
35
+ n.nspname as view_schema,
36
+ c.relname as view_name,
37
+ r.ev_action as view_definition
38
+ from pg_class c
39
+ join pg_namespace n on n.oid = c.relnamespace
40
+ join pg_rewrite r on r.ev_class = c.oid
41
+ where c.relkind in ('v', 'm')
42
+ ${props.schemaFilter ? `and n.nspname ${props.schemaFilter}` : ""}
43
+ ),
44
+ transform_json as (
45
+ select
46
+ view_id, view_schema, view_name,
47
+ -- the following formatting is without indentation on purpose
48
+ -- to allow simple diffs, with less whitespace noise
49
+ replace(
50
+ replace(
51
+ replace(
52
+ replace(
53
+ replace(
54
+ replace(
55
+ replace(
56
+ regexp_replace(
57
+ replace(
58
+ replace(
59
+ replace(
60
+ replace(
61
+ replace(
62
+ replace(
63
+ replace(
64
+ replace(
65
+ replace(
66
+ replace(
67
+ replace(
68
+ view_definition::text,
69
+ -- This conversion to json is heavily optimized for performance.
70
+ -- The general idea is to use as few regexp_replace() calls as possible.
71
+ -- Simple replace() is a lot faster, so we jump through some hoops
72
+ -- to be able to use regexp_replace() only once.
73
+ -- This has been tested against a huge schema with 250+ different views.
74
+ -- The unit tests do NOT reflect all possible inputs. Be careful when changing this!
75
+ -- -----------------------------------------------
76
+ -- pattern | replacement | flags
77
+ -- -----------------------------------------------
78
+ -- <> in pg_node_tree is the same as null in JSON, but due to very poor performance of json_typeof
79
+ -- we need to make this an empty array here to prevent json_array_elements from throwing an error
80
+ -- when the targetList is null.
81
+ -- We'll need to put it first, to make the node protection below work for node lists that start with
82
+ -- null: (<> ..., too. This is the case for coldefexprs, when the first column does not have a default value.
83
+ '<>' , '()'
84
+ -- , is not part of the pg_node_tree format, but used in the regex.
85
+ -- This removes all , that might be part of column names.
86
+ ), ',' , ''
87
+ -- The same applies for { and }, although those are used a lot in pg_node_tree.
88
+ -- We remove the escaped ones, which might be part of column names again.
89
+ ), E'\\\\{' , ''
90
+ ), E'\\\\}' , ''
91
+ -- The fields we need are formatted as json manually to protect them from the regex.
92
+ ), ' :targetList ' , ',"targetList":'
93
+ ), ' :resno ' , ',"resno":'
94
+ ), ' :resorigtbl ' , ',"resorigtbl":'
95
+ ), ' :resorigcol ' , ',"resorigcol":'
96
+ -- Make the regex also match the node type, e.g. \`{QUERY ...\`, to remove it in one pass.
97
+ ), '{' , '{ :'
98
+ -- Protect node lists, which start with \`({\` or \`((\` from the greedy regex.
99
+ -- The extra \`{\` is removed again later.
100
+ ), '((' , '{(('
101
+ ), '({' , '{({'
102
+ -- This regex removes all unused fields to avoid the need to format all of them correctly.
103
+ -- This leads to a smaller json result as well.
104
+ -- Removal stops at \`,\` for used fields (see above) and \`}\` for the end of the current node.
105
+ -- Nesting can't be parsed correctly with a regex, so we stop at \`{\` as well and
106
+ -- add an empty key for the followig node.
107
+ ), ' :[^}{,]+' , ',"":' , 'g'
108
+ -- For performance, the regex also added those empty keys when hitting a \`,\` or \`}\`.
109
+ -- Those are removed next.
110
+ ), ',"":}' , '}'
111
+ ), ',"":,' , ','
112
+ -- This reverses the "node list protection" from above.
113
+ ), '{(' , '('
114
+ -- Every key above has been added with a \`,\` so far. The first key in an object doesn't need it.
115
+ ), '{,' , '{'
116
+ -- pg_node_tree has \`()\` around lists, but JSON uses \`[]\`
117
+ ), '(' , '['
118
+ ), ')' , ']'
119
+ -- pg_node_tree has \` \` between list items, but JSON uses \`,\`
120
+ ), ' ' , ','
121
+ )::json as view_definition
122
+ from views
123
+ ),
124
+ target_entries as(
125
+ select
126
+ view_id, view_schema, view_name,
127
+ json_array_elements(view_definition->0->'targetList') as entry
128
+ from transform_json
129
+ ),
130
+ results as(
131
+ select
132
+ view_id, view_schema, view_name,
133
+ (entry->>'resno')::int as view_column,
134
+ (entry->>'resorigtbl')::oid as resorigtbl,
135
+ (entry->>'resorigcol')::int as resorigcol
136
+ from target_entries
137
+ ),
138
+ -- CYCLE detection according to PG docs: https://www.postgresql.org/docs/current/queries-with.html#QUERIES-WITH-CYCLE
139
+ -- Can be replaced with CYCLE clause once PG v13 is EOL.
140
+ recursion(view_id, view_schema, view_name, view_column, resorigtbl, resorigcol, is_cycle, path) as(
141
+ select
142
+ r.*,
143
+ false,
144
+ ARRAY[resorigtbl]
145
+ from results r
146
+ where ${props.schemaFilter ? `view_schema ${props.schemaFilter}` : "true"}
147
+ union all
148
+ select
149
+ view.view_id,
150
+ view.view_schema,
151
+ view.view_name,
152
+ view.view_column,
153
+ tab.resorigtbl,
154
+ tab.resorigcol,
155
+ tab.resorigtbl = ANY(path),
156
+ path || tab.resorigtbl
157
+ from recursion view
158
+ join results tab on view.resorigtbl=tab.view_id and view.resorigcol=tab.view_column
159
+ where not is_cycle
160
+ ),
161
+ repeated_references as(
162
+ select
163
+ view_id,
164
+ view_schema,
165
+ view_name,
166
+ resorigtbl,
167
+ resorigcol,
168
+ array_agg(attname) as view_columns
169
+ from recursion
170
+ join pg_attribute vcol on vcol.attrelid = view_id and vcol.attnum = view_column
171
+ group by
172
+ view_id,
173
+ view_schema,
174
+ view_name,
175
+ resorigtbl,
176
+ resorigcol
177
+ )
178
+ select
179
+ sch.nspname as table_schema,
180
+ tbl.relname as table_name,
181
+ rep.view_schema,
182
+ rep.view_name,
183
+ pks_fks.conname as constraint_name,
184
+ pks_fks.contype as constraint_type,
185
+ jsonb_agg(
186
+ jsonb_build_object('table_column', col.attname, 'view_columns', view_columns) order by pks_fks.ord
187
+ ) as column_dependencies
188
+ from repeated_references rep
189
+ join pks_fks using (resorigtbl, resorigcol)
190
+ join pg_class tbl on tbl.oid = rep.resorigtbl
191
+ join pg_attribute col on col.attrelid = tbl.oid and col.attnum = rep.resorigcol
192
+ join pg_namespace sch on sch.oid = tbl.relnamespace
193
+ group by sch.nspname, tbl.relname, rep.view_schema, rep.view_name, pks_fks.conname, pks_fks.contype, pks_fks.ncol
194
+ -- make sure we only return key for which all columns are referenced in the view - no partial PKs or FKs
195
+ having ncol = array_length(array_agg(row(col.attname, view_columns) order by pks_fks.ord), 1)
196
+ `;
197
+ //#endregion
198
+ export { VIEWS_KEY_DEPENDENCIES_SQL };
@@ -0,0 +1 @@
1
+ export {};