Qwiratry — Post-SQL-TaOP-Improvements
Post-SQL TaOP Improvements
Follow-on work after Aggregate Functions, Textish Formats, TaOP Basics, SQL-Features deferrals, TaOP-Plan leftovers, and related SQL / TaOP landing. Items here were deferred from those implementation cuts.
A. From Qwiratry-AggregateFunctions-Plan.md
Moved from that plan’s former §K.2–§K.5. Cross-links to Aggregate Functions sections (e.g. §J.4, §A.6, §I, §H) still mean that document.
A.1. Low-hanging Aggregate Functions
(was §K.2)
Catalogue entries that fit the existing AggregateFunction /
MovingFrameAggregateFunction + aggsub surface: ordinary row /
window-* / optional frame-*, extractors in @.parameters, no FILTER /
DISTINCT, no ordered-set / hypothetical-set syntax, no COMBINEFUNC, no
whole-tree rewrite. Skip nulls like Postgres unless noted. Already shipped:
Sum, Avg, Count (COUNT(*)), Rank, DenseRank.
A.1.1. General-purpose folds (aggreduce / broadcast in aggmap)
(was §K.2.1)
Countover an expression (COUNT(expr)— count non-null)Min/MaxAnyValue(any_value)BoolAnd/BoolOr(andEveryas alias ofBoolAnd)BitAnd/BitOr/BitXorStringAgg/ArrayAgg/XmlAgg— concatenation in input /sortorder only; in-aggregateORDER BYis not this listJsonAgg/JsonbAgg/JsonObjectAgg(and strict variants that skip nulls) — same ordering caveat as above
A.1.2. Statistics (one or two Numeric extractors; finalize in CALL-ME / RESULT)
(was §K.2.2)
VarPop/VarSamp/StddevPop/StddevSamp(and aliasesVariance/Stddev)Corr/CovarPop/CovarSampRegrCount/RegrAvgx/RegrAvgy/RegrSxx/RegrSyy/RegrSxy/RegrSlope/RegrIntercept/RegrR2
A.1.3. Window-oriented (aggmap; Rank-style or framed)
(was §K.2.3)
RowNumberLag/Lead(use Driver$.previous/$.nextor the window buffer)FirstValue/LastValue/NthValue(needframe)Ntile/PercentRank/CumeDist(need window cardinality + counter)
A.1.4. Out of this bucket
(was §K.2.4)
Need other work: FILTER (Aggregate Functions Plan §J.4.1) / DISTINCT
(§A.6) forms; mode / percentile_* / hypothetical-set; in-aggregate
ORDER BY; SORTOP-only planner wins; SSPACE / MSSPACE /
FINALFUNC_MODIFY (and related CREATE AGGREGATE metadata); range /
multirange aggregates unless Raku grows matching types. Parallel / combine
hooks: §A.2 below.
A.2. Parallel Processing of Queries
(was §K.3)
Postgres partial / parallel aggregation: workers fold their own chunk, then merge states. Map the CREATE AGGREGATE hooks onto Qwiratry when (and if) the Driver or a SQL Location runs aggregates in parallel:
COMBINEFUNC— merge two transition states of the same aggregate into one (required for partial aggregation). Authors of associative aggregates (Sum,Count) can supply this; non-associative ones stay sequential.SERIALFUNC/DESERIALFUNC— turn opaque /internal-like state into bytes (or a transferable Raku value) and back, so state can cross threads, processes, or remote shards. Needed when state is not a plain shareable Raku object.PARALLEL— today$.parallelis stub-only (Aggregate Functions Plan §I). Use it as metadata (safe/restricted/unsafe) for pushdown and for whether the in-Raku Driver may split a window. Wire it when combine (and serialize, if needed) exist — the attribute alone does nothing.Prefer starting with in-process chunking +
COMBINEFUNCfor catalogue aggregates that are naturally associative; add serialize/deserialize when state must leave the process (SQL workers, distributed Locations).
A.3. TaOP Relation mutation APIs
(was §K.4)
Not sure we'll do this, but maybe.
Aggregate work today is pipeline-shaped: aggreduce / aggmap (and ⌿ / ⇶)
consume an item stream and emit results. They do not mutate a live TaOP
Relation / Table in place. Later, if TaOP wants OOP-style table methods:
Decide whether mutation means replace-columns / replace-relation, or only convenience wrappers that still return a new
Relation.Keep a single evaluation path: methods should call
aggreduce/aggmap(or the operators), not a parallel Driver.Align naming and semantics with Qwiratry-TaOP-Plan.md / Basics (row sinks, FieldMode) so aggregates do not invent a second mutation story.
Do not block Aggregate Functions catalogue or rewrite work on this — it is a TaOP front-end concern once Relations are stable.
A.4. Other later items
(was §K.5)
Indirect / computed callees (
my $c = Sum; $c.({.amount})).Split the transform out of
AggregateFunctions.rakumodif the file grows unwieldy.Nested aggregates inside extractor Blocks (body nesting is Aggregate Functions Plan §J.4; extractors are still not scanned).
B. From Qwiratry-TaOP-Textish.md
Textish §9 steps 1–13 are complete. Remaining follow-ups from that plan (§B.1 / §B.5 done under Parity §F.1):
B.1. Consider: public multi-phase formatter API (replicates TOP)
Done (Post-SQL §F.1.1 / Qwiratry-TaOP-Parity.md).
TOP formatters expose prepare-table / output-header / output-row /
output-footer and accumulate $.output; Database::Storage.format drives
that sequence. Qwiratry exposes the same phase methods on
Qwiratry::Format::Textish::RenderWalkable with one-shot render-walkable as
a thin driver. Pull/streaming remains the better large-table path long term
(§B.2 / §F.8).
B.2. Optional streaming (§9.14 / §7.5)
ParsePull / RenderPull for HalfHuman and WithBorders when the Streaming
proposal makes pull useful; today both Formats are walkable-only.
B.3. Richer Associative cell stringify (§6.5)
Something better than .raku for nested Associatives in Textish render
(shared !cell-string); not a §9.1–13 blocker.
B.4. Other textish Formats
HTML (tree-table Location — §I), Spreadsheet / .ods, Pod6, and richer
JSON (beyond Format-JSON). See TaOP Plan §5.4; not part of Textish v1.
B.5. Stronger HalfHuman round-trip
Done for emit-side space → _ (Post-SQL §F.1.2 / Parity Q3): HalfHuman
Render option :spaces-to-underscores (default on). Still does not paper over
Parse option misuse (e.g. headers without :headers). Further dialect
round-trip hardening stays optional.
B.6. Body-cell / per-cell formatting
(Textish §8.2; was Post-SQL F.1.3; backlog §F.8.1)
Today :header-alignment aligns header cells only; body keeps Int-right /
else-left. Out of scope for Textish v1.
Old TOP has no :header-alignment / :body-alignment options (body =
Int-right / else-left; WithBorders has a TODO to centre headers). Qwiratry
already went beyond TOP with header-only :header-alignment (defaults:
HalfHuman left-int-right; WithBorders centre). Not TOP parity.
Short-term shape (choose one):
(a) leave header-only as today;
(b) extend :header-alignment to body too;
(c) add a separate :body-alignment (or wait for per-cell).
Long-term: each cell may carry its own formatting when useful; Formats must still format raw values when none is provided.
Notify Tim on any golden string changes from this work.
B.7. Consider: SourceOperator returning an Iterator
Today evaluate always uses read-walkable (whole payload: typically Str /
path shapes) and does not return an Iterator. Pull is composed by
ParseOperator when both sides support it. Revisit only if that split
proves awkward; prerequisites already rejected bare Source → Iterator for
now.
C. From Qwiratry-TaOP-Basics.md
Basics first-cut work is done (Memory, Destination, Location::JSON, CSVdemo,
Database-as-LHS, Format-CSV / DatabaseStorage::CSV, Textish Formats).
Tree-document tables (§F.7): core Location::Tree + Format-JSON /
Format-YAML / Format-XML Location::* are implemented
(DatabaseStorage-Location-Tree.md
§16.1–§16.3). Remaining from Basics (excluding items already shipped whose
§7 text is stale):
C.1. WalkCursor backends (Basics §6)
SQL DatabaseStorage cursor mapping:
FETCH/MOVEontofetch-*/move-*.Metanoia Walkable-as-cursor-factory (deferred in Cursor proposal).
Mapping TaOP’s
Cursor::Storage::PostgresontoWalkCursor(0-based Raku vs 1-based SQL adaptors).
C.2. Sibling / later (Basics §7 step 7, §4.5)
Qwiratry::DatabaseStorage::SQLinraku-Qwiratry-DatabaseStorage-SQL(DBIish + SQL::Abstract).More Format pull; full Format
Supplypush (push is a Basics Non-goal / deferred).
C.3. Planned front-end (Basics Goals / §3.2 / DQ1)
Viewclass that doesRelation(Table already does).Query output as a
Relationwithout being aTable(DQ1 “MAY”).
C.4. Low priority
DQ10 —
QueryIterator.ofadvertisingTuple(defaultMuis fine for now).
D. From SQL-Features-Plan.md
Moved from that plan’s deferred language follow-ups: §B.3 / §C (paging and transactions), plus Destination deferrals from §A.2 / §A.4.5. Section A (File/HTTP Destination) and §B.2 (aggregates) stay in SQL-Features / Aggregate Functions plans.
D.1. Paging
(was SQL-Features §B.3)
Can probably just be done with array element ranges.
D.2. Transactions / Atoms
(was SQL-Features §C / §C.1)
Suitable model: TaOP's planned atom control-flow form
(TOP::ControlFlow) —
a block that binds KEEP / UNDO to commit / rollback on one or more
transaction-capable handles.
D.2.1. Language shape (sketch)
(was §C.1.1)
atom $location [, $location2, …] {
# destination / pipeline writes against those locations
}atomis ordinary block control flow, not a Destination glyph.Arguments are Location (or later Relation / Database) handles that advertise a transaction capability.
On normal exit: KEEP → commit each listed handle.
On failure / unwind: UNDO → rollback each listed handle.
Writes outside an
atomstay in direct mode (no transaction), matching TaOP's note for database actions outsideatom.
D.2.2. Fit with Destination
(was §C.1.2)
Destination operators inside
atomparticipate in the open transaction(s) when the target Location supports it.File (and most blob HTTP) typically lack multi-op transactions;
atomon a non-capable Location should fail clearly (capability / LocationError), not pretend atomicity.Full usefulness lands with Table / database Locations (later); this section still defines the language so Destination CrUD and TaOP Relation share one story.
D.2.3. Deferral notes
(was §C.1.3)
Specify
atomin the language / slang plan; do not require implementing transactional File or HTTP in the Destination operator cut (SQL-Features §A.4.5).Nested
atom, savepoints, and multi-Location two-phase commit: deferred.
D.3. Table / Schema Destination backends
(was SQL-Features §A.4.5)
§A.4.3 maps Destination operators onto Table (row sinks) and Schema (DDL). Language meaning is specified; only File and HTTP backends shipped in the Destination cut. Implement table row sinks and schema DDL against those columns when Table / Database Locations are ready.
D.4. RETURNING-style Destination results
(was SQL-Features §A.4.5)
Today Destination evaluation is payload pass-through: the write happens, then
the upstream payload continues down the pipeline. Later: optional
RETURNING-shaped results from Destination operators (language + backends that
can supply them).
D.5. Row locking
(was SQL-Features §A.2 Non-goal)
Row locking (FOR UPDATE, …) deferred from the first Destination / language
cut. Revisit when SQL / Table Locations need lock clauses on reads.
E. From Qwiratry-TaOP-Plan.md
Moved from that plan’s remaining Immediate Next Steps (§11 items 10–11), §12 Future plans, and other still-open design notes (§5.4–5.5, §7–§9, §10.4). Steps 1–9 of §11 are done (or partial with notes there).
E.1. Immediate next steps (remaining)
(was TaOP-Plan §11 items 10–11)
E.1.1. Pull the Qwiratry-specific parts of
Implementation::Loader(and possibly Singleton) that were imported intoQwiratry::Implementation::Loaderback out into a separate module — keep the existingQwiratry::Implementation::Loadermodules as well; do not move the whole thing.E.1.2.
raku-Qwiratry-DatabaseStorage-SQLwith DBIish + SQL::Abstract.
E.2. Future plans
(was TaOP-Plan §12)
Ideas that should not block the first TOP-on-Qwiratry slice (Memory + HalfHuman
Postgres-only SQL).
E.2.1. Red-style multi-dialect SQL encoding
(was §12.1)
When we support more than Postgres (SQLite, MySQL, …), follow
Red ORM’s architecture:
keep a driver-neutral AST of the query intent; each DB Location/driver
translates that AST to its dialect and runs it (Red: ResultSeq →
Red::AST → Red::Driver.translate).
Qwiratry hook (likely Walker / Plan for table+SQL origins): in-memory Walkers execute the Query AST directly; SQL Location drivers translate. Shared SQL encoding can stay near core; dialect quirks stay in Location packages. Until then, Postgres may emit Postgres SQL directly.
E.2.2. Other later items (already noted elsewhere)
(was §12.2)
Additional DBIish Locations (SQLite, MySQL) — Accept / when someone needs them.
Spreadsheet /
.ods, HTML, richer tree formats — TaOP matrix “Make/Accept” (also §E.3.1 / §B.4).Full streaming adoption per
Streaming-Qwiratry-Proposal.rakudoc.Predicate pushdown once multi-dialect translate exists.
E.2.3. SQL Features
(was §12.3)
SQL-Features Plan §B.3 / §C (and related Destination deferrals) now live in Post-SQL §D. Aggregate Functions absorption is done; leftovers in Post-SQL §A. Spec archive: old-design-docs/Qwiratry-AggregateFunctions-Plan.md, old-design-docs/SQL-Features-Plan.md.
E.3. Other still-open notes from the plan
E.3.1. Later formats (accept / make)
(was TaOP-Plan §5.4)
HTML, Spreadsheet / .ods, Pod6, richer JSON. Consume existing
raku-Qwiratry-Format-JSON rather than reimplement; tree-shaped formats use
Navigator. Overlaps Textish follow-ups in §B.
E.3.2. Public multi-phase Format API
(was TaOP-Plan §5.5)
Done for Textish (§B.1 / §F.1.1). TOP’s prepare-table /
output-header / output-row / output-footer vs Qwiratry one-shot render:
one-shot drives the phases on Textish::RenderWalkable.
E.3.3. Deferred TOP front-end concepts
(was TaOP-Plan §7)
View, TupleSet, Section, Lot, DataDictionary — design hooks only
until Table + DatabaseStorage solidified. View / query-as-Relation also in
§C.3.
E.3.4. SQL WalkCursor / Iterators
(was TaOP-Plan §8)
SQL DatabaseStorage should expose cursors as WalkCursor / Iterators
(dialects via DBIish + SQL::Abstract). Depends on §E.1.2; cursor mapping detail
also in §C.1.
E.3.5. Migrate / retire Raku-TOP
(was TaOP-Plan §9 sequencing step 7)
Migrate TaOP tests / demos; retire or thin-wrap old Raku-TOP once Qwiratry siblings cover the same ground.
E.3.6. FieldMode Overflow on SQL → JSON/JSONB
(was TaOP-Plan §10.4)
Support Automatic, Error, and Overflow as far as each backend allows; do not silently degrade Overflow on swap. Needs §E.1.2.
F. Suggested Implementation Order
Numbered checklist of work from this document. Priority order:
TOP parity — behaviour the old TOP code already had that Qwiratry still lacks (not greenfield TOP concepts).
Core hygiene — independent cleanups that do not need SQL (postponed; see archived spec).
SQL backend (MVP) — Postgres-shaped DatabaseStorage and related TOP Storage behaviour, plus multi-dialect follow-through.
TOP parity finalisation — migrate / retire Raku-TOP once parity + SQL MVP land.
New TOP features — greenfield TOP-shaped work in Qwiratry (after SQL MVP).
New SQL features — DB-backend extras beyond the SQL MVP.
Tree-document tables —
Location::Tree+ FormatLocation::*(JSON / YAML / XML).Other new features — Format-specific, streaming, migration, etc.
Aggregate Functions — catalogue and related work after the SQL sibling.
Research — XSLT / Spreadsheet function surveys.
Metanoias and Post-Metanoias — larger architectural rethinks.
Things to consider — optional / deferred cleanups (not blocking the order above).
Items are numbered section.item (e.g. first point in §F.1 is 1.1; first in §F.12 is 12.1). Wrapped lines hang past the checkbox and number (see 3.1). Cross-refs point at sections above. “Consider” / “Maybe” items stay optional.
F.1. TOP parity (highest — old TOP had these)
Spec: Qwiratry-TaOP-Parity.md.
1.1. Consider: public multi-phase formatter API (TOP
prepare-table/output-header/output-row/output-footer; one-shot drives phases) (§B.1; also §E.3.2).1.2. Stronger HalfHuman round-trip where dialect allows (§B.5).
F.2. Core hygiene
Spec: Core-Hygiene.md.Status: All parts postponed until later. Items live under §F.5 (paging) and §F.12 (loader / SourceOperator / DQ10).
F.3. SQL backend (MVP)
Old TOP had Postgres Storage / cursors / FieldMode; keep the sibling MVP tight. Multi-dialect is explicitly in-scope even though it extends past a single Postgres port.
3.1.
raku-Qwiratry-DatabaseStorage-SQL(DBIish + SQL::Abstract) (§E.1.2; also §C.2).3.2. SQL WalkCursor / Iterators;
FETCH/MOVEmapping; Postgres cursor adaptors (§C.1; also §E.3.4).3.3. FieldMode Overflow on SQL → JSON/JSONB (§E.3.6).
3.4. Table Destination backends (row sinks per §D.3 Table column; Schema DDL may wait for §F.6 if not needed for MVP).
3.5. Red-style multi-dialect SQL encoding (§E.2.1).
3.6. Additional DBIish Locations (SQLite, MySQL) (§E.2.2).
F.4. TOP parity finalisation
After §F.1 and the SQL MVP (§F.3) are in good shape.
4.1. Migrate TaOP tests/demos; retire or thin-wrap Raku-TOP (§E.3.5).
F.5. New TOP features (in Qwiratry; after SQL MVP)
Greenfield TOP-shaped language / front-end work that was not in the old TOP code.
5.1.
View(doesRelation) (§C.3; also §E.3.3).5.2. Richer Associative cell stringify (TOP TODO / Textish §6.5) (§B.3).
5.3.
Section,Lot,DataDictionary(andTupleSetif not done as §5.8) (§E.3.3).5.4. Paging via array element ranges or equivalent (§D.1).
5.5.
atomslang + transaction capability (§D.2 / §D.2.1–§D.2.3).5.6. Nested
atom/ savepoints / multi-Location 2PC (§D.2.3).5.7. Maybe: Query output as a
Relationwithout being aTable(§C.3; Parity §7).5.8. Maybe:
TupleSet(§E.3.3; Parity §7 — never implemented in old TOP).
F.6. New SQL features (DB backend; after SQL MVP)
Extras that belong on the SQL / DatabaseStorage sibling rather than core Qwiratry.
6.1. Schema Destination / DDL side of §D.3 (if deferred from §F.3).
6.2.
RETURNING-style Destination results (§D.4).6.3. Row locking (
FOR UPDATE, …) (§D.5).6.4. Predicate pushdown once multi-dialect translate exists (§E.2.2).
F.7. Tree-document tables
Spec: DatabaseStorage-Location-Tree.md. Projection stages use tree navigation with Format defaults (caller → Format default → error) and merge-on-flush.
Shipped (plan §16.1–§16.3):
7.1. Core
DatabaseStorage::Location::Treein raku-Qwiratry (three-stage pipeline, §8 resolution, merge-on-flush) (DatabaseStorage-Location-Tree.md §16.1.2 / §16.1.4).7.2. Format-JSON thin
Location::JSONwith root/identity/object-key §8 defaults (§16.1.3 / §16.1.5).7.3. Format-YAML thin
Location::YAMLwith JSON-like §8 defaults (§16.2).7.4. Format-XML thin
Location::XMLwith blank §8 defaults (§16.3).
Follow-ons from the Tree plan (not in the JSON/YAML/XML first cut): (numbers align with plan §16.4 where noted)
7.5. Path-constrained tree walk (plan §10.4 / §16.4.1):
Qwiratry::Walker::Implementation::TreeWithPath; tree node is still the walk root; invoke as$transformer($tree-root, :path($path))/TRANSFORM-PATH(Q21); transform-only (Q22); step following via tree Navigators (Q23); missing-step frames drive ordinaryAPPLY+ Associative attach (Q24);t/unit/tree-with-path.rakutest.7.6. Path auto-creation — core (plan §10.3 / §16.4.2):
Location::Tree$.path-transformer, projection/useTablewiring,associative-path-transformer,ensure-document-path;t/unit/path-transformer.rakutest.7.7. Path auto-creation — Format / other dists (plan §10.3 / §16.4.3): JSON / YAML default
associative-path-transformer; XMLEMPTY→XML::Elementnamed from the path step; Format location tests cover defaults (no Test-dist helper).7.8. In-document missing-node path creation (plan §10 / Q7 / §16.4.4 / Q26–Q29 / Q31):
derive-write-path/:table-write-path;TreeWithPath:table-action+ Positional/XML append; flush path-ensure when derivable. Under-table DestinationMode gating remains for later polish.7.9. Flush/merge inverse via Qwiratry
Transformermolds (Q18 / plan §16.4.5):transformforwards:contextto inlineAPPLY; JSON / YAML default:transformermolds use$*CONTEXT<rows>(Callable overrides still work).7.10. Multi-site
:table-name-selectorend-to-end (Q2 / Q12 / Q15 / plan §16.4.6):select-table-rootcandidate-list origin + List/slipfilters;t/unit/table-name-selector.rakutest(includinguseTablehydrate).7.11. Sync API polish / coverage:
.check-fresh/.rehydrate/.flushwith:throw-on-conflict(Q20a / Q20b / plan §16.4.7); Tree mutations no longer auto-flush;t/unit/tree-sync-api.rakutest.7.12. Re-run JSON / YAML / XML tests after path-walk / path-transformer / sync work (plan §16.4.8); Location tests use explicit
.flush(Tree no longer auto-flushes).7.13. Optional later: HTML Format + Navigator +
Location::HTMLwith sensible<table>/ body-row / cell defaults (§I).
Streaming whole-document vs push/pull rehydrate (Q5) stays under §F.8 streaming items.
F.8. Other new features (formats, streaming, …)
8.1. Body-cell / per-cell formatting (§B.6).
8.2. Later Formats: HTML (§I), Spreadsheet/
.ods, Pod6, richer JSON (§B.4; also §E.3.1 / §E.2.2).8.3. HalfHuman / WithBorders
ParsePull/RenderPull(§B.2).8.4. More Format pull; Format
Supplypush (§C.2; Streaming-v2).8.5. Full streaming adoption per Streaming proposals (§E.2.2).
F.9. Aggregate Functions (after SQL backend)
9.1. General-purpose folds:
Count(expr),Min/Max,AnyValue,BoolAnd/BoolOr/Every, bit aggregates, string/array/xml/json aggs (§A.1.1).9.2. Statistics folds: variance/stddev, corr/covar, regr_* (§A.1.2).
9.3. Window-oriented catalogue:
RowNumber,Lag/Lead,FirstValue/LastValue/NthValue,Ntile/PercentRank/CumeDist(§A.1.3).9.4. Out-of-bucket aggregate forms when ready:
FILTER/DISTINCT, ordered-set / hypothetical-set, in-aggregateORDER BY, CREATE AGGREGATE metadata hooks (§A.1.4; also §A.2 for parallel).9.5. Indirect / computed aggregate callees (§A.4).
9.6. Split transform out of
AggregateFunctions.rakumodif needed (§A.4).9.7. Nested aggregates inside extractor Blocks (§A.4).
9.8. Consider: TaOP Relation mutation APIs wrapping
aggreduce/aggmap(§A.3).9.9. Parallel aggregation:
COMBINEFUNC, thenSERIALFUNC/DESERIALFUNC, wire$.parallel(§A.2).
F.10. Research
10.1. More XSLT features including functions (research).
10.2. Spreadsheet Functions (Research).
F.11. Metanoias and Post-Metanoias
11.1. Metanoia Walkable-as-cursor-factory (§C.1).
F.12. Things to consider
Deferred from postponed §F.2 (see archived Core-Hygiene.md). Paging moved to §F.5.4.
12.1. Extract the relevant parts of
Implementation::Loader/ Singleton to a separate module again; keep the existingQwiratry::Implementation::Loadermodules (§E.1.1).12.2. Consider (after Streaming v2):
SourceOperatorreturning an Iterator (§B.7).12.3. DQ10:
QueryIterator.ofadvertisingTuple(§C.4).12.4. Expand Tree Location write-path axis lowering beyond
child/parent(§G.1).12.5. Wire
TreeWithPathinto Query AST planning / query walkers (§G.2).12.6. Strategy participation in path-walk attach (§H).
G. Tree Location Improvements (future)
G.1. Tree Location write-path axes (future)
Related: DatabaseStorage-Location-Tree.md
§10.3–§10.4 / Q26 (derive :path from :table-selector /
:row-selector query AST, or optional :table-write-path).
First cut (landed with Q26): lower only single-step navigation
axes — child (⪪) and parent (⪫) — with plain name
selectors. No descendant/ancestor, no filters / predicates / wildcards
that are not a simple step name. If the selector exceeds those limits
and no :table-write-path is provided, writes that need path
auto-creation error (reads may still use the full selector).
Later (this section): consider expanding the allowed write-path
axis set (e.g. limited descendant forms, or other single-step axes)
once child/parent lowering and :table-write-path are solid. Track
implementation under §F.12.4.
G.2. Wire TreeWithPath into Query AST
Related: DatabaseStorage-Location-Tree.md §10.4 / Q21–Q23; Post-SQL §F.7.5.
First cut (landed): Qwiratry::Walker::Implementation::TreeWithPath
is transform-only (Q22). It runs when a Transformer is invoked with
:path / TRANSFORM-PATH (Q21) — path auto-creation for Tree Location
(:path-transformer, :table-action). Step following reuses tree
Navigators (Q23). It is not selected by Query AST planning or
ordinary query walkers.
Why deferred: path-constrained walk and document query fan-out are
different traversal models. Query walkers visit siblings / axes under a
match; TreeWithPath follows one ordered path and yields step frames
(present vs missing) for mold APPLY. Mixing them early would blur
“query over an existing tree” with “grow missing steps on a write path.”
Later (this section): consider wiring TreeWithPath (or an equivalent
path-constrained walker) into Query AST planning / query walkers when a
query is already a linear child/parent chain (or other lowerable path
shape). Candidates:
planning that chooses
TreeWithPathwhen the AST is within write-path axis limits (§G.1), instead of a full-tree walker;hydrate / read paths that share the same path walker as flush ensure (one lowering, two faces);
query-time “ensure along this path” without going through Location’s
:path-transformerconstructor.
Keep Q21 invocation shape ($root + :path) and Navigator step
follow. Do not invent a second path language. Policy for DatabaseStorage
writes stays on Location / :table-action (Q29) even if Query starts
using the walker. Track implementation under §F.12.5.
H. Strategy for Attach
Related: DatabaseStorage-Location-Tree.md
§10.4.2 / Q24 / Q28 / Q31; Qwiratry::Strategy hooks;
Navigator attach-child.
First cut (landed): after EMPTY (or another mold) returns a node for a
missing path step, the walker attaches it during the walk under the
current parent (Q24). TreeWithPath.attach-child delegates to the
tree Navigator (Navigator::Base.attach-child, Format overrides such as
XML element append) — write-side dual of tree-child-results /
follow-child (Q28). Face rules (Q31): Associative → key assign by
step name; Positional → push/append. This is a subset of tree rewriting
(grow a missing child), not a post-transform Location batch of step→node
pairs, and not Tree::Replace.replace-node.
Strategy is not required for this cut. EMPTY and Strategy do not
decide §10 policy (:table-action / DestinationMode stay on Location /
walker mechanics — Q29). Ordinary document walkers may still run
Strategy hooks (before / on-match / after); path-walk attach does not
consult them today.
Why deferred: path attach is a narrow, ordered mutation at a known
parent+step. Full Strategy / TreeRewrite targets matching and replacing
existing nodes (and traversal control via ControlSignal). Wiring
Strategy into attach early risks conflating “create missing child” with
“rewrite matched subtree,” and could let Strategy override ensure/use policy
that Location must own.
Later (this section): consider whether Strategy should participate in path-walk attach, for example:
optional Strategy hooks around attach (approve / skip / substitute the mold result) without moving policy out of Location;
unifying grow-missing-child with a controlled rewrite face so document Transformers and path ensure share one attach story;
Format-specific attach behaviour that is awkward on Navigator alone but fits a Strategy override.
Any design must keep Q29 intact: Strategy must not choose use vs
ensure vs DestinationMode. Track implementation under §F.12.6.
I. HTML Format / Navigation / Location
Status: optional later — after JSON / YAML / XML Tree Location (§F.7.1– §F.7.12). Track checklist under §F.7.13 (and Format discovery under §F.8.2).
Spec home for HTML: this section. It carries forward the HTML-relevant
parts of the former Tree Location plan so that work can proceed without
that document. Core Location::Tree (projection stages, merge-on-flush,
path ensure, sync API) is already shipped; HTML only adds Format +
Navigator + a thin Location::HTML subclass with defaults.
I.1. Goal
Ship an HTML tree Format and open HTML documents as live TaOP tables via the existing Tree Location pipeline:
Qwiratry::Format::HTML(sibling dist) — Parse / Render between HTML text and a Mu tree Navigators understand.Qwiratry::Tree::Navigator::HTML— element / attribute child lookup andattach-child(same faces as XML: Associative attributes, Positional element children).Qwiratry::DatabaseStorage::Location::HTML— thin subclass of coreLocation::Tree; default:format/ extension; advertise sensible<table>/ body-row / cell projection defaults souseTable(name => …)can open without every selector when the document matches those conventions.
Do not fork the Tree projection algorithm or invent an HTML-only path language (no CSS/XPath dialect beside what shared Query / Navigators already provide).
I.2. Layering (unchanged)
| Concern | Owner |
|---|---|
| Blob I/O | Qwiratry::Location::* |
| Whole-document encode/decode | Qwiratry::Format::HTML |
| Tree navigation / attach | Qwiratry::Tree::Navigator::HTML |
| Table projection + live rows | Qwiratry::DatabaseStorage::Location::HTML → core Location::Tree |
Formats must not grow “table mode.” Projection runs on the already-parsed Mu tree.
Class shape:
# core raku-Qwiratry
Qwiratry::DatabaseStorage::Location::Tree
# sibling Format-HTML dist (future)
…::Format::HTML
…::Tree::Navigator::HTML
…::DatabaseStorage::Location::HTML # format => 'HTML'; §I.4 defaultsI.3. Why HTML needs multi-stage projection
Even after selecting a table site, row data is often not “the site’s immediate children as Associatives.” HTML is the clearest case:
The table site may be a
<table>node.Data rows may live under
<tbody>(or be mixed with<thead>/<tfoot>).Header vs data
<tr>distinctions belong in the row-selector (e.g. onlytbody/tr, or only rows that match a navigation predicate the shared query stack already supports), and/or in ordinary query after load — not in a fourth Location-private filter stage.Cells must map to Relation fields (ordered
<td>/<th>→ columns, usually via:header-selector+ positional cells).
Chaining “open the HTML table, then pick a data row” is projection then ordinary table/query — not a Location-private query string.
I.4. Projection defaults
Core Location::Tree supplies no universal stage defaults. Resolution
per stage remains: caller argument → Format Location::* default →
error.
Location::HTML should advertise defaults so omit-args open can work for
common HTML tables:
| Stage | Proposed HTML default (illustrative) |
|---|---|
:table-selector | Locate a <table> site (document / body search; if multiple tables, require :table-name-selector or caller override — multi-site still errors when ambiguous). |
:table-name-selector | Optional; e.g. match id / name / caption text when several <table> nodes exist. |
:row-selector | From the table root: tbody/tr (or equivalent Navigator steps), skipping <thead> / <tfoot> by not selecting them. Prefer encoding “data rows only” here rather than loading header rows and filtering later. |
:header-selector | From the table root: header cells (typically thead/tr/th, or first header row) → key array. HTML tables are usually array-like. |
:field-projection | From each data <tr>: ordered cell values (td / remaining th) as a value array, spliced with the header key array into column→value hashes. |
Column-naming mode: HTML defaults should use array-like +
:header-selector (header array → keys; each row array → values). Hash
mode (no header-selector; :field-projection returns hashes) remains
available via caller override for unusual documents.
Callers may always override any default. If a document does not match the
defaults (no <table>, no tbody, irregular cells), omit-args must
error or the caller must pass selectors — do not silently guess.
Illustrative open:
# Format defaults: open a conventional HTML table by name alone
my $h = $html-db.useTable(name => 'report', action => 'use');
# Override row collection when data rows are not under tbody
my $h2 = $html-db.useTable(
name => 'report',
action => 'use',
row-selector => -> $table { … }, # Block / Callable preferred
);useTable projection argument names (already on Tree): :table-selector,
:table-name-selector, :row-selector, :header-selector,
:field-projection, :transformer (flush/merge molds),
:path-transformer (EMPTY path creation), optional :table-write-path.
I.5. Non-goals (HTML-specific)
Guessing header vs body conventions when the caller did not encode that in the row-selector (or handle it with ordinary query after load).
A separate Location row-filter stage — redundant with row-selector and with outside query operators.
Putting table-shape assumptions in
Format::HTMLitself (Parse/Render stay general; defaults live onLocation::HTML).Shipping HTML as part of the JSON / YAML / XML first cut (already done without HTML).
I.6. Pipeline sketch (HTML)
HTML document root
--(table-selector)-----> <table> root
--(header-selector)----> header key array # thead / th cells
--(row-selector)-------> data <tr> nodes # e.g. tbody/tr
--(field-projection)---> cell value arrays # then splice header×row
-----------------------> @!rows + fieldsI.7. Merge-on-flush
Tree-backed HTML tables share a document with non-table structure
(<thead>, captions, sibling elements, scripts, …). Flush must:
Keep / rehydrate the full document tree (Walkable sync:
.flush/.rehydrate/.check-fresh).Re-navigate with the same table-site and row-collection selectors.
Reconcile
@rowsinto the selected row nodes via:transformermolds (inverse of hydrate field projection). Nodes outside the row-selector result (e.g.<thead>rows never selected) stay untouched unless DestinationMode overwrite applies to that collection site alone.Format Render the whole document; Location Destination write.
Empty data-row flush writes an empty collection at the row-collection
site (does not delete <thead> / caption / unselected siblings by
default).
I.8. Path auto-creation (EMPTY)
When table action / DestinationMode allows creating missing in-document
nodes, HTML follows the same path-walk as other Tree Formats
(:path-transformer, mold name EMPTY, TreeWithPath + Navigator
attach-child).
Location::HTML may advertise a default path-creation Transformer whose
EMPTY molds build sensible empty HTML nodes by role / step name (e.g.
<table>, <tbody>, <tr>, <td> / <th>), using $*CONTEXT roles
(ancestor / table-site / row-collection) the same way JSON chooses
{} vs [] and XML names elements. Policy (use / ensure / create) stays
outside EMPTY. Write-path axis limits remain as in §G.1 until expanded.
I.9. Navigator notes
Prefer reusing XML-like element / attribute navigation patterns; specialise only where HTML differs (void elements, case folding of tag names if the parse tree requires it,
tbodyinsertion quirks if the HTML parser normalises structure).Associative face → attributes; Positional face → element children (
attach-childappend). No third attach API.Named helpers for common selectors (
tbody/tr, header row) may ship in the Format dist — built from shared navigation primitives, not a private DSL.
I.10. Implementation checklist
I.10.1. Sibling dist
raku-Qwiratry-Format-HTML(or agreed name):Format::HTMLParse / Render; META6; basic round-trip tests.I.10.2.
Tree::Navigator::HTML(child lookup +attach-child); unit coverage for element / attribute faces.I.10.3. Thin
DatabaseStorage::Location::HTMLsubclassing coreLocation::Tree; default:format/ extension.I.10.4. Implement §I.4 projection defaults + document them in the Format dist (caller → default → error).
I.10.5. Default
:path-transformer/EMPTYfor HTML empty nodes (§I.8); Location tests for ensure / create along a table path.I.10.6. Default or documented
:transformermolds for flush/merge of header + body cells; prove<thead>survives body-only flush.I.10.7. Conformance via
Qwiratry::Testwhere applicable; explicit.flushafter Tree mutations (no auto-flush).I.10.8. Changelog + Extending notes; wire discovery through
Qwiratry::Format/ Location backends.
I.11. Decisions that apply (no separate HTML Q list yet)
Taken from the Tree Location cut; restate here so this section stands alone:
Projection selectors are Blocks / Callables (preferred over
query-astfor Tree stages); optional:table-name-selectorwhen multiple table sites hit; still error if ambiguous / empty.No separate
Projectionclass or Locationrow-filterstage.Column naming: optional
:header-selectorbefore:row-selector; both return arrays spliced into hashes — the usual HTML path.Flush/merge inverse uses a caller/Format
:transformer(molds); path auto-creation uses separate:path-transformer(EMPTY).Core Tree has no universal defaults; only Format subclasses may.
Sync API:
.flush/.rehydrate/.check-freshwith:throw-on-conflict(default True); mutations mark dirty and do not auto-flush.
Open HTML-only product choices (resolve when implementing §I.10):
Exact default for “which
<table>” when several exist and no name selector is passed (error vs first table vsidmatchinguseTablename).Whether missing
<tbody>still selects direct<tr>children of<table>(parser-normalised documents often inserttbody).Header cell source when both
<thead>and a first body row of<th>exist.Which HTML parser / DOM Mu type the Format commits to (must be stable for Navigator + tests).