Add support for sqlite_version() scalar function

This commit is contained in:
김선우 2024-09-16 18:38:42 +09:00
parent 9f1ce53d18
commit 6b40acabbc
7 changed files with 60 additions and 6 deletions

View file

@ -103,7 +103,7 @@ This document describes the SQLite compatibility status of Limbo:
| sqlite_compileoption_used(X) | No | |
| sqlite_offset(X) | No | |
| sqlite_source_id() | No | |
| sqlite_version() | No | |
| sqlite_version() | Yes | |
| substr(X,Y,Z) | Yes | |
| substr(X,Y) | Yes | |
| substring(X,Y,Z) | Yes | |

View file

@ -65,6 +65,7 @@ pub enum ScalarFunc {
Time,
Unicode,
Quote,
SqliteVersion,
UnixEpoch,
}
@ -96,6 +97,7 @@ impl ToString for ScalarFunc {
ScalarFunc::Time => "time".to_string(),
ScalarFunc::Unicode => "unicode".to_string(),
ScalarFunc::Quote => "quote".to_string(),
ScalarFunc::SqliteVersion => "sqlite_version".to_string(),
ScalarFunc::UnixEpoch => "unixepoch".to_string(),
}
}
@ -160,6 +162,7 @@ impl Func {
"time" => Ok(Func::Scalar(ScalarFunc::Time)),
"unicode" => Ok(Func::Scalar(ScalarFunc::Unicode)),
"quote" => Ok(Func::Scalar(ScalarFunc::Quote)),
"sqlite_version" => Ok(Func::Scalar(ScalarFunc::SqliteVersion)),
"json" => Ok(Func::Json(JsonFunc::JSON)),
"unixepoch" => Ok(Func::Scalar(ScalarFunc::UnixEpoch)),
_ => Err(()),

View file

@ -19,7 +19,7 @@ use log::trace;
use schema::Schema;
use sqlite3_parser::ast;
use sqlite3_parser::{ast::Cmd, lexer::sql::Parser};
use std::sync::Arc;
use std::sync::{Arc, OnceLock};
use std::{cell::RefCell, rc::Rc};
#[cfg(feature = "fs")]
use storage::database::FileStorage;
@ -42,6 +42,8 @@ pub use storage::pager::Page;
pub use storage::wal::Wal;
pub use types::Value;
pub static DATABASE_VERSION: OnceLock<String> = OnceLock::new();
pub struct Database {
pager: Rc<Pager>,
schema: Rc<Schema>,
@ -64,6 +66,10 @@ impl Database {
wal: Rc<dyn Wal>,
) -> Result<Database> {
let db_header = Pager::begin_open(page_io.clone())?;
DATABASE_VERSION.get_or_init(|| {
let version = db_header.borrow().version_number.clone();
version.to_string()
});
io.run_once()?;
let pager = Rc::new(Pager::finish_open(
db_header.clone(),

View file

@ -84,7 +84,7 @@ pub struct DatabaseHeader {
application_id: u32,
reserved: [u8; 20],
version_valid_for: u32,
version_number: u32,
pub version_number: u32,
}
#[derive(Debug, Default)]

View file

@ -1311,6 +1311,26 @@ pub fn translate_expr(
Ok(target_register)
}
ScalarFunc::SqliteVersion => {
if args.is_some() {
crate::bail_parse_error!("sqlite_version function with arguments");
}
let output_register = program.alloc_register();
program.emit_insn(Insn::Function {
constant_mask: 0,
start_reg: output_register,
dest: output_register,
func: func_ctx,
});
program.emit_insn(Insn::Copy {
src_reg: output_register,
dst_reg: target_register,
amount: 1,
});
Ok(target_register)
}
}
}
}

View file

@ -31,7 +31,7 @@ use crate::schema::Table;
use crate::storage::sqlite3_ondisk::DatabaseHeader;
use crate::storage::{btree::BTreeCursor, pager::Pager};
use crate::types::{AggContext, Cursor, CursorResult, OwnedRecord, OwnedValue, Record};
use crate::Result;
use crate::{Result, DATABASE_VERSION};
use datetime::{exec_date, exec_time, exec_unixepoch};
@ -1574,6 +1574,12 @@ impl Program {
}
}
}
ScalarFunc::SqliteVersion => {
let version_integer: i64 =
DATABASE_VERSION.get().unwrap().parse()?;
let version = execute_sqlite_version(version_integer);
state.registers[*dest] = OwnedValue::Text(Rc::new(version));
}
},
crate::function::Func::Agg(_) => {
unreachable!("Aggregate functions should not be handled here")
@ -2150,13 +2156,21 @@ fn exec_if(reg: &OwnedValue, null_reg: &OwnedValue, not: bool) -> bool {
}
}
fn execute_sqlite_version(version_integer: i64) -> String {
let major = version_integer / 1_000_000;
let minor = (version_integer % 1_000_000) / 1_000;
let release = version_integer % 1_000;
format!("{}.{}.{}", major, minor, release)
}
#[cfg(test)]
mod tests {
use super::{
exec_abs, exec_char, exec_if, exec_length, exec_like, exec_lower, exec_ltrim, exec_minmax,
exec_nullif, exec_quote, exec_random, exec_round, exec_rtrim, exec_sign, exec_substring,
exec_trim, exec_unicode, exec_upper, get_new_rowid, Cursor, CursorResult, LimboError,
OwnedRecord, OwnedValue, Result,
exec_trim, exec_unicode, exec_upper, execute_sqlite_version, get_new_rowid, Cursor,
CursorResult, LimboError, OwnedRecord, OwnedValue, Result,
};
use mockall::{mock, predicate};
use rand::{rngs::mock::StepRng, thread_rng};
@ -2751,4 +2765,11 @@ mod tests {
let expected = Some(OwnedValue::Null);
assert_eq!(exec_sign(&input), expected);
}
#[test]
fn test_execute_sqlite_version() {
let version_integer = 3046001;
let expected = "3.46.1";
assert_eq!(execute_sqlite_version(version_integer), expected);
}
}

View file

@ -430,3 +430,7 @@ do_execsql_test sign-text-non-numeric {
do_execsql_test sign-null {
SELECT sign(NULL);
} {}
do_execsql_test sqlite_version {
SELECT sqlite_version();
} {3.46.1}