@prisma-next/sql-lane 0.3.0-dev.4 → 0.3.0-dev.5

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 (55) hide show
  1. package/dist/exports/sql.d.ts +5 -5
  2. package/dist/exports/sql.d.ts.map +1 -0
  3. package/dist/index.d.ts +5 -116
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/raw.d.ts +11 -0
  6. package/dist/raw.d.ts.map +1 -0
  7. package/dist/sql/builder.d.ts +11 -0
  8. package/dist/sql/builder.d.ts.map +1 -0
  9. package/dist/sql/context.d.ts +5 -0
  10. package/dist/sql/context.d.ts.map +1 -0
  11. package/dist/sql/include-builder.d.ts +35 -0
  12. package/dist/sql/include-builder.d.ts.map +1 -0
  13. package/dist/sql/join-builder.d.ts +4 -0
  14. package/dist/sql/join-builder.d.ts.map +1 -0
  15. package/dist/sql/mutation-builder.d.ts +64 -0
  16. package/dist/sql/mutation-builder.d.ts.map +1 -0
  17. package/dist/sql/plan.d.ts +4 -0
  18. package/dist/sql/plan.d.ts.map +1 -0
  19. package/dist/sql/predicate-builder.d.ts +11 -0
  20. package/dist/sql/predicate-builder.d.ts.map +1 -0
  21. package/dist/sql/projection.d.ts +18 -0
  22. package/dist/sql/projection.d.ts.map +1 -0
  23. package/dist/sql/select-builder.d.ts +35 -0
  24. package/dist/sql/select-builder.d.ts.map +1 -0
  25. package/dist/types/internal.d.ts +35 -0
  26. package/dist/types/internal.d.ts.map +1 -0
  27. package/dist/types/public.d.ts +18 -0
  28. package/dist/types/public.d.ts.map +1 -0
  29. package/dist/utils/assertions.d.ts +28 -0
  30. package/dist/utils/assertions.d.ts.map +1 -0
  31. package/dist/utils/capabilities.d.ts +4 -0
  32. package/dist/utils/capabilities.d.ts.map +1 -0
  33. package/dist/utils/errors.d.ts +30 -0
  34. package/dist/utils/errors.d.ts.map +1 -0
  35. package/dist/utils/state.d.ts +30 -0
  36. package/dist/utils/state.d.ts.map +1 -0
  37. package/package.json +10 -9
  38. package/src/exports/sql.ts +12 -0
  39. package/src/index.ts +13 -0
  40. package/src/raw.ts +230 -0
  41. package/src/sql/builder.ts +66 -0
  42. package/src/sql/context.ts +10 -0
  43. package/src/sql/include-builder.ts +248 -0
  44. package/src/sql/join-builder.ts +18 -0
  45. package/src/sql/mutation-builder.ts +494 -0
  46. package/src/sql/plan.ts +289 -0
  47. package/src/sql/predicate-builder.ts +130 -0
  48. package/src/sql/projection.ts +112 -0
  49. package/src/sql/select-builder.ts +449 -0
  50. package/src/types/internal.ts +41 -0
  51. package/src/types/public.ts +36 -0
  52. package/src/utils/assertions.ts +34 -0
  53. package/src/utils/capabilities.ts +39 -0
  54. package/src/utils/errors.ts +168 -0
  55. package/src/utils/state.ts +40 -0
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@prisma-next/sql-lane",
3
- "version": "0.3.0-dev.4",
3
+ "version": "0.3.0-dev.5",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Relational DSL and raw SQL helpers for Prisma Next",
7
7
  "dependencies": {
8
- "@prisma-next/contract": "0.3.0-dev.4",
9
- "@prisma-next/plan": "0.3.0-dev.4",
10
- "@prisma-next/sql-contract": "0.3.0-dev.4",
11
- "@prisma-next/sql-relational-core": "0.3.0-dev.4"
8
+ "@prisma-next/plan": "0.3.0-dev.5",
9
+ "@prisma-next/sql-contract": "0.3.0-dev.5",
10
+ "@prisma-next/sql-relational-core": "0.3.0-dev.5",
11
+ "@prisma-next/contract": "0.3.0-dev.5"
12
12
  },
