1use crate::{CBox, error_message_from_ptr};
2use libsqlite3_sys::{
3 SQLITE_OK, SQLITE_TRANSIENT, sqlite3_bind_blob, sqlite3_bind_double, sqlite3_bind_int,
4 sqlite3_bind_int64, sqlite3_bind_null, sqlite3_bind_text, sqlite3_clear_bindings,
5 sqlite3_db_handle, sqlite3_errmsg, sqlite3_int64, sqlite3_sql, sqlite3_stmt,
6};
7use rust_decimal::prelude::ToPrimitive;
8use std::{
9 ffi::{CStr, c_int},
10 fmt::{self, Display},
11 os::raw::{c_char, c_void},
12};
13use tank_core::{AsValue, Error, Prepared, Result, Value, printable_query};
14
15pub struct SqlitePrepared {
16 pub(crate) statement: CBox<*mut sqlite3_stmt>,
17 pub(crate) index: u64,
18}
19
20impl SqlitePrepared {
21 pub(crate) fn new(prepared: CBox<*mut sqlite3_stmt>) -> Self {
22 unsafe {
23 sqlite3_clear_bindings(*prepared);
24 }
25 Self {
26 statement: prepared,
27 index: 1,
28 }
29 }
30}
31
32impl Prepared for SqlitePrepared {
33 fn bind<V: AsValue>(&mut self, value: V) -> Result<&mut Self> {
34 self.bind_index(value, self.index)
35 }
36 fn bind_index<V: AsValue>(&mut self, v: V, index: u64) -> Result<&mut Self> {
37 let index = index as c_int;
38 unsafe {
39 let value = v.as_value();
40 let rc = match value {
41 Value::Null
42 | Value::Boolean(None, ..)
43 | Value::Int8(None, ..)
44 | Value::Int16(None, ..)
45 | Value::Int32(None, ..)
46 | Value::Int64(None, ..)
47 | Value::Int128(None, ..)
48 | Value::UInt8(None, ..)
49 | Value::UInt16(None, ..)
50 | Value::UInt32(None, ..)
51 | Value::UInt64(None, ..)
52 | Value::UInt128(None, ..)
53 | Value::Float32(None, ..)
54 | Value::Float64(None, ..)
55 | Value::Decimal(None, ..)
56 | Value::Char(None, ..)
57 | Value::Varchar(None, ..)
58 | Value::Blob(None, ..)
59 | Value::Date(None, ..)
60 | Value::Time(None, ..)
61 | Value::Timestamp(None, ..)
62 | Value::TimestampWithTimezone(None, ..)
63 | Value::Interval(None, ..)
64 | Value::Uuid(None, ..)
65 | Value::Array(None, ..)
66 | Value::List(None, ..)
67 | Value::Map(None, ..)
68 | Value::Struct(None, ..) => sqlite3_bind_null(*self.statement, index),
69 Value::Boolean(Some(v), ..) => sqlite3_bind_int(*self.statement, index, v as c_int),
70 Value::Int8(Some(v), ..) => sqlite3_bind_int(*self.statement, index, v as c_int),
71 Value::Int16(Some(v), ..) => sqlite3_bind_int(*self.statement, index, v as c_int),
72 Value::Int32(Some(v), ..) => sqlite3_bind_int(*self.statement, index, v as c_int),
73 Value::Int64(Some(v), ..) => sqlite3_bind_int64(*self.statement, index, v),
74 Value::Int128(Some(v), ..) => {
75 if v as sqlite3_int64 as i128 != v {
76 return Err(Error::msg(
77 "Cannot bind i128 value `{}` into sqlite integer because it's out of bounds",
78 ));
79 }
80 sqlite3_bind_int64(*self.statement, index, v as sqlite3_int64)
81 }
82 Value::UInt8(Some(v), ..) => sqlite3_bind_int(*self.statement, index, v as c_int),
83 Value::UInt16(Some(v), ..) => sqlite3_bind_int(*self.statement, index, v as c_int),
84 Value::UInt32(Some(v), ..) => sqlite3_bind_int(*self.statement, index, v as c_int),
85 Value::UInt64(Some(v), ..) => {
86 if v as sqlite3_int64 as u64 != v {
87 return Err(Error::msg(
88 "Cannot bind i128 value `{}` into sqlite integer because it's out of bounds",
89 ));
90 }
91 sqlite3_bind_int64(*self.statement, index, v as sqlite3_int64)
92 }
93 Value::UInt128(Some(v), ..) => {
94 if v as sqlite3_int64 as u128 != v {
95 return Err(Error::msg(
96 "Cannot bind i128 value `{}` into sqlite integer because it's out of bounds",
97 ));
98 }
99 sqlite3_bind_int64(*self.statement, index, v as sqlite3_int64)
100 }
101 Value::Float32(Some(v), ..) => {
102 sqlite3_bind_double(*self.statement, index, v as f64)
103 }
104 Value::Float64(Some(v), ..) => sqlite3_bind_double(*self.statement, index, v),
105 Value::Decimal(Some(v), ..) => sqlite3_bind_double(
106 *self.statement,
107 index,
108 v.to_f64().ok_or_else(|| {
109 Error::msg(format!("Cannot convert the Decimal value `{}` to f64", v))
110 })?,
111 ),
112 Value::Char(Some(v), ..) => {
113 let v = v.to_string();
114 sqlite3_bind_text(
115 *self.statement,
116 index,
117 v.as_ptr() as *const c_char,
118 v.len() as c_int,
119 SQLITE_TRANSIENT(),
120 )
121 }
122 Value::Varchar(Some(v), ..) => sqlite3_bind_text(
123 *self.statement,
124 index,
125 v.as_ptr() as *const c_char,
126 v.len() as c_int,
127 SQLITE_TRANSIENT(),
128 ),
129 Value::Blob(Some(v), ..) => sqlite3_bind_blob(
130 *self.statement,
131 index,
132 v.as_ptr() as *const c_void,
133 v.len() as c_int,
134 SQLITE_TRANSIENT(),
135 ),
136 Value::Date(Some(v), ..) => {
137 let v = v.to_string();
138 sqlite3_bind_text(
139 *self.statement,
140 index,
141 v.as_ptr() as *const c_char,
142 v.len() as c_int,
143 SQLITE_TRANSIENT(),
144 )
145 }
146 Value::Time(Some(v), ..) => {
147 let v = v.to_string();
148 sqlite3_bind_text(
149 *self.statement,
150 index,
151 v.as_ptr() as *const c_char,
152 v.len() as c_int,
153 SQLITE_TRANSIENT(),
154 )
155 }
156 Value::Timestamp(Some(v), ..) => {
157 let v = v.to_string();
158 sqlite3_bind_text(
159 *self.statement,
160 index,
161 v.as_ptr() as *const c_char,
162 v.len() as c_int,
163 SQLITE_TRANSIENT(),
164 )
165 }
166 Value::TimestampWithTimezone(Some(v), ..) => {
167 let v = v.to_string();
168 sqlite3_bind_text(
169 *self.statement,
170 index,
171 v.as_ptr() as *const c_char,
172 v.len() as c_int,
173 SQLITE_TRANSIENT(),
174 )
175 }
176 Value::Uuid(Some(v), ..) => {
177 let v = v.to_string();
178 sqlite3_bind_text(
179 *self.statement,
180 index,
181 v.as_ptr() as *const c_char,
182 v.len() as c_int,
183 SQLITE_TRANSIENT(),
184 )
185 }
186 _ => {
187 let error =
188 Error::msg(format!("Cannot use a {:?} as a query parameter", value));
189 log::error!("{:#}", error);
190 return Err(error);
191 }
192 };
193 if rc != SQLITE_OK {
194 let db = sqlite3_db_handle(*self.statement);
195 let query = sqlite3_sql(*self.statement);
196 let error = Error::msg(error_message_from_ptr(&sqlite3_errmsg(db)).to_string())
197 .context(format!(
198 "Cannot bind parameter {} to query:\n{}",
199 index,
200 printable_query!(CStr::from_ptr(query).to_string_lossy())
201 ));
202 log::error!("{:#}", error);
203 return Err(error);
204 }
205 self.index = index as u64 + 1;
206 Ok(self)
207 }
208 }
209}
210
211impl Display for SqlitePrepared {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 write!(f, "{:p}", *self.statement)
214 }
215}