lc_vector_stores/
pgvector.rs1use std::sync::LazyLock;
18
19use regex::Regex;
20
21static TABLE_NAME_RE: LazyLock<Regex> =
22 LazyLock::new(|| Regex::new(r"^[a-zA-Z_][a-zA-Z0-9_]*$").unwrap());
23
24pub fn validate_table_name(table: &str) -> Result<(), String> {
29 if TABLE_NAME_RE.is_match(table) {
30 Ok(())
31 } else {
32 Err(format!(
33 "Invalid table name '{}': must match ^[a-zA-Z_][a-zA-Z0-9_]*$",
34 table
35 ))
36 }
37}
38
39pub fn build_table_sql(table: &str, dim: usize) -> String {
41 format!(
42 "CREATE TABLE IF NOT EXISTS {} (id TEXT PRIMARY KEY, content TEXT, metadata JSONB, embedding vector({}))",
43 table, dim
44 )
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50
51 #[test]
52 fn test_build_table_sql() {
53 let sql = build_table_sql("docs", 1536);
54 assert!(sql.contains("CREATE TABLE"));
55 assert!(sql.contains("vector(1536)"));
56 assert!(sql.contains("docs"));
57 }
58
59 #[test]
60 fn test_build_table_sql_different_dim() {
61 let sql = build_table_sql("embeddings", 768);
62 assert!(sql.contains("vector(768)"));
63 assert!(sql.contains("embeddings"));
64 }
65
66 #[test]
67 fn test_build_table_sql_contains_metadata() {
68 let sql = build_table_sql("docs", 1536);
69 assert!(sql.contains("metadata JSONB"));
70 assert!(sql.contains("id TEXT PRIMARY KEY"));
71 }
72
73 #[test]
74 fn test_validate_table_name_valid() {
75 assert!(validate_table_name("users").is_ok());
76 assert!(validate_table_name("my_table").is_ok());
77 assert!(validate_table_name("_private").is_ok());
78 assert!(validate_table_name("Table123").is_ok());
79 }
80
81 #[test]
82 fn test_validate_table_name_invalid() {
83 assert!(validate_table_name("users; DROP TABLE users--").is_err());
85 assert!(validate_table_name("users; DROP TABLE users").is_err());
86 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()); }
91}