#[allow(unused_imports)]
use scythe_codegen as _codegen;
#[allow(unused_imports)]
use syn as _syn;
#[allow(dead_code)]
fn normalize_whitespace(s: &str) -> String {
s.split_whitespace().collect::<Vec<_>>().join(" ")
}
#[test]
fn test_ambiguous_column() {
let schema_sql = &[
"CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL);",
"CREATE TABLE orders (id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES users(id), total NUMERIC(10,2) NOT NULL);",
];
let query_sql =
"-- @name GetAmbiguous\n-- @returns :many\nSELECT id FROM users u JOIN orders o ON u.id = o.user_id;";
let catalog_result = scythe_core::catalog::Catalog::from_ddl(schema_sql);
if let Ok(catalog) = catalog_result {
let query_result = scythe_core::parser::parse_query(query_sql);
if let Ok(query) = query_result {
let result = scythe_core::analyzer::analyze(&catalog, &query);
assert!(result.is_err(), "expected analysis to fail");
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("AMBIGUOUS_COLUMN"),
"error should contain code {:?}, got: {}",
"AMBIGUOUS_COLUMN",
err_msg
);
assert!(
err_msg.contains("ambiguous"),
"error should contain {:?}, got: {}",
"ambiguous",
err_msg
);
} else {
let err = query_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("AMBIGUOUS_COLUMN"),
"error should contain code {:?}, got: {}",
"AMBIGUOUS_COLUMN",
err_msg
);
assert!(
err_msg.contains("ambiguous"),
"error should contain {:?}, got: {}",
"ambiguous",
err_msg
);
}
} else {
let err = catalog_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("AMBIGUOUS_COLUMN"),
"error should contain code {:?}, got: {}",
"AMBIGUOUS_COLUMN",
err_msg
);
assert!(
err_msg.contains("ambiguous"),
"error should contain {:?}, got: {}",
"ambiguous",
err_msg
);
}
}
#[test]
fn test_error_circular_cte() {
let schema_sql = &["CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL);"];
let query_sql =
"-- @name CircularCte\n-- @returns :many\nWITH RECURSIVE inf AS (SELECT * FROM inf) SELECT * FROM inf";
let catalog_result = scythe_core::catalog::Catalog::from_ddl(schema_sql);
if let Ok(catalog) = catalog_result {
let query_result = scythe_core::parser::parse_query(query_sql);
if let Ok(query) = query_result {
let result = scythe_core::analyzer::analyze(&catalog, &query);
assert!(result.is_err(), "expected analysis to fail");
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_RECURSION"),
"error should contain code {:?}, got: {}",
"INVALID_RECURSION",
err_msg
);
assert!(
err_msg.contains("recursive"),
"error should contain {:?}, got: {}",
"recursive",
err_msg
);
} else {
let err = query_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_RECURSION"),
"error should contain code {:?}, got: {}",
"INVALID_RECURSION",
err_msg
);
assert!(
err_msg.contains("recursive"),
"error should contain {:?}, got: {}",
"recursive",
err_msg
);
}
} else {
let err = catalog_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_RECURSION"),
"error should contain code {:?}, got: {}",
"INVALID_RECURSION",
err_msg
);
assert!(
err_msg.contains("recursive"),
"error should contain {:?}, got: {}",
"recursive",
err_msg
);
}
}
#[test]
fn test_empty_name_value() {
let schema_sql = &["CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL);"];
let query_sql = "-- @name\n-- @returns :many\nSELECT id, name FROM users;";
let catalog_result = scythe_core::catalog::Catalog::from_ddl(schema_sql);
if let Ok(catalog) = catalog_result {
let query_result = scythe_core::parser::parse_query(query_sql);
if let Ok(query) = query_result {
let result = scythe_core::analyzer::analyze(&catalog, &query);
assert!(result.is_err(), "expected analysis to fail");
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_ANNOTATION"),
"error should contain code {:?}, got: {}",
"INVALID_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("@name requires a value"),
"error should contain {:?}, got: {}",
"@name requires a value",
err_msg
);
} else {
let err = query_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_ANNOTATION"),
"error should contain code {:?}, got: {}",
"INVALID_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("@name requires a value"),
"error should contain {:?}, got: {}",
"@name requires a value",
err_msg
);
}
} else {
let err = catalog_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_ANNOTATION"),
"error should contain code {:?}, got: {}",
"INVALID_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("@name requires a value"),
"error should contain {:?}, got: {}",
"@name requires a value",
err_msg
);
}
}
#[test]
fn test_group_by_uses_a_query_alias() {
let schema_sql = &[
"CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL);",
"CREATE TABLE orders (id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES users(id), total NUMERIC);",
];
let query_sql = "-- @name GetUsersWithOrders\n-- @returns :grouped\n-- @group_by u.id\nSELECT u.id, u.name, o.id AS order_id, o.total\nFROM users u\nJOIN orders o ON o.user_id = u.id;";
let catalog_result = scythe_core::catalog::Catalog::from_ddl(schema_sql);
if let Ok(catalog) = catalog_result {
let query_result = scythe_core::parser::parse_query(query_sql);
if let Ok(query) = query_result {
let result = scythe_core::analyzer::analyze(&catalog, &query);
assert!(result.is_err(), "expected analysis to fail");
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_ANNOTATION"),
"error should contain code {:?}, got: {}",
"INVALID_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("matched none of the query's result columns"),
"error should contain {:?}, got: {}",
"matched none of the query's result columns",
err_msg
);
} else {
let err = query_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_ANNOTATION"),
"error should contain code {:?}, got: {}",
"INVALID_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("matched none of the query's result columns"),
"error should contain {:?}, got: {}",
"matched none of the query's result columns",
err_msg
);
}
} else {
let err = catalog_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_ANNOTATION"),
"error should contain code {:?}, got: {}",
"INVALID_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("matched none of the query's result columns"),
"error should contain {:?}, got: {}",
"matched none of the query's result columns",
err_msg
);
}
}
#[test]
fn test_group_by_with_disambiguated_columns() {
let schema_sql = &[
"CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL);",
"CREATE TABLE orders (id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES users(id), total NUMERIC);",
];
let query_sql = "-- @name GetUsersWithOrderIds\n-- @returns :grouped\n-- @group_by users.id\nSELECT u.id, o.id\nFROM users u\nJOIN orders o ON o.user_id = u.id;";
let catalog_result = scythe_core::catalog::Catalog::from_ddl(schema_sql);
if let Ok(catalog) = catalog_result {
let query_result = scythe_core::parser::parse_query(query_sql);
if let Ok(query) = query_result {
let result = scythe_core::analyzer::analyze(&catalog, &query);
assert!(result.is_err(), "expected analysis to fail");
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_ANNOTATION"),
"error should contain code {:?}, got: {}",
"INVALID_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("matched none of the query's result columns"),
"error should contain {:?}, got: {}",
"matched none of the query's result columns",
err_msg
);
} else {
let err = query_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_ANNOTATION"),
"error should contain code {:?}, got: {}",
"INVALID_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("matched none of the query's result columns"),
"error should contain {:?}, got: {}",
"matched none of the query's result columns",
err_msg
);
}
} else {
let err = catalog_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_ANNOTATION"),
"error should contain code {:?}, got: {}",
"INVALID_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("matched none of the query's result columns"),
"error should contain {:?}, got: {}",
"matched none of the query's result columns",
err_msg
);
}
}
#[test]
fn test_invalid_returns_value() {
let schema_sql = &["CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL);"];
let query_sql = "-- @name GetUsers\n-- @returns :invalid\nSELECT id, name FROM users;";
let catalog_result = scythe_core::catalog::Catalog::from_ddl(schema_sql);
if let Ok(catalog) = catalog_result {
let query_result = scythe_core::parser::parse_query(query_sql);
if let Ok(query) = query_result {
let result = scythe_core::analyzer::analyze(&catalog, &query);
assert!(result.is_err(), "expected analysis to fail");
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_ANNOTATION"),
"error should contain code {:?}, got: {}",
"INVALID_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("invalid @returns value"),
"error should contain {:?}, got: {}",
"invalid @returns value",
err_msg
);
} else {
let err = query_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_ANNOTATION"),
"error should contain code {:?}, got: {}",
"INVALID_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("invalid @returns value"),
"error should contain {:?}, got: {}",
"invalid @returns value",
err_msg
);
}
} else {
let err = catalog_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_ANNOTATION"),
"error should contain code {:?}, got: {}",
"INVALID_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("invalid @returns value"),
"error should contain {:?}, got: {}",
"invalid @returns value",
err_msg
);
}
}
#[test]
fn test_missing_name_annotation() {
let schema_sql = &["CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL);"];
let query_sql = "-- @returns :many\nSELECT id, name FROM users;";
let catalog_result = scythe_core::catalog::Catalog::from_ddl(schema_sql);
if let Ok(catalog) = catalog_result {
let query_result = scythe_core::parser::parse_query(query_sql);
if let Ok(query) = query_result {
let result = scythe_core::analyzer::analyze(&catalog, &query);
assert!(result.is_err(), "expected analysis to fail");
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("MISSING_ANNOTATION"),
"error should contain code {:?}, got: {}",
"MISSING_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("missing @name annotation"),
"error should contain {:?}, got: {}",
"missing @name annotation",
err_msg
);
} else {
let err = query_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("MISSING_ANNOTATION"),
"error should contain code {:?}, got: {}",
"MISSING_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("missing @name annotation"),
"error should contain {:?}, got: {}",
"missing @name annotation",
err_msg
);
}
} else {
let err = catalog_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("MISSING_ANNOTATION"),
"error should contain code {:?}, got: {}",
"MISSING_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("missing @name annotation"),
"error should contain {:?}, got: {}",
"missing @name annotation",
err_msg
);
}
}
#[test]
fn test_missing_returns_annotation() {
let schema_sql = &["CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL);"];
let query_sql = "-- @name GetUsers\nSELECT id, name FROM users;";
let catalog_result = scythe_core::catalog::Catalog::from_ddl(schema_sql);
if let Ok(catalog) = catalog_result {
let query_result = scythe_core::parser::parse_query(query_sql);
if let Ok(query) = query_result {
let result = scythe_core::analyzer::analyze(&catalog, &query);
assert!(result.is_err(), "expected analysis to fail");
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("MISSING_ANNOTATION"),
"error should contain code {:?}, got: {}",
"MISSING_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("missing @returns annotation"),
"error should contain {:?}, got: {}",
"missing @returns annotation",
err_msg
);
} else {
let err = query_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("MISSING_ANNOTATION"),
"error should contain code {:?}, got: {}",
"MISSING_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("missing @returns annotation"),
"error should contain {:?}, got: {}",
"missing @returns annotation",
err_msg
);
}
} else {
let err = catalog_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("MISSING_ANNOTATION"),
"error should contain code {:?}, got: {}",
"MISSING_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("missing @returns annotation"),
"error should contain {:?}, got: {}",
"missing @returns annotation",
err_msg
);
}
}
#[test]
fn test_name_value_not_an_identifier() {
let schema_sql = &["CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL);"];
let query_sql = "-- @name get-user\n-- @returns :many\nSELECT id, name FROM users;";
let catalog_result = scythe_core::catalog::Catalog::from_ddl(schema_sql);
if let Ok(catalog) = catalog_result {
let query_result = scythe_core::parser::parse_query(query_sql);
if let Ok(query) = query_result {
let result = scythe_core::analyzer::analyze(&catalog, &query);
assert!(result.is_err(), "expected analysis to fail");
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_ANNOTATION"),
"error should contain code {:?}, got: {}",
"INVALID_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("only ASCII letters, digits and underscores"),
"error should contain {:?}, got: {}",
"only ASCII letters, digits and underscores",
err_msg
);
} else {
let err = query_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_ANNOTATION"),
"error should contain code {:?}, got: {}",
"INVALID_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("only ASCII letters, digits and underscores"),
"error should contain {:?}, got: {}",
"only ASCII letters, digits and underscores",
err_msg
);
}
} else {
let err = catalog_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("INVALID_ANNOTATION"),
"error should contain code {:?}, got: {}",
"INVALID_ANNOTATION",
err_msg
);
assert!(
err_msg.contains("only ASCII letters, digits and underscores"),
"error should contain {:?}, got: {}",
"only ASCII letters, digits and underscores",
err_msg
);
}
}
#[test]
fn test_error_union_column_mismatch() {
let schema_sql = &["CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL);"];
let query_sql =
"-- @name UnionMismatch\n-- @returns :many\nSELECT id, name, email FROM users UNION SELECT id, name FROM users";
let catalog_result = scythe_core::catalog::Catalog::from_ddl(schema_sql);
if let Ok(catalog) = catalog_result {
let query_result = scythe_core::parser::parse_query(query_sql);
if let Ok(query) = query_result {
let result = scythe_core::analyzer::analyze(&catalog, &query);
assert!(result.is_err(), "expected analysis to fail");
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("COLUMN_COUNT_MISMATCH"),
"error should contain code {:?}, got: {}",
"COLUMN_COUNT_MISMATCH",
err_msg
);
assert!(
err_msg.contains("column"),
"error should contain {:?}, got: {}",
"column",
err_msg
);
} else {
let err = query_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("COLUMN_COUNT_MISMATCH"),
"error should contain code {:?}, got: {}",
"COLUMN_COUNT_MISMATCH",
err_msg
);
assert!(
err_msg.contains("column"),
"error should contain {:?}, got: {}",
"column",
err_msg
);
}
} else {
let err = catalog_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("COLUMN_COUNT_MISMATCH"),
"error should contain code {:?}, got: {}",
"COLUMN_COUNT_MISMATCH",
err_msg
);
assert!(
err_msg.contains("column"),
"error should contain {:?}, got: {}",
"column",
err_msg
);
}
}
#[test]
fn test_sql_syntax_error() {
let schema_sql = &["CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL);"];
let query_sql = "-- @name GetUsers\n-- @returns :many\nSELCT * FROM users;";
let catalog_result = scythe_core::catalog::Catalog::from_ddl(schema_sql);
if let Ok(catalog) = catalog_result {
let query_result = scythe_core::parser::parse_query(query_sql);
if let Ok(query) = query_result {
let result = scythe_core::analyzer::analyze(&catalog, &query);
assert!(result.is_err(), "expected analysis to fail");
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("SYNTAX_ERROR"),
"error should contain code {:?}, got: {}",
"SYNTAX_ERROR",
err_msg
);
assert!(
err_msg.contains("syntax error"),
"error should contain {:?}, got: {}",
"syntax error",
err_msg
);
} else {
let err = query_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("SYNTAX_ERROR"),
"error should contain code {:?}, got: {}",
"SYNTAX_ERROR",
err_msg
);
assert!(
err_msg.contains("syntax error"),
"error should contain {:?}, got: {}",
"syntax error",
err_msg
);
}
} else {
let err = catalog_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("SYNTAX_ERROR"),
"error should contain code {:?}, got: {}",
"SYNTAX_ERROR",
err_msg
);
assert!(
err_msg.contains("syntax error"),
"error should contain {:?}, got: {}",
"syntax error",
err_msg
);
}
}
#[test]
fn test_type_mismatch() {
let schema_sql = &[
"CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW());",
];
let query_sql = "-- @name GetTypeMismatch\n-- @returns :many\nSELECT id, name FROM users WHERE id = name;";
let catalog_result = scythe_core::catalog::Catalog::from_ddl(schema_sql);
if let Ok(catalog) = catalog_result {
let query_result = scythe_core::parser::parse_query(query_sql);
if let Ok(query) = query_result {
let result = scythe_core::analyzer::analyze(&catalog, &query);
assert!(result.is_err(), "expected analysis to fail");
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("TYPE_MISMATCH"),
"error should contain code {:?}, got: {}",
"TYPE_MISMATCH",
err_msg
);
assert!(
err_msg.contains("operator does not exist: integer = text"),
"error should contain {:?}, got: {}",
"operator does not exist: integer = text",
err_msg
);
} else {
let err = query_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("TYPE_MISMATCH"),
"error should contain code {:?}, got: {}",
"TYPE_MISMATCH",
err_msg
);
assert!(
err_msg.contains("operator does not exist: integer = text"),
"error should contain {:?}, got: {}",
"operator does not exist: integer = text",
err_msg
);
}
} else {
let err = catalog_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("TYPE_MISMATCH"),
"error should contain code {:?}, got: {}",
"TYPE_MISMATCH",
err_msg
);
assert!(
err_msg.contains("operator does not exist: integer = text"),
"error should contain {:?}, got: {}",
"operator does not exist: integer = text",
err_msg
);
}
}
#[test]
fn test_returning_unknown_column() {
let schema_sql = &["CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL);"];
let query_sql = "-- @name InsertUserReturningUnknown\n-- @returns :one\nINSERT INTO users (name) VALUES ($1) RETURNING nosuchcol;";
let catalog_result = scythe_core::catalog::Catalog::from_ddl(schema_sql);
if let Ok(catalog) = catalog_result {
let query_result = scythe_core::parser::parse_query(query_sql);
if let Ok(query) = query_result {
let result = scythe_core::analyzer::analyze(&catalog, &query);
assert!(result.is_err(), "expected analysis to fail");
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("UNKNOWN_COLUMN"),
"error should contain code {:?}, got: {}",
"UNKNOWN_COLUMN",
err_msg
);
assert!(
err_msg.contains("column \"nosuchcol\" does not exist"),
"error should contain {:?}, got: {}",
"column \"nosuchcol\" does not exist",
err_msg
);
} else {
let err = query_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("UNKNOWN_COLUMN"),
"error should contain code {:?}, got: {}",
"UNKNOWN_COLUMN",
err_msg
);
assert!(
err_msg.contains("column \"nosuchcol\" does not exist"),
"error should contain {:?}, got: {}",
"column \"nosuchcol\" does not exist",
err_msg
);
}
} else {
let err = catalog_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("UNKNOWN_COLUMN"),
"error should contain code {:?}, got: {}",
"UNKNOWN_COLUMN",
err_msg
);
assert!(
err_msg.contains("column \"nosuchcol\" does not exist"),
"error should contain {:?}, got: {}",
"column \"nosuchcol\" does not exist",
err_msg
);
}
}
#[test]
fn test_unknown_column() {
let schema_sql = &["CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL);"];
let query_sql = "-- @name GetNonexistent\n-- @returns :many\nSELECT nonexistent FROM users;";
let catalog_result = scythe_core::catalog::Catalog::from_ddl(schema_sql);
if let Ok(catalog) = catalog_result {
let query_result = scythe_core::parser::parse_query(query_sql);
if let Ok(query) = query_result {
let result = scythe_core::analyzer::analyze(&catalog, &query);
assert!(result.is_err(), "expected analysis to fail");
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("UNKNOWN_COLUMN"),
"error should contain code {:?}, got: {}",
"UNKNOWN_COLUMN",
err_msg
);
assert!(
err_msg.contains("column \"nonexistent\" does not exist"),
"error should contain {:?}, got: {}",
"column \"nonexistent\" does not exist",
err_msg
);
} else {
let err = query_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("UNKNOWN_COLUMN"),
"error should contain code {:?}, got: {}",
"UNKNOWN_COLUMN",
err_msg
);
assert!(
err_msg.contains("column \"nonexistent\" does not exist"),
"error should contain {:?}, got: {}",
"column \"nonexistent\" does not exist",
err_msg
);
}
} else {
let err = catalog_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("UNKNOWN_COLUMN"),
"error should contain code {:?}, got: {}",
"UNKNOWN_COLUMN",
err_msg
);
assert!(
err_msg.contains("column \"nonexistent\" does not exist"),
"error should contain {:?}, got: {}",
"column \"nonexistent\" does not exist",
err_msg
);
}
}
#[test]
fn test_error_unknown_function() {
let schema_sql = &["CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL);"];
let query_sql = "-- @name UnknownFunc\n-- @returns :many\nSELECT id, nonexistent_func(name) AS result FROM users";
let catalog_result = scythe_core::catalog::Catalog::from_ddl(schema_sql);
if let Ok(catalog) = catalog_result {
let query_result = scythe_core::parser::parse_query(query_sql);
if let Ok(query) = query_result {
let result = scythe_core::analyzer::analyze(&catalog, &query);
assert!(result.is_err(), "expected analysis to fail");
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("UNKNOWN_FUNCTION"),
"error should contain code {:?}, got: {}",
"UNKNOWN_FUNCTION",
err_msg
);
assert!(
err_msg.contains("nonexistent_func"),
"error should contain {:?}, got: {}",
"nonexistent_func",
err_msg
);
} else {
let err = query_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("UNKNOWN_FUNCTION"),
"error should contain code {:?}, got: {}",
"UNKNOWN_FUNCTION",
err_msg
);
assert!(
err_msg.contains("nonexistent_func"),
"error should contain {:?}, got: {}",
"nonexistent_func",
err_msg
);
}
} else {
let err = catalog_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("UNKNOWN_FUNCTION"),
"error should contain code {:?}, got: {}",
"UNKNOWN_FUNCTION",
err_msg
);
assert!(
err_msg.contains("nonexistent_func"),
"error should contain {:?}, got: {}",
"nonexistent_func",
err_msg
);
}
}
#[test]
fn test_returning_unknown_function() {
let schema_sql = &["CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL);"];
let query_sql = "-- @name InsertUserReturningWeird\n-- @returns :one\nINSERT INTO users (name) VALUES ($1) RETURNING my_weird_func(id);";
let catalog_result = scythe_core::catalog::Catalog::from_ddl(schema_sql);
if let Ok(catalog) = catalog_result {
let query_result = scythe_core::parser::parse_query(query_sql);
if let Ok(query) = query_result {
let result = scythe_core::analyzer::analyze(&catalog, &query);
assert!(result.is_err(), "expected analysis to fail");
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("UNKNOWN_FUNCTION"),
"error should contain code {:?}, got: {}",
"UNKNOWN_FUNCTION",
err_msg
);
assert!(
err_msg.contains("my_weird_func"),
"error should contain {:?}, got: {}",
"my_weird_func",
err_msg
);
} else {
let err = query_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("UNKNOWN_FUNCTION"),
"error should contain code {:?}, got: {}",
"UNKNOWN_FUNCTION",
err_msg
);
assert!(
err_msg.contains("my_weird_func"),
"error should contain {:?}, got: {}",
"my_weird_func",
err_msg
);
}
} else {
let err = catalog_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("UNKNOWN_FUNCTION"),
"error should contain code {:?}, got: {}",
"UNKNOWN_FUNCTION",
err_msg
);
assert!(
err_msg.contains("my_weird_func"),
"error should contain {:?}, got: {}",
"my_weird_func",
err_msg
);
}
}
#[test]
fn test_unknown_table() {
let schema_sql = &["CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL);"];
let query_sql = "-- @name GetFromNonexistent\n-- @returns :many\nSELECT * FROM nonexistent;";
let catalog_result = scythe_core::catalog::Catalog::from_ddl(schema_sql);
if let Ok(catalog) = catalog_result {
let query_result = scythe_core::parser::parse_query(query_sql);
if let Ok(query) = query_result {
let result = scythe_core::analyzer::analyze(&catalog, &query);
assert!(result.is_err(), "expected analysis to fail");
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("UNKNOWN_TABLE"),
"error should contain code {:?}, got: {}",
"UNKNOWN_TABLE",
err_msg
);
assert!(
err_msg.contains("relation \"nonexistent\" does not exist"),
"error should contain {:?}, got: {}",
"relation \"nonexistent\" does not exist",
err_msg
);
} else {
let err = query_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("UNKNOWN_TABLE"),
"error should contain code {:?}, got: {}",
"UNKNOWN_TABLE",
err_msg
);
assert!(
err_msg.contains("relation \"nonexistent\" does not exist"),
"error should contain {:?}, got: {}",
"relation \"nonexistent\" does not exist",
err_msg
);
}
} else {
let err = catalog_result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("UNKNOWN_TABLE"),
"error should contain code {:?}, got: {}",
"UNKNOWN_TABLE",
err_msg
);
assert!(
err_msg.contains("relation \"nonexistent\" does not exist"),
"error should contain {:?}, got: {}",
"relation \"nonexistent\" does not exist",
err_msg
);
}
}