MemGQL Changelog

MemGQL v0.7.0 - July 5th, 2026

⚠️ Behavior changes

  • The info-queries-only log level is removed. --log-level=info-queries-only now fails at startup with the list of valid levels. Use --log-level=DEBUG to see queries and their transpiled output; plain INFO no longer logs query traffic.
  • The log file now honors --log-level. Previously the --log-file mirror received every line regardless of the console level; now both destinations get the same threshold-filtered stream.

✨ New features & Improvements

  • Memgraph as a query-driven cache. A catalog graph can now be cached in a Memgraph instance — aimed at data-warehouse connectors (Iceberg first) where a table is too large to materialize wholesale and every federated query otherwise re-scans it from object storage. Enable it per graph with a CACHE CONNECTOR clause:
    ADD CONNECTOR mg TYPE memgraph URI 'memgraph:7687';
    ADD GRAPH events ON CONNECTOR ice MAPPING warehouse READ ONLY
        CACHE CONNECTOR mg TTL 3600 MAX_BYTES 8G;
    The cache is populated by query execution itself (populate-on-read): the first read of a scope tees it into Memgraph (cache MISS), and later queries whose scans are covered are served entirely from Memgraph (cache HIT) with no source I/O. Because the cached data is a real graph in Memgraph, queries over cached scopes gain Memgraph’s full capability profile — variable-length expand, quantified patterns, graph algorithms — even when the source connector can’t express them. Toggle at runtime with ALTER GRAPH <g> SET CACHE CONNECTOR … / ALTER GRAPH <g> REMOVE CACHE, and inspect with SHOW GRAPH CACHES. TTL is in seconds; MAX_BYTES accepts a K/M/G/T binary suffix. Currently validated with Iceberg sources; the mechanism generalizes to the other warehouse connectors. See Multiple Graphs → Caching.
  • Cross-backend piping. Federated queries move far less data. When one side of a two-backend query is selective — it carries a literal property filter ({name: 'Memgraph'}), a LIMIT, or a bounded CALL — MemGQL runs that side first and uses its join keys to fetch only the matching rows from the other backend, instead of pulling the whole table and joining locally. Results are identical; queries that join a small, filtered set against a large remote table get faster. If the selective side matches nothing, the other backend isn’t queried at all. Piping applies to backends registered as catalog graphs (ADD GRAPH) and falls back to the previous behavior whenever it can’t apply. See Multiple Graphs.
  • Inline-map references across backends. A property inside a node’s inline map can now reference a binding from another MATCH part — and it pipes:
    MATCH (e:Employee)
    MATCH (i:Invoice {employee_id: e.id})
    RETURN e.name, i.total;
    Previously this join had to be spelled as WHERE i.employee_id = e.id. Works USE-free (each part routes by its own labels) and with multiple keys ({country: o.country, city: o.city}).
  • SQL Server connector. New sqlserver connector type (aliases: mssql, sql_server, sql-server) translates GQL to T-SQL. The driver is tiberius, a pure-Rust TDS implementation: no ODBC or driver install needed. It uses the same mapping format as every other SQL backend and is registered in multi mode with an ADO-style connection string:
    ADD CONNECTOR mssql TYPE sqlserver
        URI 'Server=localhost,1433;Database=test;User Id=sa;Password=…;TrustServerCertificate=true'
        MAPPING social;
    There is no standalone CONNECTOR_TYPE=sqlserver environment mode yet — SQL Server is multi-mode only. Works today: matching and filters, aggregates (incl. COUNT(DISTINCT …)), arithmetic, CASE, COALESCE / NULLIF, string predicates, IN, OPTIONAL MATCH, WITH pipelines (incl. whole-node carry-through), UNION, and typed whole-node / whole-relationship projections. See the SQL Server connector page for setup and for what’s not there yet (variable-length paths, collect(), map projections).
  • ADD GRAPH IF NOT EXISTS. Re-registering an existing graph with the new IF NOT EXISTS clause succeeds as a no-op instead of erroring — for re-runnable setup scripts. Plain ADD GRAPH still errors on a duplicate name.
  • Log levels. --log-level now accepts the standard severity ladder: CRITICAL, ERROR, WARNING, INFO (default), DEBUG, TRACE (case-insensitive). A level also emits everything more severe. Incoming queries and the generated Cypher / SQL are logged at DEBUG. See Reference.

