@rex0220/kintone-sql-tools 3.5.0 → 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.
@@ -1199,8 +1199,8 @@ var require_util = __commonJS({
1199
1199
  })(Type || (exports2.Type = Type = {}));
1200
1200
  function getErrorPath(dataProp, dataPropType, jsPropertySyntax) {
1201
1201
  if (dataProp instanceof codegen_1.Name) {
1202
- const isNumber = dataPropType === Type.Num;
1203
- return jsPropertySyntax ? isNumber ? (0, codegen_1._)`"[" + ${dataProp} + "]"` : (0, codegen_1._)`"['" + ${dataProp} + "']"` : isNumber ? (0, codegen_1._)`"/" + ${dataProp}` : (0, codegen_1._)`"/" + ${dataProp}.replace(/~/g, "~0").replace(/\\//g, "~1")`;
1202
+ const isNumber2 = dataPropType === Type.Num;
1203
+ return jsPropertySyntax ? isNumber2 ? (0, codegen_1._)`"[" + ${dataProp} + "]"` : (0, codegen_1._)`"['" + ${dataProp} + "']"` : isNumber2 ? (0, codegen_1._)`"/" + ${dataProp}` : (0, codegen_1._)`"/" + ${dataProp}.replace(/~/g, "~0").replace(/\\//g, "~1")`;
1204
1204
  }
1205
1205
  return jsPropertySyntax ? (0, codegen_1.getProperty)(dataProp).toString() : "/" + escapeJsonPointer(dataProp);
1206
1206
  }
@@ -7267,15 +7267,15 @@ var makeIssue = (params) => {
7267
7267
  message: issueData.message
7268
7268
  };
7269
7269
  }
7270
- let errorMessage = "";
7270
+ let errorMessage2 = "";
7271
7271
  const maps = errorMaps.filter((m) => !!m).slice().reverse();
7272
7272
  for (const map2 of maps) {
7273
- errorMessage = map2(fullIssue, { data, defaultError: errorMessage }).message;
7273
+ errorMessage2 = map2(fullIssue, { data, defaultError: errorMessage2 }).message;
7274
7274
  }
7275
7275
  return {
7276
7276
  ...issueData,
7277
7277
  path: fullPath,
7278
- message: errorMessage
7278
+ message: errorMessage2
7279
7279
  };
7280
7280
  };
7281
7281
  function addIssueToContext(ctx, issueData) {
@@ -27067,19 +27067,19 @@ var getRefs = (options) => {
27067
27067
  };
27068
27068
 
27069
27069
  // node_modules/zod-to-json-schema/dist/esm/errorMessages.js
27070
- function addErrorMessage(res, key, errorMessage, refs) {
27070
+ function addErrorMessage(res, key, errorMessage2, refs) {
27071
27071
  if (!refs?.errorMessages)
27072
27072
  return;
27073
- if (errorMessage) {
27073
+ if (errorMessage2) {
27074
27074
  res.errorMessage = {
27075
27075
  ...res.errorMessage,
27076
- [key]: errorMessage
27076
+ [key]: errorMessage2
27077
27077
  };
27078
27078
  }
27079
27079
  }
27080
- function setResponseValueAndErrors(res, key, value, errorMessage, refs) {
27080
+ function setResponseValueAndErrors(res, key, value, errorMessage2, refs) {
27081
27081
  res[key] = value;
27082
- addErrorMessage(res, key, errorMessage, refs);
27082
+ addErrorMessage(res, key, errorMessage2, refs);
27083
27083
  }
27084
27084
 
27085
27085
  // node_modules/zod-to-json-schema/dist/esm/getRelativePath.js
@@ -28390,8 +28390,8 @@ var Protocol = class {
28390
28390
  if (queuedMessage.type === "response") {
28391
28391
  resolver(message);
28392
28392
  } else {
28393
- const errorMessage = message;
28394
- const error51 = new McpError(errorMessage.error.code, errorMessage.error.message, errorMessage.error.data);
28393
+ const errorMessage2 = message;
28394
+ const error51 = new McpError(errorMessage2.error.code, errorMessage2.error.message, errorMessage2.error.data);
28395
28395
  resolver(error51);
28396
28396
  }
28397
28397
  } else {
@@ -29691,23 +29691,23 @@ var Server = class extends Protocol {
29691
29691
  const wrappedHandler = async (request, extra) => {
29692
29692
  const validatedRequest = safeParse2(CallToolRequestSchema, request);
29693
29693
  if (!validatedRequest.success) {
29694
- const errorMessage = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
29695
- throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage}`);
29694
+ const errorMessage2 = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
29695
+ throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage2}`);
29696
29696
  }
29697
29697
  const { params } = validatedRequest.data;
29698
29698
  const result = await Promise.resolve(handler(request, extra));
29699
29699
  if (params.task) {
29700
29700
  const taskValidationResult = safeParse2(CreateTaskResultSchema, result);
29701
29701
  if (!taskValidationResult.success) {
29702
- const errorMessage = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error);
29703
- throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage}`);
29702
+ const errorMessage2 = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error);
29703
+ throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage2}`);
29704
29704
  }
29705
29705
  return taskValidationResult.data;
29706
29706
  }
29707
29707
  const validationResult = safeParse2(CallToolResultSchema, result);
29708
29708
  if (!validationResult.success) {
29709
- const errorMessage = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
29710
- throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call result: ${errorMessage}`);
29709
+ const errorMessage2 = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
29710
+ throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call result: ${errorMessage2}`);
29711
29711
  }
29712
29712
  return validationResult.data;
29713
29713
  };
@@ -30201,12 +30201,12 @@ var McpServer = class {
30201
30201
  * @param errorMessage - The error message.
30202
30202
  * @returns The tool error result.
30203
30203
  */
30204
- createToolError(errorMessage) {
30204
+ createToolError(errorMessage2) {
30205
30205
  return {
30206
30206
  content: [
30207
30207
  {
30208
30208
  type: "text",
30209
- text: errorMessage
30209
+ text: errorMessage2
30210
30210
  }
30211
30211
  ],
30212
30212
  isError: true
@@ -30224,8 +30224,8 @@ var McpServer = class {
30224
30224
  const parseResult = await safeParseAsync2(schemaToParse, args);
30225
30225
  if (!parseResult.success) {
30226
30226
  const error51 = "error" in parseResult ? parseResult.error : "Unknown error";
30227
- const errorMessage = getParseErrorMessage(error51);
30228
- throw new McpError(ErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${errorMessage}`);
30227
+ const errorMessage2 = getParseErrorMessage(error51);
30228
+ throw new McpError(ErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${errorMessage2}`);
30229
30229
  }
30230
30230
  return parseResult.data;
30231
30231
  }
@@ -30249,8 +30249,8 @@ var McpServer = class {
30249
30249
  const parseResult = await safeParseAsync2(outputObj, result.structuredContent);
30250
30250
  if (!parseResult.success) {
30251
30251
  const error51 = "error" in parseResult ? parseResult.error : "Unknown error";
30252
- const errorMessage = getParseErrorMessage(error51);
30253
- throw new McpError(ErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage}`);
30252
+ const errorMessage2 = getParseErrorMessage(error51);
30253
+ throw new McpError(ErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage2}`);
30254
30254
  }
30255
30255
  }
30256
30256
  /**
@@ -30462,8 +30462,8 @@ var McpServer = class {
30462
30462
  const parseResult = await safeParseAsync2(argsObj, request.params.arguments);
30463
30463
  if (!parseResult.success) {
30464
30464
  const error51 = "error" in parseResult ? parseResult.error : "Unknown error";
30465
- const errorMessage = getParseErrorMessage(error51);
30466
- throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for prompt ${request.params.name}: ${errorMessage}`);
30465
+ const errorMessage2 = getParseErrorMessage(error51);
30466
+ throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for prompt ${request.params.name}: ${errorMessage2}`);
30467
30467
  }
30468
30468
  const args = parseResult.data;
30469
30469
  const cb = prompt.callback;
@@ -31563,8 +31563,9 @@ var ParseError = class extends Error {
31563
31563
  }
31564
31564
  };
31565
31565
  var Parser = class {
31566
- constructor(tokens) {
31566
+ constructor(tokens, capabilities = {}) {
31567
31567
  this.tokens = tokens;
31568
+ this.capabilities = capabilities;
31568
31569
  this.allowUnaryPlusNumber = false;
31569
31570
  this.scalarAllowsAggregateArgs = true;
31570
31571
  this.scalarAllowsCase = true;
@@ -31657,6 +31658,12 @@ var Parser = class {
31657
31658
  if (upper === "DROP") return this.parseDropTempTable();
31658
31659
  if (upper === "DECLARE") return this.parseDeclareVariable();
31659
31660
  if (upper === "VALIDATE") return this.parseValidate();
31661
+ if (upper === "IMPORT") {
31662
+ if (!this.capabilities.import) {
31663
+ throw new ParseError("IMPORT is not supported (capability is disabled).", tok);
31664
+ }
31665
+ return this.parseImport();
31666
+ }
31660
31667
  break;
31661
31668
  }
31662
31669
  default:
@@ -31837,24 +31844,242 @@ var Parser = class {
31837
31844
  query = this.parseReorder();
31838
31845
  } else if (tok.kind === "IDENT" /* IDENT */ && tok.value.toUpperCase() === "VALIDATE") {
31839
31846
  query = this.parseValidate();
31847
+ } else if (tok.kind === "IDENT" /* IDENT */ && tok.value.toUpperCase() === "IMPORT") {
31848
+ if (!this.capabilities.import) {
31849
+ throw new ParseError("IMPORT is not supported (capability is disabled).", tok);
31850
+ }
31851
+ query = this.parseImport();
31840
31852
  } else {
31841
31853
  throw new ParseError("EXPLAIN \u306E\u5F8C\u306B\u306F SELECT / WITH / INSERT / UPSERT / UPDATE / DELETE / REORDER / VALIDATE \u304C\u5FC5\u8981\u3067\u3059", tok);
31842
31854
  }
31843
31855
  return { type: "EXPLAIN", query };
31844
31856
  }
31857
+ parseImport() {
31858
+ this.advance();
31859
+ let writeMode;
31860
+ if (this.peek().kind === "UPDATE" /* UPDATE */) {
31861
+ this.advance();
31862
+ writeMode = "UPDATE_RECORD_NUMBER";
31863
+ }
31864
+ this.expect("INTO" /* INTO */);
31865
+ this.rejectTempTableDml();
31866
+ const target = this.parseIdentifier();
31867
+ const { appId, subtableCode } = extractTableRef(target, this.prev());
31868
+ if (subtableCode) throw new ParseError("IMPORT does not support subtables in Phase 1.", this.prev());
31869
+ this.expect("(" /* LPAREN */);
31870
+ const targets = [];
31871
+ const fields = [];
31872
+ const targetNames = /* @__PURE__ */ new Set();
31873
+ while (true) {
31874
+ const name = this.parseIdentifier();
31875
+ if (targetNames.has(name)) throw new ParseError(`IMPORT target ${name} is declared more than once.`, this.prev());
31876
+ targetNames.add(name);
31877
+ if (this.peek().kind === "(" /* LPAREN */) {
31878
+ this.advance();
31879
+ const children = this.parseIdentList();
31880
+ this.expect(")" /* RPAREN */);
31881
+ if (new Set(children).size !== children.length) {
31882
+ throw new ParseError(`IMPORT subtable ${name} contains duplicate child declarations.`, this.prev());
31883
+ }
31884
+ let rowIdSourceHeader;
31885
+ if (this.isSoftKeyword("ROW")) {
31886
+ this.advance();
31887
+ for (const word of ["ID", "SOURCE"]) {
31888
+ if (!this.isSoftKeyword(word)) throw new ParseError(`ROW must be followed by ID SOURCE <header>.`, this.peek());
31889
+ this.advance();
31890
+ }
31891
+ rowIdSourceHeader = this.parseIdentifier();
31892
+ }
31893
+ targets.push({ kind: "SUBTABLE", subtableCode: name, children, ...rowIdSourceHeader ? { rowIdSourceHeader } : {} });
31894
+ } else {
31895
+ fields.push(name);
31896
+ targets.push({ kind: "FIELD", field: name });
31897
+ }
31898
+ if (this.peek().kind !== "," /* COMMA */) break;
31899
+ this.advance();
31900
+ }
31901
+ this.expect(")" /* RPAREN */);
31902
+ this.expect("FROM" /* FROM */);
31903
+ if (!this.isSoftKeyword("CSV") && !this.isSoftKeyword("JSON")) throw new ParseError("IMPORT FROM requires CSV or JSON.", this.peek());
31904
+ const sourceKind = this.peek().value.toUpperCase();
31905
+ this.advance();
31906
+ const sourceName = this.parseIdentifier();
31907
+ let encoding;
31908
+ let hasHeader = true;
31909
+ let columns;
31910
+ if (this.isSoftKeyword("ENCODING")) {
31911
+ if (sourceKind === "JSON") throw new ParseError("JSON source is UTF-8 only; ENCODING is not allowed.", this.peek());
31912
+ this.advance();
31913
+ const value = this.parseIdentifier().toUpperCase();
31914
+ if (value !== "UTF8" && value !== "SJIS") throw new ParseError("ENCODING must be UTF8 or SJIS.", this.prev());
31915
+ encoding = value === "UTF8" ? "utf8" : "sjis";
31916
+ }
31917
+ if (this.peek().kind === "NOT" /* NOT */ && this.peekAt(1).kind === "IDENT" /* IDENT */ && this.peekAt(1).value.toUpperCase() === "HEADER") {
31918
+ if (sourceKind === "JSON") throw new ParseError("NO HEADER is CSV-only.", this.peek());
31919
+ this.advance();
31920
+ this.advance();
31921
+ hasHeader = false;
31922
+ } else if (this.isSoftKeyword("NO") && this.peekAt(1).kind === "IDENT" /* IDENT */ && this.peekAt(1).value.toUpperCase() === "HEADER") {
31923
+ if (sourceKind === "JSON") throw new ParseError("NO HEADER is CSV-only.", this.peek());
31924
+ this.advance();
31925
+ this.advance();
31926
+ hasHeader = false;
31927
+ }
31928
+ if (this.isSoftKeyword("COLUMNS")) {
31929
+ if (sourceKind === "JSON") throw new ParseError("COLUMNS is CSV-only.", this.peek());
31930
+ if (hasHeader) throw new ParseError("COLUMNS requires NO HEADER.", this.peek());
31931
+ this.advance();
31932
+ this.expect("(" /* LPAREN */);
31933
+ columns = this.parseIdentList();
31934
+ this.expect(")" /* RPAREN */);
31935
+ }
31936
+ let projection;
31937
+ if (this.peek().kind === "SELECT" /* SELECT */) {
31938
+ if (sourceKind === "JSON") throw new ParseError("SELECT projection is CSV-only.", this.peek());
31939
+ projection = this.parseSelect();
31940
+ if (projection.from.cteName !== NO_FROM_CTE_NAME || projection.joins.length > 0) {
31941
+ throw new ParseError("IMPORT projection cannot use FROM or JOIN.", this.prev());
31942
+ }
31943
+ if (projection.where || projection.groupBy.length || projection.having || projection.orderBy.length || projection.limit !== null || projection.offset !== null) {
31944
+ throw new ParseError("IMPORT projection supports SELECT expressions only.", this.prev());
31945
+ }
31946
+ this.validateImportProjectionScope(projection, this.prev());
31947
+ if (targets.some((item) => item.kind === "SUBTABLE")) {
31948
+ throw new ParseError("IMPORT subtable sources cannot use SELECT projection.", this.prev());
31949
+ }
31950
+ if (projection.columns.length !== fields.length) {
31951
+ throw new ParseError(`IMPORT projection has ${projection.columns.length} columns; target has ${fields.length}.`, this.prev());
31952
+ }
31953
+ }
31954
+ let mappingMode = "POSITION";
31955
+ let ignoreUnknownColumns = false;
31956
+ if (this.peek().kind === "BY" /* BY */ || this.isSoftKeyword("BY")) {
31957
+ if (sourceKind === "JSON") throw new ParseError("BY NAME is CSV-only.", this.peek());
31958
+ this.advance();
31959
+ if (!this.isSoftKeyword("NAME")) throw new ParseError("BY must be followed by NAME in IMPORT.", this.peek());
31960
+ this.advance();
31961
+ if (!hasHeader) throw new ParseError("BY NAME requires HEADER.", this.prev());
31962
+ if (projection) throw new ParseError("BY NAME and SELECT projection are mutually exclusive.", this.prev());
31963
+ mappingMode = "BY_NAME";
31964
+ if (this.isSoftKeyword("IGNORE")) {
31965
+ this.advance();
31966
+ if (!this.isSoftKeyword("UNKNOWN")) throw new ParseError("IGNORE must be followed by UNKNOWN COLUMNS.", this.peek());
31967
+ this.advance();
31968
+ if (!this.isSoftKeyword("COLUMNS")) throw new ParseError("IGNORE UNKNOWN must be followed by COLUMNS.", this.peek());
31969
+ this.advance();
31970
+ ignoreUnknownColumns = true;
31971
+ }
31972
+ }
31973
+ let keyFields;
31974
+ let recordNumberSourceHeader;
31975
+ if (this.isSoftKeyword("MATCH")) {
31976
+ this.advance();
31977
+ for (const word of ["RECORD", "NUMBER", "SOURCE"]) {
31978
+ if (!this.isSoftKeyword(word)) throw new ParseError(`MATCH must be followed by RECORD NUMBER SOURCE <header>.`, this.peek());
31979
+ this.advance();
31980
+ }
31981
+ recordNumberSourceHeader = this.parseIdentifier();
31982
+ }
31983
+ if (this.peek().kind === "ON" /* ON */ && this.peekAt(1).kind === "DUPLICATE" /* DUPLICATE */) keyFields = this.parseOnDuplicate();
31984
+ let replaceSubtables;
31985
+ if (this.peek().kind === "REPLACE" /* REPLACE */ || this.isSoftKeyword("REPLACE")) {
31986
+ this.advance();
31987
+ if (!this.isSoftKeyword("SUBTABLES")) throw new ParseError("REPLACE must be followed by SUBTABLES (...).", this.peek());
31988
+ this.advance();
31989
+ this.expect("(" /* LPAREN */);
31990
+ replaceSubtables = this.parseIdentList();
31991
+ this.expect(")" /* RPAREN */);
31992
+ if (new Set(replaceSubtables).size !== replaceSubtables.length) throw new ParseError("REPLACE SUBTABLES contains duplicates.", this.prev());
31993
+ }
31994
+ const subtableTargets = targets.filter((item) => item.kind === "SUBTABLE");
31995
+ if (subtableTargets.length) {
31996
+ if (projection) throw new ParseError("IMPORT subtables cannot use SELECT projection.", this.prev());
31997
+ if (sourceKind === "JSON") {
31998
+ if (subtableTargets.some((item) => item.rowIdSourceHeader)) throw new ParseError("JSON subtable IMPORT does not accept ROW ID SOURCE.", this.prev());
31999
+ if (replaceSubtables) throw new ParseError("REPLACE SUBTABLES is CSV-only; JSON uses nested-array replacement semantics.", this.prev());
32000
+ } else {
32001
+ if (writeMode !== "UPDATE_RECORD_NUMBER" || !recordNumberSourceHeader) throw new ParseError("CSV subtable IMPORT requires IMPORT UPDATE and MATCH RECORD NUMBER SOURCE.", this.prev());
32002
+ if (mappingMode !== "BY_NAME") throw new ParseError("CSV subtable IMPORT requires BY NAME.", this.prev());
32003
+ if (!replaceSubtables) throw new ParseError("CSV subtable IMPORT requires REPLACE SUBTABLES (...).", this.prev());
32004
+ const replacement = new Set(replaceSubtables);
32005
+ for (const item of subtableTargets) {
32006
+ if (!item.rowIdSourceHeader) throw new ParseError(`CSV subtable ${item.subtableCode} requires ROW ID SOURCE <header>.`, this.prev());
32007
+ if (!replacement.has(item.subtableCode)) throw new ParseError(`IMPORT declares child columns for non-replaced subtable ${item.subtableCode}.`, this.prev());
32008
+ }
32009
+ for (const code of replacement) {
32010
+ if (!subtableTargets.some((item) => item.subtableCode === code)) throw new ParseError(`REPLACE SUBTABLES target ${code} is not declared in INTO.`, this.prev());
32011
+ }
32012
+ }
32013
+ } else if (replaceSubtables) {
32014
+ throw new ParseError("REPLACE SUBTABLES requires subtable targets in INTO.", this.prev());
32015
+ }
32016
+ if (writeMode) {
32017
+ if (sourceKind !== "CSV") throw new ParseError("IMPORT UPDATE supports CSV only.", this.prev());
32018
+ if (mappingMode !== "BY_NAME") throw new ParseError("IMPORT UPDATE requires BY NAME.", this.prev());
32019
+ if (!recordNumberSourceHeader) throw new ParseError("IMPORT UPDATE requires MATCH RECORD NUMBER SOURCE <header>.", this.peek());
32020
+ if (keyFields) throw new ParseError("IMPORT UPDATE and ON DUPLICATE are mutually exclusive.", this.prev());
32021
+ } else if (recordNumberSourceHeader) {
32022
+ throw new ParseError("MATCH RECORD NUMBER SOURCE requires IMPORT UPDATE.", this.prev());
32023
+ }
32024
+ const checkGroups = this.parseCheckGroups();
32025
+ const control = this.parseDmlControlSuffix();
32026
+ return {
32027
+ type: "IMPORT",
32028
+ appId,
32029
+ fields,
32030
+ targets,
32031
+ source: sourceKind === "JSON" ? { kind: "JSON", sourceName } : { kind: "CSV", sourceName, encoding, hasHeader, mappingMode, ignoreUnknownColumns, ...columns ? { columns } : {}, ...projection ? { projection } : {} },
32032
+ ...writeMode ? { writeMode, recordNumberSourceHeader } : {},
32033
+ ...replaceSubtables ? { replaceSubtables } : {},
32034
+ ...keyFields ? { keyFields } : {},
32035
+ ...checkGroups,
32036
+ ...control
32037
+ };
32038
+ }
32039
+ validateImportProjectionScope(node, token) {
32040
+ if (Array.isArray(node)) {
32041
+ node.forEach((item) => this.validateImportProjectionScope(item, token));
32042
+ return;
32043
+ }
32044
+ if (node === null || typeof node !== "object") return;
32045
+ const value = node;
32046
+ if (value.type === "SCALAR_SUBQUERY" || value.type === "SCALAR_SUBQUERY_COL") {
32047
+ throw new ParseError("IMPORT projection cannot use subqueries.", token);
32048
+ }
32049
+ if (typeof value.tableAlias === "string") {
32050
+ throw new ParseError("IMPORT projection cannot use qualified column references.", token);
32051
+ }
32052
+ Object.values(value).forEach((item) => this.validateImportProjectionScope(item, token));
32053
+ }
31845
32054
  /** VALIDATE APP100 [(fields)] [WHERE ...] [CHECK ...] [INTO #err]. */
31846
32055
  parseValidate() {
31847
32056
  const validateTok = this.advance();
31848
32057
  const name = this.parseIdentifier();
31849
32058
  const { appId, subtableCode } = extractTableRef(name, this.prev());
31850
- if (subtableCode) {
31851
- 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());
31852
- }
31853
- 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;
31854
32064
  if (this.consume("(" /* LPAREN */)) {
31855
- 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 */));
31856
32076
  this.expect(")" /* RPAREN */);
