Query Pattern Dispatch System

GraphQLite uses a table-driven pattern dispatch system to execute Cypher queries. This document describes how the system works and how to extend it.

Overview

Instead of a massive if-else chain checking clause combinations, queries are matched against a registry of patterns. Each pattern defines:

  • Required clauses: Must all be present
  • Forbidden clauses: Must all be absent
  • Priority: Higher priority patterns are checked first
  • Handler: Function to execute the query

Supported Query Patterns

PatternRequiredForbiddenPriorityDescription
UNWIND+CREATEUNWIND, CREATERETURN, MATCH100Batch node/edge creation
WITH+MATCH+RETURNWITH, MATCH, RETURN-100Subquery pipeline
MATCH+CREATE+RETURNMATCH, CREATE, RETURN-100Match then create with results
MATCH+SETMATCH, SET-90Update matched nodes
MATCH+DELETEMATCH, DELETE-90Delete matched nodes
MATCH+REMOVEMATCH, REMOVE-90Remove properties/labels
MATCH+MERGEMATCH, MERGE-90Conditional create/match
MATCH+CREATEMATCH, CREATERETURN90Match then create
OPTIONAL_MATCH+RETURNMATCH, OPTIONAL, RETURNCREATE, SET, DELETE, MERGE80Left join pattern
MULTI_MATCH+RETURNMATCH, MULTI_MATCH, RETURNCREATE, SET, DELETE, MERGE80Multiple match clauses
MATCH+RETURNMATCH, RETURNOPTIONAL, MULTI_MATCH, CREATE, SET, DELETE, MERGE70Simple query
UNWIND+RETURNUNWIND, RETURNCREATE60List processing
CREATECREATEMATCH, UNWIND50Create nodes/edges
MERGEMERGEMATCH50Merge nodes/edges
SETSETMATCH50Standalone set
FOREACHFOREACH-50Iterate and update
MATCHMATCHRETURN, CREATE, SET, DELETE, MERGE, REMOVE40Match without return
RETURNRETURNMATCH, UNWIND, WITH10Expressions, graph algorithms
GENERIC--0Fallback for any query

How Pattern Matching Works

  1. Analyze: Extract clause flags from query AST
  2. Match: Find highest-priority pattern where:
    • All required flags are present
    • No forbidden flags are present
  3. Execute: Call the pattern's handler function
clause_flags flags = analyze_query_clauses(query);
const query_pattern *pattern = find_matching_pattern(flags);
return pattern->handler(executor, query, result, flags);

Debugging

Debug Logging

With GRAPHQLITE_DEBUG defined, pattern matching logs:

[CYPHER_DEBUG] Query clauses: MATCH|RETURN
[CYPHER_DEBUG] Matched pattern: MATCH+RETURN (priority 70)

EXPLAIN Command

Use EXPLAIN to see pattern info without executing:

SELECT cypher('EXPLAIN MATCH (n:Person) RETURN n.name');

Output:

Pattern: MATCH+RETURN
Clauses: MATCH|RETURN
SQL: SELECT ... FROM nodes ...

Adding New Patterns

Step 1: Define the Pattern

Add an entry to the patterns[] array in query_dispatch.c:

{
    .name = "MY_PATTERN",
    .required = CLAUSE_MATCH | CLAUSE_CUSTOM,
    .forbidden = CLAUSE_DELETE,
    .handler = handle_my_pattern,
    .priority = 85
}

Step 2: Implement the Handler

static int handle_my_pattern(cypher_executor *executor,
                             cypher_query *query,
                             cypher_result *result,
                             clause_flags flags)
{
    (void)flags;
    CYPHER_DEBUG("Executing MY_PATTERN via pattern dispatch");

    // Implementation here

    result->success = true;
    return 0;
}

Step 3: Add Tests

Add tests to test_query_dispatch.c:

static void test_pattern_my_pattern(void)
{
    const query_pattern *p = find_matching_pattern(CLAUSE_MATCH | CLAUSE_CUSTOM);
    CU_ASSERT_PTR_NOT_NULL(p);
    if (p) {
        CU_ASSERT_STRING_EQUAL(p->name, "MY_PATTERN");
        CU_ASSERT_EQUAL(p->priority, 85);
    }
}

Priority Guidelines

PriorityUse Case
100Most specific multi-clause combinations
90MATCH + write operation patterns
80Complex read patterns (OPTIONAL, multi-MATCH)
70Simple read patterns
50-60Standalone clauses with modifiers
40-50Standalone write clauses
10Expressions and algorithms
0Generic fallback

Files

  • src/include/executor/query_patterns.h - Types and API
  • src/backend/executor/query_dispatch.c - Pattern registry and handlers
  • tests/test_query_dispatch.c - Unit tests

Graph Algorithm Handling

Graph algorithms (PageRank, Dijkstra, etc.) are detected within the RETURN pattern handler. When a RETURN-only query contains a graph algorithm function call, it's executed via the C-based algorithm implementations for performance.