#![cfg(feature = "sqlite")]
use sqlx::sqlite::SqlitePoolOptions;
async fn columns_of(query: &str, ddl: &[&str]) -> dataprof_db::QueryColumns {
columns_result_of(query, ddl)
.await
.expect("unique query columns")
}
async fn columns_result_of(
query: &str,
ddl: &[&str],
) -> Result<dataprof_db::QueryColumns, dataprof_core::DataProfilerError> {
let pool = SqlitePoolOptions::new()
.connect("sqlite::memory:")
.await
.expect("in-memory sqlite");
for stmt in ddl {
sqlx::query(stmt).execute(&pool).await.expect("ddl");
}
let rows = sqlx::query(query).fetch_all(&pool).await.expect("query");
dataprof_db::process_rows_to_columns!(rows)
}
#[tokio::test]
async fn numeric_and_boolean_columns_survive_decoding() {
let cols = columns_of(
"SELECT * FROM t ORDER BY i",
&[
"CREATE TABLE t (i INTEGER, r REAL, b BOOLEAN, s TEXT)",
"INSERT INTO t VALUES (42, 10.5, 1, 'hi')",
"INSERT INTO t VALUES (-7, -0.25, 0, 'there')",
],
)
.await;
assert_eq!(cols["i"], vec!["-7", "42"]);
assert_eq!(cols["r"], vec!["-0.25", "10.5"]);
assert_eq!(cols["b"], vec!["0", "1"]);
assert_eq!(cols["s"], vec!["there", "hi"]);
}
#[tokio::test]
async fn sql_null_is_still_empty_for_every_type() {
let cols = columns_of(
"SELECT * FROM t",
&[
"CREATE TABLE t (i INTEGER, r REAL, s TEXT)",
"INSERT INTO t VALUES (NULL, NULL, NULL)",
],
)
.await;
assert_eq!(cols["i"], vec![""]);
assert_eq!(cols["r"], vec![""]);
assert_eq!(cols["s"], vec![""]);
}
#[tokio::test]
async fn nulls_do_not_swallow_neighbouring_values() {
let cols = columns_of(
"SELECT * FROM t ORDER BY rowid",
&[
"CREATE TABLE t (amt REAL)",
"INSERT INTO t VALUES (100.0), (NULL), (1.0), (NULL), (2.0)",
],
)
.await;
assert_eq!(cols["amt"], vec!["100.0", "", "1.0", "", "2.0"]);
}
#[tokio::test]
async fn integral_floats_keep_their_decimal_point() {
let cols = columns_of(
"SELECT * FROM t ORDER BY rowid",
&[
"CREATE TABLE t (r REAL)",
"INSERT INTO t VALUES (100.0), (1.0), (2.5)",
],
)
.await;
assert_eq!(cols["r"], vec!["100.0", "1.0", "2.5"]);
}
#[tokio::test]
async fn large_integers_are_not_truncated_through_a_float_arm() {
let cols = columns_of(
"SELECT * FROM t",
&[
"CREATE TABLE t (big INTEGER)",
"INSERT INTO t VALUES (9007199254740993)",
],
)
.await;
assert_eq!(cols["big"], vec!["9007199254740993"]);
}
#[tokio::test]
async fn duplicate_query_aliases_are_rejected_before_columns_merge() {
let error = columns_result_of(
"SELECT a AS x, b AS x FROM t",
&[
"CREATE TABLE t (a INTEGER, b INTEGER)",
"INSERT INTO t VALUES (1, 10), (2, 20)",
],
)
.await
.expect_err("duplicate aliases must not merge");
assert!(error.to_string().contains("Duplicate column name"));
assert!(error.to_string().contains('x'));
}