@dbsp/nql 1.0.1 → 1.0.3

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 CHANGED
@@ -438,6 +438,8 @@ interface NqlCompilerOptions {
438
438
  readonly recursiveKeywords?: readonly string[];
439
439
  /** BATCH-001: Named parameters for ANY(:param) expressions */
440
440
  readonly params?: Readonly<Record<string, unknown>>;
441
+ /** Maximum number of items allowed in an ANY(:param) array. Defaults to MAX_ANY_ITEMS (10000). */
442
+ readonly maxAnyItems?: number;
441
443
  }
442
444
 
443
445
  /**
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { isRankingWindowFunction } from '@dbsp/types';
1
2
  import { createToken, Lexer, CstParser } from 'chevrotain';
2
3
 
3
4
  // src/errors/types.ts
@@ -787,6 +788,7 @@ function getISOWeekCount(year) {
787
788
  }
788
789
 
789
790
  // src/compiler/compile-expression.ts
791
+ var MAX_ANY_ITEMS = 1e4;
790
792
  function compileLogical(expr, ctx, fns, aliasContext, outerAliases) {
791
793
  if (expr.type === "binary") {
792
794
  const binary = expr;
@@ -961,7 +963,19 @@ function compileMembership(expr, ctx, fns, aliasContext, outerAliases) {
961
963
  }
962
964
  validateWhereField(ctx, field2, aliasContext, anyExpr.column);
963
965
  const rawValues = ctx.params[anyExpr.paramName];
964
- const values2 = Array.isArray(rawValues) ? rawValues : [];
966
+ if (!Array.isArray(rawValues)) {
967
+ throw new NqlSemanticException(
968
+ NqlErrorCodes.SEM_INVALID_SYNTAX,
969
+ `ANY(:${anyExpr.paramName}) requires an array argument but received ${rawValues === void 0 ? "undefined (parameter not bound)" : typeof rawValues}`
970
+ );
971
+ }
972
+ if (rawValues.length > ctx.maxAnyItems) {
973
+ throw new NqlSemanticException(
974
+ NqlErrorCodes.SEM_INVALID_SYNTAX,
975
+ `ANY(:${anyExpr.paramName}) array length ${rawValues.length} exceeds maximum of ${ctx.maxAnyItems}`
976
+ );
977
+ }
978
+ const values2 = rawValues;
965
979
  return { kind: "any", field: field2, values: values2 };
966
980
  }
967
981
  const inExpr = expr;
@@ -995,7 +1009,6 @@ function compileMembership(expr, ctx, fns, aliasContext, outerAliases) {
995
1009
  const result2 = {
996
1010
  kind: "in",
997
1011
  field,
998
- values: [],
999
1012
  subquery
1000
1013
  };
1001
1014
  if (inExpr.negated) {
@@ -1438,8 +1451,9 @@ function resolveBindingsInWhere(where, bindings) {
1438
1451
  if (!bindings || bindings.size === 0) return where;
1439
1452
  if (where.kind === "in") {
1440
1453
  const inWhere = where;
1441
- if (inWhere.values && inWhere.values.length === 1) {
1442
- const val = inWhere.values[0];
1454
+ const inValues = inWhere.subquery ? void 0 : inWhere.values;
1455
+ if (inValues && inValues.length === 1) {
1456
+ const val = inValues[0];
1443
1457
  if (val && typeof val === "object" && "$ref" in val) {
1444
1458
  const ref = val.$ref;
1445
1459
  if (bindings.has(ref)) {
@@ -1459,7 +1473,6 @@ function resolveBindingsInWhere(where, bindings) {
1459
1473
  return {
1460
1474
  kind: "in",
1461
1475
  field: inWhere.field,
1462
- values: [],
1463
1476
  subquery: cteRef
1464
1477
  };
1465
1478
  }
@@ -1494,8 +1507,6 @@ function extractBindName(stmt) {
1494
1507
  }
1495
1508
  return void 0;
1496
1509
  }
1497
-
1498
- // src/compiler/compile-select.ts
1499
1510
  function compileSelectClause(clause, ctx, fns) {
1500
1511
  if (clause.items.length === 1 && clause.items[0]?.type === "star") {
1501
1512
  return { type: "all" };
@@ -1620,17 +1631,36 @@ function compileSelectExpression(item, ctx, fns) {
1620
1631
  }
1621
1632
  return { field: f, direction: o.direction };
1622
1633
  }) : void 0;
1634
+ const over = {
1635
+ ...partitionBy && { partitionBy },
1636
+ ...orderBy && { orderBy }
1637
+ };
1638
+ const alias = exprItem.alias ?? fn;
1639
+ if (isRankingWindowFunction(fn)) {
1640
+ return {
1641
+ kind: "window",
1642
+ function: fn,
1643
+ alias,
1644
+ over
1645
+ };
1646
+ }
1647
+ if (fn === "lag" || fn === "lead") {
1648
+ return {
1649
+ kind: "window",
1650
+ function: fn,
1651
+ field: field ?? "",
1652
+ alias,
1653
+ ...offset !== void 0 && { offset },
1654
+ ...defaultValue !== void 0 && { defaultValue },
1655
+ over
1656
+ };
1657
+ }
1623
1658
  return {
1624
1659
  kind: "window",
1625
1660
  function: fn,
1626
1661
  ...field !== void 0 && { field },
1627
- alias: exprItem.alias ?? fn,
1628
- ...offset !== void 0 && { offset },
1629
- ...defaultValue !== void 0 && { defaultValue },
1630
- over: {
1631
- ...partitionBy && { partitionBy },
1632
- ...orderBy && { orderBy }
1633
- }
1662
+ alias,
1663
+ over
1634
1664
  };
1635
1665
  }
1636
1666
  if (expr.type === "subquery") {
@@ -1900,13 +1930,22 @@ var NqlCompiler = class {
1900
1930
  const recursive = options?.recursiveKeywords ?? DEFAULT_RECURSIVE_KEYWORDS;
1901
1931
  const recursiveKeywords = new Set(recursive.map((k) => k.toLowerCase()));
1902
1932
  const validator = schema ? new ColumnValidator(schema) : null;
1933
+ const maxAnyItemsRaw = options?.maxAnyItems;
1934
+ if (maxAnyItemsRaw !== void 0) {
1935
+ if (!Number.isSafeInteger(maxAnyItemsRaw) || maxAnyItemsRaw <= 0) {
1936
+ throw new Error(
1937
+ `Invalid maxAnyItems: ${maxAnyItemsRaw}. Must be a positive integer.`
1938
+ );
1939
+ }
1940
+ }
1903
1941
  this.ctx = {
1904
1942
  currentFromTable: void 0,
1905
1943
  currentRelationTarget: void 0,
1906
1944
  pseudoColumnKeywords,
1907
1945
  recursiveKeywords,
1908
1946
  validator,
1909
- params: options?.params ?? {}
1947
+ params: options?.params ?? {},
1948
+ maxAnyItems: maxAnyItemsRaw ?? MAX_ANY_ITEMS
1910
1949
  };
1911
1950
  this.fns = {
1912
1951
  compileQuery: (query, ctx) => compileQuery(query, ctx, this.fns),