Add support for sqlite_version

This commit is contained in:
김선우 2024-09-18 17:57:51 +09:00
parent ac6dd71de6
commit e9ba458514
7 changed files with 69 additions and 13 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

@ -73,6 +73,7 @@ pub enum ScalarFunc {
Time,
Unicode,
Quote,
SqliteVersion,
UnixEpoch,
}
@ -105,6 +106,7 @@ impl Display 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(),
};
write!(f, "{}", str)
@ -171,6 +173,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>,
@ -59,11 +61,15 @@ impl Database {
}
pub fn open(
io: Arc<dyn crate::io::IO>,
io: Arc<dyn IO>,
page_io: Rc<dyn DatabaseStorage>,
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

@ -1320,6 +1320,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

@ -631,12 +631,18 @@ pub fn insn_to_str(
*dest as i32,
OwnedValue::Text(Rc::new(func.func.to_string())),
0,
format!(
"r[{}]=func(r[{}..{}])",
dest,
start_reg,
start_reg + func.arg_count - 1
),
if func.arg_count == 0 {
format!("r[{}]=func()", dest)
} else if *start_reg == *start_reg + func.arg_count - 1 {
format!("r[{}]=func(r[{}])", dest, start_reg)
} else {
format!(
"r[{}]=func(r[{}..{}])",
dest,
start_reg,
start_reg + func.arg_count - 1
)
},
),
Insn::InitCoroutine {
yield_reg,

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};
@ -1608,6 +1608,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")
@ -2212,13 +2218,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};
@ -2813,4 +2827,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);
}
}