@prisma-next/adapter-postgres 0.3.0-dev.4 → 0.3.0-dev.41

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 (77) hide show
  1. package/README.md +64 -2
  2. package/dist/adapter-B5uYhMy7.mjs +266 -0
  3. package/dist/adapter-B5uYhMy7.mjs.map +1 -0
  4. package/dist/adapter.d.mts +23 -0
  5. package/dist/adapter.d.mts.map +1 -0
  6. package/dist/adapter.mjs +5 -0
  7. package/dist/codec-ids-Bsm9c7ns.mjs +29 -0
  8. package/dist/codec-ids-Bsm9c7ns.mjs.map +1 -0
  9. package/dist/codec-types.d.mts +141 -0
  10. package/dist/codec-types.d.mts.map +1 -0
  11. package/dist/codec-types.mjs +4 -0
  12. package/dist/codecs-BfC_5c-4.mjs +207 -0
  13. package/dist/codecs-BfC_5c-4.mjs.map +1 -0
  14. package/dist/column-types.d.mts +110 -0
  15. package/dist/column-types.d.mts.map +1 -0
  16. package/dist/column-types.mjs +180 -0
  17. package/dist/column-types.mjs.map +1 -0
  18. package/dist/control.d.mts +111 -0
  19. package/dist/control.d.mts.map +1 -0
  20. package/dist/control.mjs +427 -0
  21. package/dist/control.mjs.map +1 -0
  22. package/dist/descriptor-meta-ilnFI7bx.mjs +921 -0
  23. package/dist/descriptor-meta-ilnFI7bx.mjs.map +1 -0
  24. package/dist/runtime.d.mts +19 -0
  25. package/dist/runtime.d.mts.map +1 -0
  26. package/dist/runtime.mjs +85 -0
  27. package/dist/runtime.mjs.map +1 -0
  28. package/dist/sql-utils-CSfAGEwF.mjs +78 -0
  29. package/dist/sql-utils-CSfAGEwF.mjs.map +1 -0
  30. package/dist/types-CXO7EB60.d.mts +19 -0
  31. package/dist/types-CXO7EB60.d.mts.map +1 -0
  32. package/dist/types.d.mts +2 -0
  33. package/dist/types.mjs +1 -0
  34. package/package.json +39 -47
  35. package/src/core/adapter.ts +517 -0
  36. package/src/core/codec-ids.ts +28 -0
  37. package/src/core/codecs.ts +496 -0
  38. package/src/core/control-adapter.ts +536 -0
  39. package/src/core/default-normalizer.ts +90 -0
  40. package/src/core/descriptor-meta.ts +253 -0
  41. package/src/core/enum-control-hooks.ts +735 -0
  42. package/src/core/json-schema-type-expression.ts +131 -0
  43. package/src/core/json-schema-validator.ts +53 -0
  44. package/src/core/parameterized-types.ts +118 -0
  45. package/src/core/sql-utils.ts +111 -0
  46. package/src/core/standard-schema.ts +71 -0
  47. package/src/core/types.ts +53 -0
  48. package/src/exports/adapter.ts +1 -0
  49. package/src/exports/codec-types.ts +83 -0
  50. package/src/exports/column-types.ts +277 -0
  51. package/src/exports/control.ts +27 -0
  52. package/src/exports/runtime.ts +75 -0
  53. package/src/exports/types.ts +14 -0
  54. package/dist/exports/adapter.d.ts +0 -21
  55. package/dist/exports/adapter.js +0 -8
  56. package/dist/exports/adapter.js.map +0 -1
  57. package/dist/exports/chunk-B5SU5BVC.js +0 -47
  58. package/dist/exports/chunk-B5SU5BVC.js.map +0 -1
  59. package/dist/exports/chunk-CPAKRHXM.js +0 -162
  60. package/dist/exports/chunk-CPAKRHXM.js.map +0 -1
  61. package/dist/exports/chunk-ZHJOVBWT.js +0 -301
  62. package/dist/exports/chunk-ZHJOVBWT.js.map +0 -1
  63. package/dist/exports/codec-types.d.ts +0 -38
  64. package/dist/exports/codec-types.js +0 -7
  65. package/dist/exports/codec-types.js.map +0 -1
  66. package/dist/exports/column-types.d.ts +0 -20
  67. package/dist/exports/column-types.js +0 -49
  68. package/dist/exports/column-types.js.map +0 -1
  69. package/dist/exports/control.d.ts +0 -9
  70. package/dist/exports/control.js +0 -279
  71. package/dist/exports/control.js.map +0 -1
  72. package/dist/exports/runtime.d.ts +0 -17
  73. package/dist/exports/runtime.js +0 -20
  74. package/dist/exports/runtime.js.map +0 -1
  75. package/dist/exports/types.d.ts +0 -19
  76. package/dist/exports/types.js +0 -1
  77. package/dist/exports/types.js.map +0 -1
