@carbonorm/carbonnode 3.8.4 → 3.9.2

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.cjs.js CHANGED
@@ -61,6 +61,7 @@ var C6Constants = {
61
61
  INNER: 'INNER',
62
62
  INTERVAL: 'INTERVAL',
63
63
  JOIN: 'JOIN',
64
+ FROM: 'FROM',
64
65
  LEFT: 'LEFT',
65
66
  LEFT_OUTER: 'LEFT_OUTER',
66
67
  LESS_THAN: '<',
@@ -92,6 +93,8 @@ var C6Constants = {
92
93
  SECOND: 'SECOND',
93
94
  SECOND_MICROSECOND: 'SECOND_MICROSECOND',
94
95
  SELECT: 'SELECT',
96
+ SUBSELECT: 'SUBSELECT',
97
+ PARAM: 'PARAM',
95
98
  // MySQL Spatial Functions
96
99
  ST_AREA: 'ST_Area',
97
100
  ST_ASBINARY: 'ST_AsBinary',
@@ -1254,14 +1257,34 @@ var AggregateBuilder = /** @class */ (function (_super) {
1254
1257
  _this.selectAliases = new Set();
1255
1258
  return _this;
1256
1259
  }
1257
- AggregateBuilder.prototype.buildAggregateField = function (field) {
1260
+ // Overridden in ConditionBuilder where alias tracking is available.
1261
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1262
+ AggregateBuilder.prototype.assertValidIdentifier = function (_identifier, _context) {
1263
+ // no-op placeholder for subclasses that do not implement alias validation
1264
+ };
1265
+ AggregateBuilder.prototype.buildAggregateField = function (field, params) {
1258
1266
  var _this = this;
1267
+ var _a, _b;
1259
1268
  if (typeof field === 'string') {
1269
+ this.assertValidIdentifier(field, 'SELECT field');
1260
1270
  return field;
1261
1271
  }
1262
1272
  if (!Array.isArray(field) || field.length === 0) {
1263
1273
  throw new Error('Invalid SELECT field entry');
1264
1274
  }
1275
+ // If the array represents a tuple/literal list (e.g., [lng, lat]) rather than a
1276
+ // function call like [FN, ...args], serialize the list as a comma-separated
1277
+ // literal sequence so parent calls (like ORDER BY FN(<here>)) can embed it.
1278
+ var isNumericString = function (s) { return /^-?\d+(?:\.\d+)?$/.test(String(s).trim()); };
1279
+ if (typeof field[0] !== 'string' || isNumericString(field[0])) {
1280
+ return field
1281
+ .map(function (arg) {
1282
+ if (Array.isArray(arg))
1283
+ return _this.buildAggregateField(arg, params);
1284
+ return String(arg);
1285
+ })
1286
+ .join(', ');
1287
+ }
1265
1288
  var fn = field[0], args = field.slice(1);
1266
1289
  var alias;
1267
1290
  if (args.length >= 2 && String(args[args.length - 2]).toUpperCase() === 'AS') {
@@ -1269,8 +1292,50 @@ var AggregateBuilder = /** @class */ (function (_super) {
1269
1292
  args.pop();
1270
1293
  }
1271
1294
  var F = String(fn).toUpperCase();
1295
+ // Parameter placeholder helper: [C6C.PARAM, value]
1296
+ if (F === C6C.PARAM) {
1297
+ if (!params) {
1298
+ throw new Error('PARAM requires parameter tracking.');
1299
+ }
1300
+ var value = args[0];
1301
+ // Use empty column context; ORDER/SELECT literals have no column typing.
1302
+ // @ts-ignore addParam is provided by ConditionBuilder in our hierarchy.
1303
+ return this.addParam(params, '', value);
1304
+ }
1305
+ if (F === C6C.SUBSELECT) {
1306
+ if (!params) {
1307
+ throw new Error('Scalar subselects in SELECT require parameter tracking.');
1308
+ }
1309
+ var subRequest = args[0];
1310
+ var subSql = (_b = (_a = this).buildScalarSubSelect) === null || _b === void 0 ? void 0 : _b.call(_a, subRequest, params);
1311
+ if (!subSql) {
1312
+ throw new Error('Failed to build scalar subselect.');
1313
+ }
1314
+ var expr_1 = subSql;
1315
+ if (alias) {
1316
+ this.selectAliases.add(alias);
1317
+ expr_1 += " AS ".concat(alias);
1318
+ }
1319
+ this.config.verbose && console.log("[SELECT] ".concat(expr_1));
1320
+ return expr_1;
1321
+ }
1322
+ var identifierPathRegex = /^[A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_][A-Za-z0-9_]*$/;
1272
1323
  var argList = args
1273
- .map(function (arg) { return Array.isArray(arg) ? _this.buildAggregateField(arg) : arg; })
1324
+ .map(function (arg) {
1325
+ if (Array.isArray(arg))
1326
+ return _this.buildAggregateField(arg, params);
1327
+ if (typeof arg === 'string') {
1328
+ if (identifierPathRegex.test(arg)) {
1329
+ _this.assertValidIdentifier(arg, 'SELECT expression');
1330
+ return arg;
1331
+ }
1332
+ // Treat numeric-looking strings as literals, not identifier paths
1333
+ if (isNumericString(arg))
1334
+ return arg;
1335
+ return arg;
1336
+ }
1337
+ return String(arg);
1338
+ })
1274
1339
  .join(', ');
1275
1340
  var expr;
1276
1341
  if (F === 'DISTINCT') {
@@ -1289,11 +1354,91 @@ var AggregateBuilder = /** @class */ (function (_super) {
1289
1354
  return AggregateBuilder;
1290
1355
  }(Executor));
1291
1356
 
1357
+ // Alias a table name with a given alias
1358
+ var DERIVED_TABLE_PREFIX = '__c6DerivedTable__';
1359
+ var DERIVED_ID_SYMBOL = Symbol('c6DerivedTableId');
1360
+ var derivedTableLookup = new Map();
1361
+ var derivedTableReverseLookup = new WeakMap();
1362
+ var derivedTableCounter = 0;
1363
+ var isDerivedTableKey = function (key) {
1364
+ return typeof key === 'string' && key.startsWith(DERIVED_TABLE_PREFIX);
1365
+ };
1366
+ var resolveDerivedTable = function (key) {
1367
+ return derivedTableLookup.get(key);
1368
+ };
1369
+ var derivedTable = function (spec) {
1370
+ if (!spec || typeof spec !== 'object') {
1371
+ throw new Error('Derived table definition must be an object.');
1372
+ }
1373
+ var aliasRaw = spec[C6C.AS];
1374
+ if (typeof aliasRaw !== 'string' || aliasRaw.trim() === '') {
1375
+ throw new Error('Derived tables require a non-empty alias via C6C.AS.');
1376
+ }
1377
+ if (!spec[C6C.SUBSELECT] || typeof spec[C6C.SUBSELECT] !== 'object') {
1378
+ throw new Error('Derived tables require a nested SELECT payload under C6C.SUBSELECT.');
1379
+ }
1380
+ var id = derivedTableReverseLookup.get(spec);
1381
+ if (!id) {
1382
+ id = "".concat(DERIVED_TABLE_PREFIX).concat(++derivedTableCounter);
1383
+ derivedTableReverseLookup.set(spec, id);
1384
+ derivedTableLookup.set(id, spec);
1385
+ Object.defineProperty(spec, DERIVED_ID_SYMBOL, {
1386
+ value: id,
1387
+ configurable: false,
1388
+ enumerable: false,
1389
+ writable: false
1390
+ });
1391
+ }
1392
+ var alias = aliasRaw.trim();
1393
+ derivedTableLookup.set(id, spec);
1394
+ Object.defineProperty(spec, 'toString', {
1395
+ value: function () { return "".concat(id, " ").concat(alias); },
1396
+ configurable: true,
1397
+ enumerable: false,
1398
+ writable: true
1399
+ });
1400
+ return spec;
1401
+ };
1402
+ var A = function (tableName, alias) {
1403
+ return "".concat(tableName, " ").concat(alias);
1404
+ };
1405
+ // Qualify a column constant (e.g. 'property_units.parcel_id') to an alias
1406
+ var F = function (qualifiedCol, alias) {
1407
+ return "".concat(alias, ".").concat(qualifiedCol.split('.').pop());
1408
+ };
1409
+ // Equal join condition using full-qualified column constants
1410
+ var fieldEq = function (leftCol, rightCol, leftAlias, rightAlias) {
1411
+ var _a;
1412
+ return (_a = {},
1413
+ _a[F(leftCol, leftAlias)] = F(rightCol, rightAlias),
1414
+ _a);
1415
+ };
1416
+ // ST_Distance_Sphere for aliased fields
1417
+ var distSphere = function (fromCol, toCol, fromAlias, toAlias) {
1418
+ return [C6C.ST_DISTANCE_SPHERE, F(fromCol, fromAlias), F(toCol, toAlias)];
1419
+ };
1420
+ // Build a bounding-box expression.
1421
+ //
1422
+ // Arguments must be provided in `(minLng, minLat, maxLng, maxLat)` order. The
1423
+ // helper does not attempt to swap or validate coordinates; if a minimum value
1424
+ // is greater than its corresponding maximum value, MySQL's `ST_MakeEnvelope`
1425
+ // returns `NULL`.
1426
+ var bbox = function (minLng, minLat, maxLng, maxLat) {
1427
+ return [C6C.ST_SRID, [C6C.ST_MAKEENVELOPE,
1428
+ [C6C.ST_POINT, minLng, minLat],
1429
+ [C6C.ST_POINT, maxLng, maxLat]], 4326];
1430
+ };
1431
+ // ST_Contains for map envelope/shape queries
1432
+ var stContains = function (envelope, shape) {
1433
+ return [C6C.ST_CONTAINS, envelope, shape];
1434
+ };
1435
+
1292
1436
  var ConditionBuilder = /** @class */ (function (_super) {
1293
1437
  tslib.__extends(ConditionBuilder, _super);
1294
1438
  function ConditionBuilder() {
1295
1439
  var _this = _super !== null && _super.apply(this, arguments) || this;
1296
1440
  _this.aliasMap = {};
1441
+ _this.derivedAliases = new Set();
1297
1442
  _this.OPERATORS = new Set([
1298
1443
  C6C.EQUAL, C6C.NOT_EQUAL, C6C.LESS_THAN, C6C.LESS_THAN_OR_EQUAL_TO,
1299
1444
  C6C.GREATER_THAN, C6C.GREATER_THAN_OR_EQUAL_TO,
@@ -1318,21 +1463,43 @@ var ConditionBuilder = /** @class */ (function (_super) {
1318
1463
  ConditionBuilder.prototype.initAlias = function (baseTable, joins) {
1319
1464
  var _a;
1320
1465
  this.aliasMap = (_a = {}, _a[baseTable] = baseTable, _a);
1466
+ this.derivedAliases = new Set();
1321
1467
  if (!joins)
1322
1468
  return;
1323
1469
  for (var joinType in joins) {
1324
1470
  for (var raw in joins[joinType]) {
1325
- var _b = raw.split(' '), table = _b[0], alias = _b[1];
1326
- this.aliasMap[alias || table] = table;
1471
+ var _b = raw.trim().split(/\s+/, 2), table = _b[0], alias = _b[1];
1472
+ if (!table)
1473
+ continue;
1474
+ this.registerAlias(alias || table, table);
1327
1475
  }
1328
1476
  }
1329
1477
  };
1478
+ ConditionBuilder.prototype.registerAlias = function (alias, table) {
1479
+ this.aliasMap[alias] = table;
1480
+ if (isDerivedTableKey(table)) {
1481
+ this.derivedAliases.add(alias);
1482
+ }
1483
+ };
1484
+ ConditionBuilder.prototype.assertValidIdentifier = function (identifier, context) {
1485
+ if (typeof identifier !== 'string')
1486
+ return;
1487
+ if (!identifier.includes('.'))
1488
+ return;
1489
+ var alias = identifier.split('.', 2)[0];
1490
+ if (!(alias in this.aliasMap)) {
1491
+ throw new Error("Unknown table or alias '".concat(alias, "' referenced in ").concat(context, ": '").concat(identifier, "'."));
1492
+ }
1493
+ };
1330
1494
  ConditionBuilder.prototype.isColumnRef = function (ref) {
1331
1495
  var _a, _b;
1332
1496
  if (typeof ref !== 'string' || !ref.includes('.'))
1333
1497
  return false;
1334
1498
  var _c = ref.split('.', 2), prefix = _c[0], column = _c[1];
1335
1499
  var tableName = this.aliasMap[prefix] || prefix;
1500
+ if (isDerivedTableKey(tableName) || this.derivedAliases.has(prefix)) {
1501
+ return true;
1502
+ }
1336
1503
  var table = (_b = (_a = this.config.C6) === null || _a === void 0 ? void 0 : _a.TABLES) === null || _b === void 0 ? void 0 : _b[tableName];
1337
1504
  if (!table)
1338
1505
  return false;
@@ -1361,6 +1528,9 @@ var ConditionBuilder = /** @class */ (function (_super) {
1361
1528
  }
1362
1529
  var _e = val.split('.'), prefix = _e[0], column = _e[1];
1363
1530
  var tableName = (_b = this.aliasMap[prefix]) !== null && _b !== void 0 ? _b : prefix;
1531
+ if (isDerivedTableKey(tableName) || this.derivedAliases.has(prefix)) {
1532
+ return true;
1533
+ }
1364
1534
  var table = (_d = (_c = this.config.C6) === null || _c === void 0 ? void 0 : _c.TABLES) === null || _d === void 0 ? void 0 : _d[tableName];
1365
1535
  if (!table || !table.COLUMNS)
1366
1536
  return false;
@@ -1403,6 +1573,26 @@ var ConditionBuilder = /** @class */ (function (_super) {
1403
1573
  // Normalize common variants
1404
1574
  var valueNorm = (value === C6C.NULL) ? null : value;
1405
1575
  var displayOp = typeof op === 'string' ? op.replace('_', ' ') : op;
1576
+ var extractSubSelect = function (input) {
1577
+ if (Array.isArray(input) && input.length >= 2 && input[0] === C6C.SUBSELECT) {
1578
+ return input[1];
1579
+ }
1580
+ if (input && typeof input === 'object' && C6C.SUBSELECT in input) {
1581
+ return input[C6C.SUBSELECT];
1582
+ }
1583
+ return undefined;
1584
+ };
1585
+ var rightSubSelectPayload = extractSubSelect(valueNorm);
1586
+ var buildSubSelect = function (payload) {
1587
+ if (!payload)
1588
+ return undefined;
1589
+ var builder = _this.buildScalarSubSelect;
1590
+ if (typeof builder !== 'function') {
1591
+ throw new Error('Scalar subselect handling requires JoinBuilder context.');
1592
+ }
1593
+ return builder.call(_this, payload, params);
1594
+ };
1595
+ var rightSubSelectSql = buildSubSelect(rightSubSelectPayload);
1406
1596
  // Support function-based expressions like [C6C.ST_DISTANCE_SPHERE, col1, col2]
1407
1597
  if (typeof column === 'string' &&
1408
1598
  _this.OPERATORS.has(column) &&
@@ -1429,7 +1619,7 @@ var ConditionBuilder = /** @class */ (function (_super) {
1429
1619
  var leftIsCol = _this.isColumnRef(column);
1430
1620
  var leftIsRef = _this.isTableReference(column);
1431
1621
  var rightIsCol = typeof value === 'string' && _this.isColumnRef(value);
1432
- if (!leftIsCol && !leftIsRef && !rightIsCol) {
1622
+ if (!leftIsCol && !leftIsRef && !rightIsCol && !rightSubSelectSql) {
1433
1623
  throw new Error("Potential SQL injection detected: '".concat(column, " ").concat(op, " ").concat(value, "'"));
1434
1624
  }
1435
1625
  _this.validateOperator(op);
@@ -1462,6 +1652,13 @@ var ConditionBuilder = /** @class */ (function (_super) {
1462
1652
  return matchClause;
1463
1653
  }
1464
1654
  if ((op === C6C.IN || op === C6C.NOT_IN) && Array.isArray(value)) {
1655
+ if (rightSubSelectSql) {
1656
+ if (!leftIsRef) {
1657
+ throw new Error("IN operator requires a table reference as the left operand. Column '".concat(column, "' is not a valid table reference."));
1658
+ }
1659
+ var normalized_1 = op.replace('_', ' ');
1660
+ return "( ".concat(column, " ").concat(normalized_1, " ").concat(rightSubSelectSql, " )");
1661
+ }
1465
1662
  var placeholders = value.map(function (v) {
1466
1663
  return _this.isColumnRef(v) ? v : _this.addParam(params, column, v);
1467
1664
  }).join(', ');
@@ -1481,10 +1678,13 @@ var ConditionBuilder = /** @class */ (function (_super) {
1481
1678
  }
1482
1679
  return "(".concat(column, ") ").concat(op.replace('_', ' '), " ").concat(_this.addParam(params, column, start), " AND ").concat(_this.addParam(params, column, end));
1483
1680
  }
1484
- var rightIsRef = _this.isTableReference(value);
1681
+ var rightIsRef = rightSubSelectSql ? false : _this.isTableReference(value);
1485
1682
  if (leftIsRef && rightIsRef) {
1486
1683
  return "(".concat(column, ") ").concat(displayOp, " ").concat(value);
1487
1684
  }
1685
+ if (leftIsRef && rightSubSelectSql) {
1686
+ return "(".concat(column, ") ").concat(displayOp, " ").concat(rightSubSelectSql);
1687
+ }
1488
1688
  if (leftIsRef && !rightIsRef) {
1489
1689
  return "(".concat(column, ") ").concat(displayOp, " ").concat(_this.addParam(params, column, valueNorm));
1490
1690
  }
@@ -1580,85 +1780,117 @@ var JoinBuilder = /** @class */ (function (_super) {
1580
1780
  function JoinBuilder() {
1581
1781
  return _super !== null && _super.apply(this, arguments) || this;
1582
1782
  }
1783
+ JoinBuilder.prototype.createSelectBuilder = function (_request) {
1784
+ throw new Error('Subclasses must implement createSelectBuilder to support derived table serialization.');
1785
+ };
1583
1786
  JoinBuilder.prototype.buildJoinClauses = function (joinArgs, params) {
1584
1787
  var sql = '';
1585
- for (var joinType in joinArgs) {
1788
+ var _loop_1 = function (joinType) {
1586
1789
  var joinKind = joinType.replace('_', ' ').toUpperCase();
1587
- for (var raw in joinArgs[joinType]) {
1588
- var _a = raw.split(' '), table = _a[0], alias = _a[1];
1589
- this.aliasMap[alias || table] = table;
1590
- var onClause = this.buildBooleanJoinedConditions(joinArgs[joinType][raw], true, params);
1591
- var joinSql = alias ? "`".concat(table, "` AS `").concat(alias, "`") : "`".concat(table, "`");
1592
- sql += " ".concat(joinKind, " JOIN ").concat(joinSql, " ON ").concat(onClause);
1790
+ var entries = [];
1791
+ var joinSection = joinArgs[joinType];
1792
+ if (joinSection instanceof Map) {
1793
+ joinSection.forEach(function (value, key) {
1794
+ entries.push([key, value]);
1795
+ });
1593
1796
  }
1797
+ else {
1798
+ for (var raw in joinSection) {
1799
+ entries.push([raw, joinSection[raw]]);
1800
+ }
1801
+ }
1802
+ for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
1803
+ var _a = entries_1[_i], rawKey = _a[0], conditions = _a[1];
1804
+ var raw = typeof rawKey === 'string' ? rawKey : String(rawKey);
1805
+ var _b = raw.trim().split(/\s+/, 2), table = _b[0], aliasCandidate = _b[1];
1806
+ if (!table)
1807
+ continue;
1808
+ if (isDerivedTableKey(table)) {
1809
+ var derived = resolveDerivedTable(table);
1810
+ if (!derived) {
1811
+ throw new Error("Derived table '".concat(table, "' was not registered. Wrap the object with derivedTable(...) before using it in JOIN."));
1812
+ }
1813
+ var configuredAliasRaw = derived[C6C.AS];
1814
+ var configuredAlias = typeof configuredAliasRaw === 'string' ? configuredAliasRaw.trim() : '';
1815
+ var alias = (aliasCandidate !== null && aliasCandidate !== void 0 ? aliasCandidate : configuredAlias).trim();
1816
+ if (!alias) {
1817
+ throw new Error('Derived tables require an alias via C6C.AS.');
1818
+ }
1819
+ this_1.registerAlias(alias, table);
1820
+ var subRequest = derived[C6C.SUBSELECT];
1821
+ if (!subRequest || typeof subRequest !== 'object') {
1822
+ throw new Error('Derived tables must include a C6C.SUBSELECT payload.');
1823
+ }
1824
+ var fromTable = subRequest[C6C.FROM];
1825
+ if (typeof fromTable !== 'string' || fromTable.trim() === '') {
1826
+ throw new Error('Derived table subselects require a base table defined with C6C.FROM.');
1827
+ }
1828
+ var subBuilder = this_1.createSelectBuilder(subRequest);
1829
+ var _c = subBuilder.build(fromTable, true), subSql = _c.sql, subParams = _c.params;
1830
+ var normalizedSql = this_1.integrateSubSelectParams(subSql, subParams, params);
1831
+ var formatted = normalizedSql.trim().split('\n').map(function (line) { return " ".concat(line); }).join('\n');
1832
+ var joinSql = "(\n".concat(formatted, "\n) AS `").concat(alias, "`");
1833
+ var onClause = this_1.buildBooleanJoinedConditions(conditions, true, params);
1834
+ sql += " ".concat(joinKind, " JOIN ").concat(joinSql);
1835
+ if (onClause) {
1836
+ sql += " ON ".concat(onClause);
1837
+ }
1838
+ }
1839
+ else {
1840
+ var alias = aliasCandidate;
1841
+ if (alias) {
1842
+ this_1.registerAlias(alias, table);
1843
+ }
1844
+ var joinSql = alias ? "`".concat(table, "` AS `").concat(alias, "`") : "`".concat(table, "`");
1845
+ var onClause = this_1.buildBooleanJoinedConditions(conditions, true, params);
1846
+ sql += " ".concat(joinKind, " JOIN ").concat(joinSql);
1847
+ if (onClause) {
1848
+ sql += " ON ".concat(onClause);
1849
+ }
1850
+ }
1851
+ }
1852
+ };
1853
+ var this_1 = this;
1854
+ for (var joinType in joinArgs) {
1855
+ _loop_1(joinType);
1594
1856
  }
1595
1857
  this.config.verbose && console.log("[JOIN] ".concat(sql.trim()));
1596
1858
  return sql;
1597
1859
  };
1598
- return JoinBuilder;
1599
- }(ConditionBuilder));
1600
-
1601
- var DeleteQueryBuilder = /** @class */ (function (_super) {
1602
- tslib.__extends(DeleteQueryBuilder, _super);
1603
- function DeleteQueryBuilder() {
1604
- return _super !== null && _super.apply(this, arguments) || this;
1605
- }
1606
- DeleteQueryBuilder.prototype.build = function (table) {
1607
- this.aliasMap = {};
1608
- var params = this.useNamedParams ? {} : [];
1609
- this.initAlias(table, this.request.JOIN);
1610
- var sql = "DELETE `".concat(table, "` FROM `").concat(table, "`");
1611
- if (this.request.JOIN) {
1612
- sql += this.buildJoinClauses(this.request.JOIN, params);
1613
- }
1614
- if (this.request.WHERE) {
1615
- sql += this.buildWhereClause(this.request.WHERE, params);
1616
- }
1617
- return { sql: sql, params: params };
1618
- };
1619
- return DeleteQueryBuilder;
1620
- }(JoinBuilder));
1621
-
1622
- var PostQueryBuilder = /** @class */ (function (_super) {
1623
- tslib.__extends(PostQueryBuilder, _super);
1624
- function PostQueryBuilder() {
1625
- return _super !== null && _super.apply(this, arguments) || this;
1626
- }
1627
- PostQueryBuilder.prototype.trimTablePrefix = function (table, column) {
1628
- if (!column.includes('.'))
1629
- return column;
1630
- var _a = column.split('.', 2), prefix = _a[0], col = _a[1];
1631
- if (prefix !== table) {
1632
- throw new Error("Invalid prefixed column: '".concat(column, "'. Expected prefix '").concat(table, ".'"));
1860
+ JoinBuilder.prototype.integrateSubSelectParams = function (subSql, subParams, target) {
1861
+ var _a;
1862
+ if (!subParams)
1863
+ return subSql;
1864
+ if (this.useNamedParams) {
1865
+ var normalized = subSql;
1866
+ var extras = subParams;
1867
+ for (var _i = 0, _b = Object.keys(extras); _i < _b.length; _i++) {
1868
+ var key = _b[_i];
1869
+ var placeholder = this.addParam(target, '', extras[key]);
1870
+ var original = ":".concat(key);
1871
+ if (original !== placeholder) {
1872
+ normalized = normalized.split(original).join(placeholder);
1873
+ }
1874
+ }
1875
+ return normalized;
1633
1876
  }
1634
- return col;
1877
+ (_a = target).push.apply(_a, subParams);
1878
+ return subSql;
1635
1879
  };
1636
- PostQueryBuilder.prototype.build = function (table) {
1637
- var _this = this;
1638
- this.aliasMap = {};
1639
- var verb = C6C.REPLACE in this.request ? C6C.REPLACE : C6C.INSERT;
1640
- var body = verb in this.request ? this.request[verb] : this.request;
1641
- var keys = Object.keys(body);
1642
- var params = [];
1643
- var placeholders = [];
1644
- for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
1645
- var key = keys_1[_i];
1646
- var value = body[key];
1647
- var placeholder = this.addParam(params, key, value);
1648
- placeholders.push(placeholder);
1880
+ JoinBuilder.prototype.buildScalarSubSelect = function (subRequest, params) {
1881
+ if (!subRequest || typeof subRequest !== 'object') {
1882
+ throw new Error('Scalar subselect requires a C6C.SUBSELECT object payload.');
1649
1883
  }
1650
- var sql = "".concat(verb, " INTO `").concat(table, "` (\n ").concat(keys.map(function (k) { return "`".concat(_this.trimTablePrefix(table, k), "`"); }).join(', '), "\n ) VALUES (\n ").concat(placeholders.join(', '), "\n )");
1651
- if (C6C.UPDATE in this.request) {
1652
- var updateData = this.request[C6C.UPDATE];
1653
- if (!Array.isArray(updateData)) {
1654
- throw new Error("Update data must be an array of keys to update, got: ".concat(JSON.stringify(updateData)));
1655
- }
1656
- var updateClause = updateData.map(function (k) { return "`".concat(k, "` = VALUES(`").concat(k, "`)"); }).join(', ');
1657
- sql += " ON DUPLICATE KEY UPDATE ".concat(updateClause);
1884
+ var fromTable = subRequest[C6C.FROM];
1885
+ if (typeof fromTable !== 'string' || fromTable.trim() === '') {
1886
+ throw new Error('Scalar subselects require a base table specified with C6C.FROM.');
1658
1887
  }
1659
- return { sql: sql, params: params };
1888
+ var subBuilder = this.createSelectBuilder(subRequest);
1889
+ var _a = subBuilder.build(fromTable, true), subSql = _a.sql, subParams = _a.params;
1890
+ var normalized = this.integrateSubSelectParams(subSql, subParams, params).trim();
1891
+ return "(".concat(normalized, ")");
1660
1892
  };
1661
- return PostQueryBuilder;
1893
+ return JoinBuilder;
1662
1894
  }(ConditionBuilder));
1663
1895
 
1664
1896
  var PaginationBuilder = /** @class */ (function (_super) {
@@ -1679,18 +1911,36 @@ var PaginationBuilder = /** @class */ (function (_super) {
1679
1911
  * }
1680
1912
  * ```
1681
1913
  */
1682
- PaginationBuilder.prototype.buildPaginationClause = function (pagination) {
1914
+ PaginationBuilder.prototype.buildPaginationClause = function (pagination, params) {
1683
1915
  var _this = this;
1684
1916
  var sql = "";
1685
1917
  /* -------- ORDER BY -------- */
1686
1918
  if (pagination === null || pagination === void 0 ? void 0 : pagination[C6Constants.ORDER]) {
1687
1919
  var orderParts = [];
1688
- for (var _i = 0, _a = Object.entries(pagination[C6Constants.ORDER]); _i < _a.length; _i++) {
1689
- var _b = _a[_i], key = _b[0], val = _b[1];
1920
+ var _loop_1 = function (key, val) {
1921
+ if (typeof key === 'string' && key.includes('.')) {
1922
+ this_1.assertValidIdentifier(key, 'ORDER BY');
1923
+ }
1690
1924
  // FUNCTION CALL: val is an array of args
1691
1925
  if (Array.isArray(val)) {
1926
+ var identifierPathRegex_1 = /^[A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_][A-Za-z0-9_]*$/;
1927
+ var isNumericString_1 = function (s) { return /^-?\d+(?:\.\d+)?$/.test(s.trim()); };
1692
1928
  var args = val
1693
- .map(function (arg) { return Array.isArray(arg) ? _this.buildAggregateField(arg) : String(arg); })
1929
+ .map(function (arg) {
1930
+ if (Array.isArray(arg))
1931
+ return _this.buildAggregateField(arg, params);
1932
+ if (typeof arg === 'string') {
1933
+ if (identifierPathRegex_1.test(arg)) {
1934
+ _this.assertValidIdentifier(arg, 'ORDER BY argument');
1935
+ return arg;
1936
+ }
1937
+ // numeric-looking strings should be treated as literals
1938
+ if (isNumericString_1(arg))
1939
+ return arg;
1940
+ return arg;
1941
+ }
1942
+ return String(arg);
1943
+ })
1694
1944
  .join(", ");
1695
1945
  orderParts.push("".concat(key, "(").concat(args, ")"));
1696
1946
  }
@@ -1698,6 +1948,11 @@ var PaginationBuilder = /** @class */ (function (_super) {
1698
1948
  else {
1699
1949
  orderParts.push("".concat(key, " ").concat(String(val).toUpperCase()));
1700
1950
  }
1951
+ };
1952
+ var this_1 = this;
1953
+ for (var _i = 0, _a = Object.entries(pagination[C6Constants.ORDER]); _i < _a.length; _i++) {
1954
+ var _b = _a[_i], key = _b[0], val = _b[1];
1955
+ _loop_1(key, val);
1701
1956
  }
1702
1957
  if (orderParts.length)
1703
1958
  sql += " ORDER BY ".concat(orderParts.join(", "));
@@ -1727,6 +1982,9 @@ var SelectQueryBuilder = /** @class */ (function (_super) {
1727
1982
  function SelectQueryBuilder() {
1728
1983
  return _super !== null && _super.apply(this, arguments) || this;
1729
1984
  }
1985
+ SelectQueryBuilder.prototype.createSelectBuilder = function (request) {
1986
+ return new SelectQueryBuilder(this.config, request, this.useNamedParams);
1987
+ };
1730
1988
  SelectQueryBuilder.prototype.build = function (table, isSubSelect) {
1731
1989
  var _this = this;
1732
1990
  var _a;
@@ -1741,7 +1999,7 @@ var SelectQueryBuilder = /** @class */ (function (_super) {
1741
1999
  var params = this.useNamedParams ? {} : [];
1742
2000
  var selectList = (_a = args.SELECT) !== null && _a !== void 0 ? _a : ['*'];
1743
2001
  var selectFields = selectList
1744
- .map(function (f) { return _this.buildAggregateField(f); })
2002
+ .map(function (f) { return _this.buildAggregateField(f, params); })
1745
2003
  .join(', ');
1746
2004
  var sql = "SELECT ".concat(selectFields, " FROM `").concat(table, "`");
1747
2005
  if (args.JOIN) {
@@ -1760,7 +2018,7 @@ var SelectQueryBuilder = /** @class */ (function (_super) {
1760
2018
  sql += " HAVING ".concat(this.buildBooleanJoinedConditions(args.HAVING, true, params));
1761
2019
  }
1762
2020
  if (args.PAGINATION) {
1763
- sql += this.buildPaginationClause(args.PAGINATION);
2021
+ sql += this.buildPaginationClause(args.PAGINATION, params);
1764
2022
  }
1765
2023
  else if (!isSubSelect) {
1766
2024
  sql += " LIMIT 100";
@@ -1771,11 +2029,80 @@ var SelectQueryBuilder = /** @class */ (function (_super) {
1771
2029
  return SelectQueryBuilder;
1772
2030
  }(PaginationBuilder));
1773
2031
 
2032
+ var DeleteQueryBuilder = /** @class */ (function (_super) {
2033
+ tslib.__extends(DeleteQueryBuilder, _super);
2034
+ function DeleteQueryBuilder() {
2035
+ return _super !== null && _super.apply(this, arguments) || this;
2036
+ }
2037
+ DeleteQueryBuilder.prototype.createSelectBuilder = function (request) {
2038
+ return new SelectQueryBuilder(this.config, request, this.useNamedParams);
2039
+ };
2040
+ DeleteQueryBuilder.prototype.build = function (table) {
2041
+ this.aliasMap = {};
2042
+ var params = this.useNamedParams ? {} : [];
2043
+ this.initAlias(table, this.request.JOIN);
2044
+ var sql = "DELETE `".concat(table, "` FROM `").concat(table, "`");
2045
+ if (this.request.JOIN) {
2046
+ sql += this.buildJoinClauses(this.request.JOIN, params);
2047
+ }
2048
+ if (this.request.WHERE) {
2049
+ sql += this.buildWhereClause(this.request.WHERE, params);
2050
+ }
2051
+ return { sql: sql, params: params };
2052
+ };
2053
+ return DeleteQueryBuilder;
2054
+ }(JoinBuilder));
2055
+
2056
+ var PostQueryBuilder = /** @class */ (function (_super) {
2057
+ tslib.__extends(PostQueryBuilder, _super);
2058
+ function PostQueryBuilder() {
2059
+ return _super !== null && _super.apply(this, arguments) || this;
2060
+ }
2061
+ PostQueryBuilder.prototype.trimTablePrefix = function (table, column) {
2062
+ if (!column.includes('.'))
2063
+ return column;
2064
+ var _a = column.split('.', 2), prefix = _a[0], col = _a[1];
2065
+ if (prefix !== table) {
2066
+ throw new Error("Invalid prefixed column: '".concat(column, "'. Expected prefix '").concat(table, ".'"));
2067
+ }
2068
+ return col;
2069
+ };
2070
+ PostQueryBuilder.prototype.build = function (table) {
2071
+ var _this = this;
2072
+ this.aliasMap = {};
2073
+ var verb = C6C.REPLACE in this.request ? C6C.REPLACE : C6C.INSERT;
2074
+ var body = verb in this.request ? this.request[verb] : this.request;
2075
+ var keys = Object.keys(body);
2076
+ var params = [];
2077
+ var placeholders = [];
2078
+ for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
2079
+ var key = keys_1[_i];
2080
+ var value = body[key];
2081
+ var placeholder = this.addParam(params, key, value);
2082
+ placeholders.push(placeholder);
2083
+ }
2084
+ var sql = "".concat(verb, " INTO `").concat(table, "` (\n ").concat(keys.map(function (k) { return "`".concat(_this.trimTablePrefix(table, k), "`"); }).join(', '), "\n ) VALUES (\n ").concat(placeholders.join(', '), "\n )");
2085
+ if (C6C.UPDATE in this.request) {
2086
+ var updateData = this.request[C6C.UPDATE];
2087
+ if (!Array.isArray(updateData)) {
2088
+ throw new Error("Update data must be an array of keys to update, got: ".concat(JSON.stringify(updateData)));
2089
+ }
2090
+ var updateClause = updateData.map(function (k) { return "`".concat(k, "` = VALUES(`").concat(k, "`)"); }).join(', ');
2091
+ sql += " ON DUPLICATE KEY UPDATE ".concat(updateClause);
2092
+ }
2093
+ return { sql: sql, params: params };
2094
+ };
2095
+ return PostQueryBuilder;
2096
+ }(ConditionBuilder));
2097
+
1774
2098
  var UpdateQueryBuilder = /** @class */ (function (_super) {
1775
2099
  tslib.__extends(UpdateQueryBuilder, _super);
1776
2100
  function UpdateQueryBuilder() {
1777
2101
  return _super !== null && _super.apply(this, arguments) || this;
1778
2102
  }
2103
+ UpdateQueryBuilder.prototype.createSelectBuilder = function (request) {
2104
+ return new SelectQueryBuilder(this.config, request, this.useNamedParams);
2105
+ };
1779
2106
  UpdateQueryBuilder.prototype.trimTablePrefix = function (table, column) {
1780
2107
  if (!column.includes('.'))
1781
2108
  return column;
@@ -1810,7 +2137,7 @@ var UpdateQueryBuilder = /** @class */ (function (_super) {
1810
2137
  sql += this.buildWhereClause(args.WHERE, params);
1811
2138
  }
1812
2139
  if (args.PAGINATION) {
1813
- sql += this.buildPaginationClause(args.PAGINATION);
2140
+ sql += this.buildPaginationClause(args.PAGINATION, params);
1814
2141
  }
1815
2142
  return { sql: sql, params: params };
1816
2143
  };
@@ -2206,41 +2533,6 @@ function ExpressHandler(_a) {
2206
2533
  }); };
2207
2534
  }
2208
2535
 
2209
- // Alias a table name with a given alias
2210
- var A = function (tableName, alias) {
2211
- return "".concat(tableName, " ").concat(alias);
2212
- };
2213
- // Qualify a column constant (e.g. 'property_units.parcel_id') to an alias
2214
- var F = function (qualifiedCol, alias) {
2215
- return "".concat(alias, ".").concat(qualifiedCol.split('.').pop());
2216
- };
2217
- // Equal join condition using full-qualified column constants
2218
- var fieldEq = function (leftCol, rightCol, leftAlias, rightAlias) {
2219
- var _a;
2220
- return (_a = {},
2221
- _a[F(leftCol, leftAlias)] = F(rightCol, rightAlias),
2222
- _a);
2223
- };
2224
- // ST_Distance_Sphere for aliased fields
2225
- var distSphere = function (fromCol, toCol, fromAlias, toAlias) {
2226
- return [C6C.ST_DISTANCE_SPHERE, F(fromCol, fromAlias), F(toCol, toAlias)];
2227
- };
2228
- // Build a bounding-box expression.
2229
- //
2230
- // Arguments must be provided in `(minLng, minLat, maxLng, maxLat)` order. The
2231
- // helper does not attempt to swap or validate coordinates; if a minimum value
2232
- // is greater than its corresponding maximum value, MySQL's `ST_MakeEnvelope`
2233
- // returns `NULL`.
2234
- var bbox = function (minLng, minLat, maxLng, maxLat) {
2235
- return [C6C.ST_SRID, [C6C.ST_MAKEENVELOPE,
2236
- [C6C.ST_POINT, minLng, minLat],
2237
- [C6C.ST_POINT, maxLng, maxLat]], 4326];
2238
- };
2239
- // ST_Contains for map envelope/shape queries
2240
- var stContains = function (envelope, shape) {
2241
- return [C6C.ST_CONTAINS, envelope, shape];
2242
- };
2243
-
2244
2536
  function determineRuntimeJsType(mysqlType) {
2245
2537
  var base = mysqlType.toLowerCase().split('(')[0];
2246
2538
  if ([
@@ -2369,6 +2661,7 @@ exports.checkCache = checkCache;
2369
2661
  exports.clearCache = clearCache;
2370
2662
  exports.convertForRequestBody = convertForRequestBody;
2371
2663
  exports.convertHexIfBinary = convertHexIfBinary;
2664
+ exports.derivedTable = derivedTable;
2372
2665
  exports.determineRuntimeJsType = determineRuntimeJsType;
2373
2666
  exports.distSphere = distSphere;
2374
2667
  exports.error = error;
@@ -2377,6 +2670,7 @@ exports.getEnvVar = getEnvVar;
2377
2670
  exports.getPrimaryKeyTypes = getPrimaryKeyTypes;
2378
2671
  exports.group = group;
2379
2672
  exports.info = info;
2673
+ exports.isDerivedTableKey = isDerivedTableKey;
2380
2674
  exports.isLocal = isLocal;
2381
2675
  exports.isNode = isNode;
2382
2676
  exports.isTest = isTest;
@@ -2386,6 +2680,7 @@ exports.onError = onError;
2386
2680
  exports.onSuccess = onSuccess;
2387
2681
  exports.removeInvalidKeys = removeInvalidKeys;
2388
2682
  exports.removePrefixIfExists = removePrefixIfExists;
2683
+ exports.resolveDerivedTable = resolveDerivedTable;
2389
2684
  exports.restOrm = restOrm;
2390
2685
  exports.restRequest = restRequest;
2391
2686
  exports.sortAndSerializeQueryObject = sortAndSerializeQueryObject;