use spg_engine::{Engine, QueryResult};
use spg_storage::Value;
fn one_row(r: QueryResult) -> Vec<Value<'static>> {
match r {
QueryResult::Rows { rows, .. } => {
assert_eq!(rows.len(), 1);
rows.into_iter().next().unwrap().values
}
_ => panic!(),
}
}
fn text(e: &mut Engine, sql: &str) -> String {
let row = one_row(
e.execute(sql)
.unwrap_or_else(|err| panic!("{sql}: {err:?}")),
);
match &row[0] {
Value::Text(s) => s.to_string(),
other => panic!("expected Text, got {other:?}"),
}
}
#[test]
fn left_basic() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT left('hello', 3)"), "hel");
}
#[test]
fn left_n_equals_length_returns_whole() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT left('hello', 5)"), "hello");
}
#[test]
fn left_n_greater_than_length_returns_whole() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT left('hello', 10)"), "hello");
}
#[test]
fn left_n_zero_returns_empty() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT left('hello', 0)"), "");
}
#[test]
fn left_negative_n_drops_last_n_chars() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT left('hello', -2)"), "hel");
}
#[test]
fn left_negative_n_equals_length_returns_empty() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT left('hello', -5)"), "");
}
#[test]
fn left_negative_n_beyond_length_returns_empty() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT left('hello', -100)"), "");
}
#[test]
fn right_basic() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT right('hello', 3)"), "llo");
}
#[test]
fn right_n_equals_length_returns_whole() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT right('hello', 5)"), "hello");
}
#[test]
fn right_n_greater_than_length_returns_whole() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT right('hello', 10)"), "hello");
}
#[test]
fn right_n_zero_returns_empty() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT right('hello', 0)"), "");
}
#[test]
fn right_negative_n_drops_first_n_chars() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT right('hello', -2)"), "llo");
}
#[test]
fn right_negative_n_equals_length_returns_empty() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT right('hello', -5)"), "");
}
#[test]
fn right_negative_n_beyond_length_returns_empty() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT right('hello', -100)"), "");
}
#[test]
fn left_empty_input_returns_empty() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT left('', 3)"), "");
}
#[test]
fn right_empty_input_returns_empty() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT right('', 3)"), "");
}
#[test]
fn left_unicode_codepoint_counted() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT left('日本語', 2)"), "日本");
}
#[test]
fn right_unicode_codepoint_counted() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT right('日本語', 2)"), "本語");
}
#[test]
fn left_unicode_negative_n() {
let mut e = Engine::new();
assert_eq!(text(&mut e, "SELECT left('日本語', -1)"), "日本");
}
#[test]
fn left_null_string_returns_null() {
let mut e = Engine::new();
let row = one_row(e.execute("SELECT left(NULL, 3)").unwrap());
assert_eq!(row[0], Value::Null);
}
#[test]
fn left_null_n_returns_null() {
let mut e = Engine::new();
let row = one_row(e.execute("SELECT left('a', NULL)").unwrap());
assert_eq!(row[0], Value::Null);
}
#[test]
fn right_null_string_returns_null() {
let mut e = Engine::new();
let row = one_row(e.execute("SELECT right(NULL, 3)").unwrap());
assert_eq!(row[0], Value::Null);
}
#[test]
fn right_null_n_returns_null() {
let mut e = Engine::new();
let row = one_row(e.execute("SELECT right('a', NULL)").unwrap());
assert_eq!(row[0], Value::Null);
}
#[test]
fn left_one_arg_errors() {
let mut e = Engine::new();
assert!(e.execute("SELECT left('a')").is_err());
}
#[test]
fn left_three_args_errors() {
let mut e = Engine::new();
assert!(e.execute("SELECT left('a', 1, 2)").is_err());
}
#[test]
fn left_n_non_integer_errors() {
let mut e = Engine::new();
assert!(e.execute("SELECT left('a', 'foo')").is_err());
}
#[test]
fn left_numeric_input_coerced() {
let mut e = Engine::new();
let m = e
.execute("SELECT left(12345, 3)")
.expect_err("PG has no left(integer, integer)")
.to_string();
assert!(
m.contains("function left(integer, integer) does not exist"),
"{m}"
);
}
#[test]
fn left_inside_where() {
let mut e = Engine::new();
e.execute("CREATE TABLE u (id INT NOT NULL, code TEXT NOT NULL)")
.unwrap();
e.execute("INSERT INTO u VALUES (1, 'US-12345'), (2, 'EU-67890')")
.unwrap();
let r = e
.execute("SELECT id FROM u WHERE left(code, 2) = 'US'")
.unwrap();
let QueryResult::Rows { rows, .. } = r else {
panic!()
};
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].values[0], Value::Int(1));
}
#[test]
fn right_inside_insert_values() {
let mut e = Engine::new();
e.execute("CREATE TABLE u (last3 TEXT NOT NULL)").unwrap();
e.execute("INSERT INTO u VALUES (right('abc-XYZ', 3))")
.unwrap();
let row = one_row(e.execute("SELECT last3 FROM u").unwrap());
assert_eq!(row[0], Value::text("XYZ"));
}
#[test]
fn left_column_type_is_text() {
let mut e = Engine::new();
let r = e.execute("SELECT left('a', 1)").unwrap();
let QueryResult::Rows { columns, .. } = r else {
panic!()
};
assert_eq!(columns[0].ty, spg_storage::DataType::Text);
}
#[test]
fn left_does_not_collide_with_left_join_keyword() {
let mut e = Engine::new();
e.execute("CREATE TABLE a (id INT NOT NULL)").unwrap();
e.execute("CREATE TABLE b (id INT NOT NULL, label TEXT NOT NULL)")
.unwrap();
e.execute("INSERT INTO a VALUES (1)").unwrap();
e.execute("INSERT INTO b VALUES (1, 'hello')").unwrap();
let r = e
.execute(
"SELECT a.id, left(b.label, 3) \
FROM a LEFT JOIN b ON a.id = b.id",
)
.unwrap();
let QueryResult::Rows { rows, .. } = r else {
panic!()
};
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].values[1], Value::text("hel"));
}