31857
32077
  }
32078
+ let summary;
32079
+ if (this.isSoftKeyword("SUMMARY")) {
32080
+ this.advance();
32081
+ summary = true;
32082
+ }
31858
32083
  const where = this.consume("WHERE" /* WHERE */) ? this.parseWhereExpr() : null;
31859
32084
  const checks = this.parseCheckGroups();
31860
32085
  let errorTable;
@@ -31865,7 +32090,7 @@ var Parser = class {
31865
32090
  }
31866
32091
  errorTable = this.parseTableName();
31867
32092
  }
31868
- const stmt = { type: "VALIDATE", appId, fields, where, ...checks, ...errorTable ? { errorTable } : {} };
32093
+ const stmt = { type: "VALIDATE", appId, targets, ...summary ? { summary } : {}, where, ...checks, ...errorTable ? { errorTable } : {} };
31869
32094
  this.assertValidateExpressions(stmt, validateTok);
31870
32095
  return stmt;
31871
32096
  }
@@ -34006,7 +34231,7 @@ function getStatementType(stmt) {
34006
34231
  return typeof obj.type === "string" ? obj.type : "UNKNOWN";
34007
34232
  }
34008
34233
  function isDmlType(type) {
34009
- return type === "INSERT" || type === "INSERT_SELECT" || type === "UPDATE" || type === "DELETE" || type === "UPSERT" || type === "UPSERT_SELECT" || type === "REORDER";
34234
+ return type === "INSERT" || type === "INSERT_SELECT" || type === "UPDATE" || type === "DELETE" || type === "UPSERT" || type === "UPSERT_SELECT" || type === "REORDER" || type === "IMPORT";
34010
34235
  }
