Graph query planning for relational data

Keep the tables.
Ask graph questions.

Crabgraph is a data fusion layer that turns Cypher, Gremlin, and SPARQL into a shared graph plan, then transpiles relational regions into SQL islands. Query the Iceberg tables, warehouse data, and schemas you already have.

Rust · Apache Arrow · built on DataFusion · DuckDB by default

query.plancycling live plans

01 CYPHER INPUT

MATCH (p:Person)-[:KNOWS]->(f)
WHERE f.city = 'Chicago'
RETURN p.name, count(f)

02 GRAPH IR

GraphNodeScan:Person
GraphExpand:KNOWS · both
GraphFiltercity = Chicago
GraphAggregateSQL island 01

03 DUCKDB SQL

SELECT p.name, count(f.id) …
FROM person p JOIN knows e …

01 / PREMISE

A graph layer should not dictate a storage layer.

Graph languages express paths, relationships, and traverser behavior well. Relational engines store and scan the data well. Crabgraph keeps those concerns separate.

Instead of flattening a graph query into one large SQL string, the planner preserves graph semantics in Graph IR. It lowers only the regions that have a correct relational equivalent. The rest stays explicit.

02 / BRING YOUR OWN SCHEMA

Your graph is a view over the data you have.

Keep data in Iceberg, your warehouse, or ordinary relational tables. DuckDB connects to a Polaris catalog, the Iceberg extension reads its tables, and cache_httpfs caches remote object reads. SQL views define the graph shape.

Cypher and Gremlin use node and edge mappings. SPARQL adds an ontology mapping that resolves classes and predicates to those same graph concepts. Every language reaches the same views, with no graph copy and no second source of truth.

cache_httpfsApache IcebergApache PolarisDuckDB views

01 EXPOSE EXISTING DATA

INSTALL cache_httpfs FROM community;
LOAD cache_httpfs;
INSTALL iceberg;  LOAD iceberg;

ATTACH 'warehouse' AS lake (
  TYPE iceberg, ENDPOINT 'https://polaris.example/api/catalog'
);

CREATE VIEW graph_accounts AS
  SELECT account_id, owner, risk
  FROM lake.prod.accounts;

CREATE VIEW graph_transfers AS
  SELECT transfer_id, from_id, to_id, amount
  FROM lake.prod.transfers;

02 MAP THE GRAPH SHAPE

[node.Account]
table = "graph_accounts"
id = "account_id"

[node.Account.properties]
resource_id = "account_id"
owner = "owner"
risk = "risk"

[edge.TRANSFERRED_TO]
table = "graph_transfers"
src = "from_id"  ·  dst = "to_id"

# Ontology: vocabulary → graph shape
ex:Account  →  Account(resource_id)
ex:owner    →  Account.owner

03 QUERY IT AS A GRAPH

MATCH (a:Account)-[t:TRANSFERRED_TO]->(b:Account)
RETURN a.owner, b.owner, t.amount

SQL PUSHDOWN

SELECT a.owner, b.owner, t.amount
FROM graph_accounts a
JOIN graph_transfers t ON t.from_id = a.account_id
JOIN graph_accounts b ON b.account_id = t.to_id
one schema, three graph languages, relational SQL underneath

03 / ARCHITECTURE

One graph plan. SQL where it fits.

Frontends and engines meet at a semantic contract, not at a storage format.

01

Preserve meaning first

Path mode, row multiplicity, optional matches, missing properties, and traverser state survive the frontend.

02

Lower safe regions

Scans, filters, expands, joins, projections, aggregates, and other relational regions become SQL islands.

03

Bring your own schema

Map node labels and edge types onto relational tables. The graph view does not require a new data store.

04

Decline, never guess

If the SQL path cannot preserve a value or operation, it falls back instead of returning an approximate answer.

04 / MEASURED COVERAGE

Coverage, with denominators.

These are development benchmarks from the checked-in corpus notes. They are not standards certifications.

Cypher relational lowering

88.4%

4,816 / 5,446 runnable cases lowered into relational work for SQL generation.

Ladybug corpus · full denominator · August 2026 snapshot

Gremlin accuracy

95.0%

1,584 / 1,667 runnable interpreter cases matched expected output. The separate DuckDB path matches 578 / 1,667.

TinkerPop corpus · interpreter · August 2026 snapshot

SPARQL Graph IR planning

96.0%

408 / 425 selected W3C query files reached Graph IR. A mapped 6 / 6 integration matrix also matched expected rows in DuckDB.

W3C RDF tests · query collections · August 2026 snapshot

Why different measures?

Accuracy compares an execution result with imported corpus output. Relational lowering counts plans that reach relational work for SQL generation. SPARQL planning counts W3C query files that reach Graph IR after parsing. The mapped SPARQL matrix separately checks expected DuckDB rows. The language review records the current Cypher and Gremlin denominators and reproduction commands. The SPARQL benchmark note pins its corpus commit and execution matrix.

Implementation coverage as of the repository's August 2026 benchmark notes
LayerCapabilityStatusBoundary
FrontendCypher parser and plannerAvailableBroad read coverage; full conformance remains in progress.
FrontendGremlin parser and plannerAvailableRepeat internals, graph algorithms, sack semantics, and meta-properties have known gaps.
FrontendSPARQL parser and plannerPartialParsed query algebra reaches Graph IR. Ontology mappings resolve SPARQL vocabulary to node labels, relationships, and properties before SQL lowering.
Semantic coreShared Graph IRAvailableModels scans, expands, paths, joins, aggregation, branching, repetition, and mutations.
FoundationDataFusion logical planningAvailableCrabgraph is built on DataFusion logical plans, extension nodes, and relational optimization.
SQL islandsRead-plan partitioning and fallbackAvailableUnsupported regions stay in Graph IR. Hybrid execution remains a migration path.
SQL islandsRecursive variable-length pathsPartialRecursive SQL exists; path rendering and remaining correlation shapes still have release gates.
DataBring your own relational schemaAvailableMap nodes and edges onto existing tables, DuckDB views, SQL queries, and Iceberg-backed views.
EngineDuckDBAvailableDefault feature and best-tested SQL target.
EnginePostgresPartialExecutor is feature-gated; compatibility and corpus gates are unfinished.
WritesSQL mutations and transactionsPlannedCreate, merge, set, delete, sequence, and transaction lowering remain outside SQL islands.
LanguageBroader SPARQL algebraPartialFilters, optional patterns, named graphs, values, paths, datasets, services, and all query forms plan. Aggregate and reduced algebra still use explicit extension nodes.
LanguageGQL and additional graph languagesPlannedGraph IR is designed to admit more frontends behind the same semantic boundary.

05 / ENGINE POSTURE

DuckDB executes. DataFusion plans.

Crabgraph is built on DataFusion for logical plans, extension nodes, and relational optimization. DuckDB is the best-developed SQL execution target today. The executor boundary can admit other databases as their dialect and behavior gates mature.

This is not a promise that every SQL engine behaves the same. It is a boundary that keeps engine choice separate from graph-language semantics.

PLANNING FOUNDATION CRABGRAPH IS BUILT ON

DataFusion

Logical plans
Extension nodes
Relational optimization

SQL EXECUTION DATABASE TARGETS

DuckDBDefaultbest-developed / measured
PostgresPartialfeature-gated
Other SQLPlannedexecutor boundary

06 / SOURCE

Build the graph layer above the data you have.

Read the planner, map your schema, run the corpus harnesses, or help define the next SQL target.

Open Crabgraph on GitHub
git clone https://github.com/henneberger/new-graph.git