1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#![allow(clippy::missing_safety_doc)]

use crate::error::Result;

#[derive(Debug)]
pub struct Statement {
    pub raw_stmt: *mut crate::ffi::sqlite3_stmt,
}

// Safety: works as long as libSQL is compiled and set up with SERIALIZABLE threading model, which is the default.
unsafe impl Sync for Statement {}

// Safety: works as long as libSQL is compiled and set up with SERIALIZABLE threading model, which is the default.
unsafe impl Send for Statement {}

impl Drop for Statement {
    fn drop(&mut self) {
        if !self.raw_stmt.is_null() {
            unsafe {
                crate::ffi::sqlite3_finalize(self.raw_stmt);
            }
        }
    }
}

impl Statement {
    pub fn bind_null(&self, idx: i32) {
        unsafe {
            crate::ffi::sqlite3_bind_null(self.raw_stmt, idx);
        }
    }

    pub fn bind_int64(&self, idx: i32, value: i64) {
        unsafe {
            crate::ffi::sqlite3_bind_int64(self.raw_stmt, idx, value);
        }
    }

    pub fn bind_double(&self, idx: i32, value: f64) {
        unsafe {
            crate::ffi::sqlite3_bind_double(self.raw_stmt, idx, value);
        }
    }

    pub fn bind_text(&self, idx: i32, value: &[u8]) {
        unsafe {
            crate::ffi::sqlite3_bind_text(
                self.raw_stmt,
                idx,
                value.as_ptr() as *const i8,
                value.len() as i32,
                None,
            );
        }
    }

    pub fn bind_blob(&self, idx: i32, value: &[u8]) {
        unsafe {
            crate::ffi::sqlite3_bind_blob(
                self.raw_stmt,
                idx,
                value.as_ptr() as *const std::ffi::c_void,
                value.len() as i32,
                None,
            );
        }
    }

    pub fn step(&self) -> std::ffi::c_int {
        unsafe { crate::ffi::sqlite3_step(self.raw_stmt) }
    }

    pub fn reset(&self) -> std::ffi::c_int {
        unsafe { crate::ffi::sqlite3_reset(self.raw_stmt) }
    }

    pub fn column_count(&self) -> i32 {
        unsafe { crate::ffi::sqlite3_column_count(self.raw_stmt) }
    }

    pub fn column_value(&self, idx: i32) -> crate::Value {
        let raw_value = unsafe { crate::ffi::sqlite3_column_value(self.raw_stmt, idx) };
        crate::Value { raw_value }
    }

    pub fn column_type(&self, idx: i32) -> i32 {
        unsafe { crate::ffi::sqlite3_column_type(self.raw_stmt, idx) }
    }

    pub fn column_name(&self, idx: i32) -> &str {
        let raw_name = unsafe { crate::ffi::sqlite3_column_name(self.raw_stmt, idx) };
        let raw_name = unsafe { std::ffi::CStr::from_ptr(raw_name as *const i8) };
        let raw_name = raw_name.to_str().unwrap();
        raw_name
    }

    pub fn bind_parameter_index(&self, name: &str) -> i32 {
        let raw_name = std::ffi::CString::new(name).unwrap();

        unsafe { crate::ffi::sqlite3_bind_parameter_index(self.raw_stmt, raw_name.as_ptr()) }
    }

    pub fn bind_parameter_count(&self) -> usize {
        unsafe { crate::ffi::sqlite3_bind_parameter_count(self.raw_stmt) as usize }
    }

    pub fn bind_parameter_name(&self, index: i32) -> Option<&str> {
        unsafe {
            let name = crate::ffi::sqlite3_bind_parameter_name(self.raw_stmt, index);
            if name.is_null() {
                None
            } else {
                // NOTICE: unwrap(), because SQLite promises it's valid UTF-8
                Some(std::ffi::CStr::from_ptr(name).to_str().unwrap())
            }
        }
    }

    pub fn get_status(&self, status: i32) -> i32 {
        unsafe { crate::ffi::sqlite3_stmt_status(self.raw_stmt, status as i32, 0) }
    }

    pub fn is_explain(&self) -> i32 {
        unsafe { crate::ffi::sqlite3_stmt_isexplain(self.raw_stmt) }
    }

    pub fn readonly(&self) -> bool {
        unsafe { crate::ffi::sqlite3_stmt_readonly(self.raw_stmt) != 0 }
    }
}

pub unsafe fn prepare_stmt(raw: *mut crate::ffi::sqlite3, sql: &str) -> Result<Statement> {
    let mut raw_stmt = std::ptr::null_mut();
    let err = unsafe {
        crate::ffi::sqlite3_prepare_v2(
            raw,
            sql.as_ptr() as *const i8,
            sql.len() as i32,
            &mut raw_stmt,
            std::ptr::null_mut(),
        )
    };
    match err as u32 {
        crate::ffi::SQLITE_OK => Ok(Statement { raw_stmt }),
        _ => Err(err.into()),
    }
}