Skip to content

v0.1.0

Choose a tag to compare

@github-actions github-actions released this 20 Jan 00:53
· 148 commits to main since this release

v0.1.0 (2026-01-20)

Ci

Documentation

  • docs: mkdocs first version (e5d93b7)

  • docs: gen doc (7d32964)

  • docs: no futuro rever se quero ser conservador ou nao, databricks já otimiza (93a719b)

Feature

  • feat: add support for pattern predicates in CypherVisitor to handle EXISTS checks

Problem: The parser visitor was missing a handler for oC_PatternPredicate. When patterns like (c)-[:HAS_LOAN]->(:Loan) appeared in WHERE clauses, they fell through to a default case that incorrectly treated the entire pattern text as a variable name.

Location: src/gsql2rsql/parser/visitor.py:987

🛠️ Solution Implemented
Added visit_oC_PatternPredicate() method that converts pattern predicates to QueryExpressionExists AST nodes (implicit EXISTS semantics per OpenCypher spec).

Changes:

Added handler in visit_oC_Atom (visitor.py:979)
Implemented visit_oC_PatternPredicate method (visitor.py:1229-1266)
Example:

WHERE NOT (c)-[:HAS_LOAN]->(:Loan)
Now correctly transpiles to:

WHERE NOT EXISTS (SELECT 1 FROM CustomerLoan ...) (8a22ea9)

  • feat: add test-no-pyspark target to run tests excluding PySpark tests (fast) (c435db5)

  • feat: enhance column resolution and entity return handling in logical plan and SQL renderer

fix Entity Return vs Property-Level Column Pruning

quando uma query nao especifica quais campos usar ele falha em puxar no resolver , a solucoes levantadas pelo claude foram

Issue 1: Entity Return vs Property-Level Column Pruning

Current Status

Failing Test: test_01_simple_node_lookup.py::test_projects_node_properties

*let's fix issue 1 with option A.

but first create a counter-example of
MATCH (p:Person) RETURN p

but with the user specifying what they want from the property of p, what changes in the openCypher query, what changes in the current result, how will option A affect this?

Root Cause

The column resolution and pruning system tracks property-level references (e.g., p.name, p.age) but doesn't have a concept of entity-level returns. When you RETURN p, the system should understand that ALL properties of p are implicitly referenced.

The problem manifests in two places:

  1. ColumnResolver: Doesn't mark all entity properties as "used" when an entity is returned
  2. SQLRenderer: Column pruning optimization removes "unreferenced" properties, even though they should be included in entity returns

Current Architecture

MATCH (p:Person) RETURN p
         ↓
  ColumnResolver tracks: p.id (implicit node ID)
         ↓
  Column Pruning: Only p.id is "used"
         ↓
  SQL: SELECT p.id AS p  ❌ Missing name, age, etc.

Desired Architecture

MATCH (p:Person) RETURN p
         ↓
  ColumnResolver recognizes: "Entity return" → mark ALL properties
         ↓
  Column Pruning: p.id, p.name, p.age all marked as "used"
         ↓
  SQL: SELECT p.id, p.name, p.age AS p  ✅

Solution Options for Issue 1

Option A: Entity-Aware Resolution (Recommended)

Approach: Extend ColumnResolver to distinguish between entity returns and property returns.

Implementation:

  1. Add EntityReturn vs PropertyReturn distinction to ResolutionResult
  2. When processing RETURN/WITH clauses, detect if expression is a bare variable (entity) vs property access
  3. Mark ALL properties of the entity schema as "referenced" for entity returns
  4. Renderer respects entity return flag and projects all properties

Pros:

  • Semantically correct (matches Cypher behavior)
  • Minimal changes to existing architecture
  • Preserves column pruning optimization for property-level returns

Cons:

  • Increases memory/bandwidth for entity returns (potentially many unused properties)
  • Requires schema lookup during resolution to find all properties
  • Need to handle entities without schema (dynamic properties)

Trade-offs:

  • Performance vs Correctness: Projecting all properties is less efficient but semantically correct
  • Schema Dependency: Resolution now depends more heavily on schema being complete
  • Backward Compatibility: May change SQL output for existing queries

Complexity: Medium (2-3 days)


Option B: Deferred Resolution (Complex)

Approach: Don't resolve entity returns until render time, allowing renderer to query schema.

Implementation:

  1. ColumnResolver marks entity returns as "entity-level" without enumerating properties
  2. SQLRenderer queries schema during rendering to get full property list
  3. Late-stage property expansion

Pros:

  • No changes to ColumnResolver
  • Renderer has full context for decisions

