@prisma-next/sql-relational-core 0.3.0-dev.6 → 0.3.0-dev.63
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/LICENSE +201 -0
- package/README.md +18 -2
- package/dist/errors-7_V3El9I.mjs +3 -0
- package/dist/errors-DVufq9PT.d.mts +2 -0
- package/dist/exports/ast.d.mts +192 -0
- package/dist/exports/ast.d.mts.map +1 -0
- package/dist/exports/ast.mjs +361 -0
- package/dist/exports/ast.mjs.map +1 -0
- package/dist/exports/errors.d.mts +2 -0
- package/dist/exports/errors.mjs +3 -0
- package/dist/exports/guards.d.mts +5 -0
- package/dist/exports/guards.mjs +3 -0
- package/dist/exports/operations-registry.d.mts +5 -0
- package/dist/exports/operations-registry.mjs +3 -0
- package/dist/exports/param.d.mts +5 -0
- package/dist/exports/param.mjs +3 -0
- package/dist/exports/plan.d.mts +2 -0
- package/dist/exports/plan.mjs +17 -0
- package/dist/exports/plan.mjs.map +1 -0
- package/dist/exports/query-lane-context.d.mts +2 -0
- package/dist/exports/query-lane-context.mjs +1 -0
- package/dist/exports/schema.d.mts +5 -0
- package/dist/exports/schema.mjs +4 -0
- package/dist/exports/types.d.mts +4 -0
- package/dist/exports/types.mjs +3 -0
- package/dist/exports/utils/guards.d.mts +5 -0
- package/dist/exports/utils/guards.mjs +4 -0
- package/dist/guards-0ycyntYX.mjs +132 -0
- package/dist/guards-0ycyntYX.mjs.map +1 -0
- package/dist/guards-DCCpAlOL.d.mts +87 -0
- package/dist/guards-DCCpAlOL.d.mts.map +1 -0
- package/dist/index.d.mts +12 -0
- package/dist/index.mjs +12 -0
- package/dist/operations-registry-DPZ5aElH.mjs +152 -0
- package/dist/operations-registry-DPZ5aElH.mjs.map +1 -0
- package/dist/operations-registry-wVEaiLyt.d.mts +9 -0
- package/dist/operations-registry-wVEaiLyt.d.mts.map +1 -0
- package/dist/param-C4n0OW59.d.mts +8 -0
- package/dist/param-C4n0OW59.d.mts.map +1 -0
- package/dist/param-DMU3OWfI.mjs +14 -0
- package/dist/param-DMU3OWfI.mjs.map +1 -0
- package/dist/plan-BhSWuTfw.d.mts +194 -0
- package/dist/plan-BhSWuTfw.d.mts.map +1 -0
- package/dist/query-lane-context-CgkPuKaR.d.mts +259 -0
- package/dist/query-lane-context-CgkPuKaR.d.mts.map +1 -0
- package/dist/schema-Bi5p4aAc.mjs +175 -0
- package/dist/schema-Bi5p4aAc.mjs.map +1 -0
- package/dist/schema-CgrEMqEd.d.mts +86 -0
- package/dist/schema-CgrEMqEd.d.mts.map +1 -0
- package/dist/types-CEUzDuDC.d.mts +447 -0
- package/dist/types-CEUzDuDC.d.mts.map +1 -0
- package/dist/types-Cdi4Whda.mjs +18 -0
- package/dist/types-Cdi4Whda.mjs.map +1 -0
- package/package.json +38 -55
- package/src/ast/codec-types.ts +77 -8
- package/src/ast/delete.ts +2 -2
- package/src/ast/driver-types.ts +20 -3
- package/src/ast/predicate.ts +14 -4
- package/src/ast/select.ts +4 -3
- package/src/ast/sql-codecs.ts +67 -0
- package/src/ast/types.ts +89 -19
- package/src/ast/update.ts +2 -2
- package/src/exports/ast.ts +1 -0
- package/src/exports/guards.ts +5 -0
- package/src/exports/utils/guards.ts +1 -0
- package/src/operations-registry.ts +112 -73
- package/src/query-lane-context.ts +77 -1
- package/src/schema.ts +91 -33
- package/src/types.ts +327 -75
- package/src/utils/guards.ts +88 -18
- package/dist/ast/adapter-types.d.ts +0 -28
- package/dist/ast/adapter-types.d.ts.map +0 -1
- package/dist/ast/codec-types.d.ts +0 -141
- package/dist/ast/codec-types.d.ts.map +0 -1
- package/dist/ast/common.d.ts +0 -7
- package/dist/ast/common.d.ts.map +0 -1
- package/dist/ast/delete.d.ts +0 -8
- package/dist/ast/delete.d.ts.map +0 -1
- package/dist/ast/driver-types.d.ts +0 -20
- package/dist/ast/driver-types.d.ts.map +0 -1
- package/dist/ast/insert.d.ts +0 -8
- package/dist/ast/insert.d.ts.map +0 -1
- package/dist/ast/join.d.ts +0 -6
- package/dist/ast/join.d.ts.map +0 -1
- package/dist/ast/order.d.ts +0 -6
- package/dist/ast/order.d.ts.map +0 -1
- package/dist/ast/predicate.d.ts +0 -4
- package/dist/ast/predicate.d.ts.map +0 -1
- package/dist/ast/select.d.ts +0 -18
- package/dist/ast/select.d.ts.map +0 -1
- package/dist/ast/types.d.ts +0 -118
- package/dist/ast/types.d.ts.map +0 -1
- package/dist/ast/update.d.ts +0 -9
- package/dist/ast/update.d.ts.map +0 -1
- package/dist/ast/util.d.ts +0 -2
- package/dist/ast/util.d.ts.map +0 -1
- package/dist/chunk-2F7DSEOU.js +0 -8
- package/dist/chunk-2F7DSEOU.js.map +0 -1
- package/dist/chunk-36WJWNHT.js +0 -1
- package/dist/chunk-36WJWNHT.js.map +0 -1
- package/dist/chunk-5N34PNVZ.js +0 -62
- package/dist/chunk-5N34PNVZ.js.map +0 -1
- package/dist/chunk-7I3EMQID.js +0 -16
- package/dist/chunk-7I3EMQID.js.map +0 -1
- package/dist/chunk-CBTYMOX2.js +0 -152
- package/dist/chunk-CBTYMOX2.js.map +0 -1
- package/dist/chunk-G52ENULI.js +0 -1
- package/dist/chunk-G52ENULI.js.map +0 -1
- package/dist/chunk-KYSP7L5C.js +0 -16
- package/dist/chunk-KYSP7L5C.js.map +0 -1
- package/dist/chunk-M23L3JHG.js +0 -159
- package/dist/chunk-M23L3JHG.js.map +0 -1
- package/dist/chunk-MM74SVJ4.js +0 -13
- package/dist/chunk-MM74SVJ4.js.map +0 -1
- package/dist/chunk-U7AXAUJA.js +0 -1
- package/dist/chunk-U7AXAUJA.js.map +0 -1
- package/dist/chunk-WZBPVEZI.js +0 -320
- package/dist/chunk-WZBPVEZI.js.map +0 -1
- package/dist/errors.d.ts +0 -2
- package/dist/errors.d.ts.map +0 -1
- package/dist/exports/ast.d.ts +0 -14
- package/dist/exports/ast.d.ts.map +0 -1
- package/dist/exports/ast.js +0 -46
- package/dist/exports/ast.js.map +0 -1
- package/dist/exports/errors.d.ts +0 -2
- package/dist/exports/errors.d.ts.map +0 -1
- package/dist/exports/errors.js +0 -9
- package/dist/exports/errors.js.map +0 -1
- package/dist/exports/guards.d.ts +0 -2
- package/dist/exports/guards.d.ts.map +0 -1
- package/dist/exports/guards.js +0 -21
- package/dist/exports/guards.js.map +0 -1
- package/dist/exports/operations-registry.d.ts +0 -2
- package/dist/exports/operations-registry.d.ts.map +0 -1
- package/dist/exports/operations-registry.js +0 -9
- package/dist/exports/operations-registry.js.map +0 -1
- package/dist/exports/param.d.ts +0 -3
- package/dist/exports/param.d.ts.map +0 -1
- package/dist/exports/param.js +0 -7
- package/dist/exports/param.js.map +0 -1
- package/dist/exports/plan.d.ts +0 -2
- package/dist/exports/plan.d.ts.map +0 -1
- package/dist/exports/plan.js +0 -7
- package/dist/exports/plan.js.map +0 -1
- package/dist/exports/query-lane-context.d.ts +0 -2
- package/dist/exports/query-lane-context.d.ts.map +0 -1
- package/dist/exports/query-lane-context.js +0 -2
- package/dist/exports/query-lane-context.js.map +0 -1
- package/dist/exports/schema.d.ts +0 -3
- package/dist/exports/schema.d.ts.map +0 -1
- package/dist/exports/schema.js +0 -14
- package/dist/exports/schema.js.map +0 -1
- package/dist/exports/types.d.ts +0 -2
- package/dist/exports/types.d.ts.map +0 -1
- package/dist/exports/types.js +0 -10
- package/dist/exports/types.js.map +0 -1
- package/dist/index.d.ts +0 -9
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -81
- package/dist/index.js.map +0 -1
- package/dist/operations-registry.d.ts +0 -5
- package/dist/operations-registry.d.ts.map +0 -1
- package/dist/param.d.ts +0 -4
- package/dist/param.d.ts.map +0 -1
- package/dist/plan.d.ts +0 -23
- package/dist/plan.d.ts.map +0 -1
- package/dist/query-lane-context.d.ts +0 -16
- package/dist/query-lane-context.d.ts.map +0 -1
- package/dist/schema.d.ts +0 -63
- package/dist/schema.d.ts.map +0 -1
- package/dist/types.d.ts +0 -332
- package/dist/types.d.ts.map +0 -1
- package/dist/utils/guards.d.ts +0 -55
- package/dist/utils/guards.d.ts.map +0 -1
package/src/ast/update.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ColumnRef, ParamRef, TableRef, UpdateAst, WhereExpr } from './types';
|
|
2
2
|
import { compact } from './util';
|
|
3
3
|
|
|
4
4
|
export interface CreateUpdateAstOptions {
|
|
5
5
|
readonly table: TableRef;
|
|
6
6
|
readonly set: Record<string, ColumnRef | ParamRef>;
|
|
7
|
-
readonly where
|
|
7
|
+
readonly where?: WhereExpr;
|
|
8
8
|
readonly returning?: ReadonlyArray<ColumnRef>;
|
|
9
9
|
}
|
|
10
10
|
|
package/src/exports/ast.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from '../ast/join';
|
|
|
8
8
|
export * from '../ast/order';
|
|
9
9
|
export * from '../ast/predicate';
|
|
10
10
|
export * from '../ast/select';
|
|
11
|
+
export * from '../ast/sql-codecs';
|
|
11
12
|
export * from '../ast/types';
|
|
12
13
|
export * from '../ast/update';
|
|
13
14
|
export * from '../ast/util';
|
package/src/exports/guards.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
export {
|
|
2
2
|
collectColumnRefs,
|
|
3
|
+
expressionFromSource,
|
|
3
4
|
extractBaseColumnRef,
|
|
4
5
|
getColumnInfo,
|
|
5
6
|
getColumnMeta,
|
|
6
7
|
getOperationExpr,
|
|
7
8
|
isColumnBuilder,
|
|
9
|
+
isExpressionBuilder,
|
|
10
|
+
isExpressionSource,
|
|
8
11
|
isOperationExpr,
|
|
9
12
|
isParamPlaceholder,
|
|
13
|
+
isValueSource,
|
|
14
|
+
toExpression,
|
|
10
15
|
} from '../utils/guards';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../guards';
|
|
@@ -3,59 +3,71 @@ import { hasAllCapabilities } from '@prisma-next/operations';
|
|
|
3
3
|
import { planInvalid } from '@prisma-next/plan';
|
|
4
4
|
import type { StorageColumn } from '@prisma-next/sql-contract/types';
|
|
5
5
|
import type { SqlOperationSignature } from '@prisma-next/sql-operations';
|
|
6
|
-
import type {
|
|
7
|
-
|
|
6
|
+
import type {
|
|
7
|
+
BinaryOp,
|
|
8
|
+
Expression,
|
|
9
|
+
ExpressionSource,
|
|
10
|
+
LiteralExpr,
|
|
11
|
+
OperationExpr,
|
|
12
|
+
ParamRef,
|
|
13
|
+
} from './ast/types';
|
|
14
|
+
import type {
|
|
15
|
+
AnyBinaryBuilder,
|
|
16
|
+
AnyOrderBuilder,
|
|
17
|
+
ColumnBuilder,
|
|
18
|
+
ExpressionBuilder,
|
|
19
|
+
OperationTypes,
|
|
20
|
+
ParamPlaceholder,
|
|
21
|
+
} from './types';
|
|
8
22
|
import { isParamPlaceholder } from './utils/guards';
|
|
9
23
|
|
|
10
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Type guard to check if a value is an ExpressionSource (has toExpr method).
|
|
26
|
+
*/
|
|
27
|
+
function isExpressionSource(value: unknown): value is ExpressionSource {
|
|
11
28
|
return (
|
|
12
29
|
typeof value === 'object' &&
|
|
13
30
|
value !== null &&
|
|
14
|
-
'
|
|
15
|
-
(value as
|
|
31
|
+
'toExpr' in value &&
|
|
32
|
+
typeof (value as ExpressionSource).toExpr === 'function'
|
|
16
33
|
);
|
|
17
34
|
}
|
|
18
35
|
|
|
19
36
|
/**
|
|
20
|
-
* Executes an operation and returns
|
|
37
|
+
* Executes an operation and returns an ExpressionBuilder.
|
|
21
38
|
* This is the canonical entrypoint for operation invocation, enabling
|
|
22
39
|
* future enhancements like telemetry, caching, or tracing.
|
|
23
40
|
*
|
|
41
|
+
* The returned ExpressionBuilder:
|
|
42
|
+
* - Has `kind: 'expression'` to distinguish it from ColumnBuilder
|
|
43
|
+
* - Contains the operation expression in `expr`
|
|
44
|
+
* - Provides `toExpr()` method to get the Expression
|
|
45
|
+
* - Provides comparison and ordering methods for chaining
|
|
46
|
+
*
|
|
24
47
|
* @param signature - The operation signature from the registry
|
|
25
|
-
* @param selfBuilder - The
|
|
48
|
+
* @param selfBuilder - The expression source that the operation is called on
|
|
26
49
|
* @param args - The arguments passed to the operation
|
|
27
50
|
* @param columnMeta - The metadata of the column the operation is called on
|
|
28
|
-
* @returns
|
|
51
|
+
* @returns An ExpressionBuilder containing the operation expression
|
|
29
52
|
*/
|
|
30
53
|
function executeOperation(
|
|
31
54
|
signature: SqlOperationSignature,
|
|
32
|
-
selfBuilder:
|
|
55
|
+
selfBuilder: ExpressionSource,
|
|
33
56
|
args: unknown[],
|
|
34
57
|
columnMeta: StorageColumn,
|
|
35
58
|
operationRegistry?: OperationRegistry,
|
|
36
59
|
contractCapabilities?: Record<string, Record<string, boolean>>,
|
|
37
|
-
):
|
|
60
|
+
): ExpressionBuilder {
|
|
38
61
|
if (args.length !== signature.args.length) {
|
|
39
62
|
throw planInvalid(
|
|
40
63
|
`Operation ${signature.method} expects ${signature.args.length} arguments, got ${args.length}`,
|
|
41
64
|
);
|
|
42
65
|
}
|
|
43
66
|
|
|
44
|
-
//
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
column: string;
|
|
49
|
-
};
|
|
50
|
-
const selfExpr: ColumnRef | OperationExpr = selfBuilderWithExpr._operationExpr
|
|
51
|
-
? selfBuilderWithExpr._operationExpr
|
|
52
|
-
: {
|
|
53
|
-
kind: 'col',
|
|
54
|
-
table: selfBuilderWithExpr.table,
|
|
55
|
-
column: selfBuilderWithExpr.column,
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const operationArgs: Array<ColumnRef | ParamRef | LiteralExpr | OperationExpr> = [];
|
|
67
|
+
// Get the Expression from the self builder using toExpr()
|
|
68
|
+
const selfExpr: Expression = selfBuilder.toExpr();
|
|
69
|
+
|
|
70
|
+
const operationArgs: Array<Expression | ParamRef | LiteralExpr> = [];
|
|
59
71
|
for (let i = 0; i < args.length; i++) {
|
|
60
72
|
const arg = args[i];
|
|
61
73
|
const argSpec = signature.args[i];
|
|
@@ -73,25 +85,14 @@ function executeOperation(
|
|
|
73
85
|
name: arg.name,
|
|
74
86
|
});
|
|
75
87
|
} else if (argSpec.kind === 'typeId') {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
table: string;
|
|
82
|
-
column: string;
|
|
83
|
-
};
|
|
84
|
-
// Check if the column builder has an operation expression
|
|
85
|
-
if (colBuilderWithExpr._operationExpr) {
|
|
86
|
-
operationArgs.push(colBuilderWithExpr._operationExpr);
|
|
87
|
-
} else {
|
|
88
|
-
// Fall back to raw ColumnRef
|
|
89
|
-
operationArgs.push({
|
|
90
|
-
kind: 'col',
|
|
91
|
-
table: colBuilderWithExpr.table,
|
|
92
|
-
column: colBuilderWithExpr.column,
|
|
93
|
-
});
|
|
88
|
+
// Accept ExpressionSource (ColumnBuilder or ExpressionBuilder)
|
|
89
|
+
if (!isExpressionSource(arg)) {
|
|
90
|
+
throw planInvalid(
|
|
91
|
+
`Argument ${i} must be an ExpressionSource (ColumnBuilder or ExpressionBuilder)`,
|
|
92
|
+
);
|
|
94
93
|
}
|
|
94
|
+
// Use toExpr() to get the Expression
|
|
95
|
+
operationArgs.push(arg.toExpr());
|
|
95
96
|
} else if (argSpec.kind === 'literal') {
|
|
96
97
|
operationArgs.push({
|
|
97
98
|
kind: 'literal',
|
|
@@ -118,18 +119,19 @@ function executeOperation(
|
|
|
118
119
|
}
|
|
119
120
|
: columnMeta;
|
|
120
121
|
|
|
121
|
-
const createComparisonMethod =
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
122
|
+
const createComparisonMethod =
|
|
123
|
+
(op: BinaryOp) =>
|
|
124
|
+
(value: ParamPlaceholder | ExpressionSource): AnyBinaryBuilder =>
|
|
125
|
+
Object.freeze({
|
|
126
|
+
kind: 'binary' as const,
|
|
127
|
+
op,
|
|
128
|
+
left: operationExpr,
|
|
129
|
+
right: value,
|
|
130
|
+
}) as AnyBinaryBuilder;
|
|
131
|
+
|
|
132
|
+
const baseResult: ExpressionBuilder = {
|
|
133
|
+
kind: 'expression' as const,
|
|
134
|
+
expr: operationExpr,
|
|
133
135
|
get columnMeta() {
|
|
134
136
|
return returnColumnMeta;
|
|
135
137
|
},
|
|
@@ -139,41 +141,84 @@ function executeOperation(
|
|
|
139
141
|
lt: createComparisonMethod('lt'),
|
|
140
142
|
gte: createComparisonMethod('gte'),
|
|
141
143
|
lte: createComparisonMethod('lte'),
|
|
142
|
-
asc() {
|
|
144
|
+
asc(): AnyOrderBuilder {
|
|
143
145
|
return Object.freeze({
|
|
144
146
|
kind: 'order' as const,
|
|
145
147
|
expr: operationExpr,
|
|
146
148
|
dir: 'asc' as const,
|
|
147
149
|
});
|
|
148
150
|
},
|
|
149
|
-
desc() {
|
|
151
|
+
desc(): AnyOrderBuilder {
|
|
150
152
|
return Object.freeze({
|
|
151
153
|
kind: 'order' as const,
|
|
152
154
|
expr: operationExpr,
|
|
153
155
|
dir: 'desc' as const,
|
|
154
156
|
});
|
|
155
157
|
},
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
toExpr(): OperationExpr {
|
|
159
|
+
return operationExpr;
|
|
160
|
+
},
|
|
161
|
+
get __jsType(): unknown {
|
|
162
|
+
return undefined;
|
|
163
|
+
},
|
|
159
164
|
};
|
|
160
165
|
|
|
161
166
|
// If the return type is a typeId, attach operations for that type
|
|
162
167
|
if (returnTypeId && operationRegistry) {
|
|
163
|
-
const resultWithOps =
|
|
164
|
-
baseResult
|
|
168
|
+
const resultWithOps = attachOperationsToExpressionBuilder(
|
|
169
|
+
baseResult,
|
|
165
170
|
returnColumnMeta,
|
|
166
171
|
operationRegistry,
|
|
167
172
|
contractCapabilities,
|
|
168
|
-
)
|
|
169
|
-
_operationExpr?: OperationExpr;
|
|
170
|
-
};
|
|
173
|
+
);
|
|
171
174
|
return Object.freeze(resultWithOps);
|
|
172
175
|
}
|
|
173
176
|
|
|
174
177
|
return Object.freeze(baseResult);
|
|
175
178
|
}
|
|
176
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Attaches operation methods to an ExpressionBuilder for chained operations.
|
|
182
|
+
* When an operation returns a typeId, the result ExpressionBuilder needs
|
|
183
|
+
* operation methods for that type.
|
|
184
|
+
*/
|
|
185
|
+
function attachOperationsToExpressionBuilder(
|
|
186
|
+
expressionBuilder: ExpressionBuilder,
|
|
187
|
+
columnMeta: StorageColumn,
|
|
188
|
+
registry: OperationRegistry,
|
|
189
|
+
contractCapabilities?: Record<string, Record<string, boolean>>,
|
|
190
|
+
): ExpressionBuilder {
|
|
191
|
+
const codecId = columnMeta.codecId;
|
|
192
|
+
if (!codecId) {
|
|
193
|
+
return expressionBuilder;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const operations = registry.byType(codecId) as SqlOperationSignature[];
|
|
197
|
+
if (operations.length === 0) {
|
|
198
|
+
return expressionBuilder;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const builderWithOps = expressionBuilder as ExpressionBuilder & Record<string, unknown>;
|
|
202
|
+
|
|
203
|
+
for (const operation of operations) {
|
|
204
|
+
if (operation.capabilities && operation.capabilities.length > 0) {
|
|
205
|
+
if (!contractCapabilities) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!hasAllCapabilities(operation.capabilities, contractCapabilities)) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Method sugar: attach operation as a method on the expression builder
|
|
214
|
+
builderWithOps[operation.method] = function (this: ExpressionBuilder, ...args: unknown[]) {
|
|
215
|
+
return executeOperation(operation, this, args, columnMeta, registry, contractCapabilities);
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return builderWithOps;
|
|
220
|
+
}
|
|
221
|
+
|
|
177
222
|
export function attachOperationsToColumnBuilder<
|
|
178
223
|
ColumnName extends string,
|
|
179
224
|
ColumnMeta extends StorageColumn,
|
|
@@ -218,18 +263,12 @@ export function attachOperationsToColumnBuilder<
|
|
|
218
263
|
}
|
|
219
264
|
}
|
|
220
265
|
// Method sugar: attach operation as a method on the column builder
|
|
266
|
+
// Operations return ExpressionBuilder, not ColumnBuilder
|
|
221
267
|
(builderWithOps as Record<string, unknown>)[operation.method] = function (
|
|
222
268
|
this: ColumnBuilder<ColumnName, ColumnMeta, JsType, Record<string, never>>,
|
|
223
269
|
...args: unknown[]
|
|
224
270
|
) {
|
|
225
|
-
return executeOperation(
|
|
226
|
-
operation,
|
|
227
|
-
this as unknown as ColumnBuilder<string, StorageColumn, unknown>,
|
|
228
|
-
args,
|
|
229
|
-
columnMeta,
|
|
230
|
-
registry,
|
|
231
|
-
contractCapabilities,
|
|
232
|
-
);
|
|
271
|
+
return executeOperation(operation, this, args, columnMeta, registry, contractCapabilities);
|
|
233
272
|
};
|
|
234
273
|
}
|
|
235
274
|
|
|
@@ -2,6 +2,67 @@ import type { OperationRegistry } from '@prisma-next/operations';
|
|
|
2
2
|
import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
|
|
3
3
|
import type { CodecRegistry } from './ast/codec-types';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Registry of initialized type helpers from storage.types.
|
|
7
|
+
* Each key is a type name from storage.types, and the value is:
|
|
8
|
+
* - The result of the codec's init hook (if provided), or
|
|
9
|
+
* - The full StorageTypeInstance metadata (codecId, nativeType, typeParams) if no init hook
|
|
10
|
+
*/
|
|
11
|
+
export type TypeHelperRegistry = Record<string, unknown>;
|
|
12
|
+
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// JSON Schema Validation Types
|
|
15
|
+
// =============================================================================
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A single validation error from JSON Schema validation.
|
|
19
|
+
*/
|
|
20
|
+
export interface JsonSchemaValidationError {
|
|
21
|
+
readonly path: string;
|
|
22
|
+
readonly message: string;
|
|
23
|
+
readonly keyword: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Result of a JSON Schema validation.
|
|
28
|
+
*/
|
|
29
|
+
export type JsonSchemaValidationResult =
|
|
30
|
+
| { readonly valid: true }
|
|
31
|
+
| { readonly valid: false; readonly errors: ReadonlyArray<JsonSchemaValidationError> };
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A compiled JSON Schema validate function.
|
|
35
|
+
* Returns a structured result indicating whether the value conforms to the schema.
|
|
36
|
+
*/
|
|
37
|
+
export type JsonSchemaValidateFn = (value: unknown) => JsonSchemaValidationResult;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Registry of compiled JSON Schema validators for columns with typed JSON/JSONB.
|
|
41
|
+
*
|
|
42
|
+
* Built during context creation by scanning the contract for columns whose codec
|
|
43
|
+
* descriptor provides an `init` hook that returns a `{ validate }` helper.
|
|
44
|
+
* Keys are `"table.column"` (e.g., `"user.metadata"`).
|
|
45
|
+
*/
|
|
46
|
+
export interface JsonSchemaValidatorRegistry {
|
|
47
|
+
/** Get the compiled validator for a column. Key format: "table.column". */
|
|
48
|
+
get(key: string): JsonSchemaValidateFn | undefined;
|
|
49
|
+
/** Number of registered validators. */
|
|
50
|
+
readonly size: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type MutationDefaultsOp = 'create' | 'update';
|
|
54
|
+
|
|
55
|
+
export type AppliedMutationDefault = {
|
|
56
|
+
readonly column: string;
|
|
57
|
+
readonly value: unknown;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export type MutationDefaultsOptions = {
|
|
61
|
+
readonly op: MutationDefaultsOp;
|
|
62
|
+
readonly table: string;
|
|
63
|
+
readonly values: Record<string, unknown>;
|
|
64
|
+
};
|
|
65
|
+
|
|
5
66
|
/**
|
|
6
67
|
* Minimal context interface for SQL query lanes.
|
|
7
68
|
*
|
|
@@ -9,10 +70,25 @@ import type { CodecRegistry } from './ast/codec-types';
|
|
|
9
70
|
* operation builders. This interface explicitly excludes runtime concerns like
|
|
10
71
|
* adapters, connection management, and transaction state.
|
|
11
72
|
*/
|
|
12
|
-
export interface
|
|
73
|
+
export interface ExecutionContext<
|
|
13
74
|
TContract extends SqlContract<SqlStorage> = SqlContract<SqlStorage>,
|
|
14
75
|
> {
|
|
15
76
|
readonly contract: TContract;
|
|
16
77
|
readonly operations: OperationRegistry;
|
|
17
78
|
readonly codecs: CodecRegistry;
|
|
79
|
+
/**
|
|
80
|
+
* Type helper registry for parameterized types.
|
|
81
|
+
* Schema builders expose these helpers via schema.types.
|
|
82
|
+
*/
|
|
83
|
+
readonly types: TypeHelperRegistry;
|
|
84
|
+
/**
|
|
85
|
+
* Compiled JSON Schema validators for typed JSON/JSONB columns.
|
|
86
|
+
* Present only when the contract declares columns with JSON Schema typeParams.
|
|
87
|
+
*/
|
|
88
|
+
readonly jsonSchemaValidators?: JsonSchemaValidatorRegistry;
|
|
89
|
+
/**
|
|
90
|
+
* Applies execution-time mutation defaults for the given table.
|
|
91
|
+
* Returns the applied defaults (caller-provided values always win).
|
|
92
|
+
*/
|
|
93
|
+
applyMutationDefaults(options: MutationDefaultsOptions): ReadonlyArray<AppliedMutationDefault>;
|
|
18
94
|
}
|
package/src/schema.ts
CHANGED
|
@@ -7,21 +7,20 @@ import type {
|
|
|
7
7
|
SqlStorage,
|
|
8
8
|
StorageColumn,
|
|
9
9
|
} from '@prisma-next/sql-contract/types';
|
|
10
|
-
import type { BinaryOp, TableRef } from './ast/types';
|
|
10
|
+
import type { BinaryOp, ColumnRef, ExpressionSource, TableRef } from './ast/types';
|
|
11
11
|
import { attachOperationsToColumnBuilder } from './operations-registry';
|
|
12
|
-
import type {
|
|
12
|
+
import type { ExecutionContext } from './query-lane-context';
|
|
13
13
|
import type {
|
|
14
|
-
AnyColumnBuilderBase,
|
|
15
14
|
BinaryBuilder,
|
|
16
15
|
CodecTypes as CodecTypesType,
|
|
17
16
|
ColumnBuilder,
|
|
18
17
|
ComputeColumnJsType,
|
|
18
|
+
NullCheckBuilder,
|
|
19
19
|
OperationTypeSignature,
|
|
20
20
|
OperationTypes,
|
|
21
21
|
OrderBuilder,
|
|
22
22
|
ParamPlaceholder,
|
|
23
23
|
} from './types';
|
|
24
|
-
import { isColumnBuilder } from './types';
|
|
25
24
|
|
|
26
25
|
type TableColumns<Table extends { columns: Record<string, StorageColumn> }> = Table['columns'];
|
|
27
26
|
|
|
@@ -44,7 +43,8 @@ export class ColumnBuilderImpl<
|
|
|
44
43
|
ColumnName extends string,
|
|
45
44
|
ColumnMeta extends StorageColumn,
|
|
46
45
|
JsType = unknown,
|
|
47
|
-
>
|
|
46
|
+
> implements ExpressionSource
|
|
47
|
+
{
|
|
48
48
|
readonly kind = 'column' as const;
|
|
49
49
|
|
|
50
50
|
constructor(
|
|
@@ -62,64 +62,76 @@ export class ColumnBuilderImpl<
|
|
|
62
62
|
return undefined as unknown as JsType;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Converts this column builder to a ColumnRef expression.
|
|
67
|
+
* This is the canonical way to get an AST node from a builder.
|
|
68
|
+
*/
|
|
69
|
+
toExpr(): ColumnRef {
|
|
70
|
+
return Object.freeze({
|
|
71
|
+
kind: 'col' as const,
|
|
72
|
+
table: this.table,
|
|
73
|
+
column: this.column,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
65
77
|
private createBinaryBuilder(
|
|
66
78
|
op: BinaryOp,
|
|
67
|
-
value: ParamPlaceholder |
|
|
79
|
+
value: ParamPlaceholder | ExpressionSource,
|
|
68
80
|
): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
|
|
69
81
|
if (value == null) {
|
|
70
|
-
throw planInvalid(
|
|
82
|
+
throw planInvalid(
|
|
83
|
+
'Parameter placeholder or expression source required for column comparison',
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
// Check for ExpressionSource first (has toExpr method)
|
|
87
|
+
if ('toExpr' in value && typeof value.toExpr === 'function') {
|
|
88
|
+
return Object.freeze({
|
|
89
|
+
kind: 'binary' as const,
|
|
90
|
+
op,
|
|
91
|
+
left: this.toExpr(),
|
|
92
|
+
right: value,
|
|
93
|
+
}) as BinaryBuilder<ColumnName, ColumnMeta, JsType>;
|
|
71
94
|
}
|
|
72
|
-
|
|
95
|
+
// Must be a ParamPlaceholder
|
|
96
|
+
if ('kind' in value && value.kind === 'param-placeholder') {
|
|
73
97
|
return Object.freeze({
|
|
74
98
|
kind: 'binary' as const,
|
|
75
99
|
op,
|
|
76
|
-
left: this
|
|
100
|
+
left: this.toExpr(),
|
|
77
101
|
right: value,
|
|
78
102
|
}) as BinaryBuilder<ColumnName, ColumnMeta, JsType>;
|
|
79
103
|
}
|
|
80
|
-
throw planInvalid('Parameter placeholder or
|
|
104
|
+
throw planInvalid('Parameter placeholder or expression source required for column comparison');
|
|
81
105
|
}
|
|
82
106
|
|
|
83
|
-
eq(
|
|
84
|
-
value: ParamPlaceholder | AnyColumnBuilderBase,
|
|
85
|
-
): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
|
|
107
|
+
eq(value: ParamPlaceholder | ExpressionSource): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
|
|
86
108
|
return this.createBinaryBuilder('eq', value);
|
|
87
109
|
}
|
|
88
110
|
|
|
89
|
-
neq(
|
|
90
|
-
value: ParamPlaceholder | AnyColumnBuilderBase,
|
|
91
|
-
): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
|
|
111
|
+
neq(value: ParamPlaceholder | ExpressionSource): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
|
|
92
112
|
return this.createBinaryBuilder('neq', value);
|
|
93
113
|
}
|
|
94
114
|
|
|
95
|
-
gt(
|
|
96
|
-
value: ParamPlaceholder | AnyColumnBuilderBase,
|
|
97
|
-
): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
|
|
115
|
+
gt(value: ParamPlaceholder | ExpressionSource): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
|
|
98
116
|
return this.createBinaryBuilder('gt', value);
|
|
99
117
|
}
|
|
100
118
|
|
|
101
|
-
lt(
|
|
102
|
-
value: ParamPlaceholder | AnyColumnBuilderBase,
|
|
103
|
-
): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
|
|
119
|
+
lt(value: ParamPlaceholder | ExpressionSource): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
|
|
104
120
|
return this.createBinaryBuilder('lt', value);
|
|
105
121
|
}
|
|
106
122
|
|
|
107
|
-
gte(
|
|
108
|
-
value: ParamPlaceholder | AnyColumnBuilderBase,
|
|
109
|
-
): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
|
|
123
|
+
gte(value: ParamPlaceholder | ExpressionSource): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
|
|
110
124
|
return this.createBinaryBuilder('gte', value);
|
|
111
125
|
}
|
|
112
126
|
|
|
113
|
-
lte(
|
|
114
|
-
value: ParamPlaceholder | AnyColumnBuilderBase,
|
|
115
|
-
): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
|
|
127
|
+
lte(value: ParamPlaceholder | ExpressionSource): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
|
|
116
128
|
return this.createBinaryBuilder('lte', value);
|
|
117
129
|
}
|
|
118
130
|
|
|
119
131
|
asc(): OrderBuilder<ColumnName, ColumnMeta, JsType> {
|
|
120
132
|
return Object.freeze({
|
|
121
133
|
kind: 'order' as const,
|
|
122
|
-
expr: this
|
|
134
|
+
expr: this.toExpr(),
|
|
123
135
|
dir: 'asc' as const,
|
|
124
136
|
}) as OrderBuilder<ColumnName, ColumnMeta, JsType>;
|
|
125
137
|
}
|
|
@@ -127,10 +139,34 @@ export class ColumnBuilderImpl<
|
|
|
127
139
|
desc(): OrderBuilder<ColumnName, ColumnMeta, JsType> {
|
|
128
140
|
return Object.freeze({
|
|
129
141
|
kind: 'order' as const,
|
|
130
|
-
expr: this
|
|
142
|
+
expr: this.toExpr(),
|
|
131
143
|
dir: 'desc' as const,
|
|
132
144
|
}) as OrderBuilder<ColumnName, ColumnMeta, JsType>;
|
|
133
145
|
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Creates an IS NULL check for this column.
|
|
149
|
+
* Available on all columns at runtime, but typed to only be visible on nullable columns.
|
|
150
|
+
*/
|
|
151
|
+
isNull(): NullCheckBuilder<ColumnName, ColumnMeta, JsType> {
|
|
152
|
+
return Object.freeze({
|
|
153
|
+
kind: 'nullCheck' as const,
|
|
154
|
+
expr: this.toExpr(),
|
|
155
|
+
isNull: true,
|
|
156
|
+
}) as NullCheckBuilder<ColumnName, ColumnMeta, JsType>;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Creates an IS NOT NULL check for this column.
|
|
161
|
+
* Available on all columns at runtime, but typed to only be visible on nullable columns.
|
|
162
|
+
*/
|
|
163
|
+
isNotNull(): NullCheckBuilder<ColumnName, ColumnMeta, JsType> {
|
|
164
|
+
return Object.freeze({
|
|
165
|
+
kind: 'nullCheck' as const,
|
|
166
|
+
expr: this.toExpr(),
|
|
167
|
+
isNull: false,
|
|
168
|
+
}) as NullCheckBuilder<ColumnName, ColumnMeta, JsType>;
|
|
169
|
+
}
|
|
134
170
|
}
|
|
135
171
|
|
|
136
172
|
export class TableBuilderImpl<
|
|
@@ -290,12 +326,30 @@ type ExtractSchemaTables<
|
|
|
290
326
|
TableRef;
|
|
291
327
|
};
|
|
292
328
|
|
|
329
|
+
/**
|
|
330
|
+
* Extracts the types registry shape from a contract.
|
|
331
|
+
* Each key is a type name from storage.types, and the value preserves the
|
|
332
|
+
* literal type from the contract (including codecId, nativeType, and typeParams).
|
|
333
|
+
* Returns an empty object type {} when storage.types is undefined.
|
|
334
|
+
*/
|
|
335
|
+
type ExtractSchemaTypes<Contract extends SqlContract<SqlStorage>> =
|
|
336
|
+
Contract['storage']['types'] extends infer Types
|
|
337
|
+
? Types extends Record<string, unknown>
|
|
338
|
+
? { readonly [TypeName in keyof Types]: Types[TypeName] }
|
|
339
|
+
: Record<string, never>
|
|
340
|
+
: Record<string, never>;
|
|
341
|
+
|
|
293
342
|
export type SchemaHandle<
|
|
294
343
|
Contract extends SqlContract<SqlStorage> = SqlContract<SqlStorage>,
|
|
295
344
|
CodecTypes extends CodecTypesType = CodecTypesType,
|
|
296
345
|
Operations extends OperationTypes = Record<string, never>,
|
|
297
346
|
> = {
|
|
298
347
|
readonly tables: ExtractSchemaTables<Contract, CodecTypes, Operations>;
|
|
348
|
+
/**
|
|
349
|
+
* Initialized type helpers from storage.types.
|
|
350
|
+
* Each entry corresponds to a named type instance in the contract's storage.types.
|
|
351
|
+
*/
|
|
352
|
+
readonly types: ExtractSchemaTypes<Contract>;
|
|
299
353
|
};
|
|
300
354
|
|
|
301
355
|
type SchemaReturnType<Contract extends SqlContract<SqlStorage>> = SchemaHandle<
|
|
@@ -318,16 +372,17 @@ type ToOperationTypes<T> = T extends OperationTypes ? T : NormalizeOperationType
|
|
|
318
372
|
* Creates a schema handle for building SQL queries.
|
|
319
373
|
*
|
|
320
374
|
* @param context - Query lane context containing contract, codec and operation registries
|
|
321
|
-
* @returns A schema handle with typed table builders
|
|
375
|
+
* @returns A schema handle with typed table builders and type helpers
|
|
322
376
|
*
|
|
323
377
|
* @example
|
|
324
378
|
* ```typescript
|
|
325
379
|
* const schemaHandle = schema<Contract>(context);
|
|
326
380
|
* const userTable = schemaHandle.tables.user;
|
|
381
|
+
* const vectorType = schemaHandle.types.Vector1536;
|
|
327
382
|
* ```
|
|
328
383
|
*/
|
|
329
384
|
export function schema<Contract extends SqlContract<SqlStorage>>(
|
|
330
|
-
context:
|
|
385
|
+
context: ExecutionContext<Contract>,
|
|
331
386
|
): SchemaReturnType<Contract> {
|
|
332
387
|
const contract = context.contract;
|
|
333
388
|
const storage = contract.storage;
|
|
@@ -367,7 +422,10 @@ export function schema<Contract extends SqlContract<SqlStorage>>(
|
|
|
367
422
|
) as ExtractSchemaTables<Contract, CodecTypes, Operations>[typeof tableName];
|
|
368
423
|
}
|
|
369
424
|
|
|
370
|
-
|
|
425
|
+
// Get type helpers from context (populated by runtime context creation)
|
|
426
|
+
const types = context.types as ExtractSchemaTypes<Contract>;
|
|
427
|
+
|
|
428
|
+
return Object.freeze({ tables, types }) as SchemaReturnType<Contract>;
|
|
371
429
|
}
|
|
372
430
|
|
|
373
431
|
export type { ColumnBuilderImpl as Column, TableBuilderImpl as Table };
|