@rex0220/kintone-sql-tools 3.35.0 → 3.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cli/ksql.js +296 -266
- package/dist-engine/index.cjs +10 -9
- package/dist-engine/index.mjs +10 -9
- package/dist-engine/ksql-engine.umd.js +10 -9
- package/dist-engine/meta/bundle-baseline.json +10 -10
- package/dist-engine/meta/cjs.json +48 -10
- package/dist-engine/meta/esm.json +48 -10
- package/dist-engine/meta/umd.json +48 -10
- package/dist-engine/publicTypes.d.ts +2 -0
- package/dist-mcp/ksql-mcp.js +307 -270
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -18252,18 +18252,20 @@ function rememberSelectWhereCapability(stmt, capability) {
|
|
|
18252
18252
|
}
|
|
18253
18253
|
function isCountStarTotalCountEligible(stmt, whereCapability) {
|
|
18254
18254
|
if (whereCapability.capability !== "EXACT_PUSHDOWN") return false;
|
|
18255
|
-
|
|
18256
|
-
|
|
18257
|
-
|
|
18258
|
-
|
|
18259
|
-
|
|
18255
|
+
const countColumns = stmt.columns.filter(
|
|
18256
|
+
(column) => column.type === "AGGREGATE" && column.func === "COUNT" && !column.distinct && column.arg.type === "WILDCARD"
|
|
18257
|
+
);
|
|
18258
|
+
if (countColumns.length !== 1) return false;
|
|
18259
|
+
if (stmt.columns.some(
|
|
18260
|
+
(column) => column !== countColumns[0] && column.type !== "LITERAL_COL"
|
|
18261
|
+
)) return false;
|
|
18260
18262
|
return !stmt.distinct && normalizeGroupingSpec(stmt).type === "NONE" && stmt.having === null && stmt.joins.length === 0 && stmt.from.cteName === null && !stmt.from.subtableCode && stmt.limit === null && stmt.offset === null;
|
|
18261
18263
|
}
|
|
18262
18264
|
function isValidTotalCount(value) {
|
|
18263
18265
|
return typeof value === "string" && /^(?:0|[1-9]\d*)$/.test(value);
|
|
18264
18266
|
}
|
|
18265
18267
|
async function tryCountStarWithTotalCount(stmt, client) {
|
|
18266
|
-
const countColumn = stmt.columns
|
|
18268
|
+
const countColumn = stmt.columns.find((column) => column.type === "AGGREGATE");
|
|
18267
18269
|
if (countColumn?.type !== "AGGREGATE") return null;
|
|
18268
18270
|
const baseQuery = stmt.where === null ? "" : whereToKintone(stmt.where);
|
|
18269
18271
|
const response = await client.getRecords({
|
|
@@ -18274,11 +18276,15 @@ async function tryCountStarWithTotalCount(stmt, client) {
|
|
|
18274
18276
|
});
|
|
18275
18277
|
if (response.searchAborted) throw new SearchAbortedError();
|
|
18276
18278
|
if (!isValidTotalCount(response.totalCount)) return null;
|
|
18277
|
-
const
|
|
18279
|
+
const countKey = countColumn.alias ?? "COUNT(*)";
|
|
18280
|
+
const projected = project(
|
|
18281
|
+
[{ [countKey]: response.totalCount }],
|
|
18282
|
+
stmt.columns
|
|
18283
|
+
);
|
|
18278
18284
|
return {
|
|
18279
18285
|
type: "SELECT",
|
|
18280
|
-
rows:
|
|
18281
|
-
columns:
|
|
18286
|
+
rows: projected.rows,
|
|
18287
|
+
columns: projected.columns,
|
|
18282
18288
|
rowCount: 1,
|
|
18283
18289
|
warnings: []
|
|
18284
18290
|
};
|
|
@@ -18454,7 +18460,7 @@ async function executeUnion(stmt, client, options, cacheContext, captureColumnMe
|
|
|
18454
18460
|
captureColumnMeta,
|
|
18455
18461
|
forLibraryCapture
|
|
18456
18462
|
) : executeSelect(
|
|
18457
|
-
stmt.left,
|
|
18463
|
+
markCountTotalCountRoot(stmt.left),
|
|
18458
18464
|
client,
|
|
18459
18465
|
effectiveOptions,
|
|
18460
18466
|
cacheContext,
|
|
@@ -18463,7 +18469,7 @@ async function executeUnion(stmt, client, options, cacheContext, captureColumnMe
|
|
|
18463
18469
|
forLibraryCapture
|
|
18464
18470
|
),
|
|
18465
18471
|
executeSelect(
|
|
18466
|
-
stmt.right,
|
|
18472
|
+
markCountTotalCountRoot(stmt.right),
|
|
18467
18473
|
client,
|
|
18468
18474
|
effectiveOptions,
|
|
18469
18475
|
cacheContext,
|
|
@@ -23140,7 +23146,7 @@ function buildUnionPlan(stmt, capabilities, orderPlans, plainGroupByPlans) {
|
|
|
23140
23146
|
const lines = [];
|
|
23141
23147
|
selects.forEach((sel, i) => {
|
|
23142
23148
|
if (i > 0) lines.push("");
|
|
23143
|
-
lines.push(...buildSelectPlan(sel, `[union:${i + 1}]`, capabilities, orderPlans, plainGroupByPlans,
|
|
23149
|
+
lines.push(...buildSelectPlan(sel, `[union:${i + 1}]`, capabilities, orderPlans, plainGroupByPlans, true));
|
|
23144
23150
|
});
|
|
23145
23151
|
return lines;
|
|
23146
23152
|
}
|
|
@@ -23807,8 +23813,256 @@ function buildBatchEnvelope(batch, options = {}) {
|
|
|
23807
23813
|
};
|
|
23808
23814
|
}
|
|
23809
23815
|
|
|
23816
|
+
// src/core/logicalApps.ts
|
|
23817
|
+
var LOGICAL_APP_NAME_MAX_UTF16_UNITS = 64;
|
|
23818
|
+
var LOGICAL_APP_ASCII_LETTERS = "A-Za-z";
|
|
23819
|
+
var LOGICAL_APP_JAPANESE_RANGES = "\\u3040-\\u30FF\\u3400-\\u9FFF\\uF900-\\uFAFF\\uFF01-\\uFF60";
|
|
23820
|
+
var LOGICAL_APP_START_CLASS = `${LOGICAL_APP_ASCII_LETTERS}${LOGICAL_APP_JAPANESE_RANGES}`;
|
|
23821
|
+
var LOGICAL_APP_CONTINUE_CLASS = `${LOGICAL_APP_START_CLASS}0-9_`;
|
|
23822
|
+
var LOGICAL_APP_NAME_START_RE = new RegExp(`^[${LOGICAL_APP_START_CLASS}]$`, "u");
|
|
23823
|
+
var LOGICAL_APP_NAME_CONTINUE_RE = new RegExp(`^[${LOGICAL_APP_CONTINUE_CLASS}]$`, "u");
|
|
23824
|
+
var LOGICAL_APP_NAME_RE = new RegExp(
|
|
23825
|
+
`^[${LOGICAL_APP_START_CLASS}][${LOGICAL_APP_CONTINUE_CLASS}]{0,63}$`,
|
|
23826
|
+
"u"
|
|
23827
|
+
);
|
|
23828
|
+
function isLogicalAppNameStart(ch) {
|
|
23829
|
+
return LOGICAL_APP_NAME_START_RE.test(ch);
|
|
23830
|
+
}
|
|
23831
|
+
function isLogicalAppNameContinue(ch) {
|
|
23832
|
+
return LOGICAL_APP_NAME_CONTINUE_RE.test(ch);
|
|
23833
|
+
}
|
|
23834
|
+
function canonicalizeLogicalAppName(name) {
|
|
23835
|
+
const canonical = name.normalize("NFC").toUpperCase();
|
|
23836
|
+
if (!LOGICAL_APP_NAME_RE.test(canonical)) {
|
|
23837
|
+
throw new Error(
|
|
23838
|
+
`ArgumentError: logical app name "${name}" is invalid; expected 1-${LOGICAL_APP_NAME_MAX_UTF16_UNITS} UTF-16 units starting with an ASCII letter or supported Japanese character.`
|
|
23839
|
+
);
|
|
23840
|
+
}
|
|
23841
|
+
return canonical;
|
|
23842
|
+
}
|
|
23843
|
+
function isSqlIdentContinue(ch) {
|
|
23844
|
+
if (!ch) return false;
|
|
23845
|
+
const cp = ch.codePointAt(0);
|
|
23846
|
+
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;
|
|
23847
|
+
}
|
|
23848
|
+
function isProfileNameChar(ch) {
|
|
23849
|
+
if (!ch) return false;
|
|
23850
|
+
const cp = ch.codePointAt(0);
|
|
23851
|
+
return cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp >= 48 && cp <= 57 || ch === "_" || ch === "-" || ch === "." || ch === "$";
|
|
23852
|
+
}
|
|
23853
|
+
function tryParseAppProfileToken(sql, start) {
|
|
23854
|
+
const prev = start > 0 ? sql[start - 1] : "";
|
|
23855
|
+
if (isSqlIdentContinue(prev)) return null;
|
|
23856
|
+
let source;
|
|
23857
|
+
let appId;
|
|
23858
|
+
let logicalName;
|
|
23859
|
+
let referenceValueStart;
|
|
23860
|
+
let referenceValueEnd;
|
|
23861
|
+
let i;
|
|
23862
|
+
if (sql.slice(start, start + 5).toUpperCase() === "LAPP_") {
|
|
23863
|
+
source = "logical";
|
|
23864
|
+
i = start + 5;
|
|
23865
|
+
referenceValueStart = i;
|
|
23866
|
+
if (!isLogicalAppNameStart(sql[i] ?? "")) return null;
|
|
23867
|
+
i++;
|
|
23868
|
+
while (i < sql.length && isLogicalAppNameContinue(sql[i])) i++;
|
|
23869
|
+
referenceValueEnd = i;
|
|
23870
|
+
try {
|
|
23871
|
+
logicalName = canonicalizeLogicalAppName(sql.slice(referenceValueStart, referenceValueEnd));
|
|
23872
|
+
} catch {
|
|
23873
|
+
return null;
|
|
23874
|
+
}
|
|
23875
|
+
} else if (sql.slice(start, start + 3).toUpperCase() === "APP") {
|
|
23876
|
+
source = "physical";
|
|
23877
|
+
i = start + 3;
|
|
23878
|
+
referenceValueStart = i;
|
|
23879
|
+
while (i < sql.length && /[0-9]/.test(sql[i])) i++;
|
|
23880
|
+
referenceValueEnd = i;
|
|
23881
|
+
if (referenceValueEnd === referenceValueStart) return null;
|
|
23882
|
+
appId = Number(sql.slice(referenceValueStart, referenceValueEnd));
|
|
23883
|
+
} else {
|
|
23884
|
+
return null;
|
|
23885
|
+
}
|
|
23886
|
+
if (sql[i] === "$") {
|
|
23887
|
+
i++;
|
|
23888
|
+
const subStart = i;
|
|
23889
|
+
while (i < sql.length && isSqlIdentContinue(sql[i])) i++;
|
|
23890
|
+
if (i === subStart) return null;
|
|
23891
|
+
}
|
|
23892
|
+
const appEnd = i;
|
|
23893
|
+
let profile = null;
|
|
23894
|
+
if (sql[i] === "@") {
|
|
23895
|
+
i++;
|
|
23896
|
+
const pStart = i;
|
|
23897
|
+
while (i < sql.length && isProfileNameChar(sql[i])) i++;
|
|
23898
|
+
if (i === pStart) return null;
|
|
23899
|
+
profile = sql.slice(pStart, i);
|
|
23900
|
+
}
|
|
23901
|
+
const next = i < sql.length ? sql[i] : "";
|
|
23902
|
+
if (isSqlIdentContinue(next)) return null;
|
|
23903
|
+
const common = {
|
|
23904
|
+
profile,
|
|
23905
|
+
start,
|
|
23906
|
+
referenceValueStart,
|
|
23907
|
+
referenceValueEnd,
|
|
23908
|
+
appEnd,
|
|
23909
|
+
fullEnd: i
|
|
23910
|
+
};
|
|
23911
|
+
return source === "physical" ? { ...common, source, appId } : { ...common, source, logicalName };
|
|
23912
|
+
}
|
|
23913
|
+
function collectAppProfileTokens(sql) {
|
|
23914
|
+
const tokens = [];
|
|
23915
|
+
let i = 0;
|
|
23916
|
+
while (i < sql.length) {
|
|
23917
|
+
const ch = sql[i];
|
|
23918
|
+
if (ch === "'") {
|
|
23919
|
+
i++;
|
|
23920
|
+
while (i < sql.length) {
|
|
23921
|
+
if (sql[i] === "'") {
|
|
23922
|
+
i++;
|
|
23923
|
+
if (i < sql.length && sql[i] === "'") {
|
|
23924
|
+
i++;
|
|
23925
|
+
continue;
|
|
23926
|
+
}
|
|
23927
|
+
break;
|
|
23928
|
+
}
|
|
23929
|
+
i++;
|
|
23930
|
+
}
|
|
23931
|
+
continue;
|
|
23932
|
+
}
|
|
23933
|
+
if (ch === "`") {
|
|
23934
|
+
i++;
|
|
23935
|
+
while (i < sql.length && sql[i] !== "`") i++;
|
|
23936
|
+
if (i < sql.length) i++;
|
|
23937
|
+
continue;
|
|
23938
|
+
}
|
|
23939
|
+
if (ch === "-" && sql[i + 1] === "-") {
|
|
23940
|
+
i += 2;
|
|
23941
|
+
while (i < sql.length && sql[i] !== "\n") i++;
|
|
23942
|
+
continue;
|
|
23943
|
+
}
|
|
23944
|
+
if (ch === "/" && sql[i + 1] === "*") {
|
|
23945
|
+
i += 2;
|
|
23946
|
+
while (i < sql.length) {
|
|
23947
|
+
if (sql[i] === "*" && sql[i + 1] === "/") {
|
|
23948
|
+
i += 2;
|
|
23949
|
+
break;
|
|
23950
|
+
}
|
|
23951
|
+
i++;
|
|
23952
|
+
}
|
|
23953
|
+
continue;
|
|
23954
|
+
}
|
|
23955
|
+
const parsed = tryParseAppProfileToken(sql, i);
|
|
23956
|
+
if (!parsed) {
|
|
23957
|
+
i++;
|
|
23958
|
+
continue;
|
|
23959
|
+
}
|
|
23960
|
+
tokens.push(parsed);
|
|
23961
|
+
i = parsed.fullEnd;
|
|
23962
|
+
}
|
|
23963
|
+
return tokens;
|
|
23964
|
+
}
|
|
23965
|
+
function nextVirtualAppId(used) {
|
|
23966
|
+
let id = 9e8;
|
|
23967
|
+
while (used.has(id)) id++;
|
|
23968
|
+
used.add(id);
|
|
23969
|
+
return id;
|
|
23970
|
+
}
|
|
23971
|
+
function normalizeSqlAppProfiles(sql, defaultProfile = "dev", resolutionContext) {
|
|
23972
|
+
const tokens = collectAppProfileTokens(sql);
|
|
23973
|
+
const hasProfileSyntax = tokens.some((t) => t.profile !== null);
|
|
23974
|
+
const profilesByApp = /* @__PURE__ */ new Map();
|
|
23975
|
+
const normalizedProfile = (profile) => profile ?? defaultProfile;
|
|
23976
|
+
for (const t of tokens) {
|
|
23977
|
+
if (t.source !== "physical") continue;
|
|
23978
|
+
const p = normalizedProfile(t.profile);
|
|
23979
|
+
let set = profilesByApp.get(t.appId);
|
|
23980
|
+
if (!set) {
|
|
23981
|
+
set = /* @__PURE__ */ new Set();
|
|
23982
|
+
profilesByApp.set(t.appId, set);
|
|
23983
|
+
}
|
|
23984
|
+
set.add(p.toLowerCase());
|
|
23985
|
+
}
|
|
23986
|
+
const usedAppIds = new Set(tokens.filter((t) => t.source === "physical").map((t) => t.appId));
|
|
23987
|
+
const resolvedLogicalApps = /* @__PURE__ */ new Map();
|
|
23988
|
+
for (const t of tokens) {
|
|
23989
|
+
if (t.source !== "logical") continue;
|
|
23990
|
+
const pLower = normalizedProfile(t.profile).toLowerCase();
|
|
23991
|
+
const logicalKey = `logical:${t.logicalName}@${pLower}`;
|
|
23992
|
+
if (resolvedLogicalApps.has(logicalKey)) continue;
|
|
23993
|
+
if (!resolutionContext) {
|
|
23994
|
+
throw new Error(`ArgumentError: logical app LAPP_${t.logicalName}@${pLower} requires logicalApps configuration.`);
|
|
23995
|
+
}
|
|
23996
|
+
const resolvedAppId = resolutionContext.resolveLogicalApp(t.logicalName, pLower);
|
|
23997
|
+
resolvedLogicalApps.set(logicalKey, resolvedAppId);
|
|
23998
|
+
usedAppIds.add(resolvedAppId);
|
|
23999
|
+
}
|
|
24000
|
+
const pairToMapped = /* @__PURE__ */ new Map();
|
|
24001
|
+
const appBindingByMappedApp = /* @__PURE__ */ new Map();
|
|
24002
|
+
for (const [appId, pSet] of profilesByApp.entries()) {
|
|
24003
|
+
const profiles = [...pSet].sort();
|
|
24004
|
+
if (profiles.length <= 1) continue;
|
|
24005
|
+
for (const pLower of profiles) {
|
|
24006
|
+
const mapped = nextVirtualAppId(usedAppIds);
|
|
24007
|
+
pairToMapped.set(`physical:${appId}@${pLower}`, mapped);
|
|
24008
|
+
appBindingByMappedApp.set(mapped, {
|
|
24009
|
+
source: "physical",
|
|
24010
|
+
mappedAppId: mapped,
|
|
24011
|
+
appId,
|
|
24012
|
+
profile: pLower
|
|
24013
|
+
});
|
|
24014
|
+
}
|
|
24015
|
+
}
|
|
24016
|
+
const out = [];
|
|
24017
|
+
const rewriteSegments = [];
|
|
24018
|
+
let normalizedLength = 0;
|
|
24019
|
+
let cursor = 0;
|
|
24020
|
+
const appendSegment = (text, sourceStart, sourceEnd, bindingMappedAppId) => {
|
|
24021
|
+
if (!text && sourceStart === sourceEnd) return;
|
|
24022
|
+
const normalizedStart = normalizedLength;
|
|
24023
|
+
out.push(text);
|
|
24024
|
+
normalizedLength += text.length;
|
|
24025
|
+
rewriteSegments.push({
|
|
24026
|
+
normalizedStart,
|
|
24027
|
+
normalizedEnd: normalizedLength,
|
|
24028
|
+
sourceStart,
|
|
24029
|
+
sourceEnd,
|
|
24030
|
+
...bindingMappedAppId === void 0 ? {} : { bindingMappedAppId }
|
|
24031
|
+
});
|
|
24032
|
+
};
|
|
24033
|
+
for (const t of tokens) {
|
|
24034
|
+
const pLower = normalizedProfile(t.profile).toLowerCase();
|
|
24035
|
+
let binding;
|
|
24036
|
+
if (t.source === "physical") {
|
|
24037
|
+
const mapped = pairToMapped.get(`physical:${t.appId}@${pLower}`) ?? t.appId;
|
|
24038
|
+
binding = { source: "physical", mappedAppId: mapped, appId: t.appId, profile: pLower };
|
|
24039
|
+
} else {
|
|
24040
|
+
const logicalKey = `logical:${t.logicalName}@${pLower}`;
|
|
24041
|
+
let mapped = pairToMapped.get(logicalKey);
|
|
24042
|
+
if (mapped === void 0) {
|
|
24043
|
+
mapped = nextVirtualAppId(usedAppIds);
|
|
24044
|
+
pairToMapped.set(logicalKey, mapped);
|
|
24045
|
+
}
|
|
24046
|
+
binding = {
|
|
24047
|
+
source: "logical",
|
|
24048
|
+
logicalName: t.logicalName,
|
|
24049
|
+
mappedAppId: mapped,
|
|
24050
|
+
appId: resolvedLogicalApps.get(logicalKey),
|
|
24051
|
+
profile: pLower
|
|
24052
|
+
};
|
|
24053
|
+
}
|
|
24054
|
+
appBindingByMappedApp.set(binding.mappedAppId, binding);
|
|
24055
|
+
appendSegment(sql.slice(cursor, t.start), cursor, t.start);
|
|
24056
|
+
const subtableSuffix = sql.slice(t.referenceValueEnd, t.appEnd);
|
|
24057
|
+
const normalizedReference = t.source === "physical" ? `${sql.slice(t.start, t.referenceValueStart)}${binding.mappedAppId}${subtableSuffix}` : `APP${binding.mappedAppId}${subtableSuffix}`;
|
|
24058
|
+
appendSegment(normalizedReference, t.start, t.fullEnd, binding.mappedAppId);
|
|
24059
|
+
cursor = t.fullEnd;
|
|
24060
|
+
}
|
|
24061
|
+
appendSegment(sql.slice(cursor), cursor, sql.length);
|
|
24062
|
+
return { normalizedSql: out.join(""), hasProfileSyntax, appBindingByMappedApp, rewriteSegments };
|
|
24063
|
+
}
|
|
24064
|
+
|
|
23810
24065
|
// src/node/config.ts
|
|
23811
|
-
var LOGICAL_APP_NAME_RE = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
|
|
23812
24066
|
var PHYSICAL_APP_KEY_RE = /^APP\d+$/i;
|
|
23813
24067
|
var NUMERIC_APP_KEY_RE = /^\d+$/;
|
|
23814
24068
|
var LOGICAL_SQL_KEY_RE = /^LAPP_/i;
|
|
@@ -23828,12 +24082,14 @@ function normalizeLogicalApps(profileName, value) {
|
|
|
23828
24082
|
`logical app key "${rawName}" in profile "${profileName}" must be a logical name without APP, numeric, or LAPP_ syntax.`
|
|
23829
24083
|
);
|
|
23830
24084
|
}
|
|
23831
|
-
|
|
24085
|
+
let logicalName;
|
|
24086
|
+
try {
|
|
24087
|
+
logicalName = canonicalizeLogicalAppName(rawName);
|
|
24088
|
+
} catch {
|
|
23832
24089
|
throw argumentError(
|
|
23833
|
-
`logical app key "${rawName}" in profile "${profileName}" must match
|
|
24090
|
+
`logical app key "${rawName}" in profile "${profileName}" must match the logical app name rules.`
|
|
23834
24091
|
);
|
|
23835
24092
|
}
|
|
23836
|
-
const logicalName = rawName.toUpperCase();
|
|
23837
24093
|
if (Object.prototype.hasOwnProperty.call(normalized, logicalName)) {
|
|
23838
24094
|
throw argumentError(
|
|
23839
24095
|
`logical app name "${logicalName}" is duplicated after case normalization in profile "${profileName}".`
|
|
@@ -23906,11 +24162,13 @@ function createAppResolutionContext(config, defaultProfile) {
|
|
|
23906
24162
|
}
|
|
23907
24163
|
return {
|
|
23908
24164
|
resolveLogicalApp(name, profile) {
|
|
23909
|
-
if (!LOGICAL_APP_NAME_RE.test(name)) {
|
|
23910
|
-
throw argumentError(`logical app name "${name}" must match [A-Z][A-Z0-9_]{0,63}.`);
|
|
23911
|
-
}
|
|
23912
24165
|
const profileName = profile || defaultProfile;
|
|
23913
|
-
|
|
24166
|
+
let logicalName;
|
|
24167
|
+
try {
|
|
24168
|
+
logicalName = canonicalizeLogicalAppName(name);
|
|
24169
|
+
} catch {
|
|
24170
|
+
throw argumentError(`logical app name "${name}" must match the logical app name rules.`);
|
|
24171
|
+
}
|
|
23914
24172
|
const appId = requireProfile(profileName).logicalApps?.[logicalName];
|
|
23915
24173
|
if (appId === void 0) {
|
|
23916
24174
|
throw argumentError(`logical app LAPP_${logicalName}@${profileName} is not defined.`);
|
|
@@ -24967,250 +25225,6 @@ function extractAppIds(sql) {
|
|
|
24967
25225
|
}
|
|
24968
25226
|
return [...out];
|
|
24969
25227
|
}
|
|
24970
|
-
function isSqlIdentContinue(ch) {
|
|
24971
|
-
if (!ch) return false;
|
|
24972
|
-
const cp = ch.codePointAt(0);
|
|
24973
|
-
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;
|
|
24974
|
-
}
|
|
24975
|
-
function isProfileNameChar(ch) {
|
|
24976
|
-
if (!ch) return false;
|
|
24977
|
-
const cp = ch.codePointAt(0);
|
|
24978
|
-
return cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp >= 48 && cp <= 57 || ch === "_" || ch === "-" || ch === "." || ch === "$";
|
|
24979
|
-
}
|
|
24980
|
-
function isAsciiLogicalNameStart(ch) {
|
|
24981
|
-
return /^[A-Za-z]$/.test(ch);
|
|
24982
|
-
}
|
|
24983
|
-
function isAsciiLogicalNameContinue(ch) {
|
|
24984
|
-
return /^[A-Za-z0-9_]$/.test(ch);
|
|
24985
|
-
}
|
|
24986
|
-
function tryParseAppProfileToken(sql, start) {
|
|
24987
|
-
const prev = start > 0 ? sql[start - 1] : "";
|
|
24988
|
-
if (isSqlIdentContinue(prev)) return null;
|
|
24989
|
-
let source;
|
|
24990
|
-
let appId;
|
|
24991
|
-
let logicalName;
|
|
24992
|
-
let referenceValueStart;
|
|
24993
|
-
let referenceValueEnd;
|
|
24994
|
-
let i;
|
|
24995
|
-
if (sql.slice(start, start + 5).toUpperCase() === "LAPP_") {
|
|
24996
|
-
source = "logical";
|
|
24997
|
-
i = start + 5;
|
|
24998
|
-
referenceValueStart = i;
|
|
24999
|
-
if (!isAsciiLogicalNameStart(sql[i] ?? "")) return null;
|
|
25000
|
-
i++;
|
|
25001
|
-
while (i < sql.length && isAsciiLogicalNameContinue(sql[i])) i++;
|
|
25002
|
-
referenceValueEnd = i;
|
|
25003
|
-
if (referenceValueEnd - referenceValueStart > 64) return null;
|
|
25004
|
-
logicalName = sql.slice(referenceValueStart, referenceValueEnd).toUpperCase();
|
|
25005
|
-
} else if (sql.slice(start, start + 3).toUpperCase() === "APP") {
|
|
25006
|
-
source = "physical";
|
|
25007
|
-
i = start + 3;
|
|
25008
|
-
referenceValueStart = i;
|
|
25009
|
-
while (i < sql.length && /[0-9]/.test(sql[i])) i++;
|
|
25010
|
-
referenceValueEnd = i;
|
|
25011
|
-
if (referenceValueEnd === referenceValueStart) return null;
|
|
25012
|
-
appId = Number(sql.slice(referenceValueStart, referenceValueEnd));
|
|
25013
|
-
} else {
|
|
25014
|
-
return null;
|
|
25015
|
-
}
|
|
25016
|
-
if (sql[i] === "$") {
|
|
25017
|
-
i++;
|
|
25018
|
-
const subStart = i;
|
|
25019
|
-
while (i < sql.length && isSqlIdentContinue(sql[i])) i++;
|
|
25020
|
-
if (i === subStart) return null;
|
|
25021
|
-
}
|
|
25022
|
-
const appEnd = i;
|
|
25023
|
-
let profile = null;
|
|
25024
|
-
if (sql[i] === "@") {
|
|
25025
|
-
i++;
|
|
25026
|
-
const pStart = i;
|
|
25027
|
-
while (i < sql.length && isProfileNameChar(sql[i])) i++;
|
|
25028
|
-
if (i === pStart) return null;
|
|
25029
|
-
profile = sql.slice(pStart, i);
|
|
25030
|
-
}
|
|
25031
|
-
const next = i < sql.length ? sql[i] : "";
|
|
25032
|
-
if (isSqlIdentContinue(next)) return null;
|
|
25033
|
-
const common = {
|
|
25034
|
-
profile,
|
|
25035
|
-
start,
|
|
25036
|
-
referenceValueStart,
|
|
25037
|
-
referenceValueEnd,
|
|
25038
|
-
appEnd,
|
|
25039
|
-
fullEnd: i
|
|
25040
|
-
};
|
|
25041
|
-
return source === "physical" ? { ...common, source, appId } : { ...common, source, logicalName };
|
|
25042
|
-
}
|
|
25043
|
-
function collectAppProfileTokens(sql) {
|
|
25044
|
-
const tokens = [];
|
|
25045
|
-
let i = 0;
|
|
25046
|
-
while (i < sql.length) {
|
|
25047
|
-
const ch = sql[i];
|
|
25048
|
-
if (ch === "'") {
|
|
25049
|
-
i++;
|
|
25050
|
-
while (i < sql.length) {
|
|
25051
|
-
if (sql[i] === "'") {
|
|
25052
|
-
i++;
|
|
25053
|
-
if (i < sql.length && sql[i] === "'") {
|
|
25054
|
-
i++;
|
|
25055
|
-
continue;
|
|
25056
|
-
}
|
|
25057
|
-
break;
|
|
25058
|
-
}
|
|
25059
|
-
i++;
|
|
25060
|
-
}
|
|
25061
|
-
continue;
|
|
25062
|
-
}
|
|
25063
|
-
if (ch === "`") {
|
|
25064
|
-
i++;
|
|
25065
|
-
while (i < sql.length && sql[i] !== "`") i++;
|
|
25066
|
-
if (i < sql.length) i++;
|
|
25067
|
-
continue;
|
|
25068
|
-
}
|
|
25069
|
-
if (ch === "-" && sql[i + 1] === "-") {
|
|
25070
|
-
i += 2;
|
|
25071
|
-
while (i < sql.length && sql[i] !== "\n") i++;
|
|
25072
|
-
continue;
|
|
25073
|
-
}
|
|
25074
|
-
if (ch === "/" && sql[i + 1] === "*") {
|
|
25075
|
-
i += 2;
|
|
25076
|
-
while (i < sql.length) {
|
|
25077
|
-
if (sql[i] === "*" && sql[i + 1] === "/") {
|
|
25078
|
-
i += 2;
|
|
25079
|
-
break;
|
|
25080
|
-
}
|
|
25081
|
-
i++;
|
|
25082
|
-
}
|
|
25083
|
-
continue;
|
|
25084
|
-
}
|
|
25085
|
-
const parsed = tryParseAppProfileToken(sql, i);
|
|
25086
|
-
if (!parsed) {
|
|
25087
|
-
i++;
|
|
25088
|
-
continue;
|
|
25089
|
-
}
|
|
25090
|
-
tokens.push(parsed);
|
|
25091
|
-
i = parsed.fullEnd;
|
|
25092
|
-
}
|
|
25093
|
-
return tokens;
|
|
25094
|
-
}
|
|
25095
|
-
function nextVirtualAppId(used) {
|
|
25096
|
-
let id = 9e8;
|
|
25097
|
-
while (used.has(id)) id++;
|
|
25098
|
-
used.add(id);
|
|
25099
|
-
return id;
|
|
25100
|
-
}
|
|
25101
|
-
function normalizeSqlAppProfiles(sql, defaultProfile = "dev", resolutionContext) {
|
|
25102
|
-
const tokens = collectAppProfileTokens(sql);
|
|
25103
|
-
const hasProfileSyntax = tokens.some((t) => t.profile !== null);
|
|
25104
|
-
const profilesByApp = /* @__PURE__ */ new Map();
|
|
25105
|
-
const normalizedProfile = (profile) => profile ?? defaultProfile;
|
|
25106
|
-
for (const t of tokens) {
|
|
25107
|
-
if (t.source !== "physical") continue;
|
|
25108
|
-
const p = normalizedProfile(t.profile);
|
|
25109
|
-
let set = profilesByApp.get(t.appId);
|
|
25110
|
-
if (!set) {
|
|
25111
|
-
set = /* @__PURE__ */ new Set();
|
|
25112
|
-
profilesByApp.set(t.appId, set);
|
|
25113
|
-
}
|
|
25114
|
-
set.add(p.toLowerCase());
|
|
25115
|
-
}
|
|
25116
|
-
const usedAppIds = new Set(
|
|
25117
|
-
tokens.filter((t) => t.source === "physical").map((t) => t.appId)
|
|
25118
|
-
);
|
|
25119
|
-
const resolvedLogicalApps = /* @__PURE__ */ new Map();
|
|
25120
|
-
for (const t of tokens) {
|
|
25121
|
-
if (t.source !== "logical") continue;
|
|
25122
|
-
const pLower = normalizedProfile(t.profile).toLowerCase();
|
|
25123
|
-
const logicalKey = `logical:${t.logicalName}@${pLower}`;
|
|
25124
|
-
if (resolvedLogicalApps.has(logicalKey)) continue;
|
|
25125
|
-
if (!resolutionContext) {
|
|
25126
|
-
throw new Error(
|
|
25127
|
-
`ArgumentError: logical app LAPP_${t.logicalName}@${pLower} requires logicalApps configuration.`
|
|
25128
|
-
);
|
|
25129
|
-
}
|
|
25130
|
-
const resolvedAppId = resolutionContext.resolveLogicalApp(t.logicalName, pLower);
|
|
25131
|
-
resolvedLogicalApps.set(logicalKey, resolvedAppId);
|
|
25132
|
-
usedAppIds.add(resolvedAppId);
|
|
25133
|
-
}
|
|
25134
|
-
const pairToMapped = /* @__PURE__ */ new Map();
|
|
25135
|
-
const appBindingByMappedApp = /* @__PURE__ */ new Map();
|
|
25136
|
-
for (const [appId, pSet] of profilesByApp.entries()) {
|
|
25137
|
-
const profiles = [...pSet].sort();
|
|
25138
|
-
if (profiles.length <= 1) continue;
|
|
25139
|
-
for (const pLower of profiles) {
|
|
25140
|
-
const mapped = nextVirtualAppId(usedAppIds);
|
|
25141
|
-
pairToMapped.set(`physical:${appId}@${pLower}`, mapped);
|
|
25142
|
-
appBindingByMappedApp.set(mapped, {
|
|
25143
|
-
source: "physical",
|
|
25144
|
-
mappedAppId: mapped,
|
|
25145
|
-
appId,
|
|
25146
|
-
profile: pLower
|
|
25147
|
-
});
|
|
25148
|
-
}
|
|
25149
|
-
}
|
|
25150
|
-
const out = [];
|
|
25151
|
-
const rewriteSegments = [];
|
|
25152
|
-
let normalizedLength = 0;
|
|
25153
|
-
let cursor = 0;
|
|
25154
|
-
const appendSegment = (text, sourceStart, sourceEnd, bindingMappedAppId) => {
|
|
25155
|
-
if (!text && sourceStart === sourceEnd) return;
|
|
25156
|
-
const normalizedStart = normalizedLength;
|
|
25157
|
-
out.push(text);
|
|
25158
|
-
normalizedLength += text.length;
|
|
25159
|
-
rewriteSegments.push({
|
|
25160
|
-
normalizedStart,
|
|
25161
|
-
normalizedEnd: normalizedLength,
|
|
25162
|
-
sourceStart,
|
|
25163
|
-
sourceEnd,
|
|
25164
|
-
...bindingMappedAppId === void 0 ? {} : { bindingMappedAppId }
|
|
25165
|
-
});
|
|
25166
|
-
};
|
|
25167
|
-
for (const t of tokens) {
|
|
25168
|
-
const p = normalizedProfile(t.profile);
|
|
25169
|
-
const pLower = p.toLowerCase();
|
|
25170
|
-
let binding;
|
|
25171
|
-
if (t.source === "physical") {
|
|
25172
|
-
const mapped = pairToMapped.get(`physical:${t.appId}@${pLower}`) ?? t.appId;
|
|
25173
|
-
binding = {
|
|
25174
|
-
source: "physical",
|
|
25175
|
-
mappedAppId: mapped,
|
|
25176
|
-
appId: t.appId,
|
|
25177
|
-
profile: pLower
|
|
25178
|
-
};
|
|
25179
|
-
} else {
|
|
25180
|
-
const logicalKey = `logical:${t.logicalName}@${pLower}`;
|
|
25181
|
-
let mapped = pairToMapped.get(logicalKey);
|
|
25182
|
-
if (mapped === void 0) {
|
|
25183
|
-
mapped = nextVirtualAppId(usedAppIds);
|
|
25184
|
-
pairToMapped.set(logicalKey, mapped);
|
|
25185
|
-
}
|
|
25186
|
-
binding = {
|
|
25187
|
-
source: "logical",
|
|
25188
|
-
logicalName: t.logicalName,
|
|
25189
|
-
mappedAppId: mapped,
|
|
25190
|
-
appId: resolvedLogicalApps.get(logicalKey),
|
|
25191
|
-
profile: pLower
|
|
25192
|
-
};
|
|
25193
|
-
}
|
|
25194
|
-
appBindingByMappedApp.set(binding.mappedAppId, binding);
|
|
25195
|
-
appendSegment(sql.slice(cursor, t.start), cursor, t.start);
|
|
25196
|
-
const subtableSuffix = sql.slice(t.referenceValueEnd, t.appEnd);
|
|
25197
|
-
const normalizedReference = t.source === "physical" ? `${sql.slice(t.start, t.referenceValueStart)}${binding.mappedAppId}${subtableSuffix}` : `APP${binding.mappedAppId}${subtableSuffix}`;
|
|
25198
|
-
appendSegment(
|
|
25199
|
-
normalizedReference,
|
|
25200
|
-
t.start,
|
|
25201
|
-
t.fullEnd,
|
|
25202
|
-
binding.mappedAppId
|
|
25203
|
-
);
|
|
25204
|
-
cursor = t.fullEnd;
|
|
25205
|
-
}
|
|
25206
|
-
appendSegment(sql.slice(cursor), cursor, sql.length);
|
|
25207
|
-
return {
|
|
25208
|
-
normalizedSql: out.join(""),
|
|
25209
|
-
hasProfileSyntax,
|
|
25210
|
-
appBindingByMappedApp,
|
|
25211
|
-
rewriteSegments
|
|
25212
|
-
};
|
|
25213
|
-
}
|
|
25214
25228
|
function buildCacheContext(defaultProfile, appBindingByMappedApp) {
|
|
25215
25229
|
if (appBindingByMappedApp.size === 0) return `default:${defaultProfile.toLowerCase()}`;
|
|
25216
25230
|
const pairs = [...appBindingByMappedApp.entries()].sort((a, b) => a[0] - b[0]).map(([mappedAppId, b]) => b.source === "logical" ? `M${mappedAppId}=logical:${b.logicalName}:APP${b.appId}@${b.profile}` : `M${mappedAppId}=physical:APP${b.appId}@${b.profile}`);
|
|
@@ -26851,6 +26865,7 @@ async function run() {
|
|
|
26851
26865
|
let containsApplyStatement = false;
|
|
26852
26866
|
let containsApplyMutation = false;
|
|
26853
26867
|
let dryRunNeedsMetadata = false;
|
|
26868
|
+
let parsedStatements = [];
|
|
26854
26869
|
if (args.diagRecordId === null) {
|
|
26855
26870
|
sql = args.executeSql;
|
|
26856
26871
|
if (!sql && args.filePath) sql = (0, import_fs2.readFileSync)(args.filePath, "utf-8");
|
|
@@ -26878,6 +26893,7 @@ async function run() {
|
|
|
26878
26893
|
const importEnabled = Object.keys(args.importCsv).length > 0 || Object.keys(args.importJson).length > 0;
|
|
26879
26894
|
try {
|
|
26880
26895
|
const statements = parseSqlStatements(sql, { import: importEnabled });
|
|
26896
|
+
parsedStatements = statements;
|
|
26881
26897
|
const hasApply = (statement) => statement.type === "UPDATE" || statement.type === "INSERT" ? (statement.applyBlocks?.length ?? 0) > 0 : statement.type === "UPSERT" ? (statement.onInsertApplyBlocks?.length ?? 0) > 0 || (statement.onUpdateApplyBlocks?.length ?? 0) > 0 : false;
|
|
26882
26898
|
containsApplyStatement = statements.some(hasApply);
|
|
26883
26899
|
containsApplyMutation = statements.some((statement) => {
|
|
@@ -27354,7 +27370,7 @@ query=${label}`);
|
|
|
27354
27370
|
return 2;
|
|
27355
27371
|
}
|
|
27356
27372
|
}
|
|
27357
|
-
|
|
27373
|
+
let batchResult = await executeBatch(sql, client, {
|
|
27358
27374
|
maxRecords,
|
|
27359
27375
|
fetchParallel,
|
|
27360
27376
|
onLimitReached: effectiveOnLimit,
|
|
@@ -27390,6 +27406,20 @@ query=${label}`);
|
|
|
27390
27406
|
return true;
|
|
27391
27407
|
} : void 0
|
|
27392
27408
|
});
|
|
27409
|
+
if (sqlDiagnosticContext) {
|
|
27410
|
+
batchResult = {
|
|
27411
|
+
...batchResult,
|
|
27412
|
+
statements: batchResult.statements.map(
|
|
27413
|
+
(statementResult, index) => parsedStatements[index]?.type === "EXPLAIN" && statementResult.result ? {
|
|
27414
|
+
...statementResult,
|
|
27415
|
+
result: restoreSqlDiagnosticValue(
|
|
27416
|
+
statementResult.result,
|
|
27417
|
+
sqlDiagnosticContext.appBindingByMappedApp
|
|
27418
|
+
)
|
|
27419
|
+
} : statementResult
|
|
27420
|
+
)
|
|
27421
|
+
};
|
|
27422
|
+
}
|
|
27393
27423
|
return writeBatchOutput(batchResult, { format, noHeader, pretty, displayOptions, outputPath, quiet });
|
|
27394
27424
|
}
|
|
27395
27425
|
let result = args.dryRun ? await execute(`EXPLAIN ${sql}`, client, {
|
|
@@ -27417,7 +27447,7 @@ query=${label}`);
|
|
|
27417
27447
|
} : {},
|
|
27418
27448
|
...containsApplyMutation ? { allowApplyMutation: true } : {}
|
|
27419
27449
|
});
|
|
27420
|
-
if (args.dryRun && sqlDiagnosticContext) {
|
|
27450
|
+
if ((args.dryRun || parsedStatements[0]?.type === "EXPLAIN") && sqlDiagnosticContext) {
|
|
27421
27451
|
result = restoreSqlDiagnosticValue(result, sqlDiagnosticContext.appBindingByMappedApp);
|
|
27422
27452
|
}
|
|
27423
27453
|
if (result.type === "ASSERT") {
|