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