1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//! PostgreSQL Frontend/Backend Protocol v3.0 wire payload builder.
//!
//! This module implements [`build_postgres_payload`], which serializes a [`PostgresSpec`]
//! AST node into a PostgreSQL v3.0 (`0x00030000`) frontend or backend wire message over TCP port
//! 5432. It supports unprefixed `StartupMessage` frames (32-bit length, version `196608`, and
//! NUL-terminated `user`/`database` key-value pairs) as well as type-prefixed messages:
//! `AuthenticationOk` (`'R'`), `ReadyForQuery` (`'Z'`), `CommandComplete` (`'C'`), `ErrorResponse`
//! (`'E'`), and simple SQL `Query` (`'Q'`).
use crate::ast::*;
use crate::builders::WireContext;
use alloc::vec::Vec;
/// Builds a PostgreSQL 3.0 frontend/backend protocol message payload.
pub fn build_postgres_payload(
_ctx: &WireContext,
spec: &PostgresSpec,
payload_bytes: &[u8],
) -> Vec<u8> {
let mut pkt = Vec::new();
if spec.is_startup
|| ((spec.user.is_some() || spec.db.is_some()) && !spec.is_query && spec.sql.is_none())
{
// StartupMessage: 4B length (including self) + 0x00030000 (v3.0) + key/value pairs + 0x00
let user = spec.user.unwrap_or("postgres").as_bytes();
let db = spec.db.unwrap_or("postgres").as_bytes();
let mut body = Vec::new();
body.extend_from_slice(&0x0003_0000u32.to_be_bytes());
body.extend_from_slice(b"user\0");
body.extend_from_slice(user);
body.push(0);
body.extend_from_slice(b"database\0");
body.extend_from_slice(db);
body.push(0);
body.push(0); // Terminating null byte
let length = (4 + body.len()) as u32;
pkt.extend_from_slice(&length.to_be_bytes());
pkt.extend_from_slice(&body);
} else if spec.is_ready_for_query {
// ReadyForQuery ('Z', length 5, status 'I' Idle)
pkt.push(b'Z');
pkt.extend_from_slice(&5u32.to_be_bytes());
pkt.push(b'I');
} else if spec.is_command_complete {
// CommandComplete ('C', length, command tag + NUL)
let tag = spec.sql.unwrap_or("SELECT 1").as_bytes();
let length = (4 + tag.len() + 1) as u32;
pkt.push(b'C');
pkt.extend_from_slice(&length.to_be_bytes());
pkt.extend_from_slice(tag);
pkt.push(0);
} else if spec.is_error_response {
// ErrorResponse ('E', length, severity + SQLSTATE + message + NUL)
let msg = spec.sql.unwrap_or("authentication failed").as_bytes();
let mut body = Vec::new();
body.extend_from_slice(b"SERROR\0C28P01\0M");
body.extend_from_slice(msg);
body.push(0);
body.push(0);
let length = (4 + body.len()) as u32;
pkt.push(b'E');
pkt.extend_from_slice(&length.to_be_bytes());
pkt.extend_from_slice(&body);
} else if spec.is_auth_ok {
// AuthenticationOk ('R', length 8, auth code 0)
pkt.push(b'R');
pkt.extend_from_slice(&8u32.to_be_bytes());
pkt.extend_from_slice(&0u32.to_be_bytes());
} else {
// Simple Query ('Q', length, query + NUL)
let _ = spec.is_query;
let sql = spec.sql.unwrap_or("SELECT version()");
let sql_bytes = sql.as_bytes();
let length = (4 + sql_bytes.len() + 1) as u32;
pkt.push(b'Q');
pkt.extend_from_slice(&length.to_be_bytes());
pkt.extend_from_slice(sql_bytes);
pkt.push(0);
}
if !payload_bytes.is_empty() {
pkt.extend_from_slice(payload_bytes);
}
pkt
}