sqlx_core_oldapi/postgres/
arguments.rs

1use std::fmt::{self, Write};
2use std::ops::{Deref, DerefMut};
3
4use crate::arguments::Arguments;
5use crate::encode::{Encode, IsNull};
6use crate::error::Error;
7use crate::ext::ustr::UStr;
8use crate::postgres::{PgConnection, PgTypeInfo, Postgres};
9use crate::types::Type;
10
11// TODO: buf.patch(|| ...) is a poor name, can we think of a better name? Maybe `buf.lazy(||)` ?
12// TODO: Extend the patch system to support dynamic lengths
13//       Considerations:
14//          - The prefixed-len offset needs to be back-tracked and updated
15//          - message::Bind needs to take a &PgArguments and use a `write` method instead of
16//            referencing a buffer directly
17//          - The basic idea is that we write bytes for the buffer until we get somewhere
18//            that has a patch, we then apply the patch which should write to &mut Vec<u8>,
19//            backtrack and update the prefixed-len, then write until the next patch offset
20
21#[derive(Default)]
22pub struct PgArgumentBuffer {
23    buffer: Vec<u8>,
24
25    // Number of arguments
26    count: usize,
27
28    // Whenever an `Encode` impl needs to defer some work until after we resolve parameter types
29    // it can use `patch`.
30    //
31    // This currently is only setup to be useful if there is a *fixed-size* slot that needs to be
32    // tweaked from the input type. However, that's the only use case we currently have.
33    //
34    patches: Vec<(
35        usize, // offset
36        usize, // argument index
37        Box<dyn Fn(&mut [u8], &PgTypeInfo) + 'static + Send + Sync>,
38    )>,
39
40    // Whenever an `Encode` impl encounters a `PgTypeInfo` object that does not have an OID
41    // It pushes a "hole" that must be patched later.
42    //
43    // The hole is a `usize` offset into the buffer with the type name that should be resolved
44    // This is done for Records and Arrays as the OID is needed well before we are in an async
45    // function and can just ask postgres.
46    //
47    type_holes: Vec<(usize, UStr)>, // Vec<{ offset, type_name }>
48}
49
50/// Implementation of [`Arguments`] for PostgreSQL.
51#[derive(Default)]
52pub struct PgArguments {
53    // Types of each bind parameter
54    pub(crate) types: Vec<PgTypeInfo>,
55
56    // Buffer of encoded bind parameters
57    pub(crate) buffer: PgArgumentBuffer,
58}
59
60impl PgArguments {
61    pub(crate) fn add<'q, T>(&mut self, value: T)
62    where
63        T: Encode<'q, Postgres> + Type<Postgres>,
64    {
65        // remember the type information for this value
66        self.types
67            .push(value.produces().unwrap_or_else(T::type_info));
68
69        // encode the value into our buffer
70        self.buffer.encode(value);
71
72        // increment the number of arguments we are tracking
73        self.buffer.count += 1;
74    }
75
76    // Apply patches
77    // This should only go out and ask postgres if we have not seen the type name yet
78    pub(crate) async fn apply_patches(
79        &mut self,
80        conn: &mut PgConnection,
81        parameters: &[PgTypeInfo],
82    ) -> Result<(), Error> {
83        let PgArgumentBuffer {
84            ref patches,
85            ref type_holes,
86            ref mut buffer,
87            ..
88        } = self.buffer;
89
90        for (offset, ty, callback) in patches {
91            let buf = &mut buffer[*offset..];
92            let ty = &parameters[*ty];
93
94            callback(buf, ty);
95        }
96
97        for (offset, name) in type_holes {
98            let oid = conn.fetch_type_id_by_name(name).await?;
99            buffer[*offset..(*offset + 4)].copy_from_slice(&oid.0.to_be_bytes());
100        }
101
102        Ok(())
103    }
104}
105
106impl<'q> Arguments<'q> for PgArguments {
107    type Database = Postgres;
108
109    fn reserve(&mut self, additional: usize, size: usize) {
110        self.types.reserve(additional);
111        self.buffer.reserve(size);
112    }
113
114    fn add<T>(&mut self, value: T)
115    where
116        T: Encode<'q, Self::Database> + Type<Self::Database>,
117    {
118        self.add(value)
119    }
120
121    fn format_placeholder<W: Write>(&self, writer: &mut W) -> fmt::Result {
122        write!(writer, "${}", self.buffer.count)
123    }
124}
125
126impl PgArgumentBuffer {
127    pub(crate) fn encode<'q, T>(&mut self, value: T)
128    where
129        T: Encode<'q, Postgres>,
130    {
131        // reserve space to write the prefixed length of the value
132        let offset = self.len();
133        self.extend(&[0; 4]);
134
135        // encode the value into our buffer
136        let len = if let IsNull::No = value.encode(self) {
137            i32::try_from(self.len() - offset - 4).expect("bind parameter too large")
138        } else {
139            // Write a -1 to indicate NULL
140            // NOTE: It is illegal for [encode] to write any data
141            debug_assert_eq!(self.len(), offset + 4);
142            -1_i32
143        };
144
145        // write the len to the beginning of the value
146        self[offset..(offset + 4)].copy_from_slice(&len.to_be_bytes());
147    }
148
149    // Adds a callback to be invoked later when we know the parameter type
150    #[allow(dead_code)]
151    pub(crate) fn patch<F>(&mut self, callback: F)
152    where
153        F: Fn(&mut [u8], &PgTypeInfo) + 'static + Send + Sync,
154    {
155        let offset = self.len();
156        let index = self.count;
157
158        self.patches.push((offset, index, Box::new(callback)));
159    }
160
161    // Extends the inner buffer by enough space to have an OID
162    // Remembers where the OID goes and type name for the OID
163    pub(crate) fn patch_type_by_name(&mut self, type_name: &UStr) {
164        let offset = self.len();
165
166        self.extend_from_slice(&0_u32.to_be_bytes());
167        self.type_holes.push((offset, type_name.clone()));
168    }
169}
170
171impl Deref for PgArgumentBuffer {
172    type Target = Vec<u8>;
173
174    #[inline]
175    fn deref(&self) -> &Self::Target {
176        &self.buffer
177    }
178}
179
180impl DerefMut for PgArgumentBuffer {
181    #[inline]
182    fn deref_mut(&mut self) -> &mut Self::Target {
183        &mut self.buffer
184    }
185}