squonk_ast/render/mod.rs
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 Moderately AI Inc.
3
4//! SQL rendering: the `Render` trait, `RenderCtx` / `RenderConfig` / `RenderMode`,
5//! the `Displayed` wrapper, and the Tier-1 canonical renderer.
6//!
7//! Nodes are non-self-describing: an `Ident` is a `Symbol`, a literal is a byte
8//! range. So `std::fmt::Display`, whose `fmt` takes no extra
9//! arguments, cannot reach the resolver or source needed to spell a node. The
10//! renderer therefore threads a [`RenderCtx`] through a dedicated [`Render`]
11//! trait, and [`Displayed`] re-enables `Display` at any boundary by pairing a
12//! node with its context.
13//!
14//! That canonical path is exact but strict: it assumes the resolver and source
15//! match the node's parse, and panics on a symbol they cannot back. For the narrow
16//! case of debugging a *detached* node whose context may not match, [`RenderCtx::debug`]
17//! and the [`DebugSql`] wrapper (via [`RenderExt::debug_sql`]) add an opt-in,
18//! resolver-explicit path that tolerates unresolvable symbols and spans with a
19//! placeholder instead of panicking — the debug-SQL mitigation, realized as
20//! an explicit argument rather than a hidden thread-local, so
21//! normal rendering keeps no hidden global behaviour.
22
23use crate::ast::{DataType, Expr, Extension, Query, Statement};
24use crate::dialect::FeatureSet;
25use crate::precedence::BindingPower;
26use crate::vocab::{NodeId, Resolver, Span, Symbol};
27use std::fmt;
28
29mod dyn_ext;
30mod nodes;
31
32pub use dyn_ext::{DynAstExt, DynExt};
33pub use nodes::{render_extension_infix, render_extension_prefix};
34
35#[cfg(test)]
36mod tests;
37
38/// Render an AST node to SQL text using the resolver, source, and config carried
39/// by `ctx`.
40pub trait Render {
41 /// Return the render for this value.
42 fn render(&self, ctx: &RenderCtx<'_>, f: &mut fmt::Formatter<'_>) -> fmt::Result;
43
44 /// The binding power this node contributes when it appears as an operand, or
45 /// `None` (the default) for a self-delimiting node — an atom, call, or
46 /// constructor — that never needs parentheses.
47 ///
48 /// An extension operator node (the `X` inside [`Expr::Other`]) overrides this to
49 /// report its own precedence, so a custom-operator tree is parenthesized by the
50 /// same binding-power rule as the built-in operators and round-trips through the
51 /// renderer. The value MUST equal the binding power the dialect's
52 /// `peek_infix_operator_hook` reported for the same operator, so parse-time
53 /// climbing and render-time grouping stay one source of truth.
54 ///
55 /// [`Expr::Other`]: crate::ast::Expr::Other
56 fn operand_binding_power(&self) -> Option<BindingPower> {
57 None
58 }
59}
60
61/// Fallible render result used by Tier-2 dialect-target renderers.
62pub type RenderResult<T> = Result<T, RenderError>;
63
64/// Why a fallible render operation failed.
65#[derive(Clone, Debug, PartialEq, Eq)]
66pub struct RenderError {
67 kind: RenderErrorKind,
68 span: Option<Span>,
69 message: String,
70}
71
72/// Stable category for render failures.
73#[derive(Clone, Copy, Debug, PartialEq, Eq)]
74pub enum RenderErrorKind {
75 /// The target dialect cannot express the AST construct being rendered.
76 Unsupported,
77 /// The underlying formatter reported an error.
78 Format,
79}
80
81impl RenderError {
82 /// Build an unsupported-target diagnostic.
83 pub fn unsupported(span: Option<Span>, message: impl Into<String>) -> Self {
84 Self {
85 kind: RenderErrorKind::Unsupported,
86 span,
87 message: message.into(),
88 }
89 }
90
91 /// The coarse render error category.
92 pub fn kind(&self) -> RenderErrorKind {
93 self.kind
94 }
95
96 /// The source span of the unsupported construct, when known.
97 pub fn span(&self) -> Option<Span> {
98 self.span
99 }
100
101 /// Human-readable diagnostic text.
102 pub fn message(&self) -> &str {
103 &self.message
104 }
105}
106
107impl fmt::Display for RenderError {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 match self.span {
110 Some(span) if !span.is_synthetic() => {
111 write!(
112 f,
113 "{} at bytes {}..{}",
114 self.message,
115 span.start(),
116 span.end()
117 )
118 }
119 _ => f.write_str(&self.message),
120 }
121 }
122}
123
124impl std::error::Error for RenderError {}
125
126impl From<fmt::Error> for RenderError {
127 fn from(_error: fmt::Error) -> Self {
128 Self {
129 kind: RenderErrorKind::Format,
130 span: None,
131 message: "render formatter failed".to_owned(),
132 }
133 }
134}
135
136/// How a [`Render`] impl spells a node.
137#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
138pub enum RenderMode {
139 /// Round-trippable SQL with the minimal parentheses the binding-power table
140 /// requires.
141 #[default]
142 Canonical,
143 /// Every binary/unary subexpression fully parenthesized: the precedence
144 /// oracle used by testing.
145 Parenthesized,
146 /// Identifier and literal *content* replaced by fixed placeholders — `id` for
147 /// every identifier, `?` for every literal — while query *shape* is preserved,
148 /// yielding a stable, PII-free query fingerprint.
149 ///
150 /// Two statements produce the **same** redacted string exactly when they differ
151 /// only along a dimension masking erases:
152 /// - identifier spelling: every name (column, table, alias, function, and each
153 /// qualified part) becomes `id`, so `a` and `b` coincide;
154 /// - identifier quoting: the delimiters are dropped before the symbol is
155 /// resolved, so `a`, `"a"`, `` `a` ``, and `[a]` all become `id` (a keyword
156 /// spelled as an identifier masks the same way);
157 /// - literal value: every literal becomes `?`, so `1` and `999`, `'x'` and a
158 /// secret string, `TRUE` and `NULL` coincide;
159 /// - keyword casing: canonical rendering upper-cases keywords, so `select` and
160 /// `SELECT` coincide.
161 ///
162 /// Query shape is **kept**, so the fingerprint still separates statements that
163 /// differ in clause structure, projection/list arity, qualified-name arity
164 /// (`id` vs `id.id`), operators or keywords (`=` vs `<`, `AND` vs `OR`, `SELECT`
165 /// vs `SELECT DISTINCT`), or the parentheses the binding-power table requires.
166 ///
167 /// The result is a fingerprint, **not** guaranteed re-parseable — only
168 /// [`Canonical`](RenderMode::Canonical) round-trips. A masked literal renders as
169 /// `?`, the anonymous-parameter sigil, which only lexes under a dialect that
170 /// enables it (neither ANSI nor PostgreSQL do) and, even then, would re-parse as
171 /// a *parameter* rather than the literal it replaced. An identifier-only
172 /// redaction happens to re-parse, but that is incidental, not a promise.
173 Redacted,
174}
175
176/// How renderable surface tags are spelled.
177#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
178pub enum RenderSpelling {
179 /// Preserve the AST's source spelling tags while emitting normalized uppercase SQL.
180 #[default]
181 PreserveSource,
182 /// Prefer spellings for the explicit target [`FeatureSet`].
183 TargetDialect,
184}
185
186/// Tunable rendering options.
187#[derive(Clone, Debug, PartialEq, Eq)]
188pub struct RenderConfig {
189 /// Mode selected by this syntax.
190 pub mode: RenderMode,
191 /// Object targeted by this syntax.
192 pub target: FeatureSet,
193 /// Exact source spelling retained for faithful rendering.
194 pub spelling: RenderSpelling,
195}
196
197impl RenderConfig {
198 /// The canonical default, as a `const` so a borrow of it (`&RenderConfig::DEFAULT`)
199 /// promotes to `'static` (rvalue static promotion) instead of living only as long
200 /// as some local binding. [`RenderExt::debug_sql`] needs exactly that: it builds a
201 /// [`RenderCtx`] with no caller-supplied config to borrow, and the [`DebugSql`] it
202 /// returns must keep that ctx alive past the constructing call, so a local
203 /// `RenderConfig::default()` temporary would not outlive the borrow.
204 const DEFAULT: RenderConfig = RenderConfig {
205 mode: RenderMode::Canonical,
206 target: FeatureSet::ANSI,
207 spelling: RenderSpelling::PreserveSource,
208 };
209}
210
211impl Default for RenderConfig {
212 fn default() -> Self {
213 Self::DEFAULT
214 }
215}
216
217/// Placeholder spelled for a [`Symbol`] the debug renderer cannot resolve.
218///
219/// Deliberately not a valid bare identifier (the angle brackets are operators), so
220/// a foreign or unknown symbol reads as an obvious debug artifact rather than a
221/// plausible-but-wrong name. Only the opt-in [`RenderCtx::debug`] path emits it;
222/// canonical rendering panics on an unresolvable symbol instead.
223const UNRESOLVED_SYMBOL: &str = "<unresolved>";
224
225/// Whether a [`RenderCtx`] takes the canonical render path or the opt-in debug path.
226///
227/// The distinction is confined to [`RenderCtx::resolve`] and [`RenderCtx::slice`]:
228/// every [`Render`] body is written once and is unaware of it. Canonical is the
229/// default; the debug path is reached only through [`RenderCtx::debug`] /
230/// [`RenderExt::debug_sql`], so normal rendering keeps no hidden global behaviour.
231#[derive(Clone, Copy, PartialEq, Eq)]
232enum ContextKind {
233 /// Canonical rendering: resolution is strict — a symbol foreign to
234 /// the resolver is a caller bug, so it panics — and literals slice their exact
235 /// source spelling.
236 Canonical,
237 /// Opt-in debug rendering: resolution is tolerant — a symbol the resolver does
238 /// not know renders the `UNRESOLVED_SYMBOL` placeholder instead of panicking — and literals
239 /// never slice source (a detached node's spans may index a *different* string),
240 /// so they fall back to their kind-based spelling.
241 Debug,
242}
243
244/// Resolver, source, and config threaded through every [`Render`] call.
245///
246/// The [`Resolver`] trait (not the concrete interner) lives in this crate, so the
247/// renderer stays independent of parser internals. `config` is *borrowed*, not
248/// owned: a multi-statement render builds one [`RenderCtx`] per statement (or per
249/// render — see the parser crate's `Parsed::to_sql`), and `RenderConfig` is large
250/// enough — it embeds the whole target [`FeatureSet`], keyword/precedence tables and
251/// all — that owning a fresh copy each time was a real per-statement memcpy of data
252/// that never changes across a render.
253pub struct RenderCtx<'a> {
254 resolver: &'a dyn Resolver,
255 source: &'a str,
256 config: &'a RenderConfig,
257 kind: ContextKind,
258}
259
260impl<'a> RenderCtx<'a> {
261 /// Build a canonical context from a resolver, the original source text, and
262 /// config.
263 ///
264 /// This is the canonical render path: the resolver and source must
265 /// come from the *same* parse as the node, because resolution is strict (a
266 /// foreign symbol panics) and literals slice their exact source spelling. Prefer
267 /// it — and the [`Displayed`] wrapper or the parser crate's directly-`Display`
268 /// `Parsed` root over it — whenever the matched context is in hand. For a
269 /// *detached* node whose context may not match, reach for the tolerant
270 /// [`debug`](Self::debug) sibling instead.
271 ///
272 /// `config` is borrowed, so building a ctx per statement (a multi-statement
273 /// render's loop) costs copying a pointer, never a `RenderConfig` copy.
274 pub fn new(resolver: &'a dyn Resolver, source: &'a str, config: &'a RenderConfig) -> Self {
275 Self {
276 resolver,
277 source,
278 config,
279 kind: ContextKind::Canonical,
280 }
281 }
282
283 /// Build the opt-in *debug* context: an explicitly-supplied resolver with
284 /// tolerant resolution and no source slicing (the debug-SQL mitigation).
285 ///
286 /// Use this — or the [`RenderExt::debug_sql`] convenience over it — to render a
287 /// *detached* or *synthesized* node for debugging, where the canonical
288 /// [`new`](Self::new) path's guarantees may not hold:
289 ///
290 /// - The resolver is an **explicit argument**, never a hidden global or default,
291 /// so debug rendering can never *silently* pick the wrong resolver — the choice
292 /// is always visible at the call site. (A resolver from a *different* parse can
293 /// still mis-resolve a numerically-colliding symbol; only the canonical path,
294 /// which travels with its own resolver, rules that out. See [`debug_sql`].)
295 /// - A symbol the resolver does not know renders the `UNRESOLVED_SYMBOL` placeholder instead of
296 /// panicking, so a partially-detached tree renders to completion.
297 /// - Source is deliberately **not** an argument: debug rendering never slices it,
298 /// so a literal always uses its kind-based spelling and can never emit
299 /// unrelated bytes from a mismatched source. For exact literal text, use the
300 /// canonical path, which owns the matched source.
301 ///
302 /// [`debug_sql`]: RenderExt::debug_sql
303 pub fn debug(resolver: &'a dyn Resolver, config: &'a RenderConfig) -> Self {
304 Self {
305 resolver,
306 // Debug rendering never slices source (see `slice`); the field is unused
307 // on this path, so an empty borrow keeps source out of the debug API.
308 source: "",
309 config,
310 kind: ContextKind::Debug,
311 }
312 }
313
314 /// The resolver used to turn [`Symbol`]s back into identifier text.
315 pub fn resolver(&self) -> &dyn Resolver {
316 self.resolver
317 }
318
319 /// The original source text that literals are sliced from.
320 pub fn source(&self) -> &str {
321 self.source
322 }
323
324 /// The active render configuration.
325 pub fn config(&self) -> &RenderConfig {
326 self.config
327 }
328
329 /// The active render mode.
330 pub fn mode(&self) -> RenderMode {
331 self.config.mode
332 }
333
334 /// The active target dialect feature set.
335 pub fn target(&self) -> &FeatureSet {
336 &self.config.target
337 }
338
339 /// The active spelling policy.
340 pub fn spelling(&self) -> RenderSpelling {
341 self.config.spelling
342 }
343
344 /// Resolve `sym` to its identifier text.
345 ///
346 /// # Panics
347 ///
348 /// On the canonical path ([`new`](Self::new)), panics if `sym` was not produced
349 /// by this context's resolver; a node and its resolver must come from the same
350 /// parse. The debug path ([`debug`](Self::debug)) never panics — an unknown
351 /// symbol resolves to the `UNRESOLVED_SYMBOL` placeholder instead.
352 fn resolve(&self, sym: Symbol) -> &str {
353 match self.kind {
354 ContextKind::Canonical => self.resolver.resolve(sym),
355 ContextKind::Debug => self.resolver.try_resolve(sym).unwrap_or(UNRESOLVED_SYMBOL),
356 }
357 }
358
359 /// Slice the source covered by `span`, or `None` when there is no backing text.
360 ///
361 /// Returns `None` for a synthetic / out-of-range span (a detached or
362 /// rewrite-synthesized node has no source text), and always for the debug path
363 /// ([`debug`](Self::debug)), which never trusts a detached node's spans against a
364 /// possibly-mismatched source. A `None` result is total: [`Literal`] falls back
365 /// to a kind-based spelling rather than panicking.
366 ///
367 /// [`Literal`]: crate::ast::Literal
368 fn slice(&self, span: Span) -> Option<&str> {
369 if self.kind == ContextKind::Debug || span.is_synthetic() {
370 return None;
371 }
372 self.source.get(span.start() as usize..span.end() as usize)
373 }
374}
375
376/// `Display` adapter pairing a node with its [`RenderCtx`] so `format!`,
377/// `to_string`, and `{}` work at any boundary.
378pub struct Displayed<'a, T: Render>(&'a T, &'a RenderCtx<'a>);
379
380impl<T: Render> fmt::Display for Displayed<'_, T> {
381 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382 self.0.render(self.1, f)
383 }
384}
385
386/// `Display` adapter for the opt-in debug path: pairs a node with a resolver-only,
387/// tolerant [`RenderCtx`] so a *detached* node can be printed for debugging without
388/// building a full context (the debug-SQL mitigation).
389///
390/// Unlike [`Displayed`], this owns its context (built from the resolver alone), so
391/// [`RenderExt::debug_sql`] can return it directly. Rendering is tolerant: an
392/// unknown symbol becomes the `UNRESOLVED_SYMBOL` placeholder and literals use their kind-based
393/// spelling, so it never panics on a symbol or span the context cannot back. See
394/// [`debug_sql`](RenderExt::debug_sql) for when to prefer the canonical path.
395pub struct DebugSql<'a, T: Render> {
396 node: &'a T,
397 ctx: RenderCtx<'a>,
398}
399
400impl<T: Render> fmt::Display for DebugSql<'_, T> {
401 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
402 self.node.render(&self.ctx, f)
403 }
404}
405
406/// Ergonomic constructors for the [`Displayed`] and [`DebugSql`] adapters,
407/// blanket-impl'd for every [`Render`] type.
408pub trait RenderExt: Render {
409 /// Pair this node with an explicit canonical [`RenderCtx`] so `format!`,
410 /// `to_string`, and `{}` render it. This is the canonical path: the
411 /// `ctx`'s resolver and source must match the node's parse.
412 fn displayed<'a>(&'a self, ctx: &'a RenderCtx<'a>) -> Displayed<'a, Self>
413 where
414 Self: Sized;
415
416 /// Render this node for **debugging** against an explicitly-supplied `resolver`
417 /// (the debug-SQL mitigation), returning a `Display` adapter.
418 ///
419 /// Reach for this only for a *detached* or *synthesized* node — one not behind a
420 /// `Parsed` root and possibly holding symbols the `resolver` on hand does not
421 /// own. **Prefer the canonical path first:** the `Parsed` root's `Display` and
422 /// [`displayed`](Self::displayed) render exact SQL because they travel with the
423 /// matched source and resolver, and cannot mismatch.
424 ///
425 /// This helper trades exactness for safety on a detached node:
426 ///
427 /// - The `resolver` is an **explicit argument** — never a hidden thread-local,
428 /// global, or default — so it can never *silently* render with the wrong
429 /// resolver: the choice is always written at the call site.
430 /// - A symbol the `resolver` does not know renders as `<unresolved>` rather than
431 /// panicking, so a partially-detached tree still prints to completion.
432 /// - Literals use their kind-based spelling (`0`, `''`, `NULL`, …): debug
433 /// rendering never slices source, so it cannot emit unrelated bytes.
434 ///
435 /// **Limitation.** An *explicit* argument cannot detect a resolver from a
436 /// *different* parse: a symbol whose numeric id happens to collide with an entry
437 /// in that resolver resolves to the *other* parse's text. The helper prevents a
438 /// silent *choice* of the wrong resolver and turns *unknown* symbols into a
439 /// visible placeholder, but only the canonical path — which owns its own matched
440 /// resolver — rules a colliding foreign resolver out entirely.
441 fn debug_sql<'a>(&'a self, resolver: &'a dyn Resolver) -> DebugSql<'a, Self>
442 where
443 Self: Sized,
444 {
445 DebugSql {
446 node: self,
447 // `&RenderConfig::DEFAULT` promotes to `'static` (it is a bare `const`
448 // path, the case `rustc` always promotes), which is what lets an owned
449 // `RenderCtx<'a>` borrow a config here with no caller-supplied place to
450 // borrow one from.
451 ctx: RenderCtx::debug(resolver, &RenderConfig::DEFAULT),
452 }
453 }
454}
455
456impl<T: Render> RenderExt for T {
457 fn displayed<'a>(&'a self, ctx: &'a RenderCtx<'a>) -> Displayed<'a, Self> {
458 Displayed(self, ctx)
459 }
460}
461
462/// The audited allowlist of node kinds that render to a **self-contained** SQL
463/// fragment — a marker trait sealed against outside impls so the set stays a
464/// closed, reviewable list.
465///
466/// Every AST node has a [`Render`] impl, but most render only a *piece* of a larger
467/// construct that is meaningless on its own: a bare `JoinConstraint` renders `ON a =
468/// b` (no join), a `SelectItem` renders `a AS x` (no `SELECT`), an `OrderByExpr`
469/// renders `a DESC` (no `ORDER BY`). Handing those to a fragment API would emit
470/// plausible-looking but context-dependent SQL a consumer cannot re-parse. The
471/// fragment entry points ([`Parsed::render_fragment`]) therefore accept only the
472/// kinds in this list, which each stand alone:
473///
474/// - [`Expr`] — a complete scalar expression;
475/// - [`Query`] — a complete `SELECT` / set-operation / `VALUES` query;
476/// - [`Statement`] — a complete statement;
477/// - [`DataType`] — a complete type spelling (as it appears after `CAST(x AS …)` or
478/// in a column definition).
479///
480/// The gate is compile-time: a call site can only pass one of these types, so a
481/// context-dependent node is rejected by the type checker rather than silently
482/// rendered. The runtime sibling for language facades, which hold node handles by
483/// id, is [`Parsed::render_fragment_by_id`]; it enforces the *same* allowlist
484/// dynamically.
485///
486/// [`Parsed::render_fragment`]: https://docs.rs/squonk
487/// [`Parsed::render_fragment_by_id`]: https://docs.rs/squonk
488pub trait FragmentRender: Render + fragment_sealed::Sealed {
489 /// This node's [`NodeId`], used by the by-id fragment lookup to match a node
490 /// handle held by a language facade. Implementation detail of the fragment API —
491 /// prefer the node's own [`Meta`](crate::vocab::Meta) `node_id` field directly.
492 #[doc(hidden)]
493 fn fragment_node_id(&self) -> NodeId;
494}
495
496/// Seals [`FragmentRender`] so the standalone-renderable allowlist cannot be widened
497/// from outside this crate — only the audited node kinds below implement `Sealed`.
498mod fragment_sealed {
499 pub trait Sealed {}
500}
501
502impl<X: Extension + Render> fragment_sealed::Sealed for Query<X> {}
503impl<X: Extension + Render> FragmentRender for Query<X> {
504 fn fragment_node_id(&self) -> NodeId {
505 self.meta.node_id
506 }
507}
508
509// `Statement` is `#[non_exhaustive]`, so a cross-crate match would need a `_` arm
510// that could not report a new variant's id. Extracting every fragment id here also
511// keeps the `Expr` and `DataType` matches exhaustive, so adding any fragment variant
512// is a compile error to update rather than a silently unrenderable node (the
513// completeness guarantee the generated `NodeIdWalk` relies on for the same reason).
514impl<X: Extension + Render> fragment_sealed::Sealed for Statement<X> {}
515impl<X: Extension + Render> FragmentRender for Statement<X> {
516 fn fragment_node_id(&self) -> NodeId {
517 match self {
518 Statement::Query { meta, .. }
519 | Statement::CreateTable { meta, .. }
520 | Statement::AlterTable { meta, .. }
521 | Statement::Drop { meta, .. }
522 | Statement::CreateSchema { meta, .. }
523 | Statement::CreateView { meta, .. }
524 | Statement::RefreshMaterializedView { meta, .. }
525 | Statement::CreateColocationGroup { meta, .. }
526 | Statement::DropColocationGroup { meta, .. }
527 | Statement::AlterView { meta, .. }
528 | Statement::CreateIndex { meta, .. }
529 | Statement::CreateFunction { meta, .. }
530 | Statement::CreateProcedure { meta, .. }
531 | Statement::AlterRoutine { meta, .. }
532 | Statement::CreateEvent { meta, .. }
533 | Statement::AlterEvent { meta, .. }
534 | Statement::DropEvent { meta, .. }
535 | Statement::DropDatabase { meta, .. }
536 | Statement::DropIndex { meta, .. }
537 | Statement::CreateDatabase { meta, .. }
538 | Statement::DropRoutine { meta, .. }
539 | Statement::DropTransform { meta, .. }
540 | Statement::Truncate { meta, .. }
541 | Statement::CommentOn { meta, .. }
542 | Statement::Insert { meta, .. }
543 | Statement::Update { meta, .. }
544 | Statement::Delete { meta, .. }
545 | Statement::Merge { meta, .. }
546 | Statement::Transaction { meta, .. }
547 | Statement::Xa { meta, .. }
548 | Statement::Session { meta, .. }
549 | Statement::AccessControl { meta, .. }
550 | Statement::Copy { meta, .. }
551 | Statement::CopyInto { meta, .. }
552 | Statement::Export { meta, .. }
553 | Statement::Import { meta, .. }
554 | Statement::Explain { meta, .. }
555 | Statement::Describe { meta, .. }
556 | Statement::Show { meta, .. }
557 | Statement::Kill { meta, .. }
558 | Statement::Handler { meta, .. }
559 | Statement::Install { meta, .. }
560 | Statement::Uninstall { meta, .. }
561 | Statement::Shutdown { meta, .. }
562 | Statement::Restart { meta, .. }
563 | Statement::Clone { meta, .. }
564 | Statement::ImportTable { meta, .. }
565 | Statement::Help { meta, .. }
566 | Statement::Binlog { meta, .. }
567 | Statement::Pragma { meta, .. }
568 | Statement::Attach { meta, .. }
569 | Statement::Detach { meta, .. }
570 | Statement::Checkpoint { meta, .. }
571 | Statement::Load { meta, .. }
572 | Statement::LoadData { meta, .. }
573 | Statement::UpdateExtensions { meta, .. }
574 | Statement::Vacuum { meta, .. }
575 | Statement::Reindex { meta, .. }
576 | Statement::Analyze { meta, .. }
577 | Statement::Use { meta, .. }
578 | Statement::CreateTrigger { meta, .. }
579 | Statement::CreateStoredTrigger { meta, .. }
580 | Statement::CreateMacro { meta, .. }
581 | Statement::CreateSecret { meta, .. }
582 | Statement::DropSecret { meta, .. }
583 | Statement::CreateType { meta, .. }
584 | Statement::CreateVirtualTable { meta, .. }
585 | Statement::CreateSequence { meta, .. }
586 | Statement::CreateExtension { meta, .. }
587 | Statement::AlterExtension { meta, .. }
588 | Statement::CreateTablespace { meta, .. }
589 | Statement::AlterTablespace { meta, .. }
590 | Statement::DropTablespace { meta, .. }
591 | Statement::CreateLogfileGroup { meta, .. }
592 | Statement::AlterLogfileGroup { meta, .. }
593 | Statement::DropLogfileGroup { meta, .. }
594 | Statement::AlterObjectDepends { meta, .. }
595 | Statement::AlterSystem { meta, .. }
596 | Statement::AlterDatabase { meta, .. }
597 | Statement::AlterDatabaseOptions { meta, .. }
598 | Statement::CreateServer { meta, .. }
599 | Statement::AlterServer { meta, .. }
600 | Statement::DropServer { meta, .. }
601 | Statement::AlterInstance { meta, .. }
602 | Statement::CreateSpatialReferenceSystem { meta, .. }
603 | Statement::DropSpatialReferenceSystem { meta, .. }
604 | Statement::CreateResourceGroup { meta, .. }
605 | Statement::AlterResourceGroup { meta, .. }
606 | Statement::DropResourceGroup { meta, .. }
607 | Statement::AlterSequence { meta, .. }
608 | Statement::AlterObjectSchema { meta, .. }
609 | Statement::Pivot { meta, .. }
610 | Statement::Unpivot { meta, .. }
611 | Statement::ShowRef { meta, .. }
612 | Statement::Prepare { meta, .. }
613 | Statement::Execute { meta, .. }
614 | Statement::PrepareFrom { meta, .. }
615 | Statement::ExecuteUsing { meta, .. }
616 | Statement::Deallocate { meta, .. }
617 | Statement::Call { meta, .. }
618 | Statement::Do { meta, .. }
619 | Statement::DoExpressions { meta, .. }
620 | Statement::LockTables { meta, .. }
621 | Statement::UnlockTables { meta, .. }
622 | Statement::InstanceLock { meta, .. }
623 | Statement::Compound { meta, .. }
624 | Statement::If { meta, .. }
625 | Statement::Case { meta, .. }
626 | Statement::Loop { meta, .. }
627 | Statement::While { meta, .. }
628 | Statement::Repeat { meta, .. }
629 | Statement::Leave { meta, .. }
630 | Statement::Iterate { meta, .. }
631 | Statement::Return { meta, .. }
632 | Statement::OpenCursor { meta, .. }
633 | Statement::FetchCursor { meta, .. }
634 | Statement::CloseCursor { meta, .. }
635 | Statement::TableMaintenance { meta, .. }
636 | Statement::CacheIndex { meta, .. }
637 | Statement::LoadIndex { meta, .. }
638 | Statement::Rename { meta, .. }
639 | Statement::Flush { meta, .. }
640 | Statement::Purge { meta, .. }
641 | Statement::Replication { meta, .. }
642 | Statement::CreateUser { meta, .. }
643 | Statement::AlterUser { meta, .. }
644 | Statement::UserRoleList { meta, .. }
645 | Statement::Signal { meta, .. }
646 | Statement::Resignal { meta, .. }
647 | Statement::GetDiagnostics { meta, .. }
648 | Statement::Other { meta, .. } => meta.node_id,
649 }
650 }
651}
652
653impl<X: Extension + Render> fragment_sealed::Sealed for Expr<X> {}
654impl<X: Extension + Render> FragmentRender for Expr<X> {
655 fn fragment_node_id(&self) -> NodeId {
656 match self {
657 Expr::Column { meta, .. }
658 | Expr::Literal { meta, .. }
659 | Expr::BinaryOp { meta, .. }
660 | Expr::UnaryOp { meta, .. }
661 | Expr::Function { meta, .. }
662 | Expr::Case { meta, .. }
663 | Expr::Extract { meta, .. }
664 | Expr::Cast { meta, .. }
665 | Expr::IsNull { meta, .. }
666 | Expr::IsTruth { meta, .. }
667 | Expr::IsNormalized { meta, .. }
668 | Expr::Between { meta, .. }
669 | Expr::Like { meta, .. }
670 | Expr::InList { meta, .. }
671 | Expr::InSubquery { meta, .. }
672 | Expr::InExpr { meta, .. }
673 | Expr::Exists { meta, .. }
674 | Expr::QuantifiedComparison { meta, .. }
675 | Expr::QuantifiedList { meta, .. }
676 | Expr::QuantifiedLike { meta, .. }
677 | Expr::Subquery { meta, .. }
678 | Expr::Parameter { meta, .. }
679 | Expr::PositionalColumn { meta, .. }
680 | Expr::SessionVariable { meta, .. }
681 | Expr::Subscript { meta, .. }
682 | Expr::SemiStructuredAccess { meta, .. }
683 | Expr::Collate { meta, .. }
684 | Expr::AtTimeZone { meta, .. }
685 | Expr::Interval { meta, .. }
686 | Expr::Array { meta, .. }
687 | Expr::Struct { meta, .. }
688 | Expr::StructConstructor { meta, .. }
689 | Expr::Map { meta, .. }
690 | Expr::Row { meta, .. }
691 | Expr::FieldSelection { meta, .. }
692 | Expr::NamedOperator { meta, .. }
693 | Expr::PrefixOperator { meta, .. }
694 | Expr::PostfixOperator { meta, .. }
695 | Expr::Lambda { meta, .. }
696 | Expr::Columns { meta, .. }
697 | Expr::SpecialFunction { meta, .. }
698 | Expr::JsonFunc { meta, .. }
699 | Expr::JsonObject { meta, .. }
700 | Expr::JsonArray { meta, .. }
701 | Expr::JsonAggregate { meta, .. }
702 | Expr::JsonConstructor { meta, .. }
703 | Expr::IsJson { meta, .. }
704 | Expr::XmlFunc { meta, .. }
705 | Expr::IsDocument { meta, .. }
706 | Expr::StringFunc { meta, .. }
707 | Expr::Other { meta, .. } => meta.node_id,
708 }
709 }
710}
711
712impl<X: Extension + Render> fragment_sealed::Sealed for DataType<X> {}
713impl<X: Extension + Render> FragmentRender for DataType<X> {
714 fn fragment_node_id(&self) -> NodeId {
715 match self {
716 DataType::Boolean { meta, .. }
717 | DataType::TinyInt { meta, .. }
718 | DataType::SmallInt { meta, .. }
719 | DataType::MediumInt { meta, .. }
720 | DataType::Integer { meta, .. }
721 | DataType::BigInt { meta, .. }
722 | DataType::Decimal { meta, .. }
723 | DataType::Float { meta, .. }
724 | DataType::Real { meta, .. }
725 | DataType::Double { meta, .. }
726 | DataType::Text { meta, .. }
727 | DataType::Blob { meta, .. }
728 | DataType::Character { meta, .. }
729 | DataType::Binary { meta, .. }
730 | DataType::Bit { meta, .. }
731 | DataType::Json { meta, .. }
732 | DataType::Uuid { meta, .. }
733 | DataType::Date { meta, .. }
734 | DataType::Time { meta, .. }
735 | DataType::Timestamp { meta, .. }
736 | DataType::Interval { meta, .. }
737 | DataType::Enum { meta, .. }
738 | DataType::Set { meta, .. }
739 | DataType::NumericModifier { meta, .. }
740 | DataType::Array { meta, .. }
741 | DataType::Struct { meta, .. }
742 | DataType::Union { meta, .. }
743 | DataType::Map { meta, .. }
744 | DataType::Wrapped { meta, .. }
745 | DataType::FixedString { meta, .. }
746 | DataType::DateTime64 { meta, .. }
747 | DataType::Nested { meta, .. }
748 | DataType::FixedWidthInt { meta, .. }
749 | DataType::UserDefined { meta, .. }
750 | DataType::Liberal { meta, .. }
751 | DataType::Other { meta, .. } => meta.node_id,
752 }
753 }
754}