@prisma-next/adapter-postgres 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{adapter-CAlWA4ug.mjs → adapter-CwkcdpM_.mjs} +3 -3
- package/dist/adapter-CwkcdpM_.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/column-types.d.mts +1 -6
- package/dist/column-types.d.mts.map +1 -1
- package/dist/column-types.mjs +2 -17
- package/dist/column-types.mjs.map +1 -1
- package/dist/{control-adapter-ZWrjGBq7.mjs → control-adapter-Dspz5uKp.mjs} +227 -226
- package/dist/control-adapter-Dspz5uKp.mjs.map +1 -0
- package/dist/control.d.mts +25 -22
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +3 -3
- package/dist/control.mjs.map +1 -1
- package/dist/{descriptor-meta-NBwpqHS7.mjs → descriptor-meta-DOgMfoqm.mjs} +10 -3
- package/dist/descriptor-meta-DOgMfoqm.mjs.map +1 -0
- package/dist/runtime.d.mts +1 -1
- package/dist/runtime.mjs +2 -2
- package/dist/{types-Dv7M8jx8.d.mts → types-KXRwRZU8.d.mts} +3 -3
- package/dist/types-KXRwRZU8.d.mts.map +1 -0
- package/dist/types.d.mts +1 -1
- package/package.json +22 -22
- package/src/core/adapter.ts +5 -4
- package/src/core/codec-lookup.ts +5 -5
- package/src/core/control-adapter.ts +261 -86
- package/src/core/control-codecs.ts +25 -0
- package/src/core/descriptor-meta.ts +3 -0
- package/src/core/marker-ledger.ts +2 -18
- package/src/core/sql-renderer.ts +146 -8
- package/src/core/types.ts +2 -2
- package/src/exports/column-types.ts +0 -20
- package/src/exports/control.ts +1 -0
- package/dist/adapter-CAlWA4ug.mjs.map +0 -1
- package/dist/control-adapter-ZWrjGBq7.mjs.map +0 -1
- package/dist/descriptor-meta-NBwpqHS7.mjs.map +0 -1
- package/dist/types-Dv7M8jx8.d.mts.map +0 -1
- package/src/core/ddl-renderer.ts +0 -155
- package/src/core/enum-control-hooks.ts +0 -141
|
@@ -4,24 +4,23 @@ import { postgresCodecRegistry } from "@prisma-next/target-postgres/codecs";
|
|
|
4
4
|
import { parseMarkerRowSafely, rethrowMarkerReadError, withMarkerReadErrorHandling } from "@prisma-next/errors/execution";
|
|
5
5
|
import { parseContractMarkerRow } from "@prisma-next/family-sql/verify";
|
|
6
6
|
import { UNBOUND_NAMESPACE_ID } from "@prisma-next/framework-components/ir";
|
|
7
|
-
import {
|
|
7
|
+
import { REFERENTIAL_ACTION_SQL } from "@prisma-next/sql-contract/referential-action-sql";
|
|
8
|
+
import { buildControlTableBootstrapQueries, buildSignMarkerBootstrapQueries, int4, int8, jsonb, pgTable, text, textArray, timestamptz } from "@prisma-next/target-postgres/contract-free";
|
|
8
9
|
import { parsePostgresDefault } from "@prisma-next/target-postgres/default-normalizer";
|
|
9
|
-
import { createResolveExistingEnumValues, enumStorageCompoundKey, readExistingEnumValues, readPostgresSchemaIrAnnotations } from "@prisma-next/target-postgres/enum-planning";
|
|
10
10
|
import { normalizeSchemaNativeType } from "@prisma-next/target-postgres/native-type-normalizer";
|
|
11
|
+
import { escapeLiteral, quoteIdentifier } from "@prisma-next/target-postgres/sql-utils";
|
|
11
12
|
import { blindCast } from "@prisma-next/utils/casts";
|
|
12
13
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
13
|
-
import { REFERENTIAL_ACTION_SQL } from "@prisma-next/sql-contract/referential-action-sql";
|
|
14
|
-
import { escapeLiteral, quoteIdentifier } from "@prisma-next/target-postgres/sql-utils";
|
|
15
|
-
import { PG_ENUM_CODEC_ID, PG_TIMESTAMPTZ_CODEC_ID } from "@prisma-next/target-postgres/codec-ids";
|
|
16
14
|
import { createAstCodecRegistry, deriveParamMetadata, encodeParamsWithMetadata } from "@prisma-next/sql-runtime";
|
|
15
|
+
import { PG_TIMESTAMPTZ_CODEC_ID } from "@prisma-next/target-postgres/codec-ids";
|
|
17
16
|
import { runtimeError } from "@prisma-next/framework-components/runtime";
|
|
18
17
|
//#region src/core/codec-lookup.ts
|
|
19
18
|
/**
|
|
20
|
-
* Build a {@link
|
|
19
|
+
* Build a {@link CodecRegistry} populated with the Postgres-builtin codec definitions only.
|
|
21
20
|
*
|
|
22
|
-
* This is the default
|
|
21
|
+
* This is the default registry used by `createPostgresAdapter()` and `new PostgresControlAdapter()` when called without a stack-derived registry (e.g. from tests, or one-off scripts that don't compose a full stack).
|
|
23
22
|
*
|
|
24
|
-
* Extension codecs (e.g. `pg/vector@1` from `@prisma-next/extension-pgvector`) are intentionally NOT included here: a bare adapter cannot see extensions. Stack-composed paths (`SqlControlAdapterDescriptor.create(stack)` / `SqlRuntimeAdapterDescriptor.create(stack)`) supply the broader, extension-inclusive
|
|
23
|
+
* Extension codecs (e.g. `pg/vector@1` from `@prisma-next/extension-pgvector`) are intentionally NOT included here: a bare adapter cannot see extensions. Stack-composed paths (`SqlControlAdapterDescriptor.create(stack)` / `SqlRuntimeAdapterDescriptor.create(stack)`) supply the broader, extension-inclusive registry at construction time.
|
|
25
24
|
*/
|
|
26
25
|
function createPostgresBuiltinCodecLookup() {
|
|
27
26
|
return extractCodecLookup([{
|
|
@@ -36,173 +35,16 @@ function ledgerOriginFromStored(originCoreHash) {
|
|
|
36
35
|
return originCoreHash;
|
|
37
36
|
}
|
|
38
37
|
//#endregion
|
|
39
|
-
//#region src/core/
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return `PRIMARY KEY (${cols})`;
|
|
47
|
-
}
|
|
48
|
-
function renderForeignKeyConstraint(constraint) {
|
|
49
|
-
const cols = constraint.columns.map(quoteIdentifier).join(", ");
|
|
50
|
-
const refCols = constraint.refColumns.map(quoteIdentifier).join(", ");
|
|
51
|
-
let sql = `FOREIGN KEY (${cols}) REFERENCES ${quoteQualifiedIdentifier(constraint.refTable)} (${refCols})`;
|
|
52
|
-
if (constraint.onDelete !== void 0) sql += ` ON DELETE ${REFERENTIAL_ACTION_SQL[constraint.onDelete]}`;
|
|
53
|
-
if (constraint.onUpdate !== void 0) sql += ` ON UPDATE ${REFERENTIAL_ACTION_SQL[constraint.onUpdate]}`;
|
|
54
|
-
if (constraint.name !== void 0) sql = `CONSTRAINT ${quoteIdentifier(constraint.name)} ${sql}`;
|
|
55
|
-
return sql;
|
|
56
|
-
}
|
|
57
|
-
function renderUniqueConstraint(constraint) {
|
|
58
|
-
const cols = constraint.columns.map(quoteIdentifier).join(", ");
|
|
59
|
-
if (constraint.name !== void 0) return `CONSTRAINT ${quoteIdentifier(constraint.name)} UNIQUE (${cols})`;
|
|
60
|
-
return `UNIQUE (${cols})`;
|
|
61
|
-
}
|
|
62
|
-
function renderTableConstraint(constraint) {
|
|
63
|
-
switch (constraint.kind) {
|
|
64
|
-
case "primary-key": return renderPrimaryKeyConstraint(constraint);
|
|
65
|
-
case "foreign-key": return renderForeignKeyConstraint(constraint);
|
|
66
|
-
case "unique": return renderUniqueConstraint(constraint);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
var PostgresDdlVisitorImpl = class {
|
|
70
|
-
createTable(node) {
|
|
71
|
-
const ifNotExists = node.ifNotExists ? "IF NOT EXISTS " : "";
|
|
72
|
-
const tableRef = node.schema ? `${quoteIdentifier(node.schema)}.${quoteIdentifier(node.table)}` : quoteIdentifier(node.table);
|
|
73
|
-
const columnDefs = node.columns.map((column) => renderColumn$1(column));
|
|
74
|
-
const constraintDefs = node.constraints !== void 0 ? node.constraints.map(renderTableConstraint) : [];
|
|
75
|
-
return `CREATE TABLE ${ifNotExists}${tableRef} (\n ${[...columnDefs, ...constraintDefs].join(",\n ")}\n)`;
|
|
76
|
-
}
|
|
77
|
-
createSchema(node) {
|
|
78
|
-
return `CREATE SCHEMA ${node.ifNotExists ? "IF NOT EXISTS " : ""}${quoteIdentifier(node.schema)}`;
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
function isTextLikeNativeType(nativeType) {
|
|
82
|
-
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(");
|
|
83
|
-
}
|
|
84
|
-
const defaultVisitor = {
|
|
85
|
-
literal(node, ctx) {
|
|
86
|
-
const { value } = node;
|
|
87
|
-
if (typeof value === "number" || typeof value === "boolean") return `DEFAULT ${String(value)}`;
|
|
88
|
-
if (value === null) return "DEFAULT NULL";
|
|
89
|
-
const literal = `'${escapeLiteral(typeof value === "string" ? value : JSON.stringify(value))}'`;
|
|
90
|
-
return isTextLikeNativeType(ctx.nativeType) ? `DEFAULT ${literal}` : `DEFAULT ${literal}::${ctx.nativeType}`;
|
|
91
|
-
},
|
|
92
|
-
function(node, _ctx) {
|
|
93
|
-
if (node.expression === "autoincrement()") return "";
|
|
94
|
-
return `DEFAULT (${node.expression})`;
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
function renderColumn$1(column) {
|
|
98
|
-
const parts = [quoteIdentifier(column.name), column.type];
|
|
99
|
-
if (column.notNull) parts.push("NOT NULL");
|
|
100
|
-
if (column.primaryKey) parts.push("PRIMARY KEY");
|
|
101
|
-
const defaultClause = column.default ? column.default.accept(defaultVisitor, { nativeType: column.type }) : "";
|
|
102
|
-
if (defaultClause.length > 0) parts.push(defaultClause);
|
|
103
|
-
return parts.join(" ");
|
|
104
|
-
}
|
|
105
|
-
function renderLoweredDdl(ast) {
|
|
106
|
-
const sql = ast.accept(new PostgresDdlVisitorImpl());
|
|
107
|
-
return Object.freeze({
|
|
108
|
-
sql,
|
|
109
|
-
params: Object.freeze([])
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
//#endregion
|
|
113
|
-
//#region src/core/enum-control-hooks.ts
|
|
114
|
-
const ENUM_INTROSPECT_QUERY = `
|
|
115
|
-
SELECT
|
|
116
|
-
n.nspname AS schema_name,
|
|
117
|
-
t.typname AS type_name,
|
|
118
|
-
array_agg(e.enumlabel ORDER BY e.enumsortorder) AS values
|
|
119
|
-
FROM pg_type t
|
|
120
|
-
JOIN pg_namespace n ON t.typnamespace = n.oid
|
|
121
|
-
JOIN pg_enum e ON t.oid = e.enumtypid
|
|
122
|
-
WHERE n.nspname = $1
|
|
123
|
-
GROUP BY n.nspname, t.typname
|
|
124
|
-
ORDER BY n.nspname, t.typname
|
|
125
|
-
`;
|
|
126
|
-
function isStringArray(value) {
|
|
127
|
-
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Parses a PostgreSQL array value into a JavaScript string array.
|
|
131
|
-
*
|
|
132
|
-
* The `pg` library returns `array_agg` results either as a JS array
|
|
133
|
-
* (when type parsers are configured) or as a string in PostgreSQL array
|
|
134
|
-
* literal format (`{value1,value2,...}`). Handles PG's quoting rules:
|
|
135
|
-
* - Elements containing commas, quotes, backslashes, or whitespace are
|
|
136
|
-
* double-quoted.
|
|
137
|
-
* - Inside quoted elements, `\"` represents `"` and `\\` represents `\`.
|
|
138
|
-
*
|
|
139
|
-
* Returns `null` when the input cannot be parsed as a PG array.
|
|
140
|
-
*/
|
|
141
|
-
function parsePostgresArray(value) {
|
|
142
|
-
if (isStringArray(value)) return value;
|
|
143
|
-
if (typeof value === "string" && value.startsWith("{") && value.endsWith("}")) {
|
|
144
|
-
const inner = value.slice(1, -1);
|
|
145
|
-
if (inner === "") return [];
|
|
146
|
-
return parseArrayElements(inner);
|
|
147
|
-
}
|
|
148
|
-
return null;
|
|
149
|
-
}
|
|
150
|
-
function parseArrayElements(input) {
|
|
151
|
-
const result = [];
|
|
152
|
-
let i = 0;
|
|
153
|
-
while (i < input.length) {
|
|
154
|
-
if (input[i] === ",") {
|
|
155
|
-
i++;
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
|
-
if (input[i] === "\"") {
|
|
159
|
-
i++;
|
|
160
|
-
let element = "";
|
|
161
|
-
while (i < input.length && input[i] !== "\"") {
|
|
162
|
-
if (input[i] === "\\" && i + 1 < input.length) {
|
|
163
|
-
i++;
|
|
164
|
-
element += input[i];
|
|
165
|
-
} else element += input[i];
|
|
166
|
-
i++;
|
|
167
|
-
}
|
|
168
|
-
i++;
|
|
169
|
-
result.push(element);
|
|
170
|
-
} else {
|
|
171
|
-
const nextComma = input.indexOf(",", i);
|
|
172
|
-
if (nextComma === -1) {
|
|
173
|
-
result.push(input.slice(i).trim());
|
|
174
|
-
i = input.length;
|
|
175
|
-
} else {
|
|
176
|
-
result.push(input.slice(i, nextComma).trim());
|
|
177
|
-
i = nextComma;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return result;
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Reads enum types from the live Postgres schema and returns them in
|
|
185
|
-
* the codec-typed annotation shape consumed by `control-adapter.ts`
|
|
186
|
-
* (which writes them under `schema.annotations.pg.storageTypes`).
|
|
187
|
-
*/
|
|
188
|
-
async function introspectPostgresEnumTypes(options) {
|
|
189
|
-
const namespace = options.schemaName ?? "public";
|
|
190
|
-
const result = await options.driver.query(ENUM_INTROSPECT_QUERY, [namespace]);
|
|
191
|
-
const types = {};
|
|
192
|
-
for (const row of result.rows) {
|
|
193
|
-
const values = parsePostgresArray(row.values);
|
|
194
|
-
if (!values) throw new Error(`Failed to parse enum values for type "${row.type_name}": unexpected format: ${JSON.stringify(row.values)}`);
|
|
195
|
-
types[row.type_name] = {
|
|
196
|
-
codecId: PG_ENUM_CODEC_ID,
|
|
197
|
-
nativeType: row.type_name,
|
|
198
|
-
typeParams: { values }
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
return types;
|
|
38
|
+
//#region src/core/control-codecs.ts
|
|
39
|
+
const CONTROL_CODECS = createAstCodecRegistry(postgresCodecRegistry);
|
|
40
|
+
async function encodeControlQueryParams(lowered, ast, codecs = CONTROL_CODECS) {
|
|
41
|
+
return encodeParamsWithMetadata(lowered.params.map((slot) => {
|
|
42
|
+
if (slot.kind === "literal") return slot.value;
|
|
43
|
+
throw new Error(`control query lowered to a bind slot '${slot.name}', which is unsupported`);
|
|
44
|
+
}), deriveParamMetadata(ast), {}, codecs);
|
|
202
45
|
}
|
|
203
46
|
//#endregion
|
|
204
47
|
//#region src/core/marker-ledger.ts
|
|
205
|
-
const CONTROL_CODECS = createAstCodecRegistry(postgresCodecRegistry);
|
|
206
48
|
const marker = pgTable({
|
|
207
49
|
name: "marker",
|
|
208
50
|
schema: "prisma_contract"
|
|
@@ -269,10 +111,7 @@ function mergeInvariants(current, incoming) {
|
|
|
269
111
|
}
|
|
270
112
|
async function execute(lower, driver, query) {
|
|
271
113
|
const lowered = lower(query);
|
|
272
|
-
const encoded = await
|
|
273
|
-
if (slot.kind === "literal") return slot.value;
|
|
274
|
-
throw new Error("Postgres control DML lowered to a bind parameter, which is unsupported");
|
|
275
|
-
}), deriveParamMetadata(query), {}, CONTROL_CODECS);
|
|
114
|
+
const encoded = await encodeControlQueryParams(lowered, query);
|
|
276
115
|
return (await driver.query(lowered.sql, encoded)).rows;
|
|
277
116
|
}
|
|
278
117
|
//#endregion
|
|
@@ -372,20 +211,87 @@ function renderLimitOffset(keyword, value, contract, pim) {
|
|
|
372
211
|
return `${keyword} ${renderExpr(value, contract, pim)}`;
|
|
373
212
|
}
|
|
374
213
|
function renderSelect(ast, contract, pim) {
|
|
214
|
+
const sourcesByRef = collectTableSources(ast);
|
|
375
215
|
return [
|
|
376
|
-
`SELECT ${renderDistinctPrefix(ast.distinct, ast.distinctOn, contract, pim)}${renderProjection(ast.projection, contract, pim)}`,
|
|
377
|
-
`FROM ${renderSource(ast.from, contract, pim)}
|
|
216
|
+
`SELECT ${renderDistinctPrefix(ast.distinct, ast.distinctOn, sourcesByRef, contract, pim)}${renderProjection(ast.projection, contract, pim)}`,
|
|
217
|
+
ast.from !== void 0 ? `FROM ${renderSource(ast.from, contract, pim)}` : "",
|
|
378
218
|
ast.joins?.length ? ast.joins.map((join) => renderJoin(join, contract, pim)).join(" ") : "",
|
|
379
219
|
ast.where ? `WHERE ${renderWhere(ast.where, contract, pim)}` : "",
|
|
380
220
|
ast.groupBy?.length ? `GROUP BY ${ast.groupBy.map((expr) => renderExpr(expr, contract, pim)).join(", ")}` : "",
|
|
381
221
|
ast.having ? `HAVING ${renderWhere(ast.having, contract, pim)}` : "",
|
|
382
222
|
ast.orderBy?.length ? `ORDER BY ${ast.orderBy.map((order) => {
|
|
383
|
-
return `${
|
|
223
|
+
return `${renderOrderByExpr(order.expr, sourcesByRef, contract, pim)} ${order.dir.toUpperCase()}`;
|
|
384
224
|
}).join(", ")}` : "",
|
|
385
225
|
renderLimitOffset("LIMIT", ast.limit, contract, pim),
|
|
386
226
|
renderLimitOffset("OFFSET", ast.offset, contract, pim)
|
|
387
227
|
].filter((part) => part.length > 0).join(" ").trim();
|
|
388
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* Map a SELECT's table references (the FROM source and any JOIN sources) to their storage coordinate, keyed by the name a `ColumnRef.table` would carry (the alias when present, otherwise the table name). Derived-table sources are skipped — their columns are projected through a sub-select, not a base storage column, so the enum hook does not apply.
|
|
231
|
+
*/
|
|
232
|
+
function collectTableSources(ast) {
|
|
233
|
+
const sources = /* @__PURE__ */ new Map();
|
|
234
|
+
const add = (source) => {
|
|
235
|
+
if (source.kind !== "table-source") return;
|
|
236
|
+
const ref = source.alias ?? source.name;
|
|
237
|
+
sources.set(ref, {
|
|
238
|
+
name: source.name,
|
|
239
|
+
namespaceId: source.namespaceId
|
|
240
|
+
});
|
|
241
|
+
};
|
|
242
|
+
if (ast.from !== void 0) add(ast.from);
|
|
243
|
+
for (const join of ast.joins ?? []) add(join.source);
|
|
244
|
+
return sources;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Ordered, codec-encoded values of the value-set a storage column restricts to, or `undefined` when the referenced column carries no value-set (the common, non-enum case). Resolves the column's storage coordinate from the SELECT's table sources, then the column's `valueSet` ref to the value-set's `values`.
|
|
248
|
+
*/
|
|
249
|
+
function allStrings(values) {
|
|
250
|
+
return values.every((value) => typeof value === "string");
|
|
251
|
+
}
|
|
252
|
+
function resolveEnumOrderValues(ref, sourcesByRef, contract) {
|
|
253
|
+
const source = sourcesByRef.get(ref.table);
|
|
254
|
+
if (source === void 0 || source.namespaceId === void 0) return;
|
|
255
|
+
const sourceNs = contract.storage.namespaces[source.namespaceId];
|
|
256
|
+
const valueSet = (sourceNs !== void 0 ? sourceNs.entries.table?.[source.name]?.columns[ref.column] : void 0)?.valueSet;
|
|
257
|
+
if (valueSet === void 0) return;
|
|
258
|
+
const valueSetNs = contract.storage.namespaces[valueSet.namespaceId];
|
|
259
|
+
return valueSetNs !== void 0 ? valueSetNs.entries.valueSet?.[valueSet.entityName]?.values : void 0;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Ordered values for an unqualified ORDER BY column (an `identifier-ref`, the shape the sql-builder emits for `.orderBy('col')`). Scans every FROM/JOIN source for a column of that name. Resolves only when exactly one source has a column of that name and it carries a value-set; if more than one source has such a column the bare identifier is ambiguous (regardless of which are enum-backed), so it falls through to the plain column rendering.
|
|
263
|
+
*/
|
|
264
|
+
function resolveEnumOrderValuesForIdentifier(name, sourcesByRef, contract) {
|
|
265
|
+
let matchedColumns = 0;
|
|
266
|
+
let resolved;
|
|
267
|
+
for (const source of sourcesByRef.values()) {
|
|
268
|
+
if (source.namespaceId === void 0) continue;
|
|
269
|
+
const identNs = contract.storage.namespaces[source.namespaceId];
|
|
270
|
+
const column = identNs !== void 0 ? identNs.entries.table?.[source.name]?.columns[name] : void 0;
|
|
271
|
+
if (column === void 0) continue;
|
|
272
|
+
matchedColumns += 1;
|
|
273
|
+
if (matchedColumns > 1) return;
|
|
274
|
+
const valueSet = column.valueSet;
|
|
275
|
+
if (valueSet === void 0) return;
|
|
276
|
+
const valueSetNs = contract.storage.namespaces[valueSet.namespaceId];
|
|
277
|
+
resolved = valueSetNs !== void 0 ? valueSetNs.entries.valueSet?.[valueSet.entityName]?.values : void 0;
|
|
278
|
+
}
|
|
279
|
+
return resolved;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Render an ORDER BY expression. A column reference onto an enum-restricted column sorts by declaration order via `array_position(ARRAY[…]::text[], <col>)` over the value-set's ordered values (NULLs return `NULL` from `array_position`, sorting per the clause's default NULL handling). Both qualified `column-ref`s and the unqualified `identifier-ref`s the sql-builder emits for `.orderBy('col')` are intercepted. Every other expression renders unchanged.
|
|
283
|
+
*/
|
|
284
|
+
function renderOrderByExpr(expr, sourcesByRef, contract, pim) {
|
|
285
|
+
if (expr.kind === "column-ref") {
|
|
286
|
+
const orderValues = resolveEnumOrderValues(expr, sourcesByRef, contract);
|
|
287
|
+
if (orderValues !== void 0 && allStrings(orderValues)) return `array_position(ARRAY[${orderValues.map((value) => `'${escapeLiteral(value)}'`).join(", ")}]::text[], ${renderColumn(expr)})`;
|
|
288
|
+
}
|
|
289
|
+
if (expr.kind === "identifier-ref") {
|
|
290
|
+
const orderValues = resolveEnumOrderValuesForIdentifier(expr.name, sourcesByRef, contract);
|
|
291
|
+
if (orderValues !== void 0 && allStrings(orderValues)) return `array_position(ARRAY[${orderValues.map((value) => `'${escapeLiteral(value)}'`).join(", ")}]::text[], ${quoteIdentifier(expr.name)})`;
|
|
292
|
+
}
|
|
293
|
+
return renderExpr(expr, contract, pim);
|
|
294
|
+
}
|
|
389
295
|
function renderProjection(projection, contract, pim) {
|
|
390
296
|
return projection.map((item) => {
|
|
391
297
|
const alias = quoteIdentifier(item.alias);
|
|
@@ -403,13 +309,16 @@ function renderReturning(items, contract, pim) {
|
|
|
403
309
|
return `${renderExpr(item.expr, contract, pim)} AS ${quoteIdentifier(item.alias)}`;
|
|
404
310
|
}).join(", ");
|
|
405
311
|
}
|
|
406
|
-
function renderDistinctPrefix(distinct, distinctOn, contract, pim) {
|
|
407
|
-
if (distinctOn && distinctOn.length > 0) return `DISTINCT ON (${distinctOn.map((expr) =>
|
|
312
|
+
function renderDistinctPrefix(distinct, distinctOn, sourcesByRef, contract, pim) {
|
|
313
|
+
if (distinctOn && distinctOn.length > 0) return `DISTINCT ON (${distinctOn.map((expr) => renderOrderByExpr(expr, sourcesByRef, contract, pim)).join(", ")}) `;
|
|
408
314
|
if (distinct) return "DISTINCT ";
|
|
409
315
|
return "";
|
|
410
316
|
}
|
|
317
|
+
function hasExplicitSchema(table) {
|
|
318
|
+
return "schema" in table && typeof table.schema === "string";
|
|
319
|
+
}
|
|
411
320
|
function qualifyTableFromNamespaceCoordinate(table, contract) {
|
|
412
|
-
if (table
|
|
321
|
+
if (hasExplicitSchema(table)) return `${quoteIdentifier(table.schema)}.${quoteIdentifier(table.name)}`;
|
|
413
322
|
if (table.namespaceId === void 0) return quoteIdentifier(table.name);
|
|
414
323
|
const namespace = contract.storage.namespaces[table.namespaceId];
|
|
415
324
|
if (namespace === void 0) throw new Error(`Table "${table.name}" references namespace "${table.namespaceId}" which is not present as a Postgres schema on the contract`);
|
|
@@ -427,6 +336,11 @@ function renderSource(source, contract, pim) {
|
|
|
427
336
|
switch (node.kind) {
|
|
428
337
|
case "table-source": return renderTableSource(node, contract);
|
|
429
338
|
case "derived-table-source": return `(${renderSelect(node.query, contract, pim)}) AS ${quoteIdentifier(node.alias)}`;
|
|
339
|
+
case "function-source": {
|
|
340
|
+
const args = node.args.map((arg) => renderExpr(arg, contract, pim)).join(", ");
|
|
341
|
+
const call = `${node.fn}(${args})`;
|
|
342
|
+
return node.alias !== void 0 ? `${call} AS ${quoteIdentifier(node.alias)}` : call;
|
|
343
|
+
}
|
|
430
344
|
// v8 ignore next 4
|
|
431
345
|
default: throw new Error(`Unsupported source node kind: ${node.kind}`);
|
|
432
346
|
}
|
|
@@ -632,9 +546,12 @@ function getInsertColumnOrder(rows, contract, tableRef) {
|
|
|
632
546
|
}
|
|
633
547
|
if (orderedColumns.length > 0) return orderedColumns;
|
|
634
548
|
let table;
|
|
635
|
-
if (tableRef.namespaceId !== void 0)
|
|
549
|
+
if (tableRef.namespaceId !== void 0) {
|
|
550
|
+
const ns = contract.storage.namespaces[tableRef.namespaceId];
|
|
551
|
+
table = ns !== void 0 ? ns.entries.table?.[tableName] : void 0;
|
|
552
|
+
}
|
|
636
553
|
if (table === void 0) for (const ns of Object.values(contract.storage.namespaces)) {
|
|
637
|
-
const found = ns.entries.table[tableName];
|
|
554
|
+
const found = ns.entries.table?.[tableName];
|
|
638
555
|
if (found !== void 0) {
|
|
639
556
|
table = found;
|
|
640
557
|
break;
|
|
@@ -732,16 +649,9 @@ const POSTGRES_LEDGER_TABLE = "prisma_contract.ledger";
|
|
|
732
649
|
var PostgresControlAdapter = class {
|
|
733
650
|
familyId = "sql";
|
|
734
651
|
targetId = "postgres";
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
* per-codec metadata at lower-time. Defaults to a Postgres-builtins-only
|
|
739
|
-
* lookup when omitted. Stack-aware callers
|
|
740
|
-
* (`SqlControlAdapterDescriptor.create(stack)`) supply
|
|
741
|
-
* `stack.codecLookup` so extension codecs are visible to the renderer.
|
|
742
|
-
*/
|
|
743
|
-
constructor(codecLookup) {
|
|
744
|
-
this.codecLookup = codecLookup ?? createPostgresBuiltinCodecLookup();
|
|
652
|
+
codecRegistry;
|
|
653
|
+
constructor(codecRegistry) {
|
|
654
|
+
this.codecRegistry = codecRegistry;
|
|
745
655
|
}
|
|
746
656
|
/**
|
|
747
657
|
* Target-specific normalizer for raw Postgres default expressions.
|
|
@@ -754,16 +664,6 @@ var PostgresControlAdapter = class {
|
|
|
754
664
|
* before comparison with contract native types.
|
|
755
665
|
*/
|
|
756
666
|
normalizeNativeType = normalizeSchemaNativeType;
|
|
757
|
-
/**
|
|
758
|
-
* Bridges native `PostgresEnumStorageEntry` IR walks against the Postgres
|
|
759
|
-
* introspection shape (`schema.annotations.pg.storageTypes`). Lets
|
|
760
|
-
* the family-level schema verifier walk enum types without reaching
|
|
761
|
-
* into target-specific annotation layouts itself.
|
|
762
|
-
*/
|
|
763
|
-
resolveExistingEnumValues = (schema, enumType, namespaceId) => {
|
|
764
|
-
return readExistingEnumValues(schema, namespaceId === UNBOUND_NAMESPACE_ID ? readPostgresSchemaIrAnnotations(schema).schema ?? "public" : namespaceId, enumType.nativeType);
|
|
765
|
-
};
|
|
766
|
-
resolveExistingEnumValuesForContract = (contract) => createResolveExistingEnumValues(contract.storage);
|
|
767
667
|
bootstrapControlTableQueries() {
|
|
768
668
|
return buildControlTableBootstrapQueries();
|
|
769
669
|
}
|
|
@@ -779,8 +679,24 @@ var PostgresControlAdapter = class {
|
|
|
779
679
|
* without instantiating the runtime adapter.
|
|
780
680
|
*/
|
|
781
681
|
lower(ast, context) {
|
|
782
|
-
if (isDdlNode(ast))
|
|
783
|
-
return renderLoweredSql(ast, context.contract, this.
|
|
682
|
+
if (isDdlNode(ast)) throw new Error("lower() cannot lower DDL: DDL default literals require inline codec encoding, which is async. Use lowerToExecuteRequest().");
|
|
683
|
+
return renderLoweredSql(ast, blindCast(context.contract), this.codecRegistry);
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Lower an AST all the way to a driver-ready statement. For DDL nodes,
|
|
687
|
+
* literal column defaults are formatted as inline SQL with proper quoting and
|
|
688
|
+
* `::nativeType` cast suffixes. For query ASTs, params are kept as `$N`
|
|
689
|
+
* placeholders; wire values go in `params`. Does NOT call `this.lower()` —
|
|
690
|
+
* independent implementation.
|
|
691
|
+
*/
|
|
692
|
+
async lowerToExecuteRequest(ast, context) {
|
|
693
|
+
if (isDdlNode(ast)) return pgRenderDdlExecuteRequest(blindCast(ast), this.codecRegistry);
|
|
694
|
+
const lowered = renderLoweredSql(ast, blindCast(context?.contract), this.codecRegistry);
|
|
695
|
+
const params = await encodeControlQueryParams(lowered, ast, blindCast(this.codecRegistry));
|
|
696
|
+
return {
|
|
697
|
+
sql: lowered.sql,
|
|
698
|
+
params
|
|
699
|
+
};
|
|
784
700
|
}
|
|
785
701
|
/**
|
|
786
702
|
* Reads the contract marker from `prisma_contract.marker`. Probes
|
|
@@ -1020,23 +936,13 @@ var PostgresControlAdapter = class {
|
|
|
1020
936
|
for (const schema of uniqueSchemas) perSchema.push(await this.introspectSchema(driver, schema));
|
|
1021
937
|
const mergedTables = {};
|
|
1022
938
|
for (const ir of perSchema) for (const [tableName, table] of Object.entries(ir.tables)) mergedTables[tableName] = table;
|
|
1023
|
-
const mergedStorageTypes = {};
|
|
1024
|
-
for (let i = 0; i < perSchema.length; i++) {
|
|
1025
|
-
const ir = perSchema[i];
|
|
1026
|
-
const pg = blindCast(ir?.annotations?.["pg"])?.storageTypes;
|
|
1027
|
-
if (!pg) continue;
|
|
1028
|
-
for (const [key, value] of Object.entries(pg)) mergedStorageTypes[key] = value;
|
|
1029
|
-
}
|
|
1030
939
|
const firstAnnotations = perSchema[0]?.annotations;
|
|
1031
940
|
const firstPg = blindCast(firstAnnotations?.["pg"]) ?? {};
|
|
1032
941
|
return {
|
|
1033
942
|
tables: mergedTables,
|
|
1034
943
|
...ifDefined("annotations", {
|
|
1035
944
|
...firstAnnotations,
|
|
1036
|
-
pg: {
|
|
1037
|
-
...firstPg,
|
|
1038
|
-
...ifDefined("storageTypes", Object.keys(mergedStorageTypes).length > 0 ? mergedStorageTypes : void 0)
|
|
1039
|
-
}
|
|
945
|
+
pg: { ...firstPg }
|
|
1040
946
|
})
|
|
1041
947
|
};
|
|
1042
948
|
}
|
|
@@ -1295,18 +1201,18 @@ var PostgresControlAdapter = class {
|
|
|
1295
1201
|
...ifDefined("checks", checksForTable.length > 0 ? checksForTable : void 0)
|
|
1296
1202
|
};
|
|
1297
1203
|
}
|
|
1298
|
-
const
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1204
|
+
const nativeEnumTypeNames = (await driver.query(`SELECT t.typname
|
|
1205
|
+
FROM pg_catalog.pg_type t
|
|
1206
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
1207
|
+
WHERE t.typtype = 'e'
|
|
1208
|
+
AND n.nspname = $1
|
|
1209
|
+
ORDER BY t.typname`, [schema])).rows.map((r) => r.typname);
|
|
1304
1210
|
return {
|
|
1305
1211
|
tables,
|
|
1306
1212
|
annotations: { pg: {
|
|
1307
1213
|
schema,
|
|
1308
1214
|
version: await this.getPostgresVersion(driver),
|
|
1309
|
-
...
|
|
1215
|
+
...nativeEnumTypeNames.length > 0 && { nativeEnumTypeNames }
|
|
1310
1216
|
} }
|
|
1311
1217
|
};
|
|
1312
1218
|
}
|
|
@@ -1474,7 +1380,102 @@ function extractQuotedLiterals(listBody) {
|
|
|
1474
1380
|
const values = [...listBody.matchAll(/'((?:[^'\\]|\\.|'')*)'/g)].map((m) => (m[1] ?? "").replace(/''/g, "'"));
|
|
1475
1381
|
return values.length > 0 ? values : void 0;
|
|
1476
1382
|
}
|
|
1383
|
+
function pgIsTextLikeNativeType(nativeType) {
|
|
1384
|
+
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
|
+
}
|
|
1386
|
+
function pgInlineLiteral(wire, nativeType) {
|
|
1387
|
+
if (wire === null) return "NULL";
|
|
1388
|
+
if (typeof wire === "boolean") return wire ? "true" : "false";
|
|
1389
|
+
if (typeof wire === "number") {
|
|
1390
|
+
if (!Number.isFinite(wire)) throw new Error(`pgRenderDdlExecuteRequest: non-finite number wire value ${String(wire)} cannot be emitted as a DEFAULT literal for native type "${nativeType}"`);
|
|
1391
|
+
return String(wire);
|
|
1392
|
+
}
|
|
1393
|
+
if (typeof wire === "bigint") return String(wire);
|
|
1394
|
+
if (wire instanceof Date) {
|
|
1395
|
+
if (Number.isNaN(wire.getTime())) throw new Error(`pgRenderDdlExecuteRequest: invalid Date value cannot be emitted as a DEFAULT literal for native type "${nativeType}"`);
|
|
1396
|
+
const quoted = `'${escapeLiteral(wire.toISOString())}'`;
|
|
1397
|
+
return pgIsTextLikeNativeType(nativeType) ? quoted : `${quoted}::${nativeType}`;
|
|
1398
|
+
}
|
|
1399
|
+
if (typeof wire === "string") {
|
|
1400
|
+
const quoted = `'${escapeLiteral(wire)}'`;
|
|
1401
|
+
return pgIsTextLikeNativeType(nativeType) ? quoted : `${quoted}::${nativeType}`;
|
|
1402
|
+
}
|
|
1403
|
+
if (wire instanceof Uint8Array) return `'\\x${Array.from(wire).map((b) => b.toString(16).padStart(2, "0")).join("")}'::${nativeType}`;
|
|
1404
|
+
if (typeof wire === "object") return `${`'${escapeLiteral(JSON.stringify(wire))}'`}::${nativeType}`;
|
|
1405
|
+
throw new Error(`pgRenderDdlExecuteRequest: unexpected wire type "${typeof wire}" for native type "${nativeType}"`);
|
|
1406
|
+
}
|
|
1407
|
+
async function pgRenderDdlColumnDefault(def, nativeType, codecLookup, codecRef) {
|
|
1408
|
+
if (def.kind === "function") {
|
|
1409
|
+
if (def.expression === "autoincrement()") return "";
|
|
1410
|
+
return `DEFAULT (${def.expression})`;
|
|
1411
|
+
}
|
|
1412
|
+
if (codecRef !== void 0) {
|
|
1413
|
+
const codec = codecLookup.get(codecRef.codecId);
|
|
1414
|
+
if (codec !== void 0) return `DEFAULT ${pgInlineLiteral(await codec.encode(def.value, {}), nativeType)}`;
|
|
1415
|
+
}
|
|
1416
|
+
return `DEFAULT ${pgInlineLiteral(def.value, nativeType)}`;
|
|
1417
|
+
}
|
|
1418
|
+
async function pgRenderDdlColumn(column, codecLookup) {
|
|
1419
|
+
const parts = [quoteIdentifier(column.name), column.type];
|
|
1420
|
+
if (column.default) {
|
|
1421
|
+
const clause = await pgRenderDdlColumnDefault(column.default, column.type, codecLookup, column.codecRef);
|
|
1422
|
+
if (clause.length > 0) parts.push(clause);
|
|
1423
|
+
}
|
|
1424
|
+
if (column.notNull) parts.push("NOT NULL");
|
|
1425
|
+
if (column.primaryKey) parts.push("PRIMARY KEY");
|
|
1426
|
+
return parts.join(" ");
|
|
1427
|
+
}
|
|
1428
|
+
function pgRenderDdlConstraint(constraint) {
|
|
1429
|
+
if (constraint.kind === "primary-key") {
|
|
1430
|
+
const cols = constraint.columns.map(quoteIdentifier).join(", ");
|
|
1431
|
+
if (constraint.name !== void 0) return `CONSTRAINT ${quoteIdentifier(constraint.name)} PRIMARY KEY (${cols})`;
|
|
1432
|
+
return `PRIMARY KEY (${cols})`;
|
|
1433
|
+
}
|
|
1434
|
+
if (constraint.kind === "foreign-key") {
|
|
1435
|
+
let sql = `FOREIGN KEY (${constraint.columns.map(quoteIdentifier).join(", ")}) REFERENCES ${constraint.refTable.split(".").map(quoteIdentifier).join(".")} (${constraint.refColumns.map(quoteIdentifier).join(", ")})`;
|
|
1436
|
+
if (constraint.onDelete !== void 0) sql += ` ON DELETE ${REFERENTIAL_ACTION_SQL[constraint.onDelete]}`;
|
|
1437
|
+
if (constraint.onUpdate !== void 0) sql += ` ON UPDATE ${REFERENTIAL_ACTION_SQL[constraint.onUpdate]}`;
|
|
1438
|
+
if (constraint.name !== void 0) sql = `CONSTRAINT ${quoteIdentifier(constraint.name)} ${sql}`;
|
|
1439
|
+
return sql;
|
|
1440
|
+
}
|
|
1441
|
+
const cols = constraint.columns.map(quoteIdentifier).join(", ");
|
|
1442
|
+
if (constraint.name !== void 0) return `CONSTRAINT ${quoteIdentifier(constraint.name)} UNIQUE (${cols})`;
|
|
1443
|
+
return `UNIQUE (${cols})`;
|
|
1444
|
+
}
|
|
1445
|
+
async function pgRenderCreateTable(node, codecLookup) {
|
|
1446
|
+
const ifNotExists = node.ifNotExists ? "IF NOT EXISTS " : "";
|
|
1447
|
+
const tableRef = node.schema ? `${quoteIdentifier(node.schema)}.${quoteIdentifier(node.table)}` : quoteIdentifier(node.table);
|
|
1448
|
+
const columnDefs = await Promise.all(node.columns.map((col) => pgRenderDdlColumn(col, codecLookup)));
|
|
1449
|
+
const constraintDefs = node.constraints !== void 0 ? node.constraints.map(pgRenderDdlConstraint) : [];
|
|
1450
|
+
return {
|
|
1451
|
+
sql: `CREATE TABLE ${ifNotExists}${tableRef} (\n ${[...columnDefs, ...constraintDefs].join(",\n ")}\n)`,
|
|
1452
|
+
params: []
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
function pgRenderCreateSchema(node) {
|
|
1456
|
+
return {
|
|
1457
|
+
sql: `CREATE SCHEMA ${node.ifNotExists ? "IF NOT EXISTS " : ""}${quoteIdentifier(node.schema)}`,
|
|
1458
|
+
params: []
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
async function pgRenderAlterTable(node, codecLookup) {
|
|
1462
|
+
const tableRef = node.schema ? `${quoteIdentifier(node.schema)}.${quoteIdentifier(node.table)}` : quoteIdentifier(node.table);
|
|
1463
|
+
const actionVisitor = { async addColumn(action) {
|
|
1464
|
+
return `ADD COLUMN ${await pgRenderDdlColumn(action.column, codecLookup)}`;
|
|
1465
|
+
} };
|
|
1466
|
+
return {
|
|
1467
|
+
sql: `ALTER TABLE ${tableRef} ${(await Promise.all(node.actions.map((a) => a.accept(actionVisitor)))).join(", ")}`,
|
|
1468
|
+
params: []
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
async function pgRenderDdlExecuteRequest(ast, codecLookup) {
|
|
1472
|
+
return ast.accept({
|
|
1473
|
+
createTable: (node) => pgRenderCreateTable(node, codecLookup),
|
|
1474
|
+
createSchema: (node) => Promise.resolve(pgRenderCreateSchema(node)),
|
|
1475
|
+
alterTable: (node) => pgRenderAlterTable(node, codecLookup)
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1477
1478
|
//#endregion
|
|
1478
|
-
export {
|
|
1479
|
+
export { renderLoweredSql as n, createPostgresBuiltinCodecLookup as r, PostgresControlAdapter as t };
|
|
1479
1480
|
|
|
1480
|
-
//# sourceMappingURL=control-adapter-
|
|
1481
|
+
//# sourceMappingURL=control-adapter-Dspz5uKp.mjs.map
|