Skip to content

Commit 3077fc5

Browse files
committed
add check constraint
1 parent 9dcc3d6 commit 3077fc5

File tree

3 files changed

+119
-12
lines changed

3 files changed

+119
-12
lines changed

testing/differential-oracle/sql_gen/src/ast.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,7 @@ pub struct ColumnDefStmt {
642642
pub not_null: bool,
643643
pub unique: bool,
644644
pub default: Option<Expr>,
645+
pub check: Option<Expr>,
645646
}
646647

647648
impl fmt::Display for ColumnDefStmt {
@@ -664,6 +665,10 @@ impl fmt::Display for ColumnDefStmt {
664665
write!(f, " DEFAULT ({default})")?;
665666
}
666667

668+
if let Some(check) = &self.check {
669+
write!(f, " CHECK ({check})")?;
670+
}
671+
667672
Ok(())
668673
}
669674
}

testing/differential-oracle/sql_gen/src/generate/stmt.rs

Lines changed: 111 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
33
use crate::SqlGen;
44
use crate::ast::{
5-
AlterTableAction, AlterTableActionKind, AlterTableStmt, ColumnDefStmt, ConflictClause,
5+
AlterTableAction, AlterTableActionKind, AlterTableStmt, BinOp, ColumnDefStmt, ConflictClause,
66
CreateIndexStmt, CreateTableStmt, CreateTriggerStmt, DeleteStmt, DropIndexStmt, DropTableStmt,
7-
DropTriggerStmt, Expr, InsertStmt, Stmt, TriggerBodyStmtKind, TriggerEvent, TriggerEventKind,
8-
TriggerStmt, TriggerTiming, UpdateStmt,
7+
DropTriggerStmt, Expr, InsertStmt, Literal, Stmt, TriggerBodyStmtKind, TriggerEvent,
8+
TriggerEventKind, TriggerStmt, TriggerTiming, UpdateStmt,
99
};
1010
use crate::capabilities::Capabilities;
1111
use crate::context::Context;
@@ -567,6 +567,7 @@ pub fn generate_create_table<C: Capabilities>(
567567
not_null: true,
568568
unique: false,
569569
default: None,
570+
check: None,
570571
});
571572

572573
// Generate additional columns
@@ -589,13 +590,20 @@ pub fn generate_create_table<C: Capabilities>(
589590
let name = ctx.gen_unique_name("col", &col_names);
590591
col_names.insert(name.clone());
591592

593+
let check = if ctx.gen_bool_with_prob(create_table_config.check_constraint_probability) {
594+
generate_check_constraint(generator, ctx, &name, data_type)?
595+
} else {
596+
None
597+
};
598+
592599
columns.push(ColumnDefStmt {
593600
name,
594601
data_type,
595602
primary_key: false,
596603
not_null,
597604
unique,
598605
default,
606+
check,
599607
});
600608
}
601609

@@ -605,9 +613,6 @@ pub fn generate_create_table<C: Capabilities>(
605613
}
606614

