@leonardovida-md/drizzle-neo-duckdb 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1065,22 +1065,30 @@ function getTableSource(from) {
1065
1065
  if ("table" in from && from.table) {
1066
1066
  return {
1067
1067
  name: from.table,
1068
- alias: from.as ?? null
1068
+ alias: from.as ?? null,
1069
+ schema: "db" in from ? from.db ?? null : null
1069
1070
  };
1070
1071
  }
1071
1072
  if ("expr" in from && from.as) {
1072
1073
  return {
1073
1074
  name: from.as,
1074
- alias: from.as
1075
+ alias: from.as,
1076
+ schema: null
1075
1077
  };
1076
1078
  }
1077
1079
  return null;
1078
1080
  }
1079
1081
  function getQualifier(source) {
1080
- return source.alias ?? source.name;
1082
+ return {
1083
+ table: source.alias ?? source.name,
1084
+ schema: source.schema
1085
+ };
1081
1086
  }
1082
1087
  function isUnqualifiedColumnRef(expr) {
1083
- return typeof expr === "object" && expr !== null && "type" in expr && expr.type === "column_ref" && !(("table" in expr) && expr.table);
1088
+ return typeof expr === "object" && expr !== null && "type" in expr && expr.type === "column_ref" && (!("table" in expr) || !expr.table);
1089
+ }
1090
+ function isQualifiedColumnRef(expr) {
1091
+ return typeof expr === "object" && expr !== null && "type" in expr && expr.type === "column_ref" && "table" in expr && !!expr.table;
1084
1092
  }
