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()).expect("parameter of the form ?NNN")
62                } else if let Some(name) = name.strip_prefix('$') {
63                    // parameter should have the form $NNN
64                    atoi(name.as_bytes()).ok_or_else(|| {
65                        err_protocol!(
66                            "parameters with non-integer names are not currently supported: {}",
67                            name
68                        )
69                    })?
70                } else {
71                    return Err(err_protocol!("unsupported SQL parameter format: {}", name));
72                }
73            } else {
74                arg_i += 1;
75                arg_i
76            };
77
78            if n > self.values.len() {
79                // SQLite treats unbound variables as NULL
80                // we reproduce this here
81                // If you are reading this and think this should be an error, open an issue and we can
82                // discuss configuring this somehow
83                // Note that the query macros have a different way of enforcing
84                // argument arity
85                break;
86            }
87
88            self.values[n - 1].bind(handle, param_i)?;
89        }
90
91        Ok(arg_i - offset)
92    }
93}
94
95impl SqliteArgumentValue {
96    fn into_static(self) -> SqliteArgumentValue {
97        use SqliteArgumentValue::*;
98
99        match self {
100            Null => Null,
101            Text(text) => Text(text),
102            Blob(blob) => Blob(blob),
103            Int(v) => Int(v),
104            Int64(v) => Int64(v),
105            Double(v) => Double(v),
106        }
107    }
108
109    fn bind(&self, handle: &mut StatementHandle, i: usize) -> Result<(), Error> {
110        use SqliteArgumentValue::*;
111
112        let status = match self {
113            Text(v) => handle.bind_text(i, v),
114            Blob(v) => handle.bind_blob(i, v),
115            Int(v) => handle.bind_int(i, *v),
116            Int64(v) => handle.bind_int64(i, *v),
117            Double(v) => handle.bind_double(i, *v),
118            Null => handle.bind_null(i),
119        };
120
121        if status != SQLITE_OK {
122            return Err(handle.last_error().into());
123        }
124
125        Ok(())
126    }
127}