mz_pgwire_common/message.rs
1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10use std::collections::BTreeMap;
11
12use tokio_postgres::error::SqlState;
13
14use crate::{Format, Severity};
15
16// Pgwire protocol versions are represented as 32-bit integers, where the
17// high 16 bits represent the major version and the low 16 bits represent the
18// minor version.
19//
20// There have only been three released protocol versions, v1.0, v2.0, and v3.0.
21// The protocol changes very infrequently: the most recent protocol version,
22// v3.0, was released with Postgres v7.4 in 2003.
23//
24// Somewhat unfortunately, the protocol overloads the version field to indicate
25// special types of connections, namely, SSL connections and cancellation
26// connections. These pseudo-versions were constructed to avoid ever matching
27// a true protocol version.
28
29pub const VERSION_1: i32 = 0x10000;
30pub const VERSION_2: i32 = 0x20000;
31pub const VERSION_3: i32 = 0x30000;
32pub const VERSION_CANCEL: i32 = (1234 << 16) + 5678;
33pub const VERSION_SSL: i32 = (1234 << 16) + 5679;
34pub const VERSION_GSSENC: i32 = (1234 << 16) + 5680;
35
36pub const VERSIONS: &[i32] = &[
37 VERSION_1,
38 VERSION_2,
39 VERSION_3,
40 VERSION_CANCEL,
41 VERSION_SSL,
42 VERSION_GSSENC,
43];
44
45/// Like [`FrontendMessage`], but only the messages that can occur during
46/// startup protocol negotiation.
47#[derive(Debug)]
48pub enum FrontendStartupMessage {
49 /// Begin a connection.
50 Startup {
51 version: i32,
52 params: BTreeMap<String, String>,
53 },
54
55 /// Request SSL encryption for the connection.
56 SslRequest,
57
58 /// Request GSSAPI encryption for the connection.
59 GssEncRequest,
60
61 /// Cancel a query that is running on another connection.
62 CancelRequest {
63 /// The target connection ID.
64 conn_id: u32,
65 /// The secret key for the target connection.
66 secret_key: u32,
67 },
68}
69
70/// A decoded frontend pgwire [message], representing instructions for the
71/// backend.
72///
73/// [message]: https://www.postgresql.org/docs/11/protocol-message-formats.html
74#[derive(Debug)]
75pub enum FrontendMessage {
76 /// Execute the specified SQL.
77 ///
78 /// This is issued as part of the simple query flow.
79 Query {
80 /// The SQL to execute.
81 sql: String,
82 },
83
84 /// Parse the specified SQL into a prepared statement.
85 ///
86 /// This starts the extended query flow.
87 Parse {
88 /// The name of the prepared statement to create. An empty string
89 /// specifies the unnamed prepared statement.
90 name: String,
91 /// The SQL to parse.
92 sql: String,
93 /// The OID of each parameter data type for which the client wants to
94 /// prespecify types. A zero OID is equivalent to leaving the type
95 /// unspecified.
96 ///
97 /// The number of specified parameter data types can be less than the
98 /// number of parameters specified in the query.
99 param_types: Vec<u32>,
100 },
101
102 /// Describe an existing prepared statement.
103 ///
104 /// This command is part of the extended query flow.
105 DescribeStatement {
106 /// The name of the prepared statement to describe.
107 name: String,
108 },
109
110 /// Describe an existing portal.
111 ///
112 /// This command is part of the extended query flow.
113 DescribePortal {
114 /// The name of the portal to describe.
115 name: String,
116 },
117
118 /// Bind an existing prepared statement to a portal.
119 ///
120 /// This command is part of the extended query flow.
121 Bind {
122 /// The destination portal. An empty string selects the unnamed
123 /// portal. The portal can later be executed with the `Execute` command.
124 portal_name: String,
125 /// The source prepared statement. An empty string selects the unnamed
126 /// prepared statement.
127 statement_name: String,
128 /// The formats used to encode the parameters in `raw_parameters`.
129 param_formats: Vec<Format>,
130 /// The value of each parameter, encoded using the formats described
131 /// by `parameter_formats`.
132 raw_params: Vec<Option<Vec<u8>>>,
133 /// The desired formats for the columns in the result set.
134 result_formats: Vec<Format>,
135 },
136
137 /// Execute a bound portal.
138 ///
139 /// This command is part of the extended query flow.
140 Execute {
141 /// The name of the portal to execute.
142 portal_name: String,
143 /// The maximum number number of rows to return before suspending.
144 ///
145 /// 0 or negative means infinite.
146 max_rows: i32,
147 },
148
149 /// Flush any pending output.
150 ///
151 /// This command is part of the extended query flow.
152 Flush,
153
154 /// Finish an extended query.
155 ///
156 /// This command is part of the extended query flow.
157 Sync,
158
159 /// Close the named statement.
160 ///
161 /// This command is part of the extended query flow.
162 CloseStatement {
163 name: String,
164 },
165
166 /// Close the named portal.
167 ///
168 // This command is part of the extended query flow.
169 ClosePortal {
170 name: String,
171 },
172
173 /// Terminate a connection.
174 Terminate,
175
176 CopyData(Vec<u8>),
177
178 CopyDone,
179
180 CopyFail(String),
181
182 Password {
183 password: String,
184 },
185}
186
187impl FrontendMessage {
188 pub fn name(&self) -> &'static str {
189 match self {
190 FrontendMessage::Query { .. } => "query",
191 FrontendMessage::Parse { .. } => "parse",
192 FrontendMessage::DescribeStatement { .. } => "describe_statement",
193 FrontendMessage::DescribePortal { .. } => "describe_portal",
194 FrontendMessage::Bind { .. } => "bind",
195 FrontendMessage::Execute { .. } => "execute",
196 FrontendMessage::Flush => "flush",
197 FrontendMessage::Sync => "sync",
198 FrontendMessage::CloseStatement { .. } => "close_statement",
199 FrontendMessage::ClosePortal { .. } => "close_portal",
200 FrontendMessage::Terminate => "terminate",
201 FrontendMessage::CopyData(_) => "copy_data",
202 FrontendMessage::CopyDone => "copy_done",
203 FrontendMessage::CopyFail(_) => "copy_fail",
204 FrontendMessage::Password { .. } => "password",
205 }
206 }
207}
208
209#[derive(Debug)]
210pub struct ErrorResponse {
211 pub severity: Severity,
212 pub code: SqlState,
213 pub message: String,
214 pub detail: Option<String>,
215 pub hint: Option<String>,
216 pub position: Option<usize>,
217}
218
219impl ErrorResponse {
220 pub fn fatal<S>(code: SqlState, message: S) -> ErrorResponse
221 where
222 S: Into<String>,
223 {
224 ErrorResponse::new(Severity::Fatal, code, message)
225 }
226
227 pub fn error<S>(code: SqlState, message: S) -> ErrorResponse
228 where
229 S: Into<String>,
230 {
231 ErrorResponse::new(Severity::Error, code, message)
232 }
233
234 pub fn notice<S>(code: SqlState, message: S) -> ErrorResponse
235 where
236 S: Into<String>,
237 {
238 ErrorResponse::new(Severity::Notice, code, message)
239 }
240
241 fn new<S>(severity: Severity, code: SqlState, message: S) -> ErrorResponse
242 where
243 S: Into<String>,
244 {
245 ErrorResponse {
246 severity,
247 code,
248 message: message.into(),
249 detail: None,
250 hint: None,
251 position: None,
252 }
253 }
254
255 pub fn with_position(mut self, position: usize) -> ErrorResponse {
256 self.position = Some(position);
257 self
258 }
259}