1use std::io::{BufReader, BufWriter, Read, Stdout};
10use std::path::{Path, PathBuf};
11
12use arrow::array::RecordBatch;
13use arrow::datatypes::SchemaRef;
14use serde_json::{Map, Value};
15
16use crate::frame::{self, Frame, FrameError};
17use crate::msg::{ConnectionField, Request, Response, ResultSetMeta};
18use crate::options::{self, OptionField};
19use crate::{CAP_VALIDATE, Kind, MAX_VERSION, MIN_VERSION, PluginId};
20
21pub type Error = Box<dyn std::error::Error + Send + Sync>;
22pub type Result<T> = std::result::Result<T, Error>;
23
24#[derive(Debug, Clone)]
26pub struct About {
27 pub name: &'static str,
28 pub version: &'static str,
29 pub capabilities: &'static [&'static str],
30}
31
32impl About {
33 pub fn new(name: &'static str, version: &'static str) -> About {
35 let version = if version == "0.0.0" { "unreleased" } else { version };
36 About {
37 name,
38 version,
39 capabilities: &[],
40 }
41 }
42
43 pub fn capabilities(mut self, caps: &'static [&'static str]) -> About {
44 self.capabilities = caps;
45 self
46 }
47}
48
49pub trait ResultSink {
52 fn no_result(&mut self, rows_affected: Option<u64>) -> Result<()>;
53 fn begin(&mut self, schema: SchemaRef) -> Result<()>;
54 fn batch(&mut self, batch: RecordBatch) -> Result<bool>;
56}
57
58pub trait Source {
59 fn connection_fields(&self) -> Vec<ConnectionField> {
60 Vec::new()
61 }
62 fn open(&mut self, connection: &Map<String, Value>, read_only: bool) -> Result<()>;
63 fn execute(&mut self, sql: &str, row_limit: Option<u64>, out: &mut dyn ResultSink) -> Result<()>;
65 fn check(&mut self, _sql: &str) -> Result<()> {
67 Err("this source can't check statements".into())
68 }
69 fn load(&mut self, _name: &str, _data: &mut ResultSet<'_>) -> Result<Loaded> {
72 Err("this source can't load rows".into())
73 }
74 fn close(&mut self) {}
76}
77
78pub struct Loaded {
80 pub relation: String,
82 pub rows: u64,
83 pub warning: Option<String>,
85}
86
87pub struct ResultSet<'a> {
89 pub meta: ResultSetMeta,
90 pub schema: SchemaRef,
91 first: Option<Vec<RecordBatch>>,
92 input: &'a mut Input,
93 done: bool,
94}
95
96impl ResultSet<'_> {
97 pub fn next_batch(&mut self) -> Result<Option<RecordBatch>> {
99 loop {
100 if let Some(first) = self.first.as_mut() {
101 if !first.is_empty() {
102 return Ok(Some(first.remove(0)));
103 }
104 self.first = None;
105 }
106 if self.done {
107 return Ok(None);
108 }
109 match self.input.read()? {
110 Frame::Arrow(ipc) => {
111 let (_, batches) = frame::decode_batches(&ipc)?;
112 self.first = Some(batches);
113 }
114 Frame::Json(v) => match serde_json::from_value::<Request>(v)? {
115 Request::ResultSetEnd {} => {
116 self.done = true;
117 return Ok(None);
118 }
119 other => return Err(format!("expected result data, got {other:?}").into()),
120 },
121 }
122 }
123 }
124
125 fn drain(&mut self) -> Result<()> {
126 while self.next_batch()?.is_some() {}
127 Ok(())
128 }
129}
130
131pub struct WriteRequest {
133 pub path: String,
134 pub format: String,
135 pub options: Map<String, Value>,
136 pub result_sets: Vec<ResultSetMeta>,
137 pub template: Option<Value>,
138}
139
140pub struct ResultSets<'a> {
142 metas: std::vec::IntoIter<ResultSetMeta>,
143 input: &'a mut Input,
144 warnings: Vec<String>,
145}
146
147impl ResultSets<'_> {
148 pub fn warn(&mut self, message: impl Into<String>) {
150 self.warnings.push(message.into());
151 }
152
153 pub fn next_set(&mut self) -> Result<Option<ResultSet<'_>>> {
156 let Some(meta) = self.metas.next() else {
157 return Ok(None);
158 };
159 let ipc = match self.input.read()? {
160 Frame::Arrow(ipc) => ipc,
161 Frame::Json(v) => {
162 return Err(format!(
163 "expected an Arrow frame starting result set `{}`, got {v}",
164 meta.name
165 )
166 .into());
167 }
168 };
169 let (schema, batches) = frame::decode_batches(&ipc)?;
170 Ok(Some(ResultSet {
171 meta,
172 schema,
173 first: Some(batches),
174 input: &mut *self.input,
175 done: false,
176 }))
177 }
178}
179
180pub trait Format {
181 fn options(&self) -> Vec<OptionField> {
184 Vec::new()
185 }
186 fn validate(&self, _options: &Map<String, Value>) -> Vec<String> {
189 Vec::new()
190 }
191 fn write(&mut self, req: &WriteRequest, sets: &mut ResultSets<'_>) -> Result<Vec<String>>;
194}
195
196pub struct Delivery {
198 pub files: Vec<DeliveryFile>,
200 pub connection: Map<String, Value>,
201 pub options: Map<String, Value>,
203}
204
205pub struct DeliveryFile {
207 pub local: PathBuf,
208 pub remote: Option<String>,
209}
210
211pub trait Destination {
212 fn connection_fields(&self) -> Vec<ConnectionField> {
213 Vec::new()
214 }
215 fn options(&self) -> Vec<OptionField> {
219 Vec::new()
220 }
221 fn validate(&self, _options: &Map<String, Value>) -> Vec<String> {
224 Vec::new()
225 }
226 fn deliver(
229 &mut self,
230 _local: &Path,
231 _remote: Option<&str>,
232 _connection: &Map<String, Value>,
233 ) -> Result<String> {
234 Err("this destination doesn't implement `deliver`".into())
235 }
236 fn deliver_files(&mut self, d: &Delivery) -> Result<String> {
239 match d.files.as_slice() {
240 [f] => self.deliver(&f.local, f.remote.as_deref(), &d.connection),
241 _ => Err("this destination takes one file per delivery".into()),
242 }
243 }
244}
245
246pub struct Input {
247 r: BufReader<Box<dyn Read>>,
248}
249
250impl Input {
251 fn read(&mut self) -> Result<Frame> {
252 frame::read_frame(&mut self.r).map_err(|e| -> Error {
253 match e {
254 FrameError::Eof => "core closed the connection".into(),
255 e => e.to_string().into(),
256 }
257 })
258 }
259}
260
261struct Output {
262 w: BufWriter<Stdout>,
263}
264
265impl Output {
266 fn send(&mut self, r: &Response) {
267 if frame::write_json(&mut self.w, r).is_err() {
268 std::process::exit(1);
270 }
271 }
272
273 fn batch(&mut self, b: &RecordBatch) -> Result<()> {
274 let ipc = frame::encode_batch(b)?;
275 frame::write_arrow(&mut self.w, &ipc).map_err(|_| -> Error { "core closed the connection".into() })
276 }
277}
278
279enum Handler<'a> {
280 Source(&'a mut dyn Source),
281 Format(&'a mut dyn Format),
282 Destination(&'a mut dyn Destination),
283}
284
285impl Handler<'_> {
286 fn kind(&self) -> Kind {
287 match self {
288 Handler::Source(_) => Kind::Source,
289 Handler::Format(_) => Kind::Format,
290 Handler::Destination(_) => Kind::Destination,
291 }
292 }
293
294 fn option_fields(&self) -> Vec<OptionField> {
295 match self {
296 Handler::Source(_) => Vec::new(),
297 Handler::Format(f) => f.options(),
298 Handler::Destination(d) => d.options(),
299 }
300 }
301
302 fn check(&self, name: &str, o: &Map<String, Value>) -> Vec<String> {
305 let errs = options::check(self.kind(), name, &self.option_fields(), o);
306 if !errs.is_empty() {
307 return errs;
308 }
309 match self {
310 Handler::Source(_) => Vec::new(),
311 Handler::Format(f) => f.validate(o),
312 Handler::Destination(d) => d.validate(o),
313 }
314 }
315
316 fn checked(&self, name: &str, o: &Map<String, Value>) -> Result<()> {
318 let errs = self.check(name, o);
319 if errs.is_empty() {
320 Ok(())
321 } else {
322 Err(errs.join("; ").into())
323 }
324 }
325}
326
327pub fn serve_source(about: About, s: impl Source + 'static) -> ! {
328 serve_package(vec![Plugin::Source(about, Box::new(s))])
329}
330
331pub fn serve_format(about: About, f: impl Format + 'static) -> ! {
332 serve_package(vec![Plugin::Format(about, Box::new(f))])
333}
334
335pub fn serve_destination(about: About, d: impl Destination + 'static) -> ! {
336 serve_package(vec![Plugin::Destination(about, Box::new(d))])
337}
338
339pub enum Plugin {
341 Source(About, Box<dyn Source>),
342 Format(About, Box<dyn Format>),
343 Destination(About, Box<dyn Destination>),
344}
345
346impl Plugin {
347 fn id(&self) -> PluginId {
348 let (kind, about) = match self {
349 Plugin::Source(a, _) => (Kind::Source, a),
350 Plugin::Format(a, _) => (Kind::Format, a),
351 Plugin::Destination(a, _) => (Kind::Destination, a),
352 };
353 PluginId::new(kind, about.name)
354 }
355
356 fn handler(&mut self) -> (&About, Handler<'_>) {
357 match self {
358 Plugin::Source(a, s) => (a, Handler::Source(s.as_mut())),
359 Plugin::Format(a, f) => (a, Handler::Format(f.as_mut())),
360 Plugin::Destination(a, d) => (a, Handler::Destination(d.as_mut())),
361 }
362 }
363}
364
365pub fn serve_package(mut plugins: Vec<Plugin>) -> ! {
368 assert!(!plugins.is_empty(), "a package serves at least one plugin");
369 let mut input = Input {
370 r: BufReader::new(Box::new(std::io::stdin())),
371 };
372 let mut out = Output {
373 w: BufWriter::new(std::io::stdout()),
374 };
375 let provides: Vec<PluginId> = plugins.iter().map(Plugin::id).collect();
376 let chosen = loop {
377 let req = match next_request(&mut input, &mut out) {
378 Some(r) => r,
379 None => continue,
380 };
381 let Request::Hello {
382 min_version,
383 max_version,
384 plugin,
385 ..
386 } = req
387 else {
388 out.send(&Response::Error {
389 message: "the first request must be `hello`".into(),
390 });
391 continue;
392 };
393 let Some(chosen) = negotiate((min_version, max_version), (MIN_VERSION, MAX_VERSION)) else {
394 out.send(&Response::VersionMismatch {
395 min_version: MIN_VERSION,
396 max_version: MAX_VERSION,
397 });
398 std::process::exit(1);
399 };
400 let i = match &plugin {
401 None => 0,
402 Some(want) => match provides.iter().position(|p| p == want) {
403 Some(i) => i,
404 None => {
405 let list: Vec<String> = provides.iter().map(|p| p.to_string()).collect();
406 out.send(&Response::Error {
407 message: format!("this executable provides {}, not {want}", list.join(", ")),
408 });
409 std::process::exit(1);
410 }
411 },
412 };
413 let (about, h) = plugins[i].handler();
414 out.send(&Response::Hello {
415 protocol_version: chosen,
416 kind: h.kind(),
417 name: about.name.to_string(),
418 version: about.version.to_string(),
419 capabilities: about
420 .capabilities
421 .iter()
422 .chain(std::iter::once(&CAP_VALIDATE))
423 .map(|c| c.to_string())
424 .collect(),
425 provides: if provides.len() > 1 {
426 provides.clone()
427 } else {
428 Vec::new()
429 },
430 });
431 break i;
432 };
433 let (about, h) = plugins[chosen].handler();
434 serve(about.name, h, input, out)
435}
436
437fn next_request(input: &mut Input, out: &mut Output) -> Option<Request> {
439 let frame = match frame::read_frame(&mut input.r) {
440 Ok(f) => f,
441 Err(FrameError::Eof) => std::process::exit(0),
442 Err(e) => {
443 eprintln!("{e}");
444 out.send(&Response::Error {
445 message: e.to_string(),
446 });
447 std::process::exit(2);
448 }
449 };
450 match frame {
451 Frame::Json(v) => match serde_json::from_value::<Request>(v.clone()) {
452 Ok(r) => Some(r),
453 Err(_) => {
454 let t = v.get("type").and_then(Value::as_str).unwrap_or("?").to_string();
455 out.send(&Response::Error {
456 message: format!("unsupported request `{t}`"),
457 });
458 None
459 }
460 },
461 Frame::Arrow(_) => {
462 out.send(&Response::Error {
463 message: "unexpected Arrow frame".into(),
464 });
465 None
466 }
467 }
468}
469
470fn serve(name: &str, mut h: Handler<'_>, mut input: Input, mut out: Output) -> ! {
472 loop {
473 let frame = match frame::read_frame(&mut input.r) {
474 Ok(f) => f,
475 Err(FrameError::Eof) => {
476 if let Handler::Source(src) = &mut h {
477 src.close();
478 }
479 std::process::exit(0)
480 }
481 Err(e) => {
482 eprintln!("{e}");
483 out.send(&Response::Error {
484 message: e.to_string(),
485 });
486 std::process::exit(2);
487 }
488 };
489 let req = match frame {
490 Frame::Json(v) => match serde_json::from_value::<Request>(v.clone()) {
491 Ok(r) => r,
492 Err(_) => {
493 let t = v.get("type").and_then(Value::as_str).unwrap_or("?").to_string();
494 out.send(&Response::Error {
495 message: format!("unsupported request `{t}`"),
496 });
497 continue;
498 }
499 },
500 Frame::Arrow(_) => {
501 out.send(&Response::Error {
502 message: "unexpected Arrow frame".into(),
503 });
504 continue;
505 }
506 };
507 if let Request::Hello { .. } = req {
508 out.send(&Response::Error {
509 message: "`hello` was already answered".into(),
510 });
511 continue;
512 }
513 if let Request::Close {} = req {
514 if let Handler::Source(src) = &mut h {
515 src.close();
516 }
517 out.send(&Response::Ok {});
518 std::process::exit(0);
519 }
520 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
521 handle(&mut h, name, req, &mut input, &mut out)
522 }));
523 match result {
524 Ok(Ok(())) => {}
525 Ok(Err(e)) => out.send(&Response::Error {
526 message: e.to_string(),
527 }),
528 Err(p) => {
529 let msg = p
530 .downcast_ref::<String>()
531 .cloned()
532 .or_else(|| p.downcast_ref::<&str>().map(|s| s.to_string()))
533 .unwrap_or_else(|| "unknown panic".into());
534 out.send(&Response::Error {
535 message: format!("plugin panicked: {msg}"),
536 });
537 std::process::exit(101);
538 }
539 }
540 }
541}
542
543fn handle(h: &mut Handler<'_>, name: &str, req: Request, input: &mut Input, out: &mut Output) -> Result<()> {
544 if let Request::Describe {} = req {
545 let connection_fields = match h {
546 Handler::Source(s) => s.connection_fields(),
547 Handler::Destination(d) => d.connection_fields(),
548 Handler::Format(_) => Vec::new(),
549 };
550 out.send(&Response::Describe {
551 connection_fields,
552 option_fields: h.option_fields(),
553 });
554 return Ok(());
555 }
556 if let Request::Validate { options } = req {
557 out.send(&Response::Validated {
558 errors: h.check(name, &options),
559 });
560 return Ok(());
561 }
562 let checked = match &req {
563 Request::Write { options, .. } | Request::Deliver { options, .. } => h.checked(name, options),
564 _ => Ok(()),
565 };
566 match (h, req) {
567 (
568 Handler::Source(s),
569 Request::Open {
570 connection,
571 read_only,
572 },
573 ) => {
574 s.open(&connection, read_only)?;
575 out.send(&Response::Ok {});
576 }
577 (Handler::Source(s), Request::Load { name }) => {
578 let ipc = match input.read()? {
579 Frame::Arrow(ipc) => ipc,
580 Frame::Json(v) => return Err(format!("expected the rows to load, got {v}").into()),
581 };
582 let (schema, batches) = frame::decode_batches(&ipc)?;
583 let mut data = ResultSet {
584 meta: ResultSetMeta {
585 name: name.clone(),
586 query: name.clone(),
587 result_index: 1,
588 anchor: None,
589 header: None,
590 columns: Default::default(),
591 },
592 schema,
593 first: Some(batches),
594 input,
595 done: false,
596 };
597 let r = s.load(&name, &mut data);
598 data.drain()?;
599 let loaded = r?;
600 out.send(&Response::Loaded {
601 relation: loaded.relation,
602 rows: loaded.rows,
603 warning: loaded.warning,
604 });
605 }
606 (Handler::Source(s), Request::Check { sql }) => {
607 s.check(&sql)?;
608 out.send(&Response::Ok {});
609 }
610 (Handler::Source(s), Request::Execute { sql, row_limit }) => {
611 let mut sink = FrameSink {
612 out,
613 row_limit,
614 rows: 0,
615 state: SinkState::Idle,
616 schema: None,
617 sent_any: false,
618 };
619 let r = s.execute(&sql, row_limit, &mut sink);
620 match (r, sink.state) {
621 (Err(e), _) => return Err(e),
622 (Ok(()), SinkState::Idle) => {
623 return Err("the source produced neither a result nor `no_result`".into());
624 }
625 (Ok(()), SinkState::NoResult) => {}
626 (Ok(()), SinkState::Result) => {
627 if !sink.sent_any {
628 let schema = sink.schema.clone().unwrap();
629 sink.out.batch(&RecordBatch::new_empty(schema))?;
630 }
631 let rows = sink.rows;
632 sink.out.send(&Response::ResultEnd { rows });
633 }
634 }
635 }
636 (
637 Handler::Format(f),
638 Request::Write {
639 path,
640 format,
641 options,
642 result_sets,
643 template,
644 },
645 ) => {
646 let req = WriteRequest {
647 path,
648 format,
649 options,
650 result_sets: result_sets.clone(),
651 template,
652 };
653 let mut sets = ResultSets {
654 metas: result_sets.into_iter(),
655 input,
656 warnings: Vec::new(),
657 };
658 let written = checked.and_then(|()| f.write(&req, &mut sets));
659 if let Err(e) = &written {
662 out.send(&Response::Error {
663 message: e.to_string(),
664 });
665 }
666 loop {
669 match sets.input.read()? {
670 Frame::Json(v) if v.get("type").and_then(Value::as_str) == Some("finish") => break,
671 Frame::Json(v) if v.get("type").and_then(Value::as_str) == Some("result_set_end") => {}
672 Frame::Arrow(_) => {}
673 other => return Err(format!("expected `finish`, got {other:?}").into()),
674 }
675 }
676 if let Ok(files) = written {
677 out.send(&Response::Written {
678 files,
679 warnings: sets.warnings,
680 });
681 }
682 }
683 (
684 Handler::Destination(d),
685 Request::Deliver {
686 local_path,
687 remote_path,
688 files,
689 connection,
690 options,
691 },
692 ) => {
693 checked?;
694 let files = match (local_path, files.is_empty()) {
695 (Some(local), true) => vec![DeliveryFile {
696 local: PathBuf::from(local),
697 remote: remote_path,
698 }],
699 (None, false) => files
700 .into_iter()
701 .map(|f| DeliveryFile {
702 local: PathBuf::from(f.local_path),
703 remote: f.remote_path,
704 })
705 .collect(),
706 _ => return Err("`deliver` needs exactly one of `local_path` or `files`".into()),
707 };
708 let location = d.deliver_files(&Delivery {
709 files,
710 connection,
711 options,
712 })?;
713 out.send(&Response::Delivered { location });
714 }
715 (h, req) => {
716 let t = serde_json::to_value(&req)
717 .ok()
718 .and_then(|v| v.get("type").cloned())
719 .unwrap_or_default();
720 return Err(format!("a {} plugin doesn't handle {t} requests", h.kind()).into());
721 }
722 }
723 Ok(())
724}
725
726#[derive(PartialEq)]
727enum SinkState {
728 Idle,
729 NoResult,
730 Result,
731}
732
733struct FrameSink<'a> {
734 out: &'a mut Output,
735 row_limit: Option<u64>,
736 rows: u64,
737 state: SinkState,
738 schema: Option<SchemaRef>,
739 sent_any: bool,
740}
741
742impl ResultSink for FrameSink<'_> {
743 fn no_result(&mut self, rows_affected: Option<u64>) -> Result<()> {
744 if self.state != SinkState::Idle {
745 return Err("`no_result` after the result started".into());
746 }
747 self.state = SinkState::NoResult;
748 self.out.send(&Response::NoResult { rows_affected });
749 Ok(())
750 }
751
752 fn begin(&mut self, schema: SchemaRef) -> Result<()> {
753 if self.state != SinkState::Idle {
754 return Err("a statement can only produce one result".into());
755 }
756 self.state = SinkState::Result;
757 self.out.send(&Response::Result {
758 columns: schema.fields().iter().map(|f| f.name().clone()).collect(),
759 });
760 self.schema = Some(schema);
761 Ok(())
762 }
763
764 fn batch(&mut self, b: RecordBatch) -> Result<bool> {
765 if self.state != SinkState::Result {
766 return Err("`batch` before `begin`".into());
767 }
768 if self.row_limit.is_some_and(|l| self.rows >= l) {
769 return Ok(false);
770 }
771 let b = match self.row_limit {
772 Some(l) if self.rows + b.num_rows() as u64 > l => b.slice(0, (l - self.rows) as usize),
773 _ => b,
774 };
775 if b.num_rows() > 0 || !self.sent_any {
776 self.rows += b.num_rows() as u64;
777 self.out.batch(&b)?;
778 self.sent_any = true;
779 }
780 Ok(self.row_limit.is_none_or(|l| self.rows < l))
781 }
782}
783
784fn negotiate(core: (u32, u32), ours: (u32, u32)) -> Option<u32> {
786 let (lo, hi) = (core.0.max(ours.0), core.1.min(ours.1));
787 (lo <= hi).then_some(hi)
788}
789
790pub fn conn_str<'a>(c: &'a Map<String, Value>, key: &str) -> Option<&'a str> {
792 c.get(key).and_then(Value::as_str).filter(|s| !s.is_empty())
793}
794
795pub fn conn_required<'a>(c: &'a Map<String, Value>, key: &str) -> Result<&'a str> {
797 conn_str(c, key).ok_or_else(|| format!("the profile output needs a `{key}` field").into())
798}
799
800pub fn conn_bool(c: &Map<String, Value>, key: &str) -> Option<bool> {
802 match c.get(key)? {
803 Value::Bool(b) => Some(*b),
804 Value::String(s) => Some(matches!(s.as_str(), "true" | "yes" | "1")),
805 _ => None,
806 }
807}