MemGQL v0.6.3 - June 21st, 2026

✨ New features & Improvements

  • Schema-based routing (USE-free queries). In multi mode, queries no longer need a USE <graph> clause — the engine infers the backend from the query’s schema signals (labels, rel-types, properties) against a unified schema index, built from mappings for SQL backends and live introspection for Cypher backends. Routing is strict: exactly one candidate routes, zero or multiple hard-error with the fix; explicit USE always wins. USE-free federated JOIN and UNION work too, and writes route on a unique candidate. Two new statements expose the index: SHOW SCHEMA [FOR <graph>] and REFRESH SCHEMA. Identifier matching is now exact engine-wide. Still gated to explicit USE: non-default remote databases (Memgraph multi-tenancy), a single MATCH pattern spanning two backends, and a label-less MATCH (n) against a SQL backend.
  • Native Apache Iceberg connector (iceberg-direct). A Trino-free Iceberg connector that reads tables directly via the REST catalog and Apache Arrow scans from object storage (S3/MinIO) — no SQL engine in the query path, with projection and predicate pushdown into the scan. It reuses the same mapping format as the Trino-backed iceberg connector and joins other backends in multi-connector federation. Register with ADD CONNECTOR <name> TYPE iceberg-direct URI '<rest-catalog-uri>' MAPPING <mapping>. Read-only — writes return an “unsupported” error.

MemGQL v0.6.2 - June 7th, 2026

✨ New features & Improvements

  • Typed projections on every SQL backend. RETURN p now returns a structured Bolt Node and RETURN r a typed Bolt Relationship on every SQL connector (PostgreSQL, MySQL, DuckDB, ClickHouse, Iceberg, Pinot, Oracle) — previously SQL backends returned the underlying column values and a map projection (RETURN p {.id, .name}) was required. Nodes carry their label and mapped properties; relationships carry their type and mapped properties. Mixing scalar and whole-element projections in one RETURN (RETURN p.name, p) works too.
  • Connection-less queries (liveness check). RETURN 1, RETURN 1 + 2 AS x and WITH 1 AS x RETURN x now evaluate locally with no connector configured and no backend reachable — useful as a Bolt-level health probe.
  • ORDER BY by RETURN alias in cross-backend queries. A sort key can reference a projected alias (RETURN fact.tx_count AS tx_count … ORDER BY tx_count), including when the alias is nested inside an arithmetic expression (ORDER BY tx_count + 1). The post-join sort rewrites the alias back to its underlying expression.
  • Delimited (quoted) identifiers in GQL queries now parse.

🐞 Bug fixes

  • PostgreSQL NUMERIC columns now deserialize correctly — whole and fractional values arrive at the Bolt driver as Float and NULL is preserved. Previously NUMERIC-typed columns were unsupported.

MemGQL v0.6.1 - May 31st, 2026

✨ New features & Improvements

  • Cross-backend joins (phase 1). Queries that USE two or more different graphs in a single statement now execute as a federated left-deep hash-join chain inside the Bolt server. LinearQuery carries parts: Vec<FocusedPart>; a multi-USE query lowers to a CrossGraphJoin chain of per-part RemoteScans. dispatch_cross_backend peels Limit > Sort > Distinct > Project > Filter into a FederationPipeline, dispatches each part via the per-backend BoltHandler::run_plan, materializes rows to canonical scalars, and folds via hash-join (SQL 3VL — NULL keys dropped) or Cartesian product when there’s no equi-predicate. Per-side materialization is capped at 1,000,000 rows.
    • Verified end-to-end: Memgraph, MySQL, PostgreSQL, DuckDB.
    • Wired but not yet verified end-to-end: Neo4j, Oracle, ClickHouse, Iceberg, Pinot — the integration is mechanically identical and awaits a broader live-backend test harness.
    • Composite (multi-column) equi-joins are supported.
    • Post-join Filter / Sort / Limit / Distinct and a federation expression evaluator run over the joined wide row, including cross-part arithmetic, string functions (toUpper, toLower, length, size, trim), and residual filters (STARTS WITH, comparisons).
    • Three-backend left-deep chains with skip-level predicates are supported.
    • Unsupported shapes return a typed error with an actionable headline: whole-node returns across the federation boundary, non-literal LIMIT/SKIP, unrecognized plan nodes above the join (Aggregate, Union), unsupported functions, etc.

