@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 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
- throw new ParseError("VALIDATE \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u4EEE\u60F3\u30C6\u30FC\u30D6\u30EB\u3092\u5BFE\u8C61\u306B\u3067\u304D\u307E\u305B\u3093", this.prev());
1151
- }
1152
- let fields;
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
- fields = this.parseIdentList();
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, fields, where, ...checks, ...errorTable ? { errorTable } : {} };
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 = ["$id", "$err_field", "$err_code", "$err_message", "$err_value"];
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 byCode = new Map(fieldInfos.map((field) => [field.code, field]));
9162
- const auditable = (field) => !field.inSubtable && (field.fieldType === "NUMBER" || hasAuditableConstraint(field));
9163
- if (stmt.fields === void 0) return fieldInfos.filter(auditable);
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
- return stmt.fields.map((code) => {
9166
- if (seen.has(code)) throw new Error(`ArgumentError: VALIDATE field ${code} is duplicated.`);
9167
- seen.add(code);
9168
- if (code === "$id") throw new Error("ArgumentError: VALIDATE cannot audit system field $id.");
9169
- const info = byCode.get(code);
9170
- if (!info) throw new Error(`ArgumentError: VALIDATE field ${code} does not exist.`);
9171
- if (info.inSubtable) throw new Error(`ArgumentError: VALIDATE field ${code} is a subtable child field.`);
9172
- if (!auditable(info)) throw new Error(`ArgumentError: VALIDATE field ${code} has no auditable constraint.`);
9173
- return info;
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
- return new Map(EXISTING_VALIDATION_COLUMNS.map((column) => [column, {
9201
- fieldType: column === "$id" ? "KSQL_NUMBER" : "KSQL_STRING",
9202
- sortKind: column === "$id" ? "number" : "string",
9203
- semantics: syntheticSemantics(column === "$id" ? "number" : "string")
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(fieldInfos.map((field) => [field.code, field.fieldType]));
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((field) => field.code),
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((field) => field.fieldType === "NUMBER") ? await getNumberPrecisionCached(stmt.appId, client, cacheContext) : void 0;
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(fieldInfos.map((field) => [field.code, field.fieldType]));
9243
- const fieldOptions = new Map(fieldInfos.flatMap((field) => field.optionOrder ? [[field.code, new Set(Object.keys(field.optionOrder))]] : []));
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 field of targets) {
9264
- const raw = row.record[field.code]?.value;
9265
- const validation = validateAndNormalizeDmlValue(raw, field, numberPrecision);
9266
- if (validation.ok) continue;
9267
- rows.push({
9268
- "$id": row.id,
9269
- "$err_field": field.code,
9270
- "$err_code": validation.code,
9271
- "$err_message": validation.message,
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
- rows.push({
9277
- "$id": row.id,
9278
- "$err_field": "",
9279
- "$err_code": "ERR_CHECK",
9280
- "$err_message": check.message,
9281
- "$err_value": ""
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: [...EXISTING_VALIDATION_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(fields.map((field) => [field.code, field.fieldType]));
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(fields.map((field) => [field.code, field.fieldType]));
13625
- const fieldOptions = new Map(fields.flatMap((field) => field.optionOrder ? [[field.code, new Set(Object.keys(field.optionOrder))]] : []));
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((field) => field.fieldType === "NUMBER");
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((field) => field.code),
13639
- fetchFields: [.../* @__PURE__ */ new Set(["$id", ...targets.map((field) => field.code), ...whereFields, ...checks.map((ref) => ref.field)])],
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") parts.push(`rowCount=${s.result.rowCount}`);
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) process.stderr.write(`rowCount=${result.rowCount}
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,
@@ -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
- throw new ParseError("VALIDATE \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u4EEE\u60F3\u30C6\u30FC\u30D6\u30EB\u3092\u5BFE\u8C61\u306B\u3067\u304D\u307E\u305B\u3093", this.prev());
32061
- }
32062
- let fields;
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
- fields = this.parseIdentList();
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, fields, where, ...checks, ...errorTable ? { errorTable } : {} };
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 = ["$id", "$err_field", "$err_code", "$err_message", "$err_value"];
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 byCode = new Map(fieldInfos.map((field) => [field.code, field]));
40060
- const auditable = (field) => !field.inSubtable && (field.fieldType === "NUMBER" || hasAuditableConstraint(field));
40061
- if (stmt.fields === void 0) return fieldInfos.filter(auditable);
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
- return stmt.fields.map((code) => {
40064
- if (seen.has(code)) throw new Error(`ArgumentError: VALIDATE field ${code} is duplicated.`);
40065
- seen.add(code);
40066
- if (code === "$id") throw new Error("ArgumentError: VALIDATE cannot audit system field $id.");
40067
- const info = byCode.get(code);
40068
- if (!info) throw new Error(`ArgumentError: VALIDATE field ${code} does not exist.`);
40069
- if (info.inSubtable) throw new Error(`ArgumentError: VALIDATE field ${code} is a subtable child field.`);
40070
- if (!auditable(info)) throw new Error(`ArgumentError: VALIDATE field ${code} has no auditable constraint.`);
40071
- return info;
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
- return new Map(EXISTING_VALIDATION_COLUMNS.map((column) => [column, {
40099
- fieldType: column === "$id" ? "KSQL_NUMBER" : "KSQL_STRING",
40100
- sortKind: column === "$id" ? "number" : "string",
40101
- semantics: syntheticSemantics(column === "$id" ? "number" : "string")
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(fieldInfos.map((field) => [field.code, field.fieldType]));
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((field) => field.code),
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((field) => field.fieldType === "NUMBER") ? await getNumberPrecisionCached(stmt.appId, client, cacheContext) : void 0;
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(fieldInfos.map((field) => [field.code, field.fieldType]));
40141
- const fieldOptions = new Map(fieldInfos.flatMap((field) => field.optionOrder ? [[field.code, new Set(Object.keys(field.optionOrder))]] : []));
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 field of targets) {
40162
- const raw = row.record[field.code]?.value;
40163
- const validation = validateAndNormalizeDmlValue(raw, field, numberPrecision);
40164
- if (validation.ok) continue;
40165
- rows.push({
40166
- "$id": row.id,
40167
- "$err_field": field.code,
40168
- "$err_code": validation.code,
40169
- "$err_message": validation.message,
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
- rows.push({
40175
- "$id": row.id,
40176
- "$err_field": "",
40177
- "$err_code": "ERR_CHECK",
40178
- "$err_message": check2.message,
40179
- "$err_value": ""
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: [...EXISTING_VALIDATION_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(fields.map((field) => [field.code, field.fieldType]));
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(fields.map((field) => [field.code, field.fieldType]));
44523
- const fieldOptions = new Map(fields.flatMap((field) => field.optionOrder ? [[field.code, new Set(Object.keys(field.optionOrder))]] : []));
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((field) => field.fieldType === "NUMBER");
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((field) => field.code),
44537
- fetchFields: [.../* @__PURE__ */ new Set(["$id", ...targets.map((field) => field.code), ...whereFields, ...checks.map((ref) => ref.field)])],
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.6.1" : "0.0.0-dev";
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",
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rex0220/kintone-sql-tools",
3
- "version": "3.6.1",
3
+ "version": "3.7.0",
4
4
  "description": "kintone SQL plugin, CLI, and MCP tools (ksql)",
5
5
  "publishConfig": {
6
6
  "access": "public"