Skip to main content

fraiseql_wire/protocol/
message.rs

1//! Protocol message types
2
3use bytes::Bytes;
4
5/// Frontend message (client → server)
6#[derive(Debug, Clone)]
7#[non_exhaustive]
8pub enum FrontendMessage {
9    /// Startup message
10    Startup {
11        /// Protocol version
12        version: i32,
13        /// Connection parameters
14        params: Vec<(String, String)>,
15    },
16
17    /// Password message
18    Password(String),
19
20    /// Query message
21    Query(String),
22
23    /// Terminate message
24    Terminate,
25
26    /// SASL initial response message
27    SaslInitialResponse {
28        /// SASL mechanism name (e.g., "SCRAM-SHA-256")
29        mechanism: String,
30        /// SASL client first message data
31        data: Vec<u8>,
32    },
33
34    /// SASL response message
35    SaslResponse {
36        /// SASL client final message data
37        data: Vec<u8>,
38    },
39}
40
41/// Backend message (server → client)
42#[derive(Debug, Clone)]
43#[non_exhaustive]
44pub enum BackendMessage {
45    /// Authentication request
46    Authentication(AuthenticationMessage),
47
48    /// Backend key data (for cancellation)
49    BackendKeyData {
50        /// Process ID
51        process_id: i32,
52        /// Secret key
53        secret_key: i32,
54    },
55
56    /// Command complete
57    CommandComplete(String),
58
59    /// Data row
60    DataRow(Vec<Option<Bytes>>),
61
62    /// Error response
63    ErrorResponse(ErrorFields),
64
65    /// Notice response
66    NoticeResponse(ErrorFields),
67
68    /// Parameter status
69    ParameterStatus {
70        /// Parameter name
71        name: String,
72        /// Parameter value
73        value: String,
74    },
75
76    /// Ready for query
77    ReadyForQuery {
78        /// Transaction status
79        status: u8,
80    },
81
82    /// Row description
83    RowDescription(Vec<FieldDescription>),
84
85    /// Empty query response — the server's reply when the query string was empty.
86    ///
87    /// PostgreSQL sends this in place of `CommandComplete` for an empty query,
88    /// followed by `ReadyForQuery`. Decoding it explicitly lets a query loop
89    /// complete normally instead of hanging on an unrecognized tag (audit H42).
90    EmptyQueryResponse,
91
92    /// Asynchronous notification delivered via `LISTEN`/`NOTIFY`.
93    ///
94    /// These arrive out-of-band between other messages once the session has
95    /// issued `LISTEN`. Decoding them explicitly prevents a `NOTIFY` from
96    /// wedging the connection on an unrecognized tag (audit H42).
97    NotificationResponse {
98        /// Process ID of the backend that raised the notification.
99        process_id: i32,
100        /// Channel the notification was sent on.
101        channel: String,
102        /// Payload string (empty when `NOTIFY` carried no payload).
103        payload: String,
104    },
105}
106
107/// Authentication message types
108#[derive(Debug, Clone)]
109#[non_exhaustive]
110pub enum AuthenticationMessage {
111    /// Authentication OK
112    Ok,
113
114    /// Cleartext password required
115    CleartextPassword,
116
117    /// MD5 password required
118    Md5Password {
119        /// Salt for MD5 hash
120        salt: [u8; 4],
121    },
122
123    /// SASL authentication mechanisms available (Postgres 10+)
124    Sasl {
125        /// List of SASL mechanism names (e.g., ["SCRAM-SHA-256"])
126        mechanisms: Vec<String>,
127    },
128
129    /// SASL continuation message (server challenge)
130    SaslContinue {
131        /// SASL server first/continue message data
132        data: Vec<u8>,
133    },
134
135    /// SASL final message (server verification)
136    SaslFinal {
137        /// SASL server final message data
138        data: Vec<u8>,
139    },
140}
141
142/// Field description (column metadata)
143#[derive(Debug, Clone)]
144pub struct FieldDescription {
145    /// Column name
146    pub name: String,
147    /// Table OID (0 if not a table column)
148    pub table_oid: i32,
149    /// Column attribute number (0 if not a table column)
150    pub column_attr: i16,
151    /// Data type OID
152    pub type_oid: u32,
153    /// Data type size
154    pub type_size: i16,
155    /// Type modifier
156    pub type_modifier: i32,
157    /// Format code (0 = text, 1 = binary)
158    pub format_code: i16,
159}
160
161/// Error/notice fields
162#[derive(Debug, Clone, Default)]
163pub struct ErrorFields {
164    /// Severity (ERROR, WARNING, etc.)
165    pub severity: Option<String>,
166    /// SQLSTATE code
167    pub code: Option<String>,
168    /// Human-readable message
169    pub message: Option<String>,
170    /// Additional detail
171    pub detail: Option<String>,
172    /// Hint
173    pub hint: Option<String>,
174    /// Position in query string
175    pub position: Option<String>,
176}
177
178impl std::fmt::Display for ErrorFields {
179    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180        if let Some(ref msg) = self.message {
181            write!(f, "{}", msg)?;
182        }
183        if let Some(ref code) = self.code {
184            write!(f, " ({})", code)?;
185        }
186        Ok(())
187    }
188}