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.
@@ -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 indexName = clause.indexName || `idx_${clause.property}`;
318
- const sql = `CREATE INDEX IF NOT EXISTS ${indexName} ON nodes(json_extract(properties, '$.${clause.property}'))`;
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
- const sql = `DROP INDEX IF EXISTS ${clause.indexName}`;
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
  };