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 PrimaryKeyUsingIndex(ConstraintUsingIndex),
112 UniqueUsingIndex(ConstraintUsingIndex),
120 Exclude(ExcludeConstraint),
126}
127
128impl From<UniqueConstraint> for TableConstraint {
129 fn from(constraint: UniqueConstraint) -> Self {
130 TableConstraint::Unique(constraint)
131 }
132}
133
134impl From<PrimaryKeyConstraint> for TableConstraint {
135 fn from(constraint: PrimaryKeyConstraint) -> Self {
136 TableConstraint::PrimaryKey(constraint)
137 }
138}
139
140impl From<ForeignKeyConstraint> for TableConstraint {
141 fn from(constraint: ForeignKeyConstraint) -> Self {
142 TableConstraint::ForeignKey(constraint)
143 }
144}
145
146impl From<CheckConstraint> for TableConstraint {
147 fn from(constraint: CheckConstraint) -> Self {
148 TableConstraint::Check(constraint)
149 }
150}
151
152impl From<IndexConstraint> for TableConstraint {
153 fn from(constraint: IndexConstraint) -> Self {
154 TableConstraint::Index(constraint)
155 }
156}
157
158impl From<FullTextOrSpatialConstraint> for TableConstraint {
159 fn from(constraint: FullTextOrSpatialConstraint) -> Self {
160 TableConstraint::FulltextOrSpatial(constraint)
161 }
162}
163
164impl From<ExcludeConstraint> for TableConstraint {
165 fn from(constraint: ExcludeConstraint) -> Self {
166 TableConstraint::Exclude(constraint)
167 }
168}
169
170impl fmt::Display for TableConstraint {
171 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
172 match self {
173 TableConstraint::Unique(constraint) => constraint.fmt(f),
174 TableConstraint::PrimaryKey(constraint) => constraint.fmt(f),
175 TableConstraint::ForeignKey(constraint) => constraint.fmt(f),
176 TableConstraint::Check(constraint) => constraint.fmt(f),
177 TableConstraint::Index(constraint) => constraint.fmt(f),
178 TableConstraint::FulltextOrSpatial(constraint) => constraint.fmt(f),
179 TableConstraint::PrimaryKeyUsingIndex(c) => c.fmt_with_keyword(f, "PRIMARY KEY"),
180 TableConstraint::UniqueUsingIndex(c) => c.fmt_with_keyword(f, "UNIQUE"),
181 TableConstraint::Exclude(constraint) => constraint.fmt(f),
182 }
183 }
184}
185
186#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
187#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
188#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
189pub struct CheckConstraint {
191 pub name: Option<Ident>,
193 pub expr: Box<Expr>,
195 pub enforced: Option<bool>,
198}
199
200impl fmt::Display for CheckConstraint {
201 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
202 use crate::ast::ddl::display_constraint_name;
203 write!(
204 f,
205 "{}CHECK ({})",
206 display_constraint_name(&self.name),
207 self.expr
208 )?;
209 if let Some(b) = self.enforced {
210 write!(f, " {}", if b { "ENFORCED" } else { "NOT ENFORCED" })
211 } else {
212 Ok(())
213 }
214 }
215}
216
217impl crate::ast::Spanned for CheckConstraint {
218 fn span(&self) -> Span {
219 self.expr
220 .span()
221 .union_opt(&self.name.as_ref().map(|i| i.span))
222 }
223}
224
225#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
231#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
232#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
233pub struct ForeignKeyConstraint {
234 pub name: Option<Ident>,
236 pub index_name: Option<Ident>,
239 pub columns: Vec<Ident>,
241 pub foreign_table: ObjectName,
243 pub referred_columns: Vec<Ident>,
245 pub on_delete: Option<ReferentialAction>,
247 pub on_update: Option<ReferentialAction>,
249 pub match_kind: Option<ConstraintReferenceMatchKind>,
251 pub characteristics: Option<ConstraintCharacteristics>,
253}
254
255impl fmt::Display for ForeignKeyConstraint {
256 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
257 use crate::ast::ddl::{display_constraint_name, display_option_spaced};
258 write!(
259 f,
260 "{}FOREIGN KEY{} ({}) REFERENCES {}",
261 display_constraint_name(&self.name),
262 display_option_spaced(&self.index_name),
263 display_comma_separated(&self.columns),
264 self.foreign_table,
265 )?;
266 if !self.referred_columns.is_empty() {
267 write!(f, "({})", display_comma_separated(&self.referred_columns))?;
268 }
269 if let Some(match_kind) = &self.match_kind {
270 write!(f, " {match_kind}")?;
271 }
272 if let Some(action) = &self.on_delete {
273 write!(f, " ON DELETE {action}")?;
274 }
275 if let Some(action) = &self.on_update {
276 write!(f, " ON UPDATE {action}")?;
277 }
278 if let Some(characteristics) = &self.characteristics {
279 write!(f, " {characteristics}")?;
280 }
281 Ok(())
282 }
283}
284
285impl crate::ast::Spanned for ForeignKeyConstraint {
286 fn span(&self) -> Span {
287 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
288 Span::union_iter(iter)
289 }
290
291 union_spans(
292 self.name
293 .iter()
294 .map(|i| i.span)
295 .chain(self.index_name.iter().map(|i| i.span))
296 .chain(self.columns.iter().map(|i| i.span))
297 .chain(core::iter::once(self.foreign_table.span()))
298 .chain(self.referred_columns.iter().map(|i| i.span))
299 .chain(self.on_delete.iter().map(|i| i.span()))
300 .chain(self.on_update.iter().map(|i| i.span()))
301 .chain(self.characteristics.iter().map(|i| i.span())),
302 )
303 }
304}
305
306#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
320#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
321#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
322pub struct FullTextOrSpatialConstraint {
323 pub fulltext: bool,
325 pub index_type_display: KeyOrIndexDisplay,
327 pub opt_index_name: Option<Ident>,
329 pub columns: Vec<IndexColumn>,
331}
332
333impl fmt::Display for FullTextOrSpatialConstraint {
334 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
335 if self.fulltext {
336 write!(f, "FULLTEXT")?;
337 } else {
338 write!(f, "SPATIAL")?;
339 }
340
341 write!(f, "{:>}", self.index_type_display)?;
342
343 if let Some(name) = &self.opt_index_name {
344 write!(f, " {name}")?;
345 }
346
347 write!(f, " ({})", display_comma_separated(&self.columns))?;
348
349 Ok(())
350 }
351}
352
353impl crate::ast::Spanned for FullTextOrSpatialConstraint {
354 fn span(&self) -> Span {
355 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
356 Span::union_iter(iter)
357 }
358
359 union_spans(
360 self.opt_index_name
361 .iter()
362 .map(|i| i.span)
363 .chain(self.columns.iter().map(|i| i.span())),
364 )
365 }
366}
367
368#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
375#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
376#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
377pub struct IndexConstraint {
378 pub display_as_key: bool,
380 pub name: Option<Ident>,
382 pub index_type: Option<IndexType>,
386 pub columns: Vec<IndexColumn>,
388 pub index_options: Vec<IndexOption>,
391}
392
393impl fmt::Display for IndexConstraint {
394 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
395 write!(f, "{}", if self.display_as_key { "KEY" } else { "INDEX" })?;
396 if let Some(name) = &self.name {
397 write!(f, " {name}")?;
398 }
399 if let Some(index_type) = &self.index_type {
400 write!(f, " USING {index_type}")?;
401 }
402 write!(f, " ({})", display_comma_separated(&self.columns))?;
403 if !self.index_options.is_empty() {
404 write!(f, " {}", display_comma_separated(&self.index_options))?;
405 }
406 Ok(())
407 }
408}
409
410impl crate::ast::Spanned for IndexConstraint {
411 fn span(&self) -> Span {
412 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
413 Span::union_iter(iter)
414 }
415
416 union_spans(
417 self.name
418 .iter()
419 .map(|i| i.span)
420 .chain(self.columns.iter().map(|i| i.span())),
421 )
422 }
423}
424
425#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
444#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
445#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
446pub struct PrimaryKeyConstraint {
447 pub name: Option<Ident>,
451 pub index_name: Option<Ident>,
453 pub index_type: Option<IndexType>,
457 pub columns: Vec<IndexColumn>,
459 pub index_options: Vec<IndexOption>,
461 pub characteristics: Option<ConstraintCharacteristics>,
463}
464
465impl fmt::Display for PrimaryKeyConstraint {
466 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
467 use crate::ast::ddl::{display_constraint_name, display_option, display_option_spaced};
468 write!(
469 f,
470 "{}PRIMARY KEY{}{} ({})",
471 display_constraint_name(&self.name),
472 display_option_spaced(&self.index_name),
473 display_option(" USING ", "", &self.index_type),
474 display_comma_separated(&self.columns),
475 )?;
476
477 if !self.index_options.is_empty() {
478 write!(f, " {}", display_separated(&self.index_options, " "))?;
479 }
480
481 write!(f, "{}", display_option_spaced(&self.characteristics))?;
482 Ok(())
483 }
484}
485
486impl crate::ast::Spanned for PrimaryKeyConstraint {
487 fn span(&self) -> Span {
488 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
489 Span::union_iter(iter)
490 }
491
492 union_spans(
493 self.name
494 .iter()
495 .map(|i| i.span)
496 .chain(self.index_name.iter().map(|i| i.span))
497 .chain(self.columns.iter().map(|i| i.span()))
498 .chain(self.characteristics.iter().map(|i| i.span())),
499 )
500 }
501}
502
503#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
504#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
505#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
506pub struct UniqueConstraint {
508 pub name: Option<Ident>,
512 pub index_name: Option<Ident>,
514 pub index_type_display: KeyOrIndexDisplay,
516 pub index_type: Option<IndexType>,
520 pub columns: Vec<IndexColumn>,
522 pub index_options: Vec<IndexOption>,
524 pub characteristics: Option<ConstraintCharacteristics>,
526 pub nulls_distinct: NullsDistinctOption,
528}
529
530impl fmt::Display for UniqueConstraint {
531 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
532 use crate::ast::ddl::{display_constraint_name, display_option, display_option_spaced};
533 write!(
534 f,
535 "{}UNIQUE{}{:>}{}{} ({})",
536 display_constraint_name(&self.name),
537 self.nulls_distinct,
538 self.index_type_display,
539 display_option_spaced(&self.index_name),
540 display_option(" USING ", "", &self.index_type),
541 display_comma_separated(&self.columns),
542 )?;
543
544 if !self.index_options.is_empty() {
545 write!(f, " {}", display_separated(&self.index_options, " "))?;
546 }
547
548 write!(f, "{}", display_option_spaced(&self.characteristics))?;
549 Ok(())
550 }
551}
552
553impl crate::ast::Spanned for UniqueConstraint {
554 fn span(&self) -> Span {
555 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
556 Span::union_iter(iter)
557 }
558
559 union_spans(
560 self.name
561 .iter()
562 .map(|i| i.span)
563 .chain(self.index_name.iter().map(|i| i.span))
564 .chain(self.columns.iter().map(|i| i.span()))
565 .chain(self.characteristics.iter().map(|i| i.span())),
566 )
567 }
568}
569
570#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
577#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
578#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
579pub struct ConstraintUsingIndex {
580 pub name: Option<Ident>,
582 pub index_name: Ident,
584 pub characteristics: Option<ConstraintCharacteristics>,
586}
587
588impl ConstraintUsingIndex {
589 pub fn fmt_with_keyword(&self, f: &mut fmt::Formatter, keyword: &str) -> fmt::Result {
591 use crate::ast::ddl::{display_constraint_name, display_option_spaced};
592 write!(
593 f,
594 "{}{} USING INDEX {}",
595 display_constraint_name(&self.name),
596 keyword,
597 self.index_name,
598 )?;
599 write!(f, "{}", display_option_spaced(&self.characteristics))?;
600 Ok(())
601 }
602}
603
604impl crate::ast::Spanned for ConstraintUsingIndex {
605 fn span(&self) -> Span {
606 let start = self
607 .name
608 .as_ref()
609 .map(|i| i.span)
610 .unwrap_or(self.index_name.span);
611 let end = self
612 .characteristics
613 .as_ref()
614 .map(|c| c.span())
615 .unwrap_or(self.index_name.span);
616 start.union(&end)
617 }
618}
619
620#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
624#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
625#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
626pub enum ExcludeConstraintOperator {
627 Token(String),
629 PGOperator(Vec<String>),
631}
632
633impl fmt::Display for ExcludeConstraintOperator {
634 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
635 match self {
636 ExcludeConstraintOperator::Token(token) => f.write_str(token),
637 ExcludeConstraintOperator::PGOperator(parts) => {
638 write!(f, "OPERATOR({})", display_separated(parts, "."))
639 }
640 }
641 }
642}
643
644#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
648#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
649#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
650pub struct ExcludeConstraintElement {
651 pub column: IndexColumn,
653 pub operator: ExcludeConstraintOperator,
655}
656
657impl fmt::Display for ExcludeConstraintElement {
658 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
659 write!(f, "{} WITH {}", self.column, self.operator)
660 }
661}
662
663#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
667#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
668#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
669pub struct ExcludeConstraint {
670 pub name: Option<Ident>,
672 pub index_method: Option<Ident>,
674 pub elements: Vec<ExcludeConstraintElement>,
676 pub include: Vec<Ident>,
678 pub where_clause: Option<Box<Expr>>,
680 pub characteristics: Option<ConstraintCharacteristics>,
682}
683
684impl fmt::Display for ExcludeConstraint {
685 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
686 use crate::ast::ddl::display_constraint_name;
687 write!(f, "{}EXCLUDE", display_constraint_name(&self.name))?;
688 if let Some(method) = &self.index_method {
689 write!(f, " USING {method}")?;
690 }
691 write!(f, " ({})", display_comma_separated(&self.elements))?;
692 if !self.include.is_empty() {
693 write!(f, " INCLUDE ({})", display_comma_separated(&self.include))?;
694 }
695 if let Some(predicate) = &self.where_clause {
696 write!(f, " WHERE ({predicate})")?;
697 }
698 if let Some(characteristics) = &self.characteristics {
699 write!(f, " {characteristics}")?;
700 }
701 Ok(())
702 }
703}
704
705impl crate::ast::Spanned for ExcludeConstraint {
706 fn span(&self) -> Span {
707 Span::union_iter(
708 self.name
709 .iter()
710 .map(|i| i.span)
711 .chain(self.index_method.iter().map(|i| i.span))
712 .chain(self.elements.iter().map(|e| e.span()))
713 .chain(self.include.iter().map(|i| i.span))
714 .chain(self.where_clause.iter().map(|e| e.span()))
715 .chain(self.characteristics.iter().map(|c| c.span())),
716 )
717 }
718}