# Reference

## Supported GQL Query Features

What works today, split by backend category. "Cypher backends" means
Memgraph and Neo4j (translation is largely passthrough); "SQL backends"
means PostgreSQL, MySQL and DuckDB. **MongoDB** is neither — it translates to
aggregation pipelines — so it gets its own column.

SQL Server, SAP HANA, ClickHouse, Apache Iceberg, and Apache Pinot are also supported
as connectors but with a narrower verified surface. See each connector's page for the exact list of features each one
supports.

| Feature                                                            | Cypher backends | SQL backends | MongoDB |
|--------------------------------------------------------------------|-----------------|--------------|---------|
| `MATCH` / `WHERE` / `RETURN`                                       | ✓               | ✓            | ✓       |
| Pattern-level `WHERE` (`MATCH (n WHERE …)`)                        | ✓               | ✓            | ✓       |
| Multiple `MATCH` clauses in one query                              | ✓               | ✓            | ✓       |
| `OPTIONAL MATCH` (keep left side when no match found)              | ✓               | ✓            | ✓       |
| `WITH` clause (chain query steps)                                  | ✓               | ✓            | ✓       |
| `WITH DISTINCT` / `WITH … ORDER BY … LIMIT N`                      | ✓               | ✓            | ✓       |
| Multiple chained `WITH` steps in one query                         | ✓               | ✓            | ✓       |
| Pass a whole node through `WITH n` to a later step                 | ✓               | ✓            | ✓       |
| `MATCH (n)-[r:R]->(m)` typed edge expansion                        | ✓               | ✓            | ✓       |
| Untyped edge `()-[]->(b)` (union over types)                       | ✓               | ✗            | ✗       |
| `UNION` / `UNION ALL` / `UNION DISTINCT`                           | ✓               | ✓            | ✗       |
| `INTERSECT` / `EXCEPT`                                             | ✓               | ✓            | ✗       |
| Quantified path `(){m,n}` (bounded)                                | ✓               | ✓            | ✓       |
| Quantified path `(){m,}` (unbounded)                               | ✓               | ✗            | ✓       |
| Shortest-path (`ALL SHORTEST` / `ANY SHORTEST` / `SHORTEST k`)     | ✓               | ✗            | ✗       |
| Whole-node `RETURN n` / whole-relationship `RETURN r`              | ✓               | ✓            | ✓       |
| Map projections `RETURN n {.id, .title}`                           | ✓               | ✓            | ✓       |
| Connection-less `RETURN 1` / `RETURN 1 + 2` (liveness)             | ✓               | ✓            | ✓       |
| `IN` list membership `WHERE x IN […]`                              | ✓               | ✓            | ✓       |
| `STARTS WITH` / `ENDS WITH` / `CONTAINS`                           | ✓               | ✓            | ✓       |
| `FOR x IN […]` (UNWIND-style loop)                                 | ✓               | ✗            | ✓       |
| `collect()` / `collect_list()` (aggregate)                         | ✓               | ✓            | ✓       |
| `count`, `sum`, `avg`, `min`, `max`                                | ✓               | ✓            | ✓       |
| `COUNT(DISTINCT …)`                                                | ✓               | ✓            | ✓       |
| Arithmetic `+ - * / %`                                             | ✓               | ✓            | ✓       |
| `CASE WHEN … THEN … ELSE … END`                                    | ✓               | ✓            | ✓       |
| `COALESCE`, `NULLIF`                                               | ✓               | ✓            | ✓       |
| Scalar functions in `RETURN` (`upper(x)`, `abs(x)`, …)             | ✓               | ✓            | ✗       |
| Temporals (`date`, `datetime`, `localTime`, …)                     | ✓               | ✗            | ✗       |
| `INSERT (a {…})`                                                   | ✓               | ✓            | ✓       |
| `INSERT (a {…}) RETURN a.x` (post-insert projection)               | ✓               | ✓            | ✗       |
| `DELETE`                                                           | ✓               | ✓            | ✓       |
| `DETACH DELETE`                                                    | ✓               | ✗            | ✓       |
| `SET` (property update)                                            | ✓               | ✗            | ✓       |
| `REMOVE` (property delete)                                         | ✓               | ✗            | ✓       |
| `SET` / `REMOVE` of a **label**                                    | ✓               | ✗            | ✗       |

### Known limitations

- **Unbounded variable-length paths on SQL backends** (`()-[*]->()`)
  return an actionable error.
- **Untyped edge traversal on SQL backends and MongoDB** (`MATCH ()-[]->(b)`
  with no rel-type) returns an actionable error pointing users at declaring
  the edge type or running on a Cypher backend. Each relationship type is a
  separate table (or collection), so an untyped hop would have to union across
  every registered edge mapping. The form is still accepted natively on Cypher
  backends.
- **`FOR x IN [...]` (UNWIND-style) on SQL backends** returns an
  actionable error pointing users at running the query on a Cypher
  backend. The form is still accepted natively on Cypher backends.
