@rex0220/kintone-sql-tools 1.10.0 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -125,6 +125,7 @@ Options:
125
125
  --max-records <n> Max records to fetch (default: 500)
126
126
  --fetch-parallel <n> Parallel page fetches per query: 1-10 (default: 3)
127
127
  --on-limit <mode> On record limit: error | truncate
128
+ --temp-table-max-rows <n> Max rows per temp table (default: 10000, always errors on overflow)
128
129
  --timeout <ms> Request timeout in milliseconds (default: 30000)
129
130
  --max-concurrent <n> Max concurrent kintone requests: 1-50 (default: 10)
130
131
  (process-wide; fixed at first resolution; KSQL_MAX_CONCURRENT wins)
package/dist-cli/ksql.js CHANGED
@@ -25,6 +25,7 @@ __export(index_exports, {
25
25
  buildBatchDmlConfirmMessage: () => buildBatchDmlConfirmMessage,
26
26
  buildBatchStatementSummary: () => buildBatchStatementSummary,
27
27
  buildOutput: () => buildOutput,
28
+ buildReplExecArgv: () => buildReplExecArgv,
28
29
  extractAppIds: () => extractAppIds,
29
30
  normalizeAppKey: () => normalizeAppKey,
30
31
  normalizeSqlAppProfiles: () => normalizeSqlAppProfiles,
@@ -4006,6 +4007,11 @@ function applyFilter(rows, where) {
4006
4007
  if (where === null) return rows;
4007
4008
  return rows.filter((row) => evalWhere(where, row));
4008
4009
  }
4010
+ function hasAggregateColumns(columns) {
4011
+ return columns.some(
4012
+ (c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr)
4013
+ );
4014
+ }
4009
4015
  function applyGroupBy(rows, groupByKeys, columns) {
4010
4016
  const groups = /* @__PURE__ */ new Map();
4011
4017
  for (const row of rows) {
@@ -4014,6 +4020,9 @@ function applyGroupBy(rows, groupByKeys, columns) {
4014
4020
  if (bucket) bucket.push(row);
4015
4021
  else groups.set(key, [row]);
4016
4022
  }
4023
+ if (groups.size === 0 && groupByKeys.length === 0 && hasAggregateColumns(columns)) {
4024
+ groups.set("", []);
4025
+ }
4017
4026
  const result = [];
4018
4027
  for (const groupRows of groups.values()) {
4019
4028
  const outRow = { ...groupRows[0] };
@@ -4429,10 +4438,7 @@ function runFullScan(input) {
4429
4438
  rows = applyJoin(rows, rightRows, join2);
4430
4439
  }
4431
4440
  rows = applyFilter(rows, stmt.where);
4432
- const hasAggregate = stmt.columns.some(
4433
- (c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr)
4434
- );
4435
- if (stmt.groupBy.length > 0 || hasAggregate) {
4441
+ if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
4436
4442
  rows = applyGroupBy(rows, stmt.groupBy, stmt.columns);
4437
4443
  }
4438
4444
  rows = applyHaving(rows, stmt.having);
@@ -6310,7 +6316,7 @@ function buildBatchStatementPlan(stmt, info) {
6310
6316
  return [
6311
6317
  `CREATE TEMP TABLE ${stmt.name}`,
6312
6318
  ` scope: batch\uFF08\u30D0\u30C3\u30C1\u7D42\u4E86\u6642\u306B\u81EA\u52D5\u7834\u68C4\uFF09`,
6313
- ` rows: \u5B9F\u4F53\u5316\u524D\u306E\u305F\u3081\u4E0D\u660E\uFF08\u4E0A\u9650 ${TEMP_TABLE_MAX_ROWS} \u884C\u3001\u8D85\u904E\u306F\u30A8\u30E9\u30FC\uFF09`,
6319
+ ` rows: \u5B9F\u4F53\u5316\u524D\u306E\u305F\u3081\u4E0D\u660E\uFF08\u65E2\u5B9A\u4E0A\u9650 ${TEMP_TABLE_MAX_ROWS} \u884C\u3001tempTableMaxRows \u3067\u5909\u66F4\u53EF\u3001\u8D85\u904E\u306F\u30A8\u30E9\u30FC\uFF09`,
6314
6320
  ...buildPlanForBatchQuery(stmt.query, info).map((l) => ` ${l}`)
6315
6321
  ];
6316
6322
  }
@@ -7469,6 +7475,7 @@ Options:
7469
7475
  --max-records <n> Max records to fetch (default: 500)
7470
7476
  --fetch-parallel <n> Parallel page fetches per query: 1-10 (default: 3)
7471
7477
  --on-limit <mode> On record limit: error | truncate
7478
+ --temp-table-max-rows <n> Max rows per temp table (default: 10000, always errors on overflow)
7472
7479
  --timeout <ms> Request timeout in milliseconds (default: 30000)
7473
7480
  --max-concurrent <n> Max concurrent kintone requests: 1-50 (default: 10)
7474
7481
  (process-wide; fixed at first resolution; KSQL_MAX_CONCURRENT wins)
@@ -7521,6 +7528,7 @@ function parseArgs(argv) {
7521
7528
  maxRecords: null,
7522
7529
  fetchParallel: null,
7523
7530
  onLimit: null,
7531
+ tempTableMaxRows: null,
7524
7532
  timeout: null,
7525
7533
  configPath: null,
7526
7534
  profile: null,
@@ -7743,6 +7751,13 @@ function parseArgs(argv) {
7743
7751
  i++;
7744
7752
  continue;
7745
7753
  }
7754
+ if (a === "--temp-table-max-rows") {
7755
+ const n = Number(v);
7756
+ if (!Number.isInteger(n) || n <= 0) throw new Error("ArgumentError: --temp-table-max-rows must be a positive integer.");
7757
+ out.tempTableMaxRows = n;
7758
+ i++;
7759
+ continue;
7760
+ }
7746
7761
  if (a === "--fetch-parallel") {
7747
7762
  const n = Number(v);
7748
7763
  if (!Number.isInteger(n) || n < 1 || n > 10) throw new Error("ArgumentError: --fetch-parallel must be an integer between 1 and 10.");
@@ -8187,6 +8202,7 @@ function buildReplExecArgv(base, sql, dryRun, format) {
8187
8202
  pushOpt(argv, "--max-records", base.maxRecords);
8188
8203
  pushOpt(argv, "--fetch-parallel", base.fetchParallel);
8189
8204
  pushOpt(argv, "--on-limit", base.onLimit);
8205
+ pushOpt(argv, "--temp-table-max-rows", base.tempTableMaxRows);
8190
8206
  pushOpt(argv, "--timeout", base.timeout);
8191
8207
  pushOpt(argv, "--output", base.outputPath);
8192
8208
  pushOpt(argv, "--user-format", base.userFormat);
@@ -8739,6 +8755,7 @@ async function run() {
8739
8755
  const fetchParallel = args.fetchParallel ?? envInt2("KSQL_FETCH_PARALLEL") ?? profile.query?.fetchParallel ?? 3;
8740
8756
  const onLimit = args.onLimit ?? envOnLimit("KSQL_ON_LIMIT") ?? profile.query?.onLimit ?? "error";
8741
8757
  const timeout = args.timeout ?? envInt2("KSQL_TIMEOUT") ?? profile.query?.timeout ?? 3e4;
8758
+ const tempTableMaxRows = args.tempTableMaxRows ?? envInt2("KSQL_TEMP_TABLE_MAX_ROWS") ?? profile.query?.tempTableMaxRows ?? void 0;
8742
8759
  if (!Number.isInteger(fetchParallel) || fetchParallel < 1 || fetchParallel > 10) {
8743
8760
  process.stderr.write("ArgumentError: fetch-parallel must be an integer between 1 and 10.\n");
8744
8761
  return 2;
@@ -9095,6 +9112,7 @@ query=${label}`);
9095
9112
  onLimitReached: onLimit,
9096
9113
  cacheContext,
9097
9114
  continueOnError: args.continueOnError,
9115
+ tempTableMaxRows,
9098
9116
  timeoutMs: timeout,
9099
9117
  confirm: batchContainsDml ? async (count, operation) => {
9100
9118
  if (count > dmlMaxRows) {
@@ -9165,6 +9183,7 @@ if (isDirectCliRun()) {
9165
9183
  buildBatchDmlConfirmMessage,
9166
9184
  buildBatchStatementSummary,
9167
9185
  buildOutput,
9186
+ buildReplExecArgv,
9168
9187
  extractAppIds,
9169
9188
  normalizeAppKey,
9170
9189
  normalizeSqlAppProfiles,
@@ -34909,6 +34909,11 @@ function applyFilter(rows, where) {
34909
34909
  if (where === null) return rows;
34910
34910
  return rows.filter((row) => evalWhere(where, row));
34911
34911
  }
34912
+ function hasAggregateColumns(columns) {
34913
+ return columns.some(
34914
+ (c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr)
34915
+ );
34916
+ }
34912
34917
  function applyGroupBy(rows, groupByKeys, columns) {
34913
34918
  const groups = /* @__PURE__ */ new Map();
34914
34919
  for (const row of rows) {
@@ -34917,6 +34922,9 @@ function applyGroupBy(rows, groupByKeys, columns) {
34917
34922
  if (bucket) bucket.push(row);
34918
34923
  else groups.set(key, [row]);
34919
34924
  }
34925
+ if (groups.size === 0 && groupByKeys.length === 0 && hasAggregateColumns(columns)) {
34926
+ groups.set("", []);
34927
+ }
34920
34928
  const result = [];
34921
34929
  for (const groupRows of groups.values()) {
34922
34930
  const outRow = { ...groupRows[0] };
@@ -35332,10 +35340,7 @@ function runFullScan(input) {
35332
35340
  rows = applyJoin(rows, rightRows, join);
35333
35341
  }
35334
35342
  rows = applyFilter(rows, stmt.where);
35335
- const hasAggregate = stmt.columns.some(
35336
- (c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr)
35337
- );
35338
- if (stmt.groupBy.length > 0 || hasAggregate) {
35343
+ if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
35339
35344
  rows = applyGroupBy(rows, stmt.groupBy, stmt.columns);
35340
35345
  }
35341
35346
  rows = applyHaving(rows, stmt.having);
@@ -37213,7 +37218,7 @@ function buildBatchStatementPlan(stmt, info) {
37213
37218
  return [
37214
37219
  `CREATE TEMP TABLE ${stmt.name}`,
37215
37220
  ` scope: batch\uFF08\u30D0\u30C3\u30C1\u7D42\u4E86\u6642\u306B\u81EA\u52D5\u7834\u68C4\uFF09`,
37216
- ` rows: \u5B9F\u4F53\u5316\u524D\u306E\u305F\u3081\u4E0D\u660E\uFF08\u4E0A\u9650 ${TEMP_TABLE_MAX_ROWS} \u884C\u3001\u8D85\u904E\u306F\u30A8\u30E9\u30FC\uFF09`,
37221
+ ` rows: \u5B9F\u4F53\u5316\u524D\u306E\u305F\u3081\u4E0D\u660E\uFF08\u65E2\u5B9A\u4E0A\u9650 ${TEMP_TABLE_MAX_ROWS} \u884C\u3001tempTableMaxRows \u3067\u5909\u66F4\u53EF\u3001\u8D85\u904E\u306F\u30A8\u30E9\u30FC\uFF09`,
37217
37222
  ...buildPlanForBatchQuery(stmt.query, info).map((l) => ` ${l}`)
37218
37223
  ];
37219
37224
  }
@@ -38236,6 +38241,7 @@ async function createKsqlRuntime(serverOptions, input) {
38236
38241
  }
38237
38242
  const onLimit2 = input.onLimit ?? envOnLimit("KSQL_ON_LIMIT") ?? profile2.query?.onLimit ?? "error";
38238
38243
  const timeout2 = input.timeout ?? envInt("KSQL_TIMEOUT") ?? profile2.query?.timeout ?? 3e4;
38244
+ const tempTableMaxRows2 = input.tempTableMaxRows ?? envInt("KSQL_TEMP_TABLE_MAX_ROWS") ?? profile2.query?.tempTableMaxRows;
38239
38245
  const appIds = extractAppIds(sql);
38240
38246
  const defaultApp = envInt("KSQL_APP") ?? profile2.app ?? null;
38241
38247
  if (appIds.length === 0 && defaultApp !== null) appIds.push(defaultApp);
@@ -38376,7 +38382,8 @@ async function createKsqlRuntime(serverOptions, input) {
38376
38382
  maxRecords: maxRecords2,
38377
38383
  fetchParallel: fetchParallel2,
38378
38384
  onLimit: onLimit2,
38379
- timeout: timeout2
38385
+ timeout: timeout2,
38386
+ tempTableMaxRows: tempTableMaxRows2
38380
38387
  };
38381
38388
  }
38382
38389
 
@@ -38817,7 +38824,8 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
38817
38824
  maxRecords: input.maxRecords,
38818
38825
  fetchParallel: input.fetchParallel,
38819
38826
  onLimit: input.onLimit,
38820
- timeout: input.timeout
38827
+ timeout: input.timeout,
38828
+ tempTableMaxRows: input.tempTableMaxRows
38821
38829
  });
38822
38830
  const batchResult = await executeBatchSql(runtime2.sql, runtime2.client, {
38823
38831
  maxRecords: runtime2.maxRecords,
@@ -38825,6 +38833,9 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
38825
38833
  onLimitReached: runtime2.onLimit,
38826
38834
  cacheContext: runtime2.cacheContext,
38827
38835
  continueOnError: input.continueOnError,
38836
+ // 一時テーブル実体化上限(未指定 = エンジン既定 TEMP_TABLE_MAX_ROWS)。
38837
+ // 実体化は onLimit 設定によらず常に error(src/execute.ts の実体化経路で固定)
38838
+ tempTableMaxRows: runtime2.tempTableMaxRows,
38828
38839
  // バッチでは timeout を合計タイムアウトとして扱う(仕様 §5.7)。
38829
38840
  // runtime.timeout は env / profile / 既定 30000ms を解決済みの値で、
38830
38841
  // HTTP クライアント側の per-request タイムアウトと同値になる
@@ -38902,7 +38913,8 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
38902
38913
  maxRecords: resolveMutateRuntimeMaxRecords(validation.statements, dmlMaxRows),
38903
38914
  fetchParallel: input.fetchParallel,
38904
38915
  onLimit: DEFAULT_ON_LIMIT,
38905
- timeout: input.timeout
38916
+ timeout: input.timeout,
38917
+ tempTableMaxRows: input.tempTableMaxRows
38906
38918
  });
38907
38919
  let totalAffected = staticInsertTotal;
38908
38920
  const batchResult = await executeBatchSql(runtime.sql, runtime.client, {
@@ -38910,6 +38922,8 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
38910
38922
  fetchParallel: runtime.fetchParallel,
38911
38923
  onLimitReached: runtime.onLimit,
38912
38924
  cacheContext: runtime.cacheContext,
38925
+ // 一時テーブル実体化上限(未指定 = エンジン既定 TEMP_TABLE_MAX_ROWS)
38926
+ tempTableMaxRows: runtime.tempTableMaxRows,
38913
38927
  // 合計タイムアウト(解決済みの runtime.timeout。per-request と同値)
38914
38928
  timeoutMs: runtime.timeout,
38915
38929
  confirm: async (count, operation) => {
@@ -39140,6 +39154,7 @@ var profile = external_exports.string().min(1).describe("kintone connection prof
39140
39154
  var maxRecords = external_exports.number().int().positive().describe("Maximum records fetched per SELECT (default 500).").optional();
39141
39155
  var fetchParallel = external_exports.number().int().min(1).max(10).describe("Number of parallel kintone record-fetch requests (1-10).").optional();
39142
39156
  var onLimit = external_exports.enum(["error", "truncate"]).describe("Behavior when maxRecords is exceeded: 'error' rejects, 'truncate' returns the first maxRecords rows (default 'error').").optional();
39157
+ var tempTableMaxRows = external_exports.number().int().positive().describe("Per-temp-table cap on materialized rows for CREATE TEMP TABLE ... AS SELECT (default 10000). Overflow always errors \u2014 'truncate' never applies to temp tables, so downstream statements never see silently truncated data. Raising this increases memory use (up to 16 temp tables per batch); prefer narrowing the SELECT with WHERE.").optional();
39143
39158
  var timeout = external_exports.number().int().positive().describe("Request timeout in milliseconds. For multi-statement batches this also acts as the total batch deadline.").optional();
39144
39159
  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).");
39145
39160
  var savedQueryTags = external_exports.array(external_exports.string().min(1)).describe("Tags for organizing saved queries.").optional();
@@ -39157,6 +39172,7 @@ var queryInputSchema = external_exports.object({
39157
39172
  maxRecords,
39158
39173
  fetchParallel,
39159
39174
  onLimit,
39175
+ tempTableMaxRows,
39160
39176
  timeout,
39161
39177
  continueOnError: external_exports.boolean().describe("Batch (multi-statement) only: keep executing subsequent statements after a runtime error (default false = fail-fast).").optional(),
39162
39178
  maxTotalRecords: external_exports.number().int().positive().describe("Batch (multi-statement) only: cap on total rows returned across all result sets (default: unlimited).").optional()
@@ -39166,8 +39182,9 @@ var mutateInputSchema = external_exports.object({
39166
39182
  profile,
39167
39183
  allowDml: external_exports.literal(true).describe("Must be true to acknowledge that this call writes to kintone."),
39168
39184
  confirmText: external_exports.literal("yes").describe('Must be the literal string "yes" to confirm execution.'),
39169
- dmlMaxRows: external_exports.number().int().positive().describe("Per-statement cap on affected rows. The call fails before writing if any statement would exceed it; for UPSERT it counts inserts + updates. It does NOT limit source reads of INSERT/UPSERT ... SELECT: those follow the runtime maxRecords resolution (KSQL_MAX_RECORDS / profile query.maxRecords, default 500; temp tables hold at most 10000 rows), so choose it by intended write count only."),
39185
+ dmlMaxRows: external_exports.number().int().positive().describe("Per-statement cap on affected rows. The call fails before writing if any statement would exceed it; for UPSERT it counts inserts + updates. It does NOT limit source reads of INSERT/UPSERT ... SELECT: those follow the runtime maxRecords resolution (KSQL_MAX_RECORDS / profile query.maxRecords, default 500; temp tables hold at most 10000 rows by default, adjustable via tempTableMaxRows), so choose it by intended write count only."),
39170
39186
  fetchParallel,
39187
+ tempTableMaxRows,
39171
39188
  timeout,
39172
39189
  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()
39173
39190
  });
@@ -39209,7 +39226,7 @@ var runSavedQueryInputSchema = external_exports.object({
39209
39226
  timeout,
39210
39227
  allowDml: external_exports.literal(true).describe("Required for DML saved queries: must be true to acknowledge writes.").optional(),
39211
39228
  confirmText: external_exports.literal("yes").describe('Required for DML saved queries: must be the literal string "yes".').optional(),
39212
- dmlMaxRows: external_exports.number().int().positive().describe("Required for DML saved queries: per-statement cap on affected rows; for UPSERT it counts inserts + updates. It does NOT limit source reads of INSERT/UPSERT ... SELECT: those follow the runtime maxRecords resolution (KSQL_MAX_RECORDS / profile query.maxRecords, default 500; temp tables hold at most 10000 rows). Note: this tool's maxRecords / onLimit inputs apply to read-only saved queries only.").optional()
39229
+ dmlMaxRows: external_exports.number().int().positive().describe("Required for DML saved queries: per-statement cap on affected rows; for UPSERT it counts inserts + updates. It does NOT limit source reads of INSERT/UPSERT ... SELECT: those follow the runtime maxRecords resolution (KSQL_MAX_RECORDS / profile query.maxRecords, default 500). Saved queries are single-statement, so temp tables do not apply here. Note: this tool's maxRecords / onLimit inputs apply to read-only saved queries only.").optional()
39213
39230
  });
39214
39231
  var validateInputShape = validateInputSchema.shape;
39215
39232
  var explainInputShape = explainInputSchema.shape;
@@ -39258,7 +39275,7 @@ Options:
39258
39275
  -h, --help Show help
39259
39276
  `);
39260
39277
  }
39261
- var SERVER_VERSION = true ? "1.10.0" : "0.0.0-dev";
39278
+ var SERVER_VERSION = true ? "1.12.0" : "0.0.0-dev";
39262
39279
  function createServer(args) {
39263
39280
  const server = new McpServer({
39264
39281
  name: "ksql-mcp",
@@ -39285,7 +39302,7 @@ function createServer(args) {
39285
39302
  }, tools.queryTool);
39286
39303
  server.registerTool("ksql_mutate", {
39287
39304
  title: "Run mutating kSQL",
39288
- description: "Execute DML kSQL with explicit allowDml, confirmText, and dmlMaxRows safety controls. Supports multi-statement DML batches with temp tables. INSERT/UPSERT INTO app ... SELECT supports app sources, temp tables, or joins of both. For UPSERT, dmlMaxRows counts inserts + updates. dmlMaxRows caps affected rows only, not source reads: the source SELECT reads up to the runtime maxRecords (KSQL_MAX_RECORDS / profile query.maxRecords, default 500); temp tables hold at most 10000 rows.",
39305
+ description: "Execute DML kSQL with explicit allowDml, confirmText, and dmlMaxRows safety controls. Supports multi-statement DML batches with temp tables. INSERT/UPSERT INTO app ... SELECT supports app sources, temp tables, or joins of both. For UPSERT, dmlMaxRows counts inserts + updates. dmlMaxRows caps affected rows only, not source reads: the source SELECT reads up to the runtime maxRecords (KSQL_MAX_RECORDS / profile query.maxRecords, default 500); temp tables hold at most 10000 rows by default (adjustable via tempTableMaxRows).",
39289
39306
  inputSchema: mutateInputShape
39290
39307
  }, tools.mutateTool);
39291
39308
  server.registerTool("ksql_describe_app", {
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rex0220/kintone-sql-tools",
3
- "version": "1.10.0",
3
+ "version": "1.12.0",
4
4
  "description": "kintone SQL plugin, CLI, and MCP tools (ksql)",
5
5
  "publishConfig": {
6
6
  "access": "public"