@rex0220/kintone-sql-tools 1.2.0 → 1.4.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 +14 -2
- package/dist-cli/ksql.js +1617 -516
- package/dist-mcp/ksql-mcp.js +1315 -243
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +2 -2
package/dist-mcp/ksql-mcp.js
CHANGED
|
@@ -31052,11 +31052,12 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
31052
31052
|
|
|
31053
31053
|
// src/lexer/lexer.ts
|
|
31054
31054
|
var LexError = class extends Error {
|
|
31055
|
-
constructor(message, pos, input) {
|
|
31055
|
+
constructor(message, pos, input, unterminated = false) {
|
|
31056
31056
|
const around = input.slice(Math.max(0, pos - 10), pos + 10);
|
|
31057
31057
|
super(`${message}\uFF08\u4F4D\u7F6E ${pos}\u3001\u524D\u5F8C: \u300C${around}\u300D\uFF09`);
|
|
31058
31058
|
this.pos = pos;
|
|
31059
31059
|
this.input = input;
|
|
31060
|
+
this.unterminated = unterminated;
|
|
31060
31061
|
this.name = "LexError";
|
|
31061
31062
|
}
|
|
31062
31063
|
};
|
|
@@ -31093,6 +31094,7 @@ var Lexer = class {
|
|
|
31093
31094
|
const opTok = this.tryReadOperator(start);
|
|
31094
31095
|
if (opTok) return opTok;
|
|
31095
31096
|
if (isIdentStart(ch)) return this.readIdentOrKeyword(start);
|
|
31097
|
+
if (ch === "#") return this.readHashIdent(start);
|
|
31096
31098
|
throw new LexError(
|
|
31097
31099
|
`\u4E88\u671F\u3057\u306A\u3044\u6587\u5B57 \u300C${ch}\u300D \u3067\u3059`,
|
|
31098
31100
|
this.pos,
|
|
@@ -31121,7 +31123,7 @@ var Lexer = class {
|
|
|
31121
31123
|
this.pos++;
|
|
31122
31124
|
}
|
|
31123
31125
|
}
|
|
31124
|
-
throw new LexError("\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u304C\u9589\u3058\u3089\u308C\u3066\u3044\u307E\u305B\u3093", start, this.input);
|
|
31126
|
+
throw new LexError("\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u304C\u9589\u3058\u3089\u308C\u3066\u3044\u307E\u305B\u3093", start, this.input, true);
|
|
31125
31127
|
}
|
|
31126
31128
|
// ----------------------------------------------------------
|
|
31127
31129
|
// バッククォート識別子: `field name`
|
|
@@ -31141,7 +31143,8 @@ var Lexer = class {
|
|
|
31141
31143
|
throw new LexError(
|
|
31142
31144
|
"\u30D0\u30C3\u30AF\u30AF\u30A9\u30FC\u30C8\u8B58\u5225\u5B50\u304C\u9589\u3058\u3089\u308C\u3066\u3044\u307E\u305B\u3093",
|
|
31143
31145
|
start,
|
|
31144
|
-
this.input
|
|
31146
|
+
this.input,
|
|
31147
|
+
true
|
|
31145
31148
|
);
|
|
31146
31149
|
}
|
|
31147
31150
|
// ----------------------------------------------------------
|
|
@@ -31248,6 +31251,30 @@ var Lexer = class {
|
|
|
31248
31251
|
return this.makeToken(kind, value, start);
|
|
31249
31252
|
}
|
|
31250
31253
|
// ----------------------------------------------------------
|
|
31254
|
+
// 一時テーブル識別子: #temp
|
|
31255
|
+
// # は先頭のみ有効。isIdentStart に # を加えると isIdentContinue 経由で
|
|
31256
|
+
// 識別子の途中(APP#x 等)にも許容されてしまうため、専用分岐で読む。
|
|
31257
|
+
// ----------------------------------------------------------
|
|
31258
|
+
readHashIdent(start) {
|
|
31259
|
+
this.pos++;
|
|
31260
|
+
const next = this.input[this.pos] ?? "";
|
|
31261
|
+
if (!isIdentStart(next)) {
|
|
31262
|
+
throw new LexError("\u300C#\u300D \u306E\u76F4\u5F8C\u306B\u306F\u8B58\u5225\u5B50\u304C\u5FC5\u8981\u3067\u3059", start, this.input);
|
|
31263
|
+
}
|
|
31264
|
+
while (this.pos < this.input.length && isIdentContinue(this.input[this.pos])) {
|
|
31265
|
+
this.pos++;
|
|
31266
|
+
}
|
|
31267
|
+
const value = this.input.slice(start, this.pos);
|
|
31268
|
+
if (this.input[this.pos] === "@") {
|
|
31269
|
+
throw new LexError(
|
|
31270
|
+
`@profile is not allowed on temp table ${value}.`,
|
|
31271
|
+
this.pos,
|
|
31272
|
+
this.input
|
|
31273
|
+
);
|
|
31274
|
+
}
|
|
31275
|
+
return this.makeToken("IDENT" /* IDENT */, value, start);
|
|
31276
|
+
}
|
|
31277
|
+
// ----------------------------------------------------------
|
|
31251
31278
|
// 空白・コメントをスキップ
|
|
31252
31279
|
// ----------------------------------------------------------
|
|
31253
31280
|
skipWhitespaceAndComments() {
|
|
@@ -31264,14 +31291,25 @@ var Lexer = class {
|
|
|
31264
31291
|
continue;
|
|
31265
31292
|
}
|
|
31266
31293
|
if (ch === "/" && this.input[this.pos + 1] === "*") {
|
|
31294
|
+
const commentStart = this.pos;
|
|
31267
31295
|
this.pos += 2;
|
|
31296
|
+
let closed = false;
|
|
31268
31297
|
while (this.pos < this.input.length) {
|
|
31269
31298
|
if (this.input[this.pos] === "*" && this.input[this.pos + 1] === "/") {
|
|
31270
31299
|
this.pos += 2;
|
|
31300
|
+
closed = true;
|
|
31271
31301
|
break;
|
|
31272
31302
|
}
|
|
31273
31303
|
this.pos++;
|
|
31274
31304
|
}
|
|
31305
|
+
if (!closed) {
|
|
31306
|
+
throw new LexError(
|
|
31307
|
+
"\u30D6\u30ED\u30C3\u30AF\u30B3\u30E1\u30F3\u30C8\u304C\u9589\u3058\u3089\u308C\u3066\u3044\u307E\u305B\u3093",
|
|
31308
|
+
commentStart,
|
|
31309
|
+
this.input,
|
|
31310
|
+
true
|
|
31311
|
+
);
|
|
31312
|
+
}
|
|
31275
31313
|
continue;
|
|
31276
31314
|
}
|
|
31277
31315
|
break;
|
|
@@ -31305,6 +31343,7 @@ function isJapanese(cp) {
|
|
|
31305
31343
|
}
|
|
31306
31344
|
|
|
31307
31345
|
// src/parser/parser.ts
|
|
31346
|
+
var MAX_BATCH_STATEMENTS = 20;
|
|
31308
31347
|
var ParseError = class extends Error {
|
|
31309
31348
|
constructor(message, token) {
|
|
31310
31349
|
super(`${message}\uFF08\u4F4D\u7F6E ${token.pos}\u3001\u30C8\u30FC\u30AF\u30F3: \u300C${token.value}\u300D\uFF09`);
|
|
@@ -31318,15 +31357,54 @@ var Parser = class {
|
|
|
31318
31357
|
this.pos = 0;
|
|
31319
31358
|
/** WITH 句で定義された CTE 名のセット(parseTableRef で参照) */
|
|
31320
31359
|
this.cteNames = /* @__PURE__ */ new Set();
|
|
31360
|
+
/** パース中に出現した一時テーブル参照(#name)のトークン。単文 API での拒否に使う */
|
|
31361
|
+
this.tempTableRefs = [];
|
|
31321
31362
|
}
|
|
31322
31363
|
// ----------------------------------------------------------
|
|
31323
31364
|
// 公開 API
|
|
31324
31365
|
// ----------------------------------------------------------
|
|
31366
|
+
/** 単文をパースする(従来 API。複文が渡されたらエラー) */
|
|
31325
31367
|
parse() {
|
|
31326
|
-
const
|
|
31327
|
-
if (
|
|
31368
|
+
const stmts = this.parseStatements();
|
|
31369
|
+
if (stmts.length === 0) {
|
|
31370
|
+
throw new ParseError("SQL \u6587\u304C\u3042\u308A\u307E\u305B\u3093", this.peek());
|
|
31371
|
+
}
|
|
31372
|
+
if (stmts.length > 1) {
|
|
31373
|
+
throw new ParseError(
|
|
31374
|
+
"\u3053\u306E API \u306F\u5358\u6587\u306E\u307F\u53D7\u3051\u4ED8\u3051\u307E\u3059\uFF08\u8907\u6587\u306F\u30D0\u30C3\u30C1\u5B9F\u884C API \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\uFF09",
|
|
31375
|
+
this.peek()
|
|
31376
|
+
);
|
|
31377
|
+
}
|
|
31378
|
+
if (this.tempTableRefs.length > 0) {
|
|
31379
|
+
const tok = this.tempTableRefs[0];
|
|
31380
|
+
throw new ParseError(
|
|
31381
|
+
`temp table ${tok.value} is not defined in this batch.`,
|
|
31382
|
+
tok
|
|
31383
|
+
);
|
|
31384
|
+
}
|
|
31385
|
+
return stmts[0];
|
|
31386
|
+
}
|
|
31387
|
+
/** 複文(`;` 区切り)をパースする。空文はスキップする */
|
|
31388
|
+
parseStatements() {
|
|
31389
|
+
const stmts = [];
|
|
31390
|
+
while (true) {
|
|
31391
|
+
while (this.peek().kind === ";" /* SEMICOLON */) this.advance();
|
|
31392
|
+
if (this.peek().kind === "EOF" /* EOF */) break;
|
|
31393
|
+
const startTok = this.peek();
|
|
31394
|
+
stmts.push(this.parseStatement());
|
|
31395
|
+
if (stmts.length > MAX_BATCH_STATEMENTS) {
|
|
31396
|
+
throw new ParseError(
|
|
31397
|
+
`batch exceeds ${MAX_BATCH_STATEMENTS} statements.`,
|
|
31398
|
+
startTok
|
|
31399
|
+
);
|
|
31400
|
+
}
|
|
31401
|
+
const after = this.peek();
|
|
31402
|
+
if (after.kind !== ";" /* SEMICOLON */ && after.kind !== "EOF" /* EOF */) {
|
|
31403
|
+
throw new ParseError("\u6587\u306E\u533A\u5207\u308A\u306B\u306F ; \u304C\u5FC5\u8981\u3067\u3059", after);
|
|
31404
|
+
}
|
|
31405
|
+
}
|
|
31328
31406
|
this.expect("EOF" /* EOF */);
|
|
31329
|
-
return
|
|
31407
|
+
return stmts;
|
|
31330
31408
|
}
|
|
31331
31409
|
// ----------------------------------------------------------
|
|
31332
31410
|
// Statement ディスパッチ
|
|
@@ -31355,12 +31433,63 @@ var Parser = class {
|
|
|
31355
31433
|
return this.parseDescribe();
|
|
31356
31434
|
case "EXPLAIN" /* EXPLAIN */:
|
|
31357
31435
|
return this.parseExplain();
|
|
31436
|
+
case "IDENT" /* IDENT */: {
|
|
31437
|
+
const upper = tok.value.toUpperCase();
|
|
31438
|
+
if (upper === "CREATE") return this.parseCreateTempTable();
|
|
31439
|
+
if (upper === "DROP") return this.parseDropTempTable();
|
|
31440
|
+
break;
|
|
31441
|
+
}
|
|
31358
31442
|
default:
|
|
31359
|
-
|
|
31360
|
-
|
|
31361
|
-
|
|
31362
|
-
|
|
31443
|
+
break;
|
|
31444
|
+
}
|
|
31445
|
+
throw new ParseError(
|
|
31446
|
+
"SELECT / INSERT / UPDATE / DELETE / REORDER / WITH / SHOW / DESCRIBE / EXPLAIN / CREATE TEMP TABLE / DROP TEMP TABLE \u306E\u3044\u305A\u308C\u304B\u3067\u59CB\u307E\u308B SQL \u6587\u304C\u5FC5\u8981\u3067\u3059",
|
|
31447
|
+
tok
|
|
31448
|
+
);
|
|
31449
|
+
}
|
|
31450
|
+
// ----------------------------------------------------------
|
|
31451
|
+
// CREATE TEMP TABLE / DROP TEMP TABLE(バッチ内一時テーブル)
|
|
31452
|
+
// CREATE / DROP / TEMP / TABLE は予約語にしない(ソフトキーワード)
|
|
31453
|
+
// ----------------------------------------------------------
|
|
31454
|
+
parseCreateTempTable() {
|
|
31455
|
+
this.advance();
|
|
31456
|
+
this.expectSoftKeyword("TEMP", "CREATE \u306E\u5F8C\u306B\u306F TEMP TABLE \u304C\u5FC5\u8981\u3067\u3059\uFF08\u4F8B: CREATE TEMP TABLE #temp AS SELECT ...\uFF09");
|
|
31457
|
+
this.expectSoftKeyword("TABLE", "CREATE TEMP \u306E\u5F8C\u306B\u306F TABLE \u304C\u5FC5\u8981\u3067\u3059");
|
|
31458
|
+
const name = this.parseTempTableName();
|
|
31459
|
+
this.expect("AS" /* AS */, "CREATE TEMP TABLE \u306B\u306F AS SELECT \u304C\u5FC5\u8981\u3067\u3059");
|
|
31460
|
+
const tok = this.peek();
|
|
31461
|
+
let query;
|
|
31462
|
+
if (tok.kind === "WITH" /* WITH */) {
|
|
31463
|
+
query = this.parseWith();
|
|
31464
|
+
} else if (tok.kind === "SELECT" /* SELECT */) {
|
|
31465
|
+
query = this.tryParseUnionChain(this.parseSelect());
|
|
31466
|
+
} else {
|
|
31467
|
+
throw new ParseError("CREATE TEMP TABLE ... AS \u306E\u5F8C\u306B\u306F SELECT / WITH \u304C\u5FC5\u8981\u3067\u3059", tok);
|
|
31363
31468
|
}
|
|
31469
|
+
return { type: "CREATE_TEMP_TABLE", name, query };
|
|
31470
|
+
}
|
|
31471
|
+
parseDropTempTable() {
|
|
31472
|
+
this.advance();
|
|
31473
|
+
this.expectSoftKeyword("TEMP", "DROP \u306E\u5F8C\u306B\u306F TEMP TABLE \u304C\u5FC5\u8981\u3067\u3059\uFF08\u4F8B: DROP TEMP TABLE #temp\uFF09");
|
|
31474
|
+
this.expectSoftKeyword("TABLE", "DROP TEMP \u306E\u5F8C\u306B\u306F TABLE \u304C\u5FC5\u8981\u3067\u3059");
|
|
31475
|
+
const name = this.parseTempTableName();
|
|
31476
|
+
return { type: "DROP_TEMP_TABLE", name };
|
|
31477
|
+
}
|
|
31478
|
+
expectSoftKeyword(word, msg) {
|
|
31479
|
+
const tok = this.peek();
|
|
31480
|
+
if (tok.kind === "IDENT" /* IDENT */ && tok.value.toUpperCase() === word) {
|
|
31481
|
+
this.advance();
|
|
31482
|
+
return;
|
|
31483
|
+
}
|
|
31484
|
+
throw new ParseError(msg, tok);
|
|
31485
|
+
}
|
|
31486
|
+
parseTempTableName() {
|
|
31487
|
+
const tok = this.peek();
|
|
31488
|
+
if (tok.kind === "IDENT" /* IDENT */ && tok.value.startsWith("#")) {
|
|
31489
|
+
this.advance();
|
|
31490
|
+
return tok.value;
|
|
31491
|
+
}
|
|
31492
|
+
throw new ParseError("\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u540D\u306F # \u3067\u59CB\u307E\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08\u4F8B: #temp\uFF09", tok);
|
|
31364
31493
|
}
|
|
31365
31494
|
parseShow() {
|
|
31366
31495
|
this.advance();
|
|
@@ -31917,24 +32046,38 @@ var Parser = class {
|
|
|
31917
32046
|
// FROM / JOIN
|
|
31918
32047
|
// ----------------------------------------------------------
|
|
31919
32048
|
parseTableRef() {
|
|
31920
|
-
const
|
|
32049
|
+
const nameTok = this.peek();
|
|
32050
|
+
const name = this.parseTableName();
|
|
32051
|
+
if (nameTok.kind === "IDENT" /* IDENT */ && name.startsWith("#")) {
|
|
32052
|
+
this.tempTableRefs.push(this.prev());
|
|
32053
|
+
const alias2 = this.consume("AS" /* AS */) ? this.parseTableAliasName() : this.tryParseImplicitAlias();
|
|
32054
|
+
return { appId: 0, alias: alias2, cteName: name };
|
|
32055
|
+
}
|
|
31921
32056
|
if (this.cteNames.has(name)) {
|
|
31922
|
-
const alias2 = this.consume("AS" /* AS */) ? this.
|
|
32057
|
+
const alias2 = this.consume("AS" /* AS */) ? this.parseTableAliasName() : this.tryParseImplicitAlias();
|
|
31923
32058
|
return { appId: 0, alias: alias2, cteName: name };
|
|
31924
32059
|
}
|
|
31925
32060
|
const { appId, subtableCode } = extractTableRef(name, this.prev());
|
|
31926
32061
|
if (subtableCode) {
|
|
31927
|
-
const alias2 = this.consume("AS" /* AS */) ? this.
|
|
32062
|
+
const alias2 = this.consume("AS" /* AS */) ? this.parseTableAliasName() : this.tryParseImplicitAlias();
|
|
31928
32063
|
return { appId, alias: alias2, cteName: null, subtableCode };
|
|
31929
32064
|
}
|
|
31930
32065
|
const implicit = this.tryParseImplicitAlias();
|
|
31931
|
-
const alias = this.consume("AS" /* AS */) ? this.
|
|
32066
|
+
const alias = this.consume("AS" /* AS */) ? this.parseTableAliasName() : implicit ?? name;
|
|
31932
32067
|
return { appId, alias, cteName: null };
|
|
31933
32068
|
}
|
|
32069
|
+
// テーブル alias 名を読む(IDENT / BIDENT)。alias 位置の # は BIDENT でも拒否
|
|
32070
|
+
parseTableAliasName() {
|
|
32071
|
+
const tok = this.peek();
|
|
32072
|
+
if ((tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */) && tok.value.startsWith("#")) {
|
|
32073
|
+
throw new ParseError("\u30A8\u30A4\u30EA\u30A2\u30B9\u540D\u306B # \u3067\u59CB\u307E\u308B\u540D\u524D\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
|
|
32074
|
+
}
|
|
32075
|
+
return this.parseIdentifier();
|
|
32076
|
+
}
|
|
31934
32077
|
tryParseImplicitAlias() {
|
|
31935
32078
|
const k = this.peek().kind;
|
|
31936
32079
|
if (k === "IDENT" /* IDENT */ || k === "BIDENT" /* BIDENT */) {
|
|
31937
|
-
return this.
|
|
32080
|
+
return this.parseTableAliasName();
|
|
31938
32081
|
}
|
|
31939
32082
|
return null;
|
|
31940
32083
|
}
|
|
@@ -32339,6 +32482,7 @@ var Parser = class {
|
|
|
32339
32482
|
parseInsert() {
|
|
32340
32483
|
this.expect("INSERT" /* INSERT */);
|
|
32341
32484
|
this.expect("INTO" /* INTO */);
|
|
32485
|
+
this.rejectTempTableDml();
|
|
32342
32486
|
const name = this.parseIdentifier();
|
|
32343
32487
|
const { appId, subtableCode } = extractTableRef(name, this.prev());
|
|
32344
32488
|
this.expect("(" /* LPAREN */);
|
|
@@ -32364,6 +32508,7 @@ var Parser = class {
|
|
|
32364
32508
|
parseUpsert() {
|
|
32365
32509
|
this.expect("UPSERT" /* UPSERT */);
|
|
32366
32510
|
this.expect("INTO" /* INTO */);
|
|
32511
|
+
this.rejectTempTableDml();
|
|
32367
32512
|
const name = this.parseIdentifier();
|
|
32368
32513
|
const { appId, subtableCode } = extractTableRef(name, this.prev());
|
|
32369
32514
|
if (subtableCode) {
|
|
@@ -32448,6 +32593,7 @@ var Parser = class {
|
|
|
32448
32593
|
// ----------------------------------------------------------
|
|
32449
32594
|
parseUpdate() {
|
|
32450
32595
|
this.expect("UPDATE" /* UPDATE */);
|
|
32596
|
+
this.rejectTempTableDml();
|
|
32451
32597
|
const name = this.parseIdentifier();
|
|
32452
32598
|
const { appId, subtableCode } = extractTableRef(name, this.prev());
|
|
32453
32599
|
this.expect("SET" /* SET */);
|
|
@@ -32523,6 +32669,7 @@ var Parser = class {
|
|
|
32523
32669
|
parseDelete() {
|
|
32524
32670
|
this.expect("DELETE" /* DELETE */);
|
|
32525
32671
|
this.expect("FROM" /* FROM */);
|
|
32672
|
+
this.rejectTempTableDml();
|
|
32526
32673
|
const name = this.parseIdentifier();
|
|
32527
32674
|
const { appId, subtableCode } = extractTableRef(name, this.prev());
|
|
32528
32675
|
const whereTok = this.peek();
|
|
@@ -32541,6 +32688,7 @@ var Parser = class {
|
|
|
32541
32688
|
parseReorder() {
|
|
32542
32689
|
this.expect("REORDER" /* REORDER */);
|
|
32543
32690
|
const all = this.consume("ALL" /* ALL */);
|
|
32691
|
+
this.rejectTempTableDml();
|
|
32544
32692
|
const name = this.parseIdentifier();
|
|
32545
32693
|
const { appId, subtableCode } = extractTableRef(name, this.prev());
|
|
32546
32694
|
if (!subtableCode) {
|
|
@@ -32622,8 +32770,29 @@ var Parser = class {
|
|
|
32622
32770
|
}
|
|
32623
32771
|
return n;
|
|
32624
32772
|
}
|
|
32625
|
-
// 識別子(IDENT / BIDENT
|
|
32773
|
+
// 識別子(IDENT / BIDENT)を読む。# 始まりの一時テーブル名は不可
|
|
32774
|
+
//(temp マーカーはレキサが生成する IDENT のみ。`#field` のような
|
|
32775
|
+
// バッククォート識別子は # で始まる通常フィールド名として許容する)
|
|
32626
32776
|
parseIdentifier() {
|
|
32777
|
+
const tok = this.peek();
|
|
32778
|
+
if (tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */) {
|
|
32779
|
+
if (tok.kind === "IDENT" /* IDENT */ && tok.value.startsWith("#")) {
|
|
32780
|
+
throw new ParseError(
|
|
32781
|
+
"\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u540D\uFF08# \u3067\u59CB\u307E\u308B\u540D\u524D\uFF09\u306F FROM / JOIN / CREATE / DROP TEMP TABLE \u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059",
|
|
32782
|
+
tok
|
|
32783
|
+
);
|
|
32784
|
+
}
|
|
32785
|
+
this.advance();
|
|
32786
|
+
return tok.value;
|
|
32787
|
+
}
|
|
32788
|
+
throw new ParseError(
|
|
32789
|
+
"\u30D5\u30A3\u30FC\u30EB\u30C9\u540D\u307E\u305F\u306F\u30C6\u30FC\u30D6\u30EB\u540D\u304C\u5FC5\u8981\u3067\u3059",
|
|
32790
|
+
tok
|
|
32791
|
+
);
|
|
32792
|
+
}
|
|
32793
|
+
// テーブル名(IDENT / BIDENT)を読む。# 始まりの一時テーブル名を許容する
|
|
32794
|
+
//(一時テーブルを受理してよいのはテーブル参照位置のみ。他は parseIdentifier を使う)
|
|
32795
|
+
parseTableName() {
|
|
32627
32796
|
const tok = this.peek();
|
|
32628
32797
|
if (tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */) {
|
|
32629
32798
|
this.advance();
|
|
@@ -32634,10 +32803,23 @@ var Parser = class {
|
|
|
32634
32803
|
tok
|
|
32635
32804
|
);
|
|
32636
32805
|
}
|
|
32806
|
+
// DML の対象テーブル位置に一時テーブルが指定されていたら拒否する
|
|
32807
|
+
rejectTempTableDml() {
|
|
32808
|
+
const tok = this.peek();
|
|
32809
|
+
if (tok.kind === "IDENT" /* IDENT */ && tok.value.startsWith("#")) {
|
|
32810
|
+
throw new ParseError(
|
|
32811
|
+
`DML on temp table ${tok.value} is not supported.`,
|
|
32812
|
+
tok
|
|
32813
|
+
);
|
|
32814
|
+
}
|
|
32815
|
+
}
|
|
32637
32816
|
// エイリアス名: IDENT / BIDENT に加え、キーワードも許容する
|
|
32638
32817
|
// 例: SELECT SUM(金額) AS avg → "avg" は AVG キーワードだが alias として有効
|
|
32639
32818
|
parseAliasName() {
|
|
32640
32819
|
const tok = this.peek();
|
|
32820
|
+
if (tok.value.startsWith("#")) {
|
|
32821
|
+
throw new ParseError("\u30A8\u30A4\u30EA\u30A2\u30B9\u540D\u306B # \u3067\u59CB\u307E\u308B\u540D\u524D\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
|
|
32822
|
+
}
|
|
32641
32823
|
if (tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */ || KEYWORDS.has(tok.value.toUpperCase())) {
|
|
32642
32824
|
this.advance();
|
|
32643
32825
|
return tok.value.toLowerCase();
|
|
@@ -32694,6 +32876,159 @@ function extractTableRef(name, tok) {
|
|
|
32694
32876
|
return { appId: Number(m[1]), subtableCode: m[2] ?? null };
|
|
32695
32877
|
}
|
|
32696
32878
|
|
|
32879
|
+
// src/core/dmlGuard.ts
|
|
32880
|
+
function getStatementType(stmt) {
|
|
32881
|
+
if (!stmt || typeof stmt !== "object") return "UNKNOWN";
|
|
32882
|
+
const obj = stmt;
|
|
32883
|
+
return typeof obj.type === "string" ? obj.type : "UNKNOWN";
|
|
32884
|
+
}
|
|
32885
|
+
function isDmlType(type) {
|
|
32886
|
+
return type === "INSERT" || type === "INSERT_SELECT" || type === "UPDATE" || type === "DELETE" || type === "UPSERT" || type === "UPSERT_SELECT" || type === "REORDER";
|
|
32887
|
+
}
|
|
32888
|
+
function isReadOnlyType(type) {
|
|
32889
|
+
return type === "SELECT" || type === "UNION" || type === "WITH" || type === "EXPLAIN" || type === "SHOW_APPS" || type === "DESCRIBE" || type === "CREATE_TEMP_TABLE" || type === "DROP_TEMP_TABLE";
|
|
32890
|
+
}
|
|
32891
|
+
function hasWhereClause(stmt) {
|
|
32892
|
+
if (!stmt || typeof stmt !== "object") return false;
|
|
32893
|
+
const obj = stmt;
|
|
32894
|
+
return obj.where !== null && obj.where !== void 0;
|
|
32895
|
+
}
|
|
32896
|
+
function isNoFromSelectStatement(stmt) {
|
|
32897
|
+
if (!stmt || typeof stmt !== "object") return false;
|
|
32898
|
+
const obj = stmt;
|
|
32899
|
+
return obj.type === "SELECT" && obj.from?.appId === 0 && obj.from?.cteName === "__NO_FROM__";
|
|
32900
|
+
}
|
|
32901
|
+
function getInsertValuesCount(stmt) {
|
|
32902
|
+
if (!stmt || typeof stmt !== "object") return null;
|
|
32903
|
+
const obj = stmt;
|
|
32904
|
+
if (obj.type !== "INSERT") return null;
|
|
32905
|
+
return Array.isArray(obj.values) ? obj.values.length : null;
|
|
32906
|
+
}
|
|
32907
|
+
|
|
32908
|
+
// src/core/batch.ts
|
|
32909
|
+
var MAX_TEMP_TABLES = 16;
|
|
32910
|
+
var BatchAnalysisError = class extends Error {
|
|
32911
|
+
constructor(message, statementIndex) {
|
|
32912
|
+
super(message);
|
|
32913
|
+
this.statementIndex = statementIndex;
|
|
32914
|
+
}
|
|
32915
|
+
};
|
|
32916
|
+
function collectRefs(node, tempRefs, appIds) {
|
|
32917
|
+
if (Array.isArray(node)) {
|
|
32918
|
+
for (const v of node) collectRefs(v, tempRefs, appIds);
|
|
32919
|
+
return;
|
|
32920
|
+
}
|
|
32921
|
+
if (node !== null && typeof node === "object") {
|
|
32922
|
+
const obj = node;
|
|
32923
|
+
const cte = obj["cteName"];
|
|
32924
|
+
if (typeof cte === "string" && cte.startsWith("#")) tempRefs.add(cte);
|
|
32925
|
+
const appId = obj["appId"];
|
|
32926
|
+
if (typeof appId === "number" && appId > 0) appIds.add(appId);
|
|
32927
|
+
for (const v of Object.values(obj)) collectRefs(v, tempRefs, appIds);
|
|
32928
|
+
}
|
|
32929
|
+
}
|
|
32930
|
+
function analyzeBatch(statements) {
|
|
32931
|
+
if (statements.length === 0) {
|
|
32932
|
+
throw new BatchAnalysisError("ArgumentError: SQL is empty.", 0);
|
|
32933
|
+
}
|
|
32934
|
+
if (statements.length === 1) {
|
|
32935
|
+
const t = statements[0].type;
|
|
32936
|
+
if (t === "CREATE_TEMP_TABLE" || t === "DROP_TEMP_TABLE") {
|
|
32937
|
+
const verb = t === "CREATE_TEMP_TABLE" ? "CREATE TEMP TABLE" : "DROP TEMP TABLE";
|
|
32938
|
+
throw new BatchAnalysisError(
|
|
32939
|
+
`ArgumentError: ${verb} requires a batch (temp tables are batch-scoped).`,
|
|
32940
|
+
0
|
|
32941
|
+
);
|
|
32942
|
+
}
|
|
32943
|
+
}
|
|
32944
|
+
const defined = /* @__PURE__ */ new Map();
|
|
32945
|
+
const createdOrder = [];
|
|
32946
|
+
const results = [];
|
|
32947
|
+
statements.forEach((stmt, index) => {
|
|
32948
|
+
const statementType = getStatementType(stmt);
|
|
32949
|
+
const created = [];
|
|
32950
|
+
const dropped = [];
|
|
32951
|
+
const refs = /* @__PURE__ */ new Set();
|
|
32952
|
+
const stmtAppIds = /* @__PURE__ */ new Set();
|
|
32953
|
+
const dependsOn = /* @__PURE__ */ new Set();
|
|
32954
|
+
if (stmt.type === "CREATE_TEMP_TABLE") {
|
|
32955
|
+
collectRefs(stmt.query, refs, stmtAppIds);
|
|
32956
|
+
} else if (stmt.type === "DROP_TEMP_TABLE") {
|
|
32957
|
+
} else {
|
|
32958
|
+
collectRefs(stmt, refs, stmtAppIds);
|
|
32959
|
+
}
|
|
32960
|
+
let tempOnlySource = false;
|
|
32961
|
+
if (stmt.type === "INSERT_SELECT" || stmt.type === "UPSERT_SELECT") {
|
|
32962
|
+
const srcTemp = /* @__PURE__ */ new Set();
|
|
32963
|
+
const srcApps = /* @__PURE__ */ new Set();
|
|
32964
|
+
collectRefs(stmt.select, srcTemp, srcApps);
|
|
32965
|
+
tempOnlySource = srcTemp.size > 0 && srcApps.size === 0;
|
|
32966
|
+
}
|
|
32967
|
+
for (const name of refs) {
|
|
32968
|
+
const at = defined.get(name);
|
|
32969
|
+
if (at === void 0) {
|
|
32970
|
+
throw new BatchAnalysisError(
|
|
32971
|
+
`ParseError: temp table ${name} is not defined in this batch.`,
|
|
32972
|
+
index
|
|
32973
|
+
);
|
|
32974
|
+
}
|
|
32975
|
+
dependsOn.add(at);
|
|
32976
|
+
}
|
|
32977
|
+
if (stmt.type === "CREATE_TEMP_TABLE") {
|
|
32978
|
+
if (defined.has(stmt.name)) {
|
|
32979
|
+
throw new BatchAnalysisError(
|
|
32980
|
+
`ParseError: temp table ${stmt.name} is already defined.`,
|
|
32981
|
+
index
|
|
32982
|
+
);
|
|
32983
|
+
}
|
|
32984
|
+
defined.set(stmt.name, index);
|
|
32985
|
+
createdOrder.push(stmt.name);
|
|
32986
|
+
created.push(stmt.name);
|
|
32987
|
+
if (defined.size > MAX_TEMP_TABLES) {
|
|
32988
|
+
throw new BatchAnalysisError(
|
|
32989
|
+
`ParseError: batch exceeds ${MAX_TEMP_TABLES} temp tables.`,
|
|
32990
|
+
index
|
|
32991
|
+
);
|
|
32992
|
+
}
|
|
32993
|
+
}
|
|
32994
|
+
if (stmt.type === "DROP_TEMP_TABLE") {
|
|
32995
|
+
const at = defined.get(stmt.name);
|
|
32996
|
+
if (at === void 0) {
|
|
32997
|
+
throw new BatchAnalysisError(
|
|
32998
|
+
`ParseError: temp table ${stmt.name} is not defined in this batch.`,
|
|
32999
|
+
index
|
|
33000
|
+
);
|
|
33001
|
+
}
|
|
33002
|
+
dependsOn.add(at);
|
|
33003
|
+
dropped.push(stmt.name);
|
|
33004
|
+
defined.delete(stmt.name);
|
|
33005
|
+
}
|
|
33006
|
+
results.push({
|
|
33007
|
+
index,
|
|
33008
|
+
statementType,
|
|
33009
|
+
isDml: isDmlType(statementType),
|
|
33010
|
+
isReadOnly: isReadOnlyType(statementType),
|
|
33011
|
+
hasWhere: hasWhereClause(stmt),
|
|
33012
|
+
insertValuesCount: getInsertValuesCount(stmt),
|
|
33013
|
+
appIds: [...stmtAppIds].sort((a, b) => a - b),
|
|
33014
|
+
tempTablesCreated: created,
|
|
33015
|
+
tempTablesReferenced: [...refs],
|
|
33016
|
+
tempTablesDropped: dropped,
|
|
33017
|
+
dependsOn: [...dependsOn].sort((a, b) => a - b),
|
|
33018
|
+
tempOnlySource,
|
|
33019
|
+
targetAppId: isDmlType(statementType) && typeof stmt.appId === "number" ? stmt.appId : null
|
|
33020
|
+
});
|
|
33021
|
+
});
|
|
33022
|
+
const containsDml = results.some((r) => r.isDml);
|
|
33023
|
+
return {
|
|
33024
|
+
statementCount: statements.length,
|
|
33025
|
+
isReadOnlyBatch: !containsDml && results.every((r) => r.isReadOnly),
|
|
33026
|
+
containsDml,
|
|
33027
|
+
tempTables: createdOrder,
|
|
33028
|
+
statements: results
|
|
33029
|
+
};
|
|
33030
|
+
}
|
|
33031
|
+
|
|
32697
33032
|
// src/engine/pushDownNot.ts
|
|
32698
33033
|
function pushDownNot(expr) {
|
|
32699
33034
|
switch (expr.type) {
|
|
@@ -33722,23 +34057,31 @@ function resolveKintoneFunc(name) {
|
|
|
33722
34057
|
return "";
|
|
33723
34058
|
}
|
|
33724
34059
|
}
|
|
34060
|
+
var likeRegexCache = /* @__PURE__ */ new Map();
|
|
34061
|
+
var LIKE_REGEX_CACHE_MAX = 200;
|
|
33725
34062
|
function matchLike(value, pattern) {
|
|
33726
34063
|
if (!pattern.includes("%") && !pattern.includes("_")) {
|
|
33727
34064
|
return value.includes(pattern);
|
|
33728
34065
|
}
|
|
33729
|
-
let
|
|
33730
|
-
|
|
33731
|
-
|
|
33732
|
-
|
|
33733
|
-
|
|
33734
|
-
|
|
33735
|
-
|
|
33736
|
-
|
|
33737
|
-
|
|
34066
|
+
let regex = likeRegexCache.get(pattern);
|
|
34067
|
+
if (!regex) {
|
|
34068
|
+
let regexStr = "^";
|
|
34069
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
34070
|
+
const ch = pattern[i];
|
|
34071
|
+
if (ch === "%") {
|
|
34072
|
+
regexStr += ".*";
|
|
34073
|
+
} else if (ch === "_") {
|
|
34074
|
+
regexStr += ".";
|
|
34075
|
+
} else {
|
|
34076
|
+
regexStr += ch.replace(/[.+*?^${}()|[\]\\]/g, "\\$&");
|
|
34077
|
+
}
|
|
33738
34078
|
}
|
|
34079
|
+
regexStr += "$";
|
|
34080
|
+
regex = new RegExp(regexStr, "u");
|
|
34081
|
+
if (likeRegexCache.size >= LIKE_REGEX_CACHE_MAX) likeRegexCache.clear();
|
|
34082
|
+
likeRegexCache.set(pattern, regex);
|
|
33739
34083
|
}
|
|
33740
|
-
|
|
33741
|
-
return new RegExp(regexStr, "u").test(value);
|
|
34084
|
+
return regex.test(value);
|
|
33742
34085
|
}
|
|
33743
34086
|
|
|
33744
34087
|
// src/converter/dmlToKintone.ts
|
|
@@ -34166,10 +34509,12 @@ async function fetchPage(fetcher, app, query, fields, pageSize, offset) {
|
|
|
34166
34509
|
return fetcher({ app, query: pageQuery, fields });
|
|
34167
34510
|
}
|
|
34168
34511
|
function buildCursorQuery(baseQuery, cursorId) {
|
|
34169
|
-
if (cursorId <= 0) return baseQuery.trimEnd();
|
|
34170
|
-
const cursor = `$id > ${cursorId} order by $id asc`;
|
|
34171
34512
|
const base = baseQuery.trimEnd();
|
|
34172
|
-
|
|
34513
|
+
if (cursorId <= 0) {
|
|
34514
|
+
return base ? `${base} order by $id asc` : "order by $id asc";
|
|
34515
|
+
}
|
|
34516
|
+
const cursor = `$id > ${cursorId} order by $id asc`;
|
|
34517
|
+
return base ? `(${base}) and ${cursor}` : cursor;
|
|
34173
34518
|
}
|
|
34174
34519
|
function buildPageQuery(query, pageSize, offset) {
|
|
34175
34520
|
const base = query.trimEnd();
|
|
@@ -34322,6 +34667,8 @@ function applyJoin(leftRows, rightRows, join) {
|
|
|
34322
34667
|
else rightIndex.set(k, [rRow]);
|
|
34323
34668
|
}
|
|
34324
34669
|
const result = [];
|
|
34670
|
+
const emptyRight = {};
|
|
34671
|
+
for (const key of Object.keys(rightRows[0] ?? {})) emptyRight[key] = "";
|
|
34325
34672
|
for (const lRow of leftRows) {
|
|
34326
34673
|
const k = lRow[leftKey] ?? "";
|
|
34327
34674
|
const matched = rightIndex.get(k) ?? [];
|
|
@@ -34330,8 +34677,6 @@ function applyJoin(leftRows, rightRows, join) {
|
|
|
34330
34677
|
result.push({ ...lRow, ...rRow });
|
|
34331
34678
|
}
|
|
34332
34679
|
} else if (joinType === "LEFT") {
|
|
34333
|
-
const emptyRight = {};
|
|
34334
|
-
for (const key of Object.keys(rightRows[0] ?? {})) emptyRight[key] = "";
|
|
34335
34680
|
result.push({ ...lRow, ...emptyRight });
|
|
34336
34681
|
}
|
|
34337
34682
|
}
|
|
@@ -34361,8 +34706,10 @@ function applyGroupBy(rows, groupByKeys, columns) {
|
|
|
34361
34706
|
}
|
|
34362
34707
|
for (const col of columns) {
|
|
34363
34708
|
if (col.type === "AGGREGATE") {
|
|
34364
|
-
const
|
|
34365
|
-
|
|
34709
|
+
const syntheticKey = aggregateSyntheticName2(col.func, col.distinct, col.arg);
|
|
34710
|
+
const value = String(evalAggregate(col.func, col.distinct, col.arg, groupRows));
|
|
34711
|
+
outRow[col.alias ?? syntheticKey] = value;
|
|
34712
|
+
if (col.alias) outRow[syntheticKey] = value;
|
|
34366
34713
|
} else if (col.type === "ARITH_AGG_COL") {
|
|
34367
34714
|
const outputKey = col.alias ?? aggArithDefaultKey(col.expr);
|
|
34368
34715
|
outRow[outputKey] = String(evalAggArithExpr(col.expr, groupRows));
|
|
@@ -34407,12 +34754,23 @@ function evalAggregate(func, distinct, arg, rows) {
|
|
|
34407
34754
|
return nums.reduce((a, b) => a + b, 0);
|
|
34408
34755
|
case "AVG":
|
|
34409
34756
|
return nums.length === 0 ? 0 : nums.reduce((a, b) => a + b, 0) / nums.length;
|
|
34757
|
+
// Math.max(...nums) は要素数が多いと RangeError になるためループで求める
|
|
34410
34758
|
case "MAX":
|
|
34411
|
-
return nums.length === 0 ? 0 :
|
|
34759
|
+
return nums.length === 0 ? 0 : maxOf(nums);
|
|
34412
34760
|
case "MIN":
|
|
34413
|
-
return nums.length === 0 ? 0 :
|
|
34761
|
+
return nums.length === 0 ? 0 : minOf(nums);
|
|
34414
34762
|
}
|
|
34415
34763
|
}
|
|
34764
|
+
function maxOf(nums) {
|
|
34765
|
+
let m = nums[0];
|
|
34766
|
+
for (const n of nums) if (n > m) m = n;
|
|
34767
|
+
return m;
|
|
34768
|
+
}
|
|
34769
|
+
function minOf(nums) {
|
|
34770
|
+
let m = nums[0];
|
|
34771
|
+
for (const n of nums) if (n < m) m = n;
|
|
34772
|
+
return m;
|
|
34773
|
+
}
|
|
34416
34774
|
function evalAggArithExpr(node, rows) {
|
|
34417
34775
|
if (node.type === "NUMBER") return node.value;
|
|
34418
34776
|
if (node.type === "AGG_REF") return evalAggregate(node.func, node.distinct, node.arg, rows);
|
|
@@ -34449,43 +34807,89 @@ function applyHaving(rows, having) {
|
|
|
34449
34807
|
return rows.filter((row) => evalWhere(having, row));
|
|
34450
34808
|
}
|
|
34451
34809
|
function applyDistinct(rows, columns) {
|
|
34810
|
+
if (rows.length === 0) return rows;
|
|
34811
|
+
const keyFor = buildDistinctKeyBuilder(rows, columns);
|
|
34452
34812
|
const seen = /* @__PURE__ */ new Set();
|
|
34453
34813
|
return rows.filter((row) => {
|
|
34454
|
-
const key =
|
|
34814
|
+
const key = keyFor(row);
|
|
34455
34815
|
if (seen.has(key)) return false;
|
|
34456
34816
|
seen.add(key);
|
|
34457
34817
|
return true;
|
|
34458
34818
|
});
|
|
34459
34819
|
}
|
|
34460
|
-
function
|
|
34820
|
+
function buildDistinctKeyBuilder(rows, columns) {
|
|
34461
34821
|
if (columns.some((c) => c.type === "WILDCARD")) {
|
|
34462
|
-
|
|
34463
|
-
|
|
34464
|
-
|
|
34465
|
-
for (const col of columns) {
|
|
34466
|
-
if (col.type === "FIELD") {
|
|
34467
|
-
values.push(row[col.field] ?? "");
|
|
34468
|
-
continue;
|
|
34822
|
+
const allKeys = /* @__PURE__ */ new Set();
|
|
34823
|
+
for (const row of rows) {
|
|
34824
|
+
for (const k of Object.keys(row)) allKeys.add(k);
|
|
34469
34825
|
}
|
|
34470
|
-
|
|
34471
|
-
|
|
34472
|
-
|
|
34826
|
+
const keys = [...allKeys].sort();
|
|
34827
|
+
return (row) => JSON.stringify(keys.map((k) => row[k] !== void 0 ? row[k] : null));
|
|
34828
|
+
}
|
|
34829
|
+
let sortedParentKeys = [];
|
|
34830
|
+
if (columns.some((c) => c.type === "PARENT_WILDCARD")) {
|
|
34831
|
+
const parentKeys = /* @__PURE__ */ new Set();
|
|
34832
|
+
for (const row of rows) {
|
|
34833
|
+
for (const k of Object.keys(row)) {
|
|
34834
|
+
if (k.startsWith("_p.")) parentKeys.add(k);
|
|
34473
34835
|
}
|
|
34474
34836
|
}
|
|
34837
|
+
sortedParentKeys = [...parentKeys].sort();
|
|
34475
34838
|
}
|
|
34476
|
-
return
|
|
34839
|
+
return (row) => {
|
|
34840
|
+
const values = [];
|
|
34841
|
+
for (const col of columns) {
|
|
34842
|
+
if (col.type === "FIELD") {
|
|
34843
|
+
values.push(row[col.field] ?? "");
|
|
34844
|
+
continue;
|
|
34845
|
+
}
|
|
34846
|
+
if (col.type === "PARENT_WILDCARD") {
|
|
34847
|
+
for (const k of sortedParentKeys) {
|
|
34848
|
+
values.push(row[k] !== void 0 ? row[k] : null);
|
|
34849
|
+
}
|
|
34850
|
+
}
|
|
34851
|
+
}
|
|
34852
|
+
return JSON.stringify(values);
|
|
34853
|
+
};
|
|
34477
34854
|
}
|
|
34478
34855
|
function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
|
|
34479
34856
|
if (orderBy.length === 0) return rows;
|
|
34480
|
-
|
|
34481
|
-
|
|
34482
|
-
|
|
34483
|
-
|
|
34484
|
-
|
|
34485
|
-
|
|
34857
|
+
const keyMeta = orderBy.map(({ key }) => ({
|
|
34858
|
+
orderMap: key.type === "FIELD_NAME" ? optionOrders?.get(key.name) : void 0,
|
|
34859
|
+
sortKind: key.type === "FIELD_NAME" ? sortKinds?.get(key.name) : void 0
|
|
34860
|
+
}));
|
|
34861
|
+
const decorated = rows.map((row) => ({
|
|
34862
|
+
row,
|
|
34863
|
+
keys: orderBy.map(({ key }, i) => {
|
|
34864
|
+
const s = evalOrderKey(key, row);
|
|
34865
|
+
const n = Number(s);
|
|
34866
|
+
const orderMap = keyMeta[i].orderMap;
|
|
34867
|
+
return {
|
|
34868
|
+
s,
|
|
34869
|
+
n,
|
|
34870
|
+
isNum: !Number.isNaN(n),
|
|
34871
|
+
rank: orderMap ? minChoiceIndex(parseChoiceValues(s), orderMap) : 0
|
|
34872
|
+
};
|
|
34873
|
+
})
|
|
34874
|
+
}));
|
|
34875
|
+
decorated.sort((a, b) => {
|
|
34876
|
+
for (let i = 0; i < orderBy.length; i++) {
|
|
34877
|
+
const cmp = compareSortKeys(a.keys[i], b.keys[i], keyMeta[i]);
|
|
34878
|
+
if (cmp !== 0) return orderBy[i].direction === "ASC" ? cmp : -cmp;
|
|
34486
34879
|
}
|
|
34487
34880
|
return 0;
|
|
34488
34881
|
});
|
|
34882
|
+
return decorated.map((d) => d.row);
|
|
34883
|
+
}
|
|
34884
|
+
function compareSortKeys(a, b, meta3) {
|
|
34885
|
+
if (meta3.orderMap) {
|
|
34886
|
+
if (a.rank !== b.rank) return a.rank - b.rank;
|
|
34887
|
+
return a.s.localeCompare(b.s, "ja");
|
|
34888
|
+
}
|
|
34889
|
+
if (meta3.sortKind === "string") {
|
|
34890
|
+
return a.s.localeCompare(b.s, "ja");
|
|
34891
|
+
}
|
|
34892
|
+
return a.isNum && b.isNum ? a.n - b.n : a.s.localeCompare(b.s, "ja");
|
|
34489
34893
|
}
|
|
34490
34894
|
function evalOrderKey(key, row) {
|
|
34491
34895
|
switch (key.type) {
|
|
@@ -34497,32 +34901,6 @@ function evalOrderKey(key, row) {
|
|
|
34497
34901
|
return evalStringFunc(key.expr, row);
|
|
34498
34902
|
}
|
|
34499
34903
|
}
|
|
34500
|
-
function compareOrderValues(av, bv, key, optionOrders, sortKinds) {
|
|
34501
|
-
if (key.type === "FIELD_NAME") {
|
|
34502
|
-
const orderMap = optionOrders?.get(key.name);
|
|
34503
|
-
if (orderMap) {
|
|
34504
|
-
const ac = compareByChoiceOrder(av, bv, orderMap);
|
|
34505
|
-
if (ac !== 0) return ac;
|
|
34506
|
-
return av.localeCompare(bv, "ja");
|
|
34507
|
-
}
|
|
34508
|
-
const sortKind = sortKinds?.get(key.name);
|
|
34509
|
-
if (sortKind === "number") {
|
|
34510
|
-
return compareAsNumber(av, bv);
|
|
34511
|
-
}
|
|
34512
|
-
if (sortKind === "string") {
|
|
34513
|
-
return av.localeCompare(bv, "ja");
|
|
34514
|
-
}
|
|
34515
|
-
}
|
|
34516
|
-
return compareAuto(av, bv);
|
|
34517
|
-
}
|
|
34518
|
-
function compareByChoiceOrder(av, bv, orderMap) {
|
|
34519
|
-
const aValues = parseChoiceValues(av);
|
|
34520
|
-
const bValues = parseChoiceValues(bv);
|
|
34521
|
-
const aRank = minChoiceIndex(aValues, orderMap);
|
|
34522
|
-
const bRank = minChoiceIndex(bValues, orderMap);
|
|
34523
|
-
if (aRank !== bRank) return aRank - bRank;
|
|
34524
|
-
return 0;
|
|
34525
|
-
}
|
|
34526
34904
|
function parseChoiceValues(raw) {
|
|
34527
34905
|
const trimmed = raw.trim();
|
|
34528
34906
|
if (trimmed === "") return [""];
|
|
@@ -34546,18 +34924,6 @@ function minChoiceIndex(values, orderMap) {
|
|
|
34546
34924
|
}
|
|
34547
34925
|
return min;
|
|
34548
34926
|
}
|
|
34549
|
-
function compareAsNumber(av, bv) {
|
|
34550
|
-
const an = Number(av);
|
|
34551
|
-
const bn = Number(bv);
|
|
34552
|
-
const numeric = !Number.isNaN(an) && !Number.isNaN(bn);
|
|
34553
|
-
return numeric ? an - bn : av.localeCompare(bv, "ja");
|
|
34554
|
-
}
|
|
34555
|
-
function compareAuto(av, bv) {
|
|
34556
|
-
const an = Number(av);
|
|
34557
|
-
const bn = Number(bv);
|
|
34558
|
-
const numeric = !Number.isNaN(an) && !Number.isNaN(bn);
|
|
34559
|
-
return numeric ? an - bn : av.localeCompare(bv, "ja");
|
|
34560
|
-
}
|
|
34561
34927
|
function applyLimit(rows, limit, offset) {
|
|
34562
34928
|
const start = offset ?? 0;
|
|
34563
34929
|
if (limit === null) return rows.slice(start);
|
|
@@ -34802,8 +35168,61 @@ function toFlatString(value) {
|
|
|
34802
35168
|
|
|
34803
35169
|
// src/execute.ts
|
|
34804
35170
|
async function execute(sql, client, options = {}) {
|
|
35171
|
+
const metrics = createEmptyMetrics();
|
|
35172
|
+
const countedClient = wrapClientWithMetrics(client, metrics);
|
|
35173
|
+
const startedAt = Date.now();
|
|
35174
|
+
const result = await executeStatement(sql, countedClient, options);
|
|
35175
|
+
metrics.elapsedMs = Date.now() - startedAt;
|
|
35176
|
+
return { ...result, metrics };
|
|
35177
|
+
}
|
|
35178
|
+
function createEmptyMetrics() {
|
|
35179
|
+
return {
|
|
35180
|
+
getCalls: 0,
|
|
35181
|
+
postCalls: 0,
|
|
35182
|
+
putCalls: 0,
|
|
35183
|
+
deleteCalls: 0,
|
|
35184
|
+
fieldCalls: 0,
|
|
35185
|
+
appsCalls: 0,
|
|
35186
|
+
fetchedRows: 0,
|
|
35187
|
+
elapsedMs: 0
|
|
35188
|
+
};
|
|
35189
|
+
}
|
|
35190
|
+
function wrapClientWithMetrics(client, metrics) {
|
|
35191
|
+
return {
|
|
35192
|
+
getRecords: async (params) => {
|
|
35193
|
+
metrics.getCalls += 1;
|
|
35194
|
+
const res = await client.getRecords(params);
|
|
35195
|
+
metrics.fetchedRows += res.records.length;
|
|
35196
|
+
return res;
|
|
35197
|
+
},
|
|
35198
|
+
postRecords: (params) => {
|
|
35199
|
+
metrics.postCalls += 1;
|
|
35200
|
+
return client.postRecords(params);
|
|
35201
|
+
},
|
|
35202
|
+
putRecords: (params) => {
|
|
35203
|
+
metrics.putCalls += 1;
|
|
35204
|
+
return client.putRecords(params);
|
|
35205
|
+
},
|
|
35206
|
+
deleteRecords: (params) => {
|
|
35207
|
+
metrics.deleteCalls += 1;
|
|
35208
|
+
return client.deleteRecords(params);
|
|
35209
|
+
},
|
|
35210
|
+
getApps: () => {
|
|
35211
|
+
metrics.appsCalls += 1;
|
|
35212
|
+
return client.getApps();
|
|
35213
|
+
},
|
|
35214
|
+
getFields: (appId) => {
|
|
35215
|
+
metrics.fieldCalls += 1;
|
|
35216
|
+
return client.getFields(appId);
|
|
35217
|
+
}
|
|
35218
|
+
};
|
|
35219
|
+
}
|
|
35220
|
+
async function executeStatement(sql, client, options) {
|
|
34805
35221
|
const cacheContext = options.cacheContext ?? "default";
|
|
34806
35222
|
const stmt = parseSql(sql);
|
|
35223
|
+
return executeParsedStatement(stmt, client, options, cacheContext);
|
|
35224
|
+
}
|
|
35225
|
+
async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
34807
35226
|
switch (stmt.type) {
|
|
34808
35227
|
case "SELECT":
|
|
34809
35228
|
return executeSelect(stmt, client, options, cacheContext);
|
|
@@ -34812,7 +35231,7 @@ async function execute(sql, client, options = {}) {
|
|
|
34812
35231
|
case "WITH":
|
|
34813
35232
|
return executeWith(stmt, client, options, cacheContext);
|
|
34814
35233
|
case "INSERT":
|
|
34815
|
-
return executeInsert(stmt, client, cacheContext);
|
|
35234
|
+
return executeInsert(stmt, client, options, cacheContext);
|
|
34816
35235
|
case "INSERT_SELECT":
|
|
34817
35236
|
return executeInsertSelect(stmt, client, options, cacheContext);
|
|
34818
35237
|
case "UPSERT":
|
|
@@ -34831,9 +35250,181 @@ async function execute(sql, client, options = {}) {
|
|
|
34831
35250
|
return executeDescribe(stmt, client, cacheContext);
|
|
34832
35251
|
case "EXPLAIN":
|
|
34833
35252
|
return executeExplain(stmt);
|
|
35253
|
+
// 一時テーブルはバッチスコープのため単文実行では拒否する(executeBatch を使う)
|
|
35254
|
+
case "CREATE_TEMP_TABLE":
|
|
35255
|
+
throw new Error("ArgumentError: CREATE TEMP TABLE requires a batch (temp tables are batch-scoped).");
|
|
35256
|
+
case "DROP_TEMP_TABLE":
|
|
35257
|
+
throw new Error("ArgumentError: DROP TEMP TABLE requires a batch (temp tables are batch-scoped).");
|
|
35258
|
+
}
|
|
35259
|
+
}
|
|
35260
|
+
var TEMP_TABLE_MAX_ROWS = 1e4;
|
|
35261
|
+
var BatchTimeoutError = class extends Error {
|
|
35262
|
+
constructor() {
|
|
35263
|
+
super("TimeoutError: batch timeout exceeded.");
|
|
35264
|
+
}
|
|
35265
|
+
};
|
|
35266
|
+
async function executeBatch(sql, client, options = {}) {
|
|
35267
|
+
const statements = parseSqlBatch(sql);
|
|
35268
|
+
const analysis = analyzeBatch(statements);
|
|
35269
|
+
if (options.continueOnError && analysis.containsDml) {
|
|
35270
|
+
throw new Error("ArgumentError: continueOnError is not allowed for batches containing DML.");
|
|
35271
|
+
}
|
|
35272
|
+
for (const s of analysis.statements) {
|
|
35273
|
+
if (!s.isDml || s.tempTablesReferenced.length === 0) continue;
|
|
35274
|
+
if (s.statementType === "INSERT_SELECT" && s.tempOnlySource) continue;
|
|
35275
|
+
throw new BatchAnalysisError(
|
|
35276
|
+
s.statementType === "INSERT_SELECT" ? `ArgumentError: INSERT_SELECT in a batch must select from temp tables only. (statement ${s.index})` : `ArgumentError: temp table references in ${s.statementType} are not supported yet.`,
|
|
35277
|
+
s.index
|
|
35278
|
+
);
|
|
35279
|
+
}
|
|
35280
|
+
const metrics = createEmptyMetrics();
|
|
35281
|
+
const countedClient = wrapClientWithMetrics(client, metrics);
|
|
35282
|
+
const startedAt = Date.now();
|
|
35283
|
+
const deadline = options.timeoutMs != null ? startedAt + options.timeoutMs : null;
|
|
35284
|
+
const cacheContext = options.cacheContext ?? "default";
|
|
35285
|
+
const tempTables = /* @__PURE__ */ new Map();
|
|
35286
|
+
const results = [];
|
|
35287
|
+
const failed = /* @__PURE__ */ new Set();
|
|
35288
|
+
let aborted2 = null;
|
|
35289
|
+
for (let i = 0; i < statements.length; i++) {
|
|
35290
|
+
const info = analysis.statements[i];
|
|
35291
|
+
const base = { index: i, type: info.statementType };
|
|
35292
|
+
if (aborted2) {
|
|
35293
|
+
results.push({ ...base, status: "skipped", skippedReason: aborted2 });
|
|
35294
|
+
failed.add(i);
|
|
35295
|
+
continue;
|
|
35296
|
+
}
|
|
35297
|
+
const brokenDep = info.dependsOn.find((d) => failed.has(d));
|
|
35298
|
+
if (brokenDep !== void 0) {
|
|
35299
|
+
const depName = analysis.statements[brokenDep].tempTablesCreated[0] ?? `statement ${brokenDep}`;
|
|
35300
|
+
results.push({ ...base, status: "skipped", skippedReason: `dependency: ${depName}` });
|
|
35301
|
+
failed.add(i);
|
|
35302
|
+
continue;
|
|
35303
|
+
}
|
|
35304
|
+
if (deadline !== null && Date.now() >= deadline) {
|
|
35305
|
+
results.push({ ...base, status: "skipped", skippedReason: "timeout" });
|
|
35306
|
+
failed.add(i);
|
|
35307
|
+
aborted2 = "timeout";
|
|
35308
|
+
continue;
|
|
35309
|
+
}
|
|
35310
|
+
try {
|
|
35311
|
+
const remaining = deadline !== null ? deadline - Date.now() : null;
|
|
35312
|
+
const outcome = await runWithDeadline(
|
|
35313
|
+
executeBatchStatement(statements[i], info, countedClient, options, cacheContext, tempTables),
|
|
35314
|
+
remaining
|
|
35315
|
+
);
|
|
35316
|
+
results.push({ ...base, status: "success", ...outcome });
|
|
35317
|
+
} catch (e) {
|
|
35318
|
+
results.push({ ...base, status: "error", error: toBatchStatementError(e) });
|
|
35319
|
+
failed.add(i);
|
|
35320
|
+
if (e instanceof BatchTimeoutError) {
|
|
35321
|
+
aborted2 = "timeout";
|
|
35322
|
+
} else if (!options.continueOnError) {
|
|
35323
|
+
aborted2 = "fail-fast";
|
|
35324
|
+
}
|
|
35325
|
+
}
|
|
35326
|
+
}
|
|
35327
|
+
metrics.elapsedMs = Date.now() - startedAt;
|
|
35328
|
+
return {
|
|
35329
|
+
ok: results.every((r) => r.status === "success"),
|
|
35330
|
+
statementCount: statements.length,
|
|
35331
|
+
statements: results,
|
|
35332
|
+
analysis,
|
|
35333
|
+
metrics
|
|
35334
|
+
};
|
|
35335
|
+
}
|
|
35336
|
+
async function executeBatchStatement(stmt, info, client, options, cacheContext, tempTables) {
|
|
35337
|
+
if (stmt.type === "CREATE_TEMP_TABLE") {
|
|
35338
|
+
const materializeOptions = {
|
|
35339
|
+
...options,
|
|
35340
|
+
maxRecords: options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS,
|
|
35341
|
+
onLimitReached: "error"
|
|
35342
|
+
};
|
|
35343
|
+
const result = await runSelectLike(stmt.query, client, materializeOptions, cacheContext, tempTables);
|
|
35344
|
+
tempTables.set(stmt.name, result.rows);
|
|
35345
|
+
return { tempTable: stmt.name, rowCount: result.rows.length };
|
|
35346
|
+
}
|
|
35347
|
+
if (stmt.type === "DROP_TEMP_TABLE") {
|
|
35348
|
+
tempTables.delete(stmt.name);
|
|
35349
|
+
return { tempTable: stmt.name };
|
|
35350
|
+
}
|
|
35351
|
+
if (stmt.type === "EXPLAIN") {
|
|
35352
|
+
return { result: await executeParsedStatement(stmt, client, options, cacheContext) };
|
|
35353
|
+
}
|
|
35354
|
+
if (info.tempTablesReferenced.length > 0) {
|
|
35355
|
+
if (stmt.type === "SELECT" || stmt.type === "UNION") {
|
|
35356
|
+
return { result: await executeQueryWithCte(stmt, client, options, tempTables, cacheContext) };
|
|
35357
|
+
}
|
|
35358
|
+
if (stmt.type === "WITH") {
|
|
35359
|
+
return { result: await executeWith(stmt, client, options, cacheContext, tempTables) };
|
|
35360
|
+
}
|
|
35361
|
+
if (stmt.type === "INSERT_SELECT") {
|
|
35362
|
+
return { result: await executeInsertSelect(stmt, client, options, cacheContext, tempTables) };
|
|
35363
|
+
}
|
|
35364
|
+
throw new Error(`ArgumentError: temp table references in ${stmt.type} are not supported yet.`);
|
|
35365
|
+
}
|
|
35366
|
+
return { result: await executeParsedStatement(stmt, client, options, cacheContext) };
|
|
35367
|
+
}
|
|
35368
|
+
async function runSelectLike(query, client, options, cacheContext, tempTables) {
|
|
35369
|
+
if (query.type === "WITH") {
|
|
35370
|
+
return executeWith(query, client, options, cacheContext, tempTables);
|
|
35371
|
+
}
|
|
35372
|
+
return executeQueryWithCte(query, client, options, tempTables, cacheContext);
|
|
35373
|
+
}
|
|
35374
|
+
async function runWithDeadline(work, remainingMs) {
|
|
35375
|
+
if (remainingMs === null) return work;
|
|
35376
|
+
if (remainingMs <= 0) {
|
|
35377
|
+
void work.catch(() => {
|
|
35378
|
+
});
|
|
35379
|
+
throw new BatchTimeoutError();
|
|
35380
|
+
}
|
|
35381
|
+
let timer;
|
|
35382
|
+
try {
|
|
35383
|
+
return await Promise.race([
|
|
35384
|
+
work,
|
|
35385
|
+
new Promise((_, reject) => {
|
|
35386
|
+
timer = setTimeout(() => reject(new BatchTimeoutError()), remainingMs);
|
|
35387
|
+
})
|
|
35388
|
+
]);
|
|
35389
|
+
} catch (e) {
|
|
35390
|
+
if (e instanceof BatchTimeoutError) {
|
|
35391
|
+
void work.catch(() => {
|
|
35392
|
+
});
|
|
35393
|
+
}
|
|
35394
|
+
throw e;
|
|
35395
|
+
} finally {
|
|
35396
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
34834
35397
|
}
|
|
34835
35398
|
}
|
|
34836
|
-
|
|
35399
|
+
function toBatchStatementError(e) {
|
|
35400
|
+
if (e instanceof Error) {
|
|
35401
|
+
const name = e.name !== "Error" ? e.name : null;
|
|
35402
|
+
return { code: name ?? codeFromMessagePrefix(e.message), message: e.message };
|
|
35403
|
+
}
|
|
35404
|
+
if (e !== null && typeof e === "object") {
|
|
35405
|
+
const obj = e;
|
|
35406
|
+
const message2 = typeof obj.message === "string" && obj.message.length > 0 ? obj.message : safeJsonStringify(e);
|
|
35407
|
+
const code = typeof obj.code === "string" && obj.code.length > 0 ? obj.code : codeFromMessagePrefix(message2);
|
|
35408
|
+
return { code, message: message2 };
|
|
35409
|
+
}
|
|
35410
|
+
const message = String(e);
|
|
35411
|
+
return { code: codeFromMessagePrefix(message), message };
|
|
35412
|
+
}
|
|
35413
|
+
function codeFromMessagePrefix(message) {
|
|
35414
|
+
return message.match(/^([A-Za-z]+Error):/)?.[1] ?? "Error";
|
|
35415
|
+
}
|
|
35416
|
+
function safeJsonStringify(v) {
|
|
35417
|
+
try {
|
|
35418
|
+
return JSON.stringify(v) ?? String(v);
|
|
35419
|
+
} catch {
|
|
35420
|
+
return String(v);
|
|
35421
|
+
}
|
|
35422
|
+
}
|
|
35423
|
+
function parseSqlBatch(sql) {
|
|
35424
|
+
const tokens = new Lexer(sql).tokenize();
|
|
35425
|
+
return new Parser(tokens).parseStatements();
|
|
35426
|
+
}
|
|
35427
|
+
async function executeSelect(stmt, client, options, cacheContext, cteCache) {
|
|
34837
35428
|
if (isNoFromSelect(stmt)) {
|
|
34838
35429
|
return executeNoFromSelect(stmt);
|
|
34839
35430
|
}
|
|
@@ -34842,7 +35433,7 @@ async function executeSelect(stmt, client, options, cacheContext) {
|
|
|
34842
35433
|
if (mode === "SIMPLE") {
|
|
34843
35434
|
return executeSimpleSelect(stmt, client, options, cacheContext);
|
|
34844
35435
|
} else {
|
|
34845
|
-
return executeFullScanSelect(stmt, client, options, cacheContext);
|
|
35436
|
+
return executeFullScanSelect(stmt, client, options, cacheContext, cteCache);
|
|
34846
35437
|
}
|
|
34847
35438
|
}
|
|
34848
35439
|
function isNoFromSelect(stmt) {
|
|
@@ -34927,8 +35518,7 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
|
|
|
34927
35518
|
}
|
|
34928
35519
|
let rows = records.map((r) => flatten(r, null));
|
|
34929
35520
|
if (!useSingleGet) {
|
|
34930
|
-
const optionOrders = await
|
|
34931
|
-
const sortKinds = await buildSortKindsForSelect(stmt, client, cacheContext);
|
|
35521
|
+
const { optionOrders, sortKinds } = await buildOrderByMetaForSelect(stmt, client, cacheContext);
|
|
34932
35522
|
rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds);
|
|
34933
35523
|
rows = applyLimit(rows, stmt.limit, stmt.offset);
|
|
34934
35524
|
}
|
|
@@ -34956,22 +35546,25 @@ async function validateSelectFieldCodes(stmt, mode, client, cacheContext) {
|
|
|
34956
35546
|
}
|
|
34957
35547
|
}
|
|
34958
35548
|
for (const [appId, fields] of appToFields.entries()) {
|
|
34959
|
-
|
|
35549
|
+
const userFields = [...fields].filter((f) => !isSystemLikeFieldCode(f));
|
|
35550
|
+
if (userFields.length === 0) continue;
|
|
34960
35551
|
const defs = await getFieldsCached(appId, client, cacheContext);
|
|
34961
35552
|
if (defs.length === 0) continue;
|
|
34962
35553
|
const validCodes = new Set(defs.map((d) => d.code));
|
|
34963
|
-
const unknown2 =
|
|
35554
|
+
const unknown2 = userFields.filter((f) => !validCodes.has(f));
|
|
34964
35555
|
if (unknown2.length > 0) {
|
|
34965
35556
|
throw new Error(`ArgumentError: unknown field code(s): ${unknown2.join(", ")} (APP${appId})`);
|
|
34966
35557
|
}
|
|
34967
35558
|
}
|
|
34968
35559
|
}
|
|
34969
|
-
async function executeFullScanSelect(stmt, client, options, cacheContext) {
|
|
35560
|
+
async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache) {
|
|
34970
35561
|
const maxRecords2 = options.maxRecords ?? 1e4;
|
|
34971
35562
|
const warnings = /* @__PURE__ */ new Set();
|
|
34972
35563
|
const parallel = options.fetchParallel ?? 1;
|
|
34973
|
-
await
|
|
34974
|
-
|
|
35564
|
+
await Promise.all([
|
|
35565
|
+
resolveSubqueries(stmt.where, client, options, cacheContext, cteCache),
|
|
35566
|
+
resolveSubqueries(stmt.having, client, options, cacheContext, cteCache)
|
|
35567
|
+
]);
|
|
34975
35568
|
const tableConditions = /* @__PURE__ */ new Map();
|
|
34976
35569
|
if (stmt.where !== null) {
|
|
34977
35570
|
if (stmt.from.alias) {
|
|
@@ -35020,6 +35613,12 @@ async function executeFullScanSelect(stmt, client, options, cacheContext) {
|
|
|
35020
35613
|
onOptJoins.push(join);
|
|
35021
35614
|
}
|
|
35022
35615
|
}
|
|
35616
|
+
const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
35617
|
+
const orderByMetaPromise = buildOrderByMetaForSelect(stmt, client, cacheContext);
|
|
35618
|
+
scalarCachePromise.catch(() => {
|
|
35619
|
+
});
|
|
35620
|
+
orderByMetaPromise.catch(() => {
|
|
35621
|
+
});
|
|
35023
35622
|
const mainRecords = await mainFetch;
|
|
35024
35623
|
const tables = /* @__PURE__ */ new Map();
|
|
35025
35624
|
tables.set(stmt.from.alias, mainRecords);
|
|
@@ -35051,15 +35650,16 @@ async function executeFullScanSelect(stmt, client, options, cacheContext) {
|
|
|
35051
35650
|
);
|
|
35052
35651
|
tables.set(join.table.alias, joinRecords);
|
|
35053
35652
|
}));
|
|
35054
|
-
const scalarCache = await
|
|
35055
|
-
const optionOrders = await
|
|
35056
|
-
const sortKinds = await buildSortKindsForSelect(stmt, client, cacheContext);
|
|
35653
|
+
const scalarCache = await scalarCachePromise;
|
|
35654
|
+
const { optionOrders, sortKinds } = await orderByMetaPromise;
|
|
35057
35655
|
const { rows, columns } = runFullScan({ tables, stmt, scalarCache, optionOrders, sortKinds });
|
|
35058
35656
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
35059
35657
|
}
|
|
35060
35658
|
async function executeUnion(stmt, client, options, cacheContext) {
|
|
35061
|
-
const leftResult =
|
|
35062
|
-
|
|
35659
|
+
const [leftResult, rightResult] = await Promise.all([
|
|
35660
|
+
stmt.left.type === "UNION" ? executeUnion(stmt.left, client, options, cacheContext) : executeSelect(stmt.left, client, options, cacheContext),
|
|
35661
|
+
executeSelect(stmt.right, client, options, cacheContext)
|
|
35662
|
+
]);
|
|
35063
35663
|
const leftCols = leftResult.columns;
|
|
35064
35664
|
const rightCols = rightResult.columns;
|
|
35065
35665
|
const remappedRight = rightResult.rows.map((row) => {
|
|
@@ -35076,17 +35676,17 @@ async function executeUnion(stmt, client, options, cacheContext) {
|
|
|
35076
35676
|
function deduplicateRows(rows, columns) {
|
|
35077
35677
|
const seen = /* @__PURE__ */ new Set();
|
|
35078
35678
|
return rows.filter((row) => {
|
|
35079
|
-
const key = columns.map((c) => row[c] ?? "")
|
|
35679
|
+
const key = JSON.stringify(columns.map((c) => row[c] ?? ""));
|
|
35080
35680
|
if (seen.has(key)) return false;
|
|
35081
35681
|
seen.add(key);
|
|
35082
35682
|
return true;
|
|
35083
35683
|
});
|
|
35084
35684
|
}
|
|
35085
|
-
async function executeWith(stmt, client, options, cacheContext) {
|
|
35086
|
-
if (canInlineSingleCte(stmt)) {
|
|
35685
|
+
async function executeWith(stmt, client, options, cacheContext, seed) {
|
|
35686
|
+
if ((seed == null || seed.size === 0) && canInlineSingleCte(stmt)) {
|
|
35087
35687
|
return executeSelect(buildInlinedQuery(stmt), client, options, cacheContext);
|
|
35088
35688
|
}
|
|
35089
|
-
const cteCache =
|
|
35689
|
+
const cteCache = new Map(seed ?? []);
|
|
35090
35690
|
for (const cte of stmt.ctes) {
|
|
35091
35691
|
let result;
|
|
35092
35692
|
if (cte.query.type === "SHOW_APPS") {
|
|
@@ -35179,8 +35779,10 @@ function stripCteAliasFromFieldValue(fv, alias) {
|
|
|
35179
35779
|
}
|
|
35180
35780
|
async function executeQueryWithCte(query, client, options, cteCache, cacheContext) {
|
|
35181
35781
|
if (query.type === "UNION") {
|
|
35182
|
-
const leftResult = await
|
|
35183
|
-
|
|
35782
|
+
const [leftResult, rightResult] = await Promise.all([
|
|
35783
|
+
executeQueryWithCte(query.left, client, options, cteCache, cacheContext),
|
|
35784
|
+
executeQueryWithCte(query.right, client, options, cteCache, cacheContext)
|
|
35785
|
+
]);
|
|
35184
35786
|
const leftCols = leftResult.columns;
|
|
35185
35787
|
const rightCols = rightResult.columns;
|
|
35186
35788
|
const remapped = rightResult.rows.map((row) => {
|
|
@@ -35196,7 +35798,7 @@ async function executeQueryWithCte(query, client, options, cteCache, cacheContex
|
|
|
35196
35798
|
}
|
|
35197
35799
|
const hasCteRef = query.from.cteName != null || query.joins.some((j) => j.table.cteName != null);
|
|
35198
35800
|
if (!hasCteRef) {
|
|
35199
|
-
return executeSelect(query, client, options, cacheContext);
|
|
35801
|
+
return executeSelect(query, client, options, cacheContext, cteCache);
|
|
35200
35802
|
}
|
|
35201
35803
|
return executeFullScanWithCte(query, client, options, cteCache, cacheContext);
|
|
35202
35804
|
}
|
|
@@ -35204,8 +35806,16 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
35204
35806
|
const maxRecords2 = options.maxRecords ?? 1e4;
|
|
35205
35807
|
const warnings = /* @__PURE__ */ new Set();
|
|
35206
35808
|
const parallel = options.fetchParallel ?? 1;
|
|
35207
|
-
await
|
|
35208
|
-
|
|
35809
|
+
await Promise.all([
|
|
35810
|
+
resolveSubqueries(stmt.where, client, options, cacheContext, cteCache),
|
|
35811
|
+
resolveSubqueries(stmt.having, client, options, cacheContext, cteCache)
|
|
35812
|
+
]);
|
|
35813
|
+
const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
35814
|
+
const orderByMetaPromise = buildOrderByMetaForSelect(stmt, client, cacheContext);
|
|
35815
|
+
scalarCachePromise.catch(() => {
|
|
35816
|
+
});
|
|
35817
|
+
orderByMetaPromise.catch(() => {
|
|
35818
|
+
});
|
|
35209
35819
|
const tables = /* @__PURE__ */ new Map();
|
|
35210
35820
|
if (stmt.from.cteName != null) {
|
|
35211
35821
|
const rows2 = cteCache.get(stmt.from.cteName) ?? [];
|
|
@@ -35252,9 +35862,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
35252
35862
|
}
|
|
35253
35863
|
});
|
|
35254
35864
|
await Promise.all(joinFetches);
|
|
35255
|
-
const scalarCache = await
|
|
35256
|
-
const optionOrders = await
|
|
35257
|
-
const sortKinds = await buildSortKindsForSelect(stmt, client, cacheContext);
|
|
35865
|
+
const scalarCache = await scalarCachePromise;
|
|
35866
|
+
const { optionOrders, sortKinds } = await orderByMetaPromise;
|
|
35258
35867
|
const { rows, columns } = runFullScan({ tables, stmt, scalarCache, optionOrders, sortKinds });
|
|
35259
35868
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
35260
35869
|
}
|
|
@@ -35290,6 +35899,78 @@ async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords2, pa
|
|
|
35290
35899
|
const parentRecords = parentResolved.records;
|
|
35291
35900
|
return expandSubtableRecords(parentRecords, table.subtableCode);
|
|
35292
35901
|
}
|
|
35902
|
+
var UPSERT_IN_CHUNK_SIZE = 50;
|
|
35903
|
+
function normalizeKeyPart(v) {
|
|
35904
|
+
const t = v.trim();
|
|
35905
|
+
if (t !== "" && !Number.isNaN(Number(t))) return String(Number(t));
|
|
35906
|
+
return v;
|
|
35907
|
+
}
|
|
35908
|
+
function upsertCompositeKey(parts) {
|
|
35909
|
+
return JSON.stringify(parts);
|
|
35910
|
+
}
|
|
35911
|
+
function upsertNormalizedKey(parts, numericKey) {
|
|
35912
|
+
return JSON.stringify(parts.map((p, i) => numericKey[i] ? normalizeKeyPart(p) : p));
|
|
35913
|
+
}
|
|
35914
|
+
function lookupUpsertTarget(index, keyParts) {
|
|
35915
|
+
const exact = index.raw.get(upsertCompositeKey(keyParts));
|
|
35916
|
+
if (exact !== void 0) return exact;
|
|
35917
|
+
if (!index.numericKey.some(Boolean)) return void 0;
|
|
35918
|
+
return index.normalized.get(upsertNormalizedKey(keyParts, index.numericKey));
|
|
35919
|
+
}
|
|
35920
|
+
async function resolveUpsertTargets(appId, keyFields, rowKeyValues, client, options, fieldTypes) {
|
|
35921
|
+
const maxRecords2 = options.maxRecords ?? 1e4;
|
|
35922
|
+
const parallel = options.fetchParallel ?? 1;
|
|
35923
|
+
const numericKey = keyFields.map((f) => fieldTypes.get(f) === "NUMBER");
|
|
35924
|
+
const index = { raw: /* @__PURE__ */ new Map(), normalized: /* @__PURE__ */ new Map(), numericKey };
|
|
35925
|
+
const setMax = (map2, key, id) => {
|
|
35926
|
+
const cur = map2.get(key);
|
|
35927
|
+
if (cur === void 0 || id > cur) map2.set(key, id);
|
|
35928
|
+
};
|
|
35929
|
+
const addRecordToIndex = (parts, id) => {
|
|
35930
|
+
setMax(index.raw, upsertCompositeKey(parts), id);
|
|
35931
|
+
if (numericKey.some(Boolean)) {
|
|
35932
|
+
setMax(index.normalized, upsertNormalizedKey(parts, numericKey), id);
|
|
35933
|
+
}
|
|
35934
|
+
};
|
|
35935
|
+
const batchFirstKeys = /* @__PURE__ */ new Set();
|
|
35936
|
+
const perRowKeys = [];
|
|
35937
|
+
const seen = /* @__PURE__ */ new Set();
|
|
35938
|
+
for (const parts of rowKeyValues) {
|
|
35939
|
+
const composite = upsertCompositeKey(parts);
|
|
35940
|
+
if (seen.has(composite)) continue;
|
|
35941
|
+
seen.add(composite);
|
|
35942
|
+
if (parts.some((p) => p === "")) perRowKeys.push(parts);
|
|
35943
|
+
else batchFirstKeys.add(parts[0]);
|
|
35944
|
+
}
|
|
35945
|
+
const fields = ["$id", ...keyFields];
|
|
35946
|
+
for (const chunk2 of splitChunks([...batchFirstKeys], UPSERT_IN_CHUNK_SIZE)) {
|
|
35947
|
+
const query = `${keyFields[0]} in (${chunk2.map(sqlQuote).join(",")})`;
|
|
35948
|
+
const records = await fetchAll(client.getRecords, appId, query, fields, { maxRecords: maxRecords2, parallel });
|
|
35949
|
+
for (const rec of records) {
|
|
35950
|
+
const id = Number(rec["$id"]?.value);
|
|
35951
|
+
if (!Number.isFinite(id)) continue;
|
|
35952
|
+
addRecordToIndex(keyFields.map((f) => toScalarText(rec[f]?.value)), id);
|
|
35953
|
+
}
|
|
35954
|
+
}
|
|
35955
|
+
for (const parts of perRowKeys) {
|
|
35956
|
+
const query = keyFields.map((f, i) => `${f} = ${sqlQuote(parts[i])}`).join(" and ");
|
|
35957
|
+
const existing = await fetchAll(client.getRecords, appId, query, ["$id"], { maxRecords: maxRecords2, parallel });
|
|
35958
|
+
if (existing.length === 0) continue;
|
|
35959
|
+
addRecordToIndex(parts, maxRecordId(existing));
|
|
35960
|
+
}
|
|
35961
|
+
return index;
|
|
35962
|
+
}
|
|
35963
|
+
function maxRecordId(records) {
|
|
35964
|
+
let max = Number.NEGATIVE_INFINITY;
|
|
35965
|
+
for (const r of records) {
|
|
35966
|
+
const n = Number(r["$id"]?.value);
|
|
35967
|
+
if (Number.isFinite(n) && n > max) max = n;
|
|
35968
|
+
}
|
|
35969
|
+
if (!Number.isFinite(max)) {
|
|
35970
|
+
throw new Error("\u30EC\u30B3\u30FC\u30C9\u306B\u6570\u5024\u306E $id \u304C\u542B\u307E\u308C\u3066\u3044\u307E\u305B\u3093\u3002");
|
|
35971
|
+
}
|
|
35972
|
+
return max;
|
|
35973
|
+
}
|
|
35293
35974
|
function toScalarText(value) {
|
|
35294
35975
|
if (typeof value === "string") return value;
|
|
35295
35976
|
if (value === null || value === void 0) return "";
|
|
@@ -35427,6 +36108,16 @@ async function getSortKindMapByApp(appId, client, cacheContext) {
|
|
|
35427
36108
|
setScopedCacheValue(sortKindCache, cacheContext, appId, map2);
|
|
35428
36109
|
return map2;
|
|
35429
36110
|
}
|
|
36111
|
+
async function buildOrderByMetaForSelect(stmt, client, cacheContext) {
|
|
36112
|
+
if (stmt.orderBy.length === 0) {
|
|
36113
|
+
return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map() };
|
|
36114
|
+
}
|
|
36115
|
+
const [optionOrders, sortKinds] = await Promise.all([
|
|
36116
|
+
buildOptionOrdersForSelect(stmt, client, cacheContext),
|
|
36117
|
+
buildSortKindsForSelect(stmt, client, cacheContext)
|
|
36118
|
+
]);
|
|
36119
|
+
return { optionOrders, sortKinds };
|
|
36120
|
+
}
|
|
35430
36121
|
async function buildOptionOrdersForSelect(stmt, client, cacheContext) {
|
|
35431
36122
|
const optionOrders = /* @__PURE__ */ new Map();
|
|
35432
36123
|
const tables = [stmt.from, ...stmt.joins.map((j) => j.table)];
|
|
@@ -35500,9 +36191,9 @@ function convertProcessRowValue(raw, dstFieldType) {
|
|
|
35500
36191
|
}
|
|
35501
36192
|
return raw;
|
|
35502
36193
|
}
|
|
35503
|
-
async function executeInsert(stmt, client, cacheContext) {
|
|
36194
|
+
async function executeInsert(stmt, client, options, cacheContext) {
|
|
35504
36195
|
if (stmt.subtableCode) {
|
|
35505
|
-
return executeInsertSubtable(stmt, client, cacheContext);
|
|
36196
|
+
return executeInsertSubtable(stmt, client, options, cacheContext);
|
|
35506
36197
|
}
|
|
35507
36198
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
35508
36199
|
const batches = insertToPostBatches(stmt, fieldTypes);
|
|
@@ -35517,14 +36208,18 @@ async function executeInsert(stmt, client, cacheContext) {
|
|
|
35517
36208
|
insertedCount: createdIds.flat().length
|
|
35518
36209
|
};
|
|
35519
36210
|
}
|
|
35520
|
-
async function executeInsertSelect(stmt, client, options, cacheContext) {
|
|
35521
|
-
const selectResult = await executeSelect(stmt.select, client, options, cacheContext);
|
|
36211
|
+
async function executeInsertSelect(stmt, client, options, cacheContext, cteCache) {
|
|
36212
|
+
const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
|
|
35522
36213
|
const { rows, columns } = selectResult;
|
|
35523
36214
|
if (columns.length !== stmt.fields.length) {
|
|
35524
36215
|
throw new Error(
|
|
35525
36216
|
`SELECT \u306E\u5217\u6570\uFF08${columns.length}\uFF09\u3068 INSERT \u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u6570\uFF08${stmt.fields.length}\uFF09\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093`
|
|
35526
36217
|
);
|
|
35527
36218
|
}
|
|
36219
|
+
if (options.confirm) {
|
|
36220
|
+
const ok = await options.confirm(rows.length, "INSERT");
|
|
36221
|
+
if (!ok) throw new OperationCancelledError("INSERT", rows.length);
|
|
36222
|
+
}
|
|
35528
36223
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
35529
36224
|
const allRecords = rows.map((row) => {
|
|
35530
36225
|
const record2 = {};
|
|
@@ -35617,26 +36312,19 @@ async function executeDelete(stmt, client, options, cacheContext) {
|
|
|
35617
36312
|
return { type: "DELETE", deletedCount: ids.length };
|
|
35618
36313
|
}
|
|
35619
36314
|
async function executeUpsert(stmt, client, options, cacheContext) {
|
|
35620
|
-
const maxRecords2 = options.maxRecords ?? 1e4;
|
|
35621
36315
|
const toInsert = [];
|
|
35622
36316
|
const toUpdate = [];
|
|
35623
36317
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
35624
|
-
|
|
35625
|
-
|
|
36318
|
+
const rowKeyValues = stmt.values.map(
|
|
36319
|
+
(row) => stmt.keyFields.map((key) => {
|
|
35626
36320
|
const idx = stmt.fields.indexOf(key);
|
|
35627
36321
|
if (idx === -1) throw new Error(`ON DUPLICATE \u306E\u30AD\u30FC\u300C${key}\u300D\u304C INSERT \u30D5\u30A3\u30FC\u30EB\u30C9\u306B\u542B\u307E\u308C\u3066\u3044\u307E\u305B\u3093`);
|
|
35628
36322
|
const val = row[idx];
|
|
35629
|
-
|
|
35630
|
-
|
|
35631
|
-
|
|
35632
|
-
|
|
35633
|
-
|
|
35634
|
-
client.getRecords,
|
|
35635
|
-
stmt.appId,
|
|
35636
|
-
query,
|
|
35637
|
-
["$id"],
|
|
35638
|
-
{ maxRecords: maxRecords2, parallel: options.fetchParallel ?? 1 }
|
|
35639
|
-
);
|
|
36323
|
+
return val.type === "STRING" ? val.value : val.type === "NUMBER" ? String(val.value) : val.type === "CASE_VALUE" ? evalCaseWhen(val.expr, {}) : val.elements.map((e) => e.value).join(",");
|
|
36324
|
+
})
|
|
36325
|
+
);
|
|
36326
|
+
const targetIndex = await resolveUpsertTargets(stmt.appId, stmt.keyFields, rowKeyValues, client, options, fieldTypes);
|
|
36327
|
+
stmt.values.forEach((row, rowIdx) => {
|
|
35640
36328
|
const record2 = {};
|
|
35641
36329
|
stmt.fields.forEach((field, i) => {
|
|
35642
36330
|
const val = row[i];
|
|
@@ -35646,13 +36334,13 @@ async function executeUpsert(stmt, client, options, cacheContext) {
|
|
|
35646
36334
|
record2[field] = { value: toKintoneValue(val, fieldTypes.get(field)) };
|
|
35647
36335
|
}
|
|
35648
36336
|
});
|
|
35649
|
-
|
|
35650
|
-
|
|
36337
|
+
const id = lookupUpsertTarget(targetIndex, rowKeyValues[rowIdx]);
|
|
36338
|
+
if (id !== void 0) {
|
|
35651
36339
|
toUpdate.push({ id, record: record2 });
|
|
35652
36340
|
} else {
|
|
35653
36341
|
toInsert.push(record2);
|
|
35654
36342
|
}
|
|
35655
|
-
}
|
|
36343
|
+
});
|
|
35656
36344
|
if (options.confirm && toInsert.length + toUpdate.length > 0) {
|
|
35657
36345
|
const total = toInsert.length + toUpdate.length;
|
|
35658
36346
|
const ok = await options.confirm(total, "UPDATE");
|
|
@@ -35672,18 +36360,17 @@ async function executeUpsert(stmt, client, options, cacheContext) {
|
|
|
35672
36360
|
updatedCount: toUpdate.length
|
|
35673
36361
|
};
|
|
35674
36362
|
}
|
|
35675
|
-
async function executeInsertSubtable(stmt, client, _cacheContext) {
|
|
36363
|
+
async function executeInsertSubtable(stmt, client, options, _cacheContext) {
|
|
35676
36364
|
const subtableCode = stmt.subtableCode;
|
|
35677
36365
|
const pidIndex = stmt.fields.indexOf("_pid");
|
|
35678
36366
|
if (pidIndex < 0) {
|
|
35679
36367
|
throw new Error("\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB INSERT \u306B\u306F _pid \u304C\u5FC5\u9808\u3067\u3059");
|
|
35680
36368
|
}
|
|
35681
|
-
const parents = await fetchAll(client.getRecords, stmt.appId, "", [], {
|
|
35682
|
-
|
|
35683
|
-
|
|
35684
|
-
|
|
35685
|
-
|
|
35686
|
-
}
|
|
36369
|
+
const parents = await fetchAll(client.getRecords, stmt.appId, "", [], {
|
|
36370
|
+
maxRecords: options.maxRecords ?? 1e4,
|
|
36371
|
+
parallel: options.fetchParallel ?? 1
|
|
36372
|
+
});
|
|
36373
|
+
const parentMap = buildParentIdMap(parents);
|
|
35687
36374
|
const insertsByParent = /* @__PURE__ */ new Map();
|
|
35688
36375
|
for (const rowValues of stmt.values) {
|
|
35689
36376
|
const pid = valueToString(rowValues[pidIndex]);
|
|
@@ -35750,8 +36437,9 @@ async function executeUpdateSubtable(stmt, client, options, _cacheContext) {
|
|
|
35750
36437
|
}
|
|
35751
36438
|
byRid.set(t.rowId, updates);
|
|
35752
36439
|
}
|
|
36440
|
+
const parentById = buildParentIdMap(parents);
|
|
35753
36441
|
for (const [pid, updateMap] of updatesByParent.entries()) {
|
|
35754
|
-
const parent =
|
|
36442
|
+
const parent = parentById.get(pid);
|
|
35755
36443
|
if (!parent) continue;
|
|
35756
36444
|
const currentRows = getMutableTableRows(parent, subtableCode);
|
|
35757
36445
|
const payloadRows = currentRows.map((row) => {
|
|
@@ -35791,8 +36479,9 @@ async function executeDeleteSubtable(stmt, client, options, _cacheContext) {
|
|
|
35791
36479
|
if (bucket) bucket.push(t.rowIndex);
|
|
35792
36480
|
else byParent.set(t.parentId, [t.rowIndex]);
|
|
35793
36481
|
}
|
|
36482
|
+
const parentById = buildParentIdMap(parents);
|
|
35794
36483
|
for (const [pid, idxs] of byParent.entries()) {
|
|
35795
|
-
const parent =
|
|
36484
|
+
const parent = parentById.get(pid);
|
|
35796
36485
|
if (!parent) continue;
|
|
35797
36486
|
const rows = getMutableTableRows(parent, subtableCode);
|
|
35798
36487
|
const rm = new Set(idxs);
|
|
@@ -35801,6 +36490,14 @@ async function executeDeleteSubtable(stmt, client, options, _cacheContext) {
|
|
|
35801
36490
|
}
|
|
35802
36491
|
return { type: "DELETE", deletedCount: targets.length };
|
|
35803
36492
|
}
|
|
36493
|
+
function buildParentIdMap(parents) {
|
|
36494
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
36495
|
+
for (const p of parents) {
|
|
36496
|
+
const pid = String(p["$id"]?.value ?? "");
|
|
36497
|
+
if (pid) map2.set(pid, p);
|
|
36498
|
+
}
|
|
36499
|
+
return map2;
|
|
36500
|
+
}
|
|
35804
36501
|
function expandRowsForSubtableDml(parents, subtableCode) {
|
|
35805
36502
|
const out = [];
|
|
35806
36503
|
for (const parent of parents) {
|
|
@@ -35936,8 +36633,9 @@ async function executeReorder(stmt, client, options, _cacheContext) {
|
|
|
35936
36633
|
const ok = await options.confirm(targetParentIds.size, "UPDATE");
|
|
35937
36634
|
if (!ok) throw new OperationCancelledError("UPDATE", targetParentIds.size);
|
|
35938
36635
|
}
|
|
36636
|
+
const parentById = buildParentIdMap(parents);
|
|
35939
36637
|
for (const pid of targetParentIds) {
|
|
35940
|
-
const parent =
|
|
36638
|
+
const parent = parentById.get(pid);
|
|
35941
36639
|
if (!parent) continue;
|
|
35942
36640
|
const rows = getMutableTableRows(parent, stmt.subtableCode);
|
|
35943
36641
|
const sortable = rows.map((row, i) => ({ row, i, flat: buildFlatRowForSort(parent, stmt.subtableCode, row, i) }));
|
|
@@ -35985,7 +36683,6 @@ function evalOrderKeyForRow(key, row) {
|
|
|
35985
36683
|
}
|
|
35986
36684
|
}
|
|
35987
36685
|
async function executeUpsertSelect(stmt, client, options, cacheContext) {
|
|
35988
|
-
const maxRecords2 = options.maxRecords ?? 1e4;
|
|
35989
36686
|
const selectResult = await executeSelect(stmt.select, client, options, cacheContext);
|
|
35990
36687
|
const { rows, columns } = selectResult;
|
|
35991
36688
|
if (columns.length !== stmt.fields.length) {
|
|
@@ -36000,29 +36697,26 @@ async function executeUpsertSelect(stmt, client, options, cacheContext) {
|
|
|
36000
36697
|
}
|
|
36001
36698
|
const toInsert = [];
|
|
36002
36699
|
const toUpdate = [];
|
|
36003
|
-
|
|
36700
|
+
const records = rows.map((row) => {
|
|
36004
36701
|
const record2 = {};
|
|
36005
36702
|
stmt.fields.forEach((field, i) => {
|
|
36006
36703
|
record2[field] = { value: row[columns[i]] ?? "" };
|
|
36007
36704
|
});
|
|
36008
|
-
|
|
36009
|
-
|
|
36010
|
-
|
|
36011
|
-
|
|
36012
|
-
|
|
36013
|
-
|
|
36014
|
-
|
|
36015
|
-
|
|
36016
|
-
|
|
36017
|
-
|
|
36018
|
-
{
|
|
36019
|
-
);
|
|
36020
|
-
if (existing.length > 0) {
|
|
36021
|
-
toUpdate.push({ id: Number(existing[0]["$id"].value), record: record2 });
|
|
36705
|
+
return record2;
|
|
36706
|
+
});
|
|
36707
|
+
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
36708
|
+
const rowKeyValues = records.map(
|
|
36709
|
+
(record2) => stmt.keyFields.map((key) => String(record2[key]?.value ?? ""))
|
|
36710
|
+
);
|
|
36711
|
+
const targetIndex = await resolveUpsertTargets(stmt.appId, stmt.keyFields, rowKeyValues, client, options, fieldTypes);
|
|
36712
|
+
records.forEach((record2, rowIdx) => {
|
|
36713
|
+
const id = lookupUpsertTarget(targetIndex, rowKeyValues[rowIdx]);
|
|
36714
|
+
if (id !== void 0) {
|
|
36715
|
+
toUpdate.push({ id, record: record2 });
|
|
36022
36716
|
} else {
|
|
36023
36717
|
toInsert.push(record2);
|
|
36024
36718
|
}
|
|
36025
|
-
}
|
|
36719
|
+
});
|
|
36026
36720
|
if (options.confirm && toInsert.length + toUpdate.length > 0) {
|
|
36027
36721
|
const total = toInsert.length + toUpdate.length;
|
|
36028
36722
|
const ok = await options.confirm(total, "UPDATE");
|
|
@@ -36073,37 +36767,51 @@ function parseSql(sql) {
|
|
|
36073
36767
|
throw e;
|
|
36074
36768
|
}
|
|
36075
36769
|
}
|
|
36076
|
-
async function resolveSubqueries(where, client, options, cacheContext) {
|
|
36770
|
+
async function resolveSubqueries(where, client, options, cacheContext, cteCache) {
|
|
36771
|
+
const tasks = [];
|
|
36772
|
+
collectSubqueryTasks(where, client, options, cacheContext, tasks, cteCache);
|
|
36773
|
+
await Promise.all(tasks);
|
|
36774
|
+
}
|
|
36775
|
+
function runSubquery(query, client, options, cacheContext, cteCache) {
|
|
36776
|
+
if (cteCache !== void 0 && cteCache.size > 0) {
|
|
36777
|
+
return executeQueryWithCte(query, client, options, cteCache, cacheContext);
|
|
36778
|
+
}
|
|
36779
|
+
return executeSelect(query, client, options, cacheContext);
|
|
36780
|
+
}
|
|
36781
|
+
function collectSubqueryTasks(where, client, options, cacheContext, tasks, cteCache) {
|
|
36077
36782
|
if (where === null) return;
|
|
36078
36783
|
switch (where.type) {
|
|
36079
36784
|
case "BINARY": {
|
|
36080
36785
|
const right = where.right;
|
|
36081
36786
|
if (right.type === "SUBQUERY_IN_LIST") {
|
|
36082
|
-
|
|
36083
|
-
|
|
36084
|
-
|
|
36085
|
-
|
|
36787
|
+
tasks.push(runSubquery(right.query, client, options, cacheContext, cteCache).then((result) => {
|
|
36788
|
+
const col = right.column ?? (result.columns[0] ?? "");
|
|
36789
|
+
right.resolved = new Set(result.rows.map((r) => r[col] ?? ""));
|
|
36790
|
+
}));
|
|
36086
36791
|
}
|
|
36087
36792
|
if (right.type === "SCALAR_SUBQUERY") {
|
|
36088
|
-
|
|
36089
|
-
|
|
36090
|
-
|
|
36091
|
-
|
|
36092
|
-
|
|
36793
|
+
tasks.push(runSubquery(right.query, client, options, cacheContext, cteCache).then((result) => {
|
|
36794
|
+
if (result.rowCount === 0) throw new Error("\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA\u304C\u5024\u3092\u8FD4\u3057\u307E\u305B\u3093\u3067\u3057\u305F");
|
|
36795
|
+
if (result.rowCount > 1) throw new Error("\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA\u304C\u8907\u6570\u884C\u3092\u8FD4\u3057\u307E\u3057\u305F\uFF081\u884C\u306E\u307F\u8A31\u53EF\uFF09");
|
|
36796
|
+
const col = result.columns[0] ?? "";
|
|
36797
|
+
right.resolved = result.rows[0]?.[col] ?? "";
|
|
36798
|
+
}));
|
|
36093
36799
|
}
|
|
36094
36800
|
break;
|
|
36095
36801
|
}
|
|
36096
36802
|
case "LOGICAL":
|
|
36097
|
-
|
|
36098
|
-
|
|
36803
|
+
collectSubqueryTasks(where.left, client, options, cacheContext, tasks, cteCache);
|
|
36804
|
+
collectSubqueryTasks(where.right, client, options, cacheContext, tasks, cteCache);
|
|
36099
36805
|
break;
|
|
36100
36806
|
case "NOT":
|
|
36101
36807
|
case "GROUP":
|
|
36102
|
-
|
|
36808
|
+
collectSubqueryTasks(where.expr, client, options, cacheContext, tasks, cteCache);
|
|
36103
36809
|
break;
|
|
36104
36810
|
case "EXISTS": {
|
|
36105
|
-
const
|
|
36106
|
-
|
|
36811
|
+
const node = where;
|
|
36812
|
+
tasks.push(runSubquery(node.query, client, options, cacheContext, cteCache).then((result) => {
|
|
36813
|
+
node.resolved = result.rowCount > 0;
|
|
36814
|
+
}));
|
|
36107
36815
|
break;
|
|
36108
36816
|
}
|
|
36109
36817
|
}
|
|
@@ -36119,19 +36827,83 @@ async function resolveSetSubqueries(assignments, client, options, cacheContext)
|
|
|
36119
36827
|
a.value = { type: "STRING", value: resolved };
|
|
36120
36828
|
}
|
|
36121
36829
|
}
|
|
36122
|
-
async function resolveScalarColumns(columns, client, options, cacheContext) {
|
|
36123
|
-
const
|
|
36830
|
+
async function resolveScalarColumns(columns, client, options, cacheContext, cteCache) {
|
|
36831
|
+
const byQuery = /* @__PURE__ */ new Map();
|
|
36832
|
+
const pending = [];
|
|
36124
36833
|
for (let i = 0; i < columns.length; i++) {
|
|
36125
36834
|
const col = columns[i];
|
|
36126
36835
|
if (col.type !== "SCALAR_SUBQUERY_COL") continue;
|
|
36127
|
-
const
|
|
36128
|
-
|
|
36129
|
-
if (
|
|
36130
|
-
|
|
36131
|
-
|
|
36836
|
+
const key = JSON.stringify(col.query);
|
|
36837
|
+
let promise2 = byQuery.get(key);
|
|
36838
|
+
if (!promise2) {
|
|
36839
|
+
promise2 = runSubquery(col.query, client, options, cacheContext, cteCache).then((result) => {
|
|
36840
|
+
if (result.rowCount === 0) throw new Error("\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA\u304C\u5024\u3092\u8FD4\u3057\u307E\u305B\u3093\u3067\u3057\u305F");
|
|
36841
|
+
if (result.rowCount > 1) throw new Error("\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA\u304C\u8907\u6570\u884C\u3092\u8FD4\u3057\u307E\u3057\u305F\uFF081\u884C\u306E\u307F\u8A31\u53EF\uFF09");
|
|
36842
|
+
const firstCol = result.columns[0] ?? "";
|
|
36843
|
+
return result.rows[0]?.[firstCol] ?? "";
|
|
36844
|
+
});
|
|
36845
|
+
byQuery.set(key, promise2);
|
|
36846
|
+
}
|
|
36847
|
+
pending.push([i, promise2]);
|
|
36132
36848
|
}
|
|
36849
|
+
const values = await Promise.all(pending.map(([, promise2]) => promise2));
|
|
36850
|
+
const cache = /* @__PURE__ */ new Map();
|
|
36851
|
+
pending.forEach(([i], idx) => cache.set(i, values[idx]));
|
|
36133
36852
|
return cache;
|
|
36134
36853
|
}
|
|
36854
|
+
function buildBatchExplainPlans(sql) {
|
|
36855
|
+
const statements = parseSqlBatch(sql);
|
|
36856
|
+
const analysis = analyzeBatch(statements);
|
|
36857
|
+
return {
|
|
36858
|
+
statementCount: statements.length,
|
|
36859
|
+
statements: statements.map((stmt, i) => ({
|
|
36860
|
+
index: i,
|
|
36861
|
+
type: analysis.statements[i].statementType,
|
|
36862
|
+
plan: buildBatchStatementPlan(stmt, analysis.statements[i])
|
|
36863
|
+
}))
|
|
36864
|
+
};
|
|
36865
|
+
}
|
|
36866
|
+
function buildBatchStatementPlan(stmt, info) {
|
|
36867
|
+
if (stmt.type === "CREATE_TEMP_TABLE") {
|
|
36868
|
+
return [
|
|
36869
|
+
`CREATE TEMP TABLE ${stmt.name}`,
|
|
36870
|
+
` scope: batch\uFF08\u30D0\u30C3\u30C1\u7D42\u4E86\u6642\u306B\u81EA\u52D5\u7834\u68C4\uFF09`,
|
|
36871
|
+
` rows: \u5B9F\u4F53\u5316\u524D\u306E\u305F\u3081\u4E0D\u660E\uFF08\u4E0A\u9650 ${TEMP_TABLE_MAX_ROWS} \u884C\u3001\u8D85\u904E\u306F\u30A8\u30E9\u30FC\uFF09`,
|
|
36872
|
+
...buildPlanForBatchQuery(stmt.query, info).map((l) => ` ${l}`)
|
|
36873
|
+
];
|
|
36874
|
+
}
|
|
36875
|
+
if (stmt.type === "DROP_TEMP_TABLE") {
|
|
36876
|
+
return [
|
|
36877
|
+
`DROP TEMP TABLE ${stmt.name}`,
|
|
36878
|
+
" \u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u30B9\u30C8\u30A2\u306E\u89E3\u653E\u306E\u307F\uFF08kintone \u30A2\u30AF\u30BB\u30B9\u306A\u3057\uFF09"
|
|
36879
|
+
];
|
|
36880
|
+
}
|
|
36881
|
+
if (stmt.type === "SHOW_APPS") return ["SHOW APPS\uFF08\u30A2\u30D7\u30EA\u4E00\u89A7\u306E\u53D6\u5F97\uFF09"];
|
|
36882
|
+
if (stmt.type === "DESCRIBE") return [`DESCRIBE APP${stmt.appId}\uFF08\u30D5\u30A3\u30FC\u30EB\u30C9\u5B9A\u7FA9\u306E\u53D6\u5F97\uFF09`];
|
|
36883
|
+
if (stmt.type === "EXPLAIN") return buildPlanForBatchQuery(stmt.query, info);
|
|
36884
|
+
return buildPlanForBatchQuery(stmt, info);
|
|
36885
|
+
}
|
|
36886
|
+
function buildPlanForBatchQuery(query, info) {
|
|
36887
|
+
if (info.tempTablesReferenced.length === 0) {
|
|
36888
|
+
return buildExplainPlan(query);
|
|
36889
|
+
}
|
|
36890
|
+
const lines = [];
|
|
36891
|
+
if (query.type === "INSERT_SELECT") {
|
|
36892
|
+
lines.push(
|
|
36893
|
+
`INSERT INTO APP${query.appId} ... SELECT\uFF08\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u30BD\u30FC\u30B9\u3002\u5B9F\u884C\u6642\u306B\u4EF6\u6570\u78BA\u5B9A \u2192 dmlMaxRows \u9069\u7528\uFF09`
|
|
36894
|
+
);
|
|
36895
|
+
}
|
|
36896
|
+
lines.push(" mode: FULL_SCAN\uFF08\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u53C2\u7167\uFF09");
|
|
36897
|
+
lines.push(
|
|
36898
|
+
` temp: ${info.tempTablesReferenced.join(", ")}\uFF08\u30A4\u30F3\u30E1\u30E2\u30EA\u8D70\u67FB\u3002\u5B9F\u4F53\u5316\u524D\u306E\u305F\u3081\u884C\u6570\u4E0D\u660E\uFF09`
|
|
36899
|
+
);
|
|
36900
|
+
const apps = info.appIds.filter((a) => query.type !== "INSERT_SELECT" || a !== query.appId);
|
|
36901
|
+
if (apps.length > 0) {
|
|
36902
|
+
lines.push(` app: ${apps.map((a) => `APP${a}`).join(", ")}`);
|
|
36903
|
+
}
|
|
36904
|
+
lines.push(" note: \u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u3078\u306E WHERE \u30D7\u30C3\u30B7\u30E5\u30C0\u30A6\u30F3\u306F\u884C\u308F\u308C\u306A\u3044");
|
|
36905
|
+
return lines;
|
|
36906
|
+
}
|
|
36135
36907
|
function executeExplain(stmt) {
|
|
36136
36908
|
const lines = buildExplainPlan(stmt.query);
|
|
36137
36909
|
return {
|
|
@@ -36452,6 +37224,10 @@ function parseSqlStatement(sql) {
|
|
|
36452
37224
|
const tokens = new Lexer(sql).tokenize();
|
|
36453
37225
|
return new Parser(tokens).parse();
|
|
36454
37226
|
}
|
|
37227
|
+
function parseSqlStatements(sql) {
|
|
37228
|
+
const tokens = new Lexer(sql).tokenize();
|
|
37229
|
+
return new Parser(tokens).parseStatements();
|
|
37230
|
+
}
|
|
36455
37231
|
|
|
36456
37232
|
// src/node/appProfiles.ts
|
|
36457
37233
|
function parseTokenMap(raw) {
|
|
@@ -36637,35 +37413,6 @@ function buildCacheContext(defaultProfile, appBindingByMappedApp) {
|
|
|
36637
37413
|
return `apps:${pairs.join(",")}`;
|
|
36638
37414
|
}
|
|
36639
37415
|
|
|
36640
|
-
// src/node/dmlGuard.ts
|
|
36641
|
-
function getStatementType(stmt) {
|
|
36642
|
-
if (!stmt || typeof stmt !== "object") return "UNKNOWN";
|
|
36643
|
-
const obj = stmt;
|
|
36644
|
-
return typeof obj.type === "string" ? obj.type : "UNKNOWN";
|
|
36645
|
-
}
|
|
36646
|
-
function isDmlType(type) {
|
|
36647
|
-
return type === "INSERT" || type === "INSERT_SELECT" || type === "UPDATE" || type === "DELETE" || type === "UPSERT" || type === "UPSERT_SELECT" || type === "REORDER";
|
|
36648
|
-
}
|
|
36649
|
-
function isReadOnlyType(type) {
|
|
36650
|
-
return type === "SELECT" || type === "UNION" || type === "WITH" || type === "EXPLAIN" || type === "SHOW_APPS" || type === "DESCRIBE";
|
|
36651
|
-
}
|
|
36652
|
-
function hasWhereClause(stmt) {
|
|
36653
|
-
if (!stmt || typeof stmt !== "object") return false;
|
|
36654
|
-
const obj = stmt;
|
|
36655
|
-
return obj.where !== null && obj.where !== void 0;
|
|
36656
|
-
}
|
|
36657
|
-
function isNoFromSelectStatement(stmt) {
|
|
36658
|
-
if (!stmt || typeof stmt !== "object") return false;
|
|
36659
|
-
const obj = stmt;
|
|
36660
|
-
return obj.type === "SELECT" && obj.from?.appId === 0 && obj.from?.cteName === "__NO_FROM__";
|
|
36661
|
-
}
|
|
36662
|
-
function getInsertValuesCount(stmt) {
|
|
36663
|
-
if (!stmt || typeof stmt !== "object") return null;
|
|
36664
|
-
const obj = stmt;
|
|
36665
|
-
if (obj.type !== "INSERT") return null;
|
|
36666
|
-
return Array.isArray(obj.values) ? obj.values.length : null;
|
|
36667
|
-
}
|
|
36668
|
-
|
|
36669
37416
|
// src/node/config.ts
|
|
36670
37417
|
var import_fs = require("fs");
|
|
36671
37418
|
function loadKsqlConfig(configPath) {
|
|
@@ -36707,6 +37454,107 @@ function resolveTokenValue(raw) {
|
|
|
36707
37454
|
return raw;
|
|
36708
37455
|
}
|
|
36709
37456
|
|
|
37457
|
+
// src/api/requestGate.ts
|
|
37458
|
+
var DEFAULT_MAX_CONCURRENT = 10;
|
|
37459
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
37460
|
+
var DEFAULT_BASE_DELAY_MS = 500;
|
|
37461
|
+
var DEFAULT_MAX_DELAY_MS = 8e3;
|
|
37462
|
+
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([408, 429, 502, 503, 504]);
|
|
37463
|
+
function isRetryableError(err) {
|
|
37464
|
+
if (!(err instanceof Error)) return false;
|
|
37465
|
+
const status = err.message.match(/^kintone API error (\d{3}):/);
|
|
37466
|
+
if (status) return RETRYABLE_STATUSES.has(Number(status[1]));
|
|
37467
|
+
if (err.name === "AbortError" || err.name === "TimeoutError") return true;
|
|
37468
|
+
if (/fetch failed/i.test(err.message)) return true;
|
|
37469
|
+
return false;
|
|
37470
|
+
}
|
|
37471
|
+
var RequestGate = class {
|
|
37472
|
+
constructor(options = {}) {
|
|
37473
|
+
this.active = 0;
|
|
37474
|
+
this.waiters = [];
|
|
37475
|
+
this.maxConcurrent = clampInt(options.maxConcurrent ?? DEFAULT_MAX_CONCURRENT, 1, 50);
|
|
37476
|
+
this.maxRetries = clampInt(options.maxRetries ?? DEFAULT_MAX_RETRIES, 0, 10);
|
|
37477
|
+
this.baseDelayMs = options.baseDelayMs ?? DEFAULT_BASE_DELAY_MS;
|
|
37478
|
+
this.maxDelayMs = options.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
|
|
37479
|
+
this.sleep = options.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
37480
|
+
this.random = options.random ?? Math.random;
|
|
37481
|
+
}
|
|
37482
|
+
/** 現在の同時実行数(テスト・診断用) */
|
|
37483
|
+
get activeCount() {
|
|
37484
|
+
return this.active;
|
|
37485
|
+
}
|
|
37486
|
+
get limit() {
|
|
37487
|
+
return this.maxConcurrent;
|
|
37488
|
+
}
|
|
37489
|
+
/** GET 系: セマフォ + リトライ付きで実行する */
|
|
37490
|
+
async runReadOnly(fn) {
|
|
37491
|
+
let attempt = 0;
|
|
37492
|
+
while (true) {
|
|
37493
|
+
try {
|
|
37494
|
+
return await this.withSlot(fn);
|
|
37495
|
+
} catch (err) {
|
|
37496
|
+
if (attempt >= this.maxRetries || !isRetryableError(err)) throw err;
|
|
37497
|
+
await this.sleep(this.backoffDelay(attempt));
|
|
37498
|
+
attempt += 1;
|
|
37499
|
+
}
|
|
37500
|
+
}
|
|
37501
|
+
}
|
|
37502
|
+
/** 書き込み系: セマフォのみ(リトライしない — 二重実行防止) */
|
|
37503
|
+
async runMutation(fn) {
|
|
37504
|
+
return this.withSlot(fn);
|
|
37505
|
+
}
|
|
37506
|
+
async withSlot(fn) {
|
|
37507
|
+
await this.acquire();
|
|
37508
|
+
try {
|
|
37509
|
+
return await fn();
|
|
37510
|
+
} finally {
|
|
37511
|
+
this.release();
|
|
37512
|
+
}
|
|
37513
|
+
}
|
|
37514
|
+
async acquire() {
|
|
37515
|
+
if (this.active < this.maxConcurrent) {
|
|
37516
|
+
this.active += 1;
|
|
37517
|
+
return;
|
|
37518
|
+
}
|
|
37519
|
+
await new Promise((resolve2) => this.waiters.push(resolve2));
|
|
37520
|
+
this.active += 1;
|
|
37521
|
+
}
|
|
37522
|
+
release() {
|
|
37523
|
+
this.active -= 1;
|
|
37524
|
+
const next = this.waiters.shift();
|
|
37525
|
+
if (next) next();
|
|
37526
|
+
}
|
|
37527
|
+
/** 指数バックオフ + ジッタ(attempt: 0 始まり) */
|
|
37528
|
+
backoffDelay(attempt) {
|
|
37529
|
+
const base = Math.min(this.baseDelayMs * 2 ** attempt, this.maxDelayMs);
|
|
37530
|
+
const jitter = 1 + (this.random() - 0.5) * 0.5;
|
|
37531
|
+
return Math.round(base * jitter);
|
|
37532
|
+
}
|
|
37533
|
+
};
|
|
37534
|
+
function withRequestGate(client, gate) {
|
|
37535
|
+
return {
|
|
37536
|
+
getRecords: (params) => gate.runReadOnly(() => client.getRecords(params)),
|
|
37537
|
+
getApps: () => gate.runReadOnly(() => client.getApps()),
|
|
37538
|
+
getFields: (appId) => gate.runReadOnly(() => client.getFields(appId)),
|
|
37539
|
+
postRecords: (params) => gate.runMutation(() => client.postRecords(params)),
|
|
37540
|
+
putRecords: (params) => gate.runMutation(() => client.putRecords(params)),
|
|
37541
|
+
deleteRecords: (params) => gate.runMutation(() => client.deleteRecords(params))
|
|
37542
|
+
};
|
|
37543
|
+
}
|
|
37544
|
+
var globalGate = null;
|
|
37545
|
+
function getGlobalRequestGate(limitHint) {
|
|
37546
|
+
if (globalGate === null) {
|
|
37547
|
+
const envValue = Number(process.env.KSQL_MAX_CONCURRENT);
|
|
37548
|
+
const limit = Number.isInteger(envValue) && envValue > 0 ? envValue : limitHint;
|
|
37549
|
+
globalGate = new RequestGate({ maxConcurrent: limit });
|
|
37550
|
+
}
|
|
37551
|
+
return globalGate;
|
|
37552
|
+
}
|
|
37553
|
+
function clampInt(v, min, max) {
|
|
37554
|
+
if (!Number.isFinite(v)) return min;
|
|
37555
|
+
return Math.max(min, Math.min(max, Math.trunc(v)));
|
|
37556
|
+
}
|
|
37557
|
+
|
|
36710
37558
|
// src/cli/nodeKintoneClient.ts
|
|
36711
37559
|
function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
36712
37560
|
const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
@@ -36920,6 +37768,10 @@ async function createKsqlRuntime(serverOptions, input) {
|
|
|
36920
37768
|
const normalized = normalizeSqlAppProfiles(input.sql, profileName);
|
|
36921
37769
|
const sql = normalized.normalizedSql;
|
|
36922
37770
|
const maxRecords2 = input.maxRecords ?? envInt("KSQL_MAX_RECORDS") ?? profile2.query?.maxRecords ?? 500;
|
|
37771
|
+
const fetchParallel2 = input.fetchParallel ?? envInt("KSQL_FETCH_PARALLEL") ?? profile2.query?.fetchParallel ?? 3;
|
|
37772
|
+
if (!Number.isInteger(fetchParallel2) || fetchParallel2 < 1 || fetchParallel2 > 10) {
|
|
37773
|
+
throw new Error("ArgumentError: fetchParallel must be an integer between 1 and 10.");
|
|
37774
|
+
}
|
|
36923
37775
|
const onLimit2 = input.onLimit ?? envOnLimit("KSQL_ON_LIMIT") ?? profile2.query?.onLimit ?? "error";
|
|
36924
37776
|
const timeout2 = input.timeout ?? envInt("KSQL_TIMEOUT") ?? profile2.query?.timeout ?? 3e4;
|
|
36925
37777
|
const appIds = extractAppIds(sql);
|
|
@@ -37045,12 +37897,17 @@ async function createKsqlRuntime(serverOptions, input) {
|
|
|
37045
37897
|
},
|
|
37046
37898
|
getApps: () => defaultClient.getApps()
|
|
37047
37899
|
};
|
|
37900
|
+
const gatedClient = withRequestGate(
|
|
37901
|
+
routedClient,
|
|
37902
|
+
getGlobalRequestGate(profile2.query?.maxConcurrent)
|
|
37903
|
+
);
|
|
37048
37904
|
return {
|
|
37049
37905
|
sql,
|
|
37050
37906
|
profileName,
|
|
37051
|
-
client:
|
|
37907
|
+
client: gatedClient,
|
|
37052
37908
|
cacheContext: buildCacheContext(profileName, normalized.appBindingByMappedApp),
|
|
37053
37909
|
maxRecords: maxRecords2,
|
|
37910
|
+
fetchParallel: fetchParallel2,
|
|
37054
37911
|
onLimit: onLimit2,
|
|
37055
37912
|
timeout: timeout2
|
|
37056
37913
|
};
|
|
@@ -37236,6 +38093,14 @@ function deleteSavedQuery(catalog, name) {
|
|
|
37236
38093
|
}
|
|
37237
38094
|
|
|
37238
38095
|
// src/mcp/tools.ts
|
|
38096
|
+
function requireSingleStatement(validation, toolName) {
|
|
38097
|
+
if (validation.batch) {
|
|
38098
|
+
throw new Error(
|
|
38099
|
+
`ArgumentError: batch SQL (multiple statements) is not supported by ${toolName} yet.`
|
|
38100
|
+
);
|
|
38101
|
+
}
|
|
38102
|
+
return validation;
|
|
38103
|
+
}
|
|
37239
38104
|
var DEFAULT_MAX_RECORDS = 500;
|
|
37240
38105
|
var DEFAULT_ON_LIMIT = "error";
|
|
37241
38106
|
function noOpClient() {
|
|
@@ -37291,6 +38156,59 @@ function toSelectPayload(result) {
|
|
|
37291
38156
|
warnings: result.warnings ?? []
|
|
37292
38157
|
};
|
|
37293
38158
|
}
|
|
38159
|
+
function toMutationSummary(result) {
|
|
38160
|
+
if (result.type === "INSERT") {
|
|
38161
|
+
return { insertedCount: result.insertedCount, createdIds: result.createdIds };
|
|
38162
|
+
}
|
|
38163
|
+
if (result.type === "UPDATE") return { updatedCount: result.updatedCount };
|
|
38164
|
+
if (result.type === "DELETE") return { deletedCount: result.deletedCount };
|
|
38165
|
+
if (result.type === "UPSERT") {
|
|
38166
|
+
return { insertedCount: result.insertedCount, updatedCount: result.updatedCount };
|
|
38167
|
+
}
|
|
38168
|
+
return { reorderedParentCount: result.reorderedParentCount };
|
|
38169
|
+
}
|
|
38170
|
+
function toBatchQueryPayload(batch, maxTotalRecords) {
|
|
38171
|
+
const results = [];
|
|
38172
|
+
let totalRows = 0;
|
|
38173
|
+
const statements = batch.statements.map((s) => {
|
|
38174
|
+
const entry = {
|
|
38175
|
+
index: s.index,
|
|
38176
|
+
type: s.type,
|
|
38177
|
+
status: s.status
|
|
38178
|
+
};
|
|
38179
|
+
if (s.status === "error" && s.error) entry.error = s.error;
|
|
38180
|
+
if (s.status === "skipped" && s.skippedReason) entry.skippedReason = s.skippedReason;
|
|
38181
|
+
if (s.tempTable !== void 0) entry.tempTable = s.tempTable;
|
|
38182
|
+
if (s.rowCount !== void 0) entry.rowCount = s.rowCount;
|
|
38183
|
+
if (s.status === "success" && s.result?.type === "SELECT") {
|
|
38184
|
+
totalRows += s.result.rowCount;
|
|
38185
|
+
if (maxTotalRecords !== void 0 && totalRows > maxTotalRecords) {
|
|
38186
|
+
throw new Error(
|
|
38187
|
+
`ArgumentError: batch total rows (${totalRows}) exceed maxTotalRecords (${maxTotalRecords}).`
|
|
38188
|
+
);
|
|
38189
|
+
}
|
|
38190
|
+
entry.resultIndex = results.length;
|
|
38191
|
+
results.push({
|
|
38192
|
+
columns: s.result.columns,
|
|
38193
|
+
rows: s.result.rows,
|
|
38194
|
+
rowCount: s.result.rowCount,
|
|
38195
|
+
warnings: s.result.warnings ?? []
|
|
38196
|
+
});
|
|
38197
|
+
} else if (s.status === "success" && s.result && s.result.type !== "SELECT") {
|
|
38198
|
+
Object.assign(entry, toMutationSummary(s.result));
|
|
38199
|
+
}
|
|
38200
|
+
return entry;
|
|
38201
|
+
});
|
|
38202
|
+
return {
|
|
38203
|
+
ok: batch.ok,
|
|
38204
|
+
batch: true,
|
|
38205
|
+
statementCount: batch.statementCount,
|
|
38206
|
+
statements,
|
|
38207
|
+
results,
|
|
38208
|
+
// バッチ全体の警告(仕様 §6.2)。文ごとの警告は results[].warnings に入る
|
|
38209
|
+
warnings: []
|
|
38210
|
+
};
|
|
38211
|
+
}
|
|
37294
38212
|
function toMutationPayload(result) {
|
|
37295
38213
|
if (result.type === "INSERT") {
|
|
37296
38214
|
return {
|
|
@@ -37375,35 +38293,71 @@ async function runSafely(fn) {
|
|
|
37375
38293
|
function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
37376
38294
|
const createRuntime = deps.createRuntime ?? createKsqlRuntime;
|
|
37377
38295
|
const executeSql = deps.executeSql ?? execute;
|
|
38296
|
+
const executeBatchSql = deps.executeBatchSql ?? executeBatch;
|
|
37378
38297
|
async function validate(input) {
|
|
37379
38298
|
const normalized = normalizeSqlForTool(serverOptions, input.sql, input.profile);
|
|
37380
|
-
const
|
|
37381
|
-
const
|
|
37382
|
-
const isDml = isDmlType(statementType);
|
|
37383
|
-
const isReadOnly = isReadOnlyType(statementType);
|
|
38299
|
+
const statements = parseSqlStatements(normalized.normalizedSql);
|
|
38300
|
+
const analysis = analyzeBatch(statements);
|
|
37384
38301
|
const appBindings = [...normalized.appBindingByMappedApp.entries()].map(([mappedAppId, binding]) => ({
|
|
37385
38302
|
mappedAppId,
|
|
37386
38303
|
appId: binding.appId,
|
|
37387
38304
|
profile: binding.profile
|
|
37388
38305
|
}));
|
|
37389
|
-
|
|
38306
|
+
const statementValidations = analysis.statements.map((s2) => ({
|
|
38307
|
+
index: s2.index,
|
|
38308
|
+
statementType: s2.statementType,
|
|
38309
|
+
isDml: s2.isDml,
|
|
38310
|
+
isReadOnly: s2.isReadOnly,
|
|
38311
|
+
hasWhere: s2.hasWhere,
|
|
38312
|
+
insertValuesCount: s2.insertValuesCount,
|
|
38313
|
+
appIds: s2.appIds,
|
|
38314
|
+
tempTablesCreated: s2.tempTablesCreated,
|
|
38315
|
+
tempTablesReferenced: s2.tempTablesReferenced,
|
|
38316
|
+
tempTablesDropped: s2.tempTablesDropped,
|
|
38317
|
+
tempOnlySource: s2.tempOnlySource,
|
|
38318
|
+
targetAppId: s2.targetAppId
|
|
38319
|
+
}));
|
|
38320
|
+
const common = {
|
|
37390
38321
|
ok: true,
|
|
37391
|
-
|
|
37392
|
-
|
|
37393
|
-
|
|
37394
|
-
|
|
37395
|
-
|
|
37396
|
-
|
|
37397
|
-
|
|
37398
|
-
requiresMutationTool: isDml,
|
|
38322
|
+
statementCount: analysis.statementCount,
|
|
38323
|
+
isReadOnlyBatch: analysis.isReadOnlyBatch,
|
|
38324
|
+
containsDml: analysis.containsDml,
|
|
38325
|
+
tempTables: analysis.tempTables,
|
|
38326
|
+
canRunWithQueryTool: analysis.isReadOnlyBatch,
|
|
38327
|
+
requiresMutationTool: analysis.containsDml,
|
|
38328
|
+
statements: statementValidations,
|
|
37399
38329
|
normalizedSql: normalized.normalizedSql,
|
|
37400
38330
|
hasProfileSyntax: normalized.hasProfileSyntax,
|
|
37401
38331
|
cacheContext: normalized.cacheContext,
|
|
37402
38332
|
appBindings
|
|
37403
38333
|
};
|
|
38334
|
+
if (analysis.statementCount > 1) {
|
|
38335
|
+
return { ...common, batch: true };
|
|
38336
|
+
}
|
|
38337
|
+
const s = statementValidations[0];
|
|
38338
|
+
return {
|
|
38339
|
+
...common,
|
|
38340
|
+
batch: false,
|
|
38341
|
+
statementType: s.statementType,
|
|
38342
|
+
isDml: s.isDml,
|
|
38343
|
+
isReadOnly: s.isReadOnly,
|
|
38344
|
+
hasWhere: s.hasWhere,
|
|
38345
|
+
insertValuesCount: s.insertValuesCount,
|
|
38346
|
+
appIds: s.appIds
|
|
38347
|
+
};
|
|
37404
38348
|
}
|
|
37405
38349
|
async function explain(input) {
|
|
37406
38350
|
const normalized = normalizeSqlForTool(serverOptions, input.sql, input.profile);
|
|
38351
|
+
const statements = parseSqlStatements(normalized.normalizedSql);
|
|
38352
|
+
if (statements.length > 1) {
|
|
38353
|
+
const plans = buildBatchExplainPlans(normalized.normalizedSql);
|
|
38354
|
+
return {
|
|
38355
|
+
ok: true,
|
|
38356
|
+
batch: true,
|
|
38357
|
+
statementCount: plans.statementCount,
|
|
38358
|
+
statements: plans.statements
|
|
38359
|
+
};
|
|
38360
|
+
}
|
|
37407
38361
|
const result = await executeSql(explainSql(normalized.normalizedSql), noOpClient(), {
|
|
37408
38362
|
cacheContext: normalized.cacheContext
|
|
37409
38363
|
});
|
|
@@ -37414,6 +38368,31 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
37414
38368
|
}
|
|
37415
38369
|
async function query(input) {
|
|
37416
38370
|
const validation = await validate(input);
|
|
38371
|
+
if (validation.batch) {
|
|
38372
|
+
if (validation.containsDml) {
|
|
38373
|
+
throw new Error("ArgumentError: batch contains DML statements. Use ksql_mutate.");
|
|
38374
|
+
}
|
|
38375
|
+
const runtime2 = await createRuntime(serverOptions, {
|
|
38376
|
+
sql: input.sql,
|
|
38377
|
+
profile: input.profile,
|
|
38378
|
+
maxRecords: input.maxRecords,
|
|
38379
|
+
fetchParallel: input.fetchParallel,
|
|
38380
|
+
onLimit: input.onLimit,
|
|
38381
|
+
timeout: input.timeout
|
|
38382
|
+
});
|
|
38383
|
+
const batchResult = await executeBatchSql(runtime2.sql, runtime2.client, {
|
|
38384
|
+
maxRecords: runtime2.maxRecords,
|
|
38385
|
+
fetchParallel: runtime2.fetchParallel,
|
|
38386
|
+
onLimitReached: runtime2.onLimit,
|
|
38387
|
+
cacheContext: runtime2.cacheContext,
|
|
38388
|
+
continueOnError: input.continueOnError,
|
|
38389
|
+
// バッチでは timeout を合計タイムアウトとして扱う(仕様 §5.7)。
|
|
38390
|
+
// runtime.timeout は env / profile / 既定 30000ms を解決済みの値で、
|
|
38391
|
+
// HTTP クライアント側の per-request タイムアウトと同値になる
|
|
38392
|
+
timeoutMs: runtime2.timeout
|
|
38393
|
+
});
|
|
38394
|
+
return toBatchQueryPayload(batchResult, input.maxTotalRecords);
|
|
38395
|
+
}
|
|
37417
38396
|
if (!validation.isReadOnly) {
|
|
37418
38397
|
throw new Error(`ArgumentError: ${validation.statementType} is not allowed by ksql_query. Use ksql_mutate.`);
|
|
37419
38398
|
}
|
|
@@ -37434,11 +38413,13 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
37434
38413
|
sql: input.sql,
|
|
37435
38414
|
profile: input.profile,
|
|
37436
38415
|
maxRecords: input.maxRecords,
|
|
38416
|
+
fetchParallel: input.fetchParallel,
|
|
37437
38417
|
onLimit: input.onLimit,
|
|
37438
38418
|
timeout: input.timeout
|
|
37439
38419
|
});
|
|
37440
38420
|
const result = await executeSql(runtime.sql, runtime.client, {
|
|
37441
38421
|
maxRecords: runtime.maxRecords,
|
|
38422
|
+
fetchParallel: runtime.fetchParallel,
|
|
37442
38423
|
onLimitReached: runtime.onLimit,
|
|
37443
38424
|
cacheContext: runtime.cacheContext
|
|
37444
38425
|
});
|
|
@@ -37447,9 +38428,75 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
37447
38428
|
}
|
|
37448
38429
|
return toSelectPayload(result);
|
|
37449
38430
|
}
|
|
38431
|
+
async function mutateBatch(input, validation, dmlMaxRows2) {
|
|
38432
|
+
if (!validation.containsDml) {
|
|
38433
|
+
throw new Error("ArgumentError: batch contains no DML statements. Use ksql_query.");
|
|
38434
|
+
}
|
|
38435
|
+
let staticInsertTotal = 0;
|
|
38436
|
+
for (const s of validation.statements) {
|
|
38437
|
+
if (!s.isDml) continue;
|
|
38438
|
+
const at = ` (statement ${s.index})`;
|
|
38439
|
+
if (s.statementType === "INSERT_SELECT" && !s.tempOnlySource) {
|
|
38440
|
+
throw new Error(
|
|
38441
|
+
`ArgumentError: INSERT_SELECT in a batch must select from temp tables only.${at}`
|
|
38442
|
+
);
|
|
38443
|
+
}
|
|
38444
|
+
if (s.statementType === "UPSERT_SELECT") {
|
|
38445
|
+
throw new Error(`ArgumentError: ${s.statementType} is not supported by ksql_mutate yet.${at}`);
|
|
38446
|
+
}
|
|
38447
|
+
if ((s.statementType === "UPDATE" || s.statementType === "DELETE") && !s.hasWhere) {
|
|
38448
|
+
throw new Error(`ArgumentError: ${s.statementType} without WHERE is blocked by ksql_mutate.${at}`);
|
|
38449
|
+
}
|
|
38450
|
+
if (s.insertValuesCount !== null && s.insertValuesCount > dmlMaxRows2) {
|
|
38451
|
+
throw new Error(
|
|
38452
|
+
`ArgumentError: INSERT rows (${s.insertValuesCount}) exceed dmlMaxRows (${dmlMaxRows2}).${at}`
|
|
38453
|
+
);
|
|
38454
|
+
}
|
|
38455
|
+
staticInsertTotal += s.insertValuesCount ?? 0;
|
|
38456
|
+
}
|
|
38457
|
+
const dmlTotalMaxRows = input.dmlTotalMaxRows;
|
|
38458
|
+
if (dmlTotalMaxRows !== void 0 && staticInsertTotal > dmlTotalMaxRows) {
|
|
38459
|
+
throw new Error(
|
|
38460
|
+
`ArgumentError: batch INSERT rows (${staticInsertTotal}) exceed dmlTotalMaxRows (${dmlTotalMaxRows}).`
|
|
38461
|
+
);
|
|
38462
|
+
}
|
|
38463
|
+
const runtime = await createRuntime(serverOptions, {
|
|
38464
|
+
sql: input.sql,
|
|
38465
|
+
profile: input.profile,
|
|
38466
|
+
maxRecords: dmlMaxRows2 + 1,
|
|
38467
|
+
fetchParallel: input.fetchParallel,
|
|
38468
|
+
onLimit: DEFAULT_ON_LIMIT,
|
|
38469
|
+
timeout: input.timeout
|
|
38470
|
+
});
|
|
38471
|
+
let totalAffected = staticInsertTotal;
|
|
38472
|
+
const batchResult = await executeBatchSql(runtime.sql, runtime.client, {
|
|
38473
|
+
maxRecords: runtime.maxRecords,
|
|
38474
|
+
fetchParallel: runtime.fetchParallel,
|
|
38475
|
+
onLimitReached: runtime.onLimit,
|
|
38476
|
+
cacheContext: runtime.cacheContext,
|
|
38477
|
+
// 合計タイムアウト(解決済みの runtime.timeout。per-request と同値)
|
|
38478
|
+
timeoutMs: runtime.timeout,
|
|
38479
|
+
confirm: async (count, operation) => {
|
|
38480
|
+
if (count > dmlMaxRows2) {
|
|
38481
|
+
throw new Error(`ArgumentError: ${operation} affected rows (${count}) exceed dmlMaxRows (${dmlMaxRows2}).`);
|
|
38482
|
+
}
|
|
38483
|
+
totalAffected += count;
|
|
38484
|
+
if (dmlTotalMaxRows !== void 0 && totalAffected > dmlTotalMaxRows) {
|
|
38485
|
+
throw new Error(
|
|
38486
|
+
`ArgumentError: batch affected rows (${totalAffected}) exceed dmlTotalMaxRows (${dmlTotalMaxRows}).`
|
|
38487
|
+
);
|
|
38488
|
+
}
|
|
38489
|
+
return true;
|
|
38490
|
+
}
|
|
38491
|
+
});
|
|
38492
|
+
return toBatchQueryPayload(batchResult);
|
|
38493
|
+
}
|
|
37450
38494
|
async function mutate(input) {
|
|
37451
38495
|
const dmlMaxRows2 = requireDmlApproval(input, "ksql_mutate");
|
|
37452
38496
|
const validation = await validate(input);
|
|
38497
|
+
if (validation.batch) {
|
|
38498
|
+
return mutateBatch(input, validation, dmlMaxRows2);
|
|
38499
|
+
}
|
|
37453
38500
|
if (!validation.isDml) {
|
|
37454
38501
|
throw new Error(`ArgumentError: ${validation.statementType} is not allowed by ksql_mutate. Use ksql_query.`);
|
|
37455
38502
|
}
|
|
@@ -37466,11 +38513,13 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
37466
38513
|
sql: input.sql,
|
|
37467
38514
|
profile: input.profile,
|
|
37468
38515
|
maxRecords: dmlMaxRows2 + 1,
|
|
38516
|
+
fetchParallel: input.fetchParallel,
|
|
37469
38517
|
onLimit: DEFAULT_ON_LIMIT,
|
|
37470
38518
|
timeout: input.timeout
|
|
37471
38519
|
});
|
|
37472
38520
|
const result = await executeSql(runtime.sql, runtime.client, {
|
|
37473
38521
|
maxRecords: runtime.maxRecords,
|
|
38522
|
+
fetchParallel: runtime.fetchParallel,
|
|
37474
38523
|
onLimitReached: runtime.onLimit,
|
|
37475
38524
|
cacheContext: runtime.cacheContext,
|
|
37476
38525
|
confirm: async (count, operation) => {
|
|
@@ -37490,6 +38539,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
37490
38539
|
sql: `DESCRIBE APP${input.app}`,
|
|
37491
38540
|
profile: input.profile,
|
|
37492
38541
|
maxRecords: input.maxRecords,
|
|
38542
|
+
fetchParallel: input.fetchParallel,
|
|
37493
38543
|
onLimit: input.onLimit,
|
|
37494
38544
|
timeout: input.timeout
|
|
37495
38545
|
});
|
|
@@ -37499,15 +38549,19 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
37499
38549
|
sql: "SHOW APPS",
|
|
37500
38550
|
profile: input.profile,
|
|
37501
38551
|
maxRecords: input.maxRecords,
|
|
38552
|
+
fetchParallel: input.fetchParallel,
|
|
37502
38553
|
onLimit: input.onLimit,
|
|
37503
38554
|
timeout: input.timeout
|
|
37504
38555
|
});
|
|
37505
38556
|
}
|
|
37506
38557
|
async function saveQuery(input) {
|
|
37507
|
-
const validation =
|
|
37508
|
-
|
|
37509
|
-
|
|
37510
|
-
|
|
38558
|
+
const validation = requireSingleStatement(
|
|
38559
|
+
await validate({
|
|
38560
|
+
sql: input.sql,
|
|
38561
|
+
profile: input.defaultProfile
|
|
38562
|
+
}),
|
|
38563
|
+
"ksql_save_query"
|
|
38564
|
+
);
|
|
37511
38565
|
assertSavedQuerySafety(input, {
|
|
37512
38566
|
isDml: validation.isDml,
|
|
37513
38567
|
statementType: validation.statementType
|
|
@@ -37551,10 +38605,13 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
37551
38605
|
const saved = getSavedQuery(catalog, input.name);
|
|
37552
38606
|
assertProfileOverrideAllowed(saved, input.profile);
|
|
37553
38607
|
const profile2 = input.profile ?? saved.defaultProfile;
|
|
37554
|
-
const validation =
|
|
37555
|
-
|
|
37556
|
-
|
|
37557
|
-
|
|
38608
|
+
const validation = requireSingleStatement(
|
|
38609
|
+
await validate({
|
|
38610
|
+
sql: saved.sql,
|
|
38611
|
+
profile: profile2
|
|
38612
|
+
}),
|
|
38613
|
+
"ksql_run_saved_query"
|
|
38614
|
+
);
|
|
37558
38615
|
assertSavedQuerySafety(saved, {
|
|
37559
38616
|
isDml: validation.isDml,
|
|
37560
38617
|
statementType: validation.statementType
|
|
@@ -37564,6 +38621,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
37564
38621
|
sql: saved.sql,
|
|
37565
38622
|
profile: profile2,
|
|
37566
38623
|
maxRecords: input.maxRecords,
|
|
38624
|
+
fetchParallel: input.fetchParallel,
|
|
37567
38625
|
onLimit: input.onLimit,
|
|
37568
38626
|
timeout: input.timeout
|
|
37569
38627
|
});
|
|
@@ -37580,6 +38638,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
37580
38638
|
allowDml: true,
|
|
37581
38639
|
confirmText: "yes",
|
|
37582
38640
|
dmlMaxRows: dmlMaxRows2,
|
|
38641
|
+
fetchParallel: input.fetchParallel,
|
|
37583
38642
|
timeout: input.timeout
|
|
37584
38643
|
});
|
|
37585
38644
|
return {
|
|
@@ -37628,6 +38687,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
37628
38687
|
// src/mcp/schemas.ts
|
|
37629
38688
|
var profile = external_exports.string().min(1).optional();
|
|
37630
38689
|
var maxRecords = external_exports.number().int().positive().optional();
|
|
38690
|
+
var fetchParallel = external_exports.number().int().min(1).max(10).optional();
|
|
37631
38691
|
var onLimit = external_exports.enum(["error", "truncate"]).optional();
|
|
37632
38692
|
var timeout = external_exports.number().int().positive().optional();
|
|
37633
38693
|
var dmlMaxRows = external_exports.number().int().positive();
|
|
@@ -37645,8 +38705,13 @@ var queryInputSchema = external_exports.object({
|
|
|
37645
38705
|
sql: external_exports.string().min(1),
|
|
37646
38706
|
profile,
|
|
37647
38707
|
maxRecords,
|
|
38708
|
+
fetchParallel,
|
|
37648
38709
|
onLimit,
|
|
37649
|
-
timeout
|
|
38710
|
+
timeout,
|
|
38711
|
+
/** バッチ(複文)専用: 実行時エラー後も後続文を実行する(既定 false = fail-fast) */
|
|
38712
|
+
continueOnError: external_exports.boolean().optional(),
|
|
38713
|
+
/** バッチ(複文)専用: 返却する結果セットの合計行数上限(既定なし) */
|
|
38714
|
+
maxTotalRecords: external_exports.number().int().positive().optional()
|
|
37650
38715
|
});
|
|
37651
38716
|
var mutateInputSchema = external_exports.object({
|
|
37652
38717
|
sql: external_exports.string().min(1),
|
|
@@ -37654,18 +38719,24 @@ var mutateInputSchema = external_exports.object({
|
|
|
37654
38719
|
allowDml: external_exports.literal(true),
|
|
37655
38720
|
confirmText: external_exports.literal("yes"),
|
|
37656
38721
|
dmlMaxRows,
|
|
37657
|
-
|
|
38722
|
+
fetchParallel,
|
|
38723
|
+
timeout,
|
|
38724
|
+
/** バッチ(複文)専用: バッチ合計の影響行数上限(既定なし = 文ごとの dmlMaxRows のみ)。
|
|
38725
|
+
* なお DML バッチに continueOnError は存在しない(常に fail-fast) */
|
|
38726
|
+
dmlTotalMaxRows: dmlMaxRows.optional()
|
|
37658
38727
|
});
|
|
37659
38728
|
var describeAppInputSchema = external_exports.object({
|
|
37660
38729
|
app: external_exports.number().int().positive(),
|
|
37661
38730
|
profile,
|
|
37662
38731
|
maxRecords,
|
|
38732
|
+
fetchParallel,
|
|
37663
38733
|
onLimit,
|
|
37664
38734
|
timeout
|
|
37665
38735
|
});
|
|
37666
38736
|
var showAppsInputSchema = external_exports.object({
|
|
37667
38737
|
profile,
|
|
37668
38738
|
maxRecords,
|
|
38739
|
+
fetchParallel,
|
|
37669
38740
|
onLimit,
|
|
37670
38741
|
timeout
|
|
37671
38742
|
});
|
|
@@ -37687,6 +38758,7 @@ var runSavedQueryInputSchema = external_exports.object({
|
|
|
37687
38758
|
name: savedQueryName,
|
|
37688
38759
|
profile,
|
|
37689
38760
|
maxRecords,
|
|
38761
|
+
fetchParallel,
|
|
37690
38762
|
onLimit,
|
|
37691
38763
|
timeout,
|
|
37692
38764
|
allowDml: external_exports.literal(true).optional(),
|