Cons:

  • Breaks separation of concerns (renderer doing semantic work)
  • Harder to debug (resolution happens in two phases)
  • Schema must be available at render time (complicates testing)
  • Can't prune unused properties from earlier operators

Trade-offs:

  • Separation of Concerns: Violates "stupid renderer" principle
  • Debuggability: Resolution state unclear between phases
  • Performance: Can't optimize earlier pipeline stages

Complexity: High (5-7 days)

Recommendation: ❌ Avoid - breaks architectural boundaries


Option C: Explicit Property Projection (User-Facing Change)

Approach: Require users to explicitly list properties or use a wildcard syntax.

Implementation:

  1. RETURN p → error "must specify properties"
  2. RETURN p.* → return all properties (explicit)
  3. RETURN p.name, p.age → return specific properties

Pros:

  • No ambiguity in system
  • Forces explicit thinking about data flow
  • Enables aggressive column pruning

Cons:

  • Not Cypher-compliant (breaks standard)
  • Poor user experience (verbose)
  • Migration burden for existing queries

Trade-offs:

  • Standards Compliance: Breaks Cypher semantics
  • User Experience: More verbose queries
  • Migration: Breaks existing queries

Complexity: Low (implementation), High (user migration)

Recommendation: ❌ Avoid - violates Cypher standard


Option D: Post-Pruning Expansion (Hybrid)

Approach: Prune aggressively during planning, expand during rendering if entity return detected.

Implementation:

  1. ColumnResolver prunes normally (only referenced properties)
  2. Mark projection operators that return entities
  3. During render, detect entity return and add schema-based property projection

Pros:

  • Gets optimization benefits during planning
  • Can add missing properties at render time
  • Backward compatible

Cons:

  • Complex two-phase logic
  • Properties pruned early can't be recovered if needed by later operators
  • Requires render-time schema access
  • Hard to debug mismatches

Trade-offs:

  • Correctness: Risk of "too late" expansion
  • Complexity: Two resolution phases
  • Performance: Optimization benefits unclear

Complexity: High (4-6 days)

Recommendation: ⚠️ Consider only if Option A proves insufficient


adotamos a opcao A (816c911)

  • feat: enhance scope clearing for aggregation

max_operator_id and add lookup for out-of-scope symbols (a844b40)

  • feat: now, renderer is dumb, and column resolver / schema propagation are autoritative
  • Implement tests for error position tracking in ColumnResolutionError to ensure accurate line and column reporting in queries.
  • Introduce integration tests for SQLRenderer to validate its behavior with ColumnResolver, ensuring it requires resolved plans and handles property and join resolutions correctly.
  • Add tests to verify that symbols do not appear in both available and out-of-scope lists, addressing a symbol duplication bug. (78c9a5a)
  • feat: schema propagation for paths

agora é autoriatario , nao só descreve (fdcac60)

  • feat: schema propagation for all operators (so the renderer can use ResolvedColumnRef directly) (ee31842)

  • feat: Implement column resolution in LogicalPlan and enhance schema propagation

  • Added ColumnResolver and ResolutionResult for managing column resolution.
  • Introduced resolve() method in LogicalPlan to validate column references and build a symbol table.
  • Enhanced ProjectionOperator to propagate data types from input schema to output schema.
  • Created SymbolTable for tracking variable definitions and scopes, supporting nested scopes and error context.
  • Added unit tests for ColumnResolver, including tests for resolving queries and symbol table integrity.
  • Implemented tests for LogicalPlan's resolve method and operator retrieval. (1f03d31)
  • feat: add safety checks for correlated subqueries in SelectionPushdownOptimizer (cb60993)

  • feat: conservative SelectionPushdownOptimizer,

