@rex0220/kintone-sql-tools 3.2.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 +437 -106
- package/dist-mcp/ksql-mcp.js +440 -109
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-mcp/ksql-mcp.js
CHANGED
|
@@ -31034,6 +31034,9 @@ var KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
31034
31034
|
["SUBSTR", "SUBSTR" /* SUBSTR */],
|
|
31035
31035
|
["CONCAT", "CONCAT" /* CONCAT */],
|
|
31036
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 */],
|
|
31037
31040
|
["COALESCE", "COALESCE" /* COALESCE */],
|
|
31038
31041
|
["NULLIF", "NULLIF" /* NULLIF */],
|
|
31039
31042
|
["ISNULL", "ISNULL" /* ISNULL */],
|
|
@@ -31166,7 +31169,7 @@ var Lexer = class {
|
|
|
31166
31169
|
);
|
|
31167
31170
|
}
|
|
31168
31171
|
// ----------------------------------------------------------
|
|
31169
|
-
// 数値:
|
|
31172
|
+
// 数値: digits[.digits][e[+-]digits](先頭/末尾 dot は受理しない)
|
|
31170
31173
|
// ----------------------------------------------------------
|
|
31171
31174
|
readNumber(start) {
|
|
31172
31175
|
while (this.pos < this.input.length && isDigit(this.input[this.pos])) {
|
|
@@ -31178,6 +31181,16 @@ var Lexer = class {
|
|
|
31178
31181
|
this.pos++;
|
|
31179
31182
|
}
|
|
31180
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
|
+
}
|
|
31181
31194
|
return this.makeToken(
|
|
31182
31195
|
"NUMBER" /* NUMBER */,
|
|
31183
31196
|
this.input.slice(start, this.pos),
|
|
@@ -31377,8 +31390,94 @@ function isJapanese(cp) {
|
|
|
31377
31390
|
return cp >= 12352 && cp <= 12543 || cp >= 13312 && cp <= 40959 || cp >= 63744 && cp <= 64255 || cp >= 65281 && cp <= 65376;
|
|
31378
31391
|
}
|
|
31379
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
|
+
|
|
31380
31472
|
// src/types/ast.ts
|
|
31381
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
|
+
}
|
|
31382
31481
|
|
|
31383
31482
|
// src/parser/parser.ts
|
|
31384
31483
|
var MAX_BATCH_STATEMENTS = 20;
|
|
@@ -31411,6 +31510,9 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
|
|
|
31411
31510
|
"COALESCE" /* COALESCE */,
|
|
31412
31511
|
"NULLIF" /* NULLIF */,
|
|
31413
31512
|
"ISNULL" /* ISNULL */,
|
|
31513
|
+
"REGEXP_LIKE" /* REGEXP_LIKE */,
|
|
31514
|
+
"REGEXP_REPLACE" /* REGEXP_REPLACE */,
|
|
31515
|
+
"REGEXP_SUBSTR" /* REGEXP_SUBSTR */,
|
|
31414
31516
|
"LEFT" /* LEFT */,
|
|
31415
31517
|
"RIGHT" /* RIGHT */,
|
|
31416
31518
|
"INSTR" /* INSTR */,
|
|
@@ -32174,12 +32276,12 @@ var Parser = class {
|
|
|
32174
32276
|
if (this.peek().kind === "-" /* MINUS */) {
|
|
32175
32277
|
this.advance();
|
|
32176
32278
|
const operand = this.parseAggPrimary();
|
|
32177
|
-
if (operand.type === "NUMBER") return {
|
|
32178
|
-
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 };
|
|
32179
32281
|
}
|
|
32180
32282
|
if (this.peek().kind === "NUMBER" /* NUMBER */) {
|
|
32181
32283
|
const tok = this.advance();
|
|
32182
|
-
return
|
|
32284
|
+
return makeNumberLiteral(tok.value);
|
|
32183
32285
|
}
|
|
32184
32286
|
const aggFunc = this.tryAggregateFunc();
|
|
32185
32287
|
if (aggFunc !== null) {
|
|
@@ -32231,7 +32333,7 @@ var Parser = class {
|
|
|
32231
32333
|
if (this.allowUnaryPlusNumber && this.peek().kind === "+" /* PLUS */) {
|
|
32232
32334
|
this.advance();
|
|
32233
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");
|
|
32234
|
-
return {
|
|
32336
|
+
return makeNumberLiteral(`+${number4.value}`);
|
|
32235
32337
|
}
|
|
32236
32338
|
if (this.peek().kind === "-" /* MINUS */) {
|
|
32237
32339
|
this.advance();
|
|
@@ -32239,8 +32341,8 @@ var Parser = class {
|
|
|
32239
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());
|
|
32240
32342
|
}
|
|
32241
32343
|
const operand = this.parseArithPrimary();
|
|
32242
|
-
if (operand.type === "NUMBER") return {
|
|
32243
|
-
return { type: "ARITH", left:
|
|
32344
|
+
if (operand.type === "NUMBER") return makeNumberLiteral(`-${numberLiteralText(operand)}`);
|
|
32345
|
+
return { type: "ARITH", left: makeNumberLiteral("0"), op: "-", right: operand };
|
|
32244
32346
|
}
|
|
32245
32347
|
if (this.tryStringFuncName() !== null) {
|
|
32246
32348
|
return this.parseStringFuncExpr();
|
|
@@ -32248,7 +32350,7 @@ var Parser = class {
|
|
|
32248
32350
|
const tok = this.peek();
|
|
32249
32351
|
if (tok.kind === "NUMBER" /* NUMBER */) {
|
|
32250
32352
|
this.advance();
|
|
32251
|
-
return
|
|
32353
|
+
return makeNumberLiteral(tok.value);
|
|
32252
32354
|
}
|
|
32253
32355
|
if (tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */) {
|
|
32254
32356
|
this.advance();
|
|
@@ -32352,6 +32454,9 @@ var Parser = class {
|
|
|
32352
32454
|
["SUBSTR" /* SUBSTR */]: "SUBSTRING",
|
|
32353
32455
|
["CONCAT" /* CONCAT */]: "CONCAT",
|
|
32354
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",
|
|
32355
32460
|
["TRANSLATE" /* TRANSLATE */]: "TRANSLATE",
|
|
32356
32461
|
["COALESCE" /* COALESCE */]: "COALESCE",
|
|
32357
32462
|
["NULLIF" /* NULLIF */]: "NULLIF",
|
|
@@ -32891,7 +32996,7 @@ var Parser = class {
|
|
|
32891
32996
|
}
|
|
32892
32997
|
if (tok.kind === "NUMBER" /* NUMBER */ || tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */ || tok.kind === "(" /* LPAREN */ || tok.kind === "-" /* MINUS */ || this.tryStringFuncName() !== null) {
|
|
32893
32998
|
const expr = this.parseArithAddSub();
|
|
32894
|
-
if (expr.type === "NUMBER") return
|
|
32999
|
+
if (expr.type === "NUMBER") return expr;
|
|
32895
33000
|
return { type: "ARITH_VALUE", expr };
|
|
32896
33001
|
}
|
|
32897
33002
|
throw new ParseError(
|
|
@@ -32918,15 +33023,15 @@ var Parser = class {
|
|
|
32918
33023
|
if (tok.kind === "STRING" /* STRING */) {
|
|
32919
33024
|
values.push({ type: "STRING", value: tok.value });
|
|
32920
33025
|
} else if (tok.kind === "NUMBER" /* NUMBER */) {
|
|
32921
|
-
values.push(
|
|
33026
|
+
values.push(makeNumberLiteral(tok.value));
|
|
32922
33027
|
} else if (tok.kind === "-" /* MINUS */ || tok.kind === "+" /* PLUS */) {
|
|
32923
33028
|
const number4 = this.peek();
|
|
32924
33029
|
if (number4.kind !== "NUMBER" /* NUMBER */) {
|
|
32925
33030
|
throw new ParseError(invalidValueMessage, tok);
|
|
32926
33031
|
}
|
|
32927
33032
|
this.advance();
|
|
32928
|
-
const sign = tok.kind === "-" /* MINUS */ ? -
|
|
32929
|
-
values.push({
|
|
33033
|
+
const sign = tok.kind === "-" /* MINUS */ ? "-" : "+";
|
|
33034
|
+
values.push(makeNumberLiteral(`${sign}${number4.value}`));
|
|
32930
33035
|
} else if (tok.kind === "VARIABLE" /* VARIABLE */) {
|
|
32931
33036
|
values.push({ type: "VARIABLE", name: tok.value.slice(1).toLowerCase() });
|
|
32932
33037
|
} else {
|
|
@@ -33116,14 +33221,13 @@ var Parser = class {
|
|
|
33116
33221
|
} else if (this.peek().kind === "-" /* MINUS */ || this.peek().kind === "+" /* PLUS */) {
|
|
33117
33222
|
const sign = this.advance();
|
|
33118
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");
|
|
33119
|
-
|
|
33120
|
-
row.push({ type: "NUMBER", value: sign.kind === "-" /* MINUS */ ? -value : value });
|
|
33224
|
+
row.push(makeNumberLiteral(`${sign.kind === "-" /* MINUS */ ? "-" : "+"}${number4.value}`));
|
|
33121
33225
|
} else {
|
|
33122
33226
|
const tok = this.advance();
|
|
33123
33227
|
if (tok.kind === "STRING" /* STRING */) {
|
|
33124
33228
|
row.push({ type: "STRING", value: tok.value });
|
|
33125
33229
|
} else if (tok.kind === "NUMBER" /* NUMBER */) {
|
|
33126
|
-
row.push(
|
|
33230
|
+
row.push(makeNumberLiteral(tok.value));
|
|
33127
33231
|
} else {
|
|
33128
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);
|
|
33129
33233
|
}
|
|
@@ -33945,7 +34049,7 @@ function convertValue(value, op) {
|
|
|
33945
34049
|
case "STRING":
|
|
33946
34050
|
return convertString(value);
|
|
33947
34051
|
case "NUMBER":
|
|
33948
|
-
return
|
|
34052
|
+
return numberLiteralText(value);
|
|
33949
34053
|
case "KINTONE_FUNC":
|
|
33950
34054
|
return convertKintoneFunc(value);
|
|
33951
34055
|
case "IN_LIST":
|
|
@@ -33974,7 +34078,7 @@ function convertInList(v, op) {
|
|
|
33974
34078
|
}
|
|
33975
34079
|
assertResolvedInListValues(v.values);
|
|
33976
34080
|
const values = v.values.map(
|
|
33977
|
-
(item) => item.type === "STRING" ? convertString(item) :
|
|
34081
|
+
(item) => item.type === "STRING" ? convertString(item) : numberLiteralText(item)
|
|
33978
34082
|
).join(",");
|
|
33979
34083
|
return `(${values})`;
|
|
33980
34084
|
}
|
|
@@ -34487,7 +34591,7 @@ function aggregateSyntheticName(func, distinct, arg) {
|
|
|
34487
34591
|
}
|
|
34488
34592
|
function arithNodeLabel(node) {
|
|
34489
34593
|
if (node.type === "FIELD_REF") return node.field;
|
|
34490
|
-
if (node.type === "NUMBER") return
|
|
34594
|
+
if (node.type === "NUMBER") return numberLiteralText(node);
|
|
34491
34595
|
if (node.type === "STRING_FUNC") return stringFuncLabel(node);
|
|
34492
34596
|
return `(${arithNodeLabel(node.left)}${node.op}${arithNodeLabel(node.right)})`;
|
|
34493
34597
|
}
|
|
@@ -34644,7 +34748,7 @@ function isNumericCandidate(expr, options) {
|
|
|
34644
34748
|
if (!isTargetField(expr.left, options)) return false;
|
|
34645
34749
|
if (expr.right.type !== "NUMBER") return false;
|
|
34646
34750
|
if (expr.op === "=") return true;
|
|
34647
|
-
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);
|
|
34648
34752
|
}
|
|
34649
34753
|
function isSelectionInCandidate(expr, options) {
|
|
34650
34754
|
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
@@ -35249,9 +35353,10 @@ function triCompare(left, right) {
|
|
|
35249
35353
|
}
|
|
35250
35354
|
function numberKey(value) {
|
|
35251
35355
|
if (value === "") return { band: 0 };
|
|
35356
|
+
const decimal = parseExactDecimal(value);
|
|
35357
|
+
if (decimal !== null) return { band: 2, value: decimal };
|
|
35252
35358
|
const numeric = Number(value);
|
|
35253
35359
|
if (numeric === Number.NEGATIVE_INFINITY) return { band: 1 };
|
|
35254
|
-
if (Number.isFinite(numeric)) return { band: 2, value: numeric };
|
|
35255
35360
|
if (numeric === Number.POSITIVE_INFINITY) return { band: 3 };
|
|
35256
35361
|
if (value === "NaN") return { band: 4 };
|
|
35257
35362
|
return { band: 5, value };
|
|
@@ -35260,7 +35365,7 @@ function compareNumbers(left, right) {
|
|
|
35260
35365
|
const a = numberKey(left);
|
|
35261
35366
|
const b = numberKey(right);
|
|
35262
35367
|
if (a.band !== b.band) return a.band < b.band ? -1 : 1;
|
|
35263
|
-
if (a.band === 2 && b.band === 2) return
|
|
35368
|
+
if (a.band === 2 && b.band === 2) return compareExactDecimal(a.value, b.value);
|
|
35264
35369
|
if (a.band === 5 && b.band === 5) return compareCodePointStrings(a.value, b.value);
|
|
35265
35370
|
return 0;
|
|
35266
35371
|
}
|
|
@@ -35360,7 +35465,9 @@ function selectScalarExtreme(values, extreme) {
|
|
|
35360
35465
|
const numeric = candidates.every((value) => !Number.isNaN(Number(value)));
|
|
35361
35466
|
const compare = (left, right) => {
|
|
35362
35467
|
if (numeric) {
|
|
35363
|
-
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));
|
|
35364
35471
|
if (numericCmp !== 0) return numericCmp;
|
|
35365
35472
|
}
|
|
35366
35473
|
return compareCodePointStrings(left, right);
|
|
@@ -35483,6 +35590,112 @@ function makeSafePadding(pad, gap) {
|
|
|
35483
35590
|
const repeated = pad.repeat(Math.ceil(gap / pad.length));
|
|
35484
35591
|
return sliceSafePrefix(repeated, gap);
|
|
35485
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
|
+
}
|
|
35486
35699
|
function evalStringFunc(expr, row) {
|
|
35487
35700
|
const args = expr.args.map((a) => evalStringFuncArg(a, row));
|
|
35488
35701
|
switch (expr.func) {
|
|
@@ -35546,6 +35759,21 @@ function evalStringFunc(expr, row) {
|
|
|
35546
35759
|
const to = args[2] ?? "";
|
|
35547
35760
|
return from === "" ? str : str.split(from).join(to);
|
|
35548
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
|
+
}
|
|
35549
35777
|
case "TRANSLATE": {
|
|
35550
35778
|
assertArity("TRANSLATE", args, 3, 3);
|
|
35551
35779
|
const from = [...args[1]];
|
|
@@ -35724,7 +35952,7 @@ function evalStringFuncArg(arg, row) {
|
|
|
35724
35952
|
if (arg.type === "STRING") return arg.value;
|
|
35725
35953
|
if (arg.type === "STRING_FUNC") return evalStringFunc(arg, row);
|
|
35726
35954
|
if (arg.type === "FIELD_REF") return resolveFieldRef(row, arg.field);
|
|
35727
|
-
if (arg.type === "NUMBER") return
|
|
35955
|
+
if (arg.type === "NUMBER") return numberLiteralText(arg);
|
|
35728
35956
|
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return "";
|
|
35729
35957
|
return String(evalArithExpr(arg, row));
|
|
35730
35958
|
}
|
|
@@ -35773,7 +36001,7 @@ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType, semantics
|
|
|
35773
36001
|
let values = null;
|
|
35774
36002
|
if (right.type === "IN_LIST") {
|
|
35775
36003
|
assertResolvedInListValues2(right.values);
|
|
35776
|
-
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));
|
|
35777
36005
|
}
|
|
35778
36006
|
if (right.type === "SUBQUERY_IN_LIST") {
|
|
35779
36007
|
values = right.resolved;
|
|
@@ -35853,6 +36081,10 @@ var SINGLE_OBJECT_FIELD_TYPES = /* @__PURE__ */ new Set(["CREATOR", "MODIFIER"])
|
|
|
35853
36081
|
function typedInContains(leftStr, values, fieldType) {
|
|
35854
36082
|
const fallback = () => values.has(leftStr);
|
|
35855
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
|
+
}
|
|
35856
36088
|
let parsed;
|
|
35857
36089
|
if (STRING_ARRAY_FIELD_TYPES.has(fieldType) || OBJECT_ARRAY_FIELD_TYPES.has(fieldType) || SINGLE_OBJECT_FIELD_TYPES.has(fieldType)) {
|
|
35858
36090
|
try {
|
|
@@ -35911,7 +36143,7 @@ function resolveValue(value, row, resolveFieldType, resolveFieldSemantics2) {
|
|
|
35911
36143
|
case "STRING":
|
|
35912
36144
|
return value.value;
|
|
35913
36145
|
case "NUMBER":
|
|
35914
|
-
return
|
|
36146
|
+
return numberLiteralText(value);
|
|
35915
36147
|
case "KINTONE_FUNC":
|
|
35916
36148
|
return resolveKintoneFunc(value.name);
|
|
35917
36149
|
case "IN_LIST":
|
|
@@ -36351,7 +36583,7 @@ function convertDmlSqlValue(value, fieldType) {
|
|
|
36351
36583
|
case "STRING":
|
|
36352
36584
|
return convertString2(value.value, fieldType);
|
|
36353
36585
|
case "NUMBER":
|
|
36354
|
-
return
|
|
36586
|
+
return numberLiteralText(value);
|
|
36355
36587
|
case "ARRAY":
|
|
36356
36588
|
return convertArray(value.elements.map((e) => e.value), fieldType);
|
|
36357
36589
|
case "KINTONE_FUNC":
|
|
@@ -36983,7 +37215,7 @@ function evalAggArithExpr(node, rows, resolveAggSortKind) {
|
|
|
36983
37215
|
}
|
|
36984
37216
|
}
|
|
36985
37217
|
function aggArithDefaultKey(node) {
|
|
36986
|
-
if (node.type === "NUMBER") return
|
|
37218
|
+
if (node.type === "NUMBER") return numberLiteralText(node);
|
|
36987
37219
|
if (node.type === "AGG_REF") return aggregateSyntheticName2(node.func, node.distinct, node.arg);
|
|
36988
37220
|
return `${aggArithDefaultKey(node.left)}${node.op}${aggArithDefaultKey(node.right)}`;
|
|
36989
37221
|
}
|
|
@@ -37333,7 +37565,7 @@ function stripParentShortcutColumns(row) {
|
|
|
37333
37565
|
function arithColDefaultKey(expr) {
|
|
37334
37566
|
const nodeLabel = (n) => {
|
|
37335
37567
|
if (n.type === "FIELD_REF") return n.field;
|
|
37336
|
-
if (n.type === "NUMBER") return
|
|
37568
|
+
if (n.type === "NUMBER") return numberLiteralText(n);
|
|
37337
37569
|
if (n.type === "STRING_FUNC") return stringFuncDefaultKey(n);
|
|
37338
37570
|
return `(${nodeLabel(n.left)}${n.op}${nodeLabel(n.right)})`;
|
|
37339
37571
|
};
|
|
@@ -37362,10 +37594,11 @@ function hasAggregateInStringFuncExpr2(expr) {
|
|
|
37362
37594
|
function resolveAggInStringFuncArg(arg, rows, resolveAggSortKind) {
|
|
37363
37595
|
if (arg.type === "AGG_REF") {
|
|
37364
37596
|
const value = evalAggregate(arg.func, arg.distinct, arg.arg, arg.separator, rows, resolveAggSortKind);
|
|
37365
|
-
return typeof value === "number" ? { type: "NUMBER", value } : { type: "STRING", value };
|
|
37597
|
+
return typeof value === "number" ? { type: "NUMBER", value, raw: String(value) } : { type: "STRING", value };
|
|
37366
37598
|
}
|
|
37367
37599
|
if (arg.type === "AGG_ARITH") {
|
|
37368
|
-
|
|
37600
|
+
const value = evalAggArithExpr(arg, rows, resolveAggSortKind);
|
|
37601
|
+
return { type: "NUMBER", value, raw: String(value) };
|
|
37369
37602
|
}
|
|
37370
37603
|
if (arg.type === "STRING_FUNC") {
|
|
37371
37604
|
return resolveAggInStringFuncExpr(arg, rows, resolveAggSortKind);
|
|
@@ -37483,10 +37716,43 @@ function toFlatString(value) {
|
|
|
37483
37716
|
}
|
|
37484
37717
|
}
|
|
37485
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
|
+
|
|
37486
37752
|
// src/core/dmlValidation.ts
|
|
37487
37753
|
var ARRAY_TYPES2 = /* @__PURE__ */ new Set(["CHECK_BOX", "MULTI_SELECT"]);
|
|
37488
37754
|
var CHOICE_TYPES = /* @__PURE__ */ new Set(["DROP_DOWN", "RADIO_BUTTON", "CHECK_BOX", "MULTI_SELECT"]);
|
|
37489
|
-
function validateAndNormalizeDmlValue(raw, field) {
|
|
37755
|
+
function validateAndNormalizeDmlValue(raw, field, numberPrecision) {
|
|
37490
37756
|
if (field.fieldType === "DATE" || field.fieldType === "TIME" || field.fieldType === "DATETIME") {
|
|
37491
37757
|
const original = rawScalarText(raw);
|
|
37492
37758
|
if (original !== "" && !isValidTemporalInput(original, field.fieldType)) {
|
|
@@ -37505,7 +37771,8 @@ function validateAndNormalizeDmlValue(raw, field) {
|
|
|
37505
37771
|
}
|
|
37506
37772
|
if (!isEmpty(value) && field.fieldType === "NUMBER") {
|
|
37507
37773
|
const text = String(value);
|
|
37508
|
-
|
|
37774
|
+
const decimal = parseExactDecimal(text);
|
|
37775
|
+
if (decimal === null) {
|
|
37509
37776
|
return { ok: false, code: "ERR_TYPE_NUMBER", message: `${field.code} \u306F\u6570\u5024\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044` };
|
|
37510
37777
|
}
|
|
37511
37778
|
if (field.minValue != null && compareDecimal(text, field.minValue) < 0) {
|
|
@@ -37514,6 +37781,17 @@ function validateAndNormalizeDmlValue(raw, field) {
|
|
|
37514
37781
|
if (field.maxValue != null && compareDecimal(text, field.maxValue) > 0) {
|
|
37515
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` };
|
|
37516
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
|
+
}
|
|
37517
37795
|
}
|
|
37518
37796
|
if (!isEmpty(value) && (field.fieldType === "DATE" || field.fieldType === "TIME" || field.fieldType === "DATETIME")) {
|
|
37519
37797
|
if (!isValidTemporal(String(value), field.fieldType)) {
|
|
@@ -37541,7 +37819,8 @@ function validateAndNormalizeDmlValue(raw, field) {
|
|
|
37541
37819
|
}
|
|
37542
37820
|
function rawScalarText(raw) {
|
|
37543
37821
|
if (raw == null) return "";
|
|
37544
|
-
if (isSqlValue(raw) &&
|
|
37822
|
+
if (isSqlValue(raw) && raw.type === "NUMBER") return numberLiteralText(raw);
|
|
37823
|
+
if (isSqlValue(raw) && raw.type === "STRING") return raw.value;
|
|
37545
37824
|
return typeof raw === "string" || typeof raw === "number" ? String(raw) : "";
|
|
37546
37825
|
}
|
|
37547
37826
|
function isValidTemporalInput(value, type) {
|
|
@@ -37591,34 +37870,6 @@ function isEmpty(value) {
|
|
|
37591
37870
|
function typeCode(type) {
|
|
37592
37871
|
return type === "NUMBER" ? "ERR_TYPE_NUMBER" : "ERR_TYPE_DATE";
|
|
37593
37872
|
}
|
|
37594
|
-
function isFiniteDecimal(value) {
|
|
37595
|
-
return /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(value.trim());
|
|
37596
|
-
}
|
|
37597
|
-
function compareDecimal(left, right) {
|
|
37598
|
-
const normalize = (input) => {
|
|
37599
|
-
let s = input.trim();
|
|
37600
|
-
let sign = 1;
|
|
37601
|
-
if (s.startsWith("-")) {
|
|
37602
|
-
sign = -1;
|
|
37603
|
-
s = s.slice(1);
|
|
37604
|
-
} else if (s.startsWith("+")) s = s.slice(1);
|
|
37605
|
-
let [whole, fraction = ""] = s.split(".");
|
|
37606
|
-
whole = (whole || "0").replace(/^0+(?=\d)/, "");
|
|
37607
|
-
fraction = fraction.replace(/0+$/, "");
|
|
37608
|
-
if (/^0*$/.test(whole) && fraction === "") sign = 1;
|
|
37609
|
-
return { sign, whole, fraction };
|
|
37610
|
-
};
|
|
37611
|
-
const a = normalize(left);
|
|
37612
|
-
const b = normalize(right);
|
|
37613
|
-
if (a.sign !== b.sign) return a.sign < b.sign ? -1 : 1;
|
|
37614
|
-
const direction = a.sign;
|
|
37615
|
-
if (a.whole.length !== b.whole.length) return a.whole.length < b.whole.length ? -direction : direction;
|
|
37616
|
-
if (a.whole !== b.whole) return a.whole < b.whole ? -direction : direction;
|
|
37617
|
-
const width = Math.max(a.fraction.length, b.fraction.length);
|
|
37618
|
-
const af = a.fraction.padEnd(width, "0");
|
|
37619
|
-
const bf = b.fraction.padEnd(width, "0");
|
|
37620
|
-
return af === bf ? 0 : af < bf ? -direction : direction;
|
|
37621
|
-
}
|
|
37622
37873
|
function isValidTemporal(value, type) {
|
|
37623
37874
|
if (type === "TIME") {
|
|
37624
37875
|
const m2 = /^(\d{2}):(\d{2})(?::(\d{2}))?$/.exec(value);
|
|
@@ -37646,7 +37897,7 @@ var VALIDATION_META_COLUMNS = [
|
|
|
37646
37897
|
"$err_code",
|
|
37647
37898
|
"$err_message"
|
|
37648
37899
|
];
|
|
37649
|
-
function validateDmlCandidates(candidates, operation, payloadFields, targetFields, fieldInfos, statementNumber) {
|
|
37900
|
+
function validateDmlCandidates(candidates, operation, payloadFields, targetFields, fieldInfos, statementNumber, numberPrecision) {
|
|
37650
37901
|
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
37651
37902
|
const errors = [];
|
|
37652
37903
|
const invalid = /* @__PURE__ */ new Set();
|
|
@@ -37654,7 +37905,7 @@ function validateDmlCandidates(candidates, operation, payloadFields, targetField
|
|
|
37654
37905
|
candidate.record ??= {};
|
|
37655
37906
|
const rowErrors = [...candidate.preErrors];
|
|
37656
37907
|
for (const code of targetFields) {
|
|
37657
|
-
const result = validateAndNormalizeDmlValue(candidate.payload.get(code), infoByCode.get(code));
|
|
37908
|
+
const result = validateAndNormalizeDmlValue(candidate.payload.get(code), infoByCode.get(code), numberPrecision);
|
|
37658
37909
|
if (!result.ok) rowErrors.push({ field: code, code: result.code, message: result.message });
|
|
37659
37910
|
else candidate.record[code] = { value: result.value };
|
|
37660
37911
|
}
|
|
@@ -37664,14 +37915,14 @@ function validateDmlCandidates(candidates, operation, payloadFields, targetField
|
|
|
37664
37915
|
if (candidate.payload.has(info.code)) continue;
|
|
37665
37916
|
const emptyDefault = isEmptyDmlValue(info.defaultValue);
|
|
37666
37917
|
if (!emptyDefault) {
|
|
37667
|
-
const defaultResult = validateAndNormalizeDmlValue(info.defaultValue, info);
|
|
37918
|
+
const defaultResult = validateAndNormalizeDmlValue(info.defaultValue, info, numberPrecision);
|
|
37668
37919
|
if (!defaultResult.ok) rowErrors.push({
|
|
37669
37920
|
field: info.code,
|
|
37670
37921
|
code: defaultResult.code,
|
|
37671
37922
|
message: `\u65E2\u5B9A\u5024: ${defaultResult.message}`
|
|
37672
37923
|
});
|
|
37673
37924
|
} else {
|
|
37674
|
-
const emptyResult = validateAndNormalizeDmlValue("", info);
|
|
37925
|
+
const emptyResult = validateAndNormalizeDmlValue("", info, numberPrecision);
|
|
37675
37926
|
if (!emptyResult.ok) {
|
|
37676
37927
|
rowErrors.push({ field: info.code, code: emptyResult.code, message: emptyResult.message });
|
|
37677
37928
|
} else if (info.required) {
|
|
@@ -37699,7 +37950,8 @@ function renderValidationValue(value) {
|
|
|
37699
37950
|
if (value == null) return "";
|
|
37700
37951
|
if (typeof value === "object" && "type" in value) {
|
|
37701
37952
|
const sql = value;
|
|
37702
|
-
if (sql.type === "
|
|
37953
|
+
if (sql.type === "NUMBER") return sql.raw ?? String(sql.value ?? "");
|
|
37954
|
+
if (sql.type === "STRING") return String(sql.value ?? "");
|
|
37703
37955
|
if (sql.type === "ARRAY") return JSON.stringify(sql.elements?.map((e) => e.value) ?? []);
|
|
37704
37956
|
}
|
|
37705
37957
|
if (Array.isArray(value)) return JSON.stringify(value);
|
|
@@ -37941,6 +38193,7 @@ function createEmptyMetrics() {
|
|
|
37941
38193
|
putCalls: 0,
|
|
37942
38194
|
deleteCalls: 0,
|
|
37943
38195
|
fieldCalls: 0,
|
|
38196
|
+
numberPrecisionCalls: 0,
|
|
37944
38197
|
appsCalls: 0,
|
|
37945
38198
|
processStatusCalls: 0,
|
|
37946
38199
|
cursorCreateCalls: 0,
|
|
@@ -38026,6 +38279,10 @@ function wrapClientWithMetrics(client, metrics) {
|
|
|
38026
38279
|
metrics.fieldCalls += 1;
|
|
38027
38280
|
return client.getFields(appId);
|
|
38028
38281
|
},
|
|
38282
|
+
getNumberPrecision: (appId) => {
|
|
38283
|
+
metrics.numberPrecisionCalls += 1;
|
|
38284
|
+
return client.getNumberPrecision(appId);
|
|
38285
|
+
},
|
|
38029
38286
|
getProcessStatuses: (appId) => {
|
|
38030
38287
|
metrics.processStatusCalls += 1;
|
|
38031
38288
|
return client.getProcessStatuses(appId);
|
|
@@ -38293,7 +38550,7 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
38293
38550
|
const first = resolvedStmt2.expr.query.columns[0];
|
|
38294
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");
|
|
38295
38552
|
const numberValue = numeric ? Number(value) : Number.NaN;
|
|
38296
|
-
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 });
|
|
38297
38554
|
} catch (e) {
|
|
38298
38555
|
if (e instanceof ScalarSubqueryError) {
|
|
38299
38556
|
throw new Error(`ArgumentError: ${e.message}`);
|
|
@@ -38311,7 +38568,10 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
38311
38568
|
variables.set(stmt.name, { type: "string", value: injected[stmt.name] });
|
|
38312
38569
|
} else {
|
|
38313
38570
|
const value = evaluateScalarExpr(stmt.default);
|
|
38314
|
-
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
|
+
});
|
|
38315
38575
|
}
|
|
38316
38576
|
return {};
|
|
38317
38577
|
}
|
|
@@ -38487,7 +38747,7 @@ function evaluateScalarExpr(expr) {
|
|
|
38487
38747
|
case "STRING":
|
|
38488
38748
|
return { type: "string", value: expr.value };
|
|
38489
38749
|
case "NUMBER":
|
|
38490
|
-
return { type: "number", value: expr.value };
|
|
38750
|
+
return { type: "number", value: expr.value, raw: numberLiteralText(expr) };
|
|
38491
38751
|
case "KINTONE_FUNC":
|
|
38492
38752
|
return { type: "string", value: resolveKintoneFunc(expr.name) };
|
|
38493
38753
|
case "STRING_FUNC":
|
|
@@ -38497,7 +38757,7 @@ function evaluateScalarExpr(expr) {
|
|
|
38497
38757
|
if (!Number.isFinite(value)) {
|
|
38498
38758
|
throw new Error("ArgumentError: SET scalar arithmetic produced a non-finite number.");
|
|
38499
38759
|
}
|
|
38500
|
-
return { type: "number", value };
|
|
38760
|
+
return { type: "number", value, raw: String(value) };
|
|
38501
38761
|
}
|
|
38502
38762
|
}
|
|
38503
38763
|
}
|
|
@@ -38512,7 +38772,7 @@ function resolveVariableRefs(node, variables) {
|
|
|
38512
38772
|
if (value === void 0) {
|
|
38513
38773
|
throw new Error(`ParseError: variable @${obj["name"]} is not defined in this batch.`);
|
|
38514
38774
|
}
|
|
38515
|
-
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 };
|
|
38516
38776
|
}
|
|
38517
38777
|
return Object.fromEntries(
|
|
38518
38778
|
Object.entries(obj).map(([key, value]) => [key, resolveVariableRefs(value, variables)])
|
|
@@ -38578,7 +38838,7 @@ async function evalAssertOperand(operand, client, options, cacheContext, tempTab
|
|
|
38578
38838
|
case "VARIABLE":
|
|
38579
38839
|
throw new Error(`ParseError: unresolved batch variable @${operand.name}.`);
|
|
38580
38840
|
case "NUMBER":
|
|
38581
|
-
return
|
|
38841
|
+
return numberLiteralText(operand);
|
|
38582
38842
|
case "STRING":
|
|
38583
38843
|
return operand.value;
|
|
38584
38844
|
case "ARITH":
|
|
@@ -39861,8 +40121,8 @@ async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords2, pa
|
|
|
39861
40121
|
}
|
|
39862
40122
|
var UPSERT_IN_CHUNK_SIZE = 50;
|
|
39863
40123
|
function normalizeKeyPart(v) {
|
|
39864
|
-
const
|
|
39865
|
-
if (
|
|
40124
|
+
const decimal = parseExactDecimal(v);
|
|
40125
|
+
if (decimal !== null) return JSON.stringify(decimal);
|
|
39866
40126
|
return v;
|
|
39867
40127
|
}
|
|
39868
40128
|
function upsertCompositeKey(parts) {
|
|
@@ -40014,6 +40274,7 @@ var optionOrderCache = /* @__PURE__ */ new Map();
|
|
|
40014
40274
|
var sortKindCache = /* @__PURE__ */ new Map();
|
|
40015
40275
|
var fieldInfoCache = /* @__PURE__ */ new Map();
|
|
40016
40276
|
var processStatusCache = /* @__PURE__ */ new Map();
|
|
40277
|
+
var numberPrecisionCache = /* @__PURE__ */ new Map();
|
|
40017
40278
|
function getScopedCacheValue(root, cacheContext, appId) {
|
|
40018
40279
|
return root.get(cacheContext)?.get(appId);
|
|
40019
40280
|
}
|
|
@@ -40035,6 +40296,13 @@ async function getFieldsCached(appId, client, cacheContext) {
|
|
|
40035
40296
|
setScopedCacheValue(fieldInfoCache, cacheContext, appId, loading);
|
|
40036
40297
|
return loading;
|
|
40037
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
|
+
}
|
|
40038
40306
|
async function getProcessStatusesCached(appId, client, cacheContext) {
|
|
40039
40307
|
const cached2 = getScopedCacheValue(processStatusCache, cacheContext, appId);
|
|
40040
40308
|
if (cached2) return cached2;
|
|
@@ -40309,6 +40577,23 @@ async function loadWritableTopLevelDmlFields(appId, targetFields, client, cacheC
|
|
|
40309
40577
|
assertWritableTopLevelDmlFields(appId, targetFields, fieldInfos);
|
|
40310
40578
|
return fieldInfos;
|
|
40311
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
|
+
}
|
|
40312
40597
|
async function executeDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
|
|
40313
40598
|
return (await prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber)).result;
|
|
40314
40599
|
}
|
|
@@ -40336,6 +40621,13 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
|
|
|
40336
40621
|
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
40337
40622
|
}
|
|
40338
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
|
+
);
|
|
40339
40631
|
const candidates = await materializeValidationCandidates(stmt, operation, client, options, cacheContext, tempTables, infoByCode);
|
|
40340
40632
|
const { errors, invalidRows, invalidRowNumbers } = validateDmlCandidates(
|
|
40341
40633
|
candidates,
|
|
@@ -40343,7 +40635,8 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
|
|
|
40343
40635
|
payloadFields,
|
|
40344
40636
|
targetFields,
|
|
40345
40637
|
fieldInfos,
|
|
40346
|
-
statementNumber
|
|
40638
|
+
statementNumber,
|
|
40639
|
+
numberPrecision
|
|
40347
40640
|
);
|
|
40348
40641
|
const columns = [...payloadFields, ...VALIDATION_META_COLUMNS];
|
|
40349
40642
|
const result = {
|
|
@@ -40567,6 +40860,7 @@ async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cach
|
|
|
40567
40860
|
tempTables
|
|
40568
40861
|
);
|
|
40569
40862
|
const sourceByKey = /* @__PURE__ */ new Map();
|
|
40863
|
+
const sourceQueryByKey = /* @__PURE__ */ new Map();
|
|
40570
40864
|
for (const row of sourceRows) {
|
|
40571
40865
|
if (!Object.prototype.hasOwnProperty.call(row, from.joinKeyField)) {
|
|
40572
40866
|
throw new Error(`ArgumentError: UPDATE ... FROM source column ${from.joinKeyField} does not exist.`);
|
|
@@ -40576,6 +40870,7 @@ async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cach
|
|
|
40576
40870
|
throw new Error(`ArgumentError: UPDATE ... FROM source has multiple rows for normalized key ${key}.`);
|
|
40577
40871
|
}
|
|
40578
40872
|
sourceByKey.set(key, row);
|
|
40873
|
+
sourceQueryByKey.set(key, String(row[from.joinKeyField]).trim());
|
|
40579
40874
|
}
|
|
40580
40875
|
if (sourceByKey.size === 0) return [];
|
|
40581
40876
|
const maxRecords2 = options.maxRecords ?? 1e4;
|
|
@@ -40584,7 +40879,7 @@ async function resolveUpdateFromMatchedRecords(stmt, from, client, options, cach
|
|
|
40584
40879
|
const targetRecords = [];
|
|
40585
40880
|
const seenTargetIds = /* @__PURE__ */ new Set();
|
|
40586
40881
|
let fetchedTargetCount = 0;
|
|
40587
|
-
for (const keys of splitChunks([...
|
|
40882
|
+
for (const keys of splitChunks([...sourceQueryByKey.values()], UPDATE_FROM_KEY_CHUNK_SIZE)) {
|
|
40588
40883
|
const keyQuery = `${from.targetJoinField} in (${keys.map(sqlQuote).join(",")})`;
|
|
40589
40884
|
const query = filterQuery ? `(${keyQuery}) and (${filterQuery})` : keyQuery;
|
|
40590
40885
|
const resolved = await fetchRecordsForSharedPlan(
|
|
@@ -40685,37 +40980,28 @@ function normalizeUpdateFromJoinKey(raw, kind, side) {
|
|
|
40685
40980
|
}
|
|
40686
40981
|
if (kind === "number" && side === "target" && raw === "") return null;
|
|
40687
40982
|
if (kind === "id") {
|
|
40688
|
-
const
|
|
40689
|
-
const id = Number(
|
|
40690
|
-
if (
|
|
40983
|
+
const text = raw.trim();
|
|
40984
|
+
const id = Number(text);
|
|
40985
|
+
if (text === "" || !Number.isSafeInteger(id) || id <= 0) {
|
|
40691
40986
|
throw new Error(`ArgumentError: UPDATE ... FROM ${side} key must be a positive safe integer: ${raw}`);
|
|
40692
40987
|
}
|
|
40693
40988
|
return String(id);
|
|
40694
40989
|
}
|
|
40695
|
-
const
|
|
40696
|
-
if (
|
|
40990
|
+
const decimal = parseExactDecimal(raw);
|
|
40991
|
+
if (decimal === null) {
|
|
40697
40992
|
throw new Error(`ArgumentError: UPDATE ... FROM ${side} key must be a finite decimal: ${raw}`);
|
|
40698
40993
|
}
|
|
40699
|
-
|
|
40700
|
-
let negative = false;
|
|
40701
|
-
if (unsigned.startsWith("-") || unsigned.startsWith("+")) {
|
|
40702
|
-
negative = unsigned[0] === "-";
|
|
40703
|
-
unsigned = unsigned.slice(1);
|
|
40704
|
-
}
|
|
40705
|
-
let [whole, fraction = ""] = unsigned.split(".");
|
|
40706
|
-
whole = (whole || "0").replace(/^0+(?=\d)/, "");
|
|
40707
|
-
fraction = fraction.replace(/0+$/, "");
|
|
40708
|
-
const zero = /^0*$/.test(whole) && fraction === "";
|
|
40709
|
-
const canonical = fraction === "" ? whole : `${whole}.${fraction}`;
|
|
40710
|
-
return negative && !zero ? `-${canonical}` : canonical;
|
|
40994
|
+
return JSON.stringify(decimal);
|
|
40711
40995
|
}
|
|
40712
40996
|
async function executeInsert(stmt, client, options, cacheContext) {
|
|
40713
40997
|
if (stmt.subtableCode) {
|
|
40714
40998
|
return executeInsertSubtable(stmt, client, options, cacheContext);
|
|
40715
40999
|
}
|
|
40716
|
-
await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
41000
|
+
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
41001
|
+
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
40717
41002
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
40718
41003
|
const batches = insertToPostBatches(stmt, fieldTypes);
|
|
41004
|
+
assertValidDmlRecords(batches.flatMap((batch) => batch.records), stmt.fields, fieldInfos, numberPrecision);
|
|
40719
41005
|
const createdIds = [];
|
|
40720
41006
|
for (const batch of batches) {
|
|
40721
41007
|
const res = await client.postRecords(batch);
|
|
@@ -40728,7 +41014,8 @@ async function executeInsert(stmt, client, options, cacheContext) {
|
|
|
40728
41014
|
};
|
|
40729
41015
|
}
|
|
40730
41016
|
async function executeInsertSelect(stmt, client, options, cacheContext, cteCache) {
|
|
40731
|
-
await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
41017
|
+
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
41018
|
+
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
40732
41019
|
const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
|
|
40733
41020
|
const { rows, columns } = selectResult;
|
|
40734
41021
|
if (columns.length !== stmt.fields.length) {
|
|
@@ -40750,6 +41037,7 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
|
|
|
40750
41037
|
});
|
|
40751
41038
|
return record2;
|
|
40752
41039
|
});
|
|
41040
|
+
assertValidDmlRecords(allRecords, stmt.fields, fieldInfos, numberPrecision);
|
|
40753
41041
|
const createdIds = [];
|
|
40754
41042
|
for (let i = 0; i < allRecords.length; i += 100) {
|
|
40755
41043
|
const batch = allRecords.slice(i, i + 100);
|
|
@@ -40767,12 +41055,20 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
|
40767
41055
|
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
40768
41056
|
return executeUpdateSubtable(stmt, client, options, cacheContext);
|
|
40769
41057
|
}
|
|
40770
|
-
await loadWritableTopLevelDmlFields(
|
|
41058
|
+
const fieldInfos = await loadWritableTopLevelDmlFields(
|
|
40771
41059
|
stmt.appId,
|
|
40772
41060
|
stmt.assignments.map((assignment) => assignment.field),
|
|
40773
41061
|
client,
|
|
40774
41062
|
cacheContext
|
|
40775
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
|
+
);
|
|
40776
41072
|
await assertDmlWhereCapability(stmt, client, cacheContext);
|
|
40777
41073
|
if (stmt.from != null) {
|
|
40778
41074
|
return executeUpdateFrom(stmt, stmt.from, client, options, cacheContext, tempTables);
|
|
@@ -40790,11 +41086,12 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
|
40790
41086
|
{ maxRecords: maxRecords2, parallel: options.fetchParallel ?? 1 }
|
|
40791
41087
|
);
|
|
40792
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);
|
|
40793
41091
|
if (options.confirm) {
|
|
40794
41092
|
const ok = await options.confirm(records.length, "UPDATE");
|
|
40795
41093
|
if (!ok) throw new OperationCancelledError("UPDATE", records.length);
|
|
40796
41094
|
}
|
|
40797
|
-
const batches2 = updateToPutBatchesArith(stmt, records, fieldTypes);
|
|
40798
41095
|
for (const batch of batches2) {
|
|
40799
41096
|
await client.putRecords(batch);
|
|
40800
41097
|
}
|
|
@@ -40808,11 +41105,12 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
|
40808
41105
|
{ maxRecords: maxRecords2, parallel: options.fetchParallel ?? 1 }
|
|
40809
41106
|
);
|
|
40810
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);
|
|
40811
41110
|
if (options.confirm) {
|
|
40812
41111
|
const ok = await options.confirm(ids.length, "UPDATE");
|
|
40813
41112
|
if (!ok) throw new OperationCancelledError("UPDATE", ids.length);
|
|
40814
41113
|
}
|
|
40815
|
-
const batches = updateToPutBatches(stmt, ids, fieldTypes);
|
|
40816
41114
|
for (const batch of batches) {
|
|
40817
41115
|
await client.putRecords(batch);
|
|
40818
41116
|
}
|
|
@@ -40820,12 +41118,16 @@ async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
|
|
|
40820
41118
|
}
|
|
40821
41119
|
async function executeUpdateFrom(stmt, from, client, options, cacheContext, tempTables) {
|
|
40822
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);
|
|
40823
41127
|
if (options.confirm) {
|
|
40824
41128
|
const ok = await options.confirm(matched.length, "UPDATE");
|
|
40825
41129
|
if (!ok) throw new OperationCancelledError("UPDATE", matched.length);
|
|
40826
41130
|
}
|
|
40827
|
-
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
40828
|
-
const batches = updateFromToPutBatches(stmt, matched, fieldTypes);
|
|
40829
41131
|
for (const batch of batches) await client.putRecords(batch);
|
|
40830
41132
|
return { type: "UPDATE", updatedCount: matched.length };
|
|
40831
41133
|
}
|
|
@@ -40874,7 +41176,8 @@ async function executeDelete(stmt, client, options, cacheContext) {
|
|
|
40874
41176
|
return { type: "DELETE", deletedCount: ids.length };
|
|
40875
41177
|
}
|
|
40876
41178
|
async function executeUpsert(stmt, client, options, cacheContext) {
|
|
40877
|
-
await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
41179
|
+
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
41180
|
+
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
40878
41181
|
const toInsert = [];
|
|
40879
41182
|
const toUpdate = [];
|
|
40880
41183
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
@@ -40883,7 +41186,7 @@ async function executeUpsert(stmt, client, options, cacheContext) {
|
|
|
40883
41186
|
const idx = stmt.fields.indexOf(key);
|
|
40884
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`);
|
|
40885
41188
|
const val = row[idx];
|
|
40886
|
-
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(",");
|
|
40887
41190
|
})
|
|
40888
41191
|
);
|
|
40889
41192
|
const targetIndex = await resolveUpsertTargets(stmt.appId, stmt.keyFields, rowKeyValues, client, options, fieldTypes);
|
|
@@ -40904,6 +41207,12 @@ async function executeUpsert(stmt, client, options, cacheContext) {
|
|
|
40904
41207
|
toInsert.push(record2);
|
|
40905
41208
|
}
|
|
40906
41209
|
});
|
|
41210
|
+
assertValidDmlRecords(
|
|
41211
|
+
[...toInsert, ...toUpdate.map((entry) => entry.record)],
|
|
41212
|
+
stmt.fields,
|
|
41213
|
+
fieldInfos,
|
|
41214
|
+
numberPrecision
|
|
41215
|
+
);
|
|
40907
41216
|
if (options.confirm && toInsert.length + toUpdate.length > 0) {
|
|
40908
41217
|
const total = toInsert.length + toUpdate.length;
|
|
40909
41218
|
const ok = await options.confirm(total, "UPDATE");
|
|
@@ -41179,14 +41488,14 @@ function buildSubtableReorderPutParams(appId, parentId, revision, subtableCode,
|
|
|
41179
41488
|
}
|
|
41180
41489
|
function evalAssignmentValueForSubtable(value, row, resolveFieldType) {
|
|
41181
41490
|
if (value.type === "STRING") return value.value;
|
|
41182
|
-
if (value.type === "NUMBER") return
|
|
41491
|
+
if (value.type === "NUMBER") return numberLiteralText(value);
|
|
41183
41492
|
if (value.type === "ARITH") return String(evalArithExpr(value, row));
|
|
41184
41493
|
if (value.type === "CASE_VALUE") return evalCaseWhen(value.expr, row, resolveFieldType);
|
|
41185
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`);
|
|
41186
41495
|
}
|
|
41187
41496
|
function valueToString(value) {
|
|
41188
41497
|
if (value.type === "STRING") return value.value;
|
|
41189
|
-
if (value.type === "NUMBER") return
|
|
41498
|
+
if (value.type === "NUMBER") return numberLiteralText(value);
|
|
41190
41499
|
if (value.type === "CASE_VALUE") return evalCaseWhen(value.expr, {});
|
|
41191
41500
|
return value.elements.map((e) => e.value).join(",");
|
|
41192
41501
|
}
|
|
@@ -41301,7 +41610,8 @@ function evalOrderKeyForRow(key, row) {
|
|
|
41301
41610
|
}
|
|
41302
41611
|
}
|
|
41303
41612
|
async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache) {
|
|
41304
|
-
await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
41613
|
+
const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
|
|
41614
|
+
const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
|
|
41305
41615
|
const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
|
|
41306
41616
|
const { rows, columns } = selectResult;
|
|
41307
41617
|
if (columns.length !== stmt.fields.length) {
|
|
@@ -41324,6 +41634,7 @@ async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache
|
|
|
41324
41634
|
});
|
|
41325
41635
|
return record2;
|
|
41326
41636
|
});
|
|
41637
|
+
assertValidDmlRecords(records, stmt.fields, fieldInfos, numberPrecision);
|
|
41327
41638
|
const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
|
|
41328
41639
|
const rowKeyValues = records.map(
|
|
41329
41640
|
(record2) => stmt.keyFields.map((key) => String(record2[key]?.value ?? ""))
|
|
@@ -42082,7 +42393,7 @@ function formatArithExprStr(expr) {
|
|
|
42082
42393
|
}
|
|
42083
42394
|
function formatArithNodeStr(node) {
|
|
42084
42395
|
if (node.type === "FIELD_REF") return node.field;
|
|
42085
|
-
if (node.type === "NUMBER") return
|
|
42396
|
+
if (node.type === "NUMBER") return numberLiteralText(node);
|
|
42086
42397
|
if (node.type === "ARITH") return `(${formatArithExprStr(node)})`;
|
|
42087
42398
|
return "...";
|
|
42088
42399
|
}
|
|
@@ -42544,6 +42855,7 @@ function withRequestGate(client, gate) {
|
|
|
42544
42855
|
},
|
|
42545
42856
|
getApps: () => gate.runReadOnly(() => client.getApps()),
|
|
42546
42857
|
getFields: (appId) => gate.runReadOnly(() => client.getFields(appId)),
|
|
42858
|
+
getNumberPrecision: (appId) => gate.runReadOnly(() => client.getNumberPrecision(appId)),
|
|
42547
42859
|
getProcessStatuses: (appId) => gate.runReadOnly(() => client.getProcessStatuses(appId)),
|
|
42548
42860
|
postRecords: (params) => gate.runMutation(() => client.postRecords(params)),
|
|
42549
42861
|
putRecords: (params) => gate.runMutation(() => client.putRecords(params)),
|
|
@@ -43105,6 +43417,16 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
|
43105
43417
|
);
|
|
43106
43418
|
return flattenFormFieldProperties(res.properties);
|
|
43107
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
|
+
},
|
|
43108
43430
|
async getProcessStatuses(appId) {
|
|
43109
43431
|
const qs = new URLSearchParams();
|
|
43110
43432
|
qs.set("app", String(appId));
|
|
@@ -43633,6 +43955,12 @@ async function createKsqlRuntime(serverOptions, input) {
|
|
|
43633
43955
|
if (!routed) throw new Error(`AuthError: profile "${binding.profile}" is not resolved for APP${appId}.`);
|
|
43634
43956
|
return routed.getFields(binding.appId);
|
|
43635
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
|
+
},
|
|
43636
43964
|
getProcessStatuses: (appId) => {
|
|
43637
43965
|
const binding = resolveRuntimeBinding(runtimeContext.sqlContext, appId);
|
|
43638
43966
|
const routed = runtimeContext.clientsByProfile.get(binding.profile);
|
|
@@ -43868,6 +44196,9 @@ function noOpClient() {
|
|
|
43868
44196
|
getFields: fail,
|
|
43869
44197
|
async getProcessStatuses() {
|
|
43870
44198
|
return { enable: false, states: [] };
|
|
44199
|
+
},
|
|
44200
|
+
async getNumberPrecision() {
|
|
44201
|
+
return { digits: 30, decimalPlaces: 10, roundingMode: "HALF_EVEN" };
|
|
43871
44202
|
}
|
|
43872
44203
|
};
|
|
43873
44204
|
}
|
|
@@ -44667,7 +44998,7 @@ Options:
|
|
|
44667
44998
|
-h, --help Show help
|
|
44668
44999
|
`);
|
|
44669
45000
|
}
|
|
44670
|
-
var SERVER_VERSION = true ? "3.
|
|
45001
|
+
var SERVER_VERSION = true ? "3.3.0" : "0.0.0-dev";
|
|
44671
45002
|
function createServer(args) {
|
|
44672
45003
|
const server = new McpServer({
|
|
44673
45004
|
name: "ksql-mcp",
|
|
@@ -44689,12 +45020,12 @@ function createServer(args) {
|
|
|
44689
45020
|
}, tools.explainTool);
|
|
44690
45021
|
server.registerTool("ksql_query", {
|
|
44691
45022
|
title: "Run read-only kSQL",
|
|
44692
|
-
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.",
|
|
44693
45024
|
inputSchema: queryInputShape
|
|
44694
45025
|
}, tools.queryTool);
|
|
44695
45026
|
server.registerTool("ksql_mutate", {
|
|
44696
45027
|
title: "Run mutating kSQL",
|
|
44697
|
-
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).",
|
|
44698
45029
|
inputSchema: mutateInputShape
|
|
44699
45030
|
}, tools.mutateTool);
|
|
44700
45031
|
server.registerTool("ksql_describe_app", {
|