@prisma-next/sql-contract-psl 0.3.0-dev.128 → 0.3.0-dev.146
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 -6
- package/dist/index.d.mts +12 -7
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{interpreter-D7gLmaHz.mjs → interpreter-qjtea3zY.mjs} +1238 -824
- package/dist/interpreter-qjtea3zY.mjs.map +1 -0
- package/dist/provider.d.mts +4 -1
- package/dist/provider.d.mts.map +1 -1
- package/dist/provider.mjs +5 -3
- package/dist/provider.mjs.map +1 -1
- package/package.json +11 -8
- package/src/exports/index.ts +2 -2
- package/src/interpreter.ts +978 -1731
- package/src/provider.ts +11 -3
- package/src/psl-attribute-parsing.ts +303 -0
- package/src/psl-column-resolution.ts +404 -0
- package/src/psl-field-resolution.ts +312 -0
- package/src/psl-relation-resolution.ts +362 -0
- package/dist/interpreter-D7gLmaHz.mjs.map +0 -1
|
@@ -1,8 +1,191 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { instantiateAuthoringTypeConstructor, isAuthoringTypeConstructorDescriptor } from "@prisma-next/framework-components/authoring";
|
|
2
|
+
import { buildSqlContractFromDefinition } from "@prisma-next/sql-contract-ts/contract-builder";
|
|
3
3
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
4
4
|
import { notOk, ok } from "@prisma-next/utils/result";
|
|
5
|
+
import { getPositionalArgument, parseQuotedStringLiteral } from "@prisma-next/psl-parser";
|
|
6
|
+
import { assertDefined, invariant } from "@prisma-next/utils/assertions";
|
|
7
|
+
|
|
8
|
+
//#region src/psl-attribute-parsing.ts
|
|
9
|
+
function lowerFirst(value) {
|
|
10
|
+
if (value.length === 0) return value;
|
|
11
|
+
return value[0]?.toLowerCase() + value.slice(1);
|
|
12
|
+
}
|
|
13
|
+
function getAttribute(attributes, name) {
|
|
14
|
+
return attributes?.find((attribute) => attribute.name === name);
|
|
15
|
+
}
|
|
16
|
+
function getNamedArgument(attribute, name) {
|
|
17
|
+
const entry = attribute.args.find((arg) => arg.kind === "named" && arg.name === name);
|
|
18
|
+
if (!entry || entry.kind !== "named") return;
|
|
19
|
+
return entry.value;
|
|
20
|
+
}
|
|
21
|
+
function getPositionalArgumentEntry(attribute, index = 0) {
|
|
22
|
+
const entry = attribute.args.filter((arg) => arg.kind === "positional")[index];
|
|
23
|
+
if (!entry || entry.kind !== "positional") return;
|
|
24
|
+
return {
|
|
25
|
+
value: entry.value,
|
|
26
|
+
span: entry.span
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function unquoteStringLiteral(value) {
|
|
30
|
+
const trimmed = value.trim();
|
|
31
|
+
const match = trimmed.match(/^(['"])(.*)\1$/);
|
|
32
|
+
if (!match) return trimmed;
|
|
33
|
+
return match[2] ?? "";
|
|
34
|
+
}
|
|
35
|
+
function parseFieldList(value) {
|
|
36
|
+
const trimmed = value.trim();
|
|
37
|
+
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return;
|
|
38
|
+
return trimmed.slice(1, -1).split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
39
|
+
}
|
|
40
|
+
function parseMapName(input) {
|
|
41
|
+
if (!input.attribute) return input.defaultValue;
|
|
42
|
+
const value = getPositionalArgument(input.attribute);
|
|
43
|
+
if (!value) {
|
|
44
|
+
input.diagnostics.push({
|
|
45
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
46
|
+
message: `${input.entityLabel} @map requires a positional quoted string literal argument`,
|
|
47
|
+
sourceId: input.sourceId,
|
|
48
|
+
span: input.attribute.span
|
|
49
|
+
});
|
|
50
|
+
return input.defaultValue;
|
|
51
|
+
}
|
|
52
|
+
const parsed = parseQuotedStringLiteral(value);
|
|
53
|
+
if (parsed === void 0) {
|
|
54
|
+
input.diagnostics.push({
|
|
55
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
56
|
+
message: `${input.entityLabel} @map requires a positional quoted string literal argument`,
|
|
57
|
+
sourceId: input.sourceId,
|
|
58
|
+
span: input.attribute.span
|
|
59
|
+
});
|
|
60
|
+
return input.defaultValue;
|
|
61
|
+
}
|
|
62
|
+
return parsed;
|
|
63
|
+
}
|
|
64
|
+
function parseConstraintMapArgument(input) {
|
|
65
|
+
if (!input.attribute) return;
|
|
66
|
+
const raw = getNamedArgument(input.attribute, "map");
|
|
67
|
+
if (!raw) return;
|
|
68
|
+
const parsed = parseQuotedStringLiteral(raw);
|
|
69
|
+
if (parsed !== void 0) return parsed;
|
|
70
|
+
input.diagnostics.push({
|
|
71
|
+
code: input.code,
|
|
72
|
+
message: `${input.entityLabel} map argument must be a quoted string literal`,
|
|
73
|
+
sourceId: input.sourceId,
|
|
74
|
+
span: input.span
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function getPositionalArguments(attribute) {
|
|
78
|
+
return attribute.args.filter((arg) => arg.kind === "positional").map((arg) => arg.kind === "positional" ? arg.value : "");
|
|
79
|
+
}
|
|
80
|
+
function pushInvalidAttributeArgument(input) {
|
|
81
|
+
input.diagnostics.push({
|
|
82
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
83
|
+
message: input.message,
|
|
84
|
+
sourceId: input.sourceId,
|
|
85
|
+
span: input.span
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
function parseOptionalSingleIntegerArgument(input) {
|
|
89
|
+
if (input.attribute.args.some((arg) => arg.kind === "named")) return pushInvalidAttributeArgument({
|
|
90
|
+
diagnostics: input.diagnostics,
|
|
91
|
+
sourceId: input.sourceId,
|
|
92
|
+
span: input.attribute.span,
|
|
93
|
+
message: `${input.entityLabel} @${input.attribute.name} accepts zero or one positional integer argument.`
|
|
94
|
+
});
|
|
95
|
+
const positionalArguments = getPositionalArguments(input.attribute);
|
|
96
|
+
if (positionalArguments.length > 1) return pushInvalidAttributeArgument({
|
|
97
|
+
diagnostics: input.diagnostics,
|
|
98
|
+
sourceId: input.sourceId,
|
|
99
|
+
span: input.attribute.span,
|
|
100
|
+
message: `${input.entityLabel} @${input.attribute.name} accepts zero or one positional integer argument.`
|
|
101
|
+
});
|
|
102
|
+
if (positionalArguments.length === 0) return null;
|
|
103
|
+
const parsed = Number(unquoteStringLiteral(positionalArguments[0] ?? ""));
|
|
104
|
+
if (!Number.isInteger(parsed) || parsed < input.minimum) return pushInvalidAttributeArgument({
|
|
105
|
+
diagnostics: input.diagnostics,
|
|
106
|
+
sourceId: input.sourceId,
|
|
107
|
+
span: input.attribute.span,
|
|
108
|
+
message: `${input.entityLabel} @${input.attribute.name} requires a ${input.valueLabel}.`
|
|
109
|
+
});
|
|
110
|
+
return parsed;
|
|
111
|
+
}
|
|
112
|
+
function parseOptionalNumericArguments(input) {
|
|
113
|
+
if (input.attribute.args.some((arg) => arg.kind === "named")) return pushInvalidAttributeArgument({
|
|
114
|
+
diagnostics: input.diagnostics,
|
|
115
|
+
sourceId: input.sourceId,
|
|
116
|
+
span: input.attribute.span,
|
|
117
|
+
message: `${input.entityLabel} @${input.attribute.name} accepts zero, one, or two positional integer arguments.`
|
|
118
|
+
});
|
|
119
|
+
const positionalArguments = getPositionalArguments(input.attribute);
|
|
120
|
+
if (positionalArguments.length > 2) return pushInvalidAttributeArgument({
|
|
121
|
+
diagnostics: input.diagnostics,
|
|
122
|
+
sourceId: input.sourceId,
|
|
123
|
+
span: input.attribute.span,
|
|
124
|
+
message: `${input.entityLabel} @${input.attribute.name} accepts zero, one, or two positional integer arguments.`
|
|
125
|
+
});
|
|
126
|
+
if (positionalArguments.length === 0) return null;
|
|
127
|
+
const precision = Number(unquoteStringLiteral(positionalArguments[0] ?? ""));
|
|
128
|
+
if (!Number.isInteger(precision) || precision < 1) return pushInvalidAttributeArgument({
|
|
129
|
+
diagnostics: input.diagnostics,
|
|
130
|
+
sourceId: input.sourceId,
|
|
131
|
+
span: input.attribute.span,
|
|
132
|
+
message: `${input.entityLabel} @${input.attribute.name} requires a positive integer precision.`
|
|
133
|
+
});
|
|
134
|
+
if (positionalArguments.length === 1) return { precision };
|
|
135
|
+
const scale = Number(unquoteStringLiteral(positionalArguments[1] ?? ""));
|
|
136
|
+
if (!Number.isInteger(scale) || scale < 0) return pushInvalidAttributeArgument({
|
|
137
|
+
diagnostics: input.diagnostics,
|
|
138
|
+
sourceId: input.sourceId,
|
|
139
|
+
span: input.attribute.span,
|
|
140
|
+
message: `${input.entityLabel} @${input.attribute.name} requires a non-negative integer scale.`
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
precision,
|
|
144
|
+
scale
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function parseAttributeFieldList(input) {
|
|
148
|
+
const raw = getNamedArgument(input.attribute, "fields") ?? getPositionalArgument(input.attribute);
|
|
149
|
+
if (!raw) {
|
|
150
|
+
input.diagnostics.push({
|
|
151
|
+
code: input.code,
|
|
152
|
+
message: `${input.messagePrefix} requires fields list argument`,
|
|
153
|
+
sourceId: input.sourceId,
|
|
154
|
+
span: input.attribute.span
|
|
155
|
+
});
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const fields = parseFieldList(raw);
|
|
159
|
+
if (!fields || fields.length === 0) {
|
|
160
|
+
input.diagnostics.push({
|
|
161
|
+
code: input.code,
|
|
162
|
+
message: `${input.messagePrefix} requires bracketed field list argument`,
|
|
163
|
+
sourceId: input.sourceId,
|
|
164
|
+
span: input.attribute.span
|
|
165
|
+
});
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
return fields;
|
|
169
|
+
}
|
|
170
|
+
function mapFieldNamesToColumns(input) {
|
|
171
|
+
const columns = [];
|
|
172
|
+
for (const fieldName of input.fieldNames) {
|
|
173
|
+
const columnName = input.mapping.fieldColumns.get(fieldName);
|
|
174
|
+
if (!columnName) {
|
|
175
|
+
input.diagnostics.push({
|
|
176
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
177
|
+
message: `${input.contextLabel} references unknown field "${input.modelName}.${fieldName}"`,
|
|
178
|
+
sourceId: input.sourceId,
|
|
179
|
+
span: input.span
|
|
180
|
+
});
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
columns.push(columnName);
|
|
184
|
+
}
|
|
185
|
+
return columns;
|
|
186
|
+
}
|
|
5
187
|
|
|
188
|
+
//#endregion
|
|
6
189
|
//#region src/default-function-registry.ts
|
|
7
190
|
function resolveSpanPositionFromBase(base, text, offset) {
|
|
8
191
|
const safeOffset = Math.min(Math.max(0, offset), text.length);
|
|
@@ -142,267 +325,21 @@ function lowerDefaultFunctionWithRegistry(input) {
|
|
|
142
325
|
}
|
|
143
326
|
|
|
144
327
|
//#endregion
|
|
145
|
-
//#region src/
|
|
146
|
-
|
|
147
|
-
NoAction: "noAction",
|
|
148
|
-
Restrict: "restrict",
|
|
149
|
-
Cascade: "cascade",
|
|
150
|
-
SetNull: "setNull",
|
|
151
|
-
SetDefault: "setDefault",
|
|
152
|
-
noAction: "noAction",
|
|
153
|
-
restrict: "restrict",
|
|
154
|
-
cascade: "cascade",
|
|
155
|
-
setNull: "setNull",
|
|
156
|
-
setDefault: "setDefault"
|
|
157
|
-
};
|
|
158
|
-
function fkRelationPairKey(declaringModelName, targetModelName) {
|
|
159
|
-
return `${declaringModelName}::${targetModelName}`;
|
|
160
|
-
}
|
|
161
|
-
function lowerFirst(value) {
|
|
162
|
-
if (value.length === 0) return value;
|
|
163
|
-
return value[0]?.toLowerCase() + value.slice(1);
|
|
164
|
-
}
|
|
165
|
-
function getAttribute(attributes, name) {
|
|
166
|
-
return attributes?.find((attribute) => attribute.name === name);
|
|
167
|
-
}
|
|
168
|
-
function getNamedArgument(attribute, name) {
|
|
169
|
-
const entry = attribute.args.find((arg) => arg.kind === "named" && arg.name === name);
|
|
170
|
-
if (!entry || entry.kind !== "named") return;
|
|
171
|
-
return entry.value;
|
|
172
|
-
}
|
|
173
|
-
function getPositionalArgument(attribute, index = 0) {
|
|
174
|
-
const entry = attribute.args.filter((arg) => arg.kind === "positional")[index];
|
|
175
|
-
if (!entry || entry.kind !== "positional") return;
|
|
176
|
-
return entry.value;
|
|
177
|
-
}
|
|
178
|
-
function getPositionalArgumentEntry(attribute, index = 0) {
|
|
179
|
-
const entry = attribute.args.filter((arg) => arg.kind === "positional")[index];
|
|
180
|
-
if (!entry || entry.kind !== "positional") return;
|
|
328
|
+
//#region src/psl-column-resolution.ts
|
|
329
|
+
function toNamedTypeFieldDescriptor(typeRef, descriptor) {
|
|
181
330
|
return {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
function unquoteStringLiteral(value) {
|
|
187
|
-
const trimmed = value.trim();
|
|
188
|
-
const match = trimmed.match(/^(['"])(.*)\1$/);
|
|
189
|
-
if (!match) return trimmed;
|
|
190
|
-
return match[2] ?? "";
|
|
191
|
-
}
|
|
192
|
-
function parseQuotedStringLiteral(value) {
|
|
193
|
-
const match = value.trim().match(/^(['"])(.*)\1$/);
|
|
194
|
-
if (!match) return;
|
|
195
|
-
return match[2] ?? "";
|
|
196
|
-
}
|
|
197
|
-
function parseFieldList(value) {
|
|
198
|
-
const trimmed = value.trim();
|
|
199
|
-
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return;
|
|
200
|
-
return trimmed.slice(1, -1).split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
201
|
-
}
|
|
202
|
-
function parseDefaultLiteralValue(expression) {
|
|
203
|
-
const trimmed = expression.trim();
|
|
204
|
-
if (trimmed === "true" || trimmed === "false") return {
|
|
205
|
-
kind: "literal",
|
|
206
|
-
value: trimmed === "true"
|
|
207
|
-
};
|
|
208
|
-
const numericValue = Number(trimmed);
|
|
209
|
-
if (!Number.isNaN(numericValue) && trimmed.length > 0 && !/^(['"]).*\1$/.test(trimmed)) return {
|
|
210
|
-
kind: "literal",
|
|
211
|
-
value: numericValue
|
|
212
|
-
};
|
|
213
|
-
if (/^(['"]).*\1$/.test(trimmed)) return {
|
|
214
|
-
kind: "literal",
|
|
215
|
-
value: unquoteStringLiteral(trimmed)
|
|
331
|
+
codecId: descriptor.codecId,
|
|
332
|
+
nativeType: descriptor.nativeType,
|
|
333
|
+
typeRef
|
|
216
334
|
};
|
|
217
335
|
}
|
|
218
|
-
function
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
message: `Field "${input.modelName}.${input.fieldName}" requires exactly one positional @default(...) expression.`,
|
|
224
|
-
sourceId: input.sourceId,
|
|
225
|
-
span: input.defaultAttribute.span
|
|
226
|
-
});
|
|
227
|
-
return {};
|
|
336
|
+
function getAuthoringTypeConstructor(contributions, path) {
|
|
337
|
+
let current = contributions?.type;
|
|
338
|
+
for (const segment of path) {
|
|
339
|
+
if (typeof current !== "object" || current === null || Array.isArray(current)) return;
|
|
340
|
+
current = current[segment];
|
|
228
341
|
}
|
|
229
|
-
|
|
230
|
-
if (!expressionEntry) {
|
|
231
|
-
input.diagnostics.push({
|
|
232
|
-
code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
|
|
233
|
-
message: `Field "${input.modelName}.${input.fieldName}" requires a positional @default(...) expression.`,
|
|
234
|
-
sourceId: input.sourceId,
|
|
235
|
-
span: input.defaultAttribute.span
|
|
236
|
-
});
|
|
237
|
-
return {};
|
|
238
|
-
}
|
|
239
|
-
const literalDefault = parseDefaultLiteralValue(expressionEntry.value);
|
|
240
|
-
if (literalDefault) return { defaultValue: literalDefault };
|
|
241
|
-
const defaultFunctionCall = parseDefaultFunctionCall(expressionEntry.value, expressionEntry.span);
|
|
242
|
-
if (!defaultFunctionCall) {
|
|
243
|
-
input.diagnostics.push({
|
|
244
|
-
code: "PSL_INVALID_DEFAULT_VALUE",
|
|
245
|
-
message: `Unsupported default value "${expressionEntry.value}"`,
|
|
246
|
-
sourceId: input.sourceId,
|
|
247
|
-
span: input.defaultAttribute.span
|
|
248
|
-
});
|
|
249
|
-
return {};
|
|
250
|
-
}
|
|
251
|
-
const lowered = lowerDefaultFunctionWithRegistry({
|
|
252
|
-
call: defaultFunctionCall,
|
|
253
|
-
registry: input.defaultFunctionRegistry,
|
|
254
|
-
context: {
|
|
255
|
-
sourceId: input.sourceId,
|
|
256
|
-
modelName: input.modelName,
|
|
257
|
-
fieldName: input.fieldName,
|
|
258
|
-
columnCodecId: input.columnDescriptor.codecId
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
if (!lowered.ok) {
|
|
262
|
-
input.diagnostics.push(lowered.diagnostic);
|
|
263
|
-
return {};
|
|
264
|
-
}
|
|
265
|
-
if (lowered.value.kind === "storage") return { defaultValue: lowered.value.defaultValue };
|
|
266
|
-
const generatorDescriptor = input.generatorDescriptorById.get(lowered.value.generated.id);
|
|
267
|
-
if (!generatorDescriptor) {
|
|
268
|
-
input.diagnostics.push({
|
|
269
|
-
code: "PSL_INVALID_DEFAULT_APPLICABILITY",
|
|
270
|
-
message: `Default generator "${lowered.value.generated.id}" is not available in the composed mutation default registry.`,
|
|
271
|
-
sourceId: input.sourceId,
|
|
272
|
-
span: expressionEntry.span
|
|
273
|
-
});
|
|
274
|
-
return {};
|
|
275
|
-
}
|
|
276
|
-
if (!generatorDescriptor.applicableCodecIds.includes(input.columnDescriptor.codecId)) {
|
|
277
|
-
input.diagnostics.push({
|
|
278
|
-
code: "PSL_INVALID_DEFAULT_APPLICABILITY",
|
|
279
|
-
message: `Default generator "${generatorDescriptor.id}" is not applicable to "${input.modelName}.${input.fieldName}" with codecId "${input.columnDescriptor.codecId}".`,
|
|
280
|
-
sourceId: input.sourceId,
|
|
281
|
-
span: expressionEntry.span
|
|
282
|
-
});
|
|
283
|
-
return {};
|
|
284
|
-
}
|
|
285
|
-
return { executionDefault: lowered.value.generated };
|
|
286
|
-
}
|
|
287
|
-
function parseMapName(input) {
|
|
288
|
-
if (!input.attribute) return input.defaultValue;
|
|
289
|
-
const value = getPositionalArgument(input.attribute);
|
|
290
|
-
if (!value) {
|
|
291
|
-
input.diagnostics.push({
|
|
292
|
-
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
293
|
-
message: `${input.entityLabel} @map requires a positional quoted string literal argument`,
|
|
294
|
-
sourceId: input.sourceId,
|
|
295
|
-
span: input.attribute.span
|
|
296
|
-
});
|
|
297
|
-
return input.defaultValue;
|
|
298
|
-
}
|
|
299
|
-
const parsed = parseQuotedStringLiteral(value);
|
|
300
|
-
if (parsed === void 0) {
|
|
301
|
-
input.diagnostics.push({
|
|
302
|
-
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
303
|
-
message: `${input.entityLabel} @map requires a positional quoted string literal argument`,
|
|
304
|
-
sourceId: input.sourceId,
|
|
305
|
-
span: input.attribute.span
|
|
306
|
-
});
|
|
307
|
-
return input.defaultValue;
|
|
308
|
-
}
|
|
309
|
-
return parsed;
|
|
310
|
-
}
|
|
311
|
-
function parsePgvectorLength(input) {
|
|
312
|
-
const namedLength = getNamedArgument(input.attribute, "length");
|
|
313
|
-
const namedDim = getNamedArgument(input.attribute, "dim");
|
|
314
|
-
const positional = getPositionalArgument(input.attribute);
|
|
315
|
-
const raw = namedLength ?? namedDim ?? positional;
|
|
316
|
-
if (!raw) {
|
|
317
|
-
input.diagnostics.push({
|
|
318
|
-
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
319
|
-
message: "@pgvector.column requires length/dim argument",
|
|
320
|
-
sourceId: input.sourceId,
|
|
321
|
-
span: input.attribute.span
|
|
322
|
-
});
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
const parsed = Number(unquoteStringLiteral(raw));
|
|
326
|
-
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
327
|
-
input.diagnostics.push({
|
|
328
|
-
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
329
|
-
message: "@pgvector.column length/dim must be a positive integer",
|
|
330
|
-
sourceId: input.sourceId,
|
|
331
|
-
span: input.attribute.span
|
|
332
|
-
});
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
|
-
return parsed;
|
|
336
|
-
}
|
|
337
|
-
function getPositionalArguments(attribute) {
|
|
338
|
-
return attribute.args.filter((arg) => arg.kind === "positional").map((arg) => arg.kind === "positional" ? arg.value : "");
|
|
339
|
-
}
|
|
340
|
-
function pushInvalidAttributeArgument(input) {
|
|
341
|
-
input.diagnostics.push({
|
|
342
|
-
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
343
|
-
message: input.message,
|
|
344
|
-
sourceId: input.sourceId,
|
|
345
|
-
span: input.span
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
function parseOptionalSingleIntegerArgument(input) {
|
|
349
|
-
if (input.attribute.args.some((arg) => arg.kind === "named")) return pushInvalidAttributeArgument({
|
|
350
|
-
diagnostics: input.diagnostics,
|
|
351
|
-
sourceId: input.sourceId,
|
|
352
|
-
span: input.attribute.span,
|
|
353
|
-
message: `${input.entityLabel} @${input.attribute.name} accepts zero or one positional integer argument.`
|
|
354
|
-
});
|
|
355
|
-
const positionalArguments = getPositionalArguments(input.attribute);
|
|
356
|
-
if (positionalArguments.length > 1) return pushInvalidAttributeArgument({
|
|
357
|
-
diagnostics: input.diagnostics,
|
|
358
|
-
sourceId: input.sourceId,
|
|
359
|
-
span: input.attribute.span,
|
|
360
|
-
message: `${input.entityLabel} @${input.attribute.name} accepts zero or one positional integer argument.`
|
|
361
|
-
});
|
|
362
|
-
if (positionalArguments.length === 0) return null;
|
|
363
|
-
const parsed = Number(unquoteStringLiteral(positionalArguments[0] ?? ""));
|
|
364
|
-
if (!Number.isInteger(parsed) || parsed < input.minimum) return pushInvalidAttributeArgument({
|
|
365
|
-
diagnostics: input.diagnostics,
|
|
366
|
-
sourceId: input.sourceId,
|
|
367
|
-
span: input.attribute.span,
|
|
368
|
-
message: `${input.entityLabel} @${input.attribute.name} requires a ${input.valueLabel}.`
|
|
369
|
-
});
|
|
370
|
-
return parsed;
|
|
371
|
-
}
|
|
372
|
-
function parseOptionalNumericArguments(input) {
|
|
373
|
-
if (input.attribute.args.some((arg) => arg.kind === "named")) return pushInvalidAttributeArgument({
|
|
374
|
-
diagnostics: input.diagnostics,
|
|
375
|
-
sourceId: input.sourceId,
|
|
376
|
-
span: input.attribute.span,
|
|
377
|
-
message: `${input.entityLabel} @${input.attribute.name} accepts zero, one, or two positional integer arguments.`
|
|
378
|
-
});
|
|
379
|
-
const positionalArguments = getPositionalArguments(input.attribute);
|
|
380
|
-
if (positionalArguments.length > 2) return pushInvalidAttributeArgument({
|
|
381
|
-
diagnostics: input.diagnostics,
|
|
382
|
-
sourceId: input.sourceId,
|
|
383
|
-
span: input.attribute.span,
|
|
384
|
-
message: `${input.entityLabel} @${input.attribute.name} accepts zero, one, or two positional integer arguments.`
|
|
385
|
-
});
|
|
386
|
-
if (positionalArguments.length === 0) return null;
|
|
387
|
-
const precision = Number(unquoteStringLiteral(positionalArguments[0] ?? ""));
|
|
388
|
-
if (!Number.isInteger(precision) || precision < 1) return pushInvalidAttributeArgument({
|
|
389
|
-
diagnostics: input.diagnostics,
|
|
390
|
-
sourceId: input.sourceId,
|
|
391
|
-
span: input.attribute.span,
|
|
392
|
-
message: `${input.entityLabel} @${input.attribute.name} requires a positive integer precision.`
|
|
393
|
-
});
|
|
394
|
-
if (positionalArguments.length === 1) return { precision };
|
|
395
|
-
const scale = Number(unquoteStringLiteral(positionalArguments[1] ?? ""));
|
|
396
|
-
if (!Number.isInteger(scale) || scale < 0) return pushInvalidAttributeArgument({
|
|
397
|
-
diagnostics: input.diagnostics,
|
|
398
|
-
sourceId: input.sourceId,
|
|
399
|
-
span: input.attribute.span,
|
|
400
|
-
message: `${input.entityLabel} @${input.attribute.name} requires a non-negative integer scale.`
|
|
401
|
-
});
|
|
402
|
-
return {
|
|
403
|
-
precision,
|
|
404
|
-
scale
|
|
405
|
-
};
|
|
342
|
+
return isAuthoringTypeConstructorDescriptor(current) ? current : void 0;
|
|
406
343
|
}
|
|
407
344
|
const NATIVE_TYPE_SPECS = {
|
|
408
345
|
"db.VarChar": {
|
|
@@ -555,281 +492,275 @@ function resolveDbNativeTypeAttribute(input) {
|
|
|
555
492
|
}
|
|
556
493
|
}
|
|
557
494
|
}
|
|
558
|
-
function
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
diagnostics.push({
|
|
570
|
-
code: "PSL_UNSUPPORTED_FIELD_LIST",
|
|
571
|
-
message: `Field "${model.name}.${field.name}" uses a scalar/storage list type, which is not supported in SQL PSL provider v1. Model-typed lists are only supported as backrelation navigation fields when they match an FK-side relation.`,
|
|
572
|
-
sourceId,
|
|
573
|
-
span: field.span
|
|
574
|
-
});
|
|
575
|
-
continue;
|
|
576
|
-
}
|
|
577
|
-
for (const attribute of field.attributes) {
|
|
578
|
-
if (attribute.name === "id" || attribute.name === "unique" || attribute.name === "default" || attribute.name === "relation" || attribute.name === "map" || attribute.name === "pgvector.column") continue;
|
|
579
|
-
if (attribute.name.startsWith("pgvector.") && !composedExtensions.has("pgvector")) {
|
|
580
|
-
diagnostics.push({
|
|
581
|
-
code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
|
|
582
|
-
message: `Attribute "@${attribute.name}" uses unrecognized namespace "pgvector". Add extension pack "pgvector" to extensionPacks in prisma-next.config.ts.`,
|
|
583
|
-
sourceId,
|
|
584
|
-
span: attribute.span
|
|
585
|
-
});
|
|
586
|
-
continue;
|
|
587
|
-
}
|
|
588
|
-
diagnostics.push({
|
|
589
|
-
code: "PSL_UNSUPPORTED_FIELD_ATTRIBUTE",
|
|
590
|
-
message: `Field "${model.name}.${field.name}" uses unsupported attribute "@${attribute.name}"`,
|
|
591
|
-
sourceId,
|
|
592
|
-
span: attribute.span
|
|
593
|
-
});
|
|
594
|
-
}
|
|
595
|
-
if (getAttribute(field.attributes, "relation") && modelNames.has(field.typeName)) continue;
|
|
596
|
-
let descriptor = resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptors, scalarTypeDescriptors);
|
|
597
|
-
const pgvectorColumnAttribute = getAttribute(field.attributes, "pgvector.column");
|
|
598
|
-
if (pgvectorColumnAttribute) if (!composedExtensions.has("pgvector")) diagnostics.push({
|
|
599
|
-
code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
|
|
600
|
-
message: "Attribute \"@pgvector.column\" uses unrecognized namespace \"pgvector\". Add extension pack \"pgvector\" to extensionPacks in prisma-next.config.ts.",
|
|
601
|
-
sourceId,
|
|
602
|
-
span: pgvectorColumnAttribute.span
|
|
495
|
+
function parsePgvectorLength(input) {
|
|
496
|
+
const namedLength = getNamedArgument(input.attribute, "length");
|
|
497
|
+
const namedDim = getNamedArgument(input.attribute, "dim");
|
|
498
|
+
const positional = getPositionalArgument(input.attribute);
|
|
499
|
+
const raw = namedLength ?? namedDim ?? positional;
|
|
500
|
+
if (!raw) {
|
|
501
|
+
input.diagnostics.push({
|
|
502
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
503
|
+
message: "@pgvector.column requires length/dim argument",
|
|
504
|
+
sourceId: input.sourceId,
|
|
505
|
+
span: input.attribute.span
|
|
603
506
|
});
|
|
604
|
-
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
const parsed = Number(unquoteStringLiteral(raw));
|
|
510
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
511
|
+
input.diagnostics.push({
|
|
605
512
|
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
606
|
-
message:
|
|
607
|
-
sourceId,
|
|
608
|
-
span:
|
|
609
|
-
});
|
|
610
|
-
else {
|
|
611
|
-
const length = parsePgvectorLength({
|
|
612
|
-
attribute: pgvectorColumnAttribute,
|
|
613
|
-
diagnostics,
|
|
614
|
-
sourceId
|
|
615
|
-
});
|
|
616
|
-
if (length !== void 0) descriptor = {
|
|
617
|
-
codecId: "pg/vector@1",
|
|
618
|
-
nativeType: "vector",
|
|
619
|
-
typeParams: { length }
|
|
620
|
-
};
|
|
621
|
-
}
|
|
622
|
-
if (!descriptor) {
|
|
623
|
-
diagnostics.push({
|
|
624
|
-
code: "PSL_UNSUPPORTED_FIELD_TYPE",
|
|
625
|
-
message: `Field "${model.name}.${field.name}" type "${field.typeName}" is not supported in SQL PSL provider v1`,
|
|
626
|
-
sourceId,
|
|
627
|
-
span: field.span
|
|
628
|
-
});
|
|
629
|
-
continue;
|
|
630
|
-
}
|
|
631
|
-
const defaultAttribute = getAttribute(field.attributes, "default");
|
|
632
|
-
const loweredDefault = defaultAttribute ? lowerDefaultForField({
|
|
633
|
-
modelName: model.name,
|
|
634
|
-
fieldName: field.name,
|
|
635
|
-
defaultAttribute,
|
|
636
|
-
columnDescriptor: descriptor,
|
|
637
|
-
generatorDescriptorById,
|
|
638
|
-
sourceId,
|
|
639
|
-
defaultFunctionRegistry,
|
|
640
|
-
diagnostics
|
|
641
|
-
}) : {};
|
|
642
|
-
if (field.optional && loweredDefault.executionDefault) {
|
|
643
|
-
const generatorDescription = loweredDefault.executionDefault.kind === "generator" ? `"${loweredDefault.executionDefault.id}"` : "for this field";
|
|
644
|
-
diagnostics.push({
|
|
645
|
-
code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
|
|
646
|
-
message: `Field "${model.name}.${field.name}" cannot be optional when using execution default ${generatorDescription}. Remove "?" or use a storage default.`,
|
|
647
|
-
sourceId,
|
|
648
|
-
span: defaultAttribute?.span ?? field.span
|
|
649
|
-
});
|
|
650
|
-
continue;
|
|
651
|
-
}
|
|
652
|
-
if (loweredDefault.executionDefault) {
|
|
653
|
-
const generatedDescriptor = generatorDescriptorById.get(loweredDefault.executionDefault.id)?.resolveGeneratedColumnDescriptor?.({ generated: loweredDefault.executionDefault });
|
|
654
|
-
if (generatedDescriptor) descriptor = generatedDescriptor;
|
|
655
|
-
}
|
|
656
|
-
const mappedColumnName = mapping.fieldColumns.get(field.name) ?? field.name;
|
|
657
|
-
resolvedFields.push({
|
|
658
|
-
field,
|
|
659
|
-
columnName: mappedColumnName,
|
|
660
|
-
descriptor,
|
|
661
|
-
...ifDefined("defaultValue", loweredDefault.defaultValue),
|
|
662
|
-
...ifDefined("executionDefault", loweredDefault.executionDefault),
|
|
663
|
-
isId: Boolean(getAttribute(field.attributes, "id")),
|
|
664
|
-
isUnique: Boolean(getAttribute(field.attributes, "unique"))
|
|
513
|
+
message: "@pgvector.column length/dim must be a positive integer",
|
|
514
|
+
sourceId: input.sourceId,
|
|
515
|
+
span: input.attribute.span
|
|
665
516
|
});
|
|
517
|
+
return;
|
|
666
518
|
}
|
|
667
|
-
return
|
|
668
|
-
}
|
|
669
|
-
function hasSameSpan(a, b) {
|
|
670
|
-
return a.start.offset === b.start.offset && a.end.offset === b.end.offset && a.start.line === b.start.line && a.end.line === b.end.line;
|
|
519
|
+
return parsed;
|
|
671
520
|
}
|
|
672
|
-
function
|
|
673
|
-
|
|
674
|
-
if (
|
|
675
|
-
|
|
521
|
+
function parseDefaultLiteralValue(expression) {
|
|
522
|
+
const trimmed = expression.trim();
|
|
523
|
+
if (trimmed === "true" || trimmed === "false") return {
|
|
524
|
+
kind: "literal",
|
|
525
|
+
value: trimmed === "true"
|
|
526
|
+
};
|
|
527
|
+
const numericValue = Number(trimmed);
|
|
528
|
+
if (!Number.isNaN(numericValue) && trimmed.length > 0 && !/^(['"]).*\1$/.test(trimmed)) return {
|
|
529
|
+
kind: "literal",
|
|
530
|
+
value: numericValue
|
|
531
|
+
};
|
|
532
|
+
if (/^(['"]).*\1$/.test(trimmed)) return {
|
|
533
|
+
kind: "literal",
|
|
534
|
+
value: unquoteStringLiteral(trimmed)
|
|
535
|
+
};
|
|
676
536
|
}
|
|
677
|
-
function
|
|
678
|
-
const
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
fieldName: relation.declaringFieldName,
|
|
686
|
-
toModel: relation.targetModelName,
|
|
687
|
-
toTable: relation.targetTableName,
|
|
688
|
-
cardinality: "N:1",
|
|
689
|
-
parentTable: relation.declaringTableName,
|
|
690
|
-
parentColumns: relation.localColumns,
|
|
691
|
-
childTable: relation.targetTableName,
|
|
692
|
-
childColumns: relation.referencedColumns
|
|
537
|
+
function lowerDefaultForField(input) {
|
|
538
|
+
const positionalEntries = input.defaultAttribute.args.filter((arg) => arg.kind === "positional");
|
|
539
|
+
if (input.defaultAttribute.args.filter((arg) => arg.kind === "named").length > 0 || positionalEntries.length !== 1) {
|
|
540
|
+
input.diagnostics.push({
|
|
541
|
+
code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
|
|
542
|
+
message: `Field "${input.modelName}.${input.fieldName}" requires exactly one positional @default(...) expression.`,
|
|
543
|
+
sourceId: input.sourceId,
|
|
544
|
+
span: input.defaultAttribute.span
|
|
693
545
|
});
|
|
694
|
-
|
|
695
|
-
const pairRelations = fkRelationsByPair.get(pairKey);
|
|
696
|
-
if (!pairRelations) {
|
|
697
|
-
fkRelationsByPair.set(pairKey, [relation]);
|
|
698
|
-
continue;
|
|
699
|
-
}
|
|
700
|
-
pairRelations.push(relation);
|
|
546
|
+
return {};
|
|
701
547
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
const pairKey = fkRelationPairKey(candidate.targetModelName, candidate.modelName);
|
|
710
|
-
const pairMatches = input.fkRelationsByPair.get(pairKey) ?? [];
|
|
711
|
-
const matches = candidate.relationName ? pairMatches.filter((relation) => relation.relationName === candidate.relationName) : [...pairMatches];
|
|
712
|
-
if (matches.length === 0) {
|
|
713
|
-
input.diagnostics.push({
|
|
714
|
-
code: "PSL_ORPHANED_BACKRELATION_LIST",
|
|
715
|
-
message: `Backrelation list field "${candidate.modelName}.${candidate.field.name}" has no matching FK-side relation on model "${candidate.targetModelName}". Add @relation(fields: [...], references: [...]) on the FK-side relation or use an explicit join model for many-to-many.`,
|
|
716
|
-
sourceId: input.sourceId,
|
|
717
|
-
span: candidate.field.span
|
|
718
|
-
});
|
|
719
|
-
continue;
|
|
720
|
-
}
|
|
721
|
-
if (matches.length > 1) {
|
|
722
|
-
input.diagnostics.push({
|
|
723
|
-
code: "PSL_AMBIGUOUS_BACKRELATION_LIST",
|
|
724
|
-
message: `Backrelation list field "${candidate.modelName}.${candidate.field.name}" matches multiple FK-side relations on model "${candidate.targetModelName}". Add @relation(name: "...") (or @relation("...")) to both sides to disambiguate.`,
|
|
725
|
-
sourceId: input.sourceId,
|
|
726
|
-
span: candidate.field.span
|
|
727
|
-
});
|
|
728
|
-
continue;
|
|
729
|
-
}
|
|
730
|
-
invariant(matches.length === 1, "Backrelation matching requires exactly one match");
|
|
731
|
-
const matched = matches[0];
|
|
732
|
-
assertDefined(matched, "Backrelation matching requires a defined relation match");
|
|
733
|
-
const existing = input.modelRelations.get(candidate.modelName);
|
|
734
|
-
const current = existing ?? [];
|
|
735
|
-
if (!existing) input.modelRelations.set(candidate.modelName, current);
|
|
736
|
-
current.push({
|
|
737
|
-
fieldName: candidate.field.name,
|
|
738
|
-
toModel: matched.declaringModelName,
|
|
739
|
-
toTable: matched.declaringTableName,
|
|
740
|
-
cardinality: "1:N",
|
|
741
|
-
parentTable: candidate.tableName,
|
|
742
|
-
parentColumns: matched.referencedColumns,
|
|
743
|
-
childTable: matched.declaringTableName,
|
|
744
|
-
childColumns: matched.localColumns
|
|
548
|
+
const expressionEntry = getPositionalArgumentEntry(input.defaultAttribute);
|
|
549
|
+
if (!expressionEntry) {
|
|
550
|
+
input.diagnostics.push({
|
|
551
|
+
code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
|
|
552
|
+
message: `Field "${input.modelName}.${input.fieldName}" requires a positional @default(...) expression.`,
|
|
553
|
+
sourceId: input.sourceId,
|
|
554
|
+
span: input.defaultAttribute.span
|
|
745
555
|
});
|
|
556
|
+
return {};
|
|
746
557
|
}
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
const relationEntries = [...input.modelRelations.get(entry.model.name) ?? []].sort((left, right) => compareStrings(left.fieldName, right.fieldName));
|
|
757
|
-
nextBuilder = nextBuilder.model(entry.model.name, entry.mapping.tableName, (modelBuilder) => {
|
|
758
|
-
let next = modelBuilder;
|
|
759
|
-
for (const resolvedField of entry.resolvedFields) next = next.field(resolvedField.field.name, resolvedField.columnName);
|
|
760
|
-
for (const relation of relationEntries) next = next.relation(relation.fieldName, {
|
|
761
|
-
toModel: relation.toModel,
|
|
762
|
-
toTable: relation.toTable,
|
|
763
|
-
cardinality: relation.cardinality,
|
|
764
|
-
on: {
|
|
765
|
-
parentTable: relation.parentTable,
|
|
766
|
-
parentColumns: relation.parentColumns,
|
|
767
|
-
childTable: relation.childTable,
|
|
768
|
-
childColumns: relation.childColumns
|
|
769
|
-
}
|
|
770
|
-
});
|
|
771
|
-
return next;
|
|
558
|
+
const literalDefault = parseDefaultLiteralValue(expressionEntry.value);
|
|
559
|
+
if (literalDefault) return { defaultValue: literalDefault };
|
|
560
|
+
const defaultFunctionCall = parseDefaultFunctionCall(expressionEntry.value, expressionEntry.span);
|
|
561
|
+
if (!defaultFunctionCall) {
|
|
562
|
+
input.diagnostics.push({
|
|
563
|
+
code: "PSL_INVALID_DEFAULT_VALUE",
|
|
564
|
+
message: `Unsupported default value "${expressionEntry.value}"`,
|
|
565
|
+
sourceId: input.sourceId,
|
|
566
|
+
span: input.defaultAttribute.span
|
|
772
567
|
});
|
|
568
|
+
return {};
|
|
773
569
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
}
|
|
784
|
-
function normalizeReferentialAction(input) {
|
|
785
|
-
const normalized = REFERENTIAL_ACTION_MAP[input.actionToken];
|
|
786
|
-
if (normalized) return normalized;
|
|
787
|
-
input.diagnostics.push({
|
|
788
|
-
code: "PSL_UNSUPPORTED_REFERENTIAL_ACTION",
|
|
789
|
-
message: `Relation field "${input.modelName}.${input.fieldName}" has unsupported ${input.actionName} action "${input.actionToken}"`,
|
|
790
|
-
sourceId: input.sourceId,
|
|
791
|
-
span: input.span
|
|
570
|
+
const lowered = lowerDefaultFunctionWithRegistry({
|
|
571
|
+
call: defaultFunctionCall,
|
|
572
|
+
registry: input.defaultFunctionRegistry,
|
|
573
|
+
context: {
|
|
574
|
+
sourceId: input.sourceId,
|
|
575
|
+
modelName: input.modelName,
|
|
576
|
+
fieldName: input.fieldName,
|
|
577
|
+
columnCodecId: input.columnDescriptor.codecId
|
|
578
|
+
}
|
|
792
579
|
});
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
580
|
+
if (!lowered.ok) {
|
|
581
|
+
input.diagnostics.push(lowered.diagnostic);
|
|
582
|
+
return {};
|
|
583
|
+
}
|
|
584
|
+
if (lowered.value.kind === "storage") return { defaultValue: lowered.value.defaultValue };
|
|
585
|
+
const generatorDescriptor = input.generatorDescriptorById.get(lowered.value.generated.id);
|
|
586
|
+
if (!generatorDescriptor) {
|
|
797
587
|
input.diagnostics.push({
|
|
798
|
-
code:
|
|
799
|
-
message:
|
|
588
|
+
code: "PSL_INVALID_DEFAULT_APPLICABILITY",
|
|
589
|
+
message: `Default generator "${lowered.value.generated.id}" is not available in the composed mutation default registry.`,
|
|
800
590
|
sourceId: input.sourceId,
|
|
801
|
-
span:
|
|
591
|
+
span: expressionEntry.span
|
|
802
592
|
});
|
|
803
|
-
return;
|
|
593
|
+
return {};
|
|
804
594
|
}
|
|
805
|
-
|
|
806
|
-
if (!fields || fields.length === 0) {
|
|
595
|
+
if (!generatorDescriptor.applicableCodecIds.includes(input.columnDescriptor.codecId)) {
|
|
807
596
|
input.diagnostics.push({
|
|
808
|
-
code:
|
|
809
|
-
message:
|
|
597
|
+
code: "PSL_INVALID_DEFAULT_APPLICABILITY",
|
|
598
|
+
message: `Default generator "${generatorDescriptor.id}" is not applicable to "${input.modelName}.${input.fieldName}" with codecId "${input.columnDescriptor.codecId}".`,
|
|
810
599
|
sourceId: input.sourceId,
|
|
811
|
-
span:
|
|
600
|
+
span: expressionEntry.span
|
|
812
601
|
});
|
|
813
|
-
return;
|
|
602
|
+
return {};
|
|
814
603
|
}
|
|
815
|
-
return
|
|
604
|
+
return { executionDefault: lowered.value.generated };
|
|
816
605
|
}
|
|
817
|
-
function
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
606
|
+
function resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptors, scalarTypeDescriptors) {
|
|
607
|
+
if (field.typeRef && namedTypeDescriptors.has(field.typeRef)) return namedTypeDescriptors.get(field.typeRef);
|
|
608
|
+
if (namedTypeDescriptors.has(field.typeName)) return namedTypeDescriptors.get(field.typeName);
|
|
609
|
+
if (enumTypeDescriptors.has(field.typeName)) return enumTypeDescriptors.get(field.typeName);
|
|
610
|
+
return scalarTypeDescriptors.get(field.typeName);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
//#endregion
|
|
614
|
+
//#region src/psl-field-resolution.ts
|
|
615
|
+
function collectResolvedFields(model, mapping, enumTypeDescriptors, namedTypeDescriptors, namedTypeBaseTypes, modelNames, compositeTypeNames, composedExtensions, authoringContributions, defaultFunctionRegistry, generatorDescriptorById, diagnostics, sourceId, scalarTypeDescriptors) {
|
|
616
|
+
const resolvedFields = [];
|
|
617
|
+
const pgvectorVectorConstructor = getAuthoringTypeConstructor(authoringContributions, ["pgvector", "vector"]);
|
|
618
|
+
for (const field of model.fields) {
|
|
619
|
+
if (field.list && modelNames.has(field.typeName)) continue;
|
|
620
|
+
for (const attribute of field.attributes) {
|
|
621
|
+
if (attribute.name === "id" || attribute.name === "unique" || attribute.name === "default" || attribute.name === "relation" || attribute.name === "map" || attribute.name === "pgvector.column") continue;
|
|
622
|
+
if (attribute.name.startsWith("pgvector.") && !composedExtensions.has("pgvector")) {
|
|
623
|
+
diagnostics.push({
|
|
624
|
+
code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
|
|
625
|
+
message: `Attribute "@${attribute.name}" uses unrecognized namespace "pgvector". Add extension pack "pgvector" to extensionPacks in prisma-next.config.ts.`,
|
|
626
|
+
sourceId,
|
|
627
|
+
span: attribute.span
|
|
628
|
+
});
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
diagnostics.push({
|
|
632
|
+
code: "PSL_UNSUPPORTED_FIELD_ATTRIBUTE",
|
|
633
|
+
message: `Field "${model.name}.${field.name}" uses unsupported attribute "@${attribute.name}"`,
|
|
634
|
+
sourceId,
|
|
635
|
+
span: attribute.span
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
if (getAttribute(field.attributes, "relation") && modelNames.has(field.typeName)) continue;
|
|
639
|
+
const isValueObjectField = compositeTypeNames.has(field.typeName);
|
|
640
|
+
const isListField = field.list;
|
|
641
|
+
const pgvectorOnJsonField = getAttribute(field.attributes, "pgvector.column");
|
|
642
|
+
if (pgvectorOnJsonField && (isValueObjectField || isListField)) {
|
|
643
|
+
diagnostics.push({
|
|
823
644
|
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
824
|
-
message:
|
|
825
|
-
sourceId
|
|
826
|
-
span:
|
|
645
|
+
message: `Field "${model.name}.${field.name}" uses @pgvector.column on a JSON-backed field (${isValueObjectField ? "value object" : "list"}). @pgvector.column is only supported on scalar Bytes fields.`,
|
|
646
|
+
sourceId,
|
|
647
|
+
span: pgvectorOnJsonField.span
|
|
827
648
|
});
|
|
828
|
-
|
|
649
|
+
continue;
|
|
829
650
|
}
|
|
830
|
-
|
|
651
|
+
let descriptor;
|
|
652
|
+
let scalarCodecId;
|
|
653
|
+
if (isValueObjectField) descriptor = scalarTypeDescriptors.get("Json");
|
|
654
|
+
else if (isListField) {
|
|
655
|
+
const originalDescriptor = resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptors, scalarTypeDescriptors);
|
|
656
|
+
if (!originalDescriptor) {
|
|
657
|
+
diagnostics.push({
|
|
658
|
+
code: "PSL_UNSUPPORTED_FIELD_TYPE",
|
|
659
|
+
message: `Field "${model.name}.${field.name}" type "${field.typeName}" is not supported in SQL PSL provider v1`,
|
|
660
|
+
sourceId,
|
|
661
|
+
span: field.span
|
|
662
|
+
});
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
scalarCodecId = originalDescriptor.codecId;
|
|
666
|
+
descriptor = scalarTypeDescriptors.get("Json");
|
|
667
|
+
} else {
|
|
668
|
+
descriptor = resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptors, scalarTypeDescriptors);
|
|
669
|
+
const pgvectorColumnAttribute = getAttribute(field.attributes, "pgvector.column");
|
|
670
|
+
if (pgvectorColumnAttribute) if (!composedExtensions.has("pgvector")) diagnostics.push({
|
|
671
|
+
code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
|
|
672
|
+
message: "Attribute \"@pgvector.column\" uses unrecognized namespace \"pgvector\". Add extension pack \"pgvector\" to extensionPacks in prisma-next.config.ts.",
|
|
673
|
+
sourceId,
|
|
674
|
+
span: pgvectorColumnAttribute.span
|
|
675
|
+
});
|
|
676
|
+
else if (!(field.typeName === "Bytes" || namedTypeBaseTypes.get(field.typeRef ?? field.typeName) === "Bytes")) diagnostics.push({
|
|
677
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
678
|
+
message: `Field "${model.name}.${field.name}" uses @pgvector.column on unsupported base type "${field.typeName}"`,
|
|
679
|
+
sourceId,
|
|
680
|
+
span: pgvectorColumnAttribute.span
|
|
681
|
+
});
|
|
682
|
+
else {
|
|
683
|
+
const length = parsePgvectorLength({
|
|
684
|
+
attribute: pgvectorColumnAttribute,
|
|
685
|
+
diagnostics,
|
|
686
|
+
sourceId
|
|
687
|
+
});
|
|
688
|
+
if (length !== void 0) descriptor = pgvectorVectorConstructor ? instantiateAuthoringTypeConstructor(pgvectorVectorConstructor, [length]) : {
|
|
689
|
+
codecId: "pg/vector@1",
|
|
690
|
+
nativeType: "vector",
|
|
691
|
+
typeParams: { length }
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
if (!descriptor) {
|
|
696
|
+
diagnostics.push({
|
|
697
|
+
code: "PSL_UNSUPPORTED_FIELD_TYPE",
|
|
698
|
+
message: `Field "${model.name}.${field.name}" type "${field.typeName}" is not supported in SQL PSL provider v1`,
|
|
699
|
+
sourceId,
|
|
700
|
+
span: field.span
|
|
701
|
+
});
|
|
702
|
+
continue;
|
|
703
|
+
}
|
|
704
|
+
const defaultAttribute = getAttribute(field.attributes, "default");
|
|
705
|
+
const loweredDefault = defaultAttribute ? lowerDefaultForField({
|
|
706
|
+
modelName: model.name,
|
|
707
|
+
fieldName: field.name,
|
|
708
|
+
defaultAttribute,
|
|
709
|
+
columnDescriptor: descriptor,
|
|
710
|
+
generatorDescriptorById,
|
|
711
|
+
sourceId,
|
|
712
|
+
defaultFunctionRegistry,
|
|
713
|
+
diagnostics
|
|
714
|
+
}) : {};
|
|
715
|
+
if (field.optional && loweredDefault.executionDefault) {
|
|
716
|
+
const generatorDescription = loweredDefault.executionDefault.kind === "generator" ? `"${loweredDefault.executionDefault.id}"` : "for this field";
|
|
717
|
+
diagnostics.push({
|
|
718
|
+
code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
|
|
719
|
+
message: `Field "${model.name}.${field.name}" cannot be optional when using execution default ${generatorDescription}. Remove "?" or use a storage default.`,
|
|
720
|
+
sourceId,
|
|
721
|
+
span: defaultAttribute?.span ?? field.span
|
|
722
|
+
});
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
if (loweredDefault.executionDefault) {
|
|
726
|
+
const generatedDescriptor = generatorDescriptorById.get(loweredDefault.executionDefault.id)?.resolveGeneratedColumnDescriptor?.({ generated: loweredDefault.executionDefault });
|
|
727
|
+
if (generatedDescriptor) descriptor = generatedDescriptor;
|
|
728
|
+
}
|
|
729
|
+
const mappedColumnName = mapping.fieldColumns.get(field.name) ?? field.name;
|
|
730
|
+
const idAttribute = getAttribute(field.attributes, "id");
|
|
731
|
+
const uniqueAttribute = getAttribute(field.attributes, "unique");
|
|
732
|
+
const idName = parseConstraintMapArgument({
|
|
733
|
+
attribute: idAttribute,
|
|
734
|
+
sourceId,
|
|
735
|
+
diagnostics,
|
|
736
|
+
entityLabel: `Field "${model.name}.${field.name}" @id`,
|
|
737
|
+
span: field.span,
|
|
738
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT"
|
|
739
|
+
});
|
|
740
|
+
const uniqueName = parseConstraintMapArgument({
|
|
741
|
+
attribute: uniqueAttribute,
|
|
742
|
+
sourceId,
|
|
743
|
+
diagnostics,
|
|
744
|
+
entityLabel: `Field "${model.name}.${field.name}" @unique`,
|
|
745
|
+
span: field.span,
|
|
746
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT"
|
|
747
|
+
});
|
|
748
|
+
resolvedFields.push({
|
|
749
|
+
field,
|
|
750
|
+
columnName: mappedColumnName,
|
|
751
|
+
descriptor,
|
|
752
|
+
...ifDefined("defaultValue", loweredDefault.defaultValue),
|
|
753
|
+
...ifDefined("executionDefault", loweredDefault.executionDefault),
|
|
754
|
+
isId: Boolean(idAttribute),
|
|
755
|
+
isUnique: Boolean(uniqueAttribute),
|
|
756
|
+
...ifDefined("idName", idName),
|
|
757
|
+
...ifDefined("uniqueName", uniqueName),
|
|
758
|
+
...ifDefined("many", isListField ? true : void 0),
|
|
759
|
+
...ifDefined("valueObjectTypeName", isValueObjectField ? field.typeName : void 0),
|
|
760
|
+
...ifDefined("scalarCodecId", scalarCodecId)
|
|
761
|
+
});
|
|
831
762
|
}
|
|
832
|
-
return
|
|
763
|
+
return resolvedFields;
|
|
833
764
|
}
|
|
834
765
|
function buildModelMappings(models, diagnostics, sourceId) {
|
|
835
766
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -862,29 +793,33 @@ function buildModelMappings(models, diagnostics, sourceId) {
|
|
|
862
793
|
}
|
|
863
794
|
return result;
|
|
864
795
|
}
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
796
|
+
|
|
797
|
+
//#endregion
|
|
798
|
+
//#region src/psl-relation-resolution.ts
|
|
799
|
+
const REFERENTIAL_ACTION_MAP = {
|
|
800
|
+
NoAction: "noAction",
|
|
801
|
+
Restrict: "restrict",
|
|
802
|
+
Cascade: "cascade",
|
|
803
|
+
SetNull: "setNull",
|
|
804
|
+
SetDefault: "setDefault",
|
|
805
|
+
noAction: "noAction",
|
|
806
|
+
restrict: "restrict",
|
|
807
|
+
cascade: "cascade",
|
|
808
|
+
setNull: "setNull",
|
|
809
|
+
setDefault: "setDefault"
|
|
810
|
+
};
|
|
811
|
+
function fkRelationPairKey(declaringModelName, targetModelName) {
|
|
812
|
+
return `${declaringModelName}::${targetModelName}`;
|
|
813
|
+
}
|
|
814
|
+
function normalizeReferentialAction(input) {
|
|
815
|
+
const normalized = REFERENTIAL_ACTION_MAP[input.actionToken];
|
|
816
|
+
if (normalized) return normalized;
|
|
817
|
+
input.diagnostics.push({
|
|
818
|
+
code: "PSL_UNSUPPORTED_REFERENTIAL_ACTION",
|
|
819
|
+
message: `Relation field "${input.modelName}.${input.fieldName}" has unsupported ${input.actionName} action "${input.actionToken}"`,
|
|
820
|
+
sourceId: input.sourceId,
|
|
821
|
+
span: input.span
|
|
822
|
+
});
|
|
888
823
|
}
|
|
889
824
|
function parseRelationAttribute(input) {
|
|
890
825
|
if (input.attribute.args.filter((arg) => arg.kind === "positional").length > 1) {
|
|
@@ -994,63 +929,180 @@ function parseRelationAttribute(input) {
|
|
|
994
929
|
...ifDefined("onUpdate", onUpdateArgument ? unquoteStringLiteral(onUpdateArgument) : void 0)
|
|
995
930
|
};
|
|
996
931
|
}
|
|
997
|
-
function
|
|
998
|
-
const
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
932
|
+
function indexFkRelations(input) {
|
|
933
|
+
const modelRelations = /* @__PURE__ */ new Map();
|
|
934
|
+
const fkRelationsByPair = /* @__PURE__ */ new Map();
|
|
935
|
+
for (const relation of input.fkRelationMetadata) {
|
|
936
|
+
const existing = modelRelations.get(relation.declaringModelName);
|
|
937
|
+
const current = existing ?? [];
|
|
938
|
+
if (!existing) modelRelations.set(relation.declaringModelName, current);
|
|
939
|
+
current.push({
|
|
940
|
+
fieldName: relation.declaringFieldName,
|
|
941
|
+
toModel: relation.targetModelName,
|
|
942
|
+
toTable: relation.targetTableName,
|
|
943
|
+
cardinality: "N:1",
|
|
944
|
+
on: {
|
|
945
|
+
parentTable: relation.declaringTableName,
|
|
946
|
+
parentColumns: relation.localColumns,
|
|
947
|
+
childTable: relation.targetTableName,
|
|
948
|
+
childColumns: relation.referencedColumns
|
|
949
|
+
}
|
|
950
|
+
});
|
|
951
|
+
const pairKey = fkRelationPairKey(relation.declaringModelName, relation.targetModelName);
|
|
952
|
+
const pairRelations = fkRelationsByPair.get(pairKey);
|
|
953
|
+
if (!pairRelations) {
|
|
954
|
+
fkRelationsByPair.set(pairKey, [relation]);
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
957
|
+
pairRelations.push(relation);
|
|
958
|
+
}
|
|
959
|
+
return {
|
|
960
|
+
modelRelations,
|
|
961
|
+
fkRelationsByPair
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
function applyBackrelationCandidates(input) {
|
|
965
|
+
for (const candidate of input.backrelationCandidates) {
|
|
966
|
+
const pairKey = fkRelationPairKey(candidate.targetModelName, candidate.modelName);
|
|
967
|
+
const pairMatches = input.fkRelationsByPair.get(pairKey) ?? [];
|
|
968
|
+
const matches = candidate.relationName ? pairMatches.filter((relation) => relation.relationName === candidate.relationName) : [...pairMatches];
|
|
969
|
+
if (matches.length === 0) {
|
|
970
|
+
input.diagnostics.push({
|
|
971
|
+
code: "PSL_ORPHANED_BACKRELATION_LIST",
|
|
972
|
+
message: `Backrelation list field "${candidate.modelName}.${candidate.field.name}" has no matching FK-side relation on model "${candidate.targetModelName}". Add @relation(fields: [...], references: [...]) on the FK-side relation or use an explicit join model for many-to-many.`,
|
|
973
|
+
sourceId: input.sourceId,
|
|
974
|
+
span: candidate.field.span
|
|
975
|
+
});
|
|
976
|
+
continue;
|
|
977
|
+
}
|
|
978
|
+
if (matches.length > 1) {
|
|
979
|
+
input.diagnostics.push({
|
|
980
|
+
code: "PSL_AMBIGUOUS_BACKRELATION_LIST",
|
|
981
|
+
message: `Backrelation list field "${candidate.modelName}.${candidate.field.name}" matches multiple FK-side relations on model "${candidate.targetModelName}". Add @relation(name: "...") (or @relation("...")) to both sides to disambiguate.`,
|
|
982
|
+
sourceId: input.sourceId,
|
|
983
|
+
span: candidate.field.span
|
|
984
|
+
});
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
invariant(matches.length === 1, "Backrelation matching requires exactly one match");
|
|
988
|
+
const matched = matches[0];
|
|
989
|
+
assertDefined(matched, "Backrelation matching requires a defined relation match");
|
|
990
|
+
const existing = input.modelRelations.get(candidate.modelName);
|
|
991
|
+
const current = existing ?? [];
|
|
992
|
+
if (!existing) input.modelRelations.set(candidate.modelName, current);
|
|
993
|
+
current.push({
|
|
994
|
+
fieldName: candidate.field.name,
|
|
995
|
+
toModel: matched.declaringModelName,
|
|
996
|
+
toTable: matched.declaringTableName,
|
|
997
|
+
cardinality: "1:N",
|
|
998
|
+
on: {
|
|
999
|
+
parentTable: candidate.tableName,
|
|
1000
|
+
parentColumns: matched.referencedColumns,
|
|
1001
|
+
childTable: matched.declaringTableName,
|
|
1002
|
+
childColumns: matched.localColumns
|
|
1003
|
+
}
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
function validateNavigationListFieldAttributes(input) {
|
|
1008
|
+
let valid = true;
|
|
1009
|
+
for (const attribute of input.field.attributes) {
|
|
1010
|
+
if (attribute.name === "relation") continue;
|
|
1011
|
+
if (attribute.name.startsWith("pgvector.") && !input.composedExtensions.has("pgvector")) {
|
|
1012
|
+
input.diagnostics.push({
|
|
1013
|
+
code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
|
|
1014
|
+
message: `Attribute "@${attribute.name}" uses unrecognized namespace "pgvector". Add extension pack "pgvector" to extensionPacks in prisma-next.config.ts.`,
|
|
1015
|
+
sourceId: input.sourceId,
|
|
1016
|
+
span: attribute.span
|
|
1017
|
+
});
|
|
1018
|
+
valid = false;
|
|
1019
|
+
continue;
|
|
1020
|
+
}
|
|
1021
|
+
input.diagnostics.push({
|
|
1022
|
+
code: "PSL_UNSUPPORTED_FIELD_ATTRIBUTE",
|
|
1023
|
+
message: `Field "${input.modelName}.${input.field.name}" uses unsupported attribute "@${attribute.name}"`,
|
|
1024
|
+
sourceId: input.sourceId,
|
|
1025
|
+
span: attribute.span
|
|
1026
|
+
});
|
|
1027
|
+
valid = false;
|
|
1028
|
+
}
|
|
1029
|
+
return valid;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
//#endregion
|
|
1033
|
+
//#region src/interpreter.ts
|
|
1034
|
+
function buildComposedExtensionPackRefs(target, extensionIds, extensionPackRefs = []) {
|
|
1035
|
+
if (extensionIds.length === 0) return;
|
|
1036
|
+
const extensionPackRefById = new Map(extensionPackRefs.map((packRef) => [packRef.id, packRef]));
|
|
1037
|
+
return Object.fromEntries(extensionIds.map((extensionId) => [extensionId, extensionPackRefById.get(extensionId) ?? {
|
|
1038
|
+
kind: "extension",
|
|
1039
|
+
id: extensionId,
|
|
1040
|
+
familyId: target.familyId,
|
|
1041
|
+
targetId: target.targetId,
|
|
1042
|
+
version: "0.0.1"
|
|
1043
|
+
}]));
|
|
1044
|
+
}
|
|
1045
|
+
function hasSameSpan(a, b) {
|
|
1046
|
+
return a.start.offset === b.start.offset && a.end.offset === b.end.offset && a.start.line === b.start.line && a.end.line === b.end.line;
|
|
1047
|
+
}
|
|
1048
|
+
function compareStrings(left, right) {
|
|
1049
|
+
if (left < right) return -1;
|
|
1050
|
+
if (left > right) return 1;
|
|
1051
|
+
return 0;
|
|
1052
|
+
}
|
|
1053
|
+
function mapParserDiagnostics(document) {
|
|
1054
|
+
return document.diagnostics.map((diagnostic) => ({
|
|
1055
|
+
code: diagnostic.code,
|
|
1056
|
+
message: diagnostic.message,
|
|
1057
|
+
sourceId: diagnostic.sourceId,
|
|
1058
|
+
span: diagnostic.span
|
|
1059
|
+
}));
|
|
1060
|
+
}
|
|
1061
|
+
function processEnumDeclarations(input) {
|
|
1062
|
+
const storageTypes = {};
|
|
1023
1063
|
const enumTypeDescriptors = /* @__PURE__ */ new Map();
|
|
1024
|
-
const
|
|
1025
|
-
const namedTypeBaseTypes = /* @__PURE__ */ new Map();
|
|
1026
|
-
for (const enumDeclaration of input.document.ast.enums) {
|
|
1064
|
+
for (const enumDeclaration of input.enums) {
|
|
1027
1065
|
const nativeType = parseMapName({
|
|
1028
1066
|
attribute: getAttribute(enumDeclaration.attributes, "map"),
|
|
1029
1067
|
defaultValue: enumDeclaration.name,
|
|
1030
|
-
sourceId,
|
|
1031
|
-
diagnostics,
|
|
1068
|
+
sourceId: input.sourceId,
|
|
1069
|
+
diagnostics: input.diagnostics,
|
|
1032
1070
|
entityLabel: `Enum "${enumDeclaration.name}"`,
|
|
1033
1071
|
span: enumDeclaration.span
|
|
1034
1072
|
});
|
|
1035
|
-
const
|
|
1073
|
+
const enumStorageType = input.enumTypeConstructor ? instantiateAuthoringTypeConstructor(input.enumTypeConstructor, [nativeType, enumDeclaration.values.map((value) => value.name)]) : {
|
|
1036
1074
|
codecId: "pg/enum@1",
|
|
1037
1075
|
nativeType,
|
|
1076
|
+
typeParams: { values: enumDeclaration.values.map((value) => value.name) }
|
|
1077
|
+
};
|
|
1078
|
+
const descriptor = {
|
|
1079
|
+
codecId: enumStorageType.codecId,
|
|
1080
|
+
nativeType: enumStorageType.nativeType,
|
|
1038
1081
|
typeRef: enumDeclaration.name
|
|
1039
1082
|
};
|
|
1040
1083
|
enumTypeDescriptors.set(enumDeclaration.name, descriptor);
|
|
1041
|
-
|
|
1042
|
-
codecId:
|
|
1043
|
-
nativeType,
|
|
1044
|
-
typeParams: { values: enumDeclaration.values.map((value) => value.name) }
|
|
1045
|
-
}
|
|
1084
|
+
storageTypes[enumDeclaration.name] = {
|
|
1085
|
+
codecId: enumStorageType.codecId,
|
|
1086
|
+
nativeType: enumStorageType.nativeType,
|
|
1087
|
+
typeParams: enumStorageType.typeParams ?? { values: enumDeclaration.values.map((value) => value.name) }
|
|
1088
|
+
};
|
|
1046
1089
|
}
|
|
1047
|
-
|
|
1048
|
-
|
|
1090
|
+
return {
|
|
1091
|
+
storageTypes,
|
|
1092
|
+
enumTypeDescriptors
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
function resolveNamedTypeDeclarations(input) {
|
|
1096
|
+
const storageTypes = {};
|
|
1097
|
+
const namedTypeDescriptors = /* @__PURE__ */ new Map();
|
|
1098
|
+
const namedTypeBaseTypes = /* @__PURE__ */ new Map();
|
|
1099
|
+
for (const declaration of input.declarations) {
|
|
1100
|
+
const baseDescriptor = input.enumTypeDescriptors.get(declaration.baseType) ?? input.scalarTypeDescriptors.get(declaration.baseType);
|
|
1049
1101
|
if (!baseDescriptor) {
|
|
1050
|
-
diagnostics.push({
|
|
1102
|
+
input.diagnostics.push({
|
|
1051
1103
|
code: "PSL_UNSUPPORTED_NAMED_TYPE_BASE",
|
|
1052
1104
|
message: `Named type "${declaration.name}" references unsupported base type "${declaration.baseType}"`,
|
|
1053
|
-
sourceId,
|
|
1105
|
+
sourceId: input.sourceId,
|
|
1054
1106
|
span: declaration.span
|
|
1055
1107
|
});
|
|
1056
1108
|
continue;
|
|
@@ -1060,58 +1112,59 @@ function interpretPslDocumentToSqlContractIR(input) {
|
|
|
1060
1112
|
const dbNativeTypeAttribute = declaration.attributes.find((attribute) => attribute.name.startsWith("db."));
|
|
1061
1113
|
const unsupportedNamedTypeAttribute = declaration.attributes.find((attribute) => attribute.name !== "pgvector.column" && !attribute.name.startsWith("db."));
|
|
1062
1114
|
if (unsupportedNamedTypeAttribute) {
|
|
1063
|
-
diagnostics.push({
|
|
1115
|
+
input.diagnostics.push({
|
|
1064
1116
|
code: "PSL_UNSUPPORTED_NAMED_TYPE_ATTRIBUTE",
|
|
1065
1117
|
message: `Named type "${declaration.name}" uses unsupported attribute "${unsupportedNamedTypeAttribute.name}"`,
|
|
1066
|
-
sourceId,
|
|
1118
|
+
sourceId: input.sourceId,
|
|
1067
1119
|
span: unsupportedNamedTypeAttribute.span
|
|
1068
1120
|
});
|
|
1069
1121
|
continue;
|
|
1070
1122
|
}
|
|
1071
1123
|
if (pgvectorAttribute && dbNativeTypeAttribute) {
|
|
1072
|
-
diagnostics.push({
|
|
1124
|
+
input.diagnostics.push({
|
|
1073
1125
|
code: "PSL_UNSUPPORTED_NAMED_TYPE_ATTRIBUTE",
|
|
1074
1126
|
message: `Named type "${declaration.name}" cannot combine @pgvector.column with @${dbNativeTypeAttribute.name}.`,
|
|
1075
|
-
sourceId,
|
|
1127
|
+
sourceId: input.sourceId,
|
|
1076
1128
|
span: dbNativeTypeAttribute.span
|
|
1077
1129
|
});
|
|
1078
1130
|
continue;
|
|
1079
1131
|
}
|
|
1080
1132
|
if (pgvectorAttribute) {
|
|
1081
|
-
if (!composedExtensions.has("pgvector")) {
|
|
1082
|
-
diagnostics.push({
|
|
1133
|
+
if (!input.composedExtensions.has("pgvector")) {
|
|
1134
|
+
input.diagnostics.push({
|
|
1083
1135
|
code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
|
|
1084
1136
|
message: "Attribute \"@pgvector.column\" uses unrecognized namespace \"pgvector\". Add extension pack \"pgvector\" to extensionPacks in prisma-next.config.ts.",
|
|
1085
|
-
sourceId,
|
|
1137
|
+
sourceId: input.sourceId,
|
|
1086
1138
|
span: pgvectorAttribute.span
|
|
1087
1139
|
});
|
|
1088
1140
|
continue;
|
|
1089
1141
|
}
|
|
1090
1142
|
if (declaration.baseType !== "Bytes") {
|
|
1091
|
-
diagnostics.push({
|
|
1143
|
+
input.diagnostics.push({
|
|
1092
1144
|
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
1093
1145
|
message: `Named type "${declaration.name}" uses @pgvector.column on unsupported base type "${declaration.baseType}"`,
|
|
1094
|
-
sourceId,
|
|
1146
|
+
sourceId: input.sourceId,
|
|
1095
1147
|
span: pgvectorAttribute.span
|
|
1096
1148
|
});
|
|
1097
1149
|
continue;
|
|
1098
1150
|
}
|
|
1099
1151
|
const length = parsePgvectorLength({
|
|
1100
1152
|
attribute: pgvectorAttribute,
|
|
1101
|
-
diagnostics,
|
|
1102
|
-
sourceId
|
|
1153
|
+
diagnostics: input.diagnostics,
|
|
1154
|
+
sourceId: input.sourceId
|
|
1103
1155
|
});
|
|
1104
1156
|
if (length === void 0) continue;
|
|
1105
|
-
|
|
1106
|
-
codecId: "pg/vector@1",
|
|
1107
|
-
nativeType: "vector",
|
|
1108
|
-
typeParams: { length }
|
|
1109
|
-
});
|
|
1110
|
-
builder = builder.storageType(declaration.name, {
|
|
1157
|
+
const pgvectorStorageType = input.pgvectorVectorConstructor ? instantiateAuthoringTypeConstructor(input.pgvectorVectorConstructor, [length]) : {
|
|
1111
1158
|
codecId: "pg/vector@1",
|
|
1112
1159
|
nativeType: "vector",
|
|
1113
1160
|
typeParams: { length }
|
|
1114
|
-
}
|
|
1161
|
+
};
|
|
1162
|
+
namedTypeDescriptors.set(declaration.name, toNamedTypeFieldDescriptor(declaration.name, pgvectorStorageType));
|
|
1163
|
+
storageTypes[declaration.name] = {
|
|
1164
|
+
codecId: pgvectorStorageType.codecId,
|
|
1165
|
+
nativeType: pgvectorStorageType.nativeType,
|
|
1166
|
+
typeParams: pgvectorStorageType.typeParams ?? { length }
|
|
1167
|
+
};
|
|
1115
1168
|
continue;
|
|
1116
1169
|
}
|
|
1117
1170
|
if (dbNativeTypeAttribute) {
|
|
@@ -1119,273 +1172,614 @@ function interpretPslDocumentToSqlContractIR(input) {
|
|
|
1119
1172
|
attribute: dbNativeTypeAttribute,
|
|
1120
1173
|
baseType: declaration.baseType,
|
|
1121
1174
|
baseDescriptor,
|
|
1122
|
-
diagnostics,
|
|
1123
|
-
sourceId,
|
|
1175
|
+
diagnostics: input.diagnostics,
|
|
1176
|
+
sourceId: input.sourceId,
|
|
1124
1177
|
entityLabel: `Named type "${declaration.name}"`
|
|
1125
1178
|
});
|
|
1126
1179
|
if (!descriptor$1) continue;
|
|
1127
|
-
namedTypeDescriptors.set(declaration.name,
|
|
1128
|
-
|
|
1129
|
-
typeRef: declaration.name
|
|
1130
|
-
});
|
|
1131
|
-
builder = builder.storageType(declaration.name, {
|
|
1180
|
+
namedTypeDescriptors.set(declaration.name, toNamedTypeFieldDescriptor(declaration.name, descriptor$1));
|
|
1181
|
+
storageTypes[declaration.name] = {
|
|
1132
1182
|
codecId: descriptor$1.codecId,
|
|
1133
1183
|
nativeType: descriptor$1.nativeType,
|
|
1134
1184
|
typeParams: descriptor$1.typeParams ?? {}
|
|
1135
|
-
}
|
|
1185
|
+
};
|
|
1136
1186
|
continue;
|
|
1137
1187
|
}
|
|
1138
|
-
const descriptor =
|
|
1139
|
-
codecId: baseDescriptor.codecId,
|
|
1140
|
-
nativeType: baseDescriptor.nativeType,
|
|
1141
|
-
typeRef: declaration.name
|
|
1142
|
-
};
|
|
1188
|
+
const descriptor = toNamedTypeFieldDescriptor(declaration.name, baseDescriptor);
|
|
1143
1189
|
namedTypeDescriptors.set(declaration.name, descriptor);
|
|
1144
|
-
|
|
1190
|
+
storageTypes[declaration.name] = {
|
|
1145
1191
|
codecId: baseDescriptor.codecId,
|
|
1146
1192
|
nativeType: baseDescriptor.nativeType,
|
|
1147
1193
|
typeParams: {}
|
|
1148
|
-
}
|
|
1194
|
+
};
|
|
1149
1195
|
}
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1196
|
+
return {
|
|
1197
|
+
storageTypes,
|
|
1198
|
+
namedTypeDescriptors,
|
|
1199
|
+
namedTypeBaseTypes
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
function buildModelNodeFromPsl(input) {
|
|
1203
|
+
const { model, mapping, sourceId, diagnostics } = input;
|
|
1204
|
+
const tableName = mapping.tableName;
|
|
1205
|
+
const resolvedFields = collectResolvedFields(model, mapping, input.enumTypeDescriptors, input.namedTypeDescriptors, input.namedTypeBaseTypes, input.modelNames, input.compositeTypeNames, input.composedExtensions, input.authoringContributions, input.defaultFunctionRegistry, input.generatorDescriptorById, diagnostics, sourceId, input.scalarTypeDescriptors);
|
|
1206
|
+
const primaryKeyFields = resolvedFields.filter((field) => field.isId);
|
|
1207
|
+
const primaryKeyColumns = primaryKeyFields.map((field) => field.columnName);
|
|
1208
|
+
const primaryKeyName = primaryKeyFields.length === 1 ? primaryKeyFields[0]?.idName : void 0;
|
|
1209
|
+
const isVariantModel = model.attributes.some((attr) => attr.name === "base");
|
|
1210
|
+
if (primaryKeyColumns.length === 0 && !isVariantModel) diagnostics.push({
|
|
1211
|
+
code: "PSL_MISSING_PRIMARY_KEY",
|
|
1212
|
+
message: `Model "${model.name}" must declare at least one @id field for SQL provider`,
|
|
1213
|
+
sourceId,
|
|
1214
|
+
span: model.span
|
|
1215
|
+
});
|
|
1216
|
+
const resultBackrelationCandidates = [];
|
|
1217
|
+
for (const field of model.fields) {
|
|
1218
|
+
if (!field.list || !input.modelNames.has(field.typeName)) continue;
|
|
1219
|
+
const attributesValid = validateNavigationListFieldAttributes({
|
|
1220
|
+
modelName: model.name,
|
|
1221
|
+
field,
|
|
1168
1222
|
sourceId,
|
|
1169
|
-
|
|
1223
|
+
composedExtensions: input.composedExtensions,
|
|
1224
|
+
diagnostics
|
|
1170
1225
|
});
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1226
|
+
const relationAttribute = getAttribute(field.attributes, "relation");
|
|
1227
|
+
let relationName;
|
|
1228
|
+
if (relationAttribute) {
|
|
1229
|
+
const parsedRelation = parseRelationAttribute({
|
|
1230
|
+
attribute: relationAttribute,
|
|
1174
1231
|
modelName: model.name,
|
|
1175
|
-
field,
|
|
1232
|
+
fieldName: field.name,
|
|
1176
1233
|
sourceId,
|
|
1177
|
-
composedExtensions,
|
|
1178
1234
|
diagnostics
|
|
1179
1235
|
});
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
modelName: model.name,
|
|
1186
|
-
fieldName: field.name,
|
|
1236
|
+
if (!parsedRelation) continue;
|
|
1237
|
+
if (parsedRelation.fields || parsedRelation.references) {
|
|
1238
|
+
diagnostics.push({
|
|
1239
|
+
code: "PSL_INVALID_RELATION_ATTRIBUTE",
|
|
1240
|
+
message: `Backrelation list field "${model.name}.${field.name}" cannot declare fields/references; define them on the FK-side relation field`,
|
|
1187
1241
|
sourceId,
|
|
1188
|
-
|
|
1242
|
+
span: relationAttribute.span
|
|
1189
1243
|
});
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
if (parsedRelation.onDelete || parsedRelation.onUpdate) {
|
|
1201
|
-
diagnostics.push({
|
|
1202
|
-
code: "PSL_INVALID_RELATION_ATTRIBUTE",
|
|
1203
|
-
message: `Backrelation list field "${model.name}.${field.name}" cannot declare onDelete/onUpdate; define referential actions on the FK-side relation field`,
|
|
1204
|
-
sourceId,
|
|
1205
|
-
span: relationAttribute.span
|
|
1206
|
-
});
|
|
1207
|
-
continue;
|
|
1208
|
-
}
|
|
1209
|
-
relationName = parsedRelation.relationName;
|
|
1244
|
+
continue;
|
|
1245
|
+
}
|
|
1246
|
+
if (parsedRelation.onDelete || parsedRelation.onUpdate) {
|
|
1247
|
+
diagnostics.push({
|
|
1248
|
+
code: "PSL_INVALID_RELATION_ATTRIBUTE",
|
|
1249
|
+
message: `Backrelation list field "${model.name}.${field.name}" cannot declare onDelete/onUpdate; define referential actions on the FK-side relation field`,
|
|
1250
|
+
sourceId,
|
|
1251
|
+
span: relationAttribute.span
|
|
1252
|
+
});
|
|
1253
|
+
continue;
|
|
1210
1254
|
}
|
|
1211
|
-
|
|
1212
|
-
|
|
1255
|
+
relationName = parsedRelation.relationName;
|
|
1256
|
+
}
|
|
1257
|
+
if (!attributesValid) continue;
|
|
1258
|
+
resultBackrelationCandidates.push({
|
|
1259
|
+
modelName: model.name,
|
|
1260
|
+
tableName,
|
|
1261
|
+
field,
|
|
1262
|
+
targetModelName: field.typeName,
|
|
1263
|
+
...ifDefined("relationName", relationName)
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
const relationAttributes = model.fields.map((field) => ({
|
|
1267
|
+
field,
|
|
1268
|
+
relation: getAttribute(field.attributes, "relation")
|
|
1269
|
+
})).filter((entry) => Boolean(entry.relation));
|
|
1270
|
+
const uniqueConstraints = resolvedFields.filter((field) => field.isUnique).map((field) => ({
|
|
1271
|
+
columns: [field.columnName],
|
|
1272
|
+
...ifDefined("name", field.uniqueName)
|
|
1273
|
+
}));
|
|
1274
|
+
const indexNodes = [];
|
|
1275
|
+
const foreignKeyNodes = [];
|
|
1276
|
+
for (const modelAttribute of model.attributes) {
|
|
1277
|
+
if (modelAttribute.name === "map") continue;
|
|
1278
|
+
if (modelAttribute.name === "discriminator" || modelAttribute.name === "base") continue;
|
|
1279
|
+
if (modelAttribute.name === "unique" || modelAttribute.name === "index") {
|
|
1280
|
+
const fieldNames = parseAttributeFieldList({
|
|
1281
|
+
attribute: modelAttribute,
|
|
1282
|
+
sourceId,
|
|
1283
|
+
diagnostics,
|
|
1284
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
1285
|
+
messagePrefix: `Model "${model.name}" @@${modelAttribute.name}`
|
|
1286
|
+
});
|
|
1287
|
+
if (!fieldNames) continue;
|
|
1288
|
+
const columnNames = mapFieldNamesToColumns({
|
|
1213
1289
|
modelName: model.name,
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1290
|
+
fieldNames,
|
|
1291
|
+
mapping,
|
|
1292
|
+
sourceId,
|
|
1293
|
+
diagnostics,
|
|
1294
|
+
span: modelAttribute.span,
|
|
1295
|
+
contextLabel: `Model "${model.name}" @@${modelAttribute.name}`
|
|
1296
|
+
});
|
|
1297
|
+
if (!columnNames) continue;
|
|
1298
|
+
const constraintName = parseConstraintMapArgument({
|
|
1299
|
+
attribute: modelAttribute,
|
|
1300
|
+
sourceId,
|
|
1301
|
+
diagnostics,
|
|
1302
|
+
entityLabel: `Model "${model.name}" @@${modelAttribute.name}`,
|
|
1303
|
+
span: modelAttribute.span,
|
|
1304
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT"
|
|
1305
|
+
});
|
|
1306
|
+
if (modelAttribute.name === "unique") uniqueConstraints.push({
|
|
1307
|
+
columns: columnNames,
|
|
1308
|
+
...ifDefined("name", constraintName)
|
|
1218
1309
|
});
|
|
1310
|
+
else indexNodes.push({
|
|
1311
|
+
columns: columnNames,
|
|
1312
|
+
...ifDefined("name", constraintName)
|
|
1313
|
+
});
|
|
1314
|
+
continue;
|
|
1219
1315
|
}
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1316
|
+
if (modelAttribute.name.startsWith("pgvector.") && !input.composedExtensions.has("pgvector")) {
|
|
1317
|
+
diagnostics.push({
|
|
1318
|
+
code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
|
|
1319
|
+
message: `Attribute "@@${modelAttribute.name}" uses unrecognized namespace "pgvector". Add extension pack "pgvector" to extensionPacks in prisma-next.config.ts.`,
|
|
1320
|
+
sourceId,
|
|
1321
|
+
span: modelAttribute.span
|
|
1322
|
+
});
|
|
1323
|
+
continue;
|
|
1324
|
+
}
|
|
1325
|
+
diagnostics.push({
|
|
1326
|
+
code: "PSL_UNSUPPORTED_MODEL_ATTRIBUTE",
|
|
1327
|
+
message: `Model "${model.name}" uses unsupported attribute "@@${modelAttribute.name}"`,
|
|
1328
|
+
sourceId,
|
|
1329
|
+
span: modelAttribute.span
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
const resultFkRelationMetadata = [];
|
|
1333
|
+
for (const relationAttribute of relationAttributes) {
|
|
1334
|
+
if (relationAttribute.field.list) continue;
|
|
1335
|
+
if (!input.modelNames.has(relationAttribute.field.typeName)) {
|
|
1336
|
+
diagnostics.push({
|
|
1337
|
+
code: "PSL_INVALID_RELATION_TARGET",
|
|
1338
|
+
message: `Relation field "${model.name}.${relationAttribute.field.name}" references unknown model "${relationAttribute.field.typeName}"`,
|
|
1339
|
+
sourceId,
|
|
1340
|
+
span: relationAttribute.field.span
|
|
1341
|
+
});
|
|
1342
|
+
continue;
|
|
1343
|
+
}
|
|
1344
|
+
const parsedRelation = parseRelationAttribute({
|
|
1345
|
+
attribute: relationAttribute.relation,
|
|
1346
|
+
modelName: model.name,
|
|
1347
|
+
fieldName: relationAttribute.field.name,
|
|
1348
|
+
sourceId,
|
|
1349
|
+
diagnostics
|
|
1350
|
+
});
|
|
1351
|
+
if (!parsedRelation) continue;
|
|
1352
|
+
if (!parsedRelation.fields || !parsedRelation.references) {
|
|
1353
|
+
diagnostics.push({
|
|
1354
|
+
code: "PSL_INVALID_RELATION_ATTRIBUTE",
|
|
1355
|
+
message: `Relation field "${model.name}.${relationAttribute.field.name}" requires fields and references arguments`,
|
|
1356
|
+
sourceId,
|
|
1357
|
+
span: relationAttribute.relation.span
|
|
1358
|
+
});
|
|
1359
|
+
continue;
|
|
1360
|
+
}
|
|
1361
|
+
const targetMapping = input.modelMappings.get(relationAttribute.field.typeName);
|
|
1362
|
+
if (!targetMapping) {
|
|
1363
|
+
diagnostics.push({
|
|
1364
|
+
code: "PSL_INVALID_RELATION_TARGET",
|
|
1365
|
+
message: `Relation field "${model.name}.${relationAttribute.field.name}" references unknown model "${relationAttribute.field.typeName}"`,
|
|
1366
|
+
sourceId,
|
|
1367
|
+
span: relationAttribute.field.span
|
|
1368
|
+
});
|
|
1369
|
+
continue;
|
|
1370
|
+
}
|
|
1371
|
+
const localColumns = mapFieldNamesToColumns({
|
|
1372
|
+
modelName: model.name,
|
|
1373
|
+
fieldNames: parsedRelation.fields,
|
|
1374
|
+
mapping,
|
|
1375
|
+
sourceId,
|
|
1376
|
+
diagnostics,
|
|
1377
|
+
span: relationAttribute.relation.span,
|
|
1378
|
+
contextLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
|
|
1379
|
+
});
|
|
1380
|
+
if (!localColumns) continue;
|
|
1381
|
+
const referencedColumns = mapFieldNamesToColumns({
|
|
1382
|
+
modelName: targetMapping.model.name,
|
|
1383
|
+
fieldNames: parsedRelation.references,
|
|
1384
|
+
mapping: targetMapping,
|
|
1385
|
+
sourceId,
|
|
1386
|
+
diagnostics,
|
|
1387
|
+
span: relationAttribute.relation.span,
|
|
1388
|
+
contextLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
|
|
1389
|
+
});
|
|
1390
|
+
if (!referencedColumns) continue;
|
|
1391
|
+
if (localColumns.length !== referencedColumns.length) {
|
|
1392
|
+
diagnostics.push({
|
|
1393
|
+
code: "PSL_INVALID_RELATION_ATTRIBUTE",
|
|
1394
|
+
message: `Relation field "${model.name}.${relationAttribute.field.name}" must provide the same number of fields and references`,
|
|
1395
|
+
sourceId,
|
|
1396
|
+
span: relationAttribute.relation.span
|
|
1397
|
+
});
|
|
1398
|
+
continue;
|
|
1399
|
+
}
|
|
1400
|
+
const onDelete = parsedRelation.onDelete ? normalizeReferentialAction({
|
|
1401
|
+
modelName: model.name,
|
|
1402
|
+
fieldName: relationAttribute.field.name,
|
|
1403
|
+
actionName: "onDelete",
|
|
1404
|
+
actionToken: parsedRelation.onDelete,
|
|
1405
|
+
sourceId,
|
|
1406
|
+
span: relationAttribute.field.span,
|
|
1407
|
+
diagnostics
|
|
1408
|
+
}) : void 0;
|
|
1409
|
+
const onUpdate = parsedRelation.onUpdate ? normalizeReferentialAction({
|
|
1410
|
+
modelName: model.name,
|
|
1411
|
+
fieldName: relationAttribute.field.name,
|
|
1412
|
+
actionName: "onUpdate",
|
|
1413
|
+
actionToken: parsedRelation.onUpdate,
|
|
1414
|
+
sourceId,
|
|
1415
|
+
span: relationAttribute.field.span,
|
|
1416
|
+
diagnostics
|
|
1417
|
+
}) : void 0;
|
|
1418
|
+
foreignKeyNodes.push({
|
|
1419
|
+
columns: localColumns,
|
|
1420
|
+
references: {
|
|
1421
|
+
model: targetMapping.model.name,
|
|
1422
|
+
table: targetMapping.tableName,
|
|
1423
|
+
columns: referencedColumns
|
|
1424
|
+
},
|
|
1425
|
+
...ifDefined("name", parsedRelation.constraintName),
|
|
1426
|
+
...ifDefined("onDelete", onDelete),
|
|
1427
|
+
...ifDefined("onUpdate", onUpdate)
|
|
1428
|
+
});
|
|
1429
|
+
resultFkRelationMetadata.push({
|
|
1430
|
+
declaringModelName: model.name,
|
|
1431
|
+
declaringFieldName: relationAttribute.field.name,
|
|
1432
|
+
declaringTableName: tableName,
|
|
1433
|
+
targetModelName: targetMapping.model.name,
|
|
1434
|
+
targetTableName: targetMapping.tableName,
|
|
1435
|
+
...ifDefined("relationName", parsedRelation.relationName),
|
|
1436
|
+
localColumns,
|
|
1437
|
+
referencedColumns
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1440
|
+
return {
|
|
1441
|
+
modelNode: {
|
|
1442
|
+
modelName: model.name,
|
|
1443
|
+
tableName,
|
|
1444
|
+
fields: resolvedFields.map((resolvedField) => ({
|
|
1445
|
+
fieldName: resolvedField.field.name,
|
|
1446
|
+
columnName: resolvedField.columnName,
|
|
1447
|
+
descriptor: resolvedField.descriptor,
|
|
1448
|
+
nullable: resolvedField.field.optional,
|
|
1449
|
+
...ifDefined("default", resolvedField.defaultValue),
|
|
1450
|
+
...ifDefined("executionDefault", resolvedField.executionDefault)
|
|
1451
|
+
})),
|
|
1452
|
+
...primaryKeyColumns.length > 0 ? { id: {
|
|
1453
|
+
columns: primaryKeyColumns,
|
|
1454
|
+
...ifDefined("name", primaryKeyName)
|
|
1455
|
+
} } : {},
|
|
1456
|
+
...uniqueConstraints.length > 0 ? { uniques: uniqueConstraints } : {},
|
|
1457
|
+
...indexNodes.length > 0 ? { indexes: indexNodes } : {},
|
|
1458
|
+
...foreignKeyNodes.length > 0 ? { foreignKeys: foreignKeyNodes } : {}
|
|
1459
|
+
},
|
|
1460
|
+
fkRelationMetadata: resultFkRelationMetadata,
|
|
1461
|
+
backrelationCandidates: resultBackrelationCandidates,
|
|
1462
|
+
resolvedFields
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
function buildValueObjects(compositeTypes, enumTypeDescriptors, namedTypeDescriptors, scalarTypeDescriptors, diagnostics, sourceId) {
|
|
1466
|
+
const valueObjects = {};
|
|
1467
|
+
const compositeTypeNames = new Set(compositeTypes.map((ct) => ct.name));
|
|
1468
|
+
for (const compositeType of compositeTypes) {
|
|
1469
|
+
const fields = {};
|
|
1470
|
+
for (const field of compositeType.fields) {
|
|
1471
|
+
if (compositeTypeNames.has(field.typeName)) {
|
|
1472
|
+
const result = {
|
|
1473
|
+
type: {
|
|
1474
|
+
kind: "valueObject",
|
|
1475
|
+
name: field.typeName
|
|
1476
|
+
},
|
|
1477
|
+
nullable: field.optional
|
|
1478
|
+
};
|
|
1479
|
+
fields[field.name] = field.list ? {
|
|
1480
|
+
...result,
|
|
1481
|
+
many: true
|
|
1482
|
+
} : result;
|
|
1483
|
+
continue;
|
|
1240
1484
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
if (modelAttribute.name === "map") continue;
|
|
1244
|
-
if (modelAttribute.name === "unique" || modelAttribute.name === "index") {
|
|
1245
|
-
const fieldNames = parseAttributeFieldList({
|
|
1246
|
-
attribute: modelAttribute,
|
|
1247
|
-
sourceId,
|
|
1248
|
-
diagnostics,
|
|
1249
|
-
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
1250
|
-
messagePrefix: `Model "${model.name}" @@${modelAttribute.name}`
|
|
1251
|
-
});
|
|
1252
|
-
if (!fieldNames) continue;
|
|
1253
|
-
const columnNames = mapFieldNamesToColumns({
|
|
1254
|
-
modelName: model.name,
|
|
1255
|
-
fieldNames,
|
|
1256
|
-
mapping,
|
|
1257
|
-
sourceId,
|
|
1258
|
-
diagnostics,
|
|
1259
|
-
span: modelAttribute.span,
|
|
1260
|
-
contextLabel: `Model "${model.name}" @@${modelAttribute.name}`
|
|
1261
|
-
});
|
|
1262
|
-
if (!columnNames) continue;
|
|
1263
|
-
if (modelAttribute.name === "unique") table = table.unique(columnNames);
|
|
1264
|
-
else table = table.index(columnNames);
|
|
1265
|
-
continue;
|
|
1266
|
-
}
|
|
1267
|
-
if (modelAttribute.name.startsWith("pgvector.") && !composedExtensions.has("pgvector")) {
|
|
1268
|
-
diagnostics.push({
|
|
1269
|
-
code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
|
|
1270
|
-
message: `Attribute "@@${modelAttribute.name}" uses unrecognized namespace "pgvector". Add extension pack "pgvector" to extensionPacks in prisma-next.config.ts.`,
|
|
1271
|
-
sourceId,
|
|
1272
|
-
span: modelAttribute.span
|
|
1273
|
-
});
|
|
1274
|
-
continue;
|
|
1275
|
-
}
|
|
1485
|
+
const descriptor = resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptors, scalarTypeDescriptors);
|
|
1486
|
+
if (!descriptor) {
|
|
1276
1487
|
diagnostics.push({
|
|
1277
|
-
code: "
|
|
1278
|
-
message: `
|
|
1488
|
+
code: "PSL_UNSUPPORTED_FIELD_TYPE",
|
|
1489
|
+
message: `Field "${compositeType.name}.${field.name}" type "${field.typeName}" is not supported`,
|
|
1279
1490
|
sourceId,
|
|
1280
|
-
span:
|
|
1491
|
+
span: field.span
|
|
1281
1492
|
});
|
|
1493
|
+
continue;
|
|
1282
1494
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
message: `Relation field "${model.name}.${relationAttribute.field.name}" references unknown model "${relationAttribute.field.typeName}"`,
|
|
1289
|
-
sourceId,
|
|
1290
|
-
span: relationAttribute.field.span
|
|
1291
|
-
});
|
|
1292
|
-
continue;
|
|
1495
|
+
fields[field.name] = {
|
|
1496
|
+
nullable: field.optional,
|
|
1497
|
+
type: {
|
|
1498
|
+
kind: "scalar",
|
|
1499
|
+
codecId: descriptor.codecId
|
|
1293
1500
|
}
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
valueObjects[compositeType.name] = { fields };
|
|
1504
|
+
}
|
|
1505
|
+
return valueObjects;
|
|
1506
|
+
}
|
|
1507
|
+
function patchModelDomainFields(models, modelResolvedFields) {
|
|
1508
|
+
let patched = models;
|
|
1509
|
+
for (const [modelName, resolvedFields] of modelResolvedFields) {
|
|
1510
|
+
const model = patched[modelName];
|
|
1511
|
+
if (!model) continue;
|
|
1512
|
+
let needsPatch = false;
|
|
1513
|
+
const patchedFields = { ...model.fields };
|
|
1514
|
+
for (const rf of resolvedFields) if (rf.valueObjectTypeName) {
|
|
1515
|
+
needsPatch = true;
|
|
1516
|
+
patchedFields[rf.field.name] = {
|
|
1517
|
+
nullable: rf.field.optional,
|
|
1518
|
+
type: {
|
|
1519
|
+
kind: "valueObject",
|
|
1520
|
+
name: rf.valueObjectTypeName
|
|
1521
|
+
},
|
|
1522
|
+
...rf.many ? { many: true } : {}
|
|
1523
|
+
};
|
|
1524
|
+
} else if (rf.many && rf.scalarCodecId) {
|
|
1525
|
+
needsPatch = true;
|
|
1526
|
+
patchedFields[rf.field.name] = {
|
|
1527
|
+
nullable: rf.field.optional,
|
|
1528
|
+
type: {
|
|
1529
|
+
kind: "scalar",
|
|
1530
|
+
codecId: rf.scalarCodecId
|
|
1531
|
+
},
|
|
1532
|
+
many: true
|
|
1533
|
+
};
|
|
1534
|
+
}
|
|
1535
|
+
if (needsPatch) patched = {
|
|
1536
|
+
...patched,
|
|
1537
|
+
[modelName]: {
|
|
1538
|
+
...model,
|
|
1539
|
+
fields: patchedFields
|
|
1540
|
+
}
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
return patched;
|
|
1544
|
+
}
|
|
1545
|
+
function collectPolymorphismDeclarations(models, sourceId, diagnostics) {
|
|
1546
|
+
const discriminatorDeclarations = /* @__PURE__ */ new Map();
|
|
1547
|
+
const baseDeclarations = /* @__PURE__ */ new Map();
|
|
1548
|
+
for (const model of models) for (const attr of model.attributes) {
|
|
1549
|
+
if (attr.name === "discriminator") {
|
|
1550
|
+
const fieldName = getPositionalArgument(attr);
|
|
1551
|
+
if (!fieldName) {
|
|
1552
|
+
diagnostics.push({
|
|
1553
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
1554
|
+
message: `Model "${model.name}" @@discriminator requires a field name argument`,
|
|
1298
1555
|
sourceId,
|
|
1299
|
-
|
|
1556
|
+
span: attr.span
|
|
1300
1557
|
});
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
});
|
|
1309
|
-
continue;
|
|
1310
|
-
}
|
|
1311
|
-
const targetMapping = modelMappings.get(relationAttribute.field.typeName);
|
|
1312
|
-
if (!targetMapping) {
|
|
1313
|
-
diagnostics.push({
|
|
1314
|
-
code: "PSL_INVALID_RELATION_TARGET",
|
|
1315
|
-
message: `Relation field "${model.name}.${relationAttribute.field.name}" references unknown model "${relationAttribute.field.typeName}"`,
|
|
1316
|
-
sourceId,
|
|
1317
|
-
span: relationAttribute.field.span
|
|
1318
|
-
});
|
|
1319
|
-
continue;
|
|
1320
|
-
}
|
|
1321
|
-
const localColumns = mapFieldNamesToColumns({
|
|
1322
|
-
modelName: model.name,
|
|
1323
|
-
fieldNames: parsedRelation.fields,
|
|
1324
|
-
mapping,
|
|
1558
|
+
continue;
|
|
1559
|
+
}
|
|
1560
|
+
const discField = model.fields.find((f) => f.name === fieldName);
|
|
1561
|
+
if (discField && discField.typeName !== "String") {
|
|
1562
|
+
diagnostics.push({
|
|
1563
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
1564
|
+
message: `Discriminator field "${fieldName}" on model "${model.name}" must be of type String, but is "${discField.typeName}"`,
|
|
1325
1565
|
sourceId,
|
|
1326
|
-
|
|
1327
|
-
span: relationAttribute.relation.span,
|
|
1328
|
-
contextLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
|
|
1566
|
+
span: attr.span
|
|
1329
1567
|
});
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1568
|
+
continue;
|
|
1569
|
+
}
|
|
1570
|
+
discriminatorDeclarations.set(model.name, {
|
|
1571
|
+
fieldName,
|
|
1572
|
+
span: attr.span
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
if (attr.name === "base") {
|
|
1576
|
+
const baseName = getPositionalArgument(attr, 0);
|
|
1577
|
+
const rawValue = getPositionalArgument(attr, 1);
|
|
1578
|
+
if (!baseName || !rawValue) {
|
|
1579
|
+
diagnostics.push({
|
|
1580
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
1581
|
+
message: `Model "${model.name}" @@base requires two arguments: base model name and discriminator value`,
|
|
1335
1582
|
sourceId,
|
|
1336
|
-
|
|
1337
|
-
span: relationAttribute.relation.span,
|
|
1338
|
-
contextLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
|
|
1583
|
+
span: attr.span
|
|
1339
1584
|
});
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
});
|
|
1348
|
-
continue;
|
|
1349
|
-
}
|
|
1350
|
-
const onDelete = parsedRelation.onDelete ? normalizeReferentialAction({
|
|
1351
|
-
modelName: model.name,
|
|
1352
|
-
fieldName: relationAttribute.field.name,
|
|
1353
|
-
actionName: "onDelete",
|
|
1354
|
-
actionToken: parsedRelation.onDelete,
|
|
1355
|
-
sourceId,
|
|
1356
|
-
span: relationAttribute.field.span,
|
|
1357
|
-
diagnostics
|
|
1358
|
-
}) : void 0;
|
|
1359
|
-
const onUpdate = parsedRelation.onUpdate ? normalizeReferentialAction({
|
|
1360
|
-
modelName: model.name,
|
|
1361
|
-
fieldName: relationAttribute.field.name,
|
|
1362
|
-
actionName: "onUpdate",
|
|
1363
|
-
actionToken: parsedRelation.onUpdate,
|
|
1585
|
+
continue;
|
|
1586
|
+
}
|
|
1587
|
+
const value = parseQuotedStringLiteral(rawValue);
|
|
1588
|
+
if (value === void 0) {
|
|
1589
|
+
diagnostics.push({
|
|
1590
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
1591
|
+
message: `Model "${model.name}" @@base discriminator value must be a quoted string literal`,
|
|
1364
1592
|
sourceId,
|
|
1365
|
-
span:
|
|
1366
|
-
diagnostics
|
|
1367
|
-
}) : void 0;
|
|
1368
|
-
table = table.foreignKey(localColumns, {
|
|
1369
|
-
table: targetMapping.tableName,
|
|
1370
|
-
columns: referencedColumns
|
|
1371
|
-
}, {
|
|
1372
|
-
...ifDefined("name", parsedRelation.constraintName),
|
|
1373
|
-
...ifDefined("onDelete", onDelete),
|
|
1374
|
-
...ifDefined("onUpdate", onUpdate)
|
|
1593
|
+
span: attr.span
|
|
1375
1594
|
});
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1595
|
+
continue;
|
|
1596
|
+
}
|
|
1597
|
+
baseDeclarations.set(model.name, {
|
|
1598
|
+
baseName,
|
|
1599
|
+
value,
|
|
1600
|
+
span: attr.span
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
return {
|
|
1605
|
+
discriminatorDeclarations,
|
|
1606
|
+
baseDeclarations
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
function resolvePolymorphism(models, discriminatorDeclarations, baseDeclarations, modelNames, modelMappings, sourceId, diagnostics) {
|
|
1610
|
+
let patched = models;
|
|
1611
|
+
for (const [modelName, decl] of discriminatorDeclarations) {
|
|
1612
|
+
if (baseDeclarations.has(modelName)) {
|
|
1613
|
+
diagnostics.push({
|
|
1614
|
+
code: "PSL_DISCRIMINATOR_AND_BASE",
|
|
1615
|
+
message: `Model "${modelName}" cannot have both @@discriminator and @@base`,
|
|
1616
|
+
sourceId,
|
|
1617
|
+
span: decl.span
|
|
1618
|
+
});
|
|
1619
|
+
continue;
|
|
1620
|
+
}
|
|
1621
|
+
const model = patched[modelName];
|
|
1622
|
+
if (!model) continue;
|
|
1623
|
+
if (!Object.hasOwn(model.fields, decl.fieldName)) {
|
|
1624
|
+
diagnostics.push({
|
|
1625
|
+
code: "PSL_DISCRIMINATOR_FIELD_NOT_FOUND",
|
|
1626
|
+
message: `Discriminator field "${decl.fieldName}" is not a field on model "${modelName}"`,
|
|
1627
|
+
sourceId,
|
|
1628
|
+
span: decl.span
|
|
1629
|
+
});
|
|
1630
|
+
continue;
|
|
1631
|
+
}
|
|
1632
|
+
const variants = {};
|
|
1633
|
+
const seenValues = /* @__PURE__ */ new Map();
|
|
1634
|
+
for (const [variantName, baseDecl] of baseDeclarations) {
|
|
1635
|
+
if (baseDecl.baseName !== modelName) continue;
|
|
1636
|
+
const existingVariant = seenValues.get(baseDecl.value);
|
|
1637
|
+
if (existingVariant) {
|
|
1638
|
+
diagnostics.push({
|
|
1639
|
+
code: "PSL_DUPLICATE_DISCRIMINATOR_VALUE",
|
|
1640
|
+
message: `Discriminator value "${baseDecl.value}" is used by both "${existingVariant}" and "${variantName}" on base model "${modelName}"`,
|
|
1641
|
+
sourceId,
|
|
1642
|
+
span: baseDecl.span
|
|
1385
1643
|
});
|
|
1644
|
+
continue;
|
|
1645
|
+
}
|
|
1646
|
+
seenValues.set(baseDecl.value, variantName);
|
|
1647
|
+
variants[variantName] = { value: baseDecl.value };
|
|
1648
|
+
}
|
|
1649
|
+
if (Object.keys(variants).length === 0) {
|
|
1650
|
+
diagnostics.push({
|
|
1651
|
+
code: "PSL_ORPHANED_DISCRIMINATOR",
|
|
1652
|
+
message: `Model "${modelName}" has @@discriminator but no variant models declare @@base(${modelName}, ...)`,
|
|
1653
|
+
sourceId,
|
|
1654
|
+
span: decl.span
|
|
1655
|
+
});
|
|
1656
|
+
continue;
|
|
1657
|
+
}
|
|
1658
|
+
patched = {
|
|
1659
|
+
...patched,
|
|
1660
|
+
[modelName]: {
|
|
1661
|
+
...model,
|
|
1662
|
+
discriminator: { field: decl.fieldName },
|
|
1663
|
+
variants
|
|
1664
|
+
}
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
for (const [variantName, baseDecl] of baseDeclarations) {
|
|
1668
|
+
if (!modelNames.has(baseDecl.baseName)) {
|
|
1669
|
+
diagnostics.push({
|
|
1670
|
+
code: "PSL_BASE_TARGET_NOT_FOUND",
|
|
1671
|
+
message: `Model "${variantName}" @@base references non-existent model "${baseDecl.baseName}"`,
|
|
1672
|
+
sourceId,
|
|
1673
|
+
span: baseDecl.span
|
|
1674
|
+
});
|
|
1675
|
+
continue;
|
|
1676
|
+
}
|
|
1677
|
+
if (!discriminatorDeclarations.has(baseDecl.baseName)) {
|
|
1678
|
+
diagnostics.push({
|
|
1679
|
+
code: "PSL_ORPHANED_BASE",
|
|
1680
|
+
message: `Model "${variantName}" declares @@base(${baseDecl.baseName}, ...) but "${baseDecl.baseName}" has no @@discriminator`,
|
|
1681
|
+
sourceId,
|
|
1682
|
+
span: baseDecl.span
|
|
1683
|
+
});
|
|
1684
|
+
continue;
|
|
1685
|
+
}
|
|
1686
|
+
if (discriminatorDeclarations.has(variantName)) continue;
|
|
1687
|
+
const variantModel = patched[variantName];
|
|
1688
|
+
if (!variantModel) continue;
|
|
1689
|
+
const baseMapping = modelMappings.get(baseDecl.baseName);
|
|
1690
|
+
const variantMapping = modelMappings.get(variantName);
|
|
1691
|
+
const resolvedTable = variantMapping?.model.attributes.some((attr) => attr.name === "map") ?? false ? variantMapping?.tableName : baseMapping?.tableName;
|
|
1692
|
+
patched = {
|
|
1693
|
+
...patched,
|
|
1694
|
+
[variantName]: {
|
|
1695
|
+
...variantModel,
|
|
1696
|
+
base: baseDecl.baseName,
|
|
1697
|
+
...resolvedTable ? { storage: {
|
|
1698
|
+
...variantModel.storage,
|
|
1699
|
+
table: resolvedTable
|
|
1700
|
+
} } : {}
|
|
1386
1701
|
}
|
|
1387
|
-
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
return patched;
|
|
1705
|
+
}
|
|
1706
|
+
function interpretPslDocumentToSqlContract(input) {
|
|
1707
|
+
const sourceId = input.document.ast.sourceId;
|
|
1708
|
+
if (!input.target) return notOk({
|
|
1709
|
+
summary: "PSL to SQL contract interpretation failed",
|
|
1710
|
+
diagnostics: [{
|
|
1711
|
+
code: "PSL_TARGET_CONTEXT_REQUIRED",
|
|
1712
|
+
message: "PSL interpretation requires an explicit target context from composition.",
|
|
1713
|
+
sourceId
|
|
1714
|
+
}]
|
|
1715
|
+
});
|
|
1716
|
+
if (!input.scalarTypeDescriptors) return notOk({
|
|
1717
|
+
summary: "PSL to SQL contract interpretation failed",
|
|
1718
|
+
diagnostics: [{
|
|
1719
|
+
code: "PSL_SCALAR_TYPE_CONTEXT_REQUIRED",
|
|
1720
|
+
message: "PSL interpretation requires composed scalar type descriptors.",
|
|
1721
|
+
sourceId
|
|
1722
|
+
}]
|
|
1723
|
+
});
|
|
1724
|
+
const diagnostics = mapParserDiagnostics(input.document);
|
|
1725
|
+
const models = input.document.ast.models ?? [];
|
|
1726
|
+
const enums = input.document.ast.enums ?? [];
|
|
1727
|
+
const compositeTypes = input.document.ast.compositeTypes ?? [];
|
|
1728
|
+
const modelNames = new Set(models.map((model) => model.name));
|
|
1729
|
+
const compositeTypeNames = new Set(compositeTypes.map((ct) => ct.name));
|
|
1730
|
+
const composedExtensions = new Set(input.composedExtensionPacks ?? []);
|
|
1731
|
+
const defaultFunctionRegistry = input.controlMutationDefaults?.defaultFunctionRegistry ?? /* @__PURE__ */ new Map();
|
|
1732
|
+
const generatorDescriptors = input.controlMutationDefaults?.generatorDescriptors ?? [];
|
|
1733
|
+
const generatorDescriptorById = /* @__PURE__ */ new Map();
|
|
1734
|
+
for (const descriptor of generatorDescriptors) generatorDescriptorById.set(descriptor.id, descriptor);
|
|
1735
|
+
const enumResult = processEnumDeclarations({
|
|
1736
|
+
enums,
|
|
1737
|
+
sourceId,
|
|
1738
|
+
enumTypeConstructor: getAuthoringTypeConstructor(input.authoringContributions, ["enum"]),
|
|
1739
|
+
diagnostics
|
|
1740
|
+
});
|
|
1741
|
+
const namedTypeResult = resolveNamedTypeDeclarations({
|
|
1742
|
+
declarations: input.document.ast.types?.declarations ?? [],
|
|
1743
|
+
sourceId,
|
|
1744
|
+
enumTypeDescriptors: enumResult.enumTypeDescriptors,
|
|
1745
|
+
scalarTypeDescriptors: input.scalarTypeDescriptors,
|
|
1746
|
+
composedExtensions,
|
|
1747
|
+
pgvectorVectorConstructor: getAuthoringTypeConstructor(input.authoringContributions, ["pgvector", "vector"]),
|
|
1748
|
+
diagnostics
|
|
1749
|
+
});
|
|
1750
|
+
const storageTypes = {
|
|
1751
|
+
...enumResult.storageTypes,
|
|
1752
|
+
...namedTypeResult.storageTypes
|
|
1753
|
+
};
|
|
1754
|
+
const modelMappings = buildModelMappings(models, diagnostics, sourceId);
|
|
1755
|
+
const modelNodes = [];
|
|
1756
|
+
const fkRelationMetadata = [];
|
|
1757
|
+
const backrelationCandidates = [];
|
|
1758
|
+
const modelResolvedFields = /* @__PURE__ */ new Map();
|
|
1759
|
+
for (const model of models) {
|
|
1760
|
+
const mapping = modelMappings.get(model.name);
|
|
1761
|
+
if (!mapping) continue;
|
|
1762
|
+
const result = buildModelNodeFromPsl({
|
|
1763
|
+
model,
|
|
1764
|
+
mapping,
|
|
1765
|
+
modelMappings,
|
|
1766
|
+
modelNames,
|
|
1767
|
+
compositeTypeNames,
|
|
1768
|
+
enumTypeDescriptors: enumResult.enumTypeDescriptors,
|
|
1769
|
+
namedTypeDescriptors: namedTypeResult.namedTypeDescriptors,
|
|
1770
|
+
namedTypeBaseTypes: namedTypeResult.namedTypeBaseTypes,
|
|
1771
|
+
composedExtensions,
|
|
1772
|
+
authoringContributions: input.authoringContributions,
|
|
1773
|
+
defaultFunctionRegistry,
|
|
1774
|
+
generatorDescriptorById,
|
|
1775
|
+
scalarTypeDescriptors: input.scalarTypeDescriptors,
|
|
1776
|
+
sourceId,
|
|
1777
|
+
diagnostics
|
|
1388
1778
|
});
|
|
1779
|
+
modelNodes.push(result.modelNode);
|
|
1780
|
+
fkRelationMetadata.push(...result.fkRelationMetadata);
|
|
1781
|
+
backrelationCandidates.push(...result.backrelationCandidates);
|
|
1782
|
+
modelResolvedFields.set(model.name, result.resolvedFields);
|
|
1389
1783
|
}
|
|
1390
1784
|
const { modelRelations, fkRelationsByPair } = indexFkRelations({ fkRelationMetadata });
|
|
1391
1785
|
applyBackrelationCandidates({
|
|
@@ -1395,18 +1789,38 @@ function interpretPslDocumentToSqlContractIR(input) {
|
|
|
1395
1789
|
diagnostics,
|
|
1396
1790
|
sourceId
|
|
1397
1791
|
});
|
|
1398
|
-
|
|
1399
|
-
builder,
|
|
1400
|
-
resolvedModels,
|
|
1401
|
-
modelRelations
|
|
1402
|
-
});
|
|
1792
|
+
const { discriminatorDeclarations, baseDeclarations } = collectPolymorphismDeclarations(models, sourceId, diagnostics);
|
|
1403
1793
|
if (diagnostics.length > 0) return notOk({
|
|
1404
|
-
summary: "PSL to SQL
|
|
1794
|
+
summary: "PSL to SQL contract interpretation failed",
|
|
1405
1795
|
diagnostics: diagnostics.filter((diagnostic, index, allDiagnostics) => allDiagnostics.findIndex((candidate) => candidate.code === diagnostic.code && candidate.message === diagnostic.message && candidate.sourceId === diagnostic.sourceId && (candidate.span && diagnostic.span && hasSameSpan(candidate.span, diagnostic.span) || !candidate.span && !diagnostic.span)) === index)
|
|
1406
1796
|
});
|
|
1407
|
-
|
|
1797
|
+
const contract = buildSqlContractFromDefinition({
|
|
1798
|
+
target: input.target,
|
|
1799
|
+
...ifDefined("extensionPacks", buildComposedExtensionPackRefs(input.target, [...composedExtensions].sort(compareStrings), input.composedExtensionPackRefs)),
|
|
1800
|
+
...Object.keys(storageTypes).length > 0 ? { storageTypes } : {},
|
|
1801
|
+
models: modelNodes.map((model) => ({
|
|
1802
|
+
...model,
|
|
1803
|
+
...modelRelations.has(model.modelName) ? { relations: [...modelRelations.get(model.modelName) ?? []].sort((left, right) => compareStrings(left.fieldName, right.fieldName)) } : {}
|
|
1804
|
+
}))
|
|
1805
|
+
});
|
|
1806
|
+
const valueObjects = buildValueObjects(compositeTypes, enumResult.enumTypeDescriptors, namedTypeResult.namedTypeDescriptors, input.scalarTypeDescriptors, diagnostics, sourceId);
|
|
1807
|
+
let patchedModels = patchModelDomainFields(contract.models, modelResolvedFields);
|
|
1808
|
+
const polyDiagnostics = [];
|
|
1809
|
+
patchedModels = resolvePolymorphism(patchedModels, discriminatorDeclarations, baseDeclarations, modelNames, modelMappings, sourceId, polyDiagnostics);
|
|
1810
|
+
if (polyDiagnostics.length > 0) return notOk({
|
|
1811
|
+
summary: "PSL to SQL contract interpretation failed",
|
|
1812
|
+
diagnostics: polyDiagnostics
|
|
1813
|
+
});
|
|
1814
|
+
const variantModelNames = new Set(baseDeclarations.keys());
|
|
1815
|
+
const filteredRoots = Object.fromEntries(Object.entries(contract.roots).filter(([, modelName]) => !variantModelNames.has(modelName)));
|
|
1816
|
+
return ok({
|
|
1817
|
+
...contract,
|
|
1818
|
+
roots: filteredRoots,
|
|
1819
|
+
models: patchedModels,
|
|
1820
|
+
...Object.keys(valueObjects).length > 0 ? { valueObjects } : {}
|
|
1821
|
+
});
|
|
1408
1822
|
}
|
|
1409
1823
|
|
|
1410
1824
|
//#endregion
|
|
1411
|
-
export {
|
|
1412
|
-
//# sourceMappingURL=interpreter-
|
|
1825
|
+
export { interpretPslDocumentToSqlContract as t };
|
|
1826
|
+
//# sourceMappingURL=interpreter-qjtea3zY.mjs.map
|