13
13
  "devDependencies": {
14
14
  "@types/pg": "8.16.0",
@@ -17,12 +17,13 @@
17
17
  "tsup": "8.5.1",
18
18
  "typescript": "5.9.3",
19
19
  "vitest": "4.0.16",
20
- "@prisma-next/sql-contract-ts": "0.3.0-dev.4",
21
- "@prisma-next/sql-runtime": "0.3.0-dev.4",
20
+ "@prisma-next/sql-contract-ts": "0.3.0-dev.5",
21
+ "@prisma-next/sql-runtime": "0.3.0-dev.5",
22
22
  "@prisma-next/test-utils": "0.0.1"
23
23
  },
24
24
  "files": [
25
- "dist"
25
+ "dist",
26
+ "src"
26
27
  ],
27
28
  "exports": {
28
29
  ".": {
@@ -35,7 +36,7 @@
35
36
  }
36
37
  },
37
38
  "scripts": {
38
- "build": "tsup --config tsup.config.ts",
39
+ "build": "tsup --config tsup.config.ts && tsc --project tsconfig.build.json",
39
40
  "test": "vitest run",
40
41
  "test:coverage": "vitest run --coverage",
41
42
  "typecheck": "tsc --project tsconfig.json --noEmit",
@@ -0,0 +1,12 @@
1
+ export type {
2
+ ColumnsOf,
3
+ RawFactory,
4
+ RawFunctionOptions,
5
+ RawTemplateOptions,
6
+ SqlBuilderOptions,
7
+ TableKey,
8
+ TablesOf,
9
+ } from '@prisma-next/sql-relational-core/types';
10
+ export { rawOptions } from '../raw';
11
+ export type { SelectBuilder } from '../sql/builder';
12
+ export { createJoinOnBuilder, sql } from '../sql/builder';
package/src/index.ts ADDED
@@ -0,0 +1,13 @@
1
+ export type {
2
+ ColumnsOf,
3
+ JoinOnBuilder,
4
+ RawFactory,
5
+ RawFunctionOptions,
6
+ RawTemplateOptions,
7
+ SqlBuilderOptions,
8
+ TableKey,
9
+ TablesOf,
10
+ } from '@prisma-next/sql-relational-core/types';
11
+ export { rawOptions } from './raw';
12
+ export type { IncludeChildBuilder, SelectBuilder } from './sql/builder';
13
+ export { createJoinOnBuilder, sql } from './sql/builder';
package/src/raw.ts ADDED
@@ -0,0 +1,230 @@
1
+ import type {
2
+ ExecutionPlan,
3
+ ParamDescriptor,
4
+ PlanMeta,
5
+ PlanRefs,
6
+ } from '@prisma-next/contract/types';
7
+ import { planInvalid } from '@prisma-next/plan';
8
+ import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
9
+ import type {
10
+ RawFactory,
11
+ RawFunctionOptions,
12
+ RawTemplateFactory,
13
+ RawTemplateOptions,
14
+ } from '@prisma-next/sql-relational-core/types';
15
+
16
+ const RAW_OPTIONS_SENTINEL = Symbol('rawOptions');
17
+
18
+ type TemplateInvocation = {
19
+ readonly sql: string;
20
+ readonly params: readonly unknown[];
21
+ readonly paramDescriptors: ReadonlyArray<ParamDescriptor>;
22
+ };
23
+
24
+ interface RawPlanBuildArgs {
25
+ readonly contract: SqlContract<SqlStorage>;
26
+ readonly sql: string;
27
+ readonly params: readonly unknown[];
28
+ readonly paramDescriptors: ReadonlyArray<ParamDescriptor>;
29
+ readonly options?: RawTemplateOptions;
30
+ }
31
+
32
+ export function createRawFactory(contract: SqlContract<SqlStorage>): RawFactory {
33
+ const factory = ((first: TemplateStringsArray | string, ...rest: unknown[]) => {
34
+ if (isTemplateInvocation(first)) {
35
+ const { values, options } = splitTemplateValues(rest);
36
+ const compiled = compileTemplateToPositional(first, values);
37
+ return buildRawPlan({
38
+ contract,
39
+ sql: compiled.sql,
40
+ params: compiled.params,
41
+ paramDescriptors: compiled.paramDescriptors,
42
+ ...(options ? { options } : {}),
43
+ });
44
+ }
45
+
46
+ const text = first;
47
+ const [options] = rest as [RawFunctionOptions | undefined];
48
+
49
+ if (!options) {
50
+ throw planInvalid('Function form requires params option');
51
+ }
52
+
53
+ if (!Array.isArray(options.params)) {
54
+ throw planInvalid('Function form params must be an array');
55
+ }
56
+
57
+ const paramDescriptors = buildSequentialDescriptors(options.params.length);
58
+
59
+ return buildRawPlan({
60
+ contract,
61
+ sql: text,
62
+ params: options.params,
63
+ paramDescriptors,
64
+ options,
65
+ });
66
+ }) as RawFactory;
67
+
68
+ factory.with = (options: RawTemplateOptions) => {
69
+ return ((strings: TemplateStringsArray, ...values: readonly unknown[]) => {
70
+ const compiled = compileTemplateToPositional(strings, values);
71
+ return buildRawPlan({
72
+ contract,
73
+ sql: compiled.sql,
74
+ params: compiled.params,
75
+ paramDescriptors: compiled.paramDescriptors,
76
+ options,
77
+ });
78
+ }) as RawTemplateFactory;
79
+ };
80
+
81
+ return factory;
82
+ }
83
+
84
+ function compileTemplateToPositional(
85
+ strings: TemplateStringsArray,
86
+ values: readonly unknown[],
87
+ ): TemplateInvocation {
88
+ let sql = '';
89
+ const params: unknown[] = [];
90
+ const paramDescriptors: ParamDescriptor[] = [];
91
+
92
+ strings.forEach((part, index) => {
93
+ sql += part;
94
+
95
+ if (index < values.length) {
96
+ const value = values[index];
97
+ const placeholderIndex = params.push(value);
98
+ sql += `$${placeholderIndex}`;
99
+ paramDescriptors.push({
100
+ index: placeholderIndex,
101
+ name: `p${placeholderIndex}`,
102
+ source: 'raw',
103
+ });
104
+ }
105
+ });
106
+
107
+ return {
108
+ sql,
109
+ params,
110
+ paramDescriptors,
111
+ };
112
+ }
113
+
114
+ function buildRawPlan(args: RawPlanBuildArgs): ExecutionPlan {
115
+ const params = Array.from(args.params);
116
+ const descriptors = args.paramDescriptors.map((descriptor) =>
117
+ Object.freeze({ ...descriptor, source: 'raw' as const }),
118
+ );
119
+
120
+ const meta = buildRawMeta({
121
+ contract: args.contract,
122
+ paramDescriptors: descriptors,
123
+ ...(args.options ? { options: args.options } : {}),
124
+ });
125
+
126
+ return Object.freeze({
127
+ sql: args.sql,
128
+ params: Object.freeze(params),
129
+ meta,
130
+ });
131
+ }
132
+
133
+ interface RawMetaBuildArgs {
134
+ readonly contract: SqlContract<SqlStorage>;
135
+ readonly paramDescriptors: ReadonlyArray<ParamDescriptor>;
136
+ readonly options?: RawTemplateOptions;
137
+ }
138
+
139
+ function buildRawMeta(args: RawMetaBuildArgs): PlanMeta {
140
+ const { contract, paramDescriptors, options } = args;
141
+
142
+ const meta: PlanMeta = {
143
+ target: contract.target,
144
+ targetFamily: contract.targetFamily,
145
+ coreHash: contract.coreHash,
146
+ ...(contract.profileHash !== undefined ? { profileHash: contract.profileHash } : {}),
147
+ lane: 'raw',
148
+ paramDescriptors: Object.freeze([...paramDescriptors]),
149
+ ...(options?.annotations ? { annotations: Object.freeze({ ...options.annotations }) } : {}),
150
+ ...(options?.refs ? { refs: freezeRefs(options.refs) } : {}),
151
+ ...(options?.projection ? { projection: Object.freeze([...options.projection]) } : {}),
152
+ };
153
+
154
+ return Object.freeze(meta);
155
+ }
156
+
157
+ function freezeRefs(refs: PlanRefs): PlanRefs {
158
+ return Object.freeze({
159
+ ...(refs.tables ? { tables: Object.freeze([...refs.tables]) } : {}),
160
+ ...(refs.columns
161
+ ? {
162
+ columns: Object.freeze(
163
+ refs.columns.map((col: { table: string; column: string }) => Object.freeze({ ...col })),
164
+ ),
165
+ }
166
+ : {}),
167
+ ...(refs.indexes
168
+ ? {
169
+ indexes: Object.freeze(
170
+ refs.indexes.map(
171
+ (index: { table: string; columns: ReadonlyArray<string>; name?: string }) =>
172
+ Object.freeze({
173
+ ...index,
174
+ columns: Object.freeze([...index.columns]),
175
+ }),
176
+ ),
177
+ ),
178
+ }
179
+ : {}),
180
+ });
181
+ }
182
+
183
+ function buildSequentialDescriptors(count: number): ReadonlyArray<ParamDescriptor> {
184
+ return Array.from({ length: count }, (_, idx) =>
185
+ Object.freeze({
186
+ index: idx + 1,
187
+ name: `p${idx + 1}`,
188
+ source: 'raw' as const,
189
+ }),
190
+ );
191
+ }
192
+
193
+ function isTemplateInvocation(value: unknown): value is TemplateStringsArray {
194
+ return Array.isArray(value) && Object.hasOwn(value, 'raw');
195
+ }
196
+
197
+ interface RawTemplateOptionsSentinel {
198
+ readonly [RAW_OPTIONS_SENTINEL]: true;
199
+ readonly value: RawTemplateOptions;
200
+ }
201
+
202
+ export function rawOptions(options: RawTemplateOptions): RawTemplateOptionsSentinel {
203
+ return Object.freeze({
204
+ [RAW_OPTIONS_SENTINEL]: true as const,
205
+ value: options,
206
+ });
207
+ }
208
+
209
+ function splitTemplateValues(values: readonly unknown[]): {
210
+ readonly values: readonly unknown[];
211
+ readonly options?: RawTemplateOptions;
212
+ } {
213
+ if (values.length === 0) {
214
+ return { values };
215
+ }
216
+
217
+ const last = values[values.length - 1];
218
+ if (!isOptionsSentinel(last)) {
219
+ return { values };
220
+ }
221
+
222
+ return {
223
+ values: values.slice(0, values.length - 1),
224
+ options: last.value,
225
+ };
226
+ }
227
+
228
+ function isOptionsSentinel(value: unknown): value is RawTemplateOptionsSentinel {
229
+ return typeof value === 'object' && value !== null && RAW_OPTIONS_SENTINEL in value;
230
+ }
@@ -0,0 +1,66 @@
1
+ import type {
2
+ ExtractCodecTypes,
3
+ ExtractOperationTypes,
4
+ SqlContract,
5
+ SqlStorage,
6
+ } from '@prisma-next/sql-contract/types';
7
+ import type { TableRef } from '@prisma-next/sql-relational-core/ast';
8
+ import { createJoinOnBuilder } from '@prisma-next/sql-relational-core/ast';
9
+ import type { ParamPlaceholder, SqlBuilderOptions } from '@prisma-next/sql-relational-core/types';
10
+ import { createRawFactory } from '../raw';
11
+ import type { SelectBuilder } from '../types/public';
12
+ import { DeleteBuilderImpl, InsertBuilderImpl, UpdateBuilderImpl } from './mutation-builder';
13
+ import { SelectBuilderImpl } from './select-builder';
14
+
15
+ export { createJoinOnBuilder };
16
+ export type { DeleteBuilder, InsertBuilder, SelectBuilder, UpdateBuilder } from '../types/public';
17
+ export type { IncludeChildBuilder } from './include-builder';
18
+
19
+ export function sql<
20
+ TContract extends SqlContract<SqlStorage>,
21
+ CodecTypesOverride extends Record<
22
+ string,
23
+ { readonly output: unknown }
24
+ > = ExtractCodecTypes<TContract>,
25
+ >(
26
+ options: SqlBuilderOptions<TContract>,
27
+ ): SelectBuilder<TContract, unknown, CodecTypesOverride, ExtractOperationTypes<TContract>> {
28
+ type CodecTypes = CodecTypesOverride;
29
+ type Operations = ExtractOperationTypes<TContract>;
30
+ const builder = new SelectBuilderImpl<TContract, unknown, CodecTypes, Record<string, never>>(
31
+ options,
32
+ ) as SelectBuilder<TContract, unknown, CodecTypes, Operations>;
33
+ const rawFactory = createRawFactory(options.context.contract);
34
+
35
+ Object.defineProperty(builder, 'raw', {
36
+ value: rawFactory,
37
+ enumerable: true,
38
+ configurable: false,
39
+ });
40
+
41
+ Object.defineProperty(builder, 'insert', {
42
+ value: (table: TableRef, values: Record<string, ParamPlaceholder>) => {
43
+ return new InsertBuilderImpl<TContract, CodecTypes>(options, table, values);
44
+ },
45
+ enumerable: true,
46
+ configurable: false,
47
+ });
48
+
49
+ Object.defineProperty(builder, 'update', {
50
+ value: (table: TableRef, set: Record<string, ParamPlaceholder>) => {
51
+ return new UpdateBuilderImpl<TContract, CodecTypes>(options, table, set);
52
+ },
53
+ enumerable: true,
54
+ configurable: false,
55
+ });
56
+
57
+ Object.defineProperty(builder, 'delete', {
58
+ value: (table: TableRef) => {
59
+ return new DeleteBuilderImpl<TContract, CodecTypes>(options, table);
60
+ },
61
+ enumerable: true,
62
+ configurable: false,
63
+ });
64
+
65
+ return builder;
66
+ }
@@ -0,0 +1,10 @@
1
+ import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
2
+ import type { QueryLaneContext } from '@prisma-next/sql-relational-core/query-lane-context';
3
+
4
+ export type SqlContext<TContract extends SqlContract<SqlStorage>> = QueryLaneContext<TContract>;
5
+
6
+ export function createSqlContext<TContract extends SqlContract<SqlStorage>>(
7
+ context: QueryLaneContext<TContract>,
8
+ ): SqlContext<TContract> {
9
+ return context;
10
+ }
@@ -0,0 +1,248 @@
1
+ import type { ParamDescriptor } from '@prisma-next/contract/types';
2
+ import type { SqlContract, SqlStorage, StorageColumn } from '@prisma-next/sql-contract/types';
3
+ import type {
4
+ BinaryExpr,
5
+ ColumnRef,
6
+ IncludeAst,
7
+ OperationExpr,
8
+ TableRef,
9
+ } from '@prisma-next/sql-relational-core/ast';
10
+ import {
11
+ createColumnRef,
12
+ createJoinOnExpr,
13
+ createOrderByItem,
14
+ createTableRef,
15
+ } from '@prisma-next/sql-relational-core/ast';
16
+ import type {
17
+ AnyBinaryBuilder,
18
+ AnyOrderBuilder,
19
+ BinaryBuilder,
20
+ CodecTypes as CodecTypesMap,
21
+ InferNestedProjectionRow,
22
+ NestedProjection,
23
+ OrderBuilder,
24
+ } from '@prisma-next/sql-relational-core/types';
25
+ import {
26
+ extractBaseColumnRef,
27
+ isOperationExpr,
28
+ } from '@prisma-next/sql-relational-core/utils/guards';
29
+ import {
30
+ errorChildProjectionMustBeSpecified,
31
+ errorLimitMustBeNonNegativeInteger,
32
+ errorMissingColumnForAlias,
33
+ } from '../utils/errors';
34
+ import type { IncludeState, ProjectionState } from '../utils/state';
35
+ import { buildWhereExpr } from './predicate-builder';
36
+ import { buildProjectionState } from './projection';
37
+
38
+ export interface IncludeChildBuilder<
39
+ TContract extends SqlContract<SqlStorage> = SqlContract<SqlStorage>,
40
+ CodecTypes extends Record<string, { readonly output: unknown }> = Record<string, never>,
41
+ ChildRow = unknown,
42
+ > {
43
+ select<P extends NestedProjection>(
44
+ projection: P,
45
+ ): IncludeChildBuilder<TContract, CodecTypes, InferNestedProjectionRow<P, CodecTypes>>;
46
+ where(expr: AnyBinaryBuilder): IncludeChildBuilder<TContract, CodecTypes, ChildRow>;
47
+ orderBy(order: AnyOrderBuilder): IncludeChildBuilder<TContract, CodecTypes, ChildRow>;
48
+ limit(count: number): IncludeChildBuilder<TContract, CodecTypes, ChildRow>;
49
+ }
50
+
51
+ export class IncludeChildBuilderImpl<
52
+ TContract extends SqlContract<SqlStorage> = SqlContract<SqlStorage>,
53
+ CodecTypes extends CodecTypesMap = CodecTypesMap,
54
+ ChildRow = unknown,
55
+ > implements IncludeChildBuilder<TContract, CodecTypes, ChildRow>
56
+ {
57
+ private readonly contract: TContract;
58
+ private readonly codecTypes: CodecTypes;
59
+ private readonly table: TableRef;
60
+ private childProjection?: ProjectionState;
61
+ private childWhere?: BinaryBuilder;
62
+ private childOrderBy?: OrderBuilder;
63
+ private childLimit?: number;
64
+
65
+ constructor(contract: TContract, codecTypes: CodecTypes, table: TableRef) {
66
+ this.contract = contract;
67
+ this.codecTypes = codecTypes;
68
+ this.table = table;
69
+ }
70
+
71
+ select<P extends NestedProjection>(
72
+ projection: P,
73
+ ): IncludeChildBuilderImpl<TContract, CodecTypes, InferNestedProjectionRow<P, CodecTypes>> {
74
+ const projectionState = buildProjectionState(this.table, projection);
75
+ const builder = new IncludeChildBuilderImpl<
76
+ TContract,
77
+ CodecTypes,
78
+ InferNestedProjectionRow<P, CodecTypes>
79
+ >(this.contract, this.codecTypes, this.table);
80
+ builder.childProjection = projectionState;
81
+ if (this.childWhere !== undefined) {
82
+ builder.childWhere = this.childWhere;
83
+ }
84
+ if (this.childOrderBy !== undefined) {
85
+ builder.childOrderBy = this.childOrderBy;
86
+ }
87
+ if (this.childLimit !== undefined) {
88
+ builder.childLimit = this.childLimit;
89
+ }
90
+ return builder;
91
+ }
92
+
93
+ where(expr: AnyBinaryBuilder): IncludeChildBuilderImpl<TContract, CodecTypes, ChildRow> {
94
+ const builder = new IncludeChildBuilderImpl<TContract, CodecTypes, ChildRow>(
95
+ this.contract,
96
+ this.codecTypes,
97
+ this.table,
98
+ );
99
+ if (this.childProjection !== undefined) {
100
+ builder.childProjection = this.childProjection;
101
+ }
102
+ builder.childWhere = expr;
103
+ if (this.childOrderBy !== undefined) {
104
+ builder.childOrderBy = this.childOrderBy;
105
+ }
106
+ if (this.childLimit !== undefined) {
107
+ builder.childLimit = this.childLimit;
108
+ }
109
+ return builder;
110
+ }
111
+
112
+ orderBy(order: AnyOrderBuilder): IncludeChildBuilderImpl<TContract, CodecTypes, ChildRow> {
113
+ const builder = new IncludeChildBuilderImpl<TContract, CodecTypes, ChildRow>(
114
+ this.contract,
115
+ this.codecTypes,
116
+ this.table,
117
+ );
118
+ if (this.childProjection !== undefined) {
119
+ builder.childProjection = this.childProjection;
120
+ }
121
+ if (this.childWhere !== undefined) {
122
+ builder.childWhere = this.childWhere;
123
+ }
124
+ builder.childOrderBy = order;
125
+ if (this.childLimit !== undefined) {
126
+ builder.childLimit = this.childLimit;
127
+ }
128
+ return builder;
129
+ }
130
+
131
+ limit(count: number): IncludeChildBuilderImpl<TContract, CodecTypes, ChildRow> {
132
+ if (!Number.isInteger(count) || count < 0) {
133
+ errorLimitMustBeNonNegativeInteger();
134
+ }
135
+
136
+ const builder = new IncludeChildBuilderImpl<TContract, CodecTypes, ChildRow>(
137
+ this.contract,
138
+ this.codecTypes,
139
+ this.table,
140
+ );
141
+ if (this.childProjection !== undefined) {
142
+ builder.childProjection = this.childProjection;
143
+ }
144
+ if (this.childWhere !== undefined) {
145
+ builder.childWhere = this.childWhere;
146
+ }
147
+ if (this.childOrderBy !== undefined) {
148
+ builder.childOrderBy = this.childOrderBy;
149
+ }
150
+ builder.childLimit = count;
151
+ return builder;
152
+ }
153
+
154
+ getState(): {
155
+ childProjection: ProjectionState;
156
+ childWhere?: AnyBinaryBuilder;
157
+ childOrderBy?: AnyOrderBuilder;
158
+ childLimit?: number;
159
+ } {
160
+ if (!this.childProjection) {
161
+ errorChildProjectionMustBeSpecified();
162
+ }
163
+ const state: {
164
+ childProjection: ProjectionState;
165
+ childWhere?: AnyBinaryBuilder;
166
+ childOrderBy?: AnyOrderBuilder;
167
+ childLimit?: number;
168
+ } = {
169
+ childProjection: this.childProjection,
170
+ };
171
+ if (this.childWhere !== undefined) {
172
+ state.childWhere = this.childWhere;
173
+ }
174
+ if (this.childOrderBy !== undefined) {
175
+ state.childOrderBy = this.childOrderBy;
176
+ }
177
+ if (this.childLimit !== undefined) {
178
+ state.childLimit = this.childLimit;
179
+ }
180
+ return state;
181
+ }
182
+ }
183
+
184
+ export function buildIncludeAst(
185
+ include: IncludeState,
186
+ contract: SqlContract<SqlStorage>,
187
+ paramsMap: Record<string, unknown>,
188
+ paramDescriptors: ParamDescriptor[],
189
+ paramValues: unknown[],
190
+ ): IncludeAst {
191
+ const childOrderBy = include.childOrderBy
192
+ ? (() => {
193
+ const orderBy = include.childOrderBy as OrderBuilder<string, StorageColumn, unknown>;
194
+ const orderExpr = orderBy.expr;
195
+ const expr: ColumnRef | OperationExpr = (() => {
196
+ if (isOperationExpr(orderExpr)) {
197
+ const baseCol = extractBaseColumnRef(orderExpr);
198
+ return createColumnRef(baseCol.table, baseCol.column);
199
+ }
200
+ // orderExpr is ColumnBuilder - TypeScript can't narrow properly
201
+ const colBuilder = orderExpr as { table: string; column: string };
202
+ return createColumnRef(colBuilder.table, colBuilder.column);
203
+ })();
204
+ return [createOrderByItem(expr, orderBy.dir)];
205
+ })()
206
+ : undefined;
207
+
208
+ let childWhere: BinaryExpr | undefined;
209
+ if (include.childWhere) {
210
+ const whereResult = buildWhereExpr(
211
+ contract,
212
+ include.childWhere,
213
+ paramsMap,
214
+ paramDescriptors,
215
+ paramValues,
216
+ );
217
+ childWhere = whereResult.expr;
218
+ }
219
+
220
+ const onLeft = include.on.left as { table: string; column: string };
221
+ const onRight = include.on.right as { table: string; column: string };
222
+ const leftCol = createColumnRef(onLeft.table, onLeft.column);
223
+ const rightCol = createColumnRef(onRight.table, onRight.column);
224
+ const onExpr = createJoinOnExpr(leftCol, rightCol);
225
+
226
+ return {
227
+ kind: 'includeMany' as const,
228
+ alias: include.alias,
229
+ child: {
230
+ table: createTableRef(include.table.name),
231
+ on: onExpr,
232
+ ...(childWhere ? { where: childWhere } : {}),
233
+ ...(childOrderBy ? { orderBy: childOrderBy } : {}),
234
+ ...(typeof include.childLimit === 'number' ? { limit: include.childLimit } : {}),
235
+ project: include.childProjection.aliases.map((alias, idx) => {
236
+ const column = include.childProjection.columns[idx];
237
+ if (!column || !alias) {
238
+ errorMissingColumnForAlias(alias ?? 'unknown', idx);
239
+ }
240
+
241
+ return {
242
+ alias,
243
+ expr: createColumnRef(column.table, column.column),
244
+ };
245
+ }),
246
+ },
247
+ };
248
+ }
@@ -0,0 +1,18 @@
1
+ import type { JoinAst } from '@prisma-next/sql-relational-core/ast';
2
+ import {
3
+ createColumnRef,
4
+ createJoin,
5
+ createJoinOnExpr,
6
+ createTableRef,
7
+ } from '@prisma-next/sql-relational-core/ast';
8
+ import type { JoinState } from '../utils/state';
9
+
10
+ export function buildJoinAst(join: JoinState): JoinAst {
11
+ // TypeScript can't narrow ColumnBuilder properly, so we assert
12
+ const onLeft = join.on.left as { table: string; column: string };
13
+ const onRight = join.on.right as { table: string; column: string };
14
+ const leftCol = createColumnRef(onLeft.table, onLeft.column);
15
+ const rightCol = createColumnRef(onRight.table, onRight.column);
16
+ const onExpr = createJoinOnExpr(leftCol, rightCol);
17
+ return createJoin(join.joinType, createTableRef(join.table.name), onExpr);
18
+ }