@rex0220/kintone-sql-tools 2.14.1 → 2.16.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-cli/ksql.js +391 -56
- package/dist-mcp/ksql-mcp.js +392 -57
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -87,6 +87,10 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
87
87
|
["AVG", "AVG" /* AVG */],
|
|
88
88
|
["MAX", "MAX" /* MAX */],
|
|
89
89
|
["MIN", "MIN" /* MIN */],
|
|
90
|
+
["GROUP_CONCAT", "GROUP_CONCAT" /* GROUP_CONCAT */],
|
|
91
|
+
["ROW_NUMBER", "ROW_NUMBER" /* ROW_NUMBER */],
|
|
92
|
+
["RANK", "RANK" /* RANK */],
|
|
93
|
+
["DENSE_RANK", "DENSE_RANK" /* DENSE_RANK */],
|
|
90
94
|
["ASSERT", "ASSERT" /* ASSERT */],
|
|
91
95
|
["AND", "AND" /* AND */],
|
|
92
96
|
["OR", "OR" /* OR */],
|
|
@@ -463,6 +467,9 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
|
|
|
463
467
|
"AVG" /* AVG */,
|
|
464
468
|
"MAX" /* MAX */,
|
|
465
469
|
"MIN" /* MIN */,
|
|
470
|
+
"ROW_NUMBER" /* ROW_NUMBER */,
|
|
471
|
+
"RANK" /* RANK */,
|
|
472
|
+
"DENSE_RANK" /* DENSE_RANK */,
|
|
466
473
|
"TODAY" /* TODAY */,
|
|
467
474
|
"NOW" /* NOW */,
|
|
468
475
|
"LOGINUSER" /* LOGINUSER */,
|
|
@@ -981,6 +988,11 @@ var Parser = class {
|
|
|
981
988
|
const orderBy = this.consume("ORDER" /* ORDER */) ? (this.expect("BY" /* BY */), this.parseOrderBy()) : [];
|
|
982
989
|
const limit = this.consume("LIMIT" /* LIMIT */) ? this.parseUnsignedInt() : null;
|
|
983
990
|
const offset = this.consume("OFFSET" /* OFFSET */) ? this.parseUnsignedInt() : null;
|
|
991
|
+
const hasWindow = columns.some((column) => column.type === "WINDOW_COL");
|
|
992
|
+
const hasAggregate = columns.some((column) => this.selectColumnHasAggregate(column));
|
|
993
|
+
if (hasWindow && (groupBy.length > 0 || hasAggregate)) {
|
|
994
|
+
throw new ParseError("\u30A6\u30A3\u30F3\u30C9\u30A6\u95A2\u6570\u306F GROUP BY / \u96C6\u8A08\u95A2\u6570\u3068\u540C\u3058 SELECT \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", this.peek());
|
|
995
|
+
}
|
|
984
996
|
return {
|
|
985
997
|
type: "SELECT",
|
|
986
998
|
distinct,
|
|
@@ -1045,6 +1057,10 @@ var Parser = class {
|
|
|
1045
1057
|
if (this.consume("*" /* STAR */)) {
|
|
1046
1058
|
return { type: "WILDCARD" };
|
|
1047
1059
|
}
|
|
1060
|
+
const windowFunc = this.tryWindowFunc();
|
|
1061
|
+
if (windowFunc !== null) {
|
|
1062
|
+
return this.parseWindowColumn(windowFunc);
|
|
1063
|
+
}
|
|
1048
1064
|
if (this.peek().kind === "CASE" /* CASE */) {
|
|
1049
1065
|
const expr = this.parseCaseWhenExpr();
|
|
1050
1066
|
const alias2 = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
|
|
@@ -1079,6 +1095,7 @@ var Parser = class {
|
|
|
1079
1095
|
func: ref.func,
|
|
1080
1096
|
distinct: ref.distinct,
|
|
1081
1097
|
arg: ref.arg,
|
|
1098
|
+
...ref.separator !== void 0 ? { separator: ref.separator } : {},
|
|
1082
1099
|
alias: alias2
|
|
1083
1100
|
};
|
|
1084
1101
|
}
|
|
@@ -1115,6 +1132,56 @@ var Parser = class {
|
|
|
1115
1132
|
const alias = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
|
|
1116
1133
|
return { type: "FIELD", field, alias };
|
|
1117
1134
|
}
|
|
1135
|
+
tryWindowFunc() {
|
|
1136
|
+
switch (this.peek().kind) {
|
|
1137
|
+
case "ROW_NUMBER" /* ROW_NUMBER */:
|
|
1138
|
+
return "ROW_NUMBER";
|
|
1139
|
+
case "RANK" /* RANK */:
|
|
1140
|
+
return "RANK";
|
|
1141
|
+
case "DENSE_RANK" /* DENSE_RANK */:
|
|
1142
|
+
return "DENSE_RANK";
|
|
1143
|
+
default:
|
|
1144
|
+
return null;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
parseWindowColumn(func) {
|
|
1148
|
+
this.advance();
|
|
1149
|
+
this.expect("(" /* LPAREN */);
|
|
1150
|
+
if (this.peek().kind !== ")" /* RPAREN */) {
|
|
1151
|
+
throw new ParseError(`${func} \u306F\u5F15\u6570\u3092\u53D7\u3051\u4ED8\u3051\u307E\u305B\u3093`, this.peek());
|
|
1152
|
+
}
|
|
1153
|
+
this.expect(")" /* RPAREN */);
|
|
1154
|
+
this.expectSoftKeyword("OVER", `${func} \u306B\u306F OVER (...) \u304C\u5FC5\u8981\u3067\u3059`);
|
|
1155
|
+
this.expect("(" /* LPAREN */);
|
|
1156
|
+
const partitionBy = [];
|
|
1157
|
+
if (this.isSoftKeyword("PARTITION")) {
|
|
1158
|
+
this.advance();
|
|
1159
|
+
this.expect("BY" /* BY */, "PARTITION \u306E\u5F8C\u306B\u306F BY \u304C\u5FC5\u8981\u3067\u3059");
|
|
1160
|
+
do {
|
|
1161
|
+
const ref = this.parseQualifiedIdent();
|
|
1162
|
+
partitionBy.push({ type: "FIELD", tableAlias: ref.tableAlias, field: ref.field });
|
|
1163
|
+
} while (this.consume("," /* COMMA */));
|
|
1164
|
+
}
|
|
1165
|
+
const orderBy = this.consume("ORDER" /* ORDER */) ? (this.expect("BY" /* BY */), this.parseOrderBy()) : [];
|
|
1166
|
+
this.expect(")" /* RPAREN */);
|
|
1167
|
+
if (!this.consume("AS" /* AS */)) {
|
|
1168
|
+
throw new ParseError("\u30A6\u30A3\u30F3\u30C9\u30A6\u95A2\u6570\u306B\u306F AS alias \u304C\u5FC5\u8981\u3067\u3059", this.peek());
|
|
1169
|
+
}
|
|
1170
|
+
const alias = this.parseAliasName();
|
|
1171
|
+
return { type: "WINDOW_COL", func, partitionBy, orderBy, alias };
|
|
1172
|
+
}
|
|
1173
|
+
selectColumnHasAggregate(column) {
|
|
1174
|
+
if (column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL") return true;
|
|
1175
|
+
if (column.type !== "STRFUNC_COL") return false;
|
|
1176
|
+
return column.expr.args.some((arg) => this.stringFuncArgHasAggregate(arg));
|
|
1177
|
+
}
|
|
1178
|
+
stringFuncArgHasAggregate(arg) {
|
|
1179
|
+
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
1180
|
+
if (arg.type === "STRING_FUNC") {
|
|
1181
|
+
return arg.args.some((nested) => this.stringFuncArgHasAggregate(nested));
|
|
1182
|
+
}
|
|
1183
|
+
return false;
|
|
1184
|
+
}
|
|
1118
1185
|
isArithOp(kind) {
|
|
1119
1186
|
return kind === "+" /* PLUS */ || kind === "-" /* MINUS */ || kind === "*" /* STAR */ || kind === "/" /* SLASH */ || kind === "%" /* PERCENT */;
|
|
1120
1187
|
}
|
|
@@ -1450,7 +1517,8 @@ var Parser = class {
|
|
|
1450
1517
|
["SUM" /* SUM */]: "SUM",
|
|
1451
1518
|
["AVG" /* AVG */]: "AVG",
|
|
1452
1519
|
["MAX" /* MAX */]: "MAX",
|
|
1453
|
-
["MIN" /* MIN */]: "MIN"
|
|
1520
|
+
["MIN" /* MIN */]: "MIN",
|
|
1521
|
+
["GROUP_CONCAT" /* GROUP_CONCAT */]: "GROUP_CONCAT"
|
|
1454
1522
|
};
|
|
1455
1523
|
const kind = this.peek().kind;
|
|
1456
1524
|
return map[kind] ?? null;
|
|
@@ -1462,12 +1530,29 @@ var Parser = class {
|
|
|
1462
1530
|
const distinct = this.consume("DISTINCT" /* DISTINCT */);
|
|
1463
1531
|
let arg;
|
|
1464
1532
|
if (this.consume("*" /* STAR */)) {
|
|
1533
|
+
if (func === "GROUP_CONCAT") {
|
|
1534
|
+
throw new ParseError("GROUP_CONCAT(*) \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002\u30D5\u30A3\u30FC\u30EB\u30C9\u307E\u305F\u306F\u5F0F\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044", this.prev());
|
|
1535
|
+
}
|
|
1465
1536
|
arg = { type: "WILDCARD" };
|
|
1466
1537
|
} else {
|
|
1467
1538
|
arg = this.parseArithAddSub();
|
|
1468
1539
|
}
|
|
1540
|
+
let separator;
|
|
1541
|
+
if (this.isSoftKeyword("SEPARATOR")) {
|
|
1542
|
+
const separatorToken = this.advance();
|
|
1543
|
+
if (func !== "GROUP_CONCAT") {
|
|
1544
|
+
throw new ParseError("SEPARATOR \u306F GROUP_CONCAT \u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059", separatorToken);
|
|
1545
|
+
}
|
|
1546
|
+
separator = this.expect("STRING" /* STRING */, "SEPARATOR \u306E\u5F8C\u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059").value;
|
|
1547
|
+
}
|
|
1469
1548
|
this.expect(")" /* RPAREN */);
|
|
1470
|
-
return {
|
|
1549
|
+
return {
|
|
1550
|
+
type: "AGG_REF",
|
|
1551
|
+
func,
|
|
1552
|
+
distinct,
|
|
1553
|
+
arg,
|
|
1554
|
+
...separator !== void 0 ? { separator } : {}
|
|
1555
|
+
};
|
|
1471
1556
|
}
|
|
1472
1557
|
// ----------------------------------------------------------
|
|
1473
1558
|
// FROM / JOIN
|
|
@@ -1745,10 +1830,20 @@ var Parser = class {
|
|
|
1745
1830
|
const distinct = this.consume("DISTINCT" /* DISTINCT */);
|
|
1746
1831
|
let argStr;
|
|
1747
1832
|
if (this.consume("*" /* STAR */)) {
|
|
1833
|
+
if (aggFunc === "GROUP_CONCAT") {
|
|
1834
|
+
throw new ParseError("GROUP_CONCAT(*) \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002\u30D5\u30A3\u30FC\u30EB\u30C9\u307E\u305F\u306F\u5F0F\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044", this.prev());
|
|
1835
|
+
}
|
|
1748
1836
|
argStr = "*";
|
|
1749
1837
|
} else {
|
|
1750
1838
|
argStr = this.parseIdentifier();
|
|
1751
1839
|
}
|
|
1840
|
+
if (this.isSoftKeyword("SEPARATOR")) {
|
|
1841
|
+
const separatorToken = this.advance();
|
|
1842
|
+
if (aggFunc !== "GROUP_CONCAT") {
|
|
1843
|
+
throw new ParseError("SEPARATOR \u306F GROUP_CONCAT \u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059", separatorToken);
|
|
1844
|
+
}
|
|
1845
|
+
this.expect("STRING" /* STRING */, "SEPARATOR \u306E\u5F8C\u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059");
|
|
1846
|
+
}
|
|
1752
1847
|
this.expect(")" /* RPAREN */);
|
|
1753
1848
|
const syntheticName = distinct ? `${aggFunc}(DISTINCT ${argStr})` : `${aggFunc}(${argStr})`;
|
|
1754
1849
|
return { type: "FIELD", tableAlias: null, field: syntheticName };
|
|
@@ -2868,12 +2963,16 @@ var KintoneQueryError = class extends Error {
|
|
|
2868
2963
|
};
|
|
2869
2964
|
|
|
2870
2965
|
// src/converter/selectToKintone.ts
|
|
2966
|
+
function hasWindowColumns(columns) {
|
|
2967
|
+
return columns.some((column) => column.type === "WINDOW_COL");
|
|
2968
|
+
}
|
|
2871
2969
|
function resolveSelectMode(stmt) {
|
|
2872
2970
|
if (stmt.from.subtableCode) return "FULL_SCAN";
|
|
2873
2971
|
if (stmt.joins.some((j) => j.table.subtableCode)) return "FULL_SCAN";
|
|
2874
2972
|
if (stmt.joins.length > 0) return "FULL_SCAN";
|
|
2875
2973
|
if (stmt.groupBy.length > 0) return "FULL_SCAN";
|
|
2876
2974
|
if (stmt.distinct) return "FULL_SCAN";
|
|
2975
|
+
if (hasWindowColumns(stmt.columns)) return "FULL_SCAN";
|
|
2877
2976
|
if (stmt.columns.some(
|
|
2878
2977
|
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "SCALAR_SUBQUERY_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr(c.expr)
|
|
2879
2978
|
)) return "FULL_SCAN";
|
|
@@ -3245,16 +3344,16 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
3245
3344
|
}
|
|
3246
3345
|
walkStringFunc(k.expr, "groupBy");
|
|
3247
3346
|
};
|
|
3248
|
-
const walkOrderByKey = (k) => {
|
|
3347
|
+
const walkOrderByKey = (k, phase = "orderBy") => {
|
|
3249
3348
|
if (k.type === "FIELD_NAME") {
|
|
3250
|
-
addFieldName(k.name,
|
|
3349
|
+
addFieldName(k.name, phase);
|
|
3251
3350
|
return;
|
|
3252
3351
|
}
|
|
3253
3352
|
if (k.type === "ARITH_KEY") {
|
|
3254
|
-
walkArith(k.expr,
|
|
3353
|
+
walkArith(k.expr, phase);
|
|
3255
3354
|
return;
|
|
3256
3355
|
}
|
|
3257
|
-
walkStringFunc(k.expr,
|
|
3356
|
+
walkStringFunc(k.expr, phase);
|
|
3258
3357
|
};
|
|
3259
3358
|
for (const col of stmt.columns) {
|
|
3260
3359
|
switch (col.type) {
|
|
@@ -3286,6 +3385,10 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
3286
3385
|
break;
|
|
3287
3386
|
case "SCALAR_SUBQUERY_COL":
|
|
3288
3387
|
break;
|
|
3388
|
+
case "WINDOW_COL":
|
|
3389
|
+
for (const ref of col.partitionBy) addFieldRef(ref.field, ref.tableAlias, "select");
|
|
3390
|
+
for (const item of col.orderBy) walkOrderByKey(item.key, "select");
|
|
3391
|
+
break;
|
|
3289
3392
|
}
|
|
3290
3393
|
}
|
|
3291
3394
|
for (const join2 of stmt.joins) {
|
|
@@ -3332,6 +3435,10 @@ function collectSelectOutputNames(columns) {
|
|
|
3332
3435
|
}
|
|
3333
3436
|
if (col.type === "SCALAR_SUBQUERY_COL") {
|
|
3334
3437
|
names.add(col.alias ?? "(subquery)");
|
|
3438
|
+
continue;
|
|
3439
|
+
}
|
|
3440
|
+
if (col.type === "WINDOW_COL") {
|
|
3441
|
+
names.add(col.alias);
|
|
3335
3442
|
}
|
|
3336
3443
|
}
|
|
3337
3444
|
return names;
|
|
@@ -3357,7 +3464,7 @@ function stringFuncLabel(expr) {
|
|
|
3357
3464
|
return `${expr.func}(${args.join(",")})`;
|
|
3358
3465
|
}
|
|
3359
3466
|
function isAggregateSyntheticName(name) {
|
|
3360
|
-
return /^(COUNT|SUM|AVG|MAX|MIN)\(/i.test(name);
|
|
3467
|
+
return /^(COUNT|SUM|AVG|MAX|MIN|GROUP_CONCAT)\(/i.test(name);
|
|
3361
3468
|
}
|
|
3362
3469
|
|
|
3363
3470
|
// src/core/cteInlining.ts
|
|
@@ -5123,7 +5230,7 @@ function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind) {
|
|
|
5123
5230
|
for (const col of columns) {
|
|
5124
5231
|
if (col.type === "AGGREGATE") {
|
|
5125
5232
|
const syntheticKey = aggregateSyntheticName2(col.func, col.distinct, col.arg);
|
|
5126
|
-
const value = String(evalAggregate(col.func, col.distinct, col.arg, groupRows, resolveAggSortKind));
|
|
5233
|
+
const value = String(evalAggregate(col.func, col.distinct, col.arg, col.separator, groupRows, resolveAggSortKind));
|
|
5127
5234
|
outRow[col.alias ?? syntheticKey] = value;
|
|
5128
5235
|
if (col.alias) outRow[syntheticKey] = value;
|
|
5129
5236
|
} else if (col.type === "ARITH_AGG_COL") {
|
|
@@ -5144,7 +5251,7 @@ function evalGroupByKey(key, row) {
|
|
|
5144
5251
|
if (key.type === "FUNC_KEY") return evalStringFunc(key.expr, row);
|
|
5145
5252
|
return String(evalArithExpr(key.expr, row));
|
|
5146
5253
|
}
|
|
5147
|
-
function evalAggregate(func, distinct, arg, rows, resolveAggSortKind) {
|
|
5254
|
+
function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind) {
|
|
5148
5255
|
if (arg.type === "WILDCARD") {
|
|
5149
5256
|
return func === "COUNT" ? rows.length : 0;
|
|
5150
5257
|
}
|
|
@@ -5164,6 +5271,7 @@ function evalAggregate(func, distinct, arg, rows, resolveAggSortKind) {
|
|
|
5164
5271
|
}
|
|
5165
5272
|
const eff = distinct ? [...new Set(strValues)] : strValues;
|
|
5166
5273
|
if (func === "COUNT") return eff.length;
|
|
5274
|
+
if (func === "GROUP_CONCAT") return eff.join(separator ?? ",");
|
|
5167
5275
|
const sortKind = (func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" ? resolveAggSortKind?.(toAggregateFieldRef(arg.field)) : void 0;
|
|
5168
5276
|
if (sortKind === "string") {
|
|
5169
5277
|
if (eff.length === 0) return "";
|
|
@@ -5208,7 +5316,7 @@ function minOf(nums) {
|
|
|
5208
5316
|
}
|
|
5209
5317
|
function evalAggArithExpr(node, rows, resolveAggSortKind) {
|
|
5210
5318
|
if (node.type === "NUMBER") return node.value;
|
|
5211
|
-
if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, rows, resolveAggSortKind));
|
|
5319
|
+
if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, node.separator, rows, resolveAggSortKind));
|
|
5212
5320
|
const l = evalAggArithExpr(node.left, rows, resolveAggSortKind);
|
|
5213
5321
|
const r = evalAggArithExpr(node.right, rows, resolveAggSortKind);
|
|
5214
5322
|
switch (node.op) {
|
|
@@ -5278,6 +5386,10 @@ function buildDistinctKeyBuilder(rows, columns) {
|
|
|
5278
5386
|
values.push(row[col.field] ?? "");
|
|
5279
5387
|
continue;
|
|
5280
5388
|
}
|
|
5389
|
+
if (col.type === "WINDOW_COL") {
|
|
5390
|
+
values.push(row[col.alias] ?? "");
|
|
5391
|
+
continue;
|
|
5392
|
+
}
|
|
5281
5393
|
if (col.type === "PARENT_WILDCARD") {
|
|
5282
5394
|
for (const k of sortedParentKeys) {
|
|
5283
5395
|
values.push(row[k] !== void 0 ? row[k] : null);
|
|
@@ -5289,6 +5401,9 @@ function buildDistinctKeyBuilder(rows, columns) {
|
|
|
5289
5401
|
}
|
|
5290
5402
|
function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
|
|
5291
5403
|
if (orderBy.length === 0) return rows;
|
|
5404
|
+
return sortDecoratedRows(rows, orderBy, optionOrders, sortKinds).rows.map((item) => item.row);
|
|
5405
|
+
}
|
|
5406
|
+
function sortDecoratedRows(rows, orderBy, optionOrders, sortKinds) {
|
|
5292
5407
|
const keyMeta = orderBy.map(({ key }) => ({
|
|
5293
5408
|
orderMap: key.type === "FIELD_NAME" ? optionOrders?.get(key.name) : void 0,
|
|
5294
5409
|
sortKind: key.type === "FIELD_NAME" ? sortKinds?.get(key.name) : void 0
|
|
@@ -5307,14 +5422,16 @@ function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
|
|
|
5307
5422
|
};
|
|
5308
5423
|
})
|
|
5309
5424
|
}));
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5425
|
+
const compare = (a, b) => compareDecoratedRows(a, b, orderBy, keyMeta);
|
|
5426
|
+
decorated.sort(compare);
|
|
5427
|
+
return { rows: decorated, compare };
|
|
5428
|
+
}
|
|
5429
|
+
function compareDecoratedRows(a, b, orderBy, keyMeta) {
|
|
5430
|
+
for (let i = 0; i < orderBy.length; i++) {
|
|
5431
|
+
const cmp = compareSortKeys(a.keys[i], b.keys[i], keyMeta[i]);
|
|
5432
|
+
if (cmp !== 0) return orderBy[i].direction === "ASC" ? cmp : -cmp;
|
|
5433
|
+
}
|
|
5434
|
+
return 0;
|
|
5318
5435
|
}
|
|
5319
5436
|
function compareSortKeys(a, b, meta) {
|
|
5320
5437
|
if (meta.orderMap) {
|
|
@@ -5359,6 +5476,38 @@ function minChoiceIndex(values, orderMap) {
|
|
|
5359
5476
|
}
|
|
5360
5477
|
return min;
|
|
5361
5478
|
}
|
|
5479
|
+
function applyWindow(rows, columns, optionOrders, sortKinds) {
|
|
5480
|
+
const windows = columns.filter((column) => column.type === "WINDOW_COL");
|
|
5481
|
+
if (rows.length === 0 || windows.length === 0) return rows;
|
|
5482
|
+
for (const window of windows) {
|
|
5483
|
+
const partitions = /* @__PURE__ */ new Map();
|
|
5484
|
+
for (const row of rows) {
|
|
5485
|
+
const key = JSON.stringify(window.partitionBy.map((ref) => resolveWindowField(row, ref)));
|
|
5486
|
+
const partition = partitions.get(key);
|
|
5487
|
+
if (partition) partition.push(row);
|
|
5488
|
+
else partitions.set(key, [row]);
|
|
5489
|
+
}
|
|
5490
|
+
for (const partition of partitions.values()) {
|
|
5491
|
+
const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds);
|
|
5492
|
+
const sorted = sortedResult.rows;
|
|
5493
|
+
let rank = 1;
|
|
5494
|
+
let denseRank = 1;
|
|
5495
|
+
for (let index = 0; index < sorted.length; index++) {
|
|
5496
|
+
if (index > 0 && sortedResult.compare(sorted[index - 1], sorted[index]) !== 0) {
|
|
5497
|
+
rank = index + 1;
|
|
5498
|
+
denseRank++;
|
|
5499
|
+
}
|
|
5500
|
+
const value = window.func === "ROW_NUMBER" ? index + 1 : window.func === "RANK" ? rank : denseRank;
|
|
5501
|
+
sorted[index].row[window.alias] = String(value);
|
|
5502
|
+
}
|
|
5503
|
+
}
|
|
5504
|
+
}
|
|
5505
|
+
return rows;
|
|
5506
|
+
}
|
|
5507
|
+
function resolveWindowField(row, ref) {
|
|
5508
|
+
const name = ref.tableAlias ? `${ref.tableAlias}.${ref.field}` : ref.field;
|
|
5509
|
+
return resolveFieldRef(row, name);
|
|
5510
|
+
}
|
|
5362
5511
|
function applyLimit(rows, limit, offset) {
|
|
5363
5512
|
const start = offset ?? 0;
|
|
5364
5513
|
if (limit === null) return rows.slice(start);
|
|
@@ -5449,6 +5598,12 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
|
|
|
5449
5598
|
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
5450
5599
|
break;
|
|
5451
5600
|
}
|
|
5601
|
+
case "WINDOW_COL": {
|
|
5602
|
+
const key = outputKeys?.[colIdx] ?? col.alias;
|
|
5603
|
+
out[key] = row[col.alias] ?? "";
|
|
5604
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
5605
|
+
break;
|
|
5606
|
+
}
|
|
5452
5607
|
}
|
|
5453
5608
|
}
|
|
5454
5609
|
return out;
|
|
@@ -5489,6 +5644,8 @@ function computeOutputKey(col, colIdx, defaultFieldKeys) {
|
|
|
5489
5644
|
return col.alias ?? stringFuncDefaultKey(col.expr);
|
|
5490
5645
|
case "SCALAR_SUBQUERY_COL":
|
|
5491
5646
|
return col.alias ?? "(subquery)";
|
|
5647
|
+
case "WINDOW_COL":
|
|
5648
|
+
return col.alias;
|
|
5492
5649
|
case "WILDCARD":
|
|
5493
5650
|
case "PARENT_WILDCARD":
|
|
5494
5651
|
throw new Error("internal: computeOutputKey received a wildcard column");
|
|
@@ -5557,7 +5714,7 @@ function hasAggregateInStringFuncExpr2(expr) {
|
|
|
5557
5714
|
}
|
|
5558
5715
|
function resolveAggInStringFuncArg(arg, rows, resolveAggSortKind) {
|
|
5559
5716
|
if (arg.type === "AGG_REF") {
|
|
5560
|
-
const value = evalAggregate(arg.func, arg.distinct, arg.arg, rows, resolveAggSortKind);
|
|
5717
|
+
const value = evalAggregate(arg.func, arg.distinct, arg.arg, arg.separator, rows, resolveAggSortKind);
|
|
5561
5718
|
return typeof value === "number" ? { type: "NUMBER", value } : { type: "STRING", value };
|
|
5562
5719
|
}
|
|
5563
5720
|
if (arg.type === "AGG_ARITH") {
|
|
@@ -5603,6 +5760,7 @@ function runFullScan(input) {
|
|
|
5603
5760
|
rows = applyGroupBy(rows, stmt.groupBy, stmt.columns, aggregateSortKindResolver);
|
|
5604
5761
|
}
|
|
5605
5762
|
rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
|
|
5763
|
+
rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds);
|
|
5606
5764
|
if (stmt.distinct) {
|
|
5607
5765
|
rows = applyDistinct(rows, stmt.columns);
|
|
5608
5766
|
}
|
|
@@ -5803,7 +5961,8 @@ function isValidTemporal(value, type) {
|
|
|
5803
5961
|
const date = new Date(Date.UTC(year, month - 1, day));
|
|
5804
5962
|
if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day) return false;
|
|
5805
5963
|
if (type === "DATE") return true;
|
|
5806
|
-
|
|
5964
|
+
const timePart = value.slice(11, value.endsWith("Z") ? -1 : value.length - 6).replace(/\.\d+$/, "");
|
|
5965
|
+
return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(value) && isValidTemporal(timePart, "TIME");
|
|
5807
5966
|
}
|
|
5808
5967
|
|
|
5809
5968
|
// src/core/dmlValidationCandidates.ts
|
|
@@ -5883,6 +6042,8 @@ var SearchAbortedError = class extends Error {
|
|
|
5883
6042
|
this.name = "SearchAbortedError";
|
|
5884
6043
|
}
|
|
5885
6044
|
};
|
|
6045
|
+
var materializedMetaBySelectResult = /* @__PURE__ */ new WeakMap();
|
|
6046
|
+
var materializedMetaByValidationResult = /* @__PURE__ */ new WeakMap();
|
|
5886
6047
|
async function execute(sql, client, options = {}) {
|
|
5887
6048
|
const startedAt = Date.now();
|
|
5888
6049
|
const stmt = parseSql(sql);
|
|
@@ -6028,16 +6189,25 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
|
6028
6189
|
}
|
|
6029
6190
|
}
|
|
6030
6191
|
var TEMP_TABLE_MAX_ROWS = 1e4;
|
|
6031
|
-
function appendValidationErrors(tempTables, name, columns, rows, maxRows) {
|
|
6192
|
+
function appendValidationErrors(tempTables, name, columns, rows, maxRows, columnMeta) {
|
|
6032
6193
|
const current = tempTables.get(name);
|
|
6033
|
-
if (current && (current.columns.length !== columns.length || current.columns.some((c, i) => c !== columns[i]))) {
|
|
6194
|
+
if (current && (current.columns.length !== columns.length || current.columns.some((c, i) => c !== columns[i]) || !materializedColumnMetaEqual(current.columnMeta, columnMeta))) {
|
|
6034
6195
|
throw new Error(`ArgumentError: validation error table ${name} has a different schema.`);
|
|
6035
6196
|
}
|
|
6036
6197
|
const existingRows = current?.rows ?? [];
|
|
6037
6198
|
if (existingRows.length + rows.length > maxRows) {
|
|
6038
6199
|
throw new Error(`ArgumentError: temp table ${name} exceeds max rows (${maxRows}).`);
|
|
6039
6200
|
}
|
|
6040
|
-
tempTables.set(name, { columns: [...columns], rows: [...existingRows, ...rows] });
|
|
6201
|
+
tempTables.set(name, { columns: [...columns], rows: [...existingRows, ...rows], columnMeta });
|
|
6202
|
+
}
|
|
6203
|
+
function materializedColumnMetaEqual(left, right) {
|
|
6204
|
+
if (left === right) return true;
|
|
6205
|
+
if (!left || !right || left.size !== right.size) return false;
|
|
6206
|
+
for (const [column, meta] of left) {
|
|
6207
|
+
const candidate = right.get(column);
|
|
6208
|
+
if (!candidate || candidate.sortKind !== meta.sortKind || candidate.fieldType !== meta.fieldType) return false;
|
|
6209
|
+
}
|
|
6210
|
+
return true;
|
|
6041
6211
|
}
|
|
6042
6212
|
var BatchTimeoutError = class extends Error {
|
|
6043
6213
|
constructor() {
|
|
@@ -6199,7 +6369,8 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
6199
6369
|
resolvedStmt.validationErrorTable,
|
|
6200
6370
|
result.columns,
|
|
6201
6371
|
result.errors,
|
|
6202
|
-
options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS
|
|
6372
|
+
options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS,
|
|
6373
|
+
materializedMetaByValidationResult.get(result) ?? /* @__PURE__ */ new Map()
|
|
6203
6374
|
);
|
|
6204
6375
|
}
|
|
6205
6376
|
return { result };
|
|
@@ -6223,7 +6394,11 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
6223
6394
|
onLimitReached: "error"
|
|
6224
6395
|
};
|
|
6225
6396
|
const result = await runSelectLike(resolvedStmt.query, client, materializeOptions, cacheContext, tempTables);
|
|
6226
|
-
tempTables.set(resolvedStmt.name, {
|
|
6397
|
+
tempTables.set(resolvedStmt.name, {
|
|
6398
|
+
rows: result.rows,
|
|
6399
|
+
columns: result.columns,
|
|
6400
|
+
columnMeta: materializedMetaBySelectResult.get(result)
|
|
6401
|
+
});
|
|
6227
6402
|
return { tempTable: resolvedStmt.name, rowCount: result.rows.length };
|
|
6228
6403
|
}
|
|
6229
6404
|
if (stmt.type === "DROP_TEMP_TABLE") {
|
|
@@ -6259,9 +6434,9 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
6259
6434
|
}
|
|
6260
6435
|
async function runSelectLike(query, client, options, cacheContext, tempTables) {
|
|
6261
6436
|
if (query.type === "WITH") {
|
|
6262
|
-
return executeWith(query, client, options, cacheContext, tempTables);
|
|
6437
|
+
return executeWith(query, client, options, cacheContext, tempTables, true);
|
|
6263
6438
|
}
|
|
6264
|
-
return executeQueryWithCte(query, client, options, tempTables, cacheContext);
|
|
6439
|
+
return executeQueryWithCte(query, client, options, tempTables, cacheContext, true);
|
|
6265
6440
|
}
|
|
6266
6441
|
async function runWithDeadline(work, remainingMs) {
|
|
6267
6442
|
if (remainingMs === null) return work;
|
|
@@ -6474,18 +6649,27 @@ function evalAssertArith(node) {
|
|
|
6474
6649
|
}
|
|
6475
6650
|
throw new Error(`ArgumentError: unsupported operand in ASSERT expression: ${node.type}`);
|
|
6476
6651
|
}
|
|
6477
|
-
async function executeSelect(stmt, client, options, cacheContext, cteCache) {
|
|
6652
|
+
async function executeSelect(stmt, client, options, cacheContext, cteCache, captureColumnMeta = false) {
|
|
6653
|
+
let result;
|
|
6478
6654
|
if (isNoFromSelect(stmt)) {
|
|
6479
|
-
|
|
6655
|
+
result = executeNoFromSelect(stmt);
|
|
6656
|
+
if (captureColumnMeta) {
|
|
6657
|
+
materializedMetaBySelectResult.set(result, await inferSelectColumnMeta(stmt, result.columns, client, cacheContext, cteCache));
|
|
6658
|
+
}
|
|
6659
|
+
return result;
|
|
6480
6660
|
}
|
|
6481
6661
|
await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache);
|
|
6482
6662
|
const mode = resolveSelectMode(stmt);
|
|
6483
6663
|
await validateSelectFieldCodes(stmt, mode, client, cacheContext);
|
|
6484
6664
|
if (mode === "SIMPLE") {
|
|
6485
|
-
|
|
6665
|
+
result = await executeSimpleSelect(stmt, client, options, cacheContext);
|
|
6486
6666
|
} else {
|
|
6487
|
-
|
|
6667
|
+
result = await executeFullScanSelect(stmt, client, options, cacheContext, cteCache);
|
|
6668
|
+
}
|
|
6669
|
+
if (captureColumnMeta) {
|
|
6670
|
+
materializedMetaBySelectResult.set(result, await inferSelectColumnMeta(stmt, result.columns, client, cacheContext, cteCache));
|
|
6488
6671
|
}
|
|
6672
|
+
return result;
|
|
6489
6673
|
}
|
|
6490
6674
|
function isNoFromSelect(stmt) {
|
|
6491
6675
|
return stmt.from.appId === 0 && stmt.from.cteName === NO_FROM_CTE_NAME;
|
|
@@ -6521,6 +6705,11 @@ function validateNoFromColumns(stmt) {
|
|
|
6521
6705
|
throw new Error("ArgumentError: field reference is not allowed without FROM.");
|
|
6522
6706
|
}
|
|
6523
6707
|
break;
|
|
6708
|
+
case "WINDOW_COL":
|
|
6709
|
+
if (col.partitionBy.length > 0 || col.orderBy.length > 0) {
|
|
6710
|
+
throw new Error("ArgumentError: field reference is not allowed without FROM.");
|
|
6711
|
+
}
|
|
6712
|
+
break;
|
|
6524
6713
|
default:
|
|
6525
6714
|
throw new Error(`ArgumentError: ${col.type} is not supported without FROM.`);
|
|
6526
6715
|
}
|
|
@@ -6531,7 +6720,8 @@ function executeNoFromSelect(stmt) {
|
|
|
6531
6720
|
throw new Error("ArgumentError: JOIN/WHERE/GROUP BY/HAVING/ORDER BY/DISTINCT are not supported without FROM.");
|
|
6532
6721
|
}
|
|
6533
6722
|
validateNoFromColumns(stmt);
|
|
6534
|
-
const
|
|
6723
|
+
const windowed = applyWindow([{}], stmt.columns);
|
|
6724
|
+
const { rows: projected, columns } = project(windowed, stmt.columns);
|
|
6535
6725
|
const rows = applyLimit(projected, stmt.limit, stmt.offset);
|
|
6536
6726
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [] };
|
|
6537
6727
|
}
|
|
@@ -6809,7 +6999,7 @@ function aggregateSortKind(info) {
|
|
|
6809
6999
|
if (info.fieldType === "NUMBER" || info.fieldType === "RECORD_NUMBER") return "number";
|
|
6810
7000
|
return AGGREGATE_STRING_FIELD_TYPES.has(info.fieldType) ? "string" : void 0;
|
|
6811
7001
|
}
|
|
6812
|
-
async function loadAggregateSortKindResolver(stmt, client, cacheContext) {
|
|
7002
|
+
async function loadAggregateSortKindResolver(stmt, client, cacheContext, materializedTables) {
|
|
6813
7003
|
const refs = collectSelectAggregateSortRefs(stmt.columns);
|
|
6814
7004
|
if (refs.length === 0) return void 0;
|
|
6815
7005
|
const appIds = /* @__PURE__ */ new Set();
|
|
@@ -6825,11 +7015,9 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext) {
|
|
|
6825
7015
|
} else if (stmt.joins.length === 0) {
|
|
6826
7016
|
if (stmt.from.cteName === null) appIds.add(stmt.from.appId);
|
|
6827
7017
|
} else {
|
|
6828
|
-
if ([stmt.from, ...stmt.joins.map((join2) => join2.table)].some((table) => table.cteName !== null)) continue;
|
|
6829
7018
|
for (const table of physicalTables) appIds.add(table.appId);
|
|
6830
7019
|
}
|
|
6831
7020
|
}
|
|
6832
|
-
if (appIds.size === 0) return void 0;
|
|
6833
7021
|
const fieldInfosByApp = new Map(
|
|
6834
7022
|
await Promise.all([...appIds].map(async (appId) => {
|
|
6835
7023
|
const infos = await getFieldsCached(appId, client, cacheContext);
|
|
@@ -6844,17 +7032,28 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext) {
|
|
|
6844
7032
|
info = fieldInfosByApp.get(stmt.from.appId)?.get(ref.field);
|
|
6845
7033
|
} else {
|
|
6846
7034
|
const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
|
|
6847
|
-
if (!table
|
|
7035
|
+
if (!table) return void 0;
|
|
7036
|
+
if (table.cteName !== null) {
|
|
7037
|
+
return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field)?.sortKind;
|
|
7038
|
+
}
|
|
6848
7039
|
info = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
6849
7040
|
}
|
|
6850
7041
|
} else if (stmt.joins.length === 0) {
|
|
6851
|
-
if (stmt.from.cteName !== null)
|
|
7042
|
+
if (stmt.from.cteName !== null) {
|
|
7043
|
+
return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field)?.sortKind;
|
|
7044
|
+
}
|
|
6852
7045
|
info = fieldInfosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
|
|
6853
7046
|
} else {
|
|
6854
|
-
|
|
6855
|
-
|
|
7047
|
+
const matches = tables.flatMap((table) => {
|
|
7048
|
+
if (table.cteName !== null) {
|
|
7049
|
+
const materialized = materializedTables?.get(table.cteName);
|
|
7050
|
+
return materialized?.columns.includes(ref.field) ? [materialized.columnMeta?.get(ref.field)?.sortKind] : [];
|
|
7051
|
+
}
|
|
7052
|
+
const candidate = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
7053
|
+
return candidate ? [aggregateSortKind(candidate)] : [];
|
|
7054
|
+
});
|
|
6856
7055
|
if (matches.length !== 1) return void 0;
|
|
6857
|
-
|
|
7056
|
+
return matches[0];
|
|
6858
7057
|
}
|
|
6859
7058
|
return info ? aggregateSortKind(info) : void 0;
|
|
6860
7059
|
};
|
|
@@ -6863,6 +7062,108 @@ function fieldCodeForTypeLookup(table, field) {
|
|
|
6863
7062
|
if (table.subtableCode && field.startsWith("_p.")) return field.slice(3);
|
|
6864
7063
|
return field;
|
|
6865
7064
|
}
|
|
7065
|
+
function materializedMetaFromFieldInfo(info) {
|
|
7066
|
+
return { sortKind: aggregateSortKind(info), fieldType: info.fieldType };
|
|
7067
|
+
}
|
|
7068
|
+
function selectNeedsSourceColumnMeta(stmt) {
|
|
7069
|
+
return stmt.columns.some(
|
|
7070
|
+
(column) => column.type === "FIELD" || column.type === "WILDCARD" || column.type === "PARENT_WILDCARD" || column.type === "AGGREGATE" && (column.func === "MIN" || column.func === "MAX") && column.arg.type === "FIELD_REF"
|
|
7071
|
+
);
|
|
7072
|
+
}
|
|
7073
|
+
async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext, materializedTables) {
|
|
7074
|
+
const physicalInfos = /* @__PURE__ */ new Map();
|
|
7075
|
+
if (selectNeedsSourceColumnMeta(stmt)) {
|
|
7076
|
+
await Promise.all(physicalSelectTables(stmt).map(async (table) => {
|
|
7077
|
+
if (physicalInfos.has(table.appId)) return;
|
|
7078
|
+
const infos = await getFieldsCached(table.appId, client, cacheContext);
|
|
7079
|
+
physicalInfos.set(table.appId, new Map(infos.map((info) => [info.code, info])));
|
|
7080
|
+
}));
|
|
7081
|
+
}
|
|
7082
|
+
const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
7083
|
+
const resolveField2 = (ref) => {
|
|
7084
|
+
if (ref.tableAlias !== null) {
|
|
7085
|
+
if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
|
|
7086
|
+
const info2 = physicalInfos.get(stmt.from.appId)?.get(ref.field);
|
|
7087
|
+
return info2 ? materializedMetaFromFieldInfo(info2) : void 0;
|
|
7088
|
+
}
|
|
7089
|
+
const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
|
|
7090
|
+
if (!table) return void 0;
|
|
7091
|
+
if (table.cteName !== null) return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field);
|
|
7092
|
+
const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
7093
|
+
return info ? materializedMetaFromFieldInfo(info) : void 0;
|
|
7094
|
+
}
|
|
7095
|
+
if (stmt.joins.length === 0) {
|
|
7096
|
+
if (stmt.from.cteName !== null) return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field);
|
|
7097
|
+
const info = physicalInfos.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
|
|
7098
|
+
return info ? materializedMetaFromFieldInfo(info) : void 0;
|
|
7099
|
+
}
|
|
7100
|
+
const matches = tables.flatMap((table) => {
|
|
7101
|
+
if (table.cteName !== null) {
|
|
7102
|
+
const materialized = materializedTables?.get(table.cteName);
|
|
7103
|
+
if (!materialized?.columns.includes(ref.field)) return [];
|
|
7104
|
+
return [materialized.columnMeta?.get(ref.field)];
|
|
7105
|
+
}
|
|
7106
|
+
const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
|
|
7107
|
+
return info ? [materializedMetaFromFieldInfo(info)] : [];
|
|
7108
|
+
});
|
|
7109
|
+
return matches.length === 1 ? matches[0] : void 0;
|
|
7110
|
+
};
|
|
7111
|
+
const inferred = /* @__PURE__ */ new Map();
|
|
7112
|
+
const hasWildcard = stmt.columns.some((column) => column.type === "WILDCARD" || column.type === "PARENT_WILDCARD");
|
|
7113
|
+
if (stmt.columns.length === 1 && (stmt.columns[0].type === "WILDCARD" || stmt.columns[0].type === "PARENT_WILDCARD")) {
|
|
7114
|
+
for (const output of outputColumns) {
|
|
7115
|
+
const meta = resolveField2(aggregateFieldRef(output));
|
|
7116
|
+
if (meta) inferred.set(output, meta);
|
|
7117
|
+
}
|
|
7118
|
+
return inferred;
|
|
7119
|
+
}
|
|
7120
|
+
if (hasWildcard) {
|
|
7121
|
+
for (const output of outputColumns) {
|
|
7122
|
+
const meta = resolveField2(aggregateFieldRef(output));
|
|
7123
|
+
if (meta) inferred.set(output, meta);
|
|
7124
|
+
}
|
|
7125
|
+
}
|
|
7126
|
+
const explicitColumns = stmt.columns.filter(
|
|
7127
|
+
(column) => column.type !== "WILDCARD" && column.type !== "PARENT_WILDCARD"
|
|
7128
|
+
);
|
|
7129
|
+
explicitColumns.forEach((column, index) => {
|
|
7130
|
+
const output = hasWildcard ? "alias" in column && column.alias ? column.alias : void 0 : outputColumns[index];
|
|
7131
|
+
if (!output) return;
|
|
7132
|
+
let meta;
|
|
7133
|
+
if (column.type === "FIELD") {
|
|
7134
|
+
meta = resolveField2(aggregateFieldRef(column.field));
|
|
7135
|
+
} else if (column.type === "AGGREGATE") {
|
|
7136
|
+
if (column.func === "GROUP_CONCAT") {
|
|
7137
|
+
meta = { sortKind: "string" };
|
|
7138
|
+
} else if (column.func === "COUNT" || column.func === "SUM" || column.func === "AVG") {
|
|
7139
|
+
meta = { sortKind: "number" };
|
|
7140
|
+
} else if ((column.func === "MIN" || column.func === "MAX") && column.arg.type === "FIELD_REF") {
|
|
7141
|
+
const source = resolveField2(aggregateFieldRef(column.arg.field));
|
|
7142
|
+
if (source?.sortKind) meta = { sortKind: source.sortKind };
|
|
7143
|
+
}
|
|
7144
|
+
} else if (column.type === "ARITH_AGG_COL" || column.type === "ARITH_COL") {
|
|
7145
|
+
meta = { sortKind: "number" };
|
|
7146
|
+
} else if (column.type === "LITERAL_COL") {
|
|
7147
|
+
meta = { sortKind: "string" };
|
|
7148
|
+
} else if (column.type === "WINDOW_COL") {
|
|
7149
|
+
meta = { sortKind: "number" };
|
|
7150
|
+
}
|
|
7151
|
+
if (meta) inferred.set(output, meta);
|
|
7152
|
+
});
|
|
7153
|
+
return inferred;
|
|
7154
|
+
}
|
|
7155
|
+
function mergeUnionColumnMeta(left, right) {
|
|
7156
|
+
const leftMeta = materializedMetaBySelectResult.get(left);
|
|
7157
|
+
const rightMeta = materializedMetaBySelectResult.get(right);
|
|
7158
|
+
const merged = /* @__PURE__ */ new Map();
|
|
7159
|
+
left.columns.forEach((column, index) => {
|
|
7160
|
+
const a = leftMeta?.get(column);
|
|
7161
|
+
const rightColumn = right.columns[index];
|
|
7162
|
+
const b = rightColumn === void 0 ? void 0 : rightMeta?.get(rightColumn);
|
|
7163
|
+
if (a && b && a.sortKind === b.sortKind && a.fieldType === b.fieldType) merged.set(column, a);
|
|
7164
|
+
});
|
|
7165
|
+
return merged;
|
|
7166
|
+
}
|
|
6866
7167
|
function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
|
|
6867
7168
|
const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
6868
7169
|
const physicalTables = tables.filter((table) => table.cteName === null);
|
|
@@ -6907,7 +7208,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
6907
7208
|
const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
|
|
6908
7209
|
loadTypedPushdownMeta(stmt, client, cacheContext),
|
|
6909
7210
|
loadTypedInFieldTypes(stmt, client, cacheContext),
|
|
6910
|
-
loadAggregateSortKindResolver(stmt, client, cacheContext)
|
|
7211
|
+
loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
|
|
6911
7212
|
]);
|
|
6912
7213
|
const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
|
|
6913
7214
|
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
@@ -7027,9 +7328,9 @@ function deduplicateRows(rows, columns) {
|
|
|
7027
7328
|
return true;
|
|
7028
7329
|
});
|
|
7029
7330
|
}
|
|
7030
|
-
async function executeWith(stmt, client, options, cacheContext, seed) {
|
|
7331
|
+
async function executeWith(stmt, client, options, cacheContext, seed, captureColumnMeta = false) {
|
|
7031
7332
|
if ((seed == null || seed.size === 0) && canInlineSingleCte(stmt)) {
|
|
7032
|
-
return executeSelect(buildInlinedQuery(stmt), client, options, cacheContext);
|
|
7333
|
+
return executeSelect(buildInlinedQuery(stmt), client, options, cacheContext, void 0, captureColumnMeta);
|
|
7033
7334
|
}
|
|
7034
7335
|
const cteCache = new Map(seed ?? []);
|
|
7035
7336
|
for (const cte of stmt.ctes) {
|
|
@@ -7039,17 +7340,21 @@ async function executeWith(stmt, client, options, cacheContext, seed) {
|
|
|
7039
7340
|
} else if (cte.query.type === "DESCRIBE") {
|
|
7040
7341
|
result = await executeDescribe(cte.query, client, cacheContext);
|
|
7041
7342
|
} else {
|
|
7042
|
-
result = await executeQueryWithCte(cte.query, client, options, cteCache, cacheContext);
|
|
7343
|
+
result = await executeQueryWithCte(cte.query, client, options, cteCache, cacheContext, true);
|
|
7043
7344
|
}
|
|
7044
|
-
cteCache.set(cte.name, {
|
|
7345
|
+
cteCache.set(cte.name, {
|
|
7346
|
+
rows: result.rows,
|
|
7347
|
+
columns: result.columns,
|
|
7348
|
+
columnMeta: materializedMetaBySelectResult.get(result)
|
|
7349
|
+
});
|
|
7045
7350
|
}
|
|
7046
|
-
return executeQueryWithCte(stmt.query, client, options, cteCache, cacheContext);
|
|
7351
|
+
return executeQueryWithCte(stmt.query, client, options, cteCache, cacheContext, captureColumnMeta);
|
|
7047
7352
|
}
|
|
7048
|
-
async function executeQueryWithCte(query, client, options, cteCache, cacheContext) {
|
|
7353
|
+
async function executeQueryWithCte(query, client, options, cteCache, cacheContext, captureColumnMeta = false) {
|
|
7049
7354
|
if (query.type === "UNION") {
|
|
7050
7355
|
const [leftResult, rightResult] = await Promise.all([
|
|
7051
|
-
executeQueryWithCte(query.left, client, options, cteCache, cacheContext),
|
|
7052
|
-
executeQueryWithCte(query.right, client, options, cteCache, cacheContext)
|
|
7356
|
+
executeQueryWithCte(query.left, client, options, cteCache, cacheContext, captureColumnMeta),
|
|
7357
|
+
executeQueryWithCte(query.right, client, options, cteCache, cacheContext, captureColumnMeta)
|
|
7053
7358
|
]);
|
|
7054
7359
|
const leftCols = leftResult.columns;
|
|
7055
7360
|
const rightCols = rightResult.columns;
|
|
@@ -7062,13 +7367,21 @@ async function executeQueryWithCte(query, client, options, cteCache, cacheContex
|
|
|
7062
7367
|
});
|
|
7063
7368
|
const combined = [...leftResult.rows, ...remapped];
|
|
7064
7369
|
const rows = query.all ? combined : deduplicateRows(combined, leftCols);
|
|
7065
|
-
|
|
7370
|
+
const result2 = { type: "SELECT", rows, columns: leftCols, rowCount: rows.length };
|
|
7371
|
+
if (captureColumnMeta) {
|
|
7372
|
+
materializedMetaBySelectResult.set(result2, mergeUnionColumnMeta(leftResult, rightResult));
|
|
7373
|
+
}
|
|
7374
|
+
return result2;
|
|
7066
7375
|
}
|
|
7067
7376
|
const hasCteRef = query.from.cteName != null && query.from.cteName !== NO_FROM_CTE_NAME || query.joins.some((j) => j.table.cteName != null);
|
|
7068
7377
|
if (!hasCteRef) {
|
|
7069
|
-
return executeSelect(query, client, options, cacheContext, cteCache);
|
|
7378
|
+
return executeSelect(query, client, options, cacheContext, cteCache, captureColumnMeta);
|
|
7070
7379
|
}
|
|
7071
|
-
|
|
7380
|
+
const result = await executeFullScanWithCte(query, client, options, cteCache, cacheContext);
|
|
7381
|
+
if (captureColumnMeta) {
|
|
7382
|
+
materializedMetaBySelectResult.set(result, await inferSelectColumnMeta(query, result.columns, client, cacheContext, cteCache));
|
|
7383
|
+
}
|
|
7384
|
+
return result;
|
|
7072
7385
|
}
|
|
7073
7386
|
async function executeFullScanWithCte(stmt, client, options, cteCache, cacheContext) {
|
|
7074
7387
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
@@ -7082,7 +7395,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
7082
7395
|
const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
|
|
7083
7396
|
loadTypedPushdownMeta(stmt, client, cacheContext),
|
|
7084
7397
|
loadTypedInFieldTypes(stmt, client, cacheContext),
|
|
7085
|
-
loadAggregateSortKindResolver(stmt, client, cacheContext)
|
|
7398
|
+
loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
|
|
7086
7399
|
]);
|
|
7087
7400
|
const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
|
|
7088
7401
|
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
@@ -7419,7 +7732,10 @@ async function getSortKindMapByApp(appId, client, cacheContext) {
|
|
|
7419
7732
|
return map;
|
|
7420
7733
|
}
|
|
7421
7734
|
async function buildOrderByMetaForSelect(stmt, client, cacheContext) {
|
|
7422
|
-
|
|
7735
|
+
const hasWindowOrderBy = stmt.columns.some(
|
|
7736
|
+
(column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
|
|
7737
|
+
);
|
|
7738
|
+
if (stmt.orderBy.length === 0 && !hasWindowOrderBy) {
|
|
7423
7739
|
return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map() };
|
|
7424
7740
|
}
|
|
7425
7741
|
const [optionOrders, sortKinds] = await Promise.all([
|
|
@@ -7560,7 +7876,23 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
|
|
|
7560
7876
|
errors,
|
|
7561
7877
|
...stmt.validationErrorTable ? { errTable: stmt.validationErrorTable } : stmt.onErrorSkip && stmt.errorTable ? { errTable: stmt.errorTable } : {}
|
|
7562
7878
|
};
|
|
7563
|
-
|
|
7879
|
+
const columnMeta = /* @__PURE__ */ new Map();
|
|
7880
|
+
for (const column of payloadFields) {
|
|
7881
|
+
if (column === "$id") {
|
|
7882
|
+
columnMeta.set(column, { sortKind: "number", fieldType: "RECORD_NUMBER" });
|
|
7883
|
+
continue;
|
|
7884
|
+
}
|
|
7885
|
+
const info = infoByCode.get(column);
|
|
7886
|
+
if (info) columnMeta.set(column, materializedMetaFromFieldInfo(info));
|
|
7887
|
+
}
|
|
7888
|
+
columnMeta.set("$err_statement", { sortKind: "number" });
|
|
7889
|
+
columnMeta.set("$err_operation", { sortKind: "string" });
|
|
7890
|
+
columnMeta.set("$err_row", { sortKind: "number" });
|
|
7891
|
+
columnMeta.set("$err_field", { sortKind: "string" });
|
|
7892
|
+
columnMeta.set("$err_code", { sortKind: "string" });
|
|
7893
|
+
columnMeta.set("$err_message", { sortKind: "string" });
|
|
7894
|
+
materializedMetaByValidationResult.set(result, columnMeta);
|
|
7895
|
+
return { result, candidates, invalidRowNumbers, columnMeta };
|
|
7564
7896
|
}
|
|
7565
7897
|
async function executeOnErrorSkip(stmt, client, options, cacheContext, tempTables, statementNumber) {
|
|
7566
7898
|
const prepared = await prepareDmlValidation(
|
|
@@ -7578,7 +7910,8 @@ async function executeOnErrorSkip(stmt, client, options, cacheContext, tempTable
|
|
|
7578
7910
|
errTable,
|
|
7579
7911
|
prepared.result.columns,
|
|
7580
7912
|
prepared.result.errors,
|
|
7581
|
-
options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS
|
|
7913
|
+
options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS,
|
|
7914
|
+
prepared.columnMeta
|
|
7582
7915
|
);
|
|
7583
7916
|
const rejectLimit = stmt.rejectLimit ?? null;
|
|
7584
7917
|
if (rejectLimit !== null && prepared.result.invalidRows > rejectLimit) {
|
|
@@ -8864,6 +9197,8 @@ function collectFullScanReasons(stmt) {
|
|
|
8864
9197
|
r.push("DISTINCT \u3042\u308A");
|
|
8865
9198
|
if (stmt.columns.some((c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL"))
|
|
8866
9199
|
r.push("\u96C6\u8A08\u95A2\u6570\uFF08COUNT / SUM \u7B49\uFF09\u3042\u308A");
|
|
9200
|
+
if (stmt.columns.some((c) => c.type === "WINDOW_COL"))
|
|
9201
|
+
r.push("\u30A6\u30A3\u30F3\u30C9\u30A6\u95A2\u6570\u3042\u308A");
|
|
8867
9202
|
if (stmt.columns.some((c) => c.type === "SCALAR_SUBQUERY_COL"))
|
|
8868
9203
|
r.push("SELECT \u5217\u306B\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA");
|
|
8869
9204
|
if (whereRequiresJsEval(stmt.where))
|