@rex0220/kintone-sql-tools 2.15.0 → 2.17.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 +258 -15
- package/dist-mcp/ksql-mcp.js +259 -16
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -88,6 +88,9 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
88
88
|
["MAX", "MAX" /* MAX */],
|
|
89
89
|
["MIN", "MIN" /* MIN */],
|
|
90
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 */],
|
|
91
94
|
["ASSERT", "ASSERT" /* ASSERT */],
|
|
92
95
|
["AND", "AND" /* AND */],
|
|
93
96
|
["OR", "OR" /* OR */],
|
|
@@ -120,6 +123,11 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
120
123
|
["COALESCE", "COALESCE" /* COALESCE */],
|
|
121
124
|
["NULLIF", "NULLIF" /* NULLIF */],
|
|
122
125
|
["ISNULL", "ISNULL" /* ISNULL */],
|
|
126
|
+
["INSTR", "INSTR" /* INSTR */],
|
|
127
|
+
["GREATEST", "GREATEST" /* GREATEST */],
|
|
128
|
+
["LEAST", "LEAST" /* LEAST */],
|
|
129
|
+
["LPAD", "LPAD" /* LPAD */],
|
|
130
|
+
["RPAD", "RPAD" /* RPAD */],
|
|
123
131
|
["CAST", "CAST" /* CAST */],
|
|
124
132
|
["CONVERT", "CONVERT" /* CONVERT */],
|
|
125
133
|
["FORMAT", "FORMAT" /* FORMAT */],
|
|
@@ -127,6 +135,8 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
127
135
|
["FLOOR", "FLOOR" /* FLOOR */],
|
|
128
136
|
["CEIL", "CEIL" /* CEIL */],
|
|
129
137
|
["CEILING", "CEILING" /* CEILING */],
|
|
138
|
+
["TRUNCATE", "TRUNCATE" /* TRUNCATE */],
|
|
139
|
+
["TRUNC", "TRUNC" /* TRUNC */],
|
|
130
140
|
["ABS", "ABS" /* ABS */],
|
|
131
141
|
["MOD", "MOD" /* MOD */],
|
|
132
142
|
["POWER", "POWER" /* POWER */],
|
|
@@ -138,6 +148,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
138
148
|
["DATE_FORMAT", "DATE_FORMAT" /* DATE_FORMAT */],
|
|
139
149
|
["DATEDIFF", "DATEDIFF" /* DATEDIFF */],
|
|
140
150
|
["DATE_ADD", "DATE_ADD" /* DATE_ADD */],
|
|
151
|
+
["LAST_DAY", "LAST_DAY" /* LAST_DAY */],
|
|
141
152
|
["IF", "IF" /* IF */]
|
|
142
153
|
]);
|
|
143
154
|
|
|
@@ -464,6 +475,9 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
|
|
|
464
475
|
"AVG" /* AVG */,
|
|
465
476
|
"MAX" /* MAX */,
|
|
466
477
|
"MIN" /* MIN */,
|
|
478
|
+
"ROW_NUMBER" /* ROW_NUMBER */,
|
|
479
|
+
"RANK" /* RANK */,
|
|
480
|
+
"DENSE_RANK" /* DENSE_RANK */,
|
|
467
481
|
"TODAY" /* TODAY */,
|
|
468
482
|
"NOW" /* NOW */,
|
|
469
483
|
"LOGINUSER" /* LOGINUSER */,
|
|
@@ -480,6 +494,13 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
|
|
|
480
494
|
"COALESCE" /* COALESCE */,
|
|
481
495
|
"NULLIF" /* NULLIF */,
|
|
482
496
|
"ISNULL" /* ISNULL */,
|
|
497
|
+
"LEFT" /* LEFT */,
|
|
498
|
+
"RIGHT" /* RIGHT */,
|
|
499
|
+
"INSTR" /* INSTR */,
|
|
500
|
+
"GREATEST" /* GREATEST */,
|
|
501
|
+
"LEAST" /* LEAST */,
|
|
502
|
+
"LPAD" /* LPAD */,
|
|
503
|
+
"RPAD" /* RPAD */,
|
|
483
504
|
"CAST" /* CAST */,
|
|
484
505
|
"CONVERT" /* CONVERT */,
|
|
485
506
|
"FORMAT" /* FORMAT */,
|
|
@@ -487,6 +508,8 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
|
|
|
487
508
|
"FLOOR" /* FLOOR */,
|
|
488
509
|
"CEIL" /* CEIL */,
|
|
489
510
|
"CEILING" /* CEILING */,
|
|
511
|
+
"TRUNCATE" /* TRUNCATE */,
|
|
512
|
+
"TRUNC" /* TRUNC */,
|
|
490
513
|
"ABS" /* ABS */,
|
|
491
514
|
"MOD" /* MOD */,
|
|
492
515
|
"POWER" /* POWER */,
|
|
@@ -498,6 +521,7 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
|
|
|
498
521
|
"DATE_FORMAT" /* DATE_FORMAT */,
|
|
499
522
|
"DATEDIFF" /* DATEDIFF */,
|
|
500
523
|
"DATE_ADD" /* DATE_ADD */,
|
|
524
|
+
"LAST_DAY" /* LAST_DAY */,
|
|
501
525
|
"IF" /* IF */
|
|
502
526
|
]);
|
|
503
527
|
function needsSpaceBetween(prev, cur) {
|
|
@@ -982,6 +1006,11 @@ var Parser = class {
|
|
|
982
1006
|
const orderBy = this.consume("ORDER" /* ORDER */) ? (this.expect("BY" /* BY */), this.parseOrderBy()) : [];
|
|
983
1007
|
const limit = this.consume("LIMIT" /* LIMIT */) ? this.parseUnsignedInt() : null;
|
|
984
1008
|
const offset = this.consume("OFFSET" /* OFFSET */) ? this.parseUnsignedInt() : null;
|
|
1009
|
+
const hasWindow = columns.some((column) => column.type === "WINDOW_COL");
|
|
1010
|
+
const hasAggregate = columns.some((column) => this.selectColumnHasAggregate(column));
|
|
1011
|
+
if (hasWindow && (groupBy.length > 0 || hasAggregate)) {
|
|
1012
|
+
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());
|
|
1013
|
+
}
|
|
985
1014
|
return {
|
|
986
1015
|
type: "SELECT",
|
|
987
1016
|
distinct,
|
|
@@ -1046,6 +1075,10 @@ var Parser = class {
|
|
|
1046
1075
|
if (this.consume("*" /* STAR */)) {
|
|
1047
1076
|
return { type: "WILDCARD" };
|
|
1048
1077
|
}
|
|
1078
|
+
const windowFunc = this.tryWindowFunc();
|
|
1079
|
+
if (windowFunc !== null) {
|
|
1080
|
+
return this.parseWindowColumn(windowFunc);
|
|
1081
|
+
}
|
|
1049
1082
|
if (this.peek().kind === "CASE" /* CASE */) {
|
|
1050
1083
|
const expr = this.parseCaseWhenExpr();
|
|
1051
1084
|
const alias2 = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
|
|
@@ -1117,6 +1150,56 @@ var Parser = class {
|
|
|
1117
1150
|
const alias = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
|
|
1118
1151
|
return { type: "FIELD", field, alias };
|
|
1119
1152
|
}
|
|
1153
|
+
tryWindowFunc() {
|
|
1154
|
+
switch (this.peek().kind) {
|
|
1155
|
+
case "ROW_NUMBER" /* ROW_NUMBER */:
|
|
1156
|
+
return "ROW_NUMBER";
|
|
1157
|
+
case "RANK" /* RANK */:
|
|
1158
|
+
return "RANK";
|
|
1159
|
+
case "DENSE_RANK" /* DENSE_RANK */:
|
|
1160
|
+
return "DENSE_RANK";
|
|
1161
|
+
default:
|
|
1162
|
+
return null;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
parseWindowColumn(func) {
|
|
1166
|
+
this.advance();
|
|
1167
|
+
this.expect("(" /* LPAREN */);
|
|
1168
|
+
if (this.peek().kind !== ")" /* RPAREN */) {
|
|
1169
|
+
throw new ParseError(`${func} \u306F\u5F15\u6570\u3092\u53D7\u3051\u4ED8\u3051\u307E\u305B\u3093`, this.peek());
|
|
1170
|
+
}
|
|
1171
|
+
this.expect(")" /* RPAREN */);
|
|
1172
|
+
this.expectSoftKeyword("OVER", `${func} \u306B\u306F OVER (...) \u304C\u5FC5\u8981\u3067\u3059`);
|
|
1173
|
+
this.expect("(" /* LPAREN */);
|
|
1174
|
+
const partitionBy = [];
|
|
1175
|
+
if (this.isSoftKeyword("PARTITION")) {
|
|
1176
|
+
this.advance();
|
|
1177
|
+
this.expect("BY" /* BY */, "PARTITION \u306E\u5F8C\u306B\u306F BY \u304C\u5FC5\u8981\u3067\u3059");
|
|
1178
|
+
do {
|
|
1179
|
+
const ref = this.parseQualifiedIdent();
|
|
1180
|
+
partitionBy.push({ type: "FIELD", tableAlias: ref.tableAlias, field: ref.field });
|
|
1181
|
+
} while (this.consume("," /* COMMA */));
|
|
1182
|
+
}
|
|
1183
|
+
const orderBy = this.consume("ORDER" /* ORDER */) ? (this.expect("BY" /* BY */), this.parseOrderBy()) : [];
|
|
1184
|
+
this.expect(")" /* RPAREN */);
|
|
1185
|
+
if (!this.consume("AS" /* AS */)) {
|
|
1186
|
+
throw new ParseError("\u30A6\u30A3\u30F3\u30C9\u30A6\u95A2\u6570\u306B\u306F AS alias \u304C\u5FC5\u8981\u3067\u3059", this.peek());
|
|
1187
|
+
}
|
|
1188
|
+
const alias = this.parseAliasName();
|
|
1189
|
+
return { type: "WINDOW_COL", func, partitionBy, orderBy, alias };
|
|
1190
|
+
}
|
|
1191
|
+
selectColumnHasAggregate(column) {
|
|
1192
|
+
if (column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL") return true;
|
|
1193
|
+
if (column.type !== "STRFUNC_COL") return false;
|
|
1194
|
+
return column.expr.args.some((arg) => this.stringFuncArgHasAggregate(arg));
|
|
1195
|
+
}
|
|
1196
|
+
stringFuncArgHasAggregate(arg) {
|
|
1197
|
+
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
1198
|
+
if (arg.type === "STRING_FUNC") {
|
|
1199
|
+
return arg.args.some((nested) => this.stringFuncArgHasAggregate(nested));
|
|
1200
|
+
}
|
|
1201
|
+
return false;
|
|
1202
|
+
}
|
|
1120
1203
|
isArithOp(kind) {
|
|
1121
1204
|
return kind === "+" /* PLUS */ || kind === "-" /* MINUS */ || kind === "*" /* STAR */ || kind === "/" /* SLASH */ || kind === "%" /* PERCENT */;
|
|
1122
1205
|
}
|
|
@@ -1311,6 +1394,10 @@ var Parser = class {
|
|
|
1311
1394
|
// arg: 文字列リテラル / 算術式(フィールド参照・数値含む)/ ネスト文字列関数
|
|
1312
1395
|
// ──────────────────────────────────────────────────
|
|
1313
1396
|
tryStringFuncName() {
|
|
1397
|
+
if (this.peekAt(1).kind === "(" /* LPAREN */) {
|
|
1398
|
+
if (this.peek().kind === "LEFT" /* LEFT */) return "LEFT";
|
|
1399
|
+
if (this.peek().kind === "RIGHT" /* RIGHT */) return "RIGHT";
|
|
1400
|
+
}
|
|
1314
1401
|
const map = {
|
|
1315
1402
|
["UPPER" /* UPPER */]: "UPPER",
|
|
1316
1403
|
["LOWER" /* LOWER */]: "LOWER",
|
|
@@ -1325,6 +1412,11 @@ var Parser = class {
|
|
|
1325
1412
|
["COALESCE" /* COALESCE */]: "COALESCE",
|
|
1326
1413
|
["NULLIF" /* NULLIF */]: "NULLIF",
|
|
1327
1414
|
["ISNULL" /* ISNULL */]: "ISNULL",
|
|
1415
|
+
["INSTR" /* INSTR */]: "INSTR",
|
|
1416
|
+
["GREATEST" /* GREATEST */]: "GREATEST",
|
|
1417
|
+
["LEAST" /* LEAST */]: "LEAST",
|
|
1418
|
+
["LPAD" /* LPAD */]: "LPAD",
|
|
1419
|
+
["RPAD" /* RPAD */]: "RPAD",
|
|
1328
1420
|
["CAST" /* CAST */]: "CAST",
|
|
1329
1421
|
["CONVERT" /* CONVERT */]: "CAST",
|
|
1330
1422
|
// CONVERT → CAST に正規化
|
|
@@ -1334,12 +1426,16 @@ var Parser = class {
|
|
|
1334
1426
|
["CEIL" /* CEIL */]: "CEIL",
|
|
1335
1427
|
["CEILING" /* CEILING */]: "CEIL",
|
|
1336
1428
|
// CEILING → CEIL に正規化
|
|
1429
|
+
["TRUNCATE" /* TRUNCATE */]: "TRUNCATE",
|
|
1430
|
+
["TRUNC" /* TRUNC */]: "TRUNCATE",
|
|
1431
|
+
// TRUNC → TRUNCATE に正規化
|
|
1337
1432
|
["YEAR" /* YEAR */]: "YEAR",
|
|
1338
1433
|
["MONTH" /* MONTH */]: "MONTH",
|
|
1339
1434
|
["DAY" /* DAY */]: "DAY",
|
|
1340
1435
|
["DATE_FORMAT" /* DATE_FORMAT */]: "DATE_FORMAT",
|
|
1341
1436
|
["DATEDIFF" /* DATEDIFF */]: "DATEDIFF",
|
|
1342
1437
|
["DATE_ADD" /* DATE_ADD */]: "DATE_ADD",
|
|
1438
|
+
["LAST_DAY" /* LAST_DAY */]: "LAST_DAY",
|
|
1343
1439
|
["ABS" /* ABS */]: "ABS",
|
|
1344
1440
|
["MOD" /* MOD */]: "MOD",
|
|
1345
1441
|
["POWER" /* POWER */]: "POWER",
|
|
@@ -2898,12 +2994,16 @@ var KintoneQueryError = class extends Error {
|
|
|
2898
2994
|
};
|
|
2899
2995
|
|
|
2900
2996
|
// src/converter/selectToKintone.ts
|
|
2997
|
+
function hasWindowColumns(columns) {
|
|
2998
|
+
return columns.some((column) => column.type === "WINDOW_COL");
|
|
2999
|
+
}
|
|
2901
3000
|
function resolveSelectMode(stmt) {
|
|
2902
3001
|
if (stmt.from.subtableCode) return "FULL_SCAN";
|
|
2903
3002
|
if (stmt.joins.some((j) => j.table.subtableCode)) return "FULL_SCAN";
|
|
2904
3003
|
if (stmt.joins.length > 0) return "FULL_SCAN";
|
|
2905
3004
|
if (stmt.groupBy.length > 0) return "FULL_SCAN";
|
|
2906
3005
|
if (stmt.distinct) return "FULL_SCAN";
|
|
3006
|
+
if (hasWindowColumns(stmt.columns)) return "FULL_SCAN";
|
|
2907
3007
|
if (stmt.columns.some(
|
|
2908
3008
|
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "SCALAR_SUBQUERY_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr(c.expr)
|
|
2909
3009
|
)) return "FULL_SCAN";
|
|
@@ -3275,16 +3375,16 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
3275
3375
|
}
|
|
3276
3376
|
walkStringFunc(k.expr, "groupBy");
|
|
3277
3377
|
};
|
|
3278
|
-
const walkOrderByKey = (k) => {
|
|
3378
|
+
const walkOrderByKey = (k, phase = "orderBy") => {
|
|
3279
3379
|
if (k.type === "FIELD_NAME") {
|
|
3280
|
-
addFieldName(k.name,
|
|
3380
|
+
addFieldName(k.name, phase);
|
|
3281
3381
|
return;
|
|
3282
3382
|
}
|
|
3283
3383
|
if (k.type === "ARITH_KEY") {
|
|
3284
|
-
walkArith(k.expr,
|
|
3384
|
+
walkArith(k.expr, phase);
|
|
3285
3385
|
return;
|
|
3286
3386
|
}
|
|
3287
|
-
walkStringFunc(k.expr,
|
|
3387
|
+
walkStringFunc(k.expr, phase);
|
|
3288
3388
|
};
|
|
3289
3389
|
for (const col of stmt.columns) {
|
|
3290
3390
|
switch (col.type) {
|
|
@@ -3316,6 +3416,10 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
3316
3416
|
break;
|
|
3317
3417
|
case "SCALAR_SUBQUERY_COL":
|
|
3318
3418
|
break;
|
|
3419
|
+
case "WINDOW_COL":
|
|
3420
|
+
for (const ref of col.partitionBy) addFieldRef(ref.field, ref.tableAlias, "select");
|
|
3421
|
+
for (const item of col.orderBy) walkOrderByKey(item.key, "select");
|
|
3422
|
+
break;
|
|
3319
3423
|
}
|
|
3320
3424
|
}
|
|
3321
3425
|
for (const join2 of stmt.joins) {
|
|
@@ -3362,6 +3466,10 @@ function collectSelectOutputNames(columns) {
|
|
|
3362
3466
|
}
|
|
3363
3467
|
if (col.type === "SCALAR_SUBQUERY_COL") {
|
|
3364
3468
|
names.add(col.alias ?? "(subquery)");
|
|
3469
|
+
continue;
|
|
3470
|
+
}
|
|
3471
|
+
if (col.type === "WINDOW_COL") {
|
|
3472
|
+
names.add(col.alias);
|
|
3365
3473
|
}
|
|
3366
3474
|
}
|
|
3367
3475
|
return names;
|
|
@@ -4066,6 +4174,25 @@ function compareScalarValues(op, leftStr, rightStr) {
|
|
|
4066
4174
|
return numeric ? leftNum <= rightNum : leftStr <= rightStr;
|
|
4067
4175
|
}
|
|
4068
4176
|
}
|
|
4177
|
+
function selectScalarExtreme(values, extreme) {
|
|
4178
|
+
if (extreme === "least" && values.includes("")) return "";
|
|
4179
|
+
const candidates = extreme === "greatest" ? values.filter((value) => value !== "") : [...values];
|
|
4180
|
+
if (candidates.length === 0) return "";
|
|
4181
|
+
const numeric = candidates.every((value) => !Number.isNaN(Number(value)));
|
|
4182
|
+
const compare = (left, right) => {
|
|
4183
|
+
if (numeric) {
|
|
4184
|
+
const leftNum = Number(left);
|
|
4185
|
+
const rightNum = Number(right);
|
|
4186
|
+
if (leftNum < rightNum) return -1;
|
|
4187
|
+
if (leftNum > rightNum) return 1;
|
|
4188
|
+
}
|
|
4189
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
4190
|
+
};
|
|
4191
|
+
return candidates.reduce((best, candidate) => {
|
|
4192
|
+
const cmp = compare(candidate, best);
|
|
4193
|
+
return extreme === "greatest" ? cmp > 0 ? candidate : best : cmp < 0 ? candidate : best;
|
|
4194
|
+
});
|
|
4195
|
+
}
|
|
4069
4196
|
|
|
4070
4197
|
// src/engine/evalFunc.ts
|
|
4071
4198
|
function evalArithExpr(expr, row) {
|
|
@@ -4114,6 +4241,36 @@ function evalStringFunc(expr, row) {
|
|
|
4114
4241
|
const len = args[2] !== void 0 ? Number(args[2]) : void 0;
|
|
4115
4242
|
return len !== void 0 ? str.slice(start, start + len) : str.slice(start);
|
|
4116
4243
|
}
|
|
4244
|
+
case "LEFT": {
|
|
4245
|
+
assertArity("LEFT", args, 2, 2);
|
|
4246
|
+
const str = args[0];
|
|
4247
|
+
const n = Math.trunc(Number(args[1]));
|
|
4248
|
+
return Number.isNaN(n) || n <= 0 ? "" : str.slice(0, n);
|
|
4249
|
+
}
|
|
4250
|
+
case "RIGHT": {
|
|
4251
|
+
assertArity("RIGHT", args, 2, 2);
|
|
4252
|
+
const str = args[0];
|
|
4253
|
+
const n = Math.trunc(Number(args[1]));
|
|
4254
|
+
return Number.isNaN(n) || n <= 0 ? "" : str.slice(Math.max(0, str.length - n));
|
|
4255
|
+
}
|
|
4256
|
+
case "INSTR":
|
|
4257
|
+
assertArity("INSTR", args, 2, 2);
|
|
4258
|
+
return String(args[0].indexOf(args[1]) + 1);
|
|
4259
|
+
case "LPAD":
|
|
4260
|
+
case "RPAD": {
|
|
4261
|
+
assertArity(expr.func, args, 2, 3);
|
|
4262
|
+
const str = args[0];
|
|
4263
|
+
const n = Math.trunc(Number(args[1]));
|
|
4264
|
+
if (Number.isNaN(n) || n <= 0) return "";
|
|
4265
|
+
if (str.length >= n) return str.slice(0, n);
|
|
4266
|
+
const pad = args[2] ?? " ";
|
|
4267
|
+
if (pad === "") return str;
|
|
4268
|
+
return expr.func === "LPAD" ? str.padStart(n, pad) : str.padEnd(n, pad);
|
|
4269
|
+
}
|
|
4270
|
+
case "GREATEST":
|
|
4271
|
+
case "LEAST":
|
|
4272
|
+
assertArity(expr.func, args, 2);
|
|
4273
|
+
return selectScalarExtreme(args, expr.func === "GREATEST" ? "greatest" : "least");
|
|
4117
4274
|
case "CONCAT":
|
|
4118
4275
|
return args.join("");
|
|
4119
4276
|
case "REPLACE": {
|
|
@@ -4134,6 +4291,9 @@ function evalStringFunc(expr, row) {
|
|
|
4134
4291
|
return applyRoundOp("floor", Number(args[0] ?? "0"), Number(args[1] ?? "0"));
|
|
4135
4292
|
case "CEIL":
|
|
4136
4293
|
return applyRoundOp("ceil", Number(args[0] ?? "0"), Number(args[1] ?? "0"));
|
|
4294
|
+
case "TRUNCATE":
|
|
4295
|
+
assertArity("TRUNCATE", args, 1, 2);
|
|
4296
|
+
return applyRoundOp("trunc", Number(args[0]), Number(args[1] ?? "0"));
|
|
4137
4297
|
case "CAST": {
|
|
4138
4298
|
const val = args[0] ?? "";
|
|
4139
4299
|
const castType = args[1] ?? "TEXT";
|
|
@@ -4166,6 +4326,9 @@ function evalStringFunc(expr, row) {
|
|
|
4166
4326
|
return applyDateDiff(args[0] ?? "", args[1] ?? "");
|
|
4167
4327
|
case "DATE_ADD":
|
|
4168
4328
|
return applyDateAdd(args[0] ?? "", Number(args[1] ?? "0"), (args[2] ?? "DAY").toUpperCase());
|
|
4329
|
+
case "LAST_DAY":
|
|
4330
|
+
assertArity("LAST_DAY", args, 1, 1);
|
|
4331
|
+
return applyLastDay(args[0]);
|
|
4169
4332
|
case "ABS":
|
|
4170
4333
|
return String(Math.abs(Number(args[0] ?? "0")));
|
|
4171
4334
|
case "MOD": {
|
|
@@ -4188,6 +4351,11 @@ function evalStringFunc(expr, row) {
|
|
|
4188
4351
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
4189
4352
|
}
|
|
4190
4353
|
}
|
|
4354
|
+
function assertArity(func, args, min, max = Number.POSITIVE_INFINITY) {
|
|
4355
|
+
if (args.length >= min && args.length <= max) return;
|
|
4356
|
+
const expected = min === max ? String(min) : max === Number.POSITIVE_INFINITY ? `${min} or more` : `${min} to ${max}`;
|
|
4357
|
+
throw new Error(`ArgumentError: ${func} expects ${expected} argument(s).`);
|
|
4358
|
+
}
|
|
4191
4359
|
function parseDateParts(s) {
|
|
4192
4360
|
return {
|
|
4193
4361
|
y: s.slice(0, 4) || "0000",
|
|
@@ -4212,6 +4380,9 @@ function applyDateDiff(date1, date2) {
|
|
|
4212
4380
|
return String(Math.round((d1 - d2) / 864e5));
|
|
4213
4381
|
}
|
|
4214
4382
|
function applyDateAdd(dateStr, n, unit) {
|
|
4383
|
+
if (unit !== "YEAR" && unit !== "MONTH" && unit !== "DAY") {
|
|
4384
|
+
throw new Error("ArgumentError: DATE_ADD unit must be YEAR, MONTH, or DAY.");
|
|
4385
|
+
}
|
|
4215
4386
|
if (!dateStr || dateStr.length < 10) return dateStr;
|
|
4216
4387
|
const { y, mo, d } = parseDateParts(dateStr);
|
|
4217
4388
|
const dt = new Date(Date.UTC(+y, +mo - 1, +d));
|
|
@@ -4222,7 +4393,7 @@ function applyDateAdd(dateStr, n, unit) {
|
|
|
4222
4393
|
case "MONTH":
|
|
4223
4394
|
dt.setUTCMonth(dt.getUTCMonth() + n);
|
|
4224
4395
|
break;
|
|
4225
|
-
|
|
4396
|
+
case "DAY":
|
|
4226
4397
|
dt.setUTCDate(dt.getUTCDate() + n);
|
|
4227
4398
|
break;
|
|
4228
4399
|
}
|
|
@@ -4231,6 +4402,15 @@ function applyDateAdd(dateStr, n, unit) {
|
|
|
4231
4402
|
const rd = String(dt.getUTCDate()).padStart(2, "0");
|
|
4232
4403
|
return `${ry}-${rmo}-${rd}`;
|
|
4233
4404
|
}
|
|
4405
|
+
function applyLastDay(dateStr) {
|
|
4406
|
+
if (!dateStr || dateStr.length < 10) return dateStr;
|
|
4407
|
+
const { y, mo } = parseDateParts(dateStr);
|
|
4408
|
+
const dt = new Date(Date.UTC(+y, +mo, 0));
|
|
4409
|
+
const ry = String(dt.getUTCFullYear()).padStart(4, "0");
|
|
4410
|
+
const rmo = String(dt.getUTCMonth() + 1).padStart(2, "0");
|
|
4411
|
+
const rd = String(dt.getUTCDate()).padStart(2, "0");
|
|
4412
|
+
return `${ry}-${rmo}-${rd}`;
|
|
4413
|
+
}
|
|
4234
4414
|
function applyFormat(num, pattern) {
|
|
4235
4415
|
if (/^-?\d+$/.test(pattern.trim())) {
|
|
4236
4416
|
return formatWithComma(num, Math.max(0, Number(pattern)));
|
|
@@ -5309,6 +5489,10 @@ function buildDistinctKeyBuilder(rows, columns) {
|
|
|
5309
5489
|
values.push(row[col.field] ?? "");
|
|
5310
5490
|
continue;
|
|
5311
5491
|
}
|
|
5492
|
+
if (col.type === "WINDOW_COL") {
|
|
5493
|
+
values.push(row[col.alias] ?? "");
|
|
5494
|
+
continue;
|
|
5495
|
+
}
|
|
5312
5496
|
if (col.type === "PARENT_WILDCARD") {
|
|
5313
5497
|
for (const k of sortedParentKeys) {
|
|
5314
5498
|
values.push(row[k] !== void 0 ? row[k] : null);
|
|
@@ -5320,6 +5504,9 @@ function buildDistinctKeyBuilder(rows, columns) {
|
|
|
5320
5504
|
}
|
|
5321
5505
|
function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
|
|
5322
5506
|
if (orderBy.length === 0) return rows;
|
|
5507
|
+
return sortDecoratedRows(rows, orderBy, optionOrders, sortKinds).rows.map((item) => item.row);
|
|
5508
|
+
}
|
|
5509
|
+
function sortDecoratedRows(rows, orderBy, optionOrders, sortKinds) {
|
|
5323
5510
|
const keyMeta = orderBy.map(({ key }) => ({
|
|
5324
5511
|
orderMap: key.type === "FIELD_NAME" ? optionOrders?.get(key.name) : void 0,
|
|
5325
5512
|
sortKind: key.type === "FIELD_NAME" ? sortKinds?.get(key.name) : void 0
|
|
@@ -5338,14 +5525,16 @@ function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
|
|
|
5338
5525
|
};
|
|
5339
5526
|
})
|
|
5340
5527
|
}));
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5528
|
+
const compare = (a, b) => compareDecoratedRows(a, b, orderBy, keyMeta);
|
|
5529
|
+
decorated.sort(compare);
|
|
5530
|
+
return { rows: decorated, compare };
|
|
5531
|
+
}
|
|
5532
|
+
function compareDecoratedRows(a, b, orderBy, keyMeta) {
|
|
5533
|
+
for (let i = 0; i < orderBy.length; i++) {
|
|
5534
|
+
const cmp = compareSortKeys(a.keys[i], b.keys[i], keyMeta[i]);
|
|
5535
|
+
if (cmp !== 0) return orderBy[i].direction === "ASC" ? cmp : -cmp;
|
|
5536
|
+
}
|
|
5537
|
+
return 0;
|
|
5349
5538
|
}
|
|
5350
5539
|
function compareSortKeys(a, b, meta) {
|
|
5351
5540
|
if (meta.orderMap) {
|
|
@@ -5390,6 +5579,38 @@ function minChoiceIndex(values, orderMap) {
|
|
|
5390
5579
|
}
|
|
5391
5580
|
return min;
|
|
5392
5581
|
}
|
|
5582
|
+
function applyWindow(rows, columns, optionOrders, sortKinds) {
|
|
5583
|
+
const windows = columns.filter((column) => column.type === "WINDOW_COL");
|
|
5584
|
+
if (rows.length === 0 || windows.length === 0) return rows;
|
|
5585
|
+
for (const window of windows) {
|
|
5586
|
+
const partitions = /* @__PURE__ */ new Map();
|
|
5587
|
+
for (const row of rows) {
|
|
5588
|
+
const key = JSON.stringify(window.partitionBy.map((ref) => resolveWindowField(row, ref)));
|
|
5589
|
+
const partition = partitions.get(key);
|
|
5590
|
+
if (partition) partition.push(row);
|
|
5591
|
+
else partitions.set(key, [row]);
|
|
5592
|
+
}
|
|
5593
|
+
for (const partition of partitions.values()) {
|
|
5594
|
+
const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds);
|
|
5595
|
+
const sorted = sortedResult.rows;
|
|
5596
|
+
let rank = 1;
|
|
5597
|
+
let denseRank = 1;
|
|
5598
|
+
for (let index = 0; index < sorted.length; index++) {
|
|
5599
|
+
if (index > 0 && sortedResult.compare(sorted[index - 1], sorted[index]) !== 0) {
|
|
5600
|
+
rank = index + 1;
|
|
5601
|
+
denseRank++;
|
|
5602
|
+
}
|
|
5603
|
+
const value = window.func === "ROW_NUMBER" ? index + 1 : window.func === "RANK" ? rank : denseRank;
|
|
5604
|
+
sorted[index].row[window.alias] = String(value);
|
|
5605
|
+
}
|
|
5606
|
+
}
|
|
5607
|
+
}
|
|
5608
|
+
return rows;
|
|
5609
|
+
}
|
|
5610
|
+
function resolveWindowField(row, ref) {
|
|
5611
|
+
const name = ref.tableAlias ? `${ref.tableAlias}.${ref.field}` : ref.field;
|
|
5612
|
+
return resolveFieldRef(row, name);
|
|
5613
|
+
}
|
|
5393
5614
|
function applyLimit(rows, limit, offset) {
|
|
5394
5615
|
const start = offset ?? 0;
|
|
5395
5616
|
if (limit === null) return rows.slice(start);
|
|
@@ -5480,6 +5701,12 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
|
|
|
5480
5701
|
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
5481
5702
|
break;
|
|
5482
5703
|
}
|
|
5704
|
+
case "WINDOW_COL": {
|
|
5705
|
+
const key = outputKeys?.[colIdx] ?? col.alias;
|
|
5706
|
+
out[key] = row[col.alias] ?? "";
|
|
5707
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
5708
|
+
break;
|
|
5709
|
+
}
|
|
5483
5710
|
}
|
|
5484
5711
|
}
|
|
5485
5712
|
return out;
|
|
@@ -5520,6 +5747,8 @@ function computeOutputKey(col, colIdx, defaultFieldKeys) {
|
|
|
5520
5747
|
return col.alias ?? stringFuncDefaultKey(col.expr);
|
|
5521
5748
|
case "SCALAR_SUBQUERY_COL":
|
|
5522
5749
|
return col.alias ?? "(subquery)";
|
|
5750
|
+
case "WINDOW_COL":
|
|
5751
|
+
return col.alias;
|
|
5523
5752
|
case "WILDCARD":
|
|
5524
5753
|
case "PARENT_WILDCARD":
|
|
5525
5754
|
throw new Error("internal: computeOutputKey received a wildcard column");
|
|
@@ -5634,6 +5863,7 @@ function runFullScan(input) {
|
|
|
5634
5863
|
rows = applyGroupBy(rows, stmt.groupBy, stmt.columns, aggregateSortKindResolver);
|
|
5635
5864
|
}
|
|
5636
5865
|
rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
|
|
5866
|
+
rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds);
|
|
5637
5867
|
if (stmt.distinct) {
|
|
5638
5868
|
rows = applyDistinct(rows, stmt.columns);
|
|
5639
5869
|
}
|
|
@@ -6578,6 +6808,11 @@ function validateNoFromColumns(stmt) {
|
|
|
6578
6808
|
throw new Error("ArgumentError: field reference is not allowed without FROM.");
|
|
6579
6809
|
}
|
|
6580
6810
|
break;
|
|
6811
|
+
case "WINDOW_COL":
|
|
6812
|
+
if (col.partitionBy.length > 0 || col.orderBy.length > 0) {
|
|
6813
|
+
throw new Error("ArgumentError: field reference is not allowed without FROM.");
|
|
6814
|
+
}
|
|
6815
|
+
break;
|
|
6581
6816
|
default:
|
|
6582
6817
|
throw new Error(`ArgumentError: ${col.type} is not supported without FROM.`);
|
|
6583
6818
|
}
|
|
@@ -6588,7 +6823,8 @@ function executeNoFromSelect(stmt) {
|
|
|
6588
6823
|
throw new Error("ArgumentError: JOIN/WHERE/GROUP BY/HAVING/ORDER BY/DISTINCT are not supported without FROM.");
|
|
6589
6824
|
}
|
|
6590
6825
|
validateNoFromColumns(stmt);
|
|
6591
|
-
const
|
|
6826
|
+
const windowed = applyWindow([{}], stmt.columns);
|
|
6827
|
+
const { rows: projected, columns } = project(windowed, stmt.columns);
|
|
6592
6828
|
const rows = applyLimit(projected, stmt.limit, stmt.offset);
|
|
6593
6829
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [] };
|
|
6594
6830
|
}
|
|
@@ -7012,6 +7248,8 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
7012
7248
|
meta = { sortKind: "number" };
|
|
7013
7249
|
} else if (column.type === "LITERAL_COL") {
|
|
7014
7250
|
meta = { sortKind: "string" };
|
|
7251
|
+
} else if (column.type === "WINDOW_COL") {
|
|
7252
|
+
meta = { sortKind: "number" };
|
|
7015
7253
|
}
|
|
7016
7254
|
if (meta) inferred.set(output, meta);
|
|
7017
7255
|
});
|
|
@@ -7597,7 +7835,10 @@ async function getSortKindMapByApp(appId, client, cacheContext) {
|
|
|
7597
7835
|
return map;
|
|
7598
7836
|
}
|
|
7599
7837
|
async function buildOrderByMetaForSelect(stmt, client, cacheContext) {
|
|
7600
|
-
|
|
7838
|
+
const hasWindowOrderBy = stmt.columns.some(
|
|
7839
|
+
(column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
|
|
7840
|
+
);
|
|
7841
|
+
if (stmt.orderBy.length === 0 && !hasWindowOrderBy) {
|
|
7601
7842
|
return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map() };
|
|
7602
7843
|
}
|
|
7603
7844
|
const [optionOrders, sortKinds] = await Promise.all([
|
|
@@ -9059,6 +9300,8 @@ function collectFullScanReasons(stmt) {
|
|
|
9059
9300
|
r.push("DISTINCT \u3042\u308A");
|
|
9060
9301
|
if (stmt.columns.some((c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL"))
|
|
9061
9302
|
r.push("\u96C6\u8A08\u95A2\u6570\uFF08COUNT / SUM \u7B49\uFF09\u3042\u308A");
|
|
9303
|
+
if (stmt.columns.some((c) => c.type === "WINDOW_COL"))
|
|
9304
|
+
r.push("\u30A6\u30A3\u30F3\u30C9\u30A6\u95A2\u6570\u3042\u308A");
|
|
9062
9305
|
if (stmt.columns.some((c) => c.type === "SCALAR_SUBQUERY_COL"))
|
|
9063
9306
|
r.push("SELECT \u5217\u306B\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA");
|
|
9064
9307
|
if (whereRequiresJsEval(stmt.where))
|
package/dist-mcp/ksql-mcp.js
CHANGED
|
@@ -31000,6 +31000,9 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
31000
31000
|
["MAX", "MAX" /* MAX */],
|
|
31001
31001
|
["MIN", "MIN" /* MIN */],
|
|
31002
31002
|
["GROUP_CONCAT", "GROUP_CONCAT" /* GROUP_CONCAT */],
|
|
31003
|
+
["ROW_NUMBER", "ROW_NUMBER" /* ROW_NUMBER */],
|
|
31004
|
+
["RANK", "RANK" /* RANK */],
|
|
31005
|
+
["DENSE_RANK", "DENSE_RANK" /* DENSE_RANK */],
|
|
31003
31006
|
["ASSERT", "ASSERT" /* ASSERT */],
|
|
31004
31007
|
["AND", "AND" /* AND */],
|
|
31005
31008
|
["OR", "OR" /* OR */],
|
|
@@ -31032,6 +31035,11 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
31032
31035
|
["COALESCE", "COALESCE" /* COALESCE */],
|
|
31033
31036
|
["NULLIF", "NULLIF" /* NULLIF */],
|
|
31034
31037
|
["ISNULL", "ISNULL" /* ISNULL */],
|
|
31038
|
+
["INSTR", "INSTR" /* INSTR */],
|
|
31039
|
+
["GREATEST", "GREATEST" /* GREATEST */],
|
|
31040
|
+
["LEAST", "LEAST" /* LEAST */],
|
|
31041
|
+
["LPAD", "LPAD" /* LPAD */],
|
|
31042
|
+
["RPAD", "RPAD" /* RPAD */],
|
|
31035
31043
|
["CAST", "CAST" /* CAST */],
|
|
31036
31044
|
["CONVERT", "CONVERT" /* CONVERT */],
|
|
31037
31045
|
["FORMAT", "FORMAT" /* FORMAT */],
|
|
@@ -31039,6 +31047,8 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
31039
31047
|
["FLOOR", "FLOOR" /* FLOOR */],
|
|
31040
31048
|
["CEIL", "CEIL" /* CEIL */],
|
|
31041
31049
|
["CEILING", "CEILING" /* CEILING */],
|
|
31050
|
+
["TRUNCATE", "TRUNCATE" /* TRUNCATE */],
|
|
31051
|
+
["TRUNC", "TRUNC" /* TRUNC */],
|
|
31042
31052
|
["ABS", "ABS" /* ABS */],
|
|
31043
31053
|
["MOD", "MOD" /* MOD */],
|
|
31044
31054
|
["POWER", "POWER" /* POWER */],
|
|
@@ -31050,6 +31060,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
31050
31060
|
["DATE_FORMAT", "DATE_FORMAT" /* DATE_FORMAT */],
|
|
31051
31061
|
["DATEDIFF", "DATEDIFF" /* DATEDIFF */],
|
|
31052
31062
|
["DATE_ADD", "DATE_ADD" /* DATE_ADD */],
|
|
31063
|
+
["LAST_DAY", "LAST_DAY" /* LAST_DAY */],
|
|
31053
31064
|
["IF", "IF" /* IF */]
|
|
31054
31065
|
]);
|
|
31055
31066
|
|
|
@@ -31376,6 +31387,9 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
|
|
|
31376
31387
|
"AVG" /* AVG */,
|
|
31377
31388
|
"MAX" /* MAX */,
|
|
31378
31389
|
"MIN" /* MIN */,
|
|
31390
|
+
"ROW_NUMBER" /* ROW_NUMBER */,
|
|
31391
|
+
"RANK" /* RANK */,
|
|
31392
|
+
"DENSE_RANK" /* DENSE_RANK */,
|
|
31379
31393
|
"TODAY" /* TODAY */,
|
|
31380
31394
|
"NOW" /* NOW */,
|
|
31381
31395
|
"LOGINUSER" /* LOGINUSER */,
|
|
@@ -31392,6 +31406,13 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
|
|
|
31392
31406
|
"COALESCE" /* COALESCE */,
|
|
31393
31407
|
"NULLIF" /* NULLIF */,
|
|
31394
31408
|
"ISNULL" /* ISNULL */,
|
|
31409
|
+
"LEFT" /* LEFT */,
|
|
31410
|
+
"RIGHT" /* RIGHT */,
|
|
31411
|
+
"INSTR" /* INSTR */,
|
|
31412
|
+
"GREATEST" /* GREATEST */,
|
|
31413
|
+
"LEAST" /* LEAST */,
|
|
31414
|
+
"LPAD" /* LPAD */,
|
|
31415
|
+
"RPAD" /* RPAD */,
|
|
31395
31416
|
"CAST" /* CAST */,
|
|
31396
31417
|
"CONVERT" /* CONVERT */,
|
|
31397
31418
|
"FORMAT" /* FORMAT */,
|
|
@@ -31399,6 +31420,8 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
|
|
|
31399
31420
|
"FLOOR" /* FLOOR */,
|
|
31400
31421
|
"CEIL" /* CEIL */,
|
|
31401
31422
|
"CEILING" /* CEILING */,
|
|
31423
|
+
"TRUNCATE" /* TRUNCATE */,
|
|
31424
|
+
"TRUNC" /* TRUNC */,
|
|
31402
31425
|
"ABS" /* ABS */,
|
|
31403
31426
|
"MOD" /* MOD */,
|
|
31404
31427
|
"POWER" /* POWER */,
|
|
@@ -31410,6 +31433,7 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
|
|
|
31410
31433
|
"DATE_FORMAT" /* DATE_FORMAT */,
|
|
31411
31434
|
"DATEDIFF" /* DATEDIFF */,
|
|
31412
31435
|
"DATE_ADD" /* DATE_ADD */,
|
|
31436
|
+
"LAST_DAY" /* LAST_DAY */,
|
|
31413
31437
|
"IF" /* IF */
|
|
31414
31438
|
]);
|
|
31415
31439
|
function needsSpaceBetween(prev, cur) {
|
|
@@ -31894,6 +31918,11 @@ var Parser = class {
|
|
|
31894
31918
|
const orderBy = this.consume("ORDER" /* ORDER */) ? (this.expect("BY" /* BY */), this.parseOrderBy()) : [];
|
|
31895
31919
|
const limit = this.consume("LIMIT" /* LIMIT */) ? this.parseUnsignedInt() : null;
|
|
31896
31920
|
const offset = this.consume("OFFSET" /* OFFSET */) ? this.parseUnsignedInt() : null;
|
|
31921
|
+
const hasWindow = columns.some((column) => column.type === "WINDOW_COL");
|
|
31922
|
+
const hasAggregate = columns.some((column) => this.selectColumnHasAggregate(column));
|
|
31923
|
+
if (hasWindow && (groupBy.length > 0 || hasAggregate)) {
|
|
31924
|
+
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());
|
|
31925
|
+
}
|
|
31897
31926
|
return {
|
|
31898
31927
|
type: "SELECT",
|
|
31899
31928
|
distinct,
|
|
@@ -31958,6 +31987,10 @@ var Parser = class {
|
|
|
31958
31987
|
if (this.consume("*" /* STAR */)) {
|
|
31959
31988
|
return { type: "WILDCARD" };
|
|
31960
31989
|
}
|
|
31990
|
+
const windowFunc = this.tryWindowFunc();
|
|
31991
|
+
if (windowFunc !== null) {
|
|
31992
|
+
return this.parseWindowColumn(windowFunc);
|
|
31993
|
+
}
|
|
31961
31994
|
if (this.peek().kind === "CASE" /* CASE */) {
|
|
31962
31995
|
const expr = this.parseCaseWhenExpr();
|
|
31963
31996
|
const alias2 = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
|
|
@@ -32029,6 +32062,56 @@ var Parser = class {
|
|
|
32029
32062
|
const alias = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
|
|
32030
32063
|
return { type: "FIELD", field, alias };
|
|
32031
32064
|
}
|
|
32065
|
+
tryWindowFunc() {
|
|
32066
|
+
switch (this.peek().kind) {
|
|
32067
|
+
case "ROW_NUMBER" /* ROW_NUMBER */:
|
|
32068
|
+
return "ROW_NUMBER";
|
|
32069
|
+
case "RANK" /* RANK */:
|
|
32070
|
+
return "RANK";
|
|
32071
|
+
case "DENSE_RANK" /* DENSE_RANK */:
|
|
32072
|
+
return "DENSE_RANK";
|
|
32073
|
+
default:
|
|
32074
|
+
return null;
|
|
32075
|
+
}
|
|
32076
|
+
}
|
|
32077
|
+
parseWindowColumn(func) {
|
|
32078
|
+
this.advance();
|
|
32079
|
+
this.expect("(" /* LPAREN */);
|
|
32080
|
+
if (this.peek().kind !== ")" /* RPAREN */) {
|
|
32081
|
+
throw new ParseError(`${func} \u306F\u5F15\u6570\u3092\u53D7\u3051\u4ED8\u3051\u307E\u305B\u3093`, this.peek());
|
|
32082
|
+
}
|
|
32083
|
+
this.expect(")" /* RPAREN */);
|
|
32084
|
+
this.expectSoftKeyword("OVER", `${func} \u306B\u306F OVER (...) \u304C\u5FC5\u8981\u3067\u3059`);
|
|
32085
|
+
this.expect("(" /* LPAREN */);
|
|
32086
|
+
const partitionBy = [];
|
|
32087
|
+
if (this.isSoftKeyword("PARTITION")) {
|
|
32088
|
+
this.advance();
|
|
32089
|
+
this.expect("BY" /* BY */, "PARTITION \u306E\u5F8C\u306B\u306F BY \u304C\u5FC5\u8981\u3067\u3059");
|
|
32090
|
+
do {
|
|
32091
|
+
const ref = this.parseQualifiedIdent();
|
|
32092
|
+
partitionBy.push({ type: "FIELD", tableAlias: ref.tableAlias, field: ref.field });
|
|
32093
|
+
} while (this.consume("," /* COMMA */));
|
|
32094
|
+
}
|
|
32095
|
+
const orderBy = this.consume("ORDER" /* ORDER */) ? (this.expect("BY" /* BY */), this.parseOrderBy()) : [];
|
|
32096
|
+
this.expect(")" /* RPAREN */);
|
|
32097
|
+
if (!this.consume("AS" /* AS */)) {
|
|
32098
|
+
throw new ParseError("\u30A6\u30A3\u30F3\u30C9\u30A6\u95A2\u6570\u306B\u306F AS alias \u304C\u5FC5\u8981\u3067\u3059", this.peek());
|
|
32099
|
+
}
|
|
32100
|
+
const alias = this.parseAliasName();
|
|
32101
|
+
return { type: "WINDOW_COL", func, partitionBy, orderBy, alias };
|
|
32102
|
+
}
|
|
32103
|
+
selectColumnHasAggregate(column) {
|
|
32104
|
+
if (column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL") return true;
|
|
32105
|
+
if (column.type !== "STRFUNC_COL") return false;
|
|
32106
|
+
return column.expr.args.some((arg) => this.stringFuncArgHasAggregate(arg));
|
|
32107
|
+
}
|
|
32108
|
+
stringFuncArgHasAggregate(arg) {
|
|
32109
|
+
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
32110
|
+
if (arg.type === "STRING_FUNC") {
|
|
32111
|
+
return arg.args.some((nested) => this.stringFuncArgHasAggregate(nested));
|
|
32112
|
+
}
|
|
32113
|
+
return false;
|
|
32114
|
+
}
|
|
32032
32115
|
isArithOp(kind) {
|
|
32033
32116
|
return kind === "+" /* PLUS */ || kind === "-" /* MINUS */ || kind === "*" /* STAR */ || kind === "/" /* SLASH */ || kind === "%" /* PERCENT */;
|
|
32034
32117
|
}
|
|
@@ -32223,6 +32306,10 @@ var Parser = class {
|
|
|
32223
32306
|
// arg: 文字列リテラル / 算術式(フィールド参照・数値含む)/ ネスト文字列関数
|
|
32224
32307
|
// ──────────────────────────────────────────────────
|
|
32225
32308
|
tryStringFuncName() {
|
|
32309
|
+
if (this.peekAt(1).kind === "(" /* LPAREN */) {
|
|
32310
|
+
if (this.peek().kind === "LEFT" /* LEFT */) return "LEFT";
|
|
32311
|
+
if (this.peek().kind === "RIGHT" /* RIGHT */) return "RIGHT";
|
|
32312
|
+
}
|
|
32226
32313
|
const map2 = {
|
|
32227
32314
|
["UPPER" /* UPPER */]: "UPPER",
|
|
32228
32315
|
["LOWER" /* LOWER */]: "LOWER",
|
|
@@ -32237,6 +32324,11 @@ var Parser = class {
|
|
|
32237
32324
|
["COALESCE" /* COALESCE */]: "COALESCE",
|
|
32238
32325
|
["NULLIF" /* NULLIF */]: "NULLIF",
|
|
32239
32326
|
["ISNULL" /* ISNULL */]: "ISNULL",
|
|
32327
|
+
["INSTR" /* INSTR */]: "INSTR",
|
|
32328
|
+
["GREATEST" /* GREATEST */]: "GREATEST",
|
|
32329
|
+
["LEAST" /* LEAST */]: "LEAST",
|
|
32330
|
+
["LPAD" /* LPAD */]: "LPAD",
|
|
32331
|
+
["RPAD" /* RPAD */]: "RPAD",
|
|
32240
32332
|
["CAST" /* CAST */]: "CAST",
|
|
32241
32333
|
["CONVERT" /* CONVERT */]: "CAST",
|
|
32242
32334
|
// CONVERT → CAST に正規化
|
|
@@ -32246,12 +32338,16 @@ var Parser = class {
|
|
|
32246
32338
|
["CEIL" /* CEIL */]: "CEIL",
|
|
32247
32339
|
["CEILING" /* CEILING */]: "CEIL",
|
|
32248
32340
|
// CEILING → CEIL に正規化
|
|
32341
|
+
["TRUNCATE" /* TRUNCATE */]: "TRUNCATE",
|
|
32342
|
+
["TRUNC" /* TRUNC */]: "TRUNCATE",
|
|
32343
|
+
// TRUNC → TRUNCATE に正規化
|
|
32249
32344
|
["YEAR" /* YEAR */]: "YEAR",
|
|
32250
32345
|
["MONTH" /* MONTH */]: "MONTH",
|
|
32251
32346
|
["DAY" /* DAY */]: "DAY",
|
|
32252
32347
|
["DATE_FORMAT" /* DATE_FORMAT */]: "DATE_FORMAT",
|
|
32253
32348
|
["DATEDIFF" /* DATEDIFF */]: "DATEDIFF",
|
|
32254
32349
|
["DATE_ADD" /* DATE_ADD */]: "DATE_ADD",
|
|
32350
|
+
["LAST_DAY" /* LAST_DAY */]: "LAST_DAY",
|
|
32255
32351
|
["ABS" /* ABS */]: "ABS",
|
|
32256
32352
|
["MOD" /* MOD */]: "MOD",
|
|
32257
32353
|
["POWER" /* POWER */]: "POWER",
|
|
@@ -33798,12 +33894,16 @@ var KintoneQueryError = class extends Error {
|
|
|
33798
33894
|
};
|
|
33799
33895
|
|
|
33800
33896
|
// src/converter/selectToKintone.ts
|
|
33897
|
+
function hasWindowColumns(columns) {
|
|
33898
|
+
return columns.some((column) => column.type === "WINDOW_COL");
|
|
33899
|
+
}
|
|
33801
33900
|
function resolveSelectMode(stmt) {
|
|
33802
33901
|
if (stmt.from.subtableCode) return "FULL_SCAN";
|
|
33803
33902
|
if (stmt.joins.some((j) => j.table.subtableCode)) return "FULL_SCAN";
|
|
33804
33903
|
if (stmt.joins.length > 0) return "FULL_SCAN";
|
|
33805
33904
|
if (stmt.groupBy.length > 0) return "FULL_SCAN";
|
|
33806
33905
|
if (stmt.distinct) return "FULL_SCAN";
|
|
33906
|
+
if (hasWindowColumns(stmt.columns)) return "FULL_SCAN";
|
|
33807
33907
|
if (stmt.columns.some(
|
|
33808
33908
|
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "SCALAR_SUBQUERY_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr(c.expr)
|
|
33809
33909
|
)) return "FULL_SCAN";
|
|
@@ -34175,16 +34275,16 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
34175
34275
|
}
|
|
34176
34276
|
walkStringFunc(k.expr, "groupBy");
|
|
34177
34277
|
};
|
|
34178
|
-
const walkOrderByKey = (k) => {
|
|
34278
|
+
const walkOrderByKey = (k, phase = "orderBy") => {
|
|
34179
34279
|
if (k.type === "FIELD_NAME") {
|
|
34180
|
-
addFieldName(k.name,
|
|
34280
|
+
addFieldName(k.name, phase);
|
|
34181
34281
|
return;
|
|
34182
34282
|
}
|
|
34183
34283
|
if (k.type === "ARITH_KEY") {
|
|
34184
|
-
walkArith(k.expr,
|
|
34284
|
+
walkArith(k.expr, phase);
|
|
34185
34285
|
return;
|
|
34186
34286
|
}
|
|
34187
|
-
walkStringFunc(k.expr,
|
|
34287
|
+
walkStringFunc(k.expr, phase);
|
|
34188
34288
|
};
|
|
34189
34289
|
for (const col of stmt.columns) {
|
|
34190
34290
|
switch (col.type) {
|
|
@@ -34216,6 +34316,10 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
34216
34316
|
break;
|
|
34217
34317
|
case "SCALAR_SUBQUERY_COL":
|
|
34218
34318
|
break;
|
|
34319
|
+
case "WINDOW_COL":
|
|
34320
|
+
for (const ref of col.partitionBy) addFieldRef(ref.field, ref.tableAlias, "select");
|
|
34321
|
+
for (const item of col.orderBy) walkOrderByKey(item.key, "select");
|
|
34322
|
+
break;
|
|
34219
34323
|
}
|
|
34220
34324
|
}
|
|
34221
34325
|
for (const join of stmt.joins) {
|
|
@@ -34262,6 +34366,10 @@ function collectSelectOutputNames(columns) {
|
|
|
34262
34366
|
}
|
|
34263
34367
|
if (col.type === "SCALAR_SUBQUERY_COL") {
|
|
34264
34368
|
names.add(col.alias ?? "(subquery)");
|
|
34369
|
+
continue;
|
|
34370
|
+
}
|
|
34371
|
+
if (col.type === "WINDOW_COL") {
|
|
34372
|
+
names.add(col.alias);
|
|
34265
34373
|
}
|
|
34266
34374
|
}
|
|
34267
34375
|
return names;
|
|
@@ -34966,6 +35074,25 @@ function compareScalarValues(op, leftStr, rightStr) {
|
|
|
34966
35074
|
return numeric ? leftNum <= rightNum : leftStr <= rightStr;
|
|
34967
35075
|
}
|
|
34968
35076
|
}
|
|
35077
|
+
function selectScalarExtreme(values, extreme) {
|
|
35078
|
+
if (extreme === "least" && values.includes("")) return "";
|
|
35079
|
+
const candidates = extreme === "greatest" ? values.filter((value) => value !== "") : [...values];
|
|
35080
|
+
if (candidates.length === 0) return "";
|
|
35081
|
+
const numeric = candidates.every((value) => !Number.isNaN(Number(value)));
|
|
35082
|
+
const compare = (left, right) => {
|
|
35083
|
+
if (numeric) {
|
|
35084
|
+
const leftNum = Number(left);
|
|
35085
|
+
const rightNum = Number(right);
|
|
35086
|
+
if (leftNum < rightNum) return -1;
|
|
35087
|
+
if (leftNum > rightNum) return 1;
|
|
35088
|
+
}
|
|
35089
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
35090
|
+
};
|
|
35091
|
+
return candidates.reduce((best, candidate) => {
|
|
35092
|
+
const cmp = compare(candidate, best);
|
|
35093
|
+
return extreme === "greatest" ? cmp > 0 ? candidate : best : cmp < 0 ? candidate : best;
|
|
35094
|
+
});
|
|
35095
|
+
}
|
|
34969
35096
|
|
|
34970
35097
|
// src/engine/evalFunc.ts
|
|
34971
35098
|
function evalArithExpr(expr, row) {
|
|
@@ -35014,6 +35141,36 @@ function evalStringFunc(expr, row) {
|
|
|
35014
35141
|
const len = args[2] !== void 0 ? Number(args[2]) : void 0;
|
|
35015
35142
|
return len !== void 0 ? str.slice(start, start + len) : str.slice(start);
|
|
35016
35143
|
}
|
|
35144
|
+
case "LEFT": {
|
|
35145
|
+
assertArity("LEFT", args, 2, 2);
|
|
35146
|
+
const str = args[0];
|
|
35147
|
+
const n = Math.trunc(Number(args[1]));
|
|
35148
|
+
return Number.isNaN(n) || n <= 0 ? "" : str.slice(0, n);
|
|
35149
|
+
}
|
|
35150
|
+
case "RIGHT": {
|
|
35151
|
+
assertArity("RIGHT", args, 2, 2);
|
|
35152
|
+
const str = args[0];
|
|
35153
|
+
const n = Math.trunc(Number(args[1]));
|
|
35154
|
+
return Number.isNaN(n) || n <= 0 ? "" : str.slice(Math.max(0, str.length - n));
|
|
35155
|
+
}
|
|
35156
|
+
case "INSTR":
|
|
35157
|
+
assertArity("INSTR", args, 2, 2);
|
|
35158
|
+
return String(args[0].indexOf(args[1]) + 1);
|
|
35159
|
+
case "LPAD":
|
|
35160
|
+
case "RPAD": {
|
|
35161
|
+
assertArity(expr.func, args, 2, 3);
|
|
35162
|
+
const str = args[0];
|
|
35163
|
+
const n = Math.trunc(Number(args[1]));
|
|
35164
|
+
if (Number.isNaN(n) || n <= 0) return "";
|
|
35165
|
+
if (str.length >= n) return str.slice(0, n);
|
|
35166
|
+
const pad = args[2] ?? " ";
|
|
35167
|
+
if (pad === "") return str;
|
|
35168
|
+
return expr.func === "LPAD" ? str.padStart(n, pad) : str.padEnd(n, pad);
|
|
35169
|
+
}
|
|
35170
|
+
case "GREATEST":
|
|
35171
|
+
case "LEAST":
|
|
35172
|
+
assertArity(expr.func, args, 2);
|
|
35173
|
+
return selectScalarExtreme(args, expr.func === "GREATEST" ? "greatest" : "least");
|
|
35017
35174
|
case "CONCAT":
|
|
35018
35175
|
return args.join("");
|
|
35019
35176
|
case "REPLACE": {
|
|
@@ -35034,6 +35191,9 @@ function evalStringFunc(expr, row) {
|
|
|
35034
35191
|
return applyRoundOp("floor", Number(args[0] ?? "0"), Number(args[1] ?? "0"));
|
|
35035
35192
|
case "CEIL":
|
|
35036
35193
|
return applyRoundOp("ceil", Number(args[0] ?? "0"), Number(args[1] ?? "0"));
|
|
35194
|
+
case "TRUNCATE":
|
|
35195
|
+
assertArity("TRUNCATE", args, 1, 2);
|
|
35196
|
+
return applyRoundOp("trunc", Number(args[0]), Number(args[1] ?? "0"));
|
|
35037
35197
|
case "CAST": {
|
|
35038
35198
|
const val = args[0] ?? "";
|
|
35039
35199
|
const castType = args[1] ?? "TEXT";
|
|
@@ -35066,6 +35226,9 @@ function evalStringFunc(expr, row) {
|
|
|
35066
35226
|
return applyDateDiff(args[0] ?? "", args[1] ?? "");
|
|
35067
35227
|
case "DATE_ADD":
|
|
35068
35228
|
return applyDateAdd(args[0] ?? "", Number(args[1] ?? "0"), (args[2] ?? "DAY").toUpperCase());
|
|
35229
|
+
case "LAST_DAY":
|
|
35230
|
+
assertArity("LAST_DAY", args, 1, 1);
|
|
35231
|
+
return applyLastDay(args[0]);
|
|
35069
35232
|
case "ABS":
|
|
35070
35233
|
return String(Math.abs(Number(args[0] ?? "0")));
|
|
35071
35234
|
case "MOD": {
|
|
@@ -35088,6 +35251,11 @@ function evalStringFunc(expr, row) {
|
|
|
35088
35251
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
35089
35252
|
}
|
|
35090
35253
|
}
|
|
35254
|
+
function assertArity(func, args, min, max = Number.POSITIVE_INFINITY) {
|
|
35255
|
+
if (args.length >= min && args.length <= max) return;
|
|
35256
|
+
const expected = min === max ? String(min) : max === Number.POSITIVE_INFINITY ? `${min} or more` : `${min} to ${max}`;
|
|
35257
|
+
throw new Error(`ArgumentError: ${func} expects ${expected} argument(s).`);
|
|
35258
|
+
}
|
|
35091
35259
|
function parseDateParts(s) {
|
|
35092
35260
|
return {
|
|
35093
35261
|
y: s.slice(0, 4) || "0000",
|
|
@@ -35112,6 +35280,9 @@ function applyDateDiff(date1, date22) {
|
|
|
35112
35280
|
return String(Math.round((d1 - d2) / 864e5));
|
|
35113
35281
|
}
|
|
35114
35282
|
function applyDateAdd(dateStr, n, unit) {
|
|
35283
|
+
if (unit !== "YEAR" && unit !== "MONTH" && unit !== "DAY") {
|
|
35284
|
+
throw new Error("ArgumentError: DATE_ADD unit must be YEAR, MONTH, or DAY.");
|
|
35285
|
+
}
|
|
35115
35286
|
if (!dateStr || dateStr.length < 10) return dateStr;
|
|
35116
35287
|
const { y, mo, d } = parseDateParts(dateStr);
|
|
35117
35288
|
const dt = new Date(Date.UTC(+y, +mo - 1, +d));
|
|
@@ -35122,7 +35293,7 @@ function applyDateAdd(dateStr, n, unit) {
|
|
|
35122
35293
|
case "MONTH":
|
|
35123
35294
|
dt.setUTCMonth(dt.getUTCMonth() + n);
|
|
35124
35295
|
break;
|
|
35125
|
-
|
|
35296
|
+
case "DAY":
|
|
35126
35297
|
dt.setUTCDate(dt.getUTCDate() + n);
|
|
35127
35298
|
break;
|
|
35128
35299
|
}
|
|
@@ -35131,6 +35302,15 @@ function applyDateAdd(dateStr, n, unit) {
|
|
|
35131
35302
|
const rd = String(dt.getUTCDate()).padStart(2, "0");
|
|
35132
35303
|
return `${ry}-${rmo}-${rd}`;
|
|
35133
35304
|
}
|
|
35305
|
+
function applyLastDay(dateStr) {
|
|
35306
|
+
if (!dateStr || dateStr.length < 10) return dateStr;
|
|
35307
|
+
const { y, mo } = parseDateParts(dateStr);
|
|
35308
|
+
const dt = new Date(Date.UTC(+y, +mo, 0));
|
|
35309
|
+
const ry = String(dt.getUTCFullYear()).padStart(4, "0");
|
|
35310
|
+
const rmo = String(dt.getUTCMonth() + 1).padStart(2, "0");
|
|
35311
|
+
const rd = String(dt.getUTCDate()).padStart(2, "0");
|
|
35312
|
+
return `${ry}-${rmo}-${rd}`;
|
|
35313
|
+
}
|
|
35134
35314
|
function applyFormat(num, pattern) {
|
|
35135
35315
|
if (/^-?\d+$/.test(pattern.trim())) {
|
|
35136
35316
|
return formatWithComma(num, Math.max(0, Number(pattern)));
|
|
@@ -36209,6 +36389,10 @@ function buildDistinctKeyBuilder(rows, columns) {
|
|
|
36209
36389
|
values.push(row[col.field] ?? "");
|
|
36210
36390
|
continue;
|
|
36211
36391
|
}
|
|
36392
|
+
if (col.type === "WINDOW_COL") {
|
|
36393
|
+
values.push(row[col.alias] ?? "");
|
|
36394
|
+
continue;
|
|
36395
|
+
}
|
|
36212
36396
|
if (col.type === "PARENT_WILDCARD") {
|
|
36213
36397
|
for (const k of sortedParentKeys) {
|
|
36214
36398
|
values.push(row[k] !== void 0 ? row[k] : null);
|
|
@@ -36220,6 +36404,9 @@ function buildDistinctKeyBuilder(rows, columns) {
|
|
|
36220
36404
|
}
|
|
36221
36405
|
function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
|
|
36222
36406
|
if (orderBy.length === 0) return rows;
|
|
36407
|
+
return sortDecoratedRows(rows, orderBy, optionOrders, sortKinds).rows.map((item) => item.row);
|
|
36408
|
+
}
|
|
36409
|
+
function sortDecoratedRows(rows, orderBy, optionOrders, sortKinds) {
|
|
36223
36410
|
const keyMeta = orderBy.map(({ key }) => ({
|
|
36224
36411
|
orderMap: key.type === "FIELD_NAME" ? optionOrders?.get(key.name) : void 0,
|
|
36225
36412
|
sortKind: key.type === "FIELD_NAME" ? sortKinds?.get(key.name) : void 0
|
|
@@ -36238,14 +36425,16 @@ function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
|
|
|
36238
36425
|
};
|
|
36239
36426
|
})
|
|
36240
36427
|
}));
|
|
36241
|
-
|
|
36242
|
-
|
|
36243
|
-
|
|
36244
|
-
|
|
36245
|
-
|
|
36246
|
-
|
|
36247
|
-
|
|
36248
|
-
|
|
36428
|
+
const compare = (a, b) => compareDecoratedRows(a, b, orderBy, keyMeta);
|
|
36429
|
+
decorated.sort(compare);
|
|
36430
|
+
return { rows: decorated, compare };
|
|
36431
|
+
}
|
|
36432
|
+
function compareDecoratedRows(a, b, orderBy, keyMeta) {
|
|
36433
|
+
for (let i = 0; i < orderBy.length; i++) {
|
|
36434
|
+
const cmp = compareSortKeys(a.keys[i], b.keys[i], keyMeta[i]);
|
|
36435
|
+
if (cmp !== 0) return orderBy[i].direction === "ASC" ? cmp : -cmp;
|
|
36436
|
+
}
|
|
36437
|
+
return 0;
|
|
36249
36438
|
}
|
|
36250
36439
|
function compareSortKeys(a, b, meta3) {
|
|
36251
36440
|
if (meta3.orderMap) {
|
|
@@ -36290,6 +36479,38 @@ function minChoiceIndex(values, orderMap) {
|
|
|
36290
36479
|
}
|
|
36291
36480
|
return min;
|
|
36292
36481
|
}
|
|
36482
|
+
function applyWindow(rows, columns, optionOrders, sortKinds) {
|
|
36483
|
+
const windows = columns.filter((column) => column.type === "WINDOW_COL");
|
|
36484
|
+
if (rows.length === 0 || windows.length === 0) return rows;
|
|
36485
|
+
for (const window of windows) {
|
|
36486
|
+
const partitions = /* @__PURE__ */ new Map();
|
|
36487
|
+
for (const row of rows) {
|
|
36488
|
+
const key = JSON.stringify(window.partitionBy.map((ref) => resolveWindowField(row, ref)));
|
|
36489
|
+
const partition = partitions.get(key);
|
|
36490
|
+
if (partition) partition.push(row);
|
|
36491
|
+
else partitions.set(key, [row]);
|
|
36492
|
+
}
|
|
36493
|
+
for (const partition of partitions.values()) {
|
|
36494
|
+
const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds);
|
|
36495
|
+
const sorted = sortedResult.rows;
|
|
36496
|
+
let rank = 1;
|
|
36497
|
+
let denseRank = 1;
|
|
36498
|
+
for (let index = 0; index < sorted.length; index++) {
|
|
36499
|
+
if (index > 0 && sortedResult.compare(sorted[index - 1], sorted[index]) !== 0) {
|
|
36500
|
+
rank = index + 1;
|
|
36501
|
+
denseRank++;
|
|
36502
|
+
}
|
|
36503
|
+
const value = window.func === "ROW_NUMBER" ? index + 1 : window.func === "RANK" ? rank : denseRank;
|
|
36504
|
+
sorted[index].row[window.alias] = String(value);
|
|
36505
|
+
}
|
|
36506
|
+
}
|
|
36507
|
+
}
|
|
36508
|
+
return rows;
|
|
36509
|
+
}
|
|
36510
|
+
function resolveWindowField(row, ref) {
|
|
36511
|
+
const name = ref.tableAlias ? `${ref.tableAlias}.${ref.field}` : ref.field;
|
|
36512
|
+
return resolveFieldRef(row, name);
|
|
36513
|
+
}
|
|
36293
36514
|
function applyLimit(rows, limit, offset) {
|
|
36294
36515
|
const start = offset ?? 0;
|
|
36295
36516
|
if (limit === null) return rows.slice(start);
|
|
@@ -36380,6 +36601,12 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
|
|
|
36380
36601
|
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
36381
36602
|
break;
|
|
36382
36603
|
}
|
|
36604
|
+
case "WINDOW_COL": {
|
|
36605
|
+
const key = outputKeys?.[colIdx] ?? col.alias;
|
|
36606
|
+
out[key] = row[col.alias] ?? "";
|
|
36607
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
36608
|
+
break;
|
|
36609
|
+
}
|
|
36383
36610
|
}
|
|
36384
36611
|
}
|
|
36385
36612
|
return out;
|
|
@@ -36420,6 +36647,8 @@ function computeOutputKey(col, colIdx, defaultFieldKeys) {
|
|
|
36420
36647
|
return col.alias ?? stringFuncDefaultKey(col.expr);
|
|
36421
36648
|
case "SCALAR_SUBQUERY_COL":
|
|
36422
36649
|
return col.alias ?? "(subquery)";
|
|
36650
|
+
case "WINDOW_COL":
|
|
36651
|
+
return col.alias;
|
|
36423
36652
|
case "WILDCARD":
|
|
36424
36653
|
case "PARENT_WILDCARD":
|
|
36425
36654
|
throw new Error("internal: computeOutputKey received a wildcard column");
|
|
@@ -36534,6 +36763,7 @@ function runFullScan(input) {
|
|
|
36534
36763
|
rows = applyGroupBy(rows, stmt.groupBy, stmt.columns, aggregateSortKindResolver);
|
|
36535
36764
|
}
|
|
36536
36765
|
rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
|
|
36766
|
+
rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds);
|
|
36537
36767
|
if (stmt.distinct) {
|
|
36538
36768
|
rows = applyDistinct(rows, stmt.columns);
|
|
36539
36769
|
}
|
|
@@ -37478,6 +37708,11 @@ function validateNoFromColumns(stmt) {
|
|
|
37478
37708
|
throw new Error("ArgumentError: field reference is not allowed without FROM.");
|
|
37479
37709
|
}
|
|
37480
37710
|
break;
|
|
37711
|
+
case "WINDOW_COL":
|
|
37712
|
+
if (col.partitionBy.length > 0 || col.orderBy.length > 0) {
|
|
37713
|
+
throw new Error("ArgumentError: field reference is not allowed without FROM.");
|
|
37714
|
+
}
|
|
37715
|
+
break;
|
|
37481
37716
|
default:
|
|
37482
37717
|
throw new Error(`ArgumentError: ${col.type} is not supported without FROM.`);
|
|
37483
37718
|
}
|
|
@@ -37488,7 +37723,8 @@ function executeNoFromSelect(stmt) {
|
|
|
37488
37723
|
throw new Error("ArgumentError: JOIN/WHERE/GROUP BY/HAVING/ORDER BY/DISTINCT are not supported without FROM.");
|
|
37489
37724
|
}
|
|
37490
37725
|
validateNoFromColumns(stmt);
|
|
37491
|
-
const
|
|
37726
|
+
const windowed = applyWindow([{}], stmt.columns);
|
|
37727
|
+
const { rows: projected, columns } = project(windowed, stmt.columns);
|
|
37492
37728
|
const rows = applyLimit(projected, stmt.limit, stmt.offset);
|
|
37493
37729
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [] };
|
|
37494
37730
|
}
|
|
@@ -37912,6 +38148,8 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
|
|
|
37912
38148
|
meta3 = { sortKind: "number" };
|
|
37913
38149
|
} else if (column.type === "LITERAL_COL") {
|
|
37914
38150
|
meta3 = { sortKind: "string" };
|
|
38151
|
+
} else if (column.type === "WINDOW_COL") {
|
|
38152
|
+
meta3 = { sortKind: "number" };
|
|
37915
38153
|
}
|
|
37916
38154
|
if (meta3) inferred.set(output, meta3);
|
|
37917
38155
|
});
|
|
@@ -38497,7 +38735,10 @@ async function getSortKindMapByApp(appId, client, cacheContext) {
|
|
|
38497
38735
|
return map2;
|
|
38498
38736
|
}
|
|
38499
38737
|
async function buildOrderByMetaForSelect(stmt, client, cacheContext) {
|
|
38500
|
-
|
|
38738
|
+
const hasWindowOrderBy = stmt.columns.some(
|
|
38739
|
+
(column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
|
|
38740
|
+
);
|
|
38741
|
+
if (stmt.orderBy.length === 0 && !hasWindowOrderBy) {
|
|
38501
38742
|
return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map() };
|
|
38502
38743
|
}
|
|
38503
38744
|
const [optionOrders, sortKinds] = await Promise.all([
|
|
@@ -39959,6 +40200,8 @@ function collectFullScanReasons(stmt) {
|
|
|
39959
40200
|
r.push("DISTINCT \u3042\u308A");
|
|
39960
40201
|
if (stmt.columns.some((c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL"))
|
|
39961
40202
|
r.push("\u96C6\u8A08\u95A2\u6570\uFF08COUNT / SUM \u7B49\uFF09\u3042\u308A");
|
|
40203
|
+
if (stmt.columns.some((c) => c.type === "WINDOW_COL"))
|
|
40204
|
+
r.push("\u30A6\u30A3\u30F3\u30C9\u30A6\u95A2\u6570\u3042\u308A");
|
|
39962
40205
|
if (stmt.columns.some((c) => c.type === "SCALAR_SUBQUERY_COL"))
|
|
39963
40206
|
r.push("SELECT \u5217\u306B\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA");
|
|
39964
40207
|
if (whereRequiresJsEval(stmt.where))
|
|
@@ -42414,7 +42657,7 @@ Options:
|
|
|
42414
42657
|
-h, --help Show help
|
|
42415
42658
|
`);
|
|
42416
42659
|
}
|
|
42417
|
-
var SERVER_VERSION = true ? "2.
|
|
42660
|
+
var SERVER_VERSION = true ? "2.17.0" : "0.0.0-dev";
|
|
42418
42661
|
function createServer(args) {
|
|
42419
42662
|
const server = new McpServer({
|
|
42420
42663
|
name: "ksql-mcp",
|
package/dist-mcpb/ksql-mcp.mcpb
CHANGED
|
Binary file
|