Skip to content

Commit

Permalink
Merge 'Add read implementation of user_version pragma with ReadCookie…
Browse files Browse the repository at this point in the history
… opcode' from Jonathan Webb

Just a bare bones implementation of ReadCookie and support for the
user_version pragma

Closes #909
  • Loading branch information
penberg committed Feb 10, 2025
2 parents 0638550 + 98e9d33 commit d221f15
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 6 deletions.
4 changes: 2 additions & 2 deletions COMPAT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 | |
Expand Down Expand Up @@ -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 | |
Expand Down
4 changes: 2 additions & 2 deletions core/storage/sqlite3_ondisk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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],
Expand Down
15 changes: 14 additions & 1 deletion core/translate/pragma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(())
Expand Down
9 changes: 9 additions & 0 deletions core/vdbe/explain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,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} {}",
Expand Down
23 changes: 23 additions & 0 deletions core/vdbe/insn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
14 changes: 13 additions & 1 deletion core/vdbe/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -2679,6 +2679,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]);
Expand Down
8 changes: 8 additions & 0 deletions testing/pragma.test
Original file line number Diff line number Diff line change
Expand Up @@ -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 added testing/testing_user_version_10.db
Binary file not shown.
2 changes: 2 additions & 0 deletions vendored/sqlite3-parser/src/parser/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1622,6 +1622,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,
}
Expand Down

0 comments on commit d221f15

Please sign in to comment.