@rex0220/kintone-sql-tools 3.61.0 → 3.62.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 +2046 -1995
- package/dist-engine/index.cjs +12 -12
- package/dist-engine/index.mjs +12 -12
- package/dist-engine/ksql-engine.umd.js +12 -12
- package/dist-engine/meta/bundle-baseline.json +10 -10
- package/dist-engine/meta/cjs.json +95 -71
- package/dist-engine/meta/esm.json +95 -71
- package/dist-engine/meta/umd.json +95 -71
- package/dist-mcp/ksql-mcp.js +3813 -3780
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -7024,357 +7024,1066 @@ function stripCteAliasFromFieldValue(value, alias) {
|
|
|
7024
7024
|
return value;
|
|
7025
7025
|
}
|
|
7026
7026
|
|
|
7027
|
-
// src/core/
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
if (where.op !== "AND") return null;
|
|
7043
|
-
{
|
|
7044
|
-
const left = extractAndLeaves(where.left, accept);
|
|
7045
|
-
const right = extractAndLeaves(where.right, accept);
|
|
7046
|
-
if (left && right) return { ...where, left, right };
|
|
7047
|
-
return left ?? right ?? null;
|
|
7048
|
-
}
|
|
7049
|
-
case "GROUP":
|
|
7050
|
-
return extractAndLeaves(where.expr, accept);
|
|
7051
|
-
case "NULL_CHECK":
|
|
7052
|
-
case "NOT":
|
|
7053
|
-
case "EXISTS":
|
|
7054
|
-
case "BOOLEAN":
|
|
7055
|
-
return null;
|
|
7056
|
-
}
|
|
7057
|
-
}
|
|
7058
|
-
function isSafeComparison(expr, options) {
|
|
7059
|
-
if (isKlikeComparison(expr, options)) return true;
|
|
7060
|
-
if (isSafeIdComparison(expr, options)) return true;
|
|
7061
|
-
if (isNumericCandidate(expr, options)) {
|
|
7062
|
-
return options.fieldTypes?.get(expr.left.field) === "NUMBER";
|
|
7063
|
-
}
|
|
7064
|
-
return isSelectionInComparison(expr, options);
|
|
7065
|
-
}
|
|
7066
|
-
function isKlikeComparison(expr, options) {
|
|
7067
|
-
if (options.allowKlike === false) return false;
|
|
7068
|
-
if (expr.op !== "KLIKE" && expr.op !== "NOT_KLIKE") return false;
|
|
7069
|
-
if (expr.left.type !== "FIELD" || !isTargetField(expr.left, options)) return false;
|
|
7070
|
-
return expr.right.type === "STRING" || options.allowUnresolvedKlikeVariables === true && expr.right.type === "VARIABLE";
|
|
7071
|
-
}
|
|
7072
|
-
var SELECTION_IN_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
7027
|
+
// src/core/fieldSemantics.ts
|
|
7028
|
+
var STRING_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
7029
|
+
"SINGLE_LINE_TEXT",
|
|
7030
|
+
"MULTI_LINE_TEXT",
|
|
7031
|
+
"RICH_TEXT",
|
|
7032
|
+
"LINK",
|
|
7033
|
+
"DATE",
|
|
7034
|
+
"TIME",
|
|
7035
|
+
"DATETIME",
|
|
7036
|
+
"CREATED_TIME",
|
|
7037
|
+
"UPDATED_TIME",
|
|
7038
|
+
"CREATOR",
|
|
7039
|
+
"MODIFIER"
|
|
7040
|
+
]);
|
|
7041
|
+
var OPTION_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
7073
7042
|
"DROP_DOWN",
|
|
7074
7043
|
"RADIO_BUTTON",
|
|
7075
7044
|
"CHECK_BOX",
|
|
7076
7045
|
"MULTI_SELECT",
|
|
7077
7046
|
"STATUS"
|
|
7078
7047
|
]);
|
|
7079
|
-
function
|
|
7080
|
-
|
|
7081
|
-
if (
|
|
7082
|
-
|
|
7083
|
-
if (fieldType ===
|
|
7084
|
-
|
|
7085
|
-
if (
|
|
7086
|
-
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
}
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
return expr.op === "=" || expr.op === ">" || expr.op === "<" || expr.op === ">=" || expr.op === "<=";
|
|
7094
|
-
}
|
|
7095
|
-
function isTargetIdField(field, options) {
|
|
7096
|
-
if (field.type !== "FIELD" || field.field !== "$id") return false;
|
|
7097
|
-
const targetAlias = options.tableAlias ?? null;
|
|
7098
|
-
if (field.tableAlias === targetAlias) return true;
|
|
7099
|
-
return options.allowUnqualifiedFields === true && field.tableAlias === null;
|
|
7100
|
-
}
|
|
7101
|
-
function isNumericCandidate(expr, options) {
|
|
7102
|
-
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
7103
|
-
if (!isTargetField(expr.left, options)) return false;
|
|
7104
|
-
if (expr.right.type !== "NUMBER") return false;
|
|
7105
|
-
if (expr.op === "=") return true;
|
|
7106
|
-
return (expr.op === "<" || expr.op === ">") && /^[+-]?\d+$/.test(numberLiteralText(expr.right)) && Number.isSafeInteger(expr.right.value);
|
|
7107
|
-
}
|
|
7108
|
-
function isSelectionInCandidate(expr, options) {
|
|
7109
|
-
if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
|
|
7110
|
-
if (!isTargetField(expr.left, options)) return false;
|
|
7111
|
-
if (expr.op !== "IN" && expr.op !== "NOT_IN") return false;
|
|
7112
|
-
if (expr.right.type !== "IN_LIST" || expr.right.values.length === 0) return false;
|
|
7113
|
-
return expr.right.values.every((value) => value.type === "STRING" && value.value !== "");
|
|
7114
|
-
}
|
|
7115
|
-
function isTargetField(field, options) {
|
|
7116
|
-
const targetAlias = options.tableAlias ?? null;
|
|
7117
|
-
if (field.tableAlias === targetAlias) return true;
|
|
7118
|
-
return options.allowUnqualifiedFields === true && field.tableAlias === null;
|
|
7119
|
-
}
|
|
7120
|
-
|
|
7121
|
-
// src/core/optimization/klikePushdownPlan.ts
|
|
7122
|
-
function buildSingleTableKlikePushdownPlan(where, options = {}) {
|
|
7123
|
-
const condition = where !== null && options.extractCondition !== false ? extractSafePushdownLeaves(where, options) : null;
|
|
7124
|
-
const appliedKlikes = /* @__PURE__ */ new Set();
|
|
7125
|
-
collectKlikes(condition, appliedKlikes);
|
|
7126
|
-
const allKlikes = /* @__PURE__ */ new Set();
|
|
7127
|
-
collectKlikes(where, allKlikes);
|
|
7128
|
-
return { condition, appliedKlikes, allKlikes: [...allKlikes] };
|
|
7129
|
-
}
|
|
7130
|
-
function buildKlikePushdownPlan(stmt, options = {}) {
|
|
7131
|
-
const joinsAreSafeForKlike = stmt.joins.every((join2) => join2.type === "INNER");
|
|
7132
|
-
const common = {
|
|
7133
|
-
allowKlike: joinsAreSafeForKlike,
|
|
7134
|
-
allowUnresolvedKlikeVariables: options.allowUnresolvedVariables
|
|
7135
|
-
};
|
|
7136
|
-
const mainIsPhysical = !stmt.from.subtableCode && stmt.from.cteName === null;
|
|
7137
|
-
const mainHasUsableAlias = stmt.joins.length === 0 || stmt.from.alias !== null;
|
|
7138
|
-
const mainPlan = buildSingleTableKlikePushdownPlan(stmt.where, {
|
|
7139
|
-
...common,
|
|
7140
|
-
extractCondition: mainIsPhysical && mainHasUsableAlias,
|
|
7141
|
-
tableAlias: stmt.from.alias ?? void 0,
|
|
7142
|
-
allowUnqualifiedFields: stmt.joins.length === 0,
|
|
7143
|
-
fieldTypes: options.fieldTypesByApp?.get(stmt.from.appId),
|
|
7144
|
-
fieldOptions: options.fieldOptionsByApp?.get(stmt.from.appId)
|
|
7145
|
-
});
|
|
7146
|
-
const mainCondition = mainPlan.condition;
|
|
7147
|
-
const joinConditions = /* @__PURE__ */ new Map();
|
|
7148
|
-
if (stmt.where !== null) {
|
|
7149
|
-
for (const join2 of stmt.joins) {
|
|
7150
|
-
if (!join2.table.alias || join2.table.subtableCode || join2.table.cteName !== null) continue;
|
|
7151
|
-
const condition = extractSafePushdownLeaves(stmt.where, {
|
|
7152
|
-
...common,
|
|
7153
|
-
tableAlias: join2.table.alias,
|
|
7154
|
-
fieldTypes: options.fieldTypesByApp?.get(join2.table.appId),
|
|
7155
|
-
fieldOptions: options.fieldOptionsByApp?.get(join2.table.appId)
|
|
7156
|
-
});
|
|
7157
|
-
if (condition !== null) joinConditions.set(join2.table.alias, condition);
|
|
7158
|
-
}
|
|
7048
|
+
function resolveFieldSemantics(source) {
|
|
7049
|
+
let compareMode;
|
|
7050
|
+
if (source.fieldType === "RECORD_NUMBER" || source.fieldType === "__ID__") {
|
|
7051
|
+
compareMode = "recordNumber";
|
|
7052
|
+
} else if (source.fieldType === "NUMBER") {
|
|
7053
|
+
compareMode = "number";
|
|
7054
|
+
} else if (source.fieldType === "CALC") {
|
|
7055
|
+
compareMode = source.sortKind === "number" ? "number" : "string";
|
|
7056
|
+
} else if (OPTION_FIELD_TYPES.has(source.fieldType)) {
|
|
7057
|
+
compareMode = "option";
|
|
7058
|
+
} else if (STRING_FIELD_TYPES.has(source.fieldType)) {
|
|
7059
|
+
compareMode = "string";
|
|
7060
|
+
} else {
|
|
7061
|
+
compareMode = "unsupported";
|
|
7159
7062
|
}
|
|
7160
|
-
const
|
|
7161
|
-
for (const condition of joinConditions.values()) collectKlikes(condition, appliedKlikes);
|
|
7063
|
+
const optionOrder = source.optionOrder ? new Map(Object.entries(source.optionOrder)) : void 0;
|
|
7162
7064
|
return {
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7065
|
+
fieldType: source.fieldType,
|
|
7066
|
+
compareMode,
|
|
7067
|
+
inSubtable: source.inSubtable === true,
|
|
7068
|
+
requiresCollectionOperators: source.inSubtable === true || source.requiresCollectionOperators === true,
|
|
7069
|
+
...optionOrder && optionOrder.size > 0 ? { optionOrder } : {}
|
|
7167
7070
|
};
|
|
7168
7071
|
}
|
|
7169
|
-
function
|
|
7170
|
-
return
|
|
7171
|
-
}
|
|
7172
|
-
function collectKlikes(where, out) {
|
|
7173
|
-
if (where === null) return;
|
|
7174
|
-
if (isKlike(where)) {
|
|
7175
|
-
out.add(where);
|
|
7176
|
-
return;
|
|
7177
|
-
}
|
|
7178
|
-
switch (where.type) {
|
|
7179
|
-
case "LOGICAL":
|
|
7180
|
-
collectKlikes(where.left, out);
|
|
7181
|
-
collectKlikes(where.right, out);
|
|
7182
|
-
return;
|
|
7183
|
-
case "NOT":
|
|
7184
|
-
case "GROUP":
|
|
7185
|
-
collectKlikes(where.expr, out);
|
|
7186
|
-
return;
|
|
7187
|
-
case "BINARY":
|
|
7188
|
-
case "NULL_CHECK":
|
|
7189
|
-
case "EXISTS":
|
|
7190
|
-
case "BOOLEAN":
|
|
7191
|
-
return;
|
|
7192
|
-
}
|
|
7072
|
+
function syntheticSemantics(compareMode, fieldType = compareMode === "number" ? "KSQL_NUMBER" : "KSQL_STRING") {
|
|
7073
|
+
return { fieldType, compareMode, inSubtable: false, requiresCollectionOperators: false };
|
|
7193
7074
|
}
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
function buildRelativeDateFullScanExactPlan(input) {
|
|
7197
|
-
const {
|
|
7198
|
-
select,
|
|
7199
|
-
selectMode,
|
|
7200
|
-
capability,
|
|
7201
|
-
context,
|
|
7202
|
-
serializedWholeWhere,
|
|
7203
|
-
relativeFunctionNames
|
|
7204
|
-
} = input;
|
|
7205
|
-
if (select.where === null) return null;
|
|
7206
|
-
if (select.from.appId <= 0 || select.from.cteName !== null) return null;
|
|
7207
|
-
if (select.from.subtableCode) return null;
|
|
7208
|
-
if (select.joins.length > 0) return null;
|
|
7209
|
-
if (!context.allowFullScanExact) return null;
|
|
7210
|
-
if (select.orderMode === "KINTONE_NATIVE") return null;
|
|
7211
|
-
const hasCanonicalOrder2 = select.orderMode === "CANONICAL" && select.orderBy.length > 0;
|
|
7212
|
-
if (selectMode !== "FULL_SCAN" && !hasCanonicalOrder2) return null;
|
|
7213
|
-
if (capability.capability !== "EXACT_PUSHDOWN") return null;
|
|
7214
|
-
const occurrences = serverOnlyFunctionOccurrencesInWhere(select.where);
|
|
7215
|
-
if (occurrences.length === 0) return null;
|
|
7216
|
-
if (!sameOccurrenceList(occurrences, relativeFunctionNames)) return null;
|
|
7217
|
-
if (serializedWholeWhere === null || !serializedMultisetContains(serializedWholeWhere, occurrences)) {
|
|
7218
|
-
return null;
|
|
7219
|
-
}
|
|
7220
|
-
const prefilterPlan = {
|
|
7221
|
-
prefilterWhere: select.where,
|
|
7222
|
-
residualWhere: null,
|
|
7223
|
-
exactRelativeLeaves: collectExactServerFunctionLeaves(select.where),
|
|
7224
|
-
relativeFunctionNames: new Set(occurrences),
|
|
7225
|
-
appliedKlikes: /* @__PURE__ */ new Set(),
|
|
7226
|
-
capability: capability.capability,
|
|
7227
|
-
reasons: capability.reasons
|
|
7228
|
-
};
|
|
7229
|
-
const plan = {
|
|
7230
|
-
allowForm: "FULL_SCAN_EXACT",
|
|
7231
|
-
clientWhereEvaluation: false,
|
|
7232
|
-
serializedWholeWhere,
|
|
7233
|
-
prefilterPlan
|
|
7234
|
-
};
|
|
7235
|
-
assertRelativeDateFullScanExactPlan(plan, input, occurrences);
|
|
7236
|
-
return plan;
|
|
7075
|
+
function withFieldSemanticSource(semantics, appId, fieldCode) {
|
|
7076
|
+
return { ...semantics, source: { appId, fieldCode } };
|
|
7237
7077
|
}
|
|
7238
|
-
function
|
|
7239
|
-
if (
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
if (
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
if (
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
throw new Error("FULL_SCAN_EXACT invariant: whole WHERE identity");
|
|
7250
|
-
}
|
|
7251
|
-
if (plan.prefilterPlan.residualWhere !== null) {
|
|
7252
|
-
throw new Error("FULL_SCAN_EXACT invariant: residualWhere");
|
|
7253
|
-
}
|
|
7254
|
-
if (plan.prefilterPlan.capability !== "EXACT_PUSHDOWN") {
|
|
7255
|
-
throw new Error("FULL_SCAN_EXACT invariant: transport capability");
|
|
7256
|
-
}
|
|
7257
|
-
if (!serializedMultisetContains(plan.serializedWholeWhere, occurrences)) {
|
|
7258
|
-
throw new Error("FULL_SCAN_EXACT invariant: relative occurrence serialization");
|
|
7078
|
+
function fieldSemanticsEqual(left, right) {
|
|
7079
|
+
if (left === right) return true;
|
|
7080
|
+
if (!left || !right) return false;
|
|
7081
|
+
if (left.fieldType !== right.fieldType || left.compareMode !== right.compareMode || left.inSubtable !== right.inSubtable || left.requiresCollectionOperators !== right.requiresCollectionOperators) return false;
|
|
7082
|
+
if (left.source?.appId !== right.source?.appId || left.source?.fieldCode !== right.source?.fieldCode) return false;
|
|
7083
|
+
const a = left.optionOrder;
|
|
7084
|
+
const b = right.optionOrder;
|
|
7085
|
+
if (a === b) return true;
|
|
7086
|
+
if (!a || !b || a.size !== b.size) return false;
|
|
7087
|
+
for (const [key, value] of a) {
|
|
7088
|
+
if (b.get(key) !== value) return false;
|
|
7259
7089
|
}
|
|
7090
|
+
return true;
|
|
7260
7091
|
}
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7092
|
+
|
|
7093
|
+
// src/core/optimization/whereCapability.ts
|
|
7094
|
+
var RANGE_AND_EQUALITY = ["=", "!=", ">", "<", ">=", "<="];
|
|
7095
|
+
var RELATIVE_DATE_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
7096
|
+
"DATE",
|
|
7097
|
+
"DATETIME",
|
|
7098
|
+
"CREATED_TIME",
|
|
7099
|
+
"UPDATED_TIME"
|
|
7100
|
+
]);
|
|
7101
|
+
var RELATIVE_DATE_OPERATORS = new Set(RANGE_AND_EQUALITY);
|
|
7102
|
+
var LEGACY_KINTONE_FUNCTION_FIELD_TYPES = /* @__PURE__ */ new Map([
|
|
7103
|
+
["TODAY", /* @__PURE__ */ new Set(["DATE", "DATETIME", "CREATED_TIME", "UPDATED_TIME"])],
|
|
7104
|
+
["NOW", /* @__PURE__ */ new Set(["DATETIME", "CREATED_TIME", "UPDATED_TIME"])],
|
|
7105
|
+
["LOGINUSER", /* @__PURE__ */ new Set(["CREATOR", "MODIFIER", "USER_SELECT"])],
|
|
7106
|
+
["PRIMARY_ORGANIZATION", /* @__PURE__ */ new Set(["ORGANIZATION_SELECT"])]
|
|
7107
|
+
]);
|
|
7108
|
+
var LEGACY_KINTONE_FUNCTION_OPERATORS = /* @__PURE__ */ new Map([
|
|
7109
|
+
["TODAY", new Set(RANGE_AND_EQUALITY)],
|
|
7110
|
+
["NOW", new Set(RANGE_AND_EQUALITY)],
|
|
7111
|
+
["LOGINUSER", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
7112
|
+
["PRIMARY_ORGANIZATION", /* @__PURE__ */ new Set(["in", "not in"])]
|
|
7113
|
+
]);
|
|
7114
|
+
var EQUALITY_IN = ["=", "!=", "in", "not in"];
|
|
7115
|
+
var NATIVE_OPERATORS = /* @__PURE__ */ new Map([
|
|
7116
|
+
["RECORD_NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
7117
|
+
["__ID__", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
7118
|
+
["CREATOR", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
7119
|
+
["MODIFIER", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
7120
|
+
["CREATED_TIME", new Set(RANGE_AND_EQUALITY)],
|
|
7121
|
+
["UPDATED_TIME", new Set(RANGE_AND_EQUALITY)],
|
|
7122
|
+
["DATE", new Set(RANGE_AND_EQUALITY)],
|
|
7123
|
+
["TIME", new Set(RANGE_AND_EQUALITY)],
|
|
7124
|
+
["DATETIME", new Set(RANGE_AND_EQUALITY)],
|
|
7125
|
+
["SINGLE_LINE_TEXT", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
|
|
7126
|
+
["LINK", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
|
|
7127
|
+
["NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
7128
|
+
["CALC", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
7129
|
+
["MULTI_LINE_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
7130
|
+
["RICH_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
7131
|
+
["CHECK_BOX", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
7132
|
+
["RADIO_BUTTON", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
7133
|
+
["DROP_DOWN", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
7134
|
+
["MULTI_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
7135
|
+
["FILE", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
7136
|
+
["USER_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
7137
|
+
["ORGANIZATION_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
7138
|
+
["GROUP_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
7139
|
+
["STATUS", new Set(EQUALITY_IN)],
|
|
7140
|
+
["STATUS_ASSIGNEE", /* @__PURE__ */ new Set(["in", "not in"])]
|
|
7141
|
+
]);
|
|
7142
|
+
var LOCAL_VALID_OPERATORS = /* @__PURE__ */ new Map([
|
|
7143
|
+
["CREATOR", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
7144
|
+
["MODIFIER", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
7145
|
+
["CHECK_BOX", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
7146
|
+
["MULTI_SELECT", /* @__PURE__ */ new Set(["in", "not in"])]
|
|
7147
|
+
]);
|
|
7148
|
+
var LOCAL_SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
7149
|
+
"RECORD_NUMBER",
|
|
7150
|
+
"__ID__",
|
|
7151
|
+
"CREATOR",
|
|
7152
|
+
"MODIFIER",
|
|
7153
|
+
"CREATED_TIME",
|
|
7154
|
+
"UPDATED_TIME",
|
|
7155
|
+
"DATE",
|
|
7156
|
+
"TIME",
|
|
7157
|
+
"DATETIME",
|
|
7158
|
+
"SINGLE_LINE_TEXT",
|
|
7159
|
+
"LINK",
|
|
7160
|
+
"NUMBER",
|
|
7161
|
+
"CALC",
|
|
7162
|
+
"MULTI_LINE_TEXT",
|
|
7163
|
+
"RICH_TEXT",
|
|
7164
|
+
"RADIO_BUTTON",
|
|
7165
|
+
"DROP_DOWN",
|
|
7166
|
+
"STATUS",
|
|
7167
|
+
// 一時表・CTE・式列は kintone REST へは送らず、共有ローカル評価器で扱う。
|
|
7168
|
+
"KSQL_STRING",
|
|
7169
|
+
"KSQL_NUMBER",
|
|
7170
|
+
"KSQL_BOOLEAN"
|
|
7171
|
+
]);
|
|
7172
|
+
var LOCAL_COLLECTION_TYPES = /* @__PURE__ */ new Set([
|
|
7173
|
+
"CHECK_BOX",
|
|
7174
|
+
"MULTI_SELECT",
|
|
7175
|
+
"FILE",
|
|
7176
|
+
"USER_SELECT",
|
|
7177
|
+
"ORGANIZATION_SELECT",
|
|
7178
|
+
"GROUP_SELECT",
|
|
7179
|
+
"STATUS_ASSIGNEE",
|
|
7180
|
+
"CATEGORY"
|
|
7181
|
+
]);
|
|
7182
|
+
function nativeWhereOperatorsForType(fieldType) {
|
|
7183
|
+
return NATIVE_OPERATORS.get(fieldType) ?? /* @__PURE__ */ new Set();
|
|
7279
7184
|
}
|
|
7280
|
-
function
|
|
7281
|
-
const
|
|
7185
|
+
function normalizeChoiceEquality(where, resolveField2) {
|
|
7186
|
+
const rewrites = [];
|
|
7282
7187
|
const visit = (node) => {
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7188
|
+
if (node.type === "BINARY") {
|
|
7189
|
+
if ((node.op === "=" || node.op === "!=" || node.op === "<>") && node.left.type === "FIELD" && node.right.type === "STRING" && node.right.value !== "") {
|
|
7190
|
+
const semantics = resolveField2(node.left);
|
|
7191
|
+
if (semantics !== void 0 && semantics.compareMode === "option" && LOCAL_SCALAR_TYPES.has(semantics.fieldType) && nativeWhereOperatorsForType(semantics.fieldType).has("in") && semantics.optionOrder?.has(node.right.value) === true) {
|
|
7192
|
+
const normalizedOperator = node.op === "=" ? "IN" : "NOT_IN";
|
|
7193
|
+
rewrites.push({
|
|
7194
|
+
field: node.left,
|
|
7195
|
+
originalOperator: node.op,
|
|
7196
|
+
normalizedOperator,
|
|
7197
|
+
value: node.right.value
|
|
7198
|
+
});
|
|
7199
|
+
return {
|
|
7200
|
+
...node,
|
|
7201
|
+
op: normalizedOperator,
|
|
7202
|
+
right: { type: "IN_LIST", values: [node.right] }
|
|
7203
|
+
};
|
|
7287
7204
|
}
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
visit(node.expr);
|
|
7296
|
-
return;
|
|
7297
|
-
case "EXISTS":
|
|
7298
|
-
case "NULL_CHECK":
|
|
7299
|
-
case "BOOLEAN":
|
|
7300
|
-
return;
|
|
7205
|
+
}
|
|
7206
|
+
return node;
|
|
7207
|
+
}
|
|
7208
|
+
if (node.type === "LOGICAL") {
|
|
7209
|
+
const left = visit(node.left);
|
|
7210
|
+
const right = visit(node.right);
|
|
7211
|
+
return left === node.left && right === node.right ? node : { ...node, left, right };
|
|
7301
7212
|
}
|
|
7213
|
+
if (node.type === "GROUP" || node.type === "NOT") {
|
|
7214
|
+
const expr = visit(node.expr);
|
|
7215
|
+
return expr === node.expr ? node : { ...node, expr };
|
|
7216
|
+
}
|
|
7217
|
+
return node;
|
|
7302
7218
|
};
|
|
7303
|
-
visit(where);
|
|
7304
|
-
return leaves;
|
|
7305
|
-
}
|
|
7306
|
-
function sameOccurrenceList(actual, expected) {
|
|
7307
|
-
return actual.length === expected.length && actual.every((name, index) => name === expected[index]);
|
|
7219
|
+
return { normalizedWhere: visit(where), rewrites };
|
|
7308
7220
|
}
|
|
7309
|
-
function
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
if (!isServerOnlyWhereFunctionName(name)) return false;
|
|
7313
|
-
expected.set(name, (expected.get(name) ?? 0) + 1);
|
|
7314
|
-
}
|
|
7315
|
-
for (const [name, count] of expected) {
|
|
7316
|
-
const matches = query.match(new RegExp(`\\b${name}\\s*\\(`, "g"));
|
|
7317
|
-
if ((matches?.length ?? 0) < count) return false;
|
|
7221
|
+
function classifyWhereCapability(where, resolveField2) {
|
|
7222
|
+
if (where === null) {
|
|
7223
|
+
return { capability: "EXACT_PUSHDOWN", reasons: [{ code: "WHERE_EXACT" }] };
|
|
7318
7224
|
}
|
|
7319
|
-
return
|
|
7225
|
+
return classifyNode(where, resolveField2);
|
|
7320
7226
|
}
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7227
|
+
function classifyNode(where, resolveField2) {
|
|
7228
|
+
switch (where.type) {
|
|
7229
|
+
case "BOOLEAN":
|
|
7230
|
+
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
7231
|
+
case "BINARY":
|
|
7232
|
+
return classifyBinary(where.op, where.left, where.right, resolveField2);
|
|
7233
|
+
case "NULL_CHECK":
|
|
7234
|
+
if (where.field.type !== "FIELD") return localExpression();
|
|
7235
|
+
return classifyLocalOnlyField(where.field, where.not ? "IS NOT NULL" : "IS NULL", resolveField2);
|
|
7236
|
+
case "EXISTS":
|
|
7237
|
+
return localExpression();
|
|
7238
|
+
case "GROUP":
|
|
7239
|
+
return classifyNode(where.expr, resolveField2);
|
|
7240
|
+
case "NOT": {
|
|
7241
|
+
const inner = classifyNode(where.expr, resolveField2);
|
|
7242
|
+
if (inner.capability !== "SUPERSET_PREFILTER") return inner;
|
|
7243
|
+
if (!hasRelativeDateReason(inner.reasons)) {
|
|
7244
|
+
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
7245
|
+
}
|
|
7246
|
+
return requireExactFunctionPushdown({
|
|
7247
|
+
capability: "LOCAL_ONLY",
|
|
7248
|
+
reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }, ...inner.reasons]
|
|
7249
|
+
});
|
|
7250
|
+
}
|
|
7251
|
+
case "LOGICAL": {
|
|
7252
|
+
const left = classifyNode(where.left, resolveField2);
|
|
7253
|
+
const right = classifyNode(where.right, resolveField2);
|
|
7254
|
+
return combineLogical(where.op, left, right);
|
|
7255
|
+
}
|
|
7327
7256
|
}
|
|
7328
|
-
};
|
|
7329
|
-
function validateKlikeStatement(stmt) {
|
|
7330
|
-
validateStatement(stmt);
|
|
7331
7257
|
}
|
|
7332
|
-
function
|
|
7333
|
-
if (
|
|
7334
|
-
|
|
7335
|
-
"FULL_SCAN \u306E KLIKE / NOT KLIKE \u3092\u5B89\u5168\u306B\u62BC\u3057\u4E0B\u3052\u3089\u308C\u307E\u305B\u3093\u3002OR / NOT \u914D\u4E0B\u3001CTE\u30FB\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u3001LEFT / RIGHT JOIN \u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
7336
|
-
);
|
|
7258
|
+
function classifyBinary(op, left, right, resolveField2) {
|
|
7259
|
+
if (right.type === "KINTONE_FUNC" && isRelativeDateFunctionName(right.name)) {
|
|
7260
|
+
return classifyRelativeDateBinary(op, left, right, resolveField2);
|
|
7337
7261
|
}
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
validateWith(stmt);
|
|
7349
|
-
return;
|
|
7350
|
-
case "EXPLAIN":
|
|
7351
|
-
validateStatement(stmt.query);
|
|
7352
|
-
return;
|
|
7353
|
-
case "CREATE_TEMP_TABLE":
|
|
7354
|
-
validateSelectLike(stmt.query);
|
|
7355
|
-
return;
|
|
7356
|
-
case "SET_VARIABLE":
|
|
7357
|
-
case "DECLARE_VARIABLE":
|
|
7358
|
-
case "ASSERT":
|
|
7359
|
-
validateNestedSelects(stmt);
|
|
7360
|
-
return;
|
|
7361
|
-
case "UPDATE":
|
|
7362
|
-
if (stmt.applyBlocks?.length && !isSinglePositiveRecordIdWhere(stmt.where) && whereHasKlike(stmt.where)) {
|
|
7363
|
-
validateKlikeWhereExpressions(stmt.where);
|
|
7364
|
-
validateNestedSelects(stmt);
|
|
7365
|
-
return;
|
|
7366
|
-
}
|
|
7367
|
-
if (containsKlike(stmt) && stmt.subtableCode) {
|
|
7368
|
-
throw new KlikeValidationError(
|
|
7369
|
-
"KLIKE / NOT KLIKE \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u306E WHERE \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
7370
|
-
);
|
|
7371
|
-
}
|
|
7372
|
-
if (!stmt.applyBlocks?.length && !stmt.subtableCode && whereHasKlike(stmt.where) && !containsKlikeOutsideWhereAndNestedSelects(stmt, stmt.where)) {
|
|
7373
|
-
validateKlikeWhereExpressions(stmt.where);
|
|
7374
|
-
validateNestedSelects(stmt);
|
|
7375
|
-
return;
|
|
7262
|
+
if (right.type === "KINTONE_FUNC" && isLegacyKintoneFunction(right)) {
|
|
7263
|
+
return classifyLegacyKintoneFunctionBinary(op, left, right, resolveField2);
|
|
7264
|
+
}
|
|
7265
|
+
if (right.type === "IN_LIST") {
|
|
7266
|
+
const functions = right.values.filter(
|
|
7267
|
+
(value) => value.type === "KINTONE_FUNC"
|
|
7268
|
+
);
|
|
7269
|
+
if (functions.length > 0) {
|
|
7270
|
+
if (right.values.length === 1 && functions.length === 1) {
|
|
7271
|
+
return classifyLegacyKintoneFunctionBinary(op, left, functions[0], resolveField2);
|
|
7376
7272
|
}
|
|
7377
|
-
|
|
7273
|
+
return legacyKintoneFunctionUnsupported(
|
|
7274
|
+
"WHERE_KINTONE_FUNCTION_CONTEXT_UNSUPPORTED",
|
|
7275
|
+
functions[0].name,
|
|
7276
|
+
left.type === "FIELD" ? left.field : void 0,
|
|
7277
|
+
left.type === "FIELD" ? resolveField2(left)?.fieldType : void 0,
|
|
7278
|
+
normalizeOperator(op)
|
|
7279
|
+
);
|
|
7280
|
+
}
|
|
7281
|
+
}
|
|
7282
|
+
if (left.type !== "FIELD") return localExpression();
|
|
7283
|
+
const semantics = resolveField2(left);
|
|
7284
|
+
if (!semantics) {
|
|
7285
|
+
return unsupported2("WHERE_FIELD_UNRESOLVED", left.field, void 0, normalizeOperator(op));
|
|
7286
|
+
}
|
|
7287
|
+
const nativeOp = normalizeOperator(op);
|
|
7288
|
+
if (!isLocallyValidOperator(semantics.fieldType, nativeOp)) {
|
|
7289
|
+
return unsupported2(
|
|
7290
|
+
"WHERE_OPERATOR_INVALID_FOR_FIELD_TYPE",
|
|
7291
|
+
left.field,
|
|
7292
|
+
semantics.fieldType,
|
|
7293
|
+
nativeOp
|
|
7294
|
+
);
|
|
7295
|
+
}
|
|
7296
|
+
if (!hasLocalContract(semantics.fieldType, op)) {
|
|
7297
|
+
return unsupported2("WHERE_OPERATOR_UNSUPPORTED", left.field, semantics.fieldType, nativeOp);
|
|
7298
|
+
}
|
|
7299
|
+
const native = nativeWhereOperatorsForType(semantics.fieldType);
|
|
7300
|
+
const rightCanPush = right.type === "STRING" || right.type === "NUMBER" || right.type === "IN_LIST" || isLegacyKintoneFunction(right);
|
|
7301
|
+
const structureAllows = !semantics.requiresCollectionOperators || nativeOp !== "=" && nativeOp !== "!=";
|
|
7302
|
+
const sqlLikeIsResidual = op === "LIKE" || op === "NOT_LIKE";
|
|
7303
|
+
if (rightCanPush && structureAllows && native.has(nativeOp) && !sqlLikeIsResidual) {
|
|
7304
|
+
return {
|
|
7305
|
+
capability: "EXACT_PUSHDOWN",
|
|
7306
|
+
reasons: [{
|
|
7307
|
+
code: "WHERE_EXACT",
|
|
7308
|
+
field: left.field,
|
|
7309
|
+
fieldType: semantics.fieldType,
|
|
7310
|
+
operator: nativeOp
|
|
7311
|
+
}]
|
|
7312
|
+
};
|
|
7313
|
+
}
|
|
7314
|
+
return {
|
|
7315
|
+
capability: "LOCAL_ONLY",
|
|
7316
|
+
reasons: [{
|
|
7317
|
+
code: "WHERE_RESIDUAL",
|
|
7318
|
+
field: left.field,
|
|
7319
|
+
fieldType: semantics.fieldType,
|
|
7320
|
+
operator: nativeOp
|
|
7321
|
+
}]
|
|
7322
|
+
};
|
|
7323
|
+
}
|
|
7324
|
+
function isLegacyKintoneFunction(value) {
|
|
7325
|
+
return value.type === "KINTONE_FUNC" && LEGACY_KINTONE_FUNCTION_NAMES.has(value.name);
|
|
7326
|
+
}
|
|
7327
|
+
function classifyLegacyKintoneFunctionBinary(op, left, right, resolveField2) {
|
|
7328
|
+
const operator = normalizeOperator(op);
|
|
7329
|
+
const functionName = right.name;
|
|
7330
|
+
if (!LEGACY_KINTONE_FUNCTION_NAMES.has(functionName) || left.type !== "FIELD") {
|
|
7331
|
+
return legacyKintoneFunctionUnsupported(
|
|
7332
|
+
"WHERE_KINTONE_FUNCTION_CONTEXT_UNSUPPORTED",
|
|
7333
|
+
functionName,
|
|
7334
|
+
void 0,
|
|
7335
|
+
void 0,
|
|
7336
|
+
operator
|
|
7337
|
+
);
|
|
7338
|
+
}
|
|
7339
|
+
const semantics = resolveField2(left);
|
|
7340
|
+
const validFieldTypes = LEGACY_KINTONE_FUNCTION_FIELD_TYPES.get(functionName);
|
|
7341
|
+
if (!semantics || !validFieldTypes.has(semantics.fieldType) || semantics.inSubtable || semantics.requiresCollectionOperators) {
|
|
7342
|
+
return legacyKintoneFunctionUnsupported(
|
|
7343
|
+
"WHERE_KINTONE_FUNCTION_FIELD_TYPE_UNSUPPORTED",
|
|
7344
|
+
functionName,
|
|
7345
|
+
left.field,
|
|
7346
|
+
semantics?.fieldType,
|
|
7347
|
+
operator
|
|
7348
|
+
);
|
|
7349
|
+
}
|
|
7350
|
+
const validOperators = LEGACY_KINTONE_FUNCTION_OPERATORS.get(functionName);
|
|
7351
|
+
if (!validOperators.has(operator)) {
|
|
7352
|
+
return legacyKintoneFunctionUnsupported(
|
|
7353
|
+
"WHERE_KINTONE_FUNCTION_OPERATOR_UNSUPPORTED",
|
|
7354
|
+
functionName,
|
|
7355
|
+
left.field,
|
|
7356
|
+
semantics.fieldType,
|
|
7357
|
+
operator
|
|
7358
|
+
);
|
|
7359
|
+
}
|
|
7360
|
+
return {
|
|
7361
|
+
capability: "EXACT_PUSHDOWN",
|
|
7362
|
+
reasons: [{
|
|
7363
|
+
code: "WHERE_EXACT",
|
|
7364
|
+
functionName,
|
|
7365
|
+
field: left.field,
|
|
7366
|
+
fieldType: semantics.fieldType,
|
|
7367
|
+
operator
|
|
7368
|
+
}]
|
|
7369
|
+
};
|
|
7370
|
+
}
|
|
7371
|
+
function classifyRelativeDateBinary(op, left, right, resolveField2) {
|
|
7372
|
+
const operator = normalizeOperator(op);
|
|
7373
|
+
const functionName = right.name;
|
|
7374
|
+
if (left.type !== "FIELD") {
|
|
7375
|
+
return relativeDateUnsupported(
|
|
7376
|
+
"WHERE_RELATIVE_DATE_CONTEXT_UNSUPPORTED",
|
|
7377
|
+
functionName,
|
|
7378
|
+
void 0,
|
|
7379
|
+
void 0,
|
|
7380
|
+
operator
|
|
7381
|
+
);
|
|
7382
|
+
}
|
|
7383
|
+
const semantics = resolveField2(left);
|
|
7384
|
+
if (!semantics) {
|
|
7385
|
+
return relativeDateUnsupported(
|
|
7386
|
+
"WHERE_RELATIVE_DATE_FIELD_TYPE_UNSUPPORTED",
|
|
7387
|
+
functionName,
|
|
7388
|
+
left.field,
|
|
7389
|
+
void 0,
|
|
7390
|
+
operator
|
|
7391
|
+
);
|
|
7392
|
+
}
|
|
7393
|
+
if (!hasValidRelativeDateArguments(right)) {
|
|
7394
|
+
return relativeDateUnsupported(
|
|
7395
|
+
"WHERE_RELATIVE_DATE_ARGUMENT_INVALID",
|
|
7396
|
+
functionName,
|
|
7397
|
+
left.field,
|
|
7398
|
+
semantics.fieldType,
|
|
7399
|
+
operator
|
|
7400
|
+
);
|
|
7401
|
+
}
|
|
7402
|
+
if (!RELATIVE_DATE_OPERATORS.has(operator)) {
|
|
7403
|
+
return relativeDateUnsupported(
|
|
7404
|
+
"WHERE_RELATIVE_DATE_OPERATOR_UNSUPPORTED",
|
|
7405
|
+
functionName,
|
|
7406
|
+
left.field,
|
|
7407
|
+
semantics.fieldType,
|
|
7408
|
+
operator
|
|
7409
|
+
);
|
|
7410
|
+
}
|
|
7411
|
+
if (!RELATIVE_DATE_FIELD_TYPES.has(semantics.fieldType) || semantics.inSubtable || semantics.requiresCollectionOperators) {
|
|
7412
|
+
return relativeDateUnsupported(
|
|
7413
|
+
"WHERE_RELATIVE_DATE_FIELD_TYPE_UNSUPPORTED",
|
|
7414
|
+
functionName,
|
|
7415
|
+
left.field,
|
|
7416
|
+
semantics.fieldType,
|
|
7417
|
+
operator
|
|
7418
|
+
);
|
|
7419
|
+
}
|
|
7420
|
+
return {
|
|
7421
|
+
capability: "EXACT_PUSHDOWN",
|
|
7422
|
+
reasons: [{
|
|
7423
|
+
code: "WHERE_EXACT",
|
|
7424
|
+
functionName,
|
|
7425
|
+
field: left.field,
|
|
7426
|
+
fieldType: semantics.fieldType,
|
|
7427
|
+
operator
|
|
7428
|
+
}]
|
|
7429
|
+
};
|
|
7430
|
+
}
|
|
7431
|
+
function hasValidRelativeDateArguments(value) {
|
|
7432
|
+
if (!("args" in value) || !value.args) return false;
|
|
7433
|
+
switch (value.name) {
|
|
7434
|
+
case "YESTERDAY":
|
|
7435
|
+
case "TOMORROW":
|
|
7436
|
+
case "THIS_YEAR":
|
|
7437
|
+
case "LAST_YEAR":
|
|
7438
|
+
case "NEXT_YEAR":
|
|
7439
|
+
return value.args.kind === "NONE";
|
|
7440
|
+
case "FROM_TODAY":
|
|
7441
|
+
return value.args.kind === "FROM_TODAY" && Number.isSafeInteger(value.args.offset) && value.args.offsetText === String(value.args.offset === 0 ? 0 : value.args.offset) && (value.args.unit === "DAYS" || value.args.unit === "WEEKS" || value.args.unit === "MONTHS" || value.args.unit === "YEARS");
|
|
7442
|
+
case "THIS_WEEK":
|
|
7443
|
+
case "LAST_WEEK":
|
|
7444
|
+
case "NEXT_WEEK":
|
|
7445
|
+
return value.args.kind === "WEEK" && (value.args.weekday === null || value.args.weekday === "SUNDAY" || value.args.weekday === "MONDAY" || value.args.weekday === "TUESDAY" || value.args.weekday === "WEDNESDAY" || value.args.weekday === "THURSDAY" || value.args.weekday === "FRIDAY" || value.args.weekday === "SATURDAY");
|
|
7446
|
+
case "THIS_MONTH":
|
|
7447
|
+
case "LAST_MONTH":
|
|
7448
|
+
case "NEXT_MONTH":
|
|
7449
|
+
return value.args.kind === "MONTH" && (value.args.day === null || value.args.day === "LAST" || Number.isInteger(value.args.day) && value.args.day >= 1 && value.args.day <= 31);
|
|
7450
|
+
default:
|
|
7451
|
+
return false;
|
|
7452
|
+
}
|
|
7453
|
+
}
|
|
7454
|
+
function classifyLocalOnlyField(field, operator, resolveField2) {
|
|
7455
|
+
const semantics = resolveField2(field);
|
|
7456
|
+
if (!semantics) return unsupported2("WHERE_FIELD_UNRESOLVED", field.field, void 0, operator);
|
|
7457
|
+
if (!isLocallyValidOperator(semantics.fieldType, operator)) {
|
|
7458
|
+
return unsupported2(
|
|
7459
|
+
"WHERE_OPERATOR_INVALID_FOR_FIELD_TYPE",
|
|
7460
|
+
field.field,
|
|
7461
|
+
semantics.fieldType,
|
|
7462
|
+
operator
|
|
7463
|
+
);
|
|
7464
|
+
}
|
|
7465
|
+
if (!LOCAL_SCALAR_TYPES.has(semantics.fieldType) && !LOCAL_COLLECTION_TYPES.has(semantics.fieldType)) {
|
|
7466
|
+
return unsupported2("WHERE_OPERATOR_UNSUPPORTED", field.field, semantics.fieldType, operator);
|
|
7467
|
+
}
|
|
7468
|
+
return {
|
|
7469
|
+
capability: "LOCAL_ONLY",
|
|
7470
|
+
reasons: [{ code: "WHERE_RESIDUAL", field: field.field, fieldType: semantics.fieldType, operator }]
|
|
7471
|
+
};
|
|
7472
|
+
}
|
|
7473
|
+
function isLocallyValidOperator(fieldType, operator) {
|
|
7474
|
+
const policy = LOCAL_VALID_OPERATORS.get(fieldType);
|
|
7475
|
+
return policy === void 0 || policy.has(operator);
|
|
7476
|
+
}
|
|
7477
|
+
function hasLocalContract(fieldType, op) {
|
|
7478
|
+
if (LOCAL_SCALAR_TYPES.has(fieldType)) return true;
|
|
7479
|
+
if (!LOCAL_COLLECTION_TYPES.has(fieldType)) return false;
|
|
7480
|
+
return op === "=" || op === "!=" || op === "<>" || op === "IN" || op === "NOT_IN" || op === "LIKE" || op === "NOT_LIKE" || op === "KLIKE" || op === "NOT_KLIKE";
|
|
7481
|
+
}
|
|
7482
|
+
function normalizeOperator(op) {
|
|
7483
|
+
switch (op) {
|
|
7484
|
+
case "<>":
|
|
7485
|
+
return "!=";
|
|
7486
|
+
case "IN":
|
|
7487
|
+
return "in";
|
|
7488
|
+
case "NOT_IN":
|
|
7489
|
+
return "not in";
|
|
7490
|
+
case "LIKE":
|
|
7491
|
+
case "KLIKE":
|
|
7492
|
+
return "like";
|
|
7493
|
+
case "NOT_LIKE":
|
|
7494
|
+
case "NOT_KLIKE":
|
|
7495
|
+
return "not like";
|
|
7496
|
+
default:
|
|
7497
|
+
return op;
|
|
7498
|
+
}
|
|
7499
|
+
}
|
|
7500
|
+
function combineLogical(op, left, right) {
|
|
7501
|
+
const reasons = [...left.reasons, ...right.reasons];
|
|
7502
|
+
if (left.capability === "UNSUPPORTED" || right.capability === "UNSUPPORTED") {
|
|
7503
|
+
return requireExactFunctionPushdown({ capability: "UNSUPPORTED", reasons });
|
|
7504
|
+
}
|
|
7505
|
+
if (left.capability === "EXACT_PUSHDOWN" && right.capability === "EXACT_PUSHDOWN") {
|
|
7506
|
+
return { capability: "EXACT_PUSHDOWN", reasons };
|
|
7507
|
+
}
|
|
7508
|
+
if (op === "AND" && (left.capability === "EXACT_PUSHDOWN" || right.capability === "EXACT_PUSHDOWN" || left.capability === "SUPERSET_PREFILTER" || right.capability === "SUPERSET_PREFILTER")) {
|
|
7509
|
+
return requireExactFunctionPushdown({
|
|
7510
|
+
capability: "SUPERSET_PREFILTER",
|
|
7511
|
+
reasons: [{ code: "WHERE_SUPERSET_PREFILTER" }, ...reasons]
|
|
7512
|
+
});
|
|
7513
|
+
}
|
|
7514
|
+
return requireExactFunctionPushdown({ capability: "LOCAL_ONLY", reasons });
|
|
7515
|
+
}
|
|
7516
|
+
function localExpression() {
|
|
7517
|
+
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
7518
|
+
}
|
|
7519
|
+
function unsupported2(code, field, fieldType, operator) {
|
|
7520
|
+
return { capability: "UNSUPPORTED", reasons: [{ code, field, fieldType, operator }] };
|
|
7521
|
+
}
|
|
7522
|
+
function legacyKintoneFunctionUnsupported(code, functionName, field, fieldType, operator) {
|
|
7523
|
+
return requireExactFunctionPushdown({
|
|
7524
|
+
capability: "UNSUPPORTED",
|
|
7525
|
+
reasons: [{ code, functionName, field, fieldType, operator }]
|
|
7526
|
+
});
|
|
7527
|
+
}
|
|
7528
|
+
function relativeDateUnsupported(code, functionName, field, fieldType, operator) {
|
|
7529
|
+
return requireExactRelativeDatePushdown({
|
|
7530
|
+
capability: "UNSUPPORTED",
|
|
7531
|
+
reasons: [{ code, functionName, field, fieldType, operator }]
|
|
7532
|
+
});
|
|
7533
|
+
}
|
|
7534
|
+
function hasRelativeDateReason(reasons) {
|
|
7535
|
+
return reasons.some(
|
|
7536
|
+
(reason) => reason.code.startsWith("WHERE_RELATIVE_DATE_") || reason.functionName !== void 0 && isRelativeDateFunctionName(reason.functionName)
|
|
7537
|
+
);
|
|
7538
|
+
}
|
|
7539
|
+
function hasLegacyKintoneFunctionReason(reasons) {
|
|
7540
|
+
return reasons.some(
|
|
7541
|
+
(reason) => reason.code.startsWith("WHERE_KINTONE_FUNCTION_") || reason.functionName !== void 0 && LEGACY_KINTONE_FUNCTION_NAMES.has(reason.functionName)
|
|
7542
|
+
);
|
|
7543
|
+
}
|
|
7544
|
+
function requireExactRelativeDatePushdown(result) {
|
|
7545
|
+
if (result.capability === "EXACT_PUSHDOWN" || !hasRelativeDateReason(result.reasons) || result.reasons.some((reason) => reason.code === "WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN")) {
|
|
7546
|
+
return result;
|
|
7547
|
+
}
|
|
7548
|
+
const relative = result.reasons.find(
|
|
7549
|
+
(reason) => reason.code.startsWith("WHERE_RELATIVE_DATE_") || reason.functionName !== void 0 && isRelativeDateFunctionName(reason.functionName)
|
|
7550
|
+
);
|
|
7551
|
+
return {
|
|
7552
|
+
capability: result.capability,
|
|
7553
|
+
reasons: [
|
|
7554
|
+
...result.reasons,
|
|
7555
|
+
{
|
|
7556
|
+
code: "WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN",
|
|
7557
|
+
functionName: relative.functionName,
|
|
7558
|
+
field: relative.field,
|
|
7559
|
+
fieldType: relative.fieldType,
|
|
7560
|
+
operator: relative.operator
|
|
7561
|
+
}
|
|
7562
|
+
]
|
|
7563
|
+
};
|
|
7564
|
+
}
|
|
7565
|
+
function requireExactLegacyKintoneFunctionPushdown(result) {
|
|
7566
|
+
if (result.capability === "EXACT_PUSHDOWN" || !hasLegacyKintoneFunctionReason(result.reasons) || result.reasons.some(
|
|
7567
|
+
(reason) => reason.code === "WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN"
|
|
7568
|
+
)) {
|
|
7569
|
+
return result;
|
|
7570
|
+
}
|
|
7571
|
+
const legacy = result.reasons.find(
|
|
7572
|
+
(reason) => reason.code.startsWith("WHERE_KINTONE_FUNCTION_") || reason.functionName !== void 0 && LEGACY_KINTONE_FUNCTION_NAMES.has(reason.functionName)
|
|
7573
|
+
);
|
|
7574
|
+
return {
|
|
7575
|
+
capability: result.capability,
|
|
7576
|
+
reasons: [
|
|
7577
|
+
...result.reasons,
|
|
7578
|
+
{
|
|
7579
|
+
code: "WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN",
|
|
7580
|
+
functionName: legacy.functionName,
|
|
7581
|
+
field: legacy.field,
|
|
7582
|
+
fieldType: legacy.fieldType,
|
|
7583
|
+
operator: legacy.operator
|
|
7584
|
+
}
|
|
7585
|
+
]
|
|
7586
|
+
};
|
|
7587
|
+
}
|
|
7588
|
+
function requireExactFunctionPushdown(result) {
|
|
7589
|
+
return requireExactLegacyKintoneFunctionPushdown(
|
|
7590
|
+
requireExactRelativeDatePushdown(result)
|
|
7591
|
+
);
|
|
7592
|
+
}
|
|
7593
|
+
|
|
7594
|
+
// src/core/optimization/joinDateTimeLiteralPolicy.ts
|
|
7595
|
+
function isCanonicalJoinDate(value) {
|
|
7596
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
7597
|
+
if (!match) return false;
|
|
7598
|
+
const year = Number(match[1]);
|
|
7599
|
+
const month = Number(match[2]);
|
|
7600
|
+
const day = Number(match[3]);
|
|
7601
|
+
if (year < 1 || year > 9999) return false;
|
|
7602
|
+
const date = /* @__PURE__ */ new Date(0);
|
|
7603
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
7604
|
+
date.setUTCHours(0, 0, 0, 0);
|
|
7605
|
+
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
|
|
7606
|
+
}
|
|
7607
|
+
function isCanonicalJoinTime(value) {
|
|
7608
|
+
const match = /^(\d{2}):(\d{2})$/.exec(value);
|
|
7609
|
+
return match !== null && Number(match[1]) <= 23 && Number(match[2]) <= 59;
|
|
7610
|
+
}
|
|
7611
|
+
function isCanonicalJoinDateTime(value) {
|
|
7612
|
+
const match = /^(\d{4}-\d{2}-\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/.exec(value);
|
|
7613
|
+
if (!match || !isCanonicalJoinDate(match[1])) return false;
|
|
7614
|
+
return Number(match[2]) <= 23 && Number(match[3]) <= 59 && Number(match[4]) <= 59;
|
|
7615
|
+
}
|
|
7616
|
+
|
|
7617
|
+
// src/core/optimization/joinNumberLiteralPolicy.ts
|
|
7618
|
+
function isJoinNumberLiteralSupported(literal) {
|
|
7619
|
+
const source = literal.raw ?? String(literal.value);
|
|
7620
|
+
const decimal = parseExactDecimal(source);
|
|
7621
|
+
if (decimal === null) return false;
|
|
7622
|
+
if (decimal.sign === 0) return numberLiteralText(literal) === "0";
|
|
7623
|
+
const fractionDigits = Math.max(decimal.scale, 0);
|
|
7624
|
+
const integerDigits = Math.max(decimal.coefficient.length - decimal.scale, 0);
|
|
7625
|
+
if (fractionDigits > 10 || integerDigits + fractionDigits > 30) {
|
|
7626
|
+
return false;
|
|
7627
|
+
}
|
|
7628
|
+
const canonical = formatPlainDecimal(decimal);
|
|
7629
|
+
return numberLiteralText(literal) === canonical;
|
|
7630
|
+
}
|
|
7631
|
+
|
|
7632
|
+
// src/core/optimization/supportedLeafPolicy.ts
|
|
7633
|
+
var SELECTION_TYPES = /* @__PURE__ */ new Set([
|
|
7634
|
+
"DROP_DOWN",
|
|
7635
|
+
"RADIO_BUTTON",
|
|
7636
|
+
"CHECK_BOX",
|
|
7637
|
+
"MULTI_SELECT",
|
|
7638
|
+
"STATUS"
|
|
7639
|
+
]);
|
|
7640
|
+
var USER_CODE_TYPES = /* @__PURE__ */ new Set([
|
|
7641
|
+
"CREATOR",
|
|
7642
|
+
"MODIFIER",
|
|
7643
|
+
"USER_SELECT",
|
|
7644
|
+
"ORGANIZATION_SELECT",
|
|
7645
|
+
"GROUP_SELECT",
|
|
7646
|
+
"STATUS_ASSIGNEE"
|
|
7647
|
+
]);
|
|
7648
|
+
var KLIKE_TYPES = /* @__PURE__ */ new Set([
|
|
7649
|
+
"SINGLE_LINE_TEXT",
|
|
7650
|
+
"LINK",
|
|
7651
|
+
"MULTI_LINE_TEXT",
|
|
7652
|
+
"RICH_TEXT",
|
|
7653
|
+
"FILE"
|
|
7654
|
+
]);
|
|
7655
|
+
var DATETIME_TYPES = /* @__PURE__ */ new Set(["DATETIME", "CREATED_TIME", "UPDATED_TIME"]);
|
|
7656
|
+
function classifySupportedLeaf(predicate, metadata) {
|
|
7657
|
+
const { fieldCode, fieldType, fieldOptions } = metadata;
|
|
7658
|
+
if (fieldType.startsWith("KSQL_")) return "unsafe";
|
|
7659
|
+
if (predicate.op === "LIKE" || predicate.op === "NOT_LIKE") return "unsafe";
|
|
7660
|
+
if (predicate.op === "KLIKE" || predicate.op === "NOT_KLIKE") {
|
|
7661
|
+
return KLIKE_TYPES.has(fieldType) && predicate.right.type === "STRING" && predicate.right.value !== "" ? "exact" : "unsafe";
|
|
7662
|
+
}
|
|
7663
|
+
if (fieldType === "__ID__" || fieldCode === "$id") {
|
|
7664
|
+
return isPositiveSafeInteger(predicate.right) && (predicate.op === "=" || predicate.op === "<" || predicate.op === ">" || predicate.op === "<=" || predicate.op === ">=") ? "exact" : "unsafe";
|
|
7665
|
+
}
|
|
7666
|
+
if (fieldType === "RECORD_NUMBER" || fieldType === "CALC") {
|
|
7667
|
+
return classifySupersetScalarOrListLiteral(predicate);
|
|
7668
|
+
}
|
|
7669
|
+
if (fieldType === "NUMBER") {
|
|
7670
|
+
if ((predicate.op === "IN" || predicate.op === "NOT_IN") && predicate.right.type === "IN_LIST" && predicate.right.values.length > 0 && predicate.right.values.every((value) => value.type === "NUMBER" && isJoinNumberLiteralSupported(value))) {
|
|
7671
|
+
return "exact";
|
|
7672
|
+
}
|
|
7673
|
+
return (predicate.op === "=" || predicate.op === "!=" || predicate.op === "<>" || predicate.op === "<" || predicate.op === ">" || predicate.op === "<=" || predicate.op === ">=") && predicate.right.type === "NUMBER" && isJoinNumberLiteralSupported(predicate.right) ? "exact" : "unsafe";
|
|
7674
|
+
}
|
|
7675
|
+
if (USER_CODE_TYPES.has(fieldType)) {
|
|
7676
|
+
return isNonEmptyStringList(predicate) ? "exact" : "unsafe";
|
|
7677
|
+
}
|
|
7678
|
+
if (fieldType === "SINGLE_LINE_TEXT" || fieldType === "LINK") {
|
|
7679
|
+
if (isNonEmptyStringList(predicate)) return "exact";
|
|
7680
|
+
return (predicate.op === "=" || predicate.op === "!=" || predicate.op === "<>") && predicate.right.type === "STRING" && predicate.right.value !== "" ? "exact" : "unsafe";
|
|
7681
|
+
}
|
|
7682
|
+
if (fieldType === "DATE" || fieldType === "TIME" || DATETIME_TYPES.has(fieldType)) {
|
|
7683
|
+
if (predicate.op !== "=" && predicate.op !== "!=" && predicate.op !== "<>" && predicate.op !== "<" && predicate.op !== ">" && predicate.op !== "<=" && predicate.op !== ">=" || predicate.right.type !== "STRING") return "unsafe";
|
|
7684
|
+
if (fieldType === "DATE") return isCanonicalJoinDate(predicate.right.value) ? "exact" : "unsafe";
|
|
7685
|
+
if (fieldType === "TIME") return isCanonicalJoinTime(predicate.right.value) ? "exact" : "unsafe";
|
|
7686
|
+
return isCanonicalJoinDateTime(predicate.right.value) ? "exact" : "unsafe";
|
|
7687
|
+
}
|
|
7688
|
+
if (SELECTION_TYPES.has(fieldType)) {
|
|
7689
|
+
if (!isNonEmptyStringList(predicate) || fieldOptions === void 0) return "unsafe";
|
|
7690
|
+
return predicate.right.type === "IN_LIST" && predicate.right.values.every((value) => value.type === "STRING" && fieldOptions.has(value.value)) ? "exact" : "unsafe";
|
|
7691
|
+
}
|
|
7692
|
+
return "unsafe";
|
|
7693
|
+
}
|
|
7694
|
+
function isSupportedLeafMetadataCandidate(predicate, isTargetField2) {
|
|
7695
|
+
if (predicate.left.type !== "FIELD" || predicate.left.field === "$id") return false;
|
|
7696
|
+
if (!isTargetField2(predicate.left)) return false;
|
|
7697
|
+
if (predicate.op === "IN" || predicate.op === "NOT_IN") {
|
|
7698
|
+
if (predicate.right.type !== "IN_LIST" || predicate.right.values.length === 0) return false;
|
|
7699
|
+
const firstType = predicate.right.values[0].type;
|
|
7700
|
+
if (firstType !== "NUMBER" && firstType !== "STRING") return false;
|
|
7701
|
+
return predicate.right.values.every((value) => value.type === firstType && (value.type !== "STRING" || value.value !== ""));
|
|
7702
|
+
}
|
|
7703
|
+
if (predicate.op !== "=" && predicate.op !== "!=" && predicate.op !== "<>" && predicate.op !== "<" && predicate.op !== ">" && predicate.op !== "<=" && predicate.op !== ">=") return false;
|
|
7704
|
+
return predicate.right.type === "NUMBER" || predicate.right.type === "STRING" && predicate.right.value !== "";
|
|
7705
|
+
}
|
|
7706
|
+
function isNonEmptyStringList(predicate) {
|
|
7707
|
+
return (predicate.op === "IN" || predicate.op === "NOT_IN") && predicate.right.type === "IN_LIST" && predicate.right.values.length > 0 && predicate.right.values.every((value) => value.type === "STRING" && value.value !== "");
|
|
7708
|
+
}
|
|
7709
|
+
function classifySupersetScalarOrListLiteral(predicate) {
|
|
7710
|
+
const supportedLiteral = (value) => value.type === "NUMBER" && isJoinNumberLiteralSupported(value) || value.type === "STRING" && value.value !== "";
|
|
7711
|
+
if ((predicate.op === "IN" || predicate.op === "NOT_IN") && predicate.right.type === "IN_LIST") {
|
|
7712
|
+
const values = predicate.right.values;
|
|
7713
|
+
if (values.length > 0 && values.every(supportedLiteral) && values.every((value) => value.type === values[0].type)) return "superset";
|
|
7714
|
+
}
|
|
7715
|
+
return (predicate.op === "=" || predicate.op === "!=" || predicate.op === "<>" || predicate.op === "<" || predicate.op === ">" || predicate.op === "<=" || predicate.op === ">=") && supportedLiteral(predicate.right) ? "superset" : "unsafe";
|
|
7716
|
+
}
|
|
7717
|
+
function isPositiveSafeInteger(value) {
|
|
7718
|
+
return value.type === "NUMBER" && Number.isSafeInteger(value.value) && value.value > 0;
|
|
7719
|
+
}
|
|
7720
|
+
|
|
7721
|
+
// src/core/optimization/wherePredicatePushdown.ts
|
|
7722
|
+
function extractSafePushdownLeaves(where, options = {}) {
|
|
7723
|
+
return extractSafePushdownPlan(where, options).condition;
|
|
7724
|
+
}
|
|
7725
|
+
function extractSafePushdownPlan(where, options = {}) {
|
|
7726
|
+
return extractAndLeafPlan(where, (expr) => classifySafeComparison(expr, options));
|
|
7727
|
+
}
|
|
7728
|
+
function extractTypedPushdownCandidates(where, options = {}) {
|
|
7729
|
+
return extractAndLeaves(
|
|
7730
|
+
where,
|
|
7731
|
+
(expr) => isSupportedLeafMetadataCandidate(expr, (field) => isTargetField(field, options))
|
|
7732
|
+
);
|
|
7733
|
+
}
|
|
7734
|
+
function extractAndLeafPlan(where, classify) {
|
|
7735
|
+
switch (where.type) {
|
|
7736
|
+
case "BINARY": {
|
|
7737
|
+
const relation = classify(where);
|
|
7738
|
+
return relation === "unsafe" ? { condition: null, relation: null } : { condition: where, relation };
|
|
7739
|
+
}
|
|
7740
|
+
case "LOGICAL": {
|
|
7741
|
+
if (where.op !== "AND") return { condition: null, relation: null };
|
|
7742
|
+
const left = extractAndLeafPlan(where.left, classify);
|
|
7743
|
+
const right = extractAndLeafPlan(where.right, classify);
|
|
7744
|
+
if (left.condition && right.condition) {
|
|
7745
|
+
return {
|
|
7746
|
+
condition: { ...where, left: left.condition, right: right.condition },
|
|
7747
|
+
relation: left.relation === "exact" && right.relation === "exact" ? "exact" : "superset"
|
|
7748
|
+
};
|
|
7749
|
+
}
|
|
7750
|
+
return left.condition ? left : right;
|
|
7751
|
+
}
|
|
7752
|
+
case "GROUP":
|
|
7753
|
+
return extractAndLeafPlan(where.expr, classify);
|
|
7754
|
+
case "NULL_CHECK":
|
|
7755
|
+
case "NOT":
|
|
7756
|
+
case "EXISTS":
|
|
7757
|
+
case "BOOLEAN":
|
|
7758
|
+
return { condition: null, relation: null };
|
|
7759
|
+
}
|
|
7760
|
+
}
|
|
7761
|
+
function extractAndLeaves(where, accept) {
|
|
7762
|
+
switch (where.type) {
|
|
7763
|
+
case "BINARY":
|
|
7764
|
+
return accept(where) ? where : null;
|
|
7765
|
+
case "LOGICAL":
|
|
7766
|
+
if (where.op !== "AND") return null;
|
|
7767
|
+
{
|
|
7768
|
+
const left = extractAndLeaves(where.left, accept);
|
|
7769
|
+
const right = extractAndLeaves(where.right, accept);
|
|
7770
|
+
if (left && right) return { ...where, left, right };
|
|
7771
|
+
return left ?? right ?? null;
|
|
7772
|
+
}
|
|
7773
|
+
case "GROUP":
|
|
7774
|
+
return extractAndLeaves(where.expr, accept);
|
|
7775
|
+
case "NULL_CHECK":
|
|
7776
|
+
case "NOT":
|
|
7777
|
+
case "EXISTS":
|
|
7778
|
+
case "BOOLEAN":
|
|
7779
|
+
return null;
|
|
7780
|
+
}
|
|
7781
|
+
}
|
|
7782
|
+
function classifySafeComparison(expr, options) {
|
|
7783
|
+
if (isKlikeComparison(expr, options)) return "exact";
|
|
7784
|
+
if (isSafeIdComparison(expr, options)) return "exact";
|
|
7785
|
+
if (expr.left.type !== "FIELD" || !isTargetField(expr.left, options)) return "unsafe";
|
|
7786
|
+
const fieldType = options.fieldTypes?.get(expr.left.field);
|
|
7787
|
+
if (fieldType === void 0 || fieldType.startsWith("KSQL_")) return "unsafe";
|
|
7788
|
+
const capability = classifyWhereCapability(expr, (field) => {
|
|
7789
|
+
if (!isTargetField(field, options)) return void 0;
|
|
7790
|
+
const type = options.fieldTypes?.get(field.field);
|
|
7791
|
+
return type === void 0 ? void 0 : resolveFieldSemantics({ fieldType: type });
|
|
7792
|
+
});
|
|
7793
|
+
if (capability.capability !== "EXACT_PUSHDOWN") return "unsafe";
|
|
7794
|
+
return classifySupportedLeaf(expr, {
|
|
7795
|
+
fieldCode: expr.left.field,
|
|
7796
|
+
fieldType,
|
|
7797
|
+
fieldOptions: options.fieldOptions?.get(expr.left.field)
|
|
7798
|
+
});
|
|
7799
|
+
}
|
|
7800
|
+
function isKlikeComparison(expr, options) {
|
|
7801
|
+
if (options.allowKlike === false) return false;
|
|
7802
|
+
if (expr.op !== "KLIKE" && expr.op !== "NOT_KLIKE") return false;
|
|
7803
|
+
if (expr.left.type !== "FIELD" || !isTargetField(expr.left, options)) return false;
|
|
7804
|
+
return expr.right.type === "STRING" || options.allowUnresolvedKlikeVariables === true && expr.right.type === "VARIABLE";
|
|
7805
|
+
}
|
|
7806
|
+
function isSafeIdComparison(expr, options) {
|
|
7807
|
+
if (!isTargetIdField(expr.left, options)) return false;
|
|
7808
|
+
if (expr.right.type !== "NUMBER") return false;
|
|
7809
|
+
return expr.op === "=" || expr.op === ">" || expr.op === "<" || expr.op === ">=" || expr.op === "<=";
|
|
7810
|
+
}
|
|
7811
|
+
function isTargetIdField(field, options) {
|
|
7812
|
+
if (field.type !== "FIELD" || field.field !== "$id") return false;
|
|
7813
|
+
const targetAlias = options.tableAlias ?? null;
|
|
7814
|
+
if (field.tableAlias === targetAlias) return true;
|
|
7815
|
+
return options.allowUnqualifiedFields === true && field.tableAlias === null;
|
|
7816
|
+
}
|
|
7817
|
+
function isTargetField(field, options) {
|
|
7818
|
+
const targetAlias = options.tableAlias ?? null;
|
|
7819
|
+
if (field.tableAlias === targetAlias) return true;
|
|
7820
|
+
return options.allowUnqualifiedFields === true && field.tableAlias === null;
|
|
7821
|
+
}
|
|
7822
|
+
|
|
7823
|
+
// src/core/optimization/klikePushdownPlan.ts
|
|
7824
|
+
function buildSingleTableKlikePushdownPlan(where, options = {}) {
|
|
7825
|
+
const extracted = where !== null && options.extractCondition !== false ? extractSafePushdownPlan(where, options) : { condition: null, relation: null };
|
|
7826
|
+
const { condition, relation } = extracted;
|
|
7827
|
+
const appliedKlikes = /* @__PURE__ */ new Set();
|
|
7828
|
+
collectKlikes(condition, appliedKlikes);
|
|
7829
|
+
const allKlikes = /* @__PURE__ */ new Set();
|
|
7830
|
+
collectKlikes(where, allKlikes);
|
|
7831
|
+
return { condition, relation, appliedKlikes, allKlikes: [...allKlikes] };
|
|
7832
|
+
}
|
|
7833
|
+
function buildKlikePushdownPlan(stmt, options = {}) {
|
|
7834
|
+
const joinsAreSafeForKlike = stmt.joins.every((join2) => join2.type === "INNER");
|
|
7835
|
+
const common = {
|
|
7836
|
+
allowKlike: joinsAreSafeForKlike,
|
|
7837
|
+
allowUnresolvedKlikeVariables: options.allowUnresolvedVariables
|
|
7838
|
+
};
|
|
7839
|
+
const mainIsPhysical = !stmt.from.subtableCode && stmt.from.cteName === null;
|
|
7840
|
+
const mainHasUsableAlias = stmt.joins.length === 0 || stmt.from.alias !== null;
|
|
7841
|
+
const mainPlan = buildSingleTableKlikePushdownPlan(stmt.where, {
|
|
7842
|
+
...common,
|
|
7843
|
+
extractCondition: mainIsPhysical && mainHasUsableAlias && joinsAreSafeForKlike,
|
|
7844
|
+
tableAlias: stmt.from.alias ?? void 0,
|
|
7845
|
+
allowUnqualifiedFields: stmt.joins.length === 0,
|
|
7846
|
+
fieldTypes: options.fieldTypesByApp?.get(stmt.from.appId),
|
|
7847
|
+
fieldOptions: options.fieldOptionsByApp?.get(stmt.from.appId)
|
|
7848
|
+
});
|
|
7849
|
+
const mainCondition = mainPlan.condition;
|
|
7850
|
+
const joinConditions = /* @__PURE__ */ new Map();
|
|
7851
|
+
const joinRelations = /* @__PURE__ */ new Map();
|
|
7852
|
+
if (stmt.where !== null) {
|
|
7853
|
+
for (const join2 of stmt.joins) {
|
|
7854
|
+
if (join2.type !== "INNER" || !join2.table.alias || join2.table.subtableCode || join2.table.cteName !== null) continue;
|
|
7855
|
+
const extracted = extractSafePushdownPlan(stmt.where, {
|
|
7856
|
+
...common,
|
|
7857
|
+
tableAlias: join2.table.alias,
|
|
7858
|
+
fieldTypes: options.fieldTypesByApp?.get(join2.table.appId),
|
|
7859
|
+
fieldOptions: options.fieldOptionsByApp?.get(join2.table.appId)
|
|
7860
|
+
});
|
|
7861
|
+
if (extracted.condition !== null && extracted.relation !== null) {
|
|
7862
|
+
joinConditions.set(join2.table.alias, extracted.condition);
|
|
7863
|
+
joinRelations.set(join2.table.alias, extracted.relation);
|
|
7864
|
+
}
|
|
7865
|
+
}
|
|
7866
|
+
}
|
|
7867
|
+
const appliedKlikes = new Set(mainPlan.appliedKlikes);
|
|
7868
|
+
for (const condition of joinConditions.values()) collectKlikes(condition, appliedKlikes);
|
|
7869
|
+
return {
|
|
7870
|
+
mainCondition,
|
|
7871
|
+
mainRelation: mainPlan.relation,
|
|
7872
|
+
joinConditions,
|
|
7873
|
+
joinRelations,
|
|
7874
|
+
appliedKlikes,
|
|
7875
|
+
allKlikes: mainPlan.allKlikes
|
|
7876
|
+
};
|
|
7877
|
+
}
|
|
7878
|
+
function unappliedKlikes(plan) {
|
|
7879
|
+
return plan.allKlikes.filter((expr) => !plan.appliedKlikes.has(expr));
|
|
7880
|
+
}
|
|
7881
|
+
function collectKlikes(where, out) {
|
|
7882
|
+
if (where === null) return;
|
|
7883
|
+
if (isKlike(where)) {
|
|
7884
|
+
out.add(where);
|
|
7885
|
+
return;
|
|
7886
|
+
}
|
|
7887
|
+
switch (where.type) {
|
|
7888
|
+
case "LOGICAL":
|
|
7889
|
+
collectKlikes(where.left, out);
|
|
7890
|
+
collectKlikes(where.right, out);
|
|
7891
|
+
return;
|
|
7892
|
+
case "NOT":
|
|
7893
|
+
case "GROUP":
|
|
7894
|
+
collectKlikes(where.expr, out);
|
|
7895
|
+
return;
|
|
7896
|
+
case "BINARY":
|
|
7897
|
+
case "NULL_CHECK":
|
|
7898
|
+
case "EXISTS":
|
|
7899
|
+
case "BOOLEAN":
|
|
7900
|
+
return;
|
|
7901
|
+
}
|
|
7902
|
+
}
|
|
7903
|
+
|
|
7904
|
+
// src/core/optimization/relativeDateFullScanExactPlan.ts
|
|
7905
|
+
function buildRelativeDateFullScanExactPlan(input) {
|
|
7906
|
+
const {
|
|
7907
|
+
select,
|
|
7908
|
+
selectMode,
|
|
7909
|
+
capability,
|
|
7910
|
+
context,
|
|
7911
|
+
serializedWholeWhere,
|
|
7912
|
+
relativeFunctionNames
|
|
7913
|
+
} = input;
|
|
7914
|
+
if (select.where === null) return null;
|
|
7915
|
+
if (select.from.appId <= 0 || select.from.cteName !== null) return null;
|
|
7916
|
+
if (select.from.subtableCode) return null;
|
|
7917
|
+
if (select.joins.length > 0) return null;
|
|
7918
|
+
if (!context.allowFullScanExact) return null;
|
|
7919
|
+
if (select.orderMode === "KINTONE_NATIVE") return null;
|
|
7920
|
+
const hasCanonicalOrder2 = select.orderMode === "CANONICAL" && select.orderBy.length > 0;
|
|
7921
|
+
if (selectMode !== "FULL_SCAN" && !hasCanonicalOrder2) return null;
|
|
7922
|
+
if (capability.capability !== "EXACT_PUSHDOWN") return null;
|
|
7923
|
+
const occurrences = serverOnlyFunctionOccurrencesInWhere(select.where);
|
|
7924
|
+
if (occurrences.length === 0) return null;
|
|
7925
|
+
if (!sameOccurrenceList(occurrences, relativeFunctionNames)) return null;
|
|
7926
|
+
if (serializedWholeWhere === null || !serializedMultisetContains(serializedWholeWhere, occurrences)) {
|
|
7927
|
+
return null;
|
|
7928
|
+
}
|
|
7929
|
+
const prefilterPlan = {
|
|
7930
|
+
prefilterWhere: select.where,
|
|
7931
|
+
residualWhere: null,
|
|
7932
|
+
exactRelativeLeaves: collectExactServerFunctionLeaves(select.where),
|
|
7933
|
+
relativeFunctionNames: new Set(occurrences),
|
|
7934
|
+
appliedKlikes: /* @__PURE__ */ new Set(),
|
|
7935
|
+
capability: capability.capability,
|
|
7936
|
+
reasons: capability.reasons
|
|
7937
|
+
};
|
|
7938
|
+
const plan = {
|
|
7939
|
+
allowForm: "FULL_SCAN_EXACT",
|
|
7940
|
+
clientWhereEvaluation: false,
|
|
7941
|
+
serializedWholeWhere,
|
|
7942
|
+
prefilterPlan
|
|
7943
|
+
};
|
|
7944
|
+
assertRelativeDateFullScanExactPlan(plan, input, occurrences);
|
|
7945
|
+
return plan;
|
|
7946
|
+
}
|
|
7947
|
+
function assertRelativeDateFullScanExactPlan(plan, input, occurrences) {
|
|
7948
|
+
if (plan.allowForm !== "FULL_SCAN_EXACT") {
|
|
7949
|
+
throw new Error("FULL_SCAN_EXACT invariant: allowForm");
|
|
7950
|
+
}
|
|
7951
|
+
if (plan.clientWhereEvaluation !== false) {
|
|
7952
|
+
throw new Error("FULL_SCAN_EXACT invariant: clientWhereEvaluation");
|
|
7953
|
+
}
|
|
7954
|
+
if (input.capability.capability !== "EXACT_PUSHDOWN") {
|
|
7955
|
+
throw new Error("FULL_SCAN_EXACT invariant: capability");
|
|
7956
|
+
}
|
|
7957
|
+
if (input.select.where === null || plan.prefilterPlan.prefilterWhere !== input.select.where) {
|
|
7958
|
+
throw new Error("FULL_SCAN_EXACT invariant: whole WHERE identity");
|
|
7959
|
+
}
|
|
7960
|
+
if (plan.prefilterPlan.residualWhere !== null) {
|
|
7961
|
+
throw new Error("FULL_SCAN_EXACT invariant: residualWhere");
|
|
7962
|
+
}
|
|
7963
|
+
if (plan.prefilterPlan.capability !== "EXACT_PUSHDOWN") {
|
|
7964
|
+
throw new Error("FULL_SCAN_EXACT invariant: transport capability");
|
|
7965
|
+
}
|
|
7966
|
+
if (!serializedMultisetContains(plan.serializedWholeWhere, occurrences)) {
|
|
7967
|
+
throw new Error("FULL_SCAN_EXACT invariant: relative occurrence serialization");
|
|
7968
|
+
}
|
|
7969
|
+
}
|
|
7970
|
+
function serverOnlyFunctionOccurrencesInWhere(where) {
|
|
7971
|
+
const names = [];
|
|
7972
|
+
const visit = (node) => {
|
|
7973
|
+
if (Array.isArray(node)) {
|
|
7974
|
+
node.forEach(visit);
|
|
7975
|
+
return;
|
|
7976
|
+
}
|
|
7977
|
+
if (node === null || typeof node !== "object") return;
|
|
7978
|
+
const value = node;
|
|
7979
|
+
if (value["type"] === "SELECT") return;
|
|
7980
|
+
if (value["type"] === "KINTONE_FUNC" && typeof value["name"] === "string" && isServerOnlyWhereFunctionName(value["name"])) {
|
|
7981
|
+
names.push(value["name"]);
|
|
7982
|
+
return;
|
|
7983
|
+
}
|
|
7984
|
+
Object.values(value).forEach(visit);
|
|
7985
|
+
};
|
|
7986
|
+
visit(where);
|
|
7987
|
+
return names;
|
|
7988
|
+
}
|
|
7989
|
+
function collectExactServerFunctionLeaves(where) {
|
|
7990
|
+
const leaves = [];
|
|
7991
|
+
const visit = (node) => {
|
|
7992
|
+
switch (node.type) {
|
|
7993
|
+
case "BINARY":
|
|
7994
|
+
if (node.right.type === "KINTONE_FUNC" && isServerOnlyWhereFunctionName(node.right.name) || node.right.type === "IN_LIST" && node.right.values.length === 1 && node.right.values[0].type === "KINTONE_FUNC" && isServerOnlyWhereFunctionName(node.right.values[0].name)) {
|
|
7995
|
+
leaves.push(node);
|
|
7996
|
+
}
|
|
7997
|
+
return;
|
|
7998
|
+
case "LOGICAL":
|
|
7999
|
+
visit(node.left);
|
|
8000
|
+
visit(node.right);
|
|
8001
|
+
return;
|
|
8002
|
+
case "NOT":
|
|
8003
|
+
case "GROUP":
|
|
8004
|
+
visit(node.expr);
|
|
8005
|
+
return;
|
|
8006
|
+
case "EXISTS":
|
|
8007
|
+
case "NULL_CHECK":
|
|
8008
|
+
case "BOOLEAN":
|
|
8009
|
+
return;
|
|
8010
|
+
}
|
|
8011
|
+
};
|
|
8012
|
+
visit(where);
|
|
8013
|
+
return leaves;
|
|
8014
|
+
}
|
|
8015
|
+
function sameOccurrenceList(actual, expected) {
|
|
8016
|
+
return actual.length === expected.length && actual.every((name, index) => name === expected[index]);
|
|
8017
|
+
}
|
|
8018
|
+
function serializedMultisetContains(query, expectedNames) {
|
|
8019
|
+
const expected = /* @__PURE__ */ new Map();
|
|
8020
|
+
for (const name of expectedNames) {
|
|
8021
|
+
if (!isServerOnlyWhereFunctionName(name)) return false;
|
|
8022
|
+
expected.set(name, (expected.get(name) ?? 0) + 1);
|
|
8023
|
+
}
|
|
8024
|
+
for (const [name, count] of expected) {
|
|
8025
|
+
const matches = query.match(new RegExp(`\\b${name}\\s*\\(`, "g"));
|
|
8026
|
+
if ((matches?.length ?? 0) < count) return false;
|
|
8027
|
+
}
|
|
8028
|
+
return true;
|
|
8029
|
+
}
|
|
8030
|
+
|
|
8031
|
+
// src/core/klikeValidation.ts
|
|
8032
|
+
var KlikeValidationError = class extends Error {
|
|
8033
|
+
constructor(message) {
|
|
8034
|
+
super(`ArgumentError: ${message}`);
|
|
8035
|
+
this.name = "ArgumentError";
|
|
8036
|
+
}
|
|
8037
|
+
};
|
|
8038
|
+
function validateKlikeStatement(stmt) {
|
|
8039
|
+
validateStatement(stmt);
|
|
8040
|
+
}
|
|
8041
|
+
function validateKlikePushdownPlan(plan) {
|
|
8042
|
+
if (unappliedKlikes(plan).length > 0) {
|
|
8043
|
+
throw new KlikeValidationError(
|
|
8044
|
+
"FULL_SCAN \u306E KLIKE / NOT KLIKE \u3092\u5B89\u5168\u306B\u62BC\u3057\u4E0B\u3052\u3089\u308C\u307E\u305B\u3093\u3002OR / NOT \u914D\u4E0B\u3001CTE\u30FB\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u3001LEFT / RIGHT JOIN \u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
8045
|
+
);
|
|
8046
|
+
}
|
|
8047
|
+
}
|
|
8048
|
+
function validateStatement(stmt) {
|
|
8049
|
+
switch (stmt.type) {
|
|
8050
|
+
case "SELECT":
|
|
8051
|
+
validateSelect(stmt);
|
|
8052
|
+
return;
|
|
8053
|
+
case "UNION":
|
|
8054
|
+
validateUnion(stmt);
|
|
8055
|
+
return;
|
|
8056
|
+
case "WITH":
|
|
8057
|
+
validateWith(stmt);
|
|
8058
|
+
return;
|
|
8059
|
+
case "EXPLAIN":
|
|
8060
|
+
validateStatement(stmt.query);
|
|
8061
|
+
return;
|
|
8062
|
+
case "CREATE_TEMP_TABLE":
|
|
8063
|
+
validateSelectLike(stmt.query);
|
|
8064
|
+
return;
|
|
8065
|
+
case "SET_VARIABLE":
|
|
8066
|
+
case "DECLARE_VARIABLE":
|
|
8067
|
+
case "ASSERT":
|
|
8068
|
+
validateNestedSelects(stmt);
|
|
8069
|
+
return;
|
|
8070
|
+
case "UPDATE":
|
|
8071
|
+
if (stmt.applyBlocks?.length && !isSinglePositiveRecordIdWhere(stmt.where) && whereHasKlike(stmt.where)) {
|
|
8072
|
+
validateKlikeWhereExpressions(stmt.where);
|
|
8073
|
+
validateNestedSelects(stmt);
|
|
8074
|
+
return;
|
|
8075
|
+
}
|
|
8076
|
+
if (containsKlike(stmt) && stmt.subtableCode) {
|
|
8077
|
+
throw new KlikeValidationError(
|
|
8078
|
+
"KLIKE / NOT KLIKE \u306F\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB UPDATE \u306E WHERE \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
8079
|
+
);
|
|
8080
|
+
}
|
|
8081
|
+
if (!stmt.applyBlocks?.length && !stmt.subtableCode && whereHasKlike(stmt.where) && !containsKlikeOutsideWhereAndNestedSelects(stmt, stmt.where)) {
|
|
8082
|
+
validateKlikeWhereExpressions(stmt.where);
|
|
8083
|
+
validateNestedSelects(stmt);
|
|
8084
|
+
return;
|
|
8085
|
+
}
|
|
8086
|
+
if (containsKlike(stmt)) {
|
|
7378
8087
|
throw new KlikeValidationError(
|
|
7379
8088
|
"KLIKE / NOT KLIKE \u306F\u901A\u5E38\u89AA UPDATE \u306E WHERE\u3001\u307E\u305F\u306F APPLY \u8907\u6570\u89AA UPDATE \u306E\u5B89\u5168\u306A\u89AA WHERE \u3060\u3051\u3067\u4F7F\u7528\u3067\u304D\u307E\u3059"
|
|
7380
8089
|
);
|
|
@@ -8250,72 +8959,6 @@ function analyzeBatch(statements) {
|
|
|
8250
8959
|
};
|
|
8251
8960
|
}
|
|
8252
8961
|
|
|
8253
|
-
// src/core/fieldSemantics.ts
|
|
8254
|
-
var STRING_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
8255
|
-
"SINGLE_LINE_TEXT",
|
|
8256
|
-
"MULTI_LINE_TEXT",
|
|
8257
|
-
"RICH_TEXT",
|
|
8258
|
-
"LINK",
|
|
8259
|
-
"DATE",
|
|
8260
|
-
"TIME",
|
|
8261
|
-
"DATETIME",
|
|
8262
|
-
"CREATED_TIME",
|
|
8263
|
-
"UPDATED_TIME",
|
|
8264
|
-
"CREATOR",
|
|
8265
|
-
"MODIFIER"
|
|
8266
|
-
]);
|
|
8267
|
-
var OPTION_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
8268
|
-
"DROP_DOWN",
|
|
8269
|
-
"RADIO_BUTTON",
|
|
8270
|
-
"CHECK_BOX",
|
|
8271
|
-
"MULTI_SELECT",
|
|
8272
|
-
"STATUS"
|
|
8273
|
-
]);
|
|
8274
|
-
function resolveFieldSemantics(source) {
|
|
8275
|
-
let compareMode;
|
|
8276
|
-
if (source.fieldType === "RECORD_NUMBER" || source.fieldType === "__ID__") {
|
|
8277
|
-
compareMode = "recordNumber";
|
|
8278
|
-
} else if (source.fieldType === "NUMBER") {
|
|
8279
|
-
compareMode = "number";
|
|
8280
|
-
} else if (source.fieldType === "CALC") {
|
|
8281
|
-
compareMode = source.sortKind === "number" ? "number" : "string";
|
|
8282
|
-
} else if (OPTION_FIELD_TYPES.has(source.fieldType)) {
|
|
8283
|
-
compareMode = "option";
|
|
8284
|
-
} else if (STRING_FIELD_TYPES.has(source.fieldType)) {
|
|
8285
|
-
compareMode = "string";
|
|
8286
|
-
} else {
|
|
8287
|
-
compareMode = "unsupported";
|
|
8288
|
-
}
|
|
8289
|
-
const optionOrder = source.optionOrder ? new Map(Object.entries(source.optionOrder)) : void 0;
|
|
8290
|
-
return {
|
|
8291
|
-
fieldType: source.fieldType,
|
|
8292
|
-
compareMode,
|
|
8293
|
-
inSubtable: source.inSubtable === true,
|
|
8294
|
-
requiresCollectionOperators: source.inSubtable === true || source.requiresCollectionOperators === true,
|
|
8295
|
-
...optionOrder && optionOrder.size > 0 ? { optionOrder } : {}
|
|
8296
|
-
};
|
|
8297
|
-
}
|
|
8298
|
-
function syntheticSemantics(compareMode, fieldType = compareMode === "number" ? "KSQL_NUMBER" : "KSQL_STRING") {
|
|
8299
|
-
return { fieldType, compareMode, inSubtable: false, requiresCollectionOperators: false };
|
|
8300
|
-
}
|
|
8301
|
-
function withFieldSemanticSource(semantics, appId, fieldCode) {
|
|
8302
|
-
return { ...semantics, source: { appId, fieldCode } };
|
|
8303
|
-
}
|
|
8304
|
-
function fieldSemanticsEqual(left, right) {
|
|
8305
|
-
if (left === right) return true;
|
|
8306
|
-
if (!left || !right) return false;
|
|
8307
|
-
if (left.fieldType !== right.fieldType || left.compareMode !== right.compareMode || left.inSubtable !== right.inSubtable || left.requiresCollectionOperators !== right.requiresCollectionOperators) return false;
|
|
8308
|
-
if (left.source?.appId !== right.source?.appId || left.source?.fieldCode !== right.source?.fieldCode) return false;
|
|
8309
|
-
const a = left.optionOrder;
|
|
8310
|
-
const b = right.optionOrder;
|
|
8311
|
-
if (a === b) return true;
|
|
8312
|
-
if (!a || !b || a.size !== b.size) return false;
|
|
8313
|
-
for (const [key, value] of a) {
|
|
8314
|
-
if (b.get(key) !== value) return false;
|
|
8315
|
-
}
|
|
8316
|
-
return true;
|
|
8317
|
-
}
|
|
8318
|
-
|
|
8319
8962
|
// src/core/scalarCompare.ts
|
|
8320
8963
|
function compareCodePointStrings(left, right) {
|
|
8321
8964
|
const a = left[Symbol.iterator]();
|
|
@@ -10972,231 +11615,23 @@ function resolveExistingValidationTargets(stmt, fieldInfos) {
|
|
|
10972
11615
|
children.forEach((child) => add(child, code));
|
|
10973
11616
|
continue;
|
|
10974
11617
|
}
|
|
10975
|
-
if (top) {
|
|
10976
|
-
add(top);
|
|
10977
|
-
continue;
|
|
10978
|
-
}
|
|
10979
|
-
if ([...childrenByTable.values()].some((children) => children.some((field) => field.code === code))) {
|
|
10980
|
-
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`);
|
|
10981
|
-
}
|
|
10982
|
-
throw new Error(`ArgumentError: VALIDATE \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${code} \u306F\u5B58\u5728\u3057\u307E\u305B\u3093\u3002`);
|
|
10983
|
-
}
|
|
10984
|
-
return result;
|
|
10985
|
-
}
|
|
10986
|
-
function renderExistingValidationValue(raw, fieldType) {
|
|
10987
|
-
return isEmptyDmlValue(raw) ? "" : renderValidationValue(normalizeRaw(raw, fieldType));
|
|
10988
|
-
}
|
|
10989
|
-
|
|
10990
|
-
// src/core/postImageValidation.ts
|
|
10991
|
-
var NON_AUDIT_SYSTEM_TYPES = /* @__PURE__ */ new Set([
|
|
10992
|
-
"CALC",
|
|
10993
|
-
"RECORD_NUMBER",
|
|
10994
|
-
"CREATOR",
|
|
10995
|
-
"CREATED_TIME",
|
|
10996
|
-
"MODIFIER",
|
|
10997
|
-
"UPDATED_TIME",
|
|
10998
|
-
"STATUS",
|
|
10999
|
-
"STATUS_ASSIGNEE",
|
|
11000
|
-
"CATEGORY",
|
|
11001
|
-
"REFERENCE_TABLE"
|
|
11002
|
-
]);
|
|
11003
|
-
var POST_IMAGE_VALIDATION_SUFFIX_COLUMNS = VALIDATION_META_COLUMNS;
|
|
11004
|
-
function buildPostImageFieldIndex(fieldInfos, payloadFields = ["$id"]) {
|
|
11005
|
-
const metadata = buildValidationFieldMetadataIndex(fieldInfos);
|
|
11006
|
-
const topLevel = metadata.topLevel.filter((field) => field.fieldType !== "SUBTABLE" && field.fieldType !== "FILE" && !NON_AUDIT_SYSTEM_TYPES.has(field.fieldType));
|
|
11007
|
-
const subtables = /* @__PURE__ */ new Map();
|
|
11008
|
-
for (const [tableCode, fields] of metadata.childrenByTable) {
|
|
11009
|
-
subtables.set(tableCode, fields.filter((field) => field.fieldType !== "FILE"));
|
|
11010
|
-
}
|
|
11011
|
-
return {
|
|
11012
|
-
topLevel,
|
|
11013
|
-
subtables,
|
|
11014
|
-
payloadFields: ["$id", ...new Set(payloadFields.filter((field) => field !== "$id"))]
|
|
11015
|
-
};
|
|
11016
|
-
}
|
|
11017
|
-
function postImageNeedsNumberPrecision(record, fieldIndex) {
|
|
11018
|
-
if (fieldIndex.topLevel.some((field) => field.fieldType === "NUMBER" && field.code in record)) return true;
|
|
11019
|
-
for (const [tableCode, children] of fieldIndex.subtables) {
|
|
11020
|
-
if (!children.some((field) => field.fieldType === "NUMBER")) continue;
|
|
11021
|
-
const rows = record[tableCode]?.value;
|
|
11022
|
-
if (!Array.isArray(rows)) continue;
|
|
11023
|
-
for (const row of rows) {
|
|
11024
|
-
const values = row?.value;
|
|
11025
|
-
if (children.some((field) => field.fieldType === "NUMBER" && !!values && field.code in values)) return true;
|
|
11026
|
-
}
|
|
11027
|
-
}
|
|
11028
|
-
return false;
|
|
11029
|
-
}
|
|
11030
|
-
function validatePostImage(record, fieldIndex, numberPrecision, statementNumber, parentRowNumber = 1, operation = "UPDATE") {
|
|
11031
|
-
const normalizedRecord = cloneRecord(record);
|
|
11032
|
-
const errors = [];
|
|
11033
|
-
const invalidRowNumbers = /* @__PURE__ */ new Set();
|
|
11034
|
-
const columns = [...fieldIndex.payloadFields, ...POST_IMAGE_VALIDATION_SUFFIX_COLUMNS];
|
|
11035
|
-
const parentId = renderValidationValue(record["$id"]?.value);
|
|
11036
|
-
const appendError = (field, raw, validation, locator) => {
|
|
11037
|
-
invalidRowNumbers.add(parentRowNumber);
|
|
11038
|
-
const row = {};
|
|
11039
|
-
for (const code of fieldIndex.payloadFields) {
|
|
11040
|
-
row[code] = code === "$id" ? parentId : renderValidationValue(record[code]?.value);
|
|
11041
|
-
}
|
|
11042
|
-
row["$err_statement"] = String(statementNumber);
|
|
11043
|
-
row["$err_operation"] = operation;
|
|
11044
|
-
row["$err_row"] = String(parentRowNumber);
|
|
11045
|
-
row["$err_field"] = field.code;
|
|
11046
|
-
row["$err_code"] = validation.code;
|
|
11047
|
-
row["$err_message"] = validation.message;
|
|
11048
|
-
row["$err_value"] = renderExistingValidationValue(raw, field.fieldType);
|
|
11049
|
-
row["$err_subtable"] = locator?.subtable ?? "";
|
|
11050
|
-
row["$err_subrow"] = locator ? String(locator.subrow) : "";
|
|
11051
|
-
row["$err_subrow_id"] = locator?.subrowId ?? "";
|
|
11052
|
-
errors.push(row);
|
|
11053
|
-
};
|
|
11054
|
-
for (const field of fieldIndex.topLevel) {
|
|
11055
|
-
const raw = record[field.code]?.value;
|
|
11056
|
-
const result = validateAndNormalizeDmlValue(raw, field, numberPrecision);
|
|
11057
|
-
if (!result.ok) appendError(field, raw, result);
|
|
11058
|
-
else normalizedRecord[field.code] = { value: preserveCodeObjects(raw, field.fieldType, result.value) };
|
|
11059
|
-
}
|
|
11060
|
-
for (const [tableCode, children] of fieldIndex.subtables) {
|
|
11061
|
-
const sourceRows2 = record[tableCode]?.value;
|
|
11062
|
-
if (!Array.isArray(sourceRows2)) continue;
|
|
11063
|
-
const normalizedRows = normalizedRecord[tableCode]?.value;
|
|
11064
|
-
for (let rowIndex = 0; rowIndex < sourceRows2.length; rowIndex++) {
|
|
11065
|
-
const sourceRow = sourceRows2[rowIndex];
|
|
11066
|
-
const normalizedRow = normalizedRows[rowIndex];
|
|
11067
|
-
for (const field of children) {
|
|
11068
|
-
const raw = sourceRow.value?.[field.code]?.value;
|
|
11069
|
-
const result = validateAndNormalizeDmlValue(raw, field, numberPrecision);
|
|
11070
|
-
if (!result.ok) appendError(field, raw, result, buildValidationCellLocator(tableCode, rowIndex, sourceRow));
|
|
11071
|
-
else {
|
|
11072
|
-
normalizedRow.value ??= {};
|
|
11073
|
-
normalizedRow.value[field.code] = { value: preserveCodeObjects(raw, field.fieldType, result.value) };
|
|
11074
|
-
}
|
|
11075
|
-
}
|
|
11076
|
-
}
|
|
11077
|
-
}
|
|
11078
|
-
return {
|
|
11079
|
-
normalizedRecord,
|
|
11080
|
-
errors,
|
|
11081
|
-
columns,
|
|
11082
|
-
invalidRows: invalidRowNumbers.size,
|
|
11083
|
-
invalidRowNumbers,
|
|
11084
|
-
errorCount: errors.length
|
|
11085
|
-
};
|
|
11086
|
-
}
|
|
11087
|
-
function preserveCodeObjects(raw, fieldType, normalized) {
|
|
11088
|
-
return ["USER_SELECT", "ORGANIZATION_SELECT", "GROUP_SELECT"].includes(fieldType) && Array.isArray(raw) && raw.every((item) => typeof item === "object" && item !== null && "code" in item) ? raw : normalized;
|
|
11089
|
-
}
|
|
11090
|
-
function cloneRecord(record) {
|
|
11091
|
-
const clone = {};
|
|
11092
|
-
for (const [code, cell] of Object.entries(record)) {
|
|
11093
|
-
const value = cell?.value;
|
|
11094
|
-
clone[code] = { value: Array.isArray(value) ? value.map((item) => {
|
|
11095
|
-
if (item === null || typeof item !== "object") return item;
|
|
11096
|
-
const row = item;
|
|
11097
|
-
if (!row.value) return { ...row };
|
|
11098
|
-
return {
|
|
11099
|
-
...row,
|
|
11100
|
-
value: Object.fromEntries(Object.entries(row.value).map(([field, child]) => [field, { ...child }]))
|
|
11101
|
-
};
|
|
11102
|
-
}) : value };
|
|
11103
|
-
}
|
|
11104
|
-
return clone;
|
|
11105
|
-
}
|
|
11106
|
-
|
|
11107
|
-
// src/core/applyPatchPrepare.ts
|
|
11108
|
-
async function prepareApplyPatchWrite(input) {
|
|
11109
|
-
const {
|
|
11110
|
-
statement,
|
|
11111
|
-
snapshots,
|
|
11112
|
-
fieldInfos,
|
|
11113
|
-
metadata,
|
|
11114
|
-
dmlMaxRows,
|
|
11115
|
-
dmlMaxSubtableRows,
|
|
11116
|
-
statementNumber = 1
|
|
11117
|
-
} = input;
|
|
11118
|
-
assertPositiveLimit(dmlMaxRows, "dmlMaxRows");
|
|
11119
|
-
assertPositiveLimit(dmlMaxSubtableRows, "dmlMaxSubtableRows");
|
|
11120
|
-
for (const snapshot of snapshots) requireRevision(snapshot);
|
|
11121
|
-
const rawPlans = buildApplyPatchPlans(statement, snapshots, fieldInfos, metadata);
|
|
11122
|
-
const fieldIndex = buildPostImageFieldIndex(
|
|
11123
|
-
fieldInfos,
|
|
11124
|
-
statement.assignments.map((assignment) => assignment.field)
|
|
11125
|
-
);
|
|
11126
|
-
const needsNumberPrecision = rawPlans.some(
|
|
11127
|
-
(plan) => postImageNeedsNumberPrecision(plan.postImage, fieldIndex)
|
|
11128
|
-
);
|
|
11129
|
-
if (needsNumberPrecision && !input.loadNumberPrecision) {
|
|
11130
|
-
throw new Error("InternalError: APPLY number precision loader is required for NUMBER post-images.");
|
|
11131
|
-
}
|
|
11132
|
-
const numberPrecision = needsNumberPrecision ? await input.loadNumberPrecision() : void 0;
|
|
11133
|
-
const validationResults = rawPlans.map(
|
|
11134
|
-
(plan, index) => validatePostImage(
|
|
11135
|
-
plan.postImage,
|
|
11136
|
-
fieldIndex,
|
|
11137
|
-
numberPrecision,
|
|
11138
|
-
statementNumber,
|
|
11139
|
-
input.parentRowNumbers?.[index] ?? index + 1
|
|
11140
|
-
)
|
|
11141
|
-
);
|
|
11142
|
-
if (!statement.validateOnly) {
|
|
11143
|
-
const errors = validationResults.flatMap((validation) => validation.errors);
|
|
11144
|
-
if (errors.length > 0) {
|
|
11145
|
-
throw new Error(`ArgumentError: APPLY post-image validation failed: ${JSON.stringify({
|
|
11146
|
-
columns: validationResults[0]?.columns ?? [],
|
|
11147
|
-
errors
|
|
11148
|
-
})}`);
|
|
11149
|
-
}
|
|
11150
|
-
}
|
|
11151
|
-
const parentRows = rawPlans.length;
|
|
11152
|
-
const subtableRows = rawPlans.reduce((sum, plan) => sum + plan.changedSubtableRows, 0);
|
|
11153
|
-
const wouldExceed = parentRows > dmlMaxRows || subtableRows > dmlMaxSubtableRows;
|
|
11154
|
-
if (!statement.validateOnly && parentRows > dmlMaxRows) {
|
|
11155
|
-
throw new Error(`ArgumentError: APPLY parent rows (${parentRows}) exceed dmlMaxRows (${dmlMaxRows}).`);
|
|
11156
|
-
}
|
|
11157
|
-
if (!statement.validateOnly && subtableRows > dmlMaxSubtableRows) {
|
|
11158
|
-
throw new Error(
|
|
11159
|
-
`ArgumentError: APPLY changed subtable rows (${subtableRows}) exceed dmlMaxSubtableRows (${dmlMaxSubtableRows}).`
|
|
11160
|
-
);
|
|
11161
|
-
}
|
|
11162
|
-
const plans = rawPlans.map(
|
|
11163
|
-
(plan, index) => normalizeApplyPatchPlan(plan, validationResults[index].normalizedRecord)
|
|
11164
|
-
);
|
|
11165
|
-
const records = plans.flatMap((plan) => applyPatchPlanToKintone(plan).records);
|
|
11166
|
-
const validations = validationResults.map((validation) => ({
|
|
11167
|
-
errors: validation.errors,
|
|
11168
|
-
columns: validation.columns,
|
|
11169
|
-
invalidRows: validation.invalidRows,
|
|
11170
|
-
errorCount: validation.errorCount
|
|
11171
|
-
}));
|
|
11172
|
-
return deepFreeze({
|
|
11173
|
-
plans,
|
|
11174
|
-
records,
|
|
11175
|
-
validations,
|
|
11176
|
-
guards: {
|
|
11177
|
-
revisionRequired: true,
|
|
11178
|
-
parentRows,
|
|
11179
|
-
dmlMaxRows,
|
|
11180
|
-
subtableRows,
|
|
11181
|
-
dmlMaxSubtableRows,
|
|
11182
|
-
wouldExceed
|
|
11618
|
+
if (top) {
|
|
11619
|
+
add(top);
|
|
11620
|
+
continue;
|
|
11183
11621
|
}
|
|
11184
|
-
|
|
11185
|
-
}
|
|
11186
|
-
|
|
11187
|
-
|
|
11188
|
-
throw new Error(`ArgumentError: ${name} must be a positive safe integer.`);
|
|
11622
|
+
if ([...childrenByTable.values()].some((children) => children.some((field) => field.code === code))) {
|
|
11623
|
+
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`);
|
|
11624
|
+
}
|
|
11625
|
+
throw new Error(`ArgumentError: VALIDATE \u306E\u30D5\u30A3\u30FC\u30EB\u30C9 ${code} \u306F\u5B58\u5728\u3057\u307E\u305B\u3093\u3002`);
|
|
11189
11626
|
}
|
|
11627
|
+
return result;
|
|
11190
11628
|
}
|
|
11191
|
-
function
|
|
11192
|
-
|
|
11193
|
-
seen.add(value);
|
|
11194
|
-
for (const child of Object.values(value)) deepFreeze(child, seen);
|
|
11195
|
-
return Object.freeze(value);
|
|
11629
|
+
function renderExistingValidationValue(raw, fieldType) {
|
|
11630
|
+
return isEmptyDmlValue(raw) ? "" : renderValidationValue(normalizeRaw(raw, fieldType));
|
|
11196
11631
|
}
|
|
11197
11632
|
|
|
11198
|
-
// src/core/
|
|
11199
|
-
var
|
|
11633
|
+
// src/core/postImageValidation.ts
|
|
11634
|
+
var NON_AUDIT_SYSTEM_TYPES = /* @__PURE__ */ new Set([
|
|
11200
11635
|
"CALC",
|
|
11201
11636
|
"RECORD_NUMBER",
|
|
11202
11637
|
"CREATOR",
|
|
@@ -11208,1362 +11643,1046 @@ var NON_WRITABLE_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
|
11208
11643
|
"CATEGORY",
|
|
11209
11644
|
"REFERENCE_TABLE"
|
|
11210
11645
|
]);
|
|
11211
|
-
|
|
11212
|
-
|
|
11213
|
-
|
|
11214
|
-
|
|
11215
|
-
const
|
|
11216
|
-
for (const
|
|
11217
|
-
|
|
11218
|
-
const childrenByTable = /* @__PURE__ */ new Map();
|
|
11219
|
-
for (const block of statement.applyBlocks ?? []) {
|
|
11220
|
-
if (targetTables.has(block.field)) return argument4(`APPLY has more than one block for table ${block.field}.`);
|
|
11221
|
-
const table = fieldInfos.find((field) => field.code === block.field && !field.inSubtable);
|
|
11222
|
-
if (!table || table.fieldType !== "SUBTABLE") return argument4(`APPLY target ${block.field} is not a SUBTABLE.`);
|
|
11223
|
-
const children = new Map(fieldInfos.filter((field) => field.inSubtable && field.subtableCode === block.field).map((field) => [field.code, field]));
|
|
11224
|
-
targetTables.set(block.field, table);
|
|
11225
|
-
childrenByTable.set(block.field, children);
|
|
11226
|
-
for (const operation of block.operations) {
|
|
11227
|
-
if (operation.kind !== "APPEND") return argument4(`APPLY INSERT supports APPEND only (${operation.kind}).`);
|
|
11228
|
-
const specified = /* @__PURE__ */ new Set();
|
|
11229
|
-
for (const code of operation.fields) {
|
|
11230
|
-
if (specified.has(code)) return argument4(`APPLY APPEND specifies child ${code} more than once.`);
|
|
11231
|
-
specified.add(code);
|
|
11232
|
-
assertWritable(code, block.field, fieldsByCode, children);
|
|
11233
|
-
}
|
|
11234
|
-
for (const row of operation.values) {
|
|
11235
|
-
if (row.length !== operation.fields.length) {
|
|
11236
|
-
return argument4(`APPLY APPEND for ${block.field} has ${row.length} values for ${operation.fields.length} fields.`);
|
|
11237
|
-
}
|
|
11238
|
-
}
|
|
11239
|
-
}
|
|
11240
|
-
}
|
|
11241
|
-
return { targetTables, targetMultiValueFields: /* @__PURE__ */ new Map(), childrenByTable, fieldsByCode };
|
|
11242
|
-
}
|
|
11243
|
-
function buildApplyInsertCandidates(statement, fieldInfos, metadata = resolveApplyInsertMetadata(statement, fieldInfos), parentRowNumbers) {
|
|
11244
|
-
const fieldTypes = new Map(fieldInfos.map((field) => [field.code, field.fieldType]));
|
|
11245
|
-
const parentRecords = insertToPostBatches(statement, fieldTypes).flatMap((batch) => batch.records);
|
|
11246
|
-
if (parentRowNumbers && parentRowNumbers.length !== parentRecords.length) {
|
|
11247
|
-
throw new Error("InternalError: APPLY create parent row number count differs from VALUES rows.");
|
|
11646
|
+
var POST_IMAGE_VALIDATION_SUFFIX_COLUMNS = VALIDATION_META_COLUMNS;
|
|
11647
|
+
function buildPostImageFieldIndex(fieldInfos, payloadFields = ["$id"]) {
|
|
11648
|
+
const metadata = buildValidationFieldMetadataIndex(fieldInfos);
|
|
11649
|
+
const topLevel = metadata.topLevel.filter((field) => field.fieldType !== "SUBTABLE" && field.fieldType !== "FILE" && !NON_AUDIT_SYSTEM_TYPES.has(field.fieldType));
|
|
11650
|
+
const subtables = /* @__PURE__ */ new Map();
|
|
11651
|
+
for (const [tableCode, fields] of metadata.childrenByTable) {
|
|
11652
|
+
subtables.set(tableCode, fields.filter((field) => field.fieldType !== "FILE"));
|
|
11248
11653
|
}
|
|
11249
|
-
|
|
11250
|
-
|
|
11251
|
-
|
|
11252
|
-
|
|
11253
|
-
|
|
11254
|
-
return { table: block.field, rows, addedRows: rows.length };
|
|
11255
|
-
});
|
|
11256
|
-
return parentRecords.map((parentRecord, index) => {
|
|
11257
|
-
const postImage = {};
|
|
11258
|
-
for (const field of fieldInfos) {
|
|
11259
|
-
if (field.inSubtable || field.fieldType === "SUBTABLE" || field.fieldType === "FILE" || field.writable === false || NON_WRITABLE_FIELD_TYPES.has(field.fieldType)) continue;
|
|
11260
|
-
postImage[field.code] = parentRecord[field.code] ?? { value: appendDefaultValue(field) };
|
|
11261
|
-
}
|
|
11262
|
-
const record = { ...parentRecord };
|
|
11263
|
-
for (const template of templates) {
|
|
11264
|
-
const rows = template.rows.map((row) => ({ value: row.value }));
|
|
11265
|
-
postImage[template.table] = { value: rows };
|
|
11266
|
-
record[template.table] = { value: rows };
|
|
11267
|
-
}
|
|
11268
|
-
return {
|
|
11269
|
-
parentRowNumber: parentRowNumbers?.[index] ?? index + 1,
|
|
11270
|
-
tables: templates,
|
|
11271
|
-
postImage,
|
|
11272
|
-
record
|
|
11273
|
-
};
|
|
11274
|
-
});
|
|
11654
|
+
return {
|
|
11655
|
+
topLevel,
|
|
11656
|
+
subtables,
|
|
11657
|
+
payloadFields: ["$id", ...new Set(payloadFields.filter((field) => field !== "$id"))]
|
|
11658
|
+
};
|
|
11275
11659
|
}
|
|
11276
|
-
|
|
11277
|
-
|
|
11278
|
-
|
|
11279
|
-
|
|
11280
|
-
|
|
11281
|
-
|
|
11282
|
-
|
|
11283
|
-
|
|
11284
|
-
|
|
11285
|
-
assertPositiveLimit2(dmlMaxSubtableRows, "dmlMaxSubtableRows");
|
|
11286
|
-
const metadata = input.metadata ?? resolveApplyInsertMetadata(statement, fieldInfos);
|
|
11287
|
-
const rawCandidates = buildApplyInsertCandidates(statement, fieldInfos, metadata, input.parentRowNumbers);
|
|
11288
|
-
const creatableFieldInfos = fieldInfos.filter(
|
|
11289
|
-
(field) => field.fieldType === "SUBTABLE" || field.fieldType !== "FILE" && field.writable !== false && !NON_WRITABLE_FIELD_TYPES.has(field.fieldType)
|
|
11290
|
-
);
|
|
11291
|
-
const fieldIndex = buildPostImageFieldIndex(creatableFieldInfos, statement.fields);
|
|
11292
|
-
const needsNumberPrecision = rawCandidates.some(
|
|
11293
|
-
(candidate) => postImageNeedsNumberPrecision(candidate.postImage, fieldIndex)
|
|
11294
|
-
);
|
|
11295
|
-
if (needsNumberPrecision && !input.loadNumberPrecision) {
|
|
11296
|
-
throw new Error("InternalError: APPLY number precision loader is required for NUMBER post-images.");
|
|
11297
|
-
}
|
|
11298
|
-
const numberPrecision = needsNumberPrecision ? await input.loadNumberPrecision() : void 0;
|
|
11299
|
-
const results = rawCandidates.map((candidate) => validatePostImage(
|
|
11300
|
-
candidate.postImage,
|
|
11301
|
-
fieldIndex,
|
|
11302
|
-
numberPrecision,
|
|
11303
|
-
statementNumber,
|
|
11304
|
-
candidate.parentRowNumber,
|
|
11305
|
-
"INSERT"
|
|
11306
|
-
));
|
|
11307
|
-
if (!statement.validateOnly) {
|
|
11308
|
-
const errors = results.flatMap((result) => result.errors);
|
|
11309
|
-
if (errors.length > 0) {
|
|
11310
|
-
throw new Error(`ArgumentError: APPLY post-image validation failed: ${JSON.stringify({
|
|
11311
|
-
columns: results[0]?.columns ?? [],
|
|
11312
|
-
errors
|
|
11313
|
-
})}`);
|
|
11314
|
-
}
|
|
11315
|
-
}
|
|
11316
|
-
const parentRows = rawCandidates.length;
|
|
11317
|
-
const subtableRows = rawCandidates.reduce(
|
|
11318
|
-
(sum, candidate) => sum + candidate.tables.reduce((tableSum, table) => tableSum + table.addedRows, 0),
|
|
11319
|
-
0
|
|
11320
|
-
);
|
|
11321
|
-
const wouldExceed = parentRows > dmlMaxRows || subtableRows > dmlMaxSubtableRows;
|
|
11322
|
-
if (!statement.validateOnly && parentRows > dmlMaxRows) {
|
|
11323
|
-
throw new Error(`ArgumentError: APPLY parent rows (${parentRows}) exceed dmlMaxRows (${dmlMaxRows}).`);
|
|
11324
|
-
}
|
|
11325
|
-
if (!statement.validateOnly && subtableRows > dmlMaxSubtableRows) {
|
|
11326
|
-
throw new Error(`ArgumentError: APPLY changed subtable rows (${subtableRows}) exceed dmlMaxSubtableRows (${dmlMaxSubtableRows}).`);
|
|
11327
|
-
}
|
|
11328
|
-
const candidates = rawCandidates.map((candidate, index) => {
|
|
11329
|
-
const normalized = results[index].normalizedRecord;
|
|
11330
|
-
const record = {};
|
|
11331
|
-
for (const code of statement.fields) record[code] = normalized[code];
|
|
11332
|
-
for (const table of candidate.tables) {
|
|
11333
|
-
const normalizedRows = normalized[table.table]?.value;
|
|
11334
|
-
record[table.table] = { value: table.rows.map((sourceRow, rowIndex) => ({
|
|
11335
|
-
value: Object.fromEntries(Object.keys(sourceRow.value).map((code) => [
|
|
11336
|
-
code,
|
|
11337
|
-
normalizedRows[rowIndex]?.value?.[code] ?? sourceRow.value[code]
|
|
11338
|
-
]))
|
|
11339
|
-
})) };
|
|
11660
|
+
function postImageNeedsNumberPrecision(record, fieldIndex) {
|
|
11661
|
+
if (fieldIndex.topLevel.some((field) => field.fieldType === "NUMBER" && field.code in record)) return true;
|
|
11662
|
+
for (const [tableCode, children] of fieldIndex.subtables) {
|
|
11663
|
+
if (!children.some((field) => field.fieldType === "NUMBER")) continue;
|
|
11664
|
+
const rows = record[tableCode]?.value;
|
|
11665
|
+
if (!Array.isArray(rows)) continue;
|
|
11666
|
+
for (const row of rows) {
|
|
11667
|
+
const values = row?.value;
|
|
11668
|
+
if (children.some((field) => field.fieldType === "NUMBER" && !!values && field.code in values)) return true;
|
|
11340
11669
|
}
|
|
11341
|
-
return { ...candidate, postImage: normalized, record };
|
|
11342
|
-
});
|
|
11343
|
-
const records = candidates.map((candidate) => candidate.record);
|
|
11344
|
-
const batches = [];
|
|
11345
|
-
for (let index = 0; index < records.length; index += 100) {
|
|
11346
|
-
batches.push({ app: statement.appId, records: records.slice(index, index + 100) });
|
|
11347
11670
|
}
|
|
11348
|
-
|
|
11349
|
-
errors: result.errors,
|
|
11350
|
-
columns: result.columns,
|
|
11351
|
-
invalidRows: result.invalidRows,
|
|
11352
|
-
errorCount: result.errorCount
|
|
11353
|
-
}));
|
|
11354
|
-
return deepFreeze2({
|
|
11355
|
-
applyBlocks: statement.applyBlocks ?? [],
|
|
11356
|
-
candidates,
|
|
11357
|
-
records,
|
|
11358
|
-
batches,
|
|
11359
|
-
validations,
|
|
11360
|
-
guards: {
|
|
11361
|
-
revisionRequired: false,
|
|
11362
|
-
parentRows,
|
|
11363
|
-
dmlMaxRows,
|
|
11364
|
-
subtableRows,
|
|
11365
|
-
dmlMaxSubtableRows,
|
|
11366
|
-
wouldExceed
|
|
11367
|
-
}
|
|
11368
|
-
});
|
|
11671
|
+
return false;
|
|
11369
11672
|
}
|
|
11370
|
-
function
|
|
11371
|
-
|
|
11372
|
-
const
|
|
11373
|
-
|
|
11374
|
-
|
|
11375
|
-
|
|
11376
|
-
|
|
11377
|
-
|
|
11378
|
-
|
|
11379
|
-
|
|
11673
|
+
function validatePostImage(record, fieldIndex, numberPrecision, statementNumber, parentRowNumber = 1, operation = "UPDATE") {
|
|
11674
|
+
const normalizedRecord = cloneRecord(record);
|
|
11675
|
+
const errors = [];
|
|
11676
|
+
const invalidRowNumbers = /* @__PURE__ */ new Set();
|
|
11677
|
+
const columns = [...fieldIndex.payloadFields, ...POST_IMAGE_VALIDATION_SUFFIX_COLUMNS];
|
|
11678
|
+
const parentId = renderValidationValue(record["$id"]?.value);
|
|
11679
|
+
const appendError = (field, raw, validation, locator) => {
|
|
11680
|
+
invalidRowNumbers.add(parentRowNumber);
|
|
11681
|
+
const row = {};
|
|
11682
|
+
for (const code of fieldIndex.payloadFields) {
|
|
11683
|
+
row[code] = code === "$id" ? parentId : renderValidationValue(record[code]?.value);
|
|
11684
|
+
}
|
|
11685
|
+
row["$err_statement"] = String(statementNumber);
|
|
11686
|
+
row["$err_operation"] = operation;
|
|
11687
|
+
row["$err_row"] = String(parentRowNumber);
|
|
11688
|
+
row["$err_field"] = field.code;
|
|
11689
|
+
row["$err_code"] = validation.code;
|
|
11690
|
+
row["$err_message"] = validation.message;
|
|
11691
|
+
row["$err_value"] = renderExistingValidationValue(raw, field.fieldType);
|
|
11692
|
+
row["$err_subtable"] = locator?.subtable ?? "";
|
|
11693
|
+
row["$err_subrow"] = locator ? String(locator.subrow) : "";
|
|
11694
|
+
row["$err_subrow_id"] = locator?.subrowId ?? "";
|
|
11695
|
+
errors.push(row);
|
|
11696
|
+
};
|
|
11697
|
+
for (const field of fieldIndex.topLevel) {
|
|
11698
|
+
const raw = record[field.code]?.value;
|
|
11699
|
+
const result = validateAndNormalizeDmlValue(raw, field, numberPrecision);
|
|
11700
|
+
if (!result.ok) appendError(field, raw, result);
|
|
11701
|
+
else normalizedRecord[field.code] = { value: preserveCodeObjects(raw, field.fieldType, result.value) };
|
|
11380
11702
|
}
|
|
11381
|
-
|
|
11382
|
-
|
|
11383
|
-
|
|
11384
|
-
|
|
11703
|
+
for (const [tableCode, children] of fieldIndex.subtables) {
|
|
11704
|
+
const sourceRows2 = record[tableCode]?.value;
|
|
11705
|
+
if (!Array.isArray(sourceRows2)) continue;
|
|
11706
|
+
const normalizedRows = normalizedRecord[tableCode]?.value;
|
|
11707
|
+
for (let rowIndex = 0; rowIndex < sourceRows2.length; rowIndex++) {
|
|
11708
|
+
const sourceRow = sourceRows2[rowIndex];
|
|
11709
|
+
const normalizedRow = normalizedRows[rowIndex];
|
|
11710
|
+
for (const field of children) {
|
|
11711
|
+
const raw = sourceRow.value?.[field.code]?.value;
|
|
11712
|
+
const result = validateAndNormalizeDmlValue(raw, field, numberPrecision);
|
|
11713
|
+
if (!result.ok) appendError(field, raw, result, buildValidationCellLocator(tableCode, rowIndex, sourceRow));
|
|
11714
|
+
else {
|
|
11715
|
+
normalizedRow.value ??= {};
|
|
11716
|
+
normalizedRow.value[field.code] = { value: preserveCodeObjects(raw, field.fieldType, result.value) };
|
|
11717
|
+
}
|
|
11718
|
+
}
|
|
11719
|
+
}
|
|
11385
11720
|
}
|
|
11721
|
+
return {
|
|
11722
|
+
normalizedRecord,
|
|
11723
|
+
errors,
|
|
11724
|
+
columns,
|
|
11725
|
+
invalidRows: invalidRowNumbers.size,
|
|
11726
|
+
invalidRowNumbers,
|
|
11727
|
+
errorCount: errors.length
|
|
11728
|
+
};
|
|
11386
11729
|
}
|
|
11387
|
-
function
|
|
11388
|
-
|
|
11730
|
+
function preserveCodeObjects(raw, fieldType, normalized) {
|
|
11731
|
+
return ["USER_SELECT", "ORGANIZATION_SELECT", "GROUP_SELECT"].includes(fieldType) && Array.isArray(raw) && raw.every((item) => typeof item === "object" && item !== null && "code" in item) ? raw : normalized;
|
|
11389
11732
|
}
|
|
11390
|
-
function
|
|
11391
|
-
|
|
11392
|
-
|
|
11393
|
-
|
|
11394
|
-
|
|
11733
|
+
function cloneRecord(record) {
|
|
11734
|
+
const clone = {};
|
|
11735
|
+
for (const [code, cell] of Object.entries(record)) {
|
|
11736
|
+
const value = cell?.value;
|
|
11737
|
+
clone[code] = { value: Array.isArray(value) ? value.map((item) => {
|
|
11738
|
+
if (item === null || typeof item !== "object") return item;
|
|
11739
|
+
const row = item;
|
|
11740
|
+
if (!row.value) return { ...row };
|
|
11741
|
+
return {
|
|
11742
|
+
...row,
|
|
11743
|
+
value: Object.fromEntries(Object.entries(row.value).map(([field, child]) => [field, { ...child }]))
|
|
11744
|
+
};
|
|
11745
|
+
}) : value };
|
|
11746
|
+
}
|
|
11747
|
+
return clone;
|
|
11395
11748
|
}
|
|
11396
11749
|
|
|
11397
|
-
// src/core/
|
|
11398
|
-
async function
|
|
11399
|
-
const {
|
|
11400
|
-
|
|
11401
|
-
|
|
11402
|
-
assertMatchCoverage(statement, matches);
|
|
11403
|
-
const createMatches = matches.filter((match) => match.targetId === void 0);
|
|
11404
|
-
const updateMatches = matches.filter((match) => match.targetId !== void 0);
|
|
11405
|
-
const createStatement = toInsertStatement(statement, createMatches.map((match) => match.sourceRowIndex));
|
|
11406
|
-
const create = await prepareApplyInsert({
|
|
11407
|
-
statement: createStatement,
|
|
11750
|
+
// src/core/applyPatchPrepare.ts
|
|
11751
|
+
async function prepareApplyPatchWrite(input) {
|
|
11752
|
+
const {
|
|
11753
|
+
statement,
|
|
11754
|
+
snapshots,
|
|
11408
11755
|
fieldInfos,
|
|
11409
|
-
metadata
|
|
11756
|
+
metadata,
|
|
11410
11757
|
dmlMaxRows,
|
|
11411
11758
|
dmlMaxSubtableRows,
|
|
11412
|
-
statementNumber
|
|
11413
|
-
|
|
11414
|
-
|
|
11415
|
-
|
|
11416
|
-
const
|
|
11417
|
-
const
|
|
11418
|
-
|
|
11419
|
-
|
|
11420
|
-
|
|
11421
|
-
|
|
11422
|
-
|
|
11423
|
-
|
|
11424
|
-
|
|
11425
|
-
|
|
11426
|
-
|
|
11427
|
-
updateParts.push(await prepareApplyPatchWrite({
|
|
11428
|
-
statement: updateStatement,
|
|
11429
|
-
snapshots: [match.snapshot],
|
|
11430
|
-
fieldInfos,
|
|
11431
|
-
metadata,
|
|
11432
|
-
dmlMaxRows,
|
|
11433
|
-
dmlMaxSubtableRows,
|
|
11434
|
-
statementNumber,
|
|
11435
|
-
parentRowNumbers: [match.sourceRowIndex + 1],
|
|
11436
|
-
loadNumberPrecision: input.loadNumberPrecision
|
|
11437
|
-
}));
|
|
11759
|
+
statementNumber = 1
|
|
11760
|
+
} = input;
|
|
11761
|
+
assertPositiveLimit(dmlMaxRows, "dmlMaxRows");
|
|
11762
|
+
assertPositiveLimit(dmlMaxSubtableRows, "dmlMaxSubtableRows");
|
|
11763
|
+
for (const snapshot of snapshots) requireRevision(snapshot);
|
|
11764
|
+
const rawPlans = buildApplyPatchPlans(statement, snapshots, fieldInfos, metadata);
|
|
11765
|
+
const fieldIndex = buildPostImageFieldIndex(
|
|
11766
|
+
fieldInfos,
|
|
11767
|
+
statement.assignments.map((assignment) => assignment.field)
|
|
11768
|
+
);
|
|
11769
|
+
const needsNumberPrecision = rawPlans.some(
|
|
11770
|
+
(plan) => postImageNeedsNumberPrecision(plan.postImage, fieldIndex)
|
|
11771
|
+
);
|
|
11772
|
+
if (needsNumberPrecision && !input.loadNumberPrecision) {
|
|
11773
|
+
throw new Error("InternalError: APPLY number precision loader is required for NUMBER post-images.");
|
|
11438
11774
|
}
|
|
11439
|
-
const
|
|
11440
|
-
const
|
|
11441
|
-
|
|
11442
|
-
|
|
11775
|
+
const numberPrecision = needsNumberPrecision ? await input.loadNumberPrecision() : void 0;
|
|
11776
|
+
const validationResults = rawPlans.map(
|
|
11777
|
+
(plan, index) => validatePostImage(
|
|
11778
|
+
plan.postImage,
|
|
11779
|
+
fieldIndex,
|
|
11780
|
+
numberPrecision,
|
|
11781
|
+
statementNumber,
|
|
11782
|
+
input.parentRowNumbers?.[index] ?? index + 1
|
|
11783
|
+
)
|
|
11784
|
+
);
|
|
11443
11785
|
if (!statement.validateOnly) {
|
|
11444
|
-
const
|
|
11445
|
-
|
|
11446
|
-
...update.validations.flatMap((validation) => validation.errors)
|
|
11447
|
-
];
|
|
11448
|
-
if (validationErrors.length > 0) {
|
|
11786
|
+
const errors = validationResults.flatMap((validation) => validation.errors);
|
|
11787
|
+
if (errors.length > 0) {
|
|
11449
11788
|
throw new Error(`ArgumentError: APPLY post-image validation failed: ${JSON.stringify({
|
|
11450
|
-
columns:
|
|
11451
|
-
errors
|
|
11789
|
+
columns: validationResults[0]?.columns ?? [],
|
|
11790
|
+
errors
|
|
11452
11791
|
})}`);
|
|
11453
11792
|
}
|
|
11454
11793
|
}
|
|
11794
|
+
const parentRows = rawPlans.length;
|
|
11795
|
+
const subtableRows = rawPlans.reduce((sum, plan) => sum + plan.changedSubtableRows, 0);
|
|
11796
|
+
const wouldExceed = parentRows > dmlMaxRows || subtableRows > dmlMaxSubtableRows;
|
|
11455
11797
|
if (!statement.validateOnly && parentRows > dmlMaxRows) {
|
|
11456
11798
|
throw new Error(`ArgumentError: APPLY parent rows (${parentRows}) exceed dmlMaxRows (${dmlMaxRows}).`);
|
|
11457
11799
|
}
|
|
11458
11800
|
if (!statement.validateOnly && subtableRows > dmlMaxSubtableRows) {
|
|
11459
|
-
throw new Error(
|
|
11801
|
+
throw new Error(
|
|
11802
|
+
`ArgumentError: APPLY changed subtable rows (${subtableRows}) exceed dmlMaxSubtableRows (${dmlMaxSubtableRows}).`
|
|
11803
|
+
);
|
|
11460
11804
|
}
|
|
11461
|
-
|
|
11462
|
-
|
|
11463
|
-
|
|
11464
|
-
|
|
11465
|
-
|
|
11466
|
-
|
|
11467
|
-
|
|
11468
|
-
|
|
11469
|
-
|
|
11470
|
-
subtableRows,
|
|
11471
|
-
dmlMaxSubtableRows,
|
|
11472
|
-
wouldExceed,
|
|
11473
|
-
create: create.guards,
|
|
11474
|
-
update: update.guards
|
|
11475
|
-
}
|
|
11476
|
-
});
|
|
11477
|
-
}
|
|
11478
|
-
function toInsertStatement(statement, indices) {
|
|
11479
|
-
return {
|
|
11480
|
-
type: "INSERT",
|
|
11481
|
-
appId: statement.appId,
|
|
11482
|
-
fields: [...statement.fields],
|
|
11483
|
-
values: indices.map((index) => statement.values[index]),
|
|
11484
|
-
applyBlocks: statement.onInsertApplyBlocks ?? [],
|
|
11485
|
-
// Branch planners must finish every candidate before mixed errors/guards are raised below.
|
|
11486
|
-
validateOnly: true
|
|
11487
|
-
};
|
|
11488
|
-
}
|
|
11489
|
-
function toUpdateStatement(statement, sourceRowIndex, targetId) {
|
|
11490
|
-
const assignments = statement.fields.map((field, index) => ({
|
|
11491
|
-
field,
|
|
11492
|
-
value: statement.values[sourceRowIndex][index]
|
|
11805
|
+
const plans = rawPlans.map(
|
|
11806
|
+
(plan, index) => normalizeApplyPatchPlan(plan, validationResults[index].normalizedRecord)
|
|
11807
|
+
);
|
|
11808
|
+
const records = plans.flatMap((plan) => applyPatchPlanToKintone(plan).records);
|
|
11809
|
+
const validations = validationResults.map((validation) => ({
|
|
11810
|
+
errors: validation.errors,
|
|
11811
|
+
columns: validation.columns,
|
|
11812
|
+
invalidRows: validation.invalidRows,
|
|
11813
|
+
errorCount: validation.errorCount
|
|
11493
11814
|
}));
|
|
11494
|
-
return {
|
|
11495
|
-
type: "UPDATE",
|
|
11496
|
-
appId: statement.appId,
|
|
11497
|
-
subtableCode: null,
|
|
11498
|
-
assignments,
|
|
11499
|
-
from: null,
|
|
11500
|
-
where: {
|
|
11501
|
-
type: "BINARY",
|
|
11502
|
-
op: "=",
|
|
11503
|
-
left: { type: "FIELD", tableAlias: null, field: "$id" },
|
|
11504
|
-
right: { type: "NUMBER", value: targetId, raw: String(targetId) }
|
|
11505
|
-
},
|
|
11506
|
-
applyBlocks: statement.onUpdateApplyBlocks ?? [],
|
|
11507
|
-
validateOnly: true
|
|
11508
|
-
};
|
|
11509
|
-
}
|
|
11510
|
-
function emptyPatchMetadata(fieldInfos) {
|
|
11511
|
-
return {
|
|
11512
|
-
targetTables: /* @__PURE__ */ new Map(),
|
|
11513
|
-
targetMultiValueFields: /* @__PURE__ */ new Map(),
|
|
11514
|
-
childrenByTable: /* @__PURE__ */ new Map(),
|
|
11515
|
-
fieldsByCode: new Map(fieldInfos.map((field) => [field.code, field]))
|
|
11516
|
-
};
|
|
11517
|
-
}
|
|
11518
|
-
function combineUpdatePrepared(parts, dmlMaxRows, dmlMaxSubtableRows) {
|
|
11519
|
-
const plans = parts.flatMap((part) => part.plans);
|
|
11520
|
-
const records = parts.flatMap((part) => part.records);
|
|
11521
|
-
const validations = parts.flatMap((part) => part.validations);
|
|
11522
|
-
const subtableRows = plans.reduce((sum, plan) => sum + plan.changedSubtableRows, 0);
|
|
11523
|
-
return deepFreeze3({
|
|
11815
|
+
return deepFreeze({
|
|
11524
11816
|
plans,
|
|
11525
11817
|
records,
|
|
11526
11818
|
validations,
|
|
11527
11819
|
guards: {
|
|
11528
11820
|
revisionRequired: true,
|
|
11529
|
-
parentRows
|
|
11821
|
+
parentRows,
|
|
11530
11822
|
dmlMaxRows,
|
|
11531
11823
|
subtableRows,
|
|
11532
11824
|
dmlMaxSubtableRows,
|
|
11533
|
-
wouldExceed
|
|
11825
|
+
wouldExceed
|
|
11534
11826
|
}
|
|
11535
11827
|
});
|
|
11536
11828
|
}
|
|
11537
|
-
function
|
|
11538
|
-
if (matches.length !== statement.values.length) {
|
|
11539
|
-
throw new Error("InternalError: UPSERT APPLY match count differs from VALUES rows.");
|
|
11540
|
-
}
|
|
11541
|
-
const seen = /* @__PURE__ */ new Set();
|
|
11542
|
-
for (const match of matches) {
|
|
11543
|
-
if (!Number.isSafeInteger(match.sourceRowIndex) || match.sourceRowIndex < 0 || match.sourceRowIndex >= statement.values.length || seen.has(match.sourceRowIndex)) {
|
|
11544
|
-
throw new Error("InternalError: UPSERT APPLY matches must cover each source row exactly once.");
|
|
11545
|
-
}
|
|
11546
|
-
seen.add(match.sourceRowIndex);
|
|
11547
|
-
}
|
|
11548
|
-
}
|
|
11549
|
-
function chunk2(records, app) {
|
|
11550
|
-
const batches = [];
|
|
11551
|
-
for (let index = 0; index < records.length; index += 100) {
|
|
11552
|
-
batches.push({ app, records: records.slice(index, index + 100) });
|
|
11553
|
-
}
|
|
11554
|
-
return batches;
|
|
11555
|
-
}
|
|
11556
|
-
function assertPositiveLimit3(value, name) {
|
|
11829
|
+
function assertPositiveLimit(value, name) {
|
|
11557
11830
|
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
11558
11831
|
throw new Error(`ArgumentError: ${name} must be a positive safe integer.`);
|
|
11559
11832
|
}
|
|
11560
11833
|
}
|
|
11561
|
-
function
|
|
11562
|
-
throw new Error(`ArgumentError: ${message}`);
|
|
11563
|
-
}
|
|
11564
|
-
function deepFreeze3(value, seen = /* @__PURE__ */ new Set()) {
|
|
11834
|
+
function deepFreeze(value, seen = /* @__PURE__ */ new Set()) {
|
|
11565
11835
|
if (value === null || typeof value !== "object" || seen.has(value)) return value;
|
|
11566
11836
|
seen.add(value);
|
|
11567
|
-
for (const child of Object.values(value))
|
|
11837
|
+
for (const child of Object.values(value)) deepFreeze(child, seen);
|
|
11568
11838
|
return Object.freeze(value);
|
|
11569
11839
|
}
|
|
11570
11840
|
|
|
11571
|
-
// src/core/
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
11584
|
-
function
|
|
11585
|
-
|
|
11586
|
-
|
|
11587
|
-
const successfulParents = base.statementKind === "UPSERT" ? branch.branch === "insert" ? progress.successfulInserts : progress.successfulUpdates : progress.successfulParents;
|
|
11588
|
-
const successfulChunks = base.statementKind === "UPSERT" ? branch.branch === "insert" ? progress.successfulInsertChunks : progress.successfulUpdateChunks : progress.successfulChunks;
|
|
11589
|
-
const isFailedBranch = failedBranch === branch.branch;
|
|
11590
|
-
return {
|
|
11591
|
-
...branch,
|
|
11592
|
-
...successfulParents !== void 0 ? { successfulParents } : {},
|
|
11593
|
-
chunk: {
|
|
11594
|
-
...branch.chunk,
|
|
11595
|
-
...successfulChunks !== void 0 ? { successfulChunks } : {},
|
|
11596
|
-
...isFailedBranch && progress.failedChunkIndex !== void 0 ? { failedChunkIndex: progress.failedChunkIndex } : {},
|
|
11597
|
-
...isFailedBranch && progress.failedStage !== void 0 ? { failedStage: progress.failedStage } : {}
|
|
11598
|
-
}
|
|
11599
|
-
};
|
|
11600
|
-
});
|
|
11601
|
-
return {
|
|
11602
|
-
...base,
|
|
11603
|
-
branches,
|
|
11604
|
-
partialSuccess: {
|
|
11605
|
-
possible: true,
|
|
11606
|
-
successfulParents: progress.successfulParents,
|
|
11607
|
-
successfulChunks: progress.successfulChunks,
|
|
11608
|
-
...failedBranch ? { failedBranch } : {},
|
|
11609
|
-
...progress.retryAttempted !== void 0 ? { retryAttempted: progress.retryAttempted } : {}
|
|
11610
|
-
}
|
|
11611
|
-
};
|
|
11612
|
-
}
|
|
11613
|
-
function buildStaticApplyDiagnostic(statement, dmlMaxRows, dmlMaxSubtableRows) {
|
|
11614
|
-
if (statement.type === "UPDATE") {
|
|
11615
|
-
return diagnostic("UPDATE", [staticBranch(
|
|
11616
|
-
"update",
|
|
11617
|
-
statement.applyBlocks ?? [],
|
|
11618
|
-
null,
|
|
11619
|
-
true,
|
|
11620
|
-
dmlMaxRows,
|
|
11621
|
-
dmlMaxSubtableRows
|
|
11622
|
-
)]);
|
|
11623
|
-
}
|
|
11624
|
-
if (statement.type === "INSERT") {
|
|
11625
|
-
return diagnostic("INSERT", [staticBranch(
|
|
11626
|
-
"insert",
|
|
11627
|
-
statement.applyBlocks ?? [],
|
|
11628
|
-
statement.values.length,
|
|
11629
|
-
false,
|
|
11630
|
-
dmlMaxRows,
|
|
11631
|
-
dmlMaxSubtableRows
|
|
11632
|
-
)]);
|
|
11841
|
+
// src/core/applyInsertPrepare.ts
|
|
11842
|
+
var NON_WRITABLE_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
11843
|
+
"CALC",
|
|
11844
|
+
"RECORD_NUMBER",
|
|
11845
|
+
"CREATOR",
|
|
11846
|
+
"CREATED_TIME",
|
|
11847
|
+
"MODIFIER",
|
|
11848
|
+
"UPDATED_TIME",
|
|
11849
|
+
"STATUS",
|
|
11850
|
+
"STATUS_ASSIGNEE",
|
|
11851
|
+
"CATEGORY",
|
|
11852
|
+
"REFERENCE_TABLE"
|
|
11853
|
+
]);
|
|
11854
|
+
function resolveApplyInsertMetadata(statement, fieldInfos) {
|
|
11855
|
+
if (new Set(statement.fields).size !== statement.fields.length) {
|
|
11856
|
+
return argument4("DML target fields contain duplicates.");
|
|
11633
11857
|
}
|
|
11634
|
-
|
|
11635
|
-
|
|
11636
|
-
|
|
11637
|
-
|
|
11638
|
-
|
|
11639
|
-
|
|
11640
|
-
|
|
11641
|
-
|
|
11642
|
-
|
|
11643
|
-
|
|
11644
|
-
|
|
11645
|
-
|
|
11646
|
-
}
|
|
11647
|
-
|
|
11648
|
-
|
|
11649
|
-
|
|
11650
|
-
|
|
11651
|
-
|
|
11652
|
-
|
|
11653
|
-
|
|
11858
|
+
const fieldsByCode = new Map(fieldInfos.map((field) => [field.code, field]));
|
|
11859
|
+
for (const code of statement.fields) assertWritable(code, null, fieldsByCode);
|
|
11860
|
+
const targetTables = /* @__PURE__ */ new Map();
|
|
11861
|
+
const childrenByTable = /* @__PURE__ */ new Map();
|
|
11862
|
+
for (const block of statement.applyBlocks ?? []) {
|
|
11863
|
+
if (targetTables.has(block.field)) return argument4(`APPLY has more than one block for table ${block.field}.`);
|
|
11864
|
+
const table = fieldInfos.find((field) => field.code === block.field && !field.inSubtable);
|
|
11865
|
+
if (!table || table.fieldType !== "SUBTABLE") return argument4(`APPLY target ${block.field} is not a SUBTABLE.`);
|
|
11866
|
+
const children = new Map(fieldInfos.filter((field) => field.inSubtable && field.subtableCode === block.field).map((field) => [field.code, field]));
|
|
11867
|
+
targetTables.set(block.field, table);
|
|
11868
|
+
childrenByTable.set(block.field, children);
|
|
11869
|
+
for (const operation of block.operations) {
|
|
11870
|
+
if (operation.kind !== "APPEND") return argument4(`APPLY INSERT supports APPEND only (${operation.kind}).`);
|
|
11871
|
+
const specified = /* @__PURE__ */ new Set();
|
|
11872
|
+
for (const code of operation.fields) {
|
|
11873
|
+
if (specified.has(code)) return argument4(`APPLY APPEND specifies child ${code} more than once.`);
|
|
11874
|
+
specified.add(code);
|
|
11875
|
+
assertWritable(code, block.field, fieldsByCode, children);
|
|
11876
|
+
}
|
|
11877
|
+
for (const row of operation.values) {
|
|
11878
|
+
if (row.length !== operation.fields.length) {
|
|
11879
|
+
return argument4(`APPLY APPEND for ${block.field} has ${row.length} values for ${operation.fields.length} fields.`);
|
|
11654
11880
|
}
|
|
11655
|
-
const addedRows = operation.values.length * prepared.guards.parentRows;
|
|
11656
|
-
return { kind: "APPEND", count: addedRows, addedRows };
|
|
11657
|
-
});
|
|
11658
|
-
targets.set(block.field, {
|
|
11659
|
-
targetKind: block.targetKind,
|
|
11660
|
-
field: block.field,
|
|
11661
|
-
operations,
|
|
11662
|
-
changedCount: operations.reduce((sum, operation) => sum + (operation.addedRows ?? 0), 0)
|
|
11663
|
-
});
|
|
11664
|
-
}
|
|
11665
|
-
} else {
|
|
11666
|
-
for (const candidate of prepared.candidates) {
|
|
11667
|
-
for (const table of candidate.tables) {
|
|
11668
|
-
const current = targets.get(table.table);
|
|
11669
|
-
const addedRows = current?.operations[0]?.addedRows ?? 0;
|
|
11670
|
-
targets.set(table.table, {
|
|
11671
|
-
targetKind: "SUBTABLE",
|
|
11672
|
-
field: table.table,
|
|
11673
|
-
operations: [{ kind: "APPEND", count: addedRows + table.addedRows, addedRows: addedRows + table.addedRows }],
|
|
11674
|
-
changedCount: (current?.changedCount ?? 0) + table.addedRows
|
|
11675
|
-
});
|
|
11676
11881
|
}
|
|
11677
11882
|
}
|
|
11678
11883
|
}
|
|
11679
|
-
return {
|
|
11680
|
-
branch: "insert",
|
|
11681
|
-
parentRows: prepared.guards.parentRows,
|
|
11682
|
-
targets: [...targets.values()],
|
|
11683
|
-
guards: prepared.guards,
|
|
11684
|
-
chunk: { size: 100, plannedChunks: prepared.batches.length },
|
|
11685
|
-
deletedParentRows: 0
|
|
11686
|
-
};
|
|
11884
|
+
return { targetTables, targetMultiValueFields: /* @__PURE__ */ new Map(), childrenByTable, fieldsByCode };
|
|
11687
11885
|
}
|
|
11688
|
-
function
|
|
11689
|
-
const
|
|
11690
|
-
|
|
11691
|
-
|
|
11692
|
-
|
|
11693
|
-
|
|
11694
|
-
|
|
11695
|
-
|
|
11696
|
-
|
|
11697
|
-
|
|
11698
|
-
|
|
11699
|
-
|
|
11700
|
-
|
|
11701
|
-
|
|
11702
|
-
|
|
11703
|
-
|
|
11704
|
-
|
|
11886
|
+
function buildApplyInsertCandidates(statement, fieldInfos, metadata = resolveApplyInsertMetadata(statement, fieldInfos), parentRowNumbers) {
|
|
11887
|
+
const fieldTypes = new Map(fieldInfos.map((field) => [field.code, field.fieldType]));
|
|
11888
|
+
const parentRecords = insertToPostBatches(statement, fieldTypes).flatMap((batch) => batch.records);
|
|
11889
|
+
if (parentRowNumbers && parentRowNumbers.length !== parentRecords.length) {
|
|
11890
|
+
throw new Error("InternalError: APPLY create parent row number count differs from VALUES rows.");
|
|
11891
|
+
}
|
|
11892
|
+
const templates = (statement.applyBlocks ?? []).map((block) => {
|
|
11893
|
+
const children = metadata.childrenByTable.get(block.field);
|
|
11894
|
+
const rows = block.operations.flatMap(
|
|
11895
|
+
(operation) => buildApplyAppendRows(operation, children, block.field)
|
|
11896
|
+
);
|
|
11897
|
+
return { table: block.field, rows, addedRows: rows.length };
|
|
11898
|
+
});
|
|
11899
|
+
return parentRecords.map((parentRecord, index) => {
|
|
11900
|
+
const postImage = {};
|
|
11901
|
+
for (const field of fieldInfos) {
|
|
11902
|
+
if (field.inSubtable || field.fieldType === "SUBTABLE" || field.fieldType === "FILE" || field.writable === false || NON_WRITABLE_FIELD_TYPES.has(field.fieldType)) continue;
|
|
11903
|
+
postImage[field.code] = parentRecord[field.code] ?? { value: appendDefaultValue(field) };
|
|
11705
11904
|
}
|
|
11706
|
-
|
|
11707
|
-
|
|
11708
|
-
const
|
|
11709
|
-
|
|
11710
|
-
|
|
11711
|
-
|
|
11712
|
-
|
|
11713
|
-
|
|
11714
|
-
|
|
11715
|
-
|
|
11716
|
-
|
|
11717
|
-
|
|
11718
|
-
|
|
11719
|
-
|
|
11720
|
-
|
|
11721
|
-
|
|
11905
|
+
const record = { ...parentRecord };
|
|
11906
|
+
for (const template of templates) {
|
|
11907
|
+
const rows = template.rows.map((row) => ({ value: row.value }));
|
|
11908
|
+
postImage[template.table] = { value: rows };
|
|
11909
|
+
record[template.table] = { value: rows };
|
|
11910
|
+
}
|
|
11911
|
+
return {
|
|
11912
|
+
parentRowNumber: parentRowNumbers?.[index] ?? index + 1,
|
|
11913
|
+
tables: templates,
|
|
11914
|
+
postImage,
|
|
11915
|
+
record
|
|
11916
|
+
};
|
|
11917
|
+
});
|
|
11918
|
+
}
|
|
11919
|
+
async function prepareApplyInsert(input) {
|
|
11920
|
+
const {
|
|
11921
|
+
statement,
|
|
11922
|
+
fieldInfos,
|
|
11923
|
+
dmlMaxRows,
|
|
11924
|
+
dmlMaxSubtableRows,
|
|
11925
|
+
statementNumber = 1
|
|
11926
|
+
} = input;
|
|
11927
|
+
assertPositiveLimit2(dmlMaxRows, "dmlMaxRows");
|
|
11928
|
+
assertPositiveLimit2(dmlMaxSubtableRows, "dmlMaxSubtableRows");
|
|
11929
|
+
const metadata = input.metadata ?? resolveApplyInsertMetadata(statement, fieldInfos);
|
|
11930
|
+
const rawCandidates = buildApplyInsertCandidates(statement, fieldInfos, metadata, input.parentRowNumbers);
|
|
11931
|
+
const creatableFieldInfos = fieldInfos.filter(
|
|
11932
|
+
(field) => field.fieldType === "SUBTABLE" || field.fieldType !== "FILE" && field.writable !== false && !NON_WRITABLE_FIELD_TYPES.has(field.fieldType)
|
|
11933
|
+
);
|
|
11934
|
+
const fieldIndex = buildPostImageFieldIndex(creatableFieldInfos, statement.fields);
|
|
11935
|
+
const needsNumberPrecision = rawCandidates.some(
|
|
11936
|
+
(candidate) => postImageNeedsNumberPrecision(candidate.postImage, fieldIndex)
|
|
11937
|
+
);
|
|
11938
|
+
if (needsNumberPrecision && !input.loadNumberPrecision) {
|
|
11939
|
+
throw new Error("InternalError: APPLY number precision loader is required for NUMBER post-images.");
|
|
11940
|
+
}
|
|
11941
|
+
const numberPrecision = needsNumberPrecision ? await input.loadNumberPrecision() : void 0;
|
|
11942
|
+
const results = rawCandidates.map((candidate) => validatePostImage(
|
|
11943
|
+
candidate.postImage,
|
|
11944
|
+
fieldIndex,
|
|
11945
|
+
numberPrecision,
|
|
11946
|
+
statementNumber,
|
|
11947
|
+
candidate.parentRowNumber,
|
|
11948
|
+
"INSERT"
|
|
11949
|
+
));
|
|
11950
|
+
if (!statement.validateOnly) {
|
|
11951
|
+
const errors = results.flatMap((result) => result.errors);
|
|
11952
|
+
if (errors.length > 0) {
|
|
11953
|
+
throw new Error(`ArgumentError: APPLY post-image validation failed: ${JSON.stringify({
|
|
11954
|
+
columns: results[0]?.columns ?? [],
|
|
11955
|
+
errors
|
|
11956
|
+
})}`);
|
|
11722
11957
|
}
|
|
11723
|
-
if (parentHasDeletes) deletedParentRows += 1;
|
|
11724
11958
|
}
|
|
11725
|
-
|
|
11726
|
-
|
|
11727
|
-
|
|
11728
|
-
|
|
11729
|
-
|
|
11730
|
-
|
|
11731
|
-
|
|
11732
|
-
|
|
11733
|
-
}
|
|
11734
|
-
|
|
11735
|
-
|
|
11736
|
-
|
|
11737
|
-
|
|
11738
|
-
|
|
11739
|
-
|
|
11959
|
+
const parentRows = rawCandidates.length;
|
|
11960
|
+
const subtableRows = rawCandidates.reduce(
|
|
11961
|
+
(sum, candidate) => sum + candidate.tables.reduce((tableSum, table) => tableSum + table.addedRows, 0),
|
|
11962
|
+
0
|
|
11963
|
+
);
|
|
11964
|
+
const wouldExceed = parentRows > dmlMaxRows || subtableRows > dmlMaxSubtableRows;
|
|
11965
|
+
if (!statement.validateOnly && parentRows > dmlMaxRows) {
|
|
11966
|
+
throw new Error(`ArgumentError: APPLY parent rows (${parentRows}) exceed dmlMaxRows (${dmlMaxRows}).`);
|
|
11967
|
+
}
|
|
11968
|
+
if (!statement.validateOnly && subtableRows > dmlMaxSubtableRows) {
|
|
11969
|
+
throw new Error(`ArgumentError: APPLY changed subtable rows (${subtableRows}) exceed dmlMaxSubtableRows (${dmlMaxSubtableRows}).`);
|
|
11970
|
+
}
|
|
11971
|
+
const candidates = rawCandidates.map((candidate, index) => {
|
|
11972
|
+
const normalized = results[index].normalizedRecord;
|
|
11973
|
+
const record = {};
|
|
11974
|
+
for (const code of statement.fields) record[code] = normalized[code];
|
|
11975
|
+
for (const table of candidate.tables) {
|
|
11976
|
+
const normalizedRows = normalized[table.table]?.value;
|
|
11977
|
+
record[table.table] = { value: table.rows.map((sourceRow, rowIndex) => ({
|
|
11978
|
+
value: Object.fromEntries(Object.keys(sourceRow.value).map((code) => [
|
|
11979
|
+
code,
|
|
11980
|
+
normalizedRows[rowIndex]?.value?.[code] ?? sourceRow.value[code]
|
|
11981
|
+
]))
|
|
11982
|
+
})) };
|
|
11740
11983
|
}
|
|
11741
|
-
return {
|
|
11742
|
-
...operation,
|
|
11743
|
-
...previous.changed !== void 0 ? { changed: previous.changed } : {},
|
|
11744
|
-
count: addNullable(previous.count, operation.count),
|
|
11745
|
-
...operation.matchedRows !== void 0 ? { matchedRows: addNullable(previous.matchedRows, operation.matchedRows) } : {},
|
|
11746
|
-
...operation.changedRows !== void 0 ? { changedRows: addNullable(previous.changedRows, operation.changedRows) } : {},
|
|
11747
|
-
...operation.addedRows !== void 0 ? { addedRows: addNullable(previous.addedRows, operation.addedRows) } : {},
|
|
11748
|
-
...operation.removedRows !== void 0 ? { removedRows: addNullable(previous.removedRows, operation.removedRows) } : {}
|
|
11749
|
-
};
|
|
11750
|
-
});
|
|
11751
|
-
}
|
|
11752
|
-
function addNullable(left, right) {
|
|
11753
|
-
return left === null || left === void 0 || right === null ? null : left + right;
|
|
11754
|
-
}
|
|
11755
|
-
function staticBranch(branch, blocks, parentRows, revisionRequired, dmlMaxRows, dmlMaxSubtableRows) {
|
|
11756
|
-
const targets = blocks.map((block) => {
|
|
11757
|
-
const operations = block.operations.map((operation) => staticOperation(operation, parentRows));
|
|
11758
|
-
const changedCount = operations.every((operation) => operation.count !== null) ? operations.reduce((sum, operation) => sum + operation.count, 0) : null;
|
|
11759
|
-
return {
|
|
11760
|
-
targetKind: block.targetKind,
|
|
11761
|
-
field: block.field,
|
|
11762
|
-
operations,
|
|
11763
|
-
changedCount
|
|
11764
|
-
};
|
|
11984
|
+
return { ...candidate, postImage: normalized, record };
|
|
11765
11985
|
});
|
|
11766
|
-
const
|
|
11767
|
-
const
|
|
11768
|
-
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
|
|
11986
|
+
const records = candidates.map((candidate) => candidate.record);
|
|
11987
|
+
const batches = [];
|
|
11988
|
+
for (let index = 0; index < records.length; index += 100) {
|
|
11989
|
+
batches.push({ app: statement.appId, records: records.slice(index, index + 100) });
|
|
11990
|
+
}
|
|
11991
|
+
const validations = results.map((result) => ({
|
|
11992
|
+
errors: result.errors,
|
|
11993
|
+
columns: result.columns,
|
|
11994
|
+
invalidRows: result.invalidRows,
|
|
11995
|
+
errorCount: result.errorCount
|
|
11996
|
+
}));
|
|
11997
|
+
return deepFreeze2({
|
|
11998
|
+
applyBlocks: statement.applyBlocks ?? [],
|
|
11999
|
+
candidates,
|
|
12000
|
+
records,
|
|
12001
|
+
batches,
|
|
12002
|
+
validations,
|
|
11773
12003
|
guards: {
|
|
11774
|
-
revisionRequired,
|
|
12004
|
+
revisionRequired: false,
|
|
11775
12005
|
parentRows,
|
|
11776
12006
|
dmlMaxRows,
|
|
11777
12007
|
subtableRows,
|
|
11778
12008
|
dmlMaxSubtableRows,
|
|
11779
12009
|
wouldExceed
|
|
11780
|
-
}
|
|
11781
|
-
|
|
11782
|
-
deletedParentRows: branch === "insert" ? 0 : null
|
|
11783
|
-
};
|
|
12010
|
+
}
|
|
12011
|
+
});
|
|
11784
12012
|
}
|
|
11785
|
-
function
|
|
11786
|
-
if (
|
|
11787
|
-
|
|
11788
|
-
|
|
12013
|
+
function assertWritable(code, table, fieldsByCode, children) {
|
|
12014
|
+
if (code.startsWith("_") || code.startsWith("$")) return argument4(`APPLY assignment target ${code} is a system field.`);
|
|
12015
|
+
const field = table === null ? [...fieldsByCode.values()].find((candidate) => candidate.code === code && !candidate.inSubtable) : children?.get(code);
|
|
12016
|
+
if (!field) {
|
|
12017
|
+
const elsewhere = fieldsByCode.get(code);
|
|
12018
|
+
if (table !== null && elsewhere?.inSubtable) return argument4(`APPLY child ${code} does not belong to subtable ${table}.`);
|
|
12019
|
+
return argument4(`APPLY ${table === null ? "parent field" : "child"} ${code} does not exist.`);
|
|
11789
12020
|
}
|
|
11790
|
-
if (
|
|
11791
|
-
|
|
11792
|
-
return { kind: operation.kind, value: operation.value, count: null };
|
|
11793
|
-
}
|
|
11794
|
-
|
|
11795
|
-
// src/core/applyPatchExecutePrepared.ts
|
|
11796
|
-
var ApplyWritePartialFailureError = class extends Error {
|
|
11797
|
-
constructor(partialSuccess, cause) {
|
|
11798
|
-
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
11799
|
-
const method = partialSuccess.failedStage === "POST_CHUNK" ? "POST" : "PUT";
|
|
11800
|
-
const branch = partialSuccess.failedBranch ? ` UPSERT ${partialSuccess.failedBranch}` : "";
|
|
11801
|
-
super(
|
|
11802
|
-
`ApplyWritePartialFailureError: APPLY${branch} ${method} chunk ${partialSuccess.failedChunkIndex + 1} failed (index ${partialSuccess.failedChunkIndex}) after ${partialSuccess.successfulChunks} successful chunk(s) and ${partialSuccess.successfulParents} successful parent(s); writes are non-transactional and were not retried. Cause: ${detail}`
|
|
11803
|
-
);
|
|
11804
|
-
this.name = "ApplyWritePartialFailureError";
|
|
11805
|
-
this.partialSuccess = partialSuccess;
|
|
11806
|
-
this.cause = cause;
|
|
12021
|
+
if (field.fieldType === "SUBTABLE" || field.fieldType === "FILE" || field.writable === false || NON_WRITABLE_FIELD_TYPES.has(field.fieldType)) {
|
|
12022
|
+
return argument4(`APPLY assignment target ${code} is not writable (${field.fieldType}).`);
|
|
11807
12023
|
}
|
|
11808
|
-
}
|
|
11809
|
-
|
|
11810
|
-
|
|
11811
|
-
|
|
11812
|
-
let successfulChunks = 0;
|
|
11813
|
-
let successfulParents = 0;
|
|
11814
|
-
for (let failedChunkIndex = 0; failedChunkIndex < batches.length; failedChunkIndex += 1) {
|
|
11815
|
-
const batch = batches[failedChunkIndex];
|
|
11816
|
-
try {
|
|
11817
|
-
await client.putRecords(batch);
|
|
11818
|
-
} catch (cause) {
|
|
11819
|
-
throw new ApplyWritePartialFailureError({
|
|
11820
|
-
successfulChunks,
|
|
11821
|
-
successfulParents,
|
|
11822
|
-
failedChunkIndex,
|
|
11823
|
-
failedStage: "PUT_CHUNK",
|
|
11824
|
-
nonTransactional: true,
|
|
11825
|
-
retryAttempted: false,
|
|
11826
|
-
...diagnostic2 ? { diagnostic: withApplyDiagnosticProgress(diagnostic2, {
|
|
11827
|
-
successfulChunks,
|
|
11828
|
-
successfulParents,
|
|
11829
|
-
failedChunkIndex,
|
|
11830
|
-
failedStage: "PUT_CHUNK",
|
|
11831
|
-
failedBranch: "UPDATE",
|
|
11832
|
-
retryAttempted: false
|
|
11833
|
-
}) } : {}
|
|
11834
|
-
}, cause);
|
|
11835
|
-
}
|
|
11836
|
-
successfulChunks += 1;
|
|
11837
|
-
successfulParents += batch.records.length;
|
|
12024
|
+
}
|
|
12025
|
+
function assertPositiveLimit2(value, name) {
|
|
12026
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
12027
|
+
throw new Error(`ArgumentError: ${name} must be a positive safe integer.`);
|
|
11838
12028
|
}
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
|
|
11843
|
-
|
|
11844
|
-
|
|
11845
|
-
|
|
12029
|
+
}
|
|
12030
|
+
function argument4(message) {
|
|
12031
|
+
throw new Error(`ArgumentError: ${message}`);
|
|
12032
|
+
}
|
|
12033
|
+
function deepFreeze2(value, seen = /* @__PURE__ */ new Set()) {
|
|
12034
|
+
if (value === null || typeof value !== "object" || seen.has(value)) return value;
|
|
12035
|
+
seen.add(value);
|
|
12036
|
+
for (const child of Object.values(value)) deepFreeze2(child, seen);
|
|
12037
|
+
return Object.freeze(value);
|
|
11846
12038
|
}
|
|
11847
12039
|
|
|
11848
|
-
// src/core/
|
|
11849
|
-
async function
|
|
11850
|
-
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
|
|
11871
|
-
|
|
11872
|
-
|
|
11873
|
-
|
|
11874
|
-
|
|
11875
|
-
|
|
12040
|
+
// src/core/applyUpsertPrepare.ts
|
|
12041
|
+
async function prepareApplyUpsert(input) {
|
|
12042
|
+
const { statement, matches, fieldInfos, dmlMaxRows, dmlMaxSubtableRows, statementNumber = 1 } = input;
|
|
12043
|
+
assertPositiveLimit3(dmlMaxRows, "dmlMaxRows");
|
|
12044
|
+
assertPositiveLimit3(dmlMaxSubtableRows, "dmlMaxSubtableRows");
|
|
12045
|
+
assertMatchCoverage(statement, matches);
|
|
12046
|
+
const createMatches = matches.filter((match) => match.targetId === void 0);
|
|
12047
|
+
const updateMatches = matches.filter((match) => match.targetId !== void 0);
|
|
12048
|
+
const createStatement = toInsertStatement(statement, createMatches.map((match) => match.sourceRowIndex));
|
|
12049
|
+
const create = await prepareApplyInsert({
|
|
12050
|
+
statement: createStatement,
|
|
12051
|
+
fieldInfos,
|
|
12052
|
+
metadata: resolveApplyInsertMetadata(createStatement, fieldInfos),
|
|
12053
|
+
dmlMaxRows,
|
|
12054
|
+
dmlMaxSubtableRows,
|
|
12055
|
+
statementNumber,
|
|
12056
|
+
parentRowNumbers: createMatches.map((match) => match.sourceRowIndex + 1),
|
|
12057
|
+
loadNumberPrecision: input.loadNumberPrecision
|
|
12058
|
+
});
|
|
12059
|
+
const updateParts = [];
|
|
12060
|
+
const seenTargets = /* @__PURE__ */ new Set();
|
|
12061
|
+
for (const match of updateMatches) {
|
|
12062
|
+
const targetId = match.targetId;
|
|
12063
|
+
if (seenTargets.has(targetId)) argument5(`UPSERT APPLY resolves more than one source row to parent $id ${targetId}.`);
|
|
12064
|
+
seenTargets.add(targetId);
|
|
12065
|
+
if (!match.snapshot) argument5(`UPSERT APPLY snapshot for parent $id ${targetId} is missing.`);
|
|
12066
|
+
const snapshotId = Number(match.snapshot["$id"]?.value);
|
|
12067
|
+
if (snapshotId !== targetId) argument5(`UPSERT APPLY snapshot $id ${snapshotId} does not match target $id ${targetId}.`);
|
|
12068
|
+
const updateStatement = toUpdateStatement(statement, match.sourceRowIndex, targetId);
|
|
12069
|
+
const metadata = updateStatement.applyBlocks?.length ? resolveApplyPatchMetadata(updateStatement, fieldInfos) : emptyPatchMetadata(fieldInfos);
|
|
12070
|
+
updateParts.push(await prepareApplyPatchWrite({
|
|
12071
|
+
statement: updateStatement,
|
|
12072
|
+
snapshots: [match.snapshot],
|
|
12073
|
+
fieldInfos,
|
|
12074
|
+
metadata,
|
|
12075
|
+
dmlMaxRows,
|
|
12076
|
+
dmlMaxSubtableRows,
|
|
12077
|
+
statementNumber,
|
|
12078
|
+
parentRowNumbers: [match.sourceRowIndex + 1],
|
|
12079
|
+
loadNumberPrecision: input.loadNumberPrecision
|
|
12080
|
+
}));
|
|
12081
|
+
}
|
|
12082
|
+
const update = combineUpdatePrepared(updateParts, dmlMaxRows, dmlMaxSubtableRows);
|
|
12083
|
+
const parentRows = create.guards.parentRows + update.guards.parentRows;
|
|
12084
|
+
const subtableRows = create.guards.subtableRows + update.guards.subtableRows;
|
|
12085
|
+
const wouldExceed = parentRows > dmlMaxRows || subtableRows > dmlMaxSubtableRows;
|
|
12086
|
+
if (!statement.validateOnly) {
|
|
12087
|
+
const validationErrors = [
|
|
12088
|
+
...create.validations.flatMap((validation) => validation.errors),
|
|
12089
|
+
...update.validations.flatMap((validation) => validation.errors)
|
|
12090
|
+
];
|
|
12091
|
+
if (validationErrors.length > 0) {
|
|
12092
|
+
throw new Error(`ArgumentError: APPLY post-image validation failed: ${JSON.stringify({
|
|
12093
|
+
columns: create.validations[0]?.columns ?? update.validations[0]?.columns ?? [],
|
|
12094
|
+
errors: validationErrors
|
|
12095
|
+
})}`);
|
|
11876
12096
|
}
|
|
11877
|
-
successfulChunks += 1;
|
|
11878
|
-
successfulParents += batch.records.length;
|
|
11879
12097
|
}
|
|
12098
|
+
if (!statement.validateOnly && parentRows > dmlMaxRows) {
|
|
12099
|
+
throw new Error(`ArgumentError: APPLY parent rows (${parentRows}) exceed dmlMaxRows (${dmlMaxRows}).`);
|
|
12100
|
+
}
|
|
12101
|
+
if (!statement.validateOnly && subtableRows > dmlMaxSubtableRows) {
|
|
12102
|
+
throw new Error(`ArgumentError: APPLY changed subtable rows (${subtableRows}) exceed dmlMaxSubtableRows (${dmlMaxSubtableRows}).`);
|
|
12103
|
+
}
|
|
12104
|
+
return deepFreeze3({
|
|
12105
|
+
create,
|
|
12106
|
+
update,
|
|
12107
|
+
createBatches: create.batches,
|
|
12108
|
+
updateBatches: chunk2(update.records, statement.appId),
|
|
12109
|
+
guards: {
|
|
12110
|
+
revisionRequired: update.guards.parentRows > 0,
|
|
12111
|
+
parentRows,
|
|
12112
|
+
dmlMaxRows,
|
|
12113
|
+
subtableRows,
|
|
12114
|
+
dmlMaxSubtableRows,
|
|
12115
|
+
wouldExceed,
|
|
12116
|
+
create: create.guards,
|
|
12117
|
+
update: update.guards
|
|
12118
|
+
}
|
|
12119
|
+
});
|
|
12120
|
+
}
|
|
12121
|
+
function toInsertStatement(statement, indices) {
|
|
11880
12122
|
return {
|
|
11881
12123
|
type: "INSERT",
|
|
11882
|
-
|
|
11883
|
-
|
|
11884
|
-
|
|
11885
|
-
|
|
11886
|
-
|
|
12124
|
+
appId: statement.appId,
|
|
12125
|
+
fields: [...statement.fields],
|
|
12126
|
+
values: indices.map((index) => statement.values[index]),
|
|
12127
|
+
applyBlocks: statement.onInsertApplyBlocks ?? [],
|
|
12128
|
+
// Branch planners must finish every candidate before mixed errors/guards are raised below.
|
|
12129
|
+
validateOnly: true
|
|
11887
12130
|
};
|
|
11888
12131
|
}
|
|
11889
|
-
|
|
11890
|
-
|
|
11891
|
-
|
|
11892
|
-
|
|
11893
|
-
|
|
11894
|
-
let successfulInsertChunks = 0;
|
|
11895
|
-
let successfulUpdateChunks = 0;
|
|
11896
|
-
let successfulInserts = 0;
|
|
11897
|
-
let successfulUpdates = 0;
|
|
11898
|
-
for (let failedChunkIndex = 0; failedChunkIndex < prepared.createBatches.length; failedChunkIndex += 1) {
|
|
11899
|
-
const batch = prepared.createBatches[failedChunkIndex];
|
|
11900
|
-
try {
|
|
11901
|
-
const response = await client.postRecords({ app: batch.app, records: [...batch.records] });
|
|
11902
|
-
createdIds.push(response.ids);
|
|
11903
|
-
} catch (cause) {
|
|
11904
|
-
throw new ApplyWritePartialFailureError({
|
|
11905
|
-
successfulChunks: successfulInsertChunks + successfulUpdateChunks,
|
|
11906
|
-
successfulParents: successfulInserts + successfulUpdates,
|
|
11907
|
-
successfulInserts,
|
|
11908
|
-
successfulUpdates,
|
|
11909
|
-
failedChunkIndex,
|
|
11910
|
-
failedBranch: "INSERT",
|
|
11911
|
-
failedStage: "POST_CHUNK",
|
|
11912
|
-
nonTransactional: true,
|
|
11913
|
-
retryAttempted: false,
|
|
11914
|
-
...diagnostic2 ? { diagnostic: withApplyDiagnosticProgress(diagnostic2, {
|
|
11915
|
-
successfulChunks: successfulInsertChunks + successfulUpdateChunks,
|
|
11916
|
-
successfulParents: successfulInserts + successfulUpdates,
|
|
11917
|
-
successfulInserts,
|
|
11918
|
-
successfulUpdates,
|
|
11919
|
-
successfulInsertChunks,
|
|
11920
|
-
successfulUpdateChunks,
|
|
11921
|
-
failedChunkIndex,
|
|
11922
|
-
failedStage: "POST_CHUNK",
|
|
11923
|
-
failedBranch: "INSERT",
|
|
11924
|
-
retryAttempted: false
|
|
11925
|
-
}) } : {}
|
|
11926
|
-
}, cause);
|
|
11927
|
-
}
|
|
11928
|
-
successfulInsertChunks += 1;
|
|
11929
|
-
successfulInserts += batch.records.length;
|
|
11930
|
-
}
|
|
11931
|
-
for (let failedChunkIndex = 0; failedChunkIndex < prepared.updateBatches.length; failedChunkIndex += 1) {
|
|
11932
|
-
const batch = prepared.updateBatches[failedChunkIndex];
|
|
11933
|
-
try {
|
|
11934
|
-
await client.putRecords({ app: batch.app, records: [...batch.records] });
|
|
11935
|
-
} catch (cause) {
|
|
11936
|
-
throw new ApplyWritePartialFailureError({
|
|
11937
|
-
successfulChunks: successfulInsertChunks + successfulUpdateChunks,
|
|
11938
|
-
successfulParents: successfulInserts + successfulUpdates,
|
|
11939
|
-
successfulInserts,
|
|
11940
|
-
successfulUpdates,
|
|
11941
|
-
failedChunkIndex,
|
|
11942
|
-
failedBranch: "UPDATE",
|
|
11943
|
-
failedStage: "PUT_CHUNK",
|
|
11944
|
-
nonTransactional: true,
|
|
11945
|
-
retryAttempted: false,
|
|
11946
|
-
...diagnostic2 ? { diagnostic: withApplyDiagnosticProgress(diagnostic2, {
|
|
11947
|
-
successfulChunks: successfulInsertChunks + successfulUpdateChunks,
|
|
11948
|
-
successfulParents: successfulInserts + successfulUpdates,
|
|
11949
|
-
successfulInserts,
|
|
11950
|
-
successfulUpdates,
|
|
11951
|
-
successfulInsertChunks,
|
|
11952
|
-
successfulUpdateChunks,
|
|
11953
|
-
failedChunkIndex,
|
|
11954
|
-
failedStage: "PUT_CHUNK",
|
|
11955
|
-
failedBranch: "UPDATE",
|
|
11956
|
-
retryAttempted: false
|
|
11957
|
-
}) } : {}
|
|
11958
|
-
}, cause);
|
|
11959
|
-
}
|
|
11960
|
-
successfulUpdateChunks += 1;
|
|
11961
|
-
successfulUpdates += batch.records.length;
|
|
11962
|
-
}
|
|
12132
|
+
function toUpdateStatement(statement, sourceRowIndex, targetId) {
|
|
12133
|
+
const assignments = statement.fields.map((field, index) => ({
|
|
12134
|
+
field,
|
|
12135
|
+
value: statement.values[sourceRowIndex][index]
|
|
12136
|
+
}));
|
|
11963
12137
|
return {
|
|
11964
|
-
type: "
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
|
|
11968
|
-
|
|
11969
|
-
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
|
|
12138
|
+
type: "UPDATE",
|
|
12139
|
+
appId: statement.appId,
|
|
12140
|
+
subtableCode: null,
|
|
12141
|
+
assignments,
|
|
12142
|
+
from: null,
|
|
12143
|
+
where: {
|
|
12144
|
+
type: "BINARY",
|
|
12145
|
+
op: "=",
|
|
12146
|
+
left: { type: "FIELD", tableAlias: null, field: "$id" },
|
|
12147
|
+
right: { type: "NUMBER", value: targetId, raw: String(targetId) }
|
|
12148
|
+
},
|
|
12149
|
+
applyBlocks: statement.onUpdateApplyBlocks ?? [],
|
|
12150
|
+
validateOnly: true
|
|
11973
12151
|
};
|
|
11974
12152
|
}
|
|
11975
|
-
|
|
11976
|
-
|
|
11977
|
-
|
|
11978
|
-
|
|
11979
|
-
|
|
11980
|
-
|
|
11981
|
-
|
|
11982
|
-
);
|
|
11983
|
-
}
|
|
11984
|
-
return name.toLowerCase();
|
|
11985
|
-
}
|
|
11986
|
-
function normalizeBatchVariables(input) {
|
|
11987
|
-
const normalized = /* @__PURE__ */ Object.create(null);
|
|
11988
|
-
for (const [rawName, value] of Object.entries(input ?? {})) {
|
|
11989
|
-
const name = normalizeBatchVariableName(rawName);
|
|
11990
|
-
if (Object.prototype.hasOwnProperty.call(normalized, name)) {
|
|
11991
|
-
throw new Error(`ArgumentError: variable "${rawName}" is specified more than once.`);
|
|
11992
|
-
}
|
|
11993
|
-
normalized[name] = value;
|
|
11994
|
-
}
|
|
11995
|
-
return normalized;
|
|
12153
|
+
function emptyPatchMetadata(fieldInfos) {
|
|
12154
|
+
return {
|
|
12155
|
+
targetTables: /* @__PURE__ */ new Map(),
|
|
12156
|
+
targetMultiValueFields: /* @__PURE__ */ new Map(),
|
|
12157
|
+
childrenByTable: /* @__PURE__ */ new Map(),
|
|
12158
|
+
fieldsByCode: new Map(fieldInfos.map((field) => [field.code, field]))
|
|
12159
|
+
};
|
|
11996
12160
|
}
|
|
11997
|
-
function
|
|
11998
|
-
const
|
|
11999
|
-
const
|
|
12000
|
-
|
|
12001
|
-
);
|
|
12002
|
-
|
|
12003
|
-
|
|
12004
|
-
|
|
12161
|
+
function combineUpdatePrepared(parts, dmlMaxRows, dmlMaxSubtableRows) {
|
|
12162
|
+
const plans = parts.flatMap((part) => part.plans);
|
|
12163
|
+
const records = parts.flatMap((part) => part.records);
|
|
12164
|
+
const validations = parts.flatMap((part) => part.validations);
|
|
12165
|
+
const subtableRows = plans.reduce((sum, plan) => sum + plan.changedSubtableRows, 0);
|
|
12166
|
+
return deepFreeze3({
|
|
12167
|
+
plans,
|
|
12168
|
+
records,
|
|
12169
|
+
validations,
|
|
12170
|
+
guards: {
|
|
12171
|
+
revisionRequired: true,
|
|
12172
|
+
parentRows: plans.length,
|
|
12173
|
+
dmlMaxRows,
|
|
12174
|
+
subtableRows,
|
|
12175
|
+
dmlMaxSubtableRows,
|
|
12176
|
+
wouldExceed: plans.length > dmlMaxRows || subtableRows > dmlMaxSubtableRows
|
|
12005
12177
|
}
|
|
12006
|
-
}
|
|
12007
|
-
return normalized;
|
|
12008
|
-
}
|
|
12009
|
-
|
|
12010
|
-
// src/core/outerJoinSearchAbortGuard.ts
|
|
12011
|
-
function isOuterJoinSelect(value) {
|
|
12012
|
-
if (value["type"] !== "SELECT" || !Array.isArray(value["joins"])) return false;
|
|
12013
|
-
return value["joins"].some((join2) => {
|
|
12014
|
-
if (join2 === null || typeof join2 !== "object") return false;
|
|
12015
|
-
const type = join2["type"];
|
|
12016
|
-
return type === "LEFT" || type === "RIGHT";
|
|
12017
12178
|
});
|
|
12018
12179
|
}
|
|
12019
|
-
function
|
|
12020
|
-
|
|
12021
|
-
|
|
12022
|
-
if (Array.isArray(value)) return value.some(visit);
|
|
12023
|
-
if (value === null || typeof value !== "object" || seen.has(value)) return false;
|
|
12024
|
-
seen.add(value);
|
|
12025
|
-
const object = value;
|
|
12026
|
-
if (isOuterJoinSelect(object)) return true;
|
|
12027
|
-
return Object.values(object).some(visit);
|
|
12028
|
-
};
|
|
12029
|
-
return visit(statement);
|
|
12030
|
-
}
|
|
12031
|
-
function isOuterJoinNonPreservedTable(statement, table, isMainTable) {
|
|
12032
|
-
if (isMainTable) {
|
|
12033
|
-
return table === statement.from && statement.joins.some((join2) => join2.type === "RIGHT");
|
|
12180
|
+
function assertMatchCoverage(statement, matches) {
|
|
12181
|
+
if (matches.length !== statement.values.length) {
|
|
12182
|
+
throw new Error("InternalError: UPSERT APPLY match count differs from VALUES rows.");
|
|
12034
12183
|
}
|
|
12035
|
-
|
|
12036
|
-
|
|
12037
|
-
if (
|
|
12038
|
-
|
|
12039
|
-
return true;
|
|
12184
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12185
|
+
for (const match of matches) {
|
|
12186
|
+
if (!Number.isSafeInteger(match.sourceRowIndex) || match.sourceRowIndex < 0 || match.sourceRowIndex >= statement.values.length || seen.has(match.sourceRowIndex)) {
|
|
12187
|
+
throw new Error("InternalError: UPSERT APPLY matches must cover each source row exactly once.");
|
|
12040
12188
|
}
|
|
12189
|
+
seen.add(match.sourceRowIndex);
|
|
12041
12190
|
}
|
|
12042
|
-
return false;
|
|
12043
12191
|
}
|
|
12044
|
-
|
|
12045
|
-
|
|
12046
|
-
|
|
12047
|
-
|
|
12048
|
-
|
|
12049
|
-
|
|
12050
|
-
const month = Number(match[2]);
|
|
12051
|
-
const day = Number(match[3]);
|
|
12052
|
-
if (year < 1 || year > 9999) return false;
|
|
12053
|
-
const date = /* @__PURE__ */ new Date(0);
|
|
12054
|
-
date.setUTCFullYear(year, month - 1, day);
|
|
12055
|
-
date.setUTCHours(0, 0, 0, 0);
|
|
12056
|
-
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
|
|
12192
|
+
function chunk2(records, app) {
|
|
12193
|
+
const batches = [];
|
|
12194
|
+
for (let index = 0; index < records.length; index += 100) {
|
|
12195
|
+
batches.push({ app, records: records.slice(index, index + 100) });
|
|
12196
|
+
}
|
|
12197
|
+
return batches;
|
|
12057
12198
|
}
|
|
12058
|
-
function
|
|
12059
|
-
|
|
12060
|
-
|
|
12199
|
+
function assertPositiveLimit3(value, name) {
|
|
12200
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
12201
|
+
throw new Error(`ArgumentError: ${name} must be a positive safe integer.`);
|
|
12202
|
+
}
|
|
12061
12203
|
}
|
|
12062
|
-
function
|
|
12063
|
-
|
|
12064
|
-
|
|
12065
|
-
|
|
12204
|
+
function argument5(message) {
|
|
12205
|
+
throw new Error(`ArgumentError: ${message}`);
|
|
12206
|
+
}
|
|
12207
|
+
function deepFreeze3(value, seen = /* @__PURE__ */ new Set()) {
|
|
12208
|
+
if (value === null || typeof value !== "object" || seen.has(value)) return value;
|
|
12209
|
+
seen.add(value);
|
|
12210
|
+
for (const child of Object.values(value)) deepFreeze3(child, seen);
|
|
12211
|
+
return Object.freeze(value);
|
|
12066
12212
|
}
|
|
12067
12213
|
|
|
12068
|
-
// src/core/
|
|
12069
|
-
|
|
12070
|
-
|
|
12071
|
-
"DATE",
|
|
12072
|
-
"DATETIME",
|
|
12073
|
-
"CREATED_TIME",
|
|
12074
|
-
"UPDATED_TIME"
|
|
12075
|
-
]);
|
|
12076
|
-
var RELATIVE_DATE_OPERATORS = new Set(RANGE_AND_EQUALITY);
|
|
12077
|
-
var LEGACY_KINTONE_FUNCTION_FIELD_TYPES = /* @__PURE__ */ new Map([
|
|
12078
|
-
["TODAY", /* @__PURE__ */ new Set(["DATE", "DATETIME", "CREATED_TIME", "UPDATED_TIME"])],
|
|
12079
|
-
["NOW", /* @__PURE__ */ new Set(["DATETIME", "CREATED_TIME", "UPDATED_TIME"])],
|
|
12080
|
-
["LOGINUSER", /* @__PURE__ */ new Set(["CREATOR", "MODIFIER", "USER_SELECT"])],
|
|
12081
|
-
["PRIMARY_ORGANIZATION", /* @__PURE__ */ new Set(["ORGANIZATION_SELECT"])]
|
|
12082
|
-
]);
|
|
12083
|
-
var LEGACY_KINTONE_FUNCTION_OPERATORS = /* @__PURE__ */ new Map([
|
|
12084
|
-
["TODAY", new Set(RANGE_AND_EQUALITY)],
|
|
12085
|
-
["NOW", new Set(RANGE_AND_EQUALITY)],
|
|
12086
|
-
["LOGINUSER", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12087
|
-
["PRIMARY_ORGANIZATION", /* @__PURE__ */ new Set(["in", "not in"])]
|
|
12088
|
-
]);
|
|
12089
|
-
var EQUALITY_IN = ["=", "!=", "in", "not in"];
|
|
12090
|
-
var NATIVE_OPERATORS = /* @__PURE__ */ new Map([
|
|
12091
|
-
["RECORD_NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
12092
|
-
["__ID__", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
12093
|
-
["CREATOR", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12094
|
-
["MODIFIER", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12095
|
-
["CREATED_TIME", new Set(RANGE_AND_EQUALITY)],
|
|
12096
|
-
["UPDATED_TIME", new Set(RANGE_AND_EQUALITY)],
|
|
12097
|
-
["DATE", new Set(RANGE_AND_EQUALITY)],
|
|
12098
|
-
["TIME", new Set(RANGE_AND_EQUALITY)],
|
|
12099
|
-
["DATETIME", new Set(RANGE_AND_EQUALITY)],
|
|
12100
|
-
["SINGLE_LINE_TEXT", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
|
|
12101
|
-
["LINK", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
|
|
12102
|
-
["NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
12103
|
-
["CALC", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
12104
|
-
["MULTI_LINE_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
12105
|
-
["RICH_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
12106
|
-
["CHECK_BOX", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12107
|
-
["RADIO_BUTTON", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12108
|
-
["DROP_DOWN", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12109
|
-
["MULTI_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12110
|
-
["FILE", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
12111
|
-
["USER_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12112
|
-
["ORGANIZATION_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12113
|
-
["GROUP_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12114
|
-
["STATUS", new Set(EQUALITY_IN)],
|
|
12115
|
-
["STATUS_ASSIGNEE", /* @__PURE__ */ new Set(["in", "not in"])]
|
|
12116
|
-
]);
|
|
12117
|
-
var LOCAL_VALID_OPERATORS = /* @__PURE__ */ new Map([
|
|
12118
|
-
["CREATOR", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12119
|
-
["MODIFIER", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12120
|
-
["CHECK_BOX", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12121
|
-
["MULTI_SELECT", /* @__PURE__ */ new Set(["in", "not in"])]
|
|
12122
|
-
]);
|
|
12123
|
-
var LOCAL_SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
12124
|
-
"RECORD_NUMBER",
|
|
12125
|
-
"__ID__",
|
|
12126
|
-
"CREATOR",
|
|
12127
|
-
"MODIFIER",
|
|
12128
|
-
"CREATED_TIME",
|
|
12129
|
-
"UPDATED_TIME",
|
|
12130
|
-
"DATE",
|
|
12131
|
-
"TIME",
|
|
12132
|
-
"DATETIME",
|
|
12133
|
-
"SINGLE_LINE_TEXT",
|
|
12134
|
-
"LINK",
|
|
12135
|
-
"NUMBER",
|
|
12136
|
-
"CALC",
|
|
12137
|
-
"MULTI_LINE_TEXT",
|
|
12138
|
-
"RICH_TEXT",
|
|
12139
|
-
"RADIO_BUTTON",
|
|
12140
|
-
"DROP_DOWN",
|
|
12141
|
-
"STATUS",
|
|
12142
|
-
// 一時表・CTE・式列は kintone REST へは送らず、共有ローカル評価器で扱う。
|
|
12143
|
-
"KSQL_STRING",
|
|
12144
|
-
"KSQL_NUMBER",
|
|
12145
|
-
"KSQL_BOOLEAN"
|
|
12146
|
-
]);
|
|
12147
|
-
var LOCAL_COLLECTION_TYPES = /* @__PURE__ */ new Set([
|
|
12148
|
-
"CHECK_BOX",
|
|
12149
|
-
"MULTI_SELECT",
|
|
12150
|
-
"FILE",
|
|
12151
|
-
"USER_SELECT",
|
|
12152
|
-
"ORGANIZATION_SELECT",
|
|
12153
|
-
"GROUP_SELECT",
|
|
12154
|
-
"STATUS_ASSIGNEE",
|
|
12155
|
-
"CATEGORY"
|
|
12156
|
-
]);
|
|
12157
|
-
function nativeWhereOperatorsForType(fieldType) {
|
|
12158
|
-
return NATIVE_OPERATORS.get(fieldType) ?? /* @__PURE__ */ new Set();
|
|
12214
|
+
// src/core/applyDiagnostic.ts
|
|
12215
|
+
function buildPreparedApplyUpdateDiagnostic(prepared) {
|
|
12216
|
+
return diagnostic("UPDATE", [buildPreparedUpdateBranch(prepared)]);
|
|
12159
12217
|
}
|
|
12160
|
-
function
|
|
12161
|
-
|
|
12162
|
-
|
|
12163
|
-
|
|
12164
|
-
|
|
12165
|
-
|
|
12166
|
-
|
|
12167
|
-
|
|
12168
|
-
|
|
12169
|
-
|
|
12170
|
-
|
|
12171
|
-
|
|
12172
|
-
|
|
12173
|
-
|
|
12174
|
-
|
|
12175
|
-
|
|
12176
|
-
|
|
12177
|
-
|
|
12178
|
-
|
|
12179
|
-
|
|
12218
|
+
function buildPreparedApplyInsertDiagnostic(prepared) {
|
|
12219
|
+
return diagnostic("INSERT", [buildPreparedInsertBranch(prepared)]);
|
|
12220
|
+
}
|
|
12221
|
+
function buildPreparedApplyUpsertDiagnostic(prepared) {
|
|
12222
|
+
return diagnostic("UPSERT", [
|
|
12223
|
+
buildPreparedInsertBranch(prepared.create),
|
|
12224
|
+
buildPreparedUpdateBranch(prepared.update)
|
|
12225
|
+
]);
|
|
12226
|
+
}
|
|
12227
|
+
function withApplyDiagnosticProgress(base, progress) {
|
|
12228
|
+
const failedBranch = progress.failedBranch?.toLowerCase();
|
|
12229
|
+
const branches = base.branches.map((branch) => {
|
|
12230
|
+
const successfulParents = base.statementKind === "UPSERT" ? branch.branch === "insert" ? progress.successfulInserts : progress.successfulUpdates : progress.successfulParents;
|
|
12231
|
+
const successfulChunks = base.statementKind === "UPSERT" ? branch.branch === "insert" ? progress.successfulInsertChunks : progress.successfulUpdateChunks : progress.successfulChunks;
|
|
12232
|
+
const isFailedBranch = failedBranch === branch.branch;
|
|
12233
|
+
return {
|
|
12234
|
+
...branch,
|
|
12235
|
+
...successfulParents !== void 0 ? { successfulParents } : {},
|
|
12236
|
+
chunk: {
|
|
12237
|
+
...branch.chunk,
|
|
12238
|
+
...successfulChunks !== void 0 ? { successfulChunks } : {},
|
|
12239
|
+
...isFailedBranch && progress.failedChunkIndex !== void 0 ? { failedChunkIndex: progress.failedChunkIndex } : {},
|
|
12240
|
+
...isFailedBranch && progress.failedStage !== void 0 ? { failedStage: progress.failedStage } : {}
|
|
12180
12241
|
}
|
|
12181
|
-
|
|
12182
|
-
|
|
12183
|
-
|
|
12184
|
-
|
|
12185
|
-
|
|
12186
|
-
|
|
12187
|
-
|
|
12188
|
-
|
|
12189
|
-
|
|
12190
|
-
|
|
12242
|
+
};
|
|
12243
|
+
});
|
|
12244
|
+
return {
|
|
12245
|
+
...base,
|
|
12246
|
+
branches,
|
|
12247
|
+
partialSuccess: {
|
|
12248
|
+
possible: true,
|
|
12249
|
+
successfulParents: progress.successfulParents,
|
|
12250
|
+
successfulChunks: progress.successfulChunks,
|
|
12251
|
+
...failedBranch ? { failedBranch } : {},
|
|
12252
|
+
...progress.retryAttempted !== void 0 ? { retryAttempted: progress.retryAttempted } : {}
|
|
12191
12253
|
}
|
|
12192
|
-
return node;
|
|
12193
12254
|
};
|
|
12194
|
-
return { normalizedWhere: visit(where), rewrites };
|
|
12195
12255
|
}
|
|
12196
|
-
function
|
|
12197
|
-
if (
|
|
12198
|
-
return
|
|
12256
|
+
function buildStaticApplyDiagnostic(statement, dmlMaxRows, dmlMaxSubtableRows) {
|
|
12257
|
+
if (statement.type === "UPDATE") {
|
|
12258
|
+
return diagnostic("UPDATE", [staticBranch(
|
|
12259
|
+
"update",
|
|
12260
|
+
statement.applyBlocks ?? [],
|
|
12261
|
+
null,
|
|
12262
|
+
true,
|
|
12263
|
+
dmlMaxRows,
|
|
12264
|
+
dmlMaxSubtableRows
|
|
12265
|
+
)]);
|
|
12199
12266
|
}
|
|
12200
|
-
|
|
12267
|
+
if (statement.type === "INSERT") {
|
|
12268
|
+
return diagnostic("INSERT", [staticBranch(
|
|
12269
|
+
"insert",
|
|
12270
|
+
statement.applyBlocks ?? [],
|
|
12271
|
+
statement.values.length,
|
|
12272
|
+
false,
|
|
12273
|
+
dmlMaxRows,
|
|
12274
|
+
dmlMaxSubtableRows
|
|
12275
|
+
)]);
|
|
12276
|
+
}
|
|
12277
|
+
return diagnostic("UPSERT", [
|
|
12278
|
+
staticBranch("insert", statement.onInsertApplyBlocks ?? [], null, false, dmlMaxRows, dmlMaxSubtableRows),
|
|
12279
|
+
staticBranch("update", statement.onUpdateApplyBlocks ?? [], null, true, dmlMaxRows, dmlMaxSubtableRows)
|
|
12280
|
+
]);
|
|
12201
12281
|
}
|
|
12202
|
-
function
|
|
12203
|
-
|
|
12204
|
-
|
|
12205
|
-
|
|
12206
|
-
|
|
12207
|
-
|
|
12208
|
-
|
|
12209
|
-
|
|
12210
|
-
|
|
12211
|
-
|
|
12212
|
-
|
|
12213
|
-
|
|
12214
|
-
|
|
12215
|
-
|
|
12216
|
-
|
|
12217
|
-
|
|
12218
|
-
|
|
12219
|
-
return {
|
|
12220
|
-
}
|
|
12221
|
-
|
|
12222
|
-
|
|
12223
|
-
|
|
12282
|
+
function diagnostic(statementKind, branches) {
|
|
12283
|
+
return {
|
|
12284
|
+
statementKind,
|
|
12285
|
+
branches,
|
|
12286
|
+
nonTransactional: true,
|
|
12287
|
+
partialSuccess: { possible: true }
|
|
12288
|
+
};
|
|
12289
|
+
}
|
|
12290
|
+
function buildPreparedInsertBranch(prepared) {
|
|
12291
|
+
const targets = /* @__PURE__ */ new Map();
|
|
12292
|
+
if (prepared.applyBlocks) {
|
|
12293
|
+
for (const block of prepared.applyBlocks) {
|
|
12294
|
+
const operations = block.operations.map((operation) => {
|
|
12295
|
+
if (operation.kind !== "APPEND") {
|
|
12296
|
+
throw new Error(`InternalError: prepared APPLY INSERT contains ${operation.kind}.`);
|
|
12297
|
+
}
|
|
12298
|
+
const addedRows = operation.values.length * prepared.guards.parentRows;
|
|
12299
|
+
return { kind: "APPEND", count: addedRows, addedRows };
|
|
12300
|
+
});
|
|
12301
|
+
targets.set(block.field, {
|
|
12302
|
+
targetKind: block.targetKind,
|
|
12303
|
+
field: block.field,
|
|
12304
|
+
operations,
|
|
12305
|
+
changedCount: operations.reduce((sum, operation) => sum + (operation.addedRows ?? 0), 0)
|
|
12224
12306
|
});
|
|
12225
12307
|
}
|
|
12226
|
-
|
|
12227
|
-
|
|
12228
|
-
const
|
|
12229
|
-
|
|
12308
|
+
} else {
|
|
12309
|
+
for (const candidate of prepared.candidates) {
|
|
12310
|
+
for (const table of candidate.tables) {
|
|
12311
|
+
const current = targets.get(table.table);
|
|
12312
|
+
const addedRows = current?.operations[0]?.addedRows ?? 0;
|
|
12313
|
+
targets.set(table.table, {
|
|
12314
|
+
targetKind: "SUBTABLE",
|
|
12315
|
+
field: table.table,
|
|
12316
|
+
operations: [{ kind: "APPEND", count: addedRows + table.addedRows, addedRows: addedRows + table.addedRows }],
|
|
12317
|
+
changedCount: (current?.changedCount ?? 0) + table.addedRows
|
|
12318
|
+
});
|
|
12319
|
+
}
|
|
12230
12320
|
}
|
|
12231
12321
|
}
|
|
12322
|
+
return {
|
|
12323
|
+
branch: "insert",
|
|
12324
|
+
parentRows: prepared.guards.parentRows,
|
|
12325
|
+
targets: [...targets.values()],
|
|
12326
|
+
guards: prepared.guards,
|
|
12327
|
+
chunk: { size: 100, plannedChunks: prepared.batches.length },
|
|
12328
|
+
deletedParentRows: 0
|
|
12329
|
+
};
|
|
12232
12330
|
}
|
|
12233
|
-
function
|
|
12234
|
-
|
|
12235
|
-
|
|
12236
|
-
|
|
12237
|
-
|
|
12238
|
-
|
|
12239
|
-
|
|
12240
|
-
|
|
12241
|
-
|
|
12242
|
-
|
|
12243
|
-
|
|
12244
|
-
|
|
12245
|
-
|
|
12246
|
-
|
|
12247
|
-
|
|
12248
|
-
|
|
12249
|
-
|
|
12250
|
-
functions[0].name,
|
|
12251
|
-
left.type === "FIELD" ? left.field : void 0,
|
|
12252
|
-
left.type === "FIELD" ? resolveField2(left)?.fieldType : void 0,
|
|
12253
|
-
normalizeOperator(op)
|
|
12254
|
-
);
|
|
12331
|
+
function buildPreparedUpdateBranch(prepared) {
|
|
12332
|
+
const targets = /* @__PURE__ */ new Map();
|
|
12333
|
+
let deletedParentRows = 0;
|
|
12334
|
+
for (const plan of prepared.plans) {
|
|
12335
|
+
let parentHasDeletes = false;
|
|
12336
|
+
for (const table of plan.tables) {
|
|
12337
|
+
const current = targets.get(table.table);
|
|
12338
|
+
targets.set(table.table, {
|
|
12339
|
+
targetKind: "SUBTABLE",
|
|
12340
|
+
field: table.table,
|
|
12341
|
+
operations: mergeOperations(current?.operations, table.operations.map((operation) => ({
|
|
12342
|
+
...operation,
|
|
12343
|
+
count: operation.kind === "PATCH" ? operation.changedRows : operation.kind === "APPEND" ? operation.addedRows : operation.removedRows
|
|
12344
|
+
}))),
|
|
12345
|
+
changedCount: (current?.changedCount ?? 0) + table.changedSubtableRows
|
|
12346
|
+
});
|
|
12347
|
+
parentHasDeletes ||= table.deletedRows > 0;
|
|
12255
12348
|
}
|
|
12256
|
-
|
|
12257
|
-
|
|
12258
|
-
|
|
12259
|
-
|
|
12260
|
-
|
|
12261
|
-
|
|
12262
|
-
|
|
12263
|
-
|
|
12264
|
-
|
|
12265
|
-
|
|
12266
|
-
|
|
12267
|
-
|
|
12268
|
-
|
|
12269
|
-
|
|
12270
|
-
|
|
12271
|
-
|
|
12272
|
-
|
|
12273
|
-
|
|
12274
|
-
const native = nativeWhereOperatorsForType(semantics.fieldType);
|
|
12275
|
-
const rightCanPush = right.type === "STRING" || right.type === "NUMBER" || right.type === "IN_LIST" || isLegacyKintoneFunction(right);
|
|
12276
|
-
const structureAllows = !semantics.requiresCollectionOperators || nativeOp !== "=" && nativeOp !== "!=";
|
|
12277
|
-
const sqlLikeIsResidual = op === "LIKE" || op === "NOT_LIKE";
|
|
12278
|
-
if (rightCanPush && structureAllows && native.has(nativeOp) && !sqlLikeIsResidual) {
|
|
12279
|
-
return {
|
|
12280
|
-
capability: "EXACT_PUSHDOWN",
|
|
12281
|
-
reasons: [{
|
|
12282
|
-
code: "WHERE_EXACT",
|
|
12283
|
-
field: left.field,
|
|
12284
|
-
fieldType: semantics.fieldType,
|
|
12285
|
-
operator: nativeOp
|
|
12286
|
-
}]
|
|
12287
|
-
};
|
|
12349
|
+
for (const field of plan.multiValues) {
|
|
12350
|
+
const current = targets.get(field.field);
|
|
12351
|
+
const postImage = { parentId: plan.parentId, value: field.postImageValue };
|
|
12352
|
+
targets.set(field.field, {
|
|
12353
|
+
targetKind: "MULTI_VALUE",
|
|
12354
|
+
field: field.field,
|
|
12355
|
+
fieldType: field.fieldType,
|
|
12356
|
+
operations: mergeOperations(current?.operations, field.operations.map((operation) => ({
|
|
12357
|
+
kind: operation.kind,
|
|
12358
|
+
value: operation.value,
|
|
12359
|
+
count: operation.changed ? 1 : 0,
|
|
12360
|
+
changed: operation.changed
|
|
12361
|
+
}))),
|
|
12362
|
+
changedCount: (current?.changedCount ?? 0) + field.changedValues,
|
|
12363
|
+
postImages: [...current?.postImages ?? [], postImage]
|
|
12364
|
+
});
|
|
12365
|
+
}
|
|
12366
|
+
if (parentHasDeletes) deletedParentRows += 1;
|
|
12288
12367
|
}
|
|
12289
12368
|
return {
|
|
12290
|
-
|
|
12291
|
-
|
|
12292
|
-
|
|
12293
|
-
|
|
12294
|
-
|
|
12295
|
-
|
|
12296
|
-
}]
|
|
12369
|
+
branch: "update",
|
|
12370
|
+
parentRows: prepared.guards.parentRows,
|
|
12371
|
+
targets: [...targets.values()],
|
|
12372
|
+
guards: prepared.guards,
|
|
12373
|
+
chunk: { size: 100, plannedChunks: Math.ceil(prepared.records.length / 100) },
|
|
12374
|
+
deletedParentRows
|
|
12297
12375
|
};
|
|
12298
12376
|
}
|
|
12299
|
-
function
|
|
12300
|
-
return
|
|
12377
|
+
function mergeOperations(current, next) {
|
|
12378
|
+
if (!current) return next.map((operation) => ({ ...operation }));
|
|
12379
|
+
return next.map((operation, index) => {
|
|
12380
|
+
const previous = current[index];
|
|
12381
|
+
if (!previous || previous.kind !== operation.kind || previous.value !== operation.value) {
|
|
12382
|
+
throw new Error("InternalError: APPLY diagnostic operation shape differs between parents.");
|
|
12383
|
+
}
|
|
12384
|
+
return {
|
|
12385
|
+
...operation,
|
|
12386
|
+
...previous.changed !== void 0 ? { changed: previous.changed } : {},
|
|
12387
|
+
count: addNullable(previous.count, operation.count),
|
|
12388
|
+
...operation.matchedRows !== void 0 ? { matchedRows: addNullable(previous.matchedRows, operation.matchedRows) } : {},
|
|
12389
|
+
...operation.changedRows !== void 0 ? { changedRows: addNullable(previous.changedRows, operation.changedRows) } : {},
|
|
12390
|
+
...operation.addedRows !== void 0 ? { addedRows: addNullable(previous.addedRows, operation.addedRows) } : {},
|
|
12391
|
+
...operation.removedRows !== void 0 ? { removedRows: addNullable(previous.removedRows, operation.removedRows) } : {}
|
|
12392
|
+
};
|
|
12393
|
+
});
|
|
12301
12394
|
}
|
|
12302
|
-
function
|
|
12303
|
-
|
|
12304
|
-
|
|
12305
|
-
|
|
12306
|
-
|
|
12307
|
-
|
|
12308
|
-
|
|
12309
|
-
|
|
12310
|
-
|
|
12311
|
-
|
|
12312
|
-
|
|
12313
|
-
|
|
12314
|
-
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
functionName,
|
|
12320
|
-
left.field,
|
|
12321
|
-
semantics?.fieldType,
|
|
12322
|
-
operator
|
|
12323
|
-
);
|
|
12324
|
-
}
|
|
12325
|
-
const validOperators = LEGACY_KINTONE_FUNCTION_OPERATORS.get(functionName);
|
|
12326
|
-
if (!validOperators.has(operator)) {
|
|
12327
|
-
return legacyKintoneFunctionUnsupported(
|
|
12328
|
-
"WHERE_KINTONE_FUNCTION_OPERATOR_UNSUPPORTED",
|
|
12329
|
-
functionName,
|
|
12330
|
-
left.field,
|
|
12331
|
-
semantics.fieldType,
|
|
12332
|
-
operator
|
|
12333
|
-
);
|
|
12334
|
-
}
|
|
12395
|
+
function addNullable(left, right) {
|
|
12396
|
+
return left === null || left === void 0 || right === null ? null : left + right;
|
|
12397
|
+
}
|
|
12398
|
+
function staticBranch(branch, blocks, parentRows, revisionRequired, dmlMaxRows, dmlMaxSubtableRows) {
|
|
12399
|
+
const targets = blocks.map((block) => {
|
|
12400
|
+
const operations = block.operations.map((operation) => staticOperation(operation, parentRows));
|
|
12401
|
+
const changedCount = operations.every((operation) => operation.count !== null) ? operations.reduce((sum, operation) => sum + operation.count, 0) : null;
|
|
12402
|
+
return {
|
|
12403
|
+
targetKind: block.targetKind,
|
|
12404
|
+
field: block.field,
|
|
12405
|
+
operations,
|
|
12406
|
+
changedCount
|
|
12407
|
+
};
|
|
12408
|
+
});
|
|
12409
|
+
const subtableTargets = targets.filter((target) => target.targetKind === "SUBTABLE");
|
|
12410
|
+
const subtableRows = subtableTargets.every((target) => target.changedCount !== null) ? subtableTargets.reduce((sum, target) => sum + target.changedCount, 0) : null;
|
|
12411
|
+
const wouldExceed = parentRows === null || subtableRows === null ? null : parentRows > dmlMaxRows || subtableRows > dmlMaxSubtableRows;
|
|
12335
12412
|
return {
|
|
12336
|
-
|
|
12337
|
-
|
|
12338
|
-
|
|
12339
|
-
|
|
12340
|
-
|
|
12341
|
-
|
|
12342
|
-
|
|
12343
|
-
|
|
12413
|
+
branch,
|
|
12414
|
+
parentRows,
|
|
12415
|
+
targets,
|
|
12416
|
+
guards: {
|
|
12417
|
+
revisionRequired,
|
|
12418
|
+
parentRows,
|
|
12419
|
+
dmlMaxRows,
|
|
12420
|
+
subtableRows,
|
|
12421
|
+
dmlMaxSubtableRows,
|
|
12422
|
+
wouldExceed
|
|
12423
|
+
},
|
|
12424
|
+
chunk: { size: 100, plannedChunks: parentRows === null ? null : Math.ceil(parentRows / 100) },
|
|
12425
|
+
deletedParentRows: branch === "insert" ? 0 : null
|
|
12344
12426
|
};
|
|
12345
|
-
}
|
|
12346
|
-
function
|
|
12347
|
-
|
|
12348
|
-
|
|
12349
|
-
|
|
12350
|
-
return relativeDateUnsupported(
|
|
12351
|
-
"WHERE_RELATIVE_DATE_CONTEXT_UNSUPPORTED",
|
|
12352
|
-
functionName,
|
|
12353
|
-
void 0,
|
|
12354
|
-
void 0,
|
|
12355
|
-
operator
|
|
12356
|
-
);
|
|
12357
|
-
}
|
|
12358
|
-
const semantics = resolveField2(left);
|
|
12359
|
-
if (!semantics) {
|
|
12360
|
-
return relativeDateUnsupported(
|
|
12361
|
-
"WHERE_RELATIVE_DATE_FIELD_TYPE_UNSUPPORTED",
|
|
12362
|
-
functionName,
|
|
12363
|
-
left.field,
|
|
12364
|
-
void 0,
|
|
12365
|
-
operator
|
|
12366
|
-
);
|
|
12367
|
-
}
|
|
12368
|
-
if (!hasValidRelativeDateArguments(right)) {
|
|
12369
|
-
return relativeDateUnsupported(
|
|
12370
|
-
"WHERE_RELATIVE_DATE_ARGUMENT_INVALID",
|
|
12371
|
-
functionName,
|
|
12372
|
-
left.field,
|
|
12373
|
-
semantics.fieldType,
|
|
12374
|
-
operator
|
|
12375
|
-
);
|
|
12376
|
-
}
|
|
12377
|
-
if (!RELATIVE_DATE_OPERATORS.has(operator)) {
|
|
12378
|
-
return relativeDateUnsupported(
|
|
12379
|
-
"WHERE_RELATIVE_DATE_OPERATOR_UNSUPPORTED",
|
|
12380
|
-
functionName,
|
|
12381
|
-
left.field,
|
|
12382
|
-
semantics.fieldType,
|
|
12383
|
-
operator
|
|
12384
|
-
);
|
|
12427
|
+
}
|
|
12428
|
+
function staticOperation(operation, parentRows) {
|
|
12429
|
+
if (operation.kind === "APPEND") {
|
|
12430
|
+
const addedRows = parentRows === null ? null : operation.values.length * parentRows;
|
|
12431
|
+
return { kind: operation.kind, count: addedRows, addedRows };
|
|
12385
12432
|
}
|
|
12386
|
-
if (
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
12390
|
-
|
|
12391
|
-
|
|
12392
|
-
|
|
12433
|
+
if (operation.kind === "PATCH") return { kind: operation.kind, count: null, matchedRows: null, changedRows: null };
|
|
12434
|
+
if (operation.kind === "REMOVE") return { kind: operation.kind, count: null, removedRows: null };
|
|
12435
|
+
return { kind: operation.kind, value: operation.value, count: null };
|
|
12436
|
+
}
|
|
12437
|
+
|
|
12438
|
+
// src/core/applyPatchExecutePrepared.ts
|
|
12439
|
+
var ApplyWritePartialFailureError = class extends Error {
|
|
12440
|
+
constructor(partialSuccess, cause) {
|
|
12441
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
12442
|
+
const method = partialSuccess.failedStage === "POST_CHUNK" ? "POST" : "PUT";
|
|
12443
|
+
const branch = partialSuccess.failedBranch ? ` UPSERT ${partialSuccess.failedBranch}` : "";
|
|
12444
|
+
super(
|
|
12445
|
+
`ApplyWritePartialFailureError: APPLY${branch} ${method} chunk ${partialSuccess.failedChunkIndex + 1} failed (index ${partialSuccess.failedChunkIndex}) after ${partialSuccess.successfulChunks} successful chunk(s) and ${partialSuccess.successfulParents} successful parent(s); writes are non-transactional and were not retried. Cause: ${detail}`
|
|
12393
12446
|
);
|
|
12447
|
+
this.name = "ApplyWritePartialFailureError";
|
|
12448
|
+
this.partialSuccess = partialSuccess;
|
|
12449
|
+
this.cause = cause;
|
|
12450
|
+
}
|
|
12451
|
+
};
|
|
12452
|
+
async function executePreparedApplyWrite(prepared, client, diagnostic2) {
|
|
12453
|
+
assertApplyInternalWriteScope("phase10c");
|
|
12454
|
+
const batches = applyPatchPlansToKintoneBatches(prepared);
|
|
12455
|
+
let successfulChunks = 0;
|
|
12456
|
+
let successfulParents = 0;
|
|
12457
|
+
for (let failedChunkIndex = 0; failedChunkIndex < batches.length; failedChunkIndex += 1) {
|
|
12458
|
+
const batch = batches[failedChunkIndex];
|
|
12459
|
+
try {
|
|
12460
|
+
await client.putRecords(batch);
|
|
12461
|
+
} catch (cause) {
|
|
12462
|
+
throw new ApplyWritePartialFailureError({
|
|
12463
|
+
successfulChunks,
|
|
12464
|
+
successfulParents,
|
|
12465
|
+
failedChunkIndex,
|
|
12466
|
+
failedStage: "PUT_CHUNK",
|
|
12467
|
+
nonTransactional: true,
|
|
12468
|
+
retryAttempted: false,
|
|
12469
|
+
...diagnostic2 ? { diagnostic: withApplyDiagnosticProgress(diagnostic2, {
|
|
12470
|
+
successfulChunks,
|
|
12471
|
+
successfulParents,
|
|
12472
|
+
failedChunkIndex,
|
|
12473
|
+
failedStage: "PUT_CHUNK",
|
|
12474
|
+
failedBranch: "UPDATE",
|
|
12475
|
+
retryAttempted: false
|
|
12476
|
+
}) } : {}
|
|
12477
|
+
}, cause);
|
|
12478
|
+
}
|
|
12479
|
+
successfulChunks += 1;
|
|
12480
|
+
successfulParents += batch.records.length;
|
|
12394
12481
|
}
|
|
12395
12482
|
return {
|
|
12396
|
-
|
|
12397
|
-
|
|
12398
|
-
|
|
12399
|
-
|
|
12400
|
-
|
|
12401
|
-
fieldType: semantics.fieldType,
|
|
12402
|
-
operator
|
|
12403
|
-
}]
|
|
12483
|
+
type: "UPDATE",
|
|
12484
|
+
updatedCount: successfulParents,
|
|
12485
|
+
successfulChunks,
|
|
12486
|
+
successfulParents,
|
|
12487
|
+
nonTransactional: true
|
|
12404
12488
|
};
|
|
12405
12489
|
}
|
|
12406
|
-
|
|
12407
|
-
|
|
12408
|
-
|
|
12409
|
-
|
|
12410
|
-
|
|
12411
|
-
|
|
12412
|
-
|
|
12413
|
-
|
|
12414
|
-
|
|
12415
|
-
|
|
12416
|
-
|
|
12417
|
-
|
|
12418
|
-
|
|
12419
|
-
|
|
12420
|
-
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12425
|
-
|
|
12426
|
-
|
|
12490
|
+
|
|
12491
|
+
// src/core/applyInsertExecutePrepared.ts
|
|
12492
|
+
async function executePreparedApplyInsert(prepared, client, diagnostic2) {
|
|
12493
|
+
assertApplyInternalWriteScope("phase13c");
|
|
12494
|
+
const createdIds = [];
|
|
12495
|
+
let successfulChunks = 0;
|
|
12496
|
+
let successfulParents = 0;
|
|
12497
|
+
for (let failedChunkIndex = 0; failedChunkIndex < prepared.batches.length; failedChunkIndex += 1) {
|
|
12498
|
+
const batch = prepared.batches[failedChunkIndex];
|
|
12499
|
+
try {
|
|
12500
|
+
const response = await client.postRecords({ app: batch.app, records: [...batch.records] });
|
|
12501
|
+
createdIds.push(response.ids);
|
|
12502
|
+
} catch (cause) {
|
|
12503
|
+
throw new ApplyWritePartialFailureError({
|
|
12504
|
+
successfulChunks,
|
|
12505
|
+
successfulParents,
|
|
12506
|
+
failedChunkIndex,
|
|
12507
|
+
failedStage: "POST_CHUNK",
|
|
12508
|
+
nonTransactional: true,
|
|
12509
|
+
retryAttempted: false,
|
|
12510
|
+
...diagnostic2 ? { diagnostic: withApplyDiagnosticProgress(diagnostic2, {
|
|
12511
|
+
successfulChunks,
|
|
12512
|
+
successfulParents,
|
|
12513
|
+
failedChunkIndex,
|
|
12514
|
+
failedStage: "POST_CHUNK",
|
|
12515
|
+
failedBranch: "INSERT",
|
|
12516
|
+
retryAttempted: false
|
|
12517
|
+
}) } : {}
|
|
12518
|
+
}, cause);
|
|
12519
|
+
}
|
|
12520
|
+
successfulChunks += 1;
|
|
12521
|
+
successfulParents += batch.records.length;
|
|
12427
12522
|
}
|
|
12523
|
+
return {
|
|
12524
|
+
type: "INSERT",
|
|
12525
|
+
createdIds,
|
|
12526
|
+
insertedCount: successfulParents,
|
|
12527
|
+
successfulChunks,
|
|
12528
|
+
successfulParents,
|
|
12529
|
+
nonTransactional: true
|
|
12530
|
+
};
|
|
12428
12531
|
}
|
|
12429
|
-
|
|
12430
|
-
|
|
12431
|
-
|
|
12432
|
-
|
|
12433
|
-
|
|
12434
|
-
|
|
12435
|
-
|
|
12436
|
-
|
|
12437
|
-
|
|
12438
|
-
|
|
12532
|
+
|
|
12533
|
+
// src/core/applyUpsertExecutePrepared.ts
|
|
12534
|
+
async function executePreparedApplyUpsert(prepared, client, diagnostic2) {
|
|
12535
|
+
assertApplyInternalWriteScope("phase14c");
|
|
12536
|
+
const createdIds = [];
|
|
12537
|
+
let successfulInsertChunks = 0;
|
|
12538
|
+
let successfulUpdateChunks = 0;
|
|
12539
|
+
let successfulInserts = 0;
|
|
12540
|
+
let successfulUpdates = 0;
|
|
12541
|
+
for (let failedChunkIndex = 0; failedChunkIndex < prepared.createBatches.length; failedChunkIndex += 1) {
|
|
12542
|
+
const batch = prepared.createBatches[failedChunkIndex];
|
|
12543
|
+
try {
|
|
12544
|
+
const response = await client.postRecords({ app: batch.app, records: [...batch.records] });
|
|
12545
|
+
createdIds.push(response.ids);
|
|
12546
|
+
} catch (cause) {
|
|
12547
|
+
throw new ApplyWritePartialFailureError({
|
|
12548
|
+
successfulChunks: successfulInsertChunks + successfulUpdateChunks,
|
|
12549
|
+
successfulParents: successfulInserts + successfulUpdates,
|
|
12550
|
+
successfulInserts,
|
|
12551
|
+
successfulUpdates,
|
|
12552
|
+
failedChunkIndex,
|
|
12553
|
+
failedBranch: "INSERT",
|
|
12554
|
+
failedStage: "POST_CHUNK",
|
|
12555
|
+
nonTransactional: true,
|
|
12556
|
+
retryAttempted: false,
|
|
12557
|
+
...diagnostic2 ? { diagnostic: withApplyDiagnosticProgress(diagnostic2, {
|
|
12558
|
+
successfulChunks: successfulInsertChunks + successfulUpdateChunks,
|
|
12559
|
+
successfulParents: successfulInserts + successfulUpdates,
|
|
12560
|
+
successfulInserts,
|
|
12561
|
+
successfulUpdates,
|
|
12562
|
+
successfulInsertChunks,
|
|
12563
|
+
successfulUpdateChunks,
|
|
12564
|
+
failedChunkIndex,
|
|
12565
|
+
failedStage: "POST_CHUNK",
|
|
12566
|
+
failedBranch: "INSERT",
|
|
12567
|
+
retryAttempted: false
|
|
12568
|
+
}) } : {}
|
|
12569
|
+
}, cause);
|
|
12570
|
+
}
|
|
12571
|
+
successfulInsertChunks += 1;
|
|
12572
|
+
successfulInserts += batch.records.length;
|
|
12439
12573
|
}
|
|
12440
|
-
|
|
12441
|
-
|
|
12574
|
+
for (let failedChunkIndex = 0; failedChunkIndex < prepared.updateBatches.length; failedChunkIndex += 1) {
|
|
12575
|
+
const batch = prepared.updateBatches[failedChunkIndex];
|
|
12576
|
+
try {
|
|
12577
|
+
await client.putRecords({ app: batch.app, records: [...batch.records] });
|
|
12578
|
+
} catch (cause) {
|
|
12579
|
+
throw new ApplyWritePartialFailureError({
|
|
12580
|
+
successfulChunks: successfulInsertChunks + successfulUpdateChunks,
|
|
12581
|
+
successfulParents: successfulInserts + successfulUpdates,
|
|
12582
|
+
successfulInserts,
|
|
12583
|
+
successfulUpdates,
|
|
12584
|
+
failedChunkIndex,
|
|
12585
|
+
failedBranch: "UPDATE",
|
|
12586
|
+
failedStage: "PUT_CHUNK",
|
|
12587
|
+
nonTransactional: true,
|
|
12588
|
+
retryAttempted: false,
|
|
12589
|
+
...diagnostic2 ? { diagnostic: withApplyDiagnosticProgress(diagnostic2, {
|
|
12590
|
+
successfulChunks: successfulInsertChunks + successfulUpdateChunks,
|
|
12591
|
+
successfulParents: successfulInserts + successfulUpdates,
|
|
12592
|
+
successfulInserts,
|
|
12593
|
+
successfulUpdates,
|
|
12594
|
+
successfulInsertChunks,
|
|
12595
|
+
successfulUpdateChunks,
|
|
12596
|
+
failedChunkIndex,
|
|
12597
|
+
failedStage: "PUT_CHUNK",
|
|
12598
|
+
failedBranch: "UPDATE",
|
|
12599
|
+
retryAttempted: false
|
|
12600
|
+
}) } : {}
|
|
12601
|
+
}, cause);
|
|
12602
|
+
}
|
|
12603
|
+
successfulUpdateChunks += 1;
|
|
12604
|
+
successfulUpdates += batch.records.length;
|
|
12442
12605
|
}
|
|
12443
12606
|
return {
|
|
12444
|
-
|
|
12445
|
-
|
|
12607
|
+
type: "UPSERT",
|
|
12608
|
+
createdIds,
|
|
12609
|
+
insertedCount: successfulInserts,
|
|
12610
|
+
updatedCount: successfulUpdates,
|
|
12611
|
+
successfulChunks: successfulInsertChunks + successfulUpdateChunks,
|
|
12612
|
+
successfulParents: successfulInserts + successfulUpdates,
|
|
12613
|
+
successfulInsertChunks,
|
|
12614
|
+
successfulUpdateChunks,
|
|
12615
|
+
nonTransactional: true
|
|
12446
12616
|
};
|
|
12447
12617
|
}
|
|
12448
|
-
|
|
12449
|
-
|
|
12450
|
-
|
|
12451
|
-
|
|
12452
|
-
|
|
12453
|
-
|
|
12454
|
-
|
|
12455
|
-
|
|
12456
|
-
}
|
|
12457
|
-
function normalizeOperator(op) {
|
|
12458
|
-
switch (op) {
|
|
12459
|
-
case "<>":
|
|
12460
|
-
return "!=";
|
|
12461
|
-
case "IN":
|
|
12462
|
-
return "in";
|
|
12463
|
-
case "NOT_IN":
|
|
12464
|
-
return "not in";
|
|
12465
|
-
case "LIKE":
|
|
12466
|
-
case "KLIKE":
|
|
12467
|
-
return "like";
|
|
12468
|
-
case "NOT_LIKE":
|
|
12469
|
-
case "NOT_KLIKE":
|
|
12470
|
-
return "not like";
|
|
12471
|
-
default:
|
|
12472
|
-
return op;
|
|
12618
|
+
|
|
12619
|
+
// src/core/batchVariables.ts
|
|
12620
|
+
var VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
|
|
12621
|
+
function normalizeBatchVariableName(name) {
|
|
12622
|
+
if (!VARIABLE_NAME_RE.test(name)) {
|
|
12623
|
+
throw new Error(
|
|
12624
|
+
`ArgumentError: invalid variable name "${name}". Use a name without @ matching [A-Za-z_][A-Za-z0-9_]{0,63}.`
|
|
12625
|
+
);
|
|
12473
12626
|
}
|
|
12627
|
+
return name.toLowerCase();
|
|
12474
12628
|
}
|
|
12475
|
-
function
|
|
12476
|
-
const
|
|
12477
|
-
|
|
12478
|
-
|
|
12479
|
-
|
|
12480
|
-
|
|
12481
|
-
|
|
12482
|
-
|
|
12483
|
-
if (op === "AND" && (left.capability === "EXACT_PUSHDOWN" || right.capability === "EXACT_PUSHDOWN" || left.capability === "SUPERSET_PREFILTER" || right.capability === "SUPERSET_PREFILTER")) {
|
|
12484
|
-
return requireExactFunctionPushdown({
|
|
12485
|
-
capability: "SUPERSET_PREFILTER",
|
|
12486
|
-
reasons: [{ code: "WHERE_SUPERSET_PREFILTER" }, ...reasons]
|
|
12487
|
-
});
|
|
12629
|
+
function normalizeBatchVariables(input) {
|
|
12630
|
+
const normalized = /* @__PURE__ */ Object.create(null);
|
|
12631
|
+
for (const [rawName, value] of Object.entries(input ?? {})) {
|
|
12632
|
+
const name = normalizeBatchVariableName(rawName);
|
|
12633
|
+
if (Object.prototype.hasOwnProperty.call(normalized, name)) {
|
|
12634
|
+
throw new Error(`ArgumentError: variable "${rawName}" is specified more than once.`);
|
|
12635
|
+
}
|
|
12636
|
+
normalized[name] = value;
|
|
12488
12637
|
}
|
|
12489
|
-
return
|
|
12490
|
-
}
|
|
12491
|
-
function localExpression() {
|
|
12492
|
-
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
12493
|
-
}
|
|
12494
|
-
function unsupported2(code, field, fieldType, operator) {
|
|
12495
|
-
return { capability: "UNSUPPORTED", reasons: [{ code, field, fieldType, operator }] };
|
|
12496
|
-
}
|
|
12497
|
-
function legacyKintoneFunctionUnsupported(code, functionName, field, fieldType, operator) {
|
|
12498
|
-
return requireExactFunctionPushdown({
|
|
12499
|
-
capability: "UNSUPPORTED",
|
|
12500
|
-
reasons: [{ code, functionName, field, fieldType, operator }]
|
|
12501
|
-
});
|
|
12502
|
-
}
|
|
12503
|
-
function relativeDateUnsupported(code, functionName, field, fieldType, operator) {
|
|
12504
|
-
return requireExactRelativeDatePushdown({
|
|
12505
|
-
capability: "UNSUPPORTED",
|
|
12506
|
-
reasons: [{ code, functionName, field, fieldType, operator }]
|
|
12507
|
-
});
|
|
12638
|
+
return normalized;
|
|
12508
12639
|
}
|
|
12509
|
-
function
|
|
12510
|
-
|
|
12511
|
-
|
|
12640
|
+
function validateDeclaredBatchVariables(statements, input) {
|
|
12641
|
+
const normalized = normalizeBatchVariables(input);
|
|
12642
|
+
const declared = new Set(
|
|
12643
|
+
statements.filter((stmt) => stmt.type === "DECLARE_VARIABLE").map((stmt) => stmt.name)
|
|
12512
12644
|
);
|
|
12645
|
+
for (const name of Object.keys(normalized)) {
|
|
12646
|
+
if (!declared.has(name)) {
|
|
12647
|
+
throw new Error(`ArgumentError: injected variable @${name} is not declared.`);
|
|
12648
|
+
}
|
|
12649
|
+
}
|
|
12650
|
+
return normalized;
|
|
12513
12651
|
}
|
|
12514
|
-
|
|
12515
|
-
|
|
12516
|
-
|
|
12517
|
-
);
|
|
12652
|
+
|
|
12653
|
+
// src/core/outerJoinSearchAbortGuard.ts
|
|
12654
|
+
function isOuterJoinSelect(value) {
|
|
12655
|
+
if (value["type"] !== "SELECT" || !Array.isArray(value["joins"])) return false;
|
|
12656
|
+
return value["joins"].some((join2) => {
|
|
12657
|
+
if (join2 === null || typeof join2 !== "object") return false;
|
|
12658
|
+
const type = join2["type"];
|
|
12659
|
+
return type === "LEFT" || type === "RIGHT";
|
|
12660
|
+
});
|
|
12518
12661
|
}
|
|
12519
|
-
function
|
|
12520
|
-
|
|
12521
|
-
|
|
12522
|
-
|
|
12523
|
-
|
|
12524
|
-
|
|
12525
|
-
|
|
12526
|
-
|
|
12527
|
-
|
|
12528
|
-
reasons: [
|
|
12529
|
-
...result.reasons,
|
|
12530
|
-
{
|
|
12531
|
-
code: "WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN",
|
|
12532
|
-
functionName: relative.functionName,
|
|
12533
|
-
field: relative.field,
|
|
12534
|
-
fieldType: relative.fieldType,
|
|
12535
|
-
operator: relative.operator
|
|
12536
|
-
}
|
|
12537
|
-
]
|
|
12662
|
+
function statementContainsOuterJoin(statement) {
|
|
12663
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12664
|
+
const visit = (value) => {
|
|
12665
|
+
if (Array.isArray(value)) return value.some(visit);
|
|
12666
|
+
if (value === null || typeof value !== "object" || seen.has(value)) return false;
|
|
12667
|
+
seen.add(value);
|
|
12668
|
+
const object = value;
|
|
12669
|
+
if (isOuterJoinSelect(object)) return true;
|
|
12670
|
+
return Object.values(object).some(visit);
|
|
12538
12671
|
};
|
|
12672
|
+
return visit(statement);
|
|
12539
12673
|
}
|
|
12540
|
-
function
|
|
12541
|
-
if (
|
|
12542
|
-
(
|
|
12543
|
-
)) {
|
|
12544
|
-
return result;
|
|
12674
|
+
function isOuterJoinNonPreservedTable(statement, table, isMainTable) {
|
|
12675
|
+
if (isMainTable) {
|
|
12676
|
+
return table === statement.from && statement.joins.some((join2) => join2.type === "RIGHT");
|
|
12545
12677
|
}
|
|
12546
|
-
|
|
12547
|
-
|
|
12548
|
-
|
|
12549
|
-
|
|
12550
|
-
|
|
12551
|
-
|
|
12552
|
-
|
|
12553
|
-
|
|
12554
|
-
code: "WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN",
|
|
12555
|
-
functionName: legacy.functionName,
|
|
12556
|
-
field: legacy.field,
|
|
12557
|
-
fieldType: legacy.fieldType,
|
|
12558
|
-
operator: legacy.operator
|
|
12559
|
-
}
|
|
12560
|
-
]
|
|
12561
|
-
};
|
|
12562
|
-
}
|
|
12563
|
-
function requireExactFunctionPushdown(result) {
|
|
12564
|
-
return requireExactLegacyKintoneFunctionPushdown(
|
|
12565
|
-
requireExactRelativeDatePushdown(result)
|
|
12566
|
-
);
|
|
12678
|
+
for (let index = 0; index < statement.joins.length; index += 1) {
|
|
12679
|
+
const join2 = statement.joins[index];
|
|
12680
|
+
if (join2.type === "LEFT" && table === join2.table) return true;
|
|
12681
|
+
if (join2.type === "RIGHT" && statement.joins.slice(0, index).some((previousJoin) => table === previousJoin.table)) {
|
|
12682
|
+
return true;
|
|
12683
|
+
}
|
|
12684
|
+
}
|
|
12685
|
+
return false;
|
|
12567
12686
|
}
|
|
12568
12687
|
|
|
12569
12688
|
// src/core/optimization/joinKeyPrefilter.ts
|
|
@@ -12580,7 +12699,7 @@ function buildJoinKeyPrefilterQueries(plan, field, quoteValue) {
|
|
|
12580
12699
|
}
|
|
12581
12700
|
return queries;
|
|
12582
12701
|
}
|
|
12583
|
-
var
|
|
12702
|
+
var DATETIME_TYPES2 = /* @__PURE__ */ new Set(["DATETIME", "CREATED_TIME", "UPDATED_TIME"]);
|
|
12584
12703
|
var JOIN_KEY_EMPTY_IN_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
12585
12704
|
"SINGLE_LINE_TEXT",
|
|
12586
12705
|
"LINK",
|
|
@@ -12595,14 +12714,14 @@ var JOIN_KEY_EMPTY_IN_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
|
12595
12714
|
function canonicalFor(fieldType, value) {
|
|
12596
12715
|
if (fieldType === "DATE") return isCanonicalJoinDate(value);
|
|
12597
12716
|
if (fieldType === "TIME") return isCanonicalJoinTime(value);
|
|
12598
|
-
if (
|
|
12717
|
+
if (DATETIME_TYPES2.has(fieldType)) return isCanonicalJoinDateTime(value);
|
|
12599
12718
|
return false;
|
|
12600
12719
|
}
|
|
12601
12720
|
function semanticsMatch(fieldType, semantics) {
|
|
12602
12721
|
if (!semantics || semantics.compareMode !== "string") return false;
|
|
12603
12722
|
if (fieldType === "DATE") return semantics.fieldType === "DATE";
|
|
12604
12723
|
if (fieldType === "TIME") return semantics.fieldType === "TIME";
|
|
12605
|
-
if (
|
|
12724
|
+
if (DATETIME_TYPES2.has(fieldType)) return DATETIME_TYPES2.has(semantics.fieldType);
|
|
12606
12725
|
return false;
|
|
12607
12726
|
}
|
|
12608
12727
|
function planJoinKeyPrefilter(input) {
|
|
@@ -12904,49 +13023,7 @@ async function resolveDmlTargetIds(getRecords, app, query, options) {
|
|
|
12904
13023
|
};
|
|
12905
13024
|
}
|
|
12906
13025
|
|
|
12907
|
-
// src/core/optimization/joinNumberLiteralPolicy.ts
|
|
12908
|
-
function isJoinNumberLiteralSupported(literal) {
|
|
12909
|
-
const source = literal.raw ?? String(literal.value);
|
|
12910
|
-
const decimal = parseExactDecimal(source);
|
|
12911
|
-
if (decimal === null) return false;
|
|
12912
|
-
if (decimal.sign === 0) return numberLiteralText(literal) === "0";
|
|
12913
|
-
const fractionDigits = Math.max(decimal.scale, 0);
|
|
12914
|
-
const integerDigits = Math.max(decimal.coefficient.length - decimal.scale, 0);
|
|
12915
|
-
if (fractionDigits > 10 || integerDigits + fractionDigits > 30) {
|
|
12916
|
-
return false;
|
|
12917
|
-
}
|
|
12918
|
-
const canonical = formatPlainDecimal(decimal);
|
|
12919
|
-
return numberLiteralText(literal) === canonical;
|
|
12920
|
-
}
|
|
12921
|
-
|
|
12922
13026
|
// src/core/optimization/joinPredicatePushdown.ts
|
|
12923
|
-
var SELECTION_TYPES = /* @__PURE__ */ new Set([
|
|
12924
|
-
"DROP_DOWN",
|
|
12925
|
-
"RADIO_BUTTON",
|
|
12926
|
-
"CHECK_BOX",
|
|
12927
|
-
"MULTI_SELECT",
|
|
12928
|
-
"STATUS"
|
|
12929
|
-
]);
|
|
12930
|
-
var USER_CODE_TYPES = /* @__PURE__ */ new Set([
|
|
12931
|
-
"CREATOR",
|
|
12932
|
-
"MODIFIER",
|
|
12933
|
-
"USER_SELECT",
|
|
12934
|
-
"ORGANIZATION_SELECT",
|
|
12935
|
-
"GROUP_SELECT",
|
|
12936
|
-
"STATUS_ASSIGNEE"
|
|
12937
|
-
]);
|
|
12938
|
-
var KLIKE_TYPES = /* @__PURE__ */ new Set([
|
|
12939
|
-
"SINGLE_LINE_TEXT",
|
|
12940
|
-
"LINK",
|
|
12941
|
-
"MULTI_LINE_TEXT",
|
|
12942
|
-
"RICH_TEXT",
|
|
12943
|
-
"FILE"
|
|
12944
|
-
]);
|
|
12945
|
-
var DATETIME_TYPES2 = /* @__PURE__ */ new Set([
|
|
12946
|
-
"DATETIME",
|
|
12947
|
-
"CREATED_TIME",
|
|
12948
|
-
"UPDATED_TIME"
|
|
12949
|
-
]);
|
|
12950
13027
|
function resolveJoinFieldOwner(field, sources) {
|
|
12951
13028
|
if (field.tableAlias !== null) {
|
|
12952
13029
|
const aliases = sources.filter((source2) => source2.alias === field.tableAlias);
|
|
@@ -12974,7 +13051,11 @@ function classifyJoinPushdownLeaf(predicate, sources) {
|
|
|
12974
13051
|
return type === void 0 ? void 0 : resolveFieldSemantics({ fieldType: type });
|
|
12975
13052
|
});
|
|
12976
13053
|
if (capability.capability !== "EXACT_PUSHDOWN") return unsafe();
|
|
12977
|
-
const relation = classifySupportedLeaf(predicate,
|
|
13054
|
+
const relation = classifySupportedLeaf(predicate, {
|
|
13055
|
+
fieldCode: owner.fieldCode,
|
|
13056
|
+
fieldType,
|
|
13057
|
+
fieldOptions: owner.source.fieldOptions?.get(owner.fieldCode)
|
|
13058
|
+
});
|
|
12978
13059
|
return relation === "unsafe" ? unsafe() : Object.freeze({ relation, owner });
|
|
12979
13060
|
}
|
|
12980
13061
|
function classifyJoinServerFunctionLeaf(predicate, sources) {
|
|
@@ -13529,78 +13610,6 @@ function mergeAndFragments(fragments) {
|
|
|
13529
13610
|
}
|
|
13530
13611
|
return merged;
|
|
13531
13612
|
}
|
|
13532
|
-
function classifySupportedLeaf(predicate, owner, fieldType) {
|
|
13533
|
-
if (predicate.op === "LIKE" || predicate.op === "NOT_LIKE") return "unsafe";
|
|
13534
|
-
if (predicate.op === "KLIKE" || predicate.op === "NOT_KLIKE") {
|
|
13535
|
-
return KLIKE_TYPES.has(fieldType) && predicate.right.type === "STRING" && predicate.right.value !== "" ? "exact" : "unsafe";
|
|
13536
|
-
}
|
|
13537
|
-
if (fieldType === "__ID__" || owner.fieldCode === "$id") {
|
|
13538
|
-
return isPositiveSafeInteger(predicate.right) && (predicate.op === "=" || predicate.op === "<" || predicate.op === ">" || predicate.op === "<=" || predicate.op === ">=") ? "exact" : "unsafe";
|
|
13539
|
-
}
|
|
13540
|
-
if (fieldType === "RECORD_NUMBER") {
|
|
13541
|
-
return classifySupersetScalarOrListLiteral(predicate);
|
|
13542
|
-
}
|
|
13543
|
-
if (fieldType === "NUMBER") {
|
|
13544
|
-
if ((predicate.op === "IN" || predicate.op === "NOT_IN") && predicate.right.type === "IN_LIST" && predicate.right.values.length > 0 && predicate.right.values.every(
|
|
13545
|
-
(value) => value.type === "NUMBER" && isJoinNumberLiteralSupported(value)
|
|
13546
|
-
)) {
|
|
13547
|
-
return "exact";
|
|
13548
|
-
}
|
|
13549
|
-
if ((predicate.op === "=" || predicate.op === "!=" || predicate.op === "<>" || predicate.op === "<" || predicate.op === ">" || predicate.op === "<=" || predicate.op === ">=") && predicate.right.type === "NUMBER" && isJoinNumberLiteralSupported(predicate.right)) {
|
|
13550
|
-
return "exact";
|
|
13551
|
-
}
|
|
13552
|
-
return "unsafe";
|
|
13553
|
-
}
|
|
13554
|
-
if (fieldType === "CALC") {
|
|
13555
|
-
return classifySupersetScalarOrListLiteral(predicate);
|
|
13556
|
-
}
|
|
13557
|
-
if (USER_CODE_TYPES.has(fieldType)) {
|
|
13558
|
-
if (predicate.op !== "IN" && predicate.op !== "NOT_IN" || predicate.right.type !== "IN_LIST" || predicate.right.values.length === 0) return "unsafe";
|
|
13559
|
-
return predicate.right.values.every(
|
|
13560
|
-
(value) => value.type === "STRING" && value.value !== ""
|
|
13561
|
-
) ? "exact" : "unsafe";
|
|
13562
|
-
}
|
|
13563
|
-
if (fieldType === "SINGLE_LINE_TEXT" || fieldType === "LINK") {
|
|
13564
|
-
if ((predicate.op === "IN" || predicate.op === "NOT_IN") && predicate.right.type === "IN_LIST" && predicate.right.values.length > 0 && predicate.right.values.every(
|
|
13565
|
-
(value) => value.type === "STRING" && value.value !== ""
|
|
13566
|
-
)) {
|
|
13567
|
-
return "exact";
|
|
13568
|
-
}
|
|
13569
|
-
return (predicate.op === "=" || predicate.op === "!=" || predicate.op === "<>") && predicate.right.type === "STRING" && predicate.right.value !== "" ? "exact" : "unsafe";
|
|
13570
|
-
}
|
|
13571
|
-
if (fieldType === "DATE" || fieldType === "TIME" || DATETIME_TYPES2.has(fieldType)) {
|
|
13572
|
-
if (predicate.op !== "=" && predicate.op !== "!=" && predicate.op !== "<>" && predicate.op !== "<" && predicate.op !== ">" && predicate.op !== "<=" && predicate.op !== ">=" || predicate.right.type !== "STRING") return "unsafe";
|
|
13573
|
-
if (fieldType === "DATE") {
|
|
13574
|
-
return isCanonicalJoinDate(predicate.right.value) ? "exact" : "unsafe";
|
|
13575
|
-
}
|
|
13576
|
-
if (fieldType === "TIME") {
|
|
13577
|
-
return isCanonicalJoinTime(predicate.right.value) ? "exact" : "unsafe";
|
|
13578
|
-
}
|
|
13579
|
-
return isCanonicalJoinDateTime(predicate.right.value) ? "exact" : "unsafe";
|
|
13580
|
-
}
|
|
13581
|
-
if (SELECTION_TYPES.has(fieldType)) {
|
|
13582
|
-
if (predicate.op !== "IN" && predicate.op !== "NOT_IN" || predicate.right.type !== "IN_LIST" || predicate.right.values.length === 0) return "unsafe";
|
|
13583
|
-
const options = owner.source.fieldOptions?.get(owner.fieldCode);
|
|
13584
|
-
if (options === void 0) return "unsafe";
|
|
13585
|
-
return predicate.right.values.every(
|
|
13586
|
-
(value) => value.type === "STRING" && value.value !== "" && options.has(value.value)
|
|
13587
|
-
) ? "exact" : "unsafe";
|
|
13588
|
-
}
|
|
13589
|
-
return "unsafe";
|
|
13590
|
-
}
|
|
13591
|
-
function classifySupersetScalarOrListLiteral(predicate) {
|
|
13592
|
-
const supportedLiteral = (value) => value.type === "NUMBER" && isJoinNumberLiteralSupported(value) || value.type === "STRING" && value.value !== "";
|
|
13593
|
-
if ((predicate.op === "IN" || predicate.op === "NOT_IN") && predicate.right.type === "IN_LIST") {
|
|
13594
|
-
const values = predicate.right.values;
|
|
13595
|
-
if (values.length > 0 && values.every(supportedLiteral) && values.every((value) => value.type === values[0].type)) {
|
|
13596
|
-
return "superset";
|
|
13597
|
-
}
|
|
13598
|
-
}
|
|
13599
|
-
if ((predicate.op === "=" || predicate.op === "!=" || predicate.op === "<>" || predicate.op === "<" || predicate.op === ">" || predicate.op === "<=" || predicate.op === ">=") && supportedLiteral(predicate.right)) {
|
|
13600
|
-
return "superset";
|
|
13601
|
-
}
|
|
13602
|
-
return "unsafe";
|
|
13603
|
-
}
|
|
13604
13613
|
function owned(source, fieldCode) {
|
|
13605
13614
|
return Object.freeze({
|
|
13606
13615
|
status: "OWNED",
|
|
@@ -13619,12 +13628,6 @@ function sameOwner(left, right) {
|
|
|
13619
13628
|
function combineRelation(left, right) {
|
|
13620
13629
|
return left === "exact" && right === "exact" ? "exact" : "superset";
|
|
13621
13630
|
}
|
|
13622
|
-
function isPositiveSafeInteger(value) {
|
|
13623
|
-
return value.type === "NUMBER" && isSafeIntegerLiteral(value) && value.value > 0;
|
|
13624
|
-
}
|
|
13625
|
-
function isSafeIntegerLiteral(value) {
|
|
13626
|
-
return /^-?\d+$/.test(numberLiteralText(value)) && Number.isSafeInteger(value.value);
|
|
13627
|
-
}
|
|
13628
13631
|
function collectKlikes2(where, out) {
|
|
13629
13632
|
if (where === null) return;
|
|
13630
13633
|
if (isKlike(where)) {
|
|
@@ -15785,6 +15788,11 @@ function rejectionFor(candidate, capability) {
|
|
|
15785
15788
|
reasonCodes
|
|
15786
15789
|
};
|
|
15787
15790
|
}
|
|
15791
|
+
function statementUsesRelativeDateResolution(statement) {
|
|
15792
|
+
const candidates = [];
|
|
15793
|
+
collectStatement(statement, "statement", candidates);
|
|
15794
|
+
return candidates.length > 0;
|
|
15795
|
+
}
|
|
15788
15796
|
async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
15789
15797
|
const candidates = [];
|
|
15790
15798
|
collectStatement(statement, "statement", candidates);
|
|
@@ -19473,6 +19481,20 @@ function extractMainTypedPushdownCandidate(stmt) {
|
|
|
19473
19481
|
if (!stmt.from.alias) return null;
|
|
19474
19482
|
return extractTypedPushdownCandidates(stmt.where, { tableAlias: stmt.from.alias });
|
|
19475
19483
|
}
|
|
19484
|
+
function hasPushdownPlaceholder(where) {
|
|
19485
|
+
if (where.type === "BINARY") {
|
|
19486
|
+
if (where.right.type === "STRING") return where.right.value.startsWith("@");
|
|
19487
|
+
if (where.right.type === "IN_LIST") {
|
|
19488
|
+
return where.right.values.some((value) => value.type === "STRING" && value.value.startsWith("@"));
|
|
19489
|
+
}
|
|
19490
|
+
return false;
|
|
19491
|
+
}
|
|
19492
|
+
if (where.type === "LOGICAL") {
|
|
19493
|
+
return hasPushdownPlaceholder(where.left) || hasPushdownPlaceholder(where.right);
|
|
19494
|
+
}
|
|
19495
|
+
if (where.type === "GROUP" || where.type === "NOT") return hasPushdownPlaceholder(where.expr);
|
|
19496
|
+
return false;
|
|
19497
|
+
}
|
|
19476
19498
|
var boundJoinRuntimePlans = /* @__PURE__ */ new WeakMap();
|
|
19477
19499
|
function buildRuntimeJoinPushdownPlan(stmt, metadata) {
|
|
19478
19500
|
if (stmt.joins.length === 0 || stmt.where === null || stmt.joins.some((join2) => join2.type !== "INNER")) {
|
|
@@ -19511,11 +19533,16 @@ function buildRuntimeJoinPushdownPlan(stmt, metadata) {
|
|
|
19511
19533
|
const mainCondition = conditionsByAlias.get(mainAlias) ?? null;
|
|
19512
19534
|
const joinConditions = new Map(conditionsByAlias);
|
|
19513
19535
|
joinConditions.delete(mainAlias);
|
|
19536
|
+
const relationByAlias = new Map(plan.items.map((item) => [item.targetAlias, item.relation]));
|
|
19537
|
+
const mainRelation = relationByAlias.get(mainAlias) ?? null;
|
|
19538
|
+
relationByAlias.delete(mainAlias);
|
|
19514
19539
|
return {
|
|
19515
19540
|
joinPlan: plan,
|
|
19516
19541
|
queriesByAlias,
|
|
19517
19542
|
mainCondition,
|
|
19543
|
+
mainRelation,
|
|
19518
19544
|
joinConditions,
|
|
19545
|
+
joinRelations: relationByAlias,
|
|
19519
19546
|
appliedKlikes: plan.appliedKlikes,
|
|
19520
19547
|
allKlikes: plan.allKlikes
|
|
19521
19548
|
};
|
|
@@ -24243,6 +24270,7 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
|
|
|
24243
24270
|
return cache;
|
|
24244
24271
|
}
|
|
24245
24272
|
var explainJoinPushdownPlans = /* @__PURE__ */ new WeakMap();
|
|
24273
|
+
var explainPushdownPlans = /* @__PURE__ */ new WeakMap();
|
|
24246
24274
|
var explainJoinKeyPrefilters = /* @__PURE__ */ new WeakMap();
|
|
24247
24275
|
var explainChoiceEqualityRewrites = /* @__PURE__ */ new WeakMap();
|
|
24248
24276
|
var validateExplainInfo = /* @__PURE__ */ new WeakMap();
|
|
@@ -24440,12 +24468,12 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
24440
24468
|
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
|
|
24441
24469
|
}
|
|
24442
24470
|
capabilities.set(select, capability);
|
|
24471
|
+
const pushdownMetadata = await loadTypedPushdownMeta(select, tracedClient, cacheContext);
|
|
24443
24472
|
const preboundJoinPushdownPlan = boundJoinRuntimePlans.get(select);
|
|
24444
|
-
const joinPushdownPlan = preboundJoinPushdownPlan ?? buildRuntimeJoinPushdownPlan(
|
|
24445
|
-
select,
|
|
24446
|
-
await loadTypedPushdownMeta(select, tracedClient, cacheContext)
|
|
24447
|
-
);
|
|
24473
|
+
const joinPushdownPlan = preboundJoinPushdownPlan ?? buildRuntimeJoinPushdownPlan(select, pushdownMetadata);
|
|
24448
24474
|
if (joinPushdownPlan) explainJoinPushdownPlans.set(select, joinPushdownPlan);
|
|
24475
|
+
const resolvedPushdownPlan = joinPushdownPlan ?? buildKlikePushdownPlan(select, pushdownMetadata);
|
|
24476
|
+
explainPushdownPlans.set(select, resolvedPushdownPlan);
|
|
24449
24477
|
const joinKeyPlans = /* @__PURE__ */ new Map();
|
|
24450
24478
|
for (const join2 of select.joins) {
|
|
24451
24479
|
const joinAlias = join2.table.alias;
|
|
@@ -24494,20 +24522,9 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
24494
24522
|
if (runtimePlanConsumesJoin) continue;
|
|
24495
24523
|
}
|
|
24496
24524
|
const queries = buildJoinKeyPrefilterQueries(plan, joinField, sqlQuote);
|
|
24497
|
-
const
|
|
24498
|
-
|
|
24499
|
-
|
|
24500
|
-
if (additionalCandidate) {
|
|
24501
|
-
const infoByCode = new Map(targetInfos.map((info) => [info.code, info]));
|
|
24502
|
-
const additionalCapability = classifyWhereCapability(additionalCandidate, (field) => {
|
|
24503
|
-
const info = infoByCode.get(field.field);
|
|
24504
|
-
return info?.semantics ?? (info ? resolveFieldSemantics(info) : systemColumnMeta(field.field)?.semantics);
|
|
24505
|
-
});
|
|
24506
|
-
if (additionalCapability.capability === "EXACT_PUSHDOWN" || additionalCapability.capability === "SUPERSET_PREFILTER") {
|
|
24507
|
-
additionalQuery = whereToKintone(additionalCandidate);
|
|
24508
|
-
additionalRelation = additionalCapability.capability === "EXACT_PUSHDOWN" ? "exact" : "superset";
|
|
24509
|
-
}
|
|
24510
|
-
}
|
|
24525
|
+
const additionalCondition = resolvedPushdownPlan.joinConditions.get(joinAlias);
|
|
24526
|
+
const additionalQuery = additionalCondition ? whereToKintone(additionalCondition) : void 0;
|
|
24527
|
+
const additionalRelation = resolvedPushdownPlan.joinRelations.get(joinAlias);
|
|
24511
24528
|
joinKeyPlans.set(joinAlias, { plan, queries, additionalQuery, additionalRelation });
|
|
24512
24529
|
}
|
|
24513
24530
|
if (joinKeyPlans.size > 0) explainJoinKeyPrefilters.set(select, joinKeyPlans);
|
|
@@ -24940,7 +24957,7 @@ var EXPLAIN_FETCH_PLAN = /* @__PURE__ */ Symbol("ksql.explainFetchPlan");
|
|
|
24940
24957
|
function setExplainFetchPlan(result, plan) {
|
|
24941
24958
|
result[EXPLAIN_FETCH_PLAN] = plan;
|
|
24942
24959
|
}
|
|
24943
|
-
async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords = 1e4, cursorMaxActive = 2, enableImport = false, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS) {
|
|
24960
|
+
async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords = 1e4, cursorMaxActive = 2, enableImport = false, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, resolveMetadata = true) {
|
|
24944
24961
|
const invocationCacheContext = createInvocationCacheContext(cacheContext);
|
|
24945
24962
|
try {
|
|
24946
24963
|
const statements = parseSqlBatch(sql, enableImport);
|
|
@@ -24955,13 +24972,21 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
|
|
|
24955
24972
|
const planStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveBatchVariableReferences(stmt.expr, variables) } : stmt : resolveBatchVariableReferences(stmt, variables);
|
|
24956
24973
|
validateStatementStatic(planStmt);
|
|
24957
24974
|
const relativeDatePlan = await resolveRelativeDateExecutionPlan(planStmt, client, invocationCacheContext);
|
|
24958
|
-
const whereAnalysis = await buildExplainWhereAnalysis(
|
|
24975
|
+
const whereAnalysis = resolveMetadata ? await buildExplainWhereAnalysis(
|
|
24959
24976
|
planStmt,
|
|
24960
24977
|
client,
|
|
24961
24978
|
invocationCacheContext,
|
|
24962
24979
|
maxRecords,
|
|
24963
24980
|
relativeDatePlan
|
|
24964
|
-
)
|
|
24981
|
+
) : {
|
|
24982
|
+
capabilities: /* @__PURE__ */ new Map(),
|
|
24983
|
+
orderPlans: /* @__PURE__ */ new Map(),
|
|
24984
|
+
plainGroupByPlans: /* @__PURE__ */ new Map(),
|
|
24985
|
+
fieldApps: /* @__PURE__ */ new Set(),
|
|
24986
|
+
processStatusApps: /* @__PURE__ */ new Set(),
|
|
24987
|
+
numberPrecisionApps: /* @__PURE__ */ new Set(),
|
|
24988
|
+
relativeDatePlan
|
|
24989
|
+
};
|
|
24965
24990
|
const fetchCollector = { sources: [] };
|
|
24966
24991
|
const statementPlan = relativeDatePlan.hasServerOnlyWhereFunction && !relativeDatePlan.allowed ? relativeDateExplainLines(relativeDatePlan) : [
|
|
24967
24992
|
...relativeDateExplainLines(relativeDatePlan),
|
|
@@ -25554,7 +25579,8 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
25554
25579
|
lines.push(` fields: ${params.fields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : params.fields.join(", ")}`);
|
|
25555
25580
|
} else {
|
|
25556
25581
|
const runtimeJoinPlan = explainJoinPushdownPlans.get(stmt);
|
|
25557
|
-
const
|
|
25582
|
+
const metadataAwarePushdownPlan = explainPushdownPlans.get(stmt);
|
|
25583
|
+
const pushdownPlan = metadataAwarePushdownPlan ?? buildKlikePushdownPlan(stmt);
|
|
25558
25584
|
if (stmt.joins.length > 0) {
|
|
25559
25585
|
if (runtimeJoinPlan) {
|
|
25560
25586
|
lines.push(" join pushdown plan: applied (runtime metadata resolved)");
|
|
@@ -25581,7 +25607,7 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
25581
25607
|
const reason = stmt.joins.some((join2) => join2.type !== "INNER") ? "OUTER_JOIN" : [stmt.from, ...stmt.joins.map((join2) => join2.table)].some(
|
|
25582
25608
|
(table) => table.cteName !== null || Boolean(table.subtableCode)
|
|
25583
25609
|
) ? "SOURCE_KIND" : "PLAN_NOT_APPLICABLE";
|
|
25584
|
-
lines.push(" join pushdown plan: not applied");
|
|
25610
|
+
lines.push(" join pushdown plan: not applied (join key/WHERE prefilters are reported per source below)");
|
|
25585
25611
|
lines.push(` join pushdown not applied: ${reason}`);
|
|
25586
25612
|
}
|
|
25587
25613
|
}
|
|
@@ -25601,7 +25627,7 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
25601
25627
|
(consumption) => consumption.targetAlias === stmt.from.alias
|
|
25602
25628
|
);
|
|
25603
25629
|
if (emitFetch && stmt.from.cteName === null) {
|
|
25604
|
-
const mainPending = !
|
|
25630
|
+
const mainPending = !metadataAwarePushdownPlan && mainCandidate !== null;
|
|
25605
25631
|
const mainFetchScope = mainQ === "(\u5168\u4EF6\u53D6\u5F97)" ? "ALL" : mainPending ? "PREFILTERED" : mainJoinItem?.relation === "exact" || mainFunctionConsumption || exactOriginalWhere !== "" ? "EXACT" : "PREFILTERED";
|
|
25606
25632
|
lines.push(renderFetchScope(createExplainFetchSource(
|
|
25607
25633
|
collector,
|
|
@@ -25616,7 +25642,10 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
25616
25642
|
if (mainJoinItem || mainFunctionConsumption) {
|
|
25617
25643
|
lines.push(` pushdown applied: ${mainBoundQuery}`);
|
|
25618
25644
|
lines.push(` relation: ${mainJoinItem?.relation ?? "exact"}`);
|
|
25619
|
-
} else if (
|
|
25645
|
+
} else if (metadataAwarePushdownPlan && mainPushDown !== null) {
|
|
25646
|
+
lines.push(` pushdown applied: ${mainQ}`);
|
|
25647
|
+
lines.push(` relation: ${pushdownPlan.mainRelation ?? "exact"}`);
|
|
25648
|
+
} else if (mainCandidate !== null && (!metadataAwarePushdownPlan || hasPushdownPlaceholder(mainCandidate))) {
|
|
25620
25649
|
lines.push(` pushdown candidate: ${whereToKintone(mainCandidate)}\uFF08\u5B9F\u884C\u6642\u306E\u578B\u30FB\u5B9F\u5728\u78BA\u8A8D\u5F85\u3061\uFF09`);
|
|
25621
25650
|
}
|
|
25622
25651
|
lines.push(` fields: ${mainFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : mainFields.join(", ")}`);
|
|
@@ -25628,8 +25657,9 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
25628
25657
|
const joinCandidate = join2.table.alias && !join2.table.subtableCode && join2.table.cteName === null && stmt.where ? extractTypedPushdownCandidates(stmt.where, { tableAlias: join2.table.alias }) : null;
|
|
25629
25658
|
const joinBoundQuery = join2.table.alias ? runtimeJoinPlan?.queriesByAlias.get(join2.table.alias) : void 0;
|
|
25630
25659
|
const joinKey = join2.table.alias ? explainJoinKeyPrefilters.get(stmt)?.get(join2.table.alias) : void 0;
|
|
25660
|
+
const offlineJoinKeyCandidate = !metadataAwarePushdownPlan && join2.type === "INNER" && join2.table.cteName === null && !join2.table.subtableCode && [stmt.from, ...stmt.joins.map((candidate) => candidate.table)].some((table) => table.cteName !== null);
|
|
25631
25661
|
const baseJoinQ = joinBoundQuery || (joinPushDown !== null ? whereToKintone(joinPushDown) : joinKey?.additionalQuery ? joinKey.additionalQuery : "(\u5168\u4EF6\u53D6\u5F97)");
|
|
25632
|
-
const runtimeJoinKeyCandidate = joinKey?.plan.kind === "RANGE_CANDIDATE" || joinKey?.plan.kind === "FALLBACK" && joinKey.plan.reason === "JOIN_KEY_VALUES_RUNTIME";
|
|
25662
|
+
const runtimeJoinKeyCandidate = offlineJoinKeyCandidate || joinKey?.plan.kind === "RANGE_CANDIDATE" || joinKey?.plan.kind === "FALLBACK" && joinKey.plan.reason === "JOIN_KEY_VALUES_RUNTIME";
|
|
25633
25663
|
const joinQueries = joinKey && joinKey.queries.length > 0 ? joinKey.queries.map((query) => baseJoinQ !== "(\u5168\u4EF6\u53D6\u5F97)" ? `(${query}) and (${baseJoinQ})` : query) : [runtimeJoinKeyCandidate ? "(runtime source keys)" : baseJoinQ];
|
|
25634
25664
|
const joinQ = joinQueries.join(" | ");
|
|
25635
25665
|
lines.push(` ${joinType}: APP${join2.table.appId}${joinAliasStr} (${join2.table.appId})`);
|
|
@@ -25641,7 +25671,7 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
25641
25671
|
(consumption) => consumption.targetAlias === join2.table.alias
|
|
25642
25672
|
);
|
|
25643
25673
|
if (emitFetch && join2.table.cteName === null) {
|
|
25644
|
-
const joinPending = runtimeJoinKeyCandidate || !joinKey && !
|
|
25674
|
+
const joinPending = runtimeJoinKeyCandidate || !joinKey && !metadataAwarePushdownPlan && joinCandidate !== null;
|
|
25645
25675
|
const joinFetchScope = joinQ === "(\u5168\u4EF6\u53D6\u5F97)" ? "ALL" : joinPending ? "PREFILTERED" : joinKey?.plan.kind === "IN" && joinPlanItem?.relation !== "superset" && joinKey.additionalRelation !== "superset" ? "EXACT" : joinPlanItem?.relation === "exact" || joinFunctionConsumption ? "EXACT" : "PREFILTERED";
|
|
25646
25676
|
lines.push(renderFetchScope(createExplainFetchSource(
|
|
25647
25677
|
collector,
|
|
@@ -25671,10 +25701,19 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
25671
25701
|
lines.push(joinKey.plan.reason === "JOIN_KEY_VALUES_RUNTIME" ? " join key prefilter: runtime candidate" : " join key prefilter: not applied");
|
|
25672
25702
|
lines.push(` join key prefilter reason: ${joinKey.plan.reason}`);
|
|
25673
25703
|
}
|
|
25704
|
+
} else if (offlineJoinKeyCandidate) {
|
|
25705
|
+
lines.push(" join key prefilter: runtime candidate");
|
|
25706
|
+
lines.push(" join key prefilter reason: JOIN_KEY_VALUES_RUNTIME");
|
|
25707
|
+
if (joinCandidate !== null) {
|
|
25708
|
+
lines.push(` pushdown candidate: ${whereToKintone(joinCandidate)}\uFF08\u5B9F\u884C\u6642\u306E\u578B\u30FB\u5B9F\u5728\u78BA\u8A8D\u5F85\u3061\uFF09`);
|
|
25709
|
+
}
|
|
25674
25710
|
} else if (joinPlanItem || joinFunctionConsumption) {
|
|
25675
25711
|
lines.push(` pushdown applied: ${joinBoundQuery}`);
|
|
25676
25712
|
lines.push(` relation: ${joinPlanItem?.relation ?? "exact"}`);
|
|
25677
|
-
} else if (
|
|
25713
|
+
} else if (metadataAwarePushdownPlan && joinPushDown !== null) {
|
|
25714
|
+
lines.push(` pushdown applied: ${baseJoinQ}`);
|
|
25715
|
+
lines.push(` relation: ${pushdownPlan.joinRelations.get(join2.table.alias) ?? "exact"}`);
|
|
25716
|
+
} else if (joinCandidate !== null && (!metadataAwarePushdownPlan || hasPushdownPlaceholder(joinCandidate))) {
|
|
25678
25717
|
lines.push(` pushdown candidate: ${whereToKintone(joinCandidate)}\uFF08\u5B9F\u884C\u6642\u306E\u578B\u30FB\u5B9F\u5728\u78BA\u8A8D\u5F85\u3061\uFF09`);
|
|
25679
25718
|
}
|
|
25680
25719
|
lines.push(` fields: ${joinFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : joinFields.join(", ")}`);
|
|
@@ -28860,14 +28899,23 @@ function createDryRunClient() {
|
|
|
28860
28899
|
deleteRecords: notUsed,
|
|
28861
28900
|
getApps: notUsed,
|
|
28862
28901
|
getFields: notUsed,
|
|
28863
|
-
|
|
28864
|
-
|
|
28865
|
-
},
|
|
28866
|
-
async getNumberPrecision() {
|
|
28867
|
-
return { digits: 30, decimalPlaces: 10, roundingMode: "HALF_EVEN" };
|
|
28868
|
-
}
|
|
28902
|
+
getProcessStatuses: notUsed,
|
|
28903
|
+
getNumberPrecision: notUsed
|
|
28869
28904
|
};
|
|
28870
28905
|
}
|
|
28906
|
+
function hasStaticTypedPushdownCandidate(statement) {
|
|
28907
|
+
if (statement === null || typeof statement !== "object") return false;
|
|
28908
|
+
const node = statement;
|
|
28909
|
+
if (node["type"] === "WITH") return hasStaticTypedPushdownCandidate(node["query"]);
|
|
28910
|
+
if (node["type"] !== "SELECT") return false;
|
|
28911
|
+
const select = statement;
|
|
28912
|
+
if (!select.where || !Array.isArray(select.joins) || select.joins.length === 0) return false;
|
|
28913
|
+
if (![select.from, ...select.joins.map((join2) => join2.table)].some((table) => table.cteName !== null)) return false;
|
|
28914
|
+
const where = select.where;
|
|
28915
|
+
return select.joins.some(
|
|
28916
|
+
(join2) => join2.type === "INNER" && join2.table?.alias && join2.table?.cteName === null && !join2.table?.subtableCode && extractTypedPushdownCandidates(where, { tableAlias: join2.table.alias }) !== null
|
|
28917
|
+
);
|
|
28918
|
+
}
|
|
28871
28919
|
async function runDiagnosticRecordGet(params) {
|
|
28872
28920
|
const qs = `app=${encodeURIComponent(String(params.appId))}&id=${encodeURIComponent(String(params.recordId))}`;
|
|
28873
28921
|
const apiRoot = params.guestSpaceId !== null ? `/k/guest/${params.guestSpaceId}/v1` : "/k/v1";
|
|
@@ -29512,6 +29560,7 @@ async function run() {
|
|
|
29512
29560
|
let containsApplyStatement = false;
|
|
29513
29561
|
let containsApplyMutation = false;
|
|
29514
29562
|
let dryRunNeedsMetadata = false;
|
|
29563
|
+
let dryRunUsesStaticTypedPlan = false;
|
|
29515
29564
|
let parsedStatements = [];
|
|
29516
29565
|
if (args.diagRecordId === null) {
|
|
29517
29566
|
sql = args.executeSql;
|
|
@@ -29551,6 +29600,7 @@ async function run() {
|
|
|
29551
29600
|
return false;
|
|
29552
29601
|
});
|
|
29553
29602
|
dryRunNeedsMetadata = statements.some(explainNeedsAppMetadata);
|
|
29603
|
+
dryRunUsesStaticTypedPlan = statements.some(hasStaticTypedPushdownCandidate) && !statements.some(statementUsesRelativeDateResolution);
|
|
29554
29604
|
if (statements.length > 1) {
|
|
29555
29605
|
batchAnalysis = analyzeBatch(statements);
|
|
29556
29606
|
isBatchSql = true;
|
|
@@ -29677,7 +29727,7 @@ async function run() {
|
|
|
29677
29727
|
appProfileByApp.set(appId, appBindingByMappedApp.get(appId)?.profile ?? profileName.toLowerCase());
|
|
29678
29728
|
}
|
|
29679
29729
|
const cacheContext = buildCacheContext(profileName, appBindingByMappedApp);
|
|
29680
|
-
if (args.dryRun && !dryRunNeedsMetadata) {
|
|
29730
|
+
if (args.dryRun && (!dryRunNeedsMetadata || dryRunUsesStaticTypedPlan)) {
|
|
29681
29731
|
client = createDryRunClient();
|
|
29682
29732
|
} else {
|
|
29683
29733
|
for (const explicitProfile of appProfileByApp.values()) {
|
|
@@ -29925,7 +29975,7 @@ async function run() {
|
|
|
29925
29975
|
maxDelayMs: args.retryMaxDelay ?? profile.query?.retryMaxDelayMs
|
|
29926
29976
|
})));
|
|
29927
29977
|
}
|
|
29928
|
-
if (
|
|
29978
|
+
if (args.dryRun && (isBatchSql || dryRunUsesStaticTypedPlan)) {
|
|
29929
29979
|
try {
|
|
29930
29980
|
const plans = await buildBatchExplainPlans(
|
|
29931
29981
|
sql,
|
|
@@ -29936,7 +29986,8 @@ async function run() {
|
|
|
29936
29986
|
cursorMaxActive,
|
|
29937
29987
|
Object.keys(args.importCsv).length > 0 || Object.keys(args.importJson).length > 0,
|
|
29938
29988
|
dmlMaxRows,
|
|
29939
|
-
dmlMaxSubtableRows
|
|
29989
|
+
dmlMaxSubtableRows,
|
|
29990
|
+
false
|
|
29940
29991
|
);
|
|
29941
29992
|
const out = [];
|
|
29942
29993
|
const restoredStatements = sqlDiagnosticContext ? restoreSqlDiagnosticValue(plans.statements, sqlDiagnosticContext.appBindingByMappedApp) : plans.statements;
|