1use crate::ast::{
21 display_comma_separated, display_separated, ConstraintCharacteristics,
22 ConstraintReferenceMatchKind, Expr, Ident, IndexColumn, IndexOption, IndexType,
23 KeyOrIndexDisplay, NullsDistinctOption, ObjectName, ReferentialAction,
24};
25use crate::tokenizer::Span;
26use core::fmt;
27
28#[cfg(not(feature = "std"))]
29use alloc::{boxed::Box, vec::Vec};
30
31#[cfg(feature = "serde")]
32use serde::{Deserialize, Serialize};
33
34#[cfg(feature = "visitor")]
35use sqlparser_derive::{Visit, VisitMut};
36
37#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
40#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
41#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
42pub enum TableConstraint {
43 Unique(UniqueConstraint),
56 PrimaryKey(PrimaryKeyConstraint),
75 ForeignKey(ForeignKeyConstraint),
81 Check(CheckConstraint),
83 Index(IndexConstraint),
90 FulltextOrSpatial(FullTextOrSpatialConstraint),
104 PrimaryKeyUsingIndex(ConstraintUsingIndex),
112 UniqueUsingIndex(ConstraintUsingIndex),
120}
121
122impl From<UniqueConstraint> for TableConstraint {
123 fn from(constraint: UniqueConstraint) -> Self {
124 TableConstraint::Unique(constraint)
125 }
126}
127
128impl From<PrimaryKeyConstraint> for TableConstraint {
129 fn from(constraint: PrimaryKeyConstraint) -> Self {
130 TableConstraint::PrimaryKey(constraint)
131 }
132}
133
134impl From<ForeignKeyConstraint> for TableConstraint {
135 fn from(constraint: ForeignKeyConstraint) -> Self {
136 TableConstraint::ForeignKey(constraint)
137 }
138}
139
140impl From<CheckConstraint> for TableConstraint {
141 fn from(constraint: CheckConstraint) -> Self {
142 TableConstraint::Check(constraint)
143 }
144}
145
146impl From<IndexConstraint> for TableConstraint {
147 fn from(constraint: IndexConstraint) -> Self {
148 TableConstraint::Index(constraint)
149 }
150}
151
152impl From<FullTextOrSpatialConstraint> for TableConstraint {
153 fn from(constraint: FullTextOrSpatialConstraint) -> Self {
154 TableConstraint::FulltextOrSpatial(constraint)
155 }
156}
157
158impl fmt::Display for TableConstraint {
159 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
160 match self {
161 TableConstraint::Unique(constraint) => constraint.fmt(f),
162 TableConstraint::PrimaryKey(constraint) => constraint.fmt(f),
163 TableConstraint::ForeignKey(constraint) => constraint.fmt(f),
164 TableConstraint::Check(constraint) => constraint.fmt(f),
165 TableConstraint::Index(constraint) => constraint.fmt(f),
166 TableConstraint::FulltextOrSpatial(constraint) => constraint.fmt(f),
167 TableConstraint::PrimaryKeyUsingIndex(c) => c.fmt_with_keyword(f, "PRIMARY KEY"),
168 TableConstraint::UniqueUsingIndex(c) => c.fmt_with_keyword(f, "UNIQUE"),
169 }
170 }
171}
172
173#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
174#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
175#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
176pub struct CheckConstraint {
178 pub name: Option<Ident>,
180 pub expr: Box<Expr>,
182 pub enforced: Option<bool>,
185}
186
187impl fmt::Display for CheckConstraint {
188 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
189 use crate::ast::ddl::display_constraint_name;
190 write!(
191 f,
192 "{}CHECK ({})",
193 display_constraint_name(&self.name),
194 self.expr
195 )?;
196 if let Some(b) = self.enforced {
197 write!(f, " {}", if b { "ENFORCED" } else { "NOT ENFORCED" })
198 } else {
199 Ok(())
200 }
201 }
202}
203
204impl crate::ast::Spanned for CheckConstraint {
205 fn span(&self) -> Span {
206 self.expr
207 .span()
208 .union_opt(&self.name.as_ref().map(|i| i.span))
209 }
210}
211
212#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
218#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
219#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
220pub struct ForeignKeyConstraint {
221 pub name: Option<Ident>,
223 pub index_name: Option<Ident>,
226 pub columns: Vec<Ident>,
228 pub foreign_table: ObjectName,
230 pub referred_columns: Vec<Ident>,
232 pub on_delete: Option<ReferentialAction>,
234 pub on_update: Option<ReferentialAction>,
236 pub match_kind: Option<ConstraintReferenceMatchKind>,
238 pub characteristics: Option<ConstraintCharacteristics>,
240}
241
242impl fmt::Display for ForeignKeyConstraint {
243 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
244 use crate::ast::ddl::{display_constraint_name, display_option_spaced};
245 write!(
246 f,
247 "{}FOREIGN KEY{} ({}) REFERENCES {}",
248 display_constraint_name(&self.name),
249 display_option_spaced(&self.index_name),
250 display_comma_separated(&self.columns),
251 self.foreign_table,
252 )?;
253 if !self.referred_columns.is_empty() {
254 write!(f, "({})", display_comma_separated(&self.referred_columns))?;
255 }
256 if let Some(match_kind) = &self.match_kind {
257 write!(f, " {match_kind}")?;
258 }
259 if let Some(action) = &self.on_delete {
260 write!(f, " ON DELETE {action}")?;
261 }
262 if let Some(action) = &self.on_update {
263 write!(f, " ON UPDATE {action}")?;
264 }
265 if let Some(characteristics) = &self.characteristics {
266 write!(f, " {characteristics}")?;
267 }
268 Ok(())
269 }
270}
271
272impl crate::ast::Spanned for ForeignKeyConstraint {
273 fn span(&self) -> Span {
274 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
275 Span::union_iter(iter)
276 }
277
278 union_spans(
279 self.name
280 .iter()
281 .map(|i| i.span)
282 .chain(self.index_name.iter().map(|i| i.span))
283 .chain(self.columns.iter().map(|i| i.span))
284 .chain(core::iter::once(self.foreign_table.span()))
285 .chain(self.referred_columns.iter().map(|i| i.span))
286 .chain(self.on_delete.iter().map(|i| i.span()))
287 .chain(self.on_update.iter().map(|i| i.span()))
288 .chain(self.characteristics.iter().map(|i| i.span())),
289 )
290 }
291}
292
293#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
307#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
308#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
309pub struct FullTextOrSpatialConstraint {
310 pub fulltext: bool,
312 pub index_type_display: KeyOrIndexDisplay,
314 pub opt_index_name: Option<Ident>,
316 pub columns: Vec<IndexColumn>,
318}
319
320impl fmt::Display for FullTextOrSpatialConstraint {
321 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
322 if self.fulltext {
323 write!(f, "FULLTEXT")?;
324 } else {
325 write!(f, "SPATIAL")?;
326 }
327
328 write!(f, "{:>}", self.index_type_display)?;
329
330 if let Some(name) = &self.opt_index_name {
331 write!(f, " {name}")?;
332 }
333
334 write!(f, " ({})", display_comma_separated(&self.columns))?;
335
336 Ok(())
337 }
338}
339
340impl crate::ast::Spanned for FullTextOrSpatialConstraint {
341 fn span(&self) -> Span {
342 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
343 Span::union_iter(iter)
344 }
345
346 union_spans(
347 self.opt_index_name
348 .iter()
349 .map(|i| i.span)
350 .chain(self.columns.iter().map(|i| i.span())),
351 )
352 }
353}
354
355#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
362#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
363#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
364pub struct IndexConstraint {
365 pub display_as_key: bool,
367 pub name: Option<Ident>,
369 pub index_type: Option<IndexType>,
373 pub columns: Vec<IndexColumn>,
375 pub index_options: Vec<IndexOption>,
378}
379
380impl fmt::Display for IndexConstraint {
381 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
382 write!(f, "{}", if self.display_as_key { "KEY" } else { "INDEX" })?;
383 if let Some(name) = &self.name {
384 write!(f, " {name}")?;
385 }
386 if let Some(index_type) = &self.index_type {
387 write!(f, " USING {index_type}")?;
388 }
389 write!(f, " ({})", display_comma_separated(&self.columns))?;
390 if !self.index_options.is_empty() {
391 write!(f, " {}", display_comma_separated(&self.index_options))?;
392 }
393 Ok(())
394 }
395}
396
397impl crate::ast::Spanned for IndexConstraint {
398 fn span(&self) -> Span {
399 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
400 Span::union_iter(iter)
401 }
402
403 union_spans(
404 self.name
405 .iter()
406 .map(|i| i.span)
407 .chain(self.columns.iter().map(|i| i.span())),
408 )
409 }
410}
411
412#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
431#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
432#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
433pub struct PrimaryKeyConstraint {
434 pub name: Option<Ident>,
438 pub index_name: Option<Ident>,
440 pub index_type: Option<IndexType>,
444 pub columns: Vec<IndexColumn>,
446 pub index_options: Vec<IndexOption>,
448 pub characteristics: Option<ConstraintCharacteristics>,
450}
451
452impl fmt::Display for PrimaryKeyConstraint {
453 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
454 use crate::ast::ddl::{display_constraint_name, display_option, display_option_spaced};
455 write!(
456 f,
457 "{}PRIMARY KEY{}{} ({})",
458 display_constraint_name(&self.name),
459 display_option_spaced(&self.index_name),
460 display_option(" USING ", "", &self.index_type),
461 display_comma_separated(&self.columns),
462 )?;
463
464 if !self.index_options.is_empty() {
465 write!(f, " {}", display_separated(&self.index_options, " "))?;
466 }
467
468 write!(f, "{}", display_option_spaced(&self.characteristics))?;
469 Ok(())
470 }
471}
472
473impl crate::ast::Spanned for PrimaryKeyConstraint {
474 fn span(&self) -> Span {
475 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
476 Span::union_iter(iter)
477 }
478
479 union_spans(
480 self.name
481 .iter()
482 .map(|i| i.span)
483 .chain(self.index_name.iter().map(|i| i.span))
484 .chain(self.columns.iter().map(|i| i.span()))
485 .chain(self.characteristics.iter().map(|i| i.span())),
486 )
487 }
488}
489
490#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
491#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
492#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
493pub struct UniqueConstraint {
495 pub name: Option<Ident>,
499 pub index_name: Option<Ident>,
501 pub index_type_display: KeyOrIndexDisplay,
503 pub index_type: Option<IndexType>,
507 pub columns: Vec<IndexColumn>,
509 pub index_options: Vec<IndexOption>,
511 pub characteristics: Option<ConstraintCharacteristics>,
513 pub nulls_distinct: NullsDistinctOption,
515}
516
517impl fmt::Display for UniqueConstraint {
518 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
519 use crate::ast::ddl::{display_constraint_name, display_option, display_option_spaced};
520 write!(
521 f,
522 "{}UNIQUE{}{:>}{}{} ({})",
523 display_constraint_name(&self.name),
524 self.nulls_distinct,
525 self.index_type_display,
526 display_option_spaced(&self.index_name),
527 display_option(" USING ", "", &self.index_type),
528 display_comma_separated(&self.columns),
529 )?;
530
531 if !self.index_options.is_empty() {
532 write!(f, " {}", display_separated(&self.index_options, " "))?;
533 }
534
535 write!(f, "{}", display_option_spaced(&self.characteristics))?;
536 Ok(())
537 }
538}
539
540impl crate::ast::Spanned for UniqueConstraint {
541 fn span(&self) -> Span {
542 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
543 Span::union_iter(iter)
544 }
545
546 union_spans(
547 self.name
548 .iter()
549 .map(|i| i.span)
550 .chain(self.index_name.iter().map(|i| i.span))
551 .chain(self.columns.iter().map(|i| i.span()))
552 .chain(self.characteristics.iter().map(|i| i.span())),
553 )
554 }
555}
556
557#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
564#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
565#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
566pub struct ConstraintUsingIndex {
567 pub name: Option<Ident>,
569 pub index_name: Ident,
571 pub characteristics: Option<ConstraintCharacteristics>,
573}
574
575impl ConstraintUsingIndex {
576 pub fn fmt_with_keyword(&self, f: &mut fmt::Formatter, keyword: &str) -> fmt::Result {
578 use crate::ast::ddl::{display_constraint_name, display_option_spaced};
579 write!(
580 f,
581 "{}{} USING INDEX {}",
582 display_constraint_name(&self.name),
583 keyword,
584 self.index_name,
585 )?;
586 write!(f, "{}", display_option_spaced(&self.characteristics))?;
587 Ok(())
588 }
589}
590
591impl crate::ast::Spanned for ConstraintUsingIndex {
592 fn span(&self) -> Span {
593 let start = self
594 .name
595 .as_ref()
596 .map(|i| i.span)
597 .unwrap_or(self.index_name.span);
598 let end = self
599 .characteristics
600 .as_ref()
601 .map(|c| c.span())
602 .unwrap_or(self.index_name.span);
603 start.union(&end)
604 }
605}