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 let cnt = handle.bind_parameter_count();
55
56 for param_i in 1..=cnt {
57 let n: usize = if let Some(name) = handle.bind_parameter_name(param_i) {
59 if let Some(name) = name.strip_prefix('?') {
60 atoi(name.as_bytes()).expect("parameter of the form ?NNN")
62 } else if let Some(name) = name.strip_prefix('$') {
63 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 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}