🚧 Known limitations

  • Whole-node RETURN m across federation is rejected; CanonicalScalar::Node modeling is deferred. Reference individual properties instead (m.name).
  • Scalar functions reach the GQL parser catch-all today (the evaluator supports them — covered by unit tests — but the e2e path waits on a parser extension; aggregates and COALESCE / NULLIF go through the normal FnCall path).
  • No output-cardinality cap (only per-side input cap of 1,000,000 rows). Per-side dispatch is sequential; parallel fan-out is a follow-up.

MemGQL v0.6.0 - May 23rd, 2026

⚠️ Breaking changes

  • id_column is now required on every edge mapping. Previously optional (enforced at query time only for variable-length traversal), it must now be present on every edge entry in the mapping JSON. A mapping without id_column on an edge now fails at registration with Failed to parse mapping JSON: missing field 'id_column' at line N, whether the mapping is supplied via MAPPING_FILE at startup or via ADD MAPPING at runtime. Update existing mappings by adding the edge table’s primary key column to every edge — see the quick-start and connector examples for the new shape.
  • Untyped edge traversal ()-[]->(b) now errors on SQL backends. Previously this expanded into a UNION over candidate rel-type mappings and could silently over-count when label-distinct node tables shared numeric IDs. The translator now returns an actionable error explaining why untyped traversal isn’t safe on SQL backends and pointing users at either declaring the edge type or running on a Cypher backend (Memgraph, Neo4j). Cypher backends still accept ()-[]->() natively.

✨ New features & Improvements

  • Trail semantics for bounded variable-length on SQL backends. Patterns like (){1,3} now enforce the GQL DIFFERENT EDGES default — no edge is reused within a single matched path. The recursive CTE carries an _edges visited-set whose shape is per-dialect (Postgres ARRAY, MySQL JSON_ARRAY, DuckDB LIST). On cyclic graphs this matches Memgraph and Neo4j byte-for-byte where previously SQL backends returned extra rows.
  • COUNT(DISTINCT …) works end-to-end on every backend. Previously count(DISTINCT x) could leak through to backends as the synthetic COUNT_DISTINCT(...) function (no engine has that). Both Cypher and SQL translators now special-case count_distinct / collect_distinct / collect_list_distinct to emit the dialect-native COUNT(DISTINCT …).
  • GQL parse errors now surface the actual ANTLR diagnostic (line 1:N no viable alternative at input '...') instead of being swallowed into the generic No statements in GQL query message.
  • Cypher-style variable-length syntax ([:R*], [:R*1..3], [:R*1..]) now produces an actionable hint pointing at the GQL quantified-path-pattern form (-[:R]->()){1,3} instead of a confusing parse error.
  • Cross-graph parse errors (multiple USE <graph> clauses in one query) now return a clear “not yet supported” message instead of the generic parse-failure error.

🐞 Bug fixes

  • % (modulo) parses as a proper binary operator, not as a synthetic function call.
  • FOR x IN [...] retains the iterated list and binds x correctly (new UnwindClause AST node; planner emits LogicalPlan::Unwind). Execution is Cypher-only today — SQL backends parse and plan it, then return an actionable error. See the reference limitations for the SQL-side status.
  • NEXT query composition resolves names bound on the left-hand-side when the right-hand-side RETURN references them.
  • Rel-variable reuse across MATCH clauses parses without a redeclaration error.
  • RETURN 1’s internal _dummy placeholder no longer leaks into Cypher queries sent to native backends.

MemGQL v0.5.0 - May 16th, 2026

