use std::sync::LazyLock;
use regex::Regex;
static TABLE_NAME_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^[a-zA-Z_][a-zA-Z0-9_]*$").unwrap());
pub fn validate_table_name(table: &str) -> Result<(), String> {
if TABLE_NAME_RE.is_match(table) {
Ok(())
} else {
Err(format!(
"Invalid table name '{}': must match ^[a-zA-Z_][a-zA-Z0-9_]*$",
table
))
}
}
pub fn build_table_sql(table: &str, dim: usize) -> String {
format!(
"CREATE TABLE IF NOT EXISTS {} (id TEXT PRIMARY KEY, content TEXT, metadata JSONB, embedding vector({}))",
table, dim
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_build_table_sql() {
let sql = build_table_sql("docs", 1536);
assert!(sql.contains("CREATE TABLE"));
assert!(sql.contains("vector(1536)"));
assert!(sql.contains("docs"));
}
#[test]
fn test_build_table_sql_different_dim() {
let sql = build_table_sql("embeddings", 768);
assert!(sql.contains("vector(768)"));
assert!(sql.contains("embeddings"));
}
#[test]
fn test_build_table_sql_contains_metadata() {
let sql = build_table_sql("docs", 1536);
assert!(sql.contains("metadata JSONB"));
assert!(sql.contains("id TEXT PRIMARY KEY"));
}
#[test]
fn test_validate_table_name_valid() {
assert!(validate_table_name("users").is_ok());
assert!(validate_table_name("my_table").is_ok());
assert!(validate_table_name("_private").is_ok());
assert!(validate_table_name("Table123").is_ok());
}
#[test]
fn test_validate_table_name_invalid() {
assert!(validate_table_name("users; DROP TABLE users--").is_err());
assert!(validate_table_name("users; DROP TABLE users").is_err());
assert!(validate_table_name("123table").is_err()); assert!(validate_table_name("user-table").is_err()); assert!(validate_table_name("user.table").is_err()); assert!(validate_table_name("").is_err()); }
}