@nicia-ai/typegraph 0.11.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -311,72 +311,6 @@ var core = {
311
311
  implies
312
312
  };
313
313
 
314
- // src/query/compiler/passes/recursive.ts
315
- function runRecursiveTraversalSelectionPass(ast) {
316
- const variableLengthTraversal = ast.traversals.find(
317
- (traversal) => traversal.variableLength !== void 0
318
- );
319
- if (variableLengthTraversal === void 0) {
320
- throw new chunk44SXEVF4_cjs.CompilerInvariantError("No variable-length traversal found");
321
- }
322
- if (ast.traversals.length > 1) {
323
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
324
- "Variable-length traversals with multiple traversals are not yet supported. Please use a single variable-length traversal."
325
- );
326
- }
327
- return variableLengthTraversal;
328
- }
329
-
330
- // src/query/compiler/passes/runner.ts
331
- function runCompilerPass(state, pass) {
332
- const output = pass.execute(state);
333
- return {
334
- state: pass.update(state, output)
335
- };
336
- }
337
- function compileTemporalFilter(options) {
338
- const { mode, asOf, tableAlias, currentTimestamp } = options;
339
- const prefix = tableAlias ? drizzleOrm.sql.raw(`${tableAlias}.`) : drizzleOrm.sql.raw("");
340
- const deletedAt = drizzleOrm.sql`${prefix}deleted_at`;
341
- const validFrom = drizzleOrm.sql`${prefix}valid_from`;
342
- const validTo = drizzleOrm.sql`${prefix}valid_to`;
343
- switch (mode) {
344
- case "current": {
345
- const now = currentTimestamp ?? drizzleOrm.sql`CURRENT_TIMESTAMP`;
346
- return drizzleOrm.sql`${deletedAt} IS NULL AND (${validFrom} IS NULL OR ${validFrom} <= ${now}) AND (${validTo} IS NULL OR ${validTo} > ${now})`;
347
- }
348
- case "asOf": {
349
- const timestamp = asOf;
350
- return drizzleOrm.sql`${deletedAt} IS NULL AND (${validFrom} IS NULL OR ${validFrom} <= ${timestamp}) AND (${validTo} IS NULL OR ${validTo} > ${timestamp})`;
351
- }
352
- case "includeEnded": {
353
- return drizzleOrm.sql`${deletedAt} IS NULL`;
354
- }
355
- case "includeTombstones": {
356
- return drizzleOrm.sql.raw("1=1");
357
- }
358
- }
359
- }
360
- function extractTemporalOptions(ast, tableAlias) {
361
- return {
362
- mode: ast.temporalMode.mode,
363
- asOf: ast.temporalMode.asOf,
364
- tableAlias
365
- };
366
- }
367
-
368
- // src/query/compiler/passes/temporal.ts
369
- function createTemporalFilterPass(ast, currentTimestamp) {
370
- return {
371
- forAlias(tableAlias) {
372
- return compileTemporalFilter({
373
- ...extractTemporalOptions(ast, tableAlias),
374
- currentTimestamp
375
- });
376
- }
377
- };
378
- }
379
-
380
314
  // src/query/subquery-utils.ts
