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, string::String, 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 Exclusion(ExclusionConstraint),
107 PrimaryKeyUsingIndex(ConstraintUsingIndex),
115 UniqueUsingIndex(ConstraintUsingIndex),
123}
124
125impl From<UniqueConstraint> for TableConstraint {
126 fn from(constraint: UniqueConstraint) -> Self {
127 TableConstraint::Unique(constraint)
128 }
129}
130
131impl From<PrimaryKeyConstraint> for TableConstraint {
132 fn from(constraint: PrimaryKeyConstraint) -> Self {
133 TableConstraint::PrimaryKey(constraint)
134 }
135}
136
137impl From<ForeignKeyConstraint> for TableConstraint {
138 fn from(constraint: ForeignKeyConstraint) -> Self {
139 TableConstraint::ForeignKey(constraint)
140 }
141}
142
143impl From<CheckConstraint> for TableConstraint {
144 fn from(constraint: CheckConstraint) -> Self {
145 TableConstraint::Check(constraint)
146 }
147}
148
149impl From<IndexConstraint> for TableConstraint {
150 fn from(constraint: IndexConstraint) -> Self {
151 TableConstraint::Index(constraint)
152 }
153}
154
155impl From<FullTextOrSpatialConstraint> for TableConstraint {
156 fn from(constraint: FullTextOrSpatialConstraint) -> Self {
157 TableConstraint::FulltextOrSpatial(constraint)
158 }
159}
160
161impl From<ExclusionConstraint> for TableConstraint {
162 fn from(constraint: ExclusionConstraint) -> Self {
163 TableConstraint::Exclusion(constraint)
164 }
165}
166
167impl fmt::Display for TableConstraint {
168 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
169 match self {
170 TableConstraint::Unique(constraint) => constraint.fmt(f),
171 TableConstraint::PrimaryKey(constraint) => constraint.fmt(f),
172 TableConstraint::ForeignKey(constraint) => constraint.fmt(f),
173 TableConstraint::Check(constraint) => constraint.fmt(f),
174 TableConstraint::Index(constraint) => constraint.fmt(f),
175 TableConstraint::FulltextOrSpatial(constraint) => constraint.fmt(f),
176 TableConstraint::Exclusion(constraint) => constraint.fmt(f),
177 TableConstraint::PrimaryKeyUsingIndex(c) => c.fmt_with_keyword(f, "PRIMARY KEY"),
178 TableConstraint::UniqueUsingIndex(c) => c.fmt_with_keyword(f, "UNIQUE"),
179 }
180 }
181}
182
183#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
184#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
185#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
186pub struct CheckConstraint {
188 pub name: Option<Ident>,
190 pub expr: Box<Expr>,
192 pub enforced: Option<bool>,
195}
196
197impl fmt::Display for CheckConstraint {
198 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199 use crate::ast::ddl::display_constraint_name;
200 write!(
201 f,
202 "{}CHECK ({})",
203 display_constraint_name(&self.name),
204 self.expr
205 )?;
206 if let Some(b) = self.enforced {
207 write!(f, " {}", if b { "ENFORCED" } else { "NOT ENFORCED" })
208 } else {
209 Ok(())
210 }
211 }
212}
213
214impl crate::ast::Spanned for CheckConstraint {
215 fn span(&self) -> Span {
216 self.expr
217 .span()
218 .union_opt(&self.name.as_ref().map(|i| i.span))
219 }
220}
221
222#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
228#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
229#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
230pub struct ForeignKeyConstraint {
231 pub name: Option<Ident>,
233 pub index_name: Option<Ident>,
236 pub columns: Vec<Ident>,
238 pub foreign_table: ObjectName,
240 pub referred_columns: Vec<Ident>,
242 pub on_delete: Option<ReferentialAction>,
244 pub on_update: Option<ReferentialAction>,
246 pub match_kind: Option<ConstraintReferenceMatchKind>,
248 pub characteristics: Option<ConstraintCharacteristics>,
250}
251
252impl fmt::Display for ForeignKeyConstraint {
253 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
254 use crate::ast::ddl::{display_constraint_name, display_option_spaced};
255 write!(
256 f,
257 "{}FOREIGN KEY{} ({}) REFERENCES {}",
258 display_constraint_name(&self.name),
259 display_option_spaced(&self.index_name),
260 display_comma_separated(&self.columns),
261 self.foreign_table,
262 )?;
263 if !self.referred_columns.is_empty() {
264 write!(f, "({})", display_comma_separated(&self.referred_columns))?;
265 }
266 if let Some(match_kind) = &self.match_kind {
267 write!(f, " {match_kind}")?;
268 }
269 if let Some(action) = &self.on_delete {
270 write!(f, " ON DELETE {action}")?;
271 }
272 if let Some(action) = &self.on_update {
273 write!(f, " ON UPDATE {action}")?;
274 }
275 if let Some(characteristics) = &self.characteristics {
276 write!(f, " {characteristics}")?;
277 }
278 Ok(())
279 }
280}
281
282impl crate::ast::Spanned for ForeignKeyConstraint {
283 fn span(&self) -> Span {
284 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
285 Span::union_iter(iter)
286 }
287
288 union_spans(
289 self.name
290 .iter()
291 .map(|i| i.span)
292 .chain(self.index_name.iter().map(|i| i.span))
293 .chain(self.columns.iter().map(|i| i.span))
294 .chain(core::iter::once(self.foreign_table.span()))
295 .chain(self.referred_columns.iter().map(|i| i.span))
296 .chain(self.on_delete.iter().map(|i| i.span()))
297 .chain(self.on_update.iter().map(|i| i.span()))
298 .chain(self.characteristics.iter().map(|i| i.span())),
299 )
300 }
301}
302
303#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
317#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
318#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
319pub struct FullTextOrSpatialConstraint {
320 pub fulltext: bool,
322 pub index_type_display: KeyOrIndexDisplay,
324 pub opt_index_name: Option<Ident>,
326 pub columns: Vec<IndexColumn>,
328}
329
330impl fmt::Display for FullTextOrSpatialConstraint {
331 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
332 if self.fulltext {
333 write!(f, "FULLTEXT")?;
334 } else {
335 write!(f, "SPATIAL")?;
336 }
337
338 write!(f, "{:>}", self.index_type_display)?;
339
340 if let Some(name) = &self.opt_index_name {
341 write!(f, " {name}")?;
342 }
343
344 write!(f, " ({})", display_comma_separated(&self.columns))?;
345
346 Ok(())
347 }
348}
349
350impl crate::ast::Spanned for FullTextOrSpatialConstraint {
351 fn span(&self) -> Span {
352 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
353 Span::union_iter(iter)
354 }
355
356 union_spans(
357 self.opt_index_name
358 .iter()
359 .map(|i| i.span)
360 .chain(self.columns.iter().map(|i| i.span())),
361 )
362 }
363}
364
365#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
372#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
373#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
374pub struct IndexConstraint {
375 pub display_as_key: bool,
377 pub name: Option<Ident>,
379 pub index_type: Option<IndexType>,
383 pub columns: Vec<IndexColumn>,
385 pub index_options: Vec<IndexOption>,
388}
389
390impl fmt::Display for IndexConstraint {
391 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
392 write!(f, "{}", if self.display_as_key { "KEY" } else { "INDEX" })?;
393 if let Some(name) = &self.name {
394 write!(f, " {name}")?;
395 }
396 if let Some(index_type) = &self.index_type {
397 write!(f, " USING {index_type}")?;
398 }
399 write!(f, " ({})", display_comma_separated(&self.columns))?;
400 if !self.index_options.is_empty() {
401 write!(f, " {}", display_comma_separated(&self.index_options))?;
402 }
403 Ok(())
404 }
405}
406
407impl crate::ast::Spanned for IndexConstraint {
408 fn span(&self) -> Span {
409 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
410 Span::union_iter(iter)
411 }
412
413 union_spans(
414 self.name
415 .iter()
416 .map(|i| i.span)
417 .chain(self.columns.iter().map(|i| i.span())),
418 )
419 }
420}
421
422#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
441#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
442#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
443pub struct PrimaryKeyConstraint {
444 pub name: Option<Ident>,
448 pub index_name: Option<Ident>,
450 pub index_type: Option<IndexType>,
454 pub columns: Vec<IndexColumn>,
456 pub index_options: Vec<IndexOption>,
458 pub characteristics: Option<ConstraintCharacteristics>,
460}
461
462impl fmt::Display for PrimaryKeyConstraint {
463 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
464 use crate::ast::ddl::{display_constraint_name, display_option, display_option_spaced};
465 write!(
466 f,
467 "{}PRIMARY KEY{}{} ({})",
468 display_constraint_name(&self.name),
469 display_option_spaced(&self.index_name),
470 display_option(" USING ", "", &self.index_type),
471 display_comma_separated(&self.columns),
472 )?;
473
474 if !self.index_options.is_empty() {
475 write!(f, " {}", display_separated(&self.index_options, " "))?;
476 }
477
478 write!(f, "{}", display_option_spaced(&self.characteristics))?;
479 Ok(())
480 }
481}
482
483impl crate::ast::Spanned for PrimaryKeyConstraint {
484 fn span(&self) -> Span {
485 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
486 Span::union_iter(iter)
487 }
488
489 union_spans(
490 self.name
491 .iter()
492 .map(|i| i.span)
493 .chain(self.index_name.iter().map(|i| i.span))
494 .chain(self.columns.iter().map(|i| i.span()))
495 .chain(self.characteristics.iter().map(|i| i.span())),
496 )
497 }
498}
499
500#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
501#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
502#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
503pub struct UniqueConstraint {
505 pub name: Option<Ident>,
509 pub index_name: Option<Ident>,
511 pub index_type_display: KeyOrIndexDisplay,
513 pub index_type: Option<IndexType>,
517 pub columns: Vec<IndexColumn>,
519 pub index_options: Vec<IndexOption>,
521 pub characteristics: Option<ConstraintCharacteristics>,
523 pub nulls_distinct: NullsDistinctOption,
525}
526
527impl fmt::Display for UniqueConstraint {
528 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
529 use crate::ast::ddl::{display_constraint_name, display_option, display_option_spaced};
530 write!(
531 f,
532 "{}UNIQUE{}{:>}{}{} ({})",
533 display_constraint_name(&self.name),
534 self.nulls_distinct,
535 self.index_type_display,
536 display_option_spaced(&self.index_name),
537 display_option(" USING ", "", &self.index_type),
538 display_comma_separated(&self.columns),
539 )?;
540
541 if !self.index_options.is_empty() {
542 write!(f, " {}", display_separated(&self.index_options, " "))?;
543 }
544
545 write!(f, "{}", display_option_spaced(&self.characteristics))?;
546 Ok(())
547 }
548}
549
550impl crate::ast::Spanned for UniqueConstraint {
551 fn span(&self) -> Span {
552 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
553 Span::union_iter(iter)
554 }
555
556 union_spans(
557 self.name
558 .iter()
559 .map(|i| i.span)
560 .chain(self.index_name.iter().map(|i| i.span))
561 .chain(self.columns.iter().map(|i| i.span()))
562 .chain(self.characteristics.iter().map(|i| i.span())),
563 )
564 }
565}
566
567#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
570#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
571#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
572pub struct ExclusionElement {
573 pub expr: Expr,
575 pub operator: String,
577}
578
579impl fmt::Display for ExclusionElement {
580 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
581 write!(f, "{} WITH {}", self.expr, self.operator)
582 }
583}
584
585#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
588#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
589#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
590pub struct ExclusionConstraint {
591 pub name: Option<Ident>,
593 pub index_method: Option<Ident>,
595 pub elements: Vec<ExclusionElement>,
597 pub include: Vec<Ident>,
599 pub where_clause: Option<Box<Expr>>,
601 pub characteristics: Option<ConstraintCharacteristics>,
603}
604
605impl fmt::Display for ExclusionConstraint {
606 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
607 use crate::ast::ddl::display_constraint_name;
608 write!(f, "{}EXCLUDE", display_constraint_name(&self.name))?;
609 if let Some(method) = &self.index_method {
610 write!(f, " USING {method}")?;
611 }
612 write!(f, " ({})", display_comma_separated(&self.elements))?;
613 if !self.include.is_empty() {
614 write!(f, " INCLUDE ({})", display_comma_separated(&self.include))?;
615 }
616 if let Some(predicate) = &self.where_clause {
617 write!(f, " WHERE ({predicate})")?;
618 }
619 if let Some(characteristics) = &self.characteristics {
620 write!(f, " {characteristics}")?;
621 }
622 Ok(())
623 }
624}
625
626impl crate::ast::Spanned for ExclusionConstraint {
627 fn span(&self) -> Span {
628 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
629 Span::union_iter(iter)
630 }
631
632 union_spans(
633 self.name
634 .iter()
635 .map(|i| i.span)
636 .chain(self.index_method.iter().map(|i| i.span))
637 .chain(self.include.iter().map(|i| i.span))
638 .chain(self.where_clause.iter().map(|e| e.span()))
639 .chain(self.characteristics.iter().map(|c| c.span())),
640 )
641 }
642}
643
644#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
651#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
652#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
653pub struct ConstraintUsingIndex {
654 pub name: Option<Ident>,
656 pub index_name: Ident,
658 pub characteristics: Option<ConstraintCharacteristics>,
660}
661
662impl ConstraintUsingIndex {
663 pub fn fmt_with_keyword(&self, f: &mut fmt::Formatter, keyword: &str) -> fmt::Result {
665 use crate::ast::ddl::{display_constraint_name, display_option_spaced};
666 write!(
667 f,
668 "{}{} USING INDEX {}",
669 display_constraint_name(&self.name),
670 keyword,
671 self.index_name,
672 )?;
673 write!(f, "{}", display_option_spaced(&self.characteristics))?;
674 Ok(())
675 }
676}
677
678impl crate::ast::Spanned for ConstraintUsingIndex {
679 fn span(&self) -> Span {
680 let start = self
681 .name
682 .as_ref()
683 .map(|i| i.span)
684 .unwrap_or(self.index_name.span);
685 let end = self
686 .characteristics
687 .as_ref()
688 .map(|c| c.span())
689 .unwrap_or(self.index_name.span);
690 start.union(&end)
691 }
692}