src/sydra/query/executor.zig
Purpose
Wraps the operator pipeline with a cursor API suitable for HTTP/pgwire surfaces.
See also
- Operator pipeline
- Physical plan builder
- Orchestration entrypoint
- pgwire server (streams cursor results over pgwire)
Definition index (public)
pub const Value
Alias:
value.Valuefromsrc/sydra/query/value.zig
pub const ExecuteError
Alias:
operator.ExecuteError
pub const OperatorStats
Alias:
operator.Operator.StatsSnapshot
pub const ExecutionStats
Timing and counters set by exec.execute, including:
- parse/validate/optimize/physical/pipeline timings (microseconds)
trace_idrows_emitted,rows_scanned
Full field list:
parse_us,validate_us,optimize_us,physical_us,pipeline_ustrace_id: []const u8rows_emitted: u64rows_scanned: u64
pub const ExecutionCursor
Fields:
allocator: std.mem.Allocator– allocator used for operator + optional arena cleanupoperator: *operator.Operator– root of the operator pipelinecolumns: []const plan.ColumnInfo– output schema (fromphysical.nodeOutput(root))arena: ?*std.heap.ArenaAllocator– optionally owned arena for AST/plan lifetimestats: ExecutionStats– timings/counters; filled byexec.execute
Methods:
next()– returns the next row from the operator pipelinedeinit()– destroys the operator and (if present) frees the arenacollectOperatorStats(allocator)– returns a snapshot list from the pipeline
Ownership notes:
next()returnsoperator.Rowvalues that are owned by the underlying operator; treat them as valid until the nextnext()call on the same cursor/operator.collectOperatorStatsreturns an owned slice; callers must free it with the allocator they passed in.- If
arenais non-null, it is owned by the cursor and is freed bydeinit()(this is howexec.executekeeps AST/plan pointers alive for the cursor lifetime).
ExecutionCursor (excerpt)
pub const ExecutionCursor = struct {
allocator: std.mem.Allocator,
operator: *operator.Operator,
columns: []const plan.ColumnInfo,
arena: ?*std.heap.ArenaAllocator = null,
stats: ExecutionStats = .{},
pub fn next(self: *ExecutionCursor) ExecuteError!?operator.Row {
return self.operator.next();
}
pub fn deinit(self: *ExecutionCursor) void {
self.operator.destroy();
if (self.arena) |arena_ptr| {
arena_ptr.deinit();
self.allocator.destroy(arena_ptr);
self.arena = null;
}
}
pub fn collectOperatorStats(self: *ExecutionCursor, allocator: std.mem.Allocator) ![]OperatorStats {
var list = ManagedArrayList(OperatorStats).init(allocator);
errdefer list.deinit();
try self.operator.collectStats(&list);
return try list.toOwnedSlice();
}
};
pub const Executor
Creates an ExecutionCursor from a physical plan:
Executor.init(allocator, engine, physical_plan)run()→ExecutionCursordeinit()– no-op (present for symmetry)
Fields:
allocator: std.mem.Allocatorengine: *Engineplan: physical.PhysicalPlan
Behavior notes:
run()builds the operator pipeline (operator.buildPipeline) and setscolumnsfromphysical.nodeOutput(plan.root).run()returns a cursor witharena = nulland zeroed stats; the orchestration entrypoint (exec.execute) wraps this and fills the timing fields + attaches an arena.