use vibesql_ast::{ColumnDef, CreateTableStmt, TruncateTableStmt};
use vibesql_storage::{Database, Row};
use vibesql_types::{DataType, SqlValue};
use crate::{CreateTableExecutor, TruncateTableExecutor};
#[test]
fn test_truncate_single_table() {
let mut db = Database::new();
let create_stmt = CreateTableStmt {
temporary: false,
if_not_exists: false,
table_name: "USERS".to_string(),
columns: vec![
ColumnDef {
name: "ID".to_string(),
data_type: DataType::Integer,
nullable: false,
constraints: vec![],
default_value: None,
comment: None,
generated_expr: None,
is_exact_integer_type: false,
},
ColumnDef {
name: "NAME".to_string(),
data_type: DataType::Varchar { max_length: Some(100) },
nullable: true,
constraints: vec![],
default_value: None,
comment: None,
generated_expr: None,
is_exact_integer_type: false,
},
],
table_constraints: vec![],
table_options: vec![],
quoted: false,
name_source: None,
as_query: None,
without_rowid: false,
};
CreateTableExecutor::execute(&create_stmt, &mut db).unwrap();
db.insert_row(
"USERS",
Row::new(vec![SqlValue::Integer(1), SqlValue::Varchar(arcstr::ArcStr::from("Alice"))]),
)
.unwrap();
db.insert_row(
"USERS",
Row::new(vec![SqlValue::Integer(2), SqlValue::Varchar(arcstr::ArcStr::from("Bob"))]),
)
.unwrap();
db.insert_row(
"USERS",
Row::new(vec![SqlValue::Integer(3), SqlValue::Varchar(arcstr::ArcStr::from("Carol"))]),
)
.unwrap();
assert_eq!(db.get_table("USERS").unwrap().row_count(), 3);
let truncate_stmt = TruncateTableStmt {
table_names: vec!["USERS".to_string()],
if_exists: false,
cascade: None,
};
let rows_deleted = TruncateTableExecutor::execute(&truncate_stmt, &mut db).unwrap();
assert_eq!(rows_deleted, 3);
assert_eq!(db.get_table("USERS").unwrap().row_count(), 0);
}
#[test]
fn test_truncate_multiple_tables() {
let mut db = Database::new();
let create_stmt1 = CreateTableStmt {
temporary: false,
if_not_exists: false,
table_name: "ORDERS".to_string(),
columns: vec![ColumnDef {
name: "ID".to_string(),
data_type: DataType::Integer,
nullable: false,
constraints: vec![],
default_value: None,
comment: None,
generated_expr: None,
is_exact_integer_type: false,
}],
table_constraints: vec![],
table_options: vec![],
quoted: false,
name_source: None,
as_query: None,
without_rowid: false,
};
CreateTableExecutor::execute(&create_stmt1, &mut db).unwrap();
let create_stmt2 = CreateTableStmt {
temporary: false,
if_not_exists: false,
table_name: "ORDER_ITEMS".to_string(),
columns: vec![ColumnDef {
name: "ID".to_string(),
data_type: DataType::Integer,
nullable: false,
constraints: vec![],
default_value: None,
comment: None,
generated_expr: None,
is_exact_integer_type: false,
}],
table_constraints: vec![],
table_options: vec![],
quoted: false,
name_source: None,
as_query: None,
without_rowid: false,
};
CreateTableExecutor::execute(&create_stmt2, &mut db).unwrap();
let create_stmt3 = CreateTableStmt {
temporary: false,
if_not_exists: false,
table_name: "ORDER_HISTORY".to_string(),
columns: vec![ColumnDef {
name: "ID".to_string(),
data_type: DataType::Integer,
nullable: false,
constraints: vec![],
default_value: None,
comment: None,
generated_expr: None,
is_exact_integer_type: false,
}],
table_constraints: vec![],
table_options: vec![],
quoted: false,
name_source: None,
as_query: None,
without_rowid: false,
};
CreateTableExecutor::execute(&create_stmt3, &mut db).unwrap();
db.insert_row("ORDERS", Row::new(vec![SqlValue::Integer(1)])).unwrap();
db.insert_row("ORDERS", Row::new(vec![SqlValue::Integer(2)])).unwrap();
db.insert_row("ORDER_ITEMS", Row::new(vec![SqlValue::Integer(10)])).unwrap();
db.insert_row("ORDER_ITEMS", Row::new(vec![SqlValue::Integer(20)])).unwrap();
db.insert_row("ORDER_ITEMS", Row::new(vec![SqlValue::Integer(30)])).unwrap();
db.insert_row("ORDER_HISTORY", Row::new(vec![SqlValue::Integer(100)])).unwrap();
assert_eq!(db.get_table("ORDERS").unwrap().row_count(), 2);
assert_eq!(db.get_table("ORDER_ITEMS").unwrap().row_count(), 3);
assert_eq!(db.get_table("ORDER_HISTORY").unwrap().row_count(), 1);
let truncate_stmt = TruncateTableStmt {
table_names: vec![
"ORDERS".to_string(),
"ORDER_ITEMS".to_string(),
"ORDER_HISTORY".to_string(),
],
if_exists: false,
cascade: None,
};
let rows_deleted = TruncateTableExecutor::execute(&truncate_stmt, &mut db).unwrap();
assert_eq!(rows_deleted, 6); assert_eq!(db.get_table("ORDERS").unwrap().row_count(), 0);
assert_eq!(db.get_table("ORDER_ITEMS").unwrap().row_count(), 0);
assert_eq!(db.get_table("ORDER_HISTORY").unwrap().row_count(), 0);
}
#[test]
fn test_truncate_if_exists_table_not_exists() {
let mut db = Database::new();
let truncate_stmt = TruncateTableStmt {
table_names: vec!["NONEXISTENT".to_string()],
if_exists: true,
cascade: None,
};
let rows_deleted = TruncateTableExecutor::execute(&truncate_stmt, &mut db).unwrap();
assert_eq!(rows_deleted, 0);
}
#[test]
fn test_truncate_without_if_exists_table_not_exists() {
let mut db = Database::new();
let truncate_stmt = TruncateTableStmt {
table_names: vec!["NONEXISTENT".to_string()],
if_exists: false,
cascade: None,
};
let result = TruncateTableExecutor::execute(&truncate_stmt, &mut db);
assert!(result.is_err());
}
#[test]
fn test_truncate_multiple_tables_if_exists_mixed() {
let mut db = Database::new();
let create_stmt = CreateTableStmt {
temporary: false,
if_not_exists: false,
table_name: "EXISTING".to_string(),
columns: vec![ColumnDef {
name: "ID".to_string(),
data_type: DataType::Integer,
nullable: false,
constraints: vec![],
default_value: None,
comment: None,
generated_expr: None,
is_exact_integer_type: false,
}],
table_constraints: vec![],
table_options: vec![],
quoted: false,
name_source: None,
as_query: None,
without_rowid: false,
};
CreateTableExecutor::execute(&create_stmt, &mut db).unwrap();
db.insert_row("EXISTING", Row::new(vec![SqlValue::Integer(1)])).unwrap();
db.insert_row("EXISTING", Row::new(vec![SqlValue::Integer(2)])).unwrap();
let truncate_stmt = TruncateTableStmt {
table_names: vec![
"EXISTING".to_string(),
"NONEXISTENT1".to_string(),
"NONEXISTENT2".to_string(),
],
if_exists: true,
cascade: None,
};
let rows_deleted = TruncateTableExecutor::execute(&truncate_stmt, &mut db).unwrap();
assert_eq!(rows_deleted, 2); assert_eq!(db.get_table("EXISTING").unwrap().row_count(), 0);
}
#[test]
fn test_truncate_multiple_tables_all_or_nothing_validation() {
let mut db = Database::new();
let create_stmt = CreateTableStmt {
temporary: false,
if_not_exists: false,
table_name: "EXISTING".to_string(),
columns: vec![ColumnDef {
name: "ID".to_string(),
data_type: DataType::Integer,
nullable: false,
constraints: vec![],
default_value: None,
comment: None,
generated_expr: None,
is_exact_integer_type: false,
}],
table_constraints: vec![],
table_options: vec![],
quoted: false,
name_source: None,
as_query: None,
without_rowid: false,
};
CreateTableExecutor::execute(&create_stmt, &mut db).unwrap();
db.insert_row("EXISTING", Row::new(vec![SqlValue::Integer(1)])).unwrap();
let truncate_stmt = TruncateTableStmt {
table_names: vec!["EXISTING".to_string(), "NONEXISTENT".to_string()],
if_exists: false,
cascade: None,
};
let result = TruncateTableExecutor::execute(&truncate_stmt, &mut db);
assert!(result.is_err());
assert_eq!(db.get_table("EXISTING").unwrap().row_count(), 1);
}
#[test]
fn test_truncate_empty_table() {
let mut db = Database::new();
let create_stmt = CreateTableStmt {
temporary: false,
if_not_exists: false,
table_name: "EMPTY".to_string(),
columns: vec![ColumnDef {
name: "ID".to_string(),
data_type: DataType::Integer,
nullable: false,
constraints: vec![],
default_value: None,
comment: None,
generated_expr: None,
is_exact_integer_type: false,
}],
table_constraints: vec![],
table_options: vec![],
quoted: false,
name_source: None,
as_query: None,
without_rowid: false,
};
CreateTableExecutor::execute(&create_stmt, &mut db).unwrap();
let truncate_stmt = TruncateTableStmt {
table_names: vec!["EMPTY".to_string()],
if_exists: false,
cascade: None,
};
let rows_deleted = TruncateTableExecutor::execute(&truncate_stmt, &mut db).unwrap();
assert_eq!(rows_deleted, 0);
assert_eq!(db.get_table("EMPTY").unwrap().row_count(), 0);
}
#[test]
fn test_truncate_resets_auto_increment() {
use vibesql_ast::{
ColumnConstraint, ColumnConstraintKind, Expression, InsertSource, InsertStmt,
};
use crate::InsertExecutor;
let mut db = Database::new();
let stmt = CreateTableStmt {
temporary: false,
if_not_exists: false,
table_name: "auto_inc_test".to_string(),
columns: vec![
ColumnDef {
name: "id".to_string(),
data_type: DataType::Integer,
nullable: false,
constraints: vec![
ColumnConstraint { name: None, kind: ColumnConstraintKind::AutoIncrement },
ColumnConstraint {
name: None,
kind: ColumnConstraintKind::PrimaryKey { on_conflict: None },
},
],
default_value: None,
comment: None,
generated_expr: None,
is_exact_integer_type: false,
},
ColumnDef {
name: "data".to_string(),
data_type: DataType::Varchar { max_length: Some(10) },
nullable: true,
constraints: vec![],
default_value: None,
comment: None,
generated_expr: None,
is_exact_integer_type: false,
},
],
table_constraints: vec![],
table_options: vec![],
quoted: false,
name_source: None,
as_query: None,
without_rowid: false,
};
CreateTableExecutor::execute(&stmt, &mut db).unwrap();
for val in ["a", "b", "c"] {
let insert = InsertStmt {
with_clause: None,
schema_name: None,
schema_quoted: false,
table_quoted: false,
table_name: "auto_inc_test".to_string(),
columns: vec!["data".to_string()],
source: InsertSource::Values(vec![vec![Expression::Literal(SqlValue::Varchar(
arcstr::ArcStr::from(val),
))]]),
conflict_clause: None,
on_conflict: None,
on_duplicate_key_update: None,
returning: None,
};
InsertExecutor::execute(&mut db, &insert).unwrap();
}
let table = db.get_table("auto_inc_test").unwrap();
assert_eq!(table.scan().len(), 3);
assert_eq!(table.scan()[0].values[0], SqlValue::Integer(1));
assert_eq!(table.scan()[1].values[0], SqlValue::Integer(2));
assert_eq!(table.scan()[2].values[0], SqlValue::Integer(3));
let truncate = TruncateTableStmt {
table_names: vec!["auto_inc_test".to_string()],
if_exists: false,
cascade: None,
};
TruncateTableExecutor::execute(&truncate, &mut db).unwrap();
let insert = InsertStmt {
with_clause: None,
schema_name: None,
schema_quoted: false,
table_quoted: false,
table_name: "auto_inc_test".to_string(),
columns: vec!["data".to_string()],
source: InsertSource::Values(vec![vec![Expression::Literal(SqlValue::Varchar(
arcstr::ArcStr::from("d"),
))]]),
conflict_clause: None,
on_conflict: None,
on_duplicate_key_update: None,
returning: None,
};
InsertExecutor::execute(&mut db, &insert).unwrap();
let table = db.get_table("auto_inc_test").unwrap();
assert_eq!(table.scan().len(), 1);
assert_eq!(
table.scan()[0].values[0],
SqlValue::Integer(1),
"AUTO_INCREMENT should reset to 1 after TRUNCATE"
);
}
#[test]
fn test_truncate_resets_auto_increment_multiple_inserts() {
use vibesql_ast::{
ColumnConstraint, ColumnConstraintKind, Expression, InsertSource, InsertStmt,
};
use crate::InsertExecutor;
let mut db = Database::new();
let stmt = CreateTableStmt {
temporary: false,
if_not_exists: false,
table_name: "multi_test".to_string(),
columns: vec![
ColumnDef {
name: "id".to_string(),
data_type: DataType::Integer,
nullable: false,
constraints: vec![
ColumnConstraint { name: None, kind: ColumnConstraintKind::AutoIncrement },
ColumnConstraint {
name: None,
kind: ColumnConstraintKind::PrimaryKey { on_conflict: None },
},
],
default_value: None,
comment: None,
generated_expr: None,
is_exact_integer_type: false,
},
ColumnDef {
name: "value".to_string(),
data_type: DataType::Integer,
nullable: true,
constraints: vec![],
default_value: None,
comment: None,
generated_expr: None,
is_exact_integer_type: false,
},
],
table_constraints: vec![],
table_options: vec![],
quoted: false,
name_source: None,
as_query: None,
without_rowid: false,
};
CreateTableExecutor::execute(&stmt, &mut db).unwrap();
for i in 1..=10 {
let insert = InsertStmt {
with_clause: None,
schema_name: None,
schema_quoted: false,
table_quoted: false,
table_name: "multi_test".to_string(),
columns: vec!["value".to_string()],
source: InsertSource::Values(vec![vec![Expression::Literal(SqlValue::Integer(
i * 100,
))]]),
conflict_clause: None,
on_conflict: None,
on_duplicate_key_update: None,
returning: None,
};
InsertExecutor::execute(&mut db, &insert).unwrap();
}
let table = db.get_table("multi_test").unwrap();
assert_eq!(table.scan().len(), 10);
assert_eq!(table.scan().last().unwrap().values[0], SqlValue::Integer(10));
let truncate = TruncateTableStmt {
table_names: vec!["multi_test".to_string()],
if_exists: false,
cascade: None,
};
TruncateTableExecutor::execute(&truncate, &mut db).unwrap();
let insert = InsertStmt {
with_clause: None,
schema_name: None,
schema_quoted: false,
table_quoted: false,
table_name: "multi_test".to_string(),
columns: vec!["value".to_string()],
source: InsertSource::Values(vec![vec![Expression::Literal(SqlValue::Integer(9999))]]),
conflict_clause: None,
on_conflict: None,
on_duplicate_key_update: None,
returning: None,
};
InsertExecutor::execute(&mut db, &insert).unwrap();
let table = db.get_table("multi_test").unwrap();
assert_eq!(table.scan().len(), 1);
assert_eq!(
table.scan()[0].values[0],
SqlValue::Integer(1),
"AUTO_INCREMENT should reset to 1 after TRUNCATE, not continue from 10"
);
}
#[test]
fn test_truncate_without_auto_increment() {
let mut db = Database::new();
let create_stmt = CreateTableStmt {
temporary: false,
if_not_exists: false,
table_name: "no_auto_inc".to_string(),
columns: vec![
ColumnDef {
name: "id".to_string(),
data_type: DataType::Integer,
nullable: false,
constraints: vec![],
default_value: None,
comment: None,
generated_expr: None,
is_exact_integer_type: false,
},
ColumnDef {
name: "data".to_string(),
data_type: DataType::Varchar { max_length: Some(100) },
nullable: false,
constraints: vec![],
default_value: None,
comment: None,
generated_expr: None,
is_exact_integer_type: false,
},
],
table_constraints: vec![],
table_options: vec![],
quoted: false,
name_source: None,
as_query: None,
without_rowid: false,
};
CreateTableExecutor::execute(&create_stmt, &mut db).unwrap();
db.insert_row(
"no_auto_inc",
Row::new(vec![SqlValue::Integer(100), SqlValue::Varchar(arcstr::ArcStr::from("test"))]),
)
.unwrap();
let stmt = TruncateTableStmt {
table_names: vec!["no_auto_inc".to_string()],
if_exists: false,
cascade: None,
};
let result = TruncateTableExecutor::execute(&stmt, &mut db);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 1);
assert_eq!(db.get_table("no_auto_inc").unwrap().row_count(), 0);
}