leangraph 1.1.7 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +3 -6
- package/dist/auth.js.map +1 -1
- package/dist/db.d.ts +3 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +38 -7
- package/dist/db.js.map +1 -1
- package/dist/executor.d.ts +1 -0
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +60 -48
- package/dist/executor.js.map +1 -1
- package/dist/parser.d.ts +31 -3
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +140 -17
- package/dist/parser.js.map +1 -1
- package/dist/routes.d.ts +1 -3
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +184 -102
- package/dist/routes.js.map +1 -1
- package/dist/translator.d.ts +11 -0
- package/dist/translator.d.ts.map +1 -1
- package/dist/translator.js +137 -92
- package/dist/translator.js.map +1 -1
- package/package.json +1 -1
package/dist/translator.js
CHANGED
|
@@ -20,6 +20,15 @@ function toSqliteParam(value) {
|
|
|
20
20
|
function toSqliteParams(values) {
|
|
21
21
|
return values.map(toSqliteParam);
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Escape a string for safe interpolation inside a SQL string literal.
|
|
25
|
+
* Used for JSON path keys, label names, and other identifiers that are
|
|
26
|
+
* embedded directly in SQL rather than passed as bind parameters.
|
|
27
|
+
* Prevents SQL injection via backtick-quoted Cypher identifiers.
|
|
28
|
+
*/
|
|
29
|
+
function escSqlStr(s) {
|
|
30
|
+
return (s || "").replace(/'/g, "''");
|
|
31
|
+
}
|
|
23
32
|
/**
|
|
24
33
|
* Check if a string is an IANA timezone name (contains '/')
|
|
25
34
|
*/
|
|
@@ -165,6 +174,10 @@ export class Translator {
|
|
|
165
174
|
return { statements: this.translateCreateIndex(clause) };
|
|
166
175
|
case "DROP_INDEX":
|
|
167
176
|
return { statements: this.translateDropIndex(clause) };
|
|
177
|
+
case "CREATE_CONSTRAINT":
|
|
178
|
+
return { statements: this.translateCreateConstraint(clause) };
|
|
179
|
+
case "DROP_CONSTRAINT":
|
|
180
|
+
return { statements: this.translateDropConstraint(clause) };
|
|
168
181
|
default:
|
|
169
182
|
throw new Error(`Unknown clause type: ${clause.type}`);
|
|
170
183
|
}
|
|
@@ -314,15 +327,47 @@ export class Translator {
|
|
|
314
327
|
* The :Label is optional and ignored (global index) - only used if no custom name provided.
|
|
315
328
|
*/
|
|
316
329
|
translateCreateIndex(clause) {
|
|
317
|
-
const
|
|
318
|
-
|
|
330
|
+
const rawName = clause.indexName || `idx_${clause.property}`;
|
|
331
|
+
// Quote index name as a SQL identifier to prevent injection
|
|
332
|
+
const indexName = `"${rawName.replace(/"/g, '""')}"`;
|
|
333
|
+
const sql = `CREATE INDEX IF NOT EXISTS ${indexName} ON nodes(json_extract(properties, '$.${escSqlStr(clause.property)}'))`;
|
|
319
334
|
return [{ sql, params: [] }];
|
|
320
335
|
}
|
|
321
336
|
/**
|
|
322
337
|
* Translate DROP INDEX to SQL.
|
|
323
338
|
*/
|
|
324
339
|
translateDropIndex(clause) {
|
|
325
|
-
|
|
340
|
+
// Quote index name as a SQL identifier to prevent injection
|
|
341
|
+
const indexName = `"${clause.indexName.replace(/"/g, '""')}"`;
|
|
342
|
+
const sql = `DROP INDEX IF EXISTS ${indexName}`;
|
|
343
|
+
return [{ sql, params: [] }];
|
|
344
|
+
}
|
|
345
|
+
// ============================================================================
|
|
346
|
+
// CREATE CONSTRAINT / DROP CONSTRAINT
|
|
347
|
+
// ============================================================================
|
|
348
|
+
/**
|
|
349
|
+
* Translate CREATE CONSTRAINT to SQL.
|
|
350
|
+
* Creates a partial unique index on a JSON property filtered by label.
|
|
351
|
+
*
|
|
352
|
+
* Syntax: CREATE CONSTRAINT [name] ON (n:Label) ASSERT n.property IS UNIQUE
|
|
353
|
+
*/
|
|
354
|
+
translateCreateConstraint(clause) {
|
|
355
|
+
const rawName = clause.constraintName || `constraint_${clause.label}_${clause.property}_unique`;
|
|
356
|
+
// Quote constraint name as a SQL identifier to prevent injection
|
|
357
|
+
const constraintName = `"${rawName.replace(/"/g, '""')}"`;
|
|
358
|
+
const escapedProperty = escSqlStr(clause.property);
|
|
359
|
+
const escapedLabel = escSqlStr(clause.label);
|
|
360
|
+
// Create a partial unique index: unique on (property) where primary label matches
|
|
361
|
+
const sql = `CREATE UNIQUE INDEX IF NOT EXISTS ${constraintName} ON nodes(json_extract(properties, '$.${escapedProperty}')) WHERE json_extract(label, '$[0]') = '${escapedLabel}'`;
|
|
362
|
+
return [{ sql, params: [] }];
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Translate DROP CONSTRAINT to SQL.
|
|
366
|
+
*/
|
|
367
|
+
translateDropConstraint(clause) {
|
|
368
|
+
// Quote constraint name as a SQL identifier to prevent injection
|
|
369
|
+
const constraintName = `"${clause.constraintName.replace(/"/g, '""')}"`;
|
|
370
|
+
const sql = `DROP INDEX IF EXISTS ${constraintName}`;
|
|
326
371
|
return [{ sql, params: [] }];
|
|
327
372
|
}
|
|
328
373
|
// ============================================================================
|
|
@@ -769,7 +814,7 @@ export class Translator {
|
|
|
769
814
|
const withAliases = this.ctx.withAliases;
|
|
770
815
|
for (const [key, value] of Object.entries(props)) {
|
|
771
816
|
if (this.isParameterRef(value)) {
|
|
772
|
-
conditions.push(`json_extract(properties, '$.${key}') = ?`);
|
|
817
|
+
conditions.push(`json_extract(properties, '$.${escSqlStr(key)}') = ?`);
|
|
773
818
|
params.push(this.ctx.paramValues[value.name]);
|
|
774
819
|
}
|
|
775
820
|
else if (this.isVariableRef(value)) {
|
|
@@ -777,7 +822,7 @@ export class Translator {
|
|
|
777
822
|
const varName = value.name;
|
|
778
823
|
if (withAliases && withAliases.has(varName)) {
|
|
779
824
|
const originalExpr = withAliases.get(varName);
|
|
780
|
-
conditions.push(`json_extract(properties, '$.${key}') = ?`);
|
|
825
|
+
conditions.push(`json_extract(properties, '$.${escSqlStr(key)}') = ?`);
|
|
781
826
|
if (originalExpr.type === "literal") {
|
|
782
827
|
params.push(originalExpr.value);
|
|
783
828
|
}
|
|
@@ -789,12 +834,12 @@ export class Translator {
|
|
|
789
834
|
}
|
|
790
835
|
}
|
|
791
836
|
else {
|
|
792
|
-
conditions.push(`json_extract(properties, '$.${key}') = ?`);
|
|
837
|
+
conditions.push(`json_extract(properties, '$.${escSqlStr(key)}') = ?`);
|
|
793
838
|
params.push(value);
|
|
794
839
|
}
|
|
795
840
|
}
|
|
796
841
|
else {
|
|
797
|
-
conditions.push(`json_extract(properties, '$.${key}') = ?`);
|
|
842
|
+
conditions.push(`json_extract(properties, '$.${escSqlStr(key)}') = ?`);
|
|
798
843
|
params.push(value);
|
|
799
844
|
}
|
|
800
845
|
}
|
|
@@ -898,7 +943,7 @@ export class Translator {
|
|
|
898
943
|
}
|
|
899
944
|
if (nullKeys.length > 0) {
|
|
900
945
|
// Need to merge non-null props and remove null keys
|
|
901
|
-
const removePaths = nullKeys.map(k => `'$.${k}'`).join(', ');
|
|
946
|
+
const removePaths = nullKeys.map(k => `'$.${escSqlStr(k)}'`).join(', ');
|
|
902
947
|
statements.push({
|
|
903
948
|
sql: `UPDATE ${table} SET properties = json_remove(json_patch(properties, ?), ${removePaths}) WHERE id = ?`,
|
|
904
949
|
params: [JSON.stringify(nonNullProps), varInfo.alias],
|
|
@@ -927,7 +972,7 @@ export class Translator {
|
|
|
927
972
|
// For expressions on created nodes, translate with subquery pattern
|
|
928
973
|
const { sql: exprSql, params: exprParams } = this.translateExpressionForCreatedNode(assignment.value, varInfo.alias);
|
|
929
974
|
statements.push({
|
|
930
|
-
sql: `UPDATE ${table} SET properties = json_set(properties, '$.${assignment.property}', ${exprSql}) WHERE id = ?`,
|
|
975
|
+
sql: `UPDATE ${table} SET properties = json_set(properties, '$.${escSqlStr(assignment.property)}', ${exprSql}) WHERE id = ?`,
|
|
931
976
|
params: [...exprParams, varInfo.alias],
|
|
932
977
|
});
|
|
933
978
|
}
|
|
@@ -935,7 +980,7 @@ export class Translator {
|
|
|
935
980
|
const { sql: exprSql, params: exprParams } = this.translateExpression(assignment.value);
|
|
936
981
|
// Use json_set with the SQL expression directly
|
|
937
982
|
statements.push({
|
|
938
|
-
sql: `UPDATE ${table} SET properties = json_set(properties, '$.${assignment.property}', ${exprSql}) WHERE id = ?`,
|
|
983
|
+
sql: `UPDATE ${table} SET properties = json_set(properties, '$.${escSqlStr(assignment.property)}', ${exprSql}) WHERE id = ?`,
|
|
939
984
|
params: [...exprParams, varInfo.alias],
|
|
940
985
|
});
|
|
941
986
|
}
|
|
@@ -945,7 +990,7 @@ export class Translator {
|
|
|
945
990
|
// If value is null, remove the property instead of setting it to null
|
|
946
991
|
if (value === null) {
|
|
947
992
|
statements.push({
|
|
948
|
-
sql: `UPDATE ${table} SET properties = json_remove(properties, '$.${assignment.property}') WHERE id = ?`,
|
|
993
|
+
sql: `UPDATE ${table} SET properties = json_remove(properties, '$.${escSqlStr(assignment.property)}') WHERE id = ?`,
|
|
949
994
|
params: [varInfo.alias],
|
|
950
995
|
});
|
|
951
996
|
}
|
|
@@ -953,7 +998,7 @@ export class Translator {
|
|
|
953
998
|
assertValidPropertyValue(value);
|
|
954
999
|
// Use json_set to update the property
|
|
955
1000
|
statements.push({
|
|
956
|
-
sql: `UPDATE ${table} SET properties = json_set(properties, '$.${assignment.property}', json(?)) WHERE id = ?`,
|
|
1001
|
+
sql: `UPDATE ${table} SET properties = json_set(properties, '$.${escSqlStr(assignment.property)}', json(?)) WHERE id = ?`,
|
|
957
1002
|
params: [JSON.stringify(value), varInfo.alias],
|
|
958
1003
|
});
|
|
959
1004
|
}
|
|
@@ -1070,7 +1115,7 @@ export class Translator {
|
|
|
1070
1115
|
else if (item.property) {
|
|
1071
1116
|
// Remove property
|
|
1072
1117
|
statements.push({
|
|
1073
|
-
sql: `UPDATE ${table} SET properties = json_remove(properties, '$.${item.property}') WHERE id = ?`,
|
|
1118
|
+
sql: `UPDATE ${table} SET properties = json_remove(properties, '$.${escSqlStr(item.property)}') WHERE id = ?`,
|
|
1074
1119
|
params: [varInfo.alias],
|
|
1075
1120
|
});
|
|
1076
1121
|
}
|
|
@@ -1404,11 +1449,11 @@ export class Translator {
|
|
|
1404
1449
|
if (pattern.properties) {
|
|
1405
1450
|
for (const [key, value] of Object.entries(pattern.properties)) {
|
|
1406
1451
|
if (this.isParameterRef(value)) {
|
|
1407
|
-
whereParts.push(`json_extract(${info.alias}.properties, '$.${key}') = ?`);
|
|
1452
|
+
whereParts.push(`json_extract(${info.alias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
1408
1453
|
whereParams.push(this.ctx.paramValues[value.name]);
|
|
1409
1454
|
}
|
|
1410
1455
|
else {
|
|
1411
|
-
whereParts.push(`json_extract(${info.alias}.properties, '$.${key}') = ?`);
|
|
1456
|
+
whereParts.push(`json_extract(${info.alias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
1412
1457
|
whereParams.push(value);
|
|
1413
1458
|
}
|
|
1414
1459
|
}
|
|
@@ -1699,21 +1744,21 @@ export class Translator {
|
|
|
1699
1744
|
for (const [key, value] of Object.entries(relPattern.edge.properties)) {
|
|
1700
1745
|
if (this.isParameterRef(value)) {
|
|
1701
1746
|
if (isOptional) {
|
|
1702
|
-
edgeOnConditions.push(`json_extract(${relPattern.edgeAlias}.properties, '$.${key}') = ?`);
|
|
1747
|
+
edgeOnConditions.push(`json_extract(${relPattern.edgeAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
1703
1748
|
edgeOnParams.push(this.ctx.paramValues[value.name]);
|
|
1704
1749
|
}
|
|
1705
1750
|
else {
|
|
1706
|
-
whereParts.push(`json_extract(${relPattern.edgeAlias}.properties, '$.${key}') = ?`);
|
|
1751
|
+
whereParts.push(`json_extract(${relPattern.edgeAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
1707
1752
|
whereParams.push(this.ctx.paramValues[value.name]);
|
|
1708
1753
|
}
|
|
1709
1754
|
}
|
|
1710
1755
|
else {
|
|
1711
1756
|
if (isOptional) {
|
|
1712
|
-
edgeOnConditions.push(`json_extract(${relPattern.edgeAlias}.properties, '$.${key}') = ?`);
|
|
1757
|
+
edgeOnConditions.push(`json_extract(${relPattern.edgeAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
1713
1758
|
edgeOnParams.push(value);
|
|
1714
1759
|
}
|
|
1715
1760
|
else {
|
|
1716
|
-
whereParts.push(`json_extract(${relPattern.edgeAlias}.properties, '$.${key}') = ?`);
|
|
1761
|
+
whereParts.push(`json_extract(${relPattern.edgeAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
1717
1762
|
whereParams.push(value);
|
|
1718
1763
|
}
|
|
1719
1764
|
}
|
|
@@ -1926,11 +1971,11 @@ export class Translator {
|
|
|
1926
1971
|
if (isOptional && targetPattern?.properties) {
|
|
1927
1972
|
for (const [key, value] of Object.entries(targetPattern.properties)) {
|
|
1928
1973
|
if (this.isParameterRef(value)) {
|
|
1929
|
-
targetOnConditions.push(`json_extract(${relPattern.targetAlias}.properties, '$.${key}') = ?`);
|
|
1974
|
+
targetOnConditions.push(`json_extract(${relPattern.targetAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
1930
1975
|
targetOnParams.push(this.ctx.paramValues[value.name]);
|
|
1931
1976
|
}
|
|
1932
1977
|
else {
|
|
1933
|
-
targetOnConditions.push(`json_extract(${relPattern.targetAlias}.properties, '$.${key}') = ?`);
|
|
1978
|
+
targetOnConditions.push(`json_extract(${relPattern.targetAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
1934
1979
|
targetOnParams.push(value);
|
|
1935
1980
|
}
|
|
1936
1981
|
}
|
|
@@ -1999,11 +2044,11 @@ export class Translator {
|
|
|
1999
2044
|
if (sourcePattern?.properties && !sourceIsOptional) {
|
|
2000
2045
|
for (const [key, value] of Object.entries(sourcePattern.properties)) {
|
|
2001
2046
|
if (this.isParameterRef(value)) {
|
|
2002
|
-
whereParts.push(`json_extract(${relPattern.sourceAlias}.properties, '$.${key}') = ?`);
|
|
2047
|
+
whereParts.push(`json_extract(${relPattern.sourceAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
2003
2048
|
whereParams.push(this.ctx.paramValues[value.name]);
|
|
2004
2049
|
}
|
|
2005
2050
|
else {
|
|
2006
|
-
whereParts.push(`json_extract(${relPattern.sourceAlias}.properties, '$.${key}') = ?`);
|
|
2051
|
+
whereParts.push(`json_extract(${relPattern.sourceAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
2007
2052
|
whereParams.push(value);
|
|
2008
2053
|
}
|
|
2009
2054
|
}
|
|
@@ -2022,11 +2067,11 @@ export class Translator {
|
|
|
2022
2067
|
if (targetPattern?.properties) {
|
|
2023
2068
|
for (const [key, value] of Object.entries(targetPattern.properties)) {
|
|
2024
2069
|
if (this.isParameterRef(value)) {
|
|
2025
|
-
whereParts.push(`json_extract(${relPattern.targetAlias}.properties, '$.${key}') = ?`);
|
|
2070
|
+
whereParts.push(`json_extract(${relPattern.targetAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
2026
2071
|
whereParams.push(this.ctx.paramValues[value.name]);
|
|
2027
2072
|
}
|
|
2028
2073
|
else {
|
|
2029
|
-
whereParts.push(`json_extract(${relPattern.targetAlias}.properties, '$.${key}') = ?`);
|
|
2074
|
+
whereParts.push(`json_extract(${relPattern.targetAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
2030
2075
|
whereParams.push(value);
|
|
2031
2076
|
}
|
|
2032
2077
|
}
|
|
@@ -2135,11 +2180,11 @@ export class Translator {
|
|
|
2135
2180
|
if (pattern.properties) {
|
|
2136
2181
|
for (const [key, value] of Object.entries(pattern.properties)) {
|
|
2137
2182
|
if (this.isParameterRef(value)) {
|
|
2138
|
-
onConditions.push(`json_extract(${info.alias}.properties, '$.${key}') = ?`);
|
|
2183
|
+
onConditions.push(`json_extract(${info.alias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
2139
2184
|
onParams.push(this.ctx.paramValues[value.name]);
|
|
2140
2185
|
}
|
|
2141
2186
|
else {
|
|
2142
|
-
onConditions.push(`json_extract(${info.alias}.properties, '$.${key}') = ?`);
|
|
2187
|
+
onConditions.push(`json_extract(${info.alias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
2143
2188
|
onParams.push(value);
|
|
2144
2189
|
}
|
|
2145
2190
|
}
|
|
@@ -2164,11 +2209,11 @@ export class Translator {
|
|
|
2164
2209
|
if (pattern.properties) {
|
|
2165
2210
|
for (const [key, value] of Object.entries(pattern.properties)) {
|
|
2166
2211
|
if (this.isParameterRef(value)) {
|
|
2167
|
-
whereParts.push(`json_extract(${info.alias}.properties, '$.${key}') = ?`);
|
|
2212
|
+
whereParts.push(`json_extract(${info.alias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
2168
2213
|
whereParams.push(this.ctx.paramValues[value.name]);
|
|
2169
2214
|
}
|
|
2170
2215
|
else {
|
|
2171
|
-
whereParts.push(`json_extract(${info.alias}.properties, '$.${key}') = ?`);
|
|
2216
|
+
whereParts.push(`json_extract(${info.alias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
2172
2217
|
whereParams.push(value);
|
|
2173
2218
|
}
|
|
2174
2219
|
}
|
|
@@ -2197,11 +2242,11 @@ export class Translator {
|
|
|
2197
2242
|
if (pattern.properties) {
|
|
2198
2243
|
for (const [key, value] of Object.entries(pattern.properties)) {
|
|
2199
2244
|
if (this.isParameterRef(value)) {
|
|
2200
|
-
whereParts.push(`json_extract(${info.alias}.properties, '$.${key}') = ?`);
|
|
2245
|
+
whereParts.push(`json_extract(${info.alias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
2201
2246
|
whereParams.push(this.ctx.paramValues[value.name]);
|
|
2202
2247
|
}
|
|
2203
2248
|
else {
|
|
2204
|
-
whereParts.push(`json_extract(${info.alias}.properties, '$.${key}') = ?`);
|
|
2249
|
+
whereParts.push(`json_extract(${info.alias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
2205
2250
|
whereParams.push(value);
|
|
2206
2251
|
}
|
|
2207
2252
|
}
|
|
@@ -2219,11 +2264,11 @@ export class Translator {
|
|
|
2219
2264
|
if (pattern.properties) {
|
|
2220
2265
|
for (const [key, value] of Object.entries(pattern.properties)) {
|
|
2221
2266
|
if (this.isParameterRef(value)) {
|
|
2222
|
-
onConditions.push(`json_extract(${info.alias}.properties, '$.${key}') = ?`);
|
|
2267
|
+
onConditions.push(`json_extract(${info.alias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
2223
2268
|
onParams.push(this.ctx.paramValues[value.name]);
|
|
2224
2269
|
}
|
|
2225
2270
|
else {
|
|
2226
|
-
onConditions.push(`json_extract(${info.alias}.properties, '$.${key}') = ?`);
|
|
2271
|
+
onConditions.push(`json_extract(${info.alias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
2227
2272
|
onParams.push(value);
|
|
2228
2273
|
}
|
|
2229
2274
|
}
|
|
@@ -2242,11 +2287,11 @@ export class Translator {
|
|
|
2242
2287
|
if (pattern.properties) {
|
|
2243
2288
|
for (const [key, value] of Object.entries(pattern.properties)) {
|
|
2244
2289
|
if (this.isParameterRef(value)) {
|
|
2245
|
-
whereParts.push(`json_extract(${info.alias}.properties, '$.${key}') = ?`);
|
|
2290
|
+
whereParts.push(`json_extract(${info.alias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
2246
2291
|
whereParams.push(this.ctx.paramValues[value.name]);
|
|
2247
2292
|
}
|
|
2248
2293
|
else {
|
|
2249
|
-
whereParts.push(`json_extract(${info.alias}.properties, '$.${key}') = ?`);
|
|
2294
|
+
whereParts.push(`json_extract(${info.alias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
2250
2295
|
whereParams.push(value);
|
|
2251
2296
|
}
|
|
2252
2297
|
}
|
|
@@ -3156,7 +3201,7 @@ export class Translator {
|
|
|
3156
3201
|
const edgePropParams = [];
|
|
3157
3202
|
if (edgeProperties) {
|
|
3158
3203
|
for (const [key, value] of Object.entries(edgeProperties)) {
|
|
3159
|
-
edgePropConditions.push(`json_extract(properties, '$.${key}') = ?`);
|
|
3204
|
+
edgePropConditions.push(`json_extract(properties, '$.${escSqlStr(key)}') = ?`);
|
|
3160
3205
|
edgePropParams.push(value);
|
|
3161
3206
|
}
|
|
3162
3207
|
}
|
|
@@ -3179,11 +3224,11 @@ export class Translator {
|
|
|
3179
3224
|
if (sourcePattern?.properties) {
|
|
3180
3225
|
for (const [key, value] of Object.entries(sourcePattern.properties)) {
|
|
3181
3226
|
if (this.isParameterRef(value)) {
|
|
3182
|
-
sourceFilterParts.push(`json_extract(src_n.properties, '$.${key}') = ?`);
|
|
3227
|
+
sourceFilterParts.push(`json_extract(src_n.properties, '$.${escSqlStr(key)}') = ?`);
|
|
3183
3228
|
sourceFilterParams.push(this.ctx.paramValues[value.name]);
|
|
3184
3229
|
}
|
|
3185
3230
|
else {
|
|
3186
|
-
sourceFilterParts.push(`json_extract(src_n.properties, '$.${key}') = ?`);
|
|
3231
|
+
sourceFilterParts.push(`json_extract(src_n.properties, '$.${escSqlStr(key)}') = ?`);
|
|
3187
3232
|
sourceFilterParams.push(value);
|
|
3188
3233
|
}
|
|
3189
3234
|
}
|
|
@@ -3408,11 +3453,11 @@ export class Translator {
|
|
|
3408
3453
|
if (sourcePattern?.properties) {
|
|
3409
3454
|
for (const [key, value] of Object.entries(sourcePattern.properties)) {
|
|
3410
3455
|
if (this.isParameterRef(value)) {
|
|
3411
|
-
whereParts.push(`json_extract(${pattern.sourceAlias}.properties, '$.${key}') = ?`);
|
|
3456
|
+
whereParts.push(`json_extract(${pattern.sourceAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
3412
3457
|
deferredWhereParams.push(this.ctx.paramValues[value.name]);
|
|
3413
3458
|
}
|
|
3414
3459
|
else {
|
|
3415
|
-
whereParts.push(`json_extract(${pattern.sourceAlias}.properties, '$.${key}') = ?`);
|
|
3460
|
+
whereParts.push(`json_extract(${pattern.sourceAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
3416
3461
|
deferredWhereParams.push(value);
|
|
3417
3462
|
}
|
|
3418
3463
|
}
|
|
@@ -3522,11 +3567,11 @@ export class Translator {
|
|
|
3522
3567
|
if (sourcePattern?.properties) {
|
|
3523
3568
|
for (const [key, value] of Object.entries(sourcePattern.properties)) {
|
|
3524
3569
|
if (this.isParameterRef(value)) {
|
|
3525
|
-
whereParts.push(`json_extract(${varLengthSourceAlias}.properties, '$.${key}') = ?`);
|
|
3570
|
+
whereParts.push(`json_extract(${varLengthSourceAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
3526
3571
|
deferredWhereParams.push(this.ctx.paramValues[value.name]);
|
|
3527
3572
|
}
|
|
3528
3573
|
else {
|
|
3529
|
-
whereParts.push(`json_extract(${varLengthSourceAlias}.properties, '$.${key}') = ?`);
|
|
3574
|
+
whereParts.push(`json_extract(${varLengthSourceAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
3530
3575
|
deferredWhereParams.push(value);
|
|
3531
3576
|
}
|
|
3532
3577
|
}
|
|
@@ -3582,11 +3627,11 @@ export class Translator {
|
|
|
3582
3627
|
if (targetPattern?.properties) {
|
|
3583
3628
|
for (const [key, value] of Object.entries(targetPattern.properties)) {
|
|
3584
3629
|
if (this.isParameterRef(value)) {
|
|
3585
|
-
whereParts.push(`json_extract(${varLengthTargetAlias}.properties, '$.${key}') = ?`);
|
|
3630
|
+
whereParts.push(`json_extract(${varLengthTargetAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
3586
3631
|
deferredWhereParams.push(this.ctx.paramValues[value.name]);
|
|
3587
3632
|
}
|
|
3588
3633
|
else {
|
|
3589
|
-
whereParts.push(`json_extract(${varLengthTargetAlias}.properties, '$.${key}') = ?`);
|
|
3634
|
+
whereParts.push(`json_extract(${varLengthTargetAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
3590
3635
|
deferredWhereParams.push(value);
|
|
3591
3636
|
}
|
|
3592
3637
|
}
|
|
@@ -3627,11 +3672,11 @@ export class Translator {
|
|
|
3627
3672
|
if (targetPattern?.properties) {
|
|
3628
3673
|
for (const [key, value] of Object.entries(targetPattern.properties)) {
|
|
3629
3674
|
if (this.isParameterRef(value)) {
|
|
3630
|
-
targetOnConditions.push(`json_extract(${varLengthTargetAlias}.properties, '$.${key}') = ?`);
|
|
3675
|
+
targetOnConditions.push(`json_extract(${varLengthTargetAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
3631
3676
|
allParams.push(this.ctx.paramValues[value.name]);
|
|
3632
3677
|
}
|
|
3633
3678
|
else {
|
|
3634
|
-
targetOnConditions.push(`json_extract(${varLengthTargetAlias}.properties, '$.${key}') = ?`);
|
|
3679
|
+
targetOnConditions.push(`json_extract(${varLengthTargetAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
3635
3680
|
allParams.push(value);
|
|
3636
3681
|
}
|
|
3637
3682
|
}
|
|
@@ -3672,11 +3717,11 @@ export class Translator {
|
|
|
3672
3717
|
if (targetPattern?.properties) {
|
|
3673
3718
|
for (const [key, value] of Object.entries(targetPattern.properties)) {
|
|
3674
3719
|
if (this.isParameterRef(value)) {
|
|
3675
|
-
whereParts.push(`json_extract(${varLengthTargetAlias}.properties, '$.${key}') = ?`);
|
|
3720
|
+
whereParts.push(`json_extract(${varLengthTargetAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
3676
3721
|
deferredWhereParams.push(this.ctx.paramValues[value.name]);
|
|
3677
3722
|
}
|
|
3678
3723
|
else {
|
|
3679
|
-
whereParts.push(`json_extract(${varLengthTargetAlias}.properties, '$.${key}') = ?`);
|
|
3724
|
+
whereParts.push(`json_extract(${varLengthTargetAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
3680
3725
|
deferredWhereParams.push(value);
|
|
3681
3726
|
}
|
|
3682
3727
|
}
|
|
@@ -3839,11 +3884,11 @@ export class Translator {
|
|
|
3839
3884
|
if (targetPattern2?.properties) {
|
|
3840
3885
|
for (const [key, value] of Object.entries(targetPattern2.properties)) {
|
|
3841
3886
|
if (this.isParameterRef(value)) {
|
|
3842
|
-
whereParts.push(`json_extract(${pattern.targetAlias}.properties, '$.${key}') = ?`);
|
|
3887
|
+
whereParts.push(`json_extract(${pattern.targetAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
3843
3888
|
deferredWhereParams.push(this.ctx.paramValues[value.name]);
|
|
3844
3889
|
}
|
|
3845
3890
|
else {
|
|
3846
|
-
whereParts.push(`json_extract(${pattern.targetAlias}.properties, '$.${key}') = ?`);
|
|
3891
|
+
whereParts.push(`json_extract(${pattern.targetAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
3847
3892
|
deferredWhereParams.push(value);
|
|
3848
3893
|
}
|
|
3849
3894
|
}
|
|
@@ -3903,11 +3948,11 @@ export class Translator {
|
|
|
3903
3948
|
if (afterTargetPattern?.properties) {
|
|
3904
3949
|
for (const [key, value] of Object.entries(afterTargetPattern.properties)) {
|
|
3905
3950
|
if (this.isParameterRef(value)) {
|
|
3906
|
-
whereParts.push(`json_extract(${pattern.targetAlias}.properties, '$.${key}') = ?`);
|
|
3951
|
+
whereParts.push(`json_extract(${pattern.targetAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
3907
3952
|
deferredWhereParams.push(this.ctx.paramValues[value.name]);
|
|
3908
3953
|
}
|
|
3909
3954
|
else {
|
|
3910
|
-
whereParts.push(`json_extract(${pattern.targetAlias}.properties, '$.${key}') = ?`);
|
|
3955
|
+
whereParts.push(`json_extract(${pattern.targetAlias}.properties, '$.${escSqlStr(key)}') = ?`);
|
|
3911
3956
|
deferredWhereParams.push(value);
|
|
3912
3957
|
}
|
|
3913
3958
|
}
|
|
@@ -4090,7 +4135,7 @@ export class Translator {
|
|
|
4090
4135
|
if (!varInfo) {
|
|
4091
4136
|
throw new Error(`Unknown variable: ${clause.expression.variable}`);
|
|
4092
4137
|
}
|
|
4093
|
-
jsonExpr = `json_extract(${varInfo.alias}.properties, '$.${clause.expression.property}')`;
|
|
4138
|
+
jsonExpr = `json_extract(${varInfo.alias}.properties, '$.${escSqlStr(clause.expression.property)}')`;
|
|
4094
4139
|
}
|
|
4095
4140
|
else if (clause.expression.type === "function" || clause.expression.type === "binary") {
|
|
4096
4141
|
// Function call like range(1, 10) or binary expression like (first + second)
|
|
@@ -4667,7 +4712,7 @@ export class Translator {
|
|
|
4667
4712
|
params.push(...objectResult.params);
|
|
4668
4713
|
// Access property from the result using json_extract
|
|
4669
4714
|
return {
|
|
4670
|
-
sql: `json_extract(${objectResult.sql}, '$.${expr.property}')`,
|
|
4715
|
+
sql: `json_extract(${objectResult.sql}, '$.${escSqlStr(expr.property)}')`,
|
|
4671
4716
|
tables,
|
|
4672
4717
|
params,
|
|
4673
4718
|
};
|
|
@@ -4702,7 +4747,7 @@ export class Translator {
|
|
|
4702
4747
|
}
|
|
4703
4748
|
// Access property from the unwound value using json_extract
|
|
4704
4749
|
return {
|
|
4705
|
-
sql: `json_extract(${baseSql}, '$.${expr.property}')`,
|
|
4750
|
+
sql: `json_extract(${baseSql}, '$.${escSqlStr(expr.property)}')`,
|
|
4706
4751
|
tables,
|
|
4707
4752
|
params,
|
|
4708
4753
|
};
|
|
@@ -4715,7 +4760,7 @@ export class Translator {
|
|
|
4715
4760
|
tables.push(varInfo.alias);
|
|
4716
4761
|
// Use -> operator to preserve JSON types (returns 'true'/'false' not 1/0)
|
|
4717
4762
|
return {
|
|
4718
|
-
sql: `${varInfo.alias}.properties -> '$.${expr.property}'`,
|
|
4763
|
+
sql: `${varInfo.alias}.properties -> '$.${escSqlStr(expr.property)}'`,
|
|
4719
4764
|
tables,
|
|
4720
4765
|
params,
|
|
4721
4766
|
};
|
|
@@ -4737,7 +4782,7 @@ export class Translator {
|
|
|
4737
4782
|
}
|
|
4738
4783
|
tables.push(varInfo.alias);
|
|
4739
4784
|
return {
|
|
4740
|
-
sql: `COUNT(${distinctKeyword}json_extract(${varInfo.alias}.properties, '$.${arg.property}'))`,
|
|
4785
|
+
sql: `COUNT(${distinctKeyword}json_extract(${varInfo.alias}.properties, '$.${escSqlStr(arg.property)}'))`,
|
|
4741
4786
|
tables,
|
|
4742
4787
|
params,
|
|
4743
4788
|
};
|
|
@@ -4871,7 +4916,7 @@ export class Translator {
|
|
|
4871
4916
|
tables.push(unwindClause.alias);
|
|
4872
4917
|
// UNWIND variables use the 'value' column from json_each
|
|
4873
4918
|
return {
|
|
4874
|
-
sql: `${expr.functionName}(${distinctKeyword}json_extract(${unwindClause.alias}.value, '$.${arg.property}'))`,
|
|
4919
|
+
sql: `${expr.functionName}(${distinctKeyword}json_extract(${unwindClause.alias}.value, '$.${escSqlStr(arg.property)}'))`,
|
|
4875
4920
|
tables,
|
|
4876
4921
|
params,
|
|
4877
4922
|
};
|
|
@@ -4884,7 +4929,7 @@ export class Translator {
|
|
|
4884
4929
|
tables.push(varInfo.alias);
|
|
4885
4930
|
// Use json_extract for numeric properties in aggregations
|
|
4886
4931
|
return {
|
|
4887
|
-
sql: `${expr.functionName}(${distinctKeyword}json_extract(${varInfo.alias}.properties, '$.${arg.property}'))`,
|
|
4932
|
+
sql: `${expr.functionName}(${distinctKeyword}json_extract(${varInfo.alias}.properties, '$.${escSqlStr(arg.property)}'))`,
|
|
4888
4933
|
tables,
|
|
4889
4934
|
params,
|
|
4890
4935
|
};
|
|
@@ -5044,7 +5089,7 @@ export class Translator {
|
|
|
5044
5089
|
throw new Error(`Unknown variable: ${arg.variable}`);
|
|
5045
5090
|
}
|
|
5046
5091
|
tables.push(varInfo.alias);
|
|
5047
|
-
valueSql = `json_extract(${varInfo.alias}.properties, '$.${arg.property}')`;
|
|
5092
|
+
valueSql = `json_extract(${varInfo.alias}.properties, '$.${escSqlStr(arg.property)}')`;
|
|
5048
5093
|
}
|
|
5049
5094
|
else if (arg.type === "variable") {
|
|
5050
5095
|
// Check if this is an UNWIND variable
|
|
@@ -5145,7 +5190,7 @@ export class Translator {
|
|
|
5145
5190
|
throw new Error(`Unknown variable: ${valueArg.variable}`);
|
|
5146
5191
|
}
|
|
5147
5192
|
tables.push(varInfo.alias);
|
|
5148
|
-
valueExpr = `json_extract(${varInfo.alias}.properties, '$.${valueArg.property}')`;
|
|
5193
|
+
valueExpr = `json_extract(${varInfo.alias}.properties, '$.${escSqlStr(valueArg.property)}')`;
|
|
5149
5194
|
}
|
|
5150
5195
|
else {
|
|
5151
5196
|
const argResult = this.translateFunctionArg(valueArg);
|
|
@@ -5256,7 +5301,7 @@ END FROM (SELECT json_group_array(${valueExpr}) as sv))`,
|
|
|
5256
5301
|
// json_quote properly escapes strings for JSON
|
|
5257
5302
|
// Filter nulls: CASE WHEN value IS NULL THEN NULL ELSE json_quote(value) END
|
|
5258
5303
|
// GROUP_CONCAT ignores nulls
|
|
5259
|
-
const extractExpr = `json_extract(${varInfo.alias}.properties, '$.${arg.property}')`;
|
|
5304
|
+
const extractExpr = `json_extract(${varInfo.alias}.properties, '$.${escSqlStr(arg.property)}')`;
|
|
5260
5305
|
params.push(...collectOrderParams);
|
|
5261
5306
|
return {
|
|
5262
5307
|
sql: `COALESCE(json('[' || GROUP_CONCAT(DISTINCT CASE WHEN ${extractExpr} IS NOT NULL THEN json_quote(${extractExpr}) END${collectOrderClause}) || ']'), json('[]'))`,
|
|
@@ -5265,7 +5310,7 @@ END FROM (SELECT json_group_array(${valueExpr}) as sv))`,
|
|
|
5265
5310
|
};
|
|
5266
5311
|
}
|
|
5267
5312
|
// Neo4j's collect() skips NULL values - use GROUP_CONCAT with null filtering
|
|
5268
|
-
const extractExpr = `json_extract(${varInfo.alias}.properties, '$.${arg.property}')`;
|
|
5313
|
+
const extractExpr = `json_extract(${varInfo.alias}.properties, '$.${escSqlStr(arg.property)}')`;
|
|
5269
5314
|
params.push(...collectOrderParams);
|
|
5270
5315
|
return {
|
|
5271
5316
|
sql: `COALESCE(json('[' || GROUP_CONCAT(CASE WHEN ${extractExpr} IS NOT NULL THEN json_quote(${extractExpr}) END${collectOrderClause}) || ']'), json('[]'))`,
|
|
@@ -5751,7 +5796,7 @@ END FROM (SELECT json_group_array(${valueExpr}) as sv))`,
|
|
|
5751
5796
|
tables.push(...argResult.tables);
|
|
5752
5797
|
params.push(...argResult.params);
|
|
5753
5798
|
return {
|
|
5754
|
-
sql: `cypher_to_json_bool(json_type(${argResult.sql}, '$.${propName}') IS NOT NULL)`,
|
|
5799
|
+
sql: `cypher_to_json_bool(json_type(${argResult.sql}, '$.${escSqlStr(propName)}') IS NOT NULL)`,
|
|
5755
5800
|
tables,
|
|
5756
5801
|
params,
|
|
5757
5802
|
};
|
|
@@ -9072,7 +9117,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
9072
9117
|
const labelsToCheck = expr.labels || (expr.label ? [expr.label] : []);
|
|
9073
9118
|
// Use EXISTS with json_each to check if label is in the array
|
|
9074
9119
|
// For multiple labels, all must be present (AND)
|
|
9075
|
-
const labelChecks = labelsToCheck.map(l => `EXISTS(SELECT 1 FROM json_each(${varInfo.alias}.label) WHERE value = '${l}')`).join(' AND ');
|
|
9120
|
+
const labelChecks = labelsToCheck.map(l => `EXISTS(SELECT 1 FROM json_each(${varInfo.alias}.label) WHERE value = '${escSqlStr(l)}')`).join(' AND ');
|
|
9076
9121
|
return {
|
|
9077
9122
|
sql: `CASE WHEN ${varInfo.alias}.id IS NULL THEN NULL ELSE cypher_to_json_bool(${labelChecks}) END`,
|
|
9078
9123
|
tables,
|
|
@@ -9087,7 +9132,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
9087
9132
|
params.push(...objectResult.params);
|
|
9088
9133
|
// Access property from the result using json_extract
|
|
9089
9134
|
return {
|
|
9090
|
-
sql: `json_extract(${objectResult.sql}, '$.${expr.property}')`,
|
|
9135
|
+
sql: `json_extract(${objectResult.sql}, '$.${escSqlStr(expr.property)}')`,
|
|
9091
9136
|
tables,
|
|
9092
9137
|
params,
|
|
9093
9138
|
};
|
|
@@ -9104,7 +9149,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
9104
9149
|
if (item.type === "property") {
|
|
9105
9150
|
// .property shorthand - extract property from source
|
|
9106
9151
|
const key = item.property;
|
|
9107
|
-
jsonParts.push(`'${key}', json_extract(${sourceResult.sql}, '$.${key}')`);
|
|
9152
|
+
jsonParts.push(`'${escSqlStr(key)}', json_extract(${sourceResult.sql}, '$.${escSqlStr(key)}')`);
|
|
9108
9153
|
}
|
|
9109
9154
|
else if (item.type === "literal") {
|
|
9110
9155
|
// key: value syntax
|
|
@@ -9112,7 +9157,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
9112
9157
|
const valueResult = this.translateExpression(item.value);
|
|
9113
9158
|
tables.push(...valueResult.tables);
|
|
9114
9159
|
params.push(...valueResult.params);
|
|
9115
|
-
jsonParts.push(`'${key}', ${valueResult.sql}`);
|
|
9160
|
+
jsonParts.push(`'${escSqlStr(key)}', ${valueResult.sql}`);
|
|
9116
9161
|
}
|
|
9117
9162
|
else if (item.type === "allProperties") {
|
|
9118
9163
|
// .* - project all properties (not yet fully supported, just returns the source)
|
|
@@ -10456,7 +10501,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
10456
10501
|
if (expr.type === "property") {
|
|
10457
10502
|
const varInfo = this.ctx.variables.get(expr.variable);
|
|
10458
10503
|
if (varInfo) {
|
|
10459
|
-
return `json_extract(${varInfo.alias}.properties, '$.${expr.property}')`;
|
|
10504
|
+
return `json_extract(${varInfo.alias}.properties, '$.${escSqlStr(expr.property)}')`;
|
|
10460
10505
|
}
|
|
10461
10506
|
}
|
|
10462
10507
|
return sql;
|
|
@@ -10467,7 +10512,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
10467
10512
|
// Replace -> with json_extract for numeric operations
|
|
10468
10513
|
const varInfo = this.ctx.variables.get(expr.variable);
|
|
10469
10514
|
if (varInfo) {
|
|
10470
|
-
return `json_extract(${varInfo.alias}.properties, '$.${expr.property}')`;
|
|
10515
|
+
return `json_extract(${varInfo.alias}.properties, '$.${escSqlStr(expr.property)}')`;
|
|
10471
10516
|
}
|
|
10472
10517
|
}
|
|
10473
10518
|
return sql;
|
|
@@ -10496,7 +10541,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
10496
10541
|
const propName = expr.property;
|
|
10497
10542
|
params.push(nodeId);
|
|
10498
10543
|
return {
|
|
10499
|
-
sql: `(SELECT json_extract(properties, '$.${propName}') FROM nodes WHERE id = ?)`,
|
|
10544
|
+
sql: `(SELECT json_extract(properties, '$.${escSqlStr(propName)}') FROM nodes WHERE id = ?)`,
|
|
10500
10545
|
params,
|
|
10501
10546
|
};
|
|
10502
10547
|
}
|
|
@@ -10743,7 +10788,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
10743
10788
|
if (expr.type === "property") {
|
|
10744
10789
|
const varInfo = this.ctx.variables.get(expr.variable);
|
|
10745
10790
|
if (varInfo) {
|
|
10746
|
-
return `json_extract(${varInfo.alias}.properties, '$.${expr.property}')`;
|
|
10791
|
+
return `json_extract(${varInfo.alias}.properties, '$.${escSqlStr(expr.property)}')`;
|
|
10747
10792
|
}
|
|
10748
10793
|
}
|
|
10749
10794
|
return sql;
|
|
@@ -10850,7 +10895,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
10850
10895
|
// For property access, use json_extract
|
|
10851
10896
|
const varInfo = this.ctx.variables.get(listExpr.variable);
|
|
10852
10897
|
if (varInfo) {
|
|
10853
|
-
sourceExpr = `json_extract(${varInfo.alias}.properties, '$.${listExpr.property}')`;
|
|
10898
|
+
sourceExpr = `json_extract(${varInfo.alias}.properties, '$.${escSqlStr(listExpr.property)}')`;
|
|
10854
10899
|
}
|
|
10855
10900
|
}
|
|
10856
10901
|
// Determine what to select: the mapped expression or just the value
|
|
@@ -10990,7 +11035,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
10990
11035
|
// For property access, use json_extract
|
|
10991
11036
|
const varInfo = this.ctx.variables.get(listExpr.variable);
|
|
10992
11037
|
if (varInfo) {
|
|
10993
|
-
sourceExpr = `json_extract(${varInfo.alias}.properties, '$.${listExpr.property}')`;
|
|
11038
|
+
sourceExpr = `json_extract(${varInfo.alias}.properties, '$.${escSqlStr(listExpr.property)}')`;
|
|
10994
11039
|
}
|
|
10995
11040
|
}
|
|
10996
11041
|
// Build the WHERE clause from the filter condition
|
|
@@ -11025,7 +11070,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
11025
11070
|
// For property access, use json_extract
|
|
11026
11071
|
const varInfo = this.ctx.variables.get(listExpr.variable);
|
|
11027
11072
|
if (varInfo) {
|
|
11028
|
-
sourceExpr = `json_extract(${varInfo.alias}.properties, '$.${listExpr.property}')`;
|
|
11073
|
+
sourceExpr = `json_extract(${varInfo.alias}.properties, '$.${escSqlStr(listExpr.property)}')`;
|
|
11029
11074
|
}
|
|
11030
11075
|
}
|
|
11031
11076
|
// Translate the map expression
|
|
@@ -11061,18 +11106,18 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
11061
11106
|
case "property": {
|
|
11062
11107
|
if (expr.variable === iterVar) {
|
|
11063
11108
|
// Property access on the iterator variable: x.name
|
|
11064
|
-
return { sql: `json_extract(${elemAlias}.value, '$.${expr.property}')`, params };
|
|
11109
|
+
return { sql: `json_extract(${elemAlias}.value, '$.${escSqlStr(expr.property)}')`, params };
|
|
11065
11110
|
}
|
|
11066
11111
|
if (expr.variable === accVar) {
|
|
11067
11112
|
// Property access on accumulator (if acc is an object)
|
|
11068
|
-
return { sql: `json_extract(${tableAlias}.acc, '$.${expr.property}')`, params };
|
|
11113
|
+
return { sql: `json_extract(${tableAlias}.acc, '$.${escSqlStr(expr.property)}')`, params };
|
|
11069
11114
|
}
|
|
11070
11115
|
// Standard property translation
|
|
11071
11116
|
const varInfo = this.ctx.variables.get(expr.variable);
|
|
11072
11117
|
if (varInfo) {
|
|
11073
|
-
return { sql: `json_extract(${varInfo.alias}.properties, '$.${expr.property}')`, params };
|
|
11118
|
+
return { sql: `json_extract(${varInfo.alias}.properties, '$.${escSqlStr(expr.property)}')`, params };
|
|
11074
11119
|
}
|
|
11075
|
-
return { sql: `json_extract(${expr.variable}, '$.${expr.property}')`, params };
|
|
11120
|
+
return { sql: `json_extract(${expr.variable}, '$.${escSqlStr(expr.property)}')`, params };
|
|
11076
11121
|
}
|
|
11077
11122
|
case "literal": {
|
|
11078
11123
|
if (expr.value === null)
|
|
@@ -11510,13 +11555,13 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
11510
11555
|
return { sql: varResult.sql, params: varResult.params };
|
|
11511
11556
|
case "property":
|
|
11512
11557
|
if (expr.variable === startVar) {
|
|
11513
|
-
return { sql: `json_extract(${startAlias}.properties, '$.${expr.property}')`, params };
|
|
11558
|
+
return { sql: `json_extract(${startAlias}.properties, '$.${escSqlStr(expr.property)}')`, params };
|
|
11514
11559
|
}
|
|
11515
11560
|
if (expr.variable === edgeVar) {
|
|
11516
|
-
return { sql: `json_extract(${edgeAlias}.properties, '$.${expr.property}')`, params };
|
|
11561
|
+
return { sql: `json_extract(${edgeAlias}.properties, '$.${escSqlStr(expr.property)}')`, params };
|
|
11517
11562
|
}
|
|
11518
11563
|
if (expr.variable === targetVar) {
|
|
11519
|
-
return { sql: `json_extract(${targetAlias}.properties, '$.${expr.property}')`, params };
|
|
11564
|
+
return { sql: `json_extract(${targetAlias}.properties, '$.${escSqlStr(expr.property)}')`, params };
|
|
11520
11565
|
}
|
|
11521
11566
|
// Fall through to regular translation
|
|
11522
11567
|
const propResult = this.translateExpression(expr);
|
|
@@ -11595,7 +11640,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
11595
11640
|
const scope = findScope(expr.variable);
|
|
11596
11641
|
if (scope) {
|
|
11597
11642
|
// Extract property from the JSON value in the list element
|
|
11598
|
-
return { sql: `json_extract(${scope.tableAlias}.value, '$.${expr.property}')`, params };
|
|
11643
|
+
return { sql: `json_extract(${scope.tableAlias}.value, '$.${escSqlStr(expr.property)}')`, params };
|
|
11599
11644
|
}
|
|
11600
11645
|
// Fall through to regular translation for other variables
|
|
11601
11646
|
const propResult = this.translateExpression(expr);
|
|
@@ -11979,7 +12024,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
11979
12024
|
if (expr.type === "property") {
|
|
11980
12025
|
const varInfo = this.ctx.variables.get(expr.variable);
|
|
11981
12026
|
if (varInfo) {
|
|
11982
|
-
return `json_extract(${varInfo.alias}.properties, '$.${expr.property}')`;
|
|
12027
|
+
return `json_extract(${varInfo.alias}.properties, '$.${escSqlStr(expr.property)}')`;
|
|
11983
12028
|
}
|
|
11984
12029
|
}
|
|
11985
12030
|
// For literal arrays, the sql is already a json_array() call
|
|
@@ -12848,7 +12893,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
12848
12893
|
const originalExpr = withAliases.get(expr.variable);
|
|
12849
12894
|
const objectResult = this.translateExpression(originalExpr);
|
|
12850
12895
|
return {
|
|
12851
|
-
sql: this.buildDateTimeWithOffsetOrderBy(`json_extract(${objectResult.sql}, '$.${expr.property}')`),
|
|
12896
|
+
sql: this.buildDateTimeWithOffsetOrderBy(`json_extract(${objectResult.sql}, '$.${escSqlStr(expr.property)}')`),
|
|
12852
12897
|
params: objectResult.params,
|
|
12853
12898
|
};
|
|
12854
12899
|
}
|
|
@@ -12868,7 +12913,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
12868
12913
|
if (unwindClause) {
|
|
12869
12914
|
// UNWIND variables use the 'value' column from json_each
|
|
12870
12915
|
return {
|
|
12871
|
-
sql: this.buildDateTimeWithOffsetOrderBy(`json_extract(${unwindClause.alias}.value, '$.${expr.property}')`),
|
|
12916
|
+
sql: this.buildDateTimeWithOffsetOrderBy(`json_extract(${unwindClause.alias}.value, '$.${escSqlStr(expr.property)}')`),
|
|
12872
12917
|
params: [],
|
|
12873
12918
|
};
|
|
12874
12919
|
}
|
|
@@ -12878,7 +12923,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
12878
12923
|
throw new Error(`Unknown variable: ${expr.variable}`);
|
|
12879
12924
|
}
|
|
12880
12925
|
return {
|
|
12881
|
-
sql: this.buildDateTimeWithOffsetOrderBy(`json_extract(${varInfo.alias}.properties, '$.${expr.property}')`),
|
|
12926
|
+
sql: this.buildDateTimeWithOffsetOrderBy(`json_extract(${varInfo.alias}.properties, '$.${escSqlStr(expr.property)}')`),
|
|
12882
12927
|
params: [],
|
|
12883
12928
|
};
|
|
12884
12929
|
}
|
|
@@ -13068,7 +13113,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
13068
13113
|
throw new Error(`Unknown variable: ${expr.variable}`);
|
|
13069
13114
|
}
|
|
13070
13115
|
return {
|
|
13071
|
-
sql: `json_extract(${varInfo.alias}.properties, '$.${expr.property}')`,
|
|
13116
|
+
sql: `json_extract(${varInfo.alias}.properties, '$.${escSqlStr(expr.property)}')`,
|
|
13072
13117
|
params: [],
|
|
13073
13118
|
};
|
|
13074
13119
|
}
|
|
@@ -13089,7 +13134,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
13089
13134
|
if (unwindClause) {
|
|
13090
13135
|
// UNWIND variables use the 'value' column from json_each
|
|
13091
13136
|
return {
|
|
13092
|
-
sql: `json_extract(${unwindClause.alias}.value, '$.${expr.property}')`,
|
|
13137
|
+
sql: `json_extract(${unwindClause.alias}.value, '$.${escSqlStr(expr.property)}')`,
|
|
13093
13138
|
params: [],
|
|
13094
13139
|
};
|
|
13095
13140
|
}
|
|
@@ -13099,7 +13144,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
13099
13144
|
throw new Error(`Unknown variable: ${expr.variable}`);
|
|
13100
13145
|
}
|
|
13101
13146
|
return {
|
|
13102
|
-
sql: `json_extract(${varInfo.alias}.properties, '$.${expr.property}')`,
|
|
13147
|
+
sql: `json_extract(${varInfo.alias}.properties, '$.${escSqlStr(expr.property)}')`,
|
|
13103
13148
|
params: [],
|
|
13104
13149
|
};
|
|
13105
13150
|
}
|
|
@@ -13213,7 +13258,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
13213
13258
|
const labelsToCheck = expr.labels || (expr.label ? [expr.label] : []);
|
|
13214
13259
|
// Use EXISTS with json_each to check if label is in the array
|
|
13215
13260
|
// For multiple labels, all must be present (AND)
|
|
13216
|
-
const labelChecks = labelsToCheck.map((l) => `EXISTS(SELECT 1 FROM json_each(${varInfo.alias}.label) WHERE value = '${l}')`).join(' AND ');
|
|
13261
|
+
const labelChecks = labelsToCheck.map((l) => `EXISTS(SELECT 1 FROM json_each(${varInfo.alias}.label) WHERE value = '${escSqlStr(l)}')`).join(' AND ');
|
|
13217
13262
|
return {
|
|
13218
13263
|
sql: `(${labelChecks})`,
|
|
13219
13264
|
params: [],
|
|
@@ -13256,7 +13301,7 @@ SELECT COALESCE(json_group_array(CAST(n AS INTEGER)), json_array()) FROM r)`,
|
|
|
13256
13301
|
}
|
|
13257
13302
|
tables.push(varInfo.alias);
|
|
13258
13303
|
return {
|
|
13259
|
-
sql: `json_extract(${varInfo.alias}.properties, '$.${expr.property}')`,
|
|
13304
|
+
sql: `json_extract(${varInfo.alias}.properties, '$.${escSqlStr(expr.property)}')`,
|
|
13260
13305
|
tables,
|
|
13261
13306
|
params,
|
|
13262
13307
|
};
|