How the Cypher Query Engine Executes Read-Only OpenCypher Queries
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. 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) 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, 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) 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). This translation layer handles:
- Converting
MATCH … WHERE …patterns intoSELECT … FROM … WHEREqueries - Transforming label tests (
WHERE n:Label) into joins against thelabelstable - Preserving
LIMIT,ORDER BY, andSKIPclauses 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). 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 (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:
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:
#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:
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.clines 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_graphCLI and thecbm_cypher_executeC 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 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.
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. 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →