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