diff --git a/COMPAT.md b/COMPAT.md index c940cc95d..ee88d173b 100644 --- a/COMPAT.md +++ b/COMPAT.md @@ -479,7 +479,7 @@ Modifiers: | IntegrityCk | Yes | | | IsNull | Yes | | | IsUnique | No | | -| JournalMode | No | | +| JournalMode | Yes | | | Jump | Yes | | | Last | Yes | | | Le | Yes | | diff --git a/core/translate/pragma.rs b/core/translate/pragma.rs index 5d09ea366..c5a73ee71 100644 --- a/core/translate/pragma.rs +++ b/core/translate/pragma.rs @@ -120,14 +120,23 @@ fn update_pragma( let year = chrono::Local::now().year(); bail_parse_error!("It's {year}. UTF-8 won."); } - PragmaName::JournalMode => query_pragma( - PragmaName::JournalMode, - schema, - None, - pager, - connection, - program, - ), + PragmaName::JournalMode => { + // For JournalMode, when setting a value, we use the opcode + let mode_str = match value { + Expr::Name(name) => name.as_str().to_string(), + _ => parse_string(&value)?, + }; + + let result_reg = program.alloc_register(); + program.emit_insn(Insn::JournalMode { + db: 0, + dest: result_reg, + new_mode: Some(mode_str), + }); + program.emit_result_row(result_reg, 1); + program.add_pragma_result_column("journal_mode".into()); + Ok((program, TransactionMode::None)) + } PragmaName::LegacyFileFormat => Ok((program, TransactionMode::None)), PragmaName::WalCheckpoint => query_pragma( PragmaName::WalCheckpoint, @@ -349,7 +358,12 @@ fn query_pragma( Ok((program, TransactionMode::None)) } PragmaName::JournalMode => { - program.emit_string8("wal".into(), register); + // Use the JournalMode opcode to get the current journal mode + program.emit_insn(Insn::JournalMode { + db: 0, + dest: register, + new_mode: None, + }); program.emit_result_row(register, 1); program.add_pragma_result_column(pragma.to_string()); Ok((program, TransactionMode::None)) diff --git a/core/vdbe/execute.rs b/core/vdbe/execute.rs index 4fbc37f37..54f38862e 100644 --- a/core/vdbe/execute.rs +++ b/core/vdbe/execute.rs @@ -8322,12 +8322,12 @@ pub fn op_max_pgcnt( pager: &Rc, mv_store: Option<&Arc>, ) -> Result { - let Insn::MaxPgcnt { db, dest, new_max } = insn else { - unreachable!("unexpected Insn {:?}", insn) - }; + load_insn!(MaxPgcnt { db, dest, new_max }, insn); if *db > 0 { - todo!("temp/attached databases not implemented yet"); + return Err(LimboError::InternalError( + "temp/attached databases not implemented yet".to_string(), + )); } let result_value = if *new_max == 0 { @@ -8346,6 +8346,46 @@ pub fn op_max_pgcnt( Ok(InsnFunctionStepResult::Step) } +pub fn op_journal_mode( + program: &Program, + state: &mut ProgramState, + insn: &Insn, + pager: &Rc, + mv_store: Option<&Arc>, +) -> Result { + let Insn::JournalMode { db, dest, new_mode } = insn else { + unreachable!("unexpected Insn {:?}", insn) + }; + + if *db > 0 { + todo!("temp/attached databases not implemented yet"); + } + + // Currently, Turso only supports WAL mode + // If a new mode is specified, we validate it but always return "wal" + if let Some(mode) = new_mode { + let mode_lower = mode.to_lowercase(); + // Valid journal modes in SQLite are: delete, truncate, persist, memory, wal, off + // We accept any valid mode but always use WAL + match mode_lower.as_str() { + "delete" | "truncate" | "persist" | "memory" | "wal" | "off" => { + // Mode is valid, but we stay in WAL mode + } + _ => { + // Invalid journal mode + return Err(LimboError::ParseError(format!( + "Unknown journal mode: {mode}" + ))); + } + } + } + + // Always return "wal" as the current journal mode + state.registers[*dest] = Register::Value(Value::build_text("wal")); + state.pc += 1; + Ok(InsnFunctionStepResult::Step) +} + #[cfg(test)] mod tests { use super::*; diff --git a/core/vdbe/explain.rs b/core/vdbe/explain.rs index 959516c5e..2cd78ab9e 100644 --- a/core/vdbe/explain.rs +++ b/core/vdbe/explain.rs @@ -1654,6 +1654,16 @@ pub fn insn_to_str( 0, format!("r[{dest}]=max_page_count(db[{db}],{new_max})"), ), + Insn::JournalMode { db, dest, new_mode } => ( + "JournalMode", + *db as i32, + *dest as i32, + 0, + Value::build_text(new_mode.as_ref().unwrap_or(&String::new())), + 0, + format!("r[{dest}]=journal_mode(db[{db}]{})", + new_mode.as_ref().map_or(String::new(), |m| format!(",'{m}'"))), + ), Insn::CollSeq { reg, collation } => ( "CollSeq", reg.unwrap_or(0) as i32, diff --git a/core/vdbe/insn.rs b/core/vdbe/insn.rs index 8f8e69cfd..3c2dedc0f 100644 --- a/core/vdbe/insn.rs +++ b/core/vdbe/insn.rs @@ -1043,6 +1043,14 @@ pub enum Insn { dest: usize, // P2: output register new_max: usize, // P3: new maximum page count (0 = just return current) }, + /// Get or set the journal mode for database P1. + /// If P3 is not null, it contains the new journal mode string. + /// Store the resulting journal mode in register P2. + JournalMode { + db: usize, // P1: database index + dest: usize, // P2: output register for result + new_mode: Option, // P3: new journal mode (if setting) + }, } impl Insn { @@ -1175,6 +1183,7 @@ impl Insn { Insn::RenameTable { .. } => execute::op_rename_table, Insn::DropColumn { .. } => execute::op_drop_column, Insn::MaxPgcnt { .. } => execute::op_max_pgcnt, + Insn::JournalMode { .. } => execute::op_journal_mode, } } }