Skip to content

Commit 5d82f2e

Browse files
authored
Supports specifying the name of check constraints (#920)
1 parent 9ecac6b commit 5d82f2e

File tree

10 files changed

+198
-14
lines changed

10 files changed

+198
-14
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ assert_eq!(std::mem::size_of::<Value>(), 32);
3838

3939
### Breaking Changes
4040

41+
* Replace `ColumnSpec::Check(Expr)` with `ColumnSpec::Check(Check)` to support named check constraints
42+
4143
* Removed inherent `SimpleExpr` methods that duplicate `ExprTrait`. If you encounter the following error, please add `use sea_query::ExprTrait` in scope https://github.com/SeaQL/sea-query/pull/890
4244
```rust
4345
error[E0599]: no method named `like` found for enum `sea_query::Expr` in the current scope
@@ -138,8 +140,6 @@ impl Iden for Glyph {
138140
If you had custom implementations in your own code, some may no longer compile
139141
and may need to be deleted.
140142

141-
### Bug Fixes
142-
143143
### Upgrades
144144

145145
* Upgraded to Rust Edition 2024 https://github.com/SeaQL/sea-query/pull/885

src/backend/table_builder.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,14 @@ pub trait TableBuilder:
194194
}
195195

196196
/// Translate the check constraint into SQL statement
197-
fn prepare_check_constraint(&self, check: &Expr, sql: &mut dyn SqlWriter) {
197+
fn prepare_check_constraint(&self, check: &Check, sql: &mut dyn SqlWriter) {
198+
if let Some(name) = &check.name {
199+
write!(sql, "CONSTRAINT ",).unwrap();
200+
self.prepare_iden(name, sql);
201+
write!(sql, " ",).unwrap();
202+
}
198203
write!(sql, "CHECK (").unwrap();
199-
QueryBuilder::prepare_simple_expr(self, check, sql);
204+
QueryBuilder::prepare_simple_expr(self, &check.expr, sql);
200205
write!(sql, ")").unwrap();
201206
}
202207

src/table/column.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::fmt;
22

3-
use crate::{expr::*, types::*};
3+
use crate::{expr::*, table::Check, types::*};
44

55
/// Specification of a table column
66
#[derive(Debug, Clone)]
@@ -179,7 +179,7 @@ pub enum ColumnSpec {
179179
AutoIncrement,
180180
UniqueKey,
181181
PrimaryKey,
182-
Check(Expr),
182+
Check(Check),
183183
Generated { expr: Expr, stored: bool },
184184
Extra(String),
185185
Comment(String),
@@ -687,6 +687,7 @@ impl ColumnDef {
687687
///
688688
/// ```
689689
/// use sea_query::{tests_cfg::*, *};
690+
///
690691
/// assert_eq!(
691692
/// Table::create()
692693
/// .table(Glyph::Table)
@@ -699,12 +700,25 @@ impl ColumnDef {
699700
/// .to_string(MysqlQueryBuilder),
700701
/// r#"CREATE TABLE `glyph` ( `id` int NOT NULL CHECK (`id` > 10) )"#,
701702
/// );
703+
///
704+
/// assert_eq!(
705+
/// Table::create()
706+
/// .table(Glyph::Table)
707+
/// .col(
708+
/// ColumnDef::new(Glyph::Id)
709+
/// .integer()
710+
/// .not_null()
711+
/// .check(("positive_id", Expr::col(Glyph::Id).gt(10)))
712+
/// )
713+
/// .to_string(MysqlQueryBuilder),
714+
/// r#"CREATE TABLE `glyph` ( `id` int NOT NULL CONSTRAINT `positive_id` CHECK (`id` > 10) )"#,
715+
/// );
702716
/// ```
703-
pub fn check<T>(&mut self, value: T) -> &mut Self
717+
pub fn check<T>(&mut self, check: T) -> &mut Self
704718
where
705-
T: Into<Expr>,
719+
T: Into<Check>,
706720
{
707-
self.spec.push(ColumnSpec::Check(value.into()));
721+
self.spec.push(ColumnSpec::Check(check.into()));
708722
self
709723
}
710724

src/table/constraint.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use crate::{DynIden, Expr};
2+
3+
#[derive(Debug, Clone)]
4+
pub struct Check {
5+
pub(crate) name: Option<DynIden>,
6+
pub(crate) expr: Expr,
7+
}
8+
9+
impl Check {
10+
pub fn named<N, E>(name: N, expr: E) -> Self
11+
where
12+
N: Into<DynIden>,
13+
E: Into<Expr>,
14+
{
15+
Self {
16+
name: Some(name.into()),
17+
expr: expr.into(),
18+
}
19+
}
20+
21+
pub fn unnamed<E>(expr: E) -> Self
22+
where
23+
E: Into<Expr>,
24+
{
25+
Self {
26+
name: None,
27+
expr: expr.into(),
28+
}
29+
}
30+
}
31+
32+
impl<E> From<E> for Check
33+
where
34+
E: Into<Expr>,
35+
{
36+
fn from(expr: E) -> Self {
37+
Self::unnamed(expr)
38+
}
39+
}
40+
41+
impl<I, E> From<(I, E)> for Check
42+
where
43+
I: Into<DynIden>,
44+
E: Into<Expr>,
45+
{
46+
fn from((name, expr): (I, E)) -> Self {
47+
Self::named(name, expr)
48+
}
49+
}

src/table/create.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use inherent::inherent;
22

33
use crate::{
4-
ColumnDef, Expr, IntoColumnDef, SchemaStatementBuilder, backend::SchemaBuilder, foreign_key::*,
5-
index::*, types::*,
4+
ColumnDef, IntoColumnDef, SchemaStatementBuilder, backend::SchemaBuilder, foreign_key::*,
5+
index::*, table::constraint::Check, types::*,
66
};
77

88
/// Create a table
@@ -88,7 +88,7 @@ pub struct TableCreateStatement {
8888
pub(crate) indexes: Vec<IndexCreateStatement>,
8989
pub(crate) foreign_keys: Vec<ForeignKeyCreateStatement>,
9090
pub(crate) if_not_exists: bool,
91-
pub(crate) check: Vec<Expr>,
91+
pub(crate) check: Vec<Check>,
9292
pub(crate) comment: Option<String>,
9393
pub(crate) extra: Option<String>,
9494
pub(crate) temporary: bool,
@@ -146,8 +146,11 @@ impl TableCreateStatement {
146146
self
147147
}
148148

149-
pub fn check(&mut self, value: Expr) -> &mut Self {
150-
self.check.push(value);
149+
pub fn check<T>(&mut self, value: T) -> &mut Self
150+
where
151+
T: Into<Check>,
152+
{
153+
self.check.push(value.into());
151154
self
152155
}
153156

src/table/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ use crate::SchemaBuilder;
1212

1313
mod alter;
1414
mod column;
15+
mod constraint;
1516
mod create;
1617
mod drop;
1718
mod rename;
1819
mod truncate;
1920

2021
pub use alter::*;
2122
pub use column::*;
23+
pub use constraint::*;
2224
pub use create::*;
2325
pub use drop::*;
2426
pub use rename::*;

src/types.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,12 @@ impl std::fmt::Display for DynIden {
118118
}
119119
}
120120

121+
impl From<&'static str> for DynIden {
122+
fn from(s: &'static str) -> Self {
123+
Self(Cow::Borrowed(s))
124+
}
125+
}
126+
121127
pub trait IntoIden {
122128
fn into_iden(self) -> DynIden;
123129
}

tests/mysql/table.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,24 @@ fn create_with_check_constraint() {
430430
);
431431
}
432432

433+
#[test]
434+
fn create_with_named_check_constraint() {
435+
assert_eq!(
436+
Table::create()
437+
.table(Glyph::Table)
438+
.col(
439+
ColumnDef::new(Glyph::Id)
440+
.integer()
441+
.not_null()
442+
.check(("positive_id", Expr::col(Glyph::Id).gt(10)))
443+
)
444+
.check(("id_range", Expr::col(Glyph::Id).lt(20)))
445+
.check(Expr::col(Glyph::Id).ne(15))
446+
.to_string(MysqlQueryBuilder),
447+
r"CREATE TABLE `glyph` ( `id` int NOT NULL CONSTRAINT `positive_id` CHECK (`id` > 10), CONSTRAINT `id_range` CHECK (`id` < 20), CHECK (`id` <> 15) )",
448+
);
449+
}
450+
433451
#[test]
434452
fn alter_with_check_constraint() {
435453
assert_eq!(
@@ -446,3 +464,20 @@ fn alter_with_check_constraint() {
446464
r"ALTER TABLE `glyph` ADD COLUMN `aspect` int NOT NULL DEFAULT 101 CHECK (`aspect` > 100)",
447465
);
448466
}
467+
468+
#[test]
469+
fn alter_with_named_check_constraint() {
470+
assert_eq!(
471+
Table::alter()
472+
.table(Glyph::Table)
473+
.add_column(
474+
ColumnDef::new(Glyph::Aspect)
475+
.integer()
476+
.not_null()
477+
.default(101)
478+
.check(("positive_aspect", Expr::col(Glyph::Aspect).gt(100)))
479+
)
480+
.to_string(MysqlQueryBuilder),
481+
r#"ALTER TABLE `glyph` ADD COLUMN `aspect` int NOT NULL DEFAULT 101 CONSTRAINT `positive_aspect` CHECK (`aspect` > 100)"#,
482+
);
483+
}

tests/postgres/table.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,24 @@ fn create_with_check_constraint() {
559559
);
560560
}
561561

562+
#[test]
563+
fn create_with_named_check_constraint() {
564+
assert_eq!(
565+
Table::create()
566+
.table(Glyph::Table)
567+
.col(
568+
ColumnDef::new(Glyph::Id)
569+
.integer()
570+
.not_null()
571+
.check(("positive_id", Expr::col(Glyph::Id).gt(10)))
572+
)
573+
.check(("id_range", Expr::col(Glyph::Id).lt(20)))
574+
.check(Expr::col(Glyph::Id).ne(15))
575+
.to_string(PostgresQueryBuilder),
576+
r#"CREATE TABLE "glyph" ( "id" integer NOT NULL CONSTRAINT "positive_id" CHECK ("id" > 10), CONSTRAINT "id_range" CHECK ("id" < 20), CHECK ("id" <> 15) )"#,
577+
);
578+
}
579+
562580
#[test]
563581
fn alter_with_check_constraint() {
564582
assert_eq!(
@@ -576,6 +594,23 @@ fn alter_with_check_constraint() {
576594
);
577595
}
578596

597+
#[test]
598+
fn alter_with_named_check_constraint() {
599+
assert_eq!(
600+
Table::alter()
601+
.table(Glyph::Table)
602+
.add_column(
603+
ColumnDef::new(Glyph::Aspect)
604+
.integer()
605+
.not_null()
606+
.default(101)
607+
.check(("positive_aspect", Expr::col(Glyph::Aspect).gt(100)))
608+
)
609+
.to_string(PostgresQueryBuilder),
610+
r#"ALTER TABLE "glyph" ADD COLUMN "aspect" integer NOT NULL DEFAULT 101 CONSTRAINT "positive_aspect" CHECK ("aspect" > 100)"#,
611+
);
612+
}
613+
579614
#[test]
580615
fn create_16() {
581616
assert_eq!(

tests/sqlite/table.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,24 @@ fn create_with_check_constraint() {
464464
);
465465
}
466466

467+
#[test]
468+
fn create_with_named_check_constraint() {
469+
assert_eq!(
470+
Table::create()
471+
.table(Glyph::Table)
472+
.col(
473+
ColumnDef::new(Glyph::Id)
474+
.integer()
475+
.not_null()
476+
.check(("positive_id", Expr::col(Glyph::Id).gt(10)))
477+
)
478+
.check(("id_range", Expr::col(Glyph::Id).lt(20)))
479+
.check(Expr::col(Glyph::Id).ne(15))
480+
.to_string(SqliteQueryBuilder),
481+
r#"CREATE TABLE "glyph" ( "id" integer NOT NULL CONSTRAINT "positive_id" CHECK ("id" > 10), CONSTRAINT "id_range" CHECK ("id" < 20), CHECK ("id" <> 15) )"#,
482+
);
483+
}
484+
467485
#[test]
468486
fn alter_with_check_constraint() {
469487
assert_eq!(
@@ -480,3 +498,20 @@ fn alter_with_check_constraint() {
480498
r#"ALTER TABLE "glyph" ADD COLUMN "aspect" integer NOT NULL DEFAULT 101 CHECK ("aspect" > 100)"#,
481499
);
482500
}
501+
502+
#[test]
503+
fn alter_with_named_check_constraint() {
504+
assert_eq!(
505+
Table::alter()
506+
.table(Glyph::Table)
507+
.add_column(
508+
ColumnDef::new(Glyph::Aspect)
509+
.integer()
510+
.not_null()
511+
.default(101)
512+
.check(("positive_aspect", Expr::col(Glyph::Aspect).gt(100)))
513+
)
514+
.to_string(SqliteQueryBuilder),
515+
r#"ALTER TABLE "glyph" ADD COLUMN "aspect" integer NOT NULL DEFAULT 101 CONSTRAINT "positive_aspect" CHECK ("aspect" > 100)"#,
516+
);
517+
}

0 commit comments

Comments
 (0)