1use crate::db::DbError;
7use crate::sqlite_vfs::ffi;
8use std::ffi::{c_int, c_void, CString};
9use std::ptr;
10
11pub struct Null;
12
13pub const NULL: Null = Null;
14
15pub enum Value<'value> {
16 Text(&'value str),
17 Integer(i64),
18 Real(f64),
19 Blob(&'value [u8]),
20 Null,
21}
22
23pub trait ToSql {
24 fn bind_to(&self, statement: *mut ffi::sqlite3_stmt, index: c_int) -> Result<(), DbError>;
25}
26
27impl ToSql for Value<'_> {
28 fn bind_to(&self, statement: *mut ffi::sqlite3_stmt, index: c_int) -> Result<(), DbError> {
29 match self {
30 Value::Text(value) => bind_text(statement, index, value),
31 Value::Integer(value) => bind_i64(statement, index, *value),
32 Value::Real(value) => bind_f64(statement, index, *value),
33 Value::Blob(value) => bind_blob(statement, index, value),
34 Value::Null => bind_null(statement, index),
35 }
36 }
37}
38
39impl ToSql for &str {
40 fn bind_to(&self, statement: *mut ffi::sqlite3_stmt, index: c_int) -> Result<(), DbError> {
41 bind_text(statement, index, self)
42 }
43}
44
45impl ToSql for String {
46 fn bind_to(&self, statement: *mut ffi::sqlite3_stmt, index: c_int) -> Result<(), DbError> {
47 bind_text(statement, index, self)
48 }
49}
50
51impl ToSql for i64 {
52 fn bind_to(&self, statement: *mut ffi::sqlite3_stmt, index: c_int) -> Result<(), DbError> {
53 bind_i64(statement, index, *self)
54 }
55}
56
57impl ToSql for f64 {
58 fn bind_to(&self, statement: *mut ffi::sqlite3_stmt, index: c_int) -> Result<(), DbError> {
59 bind_f64(statement, index, *self)
60 }
61}
62
63impl ToSql for Vec<u8> {
64 fn bind_to(&self, statement: *mut ffi::sqlite3_stmt, index: c_int) -> Result<(), DbError> {
65 bind_blob(statement, index, self)
66 }
67}
68
69impl ToSql for &[u8] {
70 fn bind_to(&self, statement: *mut ffi::sqlite3_stmt, index: c_int) -> Result<(), DbError> {
71 bind_blob(statement, index, self)
72 }
73}
74
75impl ToSql for Null {
76 fn bind_to(&self, statement: *mut ffi::sqlite3_stmt, index: c_int) -> Result<(), DbError> {
77 bind_null(statement, index)
78 }
79}
80
81pub(crate) fn bind_all(
82 statement: *mut ffi::sqlite3_stmt,
83 values: &[&dyn ToSql],
84) -> Result<(), DbError> {
85 for (index, value) in values.iter().enumerate() {
86 let param = c_int::try_from(index + 1).map_err(|_| DbError::TooManyParameters)?;
87 value.bind_to(statement, param)?;
88 }
89 Ok(())
90}
91
92pub(crate) fn bind_named_all(
93 statement: *mut ffi::sqlite3_stmt,
94 values: &[(&str, &dyn ToSql)],
95) -> Result<(), DbError> {
96 for (name, value) in values {
97 let name_text = CString::new(*name).map_err(|_| DbError::InteriorNul)?;
98 let index = unsafe { ffi::sqlite3_bind_parameter_index(statement, name_text.as_ptr()) };
99 if index == 0 {
100 return Err(DbError::ParameterNotFound((*name).to_string()));
101 }
102 value.bind_to(statement, index)?;
103 }
104 Ok(())
105}
106
107fn bind_text(statement: *mut ffi::sqlite3_stmt, index: c_int, value: &str) -> Result<(), DbError> {
108 let value = CString::new(value).map_err(|_| DbError::InteriorNul)?;
109 let len = c_int::try_from(value.as_bytes().len()).map_err(|_| DbError::TextTooLarge)?;
110 let rc = unsafe {
111 ffi::sqlite3_bind_text(
112 statement,
113 index,
114 value.as_ptr(),
115 len,
116 ffi::SQLITE_TRANSIENT(),
117 )
118 };
119 bind_result(rc)
120}
121
122fn bind_i64(statement: *mut ffi::sqlite3_stmt, index: c_int, value: i64) -> Result<(), DbError> {
123 bind_result(unsafe { ffi::sqlite3_bind_int64(statement, index, value) })
124}
125
126fn bind_f64(statement: *mut ffi::sqlite3_stmt, index: c_int, value: f64) -> Result<(), DbError> {
127 bind_result(unsafe { ffi::sqlite3_bind_double(statement, index, value) })
128}
129
130fn bind_blob(statement: *mut ffi::sqlite3_stmt, index: c_int, value: &[u8]) -> Result<(), DbError> {
131 let len = c_int::try_from(value.len()).map_err(|_| DbError::BlobTooLarge)?;
132 let ptr = if value.is_empty() {
133 ptr::null()
134 } else {
135 value.as_ptr().cast::<c_void>()
136 };
137 let rc = unsafe { ffi::sqlite3_bind_blob(statement, index, ptr, len, ffi::SQLITE_TRANSIENT()) };
138 bind_result(rc)
139}
140
141fn bind_null(statement: *mut ffi::sqlite3_stmt, index: c_int) -> Result<(), DbError> {
142 bind_result(unsafe { ffi::sqlite3_bind_null(statement, index) })
143}
144
145fn bind_result(rc: c_int) -> Result<(), DbError> {
146 if rc == ffi::SQLITE_OK {
147 Ok(())
148 } else {
149 Err(DbError::Sqlite(rc, "sqlite bind failed".to_string()))
150 }
151}