@prisma-next/sql-relational-core 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.
- package/README.md +5 -2
- package/dist/errors-7_V3El9I.mjs +3 -0
- package/dist/errors-DVufq9PT.d.mts +2 -0
- package/dist/exports/ast.d.mts +189 -0
- package/dist/exports/ast.d.mts.map +1 -0
- package/dist/exports/ast.mjs +360 -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-l1R3q8pA.d.mts +87 -0
- package/dist/guards-l1R3q8pA.d.mts.map +1 -0
- package/dist/index.d.mts +12 -0
- package/dist/index.mjs +12 -0
- package/dist/{exports/operations-registry.d.ts → operations-registry-0GuSkOCC.d.mts} +7 -11
- package/dist/operations-registry-0GuSkOCC.d.mts.map +1 -0
- package/dist/operations-registry-DPZ5aElH.mjs +152 -0
- package/dist/operations-registry-DPZ5aElH.mjs.map +1 -0
- package/dist/param-ChBZwVcw.d.mts +8 -0
- package/dist/param-ChBZwVcw.d.mts.map +1 -0
- package/dist/param-DMU3OWfI.mjs +14 -0
- package/dist/param-DMU3OWfI.mjs.map +1 -0
- package/dist/plan-cjDF_yFX.d.mts +182 -0
- package/dist/plan-cjDF_yFX.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-CEpzzWNY.d.mts +86 -0
- package/dist/schema-CEpzzWNY.d.mts.map +1 -0
- package/dist/types-Cdi4Whda.mjs +18 -0
- package/dist/types-Cdi4Whda.mjs.map +1 -0
- package/dist/types-DicmXfBm.d.mts +447 -0
- package/dist/types-DicmXfBm.d.mts.map +1 -0
- package/package.json +41 -57
- package/src/ast/adapter-types.ts +36 -0
- package/src/ast/codec-types.ts +444 -0
- package/src/ast/common.ts +36 -0
- package/src/ast/delete.ts +17 -0
- package/src/ast/driver-types.ts +42 -0
- package/src/ast/insert.ts +17 -0
- package/src/ast/join.ts +54 -0
- package/src/ast/order.ts +11 -0
- package/src/ast/predicate.ts +40 -0
- package/src/ast/select.ts +38 -0
- package/src/ast/sql-codecs.ts +67 -0
- package/src/ast/types.ts +187 -0
- package/src/ast/update.ts +19 -0
- package/src/ast/util.ts +9 -0
- package/src/exports/ast.ts +14 -0
- package/src/exports/errors.ts +1 -0
- package/src/exports/guards.ts +15 -0
- package/src/exports/operations-registry.ts +1 -0
- package/src/exports/param.ts +2 -0
- package/src/exports/plan.ts +1 -0
- package/src/exports/query-lane-context.ts +1 -0
- package/src/exports/schema.ts +6 -0
- package/src/exports/types.ts +1 -0
- package/src/exports/utils/guards.ts +1 -0
- package/src/index.ts +8 -0
- package/src/operations-registry.ts +276 -0
- package/src/param.ts +15 -0
- package/src/plan.ts +39 -0
- package/src/query-lane-context.ts +94 -0
- package/src/schema.ts +431 -0
- package/src/types.ts +799 -0
- package/src/utils/guards.ts +193 -0
- 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/exports/ast.d.ts +0 -119
- package/dist/exports/ast.js +0 -46
- package/dist/exports/ast.js.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 -63
- package/dist/exports/guards.js +0 -21
- package/dist/exports/guards.js.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 -14
- package/dist/exports/param.js +0 -7
- package/dist/exports/param.js.map +0 -1
- package/dist/exports/plan.d.ts +0 -5
- package/dist/exports/plan.js +0 -7
- package/dist/exports/plan.js.map +0 -1
- package/dist/exports/query-lane-context.d.ts +0 -4
- 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 -69
- package/dist/exports/schema.js +0 -14
- package/dist/exports/schema.js.map +0 -1
- package/dist/exports/types.d.ts +0 -335
- package/dist/exports/types.js +0 -10
- package/dist/exports/types.js.map +0 -1
- package/dist/index.d.ts +0 -13
- package/dist/index.js +0 -81
- package/dist/index.js.map +0 -1
- package/dist/plan-D0OG5qzy.d.ts +0 -142
- package/dist/query-lane-context-BhOMmb_K.d.ts +0 -158
- /package/{dist/exports/errors.d.ts → src/errors.ts} +0 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type as arktype } from 'arktype';
|
|
2
|
+
import { codec, defineCodecs } from './codec-types';
|
|
3
|
+
|
|
4
|
+
export const SQL_CHAR_CODEC_ID = 'sql/char@1' as const;
|
|
5
|
+
export const SQL_VARCHAR_CODEC_ID = 'sql/varchar@1' as const;
|
|
6
|
+
export const SQL_INT_CODEC_ID = 'sql/int@1' as const;
|
|
7
|
+
export const SQL_FLOAT_CODEC_ID = 'sql/float@1' as const;
|
|
8
|
+
|
|
9
|
+
const lengthParamsSchema = arktype({
|
|
10
|
+
length: 'number.integer > 0',
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
type LengthTypeHelper = {
|
|
14
|
+
readonly kind: 'fixed' | 'variable';
|
|
15
|
+
readonly maxLength: number;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function createLengthTypeHelper(
|
|
19
|
+
kind: LengthTypeHelper['kind'],
|
|
20
|
+
): (params: Record<string, unknown>) => LengthTypeHelper {
|
|
21
|
+
return (params) => ({
|
|
22
|
+
kind,
|
|
23
|
+
maxLength: params['length'] as number,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const sqlCharCodec = codec<typeof SQL_CHAR_CODEC_ID, string, string>({
|
|
28
|
+
typeId: SQL_CHAR_CODEC_ID,
|
|
29
|
+
targetTypes: ['char'],
|
|
30
|
+
encode: (value: string): string => value,
|
|
31
|
+
decode: (wire: string): string => wire.trimEnd(),
|
|
32
|
+
paramsSchema: lengthParamsSchema,
|
|
33
|
+
init: createLengthTypeHelper('fixed'),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const sqlVarcharCodec = codec<typeof SQL_VARCHAR_CODEC_ID, string, string>({
|
|
37
|
+
typeId: SQL_VARCHAR_CODEC_ID,
|
|
38
|
+
targetTypes: ['varchar'],
|
|
39
|
+
encode: (value: string): string => value,
|
|
40
|
+
decode: (wire: string): string => wire,
|
|
41
|
+
paramsSchema: lengthParamsSchema,
|
|
42
|
+
init: createLengthTypeHelper('variable'),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const sqlIntCodec = codec<typeof SQL_INT_CODEC_ID, number, number>({
|
|
46
|
+
typeId: SQL_INT_CODEC_ID,
|
|
47
|
+
targetTypes: ['int'],
|
|
48
|
+
encode: (value) => value,
|
|
49
|
+
decode: (wire) => wire,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const sqlFloatCodec = codec<typeof SQL_FLOAT_CODEC_ID, number, number>({
|
|
53
|
+
typeId: SQL_FLOAT_CODEC_ID,
|
|
54
|
+
targetTypes: ['float'],
|
|
55
|
+
encode: (value) => value,
|
|
56
|
+
decode: (wire) => wire,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const codecs = defineCodecs()
|
|
60
|
+
.add('char', sqlCharCodec)
|
|
61
|
+
.add('varchar', sqlVarcharCodec)
|
|
62
|
+
.add('int', sqlIntCodec)
|
|
63
|
+
.add('float', sqlFloatCodec);
|
|
64
|
+
|
|
65
|
+
export const sqlCodecDefinitions = codecs.codecDefinitions;
|
|
66
|
+
export const sqlDataTypes = codecs.dataTypes;
|
|
67
|
+
export type SqlCodecTypes = typeof codecs.CodecTypes;
|
package/src/ast/types.ts
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import type { ReturnSpec } from '@prisma-next/operations';
|
|
2
|
+
import type { SqlLoweringSpec } from '@prisma-next/sql-operations';
|
|
3
|
+
|
|
4
|
+
// SQL-specific AST types and supporting types
|
|
5
|
+
// These types are needed by adapters and runtime for SQL query execution
|
|
6
|
+
|
|
7
|
+
export type Direction = 'asc' | 'desc';
|
|
8
|
+
|
|
9
|
+
export interface TableRef {
|
|
10
|
+
readonly kind: 'table';
|
|
11
|
+
readonly name: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ColumnRef {
|
|
15
|
+
readonly kind: 'col';
|
|
16
|
+
readonly table: string;
|
|
17
|
+
readonly column: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ParamRef {
|
|
21
|
+
readonly kind: 'param';
|
|
22
|
+
readonly index: number;
|
|
23
|
+
readonly name?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface LiteralExpr {
|
|
27
|
+
readonly kind: 'literal';
|
|
28
|
+
readonly value: unknown;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface OperationExpr {
|
|
32
|
+
readonly kind: 'operation';
|
|
33
|
+
readonly method: string;
|
|
34
|
+
readonly forTypeId: string;
|
|
35
|
+
readonly self: Expression;
|
|
36
|
+
readonly args: ReadonlyArray<Expression | ParamRef | LiteralExpr>;
|
|
37
|
+
readonly returns: ReturnSpec;
|
|
38
|
+
readonly lowering: SqlLoweringSpec;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Unified expression type - the canonical AST representation for column references
|
|
43
|
+
* and operation expressions. This is what all builders convert to via toExpr().
|
|
44
|
+
*/
|
|
45
|
+
export type Expression = ColumnRef | OperationExpr;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Interface for any builder that can produce an Expression.
|
|
49
|
+
* Implemented by ColumnBuilder and ExpressionBuilder.
|
|
50
|
+
*/
|
|
51
|
+
export interface ExpressionSource {
|
|
52
|
+
toExpr(): Expression;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function isOperationExpr(expr: Expression): expr is OperationExpr {
|
|
56
|
+
return expr.kind === 'operation';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type BinaryOp =
|
|
60
|
+
| 'eq'
|
|
61
|
+
| 'neq'
|
|
62
|
+
| 'gt'
|
|
63
|
+
| 'lt'
|
|
64
|
+
| 'gte'
|
|
65
|
+
| 'lte'
|
|
66
|
+
| 'like'
|
|
67
|
+
| 'ilike'
|
|
68
|
+
| 'in'
|
|
69
|
+
| 'notIn';
|
|
70
|
+
|
|
71
|
+
export interface ListLiteralExpr {
|
|
72
|
+
readonly kind: 'listLiteral';
|
|
73
|
+
readonly values: ReadonlyArray<ParamRef | LiteralExpr>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface BinaryExpr {
|
|
77
|
+
readonly kind: 'bin';
|
|
78
|
+
readonly op: BinaryOp;
|
|
79
|
+
readonly left: Expression;
|
|
80
|
+
readonly right: Expression | ParamRef | LiteralExpr | ListLiteralExpr;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface ExistsExpr {
|
|
84
|
+
readonly kind: 'exists';
|
|
85
|
+
readonly not: boolean;
|
|
86
|
+
readonly subquery: SelectAst;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Unary expression for IS NULL / IS NOT NULL checks.
|
|
91
|
+
* Used in WHERE clauses to check for null values.
|
|
92
|
+
*/
|
|
93
|
+
export interface NullCheckExpr {
|
|
94
|
+
readonly kind: 'nullCheck';
|
|
95
|
+
readonly expr: Expression;
|
|
96
|
+
readonly isNull: boolean;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface AndExpr {
|
|
100
|
+
readonly kind: 'and';
|
|
101
|
+
readonly exprs: ReadonlyArray<WhereExpr>;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface OrExpr {
|
|
105
|
+
readonly kind: 'or';
|
|
106
|
+
readonly exprs: ReadonlyArray<WhereExpr>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Union type for WHERE clause expressions.
|
|
111
|
+
*/
|
|
112
|
+
export type WhereExpr = BinaryExpr | ExistsExpr | NullCheckExpr | AndExpr | OrExpr;
|
|
113
|
+
|
|
114
|
+
export type JoinOnExpr = {
|
|
115
|
+
readonly kind: 'eqCol';
|
|
116
|
+
readonly left: ColumnRef;
|
|
117
|
+
readonly right: ColumnRef;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export interface JoinAst {
|
|
121
|
+
readonly kind: 'join';
|
|
122
|
+
readonly joinType: 'inner' | 'left' | 'right' | 'full';
|
|
123
|
+
readonly table: TableRef;
|
|
124
|
+
readonly on: JoinOnExpr;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface IncludeRef {
|
|
128
|
+
readonly kind: 'includeRef';
|
|
129
|
+
readonly alias: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface IncludeAst {
|
|
133
|
+
readonly kind: 'includeMany';
|
|
134
|
+
readonly alias: string;
|
|
135
|
+
readonly child: {
|
|
136
|
+
readonly table: TableRef;
|
|
137
|
+
readonly on: JoinOnExpr;
|
|
138
|
+
readonly where?: WhereExpr;
|
|
139
|
+
readonly orderBy?: ReadonlyArray<{ expr: Expression; dir: Direction }>;
|
|
140
|
+
readonly limit?: number;
|
|
141
|
+
readonly project: ReadonlyArray<{ alias: string; expr: Expression }>;
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface SelectAst {
|
|
146
|
+
readonly kind: 'select';
|
|
147
|
+
readonly from: TableRef;
|
|
148
|
+
readonly joins?: ReadonlyArray<JoinAst>;
|
|
149
|
+
readonly includes?: ReadonlyArray<IncludeAst>;
|
|
150
|
+
readonly project: ReadonlyArray<{
|
|
151
|
+
alias: string;
|
|
152
|
+
expr: Expression | IncludeRef | LiteralExpr;
|
|
153
|
+
}>;
|
|
154
|
+
readonly where?: WhereExpr;
|
|
155
|
+
readonly orderBy?: ReadonlyArray<{ expr: Expression; dir: Direction }>;
|
|
156
|
+
readonly limit?: number;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface InsertAst {
|
|
160
|
+
readonly kind: 'insert';
|
|
161
|
+
readonly table: TableRef;
|
|
162
|
+
readonly values: Record<string, ColumnRef | ParamRef>;
|
|
163
|
+
readonly returning?: ReadonlyArray<ColumnRef>;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface UpdateAst {
|
|
167
|
+
readonly kind: 'update';
|
|
168
|
+
readonly table: TableRef;
|
|
169
|
+
readonly set: Record<string, ColumnRef | ParamRef>;
|
|
170
|
+
readonly where: WhereExpr;
|
|
171
|
+
readonly returning?: ReadonlyArray<ColumnRef>;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface DeleteAst {
|
|
175
|
+
readonly kind: 'delete';
|
|
176
|
+
readonly table: TableRef;
|
|
177
|
+
readonly where: WhereExpr;
|
|
178
|
+
readonly returning?: ReadonlyArray<ColumnRef>;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export type QueryAst = SelectAst | InsertAst | UpdateAst | DeleteAst;
|
|
182
|
+
|
|
183
|
+
export interface LoweredStatement {
|
|
184
|
+
readonly sql: string;
|
|
185
|
+
readonly params: readonly unknown[];
|
|
186
|
+
readonly annotations?: Record<string, unknown>;
|
|
187
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ColumnRef, ParamRef, TableRef, UpdateAst, WhereExpr } from './types';
|
|
2
|
+
import { compact } from './util';
|
|
3
|
+
|
|
4
|
+
export interface CreateUpdateAstOptions {
|
|
5
|
+
readonly table: TableRef;
|
|
6
|
+
readonly set: Record<string, ColumnRef | ParamRef>;
|
|
7
|
+
readonly where: WhereExpr;
|
|
8
|
+
readonly returning?: ReadonlyArray<ColumnRef>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createUpdateAst(options: CreateUpdateAstOptions): UpdateAst {
|
|
12
|
+
return compact({
|
|
13
|
+
kind: 'update',
|
|
14
|
+
table: options.table,
|
|
15
|
+
set: options.set,
|
|
16
|
+
where: options.where,
|
|
17
|
+
returning: options.returning,
|
|
18
|
+
}) as UpdateAst;
|
|
19
|
+
}
|
package/src/ast/util.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function compact<T extends Record<string, unknown>>(o: T): T {
|
|
2
|
+
const out: Record<string, unknown> = {};
|
|
3
|
+
for (const [k, v] of Object.entries(o)) {
|
|
4
|
+
if (v === undefined || v === null) continue;
|
|
5
|
+
if (Array.isArray(v) && v.length === 0) continue;
|
|
6
|
+
out[k] = v;
|
|
7
|
+
}
|
|
8
|
+
return out as T;
|
|
9
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * from '../ast/adapter-types';
|
|
2
|
+
export * from '../ast/codec-types';
|
|
3
|
+
export * from '../ast/common';
|
|
4
|
+
export * from '../ast/delete';
|
|
5
|
+
export * from '../ast/driver-types';
|
|
6
|
+
export * from '../ast/insert';
|
|
7
|
+
export * from '../ast/join';
|
|
8
|
+
export * from '../ast/order';
|
|
9
|
+
export * from '../ast/predicate';
|
|
10
|
+
export * from '../ast/select';
|
|
11
|
+
export * from '../ast/sql-codecs';
|
|
12
|
+
export * from '../ast/types';
|
|
13
|
+
export * from '../ast/update';
|
|
14
|
+
export * from '../ast/util';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { planInvalid, planUnsupported } from '../errors';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export {
|
|
2
|
+
collectColumnRefs,
|
|
3
|
+
expressionFromSource,
|
|
4
|
+
extractBaseColumnRef,
|
|
5
|
+
getColumnInfo,
|
|
6
|
+
getColumnMeta,
|
|
7
|
+
getOperationExpr,
|
|
8
|
+
isColumnBuilder,
|
|
9
|
+
isExpressionBuilder,
|
|
10
|
+
isExpressionSource,
|
|
11
|
+
isOperationExpr,
|
|
12
|
+
isParamPlaceholder,
|
|
13
|
+
isValueSource,
|
|
14
|
+
toExpression,
|
|
15
|
+
} from '../utils/guards';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { attachOperationsToColumnBuilder } from '../operations-registry';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../plan';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../query-lane-context';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../types';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../guards';
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './exports/ast';
|
|
2
|
+
export * from './exports/errors';
|
|
3
|
+
export * from './exports/operations-registry';
|
|
4
|
+
export * from './exports/param';
|
|
5
|
+
export * from './exports/plan';
|
|
6
|
+
export * from './exports/query-lane-context';
|
|
7
|
+
export * from './exports/schema';
|
|
8
|
+
export * from './exports/types';
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import type { OperationRegistry } from '@prisma-next/operations';
|
|
2
|
+
import { hasAllCapabilities } from '@prisma-next/operations';
|
|
3
|
+
import { planInvalid } from '@prisma-next/plan';
|
|
4
|
+
import type { StorageColumn } from '@prisma-next/sql-contract/types';
|
|
5
|
+
import type { SqlOperationSignature } from '@prisma-next/sql-operations';
|
|
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';
|
|
22
|
+
import { isParamPlaceholder } from './utils/guards';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Type guard to check if a value is an ExpressionSource (has toExpr method).
|
|
26
|
+
*/
|
|
27
|
+
function isExpressionSource(value: unknown): value is ExpressionSource {
|
|
28
|
+
return (
|
|
29
|
+
typeof value === 'object' &&
|
|
30
|
+
value !== null &&
|
|
31
|
+
'toExpr' in value &&
|
|
32
|
+
typeof (value as ExpressionSource).toExpr === 'function'
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Executes an operation and returns an ExpressionBuilder.
|
|
38
|
+
* This is the canonical entrypoint for operation invocation, enabling
|
|
39
|
+
* future enhancements like telemetry, caching, or tracing.
|
|
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
|
+
*
|
|
47
|
+
* @param signature - The operation signature from the registry
|
|
48
|
+
* @param selfBuilder - The expression source that the operation is called on
|
|
49
|
+
* @param args - The arguments passed to the operation
|
|
50
|
+
* @param columnMeta - The metadata of the column the operation is called on
|
|
51
|
+
* @returns An ExpressionBuilder containing the operation expression
|
|
52
|
+
*/
|
|
53
|
+
function executeOperation(
|
|
54
|
+
signature: SqlOperationSignature,
|
|
55
|
+
selfBuilder: ExpressionSource,
|
|
56
|
+
args: unknown[],
|
|
57
|
+
columnMeta: StorageColumn,
|
|
58
|
+
operationRegistry?: OperationRegistry,
|
|
59
|
+
contractCapabilities?: Record<string, Record<string, boolean>>,
|
|
60
|
+
): ExpressionBuilder {
|
|
61
|
+
if (args.length !== signature.args.length) {
|
|
62
|
+
throw planInvalid(
|
|
63
|
+
`Operation ${signature.method} expects ${signature.args.length} arguments, got ${args.length}`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Get the Expression from the self builder using toExpr()
|
|
68
|
+
const selfExpr: Expression = selfBuilder.toExpr();
|
|
69
|
+
|
|
70
|
+
const operationArgs: Array<Expression | ParamRef | LiteralExpr> = [];
|
|
71
|
+
for (let i = 0; i < args.length; i++) {
|
|
72
|
+
const arg = args[i];
|
|
73
|
+
const argSpec = signature.args[i];
|
|
74
|
+
if (!argSpec) {
|
|
75
|
+
throw planInvalid(`Missing argument spec for argument ${i}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (argSpec.kind === 'param') {
|
|
79
|
+
if (!isParamPlaceholder(arg)) {
|
|
80
|
+
throw planInvalid(`Argument ${i} must be a parameter placeholder`);
|
|
81
|
+
}
|
|
82
|
+
operationArgs.push({
|
|
83
|
+
kind: 'param',
|
|
84
|
+
index: 0,
|
|
85
|
+
name: arg.name,
|
|
86
|
+
});
|
|
87
|
+
} else if (argSpec.kind === 'typeId') {
|
|
88
|
+
// Accept ExpressionSource (ColumnBuilder or ExpressionBuilder)
|
|
89
|
+
if (!isExpressionSource(arg)) {
|
|
90
|
+
throw planInvalid(
|
|
91
|
+
`Argument ${i} must be an ExpressionSource (ColumnBuilder or ExpressionBuilder)`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
// Use toExpr() to get the Expression
|
|
95
|
+
operationArgs.push(arg.toExpr());
|
|
96
|
+
} else if (argSpec.kind === 'literal') {
|
|
97
|
+
operationArgs.push({
|
|
98
|
+
kind: 'literal',
|
|
99
|
+
value: arg,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const operationExpr: OperationExpr = {
|
|
105
|
+
kind: 'operation',
|
|
106
|
+
method: signature.method,
|
|
107
|
+
forTypeId: signature.forTypeId,
|
|
108
|
+
self: selfExpr,
|
|
109
|
+
args: operationArgs,
|
|
110
|
+
returns: signature.returns,
|
|
111
|
+
lowering: signature.lowering,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const returnTypeId = signature.returns.kind === 'typeId' ? signature.returns.type : undefined;
|
|
115
|
+
const returnColumnMeta: StorageColumn = returnTypeId
|
|
116
|
+
? {
|
|
117
|
+
...columnMeta,
|
|
118
|
+
codecId: returnTypeId,
|
|
119
|
+
}
|
|
120
|
+
: columnMeta;
|
|
121
|
+
|
|
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,
|
|
135
|
+
get columnMeta() {
|
|
136
|
+
return returnColumnMeta;
|
|
137
|
+
},
|
|
138
|
+
eq: createComparisonMethod('eq'),
|
|
139
|
+
neq: createComparisonMethod('neq'),
|
|
140
|
+
gt: createComparisonMethod('gt'),
|
|
141
|
+
lt: createComparisonMethod('lt'),
|
|
142
|
+
gte: createComparisonMethod('gte'),
|
|
143
|
+
lte: createComparisonMethod('lte'),
|
|
144
|
+
asc(): AnyOrderBuilder {
|
|
145
|
+
return Object.freeze({
|
|
146
|
+
kind: 'order' as const,
|
|
147
|
+
expr: operationExpr,
|
|
148
|
+
dir: 'asc' as const,
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
desc(): AnyOrderBuilder {
|
|
152
|
+
return Object.freeze({
|
|
153
|
+
kind: 'order' as const,
|
|
154
|
+
expr: operationExpr,
|
|
155
|
+
dir: 'desc' as const,
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
toExpr(): OperationExpr {
|
|
159
|
+
return operationExpr;
|
|
160
|
+
},
|
|
161
|
+
get __jsType(): unknown {
|
|
162
|
+
return undefined;
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// If the return type is a typeId, attach operations for that type
|
|
167
|
+
if (returnTypeId && operationRegistry) {
|
|
168
|
+
const resultWithOps = attachOperationsToExpressionBuilder(
|
|
169
|
+
baseResult,
|
|
170
|
+
returnColumnMeta,
|
|
171
|
+
operationRegistry,
|
|
172
|
+
contractCapabilities,
|
|
173
|
+
);
|
|
174
|
+
return Object.freeze(resultWithOps);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return Object.freeze(baseResult);
|
|
178
|
+
}
|
|
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
|
+
|
|
222
|
+
export function attachOperationsToColumnBuilder<
|
|
223
|
+
ColumnName extends string,
|
|
224
|
+
ColumnMeta extends StorageColumn,
|
|
225
|
+
JsType = unknown,
|
|
226
|
+
Operations extends OperationTypes = Record<string, never>,
|
|
227
|
+
>(
|
|
228
|
+
columnBuilder: ColumnBuilder<ColumnName, ColumnMeta, JsType, Record<string, never>>,
|
|
229
|
+
columnMeta: ColumnMeta,
|
|
230
|
+
registry: OperationRegistry | undefined,
|
|
231
|
+
contractCapabilities?: Record<string, Record<string, boolean>>,
|
|
232
|
+
): ColumnBuilder<ColumnName, ColumnMeta, JsType, Operations> {
|
|
233
|
+
if (!registry) {
|
|
234
|
+
return columnBuilder as ColumnBuilder<ColumnName, ColumnMeta, JsType, Operations>;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Use codecId to look up operations registered for this column's type
|
|
238
|
+
const codecId = columnMeta.codecId;
|
|
239
|
+
if (!codecId) {
|
|
240
|
+
return columnBuilder as ColumnBuilder<ColumnName, ColumnMeta, JsType, Operations>;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const operations = registry.byType(codecId) as SqlOperationSignature[];
|
|
244
|
+
if (operations.length === 0) {
|
|
245
|
+
return columnBuilder as ColumnBuilder<ColumnName, ColumnMeta, JsType, Operations>;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const builderWithOps = columnBuilder as unknown as ColumnBuilder<
|
|
249
|
+
ColumnName,
|
|
250
|
+
ColumnMeta,
|
|
251
|
+
JsType,
|
|
252
|
+
Operations
|
|
253
|
+
>;
|
|
254
|
+
|
|
255
|
+
for (const operation of operations) {
|
|
256
|
+
if (operation.capabilities && operation.capabilities.length > 0) {
|
|
257
|
+
if (!contractCapabilities) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (!hasAllCapabilities(operation.capabilities, contractCapabilities)) {
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Method sugar: attach operation as a method on the column builder
|
|
266
|
+
// Operations return ExpressionBuilder, not ColumnBuilder
|
|
267
|
+
(builderWithOps as Record<string, unknown>)[operation.method] = function (
|
|
268
|
+
this: ColumnBuilder<ColumnName, ColumnMeta, JsType, Record<string, never>>,
|
|
269
|
+
...args: unknown[]
|
|
270
|
+
) {
|
|
271
|
+
return executeOperation(operation, this, args, columnMeta, registry, contractCapabilities);
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return builderWithOps;
|
|
276
|
+
}
|
package/src/param.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { planInvalid } from '@prisma-next/plan';
|
|
2
|
+
import type { ParamPlaceholder } from './types';
|
|
3
|
+
|
|
4
|
+
export type Parameter = ParamPlaceholder;
|
|
5
|
+
|
|
6
|
+
export function param(name: string): Parameter {
|
|
7
|
+
if (typeof name !== 'string' || name.length === 0) {
|
|
8
|
+
throw planInvalid('Parameter name must be a non-empty string');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return Object.freeze({
|
|
12
|
+
kind: 'param-placeholder' as const,
|
|
13
|
+
name,
|
|
14
|
+
}) satisfies Parameter;
|
|
15
|
+
}
|
package/src/plan.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ExecutionPlan, ParamDescriptor } from '@prisma-next/contract/types';
|
|
2
|
+
import type { StorageColumn } from '@prisma-next/sql-contract/types';
|
|
3
|
+
import type { QueryAst } from './ast/types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SQL query plan produced by lanes before lowering.
|
|
7
|
+
*
|
|
8
|
+
* Lanes build ASTs and metadata but do not perform SQL lowering.
|
|
9
|
+
* The `sql` field is absent - lowering happens in the runtime executor.
|
|
10
|
+
*
|
|
11
|
+
* Structurally aligns with ExecutionPlan<Row, QueryAst> (without sql field) to maintain
|
|
12
|
+
* compatibility with ExecutionPlan/Plan-based utilities.
|
|
13
|
+
* The generic parameter `_Row` is preserved for type extraction via ResultType.
|
|
14
|
+
*/
|
|
15
|
+
export interface SqlQueryPlan<_Row = unknown>
|
|
16
|
+
extends Pick<ExecutionPlan<_Row, QueryAst>, 'params' | 'meta'> {
|
|
17
|
+
readonly ast: QueryAst;
|
|
18
|
+
// Phantom property to preserve generic parameter for type extraction
|
|
19
|
+
// This allows ResultType to extract _Row for SqlQueryPlan values.
|
|
20
|
+
readonly _Row?: _Row;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Augments the last ParamDescriptor in the array with codecId and nativeType from columnMeta.
|
|
25
|
+
* This is used when building WHERE expressions to ensure param descriptors have type information.
|
|
26
|
+
*/
|
|
27
|
+
export function augmentDescriptorWithColumnMeta(
|
|
28
|
+
descriptors: ParamDescriptor[],
|
|
29
|
+
columnMeta: StorageColumn | undefined,
|
|
30
|
+
): void {
|
|
31
|
+
const descriptor = descriptors[descriptors.length - 1];
|
|
32
|
+
if (descriptor && columnMeta) {
|
|
33
|
+
descriptors[descriptors.length - 1] = {
|
|
34
|
+
...descriptor,
|
|
35
|
+
codecId: columnMeta.codecId,
|
|
36
|
+
nativeType: columnMeta.nativeType,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|