Skip to main content

squonk_ast/ast/
stored_program.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 Moderately AI Inc.
3
4//! MySQL stored-program (SQL/PSM) compound-statement AST nodes.
5//!
6//! The body sub-language of a routine/trigger/event: a `[label:] BEGIN … END
7//! [label]` compound block carrying a strict [`Declaration`] prefix (variables,
8//! conditions, cursors, handlers) then a statement list, plus the flow-control
9//! statements (`IF`/`CASE`/`LOOP`/`WHILE`/`REPEAT`/`LEAVE`/`ITERATE`/`RETURN`) and
10//! the cursor operations (`OPEN`/`FETCH`/`CLOSE`). These are body-context-only
11//! [`Statement`] variants: the parser reaches them through the separate
12//! `parse_body_statement` dispatcher, never the top-level one (a bare top-level
13//! `BEGIN … END` is a transaction, not a compound block).
14//!
15//! Each nesting node's payload is boxed into its [`Statement`] variant (the
16//! `CreateTrigger` precedent) so the enum stays within its 24-byte size budget; the
17//! bodies are `ThinVec<Statement<X>>`, reusing the existing statement nodes exactly
18//! as the trigger body does.
19
20use super::{DataType, Expr, Extension, Ident, Literal, NoExt, Query, Statement};
21use crate::vocab::Meta;
22use thin_vec::ThinVec;
23
24/// A `[<label>:] BEGIN [<declarations>] [<statements>] END [<label>]` compound block.
25///
26/// The declaration prefix is parse-time strict-ordered ({variables, conditions} →
27/// cursors → handlers); the [`body`](Self::body) is the trailing statement list, in
28/// source order, each element a full [`Statement`]. Both the opening `label:` and the
29/// closing `END <label>` are optional and independent surface tags — a block may carry
30/// either, both, or neither; when both are present they must match (a structural check
31/// the parser enforces).
32#[derive(Clone, Debug, PartialEq, Eq, Hash)]
33#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
34#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
35pub struct CompoundStatement<X: Extension = NoExt> {
36    /// The opening block label (`<label>:` before `BEGIN`); `None` when unlabelled.
37    pub label: Option<Ident>,
38    /// The `DECLARE` prefix, in source order (always ahead of [`body`](Self::body)).
39    pub declarations: ThinVec<Declaration<X>>,
40    /// The block's statement list, in source order.
41    pub body: ThinVec<Statement<X>>,
42    /// The closing block label (`END <label>`); `None` when the close is bare `END`.
43    pub end_label: Option<Ident>,
44    /// Source location and node identity.
45    pub meta: Meta,
46}
47
48/// One `DECLARE …` item in a [`CompoundStatement`] prefix.
49///
50/// The four forms are parsed uniformly into this prefix; their *ordering* is enforced
51/// post-hoc by the parser's declaration 4-counter ({[`Variable`](Self::Variable),
52/// [`Condition`](Self::Condition)} → [`Cursor`](Self::Cursor) →
53/// [`Handler`](Self::Handler)), mirroring the server's own mechanism rather than
54/// encoding the order as grammar productions.
55#[derive(Clone, Debug, PartialEq, Eq, Hash)]
56#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
57#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
58pub enum Declaration<X: Extension = NoExt> {
59    /// `DECLARE <name> [, <name> …] <type> [DEFAULT <expr>]` — one or more local
60    /// variables sharing a type and optional default.
61    Variable {
62        /// The declared variable names, in source order (always at least one).
63        names: ThinVec<Ident>,
64        /// The shared declared type.
65        data_type: DataType<X>,
66        /// The optional `DEFAULT <expr>` initializer.
67        default: Option<Expr<X>>,
68        /// Source location and node identity.
69        meta: Meta,
70    },
71    /// `DECLARE <name> CONDITION FOR <condition-value>` — a named condition alias.
72    Condition {
73        /// The condition name.
74        name: Ident,
75        /// The `SQLSTATE '…'` or MySQL error-code value the name aliases.
76        value: ConditionValue,
77        /// Source location and node identity.
78        meta: Meta,
79    },
80    /// `DECLARE <name> CURSOR FOR <select>` — a cursor over a query.
81    Cursor {
82        /// The cursor name.
83        name: Ident,
84        /// The `SELECT` the cursor iterates; boxed to keep the variant small.
85        query: Box<Query<X>>,
86        /// Source location and node identity.
87        meta: Meta,
88    },
89    /// `DECLARE {CONTINUE | EXIT | UNDO} HANDLER FOR <condition> [, …] <statement>` — a
90    /// condition handler whose body runs when any listed condition is raised.
91    Handler {
92        /// The handler action (`CONTINUE`/`EXIT`/`UNDO`).
93        action: HandlerAction,
94        /// The condition values the handler catches, in source order (at least one).
95        conditions: ThinVec<HandlerCondition>,
96        /// The handler body — a single body statement (often a compound block); boxed
97        /// to keep the variant small.
98        body: Box<Statement<X>>,
99        /// Source location and node identity.
100        meta: Meta,
101    },
102}
103
104/// The action of a `DECLARE … HANDLER` — what happens after the handler body runs.
105///
106/// A tag (no `meta`): the keyword's span is subsumed by the enclosing
107/// [`Declaration::Handler`], exactly as [`super::TriggerTiming`] rides its parent's span.
108#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
109#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
110#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
111pub enum HandlerAction {
112    /// `CONTINUE` — resume execution after the statement that raised the condition.
113    Continue,
114    /// `EXIT` — leave the compound block the handler is declared in.
115    Exit,
116    /// `UNDO` — roll back and leave (parsed for completeness; the server restricts it).
117    Undo,
118}
119
120/// A condition value across the shared condition family: a `SQLSTATE` string, a MySQL
121/// error code, or a declared condition name.
122///
123/// The variant SET each parser produces is the grammar's subset for its site, not the whole
124/// enum: `DECLARE … CONDITION FOR` produces [`SqlState`](Self::SqlState) or
125/// [`ErrorCode`](Self::ErrorCode) (never a name — that form is a syntax error there), while
126/// `SIGNAL`/`RESIGNAL` produce [`SqlState`](Self::SqlState) or
127/// [`ConditionName`](Self::ConditionName) (a bare error code is a *syntax* error for
128/// `SIGNAL`, engine-measured `1064`). The type is the union so the two siblings share one
129/// vocabulary rather than minting parallel `SqlState` shapes.
130#[derive(Clone, Debug, PartialEq, Eq, Hash)]
131#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
132#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
133pub enum ConditionValue {
134    /// `SQLSTATE [VALUE] '<sqlstate>'` — a five-character SQLSTATE string constant.
135    SqlState {
136        /// Whether the optional `VALUE` noise keyword was written (fidelity tag).
137        value_keyword: bool,
138        /// The SQLSTATE string literal.
139        sqlstate: Literal,
140        /// Source location and node identity.
141        meta: Meta,
142    },
143    /// A bare MySQL error-code integer (e.g. `1051`). `DECLARE … CONDITION FOR` only; a
144    /// bare code is a syntax error in `SIGNAL`/`RESIGNAL`.
145    ErrorCode {
146        /// The error-code integer literal.
147        code: Literal,
148        /// Source location and node identity.
149        meta: Meta,
150    },
151    /// A condition name declared earlier in the block (`DECLARE … CONDITION`), signalled by
152    /// `SIGNAL <name>` / `RESIGNAL <name>`. Grammar-valid at top level (the sp-context
153    /// resolution restriction MySQL enforces — `1319` outside a stored program — is a
154    /// semantic check this parser leaves to name resolution, not a syntax reject).
155    ConditionName {
156        /// The referenced condition name.
157        name: Ident,
158        /// Source location and node identity.
159        meta: Meta,
160    },
161}
162
163/// One condition in a `DECLARE … HANDLER FOR <condition> [, …]` list.
164///
165/// A superset of [`ConditionValue`]: a handler additionally catches a condition name
166/// declared earlier in the block and the three general condition classes.
167#[derive(Clone, Debug, PartialEq, Eq, Hash)]
168#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
169#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
170pub enum HandlerCondition {
171    /// `SQLSTATE [VALUE] '<sqlstate>'` — a SQLSTATE string constant.
172    SqlState {
173        /// Whether the optional `VALUE` noise keyword was written (fidelity tag).
174        value_keyword: bool,
175        /// The SQLSTATE string literal.
176        sqlstate: Literal,
177        /// Source location and node identity.
178        meta: Meta,
179    },
180    /// A bare MySQL error-code integer (e.g. `1051`).
181    ErrorCode {
182        /// The error-code integer literal.
183        code: Literal,
184        /// Source location and node identity.
185        meta: Meta,
186    },
187    /// A condition name declared earlier in the block (`DECLARE … CONDITION`).
188    ConditionName {
189        /// The referenced condition name.
190        name: Ident,
191        /// Source location and node identity.
192        meta: Meta,
193    },
194    /// `SQLWARNING` — the class of `SQLSTATE` values beginning `01`.
195    SqlWarning {
196        /// Source location and node identity.
197        meta: Meta,
198    },
199    /// `NOT FOUND` — the class of `SQLSTATE` values beginning `02` (cursor exhaustion).
200    NotFound {
201        /// Source location and node identity.
202        meta: Meta,
203    },
204    /// `SQLEXCEPTION` — every remaining `SQLSTATE` class.
205    SqlException {
206        /// Source location and node identity.
207        meta: Meta,
208    },
209}
210
211/// An `IF <cond> THEN … [ELSEIF <cond> THEN …] … [ELSE …] END IF` statement.
212///
213/// The `IF` and each `ELSEIF` are folded into one ordered [`branches`](Self::branches)
214/// list (the first is the `IF`, the rest the `ELSEIF`s); the optional trailing `ELSE`
215/// body is [`else_body`](Self::else_body).
216#[derive(Clone, Debug, PartialEq, Eq, Hash)]
217#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
218#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
219pub struct IfStatement<X: Extension = NoExt> {
220    /// The `IF`/`ELSEIF` branches, in source order (always at least the `IF`).
221    pub branches: ThinVec<ConditionalBranch<X>>,
222    /// The optional `ELSE` body; `None` when no `ELSE` clause was written.
223    pub else_body: Option<ThinVec<Statement<X>>>,
224    /// Source location and node identity.
225    pub meta: Meta,
226}
227
228/// One `<cond> THEN <statements>` arm of an [`IfStatement`] or the WHEN arm of a
229/// searched [`CaseStatement`].
230#[derive(Clone, Debug, PartialEq, Eq, Hash)]
231#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
232#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
233pub struct ConditionalBranch<X: Extension = NoExt> {
234    /// The branch guard — the `IF`/`ELSEIF`/`WHEN` expression.
235    pub guard: Expr<X>,
236    /// The branch body, in source order.
237    pub body: ThinVec<Statement<X>>,
238    /// Source location and node identity.
239    pub meta: Meta,
240}
241
242/// A `CASE … END CASE` statement, in either the simple or searched form.
243///
244/// [`operand`](Self::operand) distinguishes them: `Some` is the simple `CASE <operand>
245/// WHEN <value> …` (each branch guard is a value compared to the operand); `None` is
246/// the searched `CASE WHEN <condition> …` (each branch guard is a boolean predicate).
247#[derive(Clone, Debug, PartialEq, Eq, Hash)]
248#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
249#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
250pub struct CaseStatement<X: Extension = NoExt> {
251    /// The simple-`CASE` operand; `None` for the searched form. Boxed to keep the
252    /// node small.
253    pub operand: Option<Box<Expr<X>>>,
254    /// The `WHEN … THEN …` branches, in source order (always at least one).
255    pub when_branches: ThinVec<ConditionalBranch<X>>,
256    /// The optional `ELSE` body; `None` when no `ELSE` clause was written.
257    pub else_body: Option<ThinVec<Statement<X>>>,
258    /// Source location and node identity.
259    pub meta: Meta,
260}
261
262/// A `[<label>:] LOOP … END LOOP [<label>]` unconditional loop.
263#[derive(Clone, Debug, PartialEq, Eq, Hash)]
264#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
265#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
266pub struct LoopStatement<X: Extension = NoExt> {
267    /// The opening loop label; `None` when unlabelled.
268    pub label: Option<Ident>,
269    /// The loop body, in source order.
270    pub body: ThinVec<Statement<X>>,
271    /// The closing `END LOOP <label>`; `None` when the close is bare.
272    pub end_label: Option<Ident>,
273    /// Source location and node identity.
274    pub meta: Meta,
275}
276
277/// A `[<label>:] WHILE <cond> DO … END WHILE [<label>]` pre-tested loop.
278#[derive(Clone, Debug, PartialEq, Eq, Hash)]
279#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
280#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
281pub struct WhileStatement<X: Extension = NoExt> {
282    /// The opening loop label; `None` when unlabelled.
283    pub label: Option<Ident>,
284    /// The `WHILE` continuation condition; boxed to keep the node small.
285    pub condition: Box<Expr<X>>,
286    /// The loop body, in source order.
287    pub body: ThinVec<Statement<X>>,
288    /// The closing `END WHILE <label>`; `None` when the close is bare.
289    pub end_label: Option<Ident>,
290    /// Source location and node identity.
291    pub meta: Meta,
292}
293
294/// A `[<label>:] REPEAT … UNTIL <cond> END REPEAT [<label>]` post-tested loop.
295#[derive(Clone, Debug, PartialEq, Eq, Hash)]
296#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
297#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
298pub struct RepeatStatement<X: Extension = NoExt> {
299    /// The opening loop label; `None` when unlabelled.
300    pub label: Option<Ident>,
301    /// The loop body, in source order.
302    pub body: ThinVec<Statement<X>>,
303    /// The `UNTIL` termination condition (tested after each pass); boxed to keep the
304    /// node small.
305    pub condition: Box<Expr<X>>,
306    /// The closing `END REPEAT <label>`; `None` when the close is bare.
307    pub end_label: Option<Ident>,
308    /// Source location and node identity.
309    pub meta: Meta,
310}
311
312/// A `LEAVE <label>` statement — leave the labelled block or loop.
313#[derive(Clone, Debug, PartialEq, Eq, Hash)]
314#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
315#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
316pub struct LeaveStatement {
317    /// The target label.
318    pub label: Ident,
319    /// Source location and node identity.
320    pub meta: Meta,
321}
322
323/// An `ITERATE <label>` statement — restart the labelled loop.
324#[derive(Clone, Debug, PartialEq, Eq, Hash)]
325#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
326#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
327pub struct IterateStatement {
328    /// The target loop label.
329    pub label: Ident,
330    /// Source location and node identity.
331    pub meta: Meta,
332}
333
334/// A `RETURN <expr>` statement — return a value from a stored function.
335///
336/// Function-only in the server (`RETURN` in a procedure is rejected); the node lands
337/// here and the routine family enforces the context.
338#[derive(Clone, Debug, PartialEq, Eq, Hash)]
339#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
340#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
341pub struct ReturnStatement<X: Extension = NoExt> {
342    /// The returned value expression.
343    pub value: Expr<X>,
344    /// Source location and node identity.
345    pub meta: Meta,
346}
347
348/// An `OPEN <cursor>` statement — open a declared cursor.
349#[derive(Clone, Debug, PartialEq, Eq, Hash)]
350#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
351#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
352pub struct OpenCursorStatement {
353    /// The cursor name.
354    pub cursor: Ident,
355    /// Source location and node identity.
356    pub meta: Meta,
357}
358
359/// A `CLOSE <cursor>` statement — close an open cursor.
360#[derive(Clone, Debug, PartialEq, Eq, Hash)]
361#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
362#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
363pub struct CloseCursorStatement {
364    /// The cursor name.
365    pub cursor: Ident,
366    /// Source location and node identity.
367    pub meta: Meta,
368}
369
370/// A `FETCH [[NEXT] FROM] <cursor> INTO <var> [, …]` statement — fetch the next row
371/// into local variables.
372#[derive(Clone, Debug, PartialEq, Eq, Hash)]
373#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
374#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
375pub struct FetchCursorStatement {
376    /// Whether the optional `NEXT` noise keyword was written (fidelity tag; `NEXT`
377    /// implies `FROM`).
378    pub next_keyword: bool,
379    /// Whether the optional `FROM` noise keyword was written (fidelity tag).
380    pub from_keyword: bool,
381    /// The cursor name.
382    pub cursor: Ident,
383    /// The `INTO` target variables, in source order (always at least one).
384    pub targets: ThinVec<Ident>,
385    /// Source location and node identity.
386    pub meta: Meta,
387}
388
389/// A `SIGNAL`/`RESIGNAL` statement: raise (or re-raise) a condition, optionally amending
390/// the diagnostics area with a `SET` item list.
391///
392/// One payload for both keywords (the enclosing [`Statement::Signal`]/[`Statement::Resignal`]
393/// records which): they share the grammar bar one difference the parser enforces — `SIGNAL`
394/// requires a [`condition`](Self::condition), `RESIGNAL` leaves it optional (re-raise the
395/// current condition, so a bare `RESIGNAL` and `RESIGNAL SET …` are both legal).
396#[derive(Clone, Debug, PartialEq, Eq, Hash)]
397#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
398#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
399pub struct SignalStatement<X: Extension = NoExt> {
400    /// The signalled condition — a `SQLSTATE '…'` or a declared condition name. Mandatory
401    /// for `SIGNAL` (parser-enforced), optional for `RESIGNAL`.
402    pub condition: Option<ConditionValue>,
403    /// The `SET <item> = <expr> [, …]` amendments; empty when no `SET` clause was written.
404    pub set_items: ThinVec<SignalItem<X>>,
405    /// Source location and node identity.
406    pub meta: Meta,
407}
408
409/// One `<name> = <expr>` amendment in a `SIGNAL`/`RESIGNAL` `SET` list.
410#[derive(Clone, Debug, PartialEq, Eq, Hash)]
411#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
412#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
413pub struct SignalItem<X: Extension = NoExt> {
414    /// Which diagnostics-area field is being set.
415    pub name: SignalItemName,
416    /// The value expression assigned to the field.
417    pub value: Expr<X>,
418    /// Source location and node identity.
419    pub meta: Meta,
420}
421
422/// The settable condition-information item name in a `SIGNAL`/`RESIGNAL` `SET` list.
423///
424/// A closed keyword set (a tag, no `meta` — the span is the enclosing [`SignalItem`]'s). The
425/// signal-settable subset deliberately EXCLUDES `RETURNED_SQLSTATE` (readable via
426/// `GET DIAGNOSTICS` but not signal-settable), so it is a distinct enum from
427/// [`ConditionInfoItemName`], not a shared one.
428#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
429#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
430#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
431pub enum SignalItemName {
432    /// `CLASS_ORIGIN`.
433    ClassOrigin,
434    /// `SUBCLASS_ORIGIN`.
435    SubclassOrigin,
436    /// `CONSTRAINT_CATALOG`.
437    ConstraintCatalog,
438    /// `CONSTRAINT_SCHEMA`.
439    ConstraintSchema,
440    /// `CONSTRAINT_NAME`.
441    ConstraintName,
442    /// `CATALOG_NAME`.
443    CatalogName,
444    /// `SCHEMA_NAME`.
445    SchemaName,
446    /// `TABLE_NAME`.
447    TableName,
448    /// `COLUMN_NAME`.
449    ColumnName,
450    /// `CURSOR_NAME`.
451    CursorName,
452    /// `MESSAGE_TEXT`.
453    MessageText,
454    /// `MYSQL_ERRNO`.
455    MysqlErrno,
456}
457
458/// A `GET [CURRENT | STACKED] DIAGNOSTICS …` statement — read the diagnostics area into
459/// target variables.
460#[derive(Clone, Debug, PartialEq, Eq, Hash)]
461#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
462#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
463pub struct GetDiagnosticsStatement<X: Extension = NoExt> {
464    /// Which diagnostics area (the `CURRENT`/`STACKED` keyword, or none — implicit current).
465    pub area: DiagnosticsArea,
466    /// The requested information: statement-level items or a single condition's items.
467    pub info: DiagnosticsInfo<X>,
468    /// Source location and node identity.
469    pub meta: Meta,
470}
471
472/// The `which_area` written before `DIAGNOSTICS`. A tag: `None` and `Current` name the same
473/// area and differ only in spelling (a fidelity distinction so both round-trip).
474#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
475#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
476#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
477pub enum DiagnosticsArea {
478    /// No area keyword — the implicit current area.
479    Implicit,
480    /// Explicit `CURRENT`.
481    Current,
482    /// Explicit `STACKED`.
483    Stacked,
484}
485
486/// The information a `GET DIAGNOSTICS` requests: statement-level items, or a single
487/// condition's items keyed by a `CONDITION <number>` selector.
488#[derive(Clone, Debug, PartialEq, Eq, Hash)]
489#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
490#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
491pub enum DiagnosticsInfo<X: Extension = NoExt> {
492    /// `<target> = {NUMBER | ROW_COUNT} [, …]` — the statement-level diagnostics items.
493    Statement {
494        /// The requested statement items, in source order (always at least one).
495        items: ThinVec<StatementInfoItem<X>>,
496        /// Source location and node identity.
497        meta: Meta,
498    },
499    /// `CONDITION <number> <target> = <cond-item> [, …]` — one condition's items.
500    Condition {
501        /// The `CONDITION <number>` selector expression (a limited expr subset).
502        number: Box<Expr<X>>,
503        /// The requested condition items, in source order (always at least one).
504        items: ThinVec<ConditionInfoItem<X>>,
505        /// Source location and node identity.
506        meta: Meta,
507    },
508}
509
510/// One `<target> = {NUMBER | ROW_COUNT}` item in a `GET DIAGNOSTICS` statement-information
511/// list.
512#[derive(Clone, Debug, PartialEq, Eq, Hash)]
513#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
514#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
515pub struct StatementInfoItem<X: Extension = NoExt> {
516    /// The target lvalue receiving the value (a `@user` variable or a local variable name).
517    pub target: Expr<X>,
518    /// Which statement-level field is read.
519    pub name: StatementInfoItemName,
520    /// Source location and node identity.
521    pub meta: Meta,
522}
523
524/// The readable statement-level diagnostics item name (a tag, no `meta`).
525#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
526#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
527#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
528pub enum StatementInfoItemName {
529    /// `NUMBER` — the count of conditions in the diagnostics area.
530    Number,
531    /// `ROW_COUNT` — the affected-row count of the previous statement.
532    RowCount,
533}
534
535/// One `<target> = <cond-item>` item in a `GET DIAGNOSTICS CONDITION` list.
536#[derive(Clone, Debug, PartialEq, Eq, Hash)]
537#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
538#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
539pub struct ConditionInfoItem<X: Extension = NoExt> {
540    /// The target lvalue receiving the value (a `@user` variable or a local variable name).
541    pub target: Expr<X>,
542    /// Which condition-level field is read.
543    pub name: ConditionInfoItemName,
544    /// Source location and node identity.
545    pub meta: Meta,
546}
547
548/// The readable condition-level diagnostics item name (a tag, no `meta`).
549///
550/// A superset of [`SignalItemName`]: every signal-settable field plus `RETURNED_SQLSTATE`,
551/// which is readable here but not signal-settable — the two lists are intentionally
552/// asymmetric, so they are distinct enums.
553#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
554#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
555#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
556pub enum ConditionInfoItemName {
557    /// `CLASS_ORIGIN`.
558    ClassOrigin,
559    /// `SUBCLASS_ORIGIN`.
560    SubclassOrigin,
561    /// `CONSTRAINT_CATALOG`.
562    ConstraintCatalog,
563    /// `CONSTRAINT_SCHEMA`.
564    ConstraintSchema,
565    /// `CONSTRAINT_NAME`.
566    ConstraintName,
567    /// `CATALOG_NAME`.
568    CatalogName,
569    /// `SCHEMA_NAME`.
570    SchemaName,
571    /// `TABLE_NAME`.
572    TableName,
573    /// `COLUMN_NAME`.
574    ColumnName,
575    /// `CURSOR_NAME`.
576    CursorName,
577    /// `MESSAGE_TEXT`.
578    MessageText,
579    /// `MYSQL_ERRNO`.
580    MysqlErrno,
581    /// `RETURNED_SQLSTATE` — readable only (not signal-settable).
582    ReturnedSqlstate,
583}