1use std::collections::VecDeque;
9use std::io::{BufRead, BufReader, BufWriter, Write};
10use std::path::{Path, PathBuf};
11use std::process::{Child, ChildStdin, Command, ExitStatus, Stdio};
12use std::sync::atomic::{AtomicU64, Ordering};
13use std::sync::mpsc::{Receiver, RecvTimeoutError, channel};
14use std::sync::{Arc, Mutex, OnceLock};
15use std::time::Duration;
16
17use arrow::array::RecordBatch;
18use arrow::datatypes::SchemaRef;
19use serde_json::{Map, Value};
20
21use crate::frame::{self, Frame, FrameError};
22use crate::msg::{ConnectionField, DeliveryFile, Envelope, LogLevel, Request, Response, ResultSetMeta};
23use crate::options::OptionField;
24use crate::{
25 CORE_MIN_VERSION, Kind, MAX_VERSION, PluginId, parse_executable_name, parse_package_executable_name,
26};
27
28pub type LogSink = Arc<dyn Fn(&str, &str) + Send + Sync>;
32
33pub fn stderr_log() -> LogSink {
35 Arc::new(|plugin, line| eprintln!("[{plugin}] {line}"))
36}
37
38pub const DEFAULT_HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(30);
39
40pub const INTERACTIVE_ENV: &str = "DRE_INTERACTIVE";
42const STDERR_TAIL: usize = 20;
43
44#[derive(Debug, Clone, PartialEq)]
45pub struct PluginInfo {
46 pub protocol_version: u32,
47 pub kind: Kind,
48 pub name: String,
49 pub version: String,
50 pub capabilities: Vec<String>,
51 pub provides: Vec<PluginId>,
53}
54
55#[derive(Debug, Clone, PartialEq, Default)]
57pub struct Description {
58 pub connection_fields: Vec<ConnectionField>,
59 pub option_fields: Vec<OptionField>,
60 pub identifier_quote: Option<String>,
62 pub string_literal: Option<String>,
64 pub message_limit: Option<u64>,
66}
67
68#[derive(Debug)]
69pub enum HostError {
70 Spawn {
71 plugin: String,
72 error: std::io::Error,
73 },
74 Crashed {
75 plugin: String,
76 status: Option<ExitStatus>,
77 stderr: Vec<String>,
78 },
79 Malformed {
80 plugin: String,
81 message: String,
82 },
83 Incompatible {
84 plugin: String,
85 core: (u32, u32),
86 plugin_range: (u32, u32),
87 },
88 Timeout {
89 plugin: String,
90 waiting_for: &'static str,
91 },
92 Unexpected {
93 plugin: String,
94 expected: &'static str,
95 got: String,
96 },
97 Plugin {
99 plugin: String,
100 message: String,
101 kind: Option<String>,
102 code: Option<String>,
103 },
104 Arrow {
105 plugin: String,
106 message: String,
107 },
108}
109
110impl std::fmt::Display for HostError {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 match self {
113 HostError::Spawn { plugin, error } => write!(f, "can't start plugin `{plugin}`: {error}"),
114 HostError::Crashed {
115 plugin,
116 status,
117 stderr,
118 } => {
119 match status {
120 Some(s) => write!(f, "plugin `{plugin}` exited unexpectedly ({s})")?,
121 None => write!(f, "plugin `{plugin}` closed its output unexpectedly")?,
122 }
123 if !stderr.is_empty() {
124 write!(f, "; its last log lines:\n {}", stderr.join("\n "))?;
125 }
126 Ok(())
127 }
128 HostError::Malformed { plugin, message } => {
129 write!(f, "plugin `{plugin}` broke the protocol: {message}")
130 }
131 HostError::Incompatible {
132 plugin,
133 core,
134 plugin_range,
135 } => {
136 write!(
137 f,
138 "plugin `{plugin}` speaks protocol versions {}..={}, but this DRE core speaks {}..={}; ",
139 plugin_range.0, plugin_range.1, core.0, core.1,
140 )?;
141 if plugin_range.1 >= core.0 {
142 return f.write_str("update DRE");
143 }
144 let package = parse_package_executable_name(plugin)
145 .or_else(|| parse_executable_name(plugin).map(|(_, n)| n));
146 match package {
147 Some(p) => write!(f, "update the plugin with `dre plugin update {p}`"),
148 None => f.write_str("update the plugin"),
149 }
150 }
151 HostError::Timeout { plugin, waiting_for } => {
152 write!(
153 f,
154 "plugin `{plugin}` didn't answer in time (waiting for {waiting_for})"
155 )
156 }
157 HostError::Unexpected {
158 plugin,
159 expected,
160 got,
161 } => {
162 write!(f, "plugin `{plugin}` sent {got} where {expected} was expected")
163 }
164 HostError::Plugin { message, .. } => f.write_str(message),
165 HostError::Arrow { plugin, message } => {
166 write!(f, "plugin `{plugin}` sent invalid Arrow data: {message}")
167 }
168 }
169 }
170}
171
172impl std::error::Error for HostError {}
173
174impl HostError {
175 fn plugin(plugin: &str, message: impl Into<String>) -> HostError {
176 HostError::Plugin {
177 plugin: plugin.to_string(),
178 message: message.into(),
179 kind: None,
180 code: None,
181 }
182 }
183}
184
185pub type Result<T> = std::result::Result<T, HostError>;
186
187#[derive(Clone)]
190pub struct Canceller {
191 stdin: Arc<Mutex<Option<BufWriter<ChildStdin>>>>,
192 current: Arc<AtomicU64>,
193 child: Arc<Mutex<Child>>,
194}
195
196impl Canceller {
197 pub fn cancel(&self) -> bool {
199 let id = self.current.load(Ordering::SeqCst);
200 if id == 0 {
201 return false;
202 }
203 let mut stdin = self.stdin.lock().unwrap();
204 let Some(w) = stdin.as_mut() else {
205 return false;
206 };
207 frame::write_json(w, &Envelope::new(Some(id), Request::Cancel {})).is_ok()
208 }
209
210 pub fn kill(&self) {
212 if let Ok(mut c) = self.child.lock() {
213 let _ = c.kill();
214 }
215 }
216}
217
218static RUNNING: Mutex<Vec<(u64, Canceller)>> = Mutex::new(Vec::new());
221static NEXT_PROCESS: AtomicU64 = AtomicU64::new(1);
222
223pub fn cancel_all() -> usize {
225 let all: Vec<Canceller> = RUNNING.lock().unwrap().iter().map(|(_, c)| c.clone()).collect();
226 all.iter().filter(|c| c.cancel()).count()
227}
228
229pub fn kill_all() {
231 let all: Vec<Canceller> = RUNNING.lock().unwrap().iter().map(|(_, c)| c.clone()).collect();
232 for c in all {
233 c.kill();
234 }
235}
236
237pub fn running() -> usize {
239 RUNNING.lock().unwrap().len()
240}
241pub enum Incoming {
243 Json(Response),
244 Arrow(Vec<u8>),
245}
246
247impl std::fmt::Debug for Incoming {
248 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249 match self {
250 Incoming::Json(r) => write!(f, "Json({r:?})"),
251 Incoming::Arrow(b) => write!(f, "Arrow frame ({} bytes)", b.len()),
252 }
253 }
254}
255
256#[derive(Debug)]
258pub enum Execution {
259 NoResult { rows_affected: Option<u64> },
260 Result { schema: SchemaRef, rows: u64 },
261}
262
263pub struct PluginProcess {
264 label: String,
265 path: PathBuf,
266 child: Arc<Mutex<Child>>,
268 registered: u64,
270 stdin: Arc<Mutex<Option<BufWriter<ChildStdin>>>>,
272 rx: Receiver<std::result::Result<Frame, FrameError>>,
273 log: LogSink,
274 last_id: u64,
276 current: Arc<AtomicU64>,
278 stderr: Arc<Mutex<VecDeque<String>>>,
279 stderr_thread: Option<std::thread::JoinHandle<()>>,
281 info: Option<PluginInfo>,
282 early: Option<std::result::Result<Frame, FrameError>>,
284 last_attempts: u32,
286 serve: Option<PluginId>,
289}
290
291impl PluginProcess {
292 pub fn start(path: &Path, log: LogSink) -> Result<PluginProcess> {
294 Self::start_in(path, log, None)
295 }
296
297 pub fn start_in(path: &Path, log: LogSink, cwd: Option<&Path>) -> Result<PluginProcess> {
299 Self::start_for(path, None, log, cwd)
300 }
301
302 pub fn start_for(
304 path: &Path,
305 plugin: Option<&PluginId>,
306 log: LogSink,
307 cwd: Option<&Path>,
308 ) -> Result<PluginProcess> {
309 let timeout = std::env::var("DRE_PLUGIN_HANDSHAKE_TIMEOUT_MS")
310 .ok()
311 .and_then(|v| v.parse().ok())
312 .map(Duration::from_millis)
313 .unwrap_or(DEFAULT_HANDSHAKE_TIMEOUT);
314 let mut p = Self::spawn_in(path, log, &[], cwd)?;
315 if let Some(id) = plugin {
316 p.serve = Some(id.clone());
317 }
318 p.handshake((CORE_MIN_VERSION, MAX_VERSION), timeout)?;
319 Ok(p)
320 }
321
322 pub fn start_with(
324 path: &Path,
325 log: LogSink,
326 versions: (u32, u32),
327 timeout: Duration,
328 ) -> Result<PluginProcess> {
329 let mut p = Self::spawn(path, log)?;
330 p.handshake(versions, timeout)?;
331 Ok(p)
332 }
333
334 pub fn spawn(path: &Path, log: LogSink) -> Result<PluginProcess> {
336 Self::spawn_env(path, log, &[])
337 }
338
339 pub fn spawn_env(path: &Path, log: LogSink, env: &[(&str, &str)]) -> Result<PluginProcess> {
341 Self::spawn_in(path, log, env, None)
342 }
343
344 pub fn spawn_in(
346 path: &Path,
347 log: LogSink,
348 env: &[(&str, &str)],
349 cwd: Option<&Path>,
350 ) -> Result<PluginProcess> {
351 let label = path
352 .file_name()
353 .map(|f| f.to_string_lossy().to_string())
354 .unwrap_or_default();
355 let mut cmd = Command::new(path);
356 if let Some(dir) = cwd {
357 cmd.current_dir(dir);
358 }
359 if std::env::var_os(INTERACTIVE_ENV).is_none() {
362 use std::io::IsTerminal;
363 let there = std::io::stderr().is_terminal() && std::io::stdin().is_terminal();
364 cmd.env(INTERACTIVE_ENV, if there { "1" } else { "0" });
365 }
366 #[cfg(unix)]
369 {
370 use std::os::unix::process::CommandExt;
371 cmd.process_group(0);
372 }
373 #[cfg(windows)]
374 {
375 use std::os::windows::process::CommandExt;
376 const CREATE_NEW_PROCESS_GROUP: u32 = 0x0000_0200;
377 cmd.creation_flags(CREATE_NEW_PROCESS_GROUP);
378 }
379 let mut child = cmd
380 .envs(env.iter().copied())
381 .stdin(Stdio::piped())
382 .stdout(Stdio::piped())
383 .stderr(Stdio::piped())
384 .spawn()
385 .map_err(|error| HostError::Spawn {
386 plugin: label.clone(),
387 error,
388 })?;
389 let stdout = child.stdout.take().unwrap();
390 let stderr_pipe = child.stderr.take().unwrap();
391 let stdin = Arc::new(Mutex::new(child.stdin.take().map(BufWriter::new)));
392
393 let (tx, rx) = channel();
394 std::thread::spawn(move || {
395 let mut r = BufReader::new(stdout);
396 loop {
397 let f = frame::read_frame(&mut r);
398 let stop = f.is_err();
399 if tx.send(f).is_err() || stop {
400 break;
401 }
402 }
403 });
404 let stderr = Arc::new(Mutex::new(VecDeque::new()));
405 let tail = stderr.clone();
406 let who = label.clone();
407 let sink = log.clone();
408 let stderr_thread = std::thread::spawn(move || {
409 for line in BufReader::new(stderr_pipe).lines() {
410 let Ok(line) = line else { break };
411 sink(&who, &line);
412 let mut t = tail.lock().unwrap();
413 if t.len() == STDERR_TAIL {
414 t.pop_front();
415 }
416 t.push_back(line);
417 }
418 });
419 let serve = parse_executable_name(&label).map(|(k, n)| PluginId::new(k, n));
420 let child = Arc::new(Mutex::new(child));
421 let current = Arc::new(AtomicU64::new(0));
422 let registered = NEXT_PROCESS.fetch_add(1, Ordering::SeqCst);
423 RUNNING.lock().unwrap().push((
424 registered,
425 Canceller {
426 stdin: stdin.clone(),
427 current: current.clone(),
428 child: child.clone(),
429 },
430 ));
431 Ok(PluginProcess {
432 label,
433 path: path.to_path_buf(),
434 child,
435 registered,
436 stdin,
437 rx,
438 log,
439 last_id: 0,
440 current,
441 stderr,
442 stderr_thread: Some(stderr_thread),
443 info: None,
444 early: None,
445 last_attempts: 1,
446 serve,
447 })
448 }
449
450 pub fn ask_for(&mut self, plugin: Option<PluginId>) {
452 self.serve = plugin;
453 }
454
455 pub fn handshake(&mut self, (min, max): (u32, u32), timeout: Duration) -> Result<()> {
457 self.send(&Request::Hello {
458 min_version: min,
459 max_version: max,
460 core_version: core_version(),
461 plugin: self.serve.clone(),
462 })?;
463 match self.recv(Some(timeout), "the hello reply")? {
464 Incoming::Json(Response::Hello {
465 protocol_version,
466 kind,
467 name,
468 version,
469 capabilities,
470 mut provides,
471 }) => {
472 if protocol_version < min || protocol_version > max {
473 return Err(HostError::Incompatible {
474 plugin: self.label.clone(),
475 core: (min, max),
476 plugin_range: (protocol_version, protocol_version),
477 });
478 }
479 if let Some(want) = &self.serve
480 && (want.kind != kind || want.name != name)
481 {
482 return Err(HostError::plugin(
483 &self.label,
484 format!(
485 "`{}` was asked for the {want} plugin but serves {kind}/{name}",
486 self.label
487 ),
488 ));
489 }
490 if provides.is_empty() {
491 provides.push(PluginId::new(kind, name.clone()));
492 }
493 self.info = Some(PluginInfo {
494 protocol_version,
495 kind,
496 name,
497 version,
498 capabilities,
499 provides,
500 });
501 Ok(())
502 }
503 Incoming::Json(Response::VersionMismatch {
504 min_version,
505 max_version,
506 }) => Err(HostError::Incompatible {
507 plugin: self.label.clone(),
508 core: (min, max),
509 plugin_range: (min_version, max_version),
510 }),
511 Incoming::Json(Response::Error { message, kind, code }) => Err(HostError::Plugin {
512 plugin: self.label.clone(),
513 message,
514 kind,
515 code,
516 }),
517 other => Err(self.unexpected("a hello reply", &other)),
518 }
519 }
520
521 pub fn label(&self) -> &str {
522 &self.label
523 }
524
525 pub fn path(&self) -> &Path {
526 &self.path
527 }
528
529 pub fn info(&self) -> &PluginInfo {
530 self.info.as_ref().expect("handshake completed")
531 }
532
533 fn try_wait(&self) -> std::io::Result<Option<ExitStatus>> {
535 self.child.lock().unwrap().try_wait()
536 }
537
538 pub fn canceller(&self) -> Canceller {
541 Canceller {
542 stdin: self.stdin.clone(),
543 current: self.current.clone(),
544 child: self.child.clone(),
545 }
546 }
547
548 fn version(&self) -> u32 {
550 self.info.as_ref().map_or(0, |i| i.protocol_version)
551 }
552
553 pub fn has(&self, capability: &str) -> bool {
554 self.info
555 .as_ref()
556 .is_some_and(|i| i.capabilities.iter().any(|c| c == capability))
557 }
558
559 fn unexpected(&self, expected: &'static str, got: &Incoming) -> HostError {
560 let got = match got {
561 Incoming::Json(r) => format!("{:?}", r)
562 .split([' ', '{'])
563 .next()
564 .unwrap_or("?")
565 .to_string(),
566 Incoming::Arrow(_) => "Arrow data".into(),
567 };
568 HostError::Unexpected {
569 plugin: self.label.clone(),
570 expected,
571 got: format!("`{got}`"),
572 }
573 }
574
575 fn crashed(&mut self) -> HostError {
576 let mut status = None;
578 for _ in 0..50 {
579 if let Ok(Some(s)) = self.try_wait() {
580 status = Some(s);
581 break;
582 }
583 std::thread::sleep(Duration::from_millis(20));
584 }
585 std::thread::sleep(Duration::from_millis(50));
586 let stderr = self.stderr.lock().unwrap().iter().cloned().collect();
587 HostError::Crashed {
588 plugin: self.label.clone(),
589 status,
590 stderr,
591 }
592 }
593
594 pub fn send(&mut self, req: &Request) -> Result<()> {
597 let id = match req {
598 _ if self.version() == 0 => None,
599 Request::Hello { .. } => None,
600 Request::ResultSetEnd {} | Request::Finish {} | Request::Cancel {} => {
601 Some(self.current.load(Ordering::SeqCst)).filter(|id| *id != 0)
602 }
603 _ => {
604 self.last_id += 1;
605 self.current.store(self.last_id, Ordering::SeqCst);
606 Some(self.last_id)
607 }
608 };
609 let ok = match self.stdin.lock().unwrap().as_mut() {
610 Some(w) => frame::write_json(w, &Envelope::new(id, req)).is_ok(),
611 None => false,
612 };
613 if ok { Ok(()) } else { Err(self.write_failed()) }
614 }
615
616 pub fn send_batch(&mut self, batch: &RecordBatch) -> Result<()> {
617 if self.early.is_none()
620 && let Ok(f) = self.rx.try_recv()
621 {
622 self.early = Some(f);
623 }
624 if let Some(Ok(Frame::Json(v))) = &self.early
625 && let Ok(Response::Error { message, kind, code }) = serde_json::from_value::<Response>(v.clone())
626 {
627 return Err(HostError::Plugin {
628 plugin: self.label.clone(),
629 message,
630 kind,
631 code,
632 });
633 }
634 let ipc = frame::encode_batch(batch).map_err(|e| HostError::Arrow {
635 plugin: self.label.clone(),
636 message: e.to_string(),
637 })?;
638 let ok = match self.stdin.lock().unwrap().as_mut() {
639 Some(w) => frame::write_arrow(w, &ipc).is_ok(),
640 None => false,
641 };
642 if ok { Ok(()) } else { Err(self.write_failed()) }
643 }
644
645 fn write_failed(&mut self) -> HostError {
647 match self.rx.recv_timeout(Duration::from_secs(2)) {
648 Ok(Ok(Frame::Json(v))) => match serde_json::from_value::<Response>(v) {
649 Ok(Response::Error { message, kind, code }) => HostError::Plugin {
650 plugin: self.label.clone(),
651 message,
652 kind,
653 code,
654 },
655 _ => self.crashed(),
656 },
657 _ => self.crashed(),
658 }
659 }
660
661 pub fn recv(&mut self, timeout: Option<Duration>, waiting_for: &'static str) -> Result<Incoming> {
664 loop {
665 match self.recv_any(timeout, waiting_for)? {
666 Incoming::Json(Response::Log {
667 level,
668 message,
669 fields,
670 }) => {
671 (self.log)(&self.label, &log_line(level, &message, &fields));
672 }
673 Incoming::Json(Response::Progress { message, done, total }) => {
674 (self.log)(&self.label, &progress_line(message.as_deref(), done, total));
675 }
676 other => return Ok(other),
677 }
678 }
679 }
680
681 pub fn recv_raw(&mut self, timeout: Duration) -> Result<Value> {
683 match self.rx.recv_timeout(timeout) {
684 Ok(Ok(Frame::Json(v))) => Ok(v),
685 Ok(Ok(Frame::Arrow(_))) => Err(HostError::Malformed {
686 plugin: self.label.clone(),
687 message: "Arrow data where a JSON message was expected".into(),
688 }),
689 Ok(Err(FrameError::Malformed(m))) => Err(HostError::Malformed {
690 plugin: self.label.clone(),
691 message: m,
692 }),
693 Err(RecvTimeoutError::Timeout) => Err(HostError::Timeout {
694 plugin: self.label.clone(),
695 waiting_for: "a message",
696 }),
697 _ => Err(self.crashed()),
698 }
699 }
700
701 fn recv_any(&mut self, timeout: Option<Duration>, waiting_for: &'static str) -> Result<Incoming> {
702 let got = match timeout {
703 _ if self.early.is_some() => self.early.take(),
704 Some(t) => match self.rx.recv_timeout(t) {
705 Ok(f) => Some(f),
706 Err(RecvTimeoutError::Timeout) => {
707 return Err(HostError::Timeout {
708 plugin: self.label.clone(),
709 waiting_for,
710 });
711 }
712 Err(RecvTimeoutError::Disconnected) => None,
713 },
714 None => self.rx.recv().ok(),
715 };
716 match got {
717 Some(Ok(Frame::Json(v))) => match serde_json::from_value::<Envelope<Response>>(v.clone()) {
718 Ok(Envelope { id: Some(id), body })
719 if !matches!(body, Response::Log { .. } | Response::Progress { .. }) =>
720 {
721 let current = self.current.load(Ordering::SeqCst);
722 if id != current {
723 return Err(HostError::Malformed {
724 plugin: self.label.clone(),
725 message: format!(
726 "a reply to request {id} arrived while request {current} was running"
727 ),
728 });
729 }
730 Ok(Incoming::Json(body))
731 }
732 Ok(e) => Ok(Incoming::Json(e.body)),
733 Err(e) => Err(HostError::Malformed {
734 plugin: self.label.clone(),
735 message: format!("unknown message {v}: {e}"),
736 }),
737 },
738 Some(Ok(Frame::Arrow(b))) => Ok(Incoming::Arrow(b)),
739 Some(Err(FrameError::Malformed(m))) => Err(HostError::Malformed {
740 plugin: self.label.clone(),
741 message: m,
742 }),
743 Some(Err(_)) | None => Err(self.crashed()),
744 }
745 }
746
747 pub fn recv_json(&mut self, waiting_for: &'static str) -> Result<Response> {
749 match self.recv(None, waiting_for)? {
750 Incoming::Json(Response::Error { message, kind, code }) => Err(HostError::Plugin {
751 plugin: self.label.clone(),
752 message,
753 kind,
754 code,
755 }),
756 Incoming::Json(r) => Ok(r),
757 other => Err(self.unexpected(waiting_for, &other)),
758 }
759 }
760
761 fn call_ok(&mut self, req: &Request, what: &'static str) -> Result<()> {
763 self.send(req)?;
764 match self.recv_json(what)? {
765 Response::Ok {} => Ok(()),
766 other => Err(self.unexpected(what, &Incoming::Json(other))),
767 }
768 }
769
770 pub fn describe(&mut self) -> Result<Vec<ConnectionField>> {
771 Ok(self.describe_all()?.0)
772 }
773
774 pub fn describe_all(&mut self) -> Result<(Vec<ConnectionField>, Vec<OptionField>)> {
776 let d = self.description()?;
777 Ok((d.connection_fields, d.option_fields))
778 }
779
780 pub fn description(&mut self) -> Result<Description> {
782 self.send(&Request::Describe {})?;
783 match self.recv_json("a describe reply")? {
784 Response::Describe {
785 connection_fields,
786 option_fields,
787 identifier_quote,
788 string_literal,
789 message_limit,
790 } => Ok(Description {
791 connection_fields,
792 option_fields,
793 identifier_quote,
794 string_literal,
795 message_limit,
796 }),
797 other => Err(self.unexpected("a describe reply", &Incoming::Json(other))),
798 }
799 }
800
801 pub fn validate_connection(
804 &mut self,
805 mut connection: Map<String, Value>,
806 unresolved: Vec<String>,
807 ) -> Result<(Vec<String>, Vec<String>)> {
808 for k in &unresolved {
809 connection.insert(k.clone(), Value::Null);
810 }
811 self.send(&Request::ValidateConnection {
812 connection,
813 unresolved,
814 })?;
815 match self.recv_json("a validated reply")? {
816 Response::Validated { errors, warnings } => Ok((errors, warnings)),
817 other => Err(self.unexpected("a validated reply", &Incoming::Json(other))),
818 }
819 }
820
821 pub fn validate(&mut self, options: Map<String, Value>) -> Result<Vec<String>> {
823 self.send(&Request::Validate { options })?;
824 match self.recv_json("a validate reply")? {
825 Response::Validated { errors, .. } => Ok(errors),
826 other => Err(self.unexpected("a validate reply", &Incoming::Json(other))),
827 }
828 }
829
830 pub fn open(&mut self, connection: Map<String, Value>, read_only: bool) -> Result<()> {
831 self.call_ok(
832 &Request::Open {
833 connection,
834 read_only,
835 sandbox: false,
836 },
837 "an open reply",
838 )
839 }
840
841 pub fn open_sandboxed(&mut self, connection: Map<String, Value>) -> Result<()> {
844 self.call_ok(
845 &Request::Open {
846 connection,
847 read_only: true,
848 sandbox: true,
849 },
850 "an open reply",
851 )
852 }
853
854 pub fn check(&mut self, sql: &str) -> Result<()> {
855 self.call_ok(&Request::Check { sql: sql.to_string() }, "a check reply")
856 }
857
858 pub fn execute(
860 &mut self,
861 sql: &str,
862 row_limit: Option<u64>,
863 mut on_batch: impl FnMut(&SchemaRef, RecordBatch) -> std::result::Result<(), String>,
864 ) -> Result<Execution> {
865 self.send(&Request::Execute {
866 sql: sql.to_string(),
867 row_limit,
868 })?;
869 match self.recv_json("an execute reply")? {
870 Response::NoResult { rows_affected } => return Ok(Execution::NoResult { rows_affected }),
871 Response::Result { .. } => {}
872 other => return Err(self.unexpected("an execute reply", &Incoming::Json(other))),
873 }
874 let mut schema: Option<SchemaRef> = None;
875 let mut rows = 0u64;
876 let mut sink_error = None;
877 loop {
878 match self.recv(None, "result data")? {
879 Incoming::Arrow(ipc) => {
880 let (s, batches) = frame::decode_batches(&ipc).map_err(|e| HostError::Arrow {
881 plugin: self.label.clone(),
882 message: e.to_string(),
883 })?;
884 let schema = schema.get_or_insert(s);
885 for b in batches {
886 rows += b.num_rows() as u64;
887 if sink_error.is_none()
888 && let Err(e) = on_batch(schema, b)
889 {
890 sink_error = Some(e);
892 }
893 }
894 }
895 Incoming::Json(Response::ResultEnd { .. }) => break,
896 Incoming::Json(Response::Error { message, kind, code }) => {
897 return Err(HostError::Plugin {
898 plugin: self.label.clone(),
899 message,
900 kind,
901 code,
902 });
903 }
904 other => return Err(self.unexpected("result data", &other)),
905 }
906 }
907 if let Some(e) = sink_error {
908 return Err(HostError::plugin(&self.label, e));
909 }
910 let schema = schema.ok_or_else(|| HostError::Malformed {
911 plugin: self.label.clone(),
912 message: "a result ended without any Arrow frame carrying its schema".into(),
913 })?;
914 Ok(Execution::Result { schema, rows })
915 }
916
917 pub fn write_begin(
919 &mut self,
920 path: &str,
921 format: &str,
922 options: Map<String, Value>,
923 result_sets: Vec<ResultSetMeta>,
924 template: Option<Value>,
925 ) -> Result<()> {
926 self.send(&Request::Write {
927 path: path.to_string(),
928 format: format.to_string(),
929 options,
930 result_sets,
931 template,
932 })
933 }
934
935 pub fn write_result_set(
937 &mut self,
938 schema: &SchemaRef,
939 batches: impl IntoIterator<Item = RecordBatch>,
940 ) -> Result<()> {
941 let mut any = false;
942 for b in batches {
943 any = true;
944 self.send_batch(&b)?;
945 }
946 if !any {
947 self.send_batch(&RecordBatch::new_empty(schema.clone()))?;
948 }
949 self.send(&Request::ResultSetEnd {})
950 }
951
952 pub fn load(
955 &mut self,
956 name: &str,
957 schema: &SchemaRef,
958 batches: impl IntoIterator<Item = RecordBatch>,
959 ) -> Result<(String, Option<String>)> {
960 self.send(&Request::Load {
961 name: name.to_string(),
962 })?;
963 self.write_result_set(schema, batches)?;
964 match self.recv_json("a loaded reply")? {
965 Response::Loaded {
966 relation, warning, ..
967 } => Ok((relation, warning)),
968 other => Err(self.unexpected("a loaded reply", &Incoming::Json(other))),
969 }
970 }
971
972 pub fn write_finish(&mut self) -> Result<Vec<String>> {
973 Ok(self.write_finish_with_warnings()?.0)
974 }
975
976 pub fn write_finish_with_warnings(&mut self) -> Result<(Vec<String>, Vec<String>)> {
978 self.send(&Request::Finish {})?;
979 match self.recv_json("a written reply")? {
980 Response::Written { files, warnings } => Ok((files, warnings)),
981 other => Err(self.unexpected("a written reply", &Incoming::Json(other))),
982 }
983 }
984
985 pub fn deliver(
987 &mut self,
988 local_path: &str,
989 remote_path: Option<&str>,
990 connection: Map<String, Value>,
991 ) -> Result<String> {
992 let file = DeliveryFile {
993 local_path: local_path.to_string(),
994 remote_path: remote_path.map(str::to_string),
995 };
996 self.deliver_files(&[file], connection, Map::new())
997 }
998
999 pub fn deliver_files(
1002 &mut self,
1003 files: &[DeliveryFile],
1004 connection: Map<String, Value>,
1005 options: Map<String, Value>,
1006 ) -> Result<String> {
1007 let req = match files {
1008 [] => {
1009 return Err(HostError::Malformed {
1010 plugin: self.label.clone(),
1011 message: "nothing to deliver".into(),
1012 });
1013 }
1014 [one] => Request::Deliver {
1015 local_path: Some(one.local_path.clone()),
1016 remote_path: one.remote_path.clone(),
1017 files: Vec::new(),
1018 connection,
1019 options,
1020 message: None,
1021 },
1022 many => {
1023 if !self.has(crate::CAP_MULTI_FILE) {
1024 return Err(HostError::plugin(
1025 &self.label,
1026 format!(
1027 "{} files in one delivery, but the plugin doesn't advertise `multi_file`",
1028 many.len()
1029 ),
1030 ));
1031 }
1032 Request::Deliver {
1033 local_path: None,
1034 remote_path: None,
1035 files: many.to_vec(),
1036 connection,
1037 options,
1038 message: None,
1039 }
1040 }
1041 };
1042 self.send(&req)?;
1043 match self.recv_json("a delivered reply")? {
1044 Response::Delivered { location, attempts } => {
1045 self.last_attempts = attempts.unwrap_or(1);
1046 Ok(location)
1047 }
1048 other => Err(self.unexpected("a delivered reply", &Incoming::Json(other))),
1049 }
1050 }
1051
1052 pub fn deliver_message(
1054 &mut self,
1055 message: &crate::msg::Message,
1056 attach: &[DeliveryFile],
1057 connection: Map<String, Value>,
1058 options: Map<String, Value>,
1059 ) -> Result<String> {
1060 if !self.has(crate::CAP_MESSAGE) {
1061 return Err(HostError::plugin(
1062 &self.label,
1063 "a message, but the plugin doesn't advertise `message`",
1064 ));
1065 }
1066 self.send(&Request::Deliver {
1067 local_path: None,
1068 remote_path: None,
1069 files: attach.to_vec(),
1070 connection,
1071 options,
1072 message: Some(message.clone()),
1073 })?;
1074 match self.recv_json("a delivered reply")? {
1075 Response::Delivered { location, attempts } => {
1076 self.last_attempts = attempts.unwrap_or(1);
1077 Ok(location)
1078 }
1079 other => Err(self.unexpected("a delivered reply", &Incoming::Json(other))),
1080 }
1081 }
1082
1083 pub fn last_attempts(&self) -> u32 {
1086 self.last_attempts
1087 }
1088
1089 pub fn close(mut self) -> Result<()> {
1091 let _ = self.send(&Request::Close {});
1092 let _ = self.recv(Some(Duration::from_secs(5)), "the close reply");
1093 self.stdin.lock().unwrap().take();
1094 for _ in 0..250 {
1095 if let Ok(Some(_)) = self.try_wait() {
1096 return Ok(());
1097 }
1098 std::thread::sleep(Duration::from_millis(20));
1099 }
1100 let _ = self.child.lock().unwrap().kill();
1101 Ok(())
1102 }
1103
1104 pub fn write_raw(&mut self, bytes: &[u8]) -> Result<()> {
1106 let ok = match self.stdin.lock().unwrap().as_mut() {
1107 Some(w) => w.write_all(bytes).and_then(|_| w.flush()).is_ok(),
1108 None => false,
1109 };
1110 if ok { Ok(()) } else { Err(self.write_failed()) }
1111 }
1112
1113 pub fn wait_exit(&mut self, timeout: Duration) -> Option<ExitStatus> {
1115 self.stdin.lock().unwrap().take();
1116 let deadline = std::time::Instant::now() + timeout;
1117 while std::time::Instant::now() < deadline {
1118 if let Ok(Some(s)) = self.try_wait() {
1119 return Some(s);
1120 }
1121 std::thread::sleep(Duration::from_millis(20));
1122 }
1123 None
1124 }
1125}
1126
1127impl PluginProcess {
1128 fn drain_stderr(&mut self) {
1130 if let Some(t) = self.stderr_thread.take() {
1131 let deadline = std::time::Instant::now() + Duration::from_secs(2);
1132 while !t.is_finished() && std::time::Instant::now() < deadline {
1133 std::thread::sleep(Duration::from_millis(5));
1134 }
1135 if t.is_finished() {
1136 let _ = t.join();
1137 }
1138 }
1139 }
1140}
1141
1142impl Drop for PluginProcess {
1143 fn drop(&mut self) {
1144 if let Ok(mut r) = RUNNING.lock() {
1145 r.retain(|(id, _)| *id != self.registered);
1146 }
1147 if let Ok(mut stdin) = self.stdin.lock() {
1148 stdin.take();
1149 }
1150 if let Ok(None) = self.try_wait() {
1151 for _ in 0..25 {
1153 if let Ok(Some(_)) = self.try_wait() {
1154 break;
1155 }
1156 std::thread::sleep(Duration::from_millis(10));
1157 }
1158 let _ = self.child.lock().unwrap().kill();
1159 let _ = self.child.lock().unwrap().wait();
1160 }
1161 self.drain_stderr();
1162 }
1163}
1164
1165fn log_line(level: LogLevel, message: &str, fields: &Map<String, Value>) -> String {
1167 let prefix = match level {
1168 LogLevel::Error | LogLevel::Warn => "warning: ",
1169 LogLevel::Info => "info: ",
1170 LogLevel::Debug | LogLevel::Trace => "debug: ",
1171 };
1172 let mut line = format!("{prefix}{message}");
1173 for (k, v) in fields {
1174 match v {
1175 Value::String(s) => line.push_str(&format!(" {k}={s}")),
1176 v => line.push_str(&format!(" {k}={v}")),
1177 }
1178 }
1179 line
1180}
1181
1182fn progress_line(message: Option<&str>, done: Option<u64>, total: Option<u64>) -> String {
1184 let count = match (done, total) {
1185 (Some(d), Some(t)) => format!("{d}/{t}"),
1186 (Some(d), None) => d.to_string(),
1187 _ => String::new(),
1188 };
1189 match (message, count.is_empty()) {
1190 (Some(m), true) => format!("info: {m}"),
1191 (Some(m), false) => format!("info: {m} ({count})"),
1192 (None, _) => format!("info: {count}"),
1193 }
1194}
1195
1196static CORE_VERSION: OnceLock<String> = OnceLock::new();
1197
1198pub fn set_core_version(version: &str) {
1201 let _ = CORE_VERSION.set(version.to_string());
1202}
1203
1204fn core_version() -> String {
1205 CORE_VERSION.get().cloned().unwrap_or_else(|| "unreleased".into())
1206}