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