@prisma-next/adapter-postgres 0.3.0-dev.7 → 0.3.0-dev.71

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 (91) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +64 -2
  3. package/dist/adapter-DtehReRR.mjs +271 -0
  4. package/dist/adapter-DtehReRR.mjs.map +1 -0
  5. package/dist/adapter.d.mts +23 -0
  6. package/dist/adapter.d.mts.map +1 -0
  7. package/dist/adapter.mjs +3 -0
  8. package/dist/codec-ids-Bsm9c7ns.mjs +29 -0
  9. package/dist/codec-ids-Bsm9c7ns.mjs.map +1 -0
  10. package/dist/codec-types.d.mts +141 -0
  11. package/dist/codec-types.d.mts.map +1 -0
  12. package/dist/codec-types.mjs +3 -0
  13. package/dist/codecs-BfC_5c-4.mjs +207 -0
  14. package/dist/codecs-BfC_5c-4.mjs.map +1 -0
  15. package/dist/column-types.d.mts +110 -0
  16. package/dist/column-types.d.mts.map +1 -0
  17. package/dist/column-types.mjs +180 -0
  18. package/dist/column-types.mjs.map +1 -0
  19. package/dist/control.d.mts +111 -0
  20. package/dist/control.d.mts.map +1 -0
  21. package/dist/control.mjs +462 -0
  22. package/dist/control.mjs.map +1 -0
  23. package/dist/descriptor-meta-ilnFI7bx.mjs +921 -0
  24. package/dist/descriptor-meta-ilnFI7bx.mjs.map +1 -0
  25. package/dist/runtime.d.mts +19 -0
  26. package/dist/runtime.d.mts.map +1 -0
  27. package/dist/runtime.mjs +85 -0
  28. package/dist/runtime.mjs.map +1 -0
  29. package/dist/sql-utils-CSfAGEwF.mjs +78 -0
  30. package/dist/sql-utils-CSfAGEwF.mjs.map +1 -0
  31. package/dist/types-CXO7EB60.d.mts +19 -0
  32. package/dist/types-CXO7EB60.d.mts.map +1 -0
  33. package/dist/types.d.mts +2 -0
  34. package/dist/types.mjs +1 -0
  35. package/package.json +37 -46
  36. package/src/core/adapter.ts +139 -28
  37. package/src/core/codec-ids.ts +28 -0
  38. package/src/core/codecs.ts +325 -23
  39. package/src/core/control-adapter.ts +400 -178
  40. package/src/core/default-normalizer.ts +90 -0
  41. package/src/core/descriptor-meta.ts +221 -9
  42. package/src/core/enum-control-hooks.ts +735 -0
  43. package/src/core/json-schema-type-expression.ts +131 -0
  44. package/src/core/json-schema-validator.ts +53 -0
  45. package/src/core/parameterized-types.ts +118 -0
  46. package/src/core/sql-utils.ts +111 -0
  47. package/src/core/standard-schema.ts +71 -0
  48. package/src/exports/codec-types.ts +73 -1
  49. package/src/exports/column-types.ts +233 -9
  50. package/src/exports/control.ts +16 -9
  51. package/src/exports/runtime.ts +61 -18
  52. package/dist/chunk-HD5YISNQ.js +0 -47
  53. package/dist/chunk-HD5YISNQ.js.map +0 -1
  54. package/dist/chunk-J3XSOAM2.js +0 -162
  55. package/dist/chunk-J3XSOAM2.js.map +0 -1
  56. package/dist/chunk-T6S3A6VT.js +0 -301
  57. package/dist/chunk-T6S3A6VT.js.map +0 -1
  58. package/dist/core/adapter.d.ts +0 -19
  59. package/dist/core/adapter.d.ts.map +0 -1
  60. package/dist/core/codecs.d.ts +0 -110
  61. package/dist/core/codecs.d.ts.map +0 -1
  62. package/dist/core/control-adapter.d.ts +0 -33
  63. package/dist/core/control-adapter.d.ts.map +0 -1
  64. package/dist/core/descriptor-meta.d.ts +0 -72
  65. package/dist/core/descriptor-meta.d.ts.map +0 -1
  66. package/dist/core/types.d.ts +0 -16
  67. package/dist/core/types.d.ts.map +0 -1
  68. package/dist/exports/adapter.d.ts +0 -2
  69. package/dist/exports/adapter.d.ts.map +0 -1
  70. package/dist/exports/adapter.js +0 -8
  71. package/dist/exports/adapter.js.map +0 -1
  72. package/dist/exports/codec-types.d.ts +0 -11
  73. package/dist/exports/codec-types.d.ts.map +0 -1
  74. package/dist/exports/codec-types.js +0 -7
  75. package/dist/exports/codec-types.js.map +0 -1
  76. package/dist/exports/column-types.d.ts +0 -17
  77. package/dist/exports/column-types.d.ts.map +0 -1
  78. package/dist/exports/column-types.js +0 -49
  79. package/dist/exports/column-types.js.map +0 -1
  80. package/dist/exports/control.d.ts +0 -8
  81. package/dist/exports/control.d.ts.map +0 -1
  82. package/dist/exports/control.js +0 -279
  83. package/dist/exports/control.js.map +0 -1
  84. package/dist/exports/runtime.d.ts +0 -15
  85. package/dist/exports/runtime.d.ts.map +0 -1
  86. package/dist/exports/runtime.js +0 -20
  87. package/dist/exports/runtime.js.map +0 -1
  88. package/dist/exports/types.d.ts +0 -2
  89. package/dist/exports/types.d.ts.map +0 -1
  90. package/dist/exports/types.js +0 -1
  91. package/dist/exports/types.js.map +0 -1