@@ -0,0 +1,517 @@
1
+ import type {
2
+ Adapter,
3
+ AdapterProfile,
4
+ BinaryExpr,
5
+ CodecParamsDescriptor,
6
+ ColumnRef,
7
+ DeleteAst,
8
+ IncludeRef,
9
+ InsertAst,
10
+ JoinAst,
11
+ LiteralExpr,
12
+ LowererContext,
13
+ NullCheckExpr,
14
+ OperationExpr,
15
+ ParamRef,
16
+ QueryAst,
17
+ SelectAst,
18
+ UpdateAst,
19
+ WhereExpr,
20
+ } from '@prisma-next/sql-relational-core/ast';
21
+ import { createCodecRegistry, isOperationExpr } from '@prisma-next/sql-relational-core/ast';
22
+ import { ifDefined } from '@prisma-next/utils/defined';
23
+ import { PG_JSON_CODEC_ID, PG_JSONB_CODEC_ID } from './codec-ids';
24
+ import { codecDefinitions } from './codecs';
25
+ import { escapeLiteral } from './sql-utils';
26
+ import type { PostgresAdapterOptions, PostgresContract, PostgresLoweredStatement } from './types';
27
+
28
+ const VECTOR_CODEC_ID = 'pg/vector@1' as const;
29
+
30
+ function getCodecParamCast(codecId: string | undefined): string | undefined {
31
+ if (codecId === VECTOR_CODEC_ID) {
32
+ return 'vector';
33
+ }
34
+ if (codecId === PG_JSON_CODEC_ID) {
35
+ return 'json';
36
+ }
37
+ if (codecId === PG_JSONB_CODEC_ID) {
38
+ return 'jsonb';
39
+ }
40
+ return undefined;
41
+ }
42
+
43
+ function renderTypedParam(index: number, codecId: string | undefined): string {
44
+ const cast = getCodecParamCast(codecId);
45
+ return cast ? `$${index}::${cast}` : `$${index}`;
46
+ }
47
+
48
+ const defaultCapabilities = Object.freeze({
49
+ postgres: {
50
+ orderBy: true,
51
+ limit: true,
52
+ lateral: true,
53
+ jsonAgg: true,
54
+ returning: true,
55
+ },
56
+ sql: {
57
+ enums: true,
58
+ },
59
+ });
60
+
61
+ type AdapterCodec = (typeof codecDefinitions)[keyof typeof codecDefinitions]['codec'];
62
+ type ParameterizedCodec = AdapterCodec & {
63
+ readonly paramsSchema: NonNullable<AdapterCodec['paramsSchema']>;
64
+ };
65
+
66
+ const parameterizedCodecs: ReadonlyArray<CodecParamsDescriptor> = Object.values(codecDefinitions)
67
+ .map((definition) => definition.codec)
68
+ .filter((codec): codec is ParameterizedCodec => codec.paramsSchema !== undefined)
69
+ .map((codec) =>
70
+ Object.freeze({
71
+ codecId: codec.id,
72
+ paramsSchema: codec.paramsSchema,
73
+ ...ifDefined('init', codec.init),
74
+ }),
75
+ );
76
+
77
+ class PostgresAdapterImpl implements Adapter<QueryAst, PostgresContract, PostgresLoweredStatement> {
78
+ // These fields make the adapter instance structurally compatible with
79
+ // RuntimeAdapterInstance<'sql', 'postgres'> without introducing a runtime-plane dependency.
80
+ readonly familyId = 'sql' as const;
81
+ readonly targetId = 'postgres' as const;
82
+
83
+ readonly profile: AdapterProfile<'postgres'>;
84
+ private readonly codecRegistry = (() => {
85
+ const registry = createCodecRegistry();
86
+ for (const definition of Object.values(codecDefinitions)) {
87
+ registry.register(definition.codec);
88
+ }
89
+ return registry;
90
+ })();
91
+
92
+ constructor(options?: PostgresAdapterOptions) {
93
+ this.profile = Object.freeze({
94
+ id: options?.profileId ?? 'postgres/default@1',
95
+ target: 'postgres',
96
+ capabilities: defaultCapabilities,
97
+ codecs: () => this.codecRegistry,
98
+ });
99
+ }
100
+
101
+ parameterizedCodecs(): ReadonlyArray<CodecParamsDescriptor> {
102
+ return parameterizedCodecs;
103
+ }
104
+
105
+ lower(ast: QueryAst, context: LowererContext<PostgresContract>) {
106
+ let sql: string;
107
+ const params = context.params ? [...context.params] : [];
108
+
109
+ if (ast.kind === 'select') {
110
+ sql = renderSelect(ast, context.contract);
111
+ } else if (ast.kind === 'insert') {
112
+ sql = renderInsert(ast, context.contract);
113
+ } else if (ast.kind === 'update') {
114
+ sql = renderUpdate(ast, context.contract);
115
+ } else if (ast.kind === 'delete') {
116
+ sql = renderDelete(ast, context.contract);
117
+ } else {
118
+ throw new Error(`Unsupported AST kind: ${(ast as { kind: string }).kind}`);
119
+ }
120
+
121
+ return Object.freeze({
122
+ profileId: this.profile.id,
123
+ body: Object.freeze({ sql, params }),
124
+ });
125
+ }
126
+ }
127
+
128
+ function renderSelect(ast: SelectAst, contract?: PostgresContract): string {
129
+ const selectClause = `SELECT ${renderProjection(ast, contract)}`;
130
+ const fromClause = `FROM ${quoteIdentifier(ast.from.name)}`;
131
+
132
+ const joinsClause = ast.joins?.length
133
+ ? ast.joins.map((join) => renderJoin(join, contract)).join(' ')
134
+ : '';
135
+ const includesClause = ast.includes?.length
136
+ ? ast.includes.map((include) => renderInclude(include, contract)).join(' ')
137
+ : '';
138
+
139
+ const whereClause = ast.where ? ` WHERE ${renderWhere(ast.where, contract)}` : '';
140
+ const orderClause = ast.orderBy?.length
141
+ ? ` ORDER BY ${ast.orderBy
142
+ .map((order) => {
143
+ const expr = renderExpr(order.expr as ColumnRef | OperationExpr, contract);
144
+ return `${expr} ${order.dir.toUpperCase()}`;
145
+ })
146
+ .join(', ')}`
147
+ : '';
148
+ const limitClause = typeof ast.limit === 'number' ? ` LIMIT ${ast.limit}` : '';
149
+
150
+ const clauses = [joinsClause, includesClause].filter(Boolean).join(' ');
151
+ return `${selectClause} ${fromClause}${clauses ? ` ${clauses}` : ''}${whereClause}${orderClause}${limitClause}`.trim();
152
+ }
153
+
154
+ function renderProjection(ast: SelectAst, contract?: PostgresContract): string {
155
+ return ast.project
156
+ .map((item) => {
157
+ const expr = item.expr as ColumnRef | IncludeRef | OperationExpr | LiteralExpr;
158
+ if (expr.kind === 'includeRef') {
159
+ // For include references, select the column from the LATERAL join alias
160
+ // The LATERAL subquery returns a single column (the JSON array) with the alias
161
+ // The table is aliased as {alias}_lateral, and the column inside is aliased as the include alias
162
+ // We select it using table_alias.column_alias
163
+ const tableAlias = `${expr.alias}_lateral`;
164
+ return `${quoteIdentifier(tableAlias)}.${quoteIdentifier(expr.alias)} AS ${quoteIdentifier(item.alias)}`;
165
+ }
166
+ if (expr.kind === 'operation') {
167
+ const operation = renderOperation(expr, contract);
168
+ const alias = quoteIdentifier(item.alias);
169
+ return `${operation} AS ${alias}`;
170
+ }
171
+ if (expr.kind === 'literal') {
172
+ const literal = renderLiteral(expr);
173
+ const alias = quoteIdentifier(item.alias);
174
+ return `${literal} AS ${alias}`;
175
+ }
176
+ const column = renderColumn(expr as ColumnRef);
177
+ const alias = quoteIdentifier(item.alias);
178
+ return `${column} AS ${alias}`;
179
+ })
180
+ .join(', ');
181
+ }
182
+
183
+ function renderWhere(expr: WhereExpr, contract?: PostgresContract): string {
184
+ if (expr.kind === 'exists') {
185
+ const notKeyword = expr.not ? 'NOT ' : '';
186
+ const subquery = renderSelect(expr.subquery, contract);
187
+ return `${notKeyword}EXISTS (${subquery})`;
188
+ }
189
+ if (expr.kind === 'nullCheck') {
190
+ return renderNullCheck(expr, contract);
191
+ }
192
+ if (expr.kind === 'and') {
193
+ if (expr.exprs.length === 0) {
194
+ return 'TRUE';
195
+ }
196
+ return `(${expr.exprs.map((part) => renderWhere(part, contract)).join(' AND ')})`;
197
+ }
198
+ if (expr.kind === 'or') {
199
+ if (expr.exprs.length === 0) {
200
+ return 'FALSE';
201
+ }
202
+ return `(${expr.exprs.map((part) => renderWhere(part, contract)).join(' OR ')})`;
203
+ }
204
+ return renderBinary(expr, contract);
205
+ }
206
+
207
+ function renderNullCheck(expr: NullCheckExpr, contract?: PostgresContract): string {
208
+ const rendered = renderExpr(expr.expr as ColumnRef | OperationExpr, contract);
209
+ // Only wrap in parentheses if it's an operation expression
210
+ const renderedExpr = isOperationExpr(expr.expr) ? `(${rendered})` : rendered;
211
+ return expr.isNull ? `${renderedExpr} IS NULL` : `${renderedExpr} IS NOT NULL`;
212
+ }
213
+
214
+ function renderBinary(expr: BinaryExpr, contract?: PostgresContract): string {
215
+ const leftExpr = expr.left as ColumnRef | OperationExpr;
216
+ const left = renderExpr(leftExpr, contract);
217
+ // Only wrap in parentheses if it's an operation expression
218
+ const leftRendered = isOperationExpr(leftExpr) ? `(${left})` : left;
219
+ const right = renderBinaryRight(expr.right, contract);
220
+ const operatorMap: Record<BinaryExpr['op'], string> = {
221
+ eq: '=',
222
+ neq: '!=',
223
+ gt: '>',
224
+ lt: '<',
225
+ gte: '>=',
226
+ lte: '<=',
227
+ like: 'LIKE',
228
+ ilike: 'ILIKE',
229
+ in: 'IN',
230
+ notIn: 'NOT IN',
231
+ };
232
+
233
+ return `${leftRendered} ${operatorMap[expr.op]} ${right}`;
234
+ }
235
+
236
+ function renderBinaryRight(right: BinaryExpr['right'], contract?: PostgresContract): string {
237
+ if (right.kind === 'col') {
238
+ return renderColumn(right);
239
+ }
240
+ if (right.kind === 'param') {
241
+ return renderParam(right, contract);
242
+ }
243
+ if (right.kind === 'literal') {
244
+ return renderLiteral(right);
245
+ }
246
+ if (right.kind === 'operation') {
247
+ return renderExpr(right, contract);
248
+ }
249
+ if (right.kind === 'listLiteral') {
250
+ if (right.values.length === 0) {
251
+ return '(NULL)';
252
+ }
253
+ const values = right.values.map((value) =>
254
+ value.kind === 'param' ? renderParam(value, contract) : renderLiteral(value),
255
+ );
256
+ return `(${values.join(', ')})`;
257
+ }
258
+
259
+ throw new Error(`Unsupported binary right expression kind: ${(right as { kind: string }).kind}`);
260
+ }
261
+
262
+ function renderColumn(ref: ColumnRef): string {
263
+ return `${quoteIdentifier(ref.table)}.${quoteIdentifier(ref.column)}`;
264
+ }
265
+
266
+ function renderExpr(expr: ColumnRef | OperationExpr, contract?: PostgresContract): string {
267
+ if (isOperationExpr(expr)) {
268
+ return renderOperation(expr, contract);
269
+ }
270
+ return renderColumn(expr);
271
+ }
272
+
273
+ function renderParam(
274
+ ref: ParamRef,
275
+ contract?: PostgresContract,
276
+ tableName?: string,
277
+ columnName?: string,
278
+ ): string {
279
+ if (contract && tableName && columnName) {
280
+ const tableMeta = contract.storage.tables[tableName];
281
+ const columnMeta = tableMeta?.columns[columnName];
282
+ return renderTypedParam(ref.index, columnMeta?.codecId);
283
+ }
284
+ return `$${ref.index}`;
285
+ }
286
+
287
+ function renderLiteral(expr: LiteralExpr): string {
288
+ if (typeof expr.value === 'string') {
289
+ return `'${escapeLiteral(expr.value)}'`;
290
+ }
291
+ if (typeof expr.value === 'number' || typeof expr.value === 'boolean') {
292
+ return String(expr.value);
293
+ }
294
+ if (expr.value === null) {
295
+ return 'NULL';
296
+ }
297
+ if (Array.isArray(expr.value)) {
298
+ return `ARRAY[${expr.value.map((v: unknown) => renderLiteral({ kind: 'literal', value: v })).join(', ')}]`;
299
+ }
300
+ return JSON.stringify(expr.value);
301
+ }
302
+
303
+ function renderOperation(expr: OperationExpr, contract?: PostgresContract): string {
304
+ const self = renderExpr(expr.self, contract);
305
+ // For vector operations, cast param arguments to vector type
306
+ const isVectorOperation = expr.forTypeId === VECTOR_CODEC_ID;
307
+ const args = expr.args.map((arg: ColumnRef | ParamRef | LiteralExpr | OperationExpr) => {
308
+ if (arg.kind === 'col') {
309
+ return renderColumn(arg);
310
+ }
311
+ if (arg.kind === 'param') {
312
+ // Cast vector operation parameters to vector type
313
+ return isVectorOperation ? `$${arg.index}::vector` : renderParam(arg, contract);
314
+ }
315
+ if (arg.kind === 'literal') {
316
+ return renderLiteral(arg);
317
+ }
318
+ if (arg.kind === 'operation') {
319
+ return renderOperation(arg, contract);
320
+ }
321
+ const _exhaustive: never = arg;
322
+ throw new Error(`Unsupported argument kind: ${(_exhaustive as { kind: string }).kind}`);
323
+ });
324
+
325
+ let result = expr.lowering.template;
326
+ result = result.replace(/\$\{self\}/g, self);
327
+ for (let i = 0; i < args.length; i++) {
328
+ result = result.replace(new RegExp(`\\$\\{arg${i}\\}`, 'g'), args[i] ?? '');
329
+ }
330
+
331
+ if (expr.lowering.strategy === 'function') {
332
+ return result;
333
+ }
334
+
335
+ return result;
336
+ }
337
+
338
+ function renderJoin(join: JoinAst, _contract?: PostgresContract): string {
339
+ const joinType = join.joinType.toUpperCase();
340
+ const table = quoteIdentifier(join.table.name);
341
+ const onClause = renderJoinOn(join.on);
342
+ return `${joinType} JOIN ${table} ON ${onClause}`;
343
+ }
344
+
345
+ function renderJoinOn(on: JoinAst['on']): string {
346
+ if (on.kind === 'eqCol') {
347
+ const left = renderColumn(on.left);
348
+ const right = renderColumn(on.right);
349
+ return `${left} = ${right}`;
350
+ }
351
+ throw new Error(`Unsupported join ON expression kind: ${on.kind}`);
352
+ }
353
+
354
+ function renderInclude(
355
+ include: NonNullable<SelectAst['includes']>[number],
356
+ contract?: PostgresContract,
357
+ ): string {
358
+ const alias = include.alias;
359
+
360
+ // Build the lateral subquery
361
+ const childProjection = include.child.project
362
+ .map((item: { alias: string; expr: ColumnRef | OperationExpr }) => {
363
+ const expr = renderExpr(item.expr, contract);
364
+ return `'${item.alias}', ${expr}`;
365
+ })
366
+ .join(', ');
367
+
368
+ const jsonBuildObject = `json_build_object(${childProjection})`;
369
+
370
+ // Build the ON condition from the include's ON clause - this goes in the WHERE clause
371
+ const onCondition = renderJoinOn(include.child.on);
372
+
373
+ // Build WHERE clause: combine ON condition with any additional WHERE clauses
374
+ let whereClause = ` WHERE ${onCondition}`;
375
+ if (include.child.where) {
376
+ whereClause += ` AND ${renderWhere(include.child.where, contract)}`;
377
+ }
378
+
379
+ // Add ORDER BY if present - it goes inside json_agg() call
380
+ const childOrderBy = include.child.orderBy?.length
381
+ ? ` ORDER BY ${include.child.orderBy
382
+ .map(
383
+ (order: { expr: ColumnRef | OperationExpr; dir: string }) =>
384
+ `${renderExpr(order.expr, contract)} ${order.dir.toUpperCase()}`,
385
+ )
386
+ .join(', ')}`
387
+ : '';
388
+
389
+ // Add LIMIT if present
390
+ const childLimit = typeof include.child.limit === 'number' ? ` LIMIT ${include.child.limit}` : '';
391
+
392
+ // Build the lateral subquery
393
+ // When ORDER BY is present without LIMIT, it goes inside json_agg() call: json_agg(expr ORDER BY ...)
394
+ // When LIMIT is present (with or without ORDER BY), we need to wrap in a subquery
395
+ const childTable = quoteIdentifier(include.child.table.name);
396
+ let subquery: string;
397
+ if (typeof include.child.limit === 'number') {
398
+ // With LIMIT, we need to wrap in a subquery
399
+ // Select individual columns in inner query, then aggregate
400
+ // Create a map of column references to their aliases for ORDER BY
401
+ // Only ColumnRef can be mapped (OperationExpr doesn't have table/column properties)
402
+ const columnAliasMap = new Map<string, string>();
403
+ for (const item of include.child.project) {
404
+ if (item.expr.kind === 'col') {
405
+ const columnKey = `${item.expr.table}.${item.expr.column}`;
406
+ columnAliasMap.set(columnKey, item.alias);
407
+ }
408
+ }
409
+
410
+ const innerColumns = include.child.project
411
+ .map((item: { alias: string; expr: ColumnRef | OperationExpr }) => {
412
+ const expr = renderExpr(item.expr, contract);
413
+ return `${expr} AS ${quoteIdentifier(item.alias)}`;
414
+ })
415
+ .join(', ');
416
+
417
+ // For ORDER BY, use column aliases if the column is in the SELECT list
418
+ const childOrderByWithAliases = include.child.orderBy?.length
419
+ ? ` ORDER BY ${include.child.orderBy
420
+ .map((order: { expr: ColumnRef | OperationExpr; dir: string }) => {
421
+ if (order.expr.kind === 'col') {
422
+ const columnKey = `${order.expr.table}.${order.expr.column}`;
423
+ const alias = columnAliasMap.get(columnKey);
424
+ if (alias) {
425
+ return `${quoteIdentifier(alias)} ${order.dir.toUpperCase()}`;
426
+ }
427
+ }
428
+ return `${renderExpr(order.expr, contract)} ${order.dir.toUpperCase()}`;
429
+ })
430
+ .join(', ')}`
431
+ : '';
432
+
433
+ const innerSelect = `SELECT ${innerColumns} FROM ${childTable}${whereClause}${childOrderByWithAliases}${childLimit}`;
434
+ subquery = `(SELECT json_agg(row_to_json(sub.*)) AS ${quoteIdentifier(alias)} FROM (${innerSelect}) sub)`;
435
+ } else if (childOrderBy) {
436
+ // With ORDER BY but no LIMIT, ORDER BY goes inside json_agg()
437
+ subquery = `(SELECT json_agg(${jsonBuildObject}${childOrderBy}) AS ${quoteIdentifier(alias)} FROM ${childTable}${whereClause})`;
438
+ } else {
439
+ // No ORDER BY or LIMIT
440
+ subquery = `(SELECT json_agg(${jsonBuildObject}) AS ${quoteIdentifier(alias)} FROM ${childTable}${whereClause})`;
441
+ }
442
+
443
+ // Return the LATERAL join with ON true (the condition is in the WHERE clause)
444
+ // The subquery returns a single column (the JSON array) with the alias
445
+ // We use a different alias for the table to avoid ambiguity when selecting the column
446
+ const tableAlias = `${alias}_lateral`;
447
+ return `LEFT JOIN LATERAL ${subquery} AS ${quoteIdentifier(tableAlias)} ON true`;
448
+ }
449
+
450
+ function quoteIdentifier(identifier: string): string {
451
+ return `"${identifier.replace(/"/g, '""')}"`;
452
+ }
453
+
454
+ function renderInsert(ast: InsertAst, contract: PostgresContract): string {
455
+ const table = quoteIdentifier(ast.table.name);
456
+ const columns = Object.keys(ast.values).map((col) => quoteIdentifier(col));
457
+ const tableMeta = contract.storage.tables[ast.table.name];
458
+ const values = Object.entries(ast.values).map(([colName, val]) => {
459
+ if (val.kind === 'param') {
460
+ const columnMeta = tableMeta?.columns[colName];
461
+ return renderTypedParam(val.index, columnMeta?.codecId);
462
+ }
463
+ if (val.kind === 'col') {
464
+ return `${quoteIdentifier(val.table)}.${quoteIdentifier(val.column)}`;
465
+ }
466
+ throw new Error(`Unsupported value kind in INSERT: ${(val as { kind: string }).kind}`);
467
+ });
468
+
469
+ const insertClause =
470
+ columns.length === 0
471
+ ? `INSERT INTO ${table} DEFAULT VALUES`
472
+ : `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${values.join(', ')})`;
473
+ const returningClause = ast.returning?.length
474
+ ? ` RETURNING ${ast.returning.map((col) => `${quoteIdentifier(col.table)}.${quoteIdentifier(col.column)}`).join(', ')}`
475
+ : '';
476
+
477
+ return `${insertClause}${returningClause}`;
478
+ }
479
+
480
+ function renderUpdate(ast: UpdateAst, contract: PostgresContract): string {
481
+ const table = quoteIdentifier(ast.table.name);
482
+ const tableMeta = contract.storage.tables[ast.table.name];
483
+ const setClauses = Object.entries(ast.set).map(([col, val]) => {
484
+ const column = quoteIdentifier(col);
485
+ let value: string;
486
+ if (val.kind === 'param') {
487
+ const columnMeta = tableMeta?.columns[col];
488
+ value = renderTypedParam(val.index, columnMeta?.codecId);
489
+ } else if (val.kind === 'col') {
490
+ value = `${quoteIdentifier(val.table)}.${quoteIdentifier(val.column)}`;
491
+ } else {
492
+ throw new Error(`Unsupported value kind in UPDATE: ${(val as { kind: string }).kind}`);
493
+ }
494
+ return `${column} = ${value}`;
495
+ });
496
+
497
+ const whereClause = ` WHERE ${renderWhere(ast.where, contract)}`;
498
+ const returningClause = ast.returning?.length
499
+ ? ` RETURNING ${ast.returning.map((col) => `${quoteIdentifier(col.table)}.${quoteIdentifier(col.column)}`).join(', ')}`
500
+ : '';
501
+
502
+ return `UPDATE ${table} SET ${setClauses.join(', ')}${whereClause}${returningClause}`;
503
+ }
504
+
505
+ function renderDelete(ast: DeleteAst, contract?: PostgresContract): string {
506
+ const table = quoteIdentifier(ast.table.name);
507
+ const whereClause = ` WHERE ${renderWhere(ast.where, contract)}`;
508
+ const returningClause = ast.returning?.length
509
+ ? ` RETURNING ${ast.returning.map((col) => `${quoteIdentifier(col.table)}.${quoteIdentifier(col.column)}`).join(', ')}`
510
+ : '';
511
+
512
+ return `DELETE FROM ${table}${whereClause}${returningClause}`;
513
+ }
514
+
515
+ export function createPostgresAdapter(options?: PostgresAdapterOptions) {
516
+ return Object.freeze(new PostgresAdapterImpl(options));
517
+ }
@@ -0,0 +1,28 @@
1
+ export {
2
+ SQL_CHAR_CODEC_ID,
3
+ SQL_FLOAT_CODEC_ID,
4
+ SQL_INT_CODEC_ID,
5
+ SQL_VARCHAR_CODEC_ID,
6
+ } from '@prisma-next/sql-relational-core/ast';
7
+ export const PG_TEXT_CODEC_ID = 'pg/text@1' as const;
8
+ export const PG_ENUM_CODEC_ID = 'pg/enum@1' as const;
9
+ export const PG_CHAR_CODEC_ID = 'pg/char@1' as const;
10
+ export const PG_VARCHAR_CODEC_ID = 'pg/varchar@1' as const;
11
+ export const PG_INT_CODEC_ID = 'pg/int@1' as const;
12
+ export const PG_INT2_CODEC_ID = 'pg/int2@1' as const;
13
+ export const PG_INT4_CODEC_ID = 'pg/int4@1' as const;
14
+ export const PG_INT8_CODEC_ID = 'pg/int8@1' as const;
15
+ export const PG_FLOAT_CODEC_ID = 'pg/float@1' as const;
16
+ export const PG_FLOAT4_CODEC_ID = 'pg/float4@1' as const;
17
+ export const PG_FLOAT8_CODEC_ID = 'pg/float8@1' as const;
18
+ export const PG_NUMERIC_CODEC_ID = 'pg/numeric@1' as const;
19
+ export const PG_BOOL_CODEC_ID = 'pg/bool@1' as const;
20
+ export const PG_BIT_CODEC_ID = 'pg/bit@1' as const;
21
+ export const PG_VARBIT_CODEC_ID = 'pg/varbit@1' as const;
22
+ export const PG_TIMESTAMP_CODEC_ID = 'pg/timestamp@1' as const;
23
+ export const PG_TIMESTAMPTZ_CODEC_ID = 'pg/timestamptz@1' as const;
24
+ export const PG_TIME_CODEC_ID = 'pg/time@1' as const;
25
+ export const PG_TIMETZ_CODEC_ID = 'pg/timetz@1' as const;
26
+ export const PG_INTERVAL_CODEC_ID = 'pg/interval@1' as const;
27
+ export const PG_JSON_CODEC_ID = 'pg/json@1' as const;
28
+ export const PG_JSONB_CODEC_ID = 'pg/jsonb@1' as const;