Skip to main content

spg_sqlx/
value.rs

1//! v7.16.0 — `sqlx::Value` + `sqlx::ValueRef` for SPG cells.
2
3use std::borrow::Cow;
4
5use sqlx_core::error::BoxDynError;
6use sqlx_core::value::{Value, ValueRef};
7
8use spg_embedded::Value as EngineValue;
9
10use crate::database::Spg;
11use crate::type_info::{Kind, SpgTypeInfo};
12
13/// Owned form of an SPG cell as it comes back from a query.
14/// Wraps [`spg_embedded::Value`] + the column's static
15/// [`SpgTypeInfo`] so the decode path can drive sqlx's type-
16/// compatibility check (`Decode::compatible`).
17#[derive(Debug, Clone)]
18pub struct SpgValue {
19    inner: EngineValue,
20    type_info: SpgTypeInfo,
21}
22
23impl SpgValue {
24    /// Wrap an engine value with its column type info.
25    #[must_use]
26    pub fn new(inner: EngineValue, type_info: SpgTypeInfo) -> Self {
27        Self { inner, type_info }
28    }
29
30    /// Borrow the underlying engine value.
31    #[must_use]
32    pub const fn engine(&self) -> &EngineValue {
33        &self.inner
34    }
35}
36
37impl Value for SpgValue {
38    type Database = Spg;
39
40    fn as_ref(&self) -> SpgValueRef<'_> {
41        SpgValueRef::new(&self.inner, self.type_info.clone())
42    }
43
44    fn type_info(&self) -> Cow<'_, SpgTypeInfo> {
45        Cow::Borrowed(&self.type_info)
46    }
47
48    fn is_null(&self) -> bool {
49        matches!(self.inner, EngineValue::Null)
50    }
51}
52
53/// Borrowed form of an SPG cell. Returned by [`SpgRow::try_get_raw`][crate::row::SpgRow]
54/// to let Decode implementations read the value without taking
55/// ownership.
56#[derive(Debug, Clone)]
57pub struct SpgValueRef<'r> {
58    inner: &'r EngineValue,
59    type_info: SpgTypeInfo,
60}
61
62impl<'r> SpgValueRef<'r> {
63    /// Build a borrowed cell + its column type info.
64    #[must_use]
65    pub fn new(inner: &'r EngineValue, type_info: SpgTypeInfo) -> Self {
66        Self { inner, type_info }
67    }
68
69    /// Borrow the underlying engine value.
70    #[must_use]
71    pub const fn engine(&self) -> &'r EngineValue {
72        self.inner
73    }
74
75    /// The column kind tag for this value.
76    #[must_use]
77    pub const fn kind(&self) -> Kind {
78        self.type_info.kind()
79    }
80}
81
82impl<'r> ValueRef<'r> for SpgValueRef<'r> {
83    type Database = Spg;
84
85    fn to_owned(&self) -> SpgValue {
86        SpgValue {
87            inner: self.inner.clone(),
88            type_info: self.type_info.clone(),
89        }
90    }
91
92    fn type_info(&self) -> Cow<'_, SpgTypeInfo> {
93        Cow::Borrowed(&self.type_info)
94    }
95
96    fn is_null(&self) -> bool {
97        matches!(self.inner, EngineValue::Null)
98    }
99}
100
101/// Map an engine value's runtime variant to the corresponding
102/// [`Kind`] tag — used when a fetched row carries cells whose
103/// column metadata is not yet known to the adapter.
104#[must_use]
105#[allow(dead_code)]
106pub fn engine_value_kind(v: &EngineValue) -> Kind {
107    match v {
108        EngineValue::Null => Kind::Null,
109        EngineValue::SmallInt(_) => Kind::SmallInt,
110        EngineValue::Int(_) => Kind::Int,
111        EngineValue::BigInt(_) => Kind::BigInt,
112        EngineValue::Bool(_) => Kind::Bool,
113        EngineValue::Text(_) => Kind::Text,
114        EngineValue::Bytes(_) => Kind::Bytes,
115        EngineValue::Float(_) => Kind::Float,
116        EngineValue::Date(_) => Kind::Date,
117        EngineValue::Timestamp(_) => Kind::Timestamp,
118        EngineValue::Json(_) => Kind::Json,
119        // v7.17.0 Phase 3.P0-69 — UUID bridges to uuid::Uuid /
120        // String.
121        EngineValue::Uuid(_) => Kind::Uuid,
122        // v7.17.0 Phase 3.P0-67 — NUMERIC bridges to bigdecimal /
123        // String.
124        EngineValue::Numeric { .. } => Kind::Numeric,
125        // v7.17.0 Phase 3.P0-68 — pgvector (all encodings) /
126        // tsvector. Sq8 + Half storage variants dequantise to
127        // f32 inside Decode, so the column-side kind is still
128        // Vector.
129        EngineValue::Vector(_) | EngineValue::Sq8Vector(_) | EngineValue::HalfVector(_) => {
130            Kind::Vector
131        }
132        EngineValue::TsVector(_) => Kind::TsVector,
133        // v7.16.0 — non-exhaustive enum; future types
134        // (Interval, arrays beyond text/int/bigint, hstore) decode
135        // to Kind::Null until the corresponding Decode lands.
136        _ => Kind::Null,
137    }
138}
139
140/// Force a no-op use of [`BoxDynError`] so future Decode
141/// impls can return an unboxed error without losing the
142/// import path.
143#[allow(dead_code)]
144fn _box_dyn_marker() -> Option<BoxDynError> {
145    None
146}