Add read implementation of user_version pragma with ReadCookie opcode

This commit is contained in:
Jonathan Webb 2025-02-06 11:10:27 -05:00
parent 098da0794f
commit 98e9d33478
9 changed files with 73 additions and 6 deletions

View file

@ -160,7 +160,7 @@ The current status of Limbo is:
| PRAGMA temp_store_directory | Not Needed | deprecated in SQLite |
| PRAGMA threads | No | |
| PRAGMA trusted_schema | No | |
| PRAGMA user_version | No | |
| PRAGMA user_version | Partial | Only read implemented |
| PRAGMA vdbe_addoptrace | No | |
| PRAGMA vdbe_debug | No | |
| PRAGMA vdbe_listing | No | |
@ -514,7 +514,7 @@ Modifiers:
| PrevAsync | Yes | |
| PrevAwait | Yes | |
| Program | No | |
| ReadCookie | No | |
| ReadCookie | Partial| no temp databases, only user_version supported |
| Real | Yes | |
| RealAffinity | Yes | |
| Remainder | Yes | |

View file

@ -128,7 +128,7 @@ pub struct DatabaseHeader {
text_encoding: u32,
/// The "user version" as read and set by the user_version pragma.
user_version: u32,
pub user_version: u32,
/// True (non-zero) for incremental-vacuum mode. False (zero) otherwise.
incremental_vacuum_enabled: u32,
@ -232,7 +232,7 @@ impl Default for DatabaseHeader {
default_page_cache_size: 500, // pages
vacuum_mode_largest_root_page: 0,
text_encoding: 1, // utf-8
user_version: 1,
user_version: 0,
incremental_vacuum_enabled: 0,
application_id: 0,
reserved_for_expansion: [0; 20],

View file

@ -11,7 +11,7 @@ use crate::storage::sqlite3_ondisk::{DatabaseHeader, MIN_PAGE_CACHE_SIZE};
use crate::storage::wal::CheckpointMode;
use crate::util::normalize_ident;
use crate::vdbe::builder::{ProgramBuilder, ProgramBuilderOpts, QueryMode};
use crate::vdbe::insn::Insn;
use crate::vdbe::insn::{Cookie, Insn};
use crate::vdbe::BranchOffset;
use crate::{bail_parse_error, Pager};
use std::str::FromStr;
@ -148,6 +148,10 @@ fn update_pragma(
query_pragma(PragmaName::PageCount, schema, None, header, program)?;
Ok(())
}
PragmaName::UserVersion => {
// TODO: Implement updating user_version
todo!("updating user_version not yet implemented")
}
PragmaName::TableInfo => {
// because we need control over the write parameter for the transaction,
// this should be unreachable. We have to force-call query_pragma before
@ -241,6 +245,15 @@ fn query_pragma(
}
}
}
PragmaName::UserVersion => {
program.emit_transaction(false);
program.emit_insn(Insn::ReadCookie {
db: 0,
dest: register,
cookie: Cookie::UserVersion,
});
program.emit_result_row(register, 1);
}
}
Ok(())

View file

@ -1232,6 +1232,15 @@ pub fn insn_to_str(
0,
"".to_string(),
),
Insn::ReadCookie { db, dest, cookie } => (
"ReadCookie",
*db as i32,
*dest as i32,
*cookie as i32,
OwnedValue::build_text(Rc::new("".to_string())),
0,
"".to_string(),
),
};
format!(
"{:<4} {:<17} {:<4} {:<4} {:<4} {:<13} {:<2} {}",

View file

@ -639,6 +639,29 @@ pub enum Insn {
db: usize,
dest: usize,
},
/// Read cookie number P3 from database P1 and write it into register P2
ReadCookie {
db: usize,
dest: usize,
cookie: Cookie,
},
}
// TODO: Add remaining cookies.
#[derive(Description, Debug, Clone, Copy)]
pub enum Cookie {
/// The schema cookie.
SchemaVersion = 1,
/// The schema format number. Supported schema formats are 1, 2, 3, and 4.
DatabaseFormat = 2,
/// Default page cache size.
DefaultPageCacheSize = 3,
/// The page number of the largest root b-tree page when in auto-vacuum or incremental-vacuum modes, or zero otherwise.
LargestRootPageNumber = 4,
/// The database text encoding. A value of 1 means UTF-8. A value of 2 means UTF-16le. A value of 3 means UTF-16be.
DatabaseTextEncoding = 5,
/// The "user version" as read and set by the user_version pragma.
UserVersion = 6,
}
fn cast_text_to_numerical(value: &str) -> OwnedValue {

View file

@ -55,7 +55,7 @@ use crate::{resolve_ext_path, Connection, Result, TransactionState, DATABASE_VER
use insn::{
exec_add, exec_and, exec_bit_and, exec_bit_not, exec_bit_or, exec_boolean_not, exec_concat,
exec_divide, exec_multiply, exec_or, exec_remainder, exec_shift_left, exec_shift_right,
exec_subtract,
exec_subtract, Cookie,
};
use likeop::{construct_like_escape_arg, exec_glob, exec_like_with_escape};
use rand::distributions::{Distribution, Uniform};
@ -2662,6 +2662,18 @@ impl Program {
parse_schema_rows(Some(stmt), &mut schema, conn.pager.io.clone())?;
state.pc += 1;
}
Insn::ReadCookie { db, dest, cookie } => {
if *db > 0 {
// TODO: implement temp databases
todo!("temp databases not implemented yet");
}
let cookie_value = match cookie {
Cookie::UserVersion => pager.db_header.borrow().user_version.into(),
cookie => todo!("{cookie:?} is not yet implement for ReadCookie"),
};
state.registers[*dest] = OwnedValue::Integer(cookie_value);
state.pc += 1;
}
Insn::ShiftRight { lhs, rhs, dest } => {
state.registers[*dest] =
exec_shift_right(&state.registers[*lhs], &state.registers[*rhs]);

View file

@ -41,3 +41,11 @@ do_execsql_test_on_specific_db ":memory:" pragma-page-count-table {
CREATE TABLE foo(bar);
PRAGMA page_count
} {2}
do_execsql_test_on_specific_db "testing/testing_user_version_10.db" pragma-user-version-user-set {
PRAGMA user_version
} {10}
do_execsql_test_on_specific_db ":memory:" pragma-user-version-default {
PRAGMA user_version
} {0}

Binary file not shown.

View file

@ -1598,6 +1598,8 @@ pub enum PragmaName {
PageCount,
/// returns information about the columns of a table
TableInfo,
/// Returns the user version of the database file.
UserVersion,
/// trigger a checkpoint to run on database(s) if WAL is enabled
WalCheckpoint,
}