@rex0220/kintone-sql-tools 3.6.1 → 3.7.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 +253 -69
- package/dist-mcp/ksql-mcp.js +239 -68
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -27,6 +27,7 @@ __export(index_exports, {
|
|
|
27
27
|
buildBatchStatementSummary: () => buildBatchStatementSummary,
|
|
28
28
|
buildOutput: () => buildOutput,
|
|
29
29
|
buildReplExecArgv: () => buildReplExecArgv,
|
|
30
|
+
buildSelectSummary: () => buildSelectSummary,
|
|
30
31
|
buildValidationOutput: () => buildValidationOutput,
|
|
31
32
|
extractAppIds: () => extractAppIds,
|
|
32
33
|
normalizeAppKey: () => normalizeAppKey,
|
|
@@ -1146,14 +1147,30 @@ var Parser = class {
|
|
|
1146
1147
|
const validateTok = this.advance();
|
|
1147
1148
|
const name = this.parseIdentifier();
|
|
1148
1149
|
const { appId, subtableCode } = extractTableRef(name, this.prev());
|
|
1149
|
-
if (subtableCode)
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1150
|
+
if (subtableCode) throw new ParseError(
|
|
1151
|
+
`VALIDATE APP${appId}$${subtableCode} \u306F\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002VALIDATE APP${appId} (${subtableCode}) \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044`,
|
|
1152
|
+
this.prev()
|
|
1153
|
+
);
|
|
1154
|
+
let targets;
|
|
1153
1155
|
if (this.consume("(" /* LPAREN */)) {
|
|
1154
|
-
|
|
1156
|
+
targets = [];
|
|
1157
|
+
do {
|
|
1158
|
+
const field = this.parseIdentifier();
|
|
1159
|
+
if (this.consume("(" /* LPAREN */)) {
|
|
1160
|
+
const children = this.peek().kind === ")" /* RPAREN */ ? [] : this.parseIdentList();
|
|
1161
|
+
this.expect(")" /* RPAREN */);
|
|
1162
|
+
targets.push({ kind: "SUBTABLE", subtableCode: field, children });
|
|
1163
|
+
} else {
|
|
1164
|
+
targets.push({ kind: "FIELD", field });
|
|
1165
|
+
}
|
|
1166
|
+
} while (this.consume("," /* COMMA */));
|
|
1155
1167
|
this.expect(")" /* RPAREN */);
|
|
1156
1168
|
}
|
|
1169
|
+
let summary;
|
|
1170
|
+
if (this.isSoftKeyword("SUMMARY")) {
|
|
1171
|
+
this.advance();
|
|
1172
|
+
summary = true;
|
|
1173
|
+
}
|
|
1157
1174
|
const where = this.consume("WHERE" /* WHERE */) ? this.parseWhereExpr() : null;
|
|
1158
1175
|
const checks = this.parseCheckGroups();
|
|
1159
1176
|
let errorTable;
|
|
@@ -1164,7 +1181,7 @@ var Parser = class {
|
|
|
1164
1181
|
}
|
|
1165
1182
|
errorTable = this.parseTableName();
|
|
1166
1183
|
}
|
|
1167
|
-
const stmt = { type: "VALIDATE", appId,
|
|
1184
|
+
const stmt = { type: "VALIDATE", appId, targets, ...summary ? { summary } : {}, where, ...checks, ...errorTable ? { errorTable } : {} };
|
|
1168
1185
|
this.assertValidateExpressions(stmt, validateTok);
|
|
1169
1186
|
return stmt;
|
|
1170
1187
|
}
|
|
@@ -4802,7 +4819,7 @@ function analyzeBatch(statements) {
|
|
|
4802
4819
|
dependsOn.add(at);
|
|
4803
4820
|
}
|
|
4804
4821
|
if (validationTable) {
|
|
4805
|
-
const payloadFields = stmt.type === "VALIDATE" ? ["$id", "$err_field", "$err_code", "$err_message", "$err_value"] : stmt.type === "IMPORT" && stmt.targets?.some((target) => target.kind === "SUBTABLE") ? [...new Set(stmt.targets.flatMap((target) => target.kind === "FIELD" ? [target.field] : target.children)), "$err_subtable", "$err_subrow", "$err_source_row"] : stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : "fields" in stmt ? stmt.fields : [];
|
|
4822
|
+
const payloadFields = stmt.type === "VALIDATE" ? stmt.summary ? ["$id", "$err_subtable", "$err_field", "$err_code", "$err_count"] : ["$id", "$err_field", "$err_code", "$err_message", "$err_value", "$err_subtable", "$err_subrow", "$err_subrow_id", "$err_count"] : stmt.type === "IMPORT" && stmt.targets?.some((target) => target.kind === "SUBTABLE") ? [...new Set(stmt.targets.flatMap((target) => target.kind === "FIELD" ? [target.field] : target.children)), "$err_subtable", "$err_subrow", "$err_source_row"] : stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : "fields" in stmt ? stmt.fields : [];
|
|
4806
4823
|
const signature = JSON.stringify(payloadFields);
|
|
4807
4824
|
const at = defined.get(validationTable);
|
|
4808
4825
|
if (at === void 0) {
|
|
@@ -7667,7 +7684,7 @@ function validateAndNormalizeDmlValue(raw, field, numberPrecision) {
|
|
|
7667
7684
|
return { ok: false, code: "ERR_LENGTH_MAX", message: `${field.code} \u306F ${max} \u6587\u5B57\u4EE5\u4E0B\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044` };
|
|
7668
7685
|
}
|
|
7669
7686
|
}
|
|
7670
|
-
if (CHOICE_TYPES.has(field.fieldType) && field.optionOrder) {
|
|
7687
|
+
if (!isEmpty(value) && CHOICE_TYPES.has(field.fieldType) && field.optionOrder) {
|
|
7671
7688
|
const selected = Array.isArray(value) ? value.map(String) : [String(value)];
|
|
7672
7689
|
if (selected.some((choice) => !(choice in field.optionOrder))) {
|
|
7673
7690
|
return { ok: false, code: "ERR_CHOICE_INVALID", message: `${field.code} \u306B\u5B9A\u7FA9\u5916\u306E\u9078\u629E\u80A2\u304C\u3042\u308A\u307E\u3059` };
|
|
@@ -9153,25 +9170,84 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
|
9153
9170
|
return executeAssert(stmt, client, options, cacheContext);
|
|
9154
9171
|
}
|
|
9155
9172
|
}
|
|
9156
|
-
var EXISTING_VALIDATION_COLUMNS = [
|
|
9173
|
+
var EXISTING_VALIDATION_COLUMNS = [
|
|
9174
|
+
"$id",
|
|
9175
|
+
"$err_field",
|
|
9176
|
+
"$err_code",
|
|
9177
|
+
"$err_message",
|
|
9178
|
+
"$err_value",
|
|
9179
|
+
"$err_subtable",
|
|
9180
|
+
"$err_subrow",
|
|
9181
|
+
"$err_subrow_id",
|
|
9182
|
+
"$err_count"
|
|
9183
|
+
];
|
|
9184
|
+
var EXISTING_VALIDATION_SUMMARY_COLUMNS = [
|
|
9185
|
+
"$id",
|
|
9186
|
+
"$err_subtable",
|
|
9187
|
+
"$err_field",
|
|
9188
|
+
"$err_code",
|
|
9189
|
+
"$err_count"
|
|
9190
|
+
];
|
|
9157
9191
|
function hasAuditableConstraint(field) {
|
|
9158
9192
|
return field.required === true || field.minValue !== void 0 || field.maxValue !== void 0 || field.minLength !== void 0 || field.maxLength !== void 0 || field.optionOrder !== void 0;
|
|
9159
9193
|
}
|
|
9160
9194
|
function resolveExistingValidationTargets(stmt, fieldInfos) {
|
|
9161
|
-
const
|
|
9162
|
-
const
|
|
9163
|
-
|
|
9195
|
+
const topByCode = new Map(fieldInfos.filter((field) => !field.inSubtable).map((field) => [field.code, field]));
|
|
9196
|
+
const childrenByTable = /* @__PURE__ */ new Map();
|
|
9197
|
+
for (const field of fieldInfos) {
|
|
9198
|
+
if (!field.inSubtable || !field.subtableCode) continue;
|
|
9199
|
+
const children = childrenByTable.get(field.subtableCode) ?? [];
|
|
9200
|
+
children.push(field);
|
|
9201
|
+
childrenByTable.set(field.subtableCode, children);
|
|
9202
|
+
}
|
|
9203
|
+
const auditable = (field) => field.fieldType === "NUMBER" || hasAuditableConstraint(field);
|
|
9204
|
+
if (stmt.targets === void 0) return [
|
|
9205
|
+
...fieldInfos.filter((field) => !field.inSubtable && field.fieldType !== "SUBTABLE" && auditable(field)),
|
|
9206
|
+
...fieldInfos.filter((field) => field.inSubtable && !!field.subtableCode && auditable(field))
|
|
9207
|
+
].map((field) => ({ field, ...field.subtableCode ? { subtableCode: field.subtableCode } : {} }));
|
|
9208
|
+
const result = [];
|
|
9164
9209
|
const seen = /* @__PURE__ */ new Set();
|
|
9165
|
-
|
|
9166
|
-
|
|
9167
|
-
seen.
|
|
9168
|
-
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9210
|
+
const add = (field, subtableCode) => {
|
|
9211
|
+
const key = subtableCode ? `${subtableCode}\0${field.code}` : field.code;
|
|
9212
|
+
if (seen.has(key)) throw new Error(`ArgumentError: VALIDATE \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${field.code} \u304C\u91CD\u8907\u3057\u3066\u3044\u307E\u3059\u3002`);
|
|
9213
|
+
seen.add(key);
|
|
9214
|
+
if (!auditable(field)) throw new Error(`ArgumentError: VALIDATE \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${field.code} \u306B\u306F\u76E3\u67FB\u53EF\u80FD\u306A\u5236\u7D04\u304C\u3042\u308A\u307E\u305B\u3093\u3002`);
|
|
9215
|
+
result.push({ field, ...subtableCode ? { subtableCode } : {} });
|
|
9216
|
+
};
|
|
9217
|
+
for (const target of stmt.targets) {
|
|
9218
|
+
if (target.kind === "SUBTABLE") {
|
|
9219
|
+
const children = childrenByTable.get(target.subtableCode);
|
|
9220
|
+
if (!children) throw new Error(`ArgumentError: VALIDATE \u306E\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB ${target.subtableCode} \u306F\u5B58\u5728\u3057\u307E\u305B\u3093\u3002`);
|
|
9221
|
+
if (target.children.length === 0) throw new Error(`ArgumentError: VALIDATE \u306E\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB ${target.subtableCode} \u306B\u306F1\u3064\u4EE5\u4E0A\u306E\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u5FC5\u8981\u3067\u3059\u3002`);
|
|
9222
|
+
for (const code2 of target.children) {
|
|
9223
|
+
const child = children.find((field) => field.code === code2);
|
|
9224
|
+
if (!child) {
|
|
9225
|
+
const belongsElsewhere = [...childrenByTable.entries()].some(([table, fields]) => table !== target.subtableCode && fields.some((field) => field.code === code2));
|
|
9226
|
+
throw new Error(belongsElsewhere ? `ArgumentError: VALIDATE \u306E\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9 ${code2} \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB ${target.subtableCode} \u306B\u5C5E\u3057\u3066\u3044\u307E\u305B\u3093\u3002` : `ArgumentError: VALIDATE \u306E\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9 ${code2} \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB ${target.subtableCode} \u306B\u5B58\u5728\u3057\u307E\u305B\u3093\u3002`);
|
|
9227
|
+
}
|
|
9228
|
+
add(child, target.subtableCode);
|
|
9229
|
+
}
|
|
9230
|
+
continue;
|
|
9231
|
+
}
|
|
9232
|
+
const code = target.field;
|
|
9233
|
+
if (code === "$id") throw new Error("ArgumentError: VALIDATE \u3067\u306F\u30B7\u30B9\u30C6\u30E0\u30D5\u30A3\u30FC\u30EB\u30C9 $id \u3092\u76E3\u67FB\u3067\u304D\u307E\u305B\u3093\u3002");
|
|
9234
|
+
const top = topByCode.get(code);
|
|
9235
|
+
if (top?.fieldType === "SUBTABLE") {
|
|
9236
|
+
const children = (childrenByTable.get(code) ?? []).filter(auditable);
|
|
9237
|
+
if (children.length === 0) throw new Error(`ArgumentError: VALIDATE \u306E\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB ${code} \u306B\u306F\u76E3\u67FB\u53EF\u80FD\u306A\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u3042\u308A\u307E\u305B\u3093\u3002`);
|
|
9238
|
+
children.forEach((child) => add(child, code));
|
|
9239
|
+
continue;
|
|
9240
|
+
}
|
|
9241
|
+
if (top) {
|
|
9242
|
+
add(top);
|
|
9243
|
+
continue;
|
|
9244
|
+
}
|
|
9245
|
+
if ([...childrenByTable.values()].some((children) => children.some((field) => field.code === code))) {
|
|
9246
|
+
throw new Error(`ArgumentError: VALIDATE \u306E\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9 ${code} \u306F\u6240\u6709\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u3092\u542B\u3080 T(${code}) \u5F62\u5F0F\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002`);
|
|
9247
|
+
}
|
|
9248
|
+
throw new Error(`ArgumentError: VALIDATE \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${code} \u306F\u5B58\u5728\u3057\u307E\u305B\u3093\u3002`);
|
|
9249
|
+
}
|
|
9250
|
+
return result;
|
|
9175
9251
|
}
|
|
9176
9252
|
function collectValidateWhereFields(where) {
|
|
9177
9253
|
const fields = [];
|
|
@@ -9196,11 +9272,12 @@ function collectValidateWhereFields(where) {
|
|
|
9196
9272
|
visit(where);
|
|
9197
9273
|
return fields;
|
|
9198
9274
|
}
|
|
9199
|
-
function existingValidationColumnMeta() {
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9275
|
+
function existingValidationColumnMeta(summary = false) {
|
|
9276
|
+
const columns = summary ? EXISTING_VALIDATION_SUMMARY_COLUMNS : EXISTING_VALIDATION_COLUMNS;
|
|
9277
|
+
return new Map(columns.map((column) => [column, {
|
|
9278
|
+
fieldType: column === "$id" || column === "$err_count" ? "KSQL_NUMBER" : "KSQL_STRING",
|
|
9279
|
+
sortKind: column === "$id" || column === "$err_count" ? "number" : "string",
|
|
9280
|
+
semantics: syntheticSemantics(column === "$id" || column === "$err_count" ? "number" : "string")
|
|
9204
9281
|
}]));
|
|
9205
9282
|
}
|
|
9206
9283
|
async function executeExistingRecordValidation(stmt, client, options, cacheContext) {
|
|
@@ -9209,38 +9286,45 @@ async function executeExistingRecordValidation(stmt, client, options, cacheConte
|
|
|
9209
9286
|
}
|
|
9210
9287
|
async function executeExistingRecordValidationCore(stmt, client, options, cacheContext) {
|
|
9211
9288
|
const fieldInfos = await getFieldsCached(stmt.appId, client, cacheContext);
|
|
9212
|
-
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
9289
|
+
const infoByCode = new Map(fieldInfos.filter((field) => !field.inSubtable).map((field) => [field.code, field]));
|
|
9290
|
+
const childCodes = new Set(fieldInfos.filter((field) => field.inSubtable).map((field) => field.code));
|
|
9213
9291
|
const targets = resolveExistingValidationTargets(stmt, fieldInfos);
|
|
9214
9292
|
const checkGroups = stmt.checkGroups ?? [];
|
|
9215
9293
|
const checkRefs2 = collectCheckFieldRefs(checkGroups);
|
|
9216
9294
|
for (const ref of checkRefs2) {
|
|
9217
|
-
if (ref.field !== "$id" && !infoByCode.has(ref.field)) {
|
|
9295
|
+
if (ref.field !== "$id" && !infoByCode.has(ref.field) && !childCodes.has(ref.field)) {
|
|
9218
9296
|
throw customCheckParseError(`CHECK \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F APP${stmt.appId} \u306B\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
9219
9297
|
}
|
|
9298
|
+
if (ref.field !== "$id" && !infoByCode.has(ref.field) && childCodes.has(ref.field)) {
|
|
9299
|
+
throw new Error(`ArgumentError: VALIDATE \u306E CHECK \u3067\u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u3092\u53C2\u7167\u3067\u304D\u307E\u305B\u3093\u3002`);
|
|
9300
|
+
}
|
|
9220
9301
|
}
|
|
9221
|
-
const evaluationTypes = new Map(
|
|
9302
|
+
const evaluationTypes = new Map([...infoByCode].map(([code, field]) => [code, field.fieldType]));
|
|
9222
9303
|
evaluationTypes.set("$id", "RECORD_NUMBER");
|
|
9223
9304
|
assertCheckComparisonTypes(stmt, evaluationTypes);
|
|
9224
9305
|
const whereFields = collectValidateWhereFields(stmt.where);
|
|
9225
9306
|
const requiredFields = [.../* @__PURE__ */ new Set([
|
|
9226
9307
|
"$id",
|
|
9227
|
-
...targets.map((
|
|
9308
|
+
...targets.map((target) => target.subtableCode ?? target.field.code),
|
|
9228
9309
|
...whereFields,
|
|
9229
9310
|
...checkRefs2.map((ref) => ref.field)
|
|
9230
9311
|
])];
|
|
9231
9312
|
for (const field of whereFields) {
|
|
9232
|
-
if (field !== "$id" && !infoByCode.has(field)) {
|
|
9313
|
+
if (field !== "$id" && !infoByCode.has(field) && !childCodes.has(field)) {
|
|
9233
9314
|
throw new Error(`ArgumentError: WHERE field ${field} does not exist in APP${stmt.appId}.`);
|
|
9234
9315
|
}
|
|
9316
|
+
if (field !== "$id" && !infoByCode.has(field) && childCodes.has(field)) {
|
|
9317
|
+
throw new Error(`ArgumentError: VALIDATE \u306E WHERE \u3067\u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9 ${field} \u3092\u53C2\u7167\u3067\u304D\u307E\u305B\u3093\u3002`);
|
|
9318
|
+
}
|
|
9235
9319
|
}
|
|
9236
|
-
const numberPrecision = targets.some((
|
|
9320
|
+
const numberPrecision = targets.some((target) => target.field.fieldType === "NUMBER") ? await getNumberPrecisionCached(stmt.appId, client, cacheContext) : void 0;
|
|
9237
9321
|
const semantics = (field) => field.field === "$id" ? resolveFieldSemantics({ fieldType: "__ID__" }) : infoByCode.get(field.field)?.semantics ?? (infoByCode.has(field.field) ? resolveFieldSemantics(infoByCode.get(field.field)) : void 0);
|
|
9238
9322
|
const capability = classifyWhereCapability(stmt.where, semantics);
|
|
9239
9323
|
if (capability.capability === "UNSUPPORTED") {
|
|
9240
9324
|
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
|
|
9241
9325
|
}
|
|
9242
|
-
const fieldTypes = new Map(
|
|
9243
|
-
const fieldOptions = new Map(
|
|
9326
|
+
const fieldTypes = new Map([...infoByCode].map(([code, field]) => [code, field.fieldType]));
|
|
9327
|
+
const fieldOptions = new Map([...infoByCode.values()].flatMap((field) => field.optionOrder ? [[field.code, new Set(Object.keys(field.optionOrder))]] : []));
|
|
9244
9328
|
const prefilter = stmt.where === null ? null : capability.capability === "EXACT_PUSHDOWN" ? stmt.where : extractSafePushdownLeaves(stmt.where, {
|
|
9245
9329
|
allowUnqualifiedFields: true,
|
|
9246
9330
|
fieldTypes,
|
|
@@ -9259,36 +9343,110 @@ async function executeExistingRecordValidationCore(stmt, client, options, cacheC
|
|
|
9259
9343
|
flat: flatten(record, null)
|
|
9260
9344
|
})).filter((row) => stmt.where === null || evalWhere(stmt.where, row.flat, (field) => evaluationTypes.get(field.field)));
|
|
9261
9345
|
const rows = [];
|
|
9346
|
+
const detailRows = /* @__PURE__ */ new Map();
|
|
9347
|
+
const summaryRows = /* @__PURE__ */ new Map();
|
|
9348
|
+
const errorRecordIds = /* @__PURE__ */ new Set();
|
|
9349
|
+
let errorCount = 0;
|
|
9350
|
+
const appendError = (error) => {
|
|
9351
|
+
errorRecordIds.add(error.id);
|
|
9352
|
+
errorCount += 1;
|
|
9353
|
+
if (stmt.summary) {
|
|
9354
|
+
const key2 = JSON.stringify([error.id, error.subtable ?? "", error.field, error.code]);
|
|
9355
|
+
const current2 = summaryRows.get(key2);
|
|
9356
|
+
if (current2) current2["$err_count"] = String(Number(current2["$err_count"]) + 1);
|
|
9357
|
+
else summaryRows.set(key2, {
|
|
9358
|
+
"$id": error.id,
|
|
9359
|
+
"$err_subtable": error.subtable ?? "",
|
|
9360
|
+
"$err_field": error.field,
|
|
9361
|
+
"$err_code": error.code,
|
|
9362
|
+
"$err_count": "1"
|
|
9363
|
+
});
|
|
9364
|
+
return;
|
|
9365
|
+
}
|
|
9366
|
+
const key = JSON.stringify([error.id, error.subtable ?? "", error.field, error.code, error.message]);
|
|
9367
|
+
const current = detailRows.get(key);
|
|
9368
|
+
if (current) {
|
|
9369
|
+
current["$err_count"] = String(Number(current["$err_count"]) + 1);
|
|
9370
|
+
if (error.subrow !== void 0) {
|
|
9371
|
+
current["$err_subrow"] = `${current["$err_subrow"]},${error.subrow}`;
|
|
9372
|
+
current["$err_subrow_id"] = `${current["$err_subrow_id"]},${error.subrowId ?? ""}`;
|
|
9373
|
+
}
|
|
9374
|
+
} else detailRows.set(key, {
|
|
9375
|
+
"$id": error.id,
|
|
9376
|
+
"$err_field": error.field,
|
|
9377
|
+
"$err_code": error.code,
|
|
9378
|
+
"$err_message": error.message,
|
|
9379
|
+
"$err_value": error.value,
|
|
9380
|
+
"$err_subtable": error.subtable ?? "",
|
|
9381
|
+
"$err_subrow": error.subrow === void 0 ? "" : String(error.subrow),
|
|
9382
|
+
"$err_subrow_id": error.subrowId ?? "",
|
|
9383
|
+
"$err_count": "1"
|
|
9384
|
+
});
|
|
9385
|
+
};
|
|
9386
|
+
const topTargets = targets.filter((target) => !target.subtableCode);
|
|
9387
|
+
const subtableTargets = /* @__PURE__ */ new Map();
|
|
9388
|
+
for (const target of targets) {
|
|
9389
|
+
if (!target.subtableCode) continue;
|
|
9390
|
+
const children = subtableTargets.get(target.subtableCode) ?? [];
|
|
9391
|
+
children.push(target);
|
|
9392
|
+
subtableTargets.set(target.subtableCode, children);
|
|
9393
|
+
}
|
|
9262
9394
|
for (const row of validationRows) {
|
|
9263
|
-
for (const
|
|
9264
|
-
const raw = row.record[field.code]?.value;
|
|
9265
|
-
const validation = validateAndNormalizeDmlValue(raw, field, numberPrecision);
|
|
9266
|
-
if (validation.ok)
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
9272
|
-
"$err_value": renderExistingValidationValue(raw, field.fieldType)
|
|
9395
|
+
for (const target of topTargets) {
|
|
9396
|
+
const raw = row.record[target.field.code]?.value;
|
|
9397
|
+
const validation = validateAndNormalizeDmlValue(raw, target.field, numberPrecision);
|
|
9398
|
+
if (!validation.ok) appendError({
|
|
9399
|
+
id: row.id,
|
|
9400
|
+
field: target.field.code,
|
|
9401
|
+
code: validation.code,
|
|
9402
|
+
message: validation.message,
|
|
9403
|
+
value: renderExistingValidationValue(raw, target.field.fieldType)
|
|
9273
9404
|
});
|
|
9274
9405
|
}
|
|
9406
|
+
for (const [tableCode, childTargets] of subtableTargets) {
|
|
9407
|
+
const tableRows2 = row.record[tableCode]?.value;
|
|
9408
|
+
if (!Array.isArray(tableRows2)) continue;
|
|
9409
|
+
for (let i = 0; i < tableRows2.length; i++) {
|
|
9410
|
+
const tableRow = tableRows2[i];
|
|
9411
|
+
for (const target of childTargets) {
|
|
9412
|
+
const raw = tableRow.value?.[target.field.code]?.value;
|
|
9413
|
+
const validation = validateAndNormalizeDmlValue(raw, target.field, numberPrecision);
|
|
9414
|
+
if (!validation.ok) appendError({
|
|
9415
|
+
id: row.id,
|
|
9416
|
+
field: target.field.code,
|
|
9417
|
+
code: validation.code,
|
|
9418
|
+
message: validation.message,
|
|
9419
|
+
value: renderExistingValidationValue(raw, target.field.fieldType),
|
|
9420
|
+
subtable: tableCode,
|
|
9421
|
+
subrow: i + 1,
|
|
9422
|
+
subrowId: String(tableRow.id ?? "")
|
|
9423
|
+
});
|
|
9424
|
+
}
|
|
9425
|
+
}
|
|
9426
|
+
}
|
|
9275
9427
|
for (const check of evaluateCustomChecks(checkGroups, row.flat, (field) => evaluationTypes.get(field.field))) {
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9428
|
+
appendError({ id: row.id, field: "", code: "ERR_CHECK", message: check.message, value: "" });
|
|
9429
|
+
}
|
|
9430
|
+
}
|
|
9431
|
+
if (stmt.summary) rows.push(...summaryRows.values());
|
|
9432
|
+
else {
|
|
9433
|
+
for (const row of detailRows.values()) {
|
|
9434
|
+
const count = Number(row["$err_count"]);
|
|
9435
|
+
if (row["$err_subtable"] !== "" && count >= 2) {
|
|
9436
|
+
row["$err_message"] = `${row["$err_message"]}\uFF08${count}\u884C: ${row["$err_subrow"]}\uFF09`;
|
|
9437
|
+
}
|
|
9438
|
+
rows.push(row);
|
|
9283
9439
|
}
|
|
9284
9440
|
}
|
|
9441
|
+
const columns = stmt.summary ? EXISTING_VALIDATION_SUMMARY_COLUMNS : EXISTING_VALIDATION_COLUMNS;
|
|
9285
9442
|
const result = {
|
|
9286
9443
|
type: "SELECT",
|
|
9287
|
-
columns: [...
|
|
9444
|
+
columns: [...columns],
|
|
9288
9445
|
rows,
|
|
9289
|
-
rowCount: rows.length
|
|
9446
|
+
rowCount: rows.length,
|
|
9447
|
+
validateStats: { errorRecords: errorRecordIds.size, errorCount }
|
|
9290
9448
|
};
|
|
9291
|
-
materializedMetaBySelectResult.set(result, existingValidationColumnMeta());
|
|
9449
|
+
materializedMetaBySelectResult.set(result, existingValidationColumnMeta(stmt.summary === true));
|
|
9292
9450
|
return result;
|
|
9293
9451
|
}
|
|
9294
9452
|
var TEMP_TABLE_MAX_ROWS = 1e4;
|
|
@@ -9485,7 +9643,7 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
9485
9643
|
result.columns,
|
|
9486
9644
|
result.rows,
|
|
9487
9645
|
options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS,
|
|
9488
|
-
materializedMetaBySelectResult.get(result) ?? existingValidationColumnMeta()
|
|
9646
|
+
materializedMetaBySelectResult.get(result) ?? existingValidationColumnMeta(resolvedStmt.summary === true)
|
|
9489
9647
|
);
|
|
9490
9648
|
}
|
|
9491
9649
|
return { result };
|
|
@@ -13600,43 +13758,51 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
13600
13758
|
const validate = node;
|
|
13601
13759
|
fieldApps.add(validate.appId);
|
|
13602
13760
|
const fields = await getFieldsCached(validate.appId, tracedClient, cacheContext);
|
|
13603
|
-
const infoByCode = new Map(fields.map((field) => [field.code, field]));
|
|
13761
|
+
const infoByCode = new Map(fields.filter((field) => !field.inSubtable).map((field) => [field.code, field]));
|
|
13762
|
+
const childCodes = new Set(fields.filter((field) => field.inSubtable).map((field) => field.code));
|
|
13604
13763
|
const targets = resolveExistingValidationTargets(validate, fields);
|
|
13605
13764
|
const checks = collectCheckFieldRefs(validate.checkGroups ?? []);
|
|
13606
13765
|
const whereFields = collectValidateWhereFields(validate.where);
|
|
13607
13766
|
for (const ref of checks) {
|
|
13608
|
-
if (ref.field !== "$id" && !infoByCode.has(ref.field)) {
|
|
13767
|
+
if (ref.field !== "$id" && !infoByCode.has(ref.field) && !childCodes.has(ref.field)) {
|
|
13609
13768
|
throw customCheckParseError(`CHECK \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F APP${validate.appId} \u306B\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
13610
13769
|
}
|
|
13770
|
+
if (ref.field !== "$id" && !infoByCode.has(ref.field) && childCodes.has(ref.field)) {
|
|
13771
|
+
throw new Error(`ArgumentError: VALIDATE \u306E CHECK \u3067\u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u3092\u53C2\u7167\u3067\u304D\u307E\u305B\u3093\u3002`);
|
|
13772
|
+
}
|
|
13611
13773
|
}
|
|
13612
13774
|
for (const field of whereFields) {
|
|
13613
|
-
if (field !== "$id" && !infoByCode.has(field)) {
|
|
13775
|
+
if (field !== "$id" && !infoByCode.has(field) && !childCodes.has(field)) {
|
|
13614
13776
|
throw new Error(`ArgumentError: WHERE field ${field} does not exist in APP${validate.appId}.`);
|
|
13615
13777
|
}
|
|
13778
|
+
if (field !== "$id" && !infoByCode.has(field) && childCodes.has(field)) {
|
|
13779
|
+
throw new Error(`ArgumentError: VALIDATE \u306E WHERE \u3067\u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9 ${field} \u3092\u53C2\u7167\u3067\u304D\u307E\u305B\u3093\u3002`);
|
|
13780
|
+
}
|
|
13616
13781
|
}
|
|
13617
|
-
const types = new Map(
|
|
13782
|
+
const types = new Map([...infoByCode].map(([code, field]) => [code, field.fieldType]));
|
|
13618
13783
|
types.set("$id", "RECORD_NUMBER");
|
|
13619
13784
|
assertCheckComparisonTypes(validate, types);
|
|
13620
13785
|
const capability = classifyWhereCapability(validate.where, (field) => field.field === "$id" ? resolveFieldSemantics({ fieldType: "__ID__" }) : infoByCode.get(field.field)?.semantics ?? (infoByCode.has(field.field) ? resolveFieldSemantics(infoByCode.get(field.field)) : void 0));
|
|
13621
13786
|
if (capability.capability === "UNSUPPORTED") {
|
|
13622
13787
|
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
|
|
13623
13788
|
}
|
|
13624
|
-
const fieldTypes = new Map(
|
|
13625
|
-
const fieldOptions = new Map(
|
|
13789
|
+
const fieldTypes = new Map([...infoByCode].map(([code, field]) => [code, field.fieldType]));
|
|
13790
|
+
const fieldOptions = new Map([...infoByCode.values()].flatMap((field) => field.optionOrder ? [[field.code, new Set(Object.keys(field.optionOrder))]] : []));
|
|
13626
13791
|
const prefilter = validate.where === null ? null : capability.capability === "EXACT_PUSHDOWN" ? validate.where : extractSafePushdownLeaves(validate.where, {
|
|
13627
13792
|
allowUnqualifiedFields: true,
|
|
13628
13793
|
fieldTypes,
|
|
13629
13794
|
fieldOptions,
|
|
13630
13795
|
allowKlike: false
|
|
13631
13796
|
});
|
|
13632
|
-
const needsPrecision = targets.some((
|
|
13797
|
+
const needsPrecision = targets.some((target) => target.field.fieldType === "NUMBER");
|
|
13633
13798
|
if (needsPrecision) {
|
|
13634
13799
|
numberPrecisionApps.add(validate.appId);
|
|
13635
13800
|
await getNumberPrecisionCached(validate.appId, tracedClient, cacheContext);
|
|
13636
13801
|
}
|
|
13637
13802
|
validateExplainInfo.set(validate, {
|
|
13638
|
-
targetFields: targets.map((
|
|
13639
|
-
fetchFields: [.../* @__PURE__ */ new Set(["$id", ...targets.map((
|
|
13803
|
+
targetFields: targets.map((target) => target.subtableCode ? `${target.subtableCode}(${target.field.code})` : target.field.code),
|
|
13804
|
+
fetchFields: [.../* @__PURE__ */ new Set(["$id", ...targets.map((target) => target.subtableCode ?? target.field.code), ...whereFields, ...checks.map((ref) => ref.field)])],
|
|
13805
|
+
subtables: new Map([...new Set(targets.flatMap((target) => target.subtableCode ? [target.subtableCode] : []))].map((table) => [table, targets.filter((target) => target.subtableCode === table).length])),
|
|
13640
13806
|
capability,
|
|
13641
13807
|
prefilter,
|
|
13642
13808
|
numberPrecision: needsPrecision
|
|
@@ -13931,6 +14097,10 @@ function buildValidatePlan(stmt, label) {
|
|
|
13931
14097
|
lines.push(` kintone query: ${info.prefilter === null ? "(\u5168\u4EF6\u53D6\u5F97)" : whereToKintone(info.prefilter)}`);
|
|
13932
14098
|
lines.push(` audit fields: ${info.targetFields.length === 0 ? "(\u306A\u3057)" : info.targetFields.join(", ")}`);
|
|
13933
14099
|
lines.push(` fetch fields: ${info.fetchFields.join(", ")}`);
|
|
14100
|
+
lines.push(` mode: ${stmt.summary ? "SUMMARY" : "DETAIL"}`);
|
|
14101
|
+
if (info.subtables.size > 0) lines.push(` subtable audit: ${[...info.subtables].map(([table, count]) => `${table}(${count} fields)`).join(", ")}`);
|
|
14102
|
+
lines.push(` output schema: ${(stmt.summary ? EXISTING_VALIDATION_SUMMARY_COLUMNS : EXISTING_VALIDATION_COLUMNS).join(", ")}`);
|
|
14103
|
+
lines.push(stmt.summary ? " aggregation: record/subtable/field/code; row locator=none" : " row locator: grouped by message; $err_subrow / $err_subrow_id list all matching rows (first-occurrence order)");
|
|
13934
14104
|
lines.push(` number precision: ${info.numberPrecision ? "required" : "not required"}`);
|
|
13935
14105
|
lines.push(" local checks: original WHERE re-evaluation + built-in constraints + CHECK groups");
|
|
13936
14106
|
lines.push(" records/mutation API during EXPLAIN: none; violation count unavailable");
|
|
@@ -14522,7 +14692,8 @@ function buildBatchEnvelope(batch, options = {}) {
|
|
|
14522
14692
|
columns: s.result.columns,
|
|
14523
14693
|
rows: s.result.rows,
|
|
14524
14694
|
rowCount: s.result.rowCount,
|
|
14525
|
-
warnings: s.result.warnings ?? []
|
|
14695
|
+
warnings: s.result.warnings ?? [],
|
|
14696
|
+
...s.result.validateStats ? { validateStats: s.result.validateStats } : {}
|
|
14526
14697
|
});
|
|
14527
14698
|
} else if (s.result?.type === "VALIDATION") {
|
|
14528
14699
|
totalRows += s.result.errorCount;
|
|
@@ -16441,7 +16612,8 @@ function buildOutput(result, format, noHeader, pretty, display) {
|
|
|
16441
16612
|
columns: result.columns,
|
|
16442
16613
|
rowCount: result.rowCount,
|
|
16443
16614
|
warnings: result.warnings ?? [],
|
|
16444
|
-
rows: result.rows
|
|
16615
|
+
rows: result.rows,
|
|
16616
|
+
...result.validateStats ? { validateStats: result.validateStats } : {}
|
|
16445
16617
|
};
|
|
16446
16618
|
return JSON.stringify(obj, null, pretty ? 2 : 0);
|
|
16447
16619
|
}
|
|
@@ -16496,7 +16668,12 @@ function buildBatchStatementSummary(s) {
|
|
|
16496
16668
|
const parts = [`[${s.index + 1}] ${s.type}`, s.status];
|
|
16497
16669
|
if (s.tempTable) parts.push(`temp=${s.tempTable}`);
|
|
16498
16670
|
if (s.rowCount !== void 0) parts.push(`rows=${s.rowCount}`);
|
|
16499
|
-
if (s.status === "success" && s.result?.type === "SELECT")
|
|
16671
|
+
if (s.status === "success" && s.result?.type === "SELECT") {
|
|
16672
|
+
parts.push(`rowCount=${s.result.rowCount}`);
|
|
16673
|
+
if (s.result.validateStats) {
|
|
16674
|
+
parts.push(`errorRecords=${s.result.validateStats.errorRecords} errorCount=${s.result.validateStats.errorCount}`);
|
|
16675
|
+
}
|
|
16676
|
+
}
|
|
16500
16677
|
if (s.status === "success" && s.result && s.result.type !== "SELECT") {
|
|
16501
16678
|
const r = s.result;
|
|
16502
16679
|
if (r.type === "INSERT") parts.push(`inserted=${r.insertedCount}`);
|
|
@@ -16517,6 +16694,10 @@ function buildBatchStatementSummary(s) {
|
|
|
16517
16694
|
if (s.status === "skipped" && s.skippedReason) parts.push(`reason=${s.skippedReason}`);
|
|
16518
16695
|
return parts.join(" ");
|
|
16519
16696
|
}
|
|
16697
|
+
function buildSelectSummary(result) {
|
|
16698
|
+
const validateSummary = result.validateStats ? ` errorRecords=${result.validateStats.errorRecords} errorCount=${result.validateStats.errorCount}` : "";
|
|
16699
|
+
return `rowCount=${result.rowCount}${validateSummary}`;
|
|
16700
|
+
}
|
|
16520
16701
|
function buildBatchDmlConfirmMessage(analysis) {
|
|
16521
16702
|
const lines = ["[DML Confirm] batch"];
|
|
16522
16703
|
for (const s of analysis.statements) {
|
|
@@ -17785,8 +17966,10 @@ query=${label}`);
|
|
|
17785
17966
|
`, "utf-8");
|
|
17786
17967
|
else if (output) process.stdout.write(`${output}
|
|
17787
17968
|
`);
|
|
17788
|
-
if (!quiet)
|
|
17969
|
+
if (!quiet) {
|
|
17970
|
+
process.stderr.write(`${buildSelectSummary(result)}
|
|
17789
17971
|
`);
|
|
17972
|
+
}
|
|
17790
17973
|
if (shouldExitOnEmpty(args.dryRun, exitOnEmpty, result.rowCount)) return 1;
|
|
17791
17974
|
return 0;
|
|
17792
17975
|
} catch (err) {
|
|
@@ -17821,6 +18004,7 @@ if (isDirectCliRun()) {
|
|
|
17821
18004
|
buildBatchStatementSummary,
|
|
17822
18005
|
buildOutput,
|
|
17823
18006
|
buildReplExecArgv,
|
|
18007
|
+
buildSelectSummary,
|
|
17824
18008
|
buildValidationOutput,
|
|
17825
18009
|
extractAppIds,
|
|
17826
18010
|
normalizeAppKey,
|
package/dist-mcp/ksql-mcp.js
CHANGED
|
@@ -32056,14 +32056,30 @@ var Parser = class {
|
|
|
32056
32056
|
const validateTok = this.advance();
|
|
32057
32057
|
const name = this.parseIdentifier();
|
|
32058
32058
|
const { appId, subtableCode } = extractTableRef(name, this.prev());
|
|
32059
|
-
if (subtableCode)
|
|
32060
|
-
|
|
32061
|
-
|
|
32062
|
-
|
|
32059
|
+
if (subtableCode) throw new ParseError(
|
|
32060
|
+
`VALIDATE APP${appId}$${subtableCode} \u306F\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002VALIDATE APP${appId} (${subtableCode}) \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044`,
|
|
32061
|
+
this.prev()
|
|
32062
|
+
);
|
|
32063
|
+
let targets;
|
|
32063
32064
|
if (this.consume("(" /* LPAREN */)) {
|
|
32064
|
-
|
|
32065
|
+
targets = [];
|
|
32066
|
+
do {
|
|
32067
|
+
const field = this.parseIdentifier();
|
|
32068
|
+
if (this.consume("(" /* LPAREN */)) {
|
|
32069
|
+
const children = this.peek().kind === ")" /* RPAREN */ ? [] : this.parseIdentList();
|
|
32070
|
+
this.expect(")" /* RPAREN */);
|
|
32071
|
+
targets.push({ kind: "SUBTABLE", subtableCode: field, children });
|
|
32072
|
+
} else {
|
|
32073
|
+
targets.push({ kind: "FIELD", field });
|
|
32074
|
+
}
|
|
32075
|
+
} while (this.consume("," /* COMMA */));
|
|
32065
32076
|
this.expect(")" /* RPAREN */);
|
|
32066
32077
|
}
|
|
32078
|
+
let summary;
|
|
32079
|
+
if (this.isSoftKeyword("SUMMARY")) {
|
|
32080
|
+
this.advance();
|
|
32081
|
+
summary = true;
|
|
32082
|
+
}
|
|
32067
32083
|
const where = this.consume("WHERE" /* WHERE */) ? this.parseWhereExpr() : null;
|
|
32068
32084
|
const checks = this.parseCheckGroups();
|
|
32069
32085
|
let errorTable;
|
|
@@ -32074,7 +32090,7 @@ var Parser = class {
|
|
|
32074
32090
|
}
|
|
32075
32091
|
errorTable = this.parseTableName();
|
|
32076
32092
|
}
|
|
32077
|
-
const stmt = { type: "VALIDATE", appId,
|
|
32093
|
+
const stmt = { type: "VALIDATE", appId, targets, ...summary ? { summary } : {}, where, ...checks, ...errorTable ? { errorTable } : {} };
|
|
32078
32094
|
this.assertValidateExpressions(stmt, validateTok);
|
|
32079
32095
|
return stmt;
|
|
32080
32096
|
}
|
|
@@ -35700,7 +35716,7 @@ function analyzeBatch(statements) {
|
|
|
35700
35716
|
dependsOn.add(at);
|
|
35701
35717
|
}
|
|
35702
35718
|
if (validationTable) {
|
|
35703
|
-
const payloadFields = stmt.type === "VALIDATE" ? ["$id", "$err_field", "$err_code", "$err_message", "$err_value"] : stmt.type === "IMPORT" && stmt.targets?.some((target) => target.kind === "SUBTABLE") ? [...new Set(stmt.targets.flatMap((target) => target.kind === "FIELD" ? [target.field] : target.children)), "$err_subtable", "$err_subrow", "$err_source_row"] : stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : "fields" in stmt ? stmt.fields : [];
|
|
35719
|
+
const payloadFields = stmt.type === "VALIDATE" ? stmt.summary ? ["$id", "$err_subtable", "$err_field", "$err_code", "$err_count"] : ["$id", "$err_field", "$err_code", "$err_message", "$err_value", "$err_subtable", "$err_subrow", "$err_subrow_id", "$err_count"] : stmt.type === "IMPORT" && stmt.targets?.some((target) => target.kind === "SUBTABLE") ? [...new Set(stmt.targets.flatMap((target) => target.kind === "FIELD" ? [target.field] : target.children)), "$err_subtable", "$err_subrow", "$err_source_row"] : stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : "fields" in stmt ? stmt.fields : [];
|
|
35704
35720
|
const signature = JSON.stringify(payloadFields);
|
|
35705
35721
|
const at = defined.get(validationTable);
|
|
35706
35722
|
if (at === void 0) {
|
|
@@ -38565,7 +38581,7 @@ function validateAndNormalizeDmlValue(raw, field, numberPrecision) {
|
|
|
38565
38581
|
return { ok: false, code: "ERR_LENGTH_MAX", message: `${field.code} \u306F ${max} \u6587\u5B57\u4EE5\u4E0B\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044` };
|
|
38566
38582
|
}
|
|
38567
38583
|
}
|
|
38568
|
-
if (CHOICE_TYPES.has(field.fieldType) && field.optionOrder) {
|
|
38584
|
+
if (!isEmpty(value) && CHOICE_TYPES.has(field.fieldType) && field.optionOrder) {
|
|
38569
38585
|
const selected = Array.isArray(value) ? value.map(String) : [String(value)];
|
|
38570
38586
|
if (selected.some((choice) => !(choice in field.optionOrder))) {
|
|
38571
38587
|
return { ok: false, code: "ERR_CHOICE_INVALID", message: `${field.code} \u306B\u5B9A\u7FA9\u5916\u306E\u9078\u629E\u80A2\u304C\u3042\u308A\u307E\u3059` };
|
|
@@ -40051,25 +40067,84 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
|
|
|
40051
40067
|
return executeAssert(stmt, client, options, cacheContext);
|
|
40052
40068
|
}
|
|
40053
40069
|
}
|
|
40054
|
-
var EXISTING_VALIDATION_COLUMNS = [
|
|
40070
|
+
var EXISTING_VALIDATION_COLUMNS = [
|
|
40071
|
+
"$id",
|
|
40072
|
+
"$err_field",
|
|
40073
|
+
"$err_code",
|
|
40074
|
+
"$err_message",
|
|
40075
|
+
"$err_value",
|
|
40076
|
+
"$err_subtable",
|
|
40077
|
+
"$err_subrow",
|
|
40078
|
+
"$err_subrow_id",
|
|
40079
|
+
"$err_count"
|
|
40080
|
+
];
|
|
40081
|
+
var EXISTING_VALIDATION_SUMMARY_COLUMNS = [
|
|
40082
|
+
"$id",
|
|
40083
|
+
"$err_subtable",
|
|
40084
|
+
"$err_field",
|
|
40085
|
+
"$err_code",
|
|
40086
|
+
"$err_count"
|
|
40087
|
+
];
|
|
40055
40088
|
function hasAuditableConstraint(field) {
|
|
40056
40089
|
return field.required === true || field.minValue !== void 0 || field.maxValue !== void 0 || field.minLength !== void 0 || field.maxLength !== void 0 || field.optionOrder !== void 0;
|
|
40057
40090
|
}
|
|
40058
40091
|
function resolveExistingValidationTargets(stmt, fieldInfos) {
|
|
40059
|
-
const
|
|
40060
|
-
const
|
|
40061
|
-
|
|
40092
|
+
const topByCode = new Map(fieldInfos.filter((field) => !field.inSubtable).map((field) => [field.code, field]));
|
|
40093
|
+
const childrenByTable = /* @__PURE__ */ new Map();
|
|
40094
|
+
for (const field of fieldInfos) {
|
|
40095
|
+
if (!field.inSubtable || !field.subtableCode) continue;
|
|
40096
|
+
const children = childrenByTable.get(field.subtableCode) ?? [];
|
|
40097
|
+
children.push(field);
|
|
40098
|
+
childrenByTable.set(field.subtableCode, children);
|
|
40099
|
+
}
|
|
40100
|
+
const auditable = (field) => field.fieldType === "NUMBER" || hasAuditableConstraint(field);
|
|
40101
|
+
if (stmt.targets === void 0) return [
|
|
40102
|
+
...fieldInfos.filter((field) => !field.inSubtable && field.fieldType !== "SUBTABLE" && auditable(field)),
|
|
40103
|
+
...fieldInfos.filter((field) => field.inSubtable && !!field.subtableCode && auditable(field))
|
|
40104
|
+
].map((field) => ({ field, ...field.subtableCode ? { subtableCode: field.subtableCode } : {} }));
|
|
40105
|
+
const result = [];
|
|
40062
40106
|
const seen = /* @__PURE__ */ new Set();
|
|
40063
|
-
|
|
40064
|
-
|
|
40065
|
-
seen.
|
|
40066
|
-
|
|
40067
|
-
|
|
40068
|
-
|
|
40069
|
-
|
|
40070
|
-
|
|
40071
|
-
|
|
40072
|
-
|
|
40107
|
+
const add = (field, subtableCode) => {
|
|
40108
|
+
const key = subtableCode ? `${subtableCode}\0${field.code}` : field.code;
|
|
40109
|
+
if (seen.has(key)) throw new Error(`ArgumentError: VALIDATE \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${field.code} \u304C\u91CD\u8907\u3057\u3066\u3044\u307E\u3059\u3002`);
|
|
40110
|
+
seen.add(key);
|
|
40111
|
+
if (!auditable(field)) throw new Error(`ArgumentError: VALIDATE \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${field.code} \u306B\u306F\u76E3\u67FB\u53EF\u80FD\u306A\u5236\u7D04\u304C\u3042\u308A\u307E\u305B\u3093\u3002`);
|
|
40112
|
+
result.push({ field, ...subtableCode ? { subtableCode } : {} });
|
|
40113
|
+
};
|
|
40114
|
+
for (const target of stmt.targets) {
|
|
40115
|
+
if (target.kind === "SUBTABLE") {
|
|
40116
|
+
const children = childrenByTable.get(target.subtableCode);
|
|
40117
|
+
if (!children) throw new Error(`ArgumentError: VALIDATE \u306E\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB ${target.subtableCode} \u306F\u5B58\u5728\u3057\u307E\u305B\u3093\u3002`);
|
|
40118
|
+
if (target.children.length === 0) throw new Error(`ArgumentError: VALIDATE \u306E\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB ${target.subtableCode} \u306B\u306F1\u3064\u4EE5\u4E0A\u306E\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u5FC5\u8981\u3067\u3059\u3002`);
|
|
40119
|
+
for (const code2 of target.children) {
|
|
40120
|
+
const child = children.find((field) => field.code === code2);
|
|
40121
|
+
if (!child) {
|
|
40122
|
+
const belongsElsewhere = [...childrenByTable.entries()].some(([table, fields]) => table !== target.subtableCode && fields.some((field) => field.code === code2));
|
|
40123
|
+
throw new Error(belongsElsewhere ? `ArgumentError: VALIDATE \u306E\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9 ${code2} \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB ${target.subtableCode} \u306B\u5C5E\u3057\u3066\u3044\u307E\u305B\u3093\u3002` : `ArgumentError: VALIDATE \u306E\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9 ${code2} \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB ${target.subtableCode} \u306B\u5B58\u5728\u3057\u307E\u305B\u3093\u3002`);
|
|
40124
|
+
}
|
|
40125
|
+
add(child, target.subtableCode);
|
|
40126
|
+
}
|
|
40127
|
+
continue;
|
|
40128
|
+
}
|
|
40129
|
+
const code = target.field;
|
|
40130
|
+
if (code === "$id") throw new Error("ArgumentError: VALIDATE \u3067\u306F\u30B7\u30B9\u30C6\u30E0\u30D5\u30A3\u30FC\u30EB\u30C9 $id \u3092\u76E3\u67FB\u3067\u304D\u307E\u305B\u3093\u3002");
|
|
40131
|
+
const top = topByCode.get(code);
|
|
40132
|
+
if (top?.fieldType === "SUBTABLE") {
|
|
40133
|
+
const children = (childrenByTable.get(code) ?? []).filter(auditable);
|
|
40134
|
+
if (children.length === 0) throw new Error(`ArgumentError: VALIDATE \u306E\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB ${code} \u306B\u306F\u76E3\u67FB\u53EF\u80FD\u306A\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u3042\u308A\u307E\u305B\u3093\u3002`);
|
|
40135
|
+
children.forEach((child) => add(child, code));
|
|
40136
|
+
continue;
|
|
40137
|
+
}
|
|
40138
|
+
if (top) {
|
|
40139
|
+
add(top);
|
|
40140
|
+
continue;
|
|
40141
|
+
}
|
|
40142
|
+
if ([...childrenByTable.values()].some((children) => children.some((field) => field.code === code))) {
|
|
40143
|
+
throw new Error(`ArgumentError: VALIDATE \u306E\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9 ${code} \u306F\u6240\u6709\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u3092\u542B\u3080 T(${code}) \u5F62\u5F0F\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002`);
|
|
40144
|
+
}
|
|
40145
|
+
throw new Error(`ArgumentError: VALIDATE \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${code} \u306F\u5B58\u5728\u3057\u307E\u305B\u3093\u3002`);
|
|
40146
|
+
}
|
|
40147
|
+
return result;
|
|
40073
40148
|
}
|
|
40074
40149
|
function collectValidateWhereFields(where) {
|
|
40075
40150
|
const fields = [];
|
|
@@ -40094,11 +40169,12 @@ function collectValidateWhereFields(where) {
|
|
|
40094
40169
|
visit(where);
|
|
40095
40170
|
return fields;
|
|
40096
40171
|
}
|
|
40097
|
-
function existingValidationColumnMeta() {
|
|
40098
|
-
|
|
40099
|
-
|
|
40100
|
-
|
|
40101
|
-
|
|
40172
|
+
function existingValidationColumnMeta(summary = false) {
|
|
40173
|
+
const columns = summary ? EXISTING_VALIDATION_SUMMARY_COLUMNS : EXISTING_VALIDATION_COLUMNS;
|
|
40174
|
+
return new Map(columns.map((column) => [column, {
|
|
40175
|
+
fieldType: column === "$id" || column === "$err_count" ? "KSQL_NUMBER" : "KSQL_STRING",
|
|
40176
|
+
sortKind: column === "$id" || column === "$err_count" ? "number" : "string",
|
|
40177
|
+
semantics: syntheticSemantics(column === "$id" || column === "$err_count" ? "number" : "string")
|
|
40102
40178
|
}]));
|
|
40103
40179
|
}
|
|
40104
40180
|
async function executeExistingRecordValidation(stmt, client, options, cacheContext) {
|
|
@@ -40107,38 +40183,45 @@ async function executeExistingRecordValidation(stmt, client, options, cacheConte
|
|
|
40107
40183
|
}
|
|
40108
40184
|
async function executeExistingRecordValidationCore(stmt, client, options, cacheContext) {
|
|
40109
40185
|
const fieldInfos = await getFieldsCached(stmt.appId, client, cacheContext);
|
|
40110
|
-
const infoByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
40186
|
+
const infoByCode = new Map(fieldInfos.filter((field) => !field.inSubtable).map((field) => [field.code, field]));
|
|
40187
|
+
const childCodes = new Set(fieldInfos.filter((field) => field.inSubtable).map((field) => field.code));
|
|
40111
40188
|
const targets = resolveExistingValidationTargets(stmt, fieldInfos);
|
|
40112
40189
|
const checkGroups = stmt.checkGroups ?? [];
|
|
40113
40190
|
const checkRefs2 = collectCheckFieldRefs(checkGroups);
|
|
40114
40191
|
for (const ref of checkRefs2) {
|
|
40115
|
-
if (ref.field !== "$id" && !infoByCode.has(ref.field)) {
|
|
40192
|
+
if (ref.field !== "$id" && !infoByCode.has(ref.field) && !childCodes.has(ref.field)) {
|
|
40116
40193
|
throw customCheckParseError(`CHECK \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F APP${stmt.appId} \u306B\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
40117
40194
|
}
|
|
40195
|
+
if (ref.field !== "$id" && !infoByCode.has(ref.field) && childCodes.has(ref.field)) {
|
|
40196
|
+
throw new Error(`ArgumentError: VALIDATE \u306E CHECK \u3067\u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u3092\u53C2\u7167\u3067\u304D\u307E\u305B\u3093\u3002`);
|
|
40197
|
+
}
|
|
40118
40198
|
}
|
|
40119
|
-
const evaluationTypes = new Map(
|
|
40199
|
+
const evaluationTypes = new Map([...infoByCode].map(([code, field]) => [code, field.fieldType]));
|
|
40120
40200
|
evaluationTypes.set("$id", "RECORD_NUMBER");
|
|
40121
40201
|
assertCheckComparisonTypes(stmt, evaluationTypes);
|
|
40122
40202
|
const whereFields = collectValidateWhereFields(stmt.where);
|
|
40123
40203
|
const requiredFields = [.../* @__PURE__ */ new Set([
|
|
40124
40204
|
"$id",
|
|
40125
|
-
...targets.map((
|
|
40205
|
+
...targets.map((target) => target.subtableCode ?? target.field.code),
|
|
40126
40206
|
...whereFields,
|
|
40127
40207
|
...checkRefs2.map((ref) => ref.field)
|
|
40128
40208
|
])];
|
|
40129
40209
|
for (const field of whereFields) {
|
|
40130
|
-
if (field !== "$id" && !infoByCode.has(field)) {
|
|
40210
|
+
if (field !== "$id" && !infoByCode.has(field) && !childCodes.has(field)) {
|
|
40131
40211
|
throw new Error(`ArgumentError: WHERE field ${field} does not exist in APP${stmt.appId}.`);
|
|
40132
40212
|
}
|
|
40213
|
+
if (field !== "$id" && !infoByCode.has(field) && childCodes.has(field)) {
|
|
40214
|
+
throw new Error(`ArgumentError: VALIDATE \u306E WHERE \u3067\u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9 ${field} \u3092\u53C2\u7167\u3067\u304D\u307E\u305B\u3093\u3002`);
|
|
40215
|
+
}
|
|
40133
40216
|
}
|
|
40134
|
-
const numberPrecision = targets.some((
|
|
40217
|
+
const numberPrecision = targets.some((target) => target.field.fieldType === "NUMBER") ? await getNumberPrecisionCached(stmt.appId, client, cacheContext) : void 0;
|
|
40135
40218
|
const semantics = (field) => field.field === "$id" ? resolveFieldSemantics({ fieldType: "__ID__" }) : infoByCode.get(field.field)?.semantics ?? (infoByCode.has(field.field) ? resolveFieldSemantics(infoByCode.get(field.field)) : void 0);
|
|
40136
40219
|
const capability = classifyWhereCapability(stmt.where, semantics);
|
|
40137
40220
|
if (capability.capability === "UNSUPPORTED") {
|
|
40138
40221
|
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
|
|
40139
40222
|
}
|
|
40140
|
-
const fieldTypes = new Map(
|
|
40141
|
-
const fieldOptions = new Map(
|
|
40223
|
+
const fieldTypes = new Map([...infoByCode].map(([code, field]) => [code, field.fieldType]));
|
|
40224
|
+
const fieldOptions = new Map([...infoByCode.values()].flatMap((field) => field.optionOrder ? [[field.code, new Set(Object.keys(field.optionOrder))]] : []));
|
|
40142
40225
|
const prefilter = stmt.where === null ? null : capability.capability === "EXACT_PUSHDOWN" ? stmt.where : extractSafePushdownLeaves(stmt.where, {
|
|
40143
40226
|
allowUnqualifiedFields: true,
|
|
40144
40227
|
fieldTypes,
|
|
@@ -40157,36 +40240,110 @@ async function executeExistingRecordValidationCore(stmt, client, options, cacheC
|
|
|
40157
40240
|
flat: flatten(record2, null)
|
|
40158
40241
|
})).filter((row) => stmt.where === null || evalWhere(stmt.where, row.flat, (field) => evaluationTypes.get(field.field)));
|
|
40159
40242
|
const rows = [];
|
|
40243
|
+
const detailRows = /* @__PURE__ */ new Map();
|
|
40244
|
+
const summaryRows = /* @__PURE__ */ new Map();
|
|
40245
|
+
const errorRecordIds = /* @__PURE__ */ new Set();
|
|
40246
|
+
let errorCount = 0;
|
|
40247
|
+
const appendError = (error51) => {
|
|
40248
|
+
errorRecordIds.add(error51.id);
|
|
40249
|
+
errorCount += 1;
|
|
40250
|
+
if (stmt.summary) {
|
|
40251
|
+
const key2 = JSON.stringify([error51.id, error51.subtable ?? "", error51.field, error51.code]);
|
|
40252
|
+
const current2 = summaryRows.get(key2);
|
|
40253
|
+
if (current2) current2["$err_count"] = String(Number(current2["$err_count"]) + 1);
|
|
40254
|
+
else summaryRows.set(key2, {
|
|
40255
|
+
"$id": error51.id,
|
|
40256
|
+
"$err_subtable": error51.subtable ?? "",
|
|
40257
|
+
"$err_field": error51.field,
|
|
40258
|
+
"$err_code": error51.code,
|
|
40259
|
+
"$err_count": "1"
|
|
40260
|
+
});
|
|
40261
|
+
return;
|
|
40262
|
+
}
|
|
40263
|
+
const key = JSON.stringify([error51.id, error51.subtable ?? "", error51.field, error51.code, error51.message]);
|
|
40264
|
+
const current = detailRows.get(key);
|
|
40265
|
+
if (current) {
|
|
40266
|
+
current["$err_count"] = String(Number(current["$err_count"]) + 1);
|
|
40267
|
+
if (error51.subrow !== void 0) {
|
|
40268
|
+
current["$err_subrow"] = `${current["$err_subrow"]},${error51.subrow}`;
|
|
40269
|
+
current["$err_subrow_id"] = `${current["$err_subrow_id"]},${error51.subrowId ?? ""}`;
|
|
40270
|
+
}
|
|
40271
|
+
} else detailRows.set(key, {
|
|
40272
|
+
"$id": error51.id,
|
|
40273
|
+
"$err_field": error51.field,
|
|
40274
|
+
"$err_code": error51.code,
|
|
40275
|
+
"$err_message": error51.message,
|
|
40276
|
+
"$err_value": error51.value,
|
|
40277
|
+
"$err_subtable": error51.subtable ?? "",
|
|
40278
|
+
"$err_subrow": error51.subrow === void 0 ? "" : String(error51.subrow),
|
|
40279
|
+
"$err_subrow_id": error51.subrowId ?? "",
|
|
40280
|
+
"$err_count": "1"
|
|
40281
|
+
});
|
|
40282
|
+
};
|
|
40283
|
+
const topTargets = targets.filter((target) => !target.subtableCode);
|
|
40284
|
+
const subtableTargets = /* @__PURE__ */ new Map();
|
|
40285
|
+
for (const target of targets) {
|
|
40286
|
+
if (!target.subtableCode) continue;
|
|
40287
|
+
const children = subtableTargets.get(target.subtableCode) ?? [];
|
|
40288
|
+
children.push(target);
|
|
40289
|
+
subtableTargets.set(target.subtableCode, children);
|
|
40290
|
+
}
|
|
40160
40291
|
for (const row of validationRows) {
|
|
40161
|
-
for (const
|
|
40162
|
-
const raw = row.record[field.code]?.value;
|
|
40163
|
-
const validation = validateAndNormalizeDmlValue(raw, field, numberPrecision);
|
|
40164
|
-
if (validation.ok)
|
|
40165
|
-
|
|
40166
|
-
|
|
40167
|
-
|
|
40168
|
-
|
|
40169
|
-
|
|
40170
|
-
"$err_value": renderExistingValidationValue(raw, field.fieldType)
|
|
40292
|
+
for (const target of topTargets) {
|
|
40293
|
+
const raw = row.record[target.field.code]?.value;
|
|
40294
|
+
const validation = validateAndNormalizeDmlValue(raw, target.field, numberPrecision);
|
|
40295
|
+
if (!validation.ok) appendError({
|
|
40296
|
+
id: row.id,
|
|
40297
|
+
field: target.field.code,
|
|
40298
|
+
code: validation.code,
|
|
40299
|
+
message: validation.message,
|
|
40300
|
+
value: renderExistingValidationValue(raw, target.field.fieldType)
|
|
40171
40301
|
});
|
|
40172
40302
|
}
|
|
40303
|
+
for (const [tableCode, childTargets] of subtableTargets) {
|
|
40304
|
+
const tableRows2 = row.record[tableCode]?.value;
|
|
40305
|
+
if (!Array.isArray(tableRows2)) continue;
|
|
40306
|
+
for (let i = 0; i < tableRows2.length; i++) {
|
|
40307
|
+
const tableRow = tableRows2[i];
|
|
40308
|
+
for (const target of childTargets) {
|
|
40309
|
+
const raw = tableRow.value?.[target.field.code]?.value;
|
|
40310
|
+
const validation = validateAndNormalizeDmlValue(raw, target.field, numberPrecision);
|
|
40311
|
+
if (!validation.ok) appendError({
|
|
40312
|
+
id: row.id,
|
|
40313
|
+
field: target.field.code,
|
|
40314
|
+
code: validation.code,
|
|
40315
|
+
message: validation.message,
|
|
40316
|
+
value: renderExistingValidationValue(raw, target.field.fieldType),
|
|
40317
|
+
subtable: tableCode,
|
|
40318
|
+
subrow: i + 1,
|
|
40319
|
+
subrowId: String(tableRow.id ?? "")
|
|
40320
|
+
});
|
|
40321
|
+
}
|
|
40322
|
+
}
|
|
40323
|
+
}
|
|
40173
40324
|
for (const check2 of evaluateCustomChecks(checkGroups, row.flat, (field) => evaluationTypes.get(field.field))) {
|
|
40174
|
-
|
|
40175
|
-
|
|
40176
|
-
|
|
40177
|
-
|
|
40178
|
-
|
|
40179
|
-
|
|
40180
|
-
|
|
40325
|
+
appendError({ id: row.id, field: "", code: "ERR_CHECK", message: check2.message, value: "" });
|
|
40326
|
+
}
|
|
40327
|
+
}
|
|
40328
|
+
if (stmt.summary) rows.push(...summaryRows.values());
|
|
40329
|
+
else {
|
|
40330
|
+
for (const row of detailRows.values()) {
|
|
40331
|
+
const count = Number(row["$err_count"]);
|
|
40332
|
+
if (row["$err_subtable"] !== "" && count >= 2) {
|
|
40333
|
+
row["$err_message"] = `${row["$err_message"]}\uFF08${count}\u884C: ${row["$err_subrow"]}\uFF09`;
|
|
40334
|
+
}
|
|
40335
|
+
rows.push(row);
|
|
40181
40336
|
}
|
|
40182
40337
|
}
|
|
40338
|
+
const columns = stmt.summary ? EXISTING_VALIDATION_SUMMARY_COLUMNS : EXISTING_VALIDATION_COLUMNS;
|
|
40183
40339
|
const result = {
|
|
40184
40340
|
type: "SELECT",
|
|
40185
|
-
columns: [...
|
|
40341
|
+
columns: [...columns],
|
|
40186
40342
|
rows,
|
|
40187
|
-
rowCount: rows.length
|
|
40343
|
+
rowCount: rows.length,
|
|
40344
|
+
validateStats: { errorRecords: errorRecordIds.size, errorCount }
|
|
40188
40345
|
};
|
|
40189
|
-
materializedMetaBySelectResult.set(result, existingValidationColumnMeta());
|
|
40346
|
+
materializedMetaBySelectResult.set(result, existingValidationColumnMeta(stmt.summary === true));
|
|
40190
40347
|
return result;
|
|
40191
40348
|
}
|
|
40192
40349
|
var TEMP_TABLE_MAX_ROWS = 1e4;
|
|
@@ -40383,7 +40540,7 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
40383
40540
|
result.columns,
|
|
40384
40541
|
result.rows,
|
|
40385
40542
|
options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS,
|
|
40386
|
-
materializedMetaBySelectResult.get(result) ?? existingValidationColumnMeta()
|
|
40543
|
+
materializedMetaBySelectResult.get(result) ?? existingValidationColumnMeta(resolvedStmt.summary === true)
|
|
40387
40544
|
);
|
|
40388
40545
|
}
|
|
40389
40546
|
return { result };
|
|
@@ -44498,43 +44655,51 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
44498
44655
|
const validate = node;
|
|
44499
44656
|
fieldApps.add(validate.appId);
|
|
44500
44657
|
const fields = await getFieldsCached(validate.appId, tracedClient, cacheContext);
|
|
44501
|
-
const infoByCode = new Map(fields.map((field) => [field.code, field]));
|
|
44658
|
+
const infoByCode = new Map(fields.filter((field) => !field.inSubtable).map((field) => [field.code, field]));
|
|
44659
|
+
const childCodes = new Set(fields.filter((field) => field.inSubtable).map((field) => field.code));
|
|
44502
44660
|
const targets = resolveExistingValidationTargets(validate, fields);
|
|
44503
44661
|
const checks = collectCheckFieldRefs(validate.checkGroups ?? []);
|
|
44504
44662
|
const whereFields = collectValidateWhereFields(validate.where);
|
|
44505
44663
|
for (const ref of checks) {
|
|
44506
|
-
if (ref.field !== "$id" && !infoByCode.has(ref.field)) {
|
|
44664
|
+
if (ref.field !== "$id" && !infoByCode.has(ref.field) && !childCodes.has(ref.field)) {
|
|
44507
44665
|
throw customCheckParseError(`CHECK \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F APP${validate.appId} \u306B\u5B58\u5728\u3057\u307E\u305B\u3093`);
|
|
44508
44666
|
}
|
|
44667
|
+
if (ref.field !== "$id" && !infoByCode.has(ref.field) && childCodes.has(ref.field)) {
|
|
44668
|
+
throw new Error(`ArgumentError: VALIDATE \u306E CHECK \u3067\u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u3092\u53C2\u7167\u3067\u304D\u307E\u305B\u3093\u3002`);
|
|
44669
|
+
}
|
|
44509
44670
|
}
|
|
44510
44671
|
for (const field of whereFields) {
|
|
44511
|
-
if (field !== "$id" && !infoByCode.has(field)) {
|
|
44672
|
+
if (field !== "$id" && !infoByCode.has(field) && !childCodes.has(field)) {
|
|
44512
44673
|
throw new Error(`ArgumentError: WHERE field ${field} does not exist in APP${validate.appId}.`);
|
|
44513
44674
|
}
|
|
44675
|
+
if (field !== "$id" && !infoByCode.has(field) && childCodes.has(field)) {
|
|
44676
|
+
throw new Error(`ArgumentError: VALIDATE \u306E WHERE \u3067\u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u5B50\u30D5\u30A3\u30FC\u30EB\u30C9 ${field} \u3092\u53C2\u7167\u3067\u304D\u307E\u305B\u3093\u3002`);
|
|
44677
|
+
}
|
|
44514
44678
|
}
|
|
44515
|
-
const types = new Map(
|
|
44679
|
+
const types = new Map([...infoByCode].map(([code, field]) => [code, field.fieldType]));
|
|
44516
44680
|
types.set("$id", "RECORD_NUMBER");
|
|
44517
44681
|
assertCheckComparisonTypes(validate, types);
|
|
44518
44682
|
const capability = classifyWhereCapability(validate.where, (field) => field.field === "$id" ? resolveFieldSemantics({ fieldType: "__ID__" }) : infoByCode.get(field.field)?.semantics ?? (infoByCode.has(field.field) ? resolveFieldSemantics(infoByCode.get(field.field)) : void 0));
|
|
44519
44683
|
if (capability.capability === "UNSUPPORTED") {
|
|
44520
44684
|
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
|
|
44521
44685
|
}
|
|
44522
|
-
const fieldTypes = new Map(
|
|
44523
|
-
const fieldOptions = new Map(
|
|
44686
|
+
const fieldTypes = new Map([...infoByCode].map(([code, field]) => [code, field.fieldType]));
|
|
44687
|
+
const fieldOptions = new Map([...infoByCode.values()].flatMap((field) => field.optionOrder ? [[field.code, new Set(Object.keys(field.optionOrder))]] : []));
|
|
44524
44688
|
const prefilter = validate.where === null ? null : capability.capability === "EXACT_PUSHDOWN" ? validate.where : extractSafePushdownLeaves(validate.where, {
|
|
44525
44689
|
allowUnqualifiedFields: true,
|
|
44526
44690
|
fieldTypes,
|
|
44527
44691
|
fieldOptions,
|
|
44528
44692
|
allowKlike: false
|
|
44529
44693
|
});
|
|
44530
|
-
const needsPrecision = targets.some((
|
|
44694
|
+
const needsPrecision = targets.some((target) => target.field.fieldType === "NUMBER");
|
|
44531
44695
|
if (needsPrecision) {
|
|
44532
44696
|
numberPrecisionApps.add(validate.appId);
|
|
44533
44697
|
await getNumberPrecisionCached(validate.appId, tracedClient, cacheContext);
|
|
44534
44698
|
}
|
|
44535
44699
|
validateExplainInfo.set(validate, {
|
|
44536
|
-
targetFields: targets.map((
|
|
44537
|
-
fetchFields: [.../* @__PURE__ */ new Set(["$id", ...targets.map((
|
|
44700
|
+
targetFields: targets.map((target) => target.subtableCode ? `${target.subtableCode}(${target.field.code})` : target.field.code),
|
|
44701
|
+
fetchFields: [.../* @__PURE__ */ new Set(["$id", ...targets.map((target) => target.subtableCode ?? target.field.code), ...whereFields, ...checks.map((ref) => ref.field)])],
|
|
44702
|
+
subtables: new Map([...new Set(targets.flatMap((target) => target.subtableCode ? [target.subtableCode] : []))].map((table) => [table, targets.filter((target) => target.subtableCode === table).length])),
|
|
44538
44703
|
capability,
|
|
44539
44704
|
prefilter,
|
|
44540
44705
|
numberPrecision: needsPrecision
|
|
@@ -44829,6 +44994,10 @@ function buildValidatePlan(stmt, label) {
|
|
|
44829
44994
|
lines.push(` kintone query: ${info.prefilter === null ? "(\u5168\u4EF6\u53D6\u5F97)" : whereToKintone(info.prefilter)}`);
|
|
44830
44995
|
lines.push(` audit fields: ${info.targetFields.length === 0 ? "(\u306A\u3057)" : info.targetFields.join(", ")}`);
|
|
44831
44996
|
lines.push(` fetch fields: ${info.fetchFields.join(", ")}`);
|
|
44997
|
+
lines.push(` mode: ${stmt.summary ? "SUMMARY" : "DETAIL"}`);
|
|
44998
|
+
if (info.subtables.size > 0) lines.push(` subtable audit: ${[...info.subtables].map(([table, count]) => `${table}(${count} fields)`).join(", ")}`);
|
|
44999
|
+
lines.push(` output schema: ${(stmt.summary ? EXISTING_VALIDATION_SUMMARY_COLUMNS : EXISTING_VALIDATION_COLUMNS).join(", ")}`);
|
|
45000
|
+
lines.push(stmt.summary ? " aggregation: record/subtable/field/code; row locator=none" : " row locator: grouped by message; $err_subrow / $err_subrow_id list all matching rows (first-occurrence order)");
|
|
44832
45001
|
lines.push(` number precision: ${info.numberPrecision ? "required" : "not required"}`);
|
|
44833
45002
|
lines.push(" local checks: original WHERE re-evaluation + built-in constraints + CHECK groups");
|
|
44834
45003
|
lines.push(" records/mutation API during EXPLAIN: none; violation count unavailable");
|
|
@@ -45336,7 +45505,8 @@ function buildBatchEnvelope(batch, options = {}) {
|
|
|
45336
45505
|
columns: s.result.columns,
|
|
45337
45506
|
rows: s.result.rows,
|
|
45338
45507
|
rowCount: s.result.rowCount,
|
|
45339
|
-
warnings: s.result.warnings ?? []
|
|
45508
|
+
warnings: s.result.warnings ?? [],
|
|
45509
|
+
...s.result.validateStats ? { validateStats: s.result.validateStats } : {}
|
|
45340
45510
|
});
|
|
45341
45511
|
} else if (s.result?.type === "VALIDATION") {
|
|
45342
45512
|
totalRows += s.result.errorCount;
|
|
@@ -47157,7 +47327,8 @@ function toSelectPayload(result) {
|
|
|
47157
47327
|
columns: result.columns,
|
|
47158
47328
|
rows: result.rows,
|
|
47159
47329
|
rowCount: result.rowCount,
|
|
47160
|
-
warnings: result.warnings ?? []
|
|
47330
|
+
warnings: result.warnings ?? [],
|
|
47331
|
+
...result.validateStats ? { validateStats: result.validateStats } : {}
|
|
47161
47332
|
};
|
|
47162
47333
|
}
|
|
47163
47334
|
function toAssertPayload(result) {
|
|
@@ -47937,7 +48108,7 @@ Nested JSON/CSV subtable mutation is fail-closed on MCP: use VALIDATE ONLY/EXPLA
|
|
|
47937
48108
|
JSON child IDs are rejected and replacement renumbers all rows.
|
|
47938
48109
|
`);
|
|
47939
48110
|
}
|
|
47940
|
-
var SERVER_VERSION = true ? "3.
|
|
48111
|
+
var SERVER_VERSION = true ? "3.7.0" : "0.0.0-dev";
|
|
47941
48112
|
function createServer(args) {
|
|
47942
48113
|
const server = new McpServer({
|
|
47943
48114
|
name: "ksql-mcp",
|
package/dist-mcpb/ksql-mcp.mcpb
CHANGED
|
Binary file
|