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