use std::ffi::CString;
use std::fmt;
use std::marker::PhantomData;
use crate::error::{Error, Result};
use crate::ffi;
use crate::insn::{Insn, InsnP4};
use crate::value::Value;
#[doc(hidden)]
pub use crate::insn::RawOpcode as Opcode;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Address(pub i32);
impl Address {
#[inline]
pub fn raw(&self) -> i32 {
self.0
}
}
impl std::fmt::Display for Address {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "@{}", self.0)
}
}
#[derive(Debug, Clone)]
pub struct InsnRecord {
pub opcode: String,
pub p1: i32,
pub p2: i32,
pub p3: i32,
pub p4: String,
pub p5: u16,
pub comment: String,
}
pub struct ProgramBuilder {
raw: *mut ffi::Vdbe,
db: *mut ffi::sqlite3,
next_register: i32,
next_cursor: i32,
instructions: Vec<InsnRecord>,
_marker: PhantomData<*const ()>,
}
impl ProgramBuilder {
pub(crate) fn new(db: *mut ffi::sqlite3) -> Result<Self> {
let raw = unsafe { ffi::sqlite3_vdbe_create(db) };
if raw.is_null() {
return Err(Error::AllocationFailed);
}
Ok(ProgramBuilder {
raw,
db,
next_register: 1, next_cursor: 0,
instructions: Vec::new(),
_marker: PhantomData,
})
}
pub fn alloc_register(&mut self) -> i32 {
let reg = self.next_register;
self.next_register += 1;
reg
}
pub fn alloc_registers(&mut self, count: i32) -> i32 {
let first = self.next_register;
self.next_register += count;
first
}
pub fn alloc_cursor(&mut self) -> i32 {
let cursor = self.next_cursor;
self.next_cursor += 1;
cursor
}
pub fn register_count(&self) -> i32 {
self.next_register
}
pub fn cursor_count(&self) -> i32 {
self.next_cursor
}
pub fn add(&mut self, insn: Insn) -> Address {
self.add_with_comment(insn, "")
}
pub fn add_with_comment(&mut self, insn: Insn, comment: &str) -> Address {
let opcode = insn.raw_opcode() as i32;
let (p1, p2, p3, p5) = insn.operands();
let name = insn.name().to_string();
let p4_str = match insn.p4() {
Some(InsnP4::Int(i)) => i.to_string(),
Some(InsnP4::Int64(i)) => i.to_string(),
Some(InsnP4::Real(r)) => format!("{:?}", r),
Some(InsnP4::String(ref s)) => s.clone(),
None => String::new(),
};
self.instructions.push(InsnRecord {
opcode: name,
p1,
p2,
p3,
p4: p4_str,
p5,
comment: comment.to_string(),
});
if let Some(p4) = insn.p4() {
let addr = match p4 {
InsnP4::Int(i) => unsafe {
ffi::sqlite3VdbeAddOp4Int(self.raw, opcode, p1, p2, p3, i)
},
InsnP4::Int64(i) => unsafe {
let addr = ffi::sqlite3VdbeAddOp2(self.raw, opcode, p1, p2);
let ptr = ffi::sqlite3_malloc(std::mem::size_of::<i64>() as i32);
if !ptr.is_null() {
*(ptr as *mut i64) = i;
ffi::sqlite3VdbeChangeP4(self.raw, addr, ptr as *const i8, ffi::P4_INT64);
}
addr
},
InsnP4::Real(r) => unsafe {
let addr = ffi::sqlite3VdbeAddOp2(self.raw, opcode, p1, p2);
let ptr = ffi::sqlite3_malloc(std::mem::size_of::<f64>() as i32);
if !ptr.is_null() {
*(ptr as *mut f64) = r;
ffi::sqlite3VdbeChangeP4(self.raw, addr, ptr as *const i8, ffi::P4_REAL);
}
addr
},
InsnP4::String(ref s) => {
if let Ok(c_str) = CString::new(s.as_str()) {
let bytes = c_str.as_bytes_with_nul();
unsafe {
let ptr = ffi::sqlite3_malloc(bytes.len() as i32);
if !ptr.is_null() {
std::ptr::copy_nonoverlapping(
bytes.as_ptr(),
ptr as *mut u8,
bytes.len(),
);
ffi::sqlite3VdbeAddOp4(
self.raw,
opcode,
p1,
p2,
p3,
ptr as *const i8,
ffi::P4_DYNAMIC,
)
} else {
ffi::sqlite3VdbeAddOp3(self.raw, opcode, p1, p2, p3)
}
}
} else {
unsafe { ffi::sqlite3VdbeAddOp3(self.raw, opcode, p1, p2, p3) }
}
}
};
if p5 != 0 {
unsafe { ffi::sqlite3VdbeChangeP5(self.raw, p5) };
}
return Address(addr);
}
let addr = if p3 != 0 {
unsafe { ffi::sqlite3VdbeAddOp3(self.raw, opcode, p1, p2, p3) }
} else if p2 != 0 || matches!(insn, Insn::Goto { .. } | Insn::Integer { value: 0, .. }) {
unsafe { ffi::sqlite3VdbeAddOp2(self.raw, opcode, p1, p2) }
} else if p1 != 0 {
unsafe { ffi::sqlite3VdbeAddOp1(self.raw, opcode, p1) }
} else {
unsafe { ffi::sqlite3VdbeAddOp0(self.raw, opcode) }
};
if p5 != 0 {
unsafe { ffi::sqlite3VdbeChangeP5(self.raw, p5) };
}
Address(addr)
}
pub fn add_op0(&mut self, op: Opcode) -> Address {
let addr = unsafe { ffi::sqlite3VdbeAddOp0(self.raw, op as i32) };
Address(addr)
}
pub fn add_op1(&mut self, op: Opcode, p1: i32) -> Address {
let addr = unsafe { ffi::sqlite3VdbeAddOp1(self.raw, op as i32, p1) };
Address(addr)
}
pub fn add_op2(&mut self, op: Opcode, p1: i32, p2: i32) -> Address {
let addr = unsafe { ffi::sqlite3VdbeAddOp2(self.raw, op as i32, p1, p2) };
Address(addr)
}
pub fn add_op3(&mut self, op: Opcode, p1: i32, p2: i32, p3: i32) -> Address {
let addr = unsafe { ffi::sqlite3VdbeAddOp3(self.raw, op as i32, p1, p2, p3) };
Address(addr)
}
pub fn add_op4_int(&mut self, op: Opcode, p1: i32, p2: i32, p3: i32, p4: i32) -> Address {
let addr = unsafe { ffi::sqlite3VdbeAddOp4Int(self.raw, op as i32, p1, p2, p3, p4) };
Address(addr)
}
pub fn add_op4_str(
&mut self,
op: Opcode,
p1: i32,
p2: i32,
p3: i32,
p4: &str,
) -> Result<Address> {
let c_str = CString::new(p4)?;
let addr = unsafe {
ffi::sqlite3VdbeAddOp4(
self.raw,
op as i32,
p1,
p2,
p3,
c_str.as_ptr(),
ffi::P4_DYNAMIC, )
};
Ok(Address(addr))
}
pub fn change_p5(&mut self, p5: u16) {
unsafe {
ffi::sqlite3VdbeChangeP5(self.raw, p5);
}
}
pub fn current_addr(&self) -> Address {
let addr = unsafe { ffi::sqlite3VdbeCurrentAddr(self.raw) };
Address(addr)
}
pub fn jump_here(&mut self, addr: Address) {
unsafe {
ffi::sqlite3VdbeJumpHere(self.raw, addr.0);
}
}
pub fn make_label(&mut self) -> i32 {
unsafe { ffi::sqlite3_vdbe_make_label(self.raw) }
}
pub fn resolve_label(&mut self, label: i32) {
unsafe {
ffi::sqlite3_vdbe_resolve_label(self.raw, label);
}
}
pub fn op_count(&self) -> i32 {
unsafe { ffi::sqlite3_vdbe_op_count(self.raw) }
}
pub fn finish(mut self, num_columns: u16) -> Result<Program> {
unsafe {
ffi::sqlite3VdbeSetNumCols(self.raw, num_columns as i32);
ffi::sqlite3_vdbe_make_ready(self.raw, self.next_register, self.next_cursor);
}
let instructions = std::mem::take(&mut self.instructions);
let program = Program {
raw: self.raw,
db: self.db,
done: false,
instructions,
_marker: PhantomData,
};
std::mem::forget(self);
Ok(program)
}
pub unsafe fn raw_ptr(&self) -> *mut ffi::Vdbe {
self.raw
}
}
impl Drop for ProgramBuilder {
fn drop(&mut self) {
if !self.raw.is_null() {
unsafe {
ffi::sqlite3_finalize(self.raw as *mut ffi::sqlite3_stmt);
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StepResult {
Row,
Done,
}
pub struct Program {
raw: *mut ffi::Vdbe,
db: *mut ffi::sqlite3,
done: bool,
instructions: Vec<InsnRecord>,
_marker: PhantomData<*const ()>,
}
impl Program {
pub fn step(&mut self) -> Result<StepResult> {
let rc = unsafe { ffi::sqlite3_step(self.raw as *mut ffi::sqlite3_stmt) };
match rc {
ffi::SQLITE_ROW => Ok(StepResult::Row),
ffi::SQLITE_DONE => {
self.done = true;
Ok(StepResult::Done)
}
_ => {
let msg = unsafe {
let err = ffi::sqlite3_errmsg(self.db);
if err.is_null() {
String::new()
} else {
std::ffi::CStr::from_ptr(err).to_string_lossy().into_owned()
}
};
Err(Error::from_code_with_message(rc, msg))
}
}
}
pub fn is_done(&self) -> bool {
self.done
}
pub fn column_count(&self) -> i32 {
unsafe { ffi::sqlite3_column_count(self.raw as *mut ffi::sqlite3_stmt) }
}
pub fn column_type(&self, idx: i32) -> i32 {
unsafe { ffi::sqlite3_column_type(self.raw as *mut ffi::sqlite3_stmt, idx) }
}
pub fn column_int(&self, idx: i32) -> i32 {
unsafe { ffi::sqlite3_column_int(self.raw as *mut ffi::sqlite3_stmt, idx) }
}
pub fn column_int64(&self, idx: i32) -> i64 {
unsafe { ffi::sqlite3_column_int64(self.raw as *mut ffi::sqlite3_stmt, idx) }
}
pub fn column_double(&self, idx: i32) -> f64 {
unsafe { ffi::sqlite3_column_double(self.raw as *mut ffi::sqlite3_stmt, idx) }
}
pub fn column_text(&self, idx: i32) -> Option<&str> {
unsafe {
let ptr = ffi::sqlite3_column_text(self.raw as *mut ffi::sqlite3_stmt, idx);
if ptr.is_null() {
return None;
}
let bytes = ffi::sqlite3_column_bytes(self.raw as *mut ffi::sqlite3_stmt, idx);
let slice = std::slice::from_raw_parts(ptr, bytes as usize);
std::str::from_utf8(slice).ok()
}
}
pub fn column_blob(&self, idx: i32) -> Option<&[u8]> {
unsafe {
let ptr = ffi::sqlite3_column_blob(self.raw as *mut ffi::sqlite3_stmt, idx);
if ptr.is_null() {
return None;
}
let bytes = ffi::sqlite3_column_bytes(self.raw as *mut ffi::sqlite3_stmt, idx);
Some(std::slice::from_raw_parts(ptr as *const u8, bytes as usize))
}
}
pub fn column_value(&self, idx: i32) -> Value {
let col_type = self.column_type(idx);
match col_type {
ffi::SQLITE_INTEGER => Value::Integer(self.column_int64(idx)),
ffi::SQLITE_FLOAT => Value::Real(self.column_double(idx)),
ffi::SQLITE_TEXT => self
.column_text(idx)
.map(|s| Value::Text(s.to_string()))
.unwrap_or(Value::Null),
ffi::SQLITE_BLOB => self
.column_blob(idx)
.map(|b| Value::Blob(b.to_vec()))
.unwrap_or(Value::Null),
ffi::SQLITE_NULL => Value::Null,
_ => Value::Null,
}
}
pub fn reset(&mut self) {
unsafe {
ffi::sqlite3_reset(self.raw as *mut ffi::sqlite3_stmt);
}
self.done = false;
}
pub fn clear_bindings(&mut self) {
unsafe {
ffi::sqlite3_clear_bindings(self.raw as *mut ffi::sqlite3_stmt);
}
}
pub fn state(&self) -> i32 {
unsafe { ffi::sqlite3_vdbe_state(self.raw) }
}
pub fn register_count(&self) -> i32 {
unsafe { ffi::sqlite3_vdbe_mem_count(self.raw) }
}
pub fn set_register_int(&mut self, reg: i32, value: i64) -> Result<()> {
let rc = unsafe { ffi::sqlite3_vdbe_set_int(self.raw, reg, value) };
if rc == ffi::SQLITE_OK {
Ok(())
} else {
Err(Error::RegisterOutOfBounds {
index: reg,
max: self.register_count(),
})
}
}
pub fn get_register_int(&self, reg: i32) -> i64 {
unsafe { ffi::sqlite3_vdbe_get_int(self.raw, reg) }
}
pub fn set_register_double(&mut self, reg: i32, value: f64) -> Result<()> {
let rc = unsafe { ffi::sqlite3_vdbe_set_double(self.raw, reg, value) };
if rc == ffi::SQLITE_OK {
Ok(())
} else {
Err(Error::RegisterOutOfBounds {
index: reg,
max: self.register_count(),
})
}
}
pub fn get_register_double(&self, reg: i32) -> f64 {
unsafe { ffi::sqlite3_vdbe_get_double(self.raw, reg) }
}
pub fn set_register_null(&mut self, reg: i32) -> Result<()> {
let rc = unsafe { ffi::sqlite3_vdbe_set_null(self.raw, reg) };
if rc == ffi::SQLITE_OK {
Ok(())
} else {
Err(Error::RegisterOutOfBounds {
index: reg,
max: self.register_count(),
})
}
}
pub fn is_register_null(&self, reg: i32) -> bool {
unsafe { ffi::sqlite3_vdbe_is_null(self.raw, reg) != 0 }
}
pub unsafe fn raw_ptr(&self) -> *mut ffi::Vdbe {
self.raw
}
pub fn instructions(&self) -> &[InsnRecord] {
&self.instructions
}
}
impl fmt::Display for Program {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"{:<6}{:<15}{:<6}{:<6}{:<6}{:<15}{:<4}comment",
"addr", "opcode", "p1", "p2", "p3", "p4", "p5"
)?;
writeln!(
f,
"{:<6}{:<15}{:<6}{:<6}{:<6}{:<15}{:<4}-------------",
"----", "-------------", "----", "----", "----", "-------------", "--"
)?;
for (addr, insn) in self.instructions.iter().enumerate() {
writeln!(
f,
"{:<6}{:<15}{:<6}{:<6}{:<6}{:<15}{:<4}{}",
addr, insn.opcode, insn.p1, insn.p2, insn.p3, insn.p4, insn.p5, insn.comment
)?;
}
Ok(())
}
}
impl Drop for Program {
fn drop(&mut self) {
if !self.raw.is_null() {
unsafe {
ffi::sqlite3_finalize(self.raw as *mut ffi::sqlite3_stmt);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_address_display() {
let addr = Address(42);
assert_eq!(format!("{}", addr), "@42");
}
}