1#![doc(
48 html_logo_url = "https://arrow.apache.org/img/arrow-logo_chevrons_black-txt_white-bg.svg",
49 html_favicon_url = "https://arrow.apache.org/img/arrow-logo_chevrons_black-txt_transparent-bg.svg"
50)]
51#![cfg_attr(docsrs, feature(doc_cfg))]
52#![allow(rustdoc::invalid_html_tags)]
53#![warn(missing_docs)]
54#![allow(unused_crate_dependencies)]
56
57use arrow_ipc::{convert, writer, writer::EncodedData, writer::IpcWriteOptions};
58use arrow_schema::{ArrowError, Schema};
59
60use arrow_ipc::convert::try_schema_from_ipc_buffer;
61use base64::Engine;
62use base64::prelude::BASE64_STANDARD;
63use bytes::Bytes;
64use prost_types::Timestamp;
65use std::{fmt, ops::Deref};
66
67type ArrowResult<T> = std::result::Result<T, ArrowError>;
68
69#[allow(clippy::allow_attributes)]
71#[allow(clippy::all)]
72mod r#gen {
73 #![allow(missing_docs)]
74 include!("arrow.flight.protocol.rs");
75}
76
77pub mod flight_descriptor {
79 use super::r#gen;
80 pub use r#gen::flight_descriptor::DescriptorType;
81}
82
83pub mod flight_service_client {
85 use super::r#gen;
86 pub use r#gen::flight_service_client::FlightServiceClient;
87}
88
89pub mod flight_service_server {
92 use super::r#gen;
93 pub use r#gen::flight_service_server::FlightService;
94 pub use r#gen::flight_service_server::FlightServiceServer;
95}
96
97pub mod client;
99pub use client::FlightClient;
100
101pub mod decode;
104
105pub mod encode;
108
109pub mod error;
111
112pub use r#gen::Action;
113pub use r#gen::ActionType;
114pub use r#gen::BasicAuth;
115pub use r#gen::CancelFlightInfoRequest;
116pub use r#gen::CancelFlightInfoResult;
117pub use r#gen::CancelStatus;
118pub use r#gen::Criteria;
119pub use r#gen::Empty;
120pub use r#gen::FlightData;
121pub use r#gen::FlightDescriptor;
122pub use r#gen::FlightEndpoint;
123pub use r#gen::FlightInfo;
124pub use r#gen::HandshakeRequest;
125pub use r#gen::HandshakeResponse;
126pub use r#gen::Location;
127pub use r#gen::PollInfo;
128pub use r#gen::PutResult;
129pub use r#gen::RenewFlightEndpointRequest;
130pub use r#gen::Result;
131pub use r#gen::SchemaResult;
132pub use r#gen::Ticket;
133
134mod trailers;
136
137pub mod utils;
138
139#[cfg(feature = "flight-sql")]
140pub mod sql;
141mod streams;
142
143use flight_descriptor::DescriptorType;
144
145pub struct SchemaAsIpc<'a> {
147 pub pair: (&'a Schema, &'a IpcWriteOptions),
149}
150
151#[derive(Debug)]
154pub struct IpcMessage(pub Bytes);
155
156fn flight_schema_as_encoded_data(arrow_schema: &Schema, options: &IpcWriteOptions) -> EncodedData {
159 let data_gen = writer::IpcDataGenerator::default();
160 let mut dict_tracker = writer::DictionaryTracker::new(false);
161 data_gen.schema_to_bytes_with_dictionary_tracker(arrow_schema, &mut dict_tracker, options)
162}
163
164fn flight_schema_as_flatbuffer(schema: &Schema, options: &IpcWriteOptions) -> IpcMessage {
165 let encoded_data = flight_schema_as_encoded_data(schema, options);
166 IpcMessage(encoded_data.ipc_message.into())
167}
168
169impl Deref for IpcMessage {
175 type Target = [u8];
176
177 fn deref(&self) -> &Self::Target {
178 &self.0
179 }
180}
181
182impl<'a> Deref for SchemaAsIpc<'a> {
183 type Target = (&'a Schema, &'a IpcWriteOptions);
184
185 fn deref(&self) -> &Self::Target {
186 &self.pair
187 }
188}
189
190fn limited_fmt(f: &mut fmt::Formatter<'_>, value: &[u8], limit: usize) -> fmt::Result {
194 if value.len() > limit {
195 write!(f, "{:?}", &value[..limit])
196 } else {
197 write!(f, "{value:?}")
198 }
199}
200
201impl fmt::Display for FlightData {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 write!(f, "FlightData {{")?;
204 write!(f, " descriptor: ")?;
205 match &self.flight_descriptor {
206 Some(d) => write!(f, "{d}")?,
207 None => write!(f, "None")?,
208 }
209 write!(f, ", header: ")?;
210 limited_fmt(f, &self.data_header, 8)?;
211 write!(f, ", metadata: ")?;
212 limited_fmt(f, &self.app_metadata, 8)?;
213 write!(f, ", body: ")?;
214 limited_fmt(f, &self.data_body, 8)?;
215 write!(f, " }}")
216 }
217}
218
219impl fmt::Display for FlightDescriptor {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 write!(f, "FlightDescriptor {{")?;
222 write!(f, " type: ")?;
223 match self.r#type() {
224 DescriptorType::Cmd => {
225 write!(f, "cmd, value: ")?;
226 limited_fmt(f, &self.cmd, 8)?;
227 }
228 DescriptorType::Path => {
229 write!(f, "path: [")?;
230 let mut sep = "";
231 for element in &self.path {
232 write!(f, "{sep}{element}")?;
233 sep = ", ";
234 }
235 write!(f, "]")?;
236 }
237 DescriptorType::Unknown => {
238 write!(f, "unknown")?;
239 }
240 }
241 write!(f, " }}")
242 }
243}
244
245impl fmt::Display for FlightEndpoint {
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 write!(f, "FlightEndpoint {{")?;
248 write!(f, " ticket: ")?;
249 match &self.ticket {
250 Some(value) => write!(f, "{value}"),
251 None => write!(f, " None"),
252 }?;
253 write!(f, ", location: [")?;
254 let mut sep = "";
255 for location in &self.location {
256 write!(f, "{sep}{location}")?;
257 sep = ", ";
258 }
259 write!(f, "]")?;
260 write!(f, ", expiration_time:")?;
261 match &self.expiration_time {
262 Some(value) => write!(f, " {value}"),
263 None => write!(f, " None"),
264 }?;
265 write!(f, ", app_metadata: ")?;
266 limited_fmt(f, &self.app_metadata, 8)?;
267 write!(f, " }}")
268 }
269}
270
271impl fmt::Display for FlightInfo {
272 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273 let ipc_message = IpcMessage(self.schema.clone());
274 let schema: Schema = ipc_message.try_into().map_err(|_err| fmt::Error)?;
275 write!(f, "FlightInfo {{")?;
276 write!(f, " schema: {schema}")?;
277 write!(f, ", descriptor:")?;
278 match &self.flight_descriptor {
279 Some(d) => write!(f, " {d}"),
280 None => write!(f, " None"),
281 }?;
282 write!(f, ", endpoint: [")?;
283 let mut sep = "";
284 for endpoint in &self.endpoint {
285 write!(f, "{sep}{endpoint}")?;
286 sep = ", ";
287 }
288 write!(f, "], total_records: {}", self.total_records)?;
289 write!(f, ", total_bytes: {}", self.total_bytes)?;
290 write!(f, ", ordered: {}", self.ordered)?;
291 write!(f, ", app_metadata: ")?;
292 limited_fmt(f, &self.app_metadata, 8)?;
293 write!(f, " }}")
294 }
295}
296
297impl fmt::Display for PollInfo {
298 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
299 write!(f, "PollInfo {{")?;
300 write!(f, " info:")?;
301 match &self.info {
302 Some(value) => write!(f, " {value}"),
303 None => write!(f, " None"),
304 }?;
305 write!(f, ", descriptor:")?;
306 match &self.flight_descriptor {
307 Some(d) => write!(f, " {d}"),
308 None => write!(f, " None"),
309 }?;
310 write!(f, ", progress:")?;
311 match &self.progress {
312 Some(value) => write!(f, " {value}"),
313 None => write!(f, " None"),
314 }?;
315 write!(f, ", expiration_time:")?;
316 match &self.expiration_time {
317 Some(value) => write!(f, " {value}"),
318 None => write!(f, " None"),
319 }?;
320 write!(f, " }}")
321 }
322}
323
324impl fmt::Display for CancelFlightInfoRequest {
325 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
326 write!(f, "CancelFlightInfoRequest {{")?;
327 write!(f, " info: ")?;
328 match &self.info {
329 Some(value) => write!(f, "{value}")?,
330 None => write!(f, "None")?,
331 }
332 write!(f, " }}")
333 }
334}
335
336impl fmt::Display for CancelFlightInfoResult {
337 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338 write!(f, "CancelFlightInfoResult {{")?;
339 write!(f, " status: {}", self.status().as_str_name())?;
340 write!(f, " }}")
341 }
342}
343
344impl fmt::Display for RenewFlightEndpointRequest {
345 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346 write!(f, "RenewFlightEndpointRequest {{")?;
347 write!(f, " endpoint: ")?;
348 match &self.endpoint {
349 Some(value) => write!(f, "{value}")?,
350 None => write!(f, "None")?,
351 }
352 write!(f, " }}")
353 }
354}
355
356impl fmt::Display for Location {
357 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358 write!(f, "Location {{")?;
359 write!(f, " uri: ")?;
360 write!(f, "{}", self.uri)
361 }
362}
363
364impl fmt::Display for Ticket {
365 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
366 write!(f, "Ticket {{")?;
367 write!(f, " ticket: ")?;
368 write!(f, "{}", BASE64_STANDARD.encode(&self.ticket))
369 }
370}
371
372impl From<EncodedData> for FlightData {
375 fn from(data: EncodedData) -> Self {
376 FlightData {
377 data_header: data.ipc_message.into(),
378 data_body: data.arrow_data.into(),
379 ..Default::default()
380 }
381 }
382}
383
384impl From<SchemaAsIpc<'_>> for FlightData {
385 fn from(schema_ipc: SchemaAsIpc) -> Self {
386 let IpcMessage(vals) = flight_schema_as_flatbuffer(schema_ipc.0, schema_ipc.1);
387 FlightData {
388 data_header: vals,
389 ..Default::default()
390 }
391 }
392}
393
394impl TryFrom<SchemaAsIpc<'_>> for SchemaResult {
395 type Error = ArrowError;
396
397 fn try_from(schema_ipc: SchemaAsIpc) -> ArrowResult<Self> {
398 let IpcMessage(vals) = schema_to_ipc_format(schema_ipc)?;
404 Ok(SchemaResult { schema: vals })
405 }
406}
407
408impl TryFrom<SchemaAsIpc<'_>> for IpcMessage {
409 type Error = ArrowError;
410
411 fn try_from(schema_ipc: SchemaAsIpc) -> ArrowResult<Self> {
412 schema_to_ipc_format(schema_ipc)
413 }
414}
415
416fn schema_to_ipc_format(schema_ipc: SchemaAsIpc) -> ArrowResult<IpcMessage> {
417 let pair = *schema_ipc;
418 let encoded_data = flight_schema_as_encoded_data(pair.0, pair.1);
419
420 let mut schema = vec![];
421 writer::write_message(&mut schema, encoded_data, pair.1)?;
422 Ok(IpcMessage(schema.into()))
423}
424
425impl TryFrom<&FlightData> for Schema {
426 type Error = ArrowError;
427 fn try_from(data: &FlightData) -> ArrowResult<Self> {
428 convert::try_schema_from_flatbuffer_bytes(&data.data_header[..]).map_err(|err| {
429 ArrowError::ParseError(format!(
430 "Unable to convert flight data to Arrow schema: {err}"
431 ))
432 })
433 }
434}
435
436impl TryFrom<FlightInfo> for Schema {
437 type Error = ArrowError;
438
439 fn try_from(value: FlightInfo) -> ArrowResult<Self> {
440 value.try_decode_schema()
441 }
442}
443
444impl TryFrom<IpcMessage> for Schema {
445 type Error = ArrowError;
446
447 fn try_from(value: IpcMessage) -> ArrowResult<Self> {
448 try_schema_from_ipc_buffer(&value)
449 }
450}
451
452impl TryFrom<&SchemaResult> for Schema {
453 type Error = ArrowError;
454 fn try_from(data: &SchemaResult) -> ArrowResult<Self> {
455 try_schema_from_ipc_buffer(&data.schema)
456 }
457}
458
459impl TryFrom<SchemaResult> for Schema {
460 type Error = ArrowError;
461 fn try_from(data: SchemaResult) -> ArrowResult<Self> {
462 (&data).try_into()
463 }
464}
465
466impl FlightData {
469 pub fn new() -> Self {
494 Default::default()
495 }
496
497 pub fn with_descriptor(mut self, flight_descriptor: FlightDescriptor) -> Self {
499 self.flight_descriptor = Some(flight_descriptor);
500 self
501 }
502
503 pub fn with_data_header(mut self, data_header: impl Into<Bytes>) -> Self {
505 self.data_header = data_header.into();
506 self
507 }
508
509 pub fn with_data_body(mut self, data_body: impl Into<Bytes>) -> Self {
513 self.data_body = data_body.into();
514 self
515 }
516
517 pub fn with_app_metadata(mut self, app_metadata: impl Into<Bytes>) -> Self {
519 self.app_metadata = app_metadata.into();
520 self
521 }
522}
523
524impl FlightDescriptor {
525 pub fn new_cmd(cmd: impl Into<Bytes>) -> Self {
529 FlightDescriptor {
530 r#type: DescriptorType::Cmd.into(),
531 cmd: cmd.into(),
532 ..Default::default()
533 }
534 }
535
536 pub fn new_path(path: Vec<String>) -> Self {
540 FlightDescriptor {
541 r#type: DescriptorType::Path.into(),
542 path,
543 ..Default::default()
544 }
545 }
546}
547
548impl FlightInfo {
549 pub fn new() -> FlightInfo {
575 FlightInfo {
576 schema: Bytes::new(),
577 flight_descriptor: None,
578 endpoint: vec![],
579 ordered: false,
580 total_records: -1,
584 total_bytes: -1,
585 app_metadata: Bytes::new(),
586 }
587 }
588
589 pub fn try_decode_schema(self) -> ArrowResult<Schema> {
591 let msg = IpcMessage(self.schema);
592 msg.try_into()
593 }
594
595 pub fn try_with_schema(mut self, schema: &Schema) -> ArrowResult<Self> {
602 let options = IpcWriteOptions::default();
603 let IpcMessage(schema) = SchemaAsIpc::new(schema, &options).try_into()?;
604 self.schema = schema;
605 Ok(self)
606 }
607
608 pub fn with_endpoint(mut self, endpoint: FlightEndpoint) -> Self {
610 self.endpoint.push(endpoint);
611 self
612 }
613
614 pub fn with_endpoints(mut self, endpoints: Vec<FlightEndpoint>) -> Self {
616 self.endpoint = endpoints;
617 self
618 }
619
620 pub fn with_descriptor(mut self, flight_descriptor: FlightDescriptor) -> Self {
622 self.flight_descriptor = Some(flight_descriptor);
623 self
624 }
625
626 pub fn with_total_records(mut self, total_records: i64) -> Self {
628 self.total_records = total_records;
629 self
630 }
631
632 pub fn with_total_bytes(mut self, total_bytes: i64) -> Self {
634 self.total_bytes = total_bytes;
635 self
636 }
637
638 pub fn with_ordered(mut self, ordered: bool) -> Self {
642 self.ordered = ordered;
643 self
644 }
645
646 pub fn with_app_metadata(mut self, app_metadata: impl Into<Bytes>) -> Self {
648 self.app_metadata = app_metadata.into();
649 self
650 }
651}
652
653impl PollInfo {
654 pub fn new() -> Self {
671 Self {
672 info: None,
673 flight_descriptor: None,
674 progress: None,
675 expiration_time: None,
676 }
677 }
678
679 pub fn with_info(mut self, info: FlightInfo) -> Self {
681 self.info = Some(info);
682 self
683 }
684
685 pub fn with_descriptor(mut self, flight_descriptor: FlightDescriptor) -> Self {
688 self.flight_descriptor = Some(flight_descriptor);
689 self
690 }
691
692 pub fn try_with_progress(mut self, progress: f64) -> ArrowResult<Self> {
695 if !(0.0..=1.0).contains(&progress) {
696 return Err(ArrowError::InvalidArgumentError(format!(
697 "PollInfo progress must be in the range [0.0, 1.0], got {progress}"
698 )));
699 }
700 self.progress = Some(progress);
701 Ok(self)
702 }
703
704 pub fn with_expiration_time(mut self, expiration_time: Timestamp) -> Self {
706 self.expiration_time = Some(expiration_time);
707 self
708 }
709}
710
711impl<'a> SchemaAsIpc<'a> {
712 pub fn new(schema: &'a Schema, options: &'a IpcWriteOptions) -> Self {
714 SchemaAsIpc {
715 pair: (schema, options),
716 }
717 }
718}
719
720impl CancelFlightInfoRequest {
721 pub fn new(info: FlightInfo) -> Self {
724 Self { info: Some(info) }
725 }
726}
727
728impl CancelFlightInfoResult {
729 pub fn new(status: CancelStatus) -> Self {
731 Self {
732 status: status as i32,
733 }
734 }
735}
736
737impl RenewFlightEndpointRequest {
738 pub fn new(endpoint: FlightEndpoint) -> Self {
741 Self {
742 endpoint: Some(endpoint),
743 }
744 }
745}
746
747impl Action {
748 pub fn new(action_type: impl Into<String>, body: impl Into<Bytes>) -> Self {
750 Self {
751 r#type: action_type.into(),
752 body: body.into(),
753 }
754 }
755}
756
757impl Result {
758 pub fn new(body: impl Into<Bytes>) -> Self {
760 Self { body: body.into() }
761 }
762}
763
764impl Ticket {
765 pub fn new(ticket: impl Into<Bytes>) -> Self {
774 Self {
775 ticket: ticket.into(),
776 }
777 }
778}
779
780impl FlightEndpoint {
781 pub fn new() -> FlightEndpoint {
800 Default::default()
801 }
802
803 pub fn with_ticket(mut self, ticket: Ticket) -> Self {
805 self.ticket = Some(ticket);
806 self
807 }
808
809 pub fn with_location(mut self, uri: impl Into<String>) -> Self {
821 self.location.push(Location { uri: uri.into() });
822 self
823 }
824
825 pub fn with_expiration_time(mut self, expiration_time: Timestamp) -> Self {
827 self.expiration_time = Some(expiration_time);
828 self
829 }
830
831 pub fn with_app_metadata(mut self, app_metadata: impl Into<Bytes>) -> Self {
833 self.app_metadata = app_metadata.into();
834 self
835 }
836}
837
838#[cfg(test)]
839mod tests {
840 use super::*;
841 use arrow_ipc::MetadataVersion;
842 use arrow_schema::{DataType, Field, TimeUnit};
843
844 struct TestVector(Vec<u8>, usize);
845
846 impl fmt::Display for TestVector {
847 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
848 limited_fmt(f, &self.0, self.1)
849 }
850 }
851
852 #[test]
853 fn it_creates_flight_descriptor_command() {
854 let expected_cmd = b"my_command";
855 let fd = FlightDescriptor::new_cmd(expected_cmd.to_vec());
856 assert_eq!(fd.r#type(), DescriptorType::Cmd);
857 assert_eq!(fd.cmd, expected_cmd.to_vec());
858 }
859
860 #[test]
861 fn it_accepts_equal_output() {
862 let input = TestVector(vec![91; 10], 10);
863
864 let actual = format!("{input}");
865 let expected = format!("{:?}", vec![91; 10]);
866 assert_eq!(actual, expected);
867 }
868
869 #[test]
870 fn it_accepts_short_output() {
871 let input = TestVector(vec![91; 6], 10);
872
873 let actual = format!("{input}");
874 let expected = format!("{:?}", vec![91; 6]);
875 assert_eq!(actual, expected);
876 }
877
878 #[test]
879 fn it_accepts_long_output() {
880 let input = TestVector(vec![91; 10], 9);
881
882 let actual = format!("{input}");
883 let expected = format!("{:?}", vec![91; 9]);
884 assert_eq!(actual, expected);
885 }
886
887 #[test]
888 fn ser_deser_schema_result() {
889 let schema = Schema::new(vec![
890 Field::new("c1", DataType::Utf8, false),
891 Field::new("c2", DataType::Float64, true),
892 Field::new("c3", DataType::UInt32, false),
893 Field::new("c4", DataType::Boolean, true),
894 Field::new("c5", DataType::Timestamp(TimeUnit::Millisecond, None), true),
895 Field::new("c6", DataType::Time32(TimeUnit::Second), false),
896 ]);
897 let option = IpcWriteOptions::default();
900 let schema_ipc = SchemaAsIpc::new(&schema, &option);
901 let result: SchemaResult = schema_ipc.try_into().unwrap();
902 let des_schema: Schema = (&result).try_into().unwrap();
903 assert_eq!(schema, des_schema);
904
905 let option = IpcWriteOptions::try_new(8, true, MetadataVersion::V4).unwrap();
908 let schema_ipc = SchemaAsIpc::new(&schema, &option);
909 let result: SchemaResult = schema_ipc.try_into().unwrap();
910 let des_schema: Schema = (&result).try_into().unwrap();
911 assert_eq!(schema, des_schema);
912 }
913
914 #[test]
915 fn test_dict_schema() {
916 let schema = Schema::new(vec![
918 Field::new(
919 "a",
920 DataType::Dictionary(Box::new(DataType::UInt16), Box::new(DataType::Utf8)),
921 false,
922 ),
923 Field::new(
924 "b",
925 DataType::Dictionary(Box::new(DataType::UInt16), Box::new(DataType::Utf8)),
926 false,
927 ),
928 ]);
929
930 let flight_info = FlightInfo::new().try_with_schema(&schema).unwrap();
931
932 let new_schema = Schema::try_from(flight_info).unwrap();
933 assert_eq!(schema, new_schema);
934 }
935}