@@ -2,26 +2,51 @@ import type {
2
2
  Adapter,
3
3
  AdapterProfile,
4
4
  BinaryExpr,
5
+ CodecParamsDescriptor,
5
6
  ColumnRef,
6
7
  DeleteAst,
7
- ExistsExpr,
8
8
  IncludeRef,
9
9
  InsertAst,
10
10
  JoinAst,
11
+ JoinOnExpr,
12
+ ListLiteralExpr,
11
13
  LiteralExpr,
12
14
  LowererContext,
15
+ NullCheckExpr,
13
16
  OperationExpr,
14
17
  ParamRef,
15
18
  QueryAst,
16
19
  SelectAst,
17
20
  UpdateAst,
21
+ WhereExpr,
18
22
  } from '@prisma-next/sql-relational-core/ast';
19
23
  import { createCodecRegistry, isOperationExpr } from '@prisma-next/sql-relational-core/ast';
24
+ import { ifDefined } from '@prisma-next/utils/defined';
25
+ import { PG_JSON_CODEC_ID, PG_JSONB_CODEC_ID } from './codec-ids';
20
26
  import { codecDefinitions } from './codecs';
27
+ import { escapeLiteral } from './sql-utils';
21
28
  import type { PostgresAdapterOptions, PostgresContract, PostgresLoweredStatement } from './types';
22
29
 
23
30
  const VECTOR_CODEC_ID = 'pg/vector@1' as const;
24
31
 
32
+ function getCodecParamCast(codecId: string | undefined): string | undefined {
33
+ if (codecId === VECTOR_CODEC_ID) {
34
+ return 'vector';
35
+ }
36
+ if (codecId === PG_JSON_CODEC_ID) {
37
+ return 'json';
38
+ }
39
+ if (codecId === PG_JSONB_CODEC_ID) {
40
+ return 'jsonb';
41
+ }
42
+ return undefined;
43
+ }
44
+
45
+ function renderTypedParam(index: number, codecId: string | undefined): string {
46
+ const cast = getCodecParamCast(codecId);
47
+ return cast ? `$${index}::${cast}` : `$${index}`;
48
+ }
49
+
25
50
  const defaultCapabilities = Object.freeze({
26
51
  postgres: {
27
52
  orderBy: true,
@@ -30,8 +55,27 @@ const defaultCapabilities = Object.freeze({
30
55
  jsonAgg: true,
31
56
  returning: true,
32
57
  },
58
+ sql: {
59
+ enums: true,
60
+ },
33
61
  });
34
62
 
63
+ type AdapterCodec = (typeof codecDefinitions)[keyof typeof codecDefinitions]['codec'];
64
+ type ParameterizedCodec = AdapterCodec & {
65
+ readonly paramsSchema: NonNullable<AdapterCodec['paramsSchema']>;
66
+ };
67
+
68
+ const parameterizedCodecs: ReadonlyArray<CodecParamsDescriptor> = Object.values(codecDefinitions)
69
+ .map((definition) => definition.codec)
70
+ .filter((codec): codec is ParameterizedCodec => codec.paramsSchema !== undefined)
71
+ .map((codec) =>
72
+ Object.freeze({
73
+ codecId: codec.id,
74
+ paramsSchema: codec.paramsSchema,
75
+ ...ifDefined('init', codec.init),
76
+ }),
77
+ );
78
+
35
79
  class PostgresAdapterImpl implements Adapter<QueryAst, PostgresContract, PostgresLoweredStatement> {
36
80
  // These fields make the adapter instance structurally compatible with
37
81
  // RuntimeAdapterInstance<'sql', 'postgres'> without introducing a runtime-plane dependency.
@@ -56,6 +100,10 @@ class PostgresAdapterImpl implements Adapter<QueryAst, PostgresContract, Postgre
56
100
  });
57
101
  }
