ApexBase Engineering Guidelines¶
For coding agents and contributors working on the ApexBase codebase.
1. Query Signature Classifier — Single Source of Truth¶
All SQL classification MUST go through the centralized QuerySignature system.
Rust: query_signature::classify(sql)¶
- Location:
apexbase/src/query/query_signature.rs - Enum:
QuerySignature— classifies SQL intoCountStar,PointLookup,SimpleScanLimit,StringEqualityFilter,LikeFilter,DmlWrite,Ddl,Transaction,MultiStatement,SessionCommand,Explain,Cte,TableFunction,Complex - Usage: Call
classify()ONCE per query entry point, thenmatchon the result.
use crate::query::query_signature::{self, QuerySignature};
let sig = query_signature::classify(sql);
match &sig {
QuerySignature::CountStar { table } => { /* fast path */ }
QuerySignature::PointLookup { id } => { /* fast path */ }
_ => { /* full parse pipeline */ }
}
Python: Mirror classifier in _execute_impl()¶
- Location:
apexbase/python/apexbase/client.py→_execute_impl() - Pattern: Single
sql_upper = sql.strip().upper()+ if-elif chain →_sigstring - Rule:
sql.strip().upper()MUST happen exactly ONCE per_execute_impl()call
What is FORBIDDEN¶
- No inline SQL pattern matching in
bindings.rs,executor/mod.rs, orclient.pyoutside of the classifier - No duplicate
sql.to_uppercase()— each entry point does ONE classify/uppercase pass - No new fast paths added directly in bindings or client — add a new
QuerySignaturevariant first, then wire it through the 3 layers
2. Three-Layer Architecture¶
┌─────────────────────────────────────────────────┐
│ Python Client (client.py) │ Thin dispatcher
│ - Classifies once → _sig │ - Routes to correct Rust method
│ - Manages Python-side state (_in_txn, _lock) │ - NO query execution logic
├─────────────────────────────────────────────────┤
│ PyO3 Bindings (bindings.rs) │ Bridge layer
│ - classify() once per method entry │ - execute(): writes, txn, point lookups
│ - _execute_arrow_ffi(): all reads (zero-copy) │ - _execute_arrow_ipc(): multi-stmt, fallback
│ - _execute_like_ffi(): LIKE mmap scan │ - State: current_txn_id, cached_backends
├─────────────────────────────────────────────────┤
│ Rust Executor (executor/mod.rs, select.rs) │ Core engine
│ - classify() once in execute_with_base_dir() │ - Pre-parse fast paths: COUNT*, _id lookup
│ - Full SQL parse + CBO for Complex queries │ - Post-parse fast paths in select.rs (mmap)
└─────────────────────────────────────────────────┘
Dispatch rules by query type¶
| Signature | Python calls | Bindings method | Why |
|---|---|---|---|
count_star |
fast_row_count() |
atomic read | No Arrow overhead |
point_lookup |
execute() |
cached_backends DashMap |
Zero PathBuf hash |
scan_limit (≤500) |
execute() |
pread_rcix columnar |
columns_dict format |
like |
_execute_like_ffi() |
mmap scan → FFI | Zero-copy |
transaction |
execute() |
state management | txn_id tracking |
multi |
_execute_arrow_ipc() |
execute_multi_with_txn |
Transaction context |
write |
_execute_arrow_ffi() |
executor | Cache invalidation |
complex / other |
_execute_arrow_ffi() |
executor → FFI | Zero-copy Arrow |
3. Transaction Handling¶
- BEGIN → sets
_in_txn = True(Python) +current_txn_id(Rust) - COMMIT/ROLLBACK → clears both
- Multi-statement within txn → MUST go through
_execute_arrow_ipc()which callsexecute_multi_with_txn(stmts, ..., current_txn). NEVER route multi-statement throughexecute()when inside a transaction —execute()does NOT propagate txn context for multi-statement. - Single DML within txn → routes through
execute()→execute_in_txn()which buffers writes
4. Performance Rules¶
Fast Path Preservation¶
- cached_backends DashMap (bindings.rs): Per-instance cache keyed by table name string. Avoids global
STORAGE_CACHEPathBuf hashing. Populated on first point lookup, reused on subsequent calls. - pread_rcix (bindings.rs): Direct columnar read for
SELECT * LIMIT N. Returnscolumns_dictformat consumed byResultView(lazy_pydict=...). - Arrow FFI (zero-copy): Primary read path for all queries ≥500 rows. No serialization overhead.
- Arrow IPC (fallback): Used for multi-statement and when FFI fails.
What NOT to do¶
- NEVER add query result caching (e.g., caching PyObject results by SQL string). All optimizations must be genuine algorithmic improvements.
- NEVER add
sql.to_uppercase()calls outside the classifier. Each layer does ONE uppercase pass. - NEVER add new
if sql_upper.starts_with(...)checks in bindings.rs or client.py. Add a QuerySignature variant instead.
5. Adding a New Fast Path¶
- Add variant to
QuerySignatureenum inquery_signature.rs - Add classification logic in
classify()function - Add unit test in the
testsmodule ofquery_signature.rs - Wire executor dispatch in
execute_with_base_dir()(if pre-parse) orselect.rs(if post-parse) - Wire bindings dispatch in the appropriate method (
execute(),_execute_arrow_ffi(), etc.) - Wire Python dispatch in
_execute_impl()— add new_sigvalue + handler - Run full test suite:
cargo test --lib query_signature+maturin develop --release+pytest test/ -x -q
6. Testing Requirements¶
- Rust unit tests:
cargo test --lib query_signature— all classifier tests must pass - Python integration tests:
conda run -n dev maturin develop --release && conda run -n dev python -m pytest test/ -x -q - No test deletion: Never delete or weaken existing tests without explicit direction
- Regression tests: When fixing a bug, add a test that reproduces the original failure
7. Code Style¶
- Rust: Follow existing patterns —
useimports inside function scope for module-specific types,Arc<TableStorageBackend>for shared backends - Python: Pre-compiled regexes at module level (
_RE_*), singlesql_uppercomputation,ResultViewwrapping for all results - Comments: Preserve existing comments. Add
// QuerySignature: <variant>when adding new fast path dispatch. - No public API changes without explicit permission (applies to both Rust
pub fnand Python method signatures)
8. File Reference¶
| File | Role |
|---|---|
src/query/query_signature.rs |
QuerySignature enum + classify() |
src/query/mod.rs |
Module registration + re-exports |
src/query/executor/mod.rs |
Pre-parse dispatch, execute_with_base_dir |
src/query/executor/select.rs |
Post-parse SELECT fast paths (mmap) |
src/query/executor/joins.rs |
resolve_table_path, resolve_point_lookup_table_path |
src/python/bindings.rs |
PyO3 bridge: execute(), _execute_arrow_ffi/ipc/like |
python/apexbase/client.py |
Python client: _execute_impl() classifier + dispatch |