focusing on the ability to split AND conjunctions and push individual predicates to their respective DataSources. The tests cover various scenarios including both-sides pushdown, same-variable combinations, partial pushdown, and the preservation of OR predicates. Additionally, it includes checks for SQL output validation and optimizer statistics tracking, ensuring that predicates are correctly handled in the context of optional matches and volatile functions. (cbc3abb)

  • feat: add recursive sink filter pushdown optimization a (dc0c3de)

  • feat: implement selection pushdown optimization and enhance (8d29b9b)

  • feat: SQLRenderer for undirected relationships (cd8c5ea)

  • feat: cli subquery flattening optimization option and logical plan output to transpile process (ddd500e)

  • feat: optimize recursive query execution with source node filter pushdown (aaa7ae2)

  • feat: SUBQUERY flattening sqls mais compactas (abordagem conservadora) (e180c88)

  • feat: implement predicate pushdown optimization (53aeee9)

  • feat: add PathExpressionAnalyzer for optimizing recursive CTE edge collection and predicate pushdown (f866ebd)

  • feat: enhance MATCH clause to support named paths and add path variable handling in SQL rendering (61874cc)

  • feat: enhance SQL table name handling for Databricks compatibility and add tests (931f135)

  • feat: add TUI command for interactive query exploration and testing

  • Implemented a new TUI command to allow users to explore and test openCypher queries interactively.
  • Added functionality to load example queries and schemas from YAML files.
  • Enhanced schema loading to support both JSON and YAML formats.
  • Introduced a rich console for improved output formatting.
  • Updated CLI to include the new TUI command with a schema option.
  • Added tests to verify the availability of the TUI command and its schema option. (38b689e)
  • feat: Add support for UNWIND clause and HAVING expressions in SQL rendering
  • Implemented UnwindOperator to handle UNWIND clauses in logical plans.
  • Enhanced LogicalPlan to process UNWIND clauses and integrate them into the operator chain.
  • Updated ProjectionOperator to include HAVING expressions for filtering aggregated results.
  • Developed SQLRenderer methods to render UNWIND operations and HAVING clauses in Databricks SQL syntax.
  • Introduced optimizations for list predicates and comprehensions, including ARRAY_CONTAINS and FILTER functions.
  • Added support for rendering map literals, date, datetime, time, and duration from map expressions. (9e7e436)

Fix

  • fix: temporarily disable tests in CI and release workflows (b4df7f5)

  • fix: cache docs ci (5bd022e)

  • fix: remove --strict flag from mkdocs build command (b3bcd9e)

  • fix: anlt4 grammar (8bd22c1)

  • fix(docs): add .gitkeep to includes directory (5cbe9a8)

  • fix: release action (aeed997)

  • fix: c9i docs (d10141a)

  • fix: update documentation and workflow to use 'uv' (f456a42)

  • fix: faxina (959ad72)

  • fix: improve handling of variable-length path field names to prevent double-prefixing in SQL rendering (92080dc)

  • fix: enhance path and edges aliasing in SQL rendering to align with column resolver expectations (8bdaf48)

  • fix: preserve full column names for entity projections after aggregation to avoid UNRESOLVED_COLUMN errors

The Problem:

MATCH (p:POS)-[:PROCESSED]->(t:Transaction)
WITH p, COUNT(t) AS total_transactions
RETURN p.id, p.location -- ❌ Fails here
What happens:

Aggregation projects: SELECT _gsql2rsql_p_id AS p, COUNT(...)
Outer query tries: SELECT _gsql2rsql_p_id AS id
Error: _gsql2rsql_p_id doesn't exist anymore (it's been aliased to p) (155da2f)

  • fix(test): pyspark it's used to check the transpiler result in a real dataframe (07d7605)

  • fix: use the AggregationBoundaryOperator itself

When entities were used in MATCH clauses after aggregation boundaries (WITH + aggregation), they couldn't be resolved because the renderer was looking up resolved references in the wrong operator context.

Example Query:

MATCH (p:Person)-[:LIVES_IN]->(c:City)
WITH c, COUNT(p) AS population # Aggregation boundary
MATCH (c)<-[:LIVES_IN]-(other:Person) # c should be usable here
RETURN c.name, population, COUNT(other)
The Root Cause
the _render_aggregation_boundary_cte method was using the input operator as the context for rendering expressions:

context_op = op.in_operator if op.in_operator else op # Wrong!
This caused the renderer to look for resolved column references in the wrong operator's resolved expressions, resulting in "Unresolved column reference" errors.

The Fix
Changed to use the AggregationBoundaryOperator itself as the context:

context_op = op # Correct! The expressions were resolved against this operator
Why this works:

The ColumnResolver already resolves expressions in AggregationBoundaryOperator.group_keys and aggregates (line 521-522 in column_resolver.py)
These resolved references are stored with the AggregationBoundaryOperator's ID
By using the operator itself as context, the renderer can find the resolved references (6388679)

  • fix: tests (e4dfd5e)

  • fix: uses gsql2rsql pattern instead __ evitar conflitos (d271f4e)

  • fix: sql_render issues with WITH and col propagation (7091cb4)

  • fix: example wrong opencypher query (2d63406)

  • fix: detecting redundant node ID extraction pattern (16f3b25)

  • fix: missing deps for tui (b525eef)

Performance

  • perf: optimize undirected relationship queries with UNION ALL edge expansion

Problem

Undirected relationship queries (-[:REL]-) generated SQL with OR conditions
in JOIN clauses, preventing index usage and causing O(n²) execution plans:

