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 sends GSSAPI/SSPI continuation token.
87    AuthenticationGSSContinue(Vec<u8>),
88    /// Server requests SSPI authentication.
89    AuthenticationSSPI,
90    /// Server initiates SASL handshake with supported mechanisms.
91    AuthenticationSASL(Vec<String>),
92    /// SASL challenge from server.
93    AuthenticationSASLContinue(Vec<u8>),
94    /// SASL authentication complete; final server data.
95    AuthenticationSASLFinal(Vec<u8>),
96    /// Parameter status (server config)
97    ParameterStatus {
98        /// Parameter name (e.g. `server_version`, `TimeZone`).
99        name: String,
100        /// Current parameter value.
101        value: String,
102    },
103    /// Backend key data (for cancel)
104    BackendKeyData {
105        /// Backend process ID (used for cancel requests).
106        process_id: i32,
107        /// Cancel secret key.
108        secret_key: i32,
109    },
110    /// Server is ready; transaction state indicated.
111    ReadyForQuery(TransactionStatus),
112    /// Column metadata for the upcoming data rows.
113    RowDescription(Vec<FieldDescription>),
114    /// One data row; each element is `None` for SQL NULL or the raw bytes.
115    DataRow(Vec<Option<Vec<u8>>>),
116    /// Command completed with a tag like `SELECT 5` or `INSERT 0 1`.
117    CommandComplete(String),
118    /// Error response with structured fields (severity, code, message, etc.).
119    ErrorResponse(ErrorFields),
120    /// Parse step succeeded.
121    ParseComplete,
122    /// Bind step succeeded.
123    BindComplete,
124    /// Describe returned no row description (e.g. for DML statements).
125    NoData,
126    /// Execute reached row limit (`max_rows`) and suspended the portal.
127    PortalSuspended,
128    /// Copy in response (server ready to receive COPY data)
129    CopyInResponse {
130        /// Overall format: 0 = text, 1 = binary.
131        format: u8,
132        /// Per-column format codes.
133        column_formats: Vec<u8>,
134    },
135    /// Copy out response (server will send COPY data)
136    CopyOutResponse {
137        /// Overall format: 0 = text, 1 = binary.
138        format: u8,
139        /// Per-column format codes.
140        column_formats: Vec<u8>,
141    },
142    /// Copy both response (used by streaming replication).
143    CopyBothResponse {
144        /// Overall format: 0 = text, 1 = binary.
145        format: u8,
146        /// Per-column format codes.
147        column_formats: Vec<u8>,
148    },
149    /// Raw COPY data chunk from the server.
150    CopyData(Vec<u8>),
151    /// COPY transfer complete.
152    CopyDone,
153    /// Notification response (async notification from LISTEN/NOTIFY)
154    NotificationResponse {
155        /// Backend process ID that sent the notification.
156        process_id: i32,
157        /// Channel name.
158        channel: String,
159        /// Notification payload string.
160        payload: String,
161    },
162    /// Empty query string was submitted.
163    EmptyQueryResponse,
164    /// Notice response (warning/info messages, not errors)
165    NoticeResponse(ErrorFields),
166    /// Parameter description (OIDs of parameters in a prepared statement)
167    /// Sent by server in response to Describe(Statement)
168    ParameterDescription(Vec<u32>),
169    /// Close complete (server confirmation that a prepared statement/portal was released)
170    CloseComplete,
171}
172
173/// Transaction status
174#[derive(Debug, Clone, Copy)]
175pub enum TransactionStatus {
176    /// Not inside a transaction block (`I`).
177    Idle,
178    /// Inside a transaction block (`T`).
179    InBlock,
180    /// Inside a failed transaction block (`E`).
181    Failed,
182}
183
184/// Field description in RowDescription
185#[derive(Debug, Clone)]
186pub struct FieldDescription {
187    /// Column name (or alias).
188    pub name: String,
189    /// OID of the source table (0 if not a table column).
190    pub table_oid: u32,
191    /// Column attribute number within the table (0 if not a table column).
192    pub column_attr: i16,
193    /// OID of the column's data type.
194    pub type_oid: u32,
195    /// Data type size in bytes (negative = variable-length).
196    pub type_size: i16,
197    /// Type-specific modifier (e.g. precision for `numeric`).
198    pub type_modifier: i32,
199    /// Format code: 0 = text, 1 = binary.
200    pub format: i16,
201}
202
203/// Error fields from ErrorResponse
204#[derive(Debug, Clone, Default)]
205pub struct ErrorFields {
206    /// Severity level (e.g. `ERROR`, `FATAL`, `WARNING`).
207    pub severity: String,
208    /// SQLSTATE error code (e.g. `23505` for unique violation).
209    pub code: String,
210    /// Human-readable error message.
211    pub message: String,
212    /// Optional detailed error description.
213    pub detail: Option<String>,
214    /// Optional hint for resolving the error.
215    pub hint: Option<String>,
216}
217
218#[derive(Debug, Clone, PartialEq, Eq)]
219pub enum FrontendEncodeError {
220    InteriorNul(&'static str),
221    MessageTooLarge(usize),
222    TooManyParams(usize),
223    InvalidMaxRows(i32),
224    InvalidStartupParam(String),
225}
226
227impl std::fmt::Display for FrontendEncodeError {
228    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
229        match self {
230            Self::InteriorNul(field) => write!(f, "field `{}` contains interior NUL byte", field),
231            Self::MessageTooLarge(len) => write!(f, "message too large for wire length: {}", len),
232            Self::TooManyParams(n) => write!(f, "too many params for i16 wire count: {}", n),
233            Self::InvalidMaxRows(v) => write!(f, "invalid Execute max_rows (must be >= 0): {}", v),
234            Self::InvalidStartupParam(msg) => write!(f, "invalid startup parameter: {}", msg),
235        }
236    }
237}
238
239impl std::error::Error for FrontendEncodeError {}