607615
// --- Table-level constraints (not yet implemented) ---
608-
if ctx.gen_bool_with_prob(create_table_config.check_constraint_probability) {
609-
let _ = generate_check_constraint(generator, ctx);
610-
}
611616
if ctx.gen_bool_with_prob(create_table_config.foreign_key_probability) {
612617
let _ = generate_foreign_key(generator, ctx);
613618
}
@@ -730,6 +735,7 @@ fn generate_alter_table_action<C: Capabilities>(
730735
not_null,
731736
unique: false,
732737
default: None,
738+
check: None,
733739
}))
734740
}
735741
AlterTableActionKind::DropColumn => {
@@ -1096,12 +1102,108 @@ fn generate_delete_returning<C: Capabilities>(
10961102

10971103
// ---- CREATE TABLE features ----
10981104

1105+
/// Generate a CHECK constraint expression for a column, based on its data type.
1106+
/// Mirrors the approach from sql_gen_prop: simple, deterministic, type-appropriate constraints.
10991107
#[trace_gen(Origin::CheckConstraint)]
11001108
fn generate_check_constraint<C: Capabilities>(
11011109
_generator: &SqlGen<C>,
1102-
_ctx: &mut Context,
1103-
) -> Result<Expr, GenError> {
1104-
todo!("CHECK constraint generation")
1110+
ctx: &mut Context,
1111+
col_name: &str,
1112+
data_type: DataType,
1113+
) -> Result<Option<Expr>, GenError> {
1114+
match data_type {
1115+
DataType::Integer => {
1116+
let variant = ctx.gen_range(5);
1117+
let expr = match variant {
1118+
// col >= 0
1119+
0 => {
1120+
let col = Expr::column_ref(ctx, None, col_name.to_string());
1121+
let rhs = Expr::literal(ctx, Literal::Integer(0));
1122+
Expr::binary_op(ctx, col, BinOp::Ge, rhs)
1123+
}
1124+
// col > 0
1125+
1 => {
1126+
let col = Expr::column_ref(ctx, None, col_name.to_string());
1127+
let rhs = Expr::literal(ctx, Literal::Integer(0));
1128+
Expr::binary_op(ctx, col, BinOp::Gt, rhs)
1129+
}
1130+
// col BETWEEN 0 AND 1000
1131+
2 => {
1132+
let col = Expr::column_ref(ctx, None, col_name.to_string());
1133+
let low = Expr::literal(ctx, Literal::Integer(0));
1134+
let high = Expr::literal(ctx, Literal::Integer(1000));
1135+
Expr::between(ctx, col, low, high, false)
1136+
}
1137+
// col != 0
1138+
3 => {
1139+
let col = Expr::column_ref(ctx, None, col_name.to_string());
1140+
let rhs = Expr::literal(ctx, Literal::Integer(0));
1141+
Expr::binary_op(ctx, col, BinOp::Ne, rhs)
1142+
}
1143+
// col >= N (random N in 1..100)
1144+
_ => {
1145+
let n = ctx.gen_range_inclusive(1, 99) as i64;
1146+
let col = Expr::column_ref(ctx, None, col_name.to_string());
1147+
let rhs = Expr::literal(ctx, Literal::Integer(n));
1148+
Expr::binary_op(ctx, col, BinOp::Ge, rhs)
1149+
}
1150+
};
1151+
Ok(Some(expr))
1152+
}
1153+
DataType::Real => {
1154+
let variant = ctx.gen_range(3);
1155+
let expr = match variant {
1156+
// col >= 0.0
1157+
0 => {
1158+
let col = Expr::column_ref(ctx, None, col_name.to_string());
1159+
let rhs = Expr::literal(ctx, Literal::Real(0.0));
1160+
Expr::binary_op(ctx, col, BinOp::Ge, rhs)
1161+
}
1162+
// col > 0.0
1163+
1 => {
1164+
let col = Expr::column_ref(ctx, None, col_name.to_string());
1165+
let rhs = Expr::literal(ctx, Literal::Real(0.0));
1166+
Expr::binary_op(ctx, col, BinOp::Gt, rhs)
1167+
}
1168+
// col BETWEEN 0.0 AND 1000.0
1169+
_ => {
1170+
let col = Expr::column_ref(ctx, None, col_name.to_string());
1171+
let low = Expr::literal(ctx, Literal::Real(0.0));
1172+
let high = Expr::literal(ctx, Literal::Real(1000.0));
1173+
Expr::between(ctx, col, low, high, false)
1174+
}
1175+
};
1176+
Ok(Some(expr))
1177+
}
1178+
DataType::Text => {
1179+
let variant = ctx.gen_range(3);
1180+
let expr = match variant {
1181+
// length(col) > 0
1182+
0 => {
1183+
let col = Expr::column_ref(ctx, None, col_name.to_string());
1184+
let len = Expr::function_call(ctx, "length".to_string(), vec![col]);
1185+
let rhs = Expr::literal(ctx, Literal::Integer(0));
1186+
Expr::binary_op(ctx, len, BinOp::Gt, rhs)
1187+
}
1188+
// length(col) <= 100
1189+
1 => {
1190+
let col = Expr::column_ref(ctx, None, col_name.to_string());
1191+
let len = Expr::function_call(ctx, "length".to_string(), vec![col]);
1192+
let rhs = Expr::literal(ctx, Literal::Integer(100));
1193+
Expr::binary_op(ctx, len, BinOp::Le, rhs)
1194+
}
1195+
// col != ''
1196+
_ => {
1197+
let col = Expr::column_ref(ctx, None, col_name.to_string());
1198+
let rhs = Expr::literal(ctx, Literal::Text(String::new()));
1199+
Expr::binary_op(ctx, col, BinOp::Ne, rhs)
1200+
}
1201+
};
1202+
Ok(Some(expr))
1203+
}
1204+
// Blob and Null types don't have useful check constraints
1205+
DataType::Blob | DataType::Null => Ok(None),
1206+
}
11051207
}
11061208

11071209
#[trace_gen(Origin::ForeignKey)]

testing/differential-oracle/sql_gen/src/policy.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,10 +1420,10 @@ pub struct CreateTableConfig {
14201420
/// Probability of IF NOT EXISTS clause.
14211421
pub if_not_exists_probability: f64,
14221422

1423-
// Stubs (not yet implemented, probability 0.0)
1424-
/// Probability of CHECK constraint.
1423+
/// Probability of CHECK constraint on a column.
14251424
pub check_constraint_probability: f64,
14261425

1426+
// Stubs (not yet implemented, probability 0.0)
14271427
/// Probability of FOREIGN KEY.
14281428
pub foreign_key_probability: f64,
14291429

@@ -1446,8 +1446,8 @@ impl Default for CreateTableConfig {
14461446
unique_probability: 0.15,
14471447
default_probability: 0.2,
14481448
if_not_exists_probability: 0.5,
1449+
check_constraint_probability: 0.15,
14491450
// Stubs
1450-
check_constraint_probability: 0.0,
14511451
foreign_key_probability: 0.0,
14521452
autoincrement_probability: 0.0,
14531453
generated_column_probability: 0.0,

0 commit comments

Comments
 (0)