@rex0220/kintone-sql-tools 3.75.0 → 3.76.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 +12 -1
- package/dist-cli/ksql.js +17 -3
- package/dist-engine/index.cjs +14 -14
- package/dist-engine/index.mjs +14 -14
- package/dist-engine/ksql-engine.umd.js +14 -14
- package/dist-engine/meta/bundle-baseline.json +7 -7
- package/dist-engine/meta/cjs.json +9 -9
- package/dist-engine/meta/esm.json +9 -9
- package/dist-engine/meta/umd.json +9 -9
- package/dist-flow/flow-library/index.d.ts +1 -1
- package/dist-flow/flow-library/publicTypes.d.ts +17 -0
- package/dist-flow/index.cjs +10 -10
- package/dist-flow/index.mjs +10 -10
- package/dist-flow/meta/cjs.json +12 -12
- package/dist-flow/meta/esm.json +12 -12
- package/dist-mcp/ksql-mcp.js +19 -5
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -267,7 +267,7 @@ npm パッケージは 2 つのサブパスを **semver 対象の公開 API**
|
|
|
267
267
|
| サブパス | 用途 | 主な export |
|
|
268
268
|
|---|---|---|
|
|
269
269
|
| `@rex0220/kintone-sql-tools/engine` | **read-only** のクエリ実行(ダッシュボード等)。書込 API は構造的に遮断 | `runQuery` / `runBatch` / `explainQuery` / `createReadonlyKintoneClient` / `KsqlEngineError` / `version` |
|
|
270
|
-
| `@rex0220/kintone-sql-tools/flow` | **Flow dialect 1**(→ [言語リファレンス §27](docs/ksql_language_reference.md))のスクリプト解析・検証・**文単位実行**(バッチランナー向け・書込可能) | `parseScript` / `validateScript` / `explainScript`(`asOf`/`timezone` 注入可) / `createExecutionContext`(`onChunkWritten`
|
|
270
|
+
| `@rex0220/kintone-sql-tools/flow` | **Flow dialect 1**(→ [言語リファレンス §27](docs/ksql_language_reference.md))のスクリプト解析・検証・**文単位実行**(バッチランナー向け・書込可能) | `parseScript` / `validateScript` / `explainScript`(`asOf`/`timezone` 注入可) / `createExecutionContext`(`onChunkWritten` 書込チャンク通知・`onImportSourceMaterialized` IMPORT receipt) / `executeStatement` / `previewStatement`(dry-run 差分プレビュー・書込 0 回) / `disposeExecutionContext` / `createImportSourceResolver` / `FlowImportProviderError` / `createKintoneClient` / `isDmlResult`(`FlowDmlResult` 型ガード) / `version` |
|
|
271
271
|
|
|
272
272
|
バッチ実行ランナー **kSQL Flow**(`/flow` API を使った公式ランナー・別リポジトリ): https://github.com/rex0220/ksql-flow
|
|
273
273
|
|
|
@@ -308,6 +308,10 @@ const importSource = createImportSourceResolver([{
|
|
|
308
308
|
const parsed = parseScript(sql, capability);
|
|
309
309
|
const ctx = createExecutionContext({
|
|
310
310
|
...capability, client, statements: parsed.statements, meta: parsed.meta, importSource,
|
|
311
|
+
onImportSourceMaterialized(info) {
|
|
312
|
+
// { statementIndex, name, kind, rows, encoding }
|
|
313
|
+
inputFiles.push(info);
|
|
314
|
+
},
|
|
311
315
|
});
|
|
312
316
|
```
|
|
313
317
|
|
|
@@ -317,6 +321,13 @@ path解決、通常ファイル・symlink・allowlist・hash検査、open/read
|
|
|
317
321
|
engineはpathを受け取りません。providerは `FlowImportProviderError` でread不能または通常ファイル外を
|
|
318
322
|
分類でき、その他のsource境界エラーも `StatementResult.error.code` の安定codeで返ります。
|
|
319
323
|
|
|
324
|
+
`onImportSourceMaterialized` はdecode・raw materialize成功直後、projection・validation・
|
|
325
|
+
`ON ERROR SKIP` の選別・mutationより前に1回awaitされます。CSVの `rows` はheaderを除く
|
|
326
|
+
RFC 4180 data record数(subtable CSVは継続行を含む)、JSONはtop-level record数です。
|
|
327
|
+
CSVの `encoding` は上記優先順位の解決後、JSONは `"utf8"` です。callbackがthrow/rejectすると
|
|
328
|
+
当該文はerrorになり、その文のmutation APIは0回です。通知objectのkeyは
|
|
329
|
+
`statementIndex` / `name` / `kind` / `rows` / `encoding` の5つだけです。
|
|
330
|
+
|
|
320
331
|
読取上限は既定 10,000 件(`maxRecords`)です。一時テーブルの実体化には**独立の** `tempTableMaxRows`(既定 10,000 行・超過は常にエラー)が適用されるため、大きなバッチでは両方を併せて指定してください:
|
|
321
332
|
|
|
322
333
|
```ts
|
package/dist-cli/ksql.js
CHANGED
|
@@ -18046,6 +18046,7 @@ function materializeJsonDmlSource(_source, payload, targets, maxRows) {
|
|
|
18046
18046
|
importPresence.push(present);
|
|
18047
18047
|
});
|
|
18048
18048
|
return {
|
|
18049
|
+
receipt: { rows: records.length, encoding: "utf8" },
|
|
18049
18050
|
rows,
|
|
18050
18051
|
columns: targets.map((target) => target.code),
|
|
18051
18052
|
columnMeta: new Map(targets.map((target) => [target.code, { fieldType: target.fieldType }])),
|
|
@@ -18055,8 +18056,9 @@ function materializeJsonDmlSource(_source, payload, targets, maxRows) {
|
|
|
18055
18056
|
|
|
18056
18057
|
// src/import/materializeDmlSource.ts
|
|
18057
18058
|
function materializeCsvDmlSource(source, payload, maxRows, targetCodes, fieldInfos, recordNumberSourceHeader) {
|
|
18059
|
+
const encoding = source.encoding ?? payload.encoding ?? "utf8";
|
|
18058
18060
|
const decoded = decodeCsv(payload.bytes, {
|
|
18059
|
-
encoding
|
|
18061
|
+
encoding,
|
|
18060
18062
|
hasHeader: source.hasHeader,
|
|
18061
18063
|
columns: source.columns
|
|
18062
18064
|
});
|
|
@@ -18121,6 +18123,7 @@ function materializeCsvDmlSource(source, payload, maxRows, targetCodes, fieldInf
|
|
|
18121
18123
|
return row;
|
|
18122
18124
|
});
|
|
18123
18125
|
return {
|
|
18126
|
+
receipt: { rows: decoded.rows.length, encoding },
|
|
18124
18127
|
rows: rows2,
|
|
18125
18128
|
columns: [...targetCodes],
|
|
18126
18129
|
columnMeta: new Map(targetCodes.map((code) => [code, { fieldType: infoByCode.get(code)?.fieldType ?? "SINGLE_LINE_TEXT" }])),
|
|
@@ -18131,6 +18134,7 @@ function materializeCsvDmlSource(source, payload, maxRows, targetCodes, fieldInf
|
|
|
18131
18134
|
}
|
|
18132
18135
|
const rows = decoded.rows.map((values) => Object.fromEntries(decoded.columns.map((column, i) => [column, values[i]])));
|
|
18133
18136
|
return {
|
|
18137
|
+
receipt: { rows: decoded.rows.length, encoding },
|
|
18134
18138
|
rows,
|
|
18135
18139
|
columns: decoded.columns,
|
|
18136
18140
|
// CSV cells are decoded as strings; projections such as CAST may replace this metadata downstream.
|
|
@@ -18150,6 +18154,7 @@ function materializeJsonImportRecords(_source, payload, targets, maxParents, max
|
|
|
18150
18154
|
if (targetByCode.size !== targets.length) throw new ImportSourceError("IMPORT targets contain duplicates.");
|
|
18151
18155
|
let childTotal = 0;
|
|
18152
18156
|
return {
|
|
18157
|
+
receipt: { rows: decoded.length, encoding: "utf8" },
|
|
18153
18158
|
records: decoded.map((record, index) => {
|
|
18154
18159
|
const parentRow = index + 1;
|
|
18155
18160
|
for (const code of record.keys()) if (!targetByCode.has(code)) sourceFail(parentRow, code, "unknown key (not declared in INTO).");
|
|
@@ -18185,8 +18190,9 @@ function materializeJsonImportRecords(_source, payload, targets, maxParents, max
|
|
|
18185
18190
|
};
|
|
18186
18191
|
}
|
|
18187
18192
|
function materializeCliKintoneCsvImportRecords(source, payload, targets, replacementTables, maxParents, recordNumberSourceHeader) {
|
|
18193
|
+
const encoding = source.encoding ?? payload.encoding ?? "utf8";
|
|
18188
18194
|
const decoded = decodeCsv(payload.bytes, {
|
|
18189
|
-
encoding
|
|
18195
|
+
encoding,
|
|
18190
18196
|
hasHeader: source.hasHeader,
|
|
18191
18197
|
columns: source.columns
|
|
18192
18198
|
});
|
|
@@ -18242,7 +18248,7 @@ function materializeCliKintoneCsvImportRecords(source, payload, targets, replace
|
|
|
18242
18248
|
}
|
|
18243
18249
|
});
|
|
18244
18250
|
if (records.length === 0) throw new ImportSourceError("CSV has no parent rows.");
|
|
18245
|
-
return { records };
|
|
18251
|
+
return { receipt: { rows: decoded.rows.length, encoding }, records };
|
|
18246
18252
|
}
|
|
18247
18253
|
|
|
18248
18254
|
// src/import/importRecordValidation.ts
|
|
@@ -18533,6 +18539,7 @@ var materializedMetaBySelectResult = /* @__PURE__ */ new WeakMap();
|
|
|
18533
18539
|
var materializedMetaByValidationResult = /* @__PURE__ */ new WeakMap();
|
|
18534
18540
|
var importSourceByDmlStatement = /* @__PURE__ */ new WeakMap();
|
|
18535
18541
|
var statementEvaluationContextKey = /* @__PURE__ */ Symbol("statementEvaluationContext");
|
|
18542
|
+
var importSourceMaterializedCallbackKey = /* @__PURE__ */ Symbol("importSourceMaterializedCallback");
|
|
18536
18543
|
var nativeUpsertExecutionKey = /* @__PURE__ */ Symbol("nativeUpsertExecution");
|
|
18537
18544
|
var nativeUpsertExplainCapabilityKey = /* @__PURE__ */ Symbol("nativeUpsertExplainCapability");
|
|
18538
18545
|
function withNativeUpsertExecutionOption(options, enabled, explainClientCapability) {
|
|
@@ -18562,6 +18569,10 @@ function bindStatementEvaluationContext(options) {
|
|
|
18562
18569
|
function statementEvaluationContext(options) {
|
|
18563
18570
|
return options[statementEvaluationContextKey] ?? {};
|
|
18564
18571
|
}
|
|
18572
|
+
async function notifyImportSourceMaterialized(options, source, receipt) {
|
|
18573
|
+
const callback = options[importSourceMaterializedCallbackKey];
|
|
18574
|
+
if (callback) await callback({ name: source.sourceName, kind: source.kind, ...receipt });
|
|
18575
|
+
}
|
|
18565
18576
|
var defaultCacheContextByClient = /* @__PURE__ */ new WeakMap();
|
|
18566
18577
|
var nextDefaultCacheContextId = 1;
|
|
18567
18578
|
var nextCacheInvocationId = 1;
|
|
@@ -24772,6 +24783,7 @@ async function executeImport(stmt, client, options, cacheContext, tempTables) {
|
|
|
24772
24783
|
const numberPrecision = fieldInfos.some((info) => targetCodes.includes(info.code) && info.fieldType === "NUMBER") ? await getNumberPrecisionCached(stmt.appId, client, cacheContext) : void 0;
|
|
24773
24784
|
const payload = await loadImportSource(handle, /* @__PURE__ */ new Map());
|
|
24774
24785
|
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);
|
|
24786
|
+
await notifyImportSourceMaterialized(options, stmt.source, materialized.receipt);
|
|
24775
24787
|
const operation = stmt.source.kind === "CSV" ? "UPDATE" : stmt.keyFields ? "UPSERT" : "INSERT";
|
|
24776
24788
|
const prepared = prepareImportRecords(materialized, targets, fieldInfos, numberPrecision, operation);
|
|
24777
24789
|
if (stmt.source.kind === "CSV") return executeCsvSubtableReplacement(stmt, materialized, prepared, fieldInfos, client, options, tempTables);
|
|
@@ -25074,6 +25086,7 @@ async function executeImportRecordNumberUpdate(stmt, handle, client, options, ca
|
|
|
25074
25086
|
fieldInfos,
|
|
25075
25087
|
stmt.recordNumberSourceHeader
|
|
25076
25088
|
);
|
|
25089
|
+
await notifyImportSourceMaterialized(options, stmt.source, sourceTable.receipt);
|
|
25077
25090
|
const keyValues = sourceTable.recordNumberSourceValues;
|
|
25078
25091
|
if (!keyValues) throw new Error("InternalError: record-number source values were not materialized.");
|
|
25079
25092
|
const keyPlan = preflightImportRecordNumbers(keyValues, stmt.recordNumberSourceHeader);
|
|
@@ -25210,6 +25223,7 @@ async function materializeDmlSource(stmt, client, options, cacheContext, tempTab
|
|
|
25210
25223
|
const targetByCode = new Map((targetFields ?? []).map((info) => [info.code, info]));
|
|
25211
25224
|
const jsonTargets = stmt.fields.map((code) => ({ code, fieldType: targetByCode.get(code)?.fieldType ?? "SINGLE_LINE_TEXT" }));
|
|
25212
25225
|
const raw = imported.source.kind === "JSON" ? materializeJsonDmlSource(imported.source, payload, jsonTargets, rowLimit) : materializeCsvDmlSource(imported.source, payload, rowLimit, stmt.fields, targetFields);
|
|
25226
|
+
await notifyImportSourceMaterialized(options, imported.source, raw.receipt);
|
|
25213
25227
|
imported.audit = raw.importAudit;
|
|
25214
25228
|
if (imported.source.kind === "JSON") return raw;
|
|
25215
25229
|
if (!imported.source.projection) return raw;
|