Skip to main content

spg_sqlx/
arguments.rs

1//! v7.16.0 — `sqlx::Arguments` for SPG. The bind buffer holds
2//! engine-side [`Value`]s; the per-type `Encode` impls push the
3//! converted Value into the buffer at `bind` time.
4
5use sqlx_core::arguments::Arguments;
6use sqlx_core::encode::{Encode, IsNull};
7use sqlx_core::error::BoxDynError;
8use sqlx_core::impl_into_arguments_for_arguments;
9use sqlx_core::types::Type;
10
11use spg_embedded::Value as EngineValue;
12
13use crate::database::Spg;
14use crate::type_info::SpgTypeInfo;
15
16/// One bound argument. Wraps the engine-side value plus its
17/// fixed [`SpgTypeInfo`] so the engine's executor sees a
18/// pre-coerced value (matches the engine's `Expr::Placeholder`
19/// → `params[N-1]` substitution path that v6.1.1 wired up for
20/// the pgwire extended-query protocol).
21#[derive(Debug, Clone)]
22pub struct SpgArgumentValue<'q> {
23    /// Owned engine value. `'q` lifetime is unused today but
24    /// reserved to match sqlx's generic shape — future borrowed-
25    /// bind paths (large BYTEA / TEXT[]) can flow through
26    /// without a breaking change to the Arguments contract.
27    pub value: EngineValue,
28    /// The type the bind site claims for this value. Empty
29    /// when the bind reached the buffer via `add_unchecked`.
30    pub type_info: Option<SpgTypeInfo>,
31    /// Marker for the borrow lifetime.
32    pub _phantom: core::marker::PhantomData<&'q ()>,
33}
34
35/// Buffer of bound arguments for one `Execute` call. Indexed
36/// 0..N; PG-style `$1` resolves to slot 0.
37#[derive(Debug, Clone, Default)]
38pub struct SpgArguments<'q> {
39    /// The bound values. `Vec<SpgArgumentValue>` so future
40    /// borrowed cases stay non-breaking.
41    pub values: Vec<SpgArgumentValue<'q>>,
42}
43
44impl<'q> SpgArguments<'q> {
45    /// Drain the buffer into an owned `Vec<EngineValue>` shaped
46    /// for [`spg_embedded::Database::execute_prepared`]. Called
47    /// by the connection's execute path right before the engine
48    /// dispatch.
49    #[must_use]
50    pub fn into_engine_values(self) -> Vec<EngineValue> {
51        self.values.into_iter().map(|a| a.value).collect()
52    }
53
54    /// Convenience: number of bound args. `len()` is what sqlx-
55    /// core uses to validate the placeholder count at
56    /// `Arguments::len` time.
57    #[must_use]
58    pub fn count(&self) -> usize {
59        self.values.len()
60    }
61}
62
63impl<'q> Arguments<'q> for SpgArguments<'q> {
64    type Database = Spg;
65
66    fn reserve(&mut self, additional: usize, _size: usize) {
67        self.values.reserve(additional);
68    }
69
70    fn add<T>(&mut self, value: T) -> Result<(), BoxDynError>
71    where
72        T: 'q + Encode<'q, Self::Database> + Type<Self::Database>,
73    {
74        // Push a placeholder slot so the per-type Encode can
75        // write its IsNull / engine-Value into it, mirroring
76        // sqlx-core's per-driver pattern. The Encode impls in
77        // `crate::types` resolve the slot via the back of the
78        // `values` vec.
79        self.values.push(SpgArgumentValue {
80            value: EngineValue::Null,
81            type_info: Some(T::type_info()),
82            _phantom: core::marker::PhantomData,
83        });
84        let mut buf: Vec<SpgArgumentValue<'q>> = Vec::new();
85        let is_null = value.encode_by_ref(&mut buf)?;
86        // The encode impl appended one new slot. Move it into
87        // the position we just reserved and drop the placeholder.
88        if let Some(written) = buf.pop() {
89            let idx = self.values.len() - 1;
90            self.values[idx] = written;
91        }
92        if matches!(is_null, IsNull::Yes) {
93            let idx = self.values.len() - 1;
94            self.values[idx].value = EngineValue::Null;
95        }
96        Ok(())
97    }
98
99    fn len(&self) -> usize {
100        self.values.len()
101    }
102}
103
104// sqlx-core's macro generates the trivial `IntoArguments` impl
105// that lets `query.bind(...)` flow through `query.execute(pool)`.
106impl_into_arguments_for_arguments!(SpgArguments<'q>);