@supabase/postgrest-typegen 0.0.1

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 (78) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +133 -0
  3. package/dist/generation/constants.d.ts +9 -0
  4. package/dist/generation/constants.js +10 -0
  5. package/dist/generation/go.d.ts +6 -0
  6. package/dist/generation/go.js +233 -0
  7. package/dist/generation/index.d.ts +5 -0
  8. package/dist/generation/index.js +5 -0
  9. package/dist/generation/python.d.ts +6 -0
  10. package/dist/generation/python.js +339 -0
  11. package/dist/generation/swift.d.ts +11 -0
  12. package/dist/generation/swift.js +309 -0
  13. package/dist/generation/typescript.d.ts +32 -0
  14. package/dist/generation/typescript.js +796 -0
  15. package/dist/index.d.ts +4 -0
  16. package/dist/index.js +4 -0
  17. package/dist/introspection/index.d.ts +27 -0
  18. package/dist/introspection/index.js +70 -0
  19. package/dist/introspection/normalize.d.ts +31 -0
  20. package/dist/introspection/normalize.js +65 -0
  21. package/dist/introspection/relationships.d.ts +47 -0
  22. package/dist/introspection/relationships.js +85 -0
  23. package/dist/introspection/sql/columns.sql.d.ts +7 -0
  24. package/dist/introspection/sql/columns.sql.js +122 -0
  25. package/dist/introspection/sql/common.d.ts +16 -0
  26. package/dist/introspection/sql/common.js +1 -0
  27. package/dist/introspection/sql/foreign_tables.sql.d.ts +6 -0
  28. package/dist/introspection/sql/foreign_tables.sql.js +18 -0
  29. package/dist/introspection/sql/functions.sql.d.ts +5 -0
  30. package/dist/introspection/sql/functions.sql.js +148 -0
  31. package/dist/introspection/sql/helpers.d.ts +12 -0
  32. package/dist/introspection/sql/helpers.js +28 -0
  33. package/dist/introspection/sql/materialized_views.sql.d.ts +4 -0
  34. package/dist/introspection/sql/materialized_views.sql.js +19 -0
  35. package/dist/introspection/sql/primary_keys.sql.d.ts +4 -0
  36. package/dist/introspection/sql/primary_keys.sql.js +32 -0
  37. package/dist/introspection/sql/schemas.sql.d.ts +6 -0
  38. package/dist/introspection/sql/schemas.sql.js +24 -0
  39. package/dist/introspection/sql/table.sql.d.ts +4 -0
  40. package/dist/introspection/sql/table.sql.js +41 -0
  41. package/dist/introspection/sql/table_relationships.sql.d.ts +2 -0
  42. package/dist/introspection/sql/table_relationships.sql.js +49 -0
  43. package/dist/introspection/sql/types.sql.d.ts +5 -0
  44. package/dist/introspection/sql/types.sql.js +65 -0
  45. package/dist/introspection/sql/views.sql.d.ts +4 -0
  46. package/dist/introspection/sql/views.sql.js +20 -0
  47. package/dist/introspection/sql/views_key_dependencies.sql.d.ts +2 -0
  48. package/dist/introspection/sql/views_key_dependencies.sql.js +195 -0
  49. package/dist/sort.d.ts +35 -0
  50. package/dist/sort.js +48 -0
  51. package/dist/types.d.ts +372 -0
  52. package/dist/types.js +207 -0
  53. package/package.json +87 -0
  54. package/src/generation/constants.ts +11 -0
  55. package/src/generation/go.ts +356 -0
  56. package/src/generation/index.ts +13 -0
  57. package/src/generation/python.ts +461 -0
  58. package/src/generation/swift.ts +481 -0
  59. package/src/generation/typescript.ts +1144 -0
  60. package/src/index.ts +4 -0
  61. package/src/introspection/index.ts +124 -0
  62. package/src/introspection/normalize.ts +71 -0
  63. package/src/introspection/relationships.ts +173 -0
  64. package/src/introspection/sql/columns.sql.ts +131 -0
  65. package/src/introspection/sql/common.ts +18 -0
  66. package/src/introspection/sql/foreign_tables.sql.ts +26 -0
  67. package/src/introspection/sql/functions.sql.ts +159 -0
  68. package/src/introspection/sql/helpers.ts +33 -0
  69. package/src/introspection/sql/materialized_views.sql.ts +25 -0
  70. package/src/introspection/sql/primary_keys.sql.ts +38 -0
  71. package/src/introspection/sql/schemas.sql.ts +32 -0
  72. package/src/introspection/sql/table.sql.ts +47 -0
  73. package/src/introspection/sql/table_relationships.sql.ts +53 -0
  74. package/src/introspection/sql/types.sql.ts +74 -0
  75. package/src/introspection/sql/views.sql.ts +26 -0
  76. package/src/introspection/sql/views_key_dependencies.sql.ts +199 -0
  77. package/src/sort.ts +102 -0
  78. package/src/types.ts +237 -0
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./types.ts";
2
+ export * from "./sort.ts";
3
+ export * from "./introspection/index.ts";
4
+ export * from "./generation/index.ts";
@@ -0,0 +1,124 @@
1
+ import {
2
+ GENERATOR_METADATA_VERSION,
3
+ type GeneratorMetadata,
4
+ } from "../types.ts";
5
+ import { normalizeRows } from "./normalize.ts";
6
+ import { listRelationships } from "./relationships.ts";
7
+ import { COLUMNS_SQL } from "./sql/columns.sql.ts";
8
+ import { FOREIGN_TABLES_SQL } from "./sql/foreign_tables.sql.ts";
9
+ import { FUNCTIONS_SQL } from "./sql/functions.sql.ts";
10
+ import { DEFAULT_SYSTEM_SCHEMAS, filterByList } from "./sql/helpers.ts";
11
+ import { MATERIALIZED_VIEWS_SQL } from "./sql/materialized_views.sql.ts";
12
+ import { PRIMARY_KEYS_SQL } from "./sql/primary_keys.sql.ts";
13
+ import { SCHEMAS_SQL } from "./sql/schemas.sql.ts";
14
+ import { TABLES_SQL } from "./sql/table.sql.ts";
15
+ import { TYPES_SQL } from "./sql/types.sql.ts";
16
+ import { VIEWS_SQL } from "./sql/views.sql.ts";
17
+
18
+ /**
19
+ * Minimal structural database interface. `pg.Pool` / `pg.Client` satisfy it,
20
+ * as do Bun and other drivers. postgres-meta injects its forked-pg pool here,
21
+ * keeping its own error handling on its side of the boundary. Errors throw;
22
+ * callers adapt.
23
+ */
24
+ export interface Queryable {
25
+ query(sql: string): Promise<{ rows: any[] }>;
26
+ }
27
+
28
+ export interface IntrospectOptions {
29
+ includedSchemas?: string[];
30
+ excludedSchemas?: string[];
31
+ }
32
+
33
+ /**
34
+ * Introspect a database into the `GeneratorMetadata` contract.
35
+ *
36
+ * Port of `getGeneratorMetadata` from `postgres-meta/src/lib/generators.ts`.
37
+ * Instead of going through the `PostgresMeta*` manager classes, it calls the
38
+ * ported SQL builders directly with the single option combination the
39
+ * generator path uses, then applies the int8 `normalize` coercion so output is
40
+ * identical regardless of driver. Connection lifecycle is the caller's
41
+ * responsibility — unlike `getGeneratorMetadata`, this does not end the pool.
42
+ */
43
+ export async function introspect(
44
+ db: Queryable,
45
+ opts: IntrospectOptions = {},
46
+ ): Promise<GeneratorMetadata> {
47
+ const includedSchemas = opts.includedSchemas ?? [];
48
+ const excludedSchemas = opts.excludedSchemas ?? [];
49
+ // Managers receive `undefined` (not an empty array) when no filter is set.
50
+ const included = includedSchemas.length > 0 ? includedSchemas : undefined;
51
+ const excluded = excludedSchemas.length > 0 ? excludedSchemas : undefined;
52
+
53
+ // Most queries exclude the system schemas by default; foreign tables and
54
+ // materialized views do not (matching the respective manager `list()` calls).
55
+ const systemExcludingFilter = filterByList(
56
+ included,
57
+ excluded,
58
+ DEFAULT_SYSTEM_SCHEMAS,
59
+ );
60
+ const plainFilter = filterByList(included, excluded);
61
+
62
+ const queryRows = async (sql: string) =>
63
+ normalizeRows((await db.query(sql)).rows);
64
+
65
+ const schemas = await queryRows(
66
+ SCHEMAS_SQL({
67
+ includeSystemSchemas: false,
68
+ nameFilter: systemExcludingFilter,
69
+ }),
70
+ );
71
+ const tables = await queryRows(
72
+ TABLES_SQL({ schemaFilter: systemExcludingFilter }),
73
+ );
74
+ const foreignTables = await queryRows(
75
+ FOREIGN_TABLES_SQL({ schemaFilter: plainFilter }),
76
+ );
77
+ const views = await queryRows(
78
+ VIEWS_SQL({ schemaFilter: systemExcludingFilter }),
79
+ );
80
+ const materializedViews = await queryRows(
81
+ MATERIALIZED_VIEWS_SQL({ schemaFilter: plainFilter }),
82
+ );
83
+ const columns = await queryRows(
84
+ COLUMNS_SQL({ schemaFilter: systemExcludingFilter }),
85
+ );
86
+ const primaryKeys = await queryRows(
87
+ PRIMARY_KEYS_SQL({ schemaFilter: systemExcludingFilter }),
88
+ );
89
+ const relationships = await listRelationships(db, {
90
+ includedSchemas: included,
91
+ excludedSchemas: excluded,
92
+ });
93
+ const functions = await queryRows(
94
+ FUNCTIONS_SQL({ schemaFilter: systemExcludingFilter }),
95
+ );
96
+ // types: includeSystemSchemas true (no schema filter), table + array types included.
97
+ const types = await queryRows(
98
+ TYPES_SQL({
99
+ schemaFilter: "",
100
+ includeTableTypes: true,
101
+ includeArrayTypes: true,
102
+ }),
103
+ );
104
+
105
+ return {
106
+ version: GENERATOR_METADATA_VERSION,
107
+ schemas: schemas.filter(
108
+ ({ name }) =>
109
+ !excludedSchemas.includes(name) &&
110
+ (includedSchemas.length === 0 || includedSchemas.includes(name)),
111
+ ),
112
+ tables,
113
+ foreignTables,
114
+ views,
115
+ materializedViews,
116
+ columns,
117
+ primaryKeys,
118
+ relationships,
119
+ functions: functions.filter(
120
+ ({ return_type }) => !["trigger", "event_trigger"].includes(return_type),
121
+ ),
122
+ types,
123
+ } as GeneratorMetadata;
124
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * int8 → number coercion replicating postgres-meta's global int8 type parser.
3
+ *
4
+ * postgres-meta installs `pg.types.setTypeParser(INT8, ...)` in
5
+ * `src/lib/db.ts`, so every `bigint`/`int8` column comes back as a JS number
6
+ * when it fits in a safe integer, and as the raw string otherwise. Stock `pg`
7
+ * (and other drivers) instead return int8 as a string. To keep
8
+ * `GeneratorMetadata` byte-identical regardless of driver, we re-apply that
9
+ * exact coercion to the known int8 top-level fields after each query.
10
+ *
11
+ * Only top-level int8 columns need this: int8 values embedded inside `jsonb`
12
+ * columns (e.g. `args[].type_id`, composite type attributes)
13
+ * are decoded by the JSON parser as numbers in every driver, matching
14
+ * postgres-meta. Composite text ids like a column's `"<oid>.<attnum>"` are not
15
+ * safe integers, so the `Number.isSafeInteger` guard leaves them untouched —
16
+ * exactly as postgres-meta's parser does, since that column is `text`, not
17
+ * int8.
18
+ */
19
+
20
+ /**
21
+ * Top-level result columns that are `int8` across the typegen introspection
22
+ * queries (schemas/tables/foreign-tables/views/materialized-views/columns/
23
+ * functions/types). Field names are unique enough across these queries that a
24
+ * name-based set faithfully reproduces the OID-based global parser.
25
+ */
26
+ const INT8_FIELDS = [
27
+ "id",
28
+ "table_id",
29
+ "type_relation_id",
30
+ "return_type_id",
31
+ "return_type_relation_id",
32
+ "bytes",
33
+ "live_rows_estimate",
34
+ "dead_rows_estimate",
35
+ ] as const;
36
+
37
+ /**
38
+ * Coerce a single value the way postgres-meta's `setTypeParser(INT8, ...)`
39
+ * does: parse to a number when it is a safe integer, otherwise keep the raw
40
+ * string. Non-string values (already-numbers, `null`) pass through unchanged.
41
+ */
42
+ export function coerceInt8Value(value: unknown): unknown {
43
+ if (typeof value !== "string") {
44
+ return value;
45
+ }
46
+ const asNumber = Number(value);
47
+ if (Number.isSafeInteger(asNumber)) {
48
+ return asNumber;
49
+ }
50
+ return value;
51
+ }
52
+
53
+ /**
54
+ * Apply {@link coerceInt8Value} to the known int8 fields of a single row,
55
+ * mutating and returning it.
56
+ */
57
+ export function normalizeRow<T extends Record<string, unknown>>(row: T): T {
58
+ for (const field of INT8_FIELDS) {
59
+ if (field in row) {
60
+ (row as Record<string, unknown>)[field] = coerceInt8Value(row[field]);
61
+ }
62
+ }
63
+ return row;
64
+ }
65
+
66
+ /** Apply {@link normalizeRow} to every row of a query result. */
67
+ export function normalizeRows<T extends Record<string, unknown>>(
68
+ rows: T[],
69
+ ): T[] {
70
+ return rows.map(normalizeRow);
71
+ }
@@ -0,0 +1,173 @@
1
+ import type { PostgresRelationship } from "../types.ts";
2
+ import type { IntrospectOptions, Queryable } from "./index.ts";
3
+ import { DEFAULT_SYSTEM_SCHEMAS, filterByList } from "./sql/helpers.ts";
4
+ import { TABLE_RELATIONSHIPS_SQL } from "./sql/table_relationships.sql.ts";
5
+ import { VIEWS_KEY_DEPENDENCIES_SQL } from "./sql/views_key_dependencies.sql.ts";
6
+
7
+ /**
8
+ * Port of `postgres-meta/src/lib/PostgresMetaRelationships.ts`.
9
+ *
10
+ * The relationships used for type generation come in two parts:
11
+ * 1. table↔table m2o/o2o relationships, read directly from `pg_constraint`
12
+ * (`TABLE_RELATIONSHIPS_SQL`); and
13
+ * 2. relationships that involve a view on either side, which PostgREST derives
14
+ * by mapping a base table's foreign key columns onto the view columns that
15
+ * select them (`VIEWS_KEY_DEPENDENCIES_SQL` + the cartesian-product
16
+ * expansion below). This second part is pure TypeScript, not SQL — ported
17
+ * verbatim from upstream.
18
+ *
19
+ * Adapted from:
20
+ * https://github.com/PostgREST/postgrest/blob/f9f0f79fa914ac00c11fbf7f4c558e14821e67e2/src/PostgREST/SchemaCache.hs#L392
21
+ */
22
+
23
+ type ColDep = {
24
+ table_column: string;
25
+ view_columns: string[];
26
+ };
27
+
28
+ export type ViewKeyDependency = {
29
+ table_schema: string;
30
+ table_name: string;
31
+ view_schema: string;
32
+ view_name: string;
33
+ constraint_name: string;
34
+ constraint_type: "f" | "f_ref" | "p" | "p_ref";
35
+ column_dependencies: ColDep[];
36
+ };
37
+
38
+ /**
39
+ * Expand the table↔table relationships into the view↔table / table↔view /
40
+ * view↔view relationships implied by the view key dependencies. Pure function
41
+ * (no database) so the cartesian-product expansion can be unit-tested directly.
42
+ *
43
+ * Ported verbatim from `PostgresMetaRelationships.list()` (the body of the
44
+ * `allViewM2oAndO2oRelationships` block).
45
+ */
46
+ export function expandViewRelationships(
47
+ allTableM2oAndO2oRelationships: PostgresRelationship[],
48
+ viewsKeyDependencies: ViewKeyDependency[],
49
+ ): PostgresRelationship[] {
50
+ return allTableM2oAndO2oRelationships.flatMap((r) => {
51
+ const expandKeyDepCols = (
52
+ colDeps: ColDep[],
53
+ ): { tableColumns: string[]; viewColumns: string[] }[] => {
54
+ const tableColumns = colDeps.map(({ table_column }) => table_column);
55
+ // https://gist.github.com/ssippe/1f92625532eef28be6974f898efb23ef?permalink_comment_id=3474581#gistcomment-3474581
56
+ const cartesianProduct = <T>(allEntries: T[][]): T[][] => {
57
+ return allEntries.reduce<T[][]>(
58
+ (results, entries) =>
59
+ results
60
+ .map((result) => entries.map((entry) => result.concat(entry)))
61
+ .reduce((subResults, result) => subResults.concat(result), []),
62
+ [[]],
63
+ );
64
+ };
65
+ const viewColumnsPermutations = cartesianProduct(
66
+ colDeps.map((cd) => cd.view_columns),
67
+ );
68
+ return viewColumnsPermutations.map((viewColumns) => ({
69
+ tableColumns,
70
+ viewColumns,
71
+ }));
72
+ };
73
+
74
+ const viewToTableKeyDeps = viewsKeyDependencies.filter(
75
+ (vkd) =>
76
+ vkd.table_schema === r.schema &&
77
+ vkd.table_name === r.relation &&
78
+ vkd.constraint_name === r.foreign_key_name &&
79
+ vkd.constraint_type === "f",
80
+ );
81
+ const tableToViewKeyDeps = viewsKeyDependencies.filter(
82
+ (vkd) =>
83
+ vkd.table_schema === r.referenced_schema &&
84
+ vkd.table_name === r.referenced_relation &&
85
+ vkd.constraint_name === r.foreign_key_name &&
86
+ vkd.constraint_type === "f_ref",
87
+ );
88
+
89
+ const viewToTableRelationships = viewToTableKeyDeps.flatMap((vtkd) =>
90
+ expandKeyDepCols(vtkd.column_dependencies).map(({ viewColumns }) => ({
91
+ foreign_key_name: r.foreign_key_name,
92
+ schema: vtkd.view_schema,
93
+ relation: vtkd.view_name,
94
+ columns: viewColumns,
95
+ is_one_to_one: r.is_one_to_one,
96
+ referenced_schema: r.referenced_schema,
97
+ referenced_relation: r.referenced_relation,
98
+ referenced_columns: r.referenced_columns,
99
+ })),
100
+ );
101
+
102
+ const tableToViewRelationships = tableToViewKeyDeps.flatMap((tvkd) =>
103
+ expandKeyDepCols(tvkd.column_dependencies).map(({ viewColumns }) => ({
104
+ foreign_key_name: r.foreign_key_name,
105
+ schema: r.schema,
106
+ relation: r.relation,
107
+ columns: r.columns,
108
+ is_one_to_one: r.is_one_to_one,
109
+ referenced_schema: tvkd.view_schema,
110
+ referenced_relation: tvkd.view_name,
111
+ referenced_columns: viewColumns,
112
+ })),
113
+ );
114
+
115
+ const viewToViewRelationships = viewToTableKeyDeps.flatMap((vtkd) =>
116
+ expandKeyDepCols(vtkd.column_dependencies).flatMap(({ viewColumns }) =>
117
+ tableToViewKeyDeps.flatMap((tvkd) =>
118
+ expandKeyDepCols(tvkd.column_dependencies).map(
119
+ ({ viewColumns: referencedViewColumns }) => ({
120
+ foreign_key_name: r.foreign_key_name,
121
+ schema: vtkd.view_schema,
122
+ relation: vtkd.view_name,
123
+ columns: viewColumns,
124
+ is_one_to_one: r.is_one_to_one,
125
+ referenced_schema: tvkd.view_schema,
126
+ referenced_relation: tvkd.view_name,
127
+ referenced_columns: referencedViewColumns,
128
+ }),
129
+ ),
130
+ ),
131
+ ),
132
+ );
133
+
134
+ return [
135
+ ...viewToTableRelationships,
136
+ ...tableToViewRelationships,
137
+ ...viewToViewRelationships,
138
+ ];
139
+ });
140
+ }
141
+
142
+ /**
143
+ * List all relationships (table↔table plus the view-derived ones) for the
144
+ * given schema filter. Mirrors `PostgresMetaRelationships.list()` with the
145
+ * generator path's defaults (`includeSystemSchemas: false`). Errors from the
146
+ * injected `Queryable` propagate by throwing — callers adapt.
147
+ */
148
+ export async function listRelationships(
149
+ db: Queryable,
150
+ { includedSchemas, excludedSchemas }: IntrospectOptions = {},
151
+ ): Promise<PostgresRelationship[]> {
152
+ const schemaFilter = filterByList(
153
+ includedSchemas,
154
+ excludedSchemas,
155
+ DEFAULT_SYSTEM_SCHEMAS,
156
+ );
157
+
158
+ const { rows: allTableM2oAndO2oRelationships } = await db.query(
159
+ TABLE_RELATIONSHIPS_SQL({ schemaFilter }),
160
+ );
161
+ const { rows: viewsKeyDependencies } = await db.query(
162
+ VIEWS_KEY_DEPENDENCIES_SQL({ schemaFilter }),
163
+ );
164
+
165
+ const allViewM2oAndO2oRelationships = expandViewRelationships(
166
+ allTableM2oAndO2oRelationships as PostgresRelationship[],
167
+ viewsKeyDependencies as ViewKeyDependency[],
168
+ );
169
+
170
+ return (allTableM2oAndO2oRelationships as PostgresRelationship[]).concat(
171
+ allViewM2oAndO2oRelationships,
172
+ );
173
+ }
@@ -0,0 +1,131 @@
1
+ import { literal } from "pg-format";
2
+ import type { SQLQueryPropsWithSchemaFilter } from "./common.ts";
3
+
4
+ export const COLUMNS_SQL = (
5
+ props: SQLQueryPropsWithSchemaFilter & {
6
+ tableIdFilter?: string;
7
+ tableIdentifierFilter?: string;
8
+ columnNameFilter?: string;
9
+ idsFilter?: string;
10
+ },
11
+ ) => /* SQL */ `
12
+ -- Adapted from information_schema.columns
13
+
14
+ SELECT
15
+ c.oid :: int8 AS table_id,
16
+ nc.nspname AS schema,
17
+ c.relname AS table,
18
+ (c.oid || '.' || a.attnum) AS id,
19
+ a.attnum AS ordinal_position,
20
+ a.attname AS name,
21
+ CASE
22
+ WHEN a.atthasdef THEN pg_get_expr(ad.adbin, ad.adrelid)
23
+ ELSE NULL
24
+ END AS default_value,
25
+ CASE
26
+ WHEN t.typtype = 'd' THEN CASE
27
+ WHEN bt.typelem <> 0 :: oid
28
+ AND bt.typlen = -1 THEN 'ARRAY'
29
+ WHEN nbt.nspname = 'pg_catalog' THEN format_type(t.typbasetype, NULL)
30
+ ELSE 'USER-DEFINED'
31
+ END
32
+ ELSE CASE
33
+ WHEN t.typelem <> 0 :: oid
34
+ AND t.typlen = -1 THEN 'ARRAY'
35
+ WHEN nt.nspname = 'pg_catalog' THEN format_type(a.atttypid, NULL)
36
+ ELSE 'USER-DEFINED'
37
+ END
38
+ END AS data_type,
39
+ COALESCE(bt.typname, t.typname) AS format,
40
+ COALESCE(nbt.nspname, nt.nspname) AS type_schema,
41
+ a.attidentity IN ('a', 'd') AS is_identity,
42
+ CASE
43
+ a.attidentity
44
+ WHEN 'a' THEN 'ALWAYS'
45
+ WHEN 'd' THEN 'BY DEFAULT'
46
+ ELSE NULL
47
+ END AS identity_generation,
48
+ a.attgenerated IN ('s') AS is_generated,
49
+ NOT (
50
+ a.attnotnull
51
+ OR t.typtype = 'd' AND t.typnotnull
52
+ ) AS is_nullable,
53
+ (
54
+ c.relkind IN ('r', 'p')
55
+ OR c.relkind IN ('v', 'f') AND pg_column_is_updatable(c.oid, a.attnum, FALSE)
56
+ ) AS is_updatable,
57
+ uniques.table_id IS NOT NULL AS is_unique,
58
+ check_constraints.definition AS "check",
59
+ array_to_json(
60
+ array(
61
+ SELECT
62
+ enumlabel
63
+ FROM
64
+ pg_catalog.pg_enum enums
65
+ WHERE
66
+ enums.enumtypid = coalesce(bt.oid, t.oid)
67
+ OR enums.enumtypid = coalesce(bt.typelem, t.typelem)
68
+ ORDER BY
69
+ enums.enumsortorder
70
+ )
71
+ ) AS enums,
72
+ col_description(c.oid, a.attnum) AS comment
73
+ FROM
74
+ pg_attribute a
75
+ LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid
76
+ AND a.attnum = ad.adnum
77
+ JOIN (
78
+ pg_class c
79
+ JOIN pg_namespace nc ON c.relnamespace = nc.oid
80
+ ) ON a.attrelid = c.oid
81
+ JOIN (
82
+ pg_type t
83
+ JOIN pg_namespace nt ON t.typnamespace = nt.oid
84
+ ) ON a.atttypid = t.oid
85
+ LEFT JOIN (
86
+ pg_type bt
87
+ JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid
88
+ ) ON t.typtype = 'd'
89
+ AND t.typbasetype = bt.oid
90
+ LEFT JOIN (
91
+ SELECT DISTINCT ON (table_id, ordinal_position)
92
+ conrelid AS table_id,
93
+ conkey[1] AS ordinal_position
94
+ FROM pg_catalog.pg_constraint
95
+ WHERE contype = 'u' AND cardinality(conkey) = 1
96
+ ) AS uniques ON uniques.table_id = c.oid AND uniques.ordinal_position = a.attnum
97
+ LEFT JOIN (
98
+ -- We only select the first column check
99
+ SELECT DISTINCT ON (table_id, ordinal_position)
100
+ conrelid AS table_id,
101
+ conkey[1] AS ordinal_position,
102
+ substring(
103
+ pg_get_constraintdef(pg_constraint.oid, true),
104
+ 8,
105
+ length(pg_get_constraintdef(pg_constraint.oid, true)) - 8
106
+ ) AS "definition"
107
+ FROM pg_constraint
108
+ WHERE contype = 'c' AND cardinality(conkey) = 1
109
+ ORDER BY table_id, ordinal_position, oid asc
110
+ ) AS check_constraints ON check_constraints.table_id = c.oid AND check_constraints.ordinal_position = a.attnum
111
+ WHERE
112
+ ${props.schemaFilter ? `nc.nspname ${props.schemaFilter} AND` : ""}
113
+ ${props.idsFilter ? `(c.oid || '.' || a.attnum) ${props.idsFilter} AND` : ""}
114
+ ${props.columnNameFilter ? `(c.relname || '.' || a.attname) ${props.columnNameFilter} AND` : ""}
115
+ ${props.tableIdFilter ? `c.oid ${props.tableIdFilter} AND` : ""}
116
+ ${props.tableIdentifierFilter ? `nc.nspname || '.' || c.relname ${props.tableIdentifierFilter} AND` : ""}
117
+ NOT pg_is_other_temp_schema(nc.oid)
118
+ AND a.attnum > 0
119
+ AND NOT a.attisdropped
120
+ AND (c.relkind IN ('r', 'v', 'm', 'f', 'p'))
121
+ AND (
122
+ pg_has_role(c.relowner, 'USAGE')
123
+ OR has_column_privilege(
124
+ c.oid,
125
+ a.attnum,
126
+ 'SELECT, INSERT, UPDATE, REFERENCES'
127
+ )
128
+ )
129
+ ${props.limit ? `limit ${literal(props.limit)}` : ""}
130
+ ${props.offset ? `offset ${literal(props.offset)}` : ""}
131
+ `;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Shared prop shapes for the SQL builders, ported from
3
+ * `postgres-meta/src/lib/sql/common.ts`. Only the variants used by the
4
+ * typegen introspection path are kept.
5
+ */
6
+ export type SQLQueryProps = {
7
+ limit?: number;
8
+ offset?: number;
9
+ };
10
+
11
+ export type SQLQueryPropsWithSchemaFilter = SQLQueryProps & {
12
+ schemaFilter?: string;
13
+ };
14
+
15
+ export type SQLQueryPropsWithSchemaFilterAndIdsFilter = SQLQueryProps & {
16
+ schemaFilter?: string;
17
+ idsFilter?: string;
18
+ };
@@ -0,0 +1,26 @@
1
+ import { literal } from "pg-format";
2
+ import type { SQLQueryProps } from "./common.ts";
3
+
4
+ export const FOREIGN_TABLES_SQL = (
5
+ props: SQLQueryProps & {
6
+ schemaFilter?: string;
7
+ idsFilter?: string;
8
+ tableIdentifierFilter?: string;
9
+ },
10
+ ) => /* SQL */ `
11
+ SELECT
12
+ c.oid :: int8 AS id,
13
+ n.nspname AS schema,
14
+ c.relname AS name,
15
+ obj_description(c.oid) AS comment
16
+ FROM
17
+ pg_class c
18
+ JOIN pg_namespace n ON n.oid = c.relnamespace
19
+ WHERE
20
+ ${props.schemaFilter ? `n.nspname ${props.schemaFilter} AND` : ""}
21
+ ${props.idsFilter ? `c.oid ${props.idsFilter} AND` : ""}
22
+ ${props.tableIdentifierFilter ? `(n.nspname || '.' || c.relname) ${props.tableIdentifierFilter} AND` : ""}
23
+ c.relkind = 'f'
24
+ ${props.limit ? `limit ${literal(props.limit)}` : ""}
25
+ ${props.offset ? `offset ${literal(props.offset)}` : ""}
26
+ `;