From d9d3a5ecbb2d5e8ae27b7f8a638b81c12897b9c2 Mon Sep 17 00:00:00 2001 From: meteorgan Date: Mon, 26 May 2025 23:28:15 +0800 Subject: [PATCH] Use the SetCookie opcode to implement user_version pragma --- core/translate/pragma.rs | 30 +++++++++++++++++------------- core/vdbe/execute.rs | 31 +++++++++++++++++++++++++++++++ core/vdbe/explain.rs | 14 ++++++++++++++ core/vdbe/insn.rs | 9 +++++++++ 4 files changed, 71 insertions(+), 13 deletions(-) diff --git a/core/translate/pragma.rs b/core/translate/pragma.rs index 5c66445f9..5a4716672 100644 --- a/core/translate/pragma.rs +++ b/core/translate/pragma.rs @@ -10,10 +10,10 @@ use crate::fast_lock::SpinLock; use crate::schema::Schema; use crate::storage::sqlite3_ondisk::{DatabaseHeader, MIN_PAGE_CACHE_SIZE}; use crate::storage::wal::CheckpointMode; -use crate::util::normalize_ident; +use crate::util::{normalize_ident, parse_numeric_literal}; use crate::vdbe::builder::{ProgramBuilder, ProgramBuilderOpts, QueryMode}; use crate::vdbe::insn::{Cookie, Insn}; -use crate::{bail_parse_error, Pager}; +use crate::{bail_parse_error, Pager, Value}; use std::str::FromStr; use strum::IntoEnumIterator; @@ -142,27 +142,31 @@ fn update_pragma( Ok(()) } PragmaName::UserVersion => { - let version_value = match value { + let data = match value { ast::Expr::Literal(ast::Literal::Numeric(numeric_value)) => { - numeric_value.parse::()? + parse_numeric_literal(&numeric_value)? } ast::Expr::Unary(ast::UnaryOperator::Negative, expr) => match *expr { ast::Expr::Literal(ast::Literal::Numeric(numeric_value)) => { - -numeric_value.parse::()? + let data = "-".to_owned() + numeric_value.as_str(); + parse_numeric_literal(&data)? } _ => bail_parse_error!("Not a valid value"), }, _ => bail_parse_error!("Not a valid value"), }; + let version_value = match data { + Value::Integer(i) => i as i32, + Value::Float(f) => f as i32, + _ => unreachable!(), + }; - let mut header_guard = header.lock(); - - // update in-memory - header_guard.user_version = version_value; - - // update in disk - pager.write_database_header(&header_guard); - + program.emit_insn(Insn::SetCookie { + db: 0, + cookie: Cookie::UserVersion, + value: version_value, + p5: 1, + }); Ok(()) } PragmaName::SchemaVersion => { diff --git a/core/vdbe/execute.rs b/core/vdbe/execute.rs index dddfd2098..2071434bc 100644 --- a/core/vdbe/execute.rs +++ b/core/vdbe/execute.rs @@ -4520,6 +4520,37 @@ pub fn op_read_cookie( Ok(InsnFunctionStepResult::Step) } +pub fn op_set_cookie( + program: &Program, + state: &mut ProgramState, + insn: &Insn, + pager: &Rc, + mv_store: Option<&Rc>, +) -> Result { + let Insn::SetCookie { + db, + cookie, + value, + p5, + } = insn + else { + unreachable!("unexpected Insn {:?}", insn) + }; + if *db > 0 { + todo!("temp databases not implemented yet"); + } + match cookie { + Cookie::UserVersion => { + let mut header_guard = pager.db_header.lock(); + header_guard.user_version = *value; + pager.write_database_header(&*header_guard); + } + cookie => todo!("{cookie:?} is not yet implement for SetCookie"), + } + state.pc += 1; + Ok(InsnFunctionStepResult::Step) +} + pub fn op_shift_right( program: &Program, state: &mut ProgramState, diff --git a/core/vdbe/explain.rs b/core/vdbe/explain.rs index 4055fb072..425efee2d 100644 --- a/core/vdbe/explain.rs +++ b/core/vdbe/explain.rs @@ -1373,6 +1373,20 @@ pub fn insn_to_str( 0, "".to_string(), ), + Insn::SetCookie { + db, + cookie, + value, + p5, + } => ( + "SetCookie", + *db as i32, + *cookie as i32, + *value, + Value::build_text(""), + *p5, + "".to_string(), + ), Insn::AutoCommit { auto_commit, rollback, diff --git a/core/vdbe/insn.rs b/core/vdbe/insn.rs index f5ff8352d..b6c70efe4 100644 --- a/core/vdbe/insn.rs +++ b/core/vdbe/insn.rs @@ -844,6 +844,14 @@ pub enum Insn { dest: usize, cookie: Cookie, }, + /// Write the value in register P3 into cookie number P2 of database P1. + /// If P2 is the SCHEMA_VERSION cookie (cookie number 1) then the internal schema version is set to P3-P5 + SetCookie { + db: usize, + cookie: Cookie, + value: i32, + p5: u16, + }, /// Open a new cursor P1 to a transient table. OpenEphemeral { cursor_id: usize, @@ -1010,6 +1018,7 @@ impl Insn { Insn::Noop => execute::op_noop, Insn::PageCount { .. } => execute::op_page_count, Insn::ReadCookie { .. } => execute::op_read_cookie, + Insn::SetCookie { .. } => execute::op_set_cookie, Insn::OpenEphemeral { .. } | Insn::OpenAutoindex { .. } => execute::op_open_ephemeral, Insn::Once { .. } => execute::op_once, Insn::Found { .. } | Insn::NotFound { .. } => execute::op_found,