@rex0220/kintone-sql-tools 2.4.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cli/ksql.js +392 -97
- package/dist-mcp/ksql-mcp.js +393 -98
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -3508,52 +3508,93 @@ function resolveFieldRef(row, field) {
|
|
|
3508
3508
|
}
|
|
3509
3509
|
|
|
3510
3510
|
// src/engine/evalWhere.ts
|
|
3511
|
-
function evalWhere(expr, row) {
|
|
3511
|
+
function evalWhere(expr, row, resolveFieldType) {
|
|
3512
3512
|
switch (expr.type) {
|
|
3513
3513
|
case "BINARY":
|
|
3514
|
-
return evalBinary(expr, row);
|
|
3514
|
+
return evalBinary(expr, row, resolveFieldType);
|
|
3515
3515
|
case "NULL_CHECK":
|
|
3516
3516
|
return evalNullCheck(expr, row);
|
|
3517
3517
|
case "LOGICAL":
|
|
3518
|
-
return evalLogical(expr, row);
|
|
3518
|
+
return evalLogical(expr, row, resolveFieldType);
|
|
3519
3519
|
case "NOT":
|
|
3520
|
-
return !evalWhere(expr.expr, row);
|
|
3520
|
+
return !evalWhere(expr.expr, row, resolveFieldType);
|
|
3521
3521
|
case "GROUP":
|
|
3522
|
-
return evalWhere(expr.expr, row);
|
|
3522
|
+
return evalWhere(expr.expr, row, resolveFieldType);
|
|
3523
3523
|
case "EXISTS": {
|
|
3524
3524
|
const exists = expr.resolved;
|
|
3525
3525
|
return expr.not ? !exists : exists;
|
|
3526
3526
|
}
|
|
3527
3527
|
}
|
|
3528
3528
|
}
|
|
3529
|
-
function evalBinary(expr, row) {
|
|
3530
|
-
const left = resolveField(expr.left, row);
|
|
3531
|
-
|
|
3529
|
+
function evalBinary(expr, row, resolveFieldType) {
|
|
3530
|
+
const left = resolveField(expr.left, row, resolveFieldType);
|
|
3531
|
+
const fieldType = expr.left.type === "FIELD" ? resolveFieldType?.(expr.left) : void 0;
|
|
3532
|
+
return evalOp(expr.op, left, expr.right, row, fieldType, resolveFieldType);
|
|
3532
3533
|
}
|
|
3533
|
-
function evalOp(op, leftStr, right, row) {
|
|
3534
|
+
function evalOp(op, leftStr, right, row, fieldType, resolveFieldType) {
|
|
3534
3535
|
if (op === "IN" || op === "NOT_IN") {
|
|
3536
|
+
let values = null;
|
|
3535
3537
|
if (right.type === "IN_LIST") {
|
|
3536
3538
|
assertResolvedInListValues2(right.values);
|
|
3537
|
-
|
|
3538
|
-
return op === "IN" ? contains : !contains;
|
|
3539
|
+
values = new Set(right.values.map((v) => String(v.value)));
|
|
3539
3540
|
}
|
|
3540
3541
|
if (right.type === "SUBQUERY_IN_LIST") {
|
|
3541
|
-
|
|
3542
|
-
return op === "IN" ? contains : !contains;
|
|
3542
|
+
values = right.resolved;
|
|
3543
3543
|
}
|
|
3544
|
-
return op === "NOT_IN";
|
|
3544
|
+
if (values === null) return op === "NOT_IN";
|
|
3545
|
+
const contains = typedInContains(leftStr, values, fieldType);
|
|
3546
|
+
return op === "IN" ? contains : !contains;
|
|
3545
3547
|
}
|
|
3546
3548
|
if (op === "LIKE") {
|
|
3547
|
-
const pattern = resolveValue(right, row);
|
|
3549
|
+
const pattern = resolveValue(right, row, resolveFieldType);
|
|
3548
3550
|
return matchLike(leftStr, pattern);
|
|
3549
3551
|
}
|
|
3550
3552
|
if (op === "NOT_LIKE") {
|
|
3551
|
-
const pattern = resolveValue(right, row);
|
|
3553
|
+
const pattern = resolveValue(right, row, resolveFieldType);
|
|
3552
3554
|
return !matchLike(leftStr, pattern);
|
|
3553
3555
|
}
|
|
3554
|
-
const rightStr = resolveValue(right, row);
|
|
3556
|
+
const rightStr = resolveValue(right, row, resolveFieldType);
|
|
3555
3557
|
return compareScalarValues(op, leftStr, rightStr);
|
|
3556
3558
|
}
|
|
3559
|
+
var STRING_ARRAY_FIELD_TYPES = /* @__PURE__ */ new Set(["CHECK_BOX", "MULTI_SELECT"]);
|
|
3560
|
+
var OBJECT_ARRAY_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
3561
|
+
"USER_SELECT",
|
|
3562
|
+
"ORGANIZATION_SELECT",
|
|
3563
|
+
"GROUP_SELECT",
|
|
3564
|
+
"STATUS_ASSIGNEE"
|
|
3565
|
+
]);
|
|
3566
|
+
var SINGLE_OBJECT_FIELD_TYPES = /* @__PURE__ */ new Set(["CREATOR", "MODIFIER"]);
|
|
3567
|
+
function typedInContains(leftStr, values, fieldType) {
|
|
3568
|
+
const fallback = () => values.has(leftStr);
|
|
3569
|
+
if (fieldType === void 0) return fallback();
|
|
3570
|
+
let parsed;
|
|
3571
|
+
if (STRING_ARRAY_FIELD_TYPES.has(fieldType) || OBJECT_ARRAY_FIELD_TYPES.has(fieldType) || SINGLE_OBJECT_FIELD_TYPES.has(fieldType)) {
|
|
3572
|
+
try {
|
|
3573
|
+
parsed = JSON.parse(leftStr);
|
|
3574
|
+
} catch {
|
|
3575
|
+
return fallback();
|
|
3576
|
+
}
|
|
3577
|
+
} else {
|
|
3578
|
+
return fallback();
|
|
3579
|
+
}
|
|
3580
|
+
if (STRING_ARRAY_FIELD_TYPES.has(fieldType)) {
|
|
3581
|
+
if (!Array.isArray(parsed) || !parsed.every((item) => typeof item === "string")) {
|
|
3582
|
+
return fallback();
|
|
3583
|
+
}
|
|
3584
|
+
if (parsed.length === 0 && values.has("")) return true;
|
|
3585
|
+
return parsed.some((item) => values.has(item));
|
|
3586
|
+
}
|
|
3587
|
+
if (OBJECT_ARRAY_FIELD_TYPES.has(fieldType)) {
|
|
3588
|
+
if (!Array.isArray(parsed) || !parsed.every(hasStringCode)) return fallback();
|
|
3589
|
+
if (parsed.length === 0 && values.has("")) return true;
|
|
3590
|
+
return parsed.some((item) => values.has(item.code));
|
|
3591
|
+
}
|
|
3592
|
+
if (!hasStringCode(parsed)) return fallback();
|
|
3593
|
+
return values.has(parsed.code);
|
|
3594
|
+
}
|
|
3595
|
+
function hasStringCode(value) {
|
|
3596
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && typeof value.code === "string";
|
|
3597
|
+
}
|
|
3557
3598
|
function assertResolvedInListValues2(values) {
|
|
3558
3599
|
const unresolved = values.find((item) => item.type === "VARIABLE");
|
|
3559
3600
|
if (unresolved?.type === "VARIABLE") {
|
|
@@ -3564,20 +3605,20 @@ function evalNullCheck(expr, row) {
|
|
|
3564
3605
|
const val = resolveField(expr.field, row);
|
|
3565
3606
|
return expr.not ? val !== "" : val === "";
|
|
3566
3607
|
}
|
|
3567
|
-
function evalLogical(expr, row) {
|
|
3608
|
+
function evalLogical(expr, row, resolveFieldType) {
|
|
3568
3609
|
if (expr.op === "AND") {
|
|
3569
|
-
return evalWhere(expr.left, row) && evalWhere(expr.right, row);
|
|
3610
|
+
return evalWhere(expr.left, row, resolveFieldType) && evalWhere(expr.right, row, resolveFieldType);
|
|
3570
3611
|
}
|
|
3571
|
-
return evalWhere(expr.left, row) || evalWhere(expr.right, row);
|
|
3612
|
+
return evalWhere(expr.left, row, resolveFieldType) || evalWhere(expr.right, row, resolveFieldType);
|
|
3572
3613
|
}
|
|
3573
|
-
function resolveField(field, row) {
|
|
3614
|
+
function resolveField(field, row, resolveFieldType) {
|
|
3574
3615
|
if (field.type === "FUNC_FIELD") return evalStringFunc(field.expr, row);
|
|
3575
3616
|
if (field.type === "ARITH_FIELD") return String(evalArithExpr(field.expr, row));
|
|
3576
|
-
if (field.type === "CASE_FIELD") return evalCaseWhen(field.expr, row);
|
|
3617
|
+
if (field.type === "CASE_FIELD") return evalCaseWhen(field.expr, row, resolveFieldType);
|
|
3577
3618
|
const key = field.tableAlias ? `${field.tableAlias}.${field.field}` : field.field;
|
|
3578
3619
|
return resolveFieldRef(row, key);
|
|
3579
3620
|
}
|
|
3580
|
-
function resolveValue(value, row) {
|
|
3621
|
+
function resolveValue(value, row, resolveFieldType) {
|
|
3581
3622
|
switch (value.type) {
|
|
3582
3623
|
case "VARIABLE":
|
|
3583
3624
|
throw new Error(`ParseError: unresolved batch variable @${value.name}.`);
|
|
@@ -3600,14 +3641,14 @@ function resolveValue(value, row) {
|
|
|
3600
3641
|
if (value.expr.type === "STRING_FUNC") return evalStringFunc(value.expr, row);
|
|
3601
3642
|
return String(evalArithExpr(value.expr, row));
|
|
3602
3643
|
case "CASE_VALUE":
|
|
3603
|
-
return evalCaseWhen(value.expr, row);
|
|
3644
|
+
return evalCaseWhen(value.expr, row, resolveFieldType);
|
|
3604
3645
|
case "ARRAY":
|
|
3605
3646
|
return value.elements.map((e) => e.value).join(",");
|
|
3606
3647
|
}
|
|
3607
3648
|
}
|
|
3608
|
-
function evalCaseWhen(expr, row) {
|
|
3649
|
+
function evalCaseWhen(expr, row, resolveFieldType) {
|
|
3609
3650
|
for (const branch of expr.branches) {
|
|
3610
|
-
if (evalWhere(branch.condition, row)) {
|
|
3651
|
+
if (evalWhere(branch.condition, row, resolveFieldType)) {
|
|
3611
3652
|
return evalCaseResult(branch.result, row);
|
|
3612
3653
|
}
|
|
3613
3654
|
}
|
|
@@ -4158,8 +4199,11 @@ async function resolveDmlTargetIds(getRecords, app, query, options) {
|
|
|
4158
4199
|
function extractSafePushdownLeaves(where, options = {}) {
|
|
4159
4200
|
return extractAndLeaves(where, (expr) => isSafeComparison(expr, options));
|
|
4160
4201
|
}
|
|
4161
|
-
function
|
|
4162
|
-
return extractAndLeaves(
|
|
4202
|
+
function extractTypedPushdownCandidates(where, options = {}) {
|
|
4203
|
+
return extractAndLeaves(
|
|
4204
|
+
where,
|
|
4205
|
+
(expr) => isNumericCandidate(expr, options) || isSelectionInCandidate(expr, options)
|
|
4206
|
+
);
|
|
4163
4207
|
}
|
|
4164
4208
|
function extractAndLeaves(where, accept) {
|
|
4165
4209
|
switch (where.type) {
|
|
@@ -4183,8 +4227,27 @@ function extractAndLeaves(where, accept) {
|
|
|
4183
4227
|
}
|
|
4184
4228
|
function isSafeComparison(expr, options) {
|
|
4185
4229
|
if (isSafeIdComparison(expr, options)) return true;
|
|
4186
|
-
if (
|
|
4187
|
-
|
|
4230
|
+
if (isNumericCandidate(expr, options)) {
|
|
4231
|
+
return options.fieldTypes?.get(expr.left.field) === "NUMBER";
|
|
4232
|
+
}
|
|
4233
|
+
return isSelectionInComparison(expr, options);
|
|
4234
|
+
}
|
|
4235
|
+
var SELECTION_IN_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
4236
|
+
"DROP_DOWN",
|
|
4237
|
+
"RADIO_BUTTON",
|
|
4238
|
+
"CHECK_BOX",
|
|
4239
|
+
"MULTI_SELECT"
|
|
4240
|
+
]);
|
|
4241
|
+
function isSelectionInComparison(expr, options) {
|
|
4242
|
+
if (!isSelectionInCandidate(expr, options)) return false;
|
|
4243
|
+
if (expr.left.type !== "FIELD" || expr.right.type !== "IN_LIST") return false;
|
|
4244
|
+
const fieldType = options.fieldTypes?.get(expr.left.field);
|
|
4245
|
+
if (fieldType === void 0 || !SELECTION_IN_FIELD_TYPES.has(fieldType)) return false;
|
|
4246
|
+
const validOptions = options.fieldOptions?.get(expr.left.field);
|
|
4247
|
+
if (validOptions === void 0) return false;
|
|
4248
|
+
return expr.right.values.every(
|
|
4249
|
+
(value) => value.type === "STRING" && value.value !== "" && validOptions.has(value.value)
|
|
4250
|
+
);
|
|
4188
4251
|
}
|
|
4189
4252
|
function isSafeIdComparison(expr, options) {
|
|
4190
4253
|
if (!isTargetIdField(expr.left, options)) return false;
|
|
@@ -4204,6 +4267,13 @@ function isNumericCandidate(expr, options) {
|
|
|
4204
4267
|
if (expr.op === "=") return true;
|
|
4205
4268
|
return (expr.op === "<" || expr.op === ">") && Number.isSafeInteger(expr.right.value);
|
|
4206
4269
|
}
|
|
4270
|
+
function isSelectionInCandidate(expr, options) {
|
|
4271
|
+
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
4272
|
+
if (!isTargetField(expr.left, options)) return false;
|
|
4273
|
+
if (expr.op !== "IN" && expr.op !== "NOT_IN") return false;
|
|
4274
|
+
if (expr.right.type !== "IN_LIST" || expr.right.values.length === 0) return false;
|
|
4275
|
+
return expr.right.values.every((value) => value.type === "STRING" && value.value !== "");
|
|
4276
|
+
}
|
|
4207
4277
|
function isTargetField(field, options) {
|
|
4208
4278
|
const targetAlias = options.tableAlias ?? null;
|
|
4209
4279
|
if (field.tableAlias === targetAlias) return true;
|
|
@@ -4215,7 +4285,7 @@ function flatten(record, alias) {
|
|
|
4215
4285
|
const row = {};
|
|
4216
4286
|
for (const [field, fv] of Object.entries(record)) {
|
|
4217
4287
|
const val = fv.value;
|
|
4218
|
-
const strVal = typeof val === "string" ? val : JSON.stringify(val
|
|
4288
|
+
const strVal = val == null ? "" : typeof val === "string" ? val : JSON.stringify(val);
|
|
4219
4289
|
if (alias) {
|
|
4220
4290
|
row[`${alias}.${field}`] = strVal;
|
|
4221
4291
|
row[field] = strVal;
|
|
@@ -4274,9 +4344,9 @@ function applyJoin(leftRows, rightRows, join2) {
|
|
|
4274
4344
|
}
|
|
4275
4345
|
return result;
|
|
4276
4346
|
}
|
|
4277
|
-
function applyFilter(rows, where) {
|
|
4347
|
+
function applyFilter(rows, where, resolveFieldType) {
|
|
4278
4348
|
if (where === null) return rows;
|
|
4279
|
-
return rows.filter((row) => evalWhere(where, row));
|
|
4349
|
+
return rows.filter((row) => evalWhere(where, row, resolveFieldType));
|
|
4280
4350
|
}
|
|
4281
4351
|
function hasAggregateColumns(columns) {
|
|
4282
4352
|
return columns.some(
|
|
@@ -4402,9 +4472,9 @@ function aggregateSyntheticName2(func, distinct, arg) {
|
|
|
4402
4472
|
const argStr = aggregateArgLabel(arg);
|
|
4403
4473
|
return distinct ? `${func}(DISTINCT ${argStr})` : `${func}(${argStr})`;
|
|
4404
4474
|
}
|
|
4405
|
-
function applyHaving(rows, having) {
|
|
4475
|
+
function applyHaving(rows, having, resolveFieldType) {
|
|
4406
4476
|
if (having === null) return rows;
|
|
4407
|
-
return rows.filter((row) => evalWhere(having, row));
|
|
4477
|
+
return rows.filter((row) => evalWhere(having, row, resolveFieldType));
|
|
4408
4478
|
}
|
|
4409
4479
|
function applyDistinct(rows, columns) {
|
|
4410
4480
|
if (rows.length === 0) return rows;
|
|
@@ -4529,7 +4599,7 @@ function applyLimit(rows, limit, offset) {
|
|
|
4529
4599
|
if (limit === null) return rows.slice(start);
|
|
4530
4600
|
return rows.slice(start, start + limit);
|
|
4531
4601
|
}
|
|
4532
|
-
function project(rows, columns, scalarCache) {
|
|
4602
|
+
function project(rows, columns, scalarCache, resolveFieldType) {
|
|
4533
4603
|
if (columns.length === 1 && columns[0].type === "WILDCARD") {
|
|
4534
4604
|
const projected2 = rows.map((row) => stripParentShortcutColumns(row));
|
|
4535
4605
|
const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [];
|
|
@@ -4590,7 +4660,7 @@ function project(rows, columns, scalarCache) {
|
|
|
4590
4660
|
}
|
|
4591
4661
|
case "CASE_COL": {
|
|
4592
4662
|
const key = outputKeys?.[colIdx] ?? col.alias ?? "case";
|
|
4593
|
-
out[key] = evalCaseWhen(col.expr, row);
|
|
4663
|
+
out[key] = evalCaseWhen(col.expr, row, resolveFieldType);
|
|
4594
4664
|
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
4595
4665
|
break;
|
|
4596
4666
|
}
|
|
@@ -4726,7 +4796,15 @@ function resolveAggInStringFuncExpr(expr, rows) {
|
|
|
4726
4796
|
};
|
|
4727
4797
|
}
|
|
4728
4798
|
function runFullScan(input) {
|
|
4729
|
-
const {
|
|
4799
|
+
const {
|
|
4800
|
+
stmt,
|
|
4801
|
+
tables,
|
|
4802
|
+
scalarCache,
|
|
4803
|
+
optionOrders,
|
|
4804
|
+
sortKinds,
|
|
4805
|
+
fieldTypeResolver,
|
|
4806
|
+
havingFieldTypeResolver
|
|
4807
|
+
} = input;
|
|
4730
4808
|
let rows = [];
|
|
4731
4809
|
const mainAlias = stmt.from.alias;
|
|
4732
4810
|
const mainRecords = tables.get(mainAlias) ?? tables.get(null) ?? [];
|
|
@@ -4737,17 +4815,17 @@ function runFullScan(input) {
|
|
|
4737
4815
|
const rightRows = rightRecords.map((r) => flatten(r, rightAlias));
|
|
4738
4816
|
rows = applyJoin(rows, rightRows, join2);
|
|
4739
4817
|
}
|
|
4740
|
-
rows = applyFilter(rows, stmt.where);
|
|
4818
|
+
rows = applyFilter(rows, stmt.where, fieldTypeResolver);
|
|
4741
4819
|
if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
|
|
4742
4820
|
rows = applyGroupBy(rows, stmt.groupBy, stmt.columns);
|
|
4743
4821
|
}
|
|
4744
|
-
rows = applyHaving(rows, stmt.having);
|
|
4822
|
+
rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
|
|
4745
4823
|
if (stmt.distinct) {
|
|
4746
4824
|
rows = applyDistinct(rows, stmt.columns);
|
|
4747
4825
|
}
|
|
4748
4826
|
rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds);
|
|
4749
4827
|
rows = applyLimit(rows, stmt.limit, stmt.offset);
|
|
4750
|
-
return project(rows, stmt.columns, scalarCache);
|
|
4828
|
+
return project(rows, stmt.columns, scalarCache, fieldTypeResolver);
|
|
4751
4829
|
}
|
|
4752
4830
|
|
|
4753
4831
|
// src/converter/subtableAdapter.ts
|
|
@@ -5280,6 +5358,7 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache) {
|
|
|
5280
5358
|
if (isNoFromSelect(stmt)) {
|
|
5281
5359
|
return executeNoFromSelect(stmt);
|
|
5282
5360
|
}
|
|
5361
|
+
await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache);
|
|
5283
5362
|
const mode = resolveSelectMode(stmt);
|
|
5284
5363
|
await validateSelectFieldCodes(stmt, mode, client, cacheContext);
|
|
5285
5364
|
if (mode === "SIMPLE") {
|
|
@@ -5338,6 +5417,8 @@ function executeNoFromSelect(stmt) {
|
|
|
5338
5417
|
}
|
|
5339
5418
|
async function executeSimpleSelect(stmt, client, options, cacheContext) {
|
|
5340
5419
|
const params = selectToKintoneParams(stmt);
|
|
5420
|
+
const typedInFieldTypes = await loadTypedInFieldTypes(stmt, client, cacheContext);
|
|
5421
|
+
const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
|
|
5341
5422
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
5342
5423
|
const warnings = /* @__PURE__ */ new Set();
|
|
5343
5424
|
const onLimit = options.onLimitReached ?? "error";
|
|
@@ -5374,7 +5455,12 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
|
|
|
5374
5455
|
rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds);
|
|
5375
5456
|
rows = applyLimit(rows, stmt.limit, stmt.offset);
|
|
5376
5457
|
}
|
|
5377
|
-
const { rows: projected, columns } = project(
|
|
5458
|
+
const { rows: projected, columns } = project(
|
|
5459
|
+
rows,
|
|
5460
|
+
stmt.columns,
|
|
5461
|
+
void 0,
|
|
5462
|
+
fieldTypeResolvers.row
|
|
5463
|
+
);
|
|
5378
5464
|
return { type: "SELECT", rows: projected, columns, rowCount: projected.length, warnings: [...warnings] };
|
|
5379
5465
|
}
|
|
5380
5466
|
async function validateSelectFieldCodes(stmt, mode, client, cacheContext) {
|
|
@@ -5409,44 +5495,160 @@ async function validateSelectFieldCodes(stmt, mode, client, cacheContext) {
|
|
|
5409
5495
|
}
|
|
5410
5496
|
}
|
|
5411
5497
|
}
|
|
5412
|
-
function extractMainSafePushdown(stmt, fieldTypes) {
|
|
5498
|
+
function extractMainSafePushdown(stmt, fieldTypes, fieldOptions) {
|
|
5413
5499
|
if (stmt.where === null || stmt.from.subtableCode || stmt.from.cteName !== null) return null;
|
|
5414
5500
|
if (stmt.joins.length === 0) {
|
|
5415
5501
|
return extractSafePushdownLeaves(stmt.where, {
|
|
5416
5502
|
tableAlias: stmt.from.alias ?? void 0,
|
|
5417
5503
|
allowUnqualifiedFields: true,
|
|
5418
|
-
fieldTypes
|
|
5504
|
+
fieldTypes,
|
|
5505
|
+
fieldOptions
|
|
5419
5506
|
});
|
|
5420
5507
|
}
|
|
5421
5508
|
if (!stmt.from.alias) return null;
|
|
5422
|
-
return extractSafePushdownLeaves(stmt.where, {
|
|
5509
|
+
return extractSafePushdownLeaves(stmt.where, {
|
|
5510
|
+
tableAlias: stmt.from.alias,
|
|
5511
|
+
fieldTypes,
|
|
5512
|
+
fieldOptions
|
|
5513
|
+
});
|
|
5423
5514
|
}
|
|
5424
|
-
function
|
|
5515
|
+
function extractMainTypedPushdownCandidate(stmt) {
|
|
5425
5516
|
if (stmt.where === null || stmt.from.subtableCode || stmt.from.cteName !== null) return null;
|
|
5426
5517
|
if (stmt.joins.length === 0) {
|
|
5427
|
-
return
|
|
5518
|
+
return extractTypedPushdownCandidates(stmt.where, {
|
|
5428
5519
|
tableAlias: stmt.from.alias ?? void 0,
|
|
5429
5520
|
allowUnqualifiedFields: true
|
|
5430
5521
|
});
|
|
5431
5522
|
}
|
|
5432
5523
|
if (!stmt.from.alias) return null;
|
|
5433
|
-
return
|
|
5524
|
+
return extractTypedPushdownCandidates(stmt.where, { tableAlias: stmt.from.alias });
|
|
5434
5525
|
}
|
|
5435
|
-
async function
|
|
5526
|
+
async function loadTypedPushdownMeta(stmt, client, cacheContext) {
|
|
5436
5527
|
const appIds = /* @__PURE__ */ new Set();
|
|
5437
|
-
if (
|
|
5528
|
+
if (extractMainTypedPushdownCandidate(stmt) !== null) appIds.add(stmt.from.appId);
|
|
5438
5529
|
if (stmt.where !== null) {
|
|
5439
5530
|
for (const join2 of stmt.joins) {
|
|
5440
5531
|
if (!join2.table.alias || join2.table.subtableCode || join2.table.cteName !== null) continue;
|
|
5441
|
-
const candidate =
|
|
5532
|
+
const candidate = extractTypedPushdownCandidates(stmt.where, {
|
|
5442
5533
|
tableAlias: join2.table.alias
|
|
5443
5534
|
});
|
|
5444
5535
|
if (candidate !== null) appIds.add(join2.table.appId);
|
|
5445
5536
|
}
|
|
5446
5537
|
}
|
|
5538
|
+
const entries = await Promise.all([...appIds].map(async (appId) => {
|
|
5539
|
+
const [fieldTypes, fieldOptions] = await Promise.all([
|
|
5540
|
+
getFieldTypeMap(appId, client, cacheContext),
|
|
5541
|
+
getFieldOptionSetMapByApp(appId, client, cacheContext)
|
|
5542
|
+
]);
|
|
5543
|
+
return [appId, fieldTypes, fieldOptions];
|
|
5544
|
+
}));
|
|
5545
|
+
return {
|
|
5546
|
+
fieldTypesByApp: new Map(entries.map(([appId, fieldTypes]) => [appId, fieldTypes])),
|
|
5547
|
+
fieldOptionsByApp: new Map(entries.map(([appId, , fieldOptions]) => [appId, fieldOptions]))
|
|
5548
|
+
};
|
|
5549
|
+
}
|
|
5550
|
+
function collectTypedInFieldRefs(expr, out) {
|
|
5551
|
+
if (expr === null) return;
|
|
5552
|
+
switch (expr.type) {
|
|
5553
|
+
case "BINARY":
|
|
5554
|
+
if ((expr.op === "IN" || expr.op === "NOT_IN") && expr.left.type === "FIELD") {
|
|
5555
|
+
out.push(expr.left);
|
|
5556
|
+
}
|
|
5557
|
+
if (expr.left.type === "CASE_FIELD") collectCaseTypedInFieldRefs(expr.left.expr, out);
|
|
5558
|
+
if (expr.right.type === "CASE_VALUE") collectCaseTypedInFieldRefs(expr.right.expr, out);
|
|
5559
|
+
return;
|
|
5560
|
+
case "LOGICAL":
|
|
5561
|
+
collectTypedInFieldRefs(expr.left, out);
|
|
5562
|
+
collectTypedInFieldRefs(expr.right, out);
|
|
5563
|
+
return;
|
|
5564
|
+
case "NOT":
|
|
5565
|
+
case "GROUP":
|
|
5566
|
+
collectTypedInFieldRefs(expr.expr, out);
|
|
5567
|
+
return;
|
|
5568
|
+
case "NULL_CHECK":
|
|
5569
|
+
case "EXISTS":
|
|
5570
|
+
return;
|
|
5571
|
+
}
|
|
5572
|
+
}
|
|
5573
|
+
function collectCaseTypedInFieldRefs(expr, out) {
|
|
5574
|
+
for (const branch of expr.branches) collectTypedInFieldRefs(branch.condition, out);
|
|
5575
|
+
}
|
|
5576
|
+
function collectSelectTypedInFieldRefs(stmt) {
|
|
5577
|
+
const refs = [];
|
|
5578
|
+
collectTypedInFieldRefs(stmt.where, refs);
|
|
5579
|
+
collectTypedInFieldRefs(stmt.having, refs);
|
|
5580
|
+
for (const column of stmt.columns) {
|
|
5581
|
+
if (column.type === "CASE_COL") collectCaseTypedInFieldRefs(column.expr, refs);
|
|
5582
|
+
}
|
|
5583
|
+
return refs;
|
|
5584
|
+
}
|
|
5585
|
+
function findTableForAlias(stmt, alias) {
|
|
5586
|
+
return [stmt.from, ...stmt.joins.map((join2) => join2.table)].find((table) => table.alias === alias);
|
|
5587
|
+
}
|
|
5588
|
+
function physicalSelectTables(stmt) {
|
|
5589
|
+
return [stmt.from, ...stmt.joins.map((join2) => join2.table)].filter((table) => table.cteName === null);
|
|
5590
|
+
}
|
|
5591
|
+
async function loadTypedInFieldTypes(stmt, client, cacheContext) {
|
|
5592
|
+
const refs = collectSelectTypedInFieldRefs(stmt);
|
|
5593
|
+
if (refs.length === 0) return /* @__PURE__ */ new Map();
|
|
5594
|
+
const appIds = /* @__PURE__ */ new Set();
|
|
5595
|
+
const physicalTables = physicalSelectTables(stmt);
|
|
5596
|
+
for (const ref of refs) {
|
|
5597
|
+
if (ref.tableAlias !== null) {
|
|
5598
|
+
if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
|
|
5599
|
+
appIds.add(stmt.from.appId);
|
|
5600
|
+
continue;
|
|
5601
|
+
}
|
|
5602
|
+
const table = findTableForAlias(stmt, ref.tableAlias);
|
|
5603
|
+
if (table && table.cteName === null) appIds.add(table.appId);
|
|
5604
|
+
continue;
|
|
5605
|
+
}
|
|
5606
|
+
if (stmt.joins.length === 0) {
|
|
5607
|
+
if (stmt.from.cteName === null) appIds.add(stmt.from.appId);
|
|
5608
|
+
continue;
|
|
5609
|
+
}
|
|
5610
|
+
for (const table of physicalTables) appIds.add(table.appId);
|
|
5611
|
+
}
|
|
5447
5612
|
const entries = await Promise.all([...appIds].map(async (appId) => [appId, await getFieldTypeMap(appId, client, cacheContext)]));
|
|
5448
5613
|
return new Map(entries);
|
|
5449
5614
|
}
|
|
5615
|
+
function fieldCodeForTypeLookup(table, field) {
|
|
5616
|
+
if (table.subtableCode && field.startsWith("_p.")) return field.slice(3);
|
|
5617
|
+
return field;
|
|
5618
|
+
}
|
|
5619
|
+
function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
|
|
5620
|
+
const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
5621
|
+
const physicalTables = tables.filter((table) => table.cteName === null);
|
|
5622
|
+
const outputAliases = new Set(
|
|
5623
|
+
stmt.columns.map((column) => "alias" in column ? column.alias : null).filter((alias) => alias !== null)
|
|
5624
|
+
);
|
|
5625
|
+
const row = (field) => {
|
|
5626
|
+
if (field.tableAlias !== null) {
|
|
5627
|
+
if (field.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
|
|
5628
|
+
return fieldTypesByApp.get(stmt.from.appId)?.get(field.field);
|
|
5629
|
+
}
|
|
5630
|
+
const table2 = tables.find((candidate) => candidate.alias === field.tableAlias);
|
|
5631
|
+
if (!table2 || table2.cteName !== null) return void 0;
|
|
5632
|
+
return fieldTypesByApp.get(table2.appId)?.get(fieldCodeForTypeLookup(table2, field.field));
|
|
5633
|
+
}
|
|
5634
|
+
if (stmt.joins.length === 0) {
|
|
5635
|
+
if (stmt.from.cteName !== null) return void 0;
|
|
5636
|
+
return fieldTypesByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, field.field));
|
|
5637
|
+
}
|
|
5638
|
+
if (tables.some((table2) => table2.cteName !== null)) return void 0;
|
|
5639
|
+
const matches = physicalTables.filter(
|
|
5640
|
+
(table2) => fieldTypesByApp.get(table2.appId)?.has(fieldCodeForTypeLookup(table2, field.field))
|
|
5641
|
+
);
|
|
5642
|
+
if (matches.length !== 1) return void 0;
|
|
5643
|
+
const table = matches[0];
|
|
5644
|
+
return fieldTypesByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, field.field));
|
|
5645
|
+
};
|
|
5646
|
+
const having = (field) => {
|
|
5647
|
+
if (field.tableAlias === null && outputAliases.has(field.field)) return void 0;
|
|
5648
|
+
return row(field);
|
|
5649
|
+
};
|
|
5650
|
+
return { row, having };
|
|
5651
|
+
}
|
|
5450
5652
|
async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache) {
|
|
5451
5653
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
5452
5654
|
const warnings = /* @__PURE__ */ new Set();
|
|
@@ -5455,10 +5657,15 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
5455
5657
|
resolveSubqueries(stmt.where, client, options, cacheContext, cteCache),
|
|
5456
5658
|
resolveSubqueries(stmt.having, client, options, cacheContext, cteCache)
|
|
5457
5659
|
]);
|
|
5458
|
-
const
|
|
5660
|
+
const [pushdownMeta, typedInFieldTypes] = await Promise.all([
|
|
5661
|
+
loadTypedPushdownMeta(stmt, client, cacheContext),
|
|
5662
|
+
loadTypedInFieldTypes(stmt, client, cacheContext)
|
|
5663
|
+
]);
|
|
5664
|
+
const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
|
|
5459
5665
|
const mainPushDown = extractMainSafePushdown(
|
|
5460
5666
|
stmt,
|
|
5461
|
-
|
|
5667
|
+
pushdownMeta.fieldTypesByApp.get(stmt.from.appId),
|
|
5668
|
+
pushdownMeta.fieldOptionsByApp.get(stmt.from.appId)
|
|
5462
5669
|
);
|
|
5463
5670
|
const tableConditions = /* @__PURE__ */ new Map();
|
|
5464
5671
|
if (stmt.where !== null) {
|
|
@@ -5466,7 +5673,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
5466
5673
|
if (!join2.table.alias || join2.table.subtableCode || join2.table.cteName !== null) continue;
|
|
5467
5674
|
const cond = extractSafePushdownLeaves(stmt.where, {
|
|
5468
5675
|
tableAlias: join2.table.alias,
|
|
5469
|
-
fieldTypes:
|
|
5676
|
+
fieldTypes: pushdownMeta.fieldTypesByApp.get(join2.table.appId),
|
|
5677
|
+
fieldOptions: pushdownMeta.fieldOptionsByApp.get(join2.table.appId)
|
|
5470
5678
|
});
|
|
5471
5679
|
if (cond) tableConditions.set(join2.table.alias, cond);
|
|
5472
5680
|
}
|
|
@@ -5544,7 +5752,15 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
5544
5752
|
}));
|
|
5545
5753
|
const scalarCache = await scalarCachePromise;
|
|
5546
5754
|
const { optionOrders, sortKinds } = await orderByMetaPromise;
|
|
5547
|
-
const { rows, columns } = runFullScan({
|
|
5755
|
+
const { rows, columns } = runFullScan({
|
|
5756
|
+
tables,
|
|
5757
|
+
stmt,
|
|
5758
|
+
scalarCache,
|
|
5759
|
+
optionOrders,
|
|
5760
|
+
sortKinds,
|
|
5761
|
+
fieldTypeResolver: fieldTypeResolvers.row,
|
|
5762
|
+
havingFieldTypeResolver: fieldTypeResolvers.having
|
|
5763
|
+
});
|
|
5548
5764
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
5549
5765
|
}
|
|
5550
5766
|
async function executeUnion(stmt, client, options, cacheContext) {
|
|
@@ -5700,8 +5916,11 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
5700
5916
|
const parallel = options.fetchParallel ?? 1;
|
|
5701
5917
|
await Promise.all([
|
|
5702
5918
|
resolveSubqueries(stmt.where, client, options, cacheContext, cteCache),
|
|
5703
|
-
resolveSubqueries(stmt.having, client, options, cacheContext, cteCache)
|
|
5919
|
+
resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
|
|
5920
|
+
resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
|
|
5704
5921
|
]);
|
|
5922
|
+
const typedInFieldTypes = await loadTypedInFieldTypes(stmt, client, cacheContext);
|
|
5923
|
+
const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
|
|
5705
5924
|
const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
5706
5925
|
const orderByMetaPromise = buildOrderByMetaForSelect(stmt, client, cacheContext);
|
|
5707
5926
|
scalarCachePromise.catch(() => {
|
|
@@ -5756,7 +5975,15 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
5756
5975
|
await Promise.all(joinFetches);
|
|
5757
5976
|
const scalarCache = await scalarCachePromise;
|
|
5758
5977
|
const { optionOrders, sortKinds } = await orderByMetaPromise;
|
|
5759
|
-
const { rows, columns } = runFullScan({
|
|
5978
|
+
const { rows, columns } = runFullScan({
|
|
5979
|
+
tables,
|
|
5980
|
+
stmt,
|
|
5981
|
+
scalarCache,
|
|
5982
|
+
optionOrders,
|
|
5983
|
+
sortKinds,
|
|
5984
|
+
fieldTypeResolver: fieldTypeResolvers.row,
|
|
5985
|
+
havingFieldTypeResolver: fieldTypeResolvers.having
|
|
5986
|
+
});
|
|
5760
5987
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
5761
5988
|
}
|
|
5762
5989
|
function processRowToKintoneRecord(row) {
|
|
@@ -5988,6 +6215,15 @@ async function getOptionOrderMapByApp(appId, client, cacheContext) {
|
|
|
5988
6215
|
setScopedCacheValue(optionOrderCache, cacheContext, appId, map);
|
|
5989
6216
|
return map;
|
|
5990
6217
|
}
|
|
6218
|
+
async function getFieldOptionSetMapByApp(appId, client, cacheContext) {
|
|
6219
|
+
const optionOrders = await getOptionOrderMapByApp(appId, client, cacheContext);
|
|
6220
|
+
return new Map(
|
|
6221
|
+
[...optionOrders.entries()].map(([fieldCode, order]) => [
|
|
6222
|
+
fieldCode,
|
|
6223
|
+
new Set(order.keys())
|
|
6224
|
+
])
|
|
6225
|
+
);
|
|
6226
|
+
}
|
|
5991
6227
|
async function getSortKindMapByApp(appId, client, cacheContext) {
|
|
5992
6228
|
const cached = getScopedCacheValue(sortKindCache, cacheContext, appId);
|
|
5993
6229
|
if (cached) return cached;
|
|
@@ -6253,6 +6489,16 @@ async function executeUpsert(stmt, client, options, cacheContext) {
|
|
|
6253
6489
|
updatedCount: toUpdate.length
|
|
6254
6490
|
};
|
|
6255
6491
|
}
|
|
6492
|
+
async function buildSubtableFieldTypeResolver(appId, typedInRefs, client, cacheContext) {
|
|
6493
|
+
if (typedInRefs.length === 0) return void 0;
|
|
6494
|
+
const fieldTypes = await getFieldTypeMap(appId, client, cacheContext);
|
|
6495
|
+
return (field) => {
|
|
6496
|
+
if (field.tableAlias !== null && field.tableAlias !== "_p") return void 0;
|
|
6497
|
+
if (field.tableAlias === "_p") return fieldTypes.get(field.field);
|
|
6498
|
+
const code = field.field.startsWith("_p.") ? field.field.slice(3) : field.field;
|
|
6499
|
+
return fieldTypes.get(code);
|
|
6500
|
+
};
|
|
6501
|
+
}
|
|
6256
6502
|
async function executeInsertSubtable(stmt, client, options, _cacheContext) {
|
|
6257
6503
|
const subtableCode = stmt.subtableCode;
|
|
6258
6504
|
const pidIndex = stmt.fields.indexOf("_pid");
|
|
@@ -6293,11 +6539,24 @@ async function executeInsertSubtable(stmt, client, options, _cacheContext) {
|
|
|
6293
6539
|
}
|
|
6294
6540
|
return { type: "INSERT", createdIds: [], insertedCount: stmt.values.length };
|
|
6295
6541
|
}
|
|
6296
|
-
async function executeUpdateSubtable(stmt, client, options,
|
|
6542
|
+
async function executeUpdateSubtable(stmt, client, options, cacheContext) {
|
|
6297
6543
|
const subtableCode = stmt.subtableCode;
|
|
6298
6544
|
if (!hasRidCondition(stmt.where)) {
|
|
6299
6545
|
throw new Error("\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u306B\u306F _rid \u6761\u4EF6\u304C\u5FC5\u9808\u3067\u3059");
|
|
6300
6546
|
}
|
|
6547
|
+
const typedInRefs = [];
|
|
6548
|
+
collectTypedInFieldRefs(stmt.where, typedInRefs);
|
|
6549
|
+
for (const assignment of stmt.assignments) {
|
|
6550
|
+
if (assignment.value.type === "CASE_VALUE") {
|
|
6551
|
+
collectCaseTypedInFieldRefs(assignment.value.expr, typedInRefs);
|
|
6552
|
+
}
|
|
6553
|
+
}
|
|
6554
|
+
const resolveFieldType = await buildSubtableFieldTypeResolver(
|
|
6555
|
+
stmt.appId,
|
|
6556
|
+
typedInRefs,
|
|
6557
|
+
client,
|
|
6558
|
+
cacheContext
|
|
6559
|
+
);
|
|
6301
6560
|
const parents = await fetchAll(
|
|
6302
6561
|
client.getRecords,
|
|
6303
6562
|
stmt.appId,
|
|
@@ -6306,7 +6565,7 @@ async function executeUpdateSubtable(stmt, client, options, _cacheContext) {
|
|
|
6306
6565
|
{ maxRecords: options.maxRecords ?? 1e4, parallel: options.fetchParallel ?? 1 }
|
|
6307
6566
|
);
|
|
6308
6567
|
const expanded = expandRowsForSubtableDml(parents, subtableCode);
|
|
6309
|
-
const targets = expanded.filter((r) => evalWhere(stmt.where, r.flat));
|
|
6568
|
+
const targets = expanded.filter((r) => evalWhere(stmt.where, r.flat, resolveFieldType));
|
|
6310
6569
|
if (options.confirm) {
|
|
6311
6570
|
const ok = await options.confirm(targets.length, "UPDATE");
|
|
6312
6571
|
if (!ok) throw new OperationCancelledError("UPDATE", targets.length);
|
|
@@ -6326,7 +6585,7 @@ async function executeUpdateSubtable(stmt, client, options, _cacheContext) {
|
|
|
6326
6585
|
if (a.field.startsWith("_")) {
|
|
6327
6586
|
throw new Error(`\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u3067\u30B7\u30B9\u30C6\u30E0\u5217\u300C${a.field}\u300D\u306F\u66F4\u65B0\u3067\u304D\u307E\u305B\u3093`);
|
|
6328
6587
|
}
|
|
6329
|
-
updates[a.field] = { value: evalAssignmentValueForSubtable(a.value, t.flat) };
|
|
6588
|
+
updates[a.field] = { value: evalAssignmentValueForSubtable(a.value, t.flat, resolveFieldType) };
|
|
6330
6589
|
}
|
|
6331
6590
|
byRid.set(t.rowId, updates);
|
|
6332
6591
|
}
|
|
@@ -6348,11 +6607,19 @@ async function executeUpdateSubtable(stmt, client, options, _cacheContext) {
|
|
|
6348
6607
|
}
|
|
6349
6608
|
return { type: "UPDATE", updatedCount: targets.length };
|
|
6350
6609
|
}
|
|
6351
|
-
async function executeDeleteSubtable(stmt, client, options,
|
|
6610
|
+
async function executeDeleteSubtable(stmt, client, options, cacheContext) {
|
|
6352
6611
|
const subtableCode = stmt.subtableCode;
|
|
6353
6612
|
if (!hasRidCondition(stmt.where)) {
|
|
6354
6613
|
throw new Error("\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB DELETE \u306B\u306F _rid \u6761\u4EF6\u304C\u5FC5\u9808\u3067\u3059");
|
|
6355
6614
|
}
|
|
6615
|
+
const typedInRefs = [];
|
|
6616
|
+
collectTypedInFieldRefs(stmt.where, typedInRefs);
|
|
6617
|
+
const resolveFieldType = await buildSubtableFieldTypeResolver(
|
|
6618
|
+
stmt.appId,
|
|
6619
|
+
typedInRefs,
|
|
6620
|
+
client,
|
|
6621
|
+
cacheContext
|
|
6622
|
+
);
|
|
6356
6623
|
const parents = await fetchAll(
|
|
6357
6624
|
client.getRecords,
|
|
6358
6625
|
stmt.appId,
|
|
@@ -6361,7 +6628,7 @@ async function executeDeleteSubtable(stmt, client, options, _cacheContext) {
|
|
|
6361
6628
|
{ maxRecords: options.maxRecords ?? 1e4, parallel: options.fetchParallel ?? 1 }
|
|
6362
6629
|
);
|
|
6363
6630
|
const expanded = expandRowsForSubtableDml(parents, subtableCode);
|
|
6364
|
-
const targets = expanded.filter((r) => evalWhere(stmt.where, r.flat));
|
|
6631
|
+
const targets = expanded.filter((r) => evalWhere(stmt.where, r.flat, resolveFieldType));
|
|
6365
6632
|
if (options.confirm) {
|
|
6366
6633
|
const ok = await options.confirm(targets.length, "DELETE");
|
|
6367
6634
|
if (!ok) throw new OperationCancelledError("DELETE", targets.length);
|
|
@@ -6476,11 +6743,11 @@ function buildSubtableReorderPutParams(appId, parentId, revision, subtableCode,
|
|
|
6476
6743
|
]
|
|
6477
6744
|
};
|
|
6478
6745
|
}
|
|
6479
|
-
function evalAssignmentValueForSubtable(value, row) {
|
|
6746
|
+
function evalAssignmentValueForSubtable(value, row, resolveFieldType) {
|
|
6480
6747
|
if (value.type === "STRING") return value.value;
|
|
6481
6748
|
if (value.type === "NUMBER") return String(value.value);
|
|
6482
6749
|
if (value.type === "ARITH") return String(evalArithExpr(value, row));
|
|
6483
|
-
if (value.type === "CASE_VALUE") return evalCaseWhen(value.expr, row);
|
|
6750
|
+
if (value.type === "CASE_VALUE") return evalCaseWhen(value.expr, row, resolveFieldType);
|
|
6484
6751
|
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`);
|
|
6485
6752
|
}
|
|
6486
6753
|
function valueToString(value) {
|
|
@@ -6512,7 +6779,15 @@ function hasRidCondition(where) {
|
|
|
6512
6779
|
return false;
|
|
6513
6780
|
}
|
|
6514
6781
|
}
|
|
6515
|
-
async function executeReorder(stmt, client, options,
|
|
6782
|
+
async function executeReorder(stmt, client, options, cacheContext) {
|
|
6783
|
+
const typedInRefs = [];
|
|
6784
|
+
collectTypedInFieldRefs(stmt.where, typedInRefs);
|
|
6785
|
+
const resolveFieldType = await buildSubtableFieldTypeResolver(
|
|
6786
|
+
stmt.appId,
|
|
6787
|
+
typedInRefs,
|
|
6788
|
+
client,
|
|
6789
|
+
cacheContext
|
|
6790
|
+
);
|
|
6516
6791
|
const parents = await fetchAll(
|
|
6517
6792
|
client.getRecords,
|
|
6518
6793
|
stmt.appId,
|
|
@@ -6521,7 +6796,7 @@ async function executeReorder(stmt, client, options, _cacheContext) {
|
|
|
6521
6796
|
{ maxRecords: options.maxRecords ?? 1e4, parallel: options.fetchParallel ?? 1 }
|
|
6522
6797
|
);
|
|
6523
6798
|
const expanded = expandRowsForSubtableDml(parents, stmt.subtableCode);
|
|
6524
|
-
const targetParentIds = stmt.all ? new Set(parents.map((p) => String(p["$id"]?.value ?? "")).filter((id) => id !== "")) : new Set(expanded.filter((r) => stmt.where && evalWhere(stmt.where, r.flat)).map((r) => r.parentId));
|
|
6799
|
+
const targetParentIds = stmt.all ? new Set(parents.map((p) => String(p["$id"]?.value ?? "")).filter((id) => id !== "")) : new Set(expanded.filter((r) => stmt.where && evalWhere(stmt.where, r.flat, resolveFieldType)).map((r) => r.parentId));
|
|
6525
6800
|
if (options.confirm) {
|
|
6526
6801
|
const ok = await options.confirm(targetParentIds.size, "UPDATE");
|
|
6527
6802
|
if (!ok) throw new OperationCancelledError("UPDATE", targetParentIds.size);
|
|
@@ -6666,6 +6941,16 @@ async function resolveSubqueries(where, client, options, cacheContext, cteCache)
|
|
|
6666
6941
|
collectSubqueryTasks(where, client, options, cacheContext, tasks, cteCache);
|
|
6667
6942
|
await Promise.all(tasks);
|
|
6668
6943
|
}
|
|
6944
|
+
async function resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache) {
|
|
6945
|
+
const tasks = [];
|
|
6946
|
+
for (const column of stmt.columns) {
|
|
6947
|
+
if (column.type !== "CASE_COL") continue;
|
|
6948
|
+
for (const branch of column.expr.branches) {
|
|
6949
|
+
tasks.push(resolveSubqueries(branch.condition, client, options, cacheContext, cteCache));
|
|
6950
|
+
}
|
|
6951
|
+
}
|
|
6952
|
+
await Promise.all(tasks);
|
|
6953
|
+
}
|
|
6669
6954
|
function runSubquery(query, client, options, cacheContext, cteCache) {
|
|
6670
6955
|
if (cteCache !== void 0 && cteCache.size > 0) {
|
|
6671
6956
|
return executeQueryWithCte(query, client, options, cteCache, cacheContext);
|
|
@@ -6898,12 +7183,12 @@ function buildSelectPlan(stmt, label) {
|
|
|
6898
7183
|
const mainFields = selectToFetchAllFields(stmt, stmt.from);
|
|
6899
7184
|
const mainAliasStr = stmt.from.alias ? ` AS ${stmt.from.alias}` : "";
|
|
6900
7185
|
const mainPushDown = extractMainSafePushdown(stmt);
|
|
6901
|
-
const mainCandidate =
|
|
7186
|
+
const mainCandidate = extractMainTypedPushdownCandidate(stmt);
|
|
6902
7187
|
const mainQ = mainPushDown !== null ? whereToKintone(mainPushDown) : "(\u5168\u4EF6\u53D6\u5F97)";
|
|
6903
7188
|
lines.push(` app: APP${stmt.from.appId}${mainAliasStr} (${stmt.from.appId})`);
|
|
6904
7189
|
lines.push(` kintone query: ${mainQ}`);
|
|
6905
7190
|
if (mainCandidate !== null) {
|
|
6906
|
-
lines.push(` pushdown candidate: ${whereToKintone(mainCandidate)}\uFF08\u5B9F\u884C\u6642\u306E\u578B\u78BA\u8A8D\u5F85\u3061\uFF09`);
|
|
7191
|
+
lines.push(` pushdown candidate: ${whereToKintone(mainCandidate)}\uFF08\u5B9F\u884C\u6642\u306E\u578B\u30FB\u5B9F\u5728\u78BA\u8A8D\u5F85\u3061\uFF09`);
|
|
6907
7192
|
}
|
|
6908
7193
|
lines.push(` fields: ${mainFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : mainFields.join(", ")}`);
|
|
6909
7194
|
for (const join2 of stmt.joins) {
|
|
@@ -6911,12 +7196,12 @@ function buildSelectPlan(stmt, label) {
|
|
|
6911
7196
|
const joinAliasStr = join2.table.alias ? ` AS ${join2.table.alias}` : "";
|
|
6912
7197
|
const joinType = join2.type === "INNER" ? "JOIN" : `${join2.type} JOIN`;
|
|
6913
7198
|
const joinPushDown = join2.table.alias && !join2.table.subtableCode && join2.table.cteName === null && stmt.where ? extractSafePushdownLeaves(stmt.where, { tableAlias: join2.table.alias }) : null;
|
|
6914
|
-
const joinCandidate = join2.table.alias && !join2.table.subtableCode && join2.table.cteName === null && stmt.where ?
|
|
7199
|
+
const joinCandidate = join2.table.alias && !join2.table.subtableCode && join2.table.cteName === null && stmt.where ? extractTypedPushdownCandidates(stmt.where, { tableAlias: join2.table.alias }) : null;
|
|
6915
7200
|
const joinQ = joinPushDown !== null ? whereToKintone(joinPushDown) : "(\u5168\u4EF6\u53D6\u5F97)";
|
|
6916
7201
|
lines.push(` ${joinType}: APP${join2.table.appId}${joinAliasStr} (${join2.table.appId})`);
|
|
6917
7202
|
lines.push(` kintone query: ${joinQ}`);
|
|
6918
7203
|
if (joinCandidate !== null) {
|
|
6919
|
-
lines.push(` pushdown candidate: ${whereToKintone(joinCandidate)}\uFF08\u5B9F\u884C\u6642\u306E\u578B\u78BA\u8A8D\u5F85\u3061\uFF09`);
|
|
7204
|
+
lines.push(` pushdown candidate: ${whereToKintone(joinCandidate)}\uFF08\u5B9F\u884C\u6642\u306E\u578B\u30FB\u5B9F\u5728\u78BA\u8A8D\u5F85\u3061\uFF09`);
|
|
6920
7205
|
}
|
|
6921
7206
|
lines.push(` fields: ${joinFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : joinFields.join(", ")}`);
|
|
6922
7207
|
}
|
|
@@ -7595,6 +7880,42 @@ function clampInt(v, min, max) {
|
|
|
7595
7880
|
return Math.max(min, Math.min(max, Math.trunc(v)));
|
|
7596
7881
|
}
|
|
7597
7882
|
|
|
7883
|
+
// src/core/formFieldInfo.ts
|
|
7884
|
+
function flattenFormFieldProperties(properties) {
|
|
7885
|
+
const out = [];
|
|
7886
|
+
for (const field of Object.values(properties)) {
|
|
7887
|
+
out.push({
|
|
7888
|
+
code: field.code,
|
|
7889
|
+
label: field.label,
|
|
7890
|
+
fieldType: field.type,
|
|
7891
|
+
optionOrder: toOptionOrderMap(field.options),
|
|
7892
|
+
sortKind: detectSortKind(field.type, field.format)
|
|
7893
|
+
});
|
|
7894
|
+
if (field.fields) out.push(...flattenFormFieldProperties(field.fields));
|
|
7895
|
+
}
|
|
7896
|
+
return out;
|
|
7897
|
+
}
|
|
7898
|
+
function toOptionOrderMap(options) {
|
|
7899
|
+
if (!options || typeof options !== "object") return void 0;
|
|
7900
|
+
const order = {};
|
|
7901
|
+
let hasAny = false;
|
|
7902
|
+
for (const [label, meta] of Object.entries(options)) {
|
|
7903
|
+
const n = Number(meta?.index);
|
|
7904
|
+
if (!Number.isFinite(n)) continue;
|
|
7905
|
+
order[label] = n;
|
|
7906
|
+
hasAny = true;
|
|
7907
|
+
}
|
|
7908
|
+
return hasAny ? order : void 0;
|
|
7909
|
+
}
|
|
7910
|
+
function detectSortKind(fieldType, calcFormat) {
|
|
7911
|
+
if (fieldType === "NUMBER" || fieldType === "RECORD_NUMBER") return "number";
|
|
7912
|
+
if (fieldType === "CALC") {
|
|
7913
|
+
if (calcFormat === "NUMBER" || calcFormat === "NUMBER_DIGIT") return "number";
|
|
7914
|
+
return "string";
|
|
7915
|
+
}
|
|
7916
|
+
return void 0;
|
|
7917
|
+
}
|
|
7918
|
+
|
|
7598
7919
|
// src/cli/nodeKintoneClient.ts
|
|
7599
7920
|
function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
7600
7921
|
const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
@@ -7773,36 +8094,10 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
|
|
|
7773
8094
|
{ method: "GET" },
|
|
7774
8095
|
appId
|
|
7775
8096
|
);
|
|
7776
|
-
return
|
|
7777
|
-
code: f.code,
|
|
7778
|
-
label: f.label,
|
|
7779
|
-
fieldType: f.type,
|
|
7780
|
-
optionOrder: toOptionOrderMap(f.options),
|
|
7781
|
-
sortKind: detectSortKind(f.type, f.format)
|
|
7782
|
-
}));
|
|
8097
|
+
return flattenFormFieldProperties(res.properties);
|
|
7783
8098
|
}
|
|
7784
8099
|
};
|
|
7785
8100
|
}
|
|
7786
|
-
function toOptionOrderMap(options) {
|
|
7787
|
-
if (!options || typeof options !== "object") return void 0;
|
|
7788
|
-
const order = {};
|
|
7789
|
-
let hasAny = false;
|
|
7790
|
-
for (const [label, meta] of Object.entries(options)) {
|
|
7791
|
-
const n = Number(meta?.index);
|
|
7792
|
-
if (!Number.isFinite(n)) continue;
|
|
7793
|
-
order[label] = n;
|
|
7794
|
-
hasAny = true;
|
|
7795
|
-
}
|
|
7796
|
-
return hasAny ? order : void 0;
|
|
7797
|
-
}
|
|
7798
|
-
function detectSortKind(fieldType, calcFormat) {
|
|
7799
|
-
if (fieldType === "NUMBER" || fieldType === "RECORD_NUMBER") return "number";
|
|
7800
|
-
if (fieldType === "CALC") {
|
|
7801
|
-
if (calcFormat === "NUMBER" || calcFormat === "NUMBER_DIGIT") return "number";
|
|
7802
|
-
return "string";
|
|
7803
|
-
}
|
|
7804
|
-
return void 0;
|
|
7805
|
-
}
|
|
7806
8101
|
|
|
7807
8102
|
// src/node/appProfiles.ts
|
|
7808
8103
|
var import_fs = require("fs");
|