1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::collections::HashMap;
4
5#[derive(Debug, Deserialize)]
6#[serde(tag = "code", deny_unknown_fields)]
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 #[serde(rename = "session_info")]
28 SessionInfo {
29 #[serde(default)]
30 id: Option<String>,
31 #[serde(default)]
32 session: Option<String>,
33 },
34 #[serde(rename = "begin")]
38 Begin {
39 #[serde(default)]
40 id: Option<String>,
41 #[serde(default)]
42 session: Option<String>,
43 #[serde(default)]
46 read_only: bool,
47 #[serde(default)]
51 permission: Option<Permission>,
52 },
53 #[serde(rename = "commit")]
54 Commit {
55 #[serde(default)]
56 id: Option<String>,
57 #[serde(default)]
58 session: Option<String>,
59 },
60 #[serde(rename = "rollback")]
61 Rollback {
62 #[serde(default)]
63 id: Option<String>,
64 #[serde(default)]
65 session: Option<String>,
66 },
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
70pub enum Permission {
71 #[serde(rename = "read")]
72 Read,
73 #[serde(rename = "write")]
74 Write,
75 #[serde(rename = "ssh-read")]
76 SshRead,
77 #[serde(rename = "ssh-write")]
78 SshWrite,
79 #[serde(rename = "container-read")]
80 ContainerRead,
81 #[serde(rename = "container-write")]
82 ContainerWrite,
83}
84
85impl Permission {
86 pub fn as_str(self) -> &'static str {
87 match self {
88 Self::Read => "read",
89 Self::Write => "write",
90 Self::SshRead => "ssh-read",
91 Self::SshWrite => "ssh-write",
92 Self::ContainerRead => "container-read",
93 Self::ContainerWrite => "container-write",
94 }
95 }
96
97 pub fn is_read_only(self) -> bool {
98 matches!(self, Self::Read | Self::SshRead | Self::ContainerRead)
99 }
100
101 pub fn allows_ssh(self) -> bool {
102 matches!(self, Self::SshRead | Self::SshWrite)
103 }
104
105 pub fn allows_container(self) -> bool {
106 matches!(self, Self::ContainerRead | Self::ContainerWrite)
107 }
108}
109
110impl std::str::FromStr for Permission {
111 type Err = String;
112
113 fn from_str(value: &str) -> Result<Self, Self::Err> {
114 match value {
115 "read" => Ok(Self::Read),
116 "write" => Ok(Self::Write),
117 "ssh-read" => Ok(Self::SshRead),
118 "ssh-write" => Ok(Self::SshWrite),
119 "container-read" => Ok(Self::ContainerRead),
120 "container-write" => Ok(Self::ContainerWrite),
121 _ => Err(format!(
122 "invalid permission `{value}`; expected read, write, ssh-read, ssh-write, container-read, or container-write"
123 )),
124 }
125 }
126}
127
128#[derive(Debug, Deserialize, Default, Clone)]
129#[serde(deny_unknown_fields)]
130#[allow(dead_code)]
131pub struct QueryOptions {
132 #[serde(default)]
133 pub stream_rows: bool,
134 pub batch_rows: Option<usize>,
135 pub batch_bytes: Option<usize>,
136 pub statement_timeout_ms: Option<u64>,
137 pub lock_timeout_ms: Option<u64>,
138 pub permission: Option<Permission>,
139 pub inline_max_rows: Option<usize>,
140 pub inline_max_bytes: Option<usize>,
141}
142
143#[derive(Debug, Serialize)]
144#[serde(tag = "code")]
145pub enum Output {
146 #[serde(rename = "result")]
147 Result {
148 #[serde(skip_serializing_if = "Option::is_none")]
149 id: Option<String>,
150 #[serde(skip_serializing_if = "Option::is_none")]
151 session: Option<String>,
152 command_tag: String,
153 columns: Vec<ColumnInfo>,
154 rows: Vec<Value>,
155 row_count: usize,
156 #[serde(skip_serializing_if = "is_false", default)]
159 truncated: bool,
160 #[serde(skip_serializing_if = "Option::is_none")]
162 truncated_at_rows: Option<usize>,
163 #[serde(skip_serializing_if = "Option::is_none")]
165 truncated_at_bytes: Option<usize>,
166 trace: Trace,
167 },
168 #[serde(rename = "result_start")]
169 ResultStart {
170 id: String,
171 #[serde(skip_serializing_if = "Option::is_none")]
172 session: Option<String>,
173 columns: Vec<ColumnInfo>,
174 },
175 #[serde(rename = "result_rows")]
176 ResultRows {
177 id: String,
178 rows: Vec<Value>,
179 rows_batch_count: usize,
180 },
181 #[serde(rename = "result_end")]
182 ResultEnd {
183 id: String,
184 #[serde(skip_serializing_if = "Option::is_none")]
185 session: Option<String>,
186 command_tag: String,
187 trace: Trace,
188 },
189 #[serde(rename = "sql_error")]
190 SqlError {
191 #[serde(skip_serializing_if = "Option::is_none")]
192 id: Option<String>,
193 #[serde(skip_serializing_if = "Option::is_none")]
194 session: Option<String>,
195 sqlstate: String,
196 message: String,
197 #[serde(skip_serializing_if = "Option::is_none")]
198 detail: Option<String>,
199 #[serde(skip_serializing_if = "Option::is_none")]
200 hint: Option<String>,
201 #[serde(skip_serializing_if = "Option::is_none")]
202 position: Option<String>,
203 trace: Trace,
204 },
205 #[serde(rename = "error")]
206 Error {
207 #[serde(skip_serializing_if = "Option::is_none")]
208 id: Option<String>,
209 error_code: String,
210 error: String,
211 #[serde(skip_serializing_if = "Option::is_none")]
212 sqlstate: Option<String>,
213 #[serde(skip_serializing_if = "Option::is_none")]
214 message: Option<String>,
215 #[serde(skip_serializing_if = "Option::is_none")]
216 detail: Option<String>,
217 #[serde(skip_serializing_if = "Option::is_none")]
218 hint: Option<String>,
219 retryable: bool,
220 trace: Trace,
221 },
222 #[serde(rename = "dry_run")]
223 DryRun {
224 #[serde(skip_serializing_if = "Option::is_none")]
225 id: Option<String>,
226 sql: String,
227 params: Vec<String>,
228 #[serde(skip_serializing_if = "Option::is_none")]
229 session: Option<String>,
230 #[serde(skip_serializing_if = "Vec::is_empty", default)]
233 param_types: Vec<String>,
234 #[serde(skip_serializing_if = "Vec::is_empty", default)]
237 columns: Vec<ColumnInfo>,
238 trace: Trace,
239 },
240 #[serde(rename = "config")]
241 Config(RuntimeConfig),
242 #[serde(rename = "pong")]
243 Pong { trace: PongTrace },
244 #[serde(rename = "close")]
245 Close { message: String, trace: CloseTrace },
246 #[serde(rename = "session_info")]
247 SessionInfo {
248 #[serde(skip_serializing_if = "Option::is_none")]
249 id: Option<String>,
250 session: String,
251 transport_kind: String,
252 permission_default: String,
253 stream_rows_default: bool,
254 batch_rows: usize,
255 batch_bytes: usize,
256 inline_max_rows: usize,
257 inline_max_bytes: usize,
258 statement_timeout_ms: u64,
259 lock_timeout_ms: u64,
260 #[serde(skip_serializing_if = "Option::is_none")]
261 database: Option<String>,
262 #[serde(skip_serializing_if = "Option::is_none")]
263 user: Option<String>,
264 #[serde(skip_serializing_if = "Option::is_none")]
265 host: Option<String>,
266 #[serde(skip_serializing_if = "Option::is_none")]
267 port: Option<u16>,
268 #[serde(skip_serializing_if = "Option::is_none")]
269 server_version: Option<String>,
270 trace: Trace,
271 },
272 #[serde(rename = "log")]
273 Log {
274 event: String,
275 #[serde(skip_serializing_if = "Option::is_none")]
276 request_id: Option<String>,
277 #[serde(skip_serializing_if = "Option::is_none")]
278 session: Option<String>,
279 #[serde(skip_serializing_if = "Option::is_none")]
280 error_code: Option<String>,
281 #[serde(skip_serializing_if = "Option::is_none")]
282 command_tag: Option<String>,
283 #[serde(skip_serializing_if = "Option::is_none")]
284 version: Option<String>,
285 #[serde(skip_serializing_if = "Option::is_none")]
286 config: Option<Value>,
287 #[serde(skip_serializing_if = "Option::is_none")]
288 args: Option<Value>,
289 #[serde(skip_serializing_if = "Option::is_none")]
290 env: Option<Value>,
291 #[serde(skip_serializing_if = "Option::is_none")]
292 chain: Option<String>,
293 trace: Trace,
294 },
295}
296
297fn is_false(b: &bool) -> bool {
298 !*b
299}
300
301#[derive(Debug, Serialize, Clone)]
302pub struct ColumnInfo {
303 pub name: String,
304 #[serde(rename = "type")]
305 pub type_name: String,
306}
307
308#[derive(Debug, Serialize, Clone)]
309pub struct Trace {
310 pub duration_ms: u64,
311 #[serde(skip_serializing_if = "Option::is_none")]
312 pub row_count: Option<usize>,
313 #[serde(skip_serializing_if = "Option::is_none")]
314 pub payload_bytes: Option<usize>,
315}
316
317impl Trace {
318 pub fn only_duration(duration_ms: u64) -> Self {
319 Self {
320 duration_ms,
321 row_count: None,
322 payload_bytes: None,
323 }
324 }
325}
326
327#[derive(Debug, Serialize)]
328pub struct PongTrace {
329 pub uptime_s: u64,
330 pub requests_total: u64,
331 pub in_flight: usize,
332}
333
334#[derive(Debug, Serialize)]
335pub struct CloseTrace {
336 pub uptime_s: u64,
337 pub requests_total: u64,
338}
339
340#[derive(Debug, Serialize, Clone, Default)]
341pub struct SessionConfig {
342 #[serde(skip_serializing_if = "Option::is_none")]
343 pub dsn_secret: Option<String>,
344 #[serde(skip_serializing_if = "Option::is_none")]
345 pub conninfo_secret: Option<String>,
346 #[serde(skip_serializing_if = "Option::is_none")]
347 pub host: Option<String>,
348 #[serde(skip_serializing_if = "Option::is_none")]
349 pub port: Option<u16>,
350 #[serde(skip_serializing_if = "Option::is_none")]
351 pub user: Option<String>,
352 #[serde(skip_serializing_if = "Option::is_none")]
353 pub dbname: Option<String>,
354 #[serde(skip_serializing_if = "Option::is_none")]
355 pub password_secret: Option<String>,
356 #[serde(flatten)]
357 pub ssh: SshConfig,
358 #[serde(flatten)]
359 pub container: ContainerConfig,
360}
361
362#[derive(Debug, Serialize, Deserialize, Clone, Default)]
363pub struct SshConfig {
364 #[serde(rename = "ssh", skip_serializing_if = "Option::is_none")]
365 pub destination: Option<String>,
366 #[serde(rename = "ssh_options", default, skip_serializing_if = "Vec::is_empty")]
367 pub options: Vec<String>,
368 #[serde(rename = "ssh_local_host", skip_serializing_if = "Option::is_none")]
369 pub local_host: Option<String>,
370 #[serde(rename = "ssh_local_port", skip_serializing_if = "Option::is_none")]
371 pub local_port: Option<u16>,
372 #[serde(rename = "ssh_remote_socket", skip_serializing_if = "Option::is_none")]
373 pub remote_socket: Option<String>,
374 #[serde(rename = "ssh_sudo_user", skip_serializing_if = "Option::is_none")]
375 pub sudo_user: Option<String>,
376}
377
378impl SshConfig {
379 pub fn has_transport_fields(&self) -> bool {
380 self.destination.is_some()
381 || !self.options.is_empty()
382 || self.local_host.is_some()
383 || self.local_port.is_some()
384 || self.remote_socket.is_some()
385 || self.sudo_user.is_some()
386 }
387
388 pub fn has_tunnel_or_bridge_options(&self) -> bool {
389 self.local_host.is_some()
390 || self.local_port.is_some()
391 || self.remote_socket.is_some()
392 || self.sudo_user.is_some()
393 }
394}
395
396#[derive(Debug, Serialize, Deserialize, Clone, Default)]
397pub struct ContainerConfig {
398 #[serde(rename = "container", skip_serializing_if = "Option::is_none")]
399 pub target: Option<String>,
400 #[serde(rename = "container_driver", skip_serializing_if = "Option::is_none")]
401 pub driver: Option<String>,
402 #[serde(rename = "container_runtime", skip_serializing_if = "Option::is_none")]
403 pub runtime: Option<String>,
404 #[serde(rename = "container_user", skip_serializing_if = "Option::is_none")]
405 pub user: Option<String>,
406 #[serde(
407 rename = "container_namespace",
408 skip_serializing_if = "Option::is_none"
409 )]
410 pub namespace: Option<String>,
411 #[serde(rename = "container_context", skip_serializing_if = "Option::is_none")]
412 pub context: Option<String>,
413 #[serde(
414 rename = "container_compose_files",
415 default,
416 skip_serializing_if = "Vec::is_empty"
417 )]
418 pub compose_files: Vec<String>,
419 #[serde(
420 rename = "container_compose_project",
421 skip_serializing_if = "Option::is_none"
422 )]
423 pub compose_project: Option<String>,
424 #[serde(
425 rename = "container_pod_container",
426 skip_serializing_if = "Option::is_none"
427 )]
428 pub pod_container: Option<String>,
429}
430
431impl ContainerConfig {
432 pub fn has_transport_fields(&self) -> bool {
433 self.target.is_some()
434 || self.driver.is_some()
435 || self.runtime.is_some()
436 || self.user.is_some()
437 || self.namespace.is_some()
438 || self.context.is_some()
439 || !self.compose_files.is_empty()
440 || self.compose_project.is_some()
441 || self.pod_container.is_some()
442 }
443}
444
445#[derive(Debug, Deserialize, Default)]
446#[serde(deny_unknown_fields)]
447struct SessionConfigFlat {
448 #[serde(default)]
449 dsn_secret: Option<String>,
450 #[serde(default)]
451 conninfo_secret: Option<String>,
452 #[serde(default)]
453 host: Option<String>,
454 #[serde(default)]
455 port: Option<u16>,
456 #[serde(default)]
457 user: Option<String>,
458 #[serde(default)]
459 dbname: Option<String>,
460 #[serde(default)]
461 password_secret: Option<String>,
462 #[serde(default)]
463 ssh: Option<String>,
464 #[serde(default)]
465 ssh_options: Vec<String>,
466 #[serde(default)]
467 ssh_local_host: Option<String>,
468 #[serde(default)]
469 ssh_local_port: Option<u16>,
470 #[serde(default)]
471 ssh_remote_socket: Option<String>,
472 #[serde(default)]
473 ssh_sudo_user: Option<String>,
474 #[serde(default)]
475 container: Option<String>,
476 #[serde(default)]
477 container_driver: Option<String>,
478 #[serde(default)]
479 container_runtime: Option<String>,
480 #[serde(default)]
481 container_user: Option<String>,
482 #[serde(default)]
483 container_namespace: Option<String>,
484 #[serde(default)]
485 container_context: Option<String>,
486 #[serde(default)]
487 container_compose_files: Vec<String>,
488 #[serde(default)]
489 container_compose_project: Option<String>,
490 #[serde(default)]
491 container_pod_container: Option<String>,
492}
493
494impl From<SessionConfigFlat> for SessionConfig {
495 fn from(flat: SessionConfigFlat) -> Self {
496 Self {
497 dsn_secret: flat.dsn_secret,
498 conninfo_secret: flat.conninfo_secret,
499 host: flat.host,
500 port: flat.port,
501 user: flat.user,
502 dbname: flat.dbname,
503 password_secret: flat.password_secret,
504 ssh: SshConfig {
505 destination: flat.ssh,
506 options: flat.ssh_options,
507 local_host: flat.ssh_local_host,
508 local_port: flat.ssh_local_port,
509 remote_socket: flat.ssh_remote_socket,
510 sudo_user: flat.ssh_sudo_user,
511 },
512 container: ContainerConfig {
513 target: flat.container,
514 driver: flat.container_driver,
515 runtime: flat.container_runtime,
516 user: flat.container_user,
517 namespace: flat.container_namespace,
518 context: flat.container_context,
519 compose_files: flat.container_compose_files,
520 compose_project: flat.container_compose_project,
521 pod_container: flat.container_pod_container,
522 },
523 }
524 }
525}
526
527impl<'de> Deserialize<'de> for SessionConfig {
528 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
529 where
530 D: serde::Deserializer<'de>,
531 {
532 SessionConfigFlat::deserialize(deserializer).map(Self::from)
533 }
534}
535
536impl SessionConfig {
537 pub fn uses_ssh_transport(&self) -> bool {
538 self.ssh.has_transport_fields()
539 }
540
541 pub fn uses_container_transport(&self) -> bool {
542 self.container.has_transport_fields()
543 }
544
545 pub fn transport_kind(&self) -> Result<TransportKind, String> {
546 let uses_ssh = self.uses_ssh_transport();
547 let uses_container = self.uses_container_transport();
548 match (uses_ssh, uses_container) {
549 (false, false) => Ok(TransportKind::Direct),
550 (true, false) => Ok(TransportKind::Ssh),
551 (false, true) => Ok(TransportKind::Container),
552 (true, true) => Ok(TransportKind::Container),
555 }
556 }
557}
558
559#[derive(Debug, Clone, Copy, PartialEq, Eq)]
560pub enum TransportKind {
561 Direct,
562 Ssh,
563 Container,
564}
565
566impl TransportKind {
567 pub fn as_str(self) -> &'static str {
568 match self {
569 Self::Direct => "direct",
570 Self::Ssh => "ssh",
571 Self::Container => "container",
572 }
573 }
574}
575
576#[derive(Debug, Serialize, Deserialize, Clone)]
577pub struct RuntimeConfig {
578 pub default_session: String,
579 #[serde(default)]
580 pub sessions: HashMap<String, SessionConfig>,
581 pub inline_max_rows: usize,
582 pub inline_max_bytes: usize,
583 pub statement_timeout_ms: u64,
584 pub lock_timeout_ms: u64,
585 #[serde(default)]
586 pub log: Vec<String>,
587}
588
589impl Default for RuntimeConfig {
590 fn default() -> Self {
591 let mut sessions = HashMap::new();
592 sessions.insert("default".to_string(), SessionConfig::default());
593 Self {
594 default_session: "default".to_string(),
595 sessions,
596 inline_max_rows: 1000,
597 inline_max_bytes: 1_048_576,
598 statement_timeout_ms: 30_000,
599 lock_timeout_ms: 5_000,
600 log: vec![],
601 }
602 }
603}
604
605#[derive(Debug, Deserialize, Default)]
606#[serde(deny_unknown_fields)]
607pub struct ConfigPatch {
608 pub default_session: Option<String>,
609 pub sessions: Option<HashMap<String, SessionConfigPatch>>,
610 pub inline_max_rows: Option<usize>,
611 pub inline_max_bytes: Option<usize>,
612 pub statement_timeout_ms: Option<u64>,
613 pub lock_timeout_ms: Option<u64>,
614 pub log: Option<Vec<String>>,
615}
616
617#[derive(Debug, Default)]
618pub enum PatchField<T> {
619 #[default]
620 Missing,
621 Null,
622 Value(T),
623}
624
625impl<'de, T> Deserialize<'de> for PatchField<T>
626where
627 T: Deserialize<'de>,
628{
629 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
630 where
631 D: serde::Deserializer<'de>,
632 {
633 let value = Option::<T>::deserialize(deserializer)?;
634 match value {
635 Some(value) => Ok(Self::Value(value)),
636 None => Ok(Self::Null),
637 }
638 }
639}
640
641impl<T> PatchField<T> {
642 pub fn into_update(self) -> Option<Option<T>> {
643 match self {
644 Self::Missing => None,
645 Self::Null => Some(None),
646 Self::Value(value) => Some(Some(value)),
647 }
648 }
649}
650
651#[derive(Debug, Default)]
652pub struct SessionConfigPatch {
653 pub dsn_secret: PatchField<String>,
654 pub conninfo_secret: PatchField<String>,
655 pub host: PatchField<String>,
656 pub port: PatchField<u16>,
657 pub user: PatchField<String>,
658 pub dbname: PatchField<String>,
659 pub password_secret: PatchField<String>,
660 pub ssh: SshConfigPatch,
661 pub container: ContainerConfigPatch,
662}
663
664#[derive(Debug, Default)]
665pub struct SshConfigPatch {
666 pub destination: PatchField<String>,
667 pub options: PatchField<Vec<String>>,
668 pub local_host: PatchField<String>,
669 pub local_port: PatchField<u16>,
670 pub remote_socket: PatchField<String>,
671 pub sudo_user: PatchField<String>,
672}
673
674#[derive(Debug, Default)]
675pub struct ContainerConfigPatch {
676 pub target: PatchField<String>,
677 pub driver: PatchField<String>,
678 pub runtime: PatchField<String>,
679 pub user: PatchField<String>,
680 pub namespace: PatchField<String>,
681 pub context: PatchField<String>,
682 pub compose_files: PatchField<Vec<String>>,
683 pub compose_project: PatchField<String>,
684 pub pod_container: PatchField<String>,
685}
686
687#[derive(Debug, Deserialize, Default)]
688#[serde(deny_unknown_fields)]
689struct SessionConfigPatchFlat {
690 #[serde(default)]
691 dsn_secret: PatchField<String>,
692 #[serde(default)]
693 conninfo_secret: PatchField<String>,
694 #[serde(default)]
695 host: PatchField<String>,
696 #[serde(default)]
697 port: PatchField<u16>,
698 #[serde(default)]
699 user: PatchField<String>,
700 #[serde(default)]
701 dbname: PatchField<String>,
702 #[serde(default)]
703 password_secret: PatchField<String>,
704 #[serde(default)]
705 ssh: PatchField<String>,
706 #[serde(default)]
707 ssh_options: PatchField<Vec<String>>,
708 #[serde(default)]
709 ssh_local_host: PatchField<String>,
710 #[serde(default)]
711 ssh_local_port: PatchField<u16>,
712 #[serde(default)]
713 ssh_remote_socket: PatchField<String>,
714 #[serde(default)]
715 ssh_sudo_user: PatchField<String>,
716 #[serde(default)]
717 container: PatchField<String>,
718 #[serde(default)]
719 container_driver: PatchField<String>,
720 #[serde(default)]
721 container_runtime: PatchField<String>,
722 #[serde(default)]
723 container_user: PatchField<String>,
724 #[serde(default)]
725 container_namespace: PatchField<String>,
726 #[serde(default)]
727 container_context: PatchField<String>,
728 #[serde(default)]
729 container_compose_files: PatchField<Vec<String>>,
730 #[serde(default)]
731 container_compose_project: PatchField<String>,
732 #[serde(default)]
733 container_pod_container: PatchField<String>,
734}
735
736impl From<SessionConfigPatchFlat> for SessionConfigPatch {
737 fn from(flat: SessionConfigPatchFlat) -> Self {
738 Self {
739 dsn_secret: flat.dsn_secret,
740 conninfo_secret: flat.conninfo_secret,
741 host: flat.host,
742 port: flat.port,
743 user: flat.user,
744 dbname: flat.dbname,
745 password_secret: flat.password_secret,
746 ssh: SshConfigPatch {
747 destination: flat.ssh,
748 options: flat.ssh_options,
749 local_host: flat.ssh_local_host,
750 local_port: flat.ssh_local_port,
751 remote_socket: flat.ssh_remote_socket,
752 sudo_user: flat.ssh_sudo_user,
753 },
754 container: ContainerConfigPatch {
755 target: flat.container,
756 driver: flat.container_driver,
757 runtime: flat.container_runtime,
758 user: flat.container_user,
759 namespace: flat.container_namespace,
760 context: flat.container_context,
761 compose_files: flat.container_compose_files,
762 compose_project: flat.container_compose_project,
763 pod_container: flat.container_pod_container,
764 },
765 }
766 }
767}
768
769impl<'de> Deserialize<'de> for SessionConfigPatch {
770 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
771 where
772 D: serde::Deserializer<'de>,
773 {
774 SessionConfigPatchFlat::deserialize(deserializer).map(Self::from)
775 }
776}
777
778#[derive(Debug, Clone)]
779#[allow(dead_code)]
780pub struct ResolvedOptions {
781 pub stream_rows: bool,
782 pub batch_rows: usize,
783 pub batch_bytes: usize,
784 pub statement_timeout_ms: u64,
785 pub lock_timeout_ms: u64,
786 pub read_only: bool,
787 pub inline_max_rows: usize,
788 pub inline_max_bytes: usize,
789}
790
791#[cfg(test)]
792#[path = "../tests/support/unit_types.rs"]
793mod tests;