Skip to content

Commit f045594

Browse files
committed
perf: inline SQL information_schema joins
Also removed unused fields in the SQL templates.
1 parent 019541e commit f045594

File tree

8 files changed

+261
-109
lines changed

8 files changed

+261
-109
lines changed

src/api/columns.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,10 @@ ${commentSql}`,
160160
)
161161
}
162162
const getColumnSqlize = (tableId: number, name: string) => {
163-
return SQL``.append(columns).append(SQL` WHERE c.oid = ${tableId} AND column_name = ${name}`)
163+
return SQL``.append(columns).append(SQL` AND c.oid = ${tableId} AND a.attname = ${name}`)
164164
}
165165
const getColumnByPosSqlize = (tableId: number, ordinalPos: number) => {
166-
return SQL``
167-
.append(columns)
168-
.append(SQL` WHERE c.oid = ${tableId} AND ordinal_position = ${ordinalPos}`)
166+
return SQL``.append(columns).append(SQL` AND c.oid = ${tableId} AND a.attnum = ${ordinalPos}`)
169167
}
170168
const alterColumnSqlize = (
171169
old: any,

src/api/schemas.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,11 @@ router.delete('/:id', async (req, res) => {
103103

104104
// Helpers
105105
const selectSingleSql = (id: number) => {
106-
const query = SQL``.append(schemas).append(SQL` where nsp.oid = ${id}`)
106+
const query = SQL``.append(schemas).append(SQL` AND n.oid = ${id}`)
107107
return query
108108
}
109109
const selectSingleByName = (name: string) => {
110-
const query = SQL``.append(schemas).append(SQL` where schema_name = ${name}`)
110+
const query = SQL``.append(schemas).append(SQL` AND n.nspname = ${name}`)
111111
return query
112112
}
113113
const createSchema = (name: string, owner: string = 'postgres') => {

src/api/tables.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,12 @@ router.delete('/:id', async (req, res) => {
126126
const getTablesSql = (sqlTemplates) => {
127127
const { columns, grants, policies, primary_keys, relationships, tables } = sqlTemplates
128128
return `
129-
WITH tables AS MATERIALIZED ( ${tables} ),
130-
columns AS MATERIALIZED ( ${columns} ),
131-
grants AS MATERIALIZED ( ${grants} ),
132-
policies AS MATERIALIZED ( ${policies} ),
133-
primary_keys AS MATERIALIZED ( ${primary_keys} ),
134-
relationships AS MATERIALIZED ( ${relationships} )
129+
WITH tables AS ( ${tables} ),
130+
columns AS ( ${columns} ),
131+
grants AS ( ${grants} ),
132+
policies AS ( ${policies} ),
133+
primary_keys AS ( ${primary_keys} ),
134+
relationships AS ( ${relationships} )
135135
SELECT
136136
*,
137137
${coalesceRowsToArray('columns', 'SELECT * FROM columns WHERE columns.table_id = tables.id')},
@@ -151,8 +151,8 @@ const getTablesSql = (sqlTemplates) => {
151151
FROM
152152
relationships
153153
WHERE
154-
(relationships.source_schema = tables.schema AND relationships.source_table_name = tables.name)
155-
OR (relationships.target_table_schema = tables.schema AND relationships.target_table_name = tables.name)`
154+
(relationships.source_schema :: text = tables.schema AND relationships.source_table_name :: text = tables.name)
155+
OR (relationships.target_table_schema :: text = tables.schema AND relationships.target_table_name :: text = tables.name)`
156156
)}
157157
FROM tables;`.trim()
158158
}
@@ -184,8 +184,8 @@ const selectSingleSql = (sqlTemplates: { [key: string]: string }, id: number) =>
184184
FROM
185185
relationships
186186
WHERE
187-
(relationships.source_schema = tables.schema AND relationships.source_table_name = tables.name)
188-
OR (relationships.target_table_schema = tables.schema AND relationships.target_table_name = tables.name)`
187+
(relationships.source_schema :: text = tables.schema AND relationships.source_table_name :: text = tables.name)
188+
OR (relationships.target_table_schema :: text = tables.schema AND relationships.target_table_name :: text = tables.name)`
189189
)}
190190
FROM tables;`.trim()
191191
}
@@ -197,9 +197,9 @@ const selectSingleByName = (
197197
) => {
198198
const { columns, grants, policies, primary_keys, relationships, tables } = sqlTemplates
199199
return `
200-
WITH tables AS ( ${tables} AND table_schema = ${format.literal(
200+
WITH tables AS ( ${tables} AND nc.nspname = ${format.literal(
201201
schema
202-
)} AND table_name = ${format.literal(name)} ),
202+
)} AND c.relname = ${format.literal(name)} ),
203203
columns AS ( ${columns} ),
204204
grants AS ( ${grants} ),
205205
policies AS ( ${policies} ),
@@ -224,8 +224,8 @@ const selectSingleByName = (
224224
FROM
225225
relationships
226226
WHERE
227-
(relationships.source_schema = tables.schema AND relationships.source_table_name = tables.name)
228-
OR (relationships.target_table_schema = tables.schema AND relationships.target_table_name = tables.name)`
227+
(relationships.source_schema :: text = tables.schema AND relationships.source_table_name :: text = tables.name)
228+
OR (relationships.target_table_schema :: text = tables.schema AND relationships.target_table_name :: text = tables.name)`
229229
)}
230230
FROM tables;`.trim()
231231
}

