Merge 'translate: disallow creating/dropping internal tables' from Jussi Saurio

edit: we can't disallow 'turso_' prefix though, because turso-sync-
engine uses it
Closes #3313

Reviewed-by: Nikita Sivukhin (@sivukhin)

Closes #3338
This commit is contained in:
Pekka Enberg
2025-09-26 10:40:09 +03:00
committed by GitHub
3 changed files with 39 additions and 2 deletions

View File

@@ -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();

View File

@@ -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,

View File

@@ -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