Qwiratry — SQL-Features-Spitballing
Qwiratry language redesign for SQL-shaped features
Plan/Spec for version 0.10.2 (Language Redesign): how the Qwiratry language (Query AST, query slang, and operator surface) grows to cover the gaps listed in SQL-bits-missing.md — CrUD, Grouping, and Other — without turning Qwiratry into SQL.
Companion research: SQL-AST-Qwiratry-Mapping.md, SQL-AST-Comparison.md.Backend / Location / TaOP work stays out of scope here (see Qwiratry-TaOP-Basics.md and Qwiratry-TaOP-Plan.md for row sinks and Relation faces).
Status: Writing.Scope: Language only — operators, slang forms, AST nodes, pipeline semantics, and how those map conceptually to SQL.Non-scope: Postgres/Memory Location APIs, SQL string encoding, Walker pushdown strategy internals (except where the language must expose a hook).
1. Goals
Make Create / Update / Delete first-class in the query language, tying mutation into Destination rather than inventing a second write vocabulary.
Add real grouping (group-by + aggregates), window, and unique/distinct features/operators — not approximations via
ReduceOperator.Cover transactions and paging (
LIMIT/OFFSET) as language constructs that compose with pipelines.Keep Qwiratry’s design principles: declarative Query AST, Unicode/relational operators, chained pipelines, introspectable nodes, domain flexibility (tables and other models where the op makes sense).
2. Non-goals
Emitting or parsing SQL text in core.
A full SQL dialect grammar or keyword-for-keyword SQL slang.
Replacing TaOP Relation mutation APIs; pipelines and Relation remain complementary.
Replacing
ReduceOperator(⌿) — it stays a fold; grouping/distinct are different nodes.Row locking (
FOR UPDATE, …) in the first cut (note only; see §8).Backend capability matrices beyond “language must not assume every Location can do every mode”.
3. Design principles
Pipeline position is semantics.
σbefore grouping ≈WHERE;σafter grouping ≈HAVING. Prefer that over a dedicatedHAVINGnode unless pushdown later needs an explicit tag.One Destination glyph, many intents. Extend
DestinationOperatorwith write modes/adverbs (insert/update/delete/schema), informed by the survey in §4.
4. Survey: Create / Update / Delete across languages
How existing languages and APIs express mutation intents. Cells are representative idioms, not exhaustive catalogues. — means that family does not really own that column (or treats it as out of scope).
Rows mix command families (SQL sublanguages), protocol surfaces (HTTP, GraphQL), shell/file idioms, host-language containers, and a few graph / document peers, plus algebraic analogies (sets, logic gates, joins). Use this when choosing Destination adverb / mode names and when deciding whether schema CrUD and record CrUD share one operator.
Column meanings
Create — Add a new thing that did not exist (row, object, file, privilege, graph, …). Fails or no-ops if identity already exists, depending on the language.
Update — Change an existing thing in place (partial or full), identified by key / URL / path / node. Does not create if missing (unless the idiom is really an upsert — then prefer the Upsert column).
Delete — Remove an existing thing (or mark it gone). Parent container may remain.
Upsert — Create-or-update by identity in one operation (
MERGE,ON CONFLICT,REPLACE INTO, HTTP conditional put-with-create, …). Distinct from plain Create and plain Update.Truncate parent + Create — Empty or replace the parent container, then populate it afresh (SQL
TRUNCATE+INSERT; overwrite a whole file; replace an entire collection/document;PUTa whole resource body). The parent identity survives; the children are wiped and re-created. Not the same as Delete of the parent, and not the same as per-row Update.
| Language / surface | Create | Update | Delete | Upsert | Truncate parent + Create |
|---|---|---|---|---|---|
| SQL DDL (relations / schema objects) | CREATE TABLE / VIEW / INDEX / … | ALTER TABLE / ALTER VIEW / … | DROP TABLE / VIEW / … | — (no create-or-alter-object in one standard verb) | DROP + CREATE; or CREATE OR REPLACE VIEW (views) |
| SQL DDL (columns) | ALTER … ADD COLUMN | ALTER … ALTER/MODIFY COLUMN / RENAME COLUMN | ALTER … DROP COLUMN | — | drop all columns + re-add (rare; usually DROP TABLE + CREATE) |
| SQL DML (records) | INSERT (+ INSERT … SELECT) | UPDATE | DELETE | MERGE; INSERT … ON CONFLICT / REPLACE INTO (dialect) | TRUNCATE then INSERT; DELETE all then INSERT |
| SQL DQL | — (read-only; SELECT / WITH) | — | — | — | — |
| SQL DCL | GRANT | — | REVOKE | — (GRANT often additive, not upsert) | revoke all + re-grant (manual pattern) |
| SQL TCL | — (wraps DML; does not create data) | — | — (ROLLBACK undoes uncommitted work) | — | — |
| HTTP / REST (resources) | POST (create); sometimes PUT to new URL | PATCH (partial); PUT when resource must exist | DELETE | PUT to URL (create-or-replace by URI) | PUT full body (replace representation); delete-children + POST bulk |
| GraphQL (Mutation root) | createX / addX / insertX fields | updateX / setX fields | deleteX / removeX fields | schema-defined upsertX / saveX (convention only) | custom replaceX / setCollection mutation (convention only) |
| GraphQL (Hasura-style CRUD) | insert_* / insert_*_one | update_* / update_*_by_pk | delete_* / delete_*_by_pk | insert_* + on_conflict update columns | delete-all (or truncate via SQL) + insert_*; no first-class “replace table” |
| GraphQL (Relay / input objects) | …(input: Create…Input!) | …(input: Update…Input!) | …(input: Delete…Input!) | …(input: Upsert…Input!) if schema defines it | custom replace/set mutation inputs |
| SPARQL Update (graph data) | INSERT DATA / INSERT … WHERE; LOAD; CREATE GRAPH | DELETE { } INSERT { } WHERE { } | DELETE DATA / DELETE … WHERE; DROP GRAPH | DELETE + INSERT in one update (pattern upsert) | CLEAR GRAPH / DROP+CREATE then INSERT; LOAD into cleared graph |
| Cypher (Neo4j) | CREATE | SET / REMOVE property | DELETE / DETACH DELETE | MERGE (+ ON CREATE / ON MATCH) | match parent, delete children, CREATE anew; or recreate subgraph |
| MongoDB (shell / driver) | insertOne / insertMany | updateOne / updateMany; $set / … | deleteOne / deleteMany | update* / replaceOne with upsert: true; bulkWrite upserts | deleteMany({}) + insertMany; drop collection + recreate; replace root doc |
| XQuery Update Facility | insert node … into/before/after | replace node / replace value of; rename | delete node | — (compose delete+insert / replace) | delete all children + insert; replace node parent content |
| bash (files via redirect) | > create; cat … > file | sed -i / sponge edit-in-place | rm | — (no identity merge; overwrite is replace) | > / cat … > file (truncate file + write); truncate then write |
bash (tee) | … | tee file (write/overwrite) | … | tee -a file (append; not true field update) | — (pair with rm) | — | … | tee file (truncate-via-open + write stream) |
Raku (files / IO) | .IO.spurt; open :w / :create | rewrite via temp+move; partial edit by hand | .IO.unlink | — | .IO.spurt($content) / open :w (truncate + write); :truncate |
Raku (Array / List building) | .push / .append | @a[$i] = …; .splice | .pop / .shift / .splice / .DELETE-POS | — (assign-by-index is update, not upsert-by-key) | @a = @new / .STORE (replace all elements) |
Raku (Hash / Map) | %h{$k} = $v when key absent | %h{$k} = $v when key present | %h{$k}:delete / .DELETE-KEY | %h{$k} = $v (assign is upsert-by-key) | %h = %new / .STORE (replace all pairs) |
Raku (Set / BagHash) | ∪ / .set | — | ∖ / (-) / .unset | .set (add-if-absent; no payload update) | assign new set / clear + add all |
Raku (Bag) | assignment / .STORE | mutation methods | drop last reference (GC) | — | re-bless / replace object identity (rare) |
| Raku (assignment / meta-assignment) | //= / (double-vertical-bars blocked by Markdown)= (write only if absent/undefined — SQL INSERT-shaped); also append= / prepend= / ~= (always add new content) | .= (in-place method); otherwise no single “field update” metaop | -= / ∖= (remove / subtract) | += / ∪= (merge into existing; may autovivify — SQL UPSERT-shaped) | bare = (replace container contents / .STORE) |
Qwiratry today (Destination ⮷) | single write/sink (… ⮷ $loc); blob replace semantics | — | — | — | blob ⮷ often is truncate-parent+create (overwrite location) |
| Current Proposal (Destination operators, including composites) | ⮷// (Destination before // — insert-if-absent, cf. //=) | ⮷+ (Destination before + — Plus / Update) | ⮷- (Destination before -, cf. -=) | ⮷⊕ (Destination before Circled Plus ⊕ — Upsert) | bare ⮷ (replace/overwrite location — Truncate parent + Create) |
| git (working tree / index) | git add new paths; git commit | edit + git add; commit --amend | git rm; discard via restore | — | replace file contents + add; orphan branch / reset patterns (heavy-handed) |
| Makefile / build artefacts | recipe creates target | recipe rebuilds target | clean / rm | — | recipe always rebuilds target file from scratch |
| CSSOM / DOM (browser) | createElement / appendChild; insertRule | set attrs / textContent / replaceChild | remove() / deleteRule | — | replaceChildren(…) / set innerHTML; replace stylesheet content |
| LDAP | add | modify / modifyDN | delete | — (no standard upsert; search then add/modify) | delete subtree children + re-add; replace entry attrs via modify |
| IMAP (mailboxes / messages) | CREATE mailbox; APPEND message | STORE flags; RENAME | DELETE mailbox; EXPUNGE | — | empty mailbox (EXPUNGE all) + APPEND; recreate mailbox |
| Set theory (existing ⊕ new) | ∪ union (add members from new) | — (no payload; approximate (A ∖ {x}) ∪ {x′}) | ∖ / − difference (remove members in new); ∁ relative complement | ∪ (idempotent add-by-membership) | replace with new: B, or ∅ ∪ B (discard existing) |
| Z Notation (sets / relations / schemas) | ∪ (set union); add maplets x ↦ y to a relation | relational override ⊕ on a known domain element; Δ-schema with primed after-state | set difference \; domain anti-restriction ⩤ (drop pairs by key) | relational override ⊕ (add-or-replace by domain — classic Z “map update”) | after-state replace S′ = T / S′ = ∅ ∪ T (discard prior binding) |
| Logic gates (existing ⊕ new bits) | OR (set 1s from new) | MUX / conditional assign (new ? new : old); XOR = toggle | AND-NOT / inhibit (old ∧ ¬new) | OR (same as Create for bits) | replace with new (Y); FALSE ∨ Y |
| Joins (Qwiratry / RA: existing ⋈ new) | ⟕ left outer (keep existing, attach new); × cross (new combinations) | ⨝ / ▷=◁ / ≜ (match + enrich / overwrite joined attrs) | antijoin ▷ / ◁ (drop matches or keep non-matches); semijoin ⋉ / ⋊ (keep only matches) | ⟗ full outer (keep identities from both; coalesce) | take new side only: ⟖ then project right (or discard left + use right; not a pure join) |
Notes for Qwiratry design:
Schema vs data: SQL splits DDL (relations/columns) from DML (records). GraphQL usually collapses both into Mutation fields. File/
teelanguages mostly only have blob create/overwrite/append/delete.Append vs update:
tee -a, Raku:a/.spurt(:append), and SQLINSERTare create-shaped; true update needs identity (key / URL / path / node).Upsert and Truncate parent + Create are the two columns that most often collapse into today’s single Destination overwrite — the language redesign should split them from per-row Update/Delete.
Current Proposal: Destination forms are operators, not Raku metaoperators. Bare Destination is overwrite (Truncate parent + Create). The other glyphs are composite Destination operators:
⮷//= Create/Insert;⮷+= Update (Plus);⮷⊕= Upsert (Circled Plus);⮷-= Delete. Related Raku assignment operators (=,//=,+=,-=) are an analogy only.Qwiratry today only covers blob Create / Truncate-parent+Create via
⮷; record and schema CrUD (and true Upsert) are the language gap this plan targets.
5. Candidate Unicode symbols for mutation intents
There are no Unicode characters officially named Create, Update, Upsert, Replace, or Truncate for database work. The table lists the candidates from the survey discussion: usable glyphs whose names or conventional UI meaning are close enough to borrow for Qwiratry Destination modes (if we use glyphs at all rather than adverbs alone).
| Intent | Symbol | Code | Name | Notes |
|---|---|---|---|---|
| Create / insert | ➕ | U+2795 | HEAVY PLUS SIGN | Common “add” UI glyph |
| Create / insert | ⊞ | U+229E | SQUARED PLUS | Math-ish plus; pairs with ⊟ |
| Create / insert | ⊕ | U+2295 | CIRCLED PLUS | Also usable as upsert/merge metaphor |
| Create / insert | ⎀ | U+2380 | INSERTION SYMBOL | Unicode name is literally insertion |
| Create / insert | ∪ | U+222A | UNION | Set-theory Create (union) |
| Create / insert | 🆕 | U+1F195 | SQUARED NEW | Emoji; loud / informal |
| Update / edit | ✎ | U+270E | LOWER RIGHT PENCIL | Pencil = edit convention |
| Update / edit | ✏ | U+270F | PENCIL | Pencil = edit convention |
| Update / edit | 📝 | U+1F4DD | MEMO | Emoji memo |
| Update / edit | ↻ | U+21BB | CLOCKWISE OPEN CIRCLE ARROW | Refresh / rewrite metaphor |
| Update / edit | 🔄 | U+1F504 | ANTICLOCKWISE DOWNWARDS AND UPWARDS OPEN CIRCLE ARROWS | Emoji refresh |
| Delete | 🗑 | U+1F5D1 | WASTEBASKET | Emoji trash |
| Delete | ⌫ | U+232B | ERASE TO THE LEFT | Erase in name; keyboard backspace |
| Delete | ⌦ | U+2326 | ERASE TO THE RIGHT | Erase in name; keyboard delete |
| Delete | ␡ | U+2421 | SYMBOL FOR DELETE | Unicode name is Symbol for Delete |
| Delete | ➖ | U+2796 | HEAVY MINUS SIGN | Pairs with ➕ |
| Delete | ⊟ | U+229F | SQUARED MINUS | Math-ish minus; pairs with ⊞ |
| Delete | ✖ | U+2716 | HEAVY MULTIPLICATION X | Heavy X / cancel |
| Upsert / merge | ∪ | U+222A | UNION | Idempotent add-by-membership |
| Upsert / merge | ⊎ | U+228E | MULTISET UNION | Multiset union; “add with multiplicity” feel |
| Upsert / merge | ⊕ | U+2295 | CIRCLED PLUS | Circled plus / XOR-adjacent family |
| Upsert / merge | ⇄ | U+21C4 | RIGHTWARDS ARROW OVER LEFTWARDS ARROW | Exchange / merge-in-both-directions metaphor |
| Upsert / merge | ≔ | U+2254 | COLON EQUALS | Assignment; also fits Replace |
| Replace | ≔ | U+2254 | COLON EQUALS | “becomes” / assign-new-value |
| Replace | ⇄ | U+21C4 | RIGHTWARDS ARROW OVER LEFTWARDS ARROW | Swap metaphor |
| Replace | ↔ | U+2194 | LEFT RIGHT ARROW | Left-right exchange |
| Truncate / empty | ∅ | U+2205 | EMPTY SET | Empty the parent; keep container identity |
| Truncate / empty | ⎚ | U+239A | CLEAR SCREEN SYMBOL | Clear screen; rare in fonts |
| Truncate / empty | 🗋 | U+1F5CB | EMPTY DOCUMENT | Empty document emoji |
A compact math-ish palette (if choosing glyphs): ⊞ Create, ✎ or ↻ Update, ⊟ Delete, ⊕/⊎ Upsert, ≔ Replace, ∅ Truncate.
6. Table of Proposed Destination Operators
Expanded form of the Current Proposal survey row. These are operators, not Raku metaoperators. Bare Destination is the overwrite operator. The other rows are composite Destination operators (Destination combined with another glyph). Related Raku assignment operators are an analogy only.
| Sym | Code | Unicode Name | SQL DML Operation | Qwiratry Name | Related Raku Assignment Operator | Notes |
|---|---|---|---|---|---|---|
⮷ | U+2BB7 | RIBBON ARROW RIGHT DOWN | TRUNCATE + INSERT (replace all rows / contents) | Overwrite Destination | = | Truncate parent + Create — replace/overwrite location (today’s default Destination semantics) |
⮷// | U+2BB7 + U+002F U+002F | RIBBON ARROW RIGHT DOWN + SOLIDUS + SOLIDUS | INSERT | Insert Destination | //= | insert-if-absent |
⮷+ | U+2BB7 + U+002B | RIBBON ARROW RIGHT DOWN + PLUS SIGN | UPDATE | Update Destination | += | |
⮷- | U+2BB7 + U+002D | RIBBON ARROW RIGHT DOWN + HYPHEN-MINUS | DELETE | Delete from Destination | -= | |
⮷⊕ | U+2BB7 + U+2295 | RIBBON ARROW RIGHT DOWN + CIRCLED PLUS | UPSERT / MERGE | Upsert/Append Destination |