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