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 },
832 "an open reply",
833 )
834 }
835
836 pub fn check(&mut self, sql: &str) -> Result<()> {
837 self.call_ok(&Request::Check { sql: sql.to_string() }, "a check reply")
838 }
839
840 pub fn execute(
842 &mut self,
843 sql: &str,
844 row_limit: Option<u64>,
845 mut on_batch: impl FnMut(&SchemaRef, RecordBatch) -> std::result::Result<(), String>,
846 ) -> Result<Execution> {
847 self.send(&Request::Execute {
848 sql: sql.to_string(),
849 row_limit,
850 })?;
851 match self.recv_json("an execute reply")? {
852 Response::NoResult { rows_affected } => return Ok(Execution::NoResult { rows_affected }),
853 Response::Result { .. } => {}
854 other => return Err(self.unexpected("an execute reply", &Incoming::Json(other))),
855 }
856 let mut schema: Option<SchemaRef> = None;
857 let mut rows = 0u64;
858 let mut sink_error = None;
859 loop {
860 match self.recv(None, "result data")? {
861 Incoming::Arrow(ipc) => {
862 let (s, batches) = frame::decode_batches(&ipc).map_err(|e| HostError::Arrow {
863 plugin: self.label.clone(),
864 message: e.to_string(),
865 })?;
866 let schema = schema.get_or_insert(s);
867 for b in batches {
868 rows += b.num_rows() as u64;
869 if sink_error.is_none()
870 && let Err(e) = on_batch(schema, b)
871 {
872 sink_error = Some(e);
874 }
875 }
876 }
877 Incoming::Json(Response::ResultEnd { .. }) => break,
878 Incoming::Json(Response::Error { message, kind, code }) => {
879 return Err(HostError::Plugin {
880 plugin: self.label.clone(),
881 message,
882 kind,
883 code,
884 });
885 }
886 other => return Err(self.unexpected("result data", &other)),
887 }
888 }
889 if let Some(e) = sink_error {
890 return Err(HostError::plugin(&self.label, e));
891 }
892 let schema = schema.ok_or_else(|| HostError::Malformed {
893 plugin: self.label.clone(),
894 message: "a result ended without any Arrow frame carrying its schema".into(),
895 })?;
896 Ok(Execution::Result { schema, rows })
897 }
898
899 pub fn write_begin(
901 &mut self,
902 path: &str,
903 format: &str,
904 options: Map<String, Value>,
905 result_sets: Vec<ResultSetMeta>,
906 template: Option<Value>,
907 ) -> Result<()> {
908 self.send(&Request::Write {
909 path: path.to_string(),
910 format: format.to_string(),
911 options,
912 result_sets,
913 template,
914 })
915 }
916
917 pub fn write_result_set(
919 &mut self,
920 schema: &SchemaRef,
921 batches: impl IntoIterator<Item = RecordBatch>,
922 ) -> Result<()> {
923 let mut any = false;
924 for b in batches {
925 any = true;
926 self.send_batch(&b)?;
927 }
928 if !any {
929 self.send_batch(&RecordBatch::new_empty(schema.clone()))?;
930 }
931 self.send(&Request::ResultSetEnd {})
932 }
933
934 pub fn load(
937 &mut self,
938 name: &str,
939 schema: &SchemaRef,
940 batches: impl IntoIterator<Item = RecordBatch>,
941 ) -> Result<(String, Option<String>)> {
942 self.send(&Request::Load {
943 name: name.to_string(),
944 })?;
945 self.write_result_set(schema, batches)?;
946 match self.recv_json("a loaded reply")? {
947 Response::Loaded {
948 relation, warning, ..
949 } => Ok((relation, warning)),
950 other => Err(self.unexpected("a loaded reply", &Incoming::Json(other))),
951 }
952 }
953
954 pub fn write_finish(&mut self) -> Result<Vec<String>> {
955 Ok(self.write_finish_with_warnings()?.0)
956 }
957
958 pub fn write_finish_with_warnings(&mut self) -> Result<(Vec<String>, Vec<String>)> {
960 self.send(&Request::Finish {})?;
961 match self.recv_json("a written reply")? {
962 Response::Written { files, warnings } => Ok((files, warnings)),
963 other => Err(self.unexpected("a written reply", &Incoming::Json(other))),
964 }
965 }
966
967 pub fn deliver(
969 &mut self,
970 local_path: &str,
971 remote_path: Option<&str>,
972 connection: Map<String, Value>,
973 ) -> Result<String> {
974 let file = DeliveryFile {
975 local_path: local_path.to_string(),
976 remote_path: remote_path.map(str::to_string),
977 };
978 self.deliver_files(&[file], connection, Map::new())
979 }
980
981 pub fn deliver_files(
984 &mut self,
985 files: &[DeliveryFile],
986 connection: Map<String, Value>,
987 options: Map<String, Value>,
988 ) -> Result<String> {
989 let req = match files {
990 [] => {
991 return Err(HostError::Malformed {
992 plugin: self.label.clone(),
993 message: "nothing to deliver".into(),
994 });
995 }
996 [one] => Request::Deliver {
997 local_path: Some(one.local_path.clone()),
998 remote_path: one.remote_path.clone(),
999 files: Vec::new(),
1000 connection,
1001 options,
1002 message: None,
1003 },
1004 many => {
1005 if !self.has(crate::CAP_MULTI_FILE) {
1006 return Err(HostError::plugin(
1007 &self.label,
1008 format!(
1009 "{} files in one delivery, but the plugin doesn't advertise `multi_file`",
1010 many.len()
1011 ),
1012 ));
1013 }
1014 Request::Deliver {
1015 local_path: None,
1016 remote_path: None,
1017 files: many.to_vec(),
1018 connection,
1019 options,
1020 message: None,
1021 }
1022 }
1023 };
1024 self.send(&req)?;
1025 match self.recv_json("a delivered reply")? {
1026 Response::Delivered { location, attempts } => {
1027 self.last_attempts = attempts.unwrap_or(1);
1028 Ok(location)
1029 }
1030 other => Err(self.unexpected("a delivered reply", &Incoming::Json(other))),
1031 }
1032 }
1033
1034 pub fn deliver_message(
1036 &mut self,
1037 message: &crate::msg::Message,
1038 attach: &[DeliveryFile],
1039 connection: Map<String, Value>,
1040 options: Map<String, Value>,
1041 ) -> Result<String> {
1042 if !self.has(crate::CAP_MESSAGE) {
1043 return Err(HostError::plugin(
1044 &self.label,
1045 "a message, but the plugin doesn't advertise `message`",
1046 ));
1047 }
1048 self.send(&Request::Deliver {
1049 local_path: None,
1050 remote_path: None,
1051 files: attach.to_vec(),
1052 connection,
1053 options,
1054 message: Some(message.clone()),
1055 })?;
1056 match self.recv_json("a delivered reply")? {
1057 Response::Delivered { location, attempts } => {
1058 self.last_attempts = attempts.unwrap_or(1);
1059 Ok(location)
1060 }
1061 other => Err(self.unexpected("a delivered reply", &Incoming::Json(other))),
1062 }
1063 }
1064
1065 pub fn last_attempts(&self) -> u32 {
1068 self.last_attempts
1069 }
1070
1071 pub fn close(mut self) -> Result<()> {
1073 let _ = self.send(&Request::Close {});
1074 let _ = self.recv(Some(Duration::from_secs(5)), "the close reply");
1075 self.stdin.lock().unwrap().take();
1076 for _ in 0..250 {
1077 if let Ok(Some(_)) = self.try_wait() {
1078 return Ok(());
1079 }
1080 std::thread::sleep(Duration::from_millis(20));
1081 }
1082 let _ = self.child.lock().unwrap().kill();
1083 Ok(())
1084 }
1085
1086 pub fn write_raw(&mut self, bytes: &[u8]) -> Result<()> {
1088 let ok = match self.stdin.lock().unwrap().as_mut() {
1089 Some(w) => w.write_all(bytes).and_then(|_| w.flush()).is_ok(),
1090 None => false,
1091 };
1092 if ok { Ok(()) } else { Err(self.write_failed()) }
1093 }
1094
1095 pub fn wait_exit(&mut self, timeout: Duration) -> Option<ExitStatus> {
1097 self.stdin.lock().unwrap().take();
1098 let deadline = std::time::Instant::now() + timeout;
1099 while std::time::Instant::now() < deadline {
1100 if let Ok(Some(s)) = self.try_wait() {
1101 return Some(s);
1102 }
1103 std::thread::sleep(Duration::from_millis(20));
1104 }
1105 None
1106 }
1107}
1108
1109impl PluginProcess {
1110 fn drain_stderr(&mut self) {
1112 if let Some(t) = self.stderr_thread.take() {
1113 let deadline = std::time::Instant::now() + Duration::from_secs(2);
1114 while !t.is_finished() && std::time::Instant::now() < deadline {
1115 std::thread::sleep(Duration::from_millis(5));
1116 }
1117 if t.is_finished() {
1118 let _ = t.join();
1119 }
1120 }
1121 }
1122}
1123
1124impl Drop for PluginProcess {
1125 fn drop(&mut self) {
1126 if let Ok(mut r) = RUNNING.lock() {
1127 r.retain(|(id, _)| *id != self.registered);
1128 }
1129 if let Ok(mut stdin) = self.stdin.lock() {
1130 stdin.take();
1131 }
1132 if let Ok(None) = self.try_wait() {
1133 for _ in 0..25 {
1135 if let Ok(Some(_)) = self.try_wait() {
1136 break;
1137 }
1138 std::thread::sleep(Duration::from_millis(10));
1139 }
1140 let _ = self.child.lock().unwrap().kill();
1141 let _ = self.child.lock().unwrap().wait();
1142 }
1143 self.drain_stderr();
1144 }
1145}
1146
1147fn log_line(level: LogLevel, message: &str, fields: &Map<String, Value>) -> String {
1149 let prefix = match level {
1150 LogLevel::Error | LogLevel::Warn => "warning: ",
1151 LogLevel::Info => "info: ",
1152 LogLevel::Debug | LogLevel::Trace => "debug: ",
1153 };
1154 let mut line = format!("{prefix}{message}");
1155 for (k, v) in fields {
1156 match v {
1157 Value::String(s) => line.push_str(&format!(" {k}={s}")),
1158 v => line.push_str(&format!(" {k}={v}")),
1159 }
1160 }
1161 line
1162}
1163
1164fn progress_line(message: Option<&str>, done: Option<u64>, total: Option<u64>) -> String {
1166 let count = match (done, total) {
1167 (Some(d), Some(t)) => format!("{d}/{t}"),
1168 (Some(d), None) => d.to_string(),
1169 _ => String::new(),
1170 };
1171 match (message, count.is_empty()) {
1172 (Some(m), true) => format!("info: {m}"),
1173 (Some(m), false) => format!("info: {m} ({count})"),
1174 (None, _) => format!("info: {count}"),
1175 }
1176}
1177
1178static CORE_VERSION: OnceLock<String> = OnceLock::new();
1179
1180pub fn set_core_version(version: &str) {
1183 let _ = CORE_VERSION.set(version.to_string());
1184}
1185
1186fn core_version() -> String {
1187 CORE_VERSION.get().cloned().unwrap_or_else(|| "unreleased".into())
1188}