1pub type Oid = u32;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6#[repr(i16)]
7#[non_exhaustive]
8pub enum FormatCode {
9 Text = 0,
10 Binary = 1,
11}
12
13#[derive(Debug)]
15#[non_exhaustive]
16pub enum FrontendMsg<'a> {
17 Parse {
20 name: &'a [u8],
21 sql: &'a [u8],
22 param_oids: &'a [Oid],
23 },
24 Bind {
27 portal: &'a [u8],
28 statement: &'a [u8],
29 param_formats: &'a [FormatCode],
30 params: &'a [Option<&'a [u8]>],
31 result_formats: &'a [FormatCode],
32 },
33 Execute { portal: &'a [u8], max_rows: i32 },
35 Sync,
37 Query(&'a [u8]),
39 Describe {
41 kind: u8, name: &'a [u8],
43 },
44 Close {
46 kind: u8, name: &'a [u8],
48 },
49 Flush,
51 SASLInitialResponse { mechanism: &'a [u8], data: &'a [u8] },
53 SASLResponse(&'a [u8]),
55 CopyData(&'a [u8]),
57 CopyDone,
59 CopyFail(&'a [u8]),
61 Terminate,
63}
64
65#[derive(Debug)]
67#[non_exhaustive]
68pub enum BackendMsg {
69 AuthenticationOk,
70 AuthenticationCleartextPassword,
71 AuthenticationMd5Password {
72 salt: [u8; 4],
73 },
74 AuthenticationSASL {
75 mechanisms: Vec<String>,
76 },
77 AuthenticationSASLContinue {
78 data: Vec<u8>,
79 },
80 AuthenticationSASLFinal {
81 data: Vec<u8>,
82 },
83 ParameterStatus {
84 name: String,
85 value: String,
86 },
87 BackendKeyData {
88 pid: i32,
89 secret: i32,
90 },
91 ReadyForQuery {
92 status: u8,
93 },
94 ParseComplete,
95 BindComplete,
96 CloseComplete,
97 NoData,
98 CommandComplete {
99 tag: String,
100 },
101 DataRow(RawRow),
102 RowDescription {
103 fields: Vec<FieldDescription>,
104 },
105 ErrorResponse {
106 fields: PgError,
107 },
108 NoticeResponse {
109 fields: PgError,
110 },
111 EmptyQueryResponse,
112 ParameterDescription {
114 type_oids: Vec<Oid>,
115 },
116 NotificationResponse {
118 pid: i32,
119 channel: String,
120 payload: String,
121 },
122 PortalSuspended,
124 CopyInResponse {
126 format: u8, column_formats: Vec<i16>,
128 },
129 CopyOutResponse {
131 format: u8,
132 column_formats: Vec<i16>,
133 },
134 CopyData {
136 data: Vec<u8>,
137 },
138 CopyDone,
140}
141
142pub const CELL_INLINE_CAP: usize = 12;
148
149#[derive(Debug, Clone)]
150pub struct RawRow {
151 pub(crate) body: bytes::Bytes,
152 cells: Cells,
153}
154
155impl RawRow {
156 #[doc(hidden)]
160 pub fn body(&self) -> &bytes::Bytes {
161 &self.body
162 }
163}
164
165#[derive(Debug, Clone)]
166enum Cells {
167 Inline {
168 data: [(u32, i32); CELL_INLINE_CAP],
169 len: u8,
170 },
171 Heap(Box<[(u32, i32)]>),
172}
173
174impl RawRow {
175 pub fn empty() -> Self {
177 Self {
178 body: bytes::Bytes::new(),
179 cells: Cells::Inline {
180 data: [(0, 0); CELL_INLINE_CAP],
181 len: 0,
182 },
183 }
184 }
185
186 #[inline]
190 pub fn from_inline_unchecked(
191 body: bytes::Bytes,
192 data: [(u32, i32); CELL_INLINE_CAP],
193 len: u8,
194 ) -> Self {
195 debug_assert!(len as usize <= CELL_INLINE_CAP);
196 Self {
197 body,
198 cells: Cells::Inline { data, len },
199 }
200 }
201
202 pub fn from_entries(body: bytes::Bytes, entries: &[(u32, i32)]) -> Self {
206 let cells = if entries.len() <= CELL_INLINE_CAP {
207 let mut data = [(0u32, 0i32); CELL_INLINE_CAP];
208 data[..entries.len()].copy_from_slice(entries);
209 Cells::Inline {
210 data,
211 len: entries.len() as u8,
212 }
213 } else {
214 Cells::Heap(entries.into())
215 };
216 Self { body, cells }
217 }
218
219 pub fn from_full_body(body: bytes::Bytes) -> Self {
222 let len = body.len() as i32;
223 let mut data = [(0u32, 0i32); CELL_INLINE_CAP];
224 data[0] = (0, len);
225 Self {
226 body,
227 cells: Cells::Inline { data, len: 1 },
228 }
229 }
230
231 fn entries(&self) -> &[(u32, i32)] {
232 match &self.cells {
233 Cells::Inline { data, len } => &data[..*len as usize],
234 Cells::Heap(b) => b,
235 }
236 }
237
238 pub fn len(&self) -> usize {
239 self.entries().len()
240 }
241
242 pub fn is_empty(&self) -> bool {
243 self.entries().is_empty()
244 }
245
246 pub fn cell(&self, idx: usize) -> Option<&[u8]> {
249 let (off, len) = *self.entries().get(idx)?;
250 if len < 0 {
251 return None;
252 }
253 let start = off as usize;
254 let end = start + len as usize;
255 Some(&self.body[start..end])
256 }
257
258 pub fn try_cell(&self, idx: usize) -> Option<Option<&[u8]>> {
260 let (off, len) = *self.entries().get(idx)?;
261 if len < 0 {
262 return Some(None);
263 }
264 let start = off as usize;
265 let end = start + len as usize;
266 Some(Some(&self.body[start..end]))
267 }
268
269 pub fn iter(&self) -> impl Iterator<Item = Option<&[u8]>> + '_ {
271 let body = self.body.as_ref();
272 self.entries().iter().map(move |&(off, len)| {
273 if len < 0 {
274 None
275 } else {
276 let start = off as usize;
277 let end = start + len as usize;
278 Some(&body[start..end])
279 }
280 })
281 }
282}
283
284#[derive(Debug, Clone)]
285#[non_exhaustive]
286pub struct FieldDescription {
287 pub name: String,
288 pub table_oid: Oid,
289 pub column_id: i16,
290 pub type_oid: Oid,
291 pub type_size: i16,
292 pub type_modifier: i32,
293 pub format: FormatCode,
294}
295
296impl Default for FieldDescription {
297 fn default() -> Self {
298 Self {
299 name: String::new(),
300 table_oid: 0,
301 column_id: 0,
302 type_oid: 0,
303 type_size: -1,
304 type_modifier: -1,
305 format: FormatCode::Binary,
306 }
307 }
308}
309
310#[derive(Debug, Clone, Default)]
311#[non_exhaustive]
312pub struct PgError {
313 pub severity: String,
314 pub code: String,
315 pub message: String,
316 pub detail: Option<String>,
317 pub hint: Option<String>,
318 pub position: Option<String>,
319}