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