@rex0220/kintone-sql-tools 1.1.2 → 1.2.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 +190 -190
- package/dist-cli/ksql.js +252 -255
- package/dist-mcp/ksql-mcp.js +37830 -0
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +19 -6
package/dist-cli/ksql.js
CHANGED
|
@@ -34,7 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
shouldExitOnEmpty: () => shouldExitOnEmpty
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(index_exports);
|
|
37
|
-
var
|
|
37
|
+
var import_fs2 = require("fs");
|
|
38
38
|
var import_path = require("path");
|
|
39
39
|
var import_readline = require("readline");
|
|
40
40
|
var import_os = require("os");
|
|
@@ -5820,6 +5820,241 @@ function detectSortKind(fieldType, calcFormat) {
|
|
|
5820
5820
|
return void 0;
|
|
5821
5821
|
}
|
|
5822
5822
|
|
|
5823
|
+
// src/node/appProfiles.ts
|
|
5824
|
+
var import_fs = require("fs");
|
|
5825
|
+
function parseTokenMap(raw) {
|
|
5826
|
+
const out = {};
|
|
5827
|
+
if (!raw.trim()) return out;
|
|
5828
|
+
const pairs = raw.split(",");
|
|
5829
|
+
for (const pair of pairs) {
|
|
5830
|
+
const idx = pair.indexOf("=");
|
|
5831
|
+
if (idx <= 0) throw new Error("ArgumentError: --token-map must be APPxxx=token pairs.");
|
|
5832
|
+
const key = normalizeAppKey(pair.slice(0, idx).trim());
|
|
5833
|
+
const value = pair.slice(idx + 1).trim();
|
|
5834
|
+
if (!value) throw new Error(`ArgumentError: token is empty for ${key}.`);
|
|
5835
|
+
out[key] = value;
|
|
5836
|
+
}
|
|
5837
|
+
return out;
|
|
5838
|
+
}
|
|
5839
|
+
function parseTokenFile(path) {
|
|
5840
|
+
const raw = (0, import_fs.readFileSync)(path, "utf-8");
|
|
5841
|
+
const parsed = JSON.parse(raw);
|
|
5842
|
+
const out = {};
|
|
5843
|
+
for (const [k, v] of Object.entries(parsed)) out[normalizeAppKey(k)] = String(v);
|
|
5844
|
+
return out;
|
|
5845
|
+
}
|
|
5846
|
+
function normalizeAppKey(v) {
|
|
5847
|
+
const m1 = v.match(/^APP(\d+)$/i);
|
|
5848
|
+
if (m1) return `APP${m1[1]}`;
|
|
5849
|
+
const m2 = v.match(/^(\d+)$/);
|
|
5850
|
+
if (m2) return `APP${m2[1]}`;
|
|
5851
|
+
throw new Error(`ArgumentError: invalid app key "${v}"`);
|
|
5852
|
+
}
|
|
5853
|
+
function extractAppIds(sql) {
|
|
5854
|
+
const out = /* @__PURE__ */ new Set();
|
|
5855
|
+
for (const m of sql.matchAll(/\bAPP(\d+)\b/gi)) out.add(Number(m[1]));
|
|
5856
|
+
return [...out];
|
|
5857
|
+
}
|
|
5858
|
+
function isSqlIdentContinue(ch) {
|
|
5859
|
+
if (!ch) return false;
|
|
5860
|
+
const cp = ch.codePointAt(0);
|
|
5861
|
+
return cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp >= 48 && cp <= 57 || cp === 95 || cp === 36 || cp >= 12352 && cp <= 12543 || cp >= 13312 && cp <= 40959 || cp >= 63744 && cp <= 64255 || cp >= 65281 && cp <= 65376;
|
|
5862
|
+
}
|
|
5863
|
+
function isProfileNameChar(ch) {
|
|
5864
|
+
if (!ch) return false;
|
|
5865
|
+
const cp = ch.codePointAt(0);
|
|
5866
|
+
return cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp >= 48 && cp <= 57 || ch === "_" || ch === "-" || ch === "." || ch === "$";
|
|
5867
|
+
}
|
|
5868
|
+
function tryParseAppProfileToken(sql, start) {
|
|
5869
|
+
const head = sql.slice(start, start + 3);
|
|
5870
|
+
if (head.toUpperCase() !== "APP") return null;
|
|
5871
|
+
const prev = start > 0 ? sql[start - 1] : "";
|
|
5872
|
+
if (isSqlIdentContinue(prev)) return null;
|
|
5873
|
+
let i = start + 3;
|
|
5874
|
+
const digitStart = i;
|
|
5875
|
+
while (i < sql.length && /[0-9]/.test(sql[i])) i++;
|
|
5876
|
+
const digitEnd = i;
|
|
5877
|
+
if (digitEnd === digitStart) return null;
|
|
5878
|
+
if (sql[i] === "$") {
|
|
5879
|
+
i++;
|
|
5880
|
+
const subStart = i;
|
|
5881
|
+
while (i < sql.length && isSqlIdentContinue(sql[i])) i++;
|
|
5882
|
+
if (i === subStart) return null;
|
|
5883
|
+
}
|
|
5884
|
+
const appEnd = i;
|
|
5885
|
+
let profile = null;
|
|
5886
|
+
if (sql[i] === "@") {
|
|
5887
|
+
i++;
|
|
5888
|
+
const pStart = i;
|
|
5889
|
+
while (i < sql.length && isProfileNameChar(sql[i])) i++;
|
|
5890
|
+
if (i === pStart) return null;
|
|
5891
|
+
profile = sql.slice(pStart, i);
|
|
5892
|
+
}
|
|
5893
|
+
const next = i < sql.length ? sql[i] : "";
|
|
5894
|
+
if (isSqlIdentContinue(next)) return null;
|
|
5895
|
+
return {
|
|
5896
|
+
appId: Number(sql.slice(digitStart, digitEnd)),
|
|
5897
|
+
profile,
|
|
5898
|
+
start,
|
|
5899
|
+
digitStart,
|
|
5900
|
+
digitEnd,
|
|
5901
|
+
appEnd,
|
|
5902
|
+
fullEnd: i
|
|
5903
|
+
};
|
|
5904
|
+
}
|
|
5905
|
+
function collectAppProfileTokens(sql) {
|
|
5906
|
+
const tokens = [];
|
|
5907
|
+
let i = 0;
|
|
5908
|
+
while (i < sql.length) {
|
|
5909
|
+
const ch = sql[i];
|
|
5910
|
+
if (ch === "'") {
|
|
5911
|
+
i++;
|
|
5912
|
+
while (i < sql.length) {
|
|
5913
|
+
if (sql[i] === "'") {
|
|
5914
|
+
i++;
|
|
5915
|
+
if (i < sql.length && sql[i] === "'") {
|
|
5916
|
+
i++;
|
|
5917
|
+
continue;
|
|
5918
|
+
}
|
|
5919
|
+
break;
|
|
5920
|
+
}
|
|
5921
|
+
i++;
|
|
5922
|
+
}
|
|
5923
|
+
continue;
|
|
5924
|
+
}
|
|
5925
|
+
if (ch === "`") {
|
|
5926
|
+
i++;
|
|
5927
|
+
while (i < sql.length && sql[i] !== "`") i++;
|
|
5928
|
+
if (i < sql.length) i++;
|
|
5929
|
+
continue;
|
|
5930
|
+
}
|
|
5931
|
+
if (ch === "-" && sql[i + 1] === "-") {
|
|
5932
|
+
i += 2;
|
|
5933
|
+
while (i < sql.length && sql[i] !== "\n") i++;
|
|
5934
|
+
continue;
|
|
5935
|
+
}
|
|
5936
|
+
if (ch === "/" && sql[i + 1] === "*") {
|
|
5937
|
+
i += 2;
|
|
5938
|
+
while (i < sql.length) {
|
|
5939
|
+
if (sql[i] === "*" && sql[i + 1] === "/") {
|
|
5940
|
+
i += 2;
|
|
5941
|
+
break;
|
|
5942
|
+
}
|
|
5943
|
+
i++;
|
|
5944
|
+
}
|
|
5945
|
+
continue;
|
|
5946
|
+
}
|
|
5947
|
+
const parsed = tryParseAppProfileToken(sql, i);
|
|
5948
|
+
if (!parsed) {
|
|
5949
|
+
i++;
|
|
5950
|
+
continue;
|
|
5951
|
+
}
|
|
5952
|
+
tokens.push(parsed);
|
|
5953
|
+
i = parsed.fullEnd;
|
|
5954
|
+
}
|
|
5955
|
+
return tokens;
|
|
5956
|
+
}
|
|
5957
|
+
function nextVirtualAppId(used) {
|
|
5958
|
+
let id = 9e8;
|
|
5959
|
+
while (used.has(id)) id++;
|
|
5960
|
+
used.add(id);
|
|
5961
|
+
return id;
|
|
5962
|
+
}
|
|
5963
|
+
function normalizeSqlAppProfiles(sql, defaultProfile = "dev") {
|
|
5964
|
+
const tokens = collectAppProfileTokens(sql);
|
|
5965
|
+
const hasProfileSyntax = tokens.some((t) => t.profile !== null);
|
|
5966
|
+
const profilesByApp = /* @__PURE__ */ new Map();
|
|
5967
|
+
const normalizedProfile = (profile) => profile ?? defaultProfile;
|
|
5968
|
+
for (const t of tokens) {
|
|
5969
|
+
const p = normalizedProfile(t.profile);
|
|
5970
|
+
let set = profilesByApp.get(t.appId);
|
|
5971
|
+
if (!set) {
|
|
5972
|
+
set = /* @__PURE__ */ new Set();
|
|
5973
|
+
profilesByApp.set(t.appId, set);
|
|
5974
|
+
}
|
|
5975
|
+
set.add(p.toLowerCase());
|
|
5976
|
+
}
|
|
5977
|
+
const usedAppIds = new Set(tokens.map((t) => t.appId));
|
|
5978
|
+
const pairToMapped = /* @__PURE__ */ new Map();
|
|
5979
|
+
const appBindingByMappedApp = /* @__PURE__ */ new Map();
|
|
5980
|
+
for (const [appId, pSet] of profilesByApp.entries()) {
|
|
5981
|
+
const profiles = [...pSet].sort();
|
|
5982
|
+
if (profiles.length <= 1) continue;
|
|
5983
|
+
for (const pLower of profiles) {
|
|
5984
|
+
const mapped = nextVirtualAppId(usedAppIds);
|
|
5985
|
+
pairToMapped.set(`${appId}@${pLower}`, mapped);
|
|
5986
|
+
appBindingByMappedApp.set(mapped, { appId, profile: pLower });
|
|
5987
|
+
}
|
|
5988
|
+
}
|
|
5989
|
+
const out = [];
|
|
5990
|
+
let cursor = 0;
|
|
5991
|
+
for (const t of tokens) {
|
|
5992
|
+
const p = normalizedProfile(t.profile);
|
|
5993
|
+
const pLower = p.toLowerCase();
|
|
5994
|
+
const mapped = pairToMapped.get(`${t.appId}@${pLower}`) ?? t.appId;
|
|
5995
|
+
appBindingByMappedApp.set(mapped, { appId: t.appId, profile: pLower });
|
|
5996
|
+
out.push(sql.slice(cursor, t.start));
|
|
5997
|
+
out.push(sql.slice(t.start, t.digitStart));
|
|
5998
|
+
out.push(String(mapped));
|
|
5999
|
+
out.push(sql.slice(t.digitEnd, t.appEnd));
|
|
6000
|
+
cursor = t.fullEnd;
|
|
6001
|
+
}
|
|
6002
|
+
out.push(sql.slice(cursor));
|
|
6003
|
+
return {
|
|
6004
|
+
normalizedSql: out.join(""),
|
|
6005
|
+
hasProfileSyntax,
|
|
6006
|
+
appBindingByMappedApp
|
|
6007
|
+
};
|
|
6008
|
+
}
|
|
6009
|
+
function buildCacheContext(defaultProfile, appBindingByMappedApp) {
|
|
6010
|
+
if (appBindingByMappedApp.size === 0) return `default:${defaultProfile.toLowerCase()}`;
|
|
6011
|
+
const pairs = [...appBindingByMappedApp.entries()].sort((a, b) => a[0] - b[0]).map(([mappedAppId, b]) => `M${mappedAppId}=APP${b.appId}@${b.profile}`);
|
|
6012
|
+
return `apps:${pairs.join(",")}`;
|
|
6013
|
+
}
|
|
6014
|
+
function formatResolvedAppProfiles(sql, defaultProfile) {
|
|
6015
|
+
const parsed = normalizeSqlAppProfiles(sql, defaultProfile);
|
|
6016
|
+
if (parsed.appBindingByMappedApp.size === 0) return "(none)";
|
|
6017
|
+
return [...parsed.appBindingByMappedApp.values()].map((b) => `APP${b.appId}->${b.profile}`).join(", ");
|
|
6018
|
+
}
|
|
6019
|
+
|
|
6020
|
+
// src/node/dmlGuard.ts
|
|
6021
|
+
function getStatementType(stmt) {
|
|
6022
|
+
if (!stmt || typeof stmt !== "object") return "UNKNOWN";
|
|
6023
|
+
const obj = stmt;
|
|
6024
|
+
return typeof obj.type === "string" ? obj.type : "UNKNOWN";
|
|
6025
|
+
}
|
|
6026
|
+
function isDmlType(type) {
|
|
6027
|
+
return type === "INSERT" || type === "INSERT_SELECT" || type === "UPDATE" || type === "DELETE" || type === "UPSERT" || type === "UPSERT_SELECT" || type === "REORDER";
|
|
6028
|
+
}
|
|
6029
|
+
function hasWhereClause(stmt) {
|
|
6030
|
+
if (!stmt || typeof stmt !== "object") return false;
|
|
6031
|
+
const obj = stmt;
|
|
6032
|
+
return obj.where !== null && obj.where !== void 0;
|
|
6033
|
+
}
|
|
6034
|
+
function isNoFromSelectStatement(stmt) {
|
|
6035
|
+
if (!stmt || typeof stmt !== "object") return false;
|
|
6036
|
+
const obj = stmt;
|
|
6037
|
+
return obj.type === "SELECT" && obj.from?.appId === 0 && obj.from?.cteName === "__NO_FROM__";
|
|
6038
|
+
}
|
|
6039
|
+
function getInsertValuesCount(stmt) {
|
|
6040
|
+
if (!stmt || typeof stmt !== "object") return null;
|
|
6041
|
+
const obj = stmt;
|
|
6042
|
+
if (obj.type !== "INSERT") return null;
|
|
6043
|
+
return Array.isArray(obj.values) ? obj.values.length : null;
|
|
6044
|
+
}
|
|
6045
|
+
function collectDmlTargetFields(stmt) {
|
|
6046
|
+
if (!stmt || typeof stmt !== "object") return [];
|
|
6047
|
+
const obj = stmt;
|
|
6048
|
+
if (!obj.type) return [];
|
|
6049
|
+
if (obj.type === "UPDATE") {
|
|
6050
|
+
return (obj.assignments ?? []).map((a) => a.field).filter((f) => Boolean(f));
|
|
6051
|
+
}
|
|
6052
|
+
if (obj.type === "INSERT" || obj.type === "INSERT_SELECT" || obj.type === "UPSERT" || obj.type === "UPSERT_SELECT") {
|
|
6053
|
+
return [...obj.fields ?? [], ...obj.keyFields ?? []];
|
|
6054
|
+
}
|
|
6055
|
+
return [];
|
|
6056
|
+
}
|
|
6057
|
+
|
|
5823
6058
|
// src/cli/index.ts
|
|
5824
6059
|
var HELP_TEXT = `ksql - Execute SQL against kintone apps
|
|
5825
6060
|
|
|
@@ -5863,7 +6098,7 @@ Options:
|
|
|
5863
6098
|
--debug-url Show only HTTP request URL debug logs
|
|
5864
6099
|
--debug-headers Show request headers in debug logs (masked)
|
|
5865
6100
|
--exit-on-empty Return exit code 1 when rowCount is 0
|
|
5866
|
-
--allow-dml Enable UPDATE/DELETE/INSERT/UPSERT execution
|
|
6101
|
+
--allow-dml Enable UPDATE/DELETE/INSERT/UPSERT/REORDER execution
|
|
5867
6102
|
--yes Skip DML confirmation prompt
|
|
5868
6103
|
--allow-without-where Allow UPDATE/DELETE without WHERE
|
|
5869
6104
|
--dml-max-rows <n> Max affected rows for DML guard (default: 100)
|
|
@@ -6128,216 +6363,14 @@ function parseArgs(argv) {
|
|
|
6128
6363
|
}
|
|
6129
6364
|
function getVersion() {
|
|
6130
6365
|
const pkgPath = (0, import_path.resolve)(__dirname, "../package.json");
|
|
6131
|
-
const raw = (0,
|
|
6366
|
+
const raw = (0, import_fs2.readFileSync)(pkgPath, "utf-8");
|
|
6132
6367
|
const pkg = JSON.parse(raw);
|
|
6133
6368
|
return pkg.version ?? "0.0.0";
|
|
6134
6369
|
}
|
|
6135
6370
|
function loadConfig(configPath) {
|
|
6136
|
-
const raw = (0,
|
|
6371
|
+
const raw = (0, import_fs2.readFileSync)(configPath, "utf-8");
|
|
6137
6372
|
return JSON.parse(raw);
|
|
6138
6373
|
}
|
|
6139
|
-
function parseTokenMap(raw) {
|
|
6140
|
-
const out = {};
|
|
6141
|
-
if (!raw.trim()) return out;
|
|
6142
|
-
const pairs = raw.split(",");
|
|
6143
|
-
for (const pair of pairs) {
|
|
6144
|
-
const idx = pair.indexOf("=");
|
|
6145
|
-
if (idx <= 0) throw new Error("ArgumentError: --token-map must be APPxxx=token pairs.");
|
|
6146
|
-
const key = normalizeAppKey(pair.slice(0, idx).trim());
|
|
6147
|
-
const value = pair.slice(idx + 1).trim();
|
|
6148
|
-
if (!value) throw new Error(`ArgumentError: token is empty for ${key}.`);
|
|
6149
|
-
out[key] = value;
|
|
6150
|
-
}
|
|
6151
|
-
return out;
|
|
6152
|
-
}
|
|
6153
|
-
function parseTokenFile(path) {
|
|
6154
|
-
const raw = (0, import_fs.readFileSync)(path, "utf-8");
|
|
6155
|
-
const parsed = JSON.parse(raw);
|
|
6156
|
-
const out = {};
|
|
6157
|
-
for (const [k, v] of Object.entries(parsed)) out[normalizeAppKey(k)] = String(v);
|
|
6158
|
-
return out;
|
|
6159
|
-
}
|
|
6160
|
-
function normalizeAppKey(v) {
|
|
6161
|
-
const m1 = v.match(/^APP(\d+)$/i);
|
|
6162
|
-
if (m1) return `APP${m1[1]}`;
|
|
6163
|
-
const m2 = v.match(/^(\d+)$/);
|
|
6164
|
-
if (m2) return `APP${m2[1]}`;
|
|
6165
|
-
throw new Error(`ArgumentError: invalid app key "${v}"`);
|
|
6166
|
-
}
|
|
6167
|
-
function extractAppIds(sql) {
|
|
6168
|
-
const out = /* @__PURE__ */ new Set();
|
|
6169
|
-
for (const m of sql.matchAll(/\bAPP(\d+)\b/gi)) out.add(Number(m[1]));
|
|
6170
|
-
return [...out];
|
|
6171
|
-
}
|
|
6172
|
-
function isSqlIdentContinue(ch) {
|
|
6173
|
-
if (!ch) return false;
|
|
6174
|
-
const cp = ch.codePointAt(0);
|
|
6175
|
-
return cp >= 65 && cp <= 90 || // A-Z
|
|
6176
|
-
cp >= 97 && cp <= 122 || // a-z
|
|
6177
|
-
cp >= 48 && cp <= 57 || // 0-9
|
|
6178
|
-
cp === 95 || // _
|
|
6179
|
-
cp === 36 || // $
|
|
6180
|
-
cp >= 12352 && cp <= 12543 || cp >= 13312 && cp <= 40959 || cp >= 63744 && cp <= 64255 || cp >= 65281 && cp <= 65376;
|
|
6181
|
-
}
|
|
6182
|
-
function isProfileNameChar(ch) {
|
|
6183
|
-
if (!ch) return false;
|
|
6184
|
-
const cp = ch.codePointAt(0);
|
|
6185
|
-
return cp >= 65 && cp <= 90 || // A-Z
|
|
6186
|
-
cp >= 97 && cp <= 122 || // a-z
|
|
6187
|
-
cp >= 48 && cp <= 57 || // 0-9
|
|
6188
|
-
ch === "_" || ch === "-" || ch === "." || ch === "$";
|
|
6189
|
-
}
|
|
6190
|
-
function tryParseAppProfileToken(sql, start) {
|
|
6191
|
-
const head = sql.slice(start, start + 3);
|
|
6192
|
-
if (head.toUpperCase() !== "APP") return null;
|
|
6193
|
-
const prev = start > 0 ? sql[start - 1] : "";
|
|
6194
|
-
if (isSqlIdentContinue(prev)) return null;
|
|
6195
|
-
let i = start + 3;
|
|
6196
|
-
const digitStart = i;
|
|
6197
|
-
while (i < sql.length && /[0-9]/.test(sql[i])) i++;
|
|
6198
|
-
const digitEnd = i;
|
|
6199
|
-
if (digitEnd === digitStart) return null;
|
|
6200
|
-
if (sql[i] === "$") {
|
|
6201
|
-
i++;
|
|
6202
|
-
const subStart = i;
|
|
6203
|
-
while (i < sql.length && isSqlIdentContinue(sql[i])) i++;
|
|
6204
|
-
if (i === subStart) return null;
|
|
6205
|
-
}
|
|
6206
|
-
const appEnd = i;
|
|
6207
|
-
let profile = null;
|
|
6208
|
-
if (sql[i] === "@") {
|
|
6209
|
-
i++;
|
|
6210
|
-
const pStart = i;
|
|
6211
|
-
while (i < sql.length && isProfileNameChar(sql[i])) i++;
|
|
6212
|
-
if (i === pStart) return null;
|
|
6213
|
-
profile = sql.slice(pStart, i);
|
|
6214
|
-
}
|
|
6215
|
-
const next = i < sql.length ? sql[i] : "";
|
|
6216
|
-
if (isSqlIdentContinue(next)) return null;
|
|
6217
|
-
return {
|
|
6218
|
-
appId: Number(sql.slice(digitStart, digitEnd)),
|
|
6219
|
-
profile,
|
|
6220
|
-
start,
|
|
6221
|
-
digitStart,
|
|
6222
|
-
digitEnd,
|
|
6223
|
-
appEnd,
|
|
6224
|
-
fullEnd: i
|
|
6225
|
-
};
|
|
6226
|
-
}
|
|
6227
|
-
function collectAppProfileTokens(sql) {
|
|
6228
|
-
const tokens = [];
|
|
6229
|
-
let i = 0;
|
|
6230
|
-
while (i < sql.length) {
|
|
6231
|
-
const ch = sql[i];
|
|
6232
|
-
if (ch === "'") {
|
|
6233
|
-
i++;
|
|
6234
|
-
while (i < sql.length) {
|
|
6235
|
-
if (sql[i] === "'") {
|
|
6236
|
-
i++;
|
|
6237
|
-
if (i < sql.length && sql[i] === "'") {
|
|
6238
|
-
i++;
|
|
6239
|
-
continue;
|
|
6240
|
-
}
|
|
6241
|
-
break;
|
|
6242
|
-
}
|
|
6243
|
-
i++;
|
|
6244
|
-
}
|
|
6245
|
-
continue;
|
|
6246
|
-
}
|
|
6247
|
-
if (ch === "`") {
|
|
6248
|
-
i++;
|
|
6249
|
-
while (i < sql.length && sql[i] !== "`") i++;
|
|
6250
|
-
if (i < sql.length) i++;
|
|
6251
|
-
continue;
|
|
6252
|
-
}
|
|
6253
|
-
if (ch === "-" && sql[i + 1] === "-") {
|
|
6254
|
-
i += 2;
|
|
6255
|
-
while (i < sql.length && sql[i] !== "\n") i++;
|
|
6256
|
-
continue;
|
|
6257
|
-
}
|
|
6258
|
-
if (ch === "/" && sql[i + 1] === "*") {
|
|
6259
|
-
i += 2;
|
|
6260
|
-
while (i < sql.length) {
|
|
6261
|
-
if (sql[i] === "*" && sql[i + 1] === "/") {
|
|
6262
|
-
i += 2;
|
|
6263
|
-
break;
|
|
6264
|
-
}
|
|
6265
|
-
i++;
|
|
6266
|
-
}
|
|
6267
|
-
continue;
|
|
6268
|
-
}
|
|
6269
|
-
const parsed = tryParseAppProfileToken(sql, i);
|
|
6270
|
-
if (!parsed) {
|
|
6271
|
-
i++;
|
|
6272
|
-
continue;
|
|
6273
|
-
}
|
|
6274
|
-
tokens.push(parsed);
|
|
6275
|
-
i = parsed.fullEnd;
|
|
6276
|
-
}
|
|
6277
|
-
return tokens;
|
|
6278
|
-
}
|
|
6279
|
-
function nextVirtualAppId(used) {
|
|
6280
|
-
let id = 9e8;
|
|
6281
|
-
while (used.has(id)) id++;
|
|
6282
|
-
used.add(id);
|
|
6283
|
-
return id;
|
|
6284
|
-
}
|
|
6285
|
-
function normalizeSqlAppProfiles(sql, defaultProfile = "dev") {
|
|
6286
|
-
const tokens = collectAppProfileTokens(sql);
|
|
6287
|
-
const hasProfileSyntax = tokens.some((t) => t.profile !== null);
|
|
6288
|
-
const profilesByApp = /* @__PURE__ */ new Map();
|
|
6289
|
-
const normalizedProfile = (profile) => profile ?? defaultProfile;
|
|
6290
|
-
for (const t of tokens) {
|
|
6291
|
-
const p = normalizedProfile(t.profile);
|
|
6292
|
-
let set = profilesByApp.get(t.appId);
|
|
6293
|
-
if (!set) {
|
|
6294
|
-
set = /* @__PURE__ */ new Set();
|
|
6295
|
-
profilesByApp.set(t.appId, set);
|
|
6296
|
-
}
|
|
6297
|
-
set.add(p.toLowerCase());
|
|
6298
|
-
}
|
|
6299
|
-
const usedAppIds = new Set(tokens.map((t) => t.appId));
|
|
6300
|
-
const pairToMapped = /* @__PURE__ */ new Map();
|
|
6301
|
-
const appBindingByMappedApp = /* @__PURE__ */ new Map();
|
|
6302
|
-
for (const [appId, pSet] of profilesByApp.entries()) {
|
|
6303
|
-
const profiles = [...pSet].sort();
|
|
6304
|
-
if (profiles.length <= 1) continue;
|
|
6305
|
-
for (const pLower of profiles) {
|
|
6306
|
-
const mapped = nextVirtualAppId(usedAppIds);
|
|
6307
|
-
pairToMapped.set(`${appId}@${pLower}`, mapped);
|
|
6308
|
-
appBindingByMappedApp.set(mapped, { appId, profile: pLower });
|
|
6309
|
-
}
|
|
6310
|
-
}
|
|
6311
|
-
const out = [];
|
|
6312
|
-
let cursor = 0;
|
|
6313
|
-
for (const t of tokens) {
|
|
6314
|
-
const p = normalizedProfile(t.profile);
|
|
6315
|
-
const pLower = p.toLowerCase();
|
|
6316
|
-
const mapped = pairToMapped.get(`${t.appId}@${pLower}`) ?? t.appId;
|
|
6317
|
-
appBindingByMappedApp.set(mapped, { appId: t.appId, profile: pLower });
|
|
6318
|
-
out.push(sql.slice(cursor, t.start));
|
|
6319
|
-
out.push(sql.slice(t.start, t.digitStart));
|
|
6320
|
-
out.push(String(mapped));
|
|
6321
|
-
out.push(sql.slice(t.digitEnd, t.appEnd));
|
|
6322
|
-
cursor = t.fullEnd;
|
|
6323
|
-
}
|
|
6324
|
-
out.push(sql.slice(cursor));
|
|
6325
|
-
return {
|
|
6326
|
-
normalizedSql: out.join(""),
|
|
6327
|
-
hasProfileSyntax,
|
|
6328
|
-
appBindingByMappedApp
|
|
6329
|
-
};
|
|
6330
|
-
}
|
|
6331
|
-
function buildCacheContext(defaultProfile, appBindingByMappedApp) {
|
|
6332
|
-
if (appBindingByMappedApp.size === 0) return `default:${defaultProfile.toLowerCase()}`;
|
|
6333
|
-
const pairs = [...appBindingByMappedApp.entries()].sort((a, b) => a[0] - b[0]).map(([mappedAppId, b]) => `M${mappedAppId}=APP${b.appId}@${b.profile}`);
|
|
6334
|
-
return `apps:${pairs.join(",")}`;
|
|
6335
|
-
}
|
|
6336
|
-
function formatResolvedAppProfiles(sql, defaultProfile) {
|
|
6337
|
-
const parsed = normalizeSqlAppProfiles(sql, defaultProfile);
|
|
6338
|
-
if (parsed.appBindingByMappedApp.size === 0) return "(none)";
|
|
6339
|
-
return [...parsed.appBindingByMappedApp.values()].map((b) => `APP${b.appId}->${b.profile}`).join(", ");
|
|
6340
|
-
}
|
|
6341
6374
|
function resolveTokenValue(raw) {
|
|
6342
6375
|
if (raw.startsWith("env:")) {
|
|
6343
6376
|
const envKey = raw.slice(4);
|
|
@@ -6379,42 +6412,6 @@ function envAuth(name) {
|
|
|
6379
6412
|
if (v === "token" || v === "userpass" || v === "auto") return v;
|
|
6380
6413
|
return null;
|
|
6381
6414
|
}
|
|
6382
|
-
function isDmlType(type) {
|
|
6383
|
-
return type === "INSERT" || type === "INSERT_SELECT" || type === "UPDATE" || type === "DELETE" || type === "UPSERT" || type === "UPSERT_SELECT";
|
|
6384
|
-
}
|
|
6385
|
-
function hasWhereClause(stmt) {
|
|
6386
|
-
if (!stmt || typeof stmt !== "object") return false;
|
|
6387
|
-
const obj = stmt;
|
|
6388
|
-
return obj.where !== null && obj.where !== void 0;
|
|
6389
|
-
}
|
|
6390
|
-
function getStatementType(stmt) {
|
|
6391
|
-
if (!stmt || typeof stmt !== "object") return "UNKNOWN";
|
|
6392
|
-
const obj = stmt;
|
|
6393
|
-
return typeof obj.type === "string" ? obj.type : "UNKNOWN";
|
|
6394
|
-
}
|
|
6395
|
-
function isNoFromSelectStatement(stmt) {
|
|
6396
|
-
if (!stmt || typeof stmt !== "object") return false;
|
|
6397
|
-
const obj = stmt;
|
|
6398
|
-
return obj.type === "SELECT" && obj.from?.appId === 0 && obj.from?.cteName === "__NO_FROM__";
|
|
6399
|
-
}
|
|
6400
|
-
function getInsertValuesCount(stmt) {
|
|
6401
|
-
if (!stmt || typeof stmt !== "object") return null;
|
|
6402
|
-
const obj = stmt;
|
|
6403
|
-
if (obj.type !== "INSERT") return null;
|
|
6404
|
-
return Array.isArray(obj.values) ? obj.values.length : null;
|
|
6405
|
-
}
|
|
6406
|
-
function collectDmlTargetFields(stmt) {
|
|
6407
|
-
if (!stmt || typeof stmt !== "object") return [];
|
|
6408
|
-
const obj = stmt;
|
|
6409
|
-
if (!obj.type) return [];
|
|
6410
|
-
if (obj.type === "UPDATE") {
|
|
6411
|
-
return (obj.assignments ?? []).map((a) => a.field).filter((f) => Boolean(f));
|
|
6412
|
-
}
|
|
6413
|
-
if (obj.type === "INSERT" || obj.type === "INSERT_SELECT" || obj.type === "UPSERT" || obj.type === "UPSERT_SELECT") {
|
|
6414
|
-
return [...obj.fields ?? [], ...obj.keyFields ?? []];
|
|
6415
|
-
}
|
|
6416
|
-
return [];
|
|
6417
|
-
}
|
|
6418
6415
|
function normalizeUnique(values) {
|
|
6419
6416
|
const out = [];
|
|
6420
6417
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -6799,9 +6796,9 @@ function getHistoryPath() {
|
|
|
6799
6796
|
}
|
|
6800
6797
|
function loadHistory(maxItems = 200) {
|
|
6801
6798
|
const p = getHistoryPath();
|
|
6802
|
-
if (!(0,
|
|
6799
|
+
if (!(0, import_fs2.existsSync)(p)) return [];
|
|
6803
6800
|
try {
|
|
6804
|
-
const raw = (0,
|
|
6801
|
+
const raw = (0, import_fs2.readFileSync)(p, "utf-8");
|
|
6805
6802
|
const lines = raw.split(/\r?\n/).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
6806
6803
|
return lines.slice(-maxItems);
|
|
6807
6804
|
} catch {
|
|
@@ -6811,26 +6808,26 @@ function loadHistory(maxItems = 200) {
|
|
|
6811
6808
|
function appendHistory(sql) {
|
|
6812
6809
|
const p = getHistoryPath();
|
|
6813
6810
|
try {
|
|
6814
|
-
(0,
|
|
6811
|
+
(0, import_fs2.appendFileSync)(p, `${sql.replace(/\s+/g, " ").trim()}
|
|
6815
6812
|
`, "utf-8");
|
|
6816
6813
|
} catch {
|
|
6817
6814
|
}
|
|
6818
6815
|
}
|
|
6819
6816
|
function editBufferWithExternalEditor(current) {
|
|
6820
6817
|
const editor = process.env.KSQL_EDITOR ?? process.env.VISUAL ?? process.env.EDITOR ?? (process.platform === "win32" ? "notepad" : "vi");
|
|
6821
|
-
const dir = (0,
|
|
6818
|
+
const dir = (0, import_fs2.mkdtempSync)((0, import_path.join)((0, import_os.tmpdir)(), "ksql-edit-"));
|
|
6822
6819
|
const filePath = (0, import_path.join)(dir, "query.sql");
|
|
6823
6820
|
try {
|
|
6824
|
-
(0,
|
|
6821
|
+
(0, import_fs2.writeFileSync)(filePath, current, "utf-8");
|
|
6825
6822
|
const cmd = `"${editor}" "${filePath}"`;
|
|
6826
6823
|
const res = (0, import_child_process.spawnSync)(cmd, { stdio: "inherit", shell: true });
|
|
6827
6824
|
if (res.error) throw res.error;
|
|
6828
6825
|
if ((res.status ?? 0) !== 0) {
|
|
6829
6826
|
throw new Error(`Editor exited with code ${res.status ?? 1}`);
|
|
6830
6827
|
}
|
|
6831
|
-
return (0,
|
|
6828
|
+
return (0, import_fs2.readFileSync)(filePath, "utf-8").replace(/\r\n/g, "\n");
|
|
6832
6829
|
} finally {
|
|
6833
|
-
(0,
|
|
6830
|
+
(0, import_fs2.rmSync)(dir, { recursive: true, force: true });
|
|
6834
6831
|
}
|
|
6835
6832
|
}
|
|
6836
6833
|
async function runConsole(base) {
|
|
@@ -7012,11 +7009,11 @@ async function runConsole(base) {
|
|
|
7012
7009
|
}
|
|
7013
7010
|
try {
|
|
7014
7011
|
if (meta.append) {
|
|
7015
|
-
(0,
|
|
7012
|
+
(0, import_fs2.appendFileSync)(meta.path, lastOutput, "utf-8");
|
|
7016
7013
|
process.stdout.write(`saved (append): ${meta.path}
|
|
7017
7014
|
`);
|
|
7018
7015
|
} else {
|
|
7019
|
-
(0,
|
|
7016
|
+
(0, import_fs2.writeFileSync)(meta.path, lastOutput, "utf-8");
|
|
7020
7017
|
process.stdout.write(`saved: ${meta.path}
|
|
7021
7018
|
`);
|
|
7022
7019
|
}
|
|
@@ -7127,7 +7124,7 @@ async function run() {
|
|
|
7127
7124
|
let isDmlStatement = false;
|
|
7128
7125
|
if (args.diagRecordId === null) {
|
|
7129
7126
|
sql = args.executeSql;
|
|
7130
|
-
if (!sql && args.filePath) sql = (0,
|
|
7127
|
+
if (!sql && args.filePath) sql = (0, import_fs2.readFileSync)(args.filePath, "utf-8");
|
|
7131
7128
|
if (!sql || !sql.trim()) {
|
|
7132
7129
|
process.stderr.write("ArgumentError: SQL is empty.\n");
|
|
7133
7130
|
return 2;
|
|
@@ -7208,7 +7205,7 @@ async function run() {
|
|
|
7208
7205
|
return 2;
|
|
7209
7206
|
}
|
|
7210
7207
|
if (!allowDml) {
|
|
7211
|
-
process.stderr.write("ArgumentError: DML is disabled. Use --allow-dml to enable UPDATE/DELETE/INSERT/UPSERT.\n");
|
|
7208
|
+
process.stderr.write("ArgumentError: DML is disabled. Use --allow-dml to enable UPDATE/DELETE/INSERT/UPSERT/REORDER.\n");
|
|
7212
7209
|
return 2;
|
|
7213
7210
|
}
|
|
7214
7211
|
if ((stmtType === "UPDATE" || stmtType === "DELETE") && !hasWhere && !allowWithoutWhere) {
|
|
@@ -7484,7 +7481,7 @@ query=${label}`);
|
|
|
7484
7481
|
});
|
|
7485
7482
|
if (result.type !== "SELECT") {
|
|
7486
7483
|
const output2 = buildMutationOutput(result, format, noHeader, pretty);
|
|
7487
|
-
if (outputPath) (0,
|
|
7484
|
+
if (outputPath) (0, import_fs2.writeFileSync)(outputPath, `${output2}
|
|
7488
7485
|
`, "utf-8");
|
|
7489
7486
|
else if (output2) process.stdout.write(`${output2}
|
|
7490
7487
|
`);
|
|
@@ -7493,7 +7490,7 @@ query=${label}`);
|
|
|
7493
7490
|
return 0;
|
|
7494
7491
|
}
|
|
7495
7492
|
const output = buildOutput(result, format, noHeader, pretty, displayOptions);
|
|
7496
|
-
if (outputPath) (0,
|
|
7493
|
+
if (outputPath) (0, import_fs2.writeFileSync)(outputPath, `${output}
|
|
7497
7494
|
`, "utf-8");
|
|
7498
7495
|
else if (output) process.stdout.write(`${output}
|
|
7499
7496
|
`);
|