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.

§Parenthesized groups (v0.3.0)

Clause::Group wraps an inner QueryNode::Or subtree produced by a () expression in the query language. The translator recurses into the subtree via translate_and_group and parenthesizes the result so it composes correctly with surrounding AND/OR operators:

-- (level=error OR level=warn) AND service=payments
WHERE (((level = ?) OR (level = ?)) AND json_extract(fields, '$.service') = ?)

The extra level of parentheses is redundant for correctness but keeps the emitter uniform — every AND-group is parenthesized, whether it came from the top-level OR or from an inner Group clause.

§Pagination (v0.3.0)

QueryOptions bundles limit and offset so callers can request a specific page of results without separate function variants. Offset without limit uses LIMIT -1 — SQLite requires a LIMIT clause when OFFSET is present; -1 means unlimited in SQLite.

§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.

Structs§

QueryOptions
Options controlling result set size and starting position for execute.

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.