Skip to main content

rbdc_sqlite/
arguments.rs

1use crate::encode::{Encode, IsNull};
2use crate::statement::StatementHandle;
3use atoi::atoi;
4use libsqlite3_sys::SQLITE_OK;
5use rbdc::err_protocol;
6use rbdc::error::Error;
7
8#[derive(Debug, Clone)]
9pub enum SqliteArgumentValue {
10    Null,
11    Text(String),
12    Blob(Vec<u8>),
13    Double(f64),
14    Int(i32),
15    Int64(i64),
16}
17
18#[derive(Default, Debug, Clone)]
19pub struct SqliteArguments {
20    pub(crate) values: Vec<SqliteArgumentValue>,
21}
22
23impl SqliteArguments {
24    pub fn add<T>(&mut self, value: T) -> Result<(), Error>
25    where
26        T: Encode,
27    {
28        if let IsNull::Yes = value.encode(&mut self.values)? {
29            self.values.push(SqliteArgumentValue::Null);
30        }
31        Ok(())
32    }
33
34    pub(crate) fn into_static(self) -> SqliteArguments {
35        SqliteArguments {
36            values: self
37                .values
38                .into_iter()
39                .map(SqliteArgumentValue::into_static)
40                .collect(),
41        }
42    }
43
44    pub fn reserve(&mut self, len: usize, _size_hint: usize) {
45        self.values.reserve(len);
46    }
47}
48
49impl SqliteArguments {
50    pub(super) fn bind(&self, handle: &mut StatementHandle, offset: usize) -> Result<usize, Error> {
51        let mut arg_i = offset;
52        // for handle in &statement.handles {
53
54        let cnt = handle.bind_parameter_count();
55
56        for param_i in 1..=cnt {
57            // figure out the index of this bind parameter into our argument tuple
58            let n: usize = if let Some(name) = handle.bind_parameter_name(param_i) {
59                if let Some(name) = name.strip_prefix('?') {
60                    // parameter should have the form ?NNN
61                    atoi(name.as_bytes()).ok_or_else(|| {
62                        err_protocol!("invalid parameter index in ?NNN format: {}", name)
63                    })?
64                } else if let Some(name) = name.strip_prefix('$') {
65                    // parameter should have the form $NNN
66                    atoi(name.as_bytes()).ok_or_else(|| {
67                        err_protocol!(
68                            "parameters with non-integer names are not currently supported: {}",
69                            name
70                        )
71                    })?
72                } else {
73                    return Err(err_protocol!("unsupported SQL parameter format: {}", name));
74                }
75            } else {
76                arg_i += 1;
77                arg_i
78            };
79
80            if n > self.values.len() {
81                // SQLite treats unbound variables as NULL
82                // we reproduce this here
83                // If you are reading this and think this should be an error, open an issue and we can
84                // discuss configuring this somehow
85                // Note that the query macros have a different way of enforcing
86                // argument arity
87                break;
88            }
89
90            self.values[n - 1].bind(handle, param_i)?;
91        }
92
93        Ok(arg_i - offset)
94    }
95}
96
97impl SqliteArgumentValue {
98    fn into_static(self) -> SqliteArgumentValue {
99        use SqliteArgumentValue::*;
100
101        match self {
102            Null => Null,
103            Text(text) => Text(text),
104            Blob(blob) => Blob(blob),
105            Int(v) => Int(v),
106            Int64(v) => Int64(v),
107            Double(v) => Double(v),
108        }
109    }
110
111    fn bind(&self, handle: &mut StatementHandle, i: usize) -> Result<(), Error> {
112        use SqliteArgumentValue::*;
113
114        let status = match self {
115            Text(v) => handle.bind_text(i, v),
116            Blob(v) => handle.bind_blob(i, v),
117            Int(v) => handle.bind_int(i, *v),
118            Int64(v) => handle.bind_int64(i, *v),
119            Double(v) => handle.bind_double(i, *v),
120            Null => handle.bind_null(i),
121        };
122
123        if status != SQLITE_OK {
124            return Err(handle.last_error().into());
125        }
126
127        Ok(())
128    }
129}