rawsql-ts 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -13
- package/dist/esm/index.d.ts +1 -11
- package/dist/esm/index.js +1 -11
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.min.js +5 -41
- package/dist/esm/index.min.js.map +4 -4
- package/dist/esm/transformers/DynamicQueryBuilder.d.ts +4 -27
- package/dist/esm/transformers/DynamicQueryBuilder.js +10 -27
- package/dist/esm/transformers/DynamicQueryBuilder.js.map +1 -1
- package/dist/esm/transformers/SSSQLFilterBuilder.d.ts +48 -1
- package/dist/esm/transformers/SSSQLFilterBuilder.js +578 -31
- package/dist/esm/transformers/SSSQLFilterBuilder.js.map +1 -1
- package/dist/esm/utils/SchemaManager.d.ts +3 -19
- package/dist/esm/utils/SchemaManager.js +2 -62
- package/dist/esm/utils/SchemaManager.js.map +1 -1
- package/dist/index.js +2 -18
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +5 -41
- package/dist/index.min.js.map +4 -4
- package/dist/src/index.d.ts +1 -11
- package/dist/src/transformers/DynamicQueryBuilder.d.ts +4 -27
- package/dist/src/transformers/SSSQLFilterBuilder.d.ts +48 -1
- package/dist/src/utils/SchemaManager.d.ts +3 -19
- package/dist/transformers/DynamicQueryBuilder.js +10 -27
- package/dist/transformers/DynamicQueryBuilder.js.map +1 -1
- package/dist/transformers/SSSQLFilterBuilder.js +576 -29
- package/dist/transformers/SSSQLFilterBuilder.js.map +1 -1
- package/dist/tsconfig.browser.tsbuildinfo +1 -1
- package/dist/utils/SchemaManager.js +2 -63
- package/dist/utils/SchemaManager.js.map +1 -1
- package/package.json +10 -2
- package/dist/esm/transformers/EnhancedJsonMapping.d.ts +0 -194
- package/dist/esm/transformers/EnhancedJsonMapping.js +0 -217
- package/dist/esm/transformers/EnhancedJsonMapping.js.map +0 -1
- package/dist/esm/transformers/JsonMappingConverter.d.ts +0 -200
- package/dist/esm/transformers/JsonMappingConverter.js +0 -388
- package/dist/esm/transformers/JsonMappingConverter.js.map +0 -1
- package/dist/esm/transformers/JsonMappingUnifier.d.ts +0 -100
- package/dist/esm/transformers/JsonMappingUnifier.js +0 -207
- package/dist/esm/transformers/JsonMappingUnifier.js.map +0 -1
- package/dist/esm/transformers/ModelDrivenJsonMapping.d.ts +0 -62
- package/dist/esm/transformers/ModelDrivenJsonMapping.js +0 -115
- package/dist/esm/transformers/ModelDrivenJsonMapping.js.map +0 -1
- package/dist/esm/transformers/PostgresArrayEntityCteBuilder.d.ts +0 -138
- package/dist/esm/transformers/PostgresArrayEntityCteBuilder.js +0 -454
- package/dist/esm/transformers/PostgresArrayEntityCteBuilder.js.map +0 -1
- package/dist/esm/transformers/PostgresJsonQueryBuilder.d.ts +0 -88
- package/dist/esm/transformers/PostgresJsonQueryBuilder.js +0 -241
- package/dist/esm/transformers/PostgresJsonQueryBuilder.js.map +0 -1
- package/dist/esm/transformers/PostgresObjectEntityCteBuilder.d.ts +0 -165
- package/dist/esm/transformers/PostgresObjectEntityCteBuilder.js +0 -343
- package/dist/esm/transformers/PostgresObjectEntityCteBuilder.js.map +0 -1
- package/dist/esm/transformers/TypeTransformationPostProcessor.d.ts +0 -108
- package/dist/esm/transformers/TypeTransformationPostProcessor.js +0 -354
- package/dist/esm/transformers/TypeTransformationPostProcessor.js.map +0 -1
- package/dist/esm/utils/JsonSchemaValidator.d.ts +0 -81
- package/dist/esm/utils/JsonSchemaValidator.js +0 -211
- package/dist/esm/utils/JsonSchemaValidator.js.map +0 -1
- package/dist/src/transformers/EnhancedJsonMapping.d.ts +0 -194
- package/dist/src/transformers/JsonMappingConverter.d.ts +0 -200
- package/dist/src/transformers/JsonMappingUnifier.d.ts +0 -100
- package/dist/src/transformers/ModelDrivenJsonMapping.d.ts +0 -62
- package/dist/src/transformers/PostgresArrayEntityCteBuilder.d.ts +0 -138
- package/dist/src/transformers/PostgresJsonQueryBuilder.d.ts +0 -88
- package/dist/src/transformers/PostgresObjectEntityCteBuilder.d.ts +0 -165
- package/dist/src/transformers/TypeTransformationPostProcessor.d.ts +0 -108
- package/dist/src/utils/JsonSchemaValidator.d.ts +0 -81
- package/dist/transformers/EnhancedJsonMapping.js +0 -223
- package/dist/transformers/EnhancedJsonMapping.js.map +0 -1
- package/dist/transformers/JsonMappingConverter.js +0 -392
- package/dist/transformers/JsonMappingConverter.js.map +0 -1
- package/dist/transformers/JsonMappingUnifier.js +0 -216
- package/dist/transformers/JsonMappingUnifier.js.map +0 -1
- package/dist/transformers/ModelDrivenJsonMapping.js +0 -122
- package/dist/transformers/ModelDrivenJsonMapping.js.map +0 -1
- package/dist/transformers/PostgresArrayEntityCteBuilder.js +0 -458
- package/dist/transformers/PostgresArrayEntityCteBuilder.js.map +0 -1
- package/dist/transformers/PostgresJsonQueryBuilder.js +0 -245
- package/dist/transformers/PostgresJsonQueryBuilder.js.map +0 -1
- package/dist/transformers/PostgresObjectEntityCteBuilder.js +0 -347
- package/dist/transformers/PostgresObjectEntityCteBuilder.js.map +0 -1
- package/dist/transformers/TypeTransformationPostProcessor.js +0 -363
- package/dist/transformers/TypeTransformationPostProcessor.js.map +0 -1
- package/dist/utils/JsonSchemaValidator.js +0 -215
- package/dist/utils/JsonSchemaValidator.js.map +0 -1
|
@@ -1,14 +1,38 @@
|
|
|
1
|
-
import { WhereClause, TableSource } from "../models/Clause";
|
|
2
|
-
import {
|
|
1
|
+
import { WhereClause, SubQuerySource, TableSource } from "../models/Clause";
|
|
2
|
+
import { SimpleSelectQuery } from "../models/SelectQuery";
|
|
3
|
+
import { BinaryExpression, ColumnReference, IdentifierString, InlineQuery, LiteralValue, ParameterExpression, ParenExpression, RawString, UnaryExpression } from "../models/ValueComponent";
|
|
3
4
|
import { SelectQueryParser } from "../parsers/SelectQueryParser";
|
|
4
5
|
import { UpstreamSelectQueryFinder } from "./UpstreamSelectQueryFinder";
|
|
5
|
-
import { SelectableColumnCollector, DuplicateDetectionMode } from "./SelectableColumnCollector";
|
|
6
6
|
import { ColumnReferenceCollector } from "./ColumnReferenceCollector";
|
|
7
|
+
import { SelectableColumnCollector, DuplicateDetectionMode } from "./SelectableColumnCollector";
|
|
8
|
+
import { ParameterCollector } from "./ParameterCollector";
|
|
9
|
+
import { SqlFormatter } from "./SqlFormatter";
|
|
10
|
+
import { CTECollector } from "./CTECollector";
|
|
7
11
|
import { collectSupportedOptionalConditionBranches } from "./PruneOptionalConditionBranches";
|
|
12
|
+
const SUPPORTED_SCALAR_OPERATORS = new Set(["=", "<>", "<", "<=", ">", ">=", "like", "ilike"]);
|
|
13
|
+
let formatter = null;
|
|
8
14
|
const normalizeIdentifier = (value) => value.trim().toLowerCase();
|
|
15
|
+
const normalizeSql = (value) => value.replace(/\s+/g, " ").trim().toLowerCase();
|
|
9
16
|
const normalizeColumnReferenceKey = (reference) => {
|
|
10
17
|
return `${normalizeIdentifier(reference.getNamespace())}.${normalizeIdentifier(reference.column.name)}`;
|
|
11
18
|
};
|
|
19
|
+
const normalizeColumnReferenceText = (reference) => {
|
|
20
|
+
const namespace = reference.getNamespace();
|
|
21
|
+
return namespace ? `${namespace}.${reference.column.name}` : reference.column.name;
|
|
22
|
+
};
|
|
23
|
+
const normalizeScalarOperator = (value) => {
|
|
24
|
+
if (!value) {
|
|
25
|
+
return "=";
|
|
26
|
+
}
|
|
27
|
+
const normalized = value.trim().toLowerCase();
|
|
28
|
+
if (normalized === "!=") {
|
|
29
|
+
return "<>";
|
|
30
|
+
}
|
|
31
|
+
if (SUPPORTED_SCALAR_OPERATORS.has(normalized)) {
|
|
32
|
+
return normalized;
|
|
33
|
+
}
|
|
34
|
+
throw new Error(`Unsupported SSSQL operator '${value}'.`);
|
|
35
|
+
};
|
|
12
36
|
const isExplicitEqualityScaffoldValue = (value) => {
|
|
13
37
|
var _a;
|
|
14
38
|
if (value === null || value === undefined) {
|
|
@@ -40,26 +64,69 @@ const makeParameterName = (filterName) => {
|
|
|
40
64
|
.replace(/\./g, "_")
|
|
41
65
|
.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
42
66
|
};
|
|
43
|
-
const
|
|
44
|
-
|
|
67
|
+
const unwrapParens = (expression) => {
|
|
68
|
+
let candidate = expression;
|
|
69
|
+
while (candidate instanceof ParenExpression) {
|
|
70
|
+
candidate = candidate.expression;
|
|
71
|
+
}
|
|
72
|
+
return candidate;
|
|
73
|
+
};
|
|
74
|
+
const isBinaryOperator = (expression, operator) => {
|
|
75
|
+
return expression instanceof BinaryExpression && expression.operator.value.trim().toLowerCase() === operator;
|
|
76
|
+
};
|
|
77
|
+
const collectTopLevelAndTerms = (expression) => {
|
|
78
|
+
const candidate = unwrapParens(expression);
|
|
79
|
+
if (!isBinaryOperator(candidate, "and")) {
|
|
80
|
+
return [expression];
|
|
81
|
+
}
|
|
82
|
+
return [
|
|
83
|
+
...collectTopLevelAndTerms(candidate.left),
|
|
84
|
+
...collectTopLevelAndTerms(candidate.right)
|
|
85
|
+
];
|
|
86
|
+
};
|
|
87
|
+
const collectTopLevelOrTerms = (expression) => {
|
|
88
|
+
const candidate = unwrapParens(expression);
|
|
89
|
+
if (!isBinaryOperator(candidate, "or")) {
|
|
90
|
+
return [expression];
|
|
91
|
+
}
|
|
92
|
+
return [
|
|
93
|
+
...collectTopLevelOrTerms(candidate.left),
|
|
94
|
+
...collectTopLevelOrTerms(candidate.right)
|
|
95
|
+
];
|
|
96
|
+
};
|
|
97
|
+
const getGuardedParameterName = (expression) => {
|
|
98
|
+
const candidate = unwrapParens(expression);
|
|
99
|
+
if (!isBinaryOperator(candidate, "is")) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
if (!(candidate.left instanceof ParameterExpression)) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const right = unwrapParens(candidate.right);
|
|
106
|
+
const isNull = (right instanceof LiteralValue && right.value === null)
|
|
107
|
+
|| (right instanceof RawString && right.value.trim().toLowerCase() === "null");
|
|
108
|
+
if (!isNull) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
return candidate.left.name.value;
|
|
112
|
+
};
|
|
113
|
+
const buildOptionalScalarBranch = (column, parameterName, operator) => {
|
|
114
|
+
const guard = new BinaryExpression(new ParameterExpression(parameterName), "is", new LiteralValue(null));
|
|
115
|
+
const predicate = new BinaryExpression(new ColumnReference(column.getNamespace() || null, column.column.name), operator, new ParameterExpression(parameterName));
|
|
116
|
+
return new ParenExpression(new BinaryExpression(guard, "or", predicate));
|
|
117
|
+
};
|
|
118
|
+
const buildOptionalExistsBranch = (parameterName, subquery, kind) => {
|
|
45
119
|
const guard = new BinaryExpression(new ParameterExpression(parameterName), "is", new LiteralValue(null));
|
|
46
|
-
const
|
|
47
|
-
|
|
120
|
+
const existsExpression = new UnaryExpression("exists", new InlineQuery(subquery));
|
|
121
|
+
const predicate = kind === "exists"
|
|
122
|
+
? existsExpression
|
|
123
|
+
: new UnaryExpression("not", existsExpression);
|
|
124
|
+
return new ParenExpression(new BinaryExpression(guard, "or", predicate));
|
|
48
125
|
};
|
|
49
126
|
const rebuildWhereWithoutTerm = (query, termToRemove) => {
|
|
50
127
|
if (!query.whereClause) {
|
|
51
128
|
return;
|
|
52
129
|
}
|
|
53
|
-
const collectTopLevelAndTerms = (expression) => {
|
|
54
|
-
if (expression instanceof BinaryExpression &&
|
|
55
|
-
expression.operator.value.trim().toLowerCase() === "and") {
|
|
56
|
-
return [
|
|
57
|
-
...collectTopLevelAndTerms(expression.left),
|
|
58
|
-
...collectTopLevelAndTerms(expression.right)
|
|
59
|
-
];
|
|
60
|
-
}
|
|
61
|
-
return [expression];
|
|
62
|
-
};
|
|
63
130
|
const terms = collectTopLevelAndTerms(query.whereClause.condition).filter(term => term !== termToRemove);
|
|
64
131
|
if (terms.length === 0) {
|
|
65
132
|
query.whereClause = null;
|
|
@@ -71,6 +138,213 @@ const rebuildWhereWithoutTerm = (query, termToRemove) => {
|
|
|
71
138
|
}
|
|
72
139
|
query.whereClause = new WhereClause(rebuilt);
|
|
73
140
|
};
|
|
141
|
+
const formatSqlComponent = (component) => {
|
|
142
|
+
formatter !== null && formatter !== void 0 ? formatter : (formatter = new SqlFormatter());
|
|
143
|
+
return formatter.format(component).formattedSql;
|
|
144
|
+
};
|
|
145
|
+
const enforceSubqueryConstraints = (sql) => {
|
|
146
|
+
if (!sql.trim()) {
|
|
147
|
+
throw new Error("SSSQL EXISTS/NOT EXISTS scaffold query must not be empty.");
|
|
148
|
+
}
|
|
149
|
+
if (sql.includes(";")) {
|
|
150
|
+
throw new Error("SSSQL EXISTS/NOT EXISTS scaffold query must not contain semicolons or multiple statements.");
|
|
151
|
+
}
|
|
152
|
+
if (/\blateral\b/i.test(sql)) {
|
|
153
|
+
throw new Error("LATERAL is not supported in SSSQL EXISTS/NOT EXISTS scaffold.");
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
const substituteAnchorPlaceholders = (sql, formattedColumns) => {
|
|
157
|
+
const usedIndexes = new Set();
|
|
158
|
+
const replaced = sql.replace(/\$c(\d+)/g, (_, indexDigits) => {
|
|
159
|
+
const index = Number(indexDigits);
|
|
160
|
+
if (!Number.isInteger(index)) {
|
|
161
|
+
throw new Error(`Invalid placeholder '$c${indexDigits}' in SSSQL scaffold query.`);
|
|
162
|
+
}
|
|
163
|
+
if (index < 0 || index >= formattedColumns.length) {
|
|
164
|
+
throw new Error(`Placeholder '$c${index}' references a missing SSSQL scaffold anchor column.`);
|
|
165
|
+
}
|
|
166
|
+
usedIndexes.add(index);
|
|
167
|
+
return formattedColumns[index];
|
|
168
|
+
});
|
|
169
|
+
if (formattedColumns.length === 0) {
|
|
170
|
+
return replaced;
|
|
171
|
+
}
|
|
172
|
+
for (let index = 0; index < formattedColumns.length; index += 1) {
|
|
173
|
+
if (!usedIndexes.has(index)) {
|
|
174
|
+
throw new Error(`Missing placeholder '$c${index}' for SSSQL scaffold anchor column.`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return replaced;
|
|
178
|
+
};
|
|
179
|
+
const getScalarBranchDetails = (expression, parameterName) => {
|
|
180
|
+
const meaningfulTerms = collectTopLevelOrTerms(expression)
|
|
181
|
+
.filter(term => getGuardedParameterName(term) !== parameterName);
|
|
182
|
+
if (meaningfulTerms.length !== 1) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
const predicate = unwrapParens(meaningfulTerms[0]);
|
|
186
|
+
if (!(predicate instanceof BinaryExpression)) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
const left = unwrapParens(predicate.left);
|
|
190
|
+
const right = unwrapParens(predicate.right);
|
|
191
|
+
if (left instanceof ColumnReference && right instanceof ParameterExpression && right.name.value === parameterName) {
|
|
192
|
+
try {
|
|
193
|
+
return {
|
|
194
|
+
operator: normalizeScalarOperator(predicate.operator.value),
|
|
195
|
+
target: normalizeColumnReferenceText(left)
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
catch (_a) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (right instanceof ColumnReference && left instanceof ParameterExpression && left.name.value === parameterName) {
|
|
203
|
+
try {
|
|
204
|
+
return {
|
|
205
|
+
operator: normalizeScalarOperator(predicate.operator.value),
|
|
206
|
+
target: normalizeColumnReferenceText(right)
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
catch (_b) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return null;
|
|
214
|
+
};
|
|
215
|
+
const hasSelectQuery = (value) => {
|
|
216
|
+
return typeof value === "object" && value !== null && "selectQuery" in value;
|
|
217
|
+
};
|
|
218
|
+
const collectColumnReferencesDeep = (value) => {
|
|
219
|
+
const references = [];
|
|
220
|
+
const visited = new WeakSet();
|
|
221
|
+
const walk = (candidate) => {
|
|
222
|
+
if (!candidate || typeof candidate !== "object") {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (candidate instanceof ColumnReference) {
|
|
226
|
+
references.push(candidate);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (visited.has(candidate)) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
visited.add(candidate);
|
|
233
|
+
if (Array.isArray(candidate)) {
|
|
234
|
+
for (const item of candidate) {
|
|
235
|
+
walk(item);
|
|
236
|
+
}
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
for (const child of Object.values(candidate)) {
|
|
240
|
+
walk(child);
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
walk(value);
|
|
244
|
+
return references;
|
|
245
|
+
};
|
|
246
|
+
const getExistsBranchKind = (expression, parameterName) => {
|
|
247
|
+
const meaningfulTerms = collectTopLevelOrTerms(expression)
|
|
248
|
+
.filter(term => getGuardedParameterName(term) !== parameterName);
|
|
249
|
+
if (meaningfulTerms.length !== 1) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const predicate = unwrapParens(meaningfulTerms[0]);
|
|
253
|
+
const isInlineQueryValue = (value) => {
|
|
254
|
+
return value instanceof InlineQuery || hasSelectQuery(value);
|
|
255
|
+
};
|
|
256
|
+
if (predicate instanceof UnaryExpression && predicate.operator.value.trim().toLowerCase() === "exists") {
|
|
257
|
+
return isInlineQueryValue(unwrapParens(predicate.expression)) ? "exists" : null;
|
|
258
|
+
}
|
|
259
|
+
if (predicate instanceof UnaryExpression && predicate.operator.value.trim().toLowerCase() === "not exists") {
|
|
260
|
+
return isInlineQueryValue(unwrapParens(predicate.expression)) ? "not-exists" : null;
|
|
261
|
+
}
|
|
262
|
+
if (predicate instanceof UnaryExpression &&
|
|
263
|
+
predicate.operator.value.trim().toLowerCase() === "not" &&
|
|
264
|
+
unwrapParens(predicate.expression) instanceof UnaryExpression) {
|
|
265
|
+
const nested = unwrapParens(predicate.expression);
|
|
266
|
+
if (nested.operator.value.trim().toLowerCase() === "exists"
|
|
267
|
+
&& isInlineQueryValue(unwrapParens(nested.expression))) {
|
|
268
|
+
return "not-exists";
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return null;
|
|
272
|
+
};
|
|
273
|
+
const getExistsPredicateDetails = (expression, parameterName) => {
|
|
274
|
+
const meaningfulTerms = collectTopLevelOrTerms(expression)
|
|
275
|
+
.filter(term => getGuardedParameterName(term) !== parameterName);
|
|
276
|
+
if (meaningfulTerms.length !== 1) {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
const predicate = unwrapParens(meaningfulTerms[0]);
|
|
280
|
+
const isInlineQueryValue = (value) => {
|
|
281
|
+
return value instanceof InlineQuery || hasSelectQuery(value);
|
|
282
|
+
};
|
|
283
|
+
if (predicate instanceof UnaryExpression && predicate.operator.value.trim().toLowerCase() === "exists") {
|
|
284
|
+
const candidate = unwrapParens(predicate.expression);
|
|
285
|
+
if (isInlineQueryValue(candidate)) {
|
|
286
|
+
return {
|
|
287
|
+
kind: "exists",
|
|
288
|
+
subquery: candidate.selectQuery
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
if (predicate instanceof UnaryExpression && predicate.operator.value.trim().toLowerCase() === "not exists") {
|
|
294
|
+
const candidate = unwrapParens(predicate.expression);
|
|
295
|
+
if (isInlineQueryValue(candidate)) {
|
|
296
|
+
return {
|
|
297
|
+
kind: "not-exists",
|
|
298
|
+
subquery: candidate.selectQuery
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
if (predicate instanceof UnaryExpression &&
|
|
304
|
+
predicate.operator.value.trim().toLowerCase() === "not" &&
|
|
305
|
+
unwrapParens(predicate.expression) instanceof UnaryExpression) {
|
|
306
|
+
const nested = unwrapParens(predicate.expression);
|
|
307
|
+
const candidate = unwrapParens(nested.expression);
|
|
308
|
+
if (nested.operator.value.trim().toLowerCase() === "exists" && isInlineQueryValue(candidate)) {
|
|
309
|
+
return {
|
|
310
|
+
kind: "not-exists",
|
|
311
|
+
subquery: candidate.selectQuery
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return null;
|
|
316
|
+
};
|
|
317
|
+
const getBranchInfo = (branch) => {
|
|
318
|
+
const scalar = getScalarBranchDetails(branch.expression, branch.parameterName);
|
|
319
|
+
if (scalar) {
|
|
320
|
+
return {
|
|
321
|
+
parameterName: branch.parameterName,
|
|
322
|
+
kind: "scalar",
|
|
323
|
+
operator: scalar.operator,
|
|
324
|
+
target: scalar.target,
|
|
325
|
+
query: branch.query,
|
|
326
|
+
expression: branch.expression,
|
|
327
|
+
sql: formatSqlComponent(branch.expression)
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
const existsKind = getExistsBranchKind(branch.expression, branch.parameterName);
|
|
331
|
+
if (existsKind) {
|
|
332
|
+
return {
|
|
333
|
+
parameterName: branch.parameterName,
|
|
334
|
+
kind: existsKind,
|
|
335
|
+
query: branch.query,
|
|
336
|
+
expression: branch.expression,
|
|
337
|
+
sql: formatSqlComponent(branch.expression)
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
parameterName: branch.parameterName,
|
|
342
|
+
kind: "expression",
|
|
343
|
+
query: branch.query,
|
|
344
|
+
expression: branch.expression,
|
|
345
|
+
sql: formatSqlComponent(branch.expression)
|
|
346
|
+
};
|
|
347
|
+
};
|
|
74
348
|
/**
|
|
75
349
|
* Builds and refreshes truthful SSSQL optional filter branches.
|
|
76
350
|
* Runtime callers should use pruning, not dynamic predicate injection.
|
|
@@ -80,49 +354,180 @@ export class SSSQLFilterBuilder {
|
|
|
80
354
|
this.tableColumnResolver = tableColumnResolver;
|
|
81
355
|
this.finder = new UpstreamSelectQueryFinder(this.tableColumnResolver);
|
|
82
356
|
}
|
|
357
|
+
list(query) {
|
|
358
|
+
const parsed = this.parseQuery(query);
|
|
359
|
+
return collectSupportedOptionalConditionBranches(parsed).map(getBranchInfo);
|
|
360
|
+
}
|
|
83
361
|
scaffold(query, filters) {
|
|
84
362
|
const parsed = this.parseQuery(query);
|
|
85
363
|
for (const [filterName, filterValue] of Object.entries(filters)) {
|
|
86
364
|
if (!isExplicitEqualityScaffoldValue(filterValue)) {
|
|
87
|
-
throw new Error(`SSSQL scaffold only supports equality filters in v1. Use refresh for pre-authored branches: '${filterName}'.`);
|
|
365
|
+
throw new Error(`SSSQL scaffold only supports equality filters in v1. Use structured scaffold or refresh for pre-authored branches: '${filterName}'.`);
|
|
88
366
|
}
|
|
89
|
-
|
|
90
|
-
|
|
367
|
+
this.scaffoldBranch(parsed, {
|
|
368
|
+
target: filterName,
|
|
369
|
+
parameterName: makeParameterName(filterName),
|
|
370
|
+
operator: "="
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
return parsed;
|
|
374
|
+
}
|
|
375
|
+
scaffoldBranch(query, spec) {
|
|
376
|
+
const parsed = this.parseQuery(query);
|
|
377
|
+
if (spec.kind === "exists" || spec.kind === "not-exists") {
|
|
378
|
+
this.scaffoldExistsBranch(parsed, spec);
|
|
379
|
+
return parsed;
|
|
91
380
|
}
|
|
381
|
+
this.scaffoldScalarBranch(parsed, spec);
|
|
92
382
|
return parsed;
|
|
93
383
|
}
|
|
94
384
|
refresh(query, filters) {
|
|
95
385
|
const parsed = this.parseQuery(query);
|
|
96
386
|
for (const [filterName, filterValue] of Object.entries(filters)) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
387
|
+
let parameterName = filterName;
|
|
388
|
+
let target = null;
|
|
389
|
+
let matches = collectSupportedOptionalConditionBranches(parsed)
|
|
390
|
+
.filter(branch => branch.parameterName === parameterName);
|
|
391
|
+
if (matches.length === 0) {
|
|
392
|
+
target = this.resolveTarget(parsed, filterName);
|
|
393
|
+
parameterName = target.parameterName;
|
|
394
|
+
matches = collectSupportedOptionalConditionBranches(parsed)
|
|
395
|
+
.filter(branch => branch.parameterName === parameterName);
|
|
396
|
+
}
|
|
100
397
|
if (matches.length === 0) {
|
|
398
|
+
if (!target) {
|
|
399
|
+
target = this.resolveTarget(parsed, filterName);
|
|
400
|
+
parameterName = target.parameterName;
|
|
401
|
+
}
|
|
101
402
|
if (!isExplicitEqualityScaffoldValue(filterValue)) {
|
|
102
403
|
throw new Error(`No existing SSSQL branch was found for '${filterName}', and v1 scaffold only supports equality filters.`);
|
|
103
404
|
}
|
|
104
|
-
|
|
405
|
+
this.scaffoldScalarBranch(parsed, {
|
|
406
|
+
target: filterName,
|
|
407
|
+
parameterName: target.parameterName,
|
|
408
|
+
operator: "="
|
|
409
|
+
});
|
|
105
410
|
continue;
|
|
106
411
|
}
|
|
107
412
|
if (matches.length > 1) {
|
|
108
|
-
throw new Error(`Multiple SSSQL branches matched parameter ':${
|
|
413
|
+
throw new Error(`Multiple SSSQL branches matched parameter ':${parameterName}'. Refresh is ambiguous.`);
|
|
109
414
|
}
|
|
110
415
|
const [match] = matches;
|
|
111
416
|
if (!match) {
|
|
112
417
|
continue;
|
|
113
418
|
}
|
|
114
|
-
|
|
419
|
+
const correlatedPlan = this.buildCorrelatedRefreshPlan(parsed, match);
|
|
420
|
+
if (correlatedPlan) {
|
|
421
|
+
if (correlatedPlan.target.query === match.query) {
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
this.rebaseMovedBranchByAlias(match.expression, correlatedPlan.sourceAlias, correlatedPlan.target.column);
|
|
425
|
+
rebuildWhereWithoutTerm(match.query, match.expression);
|
|
426
|
+
correlatedPlan.target.query.appendWhere(match.expression);
|
|
115
427
|
continue;
|
|
116
428
|
}
|
|
117
|
-
|
|
429
|
+
if (!target) {
|
|
430
|
+
target = this.resolveTarget(parsed, filterName);
|
|
431
|
+
}
|
|
432
|
+
if (match.query !== target.query) {
|
|
433
|
+
this.rebaseMovedBranch(match.expression, match.query, target.column);
|
|
434
|
+
rebuildWhereWithoutTerm(match.query, match.expression);
|
|
435
|
+
target.query.appendWhere(match.expression);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return parsed;
|
|
439
|
+
}
|
|
440
|
+
remove(query, spec) {
|
|
441
|
+
const parsed = this.parseQuery(query);
|
|
442
|
+
const matches = this.findMatchingBranchInfos(parsed, spec);
|
|
443
|
+
if (matches.length === 0) {
|
|
444
|
+
return parsed;
|
|
445
|
+
}
|
|
446
|
+
if (matches.length > 1) {
|
|
447
|
+
throw new Error(`Multiple SSSQL branches matched parameter ':${spec.parameterName}'. Remove is ambiguous.`);
|
|
448
|
+
}
|
|
449
|
+
const [match] = matches;
|
|
450
|
+
if (!match) {
|
|
451
|
+
return parsed;
|
|
452
|
+
}
|
|
453
|
+
rebuildWhereWithoutTerm(match.query, match.expression);
|
|
454
|
+
return parsed;
|
|
455
|
+
}
|
|
456
|
+
removeAll(query) {
|
|
457
|
+
const parsed = this.parseQuery(query);
|
|
458
|
+
const matches = this.list(parsed);
|
|
459
|
+
for (const match of matches) {
|
|
118
460
|
rebuildWhereWithoutTerm(match.query, match.expression);
|
|
119
|
-
target.query.appendWhere(match.expression);
|
|
120
461
|
}
|
|
121
462
|
return parsed;
|
|
122
463
|
}
|
|
123
464
|
parseQuery(query) {
|
|
124
465
|
return typeof query === "string" ? SelectQueryParser.parse(query) : query;
|
|
125
466
|
}
|
|
467
|
+
findMatchingBranchInfos(root, spec) {
|
|
468
|
+
const normalizedOperator = spec.operator ? normalizeScalarOperator(spec.operator) : undefined;
|
|
469
|
+
const normalizedTarget = spec.target ? normalizeIdentifier(spec.target) : undefined;
|
|
470
|
+
return this.list(root).filter(branch => {
|
|
471
|
+
if (branch.parameterName !== spec.parameterName) {
|
|
472
|
+
return false;
|
|
473
|
+
}
|
|
474
|
+
if (spec.kind && branch.kind !== spec.kind) {
|
|
475
|
+
return false;
|
|
476
|
+
}
|
|
477
|
+
if (normalizedOperator && branch.operator !== normalizedOperator) {
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
if (normalizedTarget && (!branch.target || normalizeIdentifier(branch.target) !== normalizedTarget)) {
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
return true;
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
scaffoldScalarBranch(root, spec) {
|
|
487
|
+
var _a;
|
|
488
|
+
const target = this.resolveTarget(root, spec.target);
|
|
489
|
+
const parameterName = ((_a = spec.parameterName) === null || _a === void 0 ? void 0 : _a.trim()) || target.parameterName;
|
|
490
|
+
const operator = normalizeScalarOperator(spec.operator);
|
|
491
|
+
const branch = buildOptionalScalarBranch(target.column, parameterName, operator);
|
|
492
|
+
const branchSql = normalizeSql(formatSqlComponent(branch));
|
|
493
|
+
const duplicate = this.list(root).find(existing => existing.query === target.query &&
|
|
494
|
+
normalizeSql(existing.sql) === branchSql);
|
|
495
|
+
if (duplicate) {
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
target.query.appendWhere(branch);
|
|
499
|
+
}
|
|
500
|
+
scaffoldExistsBranch(root, spec) {
|
|
501
|
+
const parameterName = spec.parameterName.trim();
|
|
502
|
+
if (!parameterName) {
|
|
503
|
+
throw new Error("SSSQL EXISTS/NOT EXISTS scaffold requires parameterName.");
|
|
504
|
+
}
|
|
505
|
+
if (spec.anchorColumns.length === 0) {
|
|
506
|
+
throw new Error("SSSQL EXISTS/NOT EXISTS scaffold requires at least one anchorColumn.");
|
|
507
|
+
}
|
|
508
|
+
const anchorTargets = spec.anchorColumns.map(anchorColumn => this.resolveTarget(root, anchorColumn));
|
|
509
|
+
const targetQueries = [...new Set(anchorTargets.map(target => target.query))];
|
|
510
|
+
if (targetQueries.length !== 1) {
|
|
511
|
+
throw new Error("SSSQL EXISTS/NOT EXISTS scaffold anchor columns must resolve within one query scope.");
|
|
512
|
+
}
|
|
513
|
+
const targetQuery = targetQueries[0];
|
|
514
|
+
const formattedColumns = anchorTargets.map(target => formatSqlComponent(target.column));
|
|
515
|
+
const substitutedSql = substituteAnchorPlaceholders(spec.query, formattedColumns).trim();
|
|
516
|
+
enforceSubqueryConstraints(substitutedSql);
|
|
517
|
+
const subquery = SelectQueryParser.parse(substitutedSql);
|
|
518
|
+
const parameterNames = new Set(ParameterCollector.collect(subquery).map(parameter => parameter.name.value));
|
|
519
|
+
if (parameterNames.size !== 1 || !parameterNames.has(parameterName)) {
|
|
520
|
+
throw new Error(`SSSQL ${spec.kind.toUpperCase()} scaffold query must reference only parameter ':${parameterName}'.`);
|
|
521
|
+
}
|
|
522
|
+
const branch = buildOptionalExistsBranch(parameterName, subquery, spec.kind);
|
|
523
|
+
const branchSql = normalizeSql(formatSqlComponent(branch));
|
|
524
|
+
const duplicate = this.list(root).find(existing => existing.query === targetQuery &&
|
|
525
|
+
normalizeSql(existing.sql) === branchSql);
|
|
526
|
+
if (duplicate) {
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
targetQuery.appendWhere(branch);
|
|
530
|
+
}
|
|
126
531
|
resolveTarget(root, filterName) {
|
|
127
532
|
var _a;
|
|
128
533
|
const qualified = parseQualifiedFilterName(filterName);
|
|
@@ -210,14 +615,140 @@ export class SSSQLFilterBuilder {
|
|
|
210
615
|
}
|
|
211
616
|
return (_a = source.getAliasName()) !== null && _a !== void 0 ? _a : source.datasource.table.name;
|
|
212
617
|
}
|
|
618
|
+
buildCorrelatedRefreshPlan(root, branch) {
|
|
619
|
+
const details = getExistsPredicateDetails(branch.expression, branch.parameterName);
|
|
620
|
+
if (!details) {
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
const sourceAliases = this.collectSourceAliases(branch.query);
|
|
624
|
+
const candidatesByKey = new Map();
|
|
625
|
+
for (const reference of new ColumnReferenceCollector().collect(details.subquery)) {
|
|
626
|
+
const namespace = normalizeIdentifier(reference.getNamespace());
|
|
627
|
+
if (!namespace || !sourceAliases.has(namespace)) {
|
|
628
|
+
continue;
|
|
629
|
+
}
|
|
630
|
+
const column = normalizeIdentifier(reference.column.name);
|
|
631
|
+
const key = `${namespace}.${column}`;
|
|
632
|
+
if (!candidatesByKey.has(key)) {
|
|
633
|
+
candidatesByKey.set(key, { namespace, column });
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
const candidates = [...candidatesByKey.values()];
|
|
637
|
+
if (candidates.length === 0) {
|
|
638
|
+
throw new Error(`SSSQL refresh could not infer a correlated anchor for ':${branch.parameterName}'.`);
|
|
639
|
+
}
|
|
640
|
+
if (candidates.length > 1) {
|
|
641
|
+
const listed = candidates.map(candidate => `${candidate.namespace}.${candidate.column}`).join(", ");
|
|
642
|
+
throw new Error(`SSSQL refresh found multiple correlated anchor candidates for ':${branch.parameterName}' (${listed}).`);
|
|
643
|
+
}
|
|
644
|
+
const [anchor] = candidates;
|
|
645
|
+
if (!anchor) {
|
|
646
|
+
throw new Error(`SSSQL refresh could not infer a correlated anchor for ':${branch.parameterName}'.`);
|
|
647
|
+
}
|
|
648
|
+
return {
|
|
649
|
+
target: this.resolveCorrelatedAnchorTarget(root, branch.query, anchor, branch.parameterName),
|
|
650
|
+
sourceAlias: anchor.namespace
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
collectSourceAliases(query) {
|
|
654
|
+
var _a, _b;
|
|
655
|
+
const aliases = new Set();
|
|
656
|
+
for (const source of (_b = (_a = query.fromClause) === null || _a === void 0 ? void 0 : _a.getSources()) !== null && _b !== void 0 ? _b : []) {
|
|
657
|
+
const sourceAlias = this.getSourceAlias(source);
|
|
658
|
+
if (sourceAlias) {
|
|
659
|
+
aliases.add(sourceAlias);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
return aliases;
|
|
663
|
+
}
|
|
664
|
+
resolveCorrelatedAnchorTarget(root, sourceQuery, anchor, parameterName) {
|
|
665
|
+
const sourceExpression = this.findSourceExpressionByAlias(sourceQuery, anchor.namespace, parameterName);
|
|
666
|
+
const upstreamQuery = this.resolveSourceExpressionToUpstreamQuery(root, sourceExpression, parameterName);
|
|
667
|
+
if (!upstreamQuery) {
|
|
668
|
+
return {
|
|
669
|
+
query: sourceQuery,
|
|
670
|
+
column: new ColumnReference(anchor.namespace, anchor.column),
|
|
671
|
+
parameterName
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
return this.resolveAnchorTargetInQuery(upstreamQuery, anchor, parameterName);
|
|
675
|
+
}
|
|
676
|
+
findSourceExpressionByAlias(query, alias, parameterName) {
|
|
677
|
+
var _a, _b;
|
|
678
|
+
const matches = ((_b = (_a = query.fromClause) === null || _a === void 0 ? void 0 : _a.getSources()) !== null && _b !== void 0 ? _b : [])
|
|
679
|
+
.filter(source => this.getSourceAlias(source) === alias);
|
|
680
|
+
if (matches.length === 0) {
|
|
681
|
+
throw new Error(`SSSQL refresh could not resolve correlated alias '${alias}' for ':${parameterName}'.`);
|
|
682
|
+
}
|
|
683
|
+
if (matches.length > 1) {
|
|
684
|
+
throw new Error(`SSSQL refresh found multiple correlated sources for alias '${alias}' and ':${parameterName}'.`);
|
|
685
|
+
}
|
|
686
|
+
return matches[0];
|
|
687
|
+
}
|
|
688
|
+
resolveSourceExpressionToUpstreamQuery(root, source, parameterName) {
|
|
689
|
+
if (source.datasource instanceof SubQuerySource) {
|
|
690
|
+
if (source.datasource.query instanceof SimpleSelectQuery) {
|
|
691
|
+
return source.datasource.query;
|
|
692
|
+
}
|
|
693
|
+
throw new Error(`SSSQL refresh requires a simple query anchor for ':${parameterName}'.`);
|
|
694
|
+
}
|
|
695
|
+
if (!(source.datasource instanceof TableSource)) {
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
const cteName = normalizeIdentifier(source.datasource.table.name);
|
|
699
|
+
const cteMatches = new CTECollector()
|
|
700
|
+
.collect(root)
|
|
701
|
+
.filter(cte => normalizeIdentifier(cte.getSourceAliasName()) === cteName);
|
|
702
|
+
if (cteMatches.length === 0) {
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
if (cteMatches.length > 1) {
|
|
706
|
+
throw new Error(`SSSQL refresh found multiple CTE anchors for ':${parameterName}' (${source.datasource.table.name}).`);
|
|
707
|
+
}
|
|
708
|
+
const [cte] = cteMatches;
|
|
709
|
+
if (!cte) {
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
const cteQuery = cte.query;
|
|
713
|
+
if (!(cteQuery instanceof SimpleSelectQuery)) {
|
|
714
|
+
throw new Error(`SSSQL refresh requires a simple CTE anchor for ':${parameterName}'.`);
|
|
715
|
+
}
|
|
716
|
+
return cteQuery;
|
|
717
|
+
}
|
|
718
|
+
resolveAnchorTargetInQuery(query, anchor, parameterName) {
|
|
719
|
+
const collector = new SelectableColumnCollector(this.tableColumnResolver, false, DuplicateDetectionMode.FullName, { upstream: true });
|
|
720
|
+
const matches = collector.collect(query)
|
|
721
|
+
.filter((entry) => entry.value instanceof ColumnReference)
|
|
722
|
+
.filter(entry => normalizeIdentifier(entry.name) === anchor.column);
|
|
723
|
+
if (matches.length === 0) {
|
|
724
|
+
throw new Error(`SSSQL refresh could not resolve correlated anchor column '${anchor.column}' for ':${parameterName}'.`);
|
|
725
|
+
}
|
|
726
|
+
if (matches.length > 1) {
|
|
727
|
+
throw new Error(`SSSQL refresh found multiple correlated anchor columns '${anchor.column}' for ':${parameterName}'.`);
|
|
728
|
+
}
|
|
729
|
+
return {
|
|
730
|
+
query,
|
|
731
|
+
column: matches[0].value,
|
|
732
|
+
parameterName
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
getSourceAlias(source) {
|
|
736
|
+
const explicitAlias = source.getAliasName();
|
|
737
|
+
if (explicitAlias) {
|
|
738
|
+
return normalizeIdentifier(explicitAlias);
|
|
739
|
+
}
|
|
740
|
+
if (source.datasource instanceof TableSource) {
|
|
741
|
+
return normalizeIdentifier(source.datasource.table.name);
|
|
742
|
+
}
|
|
743
|
+
return null;
|
|
744
|
+
}
|
|
213
745
|
rebaseMovedBranch(expression, sourceQuery, targetColumn) {
|
|
214
746
|
var _a, _b, _c;
|
|
215
747
|
const targetNamespace = targetColumn.qualifiedName.namespaces
|
|
216
748
|
? targetColumn.qualifiedName.namespaces.map(namespace => namespace.name)
|
|
217
749
|
: null;
|
|
218
750
|
const targetColumnName = normalizeIdentifier(targetColumn.column.name);
|
|
219
|
-
const sourceAliases = new Set(
|
|
220
|
-
.collect(expression)
|
|
751
|
+
const sourceAliases = new Set(collectColumnReferencesDeep(expression)
|
|
221
752
|
.filter(reference => normalizeIdentifier(reference.column.name) === targetColumnName)
|
|
222
753
|
.map(reference => normalizeIdentifier(reference.getNamespace()))
|
|
223
754
|
.filter(namespace => namespace.length > 0));
|
|
@@ -236,13 +767,29 @@ export class SSSQLFilterBuilder {
|
|
|
236
767
|
if (!availableAliases.has(sourceAlias)) {
|
|
237
768
|
return;
|
|
238
769
|
}
|
|
239
|
-
for (const reference of
|
|
770
|
+
for (const reference of collectColumnReferencesDeep(expression)) {
|
|
240
771
|
if (normalizeIdentifier(reference.getNamespace()) !== sourceAlias) {
|
|
241
772
|
continue;
|
|
242
773
|
}
|
|
243
774
|
reference.qualifiedName.namespaces = (_c = targetNamespace === null || targetNamespace === void 0 ? void 0 : targetNamespace.map(namespace => new IdentifierString(namespace))) !== null && _c !== void 0 ? _c : null;
|
|
244
775
|
}
|
|
245
776
|
}
|
|
777
|
+
rebaseMovedBranchByAlias(expression, sourceAlias, targetColumn) {
|
|
778
|
+
var _a;
|
|
779
|
+
const normalizedSourceAlias = normalizeIdentifier(sourceAlias);
|
|
780
|
+
if (!normalizedSourceAlias) {
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
const targetNamespace = targetColumn.qualifiedName.namespaces
|
|
784
|
+
? targetColumn.qualifiedName.namespaces.map(namespace => namespace.name)
|
|
785
|
+
: null;
|
|
786
|
+
for (const reference of collectColumnReferencesDeep(expression)) {
|
|
787
|
+
if (normalizeIdentifier(reference.getNamespace()) !== normalizedSourceAlias) {
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
790
|
+
reference.qualifiedName.namespaces = (_a = targetNamespace === null || targetNamespace === void 0 ? void 0 : targetNamespace.map(namespace => new IdentifierString(namespace))) !== null && _a !== void 0 ? _a : null;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
246
793
|
}
|
|
247
794
|
export const scaffoldSssqlQuery = (sqlContent, filters) => ({
|
|
248
795
|
query: new SSSQLFilterBuilder().scaffold(sqlContent, filters)
|