@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
@@ -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";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./types.js";
2
+ export * from "./sort.js";
3
+ export * from "./introspection/index.js";
4
+ export * from "./generation/index.js";
@@ -0,0 +1,27 @@
1
+ import { type GeneratorMetadata } from "../types.ts";
2
+ /**
3
+ * Minimal structural database interface. `pg.Pool` / `pg.Client` satisfy it,
4
+ * as do Bun and other drivers. postgres-meta injects its forked-pg pool here,
5
+ * keeping its own error handling on its side of the boundary. Errors throw;
6
+ * callers adapt.
7
+ */
8
+ export interface Queryable {
9
+ query(sql: string): Promise<{
10
+ rows: any[];
11
+ }>;
12
+ }
13
+ export interface IntrospectOptions {
14
+ includedSchemas?: string[];
15
+ excludedSchemas?: string[];
16
+ }
17
+ /**
18
+ * Introspect a database into the `GeneratorMetadata` contract.
19
+ *
20
+ * Port of `getGeneratorMetadata` from `postgres-meta/src/lib/generators.ts`.
21
+ * Instead of going through the `PostgresMeta*` manager classes, it calls the
22
+ * ported SQL builders directly with the single option combination the
23
+ * generator path uses, then applies the int8 `normalize` coercion so output is
24
+ * identical regardless of driver. Connection lifecycle is the caller's
25
+ * responsibility — unlike `getGeneratorMetadata`, this does not end the pool.
26
+ */
27
+ export declare function introspect(db: Queryable, opts?: IntrospectOptions): Promise<GeneratorMetadata>;
@@ -0,0 +1,70 @@
1
+ import { GENERATOR_METADATA_VERSION, } from "../types.js";
2
+ import { normalizeRows } from "./normalize.js";
3
+ import { listRelationships } from "./relationships.js";
4
+ import { COLUMNS_SQL } from "./sql/columns.sql.js";
5
+ import { FOREIGN_TABLES_SQL } from "./sql/foreign_tables.sql.js";
6
+ import { FUNCTIONS_SQL } from "./sql/functions.sql.js";
7
+ import { DEFAULT_SYSTEM_SCHEMAS, filterByList } from "./sql/helpers.js";
8
+ import { MATERIALIZED_VIEWS_SQL } from "./sql/materialized_views.sql.js";
9
+ import { PRIMARY_KEYS_SQL } from "./sql/primary_keys.sql.js";
10
+ import { SCHEMAS_SQL } from "./sql/schemas.sql.js";
11
+ import { TABLES_SQL } from "./sql/table.sql.js";
12
+ import { TYPES_SQL } from "./sql/types.sql.js";
13
+ import { VIEWS_SQL } from "./sql/views.sql.js";
14
+ /**
15
+ * Introspect a database into the `GeneratorMetadata` contract.
16
+ *
17
+ * Port of `getGeneratorMetadata` from `postgres-meta/src/lib/generators.ts`.
18
+ * Instead of going through the `PostgresMeta*` manager classes, it calls the
19
+ * ported SQL builders directly with the single option combination the
20
+ * generator path uses, then applies the int8 `normalize` coercion so output is
21
+ * identical regardless of driver. Connection lifecycle is the caller's
22
+ * responsibility — unlike `getGeneratorMetadata`, this does not end the pool.
23
+ */
24
+ export async function introspect(db, opts = {}) {
25
+ const includedSchemas = opts.includedSchemas ?? [];
26
+ const excludedSchemas = opts.excludedSchemas ?? [];
27
+ // Managers receive `undefined` (not an empty array) when no filter is set.
28
+ const included = includedSchemas.length > 0 ? includedSchemas : undefined;
29
+ const excluded = excludedSchemas.length > 0 ? excludedSchemas : undefined;
30
+ // Most queries exclude the system schemas by default; foreign tables and
31
+ // materialized views do not (matching the respective manager `list()` calls).
32
+ const systemExcludingFilter = filterByList(included, excluded, DEFAULT_SYSTEM_SCHEMAS);
33
+ const plainFilter = filterByList(included, excluded);
34
+ const queryRows = async (sql) => normalizeRows((await db.query(sql)).rows);
35
+ const schemas = await queryRows(SCHEMAS_SQL({
36
+ includeSystemSchemas: false,
37
+ nameFilter: systemExcludingFilter,
38
+ }));
39
+ const tables = await queryRows(TABLES_SQL({ schemaFilter: systemExcludingFilter }));
40
+ const foreignTables = await queryRows(FOREIGN_TABLES_SQL({ schemaFilter: plainFilter }));
41
+ const views = await queryRows(VIEWS_SQL({ schemaFilter: systemExcludingFilter }));
42
+ const materializedViews = await queryRows(MATERIALIZED_VIEWS_SQL({ schemaFilter: plainFilter }));
43
+ const columns = await queryRows(COLUMNS_SQL({ schemaFilter: systemExcludingFilter }));
44
+ const primaryKeys = await queryRows(PRIMARY_KEYS_SQL({ schemaFilter: systemExcludingFilter }));
45
+ const relationships = await listRelationships(db, {
46
+ includedSchemas: included,
47
+ excludedSchemas: excluded,
48
+ });
49
+ const functions = await queryRows(FUNCTIONS_SQL({ schemaFilter: systemExcludingFilter }));
50
+ // types: includeSystemSchemas true (no schema filter), table + array types included.
51
+ const types = await queryRows(TYPES_SQL({
52
+ schemaFilter: "",
53
+ includeTableTypes: true,
54
+ includeArrayTypes: true,
55
+ }));
56
+ return {
57
+ version: GENERATOR_METADATA_VERSION,
58
+ schemas: schemas.filter(({ name }) => !excludedSchemas.includes(name) &&
59
+ (includedSchemas.length === 0 || includedSchemas.includes(name))),
60
+ tables,
61
+ foreignTables,
62
+ views,
63
+ materializedViews,
64
+ columns,
65
+ primaryKeys,
66
+ relationships,
67
+ functions: functions.filter(({ return_type }) => !["trigger", "event_trigger"].includes(return_type)),
68
+ types,
69
+ };
70
+ }
@@ -0,0 +1,31 @@
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
+ * Coerce a single value the way postgres-meta's `setTypeParser(INT8, ...)`
21
+ * does: parse to a number when it is a safe integer, otherwise keep the raw
22
+ * string. Non-string values (already-numbers, `null`) pass through unchanged.
23
+ */
24
+ export declare function coerceInt8Value(value: unknown): unknown;
25
+ /**
26
+ * Apply {@link coerceInt8Value} to the known int8 fields of a single row,
27
+ * mutating and returning it.
28
+ */
29
+ export declare function normalizeRow<T extends Record<string, unknown>>(row: T): T;
30
+ /** Apply {@link normalizeRow} to every row of a query result. */
31
+ export declare function normalizeRows<T extends Record<string, unknown>>(rows: T[]): T[];
@@ -0,0 +1,65 @@
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
+ * Top-level result columns that are `int8` across the typegen introspection
21
+ * queries (schemas/tables/foreign-tables/views/materialized-views/columns/
22
+ * functions/types). Field names are unique enough across these queries that a
23
+ * name-based set faithfully reproduces the OID-based global parser.
24
+ */
25
+ const INT8_FIELDS = [
26
+ "id",
27
+ "table_id",
28
+ "type_relation_id",
29
+ "return_type_id",
30
+ "return_type_relation_id",
31
+ "bytes",
32
+ "live_rows_estimate",
33
+ "dead_rows_estimate",
34
+ ];
35
+ /**
36
+ * Coerce a single value the way postgres-meta's `setTypeParser(INT8, ...)`
37
+ * does: parse to a number when it is a safe integer, otherwise keep the raw
38
+ * string. Non-string values (already-numbers, `null`) pass through unchanged.
39
+ */
40
+ export function coerceInt8Value(value) {
41
+ if (typeof value !== "string") {
42
+ return value;
43
+ }
44
+ const asNumber = Number(value);
45
+ if (Number.isSafeInteger(asNumber)) {
46
+ return asNumber;
47
+ }
48
+ return value;
49
+ }
50
+ /**
51
+ * Apply {@link coerceInt8Value} to the known int8 fields of a single row,
52
+ * mutating and returning it.
53
+ */
54
+ export function normalizeRow(row) {
55
+ for (const field of INT8_FIELDS) {
56
+ if (field in row) {
57
+ row[field] = coerceInt8Value(row[field]);
58
+ }
59
+ }
60
+ return row;
61
+ }
62
+ /** Apply {@link normalizeRow} to every row of a query result. */
63
+ export function normalizeRows(rows) {
64
+ return rows.map(normalizeRow);
65
+ }
@@ -0,0 +1,47 @@
1
+ import type { PostgresRelationship } from "../types.ts";
2
+ import type { IntrospectOptions, Queryable } from "./index.ts";
3
+ /**
4
+ * Port of `postgres-meta/src/lib/PostgresMetaRelationships.ts`.
5
+ *
6
+ * The relationships used for type generation come in two parts:
7
+ * 1. table↔table m2o/o2o relationships, read directly from `pg_constraint`
8
+ * (`TABLE_RELATIONSHIPS_SQL`); and
9
+ * 2. relationships that involve a view on either side, which PostgREST derives
10
+ * by mapping a base table's foreign key columns onto the view columns that
11
+ * select them (`VIEWS_KEY_DEPENDENCIES_SQL` + the cartesian-product
12
+ * expansion below). This second part is pure TypeScript, not SQL — ported
13
+ * verbatim from upstream.
14
+ *
15
+ * Adapted from:
16
+ * https://github.com/PostgREST/postgrest/blob/f9f0f79fa914ac00c11fbf7f4c558e14821e67e2/src/PostgREST/SchemaCache.hs#L392
17
+ */
18
+ type ColDep = {
19
+ table_column: string;
20
+ view_columns: string[];
21
+ };
22
+ export type ViewKeyDependency = {
23
+ table_schema: string;
24
+ table_name: string;
25
+ view_schema: string;
26
+ view_name: string;
27
+ constraint_name: string;
28
+ constraint_type: "f" | "f_ref" | "p" | "p_ref";
29
+ column_dependencies: ColDep[];
30
+ };
31
+ /**
32
+ * Expand the table↔table relationships into the view↔table / table↔view /
33
+ * view↔view relationships implied by the view key dependencies. Pure function
34
+ * (no database) so the cartesian-product expansion can be unit-tested directly.
35
+ *
36
+ * Ported verbatim from `PostgresMetaRelationships.list()` (the body of the
37
+ * `allViewM2oAndO2oRelationships` block).
38
+ */
39
+ export declare function expandViewRelationships(allTableM2oAndO2oRelationships: PostgresRelationship[], viewsKeyDependencies: ViewKeyDependency[]): PostgresRelationship[];
40
+ /**
41
+ * List all relationships (table↔table plus the view-derived ones) for the
42
+ * given schema filter. Mirrors `PostgresMetaRelationships.list()` with the
43
+ * generator path's defaults (`includeSystemSchemas: false`). Errors from the
44
+ * injected `Queryable` propagate by throwing — callers adapt.
45
+ */
46
+ export declare function listRelationships(db: Queryable, { includedSchemas, excludedSchemas }?: IntrospectOptions): Promise<PostgresRelationship[]>;
47
+ export {};
@@ -0,0 +1,85 @@
1
+ import { DEFAULT_SYSTEM_SCHEMAS, filterByList } from "./sql/helpers.js";
2
+ import { TABLE_RELATIONSHIPS_SQL } from "./sql/table_relationships.sql.js";
3
+ import { VIEWS_KEY_DEPENDENCIES_SQL } from "./sql/views_key_dependencies.sql.js";
4
+ /**
5
+ * Expand the table↔table relationships into the view↔table / table↔view /
6
+ * view↔view relationships implied by the view key dependencies. Pure function
7
+ * (no database) so the cartesian-product expansion can be unit-tested directly.
8
+ *
9
+ * Ported verbatim from `PostgresMetaRelationships.list()` (the body of the
10
+ * `allViewM2oAndO2oRelationships` block).
11
+ */
12
+ export function expandViewRelationships(allTableM2oAndO2oRelationships, viewsKeyDependencies) {
13
+ return allTableM2oAndO2oRelationships.flatMap((r) => {
14
+ const expandKeyDepCols = (colDeps) => {
15
+ const tableColumns = colDeps.map(({ table_column }) => table_column);
16
+ // https://gist.github.com/ssippe/1f92625532eef28be6974f898efb23ef?permalink_comment_id=3474581#gistcomment-3474581
17
+ const cartesianProduct = (allEntries) => {
18
+ return allEntries.reduce((results, entries) => results
19
+ .map((result) => entries.map((entry) => result.concat(entry)))
20
+ .reduce((subResults, result) => subResults.concat(result), []), [[]]);
21
+ };
22
+ const viewColumnsPermutations = cartesianProduct(colDeps.map((cd) => cd.view_columns));
23
+ return viewColumnsPermutations.map((viewColumns) => ({
24
+ tableColumns,
25
+ viewColumns,
26
+ }));
27
+ };
28
+ const viewToTableKeyDeps = viewsKeyDependencies.filter((vkd) => vkd.table_schema === r.schema &&
29
+ vkd.table_name === r.relation &&
30
+ vkd.constraint_name === r.foreign_key_name &&
31
+ vkd.constraint_type === "f");
32
+ const tableToViewKeyDeps = viewsKeyDependencies.filter((vkd) => vkd.table_schema === r.referenced_schema &&
33
+ vkd.table_name === r.referenced_relation &&
34
+ vkd.constraint_name === r.foreign_key_name &&
35
+ vkd.constraint_type === "f_ref");
36
+ const viewToTableRelationships = viewToTableKeyDeps.flatMap((vtkd) => expandKeyDepCols(vtkd.column_dependencies).map(({ viewColumns }) => ({
37
+ foreign_key_name: r.foreign_key_name,
38
+ schema: vtkd.view_schema,
39
+ relation: vtkd.view_name,
40
+ columns: viewColumns,
41
+ is_one_to_one: r.is_one_to_one,
42
+ referenced_schema: r.referenced_schema,
43
+ referenced_relation: r.referenced_relation,
44
+ referenced_columns: r.referenced_columns,
45
+ })));
46
+ const tableToViewRelationships = tableToViewKeyDeps.flatMap((tvkd) => expandKeyDepCols(tvkd.column_dependencies).map(({ viewColumns }) => ({
47
+ foreign_key_name: r.foreign_key_name,
48
+ schema: r.schema,
49
+ relation: r.relation,
50
+ columns: r.columns,
51
+ is_one_to_one: r.is_one_to_one,
52
+ referenced_schema: tvkd.view_schema,
53
+ referenced_relation: tvkd.view_name,
54
+ referenced_columns: viewColumns,
55
+ })));
56
+ const viewToViewRelationships = viewToTableKeyDeps.flatMap((vtkd) => expandKeyDepCols(vtkd.column_dependencies).flatMap(({ viewColumns }) => tableToViewKeyDeps.flatMap((tvkd) => expandKeyDepCols(tvkd.column_dependencies).map(({ viewColumns: referencedViewColumns }) => ({
57
+ foreign_key_name: r.foreign_key_name,
58
+ schema: vtkd.view_schema,
59
+ relation: vtkd.view_name,
60
+ columns: viewColumns,
61
+ is_one_to_one: r.is_one_to_one,
62
+ referenced_schema: tvkd.view_schema,
63
+ referenced_relation: tvkd.view_name,
64
+ referenced_columns: referencedViewColumns,
65
+ })))));
66
+ return [
67
+ ...viewToTableRelationships,
68
+ ...tableToViewRelationships,
69
+ ...viewToViewRelationships,
70
+ ];
71
+ });
72
+ }
73
+ /**
74
+ * List all relationships (table↔table plus the view-derived ones) for the
75
+ * given schema filter. Mirrors `PostgresMetaRelationships.list()` with the
76
+ * generator path's defaults (`includeSystemSchemas: false`). Errors from the
77
+ * injected `Queryable` propagate by throwing — callers adapt.
78
+ */
79
+ export async function listRelationships(db, { includedSchemas, excludedSchemas } = {}) {
80
+ const schemaFilter = filterByList(includedSchemas, excludedSchemas, DEFAULT_SYSTEM_SCHEMAS);
81
+ const { rows: allTableM2oAndO2oRelationships } = await db.query(TABLE_RELATIONSHIPS_SQL({ schemaFilter }));
82
+ const { rows: viewsKeyDependencies } = await db.query(VIEWS_KEY_DEPENDENCIES_SQL({ schemaFilter }));
83
+ const allViewM2oAndO2oRelationships = expandViewRelationships(allTableM2oAndO2oRelationships, viewsKeyDependencies);
84
+ return allTableM2oAndO2oRelationships.concat(allViewM2oAndO2oRelationships);
85
+ }
@@ -0,0 +1,7 @@
1
+ import type { SQLQueryPropsWithSchemaFilter } from "./common.ts";
2
+ export declare const COLUMNS_SQL: (props: SQLQueryPropsWithSchemaFilter & {
3
+ tableIdFilter?: string;
4
+ tableIdentifierFilter?: string;
5
+ columnNameFilter?: string;
6
+ idsFilter?: string;
7
+ }) => string;
@@ -0,0 +1,122 @@
1
+ import { literal } from "pg-format";
2
+ export const COLUMNS_SQL = (props) => /* SQL */ `
3
+ -- Adapted from information_schema.columns
4
+
5
+ SELECT
6
+ c.oid :: int8 AS table_id,
7
+ nc.nspname AS schema,
8
+ c.relname AS table,
9
+ (c.oid || '.' || a.attnum) AS id,
10
+ a.attnum AS ordinal_position,
11
+ a.attname AS name,
12
+ CASE
13
+ WHEN a.atthasdef THEN pg_get_expr(ad.adbin, ad.adrelid)
14
+ ELSE NULL
15
+ END AS default_value,
16
+ CASE
17
+ WHEN t.typtype = 'd' THEN CASE
18
+ WHEN bt.typelem <> 0 :: oid
19
+ AND bt.typlen = -1 THEN 'ARRAY'
20
+ WHEN nbt.nspname = 'pg_catalog' THEN format_type(t.typbasetype, NULL)
21
+ ELSE 'USER-DEFINED'
22
+ END
23
+ ELSE CASE
24
+ WHEN t.typelem <> 0 :: oid
25
+ AND t.typlen = -1 THEN 'ARRAY'
26
+ WHEN nt.nspname = 'pg_catalog' THEN format_type(a.atttypid, NULL)
27
+ ELSE 'USER-DEFINED'
28
+ END
29
+ END AS data_type,
30
+ COALESCE(bt.typname, t.typname) AS format,
31
+ COALESCE(nbt.nspname, nt.nspname) AS type_schema,
32
+ a.attidentity IN ('a', 'd') 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
+ a.attgenerated IN ('s') AS is_generated,
40
+ NOT (
41
+ a.attnotnull
42
+ OR t.typtype = 'd' AND t.typnotnull
43
+ ) AS is_nullable,
44
+ (
45
+ c.relkind IN ('r', 'p')
46
+ OR c.relkind IN ('v', 'f') AND pg_column_is_updatable(c.oid, a.attnum, FALSE)
47
+ ) AS is_updatable,
48
+ uniques.table_id IS NOT NULL AS is_unique,
49
+ check_constraints.definition AS "check",
50
+ array_to_json(
51
+ array(
52
+ SELECT
53
+ enumlabel
54
+ FROM
55
+ pg_catalog.pg_enum enums
56
+ WHERE
57
+ enums.enumtypid = coalesce(bt.oid, t.oid)
58
+ OR enums.enumtypid = coalesce(bt.typelem, t.typelem)
59
+ ORDER BY
60
+ enums.enumsortorder
61
+ )
62
+ ) AS enums,
63
+ col_description(c.oid, a.attnum) AS comment
64
+ FROM
65
+ pg_attribute a
66
+ LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid
67
+ AND a.attnum = ad.adnum
68
+ JOIN (
69
+ pg_class c
70
+ JOIN pg_namespace nc ON c.relnamespace = nc.oid
71
+ ) ON a.attrelid = c.oid
72
+ JOIN (
73
+ pg_type t
74
+ JOIN pg_namespace nt ON t.typnamespace = nt.oid
75
+ ) ON a.atttypid = t.oid
76
+ LEFT JOIN (
77
+ pg_type bt
78
+ JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid
79
+ ) ON t.typtype = 'd'
80
+ AND t.typbasetype = bt.oid
81
+ LEFT JOIN (
82
+ SELECT DISTINCT ON (table_id, ordinal_position)
83
+ conrelid AS table_id,
84
+ conkey[1] AS ordinal_position
85
+ FROM pg_catalog.pg_constraint
86
+ WHERE contype = 'u' AND cardinality(conkey) = 1
87
+ ) AS uniques ON uniques.table_id = c.oid AND uniques.ordinal_position = a.attnum
88
+ LEFT JOIN (
89
+ -- We only select the first column check
90
+ SELECT DISTINCT ON (table_id, ordinal_position)
91
+ conrelid AS table_id,
92
+ conkey[1] AS ordinal_position,
93
+ substring(
94
+ pg_get_constraintdef(pg_constraint.oid, true),
95
+ 8,
96
+ length(pg_get_constraintdef(pg_constraint.oid, true)) - 8
97
+ ) AS "definition"
98
+ FROM pg_constraint
99
+ WHERE contype = 'c' AND cardinality(conkey) = 1
100
+ ORDER BY table_id, ordinal_position, oid asc
101
+ ) AS check_constraints ON check_constraints.table_id = c.oid AND check_constraints.ordinal_position = a.attnum
102
+ WHERE
103
+ ${props.schemaFilter ? `nc.nspname ${props.schemaFilter} AND` : ""}
104
+ ${props.idsFilter ? `(c.oid || '.' || a.attnum) ${props.idsFilter} AND` : ""}
105
+ ${props.columnNameFilter ? `(c.relname || '.' || a.attname) ${props.columnNameFilter} AND` : ""}
106
+ ${props.tableIdFilter ? `c.oid ${props.tableIdFilter} AND` : ""}
107
+ ${props.tableIdentifierFilter ? `nc.nspname || '.' || c.relname ${props.tableIdentifierFilter} AND` : ""}
108
+ NOT pg_is_other_temp_schema(nc.oid)
109
+ AND a.attnum > 0
110
+ AND NOT a.attisdropped
111
+ AND (c.relkind IN ('r', 'v', 'm', 'f', 'p'))
112
+ AND (
113
+ pg_has_role(c.relowner, 'USAGE')
114
+ OR has_column_privilege(
115
+ c.oid,
116
+ a.attnum,
117
+ 'SELECT, INSERT, UPDATE, REFERENCES'
118
+ )
119
+ )
120
+ ${props.limit ? `limit ${literal(props.limit)}` : ""}
121
+ ${props.offset ? `offset ${literal(props.offset)}` : ""}
122
+ `;
@@ -0,0 +1,16 @@
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
+ export type SQLQueryPropsWithSchemaFilter = SQLQueryProps & {
11
+ schemaFilter?: string;
12
+ };
13
+ export type SQLQueryPropsWithSchemaFilterAndIdsFilter = SQLQueryProps & {
14
+ schemaFilter?: string;
15
+ idsFilter?: string;
16
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { SQLQueryProps } from "./common.ts";
2
+ export declare const FOREIGN_TABLES_SQL: (props: SQLQueryProps & {
3
+ schemaFilter?: string;
4
+ idsFilter?: string;
5
+ tableIdentifierFilter?: string;
6
+ }) => string;
@@ -0,0 +1,18 @@
1
+ import { literal } from "pg-format";
2
+ export const FOREIGN_TABLES_SQL = (props) => /* SQL */ `
3
+ SELECT
4
+ c.oid :: int8 AS id,
5
+ n.nspname AS schema,
6
+ c.relname AS name,
7
+ obj_description(c.oid) AS comment
8
+ FROM
9
+ pg_class c
10
+ JOIN pg_namespace n ON n.oid = c.relnamespace
11
+ WHERE
12
+ ${props.schemaFilter ? `n.nspname ${props.schemaFilter} AND` : ""}
13
+ ${props.idsFilter ? `c.oid ${props.idsFilter} AND` : ""}
14
+ ${props.tableIdentifierFilter ? `(n.nspname || '.' || c.relname) ${props.tableIdentifierFilter} AND` : ""}
15
+ c.relkind = 'f'
16
+ ${props.limit ? `limit ${literal(props.limit)}` : ""}
17
+ ${props.offset ? `offset ${literal(props.offset)}` : ""}
18
+ `;
@@ -0,0 +1,5 @@
1
+ import type { SQLQueryPropsWithSchemaFilterAndIdsFilter } from "./common.ts";
2
+ export declare const FUNCTIONS_SQL: (props: SQLQueryPropsWithSchemaFilterAndIdsFilter & {
3
+ nameFilter?: string;
4
+ args?: string[];
5
+ }) => string;
@@ -0,0 +1,148 @@
1
+ // oxlint-disable typescript/restrict-template-expressions -- verbatim port of postgres-meta's
2
+ // builder, which interpolates the `args` string[] into the SQL (relying on Array#toString). The
3
+ // expression sits inside a template literal where a targeted disable comment would corrupt the SQL.
4
+ import { literal } from "pg-format";
5
+ export const FUNCTIONS_SQL = (props) => /* SQL */ `
6
+ -- CTE with sane arg_modes, arg_names, and arg_types.
7
+ -- All three are always of the same length.
8
+ -- All three include all args, including OUT and TABLE args.
9
+ with functions as (
10
+ select
11
+ p.*,
12
+ -- proargmodes is null when all arg modes are IN
13
+ coalesce(
14
+ p.proargmodes,
15
+ array_fill('i'::text, array[cardinality(coalesce(p.proallargtypes, p.proargtypes))])
16
+ ) as arg_modes,
17
+ -- proargnames is null when all args are unnamed
18
+ coalesce(
19
+ p.proargnames,
20
+ array_fill(''::text, array[cardinality(coalesce(p.proallargtypes, p.proargtypes))])
21
+ ) as arg_names,
22
+ -- proallargtypes is null when all arg modes are IN
23
+ coalesce(p.proallargtypes, p.proargtypes) as arg_types,
24
+ array_cat(
25
+ array_fill(false, array[pronargs - pronargdefaults]),
26
+ array_fill(true, array[pronargdefaults])) as arg_has_defaults
27
+ from
28
+ pg_proc as p
29
+ ${props.schemaFilter ? `join pg_namespace n on p.pronamespace = n.oid` : ""}
30
+ where
31
+ ${props.schemaFilter ? `n.nspname ${props.schemaFilter} AND` : ""}
32
+ ${props.idsFilter ? `p.oid ${props.idsFilter} AND` : ""}
33
+ ${props.nameFilter ? `p.proname ${props.nameFilter} AND` : ""}
34
+ ${props.args === undefined
35
+ ? ""
36
+ : props.args.length > 0
37
+ ? `p.proargtypes::text = ${props.args.length
38
+ ? `(
39
+ SELECT STRING_AGG(type_oid::text, ' ') FROM (
40
+ SELECT (
41
+ split_args.arr[
42
+ array_length(
43
+ split_args.arr,
44
+ 1
45
+ )
46
+ ]::regtype::oid
47
+ ) AS type_oid FROM (
48
+ SELECT STRING_TO_ARRAY(
49
+ UNNEST(
50
+ ARRAY[${props.args}]
51
+ ),
52
+ ' '
53
+ ) AS arr
54
+ ) AS split_args
55
+ ) args
56
+ )`
57
+ : "''"} AND`
58
+ : ""}
59
+ p.prokind = 'f'
60
+ )
61
+ select
62
+ f.oid::int8 as id,
63
+ n.nspname as schema,
64
+ f.proname as name,
65
+ l.lanname as language,
66
+ case
67
+ when l.lanname = 'internal' then ''
68
+ else f.prosrc
69
+ end as definition,
70
+ case
71
+ when l.lanname = 'internal' then f.prosrc
72
+ else pg_get_functiondef(f.oid)
73
+ end as complete_statement,
74
+ coalesce(f_args.args, '[]') as args,
75
+ pg_get_function_arguments(f.oid) as argument_types,
76
+ pg_get_function_identity_arguments(f.oid) as identity_argument_types,
77
+ f.prorettype::int8 as return_type_id,
78
+ pg_get_function_result(f.oid) as return_type,
79
+ nullif(rt.typrelid::int8, 0) as return_type_relation_id,
80
+ f.proretset as is_set_returning_function,
81
+ case
82
+ when f.proretset then nullif(f.prorows, 0)
83
+ else null
84
+ end as prorows,
85
+ case
86
+ when f.provolatile = 'i' then 'IMMUTABLE'
87
+ when f.provolatile = 's' then 'STABLE'
88
+ when f.provolatile = 'v' then 'VOLATILE'
89
+ end as behavior,
90
+ f.prosecdef as security_definer,
91
+ f_config.config_params as config_params
92
+ from
93
+ functions f
94
+ left join pg_namespace n on f.pronamespace = n.oid
95
+ left join pg_language l on f.prolang = l.oid
96
+ left join pg_type rt on rt.oid = f.prorettype
97
+ left join (
98
+ select
99
+ oid,
100
+ jsonb_object_agg(param, value) filter (where param is not null) as config_params
101
+ from
102
+ (
103
+ select
104
+ oid,
105
+ (string_to_array(unnest(proconfig), '='))[1] as param,
106
+ (string_to_array(unnest(proconfig), '='))[2] as value
107
+ from
108
+ functions
109
+ ) as t
110
+ group by
111
+ oid
112
+ ) f_config on f_config.oid = f.oid
113
+ left join (
114
+ select
115
+ oid,
116
+ jsonb_agg(jsonb_build_object(
117
+ 'mode', t2.mode,
118
+ 'name', name,
119
+ 'type_id', type_id,
120
+ 'has_default', has_default
121
+ )) as args
122
+ from
123
+ (
124
+ select
125
+ oid,
126
+ unnest(arg_modes) as mode,
127
+ unnest(arg_names) as name,
128
+ unnest(arg_types)::int8 as type_id,
129
+ unnest(arg_has_defaults) as has_default
130
+ from
131
+ functions
132
+ ) as t1,
133
+ lateral (
134
+ select
135
+ case
136
+ when t1.mode = 'i' then 'in'
137
+ when t1.mode = 'o' then 'out'
138
+ when t1.mode = 'b' then 'inout'
139
+ when t1.mode = 'v' then 'variadic'
140
+ else 'table'
141
+ end as mode
142
+ ) as t2
143
+ group by
144
+ t1.oid
145
+ ) f_args on f_args.oid = f.oid
146
+ ${props.limit ? `limit ${literal(props.limit)}` : ""}
147
+ ${props.offset ? `offset ${literal(props.offset)}` : ""}
148
+ `;