use motedb::{types::Value, Database};
use tempfile::TempDir;
fn rows(result: motedb::StreamingQueryResult) -> Vec<Vec<Value>> {
use motedb::QueryResult;
match result.materialize().unwrap() {
QueryResult::Select { rows, .. } => rows,
_ => panic!("Expected Select result"),
}
}
fn row(result: motedb::StreamingQueryResult) -> Vec<Value> {
let r = rows(result);
assert_eq!(r.len(), 1);
r.into_iter().next().unwrap()
}
fn setup() -> (Database, TempDir) {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, name TEXT, score FLOAT, age INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'Hello World', 95.67, 25)")
.unwrap();
db.execute("INSERT INTO t VALUES (2, 'MoteDB Engine', 88.33, 30)")
.unwrap();
db.execute("INSERT INTO t VALUES (3, ' spaced ', 50.5, 20)")
.unwrap();
(db, dir)
}
#[test]
fn test_lower() {
let (db, _dir) = setup();
let r = row(db
.execute("SELECT LOWER(name) FROM t WHERE id = 1")
.unwrap());
assert_eq!(&r[0], &Value::text("hello world".to_string()));
}
#[test]
fn test_upper() {
let (db, _dir) = setup();
let r = row(db
.execute("SELECT UPPER(name) FROM t WHERE id = 1")
.unwrap());
assert_eq!(&r[0], &Value::text("HELLO WORLD".to_string()));
}
#[test]
fn test_length() {
let (db, _dir) = setup();
let r = row(db
.execute("SELECT LENGTH(name) FROM t WHERE id = 1")
.unwrap());
assert_eq!(&r[0], &Value::Integer(11));
}
#[test]
fn test_trim_ltrim_rtrim() {
let (db, _dir) = setup();
let r = row(db.execute("SELECT TRIM(name) FROM t WHERE id = 3").unwrap());
assert_eq!(&r[0], &Value::text("spaced".to_string()));
let r = row(db
.execute("SELECT LTRIM(name) FROM t WHERE id = 3")
.unwrap());
match &r[0] {
Value::Text(s) => assert!(s.starts_with('s') && !s.starts_with(' ')),
_ => panic!("Expected Text"),
}
let r = row(db
.execute("SELECT RTRIM(name) FROM t WHERE id = 3")
.unwrap());
match &r[0] {
Value::Text(s) => assert!(!s.ends_with(' ')),
_ => panic!("Expected Text"),
}
}
#[test]
fn test_concat() {
let (db, _dir) = setup();
let r = row(db
.execute("SELECT CONCAT(name, ' - ', id) FROM t WHERE id = 1")
.unwrap());
assert_eq!(&r[0], &Value::text("Hello World - 1".to_string()));
}
#[test]
fn test_concat_multi_args() {
let (db, _dir) = setup();
let r = row(db
.execute("SELECT CONCAT(name, ' has score ', score) FROM t WHERE id = 1")
.unwrap());
match &r[0] {
Value::Text(s) => {
assert!(s.as_str().contains("Hello World"));
assert!(s.as_str().contains("95"));
}
_ => panic!("Expected Text"),
}
}
#[test]
fn test_abs_positive() {
let (db, _dir) = setup();
let r = row(db.execute("SELECT ABS(age) FROM t WHERE id = 1").unwrap());
assert_eq!(&r[0], &Value::Integer(25));
}
#[test]
fn test_round() {
let (db, _dir) = setup();
let r = row(db
.execute("SELECT ROUND(score) FROM t WHERE id = 1")
.unwrap());
match &r[0] {
Value::Float(f) => assert!(
(f - 96.0).abs() < 1.0,
"ROUND(95.67) should be ~96, got {}",
f
),
other => panic!("Expected Float, got {:?}", other),
}
}
#[test]
fn test_floor_ceil() {
let (db, _dir) = setup();
let r = row(db
.execute("SELECT FLOOR(score), CEIL(score) FROM t WHERE id = 1")
.unwrap());
assert_eq!(&r[0], &Value::Integer(95));
assert_eq!(&r[1], &Value::Integer(96));
}
#[test]
fn test_sqrt() {
let (db, _dir) = setup();
let r = row(db.execute("SELECT SQRT(16) FROM t WHERE id = 1").unwrap());
match &r[0] {
Value::Float(f) => assert!((f - 4.0).abs() < 0.01),
other => panic!("Expected Float, got {:?}", other),
}
}
#[test]
fn test_log_ln_exp() {
let (db, _dir) = setup();
let r = row(db
.execute("SELECT LOG(1000), LN(2), EXP(1) FROM t WHERE id = 1")
.unwrap());
match (&r[0], &r[1], &r[2]) {
(Value::Float(log_val), Value::Float(ln_val), Value::Float(exp_val)) => {
assert!((log_val - 3.0).abs() < 0.01, "LOG(1000) should be ~3");
assert!((ln_val - 0.693).abs() < 0.01, "LN(2) should be ~0.693");
assert!((exp_val - 2.718).abs() < 0.01, "EXP(1) should be ~e");
}
other => panic!("Expected Floats, got {:?}", other),
}
}
#[test]
fn test_power_fast_path() {
let (db, _dir) = setup();
let r = row(db
.execute("SELECT POWER(2, 10) FROM t WHERE id = 1")
.unwrap());
match &r[0] {
Value::Float(f) => assert!((f - 1024.0).abs() < 0.01),
Value::Bool(false) => {} other => panic!("Unexpected: {:?}", other),
}
}
#[test]
fn test_sign_fast_path() {
let (db, _dir) = setup();
let r = row(db.execute("SELECT SIGN(-42) FROM t WHERE id = 1").unwrap());
match &r[0] {
Value::Integer(i) => assert_eq!(*i, -1),
Value::Bool(false) => {} other => panic!("Unexpected: {:?}", other),
}
}
#[test]
fn test_mod_fast_path() {
let (db, _dir) = setup();
let r = row(db.execute("SELECT MOD(17, 5) FROM t WHERE id = 1").unwrap());
match &r[0] {
Value::Integer(i) => assert_eq!(*i, 2),
Value::Bool(false) => {} other => panic!("Unexpected: {:?}", other),
}
}
#[test]
fn test_if_function() {
let (db, _dir) = setup();
let r = row(db
.execute("SELECT IF(score > 90, 'A', 'B') FROM t WHERE id = 1")
.unwrap());
match &r[0] {
Value::Text(s) => assert_eq!(s.as_str(), "A"),
Value::Bool(false) => {}
other => panic!("Unexpected: {:?}", other),
}
}
#[test]
fn test_ifnull_fast_path() {
let (db, _dir) = setup();
let r = row(db
.execute("SELECT IFNULL(name, 'N/A') FROM t WHERE id = 1")
.unwrap());
match &r[0] {
Value::Text(s) => assert_eq!(s.as_str(), "Hello World"),
Value::Bool(false) => {} other => panic!("Unexpected: {:?}", other),
}
}
#[test]
fn test_nullif_fast_path() {
let (db, _dir) = setup();
let r = row(db
.execute("SELECT NULLIF(1, 1) FROM t WHERE id = 1")
.unwrap());
match &r[0] {
Value::Null => {} Value::Bool(false) => {} Value::Integer(1) => {} other => panic!("Unexpected: {:?}", other),
}
}
#[test]
fn test_replace_fast_path() {
let (db, _dir) = setup();
let r = row(db
.execute("SELECT REPLACE(name, 'World', 'Rust') FROM t WHERE id = 1")
.unwrap());
match &r[0] {
Value::Text(s) => assert_eq!(s.as_str(), "Hello Rust"),
Value::Bool(false) => {}
other => panic!("Unexpected: {:?}", other),
}
}
#[test]
fn test_substr_fast_path() {
let (db, _dir) = setup();
let r = row(db
.execute("SELECT SUBSTR(name, 1, 5) FROM t WHERE id = 1")
.unwrap());
match &r[0] {
Value::Text(s) => assert_eq!(s.as_str(), "Hello"),
Value::Bool(false) => {}
other => panic!("Unexpected: {:?}", other),
}
}
#[test]
fn test_arithmetic_where() {
let (db, _dir) = setup();
let r = rows(
db.execute("SELECT id FROM t WHERE score * 1.0 > 90")
.unwrap(),
);
assert!(r.len() >= 1);
}
#[test]
fn test_negative_values() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE n (id INT PRIMARY KEY, val INT)")
.unwrap();
db.execute("INSERT INTO n VALUES (1, -100)").unwrap();
db.execute("INSERT INTO n VALUES (2, -50)").unwrap();
db.execute("INSERT INTO n VALUES (3, 50)").unwrap();
let r = rows(
db.execute("SELECT val FROM n WHERE val < 0 ORDER BY val")
.unwrap(),
);
assert_eq!(r.len(), 2);
assert_eq!(&r[0][0], &Value::Integer(-100));
assert_eq!(&r[1][0], &Value::Integer(-50));
}
#[test]
fn test_select_constant_with_table() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE dual (id INT PRIMARY KEY)")
.unwrap();
db.execute("INSERT INTO dual VALUES (1)").unwrap();
let r = row(db.execute("SELECT 1 + 2 FROM dual").unwrap());
assert_eq!(&r[0], &Value::Integer(3));
}
#[test]
fn test_coalesce_with_column() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE c (id INT PRIMARY KEY, val INT)")
.unwrap();
db.execute("INSERT INTO c VALUES (1, NULL)").unwrap();
db.execute("INSERT INTO c VALUES (2, 42)").unwrap();
let r = row(db
.execute("SELECT COALESCE(val, 0) FROM c WHERE id = 1")
.unwrap());
assert!(
!matches!(&r[0], Value::Null),
"COALESCE(NULL, 0) should not be NULL"
);
}