qail_pg/protocol/wire/types.rs
1//! PostgreSQL wire protocol types — message enums, structs, and error types.
2//!
3//! Reference: <https://www.postgresql.org/docs/current/protocol-message-formats.html>
4
5/// Frontend (client → server) message types
6#[derive(Debug, Clone)]
7pub enum FrontendMessage {
8 /// Startup message (sent first, no type byte)
9 Startup {
10 /// Database role / user name.
11 user: String,
12 /// Target database name.
13 database: String,
14 /// Additional startup parameters (e.g. `replication=database`).
15 startup_params: Vec<(String, String)>,
16 },
17 /// Password response (MD5 or cleartext).
18 PasswordMessage(String),
19 /// Simple query (SQL text).
20 Query(String),
21 /// Parse (prepared statement)
22 Parse {
23 /// Prepared statement name (empty string = unnamed).
24 name: String,
25 /// SQL query text with `$1`-style parameter placeholders.
26 query: String,
27 /// OIDs of the parameter types (empty = server infers).
28 param_types: Vec<u32>,
29 },
30 /// Bind parameters to prepared statement
31 Bind {
32 /// Destination portal name (empty = unnamed).
33 portal: String,
34 /// Source prepared statement name.
35 statement: String,
36 /// Parameter values (`None` = SQL NULL).
37 params: Vec<Option<Vec<u8>>>,
38 },
39 /// Execute portal
40 Execute {
41 /// Portal name to execute.
42 portal: String,
43 /// Maximum rows to return (0 = no limit).
44 max_rows: i32,
45 },
46 /// Sync — marks the end of an extended-query pipeline.
47 Sync,
48 /// Terminate — closes the connection.
49 Terminate,
50 /// SASL initial response (first message in SCRAM)
51 SASLInitialResponse {
52 /// SASL mechanism name (e.g. `SCRAM-SHA-256`).
53 mechanism: String,
54 /// Client-first message bytes.
55 data: Vec<u8>,
56 },
57 /// SASL response (subsequent messages in SCRAM)
58 SASLResponse(Vec<u8>),
59 /// GSS/SSPI response token.
60 GSSResponse(Vec<u8>),
61 /// CopyFail — abort a COPY IN with an error message
62 CopyFail(String),
63 /// Close — explicitly release a prepared statement or portal
64 Close {
65 /// `true` for portal, `false` for prepared statement.
66 is_portal: bool,
67 /// Name of the portal or statement to close.
68 name: String,
69 },
70}
71
72/// Backend (server → client) message types
73#[derive(Debug, Clone)]
74pub enum BackendMessage {
75 /// Authentication request
76 /// Authentication succeeded.
77 AuthenticationOk,
78 /// Server requests cleartext password.
79 AuthenticationCleartextPassword,
80 /// Server requests MD5-hashed password; salt provided.
81 AuthenticationMD5Password([u8; 4]),
82 /// Server requests Kerberos V5 authentication.
83 AuthenticationKerberosV5,
84 /// Server requests GSSAPI authentication.
85 AuthenticationGSS,
86 /// Server requests SCM credential auth (Unix-domain peer credential exchange).
87 ///
88 /// PostgreSQL auth code `6`. This is typically used with local Unix sockets
89 /// and requires ancillary credential passing at the socket layer.
90 AuthenticationSCMCredential,
91 /// Server sends GSSAPI/SSPI continuation token.
92 AuthenticationGSSContinue(Vec<u8>),
93 /// Server requests SSPI authentication.
94 AuthenticationSSPI,
95 /// Server initiates SASL handshake with supported mechanisms.
96 AuthenticationSASL(Vec<String>),
97 /// SASL challenge from server.
98 AuthenticationSASLContinue(Vec<u8>),
99 /// SASL authentication complete; final server data.
100 AuthenticationSASLFinal(Vec<u8>),
101 /// Parameter status (server config)
102 ParameterStatus {
103 /// Parameter name (e.g. `server_version`, `TimeZone`).
104 name: String,
105 /// Current parameter value.
106 value: String,
107 },
108 /// Backend key data (for cancel)
109 BackendKeyData {
110 /// Backend process ID (used for cancel requests).
111 process_id: i32,
112 /// Cancel secret key.
113 secret_key: i32,
114 },
115 /// Server is ready; transaction state indicated.
116 ReadyForQuery(TransactionStatus),
117 /// Column metadata for the upcoming data rows.
118 RowDescription(Vec<FieldDescription>),
119 /// One data row; each element is `None` for SQL NULL or the raw bytes.
120 DataRow(Vec<Option<Vec<u8>>>),
121 /// Command completed with a tag like `SELECT 5` or `INSERT 0 1`.
122 CommandComplete(String),
123 /// Error response with structured fields (severity, code, message, etc.).
124 ErrorResponse(ErrorFields),
125 /// Parse step succeeded.
126 ParseComplete,
127 /// Bind step succeeded.
128 BindComplete,
129 /// Describe returned no row description (e.g. for DML statements).
130 NoData,
131 /// Execute reached row limit (`max_rows`) and suspended the portal.
132 PortalSuspended,
133 /// Copy in response (server ready to receive COPY data)
134 CopyInResponse {
135 /// Overall format: 0 = text, 1 = binary.
136 format: u8,
137 /// Per-column format codes.
138 column_formats: Vec<u8>,
139 },
140 /// Copy out response (server will send COPY data)
141 CopyOutResponse {
142 /// Overall format: 0 = text, 1 = binary.
143 format: u8,
144 /// Per-column format codes.
145 column_formats: Vec<u8>,
146 },
147 /// Copy both response (used by streaming replication).
148 CopyBothResponse {
149 /// Overall format: 0 = text, 1 = binary.
150 format: u8,
151 /// Per-column format codes.
152 column_formats: Vec<u8>,
153 },
154 /// Raw COPY data chunk from the server.
155 CopyData(Vec<u8>),
156 /// COPY transfer complete.
157 CopyDone,
158 /// Notification response (async notification from LISTEN/NOTIFY)
159 NotificationResponse {
160 /// Backend process ID that sent the notification.
161 process_id: i32,
162 /// Channel name.
163 channel: String,
164 /// Notification payload string.
165 payload: String,
166 },
167 /// Empty query string was submitted.
168 EmptyQueryResponse,
169 /// Notice response (warning/info messages, not errors)
170 NoticeResponse(ErrorFields),
171 /// Parameter description (OIDs of parameters in a prepared statement)
172 /// Sent by server in response to Describe(Statement)
173 ParameterDescription(Vec<u32>),
174 /// Close complete (server confirmation that a prepared statement/portal was released)
175 CloseComplete,
176}
177
178/// Transaction status
179#[derive(Debug, Clone, Copy)]
180pub enum TransactionStatus {
181 /// Not inside a transaction block (`I`).
182 Idle,
183 /// Inside a transaction block (`T`).
184 InBlock,
185 /// Inside a failed transaction block (`E`).
186 Failed,
187}
188
189/// Field description in RowDescription
190#[derive(Debug, Clone)]
191pub struct FieldDescription {
192 /// Column name (or alias).
193 pub name: String,
194 /// OID of the source table (0 if not a table column).
195 pub table_oid: u32,
196 /// Column attribute number within the table (0 if not a table column).
197 pub column_attr: i16,
198 /// OID of the column's data type.
199 pub type_oid: u32,
200 /// Data type size in bytes (negative = variable-length).
201 pub type_size: i16,
202 /// Type-specific modifier (e.g. precision for `numeric`).
203 pub type_modifier: i32,
204 /// Format code: 0 = text, 1 = binary.
205 pub format: i16,
206}
207
208/// Error fields from ErrorResponse
209#[derive(Debug, Clone, Default)]
210pub struct ErrorFields {
211 /// Severity level (e.g. `ERROR`, `FATAL`, `WARNING`).
212 pub severity: String,
213 /// SQLSTATE error code (e.g. `23505` for unique violation).
214 pub code: String,
215 /// Human-readable error message.
216 pub message: String,
217 /// Optional detailed error description.
218 pub detail: Option<String>,
219 /// Optional hint for resolving the error.
220 pub hint: Option<String>,
221}
222
223#[derive(Debug, Clone, PartialEq, Eq)]
224pub enum FrontendEncodeError {
225 InteriorNul(&'static str),
226 MessageTooLarge(usize),
227 TooManyParams(usize),
228 InvalidMaxRows(i32),
229 InvalidStartupParam(String),
230}
231
232impl std::fmt::Display for FrontendEncodeError {
233 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234 match self {
235 Self::InteriorNul(field) => write!(f, "field `{}` contains interior NUL byte", field),
236 Self::MessageTooLarge(len) => write!(f, "message too large for wire length: {}", len),
237 Self::TooManyParams(n) => write!(f, "too many params for i16 wire count: {}", n),
238 Self::InvalidMaxRows(v) => write!(f, "invalid Execute max_rows (must be >= 0): {}", v),
239 Self::InvalidStartupParam(msg) => write!(f, "invalid startup parameter: {}", msg),
240 }
241 }
242}
243
244impl std::error::Error for FrontendEncodeError {}