use super::*;
#[test]
fn parse_create_table() {
let stmt =
parse_sql("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, age INTEGER)")
.unwrap();
match stmt {
Statement::CreateTable(ct) => {
assert_eq!(ct.name, "users");
assert_eq!(ct.columns.len(), 3);
assert_eq!(ct.columns[0].name, "id");
assert_eq!(ct.columns[0].data_type, DataType::Integer);
assert!(ct.columns[0].is_primary_key);
assert!(!ct.columns[0].nullable);
assert_eq!(ct.columns[1].name, "name");
assert_eq!(ct.columns[1].data_type, DataType::Text);
assert!(!ct.columns[1].nullable);
assert_eq!(ct.columns[2].name, "age");
assert!(ct.columns[2].nullable);
assert_eq!(ct.primary_key, vec!["id"]);
}
_ => panic!("expected CreateTable"),
}
}
#[test]
fn parse_create_table_if_not_exists() {
let stmt = parse_sql("CREATE TABLE IF NOT EXISTS t (id INT PRIMARY KEY)").unwrap();
match stmt {
Statement::CreateTable(ct) => assert!(ct.if_not_exists),
_ => panic!("expected CreateTable"),
}
}
#[test]
fn parse_drop_table() {
let stmt = parse_sql("DROP TABLE users").unwrap();
match stmt {
Statement::DropTable(dt) => {
assert_eq!(dt.name, "users");
assert!(!dt.if_exists);
}
_ => panic!("expected DropTable"),
}
}
#[test]
fn parse_drop_table_if_exists() {
let stmt = parse_sql("DROP TABLE IF EXISTS users").unwrap();
match stmt {
Statement::DropTable(dt) => assert!(dt.if_exists),
_ => panic!("expected DropTable"),
}
}
#[test]
fn parse_insert() {
let stmt = parse_sql("INSERT INTO users (id, name) VALUES (1, 'Alice'), (2, 'Bob')").unwrap();
match stmt {
Statement::Insert(ins) => {
assert_eq!(ins.table, "users");
assert_eq!(ins.columns, vec!["id", "name"]);
let values = match &ins.source {
InsertSource::Values(v) => v,
_ => panic!("expected Values"),
};
assert_eq!(values.len(), 2);
assert!(matches!(values[0][0], Expr::Literal(Value::Integer(1))));
assert!(matches!(&values[0][1], Expr::Literal(Value::Text(s)) if s == "Alice"));
assert!(ins.on_conflict.is_none());
}
_ => panic!("expected Insert"),
}
}
#[test]
fn parse_upsert_do_nothing() {
let stmt =
parse_sql("INSERT INTO t (id, v) VALUES (1, 'a') ON CONFLICT (id) DO NOTHING").unwrap();
match stmt {
Statement::Insert(ins) => {
let oc = ins.on_conflict.expect("expected on_conflict");
match oc.target.expect("target") {
ConflictTarget::Columns(cols) => assert_eq!(cols, vec!["id"]),
_ => panic!("expected Columns target"),
}
assert!(matches!(oc.action, OnConflictAction::DoNothing));
}
_ => panic!("expected Insert"),
}
}
#[test]
fn parse_upsert_do_nothing_no_target() {
let stmt = parse_sql("INSERT INTO t VALUES (1, 'a') ON CONFLICT DO NOTHING").unwrap();
match stmt {
Statement::Insert(ins) => {
let oc = ins.on_conflict.expect("expected on_conflict");
assert!(oc.target.is_none());
assert!(matches!(oc.action, OnConflictAction::DoNothing));
}
_ => panic!("expected Insert"),
}
}
#[test]
fn parse_upsert_do_update_simple() {
let stmt =
parse_sql("INSERT INTO t (id, v) VALUES (1, 'a') ON CONFLICT (id) DO UPDATE SET v = 'b'")
.unwrap();
match stmt {
Statement::Insert(ins) => {
let oc = ins.on_conflict.expect("expected on_conflict");
match oc.action {
OnConflictAction::DoUpdate {
assignments,
where_clause,
} => {
assert_eq!(assignments.len(), 1);
assert_eq!(assignments[0].0, "v");
assert!(where_clause.is_none());
}
_ => panic!("expected DoUpdate"),
}
}
_ => panic!("expected Insert"),
}
}
#[test]
fn parse_upsert_do_update_excluded() {
let stmt = parse_sql(
"INSERT INTO t (id, v) VALUES (1, 'a') \
ON CONFLICT (id) DO UPDATE SET v = excluded.v",
)
.unwrap();
match stmt {
Statement::Insert(ins) => {
let oc = ins.on_conflict.expect("expected on_conflict");
let assignments = match oc.action {
OnConflictAction::DoUpdate { assignments, .. } => assignments,
_ => panic!("expected DoUpdate"),
};
match &assignments[0].1 {
Expr::QualifiedColumn { table, column } => {
assert_eq!(table, "excluded");
assert_eq!(column, "v");
}
_ => panic!("expected QualifiedColumn"),
}
}
_ => panic!("expected Insert"),
}
}
#[test]
fn parse_upsert_do_update_where() {
let stmt = parse_sql(
"INSERT INTO t (id, v) VALUES (1, 'a') \
ON CONFLICT (id) DO UPDATE SET v = excluded.v WHERE t.v < 'z'",
)
.unwrap();
match stmt {
Statement::Insert(ins) => {
let oc = ins.on_conflict.expect("expected on_conflict");
match oc.action {
OnConflictAction::DoUpdate { where_clause, .. } => {
assert!(where_clause.is_some());
}
_ => panic!("expected DoUpdate"),
}
}
_ => panic!("expected Insert"),
}
}
#[test]
fn parse_upsert_on_constraint_named() {
let stmt = parse_sql(
"INSERT INTO t (id, v) VALUES (1, 'a') \
ON CONFLICT ON CONSTRAINT t_v_idx DO NOTHING",
)
.unwrap();
match stmt {
Statement::Insert(ins) => {
let oc = ins.on_conflict.expect("expected on_conflict");
match oc.target.expect("target") {
ConflictTarget::Constraint(name) => assert_eq!(name, "t_v_idx"),
_ => panic!("expected Constraint target"),
}
}
_ => panic!("expected Insert"),
}
}
#[test]
fn parse_upsert_rejects_duplicate_key_update() {
let err = parse_sql("INSERT INTO t (id) VALUES (1) ON DUPLICATE KEY UPDATE id = 2")
.expect_err("should reject MySQL syntax");
let msg = format!("{err}");
assert!(msg.contains("ON DUPLICATE KEY UPDATE") || msg.contains("MySQL"));
}
#[test]
fn parse_upsert_lowercases_conflict_target() {
let stmt = parse_sql("INSERT INTO t (id) VALUES (1) ON CONFLICT (ID) DO NOTHING").unwrap();
match stmt {
Statement::Insert(ins) => {
let oc = ins.on_conflict.expect("expected on_conflict");
match oc.target.expect("target") {
ConflictTarget::Columns(cols) => assert_eq!(cols, vec!["id"]),
_ => panic!("expected Columns"),
}
}
_ => panic!("expected Insert"),
}
}
#[test]
fn parse_select_all() {
let stmt = parse_sql("SELECT * FROM users").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => {
assert_eq!(sel.from, "users");
assert!(matches!(sel.columns[0], SelectColumn::AllColumns));
assert!(sel.where_clause.is_none());
}
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn parse_select_where() {
let stmt = parse_sql("SELECT id, name FROM users WHERE age > 18").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => {
assert_eq!(sel.columns.len(), 2);
assert!(sel.where_clause.is_some());
}
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn parse_select_order_limit() {
let stmt = parse_sql("SELECT * FROM users ORDER BY name ASC LIMIT 10 OFFSET 5").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => {
assert_eq!(sel.order_by.len(), 1);
assert!(!sel.order_by[0].descending);
assert!(sel.limit.is_some());
assert!(sel.offset.is_some());
}
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn parse_update() {
let stmt = parse_sql("UPDATE users SET name = 'Bob' WHERE id = 1").unwrap();
match stmt {
Statement::Update(upd) => {
assert_eq!(upd.table, "users");
assert_eq!(upd.assignments.len(), 1);
assert_eq!(upd.assignments[0].0, "name");
assert!(upd.where_clause.is_some());
}
_ => panic!("expected Update"),
}
}
#[test]
fn parse_delete() {
let stmt = parse_sql("DELETE FROM users WHERE id = 1").unwrap();
match stmt {
Statement::Delete(del) => {
assert_eq!(del.table, "users");
assert!(del.where_clause.is_some());
}
_ => panic!("expected Delete"),
}
}
#[test]
fn parse_aggregate() {
let stmt = parse_sql("SELECT COUNT(*), SUM(age) FROM users").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => {
assert_eq!(sel.columns.len(), 2);
match &sel.columns[0] {
SelectColumn::Expr {
expr: Expr::CountStar,
..
} => {}
other => panic!("expected CountStar, got {other:?}"),
}
}
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn parse_group_by_having() {
let stmt = parse_sql(
"SELECT department, COUNT(*) FROM employees GROUP BY department HAVING COUNT(*) > 5",
)
.unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => {
assert_eq!(sel.group_by.len(), 1);
assert!(sel.having.is_some());
}
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn parse_expressions() {
let stmt = parse_sql("SELECT id + 1, -price, NOT active FROM items").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => {
assert_eq!(sel.columns.len(), 3);
match &sel.columns[0] {
SelectColumn::Expr {
expr: Expr::BinaryOp { op: BinOp::Add, .. },
..
} => {}
other => panic!("expected BinaryOp Add, got {other:?}"),
}
match &sel.columns[1] {
SelectColumn::Expr {
expr:
Expr::UnaryOp {
op: UnaryOp::Neg, ..
},
..
} => {}
other => panic!("expected UnaryOp Neg, got {other:?}"),
}
match &sel.columns[2] {
SelectColumn::Expr {
expr:
Expr::UnaryOp {
op: UnaryOp::Not, ..
},
..
} => {}
other => panic!("expected UnaryOp Not, got {other:?}"),
}
}
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn parse_is_null() {
let stmt = parse_sql("SELECT * FROM t WHERE x IS NULL").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => {
assert!(matches!(sel.where_clause, Some(Expr::IsNull(_))));
}
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn parse_inner_join() {
let stmt = parse_sql("SELECT * FROM a JOIN b ON a.id = b.id").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => {
assert_eq!(sel.from, "a");
assert_eq!(sel.joins.len(), 1);
assert_eq!(sel.joins[0].join_type, JoinType::Inner);
assert_eq!(sel.joins[0].table.name, "b");
assert!(sel.joins[0].on_clause.is_some());
}
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn parse_inner_join_explicit() {
let stmt = parse_sql("SELECT * FROM a INNER JOIN b ON a.id = b.a_id").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => {
assert_eq!(sel.joins.len(), 1);
assert_eq!(sel.joins[0].join_type, JoinType::Inner);
}
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn parse_cross_join() {
let stmt = parse_sql("SELECT * FROM a CROSS JOIN b").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => {
assert_eq!(sel.joins.len(), 1);
assert_eq!(sel.joins[0].join_type, JoinType::Cross);
assert!(sel.joins[0].on_clause.is_none());
}
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn parse_left_join() {
let stmt = parse_sql("SELECT * FROM a LEFT JOIN b ON a.id = b.a_id").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => {
assert_eq!(sel.joins.len(), 1);
assert_eq!(sel.joins[0].join_type, JoinType::Left);
}
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn parse_table_alias() {
let stmt = parse_sql("SELECT u.id FROM users u JOIN orders o ON u.id = o.user_id").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => {
assert_eq!(sel.from, "users");
assert_eq!(sel.from_alias.as_deref(), Some("u"));
assert_eq!(sel.joins[0].table.name, "orders");
assert_eq!(sel.joins[0].table.alias.as_deref(), Some("o"));
}
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn parse_multi_join() {
let stmt =
parse_sql("SELECT * FROM a JOIN b ON a.id = b.a_id JOIN c ON b.id = c.b_id").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => {
assert_eq!(sel.joins.len(), 2);
}
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn parse_qualified_column() {
let stmt = parse_sql("SELECT u.id, u.name FROM users u").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => match &sel.columns[0] {
SelectColumn::Expr {
expr: Expr::QualifiedColumn { table, column },
..
} => {
assert_eq!(table, "u");
assert_eq!(column, "id");
}
other => panic!("expected QualifiedColumn, got {other:?}"),
},
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn reject_subquery() {
assert!(parse_sql("SELECT * FROM (SELECT 1)").is_err());
}
#[test]
fn parse_type_mapping() {
let stmt = parse_sql(
"CREATE TABLE t (a INT PRIMARY KEY, b BIGINT, c SMALLINT, d REAL, e DOUBLE PRECISION, f VARCHAR(255), g BOOLEAN, h BLOB, i BYTEA)"
).unwrap();
match stmt {
Statement::CreateTable(ct) => {
assert_eq!(ct.columns[0].data_type, DataType::Integer); assert_eq!(ct.columns[1].data_type, DataType::Integer); assert_eq!(ct.columns[2].data_type, DataType::Integer); assert_eq!(ct.columns[3].data_type, DataType::Real); assert_eq!(ct.columns[4].data_type, DataType::Real); assert_eq!(ct.columns[5].data_type, DataType::Text); assert_eq!(ct.columns[6].data_type, DataType::Boolean); assert_eq!(ct.columns[7].data_type, DataType::Blob); assert_eq!(ct.columns[8].data_type, DataType::Blob); }
_ => panic!("expected CreateTable"),
}
}
#[test]
fn parse_boolean_literals() {
let stmt = parse_sql("INSERT INTO t (a, b) VALUES (true, false)").unwrap();
match stmt {
Statement::Insert(ins) => {
let values = match &ins.source {
InsertSource::Values(v) => v,
_ => panic!("expected Values"),
};
assert!(matches!(values[0][0], Expr::Literal(Value::Boolean(true))));
assert!(matches!(values[0][1], Expr::Literal(Value::Boolean(false))));
}
_ => panic!("expected Insert"),
}
}
#[test]
fn parse_null_literal() {
let stmt = parse_sql("INSERT INTO t (a) VALUES (NULL)").unwrap();
match stmt {
Statement::Insert(ins) => {
let values = match &ins.source {
InsertSource::Values(v) => v,
_ => panic!("expected Values"),
};
assert!(matches!(values[0][0], Expr::Literal(Value::Null)));
}
_ => panic!("expected Insert"),
}
}
#[test]
fn parse_alias() {
let stmt = parse_sql("SELECT id AS user_id FROM users").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => match &sel.columns[0] {
SelectColumn::Expr { alias: Some(a), .. } => assert_eq!(a, "user_id"),
other => panic!("expected alias, got {other:?}"),
},
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn parse_begin() {
let stmt = parse_sql("BEGIN").unwrap();
assert!(matches!(stmt, Statement::Begin));
}
#[test]
fn parse_begin_transaction() {
let stmt = parse_sql("BEGIN TRANSACTION").unwrap();
assert!(matches!(stmt, Statement::Begin));
}
#[test]
fn parse_commit() {
let stmt = parse_sql("COMMIT").unwrap();
assert!(matches!(stmt, Statement::Commit));
}
#[test]
fn parse_rollback() {
let stmt = parse_sql("ROLLBACK").unwrap();
assert!(matches!(stmt, Statement::Rollback));
}
#[test]
fn parse_savepoint() {
let stmt = parse_sql("SAVEPOINT sp1").unwrap();
match stmt {
Statement::Savepoint(name) => assert_eq!(name, "sp1"),
other => panic!("expected Savepoint, got {other:?}"),
}
}
#[test]
fn parse_savepoint_case_insensitive() {
let stmt = parse_sql("SAVEPOINT My_SP").unwrap();
match stmt {
Statement::Savepoint(name) => assert_eq!(name, "my_sp"),
other => panic!("expected Savepoint, got {other:?}"),
}
}
#[test]
fn parse_release_savepoint() {
let stmt = parse_sql("RELEASE SAVEPOINT sp1").unwrap();
match stmt {
Statement::ReleaseSavepoint(name) => assert_eq!(name, "sp1"),
other => panic!("expected ReleaseSavepoint, got {other:?}"),
}
}
#[test]
fn parse_release_without_savepoint_keyword() {
let stmt = parse_sql("RELEASE sp1").unwrap();
match stmt {
Statement::ReleaseSavepoint(name) => assert_eq!(name, "sp1"),
other => panic!("expected ReleaseSavepoint, got {other:?}"),
}
}
#[test]
fn parse_rollback_to_savepoint() {
let stmt = parse_sql("ROLLBACK TO SAVEPOINT sp1").unwrap();
match stmt {
Statement::RollbackTo(name) => assert_eq!(name, "sp1"),
other => panic!("expected RollbackTo, got {other:?}"),
}
}
#[test]
fn parse_rollback_to_without_savepoint_keyword() {
let stmt = parse_sql("ROLLBACK TO sp1").unwrap();
match stmt {
Statement::RollbackTo(name) => assert_eq!(name, "sp1"),
other => panic!("expected RollbackTo, got {other:?}"),
}
}
#[test]
fn parse_rollback_to_case_insensitive() {
let stmt = parse_sql("ROLLBACK TO My_SP").unwrap();
match stmt {
Statement::RollbackTo(name) => assert_eq!(name, "my_sp"),
other => panic!("expected RollbackTo, got {other:?}"),
}
}
#[test]
fn parse_commit_and_chain_rejected() {
let err = parse_sql("COMMIT AND CHAIN").unwrap_err();
assert!(matches!(err, SqlError::Unsupported(_)));
}
#[test]
fn parse_rollback_and_chain_rejected() {
let err = parse_sql("ROLLBACK AND CHAIN").unwrap_err();
assert!(matches!(err, SqlError::Unsupported(_)));
}
#[test]
fn parse_select_distinct() {
let stmt = parse_sql("SELECT DISTINCT name FROM users").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => {
assert!(sel.distinct);
assert_eq!(sel.columns.len(), 1);
}
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn parse_select_without_distinct() {
let stmt = parse_sql("SELECT name FROM users").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => {
assert!(!sel.distinct);
}
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn parse_select_distinct_all_columns() {
let stmt = parse_sql("SELECT DISTINCT * FROM users").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => {
assert!(sel.distinct);
assert!(matches!(sel.columns[0], SelectColumn::AllColumns));
}
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn reject_distinct_on() {
assert!(parse_sql("SELECT DISTINCT ON (id) * FROM users").is_err());
}
#[test]
fn parse_create_index() {
let stmt = parse_sql("CREATE INDEX idx_name ON users (name)").unwrap();
match stmt {
Statement::CreateIndex(ci) => {
assert_eq!(ci.index_name, "idx_name");
assert_eq!(ci.table_name, "users");
assert_eq!(ci.columns, vec!["name"]);
assert!(!ci.unique);
assert!(!ci.if_not_exists);
}
_ => panic!("expected CreateIndex"),
}
}
#[test]
fn parse_create_unique_index() {
let stmt = parse_sql("CREATE UNIQUE INDEX idx_email ON users (email)").unwrap();
match stmt {
Statement::CreateIndex(ci) => {
assert!(ci.unique);
assert_eq!(ci.columns, vec!["email"]);
}
_ => panic!("expected CreateIndex"),
}
}
#[test]
fn parse_create_index_if_not_exists() {
let stmt = parse_sql("CREATE INDEX IF NOT EXISTS idx_x ON t (a)").unwrap();
match stmt {
Statement::CreateIndex(ci) => assert!(ci.if_not_exists),
_ => panic!("expected CreateIndex"),
}
}
#[test]
fn parse_create_index_multi_column() {
let stmt = parse_sql("CREATE INDEX idx_multi ON t (a, b, c)").unwrap();
match stmt {
Statement::CreateIndex(ci) => {
assert_eq!(ci.columns, vec!["a", "b", "c"]);
}
_ => panic!("expected CreateIndex"),
}
}
#[test]
fn parse_drop_index() {
let stmt = parse_sql("DROP INDEX idx_name").unwrap();
match stmt {
Statement::DropIndex(di) => {
assert_eq!(di.index_name, "idx_name");
assert!(!di.if_exists);
}
_ => panic!("expected DropIndex"),
}
}
#[test]
fn parse_drop_index_if_exists() {
let stmt = parse_sql("DROP INDEX IF EXISTS idx_name").unwrap();
match stmt {
Statement::DropIndex(di) => {
assert!(di.if_exists);
}
_ => panic!("expected DropIndex"),
}
}
#[test]
fn parse_explain_select() {
let stmt = parse_sql("EXPLAIN SELECT * FROM users WHERE id = 1").unwrap();
match stmt {
Statement::Explain(inner) => {
assert!(matches!(*inner, Statement::Select(_)));
}
_ => panic!("expected Explain"),
}
}
#[test]
fn parse_explain_insert() {
let stmt = parse_sql("EXPLAIN INSERT INTO t (a) VALUES (1)").unwrap();
assert!(matches!(stmt, Statement::Explain(_)));
}
#[test]
fn reject_explain_analyze() {
assert!(parse_sql("EXPLAIN ANALYZE SELECT * FROM t").is_err());
}
#[test]
fn parse_parameter_placeholder() {
let stmt = parse_sql("SELECT * FROM t WHERE id = $1").unwrap();
match stmt {
Statement::Select(sq) => match sq.body {
QueryBody::Select(sel) => match &sel.where_clause {
Some(Expr::BinaryOp { right, .. }) => {
assert!(matches!(right.as_ref(), Expr::Parameter(1)));
}
other => panic!("expected BinaryOp with Parameter, got {other:?}"),
},
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected Select"),
}
}
#[test]
fn parse_multiple_parameters() {
let stmt = parse_sql("INSERT INTO t (a, b) VALUES ($1, $2)").unwrap();
match stmt {
Statement::Insert(ins) => {
let values = match &ins.source {
InsertSource::Values(v) => v,
_ => panic!("expected Values"),
};
assert!(matches!(values[0][0], Expr::Parameter(1)));
assert!(matches!(values[0][1], Expr::Parameter(2)));
}
_ => panic!("expected Insert"),
}
}
#[test]
fn parse_insert_select() {
let stmt = parse_sql("INSERT INTO t2 (id, name) SELECT id, name FROM t1 WHERE id > 5").unwrap();
match stmt {
Statement::Insert(ins) => {
assert_eq!(ins.table, "t2");
assert_eq!(ins.columns, vec!["id", "name"]);
match &ins.source {
InsertSource::Select(sq) => match &sq.body {
QueryBody::Select(sel) => {
assert_eq!(sel.from, "t1");
assert_eq!(sel.columns.len(), 2);
assert!(sel.where_clause.is_some());
}
_ => panic!("expected QueryBody::Select"),
},
_ => panic!("expected InsertSource::Select"),
}
}
_ => panic!("expected Insert"),
}
}
#[test]
fn parse_insert_select_no_columns() {
let stmt = parse_sql("INSERT INTO t2 SELECT * FROM t1").unwrap();
match stmt {
Statement::Insert(ins) => {
assert_eq!(ins.table, "t2");
assert!(ins.columns.is_empty());
assert!(matches!(&ins.source, InsertSource::Select(_)));
}
_ => panic!("expected Insert"),
}
}
#[test]
fn reject_zero_parameter() {
assert!(parse_sql("SELECT $0 FROM t").is_err());
}
#[test]
fn count_params_basic() {
let stmt = parse_sql("SELECT * FROM t WHERE a = $1 AND b = $3").unwrap();
assert_eq!(count_params(&stmt), 3);
}
#[test]
fn count_params_none() {
let stmt = parse_sql("SELECT * FROM t WHERE a = 1").unwrap();
assert_eq!(count_params(&stmt), 0);
}
#[test]
fn parse_table_constraint_pk() {
let stmt = parse_sql("CREATE TABLE t (a INTEGER, b TEXT, PRIMARY KEY (a))").unwrap();
match stmt {
Statement::CreateTable(ct) => {
assert_eq!(ct.primary_key, vec!["a"]);
assert!(ct.columns[0].is_primary_key);
assert!(!ct.columns[0].nullable);
}
_ => panic!("expected CreateTable"),
}
}