Expand description
v7.38 P0 元机制 A — injection_points framework.
§What this gives the test suite
Pepper the engine / storage hot paths with
crate::injection_point!("aggregate_spill_trigger", &group_count);and tests can do
engine.execute("SELECT spg_injection_attach('aggregate_spill_trigger', 'wait')")?;
// … spawn worker that hits the point …
engine.execute("SELECT spg_injection_wakeup('aggregate_spill_trigger')")?;to drive deterministic timing without sleep + probability. The
framework also supports error:<msg> (panic-cum-typed-error at the
site) and notice:<msg> (record + tally, never block). Multiple
attaches to the same point clobber the previous one — same as PG’s
injection_points_attach.
§Production safety
With the crate feature injection-points off (the default
release configuration) injection_point!() expands to the no-op
{ let _ = ($($payload,)*); }so the payload exprs still type-check (catching drift between site
and tests) but no runtime trace ever happens. Zero __trigger
symbol, zero thread-local touch, zero Mutex taken. The two unit
tests at the bottom of this file verify the no-op invariant by
sampling core::mem::size_of shadow markers and inspecting the
generated symbol table (see zero_cost_release_macro_expands_empty
and the cargo asm recipe documented next to it).
The SQL-facing spg_injection_* builtin functions follow the same
gating: with the feature off they return EvalError::TypeMismatch { detail: "injection-points feature not enabled in this build" }
so a release SPG cannot be coerced into deadlocking even if an
attacker manages to call them.
§Why thread-local not global
Two engines share a process during integration tests (e.g. the
permutation runner spins up embedded + server_simple engines in
parallel). A single global registry would let test A inject into
engine B; cross-contamination defeats the whole point. So:
- Each
crate::Engineowns anArc<InjectionStore>. - Before any
execute_*entry point we push the engine’s store onto a thread-local stack via [Engine::enter_injection_scope]. __triggerlooks up the current store —Noneis silently tolerated so dropping into the framework from a non-engine code path is safe.
Drop of InjectionGuard restores the previous store, so nested
scopes (engine-within-engine for exec_select_with_meta_views
style rewrites) compose.
Structs§
- Injection
Guard - RAII guard that does nothing when the feature is off.
- Injection
Store - Zero-sized placeholder so
Engine::injection_storesurvives the#[derive(Default)]derivation when the feature is off.Arc<InjectionStore>would still allocate;()-sizedInjectionStoreplus a wrappingcore::marker::PhantomDatakeeps the field at zero bytes.
Constants§
- REGISTERED_
POINTS - Stable identifiers for every
injection_point!()site SPG ships.