# How the Cypher Query Engine Executes Read-Only OpenCypher Queries

> Discover how the Cypher query engine executes read-only OpenCypher queries by translating them to optimized SQL, blocking writes and processing through a six-stage pipeline.

- Repository: [Martin Vogel/codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp)
- Tags: internals
- Published: 2026-07-18

---

**The Cypher query engine in DeusData/codebase-memory-mcp translates OpenCypher pattern-matching queries into optimized SQLite SQL statements, executing them through a six-stage pipeline that explicitly blocks all write operations during the parsing phase.**

The DeusData/codebase-memory-mcp project provides a secure graph query interface for code analysis, implementing a custom Cypher query engine that processes read-only openCypher queries. Unlike full graph databases, this engine operates by transpiling graph patterns into relational algebra, allowing complex traversals over codebases while maintaining strict immutability guarantees.

## The Six-Stage Execution Pipeline

The engine processes queries through a rigid pipeline defined in [`src/cypher/cypher.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/cypher/cypher.c). Each stage transforms the input until it becomes executable SQL against the internal SQLite store.

### 1. Lexical Analysis with `cbm_lex`

The pipeline begins by tokenizing the raw query string. The `cbm_lex` function (lines 0‑30 in [`src/cypher/cypher.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/cypher/cypher.c)) initializes a lexer state that scans the input and produces a token stream. This stage handles keyword recognition, identifiers, and punctuation while rejecting malformed syntax early.

### 2. Parsing and AST Construction

A recursive-descent parser consumes the token stream to build an abstract syntax tree (AST). During this phase, the engine enforces the read-only grammar subset. Any attempt to use write operations triggers immediate rejection. As implemented in lines 816‑838 of [`src/cypher/cypher.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/cypher/cypher.c), the parser returns explicit errors such as `"unsupported Cypher feature: CREATE clause (write operations not supported)"` for constructs like `CREATE`, `DELETE`, `SET`, `MERGE`, `DROP`, or `CONSTRAINT`.

### 3. Query Planning and Graph Construction

Once parsed, the AST undergoes analysis to produce a logical execution plan. The planner constructs a **query graph** (lines 1148‑2211 in [`src/cypher/cypher.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/cypher/cypher.c)) that represents node and edge traversal patterns. It identifies `MATCH` clauses, label filters, and relationship directions, mapping these to the underlying relational schema where nodes and edges reside in SQLite tables.

### 4. SQL Translation

The logical plan converts to SQLite-compatible SQL statements (lines 2953‑3240 in [`src/cypher/cypher.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/cypher/cypher.c)). This translation layer handles:

- Converting `MATCH … WHERE …` patterns into `SELECT … FROM … WHERE` queries
- Transforming label tests (`WHERE n:Label`) into joins against the `labels` table
- Preserving `LIMIT`, `ORDER BY`, and `SKIP` clauses in the generated SQL

### 5. SQLite Execution

The generated SQL executes through the store's SQLite connection using `sqlite3_prepare_v2` and related APIs (lines 322‑350 in [`src/cypher/cypher.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/cypher/cypher.c)). Because the parser already filtered write operations, the executor issues only `SELECT` statements, ensuring the underlying graph store remains immutable during query processing.

### 6. Result Formatting

Raw SQLite rows marshal into JSON format for CLI consumption. The `query_graph` command in [`src/mcp/mcp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/mcp/mcp.c) (lines 415‑437) handles this final transformation, streaming results back to the caller as an array of node and relationship objects.

## Enforcing Read-Only Constraints

The engine maintains immutability through defense-in-depth. The parser acts as the primary gatekeeper, rejecting `CREATE`, `DELETE`, `DETACH DELETE`, `SET`, `REMOVE`, `MERGE`, `CALL`, `YIELD`, `FOREACH`, and schema modification clauses before they reach the planner. Even malformed attempts that might translate to SQL mutations are caught early, ensuring the SQLite authorizer never processes dangerous statements.

## Practical Usage Examples

### Querying via the CLI

The `query_graph` command provides direct access to the engine:

```bash
query_graph "MATCH (f:Function)-[e:CALLS]->(g) WHERE f.name = 'main' RETURN f,g"

```

This retrieves all call-graph edges for functions named "main", returning a JSON array of matching node pairs.

### Integrating with the C API

Applications can embed the engine directly using the public interface defined in [`src/cypher/cypher.h`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/cypher/cypher.h):

```c
#include "codebase_memory_mcp/cypher.h"

cbm_store *store = cbm_store_open("path/to/store.db");
cbm_lex *lex = NULL;
char *error = NULL;

/* Tokenize the query */
if (cbm_lex_new(&lex, "MATCH (n) RETURN n LIMIT 10", &error) != 0) {
    fprintf(stderr, "Lex error: %s\n", error);
    return;
}

/* Execute against the store */
cbm_result *res = NULL;
if (cbm_cypher_execute(store, lex, &res, &error) != 0) {
    fprintf(stderr, "Execution error: %s\n", error);
    return;
}

/* Process JSON results */
for (size_t i = 0; i < res->row_count; ++i) {
    printf("%s\n", res->rows[i].json);
}

```

### Error Handling for Write Operations

Attempting write operations returns clear error messages at the lexing stage:

```c
char *err = NULL;
cbm_lex_new(&lex, "CREATE (n:Node {name: 'test'})", &err);
/* err contains: "unsupported Cypher feature: CREATE clause (write operations not supported)" */

```

## Summary

- The **Cypher query engine** in DeusData/codebase-memory-mcpp implements a six-stage pipeline from lexing to JSON serialization.
- All queries execute as **read-only SQLite SQL**, with write clauses blocked during parsing in [`src/cypher/cypher.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/cypher/cypher.c) lines 816‑838.
- The **AST-to-SQL translation** handles complex graph patterns including multi-hop traversals and label filtering through joins.
- **Integration options** include both the `query_graph` CLI and the `cbm_cypher_execute` C API.
- Comprehensive **error handling** provides immediate feedback for unsupported write operations before they reach the execution layer.

## Frequently Asked Questions

### What subset of OpenCypher does the engine support?

The engine supports read-only pattern matching including `MATCH`, `WHERE`, `RETURN`, `LIMIT`, `ORDER BY`, and `SKIP` clauses. It handles node labels, relationship types, and property filtering. According to [`src/cypher/cypher.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/cypher/cypher.c) lines 816‑838, write operations, procedural calls (`CALL`), and schema modifications are explicitly rejected during parsing.

### How does the engine guarantee queries remain read-only?

The parser enforces read-only constraints before AST construction completes. Any token sequence representing `CREATE`, `DELETE`, `SET`, `MERGE`, or similar mutations triggers an immediate error return. This design ensures that only `SELECT`-equivalent statements reach the SQLite execution layer in [`src/store/store.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/store/store.c).

### Can I use this engine with external graph databases?

No. The engine is tightly coupled to the internal SQLite store defined in [`src/store/store.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/store/store.c). It transpiles Cypher to SQLite-specific SQL dialect, utilizing the project's specific table schema for nodes, edges, and labels. Direct connectivity to Neo4j or other external graph databases is not supported.

### What is the performance overhead of Cypher-to-SQL translation?

The translation overhead is minimal because the engine generates prepared SQL statements executed through `sqlite3_prepare_v2`. The query planner in lines 1148‑2211 performs optimizations during AST analysis, ensuring that complex multi-hop patterns translate into efficient joined queries rather than iterative traversals.