Skip to main content

libsqlite3_sys/
native.rs

1//! Native value codecs and operations behind SQLite's common type dispatch.
2pub use crate::native_types::Kind;
3use crate::{
4    decimal::{Error, Result},
5    native_types::TYPES,
6};
7use chrono::Datelike;
8use std::ffi::c_char;
9use std::hash::{Hash, Hasher};
10
11pub fn encode(kind: Kind, text: &str) -> Result<Vec<u8>> {
12    let payload = match kind {
13        Kind::Uuid => uuid::Uuid::parse_str(text)
14            .map_err(|e| Error(e.to_string()))?
15            .as_bytes()
16            .to_vec(),
17        Kind::Decimal => return crate::decimal::encode(text),
18        Kind::Uint8 | Kind::Uint16 | Kind::Uint32 | Kind::Uint64 => {
19            return crate::unsigned::encode(kind, text);
20        }
21        _ => crate::temporal::encode(kind, text)?,
22    };
23    let descriptor = TYPES.iter().find(|d| d.kind == kind).unwrap();
24    let mut bytes = vec![descriptor.tag];
25    bytes.extend(payload);
26    Ok(bytes)
27}
28
29pub fn text(kind: Kind, bytes: &[u8]) -> Result<String> {
30    match kind {
31        Kind::Uuid => Ok(uuid::Uuid::from_bytes(
32            bytes
33                .try_into()
34                .map_err(|_| Error("invalid UUID storage".into()))?,
35        )
36        .to_string()),
37        Kind::Uint8 | Kind::Uint16 | Kind::Uint32 | Kind::Uint64 => {
38            Ok(crate::unsigned::read(bytes).to_string())
39        }
40        Kind::Decimal => std::str::from_utf8(bytes)
41            .map(str::to_owned)
42            .map_err(|e| Error(e.to_string())),
43        _ => crate::temporal::text(kind, bytes),
44    }
45}
46
47pub fn decode(bytes: &[u8]) -> Option<(Kind, String)> {
48    if bytes.is_empty() {
49        return None;
50    }
51    let descriptor = TYPES
52        .iter()
53        .find(|d| d.tag == bytes[0] && d.kind != Kind::Decimal)?;
54    if bytes.len() != 1 + descriptor.payload_size {
55        return None;
56    }
57    Some((descriptor.kind, text(descriptor.kind, &bytes[1..]).ok()?))
58}
59
60pub fn canonical(kind: Kind, value: &str) -> Result<String> {
61    let bytes = encode(kind, value)?;
62    text(kind, &bytes[1..])
63}
64
65pub fn cast(target: Kind, source: Kind, value: &str) -> Result<String> {
66    let bytes = encode(source, value)?;
67    if crate::unsigned::is_unsigned(target as i32) {
68        let converted =
69            crate::unsigned::evaluate(0, target as i32, source as i32, &bytes[1..], -3, &[])?;
70        return text(target, &converted[1..]);
71    }
72    if target == Kind::Decimal && crate::unsigned::is_unsigned(source as i32) {
73        return canonical(target, &text(source, &bytes[1..])?);
74    }
75    let payload = if target == source {
76        bytes[1..].to_vec()
77    } else {
78        crate::temporal::cast(target, source, &bytes[1..], false)?
79    };
80    text(target, &payload)
81}
82
83#[repr(C)]
84struct FunctionArgument {
85    kind: i32,
86    data: *const c_char,
87    length: i32,
88}
89
90#[unsafe(no_mangle)]
91unsafe extern "C" fn boxdb_native_function(
92    function: i32,
93    argc: i32,
94    args: *const FunctionArgument,
95    out: *mut *mut c_char,
96    out_len: *mut i32,
97) -> i32 {
98    let result = (|| {
99        if !(0..=7).contains(&argc) {
100            return Err(Error("invalid temporal function argument count".into()));
101        }
102        let args = unsafe { std::slice::from_raw_parts(args, argc as usize) }
103            .iter()
104            .map(|arg| crate::temporal_functions::Argument {
105                kind: arg.kind,
106                bytes: unsafe { input(arg.data, arg.length) },
107            })
108            .collect::<Vec<_>>();
109        let (kind, payload) = crate::temporal_functions::evaluate(function, &args)?;
110        let descriptor = TYPES.iter().find(|d| d.kind == kind).unwrap();
111        let mut bytes = vec![descriptor.tag];
112        bytes.extend(payload);
113        Ok(bytes)
114    })();
115    match result {
116        Ok(bytes) => unsafe { crate::decimal::output(&bytes, out, out_len) },
117        Err(error) => {
118            let rc = unsafe { crate::decimal::output(error.0.as_bytes(), out, out_len) };
119            if rc == crate::SQLITE_OK {
120                crate::SQLITE_ERROR
121            } else {
122                rc
123            }
124        }
125    }
126}
127
128fn ordering_key(kind: i32, bytes: &[u8]) -> i128 {
129    match kind {
130        3 | 5 => {
131            i128::from(i64::from_be_bytes(bytes[..8].try_into().unwrap())) * crate::temporal::SECOND
132                + i128::from(u32::from_be_bytes(bytes[8..12].try_into().unwrap()))
133        }
134        4 => {
135            (i128::from(i32::from_be_bytes(bytes[..4].try_into().unwrap()))
136                - i128::from(
137                    chrono::NaiveDate::from_ymd_opt(1970, 1, 1)
138                        .unwrap()
139                        .num_days_from_ce(),
140                ))
141                * crate::temporal::DAY
142        }
143        6 => {
144            let value = crate::temporal::read_interval(bytes).unwrap();
145            (i128::from(value.months) * 30 + i128::from(value.days)) * crate::temporal::DAY
146                + i128::from(value.micros)
147        }
148        _ => 0,
149    }
150}
151
152#[unsafe(no_mangle)]
153unsafe extern "C" fn boxdb_native_compare(
154    kind: i32,
155    a: *const c_char,
156    na: i32,
157    kind_b: i32,
158    b: *const c_char,
159    nb: i32,
160) -> i32 {
161    let a = unsafe { input(a, na) };
162    let b = unsafe { input(b, nb) };
163    if kind == 2 {
164        a.cmp(b) as i32
165    } else {
166        ordering_key(kind, a).cmp(&ordering_key(kind_b, b)) as i32
167    }
168}
169
170#[unsafe(no_mangle)]
171unsafe extern "C" fn boxdb_native_hash(kind: i32, data: *const c_char, length: i32) -> u64 {
172    let bytes = unsafe { input(data, length) };
173    if crate::unsigned::is_unsigned(kind) {
174        // SQLite's numeric Bloom hash saturates REAL/DECIMAL at INT64_MAX.
175        // Equal cross-type numbers must land in the same bucket.
176        return crate::unsigned::read(bytes).min(i64::MAX as u64);
177    }
178    let mut hash = std::collections::hash_map::DefaultHasher::new();
179    (if matches!(kind, 4 | 5) { 4 } else { kind }).hash(&mut hash);
180    if matches!(kind, 4 | 5 | 6) {
181        ordering_key(kind, bytes).hash(&mut hash);
182    } else {
183        bytes.hash(&mut hash);
184    }
185    hash.finish()
186}
187
188unsafe fn input<'a>(data: *const c_char, length: i32) -> &'a [u8] {
189    unsafe { std::slice::from_raw_parts(data.cast(), length as usize) }
190}
191
192#[unsafe(no_mangle)]
193unsafe extern "C" fn boxdb_native_text(
194    kind: i32,
195    data: *const c_char,
196    length: i32,
197    out: *mut *mut c_char,
198    out_len: *mut i32,
199) -> i32 {
200    let result = (|| {
201        let kind = TYPES
202            .iter()
203            .find(|d| d.kind as i32 == kind)
204            .ok_or_else(|| Error("unknown native type".into()))?
205            .kind;
206        text(kind, unsafe { input(data, length) })
207    })();
208    match result {
209        Ok(value) => unsafe { crate::decimal::output(value.as_bytes(), out, out_len) },
210        Err(error) => {
211            let rc = unsafe { crate::decimal::output(error.0.as_bytes(), out, out_len) };
212            if rc == crate::SQLITE_OK {
213                crate::SQLITE_ERROR
214            } else {
215                rc
216            }
217        }
218    }
219}
220
221#[unsafe(no_mangle)]
222unsafe extern "C" fn boxdb_native_eval(
223    operation: i32,
224    target: i32,
225    kind_a: i32,
226    data_a: *const c_char,
227    len_a: i32,
228    kind_b: i32,
229    data_b: *const c_char,
230    len_b: i32,
231    out: *mut *mut c_char,
232    out_len: *mut i32,
233    out_kind: *mut i32,
234) -> i32 {
235    let result = (|| {
236        if crate::unsigned::is_unsigned(target)
237            || crate::unsigned::is_unsigned(kind_a)
238            || crate::unsigned::is_unsigned(kind_b)
239        {
240            if target == 0 || crate::unsigned::is_unsigned(target) {
241                unsafe {
242                    *out_kind = if target == 0 {
243                        kind_a.max(kind_b)
244                    } else {
245                        target
246                    };
247                }
248                return crate::unsigned::evaluate(
249                    operation,
250                    target,
251                    kind_a,
252                    unsafe { input(data_a, len_a) },
253                    kind_b,
254                    unsafe { input(data_b, len_b) },
255                );
256            }
257        }
258        if operation != 0 && operation != 8 {
259            let (kind, payload) = crate::temporal::operation(
260                operation,
261                kind_a,
262                unsafe { input(data_a, len_a) },
263                kind_b,
264                unsafe { input(data_b, len_b) },
265            )?;
266            unsafe {
267                *out_kind = kind;
268            }
269            if kind < 0 {
270                return Ok(payload);
271            }
272            let descriptor = TYPES.iter().find(|d| d.kind as i32 == kind).unwrap();
273            let mut bytes = vec![descriptor.tag];
274            bytes.extend(payload);
275            return Ok(bytes);
276        }
277        let kind = TYPES
278            .iter()
279            .find(|d| d.kind as i32 == target)
280            .ok_or_else(|| Error("unknown native type".into()))?
281            .kind;
282        unsafe {
283            *out_kind = kind as i32;
284        }
285        if kind_a > 0 {
286            let source = TYPES
287                .iter()
288                .find(|d| d.kind as i32 == kind_a)
289                .ok_or_else(|| Error("unknown native type".into()))?
290                .kind;
291            let input = unsafe { input(data_a, len_a) };
292            let payload = if kind == source {
293                text(kind, input)?;
294                input.to_vec()
295            } else {
296                crate::temporal::cast(kind, source, input, operation == 8)?
297            };
298            let descriptor = TYPES.iter().find(|d| d.kind == kind).unwrap();
299            let mut bytes = vec![descriptor.tag];
300            bytes.extend(payload);
301            return Ok(bytes);
302        }
303        if kind_a != 0 {
304            return Err(Error("unsupported native conversion".into()));
305        }
306        let text = std::str::from_utf8(unsafe { input(data_a, len_a) })
307            .map_err(|e| Error(e.to_string()))?;
308        encode(kind, text)
309    })();
310    match result {
311        Ok(bytes) => unsafe { crate::decimal::output(&bytes, out, out_len) },
312        Err(error) => {
313            let rc = unsafe { crate::decimal::output(error.0.as_bytes(), out, out_len) };
314            if rc == crate::SQLITE_OK {
315                crate::SQLITE_ERROR
316            } else {
317                rc
318            }
319        }
320    }
321}