///|
/// Backend (server → client) message types for the PostgreSQL wire protocol.
///
/// `BackendMessage` is the tagged union dispatched on the message type byte.
// ---------------------------------------------------------------------------
// Supporting types
// ---------------------------------------------------------------------------
///|
/// Per-field metadata carried in a `RowDescription` message.
pub(all) struct FieldDescription {
name : String
table_oid : Int
attr_num : Int
type_oid : Int
typlen : Int
typmod : Int
format : Int // 0 = text, 1 = binary
}
///|
/// Return the human-readable type name for this column (e.g. `"int4"`, `"text"`).
/// Looks up `type_oid` in the built-in OID map; returns `"unknown"` for
/// unrecognised OIDs. No database round-trip.
pub fn FieldDescription::type_name(self : FieldDescription) -> String {
@pgtype.oid_to_name(self.type_oid)
}
///|
/// Describes a prepared statement: its parameter OIDs and result-column
/// layout (if the statement returns rows).
pub(all) struct StatementDescription {
/// Statement name (empty = unnamed).
name : String
/// The SQL text that was parsed.
sql : String
/// OID for each parameter placeholder. `[]` = no parameters.
param_oids : Array[Int]
/// Result-column descriptions. `[]` = no result set (e.g. INSERT).
fields : Array[FieldDescription]
}
///|
/// Parsed command-completion tag from `CommandComplete`.
///
/// PostgreSQL returns tags like `"SELECT 1"`, `"INSERT 0 1"`, `"DELETE 5"`.
pub struct CommandTag {
tag : String
}
///|
pub impl Show for CommandTag with fn output(self, logger) {
logger.write_string(self.tag)
}
///|
pub fn CommandTag::new(tag : String) -> CommandTag {
{ tag, }
}
///|
/// The raw tag string as sent by the server.
pub fn CommandTag::raw(self : CommandTag) -> String {
self.tag
}
///|
/// The SQL command word, e.g. `"SELECT"`, `"INSERT"`, `"DELETE"`.
pub fn CommandTag::command(self : CommandTag) -> String {
match self.tag.find(" ") {
Some(i) => self.tag[0:i].to_owned()
None => self.tag
}
}
///|
/// Number of rows affected, if the tag includes a row count.
///
/// For `SELECT 1` returns `Some(1)`, for `INSERT 0 3` returns `Some(3)`,
/// for `CREATE TABLE` returns `None`.
pub fn CommandTag::rows_affected(self : CommandTag) -> Int? {
// Tags end with a numeric row count when applicable, e.g. "SELECT 1".
// INSERT has format "INSERT oid rows" — last token is the row count.
let mut last = self.tag
for part in self.tag.split(" ") {
last = part.to_owned()
}
let n = @string.parse_int(last) catch { _ => return None }
Some(n)
}
// ---------------------------------------------------------------------------
// BackendMessage — tagged union of all backend messages
// ---------------------------------------------------------------------------
///|
/// Tagged union of all backend (server → client) messages.
///
/// Each variant wraps a concrete message struct that implements `Message`.
pub(all) enum BackendMessage {
AuthenticationOk(AuthenticationOk)
AuthenticationCleartextPassword(AuthenticationCleartextPassword)
AuthenticationMD5Password(AuthenticationMD5Password)
AuthenticationSASL(AuthenticationSASL)
AuthenticationSASLContinue(AuthenticationSASLContinue)
AuthenticationSASLFinal(AuthenticationSASLFinal)
BackendKeyData(BackendKeyData)
ReadyForQuery(ReadyForQuery)
ParameterStatus(ParameterStatus)
ErrorResponse(ErrorResponse)
NoticeResponse(NoticeResponse)
RowDescription(RowDescription)
DataRow(DataRow)
CommandComplete(CommandComplete)
EmptyQueryResponse(EmptyQueryResponse)
ParseComplete(ParseComplete)
BindComplete(BindComplete)
CloseComplete(CloseComplete)
ParameterDescription(ParameterDescription)
NoData(NoData)
PortalSuspended(PortalSuspended)
CopyInResponse(CopyInResponse)
CopyOutResponse(CopyOutResponse)
CopyBothResponse(CopyBothResponse)
CopyData(CopyData)
CopyDone(CopyDone)
NotificationResponse(NotificationResponse)
NegotiateProtocolVersion(NegotiateProtocolVersion)
// Catch-all for messages not yet implemented
Unknown(Byte, BytesView)
}
///|
pub fn BackendMessage::describe(self : BackendMessage) -> String {
match self {
AuthenticationOk(m) => m.describe()
AuthenticationCleartextPassword(m) => m.describe()
AuthenticationMD5Password(m) => m.describe()
AuthenticationSASL(m) => m.describe()
AuthenticationSASLContinue(m) => m.describe()
AuthenticationSASLFinal(m) => m.describe()
BackendKeyData(m) => m.describe()
ReadyForQuery(m) => m.describe()
ParameterStatus(m) => m.describe()
ErrorResponse(m) => m.describe()
NoticeResponse(m) => m.describe()
RowDescription(m) => m.describe()
DataRow(m) => m.describe()
CommandComplete(m) => m.describe()
EmptyQueryResponse(m) => m.describe()
ParseComplete(m) => m.describe()
BindComplete(m) => m.describe()
CloseComplete(m) => m.describe()
ParameterDescription(m) => m.describe()
NoData(m) => m.describe()
PortalSuspended(m) => m.describe()
CopyInResponse(m) => m.describe()
CopyOutResponse(m) => m.describe()
CopyBothResponse(m) => m.describe()
CopyData(m) => m.describe()
CopyDone(m) => m.describe()
NotificationResponse(m) => m.describe()
NegotiateProtocolVersion(m) => m.describe()
Unknown(b, _) => "Unknown(type=\{b.to_int()})"
}
}
// ---------------------------------------------------------------------------
// AuthenticationCleartextPassword ('R'/3)
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `AuthenticationCleartextPassword` (B).
pub(all) struct AuthenticationCleartextPassword {}
///|
pub impl Message for AuthenticationCleartextPassword with fn encode(_self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_AUTHENTICATION)
buf.append_int_be(8)
buf.append_int_be(3)
buf.to_bytes()
}
///|
pub impl Message for AuthenticationCleartextPassword with fn decode(_payload) -> AuthenticationCleartextPassword raise WireError {
AuthenticationCleartextPassword::{ }
}
///|
pub impl Message for AuthenticationCleartextPassword with fn describe(_self) -> String {
"AuthenticationCleartextPassword"
}
// ---------------------------------------------------------------------------
// AuthenticationMD5Password ('R'/5)
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `AuthenticationMD5Password` (B).
pub(all) struct AuthenticationMD5Password {
salt : Bytes
}
///|
pub impl Message for AuthenticationMD5Password with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_AUTHENTICATION)
buf.append_int_be(12)
buf.append_int_be(5)
buf.append_bytes(self.salt)
buf.to_bytes()
}
///|
pub impl Message for AuthenticationMD5Password with fn decode(payload) -> AuthenticationMD5Password raise WireError {
{ salt: payload[0:4].to_owned() }
}
///|
pub impl Message for AuthenticationMD5Password with fn describe(_self) -> String {
"AuthenticationMD5Password"
}
// ---------------------------------------------------------------------------
// AuthenticationOk ('R'/0)
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `AuthenticationOk` (B).
pub(all) struct AuthenticationOk {}
///|
pub impl Message for AuthenticationOk with fn encode(_self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_AUTHENTICATION)
buf.append_int_be(8)
buf.append_int_be(0)
buf.to_bytes()
}
///|
pub impl Message for AuthenticationOk with fn decode(_payload) -> AuthenticationOk raise WireError {
AuthenticationOk::{ }
}
///|
pub impl Message for AuthenticationOk with fn describe(_self) -> String {
"AuthenticationOk"
}
// ---------------------------------------------------------------------------
// AuthenticationSASL ('R'/10)
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `AuthenticationSASL` (B).
pub(all) struct AuthenticationSASL {
mechanisms : Array[String]
}
///|
pub impl Message for AuthenticationSASL with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_AUTHENTICATION)
buf.append_int_be(0)
buf.append_int_be(10)
for i = 0; i < self.mechanisms.length(); i = i + 1 {
buf.append_string_null(self.mechanisms[i])
}
buf.append_byte(b'\x00')
buf.set_int_be(1, buf.len() - 1)
buf.to_bytes()
}
///|
pub impl Message for AuthenticationSASL with fn decode(payload) -> AuthenticationSASL raise WireError {
let mechanisms = read_cstrings(payload[:])
{ mechanisms, }
}
///|
pub impl Message for AuthenticationSASL with fn describe(self) -> String {
"AuthenticationSASL(\{self.mechanisms.join(", ")})"
}
// ---------------------------------------------------------------------------
// AuthenticationSASLContinue ('R'/11)
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `AuthenticationSASLContinue` (B).
pub(all) struct AuthenticationSASLContinue {
data : Bytes
}
///|
pub impl Message for AuthenticationSASLContinue with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_AUTHENTICATION)
buf.append_int_be(0)
buf.append_int_be(11)
buf.append_bytes(self.data)
buf.set_int_be(1, buf.len() - 1)
buf.to_bytes()
}
///|
pub impl Message for AuthenticationSASLContinue with fn decode(payload) -> AuthenticationSASLContinue raise WireError {
{ data: payload.to_owned() }
}
///|
pub impl Message for AuthenticationSASLContinue with fn describe(self) -> String {
"AuthenticationSASLContinue(\{self.data.length()} bytes)"
}
// ---------------------------------------------------------------------------
// AuthenticationSASLFinal ('R'/12)
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `AuthenticationSASLFinal` (B).
pub(all) struct AuthenticationSASLFinal {
data : Bytes
}
///|
pub impl Message for AuthenticationSASLFinal with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_AUTHENTICATION)
buf.append_int_be(0)
buf.append_int_be(12)
buf.append_bytes(self.data)
buf.set_int_be(1, buf.len() - 1)
buf.to_bytes()
}
///|
pub impl Message for AuthenticationSASLFinal with fn decode(payload) -> AuthenticationSASLFinal raise WireError {
{ data: payload.to_owned() }
}
///|
pub impl Message for AuthenticationSASLFinal with fn describe(self) -> String {
"AuthenticationSASLFinal(\{self.data.length()} bytes)"
}
// ---------------------------------------------------------------------------
// BackendKeyData ('K')
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `BackendKeyData` (B).
pub(all) struct BackendKeyData {
pid : Int
secret_key : Int
}
///|
pub impl Message for BackendKeyData with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_BACKEND_KEY_DATA)
buf.append_int_be(12)
buf.append_int_be(self.pid)
buf.append_int_be(self.secret_key)
buf.to_bytes()
}
///|
pub impl Message for BackendKeyData with fn decode(payload) -> BackendKeyData raise WireError {
guard payload is [i32be(pid), .. payload] else {
raise WireError::InvalidMessage("invalid BackendKeyData")
}
guard payload is [i32be(secret_key), ..] else {
raise WireError::InvalidMessage("invalid BackendKeyData")
}
{ pid, secret_key }
}
///|
pub impl Message for BackendKeyData with fn describe(self) -> String {
"BackendKeyData(pid=\{self.pid})"
}
// ---------------------------------------------------------------------------
// CommandComplete ('C')
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `CommandComplete` (B).
///
/// The tag describes the completed command, e.g. `"SELECT 1"`, `"INSERT 0 1"`.
pub(all) struct CommandComplete {
tag : CommandTag
}
///|
pub impl Message for CommandComplete with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_COMMAND_COMPLETE)
buf.append_int_be(0) // placeholder
buf.append_string_null(self.tag.raw())
let total = buf.len() - 1
buf.set_int_be(1, total)
buf.to_bytes()
}
///|
pub impl Message for CommandComplete with fn decode(payload) -> CommandComplete raise WireError {
let (tag, _) = read_cstring(payload[:])
{ tag: CommandTag::new(tag) }
}
///|
pub impl Message for CommandComplete with fn describe(self) -> String {
"CommandComplete(\{self.tag})"
}
// ---------------------------------------------------------------------------
// DataRow ('D')
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `DataRow` (B).
///
/// Each column value is `Some(bytes)` for non-NULL or `None` for NULL.
/// Values are owned `Bytes` — safe to hold across message boundaries.
pub(all) struct DataRow {
values : Array[Bytes?]
}
///|
pub impl Message for DataRow with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_DATA_ROW)
buf.append_int_be(0) // placeholder
let n = self.values.length()
buf.append_byte(((n >> 8) & 0xFF).to_byte())
buf.append_byte((n & 0xFF).to_byte())
for i = 0; i < n; i = i + 1 {
match self.values[i] {
Some(v) => {
buf.append_int_be(v.length())
buf.append_bytes(v)
}
None => buf.append_int_be(-1)
}
}
let total = buf.len() - 1
buf.set_int_be(1, total)
buf.to_bytes()
}
///|
pub impl Message for DataRow with fn decode(payload) -> DataRow raise WireError {
guard payload is [i16be(ncols), .. rest] else {
raise WireError::InvalidMessage("DataRow: too short for field count")
}
let values : Array[Bytes?] = []
for rest0 = rest {
if values.length() >= ncols {
break
}
guard rest0 is [i32be(len), .. r1] else {
raise WireError::InvalidMessage("DataRow: truncated column length")
}
if len < 0 {
values.push(None)
continue r1
} else {
guard r1.length() >= len else {
raise WireError::InvalidMessage("DataRow: truncated column value")
}
values.push(Some(r1[0:len].to_owned()))
continue r1[len:]
}
}
{ values, }
}
///|
pub impl Message for DataRow with fn describe(self) -> String {
"DataRow(ncols=\{self.values.length()})"
}
// ---------------------------------------------------------------------------
// EmptyQueryResponse ('I')
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `EmptyQueryResponse` (B).
///
/// Sent when the query string is empty or whitespace-only.
pub(all) struct EmptyQueryResponse {}
///|
pub impl Message for EmptyQueryResponse with fn encode(_self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_EMPTY_QUERY)
buf.append_int_be(4)
buf.to_bytes()
}
///|
pub impl Message for EmptyQueryResponse with fn decode(_payload) -> EmptyQueryResponse raise WireError {
EmptyQueryResponse::{ }
}
///|
pub impl Message for EmptyQueryResponse with fn describe(_self) -> String {
"EmptyQueryResponse"
}
// ---------------------------------------------------------------------------
// ErrorResponse ('E')
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `ErrorResponse` (B).
pub(all) struct ErrorResponse {
fields : Array[ErrorField]
}
///|
pub impl Message for ErrorResponse with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_ERROR_RESPONSE)
buf.append_int_be(0)
for i = 0; i < self.fields.length(); i = i + 1 {
let field = self.fields[i]
buf.append_byte(field.field_type)
buf.append_string_null(field.value)
}
buf.append_byte(b'\x00')
buf.set_int_be(1, buf.len() - 1)
buf.to_bytes()
}
///|
pub impl Message for ErrorResponse with fn decode(payload) -> ErrorResponse raise WireError {
let fields : Array[ErrorField] = []
for rest = payload {
match rest {
[b'\x00', ..] => break
_ => {
let field_type = rest[0]
let (value, next) = read_cstring(rest[1:])
fields.push(ErrorField::{ field_type, value })
continue next
}
}
}
{ fields, }
}
///|
pub impl Message for ErrorResponse with fn describe(self) -> String {
"ErrorResponse(\{self.message().unwrap_or("???")})"
}
///|
pub fn ErrorResponse::severity(self : ErrorResponse) -> String? {
for i = 0; i < self.fields.length(); i = i + 1 {
if self.fields[i].field_type == b'S' {
return Some(self.fields[i].value)
}
}
None
}
///|
pub fn ErrorResponse::code(self : ErrorResponse) -> String? {
for i = 0; i < self.fields.length(); i = i + 1 {
if self.fields[i].field_type == b'C' {
return Some(self.fields[i].value)
}
}
None
}
///|
pub fn ErrorResponse::message(self : ErrorResponse) -> String? {
for i = 0; i < self.fields.length(); i = i + 1 {
if self.fields[i].field_type == b'M' {
return Some(self.fields[i].value)
}
}
None
}
// ---------------------------------------------------------------------------
// NoticeResponse ('N')
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `NoticeResponse` (B).
pub(all) struct NoticeResponse {
fields : Array[ErrorField]
}
///|
pub impl Message for NoticeResponse with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_NOTICE_RESPONSE)
buf.append_int_be(0)
for i = 0; i < self.fields.length(); i = i + 1 {
let field = self.fields[i]
buf.append_byte(field.field_type)
buf.append_string_null(field.value)
}
buf.append_byte(b'\x00')
buf.set_int_be(1, buf.len() - 1)
buf.to_bytes()
}
///|
pub impl Message for NoticeResponse with fn decode(payload) -> NoticeResponse raise WireError {
let fields : Array[ErrorField] = []
for rest = payload {
match rest {
[b'\x00', ..] => break
_ => {
let field_type = rest[0]
let (value, next) = read_cstring(rest[1:])
fields.push(ErrorField::{ field_type, value })
continue next
}
}
}
{ fields, }
}
///|
pub impl Message for NoticeResponse with fn describe(self) -> String {
"NoticeResponse(\{self.message().unwrap_or("???")})"
}
///|
pub fn NoticeResponse::message(self : NoticeResponse) -> String? {
for i = 0; i < self.fields.length(); i = i + 1 {
if self.fields[i].field_type == b'M' {
return Some(self.fields[i].value)
}
}
None
}
// ---------------------------------------------------------------------------
// ParameterStatus ('S')
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `ParameterStatus` (B).
pub(all) struct ParameterStatus {
name : String
value : String
}
///|
pub impl Message for ParameterStatus with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_PARAMETER_STATUS)
buf.append_int_be(0)
buf.append_string_null(self.name)
buf.append_string_null(self.value)
buf.set_int_be(1, buf.len() - 1)
buf.to_bytes()
}
///|
pub impl Message for ParameterStatus with fn decode(payload) -> ParameterStatus raise WireError {
let (name, rest) = read_cstring(payload[:])
let (value, _) = read_cstring(rest)
{ name, value }
}
///|
pub impl Message for ParameterStatus with fn describe(self) -> String {
"ParameterStatus(\{self.name} = \{self.value})"
}
// ---------------------------------------------------------------------------
// ReadyForQuery ('Z')
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `ReadyForQuery` (B).
pub(all) struct ReadyForQuery {
status : TransactionStatus
}
///|
pub impl Message for ReadyForQuery with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_READY_FOR_QUERY)
buf.append_int_be(5)
buf.append_byte(self.status.to_byte())
buf.to_bytes()
}
///|
pub impl Message for ReadyForQuery with fn decode(payload) -> ReadyForQuery raise WireError {
{ status: TransactionStatus::from_byte(payload[0]) }
}
///|
pub impl Message for ReadyForQuery with fn describe(self) -> String {
"ReadyForQuery(\{self.status})"
}
// ---------------------------------------------------------------------------
// RowDescription ('T')
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `RowDescription` (B).
pub(all) struct RowDescription {
columns : Array[FieldDescription]
}
///|
pub impl Message for RowDescription with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_ROW_DESCRIPTION)
buf.append_int_be(0) // placeholder
let n = self.columns.length()
buf.append_byte(((n >> 8) & 0xFF).to_byte())
buf.append_byte((n & 0xFF).to_byte())
for i = 0; i < n; i = i + 1 {
let col = self.columns[i]
buf.append_string_null(col.name)
buf.append_int_be(col.table_oid)
buf.append_int_be(col.attr_num)
buf.append_int_be(col.type_oid)
buf.append_int_be(col.typlen)
buf.append_int_be(col.typmod)
buf.append_int_be(col.format)
}
let total = buf.len() - 1
buf.set_int_be(1, total)
buf.to_bytes()
}
///|
pub impl Message for RowDescription with fn decode(payload) -> RowDescription raise WireError {
guard payload is [b1, b2, .. rest] else {
raise WireError::InvalidMessage("RowDescription: too short for field count")
}
let ncols = (b1.to_int() << 8) | b2.to_int()
let columns : Array[FieldDescription] = []
for rest0 = rest {
if columns.length() >= ncols {
break
}
let (name, r1) = read_cstring(rest0[:])
guard r1
is [
i32be(table_oid),
i16be(attr_num),
i32be(type_oid),
i16be(typlen),
i32be(typmod),
i16be(format),
.. r2,
] else {
raise WireError::InvalidMessage(
"RowDescription: truncated field '\{name}'",
)
}
columns.push(FieldDescription::{
name,
table_oid,
attr_num,
type_oid,
typlen,
typmod,
format,
})
continue r2
}
{ columns, }
}
///|
pub impl Message for RowDescription with fn describe(self) -> String {
"RowDescription(ncols=\{self.columns.length()})"
}
// ---------------------------------------------------------------------------
// ParseComplete ('1') — Extended Query
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `ParseComplete` (B).
pub(all) struct ParseComplete {}
///|
pub impl Message for ParseComplete with fn encode(_self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_PARSE_COMPLETE)
buf.append_int_be(4)
buf.to_bytes()
}
///|
pub impl Message for ParseComplete with fn decode(_payload) -> ParseComplete raise WireError {
ParseComplete::{ }
}
///|
pub impl Message for ParseComplete with fn describe(_self) -> String {
"ParseComplete"
}
// ---------------------------------------------------------------------------
// BindComplete ('2') — Extended Query
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `BindComplete` (B).
pub(all) struct BindComplete {}
///|
pub impl Message for BindComplete with fn encode(_self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_BIND_COMPLETE)
buf.append_int_be(4)
buf.to_bytes()
}
///|
pub impl Message for BindComplete with fn decode(_payload) -> BindComplete raise WireError {
BindComplete::{ }
}
///|
pub impl Message for BindComplete with fn describe(_self) -> String {
"BindComplete"
}
// ---------------------------------------------------------------------------
// CloseComplete ('3') — Extended Query
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `CloseComplete` (B).
pub(all) struct CloseComplete {}
///|
pub impl Message for CloseComplete with fn encode(_self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_CLOSE_COMPLETE)
buf.append_int_be(4)
buf.to_bytes()
}
///|
pub impl Message for CloseComplete with fn decode(_payload) -> CloseComplete raise WireError {
CloseComplete::{ }
}
///|
pub impl Message for CloseComplete with fn describe(_self) -> String {
"CloseComplete"
}
// ---------------------------------------------------------------------------
// ParameterDescription ('t') — Extended Query
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `ParameterDescription` (B).
///
/// Provides the OIDs of the parameters expected by a prepared statement.
pub(all) struct ParameterDescription {
param_types : Array[Int]
}
///|
pub impl Message for ParameterDescription with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_PARAMETER_DESCRIPTION)
buf.append_int_be(0) // placeholder
let n = self.param_types.length()
buf.append_byte(((n >> 8) & 0xFF).to_byte())
buf.append_byte((n & 0xFF).to_byte())
for i = 0; i < n; i = i + 1 {
buf.append_int_be(self.param_types[i])
}
let total = buf.len() - 1
buf.set_int_be(1, total)
buf.to_bytes()
}
///|
pub impl Message for ParameterDescription with fn decode(payload) -> ParameterDescription raise WireError {
guard payload is [i16be(n), .. rest] else {
raise WireError::InvalidMessage("ParameterDescription: truncated")
}
let param_types : Array[Int] = []
for rest = rest {
if param_types.length() >= n {
break
}
guard rest is [i32be(oid), .. r1] else {
raise WireError::InvalidMessage(
"ParameterDescription: truncated param OID",
)
}
param_types.push(oid)
continue r1
}
{ param_types, }
}
///|
pub impl Message for ParameterDescription with fn describe(self) -> String {
"ParameterDescription(nparams=\{self.param_types.length()})"
}
// ---------------------------------------------------------------------------
// NoData ('n') — Extended Query
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `NoData` (B) — returned when Describe finds no result columns.
pub(all) struct NoData {}
///|
pub impl Message for NoData with fn encode(_self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_NO_DATA)
buf.append_int_be(4)
buf.to_bytes()
}
///|
pub impl Message for NoData with fn decode(_payload) -> NoData raise WireError {
NoData::{ }
}
///|
pub impl Message for NoData with fn describe(_self) -> String {
"NoData"
}
// ---------------------------------------------------------------------------
// PortalSuspended ('s') — Extended Query
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `PortalSuspended` (B) — Execute reached max_rows before
/// the portal was fully consumed.
pub(all) struct PortalSuspended {}
///|
pub impl Message for PortalSuspended with fn encode(_self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_PORTAL_SUSPENDED)
buf.append_int_be(4)
buf.to_bytes()
}
///|
pub impl Message for PortalSuspended with fn decode(_payload) -> PortalSuspended raise WireError {
PortalSuspended::{ }
}
///|
pub impl Message for PortalSuspended with fn describe(_self) -> String {
"PortalSuspended"
}
// ---------------------------------------------------------------------------
// COPY protocol — backend messages
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `CopyInResponse` (B) — server is ready to receive COPY data.
pub(all) struct CopyInResponse {
overall_format : Int
column_formats : Array[Int]
}
///|
pub impl Message for CopyInResponse with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_COPY_IN_RESPONSE)
let body = BytesMut::new()
body.append_byte(self.overall_format.to_byte())
body.append_int_be(self.column_formats.length())
for f in self.column_formats {
body.append_int_be(f)
}
buf.append_int_be(4 + body.to_bytes().length())
buf.append_bytes(body.to_bytes())
buf.to_bytes()
}
///|
/// Decode `CopyInResponse`. Format: Int8 overall, Int16 ncols, Int16[ncols].
pub impl Message for CopyInResponse with fn decode(payload : BytesView) -> CopyInResponse raise WireError {
guard payload is [overall_fmt, i16be(ncols), .. rest] else {
raise WireError::InvalidMessage("invalid CopyInResponse")
}
let overall_format = overall_fmt.to_int()
let col_formats : Array[Int] = []
let mut r = rest
for _ in 0.. String {
"CopyInResponse"
}
///|
/// PostgreSQL `CopyOutResponse` (B) — server is sending COPY data.
pub(all) struct CopyOutResponse {
overall_format : Int
column_formats : Array[Int]
}
///|
pub impl Message for CopyOutResponse with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_COPY_OUT_RESPONSE)
let body = BytesMut::new()
body.append_byte(self.overall_format.to_byte())
body.append_int_be(self.column_formats.length())
for f in self.column_formats {
body.append_int_be(f)
}
buf.append_int_be(4 + body.to_bytes().length())
buf.append_bytes(body.to_bytes())
buf.to_bytes()
}
///|
pub impl Message for CopyOutResponse with fn decode(payload : BytesView) -> CopyOutResponse raise WireError {
guard payload is [overall_fmt, i16be(ncols), .. rest] else {
raise WireError::InvalidMessage("invalid CopyOutResponse")
}
let overall_format = overall_fmt.to_int()
let col_formats : Array[Int] = []
let mut r = rest
for _ in 0.. String {
"CopyOutResponse"
}
///|
/// PostgreSQL `CopyBothResponse` (B) — server is ready for both COPY directions.
pub(all) struct CopyBothResponse {
overall_format : Int
column_formats : Array[Int]
}
///|
pub impl Message for CopyBothResponse with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_COPY_BOTH_RESPONSE)
let body = BytesMut::new()
body.append_byte(self.overall_format.to_byte())
body.append_int_be(self.column_formats.length())
for f in self.column_formats {
body.append_int_be(f)
}
buf.append_int_be(4 + body.to_bytes().length())
buf.append_bytes(body.to_bytes())
buf.to_bytes()
}
///|
pub impl Message for CopyBothResponse with fn decode(payload : BytesView) -> CopyBothResponse raise WireError {
guard payload is [overall_fmt, i16be(ncols), .. rest] else {
raise WireError::InvalidMessage("invalid CopyBothResponse")
}
let overall_format = overall_fmt.to_int()
let col_formats : Array[Int] = []
let mut r = rest
for _ in 0.. String {
"CopyBothResponse"
}
// ---------------------------------------------------------------------------
// NotificationResponse ('A') — LISTEN / NOTIFY
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `NotificationResponse` (B).
///
/// Sent when a `NOTIFY` command is executed for a channel the client is
/// listening on (via `LISTEN`). Can arrive at any time — even during
/// a query — so the driver queues them transparently.
pub(all) struct NotificationResponse {
/// Process ID of the notifying backend.
pid : Int
/// Channel name that was notified.
channel : String
/// Optional payload string (empty string if no payload was given).
payload : String
}
///|
pub impl Message for NotificationResponse with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_NOTIFICATION_RESPONSE)
buf.append_int_be(0) // placeholder
buf.append_int_be(self.pid)
buf.append_string_null(self.channel)
buf.append_string_null(self.payload)
let total = buf.len() - 1
buf.set_int_be(1, total)
buf.to_bytes()
}
///|
pub impl Message for NotificationResponse with fn decode(payload : BytesView) -> NotificationResponse raise WireError {
guard payload is [i32be(pid), .. rest] else {
raise WireError::InvalidMessage("NotificationResponse: truncated pid")
}
let (channel, r1) = read_cstring(rest[:])
let (payload_str, _) = read_cstring(r1)
NotificationResponse::{ pid, channel, payload: payload_str }
}
///|
pub impl Message for NotificationResponse with fn describe(self) -> String {
"NotificationResponse(ch=\{self.channel}, payload=\{self.payload})"
}
// ---------------------------------------------------------------------------
// NegotiateProtocolVersion ('v') — PG 17+ protocol negotiation
// ---------------------------------------------------------------------------
///|
/// PostgreSQL `NegotiateProtocolVersion` (B).
///
/// Sent when the client requests a protocol version newer than the server
/// supports within the same major version. The client should re-send its
/// `StartupMessage` with the version offered by the server.
pub(all) struct NegotiateProtocolVersion {
/// The newest minor version the server supports for the requested major version.
newest_minor_version : Int
/// Optionally, a list of protocol options not available to the client.
options : Array[String]
}
///|
pub impl Message for NegotiateProtocolVersion with fn encode(self) -> Bytes {
let buf = BytesMut::new()
buf.append_byte(MSG_NEGOTIATE_PROTOCOL_VERSION)
buf.append_int_be(0) // placeholder
buf.append_int_be(self.newest_minor_version)
let n = self.options.length()
buf.append_byte(((n >> 8) & 0xFF).to_byte())
buf.append_byte((n & 0xFF).to_byte())
for option in self.options {
buf.append_string_null(option)
}
let total = buf.len() - 1
buf.set_int_be(1, total)
buf.to_bytes()
}
///|
pub impl Message for NegotiateProtocolVersion with fn decode(
payload : BytesView,
) -> NegotiateProtocolVersion raise WireError {
guard payload is [i32be(minor), .. rest] else {
raise WireError::InvalidMessage(
"NegotiateProtocolVersion: truncated minor version",
)
}
// Read option count (int16be). Must use pattern match on rest.
let (nopts, r0) = match rest {
[b0, b1, .. r] => {
let count = (b0.to_int() << 8) | b1.to_int()
(count, r)
}
_ => (0, rest)
}
let options : Array[String] = []
for r = r0 {
if options.length() >= nopts {
break
}
let (opt, r1) = read_cstring(r[:])
options.push(opt)
continue r1
}
NegotiateProtocolVersion::{ newest_minor_version: minor, options }
}
///|
pub impl Message for NegotiateProtocolVersion with fn describe(self) -> String {
"NegotiateProtocolVersion(minor=\{self.newest_minor_version}, options=\{self.options.length()})"
}