1085
1093
  function getColumnName(col) {
1086
1094
  if (typeof col.column === "string") {
@@ -1091,29 +1099,80 @@ function getColumnName(col) {
1091
1099
  }
1092
1100
  return null;
1093
1101
  }
1094
- function walkOnClause(expr, leftSource, rightSource, ambiguousColumns) {
1102
+ function applyQualifier(col, qualifier) {
1103
+ col.table = qualifier.table;
1104
+ if (!("schema" in col) || !col.schema) {
1105
+ col.schema = qualifier.schema;
1106
+ }
1107
+ }
1108
+ function unwrapColumnRef(expr) {
1109
+ if (!expr || typeof expr !== "object")
1110
+ return null;
1111
+ if ("type" in expr && expr.type === "column_ref") {
1112
+ return expr;
1113
+ }
1114
+ if ("expr" in expr && expr.expr) {
1115
+ return unwrapColumnRef(expr.expr);
1116
+ }
1117
+ if ("ast" in expr && expr.ast && typeof expr.ast === "object") {
1118
+ return null;
1119
+ }
1120
+ if ("args" in expr && expr.args) {
1121
+ const args = expr.args;
1122
+ if (args.expr) {
1123
+ return unwrapColumnRef(args.expr);
1124
+ }
1125
+ if (args.value && args.value.length === 1) {
1126
+ return unwrapColumnRef(args.value[0]);
1127
+ }
1128
+ }
1129
+ return null;
1130
+ }
1131
+ function isBinaryExpr(expr) {
1132
+ return !!expr && typeof expr === "object" && "type" in expr && expr.type === "binary_expr";
1133
+ }
1134
+ function walkOnClause(expr, leftQualifier, rightQualifier, ambiguousColumns) {
1095
1135
  if (!expr || typeof expr !== "object")
1096
1136
  return false;
1097
1137
  let transformed = false;
1098
- if (expr.type === "binary_expr") {
1099
- if (expr.operator === "=") {
1100
- const left = expr.left;
1101
- const right = expr.right;
1102
- if (isUnqualifiedColumnRef(left) && isUnqualifiedColumnRef(right)) {
1103
- const leftColName = getColumnName(left);
1104
- const rightColName = getColumnName(right);
1105
- if (leftColName && rightColName && leftColName === rightColName) {
1106
- left.table = leftSource;
1107
- right.table = rightSource;
1108
- ambiguousColumns.add(leftColName);
1109
- transformed = true;
1110
- }
1138
+ if (isBinaryExpr(expr)) {
1139
+ const left = expr.left;
1140
+ const right = expr.right;
1141
+ const leftCol = unwrapColumnRef(left);
1142
+ const rightCol = unwrapColumnRef(right);
1143
+ const leftUnqualified = leftCol ? isUnqualifiedColumnRef(leftCol) : false;
1144
+ const rightUnqualified = rightCol ? isUnqualifiedColumnRef(rightCol) : false;
1145
+ const leftQualified = leftCol ? isQualifiedColumnRef(leftCol) : false;
1146
+ const rightQualified = rightCol ? isQualifiedColumnRef(rightCol) : false;
1147
+ const leftColName = leftCol ? getColumnName(leftCol) : null;
1148
+ const rightColName = rightCol ? getColumnName(rightCol) : null;
1149
+ if (expr.operator === "=" && leftColName && rightColName && leftColName === rightColName) {
1150
+ if (leftUnqualified && rightUnqualified) {
1151
+ applyQualifier(leftCol, leftQualifier);
1152
+ applyQualifier(rightCol, rightQualifier);
1153
+ ambiguousColumns.add(leftColName);
1154
+ transformed = true;
1155
+ } else if (leftQualified && rightUnqualified) {
1156
+ applyQualifier(rightCol, rightQualifier);
1157
+ ambiguousColumns.add(rightColName);
1158
+ transformed = true;
1159
+ } else if (leftUnqualified && rightQualified) {
1160
+ applyQualifier(leftCol, leftQualifier);
1161
+ ambiguousColumns.add(leftColName);
1162
+ transformed = true;
1111
1163
  }
1112
1164
  }
1113
- if (expr.operator === "AND" || expr.operator === "OR") {
1114
- transformed = walkOnClause(expr.left, leftSource, rightSource, ambiguousColumns) || transformed;
1115
- transformed = walkOnClause(expr.right, leftSource, rightSource, ambiguousColumns) || transformed;
1165
+ if (expr.operator === "=" && leftCol && rightCol && leftColName && rightColName && leftColName !== rightColName) {
1166
+ if (leftQualified && rightUnqualified && !rightColName.includes(".")) {
1167
+ applyQualifier(rightCol, rightQualifier);
1168
+ transformed = true;
1169
+ } else if (leftUnqualified && rightQualified && !leftColName.includes(".")) {
1170
+ applyQualifier(leftCol, leftQualifier);
1171
+ transformed = true;
1172
+ }
1116
1173
  }
1174
+ transformed = walkOnClause(isBinaryExpr(expr.left) ? expr.left : expr.left, leftQualifier, rightQualifier, ambiguousColumns) || transformed;
1175
+ transformed = walkOnClause(isBinaryExpr(expr.right) ? expr.right : expr.right, leftQualifier, rightQualifier, ambiguousColumns) || transformed;
1117
1176
  }
1118
1177
  return transformed;
1119
1178
  }
@@ -1124,12 +1183,12 @@ function qualifyAmbiguousInExpression(expr, defaultQualifier, ambiguousColumns)
1124
1183
  if (isUnqualifiedColumnRef(expr)) {
1125
1184
  const colName = getColumnName(expr);
1126
1185
  if (colName && ambiguousColumns.has(colName)) {
1127
- expr.table = defaultQualifier;
1186
+ applyQualifier(expr, defaultQualifier);
1128
1187
  transformed = true;
1129
1188
  }
1130
1189
  return transformed;
1131
1190
  }
1132
- if ("type" in expr && expr.type === "binary_expr") {
1191
+ if (isBinaryExpr(expr)) {
1133
1192
  const binary = expr;
1134
1193
  transformed = qualifyAmbiguousInExpression(binary.left, defaultQualifier, ambiguousColumns) || transformed;
1135
1194
  transformed = qualifyAmbiguousInExpression(binary.right, defaultQualifier, ambiguousColumns) || transformed;
@@ -1146,48 +1205,117 @@ function qualifyAmbiguousInExpression(expr, defaultQualifier, ambiguousColumns)
1146
1205
  transformed = qualifyAmbiguousInExpression(args.expr, defaultQualifier, ambiguousColumns) || transformed;
1147
1206
  }
1148
1207
  }
1208
+ if ("over" in expr && expr.over && typeof expr.over === "object") {
1209
+ const over = expr.over;
1210
+ if (Array.isArray(over.partition)) {
1211
+ for (const part of over.partition) {
1212
+ transformed = qualifyAmbiguousInExpression(part, defaultQualifier, ambiguousColumns) || transformed;
1213
+ }
1214
+ }
1215
+ if (Array.isArray(over.orderby)) {
1216
+ for (const order of over.orderby) {
1217
+ transformed = qualifyAmbiguousInExpression(order, defaultQualifier, ambiguousColumns) || transformed;
1218
+ }
1219
+ }
1220
+ }
1149
1221
  return transformed;
1150
1222
  }
1223
+ function hasUnqualifiedColumns(expr) {
1224
+ if (!expr || typeof expr !== "object")
1225
+ return false;
1226
+ if ("type" in expr && expr.type === "binary_expr") {
1227
+ const left = expr.left;
1228
+ const right = expr.right;
1229
+ const leftCol = unwrapColumnRef(left);
1230
+ const rightCol = unwrapColumnRef(right);
1231
+ if (isUnqualifiedColumnRef(left) || isUnqualifiedColumnRef(right) || leftCol && isUnqualifiedColumnRef(leftCol) || rightCol && isUnqualifiedColumnRef(rightCol)) {
1232
+ return true;
1233
+ }
1234
+ if (isBinaryExpr(expr.left) && hasUnqualifiedColumns(expr.left))
1235
+ return true;
1236
+ if (isBinaryExpr(expr.right) && hasUnqualifiedColumns(expr.right))
1237
+ return true;
1238
+ }
1239
+ if ("args" in expr && expr.args) {
1240
+ const args = expr.args;
1241
+ if (args.expr && isUnqualifiedColumnRef(args.expr))
1242
+ return true;
1243
+ if (args.value) {
1244
+ for (const arg of args.value) {
1245
+ if (isUnqualifiedColumnRef(arg))
1246
+ return true;
1247
+ }
1248
+ }
1249
+ }
1250
+ return false;
1251
+ }
1151
1252
  function walkSelect(select) {
1152
1253
  let transformed = false;
1153
1254
  const ambiguousColumns = new Set;
1154
1255
  if (Array.isArray(select.from) && select.from.length >= 2) {
1155
1256
  const firstSource = getTableSource(select.from[0]);
1156
- const defaultQualifier = firstSource ? getQualifier(firstSource) : "";
1257
+ const defaultQualifier = firstSource ? getQualifier(firstSource) : null;
1157
1258
  let prevSource = firstSource;
1259
+ let hasAnyUnqualified = false;
1158
1260
  for (const from of select.from) {
1159
1261
  if ("join" in from) {
1160
1262
  const join = from;
1161
- const currentSource = getTableSource(join);
1162
- if (join.on && prevSource && currentSource) {
1163
- const leftQualifier = getQualifier(prevSource);
1164
- const rightQualifier = getQualifier(currentSource);
1165
- transformed = walkOnClause(join.on, leftQualifier, rightQualifier, ambiguousColumns) || transformed;
1166
- }
1167
- prevSource = currentSource;
1168
- } else {
1169
- const source = getTableSource(from);
1170
- if (source) {
1171
- prevSource = source;
1263
+ if (join.on && hasUnqualifiedColumns(join.on)) {
1264
+ hasAnyUnqualified = true;
1265
+ break;
1172
1266
  }
1173
1267
  }
1174
- if ("expr" in from && from.expr && "ast" in from.expr) {
1175
- transformed = walkSelect(from.expr.ast) || transformed;
1176
- }
1177
1268
  }
1178
- if (ambiguousColumns.size > 0 && defaultQualifier) {
1179
- if (Array.isArray(select.columns)) {
1180
- for (const col of select.columns) {
1181
- if ("expr" in col) {
1182
- transformed = qualifyAmbiguousInExpression(col.expr, defaultQualifier, ambiguousColumns) || transformed;
1269
+ if (!hasAnyUnqualified) {
1270
+ for (const from of select.from) {
1271
+ if ("expr" in from && from.expr && "ast" in from.expr) {
1272
+ transformed = walkSelect(from.expr.ast) || transformed;
1273
+ }
1274
+ }
1275
+ } else {
1276
+ for (const from of select.from) {
1277
+ if ("join" in from) {
1278
+ const join = from;
1279
+ const currentSource = getTableSource(join);
1280
+ if (join.on && prevSource && currentSource) {
1281
+ const leftQualifier = getQualifier(prevSource);
1282
+ const rightQualifier = getQualifier(currentSource);
1283
+ transformed = walkOnClause(join.on, leftQualifier, rightQualifier, ambiguousColumns) || transformed;
1284
+ }
1285
+ if (join.using && prevSource && currentSource) {
1286
+ for (const usingCol of join.using) {
1287
+ if (typeof usingCol === "string") {
1288
+ ambiguousColumns.add(usingCol);
1289
+ } else if ("value" in usingCol) {
1290
+ ambiguousColumns.add(String(usingCol.value));
1291
+ }
1292
+ }
1293
+ }
1294
+ prevSource = currentSource;
1295
+ } else {
1296
+ const source = getTableSource(from);
1297
+ if (source) {
1298
+ prevSource = source;
1183
1299
  }
1184
1300
  }
1301
+ if ("expr" in from && from.expr && "ast" in from.expr) {
1302
+ transformed = walkSelect(from.expr.ast) || transformed;
1303
+ }
1185
1304
  }
1186
- transformed = qualifyAmbiguousInExpression(select.where, defaultQualifier, ambiguousColumns) || transformed;
1187
- if (Array.isArray(select.orderby)) {
1188
- for (const order of select.orderby) {
1189
- if (order.expr) {
1190
- transformed = qualifyAmbiguousInExpression(order.expr, defaultQualifier, ambiguousColumns) || transformed;
1305
+ if (ambiguousColumns.size > 0 && defaultQualifier) {
1306
+ if (Array.isArray(select.columns)) {
1307
+ for (const col of select.columns) {
1308
+ if ("expr" in col) {
1309
+ transformed = qualifyAmbiguousInExpression(col.expr, defaultQualifier, ambiguousColumns) || transformed;
1310
+ }
1311
+ }
1312
+ }
1313
+ transformed = qualifyAmbiguousInExpression(select.where, defaultQualifier, ambiguousColumns) || transformed;
1314
+ if (Array.isArray(select.orderby)) {
1315
+ for (const order of select.orderby) {
1316
+ if (order.expr) {
1317
+ transformed = qualifyAmbiguousInExpression(order.expr, defaultQualifier, ambiguousColumns) || transformed;
1318
+ }
1191
1319
  }
1192
1320
  }
1193
1321
  }
@@ -1212,6 +1340,323 @@ function qualifyJoinColumns(ast) {
1212
1340
  for (const stmt of statements) {
1213
1341
  if (stmt.type === "select") {
1214
1342
  transformed = walkSelect(stmt) || transformed;
1343
+ } else if (stmt.type === "insert") {
1344
+ const insert = stmt;
1345
+ if (insert.values && typeof insert.values === "object" && "type" in insert.values && insert.values.type === "select") {
1346
+ transformed = walkSelect(insert.values) || transformed;
1347
+ }
1348
+ } else if (stmt.type === "update") {
1349
+ const update = stmt;
1350
+ const mainSource = update.table?.[0] ? getTableSource(update.table[0]) : null;
1351
+ const defaultQualifier = mainSource ? getQualifier(mainSource) : null;
1352
+ const fromSources = update.from ?? [];
1353
+ const firstFrom = fromSources[0] ? getTableSource(fromSources[0]) : null;
1354
+ if (update.where && defaultQualifier && firstFrom) {
1355
+ const ambiguous = new Set;
1356
+ transformed = walkOnClause(update.where, defaultQualifier, getQualifier(firstFrom), ambiguous) || transformed;
1357
+ transformed = qualifyAmbiguousInExpression(update.where, defaultQualifier, ambiguous) || transformed;
1358
+ }
1359
+ if (Array.isArray(update.returning) && defaultQualifier) {
1360
+ for (const ret of update.returning) {
1361
+ transformed = qualifyAmbiguousInExpression(ret, defaultQualifier, new Set) || transformed;
1362
+ }
1363
+ }
1364
+ } else if (stmt.type === "delete") {
1365
+ const del = stmt;
1366
+ const mainSource = del.table?.[0] ? getTableSource(del.table[0]) : null;
1367
+ const defaultQualifier = mainSource ? getQualifier(mainSource) : null;
1368
+ const fromSources = del.from ?? [];
1369
+ const firstFrom = fromSources[0] ? getTableSource(fromSources[0]) : null;
1370
+ if (del.where && defaultQualifier && firstFrom) {
1371
+ const ambiguous = new Set;
1372
+ transformed = walkOnClause(del.where, defaultQualifier, getQualifier(firstFrom), ambiguous) || transformed;
1373
+ transformed = qualifyAmbiguousInExpression(del.where, defaultQualifier, ambiguous) || transformed;
1374
+ } else if (del.where && defaultQualifier) {
1375
+ transformed = qualifyAmbiguousInExpression(del.where, defaultQualifier, new Set) || transformed;
1376
+ }
1377
+ }
1378
+ }
1379
+ return transformed;
1380
+ }
1381
+
1382
+ // src/sql/visitors/generate-series-alias.ts
1383
+ function getColumnName2(col) {
1384
+ if (typeof col.column === "string") {
1385
+ return col.column;
1386
+ }
1387
+ if (col.column && "expr" in col.column && col.column.expr?.value) {
1388
+ return String(col.column.expr.value);
1389
+ }
1390
+ return null;
1391
+ }
1392
+ function isColumnRef(expr) {
1393
+ return typeof expr === "object" && expr !== null && "type" in expr && expr.type === "column_ref";
1394
+ }
1395
+ function isBinaryExpr2(expr) {
1396
+ return !!expr && typeof expr === "object" && "type" in expr && expr.type === "binary_expr";
1397
+ }
1398
+ function getGenerateSeriesAliases(from) {
1399
+ const aliases = new Set;
1400
+ if (!from || !Array.isArray(from))
1401
+ return aliases;
1402
+ for (const f of from) {
1403
+ if ("expr" in f && f.expr && typeof f.expr === "object") {
1404
+ const exprObj = f.expr;
1405
+ if (exprObj.type === "function" && "name" in exprObj) {
1406
+ const nameObj = exprObj.name;
1407
+ const nameParts = nameObj?.name;
1408
+ const fnName = nameParts?.[0]?.value;
1409
+ if (typeof fnName === "string" && fnName.toLowerCase() === "generate_series") {
1410
+ const alias = typeof f.as === "string" ? f.as : null;
1411
+ if (alias && !alias.includes("(")) {
1412
+ aliases.add(alias);
1413
+ }
1414
+ }
1415
+ }
1416
+ }
1417
+ }
1418
+ return aliases;
1419
+ }
1420
+ function rewriteAliasColumnRef(col, alias) {
1421
+ col.table = alias;
1422
+ col.column = { expr: { type: "default", value: "generate_series" } };
1423
+ }
1424
+ function walkExpression2(expr, aliases) {
1425
+ if (!expr || typeof expr !== "object")
1426
+ return false;
1427
+ let transformed = false;
1428
+ const exprObj = expr;
1429
+ if (isColumnRef(expr)) {
1430
+ if (!("table" in expr) || !expr.table) {
1431
+ const colName = getColumnName2(expr);
1432
+ if (colName && aliases.has(colName)) {
1433
+ rewriteAliasColumnRef(expr, colName);
1434
+ transformed = true;
1435
+ }
1436
+ }
1437
+ return transformed;
1438
+ }
1439
+ if (isBinaryExpr2(expr)) {
1440
+ const binary = expr;
1441
+ transformed = walkExpression2(binary.left, aliases) || transformed;
1442
+ transformed = walkExpression2(binary.right, aliases) || transformed;
1443
+ return transformed;
1444
+ }
1445
+ if (exprObj.type === "unary_expr" && exprObj.expr) {
1446
+ transformed = walkExpression2(exprObj.expr, aliases) || transformed;
1447
+ }
1448
+ if (exprObj.type === "cast" && exprObj.expr) {
1449
+ transformed = walkExpression2(exprObj.expr, aliases) || transformed;
1450
+ }
1451
+ if (exprObj.type === "case") {
1452
+ if (exprObj.expr) {
1453
+ transformed = walkExpression2(exprObj.expr, aliases) || transformed;
1454
+ }
1455
+ if (Array.isArray(exprObj.args)) {
1456
+ for (const whenClause of exprObj.args) {
1457
+ if (whenClause.cond) {
1458
+ transformed = walkExpression2(whenClause.cond, aliases) || transformed;
1459
+ }
1460
+ if (whenClause.result) {
1461
+ transformed = walkExpression2(whenClause.result, aliases) || transformed;
1462
+ }
1463
+ }
1464
+ }
1465
+ }
1466
+ if ("args" in exprObj && exprObj.args) {
1467
+ const args = exprObj.args;
1468
+ if (Array.isArray(args.value)) {
1469
+ for (const arg of args.value) {
1470
+ transformed = walkExpression2(arg, aliases) || transformed;
1471
+ }
1472
+ } else if (args.expr) {
1473
+ transformed = walkExpression2(args.expr, aliases) || transformed;
1474
+ }
1475
+ }
1476
+ if ("over" in exprObj && exprObj.over && typeof exprObj.over === "object") {
1477
+ const over = exprObj.over;
1478
+ if (Array.isArray(over.partition)) {
1479
+ for (const part of over.partition) {
1480
+ transformed = walkExpression2(part, aliases) || transformed;
1481
+ }
1482
+ }
1483
+ if (Array.isArray(over.orderby)) {
1484
+ for (const order of over.orderby) {
1485
+ transformed = walkExpression2(order, aliases) || transformed;
1486
+ }
1487
+ }
1488
+ }
1489
+ if ("ast" in exprObj && exprObj.ast) {
1490
+ const subAst = exprObj.ast;
1491
+ if (subAst.type === "select") {
1492
+ transformed = walkSelect2(subAst) || transformed;
1493
+ }
1494
+ }
1495
+ if (exprObj.type === "expr_list" && Array.isArray(exprObj.value)) {
1496
+ for (const item of exprObj.value) {
1497
+ transformed = walkExpression2(item, aliases) || transformed;
1498
+ }
1499
+ }
1500
+ return transformed;
1501
+ }
1502
+ function walkFrom2(from, aliases) {
1503
+ if (!from || !Array.isArray(from))
1504
+ return false;
1505
+ let transformed = false;
1506
+ for (const f of from) {
1507
+ if ("join" in f) {
1508
+ const join = f;
1509
+ transformed = walkExpression2(join.on, aliases) || transformed;
1510
+ }
1511
+ if ("expr" in f && f.expr && "ast" in f.expr) {
1512
+ transformed = walkSelect2(f.expr.ast) || transformed;
1513
+ }
1514
+ }
1515
+ return transformed;
1516
+ }
1517
+ function walkSelect2(select) {
1518
+ let transformed = false;
1519
+ const aliases = getGenerateSeriesAliases(select.from);
1520
+ if (select.with) {
1521
+ for (const cte of select.with) {
1522
+ const cteSelect = cte.stmt?.ast ?? cte.stmt;
1523
+ if (cteSelect && cteSelect.type === "select") {
1524
+ transformed = walkSelect2(cteSelect) || transformed;
1525
+ }
1526
+ }
1527
+ }
1528
+ transformed = walkFrom2(select.from, aliases) || transformed;
1529
+ transformed = walkExpression2(select.where, aliases) || transformed;
1530
+ if (select.having) {
1531
+ if (Array.isArray(select.having)) {
1532
+ for (const h of select.having) {
1533
+ transformed = walkExpression2(h, aliases) || transformed;
1534
+ }
1535
+ } else {
1536
+ transformed = walkExpression2(select.having, aliases) || transformed;
1537
+ }
1538
+ }
1539
+ if (Array.isArray(select.columns)) {
1540
+ for (const col of select.columns) {
1541
+ if ("expr" in col) {
1542
+ transformed = walkExpression2(col.expr, aliases) || transformed;
1543
+ }
1544
+ }
1545
+ }
1546
+ if (Array.isArray(select.groupby)) {
1547
+ for (const g of select.groupby) {
1548
+ transformed = walkExpression2(g, aliases) || transformed;
1549
+ }
1550
+ }
1551
+ if (Array.isArray(select.orderby)) {
1552
+ for (const order of select.orderby) {
1553
+ if (order.expr) {
1554
+ transformed = walkExpression2(order.expr, aliases) || transformed;
1555
+ }
1556
+ }
1557
+ }
1558
+ if (select._orderby) {
1559
+ for (const order of select._orderby) {
1560
+ if (order.expr) {
1561
+ transformed = walkExpression2(order.expr, aliases) || transformed;
1562
+ }
1563
+ }
1564
+ }
1565
+ if (select._next) {
1566
+ transformed = walkSelect2(select._next) || transformed;
1567
+ }
1568
+ return transformed;
1569
+ }
1570
+ function rewriteGenerateSeriesAliases(ast) {
1571
+ const statements = Array.isArray(ast) ? ast : [ast];
1572
+ let transformed = false;
1573
+ for (const stmt of statements) {
1574
+ if (stmt.type === "select") {
1575
+ transformed = walkSelect2(stmt) || transformed;
1576
+ }
1577
+ }
1578
+ return transformed;
1579
+ }
1580
+
1581
+ // src/sql/visitors/union-with-hoister.ts
1582
+ function getCteName(cte) {
1583
+ const nameObj = cte.name;
1584
+ if (!nameObj)
1585
+ return null;
1586
+ const value = nameObj.value;
1587
+ if (typeof value === "string")
1588
+ return value;
1589
+ return null;
1590
+ }
1591
+ function hoistWithInSelect(select) {
1592
+ if (!select.set_op || !select._next)
1593
+ return false;
1594
+ const arms = [];
1595
+ let current = select;
1596
+ while (current && current.type === "select") {
1597
+ arms.push(current);
1598
+ current = current._next;
1599
+ }
1600
+ const mergedWith = [];
1601
+ const seen = new Set;
1602
+ let hasWithBeyondFirst = false;
1603
+ for (const arm of arms) {
1604
+ if (arm.with && arm.with.length > 0) {
1605
+ if (arm !== arms[0]) {
1606
+ hasWithBeyondFirst = true;
1607
+ }
1608
+ for (const cte of arm.with) {
1609
+ const cteName = getCteName(cte);
1610
+ if (!cteName)
1611
+ return false;
1612
+ if (seen.has(cteName)) {
1613
+ return false;
1614
+ }
1615
+ seen.add(cteName);
1616
+ mergedWith.push(cte);
1617
+ }
1618
+ }
1619
+ }
1620
+ if (!hasWithBeyondFirst)
1621
+ return false;
1622
+ arms[0].with = mergedWith;
1623
+ if ("parentheses_symbol" in arms[0]) {
1624
+ arms[0].parentheses_symbol = false;
1625
+ }
1626
+ for (let i = 1;i < arms.length; i++) {
1627
+ arms[i].with = null;
1628
+ }
1629
+ return true;
1630
+ }
1631
+ function walkSelect3(select) {
1632
+ let transformed = false;
1633
+ if (select.with) {
1634
+ for (const cte of select.with) {
1635
+ const cteSelect = cte.stmt?.ast ?? cte.stmt;
1636
+ if (cteSelect && cteSelect.type === "select") {
1637
+ transformed = walkSelect3(cteSelect) || transformed;
1638
+ }
1639
+ }
1640
+ }
1641
+ if (Array.isArray(select.from)) {
1642
+ for (const from of select.from) {
1643
+ if ("expr" in from && from.expr && "ast" in from.expr) {
1644
+ transformed = walkSelect3(from.expr.ast) || transformed;
1645
+ }
1646
+ }
1647
+ }
1648
+ transformed = hoistWithInSelect(select) || transformed;
1649
+ if (select._next) {
1650
+ transformed = walkSelect3(select._next) || transformed;
1651
+ }
1652
+ return transformed;
1653
+ }
1654
+ function hoistUnionWith(ast) {
1655
+ const statements = Array.isArray(ast) ? ast : [ast];
1656
+ let transformed = false;
1657
+ for (const stmt of statements) {
1658
+ if (stmt.type === "select") {
1659
+ transformed = walkSelect3(stmt) || transformed;
1215
1660
  }
1216
1661
  }
1217
1662
  return transformed;
@@ -1220,29 +1665,73 @@ function qualifyJoinColumns(ast) {
1220
1665
  // src/sql/ast-transformer.ts
1221
1666
  var { Parser } = nodeSqlParser;
1222
1667
  var parser = new Parser;
1668
+ var CACHE_SIZE = 500;
1669
+ var transformCache = new Map;
1670
+ function getCachedOrTransform(query, transform) {
1671
+ const cached = transformCache.get(query);
1672
+ if (cached) {
1673
+ transformCache.delete(query);
1674
+ transformCache.set(query, cached);
1675
+ return cached;
1676
+ }
1677
+ const result = transform();
1678
+ if (transformCache.size >= CACHE_SIZE) {
1679
+ const oldestKey = transformCache.keys().next().value;
1680
+ if (oldestKey) {
1681
+ transformCache.delete(oldestKey);
1682
+ }
1683
+ }
1684
+ transformCache.set(query, result);
1685
+ return result;
1686
+ }
1687
+ var DEBUG_ENV = "DRIZZLE_DUCKDB_DEBUG_AST";
1688
+ function hasJoin(query) {
1689
+ return /\bjoin\b/i.test(query);
1690
+ }
1691
+ function debugLog(message, payload) {
1692
+ if (process?.env?.[DEBUG_ENV]) {
1693
+ console.debug("[duckdb-ast]", message, payload ?? "");
1694
+ }
1695
+ }
1223
1696
  function transformSQL(query) {
1224
1697
  const needsArrayTransform = query.includes("@>") || query.includes("<@") || query.includes("&&");
1225
- const needsJoinTransform = query.toLowerCase().includes("join");
1226
- if (!needsArrayTransform && !needsJoinTransform) {
1698
+ const needsJoinTransform = hasJoin(query) || /\bupdate\b/i.test(query) || /\bdelete\b/i.test(query);
1699
+ const needsUnionTransform = /\bunion\b/i.test(query) || /\bintersect\b/i.test(query) || /\bexcept\b/i.test(query);
1700
+ const needsGenerateSeriesTransform = /\bgenerate_series\b/i.test(query);
1701
+ if (!needsArrayTransform && !needsJoinTransform && !needsUnionTransform && !needsGenerateSeriesTransform) {
1227
1702
  return { sql: query, transformed: false };
1228
1703
  }
1229
- try {
1230
- const ast = parser.astify(query, { database: "PostgreSQL" });
1231
- let transformed = false;
1232
- if (needsArrayTransform) {
1233
- transformed = transformArrayOperators(ast) || transformed;
1234
- }
1235
- if (needsJoinTransform) {
1236
- transformed = qualifyJoinColumns(ast) || transformed;
1237
- }
1238
- if (!transformed) {
1704
+ return getCachedOrTransform(query, () => {
1705
+ try {
1706
+ const ast = parser.astify(query, { database: "PostgreSQL" });
1707
+ let transformed = false;
1708
+ if (needsArrayTransform) {
1709
+ transformed = transformArrayOperators(ast) || transformed;
1710
+ }
1711
+ if (needsJoinTransform) {
1712
+ transformed = qualifyJoinColumns(ast) || transformed;
1713
+ }
1714
+ if (needsGenerateSeriesTransform) {
1715
+ transformed = rewriteGenerateSeriesAliases(ast) || transformed;
1716
+ }
1717
+ if (needsUnionTransform) {
1718
+ transformed = hoistUnionWith(ast) || transformed;
1719
+ }
1720
+ if (!transformed) {
1721
+ debugLog("AST parsed but no transformation applied", {
1722
+ join: needsJoinTransform
1723
+ });
1724
+ return { sql: query, transformed: false };
1725
+ }
1726
+ const transformedSql = parser.sqlify(ast, { database: "PostgreSQL" });
1727
+ return { sql: transformedSql, transformed: true };
1728
+ } catch (err) {
1729
+ debugLog("AST transform failed; returning original SQL", {
1730
+ error: err.message
1731
+ });
1239
1732
  return { sql: query, transformed: false };
1240
1733
  }
1241
- const transformedSql = parser.sqlify(ast, { database: "PostgreSQL" });
1242
- return { sql: transformedSql, transformed: true };
1243
- } catch {
1244
- return { sql: query, transformed: false };
1245
- }
1734
+ });
1246
1735
  }
1247
1736
 
1248
1737
  // src/dialect.ts