src/lib/sql/columns.sql

Lines changed: 83 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,97 @@
1+
-- Adapted from information_schema.columns
2+
13
SELECT
24
c.oid :: int8 AS table_id,
3-
table_schema AS schema,
4-
table_name AS table,
5-
(c.oid || '.' || ordinal_position) AS id,
6-
ordinal_position,
7-
column_name AS name,
8-
column_default AS default_value,
9-
data_type,
10-
udt_name AS format,
11-
col_description(c.oid, ordinal_position) AS description,
12-
is_identity :: boolean,
13-
identity_generation,
14-
is_nullable :: boolean,
15-
is_updatable :: boolean,
5+
nc.nspname AS schema,
6+
c.relname AS table,
7+
(c.oid || '.' || a.attnum) AS id,
8+
a.attnum AS ordinal_position,
9+
a.attname AS name,
10+
CASE
11+
WHEN a.atthasdef THEN pg_get_expr(ad.adbin, ad.adrelid)
12+
ELSE NULL
13+
END AS default_value,
14+
CASE
15+
WHEN t.typtype = 'd' THEN CASE
16+
WHEN bt.typelem <> 0 :: oid
17+
AND bt.typlen = -1 THEN 'ARRAY'
18+
WHEN nbt.nspname = 'pg_catalog' THEN format_type(t.typbasetype, NULL)
19+
ELSE 'USER-DEFINED'
20+
END
21+
ELSE CASE
22+
WHEN t.typelem <> 0 :: oid
23+
AND t.typlen = -1 THEN 'ARRAY'
24+
WHEN nt.nspname = 'pg_catalog' THEN format_type(a.atttypid, NULL)
25+
ELSE 'USER-DEFINED'
26+
END
27+
END AS data_type,
28+
COALESCE(bt.typname, t.typname) AS format,
29+
CASE
30+
WHEN a.attidentity IN ('a', 'd') THEN TRUE
31+
ELSE FALSE
32+
END AS is_identity,
33+
CASE
34+
a.attidentity
35+
WHEN 'a' THEN 'ALWAYS'
36+
WHEN 'd' THEN 'BY DEFAULT'
37+
ELSE NULL
38+
END AS identity_generation,
39+
CASE
40+
WHEN a.attnotnull
41+
OR t.typtype = 'd'
42+
AND t.typnotnull THEN FALSE
43+
ELSE TRUE
44+
END AS is_nullable,
45+
CASE
46+
WHEN (
47+
c.relkind IN ('r', 'p')
48+
)
49+
OR (
50+
c.relkind IN ('v', 'f')
51+
)
52+
AND pg_column_is_updatable(c.oid, a.attnum, FALSE) THEN TRUE
53+
ELSE FALSE
54+
END AS is_updatable,
1655
array_to_json(
1756
array(
1857
SELECT
1958
enumlabel
2059
FROM
2160
pg_catalog.pg_enum enums
2261
WHERE
23-
udt_name = pg_catalog.Format_type(enums.enumtypid :: regclass, NULL)
62+
COALESCE(bt.typname, t.typname) = format_type(enums.enumtypid :: regclass, NULL)
2463
ORDER BY
2564
enums.enumsortorder
2665
)
2766
) AS enums,
28-
col_description(c.oid, ordinal_position) AS comment
67+
col_description(c.oid, a.attnum) AS comment
2968
FROM
30-
information_schema.columns
31-
JOIN pg_class c ON quote_ident(table_schema) :: regnamespace = c.relnamespace
32-
AND c.relname = table_name
69+
pg_attribute a
70+
LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid
71+
AND a.attnum = ad.adnum
72+
JOIN (
73+
pg_class c
74+
JOIN pg_namespace nc ON c.relnamespace = nc.oid
75+
) ON a.attrelid = c.oid
76+
JOIN (
77+
pg_type t
78+
JOIN pg_namespace nt ON t.typnamespace = nt.oid
79+
) ON a.atttypid = t.oid
80+
LEFT JOIN (
81+
pg_type bt
82+
JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid
83+
) ON t.typtype = 'd'
84+
AND t.typbasetype = bt.oid
85+
WHERE
86+
NOT pg_is_other_temp_schema(nc.oid)
87+
AND a.attnum > 0
88+
AND NOT a.attisdropped
89+
AND (c.relkind IN ('r', 'v', 'f', 'p'))
90+
AND (
91+
pg_has_role(c.relowner, 'USAGE')
92+
OR has_column_privilege(
93+
c.oid,
94+
a.attnum,
95+
'SELECT, INSERT, UPDATE, REFERENCES'
96+
)
97+
)

