@dbsp/nql 1.0.3 → 1.1.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/dist/index.d.ts +2 -0
- package/dist/index.js +79 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -440,6 +440,8 @@ interface NqlCompilerOptions {
|
|
|
440
440
|
readonly params?: Readonly<Record<string, unknown>>;
|
|
441
441
|
/** Maximum number of items allowed in an ANY(:param) array. Defaults to MAX_ANY_ITEMS (10000). */
|
|
442
442
|
readonly maxAnyItems?: number;
|
|
443
|
+
/** Permit a where-less update/delete to compile to an unfiltered, all-rows mutation. Default false — a where-less mutation throws unless this is set. */
|
|
444
|
+
readonly allowUnfilteredMutations?: boolean;
|
|
443
445
|
}
|
|
444
446
|
|
|
445
447
|
/**
|
package/dist/index.js
CHANGED
|
@@ -387,6 +387,21 @@ function applyIncludeLimit(includes, path, limit) {
|
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
// src/compiler/compile-query.ts
|
|
390
|
+
function compileNestedQuery(query, ctx, fns, bindings) {
|
|
391
|
+
const savedContext = {
|
|
392
|
+
currentFromTable: ctx.currentFromTable,
|
|
393
|
+
currentRelationTarget: ctx.currentRelationTarget
|
|
394
|
+
};
|
|
395
|
+
try {
|
|
396
|
+
if (bindings) {
|
|
397
|
+
return compileQuery(query, ctx, fns, bindings);
|
|
398
|
+
}
|
|
399
|
+
return fns.compileQuery(query, ctx);
|
|
400
|
+
} finally {
|
|
401
|
+
ctx.currentFromTable = savedContext.currentFromTable;
|
|
402
|
+
ctx.currentRelationTarget = savedContext.currentRelationTarget;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
390
405
|
function compileQuery(query, ctx, fns, bindings) {
|
|
391
406
|
const setClauseIndex = query.clauses.findIndex(
|
|
392
407
|
(c) => c.type === "setOperation"
|
|
@@ -569,6 +584,12 @@ function getExplicitColumnCount(intent) {
|
|
|
569
584
|
}
|
|
570
585
|
function compileSetOperation(query, setClauseIndex, ctx, fns, bindings) {
|
|
571
586
|
const setClause = query.clauses[setClauseIndex];
|
|
587
|
+
if (setClauseIndex < query.clauses.length - 1) {
|
|
588
|
+
const trailingTypes = query.clauses.slice(setClauseIndex + 1).map((c) => c.type).join(", ");
|
|
589
|
+
throw new Error(
|
|
590
|
+
`Clauses after a set operation are not supported (found: ${trailingTypes}). Wrap the set operation in a subquery or move the clause before the set operation.`
|
|
591
|
+
);
|
|
592
|
+
}
|
|
572
593
|
const leftQuery = {
|
|
573
594
|
table: query.table,
|
|
574
595
|
clauses: query.clauses.slice(0, setClauseIndex)
|
|
@@ -576,7 +597,7 @@ function compileSetOperation(query, setClauseIndex, ctx, fns, bindings) {
|
|
|
576
597
|
const left = compileQuery(leftQuery, ctx, fns, bindings);
|
|
577
598
|
let right;
|
|
578
599
|
if (setClause.right) {
|
|
579
|
-
right =
|
|
600
|
+
right = compileNestedQuery(setClause.right, ctx, fns, bindings);
|
|
580
601
|
} else if (setClause.boundName) {
|
|
581
602
|
const bound = bindings?.get(setClause.boundName);
|
|
582
603
|
if (!bound) {
|
|
@@ -646,7 +667,7 @@ function compileWithQuery(astNode, ctx, fns) {
|
|
|
646
667
|
try {
|
|
647
668
|
const ctes = [];
|
|
648
669
|
for (const cte of astNode.ctes) {
|
|
649
|
-
const bodyResult =
|
|
670
|
+
const bodyResult = compileNestedQuery(cte.query, ctx, fns);
|
|
650
671
|
if ("kind" in bodyResult && bodyResult.kind === "setOperation") {
|
|
651
672
|
throw new NqlSemanticException(
|
|
652
673
|
NqlErrorCodes.SEM_UNKNOWN_TABLE,
|
|
@@ -1005,7 +1026,11 @@ function compileMembership(expr, ctx, fns, aliasContext, outerAliases) {
|
|
|
1005
1026
|
return expandDateRangeList(field, dateRangeValues, inExpr.negated);
|
|
1006
1027
|
}
|
|
1007
1028
|
} else if ("type" in inExpr.values && inExpr.values.type === "subquery") {
|
|
1008
|
-
const subquery =
|
|
1029
|
+
const subquery = compileNestedQuery(
|
|
1030
|
+
inExpr.values.query,
|
|
1031
|
+
ctx,
|
|
1032
|
+
fns
|
|
1033
|
+
);
|
|
1009
1034
|
const result2 = {
|
|
1010
1035
|
kind: "in",
|
|
1011
1036
|
field,
|
|
@@ -1331,7 +1356,11 @@ function compileInsert(insert, ctx) {
|
|
|
1331
1356
|
};
|
|
1332
1357
|
}
|
|
1333
1358
|
function compileInsertFrom(insertFrom, ctx, fns, bindings) {
|
|
1359
|
+
ctx.validator?.validateTable(insertFrom.table);
|
|
1334
1360
|
const sourceQuery = bindings?.get(insertFrom.source);
|
|
1361
|
+
if (!sourceQuery) {
|
|
1362
|
+
ctx.validator?.validateTable(insertFrom.source);
|
|
1363
|
+
}
|
|
1335
1364
|
ctx.currentFromTable = insertFrom.source;
|
|
1336
1365
|
return {
|
|
1337
1366
|
type: "insert_from",
|
|
@@ -1365,6 +1394,11 @@ function compileUpdate(update, ctx, fns, bindings) {
|
|
|
1365
1394
|
)
|
|
1366
1395
|
};
|
|
1367
1396
|
}
|
|
1397
|
+
if (!ctx.allowUnfilteredMutations) {
|
|
1398
|
+
throw new Error(
|
|
1399
|
+
"update without a where clause would affect all rows; pass { allowUnfilteredMutations: true } to the compiler to allow an unfiltered update"
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1368
1402
|
return {
|
|
1369
1403
|
type: "update",
|
|
1370
1404
|
table: update.table,
|
|
@@ -1385,6 +1419,11 @@ function compileDelete(del, ctx, fns, bindings) {
|
|
|
1385
1419
|
)
|
|
1386
1420
|
};
|
|
1387
1421
|
}
|
|
1422
|
+
if (!ctx.allowUnfilteredMutations) {
|
|
1423
|
+
throw new Error(
|
|
1424
|
+
"delete without a where clause would affect all rows; pass { allowUnfilteredMutations: true } to the compiler to allow an unfiltered delete"
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1388
1427
|
return {
|
|
1389
1428
|
type: "delete",
|
|
1390
1429
|
table: del.table,
|
|
@@ -1401,6 +1440,11 @@ function compileUpsert(upsert, ctx) {
|
|
|
1401
1440
|
for (const col of upsert.conflictColumns) {
|
|
1402
1441
|
ctx.validator?.validateColumn(upsert.table, col);
|
|
1403
1442
|
}
|
|
1443
|
+
if (upsert.where) {
|
|
1444
|
+
throw new Error(
|
|
1445
|
+
"conditional upsert is not yet supported: a WHERE clause on `upsert` (ON CONFLICT DO UPDATE ... WHERE) is parsed but cannot be honored by the SQL generator yet. Remove the WHERE, or use a plain conditional update. Tracked for a future release."
|
|
1446
|
+
);
|
|
1447
|
+
}
|
|
1404
1448
|
return {
|
|
1405
1449
|
type: "upsert",
|
|
1406
1450
|
table: upsert.table,
|
|
@@ -1410,7 +1454,14 @@ function compileUpsert(upsert, ctx) {
|
|
|
1410
1454
|
};
|
|
1411
1455
|
}
|
|
1412
1456
|
function compileUpsertFrom(upsertFrom, ctx, fns, bindings) {
|
|
1457
|
+
ctx.validator?.validateTable(upsertFrom.table);
|
|
1413
1458
|
const sourceQuery = bindings?.get(upsertFrom.source);
|
|
1459
|
+
if (!sourceQuery) {
|
|
1460
|
+
ctx.validator?.validateTable(upsertFrom.source);
|
|
1461
|
+
}
|
|
1462
|
+
for (const col of upsertFrom.conflictColumns) {
|
|
1463
|
+
ctx.validator?.validateColumn(upsertFrom.table, col);
|
|
1464
|
+
}
|
|
1414
1465
|
ctx.currentFromTable = upsertFrom.source;
|
|
1415
1466
|
return {
|
|
1416
1467
|
type: "upsert_from",
|
|
@@ -1666,7 +1717,7 @@ function compileSelectExpression(item, ctx, fns) {
|
|
|
1666
1717
|
if (expr.type === "subquery") {
|
|
1667
1718
|
return {
|
|
1668
1719
|
kind: "subquery",
|
|
1669
|
-
query:
|
|
1720
|
+
query: compileNestedQuery(expr.query, ctx, fns),
|
|
1670
1721
|
...exprItem.alias !== void 0 && { as: exprItem.alias }
|
|
1671
1722
|
};
|
|
1672
1723
|
}
|
|
@@ -1945,7 +1996,8 @@ var NqlCompiler = class {
|
|
|
1945
1996
|
recursiveKeywords,
|
|
1946
1997
|
validator,
|
|
1947
1998
|
params: options?.params ?? {},
|
|
1948
|
-
maxAnyItems: maxAnyItemsRaw ?? MAX_ANY_ITEMS
|
|
1999
|
+
maxAnyItems: maxAnyItemsRaw ?? MAX_ANY_ITEMS,
|
|
2000
|
+
allowUnfilteredMutations: options?.allowUnfilteredMutations ?? false
|
|
1949
2001
|
};
|
|
1950
2002
|
this.fns = {
|
|
1951
2003
|
compileQuery: (query, ctx) => compileQuery(query, ctx, this.fns),
|
|
@@ -1963,15 +2015,27 @@ var NqlCompiler = class {
|
|
|
1963
2015
|
if (program.statements.length === 1) {
|
|
1964
2016
|
return this.compileSingleStatement(program.statements[0]);
|
|
1965
2017
|
}
|
|
2018
|
+
for (let i = 0; i < program.statements.length - 1; i++) {
|
|
2019
|
+
const stmt = program.statements[i];
|
|
2020
|
+
const bindName = extractBindName(stmt);
|
|
2021
|
+
if (!bindName) {
|
|
2022
|
+
throw new Error(
|
|
2023
|
+
`Multiple statements require explicit binding: statement ${i + 1} of ${program.statements.length} has no '| bind <name>' clause. Add a \`| bind <name>\` clause to each statement except the last, or pass a single statement.`
|
|
2024
|
+
);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
1966
2027
|
const bindings = /* @__PURE__ */ new Map();
|
|
1967
2028
|
const mutationBindings = /* @__PURE__ */ new Map();
|
|
2029
|
+
const materializedBindStatements = /* @__PURE__ */ new Set();
|
|
1968
2030
|
let lastResult = {};
|
|
1969
|
-
for (
|
|
2031
|
+
for (let i = 0; i < program.statements.length; i++) {
|
|
2032
|
+
const stmt = program.statements[i];
|
|
1970
2033
|
lastResult = this.compileSingleStatement(stmt, bindings);
|
|
1971
2034
|
const bindName = extractBindName(stmt);
|
|
1972
2035
|
if (bindName) {
|
|
1973
2036
|
if (lastResult.query) {
|
|
1974
2037
|
bindings.set(bindName, lastResult.query);
|
|
2038
|
+
materializedBindStatements.add(i);
|
|
1975
2039
|
} else if (lastResult.mutation?.returning?.length) {
|
|
1976
2040
|
mutationBindings.set(bindName, lastResult.mutation);
|
|
1977
2041
|
bindings.set(bindName, {
|
|
@@ -1982,9 +2046,18 @@ var NqlCompiler = class {
|
|
|
1982
2046
|
fields: [...lastResult.mutation.returning]
|
|
1983
2047
|
}
|
|
1984
2048
|
});
|
|
2049
|
+
materializedBindStatements.add(i);
|
|
1985
2050
|
}
|
|
1986
2051
|
}
|
|
1987
2052
|
}
|
|
2053
|
+
for (let i = 0; i < program.statements.length - 1; i++) {
|
|
2054
|
+
const bindName = extractBindName(program.statements[i]);
|
|
2055
|
+
if (bindName && !materializedBindStatements.has(i)) {
|
|
2056
|
+
throw new Error(
|
|
2057
|
+
`statement ${i + 1} of ${program.statements.length} binds '${bindName}' but produces no referenceable result \u2014 a mutation used as a binding must include a \`returning\` clause.`
|
|
2058
|
+
);
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
1988
2061
|
const hasMutationBindings = mutationBindings.size > 0;
|
|
1989
2062
|
if (bindings.size > 0) {
|
|
1990
2063
|
return {
|