@rex0220/kintone-sql-tools 3.42.0 → 3.44.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/README.md +0 -2
- package/dist-cli/ksql.js +1089 -790
- package/dist-engine/index.cjs +12 -12
- package/dist-engine/index.mjs +12 -12
- package/dist-engine/ksql-engine.umd.js +12 -12
- package/dist-engine/meta/bundle-baseline.json +10 -10
- package/dist-engine/meta/cjs.json +84 -46
- package/dist-engine/meta/esm.json +84 -46
- package/dist-engine/meta/umd.json +84 -46
- package/dist-mcp/ksql-mcp.js +886 -584
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -683,6 +683,7 @@ function arithLabel(node, topLevel = false) {
|
|
|
683
683
|
function fieldValueLabel(value) {
|
|
684
684
|
if (value.type === "FIELD") return value.tableAlias ? `${value.tableAlias}.${value.field}` : value.field;
|
|
685
685
|
if (value.type === "FUNC_FIELD") return stringFuncLabel(value.expr);
|
|
686
|
+
if (value.type === "AGG_FIELD") return aggregateOperandLabel(value.expr);
|
|
686
687
|
if (value.type === "ARITH_FIELD") return arithLabel(value.expr);
|
|
687
688
|
if (value.type === "GROUPING_FIELD") {
|
|
688
689
|
const field = value.ref.field;
|
|
@@ -736,6 +737,8 @@ function whereLabel(expr) {
|
|
|
736
737
|
}
|
|
737
738
|
function caseResultLabel(result) {
|
|
738
739
|
if (result.type === "ARRAY") return `[${result.elements.map((entry) => quote(entry.value)).join(",")}]`;
|
|
740
|
+
if (result.type === "AGG_REF") return aggregateSyntheticName(result.func, result.distinct, result.arg);
|
|
741
|
+
if (result.type === "AGG_ARITH") return aggregateOperandLabel(result);
|
|
739
742
|
if (result.type === "FIELD_REF" || result.type === "ARITH") return arithLabel(result);
|
|
740
743
|
return scalarValueLabel(result);
|
|
741
744
|
}
|
|
@@ -1038,8 +1041,9 @@ function needsSpaceBetween(prev, cur) {
|
|
|
1038
1041
|
return true;
|
|
1039
1042
|
}
|
|
1040
1043
|
var ParseError = class extends Error {
|
|
1041
|
-
constructor(
|
|
1042
|
-
super(`${
|
|
1044
|
+
constructor(rawMessage, token) {
|
|
1045
|
+
super(`${rawMessage}\uFF08\u4F4D\u7F6E ${token.pos}\u3001\u30C8\u30FC\u30AF\u30F3: \u300C${token.value}\u300D\uFF09`);
|
|
1046
|
+
this.rawMessage = rawMessage;
|
|
1043
1047
|
this.token = token;
|
|
1044
1048
|
this.name = "ParseError";
|
|
1045
1049
|
}
|
|
@@ -2078,8 +2082,17 @@ var Parser = class {
|
|
|
2078
2082
|
if (column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL") return true;
|
|
2079
2083
|
if (column.type === "STRFUNC_COL") return column.expr.args.some((arg) => this.stringFuncArgHasAggregate(arg));
|
|
2080
2084
|
if (column.type === "SCALAR_VALUE_COL") return this.scalarValueHasAggregate(column.expr);
|
|
2085
|
+
if (column.type === "CASE_COL") return this.nodeHasAggregate(column.expr);
|
|
2081
2086
|
return false;
|
|
2082
2087
|
}
|
|
2088
|
+
nodeHasAggregate(node) {
|
|
2089
|
+
if (node === null || typeof node !== "object") return false;
|
|
2090
|
+
if (Array.isArray(node)) return node.some((value2) => this.nodeHasAggregate(value2));
|
|
2091
|
+
const value = node;
|
|
2092
|
+
if (value["type"] === "AGG_REF" || value["type"] === "AGG_ARITH") return true;
|
|
2093
|
+
if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return false;
|
|
2094
|
+
return Object.values(value).some((child) => this.nodeHasAggregate(child));
|
|
2095
|
+
}
|
|
2083
2096
|
stringFuncArgHasAggregate(arg) {
|
|
2084
2097
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
2085
2098
|
return this.scalarValueHasAggregate(arg);
|
|
@@ -2090,11 +2103,7 @@ var Parser = class {
|
|
|
2090
2103
|
return this.scalarValueHasAggregate(expr.left) || this.scalarValueHasAggregate(expr.right);
|
|
2091
2104
|
}
|
|
2092
2105
|
if (expr.type === "CASE_WHEN") {
|
|
2093
|
-
|
|
2094
|
-
return results.some((result) => {
|
|
2095
|
-
if (result.type === "ARRAY" || result.type === "FIELD_REF" || result.type === "ARITH") return false;
|
|
2096
|
-
return this.scalarValueHasAggregate(result);
|
|
2097
|
-
});
|
|
2106
|
+
return this.nodeHasAggregate(expr);
|
|
2098
2107
|
}
|
|
2099
2108
|
return false;
|
|
2100
2109
|
}
|
|
@@ -2351,9 +2360,9 @@ var Parser = class {
|
|
|
2351
2360
|
this.expect("(" /* LPAREN */);
|
|
2352
2361
|
const condition = this.parseCaseCondition(allowGroupingCondition);
|
|
2353
2362
|
this.expect("," /* COMMA */);
|
|
2354
|
-
const thenResult = this.parseCaseResult();
|
|
2363
|
+
const thenResult = this.parseCaseResult(allowGroupingCondition);
|
|
2355
2364
|
this.expect("," /* COMMA */);
|
|
2356
|
-
const elseResult = this.parseCaseResult();
|
|
2365
|
+
const elseResult = this.parseCaseResult(allowGroupingCondition);
|
|
2357
2366
|
this.expect(")" /* RPAREN */);
|
|
2358
2367
|
return {
|
|
2359
2368
|
type: "CASE_WHEN",
|
|
@@ -2368,7 +2377,7 @@ var Parser = class {
|
|
|
2368
2377
|
this.advance();
|
|
2369
2378
|
const condition = this.parseCaseCondition(allowGroupingCondition);
|
|
2370
2379
|
this.expect("THEN" /* THEN */);
|
|
2371
|
-
const result = this.parseCaseResult();
|
|
2380
|
+
const result = this.parseCaseResult(allowGroupingCondition);
|
|
2372
2381
|
branches.push({ condition, result });
|
|
2373
2382
|
}
|
|
2374
2383
|
if (branches.length === 0) {
|
|
@@ -2376,7 +2385,7 @@ var Parser = class {
|
|
|
2376
2385
|
}
|
|
2377
2386
|
let elseResult = null;
|
|
2378
2387
|
if (this.consume("ELSE" /* ELSE */)) {
|
|
2379
|
-
elseResult = this.parseCaseResult();
|
|
2388
|
+
elseResult = this.parseCaseResult(allowGroupingCondition);
|
|
2380
2389
|
}
|
|
2381
2390
|
this.expect("END" /* END */);
|
|
2382
2391
|
return { type: "CASE_WHEN", branches, elseResult };
|
|
@@ -2386,7 +2395,7 @@ var Parser = class {
|
|
|
2386
2395
|
return this.parseWhereExpr("SELECT_CASE");
|
|
2387
2396
|
}
|
|
2388
2397
|
/** THEN / ELSE の結果値。`||` を含む場合だけ新スカラー文法へ渡す。 */
|
|
2389
|
-
parseCaseResult() {
|
|
2398
|
+
parseCaseResult(allowAggregateResult = false) {
|
|
2390
2399
|
const tok = this.peek();
|
|
2391
2400
|
if (this.insideAggregateArg > 0 && this.tryAggregateFunc() !== null) {
|
|
2392
2401
|
throw new ParseError("\u96C6\u8A08\u95A2\u6570\u306E\u5F15\u6570\u5185\u306B\u96C6\u8A08\u95A2\u6570\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
|
|
@@ -2394,6 +2403,10 @@ var Parser = class {
|
|
|
2394
2403
|
if (tok.kind === "[" /* LBRACKET */) {
|
|
2395
2404
|
return this.parseArrayLiteral();
|
|
2396
2405
|
}
|
|
2406
|
+
const aggregateFunc = this.tryAggregateFunc();
|
|
2407
|
+
if (aggregateFunc !== null && allowAggregateResult) {
|
|
2408
|
+
return this.continueAggArith(this.parseAggregateRef(aggregateFunc));
|
|
2409
|
+
}
|
|
2397
2410
|
if (this.hasTopLevelTokenBeforeValueEnd("||" /* CONCAT_OP */)) {
|
|
2398
2411
|
return this.parseScalarValueExpr({ allowAggregateArgs: true });
|
|
2399
2412
|
}
|
|
@@ -2425,16 +2438,33 @@ var Parser = class {
|
|
|
2425
2438
|
const byKind = PARSER_SCALAR_FUNCTION_TOKEN_MAP[this.peek().kind] ?? null;
|
|
2426
2439
|
if (byKind !== null && byKind !== "LEFT" && byKind !== "RIGHT") return byKind;
|
|
2427
2440
|
if (this.peek().kind === "IDENT" /* IDENT */) {
|
|
2428
|
-
const
|
|
2441
|
+
const nameToken = this.peek();
|
|
2442
|
+
const name = nameToken.value.toUpperCase();
|
|
2429
2443
|
if (PARSER_IDENT_SCALAR_FUNCTIONS.includes(name)) {
|
|
2430
2444
|
if (this.peekAt(1).kind === "(" /* LPAREN */) {
|
|
2431
2445
|
return name;
|
|
2432
2446
|
}
|
|
2433
2447
|
}
|
|
2448
|
+
if (this.peekAt(1).kind === "(" /* LPAREN */) {
|
|
2449
|
+
const hint = name === "DATE_SUB" ? "\u3002\u65E5\u4ED8\u3092\u6E1B\u7B97\u3059\u308B\u306B\u306F DATE_ADD \u306E\u52A0\u7B97\u5024\u3078\u8CA0\u6570\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044" : "";
|
|
2450
|
+
throw new ParseError(`\u300C${nameToken.value}\u300D\u3068\u3044\u3046\u95A2\u6570\u306F\u3042\u308A\u307E\u305B\u3093${hint}`, nameToken);
|
|
2451
|
+
}
|
|
2434
2452
|
}
|
|
2435
2453
|
return null;
|
|
2436
2454
|
}
|
|
2437
2455
|
parseStringFuncExpr() {
|
|
2456
|
+
const nameToken = this.peek();
|
|
2457
|
+
const func = this.tryStringFuncName();
|
|
2458
|
+
try {
|
|
2459
|
+
return this.parseStringFuncExprBody();
|
|
2460
|
+
} catch (error) {
|
|
2461
|
+
if (!(error instanceof ParseError)) throw error;
|
|
2462
|
+
const displayName = nameToken.value.toUpperCase();
|
|
2463
|
+
const guidance = func === "DATE_ADD" ? `${displayName} \u306E\u69CB\u6587\u306F DATE_ADD(\u5217, n, '\u5358\u4F4D') \u3067\u3059\u3002` : `${displayName} \u306E\u5F15\u6570\u69CB\u6587\u304C\u4E0D\u6B63\u3067\u3059\u3002`;
|
|
2464
|
+
throw new ParseError(`${guidance}${error.rawMessage}`, error.token);
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
parseStringFuncExprBody() {
|
|
2438
2468
|
const tokenKind = this.peek().kind;
|
|
2439
2469
|
const func = this.tryStringFuncName();
|
|
2440
2470
|
this.advance();
|
|
@@ -2900,8 +2930,19 @@ var Parser = class {
|
|
|
2900
2930
|
throw new ParseError("\u96C6\u8A08\u95A2\u6570\u306E\u5F15\u6570\u5185\u306B\u96C6\u8A08\u95A2\u6570\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", this.peek());
|
|
2901
2931
|
}
|
|
2902
2932
|
const ref = this.parseAggregateRef(aggFunc);
|
|
2933
|
+
if (this.isArithOp(this.peek().kind)) {
|
|
2934
|
+
return {
|
|
2935
|
+
type: "AGG_FIELD",
|
|
2936
|
+
expr: this.continueAggArith(ref)
|
|
2937
|
+
};
|
|
2938
|
+
}
|
|
2903
2939
|
const syntheticName = aggregateSyntheticName(ref.func, ref.distinct, ref.arg);
|
|
2904
|
-
return {
|
|
2940
|
+
return {
|
|
2941
|
+
type: "FIELD",
|
|
2942
|
+
tableAlias: null,
|
|
2943
|
+
field: syntheticName,
|
|
2944
|
+
...this.groupingFieldContext === "SELECT_CASE" || this.groupingFieldContext === "HAVING" ? { aggregateRef: ref } : {}
|
|
2945
|
+
};
|
|
2905
2946
|
}
|
|
2906
2947
|
if (this.peek().kind === "CASE" /* CASE */) {
|
|
2907
2948
|
const expr = this.parseCaseWhenExpr();
|
|
@@ -5100,6 +5141,9 @@ function convertField(field) {
|
|
|
5100
5141
|
`WHERE \u53E5\u306E\u95A2\u6570\uFF08${field.expr.func}\uFF09\u306F kintone \u30AF\u30A8\u30EA\u306B\u5909\u63DB\u3067\u304D\u307E\u305B\u3093`
|
|
5101
5142
|
);
|
|
5102
5143
|
}
|
|
5144
|
+
if (field.type === "AGG_FIELD") {
|
|
5145
|
+
throw new KintoneQueryError("HAVING \u53E5\u306E\u96C6\u8A08\u7B97\u8853\u5F0F\u306F kintone \u30AF\u30A8\u30EA\u306B\u5909\u63DB\u3067\u304D\u307E\u305B\u3093");
|
|
5146
|
+
}
|
|
5103
5147
|
if (field.type === "ARITH_FIELD") {
|
|
5104
5148
|
throw new KintoneQueryError("WHERE \u53E5\u306E\u7B97\u8853\u5F0F\u306F kintone \u30AF\u30A8\u30EA\u306B\u5909\u63DB\u3067\u304D\u307E\u305B\u3093");
|
|
5105
5149
|
}
|
|
@@ -5238,193 +5282,464 @@ var KintoneQueryError = class extends Error {
|
|
|
5238
5282
|
}
|
|
5239
5283
|
};
|
|
5240
5284
|
|
|
5241
|
-
// src/
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
if (stmt.columns.some(
|
|
5253
|
-
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "SCALAR_SUBQUERY_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr(c.expr) || c.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate(c.expr)
|
|
5254
|
-
)) return "FULL_SCAN";
|
|
5255
|
-
if (whereRequiresJsEval(stmt.where)) return "FULL_SCAN";
|
|
5256
|
-
if (stmt.orderBy.some((o) => o.key.type !== "FIELD_NAME")) return "FULL_SCAN";
|
|
5257
|
-
return "SIMPLE";
|
|
5285
|
+
// src/engine/groupingRowMeta.ts
|
|
5286
|
+
var groupingRowMetaKey = /* @__PURE__ */ Symbol("ksql.groupingRowMeta");
|
|
5287
|
+
var groupingRefCanonicalIds = /* @__PURE__ */ new WeakMap();
|
|
5288
|
+
function attachGroupingRowMeta(row, includedCanonicalIds) {
|
|
5289
|
+
Object.defineProperty(row, groupingRowMetaKey, {
|
|
5290
|
+
value: { includedCanonicalIds },
|
|
5291
|
+
enumerable: false,
|
|
5292
|
+
configurable: false,
|
|
5293
|
+
writable: false
|
|
5294
|
+
});
|
|
5295
|
+
return row;
|
|
5258
5296
|
}
|
|
5259
|
-
function
|
|
5260
|
-
|
|
5261
|
-
switch (where.type) {
|
|
5262
|
-
case "BOOLEAN":
|
|
5263
|
-
return true;
|
|
5264
|
-
case "BINARY":
|
|
5265
|
-
return isFunc(where.left) || where.right.type === "ARITH_VALUE" || where.right.type === "CASE_VALUE" || where.right.type === "SUBQUERY_IN_LIST" || where.right.type === "SCALAR_SUBQUERY" || isLike(where);
|
|
5266
|
-
case "NULL_CHECK":
|
|
5267
|
-
return isFunc(where.field);
|
|
5268
|
-
case "LOGICAL":
|
|
5269
|
-
return whereRequiresJsEval(where.left) || whereRequiresJsEval(where.right);
|
|
5270
|
-
case "NOT":
|
|
5271
|
-
case "GROUP":
|
|
5272
|
-
return whereRequiresJsEval(where.expr);
|
|
5273
|
-
case "EXISTS":
|
|
5274
|
-
return true;
|
|
5275
|
-
}
|
|
5297
|
+
function getGroupingRowMeta(row) {
|
|
5298
|
+
return row[groupingRowMetaKey];
|
|
5276
5299
|
}
|
|
5277
|
-
function
|
|
5278
|
-
return
|
|
5300
|
+
function readGroupingMembership(row) {
|
|
5301
|
+
return getGroupingRowMeta(row)?.includedCanonicalIds;
|
|
5279
5302
|
}
|
|
5280
|
-
function
|
|
5281
|
-
|
|
5282
|
-
const hasPagination = stmt.limit !== null || stmt.offset !== null;
|
|
5283
|
-
const shouldInjectDefaultOrder = hasPagination && stmt.orderBy.length === 0;
|
|
5284
|
-
if (stmt.where !== null) {
|
|
5285
|
-
queryParts.push(whereToKintone(stmt.where));
|
|
5286
|
-
}
|
|
5287
|
-
if (stmt.orderBy.length > 0) {
|
|
5288
|
-
const orderStr = stmt.orderBy.map(convertOrderBy).join(", ");
|
|
5289
|
-
queryParts.push(`order by ${orderStr}`);
|
|
5290
|
-
} else if (shouldInjectDefaultOrder) {
|
|
5291
|
-
queryParts.push("order by $id asc");
|
|
5292
|
-
}
|
|
5293
|
-
if (stmt.limit !== null) {
|
|
5294
|
-
queryParts.push(`limit ${stmt.limit}`);
|
|
5295
|
-
}
|
|
5296
|
-
if (stmt.offset !== null) {
|
|
5297
|
-
queryParts.push(`offset ${stmt.offset}`);
|
|
5298
|
-
}
|
|
5299
|
-
return {
|
|
5300
|
-
app: stmt.from.appId,
|
|
5301
|
-
query: queryParts.join(" "),
|
|
5302
|
-
fields: extractFields(stmt.columns),
|
|
5303
|
-
totalCount: false
|
|
5304
|
-
};
|
|
5303
|
+
function bindGroupingRefCanonicalId(ref, canonicalId) {
|
|
5304
|
+
groupingRefCanonicalIds.set(ref, canonicalId);
|
|
5305
5305
|
}
|
|
5306
|
-
function
|
|
5307
|
-
const
|
|
5308
|
-
if (
|
|
5309
|
-
|
|
5306
|
+
function evalGroupingRef(ref, row) {
|
|
5307
|
+
const membership = readGroupingMembership(row);
|
|
5308
|
+
if (!membership) {
|
|
5309
|
+
throw new Error("internal error: GROUPING() evaluation requires B65 grouping row membership.");
|
|
5310
5310
|
}
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
fields: []
|
|
5315
|
-
// 全件取得なので全フィールドを取得する
|
|
5316
|
-
};
|
|
5317
|
-
}
|
|
5318
|
-
function selectToFetchAllFields(stmt, targetTable, plainGroupByPlan) {
|
|
5319
|
-
const plan = collectRequiredFieldsByTable(stmt, plainGroupByPlan);
|
|
5320
|
-
const target = plan.get(targetTable);
|
|
5321
|
-
if (!target) return [];
|
|
5322
|
-
if (target.allFields) return [];
|
|
5323
|
-
const fields = new Set(target.fields);
|
|
5324
|
-
if (target.table.subtableCode) {
|
|
5325
|
-
fields.add(target.table.subtableCode);
|
|
5311
|
+
const canonicalId = groupingRefCanonicalIds.get(ref);
|
|
5312
|
+
if (!canonicalId) {
|
|
5313
|
+
throw new Error("internal error: GROUPING() reference was not resolved during B65 planning.");
|
|
5326
5314
|
}
|
|
5327
|
-
|
|
5328
|
-
return [...fields];
|
|
5315
|
+
return membership.has(canonicalId) ? "0" : "1";
|
|
5329
5316
|
}
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5317
|
+
|
|
5318
|
+
// src/core/groupingValidation.ts
|
|
5319
|
+
var enforceGroupingPlanningCandidateLimits = (facts) => {
|
|
5320
|
+
if (facts.expandedSetCount > B65_MAX_GROUPING_SETS) {
|
|
5321
|
+
throw new Error(
|
|
5322
|
+
`ArgumentError: B65 expanded grouping set count ${facts.expandedSetCount} exceeds limit ${B65_MAX_GROUPING_SETS} (reason=GROUPING_SET_LIMIT_EXCEEDED).`
|
|
5323
|
+
);
|
|
5334
5324
|
}
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
(c) => c.type === "WILDCARD" || c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "CASE_COL" || c.type === "SCALAR_SUBQUERY_COL"
|
|
5340
|
-
);
|
|
5341
|
-
if (hasWildcard) return [];
|
|
5342
|
-
const fields = [];
|
|
5343
|
-
for (const col of columns) {
|
|
5344
|
-
if (col.type === "FIELD") {
|
|
5345
|
-
fields.push(normalizeSimpleFieldRef(col.field));
|
|
5346
|
-
} else if (col.type === "ARITH_COL") {
|
|
5347
|
-
collectArithNode(col.expr, fields);
|
|
5348
|
-
} else if (col.type === "STRFUNC_COL") {
|
|
5349
|
-
collectStringFuncFields(col.expr, fields);
|
|
5350
|
-
} else if (col.type === "SCALAR_VALUE_COL") {
|
|
5351
|
-
collectScalarValueFields(col.expr, fields);
|
|
5352
|
-
}
|
|
5325
|
+
if (facts.canonicalItemCount > B65_MAX_GROUPING_ITEMS) {
|
|
5326
|
+
throw new Error(
|
|
5327
|
+
`ArgumentError: B65 canonical grouping item count ${facts.canonicalItemCount} exceeds limit ${B65_MAX_GROUPING_ITEMS} (reason=GROUPING_ITEM_LIMIT_EXCEEDED).`
|
|
5328
|
+
);
|
|
5353
5329
|
}
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
collectArithNode(expr.left, out);
|
|
5358
|
-
collectArithNode(expr.right, out);
|
|
5359
|
-
}
|
|
5360
|
-
function collectArithNode(node, out) {
|
|
5361
|
-
if (node.type === "VARIABLE") throw new Error(
|
|
5362
|
-
`InternalError: unresolved arithmetic variable @${node.name} reached SELECT field collection.`
|
|
5363
|
-
);
|
|
5364
|
-
if (node.type === "FIELD_REF") out.push(normalizeSimpleFieldRef(node.field));
|
|
5365
|
-
else if (node.type === "ARITH") collectArithFields(node, out);
|
|
5366
|
-
else if (node.type === "STRING_FUNC") collectStringFuncFields(node, out);
|
|
5367
|
-
}
|
|
5368
|
-
function normalizeSimpleFieldRef(field) {
|
|
5369
|
-
const dot = field.indexOf(".");
|
|
5370
|
-
if (dot <= 0) return field;
|
|
5371
|
-
const unqualified = field.slice(dot + 1);
|
|
5372
|
-
return unqualified || field;
|
|
5330
|
+
};
|
|
5331
|
+
function displayField(field) {
|
|
5332
|
+
return field.tableAlias ? `${field.tableAlias}.${field.field}` : field.field;
|
|
5373
5333
|
}
|
|
5374
|
-
function
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
}
|
|
5334
|
+
function refFromName(name) {
|
|
5335
|
+
const dot = name.indexOf(".");
|
|
5336
|
+
return dot > 0 ? { type: "FIELD", tableAlias: name.slice(0, dot), field: name.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field: name };
|
|
5378
5337
|
}
|
|
5379
|
-
function
|
|
5380
|
-
if (
|
|
5381
|
-
|
|
5338
|
+
function collectGroupingRefs(node, out) {
|
|
5339
|
+
if (node === null || typeof node !== "object") return;
|
|
5340
|
+
if (Array.isArray(node)) {
|
|
5341
|
+
node.forEach((item) => collectGroupingRefs(item, out));
|
|
5382
5342
|
return;
|
|
5383
5343
|
}
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
out.push(normalizeSimpleFieldRef(expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field));
|
|
5344
|
+
const value = node;
|
|
5345
|
+
if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return;
|
|
5346
|
+
if (value["type"] === "GROUPING_REF") {
|
|
5347
|
+
out.push(value);
|
|
5389
5348
|
return;
|
|
5390
5349
|
}
|
|
5391
|
-
|
|
5392
|
-
|
|
5350
|
+
Object.values(value).forEach((item) => collectGroupingRefs(item, out));
|
|
5351
|
+
}
|
|
5352
|
+
function collectAggregateArgumentGroupingRefs(node, out) {
|
|
5353
|
+
if (node === null || typeof node !== "object") return;
|
|
5354
|
+
if (Array.isArray(node)) {
|
|
5355
|
+
node.forEach((item) => collectAggregateArgumentGroupingRefs(item, out));
|
|
5393
5356
|
return;
|
|
5394
5357
|
}
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5358
|
+
const value = node;
|
|
5359
|
+
if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return;
|
|
5360
|
+
if (value["type"] === "AGGREGATE" || value["type"] === "AGG_REF") {
|
|
5361
|
+
collectGroupingRefs(value["arg"], out);
|
|
5398
5362
|
return;
|
|
5399
5363
|
}
|
|
5400
|
-
|
|
5401
|
-
for (const branch of expr.branches) collectCaseResultScalarFields(branch.result, out);
|
|
5402
|
-
if (expr.elseResult) collectCaseResultScalarFields(expr.elseResult, out);
|
|
5403
|
-
}
|
|
5364
|
+
Object.values(value).forEach((item) => collectAggregateArgumentGroupingRefs(item, out));
|
|
5404
5365
|
}
|
|
5405
|
-
function
|
|
5406
|
-
if (
|
|
5407
|
-
if (
|
|
5408
|
-
|
|
5366
|
+
function collectNonAggregateFieldRefs(node, out) {
|
|
5367
|
+
if (node === null || typeof node !== "object") return;
|
|
5368
|
+
if (Array.isArray(node)) {
|
|
5369
|
+
node.forEach((item) => collectNonAggregateFieldRefs(item, out));
|
|
5409
5370
|
return;
|
|
5410
5371
|
}
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
if (
|
|
5415
|
-
|
|
5372
|
+
const value = node;
|
|
5373
|
+
const type = value["type"];
|
|
5374
|
+
if (type === "SELECT" || type === "SCALAR_SUBQUERY" || type === "GROUPING_REF" || type === "AGG_REF" || type === "AGG_ARITH") return;
|
|
5375
|
+
if (type === "FIELD" && value["aggregateRef"] !== void 0) return;
|
|
5376
|
+
if (type === "FIELD" && typeof value["field"] === "string") {
|
|
5377
|
+
out.push({
|
|
5378
|
+
type: "FIELD",
|
|
5379
|
+
tableAlias: typeof value["tableAlias"] === "string" ? value["tableAlias"] : null,
|
|
5380
|
+
field: value["field"]
|
|
5381
|
+
});
|
|
5416
5382
|
return;
|
|
5417
5383
|
}
|
|
5418
|
-
if (
|
|
5419
|
-
|
|
5420
|
-
|
|
5384
|
+
if (type === "FIELD_REF" && typeof value["field"] === "string") {
|
|
5385
|
+
out.push(refFromName(value["field"]));
|
|
5386
|
+
return;
|
|
5421
5387
|
}
|
|
5388
|
+
Object.values(value).forEach((item) => collectNonAggregateFieldRefs(item, out));
|
|
5422
5389
|
}
|
|
5423
|
-
function
|
|
5424
|
-
if (node
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5390
|
+
function containsAggregate2(node) {
|
|
5391
|
+
if (node === null || typeof node !== "object") return false;
|
|
5392
|
+
if (Array.isArray(node)) return node.some(containsAggregate2);
|
|
5393
|
+
const value = node;
|
|
5394
|
+
if (value["type"] === "AGG_REF" || value["type"] === "AGG_ARITH") return true;
|
|
5395
|
+
if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return false;
|
|
5396
|
+
return Object.values(value).some(containsAggregate2);
|
|
5397
|
+
}
|
|
5398
|
+
function isAggregateMaterializedAlias(column) {
|
|
5399
|
+
if (!("alias" in column) || column.alias === null) return false;
|
|
5400
|
+
if (column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL") return true;
|
|
5401
|
+
if (column.type === "STRFUNC_COL" || column.type === "SCALAR_VALUE_COL" || column.type === "CASE_COL") {
|
|
5402
|
+
return containsAggregate2(column);
|
|
5403
|
+
}
|
|
5404
|
+
return false;
|
|
5405
|
+
}
|
|
5406
|
+
function outputAliases(columns) {
|
|
5407
|
+
return new Set(columns.flatMap(
|
|
5408
|
+
(column) => "alias" in column && typeof column.alias === "string" ? [column.alias] : []
|
|
5409
|
+
));
|
|
5410
|
+
}
|
|
5411
|
+
function isAggregateSyntheticReference(ref) {
|
|
5412
|
+
return ref.tableAlias === null && /^(COUNT|SUM|AVG|MAX|MIN|GROUP_CONCAT|STDDEV_POP|STDDEV_SAMP|VAR_POP|VAR_SAMP|MEDIAN|MODE)\(/.test(ref.field);
|
|
5413
|
+
}
|
|
5414
|
+
function validateGroupingRefMembership(ref, resolve2, canonicalItems) {
|
|
5415
|
+
const resolved = resolve2(ref.field);
|
|
5416
|
+
if (!resolved.physical) {
|
|
5417
|
+
throw new Error(`ArgumentError: B65 grouping reference ${displayField(ref.field)} must resolve to a physical APP field.`);
|
|
5418
|
+
}
|
|
5419
|
+
if (!canonicalItems.has(resolved.canonicalId)) {
|
|
5420
|
+
throw new Error(
|
|
5421
|
+
`ArgumentError: B65 GROUPING argument ${displayField(ref.field)} is not present in grouping allItems (reason=B65_GROUPING_ARG_NOT_ITEM).`
|
|
5422
|
+
);
|
|
5423
|
+
}
|
|
5424
|
+
bindGroupingRefCanonicalId(ref, resolved.canonicalId);
|
|
5425
|
+
}
|
|
5426
|
+
function validateDependency(ref, resolve2, canonicalItems, context) {
|
|
5427
|
+
const resolved = resolve2(ref);
|
|
5428
|
+
if (!resolved.physical || !canonicalItems.has(resolved.canonicalId)) {
|
|
5429
|
+
throw new Error(
|
|
5430
|
+
`ArgumentError: B65 non-aggregate field ${displayField(ref)} in ${context} is not a grouping item (reason=B65_NON_GROUPED_DEPENDENCY).`
|
|
5431
|
+
);
|
|
5432
|
+
}
|
|
5433
|
+
}
|
|
5434
|
+
function keyDependencies(key) {
|
|
5435
|
+
if (key.type === "GROUPING_KEY") return [];
|
|
5436
|
+
if (key.type === "FIELD_NAME") return [refFromName(key.name)];
|
|
5437
|
+
const refs = [];
|
|
5438
|
+
collectNonAggregateFieldRefs(key, refs);
|
|
5439
|
+
return refs;
|
|
5440
|
+
}
|
|
5441
|
+
function validateGroupingStatic(stmt) {
|
|
5442
|
+
const normalized = normalizeGroupingSpec(stmt);
|
|
5443
|
+
const groupingRefs = [];
|
|
5444
|
+
for (const column of stmt.columns) {
|
|
5445
|
+
if (column.type !== "WINDOW_COL") collectGroupingRefs(column, groupingRefs);
|
|
5446
|
+
}
|
|
5447
|
+
collectGroupingRefs(stmt.having, groupingRefs);
|
|
5448
|
+
collectGroupingRefs(stmt.orderBy, groupingRefs);
|
|
5449
|
+
const forbiddenGroupingRefs = [];
|
|
5450
|
+
collectGroupingRefs(stmt.where, forbiddenGroupingRefs);
|
|
5451
|
+
collectGroupingRefs(stmt.joins, forbiddenGroupingRefs);
|
|
5452
|
+
collectAggregateArgumentGroupingRefs(stmt.columns, forbiddenGroupingRefs);
|
|
5453
|
+
collectAggregateArgumentGroupingRefs(stmt.having, forbiddenGroupingRefs);
|
|
5454
|
+
for (const column of stmt.columns) {
|
|
5455
|
+
if (column.type === "WINDOW_COL") collectGroupingRefs(column, forbiddenGroupingRefs);
|
|
5456
|
+
}
|
|
5457
|
+
if (forbiddenGroupingRefs.length > 0) {
|
|
5458
|
+
throw new Error(
|
|
5459
|
+
"ArgumentError: B65 GROUPING() is not allowed in WHERE, JOIN, window, aggregate arguments, or DML expressions."
|
|
5460
|
+
);
|
|
5461
|
+
}
|
|
5462
|
+
if (normalized.type !== "GROUPING_SETS") {
|
|
5463
|
+
if (groupingRefs.length > 0) {
|
|
5464
|
+
throw new Error("ArgumentError: B65 GROUPING() requires GROUP BY ROLLUP or GROUPING SETS.");
|
|
5465
|
+
}
|
|
5466
|
+
return;
|
|
5467
|
+
}
|
|
5468
|
+
if (stmt.orderMode === "KINTONE_NATIVE") {
|
|
5469
|
+
throw new Error("ArgumentError: B65 KORDER BY is not supported in Phase1.");
|
|
5470
|
+
}
|
|
5471
|
+
if (stmt.columns.some((column) => column.type === "WINDOW_COL")) {
|
|
5472
|
+
throw new Error("ArgumentError: B65 window functions are not supported in Phase1.");
|
|
5473
|
+
}
|
|
5474
|
+
if (stmt.columns.some(
|
|
5475
|
+
(column) => column.type === "WILDCARD" || column.type === "PARENT_WILDCARD"
|
|
5476
|
+
)) {
|
|
5477
|
+
throw new Error("ArgumentError: B65 wildcard projection is not supported in Phase1.");
|
|
5478
|
+
}
|
|
5479
|
+
}
|
|
5480
|
+
function validateGroupingPlanning(stmt, resolve2, planningGuardHook = () => void 0) {
|
|
5481
|
+
validateGroupingStatic(stmt);
|
|
5482
|
+
const normalized = normalizeGroupingSpec(stmt);
|
|
5483
|
+
const groupingRefs = [];
|
|
5484
|
+
for (const column of stmt.columns) {
|
|
5485
|
+
if (column.type !== "WINDOW_COL") collectGroupingRefs(column, groupingRefs);
|
|
5486
|
+
}
|
|
5487
|
+
collectGroupingRefs(stmt.having, groupingRefs);
|
|
5488
|
+
collectGroupingRefs(stmt.orderBy, groupingRefs);
|
|
5489
|
+
if (normalized.type !== "GROUPING_SETS") {
|
|
5490
|
+
return null;
|
|
5491
|
+
}
|
|
5492
|
+
const resolvedSpec = resolveGroupingSpec(stmt, resolve2);
|
|
5493
|
+
const canonicalItems = /* @__PURE__ */ new Set();
|
|
5494
|
+
const resolvedItems = [];
|
|
5495
|
+
for (const item of resolvedSpec.allItems) {
|
|
5496
|
+
const resolved = item;
|
|
5497
|
+
if (!resolved.physical) {
|
|
5498
|
+
throw new Error(`ArgumentError: B65 grouping item ${displayField(item.field)} must resolve to a physical APP field.`);
|
|
5499
|
+
}
|
|
5500
|
+
if (!canonicalItems.has(resolved.canonicalId)) {
|
|
5501
|
+
canonicalItems.add(resolved.canonicalId);
|
|
5502
|
+
resolvedItems.push(resolved);
|
|
5503
|
+
}
|
|
5504
|
+
}
|
|
5505
|
+
planningGuardHook({
|
|
5506
|
+
expandedSetCount: normalized.sets.length,
|
|
5507
|
+
canonicalItemCount: canonicalItems.size
|
|
5508
|
+
});
|
|
5509
|
+
for (const ref of groupingRefs) {
|
|
5510
|
+
validateGroupingRefMembership(ref, resolve2, canonicalItems);
|
|
5511
|
+
}
|
|
5512
|
+
const aliases = outputAliases(stmt.columns);
|
|
5513
|
+
for (const column of stmt.columns) {
|
|
5514
|
+
if (column.type === "GROUPING_COL" || column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL" || column.type === "LITERAL_COL" || column.type === "VARIABLE_COL" || column.type === "SCALAR_SUBQUERY_COL") continue;
|
|
5515
|
+
const refs = [];
|
|
5516
|
+
if (column.type === "FIELD") refs.push(refFromName(column.field));
|
|
5517
|
+
else collectNonAggregateFieldRefs(column, refs);
|
|
5518
|
+
for (const ref of refs) validateDependency(ref, resolve2, canonicalItems, "SELECT");
|
|
5519
|
+
}
|
|
5520
|
+
if (stmt.having) {
|
|
5521
|
+
const refs = [];
|
|
5522
|
+
collectNonAggregateFieldRefs(stmt.having, refs);
|
|
5523
|
+
for (const ref of refs) {
|
|
5524
|
+
if (ref.tableAlias === null && aliases.has(ref.field) || isAggregateSyntheticReference(ref)) continue;
|
|
5525
|
+
validateDependency(ref, resolve2, canonicalItems, "HAVING");
|
|
5526
|
+
}
|
|
5527
|
+
}
|
|
5528
|
+
for (const order of stmt.orderBy) {
|
|
5529
|
+
for (const ref of keyDependencies(order.key)) {
|
|
5530
|
+
if (ref.tableAlias === null && aliases.has(ref.field) || isAggregateSyntheticReference(ref)) continue;
|
|
5531
|
+
validateDependency(ref, resolve2, canonicalItems, "ORDER BY");
|
|
5532
|
+
}
|
|
5533
|
+
}
|
|
5534
|
+
const collisionKeys = /* @__PURE__ */ new Set();
|
|
5535
|
+
for (const item of resolvedItems) {
|
|
5536
|
+
collisionKeys.add(item.directKey);
|
|
5537
|
+
if (item.unqualifiedBridgeKey !== null) collisionKeys.add(item.unqualifiedBridgeKey);
|
|
5538
|
+
}
|
|
5539
|
+
for (const column of stmt.columns) {
|
|
5540
|
+
if (!isAggregateMaterializedAlias(column)) continue;
|
|
5541
|
+
const alias = "alias" in column ? column.alias : null;
|
|
5542
|
+
if (alias === null) continue;
|
|
5543
|
+
if (collisionKeys.has(alias)) {
|
|
5544
|
+
throw new Error(
|
|
5545
|
+
`ArgumentError: B65 aggregate alias ${alias} collides with a grouping runtime key (reason=B65_AGGREGATE_ALIAS_COLLISION).`
|
|
5546
|
+
);
|
|
5547
|
+
}
|
|
5548
|
+
}
|
|
5549
|
+
return resolvedSpec;
|
|
5550
|
+
}
|
|
5551
|
+
|
|
5552
|
+
// src/converter/selectToKintone.ts
|
|
5553
|
+
function hasWindowColumns(columns) {
|
|
5554
|
+
return columns.some((column) => column.type === "WINDOW_COL");
|
|
5555
|
+
}
|
|
5556
|
+
function resolveSelectMode(stmt) {
|
|
5557
|
+
if (stmt.from.subtableCode) return "FULL_SCAN";
|
|
5558
|
+
if (stmt.joins.some((j) => j.table.subtableCode)) return "FULL_SCAN";
|
|
5559
|
+
if (stmt.joins.length > 0) return "FULL_SCAN";
|
|
5560
|
+
if (normalizeGroupingSpec(stmt).type !== "NONE") return "FULL_SCAN";
|
|
5561
|
+
if (stmt.distinct) return "FULL_SCAN";
|
|
5562
|
+
if (hasWindowColumns(stmt.columns)) return "FULL_SCAN";
|
|
5563
|
+
if (stmt.columns.some(
|
|
5564
|
+
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "SCALAR_SUBQUERY_COL" || c.type === "CASE_COL" && containsAggregate2(c.expr) || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr(c.expr) || c.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate(c.expr)
|
|
5565
|
+
)) return "FULL_SCAN";
|
|
5566
|
+
if (whereRequiresJsEval(stmt.where)) return "FULL_SCAN";
|
|
5567
|
+
if (stmt.orderBy.some((o) => o.key.type !== "FIELD_NAME")) return "FULL_SCAN";
|
|
5568
|
+
return "SIMPLE";
|
|
5569
|
+
}
|
|
5570
|
+
function whereRequiresJsEval(where) {
|
|
5571
|
+
if (where === null) return false;
|
|
5572
|
+
switch (where.type) {
|
|
5573
|
+
case "BOOLEAN":
|
|
5574
|
+
return true;
|
|
5575
|
+
case "BINARY":
|
|
5576
|
+
return isFunc(where.left) || where.right.type === "ARITH_VALUE" || where.right.type === "CASE_VALUE" || where.right.type === "SUBQUERY_IN_LIST" || where.right.type === "SCALAR_SUBQUERY" || isLike(where);
|
|
5577
|
+
case "NULL_CHECK":
|
|
5578
|
+
return isFunc(where.field);
|
|
5579
|
+
case "LOGICAL":
|
|
5580
|
+
return whereRequiresJsEval(where.left) || whereRequiresJsEval(where.right);
|
|
5581
|
+
case "NOT":
|
|
5582
|
+
case "GROUP":
|
|
5583
|
+
return whereRequiresJsEval(where.expr);
|
|
5584
|
+
case "EXISTS":
|
|
5585
|
+
return true;
|
|
5586
|
+
}
|
|
5587
|
+
}
|
|
5588
|
+
function isFunc(fv) {
|
|
5589
|
+
return fv.type === "FUNC_FIELD" || fv.type === "ARITH_FIELD" || fv.type === "CASE_FIELD";
|
|
5590
|
+
}
|
|
5591
|
+
function selectToKintoneParams(stmt) {
|
|
5592
|
+
const queryParts = [];
|
|
5593
|
+
const hasPagination = stmt.limit !== null || stmt.offset !== null;
|
|
5594
|
+
const shouldInjectDefaultOrder = hasPagination && stmt.orderBy.length === 0;
|
|
5595
|
+
if (stmt.where !== null) {
|
|
5596
|
+
queryParts.push(whereToKintone(stmt.where));
|
|
5597
|
+
}
|
|
5598
|
+
if (stmt.orderBy.length > 0) {
|
|
5599
|
+
const orderStr = stmt.orderBy.map(convertOrderBy).join(", ");
|
|
5600
|
+
queryParts.push(`order by ${orderStr}`);
|
|
5601
|
+
} else if (shouldInjectDefaultOrder) {
|
|
5602
|
+
queryParts.push("order by $id asc");
|
|
5603
|
+
}
|
|
5604
|
+
if (stmt.limit !== null) {
|
|
5605
|
+
queryParts.push(`limit ${stmt.limit}`);
|
|
5606
|
+
}
|
|
5607
|
+
if (stmt.offset !== null) {
|
|
5608
|
+
queryParts.push(`offset ${stmt.offset}`);
|
|
5609
|
+
}
|
|
5610
|
+
return {
|
|
5611
|
+
app: stmt.from.appId,
|
|
5612
|
+
query: queryParts.join(" "),
|
|
5613
|
+
fields: extractFields(stmt.columns),
|
|
5614
|
+
totalCount: false
|
|
5615
|
+
};
|
|
5616
|
+
}
|
|
5617
|
+
function selectToFetchAllParams(stmt, appId) {
|
|
5618
|
+
const queryParts = [];
|
|
5619
|
+
if (stmt.where !== null && stmt.joins.length === 0 && !whereRequiresJsEval(stmt.where)) {
|
|
5620
|
+
queryParts.push(whereToKintone(stmt.where));
|
|
5621
|
+
}
|
|
5622
|
+
return {
|
|
5623
|
+
app: appId,
|
|
5624
|
+
query: queryParts.join(" "),
|
|
5625
|
+
fields: []
|
|
5626
|
+
// 全件取得なので全フィールドを取得する
|
|
5627
|
+
};
|
|
5628
|
+
}
|
|
5629
|
+
function selectToFetchAllFields(stmt, targetTable, plainGroupByPlan) {
|
|
5630
|
+
const plan = collectRequiredFieldsByTable(stmt, plainGroupByPlan);
|
|
5631
|
+
const target = plan.get(targetTable);
|
|
5632
|
+
if (!target) return [];
|
|
5633
|
+
if (target.allFields) return [];
|
|
5634
|
+
const fields = new Set(target.fields);
|
|
5635
|
+
if (target.table.subtableCode) {
|
|
5636
|
+
fields.add(target.table.subtableCode);
|
|
5637
|
+
}
|
|
5638
|
+
if (fields.size === 0) fields.add("$id");
|
|
5639
|
+
return [...fields];
|
|
5640
|
+
}
|
|
5641
|
+
function convertOrderBy(item) {
|
|
5642
|
+
const dir = item.direction === "ASC" ? "asc" : "desc";
|
|
5643
|
+
if (item.key.type !== "FIELD_NAME") {
|
|
5644
|
+
throw new Error("ORDER BY \u5F0F\u306F kintone \u30AF\u30A8\u30EA\u306B\u5909\u63DB\u3067\u304D\u307E\u305B\u3093\uFF08FULL_SCAN \u304C\u5FC5\u8981\u3067\u3059\uFF09");
|
|
5645
|
+
}
|
|
5646
|
+
return `${item.key.name} ${dir}`;
|
|
5647
|
+
}
|
|
5648
|
+
function extractFields(columns) {
|
|
5649
|
+
const hasWildcard = columns.some(
|
|
5650
|
+
(c) => c.type === "WILDCARD" || c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "CASE_COL" || c.type === "SCALAR_SUBQUERY_COL"
|
|
5651
|
+
);
|
|
5652
|
+
if (hasWildcard) return [];
|
|
5653
|
+
const fields = [];
|
|
5654
|
+
for (const col of columns) {
|
|
5655
|
+
if (col.type === "FIELD") {
|
|
5656
|
+
fields.push(normalizeSimpleFieldRef(col.field));
|
|
5657
|
+
} else if (col.type === "ARITH_COL") {
|
|
5658
|
+
collectArithNode(col.expr, fields);
|
|
5659
|
+
} else if (col.type === "STRFUNC_COL") {
|
|
5660
|
+
collectStringFuncFields(col.expr, fields);
|
|
5661
|
+
} else if (col.type === "SCALAR_VALUE_COL") {
|
|
5662
|
+
collectScalarValueFields(col.expr, fields);
|
|
5663
|
+
}
|
|
5664
|
+
}
|
|
5665
|
+
return [...new Set(fields)];
|
|
5666
|
+
}
|
|
5667
|
+
function collectArithFields(expr, out) {
|
|
5668
|
+
collectArithNode(expr.left, out);
|
|
5669
|
+
collectArithNode(expr.right, out);
|
|
5670
|
+
}
|
|
5671
|
+
function collectArithNode(node, out) {
|
|
5672
|
+
if (node.type === "VARIABLE") throw new Error(
|
|
5673
|
+
`InternalError: unresolved arithmetic variable @${node.name} reached SELECT field collection.`
|
|
5674
|
+
);
|
|
5675
|
+
if (node.type === "FIELD_REF") out.push(normalizeSimpleFieldRef(node.field));
|
|
5676
|
+
else if (node.type === "ARITH") collectArithFields(node, out);
|
|
5677
|
+
else if (node.type === "STRING_FUNC") collectStringFuncFields(node, out);
|
|
5678
|
+
}
|
|
5679
|
+
function normalizeSimpleFieldRef(field) {
|
|
5680
|
+
const dot = field.indexOf(".");
|
|
5681
|
+
if (dot <= 0) return field;
|
|
5682
|
+
const unqualified = field.slice(dot + 1);
|
|
5683
|
+
return unqualified || field;
|
|
5684
|
+
}
|
|
5685
|
+
function collectStringFuncFields(expr, out) {
|
|
5686
|
+
for (const arg of expr.args) {
|
|
5687
|
+
collectStringFuncArgFields(arg, out);
|
|
5688
|
+
}
|
|
5689
|
+
}
|
|
5690
|
+
function collectStringFuncArgFields(arg, out) {
|
|
5691
|
+
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
|
|
5692
|
+
collectAggOperandFields(arg, out);
|
|
5693
|
+
return;
|
|
5694
|
+
}
|
|
5695
|
+
collectScalarValueFields(arg, out);
|
|
5696
|
+
}
|
|
5697
|
+
function collectScalarValueFields(expr, out) {
|
|
5698
|
+
if (expr.type === "FIELD") {
|
|
5699
|
+
out.push(normalizeSimpleFieldRef(expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field));
|
|
5700
|
+
return;
|
|
5701
|
+
}
|
|
5702
|
+
if (expr.type === "STRING_FUNC") {
|
|
5703
|
+
collectStringFuncFields(expr, out);
|
|
5704
|
+
return;
|
|
5705
|
+
}
|
|
5706
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
5707
|
+
collectScalarValueFields(expr.left, out);
|
|
5708
|
+
collectScalarValueFields(expr.right, out);
|
|
5709
|
+
return;
|
|
5710
|
+
}
|
|
5711
|
+
if (expr.type === "CASE_WHEN") {
|
|
5712
|
+
for (const branch of expr.branches) collectCaseResultScalarFields(branch.result, out);
|
|
5713
|
+
if (expr.elseResult) collectCaseResultScalarFields(expr.elseResult, out);
|
|
5714
|
+
}
|
|
5715
|
+
}
|
|
5716
|
+
function collectCaseResultScalarFields(result, out) {
|
|
5717
|
+
if (result.type === "ARRAY") return;
|
|
5718
|
+
if (result.type === "AGG_REF" || result.type === "AGG_ARITH") {
|
|
5719
|
+
collectAggOperandFields(result, out);
|
|
5720
|
+
return;
|
|
5721
|
+
}
|
|
5722
|
+
if (result.type === "FIELD_REF" || result.type === "ARITH") {
|
|
5723
|
+
collectArithNode(result, out);
|
|
5724
|
+
return;
|
|
5725
|
+
}
|
|
5726
|
+
collectScalarValueFields(result, out);
|
|
5727
|
+
}
|
|
5728
|
+
function collectAggOperandFields(node, out) {
|
|
5729
|
+
if (node.type === "AGG_REF") {
|
|
5730
|
+
if (node.arg.type !== "WILDCARD") collectAggregateArgFields(node.arg, out);
|
|
5731
|
+
return;
|
|
5732
|
+
}
|
|
5733
|
+
if (node.type === "AGG_ARITH") {
|
|
5734
|
+
collectAggOperandFields(node.left, out);
|
|
5735
|
+
collectAggOperandFields(node.right, out);
|
|
5736
|
+
}
|
|
5737
|
+
}
|
|
5738
|
+
function collectAggregateArgFields(node, out) {
|
|
5739
|
+
if (node.type === "FIELD_REF" || node.type === "ARITH") collectArithNode(node, out);
|
|
5740
|
+
else collectScalarValueFields(node, out);
|
|
5741
|
+
}
|
|
5742
|
+
function hasAggregateInStringFuncExpr(expr) {
|
|
5428
5743
|
return expr.args.some((arg) => {
|
|
5429
5744
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
5430
5745
|
return scalarValueHasAggregate(arg);
|
|
@@ -5441,6 +5756,7 @@ function scalarValueHasAggregate(expr) {
|
|
|
5441
5756
|
return false;
|
|
5442
5757
|
}
|
|
5443
5758
|
function caseResultHasAggregate(result) {
|
|
5759
|
+
if (result.type === "AGG_REF" || result.type === "AGG_ARITH") return true;
|
|
5444
5760
|
if (result.type === "ARRAY" || result.type === "FIELD_REF" || result.type === "ARITH") return false;
|
|
5445
5761
|
return scalarValueHasAggregate(result);
|
|
5446
5762
|
}
|
|
@@ -5649,6 +5965,10 @@ function collectRequiredFieldsByTable(stmt, plainGroupByPlan, sourceAware) {
|
|
|
5649
5965
|
};
|
|
5650
5966
|
const walkCaseResult = (result, phase = "select") => {
|
|
5651
5967
|
if (result.type === "ARRAY") return;
|
|
5968
|
+
if (result.type === "AGG_REF" || result.type === "AGG_ARITH") {
|
|
5969
|
+
walkAgg(result, phase);
|
|
5970
|
+
return;
|
|
5971
|
+
}
|
|
5652
5972
|
if (result.type === "FIELD_REF" || result.type === "ARITH") {
|
|
5653
5973
|
walkArith(result, phase);
|
|
5654
5974
|
return;
|
|
@@ -5664,6 +5984,10 @@ function collectRequiredFieldsByTable(stmt, plainGroupByPlan, sourceAware) {
|
|
|
5664
5984
|
};
|
|
5665
5985
|
const walkFieldValue = (fv, phase = "select") => {
|
|
5666
5986
|
if (fv.type === "FIELD") {
|
|
5987
|
+
if (fv.aggregateRef) {
|
|
5988
|
+
walkAgg(fv.aggregateRef, phase);
|
|
5989
|
+
return;
|
|
5990
|
+
}
|
|
5667
5991
|
addFieldRef(fv.field, fv.tableAlias, phase);
|
|
5668
5992
|
return;
|
|
5669
5993
|
}
|
|
@@ -5671,6 +5995,10 @@ function collectRequiredFieldsByTable(stmt, plainGroupByPlan, sourceAware) {
|
|
|
5671
5995
|
walkStringFunc(fv.expr, phase);
|
|
5672
5996
|
return;
|
|
5673
5997
|
}
|
|
5998
|
+
if (fv.type === "AGG_FIELD") {
|
|
5999
|
+
walkAgg(fv.expr, phase);
|
|
6000
|
+
return;
|
|
6001
|
+
}
|
|
5674
6002
|
if (fv.type === "ARITH_FIELD") {
|
|
5675
6003
|
walkArith(fv.expr, phase);
|
|
5676
6004
|
return;
|
|
@@ -6182,631 +6510,447 @@ function collectExactServerFunctionLeaves(where) {
|
|
|
6182
6510
|
}
|
|
6183
6511
|
return;
|
|
6184
6512
|
case "LOGICAL":
|
|
6185
|
-
visit(node.left);
|
|
6186
|
-
visit(node.right);
|
|
6187
|
-
return;
|
|
6188
|
-
case "NOT":
|
|
6189
|
-
case "GROUP":
|
|
6190
|
-
visit(node.expr);
|
|
6191
|
-
return;
|
|
6192
|
-
case "EXISTS":
|
|
6193
|
-
case "NULL_CHECK":
|
|
6194
|
-
case "BOOLEAN":
|
|
6195
|
-
return;
|
|
6196
|
-
}
|
|
6197
|
-
};
|
|
6198
|
-
visit(where);
|
|
6199
|
-
return leaves;
|
|
6200
|
-
}
|
|
6201
|
-
function sameOccurrenceList(actual, expected) {
|
|
6202
|
-
return actual.length === expected.length && actual.every((name, index) => name === expected[index]);
|
|
6203
|
-
}
|
|
6204
|
-
function serializedMultisetContains(query, expectedNames) {
|
|
6205
|
-
const expected = /* @__PURE__ */ new Map();
|
|
6206
|
-
for (const name of expectedNames) {
|
|
6207
|
-
if (!isServerOnlyWhereFunctionName(name)) return false;
|
|
6208
|
-
expected.set(name, (expected.get(name) ?? 0) + 1);
|
|
6209
|
-
}
|
|
6210
|
-
for (const [name, count] of expected) {
|
|
6211
|
-
const matches = query.match(new RegExp(`\\b${name}\\s*\\(`, "g"));
|
|
6212
|
-
if ((matches?.length ?? 0) < count) return false;
|
|
6213
|
-
}
|
|
6214
|
-
return true;
|
|
6215
|
-
}
|
|
6216
|
-
|
|
6217
|
-
// src/core/klikeValidation.ts
|
|
6218
|
-
var KlikeValidationError = class extends Error {
|
|
6219
|
-
constructor(message) {
|
|
6220
|
-
super(`ArgumentError: ${message}`);
|
|
6221
|
-
this.name = "ArgumentError";
|
|
6222
|
-
}
|
|
6223
|
-
};
|
|
6224
|
-
function validateKlikeStatement(stmt) {
|
|
6225
|
-
validateStatement(stmt);
|
|
6226
|
-
}
|
|
6227
|
-
function validateKlikePushdownPlan(plan) {
|
|
6228
|
-
if (unappliedKlikes(plan).length > 0) {
|
|
6229
|
-
throw new KlikeValidationError(
|
|
6230
|
-
"FULL_SCAN \u306E KLIKE / NOT KLIKE \u3092\u5B89\u5168\u306B\u62BC\u3057\u4E0B\u3052\u3089\u308C\u307E\u305B\u3093\u3002OR / NOT \u914D\u4E0B\u3001CTE\u30FB\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u3001LEFT / RIGHT JOIN \u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
6231
|
-
);
|
|
6232
|
-
}
|
|
6233
|
-
}
|
|
6234
|
-
function validateStatement(stmt) {
|
|
6235
|
-
switch (stmt.type) {
|
|
6236
|
-
case "SELECT":
|
|
6237
|
-
validateSelect(stmt);
|
|
6238
|
-
return;
|
|
6239
|
-
case "UNION":
|
|
6240
|
-
validateUnion(stmt);
|
|
6241
|
-
return;
|
|
6242
|
-
case "WITH":
|
|
6243
|
-
validateWith(stmt);
|
|
6244
|
-
return;
|
|
6245
|
-
case "EXPLAIN":
|
|
6246
|
-
validateStatement(stmt.query);
|
|
6247
|
-
return;
|
|
6248
|
-
case "CREATE_TEMP_TABLE":
|
|
6249
|
-
validateSelectLike(stmt.query);
|
|
6250
|
-
return;
|
|
6251
|
-
case "SET_VARIABLE":
|
|
6252
|
-
case "DECLARE_VARIABLE":
|
|
6253
|
-
case "ASSERT":
|
|
6254
|
-
validateNestedSelects(stmt);
|
|
6255
|
-
return;
|
|
6256
|
-
case "UPDATE":
|
|
6257
|
-
if (stmt.applyBlocks?.length && !isSinglePositiveRecordIdWhere(stmt.where) && whereHasKlike(stmt.where)) {
|
|
6258
|
-
validateKlikeWhereExpressions(stmt.where);
|
|
6259
|
-
validateNestedSelects(stmt);
|
|
6260
|
-
return;
|
|
6261
|
-
}
|
|
6262
|
-
if (containsKlike(stmt) && stmt.subtableCode) {
|
|
6263
|
-
throw new KlikeValidationError(
|
|
6264
|
-
"KLIKE / NOT KLIKE \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u306E WHERE \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
6265
|
-
);
|
|
6266
|
-
}
|
|
6267
|
-
if (!stmt.applyBlocks?.length && !stmt.subtableCode && whereHasKlike(stmt.where) && !containsKlikeOutsideWhereAndNestedSelects(stmt, stmt.where)) {
|
|
6268
|
-
validateKlikeWhereExpressions(stmt.where);
|
|
6269
|
-
validateNestedSelects(stmt);
|
|
6270
|
-
return;
|
|
6271
|
-
}
|
|
6272
|
-
if (containsKlike(stmt)) {
|
|
6273
|
-
throw new KlikeValidationError(
|
|
6274
|
-
"KLIKE / NOT KLIKE \u306F\u901A\u5E38\u89AA UPDATE \u306E WHERE\u3001\u307E\u305F\u306F APPLY \u8907\u6570\u89AA UPDATE \u306E\u5B89\u5168\u306A\u89AA WHERE \u3060\u3051\u3067\u4F7F\u7528\u3067\u304D\u307E\u3059"
|
|
6275
|
-
);
|
|
6276
|
-
}
|
|
6277
|
-
return;
|
|
6278
|
-
case "DELETE":
|
|
6279
|
-
if (containsKlike(stmt) && stmt.subtableCode) {
|
|
6280
|
-
throw new KlikeValidationError(
|
|
6281
|
-
"KLIKE / NOT KLIKE \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB DELETE \u306E WHERE \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
6282
|
-
);
|
|
6283
|
-
}
|
|
6284
|
-
if (!stmt.subtableCode && whereHasKlike(stmt.where)) {
|
|
6285
|
-
validateKlikeWhereExpressions(stmt.where);
|
|
6286
|
-
validateNestedSelects(stmt);
|
|
6287
|
-
return;
|
|
6288
|
-
}
|
|
6289
|
-
if (containsKlike(stmt)) {
|
|
6290
|
-
throw new KlikeValidationError(
|
|
6291
|
-
"KLIKE / NOT KLIKE \u306F\u901A\u5E38\u89AA DELETE \u306E WHERE \u3060\u3051\u3067\u4F7F\u7528\u3067\u304D\u307E\u3059"
|
|
6292
|
-
);
|
|
6293
|
-
}
|
|
6294
|
-
return;
|
|
6295
|
-
case "INSERT":
|
|
6296
|
-
case "INSERT_SELECT":
|
|
6297
|
-
if (containsKlike(stmt)) {
|
|
6298
|
-
throw new KlikeValidationError(
|
|
6299
|
-
"KLIKE / NOT KLIKE \u306F INSERT / INSERT SELECT \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
6300
|
-
);
|
|
6301
|
-
}
|
|
6302
|
-
return;
|
|
6303
|
-
case "UPSERT":
|
|
6304
|
-
case "UPSERT_SELECT":
|
|
6305
|
-
if (containsKlike(stmt)) {
|
|
6306
|
-
throw new KlikeValidationError(
|
|
6307
|
-
"KLIKE / NOT KLIKE \u306F UPSERT / UPSERT SELECT \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
6308
|
-
);
|
|
6309
|
-
}
|
|
6310
|
-
return;
|
|
6311
|
-
case "REORDER":
|
|
6312
|
-
if (containsKlike(stmt)) {
|
|
6313
|
-
throw new KlikeValidationError(
|
|
6314
|
-
"KLIKE / NOT KLIKE \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB REORDER \u306E WHERE \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
6315
|
-
);
|
|
6316
|
-
}
|
|
6317
|
-
return;
|
|
6318
|
-
case "VALIDATE":
|
|
6319
|
-
if (containsKlike(stmt)) {
|
|
6320
|
-
throw new KlikeValidationError("KLIKE / NOT KLIKE \u306F VALIDATE \u306E WHERE / CHECK \u3067\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093");
|
|
6321
|
-
}
|
|
6322
|
-
return;
|
|
6323
|
-
case "SHOW_APPS":
|
|
6324
|
-
case "DESCRIBE":
|
|
6325
|
-
case "DROP_TEMP_TABLE":
|
|
6326
|
-
return;
|
|
6327
|
-
}
|
|
6328
|
-
}
|
|
6329
|
-
function validateKlikeWhereExpressions(where) {
|
|
6330
|
-
walkWithoutNestedSelects(where, (expr) => {
|
|
6331
|
-
if (!isKlike(expr)) return;
|
|
6332
|
-
const right = expr.right;
|
|
6333
|
-
if (right.type !== "STRING" && right.type !== "VARIABLE") {
|
|
6334
|
-
throw new KlikeValidationError(
|
|
6335
|
-
"KLIKE / NOT KLIKE \u306E\u53F3\u8FBA\u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u307E\u305F\u306F\u6587\u5B57\u5217\u30D0\u30C3\u30C1\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059"
|
|
6336
|
-
);
|
|
6337
|
-
}
|
|
6338
|
-
if (right.type === "STRING" && right.value.includes("%")) {
|
|
6339
|
-
throw new KlikeValidationError(
|
|
6340
|
-
"KLIKE / NOT KLIKE \u306E\u691C\u7D22\u8A9E\u306B % \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002SQL \u30EF\u30A4\u30EB\u30C9\u30AB\u30FC\u30C9\u691C\u7D22\u306B\u306F LIKE \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
6341
|
-
);
|
|
6342
|
-
}
|
|
6343
|
-
});
|
|
6344
|
-
}
|
|
6345
|
-
function validateSelectLike(query) {
|
|
6346
|
-
if (query.type === "SELECT") validateSelect(query);
|
|
6347
|
-
else if (query.type === "UNION") validateUnion(query);
|
|
6348
|
-
else validateWith(query);
|
|
6349
|
-
}
|
|
6350
|
-
function validateUnion(stmt) {
|
|
6351
|
-
validateSelectLike(stmt.left);
|
|
6352
|
-
validateSelect(stmt.right);
|
|
6353
|
-
}
|
|
6354
|
-
function validateWith(stmt) {
|
|
6355
|
-
if (canInlineSingleCte(stmt)) {
|
|
6356
|
-
validateSelect(buildInlinedQuery(stmt));
|
|
6357
|
-
return;
|
|
6358
|
-
}
|
|
6359
|
-
for (const cte of stmt.ctes) {
|
|
6360
|
-
if (cte.query.type === "SELECT") validateSelect(cte.query);
|
|
6361
|
-
else if (cte.query.type === "UNION") validateUnion(cte.query);
|
|
6362
|
-
}
|
|
6363
|
-
validateSelectLike(stmt.query);
|
|
6364
|
-
}
|
|
6365
|
-
function validateSelect(stmt) {
|
|
6366
|
-
validateOwnKlikeExpressions(stmt);
|
|
6367
|
-
if (whereHasKlike(stmt.where)) {
|
|
6368
|
-
const directKintoneSimple = resolveSelectMode(stmt) === "SIMPLE" && stmt.from.cteName === null && stmt.joins.every((join2) => join2.table.cteName === null);
|
|
6369
|
-
if (!directKintoneSimple) {
|
|
6370
|
-
const plan = buildKlikePushdownPlan(stmt, { allowUnresolvedVariables: true });
|
|
6371
|
-
if (unappliedKlikes(plan).length === 0) {
|
|
6372
|
-
validateNestedSelects(stmt);
|
|
6373
|
-
return;
|
|
6374
|
-
}
|
|
6375
|
-
if (canDeferJoinWholeWhereKlikeValidation(stmt)) {
|
|
6376
|
-
validateNestedSelects(stmt);
|
|
6377
|
-
return;
|
|
6378
|
-
}
|
|
6379
|
-
throw new KlikeValidationError(
|
|
6380
|
-
"FULL_SCAN \u306E KLIKE / NOT KLIKE \u306F\u3001\u7269\u7406\u30C6\u30FC\u30D6\u30EB\u306B\u5BFE\u3059\u308B AND \u30EA\u30FC\u30D5\u3068\u3057\u3066\u5FC5\u305A\u62BC\u3057\u4E0B\u3052\u3089\u308C\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002OR / NOT \u914D\u4E0B\u3001CTE\u30FB\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u3001LEFT / RIGHT JOIN \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
6381
|
-
);
|
|
6382
|
-
}
|
|
6383
|
-
}
|
|
6384
|
-
validateNestedSelects(stmt);
|
|
6385
|
-
}
|
|
6386
|
-
function canDeferJoinWholeWhereKlikeValidation(stmt) {
|
|
6387
|
-
return stmt.where !== null && serverOnlyFunctionOccurrencesInWhere(stmt.where).length > 0 && stmt.joins.length > 0 && stmt.joins.every((join2) => join2.type === "INNER") && [stmt.from, ...stmt.joins.map((join2) => join2.table)].every(
|
|
6388
|
-
(table) => table.alias !== null && table.cteName === null && !table.subtableCode
|
|
6389
|
-
);
|
|
6390
|
-
}
|
|
6391
|
-
function validateOwnKlikeExpressions(stmt) {
|
|
6392
|
-
walkWithoutNestedSelects(stmt, (where) => {
|
|
6393
|
-
if (!isKlike(where)) return;
|
|
6394
|
-
if (!isDescendantOf(stmt.where, where)) {
|
|
6395
|
-
throw new KlikeValidationError("KLIKE / NOT KLIKE \u306F SELECT \u306E WHERE \u53E5\u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059");
|
|
6396
|
-
}
|
|
6397
|
-
const right = where.right;
|
|
6398
|
-
if (right.type !== "STRING" && right.type !== "VARIABLE") {
|
|
6399
|
-
throw new KlikeValidationError(
|
|
6400
|
-
"KLIKE / NOT KLIKE \u306E\u53F3\u8FBA\u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u307E\u305F\u306F\u6587\u5B57\u5217\u30D0\u30C3\u30C1\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059"
|
|
6401
|
-
);
|
|
6402
|
-
}
|
|
6403
|
-
if (right.type === "STRING" && right.value.includes("%")) {
|
|
6404
|
-
throw new KlikeValidationError(
|
|
6405
|
-
"KLIKE / NOT KLIKE \u306E\u691C\u7D22\u8A9E\u306B % \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002SQL \u30EF\u30A4\u30EB\u30C9\u30AB\u30FC\u30C9\u691C\u7D22\u306B\u306F LIKE \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
6406
|
-
);
|
|
6407
|
-
}
|
|
6408
|
-
});
|
|
6409
|
-
}
|
|
6410
|
-
function validateNestedSelects(node) {
|
|
6411
|
-
walkObjects(node, (obj) => {
|
|
6412
|
-
if (obj.type === "SELECT") validateSelect(obj);
|
|
6413
|
-
}, true);
|
|
6414
|
-
}
|
|
6415
|
-
function containsKlike(node) {
|
|
6416
|
-
let found = false;
|
|
6417
|
-
walkObjects(node, (obj) => {
|
|
6418
|
-
if (obj.type === "BINARY" && (obj.op === "KLIKE" || obj.op === "NOT_KLIKE")) found = true;
|
|
6419
|
-
});
|
|
6420
|
-
return found;
|
|
6421
|
-
}
|
|
6422
|
-
function containsKlikeOutsideWhereAndNestedSelects(node, allowedWhere) {
|
|
6423
|
-
let found = false;
|
|
6424
|
-
const visit = (value) => {
|
|
6425
|
-
if (found || value === allowedWhere || value === null || typeof value !== "object") return;
|
|
6426
|
-
if (Array.isArray(value)) {
|
|
6427
|
-
for (const item of value) visit(item);
|
|
6428
|
-
return;
|
|
6429
|
-
}
|
|
6430
|
-
const obj = value;
|
|
6431
|
-
if (obj.type === "SELECT") return;
|
|
6432
|
-
if (obj.type === "BINARY" && (obj.op === "KLIKE" || obj.op === "NOT_KLIKE")) {
|
|
6433
|
-
found = true;
|
|
6434
|
-
return;
|
|
6513
|
+
visit(node.left);
|
|
6514
|
+
visit(node.right);
|
|
6515
|
+
return;
|
|
6516
|
+
case "NOT":
|
|
6517
|
+
case "GROUP":
|
|
6518
|
+
visit(node.expr);
|
|
6519
|
+
return;
|
|
6520
|
+
case "EXISTS":
|
|
6521
|
+
case "NULL_CHECK":
|
|
6522
|
+
case "BOOLEAN":
|
|
6523
|
+
return;
|
|
6435
6524
|
}
|
|
6436
|
-
for (const child of Object.values(obj)) visit(child);
|
|
6437
6525
|
};
|
|
6438
|
-
visit(
|
|
6439
|
-
return
|
|
6526
|
+
visit(where);
|
|
6527
|
+
return leaves;
|
|
6440
6528
|
}
|
|
6441
|
-
function
|
|
6442
|
-
|
|
6443
|
-
if (root === target) return true;
|
|
6444
|
-
switch (root.type) {
|
|
6445
|
-
case "LOGICAL":
|
|
6446
|
-
return isDescendantOf(root.left, target) || isDescendantOf(root.right, target);
|
|
6447
|
-
case "NOT":
|
|
6448
|
-
case "GROUP":
|
|
6449
|
-
return isDescendantOf(root.expr, target);
|
|
6450
|
-
case "BINARY":
|
|
6451
|
-
case "NULL_CHECK":
|
|
6452
|
-
case "EXISTS":
|
|
6453
|
-
return false;
|
|
6454
|
-
case "BOOLEAN":
|
|
6455
|
-
return false;
|
|
6456
|
-
}
|
|
6529
|
+
function sameOccurrenceList(actual, expected) {
|
|
6530
|
+
return actual.length === expected.length && actual.every((name, index) => name === expected[index]);
|
|
6457
6531
|
}
|
|
6458
|
-
function
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
return;
|
|
6462
|
-
|
|
6463
|
-
if (node === null || typeof node !== "object") return;
|
|
6464
|
-
const obj = node;
|
|
6465
|
-
if (obj.type === "BINARY" || obj.type === "NULL_CHECK" || obj.type === "LOGICAL" || obj.type === "NOT" || obj.type === "GROUP" || obj.type === "EXISTS") {
|
|
6466
|
-
visitWhere(obj);
|
|
6467
|
-
}
|
|
6468
|
-
for (const value of Object.values(obj)) {
|
|
6469
|
-
if (value !== node && isSelectObject(value)) continue;
|
|
6470
|
-
walkWithoutNestedSelects(value, visitWhere);
|
|
6532
|
+
function serializedMultisetContains(query, expectedNames) {
|
|
6533
|
+
const expected = /* @__PURE__ */ new Map();
|
|
6534
|
+
for (const name of expectedNames) {
|
|
6535
|
+
if (!isServerOnlyWhereFunctionName(name)) return false;
|
|
6536
|
+
expected.set(name, (expected.get(name) ?? 0) + 1);
|
|
6471
6537
|
}
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
for (const value of node) walkObjects(value, visit);
|
|
6476
|
-
return;
|
|
6538
|
+
for (const [name, count] of expected) {
|
|
6539
|
+
const matches = query.match(new RegExp(`\\b${name}\\s*\\(`, "g"));
|
|
6540
|
+
if ((matches?.length ?? 0) < count) return false;
|
|
6477
6541
|
}
|
|
6478
|
-
|
|
6479
|
-
const obj = node;
|
|
6480
|
-
if (!skipRoot) visit(obj);
|
|
6481
|
-
for (const value of Object.values(obj)) walkObjects(value, visit);
|
|
6482
|
-
}
|
|
6483
|
-
function isSelectObject(value) {
|
|
6484
|
-
return value !== null && typeof value === "object" && value.type === "SELECT";
|
|
6542
|
+
return true;
|
|
6485
6543
|
}
|
|
6486
6544
|
|
|
6487
|
-
// src/core/
|
|
6488
|
-
var
|
|
6489
|
-
constructor() {
|
|
6490
|
-
super(
|
|
6491
|
-
"ArgumentError: PRIMARY_ORGANIZATION() \u306F DML \u306E WHERE \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
6492
|
-
);
|
|
6545
|
+
// src/core/klikeValidation.ts
|
|
6546
|
+
var KlikeValidationError = class extends Error {
|
|
6547
|
+
constructor(message) {
|
|
6548
|
+
super(`ArgumentError: ${message}`);
|
|
6493
6549
|
this.name = "ArgumentError";
|
|
6494
6550
|
}
|
|
6495
6551
|
};
|
|
6496
|
-
function
|
|
6497
|
-
|
|
6498
|
-
|
|
6552
|
+
function validateKlikeStatement(stmt) {
|
|
6553
|
+
validateStatement(stmt);
|
|
6554
|
+
}
|
|
6555
|
+
function validateKlikePushdownPlan(plan) {
|
|
6556
|
+
if (unappliedKlikes(plan).length > 0) {
|
|
6557
|
+
throw new KlikeValidationError(
|
|
6558
|
+
"FULL_SCAN \u306E KLIKE / NOT KLIKE \u3092\u5B89\u5168\u306B\u62BC\u3057\u4E0B\u3052\u3089\u308C\u307E\u305B\u3093\u3002OR / NOT \u914D\u4E0B\u3001CTE\u30FB\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u3001LEFT / RIGHT JOIN \u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
6559
|
+
);
|
|
6560
|
+
}
|
|
6561
|
+
}
|
|
6562
|
+
function validateStatement(stmt) {
|
|
6563
|
+
switch (stmt.type) {
|
|
6564
|
+
case "SELECT":
|
|
6565
|
+
validateSelect(stmt);
|
|
6566
|
+
return;
|
|
6567
|
+
case "UNION":
|
|
6568
|
+
validateUnion(stmt);
|
|
6569
|
+
return;
|
|
6570
|
+
case "WITH":
|
|
6571
|
+
validateWith(stmt);
|
|
6572
|
+
return;
|
|
6573
|
+
case "EXPLAIN":
|
|
6574
|
+
validateStatement(stmt.query);
|
|
6575
|
+
return;
|
|
6576
|
+
case "CREATE_TEMP_TABLE":
|
|
6577
|
+
validateSelectLike(stmt.query);
|
|
6578
|
+
return;
|
|
6579
|
+
case "SET_VARIABLE":
|
|
6580
|
+
case "DECLARE_VARIABLE":
|
|
6581
|
+
case "ASSERT":
|
|
6582
|
+
validateNestedSelects(stmt);
|
|
6583
|
+
return;
|
|
6499
6584
|
case "UPDATE":
|
|
6585
|
+
if (stmt.applyBlocks?.length && !isSinglePositiveRecordIdWhere(stmt.where) && whereHasKlike(stmt.where)) {
|
|
6586
|
+
validateKlikeWhereExpressions(stmt.where);
|
|
6587
|
+
validateNestedSelects(stmt);
|
|
6588
|
+
return;
|
|
6589
|
+
}
|
|
6590
|
+
if (containsKlike(stmt) && stmt.subtableCode) {
|
|
6591
|
+
throw new KlikeValidationError(
|
|
6592
|
+
"KLIKE / NOT KLIKE \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u306E WHERE \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
6593
|
+
);
|
|
6594
|
+
}
|
|
6595
|
+
if (!stmt.applyBlocks?.length && !stmt.subtableCode && whereHasKlike(stmt.where) && !containsKlikeOutsideWhereAndNestedSelects(stmt, stmt.where)) {
|
|
6596
|
+
validateKlikeWhereExpressions(stmt.where);
|
|
6597
|
+
validateNestedSelects(stmt);
|
|
6598
|
+
return;
|
|
6599
|
+
}
|
|
6600
|
+
if (containsKlike(stmt)) {
|
|
6601
|
+
throw new KlikeValidationError(
|
|
6602
|
+
"KLIKE / NOT KLIKE \u306F\u901A\u5E38\u89AA UPDATE \u306E WHERE\u3001\u307E\u305F\u306F APPLY \u8907\u6570\u89AA UPDATE \u306E\u5B89\u5168\u306A\u89AA WHERE \u3060\u3051\u3067\u4F7F\u7528\u3067\u304D\u307E\u3059"
|
|
6603
|
+
);
|
|
6604
|
+
}
|
|
6605
|
+
return;
|
|
6500
6606
|
case "DELETE":
|
|
6607
|
+
if (containsKlike(stmt) && stmt.subtableCode) {
|
|
6608
|
+
throw new KlikeValidationError(
|
|
6609
|
+
"KLIKE / NOT KLIKE \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB DELETE \u306E WHERE \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
6610
|
+
);
|
|
6611
|
+
}
|
|
6612
|
+
if (!stmt.subtableCode && whereHasKlike(stmt.where)) {
|
|
6613
|
+
validateKlikeWhereExpressions(stmt.where);
|
|
6614
|
+
validateNestedSelects(stmt);
|
|
6615
|
+
return;
|
|
6616
|
+
}
|
|
6617
|
+
if (containsKlike(stmt)) {
|
|
6618
|
+
throw new KlikeValidationError(
|
|
6619
|
+
"KLIKE / NOT KLIKE \u306F\u901A\u5E38\u89AA DELETE \u306E WHERE \u3060\u3051\u3067\u4F7F\u7528\u3067\u304D\u307E\u3059"
|
|
6620
|
+
);
|
|
6621
|
+
}
|
|
6622
|
+
return;
|
|
6623
|
+
case "INSERT":
|
|
6624
|
+
case "INSERT_SELECT":
|
|
6625
|
+
if (containsKlike(stmt)) {
|
|
6626
|
+
throw new KlikeValidationError(
|
|
6627
|
+
"KLIKE / NOT KLIKE \u306F INSERT / INSERT SELECT \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
6628
|
+
);
|
|
6629
|
+
}
|
|
6630
|
+
return;
|
|
6501
6631
|
case "UPSERT":
|
|
6502
6632
|
case "UPSERT_SELECT":
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6633
|
+
if (containsKlike(stmt)) {
|
|
6634
|
+
throw new KlikeValidationError(
|
|
6635
|
+
"KLIKE / NOT KLIKE \u306F UPSERT / UPSERT SELECT \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
6636
|
+
);
|
|
6506
6637
|
}
|
|
6507
6638
|
return;
|
|
6508
|
-
|
|
6639
|
+
case "REORDER":
|
|
6640
|
+
if (containsKlike(stmt)) {
|
|
6641
|
+
throw new KlikeValidationError(
|
|
6642
|
+
"KLIKE / NOT KLIKE \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB REORDER \u306E WHERE \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
6643
|
+
);
|
|
6644
|
+
}
|
|
6645
|
+
return;
|
|
6646
|
+
case "VALIDATE":
|
|
6647
|
+
if (containsKlike(stmt)) {
|
|
6648
|
+
throw new KlikeValidationError("KLIKE / NOT KLIKE \u306F VALIDATE \u306E WHERE / CHECK \u3067\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093");
|
|
6649
|
+
}
|
|
6650
|
+
return;
|
|
6651
|
+
case "SHOW_APPS":
|
|
6652
|
+
case "DESCRIBE":
|
|
6653
|
+
case "DROP_TEMP_TABLE":
|
|
6509
6654
|
return;
|
|
6510
6655
|
}
|
|
6511
6656
|
}
|
|
6512
|
-
function
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
if (
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
}
|
|
6526
|
-
});
|
|
6527
|
-
return found;
|
|
6528
|
-
}
|
|
6529
|
-
function walkObjects2(node, visit) {
|
|
6530
|
-
if (Array.isArray(node)) {
|
|
6531
|
-
for (const value of node) walkObjects2(value, visit);
|
|
6532
|
-
return;
|
|
6533
|
-
}
|
|
6534
|
-
if (node === null || typeof node !== "object") return;
|
|
6535
|
-
const obj = node;
|
|
6536
|
-
visit(obj);
|
|
6537
|
-
for (const value of Object.values(obj)) walkObjects2(value, visit);
|
|
6538
|
-
}
|
|
6539
|
-
|
|
6540
|
-
// src/core/statementValidation.ts
|
|
6541
|
-
function validateStatementStatic(stmt) {
|
|
6542
|
-
validatePrimaryOrganizationDmlStatement(stmt);
|
|
6543
|
-
validateKlikeStatement(stmt);
|
|
6544
|
-
}
|
|
6545
|
-
|
|
6546
|
-
// src/engine/groupingRowMeta.ts
|
|
6547
|
-
var groupingRowMetaKey = /* @__PURE__ */ Symbol("ksql.groupingRowMeta");
|
|
6548
|
-
var groupingRefCanonicalIds = /* @__PURE__ */ new WeakMap();
|
|
6549
|
-
function attachGroupingRowMeta(row, includedCanonicalIds) {
|
|
6550
|
-
Object.defineProperty(row, groupingRowMetaKey, {
|
|
6551
|
-
value: { includedCanonicalIds },
|
|
6552
|
-
enumerable: false,
|
|
6553
|
-
configurable: false,
|
|
6554
|
-
writable: false
|
|
6555
|
-
});
|
|
6556
|
-
return row;
|
|
6557
|
-
}
|
|
6558
|
-
function getGroupingRowMeta(row) {
|
|
6559
|
-
return row[groupingRowMetaKey];
|
|
6560
|
-
}
|
|
6561
|
-
function readGroupingMembership(row) {
|
|
6562
|
-
return getGroupingRowMeta(row)?.includedCanonicalIds;
|
|
6563
|
-
}
|
|
6564
|
-
function bindGroupingRefCanonicalId(ref, canonicalId) {
|
|
6565
|
-
groupingRefCanonicalIds.set(ref, canonicalId);
|
|
6566
|
-
}
|
|
6567
|
-
function evalGroupingRef(ref, row) {
|
|
6568
|
-
const membership = readGroupingMembership(row);
|
|
6569
|
-
if (!membership) {
|
|
6570
|
-
throw new Error("internal error: GROUPING() evaluation requires B65 grouping row membership.");
|
|
6571
|
-
}
|
|
6572
|
-
const canonicalId = groupingRefCanonicalIds.get(ref);
|
|
6573
|
-
if (!canonicalId) {
|
|
6574
|
-
throw new Error("internal error: GROUPING() reference was not resolved during B65 planning.");
|
|
6575
|
-
}
|
|
6576
|
-
return membership.has(canonicalId) ? "0" : "1";
|
|
6577
|
-
}
|
|
6578
|
-
|
|
6579
|
-
// src/core/groupingValidation.ts
|
|
6580
|
-
var enforceGroupingPlanningCandidateLimits = (facts) => {
|
|
6581
|
-
if (facts.expandedSetCount > B65_MAX_GROUPING_SETS) {
|
|
6582
|
-
throw new Error(
|
|
6583
|
-
`ArgumentError: B65 expanded grouping set count ${facts.expandedSetCount} exceeds limit ${B65_MAX_GROUPING_SETS} (reason=GROUPING_SET_LIMIT_EXCEEDED).`
|
|
6584
|
-
);
|
|
6585
|
-
}
|
|
6586
|
-
if (facts.canonicalItemCount > B65_MAX_GROUPING_ITEMS) {
|
|
6587
|
-
throw new Error(
|
|
6588
|
-
`ArgumentError: B65 canonical grouping item count ${facts.canonicalItemCount} exceeds limit ${B65_MAX_GROUPING_ITEMS} (reason=GROUPING_ITEM_LIMIT_EXCEEDED).`
|
|
6589
|
-
);
|
|
6590
|
-
}
|
|
6591
|
-
};
|
|
6592
|
-
function displayField(field) {
|
|
6593
|
-
return field.tableAlias ? `${field.tableAlias}.${field.field}` : field.field;
|
|
6594
|
-
}
|
|
6595
|
-
function refFromName(name) {
|
|
6596
|
-
const dot = name.indexOf(".");
|
|
6597
|
-
return dot > 0 ? { type: "FIELD", tableAlias: name.slice(0, dot), field: name.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field: name };
|
|
6657
|
+
function validateKlikeWhereExpressions(where) {
|
|
6658
|
+
walkWithoutNestedSelects(where, (expr) => {
|
|
6659
|
+
if (!isKlike(expr)) return;
|
|
6660
|
+
const right = expr.right;
|
|
6661
|
+
if (right.type !== "STRING" && right.type !== "VARIABLE") {
|
|
6662
|
+
throw new KlikeValidationError(
|
|
6663
|
+
"KLIKE / NOT KLIKE \u306E\u53F3\u8FBA\u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u307E\u305F\u306F\u6587\u5B57\u5217\u30D0\u30C3\u30C1\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059"
|
|
6664
|
+
);
|
|
6665
|
+
}
|
|
6666
|
+
if (right.type === "STRING" && right.value.includes("%")) {
|
|
6667
|
+
throw new KlikeValidationError(
|
|
6668
|
+
"KLIKE / NOT KLIKE \u306E\u691C\u7D22\u8A9E\u306B % \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002SQL \u30EF\u30A4\u30EB\u30C9\u30AB\u30FC\u30C9\u691C\u7D22\u306B\u306F LIKE \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
6669
|
+
);
|
|
6670
|
+
}
|
|
6671
|
+
});
|
|
6598
6672
|
}
|
|
6599
|
-
function
|
|
6600
|
-
if (
|
|
6601
|
-
if (
|
|
6602
|
-
|
|
6603
|
-
return;
|
|
6604
|
-
}
|
|
6605
|
-
const value = node;
|
|
6606
|
-
if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return;
|
|
6607
|
-
if (value["type"] === "GROUPING_REF") {
|
|
6608
|
-
out.push(value);
|
|
6609
|
-
return;
|
|
6610
|
-
}
|
|
6611
|
-
Object.values(value).forEach((item) => collectGroupingRefs(item, out));
|
|
6673
|
+
function validateSelectLike(query) {
|
|
6674
|
+
if (query.type === "SELECT") validateSelect(query);
|
|
6675
|
+
else if (query.type === "UNION") validateUnion(query);
|
|
6676
|
+
else validateWith(query);
|
|
6612
6677
|
}
|
|
6613
|
-
function
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
node.forEach((item) => collectAggregateArgumentGroupingRefs(item, out));
|
|
6617
|
-
return;
|
|
6618
|
-
}
|
|
6619
|
-
const value = node;
|
|
6620
|
-
if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return;
|
|
6621
|
-
if (value["type"] === "AGGREGATE" || value["type"] === "AGG_REF") {
|
|
6622
|
-
collectGroupingRefs(value["arg"], out);
|
|
6623
|
-
return;
|
|
6624
|
-
}
|
|
6625
|
-
Object.values(value).forEach((item) => collectAggregateArgumentGroupingRefs(item, out));
|
|
6678
|
+
function validateUnion(stmt) {
|
|
6679
|
+
validateSelectLike(stmt.left);
|
|
6680
|
+
validateSelect(stmt.right);
|
|
6626
6681
|
}
|
|
6627
|
-
function
|
|
6628
|
-
if (
|
|
6629
|
-
|
|
6630
|
-
node.forEach((item) => collectNonAggregateFieldRefs(item, out));
|
|
6631
|
-
return;
|
|
6632
|
-
}
|
|
6633
|
-
const value = node;
|
|
6634
|
-
const type = value["type"];
|
|
6635
|
-
if (type === "SELECT" || type === "SCALAR_SUBQUERY" || type === "GROUPING_REF" || type === "AGG_REF" || type === "AGG_ARITH") return;
|
|
6636
|
-
if (type === "FIELD" && typeof value["field"] === "string") {
|
|
6637
|
-
out.push({
|
|
6638
|
-
type: "FIELD",
|
|
6639
|
-
tableAlias: typeof value["tableAlias"] === "string" ? value["tableAlias"] : null,
|
|
6640
|
-
field: value["field"]
|
|
6641
|
-
});
|
|
6682
|
+
function validateWith(stmt) {
|
|
6683
|
+
if (canInlineSingleCte(stmt)) {
|
|
6684
|
+
validateSelect(buildInlinedQuery(stmt));
|
|
6642
6685
|
return;
|
|
6643
6686
|
}
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6687
|
+
for (const cte of stmt.ctes) {
|
|
6688
|
+
if (cte.query.type === "SELECT") validateSelect(cte.query);
|
|
6689
|
+
else if (cte.query.type === "UNION") validateUnion(cte.query);
|
|
6647
6690
|
}
|
|
6648
|
-
|
|
6649
|
-
}
|
|
6650
|
-
function containsAggregate2(node) {
|
|
6651
|
-
if (node === null || typeof node !== "object") return false;
|
|
6652
|
-
if (Array.isArray(node)) return node.some(containsAggregate2);
|
|
6653
|
-
const value = node;
|
|
6654
|
-
if (value["type"] === "AGG_REF" || value["type"] === "AGG_ARITH") return true;
|
|
6655
|
-
if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return false;
|
|
6656
|
-
return Object.values(value).some(containsAggregate2);
|
|
6691
|
+
validateSelectLike(stmt.query);
|
|
6657
6692
|
}
|
|
6658
|
-
function
|
|
6659
|
-
|
|
6660
|
-
if (
|
|
6661
|
-
|
|
6662
|
-
|
|
6693
|
+
function validateSelect(stmt) {
|
|
6694
|
+
validateOwnKlikeExpressions(stmt);
|
|
6695
|
+
if (whereHasKlike(stmt.where)) {
|
|
6696
|
+
const directKintoneSimple = resolveSelectMode(stmt) === "SIMPLE" && stmt.from.cteName === null && stmt.joins.every((join2) => join2.table.cteName === null);
|
|
6697
|
+
if (!directKintoneSimple) {
|
|
6698
|
+
const plan = buildKlikePushdownPlan(stmt, { allowUnresolvedVariables: true });
|
|
6699
|
+
if (unappliedKlikes(plan).length === 0) {
|
|
6700
|
+
validateNestedSelects(stmt);
|
|
6701
|
+
return;
|
|
6702
|
+
}
|
|
6703
|
+
if (canDeferJoinWholeWhereKlikeValidation(stmt)) {
|
|
6704
|
+
validateNestedSelects(stmt);
|
|
6705
|
+
return;
|
|
6706
|
+
}
|
|
6707
|
+
throw new KlikeValidationError(
|
|
6708
|
+
"FULL_SCAN \u306E KLIKE / NOT KLIKE \u306F\u3001\u7269\u7406\u30C6\u30FC\u30D6\u30EB\u306B\u5BFE\u3059\u308B AND \u30EA\u30FC\u30D5\u3068\u3057\u3066\u5FC5\u305A\u62BC\u3057\u4E0B\u3052\u3089\u308C\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002OR / NOT \u914D\u4E0B\u3001CTE\u30FB\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u3001LEFT / RIGHT JOIN \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
6709
|
+
);
|
|
6710
|
+
}
|
|
6663
6711
|
}
|
|
6664
|
-
|
|
6712
|
+
validateNestedSelects(stmt);
|
|
6665
6713
|
}
|
|
6666
|
-
function
|
|
6667
|
-
return
|
|
6668
|
-
(
|
|
6669
|
-
)
|
|
6714
|
+
function canDeferJoinWholeWhereKlikeValidation(stmt) {
|
|
6715
|
+
return stmt.where !== null && serverOnlyFunctionOccurrencesInWhere(stmt.where).length > 0 && stmt.joins.length > 0 && stmt.joins.every((join2) => join2.type === "INNER") && [stmt.from, ...stmt.joins.map((join2) => join2.table)].every(
|
|
6716
|
+
(table) => table.alias !== null && table.cteName === null && !table.subtableCode
|
|
6717
|
+
);
|
|
6670
6718
|
}
|
|
6671
|
-
function
|
|
6672
|
-
|
|
6719
|
+
function validateOwnKlikeExpressions(stmt) {
|
|
6720
|
+
walkWithoutNestedSelects(stmt, (where) => {
|
|
6721
|
+
if (!isKlike(where)) return;
|
|
6722
|
+
if (!isDescendantOf(stmt.where, where)) {
|
|
6723
|
+
throw new KlikeValidationError("KLIKE / NOT KLIKE \u306F SELECT \u306E WHERE \u53E5\u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059");
|
|
6724
|
+
}
|
|
6725
|
+
const right = where.right;
|
|
6726
|
+
if (right.type !== "STRING" && right.type !== "VARIABLE") {
|
|
6727
|
+
throw new KlikeValidationError(
|
|
6728
|
+
"KLIKE / NOT KLIKE \u306E\u53F3\u8FBA\u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u307E\u305F\u306F\u6587\u5B57\u5217\u30D0\u30C3\u30C1\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059"
|
|
6729
|
+
);
|
|
6730
|
+
}
|
|
6731
|
+
if (right.type === "STRING" && right.value.includes("%")) {
|
|
6732
|
+
throw new KlikeValidationError(
|
|
6733
|
+
"KLIKE / NOT KLIKE \u306E\u691C\u7D22\u8A9E\u306B % \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002SQL \u30EF\u30A4\u30EB\u30C9\u30AB\u30FC\u30C9\u691C\u7D22\u306B\u306F LIKE \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
6734
|
+
);
|
|
6735
|
+
}
|
|
6736
|
+
});
|
|
6673
6737
|
}
|
|
6674
|
-
function
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
}
|
|
6679
|
-
if (!canonicalItems.has(resolved.canonicalId)) {
|
|
6680
|
-
throw new Error(
|
|
6681
|
-
`ArgumentError: B65 GROUPING argument ${displayField(ref.field)} is not present in grouping allItems (reason=B65_GROUPING_ARG_NOT_ITEM).`
|
|
6682
|
-
);
|
|
6683
|
-
}
|
|
6684
|
-
bindGroupingRefCanonicalId(ref, resolved.canonicalId);
|
|
6738
|
+
function validateNestedSelects(node) {
|
|
6739
|
+
walkObjects(node, (obj) => {
|
|
6740
|
+
if (obj.type === "SELECT") validateSelect(obj);
|
|
6741
|
+
}, true);
|
|
6685
6742
|
}
|
|
6686
|
-
function
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
}
|
|
6743
|
+
function containsKlike(node) {
|
|
6744
|
+
let found = false;
|
|
6745
|
+
walkObjects(node, (obj) => {
|
|
6746
|
+
if (obj.type === "BINARY" && (obj.op === "KLIKE" || obj.op === "NOT_KLIKE")) found = true;
|
|
6747
|
+
});
|
|
6748
|
+
return found;
|
|
6693
6749
|
}
|
|
6694
|
-
function
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6750
|
+
function containsKlikeOutsideWhereAndNestedSelects(node, allowedWhere) {
|
|
6751
|
+
let found = false;
|
|
6752
|
+
const visit = (value) => {
|
|
6753
|
+
if (found || value === allowedWhere || value === null || typeof value !== "object") return;
|
|
6754
|
+
if (Array.isArray(value)) {
|
|
6755
|
+
for (const item of value) visit(item);
|
|
6756
|
+
return;
|
|
6757
|
+
}
|
|
6758
|
+
const obj = value;
|
|
6759
|
+
if (obj.type === "SELECT") return;
|
|
6760
|
+
if (obj.type === "BINARY" && (obj.op === "KLIKE" || obj.op === "NOT_KLIKE")) {
|
|
6761
|
+
found = true;
|
|
6762
|
+
return;
|
|
6763
|
+
}
|
|
6764
|
+
for (const child of Object.values(obj)) visit(child);
|
|
6765
|
+
};
|
|
6766
|
+
visit(node);
|
|
6767
|
+
return found;
|
|
6700
6768
|
}
|
|
6701
|
-
function
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
}
|
|
6717
|
-
if (forbiddenGroupingRefs.length > 0) {
|
|
6718
|
-
throw new Error(
|
|
6719
|
-
"ArgumentError: B65 GROUPING() is not allowed in WHERE, JOIN, window, aggregate arguments, or DML expressions."
|
|
6720
|
-
);
|
|
6769
|
+
function isDescendantOf(root, target) {
|
|
6770
|
+
if (root === null) return false;
|
|
6771
|
+
if (root === target) return true;
|
|
6772
|
+
switch (root.type) {
|
|
6773
|
+
case "LOGICAL":
|
|
6774
|
+
return isDescendantOf(root.left, target) || isDescendantOf(root.right, target);
|
|
6775
|
+
case "NOT":
|
|
6776
|
+
case "GROUP":
|
|
6777
|
+
return isDescendantOf(root.expr, target);
|
|
6778
|
+
case "BINARY":
|
|
6779
|
+
case "NULL_CHECK":
|
|
6780
|
+
case "EXISTS":
|
|
6781
|
+
return false;
|
|
6782
|
+
case "BOOLEAN":
|
|
6783
|
+
return false;
|
|
6721
6784
|
}
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6785
|
+
}
|
|
6786
|
+
function walkWithoutNestedSelects(node, visitWhere) {
|
|
6787
|
+
if (Array.isArray(node)) {
|
|
6788
|
+
for (const value of node) walkWithoutNestedSelects(value, visitWhere);
|
|
6726
6789
|
return;
|
|
6727
6790
|
}
|
|
6728
|
-
if (
|
|
6729
|
-
|
|
6791
|
+
if (node === null || typeof node !== "object") return;
|
|
6792
|
+
const obj = node;
|
|
6793
|
+
if (obj.type === "BINARY" || obj.type === "NULL_CHECK" || obj.type === "LOGICAL" || obj.type === "NOT" || obj.type === "GROUP" || obj.type === "EXISTS") {
|
|
6794
|
+
visitWhere(obj);
|
|
6730
6795
|
}
|
|
6731
|
-
|
|
6732
|
-
|
|
6796
|
+
for (const value of Object.values(obj)) {
|
|
6797
|
+
if (value !== node && isSelectObject(value)) continue;
|
|
6798
|
+
walkWithoutNestedSelects(value, visitWhere);
|
|
6733
6799
|
}
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
)) {
|
|
6737
|
-
|
|
6800
|
+
}
|
|
6801
|
+
function walkObjects(node, visit, skipRoot = false) {
|
|
6802
|
+
if (Array.isArray(node)) {
|
|
6803
|
+
for (const value of node) walkObjects(value, visit);
|
|
6804
|
+
return;
|
|
6738
6805
|
}
|
|
6806
|
+
if (node === null || typeof node !== "object") return;
|
|
6807
|
+
const obj = node;
|
|
6808
|
+
if (!skipRoot) visit(obj);
|
|
6809
|
+
for (const value of Object.values(obj)) walkObjects(value, visit);
|
|
6739
6810
|
}
|
|
6740
|
-
function
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6811
|
+
function isSelectObject(value) {
|
|
6812
|
+
return value !== null && typeof value === "object" && value.type === "SELECT";
|
|
6813
|
+
}
|
|
6814
|
+
|
|
6815
|
+
// src/core/primaryOrganizationDmlValidation.ts
|
|
6816
|
+
var PrimaryOrganizationDmlValidationError = class extends Error {
|
|
6817
|
+
constructor() {
|
|
6818
|
+
super(
|
|
6819
|
+
"ArgumentError: PRIMARY_ORGANIZATION() \u306F DML \u306E WHERE \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
6820
|
+
);
|
|
6821
|
+
this.name = "ArgumentError";
|
|
6746
6822
|
}
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6823
|
+
};
|
|
6824
|
+
function validatePrimaryOrganizationDmlStatement(stmt) {
|
|
6825
|
+
const target = stmt.type === "EXPLAIN" ? stmt.query : stmt;
|
|
6826
|
+
switch (target.type) {
|
|
6827
|
+
case "UPDATE":
|
|
6828
|
+
case "DELETE":
|
|
6829
|
+
case "UPSERT":
|
|
6830
|
+
case "UPSERT_SELECT":
|
|
6831
|
+
case "INSERT_SELECT":
|
|
6832
|
+
if (hasPrimaryOrganizationInWhere(target)) {
|
|
6833
|
+
throw new PrimaryOrganizationDmlValidationError();
|
|
6834
|
+
}
|
|
6835
|
+
return;
|
|
6836
|
+
default:
|
|
6837
|
+
return;
|
|
6751
6838
|
}
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
if (
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6839
|
+
}
|
|
6840
|
+
function hasPrimaryOrganizationInWhere(node) {
|
|
6841
|
+
let found = false;
|
|
6842
|
+
walkObjects2(node, (obj) => {
|
|
6843
|
+
if (found || !Object.prototype.hasOwnProperty.call(obj, "where")) return;
|
|
6844
|
+
if (containsPrimaryOrganization(obj.where)) found = true;
|
|
6845
|
+
});
|
|
6846
|
+
return found;
|
|
6847
|
+
}
|
|
6848
|
+
function containsPrimaryOrganization(node) {
|
|
6849
|
+
let found = false;
|
|
6850
|
+
walkObjects2(node, (obj) => {
|
|
6851
|
+
if (obj.type === "KINTONE_FUNC" && obj.name === "PRIMARY_ORGANIZATION") {
|
|
6852
|
+
found = true;
|
|
6763
6853
|
}
|
|
6764
|
-
}
|
|
6765
|
-
planningGuardHook({
|
|
6766
|
-
expandedSetCount: normalized.sets.length,
|
|
6767
|
-
canonicalItemCount: canonicalItems.size
|
|
6768
6854
|
});
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
const refs = [];
|
|
6776
|
-
if (column.type === "FIELD") refs.push(refFromName(column.field));
|
|
6777
|
-
else collectNonAggregateFieldRefs(column, refs);
|
|
6778
|
-
for (const ref of refs) validateDependency(ref, resolve2, canonicalItems, "SELECT");
|
|
6855
|
+
return found;
|
|
6856
|
+
}
|
|
6857
|
+
function walkObjects2(node, visit) {
|
|
6858
|
+
if (Array.isArray(node)) {
|
|
6859
|
+
for (const value of node) walkObjects2(value, visit);
|
|
6860
|
+
return;
|
|
6779
6861
|
}
|
|
6780
|
-
if (
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6862
|
+
if (node === null || typeof node !== "object") return;
|
|
6863
|
+
const obj = node;
|
|
6864
|
+
visit(obj);
|
|
6865
|
+
for (const value of Object.values(obj)) walkObjects2(value, visit);
|
|
6866
|
+
}
|
|
6867
|
+
|
|
6868
|
+
// src/core/functionArity.ts
|
|
6869
|
+
function assertArity(func, args, min, max = Number.POSITIVE_INFINITY) {
|
|
6870
|
+
if (args.length >= min && args.length <= max) return;
|
|
6871
|
+
const expected = min === max ? String(min) : max === Number.POSITIVE_INFINITY ? `${min} or more` : `${min} to ${max}`;
|
|
6872
|
+
throw new Error(`ArgumentError: ${func} expects ${expected} argument(s).`);
|
|
6873
|
+
}
|
|
6874
|
+
function assertStringFunctionArity(func, args) {
|
|
6875
|
+
switch (func) {
|
|
6876
|
+
case "UPPER":
|
|
6877
|
+
case "LOWER":
|
|
6878
|
+
case "TRIM":
|
|
6879
|
+
case "LTRIM":
|
|
6880
|
+
case "RTRIM":
|
|
6881
|
+
case "LENGTH":
|
|
6882
|
+
case "LENGTH_CHAR":
|
|
6883
|
+
case "YEAR":
|
|
6884
|
+
case "MONTH":
|
|
6885
|
+
case "DAY":
|
|
6886
|
+
case "DAYOFWEEK":
|
|
6887
|
+
case "QUARTER":
|
|
6888
|
+
case "WEEK":
|
|
6889
|
+
case "LAST_DAY":
|
|
6890
|
+
case "ABS":
|
|
6891
|
+
case "SQRT":
|
|
6892
|
+
return assertArity(func, args, 1, 1);
|
|
6893
|
+
case "SUBSTRING":
|
|
6894
|
+
case "LPAD":
|
|
6895
|
+
case "RPAD":
|
|
6896
|
+
case "REGEXP_LIKE":
|
|
6897
|
+
case "REGEXP_SUBSTR":
|
|
6898
|
+
return assertArity(func, args, 2, 3);
|
|
6899
|
+
case "LEFT":
|
|
6900
|
+
case "RIGHT":
|
|
6901
|
+
case "INSTR":
|
|
6902
|
+
case "ISNULL":
|
|
6903
|
+
case "NULLIF":
|
|
6904
|
+
case "MOD":
|
|
6905
|
+
case "POWER":
|
|
6906
|
+
case "FORMAT":
|
|
6907
|
+
case "CAST":
|
|
6908
|
+
case "DATE_FORMAT":
|
|
6909
|
+
case "DATEDIFF":
|
|
6910
|
+
return assertArity(func, args, 2, 2);
|
|
6911
|
+
case "CONCAT":
|
|
6912
|
+
case "COALESCE":
|
|
6913
|
+
case "GREATEST":
|
|
6914
|
+
case "LEAST":
|
|
6915
|
+
return assertArity(func, args, 2);
|
|
6916
|
+
case "REPLACE":
|
|
6917
|
+
case "TRANSLATE":
|
|
6918
|
+
case "DATE_ADD":
|
|
6919
|
+
return assertArity(func, args, 3, 3);
|
|
6920
|
+
case "REGEXP_REPLACE":
|
|
6921
|
+
return assertArity(func, args, 3, 5);
|
|
6922
|
+
case "ROUND":
|
|
6923
|
+
case "FLOOR":
|
|
6924
|
+
case "CEIL":
|
|
6925
|
+
case "TRUNCATE":
|
|
6926
|
+
return assertArity(func, args, 1, 2);
|
|
6927
|
+
case "CURRENT_DATE":
|
|
6928
|
+
case "CURRENT_TIMESTAMP":
|
|
6929
|
+
return assertArity(func, args, 0, 0);
|
|
6787
6930
|
}
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6931
|
+
}
|
|
6932
|
+
|
|
6933
|
+
// src/core/statementValidation.ts
|
|
6934
|
+
function validateStatementStatic(stmt) {
|
|
6935
|
+
validateStringFunctionArities(stmt);
|
|
6936
|
+
validatePrimaryOrganizationDmlStatement(stmt);
|
|
6937
|
+
validateKlikeStatement(stmt);
|
|
6938
|
+
}
|
|
6939
|
+
function validateStringFunctionArities(stmt) {
|
|
6940
|
+
const visit = (value) => {
|
|
6941
|
+
if (value === null || typeof value !== "object") return;
|
|
6942
|
+
if (Array.isArray(value)) {
|
|
6943
|
+
value.forEach(visit);
|
|
6944
|
+
return;
|
|
6792
6945
|
}
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
if (item.unqualifiedBridgeKey !== null) collisionKeys.add(item.unqualifiedBridgeKey);
|
|
6798
|
-
}
|
|
6799
|
-
for (const column of stmt.columns) {
|
|
6800
|
-
if (!isAggregateMaterializedAlias(column)) continue;
|
|
6801
|
-
const alias = "alias" in column ? column.alias : null;
|
|
6802
|
-
if (alias === null) continue;
|
|
6803
|
-
if (collisionKeys.has(alias)) {
|
|
6804
|
-
throw new Error(
|
|
6805
|
-
`ArgumentError: B65 aggregate alias ${alias} collides with a grouping runtime key (reason=B65_AGGREGATE_ALIAS_COLLISION).`
|
|
6806
|
-
);
|
|
6946
|
+
const node = value;
|
|
6947
|
+
if (node.type === "STRING_FUNC") {
|
|
6948
|
+
const expr = node;
|
|
6949
|
+
assertStringFunctionArity(expr.func, expr.args);
|
|
6807
6950
|
}
|
|
6808
|
-
|
|
6809
|
-
|
|
6951
|
+
Object.values(node).forEach(visit);
|
|
6952
|
+
};
|
|
6953
|
+
visit(stmt);
|
|
6810
6954
|
}
|
|
6811
6955
|
|
|
6812
6956
|
// src/core/batch.ts
|
|
@@ -7566,6 +7710,7 @@ function replaceNthMatch(input, globalRe, replacement, n) {
|
|
|
7566
7710
|
});
|
|
7567
7711
|
}
|
|
7568
7712
|
function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
|
|
7713
|
+
assertStringFunctionArity(expr.func, expr.args);
|
|
7569
7714
|
const args = expr.args.map((a) => evalStringFuncArg(a, row, resolveFieldType, resolveFieldSemantics2));
|
|
7570
7715
|
switch (expr.func) {
|
|
7571
7716
|
case "UPPER":
|
|
@@ -7581,7 +7726,6 @@ function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
|
|
|
7581
7726
|
case "LENGTH":
|
|
7582
7727
|
return String((args[0] ?? "").length);
|
|
7583
7728
|
case "LENGTH_CHAR":
|
|
7584
|
-
assertArity("LENGTH_CHAR", args, 1, 1);
|
|
7585
7729
|
return String([...args[0] ?? ""].length);
|
|
7586
7730
|
case "SUBSTRING": {
|
|
7587
7731
|
const str = args[0] ?? "";
|
|
@@ -7590,23 +7734,19 @@ function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
|
|
|
7590
7734
|
return sliceSafeRange(str, start, len !== void 0 ? start + len : str.length);
|
|
7591
7735
|
}
|
|
7592
7736
|
case "LEFT": {
|
|
7593
|
-
assertArity("LEFT", args, 2, 2);
|
|
7594
7737
|
const str = args[0];
|
|
7595
7738
|
const n = Math.trunc(Number(args[1]));
|
|
7596
7739
|
return Number.isNaN(n) || n <= 0 ? "" : sliceSafePrefix(str, n);
|
|
7597
7740
|
}
|
|
7598
7741
|
case "RIGHT": {
|
|
7599
|
-
assertArity("RIGHT", args, 2, 2);
|
|
7600
7742
|
const str = args[0];
|
|
7601
7743
|
const n = Math.trunc(Number(args[1]));
|
|
7602
7744
|
return Number.isNaN(n) || n <= 0 ? "" : sliceSafeSuffix(str, n);
|
|
7603
7745
|
}
|
|
7604
7746
|
case "INSTR":
|
|
7605
|
-
assertArity("INSTR", args, 2, 2);
|
|
7606
7747
|
return String(args[0].indexOf(args[1]) + 1);
|
|
7607
7748
|
case "LPAD":
|
|
7608
7749
|
case "RPAD": {
|
|
7609
|
-
assertArity(expr.func, args, 2, 3);
|
|
7610
7750
|
const str = args[0];
|
|
7611
7751
|
const n = Math.trunc(Number(args[1]));
|
|
7612
7752
|
if (Number.isNaN(n) || n <= 0) return "";
|
|
@@ -7618,7 +7758,6 @@ function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
|
|
|
7618
7758
|
}
|
|
7619
7759
|
case "GREATEST":
|
|
7620
7760
|
case "LEAST":
|
|
7621
|
-
assertArity(expr.func, args, 2);
|
|
7622
7761
|
return selectScalarExtreme(args, expr.func === "GREATEST" ? "greatest" : "least");
|
|
7623
7762
|
case "CONCAT":
|
|
7624
7763
|
return args.join("");
|
|
@@ -7629,22 +7768,18 @@ function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
|
|
|
7629
7768
|
return from === "" ? str : str.split(from).join(to);
|
|
7630
7769
|
}
|
|
7631
7770
|
case "REGEXP_LIKE": {
|
|
7632
|
-
assertArity("REGEXP_LIKE", args, 2, 3);
|
|
7633
7771
|
return compileRegexp(args[1], args[2] ?? "").test(args[0]) ? "1" : "0";
|
|
7634
7772
|
}
|
|
7635
7773
|
case "REGEXP_REPLACE": {
|
|
7636
|
-
assertArity("REGEXP_REPLACE", args, 3, 5);
|
|
7637
7774
|
assertRegexpReplacement(args[2]);
|
|
7638
7775
|
const occurrence = parseRegexpOccurrence(args[4]);
|
|
7639
7776
|
const regexp = compileRegexp(args[1], args[3] ?? "", true);
|
|
7640
7777
|
return occurrence === 0 ? args[0].replace(regexp, args[2]) : replaceNthMatch(args[0], regexp, args[2], occurrence);
|
|
7641
7778
|
}
|
|
7642
7779
|
case "REGEXP_SUBSTR": {
|
|
7643
|
-
assertArity("REGEXP_SUBSTR", args, 2, 3);
|
|
7644
7780
|
return compileRegexp(args[1], args[2] ?? "").exec(args[0])?.[0] ?? "";
|
|
7645
7781
|
}
|
|
7646
7782
|
case "TRANSLATE": {
|
|
7647
|
-
assertArity("TRANSLATE", args, 3, 3);
|
|
7648
7783
|
const from = [...args[1]];
|
|
7649
7784
|
const to = [...args[2]];
|
|
7650
7785
|
if (from.length !== to.length) {
|
|
@@ -7671,7 +7806,6 @@ function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
|
|
|
7671
7806
|
case "CEIL":
|
|
7672
7807
|
return applyRoundOp("ceil", Number(args[0] ?? "0"), Number(args[1] ?? "0"));
|
|
7673
7808
|
case "TRUNCATE":
|
|
7674
|
-
assertArity("TRUNCATE", args, 1, 2);
|
|
7675
7809
|
return applyRoundOp("trunc", Number(args[0]), Number(args[1] ?? "0"));
|
|
7676
7810
|
case "CAST": {
|
|
7677
7811
|
const val = args[0] ?? "";
|
|
@@ -7700,13 +7834,10 @@ function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
|
|
|
7700
7834
|
return d.length >= 10 ? String(parseInt(d.slice(8, 10), 10)) : "";
|
|
7701
7835
|
}
|
|
7702
7836
|
case "DAYOFWEEK":
|
|
7703
|
-
assertArity(expr.func, args, 1, 1);
|
|
7704
7837
|
return isValidYmd(args[0]) ? String(dayOfWeekIndex(args[0]) + 1) : "";
|
|
7705
7838
|
case "QUARTER":
|
|
7706
|
-
assertArity(expr.func, args, 1, 1);
|
|
7707
7839
|
return isValidYmd(args[0]) ? String(Math.ceil(Number(args[0].slice(5, 7)) / 3)) : "";
|
|
7708
7840
|
case "WEEK":
|
|
7709
|
-
assertArity(expr.func, args, 1, 1);
|
|
7710
7841
|
return isValidYmd(args[0]) ? String(isoWeekNumber(args[0])) : "";
|
|
7711
7842
|
case "DATE_FORMAT":
|
|
7712
7843
|
return applyDateFormat(args[0] ?? "", args[1] ?? "");
|
|
@@ -7715,7 +7846,6 @@ function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
|
|
|
7715
7846
|
case "DATE_ADD":
|
|
7716
7847
|
return applyDateAdd(args[0] ?? "", Number(args[1] ?? "0"), (args[2] ?? "DAY").toUpperCase());
|
|
7717
7848
|
case "LAST_DAY":
|
|
7718
|
-
assertArity("LAST_DAY", args, 1, 1);
|
|
7719
7849
|
return applyLastDay(args[0]);
|
|
7720
7850
|
case "ABS":
|
|
7721
7851
|
return String(Math.abs(Number(args[0] ?? "0")));
|
|
@@ -7739,11 +7869,6 @@ function evalStringFunc(expr, row, resolveFieldType, resolveFieldSemantics2) {
|
|
|
7739
7869
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
7740
7870
|
}
|
|
7741
7871
|
}
|
|
7742
|
-
function assertArity(func, args, min, max = Number.POSITIVE_INFINITY) {
|
|
7743
|
-
if (args.length >= min && args.length <= max) return;
|
|
7744
|
-
const expected = min === max ? String(min) : max === Number.POSITIVE_INFINITY ? `${min} or more` : `${min} to ${max}`;
|
|
7745
|
-
throw new Error(`ArgumentError: ${func} expects ${expected} argument(s).`);
|
|
7746
|
-
}
|
|
7747
7872
|
function parseDateParts(s) {
|
|
7748
7873
|
return {
|
|
7749
7874
|
y: s.slice(0, 4) || "0000",
|
|
@@ -7904,10 +8029,32 @@ function formatWithComma(num, digits) {
|
|
|
7904
8029
|
return decStr ? `${intFmt}.${decStr}` : intFmt;
|
|
7905
8030
|
}
|
|
7906
8031
|
function evalStringFuncArg(arg, row, resolveFieldType, resolveFieldSemantics2) {
|
|
7907
|
-
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH")
|
|
8032
|
+
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
|
|
8033
|
+
return String(evalMaterializedAggregateOperand(arg, row));
|
|
8034
|
+
}
|
|
7908
8035
|
if (arg.type === "NUMBER") return numberLiteralText(arg);
|
|
7909
8036
|
return String(evalScalarValueExpr(arg, row, resolveFieldType, resolveFieldSemantics2));
|
|
7910
8037
|
}
|
|
8038
|
+
function evalMaterializedAggregateOperand(node, row) {
|
|
8039
|
+
if (node.type === "NUMBER") return node.value;
|
|
8040
|
+
if (node.type === "AGG_REF") {
|
|
8041
|
+
return row[aggregateSyntheticName(node.func, node.distinct, node.arg)] ?? "";
|
|
8042
|
+
}
|
|
8043
|
+
const left = Number(evalMaterializedAggregateOperand(node.left, row));
|
|
8044
|
+
const right = Number(evalMaterializedAggregateOperand(node.right, row));
|
|
8045
|
+
switch (node.op) {
|
|
8046
|
+
case "+":
|
|
8047
|
+
return left + right;
|
|
8048
|
+
case "-":
|
|
8049
|
+
return left - right;
|
|
8050
|
+
case "*":
|
|
8051
|
+
return left * right;
|
|
8052
|
+
case "/":
|
|
8053
|
+
return right !== 0 ? left / right : NaN;
|
|
8054
|
+
case "%":
|
|
8055
|
+
return right !== 0 ? left % right : NaN;
|
|
8056
|
+
}
|
|
8057
|
+
}
|
|
7911
8058
|
function resolveFieldRef(row, field) {
|
|
7912
8059
|
const direct = row[field];
|
|
7913
8060
|
if (direct !== void 0) return direct;
|
|
@@ -8002,7 +8149,7 @@ function semanticsForLeft(left, fieldType, resolveSemantics) {
|
|
|
8002
8149
|
if (left.type === "FIELD") {
|
|
8003
8150
|
return resolveSemantics?.(left) ?? (fieldType ? resolveFieldSemantics({ fieldType }) : syntheticSemantics("string"));
|
|
8004
8151
|
}
|
|
8005
|
-
if (left.type === "ARITH_FIELD") return syntheticSemantics("number");
|
|
8152
|
+
if (left.type === "ARITH_FIELD" || left.type === "AGG_FIELD") return syntheticSemantics("number");
|
|
8006
8153
|
if (left.type === "FUNC_FIELD") {
|
|
8007
8154
|
return syntheticSemantics(NUMERIC_STRING_FUNCTIONS.has(left.expr.func) ? "number" : "string");
|
|
8008
8155
|
}
|
|
@@ -8095,6 +8242,7 @@ function evalLogical(expr, row, resolveFieldType, appliedKlikes, resolveFieldSem
|
|
|
8095
8242
|
}
|
|
8096
8243
|
function resolveField(field, row, resolveFieldType, resolveFieldSemantics2) {
|
|
8097
8244
|
if (field.type === "FUNC_FIELD") return evalStringFunc(field.expr, row);
|
|
8245
|
+
if (field.type === "AGG_FIELD") return String(evalMaterializedAggregateOperand(field.expr, row));
|
|
8098
8246
|
if (field.type === "ARITH_FIELD") return String(evalArithExpr(field.expr, row));
|
|
8099
8247
|
if (field.type === "CASE_FIELD") return evalCaseWhen(field.expr, row, resolveFieldType, resolveFieldSemantics2);
|
|
8100
8248
|
if (field.type === "GROUPING_FIELD") return evalGroupingRef(field.ref, row);
|
|
@@ -8152,12 +8300,20 @@ function evalCaseWhenNullable(expr, row, resolveFieldType, resolveFieldSemantics
|
|
|
8152
8300
|
}
|
|
8153
8301
|
function evalCaseResultNullable(result, row, resolveFieldType, resolveFieldSemantics2) {
|
|
8154
8302
|
if (result.type === "ARRAY") return result.elements.map((entry) => entry.value).join(",");
|
|
8303
|
+
if (result.type === "AGG_REF") {
|
|
8304
|
+
return row[aggregateSyntheticName(result.func, result.distinct, result.arg)] ?? "";
|
|
8305
|
+
}
|
|
8306
|
+
if (result.type === "AGG_ARITH") return row[aggregateOperandLabel(result)] ?? "";
|
|
8155
8307
|
if (result.type === "FIELD_REF") return row[result.field] ?? "";
|
|
8156
8308
|
if (result.type === "ARITH") return evalArithExpr(result, row);
|
|
8157
8309
|
return evalScalarValueExprNullable(result, row, resolveFieldType, resolveFieldSemantics2);
|
|
8158
8310
|
}
|
|
8159
8311
|
function evalCaseResult(result, row, resolveFieldType, resolveFieldSemantics2) {
|
|
8160
8312
|
if (result.type === "ARRAY") return result.elements.map((e) => e.value).join(",");
|
|
8313
|
+
if (result.type === "AGG_REF") {
|
|
8314
|
+
return row[aggregateSyntheticName(result.func, result.distinct, result.arg)] ?? "";
|
|
8315
|
+
}
|
|
8316
|
+
if (result.type === "AGG_ARITH") return row[aggregateOperandLabel(result)] ?? "";
|
|
8161
8317
|
if (result.type === "FIELD_REF") {
|
|
8162
8318
|
return row[result.field] ?? "";
|
|
8163
8319
|
}
|
|
@@ -8423,6 +8579,10 @@ function collectAggregateArgFields2(node, out) {
|
|
|
8423
8579
|
}
|
|
8424
8580
|
function collectCaseResultFields(result, out) {
|
|
8425
8581
|
if (result.type === "ARRAY") return;
|
|
8582
|
+
if (result.type === "AGG_REF" || result.type === "AGG_ARITH") {
|
|
8583
|
+
collectAggOperandFields2(result, out);
|
|
8584
|
+
return;
|
|
8585
|
+
}
|
|
8426
8586
|
if (result.type === "FIELD_REF" || result.type === "ARITH") {
|
|
8427
8587
|
collectArithNode2(result, out);
|
|
8428
8588
|
return;
|
|
@@ -8644,6 +8804,9 @@ function evalCaseResultValue(result, row, fieldType) {
|
|
|
8644
8804
|
if (result.type === "ARRAY") {
|
|
8645
8805
|
return convertArray(result.elements.map((e) => e.value), fieldType);
|
|
8646
8806
|
}
|
|
8807
|
+
if (result.type === "AGG_REF" || result.type === "AGG_ARITH") {
|
|
8808
|
+
throw new Error("InternalError: aggregate CASE result reached DML evaluation.");
|
|
8809
|
+
}
|
|
8647
8810
|
if (result.type === "STRING") {
|
|
8648
8811
|
return convertString2(result.value, fieldType);
|
|
8649
8812
|
}
|
|
@@ -12832,7 +12995,7 @@ function applyFilter(rows, where, resolveFieldType, appliedKlikes, resolveFieldS
|
|
|
12832
12995
|
}
|
|
12833
12996
|
function hasAggregateColumns(columns) {
|
|
12834
12997
|
return columns.some(
|
|
12835
|
-
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr) || c.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate2(c.expr)
|
|
12998
|
+
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "CASE_COL" && containsAggregate2(c.expr) || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr) || c.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate2(c.expr)
|
|
12836
12999
|
);
|
|
12837
13000
|
}
|
|
12838
13001
|
function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind, resolutionPlan, aliasEvaluationContext = {}) {
|
|
@@ -12937,26 +13100,72 @@ function groupingItemValue(item, row) {
|
|
|
12937
13100
|
return row[item.directKey] ?? (item.unqualifiedBridgeKey === null ? void 0 : row[item.unqualifiedBridgeKey]) ?? "";
|
|
12938
13101
|
}
|
|
12939
13102
|
function materializeAggregateColumns(outRow, groupRows, columns, resolveAggSortKind) {
|
|
12940
|
-
for (const col of columns) {
|
|
13103
|
+
for (const [columnIndex, col] of columns.entries()) {
|
|
12941
13104
|
if (col.type === "AGGREGATE") {
|
|
12942
13105
|
const syntheticKey = aggregateSyntheticName(col.func, col.distinct, col.arg);
|
|
12943
13106
|
const value = String(evalAggregate(col.func, col.distinct, col.arg, col.separator, groupRows, resolveAggSortKind));
|
|
12944
13107
|
outRow[col.alias ?? syntheticKey] = value;
|
|
12945
13108
|
if (col.alias) outRow[syntheticKey] = value;
|
|
12946
13109
|
} else if (col.type === "ARITH_AGG_COL") {
|
|
13110
|
+
materializeAggregateDependencies(outRow, groupRows, col.expr, resolveAggSortKind);
|
|
12947
13111
|
const outputKey = col.alias ?? aggArithDefaultKey(col.expr);
|
|
12948
13112
|
outRow[outputKey] = String(evalAggArithExpr(col.expr, groupRows, resolveAggSortKind));
|
|
12949
13113
|
} else if (col.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(col.expr)) {
|
|
13114
|
+
materializeAggregateDependencies(outRow, groupRows, col.expr, resolveAggSortKind);
|
|
12950
13115
|
const outputKey = col.alias ?? stringFuncDefaultKey(col.expr);
|
|
12951
13116
|
const resolvedExpr = resolveAggInStringFuncExpr(col.expr, groupRows, resolveAggSortKind);
|
|
12952
13117
|
outRow[outputKey] = evalStringFunc(resolvedExpr, outRow);
|
|
12953
13118
|
} else if (col.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate2(col.expr)) {
|
|
13119
|
+
materializeAggregateDependencies(outRow, groupRows, col.expr, resolveAggSortKind);
|
|
12954
13120
|
const outputKey = col.alias ?? scalarValueDefaultKey(col.expr);
|
|
12955
13121
|
const resolvedExpr = resolveAggInScalarValue(col.expr, groupRows, resolveAggSortKind);
|
|
12956
13122
|
outRow[outputKey] = String(evalScalarValueExpr(resolvedExpr, outRow));
|
|
13123
|
+
} else if (col.type === "CASE_COL" && containsAggregate2(col.expr)) {
|
|
13124
|
+
materializeAggregateDependencies(outRow, groupRows, col.expr, resolveAggSortKind);
|
|
13125
|
+
const resolvedExpr = resolveAggInCaseExpr(col.expr, groupRows, resolveAggSortKind);
|
|
13126
|
+
const resolveAggregateSemantics = (field) => field.aggregateRef ? aggregateResultSemantics(field.aggregateRef, resolveAggSortKind) : void 0;
|
|
13127
|
+
outRow[caseMaterializedKey(col.alias, columnIndex)] = evalCaseWhen(
|
|
13128
|
+
resolvedExpr,
|
|
13129
|
+
outRow,
|
|
13130
|
+
void 0,
|
|
13131
|
+
resolveAggregateSemantics
|
|
13132
|
+
);
|
|
12957
13133
|
}
|
|
12958
13134
|
}
|
|
12959
13135
|
}
|
|
13136
|
+
function caseMaterializedKey(alias, columnIndex) {
|
|
13137
|
+
return alias ?? `__ksql_case_column_${columnIndex}`;
|
|
13138
|
+
}
|
|
13139
|
+
function collectAggregateRefs(node, out) {
|
|
13140
|
+
if (node === null || typeof node !== "object") return;
|
|
13141
|
+
if (Array.isArray(node)) {
|
|
13142
|
+
node.forEach((value2) => collectAggregateRefs(value2, out));
|
|
13143
|
+
return;
|
|
13144
|
+
}
|
|
13145
|
+
const value = node;
|
|
13146
|
+
if (value["type"] === "AGG_REF") {
|
|
13147
|
+
out.push(value);
|
|
13148
|
+
return;
|
|
13149
|
+
}
|
|
13150
|
+
if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return;
|
|
13151
|
+
Object.values(value).forEach((child) => collectAggregateRefs(child, out));
|
|
13152
|
+
}
|
|
13153
|
+
function materializeAggregateDependencies(outRow, rows, node, resolveAggSortKind) {
|
|
13154
|
+
const refs = [];
|
|
13155
|
+
collectAggregateRefs(node, refs);
|
|
13156
|
+
for (const ref of refs) {
|
|
13157
|
+
const key = aggregateSyntheticName(ref.func, ref.distinct, ref.arg);
|
|
13158
|
+
if (outRow[key] !== void 0) continue;
|
|
13159
|
+
outRow[key] = String(evalAggregate(
|
|
13160
|
+
ref.func,
|
|
13161
|
+
ref.distinct,
|
|
13162
|
+
ref.arg,
|
|
13163
|
+
ref.separator,
|
|
13164
|
+
rows,
|
|
13165
|
+
resolveAggSortKind
|
|
13166
|
+
));
|
|
13167
|
+
}
|
|
13168
|
+
}
|
|
12960
13169
|
function evalGroupByKey(key, row, resolution, columns, aliasEvaluationContext) {
|
|
12961
13170
|
if (key.type === "FIELD_NAME") {
|
|
12962
13171
|
if (!resolution) return row[key.name] ?? "";
|
|
@@ -12997,7 +13206,7 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
|
|
|
12997
13206
|
const raw = row[arg.field];
|
|
12998
13207
|
if (raw === void 0 || raw === "" && func !== "MIN" && func !== "MAX") continue;
|
|
12999
13208
|
strVal = raw;
|
|
13000
|
-
} else if (arg.type === "ARITH" || arg.type === "NUMBER"
|
|
13209
|
+
} else if (arg.type === "ARITH" || arg.type === "NUMBER") {
|
|
13001
13210
|
const n = evalArithExpr(arg, row);
|
|
13002
13211
|
if (isNaN(n)) continue;
|
|
13003
13212
|
strVal = String(n);
|
|
@@ -13129,6 +13338,16 @@ function resolveAggregateArgSemantics(arg, resolver) {
|
|
|
13129
13338
|
const first = results[0];
|
|
13130
13339
|
return results.every((result) => JSON.stringify(result) === JSON.stringify(first)) ? first : kinds[0];
|
|
13131
13340
|
}
|
|
13341
|
+
function aggregateResultSemantics(ref, resolver) {
|
|
13342
|
+
if (ref.func === "COUNT" || ref.func === "SUM" || ref.func === "AVG" || ref.func === "STDDEV_POP" || ref.func === "STDDEV_SAMP" || ref.func === "VAR_POP" || ref.func === "VAR_SAMP" || ref.func === "MEDIAN") {
|
|
13343
|
+
return syntheticSemantics("number");
|
|
13344
|
+
}
|
|
13345
|
+
if (ref.func === "GROUP_CONCAT" || ref.func === "MODE") {
|
|
13346
|
+
return syntheticSemantics("string");
|
|
13347
|
+
}
|
|
13348
|
+
const semantics = ref.arg.type === "WILDCARD" ? "string" : resolveAggregateArgSemantics(ref.arg, resolver) ?? "string";
|
|
13349
|
+
return typeof semantics === "string" ? syntheticSemantics(semantics) : semantics;
|
|
13350
|
+
}
|
|
13132
13351
|
function applyHaving(rows, having, resolveFieldType, resolveFieldSemantics2) {
|
|
13133
13352
|
if (having === null) return rows;
|
|
13134
13353
|
return rows.filter((row) => evalWhere(having, row, resolveFieldType, void 0, resolveFieldSemantics2));
|
|
@@ -13288,7 +13507,7 @@ function buildOrderByAliasEvaluator(columns, scalarCache, resolveFieldType, reso
|
|
|
13288
13507
|
break;
|
|
13289
13508
|
}
|
|
13290
13509
|
case "CASE_COL":
|
|
13291
|
-
evaluators.set(alias, (row) => evalCaseWhen(column.expr, row, resolveFieldType, resolveFieldSemantics2));
|
|
13510
|
+
evaluators.set(alias, (row) => containsAggregate2(column.expr) ? row[alias] ?? "" : evalCaseWhen(column.expr, row, resolveFieldType, resolveFieldSemantics2));
|
|
13292
13511
|
break;
|
|
13293
13512
|
case "SCALAR_VALUE_COL": {
|
|
13294
13513
|
const source = scalarValueDefaultKey(column.expr);
|
|
@@ -13377,7 +13596,7 @@ function evaluateSelectColumnValue(column, row, columnIndex, context = {}) {
|
|
|
13377
13596
|
case "ARITH_COL":
|
|
13378
13597
|
return String(evalArithExpr(column.expr, row));
|
|
13379
13598
|
case "CASE_COL":
|
|
13380
|
-
return evalCaseWhen(
|
|
13599
|
+
return containsAggregate2(column.expr) ? row[caseMaterializedKey(column.alias, columnIndex)] ?? "" : evalCaseWhen(
|
|
13381
13600
|
column.expr,
|
|
13382
13601
|
row,
|
|
13383
13602
|
context.resolveFieldType,
|
|
@@ -13442,13 +13661,14 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns2, r
|
|
|
13442
13661
|
return { rows: projected2, columns: cols };
|
|
13443
13662
|
}
|
|
13444
13663
|
const defaultFieldKeys = buildDefaultFieldOutputKeys(columns);
|
|
13664
|
+
const defaultCaseKeys = buildDefaultCaseOutputKeys(columns);
|
|
13445
13665
|
const hasWildcard = columns.some(
|
|
13446
13666
|
(col) => col.type === "WILDCARD" || col.type === "PARENT_WILDCARD"
|
|
13447
13667
|
);
|
|
13448
|
-
const outputKeys = hasWildcard ? null : computeOutputKeys(columns, defaultFieldKeys);
|
|
13668
|
+
const outputKeys = hasWildcard ? null : computeOutputKeys(columns, defaultFieldKeys, defaultCaseKeys);
|
|
13449
13669
|
const orderedKeys = outputKeys ?? [];
|
|
13450
13670
|
if (hasWildcard && rows.length === 0) {
|
|
13451
|
-
return { rows: [], columns: computeExplicitOutputKeys(columns, defaultFieldKeys) };
|
|
13671
|
+
return { rows: [], columns: computeExplicitOutputKeys(columns, defaultFieldKeys, defaultCaseKeys) };
|
|
13452
13672
|
}
|
|
13453
13673
|
const projected = rows.map((row, rowIdx) => {
|
|
13454
13674
|
const out = {};
|
|
@@ -13515,7 +13735,7 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns2, r
|
|
|
13515
13735
|
break;
|
|
13516
13736
|
}
|
|
13517
13737
|
case "CASE_COL": {
|
|
13518
|
-
const key = outputKeys?.[colIdx] ?? col.alias ?? "case";
|
|
13738
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? defaultCaseKeys.get(colIdx) ?? "case";
|
|
13519
13739
|
out[key] = value;
|
|
13520
13740
|
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
13521
13741
|
break;
|
|
@@ -13556,15 +13776,15 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns2, r
|
|
|
13556
13776
|
});
|
|
13557
13777
|
return { rows: projected, columns: orderedKeys };
|
|
13558
13778
|
}
|
|
13559
|
-
function computeOutputKeys(columns, defaultFieldKeys) {
|
|
13560
|
-
return columns.map((col, colIdx) => computeOutputKey(col, colIdx, defaultFieldKeys));
|
|
13779
|
+
function computeOutputKeys(columns, defaultFieldKeys, defaultCaseKeys) {
|
|
13780
|
+
return columns.map((col, colIdx) => computeOutputKey(col, colIdx, defaultFieldKeys, defaultCaseKeys));
|
|
13561
13781
|
}
|
|
13562
|
-
function computeExplicitOutputKeys(columns, defaultFieldKeys) {
|
|
13782
|
+
function computeExplicitOutputKeys(columns, defaultFieldKeys, defaultCaseKeys) {
|
|
13563
13783
|
const keys = [];
|
|
13564
13784
|
const seen = /* @__PURE__ */ new Set();
|
|
13565
13785
|
for (const [colIdx, col] of columns.entries()) {
|
|
13566
13786
|
if (col.type === "WILDCARD" || col.type === "PARENT_WILDCARD") continue;
|
|
13567
|
-
const key = computeOutputKey(col, colIdx, defaultFieldKeys);
|
|
13787
|
+
const key = computeOutputKey(col, colIdx, defaultFieldKeys, defaultCaseKeys);
|
|
13568
13788
|
if (!seen.has(key)) {
|
|
13569
13789
|
seen.add(key);
|
|
13570
13790
|
keys.push(key);
|
|
@@ -13572,7 +13792,7 @@ function computeExplicitOutputKeys(columns, defaultFieldKeys) {
|
|
|
13572
13792
|
}
|
|
13573
13793
|
return keys;
|
|
13574
13794
|
}
|
|
13575
|
-
function computeOutputKey(col, colIdx, defaultFieldKeys) {
|
|
13795
|
+
function computeOutputKey(col, colIdx, defaultFieldKeys, defaultCaseKeys) {
|
|
13576
13796
|
switch (col.type) {
|
|
13577
13797
|
case "VARIABLE_COL":
|
|
13578
13798
|
throw new Error(`internal error: unresolved SELECT variable @${col.name}`);
|
|
@@ -13587,7 +13807,7 @@ function computeOutputKey(col, colIdx, defaultFieldKeys) {
|
|
|
13587
13807
|
case "ARITH_COL":
|
|
13588
13808
|
return col.alias ?? arithColDefaultKey(col.expr);
|
|
13589
13809
|
case "CASE_COL":
|
|
13590
|
-
return col.alias ?? "case";
|
|
13810
|
+
return col.alias ?? defaultCaseKeys.get(colIdx) ?? "case";
|
|
13591
13811
|
case "STRFUNC_COL":
|
|
13592
13812
|
return col.alias ?? stringFuncDefaultKey(col.expr);
|
|
13593
13813
|
case "SCALAR_VALUE_COL":
|
|
@@ -13603,6 +13823,24 @@ function computeOutputKey(col, colIdx, defaultFieldKeys) {
|
|
|
13603
13823
|
throw new Error("internal: computeOutputKey received a wildcard column");
|
|
13604
13824
|
}
|
|
13605
13825
|
}
|
|
13826
|
+
function buildDefaultCaseOutputKeys(columns) {
|
|
13827
|
+
const used = new Set(columns.flatMap(
|
|
13828
|
+
(column) => "alias" in column && column.alias !== null ? [column.alias] : []
|
|
13829
|
+
));
|
|
13830
|
+
const keys = /* @__PURE__ */ new Map();
|
|
13831
|
+
let suffix = 1;
|
|
13832
|
+
for (const [columnIndex, column] of columns.entries()) {
|
|
13833
|
+
if (column.type !== "CASE_COL" || column.alias !== null) continue;
|
|
13834
|
+
let key;
|
|
13835
|
+
do {
|
|
13836
|
+
key = suffix === 1 ? "case" : `case_${suffix}`;
|
|
13837
|
+
suffix++;
|
|
13838
|
+
} while (used.has(key));
|
|
13839
|
+
used.add(key);
|
|
13840
|
+
keys.set(columnIndex, key);
|
|
13841
|
+
}
|
|
13842
|
+
return keys;
|
|
13843
|
+
}
|
|
13606
13844
|
function buildDefaultFieldOutputKeys(columns) {
|
|
13607
13845
|
const qualifierCollisionCount = /* @__PURE__ */ new Map();
|
|
13608
13846
|
for (const col of columns) {
|
|
@@ -13692,6 +13930,7 @@ function scalarValueHasAggregate2(expr) {
|
|
|
13692
13930
|
return false;
|
|
13693
13931
|
}
|
|
13694
13932
|
function caseResultHasAggregate2(result) {
|
|
13933
|
+
if (result.type === "AGG_REF" || result.type === "AGG_ARITH") return true;
|
|
13695
13934
|
if (result.type === "ARRAY" || result.type === "FIELD_REF" || result.type === "ARITH") return false;
|
|
13696
13935
|
return scalarValueHasAggregate2(result);
|
|
13697
13936
|
}
|
|
@@ -13714,6 +13953,7 @@ function resolveAggInStringFuncArg(arg, rows, resolveAggSortKind) {
|
|
|
13714
13953
|
}
|
|
13715
13954
|
function resolveAggInScalarValue(expr, rows, resolveAggSortKind) {
|
|
13716
13955
|
if (expr.type === "STRING_FUNC") return resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind);
|
|
13956
|
+
if (expr.type === "CASE_WHEN") return resolveAggInCaseExpr(expr, rows, resolveAggSortKind);
|
|
13717
13957
|
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
13718
13958
|
return {
|
|
13719
13959
|
...expr,
|
|
@@ -13723,6 +13963,35 @@ function resolveAggInScalarValue(expr, rows, resolveAggSortKind) {
|
|
|
13723
13963
|
}
|
|
13724
13964
|
return expr;
|
|
13725
13965
|
}
|
|
13966
|
+
function resolveAggInCaseResult(result, rows, resolveAggSortKind) {
|
|
13967
|
+
if (result.type === "AGG_REF") {
|
|
13968
|
+
const value = evalAggregate(
|
|
13969
|
+
result.func,
|
|
13970
|
+
result.distinct,
|
|
13971
|
+
result.arg,
|
|
13972
|
+
result.separator,
|
|
13973
|
+
rows,
|
|
13974
|
+
resolveAggSortKind
|
|
13975
|
+
);
|
|
13976
|
+
return typeof value === "number" ? { type: "NUMBER", value, raw: String(value) } : { type: "STRING", value };
|
|
13977
|
+
}
|
|
13978
|
+
if (result.type === "AGG_ARITH") {
|
|
13979
|
+
const value = evalAggArithExpr(result, rows, resolveAggSortKind);
|
|
13980
|
+
return { type: "NUMBER", value, raw: String(value) };
|
|
13981
|
+
}
|
|
13982
|
+
if (result.type === "ARRAY" || result.type === "FIELD_REF" || result.type === "ARITH") return result;
|
|
13983
|
+
return resolveAggInScalarValue(result, rows, resolveAggSortKind);
|
|
13984
|
+
}
|
|
13985
|
+
function resolveAggInCaseExpr(expr, rows, resolveAggSortKind) {
|
|
13986
|
+
return {
|
|
13987
|
+
...expr,
|
|
13988
|
+
branches: expr.branches.map((branch) => ({
|
|
13989
|
+
...branch,
|
|
13990
|
+
result: resolveAggInCaseResult(branch.result, rows, resolveAggSortKind)
|
|
13991
|
+
})),
|
|
13992
|
+
elseResult: expr.elseResult === null ? null : resolveAggInCaseResult(expr.elseResult, rows, resolveAggSortKind)
|
|
13993
|
+
};
|
|
13994
|
+
}
|
|
13726
13995
|
function resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind) {
|
|
13727
13996
|
return {
|
|
13728
13997
|
type: "STRING_FUNC",
|
|
@@ -13845,7 +14114,8 @@ function runFullScan(input) {
|
|
|
13845
14114
|
}
|
|
13846
14115
|
);
|
|
13847
14116
|
}
|
|
13848
|
-
|
|
14117
|
+
const resolveHavingSemantics = (field) => field.aggregateRef ? aggregateResultSemantics(field.aggregateRef, aggregateSortKindResolver) : havingFieldSemanticsResolver?.(field);
|
|
14118
|
+
rows = applyHaving(rows, stmt.having, havingFieldTypeResolver, resolveHavingSemantics);
|
|
13849
14119
|
rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds, effectiveOrderSemantics);
|
|
13850
14120
|
if (stmt.distinct) {
|
|
13851
14121
|
rows = applyDistinct(
|
|
@@ -17387,7 +17657,7 @@ function scalarValueHasFieldRef(expr) {
|
|
|
17387
17657
|
}
|
|
17388
17658
|
if (expr.type === "CASE_WHEN") {
|
|
17389
17659
|
const results = [...expr.branches.map((branch) => branch.result), ...expr.elseResult ? [expr.elseResult] : []];
|
|
17390
|
-
return results.some((result) => result.type !== "ARRAY" && (result.type === "FIELD_REF" || result.type === "ARITH" ? arithHasFieldRef(result) : scalarValueHasFieldRef(result)));
|
|
17660
|
+
return results.some((result) => result.type !== "ARRAY" && (result.type === "FIELD_REF" || result.type === "ARITH" ? arithHasFieldRef(result) : result.type === "AGG_REF" || result.type === "AGG_ARITH" ? true : scalarValueHasFieldRef(result)));
|
|
17391
17661
|
}
|
|
17392
17662
|
return false;
|
|
17393
17663
|
}
|
|
@@ -17939,7 +18209,8 @@ function collectAggregateArgFieldRefs(arg, out) {
|
|
|
17939
18209
|
}
|
|
17940
18210
|
if (arg.type === "CASE_WHEN") {
|
|
17941
18211
|
for (const result of [...arg.branches.map((branch) => branch.result), ...arg.elseResult ? [arg.elseResult] : []]) {
|
|
17942
|
-
if (result.type
|
|
18212
|
+
if (result.type === "AGG_REF" || result.type === "AGG_ARITH") collectAggregateOperandRefs(result, out);
|
|
18213
|
+
else if (result.type !== "ARRAY") collectAggregateArgFieldRefs(result, out);
|
|
17943
18214
|
}
|
|
17944
18215
|
}
|
|
17945
18216
|
}
|
|
@@ -17976,10 +18247,29 @@ function collectScalarAggregateRefs(expr, out) {
|
|
|
17976
18247
|
const results = [...expr.branches.map((branch) => branch.result), ...expr.elseResult ? [expr.elseResult] : []];
|
|
17977
18248
|
for (const result of results) {
|
|
17978
18249
|
if (result.type === "STRING_FUNC") collectStringFuncAggregateRefs(result, out);
|
|
18250
|
+
else if (result.type === "AGG_REF" || result.type === "AGG_ARITH") collectAggregateOperandRefs(result, out);
|
|
17979
18251
|
else if (result.type !== "ARRAY" && result.type !== "FIELD_REF" && result.type !== "ARITH") collectScalarAggregateRefs(result, out);
|
|
17980
18252
|
}
|
|
17981
18253
|
}
|
|
17982
18254
|
}
|
|
18255
|
+
function collectCaseAggregateRefs(expr, out) {
|
|
18256
|
+
const visit = (node) => {
|
|
18257
|
+
if (node === null || typeof node !== "object") return;
|
|
18258
|
+
if (Array.isArray(node)) {
|
|
18259
|
+
node.forEach(visit);
|
|
18260
|
+
return;
|
|
18261
|
+
}
|
|
18262
|
+
const value = node;
|
|
18263
|
+
if (value["type"] === "AGG_REF") {
|
|
18264
|
+
const aggregate = value;
|
|
18265
|
+
collectAggregateRef(aggregate.func, aggregate.arg, out);
|
|
18266
|
+
return;
|
|
18267
|
+
}
|
|
18268
|
+
if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return;
|
|
18269
|
+
Object.values(value).forEach(visit);
|
|
18270
|
+
};
|
|
18271
|
+
visit(expr);
|
|
18272
|
+
}
|
|
17983
18273
|
function collectSelectAggregateSortRefs(columns) {
|
|
17984
18274
|
const refs = [];
|
|
17985
18275
|
for (const column of columns) {
|
|
@@ -17991,6 +18281,8 @@ function collectSelectAggregateSortRefs(columns) {
|
|
|
17991
18281
|
collectStringFuncAggregateRefs(column.expr, refs);
|
|
17992
18282
|
} else if (column.type === "SCALAR_VALUE_COL") {
|
|
17993
18283
|
collectScalarAggregateRefs(column.expr, refs);
|
|
18284
|
+
} else if (column.type === "CASE_COL") {
|
|
18285
|
+
collectCaseAggregateRefs(column.expr, refs);
|
|
17994
18286
|
}
|
|
17995
18287
|
}
|
|
17996
18288
|
return refs;
|
|
@@ -18168,6 +18460,13 @@ function stringFunctionColumnMeta(expr) {
|
|
|
18168
18460
|
function caseResultColumnMeta(result, resolveField2) {
|
|
18169
18461
|
if (result.type === "STRING") return syntheticColumnMeta("string");
|
|
18170
18462
|
if (result.type === "ARRAY") return unsupportedColumnMeta();
|
|
18463
|
+
if (result.type === "AGG_REF") {
|
|
18464
|
+
if (result.func === "MIN" || result.func === "MAX" || result.func === "MODE") {
|
|
18465
|
+
return result.arg.type === "WILDCARD" ? unknownStringColumnMeta() : inferAggregateArgMeta(result.arg, resolveField2);
|
|
18466
|
+
}
|
|
18467
|
+
return result.func === "GROUP_CONCAT" ? syntheticColumnMeta("string") : syntheticColumnMeta("number");
|
|
18468
|
+
}
|
|
18469
|
+
if (result.type === "AGG_ARITH") return syntheticColumnMeta("number");
|
|
18171
18470
|
if (result.type === "NUMBER" || result.type === "ARITH" || result.type === "SCALAR_ARITH") return syntheticColumnMeta("number");
|
|
18172
18471
|
if (result.type === "STRING_FUNC") return stringFunctionColumnMeta(result);
|
|
18173
18472
|
if (result.type === "FIELD_REF") return resolveField2(aggregateFieldRef(result.field)) ?? unknownStringColumnMeta();
|