How the Cypher Query Engine Executes Read-Only OpenCypher Queries Against the Codebase Graph

The Cypher query engine translates read-only OpenCypher queries into SQLite SQL statements, executing them through a six-stage pipeline that guarantees graph immutability by rejecting write operations during parsing.

The codebase-memory-mcp repository implements a specialized Cypher query engine that enables read-only graph traversal over codebases stored in SQLite. This engine allows developers to query function call graphs and code dependencies using standard OpenCypher syntax while ensuring the underlying graph remains immutable.

Execution Pipeline Overview

The Cypher query engine processes queries through a strict pipeline defined in src/cypher/cypher.c. Each query string undergoes lexical analysis, parsing, planning, SQL generation, execution, and formatting before returning results. The engine explicitly blocks all mutating operations—CREATE, DELETE, SET, MERGE, and similar clauses trigger immediate parser errors (lines 816–838), ensuring only read-only operations reach the SQLite backend.

Stage-by-Stage Execution Flow

1. Lexing and Tokenization

The pipeline begins with cbm_lex, a lexer that tokenizes the raw Cypher query string. This stage identifies keywords, identifiers, literals, and structural symbols while validating basic syntax correctness.

cbm_lex *lex = NULL;
char *error = NULL;

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

The lexer handles the initial validation before passing tokens to the recursive-descent parser.

2. Parsing and AST Construction

A recursive-descent parser consumes the token stream to build an abstract syntax tree (AST). This stage enforces the read-only grammar subset by explicitly checking for prohibited clauses. Any attempt to use write operations triggers an error message such as "unsupported Cypher feature: CREATE clause (write operations not supported)" before execution begins.

3. Logical Planning

The planner analyzes the AST to construct a query graph describing node and edge traversal patterns. It identifies MATCH patterns, optional paths, label filters (WHERE n:Label), and relationship directions. This logical plan abstracts the Cypher semantics into a traversal structure that the SQL generator can translate.

4. SQL Translation

The logical plan converts to SQLite-compatible SQL statements. The translator maps Cypher constructs to relational operations:

  • MATCH … WHERE … becomes SELECT … FROM … WHERE …
  • Label tests convert to joins against the labels table
  • LIMIT, ORDER BY, and SKIP clauses map directly to SQL equivalents

This translation occurs in src/cypher/cypher.c (lines 2953–3240), producing optimized queries against the internal schema.

5. SQLite Execution

The generated SQL executes through the store's SQLite connection using sqlite3_prepare_v2 and related APIs. Because the parser already filtered write operations, the executor never issues INSERT, UPDATE, or DELETE statements. Results stream back as rows matching the requested projection.

cbm_result *res = NULL;
cbm_store *store = cbm_store_open("path/to/store.db");

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

6. Result Serialization

Raw SQLite rows marshal into JSON format for consumption by the CLI (query_graph command) or C API callers. The src/mcp/mcp.c file (lines 415–437) handles CLI output formatting, returning structured JSON arrays containing matched nodes and edges.

Read-Only Safety Guarantees

The engine implements defense-in-depth to prevent graph mutation. The parser explicitly rejects these Cypher features with dedicated error paths:

  • Data modification: CREATE, DELETE, DETACH DELETE, SET, REMOVE, MERGE
  • Schema operations: DROP, CONSTRAINT definitions
  • Procedural extensions: CALL, YIELD, FOREACH

Even malformed attempts that might otherwise translate to SQL (such as ATTACH) fail during parsing, ensuring the SQLite authorizer never receives mutation commands.

Practical Usage Examples

CLI Query Execution

Query the call graph for functions named "main":

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

The CLI routes the string through the six-stage pipeline and prints a JSON array of matching node pairs.

C Library Integration

Embed the engine directly in C applications:

#include "codebase_memory_mcp/cypher.h"

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

if (cbm_lex_new(&lex, "MATCH (n) RETURN n LIMIT 10", &error) == 0) {
    cbm_result *res = NULL;
    if (cbm_cypher_execute(store, lex, &res, &error) == 0) {
        for (size_t i = 0; i < res->row_count; ++i) {
            printf("%s\n", res->rows[i].json);
        }
    }
}

Write Operation Rejection

Attempting mutations returns immediate errors:

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

Key Source Files and Architecture

File Role
src/cypher/cypher.h Public API declarations for cbm_lex, cbm_cypher_execute, and result structures
src/cypher/cypher.c Core implementation including lexer (lines 0–30), parser error handling (lines 816–838), query-graph construction (lines 1148–2211), SQL generation (lines 2953–3240), and execution loop (lines 322–350)
src/mcp/mcp.c CLI frontend implementing query_graph command and JSON output formatting (lines 415–437)
src/store/store.c SQLite store wrapper managing database connections and query execution
tests/test_cypher.c Test suite validating read-only query execution and write-clause rejection

Summary

  • The Cypher query engine translates OpenCypher to SQLite SQL through a six-stage pipeline (lex, parse, plan, translate, execute, format).
  • Read-only guarantees are enforced at the parser level (lines 816–838 in cypher.c), blocking CREATE, DELETE, SET, MERGE, and other mutating clauses.
  • The engine stores codebase graphs in SQLite, converting MATCH patterns to SELECT statements with joins against the labels table.
  • Results return as JSON through the CLI (query_graph) or C API (cbm_cypher_execute).
  • Direct SQLite execution uses sqlite3_prepare_v2, ensuring prepared statement safety and performance.

Frequently Asked Questions

What Cypher clauses are blocked by the read-only engine?

The engine rejects CREATE, DELETE, DETACH DELETE, SET, REMOVE, MERGE, CALL, YIELD, FOREACH, DROP, and CONSTRAINT operations. Each blocked clause triggers a specific error message during parsing, preventing any write operations from reaching the SQLite backend.

How does the engine convert Cypher MATCH statements to SQL?

The planner analyzes MATCH patterns to build a query graph, then the SQL generator (lines 2953–3240 in cypher.c) translates these into SELECT statements. Label filters become joins against the labels table, WHERE clauses map to SQL WHERE, and pagination controls (LIMIT, SKIP) translate directly to SQL equivalents.

Can I use the Cypher engine as a standalone library?

Yes. Include src/cypher/cypher.h and link against the library. Initialize lexers with cbm_lex_new(), open stores with cbm_store_open(), and execute queries via cbm_cypher_execute(). The API returns structured results containing JSON-encoded rows suitable for integration into existing tools.

What database does the Cypher engine query against?

The engine queries an internal SQLite store defined in src/store/store.c. The codebase graph persists as relational tables (nodes, edges, labels), and the Cypher engine dynamically generates SQL to traverse these tables without requiring a separate graph database server.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →