@carbonorm/carbonnode 3.9.4 → 3.9.6

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.esm.js CHANGED
@@ -11,6 +11,7 @@ var C6Constants = {
11
11
  ADDTIME: 'ADDTIME',
12
12
  AS: 'AS',
13
13
  ASC: 'ASC',
14
+ AND: 'AND',
14
15
  BETWEEN: 'BETWEEN',
15
16
  CONCAT: 'CONCAT',
16
17
  CONVERT_TZ: 'CONVERT_TZ',
@@ -84,6 +85,7 @@ var C6Constants = {
84
85
  NOW: 'NOW',
85
86
  NULL: 'NULL',
86
87
  ORDER: 'ORDER',
88
+ OR: 'OR',
87
89
  PAGE: 'PAGE',
88
90
  PAGINATION: 'PAGINATION',
89
91
  RIGHT_OUTER: 'RIGHT_OUTER',
@@ -1289,6 +1291,10 @@ var AggregateBuilder = /** @class */ (function (_super) {
1289
1291
  args.pop();
1290
1292
  }
1291
1293
  var F = String(fn).toUpperCase();
1294
+ var isGeomFromText = F === C6C.ST_GEOMFROMTEXT.toUpperCase();
1295
+ if (args.length === 1 && Array.isArray(args[0])) {
1296
+ args = args[0];
1297
+ }
1292
1298
  // Parameter placeholder helper: [C6C.PARAM, value]
1293
1299
  if (F === C6C.PARAM) {
1294
1300
  if (!params) {
@@ -1318,7 +1324,7 @@ var AggregateBuilder = /** @class */ (function (_super) {
1318
1324
  }
1319
1325
  var identifierPathRegex = /^[A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_][A-Za-z0-9_]*$/;
1320
1326
  var argList = args
1321
- .map(function (arg) {
1327
+ .map(function (arg, index) {
1322
1328
  if (Array.isArray(arg))
1323
1329
  return _this.buildAggregateField(arg, params);
1324
1330
  if (typeof arg === 'string') {
@@ -1329,6 +1335,15 @@ var AggregateBuilder = /** @class */ (function (_super) {
1329
1335
  // Treat numeric-looking strings as literals, not identifier paths
1330
1336
  if (isNumericString(arg))
1331
1337
  return arg;
1338
+ if (isGeomFromText && index === 0) {
1339
+ var trimmed = arg.trim();
1340
+ var alreadyQuoted = trimmed.startsWith("'") && trimmed.endsWith("'") && trimmed.length >= 2;
1341
+ if (alreadyQuoted) {
1342
+ return trimmed;
1343
+ }
1344
+ var escaped = arg.replace(/'/g, "''");
1345
+ return "'".concat(escaped, "'");
1346
+ }
1332
1347
  return arg;
1333
1348
  }
1334
1349
  return String(arg);
@@ -1436,24 +1451,43 @@ var ConditionBuilder = /** @class */ (function (_super) {
1436
1451
  var _this = _super !== null && _super.apply(this, arguments) || this;
1437
1452
  _this.aliasMap = {};
1438
1453
  _this.derivedAliases = new Set();
1439
- _this.OPERATORS = new Set([
1440
- C6C.EQUAL, C6C.NOT_EQUAL, C6C.LESS_THAN, C6C.LESS_THAN_OR_EQUAL_TO,
1441
- C6C.GREATER_THAN, C6C.GREATER_THAN_OR_EQUAL_TO,
1442
- C6C.LIKE, C6C.NOT_LIKE,
1443
- C6C.IN, C6C.NOT_IN, 'NOT IN',
1444
- C6C.IS, C6C.IS_NOT,
1445
- C6C.BETWEEN, 'NOT BETWEEN',
1446
- C6C.MATCH_AGAINST,
1447
- C6C.ST_DISTANCE_SPHERE,
1448
- // spatial predicates
1449
- C6C.ST_CONTAINS,
1450
- C6C.ST_INTERSECTS,
1451
- C6C.ST_WITHIN,
1452
- C6C.ST_CROSSES,
1453
- C6C.ST_DISJOINT,
1454
- C6C.ST_EQUALS,
1455
- C6C.ST_OVERLAPS,
1456
- C6C.ST_TOUCHES
1454
+ _this.BOOLEAN_OPERATORS = new Map([
1455
+ [C6C.AND, 'AND'],
1456
+ ['AND', 'AND'],
1457
+ [C6C.OR, 'OR'],
1458
+ ['OR', 'OR'],
1459
+ ]);
1460
+ _this.OPERATOR_ALIASES = new Map([
1461
+ [C6C.EQUAL, C6C.EQUAL],
1462
+ ['=', C6C.EQUAL],
1463
+ [C6C.EQUAL_NULL_SAFE, C6C.EQUAL_NULL_SAFE],
1464
+ ['<=>', C6C.EQUAL_NULL_SAFE],
1465
+ [C6C.NOT_EQUAL, C6C.NOT_EQUAL],
1466
+ ['<>', C6C.NOT_EQUAL],
1467
+ [C6C.LESS_THAN, C6C.LESS_THAN],
1468
+ ['<', C6C.LESS_THAN],
1469
+ [C6C.LESS_THAN_OR_EQUAL_TO, C6C.LESS_THAN_OR_EQUAL_TO],
1470
+ ['<=', C6C.LESS_THAN_OR_EQUAL_TO],
1471
+ [C6C.GREATER_THAN, C6C.GREATER_THAN],
1472
+ ['>', C6C.GREATER_THAN],
1473
+ [C6C.GREATER_THAN_OR_EQUAL_TO, C6C.GREATER_THAN_OR_EQUAL_TO],
1474
+ ['>=', C6C.GREATER_THAN_OR_EQUAL_TO],
1475
+ [C6C.LIKE, C6C.LIKE],
1476
+ ['LIKE', C6C.LIKE],
1477
+ [C6C.NOT_LIKE, 'NOT LIKE'],
1478
+ ['NOT LIKE', 'NOT LIKE'],
1479
+ [C6C.IN, C6C.IN],
1480
+ ['IN', C6C.IN],
1481
+ [C6C.NOT_IN, 'NOT IN'],
1482
+ ['NOT IN', 'NOT IN'],
1483
+ [C6C.IS, C6C.IS],
1484
+ ['IS', C6C.IS],
1485
+ [C6C.IS_NOT, 'IS NOT'],
1486
+ ['IS NOT', 'IS NOT'],
1487
+ [C6C.BETWEEN, C6C.BETWEEN],
1488
+ ['BETWEEN', C6C.BETWEEN],
1489
+ ['NOT BETWEEN', 'NOT BETWEEN'],
1490
+ [C6C.MATCH_AGAINST, C6C.MATCH_AGAINST],
1457
1491
  ]);
1458
1492
  return _this;
1459
1493
  }
@@ -1535,11 +1569,6 @@ var ConditionBuilder = /** @class */ (function (_super) {
1535
1569
  return (fullKey in table.COLUMNS ||
1536
1570
  Object.values(table.COLUMNS).includes(column));
1537
1571
  };
1538
- ConditionBuilder.prototype.validateOperator = function (op) {
1539
- if (!this.OPERATORS.has(op)) {
1540
- throw new Error("Invalid or unsupported SQL operator detected: '".concat(op, "'"));
1541
- }
1542
- };
1543
1572
  ConditionBuilder.prototype.addParam = function (params, column, value) {
1544
1573
  var _a, _b, _c, _d, _e;
1545
1574
  // Determine column definition from C6.TABLES to support type-aware conversions (e.g., BINARY hex -> Buffer)
@@ -1561,250 +1590,444 @@ var ConditionBuilder = /** @class */ (function (_super) {
1561
1590
  return '?';
1562
1591
  }
1563
1592
  };
1564
- ConditionBuilder.prototype.buildBooleanJoinedConditions = function (set, andMode, params) {
1565
- var _this = this;
1566
- if (andMode === void 0) { andMode = true; }
1567
- if (params === void 0) { params = []; }
1568
- var booleanOperator = andMode ? 'AND' : 'OR';
1569
- var addCondition = function (column, op, value) {
1570
- // Normalize common variants
1571
- var valueNorm = (value === C6C.NULL) ? null : value;
1572
- var displayOp = typeof op === 'string' ? op.replace('_', ' ') : op;
1573
- var extractSubSelect = function (input) {
1574
- if (Array.isArray(input) && input.length >= 2 && input[0] === C6C.SUBSELECT) {
1575
- return input[1];
1576
- }
1577
- if (input && typeof input === 'object' && C6C.SUBSELECT in input) {
1578
- return input[C6C.SUBSELECT];
1593
+ ConditionBuilder.prototype.normalizeOperatorKey = function (op) {
1594
+ if (typeof op !== 'string')
1595
+ return undefined;
1596
+ return this.OPERATOR_ALIASES.get(op);
1597
+ };
1598
+ ConditionBuilder.prototype.formatOperator = function (op) {
1599
+ var normalized = this.normalizeOperatorKey(op);
1600
+ if (!normalized) {
1601
+ throw new Error("Invalid or unsupported SQL operator detected: '".concat(op, "'"));
1602
+ }
1603
+ switch (normalized) {
1604
+ case 'NOT LIKE':
1605
+ case 'NOT IN':
1606
+ case 'IS NOT':
1607
+ case 'NOT BETWEEN':
1608
+ return normalized;
1609
+ case C6C.MATCH_AGAINST:
1610
+ return C6C.MATCH_AGAINST;
1611
+ default:
1612
+ return normalized;
1613
+ }
1614
+ };
1615
+ ConditionBuilder.prototype.isOperator = function (op) {
1616
+ return !!this.normalizeOperatorKey(op);
1617
+ };
1618
+ ConditionBuilder.prototype.looksLikeSafeFunctionExpression = function (value) {
1619
+ if (typeof value !== 'string')
1620
+ return false;
1621
+ var trimmed = value.trim();
1622
+ if (trimmed.length === 0)
1623
+ return false;
1624
+ if (trimmed.includes(';') || trimmed.includes('--') || trimmed.includes('/*') || trimmed.includes('*/')) {
1625
+ return false;
1626
+ }
1627
+ if (!trimmed.includes('(') || !trimmed.endsWith(')')) {
1628
+ return false;
1629
+ }
1630
+ var functionMatch = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*)\s*\(/);
1631
+ if (!functionMatch) {
1632
+ return false;
1633
+ }
1634
+ var allowedCharacters = /^[A-Za-z0-9_().,'"\s-]+$/;
1635
+ if (!allowedCharacters.test(trimmed)) {
1636
+ return false;
1637
+ }
1638
+ var depth = 0;
1639
+ for (var _i = 0, trimmed_1 = trimmed; _i < trimmed_1.length; _i++) {
1640
+ var char = trimmed_1[_i];
1641
+ if (char === '(') {
1642
+ depth += 1;
1643
+ }
1644
+ else if (char === ')') {
1645
+ depth -= 1;
1646
+ if (depth < 0) {
1647
+ return false;
1579
1648
  }
1580
- return undefined;
1581
- };
1582
- var rightSubSelectPayload = extractSubSelect(valueNorm);
1583
- var buildSubSelect = function (payload) {
1584
- if (!payload)
1585
- return undefined;
1586
- var builder = _this.buildScalarSubSelect;
1649
+ }
1650
+ }
1651
+ return depth === 0;
1652
+ };
1653
+ ConditionBuilder.prototype.ensureWrapped = function (expression) {
1654
+ var trimmed = expression.trim();
1655
+ if (!trimmed)
1656
+ return trimmed;
1657
+ if (trimmed.startsWith('(') && trimmed.endsWith(')')) {
1658
+ return trimmed;
1659
+ }
1660
+ return "(".concat(trimmed, ")");
1661
+ };
1662
+ ConditionBuilder.prototype.joinBooleanParts = function (parts, operator) {
1663
+ if (parts.length === 0)
1664
+ return '';
1665
+ if (parts.length === 1) {
1666
+ return parts[0];
1667
+ }
1668
+ return parts
1669
+ .map(function (part) {
1670
+ var trimmed = part.trim();
1671
+ var upper = trimmed.toUpperCase();
1672
+ var containsAnd = upper.includes(' AND ');
1673
+ var containsOr = upper.includes(' OR ');
1674
+ var needsWrap = (operator === 'AND' && containsOr) ||
1675
+ (operator === 'OR' && containsAnd);
1676
+ return needsWrap ? "(".concat(trimmed, ")") : trimmed;
1677
+ })
1678
+ .join(" ".concat(operator, " "));
1679
+ };
1680
+ ConditionBuilder.prototype.normalizeFunctionField = function (field, params) {
1681
+ var _this = this;
1682
+ if (field instanceof Map) {
1683
+ field = Object.fromEntries(field);
1684
+ }
1685
+ if (Array.isArray(field)) {
1686
+ if (field.length === 0)
1687
+ return field;
1688
+ var fn = field[0], args = field.slice(1);
1689
+ var normalizedArgs = args.map(function (arg) { return _this.normalizeFunctionField(arg, params); });
1690
+ return __spreadArray([fn], normalizedArgs, true);
1691
+ }
1692
+ if (field && typeof field === 'object') {
1693
+ if (C6C.SUBSELECT in field) {
1694
+ var builder = this.buildScalarSubSelect;
1587
1695
  if (typeof builder !== 'function') {
1588
1696
  throw new Error('Scalar subselect handling requires JoinBuilder context.');
1589
1697
  }
1590
- return builder.call(_this, payload, params);
1591
- };
1592
- var rightSubSelectSql = buildSubSelect(rightSubSelectPayload);
1593
- // Support function-based expressions like [C6C.ST_DISTANCE_SPHERE, col1, col2]
1594
- if (typeof column === 'string' &&
1595
- _this.OPERATORS.has(column) &&
1596
- Array.isArray(op)) {
1597
- // Helper to serialize operand which may be a qualified identifier or a nested function array
1598
- var serializeOperand = function (arg) {
1599
- var identifierPathRegex = /^[A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_][A-Za-z0-9_]*$/;
1600
- if (Array.isArray(arg)) {
1601
- // Delegate to aggregate builder to handle nested functions/params
1602
- // @ts-ignore - buildAggregateField is defined upstream in AggregateBuilder
1603
- return _this.buildAggregateField(arg, params);
1604
- }
1605
- if (typeof arg === 'string') {
1606
- if (identifierPathRegex.test(arg)) {
1607
- _this.assertValidIdentifier(arg, 'WHERE argument');
1608
- return arg;
1609
- }
1610
- return arg;
1611
- }
1612
- return String(arg);
1613
- };
1614
- if (column === C6C.ST_DISTANCE_SPHERE) {
1615
- var col1 = op[0], col2 = op[1];
1616
- var threshold = Array.isArray(value) ? value[0] : value;
1617
- var left = serializeOperand(col1);
1618
- var right = serializeOperand(col2);
1619
- return "ST_Distance_Sphere(".concat(left, ", ").concat(right, ") < ").concat(_this.addParam(params, '', threshold));
1620
- }
1621
- if ([
1622
- C6C.ST_CONTAINS,
1623
- C6C.ST_INTERSECTS,
1624
- C6C.ST_WITHIN,
1625
- C6C.ST_CROSSES,
1626
- C6C.ST_DISJOINT,
1627
- C6C.ST_EQUALS,
1628
- C6C.ST_OVERLAPS,
1629
- C6C.ST_TOUCHES
1630
- ].includes(column)) {
1631
- var geom1 = op[0], geom2 = op[1];
1632
- var left = serializeOperand(geom1);
1633
- var right = serializeOperand(geom2);
1634
- return "".concat(column, "(").concat(left, ", ").concat(right, ")");
1698
+ return builder.call(this, field[C6C.SUBSELECT], params);
1699
+ }
1700
+ var entries = Object.entries(field);
1701
+ if (entries.length === 1) {
1702
+ var _a = entries[0], key = _a[0], value = _a[1];
1703
+ if (this.isOperator(key)) {
1704
+ return this.buildOperatorExpression(key, value, params);
1635
1705
  }
1706
+ return this.buildFunctionCall(key, value, params);
1636
1707
  }
1637
- var leftIsCol = _this.isColumnRef(column);
1638
- var leftIsRef = _this.isTableReference(column);
1639
- var rightIsCol = typeof value === 'string' && _this.isColumnRef(value);
1640
- if (!leftIsCol && !leftIsRef && !rightIsCol && !rightSubSelectSql) {
1641
- throw new Error("Potential SQL injection detected: '".concat(column, " ").concat(op, " ").concat(value, "'"));
1708
+ }
1709
+ return field;
1710
+ };
1711
+ ConditionBuilder.prototype.buildFunctionCall = function (fn, value, params) {
1712
+ var args = Array.isArray(value) ? value : [value];
1713
+ var normalized = this.normalizeFunctionField(__spreadArray([fn], args, true), params);
1714
+ return this.buildAggregateField(normalized, params);
1715
+ };
1716
+ ConditionBuilder.prototype.serializeOperand = function (operand, params, contextColumn) {
1717
+ var _a;
1718
+ var _this = this;
1719
+ var asParam = function (val) { return _this.addParam(params, contextColumn !== null && contextColumn !== void 0 ? contextColumn : '', val); };
1720
+ if (operand === C6C.NULL) {
1721
+ operand = null;
1722
+ }
1723
+ if (operand === null || typeof operand === 'number' || typeof operand === 'boolean') {
1724
+ return { sql: asParam(operand), isReference: false, isExpression: false, isSubSelect: false };
1725
+ }
1726
+ if (typeof operand === 'string') {
1727
+ if (this.isTableReference(operand) || this.isColumnRef(operand)) {
1728
+ return { sql: operand, isReference: true, isExpression: false, isSubSelect: false };
1642
1729
  }
1643
- _this.validateOperator(op);
1644
- if (op === C6C.MATCH_AGAINST && Array.isArray(value)) {
1645
- var search = value[0], mode = value[1];
1646
- var paramName = _this.useNamedParams ? "param".concat(Object.keys(params).length) : null;
1647
- if (_this.useNamedParams) {
1648
- params[paramName] = search;
1649
- }
1650
- else {
1651
- params.push(search);
1652
- }
1653
- var againstClause = void 0;
1654
- switch ((mode || '').toUpperCase()) {
1655
- case 'BOOLEAN':
1656
- againstClause = _this.useNamedParams ? "AGAINST(:".concat(paramName, " IN BOOLEAN MODE)") : "AGAINST(? IN BOOLEAN MODE)";
1657
- break;
1658
- case 'WITH QUERY EXPANSION':
1659
- againstClause = _this.useNamedParams ? "AGAINST(:".concat(paramName, " WITH QUERY EXPANSION)") : "AGAINST(? WITH QUERY EXPANSION)";
1660
- break;
1661
- default: // NATURAL or undefined
1662
- againstClause = _this.useNamedParams ? "AGAINST(:".concat(paramName, ")") : "AGAINST(?)";
1663
- break;
1664
- }
1665
- if (!leftIsCol) {
1666
- throw new Error("MATCH_AGAINST requires a table reference as the left operand. Column '".concat(column, "' is not a valid table reference."));
1730
+ if (this.looksLikeSafeFunctionExpression(operand)) {
1731
+ return { sql: operand.trim(), isReference: false, isExpression: true, isSubSelect: false };
1732
+ }
1733
+ return { sql: asParam(operand), isReference: false, isExpression: false, isSubSelect: false };
1734
+ }
1735
+ if (Array.isArray(operand)) {
1736
+ var normalized = this.normalizeFunctionField(operand, params);
1737
+ var sql = this.buildAggregateField(normalized, params);
1738
+ return { sql: sql, isReference: false, isExpression: true, isSubSelect: false };
1739
+ }
1740
+ if (operand instanceof Map) {
1741
+ operand = Object.fromEntries(operand);
1742
+ }
1743
+ if (typeof operand === 'object' && operand !== null) {
1744
+ if (C6C.SUBSELECT in operand) {
1745
+ var builder = this.buildScalarSubSelect;
1746
+ if (typeof builder !== 'function') {
1747
+ throw new Error('Scalar subselect handling requires JoinBuilder context.');
1667
1748
  }
1668
- var matchClause = "(MATCH(".concat(column, ") ").concat(againstClause, ")");
1669
- _this.config.verbose && console.log("[MATCH_AGAINST] ".concat(matchClause));
1670
- return matchClause;
1749
+ var subSql = builder.call(this, operand[C6C.SUBSELECT], params);
1750
+ return { sql: subSql, isReference: false, isExpression: true, isSubSelect: true };
1671
1751
  }
1672
- if ((op === C6C.IN || op === C6C.NOT_IN) && Array.isArray(value)) {
1673
- if (rightSubSelectSql) {
1674
- if (!leftIsRef) {
1675
- throw new Error("IN operator requires a table reference as the left operand. Column '".concat(column, "' is not a valid table reference."));
1676
- }
1677
- var normalized_1 = op.replace('_', ' ');
1678
- return "( ".concat(column, " ").concat(normalized_1, " ").concat(rightSubSelectSql, " )");
1752
+ var entries = Object.entries(operand);
1753
+ if (entries.length === 1) {
1754
+ var _b = entries[0], key = _b[0], value = _b[1];
1755
+ if (this.isOperator(key)) {
1756
+ var sql_1 = this.buildOperatorExpression(key, value, params);
1757
+ return { sql: this.ensureWrapped(sql_1), isReference: false, isExpression: true, isSubSelect: false };
1679
1758
  }
1680
- var placeholders = value.map(function (v) {
1681
- return _this.isColumnRef(v) ? v : _this.addParam(params, column, v);
1682
- }).join(', ');
1683
- var normalized = op.replace('_', ' ');
1684
- if (!leftIsRef) {
1685
- throw new Error("IN operator requires a table reference as the left operand. Column '".concat(column, "' is not a valid table reference."));
1759
+ if (this.BOOLEAN_OPERATORS.has(key)) {
1760
+ var sql_2 = this.buildBooleanExpression((_a = {}, _a[key] = value, _a), params, 'AND');
1761
+ return { sql: this.ensureWrapped(sql_2), isReference: false, isExpression: true, isSubSelect: false };
1686
1762
  }
1687
- return "( ".concat(column, " ").concat(normalized, " (").concat(placeholders, ") )");
1763
+ var sql = this.buildFunctionCall(key, value, params);
1764
+ return { sql: sql, isReference: false, isExpression: true, isSubSelect: false };
1688
1765
  }
1689
- if (op === C6C.BETWEEN || op === 'NOT BETWEEN') {
1690
- if (!Array.isArray(value) || value.length !== 2) {
1691
- throw new Error("BETWEEN operator requires an array of two values");
1766
+ }
1767
+ throw new Error('Unsupported operand type in SQL expression.');
1768
+ };
1769
+ ConditionBuilder.prototype.buildOperatorExpression = function (op, rawOperands, params, contextColumn) {
1770
+ var _a, _b;
1771
+ var _this = this;
1772
+ var operator = this.formatOperator(op);
1773
+ if (operator === C6C.MATCH_AGAINST) {
1774
+ if (!Array.isArray(rawOperands) || rawOperands.length !== 2) {
1775
+ throw new Error('MATCH_AGAINST requires an array of two operands.');
1776
+ }
1777
+ var left = rawOperands[0], right = rawOperands[1];
1778
+ var leftInfo_1 = this.serializeOperand(left, params, contextColumn);
1779
+ if (!leftInfo_1.isReference) {
1780
+ throw new Error('MATCH_AGAINST requires the left operand to be a table reference.');
1781
+ }
1782
+ if (!Array.isArray(right) || right.length === 0) {
1783
+ throw new Error('MATCH_AGAINST expects an array [search, mode?].');
1784
+ }
1785
+ var search = right[0], mode = right[1];
1786
+ var placeholder = this.addParam(params, leftInfo_1.sql, search);
1787
+ var againstClause = void 0;
1788
+ switch (typeof mode === 'string' ? mode.toUpperCase() : '') {
1789
+ case 'BOOLEAN':
1790
+ againstClause = "AGAINST(".concat(placeholder, " IN BOOLEAN MODE)");
1791
+ break;
1792
+ case 'WITH QUERY EXPANSION':
1793
+ againstClause = "AGAINST(".concat(placeholder, " WITH QUERY EXPANSION)");
1794
+ break;
1795
+ case 'NATURAL LANGUAGE MODE':
1796
+ againstClause = "AGAINST(".concat(placeholder, " IN NATURAL LANGUAGE MODE)");
1797
+ break;
1798
+ default:
1799
+ againstClause = "AGAINST(".concat(placeholder, ")");
1800
+ break;
1801
+ }
1802
+ var clause = "(MATCH(".concat(leftInfo_1.sql, ") ").concat(againstClause, ")");
1803
+ this.config.verbose && console.log("[MATCH_AGAINST] ".concat(clause));
1804
+ return clause;
1805
+ }
1806
+ var operands = Array.isArray(rawOperands) ? rawOperands : [rawOperands];
1807
+ if (operator === C6C.IN || operator === 'NOT IN') {
1808
+ if (operands.length < 2) {
1809
+ throw new Error("".concat(operator, " requires two operands."));
1810
+ }
1811
+ var leftRaw = operands[0], rest = operands.slice(1);
1812
+ var left_1 = leftRaw;
1813
+ var right = rest.length <= 1 ? rest[0] : rest;
1814
+ var leftInfo_2 = this.serializeOperand(left_1, params, typeof left_1 === 'string' ? left_1 : contextColumn);
1815
+ if (!leftInfo_2.isReference) {
1816
+ throw new Error("".concat(operator, " requires the left operand to be a table reference."));
1817
+ }
1818
+ if (Array.isArray(right)) {
1819
+ if (right.length === 0) {
1820
+ throw new Error("".concat(operator, " requires at least one value."));
1692
1821
  }
1693
- var start = value[0], end = value[1];
1694
- if (!leftIsRef) {
1695
- throw new Error("BETWEEN operator requires a table reference as the left operand. Column '".concat(column, "' is not a valid table reference."));
1822
+ if (right.length === 2 && right[0] === C6C.SUBSELECT) {
1823
+ var sub = this.serializeOperand(right, params, typeof left_1 === 'string' ? left_1 : contextColumn);
1824
+ return "( ".concat(leftInfo_2.sql, " ").concat(operator, " ").concat(sub.sql, " )");
1696
1825
  }
1697
- return "(".concat(column, ") ").concat(op.replace('_', ' '), " ").concat(_this.addParam(params, column, start), " AND ").concat(_this.addParam(params, column, end));
1826
+ var placeholders = right.map(function (item) {
1827
+ if (typeof item === 'string' && _this.isTableReference(item)) {
1828
+ return item;
1829
+ }
1830
+ var sql = _this.serializeOperand(item, params, typeof left_1 === 'string' ? left_1 : contextColumn).sql;
1831
+ return sql;
1832
+ });
1833
+ return "( ".concat(leftInfo_2.sql, " ").concat(operator, " (").concat(placeholders.join(', '), ") )");
1698
1834
  }
1699
- var rightIsRef = rightSubSelectSql ? false : _this.isTableReference(value);
1700
- if (leftIsRef && rightIsRef) {
1701
- return "(".concat(column, ") ").concat(displayOp, " ").concat(value);
1835
+ var rightInfo_1 = this.serializeOperand(right, params, typeof left_1 === 'string' ? left_1 : contextColumn);
1836
+ if (!rightInfo_1.isSubSelect) {
1837
+ throw new Error("".concat(operator, " requires an array of values or a subselect."));
1702
1838
  }
1703
- if (leftIsRef && rightSubSelectSql) {
1704
- return "(".concat(column, ") ").concat(displayOp, " ").concat(rightSubSelectSql);
1839
+ return "( ".concat(leftInfo_2.sql, " ").concat(operator, " ").concat(rightInfo_1.sql, " )");
1840
+ }
1841
+ if (operator === C6C.BETWEEN || operator === 'NOT BETWEEN') {
1842
+ var left = void 0;
1843
+ var start = void 0;
1844
+ var end = void 0;
1845
+ if (operands.length === 3) {
1846
+ left = operands[0], start = operands[1], end = operands[2];
1705
1847
  }
1706
- if (leftIsRef && !rightIsRef) {
1707
- return "(".concat(column, ") ").concat(displayOp, " ").concat(_this.addParam(params, column, valueNorm));
1848
+ else if (operands.length === 2 && Array.isArray(operands[1]) && operands[1].length === 2) {
1849
+ _a = operands, left = _a[0], _b = _a[1], start = _b[0], end = _b[1];
1708
1850
  }
1709
- if (rightIsRef) {
1710
- return "(".concat(_this.addParam(params, column, column), ") ").concat(displayOp, " ").concat(value);
1851
+ else {
1852
+ throw new Error("".concat(operator, " requires three operands."));
1711
1853
  }
1712
- throw new Error("Neither operand appears to be a table reference (".concat(column, ") or (").concat(value, ")"));
1713
- };
1714
- var parts = [];
1715
- var buildFromObject = function (obj, mode) {
1716
- var subParts = [];
1717
- var entries = Object.entries(obj);
1718
- var nonNumeric = entries.filter(function (_a) {
1719
- var k = _a[0];
1720
- return isNaN(Number(k));
1721
- });
1722
- var numeric = entries.filter(function (_a) {
1723
- var k = _a[0];
1724
- return !isNaN(Number(k));
1725
- });
1726
- var processEntry = function (k, v) {
1727
- // Operator-as-key handling, e.g., { [C6C.ST_DISTANCE_SPHERE]: [arg1, arg2, threshold] }
1728
- if (typeof k === 'string' && _this.OPERATORS.has(k) && Array.isArray(v)) {
1729
- if (k === C6C.ST_DISTANCE_SPHERE) {
1730
- // Accept either [arg1, arg2, threshold] or [[arg1, arg2], threshold]
1731
- var args = void 0;
1732
- var threshold = void 0;
1733
- if (Array.isArray(v[0]) && v.length >= 2) {
1734
- args = v[0];
1735
- threshold = v[1];
1736
- }
1737
- else {
1738
- args = v.slice(0, 2);
1739
- threshold = v[2];
1740
- }
1741
- subParts.push(addCondition(k, args, threshold));
1742
- return;
1743
- }
1744
- }
1745
- if (typeof v === 'object' && v !== null && Object.keys(v).length === 1) {
1746
- var _a = Object.entries(v)[0], op = _a[0], val = _a[1];
1747
- subParts.push(addCondition(k, op, val));
1854
+ var leftInfo_3 = this.serializeOperand(left, params, typeof left === 'string' ? left : contextColumn);
1855
+ if (!leftInfo_3.isReference) {
1856
+ throw new Error("".concat(operator, " requires the left operand to be a table reference."));
1857
+ }
1858
+ var startInfo = this.serializeOperand(start, params, typeof left === 'string' ? left : contextColumn);
1859
+ var endInfo = this.serializeOperand(end, params, typeof left === 'string' ? left : contextColumn);
1860
+ var betweenOperator = operator === 'NOT BETWEEN' ? 'NOT BETWEEN' : 'BETWEEN';
1861
+ return "".concat(this.ensureWrapped(leftInfo_3.sql), " ").concat(betweenOperator, " ").concat(startInfo.sql, " AND ").concat(endInfo.sql);
1862
+ }
1863
+ if (operands.length !== 2) {
1864
+ throw new Error("".concat(operator, " requires two operands."));
1865
+ }
1866
+ var leftOperand = operands[0], rightOperand = operands[1];
1867
+ var leftInfo = this.serializeOperand(leftOperand, params, typeof leftOperand === 'string' ? leftOperand : contextColumn);
1868
+ var rightInfo = this.serializeOperand(rightOperand, params, typeof leftOperand === 'string' ? leftOperand : contextColumn);
1869
+ if (!leftInfo.isReference && !leftInfo.isExpression && !rightInfo.isReference && !rightInfo.isExpression) {
1870
+ throw new Error("Potential SQL injection detected: '".concat(operator, "' with non-reference operands."));
1871
+ }
1872
+ var leftSql = leftInfo.isExpression ? leftInfo.sql : this.ensureWrapped(leftInfo.sql);
1873
+ var rightSql = rightInfo.isExpression ? rightInfo.sql : rightInfo.sql;
1874
+ return "".concat(leftSql, " ").concat(operator, " ").concat(rightSql);
1875
+ };
1876
+ ConditionBuilder.prototype.buildLegacyColumnCondition = function (column, value, params) {
1877
+ var _a;
1878
+ var _this = this;
1879
+ if (value instanceof Map) {
1880
+ value = Object.fromEntries(value);
1881
+ }
1882
+ if (Array.isArray(value)) {
1883
+ if (value.length >= 2 && typeof value[0] === 'string') {
1884
+ var op = value[0], rest = value.slice(1);
1885
+ return this.buildOperatorExpression(op, __spreadArray([column], rest, true), params, column);
1886
+ }
1887
+ if (value.length === 3 && typeof value[0] === 'string' && typeof value[1] === 'string') {
1888
+ return this.buildOperatorExpression(value[1], [value[0], value[2]], params, value[0]);
1889
+ }
1890
+ }
1891
+ if (typeof value === 'object' && value !== null) {
1892
+ var entries = Object.entries(value);
1893
+ if (entries.length === 1) {
1894
+ var _b = entries[0], op = _b[0], operand = _b[1];
1895
+ if (this.isOperator(op)) {
1896
+ return this.buildOperatorExpression(op, [column, operand], params, column);
1748
1897
  }
1749
- else if (Array.isArray(v) && v.length >= 2 && typeof v[0] === 'string') {
1750
- var _b = v, op = _b[0], val = _b[1];
1751
- subParts.push(addCondition(k, op, val));
1898
+ if (this.BOOLEAN_OPERATORS.has(op)) {
1899
+ var expression = this.buildBooleanExpression((_a = {}, _a[op] = operand, _a), params, 'AND');
1900
+ return expression;
1752
1901
  }
1753
- else if (typeof v === 'object' && v !== null) {
1754
- var sub = _this.buildBooleanJoinedConditions(v, mode, params);
1755
- if (sub)
1756
- subParts.push(sub);
1902
+ }
1903
+ var subParts = entries.map(function (_a) {
1904
+ var _b;
1905
+ var op = _a[0], operand = _a[1];
1906
+ if (_this.isOperator(op)) {
1907
+ return _this.buildOperatorExpression(op, [column, operand], params, column);
1757
1908
  }
1758
- else {
1759
- subParts.push(addCondition(k, '=', v));
1909
+ return _this.buildBooleanExpression((_b = {}, _b[op] = operand, _b), params, 'AND');
1910
+ }).filter(Boolean);
1911
+ return this.joinBooleanParts(subParts, 'AND');
1912
+ }
1913
+ return this.buildOperatorExpression(C6C.EQUAL, [column, value], params, column);
1914
+ };
1915
+ ConditionBuilder.prototype.buildBooleanExpression = function (node, params, defaultOperator) {
1916
+ var _this = this;
1917
+ if (node === null || node === undefined) {
1918
+ return '';
1919
+ }
1920
+ if (Array.isArray(node)) {
1921
+ if (node.length === 0)
1922
+ return '';
1923
+ if (node.length === 3 && typeof node[0] === 'string' && typeof node[1] === 'string') {
1924
+ return this.buildOperatorExpression(node[1], [node[0], node[2]], params, node[0]);
1925
+ }
1926
+ var parts_1 = node
1927
+ .map(function (item) { return _this.buildBooleanExpression(item, params, 'OR'); })
1928
+ .filter(Boolean);
1929
+ return this.joinBooleanParts(parts_1, 'OR');
1930
+ }
1931
+ if (node instanceof Map) {
1932
+ node = Object.fromEntries(node);
1933
+ }
1934
+ if (typeof node !== 'object') {
1935
+ throw new Error('Invalid WHERE clause structure.');
1936
+ }
1937
+ var entries = Object.entries(node);
1938
+ if (entries.length === 0)
1939
+ return '';
1940
+ if (entries.length === 1) {
1941
+ var _a = entries[0], key = _a[0], value = _a[1];
1942
+ if (this.BOOLEAN_OPERATORS.has(key)) {
1943
+ if (!Array.isArray(value)) {
1944
+ throw new Error("".concat(key, " expects an array of expressions."));
1760
1945
  }
1761
- };
1762
- // Process non-numeric keys first to preserve intuitive insertion order for params
1763
- for (var _i = 0, nonNumeric_1 = nonNumeric; _i < nonNumeric_1.length; _i++) {
1764
- var _a = nonNumeric_1[_i], k = _a[0], v = _a[1];
1765
- processEntry(k, v);
1946
+ var op_1 = this.BOOLEAN_OPERATORS.get(key);
1947
+ var parts_2 = value
1948
+ .map(function (item) { return _this.buildBooleanExpression(item, params, op_1); })
1949
+ .filter(Boolean);
1950
+ return this.joinBooleanParts(parts_2, op_1);
1766
1951
  }
1767
- // Then process numeric keys (treated as grouped OR conditions)
1768
- for (var _b = 0, numeric_1 = numeric; _b < numeric_1.length; _b++) {
1769
- var _c = numeric_1[_b]; _c[0]; var v = _c[1];
1770
- var sub = _this.buildBooleanJoinedConditions(v, false, params);
1771
- if (sub)
1772
- subParts.push(sub);
1952
+ if (this.isOperator(key)) {
1953
+ return this.buildOperatorExpression(key, value, params);
1773
1954
  }
1774
- return subParts.join(" ".concat(mode ? 'AND' : 'OR', " "));
1775
- };
1776
- if (Array.isArray(set)) {
1777
- // Detect a single condition triple: [column, op, value]
1778
- if (set.length === 3 && typeof set[0] === 'string' && typeof set[1] === 'string') {
1779
- var _a = set, column = _a[0], rawOp = _a[1], rawVal = _a[2];
1780
- var op = rawOp;
1781
- var value = rawVal === C6C.NULL ? null : rawVal;
1782
- var sub = addCondition(column, op, value);
1783
- if (sub)
1784
- parts.push(sub);
1955
+ if (!isNaN(Number(key))) {
1956
+ return this.buildBooleanExpression(value, params, 'OR');
1785
1957
  }
1786
- else {
1787
- for (var _i = 0, set_1 = set; _i < set_1.length; _i++) {
1788
- var item = set_1[_i];
1789
- var sub = this.buildBooleanJoinedConditions(item, false, params);
1790
- if (sub)
1791
- parts.push(sub);
1958
+ }
1959
+ var parts = [];
1960
+ var nonNumeric = entries.filter(function (_a) {
1961
+ var k = _a[0];
1962
+ return isNaN(Number(k));
1963
+ });
1964
+ var numeric = entries.filter(function (_a) {
1965
+ var k = _a[0];
1966
+ return !isNaN(Number(k));
1967
+ });
1968
+ var _loop_1 = function (key, value) {
1969
+ if (this_1.BOOLEAN_OPERATORS.has(key)) {
1970
+ var op_2 = this_1.BOOLEAN_OPERATORS.get(key);
1971
+ if (!Array.isArray(value)) {
1972
+ throw new Error("".concat(key, " expects an array of expressions."));
1973
+ }
1974
+ var nested = value
1975
+ .map(function (item) { return _this.buildBooleanExpression(item, params, op_2); })
1976
+ .filter(Boolean);
1977
+ if (nested.length) {
1978
+ parts.push(this_1.joinBooleanParts(nested, op_2));
1792
1979
  }
1980
+ return "continue";
1793
1981
  }
1982
+ if (this_1.isOperator(key)) {
1983
+ parts.push(this_1.buildOperatorExpression(key, value, params));
1984
+ return "continue";
1985
+ }
1986
+ parts.push(this_1.buildLegacyColumnCondition(key, value, params));
1987
+ };
1988
+ var this_1 = this;
1989
+ for (var _i = 0, nonNumeric_1 = nonNumeric; _i < nonNumeric_1.length; _i++) {
1990
+ var _b = nonNumeric_1[_i], key = _b[0], value = _b[1];
1991
+ _loop_1(key, value);
1794
1992
  }
1795
- else if (typeof set === 'object' && set !== null) {
1796
- var sub = buildFromObject(set, andMode);
1797
- if (sub)
1798
- parts.push(sub);
1993
+ for (var _c = 0, numeric_1 = numeric; _c < numeric_1.length; _c++) {
1994
+ var _d = numeric_1[_c], value = _d[1];
1995
+ var nested = this.buildBooleanExpression(value, params, 'OR');
1996
+ if (nested) {
1997
+ parts.push(nested);
1998
+ }
1799
1999
  }
1800
- var clause = parts.join(" ".concat(booleanOperator, " "));
1801
- return clause ? "(".concat(clause, ")") : '';
2000
+ return this.joinBooleanParts(parts, defaultOperator);
2001
+ };
2002
+ ConditionBuilder.prototype.buildBooleanJoinedConditions = function (set, andMode, params) {
2003
+ if (andMode === void 0) { andMode = true; }
2004
+ if (params === void 0) { params = []; }
2005
+ var expression = this.buildBooleanExpression(set, params, andMode ? 'AND' : 'OR');
2006
+ if (!expression)
2007
+ return '';
2008
+ return this.ensureWrapped(expression);
1802
2009
  };
1803
2010
  ConditionBuilder.prototype.buildWhereClause = function (whereArg, params) {
1804
2011
  var clause = this.buildBooleanJoinedConditions(whereArg, true, params);
1805
2012
  if (!clause)
1806
2013
  return '';
1807
- var trimmed = clause.replace(/^\((.*)\)$/, '$1');
2014
+ var trimmed = clause.trim();
2015
+ var upper = trimmed.toUpperCase();
2016
+ if (!upper.includes(' AND ') && !upper.includes(' OR ')) {
2017
+ if (trimmed.startsWith('(') && trimmed.endsWith(')')) {
2018
+ var inner = trimmed.substring(1, trimmed.length - 1);
2019
+ var innerUpper = inner.toUpperCase();
2020
+ var requiresOuterWrap = innerUpper.includes(' IN ') ||
2021
+ innerUpper.includes(' BETWEEN ') ||
2022
+ innerUpper.includes(' SELECT ');
2023
+ if (requiresOuterWrap) {
2024
+ trimmed = "( ".concat(inner.trim(), " )");
2025
+ }
2026
+ else {
2027
+ trimmed = inner.trim();
2028
+ }
2029
+ }
2030
+ }
1808
2031
  this.config.verbose && console.log("[WHERE] ".concat(trimmed));
1809
2032
  return " WHERE ".concat(trimmed);
1810
2033
  };
@@ -1821,24 +2044,29 @@ var JoinBuilder = /** @class */ (function (_super) {
1821
2044
  };
1822
2045
  JoinBuilder.prototype.buildJoinClauses = function (joinArgs, params) {
1823
2046
  var sql = '';
1824
- var _loop_1 = function (joinType) {
1825
- var joinKind = joinType.replace('_', ' ').toUpperCase();
2047
+ var joinTypeEntries = joinArgs instanceof Map
2048
+ ? Array.from(joinArgs.entries()).map(function (_a) {
2049
+ var key = _a[0], value = _a[1];
2050
+ return [String(key), value];
2051
+ })
2052
+ : Object.keys(joinArgs).map(function (key) { return [key, joinArgs[key]]; });
2053
+ var _loop_1 = function (joinTypeRaw, joinSection) {
2054
+ var joinKind = joinTypeRaw.replace('_', ' ').toUpperCase();
1826
2055
  var entries = [];
1827
- var joinSection = joinArgs[joinType];
1828
2056
  if (joinSection instanceof Map) {
1829
2057
  joinSection.forEach(function (value, key) {
1830
2058
  entries.push([key, value]);
1831
2059
  });
1832
2060
  }
1833
2061
  else {
1834
- for (var raw in joinSection) {
2062
+ Object.keys(joinSection).forEach(function (raw) {
1835
2063
  entries.push([raw, joinSection[raw]]);
1836
- }
2064
+ });
1837
2065
  }
1838
- for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
1839
- var _a = entries_1[_i], rawKey = _a[0], conditions = _a[1];
2066
+ for (var _b = 0, entries_1 = entries; _b < entries_1.length; _b++) {
2067
+ var _c = entries_1[_b], rawKey = _c[0], conditions = _c[1];
1840
2068
  var raw = typeof rawKey === 'string' ? rawKey : String(rawKey);
1841
- var _b = raw.trim().split(/\s+/, 2), table = _b[0], aliasCandidate = _b[1];
2069
+ var _d = raw.trim().split(/\s+/, 2), table = _d[0], aliasCandidate = _d[1];
1842
2070
  if (!table)
1843
2071
  continue;
1844
2072
  if (isDerivedTableKey(table)) {
@@ -1862,7 +2090,7 @@ var JoinBuilder = /** @class */ (function (_super) {
1862
2090
  throw new Error('Derived table subselects require a base table defined with C6C.FROM.');
1863
2091
  }
1864
2092
  var subBuilder = this_1.createSelectBuilder(subRequest);
1865
- var _c = subBuilder.build(fromTable, true), subSql = _c.sql, subParams = _c.params;
2093
+ var _e = subBuilder.build(fromTable, true), subSql = _e.sql, subParams = _e.params;
1866
2094
  var normalizedSql = this_1.integrateSubSelectParams(subSql, subParams, params);
1867
2095
  var formatted = normalizedSql.trim().split('\n').map(function (line) { return " ".concat(line); }).join('\n');
1868
2096
  var joinSql = "(\n".concat(formatted, "\n) AS `").concat(alias, "`");
@@ -1887,8 +2115,9 @@ var JoinBuilder = /** @class */ (function (_super) {
1887
2115
  }
1888
2116
  };
1889
2117
  var this_1 = this;
1890
- for (var joinType in joinArgs) {
1891
- _loop_1(joinType);
2118
+ for (var _i = 0, joinTypeEntries_1 = joinTypeEntries; _i < joinTypeEntries_1.length; _i++) {
2119
+ var _a = joinTypeEntries_1[_i], joinTypeRaw = _a[0], joinSection = _a[1];
2120
+ _loop_1(joinTypeRaw, joinSection);
1892
2121
  }
1893
2122
  this.config.verbose && console.log("[JOIN] ".concat(sql.trim()));
1894
2123
  return sql;