Finish all todos from previous interaction outlined in `${base_impl_todo_file}`.
Focus on the build logic to convert ArrowTable into an efficient in-memory data structure.
Implement the build logic in `${builder_path}`.

Before editing, plan your approach and implementation steps.

IMPORTANT - populate the in-memory columns using the framework helpers in `column_ingest.hpp`. A copy of `column_ingest.hpp` is in your workspace (and it is also on the include path), so READ it to see exactly what each helper does. This helper header is the sanctioned extension point for generic ingest behavior: if a flat scalar column needs handling the current helpers do not cover, extend `column_ingest.hpp` once while preserving Arrow safe-cast, null-mask, scale, and range-check semantics, then call the helper from `${builder_path}`. Add `#include "column_ingest.hpp"`. Do NOT hand-roll Arrow decoding in `db_loader.cpp` - no `chunk->type_id()` switch, no `static_cast` to `ArrowNumericArray<T>`, no `raw_values()`/`GetValue`/manual decimal/endianness/scale handling.

Use Arrow casts to decode correctly, then store the narrowest correct C++ representation chosen by the storage plan. Use: `synnodb::ingest::scaled_integer<T>(*tables->TBL, "COL", DECIMALS)` for decimal/money/quantity columns (returns std::vector<T> of value*10^DECIMALS; choose T as the narrowest safe integer such as int16_t/int32_t/int64_t and use the column's decimal scale), `as_integer<T>` for integer/code/key columns (choose T from int8_t/uint8_t/.../int64_t/uint64_t based on schema/range/domain; do not default to int64_t when a narrower type is correct), `as_string` for strings, `as_date_days` for dates (int32 days since 1970-01-01), `as_double` for floating columns. `as_int64` and `scaled_int64` are compatibility aliases, not the default storage policy. These helpers accept Arrow physical representations by delegating to `arrow::compute::Cast`, so you do not handle the physical-vs-logical type difference, and a cast or range conversion that cannot be done raises.

Limit file inspection to: `${base_impl_todo_file}`, `${builder_path}`, `${query_impl_path}`, `${args_path}`, the query implementation files (`query<QID>.cpp/hpp`) and the parquet schema. Do not inspect validator/compile/cache/tool internals unless the error explicitly indicates an interface mismatch.
Do not use broad `find`, repo-wide `grep`, or directory scans. If a needed file is missing, inspect at most one directory level around the named files.

IMPORTANT: Focus on producing a correct build implementation. Prefer simple and efficient build code.

For the query execution logic in `${query_impl_path}` and the individual query files (`query<QID>.cpp/hpp`) keep the stubs for now. The full query-implementation will be done later.

After implementing, call compile tool to check for errors. If there are errors, fix them.