src/lib/sql/grants.sql

Lines changed: 101 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,104 @@
1+
-- Adapted from information_schema.role_table_grants
2+
13
SELECT
24
c.oid :: int8 AS table_id,
3-
grantor,
4-
grantee,
5-
table_catalog AS catalog,
6-
table_schema AS schema,
7-
table_name,
8-
privilege_type,
9-
is_grantable :: boolean,
10-
with_hierarchy :: boolean
5+
u_grantor.rolname AS grantor,
6+
grantee.rolname AS grantee,
7+
nc.nspname AS schema,
8+
c.relname AS table_name,
9+
c.prtype AS privilege_type,
10+
CASE
11+
WHEN pg_has_role(grantee.oid, c.relowner, 'USAGE')
12+
OR c.grantable THEN TRUE
13+
ELSE FALSE
14+
END AS is_grantable,
15+
CASE
16+
WHEN c.prtype = 'SELECT' THEN TRUE
17+
ELSE FALSE
18+
END AS with_hierarchy
1119
FROM
12-
information_schema.role_table_grants
13-
JOIN pg_class c ON quote_ident(table_schema) :: regnamespace = c.relnamespace
14-
AND table_name = c.relname
20+
(
21+
SELECT
22+
pg_class.oid,
23+
pg_class.relname,
24+
pg_class.relnamespace,
25+
pg_class.relkind,
26+
pg_class.relowner,
27+
(
28+
aclexplode(
29+
COALESCE(
30+
pg_class.relacl,
31+
acldefault('r', pg_class.relowner)
32+
)
33+
)
34+
).grantor AS grantor,
35+
(
36+
aclexplode(
37+
COALESCE(
38+
pg_class.relacl,
39+
acldefault('r', pg_class.relowner)
40+
)
41+
)
42+
).grantee AS grantee,
43+
(
44+
aclexplode(
45+
COALESCE(
46+
pg_class.relacl,
47+
acldefault('r', pg_class.relowner)
48+
)
49+
)
50+
).privilege_type AS privilege_type,
51+
(
52+
aclexplode(
53+
COALESCE(
54+
pg_class.relacl,
55+
acldefault('r', pg_class.relowner)
56+
)
57+
)
58+
).is_grantable AS is_grantable
59+
FROM
60+
pg_class
61+
) c(
62+
oid,
63+
relname,
64+
relnamespace,
65+
relkind,
66+
relowner,
67+
grantor,
68+
grantee,
69+
prtype,
70+
grantable
71+
),
72+
pg_namespace nc,
73+
pg_authid u_grantor,
74+
(
75+
SELECT
76+
pg_authid.oid,
77+
pg_authid.rolname
78+
FROM
79+
pg_authid
80+
UNION ALL
81+
SELECT
82+
0 :: oid AS oid,
83+
'PUBLIC'
84+
) grantee(oid, rolname)
85+
WHERE
86+
c.relnamespace = nc.oid
87+
AND (c.relkind IN ('r', 'v', 'f', 'p'))
88+
AND c.grantee = grantee.oid
89+
AND c.grantor = u_grantor.oid
90+
AND (
91+
c.prtype IN (
92+
'INSERT',
93+
'SELECT',
94+
'UPDATE',
95+
'DELETE',
96+
'TRUNCATE',
97+
'REFERENCES',
98+
'TRIGGER'
99+
)
100+
)
101+
AND (
102+
pg_has_role(u_grantor.oid, 'USAGE')
103+
OR pg_has_role(grantee.oid, 'USAGE')
104+
)

