Skip to main content

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 {}