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 pub single_writer: Option<crate::msg::SingleWriter>,
180}
181
182#[derive(Debug)]
183pub enum HostError {
184 Spawn {
185 plugin: String,
186 error: std::io::Error,
187 },
188 Crashed {
189 plugin: String,
190 status: Option<ExitStatus>,
191 stderr: Vec<String>,
192 },
193 Malformed {
194 plugin: String,
195 message: String,
196 },
197 Incompatible {
198 plugin: String,
199 core: (u32, u32),
200 plugin_range: (u32, u32),
201 },
202 Timeout {
203 plugin: String,
204 waiting_for: &'static str,
205 },
206 Unexpected {
207 plugin: String,
208 expected: &'static str,
209 got: String,
210 },
211 Plugin {
213 plugin: String,
214 message: String,
215 kind: Option<String>,
216 code: Option<String>,
217 },
218 Arrow {
219 plugin: String,
220 message: String,
221 },
222}
223
224impl std::fmt::Display for HostError {
225 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 match self {
227 HostError::Spawn { plugin, error } => write!(f, "can't start plugin `{plugin}`: {error}"),
228 HostError::Crashed {
229 plugin,
230 status,
231 stderr,
232 } => {
233 match status {
234 Some(s) => write!(f, "plugin `{plugin}` exited unexpectedly ({s})")?,
235 None => write!(f, "plugin `{plugin}` closed its output unexpectedly")?,
236 }
237 if !stderr.is_empty() {
238 write!(f, "; its last log lines:\n {}", stderr.join("\n "))?;
239 }
240 Ok(())
241 }
242 HostError::Malformed { plugin, message } => {
243 write!(f, "plugin `{plugin}` broke the protocol: {message}")
244 }
245 HostError::Incompatible {
246 plugin,
247 core,
248 plugin_range,
249 } => {
250 write!(
251 f,
252 "plugin `{plugin}` speaks protocol versions {}..={}, but this DRE core speaks {}..={}; ",
253 plugin_range.0, plugin_range.1, core.0, core.1,
254 )?;
255 if plugin_range.1 >= core.0 {
256 return f.write_str("update DRE");
257 }
258 let package = parse_package_executable_name(plugin)
259 .or_else(|| parse_executable_name(plugin).map(|(_, n)| n));
260 match package {
261 Some(p) => write!(f, "update the plugin with `dre plugin update {p}`"),
262 None => f.write_str("update the plugin"),
263 }
264 }
265 HostError::Timeout { plugin, waiting_for } => {
266 write!(
267 f,
268 "plugin `{plugin}` didn't answer in time (waiting for {waiting_for})"
269 )
270 }
271 HostError::Unexpected {
272 plugin,
273 expected,
274 got,
275 } => {
276 write!(f, "plugin `{plugin}` sent {got} where {expected} was expected")
277 }
278 HostError::Plugin { message, .. } => f.write_str(message),
279 HostError::Arrow { plugin, message } => {
280 write!(f, "plugin `{plugin}` sent invalid Arrow data: {message}")
281 }
282 }
283 }
284}
285
286impl std::error::Error for HostError {}
287
288impl HostError {
289 fn plugin(plugin: &str, message: impl Into<String>) -> HostError {
290 HostError::Plugin {
291 plugin: plugin.to_string(),
292 message: message.into(),
293 kind: None,
294 code: None,
295 }
296 }
297}
298
299pub type Result<T> = std::result::Result<T, HostError>;
300
301#[derive(Clone)]
304pub struct Canceller {
305 stdin: Arc<Mutex<Option<BufWriter<ChildStdin>>>>,
306 current: Arc<AtomicU64>,
307 child: Arc<Mutex<Child>>,
308}
309
310impl Canceller {
311 pub fn cancel(&self) -> bool {
313 let id = self.current.load(Ordering::SeqCst);
314 if id == 0 {
315 return false;
316 }
317 self.stdin.lock().is_ok_and(|mut stdin| {
319 stdin
320 .as_mut()
321 .is_some_and(|w| frame::write_json(w, &Envelope::new(Some(id), Request::Cancel {})).is_ok())
322 })
323 }
324
325 pub fn kill(&self) {
327 if let Ok(mut c) = self.child.lock() {
328 let _ = c.kill();
329 }
330 }
331}
332
333pub enum Incoming {
335 Json(Response),
336 Arrow(Vec<u8>),
337}
338
339impl std::fmt::Debug for Incoming {
340 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
341 match self {
342 Incoming::Json(r) => write!(f, "Json({r:?})"),
343 Incoming::Arrow(b) => write!(f, "Arrow frame ({} bytes)", b.len()),
344 }
345 }
346}
347
348#[derive(Debug)]
350pub enum Execution {
351 NoResult { rows_affected: Option<u64> },
352 Result { schema: SchemaRef, rows: u64 },
353}
354
355pub struct PluginProcess {
356 label: String,
357 path: PathBuf,
358 child: Arc<Mutex<Child>>,
360 stdin: Arc<Mutex<Option<BufWriter<ChildStdin>>>>,
362 rx: Receiver<std::result::Result<Frame, FrameError>>,
363 log: LogSink,
364 last_id: u64,
366 current: Arc<AtomicU64>,
368 stderr: Arc<Mutex<VecDeque<String>>>,
369 stderr_sync: StderrSync,
370 stderr_thread: Option<std::thread::JoinHandle<()>>,
372 info: Option<PluginInfo>,
373 early: Option<std::result::Result<Frame, FrameError>>,
375 last_attempts: u32,
377 serve: Option<PluginId>,
380}
381
382impl PluginProcess {
383 pub fn start(path: &Path, log: LogSink) -> Result<PluginProcess> {
385 Self::start_in(path, log, None)
386 }
387
388 pub fn start_in(path: &Path, log: LogSink, cwd: Option<&Path>) -> Result<PluginProcess> {
390 Self::start_for(path, None, log, cwd)
391 }
392
393 pub fn start_for(
395 path: &Path,
396 plugin: Option<&PluginId>,
397 log: LogSink,
398 cwd: Option<&Path>,
399 ) -> Result<PluginProcess> {
400 let timeout = std::env::var("DRE_PLUGIN_HANDSHAKE_TIMEOUT_MS")
401 .ok()
402 .and_then(|v| v.parse().ok())
403 .map(Duration::from_millis)
404 .unwrap_or(DEFAULT_HANDSHAKE_TIMEOUT);
405 let mut p = Self::spawn_in(path, log, &[], cwd)?;
406 if let Some(id) = plugin {
407 p.serve = Some(id.clone());
408 }
409 p.handshake((CORE_MIN_VERSION, MAX_VERSION), timeout)?;
410 Ok(p)
411 }
412
413 pub fn start_with(
415 path: &Path,
416 log: LogSink,
417 versions: (u32, u32),
418 timeout: Duration,
419 ) -> Result<PluginProcess> {
420 let mut p = Self::spawn(path, log)?;
421 p.handshake(versions, timeout)?;
422 Ok(p)
423 }
424
425 pub fn spawn(path: &Path, log: LogSink) -> Result<PluginProcess> {
427 Self::spawn_env(path, log, &[])
428 }
429
430 pub fn spawn_env(path: &Path, log: LogSink, env: &[(&str, &str)]) -> Result<PluginProcess> {
432 Self::spawn_in(path, log, env, None)
433 }
434
435 pub fn spawn_in(
442 path: &Path,
443 log: LogSink,
444 env: &[(&str, &str)],
445 cwd: Option<&Path>,
446 ) -> Result<PluginProcess> {
447 let label = path
448 .file_name()
449 .map(|f| f.to_string_lossy().to_string())
450 .unwrap_or_default();
451 let mut cmd = Command::new(path);
452 if let Some(dir) = cwd {
453 cmd.current_dir(dir);
454 }
455 if std::env::var_os(INTERACTIVE_ENV).is_none() {
458 use std::io::IsTerminal;
459 let there = std::io::stderr().is_terminal() && std::io::stdin().is_terminal();
460 cmd.env(INTERACTIVE_ENV, if there { "1" } else { "0" });
461 }
462 #[cfg(unix)]
465 {
466 use std::os::unix::process::CommandExt;
467 cmd.process_group(0);
468 }
469 #[cfg(target_os = "linux")]
472 crate::fuse_fds::close_in_child(&mut cmd);
473 #[cfg(windows)]
474 {
475 use std::os::windows::process::CommandExt;
476 const CREATE_NEW_PROCESS_GROUP: u32 = 0x0000_0200;
477 cmd.creation_flags(CREATE_NEW_PROCESS_GROUP);
478 }
479 let mut child = cmd
480 .envs(env.iter().copied())
481 .stdin(Stdio::piped())
482 .stdout(Stdio::piped())
483 .stderr(Stdio::piped())
484 .spawn()
485 .map_err(|error| HostError::Spawn {
486 plugin: label.clone(),
487 error,
488 })?;
489 let (Some(stdout), Some(stderr_pipe)) = (child.stdout.take(), child.stderr.take()) else {
490 let _ = child.kill();
491 return Err(HostError::Spawn {
492 plugin: label,
493 error: std::io::Error::other("the plugin's stdout or stderr isn't piped"),
494 });
495 };
496 let stdin = Arc::new(Mutex::new(child.stdin.take().map(BufWriter::new)));
497
498 let (tx, rx) = channel();
499 std::thread::spawn(move || {
500 let mut r = BufReader::new(stdout);
501 loop {
502 let f = frame::read_frame(&mut r);
503 let stop = f.is_err();
504 if tx.send(f).is_err() || stop {
505 break;
506 }
507 }
508 });
509 let stderr = Arc::new(Mutex::new(VecDeque::new()));
510 let tail = stderr.clone();
511 let who = label.clone();
512 let sink = log.clone();
513 let stderr_sync = StderrSync {
514 progress: Arc::default(),
515 pipe: raw_pipe(&stderr_pipe),
516 };
517 let progress = stderr_sync.progress.clone();
518 let stderr_thread = std::thread::spawn(move || {
519 let mut reader = BufReader::new(TrackedPipe {
520 pipe: stderr_pipe,
521 progress: progress.clone(),
522 });
523 for line in reader.by_ref().lines() {
524 let Ok(line) = line else { break };
525 sink(&who, &line);
526 let mut t = tail.lock().unwrap();
527 if t.len() == STDERR_TAIL {
528 t.pop_front();
529 }
530 t.push_back(line);
531 }
532 progress.done.store(true, Ordering::SeqCst);
534 drop(reader);
535 });
536 let serve = parse_executable_name(&label).map(|(k, n)| PluginId::new(k, n));
537 let child = Arc::new(Mutex::new(child));
538 let current = Arc::new(AtomicU64::new(0));
539 Ok(PluginProcess {
540 label,
541 path: path.to_path_buf(),
542 child,
543 stdin,
544 rx,
545 log,
546 last_id: 0,
547 current,
548 stderr,
549 stderr_sync,
550 stderr_thread: Some(stderr_thread),
551 info: None,
552 early: None,
553 last_attempts: 1,
554 serve,
555 })
556 }
557
558 pub fn ask_for(&mut self, plugin: Option<PluginId>) {
560 self.serve = plugin;
561 }
562
563 pub fn handshake(&mut self, (min, max): (u32, u32), timeout: Duration) -> Result<()> {
565 self.send(&Request::Hello {
566 min_version: min,
567 max_version: max,
568 core_version: core_version(),
569 plugin: self.serve.clone(),
570 })?;
571 match self.recv(Some(timeout), "the hello reply")? {
572 Incoming::Json(Response::Hello {
573 protocol_version,
574 kind,
575 name,
576 version,
577 capabilities,
578 mut provides,
579 }) => {
580 if protocol_version < min || protocol_version > max {
581 return Err(HostError::Incompatible {
582 plugin: self.label.clone(),
583 core: (min, max),
584 plugin_range: (protocol_version, protocol_version),
585 });
586 }
587 if let Some(want) = &self.serve
588 && (want.kind != kind || want.name != name)
589 {
590 return Err(HostError::plugin(
591 &self.label,
592 format!(
593 "`{}` was asked for the {want} plugin but serves {kind}/{name}",
594 self.label
595 ),
596 ));
597 }
598 if provides.is_empty() {
599 provides.push(PluginId::new(kind, name.clone()));
600 }
601 self.info = Some(PluginInfo {
602 protocol_version,
603 kind,
604 name,
605 version,
606 capabilities,
607 provides,
608 });
609 Ok(())
610 }
611 Incoming::Json(Response::VersionMismatch {
612 min_version,
613 max_version,
614 }) => Err(HostError::Incompatible {
615 plugin: self.label.clone(),
616 core: (min, max),
617 plugin_range: (min_version, max_version),
618 }),
619 Incoming::Json(Response::Error { message, kind, code }) => Err(HostError::Plugin {
620 plugin: self.label.clone(),
621 message,
622 kind,
623 code,
624 }),
625 other => Err(self.unexpected("a hello reply", &other)),
626 }
627 }
628
629 pub fn label(&self) -> &str {
630 &self.label
631 }
632
633 pub fn path(&self) -> &Path {
634 &self.path
635 }
636
637 pub fn info(&self) -> &PluginInfo {
644 self.info.as_ref().expect("handshake completed")
645 }
646
647 fn try_wait(&self) -> std::io::Result<Option<ExitStatus>> {
649 self.child.lock().unwrap().try_wait()
650 }
651
652 pub fn canceller(&self) -> Canceller {
655 Canceller {
656 stdin: self.stdin.clone(),
657 current: self.current.clone(),
658 child: self.child.clone(),
659 }
660 }
661
662 pub fn stderr_sync(&self) -> StderrSync {
664 self.stderr_sync.clone()
665 }
666
667 fn version(&self) -> u32 {
669 self.info.as_ref().map_or(0, |i| i.protocol_version)
670 }
671
672 pub fn has(&self, capability: &str) -> bool {
673 self.info
674 .as_ref()
675 .is_some_and(|i| i.capabilities.iter().any(|c| c == capability))
676 }
677
678 fn unexpected(&self, expected: &'static str, got: &Incoming) -> HostError {
679 let got = match got {
680 Incoming::Json(r) => format!("{:?}", r)
681 .split([' ', '{'])
682 .next()
683 .unwrap_or("?")
684 .to_string(),
685 Incoming::Arrow(_) => "Arrow data".into(),
686 };
687 HostError::Unexpected {
688 plugin: self.label.clone(),
689 expected,
690 got: format!("`{got}`"),
691 }
692 }
693
694 fn crashed(&mut self) -> HostError {
695 let mut status = None;
697 for _ in 0..50 {
698 if let Ok(Some(s)) = self.try_wait() {
699 status = Some(s);
700 break;
701 }
702 std::thread::sleep(Duration::from_millis(20));
703 }
704 std::thread::sleep(Duration::from_millis(50));
705 let stderr = self.stderr.lock().unwrap().iter().cloned().collect();
706 HostError::Crashed {
707 plugin: self.label.clone(),
708 status,
709 stderr,
710 }
711 }
712
713 pub fn send(&mut self, req: &Request) -> Result<()> {
716 let id = match req {
717 _ if self.version() == 0 => None,
718 Request::Hello { .. } => None,
719 Request::ResultSetEnd {} | Request::Finish {} | Request::Cancel {} | Request::Fonts { .. } => {
720 Some(self.current.load(Ordering::SeqCst)).filter(|id| *id != 0)
721 }
722 _ => {
723 self.last_id += 1;
724 self.current.store(self.last_id, Ordering::SeqCst);
725 Some(self.last_id)
726 }
727 };
728 let ok = self.stdin.lock().is_ok_and(|mut stdin| {
730 stdin
731 .as_mut()
732 .is_some_and(|w| frame::write_json(w, &Envelope::new(id, req)).is_ok())
733 });
734 if ok { Ok(()) } else { Err(self.write_failed()) }
735 }
736
737 pub fn send_batch(&mut self, batch: &RecordBatch) -> Result<()> {
738 if self.early.is_none()
741 && let Ok(f) = self.rx.try_recv()
742 {
743 self.early = Some(f);
744 }
745 if let Some(Ok(Frame::Json(v))) = &self.early
746 && let Ok(Response::Error { message, kind, code }) = serde_json::from_value::<Response>(v.clone())
747 {
748 return Err(HostError::Plugin {
749 plugin: self.label.clone(),
750 message,
751 kind,
752 code,
753 });
754 }
755 let ipc = frame::encode_batch(batch).map_err(|e| HostError::Arrow {
756 plugin: self.label.clone(),
757 message: e.to_string(),
758 })?;
759 let ok = self.stdin.lock().is_ok_and(|mut stdin| {
760 stdin
761 .as_mut()
762 .is_some_and(|w| frame::write_arrow(w, &ipc).is_ok())
763 });
764 if ok { Ok(()) } else { Err(self.write_failed()) }
765 }
766
767 fn write_failed(&mut self) -> HostError {
769 match self.rx.recv_timeout(Duration::from_secs(2)) {
770 Ok(Ok(Frame::Json(v))) => match serde_json::from_value::<Response>(v) {
771 Ok(Response::Error { message, kind, code }) => HostError::Plugin {
772 plugin: self.label.clone(),
773 message,
774 kind,
775 code,
776 },
777 _ => self.crashed(),
778 },
779 _ => self.crashed(),
780 }
781 }
782
783 pub fn recv(&mut self, timeout: Option<Duration>, waiting_for: &'static str) -> Result<Incoming> {
786 loop {
787 match self.recv_any(timeout, waiting_for)? {
788 Incoming::Json(Response::Log {
789 level,
790 message,
791 fields,
792 }) => {
793 (self.log)(&self.label, &log_line(level, &message, &fields));
794 }
795 Incoming::Json(Response::Progress { message, done, total }) => {
796 (self.log)(&self.label, &progress_line(message.as_deref(), done, total));
797 }
798 other => return Ok(other),
799 }
800 }
801 }
802
803 pub fn recv_raw(&mut self, timeout: Duration) -> Result<Value> {
805 match self.rx.recv_timeout(timeout) {
806 Ok(Ok(Frame::Json(v))) => Ok(v),
807 Ok(Ok(Frame::Arrow(_))) => Err(HostError::Malformed {
808 plugin: self.label.clone(),
809 message: "Arrow data where a JSON message was expected".into(),
810 }),
811 Ok(Err(FrameError::Malformed(m))) => Err(HostError::Malformed {
812 plugin: self.label.clone(),
813 message: m,
814 }),
815 Err(RecvTimeoutError::Timeout) => Err(HostError::Timeout {
816 plugin: self.label.clone(),
817 waiting_for: "a message",
818 }),
819 _ => Err(self.crashed()),
820 }
821 }
822
823 pub fn recv_frame(&mut self, timeout: Duration) -> Result<Frame> {
826 match self.rx.recv_timeout(timeout) {
827 Ok(Ok(f)) => Ok(f),
828 Ok(Err(FrameError::Malformed(m))) => Err(HostError::Malformed {
829 plugin: self.label.clone(),
830 message: m,
831 }),
832 Err(RecvTimeoutError::Timeout) => Err(HostError::Timeout {
833 plugin: self.label.clone(),
834 waiting_for: "a message",
835 }),
836 _ => Err(self.crashed()),
837 }
838 }
839
840 fn recv_any(&mut self, timeout: Option<Duration>, waiting_for: &'static str) -> Result<Incoming> {
841 let got = match timeout {
842 _ if self.early.is_some() => self.early.take(),
843 Some(t) => match self.rx.recv_timeout(t) {
844 Ok(f) => Some(f),
845 Err(RecvTimeoutError::Timeout) => {
846 return Err(HostError::Timeout {
847 plugin: self.label.clone(),
848 waiting_for,
849 });
850 }
851 Err(RecvTimeoutError::Disconnected) => None,
852 },
853 None => self.rx.recv().ok(),
854 };
855 match got {
856 Some(Ok(Frame::Json(v))) => match serde_json::from_value::<Envelope<Response>>(v.clone()) {
857 Ok(Envelope { id: Some(id), body })
858 if !matches!(body, Response::Log { .. } | Response::Progress { .. }) =>
859 {
860 let current = self.current.load(Ordering::SeqCst);
861 if id != current {
862 return Err(HostError::Malformed {
863 plugin: self.label.clone(),
864 message: format!(
865 "a reply to request {id} arrived while request {current} was running"
866 ),
867 });
868 }
869 Ok(Incoming::Json(body))
870 }
871 Ok(e) => Ok(Incoming::Json(e.body)),
872 Err(e) => Err(HostError::Malformed {
873 plugin: self.label.clone(),
874 message: format!("unknown message {v}: {e}"),
875 }),
876 },
877 Some(Ok(Frame::Arrow(b))) => Ok(Incoming::Arrow(b)),
878 Some(Err(FrameError::Malformed(m))) => Err(HostError::Malformed {
879 plugin: self.label.clone(),
880 message: m,
881 }),
882 Some(Err(_)) | None => Err(self.crashed()),
883 }
884 }
885
886 pub fn recv_json(&mut self, waiting_for: &'static str) -> Result<Response> {
888 match self.recv(None, waiting_for)? {
889 Incoming::Json(Response::Error { message, kind, code }) => Err(HostError::Plugin {
890 plugin: self.label.clone(),
891 message,
892 kind,
893 code,
894 }),
895 Incoming::Json(r) => Ok(r),
896 other => Err(self.unexpected(waiting_for, &other)),
897 }
898 }
899
900 fn call_ok(&mut self, req: &Request, what: &'static str) -> Result<()> {
902 self.send(req)?;
903 match self.recv_json(what)? {
904 Response::Ok {} => Ok(()),
905 other => Err(self.unexpected(what, &Incoming::Json(other))),
906 }
907 }
908
909 pub fn describe(&mut self) -> Result<Vec<ConnectionField>> {
910 Ok(self.describe_all()?.0)
911 }
912
913 pub fn describe_all(&mut self) -> Result<(Vec<ConnectionField>, Vec<OptionField>)> {
915 let d = self.description()?;
916 Ok((d.connection_fields, d.option_fields))
917 }
918
919 pub fn description(&mut self) -> Result<Description> {
921 self.send(&Request::Describe {})?;
922 match self.recv_json("a describe reply")? {
923 Response::Describe {
924 connection_fields,
925 option_fields,
926 identifier_quote,
927 string_literal,
928 message_limit,
929 single_writer,
930 } => Ok(Description {
931 connection_fields,
932 option_fields,
933 identifier_quote,
934 string_literal,
935 message_limit,
936 single_writer,
937 }),
938 other => Err(self.unexpected("a describe reply", &Incoming::Json(other))),
939 }
940 }
941
942 pub fn validate_connection(
945 &mut self,
946 mut connection: Map<String, Value>,
947 unresolved: Vec<String>,
948 ) -> Result<(Vec<String>, Vec<String>)> {
949 for k in &unresolved {
950 connection.insert(k.clone(), Value::Null);
951 }
952 self.send(&Request::ValidateConnection {
953 connection,
954 unresolved,
955 })?;
956 match self.recv_json("a validated reply")? {
957 Response::Validated { errors, warnings } => Ok((errors, warnings)),
958 other => Err(self.unexpected("a validated reply", &Incoming::Json(other))),
959 }
960 }
961
962 pub fn validate(&mut self, options: Map<String, Value>) -> Result<Vec<String>> {
964 Ok(self.validate_with(options, None)?.0)
965 }
966
967 pub fn validate_with(
970 &mut self,
971 options: Map<String, Value>,
972 template: Option<Value>,
973 ) -> Result<(Vec<String>, Vec<String>)> {
974 self.send(&Request::Validate { options, template })?;
975 match self.recv_json("a validate reply")? {
976 Response::Validated { errors, warnings } => Ok((errors, warnings)),
977 other => Err(self.unexpected("a validate reply", &Incoming::Json(other))),
978 }
979 }
980
981 pub fn open(&mut self, connection: Map<String, Value>, read_only: bool) -> Result<()> {
982 self.call_ok(
983 &Request::Open {
984 connection,
985 read_only,
986 sandbox: false,
987 },
988 "an open reply",
989 )
990 }
991
992 pub fn open_sandboxed(&mut self, connection: Map<String, Value>) -> Result<()> {
995 self.call_ok(
996 &Request::Open {
997 connection,
998 read_only: true,
999 sandbox: true,
1000 },
1001 "an open reply",
1002 )
1003 }
1004
1005 pub fn check(&mut self, sql: &str) -> Result<()> {
1006 self.call_ok(&Request::Check { sql: sql.to_string() }, "a check reply")
1007 }
1008
1009 pub fn catalog(&mut self, tables: Vec<String>, schemas: Vec<String>) -> Result<Value> {
1012 self.send(&Request::Catalog { tables, schemas })?;
1013 match self.recv_json("a catalog reply")? {
1014 Response::Catalog { catalog } => Ok(catalog),
1015 other => Err(self.unexpected("a catalog reply", &Incoming::Json(other))),
1016 }
1017 }
1018
1019 pub fn execute(
1021 &mut self,
1022 sql: &str,
1023 row_limit: Option<u64>,
1024 mut on_batch: impl FnMut(&SchemaRef, RecordBatch) -> std::result::Result<(), String>,
1025 ) -> Result<Execution> {
1026 self.send(&Request::Execute {
1027 sql: sql.to_string(),
1028 row_limit,
1029 })?;
1030 match self.recv_json("an execute reply")? {
1031 Response::NoResult { rows_affected } => return Ok(Execution::NoResult { rows_affected }),
1032 Response::Result { .. } => {}
1033 other => return Err(self.unexpected("an execute reply", &Incoming::Json(other))),
1034 }
1035 let mut schema: Option<SchemaRef> = None;
1036 let mut rows = 0u64;
1037 let mut sink_error = None;
1038 loop {
1039 match self.recv(None, "result data")? {
1040 Incoming::Arrow(ipc) => {
1041 let (s, batches) = frame::decode_batches(&ipc).map_err(|e| HostError::Arrow {
1042 plugin: self.label.clone(),
1043 message: e.to_string(),
1044 })?;
1045 let schema = schema.get_or_insert(s);
1046 for b in batches {
1047 rows += b.num_rows() as u64;
1048 if sink_error.is_none()
1049 && let Err(e) = on_batch(schema, b)
1050 {
1051 sink_error = Some(e);
1053 }
1054 }
1055 }
1056 Incoming::Json(Response::ResultEnd { .. }) => break,
1057 Incoming::Json(Response::Error { message, kind, code }) => {
1058 return Err(HostError::Plugin {
1059 plugin: self.label.clone(),
1060 message,
1061 kind,
1062 code,
1063 });
1064 }
1065 other => return Err(self.unexpected("result data", &other)),
1066 }
1067 }
1068 if let Some(e) = sink_error {
1069 return Err(HostError::plugin(&self.label, e));
1070 }
1071 let schema = schema.ok_or_else(|| HostError::Malformed {
1072 plugin: self.label.clone(),
1073 message: "a result ended without any Arrow frame carrying its schema".into(),
1074 })?;
1075 Ok(Execution::Result { schema, rows })
1076 }
1077
1078 pub fn write_begin(
1080 &mut self,
1081 path: &str,
1082 format: &str,
1083 options: Map<String, Value>,
1084 result_sets: Vec<ResultSetMeta>,
1085 template: Option<Value>,
1086 ) -> Result<()> {
1087 self.write_begin_with_font_requests(path, format, options, result_sets, template, false)
1088 }
1089
1090 pub fn write_begin_with_font_requests(
1092 &mut self,
1093 path: &str,
1094 format: &str,
1095 options: Map<String, Value>,
1096 result_sets: Vec<ResultSetMeta>,
1097 template: Option<Value>,
1098 font_requests: bool,
1099 ) -> Result<()> {
1100 self.send(&Request::Write {
1101 font_requests,
1102 path: path.to_string(),
1103 format: format.to_string(),
1104 options,
1105 result_sets,
1106 template,
1107 })
1108 }
1109
1110 pub fn write_result_set(
1112 &mut self,
1113 schema: &SchemaRef,
1114 batches: impl IntoIterator<Item = RecordBatch>,
1115 ) -> Result<()> {
1116 let mut any = false;
1117 for b in batches {
1118 any = true;
1119 self.send_batch(&b)?;
1120 }
1121 if !any {
1122 self.send_batch(&RecordBatch::new_empty(schema.clone()))?;
1123 }
1124 self.send(&Request::ResultSetEnd {})
1125 }
1126
1127 pub fn load(
1130 &mut self,
1131 name: &str,
1132 schema: &SchemaRef,
1133 batches: impl IntoIterator<Item = RecordBatch>,
1134 ) -> Result<(String, Option<String>)> {
1135 self.send(&Request::Load {
1136 name: name.to_string(),
1137 })?;
1138 self.write_result_set(schema, batches)?;
1139 match self.recv_json("a loaded reply")? {
1140 Response::Loaded {
1141 relation, warning, ..
1142 } => Ok((relation, warning)),
1143 other => Err(self.unexpected("a loaded reply", &Incoming::Json(other))),
1144 }
1145 }
1146
1147 pub fn write_finish(&mut self) -> Result<Vec<String>> {
1148 Ok(self.write_finish_with_warnings()?.0)
1149 }
1150
1151 pub fn write_finish_with_warnings(&mut self) -> Result<(Vec<String>, Vec<String>)> {
1153 self.write_finish_with_fonts(&mut |_| Err("this host cannot fetch fonts".into()))
1154 }
1155
1156 pub fn write_finish_with_fonts(
1158 &mut self,
1159 resolve: &mut dyn FnMut(
1160 &crate::msg::FontRequest,
1161 ) -> std::result::Result<Vec<String>, crate::msg::FontError>,
1162 ) -> Result<(Vec<String>, Vec<String>)> {
1163 self.send(&Request::Finish {})?;
1164 loop {
1165 match self.recv_json("a written reply")? {
1166 Response::NeedFonts { font } => {
1167 let reply = match resolve(&font) {
1168 Ok(paths) => Request::Fonts {
1169 paths,
1170 error: None,
1171 repeated: false,
1172 },
1173 Err(e) => Request::Fonts {
1174 paths: Vec::new(),
1175 error: Some(e.message),
1176 repeated: e.repeated,
1177 },
1178 };
1179 self.send(&reply)?;
1180 }
1181 Response::Written { files, warnings } => return Ok((files, warnings)),
1182 other => return Err(self.unexpected("a written reply", &Incoming::Json(other))),
1183 }
1184 }
1185 }
1186
1187 pub fn deliver(
1189 &mut self,
1190 local_path: &str,
1191 remote_path: Option<&str>,
1192 connection: Map<String, Value>,
1193 ) -> Result<String> {
1194 let file = DeliveryFile {
1195 local_path: local_path.to_string(),
1196 remote_path: remote_path.map(str::to_string),
1197 };
1198 self.deliver_files(&[file], connection, Map::new())
1199 }
1200
1201 pub fn deliver_files(
1204 &mut self,
1205 files: &[DeliveryFile],
1206 connection: Map<String, Value>,
1207 options: Map<String, Value>,
1208 ) -> Result<String> {
1209 let req = match files {
1210 [] => {
1211 return Err(HostError::Malformed {
1212 plugin: self.label.clone(),
1213 message: "nothing to deliver".into(),
1214 });
1215 }
1216 [one] => Request::Deliver {
1217 local_path: Some(one.local_path.clone()),
1218 remote_path: one.remote_path.clone(),
1219 files: Vec::new(),
1220 connection,
1221 options,
1222 message: None,
1223 },
1224 many => {
1225 if !self.has(crate::CAP_MULTI_FILE) {
1226 return Err(HostError::plugin(
1227 &self.label,
1228 format!(
1229 "{} files in one delivery, but the plugin doesn't advertise `multi_file`",
1230 many.len()
1231 ),
1232 ));
1233 }
1234 Request::Deliver {
1235 local_path: None,
1236 remote_path: None,
1237 files: many.to_vec(),
1238 connection,
1239 options,
1240 message: None,
1241 }
1242 }
1243 };
1244 self.send(&req)?;
1245 match self.recv_json("a delivered reply")? {
1246 Response::Delivered { location, attempts } => {
1247 self.last_attempts = attempts.unwrap_or(1);
1248 Ok(location)
1249 }
1250 other => Err(self.unexpected("a delivered reply", &Incoming::Json(other))),
1251 }
1252 }
1253
1254 pub fn deliver_message(
1256 &mut self,
1257 message: &crate::msg::Message,
1258 attach: &[DeliveryFile],
1259 connection: Map<String, Value>,
1260 options: Map<String, Value>,
1261 ) -> Result<String> {
1262 if !self.has(crate::CAP_MESSAGE) {
1263 return Err(HostError::plugin(
1264 &self.label,
1265 "a message, but the plugin doesn't advertise `message`",
1266 ));
1267 }
1268 self.send(&Request::Deliver {
1269 local_path: None,
1270 remote_path: None,
1271 files: attach.to_vec(),
1272 connection,
1273 options,
1274 message: Some(message.clone()),
1275 })?;
1276 match self.recv_json("a delivered reply")? {
1277 Response::Delivered { location, attempts } => {
1278 self.last_attempts = attempts.unwrap_or(1);
1279 Ok(location)
1280 }
1281 other => Err(self.unexpected("a delivered reply", &Incoming::Json(other))),
1282 }
1283 }
1284
1285 pub fn last_attempts(&self) -> u32 {
1288 self.last_attempts
1289 }
1290
1291 pub fn close(mut self) -> Result<()> {
1293 let _ = self.send(&Request::Close {});
1294 let _ = self.recv(Some(Duration::from_secs(5)), "the close reply");
1295 if let Ok(mut stdin) = self.stdin.lock() {
1296 stdin.take();
1297 }
1298 for _ in 0..250 {
1299 if let Ok(Some(_)) = self.try_wait() {
1300 return Ok(());
1301 }
1302 std::thread::sleep(Duration::from_millis(20));
1303 }
1304 if let Ok(mut child) = self.child.lock() {
1305 let _ = child.kill();
1306 }
1307 Ok(())
1308 }
1309
1310 pub fn write_raw(&mut self, bytes: &[u8]) -> Result<()> {
1312 let ok = self.stdin.lock().is_ok_and(|mut stdin| {
1313 stdin
1314 .as_mut()
1315 .is_some_and(|w| w.write_all(bytes).and_then(|_| w.flush()).is_ok())
1316 });
1317 if ok { Ok(()) } else { Err(self.write_failed()) }
1318 }
1319
1320 pub fn close_input(&mut self) {
1322 if let Ok(mut stdin) = self.stdin.lock() {
1323 stdin.take();
1324 }
1325 }
1326
1327 pub fn wait_exit(&mut self, timeout: Duration) -> Option<ExitStatus> {
1329 if let Ok(mut stdin) = self.stdin.lock() {
1330 stdin.take();
1331 }
1332 let deadline = std::time::Instant::now() + timeout;
1333 while std::time::Instant::now() < deadline {
1334 if let Ok(Some(s)) = self.try_wait() {
1335 return Some(s);
1336 }
1337 std::thread::sleep(Duration::from_millis(20));
1338 }
1339 None
1340 }
1341}
1342
1343impl PluginProcess {
1344 fn drain_stderr(&mut self) {
1346 if let Some(t) = self.stderr_thread.take() {
1347 let deadline = std::time::Instant::now() + Duration::from_secs(2);
1348 while !t.is_finished() && std::time::Instant::now() < deadline {
1349 std::thread::sleep(Duration::from_millis(5));
1350 }
1351 if t.is_finished() {
1352 let _ = t.join();
1353 }
1354 }
1355 }
1356}
1357
1358impl Drop for PluginProcess {
1359 fn drop(&mut self) {
1360 if let Ok(mut stdin) = self.stdin.lock() {
1361 stdin.take();
1362 }
1363 if let Ok(None) = self.try_wait() {
1364 for _ in 0..25 {
1366 if let Ok(Some(_)) = self.try_wait() {
1367 break;
1368 }
1369 std::thread::sleep(Duration::from_millis(10));
1370 }
1371 let _ = self.child.lock().unwrap().kill();
1372 let _ = self.child.lock().unwrap().wait();
1373 }
1374 self.drain_stderr();
1375 }
1376}
1377
1378fn log_line(level: LogLevel, message: &str, fields: &Map<String, Value>) -> String {
1380 let prefix = match level {
1381 LogLevel::Error | LogLevel::Warn => "warning: ",
1382 LogLevel::Info => "info: ",
1383 LogLevel::Debug | LogLevel::Trace => "debug: ",
1384 };
1385 let mut line = format!("{prefix}{message}");
1386 for (k, v) in fields {
1387 match v {
1388 Value::String(s) => line.push_str(&format!(" {k}={s}")),
1389 v => line.push_str(&format!(" {k}={v}")),
1390 }
1391 }
1392 line
1393}
1394
1395fn progress_line(message: Option<&str>, done: Option<u64>, total: Option<u64>) -> String {
1397 let count = match (done, total) {
1398 (Some(d), Some(t)) => format!("{d}/{t}"),
1399 (Some(d), None) => d.to_string(),
1400 _ => String::new(),
1401 };
1402 match (message, count.is_empty()) {
1403 (Some(m), true) => format!("info: {m}"),
1404 (Some(m), false) => format!("info: {m} ({count})"),
1405 (None, _) => format!("info: {count}"),
1406 }
1407}
1408
1409static CORE_VERSION: OnceLock<String> = OnceLock::new();
1410
1411pub fn set_core_version(version: &str) {
1414 let _ = CORE_VERSION.set(version.to_string());
1415}
1416
1417fn core_version() -> String {
1418 CORE_VERSION.get().cloned().unwrap_or_else(|| "unreleased".into())
1419}