1use std::collections::VecDeque;
9use std::io::{BufRead, BufReader, BufWriter, Read, Write};
10use std::path::{Path, PathBuf};
11use std::process::{Child, ChildStdin, Command, ExitStatus, Stdio};
12use std::sync::atomic::{AtomicBool, 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
38#[derive(Clone)]
43pub struct StderrSync {
44 progress: Arc<StderrProgress>,
45 pipe: RawPipe,
46}
47
48impl StderrSync {
49 pub fn wait(&self, timeout: Duration) -> bool {
52 let deadline = std::time::Instant::now() + timeout;
53 loop {
54 let p = &self.progress;
55 if p.done.load(Ordering::SeqCst) {
56 return true;
57 }
58 let reads = p.reads.load(Ordering::SeqCst);
61 if p.reading.load(Ordering::SeqCst)
62 && pending(self.pipe) == Some(0)
63 && p.reading.load(Ordering::SeqCst)
64 && p.reads.load(Ordering::SeqCst) == reads
65 {
66 return true;
67 }
68 if std::time::Instant::now() >= deadline {
69 return false;
70 }
71 std::thread::sleep(Duration::from_millis(1));
72 }
73 }
74}
75
76#[derive(Default)]
78struct StderrProgress {
79 reading: AtomicBool,
81 reads: AtomicU64,
83 done: AtomicBool,
85}
86
87struct TrackedPipe<R> {
89 pipe: R,
90 progress: Arc<StderrProgress>,
91}
92
93impl<R: Read> Read for TrackedPipe<R> {
94 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
95 self.progress.reading.store(true, Ordering::SeqCst);
96 let n = self.pipe.read(buf);
97 self.progress.reading.store(false, Ordering::SeqCst);
98 self.progress.reads.fetch_add(1, Ordering::SeqCst);
99 n
100 }
101}
102
103#[cfg(unix)]
104type RawPipe = std::os::fd::RawFd;
105#[cfg(windows)]
106type RawPipe = usize;
107#[cfg(not(any(unix, windows)))]
108type RawPipe = ();
109
110#[cfg(unix)]
111fn raw_pipe(pipe: &std::process::ChildStderr) -> RawPipe {
112 std::os::fd::AsRawFd::as_raw_fd(pipe)
113}
114#[cfg(windows)]
115fn raw_pipe(pipe: &std::process::ChildStderr) -> RawPipe {
116 std::os::windows::io::AsRawHandle::as_raw_handle(pipe) as usize
117}
118#[cfg(not(any(unix, windows)))]
119fn raw_pipe(_: &std::process::ChildStderr) -> RawPipe {}
120
121#[cfg(unix)]
123fn pending(fd: RawPipe) -> Option<usize> {
124 let mut n: libc::c_int = 0;
125 let r = unsafe { libc::ioctl(fd, libc::FIONREAD, &mut n) };
127 (r == 0).then_some(n.max(0) as usize)
128}
129#[cfg(windows)]
130fn pending(handle: RawPipe) -> Option<usize> {
131 let mut n: u32 = 0;
132 let ok = unsafe {
134 windows_sys::Win32::System::Pipes::PeekNamedPipe(
135 handle as windows_sys::Win32::Foundation::HANDLE,
136 std::ptr::null_mut(),
137 0,
138 std::ptr::null_mut(),
139 &mut n,
140 std::ptr::null_mut(),
141 )
142 };
143 (ok != 0).then_some(n as usize)
144}
145#[cfg(not(any(unix, windows)))]
146fn pending(_: RawPipe) -> Option<usize> {
147 None
148}
149
150pub const DEFAULT_HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(30);
151
152pub const INTERACTIVE_ENV: &str = "DRE_INTERACTIVE";
154const STDERR_TAIL: usize = 20;
155
156#[derive(Debug, Clone, PartialEq)]
157pub struct PluginInfo {
158 pub protocol_version: u32,
159 pub kind: Kind,
160 pub name: String,
161 pub version: String,
162 pub capabilities: Vec<String>,
163 pub provides: Vec<PluginId>,
165}
166
167#[derive(Debug, Clone, PartialEq, Default)]
169pub struct Description {
170 pub connection_fields: Vec<ConnectionField>,
171 pub option_fields: Vec<OptionField>,
172 pub identifier_quote: Option<String>,
174 pub string_literal: Option<String>,
176 pub message_limit: Option<u64>,
178}
179
180#[derive(Debug)]
181pub enum HostError {
182 Spawn {
183 plugin: String,
184 error: std::io::Error,
185 },
186 Crashed {
187 plugin: String,
188 status: Option<ExitStatus>,
189 stderr: Vec<String>,
190 },
191 Malformed {
192 plugin: String,
193 message: String,
194 },
195 Incompatible {
196 plugin: String,
197 core: (u32, u32),
198 plugin_range: (u32, u32),
199 },
200 Timeout {
201 plugin: String,
202 waiting_for: &'static str,
203 },
204 Unexpected {
205 plugin: String,
206 expected: &'static str,
207 got: String,
208 },
209 Plugin {
211 plugin: String,
212 message: String,
213 kind: Option<String>,
214 code: Option<String>,
215 },
216 Arrow {
217 plugin: String,
218 message: String,
219 },
220}
221
222impl std::fmt::Display for HostError {
223 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
224 match self {
225 HostError::Spawn { plugin, error } => write!(f, "can't start plugin `{plugin}`: {error}"),
226 HostError::Crashed {
227 plugin,
228 status,
229 stderr,
230 } => {
231 match status {
232 Some(s) => write!(f, "plugin `{plugin}` exited unexpectedly ({s})")?,
233 None => write!(f, "plugin `{plugin}` closed its output unexpectedly")?,
234 }
235 if !stderr.is_empty() {
236 write!(f, "; its last log lines:\n {}", stderr.join("\n "))?;
237 }
238 Ok(())
239 }
240 HostError::Malformed { plugin, message } => {
241 write!(f, "plugin `{plugin}` broke the protocol: {message}")
242 }
243 HostError::Incompatible {
244 plugin,
245 core,
246 plugin_range,
247 } => {
248 write!(
249 f,
250 "plugin `{plugin}` speaks protocol versions {}..={}, but this DRE core speaks {}..={}; ",
251 plugin_range.0, plugin_range.1, core.0, core.1,
252 )?;
253 if plugin_range.1 >= core.0 {
254 return f.write_str("update DRE");
255 }
256 let package = parse_package_executable_name(plugin)
257 .or_else(|| parse_executable_name(plugin).map(|(_, n)| n));
258 match package {
259 Some(p) => write!(f, "update the plugin with `dre plugin update {p}`"),
260 None => f.write_str("update the plugin"),
261 }
262 }
263 HostError::Timeout { plugin, waiting_for } => {
264 write!(
265 f,
266 "plugin `{plugin}` didn't answer in time (waiting for {waiting_for})"
267 )
268 }
269 HostError::Unexpected {
270 plugin,
271 expected,
272 got,
273 } => {
274 write!(f, "plugin `{plugin}` sent {got} where {expected} was expected")
275 }
276 HostError::Plugin { message, .. } => f.write_str(message),
277 HostError::Arrow { plugin, message } => {
278 write!(f, "plugin `{plugin}` sent invalid Arrow data: {message}")
279 }
280 }
281 }
282}
283
284impl std::error::Error for HostError {}
285
286impl HostError {
287 fn plugin(plugin: &str, message: impl Into<String>) -> HostError {
288 HostError::Plugin {
289 plugin: plugin.to_string(),
290 message: message.into(),
291 kind: None,
292 code: None,
293 }
294 }
295}
296
297pub type Result<T> = std::result::Result<T, HostError>;
298
299#[derive(Clone)]
302pub struct Canceller {
303 stdin: Arc<Mutex<Option<BufWriter<ChildStdin>>>>,
304 current: Arc<AtomicU64>,
305 child: Arc<Mutex<Child>>,
306}
307
308impl Canceller {
309 pub fn cancel(&self) -> bool {
311 let id = self.current.load(Ordering::SeqCst);
312 if id == 0 {
313 return false;
314 }
315 self.stdin.lock().is_ok_and(|mut stdin| {
317 stdin
318 .as_mut()
319 .is_some_and(|w| frame::write_json(w, &Envelope::new(Some(id), Request::Cancel {})).is_ok())
320 })
321 }
322
323 pub fn kill(&self) {
325 if let Ok(mut c) = self.child.lock() {
326 let _ = c.kill();
327 }
328 }
329}
330
331static RUNNING: Mutex<Vec<(u64, Canceller)>> = Mutex::new(Vec::new());
334static NEXT_PROCESS: AtomicU64 = AtomicU64::new(1);
335
336pub fn cancel_all() -> usize {
342 let all: Vec<Canceller> = RUNNING.lock().unwrap().iter().map(|(_, c)| c.clone()).collect();
343 all.iter().filter(|c| c.cancel()).count()
344}
345
346pub fn kill_all() {
352 let all: Vec<Canceller> = RUNNING.lock().unwrap().iter().map(|(_, c)| c.clone()).collect();
353 for c in all {
354 c.kill();
355 }
356}
357
358pub fn running() -> usize {
364 RUNNING.lock().unwrap().len()
365}
366pub enum Incoming {
368 Json(Response),
369 Arrow(Vec<u8>),
370}
371
372impl std::fmt::Debug for Incoming {
373 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
374 match self {
375 Incoming::Json(r) => write!(f, "Json({r:?})"),
376 Incoming::Arrow(b) => write!(f, "Arrow frame ({} bytes)", b.len()),
377 }
378 }
379}
380
381#[derive(Debug)]
383pub enum Execution {
384 NoResult { rows_affected: Option<u64> },
385 Result { schema: SchemaRef, rows: u64 },
386}
387
388pub struct PluginProcess {
389 label: String,
390 path: PathBuf,
391 child: Arc<Mutex<Child>>,
393 registered: u64,
395 stdin: Arc<Mutex<Option<BufWriter<ChildStdin>>>>,
397 rx: Receiver<std::result::Result<Frame, FrameError>>,
398 log: LogSink,
399 last_id: u64,
401 current: Arc<AtomicU64>,
403 stderr: Arc<Mutex<VecDeque<String>>>,
404 stderr_sync: StderrSync,
405 stderr_thread: Option<std::thread::JoinHandle<()>>,
407 info: Option<PluginInfo>,
408 early: Option<std::result::Result<Frame, FrameError>>,
410 last_attempts: u32,
412 serve: Option<PluginId>,
415}
416
417impl PluginProcess {
418 pub fn start(path: &Path, log: LogSink) -> Result<PluginProcess> {
420 Self::start_in(path, log, None)
421 }
422
423 pub fn start_in(path: &Path, log: LogSink, cwd: Option<&Path>) -> Result<PluginProcess> {
425 Self::start_for(path, None, log, cwd)
426 }
427
428 pub fn start_for(
430 path: &Path,
431 plugin: Option<&PluginId>,
432 log: LogSink,
433 cwd: Option<&Path>,
434 ) -> Result<PluginProcess> {
435 let timeout = std::env::var("DRE_PLUGIN_HANDSHAKE_TIMEOUT_MS")
436 .ok()
437 .and_then(|v| v.parse().ok())
438 .map(Duration::from_millis)
439 .unwrap_or(DEFAULT_HANDSHAKE_TIMEOUT);
440 let mut p = Self::spawn_in(path, log, &[], cwd)?;
441 if let Some(id) = plugin {
442 p.serve = Some(id.clone());
443 }
444 p.handshake((CORE_MIN_VERSION, MAX_VERSION), timeout)?;
445 Ok(p)
446 }
447
448 pub fn start_with(
450 path: &Path,
451 log: LogSink,
452 versions: (u32, u32),
453 timeout: Duration,
454 ) -> Result<PluginProcess> {
455 let mut p = Self::spawn(path, log)?;
456 p.handshake(versions, timeout)?;
457 Ok(p)
458 }
459
460 pub fn spawn(path: &Path, log: LogSink) -> Result<PluginProcess> {
462 Self::spawn_env(path, log, &[])
463 }
464
465 pub fn spawn_env(path: &Path, log: LogSink, env: &[(&str, &str)]) -> Result<PluginProcess> {
467 Self::spawn_in(path, log, env, None)
468 }
469
470 pub fn spawn_in(
476 path: &Path,
477 log: LogSink,
478 env: &[(&str, &str)],
479 cwd: Option<&Path>,
480 ) -> Result<PluginProcess> {
481 let label = path
482 .file_name()
483 .map(|f| f.to_string_lossy().to_string())
484 .unwrap_or_default();
485 let mut cmd = Command::new(path);
486 if let Some(dir) = cwd {
487 cmd.current_dir(dir);
488 }
489 if std::env::var_os(INTERACTIVE_ENV).is_none() {
492 use std::io::IsTerminal;
493 let there = std::io::stderr().is_terminal() && std::io::stdin().is_terminal();
494 cmd.env(INTERACTIVE_ENV, if there { "1" } else { "0" });
495 }
496 #[cfg(unix)]
499 {
500 use std::os::unix::process::CommandExt;
501 cmd.process_group(0);
502 }
503 #[cfg(target_os = "linux")]
506 crate::fuse_fds::close_in_child(&mut cmd);
507 #[cfg(windows)]
508 {
509 use std::os::windows::process::CommandExt;
510 const CREATE_NEW_PROCESS_GROUP: u32 = 0x0000_0200;
511 cmd.creation_flags(CREATE_NEW_PROCESS_GROUP);
512 }
513 let mut child = cmd
514 .envs(env.iter().copied())
515 .stdin(Stdio::piped())
516 .stdout(Stdio::piped())
517 .stderr(Stdio::piped())
518 .spawn()
519 .map_err(|error| HostError::Spawn {
520 plugin: label.clone(),
521 error,
522 })?;
523 let (Some(stdout), Some(stderr_pipe)) = (child.stdout.take(), child.stderr.take()) else {
524 let _ = child.kill();
525 return Err(HostError::Spawn {
526 plugin: label,
527 error: std::io::Error::other("the plugin's stdout or stderr isn't piped"),
528 });
529 };
530 let stdin = Arc::new(Mutex::new(child.stdin.take().map(BufWriter::new)));
531
532 let (tx, rx) = channel();
533 std::thread::spawn(move || {
534 let mut r = BufReader::new(stdout);
535 loop {
536 let f = frame::read_frame(&mut r);
537 let stop = f.is_err();
538 if tx.send(f).is_err() || stop {
539 break;
540 }
541 }
542 });
543 let stderr = Arc::new(Mutex::new(VecDeque::new()));
544 let tail = stderr.clone();
545 let who = label.clone();
546 let sink = log.clone();
547 let stderr_sync = StderrSync {
548 progress: Arc::default(),
549 pipe: raw_pipe(&stderr_pipe),
550 };
551 let progress = stderr_sync.progress.clone();
552 let stderr_thread = std::thread::spawn(move || {
553 let mut reader = BufReader::new(TrackedPipe {
554 pipe: stderr_pipe,
555 progress: progress.clone(),
556 });
557 for line in reader.by_ref().lines() {
558 let Ok(line) = line else { break };
559 sink(&who, &line);
560 let mut t = tail.lock().unwrap();
561 if t.len() == STDERR_TAIL {
562 t.pop_front();
563 }
564 t.push_back(line);
565 }
566 progress.done.store(true, Ordering::SeqCst);
568 drop(reader);
569 });
570 let serve = parse_executable_name(&label).map(|(k, n)| PluginId::new(k, n));
571 let child = Arc::new(Mutex::new(child));
572 let current = Arc::new(AtomicU64::new(0));
573 let registered = NEXT_PROCESS.fetch_add(1, Ordering::SeqCst);
574 RUNNING.lock().unwrap().push((
575 registered,
576 Canceller {
577 stdin: stdin.clone(),
578 current: current.clone(),
579 child: child.clone(),
580 },
581 ));
582 Ok(PluginProcess {
583 label,
584 path: path.to_path_buf(),
585 child,
586 registered,
587 stdin,
588 rx,
589 log,
590 last_id: 0,
591 current,
592 stderr,
593 stderr_sync,
594 stderr_thread: Some(stderr_thread),
595 info: None,
596 early: None,
597 last_attempts: 1,
598 serve,
599 })
600 }
601
602 pub fn ask_for(&mut self, plugin: Option<PluginId>) {
604 self.serve = plugin;
605 }
606
607 pub fn handshake(&mut self, (min, max): (u32, u32), timeout: Duration) -> Result<()> {
609 self.send(&Request::Hello {
610 min_version: min,
611 max_version: max,
612 core_version: core_version(),
613 plugin: self.serve.clone(),
614 })?;
615 match self.recv(Some(timeout), "the hello reply")? {
616 Incoming::Json(Response::Hello {
617 protocol_version,
618 kind,
619 name,
620 version,
621 capabilities,
622 mut provides,
623 }) => {
624 if protocol_version < min || protocol_version > max {
625 return Err(HostError::Incompatible {
626 plugin: self.label.clone(),
627 core: (min, max),
628 plugin_range: (protocol_version, protocol_version),
629 });
630 }
631 if let Some(want) = &self.serve
632 && (want.kind != kind || want.name != name)
633 {
634 return Err(HostError::plugin(
635 &self.label,
636 format!(
637 "`{}` was asked for the {want} plugin but serves {kind}/{name}",
638 self.label
639 ),
640 ));
641 }
642 if provides.is_empty() {
643 provides.push(PluginId::new(kind, name.clone()));
644 }
645 self.info = Some(PluginInfo {
646 protocol_version,
647 kind,
648 name,
649 version,
650 capabilities,
651 provides,
652 });
653 Ok(())
654 }
655 Incoming::Json(Response::VersionMismatch {
656 min_version,
657 max_version,
658 }) => Err(HostError::Incompatible {
659 plugin: self.label.clone(),
660 core: (min, max),
661 plugin_range: (min_version, max_version),
662 }),
663 Incoming::Json(Response::Error { message, kind, code }) => Err(HostError::Plugin {
664 plugin: self.label.clone(),
665 message,
666 kind,
667 code,
668 }),
669 other => Err(self.unexpected("a hello reply", &other)),
670 }
671 }
672
673 pub fn label(&self) -> &str {
674 &self.label
675 }
676
677 pub fn path(&self) -> &Path {
678 &self.path
679 }
680
681 pub fn info(&self) -> &PluginInfo {
688 self.info.as_ref().expect("handshake completed")
689 }
690
691 fn try_wait(&self) -> std::io::Result<Option<ExitStatus>> {
693 self.child.lock().unwrap().try_wait()
694 }
695
696 pub fn canceller(&self) -> Canceller {
699 Canceller {
700 stdin: self.stdin.clone(),
701 current: self.current.clone(),
702 child: self.child.clone(),
703 }
704 }
705
706 pub fn stderr_sync(&self) -> StderrSync {
708 self.stderr_sync.clone()
709 }
710
711 fn version(&self) -> u32 {
713 self.info.as_ref().map_or(0, |i| i.protocol_version)
714 }
715
716 pub fn has(&self, capability: &str) -> bool {
717 self.info
718 .as_ref()
719 .is_some_and(|i| i.capabilities.iter().any(|c| c == capability))
720 }
721
722 fn unexpected(&self, expected: &'static str, got: &Incoming) -> HostError {
723 let got = match got {
724 Incoming::Json(r) => format!("{:?}", r)
725 .split([' ', '{'])
726 .next()
727 .unwrap_or("?")
728 .to_string(),
729 Incoming::Arrow(_) => "Arrow data".into(),
730 };
731 HostError::Unexpected {
732 plugin: self.label.clone(),
733 expected,
734 got: format!("`{got}`"),
735 }
736 }
737
738 fn crashed(&mut self) -> HostError {
739 let mut status = None;
741 for _ in 0..50 {
742 if let Ok(Some(s)) = self.try_wait() {
743 status = Some(s);
744 break;
745 }
746 std::thread::sleep(Duration::from_millis(20));
747 }
748 std::thread::sleep(Duration::from_millis(50));
749 let stderr = self.stderr.lock().unwrap().iter().cloned().collect();
750 HostError::Crashed {
751 plugin: self.label.clone(),
752 status,
753 stderr,
754 }
755 }
756
757 pub fn send(&mut self, req: &Request) -> Result<()> {
760 let id = match req {
761 _ if self.version() == 0 => None,
762 Request::Hello { .. } => None,
763 Request::ResultSetEnd {} | Request::Finish {} | Request::Cancel {} => {
764 Some(self.current.load(Ordering::SeqCst)).filter(|id| *id != 0)
765 }
766 _ => {
767 self.last_id += 1;
768 self.current.store(self.last_id, Ordering::SeqCst);
769 Some(self.last_id)
770 }
771 };
772 let ok = self.stdin.lock().is_ok_and(|mut stdin| {
774 stdin
775 .as_mut()
776 .is_some_and(|w| frame::write_json(w, &Envelope::new(id, req)).is_ok())
777 });
778 if ok { Ok(()) } else { Err(self.write_failed()) }
779 }
780
781 pub fn send_batch(&mut self, batch: &RecordBatch) -> Result<()> {
782 if self.early.is_none()
785 && let Ok(f) = self.rx.try_recv()
786 {
787 self.early = Some(f);
788 }
789 if let Some(Ok(Frame::Json(v))) = &self.early
790 && let Ok(Response::Error { message, kind, code }) = serde_json::from_value::<Response>(v.clone())
791 {
792 return Err(HostError::Plugin {
793 plugin: self.label.clone(),
794 message,
795 kind,
796 code,
797 });
798 }
799 let ipc = frame::encode_batch(batch).map_err(|e| HostError::Arrow {
800 plugin: self.label.clone(),
801 message: e.to_string(),
802 })?;
803 let ok = self.stdin.lock().is_ok_and(|mut stdin| {
804 stdin
805 .as_mut()
806 .is_some_and(|w| frame::write_arrow(w, &ipc).is_ok())
807 });
808 if ok { Ok(()) } else { Err(self.write_failed()) }
809 }
810
811 fn write_failed(&mut self) -> HostError {
813 match self.rx.recv_timeout(Duration::from_secs(2)) {
814 Ok(Ok(Frame::Json(v))) => match serde_json::from_value::<Response>(v) {
815 Ok(Response::Error { message, kind, code }) => HostError::Plugin {
816 plugin: self.label.clone(),
817 message,
818 kind,
819 code,
820 },
821 _ => self.crashed(),
822 },
823 _ => self.crashed(),
824 }
825 }
826
827 pub fn recv(&mut self, timeout: Option<Duration>, waiting_for: &'static str) -> Result<Incoming> {
830 loop {
831 match self.recv_any(timeout, waiting_for)? {
832 Incoming::Json(Response::Log {
833 level,
834 message,
835 fields,
836 }) => {
837 (self.log)(&self.label, &log_line(level, &message, &fields));
838 }
839 Incoming::Json(Response::Progress { message, done, total }) => {
840 (self.log)(&self.label, &progress_line(message.as_deref(), done, total));
841 }
842 other => return Ok(other),
843 }
844 }
845 }
846
847 pub fn recv_raw(&mut self, timeout: Duration) -> Result<Value> {
849 match self.rx.recv_timeout(timeout) {
850 Ok(Ok(Frame::Json(v))) => Ok(v),
851 Ok(Ok(Frame::Arrow(_))) => Err(HostError::Malformed {
852 plugin: self.label.clone(),
853 message: "Arrow data where a JSON message was expected".into(),
854 }),
855 Ok(Err(FrameError::Malformed(m))) => Err(HostError::Malformed {
856 plugin: self.label.clone(),
857 message: m,
858 }),
859 Err(RecvTimeoutError::Timeout) => Err(HostError::Timeout {
860 plugin: self.label.clone(),
861 waiting_for: "a message",
862 }),
863 _ => Err(self.crashed()),
864 }
865 }
866
867 fn recv_any(&mut self, timeout: Option<Duration>, waiting_for: &'static str) -> Result<Incoming> {
868 let got = match timeout {
869 _ if self.early.is_some() => self.early.take(),
870 Some(t) => match self.rx.recv_timeout(t) {
871 Ok(f) => Some(f),
872 Err(RecvTimeoutError::Timeout) => {
873 return Err(HostError::Timeout {
874 plugin: self.label.clone(),
875 waiting_for,
876 });
877 }
878 Err(RecvTimeoutError::Disconnected) => None,
879 },
880 None => self.rx.recv().ok(),
881 };
882 match got {
883 Some(Ok(Frame::Json(v))) => match serde_json::from_value::<Envelope<Response>>(v.clone()) {
884 Ok(Envelope { id: Some(id), body })
885 if !matches!(body, Response::Log { .. } | Response::Progress { .. }) =>
886 {
887 let current = self.current.load(Ordering::SeqCst);
888 if id != current {
889 return Err(HostError::Malformed {
890 plugin: self.label.clone(),
891 message: format!(
892 "a reply to request {id} arrived while request {current} was running"
893 ),
894 });
895 }
896 Ok(Incoming::Json(body))
897 }
898 Ok(e) => Ok(Incoming::Json(e.body)),
899 Err(e) => Err(HostError::Malformed {
900 plugin: self.label.clone(),
901 message: format!("unknown message {v}: {e}"),
902 }),
903 },
904 Some(Ok(Frame::Arrow(b))) => Ok(Incoming::Arrow(b)),
905 Some(Err(FrameError::Malformed(m))) => Err(HostError::Malformed {
906 plugin: self.label.clone(),
907 message: m,
908 }),
909 Some(Err(_)) | None => Err(self.crashed()),
910 }
911 }
912
913 pub fn recv_json(&mut self, waiting_for: &'static str) -> Result<Response> {
915 match self.recv(None, waiting_for)? {
916 Incoming::Json(Response::Error { message, kind, code }) => Err(HostError::Plugin {
917 plugin: self.label.clone(),
918 message,
919 kind,
920 code,
921 }),
922 Incoming::Json(r) => Ok(r),
923 other => Err(self.unexpected(waiting_for, &other)),
924 }
925 }
926
927 fn call_ok(&mut self, req: &Request, what: &'static str) -> Result<()> {
929 self.send(req)?;
930 match self.recv_json(what)? {
931 Response::Ok {} => Ok(()),
932 other => Err(self.unexpected(what, &Incoming::Json(other))),
933 }
934 }
935
936 pub fn describe(&mut self) -> Result<Vec<ConnectionField>> {
937 Ok(self.describe_all()?.0)
938 }
939
940 pub fn describe_all(&mut self) -> Result<(Vec<ConnectionField>, Vec<OptionField>)> {
942 let d = self.description()?;
943 Ok((d.connection_fields, d.option_fields))
944 }
945
946 pub fn description(&mut self) -> Result<Description> {
948 self.send(&Request::Describe {})?;
949 match self.recv_json("a describe reply")? {
950 Response::Describe {
951 connection_fields,
952 option_fields,
953 identifier_quote,
954 string_literal,
955 message_limit,
956 } => Ok(Description {
957 connection_fields,
958 option_fields,
959 identifier_quote,
960 string_literal,
961 message_limit,
962 }),
963 other => Err(self.unexpected("a describe reply", &Incoming::Json(other))),
964 }
965 }
966
967 pub fn validate_connection(
970 &mut self,
971 mut connection: Map<String, Value>,
972 unresolved: Vec<String>,
973 ) -> Result<(Vec<String>, Vec<String>)> {
974 for k in &unresolved {
975 connection.insert(k.clone(), Value::Null);
976 }
977 self.send(&Request::ValidateConnection {
978 connection,
979 unresolved,
980 })?;
981 match self.recv_json("a validated reply")? {
982 Response::Validated { errors, warnings } => Ok((errors, warnings)),
983 other => Err(self.unexpected("a validated reply", &Incoming::Json(other))),
984 }
985 }
986
987 pub fn validate(&mut self, options: Map<String, Value>) -> Result<Vec<String>> {
989 Ok(self.validate_with(options, None)?.0)
990 }
991
992 pub fn validate_with(
995 &mut self,
996 options: Map<String, Value>,
997 template: Option<Value>,
998 ) -> Result<(Vec<String>, Vec<String>)> {
999 self.send(&Request::Validate { options, template })?;
1000 match self.recv_json("a validate reply")? {
1001 Response::Validated { errors, warnings } => Ok((errors, warnings)),
1002 other => Err(self.unexpected("a validate reply", &Incoming::Json(other))),
1003 }
1004 }
1005
1006 pub fn open(&mut self, connection: Map<String, Value>, read_only: bool) -> Result<()> {
1007 self.call_ok(
1008 &Request::Open {
1009 connection,
1010 read_only,
1011 sandbox: false,
1012 },
1013 "an open reply",
1014 )
1015 }
1016
1017 pub fn open_sandboxed(&mut self, connection: Map<String, Value>) -> Result<()> {
1020 self.call_ok(
1021 &Request::Open {
1022 connection,
1023 read_only: true,
1024 sandbox: true,
1025 },
1026 "an open reply",
1027 )
1028 }
1029
1030 pub fn check(&mut self, sql: &str) -> Result<()> {
1031 self.call_ok(&Request::Check { sql: sql.to_string() }, "a check reply")
1032 }
1033
1034 pub fn execute(
1036 &mut self,
1037 sql: &str,
1038 row_limit: Option<u64>,
1039 mut on_batch: impl FnMut(&SchemaRef, RecordBatch) -> std::result::Result<(), String>,
1040 ) -> Result<Execution> {
1041 self.send(&Request::Execute {
1042 sql: sql.to_string(),
1043 row_limit,
1044 })?;
1045 match self.recv_json("an execute reply")? {
1046 Response::NoResult { rows_affected } => return Ok(Execution::NoResult { rows_affected }),
1047 Response::Result { .. } => {}
1048 other => return Err(self.unexpected("an execute reply", &Incoming::Json(other))),
1049 }
1050 let mut schema: Option<SchemaRef> = None;
1051 let mut rows = 0u64;
1052 let mut sink_error = None;
1053 loop {
1054 match self.recv(None, "result data")? {
1055 Incoming::Arrow(ipc) => {
1056 let (s, batches) = frame::decode_batches(&ipc).map_err(|e| HostError::Arrow {
1057 plugin: self.label.clone(),
1058 message: e.to_string(),
1059 })?;
1060 let schema = schema.get_or_insert(s);
1061 for b in batches {
1062 rows += b.num_rows() as u64;
1063 if sink_error.is_none()
1064 && let Err(e) = on_batch(schema, b)
1065 {
1066 sink_error = Some(e);
1068 }
1069 }
1070 }
1071 Incoming::Json(Response::ResultEnd { .. }) => break,
1072 Incoming::Json(Response::Error { message, kind, code }) => {
1073 return Err(HostError::Plugin {
1074 plugin: self.label.clone(),
1075 message,
1076 kind,
1077 code,
1078 });
1079 }
1080 other => return Err(self.unexpected("result data", &other)),
1081 }
1082 }
1083 if let Some(e) = sink_error {
1084 return Err(HostError::plugin(&self.label, e));
1085 }
1086 let schema = schema.ok_or_else(|| HostError::Malformed {
1087 plugin: self.label.clone(),
1088 message: "a result ended without any Arrow frame carrying its schema".into(),
1089 })?;
1090 Ok(Execution::Result { schema, rows })
1091 }
1092
1093 pub fn write_begin(
1095 &mut self,
1096 path: &str,
1097 format: &str,
1098 options: Map<String, Value>,
1099 result_sets: Vec<ResultSetMeta>,
1100 template: Option<Value>,
1101 ) -> Result<()> {
1102 self.send(&Request::Write {
1103 path: path.to_string(),
1104 format: format.to_string(),
1105 options,
1106 result_sets,
1107 template,
1108 })
1109 }
1110
1111 pub fn write_result_set(
1113 &mut self,
1114 schema: &SchemaRef,
1115 batches: impl IntoIterator<Item = RecordBatch>,
1116 ) -> Result<()> {
1117 let mut any = false;
1118 for b in batches {
1119 any = true;
1120 self.send_batch(&b)?;
1121 }
1122 if !any {
1123 self.send_batch(&RecordBatch::new_empty(schema.clone()))?;
1124 }
1125 self.send(&Request::ResultSetEnd {})
1126 }
1127
1128 pub fn load(
1131 &mut self,
1132 name: &str,
1133 schema: &SchemaRef,
1134 batches: impl IntoIterator<Item = RecordBatch>,
1135 ) -> Result<(String, Option<String>)> {
1136 self.send(&Request::Load {
1137 name: name.to_string(),
1138 })?;
1139 self.write_result_set(schema, batches)?;
1140 match self.recv_json("a loaded reply")? {
1141 Response::Loaded {
1142 relation, warning, ..
1143 } => Ok((relation, warning)),
1144 other => Err(self.unexpected("a loaded reply", &Incoming::Json(other))),
1145 }
1146 }
1147
1148 pub fn write_finish(&mut self) -> Result<Vec<String>> {
1149 Ok(self.write_finish_with_warnings()?.0)
1150 }
1151
1152 pub fn write_finish_with_warnings(&mut self) -> Result<(Vec<String>, Vec<String>)> {
1154 self.send(&Request::Finish {})?;
1155 match self.recv_json("a written reply")? {
1156 Response::Written { files, warnings } => Ok((files, warnings)),
1157 other => Err(self.unexpected("a written reply", &Incoming::Json(other))),
1158 }
1159 }
1160
1161 pub fn deliver(
1163 &mut self,
1164 local_path: &str,
1165 remote_path: Option<&str>,
1166 connection: Map<String, Value>,
1167 ) -> Result<String> {
1168 let file = DeliveryFile {
1169 local_path: local_path.to_string(),
1170 remote_path: remote_path.map(str::to_string),
1171 };
1172 self.deliver_files(&[file], connection, Map::new())
1173 }
1174
1175 pub fn deliver_files(
1178 &mut self,
1179 files: &[DeliveryFile],
1180 connection: Map<String, Value>,
1181 options: Map<String, Value>,
1182 ) -> Result<String> {
1183 let req = match files {
1184 [] => {
1185 return Err(HostError::Malformed {
1186 plugin: self.label.clone(),
1187 message: "nothing to deliver".into(),
1188 });
1189 }
1190 [one] => Request::Deliver {
1191 local_path: Some(one.local_path.clone()),
1192 remote_path: one.remote_path.clone(),
1193 files: Vec::new(),
1194 connection,
1195 options,
1196 message: None,
1197 },
1198 many => {
1199 if !self.has(crate::CAP_MULTI_FILE) {
1200 return Err(HostError::plugin(
1201 &self.label,
1202 format!(
1203 "{} files in one delivery, but the plugin doesn't advertise `multi_file`",
1204 many.len()
1205 ),
1206 ));
1207 }
1208 Request::Deliver {
1209 local_path: None,
1210 remote_path: None,
1211 files: many.to_vec(),
1212 connection,
1213 options,
1214 message: None,
1215 }
1216 }
1217 };
1218 self.send(&req)?;
1219 match self.recv_json("a delivered reply")? {
1220 Response::Delivered { location, attempts } => {
1221 self.last_attempts = attempts.unwrap_or(1);
1222 Ok(location)
1223 }
1224 other => Err(self.unexpected("a delivered reply", &Incoming::Json(other))),
1225 }
1226 }
1227
1228 pub fn deliver_message(
1230 &mut self,
1231 message: &crate::msg::Message,
1232 attach: &[DeliveryFile],
1233 connection: Map<String, Value>,
1234 options: Map<String, Value>,
1235 ) -> Result<String> {
1236 if !self.has(crate::CAP_MESSAGE) {
1237 return Err(HostError::plugin(
1238 &self.label,
1239 "a message, but the plugin doesn't advertise `message`",
1240 ));
1241 }
1242 self.send(&Request::Deliver {
1243 local_path: None,
1244 remote_path: None,
1245 files: attach.to_vec(),
1246 connection,
1247 options,
1248 message: Some(message.clone()),
1249 })?;
1250 match self.recv_json("a delivered reply")? {
1251 Response::Delivered { location, attempts } => {
1252 self.last_attempts = attempts.unwrap_or(1);
1253 Ok(location)
1254 }
1255 other => Err(self.unexpected("a delivered reply", &Incoming::Json(other))),
1256 }
1257 }
1258
1259 pub fn last_attempts(&self) -> u32 {
1262 self.last_attempts
1263 }
1264
1265 pub fn close(mut self) -> Result<()> {
1267 let _ = self.send(&Request::Close {});
1268 let _ = self.recv(Some(Duration::from_secs(5)), "the close reply");
1269 if let Ok(mut stdin) = self.stdin.lock() {
1270 stdin.take();
1271 }
1272 for _ in 0..250 {
1273 if let Ok(Some(_)) = self.try_wait() {
1274 return Ok(());
1275 }
1276 std::thread::sleep(Duration::from_millis(20));
1277 }
1278 if let Ok(mut child) = self.child.lock() {
1279 let _ = child.kill();
1280 }
1281 Ok(())
1282 }
1283
1284 pub fn write_raw(&mut self, bytes: &[u8]) -> Result<()> {
1286 let ok = self.stdin.lock().is_ok_and(|mut stdin| {
1287 stdin
1288 .as_mut()
1289 .is_some_and(|w| w.write_all(bytes).and_then(|_| w.flush()).is_ok())
1290 });
1291 if ok { Ok(()) } else { Err(self.write_failed()) }
1292 }
1293
1294 pub fn wait_exit(&mut self, timeout: Duration) -> Option<ExitStatus> {
1296 if let Ok(mut stdin) = self.stdin.lock() {
1297 stdin.take();
1298 }
1299 let deadline = std::time::Instant::now() + timeout;
1300 while std::time::Instant::now() < deadline {
1301 if let Ok(Some(s)) = self.try_wait() {
1302 return Some(s);
1303 }
1304 std::thread::sleep(Duration::from_millis(20));
1305 }
1306 None
1307 }
1308}
1309
1310impl PluginProcess {
1311 fn drain_stderr(&mut self) {
1313 if let Some(t) = self.stderr_thread.take() {
1314 let deadline = std::time::Instant::now() + Duration::from_secs(2);
1315 while !t.is_finished() && std::time::Instant::now() < deadline {
1316 std::thread::sleep(Duration::from_millis(5));
1317 }
1318 if t.is_finished() {
1319 let _ = t.join();
1320 }
1321 }
1322 }
1323}
1324
1325impl Drop for PluginProcess {
1326 fn drop(&mut self) {
1327 if let Ok(mut r) = RUNNING.lock() {
1328 r.retain(|(id, _)| *id != self.registered);
1329 }
1330 if let Ok(mut stdin) = self.stdin.lock() {
1331 stdin.take();
1332 }
1333 if let Ok(None) = self.try_wait() {
1334 for _ in 0..25 {
1336 if let Ok(Some(_)) = self.try_wait() {
1337 break;
1338 }
1339 std::thread::sleep(Duration::from_millis(10));
1340 }
1341 let _ = self.child.lock().unwrap().kill();
1342 let _ = self.child.lock().unwrap().wait();
1343 }
1344 self.drain_stderr();
1345 }
1346}
1347
1348fn log_line(level: LogLevel, message: &str, fields: &Map<String, Value>) -> String {
1350 let prefix = match level {
1351 LogLevel::Error | LogLevel::Warn => "warning: ",
1352 LogLevel::Info => "info: ",
1353 LogLevel::Debug | LogLevel::Trace => "debug: ",
1354 };
1355 let mut line = format!("{prefix}{message}");
1356 for (k, v) in fields {
1357 match v {
1358 Value::String(s) => line.push_str(&format!(" {k}={s}")),
1359 v => line.push_str(&format!(" {k}={v}")),
1360 }
1361 }
1362 line
1363}
1364
1365fn progress_line(message: Option<&str>, done: Option<u64>, total: Option<u64>) -> String {
1367 let count = match (done, total) {
1368 (Some(d), Some(t)) => format!("{d}/{t}"),
1369 (Some(d), None) => d.to_string(),
1370 _ => String::new(),
1371 };
1372 match (message, count.is_empty()) {
1373 (Some(m), true) => format!("info: {m}"),
1374 (Some(m), false) => format!("info: {m} ({count})"),
1375 (None, _) => format!("info: {count}"),
1376 }
1377}
1378
1379static CORE_VERSION: OnceLock<String> = OnceLock::new();
1380
1381pub fn set_core_version(version: &str) {
1384 let _ = CORE_VERSION.set(version.to_string());
1385}
1386
1387fn core_version() -> String {
1388 CORE_VERSION.get().cloned().unwrap_or_else(|| "unreleased".into())
1389}