@prisma-next/sql-orm-client 0.6.0-dev.6 → 0.6.0-dev.8

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/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@prisma-next/sql-orm-client",
3
- "version": "0.6.0-dev.6",
3
+ "version": "0.6.0-dev.8",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "ORM client for Prisma Next — fluent, type-safe model collections",
8
8
  "dependencies": {
9
- "@prisma-next/operations": "0.6.0-dev.6",
10
- "@prisma-next/contract": "0.6.0-dev.6",
11
- "@prisma-next/framework-components": "0.6.0-dev.6",
12
- "@prisma-next/sql-contract": "0.6.0-dev.6",
13
- "@prisma-next/sql-operations": "0.6.0-dev.6",
14
- "@prisma-next/sql-relational-core": "0.6.0-dev.6",
15
- "@prisma-next/sql-runtime": "0.6.0-dev.6",
16
- "@prisma-next/utils": "0.6.0-dev.6"
9
+ "@prisma-next/contract": "0.6.0-dev.8",
10
+ "@prisma-next/framework-components": "0.6.0-dev.8",
11
+ "@prisma-next/operations": "0.6.0-dev.8",
12
+ "@prisma-next/sql-contract": "0.6.0-dev.8",
13
+ "@prisma-next/sql-relational-core": "0.6.0-dev.8",
14
+ "@prisma-next/utils": "0.6.0-dev.8",
15
+ "@prisma-next/sql-runtime": "0.6.0-dev.8",
16
+ "@prisma-next/sql-operations": "0.6.0-dev.8"
17
17
  },
18
18
  "devDependencies": {
19
19
  "@types/pg": "8.20.0",
@@ -21,17 +21,17 @@
21
21
  "tsdown": "0.22.0",
22
22
  "typescript": "5.9.3",
23
23
  "vitest": "4.1.5",
24
- "@prisma-next/driver-postgres": "0.6.0-dev.6",
25
- "@prisma-next/adapter-postgres": "0.6.0-dev.6",
26
- "@prisma-next/family-sql": "0.6.0-dev.6",
27
- "@prisma-next/cli": "0.6.0-dev.6",
28
- "@prisma-next/extension-pgvector": "0.6.0-dev.6",
29
- "@prisma-next/sql-contract-ts": "0.6.0-dev.6",
24
+ "@prisma-next/adapter-postgres": "0.6.0-dev.8",
25
+ "@prisma-next/driver-postgres": "0.6.0-dev.8",
26
+ "@prisma-next/extension-pgvector": "0.6.0-dev.8",
27
+ "@prisma-next/cli": "0.6.0-dev.8",
28
+ "@prisma-next/family-sql": "0.6.0-dev.8",
29
+ "@prisma-next/ids": "0.6.0-dev.8",
30
+ "@prisma-next/sql-contract-ts": "0.6.0-dev.8",
30
31
  "@prisma-next/test-utils": "0.0.1",
31
- "@prisma-next/tsconfig": "0.0.0",
32
- "@prisma-next/target-postgres": "0.6.0-dev.6",
33
32
  "@prisma-next/tsdown": "0.0.0",
34
- "@prisma-next/ids": "0.6.0-dev.6"
33
+ "@prisma-next/tsconfig": "0.0.0",
34
+ "@prisma-next/target-postgres": "0.6.0-dev.8"
35
35
  },
