diff --git a/core/schema.rs b/core/schema.rs index 693738077..13bd620fb 100644 --- a/core/schema.rs +++ b/core/schema.rs @@ -48,6 +48,9 @@ pub const DBSP_TABLE_PREFIX: &str = "__turso_internal_dbsp_state_v"; /// Used to refer to the implicit rowid column in tables without an alias during UPDATE pub const ROWID_SENTINEL: usize = usize::MAX; +/// Internal table prefixes that should be protected from CREATE/DROP +pub const RESERVED_TABLE_PREFIXES: [&str; 2] = ["sqlite_", "__turso_internal_"]; + /// Check if a table name refers to a system table that should be protected from direct writes pub fn is_system_table(table_name: &str) -> bool { let normalized = table_name.to_lowercase(); diff --git a/core/translate/index.rs b/core/translate/index.rs index 169ad854c..e78b9dc02 100644 --- a/core/translate/index.rs +++ b/core/translate/index.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use crate::schema::Table; +use crate::schema::{Table, RESERVED_TABLE_PREFIXES}; use crate::translate::emitter::{ emit_cdc_full_record, emit_cdc_insns, prepare_cdc_if_necessary, OperationMode, Resolver, }; @@ -11,7 +11,7 @@ use crate::translate::plan::{ use crate::vdbe::builder::CursorKey; use crate::vdbe::insn::{CmpInsFlags, Cookie}; use crate::vdbe::BranchOffset; -use crate::SymbolTable; +use crate::{bail_parse_error, SymbolTable}; use crate::{ schema::{BTreeTable, Column, Index, IndexColumn, PseudoCursorType, Schema}, storage::pager::CreateBTreeFlags, @@ -49,6 +49,21 @@ pub fn translate_create_index( let original_tbl_name = tbl_name; let idx_name = normalize_ident(idx_name); let tbl_name = normalize_ident(tbl_name); + if RESERVED_TABLE_PREFIXES + .iter() + .any(|prefix| idx_name.starts_with(prefix)) + { + bail_parse_error!( + "Object name reserved for internal use: {}", + original_idx_name + ); + } + if RESERVED_TABLE_PREFIXES + .iter() + .any(|prefix| tbl_name.starts_with(prefix)) + { + bail_parse_error!("Object name reserved for internal use: {}", tbl_name); + } let opts = crate::vdbe::builder::ProgramBuilderOpts { num_cursors: 5, approx_num_insns: 40, diff --git a/core/translate/schema.rs b/core/translate/schema.rs index d961eb8ce..c4f705113 100644 --- a/core/translate/schema.rs +++ b/core/translate/schema.rs @@ -8,6 +8,7 @@ use crate::schema::Column; use crate::schema::Schema; use crate::schema::Table; use crate::schema::Type; +use crate::schema::RESERVED_TABLE_PREFIXES; use crate::storage::pager::CreateBTreeFlags; use crate::translate::emitter::emit_cdc_full_record; use crate::translate::emitter::emit_cdc_insns; @@ -58,6 +59,17 @@ pub fn translate_create_table( approx_num_labels: 1, }; program.extend(&opts); + + if RESERVED_TABLE_PREFIXES + .iter() + .any(|prefix| normalized_tbl_name.starts_with(prefix)) + { + bail_parse_error!( + "Object name reserved for internal use: {}", + tbl_name.name.as_str() + ); + } + if schema.get_table(&normalized_tbl_name).is_some() { if if_not_exists { return Ok(program); @@ -601,6 +613,13 @@ pub fn translate_drop_table( bail_parse_error!("No such table: {}", tbl_name.name.as_str()); } + if RESERVED_TABLE_PREFIXES + .iter() + .any(|prefix| tbl_name.name.as_str().starts_with(prefix)) + { + bail_parse_error!("table {} may not be dropped", tbl_name.name.as_str()); + } + let table = table.unwrap(); // safe since we just checked for None // Check if this is a materialized view - if so, refuse to drop it with DROP TABLE