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()).ok_or_else(|| {
62 err_protocol!("invalid parameter index in ?NNN format: {}", name)
63 })?
64 } else if let Some(name) = name.strip_prefix('$') {
65 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 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}