use stoolap::Database;
fn setup_db(name: &str) -> Database {
let db = Database::open(&format!("memory://{}", name)).unwrap();
db.execute(
"CREATE TABLE logs (id INTEGER PRIMARY KEY, message TEXT)",
(),
)
.unwrap();
db.execute("INSERT INTO logs VALUES (1, 'hello world')", ())
.unwrap();
db.execute("INSERT INTO logs VALUES (2, 'foo bar')", ())
.unwrap();
db.execute("INSERT INTO logs VALUES (3, 'hello again')", ())
.unwrap();
db.execute("INSERT INTO logs VALUES (4, 'HELLO UPPER')", ())
.unwrap();
db
}
fn count_rows(db: &Database, sql: &str, params: impl stoolap::Params) -> usize {
let mut rows = db.query(sql, params).unwrap();
let mut count = 0;
while let Some(Ok(_)) = rows.next() {
count += 1;
}
count
}
#[test]
fn test_like_with_parameter() {
let db = setup_db("like_param");
assert_eq!(
count_rows(
&db,
"SELECT * FROM logs WHERE message LIKE $1",
("%hello%",)
),
2
);
}
#[test]
fn test_like_with_parameter_prefix() {
let db = setup_db("like_param_prefix");
assert_eq!(
count_rows(&db, "SELECT * FROM logs WHERE message LIKE $1", ("hello%",)),
2
);
}
#[test]
fn test_like_with_parameter_suffix() {
let db = setup_db("like_param_suffix");
assert_eq!(
count_rows(&db, "SELECT * FROM logs WHERE message LIKE $1", ("%world",)),
1
);
}
#[test]
fn test_like_with_parameter_no_match() {
let db = setup_db("like_param_no_match");
assert_eq!(
count_rows(
&db,
"SELECT * FROM logs WHERE message LIKE $1",
("nonexistent%",)
),
0
);
}
#[test]
fn test_ilike_with_parameter() {
let db = setup_db("ilike_param");
assert_eq!(
count_rows(
&db,
"SELECT * FROM logs WHERE message ILIKE $1",
("hello%",)
),
3
);
}
#[test]
fn test_not_like_with_parameter() {
let db = setup_db("not_like_param");
assert_eq!(
count_rows(
&db,
"SELECT * FROM logs WHERE message NOT LIKE $1",
("%hello%",)
),
2
);
}
#[test]
fn test_not_ilike_with_parameter() {
let db = setup_db("not_ilike_param");
assert_eq!(
count_rows(
&db,
"SELECT * FROM logs WHERE message NOT ILIKE $1",
("hello%",)
),
1
);
}
#[test]
fn test_like_with_parameter_and_order_by() {
let db = setup_db("like_param_order");
let mut rows = db
.query(
"SELECT id FROM logs WHERE message LIKE $1 ORDER BY id DESC",
("%hello%",),
)
.unwrap();
let mut ids = Vec::new();
while let Some(Ok(row)) = rows.next() {
ids.push(row.get::<i64>(0).unwrap());
}
assert_eq!(ids, vec![3, 1]);
}
#[test]
fn test_like_with_parameter_exact() {
let db = setup_db("like_param_exact");
assert_eq!(
count_rows(
&db,
"SELECT * FROM logs WHERE message LIKE $1",
("hello world",)
),
1
);
}
#[test]
fn test_like_with_parameter_underscore() {
let db = setup_db("like_param_underscore");
assert_eq!(
count_rows(
&db,
"SELECT * FROM logs WHERE message LIKE $1",
("hello_world",)
),
1
);
}
#[test]
fn test_like_literal_still_works() {
let db = setup_db("like_literal");
assert_eq!(
count_rows(&db, "SELECT * FROM logs WHERE message LIKE '%hello%'", ()),
2
);
}
#[test]
fn test_like_and_glob_dynamic_no_cache_collision() {
let db = Database::open("memory://like_glob_cache").unwrap();
db.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT)", ())
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'hello')", ()).unwrap();
db.execute("INSERT INTO t VALUES (2, '*')", ()).unwrap();
assert_eq!(
count_rows(&db, "SELECT * FROM t WHERE val LIKE $1", ("*",)),
1
);
assert_eq!(
count_rows(&db, "SELECT * FROM t WHERE val GLOB $1", ("*",)),
2
);
}
#[test]
fn test_like_dynamic_with_escape() {
let db = Database::open("memory://like_dyn_escape").unwrap();
db.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT)", ())
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'hello%world')", ())
.unwrap();
db.execute("INSERT INTO t VALUES (2, 'helloXworld')", ())
.unwrap();
db.execute("INSERT INTO t VALUES (3, '100% done')", ())
.unwrap();
let mut rows = db
.query("SELECT id FROM t WHERE val LIKE $1 ESCAPE '!'", ("%!%%",))
.unwrap();
let mut ids = Vec::new();
while let Some(Ok(row)) = rows.next() {
ids.push(row.get::<i64>(0).unwrap());
}
ids.sort();
assert_eq!(
ids,
vec![1, 3],
"Dynamic LIKE with ESCAPE should match literal %"
);
}
#[test]
fn test_regexp_dynamic_invalid_pattern_errors() {
let db = Database::open("memory://regexp_dyn_err").unwrap();
db.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT)", ())
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'hello')", ()).unwrap();
let result = db.query("SELECT * FROM t WHERE val REGEXP $1", ("[invalid",));
let has_error = match result {
Err(_) => true,
Ok(rows) => {
let mut found_err = false;
for row_result in rows {
if row_result.is_err() {
found_err = true;
break;
}
}
found_err
}
};
assert!(has_error, "Invalid dynamic REGEXP should return an error");
}
#[test]
fn test_not_regexp_dynamic_invalid_pattern_errors() {
let db = Database::open("memory://not_regexp_dyn_err").unwrap();
db.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT)", ())
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'hello')", ()).unwrap();
let result = db.query("SELECT * FROM t WHERE val NOT REGEXP $1", ("[invalid",));
let has_error = match result {
Err(_) => true,
Ok(rows) => {
let mut found_err = false;
for row_result in rows {
if row_result.is_err() {
found_err = true;
break;
}
}
found_err
}
};
assert!(
has_error,
"Invalid dynamic NOT REGEXP should return an error, not match all rows"
);
}
#[test]
fn test_regexp_dynamic_error_with_order_by() {
let db = Database::open("memory://regexp_dyn_order_err").unwrap();
db.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT)", ())
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'hello')", ()).unwrap();
db.execute("INSERT INTO t VALUES (2, 'world')", ()).unwrap();
let result = db.query(
"SELECT * FROM t WHERE val REGEXP $1 ORDER BY id",
("[invalid",),
);
let has_error = match result {
Err(_) => true,
Ok(rows) => {
let mut found_err = false;
for row_result in rows {
if row_result.is_err() {
found_err = true;
break;
}
}
found_err
}
};
assert!(
has_error,
"Invalid dynamic REGEXP with ORDER BY should surface error, not return empty results"
);
}
#[test]
fn test_like_escape_cache_distinguishes_escape_char() {
let db = Database::open("memory://like_esc_cache").unwrap();
db.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT)", ())
.unwrap();
db.execute("INSERT INTO t VALUES (1, '100%')", ()).unwrap();
db.execute("INSERT INTO t VALUES (2, '100X')", ()).unwrap();
assert_eq!(
count_rows(&db, "SELECT * FROM t WHERE val LIKE $1", ("100%",)),
2
);
assert_eq!(
count_rows(
&db,
"SELECT * FROM t WHERE val LIKE $1 ESCAPE '!'",
("100!%",)
),
1
);
}