- **`DETACH DELETE` on SQL backends** currently executes as a plain `DELETE`;
  relationship rows are not detached. It deletes a node that has no relationship
  rows and otherwise surfaces the backend's constraint error; don't rely on it.
- **Path variables on variable-length patterns**:
  `MATCH p = (a){1,3}(b) RETURN p` is not yet supported on SQL backends or
  MongoDB. Drop the `p =` binding (or query a Cypher backend) and `RETURN` the
  individual nodes / edges instead.

#### MongoDB-specific

- **`UNION` / `UNION ALL` / `INTERSECT` / `EXCEPT`** return an actionable error.
  Compose the arms client-side, or run the query on another backend.
- **Quantified paths are reachability, not path enumeration.** MongoDB runs
  `(){m,n}` on `$graphLookup`, which never revisits an edge document. That
  matches trail semantics (no edge repeats) and terminates on cycles, but it
  deduplicates across the whole traversal: where several distinct paths reach
  the same node, the SQL backends count each and MongoDB counts one. Use a
  Cypher backend when the number of paths is the answer.
- **Undirected variable-length** (`(-[:R]-()){m,n}`) returns an actionable error:
  `$graphLookup` follows a single connect-from/connect-to field pair. Give the
  pattern a direction.
- **Scalar functions inside `RETURN`** (`upper(n.name)`, `abs(-7)`,
  `char_length(s)`) return an actionable error. MemGQL passes such a call to
  the other backends as raw query text, which happens to parse in their SQL or
  Cypher dialect; an aggregation pipeline has no expression string to run one.
  Aggregates (`count`, `sum`, `avg`, `min`, `max`, `collect`, and their
  `DISTINCT` forms) are unaffected and run natively.
- **`INSERT … RETURN` drops the projection** and reports the affected count
  instead. Re-read the inserted node with a follow-up `MATCH`.
- **Every collection in one graph must live in the same database.** MongoDB's
  `$lookup` resolves collections inside the aggregation's own database and
  cannot join across databases; a mapping that spans two is rejected at
  translation time rather than silently reading the wrong collection. Use a
  second connector instead.
- **A label is a collection**, so `SET n:Label` / `REMOVE n:Label` would mean
  moving the document between collections and returns an actionable error.
- **`OPTIONAL MATCH` predicates may reference one variable.** A condition
  inside the optional pattern is folded into the `$lookup` that binds the
  variable it constrains; one spanning two variables returns an actionable
  error rather than silently dropping rows.

## Query Languages

MemGQL speaks **GQL** (the default) and **Cypher**. Which language a
statement is parsed in is resolved by precedence, highest first:

| Level | Mechanism                                            | Scope                                                    |
|-------|------------------------------------------------------|----------------------------------------------------------|
| 1     | `CYPHER [25] <query>` / `GQL <query>` prefix         | one statement                                            |
| 2     | `SET SESSION LANGUAGE CYPHER`                        | the current Bolt session                                 |
| 3     | `ADD CONNECTOR … LANGUAGE 'cypher'`                  | `USE <graph>` queries whose graph maps to that connector |
| 4     | `--default-language=<l>` / `MEMGQL_DEFAULT_LANGUAGE` | server                                                   |

Nothing set means GQL, so existing deployments see no change. Management
statements (`SHOW …`, `ADD CONNECTOR …`) are language-independent. A version
in the prefix (`CYPHER 25 MATCH …`, Neo4j-style) is accepted and recorded;
today every version selects the same dialect.

```cypher
CYPHER MATCH (e:Entity {name: 'Acme'})-[:HAS_ALIAS*0..]->(a) RETURN a.name;
CYPHER 25 USE live MATCH (m:Message) RETURN count(m) AS messages;
SET SESSION LANGUAGE CYPHER;   -- bare queries are Cypher from here on
MATCH (p)-[:BELONGS_TO*]->(ancestor) RETURN ancestor.name;
SET SESSION LANGUAGE GQL;
```

How a Cypher statement executes depends on the backend it routes to:

- **Memgraph / Neo4j:** the text executes **verbatim** — no
  parse → plan → re-generate round trip — so whatever the backend supports
  works. That includes Cypher constructs beyond MemGQL's own parser (`CALL`
  subqueries, pattern predicates, list comprehensions, `reduce`, `UNION`, map
  literals, writes): those forward as-is whenever the statement's target is
  unambiguously one Cypher backend (an explicit `USE <graph>`, or a single
  live connection). A graph set to `READ ONLY` still refuses write-looking
  statements before they reach the backend.
- **Every other backend** (Iceberg, PostgreSQL, Snowflake, …): the Cypher
  parses into the same internal representation as GQL and runs through the
  regular planning / translation path. The parsed surface covers `MATCH` /
  `OPTIONAL MATCH`, `WHERE`, `WITH`, `UNWIND`, `RETURN`, variable-length
  relationships (`[:R*0..]`, `[:R*1..3]`), relationship-type alternation
  (`[:A|B]`), `ORDER BY` / `SKIP` / `LIMIT`, and the expression core
  (comparisons, boolean logic, arithmetic, `IN`, `STARTS WITH` /
  `ENDS WITH` / `CONTAINS`, `IS [NOT] NULL`, `CASE`, lists, parameters,
  aggregates with `DISTINCT`).

