@ronin/compiler 0.6.1 → 0.7.0
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 +4 -4
- package/dist/index.d.ts +332 -111
- package/dist/index.js +328 -346
- package/package.json +1 -1
package/dist/index.js
CHANGED
@@ -1,19 +1,23 @@
|
|
1
1
|
// src/utils/helpers.ts
|
2
2
|
import { init as cuid } from "@paralleldrive/cuid2";
|
3
|
-
var
|
3
|
+
var RONIN_MODEL_SYMBOLS = {
|
4
4
|
// Represents a sub query.
|
5
5
|
QUERY: "__RONIN_QUERY",
|
6
|
-
// Represents
|
6
|
+
// Represents an expression that should be evaluated.
|
7
|
+
EXPRESSION: "__RONIN_EXPRESSION",
|
8
|
+
// Represents the value of a field in the model.
|
7
9
|
FIELD: "__RONIN_FIELD_",
|
8
|
-
// Represents the
|
9
|
-
|
10
|
-
// Represents the
|
11
|
-
|
10
|
+
// Represents the value of a field in the model of a parent query.
|
11
|
+
FIELD_PARENT: "__RONIN_FIELD_PARENT_",
|
12
|
+
// Represents the old value of a field in the parent model. Used for triggers.
|
13
|
+
FIELD_PARENT_OLD: "__RONIN_FIELD_PARENT_OLD_",
|
14
|
+
// Represents the new value of a field in the parent model. Used for triggers.
|
15
|
+
FIELD_PARENT_NEW: "__RONIN_FIELD_PARENT_NEW_",
|
12
16
|
// Represents a value provided to a query preset.
|
13
17
|
VALUE: "__RONIN_VALUE"
|
14
18
|
};
|
15
|
-
var
|
16
|
-
`${
|
19
|
+
var RONIN_MODEL_FIELD_REGEX = new RegExp(
|
20
|
+
`${RONIN_MODEL_SYMBOLS.FIELD}[_a-zA-Z0-9]+`,
|
17
21
|
"g"
|
18
22
|
);
|
19
23
|
var RoninError = class extends Error {
|
@@ -94,9 +98,9 @@ var expand = (obj) => {
|
|
94
98
|
};
|
95
99
|
var splitQuery = (query) => {
|
96
100
|
const queryType = Object.keys(query)[0];
|
97
|
-
const
|
98
|
-
const queryInstructions = query[queryType][
|
99
|
-
return { queryType,
|
101
|
+
const queryModel = Object.keys(query[queryType])[0];
|
102
|
+
const queryInstructions = query[queryType][queryModel];
|
103
|
+
return { queryType, queryModel, queryInstructions };
|
100
104
|
};
|
101
105
|
|
102
106
|
// src/utils/statement.ts
|
@@ -112,39 +116,44 @@ var prepareStatementValue = (statementParams, value) => {
|
|
112
116
|
const index = statementParams.push(formattedValue);
|
113
117
|
return `?${index}`;
|
114
118
|
};
|
115
|
-
var
|
116
|
-
|
117
|
-
|
119
|
+
var parseFieldExpression = (model, instructionName, expression, parentModel) => {
|
120
|
+
return expression.replace(RONIN_MODEL_FIELD_REGEX, (match) => {
|
121
|
+
let toReplace = RONIN_MODEL_SYMBOLS.FIELD;
|
122
|
+
let rootModel = model;
|
123
|
+
if (match.startsWith(RONIN_MODEL_SYMBOLS.FIELD_PARENT)) {
|
124
|
+
rootModel = parentModel;
|
125
|
+
toReplace = RONIN_MODEL_SYMBOLS.FIELD_PARENT;
|
126
|
+
if (match.startsWith(RONIN_MODEL_SYMBOLS.FIELD_PARENT_OLD)) {
|
127
|
+
rootModel.tableAlias = toReplace = RONIN_MODEL_SYMBOLS.FIELD_PARENT_OLD;
|
128
|
+
} else if (match.startsWith(RONIN_MODEL_SYMBOLS.FIELD_PARENT_NEW)) {
|
129
|
+
rootModel.tableAlias = toReplace = RONIN_MODEL_SYMBOLS.FIELD_PARENT_NEW;
|
130
|
+
}
|
131
|
+
}
|
132
|
+
const fieldSlug = match.replace(toReplace, "");
|
133
|
+
const field = getFieldFromModel(rootModel, fieldSlug, instructionName);
|
134
|
+
return field.fieldSelector;
|
135
|
+
});
|
136
|
+
};
|
137
|
+
var composeFieldValues = (models, model, statementParams, instructionName, value, options) => {
|
138
|
+
const { fieldSelector: conditionSelector } = getFieldFromModel(
|
139
|
+
model,
|
118
140
|
options.fieldSlug,
|
119
|
-
instructionName
|
120
|
-
options.rootTable
|
141
|
+
instructionName
|
121
142
|
);
|
122
143
|
const collectStatementValue = options.type !== "fields";
|
123
|
-
|
144
|
+
const symbol = getSymbol(value);
|
124
145
|
let conditionValue = value;
|
125
|
-
if (
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
let toReplace = RONIN_SCHEMA_SYMBOLS.FIELD;
|
134
|
-
if (value.startsWith(RONIN_SCHEMA_SYMBOLS.FIELD_OLD)) {
|
135
|
-
targetTable = "OLD";
|
136
|
-
toReplace = RONIN_SCHEMA_SYMBOLS.FIELD_OLD;
|
137
|
-
} else if (value.startsWith(RONIN_SCHEMA_SYMBOLS.FIELD_NEW)) {
|
138
|
-
targetTable = "NEW";
|
139
|
-
toReplace = RONIN_SCHEMA_SYMBOLS.FIELD_NEW;
|
146
|
+
if (symbol) {
|
147
|
+
if (symbol?.type === "expression") {
|
148
|
+
conditionValue = parseFieldExpression(
|
149
|
+
model,
|
150
|
+
instructionName,
|
151
|
+
symbol.value,
|
152
|
+
options.parentModel
|
153
|
+
);
|
140
154
|
}
|
141
|
-
|
142
|
-
|
143
|
-
} else if (schemaField.type === "json" && instructionName === "to") {
|
144
|
-
conditionSelector = `"${schemaField.slug}"`;
|
145
|
-
if (collectStatementValue) {
|
146
|
-
const preparedValue = prepareStatementValue(statementParams, value);
|
147
|
-
conditionValue = `IIF(${conditionSelector} IS NULL, ${preparedValue}, json_patch(${conditionSelector}, ${preparedValue}))`;
|
155
|
+
if (symbol.type === "query" && collectStatementValue) {
|
156
|
+
conditionValue = `(${compileQueryInput(symbol.value, models, statementParams).main.statement})`;
|
148
157
|
}
|
149
158
|
} else if (collectStatementValue) {
|
150
159
|
conditionValue = prepareStatementValue(statementParams, value);
|
@@ -153,11 +162,11 @@ var composeFieldValues = (schemas, schema, statementParams, instructionName, val
|
|
153
162
|
if (options.type === "values") return conditionValue;
|
154
163
|
return `${conditionSelector} ${WITH_CONDITIONS[options.condition || "being"](conditionValue, value)}`;
|
155
164
|
};
|
156
|
-
var composeConditions = (
|
165
|
+
var composeConditions = (models, model, statementParams, instructionName, value, options) => {
|
157
166
|
const isNested = isObject(value) && Object.keys(value).length > 0;
|
158
167
|
if (isNested && Object.keys(value).every((key) => key in WITH_CONDITIONS)) {
|
159
168
|
const conditions = Object.entries(value).map(
|
160
|
-
([conditionType, checkValue]) => composeConditions(
|
169
|
+
([conditionType, checkValue]) => composeConditions(models, model, statementParams, instructionName, checkValue, {
|
161
170
|
...options,
|
162
171
|
condition: conditionType
|
163
172
|
})
|
@@ -165,47 +174,42 @@ var composeConditions = (schemas, schema, statementParams, instructionName, valu
|
|
165
174
|
return conditions.join(" AND ");
|
166
175
|
}
|
167
176
|
if (options.fieldSlug) {
|
168
|
-
const fieldDetails =
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
options.rootTable
|
173
|
-
);
|
174
|
-
const { field: schemaField } = fieldDetails;
|
175
|
-
const consumeJSON = schemaField.type === "json" && instructionName === "to";
|
176
|
-
if (!(isObject(value) || Array.isArray(value)) || getSubQuery(value) || consumeJSON) {
|
177
|
+
const fieldDetails = getFieldFromModel(model, options.fieldSlug, instructionName);
|
178
|
+
const { field: modelField } = fieldDetails;
|
179
|
+
const consumeJSON = modelField.type === "json" && instructionName === "to";
|
180
|
+
if (!(isObject(value) || Array.isArray(value)) || getSymbol(value) || consumeJSON) {
|
177
181
|
return composeFieldValues(
|
178
|
-
|
179
|
-
|
182
|
+
models,
|
183
|
+
model,
|
180
184
|
statementParams,
|
181
185
|
instructionName,
|
182
186
|
value,
|
183
187
|
{ ...options, fieldSlug: options.fieldSlug }
|
184
188
|
);
|
185
189
|
}
|
186
|
-
if (
|
190
|
+
if (modelField.type === "reference" && isNested) {
|
187
191
|
const keys = Object.keys(value);
|
188
192
|
const values = Object.values(value);
|
189
193
|
let recordTarget;
|
190
194
|
if (keys.length === 1 && keys[0] === "id") {
|
191
195
|
recordTarget = values[0];
|
192
196
|
} else {
|
193
|
-
const
|
197
|
+
const relatedModel = getModelBySlug(models, modelField.target.slug);
|
194
198
|
const subQuery = {
|
195
199
|
get: {
|
196
|
-
[
|
200
|
+
[relatedModel.slug]: {
|
197
201
|
with: value,
|
198
202
|
selecting: ["id"]
|
199
203
|
}
|
200
204
|
}
|
201
205
|
};
|
202
206
|
recordTarget = {
|
203
|
-
[
|
207
|
+
[RONIN_MODEL_SYMBOLS.QUERY]: subQuery
|
204
208
|
};
|
205
209
|
}
|
206
210
|
return composeConditions(
|
207
|
-
|
208
|
-
|
211
|
+
models,
|
212
|
+
model,
|
209
213
|
statementParams,
|
210
214
|
instructionName,
|
211
215
|
recordTarget,
|
@@ -216,7 +220,7 @@ var composeConditions = (schemas, schema, statementParams, instructionName, valu
|
|
216
220
|
if (isNested) {
|
217
221
|
const conditions = Object.entries(value).map(([field, value2]) => {
|
218
222
|
const nestedFieldSlug = options.fieldSlug ? `${options.fieldSlug}.${field}` : field;
|
219
|
-
return composeConditions(
|
223
|
+
return composeConditions(models, model, statementParams, instructionName, value2, {
|
220
224
|
...options,
|
221
225
|
fieldSlug: nestedFieldSlug
|
222
226
|
});
|
@@ -227,14 +231,7 @@ var composeConditions = (schemas, schema, statementParams, instructionName, valu
|
|
227
231
|
}
|
228
232
|
if (Array.isArray(value)) {
|
229
233
|
const conditions = value.map(
|
230
|
-
(filter) => composeConditions(
|
231
|
-
schemas,
|
232
|
-
schema,
|
233
|
-
statementParams,
|
234
|
-
instructionName,
|
235
|
-
filter,
|
236
|
-
options
|
237
|
-
)
|
234
|
+
(filter) => composeConditions(models, model, statementParams, instructionName, filter, options)
|
238
235
|
);
|
239
236
|
return conditions.join(" OR ");
|
240
237
|
}
|
@@ -265,8 +262,22 @@ var formatIdentifiers = ({ identifiers }, queryInstructions) => {
|
|
265
262
|
[type]: newNestedInstructions
|
266
263
|
};
|
267
264
|
};
|
268
|
-
var
|
269
|
-
|
265
|
+
var getSymbol = (value) => {
|
266
|
+
if (!isObject(value)) return null;
|
267
|
+
const objectValue = value;
|
268
|
+
if (RONIN_MODEL_SYMBOLS.QUERY in objectValue) {
|
269
|
+
return {
|
270
|
+
type: "query",
|
271
|
+
value: objectValue[RONIN_MODEL_SYMBOLS.QUERY]
|
272
|
+
};
|
273
|
+
}
|
274
|
+
if (RONIN_MODEL_SYMBOLS.EXPRESSION in objectValue) {
|
275
|
+
return {
|
276
|
+
type: "expression",
|
277
|
+
value: objectValue[RONIN_MODEL_SYMBOLS.EXPRESSION]
|
278
|
+
};
|
279
|
+
}
|
280
|
+
return null;
|
270
281
|
};
|
271
282
|
|
272
283
|
// src/instructions/with.ts
|
@@ -292,41 +303,37 @@ var WITH_CONDITIONS = {
|
|
292
303
|
lessThan: (value) => `< ${value}`,
|
293
304
|
lessOrEqual: (value) => `<= ${value}`
|
294
305
|
};
|
295
|
-
var handleWith = (
|
306
|
+
var handleWith = (models, model, statementParams, instruction, parentModel) => {
|
296
307
|
const subStatement = composeConditions(
|
297
|
-
|
298
|
-
|
308
|
+
models,
|
309
|
+
model,
|
299
310
|
statementParams,
|
300
311
|
"with",
|
301
312
|
instruction,
|
302
|
-
{
|
313
|
+
{ parentModel }
|
303
314
|
);
|
304
315
|
return `(${subStatement})`;
|
305
316
|
};
|
306
317
|
|
307
|
-
// src/utils/
|
318
|
+
// src/utils/model.ts
|
308
319
|
import title from "title";
|
309
|
-
var
|
310
|
-
const
|
311
|
-
return
|
320
|
+
var getModelBySlug = (models, slug) => {
|
321
|
+
const model = models.find((model2) => {
|
322
|
+
return model2.slug === slug || model2.pluralSlug === slug;
|
312
323
|
});
|
313
|
-
if (!
|
324
|
+
if (!model) {
|
314
325
|
throw new RoninError({
|
315
|
-
message: `No matching
|
316
|
-
code: "
|
326
|
+
message: `No matching model with either Slug or Plural Slug of "${slug}" could be found.`,
|
327
|
+
code: "MODEL_NOT_FOUND"
|
317
328
|
});
|
318
329
|
}
|
319
|
-
return
|
320
|
-
};
|
321
|
-
var getTableForSchema = (schema) => {
|
322
|
-
return convertToSnakeCase(schema.pluralSlug);
|
330
|
+
return model;
|
323
331
|
};
|
324
|
-
var
|
325
|
-
var
|
326
|
-
|
327
|
-
const
|
328
|
-
|
329
|
-
if (field.type === "json") {
|
332
|
+
var composeAssociationModelSlug = (model, field) => convertToCamelCase(`ronin_link_${model.slug}_${field.slug}`);
|
333
|
+
var getFieldSelector = (model, field, fieldPath, instructionName) => {
|
334
|
+
const symbol = model.tableAlias?.startsWith(RONIN_MODEL_SYMBOLS.FIELD_PARENT) ? `${model.tableAlias.replace(RONIN_MODEL_SYMBOLS.FIELD_PARENT, "").slice(0, -1)}.` : "";
|
335
|
+
const tablePrefix = symbol || (model.tableAlias ? `"${model.tableAlias}".` : "");
|
336
|
+
if (field.type === "json" && instructionName !== "to") {
|
330
337
|
const dotParts = fieldPath.split(".");
|
331
338
|
const columnName = tablePrefix + dotParts.shift();
|
332
339
|
const jsonField = dotParts.join(".");
|
@@ -334,28 +341,33 @@ var getFieldSelector = (field, fieldPath, rootTable) => {
|
|
334
341
|
}
|
335
342
|
return `${tablePrefix}"${fieldPath}"`;
|
336
343
|
};
|
337
|
-
var
|
344
|
+
var getFieldFromModel = (model, fieldPath, instructionName) => {
|
338
345
|
const errorPrefix = `Field "${fieldPath}" defined for \`${instructionName}\``;
|
339
|
-
const
|
340
|
-
let
|
346
|
+
const modelFields = model.fields || [];
|
347
|
+
let modelField;
|
341
348
|
if (fieldPath.includes(".")) {
|
342
|
-
|
343
|
-
if (
|
344
|
-
const fieldSelector2 = getFieldSelector(
|
345
|
-
|
349
|
+
modelField = modelFields.find((field) => field.slug === fieldPath.split(".")[0]);
|
350
|
+
if (modelField?.type === "json") {
|
351
|
+
const fieldSelector2 = getFieldSelector(
|
352
|
+
model,
|
353
|
+
modelField,
|
354
|
+
fieldPath,
|
355
|
+
instructionName
|
356
|
+
);
|
357
|
+
return { field: modelField, fieldSelector: fieldSelector2 };
|
346
358
|
}
|
347
359
|
}
|
348
|
-
|
349
|
-
if (!
|
360
|
+
modelField = modelFields.find((field) => field.slug === fieldPath);
|
361
|
+
if (!modelField) {
|
350
362
|
throw new RoninError({
|
351
|
-
message: `${errorPrefix} does not exist in
|
363
|
+
message: `${errorPrefix} does not exist in model "${model.name}".`,
|
352
364
|
code: "FIELD_NOT_FOUND",
|
353
365
|
field: fieldPath,
|
354
366
|
queries: null
|
355
367
|
});
|
356
368
|
}
|
357
|
-
const fieldSelector = getFieldSelector(
|
358
|
-
return { field:
|
369
|
+
const fieldSelector = getFieldSelector(model, modelField, fieldPath, instructionName);
|
370
|
+
return { field: modelField, fieldSelector };
|
359
371
|
};
|
360
372
|
var slugToName = (slug) => {
|
361
373
|
const name = slug.replace(/([a-z])([A-Z])/g, "$1 $2");
|
@@ -373,36 +385,37 @@ var pluralize = (word) => {
|
|
373
385
|
}
|
374
386
|
return `${word}s`;
|
375
387
|
};
|
376
|
-
var
|
388
|
+
var modelSettings = [
|
377
389
|
["pluralSlug", "slug", pluralize],
|
378
390
|
["name", "slug", slugToName],
|
379
391
|
["pluralName", "pluralSlug", slugToName],
|
380
|
-
["idPrefix", "slug", (slug) => slug.slice(0, 3)]
|
392
|
+
["idPrefix", "slug", (slug) => slug.slice(0, 3)],
|
393
|
+
["table", "pluralSlug", convertToSnakeCase]
|
381
394
|
];
|
382
|
-
var
|
383
|
-
const
|
384
|
-
for (const [setting, base, generator] of
|
385
|
-
if (
|
386
|
-
|
395
|
+
var addDefaultModelFields = (model, isNew) => {
|
396
|
+
const copiedModel = { ...model };
|
397
|
+
for (const [setting, base, generator] of modelSettings) {
|
398
|
+
if (copiedModel[setting] || !copiedModel[base]) continue;
|
399
|
+
copiedModel[setting] = generator(copiedModel[base]);
|
387
400
|
}
|
388
|
-
const newFields =
|
401
|
+
const newFields = copiedModel.fields || [];
|
389
402
|
if (isNew || newFields.length > 0) {
|
390
|
-
if (!
|
391
|
-
if (!
|
403
|
+
if (!copiedModel.identifiers) copiedModel.identifiers = {};
|
404
|
+
if (!copiedModel.identifiers.name) {
|
392
405
|
const suitableField = newFields.find(
|
393
406
|
(field) => field.type === "string" && field.required === true && ["name"].includes(field.slug)
|
394
407
|
);
|
395
|
-
|
408
|
+
copiedModel.identifiers.name = suitableField?.slug || "id";
|
396
409
|
}
|
397
|
-
if (!
|
410
|
+
if (!copiedModel.identifiers.slug) {
|
398
411
|
const suitableField = newFields.find(
|
399
412
|
(field) => field.type === "string" && field.unique === true && field.required === true && ["slug", "handle"].includes(field.slug)
|
400
413
|
);
|
401
|
-
|
414
|
+
copiedModel.identifiers.slug = suitableField?.slug || "id";
|
402
415
|
}
|
403
|
-
|
416
|
+
copiedModel.fields = [...SYSTEM_FIELDS, ...newFields];
|
404
417
|
}
|
405
|
-
return
|
418
|
+
return copiedModel;
|
406
419
|
};
|
407
420
|
var SYSTEM_FIELDS = [
|
408
421
|
{
|
@@ -442,9 +455,9 @@ var SYSTEM_FIELDS = [
|
|
442
455
|
slug: "ronin.updatedBy"
|
443
456
|
}
|
444
457
|
];
|
445
|
-
var
|
458
|
+
var SYSTEM_MODELS = [
|
446
459
|
{
|
447
|
-
slug: "
|
460
|
+
slug: "model",
|
448
461
|
identifiers: {
|
449
462
|
name: "name",
|
450
463
|
slug: "slug"
|
@@ -455,6 +468,7 @@ var SYSTEM_SCHEMAS = [
|
|
455
468
|
{ slug: "slug", type: "string" },
|
456
469
|
{ slug: "pluralSlug", type: "string" },
|
457
470
|
{ slug: "idPrefix", type: "string" },
|
471
|
+
{ slug: "table", type: "string" },
|
458
472
|
{ slug: "identifiers", type: "group" },
|
459
473
|
{ slug: "identifiers.name", type: "string" },
|
460
474
|
{ slug: "identifiers.slug", type: "string" },
|
@@ -475,9 +489,9 @@ var SYSTEM_SCHEMAS = [
|
|
475
489
|
{ slug: "slug", type: "string", required: true },
|
476
490
|
{ slug: "type", type: "string", required: true },
|
477
491
|
{
|
478
|
-
slug: "
|
492
|
+
slug: "model",
|
479
493
|
type: "reference",
|
480
|
-
target: { slug: "
|
494
|
+
target: { slug: "model" },
|
481
495
|
required: true
|
482
496
|
},
|
483
497
|
{ slug: "required", type: "boolean" },
|
@@ -485,7 +499,7 @@ var SYSTEM_SCHEMAS = [
|
|
485
499
|
{ slug: "unique", type: "boolean" },
|
486
500
|
{ slug: "autoIncrement", type: "boolean" },
|
487
501
|
// Only allowed for fields of type "reference".
|
488
|
-
{ slug: "target", type: "reference", target: { slug: "
|
502
|
+
{ slug: "target", type: "reference", target: { slug: "model" } },
|
489
503
|
{ slug: "kind", type: "string" },
|
490
504
|
{ slug: "actions", type: "group" },
|
491
505
|
{ slug: "actions.onDelete", type: "string" },
|
@@ -501,9 +515,9 @@ var SYSTEM_SCHEMAS = [
|
|
501
515
|
fields: [
|
502
516
|
{ slug: "slug", type: "string", required: true },
|
503
517
|
{
|
504
|
-
slug: "
|
518
|
+
slug: "model",
|
505
519
|
type: "reference",
|
506
|
-
target: { slug: "
|
520
|
+
target: { slug: "model" },
|
507
521
|
required: true
|
508
522
|
},
|
509
523
|
{ slug: "unique", type: "boolean" },
|
@@ -520,9 +534,9 @@ var SYSTEM_SCHEMAS = [
|
|
520
534
|
fields: [
|
521
535
|
{ slug: "slug", type: "string", required: true },
|
522
536
|
{
|
523
|
-
slug: "
|
537
|
+
slug: "model",
|
524
538
|
type: "reference",
|
525
|
-
target: { slug: "
|
539
|
+
target: { slug: "model" },
|
526
540
|
required: true
|
527
541
|
},
|
528
542
|
{ slug: "when", type: "string", required: true },
|
@@ -537,71 +551,71 @@ var SYSTEM_SCHEMAS = [
|
|
537
551
|
fields: [
|
538
552
|
{ slug: "slug", type: "string", required: true },
|
539
553
|
{
|
540
|
-
slug: "
|
554
|
+
slug: "model",
|
541
555
|
type: "reference",
|
542
|
-
target: { slug: "
|
556
|
+
target: { slug: "model" },
|
543
557
|
required: true
|
544
558
|
},
|
545
559
|
{ slug: "instructions", type: "json", required: true }
|
546
560
|
]
|
547
561
|
}
|
548
|
-
].map((
|
549
|
-
var
|
562
|
+
].map((model) => addDefaultModelFields(model, true));
|
563
|
+
var SYSTEM_MODEL_SLUGS = SYSTEM_MODELS.flatMap(({ slug, pluralSlug }) => [
|
550
564
|
slug,
|
551
565
|
pluralSlug
|
552
566
|
]);
|
553
|
-
var
|
554
|
-
const
|
555
|
-
const
|
556
|
-
for (const field of
|
567
|
+
var addSystemModels = (models) => {
|
568
|
+
const associativeModels = models.flatMap((model) => {
|
569
|
+
const addedModels = [];
|
570
|
+
for (const field of model.fields || []) {
|
557
571
|
if (field.type === "reference" && !field.slug.startsWith("ronin.")) {
|
558
|
-
const
|
559
|
-
let fieldSlug =
|
572
|
+
const relatedModel = getModelBySlug(models, field.target.slug);
|
573
|
+
let fieldSlug = relatedModel.slug;
|
560
574
|
if (field.kind === "many") {
|
561
|
-
fieldSlug =
|
562
|
-
|
575
|
+
fieldSlug = composeAssociationModelSlug(model, field);
|
576
|
+
addedModels.push({
|
563
577
|
pluralSlug: fieldSlug,
|
564
578
|
slug: fieldSlug,
|
579
|
+
associationSlug: field.slug,
|
565
580
|
fields: [
|
566
581
|
{
|
567
582
|
slug: "source",
|
568
583
|
type: "reference",
|
569
|
-
target: { slug:
|
584
|
+
target: { slug: model.slug }
|
570
585
|
},
|
571
586
|
{
|
572
587
|
slug: "target",
|
573
588
|
type: "reference",
|
574
|
-
target: { slug:
|
589
|
+
target: { slug: relatedModel.slug }
|
575
590
|
}
|
576
591
|
]
|
577
592
|
});
|
578
593
|
}
|
579
594
|
}
|
580
595
|
}
|
581
|
-
return
|
596
|
+
return addedModels;
|
582
597
|
});
|
583
|
-
return [...
|
598
|
+
return [...SYSTEM_MODELS, ...associativeModels, ...models];
|
584
599
|
};
|
585
|
-
var
|
600
|
+
var addDefaultModelPresets = (list, model) => {
|
586
601
|
const defaultPresets = [];
|
587
|
-
for (const field of
|
602
|
+
for (const field of model.fields || []) {
|
588
603
|
if (field.type === "reference" && !field.slug.startsWith("ronin.")) {
|
589
|
-
const
|
590
|
-
|
591
|
-
if (field.kind === "many") {
|
592
|
-
fieldSlug = composeAssociationSchemaSlug(schema, field);
|
593
|
-
}
|
604
|
+
const relatedModel = getModelBySlug(list, field.target.slug);
|
605
|
+
if (field.kind === "many") continue;
|
594
606
|
defaultPresets.push({
|
595
607
|
instructions: {
|
596
608
|
including: {
|
597
609
|
[field.slug]: {
|
598
|
-
[
|
610
|
+
[RONIN_MODEL_SYMBOLS.QUERY]: {
|
599
611
|
get: {
|
600
|
-
[
|
612
|
+
[relatedModel.slug]: {
|
601
613
|
with: {
|
602
|
-
// Compare the `id` field of the related
|
603
|
-
// the root
|
604
|
-
id:
|
614
|
+
// Compare the `id` field of the related model to the reference field on
|
615
|
+
// the root model (`field.slug`).
|
616
|
+
id: {
|
617
|
+
[RONIN_MODEL_SYMBOLS.EXPRESSION]: `${RONIN_MODEL_SYMBOLS.FIELD_PARENT}${field.slug}`
|
618
|
+
}
|
605
619
|
}
|
606
620
|
}
|
607
621
|
}
|
@@ -613,25 +627,28 @@ var addDefaultSchemaPresets = (list, schema) => {
|
|
613
627
|
});
|
614
628
|
}
|
615
629
|
}
|
616
|
-
const
|
617
|
-
const field =
|
618
|
-
return field2.type === "reference" && field2.target.slug ===
|
630
|
+
const childModels = list.map((subModel) => {
|
631
|
+
const field = subModel.fields?.find((field2) => {
|
632
|
+
return field2.type === "reference" && field2.target.slug === model.slug;
|
619
633
|
});
|
620
634
|
if (!field) return null;
|
621
|
-
return {
|
635
|
+
return { model: subModel, field };
|
622
636
|
}).filter((match) => match !== null);
|
623
|
-
for (const childMatch of
|
624
|
-
const {
|
625
|
-
const pluralSlug =
|
637
|
+
for (const childMatch of childModels) {
|
638
|
+
const { model: childModel, field: childField } = childMatch;
|
639
|
+
const pluralSlug = childModel.pluralSlug;
|
640
|
+
const presetSlug = childModel.associationSlug || pluralSlug;
|
626
641
|
defaultPresets.push({
|
627
642
|
instructions: {
|
628
643
|
including: {
|
629
|
-
[
|
630
|
-
[
|
644
|
+
[presetSlug]: {
|
645
|
+
[RONIN_MODEL_SYMBOLS.QUERY]: {
|
631
646
|
get: {
|
632
647
|
[pluralSlug]: {
|
633
648
|
with: {
|
634
|
-
[childField.slug]:
|
649
|
+
[childField.slug]: {
|
650
|
+
[RONIN_MODEL_SYMBOLS.EXPRESSION]: `${RONIN_MODEL_SYMBOLS.FIELD_PARENT}id`
|
651
|
+
}
|
635
652
|
}
|
636
653
|
}
|
637
654
|
}
|
@@ -639,13 +656,13 @@ var addDefaultSchemaPresets = (list, schema) => {
|
|
639
656
|
}
|
640
657
|
}
|
641
658
|
},
|
642
|
-
slug:
|
659
|
+
slug: presetSlug
|
643
660
|
});
|
644
661
|
}
|
645
662
|
if (Object.keys(defaultPresets).length > 0) {
|
646
|
-
|
663
|
+
model.presets = [...defaultPresets, ...model.presets || []];
|
647
664
|
}
|
648
|
-
return
|
665
|
+
return model;
|
649
666
|
};
|
650
667
|
var mappedInstructions = {
|
651
668
|
create: "to",
|
@@ -681,35 +698,31 @@ var getFieldStatement = (field) => {
|
|
681
698
|
}
|
682
699
|
return statement;
|
683
700
|
};
|
684
|
-
var
|
685
|
-
const {
|
686
|
-
queryType,
|
687
|
-
querySchema,
|
688
|
-
queryInstructions: queryInstructionsRaw
|
689
|
-
} = queryDetails;
|
701
|
+
var addModelQueries = (models, queryDetails, dependencyStatements) => {
|
702
|
+
const { queryType, queryModel, queryInstructions: queryInstructionsRaw } = queryDetails;
|
690
703
|
const queryInstructions = queryInstructionsRaw;
|
691
704
|
if (!["create", "set", "drop"].includes(queryType)) return queryInstructions;
|
692
|
-
if (!
|
705
|
+
if (!SYSTEM_MODEL_SLUGS.includes(queryModel)) return queryInstructions;
|
693
706
|
const instructionName = mappedInstructions[queryType];
|
694
707
|
const instructionList = queryInstructions[instructionName];
|
695
|
-
const kind =
|
708
|
+
const kind = getModelBySlug(SYSTEM_MODELS, queryModel).pluralSlug;
|
696
709
|
let tableAction = "ALTER";
|
697
710
|
let queryTypeReadable = null;
|
698
711
|
switch (queryType) {
|
699
712
|
case "create": {
|
700
|
-
if (kind === "
|
713
|
+
if (kind === "models" || kind === "indexes" || kind === "triggers") {
|
701
714
|
tableAction = "CREATE";
|
702
715
|
}
|
703
716
|
queryTypeReadable = "creating";
|
704
717
|
break;
|
705
718
|
}
|
706
719
|
case "set": {
|
707
|
-
if (kind === "
|
720
|
+
if (kind === "models") tableAction = "ALTER";
|
708
721
|
queryTypeReadable = "updating";
|
709
722
|
break;
|
710
723
|
}
|
711
724
|
case "drop": {
|
712
|
-
if (kind === "
|
725
|
+
if (kind === "models" || kind === "indexes" || kind === "triggers") {
|
713
726
|
tableAction = "DROP";
|
714
727
|
}
|
715
728
|
queryTypeReadable = "deleting";
|
@@ -724,18 +737,18 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
|
|
724
737
|
fields: ["slug"]
|
725
738
|
});
|
726
739
|
}
|
727
|
-
const
|
728
|
-
const
|
729
|
-
if (kind !== "
|
740
|
+
const modelInstruction = instructionList?.model;
|
741
|
+
const modelSlug = modelInstruction?.slug?.being || modelInstruction?.slug;
|
742
|
+
if (kind !== "models" && !modelSlug) {
|
730
743
|
throw new RoninError({
|
731
|
-
message: `When ${queryTypeReadable} ${kind}, a \`
|
744
|
+
message: `When ${queryTypeReadable} ${kind}, a \`model.slug\` field must be provided in the \`${instructionName}\` instruction.`,
|
732
745
|
code: "MISSING_FIELD",
|
733
|
-
fields: ["
|
746
|
+
fields: ["model.slug"]
|
734
747
|
});
|
735
748
|
}
|
736
|
-
const usableSlug = kind === "
|
749
|
+
const usableSlug = kind === "models" ? slug : modelSlug;
|
737
750
|
const tableName = convertToSnakeCase(pluralize(usableSlug));
|
738
|
-
const
|
751
|
+
const targetModel = kind === "models" && queryType === "create" ? null : getModelBySlug(models, usableSlug);
|
739
752
|
if (kind === "indexes") {
|
740
753
|
const indexName = convertToSnakeCase(slug);
|
741
754
|
const unique = instructionList?.unique;
|
@@ -744,16 +757,13 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
|
|
744
757
|
const params = [];
|
745
758
|
let statement2 = `${tableAction}${unique ? " UNIQUE" : ""} INDEX "${indexName}"`;
|
746
759
|
if (queryType === "create") {
|
747
|
-
const
|
760
|
+
const model = targetModel;
|
748
761
|
const columns = fields.map((field) => {
|
749
762
|
let fieldSelector = "";
|
750
763
|
if ("slug" in field) {
|
751
|
-
({ fieldSelector } =
|
764
|
+
({ fieldSelector } = getFieldFromModel(model, field.slug, "to"));
|
752
765
|
} else if ("expression" in field) {
|
753
|
-
fieldSelector = field.expression
|
754
|
-
const fieldSlug = match.replace(RONIN_SCHEMA_SYMBOLS.FIELD, "");
|
755
|
-
return getFieldFromSchema(schema, fieldSlug, "to").fieldSelector;
|
756
|
-
});
|
766
|
+
fieldSelector = parseFieldExpression(model, "to", field.expression, model);
|
757
767
|
}
|
758
768
|
if (field.collation) fieldSelector += ` COLLATE ${field.collation}`;
|
759
769
|
if (field.order) fieldSelector += ` ${field.order}`;
|
@@ -762,8 +772,8 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
|
|
762
772
|
statement2 += ` ON "${tableName}" (${columns.join(", ")})`;
|
763
773
|
if (filterQuery) {
|
764
774
|
const withStatement = handleWith(
|
765
|
-
|
766
|
-
|
775
|
+
models,
|
776
|
+
targetModel,
|
767
777
|
params,
|
768
778
|
filterQuery
|
769
779
|
);
|
@@ -778,6 +788,7 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
|
|
778
788
|
const params = [];
|
779
789
|
let statement2 = `${tableAction} TRIGGER "${triggerName}"`;
|
780
790
|
if (queryType === "create") {
|
791
|
+
const currentModel = targetModel;
|
781
792
|
const { when, action } = instructionList;
|
782
793
|
const statementParts = [`${when} ${action}`];
|
783
794
|
const effectQueries = instructionList?.effects;
|
@@ -787,33 +798,33 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
|
|
787
798
|
if (action !== "UPDATE") {
|
788
799
|
throw new RoninError({
|
789
800
|
message: `When ${queryTypeReadable} ${kind}, targeting specific fields requires the \`UPDATE\` action.`,
|
790
|
-
code: "
|
801
|
+
code: "INVALID_MODEL_VALUE",
|
791
802
|
fields: ["action"]
|
792
803
|
});
|
793
804
|
}
|
794
805
|
const fieldSelectors = fields.map((field) => {
|
795
|
-
return
|
806
|
+
return getFieldFromModel(currentModel, field.slug, "to").fieldSelector;
|
796
807
|
});
|
797
808
|
statementParts.push(`OF (${fieldSelectors.join(", ")})`);
|
798
809
|
}
|
799
810
|
statementParts.push("ON", `"${tableName}"`);
|
800
|
-
if (filterQuery || effectQueries.some((query) => findInObject(query,
|
811
|
+
if (filterQuery || effectQueries.some((query) => findInObject(query, RONIN_MODEL_SYMBOLS.FIELD))) {
|
801
812
|
statementParts.push("FOR EACH ROW");
|
802
813
|
}
|
803
814
|
if (filterQuery) {
|
804
|
-
const
|
815
|
+
const tableAlias = action === "DELETE" ? RONIN_MODEL_SYMBOLS.FIELD_PARENT_OLD : RONIN_MODEL_SYMBOLS.FIELD_PARENT_NEW;
|
805
816
|
const withStatement = handleWith(
|
806
|
-
|
807
|
-
|
817
|
+
models,
|
818
|
+
{ ...currentModel, tableAlias },
|
808
819
|
params,
|
809
|
-
filterQuery
|
810
|
-
tablePlaceholder
|
820
|
+
filterQuery
|
811
821
|
);
|
812
822
|
statementParts.push("WHEN", `(${withStatement})`);
|
813
823
|
}
|
814
824
|
const effectStatements = effectQueries.map((effectQuery) => {
|
815
|
-
return compileQueryInput(effectQuery,
|
816
|
-
returning: false
|
825
|
+
return compileQueryInput(effectQuery, models, params, {
|
826
|
+
returning: false,
|
827
|
+
parentModel: currentModel
|
817
828
|
}).main.statement;
|
818
829
|
});
|
819
830
|
if (effectStatements.length > 1) statementParts.push("BEGIN");
|
@@ -825,14 +836,14 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
|
|
825
836
|
return queryInstructions;
|
826
837
|
}
|
827
838
|
const statement = `${tableAction} TABLE "${tableName}"`;
|
828
|
-
if (kind === "
|
839
|
+
if (kind === "models") {
|
829
840
|
if (queryType === "create" || queryType === "set") {
|
830
|
-
const
|
841
|
+
const modelWithFields = addDefaultModelFields(
|
831
842
|
queryInstructions.to,
|
832
843
|
queryType === "create"
|
833
844
|
);
|
834
|
-
const
|
835
|
-
queryInstructions.to =
|
845
|
+
const modelWithPresets = addDefaultModelPresets(models, modelWithFields);
|
846
|
+
queryInstructions.to = modelWithPresets;
|
836
847
|
}
|
837
848
|
if (queryType === "create") {
|
838
849
|
const { fields } = queryInstructions.to;
|
@@ -841,7 +852,7 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
|
|
841
852
|
statement: `${statement} (${columns.join(", ")})`,
|
842
853
|
params: []
|
843
854
|
});
|
844
|
-
|
855
|
+
models.push(queryInstructions.to);
|
845
856
|
} else if (queryType === "set") {
|
846
857
|
const newSlug = queryInstructions.to?.pluralSlug;
|
847
858
|
if (newSlug) {
|
@@ -851,9 +862,9 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
|
|
851
862
|
params: []
|
852
863
|
});
|
853
864
|
}
|
854
|
-
Object.assign(
|
865
|
+
Object.assign(targetModel, queryInstructions.to);
|
855
866
|
} else if (queryType === "drop") {
|
856
|
-
|
867
|
+
models.splice(models.indexOf(targetModel), 1);
|
857
868
|
dependencyStatements.push({ statement, params: [] });
|
858
869
|
}
|
859
870
|
return queryInstructions;
|
@@ -892,7 +903,7 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
|
|
892
903
|
// src/instructions/before-after.ts
|
893
904
|
var CURSOR_SEPARATOR = ",";
|
894
905
|
var CURSOR_NULL_PLACEHOLDER = "RONIN_NULL";
|
895
|
-
var handleBeforeOrAfter = (
|
906
|
+
var handleBeforeOrAfter = (model, statementParams, instructions) => {
|
896
907
|
if (!(instructions.before || instructions.after)) {
|
897
908
|
throw new RoninError({
|
898
909
|
message: "The `before` or `after` instruction must not be empty.",
|
@@ -923,7 +934,7 @@ var handleBeforeOrAfter = (schema, statementParams, instructions, rootTable) =>
|
|
923
934
|
if (value === CURSOR_NULL_PLACEHOLDER) {
|
924
935
|
return "NULL";
|
925
936
|
}
|
926
|
-
const { field } =
|
937
|
+
const { field } = getFieldFromModel(model, key, "orderedBy");
|
927
938
|
if (field.type === "boolean") {
|
928
939
|
return prepareStatementValue(statementParams, value === "true");
|
929
940
|
}
|
@@ -949,12 +960,7 @@ var handleBeforeOrAfter = (schema, statementParams, instructions, rootTable) =>
|
|
949
960
|
for (let j = 0; j <= i; j++) {
|
950
961
|
const key = keys[j];
|
951
962
|
const value = values[j];
|
952
|
-
let { field, fieldSelector } =
|
953
|
-
schema,
|
954
|
-
key,
|
955
|
-
"orderedBy",
|
956
|
-
rootTable
|
957
|
-
);
|
963
|
+
let { field, fieldSelector } = getFieldFromModel(model, key, "orderedBy");
|
958
964
|
if (j === i) {
|
959
965
|
const closingParentheses = ")".repeat(condition.length);
|
960
966
|
const operator = value === "NULL" ? "IS NOT" : compareOperators[j];
|
@@ -976,14 +982,14 @@ var handleBeforeOrAfter = (schema, statementParams, instructions, rootTable) =>
|
|
976
982
|
};
|
977
983
|
|
978
984
|
// src/instructions/for.ts
|
979
|
-
var handleFor = (
|
985
|
+
var handleFor = (model, instructions) => {
|
980
986
|
const normalizedFor = Array.isArray(instructions.for) ? Object.fromEntries(instructions.for.map((presetSlug) => [presetSlug, null])) : instructions.for;
|
981
987
|
for (const presetSlug in normalizedFor) {
|
982
988
|
const arg = normalizedFor[presetSlug];
|
983
|
-
const preset =
|
989
|
+
const preset = model.presets?.find((preset2) => preset2.slug === presetSlug);
|
984
990
|
if (!preset) {
|
985
991
|
throw new RoninError({
|
986
|
-
message: `Preset "${presetSlug}" does not exist in
|
992
|
+
message: `Preset "${presetSlug}" does not exist in model "${model.name}".`,
|
987
993
|
code: "PRESET_NOT_FOUND"
|
988
994
|
});
|
989
995
|
}
|
@@ -991,8 +997,8 @@ var handleFor = (schema, instructions) => {
|
|
991
997
|
if (arg !== null) {
|
992
998
|
findInObject(
|
993
999
|
replacedForFilter,
|
994
|
-
|
995
|
-
(match) => match.replace(
|
1000
|
+
RONIN_MODEL_SYMBOLS.VALUE,
|
1001
|
+
(match) => match.replace(RONIN_MODEL_SYMBOLS.VALUE, arg)
|
996
1002
|
);
|
997
1003
|
}
|
998
1004
|
for (const subInstruction in replacedForFilter) {
|
@@ -1023,20 +1029,19 @@ var handleFor = (schema, instructions) => {
|
|
1023
1029
|
};
|
1024
1030
|
|
1025
1031
|
// src/instructions/including.ts
|
1026
|
-
var handleIncluding = (
|
1032
|
+
var handleIncluding = (models, model, statementParams, instruction) => {
|
1027
1033
|
let statement = "";
|
1028
|
-
let
|
1029
|
-
let rootTableName = rootTable;
|
1034
|
+
let tableSubQuery;
|
1030
1035
|
for (const ephemeralFieldSlug in instruction) {
|
1031
|
-
const
|
1032
|
-
if (
|
1033
|
-
const { queryType,
|
1036
|
+
const symbol = getSymbol(instruction[ephemeralFieldSlug]);
|
1037
|
+
if (symbol?.type !== "query") continue;
|
1038
|
+
const { queryType, queryModel, queryInstructions } = splitQuery(symbol.value);
|
1034
1039
|
let modifiableQueryInstructions = queryInstructions;
|
1035
|
-
const
|
1040
|
+
const relatedModel = getModelBySlug(models, queryModel);
|
1036
1041
|
let joinType = "LEFT";
|
1037
|
-
let relatedTableSelector = `"${
|
1042
|
+
let relatedTableSelector = `"${relatedModel.table}"`;
|
1038
1043
|
const tableAlias = `including_${ephemeralFieldSlug}`;
|
1039
|
-
const single =
|
1044
|
+
const single = queryModel !== relatedModel.pluralSlug;
|
1040
1045
|
if (!modifiableQueryInstructions?.with) {
|
1041
1046
|
joinType = "CROSS";
|
1042
1047
|
if (single) {
|
@@ -1048,35 +1053,35 @@ var handleIncluding = (schemas, statementParams, instruction, rootTable) => {
|
|
1048
1053
|
const subSelect = compileQueryInput(
|
1049
1054
|
{
|
1050
1055
|
[queryType]: {
|
1051
|
-
[
|
1056
|
+
[queryModel]: modifiableQueryInstructions
|
1052
1057
|
}
|
1053
1058
|
},
|
1054
|
-
|
1059
|
+
models,
|
1055
1060
|
statementParams
|
1056
1061
|
);
|
1057
1062
|
relatedTableSelector = `(${subSelect.main.statement})`;
|
1058
1063
|
}
|
1059
1064
|
statement += `${joinType} JOIN ${relatedTableSelector} as ${tableAlias}`;
|
1065
|
+
model.tableAlias = model.table;
|
1060
1066
|
if (joinType === "LEFT") {
|
1061
1067
|
if (!single) {
|
1062
|
-
|
1063
|
-
|
1068
|
+
tableSubQuery = `SELECT * FROM "${model.table}" LIMIT 1`;
|
1069
|
+
model.tableAlias = `sub_${model.table}`;
|
1064
1070
|
}
|
1065
1071
|
const subStatement = composeConditions(
|
1066
|
-
|
1067
|
-
|
1072
|
+
models,
|
1073
|
+
{ ...relatedModel, tableAlias },
|
1068
1074
|
statementParams,
|
1069
1075
|
"including",
|
1070
1076
|
queryInstructions?.with,
|
1071
1077
|
{
|
1072
|
-
|
1073
|
-
customTable: tableAlias
|
1078
|
+
parentModel: model
|
1074
1079
|
}
|
1075
1080
|
);
|
1076
1081
|
statement += ` ON (${subStatement})`;
|
1077
1082
|
}
|
1078
1083
|
}
|
1079
|
-
return { statement,
|
1084
|
+
return { statement, tableSubQuery };
|
1080
1085
|
};
|
1081
1086
|
|
1082
1087
|
// src/instructions/limited-to.ts
|
@@ -1088,46 +1093,43 @@ var handleLimitedTo = (single, instruction) => {
|
|
1088
1093
|
};
|
1089
1094
|
|
1090
1095
|
// src/instructions/ordered-by.ts
|
1091
|
-
var handleOrderedBy = (
|
1096
|
+
var handleOrderedBy = (model, instruction) => {
|
1092
1097
|
let statement = "";
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
rootTable
|
1099
|
-
);
|
1098
|
+
const items = [
|
1099
|
+
...(instruction.ascending || []).map((value) => ({ value, order: "ASC" })),
|
1100
|
+
...(instruction.descending || []).map((value) => ({ value, order: "DESC" }))
|
1101
|
+
];
|
1102
|
+
for (const item of items) {
|
1100
1103
|
if (statement.length > 0) {
|
1101
1104
|
statement += ", ";
|
1102
1105
|
}
|
1103
|
-
const
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
schema,
|
1109
|
-
field,
|
1110
|
-
"orderedBy.descending",
|
1111
|
-
rootTable
|
1112
|
-
);
|
1113
|
-
if (statement.length > 0) {
|
1114
|
-
statement += ", ";
|
1106
|
+
const symbol = getSymbol(item.value);
|
1107
|
+
const instructionName = item.order === "ASC" ? "orderedBy.ascending" : "orderedBy.descending";
|
1108
|
+
if (symbol?.type === "expression") {
|
1109
|
+
statement += `(${parseFieldExpression(model, instructionName, symbol.value)}) ${item.order}`;
|
1110
|
+
continue;
|
1115
1111
|
}
|
1116
|
-
const
|
1117
|
-
|
1112
|
+
const { field: modelField, fieldSelector } = getFieldFromModel(
|
1113
|
+
model,
|
1114
|
+
item.value,
|
1115
|
+
instructionName
|
1116
|
+
);
|
1117
|
+
const caseInsensitiveStatement = modelField.type === "string" ? " COLLATE NOCASE" : "";
|
1118
|
+
statement += `${fieldSelector}${caseInsensitiveStatement} ${item.order}`;
|
1118
1119
|
}
|
1119
1120
|
return `ORDER BY ${statement}`;
|
1120
1121
|
};
|
1121
1122
|
|
1122
1123
|
// src/instructions/selecting.ts
|
1123
|
-
var handleSelecting = (
|
1124
|
+
var handleSelecting = (model, statementParams, instructions) => {
|
1124
1125
|
let isJoining = false;
|
1125
1126
|
let statement = instructions.selecting ? instructions.selecting.map((slug) => {
|
1126
|
-
return
|
1127
|
+
return getFieldFromModel(model, slug, "selecting").fieldSelector;
|
1127
1128
|
}).join(", ") : "*";
|
1128
1129
|
if (instructions.including) {
|
1129
1130
|
const filteredObject = Object.entries(instructions.including).filter(([_, value]) => {
|
1130
|
-
const
|
1131
|
+
const symbol = getSymbol(value);
|
1132
|
+
const hasQuery = symbol?.type === "query";
|
1131
1133
|
if (hasQuery) isJoining = true;
|
1132
1134
|
return !hasQuery;
|
1133
1135
|
});
|
@@ -1143,12 +1145,12 @@ var handleSelecting = (schema, statementParams, instructions) => {
|
|
1143
1145
|
};
|
1144
1146
|
|
1145
1147
|
// src/instructions/to.ts
|
1146
|
-
var handleTo = (
|
1148
|
+
var handleTo = (models, model, statementParams, queryType, dependencyStatements, instructions, parentModel) => {
|
1147
1149
|
const currentTime = (/* @__PURE__ */ new Date()).toISOString();
|
1148
1150
|
const { with: withInstruction, to: toInstruction } = instructions;
|
1149
1151
|
const defaultFields = {};
|
1150
1152
|
if (queryType === "create") {
|
1151
|
-
defaultFields.id = toInstruction.id || generateRecordId(
|
1153
|
+
defaultFields.id = toInstruction.id || generateRecordId(model.idPrefix);
|
1152
1154
|
}
|
1153
1155
|
defaultFields.ronin = {
|
1154
1156
|
// If records are being created, set their creation time.
|
@@ -1158,20 +1160,20 @@ var handleTo = (schemas, schema, statementParams, queryType, dependencyStatement
|
|
1158
1160
|
// Allow for overwriting the default values provided above.
|
1159
1161
|
...toInstruction.ronin
|
1160
1162
|
};
|
1161
|
-
const
|
1162
|
-
if (
|
1163
|
-
let {
|
1164
|
-
const
|
1163
|
+
const symbol = getSymbol(toInstruction);
|
1164
|
+
if (symbol?.type === "query") {
|
1165
|
+
let { queryModel: subQueryModelSlug, queryInstructions: subQueryInstructions } = splitQuery(symbol.value);
|
1166
|
+
const subQueryModel = getModelBySlug(models, subQueryModelSlug);
|
1165
1167
|
const subQuerySelectedFields = subQueryInstructions?.selecting;
|
1166
1168
|
const subQueryIncludedFields = subQueryInstructions?.including;
|
1167
1169
|
const subQueryFields = [
|
1168
|
-
...subQuerySelectedFields || (
|
1170
|
+
...subQuerySelectedFields || (subQueryModel.fields || []).map((field) => field.slug),
|
1169
1171
|
...subQueryIncludedFields ? Object.keys(
|
1170
1172
|
flatten(subQueryIncludedFields || {})
|
1171
1173
|
) : []
|
1172
1174
|
];
|
1173
1175
|
for (const field of subQueryFields || []) {
|
1174
|
-
|
1176
|
+
getFieldFromModel(model, field, "to");
|
1175
1177
|
}
|
1176
1178
|
const defaultFieldsToAdd = subQuerySelectedFields ? Object.entries(flatten(defaultFields)).filter(([key]) => {
|
1177
1179
|
return !subQuerySelectedFields.includes(key);
|
@@ -1184,18 +1186,15 @@ var handleTo = (schemas, schema, statementParams, queryType, dependencyStatement
|
|
1184
1186
|
...subQueryInstructions.including
|
1185
1187
|
};
|
1186
1188
|
}
|
1187
|
-
return compileQueryInput(
|
1189
|
+
return compileQueryInput(symbol.value, models, statementParams).main.statement;
|
1188
1190
|
}
|
1189
1191
|
Object.assign(toInstruction, defaultFields);
|
1190
1192
|
for (const fieldSlug in toInstruction) {
|
1191
1193
|
const fieldValue = toInstruction[fieldSlug];
|
1192
|
-
const fieldDetails =
|
1194
|
+
const fieldDetails = getFieldFromModel(model, fieldSlug, "to");
|
1193
1195
|
if (fieldDetails.field.type === "reference" && fieldDetails.field.kind === "many") {
|
1194
1196
|
delete toInstruction[fieldSlug];
|
1195
|
-
const
|
1196
|
-
schema,
|
1197
|
-
fieldDetails.field
|
1198
|
-
);
|
1197
|
+
const associativeModelSlug = composeAssociationModelSlug(model, fieldDetails.field);
|
1199
1198
|
const composeStatement = (subQueryType, value) => {
|
1200
1199
|
const source = queryType === "create" ? { id: toInstruction.id } : withInstruction;
|
1201
1200
|
const recordDetails = { source };
|
@@ -1203,10 +1202,10 @@ var handleTo = (schemas, schema, statementParams, queryType, dependencyStatement
|
|
1203
1202
|
return compileQueryInput(
|
1204
1203
|
{
|
1205
1204
|
[subQueryType]: {
|
1206
|
-
[
|
1205
|
+
[associativeModelSlug]: subQueryType === "create" ? { to: recordDetails } : { with: recordDetails }
|
1207
1206
|
}
|
1208
1207
|
},
|
1209
|
-
|
1208
|
+
models,
|
1210
1209
|
[],
|
1211
1210
|
{ returning: false }
|
1212
1211
|
).main;
|
@@ -1226,26 +1225,19 @@ var handleTo = (schemas, schema, statementParams, queryType, dependencyStatement
|
|
1226
1225
|
}
|
1227
1226
|
}
|
1228
1227
|
}
|
1229
|
-
let statement = composeConditions(
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
"to",
|
1234
|
-
toInstruction,
|
1235
|
-
{
|
1236
|
-
rootTable,
|
1237
|
-
type: queryType === "create" ? "fields" : void 0
|
1238
|
-
}
|
1239
|
-
);
|
1228
|
+
let statement = composeConditions(models, model, statementParams, "to", toInstruction, {
|
1229
|
+
parentModel,
|
1230
|
+
type: queryType === "create" ? "fields" : void 0
|
1231
|
+
});
|
1240
1232
|
if (queryType === "create") {
|
1241
1233
|
const deepStatement = composeConditions(
|
1242
|
-
|
1243
|
-
|
1234
|
+
models,
|
1235
|
+
model,
|
1244
1236
|
statementParams,
|
1245
1237
|
"to",
|
1246
1238
|
toInstruction,
|
1247
1239
|
{
|
1248
|
-
|
1240
|
+
parentModel,
|
1249
1241
|
type: "values"
|
1250
1242
|
}
|
1251
1243
|
);
|
@@ -1257,24 +1249,23 @@ var handleTo = (schemas, schema, statementParams, queryType, dependencyStatement
|
|
1257
1249
|
};
|
1258
1250
|
|
1259
1251
|
// src/utils/index.ts
|
1260
|
-
var compileQueryInput = (query,
|
1252
|
+
var compileQueryInput = (query, models, statementParams, options) => {
|
1261
1253
|
const parsedQuery = splitQuery(query);
|
1262
|
-
const { queryType,
|
1263
|
-
const
|
1264
|
-
const single =
|
1265
|
-
let instructions = formatIdentifiers(
|
1266
|
-
let table = getTableForSchema(schema);
|
1254
|
+
const { queryType, queryModel, queryInstructions } = parsedQuery;
|
1255
|
+
const model = getModelBySlug(models, queryModel);
|
1256
|
+
const single = queryModel !== model.pluralSlug;
|
1257
|
+
let instructions = formatIdentifiers(model, queryInstructions);
|
1267
1258
|
const dependencyStatements = [];
|
1268
1259
|
const returning = options?.returning ?? true;
|
1269
|
-
instructions =
|
1270
|
-
|
1271
|
-
{ queryType,
|
1260
|
+
instructions = addModelQueries(
|
1261
|
+
models,
|
1262
|
+
{ queryType, queryModel, queryInstructions: instructions },
|
1272
1263
|
dependencyStatements
|
1273
1264
|
);
|
1274
1265
|
if (instructions && Object.hasOwn(instructions, "for")) {
|
1275
|
-
instructions = handleFor(
|
1266
|
+
instructions = handleFor(model, instructions);
|
1276
1267
|
}
|
1277
|
-
const { columns, isJoining } = handleSelecting(
|
1268
|
+
const { columns, isJoining } = handleSelecting(model, statementParams, {
|
1278
1269
|
selecting: instructions?.selecting,
|
1279
1270
|
including: instructions?.including
|
1280
1271
|
});
|
@@ -1298,21 +1289,21 @@ var compileQueryInput = (query, schemas, statementParams, options) => {
|
|
1298
1289
|
}
|
1299
1290
|
let isJoiningMultipleRows = false;
|
1300
1291
|
if (isJoining) {
|
1301
|
-
const {
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
statement += `(${
|
1292
|
+
const { statement: including, tableSubQuery } = handleIncluding(
|
1293
|
+
models,
|
1294
|
+
model,
|
1295
|
+
statementParams,
|
1296
|
+
instructions?.including
|
1297
|
+
);
|
1298
|
+
if (tableSubQuery) {
|
1299
|
+
statement += `(${tableSubQuery}) as ${model.tableAlias} `;
|
1309
1300
|
isJoiningMultipleRows = true;
|
1310
1301
|
} else {
|
1311
|
-
statement += `"${table}" `;
|
1302
|
+
statement += `"${model.table}" `;
|
1312
1303
|
}
|
1313
1304
|
statement += `${including} `;
|
1314
1305
|
} else {
|
1315
|
-
statement += `"${table}" `;
|
1306
|
+
statement += `"${model.table}" `;
|
1316
1307
|
}
|
1317
1308
|
if (queryType === "create" || queryType === "set") {
|
1318
1309
|
if (!isObject(instructions.to) || Object.keys(instructions.to).length === 0) {
|
@@ -1323,24 +1314,24 @@ var compileQueryInput = (query, schemas, statementParams, options) => {
|
|
1323
1314
|
});
|
1324
1315
|
}
|
1325
1316
|
const toStatement = handleTo(
|
1326
|
-
|
1327
|
-
|
1317
|
+
models,
|
1318
|
+
model,
|
1328
1319
|
statementParams,
|
1329
1320
|
queryType,
|
1330
1321
|
dependencyStatements,
|
1331
1322
|
{ with: instructions.with, to: instructions.to },
|
1332
|
-
|
1323
|
+
options?.parentModel
|
1333
1324
|
);
|
1334
1325
|
statement += `${toStatement} `;
|
1335
1326
|
}
|
1336
1327
|
const conditions = [];
|
1337
1328
|
if (queryType !== "create" && instructions && Object.hasOwn(instructions, "with")) {
|
1338
1329
|
const withStatement = handleWith(
|
1339
|
-
|
1340
|
-
|
1330
|
+
models,
|
1331
|
+
model,
|
1341
1332
|
statementParams,
|
1342
1333
|
instructions?.with,
|
1343
|
-
|
1334
|
+
options?.parentModel
|
1344
1335
|
);
|
1345
1336
|
if (withStatement.length > 0) conditions.push(withStatement);
|
1346
1337
|
}
|
@@ -1364,18 +1355,13 @@ var compileQueryInput = (query, schemas, statementParams, options) => {
|
|
1364
1355
|
queries: [query]
|
1365
1356
|
});
|
1366
1357
|
}
|
1367
|
-
const beforeAndAfterStatement = handleBeforeOrAfter(
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
orderedBy: instructions.orderedBy,
|
1375
|
-
limitedTo: instructions.limitedTo
|
1376
|
-
},
|
1377
|
-
isJoining ? table : void 0
|
1378
|
-
);
|
1358
|
+
const beforeAndAfterStatement = handleBeforeOrAfter(model, statementParams, {
|
1359
|
+
before: instructions.before,
|
1360
|
+
after: instructions.after,
|
1361
|
+
with: instructions.with,
|
1362
|
+
orderedBy: instructions.orderedBy,
|
1363
|
+
limitedTo: instructions.limitedTo
|
1364
|
+
});
|
1379
1365
|
conditions.push(beforeAndAfterStatement);
|
1380
1366
|
}
|
1381
1367
|
if (conditions.length > 0) {
|
@@ -1386,11 +1372,7 @@ var compileQueryInput = (query, schemas, statementParams, options) => {
|
|
1386
1372
|
}
|
1387
1373
|
}
|
1388
1374
|
if (instructions?.orderedBy) {
|
1389
|
-
const orderedByStatement = handleOrderedBy(
|
1390
|
-
schema,
|
1391
|
-
instructions.orderedBy,
|
1392
|
-
isJoining ? table : void 0
|
1393
|
-
);
|
1375
|
+
const orderedByStatement = handleOrderedBy(model, instructions.orderedBy);
|
1394
1376
|
statement += `${orderedByStatement} `;
|
1395
1377
|
}
|
1396
1378
|
if (queryType === "get" && !isJoiningMultipleRows && (single || instructions?.limitedTo)) {
|
@@ -1411,19 +1393,19 @@ var compileQueryInput = (query, schemas, statementParams, options) => {
|
|
1411
1393
|
};
|
1412
1394
|
|
1413
1395
|
// src/index.ts
|
1414
|
-
var compileQueries = (queries,
|
1415
|
-
const
|
1416
|
-
return
|
1396
|
+
var compileQueries = (queries, models, options) => {
|
1397
|
+
const modelList = addSystemModels(models).map((model) => {
|
1398
|
+
return addDefaultModelFields(model, true);
|
1417
1399
|
});
|
1418
|
-
const
|
1419
|
-
return
|
1400
|
+
const modelListWithPresets = modelList.map((model) => {
|
1401
|
+
return addDefaultModelPresets(modelList, model);
|
1420
1402
|
});
|
1421
1403
|
const dependencyStatements = [];
|
1422
1404
|
const mainStatements = [];
|
1423
1405
|
for (const query of queries) {
|
1424
1406
|
const result = compileQueryInput(
|
1425
1407
|
query,
|
1426
|
-
|
1408
|
+
modelListWithPresets,
|
1427
1409
|
options?.inlineParams ? null : []
|
1428
1410
|
);
|
1429
1411
|
dependencyStatements.push(...result.dependencies);
|