ankurah_storage_sqlite/
value.rs

1//! SQLite value type conversions
2
3use ankurah_core::value::Value;
4
5/// SQLite value wrapper for type mapping
6#[derive(Debug, Clone)]
7pub enum SqliteValue {
8    /// TEXT type for strings
9    Text(String),
10    /// INTEGER type for small integers
11    Integer(i64),
12    /// REAL type for floating point
13    Real(f64),
14    /// BLOB type for binary data
15    Blob(Vec<u8>),
16    /// JSONB type for JSON (stored as BLOB, queried via -> operator)
17    /// Note: SQLite JSONB is different from PostgreSQL JSONB but provides
18    /// similar benefits: pre-parsed binary format for faster queries.
19    Jsonb(serde_json::Value),
20    /// NULL
21    Null,
22}
23
24impl SqliteValue {
25    /// Get the SQLite type name for column creation
26    pub fn sqlite_type(&self) -> &'static str {
27        match self {
28            SqliteValue::Text(_) => "TEXT",
29            SqliteValue::Integer(_) => "INTEGER",
30            SqliteValue::Real(_) => "REAL",
31            SqliteValue::Blob(_) => "BLOB",
32            // JSONB is stored as BLOB in SQLite, but we use BLOB type
33            // and rely on jsonb() function for conversion
34            SqliteValue::Jsonb(_) => "BLOB",
35            SqliteValue::Null => "TEXT", // Default to TEXT for NULL
36        }
37    }
38
39    /// Check if this value is a JSONB type that needs special SQL handling
40    pub fn is_jsonb(&self) -> bool { matches!(self, SqliteValue::Jsonb(_)) }
41
42    /// Get the JSON string representation (for use with jsonb() function)
43    pub fn as_json_string(&self) -> Option<String> {
44        match self {
45            SqliteValue::Jsonb(j) => Some(j.to_string()),
46            _ => None,
47        }
48    }
49
50    /// Convert to a rusqlite parameter value
51    /// Note: For JSONB, this returns the JSON text - the caller must wrap with jsonb()
52    pub fn to_sql(&self) -> rusqlite::types::Value {
53        match self {
54            SqliteValue::Text(s) => rusqlite::types::Value::Text(s.clone()),
55            SqliteValue::Integer(i) => rusqlite::types::Value::Integer(*i),
56            SqliteValue::Real(f) => rusqlite::types::Value::Real(*f),
57            SqliteValue::Blob(b) => rusqlite::types::Value::Blob(b.clone()),
58            // Return as text - the SQL query will wrap this with jsonb()
59            SqliteValue::Jsonb(j) => rusqlite::types::Value::Text(j.to_string()),
60            SqliteValue::Null => rusqlite::types::Value::Null,
61        }
62    }
63}
64
65impl From<Value> for SqliteValue {
66    fn from(value: Value) -> Self {
67        match value {
68            Value::String(s) => SqliteValue::Text(s),
69            Value::I16(i) => SqliteValue::Integer(i as i64),
70            Value::I32(i) => SqliteValue::Integer(i as i64),
71            Value::I64(i) => SqliteValue::Integer(i),
72            Value::F64(f) => SqliteValue::Real(f),
73            Value::Bool(b) => SqliteValue::Integer(if b { 1 } else { 0 }),
74            Value::EntityId(id) => SqliteValue::Text(id.to_base64()),
75            Value::Object(bytes) => SqliteValue::Blob(bytes),
76            Value::Binary(bytes) => SqliteValue::Blob(bytes),
77            Value::Json(json) => SqliteValue::Jsonb(json),
78        }
79    }
80}
81
82impl From<Option<Value>> for SqliteValue {
83    fn from(value: Option<Value>) -> Self {
84        match value {
85            Some(v) => v.into(),
86            None => SqliteValue::Null,
87        }
88    }
89}
90
91/// Convert rusqlite Value to our SqliteValue
92impl From<rusqlite::types::Value> for SqliteValue {
93    fn from(value: rusqlite::types::Value) -> Self {
94        match value {
95            rusqlite::types::Value::Null => SqliteValue::Null,
96            rusqlite::types::Value::Integer(i) => SqliteValue::Integer(i),
97            rusqlite::types::Value::Real(f) => SqliteValue::Real(f),
98            rusqlite::types::Value::Text(s) => SqliteValue::Text(s),
99            rusqlite::types::Value::Blob(b) => SqliteValue::Blob(b),
100        }
101    }
102}