One note on result columns for verbatim execution: when the statement parses
in MemGQL's Cypher grammar, columns come back in the query's `RETURN` order;
a statement that runs only via the verbatim forward reports its columns in
alphabetical order.

## Graph Management Query Syntax

```
-- Connectors (connections only)
ADD CONNECTOR <name> TYPE <type>
    [URI '<uri>'] [PATH '<path>']
    [USER '<user>'] [PASSWORD '<pass>']
    [DATABASE '<db>'] [CATALOG '<catalog>'] [SCHEMA '<schema>'] [GRAPH '<db>']
    [WAREHOUSE '<wh>'] [ROLE '<role>']
    [PRIVATE_KEY_PATH '<path>'] [TOKEN '<token>']
    [TENANT_ID '<tid>'] [CLIENT_ID '<cid>'] [CLIENT_SECRET '<secret>']
    [LANGUAGE '<gql|cypher>'];
DROP CONNECTOR <name>;
PING <connector>;

-- Session
SET SESSION LANGUAGE GQL|CYPHER [<version>];  -- language for bare queries on this session

-- Graphs (mappings over connectors)
CREATE GRAPH <name> FROM '<json>';        -- inline { "vertices": …, "edges": … } body
CREATE GRAPH <name> FROM FILE '<path>';   -- same body from a file
DROP GRAPH [IF EXISTS] <name>;
ALTER GRAPH <name> SET READ ONLY;
ALTER GRAPH <name> SET READ WRITE;

-- Query-driven cache
ALTER GRAPH <name> SET CACHE CONNECTOR <cache_connector> [TTL <secs>] [MAX_BYTES <n>[K|M|G|T]];
ALTER GRAPH <name> REMOVE CACHE;
SHOW GRAPH CACHES;   -- graph, cache_connector, ttl_secs, max_bytes, fragments, hits, misses, cached_properties
```

A connector is a **connection only**; it carries no graph shape. Which options
apply depends on the type:

