sqlx_sqlite/
arguments.rs

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