Skip to main content

Module executor

Module executor 

Source
Expand description

Query executor: translate a QueryNode into parameterized SQL, run it against the index, and reconstruct LogEntry values from the result rows.

This module is the bridge between the query AST and the SQLite schema. It never mixes user-controlled strings into SQL text — every literal value is bound as a parameter. The one exception is JSON extraction paths like $.service, which embed the field name directly because SQLite parameters aren’t allowed inside json_extract path expressions; safety there comes from the field name having passed validate_field_name’s strict regex in the parser, which we defensively re-check at the executor boundary.

§Disjunction (OR) shape

v0.2.0 introduced OR. The AST is QueryNode::Or(Vec<AndGroup>) where each AndGroup is a conjunction of clauses. The SQL we emit parenthesizes each AND-group and joins them with OR:

WHERE (level = ? AND json_extract(fields, '$.service') = ?)
   OR (level = ?)

For queries with no OR (a single AND-group), the parens are still emitted — the alternative is a special-case branch in the SQL builder that adds maintenance cost without performance benefit. SQLite’s planner ignores redundant parens.

§Timestamp handling

Timestamps are compared as TEXT, which works correctly for any ISO-8601 format because those sort lexicographically in chronological order when all components are fixed-width. Ingested timestamps that aren’t ISO-8601 shaped will compare incorrectly against last/since bounds — a known limitation of accepting arbitrary timestamp strings at ingestion time.

Functions§

execute
Execute a parsed query against the index and return matching entries.
execute_at
Variant of execute that uses a caller-supplied “now” value.