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 include: Vec<Ident>,
448 pub index_options: Vec<IndexOption>,
450 pub characteristics: Option<ConstraintCharacteristics>,
452}
453
454impl fmt::Display for PrimaryKeyConstraint {
455 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
456 use crate::ast::ddl::{display_constraint_name, display_option, display_option_spaced};
457 write!(
458 f,
459 "{}PRIMARY KEY{}{} ({})",
460 display_constraint_name(&self.name),
461 display_option_spaced(&self.index_name),
462 display_option(" USING ", "", &self.index_type),
463 display_comma_separated(&self.columns),
464 )?;
465
466 if !self.include.is_empty() {
467 write!(f, " INCLUDE ({})", display_comma_separated(&self.include))?;
468 }
469
470 if !self.index_options.is_empty() {
471 write!(f, " {}", display_separated(&self.index_options, " "))?;
472 }
473
474 write!(f, "{}", display_option_spaced(&self.characteristics))?;
475 Ok(())
476 }
477}
478
479impl crate::ast::Spanned for PrimaryKeyConstraint {
480 fn span(&self) -> Span {
481 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
482 Span::union_iter(iter)
483 }
484
485 union_spans(
486 self.name
487 .iter()
488 .map(|i| i.span)
489 .chain(self.index_name.iter().map(|i| i.span))
490 .chain(self.columns.iter().map(|i| i.span()))
491 .chain(self.include.iter().map(|i| i.span))
492 .chain(self.characteristics.iter().map(|i| i.span())),
493 )
494 }
495}
496
497#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
498#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
499#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
500pub struct UniqueConstraint {
502 pub name: Option<Ident>,
506 pub index_name: Option<Ident>,
508 pub index_type_display: KeyOrIndexDisplay,
510 pub index_type: Option<IndexType>,
514 pub columns: Vec<IndexColumn>,
516 pub include: Vec<Ident>,
518 pub index_options: Vec<IndexOption>,
520 pub characteristics: Option<ConstraintCharacteristics>,
522 pub nulls_distinct: NullsDistinctOption,
524}
525
526impl fmt::Display for UniqueConstraint {
527 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
528 use crate::ast::ddl::{display_constraint_name, display_option, display_option_spaced};
529 write!(
530 f,
531 "{}UNIQUE{}{:>}{}{} ({})",
532 display_constraint_name(&self.name),
533 self.nulls_distinct,
534 self.index_type_display,
535 display_option_spaced(&self.index_name),
536 display_option(" USING ", "", &self.index_type),
537 display_comma_separated(&self.columns),
538 )?;
539
540 if !self.include.is_empty() {
541 write!(f, " INCLUDE ({})", display_comma_separated(&self.include))?;
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.include.iter().map(|i| i.span))
566 .chain(self.characteristics.iter().map(|i| i.span())),
567 )
568 }
569}
570
571#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
578#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
579#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
580pub struct ConstraintUsingIndex {
581 pub name: Option<Ident>,
583 pub index_name: Ident,
585 pub characteristics: Option<ConstraintCharacteristics>,
587}
588
589impl ConstraintUsingIndex {
590 pub fn fmt_with_keyword(&self, f: &mut fmt::Formatter, keyword: &str) -> fmt::Result {
592 use crate::ast::ddl::{display_constraint_name, display_option_spaced};
593 write!(
594 f,
595 "{}{} USING INDEX {}",
596 display_constraint_name(&self.name),
597 keyword,
598 self.index_name,
599 )?;
600 write!(f, "{}", display_option_spaced(&self.characteristics))?;
601 Ok(())
602 }
603}
604
605impl crate::ast::Spanned for ConstraintUsingIndex {
606 fn span(&self) -> Span {
607 let start = self
608 .name
609 .as_ref()
610 .map(|i| i.span)
611 .unwrap_or(self.index_name.span);
612 let end = self
613 .characteristics
614 .as_ref()
615 .map(|c| c.span())
616 .unwrap_or(self.index_name.span);
617 start.union(&end)
618 }
619}