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}