34011
34236
  function isReadOnlyType(type) {
34012
34237
  return type === "SELECT" || type === "VALIDATE" || type === "UNION" || type === "WITH" || type === "EXPLAIN" || type === "SHOW_APPS" || type === "DESCRIBE" || type === "CREATE_TEMP_TABLE" || type === "DROP_TEMP_TABLE" || type === "SET_VARIABLE" || type === "DECLARE_VARIABLE" || type === "ASSERT";
@@ -35491,7 +35716,7 @@ function analyzeBatch(statements) {
35491
35716
  dependsOn.add(at);
35492
35717
  }
35493
35718
  if (validationTable) {
35494
- const payloadFields = stmt.type === "VALIDATE" ? ["$id", "$err_field", "$err_code", "$err_message", "$err_value"] : 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 : [];
35495
35720
  const signature = JSON.stringify(payloadFields);
35496
35721
  const at = defined.get(validationTable);
35497
35722
  if (at === void 0) {
@@ -38356,7 +38581,7 @@ function validateAndNormalizeDmlValue(raw, field, numberPrecision) {
38356
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` };
38357
38582
  }
38358
38583
  }
38359
- if (CHOICE_TYPES.has(field.fieldType) && field.optionOrder) {
38584
+ if (!isEmpty(value) && CHOICE_TYPES.has(field.fieldType) && field.optionOrder) {
38360
38585
  const selected = Array.isArray(value) ? value.map(String) : [String(value)];
38361
38586
  if (selected.some((choice) => !(choice in field.optionOrder))) {
38362
38587
  return { ok: false, code: "ERR_CHOICE_INVALID", message: `${field.code} \u306B\u5B9A\u7FA9\u5916\u306E\u9078\u629E\u80A2\u304C\u3042\u308A\u307E\u3059` };
@@ -38453,9 +38678,15 @@ function validateDmlCandidates(candidates, operation, payloadFields, targetField
38453
38678
  candidate.record ??= {};
38454
38679
  const rowErrors = includePreErrors ? [...candidate.preErrors] : [];
38455
38680
  for (const code of targetFields) {
38681
+ if (!candidate.payload.has(code)) continue;
38456
38682
  const result = validateAndNormalizeDmlValue(candidate.payload.get(code), infoByCode.get(code), numberPrecision);
38457
38683
  if (!result.ok) rowErrors.push({ field: code, code: result.code, message: result.message });
38458
- else candidate.record[code] = { value: result.value };
38684
+ else {
38685
+ const original = candidate.payload.get(code);
38686
+ const type = infoByCode.get(code).fieldType;
38687
+ const preserveCodes = ["USER_SELECT", "ORGANIZATION_SELECT", "GROUP_SELECT"].includes(type) && Array.isArray(original) && original.every((item) => typeof item === "object" && item !== null && "code" in item);
38688
+ candidate.record[code] = { value: preserveCodes ? original : result.value };
38689
+ }
38459
38690
  }
38460
38691
  if (validateMissingCreateFields && candidate.mode === "create") {
38461
38692
  for (const info of fieldInfos) {
@@ -38717,6 +38948,862 @@ function unsupported(code, field, fieldType, operator) {
38717
38948
  return { capability: "UNSUPPORTED", reasons: [{ code, field, fieldType, operator }] };
38718
38949
  }
38719
38950
 
38951
+ // src/import/sourceLoader.ts
38952
+ var IMPORT_MAX_BYTES = 10 * 1024 * 1024;
38953
+ var ImportSourceError = class extends Error {
38954
+ constructor(message) {
38955
+ super(`ImportSourceError: ${message}`);
38956
+ this.name = "ImportSourceError";
38957
+ }
38958
+ };
38959
+ function resolveImportSource(name, resolver) {
38960
+ if (!resolver) throw new ImportSourceError("IMPORT source capability is not available.");
38961
+ const handle = resolver(name);
38962
+ if (!handle) throw new ImportSourceError(`source "${name}" is not supplied.`);
38963
+ return handle;
38964
+ }
38965
+ async function loadImportSource(handle, cache) {
38966
+ let pending = cache.get(handle);
38967
+ if (!pending) {
38968
+ pending = handle.load().then((payload) => {
38969
+ if (!(payload.bytes instanceof Uint8Array)) throw new ImportSourceError("loader must return Uint8Array bytes.");
38970
+ if (payload.bytes.byteLength > IMPORT_MAX_BYTES) {
38971
+ throw new ImportSourceError(`source exceeds the ${IMPORT_MAX_BYTES} byte limit.`);
38972
+ }
38973
+ return payload;
38974
+ });
38975
+ cache.set(handle, pending);
38976
+ }
38977
+ return pending;
38978
+ }
38979
+
38980
+ // src/import/csvDecoder.ts
38981
+ function decodeImportText(bytes, encoding) {
38982
+ try {
38983
+ return new TextDecoder(encoding === "sjis" ? "shift_jis" : "utf-8", { fatal: true }).decode(bytes).replace(/^\uFEFF/, "");
38984
+ } catch {
38985
+ throw new ImportSourceError(`invalid ${encoding.toUpperCase()} byte sequence.`);
38986
+ }
38987
+ }
38988
+ function parseRfc4180(text) {
38989
+ const records = [];
38990
+ let record2 = [];
38991
+ let cell = "";
38992
+ let quoted = false;
38993
+ let afterQuote = false;
38994
+ let i = 0;
38995
+ const finishCell = () => {
38996
+ record2.push(cell);
38997
+ cell = "";
38998
+ afterQuote = false;
38999
+ };
39000
+ const finishRecord = () => {
39001
+ finishCell();
39002
+ records.push(record2);
39003
+ record2 = [];
39004
+ };
39005
+ while (i < text.length) {
39006
+ const ch = text[i];
39007
+ if (quoted) {
39008
+ if (ch === '"') {
39009
+ if (text[i + 1] === '"') {
39010
+ cell += '"';
39011
+ i += 2;
39012
+ continue;
39013
+ }
39014
+ quoted = false;
39015
+ afterQuote = true;
39016
+ i++;
39017
+ continue;
39018
+ }
39019
+ cell += ch;
39020
+ i++;
39021
+ continue;
39022
+ }
39023
+ if (afterQuote && ch !== "," && ch !== "\r" && ch !== "\n") {
39024
+ throw new ImportSourceError(`unexpected character after closing quote at offset ${i}.`);
39025
+ }
39026
+ if (ch === '"') {
39027
+ if (cell.length !== 0) throw new ImportSourceError(`quote in unquoted cell at offset ${i}.`);
39028
+ quoted = true;
39029
+ i++;
39030
+ continue;
39031
+ }
39032
+ if (ch === ",") {
39033
+ finishCell();
39034
+ i++;
39035
+ continue;
39036
+ }
39037
+ if (ch === "\r" || ch === "\n") {
39038
+ if (ch === "\r" && text[i + 1] === "\n") i++;
39039
+ finishRecord();
39040
+ i++;
39041
+ continue;
39042
+ }
39043
+ cell += ch;
39044
+ i++;
39045
+ }
39046
+ if (quoted) throw new ImportSourceError("unterminated quoted cell.");
39047
+ if (cell.length > 0 || record2.length > 0 || afterQuote) finishRecord();
39048
+ return records;
39049
+ }
39050
+ function assertColumns(columns) {
39051
+ const seen = /* @__PURE__ */ new Set();
39052
+ columns.forEach((column, index) => {
39053
+ if (column === "") throw new ImportSourceError(`CSV column ${index + 1} has an empty name.`);
39054
+ if (seen.has(column)) throw new ImportSourceError(`CSV column name "${column}" is duplicated.`);
39055
+ seen.add(column);
39056
+ });
39057
+ }
39058
+ function decodeCsv(bytes, options) {
39059
+ const records = parseRfc4180(decodeImportText(bytes, options.encoding));
39060
+ let columns;
39061
+ let rows;
39062
+ if (options.hasHeader) {
39063
+ columns = records[0] ?? [];
39064
+ rows = records.slice(1);
39065
+ } else {
39066
+ rows = records;
39067
+ columns = options.columns ? [...options.columns] : Array.from({ length: rows[0]?.length ?? 0 }, (_, i) => `c${i + 1}`);
39068
+ }
39069
+ assertColumns(columns);
39070
+ if (rows.length === 0) throw new ImportSourceError("CSV has no data rows.");
39071
+ rows.forEach((row, i) => {
39072
+ if (row.length !== columns.length) {
39073
+ throw new ImportSourceError(`CSV row ${i + (options.hasHeader ? 2 : 1)} has ${row.length} cells; expected ${columns.length}.`);
39074
+ }
39075
+ });
39076
+ return { columns, rows };
39077
+ }
39078
+
39079
+ // src/import/convertImportCsvValue.ts
39080
+ var LF_MULTI_TYPES = /* @__PURE__ */ new Set([
39081
+ "CHECK_BOX",
39082
+ "MULTI_SELECT",
39083
+ "USER_SELECT",
39084
+ "ORGANIZATION_SELECT",
39085
+ "GROUP_SELECT"
39086
+ ]);
39087
+ var USER_TYPES2 = /* @__PURE__ */ new Set(["USER_SELECT", "ORGANIZATION_SELECT", "GROUP_SELECT"]);
39088
+ var ImportCsvValueError = class extends Error {
39089
+ constructor() {
39090
+ super("multiple-value CSV cell contains an empty LF-delimited item");
39091
+ this.code = "ERR_IMPORT_MULTI_EMPTY_ITEM";
39092
+ this.name = "ImportCsvValueError";
39093
+ }
39094
+ };
39095
+ function convertImportCsvValue(raw, type, options) {
39096
+ void options;
39097
+ if (!LF_MULTI_TYPES.has(type ?? "")) return raw;
39098
+ if (raw === "") return [];
39099
+ const items = raw.split(/\r\n|\n/);
39100
+ if (items.some((item) => item === "")) throw new ImportCsvValueError();
39101
+ return USER_TYPES2.has(type ?? "") ? items.map((code) => ({ code })) : items;
39102
+ }
39103
+
39104
+ // src/import/jsonTokenizer.ts
39105
+ function fail(message, offset, line, column) {
39106
+ throw new ImportSourceError(`JSON ${message} (offset=${offset}, line=${line}, column=${column}).`);
39107
+ }
39108
+ function decodeUtf8Json(bytes) {
39109
+ try {
39110
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
39111
+ } catch {
39112
+ throw new ImportSourceError("JSON source is not valid UTF-8.");
39113
+ }
39114
+ }
39115
+ function tokenizeJson(text) {
39116
+ const tokens = [];
39117
+ let i = 0, line = 1, column = 1;
39118
+ const advance = () => {
39119
+ const ch = text[i++];
39120
+ if (ch === "\n") {
39121
+ line++;
39122
+ column = 1;
39123
+ } else column++;
39124
+ return ch;
39125
+ };
39126
+ const position = () => ({ offset: i, line, column });
39127
+ while (i < text.length) {
39128
+ const ch = text[i];
39129
+ if (ch === " " || ch === " " || ch === "\r" || ch === "\n") {
39130
+ advance();
39131
+ continue;
39132
+ }
39133
+ const start = position();
39134
+ if ("{}[]:,".includes(ch)) {
39135
+ advance();
39136
+ tokens.push({ kind: "punct", value: ch, ...start });
39137
+ continue;
39138
+ }
39139
+ if (ch === '"') {
39140
+ advance();
39141
+ let value = "";
39142
+ let closed = false;
39143
+ while (i < text.length) {
39144
+ const c = advance();
39145
+ if (c === '"') {
39146
+ closed = true;
39147
+ break;
39148
+ }
39149
+ if (c.charCodeAt(0) < 32) fail("string contains an unescaped control character", start.offset, start.line, start.column);
39150
+ if (c !== "\\") {
39151
+ value += c;
39152
+ continue;
39153
+ }
39154
+ if (i >= text.length) fail("string has an unterminated escape", start.offset, start.line, start.column);
39155
+ const esc2 = advance();
39156
+ const simple = { '"': '"', "\\": "\\", "/": "/", b: "\b", f: "\f", n: "\n", r: "\r", t: " " };
39157
+ if (esc2 in simple) {
39158
+ value += simple[esc2];
39159
+ continue;
39160
+ }
39161
+ if (esc2 !== "u") fail(`has invalid escape \\${esc2}`, i - 2, line, Math.max(1, column - 2));
39162
+ const hex3 = text.slice(i, i + 4);
39163
+ if (!/^[0-9a-fA-F]{4}$/.test(hex3)) fail("has invalid unicode escape", i, line, column);
39164
+ for (let n = 0; n < 4; n++) advance();
39165
+ const code = Number.parseInt(hex3, 16);
39166
+ if (code >= 55296 && code <= 56319) {
39167
+ if (text.slice(i, i + 2) !== "\\u" || !/^[0-9a-fA-F]{4}$/.test(text.slice(i + 2, i + 6))) fail("has an unpaired high surrogate", i, line, column);
39168
+ advance();
39169
+ advance();
39170
+ const lowHex = text.slice(i, i + 4);
39171
+ for (let n = 0; n < 4; n++) advance();
39172
+ const low = Number.parseInt(lowHex, 16);
39173
+ if (low < 56320 || low > 57343) fail("has an invalid surrogate pair", i - 4, line, Math.max(1, column - 4));
39174
+ value += String.fromCodePoint(65536 + (code - 55296 << 10) + low - 56320);
39175
+ } else if (code >= 56320 && code <= 57343) {
39176
+ fail("has an unpaired low surrogate", i - 4, line, Math.max(1, column - 4));
39177
+ } else value += String.fromCharCode(code);
39178
+ }
39179
+ if (!closed) fail("string is unterminated", start.offset, start.line, start.column);
39180
+ tokens.push({ kind: "string", value, ...start });
39181
+ continue;
39182
+ }
39183
+ const rest = text.slice(i);
39184
+ const number4 = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/.exec(rest)?.[0];
39185
+ if (number4) {
39186
+ for (let n = 0; n < number4.length; n++) advance();
39187
+ tokens.push({ kind: "number", lexeme: number4, ...start });
39188
+ continue;
39189
+ }
39190
+ const literal2 = /^(true|false|null)/.exec(rest)?.[0];
39191
+ if (literal2) {
39192
+ for (let n = 0; n < literal2.length; n++) advance();
39193
+ tokens.push({ kind: "literal", value: literal2 === "true" ? true : literal2 === "false" ? false : null, ...start });
39194
+ continue;
39195
+ }
39196
+ fail(`has an unexpected token ${JSON.stringify(ch)}`, start.offset, start.line, start.column);
39197
+ }
39198
+ tokens.push({ kind: "eof", offset: i, line, column });
39199
+ return tokens;
39200
+ }
39201
+
39202
+ // src/import/jsonDecoder.ts
39203
+ function describe3(token) {
39204
+ return token.kind === "eof" ? "end of input" : token.kind === "punct" ? token.value : token.kind;
39205
+ }
39206
+ function decodeJsonRecords(bytes) {
39207
+ if (bytes.byteLength === 0) throw new ImportSourceError("JSON source is empty.");
39208
+ const tokens = tokenizeJson(decodeUtf8Json(bytes));
39209
+ let index = 0;
39210
+ const fail3 = (message, token = tokens[index]) => {
39211
+ throw new ImportSourceError(`JSON ${message} (offset=${token.offset}, line=${token.line}, column=${token.column}).`);
39212
+ };
39213
+ const isPunct = (token, value) => token.kind === "punct" && token.value === value;
39214
+ const punct = (value) => {
39215
+ const token = tokens[index];
39216
+ if (token.kind !== "punct" || token.value !== value) fail3(`expected ${value}; found ${describe3(token)}`, token);
39217
+ index++;
39218
+ };
39219
+ const parseValue = () => {
39220
+ const token = tokens[index++];
39221
+ if (token.kind === "string") return token.value;
39222
+ if (token.kind === "number") return { kind: "number", lexeme: token.lexeme };
39223
+ if (token.kind === "literal") return token.value;
39224
+ if (token.kind === "punct" && token.value === "{") {
39225
+ const object3 = /* @__PURE__ */ new Map();
39226
+ if (isPunct(tokens[index], "}")) {
39227
+ index++;
39228
+ return object3;
39229
+ }
39230
+ while (true) {
39231
+ const key = tokens[index++];
39232
+ if (key.kind !== "string") return fail3(`object key must be a string; found ${describe3(key)}`, key);
39233
+ const keyValue = key.value;
39234
+ if (object3.has(keyValue)) fail3(`duplicate key ${JSON.stringify(keyValue)}`, key);
39235
+ punct(":");
39236
+ object3.set(keyValue, parseValue());
39237
+ const separator = tokens[index];
39238
+ if (isPunct(separator, "}")) {
39239
+ index++;
39240
+ break;
39241
+ }
39242
+ punct(",");
39243
+ }
39244
+ return object3;
39245
+ }
39246
+ if (token.kind === "punct" && token.value === "[") {
39247
+ const array2 = [];
39248
+ if (isPunct(tokens[index], "]")) {
39249
+ index++;
39250
+ return array2;
39251
+ }
39252
+ while (true) {
39253
+ array2.push(parseValue());
39254
+ const separator = tokens[index];
39255
+ if (isPunct(separator, "]")) {
39256
+ index++;
39257
+ break;
39258
+ }
39259
+ punct(",");
39260
+ }
39261
+ return array2;
39262
+ }
39263
+ return fail3(`expected a value; found ${describe3(token)}`, token);
39264
+ };
39265
+ const root = parseValue();
39266
+ if (tokens[index].kind !== "eof") fail3(`has trailing data; found ${describe3(tokens[index])}`);
39267
+ const records = root instanceof Map ? [root] : Array.isArray(root) ? root : fail3("root must be an object or array.", tokens[0]);
39268
+ if (records.length === 0) throw new ImportSourceError("JSON source contains no records.");
39269
+ records.forEach((record2, i) => {
39270
+ if (!(record2 instanceof Map)) throw new ImportSourceError(`JSON record ${i + 1} must be an object.`);
39271
+ });
39272
+ return records;
39273
+ }
39274
+
39275
+ // src/import/jsonMaterializer.ts
39276
+ var STRING_ARRAY_TYPES = /* @__PURE__ */ new Set(["CHECK_BOX", "MULTI_SELECT"]);
39277
+ var CODE_ARRAY_TYPES = /* @__PURE__ */ new Set(["USER_SELECT", "ORGANIZATION_SELECT", "GROUP_SELECT"]);
39278
+ function fail2(row, field, message) {
39279
+ throw new ImportSourceError(`JSON field validation failed (row=${row}, field=${field}): ${message}`);
39280
+ }
39281
+ function isNumber(value) {
39282
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Map) && value.kind === "number";
39283
+ }
39284
+ function materializeValue(value, target, row) {
39285
+ if (value === null) return "";
39286
+ if (typeof value === "string") return value;
39287
+ if (typeof value === "boolean") fail2(row, target.code, "boolean is not accepted.");
39288
+ if (isNumber(value)) {
39289
+ if (target.fieldType === "NUMBER") fail2(row, target.code, "precision target requires a JSON string.");
39290
+ if (!/^-?(?:0|[1-9]\d*)$/.test(value.lexeme) || value.lexeme === "-0") {
39291
+ fail2(row, target.code, `JSON number ${value.lexeme} must be a non-negative-zero safe integer lexeme.`);
39292
+ }
39293
+ const number4 = Number(value.lexeme);
39294
+ if (!Number.isSafeInteger(number4)) fail2(row, target.code, `JSON number ${value.lexeme} is outside the safe integer range.`);
39295
+ return String(number4);
39296
+ }
39297
+ if (value instanceof Map) fail2(row, target.code, "object is not accepted for a flat field.");
39298
+ if (!Array.isArray(value)) fail2(row, target.code, "unsupported value type.");
39299
+ if (!STRING_ARRAY_TYPES.has(target.fieldType) && !CODE_ARRAY_TYPES.has(target.fieldType)) {
39300
+ fail2(row, target.code, "array is accepted only for multi-value fields.");
39301
+ }
39302
+ const strings = value.map((entry) => {
39303
+ if (typeof entry !== "string") fail2(row, target.code, "array elements must be strings.");
39304
+ return entry;
39305
+ });
39306
+ if (new Set(strings).size !== strings.length) fail2(row, target.code, "array elements must not contain duplicates.");
39307
+ return CODE_ARRAY_TYPES.has(target.fieldType) ? JSON.stringify(strings.map((code) => ({ code }))) : JSON.stringify(strings);
39308
+ }
39309
+ function materializeJsonDmlSource(_source, payload, targets, maxRows) {
39310
+ if (payload.encoding && payload.encoding !== "utf8") throw new ImportSourceError("JSON source is UTF-8 only.");
39311
+ const records = decodeJsonRecords(payload.bytes);
39312
+ if (records.length > maxRows) throw new ImportSourceError(`source rows (${records.length}) exceed maxRecords (${maxRows}).`);
39313
+ const targetByCode = new Map(targets.map((target) => [target.code, target]));
39314
+ if (targetByCode.size !== targets.length) throw new ImportSourceError("JSON target fields contain duplicates.");
39315
+ const rows = [];
39316
+ const importPresence = [];
39317
+ records.forEach((record2, index) => {
39318
+ for (const key of record2.keys()) {
39319
+ if (!targetByCode.has(key)) fail2(index + 1, key, "unknown key (not declared in INTO).");
39320
+ }
39321
+ const row = {};
39322
+ const present = /* @__PURE__ */ new Set();
39323
+ for (const target of targets) {
39324
+ if (!record2.has(target.code)) continue;
39325
+ present.add(target.code);
39326
+ row[target.code] = materializeValue(record2.get(target.code), target, index + 1);
39327
+ }
39328
+ rows.push(row);
39329
+ importPresence.push(present);
39330
+ });
39331
+ return {
39332
+ rows,
39333
+ columns: targets.map((target) => target.code),
39334
+ columnMeta: new Map(targets.map((target) => [target.code, { fieldType: target.fieldType }])),
39335
+ importPresence
39336
+ };
39337
+ }
39338
+
39339
+ // src/import/materializeDmlSource.ts
39340
+ function materializeCsvDmlSource(source, payload, maxRows, targetCodes, fieldInfos, recordNumberSourceHeader) {
39341
+ const decoded = decodeCsv(payload.bytes, {
39342
+ encoding: source.encoding ?? payload.encoding ?? "utf8",
39343
+ hasHeader: source.hasHeader,
39344
+ columns: source.columns
39345
+ });
39346
+ if (decoded.rows.length > maxRows) {
39347
+ throw new ImportSourceError(`source rows (${decoded.rows.length}) exceed maxRecords (${maxRows}).`);
39348
+ }
39349
+ if (source.mappingMode === "BY_NAME") {
39350
+ if (!targetCodes || !fieldInfos) throw new Error("InternalError: BY NAME requires destination form metadata.");
39351
+ if (new Set(targetCodes).size !== targetCodes.length) {
39352
+ throw new ImportSourceError("ERR_IMPORT_HEADER_REUSED: a BY NAME header cannot be consumed more than once.");
39353
+ }
39354
+ const indexes = new Map(decoded.columns.map((column, index) => [column, index]));
39355
+ for (const code of targetCodes) {
39356
+ if (!indexes.has(code)) throw new ImportSourceError(`ERR_IMPORT_MISSING_COLUMN: required header "${code}" is missing.`);
39357
+ }
39358
+ const infoByCode = new Map(fieldInfos.map((info) => [info.code, info]));
39359
+ const targetSet = new Set(targetCodes);
39360
+ if (recordNumberSourceHeader && targetSet.has(recordNumberSourceHeader)) {
39361
+ throw new ImportSourceError("ERR_IMPORT_HEADER_REUSED: record-number source header is lookup-only and cannot be a write target.");
39362
+ }
39363
+ if (recordNumberSourceHeader && !indexes.has(recordNumberSourceHeader)) {
39364
+ throw new ImportSourceError(`ERR_IMPORT_MISSING_COLUMN: required header "${recordNumberSourceHeader}" is missing.`);
39365
+ }
39366
+ const ignoredKnownColumns = [];
39367
+ const ignoredUnknownColumns = [];
39368
+ const nonEmpty = (index) => decoded.rows.filter((row) => row[index] !== "").length;
39369
+ const reasonFor = (info) => {
39370
+ if (info.fieldType === "FILE") return "FILE attachment is outside flat IMPORT scope";
39371
+ if (info.inSubtable || info.fieldType === "SUBTABLE") return "subtable field is not writable in Phase 3";
39372
+ if (info.writable === false) return `non-writable ${info.fieldType} field`;
39373
+ return `known export-only ${info.fieldType} field`;
39374
+ };
39375
+ for (const [index, column] of decoded.columns.entries()) {
39376
+ if (targetSet.has(column) || column === recordNumberSourceHeader) continue;
39377
+ const info = infoByCode.get(column);
39378
+ if (info) ignoredKnownColumns.push({ column, reason: reasonFor(info), nonEmptyCells: nonEmpty(index) });
39379
+ else if (!source.ignoreUnknownColumns) throw new ImportSourceError(`ERR_IMPORT_UNKNOWN_COLUMN: unknown CSV header "${column}".`);
39380
+ else ignoredUnknownColumns.push({ column, reason: "unknown column ignored by explicit policy", nonEmptyCells: nonEmpty(index) });
39381
+ }
39382
+ for (const code of targetCodes) {
39383
+ const info = infoByCode.get(code);
39384
+ if (!info) throw new Error(`ArgumentError: DML target field ${code} does not exist.`);
39385
+ if (info.inSubtable || info.writable === false || info.fieldType === "FILE" || info.fieldType === "SUBTABLE") {
39386
+ throw new Error(`ArgumentError: DML target field ${code} is not writable (${info.fieldType}).`);
39387
+ }
39388
+ }
39389
+ const importRowErrors = [];
39390
+ const rows2 = decoded.rows.map((values) => {
39391
+ const errors = [];
39392
+ const row = {};
39393
+ for (const code of targetCodes) {
39394
+ const raw = values[indexes.get(code)];
39395
+ try {
39396
+ row[code] = convertImportCsvValue(raw, infoByCode.get(code)?.fieldType, { cliKintone: true });
39397
+ } catch (error51) {
39398
+ if (!(error51 instanceof ImportCsvValueError)) throw error51;
39399
+ row[code] = raw;
39400
+ errors.push({ field: code, code: error51.code, message: error51.message });
39401
+ }
39402
+ }
39403
+ importRowErrors.push(errors);
39404
+ return row;
39405
+ });
39406
+ return {
39407
+ rows: rows2,
39408
+ columns: [...targetCodes],
39409
+ columnMeta: new Map(targetCodes.map((code) => [code, { fieldType: infoByCode.get(code)?.fieldType ?? "SINGLE_LINE_TEXT" }])),
39410
+ importRowErrors,
39411
+ ...recordNumberSourceHeader ? { recordNumberSourceValues: decoded.rows.map((row) => row[indexes.get(recordNumberSourceHeader)]) } : {},
39412
+ importAudit: { mapping: "BY_NAME", writtenColumns: [...targetCodes], ignoredKnownColumns, ignoredUnknownColumns }
39413
+ };
39414
+ }
39415
+ const rows = decoded.rows.map((values) => Object.fromEntries(decoded.columns.map((column, i) => [column, values[i]])));
39416
+ return {
39417
+ rows,
39418
+ columns: decoded.columns,
39419
+ // CSV cells are decoded as strings; projections such as CAST may replace this metadata downstream.
39420
+ columnMeta: new Map(decoded.columns.map((column) => [column, { fieldType: "SINGLE_LINE_TEXT" }]))
39421
+ };
39422
+ }
39423
+
39424
+ // src/import/importRecordsMaterializer.ts
39425
+ var sourceFail = (parentRow, code, message) => {
39426
+ throw new ImportSourceError(`JSON subtable validation failed (parentRow=${parentRow}, field=${code}): ${message}`);
39427
+ };
39428
+ function materializeJsonImportRecords(_source, payload, targets, maxParents, maxChildRows = maxParents) {
39429
+ if (payload.encoding && payload.encoding !== "utf8") throw new ImportSourceError("JSON source is UTF-8 only.");
39430
+ const decoded = decodeJsonRecords(payload.bytes);
39431
+ if (decoded.length > maxParents) throw new ImportSourceError(`source parent rows (${decoded.length}) exceed maxRecords (${maxParents}).`);
39432
+ const targetByCode = new Map(targets.map((target) => [target.kind === "FIELD" ? target.field : target.subtableCode, target]));
39433
+ if (targetByCode.size !== targets.length) throw new ImportSourceError("IMPORT targets contain duplicates.");
39434
+ let childTotal = 0;
39435
+ return {
39436
+ records: decoded.map((record2, index) => {
39437
+ const parentRow = index + 1;
39438
+ for (const code of record2.keys()) if (!targetByCode.has(code)) sourceFail(parentRow, code, "unknown key (not declared in INTO).");
39439
+ const top = /* @__PURE__ */ new Map();
39440
+ const subtables = /* @__PURE__ */ new Map();
39441
+ const replacementTables = /* @__PURE__ */ new Set();
39442
+ for (const target of targets) {
39443
+ const code = target.kind === "FIELD" ? target.field : target.subtableCode;
39444
+ if (!record2.has(code)) continue;
39445
+ const value = record2.get(code);
39446
+ if (target.kind === "FIELD") {
39447
+ if (value instanceof Map) sourceFail(parentRow, code, "object is not accepted for a top-level field.");
39448
+ top.set(code, value);
39449
+ continue;
39450
+ }
39451
+ if (!Array.isArray(value)) sourceFail(parentRow, code, "subtable value must be an array.");
39452
+ replacementTables.add(code);
39453
+ const children = new Set(target.children);
39454
+ const rows = value.map((entry, childIndex) => {
39455
+ if (!(entry instanceof Map)) sourceFail(parentRow, code, `childRow=${childIndex + 1} must be an object.`);
39456
+ const child = entry;
39457
+ for (const childCode of child.keys()) {
39458
+ if (!children.has(childCode)) sourceFail(parentRow, childCode, `unknown child key in subtable ${code} at childRow=${childIndex + 1}.`);
39459
+ }
39460
+ childTotal++;
39461
+ if (childTotal > maxChildRows) throw new ImportSourceError(`source child rows (${childTotal}) exceed limit (${maxChildRows}).`);
39462
+ return { childRowNumber: childIndex + 1, values: child };
39463
+ });
39464
+ subtables.set(code, rows);
39465
+ }
39466
+ return { rowNumber: parentRow, top, subtables, replacementTables };
39467
+ })
39468
+ };
39469
+ }
39470
+ function materializeCliKintoneCsvImportRecords(source, payload, targets, replacementTables, maxParents, recordNumberSourceHeader) {
39471
+ const decoded = decodeCsv(payload.bytes, {
39472
+ encoding: source.encoding ?? payload.encoding ?? "utf8",
39473
+ hasHeader: source.hasHeader,
39474
+ columns: source.columns
39475
+ });
39476
+ if (!source.hasHeader || decoded.columns[0] !== "*") throw new ImportSourceError('ERR_IMPORT_MARKER: first CSV header must be "*".');
39477
+ const indexes = new Map(decoded.columns.map((code, index) => [code, index]));
39478
+ if (recordNumberSourceHeader && !indexes.has(recordNumberSourceHeader)) throw new ImportSourceError(`ERR_IMPORT_MISSING_COLUMN: required header "${recordNumberSourceHeader}" is missing.`);
39479
+ const fields = targets.filter((target) => target.kind === "FIELD");
39480
+ const tables = targets.filter((target) => target.kind === "SUBTABLE");
39481
+ for (const field of fields) if (!indexes.has(field.field)) throw new ImportSourceError(`ERR_IMPORT_MISSING_COLUMN: required header "${field.field}" is missing.`);
39482
+ for (const table of tables) {
39483
+ if (!table.rowIdSourceHeader || !indexes.has(table.rowIdSourceHeader)) throw new ImportSourceError(`ERR_IMPORT_MISSING_COLUMN: row-ID header for ${table.subtableCode} is missing.`);
39484
+ for (const child of table.children) if (!indexes.has(child)) throw new ImportSourceError(`ERR_IMPORT_MISSING_COLUMN: required child header "${child}" is missing.`);
39485
+ }
39486
+ const records = [];
39487
+ let current;
39488
+ decoded.rows.forEach((cells, physicalIndex) => {
39489
+ const sourceRowNumber = physicalIndex + 2;
39490
+ const marker = cells[0];
39491
+ if (marker !== "" && marker !== "*") throw new ImportSourceError(`ERR_IMPORT_MARKER: invalid marker ${JSON.stringify(marker)} at source row ${sourceRowNumber}.`);
39492
+ if (marker === "*") {
39493
+ if (records.length >= maxParents) throw new ImportSourceError(`source parent rows exceed maxRecords (${maxParents}).`);
39494
+ current = {
39495
+ rowNumber: records.length + 1,
39496
+ markerRowNumber: sourceRowNumber,
39497
+ top: new Map(fields.map((field) => [field.field, cells[indexes.get(field.field)]])),
39498
+ subtables: new Map(tables.map((table) => [table.subtableCode, []])),
39499
+ replacementTables: new Set(replacementTables),
39500
+ ...recordNumberSourceHeader ? { recordNumberSourceValue: cells[indexes.get(recordNumberSourceHeader)] } : {}
39501
+ };
39502
+ records.push(current);
39503
+ } else if (!current) {
39504
+ throw new ImportSourceError(`ERR_IMPORT_MARKER: first data row must start a parent (source row ${sourceRowNumber}).`);
39505
+ } else {
39506
+ for (const field of fields) {
39507
+ const continuationValue = cells[indexes.get(field.field)];
39508
+ if (continuationValue !== "" && continuationValue !== current.top.get(field.field)) {
39509
+ throw new ImportSourceError(`ERR_IMPORT_PARENT_VALUE_ON_CONTINUATION: ${field.field} at source row ${sourceRowNumber}.`);
39510
+ }
39511
+ }
39512
+ if (recordNumberSourceHeader) {
39513
+ const continuationValue = cells[indexes.get(recordNumberSourceHeader)];
39514
+ if (continuationValue !== "" && continuationValue !== current.recordNumberSourceValue) {
39515
+ throw new ImportSourceError(`ERR_IMPORT_PARENT_VALUE_ON_CONTINUATION: ${recordNumberSourceHeader} at source row ${sourceRowNumber}.`);
39516
+ }
39517
+ }
39518
+ }
39519
+ for (const table of tables) {
39520
+ const rowId = cells[indexes.get(table.rowIdSourceHeader)];
39521
+ const values = new Map(table.children.map((child) => [child, cells[indexes.get(child)]]));
39522
+ if (rowId === "" && [...values.values()].every((value) => value === "")) continue;
39523
+ const rows = current.subtables.get(table.subtableCode);
39524
+ rows.push({ childRowNumber: rows.length + 1, sourceRowNumber, ...rowId ? { rowId } : {}, values });
39525
+ }
39526
+ });
39527
+ if (records.length === 0) throw new ImportSourceError("CSV has no parent rows.");
39528
+ return { records };
39529
+ }
39530
+
39531
+ // src/import/importRecordValidation.ts
39532
+ var USER_TYPES3 = /* @__PURE__ */ new Set(["USER_SELECT", "ORGANIZATION_SELECT", "GROUP_SELECT"]);
39533
+ var UNSUPPORTED_CHILD_TYPES = /* @__PURE__ */ new Set(["SUBTABLE", "FILE", "CALC", "RECORD_NUMBER", "CREATOR", "CREATED_TIME", "MODIFIER", "UPDATED_TIME", "STATUS", "STATUS_ASSIGNEE", "CATEGORY", "REFERENCE_TABLE"]);
39534
+ function assertImportRejectLimit(prepared, rejectLimit) {
39535
+ if (rejectLimit != null && prepared.invalidParentRows.size > rejectLimit) {
39536
+ throw new Error(`RejectLimitExceededError: rejected parents (${prepared.invalidParentRows.size}) exceed REJECT LIMIT (${rejectLimit}).`);
39537
+ }
39538
+ }
39539
+ function prepareImportRecords(materialized, targets, fieldInfos, numberPrecision, operation) {
39540
+ const topInfos = new Map(fieldInfos.filter((f) => !f.inSubtable).map((f) => [f.code, f]));
39541
+ const scoped = /* @__PURE__ */ new Map();
39542
+ for (const info of fieldInfos) if (info.inSubtable && info.subtableCode) {
39543
+ let children = scoped.get(info.subtableCode);
39544
+ if (!children) scoped.set(info.subtableCode, children = /* @__PURE__ */ new Map());
39545
+ children.set(info.code, info);
39546
+ }
39547
+ const targetTop = targets.filter((t) => t.kind === "FIELD");
39548
+ const targetTables = targets.filter((t) => t.kind === "SUBTABLE");
39549
+ for (const target of targetTop) assertWritable(target.field, topInfos.get(target.field), void 0);
39550
+ for (const target of targetTables) {
39551
+ const table = topInfos.get(target.subtableCode);
39552
+ if (!table || table.fieldType !== "SUBTABLE") throw new Error(`ArgumentError: IMPORT subtable ${target.subtableCode} does not exist.`);
39553
+ const children = scoped.get(target.subtableCode) ?? /* @__PURE__ */ new Map();
39554
+ for (const child of target.children) assertWritable(child, children.get(child), target.subtableCode);
39555
+ }
39556
+ const tableCounts = new Map(targetTables.map((t) => [t.subtableCode, { parentsPresent: 0, childRows: 0, validChildRows: 0, invalidChildRows: 0 }]));
39557
+ const parents = materialized.records.map((record2) => validateParent(record2, targetTop, targetTables, topInfos, scoped, numberPrecision, operation, tableCounts));
39558
+ const errors = parents.flatMap((parent) => [...parent.errors]);
39559
+ return { parents, errors, invalidParentRows: new Set(parents.filter((p) => !p.valid).map((p) => p.parentRow)), tableCounts };
39560
+ }
39561
+ function validateParent(source, topTargets, tableTargets, topInfos, scoped, precision, operation, tableCounts) {
39562
+ const errors = [];
39563
+ const top = {};
39564
+ for (const target of topTargets) {
39565
+ if (!source.top.has(target.field)) continue;
39566
+ validateValue(source.top.get(target.field), topInfos.get(target.field), precision, top, target.field, errors, location(source, operation, target.field));
39567
+ }
39568
+ const createValidationOnly = {};
39569
+ if (operation === "INSERT") for (const info of topInfos.values()) {
39570
+ if (info.fieldType === "SUBTABLE" || info.writable === false || source.top.has(info.code)) continue;
39571
+ validateMissing(info, precision, createValidationOnly, errors, location(source, operation, info.code));
39572
+ }
39573
+ const subtables = /* @__PURE__ */ new Map();
39574
+ for (const target of tableTargets) {
39575
+ if (!source.subtables.has(target.subtableCode)) continue;
39576
+ const count = tableCounts.get(target.subtableCode);
39577
+ count.parentsPresent++;
39578
+ const preparedRows = [];
39579
+ for (const child of source.subtables.get(target.subtableCode)) {
39580
+ count.childRows++;
39581
+ const before = errors.length;
39582
+ const record2 = {};
39583
+ const infos = scoped.get(target.subtableCode);
39584
+ for (const code of target.children) {
39585
+ const info = infos.get(code);
39586
+ const loc = location(source, operation, code, target.subtableCode, child.childRowNumber, child.sourceRowNumber ?? source.markerRowNumber);
39587
+ if (child.values.has(code)) validateValue(child.values.get(code), info, precision, record2, code, errors, loc);
39588
+ else validateMissing(info, precision, record2, errors, loc);
39589
+ }
39590
+ if (errors.length === before) {
39591
+ count.validChildRows++;
39592
+ preparedRows.push(record2);
39593
+ } else count.invalidChildRows++;
39594
+ }
39595
+ subtables.set(target.subtableCode, preparedRows);
39596
+ }
39597
+ return { parentRow: source.rowNumber, valid: errors.length === 0, top, subtables, replacementTables: source.replacementTables, errors };
39598
+ }
39599
+ function assertWritable(code, info, table) {
39600
+ if (!info) throw new Error(table ? `ArgumentError: IMPORT child ${code} does not belong to subtable ${table}.` : `ArgumentError: IMPORT top-level field ${code} does not exist.`);
39601
+ if (info.writable === false || table && UNSUPPORTED_CHILD_TYPES.has(info.fieldType)) {
39602
+ throw new Error(`ArgumentError: IMPORT ${table ? `child ${table}.${code}` : `field ${code}`} is not writable (${info.fieldType}).`);
39603
+ }
39604
+ }
39605
+ function validateMissing(info, precision, record2, errors, loc) {
39606
+ const raw = isEmptyDmlValue(info.defaultValue) ? "" : info.defaultValue;
39607
+ validateValue(raw, info, precision, record2, info.code, errors, loc, !isEmptyDmlValue(info.defaultValue));
39608
+ }
39609
+ function validateValue(raw, info, precision, record2, code, errors, loc, isDefault = false) {
39610
+ const normalizedRaw = decodeRaw(raw);
39611
+ const result = validateAndNormalizeDmlValue(normalizedRaw, info, precision);
39612
+ if (!result.ok) errors.push({ ...loc, code: result.code, message: isDefault ? `\u65E2\u5B9A\u5024: ${result.message}` : result.message });
39613
+ else record2[code] = { value: preserveUserCodes(normalizedRaw, info) ? normalizedRaw : result.value };
39614
+ }
39615
+ function decodeRaw(raw) {
39616
+ if (isJsonNumber(raw)) return raw.lexeme;
39617
+ if (Array.isArray(raw)) return raw.map((value) => value instanceof Map ? value : isJsonNumber(value) ? value.lexeme : value);
39618
+ return raw;
39619
+ }
39620
+ function isJsonNumber(raw) {
39621
+ return typeof raw === "object" && raw !== null && raw.kind === "number";
39622
+ }
39623
+ function preserveUserCodes(raw, info) {
39624
+ return USER_TYPES3.has(info.fieldType) && Array.isArray(raw) && raw.every((v) => typeof v === "object" && v !== null && "code" in v);
39625
+ }
39626
+ function location(source, operation, field, subtable, subrow, sourceRow) {
39627
+ const physicalRow = sourceRow ?? source.markerRowNumber;
39628
+ return { operation, parentRow: source.rowNumber, field, ...subtable ? { subtable } : {}, ...subrow == null ? {} : { subrow }, ...physicalRow == null ? {} : { sourceRow: physicalRow }, sourceValues: subtable ? source.subtables.get(subtable)?.[subrow - 1]?.values ?? /* @__PURE__ */ new Map() : source.top };
39629
+ }
39630
+
39631
+ // src/import/importErrors.ts
39632
+ var IMPORT_VALIDATION_META_COLUMNS = [
39633
+ "$err_statement",
39634
+ "$err_operation",
39635
+ "$err_row",
39636
+ "$err_field",
39637
+ "$err_subtable",
39638
+ "$err_subrow",
39639
+ "$err_source_row",
39640
+ "$err_code",
39641
+ "$err_message"
39642
+ ];
39643
+ function materializeImportValidationErrors(errors, payloadFields, statementNumber = 1) {
39644
+ return errors.map((error51) => {
39645
+ const row = {};
39646
+ for (const field of payloadFields) row[field] = error51.sourceValues.get(field) == null ? "" : render(error51.sourceValues.get(field));
39647
+ row["$err_statement"] = String(statementNumber);
39648
+ row["$err_operation"] = error51.operation;
39649
+ row["$err_row"] = String(error51.parentRow);
39650
+ row["$err_field"] = error51.field;
39651
+ row["$err_subtable"] = error51.subtable ?? "";
39652
+ row["$err_subrow"] = error51.subrow == null ? "" : String(error51.subrow);
39653
+ row["$err_source_row"] = error51.sourceRow == null ? null : String(error51.sourceRow);
39654
+ row["$err_code"] = error51.code;
39655
+ row["$err_message"] = error51.message;
39656
+ return row;
39657
+ });
39658
+ }
39659
+ function render(value) {
39660
+ if (value === null || value === void 0) return "";
39661
+ if (typeof value === "object" && value !== null && "kind" in value && "lexeme" in value && value.kind === "number") {
39662
+ return String(value.lexeme);
39663
+ }
39664
+ if (Array.isArray(value)) return JSON.stringify(value);
39665
+ return String(value);
39666
+ }
39667
+
39668
+ // src/import/subtablePayload.ts
39669
+ function buildImportRecordPayload(top, subtables, rowIdMode) {
39670
+ const record2 = {};
39671
+ for (const [code, value] of top) record2[code] = { value };
39672
+ for (const [tableCode, sourceRows] of subtables) {
39673
+ record2[tableCode] = {
39674
+ value: sourceRows.map((sourceRow) => ({
39675
+ ...rowIdMode === "PRESERVE" && sourceRow.rowId ? { id: sourceRow.rowId } : {},
39676
+ value: Object.fromEntries([...sourceRow.values].map(([childCode, value]) => [childCode, { value }]))
39677
+ }))
39678
+ };
39679
+ }
39680
+ return record2;
39681
+ }
39682
+ function buildJsonImportRecordPayload(top, subtables) {
39683
+ return buildImportRecordPayload(top, subtables, "DROP");
39684
+ }
39685
+
39686
+ // src/import/jsonSubtableWritePlan.ts
39687
+ function assertJsonImportHasNoRowIds(materialized) {
39688
+ for (const parent of materialized.records) for (const [table, rows] of parent.subtables) {
39689
+ for (const row of rows) {
39690
+ if (row.rowId !== void 0 || row.values.has("_rid") || row.values.has("id")) {
39691
+ throw new Error(`ArgumentError: JSON IMPORT subtable ${table} does not accept _rid/id; rows are always newly numbered.`);
39692
+ }
39693
+ }
39694
+ }
39695
+ }
39696
+ function buildJsonSubtableWritePlan(parents, targetIds, existingById) {
39697
+ return parents.map((parent, index) => {
39698
+ const targetId = targetIds[index];
39699
+ const existing = targetId === void 0 ? void 0 : existingById.get(targetId);
39700
+ if (targetId !== void 0 && !existing) throw new Error(`InternalError: IMPORT UPSERT target APP record ${targetId} was not loaded.`);
39701
+ const tables = [...parent.subtables].map(([table, input]) => {
39702
+ const raw = existing?.record[table]?.value;
39703
+ const existingRows = Array.isArray(raw) ? raw.length : 0;
39704
+ return { table, existingRows, inputRows: input.length, addRows: input.length, deleteRows: existingRows };
39705
+ });
39706
+ return {
39707
+ parentRow: parent.parentRow,
39708
+ mode: targetId === void 0 ? "INSERT" : "UPDATE",
39709
+ ...targetId === void 0 ? {} : { targetId, revision: existing?.revision },
39710
+ top: parent.top,
39711
+ subtables: parent.subtables,
39712
+ tables
39713
+ };
39714
+ });
39715
+ }
39716
+
39717
+ // src/import/subtableReplacementPlan.ts
39718
+ function tableRows(record2, table) {
39719
+ const raw = record2[table]?.value;
39720
+ return Array.isArray(raw) ? raw : [];
39721
+ }
39722
+ function assertNoDuplicateCsvSubtableRowIds(records) {
39723
+ const seen = /* @__PURE__ */ new Map();
39724
+ for (const parent of records) for (const [table, rows] of parent.subtables) for (const row of rows) {
39725
+ if (!row.rowId) continue;
39726
+ const key = `${table}\0${row.rowId}`;
39727
+ if (seen.has(key)) throw new Error(`ERR_SUBTABLE_ROW_ID_DUP_SOURCE: duplicate row ID ${row.rowId} in ${table}`);
39728
+ seen.set(key, parent.rowNumber);
39729
+ }
39730
+ }
39731
+ function buildCsvSubtableReplacementPlan(sources, prepared, targetIds, existingById, ownership) {
39732
+ return prepared.map((parent, index) => {
39733
+ const source = sources[index];
39734
+ const targetId = targetIds[index];
39735
+ const existing = targetId === void 0 ? void 0 : existingById.get(targetId);
39736
+ const errors = [...parent.errors];
39737
+ if (!existing || targetId === void 0) return { parentRow: parent.parentRow, targetId: targetId ?? 0, valid: false, top: parent.top, subtables: /* @__PURE__ */ new Map(), tables: [], errors };
39738
+ const subtables = /* @__PURE__ */ new Map();
39739
+ const tables = [];
39740
+ for (const table of parent.replacementTables) {
39741
+ const current = tableRows(existing.record, table);
39742
+ const currentIds = new Set(current.map((row) => row.id).filter((id) => !!id));
39743
+ const input = source.subtables.get(table) ?? [];
39744
+ const normalized = parent.subtables.get(table) ?? [];
39745
+ let updateRows = 0, addRows = 0, rowIdNotFound = 0;
39746
+ const payloadRows = input.map((row, rowIndex) => {
39747
+ const normalizedRecord = normalized[rowIndex] ?? {};
39748
+ if (row.rowId && currentIds.has(row.rowId)) {
39749
+ updateRows++;
39750
+ return { rowId: row.rowId, record: normalizedRecord };
39751
+ }
39752
+ if (row.rowId) {
39753
+ const owners = ownership.get(row.rowId) ?? [];
39754
+ if (owners.some((owner) => owner.parentId !== targetId || owner.table !== table)) errors.push({
39755
+ operation: "UPDATE",
39756
+ parentRow: parent.parentRow,
39757
+ field: row.rowId,
39758
+ subtable: table,
39759
+ subrow: row.childRowNumber,
39760
+ sourceRow: row.sourceRowNumber,
39761
+ code: "ERR_IMPORT_FIELD_OWNERSHIP",
39762
+ message: `rowIdOwnedElsewhere: ${row.rowId}`,
39763
+ sourceValues: row.values
39764
+ });
39765
+ rowIdNotFound++;
39766
+ }
39767
+ addRows++;
39768
+ return { record: normalizedRecord };
39769
+ });
39770
+ subtables.set(table, payloadRows);
39771
+ tables.push({ table, existingRows: current.length, inputRows: input.length, updateRows, addRows, deleteRows: current.length - updateRows, rowIdNotFound });
39772
+ }
39773
+ return { parentRow: parent.parentRow, targetId, ...existing.revision === void 0 ? {} : { revision: existing.revision }, valid: errors.length === 0, top: parent.top, subtables, tables, errors };
39774
+ });
39775
+ }
39776
+
39777
+ // src/import/importProjection.ts
39778
+ var IMPORT_PROJECTION_SOURCE = "#__import_source";
39779
+ function bindImportProjection(projection) {
39780
+ return { ...projection, from: { appId: 0, alias: null, cteName: IMPORT_PROJECTION_SOURCE } };
39781
+ }
39782
+
39783
+ // src/import/recordNumberUpdate.ts
39784
+ function normalizeImportRecordNumber(raw) {
39785
+ return /^[0-9]+$/.test(raw) ? raw.replace(/^0+(?=\d)/, "") : null;
39786
+ }
39787
+ function preflightImportRecordNumbers(values, header) {
39788
+ const normalized = values.map(normalizeImportRecordNumber);
39789
+ const seen = /* @__PURE__ */ new Set();
39790
+ for (const key of normalized) {
39791
+ if (key === null) continue;
39792
+ if (seen.has(key)) {
39793
+ throw new Error("ERR_RECORD_NUMBER_DUP_SOURCE: source contains a duplicate record number");
39794
+ }
39795
+ seen.add(key);
39796
+ }
39797
+ return {
39798
+ normalized,
39799
+ errors: normalized.map((key) => key === null ? [{
39800
+ field: header,
39801
+ code: "ERR_RECORD_NUMBER_INVALID",
39802
+ message: `${header} must be a non-empty ASCII decimal record number`
39803
+ }] : [])
39804
+ };
39805
+ }
39806
+
38720
39807
  // src/execute.ts
38721
39808
  var SEARCH_ABORTED_WARNING = "\u691C\u7D22\u304C 10 \u4E07\u4EF6\u3067\u6253\u3061\u5207\u3089\u308C\u3001\u7D50\u679C\u304C\u6B20\u843D\u3057\u305F\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002";
38722
39809
  var SearchAbortedError = class extends Error {
@@ -38727,6 +39814,7 @@ var SearchAbortedError = class extends Error {
38727
39814
  };
38728
39815
  var materializedMetaBySelectResult = /* @__PURE__ */ new WeakMap();
38729
39816
  var materializedMetaByValidationResult = /* @__PURE__ */ new WeakMap();
39817
+ var importSourceByDmlStatement = /* @__PURE__ */ new WeakMap();
38730
39818
  var defaultCacheContextByClient = /* @__PURE__ */ new WeakMap();
38731
39819
  var nextDefaultCacheContextId = 1;
38732
39820
  function resolveCacheContext(client, explicit) {
@@ -38741,7 +39829,7 @@ function resolveCacheContext(client, explicit) {
38741
39829
  async function execute(sql, client, options = {}) {
38742
39830
  const startedAt = Date.now();
38743
39831
  const cacheContext = resolveCacheContext(client, options.cacheContext);
38744
- const stmt = parseSql(sql);
39832
+ const stmt = parseSql(sql, options.enableImport === true);
38745
39833
  const metrics = createEmptyMetrics();
38746
39834
  const countedClient = wrapClientWithMetrics(client, metrics);
38747
39835
  const collector = { aborted: false };
@@ -38921,6 +40009,7 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
38921
40009
  throw new Error(`ParseError: variable @${unresolved} is not defined in a batch.`);
38922
40010
  }
38923
40011
  validateKlikeStatement(stmt);
40012
+ if (stmt.type === "IMPORT") return executeImport(stmt, client, options, cacheContext);
38924
40013
  if ("validateOnly" in stmt && stmt.validateOnly === true) {
38925
40014
  if (stmt.validationErrorTable) {
38926
40015
  throw new Error("ArgumentError: VALIDATE ONLY INTO requires a batch.");
@@ -38978,25 +40067,84 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
38978
40067
  return executeAssert(stmt, client, options, cacheContext);
38979
40068
  }
38980
40069
  }
38981
- 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
+ ];
38982
40088
  function hasAuditableConstraint(field) {
38983
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;
38984
40090
  }
38985
40091
  function resolveExistingValidationTargets(stmt, fieldInfos) {
38986
- const byCode = new Map(fieldInfos.map((field) => [field.code, field]));
38987
- const auditable = (field) => !field.inSubtable && (field.fieldType === "NUMBER" || hasAuditableConstraint(field));
38988
- 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 = [];
38989
40106
  const seen = /* @__PURE__ */ new Set();
38990
- return stmt.fields.map((code) => {
38991
- if (seen.has(code)) throw new Error(`ArgumentError: VALIDATE field ${code} is duplicated.`);
38992
- seen.add(code);
38993
- if (code === "$id") throw new Error("ArgumentError: VALIDATE cannot audit system field $id.");
38994
- const info = byCode.get(code);
38995
- if (!info) throw new Error(`ArgumentError: VALIDATE field ${code} does not exist.`);
38996
- if (info.inSubtable) throw new Error(`ArgumentError: VALIDATE field ${code} is a subtable child field.`);
38997
- if (!auditable(info)) throw new Error(`ArgumentError: VALIDATE field ${code} has no auditable constraint.`);
38998
- return info;
38999
- });
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;
39000
40148
  }
39001
40149
  function collectValidateWhereFields(where) {
39002
40150
  const fields = [];
@@ -39021,11 +40169,12 @@ function collectValidateWhereFields(where) {
39021
40169
  visit(where);
39022
40170
  return fields;
39023
40171
  }
39024
- function existingValidationColumnMeta() {
39025
- return new Map(EXISTING_VALIDATION_COLUMNS.map((column) => [column, {
39026
- fieldType: column === "$id" ? "KSQL_NUMBER" : "KSQL_STRING",
39027
- sortKind: column === "$id" ? "number" : "string",
39028
- 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")
39029
40178
  }]));
39030
40179
  }
39031
40180
  async function executeExistingRecordValidation(stmt, client, options, cacheContext) {
@@ -39034,38 +40183,45 @@ async function executeExistingRecordValidation(stmt, client, options, cacheConte
39034
40183
  }
39035
40184
  async function executeExistingRecordValidationCore(stmt, client, options, cacheContext) {
39036
40185
  const fieldInfos = await getFieldsCached(stmt.appId, client, cacheContext);
39037
- 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));
39038
40188
  const targets = resolveExistingValidationTargets(stmt, fieldInfos);
39039
40189
  const checkGroups = stmt.checkGroups ?? [];
39040
40190
  const checkRefs2 = collectCheckFieldRefs(checkGroups);
39041
40191
  for (const ref of checkRefs2) {
39042
- if (ref.field !== "$id" && !infoByCode.has(ref.field)) {
40192
+ if (ref.field !== "$id" && !infoByCode.has(ref.field) && !childCodes.has(ref.field)) {
39043
40193
  throw customCheckParseError(`CHECK \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F APP${stmt.appId} \u306B\u5B58\u5728\u3057\u307E\u305B\u3093`);
39044
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
+ }
39045
40198
  }
39046
- const evaluationTypes = new Map(fieldInfos.map((field) => [field.code, field.fieldType]));
40199
+ const evaluationTypes = new Map([...infoByCode].map(([code, field]) => [code, field.fieldType]));
39047
40200
  evaluationTypes.set("$id", "RECORD_NUMBER");
39048
40201
  assertCheckComparisonTypes(stmt, evaluationTypes);
39049
40202
  const whereFields = collectValidateWhereFields(stmt.where);
39050
40203
  const requiredFields = [.../* @__PURE__ */ new Set([
39051
40204
  "$id",
39052
- ...targets.map((field) => field.code),
40205
+ ...targets.map((target) => target.subtableCode ?? target.field.code),
39053
40206
  ...whereFields,
39054
40207
  ...checkRefs2.map((ref) => ref.field)
39055
40208
  ])];
39056
40209
  for (const field of whereFields) {
39057
- if (field !== "$id" && !infoByCode.has(field)) {
40210
+ if (field !== "$id" && !infoByCode.has(field) && !childCodes.has(field)) {
39058
40211
  throw new Error(`ArgumentError: WHERE field ${field} does not exist in APP${stmt.appId}.`);
39059
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
+ }
39060
40216
  }
39061
- 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;
39062
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);
39063
40219
  const capability = classifyWhereCapability(stmt.where, semantics);
39064
40220
  if (capability.capability === "UNSUPPORTED") {
39065
40221
  throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
39066
40222
  }
39067
- const fieldTypes = new Map(fieldInfos.map((field) => [field.code, field.fieldType]));
39068
- 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))]] : []));
39069
40225
  const prefilter = stmt.where === null ? null : capability.capability === "EXACT_PUSHDOWN" ? stmt.where : extractSafePushdownLeaves(stmt.where, {
39070
40226
  allowUnqualifiedFields: true,
39071
40227
  fieldTypes,
@@ -39084,36 +40240,110 @@ async function executeExistingRecordValidationCore(stmt, client, options, cacheC
39084
40240
  flat: flatten(record2, null)
39085
40241
  })).filter((row) => stmt.where === null || evalWhere(stmt.where, row.flat, (field) => evaluationTypes.get(field.field)));
39086
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
+ }
39087
40291
  for (const row of validationRows) {
39088
- for (const field of targets) {
39089
- const raw = row.record[field.code]?.value;
39090
- const validation = validateAndNormalizeDmlValue(raw, field, numberPrecision);
39091
- if (validation.ok) continue;
39092
- rows.push({
39093
- "$id": row.id,
39094
- "$err_field": field.code,
39095
- "$err_code": validation.code,
39096
- "$err_message": validation.message,
39097
- "$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)
39098
40301
  });
39099
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
+ }
39100
40324
  for (const check2 of evaluateCustomChecks(checkGroups, row.flat, (field) => evaluationTypes.get(field.field))) {
39101
- rows.push({
39102
- "$id": row.id,
39103
- "$err_field": "",
39104
- "$err_code": "ERR_CHECK",
39105
- "$err_message": check2.message,
39106
- "$err_value": ""
39107
- });
40325
+ appendError({ id: row.id, field: "", code: "ERR_CHECK", message: check2.message, value: "" });
39108
40326
  }
39109
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);
40336
+ }
40337
+ }
40338
+ const columns = stmt.summary ? EXISTING_VALIDATION_SUMMARY_COLUMNS : EXISTING_VALIDATION_COLUMNS;
39110
40339
  const result = {
39111
40340
  type: "SELECT",
39112
- columns: [...EXISTING_VALIDATION_COLUMNS],
40341
+ columns: [...columns],
39113
40342
  rows,
39114
- rowCount: rows.length
40343
+ rowCount: rows.length,
40344
+ validateStats: { errorRecords: errorRecordIds.size, errorCount }
39115
40345
  };
39116
- materializedMetaBySelectResult.set(result, existingValidationColumnMeta());
40346
+ materializedMetaBySelectResult.set(result, existingValidationColumnMeta(stmt.summary === true));
39117
40347
  return result;
39118
40348
  }
39119
40349
  var TEMP_TABLE_MAX_ROWS = 1e4;
@@ -39143,7 +40373,7 @@ var BatchTimeoutError = class extends Error {
39143
40373
  }
39144
40374
  };
39145
40375
  async function executeBatch(sql, client, options = {}) {
39146
- const statements = parseSqlBatch(sql);
40376
+ const statements = parseSqlBatch(sql, options.enableImport === true);
39147
40377
  const analysis = analyzeBatch(statements);
39148
40378
  const injectedVariables = validateDeclaredBatchVariables(statements, options.variables);
39149
40379
  const batchOptions = { ...options, variables: injectedVariables };
@@ -39196,11 +40426,12 @@ async function executeBatch(sql, client, options = {}) {
39196
40426
  const userConfirm = batchOptions.confirm;
39197
40427
  const stmtOptions = userConfirm ? {
39198
40428
  ...batchOptions,
39199
- confirm: (count, operation) => userConfirm(count, operation, {
40429
+ confirm: (count, operation, detailContext) => userConfirm(count, operation, {
39200
40430
  statementIndex: i,
39201
40431
  statementCount: statements.length,
39202
40432
  statementType: info.statementType,
39203
- targetAppId: info.targetAppId
40433
+ targetAppId: info.targetAppId,
40434
+ ...detailContext?.importDetail ? { importDetail: detailContext.importDetail } : {}
39204
40435
  })
39205
40436
  } : batchOptions;
39206
40437
  const searchAbortCollector = { aborted: false };
@@ -39309,11 +40540,14 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
39309
40540
  result.columns,
39310
40541
  result.rows,
39311
40542
  options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS,
39312
- materializedMetaBySelectResult.get(result) ?? existingValidationColumnMeta()
40543
+ materializedMetaBySelectResult.get(result) ?? existingValidationColumnMeta(resolvedStmt.summary === true)
39313
40544
  );
39314
40545
  }
39315
40546
  return { result };
39316
40547
  }
40548
+ if (resolvedStmt.type === "IMPORT") {
40549
+ return { result: await executeImport(resolvedStmt, client, options, cacheContext, tempTables) };
40550
+ }
39317
40551
  if ("validateOnly" in resolvedStmt && resolvedStmt.validateOnly === true) {
39318
40552
  const result = await executeDmlValidation(
39319
40553
  resolvedStmt,
@@ -39475,9 +40709,9 @@ function safeJsonStringify(v) {
39475
40709
  return String(v);
39476
40710
  }
39477
40711
  }
39478
- function parseSqlBatch(sql) {
40712
+ function parseSqlBatch(sql, enableImport = false) {
39479
40713
  const tokens = new Lexer(sql).tokenize();
39480
- return new Parser(tokens).parseStatements();
40714
+ return new Parser(tokens, { import: enableImport }).parseStatements();
39481
40715
  }
39482
40716
  function evaluateScalarExpr(expr) {
39483
40717
  switch (expr.type) {
@@ -40546,8 +41780,14 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
40546
41780
  }
40547
41781
  } else if (column.type === "ARITH_AGG_COL" || column.type === "ARITH_COL") {
40548
41782
  meta3 = syntheticColumnMeta("number");
40549
- } else if (column.type === "LITERAL_COL" || column.type === "SCALAR_VALUE_COL") {
41783
+ } else if (column.type === "LITERAL_COL") {
40550
41784
  meta3 = syntheticColumnMeta("string");
41785
+ } else if (column.type === "SCALAR_VALUE_COL") {
41786
+ const expr = column.expr;
41787
+ if (expr.type === "STRING_FUNC") meta3 = stringFunctionColumnMeta(expr);
41788
+ else if (expr.type === "NUMBER" || expr.type === "SCALAR_ARITH") meta3 = syntheticColumnMeta("number");
41789
+ else if (expr.type === "FIELD") meta3 = resolveField2(expr);
41790
+ else meta3 = syntheticColumnMeta("string");
40551
41791
  } else if (column.type === "STRFUNC_COL") {
40552
41792
  meta3 = stringFunctionColumnMeta(column.expr);
40553
41793
  } else if (column.type === "WINDOW_COL") {
@@ -41361,9 +42601,10 @@ async function buildSortKindsForSelect(stmt, client, cacheContext) {
41361
42601
  return sortKinds;
41362
42602
  }
41363
42603
  function convertProcessRowValue(raw, dstFieldType) {
41364
- const USER_TYPES2 = /* @__PURE__ */ new Set(["USER_SELECT", "ORGANIZATION_SELECT", "GROUP_SELECT"]);
42604
+ if (typeof raw !== "string") return raw;
42605
+ const USER_TYPES4 = /* @__PURE__ */ new Set(["USER_SELECT", "ORGANIZATION_SELECT", "GROUP_SELECT"]);
41365
42606
  const ARRAY_TYPES3 = /* @__PURE__ */ new Set(["CHECK_BOX", "MULTI_SELECT"]);
41366
- if (USER_TYPES2.has(dstFieldType ?? "")) {
42607
+ if (USER_TYPES4.has(dstFieldType ?? "")) {
41367
42608
  if (raw === "") return [];
41368
42609
  try {
41369
42610
  const parsed = JSON.parse(raw);
@@ -41428,11 +42669,13 @@ function assertValidDmlRecords(records, targetFields, fieldInfos, numberPrecisio
41428
42669
  records.forEach((record2, rowIndex) => {
41429
42670
  for (const code of targetFields) {
41430
42671
  const info = infoByCode.get(code);
41431
- const result = validateAndNormalizeDmlValue(record2[code]?.value ?? "", info, numberPrecision);
42672
+ const original = record2[code]?.value ?? "";
42673
+ const result = validateAndNormalizeDmlValue(original, info, numberPrecision);
41432
42674
  if (!result.ok) {
41433
42675
  throw new Error(`DmlValidationError: ${result.code} ${result.message} (row=${rowIndex + 1}, field=${code})`);
41434
42676
  }
41435
- record2[code] = { value: result.value };
42677
+ const preserveCodes = ["USER_SELECT", "ORGANIZATION_SELECT", "GROUP_SELECT"].includes(info.fieldType) && Array.isArray(original) && original.every((item) => typeof item === "object" && item !== null && "code" in item);
42678
+ record2[code] = { value: preserveCodes ? original : result.value };
41436
42679
  }
41437
42680
  });
41438
42681
  }
@@ -41592,6 +42835,8 @@ async function materializeValidationCandidates(stmt, operation, client, options,
41592
42835
  if (stmt.type === "UPDATE") return materializeUpdateValidationCandidates(stmt, client, options, cacheContext, tempTables);
41593
42836
  let rows;
41594
42837
  let sourceRows;
42838
+ let sourcePresence;
42839
+ let sourceRowErrors;
41595
42840
  let evaluationTypes;
41596
42841
  if (stmt.type === "INSERT" || stmt.type === "UPSERT") {
41597
42842
  assertInsertCheckRefs(stmt, stmt.fields);
@@ -41601,7 +42846,7 @@ async function materializeValidationCandidates(stmt, operation, client, options,
41601
42846
  (value, i) => value.type === "CASE_VALUE" ? evalCaseWhenValue(value.expr, {}, infoByCode.get(stmt.fields[i])?.fieldType) : value
41602
42847
  ));
41603
42848
  } else {
41604
- const selectResult = tempTables && tempTables.size > 0 ? await executeQueryWithCte(stmt.select, client, { ...options, onLimitReached: "error" }, tempTables, cacheContext) : await executeSelect(stmt.select, client, { ...options, onLimitReached: "error" }, cacheContext, void 0, true);
42849
+ const selectResult = await dmlSourceMaterializer.materialize(stmt, client, options, cacheContext, tempTables, [...infoByCode.values()]);
41605
42850
  const hasChecks = (stmt.checkGroups?.length ?? 0) > 0;
41606
42851
  if (selectResult.columns.length < stmt.fields.length || !hasChecks && selectResult.columns.length !== stmt.fields.length) {
41607
42852
  throw new Error(`SELECT \u306E\u5217\u6570\uFF08${selectResult.columns.length}\uFF09\u3068 DML \u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u6570\uFF08${stmt.fields.length}\uFF09\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093`);
@@ -41611,7 +42856,9 @@ async function materializeValidationCandidates(stmt, operation, client, options,
41611
42856
  }
41612
42857
  assertInsertCheckRefs(stmt, selectResult.columns);
41613
42858
  sourceRows = selectResult.rows;
41614
- const meta3 = materializedMetaBySelectResult.get(selectResult);
42859
+ sourcePresence = selectResult.importPresence;
42860
+ sourceRowErrors = selectResult.importRowErrors;
42861
+ const meta3 = selectResult.columnMeta;
41615
42862
  evaluationTypes = new Map(selectResult.columns.map((column) => {
41616
42863
  const columnMeta = meta3?.get(column);
41617
42864
  const type = columnMeta?.fieldType ?? (columnMeta?.semantics?.compareMode === "number" || columnMeta?.sortKind === "number" ? "NUMBER" : "SINGLE_LINE_TEXT");
@@ -41624,8 +42871,10 @@ async function materializeValidationCandidates(stmt, operation, client, options,
41624
42871
  rowNumber: index + 1,
41625
42872
  operation,
41626
42873
  mode: "create",
41627
- payload: new Map(stmt.fields.map((field, i) => [field, values[i]])),
41628
- preErrors: [],
42874
+ payload: new Map(stmt.fields.flatMap(
42875
+ (field, i) => sourcePresence && !sourcePresence[index]?.has(field) ? [] : [[field, values[i]]]
42876
+ )),
42877
+ preErrors: [...sourceRowErrors?.[index] ?? []],
41629
42878
  record: {},
41630
42879
  evaluationRow: sourceRows?.[index] ?? Object.fromEntries(
41631
42880
  stmt.fields.map((field, i) => [field, renderValidationValue(values[i])])
@@ -41638,13 +42887,17 @@ async function materializeValidationCandidates(stmt, operation, client, options,
41638
42887
  }
41639
42888
  const fieldTypes = new Map([...infoByCode].map(([code, info]) => [code, info.fieldType]));
41640
42889
  const rowKeys = candidates.map((candidate) => stmt.keyFields.map((key) => renderValidationValue(candidate.payload.get(key))));
41641
- const targets = await resolveUpsertTargets(stmt.appId, stmt.keyFields, rowKeys, client, options, fieldTypes);
41642
42890
  const numeric = stmt.keyFields.map((key) => fieldTypes.get(key) === "NUMBER");
41643
42891
  const keyCounts = /* @__PURE__ */ new Map();
41644
42892
  for (const parts of rowKeys) {
41645
42893
  const key = upsertNormalizedKey(parts, numeric);
41646
42894
  keyCounts.set(key, (keyCounts.get(key) ?? 0) + 1);
41647
42895
  }
42896
+ const isImport = importSourceByDmlStatement.has(stmt);
42897
+ if (isImport && [...keyCounts.values()].some((count) => count > 1)) {
42898
+ throw new Error("ERR_KEY_DUP_SOURCE: UPSERT \u30BD\u30FC\u30B9\u5185\u3067\u30AD\u30FC\u304C\u91CD\u8907\u3057\u3066\u3044\u307E\u3059");
42899
+ }
42900
+ const targets = await resolveUpsertTargets(stmt.appId, stmt.keyFields, rowKeys, client, options, fieldTypes);
41648
42901
  candidates.forEach((candidate, index) => {
41649
42902
  const parts = rowKeys[index];
41650
42903
  const targetId = lookupUpsertTarget(targets, parts);
@@ -41653,7 +42906,7 @@ async function materializeValidationCandidates(stmt, operation, client, options,
41653
42906
  stmt.keyFields.forEach((key, keyIndex) => {
41654
42907
  if (parts[keyIndex] === "") candidate.preErrors.push({ field: key, code: "ERR_KEY_EMPTY", message: `UPSERT \u30AD\u30FC ${key} \u306F\u7A7A\u306B\u3067\u304D\u307E\u305B\u3093` });
41655
42908
  });
41656
- if ((keyCounts.get(upsertNormalizedKey(parts, numeric)) ?? 0) > 1) {
42909
+ if (!isImport && (keyCounts.get(upsertNormalizedKey(parts, numeric)) ?? 0) > 1) {
41657
42910
  candidate.preErrors.push({ field: stmt.keyFields[0], code: "ERR_KEY_DUP_SOURCE", message: "UPSERT \u30BD\u30FC\u30B9\u5185\u3067\u30AD\u30FC\u304C\u91CD\u8907\u3057\u3066\u3044\u307E\u3059" });
41658
42911
  }
41659
42912
  });
@@ -42013,12 +43266,508 @@ async function executeInsert(stmt, client, options, cacheContext) {
42013
43266
  insertedCount: createdIds.flat().length
42014
43267
  };
42015
43268
  }
43269
+ function importPlaceholderSelect() {
43270
+ return {
43271
+ type: "SELECT",
43272
+ distinct: false,
43273
+ columns: [],
43274
+ from: { appId: 0, alias: null, cteName: NO_FROM_CTE_NAME },
43275
+ joins: [],
43276
+ where: null,
43277
+ groupBy: [],
43278
+ having: null,
43279
+ orderMode: "CANONICAL",
43280
+ orderBy: [],
43281
+ limit: null,
43282
+ offset: null
43283
+ };
43284
+ }
43285
+ async function executeImport(stmt, client, options, cacheContext, tempTables) {
43286
+ if (!options.enableImport) throw new Error("UnsupportedError: IMPORT capability is disabled.");
43287
+ const handle = resolveImportSource(stmt.source.sourceName, options.importSource);
43288
+ if (stmt.targets?.some((target) => target.kind === "SUBTABLE")) {
43289
+ if (!stmt.validateOnly && !options.supportsImportConfirmDetail) {
43290
+ throw new Error("UnsupportedError: IMPORT subtable mutation requires a surface that displays parent/table replacement and deletion detail; use VALIDATE ONLY/EXPLAIN.");
43291
+ }
43292
+ if (stmt.source.kind === "CSV") {
43293
+ if (stmt.writeMode !== "UPDATE_RECORD_NUMBER" || !stmt.recordNumberSourceHeader) throw new Error("ArgumentError: CSV subtable replacement requires IMPORT UPDATE and MATCH RECORD NUMBER SOURCE.");
43294
+ if (!stmt.replaceSubtables?.length) throw new Error("ArgumentError: CSV subtable replacement requires REPLACE SUBTABLES (...).");
43295
+ const declared = new Set(stmt.targets.filter((target) => target.kind === "SUBTABLE").map((target) => target.subtableCode));
43296
+ if (stmt.replaceSubtables.some((table) => !declared.has(table))) throw new Error("ArgumentError: REPLACE SUBTABLES contains a table not declared in INTO.");
43297
+ for (const target of stmt.targets.filter((target2) => target2.kind === "SUBTABLE")) {
43298
+ if (!target.rowIdSourceHeader || !stmt.replaceSubtables.includes(target.subtableCode)) throw new Error(`ArgumentError: CSV subtable ${target.subtableCode} requires ROW ID SOURCE and REPLACE SUBTABLES declaration.`);
43299
+ }
43300
+ }
43301
+ if (stmt.validationErrorTable && !tempTables) throw new Error("ArgumentError: VALIDATE ONLY INTO requires a batch.");
43302
+ const targets = stmt.targets;
43303
+ const fieldInfos = await getFieldsCached(stmt.appId, client, cacheContext);
43304
+ const targetCodes = targets.flatMap((target) => target.kind === "FIELD" ? [target.field] : target.children);
43305
+ const numberPrecision = fieldInfos.some((info) => targetCodes.includes(info.code) && info.fieldType === "NUMBER") ? await getNumberPrecisionCached(stmt.appId, client, cacheContext) : void 0;
43306
+ const payload = await loadImportSource(handle, /* @__PURE__ */ new Map());
43307
+ const materialized = stmt.source.kind === "JSON" ? materializeJsonImportRecords(stmt.source, payload, targets, options.maxRecords ?? 1e4) : materializeCliKintoneCsvImportRecords(stmt.source, payload, targets, stmt.replaceSubtables ?? [], options.maxRecords ?? 1e4, stmt.recordNumberSourceHeader);
43308
+ const operation = stmt.source.kind === "CSV" ? "UPDATE" : stmt.keyFields ? "UPSERT" : "INSERT";
43309
+ const prepared = prepareImportRecords(materialized, targets, fieldInfos, numberPrecision, operation);
43310
+ if (stmt.source.kind === "CSV") return executeCsvSubtableReplacement(stmt, materialized, prepared, fieldInfos, client, options, tempTables);
43311
+ assertImportRejectLimit(prepared, stmt.rejectLimit);
43312
+ const payloadFields = [...new Set(targets.flatMap((target) => target.kind === "FIELD" ? [target.field] : target.children))];
43313
+ const errors = materializeImportValidationErrors(prepared.errors, payloadFields);
43314
+ const columns = [...payloadFields, ...IMPORT_VALIDATION_META_COLUMNS];
43315
+ const invalidRows = prepared.invalidParentRows.size;
43316
+ const detail = {
43317
+ preflight: "ACTUAL_DATA",
43318
+ parents: { total: prepared.parents.length, valid: prepared.parents.length - invalidRows, invalid: invalidRows, mutationCandidates: prepared.parents.filter((parent) => parent.valid).length },
43319
+ tables: Object.fromEntries(prepared.tableCounts),
43320
+ writesKintone: false
43321
+ };
43322
+ const result2 = {
43323
+ type: "VALIDATION",
43324
+ operation,
43325
+ validatedRows: prepared.parents.length,
43326
+ validRows: prepared.parents.length - invalidRows,
43327
+ invalidRows,
43328
+ errorCount: errors.length,
43329
+ columns,
43330
+ errors,
43331
+ importDetail: detail,
43332
+ ...stmt.validationErrorTable ? { errTable: stmt.validationErrorTable } : {}
43333
+ };
43334
+ if (stmt.validationErrorTable && tempTables) appendValidationErrors(
43335
+ tempTables,
43336
+ stmt.validationErrorTable,
43337
+ columns,
43338
+ errors,
43339
+ options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS,
43340
+ /* @__PURE__ */ new Map()
43341
+ );
43342
+ if (stmt.validateOnly) return result2;
43343
+ assertJsonImportHasNoRowIds(materialized);
43344
+ if (prepared.errors.length > 0 && !stmt.onErrorSkip) {
43345
+ const first = prepared.errors[0];
43346
+ throw new Error(`DmlValidationError: ${first.code} ${first.message} (row=${first.parentRow}, field=${first.field})`);
43347
+ }
43348
+ if (stmt.onErrorSkip) {
43349
+ if (!tempTables || !stmt.errorTable) throw new Error("ArgumentError: ON ERROR SKIP requires a batch and INTO error table.");
43350
+ appendValidationErrors(
43351
+ tempTables,
43352
+ stmt.errorTable,
43353
+ columns,
43354
+ errors,
43355
+ options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS,
43356
+ /* @__PURE__ */ new Map()
43357
+ );
43358
+ }
43359
+ const validParents = prepared.parents.filter((parent) => parent.valid);
43360
+ const fieldTypes = new Map(fieldInfos.map((info) => [info.code, info.fieldType]));
43361
+ const targetIds = validParents.map(() => void 0);
43362
+ if (stmt.keyFields) {
43363
+ for (const key of stmt.keyFields) if (!stmt.fields.includes(key)) {
43364
+ throw new Error(`ON DUPLICATE \u306E\u30AD\u30FC\u300C${key}\u300D\u304C UPSERT \u30D5\u30A3\u30FC\u30EB\u30C9\u306B\u542B\u307E\u308C\u3066\u3044\u307E\u305B\u3093`);
43365
+ }
43366
+ const numeric = stmt.keyFields.map((key) => fieldTypes.get(key) === "NUMBER");
43367
+ const sourceKeys = /* @__PURE__ */ new Set();
43368
+ const rowKeys = validParents.map((parent) => stmt.keyFields.map((key) => String(parent.top[key]?.value ?? "")));
43369
+ for (const parts of rowKeys) {
43370
+ const normalized = upsertNormalizedKey(parts, numeric);
43371
+ if (sourceKeys.has(normalized)) throw new Error("ERR_KEY_DUP_SOURCE: UPSERT \u30BD\u30FC\u30B9\u5185\u3067\u30AD\u30FC\u304C\u91CD\u8907\u3057\u3066\u3044\u307E\u3059");
43372
+ sourceKeys.add(normalized);
43373
+ }
43374
+ const targetsIndex = await resolveUpsertTargets(stmt.appId, stmt.keyFields, rowKeys, client, options, fieldTypes);
43375
+ rowKeys.forEach((parts, index) => {
43376
+ targetIds[index] = lookupUpsertTarget(targetsIndex, parts);
43377
+ });
43378
+ }
43379
+ const tableCodes = targets.filter((target) => target.kind === "SUBTABLE").map((target) => target.subtableCode);
43380
+ const existingById = /* @__PURE__ */ new Map();
43381
+ const updateIds = targetIds.filter((id) => id !== void 0);
43382
+ for (const chunk2 of splitChunks([...new Set(updateIds)], 100)) {
43383
+ const response = await client.getRecords({ app: stmt.appId, query: `$id in (${chunk2.join(",")}) limit 500`, fields: ["$id", "$revision", ...tableCodes] });
43384
+ for (const record2 of response.records) {
43385
+ const id = Number(record2["$id"]?.value);
43386
+ const revision = Number(record2["$revision"]?.value);
43387
+ if (Number.isFinite(id)) existingById.set(id, { id, ...Number.isFinite(revision) ? { revision } : {}, record: record2 });
43388
+ }
43389
+ }
43390
+ const writePlan = buildJsonSubtableWritePlan(validParents, targetIds, existingById);
43391
+ const importDetail = {
43392
+ kind: "IMPORT_JSON_SUBTABLE",
43393
+ rowIdPolicy: "DROP_AND_RENUMBER_ALL",
43394
+ parentsToWrite: writePlan.length,
43395
+ insertedParents: writePlan.filter((parent) => parent.mode === "INSERT").length,
43396
+ updatedParents: writePlan.filter((parent) => parent.mode === "UPDATE").length,
43397
+ hasDeletes: writePlan.some((parent) => parent.tables.some((table) => table.deleteRows > 0)),
43398
+ parents: writePlan.map((parent) => ({ parentRow: parent.parentRow, mode: parent.mode, ...parent.targetId === void 0 ? {} : { targetId: parent.targetId }, tables: parent.tables }))
43399
+ };
43400
+ if (writePlan.length > 0) {
43401
+ if (!options.confirm) throw new Error("UnsupportedError: JSON IMPORT subtable mutation requires explicit confirmation detail approval.");
43402
+ const ok = await options.confirm(writePlan.length, stmt.keyFields ? "UPDATE" : "INSERT", { statementIndex: 0, statementCount: 1, statementType: "IMPORT", targetAppId: stmt.appId, importDetail });
43403
+ if (!ok) throw new OperationCancelledError(stmt.keyFields ? "UPDATE" : "INSERT", writePlan.length);
43404
+ }
43405
+ const toScalarMap = (record2) => new Map(
43406
+ Object.entries(record2).map(([code, field]) => [code, field.value])
43407
+ );
43408
+ const payloadFor = (parent) => buildJsonImportRecordPayload(
43409
+ toScalarMap(parent.top),
43410
+ new Map([...parent.subtables].map(([table, rows]) => [table, rows.map((row) => ({ values: toScalarMap(row) }))]))
43411
+ );
43412
+ const inserts = writePlan.filter((parent) => parent.mode === "INSERT");
43413
+ const updates = writePlan.filter((parent) => parent.mode === "UPDATE");
43414
+ const createdIds = [];
43415
+ for (let i = 0; i < inserts.length; i += 100) {
43416
+ const response = await client.postRecords({ app: stmt.appId, records: inserts.slice(i, i + 100).map(payloadFor) });
43417
+ createdIds.push(response.ids);
43418
+ }
43419
+ for (let i = 0; i < updates.length; i += 100) await client.putRecords({
43420
+ app: stmt.appId,
43421
+ records: updates.slice(i, i + 100).map((parent) => ({ id: parent.targetId, ...parent.revision === void 0 ? {} : { revision: parent.revision }, record: payloadFor(parent) }))
43422
+ });
43423
+ return stmt.keyFields ? { type: "UPSERT", insertedCount: createdIds.flat().length, updatedCount: updates.length, affectedRows: writePlan.length, skippedRows: prepared.invalidParentRows.size, rejectLimit: stmt.rejectLimit, ...stmt.errorTable ? { errTable: stmt.errorTable } : {}, importDetail } : { type: "INSERT", createdIds, insertedCount: createdIds.flat().length, affectedRows: writePlan.length, skippedRows: prepared.invalidParentRows.size, rejectLimit: stmt.rejectLimit, ...stmt.errorTable ? { errTable: stmt.errorTable } : {}, importDetail };
43424
+ }
43425
+ if (stmt.writeMode === "UPDATE_RECORD_NUMBER") {
43426
+ return executeImportRecordNumberUpdate(
43427
+ stmt,
43428
+ handle,
43429
+ client,
43430
+ options,
43431
+ cacheContext,
43432
+ tempTables
43433
+ );
43434
+ }
43435
+ const common = {
43436
+ appId: stmt.appId,
43437
+ fields: stmt.fields,
43438
+ select: importPlaceholderSelect(),
43439
+ validateOnly: stmt.validateOnly,
43440
+ validationErrorTable: stmt.validationErrorTable,
43441
+ onErrorSkip: stmt.onErrorSkip,
43442
+ errorTable: stmt.errorTable,
43443
+ rejectLimit: stmt.rejectLimit,
43444
+ checkGroups: stmt.checkGroups
43445
+ };
43446
+ const generated = stmt.keyFields ? { type: "UPSERT_SELECT", ...common, keyFields: stmt.keyFields } : { type: "INSERT_SELECT", ...common };
43447
+ const executionSource = { source: stmt.source, handle, cache: /* @__PURE__ */ new Map() };
43448
+ importSourceByDmlStatement.set(generated, executionSource);
43449
+ const withAudit = (result2) => {
43450
+ if (executionSource.audit) Object.assign(result2, { importAudit: executionSource.audit });
43451
+ return result2;
43452
+ };
43453
+ if (generated.validateOnly) {
43454
+ if (generated.validationErrorTable && !tempTables) throw new Error("ArgumentError: VALIDATE ONLY INTO requires a batch.");
43455
+ const result2 = await executeDmlValidation(generated, client, { ...options, onLimitReached: "error" }, cacheContext, tempTables, 1);
43456
+ if (generated.validationErrorTable && tempTables) {
43457
+ appendValidationErrors(
43458
+ tempTables,
43459
+ generated.validationErrorTable,
43460
+ result2.columns,
43461
+ result2.errors,
43462
+ options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS,
43463
+ materializedMetaByValidationResult.get(result2) ?? /* @__PURE__ */ new Map()
43464
+ );
43465
+ }
43466
+ return withAudit(result2);
43467
+ }
43468
+ if (generated.onErrorSkip) {
43469
+ if (!tempTables) throw new Error("ArgumentError: ON ERROR SKIP requires a batch.");
43470
+ const result2 = await (generated.type === "UPSERT_SELECT" ? executeOnErrorSkip(generated, client, options, cacheContext, tempTables, 1) : executeOnErrorSkip(generated, client, options, cacheContext, tempTables, 1));
43471
+ return withAudit(result2);
43472
+ }
43473
+ const result = await (generated.type === "UPSERT_SELECT" ? executeUpsertSelect(generated, client, options, cacheContext, tempTables) : executeInsertSelect(generated, client, options, cacheContext, tempTables));
43474
+ return withAudit(result);
43475
+ }
43476
+ async function executeCsvSubtableReplacement(stmt, materialized, preparedBase, fieldInfos, client, options, tempTables) {
43477
+ if (!stmt.recordNumberSourceHeader || !stmt.replaceSubtables?.length) throw new Error("InternalError: incomplete CSV subtable replacement AST.");
43478
+ assertNoDuplicateCsvSubtableRowIds(materialized.records);
43479
+ const rawKeys = materialized.records.map((record2) => record2.recordNumberSourceValue ?? "");
43480
+ const keyPlan = preflightImportRecordNumbers(rawKeys, stmt.recordNumberSourceHeader);
43481
+ const tableCodes = [...stmt.replaceSubtables];
43482
+ const ownershipTableCodes = [...new Set(fieldInfos.filter((info) => !info.inSubtable && info.fieldType === "SUBTABLE").map((info) => info.code))];
43483
+ const allRecords = await fetchAll(client.getRecords, stmt.appId, "", ["$id", "$revision", ...ownershipTableCodes], {
43484
+ maxRecords: options.maxRecords ?? 1e4,
43485
+ parallel: options.fetchParallel ?? 1,
43486
+ onLimit: "error"
43487
+ });
43488
+ const existingById = /* @__PURE__ */ new Map();
43489
+ const ownership = /* @__PURE__ */ new Map();
43490
+ for (const record2 of allRecords) {
43491
+ const id = Number(record2["$id"]?.value);
43492
+ const revision = Number(record2["$revision"]?.value);
43493
+ if (!Number.isFinite(id)) continue;
43494
+ existingById.set(id, { id, ...Number.isFinite(revision) ? { revision } : {}, record: record2 });
43495
+ for (const table of ownershipTableCodes) {
43496
+ const rows = record2[table]?.value;
43497
+ if (!Array.isArray(rows)) continue;
43498
+ for (const row of rows) if (row.id) {
43499
+ const owners = ownership.get(row.id) ?? [];
43500
+ owners.push({ parentId: id, table });
43501
+ ownership.set(row.id, owners);
43502
+ }
43503
+ }
43504
+ }
43505
+ const targetIds = keyPlan.normalized.map((key) => key === null ? void 0 : Number(key));
43506
+ const parents = preparedBase.parents.map((parent, index) => {
43507
+ const errors2 = [...parent.errors];
43508
+ for (const error51 of keyPlan.errors[index]) errors2.push({
43509
+ operation: "UPDATE",
43510
+ parentRow: parent.parentRow,
43511
+ field: error51.field,
43512
+ code: error51.code,
43513
+ message: error51.message,
43514
+ sourceValues: materialized.records[index].top
43515
+ });
43516
+ const targetId = targetIds[index];
43517
+ if (targetId !== void 0 && !existingById.has(targetId)) errors2.push({
43518
+ operation: "UPDATE",
43519
+ parentRow: parent.parentRow,
43520
+ field: stmt.recordNumberSourceHeader,
43521
+ code: "ERR_RECORD_NUMBER_NOT_FOUND",
43522
+ message: `record number ${targetId} does not exist in APP${stmt.appId}`,
43523
+ sourceValues: materialized.records[index].top
43524
+ });
43525
+ return { ...parent, valid: errors2.length === 0, errors: errors2 };
43526
+ });
43527
+ const initialPlan = buildCsvSubtableReplacementPlan(materialized.records, parents, targetIds, existingById, ownership);
43528
+ const planErrors = initialPlan.flatMap((parent) => [...parent.errors]);
43529
+ const invalidParentRows = new Set(initialPlan.filter((parent) => !parent.valid).map((parent) => parent.parentRow));
43530
+ const prepared = { ...preparedBase, parents, errors: planErrors, invalidParentRows };
43531
+ assertImportRejectLimit(prepared, stmt.rejectLimit);
43532
+ const validPlan = initialPlan.filter((parent) => parent.valid);
43533
+ const allTables = initialPlan.flatMap((parent) => parent.tables);
43534
+ const sum = (table, key) => allTables.filter((item) => item.table === table).reduce((n, item) => n + Number(item[key]), 0);
43535
+ const tableDetail = Object.fromEntries(tableCodes.map((table) => [table, {
43536
+ existingRows: sum(table, "existingRows"),
43537
+ inputRows: sum(table, "inputRows"),
43538
+ updateRows: sum(table, "updateRows"),
43539
+ addRows: sum(table, "addRows"),
43540
+ deleteRows: sum(table, "deleteRows"),
43541
+ rowIdNotFound: sum(table, "rowIdNotFound")
43542
+ }]));
43543
+ const importDetail = {
43544
+ kind: "IMPORT_CSV_SUBTABLE_REPLACE",
43545
+ rowIdPolicy: "PRESERVE_EXISTING",
43546
+ parentsToWrite: validPlan.length,
43547
+ insertedParents: 0,
43548
+ updatedParents: validPlan.length,
43549
+ hasDeletes: validPlan.some((parent) => parent.tables.some((table) => table.deleteRows > 0)),
43550
+ totalDeleteRows: validPlan.flatMap((parent) => parent.tables).reduce((n, table) => n + table.deleteRows, 0),
43551
+ rowIdNotFound: validPlan.flatMap((parent) => parent.tables).reduce((n, table) => n + table.rowIdNotFound, 0),
43552
+ invalidParents: invalidParentRows.size,
43553
+ parents: validPlan.map((parent) => ({ parentRow: parent.parentRow, mode: "UPDATE", targetId: parent.targetId, tables: parent.tables }))
43554
+ };
43555
+ const payloadFields = [...new Set(stmt.targets.flatMap((target) => target.kind === "FIELD" ? [target.field] : target.children))];
43556
+ const errors = materializeImportValidationErrors(planErrors, payloadFields);
43557
+ const columns = [...payloadFields, ...IMPORT_VALIDATION_META_COLUMNS];
43558
+ if (stmt.validateOnly) {
43559
+ if (stmt.validationErrorTable && !tempTables) throw new Error("ArgumentError: VALIDATE ONLY INTO requires a batch.");
43560
+ if (stmt.validationErrorTable && tempTables) appendValidationErrors(tempTables, stmt.validationErrorTable, columns, errors, options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS, /* @__PURE__ */ new Map());
43561
+ return {
43562
+ type: "VALIDATION",
43563
+ operation: "UPDATE",
43564
+ validatedRows: parents.length,
43565
+ validRows: parents.length - invalidParentRows.size,
43566
+ invalidRows: invalidParentRows.size,
43567
+ errorCount: errors.length,
43568
+ columns,
43569
+ errors,
43570
+ ...stmt.validationErrorTable ? { errTable: stmt.validationErrorTable } : {},
43571
+ importDetail: { preflight: "ACTUAL_DATA", parents: { total: parents.length, valid: validPlan.length, invalid: invalidParentRows.size, mutationCandidates: validPlan.length }, tables: tableDetail, rowIdPolicy: "PRESERVE_EXISTING", rowIdNotFound: importDetail.rowIdNotFound, writesKintone: false }
43572
+ };
43573
+ }
43574
+ if (planErrors.length && !stmt.onErrorSkip) {
43575
+ const first = planErrors[0];
43576
+ throw new Error(`DmlValidationError: ${first.code} ${first.message} (row=${first.parentRow}, field=${first.field})`);
43577
+ }
43578
+ if (stmt.onErrorSkip) {
43579
+ if (!tempTables || !stmt.errorTable) throw new Error("ArgumentError: ON ERROR SKIP requires a batch and INTO error table.");
43580
+ appendValidationErrors(tempTables, stmt.errorTable, columns, errors, options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS, /* @__PURE__ */ new Map());
43581
+ }
43582
+ if (validPlan.length) {
43583
+ if (!options.supportsImportConfirmDetail || !options.confirm) throw new Error("UnsupportedError: CSV subtable replacement requires explicit rendered detail approval.");
43584
+ const ok = await options.confirm(validPlan.length, "UPDATE", { statementIndex: 0, statementCount: 1, statementType: "IMPORT", targetAppId: stmt.appId, importDetail });
43585
+ if (!ok) throw new OperationCancelledError("UPDATE", validPlan.length);
43586
+ }
43587
+ const scalarMap = (record2) => new Map(Object.entries(record2).map(([code, field]) => [code, field.value]));
43588
+ for (const parent of validPlan) {
43589
+ const record2 = buildImportRecordPayload(scalarMap(parent.top), new Map([...parent.subtables].map(([table, rows]) => [table, rows.map((row) => ({ ...row.rowId ? { rowId: row.rowId } : {}, values: scalarMap(row.record) }))])), "PRESERVE");
43590
+ await client.putRecords({ app: stmt.appId, records: [{ id: parent.targetId, ...parent.revision === void 0 ? {} : { revision: parent.revision }, record: record2 }] });
43591
+ }
43592
+ return { type: "UPDATE", updatedCount: validPlan.length, affectedRows: validPlan.length, skippedRows: invalidParentRows.size, rejectLimit: stmt.rejectLimit, ...stmt.errorTable ? { errTable: stmt.errorTable } : {}, importDetail };
43593
+ }
43594
+ async function executeImportRecordNumberUpdate(stmt, handle, client, options, cacheContext, tempTables) {
43595
+ if (stmt.source.kind !== "CSV" || stmt.source.mappingMode !== "BY_NAME" || !stmt.recordNumberSourceHeader) {
43596
+ throw new Error("InternalError: invalid IMPORT UPDATE AST.");
43597
+ }
43598
+ if (new Set(stmt.fields).size !== stmt.fields.length) throw new Error("ArgumentError: DML target fields contain duplicates.");
43599
+ const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
43600
+ const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
43601
+ const payload = await loadImportSource(handle, /* @__PURE__ */ new Map());
43602
+ const sourceTable = materializeCsvDmlSource(
43603
+ stmt.source,
43604
+ payload,
43605
+ options.maxRecords ?? 1e4,
43606
+ stmt.fields,
43607
+ fieldInfos,
43608
+ stmt.recordNumberSourceHeader
43609
+ );
43610
+ const keyValues = sourceTable.recordNumberSourceValues;
43611
+ if (!keyValues) throw new Error("InternalError: record-number source values were not materialized.");
43612
+ const keyPlan = preflightImportRecordNumbers(keyValues, stmt.recordNumberSourceHeader);
43613
+ const matchedIds = /* @__PURE__ */ new Set();
43614
+ const lookupKeys = [...new Set(keyPlan.normalized.filter((key) => key !== null))];
43615
+ for (let i = 0; i < lookupKeys.length; i += 100) {
43616
+ const chunk2 = lookupKeys.slice(i, i + 100);
43617
+ const response = await client.getRecords({
43618
+ app: stmt.appId,
43619
+ query: `$id in (${chunk2.join(",")}) limit 500`,
43620
+ fields: ["$id"]
43621
+ });
43622
+ for (const record2 of response.records) {
43623
+ const id = record2["$id"]?.value;
43624
+ if (typeof id === "string" && id !== "") matchedIds.add(id.replace(/^0+(?=\d)/, ""));
43625
+ }
43626
+ }
43627
+ const infoByCode = new Map(fieldInfos.map((info) => [info.code, info]));
43628
+ const evaluationTypes = new Map(stmt.fields.map((field) => [field, infoByCode.get(field)?.fieldType ?? "SINGLE_LINE_TEXT"]));
43629
+ const candidates = sourceTable.rows.map((row, index) => {
43630
+ const key = keyPlan.normalized[index];
43631
+ const preErrors = [
43632
+ ...sourceTable.importRowErrors?.[index] ?? [],
43633
+ ...keyPlan.errors[index]
43634
+ ];
43635
+ if (key !== null && !matchedIds.has(key)) preErrors.push({
43636
+ field: stmt.recordNumberSourceHeader,
43637
+ code: "ERR_RECORD_NUMBER_NOT_FOUND",
43638
+ message: `record number ${key} does not exist in APP${stmt.appId}`
43639
+ });
43640
+ return {
43641
+ rowNumber: index + 1,
43642
+ operation: "UPDATE",
43643
+ mode: "update",
43644
+ ...key !== null && matchedIds.has(key) ? { targetId: Number(key) } : {},
43645
+ payload: new Map([
43646
+ [stmt.recordNumberSourceHeader, keyValues[index]],
43647
+ ...stmt.fields.map((field) => [field, row[field] ?? ""])
43648
+ ]),
43649
+ preErrors,
43650
+ record: {},
43651
+ evaluationRow: row,
43652
+ evaluationFieldTypes: evaluationTypes
43653
+ };
43654
+ });
43655
+ const diagnosticFields = [stmt.recordNumberSourceHeader, ...stmt.fields];
43656
+ const validation = validateDmlCandidates(
43657
+ candidates,
43658
+ "UPDATE",
43659
+ diagnosticFields,
43660
+ stmt.fields,
43661
+ fieldInfos,
43662
+ 1,
43663
+ numberPrecision,
43664
+ stmt.checkGroups ?? [],
43665
+ false
43666
+ );
43667
+ const columns = [...diagnosticFields, ...VALIDATION_META_COLUMNS];
43668
+ const validationResult = {
43669
+ type: "VALIDATION",
43670
+ operation: "UPDATE",
43671
+ validatedRows: candidates.length,
43672
+ validRows: candidates.length - validation.invalidRows,
43673
+ invalidRows: validation.invalidRows,
43674
+ errorCount: validation.errors.length,
43675
+ columns,
43676
+ errors: validation.errors,
43677
+ ...stmt.validationErrorTable ?? stmt.errorTable ? { errTable: stmt.validationErrorTable ?? stmt.errorTable } : {}
43678
+ };
43679
+ Object.assign(validationResult, { importAudit: sourceTable.importAudit });
43680
+ if (stmt.validateOnly) {
43681
+ if (stmt.validationErrorTable && !tempTables) throw new Error("ArgumentError: VALIDATE ONLY INTO requires a batch.");
43682
+ if (stmt.validationErrorTable && tempTables) appendValidationErrors(
43683
+ tempTables,
43684
+ stmt.validationErrorTable,
43685
+ columns,
43686
+ validation.errors,
43687
+ options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS,
43688
+ /* @__PURE__ */ new Map()
43689
+ );
43690
+ return validationResult;
43691
+ }
43692
+ if (!stmt.onErrorSkip && validation.invalidRows > 0) {
43693
+ const first = validation.errors[0];
43694
+ throw new Error(`DmlValidationError: ${first.$err_code} ${first.$err_message} (row=${first.$err_row}, field=${first.$err_field})`);
43695
+ }
43696
+ if (stmt.onErrorSkip) {
43697
+ if (!tempTables || !stmt.errorTable) throw new Error("ArgumentError: ON ERROR SKIP requires a batch.");
43698
+ appendValidationErrors(
43699
+ tempTables,
43700
+ stmt.errorTable,
43701
+ columns,
43702
+ validation.errors,
43703
+ options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS,
43704
+ /* @__PURE__ */ new Map()
43705
+ );
43706
+ if (stmt.rejectLimit != null && validation.invalidRows > stmt.rejectLimit) {
43707
+ throw new RejectLimitExceededError(
43708
+ `rejected rows (${validation.invalidRows}) exceed REJECT LIMIT (${stmt.rejectLimit}).`,
43709
+ validationResult
43710
+ );
43711
+ }
43712
+ }
43713
+ const valid = candidates.filter((candidate) => !validation.invalidRowNumbers.has(candidate.rowNumber));
43714
+ if (options.confirm) {
43715
+ const ok = await options.confirm(valid.length, "UPDATE");
43716
+ if (!ok) throw new OperationCancelledError("UPDATE", valid.length);
43717
+ }
43718
+ const updates = valid.map((candidate) => ({ id: candidate.targetId, record: candidate.record }));
43719
+ for (let i = 0; i < updates.length; i += 100) {
43720
+ await client.putRecords({ app: stmt.appId, records: updates.slice(i, i + 100) });
43721
+ }
43722
+ const result = {
43723
+ type: "UPDATE",
43724
+ updatedCount: updates.length,
43725
+ ...stmt.onErrorSkip ? {
43726
+ affectedRows: updates.length,
43727
+ skippedRows: validation.invalidRows,
43728
+ rejectLimit: stmt.rejectLimit ?? null,
43729
+ errTable: stmt.errorTable
43730
+ } : {}
43731
+ };
43732
+ Object.assign(result, { insertedCount: 0, importAudit: sourceTable.importAudit });
43733
+ return result;
43734
+ }
43735
+ async function materializeDmlSource(stmt, client, options, cacheContext, tempTables, targetFields) {
43736
+ const imported = importSourceByDmlStatement.get(stmt);
43737
+ if (!imported) {
43738
+ const selected2 = tempTables && tempTables.size > 0 ? await executeQueryWithCte(stmt.select, client, options, tempTables, cacheContext, true) : await executeSelect(stmt.select, client, options, cacheContext, void 0, true);
43739
+ return { rows: selected2.rows, columns: selected2.columns, columnMeta: materializedMetaBySelectResult.get(selected2) };
43740
+ }
43741
+ const payload = await loadImportSource(imported.handle, imported.cache);
43742
+ const rowLimit = options.maxRecords ?? 1e4;
43743
+ const targetByCode = new Map((targetFields ?? []).map((info) => [info.code, info]));
43744
+ const jsonTargets = stmt.fields.map((code) => ({ code, fieldType: targetByCode.get(code)?.fieldType ?? "SINGLE_LINE_TEXT" }));
43745
+ const raw = imported.source.kind === "JSON" ? materializeJsonDmlSource(imported.source, payload, jsonTargets, rowLimit) : materializeCsvDmlSource(imported.source, payload, rowLimit, stmt.fields, targetFields);
43746
+ imported.audit = raw.importAudit;
43747
+ if (imported.source.kind === "JSON") return raw;
43748
+ if (!imported.source.projection) return raw;
43749
+ const projection = bindImportProjection(imported.source.projection);
43750
+ const tables = new Map(tempTables ?? []);
43751
+ tables.set(IMPORT_PROJECTION_SOURCE, raw);
43752
+ const selected = await executeQueryWithCte(projection, client, { ...options, onLimitReached: "error" }, tables, cacheContext, true);
43753
+ return { rows: selected.rows, columns: selected.columns, columnMeta: materializedMetaBySelectResult.get(selected) };
43754
+ }
43755
+ var dmlSourceMaterializer = { materialize: materializeDmlSource };
43756
+ function assertNoImportRowErrors(table) {
43757
+ for (let rowIndex = 0; rowIndex < (table.importRowErrors?.length ?? 0); rowIndex++) {
43758
+ const first = table.importRowErrors?.[rowIndex]?.[0];
43759
+ if (first) {
43760
+ throw new Error(`DmlValidationError: ${first.code} ${first.message} (row=${rowIndex + 1}, field=${first.field})`);
43761
+ }
43762
+ }
43763
+ }
42016
43764
  async function executeInsertSelect(stmt, client, options, cacheContext, cteCache) {
42017
43765
  if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext, cteCache);
42018
43766
  const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
42019
43767
  const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
42020
- const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
42021
- const { rows, columns } = selectResult;
43768
+ const sourceTable = await dmlSourceMaterializer.materialize(stmt, client, options, cacheContext, cteCache, fieldInfos);
43769
+ const { rows, columns } = sourceTable;
43770
+ assertNoImportRowErrors(sourceTable);
42022
43771
  if (columns.length !== stmt.fields.length) {
42023
43772
  const emptySourceHint = columns.length === 0 && rows.length === 0 ? "\u3002\u7D50\u679C\u304C 0 \u884C\u306E\u305F\u3081\u5217\u3092\u7279\u5B9A\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\uFF08SELECT * \u3092\u7A7A\u30BD\u30FC\u30B9\u306B\u4F7F\u3046\u3068\u5217\u3092\u6C7A\u5B9A\u3067\u304D\u307E\u305B\u3093\u3002\u660E\u793A\u5217\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\uFF09" : "";
42024
43773
  throw new Error(
@@ -42030,15 +43779,16 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
42030
43779
  if (!ok) throw new OperationCancelledError("INSERT", rows.length);
42031
43780
  }
42032
43781
  const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
42033
- const allRecords = rows.map((row) => {
43782
+ const allRecords = rows.map((row, rowIndex) => {
42034
43783
  const record2 = {};
42035
43784
  stmt.fields.forEach((field, i) => {
43785
+ if (sourceTable.importPresence && !sourceTable.importPresence[rowIndex]?.has(field)) return;
42036
43786
  const raw = row[columns[i]] ?? "";
42037
43787
  record2[field] = { value: convertProcessRowValue(raw, fieldTypes.get(field)) };
42038
43788
  });
42039
43789
  return record2;
42040
43790
  });
42041
- assertValidDmlRecords(allRecords, stmt.fields, fieldInfos, numberPrecision);
43791
+ allRecords.forEach((record2) => assertValidDmlRecords([record2], stmt.fields.filter((field) => field in record2), fieldInfos, numberPrecision));
42042
43792
  const createdIds = [];
42043
43793
  for (let i = 0; i < allRecords.length; i += 100) {
42044
43794
  const batch = allRecords.slice(i, i + 100);
@@ -42431,9 +44181,9 @@ function expandRowsForSubtableDml(parents, subtableCode) {
42431
44181
  for (const parent of parents) {
42432
44182
  const parentId = String(parent["$id"]?.value ?? "");
42433
44183
  const parentRevision = getRevision(parent);
42434
- const tableRows = getMutableTableRows(parent, subtableCode);
42435
- for (let i = 0; i < tableRows.length; i++) {
42436
- const row = tableRows[i];
44184
+ const tableRows2 = getMutableTableRows(parent, subtableCode);
44185
+ for (let i = 0; i < tableRows2.length; i++) {
44186
+ const row = tableRows2[i];
42437
44187
  const flat = {
42438
44188
  _pid: parentId,
42439
44189
  _rid: row.id ?? "",
@@ -42639,8 +44389,9 @@ async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache
42639
44389
  if (stmt.checkGroups?.length) return executeCheckedPlainDml(stmt, client, options, cacheContext, cteCache);
42640
44390
  const fieldInfos = await loadWritableTopLevelDmlFields(stmt.appId, stmt.fields, client, cacheContext);
42641
44391
  const numberPrecision = await loadNumberPrecisionForTargets(stmt.appId, stmt.fields, fieldInfos, client, cacheContext);
42642
- const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
42643
- const { rows, columns } = selectResult;
44392
+ const sourceTable = await dmlSourceMaterializer.materialize(stmt, client, options, cacheContext, cteCache, fieldInfos);
44393
+ const { rows, columns } = sourceTable;
44394
+ assertNoImportRowErrors(sourceTable);
42644
44395
  if (columns.length !== stmt.fields.length) {
42645
44396
  const emptySourceHint = columns.length === 0 && rows.length === 0 ? "\u3002\u7D50\u679C\u304C 0 \u884C\u306E\u305F\u3081\u5217\u3092\u7279\u5B9A\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\uFF08SELECT * \u3092\u7A7A\u30BD\u30FC\u30B9\u306B\u4F7F\u3046\u3068\u5217\u3092\u6C7A\u5B9A\u3067\u304D\u307E\u305B\u3093\u3002\u660E\u793A\u5217\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\uFF09" : "";
42646
44397
  throw new Error(
@@ -42654,18 +44405,29 @@ async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache
42654
44405
  }
42655
44406
  const toInsert = [];
42656
44407
  const toUpdate = [];
42657
- const records = rows.map((row) => {
44408
+ const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
44409
+ const records = rows.map((row, rowIndex) => {
42658
44410
  const record2 = {};
42659
44411
  stmt.fields.forEach((field, i) => {
42660
- record2[field] = { value: row[columns[i]] ?? "" };
44412
+ if (sourceTable.importPresence && !sourceTable.importPresence[rowIndex]?.has(field)) return;
44413
+ const raw = row[columns[i]] ?? "";
44414
+ record2[field] = { value: convertProcessRowValue(raw, fieldTypes.get(field)) };
42661
44415
  });
42662
44416
  return record2;
42663
44417
  });
42664
- assertValidDmlRecords(records, stmt.fields, fieldInfos, numberPrecision);
42665
- const fieldTypes = await getFieldTypeMap(stmt.appId, client, cacheContext);
44418
+ records.forEach((record2) => assertValidDmlRecords([record2], stmt.fields.filter((field) => field in record2), fieldInfos, numberPrecision));
42666
44419
  const rowKeyValues = records.map(
42667
44420
  (record2) => stmt.keyFields.map((key) => String(record2[key]?.value ?? ""))
42668
44421
  );
44422
+ if (importSourceByDmlStatement.has(stmt)) {
44423
+ const numericKey = stmt.keyFields.map((key) => fieldTypes.get(key) === "NUMBER");
44424
+ const sourceKeys = /* @__PURE__ */ new Set();
44425
+ for (const parts of rowKeyValues) {
44426
+ const normalized = upsertNormalizedKey(parts, numericKey);
44427
+ if (sourceKeys.has(normalized)) throw new Error("ERR_KEY_DUP_SOURCE: UPSERT \u30BD\u30FC\u30B9\u5185\u3067\u30AD\u30FC\u304C\u91CD\u8907\u3057\u3066\u3044\u307E\u3059");
44428
+ sourceKeys.add(normalized);
44429
+ }
44430
+ }
42669
44431
  const targetIndex = await resolveUpsertTargets(stmt.appId, stmt.keyFields, rowKeyValues, client, options, fieldTypes);
42670
44432
  records.forEach((record2, rowIdx) => {
42671
44433
  const id = lookupUpsertTarget(targetIndex, rowKeyValues[rowIdx]);
@@ -42714,10 +44476,10 @@ async function executeDescribe(stmt, client, cacheContext) {
42714
44476
  }));
42715
44477
  return { type: "SELECT", rows, columns, rowCount: rows.length };
42716
44478
  }
42717
- function parseSql(sql) {
44479
+ function parseSql(sql, enableImport = false) {
42718
44480
  try {
42719
44481
  const tokens = new Lexer(sql).tokenize();
42720
- const stmt = new Parser(tokens).parse();
44482
+ const stmt = new Parser(tokens, { import: enableImport }).parse();
42721
44483
  validateKlikeStatement(stmt);
42722
44484
  return stmt;
42723
44485
  } catch (e) {
@@ -42893,43 +44655,51 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
42893
44655
  const validate = node;
42894
44656
  fieldApps.add(validate.appId);
42895
44657
  const fields = await getFieldsCached(validate.appId, tracedClient, cacheContext);
42896
- 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));
42897
44660
  const targets = resolveExistingValidationTargets(validate, fields);
42898
44661
  const checks = collectCheckFieldRefs(validate.checkGroups ?? []);
42899
44662
  const whereFields = collectValidateWhereFields(validate.where);
42900
44663
  for (const ref of checks) {
42901
- if (ref.field !== "$id" && !infoByCode.has(ref.field)) {
44664
+ if (ref.field !== "$id" && !infoByCode.has(ref.field) && !childCodes.has(ref.field)) {
42902
44665
  throw customCheckParseError(`CHECK \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${ref.field} \u306F APP${validate.appId} \u306B\u5B58\u5728\u3057\u307E\u305B\u3093`);
42903
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
+ }
42904
44670
  }
42905
44671
  for (const field of whereFields) {
42906
- if (field !== "$id" && !infoByCode.has(field)) {
44672
+ if (field !== "$id" && !infoByCode.has(field) && !childCodes.has(field)) {
42907
44673
  throw new Error(`ArgumentError: WHERE field ${field} does not exist in APP${validate.appId}.`);
42908
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
+ }
42909
44678
  }
42910
- const types = new Map(fields.map((field) => [field.code, field.fieldType]));
44679
+ const types = new Map([...infoByCode].map(([code, field]) => [code, field.fieldType]));
42911
44680
  types.set("$id", "RECORD_NUMBER");
42912
44681
  assertCheckComparisonTypes(validate, types);
42913
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));
42914
44683
  if (capability.capability === "UNSUPPORTED") {
42915
44684
  throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
42916
44685
  }
42917
- const fieldTypes = new Map(fields.map((field) => [field.code, field.fieldType]));
42918
- 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))]] : []));
42919
44688
  const prefilter = validate.where === null ? null : capability.capability === "EXACT_PUSHDOWN" ? validate.where : extractSafePushdownLeaves(validate.where, {
42920
44689
  allowUnqualifiedFields: true,
42921
44690
  fieldTypes,
42922
44691
  fieldOptions,
42923
44692
  allowKlike: false
42924
44693
  });
42925
- const needsPrecision = targets.some((field) => field.fieldType === "NUMBER");
44694
+ const needsPrecision = targets.some((target) => target.field.fieldType === "NUMBER");
42926
44695
  if (needsPrecision) {
42927
44696
  numberPrecisionApps.add(validate.appId);
42928
44697
  await getNumberPrecisionCached(validate.appId, tracedClient, cacheContext);
42929
44698
  }
42930
44699
  validateExplainInfo.set(validate, {
42931
- targetFields: targets.map((field) => field.code),
42932
- 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])),
42933
44703
  capability,
42934
44704
  prefilter,
42935
44705
  numberPrecision: needsPrecision
@@ -42973,8 +44743,8 @@ function explainMetadataLines(analysis) {
42973
44743
  ...[...analysis.numberPrecisionApps].sort((a, b) => a - b).map((appId) => ` metadata API: number precision APP${appId}`)
42974
44744
  ];
42975
44745
  }
42976
- async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords2 = 1e4, cursorMaxActive2 = 2) {
42977
- const statements = parseSqlBatch(sql);
44746
+ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords2 = 1e4, cursorMaxActive2 = 2, enableImport = false) {
44747
+ const statements = parseSqlBatch(sql, enableImport);
42978
44748
  const analysis = analyzeBatch(statements);
42979
44749
  validateDeclaredBatchVariables(statements, injectedVariables);
42980
44750
  const variables = /* @__PURE__ */ new Map();
@@ -43133,6 +44903,79 @@ function buildExplainPlan(query, label, capabilities, orderPlans) {
43133
44903
  if (query.type === "DELETE") return buildDeletePlan(query, label);
43134
44904
  if (query.type === "REORDER") return buildReorderPlan(query, label);
43135
44905
  if (query.type === "VALIDATE") return buildValidatePlan(query, label);
44906
+ if (query.type === "IMPORT") {
44907
+ if (query.writeMode === "UPDATE_RECORD_NUMBER") {
44908
+ const csvTables = query.targets?.filter((target) => target.kind === "SUBTABLE") ?? [];
44909
+ return [
44910
+ ...label ? [label] : [],
44911
+ `IMPORT UPDATE INTO APP${query.appId}`,
44912
+ ` writeMode: UPDATE_RECORD_NUMBER`,
44913
+ ` source: CSV ${query.source.sourceName}`,
44914
+ ` keyHeader: ${query.recordNumberSourceHeader}`,
44915
+ ` mapping: BY_NAME`,
44916
+ ` parentRows: requires source load`,
44917
+ ` duplicate: preflight before lookup/write`,
44918
+ ` matched: requires lookup`,
44919
+ ` unmatched: requires lookup`,
44920
+ ` invalid: requires source load`,
44921
+ ` requiresLookup:true`,
44922
+ ` inserted: 0`,
44923
+ ` keyInPayload: false`,
44924
+ ...csvTables.length ? [
44925
+ ` replaceSubtables: ${query.replaceSubtables?.join(", ") ?? "ERROR: required"}`,
44926
+ ` subtableRowIdPolicy: PRESERVE existing; empty/unknown add without id`,
44927
+ ` rowIdOwnership: owned elsewhere invalidates the parent`,
44928
+ ` replacementDiff: existing/input/update/add/delete/rowIdNotFound requires actual-data preflight`,
44929
+ ` confirmPolicy: highest warning "\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u5168\u7F6E\u63DB\u30FBN\u884C\u524A\u9664" plus per-table detail (including delete=0)`
44930
+ ] : [],
44931
+ ` disposition: ${query.validateOnly ? "VALIDATE ONLY" : query.onErrorSkip ? `ON ERROR SKIP INTO ${query.errorTable}` : "fail-fast"}`,
44932
+ ` gate: enabled for this parse`,
44933
+ ` writesKintone: ${query.validateOnly ? "false" : "true"}`
44934
+ ];
44935
+ }
44936
+ const mode = query.keyFields ? "UPSERT" : "INSERT";
44937
+ const hasSubtables = query.targets?.some((target) => target.kind === "SUBTABLE") === true;
44938
+ return [
44939
+ ...label ? [label] : [],
44940
+ `IMPORT ${mode} INTO APP${query.appId}`,
44941
+ ` source: ${query.source.kind} ${query.source.sourceName}`,
44942
+ ` sourceFormat: ${query.source.kind}`,
44943
+ ` encoding: ${query.source.kind === "JSON" ? "UTF8 only" : query.source.encoding ?? "UTF8 (or loader metadata)"}`,
44944
+ ` mapping: ${query.source.kind === "JSON" ? "BY NAME (INTO order)" : query.source.projection ? "SELECT expressions" : query.source.mappingMode}`,
44945
+ ...query.source.kind === "JSON" ? [
44946
+ ` duplicateKeyPolicy: reject`,
44947
+ ` numberLexemePolicy: preserve; JSON number accepts safe integer only`,
44948
+ ` precisionTargetsRequireString: true`,
44949
+ ` unknownKeyPolicy: reject`,
44950
+ ` presenceAware: true`,
44951
+ ...hasSubtables ? [
44952
+ ` subtableRowIdPolicy: reject _rid/id; DROP IDs and renumber every input row`,
44953
+ ` subtableUpdatePolicy: present table replaces all rows; missing table is preserved; [] deletes all rows`,
44954
+ ` confirmPolicy: parent/table existing/input/add/delete detail required; delete is highest warning`
44955
+ ] : []
44956
+ ] : [
44957
+ ` header: ${query.source.hasHeader ? "HEADER" : "NO HEADER"}`,
44958
+ ...query.source.mappingMode === "BY_NAME" ? [
44959
+ ` writtenColumns: ${query.fields.join(", ")}`,
44960
+ ` knownExportColumns: audit and ignore with reason/non-empty count`,
44961
+ ` unknownColumnPolicy: ${query.source.ignoreUnknownColumns ? "ignore with audit/non-empty count" : "ERR_IMPORT_UNKNOWN_COLUMN"}`,
44962
+ ` multipleValueDelimiter: LF (CRLF or LF)`,
44963
+ ` sourceValueMode: string-preserving`,
44964
+ ` roundTripNumericGuarantee: exact CSV lexeme passes strict decimal validation`,
44965
+ ` FILE: audit-ignore unless named in INTO (analyze error)`
44966
+ ] : []
44967
+ ],
44968
+ ` sourceLimit: 10485760 bytes / ${query.fields.length} target columns`,
44969
+ ` key: ${query.keyFields?.join(", ") ?? "none"}`,
44970
+ ` checks: ${query.checkGroups?.length ?? 0}`,
44971
+ ` disposition: ${query.validateOnly ? "VALIDATE ONLY" : query.onErrorSkip ? `ON ERROR SKIP INTO ${query.errorTable}` : "fail-fast"}`,
44972
+ ` gate: enabled for this parse`,
44973
+ ` preflight: ${query.validateOnly && hasSubtables ? "requires actual source load at execution; this EXPLAIN is static" : "requires load"}`,
44974
+ ...hasSubtables ? [query.source.kind === "JSON" ? ` Phase5C: JSON mutation requires detail-capable confirmation surface` : ` Phase5D: CSV mutation requires detail-capable confirmation surface`] : [],
44975
+ ` writesKintone: ${query.validateOnly ? "false" : "true"}`,
44976
+ ` duplicateKey: preflight before lookup/write (requires load)`
44977
+ ];
44978
+ }
43136
44979
  return buildSelectPlan(query, label, capabilities, orderPlans);
43137
44980
  }
43138
44981
  function buildValidatePlan(stmt, label) {
@@ -43151,6 +44994,10 @@ function buildValidatePlan(stmt, label) {
43151
44994
  lines.push(` kintone query: ${info.prefilter === null ? "(\u5168\u4EF6\u53D6\u5F97)" : whereToKintone(info.prefilter)}`);
43152
44995
  lines.push(` audit fields: ${info.targetFields.length === 0 ? "(\u306A\u3057)" : info.targetFields.join(", ")}`);
43153
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)");
43154
45001
  lines.push(` number precision: ${info.numberPrecision ? "required" : "not required"}`);
43155
45002
  lines.push(" local checks: original WHERE re-evaluation + built-in constraints + CHECK groups");
43156
45003
  lines.push(" records/mutation API during EXPLAIN: none; violation count unavailable");
@@ -43586,15 +45433,15 @@ var OperationCancelledError = class extends Error {
43586
45433
  };
43587
45434
 
43588
45435
  // src/core/sql.ts
43589
- function parseSqlStatement(sql) {
45436
+ function parseSqlStatement(sql, capabilities = {}) {
43590
45437
  const tokens = new Lexer(sql).tokenize();
43591
- const stmt = new Parser(tokens).parse();
45438
+ const stmt = new Parser(tokens, capabilities).parse();
43592
45439
  validateKlikeStatement(stmt);
43593
45440
  return stmt;
43594
45441
  }
43595
- function parseSqlStatements(sql) {
45442
+ function parseSqlStatements(sql, capabilities = {}) {
43596
45443
  const tokens = new Lexer(sql).tokenize();
43597
- const statements = new Parser(tokens).parseStatements();
45444
+ const statements = new Parser(tokens, capabilities).parseStatements();
43598
45445
  statements.forEach(validateKlikeStatement);
43599
45446
  return statements;
43600
45447
  }
@@ -43658,7 +45505,8 @@ function buildBatchEnvelope(batch, options = {}) {
43658
45505
  columns: s.result.columns,
43659
45506
  rows: s.result.rows,
43660
45507
  rowCount: s.result.rowCount,
43661
- warnings: s.result.warnings ?? []
45508
+ warnings: s.result.warnings ?? [],
45509
+ ...s.result.validateStats ? { validateStats: s.result.validateStats } : {}
43662
45510
  });
43663
45511
  } else if (s.result?.type === "VALIDATION") {
43664
45512
  totalRows += s.result.errorCount;
@@ -43677,7 +45525,8 @@ function buildBatchEnvelope(batch, options = {}) {
43677
45525
  validRows: s.result.validRows,
43678
45526
  invalidRows: s.result.invalidRows,
43679
45527
  errorCount: s.result.errorCount,
43680
- ...s.result.errTable ? { errTable: s.result.errTable } : {}
45528
+ ...s.result.errTable ? { errTable: s.result.errTable } : {},
45529
+ ...s.result.importDetail ? { importDetail: s.result.importDetail } : {}
43681
45530
  });
43682
45531
  } else if (s.status === "success" && s.result && s.result.type !== "SELECT" && s.result.type !== "ASSERT") {
43683
45532
  Object.assign(entry, toMutationSummary(s.result));
@@ -43753,6 +45602,17 @@ function restoreSqlContextError(err, sourceSql, context) {
43753
45602
  return err;
43754
45603
  }
43755
45604
 
45605
+ // src/import/importGateError.ts
45606
+ var IMPORT_CAPABILITY_GATE_MARKER = "capability is disabled";
45607
+ function errorMessage(error51) {
45608
+ if (error51 instanceof Error) return error51.message;
45609
+ if (typeof error51 === "string") return error51;
45610
+ return null;
45611
+ }
45612
+ function isImportCapabilityGateError(error51) {
45613
+ return errorMessage(error51)?.includes(IMPORT_CAPABILITY_GATE_MARKER) === true;
45614
+ }
45615
+
43756
45616
  // src/node/config.ts
43757
45617
  var import_fs = require("fs");
43758
45618
  var LOGICAL_APP_NAME_RE = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
@@ -44057,7 +45917,7 @@ function clampInt(v, min, max) {
44057
45917
  function flattenFormFieldProperties(properties) {
44058
45918
  return flattenFields(properties, collectLookupCopyFields(properties));
44059
45919
  }
44060
- function flattenFields(properties, lookupCopyFields, inSubtable = false) {
45920
+ function flattenFields(properties, lookupCopyFields, inSubtable = false, subtableCode) {
44061
45921
  const out = [];
44062
45922
  for (const field of Object.values(properties)) {
44063
45923
  const optionOrder = toOptionOrderMap(field.options);
@@ -44075,11 +45935,12 @@ function flattenFields(properties, lookupCopyFields, inSubtable = false) {
44075
45935
  maxLength: normalizeConstraintValue(field.maxLength),
44076
45936
  defaultValue: field.defaultValue,
44077
45937
  inSubtable,
45938
+ ...subtableCode ? { subtableCode } : {},
44078
45939
  writable: !lookupCopyFields.has(field.code) && !NON_WRITABLE_FIELD_TYPES2.has(field.type)
44079
45940
  };
44080
45941
  info.semantics = resolveFieldSemantics(info);
44081
45942
  out.push(info);
44082
- if (field.fields) out.push(...flattenFields(field.fields, lookupCopyFields, true));
45943
+ if (field.fields) out.push(...flattenFields(field.fields, lookupCopyFields, true, field.type === "SUBTABLE" ? field.code : subtableCode));
44083
45944
  }
44084
45945
  return out;
44085
45946
  }
@@ -45359,18 +47220,55 @@ function requireSingleStatement(validation, toolName) {
45359
47220
  }
45360
47221
  var DEFAULT_MAX_RECORDS = 500;
45361
47222
  var DEFAULT_ON_LIMIT = "error";
47223
+ var MCP_IMPORT_SOURCE_REQUIRED_MESSAGE = "IMPORT \u306B\u306F importSources\uFF08inline CSV/JSON\uFF09\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002";
47224
+ function importCapability(input) {
47225
+ const sources = input.importSources;
47226
+ if (!sources || sources.length === 0) return {};
47227
+ const byName = /* @__PURE__ */ new Map();
47228
+ for (const source of sources) {
47229
+ if (byName.has(source.name)) throw new Error(`ArgumentError: duplicate import source name: ${source.name}`);
47230
+ let bytes;
47231
+ if (source.text !== void 0 && source.base64 === void 0) {
47232
+ bytes = new TextEncoder().encode(source.text);
47233
+ } else if (source.base64 !== void 0 && source.text === void 0) {
47234
+ const normalized = source.base64.replace(/\s/g, "");
47235
+ if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(normalized)) {
47236
+ throw new Error(`ArgumentError: invalid base64 for import source: ${source.name}`);
47237
+ }
47238
+ bytes = new Uint8Array(Buffer.from(normalized, "base64"));
47239
+ } else {
47240
+ throw new Error(`ArgumentError: import source ${source.name} requires exactly one of text or base64.`);
47241
+ }
47242
+ byName.set(source.name, { bytes, encoding: source.encoding });
47243
+ }
47244
+ return {
47245
+ enableImport: true,
47246
+ importSource: (name) => {
47247
+ const source = byName.get(name);
47248
+ return source ? { load: async () => source } : void 0;
47249
+ }
47250
+ };
47251
+ }
47252
+ function toMcpImportError(error51, importEnabled) {
47253
+ if (importEnabled || !isImportCapabilityGateError(error51)) return error51;
47254
+ if (error51 instanceof Error) {
47255
+ error51.message = MCP_IMPORT_SOURCE_REQUIRED_MESSAGE;
47256
+ return error51;
47257
+ }
47258
+ return MCP_IMPORT_SOURCE_REQUIRED_MESSAGE;
47259
+ }
45362
47260
  function noOpClient() {
45363
- const fail = async () => {
47261
+ const fail3 = async () => {
45364
47262
  throw new Error("No-op client should not be called.");
45365
47263
  };
45366
47264
  return {
45367
- getRecords: fail,
45368
- openCursor: fail,
45369
- postRecords: fail,
45370
- putRecords: fail,
45371
- deleteRecords: fail,
45372
- getApps: fail,
45373
- getFields: fail,
47265
+ getRecords: fail3,
47266
+ openCursor: fail3,
47267
+ postRecords: fail3,
47268
+ putRecords: fail3,
47269
+ deleteRecords: fail3,
47270
+ getApps: fail3,
47271
+ getFields: fail3,
45374
47272
  async getProcessStatuses() {
45375
47273
  return { enable: false, states: [] };
45376
47274
  },
@@ -45429,7 +47327,8 @@ function toSelectPayload(result) {
45429
47327
  columns: result.columns,
45430
47328
  rows: result.rows,
45431
47329
  rowCount: result.rowCount,
45432
- warnings: result.warnings ?? []
47330
+ warnings: result.warnings ?? [],
47331
+ ...result.validateStats ? { validateStats: result.validateStats } : {}
45433
47332
  };
45434
47333
  }
45435
47334
  function toAssertPayload(result) {
@@ -45450,7 +47349,8 @@ function toDmlValidationPayload(result) {
45450
47349
  errorCount: result.errorCount,
45451
47350
  columns: result.columns,
45452
47351
  errors: result.errors,
45453
- ...result.errTable ? { errTable: result.errTable } : {}
47352
+ ...result.errTable ? { errTable: result.errTable } : {},
47353
+ ...result.importDetail ? { importDetail: result.importDetail } : {}
45454
47354
  };
45455
47355
  }
45456
47356
  function toMutationPayload(result) {
@@ -45571,12 +47471,14 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
45571
47471
  const validationContexts = /* @__PURE__ */ new WeakMap();
45572
47472
  async function validate(input) {
45573
47473
  const normalized = normalizeSqlForTool(serverOptions, input.sql, input.profile);
47474
+ const importOptions = importCapability(input);
45574
47475
  let analysis;
45575
47476
  try {
45576
- const statements = parseSqlStatements(normalized.normalizedSql);
47477
+ const statements = parseSqlStatements(normalized.normalizedSql, { import: importOptions.enableImport });
45577
47478
  analysis = analyzeBatch(statements);
45578
47479
  } catch (err) {
45579
- throw restoreSqlContextError(err, normalized.sourceSql, normalized.sqlContext);
47480
+ const restored = restoreSqlContextError(err, normalized.sourceSql, normalized.sqlContext);
47481
+ throw toMcpImportError(restored, importOptions.enableImport === true);
45580
47482
  }
45581
47483
  const appBindings = [...normalized.appBindingByMappedApp.entries()].map(([mappedAppId, binding]) => toValidationBinding(mappedAppId, binding));
45582
47484
  const statementValidations = analysis.statements.map((s2) => ({
@@ -45634,12 +47536,14 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
45634
47536
  }
45635
47537
  async function explain(input) {
45636
47538
  const normalized = normalizeSqlForTool(serverOptions, input.sql, input.profile);
47539
+ const importOptions = importCapability(input);
45637
47540
  const appBindings = toExplainBindings(normalized.appBindingByMappedApp);
45638
47541
  let statements;
45639
47542
  try {
45640
- statements = parseSqlStatements(normalized.normalizedSql);
47543
+ statements = parseSqlStatements(normalized.normalizedSql, { import: importOptions.enableImport });
45641
47544
  } catch (err) {
45642
- throw restoreSqlContextError(err, normalized.sourceSql, normalized.sqlContext);
47545
+ const restored = restoreSqlContextError(err, normalized.sourceSql, normalized.sqlContext);
47546
+ throw toMcpImportError(restored, importOptions.enableImport === true);
45643
47547
  }
45644
47548
  const needsAppMetadata = normalized.appBindingByMappedApp.size > 0 && statements.some(explainNeedsAppMetadata);
45645
47549
  const runtime = needsAppMetadata ? await createRuntime(serverOptions, {
@@ -45659,7 +47563,8 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
45659
47563
  void 0,
45660
47564
  explainCacheContext,
45661
47565
  runtime?.maxRecords ?? input.maxRecords,
45662
- runtime?.cursorMaxActive ?? input.cursorMaxActive ?? 2
47566
+ runtime?.cursorMaxActive ?? input.cursorMaxActive ?? 2,
47567
+ importOptions.enableImport
45663
47568
  );
45664
47569
  return {
45665
47570
  ok: true,
@@ -45672,7 +47577,8 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
45672
47577
  const result = await executeSql(explainSql(explainSourceSql), explainClient, {
45673
47578
  cacheContext: explainCacheContext,
45674
47579
  maxRecords: runtime?.maxRecords ?? input.maxRecords,
45675
- cursorMaxActive: runtime?.cursorMaxActive ?? input.cursorMaxActive ?? 2
47580
+ cursorMaxActive: runtime?.cursorMaxActive ?? input.cursorMaxActive ?? 2,
47581
+ ...importOptions
45676
47582
  });
45677
47583
  if (result.type !== "SELECT") {
45678
47584
  throw new Error(`ArgumentError: EXPLAIN returned unexpected result type ${result.type}.`);
@@ -45684,6 +47590,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
45684
47590
  }
45685
47591
  async function query(input, validated) {
45686
47592
  const validation = validated ?? await validate(input);
47593
+ const importOptions = importCapability(input);
45687
47594
  if (!validation.batch && input.variables && Object.keys(input.variables).length > 0) {
45688
47595
  throw new Error("ArgumentError: variables require a batch containing DECLARE.");
45689
47596
  }
@@ -45716,20 +47623,22 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
45716
47623
  // HTTP クライアント側の per-request タイムアウトと同値になる
45717
47624
  timeoutMs: runtime2.timeout,
45718
47625
  cursorMaxActive: runtime2.cursorMaxActive ?? input.cursorMaxActive ?? 2,
45719
- variables: input.variables
47626
+ variables: input.variables,
47627
+ ...importOptions
45720
47628
  });
45721
47629
  return { ...buildBatchEnvelope(batchResult, { maxTotalRecords: input.maxTotalRecords }) };
45722
47630
  }
45723
47631
  if (!validation.isReadOnly) {
45724
47632
  throw new Error(`ArgumentError: ${validation.statementType} is not allowed by ksql_query. Use ksql_mutate.`);
45725
47633
  }
45726
- const stmt = parseSqlStatement(validation.normalizedSql);
47634
+ const stmt = parseSqlStatement(validation.normalizedSql, { import: importOptions.enableImport });
45727
47635
  const noAppApiNeeded = isNoFromSelectStatement(stmt);
45728
47636
  if (noAppApiNeeded) {
45729
47637
  const result2 = await executeSql(validation.normalizedSql, noOpClient(), {
45730
47638
  maxRecords: input.maxRecords ?? DEFAULT_MAX_RECORDS,
45731
47639
  onLimitReached: input.onLimit ?? DEFAULT_ON_LIMIT,
45732
- cacheContext: validation.cacheContext
47640
+ cacheContext: validation.cacheContext,
47641
+ ...importOptions
45733
47642
  });
45734
47643
  if (result2.type === "ASSERT") return toAssertPayload(result2);
45735
47644
  if (result2.type === "VALIDATION") return toDmlValidationPayload(result2);
@@ -45753,7 +47662,8 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
45753
47662
  fetchParallel: runtime.fetchParallel,
45754
47663
  onLimitReached: runtime.onLimit,
45755
47664
  cacheContext: runtime.cacheContext,
45756
- cursorMaxActive: runtime.cursorMaxActive ?? input.cursorMaxActive ?? 2
47665
+ cursorMaxActive: runtime.cursorMaxActive ?? input.cursorMaxActive ?? 2,
47666
+ ...importOptions
45757
47667
  });
45758
47668
  if (result.type === "ASSERT") return toAssertPayload(result);
45759
47669
  if (result.type === "VALIDATION") return toDmlValidationPayload(result);
@@ -45763,6 +47673,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
45763
47673
  return toSelectPayload(result);
45764
47674
  }
45765
47675
  async function mutateBatch(input, validation, dmlMaxRows) {
47676
+ const importOptions = importCapability(input);
45766
47677
  if (!validation.containsDml) {
45767
47678
  throw new Error("ArgumentError: batch contains no DML statements. Use ksql_query.");
45768
47679
  }
@@ -45812,6 +47723,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
45812
47723
  timeoutMs: runtime.timeout,
45813
47724
  cursorMaxActive: runtime.cursorMaxActive ?? input.cursorMaxActive ?? 2,
45814
47725
  variables: input.variables,
47726
+ ...importOptions,
45815
47727
  confirm: async (count, operation) => {
45816
47728
  if (count > dmlMaxRows) {
45817
47729
  throw new Error(`ArgumentError: ${operation} affected rows (${count}) exceed dmlMaxRows (${dmlMaxRows}).`);
@@ -45840,6 +47752,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
45840
47752
  }
45841
47753
  async function mutate(input, validated) {
45842
47754
  const dmlMaxRows = requireDmlApproval(input, "ksql_mutate");
47755
+ const importOptions = importCapability(input);
45843
47756
  const validation = validated ?? await validate(input);
45844
47757
  if (!validation.batch && input.variables && Object.keys(input.variables).length > 0) {
45845
47758
  throw new Error("ArgumentError: variables require a batch containing DECLARE.");
@@ -45882,7 +47795,8 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
45882
47795
  throw new Error(`ArgumentError: ${operation} affected rows (${count}) exceed dmlMaxRows (${dmlMaxRows}).`);
45883
47796
  }
45884
47797
  return true;
45885
- }
47798
+ },
47799
+ ...importOptions
45886
47800
  });
45887
47801
  } catch (err) {
45888
47802
  throw selectBasedDml ? appendSelectBasedDmlReadLimitHint(err) : err;
@@ -46052,15 +47966,27 @@ var timeout = external_exports.number().int().positive().describe("Request timeo
46052
47966
  var cursorMaxActive = external_exports.number().int().min(1).max(5).describe("Maximum active Cursor API handles per kintone host in this process (1-5, default 2). Later calls update the host limit; lowering it keeps existing cursors and delays new ones until active usage falls below the new limit. Create/Get are never automatically retried; capacity waits up to 30 seconds.").optional();
46053
47967
  var savedQueryName = external_exports.string().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/).describe("Saved query name (alphanumeric, '_' and '-', up to 64 chars).");
46054
47968
  var savedQueryTags = external_exports.array(external_exports.string().min(1)).describe("Tags for organizing saved queries.").optional();
47969
+ var importSources = external_exports.array(external_exports.object({
47970
+ name: external_exports.string().min(1).describe("Source name referenced after FROM CSV."),
47971
+ text: external_exports.string().describe("Inline CSV text. Mutually exclusive with base64.").optional(),
47972
+ base64: external_exports.string().describe("Inline CSV bytes encoded as base64. Mutually exclusive with text.").optional(),
47973
+ encoding: external_exports.enum(["utf8", "sjis"]).describe("Optional source encoding metadata; SQL ENCODING takes precedence.").optional()
47974
+ }).superRefine((source, ctx) => {
47975
+ if (source.text === void 0 === (source.base64 === void 0)) {
47976
+ ctx.addIssue({ code: "custom", message: "Exactly one of text or base64 is required." });
47977
+ }
47978
+ })).max(16).describe("IMPORT CSV/JSON named inline sources (maximum 16). Nested subtable VALIDATE ONLY/EXPLAIN is supported, but mutation is fail-closed because MCP cannot interactively display and approve parent/table delete detail. JSON drops child IDs and renumbers; cli-kintone CSV preserves matching IDs and requires REPLACE SUBTABLES. Paths are not accepted; each source is limited to 10 MiB.").optional();
46055
47979
  var validateInputSchema = external_exports.object({
46056
47980
  sql: external_exports.string().min(1).describe("kSQL text to validate. May contain multiple ;-separated statements (batch) and temp tables (#name)."),
46057
- profile
47981
+ profile,
47982
+ importSources
46058
47983
  });
46059
47984
  var explainInputSchema = external_exports.object({
46060
47985
  sql: external_exports.string().min(1).describe("kSQL text to explain. May contain multiple ;-separated statements (batch) and temp tables (#name)."),
46061
47986
  profile,
46062
47987
  maxRecords,
46063
- cursorMaxActive
47988
+ cursorMaxActive,
47989
+ importSources
46064
47990
  });
46065
47991
  var queryInputSchema = external_exports.object({
46066
47992
  sql: external_exports.string().min(1).describe("Read-only kSQL text. May contain multiple ;-separated statements (batch) with temp tables, e.g. CREATE TEMP TABLE #t AS SELECT ...; SELECT ... FROM #t;"),
@@ -46071,6 +47997,7 @@ var queryInputSchema = external_exports.object({
46071
47997
  tempTableMaxRows,
46072
47998
  timeout,
46073
47999
  cursorMaxActive,
48000
+ importSources,
46074
48001
  continueOnError: external_exports.boolean().describe("Batch (multi-statement) only: keep executing subsequent statements after a runtime error (default false = fail-fast).").optional(),
46075
48002
  maxTotalRecords: external_exports.number().int().positive().describe("Batch (multi-statement) only: cap on total rows returned across all result sets (default: unlimited).").optional(),
46076
48003
  variables: external_exports.record(external_exports.string(), external_exports.string()).describe("Batch only: string values for variables declared with DECLARE. Keys omit @ and are case-insensitive.").optional()
@@ -46085,6 +48012,7 @@ var mutateInputSchema = external_exports.object({
46085
48012
  tempTableMaxRows,
46086
48013
  timeout,
46087
48014
  cursorMaxActive,
48015
+ importSources,
46088
48016
  dmlTotalMaxRows: external_exports.number().int().positive().describe("Batch (multi-statement) only: cap on total affected rows across the whole batch (default: per-statement dmlMaxRows only). DML batches always run fail-fast.").optional(),
46089
48017
  variables: external_exports.record(external_exports.string(), external_exports.string()).describe("Batch only: string values for variables declared with DECLARE. Keys omit @ and are case-insensitive.").optional()
46090
48018
  });
@@ -46173,9 +48101,14 @@ Options:
46173
48101
  --config <path> Config file path (default: ./ksql.config.json or KSQL_CONFIG)
46174
48102
  --profile <name> Default profile name
46175
48103
  -h, --help Show help
48104
+
48105
+ IMPORT CSV/JSON is call-scoped and disabled by default. Supply named
48106
+ inline importSources (text or base64 bytes) to enable it; filesystem paths are not accepted.
48107
+ Nested JSON/CSV subtable mutation is fail-closed on MCP: use VALIDATE ONLY/EXPLAIN.
48108
+ JSON child IDs are rejected and replacement renumbers all rows.
46176
48109
  `);
46177
48110
  }
46178
- var SERVER_VERSION = true ? "3.5.0" : "0.0.0-dev";
48111
+ var SERVER_VERSION = true ? "3.7.0" : "0.0.0-dev";
46179
48112
  function createServer(args) {
46180
48113
  const server = new McpServer({
46181
48114
  name: "ksql-mcp",
@@ -46187,12 +48120,12 @@ function createServer(args) {
46187
48120
  });
46188
48121
  server.registerTool("ksql_validate", {
46189
48122
  title: "Validate kSQL",
46190
- description: "Parse and validate kSQL without calling kintone APIs. Use this before executing generated SQL.",
48123
+ description: "Parse and validate kSQL without calling kintone APIs. Use this before executing generated SQL. IMPORT CSV/JSON is enabled only when named inline importSources are supplied.",
46191
48124
  inputSchema: validateInputShape
46192
48125
  }, tools.validateTool);
46193
48126
  server.registerTool("ksql_explain", {
46194
48127
  title: "Explain kSQL",
46195
- description: "Return the schema-aware kSQL execution plan. Reads form metadata and, when needed, process status metadata; never reads or writes records.",
48128
+ description: "Return the schema-aware kSQL execution plan. Reads form metadata and, when needed, process status metadata; never reads or writes records. IMPORT CSV/JSON is enabled only when named inline importSources are supplied.",
46196
48129
  inputSchema: explainInputShape
46197
48130
  }, tools.explainTool);
46198
48131
  server.registerTool("ksql_query", {
@@ -46201,7 +48134,7 @@ function createServer(args) {
46201
48134
  inputSchema: queryInputShape
46202
48135
  }, tools.queryTool);
46203
48136
  server.registerTool("ksql_mutate", {
46204
- title: "Run mutating kSQL",
48137
+ title: "Run mutating kSQL (IMPORT CSV/JSON via importSources)",
46205
48138
  description: "Execute DML kSQL with explicit allowDml, confirmText, and dmlMaxRows safety controls. Supports multi-statement DML batches with temp tables. ON ERROR SKIP INTO #err optionally isolates local Tier-0 validation failures and writes only valid rows; REJECT LIMIT stops with zero writes while returning diagnostics. NUMBER targets use the destination app numberPrecision settings for integer-digit validation in normal, validation-only, and skip paths; settings failures are fail-closed. Excess fractional digits pass through for kintone to round automatically. INSERT/UPSERT INTO app ... SELECT supports app sources, temp tables, or joins of both. UPDATE ... FROM supports copying scalar fields from an app or temp table by matching target $id or a single-line-text/number business key to one source key. For UPSERT, dmlMaxRows counts inserts + updates. dmlMaxRows caps affected rows only, not source reads: source SELECT, ON ERROR SKIP candidates, and UPDATE ... FROM app reads use the runtime maxRecords (KSQL_MAX_RECORDS / profile query.maxRecords, default 500); temp tables hold at most 10000 rows by default (adjustable via tempTableMaxRows).",
46206
48139
  inputSchema: mutateInputShape
46207
48140
  }, tools.mutateTool);