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