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 Exclusion(ExclusionConstraint),
107}
108
109impl From<UniqueConstraint> for TableConstraint {
110 fn from(constraint: UniqueConstraint) -> Self {
111 TableConstraint::Unique(constraint)
112 }
113}
114
115impl From<PrimaryKeyConstraint> for TableConstraint {
116 fn from(constraint: PrimaryKeyConstraint) -> Self {
117 TableConstraint::PrimaryKey(constraint)
118 }
119}
120
121impl From<ForeignKeyConstraint> for TableConstraint {
122 fn from(constraint: ForeignKeyConstraint) -> Self {
123 TableConstraint::ForeignKey(constraint)
124 }
125}
126
127impl From<CheckConstraint> for TableConstraint {
128 fn from(constraint: CheckConstraint) -> Self {
129 TableConstraint::Check(constraint)
130 }
131}
132
133impl From<IndexConstraint> for TableConstraint {
134 fn from(constraint: IndexConstraint) -> Self {
135 TableConstraint::Index(constraint)
136 }
137}
138
139impl From<FullTextOrSpatialConstraint> for TableConstraint {
140 fn from(constraint: FullTextOrSpatialConstraint) -> Self {
141 TableConstraint::FulltextOrSpatial(constraint)
142 }
143}
144
145impl From<ExclusionConstraint> for TableConstraint {
146 fn from(constraint: ExclusionConstraint) -> Self {
147 TableConstraint::Exclusion(constraint)
148 }
149}
150
151impl fmt::Display for TableConstraint {
152 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
153 match self {
154 TableConstraint::Unique(constraint) => constraint.fmt(f),
155 TableConstraint::PrimaryKey(constraint) => constraint.fmt(f),
156 TableConstraint::ForeignKey(constraint) => constraint.fmt(f),
157 TableConstraint::Check(constraint) => constraint.fmt(f),
158 TableConstraint::Index(constraint) => constraint.fmt(f),
159 TableConstraint::FulltextOrSpatial(constraint) => constraint.fmt(f),
160 TableConstraint::Exclusion(constraint) => constraint.fmt(f),
161 }
162 }
163}
164
165#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
166#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
167#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
168pub struct CheckConstraint {
169 pub name: Option<Ident>,
170 pub expr: Box<Expr>,
171 pub enforced: Option<bool>,
174}
175
176impl fmt::Display for CheckConstraint {
177 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178 use crate::ast::ddl::display_constraint_name;
179 write!(
180 f,
181 "{}CHECK ({})",
182 display_constraint_name(&self.name),
183 self.expr
184 )?;
185 if let Some(b) = self.enforced {
186 write!(f, " {}", if b { "ENFORCED" } else { "NOT ENFORCED" })
187 } else {
188 Ok(())
189 }
190 }
191}
192
193impl crate::ast::Spanned for CheckConstraint {
194 fn span(&self) -> Span {
195 self.expr
196 .span()
197 .union_opt(&self.name.as_ref().map(|i| i.span))
198 }
199}
200
201#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
207#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
208#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
209pub struct ForeignKeyConstraint {
210 pub name: Option<Ident>,
211 pub index_name: Option<Ident>,
214 pub columns: Vec<Ident>,
215 pub foreign_table: ObjectName,
216 pub referred_columns: Vec<Ident>,
217 pub on_delete: Option<ReferentialAction>,
218 pub on_update: Option<ReferentialAction>,
219 pub match_kind: Option<ConstraintReferenceMatchKind>,
220 pub characteristics: Option<ConstraintCharacteristics>,
221}
222
223impl fmt::Display for ForeignKeyConstraint {
224 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
225 use crate::ast::ddl::{display_constraint_name, display_option_spaced};
226 write!(
227 f,
228 "{}FOREIGN KEY{} ({}) REFERENCES {}",
229 display_constraint_name(&self.name),
230 display_option_spaced(&self.index_name),
231 display_comma_separated(&self.columns),
232 self.foreign_table,
233 )?;
234 if !self.referred_columns.is_empty() {
235 write!(f, "({})", display_comma_separated(&self.referred_columns))?;
236 }
237 if let Some(match_kind) = &self.match_kind {
238 write!(f, " {match_kind}")?;
239 }
240 if let Some(action) = &self.on_delete {
241 write!(f, " ON DELETE {action}")?;
242 }
243 if let Some(action) = &self.on_update {
244 write!(f, " ON UPDATE {action}")?;
245 }
246 if let Some(characteristics) = &self.characteristics {
247 write!(f, " {characteristics}")?;
248 }
249 Ok(())
250 }
251}
252
253impl crate::ast::Spanned for ForeignKeyConstraint {
254 fn span(&self) -> Span {
255 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
256 Span::union_iter(iter)
257 }
258
259 union_spans(
260 self.name
261 .iter()
262 .map(|i| i.span)
263 .chain(self.index_name.iter().map(|i| i.span))
264 .chain(self.columns.iter().map(|i| i.span))
265 .chain(core::iter::once(self.foreign_table.span()))
266 .chain(self.referred_columns.iter().map(|i| i.span))
267 .chain(self.on_delete.iter().map(|i| i.span()))
268 .chain(self.on_update.iter().map(|i| i.span()))
269 .chain(self.characteristics.iter().map(|i| i.span())),
270 )
271 }
272}
273
274#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
288#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
289#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
290pub struct FullTextOrSpatialConstraint {
291 pub fulltext: bool,
293 pub index_type_display: KeyOrIndexDisplay,
295 pub opt_index_name: Option<Ident>,
297 pub columns: Vec<IndexColumn>,
299}
300
301impl fmt::Display for FullTextOrSpatialConstraint {
302 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
303 if self.fulltext {
304 write!(f, "FULLTEXT")?;
305 } else {
306 write!(f, "SPATIAL")?;
307 }
308
309 write!(f, "{:>}", self.index_type_display)?;
310
311 if let Some(name) = &self.opt_index_name {
312 write!(f, " {name}")?;
313 }
314
315 write!(f, " ({})", display_comma_separated(&self.columns))?;
316
317 Ok(())
318 }
319}
320
321impl crate::ast::Spanned for FullTextOrSpatialConstraint {
322 fn span(&self) -> Span {
323 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
324 Span::union_iter(iter)
325 }
326
327 union_spans(
328 self.opt_index_name
329 .iter()
330 .map(|i| i.span)
331 .chain(self.columns.iter().map(|i| i.span())),
332 )
333 }
334}
335
336#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
343#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
344#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
345pub struct IndexConstraint {
346 pub display_as_key: bool,
348 pub name: Option<Ident>,
350 pub index_type: Option<IndexType>,
354 pub columns: Vec<IndexColumn>,
356 pub index_options: Vec<IndexOption>,
358}
359
360impl fmt::Display for IndexConstraint {
361 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
362 write!(f, "{}", if self.display_as_key { "KEY" } else { "INDEX" })?;
363 if let Some(name) = &self.name {
364 write!(f, " {name}")?;
365 }
366 if let Some(index_type) = &self.index_type {
367 write!(f, " USING {index_type}")?;
368 }
369 write!(f, " ({})", display_comma_separated(&self.columns))?;
370 if !self.index_options.is_empty() {
371 write!(f, " {}", display_comma_separated(&self.index_options))?;
372 }
373 Ok(())
374 }
375}
376
377impl crate::ast::Spanned for IndexConstraint {
378 fn span(&self) -> Span {
379 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
380 Span::union_iter(iter)
381 }
382
383 union_spans(
384 self.name
385 .iter()
386 .map(|i| i.span)
387 .chain(self.columns.iter().map(|i| i.span())),
388 )
389 }
390}
391
392#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
411#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
412#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
413pub struct PrimaryKeyConstraint {
414 pub name: Option<Ident>,
418 pub index_name: Option<Ident>,
420 pub index_type: Option<IndexType>,
424 pub columns: Vec<IndexColumn>,
426 pub index_options: Vec<IndexOption>,
427 pub characteristics: Option<ConstraintCharacteristics>,
428}
429
430impl fmt::Display for PrimaryKeyConstraint {
431 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
432 use crate::ast::ddl::{display_constraint_name, display_option, display_option_spaced};
433 write!(
434 f,
435 "{}PRIMARY KEY{}{} ({})",
436 display_constraint_name(&self.name),
437 display_option_spaced(&self.index_name),
438 display_option(" USING ", "", &self.index_type),
439 display_comma_separated(&self.columns),
440 )?;
441
442 if !self.index_options.is_empty() {
443 write!(f, " {}", display_separated(&self.index_options, " "))?;
444 }
445
446 write!(f, "{}", display_option_spaced(&self.characteristics))?;
447 Ok(())
448 }
449}
450
451impl crate::ast::Spanned for PrimaryKeyConstraint {
452 fn span(&self) -> Span {
453 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
454 Span::union_iter(iter)
455 }
456
457 union_spans(
458 self.name
459 .iter()
460 .map(|i| i.span)
461 .chain(self.index_name.iter().map(|i| i.span))
462 .chain(self.columns.iter().map(|i| i.span()))
463 .chain(self.characteristics.iter().map(|i| i.span())),
464 )
465 }
466}
467
468#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
469#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
470#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
471pub struct UniqueConstraint {
472 pub name: Option<Ident>,
476 pub index_name: Option<Ident>,
478 pub index_type_display: KeyOrIndexDisplay,
480 pub index_type: Option<IndexType>,
484 pub columns: Vec<IndexColumn>,
486 pub index_options: Vec<IndexOption>,
487 pub characteristics: Option<ConstraintCharacteristics>,
488 pub nulls_distinct: NullsDistinctOption,
490}
491
492impl fmt::Display for UniqueConstraint {
493 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
494 use crate::ast::ddl::{display_constraint_name, display_option, display_option_spaced};
495 write!(
496 f,
497 "{}UNIQUE{}{:>}{}{} ({})",
498 display_constraint_name(&self.name),
499 self.nulls_distinct,
500 self.index_type_display,
501 display_option_spaced(&self.index_name),
502 display_option(" USING ", "", &self.index_type),
503 display_comma_separated(&self.columns),
504 )?;
505
506 if !self.index_options.is_empty() {
507 write!(f, " {}", display_separated(&self.index_options, " "))?;
508 }
509
510 write!(f, "{}", display_option_spaced(&self.characteristics))?;
511 Ok(())
512 }
513}
514
515impl crate::ast::Spanned for UniqueConstraint {
516 fn span(&self) -> Span {
517 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
518 Span::union_iter(iter)
519 }
520
521 union_spans(
522 self.name
523 .iter()
524 .map(|i| i.span)
525 .chain(self.index_name.iter().map(|i| i.span))
526 .chain(self.columns.iter().map(|i| i.span()))
527 .chain(self.characteristics.iter().map(|i| i.span())),
528 )
529 }
530}
531
532#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
535#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
536#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
537pub struct ExclusionElement {
538 pub expr: Expr,
539 pub operator: String,
540}
541
542impl fmt::Display for ExclusionElement {
543 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
544 write!(f, "{} WITH {}", self.expr, self.operator)
545 }
546}
547
548#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
551#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
552#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
553pub struct ExclusionConstraint {
554 pub name: Option<Ident>,
555 pub index_method: Option<Ident>,
556 pub elements: Vec<ExclusionElement>,
557 pub include: Vec<Ident>,
558 pub where_clause: Option<Box<Expr>>,
559 pub characteristics: Option<ConstraintCharacteristics>,
560}
561
562impl fmt::Display for ExclusionConstraint {
563 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
564 use crate::ast::ddl::display_constraint_name;
565 write!(f, "{}EXCLUDE", display_constraint_name(&self.name))?;
566 if let Some(method) = &self.index_method {
567 write!(f, " USING {method}")?;
568 }
569 write!(f, " ({})", display_comma_separated(&self.elements))?;
570 if !self.include.is_empty() {
571 write!(f, " INCLUDE ({})", display_comma_separated(&self.include))?;
572 }
573 if let Some(predicate) = &self.where_clause {
574 write!(f, " WHERE ({predicate})")?;
575 }
576 if let Some(characteristics) = &self.characteristics {
577 write!(f, " {characteristics}")?;
578 }
579 Ok(())
580 }
581}
582
583impl crate::ast::Spanned for ExclusionConstraint {
584 fn span(&self) -> Span {
585 fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
586 Span::union_iter(iter)
587 }
588
589 union_spans(
590 self.name
591 .iter()
592 .map(|i| i.span)
593 .chain(self.index_method.iter().map(|i| i.span))
594 .chain(self.include.iter().map(|i| i.span))
595 .chain(self.where_clause.iter().map(|e| e.span()))
596 .chain(self.characteristics.iter().map(|c| c.span())),
597 )
598 }
599}