58
102
 
103
+ parameterizedCodecs(): ReadonlyArray<CodecParamsDescriptor> {
104
+ return parameterizedCodecs;
105
+ }
106
+
59
107
  lower(ast: QueryAst, context: LowererContext<PostgresContract>) {
60
108
  let sql: string;
61
109
  const params = context.params ? [...context.params] : [];
@@ -134,29 +182,73 @@ function renderProjection(ast: SelectAst, contract?: PostgresContract): string {
134
182
  .join(', ');
135
183
  }
136
184
 
137
- function renderWhere(expr: BinaryExpr | ExistsExpr, contract?: PostgresContract): string {
185
+ function renderWhere(expr: WhereExpr, contract?: PostgresContract): string {
138
186
  if (expr.kind === 'exists') {
139
187
  const notKeyword = expr.not ? 'NOT ' : '';
140
188
  const subquery = renderSelect(expr.subquery, contract);
141
189
  return `${notKeyword}EXISTS (${subquery})`;
142
190
  }
191
+ if (expr.kind === 'nullCheck') {
192
+ return renderNullCheck(expr, contract);
193
+ }
194
+ if (expr.kind === 'and') {
195
+ if (expr.exprs.length === 0) {
196
+ return 'TRUE';
197
+ }
198
+ return `(${expr.exprs.map((part) => renderWhere(part, contract)).join(' AND ')})`;
199
+ }
200
+ if (expr.kind === 'or') {
201
+ if (expr.exprs.length === 0) {
202
+ return 'FALSE';
203
+ }
204
+ return `(${expr.exprs.map((part) => renderWhere(part, contract)).join(' OR ')})`;
205
+ }
143
206
  return renderBinary(expr, contract);
144
207
  }
145
208
 
209
+ function renderNullCheck(expr: NullCheckExpr, contract?: PostgresContract): string {
210
+ const rendered = renderExpr(expr.expr as ColumnRef | OperationExpr, contract);
211
+ // Only wrap in parentheses if it's an operation expression
212
+ const renderedExpr = isOperationExpr(expr.expr) ? `(${rendered})` : rendered;
213
+ return expr.isNull ? `${renderedExpr} IS NULL` : `${renderedExpr} IS NOT NULL`;
214
+ }
215
+
146
216
  function renderBinary(expr: BinaryExpr, contract?: PostgresContract): string {
217
+ if (expr.right.kind === 'listLiteral' && expr.right.values.length === 0) {
218
+ if (expr.op === 'in') {
219
+ return 'FALSE';
220
+ }
221
+ if (expr.op === 'notIn') {
222
+ return 'TRUE';
223
+ }
224
+ }
225
+
147
226
  const leftExpr = expr.left as ColumnRef | OperationExpr;
148
227
  const left = renderExpr(leftExpr, contract);
149
- // Handle both ParamRef and ColumnRef on the right side
150
- // (ColumnRef can appear in EXISTS subqueries for correlation)
151
- const rightExpr = expr.right as ParamRef | ColumnRef;
152
- const right =
153
- rightExpr.kind === 'col'
154
- ? renderColumn(rightExpr)
155
- : renderParam(rightExpr as ParamRef, contract);
156
- // Only wrap in parentheses if it's an operation expression
157
228
  const leftRendered = isOperationExpr(leftExpr) ? `(${left})` : left;
229
+ const leftCol = leftExpr.kind === 'col' ? leftExpr : undefined;
230
+
231
+ const rightExpr = expr.right;
232
+ let right: string;
233
+ if (rightExpr.kind === 'listLiteral') {
234
+ right = renderListLiteral(
235
+ rightExpr as ListLiteralExpr,
236
+ contract,
237
+ leftCol?.table,
238
+ leftCol?.column,
239
+ );
240
+ } else if (rightExpr.kind === 'literal') {
241
+ right = renderLiteral(rightExpr);
242
+ } else if (rightExpr.kind === 'col') {
243
+ right = renderColumn(rightExpr);
244
+ } else if (rightExpr.kind === 'param') {
245
+ right = renderParam(rightExpr, contract, leftCol?.table, leftCol?.column);
246
+ } else if (rightExpr.kind === 'operation') {
247
+ right = renderOperation(rightExpr, contract);
248
+ } else {
249
+ right = renderColumn(rightExpr as ColumnRef);
250
+ }
158
251
 
159
- // Map operators to SQL symbols
160
252
  const operatorMap: Record<BinaryExpr['op'], string> = {
161
253
  eq: '=',
162
254
  neq: '!=',
@@ -164,11 +256,32 @@ function renderBinary(expr: BinaryExpr, contract?: PostgresContract): string {
164
256
  lt: '<',
165
257
  gte: '>=',
166
258
  lte: '<=',
259
+ like: 'LIKE',
260
+ ilike: 'ILIKE',
261
+ in: 'IN',
262
+ notIn: 'NOT IN',
167
263
  };
168
264
 
169
265
  return `${leftRendered} ${operatorMap[expr.op]} ${right}`;
170
266
  }
171
267
 
268
+ function renderListLiteral(
269
+ expr: ListLiteralExpr,
270
+ contract?: PostgresContract,
271
+ tableName?: string,
272
+ columnName?: string,
273
+ ): string {
274
+ if (expr.values.length === 0) {
275
+ return '(NULL)';
276
+ }
277
+ const values = expr.values
278
+ .map((v) =>
279
+ v.kind === 'param' ? renderParam(v, contract, tableName, columnName) : renderLiteral(v),
280
+ )
281
+ .join(', ');
282
+ return `(${values})`;
283
+ }
284
+
172
285
  function renderColumn(ref: ColumnRef): string {
173
286
  return `${quoteIdentifier(ref.table)}.${quoteIdentifier(ref.column)}`;
174
287
  }
@@ -186,20 +299,17 @@ function renderParam(
186
299
  tableName?: string,
187
300
  columnName?: string,
188
301
  ): string {
189
- // Cast vector parameters to vector type for PostgreSQL
190
302
  if (contract && tableName && columnName) {
191
303
  const tableMeta = contract.storage.tables[tableName];
192
304
  const columnMeta = tableMeta?.columns[columnName];
193
- if (columnMeta?.codecId === VECTOR_CODEC_ID) {
194
- return `$${ref.index}::vector`;
195
- }
305
+ return renderTypedParam(ref.index, columnMeta?.codecId);
196
306
  }
197
307
  return `$${ref.index}`;
198
308
  }
199
309
 
200
310
  function renderLiteral(expr: LiteralExpr): string {
201
311
  if (typeof expr.value === 'string') {
202
- return `'${expr.value.replace(/'/g, "''")}'`;
312
+ return `'${escapeLiteral(expr.value)}'`;
203
313
  }
204
314
  if (typeof expr.value === 'number' || typeof expr.value === 'boolean') {
205
315
  return String(expr.value);
@@ -248,20 +358,20 @@ function renderOperation(expr: OperationExpr, contract?: PostgresContract): stri
248
358
  return result;
249
359
  }
250
360
 
251
- function renderJoin(join: JoinAst, _contract?: PostgresContract): string {
361
+ function renderJoin(join: JoinAst, contract?: PostgresContract): string {
252
362
  const joinType = join.joinType.toUpperCase();
253
363
  const table = quoteIdentifier(join.table.name);
254
- const onClause = renderJoinOn(join.on);
364
+ const onClause = renderJoinOn(join.on, contract);
255
365
  return `${joinType} JOIN ${table} ON ${onClause}`;
256
366
  }
257
367
 
258
- function renderJoinOn(on: JoinAst['on']): string {
368
+ function renderJoinOn(on: JoinOnExpr, contract?: PostgresContract): string {
259
369
  if (on.kind === 'eqCol') {
260
370
  const left = renderColumn(on.left);
261
371
  const right = renderColumn(on.right);
262
372
  return `${left} = ${right}`;
263
373
  }
264
- throw new Error(`Unsupported join ON expression kind: ${on.kind}`);
374
+ return renderWhere(on, contract);
265
375
  }
266
376
 
267
377
  function renderInclude(
@@ -281,7 +391,7 @@ function renderInclude(
281
391
  const jsonBuildObject = `json_build_object(${childProjection})`;
282
392
 
283
393
  // Build the ON condition from the include's ON clause - this goes in the WHERE clause
284
- const onCondition = renderJoinOn(include.child.on);
394
+ const onCondition = renderJoinOn(include.child.on, contract);
285
395
 
286
396
  // Build WHERE clause: combine ON condition with any additional WHERE clauses
287
397
  let whereClause = ` WHERE ${onCondition}`;
@@ -371,8 +481,7 @@ function renderInsert(ast: InsertAst, contract: PostgresContract): string {
371
481
  const values = Object.entries(ast.values).map(([colName, val]) => {
372
482
  if (val.kind === 'param') {
373
483
  const columnMeta = tableMeta?.columns[colName];
374
- const isVector = columnMeta?.codecId === VECTOR_CODEC_ID;
375
- return isVector ? `$${val.index}::vector` : `$${val.index}`;
484
+ return renderTypedParam(val.index, columnMeta?.codecId);
376
485
  }
377
486
  if (val.kind === 'col') {
378
487
  return `${quoteIdentifier(val.table)}.${quoteIdentifier(val.column)}`;
@@ -380,7 +489,10 @@ function renderInsert(ast: InsertAst, contract: PostgresContract): string {
380
489
  throw new Error(`Unsupported value kind in INSERT: ${(val as { kind: string }).kind}`);
381
490
  });
382
491
 
383
- const insertClause = `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${values.join(', ')})`;
492
+ const insertClause =
493
+ columns.length === 0
494
+ ? `INSERT INTO ${table} DEFAULT VALUES`
495
+ : `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${values.join(', ')})`;
384
496
  const returningClause = ast.returning?.length
385
497
  ? ` RETURNING ${ast.returning.map((col) => `${quoteIdentifier(col.table)}.${quoteIdentifier(col.column)}`).join(', ')}`
386
498
  : '';
@@ -396,8 +508,7 @@ function renderUpdate(ast: UpdateAst, contract: PostgresContract): string {
396
508
  let value: string;
397
509
  if (val.kind === 'param') {
398
510
  const columnMeta = tableMeta?.columns[col];
399
- const isVector = columnMeta?.codecId === VECTOR_CODEC_ID;
400
- value = isVector ? `$${val.index}::vector` : `$${val.index}`;
511
+ value = renderTypedParam(val.index, columnMeta?.codecId);
401
512
  } else if (val.kind === 'col') {
402
513
  value = `${quoteIdentifier(val.table)}.${quoteIdentifier(val.column)}`;
403
514
  } else {
@@ -406,7 +517,7 @@ function renderUpdate(ast: UpdateAst, contract: PostgresContract): string {
406
517
  return `${column} = ${value}`;
407
518
  });
408
519
 
409
- const whereClause = ` WHERE ${renderBinary(ast.where, contract)}`;
520
+ const whereClause = ast.where ? ` WHERE ${renderWhere(ast.where, contract)}` : '';
410
521
  const returningClause = ast.returning?.length
411
522
  ? ` RETURNING ${ast.returning.map((col) => `${quoteIdentifier(col.table)}.${quoteIdentifier(col.column)}`).join(', ')}`
412
523
  : '';
@@ -416,7 +527,7 @@ function renderUpdate(ast: UpdateAst, contract: PostgresContract): string {
416
527
 
417
528
  function renderDelete(ast: DeleteAst, contract?: PostgresContract): string {
418
529
  const table = quoteIdentifier(ast.table.name);
419
- const whereClause = ` WHERE ${renderBinary(ast.where, contract)}`;
530
+ const whereClause = ast.where ? ` WHERE ${renderWhere(ast.where, contract)}` : '';
420
531
  const returningClause = ast.returning?.length
421
532
  ? ` RETURNING ${ast.returning.map((col) => `${quoteIdentifier(col.table)}.${quoteIdentifier(col.column)}`).join(', ')}`
422
533
  : '';
@@ -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;