381
315
  function normalizeValueType(valueType) {
382
316
  if (valueType === void 0 || valueType === "unknown") {
@@ -1463,1179 +1397,1876 @@ function extractVectorSimilarityPredicates(predicates) {
1463
1397
  return results;
1464
1398
  }
1465
1399
 
1466
- // src/query/compiler/passes/vector.ts
1467
- function runVectorPredicatePass(ast, dialect) {
1468
- const vectorPredicates = extractVectorSimilarityPredicates(ast.predicates);
1469
- if (vectorPredicates.length > 1) {
1470
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
1471
- "Multiple vector similarity predicates in a single query are not supported"
1400
+ // src/query/compiler/predicate-utils.ts
1401
+ var EMPTY_PREDICATES = [];
1402
+ function buildPredicateIndexKey(alias, targetType) {
1403
+ return `${alias}\0${targetType}`;
1404
+ }
1405
+ function resolvePredicateTargetType(predicate2) {
1406
+ return predicate2.targetType === "edge" ? "edge" : "node";
1407
+ }
1408
+ function buildPredicateIndex(ast) {
1409
+ const byAliasAndType = /* @__PURE__ */ new Map();
1410
+ for (const predicate2 of ast.predicates) {
1411
+ const key = buildPredicateIndexKey(
1412
+ predicate2.targetAlias,
1413
+ resolvePredicateTargetType(predicate2)
1472
1414
  );
1415
+ const existing = byAliasAndType.get(key);
1416
+ if (existing === void 0) {
1417
+ byAliasAndType.set(key, [predicate2]);
1418
+ } else {
1419
+ existing.push(predicate2);
1420
+ }
1473
1421
  }
1474
- const vectorPredicate = vectorPredicates[0];
1475
- if (vectorPredicate === void 0) {
1476
- return { vectorPredicate: void 0 };
1422
+ return { byAliasAndType };
1423
+ }
1424
+ function getPredicatesForAlias(predicateIndex, alias, targetType) {
1425
+ return predicateIndex.byAliasAndType.get(
1426
+ buildPredicateIndexKey(alias, targetType)
1427
+ ) ?? EMPTY_PREDICATES;
1428
+ }
1429
+ function compilePredicateClauses(predicates, predicateContext) {
1430
+ return predicates.map(
1431
+ (predicate2) => compilePredicateExpression(predicate2.expression, predicateContext)
1432
+ );
1433
+ }
1434
+ function compileKindFilter(column, kinds) {
1435
+ if (kinds.length === 0) {
1436
+ return drizzleOrm.sql`1 = 0`;
1477
1437
  }
1478
- const vectorStrategy = dialect.capabilities.vectorPredicateStrategy;
1479
- if (vectorStrategy === "unsupported" || !dialect.supportsVectors) {
1480
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
1481
- `Vector similarity predicates are not supported for dialect "${dialect.name}"`
1482
- );
1438
+ if (kinds.length === 1) {
1439
+ return drizzleOrm.sql`${column} = ${kinds[0]}`;
1483
1440
  }
1484
- if (!dialect.capabilities.vectorMetrics.includes(vectorPredicate.metric)) {
1485
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
1486
- `Vector metric "${vectorPredicate.metric}" is not supported for dialect "${dialect.name}"`
1487
- );
1441
+ return drizzleOrm.sql`${column} IN (${drizzleOrm.sql.join(
1442
+ kinds.map((kind) => drizzleOrm.sql`${kind}`),
1443
+ drizzleOrm.sql`, `
1444
+ )})`;
1445
+ }
1446
+ function getNodeKindsForAlias(ast, alias) {
1447
+ if (alias === ast.start.alias) {
1448
+ return ast.start.kinds;
1488
1449
  }
1489
- if (!Number.isFinite(vectorPredicate.limit) || vectorPredicate.limit <= 0) {
1490
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
1491
- `Vector predicate limit must be a positive finite number, got ${String(vectorPredicate.limit)}`
1492
- );
1450
+ for (const traversal of ast.traversals) {
1451
+ if (traversal.nodeAlias === alias) {
1452
+ return traversal.nodeKinds;
1453
+ }
1493
1454
  }
1494
- const { minScore } = vectorPredicate;
1495
- if (minScore !== void 0) {
1496
- if (!Number.isFinite(minScore)) {
1497
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
1498
- `Vector minScore must be a finite number, got ${String(minScore)}`
1499
- );
1455
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(`Unknown traversal source alias: ${alias}`);
1456
+ }
1457
+
1458
+ // src/query/compiler/emitter/plan-inspector.ts
1459
+ function collectPlanOperations(node, ops) {
1460
+ ops.add(node.op);
1461
+ switch (node.op) {
1462
+ case "aggregate":
1463
+ case "filter":
1464
+ case "join":
1465
+ case "limit_offset":
1466
+ case "project":
1467
+ case "recursive_expand":
1468
+ case "sort":
1469
+ case "vector_knn": {
1470
+ collectPlanOperations(node.input, ops);
1471
+ return;
1500
1472
  }
1501
- if (vectorPredicate.metric === "cosine" && (minScore < -1 || minScore > 1)) {
1502
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
1503
- `Cosine minScore must be between -1 and 1, got ${String(minScore)}`
1504
- );
1473
+ case "set_op": {
1474
+ collectPlanOperations(node.left, ops);
1475
+ collectPlanOperations(node.right, ops);
1476
+ return;
1477
+ }
1478
+ case "scan": {
1479
+ return;
1505
1480
  }
1506
1481
  }
1507
- return { vectorPredicate };
1508
1482
  }
1509
- function resolveVectorAwareLimit(astLimit, vectorPredicate) {
1510
- if (vectorPredicate === void 0) {
1511
- return astLimit;
1512
- }
1513
- if (astLimit === void 0) {
1514
- return vectorPredicate.limit;
1483
+ function findUnaryNodeInProjectChain(rootNode, op) {
1484
+ let currentNode = rootNode.input;
1485
+ for (; ; ) {
1486
+ if (currentNode.op === op) {
1487
+ return currentNode;
1488
+ }
1489
+ switch (currentNode.op) {
1490
+ case "aggregate":
1491
+ case "filter":
1492
+ case "join":
1493
+ case "limit_offset":
1494
+ case "recursive_expand":
1495
+ case "sort":
1496
+ case "vector_knn": {
1497
+ currentNode = currentNode.input;
1498
+ continue;
1499
+ }
1500
+ case "project":
1501
+ case "scan":
1502
+ case "set_op": {
1503
+ return void 0;
1504
+ }
1505
+ }
1515
1506
  }
1516
- return Math.min(astLimit, vectorPredicate.limit);
1517
1507
  }
1518
-
1519
- // src/query/compiler/plan/lowering.ts
1520
- function createPlanNodeIdFactory() {
1521
- let current = 0;
1522
- return function nextPlanNodeId() {
1523
- current += 1;
1524
- return `plan_${current.toString(36)}`;
1508
+ function inspectProjectPlan(logicalPlan) {
1509
+ if (logicalPlan.root.op !== "project") {
1510
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1511
+ `SQL emitter expected logical plan root to be "project", got "${logicalPlan.root.op}"`,
1512
+ { component: "plan-inspector" }
1513
+ );
1514
+ }
1515
+ const operations = /* @__PURE__ */ new Set();
1516
+ collectPlanOperations(logicalPlan.root, operations);
1517
+ const limitOffsetNode = findUnaryNodeInProjectChain(
1518
+ logicalPlan.root,
1519
+ "limit_offset"
1520
+ );
1521
+ const sortNode = findUnaryNodeInProjectChain(
1522
+ logicalPlan.root,
1523
+ "sort"
1524
+ );
1525
+ return {
1526
+ hasAggregate: operations.has("aggregate"),
1527
+ hasLimitOffset: operations.has("limit_offset"),
1528
+ hasRecursiveExpand: operations.has("recursive_expand"),
1529
+ hasSetOperation: operations.has("set_op"),
1530
+ hasSort: operations.has("sort"),
1531
+ hasVectorKnn: operations.has("vector_knn"),
1532
+ limitOffsetNode,
1533
+ rootProjectNode: logicalPlan.root,
1534
+ sortNode
1525
1535
  };
1526
1536
  }
1527
- function extractAggregateExpressions(ast) {
1528
- const aggregates = [];
1529
- for (const field2 of ast.projection.fields) {
1530
- if ("__type" in field2.source && field2.source.__type === "aggregate") {
1531
- aggregates.push(field2.source);
1532
- }
1537
+ function inspectStandardProjectPlan(logicalPlan) {
1538
+ const shape = inspectProjectPlan(logicalPlan);
1539
+ if (shape.hasSetOperation) {
1540
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1541
+ 'Standard SQL emitter does not support plans containing "set_op" nodes',
1542
+ { component: "plan-inspector" }
1543
+ );
1533
1544
  }
1534
- return aggregates;
1545
+ if (shape.hasRecursiveExpand) {
1546
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1547
+ 'Standard SQL emitter does not support plans containing "recursive_expand" nodes',
1548
+ { component: "plan-inspector" }
1549
+ );
1550
+ }
1551
+ return shape;
1535
1552
  }
1536
- function getAliasPredicates(ast, alias, predicateTargetType) {
1537
- return ast.predicates.filter((predicate2) => {
1538
- const targetType = predicate2.targetType ?? "node";
1539
- return predicate2.targetAlias === alias && targetType === predicateTargetType;
1540
- });
1553
+ function inspectRecursiveProjectPlan(logicalPlan) {
1554
+ const shape = inspectProjectPlan(logicalPlan);
1555
+ if (!shape.hasRecursiveExpand) {
1556
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1557
+ 'Recursive SQL emitter expected logical plan to contain a "recursive_expand" node',
1558
+ { component: "plan-inspector" }
1559
+ );
1560
+ }
1561
+ if (shape.hasSetOperation) {
1562
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1563
+ 'Recursive SQL emitter does not support plans containing "set_op" nodes',
1564
+ { component: "plan-inspector" }
1565
+ );
1566
+ }
1567
+ return shape;
1541
1568
  }
1542
- function wrapWithAliasFilterNode(currentNode, ast, alias, predicateTargetType, nextPlanNodeId) {
1543
- const aliasPredicates = getAliasPredicates(ast, alias, predicateTargetType);
1544
- if (aliasPredicates.length === 0) {
1545
- return currentNode;
1569
+ function findTopLevelLimitOffsetNode(rootNode) {
1570
+ let currentNode = rootNode;
1571
+ for (; ; ) {
1572
+ if (currentNode.op === "limit_offset") {
1573
+ return currentNode;
1574
+ }
1575
+ switch (currentNode.op) {
1576
+ case "aggregate":
1577
+ case "filter":
1578
+ case "join":
1579
+ case "project":
1580
+ case "recursive_expand":
1581
+ case "sort":
1582
+ case "vector_knn": {
1583
+ currentNode = currentNode.input;
1584
+ continue;
1585
+ }
1586
+ case "scan":
1587
+ case "set_op": {
1588
+ return void 0;
1589
+ }
1590
+ }
1591
+ }
1592
+ }
1593
+ function findTopLevelSortNode(rootNode) {
1594
+ let currentNode = rootNode;
1595
+ for (; ; ) {
1596
+ if (currentNode.op === "sort") {
1597
+ return currentNode;
1598
+ }
1599
+ switch (currentNode.op) {
1600
+ case "aggregate":
1601
+ case "filter":
1602
+ case "join":
1603
+ case "limit_offset":
1604
+ case "project":
1605
+ case "recursive_expand":
1606
+ case "vector_knn": {
1607
+ currentNode = currentNode.input;
1608
+ continue;
1609
+ }
1610
+ case "scan":
1611
+ case "set_op": {
1612
+ return void 0;
1613
+ }
1614
+ }
1615
+ }
1616
+ }
1617
+ function inspectSetOperationPlan(logicalPlan) {
1618
+ const operations = /* @__PURE__ */ new Set();
1619
+ collectPlanOperations(logicalPlan.root, operations);
1620
+ if (!operations.has("set_op")) {
1621
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1622
+ 'Set-operation SQL emitter expected logical plan to contain a "set_op" node',
1623
+ { component: "plan-inspector" }
1624
+ );
1546
1625
  }
1626
+ const limitOffsetNode = findTopLevelLimitOffsetNode(logicalPlan.root);
1627
+ const sortNode = findTopLevelSortNode(logicalPlan.root);
1547
1628
  return {
1548
- alias,
1549
- id: nextPlanNodeId(),
1550
- input: currentNode,
1551
- op: "filter",
1552
- predicateTargetType,
1553
- predicates: aliasPredicates.map((predicate2) => predicate2.expression)
1629
+ hasLimitOffset: limitOffsetNode !== void 0,
1630
+ hasSetOperation: true,
1631
+ hasSort: sortNode !== void 0,
1632
+ limitOffsetNode,
1633
+ sortNode
1554
1634
  };
1555
1635
  }
1556
- function appendAggregateSortLimitAndProjectNodes(currentNode, ast, nextPlanNodeId, limit, collapsedTraversalCteAlias) {
1557
- let node = currentNode;
1558
- const aggregateExpressions = extractAggregateExpressions(ast);
1559
- if (aggregateExpressions.length > 0 || ast.groupBy !== void 0 || ast.having !== void 0) {
1560
- const aggregateNode = {
1561
- aggregates: aggregateExpressions,
1562
- groupBy: ast.groupBy?.fields ?? [],
1563
- id: nextPlanNodeId(),
1564
- input: node,
1565
- op: "aggregate"
1566
- };
1567
- node = ast.having === void 0 ? aggregateNode : { ...aggregateNode, having: ast.having };
1636
+ function assertRecursiveEmitterClauseAlignment(logicalPlan, input) {
1637
+ const planShape = inspectRecursiveProjectPlan(logicalPlan);
1638
+ if (planShape.hasSort && input.orderBy === void 0) {
1639
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1640
+ "Recursive SQL emitter expected ORDER BY clause for plan containing a sort node",
1641
+ { component: "recursive-emitter" }
1642
+ );
1568
1643
  }
1569
- if (ast.orderBy !== void 0 && ast.orderBy.length > 0) {
1570
- node = {
1571
- id: nextPlanNodeId(),
1572
- input: node,
1573
- op: "sort",
1574
- orderBy: ast.orderBy
1575
- };
1644
+ if (!planShape.hasSort && input.orderBy !== void 0) {
1645
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1646
+ "Recursive SQL emitter received ORDER BY clause for a plan without sort nodes",
1647
+ { component: "recursive-emitter" }
1648
+ );
1576
1649
  }
1577
- if (limit !== void 0 || ast.offset !== void 0) {
1578
- const limitOffsetNodeBase = {
1579
- id: nextPlanNodeId(),
1580
- input: node,
1581
- op: "limit_offset"
1582
- };
1583
- const hasLimit = limit !== void 0;
1584
- const hasOffset = ast.offset !== void 0;
1585
- if (hasLimit && hasOffset) {
1586
- node = {
1587
- ...limitOffsetNodeBase,
1588
- limit,
1589
- offset: ast.offset
1590
- };
1591
- } else if (hasLimit) {
1592
- node = { ...limitOffsetNodeBase, limit };
1593
- } else if (hasOffset) {
1594
- node = { ...limitOffsetNodeBase, offset: ast.offset };
1595
- } else {
1650
+ if (planShape.hasLimitOffset && input.limitOffset === void 0) {
1651
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1652
+ "Recursive SQL emitter expected LIMIT/OFFSET clause for plan containing a limit_offset node",
1653
+ { component: "recursive-emitter" }
1654
+ );
1655
+ }
1656
+ if (!planShape.hasLimitOffset && input.limitOffset !== void 0) {
1657
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1658
+ "Recursive SQL emitter received LIMIT/OFFSET clause for a plan without limit_offset nodes",
1659
+ { component: "recursive-emitter" }
1660
+ );
1661
+ }
1662
+ }
1663
+ function emitRecursiveQuerySql(input) {
1664
+ assertRecursiveEmitterClauseAlignment(input.logicalPlan, input);
1665
+ const parts = [
1666
+ drizzleOrm.sql`WITH RECURSIVE`,
1667
+ input.recursiveCte,
1668
+ drizzleOrm.sql`SELECT ${input.projection}`,
1669
+ drizzleOrm.sql`FROM recursive_cte`,
1670
+ input.depthFilter
1671
+ ];
1672
+ if (input.orderBy !== void 0) {
1673
+ parts.push(input.orderBy);
1674
+ }
1675
+ if (input.limitOffset !== void 0) {
1676
+ parts.push(input.limitOffset);
1677
+ }
1678
+ return drizzleOrm.sql.join(parts, drizzleOrm.sql` `);
1679
+ }
1680
+ function assertSetOperationEmitterClauseAlignment(logicalPlan, suffixClauses) {
1681
+ const shape = inspectSetOperationPlan(logicalPlan);
1682
+ const hasSuffixClauses = suffixClauses !== void 0 && suffixClauses.length > 0;
1683
+ if (!shape.hasSort && !shape.hasLimitOffset && hasSuffixClauses) {
1684
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1685
+ "Set-operation SQL emitter received suffix clauses for a plan without top-level sort or limit_offset nodes",
1686
+ { component: "set-operation-emitter" }
1687
+ );
1688
+ }
1689
+ if (!hasSuffixClauses) {
1690
+ if (shape.hasSort || shape.hasLimitOffset) {
1596
1691
  throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1597
- "limit_offset node requires limit or offset to be present"
1692
+ "Set-operation SQL emitter expected suffix clauses for plan containing top-level sort or limit_offset nodes",
1693
+ { component: "set-operation-emitter" }
1598
1694
  );
1599
1695
  }
1696
+ return;
1600
1697
  }
1601
- const projectNodeBase = {
1602
- fields: ast.projection.fields,
1603
- id: nextPlanNodeId(),
1604
- input: node,
1605
- op: "project"
1606
- };
1607
- return collapsedTraversalCteAlias === void 0 ? projectNodeBase : {
1608
- ...projectNodeBase,
1609
- collapsedTraversalAlias: collapsedTraversalCteAlias
1610
- };
1611
- }
1612
- function lowerStandardQueryToLogicalPlanNode(input) {
1613
- const { ast, nextPlanNodeId } = input;
1614
- let currentNode = {
1615
- alias: ast.start.alias,
1616
- graphId: input.graphId,
1617
- id: nextPlanNodeId(),
1618
- kinds: ast.start.kinds,
1619
- op: "scan",
1620
- source: "nodes"
1621
- };
1622
- currentNode = wrapWithAliasFilterNode(
1623
- currentNode,
1624
- ast,
1625
- ast.start.alias,
1626
- "node",
1627
- nextPlanNodeId
1628
- );
1629
- for (const traversal of ast.traversals) {
1630
- currentNode = {
1631
- direction: traversal.direction,
1632
- edgeAlias: traversal.edgeAlias,
1633
- edgeKinds: traversal.edgeKinds,
1634
- id: nextPlanNodeId(),
1635
- input: currentNode,
1636
- inverseEdgeKinds: traversal.inverseEdgeKinds ?? [],
1637
- joinFromAlias: traversal.joinFromAlias,
1638
- joinType: traversal.optional ? "left" : "inner",
1639
- nodeAlias: traversal.nodeAlias,
1640
- nodeKinds: traversal.nodeKinds,
1641
- op: "join"
1642
- };
1643
- currentNode = wrapWithAliasFilterNode(
1644
- currentNode,
1645
- ast,
1646
- traversal.edgeAlias,
1647
- "edge",
1648
- nextPlanNodeId
1649
- );
1650
- currentNode = wrapWithAliasFilterNode(
1651
- currentNode,
1652
- ast,
1653
- traversal.nodeAlias,
1654
- "node",
1655
- nextPlanNodeId
1698
+ const limitOffsetClauseCount = shape.limitOffsetNode === void 0 ? 0 : (shape.limitOffsetNode.limit === void 0 ? 0 : 1) + (shape.limitOffsetNode.offset === void 0 ? 0 : 1);
1699
+ const expectedClauseCount = (shape.sortNode === void 0 ? 0 : 1) + limitOffsetClauseCount;
1700
+ if (suffixClauses.length !== expectedClauseCount) {
1701
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1702
+ `Set-operation SQL emitter expected ${String(expectedClauseCount)} top-level suffix clause(s) from logical plan, got ${String(suffixClauses.length)}`,
1703
+ { component: "set-operation-emitter" }
1656
1704
  );
1657
1705
  }
1658
- if (input.vectorPredicate !== void 0) {
1659
- currentNode = {
1660
- id: nextPlanNodeId(),
1661
- input: currentNode,
1662
- op: "vector_knn",
1663
- predicate: input.vectorPredicate
1664
- };
1665
- }
1666
- return appendAggregateSortLimitAndProjectNodes(
1667
- currentNode,
1668
- ast,
1669
- nextPlanNodeId,
1670
- input.effectiveLimit,
1671
- input.collapsedTraversalCteAlias
1672
- );
1673
1706
  }
1674
- function lowerRecursiveQueryToLogicalPlanNode(input) {
1675
- const { ast, nextPlanNodeId } = input;
1676
- const traversal = input.traversal ?? runRecursiveTraversalSelectionPass(input.ast);
1677
- let currentNode = {
1678
- alias: ast.start.alias,
1679
- graphId: input.graphId,
1680
- id: nextPlanNodeId(),
1681
- kinds: ast.start.kinds,
1682
- op: "scan",
1683
- source: "nodes"
1684
- };
1685
- currentNode = wrapWithAliasFilterNode(
1686
- currentNode,
1687
- ast,
1688
- ast.start.alias,
1689
- "node",
1690
- nextPlanNodeId
1691
- );
1692
- currentNode = {
1693
- edgeAlias: traversal.edgeAlias,
1694
- edgeKinds: traversal.edgeKinds,
1695
- id: nextPlanNodeId(),
1696
- input: currentNode,
1697
- inverseEdgeKinds: traversal.inverseEdgeKinds ?? [],
1698
- nodeAlias: traversal.nodeAlias,
1699
- nodeKinds: traversal.nodeKinds,
1700
- op: "recursive_expand",
1701
- traversal: traversal.variableLength
1702
- };
1703
- currentNode = wrapWithAliasFilterNode(
1704
- currentNode,
1705
- ast,
1706
- traversal.edgeAlias,
1707
- "edge",
1708
- nextPlanNodeId
1709
- );
1710
- currentNode = wrapWithAliasFilterNode(
1711
- currentNode,
1712
- ast,
1713
- traversal.nodeAlias,
1714
- "node",
1715
- nextPlanNodeId
1716
- );
1717
- return appendAggregateSortLimitAndProjectNodes(
1718
- currentNode,
1719
- ast,
1720
- nextPlanNodeId,
1721
- ast.limit
1707
+ function emitSetOperationQuerySql(input) {
1708
+ assertSetOperationEmitterClauseAlignment(
1709
+ input.logicalPlan,
1710
+ input.suffixClauses
1722
1711
  );
1712
+ const parts = [];
1713
+ if (input.ctes !== void 0 && input.ctes.length > 0) {
1714
+ parts.push(drizzleOrm.sql`WITH ${drizzleOrm.sql.join([...input.ctes], drizzleOrm.sql`, `)}`);
1715
+ }
1716
+ parts.push(input.baseQuery);
1717
+ if (input.suffixClauses !== void 0 && input.suffixClauses.length > 0) {
1718
+ parts.push(...input.suffixClauses);
1719
+ }
1720
+ return drizzleOrm.sql.join(parts, drizzleOrm.sql` `);
1723
1721
  }
1724
- function lowerComposableQueryToLogicalPlanNode(query, dialect, graphId, nextPlanNodeId) {
1725
- if ("__type" in query) {
1726
- return lowerSetOperationToLogicalPlanNode(
1727
- query,
1728
- graphId,
1729
- dialect,
1730
- nextPlanNodeId
1722
+ function assertStandardEmitterClauseAlignment(logicalPlan, input) {
1723
+ const planShape = inspectStandardProjectPlan(logicalPlan);
1724
+ if (input.groupBy !== void 0 && !planShape.hasAggregate) {
1725
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1726
+ "Standard SQL emitter received GROUP BY clause for a plan without aggregate nodes",
1727
+ { component: "standard-emitter" }
1731
1728
  );
1732
1729
  }
1733
- const hasVariableLengthTraversal2 = query.traversals.some(
1734
- (traversal) => traversal.variableLength !== void 0
1735
- );
1736
- if (hasVariableLengthTraversal2) {
1737
- return lowerRecursiveQueryToLogicalPlanNode({
1738
- ast: query,
1739
- graphId,
1740
- nextPlanNodeId
1741
- });
1730
+ if (input.having !== void 0 && !planShape.hasAggregate) {
1731
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1732
+ "Standard SQL emitter received HAVING clause for a plan without aggregate nodes",
1733
+ { component: "standard-emitter" }
1734
+ );
1742
1735
  }
1743
- const vectorPredicate = runVectorPredicatePass(
1744
- query,
1745
- chunk2WVFEIHR_cjs.getDialect(dialect)
1746
- ).vectorPredicate;
1747
- const effectiveLimit = resolveVectorAwareLimit(query.limit, vectorPredicate);
1748
- const loweringInput = {
1749
- ast: query,
1750
- graphId,
1751
- nextPlanNodeId,
1752
- ...effectiveLimit === void 0 ? {} : { effectiveLimit },
1753
- ...vectorPredicate === void 0 ? {} : { vectorPredicate }
1754
- };
1755
- return lowerStandardQueryToLogicalPlanNode(loweringInput);
1756
- }
1757
- function lowerSetOperationToLogicalPlanNode(op, graphId, dialect, nextPlanNodeId) {
1758
- let currentNode = {
1759
- id: nextPlanNodeId(),
1760
- left: lowerComposableQueryToLogicalPlanNode(
1761
- op.left,
1762
- dialect,
1763
- graphId,
1764
- nextPlanNodeId
1765
- ),
1766
- op: "set_op",
1767
- operator: op.operator,
1768
- right: lowerComposableQueryToLogicalPlanNode(
1769
- op.right,
1770
- dialect,
1771
- graphId,
1772
- nextPlanNodeId
1773
- )
1774
- };
1775
- if (op.orderBy !== void 0 && op.orderBy.length > 0) {
1776
- currentNode = {
1777
- id: nextPlanNodeId(),
1778
- input: currentNode,
1779
- op: "sort",
1780
- orderBy: op.orderBy
1781
- };
1736
+ const expectsOrderBy = planShape.hasSort || planShape.hasVectorKnn;
1737
+ if (expectsOrderBy && input.orderBy === void 0) {
1738
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1739
+ "Standard SQL emitter expected ORDER BY clause for plan containing a sort or vector_knn node",
1740
+ { component: "standard-emitter" }
1741
+ );
1782
1742
  }
1783
- if (op.limit !== void 0 || op.offset !== void 0) {
1784
- const limitOffsetBase = {
1785
- id: nextPlanNodeId(),
1786
- input: currentNode,
1787
- op: "limit_offset"
1788
- };
1789
- if (op.limit !== void 0 && op.offset !== void 0) {
1790
- currentNode = { ...limitOffsetBase, limit: op.limit, offset: op.offset };
1791
- } else if (op.limit === void 0) {
1792
- currentNode = { ...limitOffsetBase, offset: op.offset };
1793
- } else {
1794
- currentNode = { ...limitOffsetBase, limit: op.limit };
1795
- }
1743
+ if (!expectsOrderBy && input.orderBy !== void 0) {
1744
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1745
+ "Standard SQL emitter received ORDER BY clause for a plan without sort or vector_knn nodes",
1746
+ { component: "standard-emitter" }
1747
+ );
1748
+ }
1749
+ if (planShape.hasLimitOffset && input.limitOffset === void 0) {
1750
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1751
+ "Standard SQL emitter expected LIMIT/OFFSET clause for plan containing a limit_offset node",
1752
+ { component: "standard-emitter" }
1753
+ );
1754
+ }
1755
+ if (!planShape.hasLimitOffset && input.limitOffset !== void 0) {
1756
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1757
+ "Standard SQL emitter received LIMIT/OFFSET clause for a plan without limit_offset nodes",
1758
+ { component: "standard-emitter" }
1759
+ );
1796
1760
  }
1797
- return currentNode;
1798
- }
1799
- function lowerStandardQueryToLogicalPlan(input) {
1800
- const nextPlanNodeId = createPlanNodeIdFactory();
1801
- return {
1802
- metadata: {
1803
- dialect: input.dialect,
1804
- graphId: input.graphId
1805
- },
1806
- root: lowerStandardQueryToLogicalPlanNode({
1807
- ...input,
1808
- nextPlanNodeId
1809
- })
1810
- };
1811
- }
1812
- function lowerRecursiveQueryToLogicalPlan(input) {
1813
- const nextPlanNodeId = createPlanNodeIdFactory();
1814
- return {
1815
- metadata: {
1816
- dialect: input.dialect,
1817
- graphId: input.graphId
1818
- },
1819
- root: lowerRecursiveQueryToLogicalPlanNode({
1820
- ...input,
1821
- nextPlanNodeId
1822
- })
1823
- };
1824
1761
  }
1825
- function lowerSetOperationToLogicalPlan(input) {
1826
- const nextPlanNodeId = createPlanNodeIdFactory();
1827
- return {
1828
- metadata: {
1829
- dialect: input.dialect,
1830
- graphId: input.graphId
1831
- },
1832
- root: lowerSetOperationToLogicalPlanNode(
1833
- input.op,
1834
- input.graphId,
1835
- input.dialect,
1836
- nextPlanNodeId
1837
- )
1838
- };
1762
+ function emitStandardQuerySql(input) {
1763
+ assertStandardEmitterClauseAlignment(input.logicalPlan, input);
1764
+ const parts = [];
1765
+ if (input.ctes.length > 0) {
1766
+ parts.push(drizzleOrm.sql`WITH ${drizzleOrm.sql.join([...input.ctes], drizzleOrm.sql`, `)}`);
1767
+ }
1768
+ parts.push(drizzleOrm.sql`SELECT ${input.projection}`, input.fromClause);
1769
+ if (input.groupBy !== void 0) {
1770
+ parts.push(input.groupBy);
1771
+ }
1772
+ if (input.having !== void 0) {
1773
+ parts.push(input.having);
1774
+ }
1775
+ if (input.orderBy !== void 0) {
1776
+ parts.push(input.orderBy);
1777
+ }
1778
+ if (input.limitOffset !== void 0) {
1779
+ parts.push(input.limitOffset);
1780
+ }
1781
+ return drizzleOrm.sql.join(parts, drizzleOrm.sql` `);
1839
1782
  }
1840
1783
 
1841
- // src/query/compiler/emitter/plan-inspector.ts
1842
- function collectPlanOperations(node, ops) {
1843
- ops.add(node.op);
1844
- switch (node.op) {
1845
- case "aggregate":
1846
- case "filter":
1847
- case "join":
1848
- case "limit_offset":
1849
- case "project":
1850
- case "recursive_expand":
1851
- case "sort":
1852
- case "vector_knn": {
1853
- collectPlanOperations(node.input, ops);
1854
- return;
1784
+ // src/query/compiler/typed-json-extract.ts
1785
+ function compileTypedJsonExtract(input) {
1786
+ const { column, dialect, pointer, valueType } = input;
1787
+ const fallback = input.fallback ?? "json";
1788
+ switch (valueType) {
1789
+ case "string": {
1790
+ return dialect.jsonExtractText(column, pointer);
1855
1791
  }
1856
- case "set_op": {
1857
- collectPlanOperations(node.left, ops);
1858
- collectPlanOperations(node.right, ops);
1859
- return;
1792
+ case "number": {
1793
+ return dialect.jsonExtractNumber(column, pointer);
1860
1794
  }
1861
- case "scan": {
1862
- return;
1795
+ case "boolean": {
1796
+ return dialect.jsonExtractBoolean(column, pointer);
1863
1797
  }
1864
- }
1865
- }
1866
- function findUnaryNodeInProjectChain(rootNode, op) {
1867
- let currentNode = rootNode.input;
1868
- for (; ; ) {
1869
- if (currentNode.op === op) {
1870
- return currentNode;
1798
+ case "date": {
1799
+ return dialect.jsonExtractDate(column, pointer);
1871
1800
  }
1872
- switch (currentNode.op) {
1873
- case "aggregate":
1874
- case "filter":
1875
- case "join":
1876
- case "limit_offset":
1877
- case "recursive_expand":
1878
- case "sort":
1879
- case "vector_knn": {
1880
- currentNode = currentNode.input;
1881
- continue;
1882
- }
1883
- case "project":
1884
- case "scan":
1885
- case "set_op": {
1886
- return void 0;
1887
- }
1801
+ case "array":
1802
+ case "object":
1803
+ case "embedding":
1804
+ case "unknown":
1805
+ case void 0: {
1806
+ return fallback === "text" ? dialect.jsonExtractText(column, pointer) : dialect.jsonExtract(column, pointer);
1807
+ }
1808
+ default: {
1809
+ return fallback === "text" ? dialect.jsonExtractText(column, pointer) : dialect.jsonExtract(column, pointer);
1888
1810
  }
1889
1811
  }
1890
1812
  }
1891
- function inspectProjectPlan(logicalPlan) {
1892
- if (logicalPlan.root.op !== "project") {
1893
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1894
- `SQL emitter expected logical plan root to be "project", got "${logicalPlan.root.op}"`,
1895
- { component: "plan-inspector" }
1896
- );
1897
- }
1898
- const operations = /* @__PURE__ */ new Set();
1899
- collectPlanOperations(logicalPlan.root, operations);
1900
- const limitOffsetNode = findUnaryNodeInProjectChain(
1901
- logicalPlan.root,
1902
- "limit_offset"
1903
- );
1904
- const sortNode = findUnaryNodeInProjectChain(
1905
- logicalPlan.root,
1906
- "sort"
1907
- );
1908
- return {
1909
- hasAggregate: operations.has("aggregate"),
1910
- hasLimitOffset: operations.has("limit_offset"),
1911
- hasRecursiveExpand: operations.has("recursive_expand"),
1912
- hasSetOperation: operations.has("set_op"),
1913
- hasSort: operations.has("sort"),
1914
- hasVectorKnn: operations.has("vector_knn"),
1915
- limitOffsetNode,
1916
- rootProjectNode: logicalPlan.root,
1917
- sortNode
1918
- };
1813
+ var NODE_COLUMNS = [
1814
+ "id",
1815
+ "kind",
1816
+ "props",
1817
+ "version",
1818
+ "valid_from",
1819
+ "valid_to",
1820
+ "created_at",
1821
+ "updated_at",
1822
+ "deleted_at"
1823
+ ];
1824
+ var EDGE_COLUMNS = [
1825
+ "id",
1826
+ "kind",
1827
+ "from_id",
1828
+ "to_id",
1829
+ "props",
1830
+ "valid_from",
1831
+ "valid_to",
1832
+ "created_at",
1833
+ "updated_at",
1834
+ "deleted_at"
1835
+ ];
1836
+ var EMPTY_REQUIRED_COLUMNS = /* @__PURE__ */ new Set();
1837
+ function quoteIdentifier(identifier) {
1838
+ return drizzleOrm.sql.raw(`"${identifier.replaceAll('"', '""')}"`);
1919
1839
  }
1920
- function inspectStandardProjectPlan(logicalPlan) {
1921
- const shape = inspectProjectPlan(logicalPlan);
1922
- if (shape.hasSetOperation) {
1923
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1924
- 'Standard SQL emitter does not support plans containing "set_op" nodes',
1925
- { component: "plan-inspector" }
1926
- );
1927
- }
1928
- if (shape.hasRecursiveExpand) {
1929
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1930
- 'Standard SQL emitter does not support plans containing "recursive_expand" nodes',
1931
- { component: "plan-inspector" }
1932
- );
1933
- }
1934
- return shape;
1840
+ function shouldProjectColumn(requiredColumns, column, alwaysRequiredColumns) {
1841
+ if (alwaysRequiredColumns?.has(column)) return true;
1842
+ if (requiredColumns === void 0) return true;
1843
+ return requiredColumns.has(column);
1935
1844
  }
1936
- function inspectRecursiveProjectPlan(logicalPlan) {
1937
- const shape = inspectProjectPlan(logicalPlan);
1938
- if (!shape.hasRecursiveExpand) {
1939
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1940
- 'Recursive SQL emitter expected logical plan to contain a "recursive_expand" node',
1941
- { component: "plan-inspector" }
1942
- );
1943
- }
1944
- if (shape.hasSetOperation) {
1945
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
1946
- 'Recursive SQL emitter does not support plans containing "set_op" nodes',
1947
- { component: "plan-inspector" }
1948
- );
1845
+ function addRequiredColumn(requiredColumnsByAlias, alias, column) {
1846
+ const existing = requiredColumnsByAlias.get(alias);
1847
+ if (existing) {
1848
+ existing.add(column);
1849
+ return;
1949
1850
  }
1950
- return shape;
1851
+ requiredColumnsByAlias.set(alias, /* @__PURE__ */ new Set([column]));
1951
1852
  }
1952
- function findTopLevelLimitOffsetNode(rootNode) {
1953
- let currentNode = rootNode;
1954
- for (; ; ) {
1955
- if (currentNode.op === "limit_offset") {
1956
- return currentNode;
1957
- }
1958
- switch (currentNode.op) {
1959
- case "aggregate":
1960
- case "filter":
1961
- case "join":
1962
- case "project":
1963
- case "recursive_expand":
1964
- case "sort":
1965
- case "vector_knn": {
1966
- currentNode = currentNode.input;
1967
- continue;
1968
- }
1969
- case "scan":
1970
- case "set_op": {
1971
- return void 0;
1972
- }
1973
- }
1974
- }
1853
+ function markFieldRefAsRequired(requiredColumnsByAlias, field2) {
1854
+ const column = field2.path[0];
1855
+ if (column === void 0) return;
1856
+ addRequiredColumn(requiredColumnsByAlias, field2.alias, column);
1975
1857
  }
1976
- function findTopLevelSortNode(rootNode) {
1977
- let currentNode = rootNode;
1978
- for (; ; ) {
1979
- if (currentNode.op === "sort") {
1980
- return currentNode;
1981
- }
1982
- switch (currentNode.op) {
1983
- case "aggregate":
1984
- case "filter":
1985
- case "join":
1986
- case "limit_offset":
1987
- case "project":
1988
- case "recursive_expand":
1989
- case "vector_knn": {
1990
- currentNode = currentNode.input;
1991
- continue;
1992
- }
1993
- case "scan":
1994
- case "set_op": {
1995
- return void 0;
1996
- }
1997
- }
1858
+ function mapSelectiveSystemFieldToColumn(field2) {
1859
+ if (field2 === "fromId") return "from_id";
1860
+ if (field2 === "toId") return "to_id";
1861
+ if (field2.startsWith("meta.")) {
1862
+ return field2.slice(5).replaceAll(/([A-Z])/g, "_$1").toLowerCase();
1998
1863
  }
1864
+ return field2;
1999
1865
  }
2000
- function inspectSetOperationPlan(logicalPlan) {
2001
- const operations = /* @__PURE__ */ new Set();
2002
- collectPlanOperations(logicalPlan.root, operations);
2003
- if (!operations.has("set_op")) {
2004
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2005
- 'Set-operation SQL emitter expected logical plan to contain a "set_op" node',
2006
- { component: "plan-inspector" }
1866
+ function markSelectiveFieldAsRequired(requiredColumnsByAlias, field2) {
1867
+ if (field2.isSystemField) {
1868
+ addRequiredColumn(
1869
+ requiredColumnsByAlias,
1870
+ field2.alias,
1871
+ mapSelectiveSystemFieldToColumn(field2.field)
2007
1872
  );
1873
+ return;
2008
1874
  }
2009
- const limitOffsetNode = findTopLevelLimitOffsetNode(logicalPlan.root);
2010
- const sortNode = findTopLevelSortNode(logicalPlan.root);
2011
- return {
2012
- hasLimitOffset: limitOffsetNode !== void 0,
2013
- hasSetOperation: true,
2014
- hasSort: sortNode !== void 0,
2015
- limitOffsetNode,
2016
- sortNode
2017
- };
1875
+ addRequiredColumn(requiredColumnsByAlias, field2.alias, "props");
2018
1876
  }
2019
- function assertRecursiveEmitterClauseAlignment(logicalPlan, input) {
2020
- const planShape = inspectRecursiveProjectPlan(logicalPlan);
2021
- if (planShape.hasSort && input.orderBy === void 0) {
2022
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2023
- "Recursive SQL emitter expected ORDER BY clause for plan containing a sort node",
2024
- { component: "recursive-emitter" }
2025
- );
2026
- }
2027
- if (!planShape.hasSort && input.orderBy !== void 0) {
2028
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2029
- "Recursive SQL emitter received ORDER BY clause for a plan without sort nodes",
2030
- { component: "recursive-emitter" }
2031
- );
2032
- }
2033
- if (planShape.hasLimitOffset && input.limitOffset === void 0) {
2034
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2035
- "Recursive SQL emitter expected LIMIT/OFFSET clause for plan containing a limit_offset node",
2036
- { component: "recursive-emitter" }
2037
- );
2038
- }
2039
- if (!planShape.hasLimitOffset && input.limitOffset !== void 0) {
2040
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2041
- "Recursive SQL emitter received LIMIT/OFFSET clause for a plan without limit_offset nodes",
2042
- { component: "recursive-emitter" }
2043
- );
1877
+ function isIdFieldRef(field2) {
1878
+ return field2.path.length === 1 && field2.path[0] === "id" && field2.jsonPointer === void 0;
1879
+ }
1880
+ function isAggregateExpr(source) {
1881
+ return "__type" in source && source.__type === "aggregate";
1882
+ }
1883
+
1884
+ // src/query/compiler/emitter/standard-builders.ts
1885
+ function compileColumnReference(tableAlias, column) {
1886
+ if (tableAlias === void 0) {
1887
+ return drizzleOrm.sql.raw(column);
2044
1888
  }
1889
+ return drizzleOrm.sql`${drizzleOrm.sql.raw(tableAlias)}.${drizzleOrm.sql.raw(column)}`;
2045
1890
  }
2046
- function emitRecursiveQuerySql(input) {
2047
- assertRecursiveEmitterClauseAlignment(input.logicalPlan, input);
2048
- const parts = [
2049
- drizzleOrm.sql`WITH RECURSIVE`,
2050
- input.recursiveCte,
2051
- drizzleOrm.sql`SELECT ${input.projection}`,
2052
- drizzleOrm.sql`FROM recursive_cte`,
2053
- input.depthFilter
1891
+ function compileNodeSelectColumns(tableAlias, alias, requiredColumns) {
1892
+ return NODE_COLUMNS.filter(
1893
+ (column) => column === "id" || column === "kind" || shouldProjectColumn(requiredColumns, column)
1894
+ ).map(
1895
+ (column) => drizzleOrm.sql`${compileColumnReference(tableAlias, column)} AS ${drizzleOrm.sql.raw(`${alias}_${column}`)}`
1896
+ );
1897
+ }
1898
+ function compileEdgeSelectColumns(tableAlias, alias, requiredColumns) {
1899
+ return EDGE_COLUMNS.filter(
1900
+ (column) => shouldProjectColumn(requiredColumns, column)
1901
+ ).map(
1902
+ (column) => drizzleOrm.sql`${compileColumnReference(tableAlias, column)} AS ${drizzleOrm.sql.raw(`${alias}_${column}`)}`
1903
+ );
1904
+ }
1905
+ function buildStandardStartCte(input) {
1906
+ const { ast, ctx, graphId, predicateIndex, requiredColumnsByAlias } = input;
1907
+ const alias = ast.start.alias;
1908
+ const kinds = ast.start.kinds;
1909
+ const kindFilter = compileKindFilter(drizzleOrm.sql.raw("kind"), kinds);
1910
+ const temporalFilter = input.temporalFilterPass.forAlias();
1911
+ const cteContext = { ...ctx, cteColumnPrefix: "" };
1912
+ const predicateClauses = compilePredicateClauses(
1913
+ getPredicatesForAlias(predicateIndex, alias, "node"),
1914
+ cteContext
1915
+ );
1916
+ const whereClauses = [
1917
+ drizzleOrm.sql`graph_id = ${graphId}`,
1918
+ kindFilter,
1919
+ temporalFilter,
1920
+ ...predicateClauses
2054
1921
  ];
2055
- if (input.orderBy !== void 0) {
2056
- parts.push(input.orderBy);
1922
+ const effectiveRequiredColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(alias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
1923
+ return drizzleOrm.sql`
1924
+ cte_${drizzleOrm.sql.raw(alias)} AS (
1925
+ SELECT ${drizzleOrm.sql.join(
1926
+ compileNodeSelectColumns(void 0, alias, effectiveRequiredColumns),
1927
+ drizzleOrm.sql`, `
1928
+ )}
1929
+ FROM ${ctx.schema.nodesTable}
1930
+ WHERE ${drizzleOrm.sql.join(whereClauses, drizzleOrm.sql` AND `)}
1931
+ )
1932
+ `;
1933
+ }
1934
+ function buildStandardTraversalCte(input) {
1935
+ const {
1936
+ ast,
1937
+ carryForwardPreviousColumns,
1938
+ ctx,
1939
+ graphId,
1940
+ materializeCte,
1941
+ predicateIndex,
1942
+ requiredColumnsByAlias,
1943
+ temporalFilterPass,
1944
+ traversalIndex,
1945
+ traversalLimit
1946
+ } = input;
1947
+ const traversal = ast.traversals[traversalIndex];
1948
+ const traversalLimitValue = traversalIndex === ast.traversals.length - 1 ? traversalLimit : void 0;
1949
+ const previousNodeKinds = getNodeKindsForAlias(ast, traversal.joinFromAlias);
1950
+ const directEdgeKinds = [...new Set(traversal.edgeKinds)];
1951
+ const inverseEdgeKinds = traversal.inverseEdgeKinds === void 0 ? [] : [...new Set(traversal.inverseEdgeKinds)];
1952
+ const nodeKinds = traversal.nodeKinds;
1953
+ const nodeKindFilter = compileKindFilter(drizzleOrm.sql.raw("n.kind"), nodeKinds);
1954
+ const edgeTemporalFilter = temporalFilterPass.forAlias("e");
1955
+ const nodeTemporalFilter = temporalFilterPass.forAlias("n");
1956
+ const nodeCteContext = {
1957
+ ...ctx,
1958
+ cteColumnPrefix: "n"
1959
+ };
1960
+ const nodePredicateClauses = compilePredicateClauses(
1961
+ getPredicatesForAlias(predicateIndex, traversal.nodeAlias, "node"),
1962
+ nodeCteContext
1963
+ );
1964
+ const edgeCteContext = {
1965
+ ...ctx,
1966
+ cteColumnPrefix: "e"
1967
+ };
1968
+ const edgePredicateClauses = compilePredicateClauses(
1969
+ getPredicatesForAlias(predicateIndex, traversal.edgeAlias, "edge"),
1970
+ edgeCteContext
1971
+ );
1972
+ const baseWhereClauses = [
1973
+ drizzleOrm.sql`e.graph_id = ${graphId}`,
1974
+ nodeKindFilter,
1975
+ edgeTemporalFilter,
1976
+ nodeTemporalFilter,
1977
+ ...nodePredicateClauses,
1978
+ ...edgePredicateClauses
1979
+ ];
1980
+ const previousAlias = traversal.joinFromAlias;
1981
+ const edgeAlias = traversal.edgeAlias;
1982
+ const nodeAlias = traversal.nodeAlias;
1983
+ const requiredNodeColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(nodeAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
1984
+ const requiredEdgeColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(edgeAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
1985
+ const previousRowColumns = carryForwardPreviousColumns ? [drizzleOrm.sql`cte_${drizzleOrm.sql.raw(previousAlias)}.*`] : [
1986
+ drizzleOrm.sql`cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id AS ${drizzleOrm.sql.raw(previousAlias)}_id`,
1987
+ drizzleOrm.sql`cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind AS ${drizzleOrm.sql.raw(previousAlias)}_kind`
1988
+ ];
1989
+ const selectColumns = [
1990
+ ...previousRowColumns,
1991
+ ...compileEdgeSelectColumns("e", edgeAlias, requiredEdgeColumns),
1992
+ ...compileNodeSelectColumns("n", nodeAlias, requiredNodeColumns)
1993
+ ];
1994
+ const cteMaterialization = materializeCte ? drizzleOrm.sql`MATERIALIZED ` : drizzleOrm.sql``;
1995
+ function compileTraversalBranch(branch) {
1996
+ const whereClauses = [
1997
+ ...baseWhereClauses,
1998
+ compileKindFilter(drizzleOrm.sql.raw("e.kind"), branch.edgeKinds),
1999
+ compileKindFilter(
2000
+ drizzleOrm.sql.raw(`e.${branch.joinKindField}`),
2001
+ previousNodeKinds
2002
+ ),
2003
+ compileKindFilter(drizzleOrm.sql.raw(`e.${branch.targetKindField}`), nodeKinds)
2004
+ ];
2005
+ if (branch.duplicateGuard !== void 0) {
2006
+ whereClauses.push(branch.duplicateGuard);
2007
+ }
2008
+ return drizzleOrm.sql`
2009
+ SELECT ${drizzleOrm.sql.join(selectColumns, drizzleOrm.sql`, `)}
2010
+ FROM cte_${drizzleOrm.sql.raw(previousAlias)}
2011
+ JOIN ${ctx.schema.edgesTable} e ON cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id = e.${drizzleOrm.sql.raw(branch.joinField)}
2012
+ AND cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind = e.${drizzleOrm.sql.raw(branch.joinKindField)}
2013
+ JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
2014
+ AND n.id = e.${drizzleOrm.sql.raw(branch.targetField)}
2015
+ AND n.kind = e.${drizzleOrm.sql.raw(branch.targetKindField)}
2016
+ WHERE ${drizzleOrm.sql.join(whereClauses, drizzleOrm.sql` AND `)}
2017
+ `;
2057
2018
  }
2058
- if (input.limitOffset !== void 0) {
2059
- parts.push(input.limitOffset);
2019
+ const directJoinField = traversal.direction === "out" ? "from_id" : "to_id";
2020
+ const directTargetField = traversal.direction === "out" ? "to_id" : "from_id";
2021
+ const directJoinKindField = traversal.direction === "out" ? "from_kind" : "to_kind";
2022
+ const directTargetKindField = traversal.direction === "out" ? "to_kind" : "from_kind";
2023
+ const directBranch = compileTraversalBranch({
2024
+ edgeKinds: directEdgeKinds,
2025
+ joinField: directJoinField,
2026
+ joinKindField: directJoinKindField,
2027
+ targetField: directTargetField,
2028
+ targetKindField: directTargetKindField
2029
+ });
2030
+ if (inverseEdgeKinds.length === 0) {
2031
+ if (traversalLimitValue !== void 0) {
2032
+ return drizzleOrm.sql`
2033
+ cte_${drizzleOrm.sql.raw(nodeAlias)} AS ${cteMaterialization}(
2034
+ SELECT * FROM (
2035
+ ${directBranch}
2036
+ ) AS traversal_rows
2037
+ LIMIT ${traversalLimitValue}
2038
+ )
2039
+ `;
2040
+ }
2041
+ return drizzleOrm.sql`
2042
+ cte_${drizzleOrm.sql.raw(nodeAlias)} AS ${cteMaterialization}(
2043
+ ${directBranch}
2044
+ )
2045
+ `;
2046
+ }
2047
+ const inverseJoinField = traversal.direction === "out" ? "to_id" : "from_id";
2048
+ const inverseTargetField = traversal.direction === "out" ? "from_id" : "to_id";
2049
+ const inverseJoinKindField = traversal.direction === "out" ? "to_kind" : "from_kind";
2050
+ const inverseTargetKindField = traversal.direction === "out" ? "from_kind" : "to_kind";
2051
+ const overlappingKinds = inverseEdgeKinds.filter(
2052
+ (kind) => directEdgeKinds.includes(kind)
2053
+ );
2054
+ const duplicateGuard = overlappingKinds.length > 0 ? drizzleOrm.sql`NOT (e.from_id = e.to_id AND ${compileKindFilter(
2055
+ drizzleOrm.sql.raw("e.kind"),
2056
+ overlappingKinds
2057
+ )})` : void 0;
2058
+ const inverseBranch = compileTraversalBranch({
2059
+ duplicateGuard,
2060
+ edgeKinds: inverseEdgeKinds,
2061
+ joinField: inverseJoinField,
2062
+ joinKindField: inverseJoinKindField,
2063
+ targetField: inverseTargetField,
2064
+ targetKindField: inverseTargetKindField
2065
+ });
2066
+ if (traversalLimitValue !== void 0) {
2067
+ return drizzleOrm.sql`
2068
+ cte_${drizzleOrm.sql.raw(nodeAlias)} AS ${cteMaterialization}(
2069
+ SELECT * FROM (
2070
+ ${directBranch}
2071
+ UNION ALL
2072
+ ${inverseBranch}
2073
+ ) AS traversal_rows
2074
+ LIMIT ${traversalLimitValue}
2075
+ )
2076
+ `;
2077
+ }
2078
+ return drizzleOrm.sql`
2079
+ cte_${drizzleOrm.sql.raw(nodeAlias)} AS ${cteMaterialization}(
2080
+ ${directBranch}
2081
+ UNION ALL
2082
+ ${inverseBranch}
2083
+ )
2084
+ `;
2085
+ }
2086
+ function compileAggregateExprFromSource(expr, dialect) {
2087
+ const { field: field2 } = expr;
2088
+ const fn = expr.function;
2089
+ switch (fn) {
2090
+ case "count":
2091
+ case "countDistinct":
2092
+ case "sum":
2093
+ case "avg":
2094
+ case "min":
2095
+ case "max": {
2096
+ const cteAlias = `cte_${field2.alias}`;
2097
+ const column = compileFieldValue(
2098
+ field2,
2099
+ dialect,
2100
+ field2.valueType,
2101
+ cteAlias
2102
+ );
2103
+ if (fn === "countDistinct") {
2104
+ return drizzleOrm.sql`COUNT(DISTINCT ${column})`;
2105
+ }
2106
+ return drizzleOrm.sql`${drizzleOrm.sql.raw(fn.toUpperCase())}(${column})`;
2107
+ }
2108
+ default: {
2109
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2110
+ `Unknown aggregate function: ${String(fn)}`
2111
+ );
2112
+ }
2113
+ }
2114
+ }
2115
+ function compileProjectedSource(field2, dialect) {
2116
+ if (isAggregateExpr(field2.source)) {
2117
+ return compileAggregateExprFromSource(field2.source, dialect);
2118
+ }
2119
+ const cteAlias = field2.cteAlias ?? `cte_${field2.source.alias}`;
2120
+ return compileFieldValue(
2121
+ field2.source,
2122
+ dialect,
2123
+ field2.source.valueType,
2124
+ cteAlias
2125
+ );
2126
+ }
2127
+ function buildStandardProjection(input) {
2128
+ const { ast, collapsedTraversalCteAlias, dialect } = input;
2129
+ if (ast.selectiveFields && ast.selectiveFields.length > 0) {
2130
+ return compileSelectiveProjection(
2131
+ ast.selectiveFields,
2132
+ dialect,
2133
+ ast,
2134
+ collapsedTraversalCteAlias
2135
+ );
2136
+ }
2137
+ const fields = ast.projection.fields;
2138
+ if (fields.length === 0) {
2139
+ return drizzleOrm.sql.raw("*");
2140
+ }
2141
+ const projectedFields = fields.map((field2) => {
2142
+ const source = compileProjectedSource(field2, dialect);
2143
+ return drizzleOrm.sql`${source} AS ${quoteIdentifier(field2.outputName)}`;
2144
+ });
2145
+ return drizzleOrm.sql.join(projectedFields, drizzleOrm.sql`, `);
2146
+ }
2147
+ function compileSelectiveProjection(fields, dialect, ast, collapsedTraversalCteAlias) {
2148
+ const aliasToCte = /* @__PURE__ */ new Map([
2149
+ [ast.start.alias, `cte_${ast.start.alias}`]
2150
+ ]);
2151
+ for (const traversal of ast.traversals) {
2152
+ aliasToCte.set(traversal.nodeAlias, `cte_${traversal.nodeAlias}`);
2153
+ aliasToCte.set(traversal.edgeAlias, `cte_${traversal.nodeAlias}`);
2154
+ }
2155
+ const columns = fields.map((field2) => {
2156
+ const cteAlias = collapsedTraversalCteAlias ?? aliasToCte.get(field2.alias) ?? `cte_${field2.alias}`;
2157
+ if (field2.isSystemField) {
2158
+ const dbColumn = mapSelectiveSystemFieldToColumn(field2.field);
2159
+ return drizzleOrm.sql`${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(`${field2.alias}_${dbColumn}`)} AS ${quoteIdentifier(field2.outputName)}`;
2160
+ }
2161
+ const propsColumn = `${field2.alias}_props`;
2162
+ const column = drizzleOrm.sql`${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(propsColumn)}`;
2163
+ const pointer = chunkP5CNM325_cjs.jsonPointer([field2.field]);
2164
+ const extracted = compileTypedJsonExtract({
2165
+ column,
2166
+ dialect,
2167
+ pointer,
2168
+ valueType: field2.valueType
2169
+ });
2170
+ return drizzleOrm.sql`${extracted} AS ${quoteIdentifier(field2.outputName)}`;
2171
+ });
2172
+ return drizzleOrm.sql.join(columns, drizzleOrm.sql`, `);
2173
+ }
2174
+ function buildStandardFromClause(input) {
2175
+ const { ast, collapsedTraversalCteAlias, vectorPredicate } = input;
2176
+ if (collapsedTraversalCteAlias !== void 0) {
2177
+ return drizzleOrm.sql`FROM ${drizzleOrm.sql.raw(collapsedTraversalCteAlias)}`;
2178
+ }
2179
+ const startAlias = ast.start.alias;
2180
+ const fromClause = drizzleOrm.sql`FROM cte_${drizzleOrm.sql.raw(startAlias)}`;
2181
+ const joins = [];
2182
+ for (const traversal of ast.traversals) {
2183
+ const cteAlias = `cte_${traversal.nodeAlias}`;
2184
+ const previousAlias = traversal.joinFromAlias;
2185
+ const joinType = traversal.optional ? "LEFT JOIN" : "INNER JOIN";
2186
+ joins.push(
2187
+ drizzleOrm.sql`${drizzleOrm.sql.raw(joinType)} ${drizzleOrm.sql.raw(cteAlias)} ON ${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id = cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id AND ${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind = cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind`
2188
+ );
2189
+ }
2190
+ if (vectorPredicate) {
2191
+ const nodeAlias = vectorPredicate.field.alias;
2192
+ joins.push(
2193
+ drizzleOrm.sql`INNER JOIN cte_embeddings ON cte_embeddings.node_id = cte_${drizzleOrm.sql.raw(nodeAlias)}.${drizzleOrm.sql.raw(nodeAlias)}_id`
2194
+ );
2195
+ }
2196
+ return joins.length === 0 ? fromClause : drizzleOrm.sql`${fromClause} ${drizzleOrm.sql.join(joins, drizzleOrm.sql` `)}`;
2197
+ }
2198
+ function buildStandardOrderBy(input) {
2199
+ const { ast, collapsedTraversalCteAlias, dialect } = input;
2200
+ if (!ast.orderBy || ast.orderBy.length === 0) {
2201
+ return void 0;
2202
+ }
2203
+ const parts = [];
2204
+ for (const orderSpec of ast.orderBy) {
2205
+ const valueType = orderSpec.field.valueType;
2206
+ if (valueType === "array" || valueType === "object") {
2207
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2208
+ "Ordering by JSON arrays or objects is not supported"
2209
+ );
2210
+ }
2211
+ const cteAlias = collapsedTraversalCteAlias ?? `cte_${orderSpec.field.alias}`;
2212
+ const field2 = compileFieldValue(
2213
+ orderSpec.field,
2214
+ dialect,
2215
+ valueType,
2216
+ cteAlias
2217
+ );
2218
+ const direction = drizzleOrm.sql.raw(orderSpec.direction.toUpperCase());
2219
+ const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
2220
+ const nullsDirection = drizzleOrm.sql.raw(nulls === "first" ? "DESC" : "ASC");
2221
+ parts.push(
2222
+ drizzleOrm.sql`(${field2} IS NULL) ${nullsDirection}`,
2223
+ drizzleOrm.sql`${field2} ${direction}`
2224
+ );
2225
+ }
2226
+ return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join(parts, drizzleOrm.sql`, `)}`;
2227
+ }
2228
+ function fieldRefKey(field2) {
2229
+ const pointer = field2.jsonPointer ?? "";
2230
+ return `${field2.alias}:${field2.path.join(".")}:${pointer}`;
2231
+ }
2232
+ function buildStandardGroupBy(input) {
2233
+ const { ast, dialect } = input;
2234
+ if (!ast.groupBy || ast.groupBy.fields.length === 0) {
2235
+ return void 0;
2236
+ }
2237
+ const seenKeys = /* @__PURE__ */ new Set();
2238
+ const allFields = [];
2239
+ for (const projectedField of ast.projection.fields) {
2240
+ if (projectedField.source.__type === "field_ref") {
2241
+ const key = fieldRefKey(projectedField.source);
2242
+ if (!seenKeys.has(key)) {
2243
+ seenKeys.add(key);
2244
+ allFields.push(projectedField.source);
2245
+ }
2246
+ }
2247
+ }
2248
+ for (const field2 of ast.groupBy.fields) {
2249
+ const key = fieldRefKey(field2);
2250
+ if (!seenKeys.has(key)) {
2251
+ seenKeys.add(key);
2252
+ allFields.push(field2);
2253
+ }
2254
+ }
2255
+ if (allFields.length === 0) {
2256
+ return void 0;
2257
+ }
2258
+ const parts = allFields.map(
2259
+ (field2) => compileFieldValue(field2, dialect, field2.valueType, `cte_${field2.alias}`)
2260
+ );
2261
+ return drizzleOrm.sql`GROUP BY ${drizzleOrm.sql.join(parts, drizzleOrm.sql`, `)}`;
2262
+ }
2263
+ function buildStandardHaving(input) {
2264
+ const { ast, ctx } = input;
2265
+ if (!ast.having) {
2266
+ return void 0;
2267
+ }
2268
+ const condition = compilePredicateExpression(ast.having, ctx);
2269
+ return drizzleOrm.sql`HAVING ${condition}`;
2270
+ }
2271
+ function buildStandardEmbeddingsCte(input) {
2272
+ const { ctx, graphId, vectorPredicate } = input;
2273
+ const { dialect } = ctx;
2274
+ const { field: field2, metric, minScore, queryEmbedding } = vectorPredicate;
2275
+ const fieldPath = field2.jsonPointer ? field2.jsonPointer : field2.path.length > 1 && field2.path[0] === "props" ? `/${field2.path.slice(1).join("/")}` : `/${field2.path.join("/")}`;
2276
+ const distanceExpr = dialect.vectorDistance(
2277
+ drizzleOrm.sql.raw("embedding"),
2278
+ queryEmbedding,
2279
+ metric
2280
+ );
2281
+ const conditions = [
2282
+ drizzleOrm.sql`graph_id = ${graphId}`,
2283
+ drizzleOrm.sql`field_path = ${fieldPath}`
2284
+ ];
2285
+ if (minScore !== void 0) {
2286
+ conditions.push(
2287
+ compileVectorMinScoreCondition(distanceExpr, metric, minScore)
2288
+ );
2289
+ }
2290
+ const scoreExpr = compileVectorScoreExpression(distanceExpr, metric);
2291
+ return drizzleOrm.sql`
2292
+ cte_embeddings AS (
2293
+ SELECT
2294
+ node_id,
2295
+ ${distanceExpr} AS distance,
2296
+ ${scoreExpr} AS score
2297
+ FROM ${ctx.schema.embeddingsTable}
2298
+ WHERE ${drizzleOrm.sql.join(conditions, drizzleOrm.sql` AND `)}
2299
+ ORDER BY ${distanceExpr} ASC
2300
+ )
2301
+ `;
2302
+ }
2303
+ function compileVectorScoreExpression(distanceExpr, metric) {
2304
+ switch (metric) {
2305
+ case "cosine": {
2306
+ return drizzleOrm.sql`(1.0 - ${distanceExpr})`;
2307
+ }
2308
+ case "l2":
2309
+ case "inner_product": {
2310
+ return distanceExpr;
2311
+ }
2060
2312
  }
2061
- return drizzleOrm.sql.join(parts, drizzleOrm.sql` `);
2062
2313
  }
2063
- function assertSetOperationEmitterClauseAlignment(logicalPlan, suffixClauses) {
2064
- const shape = inspectSetOperationPlan(logicalPlan);
2065
- const hasSuffixClauses = suffixClauses !== void 0 && suffixClauses.length > 0;
2066
- if (!shape.hasSort && !shape.hasLimitOffset && hasSuffixClauses) {
2067
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2068
- "Set-operation SQL emitter received suffix clauses for a plan without top-level sort or limit_offset nodes",
2069
- { component: "set-operation-emitter" }
2070
- );
2314
+ function compileVectorMinScoreCondition(distanceExpr, metric, minScore) {
2315
+ switch (metric) {
2316
+ case "cosine": {
2317
+ const threshold = 1 - minScore;
2318
+ return drizzleOrm.sql`${distanceExpr} <= ${threshold}`;
2319
+ }
2320
+ case "l2": {
2321
+ return drizzleOrm.sql`${distanceExpr} <= ${minScore}`;
2322
+ }
2323
+ case "inner_product": {
2324
+ const negativeThreshold = -minScore;
2325
+ return drizzleOrm.sql`${distanceExpr} <= ${negativeThreshold}`;
2326
+ }
2071
2327
  }
2072
- if (!hasSuffixClauses) {
2073
- if (shape.hasSort || shape.hasLimitOffset) {
2074
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2075
- "Set-operation SQL emitter expected suffix clauses for plan containing top-level sort or limit_offset nodes",
2076
- { component: "set-operation-emitter" }
2328
+ }
2329
+ function buildStandardVectorOrderBy(input) {
2330
+ const { ast, dialect } = input;
2331
+ const distanceOrder = drizzleOrm.sql`cte_embeddings.distance ASC`;
2332
+ const additionalOrders = [];
2333
+ if (ast.orderBy && ast.orderBy.length > 0) {
2334
+ for (const orderSpec of ast.orderBy) {
2335
+ const valueType = orderSpec.field.valueType;
2336
+ if (valueType === "array" || valueType === "object") {
2337
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2338
+ "Ordering by JSON arrays or objects is not supported"
2339
+ );
2340
+ }
2341
+ const cteAlias = `cte_${orderSpec.field.alias}`;
2342
+ const field2 = compileFieldValue(
2343
+ orderSpec.field,
2344
+ dialect,
2345
+ valueType,
2346
+ cteAlias
2347
+ );
2348
+ const direction = drizzleOrm.sql.raw(orderSpec.direction.toUpperCase());
2349
+ const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
2350
+ const nullsDirection = drizzleOrm.sql.raw(nulls === "first" ? "DESC" : "ASC");
2351
+ additionalOrders.push(
2352
+ drizzleOrm.sql`(${field2} IS NULL) ${nullsDirection}`,
2353
+ drizzleOrm.sql`${field2} ${direction}`
2077
2354
  );
2078
2355
  }
2079
- return;
2080
- }
2081
- const limitOffsetClauseCount = shape.limitOffsetNode === void 0 ? 0 : (shape.limitOffsetNode.limit === void 0 ? 0 : 1) + (shape.limitOffsetNode.offset === void 0 ? 0 : 1);
2082
- const expectedClauseCount = (shape.sortNode === void 0 ? 0 : 1) + limitOffsetClauseCount;
2083
- if (suffixClauses.length !== expectedClauseCount) {
2084
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2085
- `Set-operation SQL emitter expected ${String(expectedClauseCount)} top-level suffix clause(s) from logical plan, got ${String(suffixClauses.length)}`,
2086
- { component: "set-operation-emitter" }
2087
- );
2088
2356
  }
2357
+ const allOrders = [distanceOrder, ...additionalOrders];
2358
+ return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join(allOrders, drizzleOrm.sql`, `)}`;
2089
2359
  }
2090
- function emitSetOperationQuerySql(input) {
2091
- assertSetOperationEmitterClauseAlignment(
2092
- input.logicalPlan,
2093
- input.suffixClauses
2094
- );
2360
+ function buildLimitOffsetClause(input) {
2361
+ const { limit, offset } = input;
2095
2362
  const parts = [];
2096
- if (input.ctes !== void 0 && input.ctes.length > 0) {
2097
- parts.push(drizzleOrm.sql`WITH ${drizzleOrm.sql.join([...input.ctes], drizzleOrm.sql`, `)}`);
2363
+ if (limit !== void 0) {
2364
+ parts.push(drizzleOrm.sql`LIMIT ${limit}`);
2098
2365
  }
2099
- parts.push(input.baseQuery);
2100
- if (input.suffixClauses !== void 0 && input.suffixClauses.length > 0) {
2101
- parts.push(...input.suffixClauses);
2366
+ if (offset !== void 0) {
2367
+ parts.push(drizzleOrm.sql`OFFSET ${offset}`);
2102
2368
  }
2103
- return drizzleOrm.sql.join(parts, drizzleOrm.sql` `);
2369
+ return parts.length > 0 ? drizzleOrm.sql.join(parts, drizzleOrm.sql` `) : void 0;
2104
2370
  }
2105
- function assertStandardEmitterClauseAlignment(logicalPlan, input) {
2106
- const planShape = inspectStandardProjectPlan(logicalPlan);
2107
- if (input.groupBy !== void 0 && !planShape.hasAggregate) {
2108
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2109
- "Standard SQL emitter received GROUP BY clause for a plan without aggregate nodes",
2110
- { component: "standard-emitter" }
2111
- );
2371
+
2372
+ // src/query/compiler/passes/recursive.ts
2373
+ function runRecursiveTraversalSelectionPass(ast) {
2374
+ const variableLengthTraversal = ast.traversals.find(
2375
+ (traversal) => traversal.variableLength !== void 0
2376
+ );
2377
+ if (variableLengthTraversal === void 0) {
2378
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError("No variable-length traversal found");
2112
2379
  }
2113
- if (input.having !== void 0 && !planShape.hasAggregate) {
2114
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2115
- "Standard SQL emitter received HAVING clause for a plan without aggregate nodes",
2116
- { component: "standard-emitter" }
2380
+ if (ast.traversals.length > 1) {
2381
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2382
+ "Variable-length traversals with multiple traversals are not yet supported. Please use a single variable-length traversal."
2117
2383
  );
2118
2384
  }
2119
- const expectsOrderBy = planShape.hasSort || planShape.hasVectorKnn;
2120
- if (expectsOrderBy && input.orderBy === void 0) {
2121
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2122
- "Standard SQL emitter expected ORDER BY clause for plan containing a sort or vector_knn node",
2123
- { component: "standard-emitter" }
2124
- );
2385
+ return variableLengthTraversal;
2386
+ }
2387
+
2388
+ // src/query/compiler/passes/runner.ts
2389
+ function runCompilerPass(state, pass) {
2390
+ const output = pass.execute(state);
2391
+ return {
2392
+ state: pass.update(state, output)
2393
+ };
2394
+ }
2395
+ function compileTemporalFilter(options) {
2396
+ const { mode, asOf, tableAlias, currentTimestamp } = options;
2397
+ const prefix = tableAlias ? drizzleOrm.sql.raw(`${tableAlias}.`) : drizzleOrm.sql.raw("");
2398
+ const deletedAt = drizzleOrm.sql`${prefix}deleted_at`;
2399
+ const validFrom = drizzleOrm.sql`${prefix}valid_from`;
2400
+ const validTo = drizzleOrm.sql`${prefix}valid_to`;
2401
+ switch (mode) {
2402
+ case "current": {
2403
+ const now = currentTimestamp ?? drizzleOrm.sql`CURRENT_TIMESTAMP`;
2404
+ return drizzleOrm.sql`${deletedAt} IS NULL AND (${validFrom} IS NULL OR ${validFrom} <= ${now}) AND (${validTo} IS NULL OR ${validTo} > ${now})`;
2405
+ }
2406
+ case "asOf": {
2407
+ const timestamp = asOf;
2408
+ return drizzleOrm.sql`${deletedAt} IS NULL AND (${validFrom} IS NULL OR ${validFrom} <= ${timestamp}) AND (${validTo} IS NULL OR ${validTo} > ${timestamp})`;
2409
+ }
2410
+ case "includeEnded": {
2411
+ return drizzleOrm.sql`${deletedAt} IS NULL`;
2412
+ }
2413
+ case "includeTombstones": {
2414
+ return drizzleOrm.sql.raw("1=1");
2415
+ }
2125
2416
  }
2126
- if (!expectsOrderBy && input.orderBy !== void 0) {
2127
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2128
- "Standard SQL emitter received ORDER BY clause for a plan without sort or vector_knn nodes",
2129
- { component: "standard-emitter" }
2417
+ }
2418
+ function extractTemporalOptions(ast, tableAlias) {
2419
+ return {
2420
+ mode: ast.temporalMode.mode,
2421
+ asOf: ast.temporalMode.asOf,
2422
+ tableAlias
2423
+ };
2424
+ }
2425
+
2426
+ // src/query/compiler/passes/temporal.ts
2427
+ function createTemporalFilterPass(ast, currentTimestamp) {
2428
+ return {
2429
+ forAlias(tableAlias) {
2430
+ return compileTemporalFilter({
2431
+ ...extractTemporalOptions(ast, tableAlias),
2432
+ currentTimestamp
2433
+ });
2434
+ }
2435
+ };
2436
+ }
2437
+
2438
+ // src/query/compiler/passes/vector.ts
2439
+ function runVectorPredicatePass(ast, dialect) {
2440
+ const vectorPredicates = extractVectorSimilarityPredicates(ast.predicates);
2441
+ if (vectorPredicates.length > 1) {
2442
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2443
+ "Multiple vector similarity predicates in a single query are not supported"
2130
2444
  );
2131
2445
  }
2132
- if (planShape.hasLimitOffset && input.limitOffset === void 0) {
2133
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2134
- "Standard SQL emitter expected LIMIT/OFFSET clause for plan containing a limit_offset node",
2135
- { component: "standard-emitter" }
2136
- );
2446
+ const vectorPredicate = vectorPredicates[0];
2447
+ if (vectorPredicate === void 0) {
2448
+ return { vectorPredicate: void 0 };
2137
2449
  }
2138
- if (!planShape.hasLimitOffset && input.limitOffset !== void 0) {
2139
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2140
- "Standard SQL emitter received LIMIT/OFFSET clause for a plan without limit_offset nodes",
2141
- { component: "standard-emitter" }
2450
+ const vectorStrategy = dialect.capabilities.vectorPredicateStrategy;
2451
+ if (vectorStrategy === "unsupported" || !dialect.supportsVectors) {
2452
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2453
+ `Vector similarity predicates are not supported for dialect "${dialect.name}"`
2142
2454
  );
2143
2455
  }
2144
- }
2145
- function emitStandardQuerySql(input) {
2146
- assertStandardEmitterClauseAlignment(input.logicalPlan, input);
2147
- const parts = [];
2148
- if (input.ctes.length > 0) {
2149
- parts.push(drizzleOrm.sql`WITH ${drizzleOrm.sql.join([...input.ctes], drizzleOrm.sql`, `)}`);
2456
+ if (!dialect.capabilities.vectorMetrics.includes(vectorPredicate.metric)) {
2457
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2458
+ `Vector metric "${vectorPredicate.metric}" is not supported for dialect "${dialect.name}"`
2459
+ );
2150
2460
  }
2151
- parts.push(drizzleOrm.sql`SELECT ${input.projection}`, input.fromClause);
2152
- if (input.groupBy !== void 0) {
2153
- parts.push(input.groupBy);
2461
+ if (!Number.isFinite(vectorPredicate.limit) || vectorPredicate.limit <= 0) {
2462
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2463
+ `Vector predicate limit must be a positive finite number, got ${String(vectorPredicate.limit)}`
2464
+ );
2154
2465
  }
2155
- if (input.having !== void 0) {
2156
- parts.push(input.having);
2466
+ const { minScore } = vectorPredicate;
2467
+ if (minScore !== void 0) {
2468
+ if (!Number.isFinite(minScore)) {
2469
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2470
+ `Vector minScore must be a finite number, got ${String(minScore)}`
2471
+ );
2472
+ }
2473
+ if (vectorPredicate.metric === "cosine" && (minScore < -1 || minScore > 1)) {
2474
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2475
+ `Cosine minScore must be between -1 and 1, got ${String(minScore)}`
2476
+ );
2477
+ }
2157
2478
  }
2158
- if (input.orderBy !== void 0) {
2159
- parts.push(input.orderBy);
2479
+ return { vectorPredicate };
2480
+ }
2481
+ function resolveVectorAwareLimit(astLimit, vectorPredicate) {
2482
+ if (vectorPredicate === void 0) {
2483
+ return astLimit;
2160
2484
  }
2161
- if (input.limitOffset !== void 0) {
2162
- parts.push(input.limitOffset);
2485
+ if (astLimit === void 0) {
2486
+ return vectorPredicate.limit;
2163
2487
  }
2164
- return drizzleOrm.sql.join(parts, drizzleOrm.sql` `);
2165
- }
2166
- var EMPTY_PREDICATES = [];
2167
- function buildPredicateIndexKey(alias, targetType) {
2168
- return `${alias}\0${targetType}`;
2169
- }
2170
- function resolvePredicateTargetType(predicate2) {
2171
- return predicate2.targetType === "edge" ? "edge" : "node";
2488
+ return Math.min(astLimit, vectorPredicate.limit);
2172
2489
  }
2173
- function buildPredicateIndex(ast) {
2174
- const byAliasAndType = /* @__PURE__ */ new Map();
2175
- for (const predicate2 of ast.predicates) {
2176
- const key = buildPredicateIndexKey(
2177
- predicate2.targetAlias,
2178
- resolvePredicateTargetType(predicate2)
2179
- );
2180
- const existing = byAliasAndType.get(key);
2181
- if (existing === void 0) {
2182
- byAliasAndType.set(key, [predicate2]);
2183
- } else {
2184
- existing.push(predicate2);
2490
+
2491
+ // src/query/compiler/plan/lowering.ts
2492
+ function createPlanNodeIdFactory() {
2493
+ let current = 0;
2494
+ return function nextPlanNodeId() {
2495
+ current += 1;
2496
+ return `plan_${current.toString(36)}`;
2497
+ };
2498
+ }
2499
+ function extractAggregateExpressions(ast) {
2500
+ const aggregates = [];
2501
+ for (const field2 of ast.projection.fields) {
2502
+ if ("__type" in field2.source && field2.source.__type === "aggregate") {
2503
+ aggregates.push(field2.source);
2185
2504
  }
2186
2505
  }
2187
- return { byAliasAndType };
2188
- }
2189
- function getPredicatesForAlias(predicateIndex, alias, targetType) {
2190
- return predicateIndex.byAliasAndType.get(
2191
- buildPredicateIndexKey(alias, targetType)
2192
- ) ?? EMPTY_PREDICATES;
2506
+ return aggregates;
2193
2507
  }
2194
- function compilePredicateClauses(predicates, predicateContext) {
2195
- return predicates.map(
2196
- (predicate2) => compilePredicateExpression(predicate2.expression, predicateContext)
2197
- );
2508
+ function getAliasPredicates(ast, alias, predicateTargetType) {
2509
+ return ast.predicates.filter((predicate2) => {
2510
+ const targetType = predicate2.targetType ?? "node";
2511
+ return predicate2.targetAlias === alias && targetType === predicateTargetType;
2512
+ });
2198
2513
  }
2199
- function compileKindFilter(column, kinds) {
2200
- if (kinds.length === 0) {
2201
- return drizzleOrm.sql`1 = 0`;
2202
- }
2203
- if (kinds.length === 1) {
2204
- return drizzleOrm.sql`${column} = ${kinds[0]}`;
2514
+ function wrapWithAliasFilterNode(currentNode, ast, alias, predicateTargetType, nextPlanNodeId) {
2515
+ const aliasPredicates = getAliasPredicates(ast, alias, predicateTargetType);
2516
+ if (aliasPredicates.length === 0) {
2517
+ return currentNode;
2205
2518
  }
2206
- return drizzleOrm.sql`${column} IN (${drizzleOrm.sql.join(
2207
- kinds.map((kind) => drizzleOrm.sql`${kind}`),
2208
- drizzleOrm.sql`, `
2209
- )})`;
2519
+ return {
2520
+ alias,
2521
+ id: nextPlanNodeId(),
2522
+ input: currentNode,
2523
+ op: "filter",
2524
+ predicateTargetType,
2525
+ predicates: aliasPredicates.map((predicate2) => predicate2.expression)
2526
+ };
2210
2527
  }
2211
- function getNodeKindsForAlias(ast, alias) {
2212
- if (alias === ast.start.alias) {
2213
- return ast.start.kinds;
2528
+ function appendAggregateSortLimitAndProjectNodes(currentNode, ast, nextPlanNodeId, limit, collapsedTraversalCteAlias) {
2529
+ let node = currentNode;
2530
+ const aggregateExpressions = extractAggregateExpressions(ast);
2531
+ if (aggregateExpressions.length > 0 || ast.groupBy !== void 0 || ast.having !== void 0) {
2532
+ const aggregateNode = {
2533
+ aggregates: aggregateExpressions,
2534
+ groupBy: ast.groupBy?.fields ?? [],
2535
+ id: nextPlanNodeId(),
2536
+ input: node,
2537
+ op: "aggregate"
2538
+ };
2539
+ node = ast.having === void 0 ? aggregateNode : { ...aggregateNode, having: ast.having };
2214
2540
  }
2215
- for (const traversal of ast.traversals) {
2216
- if (traversal.nodeAlias === alias) {
2217
- return traversal.nodeKinds;
2218
- }
2541
+ if (ast.orderBy !== void 0 && ast.orderBy.length > 0) {
2542
+ node = {
2543
+ id: nextPlanNodeId(),
2544
+ input: node,
2545
+ op: "sort",
2546
+ orderBy: ast.orderBy
2547
+ };
2219
2548
  }
2220
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(`Unknown traversal source alias: ${alias}`);
2221
- }
2222
-
2223
- // src/query/compiler/typed-json-extract.ts
2224
- function compileTypedJsonExtract(input) {
2225
- const { column, dialect, pointer, valueType } = input;
2226
- const fallback = input.fallback ?? "json";
2227
- switch (valueType) {
2228
- case "string": {
2229
- return dialect.jsonExtractText(column, pointer);
2230
- }
2231
- case "number": {
2232
- return dialect.jsonExtractNumber(column, pointer);
2233
- }
2234
- case "boolean": {
2235
- return dialect.jsonExtractBoolean(column, pointer);
2236
- }
2237
- case "date": {
2238
- return dialect.jsonExtractDate(column, pointer);
2239
- }
2240
- case "array":
2241
- case "object":
2242
- case "embedding":
2243
- case "unknown":
2244
- case void 0: {
2245
- return fallback === "text" ? dialect.jsonExtractText(column, pointer) : dialect.jsonExtract(column, pointer);
2246
- }
2247
- default: {
2248
- return fallback === "text" ? dialect.jsonExtractText(column, pointer) : dialect.jsonExtract(column, pointer);
2549
+ if (limit !== void 0 || ast.offset !== void 0) {
2550
+ const limitOffsetNodeBase = {
2551
+ id: nextPlanNodeId(),
2552
+ input: node,
2553
+ op: "limit_offset"
2554
+ };
2555
+ const hasLimit = limit !== void 0;
2556
+ const hasOffset = ast.offset !== void 0;
2557
+ if (hasLimit && hasOffset) {
2558
+ node = {
2559
+ ...limitOffsetNodeBase,
2560
+ limit,
2561
+ offset: ast.offset
2562
+ };
2563
+ } else if (hasLimit) {
2564
+ node = { ...limitOffsetNodeBase, limit };
2565
+ } else if (hasOffset) {
2566
+ node = { ...limitOffsetNodeBase, offset: ast.offset };
2567
+ } else {
2568
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2569
+ "limit_offset node requires limit or offset to be present"
2570
+ );
2249
2571
  }
2250
2572
  }
2573
+ const projectNodeBase = {
2574
+ fields: ast.projection.fields,
2575
+ id: nextPlanNodeId(),
2576
+ input: node,
2577
+ op: "project"
2578
+ };
2579
+ return collapsedTraversalCteAlias === void 0 ? projectNodeBase : {
2580
+ ...projectNodeBase,
2581
+ collapsedTraversalAlias: collapsedTraversalCteAlias
2582
+ };
2251
2583
  }
2252
- var NODE_COLUMNS = [
2253
- "id",
2254
- "kind",
2255
- "props",
2256
- "version",
2257
- "valid_from",
2258
- "valid_to",
2259
- "created_at",
2260
- "updated_at",
2261
- "deleted_at"
2262
- ];
2263
- var EDGE_COLUMNS = [
2264
- "id",
2265
- "kind",
2266
- "from_id",
2267
- "to_id",
2268
- "props",
2269
- "valid_from",
2270
- "valid_to",
2271
- "created_at",
2272
- "updated_at",
2273
- "deleted_at"
2274
- ];
2275
- var EMPTY_REQUIRED_COLUMNS = /* @__PURE__ */ new Set();
2276
- function quoteIdentifier(identifier) {
2277
- return drizzleOrm.sql.raw(`"${identifier.replaceAll('"', '""')}"`);
2584
+ function lowerStandardQueryToLogicalPlanNode(input) {
2585
+ const { ast, nextPlanNodeId } = input;
2586
+ let currentNode = {
2587
+ alias: ast.start.alias,
2588
+ graphId: input.graphId,
2589
+ id: nextPlanNodeId(),
2590
+ kinds: ast.start.kinds,
2591
+ op: "scan",
2592
+ source: "nodes"
2593
+ };
2594
+ currentNode = wrapWithAliasFilterNode(
2595
+ currentNode,
2596
+ ast,
2597
+ ast.start.alias,
2598
+ "node",
2599
+ nextPlanNodeId
2600
+ );
2601
+ for (const traversal of ast.traversals) {
2602
+ currentNode = {
2603
+ direction: traversal.direction,
2604
+ edgeAlias: traversal.edgeAlias,
2605
+ edgeKinds: traversal.edgeKinds,
2606
+ id: nextPlanNodeId(),
2607
+ input: currentNode,
2608
+ inverseEdgeKinds: traversal.inverseEdgeKinds ?? [],
2609
+ joinFromAlias: traversal.joinFromAlias,
2610
+ joinType: traversal.optional ? "left" : "inner",
2611
+ nodeAlias: traversal.nodeAlias,
2612
+ nodeKinds: traversal.nodeKinds,
2613
+ op: "join"
2614
+ };
2615
+ currentNode = wrapWithAliasFilterNode(
2616
+ currentNode,
2617
+ ast,
2618
+ traversal.edgeAlias,
2619
+ "edge",
2620
+ nextPlanNodeId
2621
+ );
2622
+ currentNode = wrapWithAliasFilterNode(
2623
+ currentNode,
2624
+ ast,
2625
+ traversal.nodeAlias,
2626
+ "node",
2627
+ nextPlanNodeId
2628
+ );
2629
+ }
2630
+ if (input.vectorPredicate !== void 0) {
2631
+ currentNode = {
2632
+ id: nextPlanNodeId(),
2633
+ input: currentNode,
2634
+ op: "vector_knn",
2635
+ predicate: input.vectorPredicate
2636
+ };
2637
+ }
2638
+ return appendAggregateSortLimitAndProjectNodes(
2639
+ currentNode,
2640
+ ast,
2641
+ nextPlanNodeId,
2642
+ input.effectiveLimit,
2643
+ input.collapsedTraversalCteAlias
2644
+ );
2278
2645
  }
2279
- function shouldProjectColumn(requiredColumns, column, alwaysRequiredColumns) {
2280
- if (alwaysRequiredColumns?.has(column)) return true;
2281
- if (requiredColumns === void 0) return true;
2282
- return requiredColumns.has(column);
2646
+ function lowerRecursiveQueryToLogicalPlanNode(input) {
2647
+ const { ast, nextPlanNodeId } = input;
2648
+ const traversal = input.traversal ?? runRecursiveTraversalSelectionPass(input.ast);
2649
+ let currentNode = {
2650
+ alias: ast.start.alias,
2651
+ graphId: input.graphId,
2652
+ id: nextPlanNodeId(),
2653
+ kinds: ast.start.kinds,
2654
+ op: "scan",
2655
+ source: "nodes"
2656
+ };
2657
+ currentNode = wrapWithAliasFilterNode(
2658
+ currentNode,
2659
+ ast,
2660
+ ast.start.alias,
2661
+ "node",
2662
+ nextPlanNodeId
2663
+ );
2664
+ currentNode = {
2665
+ edgeAlias: traversal.edgeAlias,
2666
+ edgeKinds: traversal.edgeKinds,
2667
+ id: nextPlanNodeId(),
2668
+ input: currentNode,
2669
+ inverseEdgeKinds: traversal.inverseEdgeKinds ?? [],
2670
+ nodeAlias: traversal.nodeAlias,
2671
+ nodeKinds: traversal.nodeKinds,
2672
+ op: "recursive_expand",
2673
+ traversal: traversal.variableLength
2674
+ };
2675
+ currentNode = wrapWithAliasFilterNode(
2676
+ currentNode,
2677
+ ast,
2678
+ traversal.edgeAlias,
2679
+ "edge",
2680
+ nextPlanNodeId
2681
+ );
2682
+ currentNode = wrapWithAliasFilterNode(
2683
+ currentNode,
2684
+ ast,
2685
+ traversal.nodeAlias,
2686
+ "node",
2687
+ nextPlanNodeId
2688
+ );
2689
+ return appendAggregateSortLimitAndProjectNodes(
2690
+ currentNode,
2691
+ ast,
2692
+ nextPlanNodeId,
2693
+ ast.limit
2694
+ );
2283
2695
  }
2284
- function addRequiredColumn(requiredColumnsByAlias, alias, column) {
2285
- const existing = requiredColumnsByAlias.get(alias);
2286
- if (existing) {
2287
- existing.add(column);
2288
- return;
2696
+ function lowerComposableQueryToLogicalPlanNode(query, dialect, graphId, nextPlanNodeId) {
2697
+ if ("__type" in query) {
2698
+ return lowerSetOperationToLogicalPlanNode(
2699
+ query,
2700
+ graphId,
2701
+ dialect,
2702
+ nextPlanNodeId
2703
+ );
2289
2704
  }
2290
- requiredColumnsByAlias.set(alias, /* @__PURE__ */ new Set([column]));
2291
- }
2292
- function markFieldRefAsRequired(requiredColumnsByAlias, field2) {
2293
- const column = field2.path[0];
2294
- if (column === void 0) return;
2295
- addRequiredColumn(requiredColumnsByAlias, field2.alias, column);
2296
- }
2297
- function mapSelectiveSystemFieldToColumn(field2) {
2298
- if (field2 === "fromId") return "from_id";
2299
- if (field2 === "toId") return "to_id";
2300
- if (field2.startsWith("meta.")) {
2301
- return field2.slice(5).replaceAll(/([A-Z])/g, "_$1").toLowerCase();
2705
+ const hasVariableLengthTraversal2 = query.traversals.some(
2706
+ (traversal) => traversal.variableLength !== void 0
2707
+ );
2708
+ if (hasVariableLengthTraversal2) {
2709
+ return lowerRecursiveQueryToLogicalPlanNode({
2710
+ ast: query,
2711
+ graphId,
2712
+ nextPlanNodeId
2713
+ });
2302
2714
  }
2303
- return field2;
2715
+ const vectorPredicate = runVectorPredicatePass(
2716
+ query,
2717
+ chunk2WVFEIHR_cjs.getDialect(dialect)
2718
+ ).vectorPredicate;
2719
+ const effectiveLimit = resolveVectorAwareLimit(query.limit, vectorPredicate);
2720
+ const loweringInput = {
2721
+ ast: query,
2722
+ graphId,
2723
+ nextPlanNodeId,
2724
+ ...effectiveLimit === void 0 ? {} : { effectiveLimit },
2725
+ ...vectorPredicate === void 0 ? {} : { vectorPredicate }
2726
+ };
2727
+ return lowerStandardQueryToLogicalPlanNode(loweringInput);
2304
2728
  }
2305
- function markSelectiveFieldAsRequired(requiredColumnsByAlias, field2) {
2306
- if (field2.isSystemField) {
2307
- addRequiredColumn(
2308
- requiredColumnsByAlias,
2309
- field2.alias,
2310
- mapSelectiveSystemFieldToColumn(field2.field)
2311
- );
2312
- return;
2729
+ function lowerSetOperationToLogicalPlanNode(op, graphId, dialect, nextPlanNodeId) {
2730
+ let currentNode = {
2731
+ id: nextPlanNodeId(),
2732
+ left: lowerComposableQueryToLogicalPlanNode(
2733
+ op.left,
2734
+ dialect,
2735
+ graphId,
2736
+ nextPlanNodeId
2737
+ ),
2738
+ op: "set_op",
2739
+ operator: op.operator,
2740
+ right: lowerComposableQueryToLogicalPlanNode(
2741
+ op.right,
2742
+ dialect,
2743
+ graphId,
2744
+ nextPlanNodeId
2745
+ )
2746
+ };
2747
+ if (op.orderBy !== void 0 && op.orderBy.length > 0) {
2748
+ currentNode = {
2749
+ id: nextPlanNodeId(),
2750
+ input: currentNode,
2751
+ op: "sort",
2752
+ orderBy: op.orderBy
2753
+ };
2313
2754
  }
2314
- addRequiredColumn(requiredColumnsByAlias, field2.alias, "props");
2315
- }
2316
- function isIdFieldRef(field2) {
2317
- return field2.path.length === 1 && field2.path[0] === "id" && field2.jsonPointer === void 0;
2755
+ if (op.limit !== void 0 || op.offset !== void 0) {
2756
+ const limitOffsetBase = {
2757
+ id: nextPlanNodeId(),
2758
+ input: currentNode,
2759
+ op: "limit_offset"
2760
+ };
2761
+ if (op.limit !== void 0 && op.offset !== void 0) {
2762
+ currentNode = { ...limitOffsetBase, limit: op.limit, offset: op.offset };
2763
+ } else if (op.limit === void 0) {
2764
+ currentNode = { ...limitOffsetBase, offset: op.offset };
2765
+ } else {
2766
+ currentNode = { ...limitOffsetBase, limit: op.limit };
2767
+ }
2768
+ }
2769
+ return currentNode;
2318
2770
  }
2319
- function isAggregateExpr(source) {
2320
- return "__type" in source && source.__type === "aggregate";
2771
+ function lowerStandardQueryToLogicalPlan(input) {
2772
+ const nextPlanNodeId = createPlanNodeIdFactory();
2773
+ return {
2774
+ metadata: {
2775
+ dialect: input.dialect,
2776
+ graphId: input.graphId
2777
+ },
2778
+ root: lowerStandardQueryToLogicalPlanNode({
2779
+ ...input,
2780
+ nextPlanNodeId
2781
+ })
2782
+ };
2321
2783
  }
2322
-
2323
- // src/query/compiler/emitter/standard-builders.ts
2324
- function compileColumnReference(tableAlias, column) {
2325
- if (tableAlias === void 0) {
2326
- return drizzleOrm.sql.raw(column);
2327
- }
2328
- return drizzleOrm.sql`${drizzleOrm.sql.raw(tableAlias)}.${drizzleOrm.sql.raw(column)}`;
2784
+ function lowerRecursiveQueryToLogicalPlan(input) {
2785
+ const nextPlanNodeId = createPlanNodeIdFactory();
2786
+ return {
2787
+ metadata: {
2788
+ dialect: input.dialect,
2789
+ graphId: input.graphId
2790
+ },
2791
+ root: lowerRecursiveQueryToLogicalPlanNode({
2792
+ ...input,
2793
+ nextPlanNodeId
2794
+ })
2795
+ };
2329
2796
  }
2330
- function compileNodeSelectColumns(tableAlias, alias, requiredColumns) {
2331
- return NODE_COLUMNS.filter(
2332
- (column) => column === "id" || column === "kind" || shouldProjectColumn(requiredColumns, column)
2333
- ).map(
2334
- (column) => drizzleOrm.sql`${compileColumnReference(tableAlias, column)} AS ${drizzleOrm.sql.raw(`${alias}_${column}`)}`
2335
- );
2797
+ function lowerSetOperationToLogicalPlan(input) {
2798
+ const nextPlanNodeId = createPlanNodeIdFactory();
2799
+ return {
2800
+ metadata: {
2801
+ dialect: input.dialect,
2802
+ graphId: input.graphId
2803
+ },
2804
+ root: lowerSetOperationToLogicalPlanNode(
2805
+ input.op,
2806
+ input.graphId,
2807
+ input.dialect,
2808
+ nextPlanNodeId
2809
+ )
2810
+ };
2336
2811
  }
2337
- function compileEdgeSelectColumns(tableAlias, alias, requiredColumns) {
2338
- return EDGE_COLUMNS.filter(
2339
- (column) => shouldProjectColumn(requiredColumns, column)
2340
- ).map(
2341
- (column) => drizzleOrm.sql`${compileColumnReference(tableAlias, column)} AS ${drizzleOrm.sql.raw(`${alias}_${column}`)}`
2342
- );
2812
+
2813
+ // src/query/compiler/recursive.ts
2814
+ var MAX_RECURSIVE_DEPTH = 100;
2815
+ var MAX_EXPLICIT_RECURSIVE_DEPTH = 1e3;
2816
+ var NO_ALWAYS_REQUIRED_COLUMNS = /* @__PURE__ */ new Set();
2817
+ function runRecursiveQueryPassPipeline(ast, graphId, ctx) {
2818
+ let state = {
2819
+ ast,
2820
+ ctx,
2821
+ graphId,
2822
+ logicalPlan: void 0,
2823
+ requiredColumnsByAlias: void 0,
2824
+ temporalFilterPass: void 0,
2825
+ traversal: void 0
2826
+ };
2827
+ const recursiveTraversalPass = runCompilerPass(state, {
2828
+ name: "recursive_traversal",
2829
+ execute(currentState) {
2830
+ return runRecursiveTraversalSelectionPass(currentState.ast);
2831
+ },
2832
+ update(currentState, traversal) {
2833
+ return {
2834
+ ...currentState,
2835
+ traversal
2836
+ };
2837
+ }
2838
+ });
2839
+ state = recursiveTraversalPass.state;
2840
+ const temporalPass = runCompilerPass(state, {
2841
+ name: "temporal_filters",
2842
+ execute(currentState) {
2843
+ return createTemporalFilterPass(
2844
+ currentState.ast,
2845
+ currentState.ctx.dialect.currentTimestamp()
2846
+ );
2847
+ },
2848
+ update(currentState, temporalFilterPass) {
2849
+ return {
2850
+ ...currentState,
2851
+ temporalFilterPass
2852
+ };
2853
+ }
2854
+ });
2855
+ state = temporalPass.state;
2856
+ const columnPruningPass = runCompilerPass(state, {
2857
+ name: "column_pruning",
2858
+ execute(currentState) {
2859
+ const traversal = currentState.traversal;
2860
+ if (traversal === void 0) {
2861
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2862
+ "Recursive traversal pass did not select traversal"
2863
+ );
2864
+ }
2865
+ return collectRequiredColumnsByAlias(currentState.ast, traversal);
2866
+ },
2867
+ update(currentState, requiredColumnsByAlias) {
2868
+ return {
2869
+ ...currentState,
2870
+ requiredColumnsByAlias
2871
+ };
2872
+ }
2873
+ });
2874
+ state = columnPruningPass.state;
2875
+ const logicalPlanPass = runCompilerPass(state, {
2876
+ name: "logical_plan",
2877
+ execute(currentState) {
2878
+ const loweringInput = {
2879
+ ast: currentState.ast,
2880
+ dialect: currentState.ctx.dialect.name,
2881
+ graphId: currentState.graphId,
2882
+ ...currentState.traversal === void 0 ? {} : { traversal: currentState.traversal }
2883
+ };
2884
+ return lowerRecursiveQueryToLogicalPlan(loweringInput);
2885
+ },
2886
+ update(currentState, logicalPlan) {
2887
+ return {
2888
+ ...currentState,
2889
+ logicalPlan
2890
+ };
2891
+ }
2892
+ });
2893
+ state = logicalPlanPass.state;
2894
+ return state;
2343
2895
  }
2344
- function buildStandardStartCte(input) {
2345
- const { ast, ctx, graphId, predicateIndex, requiredColumnsByAlias } = input;
2346
- const alias = ast.start.alias;
2347
- const kinds = ast.start.kinds;
2348
- const kindFilter = compileKindFilter(drizzleOrm.sql.raw("kind"), kinds);
2349
- const temporalFilter = input.temporalFilterPass.forAlias();
2350
- const cteContext = { ...ctx, cteColumnPrefix: "" };
2351
- const predicateClauses = compilePredicateClauses(
2352
- getPredicatesForAlias(predicateIndex, alias, "node"),
2353
- cteContext
2354
- );
2355
- const whereClauses = [
2356
- drizzleOrm.sql`graph_id = ${graphId}`,
2357
- kindFilter,
2358
- temporalFilter,
2359
- ...predicateClauses
2360
- ];
2361
- const effectiveRequiredColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(alias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
2362
- return drizzleOrm.sql`
2363
- cte_${drizzleOrm.sql.raw(alias)} AS (
2364
- SELECT ${drizzleOrm.sql.join(
2365
- compileNodeSelectColumns(void 0, alias, effectiveRequiredColumns),
2366
- drizzleOrm.sql`, `
2367
- )}
2368
- FROM ${ctx.schema.nodesTable}
2369
- WHERE ${drizzleOrm.sql.join(whereClauses, drizzleOrm.sql` AND `)}
2370
- )
2371
- `;
2896
+ function compileVariableLengthQuery(ast, graphId, ctx) {
2897
+ const strategy = ctx.dialect.capabilities.recursiveQueryStrategy;
2898
+ const handler = RECURSIVE_QUERY_STRATEGY_HANDLERS[strategy];
2899
+ return handler(ast, graphId, ctx);
2372
2900
  }
2373
- function buildStandardTraversalCte(input) {
2901
+ var RECURSIVE_QUERY_STRATEGY_HANDLERS = {
2902
+ recursive_cte: compileVariableLengthQueryWithRecursiveCteStrategy
2903
+ };
2904
+ function compileVariableLengthQueryWithRecursiveCteStrategy(ast, graphId, ctx) {
2905
+ const passState = runRecursiveQueryPassPipeline(ast, graphId, ctx);
2906
+ const { dialect } = ctx;
2374
2907
  const {
2908
+ logicalPlan,
2909
+ requiredColumnsByAlias,
2910
+ temporalFilterPass,
2911
+ traversal: vlTraversal
2912
+ } = passState;
2913
+ if (temporalFilterPass === void 0) {
2914
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2915
+ "Temporal filter pass did not initialize temporal state"
2916
+ );
2917
+ }
2918
+ if (logicalPlan === void 0) {
2919
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2920
+ "Logical plan pass did not initialize plan state"
2921
+ );
2922
+ }
2923
+ if (vlTraversal === void 0) {
2924
+ throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2925
+ "Recursive traversal pass did not select traversal"
2926
+ );
2927
+ }
2928
+ const recursiveCte = compileRecursiveCte(
2375
2929
  ast,
2376
- carryForwardPreviousColumns,
2377
- ctx,
2930
+ vlTraversal,
2378
2931
  graphId,
2379
- materializeCte,
2380
- predicateIndex,
2932
+ ctx,
2381
2933
  requiredColumnsByAlias,
2382
- temporalFilterPass,
2383
- traversalIndex,
2384
- traversalLimit
2385
- } = input;
2386
- const traversal = ast.traversals[traversalIndex];
2387
- const traversalLimitValue = traversalIndex === ast.traversals.length - 1 ? traversalLimit : void 0;
2388
- const previousNodeKinds = getNodeKindsForAlias(ast, traversal.joinFromAlias);
2934
+ temporalFilterPass
2935
+ );
2936
+ const projection = compileRecursiveProjection(ast, vlTraversal, dialect);
2937
+ const minDepth = vlTraversal.variableLength.minDepth;
2938
+ const depthFilter = minDepth > 0 ? drizzleOrm.sql`WHERE depth >= ${minDepth}` : drizzleOrm.sql.raw("");
2939
+ const orderBy = compileRecursiveOrderBy(ast, dialect);
2940
+ const limitOffset = compileLimitOffset(ast);
2941
+ return emitRecursiveQuerySql({
2942
+ depthFilter,
2943
+ ...limitOffset === void 0 ? {} : { limitOffset },
2944
+ logicalPlan,
2945
+ ...orderBy === void 0 ? {} : { orderBy },
2946
+ projection,
2947
+ recursiveCte
2948
+ });
2949
+ }
2950
+ function hasVariableLengthTraversal(ast) {
2951
+ return ast.traversals.some((t) => t.variableLength !== void 0);
2952
+ }
2953
+ function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlias, temporalFilterPass) {
2954
+ const { dialect } = ctx;
2955
+ const startAlias = ast.start.alias;
2956
+ const startKinds = ast.start.kinds;
2957
+ const nodeAlias = traversal.nodeAlias;
2389
2958
  const directEdgeKinds = [...new Set(traversal.edgeKinds)];
2390
2959
  const inverseEdgeKinds = traversal.inverseEdgeKinds === void 0 ? [] : [...new Set(traversal.inverseEdgeKinds)];
2960
+ const forceWorktableOuterJoinOrder = dialect.capabilities.forceRecursiveWorktableOuterJoinOrder;
2391
2961
  const nodeKinds = traversal.nodeKinds;
2392
- const nodeKindFilter = compileKindFilter(drizzleOrm.sql.raw("n.kind"), nodeKinds);
2962
+ const previousNodeKinds = [.../* @__PURE__ */ new Set([...startKinds, ...nodeKinds])];
2963
+ const direction = traversal.direction;
2964
+ const vl = traversal.variableLength;
2965
+ const shouldEnforceCycleCheck = vl.cyclePolicy !== "allow";
2966
+ const shouldTrackPath = shouldEnforceCycleCheck || vl.pathAlias !== void 0;
2967
+ const recursiveJoinRequiredColumns = /* @__PURE__ */ new Set(["id"]);
2968
+ if (previousNodeKinds.length > 1) {
2969
+ recursiveJoinRequiredColumns.add("kind");
2970
+ }
2971
+ const requiredStartColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(startAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
2972
+ const requiredNodeColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(nodeAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
2973
+ const startColumnsFromBase = compileNodeSelectColumnsFromTable(
2974
+ "n0",
2975
+ startAlias,
2976
+ requiredStartColumns,
2977
+ NO_ALWAYS_REQUIRED_COLUMNS
2978
+ );
2979
+ const startColumnsFromRecursive = compileNodeSelectColumnsFromRecursiveRow(
2980
+ startAlias,
2981
+ requiredStartColumns,
2982
+ NO_ALWAYS_REQUIRED_COLUMNS
2983
+ );
2984
+ const nodeColumnsFromBase = compileNodeSelectColumnsFromTable(
2985
+ "n0",
2986
+ nodeAlias,
2987
+ requiredNodeColumns,
2988
+ recursiveJoinRequiredColumns
2989
+ );
2990
+ const nodeColumnsFromRecursive = compileNodeSelectColumnsFromTable(
2991
+ "n",
2992
+ nodeAlias,
2993
+ requiredNodeColumns,
2994
+ recursiveJoinRequiredColumns
2995
+ );
2996
+ const startKindFilter = compileKindFilter2(startKinds, "n0.kind");
2997
+ const nodeKindFilter = compileKindFilter2(nodeKinds, "n.kind");
2998
+ const startTemporalFilter = temporalFilterPass.forAlias("n0");
2393
2999
  const edgeTemporalFilter = temporalFilterPass.forAlias("e");
2394
3000
  const nodeTemporalFilter = temporalFilterPass.forAlias("n");
2395
- const nodeCteContext = {
2396
- ...ctx,
2397
- cteColumnPrefix: "n"
2398
- };
2399
- const nodePredicateClauses = compilePredicateClauses(
2400
- getPredicatesForAlias(predicateIndex, traversal.nodeAlias, "node"),
2401
- nodeCteContext
3001
+ const startContext = { ...ctx, cteColumnPrefix: "" };
3002
+ const startPredicates = compileNodePredicates(ast, startAlias, startContext);
3003
+ const edgeContext = { ...ctx, cteColumnPrefix: "e" };
3004
+ const edgePredicates = compileEdgePredicates(
3005
+ ast,
3006
+ traversal.edgeAlias,
3007
+ edgeContext
2402
3008
  );
2403
- const edgeCteContext = {
2404
- ...ctx,
2405
- cteColumnPrefix: "e"
2406
- };
2407
- const edgePredicateClauses = compilePredicateClauses(
2408
- getPredicatesForAlias(predicateIndex, traversal.edgeAlias, "edge"),
2409
- edgeCteContext
3009
+ const targetContext = { ...ctx, cteColumnPrefix: "n" };
3010
+ const targetNodePredicates = compileNodePredicates(
3011
+ ast,
3012
+ nodeAlias,
3013
+ targetContext
2410
3014
  );
3015
+ if (vl.maxDepth > MAX_EXPLICIT_RECURSIVE_DEPTH) {
3016
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
3017
+ `Recursive traversal maxHops(${vl.maxDepth}) exceeds maximum explicit depth of ${MAX_EXPLICIT_RECURSIVE_DEPTH}`
3018
+ );
3019
+ }
3020
+ const effectiveMaxDepth = vl.maxDepth > 0 ? vl.maxDepth : MAX_RECURSIVE_DEPTH;
3021
+ const maxDepthCondition = drizzleOrm.sql`r.depth < ${effectiveMaxDepth}`;
3022
+ const cycleCheck = shouldEnforceCycleCheck ? dialect.cycleCheck(drizzleOrm.sql.raw("n.id"), drizzleOrm.sql.raw("r.path")) : void 0;
3023
+ const initialPath = shouldTrackPath ? dialect.initializePath(drizzleOrm.sql.raw("n0.id")) : void 0;
3024
+ const pathExtension = shouldTrackPath ? dialect.extendPath(drizzleOrm.sql.raw("r.path"), drizzleOrm.sql.raw("n.id")) : void 0;
2411
3025
  const baseWhereClauses = [
3026
+ drizzleOrm.sql`n0.graph_id = ${graphId}`,
3027
+ startKindFilter,
3028
+ startTemporalFilter,
3029
+ ...startPredicates
3030
+ ];
3031
+ const recursiveBaseWhereClauses = [
2412
3032
  drizzleOrm.sql`e.graph_id = ${graphId}`,
2413
3033
  nodeKindFilter,
2414
3034
  edgeTemporalFilter,
2415
3035
  nodeTemporalFilter,
2416
- ...nodePredicateClauses,
2417
- ...edgePredicateClauses
2418
- ];
2419
- const previousAlias = traversal.joinFromAlias;
2420
- const edgeAlias = traversal.edgeAlias;
2421
- const nodeAlias = traversal.nodeAlias;
2422
- const requiredNodeColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(nodeAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
2423
- const requiredEdgeColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(edgeAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
2424
- const previousRowColumns = carryForwardPreviousColumns ? [drizzleOrm.sql`cte_${drizzleOrm.sql.raw(previousAlias)}.*`] : [
2425
- drizzleOrm.sql`cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id AS ${drizzleOrm.sql.raw(previousAlias)}_id`,
2426
- drizzleOrm.sql`cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind AS ${drizzleOrm.sql.raw(previousAlias)}_kind`
2427
- ];
2428
- const selectColumns = [
2429
- ...previousRowColumns,
2430
- ...compileEdgeSelectColumns("e", edgeAlias, requiredEdgeColumns),
2431
- ...compileNodeSelectColumns("n", nodeAlias, requiredNodeColumns)
3036
+ maxDepthCondition
2432
3037
  ];
2433
- const cteMaterialization = materializeCte ? drizzleOrm.sql`MATERIALIZED ` : drizzleOrm.sql``;
2434
- function compileTraversalBranch(branch) {
2435
- const whereClauses = [
2436
- ...baseWhereClauses,
2437
- compileKindFilter(drizzleOrm.sql.raw("e.kind"), branch.edgeKinds),
2438
- compileKindFilter(
2439
- drizzleOrm.sql.raw(`e.${branch.joinKindField}`),
2440
- previousNodeKinds
2441
- ),
2442
- compileKindFilter(drizzleOrm.sql.raw(`e.${branch.targetKindField}`), nodeKinds)
3038
+ if (cycleCheck !== void 0) {
3039
+ recursiveBaseWhereClauses.push(cycleCheck);
3040
+ }
3041
+ recursiveBaseWhereClauses.push(...edgePredicates, ...targetNodePredicates);
3042
+ function compileRecursiveBranch2(branch) {
3043
+ const recursiveFilterClauses = [
3044
+ ...recursiveBaseWhereClauses,
3045
+ compileKindFilter2(branch.edgeKinds, "e.kind"),
3046
+ compileKindFilter2(previousNodeKinds, `e.${branch.joinKindField}`),
3047
+ compileKindFilter2(nodeKinds, `e.${branch.targetKindField}`)
2443
3048
  ];
2444
3049
  if (branch.duplicateGuard !== void 0) {
2445
- whereClauses.push(branch.duplicateGuard);
3050
+ recursiveFilterClauses.push(branch.duplicateGuard);
2446
3051
  }
2447
- return drizzleOrm.sql`
2448
- SELECT ${drizzleOrm.sql.join(selectColumns, drizzleOrm.sql`, `)}
2449
- FROM cte_${drizzleOrm.sql.raw(previousAlias)}
2450
- JOIN ${ctx.schema.edgesTable} e ON cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id = e.${drizzleOrm.sql.raw(branch.joinField)}
2451
- AND cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind = e.${drizzleOrm.sql.raw(branch.joinKindField)}
3052
+ const recursiveSelectColumns = [
3053
+ ...startColumnsFromRecursive,
3054
+ ...nodeColumnsFromRecursive,
3055
+ drizzleOrm.sql`r.depth + 1 AS depth`
3056
+ ];
3057
+ if (pathExtension !== void 0) {
3058
+ recursiveSelectColumns.push(drizzleOrm.sql`${pathExtension} AS path`);
3059
+ }
3060
+ const recursiveJoinClauses = [
3061
+ drizzleOrm.sql`e.${drizzleOrm.sql.raw(branch.joinField)} = r.${drizzleOrm.sql.raw(nodeAlias)}_id`
3062
+ ];
3063
+ if (previousNodeKinds.length > 1) {
3064
+ recursiveJoinClauses.push(
3065
+ drizzleOrm.sql`e.${drizzleOrm.sql.raw(branch.joinKindField)} = r.${drizzleOrm.sql.raw(nodeAlias)}_kind`
3066
+ );
3067
+ }
3068
+ if (forceWorktableOuterJoinOrder) {
3069
+ const recursiveWhereClauses = [
3070
+ ...recursiveJoinClauses,
3071
+ ...recursiveFilterClauses
3072
+ ];
3073
+ return drizzleOrm.sql`
3074
+ SELECT ${drizzleOrm.sql.join(recursiveSelectColumns, drizzleOrm.sql`, `)}
3075
+ FROM recursive_cte r
3076
+ CROSS JOIN ${ctx.schema.edgesTable} e
3077
+ JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
3078
+ AND n.id = e.${drizzleOrm.sql.raw(branch.targetField)}
3079
+ AND n.kind = e.${drizzleOrm.sql.raw(branch.targetKindField)}
3080
+ WHERE ${drizzleOrm.sql.join(recursiveWhereClauses, drizzleOrm.sql` AND `)}
3081
+ `;
3082
+ }
3083
+ return drizzleOrm.sql`
3084
+ SELECT ${drizzleOrm.sql.join(recursiveSelectColumns, drizzleOrm.sql`, `)}
3085
+ FROM recursive_cte r
3086
+ JOIN ${ctx.schema.edgesTable} e ON ${drizzleOrm.sql.join(recursiveJoinClauses, drizzleOrm.sql` AND `)}
2452
3087
  JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
2453
3088
  AND n.id = e.${drizzleOrm.sql.raw(branch.targetField)}
2454
3089
  AND n.kind = e.${drizzleOrm.sql.raw(branch.targetKindField)}
2455
- WHERE ${drizzleOrm.sql.join(whereClauses, drizzleOrm.sql` AND `)}
3090
+ WHERE ${drizzleOrm.sql.join(recursiveFilterClauses, drizzleOrm.sql` AND `)}
2456
3091
  `;
2457
3092
  }
2458
- const directJoinField = traversal.direction === "out" ? "from_id" : "to_id";
2459
- const directTargetField = traversal.direction === "out" ? "to_id" : "from_id";
2460
- const directJoinKindField = traversal.direction === "out" ? "from_kind" : "to_kind";
2461
- const directTargetKindField = traversal.direction === "out" ? "to_kind" : "from_kind";
2462
- const directBranch = compileTraversalBranch({
2463
- edgeKinds: directEdgeKinds,
3093
+ const directJoinField = direction === "out" ? "from_id" : "to_id";
3094
+ const directTargetField = direction === "out" ? "to_id" : "from_id";
3095
+ const directJoinKindField = direction === "out" ? "from_kind" : "to_kind";
3096
+ const directTargetKindField = direction === "out" ? "to_kind" : "from_kind";
3097
+ const directBranch = compileRecursiveBranch2({
2464
3098
  joinField: directJoinField,
2465
- joinKindField: directJoinKindField,
2466
3099
  targetField: directTargetField,
2467
- targetKindField: directTargetKindField
3100
+ joinKindField: directJoinKindField,
3101
+ targetKindField: directTargetKindField,
3102
+ edgeKinds: directEdgeKinds
2468
3103
  });
2469
- if (inverseEdgeKinds.length === 0) {
2470
- if (traversalLimitValue !== void 0) {
2471
- return drizzleOrm.sql`
2472
- cte_${drizzleOrm.sql.raw(nodeAlias)} AS ${cteMaterialization}(
2473
- SELECT * FROM (
2474
- ${directBranch}
2475
- ) AS traversal_rows
2476
- LIMIT ${traversalLimitValue}
2477
- )
2478
- `;
2479
- }
3104
+ function compileInverseRecursiveBranch() {
3105
+ const inverseJoinField = direction === "out" ? "to_id" : "from_id";
3106
+ const inverseTargetField = direction === "out" ? "from_id" : "to_id";
3107
+ const inverseJoinKindField = direction === "out" ? "to_kind" : "from_kind";
3108
+ const inverseTargetKindField = direction === "out" ? "from_kind" : "to_kind";
3109
+ const overlappingKinds = inverseEdgeKinds.filter(
3110
+ (kind) => directEdgeKinds.includes(kind)
3111
+ );
3112
+ const duplicateGuard = overlappingKinds.length > 0 ? drizzleOrm.sql`NOT (e.from_id = e.to_id AND ${compileKindFilter2(overlappingKinds, "e.kind")})` : void 0;
3113
+ const inverseBranch = compileRecursiveBranch2({
3114
+ joinField: inverseJoinField,
3115
+ targetField: inverseTargetField,
3116
+ joinKindField: inverseJoinKindField,
3117
+ targetKindField: inverseTargetKindField,
3118
+ edgeKinds: inverseEdgeKinds,
3119
+ duplicateGuard
3120
+ });
2480
3121
  return drizzleOrm.sql`
2481
- cte_${drizzleOrm.sql.raw(nodeAlias)} AS ${cteMaterialization}(
2482
- ${directBranch}
2483
- )
3122
+ ${directBranch}
3123
+ UNION ALL
3124
+ ${inverseBranch}
2484
3125
  `;
2485
3126
  }
2486
- const inverseJoinField = traversal.direction === "out" ? "to_id" : "from_id";
2487
- const inverseTargetField = traversal.direction === "out" ? "from_id" : "to_id";
2488
- const inverseJoinKindField = traversal.direction === "out" ? "to_kind" : "from_kind";
2489
- const inverseTargetKindField = traversal.direction === "out" ? "from_kind" : "to_kind";
2490
- const overlappingKinds = inverseEdgeKinds.filter(
2491
- (kind) => directEdgeKinds.includes(kind)
2492
- );
2493
- const duplicateGuard = overlappingKinds.length > 0 ? drizzleOrm.sql`NOT (e.from_id = e.to_id AND ${compileKindFilter(
2494
- drizzleOrm.sql.raw("e.kind"),
2495
- overlappingKinds
2496
- )})` : void 0;
2497
- const inverseBranch = compileTraversalBranch({
2498
- duplicateGuard,
2499
- edgeKinds: inverseEdgeKinds,
2500
- joinField: inverseJoinField,
2501
- joinKindField: inverseJoinKindField,
2502
- targetField: inverseTargetField,
2503
- targetKindField: inverseTargetKindField
2504
- });
2505
- if (traversalLimitValue !== void 0) {
2506
- return drizzleOrm.sql`
2507
- cte_${drizzleOrm.sql.raw(nodeAlias)} AS ${cteMaterialization}(
2508
- SELECT * FROM (
2509
- ${directBranch}
2510
- UNION ALL
2511
- ${inverseBranch}
2512
- ) AS traversal_rows
2513
- LIMIT ${traversalLimitValue}
2514
- )
2515
- `;
3127
+ const recursiveBranchSql = inverseEdgeKinds.length === 0 ? directBranch : compileInverseRecursiveBranch();
3128
+ const baseSelectColumns = [
3129
+ ...startColumnsFromBase,
3130
+ ...nodeColumnsFromBase,
3131
+ drizzleOrm.sql`0 AS depth`
3132
+ ];
3133
+ if (initialPath !== void 0) {
3134
+ baseSelectColumns.push(drizzleOrm.sql`${initialPath} AS path`);
2516
3135
  }
2517
3136
  return drizzleOrm.sql`
2518
- cte_${drizzleOrm.sql.raw(nodeAlias)} AS ${cteMaterialization}(
2519
- ${directBranch}
3137
+ recursive_cte AS (
3138
+ -- Base case: starting nodes
3139
+ SELECT ${drizzleOrm.sql.join(baseSelectColumns, drizzleOrm.sql`, `)}
3140
+ FROM ${ctx.schema.nodesTable} n0
3141
+ WHERE ${drizzleOrm.sql.join(baseWhereClauses, drizzleOrm.sql` AND `)}
3142
+
2520
3143
  UNION ALL
2521
- ${inverseBranch}
3144
+
3145
+ -- Recursive case: follow edges
3146
+ ${recursiveBranchSql}
2522
3147
  )
2523
3148
  `;
2524
3149
  }
2525
- function compileAggregateExprFromSource(expr, dialect) {
2526
- const { field: field2 } = expr;
2527
- const fn = expr.function;
2528
- switch (fn) {
2529
- case "count":
2530
- case "countDistinct":
2531
- case "sum":
2532
- case "avg":
2533
- case "min":
2534
- case "max": {
2535
- const cteAlias = `cte_${field2.alias}`;
2536
- const column = compileFieldValue(
2537
- field2,
2538
- dialect,
2539
- field2.valueType,
2540
- cteAlias
2541
- );
2542
- if (fn === "countDistinct") {
2543
- return drizzleOrm.sql`COUNT(DISTINCT ${column})`;
2544
- }
2545
- return drizzleOrm.sql`${drizzleOrm.sql.raw(fn.toUpperCase())}(${column})`;
2546
- }
2547
- default: {
2548
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2549
- `Unknown aggregate function: ${String(fn)}`
2550
- );
3150
+ function compileKindFilter2(kinds, columnExpr) {
3151
+ return compileKindFilter(drizzleOrm.sql.raw(columnExpr), kinds);
3152
+ }
3153
+ function compileNodePredicates(ast, alias, ctx) {
3154
+ return ast.predicates.filter((p) => p.targetAlias === alias && p.targetType !== "edge").map((p) => compilePredicateExpression(p.expression, ctx));
3155
+ }
3156
+ function compileEdgePredicates(ast, edgeAlias, ctx) {
3157
+ return ast.predicates.filter((p) => p.targetAlias === edgeAlias && p.targetType === "edge").map((p) => compilePredicateExpression(p.expression, ctx));
3158
+ }
3159
+ function collectRequiredColumnsByAlias(ast, traversal) {
3160
+ const selectiveFields = ast.selectiveFields;
3161
+ if (selectiveFields === void 0 || selectiveFields.length === 0) {
3162
+ return void 0;
3163
+ }
3164
+ const requiredColumnsByAlias = /* @__PURE__ */ new Map();
3165
+ const previousNodeKinds = [
3166
+ .../* @__PURE__ */ new Set([...ast.start.kinds, ...traversal.nodeKinds])
3167
+ ];
3168
+ addRequiredColumn(requiredColumnsByAlias, traversal.nodeAlias, "id");
3169
+ if (previousNodeKinds.length > 1) {
3170
+ addRequiredColumn(requiredColumnsByAlias, traversal.nodeAlias, "kind");
3171
+ }
3172
+ for (const field2 of selectiveFields) {
3173
+ markSelectiveFieldAsRequired(requiredColumnsByAlias, field2);
3174
+ }
3175
+ if (ast.orderBy) {
3176
+ for (const orderSpec of ast.orderBy) {
3177
+ markFieldRefAsRequired(requiredColumnsByAlias, orderSpec.field);
2551
3178
  }
2552
3179
  }
3180
+ return requiredColumnsByAlias;
2553
3181
  }
2554
- function compileProjectedSource(field2, dialect) {
2555
- if (isAggregateExpr(field2.source)) {
2556
- return compileAggregateExprFromSource(field2.source, dialect);
2557
- }
2558
- const cteAlias = field2.cteAlias ?? `cte_${field2.source.alias}`;
2559
- return compileFieldValue(
2560
- field2.source,
2561
- dialect,
2562
- field2.source.valueType,
2563
- cteAlias
3182
+ function compileNodeSelectColumnsFromTable(tableAlias, alias, requiredColumns, alwaysRequiredColumns) {
3183
+ return NODE_COLUMNS.filter(
3184
+ (column) => shouldProjectColumn(requiredColumns, column, alwaysRequiredColumns)
3185
+ ).map(
3186
+ (column) => drizzleOrm.sql`${drizzleOrm.sql.raw(tableAlias)}.${drizzleOrm.sql.raw(column)} AS ${drizzleOrm.sql.raw(`${alias}_${column}`)}`
2564
3187
  );
2565
3188
  }
2566
- function buildStandardProjection(input) {
2567
- const { ast, collapsedTraversalCteAlias, dialect } = input;
3189
+ function compileNodeSelectColumnsFromRecursiveRow(alias, requiredColumns, alwaysRequiredColumns) {
3190
+ return NODE_COLUMNS.filter(
3191
+ (column) => shouldProjectColumn(requiredColumns, column, alwaysRequiredColumns)
3192
+ ).map((column) => {
3193
+ const projected = `${alias}_${column}`;
3194
+ return drizzleOrm.sql`r.${drizzleOrm.sql.raw(projected)} AS ${drizzleOrm.sql.raw(projected)}`;
3195
+ });
3196
+ }
3197
+ function compileRecursiveProjection(ast, traversal, dialect) {
2568
3198
  if (ast.selectiveFields && ast.selectiveFields.length > 0) {
2569
- return compileSelectiveProjection(
3199
+ return compileRecursiveSelectiveProjection(
2570
3200
  ast.selectiveFields,
2571
- dialect,
2572
3201
  ast,
2573
- collapsedTraversalCteAlias
3202
+ traversal,
3203
+ dialect
2574
3204
  );
2575
3205
  }
2576
- const fields = ast.projection.fields;
2577
- if (fields.length === 0) {
2578
- return drizzleOrm.sql.raw("*");
3206
+ const startAlias = ast.start.alias;
3207
+ const nodeAlias = traversal.nodeAlias;
3208
+ const vl = traversal.variableLength;
3209
+ const fields = [
3210
+ // Start alias fields with metadata
3211
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_id`,
3212
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_kind`,
3213
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_props`,
3214
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_version`,
3215
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_valid_from`,
3216
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_valid_to`,
3217
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_created_at`,
3218
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_updated_at`,
3219
+ drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_deleted_at`,
3220
+ // Node alias fields with metadata
3221
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_id`,
3222
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_kind`,
3223
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_props`,
3224
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_version`,
3225
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_valid_from`,
3226
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_valid_to`,
3227
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_created_at`,
3228
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_updated_at`,
3229
+ drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_deleted_at`
3230
+ ];
3231
+ if (vl.depthAlias !== void 0) {
3232
+ fields.push(drizzleOrm.sql`depth AS ${quoteIdentifier(vl.depthAlias)}`);
2579
3233
  }
2580
- const projectedFields = fields.map((field2) => {
2581
- const source = compileProjectedSource(field2, dialect);
2582
- return drizzleOrm.sql`${source} AS ${quoteIdentifier(field2.outputName)}`;
2583
- });
2584
- return drizzleOrm.sql.join(projectedFields, drizzleOrm.sql`, `);
2585
- }
2586
- function compileSelectiveProjection(fields, dialect, ast, collapsedTraversalCteAlias) {
2587
- const aliasToCte = /* @__PURE__ */ new Map([
2588
- [ast.start.alias, `cte_${ast.start.alias}`]
2589
- ]);
2590
- for (const traversal of ast.traversals) {
2591
- aliasToCte.set(traversal.nodeAlias, `cte_${traversal.nodeAlias}`);
2592
- aliasToCte.set(traversal.edgeAlias, `cte_${traversal.nodeAlias}`);
3234
+ if (vl.pathAlias !== void 0) {
3235
+ fields.push(drizzleOrm.sql`path AS ${quoteIdentifier(vl.pathAlias)}`);
2593
3236
  }
3237
+ return drizzleOrm.sql.join(fields, drizzleOrm.sql`, `);
3238
+ }
3239
+ function compileRecursiveSelectiveProjection(fields, ast, traversal, dialect) {
3240
+ const allowedAliases = /* @__PURE__ */ new Set([ast.start.alias, traversal.nodeAlias]);
2594
3241
  const columns = fields.map((field2) => {
2595
- const cteAlias = collapsedTraversalCteAlias ?? aliasToCte.get(field2.alias) ?? `cte_${field2.alias}`;
3242
+ if (!allowedAliases.has(field2.alias)) {
3243
+ throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
3244
+ `Selective projection for recursive traversals does not support alias "${field2.alias}"`
3245
+ );
3246
+ }
2596
3247
  if (field2.isSystemField) {
2597
3248
  const dbColumn = mapSelectiveSystemFieldToColumn(field2.field);
2598
- return drizzleOrm.sql`${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(`${field2.alias}_${dbColumn}`)} AS ${quoteIdentifier(field2.outputName)}`;
3249
+ return drizzleOrm.sql`${drizzleOrm.sql.raw(`${field2.alias}_${dbColumn}`)} AS ${quoteIdentifier(field2.outputName)}`;
2599
3250
  }
2600
- const propsColumn = `${field2.alias}_props`;
2601
- const column = drizzleOrm.sql`${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(propsColumn)}`;
2602
- const pointer = chunkP5CNM325_cjs.jsonPointer([field2.field]);
3251
+ const column = drizzleOrm.sql.raw(`${field2.alias}_props`);
2603
3252
  const extracted = compileTypedJsonExtract({
2604
3253
  column,
2605
3254
  dialect,
2606
- pointer,
3255
+ pointer: chunkP5CNM325_cjs.jsonPointer([field2.field]),
2607
3256
  valueType: field2.valueType
2608
3257
  });
2609
3258
  return drizzleOrm.sql`${extracted} AS ${quoteIdentifier(field2.outputName)}`;
2610
3259
  });
2611
- return drizzleOrm.sql.join(columns, drizzleOrm.sql`, `);
2612
- }
2613
- function buildStandardFromClause(input) {
2614
- const { ast, collapsedTraversalCteAlias, vectorPredicate } = input;
2615
- if (collapsedTraversalCteAlias !== void 0) {
2616
- return drizzleOrm.sql`FROM ${drizzleOrm.sql.raw(collapsedTraversalCteAlias)}`;
2617
- }
2618
- const startAlias = ast.start.alias;
2619
- const fromClause = drizzleOrm.sql`FROM cte_${drizzleOrm.sql.raw(startAlias)}`;
2620
- const joins = [];
2621
- for (const traversal of ast.traversals) {
2622
- const cteAlias = `cte_${traversal.nodeAlias}`;
2623
- const previousAlias = traversal.joinFromAlias;
2624
- const joinType = traversal.optional ? "LEFT JOIN" : "INNER JOIN";
2625
- joins.push(
2626
- drizzleOrm.sql`${drizzleOrm.sql.raw(joinType)} ${drizzleOrm.sql.raw(cteAlias)} ON ${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id = cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id AND ${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind = cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind`
2627
- );
3260
+ const vl = traversal.variableLength;
3261
+ if (vl.depthAlias !== void 0) {
3262
+ columns.push(drizzleOrm.sql`depth AS ${quoteIdentifier(vl.depthAlias)}`);
2628
3263
  }
2629
- if (vectorPredicate) {
2630
- const nodeAlias = vectorPredicate.field.alias;
2631
- joins.push(
2632
- drizzleOrm.sql`INNER JOIN cte_embeddings ON cte_embeddings.node_id = cte_${drizzleOrm.sql.raw(nodeAlias)}.${drizzleOrm.sql.raw(nodeAlias)}_id`
2633
- );
3264
+ if (vl.pathAlias !== void 0) {
3265
+ columns.push(drizzleOrm.sql`path AS ${quoteIdentifier(vl.pathAlias)}`);
2634
3266
  }
2635
- return joins.length === 0 ? fromClause : drizzleOrm.sql`${fromClause} ${drizzleOrm.sql.join(joins, drizzleOrm.sql` `)}`;
3267
+ return drizzleOrm.sql.join(columns, drizzleOrm.sql`, `);
2636
3268
  }
2637
- function buildStandardOrderBy(input) {
2638
- const { ast, collapsedTraversalCteAlias, dialect } = input;
3269
+ function compileRecursiveOrderBy(ast, dialect) {
2639
3270
  if (!ast.orderBy || ast.orderBy.length === 0) {
2640
3271
  return void 0;
2641
3272
  }
@@ -2647,13 +3278,7 @@ function buildStandardOrderBy(input) {
2647
3278
  "Ordering by JSON arrays or objects is not supported"
2648
3279
  );
2649
3280
  }
2650
- const cteAlias = collapsedTraversalCteAlias ?? `cte_${orderSpec.field.alias}`;
2651
- const field2 = compileFieldValue(
2652
- orderSpec.field,
2653
- dialect,
2654
- valueType,
2655
- cteAlias
2656
- );
3281
+ const field2 = compileFieldValue(orderSpec.field, dialect, valueType);
2657
3282
  const direction = drizzleOrm.sql.raw(orderSpec.direction.toUpperCase());
2658
3283
  const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
2659
3284
  const nullsDirection = drizzleOrm.sql.raw(nulls === "first" ? "DESC" : "ASC");
@@ -2664,677 +3289,585 @@ function buildStandardOrderBy(input) {
2664
3289
  }
2665
3290
  return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join(parts, drizzleOrm.sql`, `)}`;
2666
3291
  }
2667
- function fieldRefKey(field2) {
2668
- const pointer = field2.jsonPointer ?? "";
2669
- return `${field2.alias}:${field2.path.join(".")}:${pointer}`;
2670
- }
2671
- function buildStandardGroupBy(input) {
2672
- const { ast, dialect } = input;
2673
- if (!ast.groupBy || ast.groupBy.fields.length === 0) {
2674
- return void 0;
2675
- }
2676
- const seenKeys = /* @__PURE__ */ new Set();
2677
- const allFields = [];
2678
- for (const projectedField of ast.projection.fields) {
2679
- if (projectedField.source.__type === "field_ref") {
2680
- const key = fieldRefKey(projectedField.source);
2681
- if (!seenKeys.has(key)) {
2682
- seenKeys.add(key);
2683
- allFields.push(projectedField.source);
2684
- }
2685
- }
2686
- }
2687
- for (const field2 of ast.groupBy.fields) {
2688
- const key = fieldRefKey(field2);
2689
- if (!seenKeys.has(key)) {
2690
- seenKeys.add(key);
2691
- allFields.push(field2);
2692
- }
3292
+ function compileLimitOffset(ast) {
3293
+ const parts = [];
3294
+ if (ast.limit !== void 0) {
3295
+ parts.push(drizzleOrm.sql`LIMIT ${ast.limit}`);
2693
3296
  }
2694
- if (allFields.length === 0) {
2695
- return void 0;
3297
+ if (ast.offset !== void 0) {
3298
+ parts.push(drizzleOrm.sql`OFFSET ${ast.offset}`);
2696
3299
  }
2697
- const parts = allFields.map(
2698
- (field2) => compileFieldValue(field2, dialect, field2.valueType, `cte_${field2.alias}`)
2699
- );
2700
- return drizzleOrm.sql`GROUP BY ${drizzleOrm.sql.join(parts, drizzleOrm.sql`, `)}`;
3300
+ return parts.length > 0 ? drizzleOrm.sql.join(parts, drizzleOrm.sql` `) : void 0;
2701
3301
  }
2702
- function buildStandardHaving(input) {
2703
- const { ast, ctx } = input;
2704
- if (!ast.having) {
2705
- return void 0;
3302
+ var DEFAULT_TABLE_NAMES = {
3303
+ nodes: "typegraph_nodes",
3304
+ edges: "typegraph_edges",
3305
+ embeddings: "typegraph_node_embeddings"
3306
+ };
3307
+ var MAX_IDENTIFIER_LENGTH = 63;
3308
+ var VALID_IDENTIFIER_PATTERN = /^[a-z_][a-z0-9_$]*$/i;
3309
+ function validateTableName(name, label) {
3310
+ if (!name || name.length === 0) {
3311
+ throw new chunk44SXEVF4_cjs.ConfigurationError(`${label} table name cannot be empty`);
2706
3312
  }
2707
- const condition = compilePredicateExpression(ast.having, ctx);
2708
- return drizzleOrm.sql`HAVING ${condition}`;
2709
- }
2710
- function buildStandardEmbeddingsCte(input) {
2711
- const { ctx, graphId, vectorPredicate } = input;
2712
- const { dialect } = ctx;
2713
- const { field: field2, metric, minScore, queryEmbedding } = vectorPredicate;
2714
- const fieldPath = field2.jsonPointer ? field2.jsonPointer : field2.path.length > 1 && field2.path[0] === "props" ? `/${field2.path.slice(1).join("/")}` : `/${field2.path.join("/")}`;
2715
- const distanceExpr = dialect.vectorDistance(
2716
- drizzleOrm.sql.raw("embedding"),
2717
- queryEmbedding,
2718
- metric
2719
- );
2720
- const conditions = [
2721
- drizzleOrm.sql`graph_id = ${graphId}`,
2722
- drizzleOrm.sql`field_path = ${fieldPath}`
2723
- ];
2724
- if (minScore !== void 0) {
2725
- conditions.push(
2726
- compileVectorMinScoreCondition(distanceExpr, metric, minScore)
3313
+ if (name.length > MAX_IDENTIFIER_LENGTH) {
3314
+ throw new chunk44SXEVF4_cjs.ConfigurationError(
3315
+ `${label} table name exceeds maximum length of ${MAX_IDENTIFIER_LENGTH} characters`
2727
3316
  );
2728
3317
  }
2729
- const scoreExpr = compileVectorScoreExpression(distanceExpr, metric);
2730
- return drizzleOrm.sql`
2731
- cte_embeddings AS (
2732
- SELECT
2733
- node_id,
2734
- ${distanceExpr} AS distance,
2735
- ${scoreExpr} AS score
2736
- FROM ${ctx.schema.embeddingsTable}
2737
- WHERE ${drizzleOrm.sql.join(conditions, drizzleOrm.sql` AND `)}
2738
- ORDER BY ${distanceExpr} ASC
2739
- )
2740
- `;
2741
- }
2742
- function compileVectorScoreExpression(distanceExpr, metric) {
2743
- switch (metric) {
2744
- case "cosine": {
2745
- return drizzleOrm.sql`(1.0 - ${distanceExpr})`;
2746
- }
2747
- case "l2":
2748
- case "inner_product": {
2749
- return distanceExpr;
2750
- }
3318
+ if (!VALID_IDENTIFIER_PATTERN.test(name)) {
3319
+ throw new chunk44SXEVF4_cjs.ConfigurationError(
3320
+ `${label} table name "${name}" is not a valid SQL identifier. Table names must start with a letter or underscore and contain only letters, digits, underscores, or dollar signs.`
3321
+ );
2751
3322
  }
2752
3323
  }
2753
- function compileVectorMinScoreCondition(distanceExpr, metric, minScore) {
2754
- switch (metric) {
2755
- case "cosine": {
2756
- const threshold = 1 - minScore;
2757
- return drizzleOrm.sql`${distanceExpr} <= ${threshold}`;
2758
- }
2759
- case "l2": {
2760
- return drizzleOrm.sql`${distanceExpr} <= ${minScore}`;
2761
- }
2762
- case "inner_product": {
2763
- const negativeThreshold = -minScore;
2764
- return drizzleOrm.sql`${distanceExpr} <= ${negativeThreshold}`;
2765
- }
2766
- }
3324
+ function quoteIdentifier2(name) {
3325
+ return `"${name.replaceAll('"', '""')}"`;
2767
3326
  }
2768
- function buildStandardVectorOrderBy(input) {
2769
- const { ast, dialect } = input;
2770
- const distanceOrder = drizzleOrm.sql`cte_embeddings.distance ASC`;
2771
- const additionalOrders = [];
2772
- if (ast.orderBy && ast.orderBy.length > 0) {
2773
- for (const orderSpec of ast.orderBy) {
2774
- const valueType = orderSpec.field.valueType;
2775
- if (valueType === "array" || valueType === "object") {
2776
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
2777
- "Ordering by JSON arrays or objects is not supported"
2778
- );
2779
- }
2780
- const cteAlias = `cte_${orderSpec.field.alias}`;
2781
- const field2 = compileFieldValue(
2782
- orderSpec.field,
2783
- dialect,
2784
- valueType,
2785
- cteAlias
2786
- );
2787
- const direction = drizzleOrm.sql.raw(orderSpec.direction.toUpperCase());
2788
- const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
2789
- const nullsDirection = drizzleOrm.sql.raw(nulls === "first" ? "DESC" : "ASC");
2790
- additionalOrders.push(
2791
- drizzleOrm.sql`(${field2} IS NULL) ${nullsDirection}`,
2792
- drizzleOrm.sql`${field2} ${direction}`
2793
- );
2794
- }
2795
- }
2796
- const allOrders = [distanceOrder, ...additionalOrders];
2797
- return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join(allOrders, drizzleOrm.sql`, `)}`;
3327
+ function createSqlSchema(names = {}) {
3328
+ const tables = { ...DEFAULT_TABLE_NAMES, ...names };
3329
+ validateTableName(tables.nodes, "nodes");
3330
+ validateTableName(tables.edges, "edges");
3331
+ validateTableName(tables.embeddings, "embeddings");
3332
+ return {
3333
+ tables,
3334
+ nodesTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.nodes)),
3335
+ edgesTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.edges)),
3336
+ embeddingsTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.embeddings))
3337
+ };
2798
3338
  }
2799
- function buildLimitOffsetClause(input) {
2800
- const { limit, offset } = input;
2801
- const parts = [];
2802
- if (limit !== void 0) {
2803
- parts.push(drizzleOrm.sql`LIMIT ${limit}`);
2804
- }
2805
- if (offset !== void 0) {
2806
- parts.push(drizzleOrm.sql`OFFSET ${offset}`);
3339
+ var DEFAULT_SQL_SCHEMA = createSqlSchema();
3340
+
3341
+ // src/query/execution/value-decoder.ts
3342
+ function nullToUndefined(value) {
3343
+ return value === null ? void 0 : value;
3344
+ }
3345
+ function decodeSelectedValue(value, typeInfo) {
3346
+ const normalized = nullToUndefined(value);
3347
+ if (normalized === void 0) return void 0;
3348
+ if (typeInfo === void 0) {
3349
+ return normalized;
2807
3350
  }
2808
- return parts.length > 0 ? drizzleOrm.sql.join(parts, drizzleOrm.sql` `) : void 0;
3351
+ return decodeByValueType(normalized, typeInfo.valueType);
2809
3352
  }
2810
-
2811
- // src/query/compiler/recursive.ts
2812
- var MAX_RECURSIVE_DEPTH = 100;
2813
- var MAX_EXPLICIT_RECURSIVE_DEPTH = 1e3;
2814
- var NO_ALWAYS_REQUIRED_COLUMNS = /* @__PURE__ */ new Set();
2815
- function runRecursiveQueryPassPipeline(ast, graphId, ctx) {
2816
- let state = {
2817
- ast,
2818
- ctx,
2819
- graphId,
2820
- logicalPlan: void 0,
2821
- requiredColumnsByAlias: void 0,
2822
- temporalFilterPass: void 0,
2823
- traversal: void 0
2824
- };
2825
- const recursiveTraversalPass = runCompilerPass(state, {
2826
- name: "recursive_traversal",
2827
- execute(currentState) {
2828
- return runRecursiveTraversalSelectionPass(currentState.ast);
2829
- },
2830
- update(currentState, traversal) {
2831
- return {
2832
- ...currentState,
2833
- traversal
2834
- };
3353
+ function decodeByValueType(value, valueType) {
3354
+ switch (valueType) {
3355
+ case "boolean": {
3356
+ if (typeof value === "boolean") return value;
3357
+ if (typeof value === "number") return value !== 0;
3358
+ if (typeof value === "string") {
3359
+ if (value === "0") return false;
3360
+ if (value === "1") return true;
3361
+ if (value.toLowerCase() === "true") return true;
3362
+ if (value.toLowerCase() === "false") return false;
3363
+ }
3364
+ return Boolean(value);
2835
3365
  }
2836
- });
2837
- state = recursiveTraversalPass.state;
2838
- const temporalPass = runCompilerPass(state, {
2839
- name: "temporal_filters",
2840
- execute(currentState) {
2841
- return createTemporalFilterPass(
2842
- currentState.ast,
2843
- currentState.ctx.dialect.currentTimestamp()
2844
- );
2845
- },
2846
- update(currentState, temporalFilterPass) {
2847
- return {
2848
- ...currentState,
2849
- temporalFilterPass
2850
- };
3366
+ case "number": {
3367
+ if (typeof value === "number") return value;
3368
+ if (typeof value === "string") {
3369
+ const parsed = Number(value);
3370
+ return Number.isNaN(parsed) ? value : parsed;
3371
+ }
3372
+ return value;
2851
3373
  }
2852
- });
2853
- state = temporalPass.state;
2854
- const columnPruningPass = runCompilerPass(state, {
2855
- name: "column_pruning",
2856
- execute(currentState) {
2857
- const traversal = currentState.traversal;
2858
- if (traversal === void 0) {
2859
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2860
- "Recursive traversal pass did not select traversal"
2861
- );
3374
+ case "array":
3375
+ case "object":
3376
+ case "embedding": {
3377
+ if (typeof value !== "string") return value;
3378
+ const trimmed = value.trim();
3379
+ const looksJson = trimmed.startsWith("[") || trimmed.startsWith("{");
3380
+ if (!looksJson) return value;
3381
+ try {
3382
+ return JSON.parse(trimmed);
3383
+ } catch {
3384
+ return value;
2862
3385
  }
2863
- return collectRequiredColumnsByAlias(currentState.ast, traversal);
2864
- },
2865
- update(currentState, requiredColumnsByAlias) {
2866
- return {
2867
- ...currentState,
2868
- requiredColumnsByAlias
2869
- };
2870
3386
  }
2871
- });
2872
- state = columnPruningPass.state;
2873
- const logicalPlanPass = runCompilerPass(state, {
2874
- name: "logical_plan",
2875
- execute(currentState) {
2876
- const loweringInput = {
2877
- ast: currentState.ast,
2878
- dialect: currentState.ctx.dialect.name,
2879
- graphId: currentState.graphId,
2880
- ...currentState.traversal === void 0 ? {} : { traversal: currentState.traversal }
2881
- };
2882
- return lowerRecursiveQueryToLogicalPlan(loweringInput);
2883
- },
2884
- update(currentState, logicalPlan) {
2885
- return {
2886
- ...currentState,
2887
- logicalPlan
2888
- };
3387
+ case "string":
3388
+ case "date":
3389
+ case "unknown": {
3390
+ return value;
2889
3391
  }
2890
- });
2891
- state = logicalPlanPass.state;
2892
- return state;
2893
- }
2894
- function compileVariableLengthQuery(ast, graphId, ctx) {
2895
- const strategy = ctx.dialect.capabilities.recursiveQueryStrategy;
2896
- const handler = RECURSIVE_QUERY_STRATEGY_HANDLERS[strategy];
2897
- return handler(ast, graphId, ctx);
3392
+ default: {
3393
+ return value;
3394
+ }
3395
+ }
2898
3396
  }
2899
- var RECURSIVE_QUERY_STRATEGY_HANDLERS = {
2900
- recursive_cte: compileVariableLengthQueryWithRecursiveCteStrategy
2901
- };
2902
- function compileVariableLengthQueryWithRecursiveCteStrategy(ast, graphId, ctx) {
2903
- const passState = runRecursiveQueryPassPipeline(ast, graphId, ctx);
2904
- const { dialect } = ctx;
2905
- const {
2906
- logicalPlan,
2907
- requiredColumnsByAlias,
2908
- temporalFilterPass,
2909
- traversal: vlTraversal
2910
- } = passState;
2911
- if (temporalFilterPass === void 0) {
2912
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2913
- "Temporal filter pass did not initialize temporal state"
3397
+
3398
+ // src/store/reserved-keys.ts
3399
+ var RESERVED_NODE_KEYS2 = /* @__PURE__ */ new Set([
3400
+ "id",
3401
+ "kind",
3402
+ "meta"
3403
+ ]);
3404
+ var RESERVED_EDGE_KEYS2 = /* @__PURE__ */ new Set([
3405
+ "id",
3406
+ "kind",
3407
+ "meta",
3408
+ "fromKind",
3409
+ "fromId",
3410
+ "toKind",
3411
+ "toId"
3412
+ ]);
3413
+ var PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set([
3414
+ "__proto__",
3415
+ "constructor",
3416
+ "prototype"
3417
+ ]);
3418
+ function validateProjectionField(field2, entityType, kind) {
3419
+ const reserved = entityType === "node" ? RESERVED_NODE_KEYS2 : RESERVED_EDGE_KEYS2;
3420
+ if (reserved.has(field2)) {
3421
+ throw new chunk44SXEVF4_cjs.ConfigurationError(
3422
+ `Projection field "${field2}" on ${entityType} kind "${kind}" conflicts with a reserved structural key`,
3423
+ { field: field2, kind, entityType, reservedKeys: [...reserved] },
3424
+ {
3425
+ suggestion: `Remove "${field2}" from the projection. Structural fields (${[...reserved].join(", ")}) are included automatically when relevant.`
3426
+ }
2914
3427
  );
2915
3428
  }
2916
- if (logicalPlan === void 0) {
2917
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2918
- "Logical plan pass did not initialize plan state"
3429
+ if (PROTOTYPE_POLLUTION_KEYS.has(field2)) {
3430
+ throw new chunk44SXEVF4_cjs.ConfigurationError(
3431
+ `Projection field "${field2}" on ${entityType} kind "${kind}" is not allowed`,
3432
+ { field: field2, kind, entityType },
3433
+ {
3434
+ suggestion: `"${field2}" cannot be used as a projection field name.`
3435
+ }
2919
3436
  );
2920
3437
  }
2921
- if (vlTraversal === void 0) {
2922
- throw new chunk44SXEVF4_cjs.CompilerInvariantError(
2923
- "Recursive traversal pass did not select traversal"
2924
- );
3438
+ }
3439
+ function filterReservedKeys(props, reservedKeys) {
3440
+ const filtered = {};
3441
+ for (const [key, value] of Object.entries(props)) {
3442
+ if (!reservedKeys.has(key)) {
3443
+ filtered[key] = value;
3444
+ }
2925
3445
  }
2926
- const recursiveCte = compileRecursiveCte(
2927
- ast,
2928
- vlTraversal,
2929
- graphId,
2930
- ctx,
2931
- requiredColumnsByAlias,
2932
- temporalFilterPass
2933
- );
2934
- const projection = compileRecursiveProjection(ast, vlTraversal, dialect);
2935
- const minDepth = vlTraversal.variableLength.minDepth;
2936
- const depthFilter = minDepth > 0 ? drizzleOrm.sql`WHERE depth >= ${minDepth}` : drizzleOrm.sql.raw("");
2937
- const orderBy = compileRecursiveOrderBy(ast, dialect);
2938
- const limitOffset = compileLimitOffset(ast);
2939
- return emitRecursiveQuerySql({
2940
- depthFilter,
2941
- ...limitOffset === void 0 ? {} : { limitOffset },
2942
- logicalPlan,
2943
- ...orderBy === void 0 ? {} : { orderBy },
2944
- projection,
2945
- recursiveCte
2946
- });
3446
+ return filtered;
2947
3447
  }
2948
- function hasVariableLengthTraversal(ast) {
2949
- return ast.traversals.some((t) => t.variableLength !== void 0);
3448
+
3449
+ // src/store/row-mappers.ts
3450
+ function nullToUndefined2(value) {
3451
+ return value === null ? void 0 : value;
2950
3452
  }
2951
- function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlias, temporalFilterPass) {
2952
- const { dialect } = ctx;
2953
- const startAlias = ast.start.alias;
2954
- const startKinds = ast.start.kinds;
2955
- const nodeAlias = traversal.nodeAlias;
2956
- const directEdgeKinds = [...new Set(traversal.edgeKinds)];
2957
- const inverseEdgeKinds = traversal.inverseEdgeKinds === void 0 ? [] : [...new Set(traversal.inverseEdgeKinds)];
2958
- const forceWorktableOuterJoinOrder = dialect.capabilities.forceRecursiveWorktableOuterJoinOrder;
2959
- const nodeKinds = traversal.nodeKinds;
2960
- const previousNodeKinds = [.../* @__PURE__ */ new Set([...startKinds, ...nodeKinds])];
2961
- const direction = traversal.direction;
2962
- const vl = traversal.variableLength;
2963
- const shouldEnforceCycleCheck = vl.cyclePolicy !== "allow";
2964
- const shouldTrackPath = shouldEnforceCycleCheck || vl.pathAlias !== void 0;
2965
- const recursiveJoinRequiredColumns = /* @__PURE__ */ new Set(["id"]);
2966
- if (previousNodeKinds.length > 1) {
2967
- recursiveJoinRequiredColumns.add("kind");
3453
+ function rowToNode(row) {
3454
+ const rawProps = JSON.parse(row.props);
3455
+ const props = filterReservedKeys(rawProps, RESERVED_NODE_KEYS2);
3456
+ return {
3457
+ kind: row.kind,
3458
+ id: row.id,
3459
+ meta: rowToNodeMeta(row),
3460
+ ...props
3461
+ };
3462
+ }
3463
+ function rowToNodeMeta(row) {
3464
+ return {
3465
+ version: row.version,
3466
+ validFrom: nullToUndefined2(row.valid_from),
3467
+ validTo: nullToUndefined2(row.valid_to),
3468
+ createdAt: row.created_at,
3469
+ updatedAt: row.updated_at,
3470
+ deletedAt: nullToUndefined2(row.deleted_at)
3471
+ };
3472
+ }
3473
+ function rowToEdge(row) {
3474
+ const rawProps = JSON.parse(row.props);
3475
+ const props = filterReservedKeys(rawProps, RESERVED_EDGE_KEYS2);
3476
+ return {
3477
+ id: row.id,
3478
+ kind: row.kind,
3479
+ fromKind: row.from_kind,
3480
+ fromId: row.from_id,
3481
+ toKind: row.to_kind,
3482
+ toId: row.to_id,
3483
+ meta: rowToEdgeMeta(row),
3484
+ ...props
3485
+ };
3486
+ }
3487
+ function rowToEdgeMeta(row) {
3488
+ return {
3489
+ validFrom: nullToUndefined2(row.valid_from),
3490
+ validTo: nullToUndefined2(row.valid_to),
3491
+ createdAt: row.created_at,
3492
+ updatedAt: row.updated_at,
3493
+ deletedAt: nullToUndefined2(row.deleted_at)
3494
+ };
3495
+ }
3496
+
3497
+ // src/store/subgraph.ts
3498
+ var DEFAULT_SUBGRAPH_MAX_DEPTH = 10;
3499
+ var MAX_PG_IDENTIFIER_LENGTH = 63;
3500
+ function fnv1aBase36(input) {
3501
+ let hash = 2166136261;
3502
+ for (const character of input) {
3503
+ const codePoint = character.codePointAt(0);
3504
+ if (codePoint === void 0) continue;
3505
+ hash ^= codePoint;
3506
+ hash = Math.imul(hash, 16777619);
3507
+ }
3508
+ return (hash >>> 0).toString(36);
3509
+ }
3510
+ var TEXT_ENCODER = new TextEncoder();
3511
+ function truncateToBytes(value, maxBytes) {
3512
+ const encoded = TEXT_ENCODER.encode(value);
3513
+ if (encoded.byteLength <= maxBytes) return value;
3514
+ let end = maxBytes;
3515
+ while (end > 0 && encoded[end] >= 128 && encoded[end] < 192) {
3516
+ end--;
3517
+ }
3518
+ return new TextDecoder().decode(encoded.slice(0, end));
3519
+ }
3520
+ function projectionAlias(entityPrefix, kind, field2) {
3521
+ const prefix = entityPrefix === "node" ? "sg_n" : "sg_e";
3522
+ const hash = fnv1aBase36(`${kind}\0${field2}`);
3523
+ const fixedBytes = prefix.length + 1 + 1 + hash.length;
3524
+ const maxKindBytes = MAX_PG_IDENTIFIER_LENGTH - fixedBytes;
3525
+ const truncatedKind = truncateToBytes(kind, maxKindBytes);
3526
+ return `${prefix}_${truncatedKind}_${hash}`;
3527
+ }
3528
+ function normalizeProps(value) {
3529
+ return typeof value === "string" ? value : JSON.stringify(value ?? {});
3530
+ }
3531
+ function defineSubgraphProject(_graph) {
3532
+ return (project) => project;
3533
+ }
3534
+ async function executeSubgraph(params) {
3535
+ const { options } = params;
3536
+ if (options.edges.length === 0) {
3537
+ return { nodes: [], edges: [] };
2968
3538
  }
2969
- const requiredStartColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(startAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
2970
- const requiredNodeColumns = requiredColumnsByAlias ? requiredColumnsByAlias.get(nodeAlias) ?? EMPTY_REQUIRED_COLUMNS : void 0;
2971
- const startColumnsFromBase = compileNodeSelectColumnsFromTable(
2972
- "n0",
2973
- startAlias,
2974
- requiredStartColumns,
2975
- NO_ALWAYS_REQUIRED_COLUMNS
3539
+ const maxDepth = Math.min(
3540
+ options.maxDepth ?? DEFAULT_SUBGRAPH_MAX_DEPTH,
3541
+ MAX_RECURSIVE_DEPTH
2976
3542
  );
2977
- const startColumnsFromRecursive = compileNodeSelectColumnsFromRecursiveRow(
2978
- startAlias,
2979
- requiredStartColumns,
2980
- NO_ALWAYS_REQUIRED_COLUMNS
3543
+ const ctx = {
3544
+ graphId: params.graphId,
3545
+ rootId: params.rootId,
3546
+ edgeKinds: options.edges,
3547
+ maxDepth,
3548
+ includeKinds: options.includeKinds,
3549
+ excludeRoot: options.excludeRoot ?? false,
3550
+ direction: options.direction ?? "out",
3551
+ cyclePolicy: options.cyclePolicy ?? "prevent",
3552
+ dialect: params.dialect,
3553
+ schema: params.schema ?? DEFAULT_SQL_SCHEMA,
3554
+ backend: params.backend
3555
+ };
3556
+ const schemaIntrospector = getSubgraphSchemaIntrospector(params.graph);
3557
+ const nodeProjectionPlan = buildProjectionPlan(
3558
+ getIncludedNodeKinds(params.graph, options.includeKinds),
3559
+ options.project?.nodes,
3560
+ (kind, field2) => schemaIntrospector.getFieldTypeInfo(kind, field2),
3561
+ "node"
2981
3562
  );
2982
- const nodeColumnsFromBase = compileNodeSelectColumnsFromTable(
2983
- "n0",
2984
- nodeAlias,
2985
- requiredNodeColumns,
2986
- recursiveJoinRequiredColumns
3563
+ const edgeProjectionPlan = buildProjectionPlan(
3564
+ dedupeStrings(options.edges),
3565
+ options.project?.edges,
3566
+ (kind, field2) => schemaIntrospector.getEdgeFieldTypeInfo(kind, field2),
3567
+ "edge"
3568
+ );
3569
+ const reachableCte = buildReachableCte(ctx);
3570
+ const includedIdsCte = buildIncludedIdsCte(ctx);
3571
+ const [nodeRows, edgeRows] = await Promise.all([
3572
+ fetchSubgraphNodes(ctx, reachableCte, includedIdsCte, nodeProjectionPlan),
3573
+ fetchSubgraphEdges(ctx, reachableCte, includedIdsCte, edgeProjectionPlan)
3574
+ ]);
3575
+ const nodes = nodeRows.map(
3576
+ (row) => mapSubgraphNodeRow(row, nodeProjectionPlan)
2987
3577
  );
2988
- const nodeColumnsFromRecursive = compileNodeSelectColumnsFromTable(
2989
- "n",
2990
- nodeAlias,
2991
- requiredNodeColumns,
2992
- recursiveJoinRequiredColumns
3578
+ const edges = edgeRows.map(
3579
+ (row) => mapSubgraphEdgeRow(row, edgeProjectionPlan)
2993
3580
  );
2994
- const startKindFilter = compileKindFilter2(startKinds, "n0.kind");
2995
- const nodeKindFilter = compileKindFilter2(nodeKinds, "n.kind");
2996
- const startTemporalFilter = temporalFilterPass.forAlias("n0");
2997
- const edgeTemporalFilter = temporalFilterPass.forAlias("e");
2998
- const nodeTemporalFilter = temporalFilterPass.forAlias("n");
2999
- const startContext = { ...ctx, cteColumnPrefix: "" };
3000
- const startPredicates = compileNodePredicates(ast, startAlias, startContext);
3001
- const edgeContext = { ...ctx, cteColumnPrefix: "e" };
3002
- const edgePredicates = compileEdgePredicates(
3003
- ast,
3004
- traversal.edgeAlias,
3005
- edgeContext
3581
+ return {
3582
+ nodes,
3583
+ edges
3584
+ };
3585
+ }
3586
+ var introspectorCache = /* @__PURE__ */ new WeakMap();
3587
+ function getSubgraphSchemaIntrospector(graph) {
3588
+ const cached = introspectorCache.get(graph);
3589
+ if (cached !== void 0) return cached;
3590
+ const nodeKinds = new Map(
3591
+ Object.entries(graph.nodes).map(([kind, definition]) => [
3592
+ kind,
3593
+ { schema: definition.type.schema }
3594
+ ])
3006
3595
  );
3007
- const targetContext = { ...ctx, cteColumnPrefix: "n" };
3008
- const targetNodePredicates = compileNodePredicates(
3009
- ast,
3010
- nodeAlias,
3011
- targetContext
3596
+ const edgeKinds = new Map(
3597
+ Object.entries(graph.edges).map(([kind, definition]) => [
3598
+ kind,
3599
+ { schema: definition.type.schema }
3600
+ ])
3012
3601
  );
3013
- if (vl.maxDepth > MAX_EXPLICIT_RECURSIVE_DEPTH) {
3014
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
3015
- `Recursive traversal maxHops(${vl.maxDepth}) exceeds maximum explicit depth of ${MAX_EXPLICIT_RECURSIVE_DEPTH}`
3602
+ const introspector = chunkP5CNM325_cjs.createSchemaIntrospector(nodeKinds, edgeKinds);
3603
+ introspectorCache.set(graph, introspector);
3604
+ return introspector;
3605
+ }
3606
+ function buildProjectionPlan(kinds, projectionMap, resolveFieldType, entityPrefix) {
3607
+ const projectedKinds = /* @__PURE__ */ new Map();
3608
+ const fullKinds = [];
3609
+ for (const kind of kinds) {
3610
+ const selection = projectionMap?.[kind];
3611
+ if (selection === void 0) {
3612
+ fullKinds.push(kind);
3613
+ continue;
3614
+ }
3615
+ projectedKinds.set(
3616
+ kind,
3617
+ buildKindProjectionPlan(kind, selection, resolveFieldType, entityPrefix)
3016
3618
  );
3017
3619
  }
3018
- const effectiveMaxDepth = vl.maxDepth > 0 ? vl.maxDepth : MAX_RECURSIVE_DEPTH;
3019
- const maxDepthCondition = drizzleOrm.sql`r.depth < ${effectiveMaxDepth}`;
3020
- const cycleCheck = shouldEnforceCycleCheck ? dialect.cycleCheck(drizzleOrm.sql.raw("n.id"), drizzleOrm.sql.raw("r.path")) : void 0;
3021
- const initialPath = shouldTrackPath ? dialect.initializePath(drizzleOrm.sql.raw("n0.id")) : void 0;
3022
- const pathExtension = shouldTrackPath ? dialect.extendPath(drizzleOrm.sql.raw("r.path"), drizzleOrm.sql.raw("n.id")) : void 0;
3023
- const baseWhereClauses = [
3024
- drizzleOrm.sql`n0.graph_id = ${graphId}`,
3025
- startKindFilter,
3026
- startTemporalFilter,
3027
- ...startPredicates
3028
- ];
3029
- const recursiveBaseWhereClauses = [
3030
- drizzleOrm.sql`e.graph_id = ${graphId}`,
3031
- nodeKindFilter,
3032
- edgeTemporalFilter,
3033
- nodeTemporalFilter,
3034
- maxDepthCondition
3035
- ];
3036
- if (cycleCheck !== void 0) {
3037
- recursiveBaseWhereClauses.push(cycleCheck);
3038
- }
3039
- recursiveBaseWhereClauses.push(...edgePredicates, ...targetNodePredicates);
3040
- function compileRecursiveBranch2(branch) {
3041
- const recursiveFilterClauses = [
3042
- ...recursiveBaseWhereClauses,
3043
- compileKindFilter2(branch.edgeKinds, "e.kind"),
3044
- compileKindFilter2(previousNodeKinds, `e.${branch.joinKindField}`),
3045
- compileKindFilter2(nodeKinds, `e.${branch.targetKindField}`)
3046
- ];
3047
- if (branch.duplicateGuard !== void 0) {
3048
- recursiveFilterClauses.push(branch.duplicateGuard);
3049
- }
3050
- const recursiveSelectColumns = [
3051
- ...startColumnsFromRecursive,
3052
- ...nodeColumnsFromRecursive,
3053
- drizzleOrm.sql`r.depth + 1 AS depth`
3054
- ];
3055
- if (pathExtension !== void 0) {
3056
- recursiveSelectColumns.push(drizzleOrm.sql`${pathExtension} AS path`);
3057
- }
3058
- const recursiveJoinClauses = [
3059
- drizzleOrm.sql`e.${drizzleOrm.sql.raw(branch.joinField)} = r.${drizzleOrm.sql.raw(nodeAlias)}_id`
3060
- ];
3061
- if (previousNodeKinds.length > 1) {
3062
- recursiveJoinClauses.push(
3063
- drizzleOrm.sql`e.${drizzleOrm.sql.raw(branch.joinKindField)} = r.${drizzleOrm.sql.raw(nodeAlias)}_kind`
3064
- );
3620
+ return { fullKinds, projectedKinds };
3621
+ }
3622
+ function buildKindProjectionPlan(kind, selection, resolveFieldType, entityPrefix) {
3623
+ const propertyFields = /* @__PURE__ */ new Map();
3624
+ let includeMeta = false;
3625
+ for (const field2 of selection) {
3626
+ if (field2 === "meta") {
3627
+ includeMeta = true;
3628
+ continue;
3065
3629
  }
3066
- if (forceWorktableOuterJoinOrder) {
3067
- const recursiveWhereClauses = [
3068
- ...recursiveJoinClauses,
3069
- ...recursiveFilterClauses
3070
- ];
3071
- return drizzleOrm.sql`
3072
- SELECT ${drizzleOrm.sql.join(recursiveSelectColumns, drizzleOrm.sql`, `)}
3073
- FROM recursive_cte r
3074
- CROSS JOIN ${ctx.schema.edgesTable} e
3075
- JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
3076
- AND n.id = e.${drizzleOrm.sql.raw(branch.targetField)}
3077
- AND n.kind = e.${drizzleOrm.sql.raw(branch.targetKindField)}
3078
- WHERE ${drizzleOrm.sql.join(recursiveWhereClauses, drizzleOrm.sql` AND `)}
3079
- `;
3630
+ validateProjectionField(field2, entityPrefix, kind);
3631
+ if (!propertyFields.has(field2)) {
3632
+ propertyFields.set(field2, {
3633
+ field: field2,
3634
+ outputName: projectionAlias(entityPrefix, kind, field2),
3635
+ typeInfo: resolveFieldType(kind, field2)
3636
+ });
3080
3637
  }
3081
- return drizzleOrm.sql`
3082
- SELECT ${drizzleOrm.sql.join(recursiveSelectColumns, drizzleOrm.sql`, `)}
3083
- FROM recursive_cte r
3084
- JOIN ${ctx.schema.edgesTable} e ON ${drizzleOrm.sql.join(recursiveJoinClauses, drizzleOrm.sql` AND `)}
3085
- JOIN ${ctx.schema.nodesTable} n ON n.graph_id = e.graph_id
3086
- AND n.id = e.${drizzleOrm.sql.raw(branch.targetField)}
3087
- AND n.kind = e.${drizzleOrm.sql.raw(branch.targetKindField)}
3088
- WHERE ${drizzleOrm.sql.join(recursiveFilterClauses, drizzleOrm.sql` AND `)}
3089
- `;
3090
- }
3091
- const directJoinField = direction === "out" ? "from_id" : "to_id";
3092
- const directTargetField = direction === "out" ? "to_id" : "from_id";
3093
- const directJoinKindField = direction === "out" ? "from_kind" : "to_kind";
3094
- const directTargetKindField = direction === "out" ? "to_kind" : "from_kind";
3095
- const directBranch = compileRecursiveBranch2({
3096
- joinField: directJoinField,
3097
- targetField: directTargetField,
3098
- joinKindField: directJoinKindField,
3099
- targetKindField: directTargetKindField,
3100
- edgeKinds: directEdgeKinds
3101
- });
3102
- function compileInverseRecursiveBranch() {
3103
- const inverseJoinField = direction === "out" ? "to_id" : "from_id";
3104
- const inverseTargetField = direction === "out" ? "from_id" : "to_id";
3105
- const inverseJoinKindField = direction === "out" ? "to_kind" : "from_kind";
3106
- const inverseTargetKindField = direction === "out" ? "from_kind" : "to_kind";
3107
- const overlappingKinds = inverseEdgeKinds.filter(
3108
- (kind) => directEdgeKinds.includes(kind)
3109
- );
3110
- const duplicateGuard = overlappingKinds.length > 0 ? drizzleOrm.sql`NOT (e.from_id = e.to_id AND ${compileKindFilter2(overlappingKinds, "e.kind")})` : void 0;
3111
- const inverseBranch = compileRecursiveBranch2({
3112
- joinField: inverseJoinField,
3113
- targetField: inverseTargetField,
3114
- joinKindField: inverseJoinKindField,
3115
- targetKindField: inverseTargetKindField,
3116
- edgeKinds: inverseEdgeKinds,
3117
- duplicateGuard
3118
- });
3119
- return drizzleOrm.sql`
3120
- ${directBranch}
3121
- UNION ALL
3122
- ${inverseBranch}
3123
- `;
3124
- }
3125
- const recursiveBranchSql = inverseEdgeKinds.length === 0 ? directBranch : compileInverseRecursiveBranch();
3126
- const baseSelectColumns = [
3127
- ...startColumnsFromBase,
3128
- ...nodeColumnsFromBase,
3129
- drizzleOrm.sql`0 AS depth`
3130
- ];
3131
- if (initialPath !== void 0) {
3132
- baseSelectColumns.push(drizzleOrm.sql`${initialPath} AS path`);
3133
3638
  }
3134
- return drizzleOrm.sql`
3135
- recursive_cte AS (
3136
- -- Base case: starting nodes
3137
- SELECT ${drizzleOrm.sql.join(baseSelectColumns, drizzleOrm.sql`, `)}
3138
- FROM ${ctx.schema.nodesTable} n0
3139
- WHERE ${drizzleOrm.sql.join(baseWhereClauses, drizzleOrm.sql` AND `)}
3140
-
3141
- UNION ALL
3142
-
3143
- -- Recursive case: follow edges
3144
- ${recursiveBranchSql}
3145
- )
3146
- `;
3147
- }
3148
- function compileKindFilter2(kinds, columnExpr) {
3149
- return compileKindFilter(drizzleOrm.sql.raw(columnExpr), kinds);
3639
+ return {
3640
+ includeMeta,
3641
+ propertyFields: [...propertyFields.values()]
3642
+ };
3150
3643
  }
3151
- function compileNodePredicates(ast, alias, ctx) {
3152
- return ast.predicates.filter((p) => p.targetAlias === alias && p.targetType !== "edge").map((p) => compilePredicateExpression(p.expression, ctx));
3644
+ function getIncludedNodeKinds(graph, includeKinds) {
3645
+ if (includeKinds === void 0 || includeKinds.length === 0) {
3646
+ return Object.keys(graph.nodes);
3647
+ }
3648
+ return dedupeStrings(includeKinds);
3153
3649
  }
3154
- function compileEdgePredicates(ast, edgeAlias, ctx) {
3155
- return ast.predicates.filter((p) => p.targetAlias === edgeAlias && p.targetType === "edge").map((p) => compilePredicateExpression(p.expression, ctx));
3650
+ function dedupeStrings(values) {
3651
+ return [...new Set(values)];
3156
3652
  }
3157
- function collectRequiredColumnsByAlias(ast, traversal) {
3158
- const selectiveFields = ast.selectiveFields;
3159
- if (selectiveFields === void 0 || selectiveFields.length === 0) {
3160
- return void 0;
3653
+ function buildReachableCte(ctx) {
3654
+ const shouldTrackPath = ctx.cyclePolicy === "prevent";
3655
+ const edgeKindFilter = compileKindFilter(drizzleOrm.sql.raw("e.kind"), ctx.edgeKinds);
3656
+ const initialPath = shouldTrackPath ? ctx.dialect.initializePath(drizzleOrm.sql.raw("n.id")) : void 0;
3657
+ const pathExtension = shouldTrackPath ? ctx.dialect.extendPath(drizzleOrm.sql.raw("r.path"), drizzleOrm.sql.raw("n.id")) : void 0;
3658
+ const cycleCheck = shouldTrackPath ? ctx.dialect.cycleCheck(drizzleOrm.sql.raw("n.id"), drizzleOrm.sql.raw("r.path")) : void 0;
3659
+ const baseColumns = [drizzleOrm.sql`n.id`, drizzleOrm.sql`n.kind`, drizzleOrm.sql`0 AS depth`];
3660
+ if (initialPath !== void 0) {
3661
+ baseColumns.push(drizzleOrm.sql`${initialPath} AS path`);
3161
3662
  }
3162
- const requiredColumnsByAlias = /* @__PURE__ */ new Map();
3163
- const previousNodeKinds = [
3164
- .../* @__PURE__ */ new Set([...ast.start.kinds, ...traversal.nodeKinds])
3663
+ const baseCase = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(baseColumns, drizzleOrm.sql`, `)} FROM ${ctx.schema.nodesTable} n WHERE n.graph_id = ${ctx.graphId} AND n.id = ${ctx.rootId} AND n.deleted_at IS NULL`;
3664
+ const recursiveColumns = [
3665
+ drizzleOrm.sql`n.id`,
3666
+ drizzleOrm.sql`n.kind`,
3667
+ drizzleOrm.sql`r.depth + 1 AS depth`
3165
3668
  ];
3166
- addRequiredColumn(requiredColumnsByAlias, traversal.nodeAlias, "id");
3167
- if (previousNodeKinds.length > 1) {
3168
- addRequiredColumn(requiredColumnsByAlias, traversal.nodeAlias, "kind");
3669
+ if (pathExtension !== void 0) {
3670
+ recursiveColumns.push(drizzleOrm.sql`${pathExtension} AS path`);
3169
3671
  }
3170
- for (const field2 of selectiveFields) {
3171
- markSelectiveFieldAsRequired(requiredColumnsByAlias, field2);
3672
+ const recursiveWhereClauses = [
3673
+ drizzleOrm.sql`e.graph_id = ${ctx.graphId}`,
3674
+ edgeKindFilter,
3675
+ drizzleOrm.sql`e.deleted_at IS NULL`,
3676
+ drizzleOrm.sql`n.deleted_at IS NULL`,
3677
+ drizzleOrm.sql`r.depth < ${ctx.maxDepth}`
3678
+ ];
3679
+ if (cycleCheck !== void 0) {
3680
+ recursiveWhereClauses.push(cycleCheck);
3681
+ }
3682
+ const forceWorktableOuterJoinOrder = ctx.dialect.capabilities.forceRecursiveWorktableOuterJoinOrder;
3683
+ const recursiveCase = ctx.direction === "both" ? compileBidirectionalBranch({
3684
+ recursiveColumns,
3685
+ whereClauses: recursiveWhereClauses,
3686
+ forceWorktableOuterJoinOrder,
3687
+ schema: ctx.schema
3688
+ }) : compileRecursiveBranch({
3689
+ recursiveColumns,
3690
+ whereClauses: recursiveWhereClauses,
3691
+ joinField: "from_id",
3692
+ targetField: "to_id",
3693
+ targetKindField: "to_kind",
3694
+ forceWorktableOuterJoinOrder,
3695
+ schema: ctx.schema
3696
+ });
3697
+ return drizzleOrm.sql`WITH RECURSIVE reachable AS (${baseCase} UNION ALL ${recursiveCase})`;
3698
+ }
3699
+ function compileRecursiveBranch(params) {
3700
+ const columns = [...params.recursiveColumns];
3701
+ const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)}`;
3702
+ const nodeJoin = drizzleOrm.sql`JOIN ${params.schema.nodesTable} n ON n.graph_id = e.graph_id AND n.id = e.${drizzleOrm.sql.raw(params.targetField)} AND n.kind = e.${drizzleOrm.sql.raw(params.targetKindField)}`;
3703
+ if (params.forceWorktableOuterJoinOrder) {
3704
+ const allWhere = [
3705
+ ...params.whereClauses,
3706
+ drizzleOrm.sql`e.${drizzleOrm.sql.raw(params.joinField)} = r.id`
3707
+ ];
3708
+ return drizzleOrm.sql`${selectClause} FROM reachable r CROSS JOIN ${params.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
3709
+ }
3710
+ const where = [...params.whereClauses];
3711
+ return drizzleOrm.sql`${selectClause} FROM reachable r JOIN ${params.schema.edgesTable} e ON e.${drizzleOrm.sql.raw(params.joinField)} = r.id ${nodeJoin} WHERE ${drizzleOrm.sql.join(where, drizzleOrm.sql` AND `)}`;
3712
+ }
3713
+ function compileBidirectionalBranch(params) {
3714
+ const columns = [...params.recursiveColumns];
3715
+ const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)}`;
3716
+ const nodeJoin = drizzleOrm.sql`JOIN ${params.schema.nodesTable} n ON n.graph_id = e.graph_id AND ((e.to_id = r.id AND n.id = e.from_id AND n.kind = e.from_kind) OR (e.from_id = r.id AND n.id = e.to_id AND n.kind = e.to_kind))`;
3717
+ if (params.forceWorktableOuterJoinOrder) {
3718
+ const allWhere = [
3719
+ ...params.whereClauses,
3720
+ drizzleOrm.sql`(e.from_id = r.id OR e.to_id = r.id)`
3721
+ ];
3722
+ return drizzleOrm.sql`${selectClause} FROM reachable r CROSS JOIN ${params.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
3723
+ }
3724
+ return drizzleOrm.sql`${selectClause} FROM reachable r JOIN ${params.schema.edgesTable} e ON (e.from_id = r.id OR e.to_id = r.id) ${nodeJoin} WHERE ${drizzleOrm.sql.join([...params.whereClauses], drizzleOrm.sql` AND `)}`;
3725
+ }
3726
+ function buildIncludedIdsCte(ctx) {
3727
+ const filters = [];
3728
+ if (ctx.includeKinds !== void 0 && ctx.includeKinds.length > 0) {
3729
+ filters.push(compileKindFilter(drizzleOrm.sql.raw("kind"), ctx.includeKinds));
3172
3730
  }
3173
- if (ast.orderBy) {
3174
- for (const orderSpec of ast.orderBy) {
3175
- markFieldRefAsRequired(requiredColumnsByAlias, orderSpec.field);
3176
- }
3731
+ if (ctx.excludeRoot) {
3732
+ filters.push(drizzleOrm.sql`id != ${ctx.rootId}`);
3177
3733
  }
3178
- return requiredColumnsByAlias;
3734
+ const whereClause = filters.length > 0 ? drizzleOrm.sql` WHERE ${drizzleOrm.sql.join(filters, drizzleOrm.sql` AND `)}` : drizzleOrm.sql``;
3735
+ return drizzleOrm.sql`, included_ids AS (SELECT DISTINCT id FROM reachable${whereClause})`;
3179
3736
  }
3180
- function compileNodeSelectColumnsFromTable(tableAlias, alias, requiredColumns, alwaysRequiredColumns) {
3181
- return NODE_COLUMNS.filter(
3182
- (column) => shouldProjectColumn(requiredColumns, column, alwaysRequiredColumns)
3183
- ).map(
3184
- (column) => drizzleOrm.sql`${drizzleOrm.sql.raw(tableAlias)}.${drizzleOrm.sql.raw(column)} AS ${drizzleOrm.sql.raw(`${alias}_${column}`)}`
3185
- );
3737
+ async function fetchSubgraphNodes(ctx, reachableCte, includedIdsCte, projectionPlan) {
3738
+ const columns = [
3739
+ drizzleOrm.sql`n.kind`,
3740
+ drizzleOrm.sql`n.id`,
3741
+ buildFullPropsColumn("n", projectionPlan),
3742
+ ...buildMetadataColumns("n", projectionPlan, [
3743
+ "version",
3744
+ "valid_from",
3745
+ "valid_to",
3746
+ "created_at",
3747
+ "updated_at",
3748
+ "deleted_at"
3749
+ ]),
3750
+ ...buildProjectedPropertyColumns("n", projectionPlan, ctx.dialect)
3751
+ ];
3752
+ const query = drizzleOrm.sql`${reachableCte}${includedIdsCte} SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)} FROM ${ctx.schema.nodesTable} n WHERE n.graph_id = ${ctx.graphId} AND n.id IN (SELECT id FROM included_ids)`;
3753
+ return ctx.backend.execute(query);
3186
3754
  }
3187
- function compileNodeSelectColumnsFromRecursiveRow(alias, requiredColumns, alwaysRequiredColumns) {
3188
- return NODE_COLUMNS.filter(
3189
- (column) => shouldProjectColumn(requiredColumns, column, alwaysRequiredColumns)
3190
- ).map((column) => {
3191
- const projected = `${alias}_${column}`;
3192
- return drizzleOrm.sql`r.${drizzleOrm.sql.raw(projected)} AS ${drizzleOrm.sql.raw(projected)}`;
3193
- });
3755
+ async function fetchSubgraphEdges(ctx, reachableCte, includedIdsCte, projectionPlan) {
3756
+ const edgeKindFilter = compileKindFilter(drizzleOrm.sql.raw("e.kind"), ctx.edgeKinds);
3757
+ const columns = [
3758
+ drizzleOrm.sql`e.id`,
3759
+ drizzleOrm.sql`e.kind`,
3760
+ drizzleOrm.sql`e.from_kind`,
3761
+ drizzleOrm.sql`e.from_id`,
3762
+ drizzleOrm.sql`e.to_kind`,
3763
+ drizzleOrm.sql`e.to_id`,
3764
+ buildFullPropsColumn("e", projectionPlan),
3765
+ ...buildMetadataColumns("e", projectionPlan, [
3766
+ "valid_from",
3767
+ "valid_to",
3768
+ "created_at",
3769
+ "updated_at",
3770
+ "deleted_at"
3771
+ ]),
3772
+ ...buildProjectedPropertyColumns("e", projectionPlan, ctx.dialect)
3773
+ ];
3774
+ const query = drizzleOrm.sql`${reachableCte}${includedIdsCte} SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)} FROM ${ctx.schema.edgesTable} e WHERE e.graph_id = ${ctx.graphId} AND ${edgeKindFilter} AND e.deleted_at IS NULL AND e.from_id IN (SELECT id FROM included_ids) AND e.to_id IN (SELECT id FROM included_ids)`;
3775
+ return ctx.backend.execute(query);
3194
3776
  }
3195
- function compileRecursiveProjection(ast, traversal, dialect) {
3196
- if (ast.selectiveFields && ast.selectiveFields.length > 0) {
3197
- return compileRecursiveSelectiveProjection(
3198
- ast.selectiveFields,
3199
- ast,
3200
- traversal,
3201
- dialect
3202
- );
3777
+ function buildMetadataColumns(alias, plan, columns) {
3778
+ if (plan.projectedKinds.size === 0) {
3779
+ return columns.map((col) => drizzleOrm.sql`${drizzleOrm.sql.raw(`${alias}.${col}`)}`);
3203
3780
  }
3204
- const startAlias = ast.start.alias;
3205
- const nodeAlias = traversal.nodeAlias;
3206
- const vl = traversal.variableLength;
3207
- const fields = [
3208
- // Start alias fields with metadata
3209
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_id`,
3210
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_kind`,
3211
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_props`,
3212
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_version`,
3213
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_valid_from`,
3214
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_valid_to`,
3215
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_created_at`,
3216
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_updated_at`,
3217
- drizzleOrm.sql`${drizzleOrm.sql.raw(startAlias)}_deleted_at`,
3218
- // Node alias fields with metadata
3219
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_id`,
3220
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_kind`,
3221
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_props`,
3222
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_version`,
3223
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_valid_from`,
3224
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_valid_to`,
3225
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_created_at`,
3226
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_updated_at`,
3227
- drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_deleted_at`
3228
- ];
3229
- if (vl.depthAlias !== void 0) {
3230
- fields.push(drizzleOrm.sql`depth AS ${quoteIdentifier(vl.depthAlias)}`);
3781
+ const metaKinds = [...plan.fullKinds];
3782
+ for (const [kind, kindPlan] of plan.projectedKinds) {
3783
+ if (kindPlan.includeMeta) metaKinds.push(kind);
3231
3784
  }
3232
- if (vl.pathAlias !== void 0) {
3233
- fields.push(drizzleOrm.sql`path AS ${quoteIdentifier(vl.pathAlias)}`);
3785
+ if (metaKinds.length === 0) {
3786
+ return columns.map((col) => drizzleOrm.sql`NULL AS ${drizzleOrm.sql.raw(col)}`);
3234
3787
  }
3235
- return drizzleOrm.sql.join(fields, drizzleOrm.sql`, `);
3788
+ if (metaKinds.length === plan.fullKinds.length + plan.projectedKinds.size) {
3789
+ return columns.map((col) => drizzleOrm.sql`${drizzleOrm.sql.raw(`${alias}.${col}`)}`);
3790
+ }
3791
+ const filter = compileKindFilter(drizzleOrm.sql.raw(`${alias}.kind`), metaKinds);
3792
+ return columns.map(
3793
+ (col) => drizzleOrm.sql`CASE WHEN ${filter} THEN ${drizzleOrm.sql.raw(`${alias}.${col}`)} ELSE NULL END AS ${drizzleOrm.sql.raw(col)}`
3794
+ );
3236
3795
  }
3237
- function compileRecursiveSelectiveProjection(fields, ast, traversal, dialect) {
3238
- const allowedAliases = /* @__PURE__ */ new Set([ast.start.alias, traversal.nodeAlias]);
3239
- const columns = fields.map((field2) => {
3240
- if (!allowedAliases.has(field2.alias)) {
3241
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
3242
- `Selective projection for recursive traversals does not support alias "${field2.alias}"`
3243
- );
3244
- }
3245
- if (field2.isSystemField) {
3246
- const dbColumn = mapSelectiveSystemFieldToColumn(field2.field);
3247
- return drizzleOrm.sql`${drizzleOrm.sql.raw(`${field2.alias}_${dbColumn}`)} AS ${quoteIdentifier(field2.outputName)}`;
3248
- }
3249
- const column = drizzleOrm.sql.raw(`${field2.alias}_props`);
3250
- const extracted = compileTypedJsonExtract({
3251
- column,
3252
- dialect,
3253
- pointer: chunkP5CNM325_cjs.jsonPointer([field2.field]),
3254
- valueType: field2.valueType
3255
- });
3256
- return drizzleOrm.sql`${extracted} AS ${quoteIdentifier(field2.outputName)}`;
3257
- });
3258
- const vl = traversal.variableLength;
3259
- if (vl.depthAlias !== void 0) {
3260
- columns.push(drizzleOrm.sql`depth AS ${quoteIdentifier(vl.depthAlias)}`);
3796
+ function buildFullPropsColumn(alias, plan) {
3797
+ if (plan.projectedKinds.size === 0) {
3798
+ return drizzleOrm.sql`${drizzleOrm.sql.raw(`${alias}.props`)} AS props`;
3261
3799
  }
3262
- if (vl.pathAlias !== void 0) {
3263
- columns.push(drizzleOrm.sql`path AS ${quoteIdentifier(vl.pathAlias)}`);
3800
+ if (plan.fullKinds.length === 0) {
3801
+ return drizzleOrm.sql`NULL AS props`;
3264
3802
  }
3265
- return drizzleOrm.sql.join(columns, drizzleOrm.sql`, `);
3803
+ const filter = compileKindFilter(drizzleOrm.sql.raw(`${alias}.kind`), plan.fullKinds);
3804
+ return drizzleOrm.sql`CASE WHEN ${filter} THEN ${drizzleOrm.sql.raw(`${alias}.props`)} ELSE NULL END AS props`;
3266
3805
  }
3267
- function compileRecursiveOrderBy(ast, dialect) {
3268
- if (!ast.orderBy || ast.orderBy.length === 0) {
3269
- return void 0;
3270
- }
3271
- const parts = [];
3272
- for (const orderSpec of ast.orderBy) {
3273
- const valueType = orderSpec.field.valueType;
3274
- if (valueType === "array" || valueType === "object") {
3275
- throw new chunk44SXEVF4_cjs.UnsupportedPredicateError(
3276
- "Ordering by JSON arrays or objects is not supported"
3806
+ function buildProjectedPropertyColumns(alias, plan, dialect) {
3807
+ const columns = [];
3808
+ for (const [kind, kindPlan] of plan.projectedKinds.entries()) {
3809
+ for (const fieldPlan of kindPlan.propertyFields) {
3810
+ const extracted = compileTypedJsonExtract({
3811
+ column: drizzleOrm.sql.raw(`${alias}.props`),
3812
+ dialect,
3813
+ pointer: chunkP5CNM325_cjs.jsonPointer([fieldPlan.field]),
3814
+ valueType: fieldPlan.typeInfo?.valueType
3815
+ });
3816
+ columns.push(
3817
+ drizzleOrm.sql`CASE WHEN ${drizzleOrm.sql.raw(alias)}.kind = ${kind} THEN ${extracted} ELSE NULL END AS ${quoteIdentifier(fieldPlan.outputName)}`
3277
3818
  );
3278
3819
  }
3279
- const field2 = compileFieldValue(orderSpec.field, dialect, valueType);
3280
- const direction = drizzleOrm.sql.raw(orderSpec.direction.toUpperCase());
3281
- const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
3282
- const nullsDirection = drizzleOrm.sql.raw(nulls === "first" ? "DESC" : "ASC");
3283
- parts.push(
3284
- drizzleOrm.sql`(${field2} IS NULL) ${nullsDirection}`,
3285
- drizzleOrm.sql`${field2} ${direction}`
3286
- );
3287
3820
  }
3288
- return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join(parts, drizzleOrm.sql`, `)}`;
3821
+ return columns;
3289
3822
  }
3290
- function compileLimitOffset(ast) {
3291
- const parts = [];
3292
- if (ast.limit !== void 0) {
3293
- parts.push(drizzleOrm.sql`LIMIT ${ast.limit}`);
3294
- }
3295
- if (ast.offset !== void 0) {
3296
- parts.push(drizzleOrm.sql`OFFSET ${ast.offset}`);
3823
+ function applyProjectedFields(target, row, kindPlan) {
3824
+ for (const fieldPlan of kindPlan.propertyFields) {
3825
+ target[fieldPlan.field] = decodeSelectedValue(
3826
+ row[fieldPlan.outputName],
3827
+ fieldPlan.typeInfo
3828
+ );
3297
3829
  }
3298
- return parts.length > 0 ? drizzleOrm.sql.join(parts, drizzleOrm.sql` `) : void 0;
3299
3830
  }
3300
- var DEFAULT_TABLE_NAMES = {
3301
- nodes: "typegraph_nodes",
3302
- edges: "typegraph_edges",
3303
- embeddings: "typegraph_node_embeddings"
3304
- };
3305
- var MAX_IDENTIFIER_LENGTH = 63;
3306
- var VALID_IDENTIFIER_PATTERN = /^[a-z_][a-z0-9_$]*$/i;
3307
- function validateTableName(name, label) {
3308
- if (!name || name.length === 0) {
3309
- throw new chunk44SXEVF4_cjs.ConfigurationError(`${label} table name cannot be empty`);
3310
- }
3311
- if (name.length > MAX_IDENTIFIER_LENGTH) {
3312
- throw new chunk44SXEVF4_cjs.ConfigurationError(
3313
- `${label} table name exceeds maximum length of ${MAX_IDENTIFIER_LENGTH} characters`
3314
- );
3831
+ function mapSubgraphNodeRow(row, projectionPlan) {
3832
+ const kindPlan = projectionPlan.projectedKinds.get(row.kind);
3833
+ if (kindPlan === void 0) {
3834
+ return rowToNode({
3835
+ ...row,
3836
+ props: normalizeProps(row.props)
3837
+ });
3315
3838
  }
3316
- if (!VALID_IDENTIFIER_PATTERN.test(name)) {
3317
- throw new chunk44SXEVF4_cjs.ConfigurationError(
3318
- `${label} table name "${name}" is not a valid SQL identifier. Table names must start with a letter or underscore and contain only letters, digits, underscores, or dollar signs.`
3319
- );
3839
+ const projectedNode = {
3840
+ kind: row.kind,
3841
+ id: row.id
3842
+ };
3843
+ if (kindPlan.includeMeta) {
3844
+ projectedNode.meta = rowToNodeMeta(row);
3845
+ }
3846
+ applyProjectedFields(projectedNode, row, kindPlan);
3847
+ return projectedNode;
3848
+ }
3849
+ function mapSubgraphEdgeRow(row, projectionPlan) {
3850
+ const kindPlan = projectionPlan.projectedKinds.get(row.kind);
3851
+ if (kindPlan === void 0) {
3852
+ return rowToEdge({
3853
+ ...row,
3854
+ props: normalizeProps(row.props)
3855
+ });
3320
3856
  }
3321
- }
3322
- function quoteIdentifier2(name) {
3323
- return `"${name.replaceAll('"', '""')}"`;
3324
- }
3325
- function createSqlSchema(names = {}) {
3326
- const tables = { ...DEFAULT_TABLE_NAMES, ...names };
3327
- validateTableName(tables.nodes, "nodes");
3328
- validateTableName(tables.edges, "edges");
3329
- validateTableName(tables.embeddings, "embeddings");
3330
- return {
3331
- tables,
3332
- nodesTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.nodes)),
3333
- edgesTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.edges)),
3334
- embeddingsTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.embeddings))
3857
+ const projectedEdge = {
3858
+ id: row.id,
3859
+ kind: row.kind,
3860
+ fromKind: row.from_kind,
3861
+ fromId: row.from_id,
3862
+ toKind: row.to_kind,
3863
+ toId: row.to_id
3335
3864
  };
3865
+ if (kindPlan.includeMeta) {
3866
+ projectedEdge.meta = rowToEdgeMeta(row);
3867
+ }
3868
+ applyProjectedFields(projectedEdge, row, kindPlan);
3869
+ return projectedEdge;
3336
3870
  }
3337
- var DEFAULT_SQL_SCHEMA = createSqlSchema();
3338
3871
  var OPERATOR_MAP = {
3339
3872
  union: "UNION",
3340
3873
  unionAll: "UNION ALL",
@@ -4703,9 +5236,9 @@ function transformPathColumns(rows, state, _dialect) {
4703
5236
  }
4704
5237
  return changed ? result : rows;
4705
5238
  }
4706
- var RESERVED_NODE_KEYS2 = /* @__PURE__ */ new Set(["id", "kind", "meta"]);
4707
- var RESERVED_EDGE_KEYS2 = /* @__PURE__ */ new Set(["id", "kind", "fromId", "toId", "meta"]);
4708
- function nullToUndefined(value) {
5239
+ var RESERVED_NODE_KEYS3 = /* @__PURE__ */ new Set(["id", "kind", "meta"]);
5240
+ var RESERVED_EDGE_KEYS3 = /* @__PURE__ */ new Set(["id", "kind", "fromId", "toId", "meta"]);
5241
+ function nullToUndefined3(value) {
4709
5242
  return value === null ? void 0 : value;
4710
5243
  }
4711
5244
  function assignPropsExcludingReserved(target, props, reservedKeys) {
@@ -4721,13 +5254,13 @@ function buildSelectableNode(row, alias) {
4721
5254
  const propsRaw = row[`${alias}_props`];
4722
5255
  const rawProps = typeof propsRaw === "string" ? JSON.parse(propsRaw) : propsRaw ?? {};
4723
5256
  const version = row[`${alias}_version`];
4724
- const validFrom = nullToUndefined(
5257
+ const validFrom = nullToUndefined3(
4725
5258
  row[`${alias}_valid_from`]
4726
5259
  );
4727
- const validTo = nullToUndefined(row[`${alias}_valid_to`]);
5260
+ const validTo = nullToUndefined3(row[`${alias}_valid_to`]);
4728
5261
  const createdAt = row[`${alias}_created_at`];
4729
5262
  const updatedAt = row[`${alias}_updated_at`];
4730
- const deletedAt = nullToUndefined(
5263
+ const deletedAt = nullToUndefined3(
4731
5264
  row[`${alias}_deleted_at`]
4732
5265
  );
4733
5266
  const result = {
@@ -4742,7 +5275,7 @@ function buildSelectableNode(row, alias) {
4742
5275
  deletedAt
4743
5276
  }
4744
5277
  };
4745
- assignPropsExcludingReserved(result, rawProps, RESERVED_NODE_KEYS2);
5278
+ assignPropsExcludingReserved(result, rawProps, RESERVED_NODE_KEYS3);
4746
5279
  return result;
4747
5280
  }
4748
5281
  function buildSelectableNodeOrUndefined(row, alias) {
@@ -4762,13 +5295,13 @@ function buildSelectableEdge(row, alias) {
4762
5295
  const toId = row[`${alias}_to_id`];
4763
5296
  const propsRaw = row[`${alias}_props`];
4764
5297
  const rawProps = typeof propsRaw === "string" ? JSON.parse(propsRaw) : propsRaw ?? {};
4765
- const validFrom = nullToUndefined(
5298
+ const validFrom = nullToUndefined3(
4766
5299
  row[`${alias}_valid_from`]
4767
5300
  );
4768
- const validTo = nullToUndefined(row[`${alias}_valid_to`]);
5301
+ const validTo = nullToUndefined3(row[`${alias}_valid_to`]);
4769
5302
  const createdAt = row[`${alias}_created_at`];
4770
5303
  const updatedAt = row[`${alias}_updated_at`];
4771
- const deletedAt = nullToUndefined(
5304
+ const deletedAt = nullToUndefined3(
4772
5305
  row[`${alias}_deleted_at`]
4773
5306
  );
4774
5307
  const result = {
@@ -4784,7 +5317,7 @@ function buildSelectableEdge(row, alias) {
4784
5317
  deletedAt
4785
5318
  }
4786
5319
  };
4787
- assignPropsExcludingReserved(result, rawProps, RESERVED_EDGE_KEYS2);
5320
+ assignPropsExcludingReserved(result, rawProps, RESERVED_EDGE_KEYS3);
4788
5321
  return result;
4789
5322
  }
4790
5323
  function buildSelectContext(row, startAlias, traversals) {
@@ -5170,63 +5703,6 @@ function getPlaceholderForValueType(valueType, mode) {
5170
5703
  }
5171
5704
  }
5172
5705
 
5173
- // src/query/execution/value-decoder.ts
5174
- function nullToUndefined2(value) {
5175
- return value === null ? void 0 : value;
5176
- }
5177
- function decodeSelectedValue(value, typeInfo) {
5178
- const normalized = nullToUndefined2(value);
5179
- if (normalized === void 0) return void 0;
5180
- if (typeInfo === void 0) {
5181
- return normalized;
5182
- }
5183
- return decodeByValueType(normalized, typeInfo.valueType);
5184
- }
5185
- function decodeByValueType(value, valueType) {
5186
- switch (valueType) {
5187
- case "boolean": {
5188
- if (typeof value === "boolean") return value;
5189
- if (typeof value === "number") return value !== 0;
5190
- if (typeof value === "string") {
5191
- if (value === "0") return false;
5192
- if (value === "1") return true;
5193
- if (value.toLowerCase() === "true") return true;
5194
- if (value.toLowerCase() === "false") return false;
5195
- }
5196
- return Boolean(value);
5197
- }
5198
- case "number": {
5199
- if (typeof value === "number") return value;
5200
- if (typeof value === "string") {
5201
- const parsed = Number(value);
5202
- return Number.isNaN(parsed) ? value : parsed;
5203
- }
5204
- return value;
5205
- }
5206
- case "array":
5207
- case "object":
5208
- case "embedding": {
5209
- if (typeof value !== "string") return value;
5210
- const trimmed = value.trim();
5211
- const looksJson = trimmed.startsWith("[") || trimmed.startsWith("{");
5212
- if (!looksJson) return value;
5213
- try {
5214
- return JSON.parse(trimmed);
5215
- } catch {
5216
- return value;
5217
- }
5218
- }
5219
- case "string":
5220
- case "date":
5221
- case "unknown": {
5222
- return value;
5223
- }
5224
- default: {
5225
- return value;
5226
- }
5227
- }
5228
- }
5229
-
5230
5706
  // src/query/execution/selective-result-mapper.ts
5231
5707
  var MissingSelectiveFieldError = class extends Error {
5232
5708
  alias;
@@ -5395,12 +5871,12 @@ function buildRequiredAliasValue(row, plan) {
5395
5871
  }
5396
5872
  };
5397
5873
  for (const field2 of plan.systemFields) {
5398
- base[field2.field] = nullToUndefined2(row[field2.outputName]);
5874
+ base[field2.field] = nullToUndefined(row[field2.outputName]);
5399
5875
  }
5400
5876
  if (plan.metaFields.length > 0) {
5401
5877
  const meta = {};
5402
5878
  for (const field2 of plan.metaFields) {
5403
- meta[field2.metaKey] = nullToUndefined2(row[field2.outputName]);
5879
+ meta[field2.metaKey] = nullToUndefined(row[field2.outputName]);
5404
5880
  }
5405
5881
  base.meta = createGuardedProxy(meta, `${plan.alias}.meta`);
5406
5882
  }
@@ -6368,7 +6844,35 @@ var ExecutableQuery = class _ExecutableQuery {
6368
6844
  return optimizedResult;
6369
6845
  }
6370
6846
  const compiled = this.compile();
6371
- const rawRows = await this.#config.backend.execute(compiled);
6847
+ const rawRows = await this.#config.backend.execute(compiled);
6848
+ this.#config.dialect ?? "sqlite";
6849
+ const rows = transformPathColumns(rawRows, this.#state);
6850
+ return mapResults(
6851
+ rows,
6852
+ this.#state.startAlias,
6853
+ this.#state.traversals,
6854
+ this.#selectFn
6855
+ );
6856
+ }
6857
+ /**
6858
+ * Executes the query against a provided backend.
6859
+ *
6860
+ * Used by `store.batch()` to run multiple queries over a single connection
6861
+ * (e.g., within a transaction). The full compile → execute → transform
6862
+ * pipeline runs identically to `execute()`, but against the given backend.
6863
+ */
6864
+ async executeOn(backend) {
6865
+ if (hasParameterReferences(this.toAst())) {
6866
+ throw new Error(
6867
+ "Query contains param() references. Use .prepare().execute({...}) instead of .execute()."
6868
+ );
6869
+ }
6870
+ const optimizedResult = await this.#tryOptimizedExecutionOn(backend);
6871
+ if (optimizedResult !== void 0) {
6872
+ return optimizedResult;
6873
+ }
6874
+ const compiled = this.compile();
6875
+ const rawRows = await backend.execute(compiled);
6372
6876
  this.#config.dialect ?? "sqlite";
6373
6877
  const rows = transformPathColumns(rawRows, this.#state);
6374
6878
  return mapResults(
@@ -6430,6 +6934,56 @@ var ExecutableQuery = class _ExecutableQuery {
6430
6934
  throw error;
6431
6935
  }
6432
6936
  }
6937
+ /**
6938
+ * Attempts optimized execution against a provided backend.
6939
+ * Mirror of #tryOptimizedExecution but delegates to the given backend.
6940
+ */
6941
+ async #tryOptimizedExecutionOn(backend) {
6942
+ const selectiveFields = this.#getSelectiveFieldsForExecute();
6943
+ if (selectiveFields === void 0) {
6944
+ return void 0;
6945
+ }
6946
+ let compiled;
6947
+ if (this.#cachedOptimizedCompiled === NOT_COMPUTED) {
6948
+ const baseAst = buildQueryAst(this.#config, this.#state);
6949
+ const selectiveAst = {
6950
+ ...baseAst,
6951
+ selectiveFields
6952
+ };
6953
+ compiled = compileQuery(
6954
+ selectiveAst,
6955
+ this.#config.graphId,
6956
+ this.#compileOptions()
6957
+ );
6958
+ this.#cachedOptimizedCompiled = compiled;
6959
+ } else {
6960
+ compiled = this.#cachedOptimizedCompiled;
6961
+ }
6962
+ const rawSelectiveRows = await backend.execute(compiled);
6963
+ this.#config.dialect ?? "sqlite";
6964
+ const rows = transformPathColumns(rawSelectiveRows, this.#state);
6965
+ try {
6966
+ return mapSelectiveResults(
6967
+ rows,
6968
+ this.#state,
6969
+ selectiveFields,
6970
+ this.#config.schemaIntrospector,
6971
+ this.#selectFn
6972
+ );
6973
+ } catch (error) {
6974
+ if (error instanceof MissingSelectiveFieldError) {
6975
+ this.#cachedSelectiveFieldsForExecute = void 0;
6976
+ this.#cachedOptimizedCompiled = NOT_COMPUTED;
6977
+ return void 0;
6978
+ }
6979
+ if (error instanceof chunk44SXEVF4_cjs.UnsupportedPredicateError) {
6980
+ this.#cachedSelectiveFieldsForExecute = void 0;
6981
+ this.#cachedOptimizedCompiled = NOT_COMPUTED;
6982
+ return void 0;
6983
+ }
6984
+ throw error;
6985
+ }
6986
+ }
6433
6987
  #trackSelectFunctionAccesses(tracker) {
6434
6988
  const hasOptionalTraversal = this.#state.traversals.some(
6435
6989
  (traversal) => traversal.optional
@@ -6680,7 +7234,7 @@ var ExecutableQuery = class _ExecutableQuery {
6680
7234
  throw new MissingSelectiveFieldError(alias, fieldName);
6681
7235
  }
6682
7236
  const aliasObject2 = this.#getOrCreateAliasObject(cursorContext, alias);
6683
- aliasObject2[fieldName] = nullToUndefined2(row[outputName2]);
7237
+ aliasObject2[fieldName] = nullToUndefined(row[outputName2]);
6684
7238
  continue;
6685
7239
  }
6686
7240
  const segments = chunkP5CNM325_cjs.parseJsonPointer(jsonPointer2);
@@ -8063,6 +8617,29 @@ var UnionableQuery = class _UnionableQuery {
8063
8617
  }
8064
8618
  return rows;
8065
8619
  }
8620
+ /**
8621
+ * Executes the combined query against a provided backend.
8622
+ *
8623
+ * Used by `store.batch()` to run multiple queries over a single connection.
8624
+ */
8625
+ async executeOn(backend) {
8626
+ if (composableQueryHasParameterReferences(this.toAst())) {
8627
+ throw new Error(
8628
+ "Query contains param() references. Use .prepare().execute({...}) instead of .execute()."
8629
+ );
8630
+ }
8631
+ const compiled = this.compile();
8632
+ const rows = await backend.execute(compiled);
8633
+ if (this.#state.selectFn && this.#state.startAlias) {
8634
+ return mapResults(
8635
+ rows,
8636
+ this.#state.startAlias,
8637
+ this.#state.traversals ?? [],
8638
+ this.#state.selectFn
8639
+ );
8640
+ }
8641
+ return rows;
8642
+ }
8066
8643
  };
8067
8644
 
8068
8645
  // src/query/builder/aggregates.ts
@@ -9283,67 +9860,6 @@ async function checkCardinalityConstraint(ctx, edgeKind, cardinality, fromKind,
9283
9860
  }
9284
9861
  }
9285
9862
 
9286
- // src/store/row-mappers.ts
9287
- var RESERVED_NODE_KEYS3 = /* @__PURE__ */ new Set(["id", "kind", "meta"]);
9288
- function nullToUndefined3(value) {
9289
- return value === null ? void 0 : value;
9290
- }
9291
- function filterReservedKeys(props, reservedKeys) {
9292
- const filtered = {};
9293
- for (const [key, value] of Object.entries(props)) {
9294
- if (!reservedKeys.has(key)) {
9295
- filtered[key] = value;
9296
- }
9297
- }
9298
- return filtered;
9299
- }
9300
- function rowToNode(row) {
9301
- const rawProps = JSON.parse(row.props);
9302
- const props = filterReservedKeys(rawProps, RESERVED_NODE_KEYS3);
9303
- return {
9304
- kind: row.kind,
9305
- id: row.id,
9306
- meta: {
9307
- version: row.version,
9308
- validFrom: nullToUndefined3(row.valid_from),
9309
- validTo: nullToUndefined3(row.valid_to),
9310
- createdAt: row.created_at,
9311
- updatedAt: row.updated_at,
9312
- deletedAt: nullToUndefined3(row.deleted_at)
9313
- },
9314
- ...props
9315
- };
9316
- }
9317
- var RESERVED_EDGE_KEYS3 = /* @__PURE__ */ new Set([
9318
- "id",
9319
- "kind",
9320
- "meta",
9321
- "fromKind",
9322
- "fromId",
9323
- "toKind",
9324
- "toId"
9325
- ]);
9326
- function rowToEdge(row) {
9327
- const rawProps = JSON.parse(row.props);
9328
- const props = filterReservedKeys(rawProps, RESERVED_EDGE_KEYS3);
9329
- return {
9330
- id: row.id,
9331
- kind: row.kind,
9332
- fromKind: row.from_kind,
9333
- fromId: row.from_id,
9334
- toKind: row.to_kind,
9335
- toId: row.to_id,
9336
- meta: {
9337
- validFrom: nullToUndefined3(row.valid_from),
9338
- validTo: nullToUndefined3(row.valid_to),
9339
- createdAt: row.created_at,
9340
- updatedAt: row.updated_at,
9341
- deletedAt: nullToUndefined3(row.deleted_at)
9342
- },
9343
- ...props
9344
- };
9345
- }
9346
-
9347
9863
  // src/store/operations/edge-operations.ts
9348
9864
  function getEdgeRegistration(graph, kind) {
9349
9865
  const registration = graph.edges[kind];
@@ -11217,142 +11733,6 @@ async function executeNodeBulkGetOrCreateByConstraint(ctx, kind, constraintName,
11217
11733
  }
11218
11734
  return results;
11219
11735
  }
11220
- var DEFAULT_SUBGRAPH_MAX_DEPTH = 10;
11221
- function normalizeProps(value) {
11222
- return typeof value === "string" ? value : JSON.stringify(value ?? {});
11223
- }
11224
- async function executeSubgraph(params) {
11225
- const { options } = params;
11226
- if (options.edges.length === 0) {
11227
- return { nodes: [], edges: [] };
11228
- }
11229
- const maxDepth = Math.min(
11230
- options.maxDepth ?? DEFAULT_SUBGRAPH_MAX_DEPTH,
11231
- MAX_RECURSIVE_DEPTH
11232
- );
11233
- const ctx = {
11234
- graphId: params.graphId,
11235
- rootId: params.rootId,
11236
- edgeKinds: options.edges,
11237
- maxDepth,
11238
- includeKinds: options.includeKinds,
11239
- excludeRoot: options.excludeRoot ?? false,
11240
- direction: options.direction ?? "out",
11241
- cyclePolicy: options.cyclePolicy ?? "prevent",
11242
- dialect: params.dialect,
11243
- schema: params.schema ?? DEFAULT_SQL_SCHEMA,
11244
- backend: params.backend
11245
- };
11246
- const reachableCte = buildReachableCte(ctx);
11247
- const includedIdsCte = buildIncludedIdsCte(ctx);
11248
- const [nodeRows, edgeRows] = await Promise.all([
11249
- fetchSubgraphNodes(ctx, reachableCte, includedIdsCte),
11250
- fetchSubgraphEdges(ctx, reachableCte, includedIdsCte)
11251
- ]);
11252
- const nodes = nodeRows.map(
11253
- (row) => rowToNode({ ...row, props: normalizeProps(row.props) })
11254
- );
11255
- const edges = edgeRows.map(
11256
- (row) => rowToEdge({ ...row, props: normalizeProps(row.props) })
11257
- );
11258
- return {
11259
- nodes,
11260
- edges
11261
- };
11262
- }
11263
- function buildReachableCte(ctx) {
11264
- const shouldTrackPath = ctx.cyclePolicy === "prevent";
11265
- const edgeKindFilter = compileKindFilter(drizzleOrm.sql.raw("e.kind"), ctx.edgeKinds);
11266
- const initialPath = shouldTrackPath ? ctx.dialect.initializePath(drizzleOrm.sql.raw("n.id")) : void 0;
11267
- const pathExtension = shouldTrackPath ? ctx.dialect.extendPath(drizzleOrm.sql.raw("r.path"), drizzleOrm.sql.raw("n.id")) : void 0;
11268
- const cycleCheck = shouldTrackPath ? ctx.dialect.cycleCheck(drizzleOrm.sql.raw("n.id"), drizzleOrm.sql.raw("r.path")) : void 0;
11269
- const baseColumns = [drizzleOrm.sql`n.id`, drizzleOrm.sql`n.kind`, drizzleOrm.sql`0 AS depth`];
11270
- if (initialPath !== void 0) {
11271
- baseColumns.push(drizzleOrm.sql`${initialPath} AS path`);
11272
- }
11273
- const baseCase = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(baseColumns, drizzleOrm.sql`, `)} FROM ${ctx.schema.nodesTable} n WHERE n.graph_id = ${ctx.graphId} AND n.id = ${ctx.rootId} AND n.deleted_at IS NULL`;
11274
- const recursiveColumns = [
11275
- drizzleOrm.sql`n.id`,
11276
- drizzleOrm.sql`n.kind`,
11277
- drizzleOrm.sql`r.depth + 1 AS depth`
11278
- ];
11279
- if (pathExtension !== void 0) {
11280
- recursiveColumns.push(drizzleOrm.sql`${pathExtension} AS path`);
11281
- }
11282
- const recursiveWhereClauses = [
11283
- drizzleOrm.sql`e.graph_id = ${ctx.graphId}`,
11284
- edgeKindFilter,
11285
- drizzleOrm.sql`e.deleted_at IS NULL`,
11286
- drizzleOrm.sql`n.deleted_at IS NULL`,
11287
- drizzleOrm.sql`r.depth < ${ctx.maxDepth}`
11288
- ];
11289
- if (cycleCheck !== void 0) {
11290
- recursiveWhereClauses.push(cycleCheck);
11291
- }
11292
- const forceWorktableOuterJoinOrder = ctx.dialect.capabilities.forceRecursiveWorktableOuterJoinOrder;
11293
- const recursiveCase = ctx.direction === "both" ? compileBidirectionalBranch({
11294
- recursiveColumns,
11295
- whereClauses: recursiveWhereClauses,
11296
- forceWorktableOuterJoinOrder,
11297
- schema: ctx.schema
11298
- }) : compileRecursiveBranch({
11299
- recursiveColumns,
11300
- whereClauses: recursiveWhereClauses,
11301
- joinField: "from_id",
11302
- targetField: "to_id",
11303
- targetKindField: "to_kind",
11304
- forceWorktableOuterJoinOrder,
11305
- schema: ctx.schema
11306
- });
11307
- return drizzleOrm.sql`WITH RECURSIVE reachable AS (${baseCase} UNION ALL ${recursiveCase})`;
11308
- }
11309
- function compileRecursiveBranch(params) {
11310
- const columns = [...params.recursiveColumns];
11311
- const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)}`;
11312
- const nodeJoin = drizzleOrm.sql`JOIN ${params.schema.nodesTable} n ON n.graph_id = e.graph_id AND n.id = e.${drizzleOrm.sql.raw(params.targetField)} AND n.kind = e.${drizzleOrm.sql.raw(params.targetKindField)}`;
11313
- if (params.forceWorktableOuterJoinOrder) {
11314
- const allWhere = [
11315
- ...params.whereClauses,
11316
- drizzleOrm.sql`e.${drizzleOrm.sql.raw(params.joinField)} = r.id`
11317
- ];
11318
- return drizzleOrm.sql`${selectClause} FROM reachable r CROSS JOIN ${params.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
11319
- }
11320
- const where = [...params.whereClauses];
11321
- return drizzleOrm.sql`${selectClause} FROM reachable r JOIN ${params.schema.edgesTable} e ON e.${drizzleOrm.sql.raw(params.joinField)} = r.id ${nodeJoin} WHERE ${drizzleOrm.sql.join(where, drizzleOrm.sql` AND `)}`;
11322
- }
11323
- function compileBidirectionalBranch(params) {
11324
- const columns = [...params.recursiveColumns];
11325
- const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)}`;
11326
- const nodeJoin = drizzleOrm.sql`JOIN ${params.schema.nodesTable} n ON n.graph_id = e.graph_id AND ((e.to_id = r.id AND n.id = e.from_id AND n.kind = e.from_kind) OR (e.from_id = r.id AND n.id = e.to_id AND n.kind = e.to_kind))`;
11327
- if (params.forceWorktableOuterJoinOrder) {
11328
- const allWhere = [
11329
- ...params.whereClauses,
11330
- drizzleOrm.sql`(e.from_id = r.id OR e.to_id = r.id)`
11331
- ];
11332
- return drizzleOrm.sql`${selectClause} FROM reachable r CROSS JOIN ${params.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
11333
- }
11334
- return drizzleOrm.sql`${selectClause} FROM reachable r JOIN ${params.schema.edgesTable} e ON (e.from_id = r.id OR e.to_id = r.id) ${nodeJoin} WHERE ${drizzleOrm.sql.join([...params.whereClauses], drizzleOrm.sql` AND `)}`;
11335
- }
11336
- function buildIncludedIdsCte(ctx) {
11337
- const filters = [];
11338
- if (ctx.includeKinds !== void 0 && ctx.includeKinds.length > 0) {
11339
- filters.push(compileKindFilter(drizzleOrm.sql.raw("kind"), ctx.includeKinds));
11340
- }
11341
- if (ctx.excludeRoot) {
11342
- filters.push(drizzleOrm.sql`id != ${ctx.rootId}`);
11343
- }
11344
- const whereClause = filters.length > 0 ? drizzleOrm.sql` WHERE ${drizzleOrm.sql.join(filters, drizzleOrm.sql` AND `)}` : drizzleOrm.sql``;
11345
- return drizzleOrm.sql`, included_ids AS (SELECT DISTINCT id FROM reachable${whereClause})`;
11346
- }
11347
- async function fetchSubgraphNodes(ctx, reachableCte, includedIdsCte) {
11348
- const query = drizzleOrm.sql`${reachableCte}${includedIdsCte} SELECT n.kind, n.id, n.props, n.version, n.valid_from, n.valid_to, n.created_at, n.updated_at, n.deleted_at FROM ${ctx.schema.nodesTable} n WHERE n.graph_id = ${ctx.graphId} AND n.id IN (SELECT id FROM included_ids)`;
11349
- return ctx.backend.execute(query);
11350
- }
11351
- async function fetchSubgraphEdges(ctx, reachableCte, includedIdsCte) {
11352
- const edgeKindFilter = compileKindFilter(drizzleOrm.sql.raw("e.kind"), ctx.edgeKinds);
11353
- const query = drizzleOrm.sql`${reachableCte}${includedIdsCte} SELECT e.id, e.kind, e.from_kind, e.from_id, e.to_kind, e.to_id, e.props, e.valid_from, e.valid_to, e.created_at, e.updated_at, e.deleted_at FROM ${ctx.schema.edgesTable} e WHERE e.graph_id = ${ctx.graphId} AND ${edgeKindFilter} AND e.deleted_at IS NULL AND e.from_id IN (SELECT id FROM included_ids) AND e.to_id IN (SELECT id FROM included_ids)`;
11354
- return ctx.backend.execute(query);
11355
- }
11356
11736
 
11357
11737
  // src/store/store.ts
11358
11738
  var Store = class {
@@ -11556,6 +11936,46 @@ var Store = class {
11556
11936
  query() {
11557
11937
  return this.#createQueryForBackend(this.#backend);
11558
11938
  }
11939
+ // === Batch Query Execution ===
11940
+ /**
11941
+ * Executes multiple queries over a single connection with snapshot consistency.
11942
+ *
11943
+ * Acquires one connection via an implicit transaction, executes each query
11944
+ * sequentially on that connection, and returns a typed tuple of results.
11945
+ * Each query preserves its own result type, projection, filtering,
11946
+ * sorting, and pagination.
11947
+ *
11948
+ * Read-only — use `bulkCreate`, `bulkInsert`, etc. for write batching.
11949
+ *
11950
+ * @example
11951
+ * ```typescript
11952
+ * const [people, companies] = await store.batch(
11953
+ * store.query()
11954
+ * .from("Person", "p")
11955
+ * .select((ctx) => ({ id: ctx.p.id, name: ctx.p.name })),
11956
+ * store.query()
11957
+ * .from("Company", "c")
11958
+ * .select((ctx) => ({ id: ctx.c.id, name: ctx.c.name }))
11959
+ * .orderBy("c", "name", "asc")
11960
+ * .limit(5),
11961
+ * );
11962
+ * // people: readonly { id: string; name: string }[]
11963
+ * // companies: readonly { id: string; name: string }[]
11964
+ * ```
11965
+ *
11966
+ * @param queries - Two or more executable queries (from `.select()` or set operations)
11967
+ * @returns A tuple with per-query typed results, preserving input order
11968
+ */
11969
+ async batch(...queries) {
11970
+ return this.#backend.transaction(async (txBackend) => {
11971
+ const results = [];
11972
+ for (const query of queries) {
11973
+ const result = await query.executeOn(txBackend);
11974
+ results.push(result);
11975
+ }
11976
+ return results;
11977
+ });
11978
+ }
11559
11979
  // === Subgraph Extraction ===
11560
11980
  /**
11561
11981
  * Extracts a typed subgraph by traversing from a root node.
@@ -11581,6 +12001,7 @@ var Store = class {
11581
12001
  */
11582
12002
  async subgraph(rootId, options) {
11583
12003
  return executeSubgraph({
12004
+ graph: this.#graph,
11584
12005
  graphId: this.graphId,
11585
12006
  rootId,
11586
12007
  backend: this.#backend,
@@ -11942,6 +12363,7 @@ exports.createStore = createStore;
11942
12363
  exports.createStoreWithSchema = createStoreWithSchema;
11943
12364
  exports.defineEdge = defineEdge;
11944
12365
  exports.defineNode = defineNode;
12366
+ exports.defineSubgraphProject = defineSubgraphProject;
11945
12367
  exports.differentFrom = differentFrom;
11946
12368
  exports.disjointWith = disjointWith;
11947
12369
  exports.equivalentTo = equivalentTo;