@prisma-next/adapter-postgres 0.14.0 → 0.15.0-dev.10
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/dist/{adapter-CwkcdpM_.mjs → adapter-Bh8GA6sC.mjs} +4 -3
- package/dist/adapter-Bh8GA6sC.mjs.map +1 -0
- package/dist/adapter.d.mts +1 -1
- package/dist/adapter.d.mts.map +1 -1
- package/dist/adapter.mjs +1 -1
- package/dist/{control-adapter-Dspz5uKp.mjs → control-adapter-aFfSI2bK.mjs} +427 -120
- package/dist/control-adapter-aFfSI2bK.mjs.map +1 -0
- package/dist/control.d.mts +23 -12
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +46 -107
- package/dist/control.mjs.map +1 -1
- package/dist/{descriptor-meta-DOgMfoqm.mjs → descriptor-meta-jMtx881n.mjs} +12 -4
- package/dist/descriptor-meta-jMtx881n.mjs.map +1 -0
- package/dist/runtime.d.mts +1 -1
- package/dist/runtime.mjs +2 -2
- package/dist/runtime.mjs.map +1 -1
- package/dist/{types-KXRwRZU8.d.mts → types-DkGeLfUp.d.mts} +3 -6
- package/dist/types-DkGeLfUp.d.mts.map +1 -0
- package/dist/types.d.mts +1 -1
- package/package.json +26 -25
- package/src/core/adapter.ts +1 -0
- package/src/core/codec-lookup.ts +2 -1
- package/src/core/control-adapter.ts +611 -152
- package/src/core/control-mutation-defaults.ts +61 -190
- package/src/core/descriptor-meta.ts +4 -0
- package/src/core/marker-ledger.ts +15 -0
- package/src/core/sql-renderer.ts +32 -6
- package/src/core/types.ts +2 -3
- package/src/exports/runtime.ts +2 -5
- package/dist/adapter-CwkcdpM_.mjs.map +0 -1
- package/dist/control-adapter-Dspz5uKp.mjs.map +0 -1
- package/dist/descriptor-meta-DOgMfoqm.mjs.map +0 -1
- package/dist/types-KXRwRZU8.d.mts.map +0 -1
|
@@ -5,10 +5,13 @@ import { parseMarkerRowSafely, rethrowMarkerReadError, withMarkerReadErrorHandli
|
|
|
5
5
|
import { parseContractMarkerRow } from "@prisma-next/family-sql/verify";
|
|
6
6
|
import { UNBOUND_NAMESPACE_ID } from "@prisma-next/framework-components/ir";
|
|
7
7
|
import { REFERENTIAL_ACTION_SQL } from "@prisma-next/sql-contract/referential-action-sql";
|
|
8
|
+
import { RelationalSchemaNodeKind } from "@prisma-next/sql-schema-ir/types";
|
|
8
9
|
import { buildControlTableBootstrapQueries, buildSignMarkerBootstrapQueries, int4, int8, jsonb, pgTable, text, textArray, timestamptz } from "@prisma-next/target-postgres/contract-free";
|
|
9
10
|
import { parsePostgresDefault } from "@prisma-next/target-postgres/default-normalizer";
|
|
10
11
|
import { normalizeSchemaNativeType } from "@prisma-next/target-postgres/native-type-normalizer";
|
|
12
|
+
import { parseRlsPolicyWireName } from "@prisma-next/target-postgres/rls-canonicalize";
|
|
11
13
|
import { escapeLiteral, quoteIdentifier } from "@prisma-next/target-postgres/sql-utils";
|
|
14
|
+
import { PostgresDatabaseSchemaNode, PostgresNamespaceSchemaNode, PostgresNativeEnumSchemaNode, PostgresPolicySchemaNode, PostgresRoleSchemaNode, PostgresSchemaNodeKind, PostgresTableSchemaNode } from "@prisma-next/target-postgres/types";
|
|
12
15
|
import { blindCast } from "@prisma-next/utils/casts";
|
|
13
16
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
14
17
|
import { createAstCodecRegistry, deriveParamMetadata, encodeParamsWithMetadata } from "@prisma-next/sql-runtime";
|
|
@@ -76,6 +79,20 @@ const ledger = pgTable({
|
|
|
76
79
|
operations: jsonb()
|
|
77
80
|
});
|
|
78
81
|
/**
|
|
82
|
+
* Content-addressed contract store: one row per distinct contract, keyed
|
|
83
|
+
* by its storage hash. The ledger's `origin_core_hash` /
|
|
84
|
+
* `destination_core_hash` resolve here by hash equality, so both
|
|
85
|
+
* endpoints of every edge are direct lookups and a contract revisited by
|
|
86
|
+
* a rollback cycle is stored exactly once (upsert DO NOTHING).
|
|
87
|
+
*/
|
|
88
|
+
const ledgerContract = pgTable({
|
|
89
|
+
name: "contract",
|
|
90
|
+
schema: "prisma_contract"
|
|
91
|
+
}, {
|
|
92
|
+
core_hash: text(),
|
|
93
|
+
contract_json: jsonb()
|
|
94
|
+
});
|
|
95
|
+
/**
|
|
79
96
|
* Read-side handle covering every column of `prisma_contract.ledger`,
|
|
80
97
|
* including the DB-generated `id` (for ORDER BY) and `created_at`.
|
|
81
98
|
*/
|
|
@@ -107,7 +124,7 @@ const NOW = new RawExpr({
|
|
|
107
124
|
}
|
|
108
125
|
});
|
|
109
126
|
function mergeInvariants(current, incoming) {
|
|
110
|
-
return [
|
|
127
|
+
return [.../* @__PURE__ */ new Set([...current, ...incoming])].sort();
|
|
111
128
|
}
|
|
112
129
|
async function execute(lower, driver, query) {
|
|
113
130
|
const lowered = lower(query);
|
|
@@ -123,7 +140,7 @@ async function execute(lower, driver, query) {
|
|
|
123
140
|
*
|
|
124
141
|
* Spellings match the on-disk `meta.db.sql.postgres.nativeType` values in `@prisma-next/target-postgres`'s codec definitions, not the `udt_name` abbreviations that ADR 205 used as illustrative shorthand. The lookup-based cast policy compares against these strings directly.
|
|
125
142
|
*/
|
|
126
|
-
const POSTGRES_INFERRABLE_NATIVE_TYPES = new Set([
|
|
143
|
+
const POSTGRES_INFERRABLE_NATIVE_TYPES = /* @__PURE__ */ new Set([
|
|
127
144
|
"integer",
|
|
128
145
|
"smallint",
|
|
129
146
|
"bigint",
|
|
@@ -143,15 +160,19 @@ const POSTGRES_INFERRABLE_NATIVE_TYPES = new Set([
|
|
|
143
160
|
"bit",
|
|
144
161
|
"bit varying"
|
|
145
162
|
]);
|
|
146
|
-
function renderTypedParam(index, codecId, codecLookup) {
|
|
163
|
+
function renderTypedParam(index, codecId, codecLookup, many, typeParams) {
|
|
147
164
|
if (codecId === void 0) return `$${index}`;
|
|
148
|
-
const meta = codecLookup.metaFor(codecId);
|
|
165
|
+
const meta = codecLookup.metaFor(codecId, typeParams);
|
|
149
166
|
if (!(codecLookup.get(codecId) !== void 0 || meta !== void 0 || codecLookup.targetTypesFor(codecId) !== void 0)) throw new Error(`Postgres lowering: ParamRef carries codecId "${codecId}" but the assembled codec lookup has no entry for it. This usually indicates a missing extension pack in the runtime stack — register the pack that contributes this codec (e.g. \`extensionPacks: [pgvectorRuntime]\`), or use the codec directly from \`@prisma-next/target-postgres/codecs\` if it's a builtin.`);
|
|
150
167
|
const dbRecord = meta?.db;
|
|
151
168
|
const sqlBlock = isRecord(dbRecord) ? dbRecord["sql"] : void 0;
|
|
152
169
|
const dialectBlock = isRecord(sqlBlock) ? sqlBlock["postgres"] : void 0;
|
|
153
170
|
const nativeType = isRecord(dialectBlock) ? dialectBlock["nativeType"] : void 0;
|
|
154
|
-
if (typeof nativeType === "string"
|
|
171
|
+
if (typeof nativeType === "string") {
|
|
172
|
+
const arraySuffix = many ? "[]" : "";
|
|
173
|
+
if (!POSTGRES_INFERRABLE_NATIVE_TYPES.has(nativeType)) return `$${index}::${nativeType}${arraySuffix}`;
|
|
174
|
+
if (many) return `$${index}::${nativeType}${arraySuffix}`;
|
|
175
|
+
}
|
|
155
176
|
return `$${index}`;
|
|
156
177
|
}
|
|
157
178
|
function isRecord(value) {
|
|
@@ -497,12 +518,12 @@ function renderExpr(expr, contract, pim) {
|
|
|
497
518
|
function renderParamRef(ref, pim) {
|
|
498
519
|
const index = pim.indexMap.get(ref);
|
|
499
520
|
if (index === void 0) throw new Error("ParamRef not found in index map");
|
|
500
|
-
if (ref.kind === "prepared-param-ref") return renderTypedParam(index, ref.codec.codecId, pim.codecLookup);
|
|
521
|
+
if (ref.kind === "prepared-param-ref") return renderTypedParam(index, ref.codec.codecId, pim.codecLookup, ref.codec.many, ref.codec.typeParams);
|
|
501
522
|
if (ref.codec === void 0) throw runtimeError("RUNTIME.PARAM_REF_MISSING_CODEC", "Postgres renderer: ParamRef reached lowering without a bound CodecRef. Every column-bound ParamRef must carry a codec under the AST-bound codec contract. This usually indicates a builder path that constructed a ParamRef without threading the column codec.", {
|
|
502
523
|
paramIndex: index,
|
|
503
524
|
...ifDefined("name", ref.name)
|
|
504
525
|
});
|
|
505
|
-
return renderTypedParam(index, ref.codec.codecId, pim.codecLookup);
|
|
526
|
+
return renderTypedParam(index, ref.codec.codecId, pim.codecLookup, ref.codec.many, ref.codec.typeParams);
|
|
506
527
|
}
|
|
507
528
|
function renderLiteral(expr) {
|
|
508
529
|
if (typeof expr.value === "string") return `'${escapeLiteral(expr.value)}'`;
|
|
@@ -824,11 +845,20 @@ var PostgresControlAdapter = class {
|
|
|
824
845
|
}).where(marker.space.eq(space).and(marker.core_hash.eq(expectedFrom))).returning(marker.space).build())).length > 0;
|
|
825
846
|
}
|
|
826
847
|
/**
|
|
827
|
-
* Appends a ledger entry for `space`.
|
|
848
|
+
* Appends a ledger entry for `space`. When the edge carries a
|
|
849
|
+
* destination contract snapshot, the content-addressed
|
|
850
|
+
* `prisma_contract.contract` store is populated first (keyed by the
|
|
851
|
+
* destination hash, DO NOTHING on revisit) so a reader never sees a
|
|
852
|
+
* ledger row whose stored destination contract is missing. See the
|
|
828
853
|
* `SqlControlAdapter.writeLedgerEntry` contract.
|
|
829
854
|
*/
|
|
830
855
|
async writeLedgerEntry(driver, space, entry) {
|
|
831
|
-
|
|
856
|
+
const lower = (query) => this.lower(query, { contract: void 0 });
|
|
857
|
+
if (entry.destinationContractJson !== void 0) await execute(lower, driver, ledgerContract.upsert({
|
|
858
|
+
core_hash: entry.to,
|
|
859
|
+
contract_json: entry.destinationContractJson
|
|
860
|
+
}).onConflict(ledgerContract.core_hash).doNothing().build());
|
|
861
|
+
await execute(lower, driver, ledger.insert({
|
|
832
862
|
space,
|
|
833
863
|
migration_name: entry.migrationName,
|
|
834
864
|
migration_hash: entry.migrationHash,
|
|
@@ -887,20 +917,34 @@ var PostgresControlAdapter = class {
|
|
|
887
917
|
*/
|
|
888
918
|
async introspect(driver, contract, schema = "public") {
|
|
889
919
|
const declaredNamespaces = extractContractNamespaceIds(contract);
|
|
890
|
-
const
|
|
891
|
-
const
|
|
892
|
-
|
|
893
|
-
const
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
};
|
|
920
|
+
const resolvedSchemas = declaredNamespaces.length > 0 ? await this.resolveNamespaceSchemas(driver, declaredNamespaces) : [schema];
|
|
921
|
+
const namespaces = {};
|
|
922
|
+
let pgVersion = "unknown";
|
|
923
|
+
for (const resolved of resolvedSchemas) {
|
|
924
|
+
const { namespace, pgVersion: version } = await this.introspectSchema(driver, resolved);
|
|
925
|
+
namespaces[resolved] = namespace;
|
|
926
|
+
pgVersion = version;
|
|
927
|
+
}
|
|
928
|
+
return new PostgresDatabaseSchemaNode({
|
|
929
|
+
namespaces,
|
|
930
|
+
roles: await this.introspectRoles(driver),
|
|
931
|
+
existingSchemas: await this.listExistingSchemas(driver),
|
|
932
|
+
pgVersion
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Reads cluster-scoped database roles. Roles are not schema-qualified, so
|
|
937
|
+
* this is queried once for the whole database rather than per namespace.
|
|
938
|
+
*/
|
|
939
|
+
async introspectRoles(driver) {
|
|
940
|
+
return (await driver.query(`SELECT rolname
|
|
941
|
+
FROM pg_catalog.pg_roles
|
|
942
|
+
WHERE rolname NOT LIKE 'pg_%'
|
|
943
|
+
AND rolname != 'postgres'
|
|
944
|
+
ORDER BY rolname`)).rows.map((row) => new PostgresRoleSchemaNode({
|
|
945
|
+
name: row.rolname,
|
|
946
|
+
namespaceId: UNBOUND_NAMESPACE_ID
|
|
947
|
+
}));
|
|
904
948
|
}
|
|
905
949
|
/**
|
|
906
950
|
* Lists every non-system schema present in the connected database.
|
|
@@ -919,36 +963,24 @@ var PostgresControlAdapter = class {
|
|
|
919
963
|
ORDER BY nspname`)).rows.map((row) => row.nspname);
|
|
920
964
|
}
|
|
921
965
|
/**
|
|
922
|
-
*
|
|
923
|
-
* the connection's `current_schema()
|
|
924
|
-
*
|
|
925
|
-
*
|
|
926
|
-
*
|
|
966
|
+
* Resolves the declared namespace ids to their live DDL schema names,
|
|
967
|
+
* mapping `UNBOUND_NAMESPACE_ID` to the connection's `current_schema()`
|
|
968
|
+
* and de-duplicating. The caller introspects one namespace node per
|
|
969
|
+
* resolved schema — there is no flat cross-schema merge, so two schemas
|
|
970
|
+
* holding a same-named table no longer collide.
|
|
927
971
|
*/
|
|
928
|
-
async
|
|
972
|
+
async resolveNamespaceSchemas(driver, namespaceIds) {
|
|
929
973
|
const resolvedSchemas = [];
|
|
930
974
|
for (const id of namespaceIds) if (id === UNBOUND_NAMESPACE_ID) {
|
|
931
975
|
const { rows } = await driver.query("SELECT current_schema() AS current_schema");
|
|
932
976
|
resolvedSchemas.push(rows[0]?.current_schema ?? "public");
|
|
933
977
|
} else resolvedSchemas.push(id);
|
|
934
|
-
|
|
935
|
-
const perSchema = [];
|
|
936
|
-
for (const schema of uniqueSchemas) perSchema.push(await this.introspectSchema(driver, schema));
|
|
937
|
-
const mergedTables = {};
|
|
938
|
-
for (const ir of perSchema) for (const [tableName, table] of Object.entries(ir.tables)) mergedTables[tableName] = table;
|
|
939
|
-
const firstAnnotations = perSchema[0]?.annotations;
|
|
940
|
-
const firstPg = blindCast(firstAnnotations?.["pg"]) ?? {};
|
|
941
|
-
return {
|
|
942
|
-
tables: mergedTables,
|
|
943
|
-
...ifDefined("annotations", {
|
|
944
|
-
...firstAnnotations,
|
|
945
|
-
pg: { ...firstPg }
|
|
946
|
-
})
|
|
947
|
-
};
|
|
978
|
+
return Array.from(new Set(resolvedSchemas));
|
|
948
979
|
}
|
|
949
980
|
/**
|
|
950
|
-
* Introspects a single Postgres schema and returns
|
|
951
|
-
*
|
|
981
|
+
* Introspects a single Postgres schema and returns the namespace node for
|
|
982
|
+
* that schema (its tables, their policies, and its native enum type names),
|
|
983
|
+
* alongside the cluster-scoped Postgres version. Used by `introspect` as
|
|
952
984
|
* the per-namespace walk.
|
|
953
985
|
*/
|
|
954
986
|
async introspectSchema(driver, schema) {
|
|
@@ -982,64 +1014,74 @@ var PostgresControlAdapter = class {
|
|
|
982
1014
|
WHERE c.table_schema = $1
|
|
983
1015
|
ORDER BY c.table_name, c.ordinal_position`, [schema]);
|
|
984
1016
|
const pkResult = await driver.query(`SELECT
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
FROM
|
|
990
|
-
JOIN
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
AND
|
|
996
|
-
|
|
1017
|
+
cl.relname AS table_name,
|
|
1018
|
+
con.conname AS constraint_name,
|
|
1019
|
+
a.attname AS column_name,
|
|
1020
|
+
k.ord AS ordinal_position
|
|
1021
|
+
FROM pg_catalog.pg_constraint con
|
|
1022
|
+
JOIN pg_catalog.pg_class cl ON cl.oid = con.conrelid
|
|
1023
|
+
JOIN pg_catalog.pg_namespace ns ON ns.oid = cl.relnamespace
|
|
1024
|
+
JOIN LATERAL unnest(con.conkey) WITH ORDINALITY AS k(attnum, ord) ON true
|
|
1025
|
+
JOIN pg_catalog.pg_attribute a
|
|
1026
|
+
ON a.attrelid = con.conrelid
|
|
1027
|
+
AND a.attnum = k.attnum
|
|
1028
|
+
WHERE ns.nspname = $1
|
|
1029
|
+
AND con.contype = 'p'
|
|
1030
|
+
ORDER BY cl.relname, k.ord`, [schema]);
|
|
997
1031
|
const fkResult = await driver.query(`SELECT
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1032
|
+
cl.relname AS table_name,
|
|
1033
|
+
con.conname AS constraint_name,
|
|
1034
|
+
a.attname AS column_name,
|
|
1035
|
+
k.ord AS ordinal_position,
|
|
1002
1036
|
ref_ns.nspname AS referenced_table_schema,
|
|
1003
1037
|
ref_cl.relname AS referenced_table_name,
|
|
1004
1038
|
ref_att.attname AS referenced_column_name,
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1039
|
+
CASE con.confdeltype
|
|
1040
|
+
WHEN 'a' THEN 'NO ACTION'
|
|
1041
|
+
WHEN 'r' THEN 'RESTRICT'
|
|
1042
|
+
WHEN 'c' THEN 'CASCADE'
|
|
1043
|
+
WHEN 'n' THEN 'SET NULL'
|
|
1044
|
+
WHEN 'd' THEN 'SET DEFAULT'
|
|
1045
|
+
END AS delete_rule,
|
|
1046
|
+
CASE con.confupdtype
|
|
1047
|
+
WHEN 'a' THEN 'NO ACTION'
|
|
1048
|
+
WHEN 'r' THEN 'RESTRICT'
|
|
1049
|
+
WHEN 'c' THEN 'CASCADE'
|
|
1050
|
+
WHEN 'n' THEN 'SET NULL'
|
|
1051
|
+
WHEN 'd' THEN 'SET DEFAULT'
|
|
1052
|
+
END AS update_rule
|
|
1053
|
+
FROM pg_catalog.pg_constraint con
|
|
1054
|
+
JOIN pg_catalog.pg_class cl ON cl.oid = con.conrelid
|
|
1055
|
+
JOIN pg_catalog.pg_namespace ns ON ns.oid = cl.relnamespace
|
|
1056
|
+
JOIN LATERAL unnest(con.conkey) WITH ORDINALITY AS k(attnum, ord) ON true
|
|
1057
|
+
JOIN pg_catalog.pg_attribute a
|
|
1058
|
+
ON a.attrelid = con.conrelid
|
|
1059
|
+
AND a.attnum = k.attnum
|
|
1017
1060
|
JOIN pg_catalog.pg_class ref_cl
|
|
1018
|
-
ON ref_cl.oid =
|
|
1061
|
+
ON ref_cl.oid = con.confrelid
|
|
1019
1062
|
JOIN pg_catalog.pg_namespace ref_ns
|
|
1020
1063
|
ON ref_ns.oid = ref_cl.relnamespace
|
|
1021
1064
|
JOIN pg_catalog.pg_attribute ref_att
|
|
1022
|
-
ON ref_att.attrelid =
|
|
1023
|
-
AND ref_att.attnum =
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
WHERE tc.table_schema = $1
|
|
1028
|
-
AND tc.constraint_type = 'FOREIGN KEY'
|
|
1029
|
-
ORDER BY tc.table_name, tc.constraint_name, kcu.ordinal_position`, [schema]);
|
|
1065
|
+
ON ref_att.attrelid = con.confrelid
|
|
1066
|
+
AND ref_att.attnum = con.confkey[k.ord]
|
|
1067
|
+
WHERE ns.nspname = $1
|
|
1068
|
+
AND con.contype = 'f'
|
|
1069
|
+
ORDER BY cl.relname, con.conname, k.ord`, [schema]);
|
|
1030
1070
|
const uniqueResult = await driver.query(`SELECT
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
FROM
|
|
1036
|
-
JOIN
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
AND
|
|
1042
|
-
|
|
1071
|
+
cl.relname AS table_name,
|
|
1072
|
+
con.conname AS constraint_name,
|
|
1073
|
+
a.attname AS column_name,
|
|
1074
|
+
k.ord AS ordinal_position
|
|
1075
|
+
FROM pg_catalog.pg_constraint con
|
|
1076
|
+
JOIN pg_catalog.pg_class cl ON cl.oid = con.conrelid
|
|
1077
|
+
JOIN pg_catalog.pg_namespace ns ON ns.oid = cl.relnamespace
|
|
1078
|
+
JOIN LATERAL unnest(con.conkey) WITH ORDINALITY AS k(attnum, ord) ON true
|
|
1079
|
+
JOIN pg_catalog.pg_attribute a
|
|
1080
|
+
ON a.attrelid = con.conrelid
|
|
1081
|
+
AND a.attnum = k.attnum
|
|
1082
|
+
WHERE ns.nspname = $1
|
|
1083
|
+
AND con.contype = 'u'
|
|
1084
|
+
ORDER BY cl.relname, con.conname, k.ord`, [schema]);
|
|
1043
1085
|
const indexResult = await driver.query(`SELECT
|
|
1044
1086
|
i.tablename,
|
|
1045
1087
|
i.indexname,
|
|
@@ -1060,10 +1102,9 @@ var PostgresControlAdapter = class {
|
|
|
1060
1102
|
WHERE i.schemaname = $1
|
|
1061
1103
|
AND NOT EXISTS (
|
|
1062
1104
|
SELECT 1
|
|
1063
|
-
FROM
|
|
1064
|
-
WHERE
|
|
1065
|
-
AND
|
|
1066
|
-
AND tc.constraint_name = i.indexname
|
|
1105
|
+
FROM pg_catalog.pg_constraint con
|
|
1106
|
+
WHERE con.conindid = ic.oid
|
|
1107
|
+
AND con.contype IN ('p', 'u', 'x')
|
|
1067
1108
|
)
|
|
1068
1109
|
ORDER BY i.tablename, i.indexname, k.ord`, [schema]);
|
|
1069
1110
|
const checkResult = await driver.query(`SELECT
|
|
@@ -1091,7 +1132,7 @@ var PostgresControlAdapter = class {
|
|
|
1091
1132
|
}
|
|
1092
1133
|
constraints.add(row.constraint_name);
|
|
1093
1134
|
}
|
|
1094
|
-
const
|
|
1135
|
+
const tableInputs = {};
|
|
1095
1136
|
for (const tableRow of tablesResult.rows) {
|
|
1096
1137
|
const tableName = tableRow.table_name;
|
|
1097
1138
|
const columns = {};
|
|
@@ -1105,18 +1146,26 @@ var PostgresControlAdapter = class {
|
|
|
1105
1146
|
else if (colRow.numeric_precision) nativeType = `${colRow.data_type}(${colRow.numeric_precision})`;
|
|
1106
1147
|
else nativeType = colRow.data_type;
|
|
1107
1148
|
else nativeType = colRow.udt_name || colRow.data_type;
|
|
1149
|
+
const many = nativeType.endsWith("[]") ? true : void 0;
|
|
1150
|
+
if (many) nativeType = normalizeSchemaNativeType(nativeType.slice(0, -2));
|
|
1151
|
+
const resolvedNativeType = `${normalizeSchemaNativeType(nativeType)}${many ? "[]" : ""}`;
|
|
1152
|
+
const rawDefault = colRow.column_default ?? void 0;
|
|
1108
1153
|
columns[colRow.column_name] = {
|
|
1109
1154
|
name: colRow.column_name,
|
|
1110
1155
|
nativeType,
|
|
1111
1156
|
nullable: colRow.is_nullable === "YES",
|
|
1112
|
-
...ifDefined("default",
|
|
1157
|
+
...ifDefined("default", rawDefault),
|
|
1158
|
+
...ifDefined("many", many),
|
|
1159
|
+
resolvedNativeType,
|
|
1160
|
+
...ifDefined("resolvedDefault", rawDefault !== void 0 ? parsePostgresDefault(rawDefault, resolvedNativeType) : void 0)
|
|
1113
1161
|
};
|
|
1114
1162
|
}
|
|
1115
1163
|
const pkRows = [...pksByTable.get(tableName) ?? []];
|
|
1116
1164
|
const primaryKeyColumns = pkRows.sort((a, b) => a.ordinal_position - b.ordinal_position).map((row) => row.column_name);
|
|
1117
1165
|
const primaryKey = primaryKeyColumns.length > 0 ? {
|
|
1118
1166
|
columns: primaryKeyColumns,
|
|
1119
|
-
...pkRows[0]?.constraint_name ? { name: pkRows[0].constraint_name } : {}
|
|
1167
|
+
...pkRows[0]?.constraint_name ? { name: pkRows[0].constraint_name } : {},
|
|
1168
|
+
dependsOn: postgresColumnDependsOn(schema, tableName, primaryKeyColumns)
|
|
1120
1169
|
} : void 0;
|
|
1121
1170
|
const foreignKeysMap = /* @__PURE__ */ new Map();
|
|
1122
1171
|
for (const fkRow of fksByTable.get(tableName) ?? []) {
|
|
@@ -1141,7 +1190,8 @@ var PostgresControlAdapter = class {
|
|
|
1141
1190
|
referencedColumns: Object.freeze([...fk.referencedColumns]),
|
|
1142
1191
|
name: fk.name,
|
|
1143
1192
|
...ifDefined("onDelete", mapReferentialAction(fk.deleteRule)),
|
|
1144
|
-
...ifDefined("onUpdate", mapReferentialAction(fk.updateRule))
|
|
1193
|
+
...ifDefined("onUpdate", mapReferentialAction(fk.updateRule)),
|
|
1194
|
+
dependsOn: [postgresTableDependsOn(fk.referencedSchema, fk.referencedTable), ...postgresColumnDependsOn(schema, tableName, fk.columns)]
|
|
1145
1195
|
}));
|
|
1146
1196
|
const pkConstraints = pkConstraintsByTable.get(tableName) ?? /* @__PURE__ */ new Set();
|
|
1147
1197
|
const uniquesMap = /* @__PURE__ */ new Map();
|
|
@@ -1156,11 +1206,16 @@ var PostgresControlAdapter = class {
|
|
|
1156
1206
|
}
|
|
1157
1207
|
const uniques = Array.from(uniquesMap.values()).map((uq) => ({
|
|
1158
1208
|
columns: Object.freeze([...uq.columns]),
|
|
1159
|
-
name: uq.name
|
|
1209
|
+
name: uq.name,
|
|
1210
|
+
dependsOn: postgresColumnDependsOn(schema, tableName, uq.columns)
|
|
1160
1211
|
}));
|
|
1161
1212
|
const indexesMap = /* @__PURE__ */ new Map();
|
|
1213
|
+
const indexNamesWithExpressionKey = /* @__PURE__ */ new Set();
|
|
1162
1214
|
for (const idxRow of indexesByTable.get(tableName) ?? []) {
|
|
1163
|
-
if (!idxRow.attname)
|
|
1215
|
+
if (!idxRow.attname) {
|
|
1216
|
+
indexNamesWithExpressionKey.add(idxRow.indexname);
|
|
1217
|
+
continue;
|
|
1218
|
+
}
|
|
1164
1219
|
const existing = indexesMap.get(idxRow.indexname);
|
|
1165
1220
|
if (existing) existing.columns.push(idxRow.attname);
|
|
1166
1221
|
else {
|
|
@@ -1175,12 +1230,20 @@ var PostgresControlAdapter = class {
|
|
|
1175
1230
|
});
|
|
1176
1231
|
}
|
|
1177
1232
|
}
|
|
1178
|
-
const
|
|
1233
|
+
const survivingIndexes = Array.from(indexesMap.values()).filter((idx) => !indexNamesWithExpressionKey.has(idx.name));
|
|
1234
|
+
const bestByColumnTuple = /* @__PURE__ */ new Map();
|
|
1235
|
+
for (const idx of survivingIndexes) {
|
|
1236
|
+
const tupleKey = idx.columns.join(",");
|
|
1237
|
+
const existing = bestByColumnTuple.get(tupleKey);
|
|
1238
|
+
if (!existing || idx.unique && !existing.unique || idx.unique === existing.unique && idx.name < existing.name) bestByColumnTuple.set(tupleKey, idx);
|
|
1239
|
+
}
|
|
1240
|
+
const indexes = Array.from(bestByColumnTuple.values()).map((idx) => ({
|
|
1179
1241
|
columns: Object.freeze([...idx.columns]),
|
|
1180
1242
|
name: idx.name,
|
|
1181
1243
|
unique: idx.unique,
|
|
1182
1244
|
...idx.type !== void 0 && { type: idx.type },
|
|
1183
|
-
...idx.options !== void 0 && { options: idx.options }
|
|
1245
|
+
...idx.options !== void 0 && { options: idx.options },
|
|
1246
|
+
dependsOn: postgresColumnDependsOn(schema, tableName, idx.columns)
|
|
1184
1247
|
}));
|
|
1185
1248
|
const checksForTable = [];
|
|
1186
1249
|
for (const checkRow of checksByTable.get(tableName) ?? []) {
|
|
@@ -1191,7 +1254,7 @@ var PostgresControlAdapter = class {
|
|
|
1191
1254
|
permittedValues: parsed.permittedValues
|
|
1192
1255
|
});
|
|
1193
1256
|
}
|
|
1194
|
-
|
|
1257
|
+
tableInputs[tableName] = {
|
|
1195
1258
|
name: tableName,
|
|
1196
1259
|
columns,
|
|
1197
1260
|
...ifDefined("primaryKey", primaryKey),
|
|
@@ -1201,19 +1264,64 @@ var PostgresControlAdapter = class {
|
|
|
1201
1264
|
...ifDefined("checks", checksForTable.length > 0 ? checksForTable : void 0)
|
|
1202
1265
|
};
|
|
1203
1266
|
}
|
|
1204
|
-
const
|
|
1267
|
+
const enums = (await driver.query(`SELECT t.typname, array_agg(e.enumlabel ORDER BY e.enumsortorder) AS enumvalues
|
|
1205
1268
|
FROM pg_catalog.pg_type t
|
|
1206
1269
|
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
1270
|
+
JOIN pg_catalog.pg_enum e ON e.enumtypid = t.oid
|
|
1207
1271
|
WHERE t.typtype = 'e'
|
|
1208
1272
|
AND n.nspname = $1
|
|
1209
|
-
|
|
1273
|
+
GROUP BY t.typname
|
|
1274
|
+
ORDER BY t.typname`, [schema])).rows.map((r) => new PostgresNativeEnumSchemaNode({
|
|
1275
|
+
typeName: r.typname,
|
|
1276
|
+
namespaceId: schema,
|
|
1277
|
+
members: parsePgNameArray(r.enumvalues)
|
|
1278
|
+
}));
|
|
1279
|
+
const policiesResult = await driver.query(`SELECT schemaname, tablename, policyname, cmd, roles, qual, with_check, permissive
|
|
1280
|
+
FROM pg_catalog.pg_policies
|
|
1281
|
+
WHERE schemaname = $1
|
|
1282
|
+
ORDER BY tablename, policyname`, [schema]);
|
|
1283
|
+
const policiesByTable = /* @__PURE__ */ new Map();
|
|
1284
|
+
for (const row of policiesResult.rows) {
|
|
1285
|
+
const operation = mapPgCmd(row.cmd);
|
|
1286
|
+
const policyRoles = [...new Set(parsePgNameArray(row.roles).map((r) => r.toLowerCase()))].sort();
|
|
1287
|
+
const permissive = row.permissive.toUpperCase() === "PERMISSIVE";
|
|
1288
|
+
const prefix = parseRlsPolicyWireName(row.policyname)?.prefix ?? row.policyname;
|
|
1289
|
+
const policy = new PostgresPolicySchemaNode({
|
|
1290
|
+
name: row.policyname,
|
|
1291
|
+
prefix,
|
|
1292
|
+
tableName: row.tablename,
|
|
1293
|
+
namespaceId: row.schemaname,
|
|
1294
|
+
operation,
|
|
1295
|
+
roles: policyRoles,
|
|
1296
|
+
...row.qual !== null ? { using: row.qual } : {},
|
|
1297
|
+
...row.with_check !== null ? { withCheck: row.with_check } : {},
|
|
1298
|
+
permissive,
|
|
1299
|
+
dependsOn: [postgresTableDependsOn(row.schemaname, row.tablename), ...policyRoles.map(postgresRoleDependsOn)]
|
|
1300
|
+
});
|
|
1301
|
+
const list = policiesByTable.get(row.tablename) ?? [];
|
|
1302
|
+
list.push(policy);
|
|
1303
|
+
policiesByTable.set(row.tablename, list);
|
|
1304
|
+
}
|
|
1305
|
+
const rlsEnabledResult = await driver.query(`SELECT c.relname AS tablename, c.relrowsecurity AS rls_enabled
|
|
1306
|
+
FROM pg_catalog.pg_class c
|
|
1307
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
1308
|
+
WHERE n.nspname = $1
|
|
1309
|
+
AND c.relkind IN ('r', 'p')
|
|
1310
|
+
ORDER BY c.relname`, [schema]);
|
|
1311
|
+
const rlsEnabledByTable = new Map(rlsEnabledResult.rows.map((row) => [row.tablename, row.rls_enabled]));
|
|
1312
|
+
const tables = {};
|
|
1313
|
+
for (const [tableName, input] of Object.entries(tableInputs)) tables[tableName] = new PostgresTableSchemaNode({
|
|
1314
|
+
...input,
|
|
1315
|
+
policies: policiesByTable.get(tableName) ?? [],
|
|
1316
|
+
rlsEnabled: rlsEnabledByTable.get(tableName) ?? false
|
|
1317
|
+
});
|
|
1210
1318
|
return {
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1319
|
+
namespace: new PostgresNamespaceSchemaNode({
|
|
1320
|
+
schemaName: schema,
|
|
1321
|
+
tables,
|
|
1322
|
+
nativeEnums: enums
|
|
1323
|
+
}),
|
|
1324
|
+
pgVersion: await this.getPostgresVersion(driver)
|
|
1217
1325
|
};
|
|
1218
1326
|
}
|
|
1219
1327
|
/**
|
|
@@ -1224,6 +1332,86 @@ var PostgresControlAdapter = class {
|
|
|
1224
1332
|
}
|
|
1225
1333
|
};
|
|
1226
1334
|
/**
|
|
1335
|
+
* Normalises a `name[]` column value from `pg_policies.roles`.
|
|
1336
|
+
*
|
|
1337
|
+
* The `pg` client's type-parser registry handles `text[]` (OID 1009) but not
|
|
1338
|
+
* `name[]` (OID 1003). When the parser is absent the raw Postgres text-array
|
|
1339
|
+
* literal (`{role1,role2}`) is returned as a string instead of a JS array.
|
|
1340
|
+
* This function accepts either form and returns a plain string array.
|
|
1341
|
+
*
|
|
1342
|
+
* The string branch honors Postgres array-literal quoting: an element
|
|
1343
|
+
* containing a comma, quote, backslash, brace, or significant whitespace is
|
|
1344
|
+
* emitted double-quoted with `\"` / `\\` escapes, and unquoted elements are
|
|
1345
|
+
* whitespace-trimmed — so a label like `in progress` or `say "hi"` parses to
|
|
1346
|
+
* its true value instead of being split or kept escaped.
|
|
1347
|
+
*/
|
|
1348
|
+
function parsePgNameArray(value) {
|
|
1349
|
+
if (Array.isArray(value)) return value.map(String);
|
|
1350
|
+
if (typeof value !== "string") return [];
|
|
1351
|
+
const trimmed = value.trim();
|
|
1352
|
+
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) return [];
|
|
1353
|
+
const inner = trimmed.slice(1, -1);
|
|
1354
|
+
if (inner === "") return [];
|
|
1355
|
+
const elements = [];
|
|
1356
|
+
let current = "";
|
|
1357
|
+
let inQuotes = false;
|
|
1358
|
+
let wasQuoted = false;
|
|
1359
|
+
const pushCurrent = () => {
|
|
1360
|
+
elements.push(wasQuoted ? current : current.trim());
|
|
1361
|
+
current = "";
|
|
1362
|
+
wasQuoted = false;
|
|
1363
|
+
};
|
|
1364
|
+
let i = 0;
|
|
1365
|
+
while (i < inner.length) {
|
|
1366
|
+
const char = inner.charAt(i);
|
|
1367
|
+
if (inQuotes) {
|
|
1368
|
+
if (char === "\\") {
|
|
1369
|
+
current += inner[i + 1] ?? "";
|
|
1370
|
+
i += 2;
|
|
1371
|
+
continue;
|
|
1372
|
+
}
|
|
1373
|
+
if (char === "\"") {
|
|
1374
|
+
inQuotes = false;
|
|
1375
|
+
i++;
|
|
1376
|
+
continue;
|
|
1377
|
+
}
|
|
1378
|
+
current += char;
|
|
1379
|
+
i++;
|
|
1380
|
+
continue;
|
|
1381
|
+
}
|
|
1382
|
+
if (char === "\"") {
|
|
1383
|
+
inQuotes = true;
|
|
1384
|
+
wasQuoted = true;
|
|
1385
|
+
i++;
|
|
1386
|
+
continue;
|
|
1387
|
+
}
|
|
1388
|
+
if (char === ",") {
|
|
1389
|
+
pushCurrent();
|
|
1390
|
+
i++;
|
|
1391
|
+
continue;
|
|
1392
|
+
}
|
|
1393
|
+
current += char;
|
|
1394
|
+
i++;
|
|
1395
|
+
}
|
|
1396
|
+
if (inQuotes) return [];
|
|
1397
|
+
pushCurrent();
|
|
1398
|
+
return elements;
|
|
1399
|
+
}
|
|
1400
|
+
/**
|
|
1401
|
+
* Maps `pg_policies.cmd` text values to the `RlsPolicyOperation` union.
|
|
1402
|
+
* The `pg_policies` view renders the internal command code as an uppercase
|
|
1403
|
+
* English keyword; this function lowercases to match the IR type.
|
|
1404
|
+
*/
|
|
1405
|
+
function mapPgCmd(cmd) {
|
|
1406
|
+
switch (cmd.toUpperCase()) {
|
|
1407
|
+
case "SELECT": return "select";
|
|
1408
|
+
case "INSERT": return "insert";
|
|
1409
|
+
case "UPDATE": return "update";
|
|
1410
|
+
case "DELETE": return "delete";
|
|
1411
|
+
default: return "all";
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
/**
|
|
1227
1415
|
* Extracts the namespace coordinate ids declared on a contract's storage,
|
|
1228
1416
|
* or returns an empty array when no contract (or no storage / namespaces)
|
|
1229
1417
|
* is present. Used by `PostgresControlAdapter.introspect` to decide
|
|
@@ -1238,6 +1426,7 @@ function extractContractNamespaceIds(contract) {
|
|
|
1238
1426
|
return Object.keys(namespaces);
|
|
1239
1427
|
}
|
|
1240
1428
|
function normalizeFormattedType(formattedType, dataType, udtName) {
|
|
1429
|
+
if (formattedType.endsWith("[]")) return `${normalizeFormattedType(formattedType.slice(0, -2), dataType, udtName)}[]`;
|
|
1241
1430
|
if (formattedType === "integer") return "int4";
|
|
1242
1431
|
if (formattedType === "smallint") return "int2";
|
|
1243
1432
|
if (formattedType === "bigint") return "int8";
|
|
@@ -1273,6 +1462,49 @@ function mapReferentialAction(rule) {
|
|
|
1273
1462
|
return mapped;
|
|
1274
1463
|
}
|
|
1275
1464
|
/**
|
|
1465
|
+
* A FK/policy dependency chain, mirroring the shape the expected-side
|
|
1466
|
+
* derivation stamps (`contractToPostgresDatabaseSchemaNode`): the database
|
|
1467
|
+
* root's fixed sentinel id, then the namespace, then the table.
|
|
1468
|
+
*/
|
|
1469
|
+
function postgresTableDependsOn(namespaceId, tableName) {
|
|
1470
|
+
return [
|
|
1471
|
+
{
|
|
1472
|
+
nodeKind: PostgresSchemaNodeKind.database,
|
|
1473
|
+
id: "database"
|
|
1474
|
+
},
|
|
1475
|
+
{
|
|
1476
|
+
nodeKind: PostgresSchemaNodeKind.namespace,
|
|
1477
|
+
id: namespaceId
|
|
1478
|
+
},
|
|
1479
|
+
{
|
|
1480
|
+
nodeKind: PostgresSchemaNodeKind.table,
|
|
1481
|
+
id: tableName
|
|
1482
|
+
}
|
|
1483
|
+
];
|
|
1484
|
+
}
|
|
1485
|
+
/** A policy's dependency chain onto one of the roles it grants to. */
|
|
1486
|
+
function postgresRoleDependsOn(role) {
|
|
1487
|
+
return [{
|
|
1488
|
+
nodeKind: PostgresSchemaNodeKind.database,
|
|
1489
|
+
id: "database"
|
|
1490
|
+
}, {
|
|
1491
|
+
nodeKind: PostgresSchemaNodeKind.role,
|
|
1492
|
+
id: role
|
|
1493
|
+
}];
|
|
1494
|
+
}
|
|
1495
|
+
/**
|
|
1496
|
+
* The chains from a table-child object (foreign key, index, unique, primary
|
|
1497
|
+
* key) to each of the own columns it is built on — the introspection-side
|
|
1498
|
+
* mirror of `contractToPostgresDatabaseSchemaNode`'s `columnDependsOn`. An
|
|
1499
|
+
* object is dropped before the columns it covers.
|
|
1500
|
+
*/
|
|
1501
|
+
function postgresColumnDependsOn(namespaceId, tableName, columns) {
|
|
1502
|
+
return columns.map((column) => [...postgresTableDependsOn(namespaceId, tableName), {
|
|
1503
|
+
nodeKind: RelationalSchemaNodeKind.column,
|
|
1504
|
+
id: `column:${column}`
|
|
1505
|
+
}]);
|
|
1506
|
+
}
|
|
1507
|
+
/**
|
|
1276
1508
|
* Groups an array of objects by a specified key.
|
|
1277
1509
|
* Returns a Map for O(1) lookup by group key.
|
|
1278
1510
|
*/
|
|
@@ -1383,6 +1615,16 @@ function extractQuotedLiterals(listBody) {
|
|
|
1383
1615
|
function pgIsTextLikeNativeType(nativeType) {
|
|
1384
1616
|
return nativeType === "text" || nativeType === "varchar" || nativeType.startsWith("varchar(") || nativeType === "character varying" || nativeType.startsWith("character varying(") || nativeType === "char" || nativeType.startsWith("char(") || nativeType === "character" || nativeType.startsWith("character(");
|
|
1385
1617
|
}
|
|
1618
|
+
function pgRenderArrayElement(el) {
|
|
1619
|
+
if (el === null) return "NULL";
|
|
1620
|
+
if (typeof el === "number" || typeof el === "boolean") return String(el);
|
|
1621
|
+
if (typeof el === "string") return `'${escapeLiteral(el)}'`;
|
|
1622
|
+
return `'${escapeLiteral(JSON.stringify(el))}'`;
|
|
1623
|
+
}
|
|
1624
|
+
function pgRenderArrayLiteral(elements) {
|
|
1625
|
+
if (elements.length === 0) return "'{}'";
|
|
1626
|
+
return `ARRAY[${elements.map(pgRenderArrayElement).join(", ")}]`;
|
|
1627
|
+
}
|
|
1386
1628
|
function pgInlineLiteral(wire, nativeType) {
|
|
1387
1629
|
if (wire === null) return "NULL";
|
|
1388
1630
|
if (typeof wire === "boolean") return wire ? "true" : "false";
|
|
@@ -1401,6 +1643,7 @@ function pgInlineLiteral(wire, nativeType) {
|
|
|
1401
1643
|
return pgIsTextLikeNativeType(nativeType) ? quoted : `${quoted}::${nativeType}`;
|
|
1402
1644
|
}
|
|
1403
1645
|
if (wire instanceof Uint8Array) return `'\\x${Array.from(wire).map((b) => b.toString(16).padStart(2, "0")).join("")}'::${nativeType}`;
|
|
1646
|
+
if (Array.isArray(wire) && nativeType.endsWith("[]")) return pgRenderArrayLiteral(wire);
|
|
1404
1647
|
if (typeof wire === "object") return `${`'${escapeLiteral(JSON.stringify(wire))}'`}::${nativeType}`;
|
|
1405
1648
|
throw new Error(`pgRenderDdlExecuteRequest: unexpected wire type "${typeof wire}" for native type "${nativeType}"`);
|
|
1406
1649
|
}
|
|
@@ -1438,6 +1681,7 @@ function pgRenderDdlConstraint(constraint) {
|
|
|
1438
1681
|
if (constraint.name !== void 0) sql = `CONSTRAINT ${quoteIdentifier(constraint.name)} ${sql}`;
|
|
1439
1682
|
return sql;
|
|
1440
1683
|
}
|
|
1684
|
+
if (constraint.kind === "check-expression") return `CONSTRAINT ${quoteIdentifier(constraint.name)} CHECK (${constraint.expression})`;
|
|
1441
1685
|
const cols = constraint.columns.map(quoteIdentifier).join(", ");
|
|
1442
1686
|
if (constraint.name !== void 0) return `CONSTRAINT ${quoteIdentifier(constraint.name)} UNIQUE (${cols})`;
|
|
1443
1687
|
return `UNIQUE (${cols})`;
|
|
@@ -1458,24 +1702,87 @@ function pgRenderCreateSchema(node) {
|
|
|
1458
1702
|
params: []
|
|
1459
1703
|
};
|
|
1460
1704
|
}
|
|
1705
|
+
function pgRenderCreateType(node) {
|
|
1706
|
+
return {
|
|
1707
|
+
sql: `CREATE TYPE ${node.schema ? `${quoteIdentifier(node.schema)}.${quoteIdentifier(node.name)}` : quoteIdentifier(node.name)} AS ENUM (${node.values.map((value) => `'${escapeLiteral(value)}'`).join(", ")})`,
|
|
1708
|
+
params: []
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
function pgRenderDropType(node) {
|
|
1712
|
+
return {
|
|
1713
|
+
sql: `DROP TYPE ${node.schema ? `${quoteIdentifier(node.schema)}.${quoteIdentifier(node.name)}` : quoteIdentifier(node.name)}`,
|
|
1714
|
+
params: []
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1461
1717
|
async function pgRenderAlterTable(node, codecLookup) {
|
|
1462
1718
|
const tableRef = node.schema ? `${quoteIdentifier(node.schema)}.${quoteIdentifier(node.table)}` : quoteIdentifier(node.table);
|
|
1463
|
-
const actionVisitor = {
|
|
1464
|
-
|
|
1465
|
-
|
|
1719
|
+
const actionVisitor = {
|
|
1720
|
+
async addColumn(action) {
|
|
1721
|
+
return `ADD COLUMN ${await pgRenderDdlColumn(action.column, codecLookup)}`;
|
|
1722
|
+
},
|
|
1723
|
+
dropDefault(action) {
|
|
1724
|
+
return Promise.resolve(`ALTER COLUMN ${quoteIdentifier(action.columnName)} DROP DEFAULT`);
|
|
1725
|
+
}
|
|
1726
|
+
};
|
|
1466
1727
|
return {
|
|
1467
1728
|
sql: `ALTER TABLE ${tableRef} ${(await Promise.all(node.actions.map((a) => a.accept(actionVisitor)))).join(", ")}`,
|
|
1468
1729
|
params: []
|
|
1469
1730
|
};
|
|
1470
1731
|
}
|
|
1732
|
+
const POLICY_OPERATION_SQL = {
|
|
1733
|
+
select: "SELECT",
|
|
1734
|
+
insert: "INSERT",
|
|
1735
|
+
update: "UPDATE",
|
|
1736
|
+
delete: "DELETE",
|
|
1737
|
+
all: "ALL"
|
|
1738
|
+
};
|
|
1739
|
+
function pgRenderCreatePolicy(node) {
|
|
1740
|
+
const tableRef = `${quoteIdentifier(node.schema)}.${quoteIdentifier(node.table)}`;
|
|
1741
|
+
const permissiveness = node.permissive ? "PERMISSIVE" : "RESTRICTIVE";
|
|
1742
|
+
const command = POLICY_OPERATION_SQL[node.operation];
|
|
1743
|
+
const roles = node.roles.length === 0 ? "PUBLIC" : node.roles.join(", ");
|
|
1744
|
+
let sql = `CREATE POLICY ${quoteIdentifier(node.name)} ON ${tableRef} AS ${permissiveness} FOR ${command} TO ${roles}`;
|
|
1745
|
+
if (node.using !== void 0) sql += ` USING (${node.using})`;
|
|
1746
|
+
if (node.withCheck !== void 0) sql += ` WITH CHECK (${node.withCheck})`;
|
|
1747
|
+
return {
|
|
1748
|
+
sql,
|
|
1749
|
+
params: []
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
function pgRenderDropPolicy(node) {
|
|
1753
|
+
const tableRef = `${quoteIdentifier(node.schema)}.${quoteIdentifier(node.table)}`;
|
|
1754
|
+
return {
|
|
1755
|
+
sql: `DROP POLICY ${quoteIdentifier(node.name)} ON ${tableRef}`,
|
|
1756
|
+
params: []
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
function pgRenderAlterPolicyRename(node) {
|
|
1760
|
+
const tableRef = `${quoteIdentifier(node.schema)}.${quoteIdentifier(node.table)}`;
|
|
1761
|
+
return {
|
|
1762
|
+
sql: `ALTER POLICY ${quoteIdentifier(node.name)} ON ${tableRef} RENAME TO ${quoteIdentifier(node.newName)}`,
|
|
1763
|
+
params: []
|
|
1764
|
+
};
|
|
1765
|
+
}
|
|
1766
|
+
function pgRenderDisableRowLevelSecurity(node) {
|
|
1767
|
+
return {
|
|
1768
|
+
sql: `ALTER TABLE ${`${quoteIdentifier(node.schema)}.${quoteIdentifier(node.table)}`} DISABLE ROW LEVEL SECURITY`,
|
|
1769
|
+
params: []
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1471
1772
|
async function pgRenderDdlExecuteRequest(ast, codecLookup) {
|
|
1472
1773
|
return ast.accept({
|
|
1473
1774
|
createTable: (node) => pgRenderCreateTable(node, codecLookup),
|
|
1474
1775
|
createSchema: (node) => Promise.resolve(pgRenderCreateSchema(node)),
|
|
1475
|
-
|
|
1776
|
+
createType: (node) => Promise.resolve(pgRenderCreateType(node)),
|
|
1777
|
+
dropType: (node) => Promise.resolve(pgRenderDropType(node)),
|
|
1778
|
+
alterTable: (node) => pgRenderAlterTable(node, codecLookup),
|
|
1779
|
+
createPolicy: (node) => Promise.resolve(pgRenderCreatePolicy(node)),
|
|
1780
|
+
dropPolicy: (node) => Promise.resolve(pgRenderDropPolicy(node)),
|
|
1781
|
+
alterPolicyRename: (node) => Promise.resolve(pgRenderAlterPolicyRename(node)),
|
|
1782
|
+
disableRowLevelSecurity: (node) => Promise.resolve(pgRenderDisableRowLevelSecurity(node))
|
|
1476
1783
|
});
|
|
1477
1784
|
}
|
|
1478
1785
|
//#endregion
|
|
1479
1786
|
export { renderLoweredSql as n, createPostgresBuiltinCodecLookup as r, PostgresControlAdapter as t };
|
|
1480
1787
|
|
|
1481
|
-
//# sourceMappingURL=control-adapter-
|
|
1788
|
+
//# sourceMappingURL=control-adapter-aFfSI2bK.mjs.map
|