-- Before (inefficient - OR prevents index usage)
JOIN Knows k ON (p.id = k.source_id OR p.id = k.target_id)
Impact: All 11 undirected PySpark tests timed out (&gt;60s each) even with
small datasets (10 nodes, 30 edges).

Solution
Implemented Option A: UNION ALL of edges strategy to expand edges
bidirectionally before joining, enabling hash/merge joins instead of nested loops:

-- After (optimized - simple equality enables hash join)
JOIN (
  SELECT source_id AS node_id, target_id AS other_id, props FROM Knows
  UNION ALL
  SELECT target_id AS node_id, source_id AS other_id, props FROM Knows
) k ON p.id = k.node_id
Key Features:

✅ Feature flag: config={&#34;undirected_strategy&#34;: &#34;union_edges&#34;} (default)
✅ Backward compatible: Can disable with &#34;or_join&#34; for debugging
✅ Defensive programming: Added validation to catch planner bugs early
Performance Results
Metric	Before	After	Improvement
Undirected tests passing	0/11	11/11	100% ✅
Runtime	Timeout (&gt;60s each)	19s total	&gt;30x faster
Complexity	O(n²) nested loops	O(n) hash joins	Scalable
Implementation Details
1. Renderer Changes (sql_renderer.py)
Added config parameter with undirected_strategy feature flag
Implemented _render_undirected_edge_union() (100 lines)
Modified _render_join() to detect and render UNION ALL for undirected joins
Updated _render_join_conditions() to use simple equality when optimized
2. Defensive Programming
Added _determine_column_side() helper method (66 lines)
Now validates field membership explicitly using right_aliases
Catches planner bugs early with clear error messages (fail-fast)
Prevents silent failures that would cause runtime SQL errors
Example defensive error:

RuntimeError: Field &#39;orphan&#39; not found in left or right join output schemas.
This indicates a bug in the query planner or resolver. ([`3074117`](https://github.com/devmessias/gsql2rsql/commit/30741174d420eabade7506badc760a40c0322dcd))

## Test

* test: add tests for multi-WITH entity continuation bug ([`7ec0add`](https://github.com/devmessias/gsql2rsql/commit/7ec0addc8eb3ceba4b3199dbf816f93b45528ec5))

## Unknown

* tests ([`12640c6`](https://github.com/devmessias/gsql2rsql/commit/12640c6ab07884a08312259d24dd9b36650d8af8))

* tests: claude new tests tdd ([`f777a98`](https://github.com/devmessias/gsql2rsql/commit/f777a9889b6f2b623e64222238bd04f9ffb5f96d))

* tests: claude tests with pyspark ([`6635739`](https://github.com/devmessias/gsql2rsql/commit/66357398ef8fac4d44c0ed66181a94d417f21ed9))

* claude tests ([`fe9e74f`](https://github.com/devmessias/gsql2rsql/commit/fe9e74f7374638d1426328830d3f229051e716e8))

* feat tui more examples and fix some stuff ([`7ffb039`](https://github.com/devmessias/gsql2rsql/commit/7ffb03993b4497897b641f99e5c8efc1525ac501))

* claude tests ([`9f561f5`](https://github.com/devmessias/gsql2rsql/commit/9f561f5cf83748448ffe84fc8f630aec6a702941))

* Refactor min_hops handling to ensure proper default value assignment and enhance depth condition checks in SQLRenderer ([`fec8b15`](https://github.com/devmessias/gsql2rsql/commit/fec8b15884af226c837c0d71dc11450011713535))

* claude code tests ([`175e087`](https://github.com/devmessias/gsql2rsql/commit/175e0872a6f31afd92917dd674560eddb14b7dab))

* Add COALESCE function and implement column pruning in SQLRenderer ([`00b7c03`](https://github.com/devmessias/gsql2rsql/commit/00b7c03586626fbfa845f3ae5af8df3632e87680))

* Add EXISTS subquery expression and related rendering logic ([`a388f52`](https://github.com/devmessias/gsql2rsql/commit/a388f52be8588b18a0354747c399c84b82804d8e))

* claude code novos testes ([`cc4c225`](https://github.com/devmessias/gsql2rsql/commit/cc4c22503af362f7a868d763359404f33b0f24c6))

* refactor pkg name ([`9320ec2`](https://github.com/devmessias/gsql2rsql/commit/9320ec29d79902fc7c55c7e56c07e41cbe9765f5))

* working prj ([`79b8cb7`](https://github.com/devmessias/gsql2rsql/commit/79b8cb74fb57566e9127df6b844e1c4afe35d60d))