Skip to main content

datafusion_flight_sql_server/
service.rs

1use std::{collections::BTreeMap, pin::Pin, sync::Arc};
2
3use arrow_flight::{
4    decode::{DecodedPayload, FlightDataDecoder},
5    sql::{
6        self,
7        server::{FlightSqlService as ArrowFlightSqlService, PeekableFlightDataStream},
8        ActionBeginSavepointRequest, ActionBeginSavepointResult, ActionBeginTransactionRequest,
9        ActionBeginTransactionResult, ActionCancelQueryRequest, ActionCancelQueryResult,
10        ActionClosePreparedStatementRequest, ActionCreatePreparedStatementRequest,
11        ActionCreatePreparedStatementResult, ActionCreatePreparedSubstraitPlanRequest,
12        ActionEndSavepointRequest, ActionEndTransactionRequest, Any, CommandGetCatalogs,
13        CommandGetCrossReference, CommandGetDbSchemas, CommandGetExportedKeys,
14        CommandGetImportedKeys, CommandGetPrimaryKeys, CommandGetSqlInfo, CommandGetTableTypes,
15        CommandGetTables, CommandGetXdbcTypeInfo, CommandPreparedStatementQuery,
16        CommandPreparedStatementUpdate, CommandStatementQuery, CommandStatementSubstraitPlan,
17        CommandStatementUpdate, DoPutPreparedStatementResult, ProstMessageExt as _, SqlInfo,
18        TicketStatementQuery,
19    },
20};
21use arrow_flight::{
22    encode::FlightDataEncoderBuilder,
23    error::FlightError,
24    flight_service_server::{FlightService, FlightServiceServer},
25    Action, FlightDescriptor, FlightEndpoint, FlightInfo, HandshakeRequest, HandshakeResponse,
26    IpcMessage, SchemaAsIpc, Ticket,
27};
28use datafusion::arrow::{
29    array::{ArrayRef, RecordBatch, StringArray},
30    compute::concat_batches,
31    datatypes::{DataType, Field, SchemaBuilder, SchemaRef},
32    error::ArrowError,
33    ipc::{
34        reader::StreamReader,
35        writer::{IpcWriteOptions, StreamWriter},
36    },
37};
38use datafusion::{
39    common::{arrow::datatypes::Schema, ParamValues},
40    datasource::TableType,
41    error::{DataFusionError, Result as DataFusionResult},
42    execution::context::{SQLOptions, SessionContext, SessionState},
43    logical_expr::LogicalPlan,
44    physical_plan::SendableRecordBatchStream,
45    scalar::ScalarValue,
46};
47use datafusion_substrait::{
48    logical_plan::consumer::from_substrait_plan, serializer::deserialize_bytes,
49};
50
51use futures::{Stream, StreamExt, TryStreamExt};
52use log::info;
53use once_cell::sync::Lazy;
54use prost::bytes::Bytes;
55use prost::Message;
56use tokio::io::{AsyncRead, AsyncWrite};
57use tonic::transport::{
58    server::{Connected, TcpIncoming},
59    Server,
60};
61use tonic::{Request, Response, Status, Streaming};
62
63use super::config::FlightSqlServiceConfig;
64use super::session::{SessionStateProvider, StaticSessionStateProvider};
65use super::state::{CommandTicket, QueryHandle};
66
67type Result<T, E = Status> = std::result::Result<T, E>;
68
69/// FlightSqlService is a basic stateless FlightSqlService implementation.
70pub struct FlightSqlService {
71    provider: Box<dyn SessionStateProvider>,
72    sql_options: Option<SQLOptions>,
73    config: FlightSqlServiceConfig,
74}
75
76impl FlightSqlService {
77    /// Creates a new FlightSqlService with a static SessionState.
78    pub fn new(state: SessionState) -> Self {
79        Self::new_with_provider(Box::new(StaticSessionStateProvider::new(state)))
80    }
81
82    /// Creates a new FlightSqlService with a SessionStateProvider.
83    pub fn new_with_provider(provider: Box<dyn SessionStateProvider>) -> Self {
84        Self {
85            provider,
86            sql_options: None,
87            config: FlightSqlServiceConfig::default(),
88        }
89    }
90
91    /// Replaces the FlightSqlServiceConfig with the provided config.
92    pub fn with_config(self, config: FlightSqlServiceConfig) -> Self {
93        Self { config, ..self }
94    }
95
96    /// Replaces the sql_options with the provided options.
97    /// These options are used to verify all SQL queries.
98    /// When None the default [`SQLOptions`] are used.
99    pub fn with_sql_options(self, sql_options: SQLOptions) -> Self {
100        Self {
101            sql_options: Some(sql_options),
102            ..self
103        }
104    }
105
106    // Federate substrait plans instead of SQL
107    // pub fn with_substrait() -> Self {
108    // TODO: Substrait federation
109    // }
110
111    // Serves straightforward on the specified address.
112    pub async fn serve(self, addr: String) -> Result<(), Box<dyn std::error::Error>> {
113        let addr = addr.parse()?;
114        info!("Listening on {addr:?}");
115
116        let svc = FlightServiceServer::new(self);
117
118        Ok(Server::builder().add_service(svc).serve(addr).await?)
119    }
120
121    pub async fn serve_with_listener(
122        self,
123        listener: std::net::TcpListener,
124    ) -> Result<(), Box<dyn std::error::Error>> {
125        info!("Listening on {}", listener.local_addr()?);
126
127        let listener = tokio::net::TcpListener::from_std(listener)?;
128        let incoming = TcpIncoming::from(listener).with_nodelay(Some(true));
129
130        self.serve_with_incoming(incoming).await
131    }
132
133    pub async fn serve_with_incoming<I, IO, IE>(
134        self,
135        incoming: I,
136    ) -> Result<(), Box<dyn std::error::Error>>
137    where
138        I: Stream<Item = std::result::Result<IO, IE>>,
139        IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static,
140        IE: Into<Box<dyn std::error::Error + Send + Sync>>,
141    {
142        let svc = FlightServiceServer::new(self);
143        Ok(Server::builder()
144            .add_service(svc)
145            .serve_with_incoming(incoming)
146            .await?)
147    }
148
149    async fn new_context<T>(
150        &self,
151        request: Request<T>,
152    ) -> Result<(Request<T>, FlightSqlSessionContext)> {
153        let (metadata, extensions, msg) = request.into_parts();
154        let inspect_request = Request::from_parts(metadata, extensions, ());
155
156        let state = self.provider.new_context(&inspect_request).await?;
157        let ctx = SessionContext::new_with_state(state);
158
159        let (metadata, extensions, _) = inspect_request.into_parts();
160        Ok((
161            Request::from_parts(metadata, extensions, msg),
162            FlightSqlSessionContext {
163                inner: ctx,
164                sql_options: self.sql_options,
165            },
166        ))
167    }
168}
169
170/// The schema for GetTableTypes
171static GET_TABLE_TYPES_SCHEMA: Lazy<SchemaRef> = Lazy::new(|| {
172    //TODO: Move this into arrow-flight itself, similar to the builder pattern for CommandGetCatalogs and CommandGetDbSchemas
173    Arc::new(Schema::new(vec![Field::new(
174        "table_type",
175        DataType::Utf8,
176        false,
177    )]))
178});
179
180struct FlightSqlSessionContext {
181    inner: SessionContext,
182    sql_options: Option<SQLOptions>,
183}
184
185impl FlightSqlSessionContext {
186    async fn sql_to_logical_plan(&self, sql: &str) -> DataFusionResult<LogicalPlan> {
187        let plan = self.inner.state().create_logical_plan(sql).await?;
188        let verifier = self.sql_options.unwrap_or_default();
189        verifier.verify_plan(&plan)?;
190        Ok(plan)
191    }
192
193    async fn execute_logical_plan(
194        &self,
195        plan: LogicalPlan,
196    ) -> DataFusionResult<SendableRecordBatchStream> {
197        self.inner
198            .execute_logical_plan(plan)
199            .await?
200            .execute_stream()
201            .await
202    }
203}
204
205#[tonic::async_trait]
206impl ArrowFlightSqlService for FlightSqlService {
207    type FlightService = FlightSqlService;
208
209    async fn do_handshake(
210        &self,
211        _request: Request<Streaming<HandshakeRequest>>,
212    ) -> Result<Response<Pin<Box<dyn Stream<Item = Result<HandshakeResponse>> + Send>>>> {
213        info!("do_handshake");
214        // Favor middleware over handshake
215        // https://github.com/apache/arrow/issues/23836
216        // https://github.com/apache/arrow/issues/25848
217        Err(Status::unimplemented("handshake is not supported"))
218    }
219
220    async fn do_get_fallback(
221        &self,
222        request: Request<Ticket>,
223        _message: Any,
224    ) -> Result<Response<<Self as FlightService>::DoGetStream>> {
225        let (request, ctx) = self.new_context(request).await?;
226
227        let ticket = CommandTicket::try_decode(request.into_inner().ticket)
228            .map_err(flight_error_to_status)?;
229
230        // All query arms declare the logical plan schema (the very same schema
231        // that the matching `get_flight_info_*` advertises) rather than the
232        // physical stream schema. The two can differ in field nullability
233        // (e.g. aggregates over statistics-backed sources), and strict clients
234        // such as the ADBC Flight SQL driver reject a DoGet stream whose schema
235        // does not match the advertised FlightInfo schema exactly.
236        let plan = match ticket.command {
237            sql::Command::CommandStatementQuery(CommandStatementQuery { query, .. }) => ctx
238                .sql_to_logical_plan(&query)
239                .await
240                .map_err(df_error_to_status)?,
241            sql::Command::CommandPreparedStatementQuery(CommandPreparedStatementQuery {
242                prepared_statement_handle,
243            }) => {
244                let handle = QueryHandle::try_decode(prepared_statement_handle)?;
245
246                let plan = ctx
247                    .sql_to_logical_plan(handle.query())
248                    .await
249                    .map_err(df_error_to_status)?;
250
251                match decode_param_values(handle.parameters()).map_err(arrow_error_to_status)? {
252                    Some(param_values) => plan
253                        .with_param_values(param_values)
254                        .map_err(df_error_to_status)?,
255                    None => plan,
256                }
257            }
258            sql::Command::CommandStatementSubstraitPlan(CommandStatementSubstraitPlan {
259                plan,
260                ..
261            }) => {
262                let substrait_bytes = &plan
263                    .ok_or(Status::invalid_argument(
264                        "Expected substrait plan, found None",
265                    ))?
266                    .plan;
267
268                parse_substrait_bytes(&ctx, substrait_bytes).await?
269            }
270            _ => {
271                return Err(Status::internal(format!(
272                    "statement handle not found: {:?}",
273                    ticket.command
274                )));
275            }
276        };
277
278        let arrow_schema = get_schema_for_plan(&plan, self.config.schema_with_metadata);
279        let stream = ctx
280            .execute_logical_plan(plan)
281            .await
282            .map_err(df_error_to_status)?;
283
284        let arrow_stream = stream.map_err(|e| FlightError::ExternalError(e.into()));
285
286        let flight_data_stream = FlightDataEncoderBuilder::new()
287            .with_schema(arrow_schema)
288            .build(arrow_stream)
289            .map_err(flight_error_to_status)
290            .boxed();
291
292        Ok(Response::new(flight_data_stream))
293    }
294
295    async fn get_flight_info_statement(
296        &self,
297        query: CommandStatementQuery,
298        request: Request<FlightDescriptor>,
299    ) -> Result<Response<FlightInfo>> {
300        let (request, ctx) = self.new_context(request).await?;
301
302        let sql = &query.query;
303        info!("get_flight_info_statement with query={sql}");
304
305        let flight_descriptor = request.into_inner();
306
307        let plan = ctx
308            .sql_to_logical_plan(sql)
309            .await
310            .map_err(df_error_to_status)?;
311
312        let dataset_schema = get_schema_for_plan(&plan, self.config.schema_with_metadata);
313
314        // Form the response ticket (that the client will pass back to DoGet)
315        let ticket = CommandTicket::new(sql::Command::CommandStatementQuery(query))
316            .try_encode()
317            .map_err(flight_error_to_status)?;
318
319        let endpoint = FlightEndpoint::new().with_ticket(Ticket { ticket });
320
321        let flight_info = FlightInfo::new()
322            .with_endpoint(endpoint)
323            // return descriptor we were passed
324            .with_descriptor(flight_descriptor)
325            .try_with_schema(dataset_schema.as_ref())
326            .map_err(arrow_error_to_status)?;
327
328        Ok(Response::new(flight_info))
329    }
330
331    async fn get_flight_info_substrait_plan(
332        &self,
333        query: CommandStatementSubstraitPlan,
334        request: Request<FlightDescriptor>,
335    ) -> Result<Response<FlightInfo>> {
336        info!("get_flight_info_substrait_plan");
337        let (request, ctx) = self.new_context(request).await?;
338
339        let substrait_bytes = &query
340            .plan
341            .as_ref()
342            .ok_or(Status::invalid_argument(
343                "Expected substrait plan, found None",
344            ))?
345            .plan;
346
347        let plan = parse_substrait_bytes(&ctx, substrait_bytes).await?;
348
349        let flight_descriptor = request.into_inner();
350
351        let dataset_schema = get_schema_for_plan(&plan, self.config.schema_with_metadata);
352
353        // Form the response ticket (that the client will pass back to DoGet)
354        let ticket = CommandTicket::new(sql::Command::CommandStatementSubstraitPlan(query))
355            .try_encode()
356            .map_err(flight_error_to_status)?;
357
358        let endpoint = FlightEndpoint::new().with_ticket(Ticket { ticket });
359
360        let flight_info = FlightInfo::new()
361            .with_endpoint(endpoint)
362            // return descriptor we were passed
363            .with_descriptor(flight_descriptor)
364            .try_with_schema(dataset_schema.as_ref())
365            .map_err(arrow_error_to_status)?;
366
367        Ok(Response::new(flight_info))
368    }
369
370    async fn get_flight_info_prepared_statement(
371        &self,
372        cmd: CommandPreparedStatementQuery,
373        request: Request<FlightDescriptor>,
374    ) -> Result<Response<FlightInfo>> {
375        let (request, ctx) = self.new_context(request).await?;
376
377        let handle = QueryHandle::try_decode(cmd.prepared_statement_handle.clone())
378            .map_err(|e| Status::internal(format!("Error decoding handle: {e}")))?;
379
380        info!("get_flight_info_prepared_statement with handle={handle}");
381
382        let flight_descriptor = request.into_inner();
383
384        let sql = handle.query();
385        let plan = ctx
386            .sql_to_logical_plan(sql)
387            .await
388            .map_err(df_error_to_status)?;
389
390        let dataset_schema = get_schema_for_plan(&plan, self.config.schema_with_metadata);
391
392        // Form the response ticket (that the client will pass back to DoGet)
393        let ticket = CommandTicket::new(sql::Command::CommandPreparedStatementQuery(cmd))
394            .try_encode()
395            .map_err(flight_error_to_status)?;
396
397        let endpoint = FlightEndpoint::new().with_ticket(Ticket { ticket });
398
399        let flight_info = FlightInfo::new()
400            .with_endpoint(endpoint)
401            // return descriptor we were passed
402            .with_descriptor(flight_descriptor)
403            .try_with_schema(dataset_schema.as_ref())
404            .map_err(arrow_error_to_status)?;
405
406        Ok(Response::new(flight_info))
407    }
408
409    async fn get_flight_info_catalogs(
410        &self,
411        query: CommandGetCatalogs,
412        request: Request<FlightDescriptor>,
413    ) -> Result<Response<FlightInfo>> {
414        info!("get_flight_info_catalogs");
415        let (request, _ctx) = self.new_context(request).await?;
416
417        let flight_descriptor = request.into_inner();
418        let ticket = Ticket {
419            ticket: query.as_any().encode_to_vec().into(),
420        };
421        let endpoint = FlightEndpoint::new().with_ticket(ticket);
422
423        let flight_info = FlightInfo::new()
424            .try_with_schema(&query.into_builder().schema())
425            .map_err(arrow_error_to_status)?
426            .with_endpoint(endpoint)
427            .with_descriptor(flight_descriptor);
428
429        Ok(Response::new(flight_info))
430    }
431
432    async fn get_flight_info_schemas(
433        &self,
434        query: CommandGetDbSchemas,
435        request: Request<FlightDescriptor>,
436    ) -> Result<Response<FlightInfo>> {
437        info!("get_flight_info_schemas");
438        let (request, _ctx) = self.new_context(request).await?;
439        let flight_descriptor = request.into_inner();
440        let ticket = Ticket {
441            ticket: query.as_any().encode_to_vec().into(),
442        };
443        let endpoint = FlightEndpoint::new().with_ticket(ticket);
444
445        let flight_info = FlightInfo::new()
446            .try_with_schema(&query.into_builder().schema())
447            .map_err(arrow_error_to_status)?
448            .with_endpoint(endpoint)
449            .with_descriptor(flight_descriptor);
450
451        Ok(Response::new(flight_info))
452    }
453
454    async fn get_flight_info_tables(
455        &self,
456        query: CommandGetTables,
457        request: Request<FlightDescriptor>,
458    ) -> Result<Response<FlightInfo>> {
459        info!("get_flight_info_tables");
460        let (request, _ctx) = self.new_context(request).await?;
461
462        let flight_descriptor = request.into_inner();
463        let ticket = Ticket {
464            ticket: query.as_any().encode_to_vec().into(),
465        };
466        let endpoint = FlightEndpoint::new().with_ticket(ticket);
467
468        let flight_info = FlightInfo::new()
469            .try_with_schema(&query.into_builder().schema())
470            .map_err(arrow_error_to_status)?
471            .with_endpoint(endpoint)
472            .with_descriptor(flight_descriptor);
473
474        Ok(Response::new(flight_info))
475    }
476
477    async fn get_flight_info_table_types(
478        &self,
479        query: CommandGetTableTypes,
480        request: Request<FlightDescriptor>,
481    ) -> Result<Response<FlightInfo>> {
482        info!("get_flight_info_table_types");
483        let (request, _ctx) = self.new_context(request).await?;
484
485        let flight_descriptor = request.into_inner();
486        let ticket = Ticket {
487            ticket: query.as_any().encode_to_vec().into(),
488        };
489        let endpoint = FlightEndpoint::new().with_ticket(ticket);
490
491        let flight_info = FlightInfo::new()
492            .try_with_schema(&GET_TABLE_TYPES_SCHEMA)
493            .map_err(arrow_error_to_status)?
494            .with_endpoint(endpoint)
495            .with_descriptor(flight_descriptor);
496
497        Ok(Response::new(flight_info))
498    }
499
500    async fn get_flight_info_sql_info(
501        &self,
502        _query: CommandGetSqlInfo,
503        request: Request<FlightDescriptor>,
504    ) -> Result<Response<FlightInfo>> {
505        info!("get_flight_info_sql_info");
506        let (_, _) = self.new_context(request).await?;
507
508        Err(Status::unimplemented("Implement CommandGetSqlInfo"))
509    }
510
511    async fn get_flight_info_primary_keys(
512        &self,
513        _query: CommandGetPrimaryKeys,
514        request: Request<FlightDescriptor>,
515    ) -> Result<Response<FlightInfo>> {
516        info!("get_flight_info_primary_keys");
517        let (_, _) = self.new_context(request).await?;
518
519        Err(Status::unimplemented(
520            "Implement get_flight_info_primary_keys",
521        ))
522    }
523
524    async fn get_flight_info_exported_keys(
525        &self,
526        _query: CommandGetExportedKeys,
527        request: Request<FlightDescriptor>,
528    ) -> Result<Response<FlightInfo>> {
529        info!("get_flight_info_exported_keys");
530        let (_, _) = self.new_context(request).await?;
531
532        Err(Status::unimplemented(
533            "Implement get_flight_info_exported_keys",
534        ))
535    }
536
537    async fn get_flight_info_imported_keys(
538        &self,
539        _query: CommandGetImportedKeys,
540        request: Request<FlightDescriptor>,
541    ) -> Result<Response<FlightInfo>> {
542        info!("get_flight_info_imported_keys");
543        let (_, _) = self.new_context(request).await?;
544
545        Err(Status::unimplemented(
546            "Implement get_flight_info_imported_keys",
547        ))
548    }
549
550    async fn get_flight_info_cross_reference(
551        &self,
552        _query: CommandGetCrossReference,
553        request: Request<FlightDescriptor>,
554    ) -> Result<Response<FlightInfo>> {
555        info!("get_flight_info_cross_reference");
556        let (_, _) = self.new_context(request).await?;
557
558        Err(Status::unimplemented(
559            "Implement get_flight_info_cross_reference",
560        ))
561    }
562
563    async fn get_flight_info_xdbc_type_info(
564        &self,
565        _query: CommandGetXdbcTypeInfo,
566        request: Request<FlightDescriptor>,
567    ) -> Result<Response<FlightInfo>> {
568        info!("get_flight_info_xdbc_type_info");
569        let (_, _) = self.new_context(request).await?;
570
571        Err(Status::unimplemented(
572            "Implement get_flight_info_xdbc_type_info",
573        ))
574    }
575
576    async fn do_get_statement(
577        &self,
578        _ticket: TicketStatementQuery,
579        request: Request<Ticket>,
580    ) -> Result<Response<<Self as FlightService>::DoGetStream>> {
581        info!("do_get_statement");
582        let (_, _) = self.new_context(request).await?;
583
584        Err(Status::unimplemented("Implement do_get_statement"))
585    }
586
587    async fn do_get_prepared_statement(
588        &self,
589        _query: CommandPreparedStatementQuery,
590        request: Request<Ticket>,
591    ) -> Result<Response<<Self as FlightService>::DoGetStream>> {
592        info!("do_get_prepared_statement");
593        let (_, _) = self.new_context(request).await?;
594
595        Err(Status::unimplemented("Implement do_get_prepared_statement"))
596    }
597
598    async fn do_get_catalogs(
599        &self,
600        query: CommandGetCatalogs,
601        request: Request<Ticket>,
602    ) -> Result<Response<<Self as FlightService>::DoGetStream>> {
603        info!("do_get_catalogs");
604        let (_request, ctx) = self.new_context(request).await?;
605        let catalog_names = ctx.inner.catalog_names();
606
607        let mut builder = query.into_builder();
608        for catalog_name in &catalog_names {
609            builder.append(catalog_name);
610        }
611        let schema = builder.schema();
612        let batch = builder.build();
613        let stream = FlightDataEncoderBuilder::new()
614            .with_schema(schema)
615            .build(futures::stream::once(async { batch }))
616            .map_err(Status::from);
617        Ok(Response::new(Box::pin(stream)))
618    }
619
620    async fn do_get_schemas(
621        &self,
622        query: CommandGetDbSchemas,
623        request: Request<Ticket>,
624    ) -> Result<Response<<Self as FlightService>::DoGetStream>> {
625        info!("do_get_schemas");
626        let (_request, ctx) = self.new_context(request).await?;
627        let catalog_name = query.catalog.clone();
628        // Append all schemas to builder, the builder handles applying the filters.
629        let mut builder = query.into_builder();
630        if let Some(catalog_name) = &catalog_name {
631            if let Some(catalog) = ctx.inner.catalog(catalog_name) {
632                for schema_name in &catalog.schema_names() {
633                    builder.append(catalog_name, schema_name);
634                }
635            }
636        };
637
638        let schema = builder.schema();
639        let batch = builder.build();
640        let stream = FlightDataEncoderBuilder::new()
641            .with_schema(schema)
642            .build(futures::stream::once(async { batch }))
643            .map_err(Status::from);
644        Ok(Response::new(Box::pin(stream)))
645    }
646
647    async fn do_get_tables(
648        &self,
649        query: CommandGetTables,
650        request: Request<Ticket>,
651    ) -> Result<Response<<Self as FlightService>::DoGetStream>> {
652        info!("do_get_tables");
653        let (_request, ctx) = self.new_context(request).await?;
654        let catalog_name = query.catalog.clone();
655        let mut builder = query.into_builder();
656        // Append all schemas/tables to builder, the builder handles applying the filters.
657        if let Some(catalog_name) = &catalog_name {
658            if let Some(catalog) = ctx.inner.catalog(catalog_name) {
659                for schema_name in &catalog.schema_names() {
660                    if let Some(schema) = catalog.schema(schema_name) {
661                        for table_name in &schema.table_names() {
662                            if let Some(table) =
663                                schema.table(table_name).await.map_err(df_error_to_status)?
664                            {
665                                builder
666                                    .append(
667                                        catalog_name,
668                                        schema_name,
669                                        table_name,
670                                        table.table_type().to_string(),
671                                        &table.schema(),
672                                    )
673                                    .map_err(flight_error_to_status)?;
674                            }
675                        }
676                    }
677                }
678            }
679        };
680
681        let schema = builder.schema();
682        let batch = builder.build();
683        let stream = FlightDataEncoderBuilder::new()
684            .with_schema(schema)
685            .build(futures::stream::once(async { batch }))
686            .map_err(Status::from);
687        Ok(Response::new(Box::pin(stream)))
688    }
689
690    async fn do_get_table_types(
691        &self,
692        _query: CommandGetTableTypes,
693        request: Request<Ticket>,
694    ) -> Result<Response<<Self as FlightService>::DoGetStream>> {
695        info!("do_get_table_types");
696        let (_, _) = self.new_context(request).await?;
697
698        // Report all variants of table types that datafusion uses.
699        let table_types: ArrayRef = Arc::new(StringArray::from(
700            vec![TableType::Base, TableType::View, TableType::Temporary]
701                .into_iter()
702                .map(|tt| tt.to_string())
703                .collect::<Vec<String>>(),
704        ));
705
706        let batch = RecordBatch::try_from_iter(vec![("table_type", table_types)]).unwrap();
707
708        let stream = FlightDataEncoderBuilder::new()
709            .with_schema(GET_TABLE_TYPES_SCHEMA.clone())
710            .build(futures::stream::once(async { Ok(batch) }))
711            .map_err(Status::from);
712        Ok(Response::new(Box::pin(stream)))
713    }
714
715    async fn do_get_sql_info(
716        &self,
717        _query: CommandGetSqlInfo,
718        request: Request<Ticket>,
719    ) -> Result<Response<<Self as FlightService>::DoGetStream>> {
720        info!("do_get_sql_info");
721        let (_, _) = self.new_context(request).await?;
722
723        Err(Status::unimplemented("Implement do_get_sql_info"))
724    }
725
726    async fn do_get_primary_keys(
727        &self,
728        _query: CommandGetPrimaryKeys,
729        request: Request<Ticket>,
730    ) -> Result<Response<<Self as FlightService>::DoGetStream>> {
731        info!("do_get_primary_keys");
732        let (_, _) = self.new_context(request).await?;
733
734        Err(Status::unimplemented("Implement do_get_primary_keys"))
735    }
736
737    async fn do_get_exported_keys(
738        &self,
739        _query: CommandGetExportedKeys,
740        request: Request<Ticket>,
741    ) -> Result<Response<<Self as FlightService>::DoGetStream>> {
742        info!("do_get_exported_keys");
743        let (_, _) = self.new_context(request).await?;
744
745        Err(Status::unimplemented("Implement do_get_exported_keys"))
746    }
747
748    async fn do_get_imported_keys(
749        &self,
750        _query: CommandGetImportedKeys,
751        request: Request<Ticket>,
752    ) -> Result<Response<<Self as FlightService>::DoGetStream>> {
753        info!("do_get_imported_keys");
754        let (_, _) = self.new_context(request).await?;
755
756        Err(Status::unimplemented("Implement do_get_imported_keys"))
757    }
758
759    async fn do_get_cross_reference(
760        &self,
761        _query: CommandGetCrossReference,
762        request: Request<Ticket>,
763    ) -> Result<Response<<Self as FlightService>::DoGetStream>> {
764        info!("do_get_cross_reference");
765        let (_, _) = self.new_context(request).await?;
766
767        Err(Status::unimplemented("Implement do_get_cross_reference"))
768    }
769
770    async fn do_get_xdbc_type_info(
771        &self,
772        _query: CommandGetXdbcTypeInfo,
773        request: Request<Ticket>,
774    ) -> Result<Response<<Self as FlightService>::DoGetStream>> {
775        info!("do_get_xdbc_type_info");
776        let (_, _) = self.new_context(request).await?;
777
778        Err(Status::unimplemented("Implement do_get_xdbc_type_info"))
779    }
780
781    async fn do_put_statement_update(
782        &self,
783        _ticket: CommandStatementUpdate,
784        request: Request<PeekableFlightDataStream>,
785    ) -> Result<i64, Status> {
786        info!("do_put_statement_update");
787        let (_, _) = self.new_context(request).await?;
788
789        Err(Status::unimplemented("Implement do_put_statement_update"))
790    }
791
792    async fn do_put_prepared_statement_query(
793        &self,
794        query: CommandPreparedStatementQuery,
795        request: Request<PeekableFlightDataStream>,
796    ) -> Result<DoPutPreparedStatementResult, Status> {
797        info!("do_put_prepared_statement_query");
798        let (request, _) = self.new_context(request).await?;
799
800        let mut handle = QueryHandle::try_decode(query.prepared_statement_handle)?;
801
802        info!(
803            "do_action_create_prepared_statement query={:?}",
804            handle.query()
805        );
806        // Collect request flight data as parameters
807        // Decode and encode as a single ipc stream
808        let mut decoder =
809            FlightDataDecoder::new(request.into_inner().map_err(status_to_flight_error));
810        let schema = decode_schema(&mut decoder).await?;
811        let mut parameters = Vec::new();
812        let mut encoder =
813            StreamWriter::try_new(&mut parameters, &schema).map_err(arrow_error_to_status)?;
814        let mut total_rows = 0;
815        while let Some(msg) = decoder.try_next().await? {
816            match msg.payload {
817                DecodedPayload::None => {}
818                DecodedPayload::Schema(_) => {
819                    return Err(Status::invalid_argument(
820                        "parameter flight data must contain a single schema",
821                    ));
822                }
823                DecodedPayload::RecordBatch(record_batch) => {
824                    total_rows += record_batch.num_rows();
825                    encoder
826                        .write(&record_batch)
827                        .map_err(arrow_error_to_status)?;
828                }
829            }
830        }
831        if total_rows > 1 {
832            return Err(Status::invalid_argument(
833                "parameters should contain a single row",
834            ));
835        }
836
837        handle.set_parameters(Some(parameters.into()));
838
839        let res = DoPutPreparedStatementResult {
840            prepared_statement_handle: Some(Bytes::from(handle)),
841        };
842
843        Ok(res)
844    }
845
846    async fn do_put_prepared_statement_update(
847        &self,
848        _handle: CommandPreparedStatementUpdate,
849        request: Request<PeekableFlightDataStream>,
850    ) -> Result<i64, Status> {
851        info!("do_put_prepared_statement_update");
852        let (_, _) = self.new_context(request).await?;
853
854        // statements like "CREATE TABLE.." or "SET datafusion.nnn.." call this function
855        // and we are required to return some row count here
856        Ok(-1)
857    }
858
859    async fn do_put_substrait_plan(
860        &self,
861        _query: CommandStatementSubstraitPlan,
862        request: Request<PeekableFlightDataStream>,
863    ) -> Result<i64, Status> {
864        info!("do_put_prepared_statement_update");
865        let (_, _) = self.new_context(request).await?;
866
867        Err(Status::unimplemented(
868            "Implement do_put_prepared_statement_update",
869        ))
870    }
871
872    async fn do_action_create_prepared_statement(
873        &self,
874        query: ActionCreatePreparedStatementRequest,
875        request: Request<Action>,
876    ) -> Result<ActionCreatePreparedStatementResult, Status> {
877        let (_, ctx) = self.new_context(request).await?;
878
879        let sql = query.query.clone();
880        info!(
881            "do_action_create_prepared_statement query={:?}",
882            query.query
883        );
884
885        let plan = ctx
886            .sql_to_logical_plan(sql.as_str())
887            .await
888            .map_err(df_error_to_status)?;
889
890        let dataset_schema = get_schema_for_plan(&plan, self.config.schema_with_metadata);
891        let parameter_schema = parameter_schema_for_plan(&plan).map_err(|e| e.as_ref().clone())?;
892
893        let dataset_schema =
894            encode_schema(dataset_schema.as_ref()).map_err(arrow_error_to_status)?;
895        let parameter_schema =
896            encode_schema(parameter_schema.as_ref()).map_err(arrow_error_to_status)?;
897
898        let handle = QueryHandle::new(sql, None);
899
900        let res = ActionCreatePreparedStatementResult {
901            prepared_statement_handle: Bytes::from(handle),
902            dataset_schema,
903            parameter_schema,
904        };
905
906        Ok(res)
907    }
908
909    async fn do_action_close_prepared_statement(
910        &self,
911        query: ActionClosePreparedStatementRequest,
912        request: Request<Action>,
913    ) -> Result<(), Status> {
914        let (_, _) = self.new_context(request).await?;
915
916        let handle = query.prepared_statement_handle.as_ref();
917        if let Ok(handle) = std::str::from_utf8(handle) {
918            info!("do_action_close_prepared_statement with handle {handle:?}",);
919
920            // NOP since stateless
921        }
922        Ok(())
923    }
924
925    async fn do_action_create_prepared_substrait_plan(
926        &self,
927        _query: ActionCreatePreparedSubstraitPlanRequest,
928        request: Request<Action>,
929    ) -> Result<ActionCreatePreparedStatementResult, Status> {
930        info!("do_action_create_prepared_substrait_plan");
931        let (_, _) = self.new_context(request).await?;
932
933        Err(Status::unimplemented(
934            "Implement do_action_create_prepared_substrait_plan",
935        ))
936    }
937
938    async fn do_action_begin_transaction(
939        &self,
940        _query: ActionBeginTransactionRequest,
941        request: Request<Action>,
942    ) -> Result<ActionBeginTransactionResult, Status> {
943        let (_, _) = self.new_context(request).await?;
944
945        info!("do_action_begin_transaction");
946        Err(Status::unimplemented(
947            "Implement do_action_begin_transaction",
948        ))
949    }
950
951    async fn do_action_end_transaction(
952        &self,
953        _query: ActionEndTransactionRequest,
954        request: Request<Action>,
955    ) -> Result<(), Status> {
956        info!("do_action_end_transaction");
957        let (_, _) = self.new_context(request).await?;
958
959        Err(Status::unimplemented("Implement do_action_end_transaction"))
960    }
961
962    async fn do_action_begin_savepoint(
963        &self,
964        _query: ActionBeginSavepointRequest,
965        request: Request<Action>,
966    ) -> Result<ActionBeginSavepointResult, Status> {
967        info!("do_action_begin_savepoint");
968        let (_, _) = self.new_context(request).await?;
969
970        Err(Status::unimplemented("Implement do_action_begin_savepoint"))
971    }
972
973    async fn do_action_end_savepoint(
974        &self,
975        _query: ActionEndSavepointRequest,
976        request: Request<Action>,
977    ) -> Result<(), Status> {
978        info!("do_action_end_savepoint");
979        let (_, _) = self.new_context(request).await?;
980
981        Err(Status::unimplemented("Implement do_action_end_savepoint"))
982    }
983
984    async fn do_action_cancel_query(
985        &self,
986        _query: ActionCancelQueryRequest,
987        request: Request<Action>,
988    ) -> Result<ActionCancelQueryResult, Status> {
989        info!("do_action_cancel_query");
990        let (_, _) = self.new_context(request).await?;
991
992        Err(Status::unimplemented("Implement do_action_cancel_query"))
993    }
994
995    async fn register_sql_info(&self, _id: i32, _result: &SqlInfo) {}
996}
997
998/// Takes a substrait plan serialized as [Bytes] and deserializes this to
999/// a Datafusion [LogicalPlan]
1000async fn parse_substrait_bytes(
1001    ctx: &FlightSqlSessionContext,
1002    substrait: &Bytes,
1003) -> Result<LogicalPlan> {
1004    let substrait_plan = deserialize_bytes(substrait.to_vec())
1005        .await
1006        .map_err(df_error_to_status)?;
1007
1008    from_substrait_plan(&ctx.inner.state(), &substrait_plan)
1009        .await
1010        .map_err(df_error_to_status)
1011}
1012
1013/// Encodes the schema IPC encoded (schema_bytes)
1014fn encode_schema(schema: &Schema) -> std::result::Result<Bytes, ArrowError> {
1015    let options = IpcWriteOptions::default();
1016
1017    // encode the schema into the correct form
1018    let message: Result<IpcMessage, ArrowError> = SchemaAsIpc::new(schema, &options).try_into();
1019
1020    let IpcMessage(schema) = message?;
1021
1022    Ok(schema)
1023}
1024
1025/// Return the schema for the specified logical plan
1026fn get_schema_for_plan(logical_plan: &LogicalPlan, with_metadata: bool) -> SchemaRef {
1027    let schema: SchemaRef = if with_metadata {
1028        // Get the DFSchema which contains table qualifiers
1029        let df_schema = logical_plan.schema();
1030
1031        // Convert to Arrow Schema and add table name metadata to fields
1032        let fields_with_metadata: Vec<_> = df_schema
1033            .iter()
1034            .map(|(qualifier, field)| {
1035                // If there's a table qualifier, add it as metadata
1036                if let Some(table_ref) = qualifier {
1037                    let mut metadata = field.metadata().clone();
1038                    metadata.insert("table_name".to_string(), table_ref.to_string());
1039                    field.as_ref().clone().with_metadata(metadata)
1040                } else {
1041                    field.as_ref().clone()
1042                }
1043            })
1044            .collect();
1045
1046        Arc::new(Schema::new_with_metadata(
1047            fields_with_metadata,
1048            df_schema.as_ref().metadata().clone(),
1049        ))
1050    } else {
1051        Arc::new(logical_plan.schema().as_arrow().clone())
1052    };
1053
1054    // Use an empty FlightDataEncoder to determine the schema of the encoded flight data.
1055    // This is necessary as the schema can change based on dictionary hydration behavior.
1056    let flight_data_stream = FlightDataEncoderBuilder::new()
1057        // Inform the builder of the input stream schema
1058        .with_schema(schema)
1059        .build(futures::stream::iter([]));
1060
1061    // Retrieve the schema of the encoded data
1062    flight_data_stream
1063        .known_schema()
1064        .expect("flight data schema should be known when explicitly provided via `with_schema`")
1065}
1066
1067fn parameter_schema_for_plan(plan: &LogicalPlan) -> Result<SchemaRef, Box<Status>> {
1068    let parameters = plan
1069        .get_parameter_types()
1070        .map_err(df_error_to_status)?
1071        .into_iter()
1072        .map(|(name, dt)| {
1073            dt.map(|dt| (name.clone(), dt)).ok_or_else(|| {
1074                Status::internal(format!(
1075                    "unable to determine type of query parameter {name}"
1076                ))
1077            })
1078        })
1079        // Collect into BTreeMap so we get a consistent order of the parameters
1080        .collect::<Result<BTreeMap<_, _>, Status>>()?;
1081
1082    let mut builder = SchemaBuilder::new();
1083    parameters
1084        .into_iter()
1085        .for_each(|(name, typ)| builder.push(Field::new(name, typ, false)));
1086    Ok(builder.finish().into())
1087}
1088
1089fn arrow_error_to_status(err: ArrowError) -> Status {
1090    Status::internal(format!("{err:?}"))
1091}
1092
1093fn flight_error_to_status(err: FlightError) -> Status {
1094    Status::internal(format!("{err:?}"))
1095}
1096
1097fn df_error_to_status(err: DataFusionError) -> Status {
1098    Status::internal(format!("{err:?}"))
1099}
1100
1101fn status_to_flight_error(status: Status) -> FlightError {
1102    FlightError::Tonic(Box::new(status))
1103}
1104
1105async fn decode_schema(decoder: &mut FlightDataDecoder) -> Result<SchemaRef, Status> {
1106    while let Some(msg) = decoder.try_next().await? {
1107        match msg.payload {
1108            DecodedPayload::None => {}
1109            DecodedPayload::Schema(schema) => {
1110                return Ok(schema);
1111            }
1112            DecodedPayload::RecordBatch(_) => {
1113                return Err(Status::invalid_argument(
1114                    "parameter flight data must have a known schema",
1115                ));
1116            }
1117        }
1118    }
1119
1120    Err(Status::invalid_argument(
1121        "parameter flight data must have a schema",
1122    ))
1123}
1124
1125// Decode parameter ipc stream as ParamValues
1126fn decode_param_values(parameters: Option<&[u8]>) -> Result<Option<ParamValues>, ArrowError> {
1127    parameters
1128        .map(|parameters| {
1129            let decoder = StreamReader::try_new(parameters, None)?;
1130            let schema = decoder.schema();
1131            let batches = decoder.into_iter().collect::<Result<Vec<_>, _>>()?;
1132            let batch = concat_batches(&schema, batches.iter())?;
1133            Ok(record_to_param_values(&batch)?)
1134        })
1135        .transpose()
1136}
1137
1138// Converts a record batch with a single row into ParamValues
1139fn record_to_param_values(batch: &RecordBatch) -> Result<ParamValues, DataFusionError> {
1140    let mut param_values: Vec<(String, Option<usize>, ScalarValue)> = Vec::new();
1141
1142    let mut is_list = true;
1143    for col_index in 0..batch.num_columns() {
1144        let array = batch.column(col_index);
1145        let scalar = ScalarValue::try_from_array(array, 0)?;
1146        let name = batch
1147            .schema_ref()
1148            .field(col_index)
1149            .name()
1150            .trim_start_matches('$')
1151            .to_string();
1152        let index = name.parse().ok();
1153        is_list &= index.is_some();
1154        param_values.push((name, index, scalar));
1155    }
1156    if is_list {
1157        let mut values: Vec<(Option<usize>, ScalarValue)> = param_values
1158            .into_iter()
1159            .map(|(_name, index, value)| (index, value))
1160            .collect();
1161        values.sort_by_key(|(index, _value)| *index);
1162        Ok(values
1163            .into_iter()
1164            .map(|(_index, value)| value)
1165            .collect::<Vec<ScalarValue>>()
1166            .into())
1167    } else {
1168        Ok(param_values
1169            .into_iter()
1170            .map(|(name, _index, value)| (name, value))
1171            .collect::<Vec<(String, ScalarValue)>>()
1172            .into())
1173    }
1174}