#[cfg(feature = "js")]
use oxibase::Database;
#[cfg(feature = "js")]
#[test]
fn test_function_persistence_basic() {
let db = Database::open("memory://test").unwrap();
db.execute(
"CREATE FUNCTION test_add(a INTEGER, b INTEGER) RETURNS INTEGER LANGUAGE DENO AS 'return a + b;'",
(),
).expect("Failed to create function");
let call_result: i64 = db
.query_one("SELECT test_add(5, 3)", ())
.expect("Failed to call function");
assert_eq!(call_result, 8);
let result = db
.query("SHOW FUNCTIONS", ())
.expect("Failed to execute SHOW FUNCTIONS");
let rows: Vec<_> = result.collect();
assert_eq!(rows.len(), 1);
if let Some(Ok(row)) = rows.first() {
let name: String = row.get(0).expect("Failed to get name");
assert_eq!(name, "TEST_ADD");
}
let result: i64 = db
.query_one("SELECT test_add(5, 3)", ())
.expect("Failed to call function");
assert_eq!(result, 8);
}
#[cfg(feature = "js")]
#[test]
fn test_function_persistence_restart() {
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
let db_path = temp_dir
.path()
.join("test.db")
.to_str()
.unwrap()
.to_string();
{
let db = Database::open(&format!("file://{}", db_path)).expect("Failed to open database");
db.execute(
"CREATE FUNCTION persistent_func(x INTEGER) RETURNS INTEGER LANGUAGE DENO AS 'return x * 2;'",
(),
).expect("Failed to create function");
let result: i64 = db
.query_one("SELECT persistent_func(5)", ())
.expect("Failed to call function");
assert_eq!(result, 10);
}
{
let db = Database::open(&format!("file://{}", db_path)).expect("Failed to reopen database");
let result: i64 = db
.query_one("SELECT persistent_func(7)", ())
.expect("Failed to call function after restart");
assert_eq!(result, 14);
}
}
#[cfg(feature = "js")]
#[test]
fn test_multiple_functions_persistence() {
let db = Database::open("memory://multi_func").expect("Failed to create database");
db.execute(
"CREATE FUNCTION func1(a TEXT) RETURNS TEXT LANGUAGE DENO AS 'return `Hello ${a}`;'",
(),
)
.expect("Failed to create func1");
db.execute(
"CREATE FUNCTION func2(x INTEGER, y INTEGER) RETURNS INTEGER LANGUAGE DENO AS 'return Math.max(x, y);'",
(),
).expect("Failed to create func2");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM system.functions", ())
.expect("Failed to count functions");
assert_eq!(count, 2);
let result1: String = db
.query_one("SELECT func1('World')", ())
.expect("Failed to call func1");
assert_eq!(result1, "Hello World");
let result2: i64 = db
.query_one("SELECT func2(3, 7)", ())
.expect("Failed to call func2");
assert_eq!(result2, 7);
}
#[cfg(feature = "js")]
#[test]
fn test_function_if_not_exists_persistence() {
let db = Database::open("memory://if_not_exists").expect("Failed to create database");
db.execute(
"CREATE FUNCTION IF NOT EXISTS conditional_func() RETURNS TEXT LANGUAGE DENO AS 'return \"created\";'",
(),
).expect("Failed to create function first time");
db.execute(
"CREATE FUNCTION IF NOT EXISTS conditional_func() RETURNS TEXT LANGUAGE DENO AS 'return \"duplicate\";'",
(),
).expect("Failed to create function second time");
let result: String = db
.query_one("SELECT conditional_func()", ())
.expect("Failed to call function");
assert_eq!(result, "created");
}
#[cfg(feature = "js")]
#[test]
fn test_functions_table_starts_empty() {
let db = Database::open("memory://empty_table").expect("Failed to create database");
let result = db.query_one::<i64, _>("SELECT COUNT(*) FROM system.functions", ());
assert!(result.is_err(), "System table should not exist initially");
db.execute(
"CREATE FUNCTION temp_func() RETURNS INTEGER LANGUAGE DENO AS 'return 42;'",
(),
)
.expect("Failed to create function");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM system.functions", ())
.expect("Failed to count functions");
assert_eq!(count, 1);
}
#[cfg(feature = "js")]
#[test]
fn test_show_functions() {
let db = Database::open("memory://show_functions").expect("Failed to create database");
let result = db
.query("SHOW FUNCTIONS", ())
.expect("Failed to execute SHOW FUNCTIONS");
let rows: Vec<_> = result.collect();
assert_eq!(rows.len(), 0);
db.execute(
"CREATE FUNCTION add_nums(a INTEGER, b INTEGER) RETURNS INTEGER LANGUAGE DENO AS 'return a + b;'",
(),
)
.expect("Failed to create function");
let result = db
.query("SELECT name FROM system.functions", ())
.expect("Failed to select functions after first");
let rows: Vec<_> = result.map(|r| r.unwrap()).collect();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].get::<String>(0).unwrap(), "ADD_NUMS");
db.execute(
"CREATE FUNCTION greet(name TEXT) RETURNS TEXT LANGUAGE DENO AS 'return `Hello, ${name}!`;'",
(),
)
.expect("Failed to create function");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM system.functions", ())
.expect("Failed to count functions");
assert_eq!(count, 2);
let result = db
.query("SHOW FUNCTIONS", ())
.expect("Failed to execute SHOW FUNCTIONS");
let rows: Vec<_> = result.map(|r| r.unwrap()).collect();
assert_eq!(rows.len(), 2);
let row = &rows[0];
assert_eq!(row.get::<String>(0).unwrap(), "ADD_NUMS");
assert_eq!(row.get::<String>(1).unwrap(), "(a INTEGER, b INTEGER)");
assert_eq!(row.get::<String>(2).unwrap(), "INTEGER");
assert_eq!(row.get::<String>(3).unwrap(), "DENO");
assert!(row.get::<String>(4).unwrap().contains("a + b"));
let row = &rows[1];
assert_eq!(row.get::<String>(0).unwrap(), "GREET");
assert_eq!(row.get::<String>(1).unwrap(), "(name TEXT)");
assert_eq!(row.get::<String>(2).unwrap(), "TEXT");
assert_eq!(row.get::<String>(3).unwrap(), "DENO");
assert!(row.get::<String>(4).unwrap().contains("Hello"));
}
#[cfg(feature = "js")]
#[test]
fn test_drop_function_basic() {
let db = Database::open("memory://drop_basic").expect("Failed to create database");
db.execute(
"CREATE FUNCTION drop_me(x INTEGER) RETURNS INTEGER LANGUAGE DENO AS 'return x * 3;'",
(),
)
.expect("Failed to create function");
let result: i64 = db
.query_one("SELECT drop_me(4)", ())
.expect("Failed to call function");
assert_eq!(result, 12);
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM system.functions WHERE name = 'DROP_ME'",
(),
)
.expect("Failed to count functions");
assert_eq!(count, 1);
db.execute("DROP FUNCTION drop_me", ())
.expect("Failed to drop function");
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM system.functions WHERE name = 'DROP_ME'",
(),
)
.expect("Failed to count functions after drop");
assert_eq!(count, 0);
let result = db.query_one::<i64, _>("SELECT drop_me(4)", ());
assert!(result.is_err(), "Function should not exist after DROP");
}
#[cfg(feature = "js")]
#[test]
fn test_drop_function_if_exists_exists() {
let db = Database::open("memory://drop_if_exists").expect("Failed to create database");
db.execute(
"CREATE FUNCTION if_exists_func() RETURNS TEXT LANGUAGE DENO AS 'return \"exists\";'",
(),
)
.expect("Failed to create function");
db.execute("DROP FUNCTION IF EXISTS if_exists_func", ())
.expect("Failed to drop function with IF EXISTS");
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM system.functions WHERE name = 'IF_EXISTS_FUNC'",
(),
)
.expect("Failed to count functions");
assert_eq!(count, 0);
}
#[cfg(feature = "js")]
#[test]
fn test_drop_function_if_exists_not_exists() {
let db = Database::open("memory://drop_if_not_exists").expect("Failed to create database");
db.execute("DROP FUNCTION IF EXISTS nonexistent_func", ())
.expect("DROP IF EXISTS should not fail for non-existent function");
let count_result: Result<i64, _> = db.query_one("SELECT COUNT(*) FROM system.functions", ());
match count_result {
Ok(count) => assert_eq!(count, 0),
Err(_) => {
}
}
}
#[cfg(feature = "js")]
#[test]
fn test_drop_function_not_exists_error() {
let db = Database::open("memory://drop_error").expect("Failed to create database");
let result = db.execute("DROP FUNCTION nonexistent_func", ());
assert!(
result.is_err(),
"DROP without IF EXISTS should fail for non-existent function"
);
}
#[cfg(feature = "js")]
#[test]
fn test_drop_function_registry_cleanup() {
let db = Database::open("memory://drop_registry").expect("Failed to create database");
db.execute(
"CREATE FUNCTION keep_func() RETURNS INTEGER LANGUAGE DENO AS 'return 1;'",
(),
)
.expect("Failed to create keep_func");
db.execute(
"CREATE FUNCTION remove_func() RETURNS INTEGER LANGUAGE DENO AS 'return 2;'",
(),
)
.expect("Failed to create remove_func");
let result1: i64 = db
.query_one("SELECT keep_func()", ())
.expect("Failed to call keep_func");
assert_eq!(result1, 1);
let result2: i64 = db
.query_one("SELECT remove_func()", ())
.expect("Failed to call remove_func");
assert_eq!(result2, 2);
db.execute("DROP FUNCTION remove_func", ())
.expect("Failed to drop remove_func");
let result1: i64 = db
.query_one("SELECT keep_func()", ())
.expect("keep_func should still work");
assert_eq!(result1, 1);
let result = db.query_one::<i64, _>("SELECT remove_func()", ());
assert!(
result.is_err(),
"remove_func should not be callable after DROP"
);
let count: i64 = db
.query_one("SELECT COUNT(*) FROM system.functions", ())
.expect("Failed to count functions");
assert_eq!(count, 1);
let name: String = db
.query_one("SELECT name FROM system.functions", ())
.expect("Failed to get remaining function name");
assert_eq!(name, "KEEP_FUNC");
}
#[cfg(feature = "js")]
#[test]
fn test_drop_function_persistence_restart() {
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
let db_path = temp_dir
.path()
.join("test_drop.db")
.to_str()
.unwrap()
.to_string();
{
let db = Database::open(&format!("file://{}", db_path)).expect("Failed to open database");
db.execute(
"CREATE FUNCTION temp_drop_func(x INTEGER) RETURNS INTEGER LANGUAGE DENO AS 'return x + 100;'",
(),
).expect("Failed to create function");
let result: i64 = db
.query_one("SELECT temp_drop_func(5)", ())
.expect("Failed to call function");
assert_eq!(result, 105);
db.execute("DROP FUNCTION temp_drop_func", ())
.expect("Failed to drop function");
}
{
let db = Database::open(&format!("file://{}", db_path)).expect("Failed to reopen database");
let result = db.query_one::<i64, _>("SELECT temp_drop_func(5)", ());
assert!(
result.is_err(),
"Function should remain dropped after restart"
);
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM system.functions WHERE name = 'TEMP_DROP_FUNC'",
(),
)
.expect("Failed to count functions after restart");
assert_eq!(count, 0);
}
}
#[cfg(feature = "js")]
#[test]
fn test_create_function_schema_qualified() {
let db = Database::open("memory://create_schema_func").expect("Failed to create database");
db.execute(
"CREATE FUNCTION myschema.add_nums(a INTEGER, b INTEGER) RETURNS INTEGER LANGUAGE DENO AS 'return a + b;'",
(),
).expect("Failed to create schema-qualified function");
let result: i64 = db
.query_one("SELECT myschema.add_nums(3, 4)", ())
.expect("Failed to call schema-qualified function");
assert_eq!(result, 7);
let schema: Option<String> = db
.query_one(
"SELECT schema FROM system.functions WHERE name = 'ADD_NUMS'",
(),
)
.expect("Failed to query schema");
assert_eq!(schema, Some("MYSCHEMA".to_string()));
let name: String = db
.query_one(
"SELECT name FROM system.functions WHERE name = 'ADD_NUMS'",
(),
)
.expect("Failed to query name");
assert_eq!(name, "ADD_NUMS");
}
#[cfg(feature = "js")]
#[test]
fn test_drop_function_schema_qualified() {
let db = Database::open("memory://drop_schema_func").expect("Failed to create database");
db.execute(
"CREATE FUNCTION test_schema.multiply(x INTEGER, y INTEGER) RETURNS INTEGER LANGUAGE DENO AS 'return x * y;'",
(),
).expect("Failed to create schema-qualified function");
let result: i64 = db
.query_one("SELECT test_schema.multiply(5, 6)", ())
.expect("Failed to call function");
assert_eq!(result, 30);
db.execute("DROP FUNCTION test_schema.multiply", ())
.expect("Failed to drop schema-qualified function");
let result = db.query_one::<i64, _>("SELECT test_schema.multiply(5, 6)", ());
assert!(result.is_err(), "Function should not exist after DROP");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM system.functions", ())
.expect("Failed to count functions");
assert_eq!(count, 0);
}
#[cfg(feature = "js")]
#[test]
fn test_function_call_schema_qualified() {
let db = Database::open("memory://call_schema_func").expect("Failed to create database");
db.execute(
"CREATE FUNCTION db.calc_power(base INTEGER, exp INTEGER) RETURNS INTEGER LANGUAGE DENO AS 'return Math.pow(base, exp);'",
(),
).expect("Failed to create function");
let result: f64 = db
.query_one("SELECT db.calc_power(2, 3)", ())
.expect("Failed to call with schema qualification");
assert_eq!(result, 8.0);
let result2: f64 = db
.query_one("SELECT calc_power(3, 2)", ())
.expect("Failed to call without schema qualification");
assert_eq!(result2, 9.0);
}
#[cfg(feature = "js")]
#[test]
fn test_functions_remain_global() {
let db = Database::open("memory://global_funcs").expect("Failed to create database");
db.execute(
"CREATE FUNCTION schema1.func() RETURNS TEXT LANGUAGE DENO AS 'return \"schema1\";'",
(),
)
.expect("Failed to create function with schema1");
let result = db.execute(
"CREATE FUNCTION schema2.func() RETURNS TEXT LANGUAGE DENO AS 'return \"schema2\";'",
(),
);
assert!(
result.is_err(),
"Should not allow duplicate function names even with different schemas"
);
let result1: String = db
.query_one("SELECT schema1.func()", ())
.expect("Failed to call with original schema");
assert_eq!(result1, "schema1");
let result2: String = db
.query_one("SELECT schema2.func()", ())
.expect("Failed to call with different schema");
assert_eq!(result2, "schema1");
let result3: String = db
.query_one("SELECT func()", ())
.expect("Failed to call without schema");
assert_eq!(result3, "schema1"); }