src/lib/sql/relationships.sql

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
SELECT
2-
tc.table_schema AS source_schema,
3-
tc.table_name AS source_table_name,
4-
kcu.column_name AS source_column_name,
5-
ccu.table_schema AS target_table_schema,
6-
ccu.table_name AS target_table_name,
7-
ccu.column_name AS target_column_name,
8-
tc.constraint_name
2+
c.oid :: int8 AS id,
3+
c.conname AS constraint_name,
4+
csa.relnamespace :: regnamespace AS source_schema,
5+
c.conrelid :: regclass AS source_table_name,
6+
sa.attname AS source_column_name,
7+
cta.relnamespace :: regnamespace AS target_table_schema,
8+
c.confrelid :: regclass AS target_table_name,
9+
ta.attname AS target_column_name
910
FROM
10-
information_schema.table_constraints AS tc
11-
JOIN information_schema.key_column_usage AS kcu USING (constraint_schema, constraint_name)
12-
JOIN information_schema.constraint_column_usage AS ccu USING (constraint_schema, constraint_name)
11+
pg_constraint c
12+
JOIN (
13+
pg_attribute sa
14+
JOIN pg_class csa ON sa.attrelid = csa.oid
15+
) ON sa.attrelid = c.conrelid
16+
AND sa.attnum = ANY (c.conkey)
17+
JOIN (
18+
pg_attribute ta
19+
JOIN pg_class cta ON ta.attrelid = cta.oid
20+
) ON ta.attrelid = c.confrelid
21+
AND ta.attnum = ANY (c.confkey)
1322
WHERE
14-
tc.constraint_type = 'FOREIGN KEY'
23+
c.contype = 'f'

src/lib/sql/schemas.sql

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1+
-- Adapted from infromation_schema.schemata
2+
13
SELECT
2-
nsp.oid AS id,
3-
catalog_name,
4-
schema_name AS name,
5-
schema_owner AS owner,
6-
default_character_set_catalog,
7-
default_character_set_schema,
8-
default_character_set_name,
9-
sql_path
4+
n.oid :: int8 AS id,
5+
n.nspname AS name,
6+
u.rolname AS owner
107
FROM
11-
information_schema.schemata
12-
JOIN pg_namespace nsp ON schema_name = nsp.nspname
8+
pg_namespace n,
9+
pg_authid u
10+
WHERE
11+
n.nspowner = u.oid
12+
AND (
13+
pg_has_role(n.nspowner, 'USAGE')
14+
OR has_schema_privilege(n.oid, 'CREATE, USAGE')
15+
)

0 commit comments

Comments
 (0)