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 let mut stdin = self.stdin.lock().unwrap();
316 let Some(w) = stdin.as_mut() else {
317 return false;
318 };
319 frame::write_json(w, &Envelope::new(Some(id), Request::Cancel {})).is_ok()
320 }
321
322 pub fn kill(&self) {
324 if let Ok(mut c) = self.child.lock() {
325 let _ = c.kill();
326 }
327 }
328}
329
330static RUNNING: Mutex<Vec<(u64, Canceller)>> = Mutex::new(Vec::new());
333static NEXT_PROCESS: AtomicU64 = AtomicU64::new(1);
334
335pub fn cancel_all() -> usize {
337 let all: Vec<Canceller> = RUNNING.lock().unwrap().iter().map(|(_, c)| c.clone()).collect();
338 all.iter().filter(|c| c.cancel()).count()
339}
340
341pub fn kill_all() {
343 let all: Vec<Canceller> = RUNNING.lock().unwrap().iter().map(|(_, c)| c.clone()).collect();
344 for c in all {
345 c.kill();
346 }
347}
348
349pub fn running() -> usize {
351 RUNNING.lock().unwrap().len()
352}
353pub enum Incoming {
355 Json(Response),
356 Arrow(Vec<u8>),
357}
358
359impl std::fmt::Debug for Incoming {
360 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
361 match self {
362 Incoming::Json(r) => write!(f, "Json({r:?})"),
363 Incoming::Arrow(b) => write!(f, "Arrow frame ({} bytes)", b.len()),
364 }
365 }
366}
367
368#[derive(Debug)]
370pub enum Execution {
371 NoResult { rows_affected: Option<u64> },
372 Result { schema: SchemaRef, rows: u64 },
373}
374
375pub struct PluginProcess {
376 label: String,
377 path: PathBuf,
378 child: Arc<Mutex<Child>>,
380 registered: u64,
382 stdin: Arc<Mutex<Option<BufWriter<ChildStdin>>>>,
384 rx: Receiver<std::result::Result<Frame, FrameError>>,
385 log: LogSink,
386 last_id: u64,
388 current: Arc<AtomicU64>,
390 stderr: Arc<Mutex<VecDeque<String>>>,
391 stderr_sync: StderrSync,
392 stderr_thread: Option<std::thread::JoinHandle<()>>,
394 info: Option<PluginInfo>,
395 early: Option<std::result::Result<Frame, FrameError>>,
397 last_attempts: u32,
399 serve: Option<PluginId>,
402}
403
404impl PluginProcess {
405 pub fn start(path: &Path, log: LogSink) -> Result<PluginProcess> {
407 Self::start_in(path, log, None)
408 }
409
410 pub fn start_in(path: &Path, log: LogSink, cwd: Option<&Path>) -> Result<PluginProcess> {
412 Self::start_for(path, None, log, cwd)
413 }
414
415 pub fn start_for(
417 path: &Path,
418 plugin: Option<&PluginId>,
419 log: LogSink,
420 cwd: Option<&Path>,
421 ) -> Result<PluginProcess> {
422 let timeout = std::env::var("DRE_PLUGIN_HANDSHAKE_TIMEOUT_MS")
423 .ok()
424 .and_then(|v| v.parse().ok())
425 .map(Duration::from_millis)
426 .unwrap_or(DEFAULT_HANDSHAKE_TIMEOUT);
427 let mut p = Self::spawn_in(path, log, &[], cwd)?;
428 if let Some(id) = plugin {
429 p.serve = Some(id.clone());
430 }
431 p.handshake((CORE_MIN_VERSION, MAX_VERSION), timeout)?;
432 Ok(p)
433 }
434
435 pub fn start_with(
437 path: &Path,
438 log: LogSink,
439 versions: (u32, u32),
440 timeout: Duration,
441 ) -> Result<PluginProcess> {
442 let mut p = Self::spawn(path, log)?;
443 p.handshake(versions, timeout)?;
444 Ok(p)
445 }
446
447 pub fn spawn(path: &Path, log: LogSink) -> Result<PluginProcess> {
449 Self::spawn_env(path, log, &[])
450 }
451
452 pub fn spawn_env(path: &Path, log: LogSink, env: &[(&str, &str)]) -> Result<PluginProcess> {
454 Self::spawn_in(path, log, env, None)
455 }
456
457 pub fn spawn_in(
459 path: &Path,
460 log: LogSink,
461 env: &[(&str, &str)],
462 cwd: Option<&Path>,
463 ) -> Result<PluginProcess> {
464 let label = path
465 .file_name()
466 .map(|f| f.to_string_lossy().to_string())
467 .unwrap_or_default();
468 let mut cmd = Command::new(path);
469 if let Some(dir) = cwd {
470 cmd.current_dir(dir);
471 }
472 if std::env::var_os(INTERACTIVE_ENV).is_none() {
475 use std::io::IsTerminal;
476 let there = std::io::stderr().is_terminal() && std::io::stdin().is_terminal();
477 cmd.env(INTERACTIVE_ENV, if there { "1" } else { "0" });
478 }
479 #[cfg(unix)]
482 {
483 use std::os::unix::process::CommandExt;
484 cmd.process_group(0);
485 }
486 #[cfg(windows)]
487 {
488 use std::os::windows::process::CommandExt;
489 const CREATE_NEW_PROCESS_GROUP: u32 = 0x0000_0200;
490 cmd.creation_flags(CREATE_NEW_PROCESS_GROUP);
491 }
492 let mut child = cmd
493 .envs(env.iter().copied())
494 .stdin(Stdio::piped())
495 .stdout(Stdio::piped())
496 .stderr(Stdio::piped())
497 .spawn()
498 .map_err(|error| HostError::Spawn {
499 plugin: label.clone(),
500 error,
501 })?;
502 let stdout = child.stdout.take().unwrap();
503 let stderr_pipe = child.stderr.take().unwrap();
504 let stdin = Arc::new(Mutex::new(child.stdin.take().map(BufWriter::new)));
505
506 let (tx, rx) = channel();
507 std::thread::spawn(move || {
508 let mut r = BufReader::new(stdout);
509 loop {
510 let f = frame::read_frame(&mut r);
511 let stop = f.is_err();
512 if tx.send(f).is_err() || stop {
513 break;
514 }
515 }
516 });
517 let stderr = Arc::new(Mutex::new(VecDeque::new()));
518 let tail = stderr.clone();
519 let who = label.clone();
520 let sink = log.clone();
521 let stderr_sync = StderrSync {
522 progress: Arc::default(),
523 pipe: raw_pipe(&stderr_pipe),
524 };
525 let progress = stderr_sync.progress.clone();
526 let stderr_thread = std::thread::spawn(move || {
527 let mut reader = BufReader::new(TrackedPipe {
528 pipe: stderr_pipe,
529 progress: progress.clone(),
530 });
531 for line in reader.by_ref().lines() {
532 let Ok(line) = line else { break };
533 sink(&who, &line);
534 let mut t = tail.lock().unwrap();
535 if t.len() == STDERR_TAIL {
536 t.pop_front();
537 }
538 t.push_back(line);
539 }
540 progress.done.store(true, Ordering::SeqCst);
542 drop(reader);
543 });
544 let serve = parse_executable_name(&label).map(|(k, n)| PluginId::new(k, n));
545 let child = Arc::new(Mutex::new(child));
546 let current = Arc::new(AtomicU64::new(0));
547 let registered = NEXT_PROCESS.fetch_add(1, Ordering::SeqCst);
548 RUNNING.lock().unwrap().push((
549 registered,
550 Canceller {
551 stdin: stdin.clone(),
552 current: current.clone(),
553 child: child.clone(),
554 },
555 ));
556 Ok(PluginProcess {
557 label,
558 path: path.to_path_buf(),
559 child,
560 registered,
561 stdin,
562 rx,
563 log,
564 last_id: 0,
565 current,
566 stderr,
567 stderr_sync,
568 stderr_thread: Some(stderr_thread),
569 info: None,
570 early: None,
571 last_attempts: 1,
572 serve,
573 })
574 }
575
576 pub fn ask_for(&mut self, plugin: Option<PluginId>) {
578 self.serve = plugin;
579 }
580
581 pub fn handshake(&mut self, (min, max): (u32, u32), timeout: Duration) -> Result<()> {
583 self.send(&Request::Hello {
584 min_version: min,
585 max_version: max,
586 core_version: core_version(),
587 plugin: self.serve.clone(),
588 })?;
589 match self.recv(Some(timeout), "the hello reply")? {
590 Incoming::Json(Response::Hello {
591 protocol_version,
592 kind,
593 name,
594 version,
595 capabilities,
596 mut provides,
597 }) => {
598 if protocol_version < min || protocol_version > max {
599 return Err(HostError::Incompatible {
600 plugin: self.label.clone(),
601 core: (min, max),
602 plugin_range: (protocol_version, protocol_version),
603 });
604 }
605 if let Some(want) = &self.serve
606 && (want.kind != kind || want.name != name)
607 {
608 return Err(HostError::plugin(
609 &self.label,
610 format!(
611 "`{}` was asked for the {want} plugin but serves {kind}/{name}",
612 self.label
613 ),
614 ));
615 }
616 if provides.is_empty() {
617 provides.push(PluginId::new(kind, name.clone()));
618 }
619 self.info = Some(PluginInfo {
620 protocol_version,
621 kind,
622 name,
623 version,
624 capabilities,
625 provides,
626 });
627 Ok(())
628 }
629 Incoming::Json(Response::VersionMismatch {
630 min_version,
631 max_version,
632 }) => Err(HostError::Incompatible {
633 plugin: self.label.clone(),
634 core: (min, max),
635 plugin_range: (min_version, max_version),
636 }),
637 Incoming::Json(Response::Error { message, kind, code }) => Err(HostError::Plugin {
638 plugin: self.label.clone(),
639 message,
640 kind,
641 code,
642 }),
643 other => Err(self.unexpected("a hello reply", &other)),
644 }
645 }
646
647 pub fn label(&self) -> &str {
648 &self.label
649 }
650
651 pub fn path(&self) -> &Path {
652 &self.path
653 }
654
655 pub fn info(&self) -> &PluginInfo {
656 self.info.as_ref().expect("handshake completed")
657 }
658
659 fn try_wait(&self) -> std::io::Result<Option<ExitStatus>> {
661 self.child.lock().unwrap().try_wait()
662 }
663
664 pub fn canceller(&self) -> Canceller {
667 Canceller {
668 stdin: self.stdin.clone(),
669 current: self.current.clone(),
670 child: self.child.clone(),
671 }
672 }
673
674 pub fn stderr_sync(&self) -> StderrSync {
676 self.stderr_sync.clone()
677 }
678
679 fn version(&self) -> u32 {
681 self.info.as_ref().map_or(0, |i| i.protocol_version)
682 }
683
684 pub fn has(&self, capability: &str) -> bool {
685 self.info
686 .as_ref()
687 .is_some_and(|i| i.capabilities.iter().any(|c| c == capability))
688 }
689
690 fn unexpected(&self, expected: &'static str, got: &Incoming) -> HostError {
691 let got = match got {
692 Incoming::Json(r) => format!("{:?}", r)
693 .split([' ', '{'])
694 .next()
695 .unwrap_or("?")
696 .to_string(),
697 Incoming::Arrow(_) => "Arrow data".into(),
698 };
699 HostError::Unexpected {
700 plugin: self.label.clone(),
701 expected,
702 got: format!("`{got}`"),
703 }
704 }
705
706 fn crashed(&mut self) -> HostError {
707 let mut status = None;
709 for _ in 0..50 {
710 if let Ok(Some(s)) = self.try_wait() {
711 status = Some(s);
712 break;
713 }
714 std::thread::sleep(Duration::from_millis(20));
715 }
716 std::thread::sleep(Duration::from_millis(50));
717 let stderr = self.stderr.lock().unwrap().iter().cloned().collect();
718 HostError::Crashed {
719 plugin: self.label.clone(),
720 status,
721 stderr,
722 }
723 }
724
725 pub fn send(&mut self, req: &Request) -> Result<()> {
728 let id = match req {
729 _ if self.version() == 0 => None,
730 Request::Hello { .. } => None,
731 Request::ResultSetEnd {} | Request::Finish {} | Request::Cancel {} => {
732 Some(self.current.load(Ordering::SeqCst)).filter(|id| *id != 0)
733 }
734 _ => {
735 self.last_id += 1;
736 self.current.store(self.last_id, Ordering::SeqCst);
737 Some(self.last_id)
738 }
739 };
740 let ok = match self.stdin.lock().unwrap().as_mut() {
741 Some(w) => frame::write_json(w, &Envelope::new(id, req)).is_ok(),
742 None => false,
743 };
744 if ok { Ok(()) } else { Err(self.write_failed()) }
745 }
746
747 pub fn send_batch(&mut self, batch: &RecordBatch) -> Result<()> {
748 if self.early.is_none()
751 && let Ok(f) = self.rx.try_recv()
752 {
753 self.early = Some(f);
754 }
755 if let Some(Ok(Frame::Json(v))) = &self.early
756 && let Ok(Response::Error { message, kind, code }) = serde_json::from_value::<Response>(v.clone())
757 {
758 return Err(HostError::Plugin {
759 plugin: self.label.clone(),
760 message,
761 kind,
762 code,
763 });
764 }
765 let ipc = frame::encode_batch(batch).map_err(|e| HostError::Arrow {
766 plugin: self.label.clone(),
767 message: e.to_string(),
768 })?;
769 let ok = match self.stdin.lock().unwrap().as_mut() {
770 Some(w) => frame::write_arrow(w, &ipc).is_ok(),
771 None => false,
772 };
773 if ok { Ok(()) } else { Err(self.write_failed()) }
774 }
775
776 fn write_failed(&mut self) -> HostError {
778 match self.rx.recv_timeout(Duration::from_secs(2)) {
779 Ok(Ok(Frame::Json(v))) => match serde_json::from_value::<Response>(v) {
780 Ok(Response::Error { message, kind, code }) => HostError::Plugin {
781 plugin: self.label.clone(),
782 message,
783 kind,
784 code,
785 },
786 _ => self.crashed(),
787 },
788 _ => self.crashed(),
789 }
790 }
791
792 pub fn recv(&mut self, timeout: Option<Duration>, waiting_for: &'static str) -> Result<Incoming> {
795 loop {
796 match self.recv_any(timeout, waiting_for)? {
797 Incoming::Json(Response::Log {
798 level,
799 message,
800 fields,
801 }) => {
802 (self.log)(&self.label, &log_line(level, &message, &fields));
803 }
804 Incoming::Json(Response::Progress { message, done, total }) => {
805 (self.log)(&self.label, &progress_line(message.as_deref(), done, total));
806 }
807 other => return Ok(other),
808 }
809 }
810 }
811
812 pub fn recv_raw(&mut self, timeout: Duration) -> Result<Value> {
814 match self.rx.recv_timeout(timeout) {
815 Ok(Ok(Frame::Json(v))) => Ok(v),
816 Ok(Ok(Frame::Arrow(_))) => Err(HostError::Malformed {
817 plugin: self.label.clone(),
818 message: "Arrow data where a JSON message was expected".into(),
819 }),
820 Ok(Err(FrameError::Malformed(m))) => Err(HostError::Malformed {
821 plugin: self.label.clone(),
822 message: m,
823 }),
824 Err(RecvTimeoutError::Timeout) => Err(HostError::Timeout {
825 plugin: self.label.clone(),
826 waiting_for: "a message",
827 }),
828 _ => Err(self.crashed()),
829 }
830 }
831
832 fn recv_any(&mut self, timeout: Option<Duration>, waiting_for: &'static str) -> Result<Incoming> {
833 let got = match timeout {
834 _ if self.early.is_some() => self.early.take(),
835 Some(t) => match self.rx.recv_timeout(t) {
836 Ok(f) => Some(f),
837 Err(RecvTimeoutError::Timeout) => {
838 return Err(HostError::Timeout {
839 plugin: self.label.clone(),
840 waiting_for,
841 });
842 }
843 Err(RecvTimeoutError::Disconnected) => None,
844 },
845 None => self.rx.recv().ok(),
846 };
847 match got {
848 Some(Ok(Frame::Json(v))) => match serde_json::from_value::<Envelope<Response>>(v.clone()) {
849 Ok(Envelope { id: Some(id), body })
850 if !matches!(body, Response::Log { .. } | Response::Progress { .. }) =>
851 {
852 let current = self.current.load(Ordering::SeqCst);
853 if id != current {
854 return Err(HostError::Malformed {
855 plugin: self.label.clone(),
856 message: format!(
857 "a reply to request {id} arrived while request {current} was running"
858 ),
859 });
860 }
861 Ok(Incoming::Json(body))
862 }
863 Ok(e) => Ok(Incoming::Json(e.body)),
864 Err(e) => Err(HostError::Malformed {
865 plugin: self.label.clone(),
866 message: format!("unknown message {v}: {e}"),
867 }),
868 },
869 Some(Ok(Frame::Arrow(b))) => Ok(Incoming::Arrow(b)),
870 Some(Err(FrameError::Malformed(m))) => Err(HostError::Malformed {
871 plugin: self.label.clone(),
872 message: m,
873 }),
874 Some(Err(_)) | None => Err(self.crashed()),
875 }
876 }
877
878 pub fn recv_json(&mut self, waiting_for: &'static str) -> Result<Response> {
880 match self.recv(None, waiting_for)? {
881 Incoming::Json(Response::Error { message, kind, code }) => Err(HostError::Plugin {
882 plugin: self.label.clone(),
883 message,
884 kind,
885 code,
886 }),
887 Incoming::Json(r) => Ok(r),
888 other => Err(self.unexpected(waiting_for, &other)),
889 }
890 }
891
892 fn call_ok(&mut self, req: &Request, what: &'static str) -> Result<()> {
894 self.send(req)?;
895 match self.recv_json(what)? {
896 Response::Ok {} => Ok(()),
897 other => Err(self.unexpected(what, &Incoming::Json(other))),
898 }
899 }
900
901 pub fn describe(&mut self) -> Result<Vec<ConnectionField>> {
902 Ok(self.describe_all()?.0)
903 }
904
905 pub fn describe_all(&mut self) -> Result<(Vec<ConnectionField>, Vec<OptionField>)> {
907 let d = self.description()?;
908 Ok((d.connection_fields, d.option_fields))
909 }
910
911 pub fn description(&mut self) -> Result<Description> {
913 self.send(&Request::Describe {})?;
914 match self.recv_json("a describe reply")? {
915 Response::Describe {
916 connection_fields,
917 option_fields,
918 identifier_quote,
919 string_literal,
920 message_limit,
921 } => Ok(Description {
922 connection_fields,
923 option_fields,
924 identifier_quote,
925 string_literal,
926 message_limit,
927 }),
928 other => Err(self.unexpected("a describe reply", &Incoming::Json(other))),
929 }
930 }
931
932 pub fn validate_connection(
935 &mut self,
936 mut connection: Map<String, Value>,
937 unresolved: Vec<String>,
938 ) -> Result<(Vec<String>, Vec<String>)> {
939 for k in &unresolved {
940 connection.insert(k.clone(), Value::Null);
941 }
942 self.send(&Request::ValidateConnection {
943 connection,
944 unresolved,
945 })?;
946 match self.recv_json("a validated reply")? {
947 Response::Validated { errors, warnings } => Ok((errors, warnings)),
948 other => Err(self.unexpected("a validated reply", &Incoming::Json(other))),
949 }
950 }
951
952 pub fn validate(&mut self, options: Map<String, Value>) -> Result<Vec<String>> {
954 Ok(self.validate_with(options, None)?.0)
955 }
956
957 pub fn validate_with(
960 &mut self,
961 options: Map<String, Value>,
962 template: Option<Value>,
963 ) -> Result<(Vec<String>, Vec<String>)> {
964 self.send(&Request::Validate { options, template })?;
965 match self.recv_json("a validate reply")? {
966 Response::Validated { errors, warnings } => Ok((errors, warnings)),
967 other => Err(self.unexpected("a validate reply", &Incoming::Json(other))),
968 }
969 }
970
971 pub fn open(&mut self, connection: Map<String, Value>, read_only: bool) -> Result<()> {
972 self.call_ok(
973 &Request::Open {
974 connection,
975 read_only,
976 sandbox: false,
977 },
978 "an open reply",
979 )
980 }
981
982 pub fn open_sandboxed(&mut self, connection: Map<String, Value>) -> Result<()> {
985 self.call_ok(
986 &Request::Open {
987 connection,
988 read_only: true,
989 sandbox: true,
990 },
991 "an open reply",
992 )
993 }
994
995 pub fn check(&mut self, sql: &str) -> Result<()> {
996 self.call_ok(&Request::Check { sql: sql.to_string() }, "a check reply")
997 }
998
999 pub fn execute(
1001 &mut self,
1002 sql: &str,
1003 row_limit: Option<u64>,
1004 mut on_batch: impl FnMut(&SchemaRef, RecordBatch) -> std::result::Result<(), String>,
1005 ) -> Result<Execution> {
1006 self.send(&Request::Execute {
1007 sql: sql.to_string(),
1008 row_limit,
1009 })?;
1010 match self.recv_json("an execute reply")? {
1011 Response::NoResult { rows_affected } => return Ok(Execution::NoResult { rows_affected }),
1012 Response::Result { .. } => {}
1013 other => return Err(self.unexpected("an execute reply", &Incoming::Json(other))),
1014 }
1015 let mut schema: Option<SchemaRef> = None;
1016 let mut rows = 0u64;
1017 let mut sink_error = None;
1018 loop {
1019 match self.recv(None, "result data")? {
1020 Incoming::Arrow(ipc) => {
1021 let (s, batches) = frame::decode_batches(&ipc).map_err(|e| HostError::Arrow {
1022 plugin: self.label.clone(),
1023 message: e.to_string(),
1024 })?;
1025 let schema = schema.get_or_insert(s);
1026 for b in batches {
1027 rows += b.num_rows() as u64;
1028 if sink_error.is_none()
1029 && let Err(e) = on_batch(schema, b)
1030 {
1031 sink_error = Some(e);
1033 }
1034 }
1035 }
1036 Incoming::Json(Response::ResultEnd { .. }) => break,
1037 Incoming::Json(Response::Error { message, kind, code }) => {
1038 return Err(HostError::Plugin {
1039 plugin: self.label.clone(),
1040 message,
1041 kind,
1042 code,
1043 });
1044 }
1045 other => return Err(self.unexpected("result data", &other)),
1046 }
1047 }
1048 if let Some(e) = sink_error {
1049 return Err(HostError::plugin(&self.label, e));
1050 }
1051 let schema = schema.ok_or_else(|| HostError::Malformed {
1052 plugin: self.label.clone(),
1053 message: "a result ended without any Arrow frame carrying its schema".into(),
1054 })?;
1055 Ok(Execution::Result { schema, rows })
1056 }
1057
1058 pub fn write_begin(
1060 &mut self,
1061 path: &str,
1062 format: &str,
1063 options: Map<String, Value>,
1064 result_sets: Vec<ResultSetMeta>,
1065 template: Option<Value>,
1066 ) -> Result<()> {
1067 self.send(&Request::Write {
1068 path: path.to_string(),
1069 format: format.to_string(),
1070 options,
1071 result_sets,
1072 template,
1073 })
1074 }
1075
1076 pub fn write_result_set(
1078 &mut self,
1079 schema: &SchemaRef,
1080 batches: impl IntoIterator<Item = RecordBatch>,
1081 ) -> Result<()> {
1082 let mut any = false;
1083 for b in batches {
1084 any = true;
1085 self.send_batch(&b)?;
1086 }
1087 if !any {
1088 self.send_batch(&RecordBatch::new_empty(schema.clone()))?;
1089 }
1090 self.send(&Request::ResultSetEnd {})
1091 }
1092
1093 pub fn load(
1096 &mut self,
1097 name: &str,
1098 schema: &SchemaRef,
1099 batches: impl IntoIterator<Item = RecordBatch>,
1100 ) -> Result<(String, Option<String>)> {
1101 self.send(&Request::Load {
1102 name: name.to_string(),
1103 })?;
1104 self.write_result_set(schema, batches)?;
1105 match self.recv_json("a loaded reply")? {
1106 Response::Loaded {
1107 relation, warning, ..
1108 } => Ok((relation, warning)),
1109 other => Err(self.unexpected("a loaded reply", &Incoming::Json(other))),
1110 }
1111 }
1112
1113 pub fn write_finish(&mut self) -> Result<Vec<String>> {
1114 Ok(self.write_finish_with_warnings()?.0)
1115 }
1116
1117 pub fn write_finish_with_warnings(&mut self) -> Result<(Vec<String>, Vec<String>)> {
1119 self.send(&Request::Finish {})?;
1120 match self.recv_json("a written reply")? {
1121 Response::Written { files, warnings } => Ok((files, warnings)),
1122 other => Err(self.unexpected("a written reply", &Incoming::Json(other))),
1123 }
1124 }
1125
1126 pub fn deliver(
1128 &mut self,
1129 local_path: &str,
1130 remote_path: Option<&str>,
1131 connection: Map<String, Value>,
1132 ) -> Result<String> {
1133 let file = DeliveryFile {
1134 local_path: local_path.to_string(),
1135 remote_path: remote_path.map(str::to_string),
1136 };
1137 self.deliver_files(&[file], connection, Map::new())
1138 }
1139
1140 pub fn deliver_files(
1143 &mut self,
1144 files: &[DeliveryFile],
1145 connection: Map<String, Value>,
1146 options: Map<String, Value>,
1147 ) -> Result<String> {
1148 let req = match files {
1149 [] => {
1150 return Err(HostError::Malformed {
1151 plugin: self.label.clone(),
1152 message: "nothing to deliver".into(),
1153 });
1154 }
1155 [one] => Request::Deliver {
1156 local_path: Some(one.local_path.clone()),
1157 remote_path: one.remote_path.clone(),
1158 files: Vec::new(),
1159 connection,
1160 options,
1161 message: None,
1162 },
1163 many => {
1164 if !self.has(crate::CAP_MULTI_FILE) {
1165 return Err(HostError::plugin(
1166 &self.label,
1167 format!(
1168 "{} files in one delivery, but the plugin doesn't advertise `multi_file`",
1169 many.len()
1170 ),
1171 ));
1172 }
1173 Request::Deliver {
1174 local_path: None,
1175 remote_path: None,
1176 files: many.to_vec(),
1177 connection,
1178 options,
1179 message: None,
1180 }
1181 }
1182 };
1183 self.send(&req)?;
1184 match self.recv_json("a delivered reply")? {
1185 Response::Delivered { location, attempts } => {
1186 self.last_attempts = attempts.unwrap_or(1);
1187 Ok(location)
1188 }
1189 other => Err(self.unexpected("a delivered reply", &Incoming::Json(other))),
1190 }
1191 }
1192
1193 pub fn deliver_message(
1195 &mut self,
1196 message: &crate::msg::Message,
1197 attach: &[DeliveryFile],
1198 connection: Map<String, Value>,
1199 options: Map<String, Value>,
1200 ) -> Result<String> {
1201 if !self.has(crate::CAP_MESSAGE) {
1202 return Err(HostError::plugin(
1203 &self.label,
1204 "a message, but the plugin doesn't advertise `message`",
1205 ));
1206 }
1207 self.send(&Request::Deliver {
1208 local_path: None,
1209 remote_path: None,
1210 files: attach.to_vec(),
1211 connection,
1212 options,
1213 message: Some(message.clone()),
1214 })?;
1215 match self.recv_json("a delivered reply")? {
1216 Response::Delivered { location, attempts } => {
1217 self.last_attempts = attempts.unwrap_or(1);
1218 Ok(location)
1219 }
1220 other => Err(self.unexpected("a delivered reply", &Incoming::Json(other))),
1221 }
1222 }
1223
1224 pub fn last_attempts(&self) -> u32 {
1227 self.last_attempts
1228 }
1229
1230 pub fn close(mut self) -> Result<()> {
1232 let _ = self.send(&Request::Close {});
1233 let _ = self.recv(Some(Duration::from_secs(5)), "the close reply");
1234 self.stdin.lock().unwrap().take();
1235 for _ in 0..250 {
1236 if let Ok(Some(_)) = self.try_wait() {
1237 return Ok(());
1238 }
1239 std::thread::sleep(Duration::from_millis(20));
1240 }
1241 let _ = self.child.lock().unwrap().kill();
1242 Ok(())
1243 }
1244
1245 pub fn write_raw(&mut self, bytes: &[u8]) -> Result<()> {
1247 let ok = match self.stdin.lock().unwrap().as_mut() {
1248 Some(w) => w.write_all(bytes).and_then(|_| w.flush()).is_ok(),
1249 None => false,
1250 };
1251 if ok { Ok(()) } else { Err(self.write_failed()) }
1252 }
1253
1254 pub fn wait_exit(&mut self, timeout: Duration) -> Option<ExitStatus> {
1256 self.stdin.lock().unwrap().take();
1257 let deadline = std::time::Instant::now() + timeout;
1258 while std::time::Instant::now() < deadline {
1259 if let Ok(Some(s)) = self.try_wait() {
1260 return Some(s);
1261 }
1262 std::thread::sleep(Duration::from_millis(20));
1263 }
1264 None
1265 }
1266}
1267
1268impl PluginProcess {
1269 fn drain_stderr(&mut self) {
1271 if let Some(t) = self.stderr_thread.take() {
1272 let deadline = std::time::Instant::now() + Duration::from_secs(2);
1273 while !t.is_finished() && std::time::Instant::now() < deadline {
1274 std::thread::sleep(Duration::from_millis(5));
1275 }
1276 if t.is_finished() {
1277 let _ = t.join();
1278 }
1279 }
1280 }
1281}
1282
1283impl Drop for PluginProcess {
1284 fn drop(&mut self) {
1285 if let Ok(mut r) = RUNNING.lock() {
1286 r.retain(|(id, _)| *id != self.registered);
1287 }
1288 if let Ok(mut stdin) = self.stdin.lock() {
1289 stdin.take();
1290 }
1291 if let Ok(None) = self.try_wait() {
1292 for _ in 0..25 {
1294 if let Ok(Some(_)) = self.try_wait() {
1295 break;
1296 }
1297 std::thread::sleep(Duration::from_millis(10));
1298 }
1299 let _ = self.child.lock().unwrap().kill();
1300 let _ = self.child.lock().unwrap().wait();
1301 }
1302 self.drain_stderr();
1303 }
1304}
1305
1306fn log_line(level: LogLevel, message: &str, fields: &Map<String, Value>) -> String {
1308 let prefix = match level {
1309 LogLevel::Error | LogLevel::Warn => "warning: ",
1310 LogLevel::Info => "info: ",
1311 LogLevel::Debug | LogLevel::Trace => "debug: ",
1312 };
1313 let mut line = format!("{prefix}{message}");
1314 for (k, v) in fields {
1315 match v {
1316 Value::String(s) => line.push_str(&format!(" {k}={s}")),
1317 v => line.push_str(&format!(" {k}={v}")),
1318 }
1319 }
1320 line
1321}
1322
1323fn progress_line(message: Option<&str>, done: Option<u64>, total: Option<u64>) -> String {
1325 let count = match (done, total) {
1326 (Some(d), Some(t)) => format!("{d}/{t}"),
1327 (Some(d), None) => d.to_string(),
1328 _ => String::new(),
1329 };
1330 match (message, count.is_empty()) {
1331 (Some(m), true) => format!("info: {m}"),
1332 (Some(m), false) => format!("info: {m} ({count})"),
1333 (None, _) => format!("info: {count}"),
1334 }
1335}
1336
1337static CORE_VERSION: OnceLock<String> = OnceLock::new();
1338
1339pub fn set_core_version(version: &str) {
1342 let _ = CORE_VERSION.set(version.to_string());
1343}
1344
1345fn core_version() -> String {
1346 CORE_VERSION.get().cloned().unwrap_or_else(|| "unreleased".into())
1347}