Skip to content

Commit 5f1607e

Browse files
committed
docs: update spec to use values to store introducer
Signed-off-by: Snehil Shah <snehilshah.989@gmail.com>
1 parent 0af0b39 commit 5f1607e

1 file changed

Lines changed: 10 additions & 18 deletions

File tree

docs/superpowers/specs/2026-08-16-transaction-statement-node-design.md

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,6 @@ function because they shared a *lack* of parsing.
6565
NODE_TRANSACTION_STMT,
6666
```
6767

68-
Flags for `NODE_TRANSACTION_STMT`:
69-
70-
```cpp
71-
static constexpr uint16_t FLAG_TXN_BEGIN = 0;
72-
static constexpr uint16_t FLAG_TXN_BEGIN_TRANSACTION = 1; // PostgreSQL only
73-
static constexpr uint16_t FLAG_TXN_START_TRANSACTION = 2;
74-
```
75-
7668
Flags for a `NODE_TRANSACTION_STMT` mode child:
7769

7870
```cpp
@@ -138,31 +130,31 @@ A construct the dialect does not define terminates the loop and falls to `scan_t
138130

139131
```
140132
BEGIN READ ONLY (PostgreSQL)
141-
└── NODE_TRANSACTION_STMT flags = FLAG_TXN_BEGIN
133+
└── NODE_TRANSACTION_STMT "BEGIN"
142134
└── NODE_IDENTIFIER "READ ONLY"
143135
144136
BEGIN ISOLATION LEVEL READ COMMITTED, READ ONLY (PostgreSQL)
145-
└── NODE_TRANSACTION_STMT flags = FLAG_TXN_BEGIN
137+
└── NODE_TRANSACTION_STMT "BEGIN"
146138
├── NODE_IDENTIFIER "READ COMMITTED" flags = FLAG_TXN_MODE_ISOLATION
147139
└── NODE_IDENTIFIER "READ ONLY"
148140
149141
BEGIN ISOLATION LEVEL SERIALIZABLE, READ ONLY, DEFERRABLE (PostgreSQL)
150-
└── NODE_TRANSACTION_STMT flags = FLAG_TXN_BEGIN
142+
└── NODE_TRANSACTION_STMT "BEGIN"
151143
├── NODE_IDENTIFIER "SERIALIZABLE" flags = FLAG_TXN_MODE_ISOLATION
152144
├── NODE_IDENTIFIER "READ ONLY"
153145
└── NODE_IDENTIFIER "DEFERRABLE"
154146
155147
START TRANSACTION WITH CONSISTENT SNAPSHOT, READ ONLY (MySQL)
156-
└── NODE_TRANSACTION_STMT flags = FLAG_TXN_START_TRANSACTION
148+
└── NODE_TRANSACTION_STMT "START TRANSACTION"
157149
├── NODE_IDENTIFIER "WITH CONSISTENT SNAPSHOT"
158150
└── NODE_IDENTIFIER "READ ONLY"
159151
160152
START TRANSACTION READ ONLY (both)
161-
└── NODE_TRANSACTION_STMT flags = FLAG_TXN_START_TRANSACTION
153+
└── NODE_TRANSACTION_STMT "START TRANSACTION"
162154
└── NODE_IDENTIFIER "READ ONLY"
163155
164156
START TRANSACTION (both)
165-
└── NODE_TRANSACTION_STMT flags = FLAG_TXN_START_TRANSACTION
157+
└── NODE_TRANSACTION_STMT "START TRANSACTION"
166158
```
167159

168160
Mode values are stored under their **canonical spelling**, not as a span of the input, so a consumer can compare a child against `"READ ONLY"` without first normalizing case or internal whitespace. `select_parser.h` already does this for `NOWAIT` and `SKIP LOCKED`. Spanning the source instead would make `READ ONLY` a different string from `READ ONLY`, which every consumer would then have to work around.
@@ -171,13 +163,13 @@ Mode values are stored under their **canonical spelling**, not as a span of the
171163

172164
### Recording the introducing keywords
173165

174-
`stmt_type` distinguishes `BEGIN` from `START TRANSACTION`, but not `BEGIN` from `BEGIN TRANSACTION`. The emitter needs that to round-trip, so it is recorded in the node flags rather than the node value — keywords are then emitted canonically like every other keyword in the emitter, instead of copying the input's casing. This matters for digests: `begin read only` and `BEGIN READ ONLY` must normalize to the same digest text, as they did when the statement had no AST and fell through to the token-level digest path, which uppercases keyword tokens.
166+
`stmt_type` distinguishes `BEGIN` from `START TRANSACTION`, but not `BEGIN` from `BEGIN TRANSACTION`. The emitter needs that to round-trip, so the introducing keywords are stored as the node's **value**, under their canonical spelling — following `NODE_SET_OPERATION`, which likewise holds its mutually exclusive operator (`UNION` / `INTERSECT` / `EXCEPT`) in the value and reserves `flags` for the independent `ALL` modifier. A canonical literal rather than a source span keeps casing out of the digest: `begin read only` and `BEGIN READ ONLY` must normalize to the same digest text, as they did when the statement had no AST and fell through to the token-level digest path, which uppercases keyword tokens.
175167

176168
---
177169

178170
## Emitter Extensions
179171

180-
One new method, `emit_transaction_stmt()`, plus its dispatch case. It writes the introducing keywords from `flags`, then each mode. A single mode is emitted without a comma so the common forms round-trip exactly; multiple modes are comma-separated, which PostgreSQL accepts and MySQL requires. No case folding happens here as the parser already stored each mode canonically.
172+
One new method, `emit_transaction_stmt()`, plus its dispatch case. It writes the node's value — the introducing keywords then each mode. A single mode is emitted without a comma so the common forms round-trip exactly; multiple modes are comma-separated, which PostgreSQL accepts and MySQL requires. No case folding happens here as the parser already stored each mode canonically.
181173

182174
| input | emitted | dialect |
183175
|---|---|---|
@@ -201,9 +193,9 @@ An input that starts a mode without completing it — `BEGIN READ`, `BEGIN ISOLA
201193

202194
## Implementation
203195

204-
1. `NODE_TRANSACTION_STMT` and the `FLAG_TXN_*` constants in `common.h`.
196+
1. `NODE_TRANSACTION_STMT` and `FLAG_TXN_MODE_ISOLATION` in `common.h`.
205197
2. `parse_transaction()` in `parser.cpp`, declared in the Tier-1 block of `parser.h`; `TK_BEGIN` / `TK_START` routed to it from `classify_and_dispatch()` and removed from `extract_transaction()`.
206-
3. `parse_transaction_modes(ParseResult&, uint16_t)` as the mode loop, following the Tier-1 conventions: `ERROR` if the node cannot be allocated, `PARTIAL` on an incomplete mode. Each mode is stored under its canonical spelling, as `select_parser.h` already does for `NOWAIT` and `SKIP LOCKED`, so a consumer never has to normalize before comparing.
198+
3. `parse_transaction_modes(ParseResult&, StringRef)` as the mode loop, following the Tier-1 conventions: `ERROR` if the node cannot be allocated, `PARTIAL` on an incomplete mode. Each mode is stored under its canonical spelling, as `select_parser.h` already does for `NOWAIT` and `SKIP LOCKED`, so a consumer never has to normalize before comparing.
207199
4. `emit_transaction_stmt()` and its dispatch case in `emitter.h`.
208200
5. Tests in `tests/test_misc_stmts.cpp`, beside the other Tier-1 statements that live in `parser.cpp`, plus digest coverage in `tests/test_digest.cpp`.
209201

0 commit comments

Comments
 (0)