36
36
  "files": [
37
37
  "dist",
@@ -5,12 +5,14 @@ import {
5
5
  AndExpr,
6
6
  type AnyExpression,
7
7
  BinaryExpr,
8
+ type CodecRef,
8
9
  ColumnRef,
9
10
  ExistsExpr,
10
11
  ProjectionItem,
11
12
  SelectAst,
12
13
  TableSource,
13
14
  } from '@prisma-next/sql-relational-core/ast';
15
+ import { codecRefForStorageColumn } from '@prisma-next/sql-relational-core/codec-descriptor-registry';
14
16
  import type { Expression, ScopeField } from '@prisma-next/sql-relational-core/expression';
15
17
  import type { ExecutionContext } from '@prisma-next/sql-relational-core/query-lane-context';
16
18
  import {
@@ -104,11 +106,13 @@ export function createModelAccessor<
104
106
  }
105
107
  const traits = context.codecDescriptors.descriptorFor(column.codecId)?.traits ?? [];
106
108
  const operations = opsByCodecId.get(column.codecId) ?? [];
109
+ const codec = codecRefForStorageColumn(contract.storage, tableName, columnName);
107
110
  return createScalarFieldAccessor(
108
111
  tableName,
109
112
  columnName,
110
113
  column.codecId,
111
114
  column.nullable,
115
+ codec,
112
116
  traits,
113
117
  operations,
114
118
  context,
@@ -132,6 +136,7 @@ function createScalarFieldAccessor(
132
136
  columnName: string,
133
137
  codecId: string,
134
138
  nullable: boolean,
139
+ codec: CodecRef | undefined,
135
140
  traits: readonly string[],
136
141
  operations: readonly NamedOp[],
137
142
  context: ExecutionContext,
@@ -140,11 +145,12 @@ function createScalarFieldAccessor(
140
145
  const comparisonEntries: Array<[string, unknown]> = [];
141
146
  for (const [name, meta] of Object.entries(COMPARISON_METHODS_META)) {
142
147
  if (meta.traits.some((t) => !traits.includes(t))) continue;
143
- comparisonEntries.push([name, meta.create(column, codecId)]);
148
+ comparisonEntries.push([name, meta.create(column, codec)]);
144
149
  }
145
150
 
146
151
  const accessor = {
147
- returnType: { codecId, nullable },
152
+ returnType: { codecId, nullable, codec },
153
+ codec,
148
154
  buildAst: () => column,
149
155
  ...Object.fromEntries(comparisonEntries),
150
156
  } as Expression<ScopeField> & Record<string, unknown>;
@@ -178,10 +184,11 @@ function createExtensionMethodFactory(
178
184
  }
179
185
 
180
186
  const resultAst = result.buildAst();
187
+ const returnCodec: CodecRef = { codecId: returnCodecId };
181
188
  const methods: Record<string, unknown> = {};
182
189
  for (const [resultMethodName, meta] of Object.entries(COMPARISON_METHODS_META)) {
183
190
  if (meta.traits.some((t) => !returnTraits.includes(t))) continue;
184
- methods[resultMethodName] = meta.create(resultAst, returnCodecId);
191
+ methods[resultMethodName] = meta.create(resultAst, returnCodec);
185
192
  }
186
193
  return methods;
187
194
  };
@@ -5,6 +5,7 @@ import {
5
5
  AndExpr,
6
6
  type AnyExpression,
7
7
  BinaryExpr,
8
+ type CodecRef,
8
9
  ColumnRef,
9
10
  NotExpr,
10
11
  NullCheckExpr,
@@ -13,6 +14,7 @@ import {
13
14
  SelectAst,
14
15
  TableSource,
15
16
  } from '@prisma-next/sql-relational-core/ast';
17
+ import { codecRefForStorageColumn } from '@prisma-next/sql-relational-core/codec-descriptor-registry';
16
18
  import type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
17
19
  import { buildOrmQueryPlan, deriveParamsFromAst } from './query-plan-meta';
18
20
  import type { AggregateSelector } from './types';
@@ -22,11 +24,11 @@ function toAggregateProjection(
22
24
  contract: Contract<SqlStorage>,
23
25
  tableName: string,
24
26
  selector: AggregateSelector<unknown>,
25
- ): { expr: AggregateExpr; codecId: string | undefined } {
27
+ ): { expr: AggregateExpr; codec: CodecRef | undefined } {
26
28
  if (selector.fn === 'count') {
27
29
  // count() returns a target-specific bigint; mapping isn't derivable here
28
- // without target coupling, so we leave codecId unstamped.
29
- return { expr: AggregateExpr.count(), codecId: undefined };
30
+ // without target coupling, so we leave the codec slot empty.
31
+ return { expr: AggregateExpr.count(), codec: undefined };
30
32
  }
31
33
 
32
34
  if (!selector.column) {
@@ -38,10 +40,10 @@ function toAggregateProjection(
38
40
  // sum widens (int4 → int8 in Postgres) and avg → numeric; both need
39
41
  // target+input-aware mapping that doesn't exist yet, so leave unstamped.
40
42
  if (selector.fn === 'min' || selector.fn === 'max') {
41
- const codecId = contract.storage.tables[tableName]?.columns[selector.column]?.codecId;
42
- return { expr, codecId };
43
+ const codec = codecRefForStorageColumn(contract.storage, tableName, selector.column);
44
+ return { expr, codec };
43
45
  }
44
- return { expr, codecId: undefined };
46
+ return { expr, codec: undefined };
45
47
  }
46
48
 
47
49
  // ORM HAVING filters use literal binding (values inlined at plan-build time),
@@ -130,8 +132,8 @@ export function compileAggregate(
130
132
  }
131
133
 
132
134
  const projection: ProjectionItem[] = entries.map(([alias, selector]) => {
133
- const { expr, codecId } = toAggregateProjection(contract, tableName, selector);
134
- return ProjectionItem.of(alias, expr, codecId);
135
+ const { expr, codec } = toAggregateProjection(contract, tableName, selector);
136
+ return ProjectionItem.of(alias, expr, codec);
135
137
  });
136
138
  let ast = SelectAst.from(TableSource.named(tableName)).withProjection(projection);
137
139
  const where = combineWhereExprs(filters);
@@ -160,14 +162,17 @@ export function compileGroupedAggregate(
160
162
  throw new Error('groupBy().aggregate() requires at least one aggregation selector');
161
163
  }
162
164
 
163
- const table = contract.storage.tables[tableName];
164
165
  const projection: ProjectionItem[] = [
165
166
  ...groupByColumns.map((column) =>
166
- ProjectionItem.of(column, ColumnRef.of(tableName, column), table?.columns[column]?.codecId),
167
+ ProjectionItem.of(
168
+ column,
169
+ ColumnRef.of(tableName, column),
170
+ codecRefForStorageColumn(contract.storage, tableName, column),
171
+ ),
167
172
  ),
168
173
  ...entries.map(([alias, selector]) => {
169
- const { expr, codecId } = toAggregateProjection(contract, tableName, selector);
170
- return ProjectionItem.of(alias, expr, codecId);
174
+ const { expr, codec } = toAggregateProjection(contract, tableName, selector);
175
+ return ProjectionItem.of(alias, expr, codec);
171
176
  }),
172
177
  ];
173
178
 
@@ -12,7 +12,9 @@ import {
12
12
  TableSource,
13
13
  UpdateAst,
14
14
  } from '@prisma-next/sql-relational-core/ast';
15
+ import { codecRefForStorageColumn } from '@prisma-next/sql-relational-core/codec-descriptor-registry';
15
16
  import type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
17
+ import { ifDefined } from '@prisma-next/utils/defined';
16
18
  import { buildOrmQueryPlan, deriveParamsFromAst, resolveTableColumns } from './query-plan-meta';
17
19
  import { combineWhereExprs } from './where-utils';
18
20
 
@@ -26,9 +28,12 @@ function buildReturningColumns(
26
28
  ? [...returningColumns]
27
29
  : resolveTableColumns(contract, tableName);
28
30
 
29
- const table = contract.storage.tables[tableName];
30
31
  return columns.map((column) =>
31
- ProjectionItem.of(column, ColumnRef.of(tableName, column), table?.columns[column]?.codecId),
32
+ ProjectionItem.of(
33
+ column,
34
+ ColumnRef.of(tableName, column),
35
+ codecRefForStorageColumn(contract.storage, tableName, column),
36
+ ),
32
37
  );
33
38
  }
34
39
 
@@ -47,14 +52,13 @@ function toParamAssignments(
47
52
  }
48
53
 
49
54
  for (const [column, value] of Object.entries(values)) {
50
- const codecId = table.columns[column]?.codecId;
51
- if (!codecId) {
55
+ if (!table.columns[column]) {
52
56
  throw new Error(`Unknown column "${column}" in table "${tableName}"`);
53
57
  }
58
+ const codec = codecRefForStorageColumn(contract.storage, tableName, column);
54
59
  assignments[column] = ParamRef.of(value, {
55
60
  name: column,
56
- codecId,
57
- refs: { table: tableName, column },
61
+ ...ifDefined('codec', codec),
58
62
  });
59
63
  }
60
64
 
@@ -85,6 +89,11 @@ function normalizeInsertRows(
85
89
  }
86
90
  }
87
91
 
92
+ const table = contract.storage.tables[tableName];
93
+ if (!table) {
94
+ throw new Error(`Unknown table "${tableName}"`);
95
+ }
96
+
88
97
  const normalizedRows = rows.map((row) => {
89
98
  if (orderedColumns.length === 0) {
90
99
  return {};
@@ -93,18 +102,13 @@ function normalizeInsertRows(
93
102
  const normalizedRow: Record<string, ParamRef | DefaultValueExpr> = {};
94
103
  for (const column of orderedColumns) {
95
104
  if (Object.hasOwn(row, column)) {
96
- const table = contract.storage.tables[tableName];
97
- if (!table) {
98
- throw new Error(`Unknown table "${tableName}"`);
99
- }
100
- const codecId = table?.columns[column]?.codecId;
101
- if (!codecId) {
105
+ if (!table.columns[column]) {
102
106
  throw new Error(`Unknown column "${column}" in table "${tableName}"`);
103
107
  }
108
+ const codec = codecRefForStorageColumn(contract.storage, tableName, column);
104
109
  normalizedRow[column] = ParamRef.of(row[column], {
105
110
  name: column,
106
- codecId,
107
- refs: { table: tableName, column },
111
+ ...ifDefined('codec', codec),
108
112
  });
109
113
  continue;
110
114
  }
@@ -21,6 +21,7 @@ import {
21
21
  SubqueryExpr,
22
22
  TableSource,
23
23
  } from '@prisma-next/sql-relational-core/ast';
24
+ import { codecRefForStorageColumn } from '@prisma-next/sql-relational-core/codec-descriptor-registry';
24
25
  import type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
25
26
  import {
26
27
  type PolymorphismInfo,
@@ -49,9 +50,12 @@ function buildProjection(
49
50
  ? [...selectedFields]
50
51
  : resolveTableColumns(contract, tableName);
51
52
 
52
- const table = contract.storage.tables[tableName];
53
53
  return columns.map((column) =>
54
- ProjectionItem.of(column, ColumnRef.of(tableRef, column), table?.columns[column]?.codecId),
54
+ ProjectionItem.of(
55
+ column,
56
+ ColumnRef.of(tableRef, column),
57
+ codecRefForStorageColumn(contract.storage, tableName, column),
58
+ ),
55
59
  );
56
60
  }
57
61
 
@@ -406,7 +410,6 @@ function buildMtiJoins(
406
410
  joins.push(join);
407
411
 
408
412
  const variantColumns = resolveTableColumns(contract, variant.table);
409
- const variantTable = contract.storage.tables[variant.table];
410
413
  for (const col of variantColumns) {
411
414
  if (col === pkColumn) continue;
412
415
  const alias = `${variant.table}__${col}`;
@@ -414,7 +417,7 @@ function buildMtiJoins(
414
417
  ProjectionItem.of(
415
418
  alias,
416
419
  ColumnRef.of(variant.table, col),
417
- variantTable?.columns[col]?.codecId,
420
+ codecRefForStorageColumn(contract.storage, variant.table, col),
418
421
  ),
419
422
  );
420
423
  }
package/src/types.ts CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  type AnyExpression,
11
11
  BinaryExpr,
12
12
  type BinaryOp,
13
+ type CodecRef,
13
14
  type CodecTrait,
14
15
  ListExpression,
15
16
  NullCheckExpr,
@@ -19,7 +20,6 @@ import {
19
20
  import type { Expression } from '@prisma-next/sql-relational-core/expression';
20
21
  import type { ExecutionContext } from '@prisma-next/sql-relational-core/query-lane-context';
21
22
  import type { ComputeColumnJsType, RuntimeScope } from '@prisma-next/sql-relational-core/types';
22
- import { ifDefined } from '@prisma-next/utils/defined';
23
23
  import type { RowSelection } from './collection-internal-types';
24
24
 
25
25
  export type AggregateFn = 'count' | 'sum' | 'avg' | 'min' | 'max';
@@ -255,46 +255,19 @@ type FieldOperations<
255
255
  : unknown
256
256
  : unknown;
257
257
 
258
- /**
259
- * Resolve the unique column ref carried by `left` so its surrounding `ParamRef` can dispatch through `forColumn`. The previous implementation only matched bare `column-ref` expressions, which lost refs the moment the expression got wrapped (`upper(f.id)`, `BinaryExpr`, function-call expressions, etc.) — and column-aware dispatch (AC-5) silently degraded for any predicate that touched a computed column.
260
- *
261
- * Walking via `collectColumnRefs()` and accepting only a single unambiguous ref gives `eq(upper(f.id), value)` and friends correct dispatch without inventing refs for ambiguous shapes (e.g. `eq(concat(f.firstName, f.lastName), value)` returns `undefined` and falls through to the codec-id path, which is correct for non-parameterized comparisons).
262
- */
263
- function refsFromLeft(left: AnyExpression): { table: string; column: string } | undefined {
264
- if (left.kind === 'column-ref') {
265
- return { table: left.table, column: left.column };
266
- }
267
- const columnRefs = left.collectColumnRefs();
268
- if (columnRefs.length !== 1) return undefined;
269
- const single = columnRefs[0];
270
- if (!single) return undefined;
271
- return { table: single.table, column: single.column };
258
+ function param(codec: CodecRef | undefined, value: unknown): ParamRef {
259
+ if (codec === undefined) return ParamRef.of(value);
260
+ return ParamRef.of(value, { codec });
272
261
  }
273
262
 
274
- function param(
275
- codecId: string | undefined,
276
- value: unknown,
277
- refs: { table: string; column: string } | undefined,
278
- ): ParamRef {
279
- if (codecId === undefined && refs === undefined) return ParamRef.of(value);
280
- return ParamRef.of(value, {
281
- ...ifDefined('codecId', codecId),
282
- ...ifDefined('refs', refs),
283
- });
284
- }
285
-
286
- function paramList(
287
- codecId: string | undefined,
288
- values: readonly unknown[],
289
- refs: { table: string; column: string } | undefined,
290
- ): ListExpression {
291
- return ListExpression.of(values.map((value) => param(codecId, value, refs)));
263
+ function paramList(codec: CodecRef | undefined, values: readonly unknown[]): ListExpression {
264
+ return ListExpression.of(values.map((value) => param(codec, value)));
292
265
  }
293
266
 
294
267
  // never[] is intentional: factories have heterogeneous signatures (value: unknown, values: readonly unknown[], pattern: string, etc.) but are only called through the typed ComparisonMethodFns interface, never through this type directly.
295
268
  type MethodFactory = (
296
269
  left: AnyExpression,
297
- codecId: string | undefined,
270
+ codec: CodecRef | undefined,
298
271
  ) => (...args: never[]) => unknown;
299
272
 
300
273
  type ComparisonMethodMeta = {
@@ -303,17 +276,17 @@ type ComparisonMethodMeta = {
303
276
  };
304
277
 
305
278
  function scalarComparisonMethod(op: BinaryOp) {
306
- return ((left, codecId) => (value: unknown) =>
307
- new BinaryExpr(op, left, param(codecId, value, refsFromLeft(left)))) satisfies MethodFactory;
279
+ return ((left, codec) => (value: unknown) => {
280
+ if (value === null && (op === 'eq' || op === 'neq')) {
281
+ return op === 'eq' ? NullCheckExpr.isNull(left) : NullCheckExpr.isNotNull(left);
282
+ }
283
+ return new BinaryExpr(op, left, param(codec, value));
284
+ }) satisfies MethodFactory;
308
285
  }
309
286
 
310
287
  function listComparisonMethod(op: BinaryOp) {
311
- return ((left, codecId) => (values: readonly unknown[]) =>
312
- new BinaryExpr(
313
- op,
314
- left,
315
- paramList(codecId, values, refsFromLeft(left)),
316
- )) satisfies MethodFactory;
288
+ return ((left, codec) => (values: readonly unknown[]) =>
289
+ new BinaryExpr(op, left, paramList(codec, values))) satisfies MethodFactory;
317
290
  }
318
291
 
319
292
  /**
@@ -20,6 +20,7 @@ import {
20
20
  ProjectionItem,
21
21
  SelectAst,
22
22
  } from '@prisma-next/sql-relational-core/ast';
23
+ import { codecRefForStorageColumn } from '@prisma-next/sql-relational-core/codec-descriptor-registry';
23
24
 
24
25
  export function bindWhereExpr(contract: Contract<SqlStorage>, expr: AnyExpression): AnyExpression {
25
26
  return bindWhereExprNode(contract, expr);
@@ -118,14 +119,11 @@ function createParamRef(
118
119
  columnRef: ColumnRef,
119
120
  value: unknown,
120
121
  ): ParamRef {
121
- const codecId = contract.storage.tables[columnRef.table]?.columns[columnRef.column]?.codecId;
122
- if (!codecId) {
122
+ if (!contract.storage.tables[columnRef.table]?.columns[columnRef.column]) {
123
123
  throw new Error(`Unknown column "${columnRef.column}" in table "${columnRef.table}"`);
124
124
  }
125
- return ParamRef.of(value, {
126
- codecId,
127
- refs: { table: columnRef.table, column: columnRef.column },
128
- });
125
+ const codec = codecRefForStorageColumn(contract.storage, columnRef.table, columnRef.column);
126
+ return ParamRef.of(value, codec ? { codec } : undefined);
129
127
  }
130
128
 
131
129
  function createExpressionBinder(contract: Contract<SqlStorage>): ExpressionRewriter {
@@ -176,8 +174,7 @@ function bindSelectAst(contract: Contract<SqlStorage>, ast: SelectAst): SelectAs
176
174
  new ProjectionItem(
177
175
  projection.alias,
178
176
  bindProjectionExpr(contract, projection.expr),
179
- projection.codecId,
180
- projection.refs,
177
+ projection.codec,
181
178
  ),
182
179
  ),
183
180
  where: ast.where ? bindWhereExprNode(contract, ast.where) : undefined,