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 pub index_options: Vec<IndexOption>,
333}
334
335impl fmt::Display for FullTextOrSpatialConstraint {
336 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
337 if self.fulltext {
338 write!(f, "FULLTEXT")?;
339 } else {
340 write!(f, "SPATIAL")?;
341 }
342
343 write!(f, "{:>}", self.index_type_display)?;
344
345 if let Some(name) = &self.opt_index_name {
346 write!(f, " {name}")?;
347 }
348
349 write!(f, " ({})", display_comma_separated(&self.columns))?;
350 if !self.index_options.is_empty() {
351 write!(f, " {}", display_separated(&self.index_options, " "))?;
352 }
353
354 Ok(())
355 }
356}
357
358impl crate::ast::Spanned for FullTextOrSpatialConstraint {
359 fn span(&self) -> Span {
360 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
361 Span::union_iter(iter)
362 }
363
364 union_spans(
365 self.opt_index_name
366 .iter()
367 .map(|i| i.span)
368 .chain(self.columns.iter().map(|i| i.span())),
369 )
370 }
371}
372
373#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
380#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
381#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
382pub struct IndexConstraint {
383 pub display_as_key: bool,
385 pub name: Option<Ident>,
387 pub index_type: Option<IndexType>,
391 pub columns: Vec<IndexColumn>,
393 pub index_options: Vec<IndexOption>,
396}
397
398impl fmt::Display for IndexConstraint {
399 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
400 write!(f, "{}", if self.display_as_key { "KEY" } else { "INDEX" })?;
401 if let Some(name) = &self.name {
402 write!(f, " {name}")?;
403 }
404 if let Some(index_type) = &self.index_type {
405 write!(f, " USING {index_type}")?;
406 }
407 write!(f, " ({})", display_comma_separated(&self.columns))?;
408 if !self.index_options.is_empty() {
409 write!(f, " {}", display_comma_separated(&self.index_options))?;
410 }
411 Ok(())
412 }
413}
414
415impl crate::ast::Spanned for IndexConstraint {
416 fn span(&self) -> Span {
417 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
418 Span::union_iter(iter)
419 }
420
421 union_spans(
422 self.name
423 .iter()
424 .map(|i| i.span)
425 .chain(self.columns.iter().map(|i| i.span())),
426 )
427 }
428}
429
430#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
449#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
450#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
451pub struct PrimaryKeyConstraint {
452 pub name: Option<Ident>,
456 pub index_name: Option<Ident>,
458 pub index_type: Option<IndexType>,
462 pub columns: Vec<IndexColumn>,
464 pub index_options: Vec<IndexOption>,
466 pub characteristics: Option<ConstraintCharacteristics>,
468}
469
470impl fmt::Display for PrimaryKeyConstraint {
471 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
472 use crate::ast::ddl::{display_constraint_name, display_option, display_option_spaced};
473 write!(
474 f,
475 "{}PRIMARY KEY{}{} ({})",
476 display_constraint_name(&self.name),
477 display_option_spaced(&self.index_name),
478 display_option(" USING ", "", &self.index_type),
479 display_comma_separated(&self.columns),
480 )?;
481
482 if !self.index_options.is_empty() {
483 write!(f, " {}", display_separated(&self.index_options, " "))?;
484 }
485
486 write!(f, "{}", display_option_spaced(&self.characteristics))?;
487 Ok(())
488 }
489}
490
491impl crate::ast::Spanned for PrimaryKeyConstraint {
492 fn span(&self) -> Span {
493 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
494 Span::union_iter(iter)
495 }
496
497 union_spans(
498 self.name
499 .iter()
500 .map(|i| i.span)
501 .chain(self.index_name.iter().map(|i| i.span))
502 .chain(self.columns.iter().map(|i| i.span()))
503 .chain(self.characteristics.iter().map(|i| i.span())),
504 )
505 }
506}
507
508#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
509#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
510#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
511pub struct UniqueConstraint {
513 pub name: Option<Ident>,
517 pub index_name: Option<Ident>,
519 pub index_type_display: KeyOrIndexDisplay,
521 pub index_type: Option<IndexType>,
525 pub columns: Vec<IndexColumn>,
527 pub index_options: Vec<IndexOption>,
529 pub characteristics: Option<ConstraintCharacteristics>,
531 pub nulls_distinct: NullsDistinctOption,
533}
534
535impl fmt::Display for UniqueConstraint {
536 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
537 use crate::ast::ddl::{display_constraint_name, display_option, display_option_spaced};
538 write!(
539 f,
540 "{}UNIQUE{}{:>}{}{} ({})",
541 display_constraint_name(&self.name),
542 self.nulls_distinct,
543 self.index_type_display,
544 display_option_spaced(&self.index_name),
545 display_option(" USING ", "", &self.index_type),
546 display_comma_separated(&self.columns),
547 )?;
548
549 if !self.index_options.is_empty() {
550 write!(f, " {}", display_separated(&self.index_options, " "))?;
551 }
552
553 write!(f, "{}", display_option_spaced(&self.characteristics))?;
554 Ok(())
555 }
556}
557
558impl crate::ast::Spanned for UniqueConstraint {
559 fn span(&self) -> Span {
560 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
561 Span::union_iter(iter)
562 }
563
564 union_spans(
565 self.name
566 .iter()
567 .map(|i| i.span)
568 .chain(self.index_name.iter().map(|i| i.span))
569 .chain(self.columns.iter().map(|i| i.span()))
570 .chain(self.characteristics.iter().map(|i| i.span())),
571 )
572 }
573}
574
575#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
582#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
583#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
584pub struct ConstraintUsingIndex {
585 pub name: Option<Ident>,
587 pub index_name: Ident,
589 pub characteristics: Option<ConstraintCharacteristics>,
591}
592
593impl ConstraintUsingIndex {
594 pub fn fmt_with_keyword(&self, f: &mut fmt::Formatter, keyword: &str) -> fmt::Result {
596 use crate::ast::ddl::{display_constraint_name, display_option_spaced};
597 write!(
598 f,
599 "{}{} USING INDEX {}",
600 display_constraint_name(&self.name),
601 keyword,
602 self.index_name,
603 )?;
604 write!(f, "{}", display_option_spaced(&self.characteristics))?;
605 Ok(())
606 }
607}
608
609impl crate::ast::Spanned for ConstraintUsingIndex {
610 fn span(&self) -> Span {
611 let start = self
612 .name
613 .as_ref()
614 .map(|i| i.span)
615 .unwrap_or(self.index_name.span);
616 let end = self
617 .characteristics
618 .as_ref()
619 .map(|c| c.span())
620 .unwrap_or(self.index_name.span);
621 start.union(&end)
622 }
623}
624
625#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
629#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
630#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
631pub enum ExcludeConstraintOperator {
632 Token(String),
634 PGOperator(Vec<String>),
636}
637
638impl fmt::Display for ExcludeConstraintOperator {
639 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
640 match self {
641 ExcludeConstraintOperator::Token(token) => f.write_str(token),
642 ExcludeConstraintOperator::PGOperator(parts) => {
643 write!(f, "OPERATOR({})", display_separated(parts, "."))
644 }
645 }
646 }
647}
648
649#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
653#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
654#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
655pub struct ExcludeConstraintElement {
656 pub column: IndexColumn,
658 pub operator: ExcludeConstraintOperator,
660}
661
662impl fmt::Display for ExcludeConstraintElement {
663 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
664 write!(f, "{} WITH {}", self.column, self.operator)
665 }
666}
667
668#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
672#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
673#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
674pub struct ExcludeConstraint {
675 pub name: Option<Ident>,
677 pub index_method: Option<Ident>,
679 pub elements: Vec<ExcludeConstraintElement>,
681 pub include: Vec<Ident>,
683 pub where_clause: Option<Box<Expr>>,
685 pub characteristics: Option<ConstraintCharacteristics>,
687}
688
689impl fmt::Display for ExcludeConstraint {
690 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
691 use crate::ast::ddl::display_constraint_name;
692 write!(f, "{}EXCLUDE", display_constraint_name(&self.name))?;
693 if let Some(method) = &self.index_method {
694 write!(f, " USING {method}")?;
695 }
696 write!(f, " ({})", display_comma_separated(&self.elements))?;
697 if !self.include.is_empty() {
698 write!(f, " INCLUDE ({})", display_comma_separated(&self.include))?;
699 }
700 if let Some(predicate) = &self.where_clause {
701 write!(f, " WHERE ({predicate})")?;
702 }
703 if let Some(characteristics) = &self.characteristics {
704 write!(f, " {characteristics}")?;
705 }
706 Ok(())
707 }
708}
709
710impl crate::ast::Spanned for ExcludeConstraint {
711 fn span(&self) -> Span {
712 Span::union_iter(
713 self.name
714 .iter()
715 .map(|i| i.span)
716 .chain(self.index_method.iter().map(|i| i.span))
717 .chain(self.elements.iter().map(|e| e.span()))
718 .chain(self.include.iter().map(|i| i.span))
719 .chain(self.where_clause.iter().map(|e| e.span()))
720 .chain(self.characteristics.iter().map(|c| c.span())),
721 )
722 }
723}