@rex0220/kintone-sql-tools 3.1.0 → 3.3.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 +5 -4
- package/dist-cli/ksql.js +614 -123
- package/dist-mcp/ksql-mcp.js +617 -126
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-mcp/ksql-mcp.js
CHANGED
|
@@ -31029,10 +31029,14 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
31029
31029
|
["LTRIM", "LTRIM" /* LTRIM */],
|
|
31030
31030
|
["RTRIM", "RTRIM" /* RTRIM */],
|
|
31031
31031
|
["LENGTH", "LENGTH" /* LENGTH */],
|
|
31032
|
+
["LENGTH_CHAR", "LENGTH_CHAR" /* LENGTH_CHAR */],
|
|
31032
31033
|
["SUBSTRING", "SUBSTRING" /* SUBSTRING */],
|
|
31033
31034
|
["SUBSTR", "SUBSTR" /* SUBSTR */],
|
|
31034
31035
|
["CONCAT", "CONCAT" /* CONCAT */],
|
|
31035
31036
|
["REPLACE", "REPLACE" /* REPLACE */],
|
|
31037
|
+
["REGEXP_LIKE", "REGEXP_LIKE" /* REGEXP_LIKE */],
|
|
31038
|
+
["REGEXP_REPLACE", "REGEXP_REPLACE" /* REGEXP_REPLACE */],
|
|
31039
|
+
["REGEXP_SUBSTR", "REGEXP_SUBSTR" /* REGEXP_SUBSTR */],
|
|
31036
31040
|
["COALESCE", "COALESCE" /* COALESCE */],
|
|
31037
31041
|
["NULLIF", "NULLIF" /* NULLIF */],
|
|
31038
31042
|
["ISNULL", "ISNULL" /* ISNULL */],
|
|
@@ -31041,6 +31045,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
31041
31045
|
["LEAST", "LEAST" /* LEAST */],
|
|
31042
31046
|
["LPAD", "LPAD" /* LPAD */],
|
|
31043
31047
|
["RPAD", "RPAD" /* RPAD */],
|
|
31048
|
+
["TRANSLATE", "TRANSLATE" /* TRANSLATE */],
|
|
31044
31049
|
["CAST", "CAST" /* CAST */],
|
|
31045
31050
|
["CONVERT", "CONVERT" /* CONVERT */],
|
|
31046
31051
|
["FORMAT", "FORMAT" /* FORMAT */],
|
|
@@ -31164,7 +31169,7 @@ var Lexer = class {
|
|
|
31164
31169
|
);
|
|
31165
31170
|
}
|
|
31166
31171
|
// ----------------------------------------------------------
|
|
31167
|
-
// 数値:
|
|
31172
|
+
// 数値: digits[.digits][e[+-]digits](先頭/末尾 dot は受理しない)
|
|
31168
31173
|
// ----------------------------------------------------------
|
|
31169
31174
|
readNumber(start) {
|
|
31170
31175
|
while (this.pos < this.input.length && isDigit(this.input[this.pos])) {
|
|
@@ -31176,6 +31181,16 @@ var Lexer = class {
|
|
|
31176
31181
|
this.pos++;
|
|
31177
31182
|
}
|
|
31178
31183
|
}
|
|
31184
|
+
if (this.pos < this.input.length && (this.input[this.pos] === "e" || this.input[this.pos] === "E")) {
|
|
31185
|
+
this.pos++;
|
|
31186
|
+
if (this.pos < this.input.length && (this.input[this.pos] === "+" || this.input[this.pos] === "-")) {
|
|
31187
|
+
this.pos++;
|
|
31188
|
+
}
|
|
31189
|
+
if (this.pos >= this.input.length || !isDigit(this.input[this.pos])) {
|
|
31190
|
+
throw new LexError("\u6307\u6570\u90E8\u306B\u306F\u6570\u5B57\u304C\u5FC5\u8981\u3067\u3059", start, this.input, this.pos >= this.input.length);
|
|
31191
|
+
}
|
|
31192
|
+
while (this.pos < this.input.length && isDigit(this.input[this.pos])) this.pos++;
|
|
31193
|
+
}
|
|
31179
31194
|
return this.makeToken(
|
|
31180
31195
|
"NUMBER" /* NUMBER */,
|
|
31181
31196
|
this.input.slice(start, this.pos),
|
|
@@ -31375,8 +31390,94 @@ function isJapanese(cp) {
|
|
|
31375
31390
|
return cp >= 12352 && cp <= 12543 || cp >= 13312 && cp <= 40959 || cp >= 63744 && cp <= 64255 || cp >= 65281 && cp <= 65376;
|
|
31376
31391
|
}
|
|
31377
31392
|
|
|
31393
|
+
// src/core/exactDecimal.ts
|
|
31394
|
+
var DECIMAL_PATTERN = /^([+-]?)(?:(\d+)(?:\.(\d*))?|\.(\d+))(?:[eE]([+-]?)(\d+))?$/;
|
|
31395
|
+
function parseSafeExponent(sign, digits) {
|
|
31396
|
+
if (digits === void 0) return 0;
|
|
31397
|
+
let value = 0;
|
|
31398
|
+
for (const digit of digits) {
|
|
31399
|
+
value = value * 10 + (digit.charCodeAt(0) - 48);
|
|
31400
|
+
if (!Number.isSafeInteger(value)) return null;
|
|
31401
|
+
}
|
|
31402
|
+
return sign === "-" ? -value : value;
|
|
31403
|
+
}
|
|
31404
|
+
function parseExactDecimal(input) {
|
|
31405
|
+
const match = DECIMAL_PATTERN.exec(input.trim());
|
|
31406
|
+
if (match === null) return null;
|
|
31407
|
+
const exponent = parseSafeExponent(match[5], match[6]);
|
|
31408
|
+
if (exponent === null) return null;
|
|
31409
|
+
const fraction = match[3] ?? match[4] ?? "";
|
|
31410
|
+
let coefficient = `${match[2] ?? ""}${fraction}`.replace(/^0+/, "");
|
|
31411
|
+
if (coefficient === "") return { sign: 0, coefficient: "0", scale: 0 };
|
|
31412
|
+
let scale = fraction.length - exponent;
|
|
31413
|
+
if (!Number.isSafeInteger(scale)) return null;
|
|
31414
|
+
const trailingZeros = /0+$/.exec(coefficient)?.[0].length ?? 0;
|
|
31415
|
+
if (trailingZeros > 0) {
|
|
31416
|
+
coefficient = coefficient.slice(0, -trailingZeros);
|
|
31417
|
+
scale -= trailingZeros;
|
|
31418
|
+
if (!Number.isSafeInteger(scale)) return null;
|
|
31419
|
+
}
|
|
31420
|
+
if (!Number.isSafeInteger(coefficient.length - scale)) return null;
|
|
31421
|
+
const sign = match[1] === "-" ? -1 : 1;
|
|
31422
|
+
return { sign, coefficient, scale };
|
|
31423
|
+
}
|
|
31424
|
+
function formatPlainDecimal(dec) {
|
|
31425
|
+
if (dec.sign === 0) return "0";
|
|
31426
|
+
const digits = dec.coefficient;
|
|
31427
|
+
let magnitude;
|
|
31428
|
+
if (dec.scale <= 0) {
|
|
31429
|
+
magnitude = `${digits}${"0".repeat(-dec.scale)}`;
|
|
31430
|
+
} else if (digits.length > dec.scale) {
|
|
31431
|
+
const point = digits.length - dec.scale;
|
|
31432
|
+
magnitude = `${digits.slice(0, point)}.${digits.slice(point)}`;
|
|
31433
|
+
} else {
|
|
31434
|
+
magnitude = `0.${"0".repeat(dec.scale - digits.length)}${digits}`;
|
|
31435
|
+
}
|
|
31436
|
+
return dec.sign === -1 ? `-${magnitude}` : magnitude;
|
|
31437
|
+
}
|
|
31438
|
+
function toPlainDecimal(input) {
|
|
31439
|
+
const dec = parseExactDecimal(input);
|
|
31440
|
+
return dec === null ? null : formatPlainDecimal(dec);
|
|
31441
|
+
}
|
|
31442
|
+
function compareMagnitudes(left, right) {
|
|
31443
|
+
const leftPoint = left.coefficient.length - left.scale;
|
|
31444
|
+
const rightPoint = right.coefficient.length - right.scale;
|
|
31445
|
+
if (!Number.isSafeInteger(leftPoint) || !Number.isSafeInteger(rightPoint)) {
|
|
31446
|
+
throw new Error("ArgumentError: exact decimal scale is outside the supported range.");
|
|
31447
|
+
}
|
|
31448
|
+
if (leftPoint !== rightPoint) return leftPoint < rightPoint ? -1 : 1;
|
|
31449
|
+
const width = Math.max(left.coefficient.length, right.coefficient.length);
|
|
31450
|
+
for (let index = 0; index < width; index++) {
|
|
31451
|
+
const a = index < left.coefficient.length ? left.coefficient.charCodeAt(index) : 48;
|
|
31452
|
+
const b = index < right.coefficient.length ? right.coefficient.charCodeAt(index) : 48;
|
|
31453
|
+
if (a !== b) return a < b ? -1 : 1;
|
|
31454
|
+
}
|
|
31455
|
+
return 0;
|
|
31456
|
+
}
|
|
31457
|
+
function compareExactDecimal(left, right) {
|
|
31458
|
+
if (left.sign !== right.sign) return left.sign < right.sign ? -1 : 1;
|
|
31459
|
+
if (left.sign === 0) return 0;
|
|
31460
|
+
const magnitude = compareMagnitudes(left, right);
|
|
31461
|
+
return left.sign === -1 ? magnitude === 0 ? 0 : magnitude === -1 ? 1 : -1 : magnitude;
|
|
31462
|
+
}
|
|
31463
|
+
function compareDecimal(left, right) {
|
|
31464
|
+
const a = parseExactDecimal(left);
|
|
31465
|
+
const b = parseExactDecimal(right);
|
|
31466
|
+
if (a === null || b === null) {
|
|
31467
|
+
throw new Error("ArgumentError: compareDecimal requires finite decimal inputs.");
|
|
31468
|
+
}
|
|
31469
|
+
return compareExactDecimal(a, b);
|
|
31470
|
+
}
|
|
31471
|
+
|
|
31378
31472
|
// src/types/ast.ts
|
|
31379
31473
|
var NO_FROM_CTE_NAME = "__NO_FROM__";
|
|
31474
|
+
function makeNumberLiteral(raw) {
|
|
31475
|
+
return { type: "NUMBER", value: Number(raw), raw };
|
|
31476
|
+
}
|
|
31477
|
+
function numberLiteralText(node) {
|
|
31478
|
+
const source = node.raw ?? String(node.value);
|
|
31479
|
+
return toPlainDecimal(source) ?? source;
|
|
31480
|
+
}
|
|
31380
31481
|
|
|
31381
31482
|
// src/parser/parser.ts
|
|
31382
31483
|
var MAX_BATCH_STATEMENTS = 20;
|
|
@@ -31400,13 +31501,18 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
|
|
|
31400
31501
|
"LTRIM" /* LTRIM */,
|
|
31401
31502
|
"RTRIM" /* RTRIM */,
|
|
31402
31503
|
"LENGTH" /* LENGTH */,
|
|
31504
|
+
"LENGTH_CHAR" /* LENGTH_CHAR */,
|
|
31403
31505
|
"SUBSTRING" /* SUBSTRING */,
|
|
31404
31506
|
"SUBSTR" /* SUBSTR */,
|
|
31405
31507
|
"CONCAT" /* CONCAT */,
|
|
31406
31508
|
"REPLACE" /* REPLACE */,
|
|
31509
|
+
"TRANSLATE" /* TRANSLATE */,
|
|
31407
31510
|
"COALESCE" /* COALESCE */,
|
|
31408
31511
|
"NULLIF" /* NULLIF */,
|
|
31409
31512
|
"ISNULL" /* ISNULL */,
|
|
31513
|
+
"REGEXP_LIKE" /* REGEXP_LIKE */,
|
|
31514
|
+
"REGEXP_REPLACE" /* REGEXP_REPLACE */,
|
|
31515
|
+
"REGEXP_SUBSTR" /* REGEXP_SUBSTR */,
|
|
31410
31516
|
"LEFT" /* LEFT */,
|
|
31411
31517
|
"RIGHT" /* RIGHT */,
|
|
31412
31518
|
"INSTR" /* INSTR */,
|
|
@@ -31455,6 +31561,7 @@ var ParseError = class extends Error {
|
|
|
31455
31561
|
var Parser = class {
|
|
31456
31562
|
constructor(tokens) {
|
|
31457
31563
|
this.tokens = tokens;
|
|
31564
|
+
this.allowUnaryPlusNumber = false;
|
|
31458
31565
|
this.pos = 0;
|
|
31459
31566
|
/** WITH 句で定義された CTE 名のセット(parseTableRef で参照) */
|
|
31460
31567
|
this.cteNames = /* @__PURE__ */ new Set();
|
|
@@ -32169,12 +32276,12 @@ var Parser = class {
|
|
|
32169
32276
|
if (this.peek().kind === "-" /* MINUS */) {
|
|
32170
32277
|
this.advance();
|
|
32171
32278
|
const operand = this.parseAggPrimary();
|
|
32172
|
-
if (operand.type === "NUMBER") return {
|
|
32173
|
-
return { type: "AGG_ARITH", left:
|
|
32279
|
+
if (operand.type === "NUMBER") return makeNumberLiteral(`-${numberLiteralText(operand)}`);
|
|
32280
|
+
return { type: "AGG_ARITH", left: makeNumberLiteral("0"), op: "-", right: operand };
|
|
32174
32281
|
}
|
|
32175
32282
|
if (this.peek().kind === "NUMBER" /* NUMBER */) {
|
|
32176
32283
|
const tok = this.advance();
|
|
32177
|
-
return
|
|
32284
|
+
return makeNumberLiteral(tok.value);
|
|
32178
32285
|
}
|
|
32179
32286
|
const aggFunc = this.tryAggregateFunc();
|
|
32180
32287
|
if (aggFunc !== null) {
|
|
@@ -32223,11 +32330,19 @@ var Parser = class {
|
|
|
32223
32330
|
this.expect(")" /* RPAREN */);
|
|
32224
32331
|
return expr;
|
|
32225
32332
|
}
|
|
32333
|
+
if (this.allowUnaryPlusNumber && this.peek().kind === "+" /* PLUS */) {
|
|
32334
|
+
this.advance();
|
|
32335
|
+
const number4 = this.expect("NUMBER" /* NUMBER */, "\u5358\u9805 + \u306E\u76F4\u5F8C\u306B\u306F\u6570\u5024\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059");
|
|
32336
|
+
return makeNumberLiteral(`+${number4.value}`);
|
|
32337
|
+
}
|
|
32226
32338
|
if (this.peek().kind === "-" /* MINUS */) {
|
|
32227
32339
|
this.advance();
|
|
32340
|
+
if (this.peek().kind === "-" /* MINUS */ || this.peek().kind === "+" /* PLUS */) {
|
|
32341
|
+
throw new ParseError("\u5358\u9805\u7B26\u53F7\u3092\u91CD\u306D\u3066\u6307\u5B9A\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093", this.peek());
|
|
32342
|
+
}
|
|
32228
32343
|
const operand = this.parseArithPrimary();
|
|
32229
|
-
if (operand.type === "NUMBER") return {
|
|
32230
|
-
return { type: "ARITH", left:
|
|
32344
|
+
if (operand.type === "NUMBER") return makeNumberLiteral(`-${numberLiteralText(operand)}`);
|
|
32345
|
+
return { type: "ARITH", left: makeNumberLiteral("0"), op: "-", right: operand };
|
|
32231
32346
|
}
|
|
32232
32347
|
if (this.tryStringFuncName() !== null) {
|
|
32233
32348
|
return this.parseStringFuncExpr();
|
|
@@ -32235,7 +32350,7 @@ var Parser = class {
|
|
|
32235
32350
|
const tok = this.peek();
|
|
32236
32351
|
if (tok.kind === "NUMBER" /* NUMBER */) {
|
|
32237
32352
|
this.advance();
|
|
32238
|
-
return
|
|
32353
|
+
return makeNumberLiteral(tok.value);
|
|
32239
32354
|
}
|
|
32240
32355
|
if (tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */) {
|
|
32241
32356
|
this.advance();
|
|
@@ -32334,10 +32449,15 @@ var Parser = class {
|
|
|
32334
32449
|
["LTRIM" /* LTRIM */]: "LTRIM",
|
|
32335
32450
|
["RTRIM" /* RTRIM */]: "RTRIM",
|
|
32336
32451
|
["LENGTH" /* LENGTH */]: "LENGTH",
|
|
32452
|
+
["LENGTH_CHAR" /* LENGTH_CHAR */]: "LENGTH_CHAR",
|
|
32337
32453
|
["SUBSTRING" /* SUBSTRING */]: "SUBSTRING",
|
|
32338
32454
|
["SUBSTR" /* SUBSTR */]: "SUBSTRING",
|
|
32339
32455
|
["CONCAT" /* CONCAT */]: "CONCAT",
|
|
32340
32456
|
["REPLACE" /* REPLACE */]: "REPLACE",
|
|
32457
|
+
["REGEXP_LIKE" /* REGEXP_LIKE */]: "REGEXP_LIKE",
|
|
32458
|
+
["REGEXP_REPLACE" /* REGEXP_REPLACE */]: "REGEXP_REPLACE",
|
|
32459
|
+
["REGEXP_SUBSTR" /* REGEXP_SUBSTR */]: "REGEXP_SUBSTR",
|
|
32460
|
+
["TRANSLATE" /* TRANSLATE */]: "TRANSLATE",
|
|
32341
32461
|
["COALESCE" /* COALESCE */]: "COALESCE",
|
|
32342
32462
|
["NULLIF" /* NULLIF */]: "NULLIF",
|
|
32343
32463
|
["ISNULL" /* ISNULL */]: "ISNULL",
|
|
@@ -32876,7 +32996,7 @@ var Parser = class {
|
|
|
32876
32996
|
}
|
|
32877
32997
|
if (tok.kind === "NUMBER" /* NUMBER */ || tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */ || tok.kind === "(" /* LPAREN */ || tok.kind === "-" /* MINUS */ || this.tryStringFuncName() !== null) {
|
|
32878
32998
|
const expr = this.parseArithAddSub();
|
|
32879
|
-
if (expr.type === "NUMBER") return
|
|
32999
|
+
if (expr.type === "NUMBER") return expr;
|
|
32880
33000
|
return { type: "ARITH_VALUE", expr };
|
|
32881
33001
|
}
|
|
32882
33002
|
throw new ParseError(
|
|
@@ -32903,15 +33023,15 @@ var Parser = class {
|
|
|
32903
33023
|
if (tok.kind === "STRING" /* STRING */) {
|
|
32904
33024
|
values.push({ type: "STRING", value: tok.value });
|
|
32905
33025
|
} else if (tok.kind === "NUMBER" /* NUMBER */) {
|
|
32906
|
-
values.push(
|
|
33026
|
+
values.push(makeNumberLiteral(tok.value));
|
|
32907
33027
|
} else if (tok.kind === "-" /* MINUS */ || tok.kind === "+" /* PLUS */) {
|
|
32908
33028
|
const number4 = this.peek();
|
|
32909
33029
|
if (number4.kind !== "NUMBER" /* NUMBER */) {
|
|
32910
33030
|
throw new ParseError(invalidValueMessage, tok);
|
|
32911
33031
|
}
|
|
32912
33032
|
this.advance();
|
|
32913
|
-
const sign = tok.kind === "-" /* MINUS */ ? -
|
|
32914
|
-
values.push({
|
|
33033
|
+
const sign = tok.kind === "-" /* MINUS */ ? "-" : "+";
|
|
33034
|
+
values.push(makeNumberLiteral(`${sign}${number4.value}`));
|
|
32915
33035
|
} else if (tok.kind === "VARIABLE" /* VARIABLE */) {
|
|
32916
33036
|
values.push({ type: "VARIABLE", name: tok.value.slice(1).toLowerCase() });
|
|
32917
33037
|
} else {
|
|
@@ -33098,12 +33218,16 @@ var Parser = class {
|
|
|
33098
33218
|
} else if (this.peek().kind === "IF" /* IF */) {
|
|
33099
33219
|
const expr = this.parseIfExpr();
|
|
33100
33220
|
row.push({ type: "CASE_VALUE", expr });
|
|
33221
|
+
} else if (this.peek().kind === "-" /* MINUS */ || this.peek().kind === "+" /* PLUS */) {
|
|
33222
|
+
const sign = this.advance();
|
|
33223
|
+
const number4 = this.expect("NUMBER" /* NUMBER */, "INSERT \u306E\u5358\u9805\u7B26\u53F7\u306E\u76F4\u5F8C\u306B\u306F\u6570\u5024\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059");
|
|
33224
|
+
row.push(makeNumberLiteral(`${sign.kind === "-" /* MINUS */ ? "-" : "+"}${number4.value}`));
|
|
33101
33225
|
} else {
|
|
33102
33226
|
const tok = this.advance();
|
|
33103
33227
|
if (tok.kind === "STRING" /* STRING */) {
|
|
33104
33228
|
row.push({ type: "STRING", value: tok.value });
|
|
33105
33229
|
} else if (tok.kind === "NUMBER" /* NUMBER */) {
|
|
33106
|
-
row.push(
|
|
33230
|
+
row.push(makeNumberLiteral(tok.value));
|
|
33107
33231
|
} else {
|
|
33108
33232
|
throw new ParseError("INSERT \u306E\u5024\u306B\u306F\u6587\u5B57\u5217\u30FB\u6570\u5024\u30FB\u914D\u5217\u30EA\u30C6\u30E9\u30EB\u30FBCASE WHEN \u304C\u5FC5\u8981\u3067\u3059", tok);
|
|
33109
33233
|
}
|
|
@@ -33127,6 +33251,12 @@ var Parser = class {
|
|
|
33127
33251
|
const { appId, subtableCode } = extractTableRef(name, this.prev());
|
|
33128
33252
|
this.expect("SET" /* SET */);
|
|
33129
33253
|
const assignments = this.parseAssignments();
|
|
33254
|
+
if (subtableCode && assignments.some((a) => a.value.type === "STRING_FUNC")) {
|
|
33255
|
+
throw new ParseError(
|
|
33256
|
+
"\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE SET \u3067\u306F\u6587\u5B57\u5217\u95A2\u6570\u3092\u76F4\u63A5\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093",
|
|
33257
|
+
this.prev()
|
|
33258
|
+
);
|
|
33259
|
+
}
|
|
33130
33260
|
let from = null;
|
|
33131
33261
|
if (this.consume("FROM" /* FROM */)) {
|
|
33132
33262
|
const table = this.parseTableRef();
|
|
@@ -33170,7 +33300,14 @@ var Parser = class {
|
|
|
33170
33300
|
from.targetFilter = decomposed.targetFilter;
|
|
33171
33301
|
} else if (assignments.some((a) => a.value.type === "SOURCE_FIELD")) {
|
|
33172
33302
|
throw new ParseError(
|
|
33173
|
-
"SET \u306E\u5024\u306B\
|
|
33303
|
+
"SET \u306E\u5024\u306B\u30D5\u30A3\u30FC\u30EB\u30C9\u53C2\u7167\u3092\u5358\u72EC\u3067\u6307\u5B9A\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093",
|
|
33304
|
+
whereTok
|
|
33305
|
+
);
|
|
33306
|
+
} else if (assignments.some(
|
|
33307
|
+
(a) => a.value.type === "STRING_FUNC" && this.nodeContainsAnyQualifier(a.value)
|
|
33308
|
+
)) {
|
|
33309
|
+
throw new ParseError(
|
|
33310
|
+
"UPDATE SET \u306E\u6587\u5B57\u5217\u95A2\u6570\u3067\u306F\u66F4\u65B0\u5148\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u4FEE\u98FE\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044",
|
|
33174
33311
|
whereTok
|
|
33175
33312
|
);
|
|
33176
33313
|
}
|
|
@@ -33237,6 +33374,12 @@ var Parser = class {
|
|
|
33237
33374
|
}
|
|
33238
33375
|
validateUpdateFromAssignments(assignments, sourceAlias, tok) {
|
|
33239
33376
|
for (const assignment of assignments) {
|
|
33377
|
+
if (assignment.value.type === "STRING_FUNC") {
|
|
33378
|
+
throw new ParseError(
|
|
33379
|
+
"UPDATE ... FROM \u306E SET \u3067\u306F\u6587\u5B57\u5217\u95A2\u6570\u3092\u76F4\u63A5\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093",
|
|
33380
|
+
tok
|
|
33381
|
+
);
|
|
33382
|
+
}
|
|
33240
33383
|
if (assignment.value.type === "SOURCE_FIELD") {
|
|
33241
33384
|
if (assignment.value.alias.toLowerCase() !== sourceAlias.toLowerCase()) {
|
|
33242
33385
|
throw new ParseError(`UPDATE ... FROM \u306E SET \u53C2\u7167\u306F\u30BD\u30FC\u30B9 alias ${sourceAlias} \u3067\u4FEE\u98FE\u3057\u3066\u304F\u3060\u3055\u3044`, tok);
|
|
@@ -33376,9 +33519,17 @@ var Parser = class {
|
|
|
33376
33519
|
this.expect(")" /* RPAREN */);
|
|
33377
33520
|
return { type: "SCALAR_SUBQUERY", query };
|
|
33378
33521
|
}
|
|
33379
|
-
const
|
|
33522
|
+
const previousAllowUnaryPlusNumber = this.allowUnaryPlusNumber;
|
|
33523
|
+
this.allowUnaryPlusNumber = true;
|
|
33524
|
+
let node;
|
|
33525
|
+
try {
|
|
33526
|
+
node = this.parseArithAddSub();
|
|
33527
|
+
} finally {
|
|
33528
|
+
this.allowUnaryPlusNumber = previousAllowUnaryPlusNumber;
|
|
33529
|
+
}
|
|
33380
33530
|
if (node.type === "NUMBER") return node;
|
|
33381
33531
|
if (node.type === "ARITH") return node;
|
|
33532
|
+
if (node.type === "STRING_FUNC") return node;
|
|
33382
33533
|
if (node.type === "FIELD_REF") {
|
|
33383
33534
|
const dot = node.field.indexOf(".");
|
|
33384
33535
|
if (dot > 0 && dot < node.field.length - 1) {
|
|
@@ -33386,7 +33537,7 @@ var Parser = class {
|
|
|
33386
33537
|
}
|
|
33387
33538
|
}
|
|
33388
33539
|
throw new ParseError(
|
|
33389
|
-
"SET \u306E\u5024\u306B\
|
|
33540
|
+
"SET \u306E\u5024\u306B\u30D5\u30A3\u30FC\u30EB\u30C9\u53C2\u7167\u3092\u5358\u72EC\u3067\u6307\u5B9A\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093",
|
|
33390
33541
|
tok
|
|
33391
33542
|
);
|
|
33392
33543
|
}
|
|
@@ -33898,7 +34049,7 @@ function convertValue(value, op) {
|
|
|
33898
34049
|
case "STRING":
|
|
33899
34050
|
return convertString(value);
|
|
33900
34051
|
case "NUMBER":
|
|
33901
|
-
return
|
|
34052
|
+
return numberLiteralText(value);
|
|
33902
34053
|
case "KINTONE_FUNC":
|
|
33903
34054
|
return convertKintoneFunc(value);
|
|
33904
34055
|
case "IN_LIST":
|
|
@@ -33927,7 +34078,7 @@ function convertInList(v, op) {
|
|
|
33927
34078
|
}
|
|
33928
34079
|
assertResolvedInListValues(v.values);
|
|
33929
34080
|
const values = v.values.map(
|
|
33930
|
-
(item) => item.type === "STRING" ? convertString(item) :
|
|
34081
|
+
(item) => item.type === "STRING" ? convertString(item) : numberLiteralText(item)
|
|
33931
34082
|
).join(",");
|
|
33932
34083
|
return `(${values})`;
|
|
33933
34084
|
}
|
|
@@ -34440,7 +34591,7 @@ function aggregateSyntheticName(func, distinct, arg) {
|
|
|
34440
34591
|
}
|
|
34441
34592
|
function arithNodeLabel(node) {
|
|
34442
34593
|
if (node.type === "FIELD_REF") return node.field;
|
|
34443
|
-
if (node.type === "NUMBER") return
|
|
34594
|
+
if (node.type === "NUMBER") return numberLiteralText(node);
|
|
34444
34595
|
if (node.type === "STRING_FUNC") return stringFuncLabel(node);
|
|
34445
34596
|
return `(${arithNodeLabel(node.left)}${node.op}${arithNodeLabel(node.right)})`;
|
|
34446
34597
|
}
|
|
@@ -34597,7 +34748,7 @@ function isNumericCandidate(expr, options) {
|
|
|
34597
34748
|
if (!isTargetField(expr.left, options)) return false;
|
|
34598
34749
|
if (expr.right.type !== "NUMBER") return false;
|
|
34599
34750
|
if (expr.op === "=") return true;
|
|
34600
|
-
return (expr.op === "<" || expr.op === ">") && Number.isSafeInteger(expr.right.value);
|
|
34751
|
+
return (expr.op === "<" || expr.op === ">") && /^[+-]?\d+$/.test(numberLiteralText(expr.right)) && Number.isSafeInteger(expr.right.value);
|
|
34601
34752
|
}
|
|
34602
34753
|
function isSelectionInCandidate(expr, options) {
|
|
34603
34754
|
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
@@ -35202,9 +35353,10 @@ function triCompare(left, right) {
|
|
|
35202
35353
|
}
|
|
35203
35354
|
function numberKey(value) {
|
|
35204
35355
|
if (value === "") return { band: 0 };
|
|
35356
|
+
const decimal = parseExactDecimal(value);
|
|
35357
|
+
if (decimal !== null) return { band: 2, value: decimal };
|
|
35205
35358
|
const numeric = Number(value);
|
|
35206
35359
|
if (numeric === Number.NEGATIVE_INFINITY) return { band: 1 };
|
|
35207
|
-
if (Number.isFinite(numeric)) return { band: 2, value: numeric };
|
|
35208
35360
|
if (numeric === Number.POSITIVE_INFINITY) return { band: 3 };
|
|
35209
35361
|
if (value === "NaN") return { band: 4 };
|
|
35210
35362
|
return { band: 5, value };
|
|
@@ -35213,7 +35365,7 @@ function compareNumbers(left, right) {
|
|
|
35213
35365
|
const a = numberKey(left);
|
|
35214
35366
|
const b = numberKey(right);
|
|
35215
35367
|
if (a.band !== b.band) return a.band < b.band ? -1 : 1;
|
|
35216
|
-
if (a.band === 2 && b.band === 2) return
|
|
35368
|
+
if (a.band === 2 && b.band === 2) return compareExactDecimal(a.value, b.value);
|
|
35217
35369
|
if (a.band === 5 && b.band === 5) return compareCodePointStrings(a.value, b.value);
|
|
35218
35370
|
return 0;
|
|
35219
35371
|
}
|
|
@@ -35313,7 +35465,9 @@ function selectScalarExtreme(values, extreme) {
|
|
|
35313
35465
|
const numeric = candidates.every((value) => !Number.isNaN(Number(value)));
|
|
35314
35466
|
const compare = (left, right) => {
|
|
35315
35467
|
if (numeric) {
|
|
35316
|
-
const
|
|
35468
|
+
const leftDecimal = parseExactDecimal(left);
|
|
35469
|
+
const rightDecimal = parseExactDecimal(right);
|
|
35470
|
+
const numericCmp = leftDecimal !== null && rightDecimal !== null ? compareExactDecimal(leftDecimal, rightDecimal) : triCompare(Number(left), Number(right));
|
|
35317
35471
|
if (numericCmp !== 0) return numericCmp;
|
|
35318
35472
|
}
|
|
35319
35473
|
return compareCodePointStrings(left, right);
|
|
@@ -35399,6 +35553,149 @@ function applyRoundOp(op, num, digits) {
|
|
|
35399
35553
|
if (digits > 0) return String(parseFloat(raw.toFixed(digits)));
|
|
35400
35554
|
return String(raw);
|
|
35401
35555
|
}
|
|
35556
|
+
function isHighSurrogate(codeUnit) {
|
|
35557
|
+
return codeUnit >= 55296 && codeUnit <= 56319;
|
|
35558
|
+
}
|
|
35559
|
+
function isLowSurrogate(codeUnit) {
|
|
35560
|
+
return codeUnit >= 56320 && codeUnit <= 57343;
|
|
35561
|
+
}
|
|
35562
|
+
function splitsSurrogatePair(value, index) {
|
|
35563
|
+
return index > 0 && index < value.length && isHighSurrogate(value.charCodeAt(index - 1)) && isLowSurrogate(value.charCodeAt(index));
|
|
35564
|
+
}
|
|
35565
|
+
function normalizeSliceIndex(index, length) {
|
|
35566
|
+
if (Number.isNaN(index) || index === Number.NEGATIVE_INFINITY) return 0;
|
|
35567
|
+
if (index === Number.POSITIVE_INFINITY) return length;
|
|
35568
|
+
const integer2 = Math.trunc(index);
|
|
35569
|
+
return integer2 < 0 ? Math.max(length + integer2, 0) : Math.min(integer2, length);
|
|
35570
|
+
}
|
|
35571
|
+
function sliceSafePrefix(value, budget) {
|
|
35572
|
+
let end = Math.min(Math.max(0, budget), value.length);
|
|
35573
|
+
if (splitsSurrogatePair(value, end)) end -= 1;
|
|
35574
|
+
return value.slice(0, end);
|
|
35575
|
+
}
|
|
35576
|
+
function sliceSafeSuffix(value, budget) {
|
|
35577
|
+
let start = Math.max(0, value.length - budget);
|
|
35578
|
+
if (splitsSurrogatePair(value, start)) start += 1;
|
|
35579
|
+
return value.slice(start);
|
|
35580
|
+
}
|
|
35581
|
+
function sliceSafeRange(value, rawStart, rawEnd) {
|
|
35582
|
+
let start = normalizeSliceIndex(rawStart, value.length);
|
|
35583
|
+
let end = normalizeSliceIndex(rawEnd, value.length);
|
|
35584
|
+
if (end <= start) return "";
|
|
35585
|
+
if (splitsSurrogatePair(value, start)) start += 1;
|
|
35586
|
+
if (splitsSurrogatePair(value, end)) end -= 1;
|
|
35587
|
+
return value.slice(start, Math.max(start, end));
|
|
35588
|
+
}
|
|
35589
|
+
function makeSafePadding(pad, gap) {
|
|
35590
|
+
const repeated = pad.repeat(Math.ceil(gap / pad.length));
|
|
35591
|
+
return sliceSafePrefix(repeated, gap);
|
|
35592
|
+
}
|
|
35593
|
+
var REGEXP_CACHE_MAX = 200;
|
|
35594
|
+
var regexpCache = /* @__PURE__ */ new Map();
|
|
35595
|
+
function normalizeRegexpFlags(flags) {
|
|
35596
|
+
if (/[^ims]/.test(flags)) {
|
|
35597
|
+
throw new Error("ArgumentError: regular expression flags may contain only i, m, or s.");
|
|
35598
|
+
}
|
|
35599
|
+
if (new Set(flags).size !== flags.length) {
|
|
35600
|
+
throw new Error("ArgumentError: regular expression flags must not contain duplicates.");
|
|
35601
|
+
}
|
|
35602
|
+
return `${flags}u`;
|
|
35603
|
+
}
|
|
35604
|
+
function compileRegexp(pattern, flags, global = false) {
|
|
35605
|
+
const normalizedFlags = normalizeRegexpFlags(flags) + (global ? "g" : "");
|
|
35606
|
+
const key = `${pattern}\0${normalizedFlags}`;
|
|
35607
|
+
const cached2 = regexpCache.get(key);
|
|
35608
|
+
if (cached2 !== void 0) {
|
|
35609
|
+
cached2.lastIndex = 0;
|
|
35610
|
+
return cached2;
|
|
35611
|
+
}
|
|
35612
|
+
let regexp;
|
|
35613
|
+
try {
|
|
35614
|
+
regexp = new RegExp(pattern, normalizedFlags);
|
|
35615
|
+
} catch (error51) {
|
|
35616
|
+
const detail = error51 instanceof Error ? error51.message : String(error51);
|
|
35617
|
+
throw new Error(`ArgumentError: invalid regular expression: ${detail}`);
|
|
35618
|
+
}
|
|
35619
|
+
if (regexpCache.size >= REGEXP_CACHE_MAX) {
|
|
35620
|
+
const oldest = regexpCache.keys().next().value;
|
|
35621
|
+
if (oldest !== void 0) regexpCache.delete(oldest);
|
|
35622
|
+
}
|
|
35623
|
+
regexpCache.set(key, regexp);
|
|
35624
|
+
return regexp;
|
|
35625
|
+
}
|
|
35626
|
+
function assertRegexpReplacement(replacement) {
|
|
35627
|
+
if (replacement.includes("$`") || replacement.includes("$'")) {
|
|
35628
|
+
throw new Error("ArgumentError: REGEXP_REPLACE replacement must not contain $` or $'.");
|
|
35629
|
+
}
|
|
35630
|
+
}
|
|
35631
|
+
function parseRegexpOccurrence(arg) {
|
|
35632
|
+
if (arg === void 0) return 0;
|
|
35633
|
+
if (!/^\d+$/.test(arg)) {
|
|
35634
|
+
throw new Error("ArgumentError: REGEXP_REPLACE occurrence must be a non-negative integer.");
|
|
35635
|
+
}
|
|
35636
|
+
return Number(arg);
|
|
35637
|
+
}
|
|
35638
|
+
function expandRegexpReplacement(replacement, match, captures, namedGroups) {
|
|
35639
|
+
let result = "";
|
|
35640
|
+
for (let i = 0; i < replacement.length; i += 1) {
|
|
35641
|
+
const char = replacement[i];
|
|
35642
|
+
if (char !== "$" || i + 1 >= replacement.length) {
|
|
35643
|
+
result += char;
|
|
35644
|
+
continue;
|
|
35645
|
+
}
|
|
35646
|
+
const next = replacement[i + 1];
|
|
35647
|
+
if (next === "$") {
|
|
35648
|
+
result += "$";
|
|
35649
|
+
i += 1;
|
|
35650
|
+
continue;
|
|
35651
|
+
}
|
|
35652
|
+
if (next === "&") {
|
|
35653
|
+
result += match;
|
|
35654
|
+
i += 1;
|
|
35655
|
+
continue;
|
|
35656
|
+
}
|
|
35657
|
+
if (next === "<" && namedGroups !== void 0) {
|
|
35658
|
+
const end = replacement.indexOf(">", i + 2);
|
|
35659
|
+
if (end >= 0) {
|
|
35660
|
+
result += namedGroups[replacement.slice(i + 2, end)] ?? "";
|
|
35661
|
+
i = end;
|
|
35662
|
+
continue;
|
|
35663
|
+
}
|
|
35664
|
+
}
|
|
35665
|
+
if (/\d/.test(next)) {
|
|
35666
|
+
const secondDigit = replacement[i + 2];
|
|
35667
|
+
if (secondDigit !== void 0 && /\d/.test(secondDigit)) {
|
|
35668
|
+
const twoDigitIndex = Number(next + secondDigit);
|
|
35669
|
+
if (twoDigitIndex >= 1 && twoDigitIndex <= captures.length) {
|
|
35670
|
+
result += captures[twoDigitIndex - 1] ?? "";
|
|
35671
|
+
i += 2;
|
|
35672
|
+
continue;
|
|
35673
|
+
}
|
|
35674
|
+
}
|
|
35675
|
+
const oneDigitIndex = Number(next);
|
|
35676
|
+
if (oneDigitIndex >= 1 && oneDigitIndex <= captures.length) {
|
|
35677
|
+
result += captures[oneDigitIndex - 1] ?? "";
|
|
35678
|
+
i += 1;
|
|
35679
|
+
continue;
|
|
35680
|
+
}
|
|
35681
|
+
}
|
|
35682
|
+
result += "$";
|
|
35683
|
+
}
|
|
35684
|
+
return result;
|
|
35685
|
+
}
|
|
35686
|
+
function replaceNthMatch(input, globalRe, replacement, n) {
|
|
35687
|
+
let matchCount = 0;
|
|
35688
|
+
return input.replace(globalRe, (match, ...callbackArgs) => {
|
|
35689
|
+
matchCount += 1;
|
|
35690
|
+
if (matchCount !== n) return match;
|
|
35691
|
+
const lastArg = callbackArgs[callbackArgs.length - 1];
|
|
35692
|
+
const hasNamedGroups = typeof lastArg === "object" && lastArg !== null;
|
|
35693
|
+
const capturesEnd = callbackArgs.length - (hasNamedGroups ? 3 : 2);
|
|
35694
|
+
const captures = callbackArgs.slice(0, capturesEnd);
|
|
35695
|
+
const namedGroups = hasNamedGroups ? lastArg : void 0;
|
|
35696
|
+
return expandRegexpReplacement(replacement, match, captures, namedGroups);
|
|
35697
|
+
});
|
|
35698
|
+
}
|
|
35402
35699
|
function evalStringFunc(expr, row) {
|
|
35403
35700
|
const args = expr.args.map((a) => evalStringFuncArg(a, row));
|
|
35404
35701
|
switch (expr.func) {
|
|
@@ -35414,23 +35711,26 @@ function evalStringFunc(expr, row) {
|
|
|
35414
35711
|
return (args[0] ?? "").trimEnd();
|
|
35415
35712
|
case "LENGTH":
|
|
35416
35713
|
return String((args[0] ?? "").length);
|
|
35714
|
+
case "LENGTH_CHAR":
|
|
35715
|
+
assertArity("LENGTH_CHAR", args, 1, 1);
|
|
35716
|
+
return String([...args[0] ?? ""].length);
|
|
35417
35717
|
case "SUBSTRING": {
|
|
35418
35718
|
const str = args[0] ?? "";
|
|
35419
35719
|
const start = Math.max(0, Number(args[1] ?? "1") - 1);
|
|
35420
35720
|
const len = args[2] !== void 0 ? Number(args[2]) : void 0;
|
|
35421
|
-
return len !== void 0 ?
|
|
35721
|
+
return sliceSafeRange(str, start, len !== void 0 ? start + len : str.length);
|
|
35422
35722
|
}
|
|
35423
35723
|
case "LEFT": {
|
|
35424
35724
|
assertArity("LEFT", args, 2, 2);
|
|
35425
35725
|
const str = args[0];
|
|
35426
35726
|
const n = Math.trunc(Number(args[1]));
|
|
35427
|
-
return Number.isNaN(n) || n <= 0 ? "" : str
|
|
35727
|
+
return Number.isNaN(n) || n <= 0 ? "" : sliceSafePrefix(str, n);
|
|
35428
35728
|
}
|
|
35429
35729
|
case "RIGHT": {
|
|
35430
35730
|
assertArity("RIGHT", args, 2, 2);
|
|
35431
35731
|
const str = args[0];
|
|
35432
35732
|
const n = Math.trunc(Number(args[1]));
|
|
35433
|
-
return Number.isNaN(n) || n <= 0 ? "" : str
|
|
35733
|
+
return Number.isNaN(n) || n <= 0 ? "" : sliceSafeSuffix(str, n);
|
|
35434
35734
|
}
|
|
35435
35735
|
case "INSTR":
|
|
35436
35736
|
assertArity("INSTR", args, 2, 2);
|
|
@@ -35441,10 +35741,11 @@ function evalStringFunc(expr, row) {
|
|
|
35441
35741
|
const str = args[0];
|
|
35442
35742
|
const n = Math.trunc(Number(args[1]));
|
|
35443
35743
|
if (Number.isNaN(n) || n <= 0) return "";
|
|
35444
|
-
if (str.length >= n) return str
|
|
35744
|
+
if (str.length >= n) return sliceSafePrefix(str, n);
|
|
35445
35745
|
const pad = args[2] ?? " ";
|
|
35446
35746
|
if (pad === "") return str;
|
|
35447
|
-
|
|
35747
|
+
const padding = makeSafePadding(pad, n - str.length);
|
|
35748
|
+
return expr.func === "LPAD" ? padding + str : str + padding;
|
|
35448
35749
|
}
|
|
35449
35750
|
case "GREATEST":
|
|
35450
35751
|
case "LEAST":
|
|
@@ -35458,6 +35759,36 @@ function evalStringFunc(expr, row) {
|
|
|
35458
35759
|
const to = args[2] ?? "";
|
|
35459
35760
|
return from === "" ? str : str.split(from).join(to);
|
|
35460
35761
|
}
|
|
35762
|
+
case "REGEXP_LIKE": {
|
|
35763
|
+
assertArity("REGEXP_LIKE", args, 2, 3);
|
|
35764
|
+
return compileRegexp(args[1], args[2] ?? "").test(args[0]) ? "1" : "0";
|
|
35765
|
+
}
|
|
35766
|
+
case "REGEXP_REPLACE": {
|
|
35767
|
+
assertArity("REGEXP_REPLACE", args, 3, 5);
|
|
35768
|
+
assertRegexpReplacement(args[2]);
|
|
35769
|
+
const occurrence = parseRegexpOccurrence(args[4]);
|
|
35770
|
+
const regexp = compileRegexp(args[1], args[3] ?? "", true);
|
|
35771
|
+
return occurrence === 0 ? args[0].replace(regexp, args[2]) : replaceNthMatch(args[0], regexp, args[2], occurrence);
|
|
35772
|
+
}
|
|
35773
|
+
case "REGEXP_SUBSTR": {
|
|
35774
|
+
assertArity("REGEXP_SUBSTR", args, 2, 3);
|
|
35775
|
+
return compileRegexp(args[1], args[2] ?? "").exec(args[0])?.[0] ?? "";
|
|
35776
|
+
}
|
|
35777
|
+
case "TRANSLATE": {
|
|
35778
|
+
assertArity("TRANSLATE", args, 3, 3);
|
|
35779
|
+
const from = [...args[1]];
|
|
35780
|
+
const to = [...args[2]];
|
|
35781
|
+
if (from.length !== to.length) {
|
|
35782
|
+
throw new Error(
|
|
35783
|
+
`ArgumentError: TRANSLATE \u306E from \u3068 to \u306F\u540C\u3058\u6587\u5B57\u6570\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08from=${from.length}, to=${to.length}\uFF09`
|
|
35784
|
+
);
|
|
35785
|
+
}
|
|
35786
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
35787
|
+
from.forEach((ch, i) => {
|
|
35788
|
+
if (!map2.has(ch)) map2.set(ch, to[i]);
|
|
35789
|
+
});
|
|
35790
|
+
return [...args[0]].map((ch) => map2.get(ch) ?? ch).join("");
|
|
35791
|
+
}
|
|
35461
35792
|
case "COALESCE":
|
|
35462
35793
|
return args.find((a) => a !== "") ?? "";
|
|
35463
35794
|
case "NULLIF":
|
|
@@ -35621,7 +35952,7 @@ function evalStringFuncArg(arg, row) {
|
|
|
35621
35952
|
if (arg.type === "STRING") return arg.value;
|
|
35622
35953
|
if (arg.type === "STRING_FUNC") return evalStringFunc(arg, row);
|
|
35623
35954
|
if (arg.type === "FIELD_REF") return resolveFieldRef(row, arg.field);
|
|
35624
|
-
if (arg.type === "NUMBER") return
|
|
35955
|
+
if (arg.type === "NUMBER") return numberLiteralText(arg);
|
|
35625
35956
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return "";
|
|
35626
35957
|
return String(evalArithExpr(arg, row));
|
|
35627
35958
|
}
|
|
@@ -35670,7 +36001,7 @@ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType, semantics
|
|
|
35670
36001
|
let values = null;
|
|
35671
36002
|
if (right.type === "IN_LIST") {
|
|
35672
36003
|
assertResolvedInListValues2(right.values);
|
|
35673
|
-
values = new Set(right.values.map((v) => String(v.value)));
|
|
36004
|
+
values = new Set(right.values.map((v) => v.type === "NUMBER" ? fieldType === "NUMBER" ? numberLiteralText(v) : String(v.value) : v.value));
|
|
35674
36005
|
}
|
|
35675
36006
|
if (right.type === "SUBQUERY_IN_LIST") {
|
|
35676
36007
|
values = right.resolved;
|
|
@@ -35695,6 +36026,7 @@ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType, semantics
|
|
|
35695
36026
|
}
|
|
35696
36027
|
var NUMERIC_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
35697
36028
|
"LENGTH",
|
|
36029
|
+
"LENGTH_CHAR",
|
|
35698
36030
|
"INSTR",
|
|
35699
36031
|
"ROUND",
|
|
35700
36032
|
"FLOOR",
|
|
@@ -35749,6 +36081,10 @@ var SINGLE_OBJECT_FIELD_TYPES = /* @__PURE__ */ new Set(["CREATOR", "MODIFIER"])
|
|
|
35749
36081
|
function typedInContains(leftStr, values, fieldType) {
|
|
35750
36082
|
const fallback = () => values.has(leftStr);
|
|
35751
36083
|
if (fieldType === void 0) return fallback();
|
|
36084
|
+
if (fieldType === "NUMBER") {
|
|
36085
|
+
const semantics = syntheticSemantics("number");
|
|
36086
|
+
return [...values].some((value) => compareScalarValues("=", leftStr, value, semantics));
|
|
36087
|
+
}
|
|
35752
36088
|
let parsed;
|
|
35753
36089
|
if (STRING_ARRAY_FIELD_TYPES.has(fieldType) || OBJECT_ARRAY_FIELD_TYPES.has(fieldType) || SINGLE_OBJECT_FIELD_TYPES.has(fieldType)) {
|
|
35754
36090
|
try {
|
|
@@ -35807,7 +36143,7 @@ function resolveValue(value, row, resolveFieldType, resolveFieldSemantics2) {
|
|
|
35807
36143
|
case "STRING":
|
|
35808
36144
|
return value.value;
|
|
35809
36145
|
case "NUMBER":
|
|
35810
|
-
return
|
|
36146
|
+
return numberLiteralText(value);
|
|
35811
36147
|
case "KINTONE_FUNC":
|
|
35812
36148
|
return resolveKintoneFunc(value.name);
|
|
35813
36149
|
case "IN_LIST":
|
|
@@ -35942,7 +36278,7 @@ function updateToPutBatches(stmt, ids, fieldTypes = /* @__PURE__ */ new Map()) {
|
|
|
35942
36278
|
function buildUpdateRecord(assignments, fieldTypes) {
|
|
35943
36279
|
const record2 = {};
|
|
35944
36280
|
for (const { field, value } of assignments) {
|
|
35945
|
-
if (value.type === "ARITH" || value.type === "CASE_VALUE" || value.type === "SOURCE_FIELD") continue;
|
|
36281
|
+
if (value.type === "ARITH" || value.type === "CASE_VALUE" || value.type === "STRING_FUNC" || value.type === "SOURCE_FIELD") continue;
|
|
35946
36282
|
record2[field] = { value: toKintoneValue(value, fieldTypes.get(field)) };
|
|
35947
36283
|
}
|
|
35948
36284
|
return record2;
|
|
@@ -35952,12 +36288,19 @@ function hasArithAssignment(stmt) {
|
|
|
35952
36288
|
(a) => a.value.type === "ARITH" || a.value.type === "CASE_VALUE"
|
|
35953
36289
|
);
|
|
35954
36290
|
}
|
|
36291
|
+
function hasRowDependentAssignment(stmt) {
|
|
36292
|
+
return stmt.assignments.some(
|
|
36293
|
+
(a) => a.value.type === "ARITH" || a.value.type === "CASE_VALUE" || a.value.type === "STRING_FUNC"
|
|
36294
|
+
);
|
|
36295
|
+
}
|
|
35955
36296
|
function updateToGetQueryForArith(stmt) {
|
|
35956
36297
|
assertDmlWhereIsSafe(stmt.where);
|
|
35957
36298
|
const refFields = /* @__PURE__ */ new Set();
|
|
35958
36299
|
for (const { value } of stmt.assignments) {
|
|
35959
36300
|
if (value.type === "ARITH") {
|
|
35960
36301
|
collectArithFields2(value, refFields);
|
|
36302
|
+
} else if (value.type === "STRING_FUNC") {
|
|
36303
|
+
collectStringFuncFields2(value, refFields);
|
|
35961
36304
|
} else if (value.type === "CASE_VALUE") {
|
|
35962
36305
|
collectCaseFields(value.expr, refFields);
|
|
35963
36306
|
}
|
|
@@ -36044,6 +36387,8 @@ function updateToPutBatchesArith(stmt, records, fieldTypes = /* @__PURE__ */ new
|
|
|
36044
36387
|
for (const { field, value } of stmt.assignments) {
|
|
36045
36388
|
if (value.type === "ARITH") {
|
|
36046
36389
|
record2[field] = { value: String(evalArith(value, raw)) };
|
|
36390
|
+
} else if (value.type === "STRING_FUNC") {
|
|
36391
|
+
record2[field] = { value: evalStringFunc(value, row) };
|
|
36047
36392
|
} else if (value.type === "CASE_VALUE") {
|
|
36048
36393
|
record2[field] = { value: evalCaseWhenValue(value.expr, row, fieldTypes.get(field)) };
|
|
36049
36394
|
} else if (value.type === "SOURCE_FIELD") {
|
|
@@ -36092,6 +36437,8 @@ function updateFromToPutBatches(stmt, matched, fieldTypes = /* @__PURE__ */ new
|
|
|
36092
36437
|
throw new DmlConvertError(`\u6570\u5024\u30D5\u30A3\u30FC\u30EB\u30C9 ${field} \u306B\u5909\u63DB\u3067\u304D\u306A\u3044\u5024\u3067\u3059: ${raw}`);
|
|
36093
36438
|
}
|
|
36094
36439
|
record2[field] = { value: toKintoneValue({ type: "STRING", value: raw }, fieldType) };
|
|
36440
|
+
} else if (value.type === "STRING_FUNC") {
|
|
36441
|
+
throw new DmlConvertError("UPDATE ... FROM \u306E SET \u3067\u306F\u6587\u5B57\u5217\u95A2\u6570\u3092\u76F4\u63A5\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093");
|
|
36095
36442
|
} else if (value.type === "ARITH") {
|
|
36096
36443
|
record2[field] = { value: String(evalArith(value, target)) };
|
|
36097
36444
|
} else if (value.type === "CASE_VALUE") {
|
|
@@ -36236,7 +36583,7 @@ function convertDmlSqlValue(value, fieldType) {
|
|
|
36236
36583
|
case "STRING":
|
|
36237
36584
|
return convertString2(value.value, fieldType);
|
|
36238
36585
|
case "NUMBER":
|
|
36239
|
-
return
|
|
36586
|
+
return numberLiteralText(value);
|
|
36240
36587
|
case "ARRAY":
|
|
36241
36588
|
return convertArray(value.elements.map((e) => e.value), fieldType);
|
|
36242
36589
|
case "KINTONE_FUNC":
|
|
@@ -36868,7 +37215,7 @@ function evalAggArithExpr(node, rows, resolveAggSortKind) {
|
|
|
36868
37215
|
}
|
|
36869
37216
|
}
|
|
36870
37217
|
function aggArithDefaultKey(node) {
|
|
36871
|
-
if (node.type === "NUMBER") return
|
|
37218
|
+
if (node.type === "NUMBER") return numberLiteralText(node);
|
|
36872
37219
|
if (node.type === "AGG_REF") return aggregateSyntheticName2(node.func, node.distinct, node.arg);
|
|
36873
37220
|
return `${aggArithDefaultKey(node.left)}${node.op}${aggArithDefaultKey(node.right)}`;
|
|
36874
37221
|
}
|
|
@@ -36983,6 +37330,7 @@ function compareSortKeys(a, b, meta3) {
|
|
|
36983
37330
|
}
|
|
36984
37331
|
var NUMERIC_ORDER_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
36985
37332
|
"LENGTH",
|
|
37333
|
+
"LENGTH_CHAR",
|
|
36986
37334
|
"INSTR",
|
|
36987
37335
|
"ROUND",
|
|
36988
37336
|
"FLOOR",
|
|
@@ -37217,7 +37565,7 @@ function stripParentShortcutColumns(row) {
|
|
|
37217
37565
|
function arithColDefaultKey(expr) {
|
|
37218
37566
|
const nodeLabel = (n) => {
|
|
37219
37567
|
if (n.type === "FIELD_REF") return n.field;
|
|
37220
|
-
if (n.type === "NUMBER") return
|
|
37568
|
+
if (n.type === "NUMBER") return numberLiteralText(n);
|
|
37221
37569
|
if (n.type === "STRING_FUNC") return stringFuncDefaultKey(n);
|
|
37222
37570
|
return `(${nodeLabel(n.left)}${n.op}${nodeLabel(n.right)})`;
|
|
37223
37571
|
};
|
|
@@ -37246,10 +37594,11 @@ function hasAggregateInStringFuncExpr2(expr) {
|
|
|
37246
37594
|
function resolveAggInStringFuncArg(arg, rows, resolveAggSortKind) {
|
|
37247
37595
|
if (arg.type === "AGG_REF") {
|
|
37248
37596
|
const value = evalAggregate(arg.func, arg.distinct, arg.arg, arg.separator, rows, resolveAggSortKind);
|
|
37249
|
-
return typeof value === "number" ? { type: "NUMBER", value } : { type: "STRING", value };
|
|
37597
|
+
return typeof value === "number" ? { type: "NUMBER", value, raw: String(value) } : { type: "STRING", value };
|
|
37250
37598
|
}
|
|
37251
37599
|
if (arg.type === "AGG_ARITH") {
|
|
37252
|
-
|
|
37600
|
+
const value = evalAggArithExpr(arg, rows, resolveAggSortKind);
|
|
37601
|
+
return { type: "NUMBER", value, raw: String(value) };
|
|
37253
37602
|
}
|
|
37254
37603
|
if (arg.type === "STRING_FUNC") {
|
|
37255
37604
|
return resolveAggInStringFuncExpr(arg, rows, resolveAggSortKind);
|
|
@@ -37367,10 +37716,43 @@ function toFlatString(value) {
|
|
|
37367
37716
|
}
|
|
37368
37717
|
}
|
|
37369
37718
|
|
|
37719
|
+
// src/core/numberPrecision.ts
|
|
37720
|
+
function parseIntegerSetting(value, name, min, max) {
|
|
37721
|
+
if (typeof value !== "string" || !/^\d+$/.test(value)) {
|
|
37722
|
+
throw new Error(`SettingsError: numberPrecision.${name} must be an integer string.`);
|
|
37723
|
+
}
|
|
37724
|
+
let parsed = 0;
|
|
37725
|
+
for (const digit of value) parsed = parsed * 10 + digit.charCodeAt(0) - 48;
|
|
37726
|
+
if (parsed < min || parsed > max) {
|
|
37727
|
+
throw new Error(`SettingsError: numberPrecision.${name} must be between ${min} and ${max}.`);
|
|
37728
|
+
}
|
|
37729
|
+
return parsed;
|
|
37730
|
+
}
|
|
37731
|
+
function parseNumberPrecisionSettings(response) {
|
|
37732
|
+
const raw = response.numberPrecision;
|
|
37733
|
+
if (raw === void 0 || raw === null || typeof raw !== "object") {
|
|
37734
|
+
throw new Error("SettingsError: numberPrecision is missing from app settings.");
|
|
37735
|
+
}
|
|
37736
|
+
const digits = parseIntegerSetting(raw.digits, "digits", 1, 30);
|
|
37737
|
+
const decimalPlaces = parseIntegerSetting(raw.decimalPlaces, "decimalPlaces", 0, 10);
|
|
37738
|
+
const roundingMode = raw.roundingMode;
|
|
37739
|
+
if (roundingMode !== "HALF_EVEN" && roundingMode !== "UP" && roundingMode !== "DOWN") {
|
|
37740
|
+
throw new Error("SettingsError: numberPrecision.roundingMode is unsupported.");
|
|
37741
|
+
}
|
|
37742
|
+
return { digits, decimalPlaces, roundingMode };
|
|
37743
|
+
}
|
|
37744
|
+
function exactDecimalDigitCounts(value) {
|
|
37745
|
+
if (value.sign === 0) return { integerDigits: 0, fractionDigits: 0 };
|
|
37746
|
+
return {
|
|
37747
|
+
integerDigits: Math.max(value.coefficient.length - value.scale, 0),
|
|
37748
|
+
fractionDigits: Math.max(value.scale, 0)
|
|
37749
|
+
};
|
|
37750
|
+
}
|
|
37751
|
+
|
|
37370
37752
|
// src/core/dmlValidation.ts
|
|
37371
37753
|
var ARRAY_TYPES2 = /* @__PURE__ */ new Set(["CHECK_BOX", "MULTI_SELECT"]);
|
|
37372
37754
|
var CHOICE_TYPES = /* @__PURE__ */ new Set(["DROP_DOWN", "RADIO_BUTTON", "CHECK_BOX", "MULTI_SELECT"]);
|
|
37373
|
-
function validateAndNormalizeDmlValue(raw, field) {
|
|
37755
|
+
function validateAndNormalizeDmlValue(raw, field, numberPrecision) {
|
|
37374
37756
|
if (field.fieldType === "DATE" || field.fieldType === "TIME" || field.fieldType === "DATETIME") {
|
|
37375
37757
|
const original = rawScalarText(raw);
|
|
37376
37758
|
if (original !== "" && !isValidTemporalInput(original, field.fieldType)) {
|
|
@@ -37389,7 +37771,8 @@ function validateAndNormalizeDmlValue(raw, field) {
|
|
|
37389
37771
|
}
|
|
37390
37772
|
if (!isEmpty(value) && field.fieldType === "NUMBER") {
|
|
37391
37773
|
const text = String(value);
|
|
37392
|
-
|
|
37774
|
+
const decimal = parseExactDecimal(text);
|
|
37775
|
+
if (decimal === null) {
|
|
37393
37776
|
return { ok: false, code: "ERR_TYPE_NUMBER", message: `${field.code} \u306F\u6570\u5024\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044` };
|
|
37394
37777
|
}
|
|
37395
37778
|
if (field.minValue != null && compareDecimal(text, field.minValue) < 0) {
|
|
@@ -37398,6 +37781,17 @@ function validateAndNormalizeDmlValue(raw, field) {
|
|
|
37398
37781
|
if (field.maxValue != null && compareDecimal(text, field.maxValue) > 0) {
|
|
37399
37782
|
return { ok: false, code: "ERR_RANGE_MAX", message: `${field.code} \u306F ${field.maxValue} \u4EE5\u4E0B\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044` };
|
|
37400
37783
|
}
|
|
37784
|
+
if (numberPrecision !== void 0) {
|
|
37785
|
+
const { integerDigits } = exactDecimalDigitCounts(decimal);
|
|
37786
|
+
const integerBudget = numberPrecision.digits - numberPrecision.decimalPlaces;
|
|
37787
|
+
if (integerDigits > integerBudget) {
|
|
37788
|
+
return {
|
|
37789
|
+
ok: false,
|
|
37790
|
+
code: "ERR_NUMBER_INTEGER_DIGITS",
|
|
37791
|
+
message: `${field.code} \u306E\u6574\u6570\u90E8\u306F ${integerDigits} \u6841\u3067\u3059\u3002\u8A31\u5BB9\u306F ${integerBudget} \u6841\u307E\u3067\u3067\u3059 (digits=${numberPrecision.digits}, decimalPlaces=${numberPrecision.decimalPlaces})`
|
|
37792
|
+
};
|
|
37793
|
+
}
|
|
37794
|
+
}
|
|
37401
37795
|
}
|
|
37402
37796
|
if (!isEmpty(value) && (field.fieldType === "DATE" || field.fieldType === "TIME" || field.fieldType === "DATETIME")) {
|
|
37403
37797
|
if (!isValidTemporal(String(value), field.fieldType)) {
|
|
@@ -37425,7 +37819,8 @@ function validateAndNormalizeDmlValue(raw, field) {
|
|
|
37425
37819
|
}
|
|
37426
37820
|
function rawScalarText(raw) {
|
|
37427
37821
|
if (raw == null) return "";
|
|
37428
|
-
if (isSqlValue(raw) &&
|
|
37822
|
+
if (isSqlValue(raw) && raw.type === "NUMBER") return numberLiteralText(raw);
|
|
37823
|
+
if (isSqlValue(raw) && raw.type === "STRING") return raw.value;
|
|
37429
37824
|
return typeof raw === "string" || typeof raw === "number" ? String(raw) : "";
|
|
37430
37825
|
}
|
|
37431
37826
|
function isValidTemporalInput(value, type) {
|
|
@@ -37475,34 +37870,6 @@ function isEmpty(value) {
|
|
|
37475
37870
|
function typeCode(type) {
|
|
37476
37871
|
return type === "NUMBER" ? "ERR_TYPE_NUMBER" : "ERR_TYPE_DATE";
|
|
37477
37872
|
}
|
|
37478
|
-
function isFiniteDecimal(value) {
|
|
37479
|
-
return /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(value.trim());
|
|
37480
|
-
}
|
|
37481
|
-
function compareDecimal(left, right) {
|
|
37482
|
-
const normalize = (input) => {
|
|
37483
|
-
let s = input.trim();
|
|
37484
|
-
let sign = 1;
|
|
37485
|
-
if (s.startsWith("-")) {
|
|
37486
|
-
sign = -1;
|
|
37487
|
-
s = s.slice(1);
|
|
37488
|
-
} else if (s.startsWith("+")) s = s.slice(1);
|
|
37489
|
-
let [whole, fraction = ""] = s.split(".");
|
|
37490
|
-
whole = (whole || "0").replace(/^0+(?=\d)/, "");
|
|
37491
|
-
fraction = fraction.replace(/0+$/, "");
|
|
37492
|
-
if (/^0*$/.test(whole) && fraction === "") sign = 1;
|
|
37493
|
-
return { sign, whole, fraction };
|
|
37494
|
-
};
|
|
37495
|
-
const a = normalize(left);
|
|
37496
|
-
const b = normalize(right);
|
|
37497
|
-
if (a.sign !== b.sign) return a.sign < b.sign ? -1 : 1;
|
|
37498
|
-
const direction = a.sign;
|
|
37499
|
-
if (a.whole.length !== b.whole.length) return a.whole.length < b.whole.length ? -direction : direction;
|
|
37500
|
-
if (a.whole !== b.whole) return a.whole < b.whole ? -direction : direction;
|
|
37501
|
-
const width = Math.max(a.fraction.length, b.fraction.length);
|
|
37502
|
-
const af = a.fraction.padEnd(width, "0");
|
|
37503
|
-
const bf = b.fraction.padEnd(width, "0");
|
|
37504
|
-
return af === bf ? 0 : af < bf ? -direction : direction;
|
|
37505
|
-
}
|
|
37506
37873
|
function isValidTemporal(value, type) {
|
|
37507
37874
|
if (type === "TIME") {
|
|
37508
37875
|
const m2 = /^(\d{2}):(\d{2})(?::(\d{2}))?$/.exec(value);
|
|
@@ -37530,7 +37897,7 @@ var VALIDATION_META_COLUMNS = [
|
|
|
37530
37897
|
"$err_code",
|
|
37531
37898
|
"$err_message"
|
|
37532
37899
|
];
|
|
37533
|
-
function validateDmlCandidates(candidates, operation, payloadFields, targetFields, fieldInfos, statementNumber) {
|
|
37900
|
+
function validateDmlCandidates(candidates, operation, payloadFields, targetFields, fieldInfos, statementNumber, numberPrecision) {
|
|
37534
37901
|
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
37535
37902
|
const errors = [];
|
|
37536
37903
|
const invalid = /* @__PURE__ */ new Set();
|
|
@@ -37538,7 +37905,7 @@ function validateDmlCandidates(candidates, operation, payloadFields, targetField
|
|
|
37538
37905
|
candidate.record ??= {};
|
|
37539
37906
|
const rowErrors = [...candidate.preErrors];
|
|
37540
37907
|
for (const code of targetFields) {
|
|
37541
|
-
const result = validateAndNormalizeDmlValue(candidate.payload.get(code), infoByCode.get(code));
|
|
37908
|
+
const result = validateAndNormalizeDmlValue(candidate.payload.get(code), infoByCode.get(code), numberPrecision);
|
|
37542
37909
|
if (!result.ok) rowErrors.push({ field: code, code: result.code, message: result.message });
|
|
37543
37910
|
else candidate.record[code] = { value: result.value };
|
|
37544
37911
|
}
|
|
@@ -37548,14 +37915,14 @@ function validateDmlCandidates(candidates, operation, payloadFields, targetField
|
|
|
37548
37915
|
if (candidate.payload.has(info.code)) continue;
|
|
37549
37916
|
const emptyDefault = isEmptyDmlValue(info.defaultValue);
|
|
37550
37917
|
if (!emptyDefault) {
|
|
37551
|
-
const defaultResult = validateAndNormalizeDmlValue(info.defaultValue, info);
|
|
37918
|
+
const defaultResult = validateAndNormalizeDmlValue(info.defaultValue, info, numberPrecision);
|
|
37552
37919
|
if (!defaultResult.ok) rowErrors.push({
|
|
37553
37920
|
field: info.code,
|
|
37554
37921
|
code: defaultResult.code,
|
|
37555
37922
|
message: `\u65E2\u5B9A\u5024: ${defaultResult.message}`
|
|
37556
37923
|
});
|
|
37557
37924
|
} else {
|
|
37558
|
-
const emptyResult = validateAndNormalizeDmlValue("", info);
|
|
37925
|
+
const emptyResult = validateAndNormalizeDmlValue("", info, numberPrecision);
|
|
37559
37926
|
if (!emptyResult.ok) {
|
|
37560
37927
|
rowErrors.push({ field: info.code, code: emptyResult.code, message: emptyResult.message });
|
|
37561
37928
|
} else if (info.required) {
|
|
@@ -37583,7 +37950,8 @@ function renderValidationValue(value) {
|
|
|
37583
37950
|
if (value == null) return "";
|
|
37584
37951
|
if (typeof value === "object" && "type" in value) {
|
|
37585
37952
|
const sql = value;
|
|
37586
|
-
if (sql.type === "
|
|
37953
|
+
if (sql.type === "NUMBER") return sql.raw ?? String(sql.value ?? "");
|
|
37954
|
+
if (sql.type === "STRING") return String(sql.value ?? "");
|
|
37587
37955
|
if (sql.type === "ARRAY") return JSON.stringify(sql.elements?.map((e) => e.value) ?? []);
|
|
37588
37956
|
}
|
|
37589
37957
|
if (Array.isArray(value)) return JSON.stringify(value);
|
|
@@ -37825,6 +38193,7 @@ function createEmptyMetrics() {
|
|
|
37825
38193
|
putCalls: 0,
|
|
37826
38194
|
deleteCalls: 0,
|
|
37827
38195
|
fieldCalls: 0,
|
|
38196
|
+
numberPrecisionCalls: 0,
|
|
37828
38197
|
appsCalls: 0,
|
|
37829
38198
|
processStatusCalls: 0,
|
|
37830
38199
|
cursorCreateCalls: 0,
|
|
@@ -37910,6 +38279,10 @@ function wrapClientWithMetrics(client, metrics) {
|
|
|
37910
38279
|
metrics.fieldCalls += 1;
|
|
37911
38280
|
return client.getFields(appId);
|
|
37912
38281
|
},
|
|
38282
|
+
getNumberPrecision: (appId) => {
|
|
38283
|
+
metrics.numberPrecisionCalls += 1;
|
|
38284
|
+
return client.getNumberPrecision(appId);
|
|
38285
|
+
},
|
|
37913
38286
|
getProcessStatuses: (appId) => {
|
|
37914
38287
|
metrics.processStatusCalls += 1;
|
|
37915
38288
|
return client.getProcessStatuses(appId);
|
|
@@ -38177,7 +38550,7 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
38177
38550
|
const first = resolvedStmt2.expr.query.columns[0];
|
|
38178
38551
|
const numeric = first?.type === "ARITH_COL" || first?.type === "ARITH_AGG_COL" || first?.type === "WINDOW_COL" || first?.type === "AGGREGATE" && (first.func === "COUNT" || first.func === "SUM" || first.func === "AVG");
|
|
38179
38552
|
const numberValue = numeric ? Number(value) : Number.NaN;
|
|
38180
|
-
variables.set(stmt.name, numeric && Number.isFinite(numberValue) ? { type: "number", value: numberValue } : { type: "string", value });
|
|
38553
|
+
variables.set(stmt.name, numeric && Number.isFinite(numberValue) ? { type: "number", value: numberValue, raw: value } : { type: "string", value });
|
|
38181
38554
|
} catch (e) {
|
|
38182
38555
|
if (e instanceof ScalarSubqueryError) {
|
|
38183
38556
|
throw new Error(`ArgumentError: ${e.message}`);
|
|
@@ -38195,7 +38568,10 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
38195
38568
|
variables.set(stmt.name, { type: "string", value: injected[stmt.name] });
|
|
38196
38569
|
} else {
|
|
38197
38570
|
const value = evaluateScalarExpr(stmt.default);
|
|
38198
|
-
variables.set(stmt.name, {
|
|
38571
|
+
variables.set(stmt.name, {
|
|
38572
|
+
type: "string",
|
|
38573
|
+
value: value.type === "number" ? value.raw ?? String(value.value) : value.value
|
|
38574
|
+
});
|
|
38199
38575
|
}
|
|
38200
38576
|
return {};
|
|
38201
38577
|
}
|
|
@@ -38371,7 +38747,7 @@ function evaluateScalarExpr(expr) {
|
|
|
38371
38747
|
case "STRING":
|
|
38372
38748
|
return { type: "string", value: expr.value };
|
|
38373
38749
|
case "NUMBER":
|
|
38374
|
-
return { type: "number", value: expr.value };
|
|
38750
|
+
return { type: "number", value: expr.value, raw: numberLiteralText(expr) };
|
|
38375
38751
|
case "KINTONE_FUNC":
|
|
38376
38752
|
return { type: "string", value: resolveKintoneFunc(expr.name) };
|
|
38377
38753
|
case "STRING_FUNC":
|
|
@@ -38381,7 +38757,7 @@ function evaluateScalarExpr(expr) {
|
|
|
38381
38757
|
if (!Number.isFinite(value)) {
|
|
38382
38758
|
throw new Error("ArgumentError: SET scalar arithmetic produced a non-finite number.");
|
|
38383
38759
|
}
|
|
38384
|
-
return { type: "number", value };
|
|
38760
|
+
return { type: "number", value, raw: String(value) };
|
|
38385
38761
|
}
|
|
38386
38762
|
}
|
|
38387
38763
|
}
|
|
@@ -38396,7 +38772,7 @@ function resolveVariableRefs(node, variables) {
|
|
|
38396
38772
|
if (value === void 0) {
|
|
38397
38773
|
throw new Error(`ParseError: variable @${obj["name"]} is not defined in this batch.`);
|
|
38398
38774
|
}
|
|
38399
|
-
return value.type === "number" ? { type: "NUMBER", value: value.value } : { type: "STRING", value: value.value };
|
|
38775
|
+
return value.type === "number" ? { type: "NUMBER", value: value.value, raw: value.raw ?? String(value.value) } : { type: "STRING", value: value.value };
|
|
38400
38776
|
}
|
|
38401
38777
|
return Object.fromEntries(
|
|
38402
38778
|
Object.entries(obj).map(([key, value]) => [key, resolveVariableRefs(value, variables)])
|
|
@@ -38462,7 +38838,7 @@ async function evalAssertOperand(operand, client, options, cacheContext, tempTab
|
|
|
38462
38838
|
case "VARIABLE":
|
|
38463
38839
|
throw new Error(`ParseError: unresolved batch variable @${operand.name}.`);
|
|
38464
38840
|
case "NUMBER":
|
|
38465
|
-
return
|
|
38841
|
+
return numberLiteralText(operand);
|
|
38466
38842
|
case "STRING":
|
|
38467
38843
|
return operand.value;
|
|
38468
38844
|
case "ARITH":
|
|
@@ -39203,6 +39579,7 @@ function systemColumnMeta(field) {
|
|
|
39203
39579
|
}
|
|
39204
39580
|
var NUMBER_RETURNING_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
39205
39581
|
"LENGTH",
|
|
39582
|
+
"LENGTH_CHAR",
|
|
39206
39583
|
"INSTR",
|
|
39207
39584
|
"ROUND",
|
|
39208
39585
|
"FLOOR",
|
|
@@ -39744,8 +40121,8 @@ async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords2, pa
|
|
|
39744
40121
|
}
|
|
39745
40122
|
var UPSERT_IN_CHUNK_SIZE = 50;
|
|
39746
40123
|
function normalizeKeyPart(v) {
|
|
39747
|
-
const
|
|
39748
|
-
if (
|
|
40124
|
+
const decimal = parseExactDecimal(v);
|
|
40125
|
+
if (decimal !== null) return JSON.stringify(decimal);
|
|
39749
40126
|
return v;
|
|
39750
40127
|
}
|
|
39751
40128
|
function upsertCompositeKey(parts) {
|
|
@@ -39897,6 +40274,7 @@ var optionOrderCache = /* @__PURE__ */ new Map();
|
|
|
39897
40274
|
var sortKindCache = /* @__PURE__ */ new Map();
|
|
39898
40275
|
var fieldInfoCache = /* @__PURE__ */ new Map();
|
|
39899
40276
|
var processStatusCache = /* @__PURE__ */ new Map();
|
|
40277
|
+
var numberPrecisionCache = /* @__PURE__ */ new Map();
|
|
39900
40278
|
function getScopedCacheValue(root, cacheContext, appId) {
|
|
39901
40279
|
return root.get(cacheContext)?.get(appId);
|
|
39902
40280
|
}
|
|
@@ -39918,6 +40296,13 @@ async function getFieldsCached(appId, client, cacheContext) {
|
|
|
39918
40296
|
setScopedCacheValue(fieldInfoCache, cacheContext, appId, loading);
|
|
39919
40297
|
return loading;
|
|
39920
40298
|
}
|
|
40299
|
+
async function getNumberPrecisionCached(appId, client, cacheContext) {
|
|
40300
|
+
const cached2 = getScopedCacheValue(numberPrecisionCache, cacheContext, appId);
|
|
40301
|
+
if (cached2) return cached2;
|
|
40302
|
+
const loading = client.getNumberPrecision(appId);
|
|
40303
|
+
setScopedCacheValue(numberPrecisionCache, cacheContext, appId, loading);
|
|
40304
|
+
return loading;
|
|
40305
|
+
}
|
|
39921
40306
|
async function getProcessStatusesCached(appId, client, cacheContext) {
|
|
39922
40307
|
const cached2 = getScopedCacheValue(processStatusCache, cacheContext, appId);
|
|
39923
40308
|
if (cached2) return cached2;
|
|
@@ -40170,6 +40555,45 @@ var NON_WRITABLE_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
|
40170
40555
|
"CATEGORY",
|
|
40171
40556
|
"REFERENCE_TABLE"
|
|
40172
40557
|
]);
|
|
40558
|
+
function assertWritableTopLevelDmlFields(appId, targetFields, fieldInfos) {
|
|
40559
|
+
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
40560
|
+
for (const code of targetFields) {
|
|
40561
|
+
const info = infoByCode.get(code);
|
|
40562
|
+
if (!info) {
|
|
40563
|
+
throw new Error(`ArgumentError: DML target field ${code} does not exist.`);
|
|
40564
|
+
}
|
|
40565
|
+
if (info.inSubtable) {
|
|
40566
|
+
throw new Error(
|
|
40567
|
+
`ArgumentError: DML target field ${code} is inside a subtable. Use subtable DML syntax (for example, APP${appId}$\u30C6\u30FC\u30D6\u30EB).`
|
|
40568
|
+
);
|
|
40569
|
+
}
|
|
40570
|
+
if (info.writable === false || NON_WRITABLE_FIELD_TYPES.has(info.fieldType)) {
|
|
40571
|
+
throw new Error(`ArgumentError: DML target field ${code} is not writable (${info.fieldType}).`);
|
|
40572
|
+
}
|
|
40573
|
+
}
|
|
40574
|
+
}
|
|
40575
|
+
async function loadWritableTopLevelDmlFields(appId, targetFields, client, cacheContext) {
|
|
40576
|
+
const fieldInfos = await getFieldsCached(appId, client, cacheContext);
|
|
40577
|
+
assertWritableTopLevelDmlFields(appId, targetFields, fieldInfos);
|
|
40578
|
+
return fieldInfos;
|
|
40579
|
+
}
|
|
40580
|
+
async function loadNumberPrecisionForTargets(appId, targetFields, fieldInfos, client, cacheContext) {
|
|
40581
|
+
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
40582
|
+
return targetFields.some((code) => infoByCode.get(code)?.fieldType === "NUMBER") ? getNumberPrecisionCached(appId, client, cacheContext) : void 0;
|
|
40583
|
+
}
|
|
40584
|
+
function assertValidDmlRecords(records, targetFields, fieldInfos, numberPrecision) {
|
|
40585
|
+
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
40586
|
+
records.forEach((record2, rowIndex) => {
|
|
40587
|
+
for (const code of targetFields) {
|
|
40588
|
+
const info = infoByCode.get(code);
|
|
40589
|
+
const result = validateAndNormalizeDmlValue(record2[code]?.value ?? "", info, numberPrecision);
|
|
40590
|
+
if (!result.ok) {
|
|
40591
|
+
throw new Error(`DmlValidationError: ${result.code} ${result.message} (row=${rowIndex + 1}, field=${code})`);
|
|
40592
|
+
}
|
|
40593
|
+
record2[code] = { value: result.value };
|
|
40594
|
+
}
|
|
40595
|
+
});
|
|
40596
|
+
}
|
|
40173
40597
|
async function executeDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
|
|
40174
40598
|
return (await prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber)).result;
|
|
40175
40599
|
}
|
|
@@ -40181,24 +40605,29 @@ var RejectLimitExceededError = class extends Error {
|
|
|
40181
40605
|
}
|
|
40182
40606
|
};
|
|
40183
40607
|
async function prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
|
|
40184
|
-
if (stmt.type === "UPDATE") {
|
|
40185
|
-
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
40186
|
-
}
|
|
40187
40608
|
const operation = stmt.type === "UPDATE" ? "UPDATE" : stmt.type.startsWith("UPSERT") ? "UPSERT" : "INSERT";
|
|
40188
40609
|
const payloadFields = stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : [...stmt.fields];
|
|
40189
40610
|
if (new Set(payloadFields).size !== payloadFields.length) {
|
|
40190
40611
|
throw new Error("ArgumentError: DML target fields contain duplicates.");
|
|
40191
40612
|
}
|
|
40192
|
-
const fieldInfos = await getFieldsCached(stmt.appId, client, cacheContext);
|
|
40193
|
-
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
40194
40613
|
const targetFields = stmt.type === "UPDATE" ? stmt.assignments.map((a) => a.field) : stmt.fields;
|
|
40195
|
-
|
|
40196
|
-
|
|
40197
|
-
|
|
40198
|
-
|
|
40199
|
-
|
|
40200
|
-
|
|
40614
|
+
const fieldInfos = await loadWritableTopLevelDmlFields(
|
|
40615
|
+
stmt.appId,
|
|
40616
|
+
targetFields,
|
|
40617
|
+
client,
|
|
40618
|
+
cacheContext
|
|
40619
|
+
);
|
|
40620
|
+
if (stmt.type === "UPDATE") {
|
|
40621
|
+
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
40201
40622
|
}
|
|
40623
|
+
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
40624
|
+
const numberPrecision = await loadNumberPrecisionForTargets(
|
|
40625
|
+
stmt.appId,
|
|
40626
|
+
targetFields,
|
|
40627
|
+
fieldInfos,
|
|
40628
|
+
client,
|
|
40629
|
+
cacheContext
|
|
40630
|
+
);
|
|
40202
40631
|
const candidates = await materializeValidationCandidates(stmt, operation, client, options, cacheContext, tempTables, infoByCode);
|
|
40203
40632
|
const { errors, invalidRows, invalidRowNumbers } = validateDmlCandidates(
|
|
40204
40633
|
candidates,
|
|
@@ -40206,7 +40635,8 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
|
|
|
40206
40635
|
payloadFields,
|
|
40207
40636
|
targetFields,
|
|
40208
40637
|
fieldInfos,
|
|
40209
|
-
statementNumber
|
|
40638
|
+
statementNumber,
|
|
40639
|
+
numberPrecision
|
|
40210
40640
|
);
|
|
40211
40641
|
const columns = [...payloadFields, ...VALIDATION_META_COLUMNS];
|
|
40212
40642
|
const result = {
|
|
@@ -40367,7 +40797,7 @@ async function materializeUpdateValidationCandidates(stmt, client, options, cach
|
|
|
40367
40797
|
await resolveSetSubqueries(stmt.assignments, client, options, cacheContext);
|
|
40368
40798
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
40369
40799
|
let records;
|
|
40370
|
-
if (
|
|
40800
|
+
if (hasRowDependentAssignment(stmt)) {
|
|
40371
40801
|
const getParams = updateToGetQueryForArith(stmt);
|
|
40372
40802
|
const resolved = await fetchRecordsForSharedPlan(client.getRecords, getParams.app, getParams.query, [...getParams.fields], {
|
|
40373
40803
|
maxRecords: options.maxRecords ?? 1e4,
|
|
@@ -40430,6 +40860,7 @@ async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cach
|
|
|
40430
40860
|
tempTables
|
|
40431
40861
|
);
|
|
40432
40862
|
const sourceByKey = /* @__PURE__ */ new Map();
|
|
40863
|
+
const sourceQueryByKey = /* @__PURE__ */ new Map();
|
|
40433
40864
|
for (const row of sourceRows) {
|
|
40434
40865
|
if (!Object.prototype.hasOwnProperty.call(row, from.joinKeyField)) {
|
|
40435
40866
|
throw new Error(`ArgumentError: UPDATE ... FROM source column ${from.joinKeyField} does not exist.`);
|
|
@@ -40439,6 +40870,7 @@ async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cach
|
|
|
40439
40870
|
throw new Error(`ArgumentError: UPDATE ... FROM source has multiple rows for normalized key ${key}.`);
|
|
40440
40871
|
}
|
|
40441
40872
|
sourceByKey.set(key, row);
|
|
40873
|
+
sourceQueryByKey.set(key, String(row[from.joinKeyField]).trim());
|
|
40442
40874
|
}
|
|
40443
40875
|
if (sourceByKey.size === 0) return [];
|
|
40444
40876
|
const maxRecords2 = options.maxRecords ?? 1e4;
|
|
@@ -40447,7 +40879,7 @@ async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cach
|
|
|
40447
40879
|
const targetRecords = [];
|
|
40448
40880
|
const seenTargetIds = /* @__PURE__ */ new Set();
|
|
40449
40881
|
let fetchedTargetCount = 0;
|
|
40450
|
-
for (const keys of splitChunks([...
|
|
40882
|
+
for (const keys of splitChunks([...sourceQueryByKey.values()], UPDATE_FROM_KEY_CHUNK_SIZE)) {
|
|
40451
40883
|
const keyQuery = `${from.targetJoinField} in (${keys.map(sqlQuote).join(",")})`;
|
|
40452
40884
|
const query = filterQuery ? `(${keyQuery}) and (${filterQuery})` : keyQuery;
|
|
40453
40885
|
const resolved = await fetchRecordsForSharedPlan(
|
|
@@ -40548,36 +40980,28 @@ function normalizeUpdateFromJoinKey(raw, kind, side) {
|
|
|
40548
40980
|
}
|
|
40549
40981
|
if (kind === "number" && side === "target" && raw === "") return null;
|
|
40550
40982
|
if (kind === "id") {
|
|
40551
|
-
const
|
|
40552
|
-
const id = Number(
|
|
40553
|
-
if (
|
|
40983
|
+
const text = raw.trim();
|
|
40984
|
+
const id = Number(text);
|
|
40985
|
+
if (text === "" || !Number.isSafeInteger(id) || id <= 0) {
|
|
40554
40986
|
throw new Error(`ArgumentError: UPDATE ... FROM ${side} key must be a positive safe integer: ${raw}`);
|
|
40555
40987
|
}
|
|
40556
40988
|
return String(id);
|
|
40557
40989
|
}
|
|
40558
|
-
const
|
|
40559
|
-
if (
|
|
40990
|
+
const decimal = parseExactDecimal(raw);
|
|
40991
|
+
if (decimal === null) {
|
|
40560
40992
|
throw new Error(`ArgumentError: UPDATE ... FROM ${side} key must be a finite decimal: ${raw}`);
|
|
40561
40993
|
}
|
|
40562
|
-
|
|
40563
|
-
let negative = false;
|
|
40564
|
-
if (unsigned.startsWith("-") || unsigned.startsWith("+")) {
|
|
40565
|
-
negative = unsigned[0] === "-";
|
|
40566
|
-
unsigned = unsigned.slice(1);
|
|
40567
|
-
}
|
|
40568
|
-
let [whole, fraction = ""] = unsigned.split(".");
|
|
40569
|
-
whole = (whole || "0").replace(/^0+(?=\d)/, "");
|
|
40570
|
-
fraction = fraction.replace(/0+$/, "");
|
|
40571
|
-
const zero = /^0*$/.test(whole) && fraction === "";
|
|
40572
|
-
const canonical = fraction === "" ? whole : `${whole}.${fraction}`;
|
|
40573
|
-
return negative && !zero ? `-${canonical}` : canonical;
|
|
40994
|
+
return JSON.stringify(decimal);
|
|
40574
40995
|
}
|
|
40575
40996
|
async function executeInsert(stmt, client, options, cacheContext) {
|
|
40576
40997
|
if (stmt.subtableCode) {
|
|
40577
40998
|
return executeInsertSubtable(stmt, client, options, cacheContext);
|
|
40578
40999
|
}
|
|
41000
|
+
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
41001
|
+
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
40579
41002
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
40580
41003
|
const batches = insertToPostBatches(stmt, fieldTypes);
|
|
41004
|
+
assertValidDmlRecords(batches.flatMap((batch) => batch.records), stmt.fields, fieldInfos, numberPrecision);
|
|
40581
41005
|
const createdIds = [];
|
|
40582
41006
|
for (const batch of batches) {
|
|
40583
41007
|
const res = await client.postRecords(batch);
|
|
@@ -40590,6 +41014,8 @@ async function executeInsert(stmt, client, options, cacheContext) {
|
|
|
40590
41014
|
};
|
|
40591
41015
|
}
|
|
40592
41016
|
async function executeInsertSelect(stmt, client, options, cacheContext, cteCache) {
|
|
41017
|
+
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
41018
|
+
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
40593
41019
|
const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
|
|
40594
41020
|
const { rows, columns } = selectResult;
|
|
40595
41021
|
if (columns.length !== stmt.fields.length) {
|
|
@@ -40611,6 +41037,7 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
|
|
|
40611
41037
|
});
|
|
40612
41038
|
return record2;
|
|
40613
41039
|
});
|
|
41040
|
+
assertValidDmlRecords(allRecords, stmt.fields, fieldInfos, numberPrecision);
|
|
40614
41041
|
const createdIds = [];
|
|
40615
41042
|
for (let i = 0; i < allRecords.length; i += 100) {
|
|
40616
41043
|
const batch = allRecords.slice(i, i + 100);
|
|
@@ -40624,17 +41051,32 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
|
|
|
40624
41051
|
};
|
|
40625
41052
|
}
|
|
40626
41053
|
async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
40627
|
-
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
40628
41054
|
if (stmt.subtableCode) {
|
|
41055
|
+
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
40629
41056
|
return executeUpdateSubtable(stmt, client, options, cacheContext);
|
|
40630
41057
|
}
|
|
41058
|
+
const fieldInfos = await loadWritableTopLevelDmlFields(
|
|
41059
|
+
stmt.appId,
|
|
41060
|
+
stmt.assignments.map((assignment) => assignment.field),
|
|
41061
|
+
client,
|
|
41062
|
+
cacheContext
|
|
41063
|
+
);
|
|
41064
|
+
const targetFields = stmt.assignments.map((assignment) => assignment.field);
|
|
41065
|
+
const numberPrecision = await loadNumberPrecisionForTargets(
|
|
41066
|
+
stmt.appId,
|
|
41067
|
+
targetFields,
|
|
41068
|
+
fieldInfos,
|
|
41069
|
+
client,
|
|
41070
|
+
cacheContext
|
|
41071
|
+
);
|
|
41072
|
+
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
40631
41073
|
if (stmt.from != null) {
|
|
40632
41074
|
return executeUpdateFrom(stmt, stmt.from, client, options, cacheContext, tempTables);
|
|
40633
41075
|
}
|
|
40634
41076
|
const maxRecords2 = options.maxRecords ?? 1e4;
|
|
40635
41077
|
await resolveSetSubqueries(stmt.assignments, client, options, cacheContext);
|
|
40636
41078
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
40637
|
-
if (
|
|
41079
|
+
if (hasRowDependentAssignment(stmt)) {
|
|
40638
41080
|
const getParams2 = updateToGetQueryForArith(stmt);
|
|
40639
41081
|
const resolved2 = await fetchRecordsForSharedPlan(
|
|
40640
41082
|
client.getRecords,
|
|
@@ -40644,11 +41086,12 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
|
40644
41086
|
{ maxRecords: maxRecords2, parallel: options.fetchParallel ?? 1 }
|
|
40645
41087
|
);
|
|
40646
41088
|
const records = resolved2.records;
|
|
41089
|
+
const batches2 = updateToPutBatchesArith(stmt, records, fieldTypes);
|
|
41090
|
+
assertValidDmlRecords(batches2.flatMap((batch) => batch.records.map((entry) => entry.record)), targetFields, fieldInfos, numberPrecision);
|
|
40647
41091
|
if (options.confirm) {
|
|
40648
41092
|
const ok = await options.confirm(records.length, "UPDATE");
|
|
40649
41093
|
if (!ok) throw new OperationCancelledError("UPDATE", records.length);
|
|
40650
41094
|
}
|
|
40651
|
-
const batches2 = updateToPutBatchesArith(stmt, records, fieldTypes);
|
|
40652
41095
|
for (const batch of batches2) {
|
|
40653
41096
|
await client.putRecords(batch);
|
|
40654
41097
|
}
|
|
@@ -40662,11 +41105,12 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
|
40662
41105
|
{ maxRecords: maxRecords2, parallel: options.fetchParallel ?? 1 }
|
|
40663
41106
|
);
|
|
40664
41107
|
const ids = resolved.ids;
|
|
41108
|
+
const batches = updateToPutBatches(stmt, ids, fieldTypes);
|
|
41109
|
+
assertValidDmlRecords(batches.flatMap((batch) => batch.records.map((entry) => entry.record)), targetFields, fieldInfos, numberPrecision);
|
|
40665
41110
|
if (options.confirm) {
|
|
40666
41111
|
const ok = await options.confirm(ids.length, "UPDATE");
|
|
40667
41112
|
if (!ok) throw new OperationCancelledError("UPDATE", ids.length);
|
|
40668
41113
|
}
|
|
40669
|
-
const batches = updateToPutBatches(stmt, ids, fieldTypes);
|
|
40670
41114
|
for (const batch of batches) {
|
|
40671
41115
|
await client.putRecords(batch);
|
|
40672
41116
|
}
|
|
@@ -40674,12 +41118,16 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
|
40674
41118
|
}
|
|
40675
41119
|
async function executeUpdateFrom(stmt, from, client, options, cacheContext, tempTables) {
|
|
40676
41120
|
const matched = await resolveUpdateFromMatchedRecords(stmt, from, client, options, cacheContext, tempTables);
|
|
41121
|
+
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
41122
|
+
const batches = updateFromToPutBatches(stmt, matched, fieldTypes);
|
|
41123
|
+
const targetFields = stmt.assignments.map((assignment) => assignment.field);
|
|
41124
|
+
const fieldInfos = await getFieldsCached(stmt.appId, client, cacheContext);
|
|
41125
|
+
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, targetFields, fieldInfos, client, cacheContext);
|
|
41126
|
+
assertValidDmlRecords(batches.flatMap((batch) => batch.records.map((entry) => entry.record)), targetFields, fieldInfos, numberPrecision);
|
|
40677
41127
|
if (options.confirm) {
|
|
40678
41128
|
const ok = await options.confirm(matched.length, "UPDATE");
|
|
40679
41129
|
if (!ok) throw new OperationCancelledError("UPDATE", matched.length);
|
|
40680
41130
|
}
|
|
40681
|
-
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
40682
|
-
const batches = updateFromToPutBatches(stmt, matched, fieldTypes);
|
|
40683
41131
|
for (const batch of batches) await client.putRecords(batch);
|
|
40684
41132
|
return { type: "UPDATE", updatedCount: matched.length };
|
|
40685
41133
|
}
|
|
@@ -40728,6 +41176,8 @@ async function executeDelete(stmt, client, options, cacheContext) {
|
|
|
40728
41176
|
return { type: "DELETE", deletedCount: ids.length };
|
|
40729
41177
|
}
|
|
40730
41178
|
async function executeUpsert(stmt, client, options, cacheContext) {
|
|
41179
|
+
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
41180
|
+
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
40731
41181
|
const toInsert = [];
|
|
40732
41182
|
const toUpdate = [];
|
|
40733
41183
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
@@ -40736,7 +41186,7 @@ async function executeUpsert(stmt, client, options, cacheContext) {
|
|
|
40736
41186
|
const idx = stmt.fields.indexOf(key);
|
|
40737
41187
|
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`);
|
|
40738
41188
|
const val = row[idx];
|
|
40739
|
-
return val.type === "STRING" ? val.value : val.type === "NUMBER" ?
|
|
41189
|
+
return val.type === "STRING" ? val.value : val.type === "NUMBER" ? numberLiteralText(val) : val.type === "CASE_VALUE" ? evalCaseWhen(val.expr, {}) : val.elements.map((e) => e.value).join(",");
|
|
40740
41190
|
})
|
|
40741
41191
|
);
|
|
40742
41192
|
const targetIndex = await resolveUpsertTargets(stmt.appId, stmt.keyFields, rowKeyValues, client, options, fieldTypes);
|
|
@@ -40757,6 +41207,12 @@ async function executeUpsert(stmt, client, options, cacheContext) {
|
|
|
40757
41207
|
toInsert.push(record2);
|
|
40758
41208
|
}
|
|
40759
41209
|
});
|
|
41210
|
+
assertValidDmlRecords(
|
|
41211
|
+
[...toInsert, ...toUpdate.map((entry) => entry.record)],
|
|
41212
|
+
stmt.fields,
|
|
41213
|
+
fieldInfos,
|
|
41214
|
+
numberPrecision
|
|
41215
|
+
);
|
|
40760
41216
|
if (options.confirm && toInsert.length + toUpdate.length > 0) {
|
|
40761
41217
|
const total = toInsert.length + toUpdate.length;
|
|
40762
41218
|
const ok = await options.confirm(total, "UPDATE");
|
|
@@ -41032,14 +41488,14 @@ function buildSubtableReorderPutParams(appId, parentId, revision, subtableCode,
|
|
|
41032
41488
|
}
|
|
41033
41489
|
function evalAssignmentValueForSubtable(value, row, resolveFieldType) {
|
|
41034
41490
|
if (value.type === "STRING") return value.value;
|
|
41035
|
-
if (value.type === "NUMBER") return
|
|
41491
|
+
if (value.type === "NUMBER") return numberLiteralText(value);
|
|
41036
41492
|
if (value.type === "ARITH") return String(evalArithExpr(value, row));
|
|
41037
41493
|
if (value.type === "CASE_VALUE") return evalCaseWhen(value.expr, row, resolveFieldType);
|
|
41038
41494
|
throw new Error(`${value.type} \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u306E\u5024\u3068\u3057\u3066\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`);
|
|
41039
41495
|
}
|
|
41040
41496
|
function valueToString(value) {
|
|
41041
41497
|
if (value.type === "STRING") return value.value;
|
|
41042
|
-
if (value.type === "NUMBER") return
|
|
41498
|
+
if (value.type === "NUMBER") return numberLiteralText(value);
|
|
41043
41499
|
if (value.type === "CASE_VALUE") return evalCaseWhen(value.expr, {});
|
|
41044
41500
|
return value.elements.map((e) => e.value).join(",");
|
|
41045
41501
|
}
|
|
@@ -41154,6 +41610,8 @@ function evalOrderKeyForRow(key, row) {
|
|
|
41154
41610
|
}
|
|
41155
41611
|
}
|
|
41156
41612
|
async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache) {
|
|
41613
|
+
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
41614
|
+
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
41157
41615
|
const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
|
|
41158
41616
|
const { rows, columns } = selectResult;
|
|
41159
41617
|
if (columns.length !== stmt.fields.length) {
|
|
@@ -41176,6 +41634,7 @@ async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache
|
|
|
41176
41634
|
});
|
|
41177
41635
|
return record2;
|
|
41178
41636
|
});
|
|
41637
|
+
assertValidDmlRecords(records, stmt.fields, fieldInfos, numberPrecision);
|
|
41179
41638
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
41180
41639
|
const rowKeyValues = records.map(
|
|
41181
41640
|
(record2) => stmt.keyFields.map((key) => String(record2[key]?.value ?? ""))
|
|
@@ -41789,6 +42248,8 @@ function buildInsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
41789
42248
|
}
|
|
41790
42249
|
function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
|
|
41791
42250
|
const isArith = hasArithAssignment(stmt);
|
|
42251
|
+
const isStringFunc = stmt.assignments.some((a) => a.value.type === "STRING_FUNC");
|
|
42252
|
+
const isRowDependent = hasRowDependentAssignment(stmt);
|
|
41792
42253
|
const isSubq = stmt.assignments.some((a) => a.value.type === "SCALAR_SUBQUERY");
|
|
41793
42254
|
const lines = [];
|
|
41794
42255
|
if (label) lines.push(label);
|
|
@@ -41805,10 +42266,11 @@ function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
|
|
|
41805
42266
|
lines.push(` api: GET /k/v1/records.json \u2192 PUT /k/v1/records.json`);
|
|
41806
42267
|
const setTypes = [];
|
|
41807
42268
|
if (isArith) setTypes.push("\u7B97\u8853 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A08\u7B97\uFF09");
|
|
42269
|
+
if (isStringFunc) setTypes.push("\u6587\u5B57\u5217\u95A2\u6570 SET\uFF08\u73FE\u5728\u5024\u3092\u53D6\u5F97\u3057\u3066\u8A55\u4FA1\uFF09");
|
|
41808
42270
|
if (isSubq) setTypes.push("\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA SET");
|
|
41809
|
-
if (!
|
|
42271
|
+
if (!isRowDependent && !isSubq) setTypes.push("\u5358\u7D14 SET");
|
|
41810
42272
|
lines.push(` set type: ${setTypes.join(", ")}`);
|
|
41811
|
-
if (
|
|
42273
|
+
if (isRowDependent) {
|
|
41812
42274
|
const refFields = collectArithRefFields(stmt);
|
|
41813
42275
|
if (refFields.length > 0) {
|
|
41814
42276
|
lines.push(` ref fields: ${refFields.join(", ")}\uFF08GET \u306B\u542B\u3081\u308B\uFF09`);
|
|
@@ -41894,6 +42356,7 @@ function collectArithRefFields(stmt) {
|
|
|
41894
42356
|
const refs = /* @__PURE__ */ new Set();
|
|
41895
42357
|
for (const { value } of stmt.assignments) {
|
|
41896
42358
|
if (value.type === "ARITH") collectArithNodeRefs(value, refs);
|
|
42359
|
+
if (value.type === "STRING_FUNC") collectArithNodeRefs(value, refs);
|
|
41897
42360
|
}
|
|
41898
42361
|
return [...refs];
|
|
41899
42362
|
}
|
|
@@ -41906,6 +42369,13 @@ function collectArithNodeRefs(node, out) {
|
|
|
41906
42369
|
collectArithNodeRefs(node.left, out);
|
|
41907
42370
|
collectArithNodeRefs(node.right, out);
|
|
41908
42371
|
}
|
|
42372
|
+
if (node.type === "STRING_FUNC") {
|
|
42373
|
+
for (const arg of node.args) {
|
|
42374
|
+
if (arg.type !== "STRING" && arg.type !== "AGG_REF" && arg.type !== "AGG_ARITH") {
|
|
42375
|
+
collectArithNodeRefs(arg, out);
|
|
42376
|
+
}
|
|
42377
|
+
}
|
|
42378
|
+
}
|
|
41909
42379
|
}
|
|
41910
42380
|
function formatAssignment(a) {
|
|
41911
42381
|
const v = a.value;
|
|
@@ -41913,6 +42383,7 @@ function formatAssignment(a) {
|
|
|
41913
42383
|
if (v.type === "NUMBER") return `${a.field} = ${v.value}`;
|
|
41914
42384
|
if (v.type === "ARITH") return `${a.field} = ${formatArithExprStr(v)}`;
|
|
41915
42385
|
if (v.type === "CASE_VALUE") return `${a.field} = CASE WHEN ...`;
|
|
42386
|
+
if (v.type === "STRING_FUNC") return `${a.field} = ${v.func}(...)`;
|
|
41916
42387
|
if (v.type === "SCALAR_SUBQUERY") return `${a.field} = (SELECT ...)`;
|
|
41917
42388
|
if (v.type === "SOURCE_FIELD") return `${a.field} = ${v.alias}.${v.field}`;
|
|
41918
42389
|
return `${a.field} = (${v.type})`;
|
|
@@ -41922,7 +42393,7 @@ function formatArithExprStr(expr) {
|
|
|
41922
42393
|
}
|
|
41923
42394
|
function formatArithNodeStr(node) {
|
|
41924
42395
|
if (node.type === "FIELD_REF") return node.field;
|
|
41925
|
-
if (node.type === "NUMBER") return
|
|
42396
|
+
if (node.type === "NUMBER") return numberLiteralText(node);
|
|
41926
42397
|
if (node.type === "ARITH") return `(${formatArithExprStr(node)})`;
|
|
41927
42398
|
return "...";
|
|
41928
42399
|
}
|
|
@@ -42384,6 +42855,7 @@ function withRequestGate(client, gate) {
|
|
|
42384
42855
|
},
|
|
42385
42856
|
getApps: () => gate.runReadOnly(() => client.getApps()),
|
|
42386
42857
|
getFields: (appId) => gate.runReadOnly(() => client.getFields(appId)),
|
|
42858
|
+
getNumberPrecision: (appId) => gate.runReadOnly(() => client.getNumberPrecision(appId)),
|
|
42387
42859
|
getProcessStatuses: (appId) => gate.runReadOnly(() => client.getProcessStatuses(appId)),
|
|
42388
42860
|
postRecords: (params) => gate.runMutation(() => client.postRecords(params)),
|
|
42389
42861
|
putRecords: (params) => gate.runMutation(() => client.putRecords(params)),
|
|
@@ -42945,6 +43417,16 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
|
42945
43417
|
);
|
|
42946
43418
|
return flattenFormFieldProperties(res.properties);
|
|
42947
43419
|
},
|
|
43420
|
+
async getNumberPrecision(appId) {
|
|
43421
|
+
const qs = new URLSearchParams();
|
|
43422
|
+
qs.set("app", String(appId));
|
|
43423
|
+
const res = await requestJson(
|
|
43424
|
+
`${apiBasePath}/app/settings.json?${qs.toString()}`,
|
|
43425
|
+
{ method: "GET" },
|
|
43426
|
+
appId
|
|
43427
|
+
);
|
|
43428
|
+
return parseNumberPrecisionSettings(res);
|
|
43429
|
+
},
|
|
42948
43430
|
async getProcessStatuses(appId) {
|
|
42949
43431
|
const qs = new URLSearchParams();
|
|
42950
43432
|
qs.set("app", String(appId));
|
|
@@ -43473,6 +43955,12 @@ async function createKsqlRuntime(serverOptions, input) {
|
|
|
43473
43955
|
if (!routed) throw new Error(`AuthError: profile "${binding.profile}" is not resolved for APP${appId}.`);
|
|
43474
43956
|
return routed.getFields(binding.appId);
|
|
43475
43957
|
},
|
|
43958
|
+
getNumberPrecision: (appId) => {
|
|
43959
|
+
const binding = resolveRuntimeBinding(runtimeContext.sqlContext, appId);
|
|
43960
|
+
const routed = runtimeContext.clientsByProfile.get(binding.profile);
|
|
43961
|
+
if (!routed) throw new Error(`AuthError: profile "${binding.profile}" is not resolved for APP${appId}.`);
|
|
43962
|
+
return routed.getNumberPrecision(binding.appId);
|
|
43963
|
+
},
|
|
43476
43964
|
getProcessStatuses: (appId) => {
|
|
43477
43965
|
const binding = resolveRuntimeBinding(runtimeContext.sqlContext, appId);
|
|
43478
43966
|
const routed = runtimeContext.clientsByProfile.get(binding.profile);
|
|
@@ -43708,6 +44196,9 @@ function noOpClient() {
|
|
|
43708
44196
|
getFields: fail,
|
|
43709
44197
|
async getProcessStatuses() {
|
|
43710
44198
|
return { enable: false, states: [] };
|
|
44199
|
+
},
|
|
44200
|
+
async getNumberPrecision() {
|
|
44201
|
+
return { digits: 30, decimalPlaces: 10, roundingMode: "HALF_EVEN" };
|
|
43711
44202
|
}
|
|
43712
44203
|
};
|
|
43713
44204
|
}
|
|
@@ -44507,7 +44998,7 @@ Options:
|
|
|
44507
44998
|
-h, --help Show help
|
|
44508
44999
|
`);
|
|
44509
45000
|
}
|
|
44510
|
-
var SERVER_VERSION = true ? "3.
|
|
45001
|
+
var SERVER_VERSION = true ? "3.3.0" : "0.0.0-dev";
|
|
44511
45002
|
function createServer(args) {
|
|
44512
45003
|
const server = new McpServer({
|
|
44513
45004
|
name: "ksql-mcp",
|
|
@@ -44529,12 +45020,12 @@ function createServer(args) {
|
|
|
44529
45020
|
}, tools.explainTool);
|
|
44530
45021
|
server.registerTool("ksql_query", {
|
|
44531
45022
|
title: "Run read-only kSQL",
|
|
44532
|
-
description: "Execute read-only kSQL: SELECT, WITH, UNION, EXPLAIN, SHOW APPS, DESCRIBE, ASSERT, and INSERT/UPSERT/UPDATE ... VALIDATE ONLY. ASSERT failure always stops the batch. Local ORDER BY plans require complete input and fail instead of returning a truncated top-N; REST top-N and KORDER_NATIVE do not fetch a partial candidate set. VALIDATE ONLY always treats onLimit=truncate as error. VALIDATE ONLY performs local Tier-0 validation with zero write API calls. Supports multi-statement batches with temp tables, including VALIDATE ONLY INTO #err for later SELECT. Mutating DML is rejected.",
|
|
45023
|
+
description: "Execute read-only kSQL: SELECT, WITH, UNION, EXPLAIN, SHOW APPS, DESCRIBE, ASSERT, and INSERT/UPSERT/UPDATE ... VALIDATE ONLY. ASSERT failure always stops the batch. Local ORDER BY plans require complete input and fail instead of returning a truncated top-N; REST top-N and KORDER_NATIVE do not fetch a partial candidate set. VALIDATE ONLY always treats onLimit=truncate as error. VALIDATE ONLY performs local Tier-0 validation with zero write API calls; NUMBER targets use the app numberPrecision settings for integer-digit validation and fail closed if settings cannot be read. Excess fractional digits pass through for kintone to round automatically. Supports multi-statement batches with temp tables, including VALIDATE ONLY INTO #err for later SELECT. Mutating DML is rejected.",
|
|
44533
45024
|
inputSchema: queryInputShape
|
|
44534
45025
|
}, tools.queryTool);
|
|
44535
45026
|
server.registerTool("ksql_mutate", {
|
|
44536
45027
|
title: "Run mutating kSQL",
|
|
44537
|
-
description: "Execute DML kSQL with explicit allowDml, confirmText, and dmlMaxRows safety controls. Supports multi-statement DML batches with temp tables. ON ERROR SKIP INTO #err optionally isolates local Tier-0 validation failures and writes only valid rows; REJECT LIMIT stops with zero writes while returning diagnostics. INSERT/UPSERT INTO app ... SELECT supports app sources, temp tables, or joins of both. UPDATE ... FROM supports copying scalar fields from an app or temp table by matching target $id or a single-line-text/number business key to one source key. For UPSERT, dmlMaxRows counts inserts + updates. dmlMaxRows caps affected rows only, not source reads: source SELECT, ON ERROR SKIP candidates, and UPDATE ... FROM app reads use the runtime maxRecords (KSQL_MAX_RECORDS / profile query.maxRecords, default 500); temp tables hold at most 10000 rows by default (adjustable via tempTableMaxRows).",
|
|
45028
|
+
description: "Execute DML kSQL with explicit allowDml, confirmText, and dmlMaxRows safety controls. Supports multi-statement DML batches with temp tables. ON ERROR SKIP INTO #err optionally isolates local Tier-0 validation failures and writes only valid rows; REJECT LIMIT stops with zero writes while returning diagnostics. NUMBER targets use the destination app numberPrecision settings for integer-digit validation in normal, validation-only, and skip paths; settings failures are fail-closed. Excess fractional digits pass through for kintone to round automatically. INSERT/UPSERT INTO app ... SELECT supports app sources, temp tables, or joins of both. UPDATE ... FROM supports copying scalar fields from an app or temp table by matching target $id or a single-line-text/number business key to one source key. For UPSERT, dmlMaxRows counts inserts + updates. dmlMaxRows caps affected rows only, not source reads: source SELECT, ON ERROR SKIP candidates, and UPDATE ... FROM app reads use the runtime maxRecords (KSQL_MAX_RECORDS / profile query.maxRecords, default 500); temp tables hold at most 10000 rows by default (adjustable via tempTableMaxRows).",
|
|
44538
45029
|
inputSchema: mutateInputShape
|
|
44539
45030
|
}, tools.mutateTool);
|
|
44540
45031
|
server.registerTool("ksql_describe_app", {
|