1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::collections::HashMap;
4
5#[derive(Debug, Deserialize)]
6#[serde(tag = "code")]
7pub enum Input {
8 #[serde(rename = "query")]
9 Query {
10 id: String,
11 #[serde(default)]
12 session: Option<String>,
13 sql: String,
14 #[serde(default)]
15 params: Vec<Value>,
16 #[serde(default)]
17 options: QueryOptions,
18 },
19 #[serde(rename = "config")]
20 Config(ConfigPatch),
21 #[serde(rename = "cancel")]
22 Cancel { id: String },
23 #[serde(rename = "ping")]
24 Ping,
25 #[serde(rename = "close")]
26 Close,
27}
28
29#[derive(Debug, Deserialize, Default, Clone)]
30#[allow(dead_code)]
31pub struct QueryOptions {
32 #[serde(default)]
33 pub stream_rows: bool,
34 pub batch_rows: Option<usize>,
35 pub batch_bytes: Option<usize>,
36 pub statement_timeout_ms: Option<u64>,
37 pub lock_timeout_ms: Option<u64>,
38 pub read_only: Option<bool>,
39 pub inline_max_rows: Option<usize>,
40 pub inline_max_bytes: Option<usize>,
41}
42
43#[derive(Debug, Serialize)]
44#[serde(tag = "code")]
45pub enum Output {
46 #[serde(rename = "result")]
47 Result {
48 #[serde(skip_serializing_if = "Option::is_none")]
49 id: Option<String>,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 session: Option<String>,
52 command_tag: String,
53 columns: Vec<ColumnInfo>,
54 rows: Vec<Value>,
55 row_count: usize,
56 trace: Trace,
57 },
58 #[serde(rename = "result_start")]
59 ResultStart {
60 id: String,
61 #[serde(skip_serializing_if = "Option::is_none")]
62 session: Option<String>,
63 columns: Vec<ColumnInfo>,
64 },
65 #[serde(rename = "result_rows")]
66 ResultRows {
67 id: String,
68 rows: Vec<Value>,
69 rows_batch_count: usize,
70 },
71 #[serde(rename = "result_end")]
72 ResultEnd {
73 id: String,
74 #[serde(skip_serializing_if = "Option::is_none")]
75 session: Option<String>,
76 command_tag: String,
77 trace: Trace,
78 },
79 #[serde(rename = "sql_error")]
80 SqlError {
81 #[serde(skip_serializing_if = "Option::is_none")]
82 id: Option<String>,
83 #[serde(skip_serializing_if = "Option::is_none")]
84 session: Option<String>,
85 sqlstate: String,
86 message: String,
87 #[serde(skip_serializing_if = "Option::is_none")]
88 detail: Option<String>,
89 #[serde(skip_serializing_if = "Option::is_none")]
90 hint: Option<String>,
91 #[serde(skip_serializing_if = "Option::is_none")]
92 position: Option<String>,
93 trace: Trace,
94 },
95 #[serde(rename = "error")]
96 Error {
97 #[serde(skip_serializing_if = "Option::is_none")]
98 id: Option<String>,
99 error_code: String,
100 error: String,
101 #[serde(skip_serializing_if = "Option::is_none")]
102 hint: Option<String>,
103 retryable: bool,
104 trace: Trace,
105 },
106 #[serde(rename = "dry_run")]
107 DryRun {
108 #[serde(skip_serializing_if = "Option::is_none")]
109 id: Option<String>,
110 sql: String,
111 params: Vec<String>,
112 #[serde(skip_serializing_if = "Option::is_none")]
113 session: Option<String>,
114 trace: Trace,
115 },
116 #[serde(rename = "config")]
117 Config(RuntimeConfig),
118 #[serde(rename = "pong")]
119 Pong { trace: PongTrace },
120 #[serde(rename = "close")]
121 Close { message: String, trace: CloseTrace },
122 #[serde(rename = "log")]
123 Log {
124 event: String,
125 #[serde(skip_serializing_if = "Option::is_none")]
126 request_id: Option<String>,
127 #[serde(skip_serializing_if = "Option::is_none")]
128 session: Option<String>,
129 #[serde(skip_serializing_if = "Option::is_none")]
130 error_code: Option<String>,
131 #[serde(skip_serializing_if = "Option::is_none")]
132 command_tag: Option<String>,
133 #[serde(skip_serializing_if = "Option::is_none")]
134 version: Option<String>,
135 #[serde(skip_serializing_if = "Option::is_none")]
136 argv: Option<Vec<String>>,
137 #[serde(skip_serializing_if = "Option::is_none")]
138 config: Option<Value>,
139 #[serde(skip_serializing_if = "Option::is_none")]
140 args: Option<Value>,
141 #[serde(skip_serializing_if = "Option::is_none")]
142 env: Option<Value>,
143 trace: Trace,
144 },
145}
146
147#[derive(Debug, Serialize, Clone)]
148pub struct ColumnInfo {
149 pub name: String,
150 #[serde(rename = "type")]
151 pub type_name: String,
152}
153
154#[derive(Debug, Serialize, Clone)]
155pub struct Trace {
156 pub duration_ms: u64,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub row_count: Option<usize>,
159 #[serde(skip_serializing_if = "Option::is_none")]
160 pub payload_bytes: Option<usize>,
161}
162
163impl Trace {
164 pub fn only_duration(duration_ms: u64) -> Self {
165 Self {
166 duration_ms,
167 row_count: None,
168 payload_bytes: None,
169 }
170 }
171}
172
173#[derive(Debug, Serialize)]
174pub struct PongTrace {
175 pub uptime_s: u64,
176 pub requests_total: u64,
177 pub in_flight: usize,
178}
179
180#[derive(Debug, Serialize)]
181pub struct CloseTrace {
182 pub uptime_s: u64,
183 pub requests_total: u64,
184}
185
186#[derive(Debug, Serialize, Deserialize, Clone, Default)]
187pub struct SessionConfig {
188 #[serde(skip_serializing_if = "Option::is_none")]
189 pub dsn_secret: Option<String>,
190 #[serde(skip_serializing_if = "Option::is_none")]
191 pub conninfo_secret: Option<String>,
192 #[serde(skip_serializing_if = "Option::is_none")]
193 pub host: Option<String>,
194 #[serde(skip_serializing_if = "Option::is_none")]
195 pub port: Option<u16>,
196 #[serde(skip_serializing_if = "Option::is_none")]
197 pub user: Option<String>,
198 #[serde(skip_serializing_if = "Option::is_none")]
199 pub dbname: Option<String>,
200 #[serde(skip_serializing_if = "Option::is_none")]
201 pub password_secret: Option<String>,
202}
203
204#[derive(Debug, Serialize, Deserialize, Clone)]
205pub struct RuntimeConfig {
206 pub default_session: String,
207 #[serde(default)]
208 pub sessions: HashMap<String, SessionConfig>,
209 pub inline_max_rows: usize,
210 pub inline_max_bytes: usize,
211 pub statement_timeout_ms: u64,
212 pub lock_timeout_ms: u64,
213 #[serde(default)]
214 pub log: Vec<String>,
215}
216
217impl Default for RuntimeConfig {
218 fn default() -> Self {
219 let mut sessions = HashMap::new();
220 sessions.insert("default".to_string(), SessionConfig::default());
221 Self {
222 default_session: "default".to_string(),
223 sessions,
224 inline_max_rows: 1000,
225 inline_max_bytes: 1_048_576,
226 statement_timeout_ms: 30_000,
227 lock_timeout_ms: 5_000,
228 log: vec![],
229 }
230 }
231}
232
233#[derive(Debug, Deserialize, Default)]
234pub struct ConfigPatch {
235 pub default_session: Option<String>,
236 pub sessions: Option<HashMap<String, SessionConfigPatch>>,
237 pub inline_max_rows: Option<usize>,
238 pub inline_max_bytes: Option<usize>,
239 pub statement_timeout_ms: Option<u64>,
240 pub lock_timeout_ms: Option<u64>,
241 pub log: Option<Vec<String>>,
242}
243
244#[derive(Debug, Default)]
245pub enum PatchField<T> {
246 #[default]
247 Missing,
248 Null,
249 Value(T),
250}
251
252impl<'de, T> Deserialize<'de> for PatchField<T>
253where
254 T: Deserialize<'de>,
255{
256 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
257 where
258 D: serde::Deserializer<'de>,
259 {
260 let value = Option::<T>::deserialize(deserializer)?;
261 match value {
262 Some(value) => Ok(Self::Value(value)),
263 None => Ok(Self::Null),
264 }
265 }
266}
267
268impl<T> PatchField<T> {
269 pub fn into_update(self) -> Option<Option<T>> {
270 match self {
271 Self::Missing => None,
272 Self::Null => Some(None),
273 Self::Value(value) => Some(Some(value)),
274 }
275 }
276}
277
278#[derive(Debug, Deserialize, Default)]
279pub struct SessionConfigPatch {
280 #[serde(default)]
281 pub dsn_secret: PatchField<String>,
282 #[serde(default)]
283 pub conninfo_secret: PatchField<String>,
284 #[serde(default)]
285 pub host: PatchField<String>,
286 #[serde(default)]
287 pub port: PatchField<u16>,
288 #[serde(default)]
289 pub user: PatchField<String>,
290 #[serde(default)]
291 pub dbname: PatchField<String>,
292 #[serde(default)]
293 pub password_secret: PatchField<String>,
294}
295
296#[derive(Debug, Clone)]
297#[allow(dead_code)]
298pub struct ResolvedOptions {
299 pub stream_rows: bool,
300 pub batch_rows: usize,
301 pub batch_bytes: usize,
302 pub statement_timeout_ms: u64,
303 pub lock_timeout_ms: u64,
304 pub read_only: bool,
305 pub inline_max_rows: usize,
306 pub inline_max_bytes: usize,
307}
308
309#[cfg(test)]
310#[path = "../tests/support/unit_types.rs"]
311mod tests;