use crate::QueryEngine;
use crate::QueryError;
use crate::key_encoder::encode_key;
use crate::schema::{ColumnDef, DataType, SchemaBuilder};
use crate::tests::MockStore;
use crate::value::Value;
use kimberlite_store::TableId;
#[test]
fn test_type_mismatch_in_where() {
let schema = SchemaBuilder::new()
.table(
"users",
TableId::new(1),
vec![
ColumnDef::new("id", DataType::BigInt).not_null(),
ColumnDef::new("name", DataType::Text).not_null(),
],
vec!["id".into()],
)
.build();
let engine = QueryEngine::new(schema);
let mut store = MockStore::new();
let result = engine.query(&mut store, "SELECT * FROM users WHERE id = 'Alice'", &[]);
assert!(result.is_ok());
assert_eq!(result.unwrap().rows.len(), 0);
}
#[test]
fn test_column_not_found() {
let schema = SchemaBuilder::new()
.table(
"users",
TableId::new(1),
vec![
ColumnDef::new("id", DataType::BigInt).not_null(),
ColumnDef::new("name", DataType::Text).not_null(),
],
vec!["id".into()],
)
.build();
let engine = QueryEngine::new(schema);
let mut store = MockStore::new();
let result = engine.query(&mut store, "SELECT nonexistent_column FROM users", &[]);
assert!(result.is_err());
match result.unwrap_err() {
QueryError::ColumnNotFound { table, column } => {
assert_eq!(table, "users");
assert_eq!(column, "nonexistent_column");
}
other => panic!("Expected ColumnNotFound, got {other:?}"),
}
}
#[test]
fn test_column_not_found_in_where() {
let schema = SchemaBuilder::new()
.table(
"users",
TableId::new(1),
vec![
ColumnDef::new("id", DataType::BigInt).not_null(),
ColumnDef::new("name", DataType::Text).not_null(),
],
vec!["id".into()],
)
.build();
let engine = QueryEngine::new(schema);
let mut store = MockStore::new();
let result = engine.query(
&mut store,
"SELECT * FROM users WHERE nonexistent_column = 'test'",
&[],
);
assert!(result.is_err());
match result.unwrap_err() {
QueryError::ColumnNotFound { table, column } => {
assert_eq!(table, "users");
assert_eq!(column, "nonexistent_column");
}
other => panic!("Expected ColumnNotFound, got {other:?}"),
}
}
#[test]
fn test_table_not_found() {
let schema = SchemaBuilder::new()
.table(
"users",
TableId::new(1),
vec![
ColumnDef::new("id", DataType::BigInt).not_null(),
ColumnDef::new("name", DataType::Text).not_null(),
],
vec!["id".into()],
)
.build();
let engine = QueryEngine::new(schema);
let mut store = MockStore::new();
let result = engine.query(&mut store, "SELECT * FROM nonexistent_table", &[]);
assert!(result.is_err());
match result.unwrap_err() {
QueryError::TableNotFound(name) => {
assert_eq!(name, "nonexistent_table");
}
other => panic!("Expected TableNotFound, got {other:?}"),
}
}
#[test]
fn test_parameter_out_of_bounds() {
let schema = SchemaBuilder::new()
.table(
"users",
TableId::new(1),
vec![
ColumnDef::new("id", DataType::BigInt).not_null(),
ColumnDef::new("name", DataType::Text).not_null(),
],
vec!["id".into()],
)
.build();
let engine = QueryEngine::new(schema);
let mut store = MockStore::new();
let result = engine.query(
&mut store,
"SELECT * FROM users WHERE id = $5",
&[Value::BigInt(1), Value::BigInt(2), Value::BigInt(3)],
);
assert!(result.is_err());
match result.unwrap_err() {
QueryError::ParameterNotFound(index) => {
assert_eq!(index, 5);
}
other => panic!("Expected ParameterNotFound, got {other:?}"),
}
}
#[test]
fn test_invalid_sql_syntax() {
let schema = SchemaBuilder::new()
.table(
"users",
TableId::new(1),
vec![
ColumnDef::new("id", DataType::BigInt).not_null(),
ColumnDef::new("name", DataType::Text).not_null(),
],
vec!["id".into()],
)
.build();
let engine = QueryEngine::new(schema);
let mut store = MockStore::new();
let result = engine.query(&mut store, "SELECT * FROM", &[]);
assert!(result.is_err());
match result.unwrap_err() {
QueryError::ParseError(_) => {
}
other => panic!("Expected ParseError, got {other:?}"),
}
}
#[test]
fn test_aggregate_without_group_by_with_non_aggregate() {
let schema = SchemaBuilder::new()
.table(
"users",
TableId::new(1),
vec![
ColumnDef::new("id", DataType::BigInt).not_null(),
ColumnDef::new("name", DataType::Text).not_null(),
ColumnDef::new("age", DataType::BigInt).not_null(),
],
vec!["id".into()],
)
.build();
let engine = QueryEngine::new(schema);
let mut store = MockStore::new();
let result = engine.query(&mut store, "SELECT name, COUNT(*) FROM users", &[]);
assert!(result.is_ok());
}
#[test]
fn test_order_by_column_not_in_select() {
let schema = SchemaBuilder::new()
.table(
"users",
TableId::new(1),
vec![
ColumnDef::new("id", DataType::BigInt).not_null(),
ColumnDef::new("name", DataType::Text).not_null(),
ColumnDef::new("age", DataType::BigInt).not_null(),
],
vec!["id".into()],
)
.build();
let engine = QueryEngine::new(schema);
let mut store = MockStore::new();
store.insert_json(
TableId::new(1),
encode_key(&[Value::BigInt(1)]),
&serde_json::json!({"id": 1, "name": "Alice", "age": 30}),
);
let result = engine.query(&mut store, "SELECT name FROM users ORDER BY age", &[]);
assert!(result.is_ok());
}
#[test]
fn test_empty_in_list() {
let schema = SchemaBuilder::new()
.table(
"users",
TableId::new(1),
vec![
ColumnDef::new("id", DataType::BigInt).not_null(),
ColumnDef::new("name", DataType::Text).not_null(),
],
vec!["id".into()],
)
.build();
let engine = QueryEngine::new(schema);
let mut store = MockStore::new();
let result = engine.query(&mut store, "SELECT * FROM users WHERE id IN ()", &[]);
if result.is_err() {
assert!(matches!(result.unwrap_err(), QueryError::ParseError(_)));
} else {
assert_eq!(result.unwrap().rows.len(), 0);
}
}
#[test]
fn test_limit_zero() {
let schema = SchemaBuilder::new()
.table(
"users",
TableId::new(1),
vec![
ColumnDef::new("id", DataType::BigInt).not_null(),
ColumnDef::new("name", DataType::Text).not_null(),
],
vec!["id".into()],
)
.build();
let engine = QueryEngine::new(schema);
let mut store = MockStore::new();
store.insert_json(
TableId::new(1),
encode_key(&[Value::BigInt(1)]),
&serde_json::json!({"id": 1, "name": "Alice"}),
);
let result = engine
.query(&mut store, "SELECT * FROM users LIMIT 0", &[])
.unwrap();
assert_eq!(result.rows.len(), 0);
}
#[test]
fn test_negative_limit() {
let schema = SchemaBuilder::new()
.table(
"users",
TableId::new(1),
vec![
ColumnDef::new("id", DataType::BigInt).not_null(),
ColumnDef::new("name", DataType::Text).not_null(),
],
vec!["id".into()],
)
.build();
let engine = QueryEngine::new(schema);
let mut store = MockStore::new();
let result = engine.query(&mut store, "SELECT * FROM users LIMIT -1", &[]);
assert!(result.is_err());
}
#[test]
fn test_duplicate_column_in_select() {
let schema = SchemaBuilder::new()
.table(
"users",
TableId::new(1),
vec![
ColumnDef::new("id", DataType::BigInt).not_null(),
ColumnDef::new("name", DataType::Text).not_null(),
],
vec!["id".into()],
)
.build();
let engine = QueryEngine::new(schema);
let mut store = MockStore::new();
store.insert_json(
TableId::new(1),
encode_key(&[Value::BigInt(1)]),
&serde_json::json!({"id": 1, "name": "Alice"}),
);
let result = engine.query(&mut store, "SELECT id, id FROM users", &[]);
assert!(result.is_ok());
let result = result.unwrap();
assert_eq!(result.rows[0].len(), 2); assert_eq!(result.rows[0][0], result.rows[0][1]); }
#[test]
fn test_select_star_with_explicit_columns() {
let schema = SchemaBuilder::new()
.table(
"users",
TableId::new(1),
vec![
ColumnDef::new("id", DataType::BigInt).not_null(),
ColumnDef::new("name", DataType::Text).not_null(),
],
vec!["id".into()],
)
.build();
let engine = QueryEngine::new(schema);
let mut store = MockStore::new();
store.insert_json(
TableId::new(1),
encode_key(&[Value::BigInt(1)]),
&serde_json::json!({"id": 1, "name": "Alice"}),
);
let result = engine.query(&mut store, "SELECT *, id FROM users", &[]);
assert!(result.is_ok());
let result = result.unwrap();
assert!(result.rows[0].len() == 2 || result.rows[0].len() == 3);
}