✨ New features & Improvements

  • Added Oracle connector (CONNECTOR_TYPE=oracle).
  • DuckDB connector joins as a fifth GQL-over-SQL backend (alongside Memgraph, Neo4j, PostgreSQL, MySQL).
  • OPTIONAL MATCH now works on SQL backends (PostgreSQL, MySQL, DuckDB) — previously Cypher-only.
  • WITH pipeline boundary (GQL scope D) on SQL backends — supports WITH, WITH DISTINCT, WITH … ORDER BY … LIMIT N, chained WITH … WITH …, and whole-node WITH n carry-through via derived-table SQL.
  • UNION / UNION ALL / UNION DISTINCT between query statements work across all five backends. Branches on the same backend translate to that backend’s native combinator; branches on different backends materialize locally and combine in-memory. - Map projectionsRETURN n {.id, .title} AS info returns a Bolt Map (Memgraph, Neo4j, PostgreSQL, MySQL, DuckDB).
  • collect() aggregate returns a typed Bolt List (Memgraph, Neo4j, PostgreSQL, MySQL, DuckDB).
  • IN list-membership predicate — WHERE n.name IN ['Alice', 'Bob'].
  • STARTS WITH / ENDS WITH / CONTAINS string predicates portable across all backends.
  • Quantified path patterns (){m,n} on SQL backends emit a recursive CTE.
  • MATCH p = (…) RETURN p path binding works on Cypher backends and bounded-path SQL.
  • Unbounded variable-length paths (()-[*]->()) on SQL backends now return a clear error pointing at the bounded form ((){1,5}) or the Cypher fallback.
  • SHOW MAPPINGS / SHOW CONNECTORS error messages now hint at the correct setup statements (ADD MAPPING, ADD CONNECTOR).
  • Untyped edges ()-[]->(b) on SQL backends translate via a UNION ALL over candidate rel-type mappings.

🐞 Bug fixes

  • INSERT (a {…}) RETURN a.name no longer drops the RETURN clause.
  • % (modulo) operator now recognized in the grammar and routed through every translator.
  • PATH_LENGTH(p) on Cypher backends returns the integer length, not the relationship list.
  • Temporal types (date(...), LOCAL_DATETIME(...), etc.) arrive at the Bolt driver as proper Date / LocalDateTime structs (previously leaked as Rust debug-format strings).
  • RETURN column headers reflect the source expression text (n.age) instead of the literal placeholder "expr".
  • RETURN * no longer leaks internal Strategy-B _u / _e placeholders.
  • SKIP without LIMIT is now honored (was silently dropped).
  • NULL cells are sent as the PackStream 0xC0 byte (previously the string "NULL").
  • NULLIF and COALESCE work end-to-end on every backend.
  • OPTIONAL MATCH WHERE predicates inside the optional pattern land on the correct JOIN clause so unmatched outer rows survive Cypher’s semantics.

MemGQL v0.4.0 - May 7th, 2026

  • Added “Federated GQL Across Heterogeneous Backends” use case showing graph queries over ClickHouse and PostgreSQL
  • Added vector search capabilities (only Memgraph backend)
  • Fixed SET DEFAULT CONNECTION handling
  • Fixed flaky USE graph behavior and corrected USE graph routing
  • Fixed multi node and edge SQL INSERT
  • Fixed connection handling
  • Improved Trino startup wait
  • Fixed all tests under run_tests.sh

MemGQL v0.3.0 - April 26th, 2026

  • Added Apache Pinot connector support, including CONNECTION_TYPE=pinot single mode and multi-connection mode
  • Added MySQL connector support
  • Added multi-graph (USE graph …) and composite queries support

MemGQL v0.2.1 - April 17th, 2026

  • Fixed all required to make the Docker Compose example working as expected

MemGQL v0.2.0 - April 12th, 2026

  • Added MCP server
  • Added Clickhouse connector
  • Added the structured2graph agent to help generate mappings

MemGQL v0.1.0 - March 29th, 2026

  • GQL parser with ISO/IEC 39075 standard support including quantified path patterns
  • Federated Bolt server for querying across Neo4j, Memgraph, PostgreSQL, DuckDB, and Iceberg/Trino
  • GQL-to-native query translation (Cypher for graph DBs, SQL for relational)
  • Runtime connector management via ADD CONNECTOR, CONNECT, and USE statements
  • Shortest path queries with ALL SHORTEST, ANY SHORTEST, and SHORTEST k support