Merge 'names shall not be shared between tables,indexs,vtabs,views' from Pavan Nambi

closes #3675

Closes #3681
This commit is contained in:
Jussi Saurio
2025-10-14 07:30:37 +03:00
committed by GitHub
5 changed files with 128 additions and 27 deletions

View File

@@ -2247,7 +2247,9 @@ mod tests {
unique_sets: vec![],
foreign_keys: vec![],
};
schema.add_btree_table(Arc::new(users_table));
schema
.add_btree_table(Arc::new(users_table))
.expect("Test setup: failed to add users table");
// Add products table for join tests
let products_table = BTreeTable {
@@ -2301,7 +2303,9 @@ mod tests {
unique_sets: vec![],
foreign_keys: vec![],
};
schema.add_btree_table(Arc::new(products_table));
schema
.add_btree_table(Arc::new(products_table))
.expect("Test setup: failed to add products table");
// Add orders table for join tests
let orders_table = BTreeTable {
@@ -2367,7 +2371,9 @@ mod tests {
unique_sets: vec![],
foreign_keys: vec![],
};
schema.add_btree_table(Arc::new(orders_table));
schema
.add_btree_table(Arc::new(orders_table))
.expect("Test setup: failed to add orders table");
// Add customers table with id and name for testing column ambiguity
let customers_table = BTreeTable {
@@ -2406,7 +2412,9 @@ mod tests {
unique_sets: vec![],
foreign_keys: vec![],
};
schema.add_btree_table(Arc::new(customers_table));
schema
.add_btree_table(Arc::new(customers_table))
.expect("Test setup: failed to add customers table");
// Add purchases table (junction table for three-way join)
let purchases_table = BTreeTable {
@@ -2469,7 +2477,9 @@ mod tests {
unique_sets: vec![],
foreign_keys: vec![],
};
schema.add_btree_table(Arc::new(purchases_table));
schema
.add_btree_table(Arc::new(purchases_table))
.expect("Test setup: failed to add purchases table");
// Add vendors table with id, name, and price (ambiguous columns with customers)
let vendors_table = BTreeTable {
@@ -2520,7 +2530,9 @@ mod tests {
unique_sets: vec![],
foreign_keys: vec![],
};
schema.add_btree_table(Arc::new(vendors_table));
schema
.add_btree_table(Arc::new(vendors_table))
.expect("Test setup: failed to add vendors table");
let sales_table = BTreeTable {
name: "sales".to_string(),
@@ -2558,7 +2570,9 @@ mod tests {
unique_sets: vec![],
foreign_keys: vec![],
};
schema.add_btree_table(Arc::new(sales_table));
schema
.add_btree_table(Arc::new(sales_table))
.expect("Test setup: failed to add sales table");
schema
}};

View File

@@ -1565,10 +1565,22 @@ mod tests {
unique_sets: vec![],
};
schema.add_btree_table(Arc::new(customers_table));
schema.add_btree_table(Arc::new(orders_table));
schema.add_btree_table(Arc::new(products_table));
schema.add_btree_table(Arc::new(logs_table));
schema
.add_btree_table(Arc::new(customers_table))
.expect("Test setup: failed to add customers table");
schema
.add_btree_table(Arc::new(orders_table))
.expect("Test setup: failed to add orders table");
schema
.add_btree_table(Arc::new(products_table))
.expect("Test setup: failed to add products table");
schema
.add_btree_table(Arc::new(logs_table))
.expect("Test setup: failed to add logs table");
schema
}

View File

@@ -289,9 +289,11 @@ impl Schema {
}
/// Add a regular (non-materialized) view
pub fn add_view(&mut self, view: View) {
pub fn add_view(&mut self, view: View) -> Result<()> {
self.check_object_name_conflict(&view.name)?;
let name = normalize_ident(&view.name);
self.views.insert(name, Arc::new(view));
Ok(())
}
/// Get a regular view by name
@@ -300,14 +302,18 @@ impl Schema {
self.views.get(&name).cloned()
}
pub fn add_btree_table(&mut self, table: Arc<BTreeTable>) {
pub fn add_btree_table(&mut self, table: Arc<BTreeTable>) -> Result<()> {
self.check_object_name_conflict(&table.name)?;
let name = normalize_ident(&table.name);
self.tables.insert(name, Table::BTree(table).into());
Ok(())
}
pub fn add_virtual_table(&mut self, table: Arc<VirtualTable>) {
pub fn add_virtual_table(&mut self, table: Arc<VirtualTable>) -> Result<()> {
self.check_object_name_conflict(&table.name)?;
let name = normalize_ident(&table.name);
self.tables.insert(name, Table::Virtual(table).into());
Ok(())
}
pub fn get_table(&self, name: &str) -> Option<Arc<Table>> {
@@ -340,7 +346,8 @@ impl Schema {
}
}
pub fn add_index(&mut self, index: Arc<Index>) {
pub fn add_index(&mut self, index: Arc<Index>) -> Result<()> {
self.check_object_name_conflict(&index.name)?;
let table_name = normalize_ident(&index.table_name);
// We must add the new index to the front of the deque, because SQLite stores index definitions as a linked list
// where the newest parsed index entry is at the head of list. If we would add it to the back of a regular Vec for example,
@@ -350,7 +357,8 @@ impl Schema {
self.indexes
.entry(table_name)
.or_default()
.push_front(index.clone())
.push_front(index.clone());
Ok(())
}
pub fn get_indices(&self, table_name: &str) -> impl Iterator<Item = &Arc<Index>> {
@@ -507,7 +515,7 @@ impl Schema {
unparsed_sql_from_index.root_page,
table.as_ref(),
)?;
self.add_index(Arc::new(index));
self.add_index(Arc::new(index))?;
}
}
@@ -549,7 +557,7 @@ impl Schema {
table.as_ref(),
automatic_indexes.pop().unwrap(),
1,
)?));
)?))?;
} else {
// Add single column unique index
if let Some(autoidx) = automatic_indexes.pop() {
@@ -557,7 +565,7 @@ impl Schema {
table.as_ref(),
autoidx,
vec![(pos_in_table, unique_set.columns.first().unwrap().1)],
)?));
)?))?;
}
}
}
@@ -575,7 +583,7 @@ impl Schema {
table.as_ref(),
automatic_indexes.pop().unwrap(),
unique_set.columns.len(),
)?));
)?))?;
} else {
// Add composite unique index
let mut column_indices_and_sort_orders =
@@ -593,7 +601,7 @@ impl Schema {
table.as_ref(),
automatic_indexes.pop().unwrap(),
column_indices_and_sort_orders,
)?));
)?))?;
}
}
@@ -701,7 +709,7 @@ impl Schema {
syms,
)?
};
self.add_virtual_table(vtab);
self.add_virtual_table(vtab)?;
} else {
let table = BTreeTable::from_sql(sql, root_page)?;
@@ -735,7 +743,7 @@ impl Schema {
}
}
self.add_btree_table(Arc::new(table));
self.add_btree_table(Arc::new(table))?;
}
}
"index" => {
@@ -834,7 +842,7 @@ impl Schema {
// Create regular view
let view =
View::new(name.to_string(), sql.to_string(), select, final_columns);
self.add_view(view);
self.add_view(view)?;
}
_ => {}
}
@@ -1105,6 +1113,32 @@ impl Schema {
.and_then(|t| t.btree())
.is_some_and(|t| !t.foreign_keys.is_empty())
}
fn check_object_name_conflict(&self, name: &str) -> Result<()> {
let normalized_name = normalize_ident(name);
if self.tables.contains_key(&normalized_name) {
return Err(crate::LimboError::ParseError(
["table \"", name, "\" already exists"].concat().to_string(),
));
}
if self.views.contains_key(&normalized_name) {
return Err(crate::LimboError::ParseError(
["view \"", name, "\" already exists"].concat().to_string(),
));
}
for index_list in self.indexes.values() {
if index_list.iter().any(|i| i.name.eq_ignore_ascii_case(name)) {
return Err(crate::LimboError::ParseError(
["index \"", name, "\" already exists"].concat().to_string(),
));
}
}
Ok(())
}
}
impl Clone for Schema {

View File

@@ -2445,7 +2445,9 @@ mod tests {
has_autoincrement: false,
unique_sets: vec![],
};
schema.add_btree_table(Arc::new(users_table));
schema
.add_btree_table(Arc::new(users_table))
.expect("Test setup: failed to add users table");
// Create orders table
let orders_table = BTreeTable {
@@ -2508,7 +2510,9 @@ mod tests {
unique_sets: vec![],
foreign_keys: vec![],
};
schema.add_btree_table(Arc::new(orders_table));
schema
.add_btree_table(Arc::new(orders_table))
.expect("Test setup: failed to add orders table");
// Create products table
let products_table = BTreeTable {
@@ -2571,7 +2575,9 @@ mod tests {
unique_sets: vec![],
foreign_keys: vec![],
};
schema.add_btree_table(Arc::new(products_table));
schema
.add_btree_table(Arc::new(products_table))
.expect("Test setup: failed to add products table");
schema
}

View File

@@ -66,3 +66,38 @@ do_execsql_test_in_memory_any_error create_table_duplicate_column_names_case_ins
do_execsql_test_in_memory_any_error create_table_duplicate_column_names_quoted {
CREATE TABLE t("a", a);
}
# https://github.com/tursodatabase/turso/issues/3675
do_execsql_test_in_memory_any_error create_table_view_collision-1 {
CREATE VIEW v_same AS SELECT 1;
CREATE TABLE v_same(x INT);
}
do_execsql_test_in_memory_any_error create_view_table_collision-1 {
CREATE TABLE t_same(x INT);
CREATE VIEW t_same AS SELECT 1;
}
do_execsql_test_in_memory_any_error create_index_view_collision-1 {
CREATE VIEW i_same AS SELECT 1;
CREATE TABLE t1(x);
CREATE INDEX i_same ON t1(x);
}
do_execsql_test_in_memory_any_error create_index_table_collision-1 {
CREATE TABLE i_same(x INT);
CREATE TABLE t2(y);
CREATE INDEX i_same ON t2(y);
}
do_execsql_test_in_memory_any_error create_table_index_collision-1 {
CREATE TABLE t3(z);
CREATE INDEX ix_same ON t3(z);
CREATE TABLE ix_same(x INT);
}
do_execsql_test_in_memory_any_error create_view_index_collision-1 {
CREATE TABLE t4(w);
CREATE INDEX ix_same ON t4(w);
CREATE VIEW ix_same AS SELECT 1;
}