| Option                                      | Read by                                                              |
|---------------------------------------------|----------------------------------------------------------------------|
| `GRAPH '<db>'`                              | Memgraph, Neo4j — selects the Cypher database (it is not a mapping)  |
| `DATABASE '<db>'`                           | PostgreSQL, MySQL, SQL Server, Oracle (service name), ClickHouse, MongoDB, Snowflake, Fabric (warehouse/lakehouse item), SAP HANA (tenant database of an MDC system) |
| `CATALOG '<catalog>'`                       | Iceberg (Trino catalog), Iceberg Direct (warehouse)                 |
| `SCHEMA '<schema>'`                         | Iceberg, Snowflake, Fabric (default `dbo`), SAP HANA (defaults to the connection user's own schema); MongoDB accepts it as a fallback for `DATABASE` |
| `WAREHOUSE` / `ROLE`                        | Snowflake session settings                                           |
| `PRIVATE_KEY_PATH` / `TOKEN`                | Snowflake auth (key-pair JWT / programmatic access token); `TOKEN` is also a Fabric Entra ID access token |
| `TENANT_ID` / `CLIENT_ID` / `CLIENT_SECRET` | Fabric service-principal auth (Microsoft Entra ID)     |
| `PATH '<path>'`                             | DuckDB (database file; `:memory:` by default)                        |
| `LANGUAGE '<lang>'`                         | Any connector — default [query language](https://memgraph.com/docs/memgraph-zero/memgql/reference#query-languages) (`gql` / `cypher`) for `USE <graph>` queries whose graph maps to it |

An option a connector doesn't read is ignored, and one that is omitted falls
back to that connector's environment variable. Re-adding a connector replaces
its config; `DROP CONNECTOR` is refused while a graph still references it.

`CREATE GRAPH … FROM` registers a graph from a `{ "vertices": …, "edges": … }`
body and auto-connects the connectors it references. The mapping format is
documented on the [Schema File](https://memgraph.com/docs/memgraph-zero/memgql/schema-file) page; the same
body loads at boot via `--schema`.

`SET CACHE CONNECTOR` makes a graph **cache-enabled**: touched labels and edge
types are copied into the Memgraph cache connector on first read, and later
covered queries are served from that cache. See
[Multiple Graphs → Caching](https://memgraph.com/docs/memgraph-zero/memgql/multiple-graphs#caching-a-graph-in-memgraph).
`TTL` is in seconds; `MAX_BYTES` accepts a plain byte count or a `K`/`M`/`G`/`T`
binary suffix (e.g. `8G`).

### Access modes

A graph is `READ WRITE` by default. Set it `READ ONLY` — with
`ALTER GRAPH <name> SET READ ONLY`, or `"accessMode": "readOnly"` in the
[schema file](https://memgraph.com/docs/memgraph-zero/memgql/schema-file#access-mode) — and any write
(`INSERT` / `DELETE` / `SET` / `REMOVE`) routed to it, explicitly via `USE` or
by label inference, is refused **before any backend sees the statement**, with
a message naming `ALTER GRAPH <name> SET READ WRITE` as the way to lift it.
The flip persists back to the boot `--schema` file, so a restart cannot
quietly reopen a replica, and `EXPORT SCHEMA` round-trips the mode. Typical
use: a vendor-refreshed replica (e.g. a Benchling Postgres copy) that must
stay read-only through MemGQL even though the connector itself supports
writes.

```
-- Introspection
SHOW CONNECTORS;                 -- registered connectors (name, type, uri, graph, connections)
SHOW CONNECTIONS;                -- live connections
SHOW GRAPHS [ON CONNECTOR <c>];  -- registered graphs (name, connector(s), type, access, counts)
SHOW GRAPH <name>;               -- one graph's details
SHOW MAPPINGS;                   -- per-graph mappings
SHOW SCHEMA [FOR <graph>] [AS GRAPH];  -- unified routing index: labels, rel-types, properties;
                                 --   AS GRAPH returns it as Bolt nodes and relationships
EXPORT SCHEMA [TO '<path>'];     -- merged catalog as canonical schema JSON (round-trippable)
REFRESH SCHEMA;                  -- re-introspect live Cypher connections (Memgraph/Neo4j)

-- Source onboarding (see below)
DESCRIBE CONNECTOR <name>;       -- the source's physical catalog: tables, columns, types, keys
GENERATE MAPPING FOR CONNECTOR <name> [GRAPH <g>] [TO '<path>'];  -- draft mapping from that catalog

-- Runtime counters
SHOW STATS;                      -- lists the targets: SHOW STATS CONNECTORS, SHOW STATS COMPUTE
SHOW STATS CONNECTORS;           -- connector, queries, rows, errors, avg_latency_ms, max_latency_ms
SHOW STATS COMPUTE;              -- key, value: cores.busy, cores.queued, cores.refused, queries.in_flight, cpu.secs, ...
RESET STATS;                     -- zero the per-connector counters

-- License and configuration
SHOW LICENSE;                    -- key, value: status, organization, valid_until, days_left, limit.max_cores, ...
SHOW CONFIG;                     -- name, value: compute.cores, compute.queue_timeout_ms, server.default_language, ...
```

`SHOW STATS CONNECTORS` reports what each **connector** saw — statements
MemGQL dispatched to it and rows it returned — so the load federation puts on
a production backend can be measured before rollout:

```
+-----------+---------+------+--------+----------------+----------------+
| connector | queries | rows | errors | avg_latency_ms | max_latency_ms |
+-----------+---------+------+--------+----------------+----------------+
| mg        |       3 |    6 |      0 |           6.42 |          11.03 |
| pg        |       3 |   12 |      0 |           9.18 |          18.55 |
+-----------+---------+------+--------+----------------+----------------+
```

Latency covers a whole query — the statement *and* the fetch of its rows — and
is reported in fractional milliseconds, since a healthy local source answers in
hundreds of microseconds. `max_latency_ms` sits next to the average because an
average hides the tail that shows up as a load problem. `RESET STATS` zeroes the
counters, so a single query's cost can be measured in isolation.

Counters are keyed by connector. **Cache** efficacy is keyed by graph and lives
in [`SHOW GRAPH CACHES`](https://memgraph.com/docs/memgraph-zero/memgql/multiple-graphs#caching-a-graph-in-memgraph)
(hits, misses, resident fragments).

### Onboarding a source

Writing a [mapping](https://memgraph.com/docs/memgraph-zero/memgql/schema-file) shouldn't require
out-of-band database access. `DESCRIBE CONNECTOR` reads a source's physical
catalog **live through the connector** — tables, columns, types, nullability,
primary keys, and single-column foreign keys as `→ table.column`
(PostgreSQL and SAP HANA; a graph backend answers via `SHOW SCHEMA` /
`REFRESH SCHEMA` instead and says so):

```
DESCRIBE CONNECTOR pgdemo;
```

```
+---------------------+---------------------+---------------------+---------------------+---------------------+---------------------+
| schema              | table               | column              | type                | nullable            | key                 |
+---------------------+---------------------+---------------------+---------------------+---------------------+---------------------+
| "public"            | "customers"         | "cid"               | "integer"           | "NO"                | "PRIMARY KEY"       |
| "public"            | "customers"         | "full_name"         | "text"              | "NO"                | ""                  |
| "public"            | "customers"         | "city"              | "text"              | "YES"               | ""                  |
| "public"            | "orders"            | "oid"               | "integer"           | "NO"                | "PRIMARY KEY"       |
| "public"            | "orders"            | "customer_id"       | "integer"           | "YES"               | "→ customers.cid" |
| "public"            | "orders"            | "total"             | "numeric"           | "NO"                | ""                  |
+---------------------+---------------------+---------------------+---------------------+---------------------+---------------------+
```

`GENERATE MAPPING FOR CONNECTOR <name>` turns that catalog into a **draft**
mapping: tables with a single-column primary key become vertices
(`metaFields.id` = the key), single-column foreign keys become table-backed
edges, and `json`/`jsonb` columns are typed `Json` so they stay descendable.
For the catalog above it emits:

```json
{
  "name": "pgdemo_draft",
  "_comment": "Draft mapping generated from the live 'pgdemo' catalog by GENERATE MAPPING. Review labels and attributes, then load explicitly: CREATE GRAPH pgdemo_draft FROM '<this JSON>'.",
  "vertices": [
    {
      "label": "customers",
      "mappedTableSource": { "connector": "pgdemo", "table": "customers", "metaFields": { "id": "cid" } },
      "attributes": [
        { "name": "cid", "type": "Int" },
        { "name": "full_name" },
        { "name": "city" }
      ]
    },
    {
      "label": "orders",
      "mappedTableSource": { "connector": "pgdemo", "table": "orders", "metaFields": { "id": "oid" } },
      "attributes": [
        { "name": "oid", "type": "Int" },
        { "name": "customer_id", "type": "Int" },
        { "name": "total", "type": "Double" }
      ]
    }
  ],
  "edges": [
    {
      "label": "ORDERS_CUSTOMER_ID",
      "from": "orders",
      "to": "customers",
      "mappedTableSource": { "connector": "pgdemo", "table": "orders", "metaFields": { "id": "oid", "from": "oid", "to": "customer_id" } }
    }
  ]
}
```

Edge labels are deliberately machine-looking (`ORDERS_CUSTOMER_ID` invites a
rename; a guessed business name would invite trust), and anything the draft
can't represent — composite keys, tables without a primary key, foreign keys
to non-key columns — is named in `_comment` rather than silently dropped.
The draft is **never loaded implicitly**: `TO '<path>'` writes it to a file,
and `CREATE GRAPH <name> FROM FILE '<path>'` accepts it verbatim once
reviewed.

Once graphs are registered, the merged schema itself is queryable as a graph:
`SHOW SCHEMA [FOR <graph>] AS GRAPH` returns one node per (source, label)
carrying its property list and one relationship per edge type —
cross-connector join edges included and marked — so an agent can plan a
federated query by navigating the schema, and a graph UI can draw it:

```
SHOW SCHEMA FOR shop AS GRAPH;
```

```
+---------------------------------------------------------------------------------------------------------------------+
| element                                                                                                             |
+---------------------------------------------------------------------------------------------------------------------+
| (:Customer {name: "Customer", source: "shop/pgdemo", connector: "pgdemo", properties: ["cid", "full_name", "id"]})  |
| (:Order {name: "Order", source: "shop/pgdemo", connector: "pgdemo", properties: ["id", "oid", "total"]})            |
| [:PLACED_BY {name: "PLACED_BY", source: "shop/pgdemo"}]                                                             |
+---------------------------------------------------------------------------------------------------------------------+
```

```
-- Single graph
USE <graph> <query>;

-- Composite
USE <graph1> <query>
UNION | UNION ALL | INTERSECT | INTERSECT ALL | EXCEPT | EXCEPT ALL
USE <graph2> <query>;

-- Routing: routes automatically when the query's labels / rel-types /
-- properties match exactly one registered source (see Multiple Graphs).
<query>;
```

In `multi` mode, a query with no `USE` clause routes automatically when its
schema signals (labels, relationship types, properties) match exactly one
source; zero or multiple matches hard-error. Identifier matching is **exact**
(`:person` ≠ `Person`). Memgraph sources must run with `--schema-info-enabled`
for property-level introspection. See
[Multiple Graphs → Routing](https://memgraph.com/docs/memgraph-zero/memgql/multiple-graphs#routing).

## Configuration Reference

### General

| Variable                  | Default          | Description                                                       |
|---------------------------|------------------|-------------------------------------------------------------------|
| `CONNECTOR_TYPE`          | `memgraph`       | Connector to use (see table below)                                |
| `CONNECTION_TYPE`         | _(none)_         | Alias for `CONNECTOR_TYPE`                                        |
| `BOLT_LISTEN_ADDR`        | `127.0.0.1:7688` | Address the Bolt server binds to                                  |
| `MEMGQL_DEFAULT_LANGUAGE` | `gql`            | [Query language](https://memgraph.com/docs/memgraph-zero/memgql/reference#query-languages) for bare (unprefixed) queries |

### Logging

MemGQL writes log lines to the console (stdout for most levels, stderr for
`ERROR` and `CRITICAL`) and, in parallel, to a log file. Both destinations
receive the same stream, filtered by `--log-level`. Both are configured via
CLI flags on the Bolt server binary.

#### CLI Flags

| Flag                       | Default            | Description                                          |
|----------------------------|--------------------|------------------------------------------------------|
| `--log-level=<LEVEL>`      | `INFO`             | Logging verbosity for console and file (see below)   |
| `--log-file=<PATH>`        | `bolt_server.log`  | File to mirror the (level-filtered) log output to    |
| `--default-language=<L>`   | `gql`              | [Query language](https://memgraph.com/docs/memgraph-zero/memgql/reference#query-languages) for bare queries (`gql` / `cypher`) |

#### Log Levels

Levels form a severity ladder; picking a level also emits everything more
severe. Values are case-insensitive (`--log-level=debug` and
`--log-level=DEBUG` are the same; `WARN` is accepted for `WARNING`).

| Level      | What it adds                                                          |
|------------|-----------------------------------------------------------------------|
| `CRITICAL` | Critical failures only                                                |
| `ERROR`    | + errors                                                              |
| `WARNING`  | + warnings                                                            |
| `INFO`     | + connections, state changes, lifecycle events (**default**)          |
| `DEBUG`    | + incoming queries and their transpiled Cypher / SQL                  |
| `TRACE`    | + plan and per-row execution detail                                   |

An unknown level fails at startup with an actionable error listing the valid
values. The `RUST_LOG` environment variable is not consulted.

To see query traffic and what MemGQL translates it into, run with
`--log-level=DEBUG`. The `info-queries-only` value from earlier releases was
removed in v0.7.0; see the
[changelog](https://memgraph.com/docs/memgraph-zero/memgql/changelog).

### Enterprise License

| Variable                      | Default | Description                                  |
|-------------------------------|---------|----------------------------------------------|
| `MEMGQL_ENTERPRISE_LICENSE`   | _(none)_ | License key (`mglk-...`)                    |
| `MEMGQL_ORGANIZATION_NAME`    | _(none)_ | Organization name to verify against license |

When set, the license is decoded and verified against the organization name at
startup. A valid enterprise license removes connector and connection limits and
carries its own [compute core limit](#compute). Without a license, community
mode allows up to 2 connectors, 2 simultaneous connections and 2 compute cores.
`SHOW LICENSE` reports the terms in force: `offering`, `status`, `valid_until`,
`days_left`, `limit.max_cores`, `limit.max_connectors`, `limit.max_connections`.

### Compute

| Variable                          | Default  | Description                                                                          |
|-----------------------------------|----------|--------------------------------------------------------------------------------------|
| `MEMGQL_COMPUTE_CORES`            | _(none)_ | Cap compute at this many cores; can only lower the budget, never raise it            |
| `MEMGQL_COMPUTE_QUEUE_TIMEOUT_MS` | `10000`  | How long a query waits for a free core before it is refused; `0` refuses immediately |

The **compute budget** is the number of cores MemGQL will use for in-process
work: it sizes the query workers, the embedded DuckDB engine and the native
Iceberg runtime, and at most that many queries do in-process compute at once.
Connections and in-flight queries are not limited by it: sessions waiting on a
remote backend do not consume the budget.

| Edition    | Max compute cores                                   |
|------------|-----------------------------------------------------|
| Community  | 2                                                   |
| Enterprise | The core limit carried by the license, or unlimited |

The effective budget is the **smallest** of the edition's limit, the cores
available to the process, and `MEMGQL_COMPUTE_CORES` when set. A value of
`MEMGQL_COMPUTE_CORES` above the edition's limit is ignored.

When every core is busy, a query waits up to the queue timeout and is then
refused with a `Compute limit reached` error; the client can retry.

- `SHOW CONFIG` reports the effective budget as `compute.cores`, alongside
  `compute.host_cores` and `compute.queue_timeout_ms`.
- `SHOW STATS COMPUTE` shows the live picture — cores busy, queued, peak and
  refused, queries in flight, and the server's own CPU time — and works in
  every connector mode, not just `multi`.

### Connector Types

| Connector        | Translation                 | Backend                          |
|------------------|-----------------------------|----------------------------------|
| `memgraph`       | None (passthrough)          | Memgraph                         |
| `memgraph-gql`   | GQL -> Cypher               | Memgraph                         |
| `neo4j`          | None (passthrough)          | Neo4j                            |
| `neo4j-gql`      | GQL -> Cypher               | Neo4j                            |
| `postgres`       | GQL -> SQL                  | PostgreSQL                       |
| `mysql`          | GQL -> SQL                  | MySQL 8.0+                       |
| `oracle`         | GQL -> SQL                  | Oracle 19c+ (incl. Free 23ai)    |
| `sqlserver`      | GQL -> SQL                  | Microsoft SQL Server (multi mode only) |
| `duckdb`         | GQL -> SQL                  | DuckDB (embedded)                |
| `clickhouse`     | GQL -> SQL                  | ClickHouse                       |
| `iceberg`        | GQL -> SQL                  | Iceberg via Trino                |
| `iceberg-direct` | None (native in-process)    | Iceberg (REST catalog + Arrow)   |
| `pinot`          | GQL -> SQL                  | Apache Pinot                     |
| `mongodb`        | GQL -> aggregation pipeline | MongoDB 5.0+                     |
| `hana`           | GQL -> SQL                  | SAP HANA 2.0 SPS05+, HANA Cloud, HANA Express |
| `multi`          | Per-connector               | Multiple backends simultaneously |

#### Memgraph (`memgraph`, `memgraph-gql`)

| Variable        | Default          | Description    |
|-----------------|------------------|----------------|
| `MEMGRAPH_URI`  | `127.0.0.1:7687` | Connection URI |
| `MEMGRAPH_USER` | `user`           | Username       |
| `MEMGRAPH_PASS` | `pass`           | Password       |
| `MEMGRAPH_DB`   | `memgraph`       | Database name  |

#### Neo4j (`neo4j`, `neo4j-gql`)

| Variable     | Default          | Description    |
|--------------|------------------|----------------|
| `NEO4J_URI`  | `127.0.0.1:7687` | Connection URI |
| `NEO4J_USER` | `neo4j`          | Username       |
| `NEO4J_PASS` | `password`       | Password       |
| `NEO4J_DB`   | `neo4j`          | Database name  |

#### PostgreSQL (`postgres`)

| Variable       | Default                                                          | Description               |
|----------------|------------------------------------------------------------------|---------------------------|
| `POSTGRES_URL` | `host=localhost user=postgres password=postgres dbname=postgres` | libpq connection string   |
| `MAPPING_FILE` | _(required)_                                                     | Path to JSON mapping file |

#### MySQL (`mysql`)

| Variable       | Default                                       | Description                              |
|----------------|-----------------------------------------------|------------------------------------------|
| `MYSQL_URL`    | `mysql://root:mysql@localhost:3306/test`      | MySQL connection URL (`mysql://user:pass@host:port/database`) |
| `MAPPING_FILE` | _(required)_                                  | Path to JSON mapping file                |

#### Oracle (`oracle`)

| Variable        | Default                                                  | Description                              |
|-----------------|----------------------------------------------------------|------------------------------------------|
| `ORACLE_URL`    | `oracle://system:oracle@localhost:1521/FREEPDB1`         | Easy Connect URL (`oracle://user:pass@host:port/service_name`). The default targets [Oracle Database Free 23ai](https://www.oracle.com/database/free/) (service `FREEPDB1`). |
| `MAPPING_FILE`  | _(required)_                                             | Path to JSON mapping file (same format as Postgres) |

The Oracle connector uses [oracle-rs](https://crates.io/crates/oracle-rs), a
pure-Rust implementation of Oracle's TNS wire protocol, pooled via
[deadpool-oracle](https://crates.io/crates/deadpool-oracle). No OCI / ODPI-C
/ Instant Client is required at build or runtime: the bundled Docker image
ships only the bolt server binary plus TLS roots, and local builds work on
macOS, Linux, and Windows without any extra system packages.

#### SQL Server (`sqlserver`)

SQL Server has no standalone environment-variable mode; `CONNECTOR_TYPE=sqlserver`
is not supported. Register it at runtime in
[`multi` mode](https://memgraph.com/docs/memgraph-zero/memgql/multiple-graphs) with an ADO-style
connection string:

```gql
ADD CONNECTOR mssql TYPE sqlserver
    URI 'Server=localhost,1433;Database=test;User Id=sa;Password=YourPassword;TrustServerCertificate=true';
-- then map it into a graph
CREATE GRAPH sales FROM FILE '/data/sales.graph.json';
```

`mssql`, `sql_server`, and `sql-server` are accepted aliases for the `sqlserver`
type. The connection string also takes `NoLock=true` (every read carries
`WITH (NOLOCK)`) and `IsolationLevel=<level>` (set once per session). See the
[SQL Server connector page](https://memgraph.com/docs/memgraph-zero/memgql/connect/sqlserver).

#### DuckDB (`duckdb`)

| Variable       | Default                         | Description               |
|----------------|---------------------------------|---------------------------|
| `DUCKDB_PATH`  | `:memory:`                      | Path to DuckDB file       |
| `MAPPING_FILE` | _(required)_ | Path to JSON mapping file |

#### ClickHouse (`clickhouse`)

| Variable          | Default                   | Description               |
|-------------------|---------------------------|---------------------------|
| `CLICKHOUSE_URL`  | `http://localhost:8123`   | ClickHouse HTTP API URL   |
| `CLICKHOUSE_USER` | `default`                 | ClickHouse user           |
| `CLICKHOUSE_PASS` | _(none)_                  | ClickHouse password       |
| `CLICKHOUSE_DB`   | `default`                 | ClickHouse database       |
| `MAPPING_FILE`    | _(required)_              | Path to JSON mapping file |

#### Apache Pinot (`pinot`)

| Variable              | Default                    | Description                                      |
|-----------------------|----------------------------|--------------------------------------------------|
| `PINOT_URL`           | `http://localhost:8099`    | Pinot broker base URL or full SQL endpoint       |
| `PINOT_QUERY_OPTIONS` | `useMultistageEngine=true` | Query options sent with broker SQL requests      |
| `MAPPING_FILE`        | _(required)_               | Path to JSON mapping file                        |

#### MongoDB (`mongodb`)

Translates to MongoDB aggregation pipelines rather than SQL. `mongo` is accepted
as an alias for the connector type.

| Variable       | Default                     | Description                              |
|----------------|-----------------------------|------------------------------------------|
| `MONGODB_URL`  | `mongodb://localhost:27017` | Connection string (`mongodb+srv://` too) |
| `MONGODB_DB`   | `test`                      | Default database                         |
| `MAPPING_FILE` | _(required)_                | Path to JSON mapping file                |

Every collection in one graph must live in the same database — MongoDB's
`$lookup` cannot join across databases.

#### SAP HANA (`hana`)

| Variable        | Default                                     | Description                                                |
|-----------------|---------------------------------------------|------------------------------------------------------------|
| `HANA_URL`      | `hdbsql://SYSTEM:HXEHana1@localhost:39017`  | Connection URL. The scheme selects the transport: `hdbsql://` plaintext, `hdbsqls://` TLS (required by SAP HANA Cloud) |
| `HANA_USER`     | _(from the URL)_                            | DB user, when not written into the URL                      |
| `HANA_PASSWORD` | _(from the URL)_                            | Password, when not written into the URL                     |
| `HANA_DATABASE` | _(none)_                                    | Tenant database of a multitenant (MDC) system               |
| `MAPPING_FILE`  | _(required)_                                | Path to JSON mapping file (same format as Postgres)         |

`sap-hana`, `sap_hana`, and `saphana` are accepted aliases for the `hana` type.

Prefer `HANA_USER` / `HANA_PASSWORD` (or the `USER` / `PASSWORD` connector
options) over embedding credentials in the URL: a HANA password often contains
`@` or `:`, which cannot be expressed inside a URL.

The connector uses [hdbconnect](https://crates.io/crates/hdbconnect), a
pure-Rust implementation of HANA's SQL Command Network Protocol. No ODBC, no
JDBC, and no SAP HANA client installation is required at build or runtime.

There is no `CATALOG` level for HANA: the tenant database is chosen at login,
not spelled into a qualified table name, so a HANA table reference is at most
`schema.table`. See the [SAP HANA connector page](https://memgraph.com/docs/memgraph-zero/memgql/connect/hana).

#### Iceberg (`iceberg`)

| Variable        | Default                 | Description               |
|-----------------|-------------------------|---------------------------|
| `TRINO_URL`     | `http://localhost:8080` | Trino REST API URL        |
| `TRINO_USER`    | `trino`                 | Trino user                |
| `TRINO_CATALOG` | `iceberg`               | Trino catalog             |
| `TRINO_SCHEMA`  | `default`               | Trino schema              |
| `MAPPING_FILE`  | _(required)_            | Path to JSON mapping file |

#### Iceberg Direct (`iceberg-direct`)

Native, in-process execution over Iceberg: reads the REST catalog and object
storage (S3/MinIO) directly, no Trino. Read-only.

| Variable                              | Default                 | Description                          |
|---------------------------------------|-------------------------|--------------------------------------|
| `ICEBERG_REST_URI`                    | `http://localhost:8181` | Iceberg REST Catalog URI             |
| `ICEBERG_WAREHOUSE`                   | `iceberg`               | Warehouse / catalog name             |
| `ICEBERG_SCHEMA`                      | `default`               | Default namespace (schema)           |
| `ICEBERG_DIRECT_S3_ENDPOINT`          | `http://localhost:9000` | S3/MinIO endpoint                    |
| `ICEBERG_DIRECT_S3_REGION`            | `us-east-1`             | S3 region                            |
| `ICEBERG_DIRECT_S3_ACCESS_KEY_ID`     | `admin`                 | S3/MinIO access key                  |
| `ICEBERG_DIRECT_S3_SECRET_ACCESS_KEY` | `password`              | S3/MinIO secret key                  |
| `MAPPING_FILE`                        | _(required)_            | Path to JSON mapping file    |

S3 path-style access is always enabled (`s3.path-style-access=true`).

## Mapping Schema

A graph's mapping declares its `vertices` and `edges`: labels and relationship
types mapped to backend tables (via `metaFields` and `attributes`) or to native
graph sources. The full format, with field tables and worked examples, lives on
the [Schema File](https://memgraph.com/docs/memgraph-zero/memgql/schema-file) page.

The same mapping body is used everywhere a graph is defined:

- **`--schema=<path>`** at boot: connectors + graphs in one file.
- **`CREATE GRAPH <name> FROM '<json>'`** / **`FROM FILE '<path>'`** at runtime.
- **`MAPPING_FILE`** in single-connector mode: a bare `{ "vertices": …, "edges": … }`
  body (the connector comes from the environment).

A mapping with a required field missing (for example a relational edge without
`metaFields.from` / `metaFields.to`) is rejected before the server serves
queries against it. The legacy `nodes` / `id_column` / `rel_type` format is no
longer accepted; loading it raises an actionable error pointing at the new
format.
