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.
- package/README.md +11 -11
- package/dist/_chunks/{env-4WGRW2nw.js → env-BNm69Qh8.js} +2 -5
- package/dist/commands/config.js +11 -28
- package/dist/commands/dev.js +3 -3
- package/dist/commands/env.js +2 -3
- package/dist/commands/index.js +0 -2
- package/dist/commands/init.js +1 -2
- package/dist/commands/manifest.js +2 -10
- package/dist/commands/realtime.js +2 -1
- package/dist/commands/subscribe.js +24 -43
- package/dist/credential_io.js +1 -2
- package/dist/dev/env.js +1 -2
- package/dist/index.js +2 -2
- package/dist/init/run.js +1 -2
- package/dist/plugins/run.js +2 -1
- package/dist/psql/command/cmd_io.js +1 -2
- package/dist/psql/command/cmd_pipeline.js +1 -2
- package/dist/psql/core/common.js +1 -2
- package/dist/psql/core/startup.js +1 -2
- package/dist/psql/index.js +1 -2
- package/dist/psql/types/index.js +1 -2
- package/dist/skills/catalog.js +5 -0
- package/dist/utils/hooks.js +0 -2
- package/dist/utils/staging.js +1 -14
- package/{LICENSE → dist/vendor/postgres-meta/LICENSE} +0 -1
- package/dist/vendor/postgres-meta/constants.js +14 -0
- package/dist/vendor/postgres-meta/lib/PostgresMeta.js +49 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaColumnPrivileges.js +85 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaColumns.js +235 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaConfig.js +16 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaExtensions.js +79 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaForeignTables.js +81 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaFunctions.js +173 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaIndexes.js +42 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaMaterializedViews.js +84 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaPolicies.js +120 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaPublications.js +193 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaRelationships.js +87 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaRoles.js +203 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaSchemas.js +105 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaTablePrivileges.js +91 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaTables.js +183 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaTriggers.js +156 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaTypes.js +22 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaVersion.js +20 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaViews.js +83 -0
- package/dist/vendor/postgres-meta/lib/constants.js +8 -0
- package/dist/vendor/postgres-meta/lib/db.js +147 -0
- package/dist/vendor/postgres-meta/lib/generators.js +106 -0
- package/dist/vendor/postgres-meta/lib/helpers.js +26 -0
- package/dist/vendor/postgres-meta/lib/index.js +1 -0
- package/dist/vendor/postgres-meta/lib/sql/column_privileges.sql.js +154 -0
- package/dist/vendor/postgres-meta/lib/sql/columns.sql.js +123 -0
- package/dist/vendor/postgres-meta/lib/sql/common.js +1 -0
- package/dist/vendor/postgres-meta/lib/sql/config.sql.js +32 -0
- package/dist/vendor/postgres-meta/lib/sql/extensions.sql.js +20 -0
- package/dist/vendor/postgres-meta/lib/sql/foreign_tables.sql.js +20 -0
- package/dist/vendor/postgres-meta/lib/sql/functions.sql.js +141 -0
- package/dist/vendor/postgres-meta/lib/sql/indexes.sql.js +51 -0
- package/dist/vendor/postgres-meta/lib/sql/materialized_views.sql.js +21 -0
- package/dist/vendor/postgres-meta/lib/sql/policies.sql.js +53 -0
- package/dist/vendor/postgres-meta/lib/sql/publications.sql.js +46 -0
- package/dist/vendor/postgres-meta/lib/sql/roles.sql.js +40 -0
- package/dist/vendor/postgres-meta/lib/sql/schemas.sql.js +26 -0
- package/dist/vendor/postgres-meta/lib/sql/table.sql.js +109 -0
- package/dist/vendor/postgres-meta/lib/sql/table_privileges.sql.js +85 -0
- package/dist/vendor/postgres-meta/lib/sql/table_relationships.sql.js +52 -0
- package/dist/vendor/postgres-meta/lib/sql/triggers.sql.js +64 -0
- package/dist/vendor/postgres-meta/lib/sql/types.sql.js +65 -0
- package/dist/vendor/postgres-meta/lib/sql/version.sql.js +15 -0
- package/dist/vendor/postgres-meta/lib/sql/views.sql.js +22 -0
- package/dist/vendor/postgres-meta/lib/sql/views_key_dependencies.sql.js +198 -0
- package/dist/vendor/postgres-meta/lib/types.js +1 -0
- package/dist/vendor/postgres-meta/templates/typescript.js +608 -0
- 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 {};
|