This is an automated email from the ASF dual-hosted git repository.
agrove pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-ballista.git
The following commit(s) were added to refs/heads/master by this push:
new b4653522 Store sessions so users can register tables and query them
through flight (#269)
b4653522 is described below
commit b465352233bb980b32b38b6bfded6c7e033dd771
Author: Brent Gardner <[email protected]>
AuthorDate: Sat Sep 24 13:10:12 2022 -0700
Store sessions so users can register tables and query them through flight
(#269)
---
ballista/rust/core/Cargo.toml | 2 +-
ballista/rust/core/proto/ballista.proto | 2 +
ballista/rust/core/src/client.rs | 4 +
.../core/src/execution_plans/distributed_query.rs | 11 +-
.../core/src/execution_plans/shuffle_reader.rs | 11 +-
ballista/rust/core/src/serde/mod.rs | 14 ++
.../rust/core/src/serde/scheduler/from_proto.rs | 2 +
ballista/rust/core/src/serde/scheduler/mod.rs | 2 +
ballista/rust/core/src/serde/scheduler/to_proto.rs | 4 +
ballista/rust/executor/src/flight_service.rs | 22 +-
ballista/rust/scheduler/src/flight_sql.rs | 243 ++++++++++++++++++---
11 files changed, 280 insertions(+), 37 deletions(-)
diff --git a/ballista/rust/core/Cargo.toml b/ballista/rust/core/Cargo.toml
index 4561c1e3..2ef4995a 100644
--- a/ballista/rust/core/Cargo.toml
+++ b/ballista/rust/core/Cargo.toml
@@ -38,7 +38,7 @@ simd = ["datafusion/simd"]
[dependencies]
ahash = { version = "0.8", default-features = false }
-arrow-flight = { version = "22.0.0" }
+arrow-flight = { version = "22.0.0", features = ["flight-sql-experimental"] }
async-trait = "0.1.41"
chrono = { version = "0.4", default-features = false }
clap = { version = "3", features = ["derive", "cargo"] }
diff --git a/ballista/rust/core/proto/ballista.proto
b/ballista/rust/core/proto/ballista.proto
index d998cc7b..a2b5f1fd 100644
--- a/ballista/rust/core/proto/ballista.proto
+++ b/ballista/rust/core/proto/ballista.proto
@@ -517,6 +517,8 @@ message FetchPartition {
uint32 stage_id = 2;
uint32 partition_id = 3;
string path = 4;
+ string host = 5;
+ uint32 port = 6;
}
// Mapping from partition id to executor id
diff --git a/ballista/rust/core/src/client.rs b/ballista/rust/core/src/client.rs
index 61c19c64..13276f1b 100644
--- a/ballista/rust/core/src/client.rs
+++ b/ballista/rust/core/src/client.rs
@@ -80,12 +80,16 @@ impl BallistaClient {
stage_id: usize,
partition_id: usize,
path: &str,
+ host: &str,
+ port: u16,
) -> Result<SendableRecordBatchStream> {
let action = Action::FetchPartition {
job_id: job_id.to_string(),
stage_id,
partition_id,
path: path.to_owned(),
+ host: host.to_string(),
+ port,
};
self.execute_action(&action).await
}
diff --git a/ballista/rust/core/src/execution_plans/distributed_query.rs
b/ballista/rust/core/src/execution_plans/distributed_query.rs
index e9d85281..67393c20 100644
--- a/ballista/rust/core/src/execution_plans/distributed_query.rs
+++ b/ballista/rust/core/src/execution_plans/distributed_query.rs
@@ -317,16 +317,19 @@ async fn fetch_partition(
let partition_id = location.partition_id.ok_or_else(|| {
DataFusionError::Internal("Received empty partition id".to_owned())
})?;
- let mut ballista_client =
- BallistaClient::try_new(metadata.host.as_str(), metadata.port as u16)
- .await
- .map_err(|e| DataFusionError::Execution(format!("{:?}", e)))?;
+ let host = metadata.host.as_str();
+ let port = metadata.port as u16;
+ let mut ballista_client = BallistaClient::try_new(host, port)
+ .await
+ .map_err(|e| DataFusionError::Execution(format!("{:?}", e)))?;
ballista_client
.fetch_partition(
&partition_id.job_id,
partition_id.stage_id as usize,
partition_id.partition_id as usize,
&location.path,
+ host,
+ port,
)
.await
.map_err(|e| DataFusionError::Execution(format!("{:?}", e)))
diff --git a/ballista/rust/core/src/execution_plans/shuffle_reader.rs
b/ballista/rust/core/src/execution_plans/shuffle_reader.rs
index 17609c77..0c153d3e 100644
--- a/ballista/rust/core/src/execution_plans/shuffle_reader.rs
+++ b/ballista/rust/core/src/execution_plans/shuffle_reader.rs
@@ -185,16 +185,19 @@ async fn fetch_partition(
let partition_id = &location.partition_id;
// TODO for shuffle client connections, we should avoid creating new
connections again and again.
// And we should also avoid to keep alive too many connections for long
time.
- let mut ballista_client =
- BallistaClient::try_new(metadata.host.as_str(), metadata.port as u16)
- .await
- .map_err(|e| DataFusionError::Execution(format!("{:?}", e)))?;
+ let host = metadata.host.as_str();
+ let port = metadata.port as u16;
+ let mut ballista_client = BallistaClient::try_new(host, port)
+ .await
+ .map_err(|e| DataFusionError::Execution(format!("{:?}", e)))?;
ballista_client
.fetch_partition(
&partition_id.job_id,
partition_id.stage_id as usize,
partition_id.partition_id as usize,
&location.path,
+ host,
+ port,
)
.await
.map_err(|e| DataFusionError::Execution(format!("{:?}", e)))
diff --git a/ballista/rust/core/src/serde/mod.rs
b/ballista/rust/core/src/serde/mod.rs
index 1e3be74b..4553c2f4 100644
--- a/ballista/rust/core/src/serde/mod.rs
+++ b/ballista/rust/core/src/serde/mod.rs
@@ -19,6 +19,7 @@
//! as convenience code for interacting with the generated code.
use crate::{error::BallistaError, serde::scheduler::Action as BallistaAction};
+use arrow_flight::sql::ProstMessageExt;
use datafusion::execution::runtime_env::RuntimeEnv;
use datafusion::logical_plan::{FunctionRegistry, Operator};
use datafusion::physical_plan::join_utils::JoinSide;
@@ -39,6 +40,19 @@ pub mod generated;
pub mod physical_plan;
pub mod scheduler;
+impl ProstMessageExt for protobuf::Action {
+ fn type_url() -> &'static str {
+ "type.googleapis.com/arrow.flight.protocol.sql.Action"
+ }
+
+ fn as_any(&self) -> prost_types::Any {
+ prost_types::Any {
+ type_url: protobuf::Action::type_url().to_string(),
+ value: self.encode_to_vec(),
+ }
+ }
+}
+
pub fn decode_protobuf(bytes: &[u8]) -> Result<BallistaAction, BallistaError> {
let mut buf = Cursor::new(bytes);
diff --git a/ballista/rust/core/src/serde/scheduler/from_proto.rs
b/ballista/rust/core/src/serde/scheduler/from_proto.rs
index 536e2a5c..cfe0cbbf 100644
--- a/ballista/rust/core/src/serde/scheduler/from_proto.rs
+++ b/ballista/rust/core/src/serde/scheduler/from_proto.rs
@@ -44,6 +44,8 @@ impl TryInto<Action> for protobuf::Action {
stage_id: fetch.stage_id as usize,
partition_id: fetch.partition_id as usize,
path: fetch.path,
+ host: fetch.host,
+ port: fetch.port as u16,
}),
_ => Err(BallistaError::General(
"scheduler::from_proto(Action) invalid or missing
action".to_owned(),
diff --git a/ballista/rust/core/src/serde/scheduler/mod.rs
b/ballista/rust/core/src/serde/scheduler/mod.rs
index c1fa78a2..7a710f49 100644
--- a/ballista/rust/core/src/serde/scheduler/mod.rs
+++ b/ballista/rust/core/src/serde/scheduler/mod.rs
@@ -40,6 +40,8 @@ pub enum Action {
stage_id: usize,
partition_id: usize,
path: String,
+ host: String,
+ port: u16,
},
}
diff --git a/ballista/rust/core/src/serde/scheduler/to_proto.rs
b/ballista/rust/core/src/serde/scheduler/to_proto.rs
index 5a7ad394..0c43b533 100644
--- a/ballista/rust/core/src/serde/scheduler/to_proto.rs
+++ b/ballista/rust/core/src/serde/scheduler/to_proto.rs
@@ -41,12 +41,16 @@ impl TryInto<protobuf::Action> for Action {
stage_id,
partition_id,
path,
+ host,
+ port,
} => Ok(protobuf::Action {
action_type:
Some(ActionType::FetchPartition(protobuf::FetchPartition {
job_id,
stage_id: stage_id as u32,
partition_id: partition_id as u32,
path,
+ host,
+ port: port as u32,
})),
settings: vec![],
}),
diff --git a/ballista/rust/executor/src/flight_service.rs
b/ballista/rust/executor/src/flight_service.rs
index 2c25107d..82c4f0ae 100644
--- a/ballista/rust/executor/src/flight_service.rs
+++ b/ballista/rust/executor/src/flight_service.rs
@@ -17,6 +17,7 @@
//! Implementation of the Apache Arrow Flight protocol that wraps an executor.
+use std::convert::TryFrom;
use std::fs::File;
use std::pin::Pin;
@@ -35,7 +36,7 @@ use datafusion::arrow::{
record_batch::RecordBatch,
};
use futures::{Stream, StreamExt};
-use log::{debug, warn};
+use log::{debug, info, warn};
use std::io::{Read, Seek};
use tokio::sync::mpsc::channel;
use tokio::{
@@ -43,6 +44,7 @@ use tokio::{
task,
};
use tokio_stream::wrappers::ReceiverStream;
+use tonic::metadata::MetadataValue;
use tonic::{Request, Response, Status, Streaming};
type FlightDataSender = Sender<Result<FlightData, Status>>;
@@ -135,7 +137,23 @@ impl FlightService for BallistaFlightService {
&self,
_request: Request<Streaming<HandshakeRequest>>,
) -> Result<Response<Self::HandshakeStream>, Status> {
- Err(Status::unimplemented("handshake"))
+ let token = uuid::Uuid::new_v4();
+ info!("do_handshake token={}", token);
+
+ let result = HandshakeResponse {
+ protocol_version: 0,
+ payload: token.as_bytes().to_vec(),
+ };
+ let result = Ok(result);
+ let output = futures::stream::iter(vec![result]);
+ let str = format!("Bearer {}", token);
+ let mut resp: Response<
+ Pin<Box<dyn Stream<Item = Result<_, Status>> + Sync + Send>>,
+ > = Response::new(Box::pin(output));
+ let md = MetadataValue::try_from(str)
+ .map_err(|_| Status::invalid_argument("authorization not
parsable"))?;
+ resp.metadata_mut().insert("authorization", md);
+ Ok(resp)
}
async fn list_flights(
diff --git a/ballista/rust/scheduler/src/flight_sql.rs
b/ballista/rust/scheduler/src/flight_sql.rs
index 2b1c3fcd..a6218c48 100644
--- a/ballista/rust/scheduler/src/flight_sql.rs
+++ b/ballista/rust/scheduler/src/flight_sql.rs
@@ -24,25 +24,33 @@ use arrow_flight::sql::{
CommandGetDbSchemas, CommandGetExportedKeys, CommandGetImportedKeys,
CommandGetPrimaryKeys, CommandGetSqlInfo, CommandGetTableTypes,
CommandGetTables,
CommandPreparedStatementQuery, CommandPreparedStatementUpdate,
CommandStatementQuery,
- CommandStatementUpdate, SqlInfo, TicketStatementQuery,
+ CommandStatementUpdate, ProstAnyExt, SqlInfo, TicketStatementQuery,
};
use arrow_flight::{
- Action, FlightData, FlightDescriptor, FlightEndpoint, FlightInfo,
Location, Ticket,
+ Action, FlightData, FlightDescriptor, FlightEndpoint, FlightInfo,
HandshakeRequest,
+ HandshakeResponse, Location, Ticket,
};
use log::{debug, error, warn};
use std::collections::HashMap;
+use std::convert::TryFrom;
+use std::pin::Pin;
+use std::str::FromStr;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use tonic::{Request, Response, Status, Streaming};
use crate::scheduler_server::SchedulerServer;
+use arrow_flight::flight_service_client::FlightServiceClient;
+use arrow_flight::sql::ProstMessageExt;
use arrow_flight::SchemaAsIpc;
use ballista_core::config::BallistaConfig;
use ballista_core::serde::protobuf;
+use ballista_core::serde::protobuf::action::ActionType::FetchPartition;
use ballista_core::serde::protobuf::job_status;
use ballista_core::serde::protobuf::CompletedJob;
use ballista_core::serde::protobuf::JobStatus;
use ballista_core::serde::protobuf::PhysicalPlanNode;
+use ballista_core::utils::create_grpc_client_connection;
use datafusion::arrow;
use datafusion::arrow::datatypes::Schema;
use datafusion::arrow::ipc::writer::{IpcDataGenerator, IpcWriteOptions};
@@ -52,11 +60,14 @@ use datafusion::prelude::SessionContext;
use datafusion_proto::protobuf::LogicalPlanNode;
use prost::Message;
use tokio::time::sleep;
+use tonic::codegen::futures_core::Stream;
+use tonic::metadata::MetadataValue;
use uuid::Uuid;
pub struct FlightSqlServiceImpl {
server: SchedulerServer<LogicalPlanNode, PhysicalPlanNode>,
statements: Arc<Mutex<HashMap<Uuid, LogicalPlan>>>,
+ contexts: Arc<Mutex<HashMap<Uuid, Arc<SessionContext>>>>,
}
impl FlightSqlServiceImpl {
@@ -64,10 +75,11 @@ impl FlightSqlServiceImpl {
Self {
server,
statements: Arc::new(Mutex::new(HashMap::new())),
+ contexts: Arc::new(Mutex::new(HashMap::new())),
}
}
- async fn create_ctx(&self) -> Result<Arc<SessionContext>, Status> {
+ async fn create_ctx(&self) -> Result<Uuid, Status> {
let config_builder = BallistaConfig::builder();
let config = config_builder
.build()
@@ -81,7 +93,45 @@ impl FlightSqlServiceImpl {
.map_err(|e| {
Status::internal(format!("Failed to create SessionContext:
{:?}", e))
})?;
- Ok(ctx)
+ let handle = Uuid::new_v4();
+ let mut contexts = self
+ .contexts
+ .try_lock()
+ .map_err(|e| Status::internal(format!("Error locking contexts:
{}", e)))?;
+ contexts.insert(handle.clone(), ctx);
+ Ok(handle)
+ }
+
+ fn get_ctx<T>(&self, req: &Request<T>) -> Result<Arc<SessionContext>,
Status> {
+ let auth = req
+ .metadata()
+ .get("authorization")
+ .ok_or(Status::internal("No authorization header!"))?;
+ let str = auth
+ .to_str()
+ .map_err(|e| Status::internal(format!("Error parsing header: {}",
e)))?;
+ let authorization = str.to_string();
+ let bearer = "Bearer ";
+ if !authorization.starts_with(bearer) {
+ Err(Status::internal(format!("Invalid auth header!")))?;
+ }
+ let auth = authorization[bearer.len()..].to_string();
+
+ let handle = Uuid::from_str(auth.as_str())
+ .map_err(|e| Status::internal(format!("Error locking contexts:
{}", e)))?;
+ let contexts = self
+ .contexts
+ .try_lock()
+ .map_err(|e| Status::internal(format!("Error locking contexts:
{}", e)))?;
+ let context = if let Some(context) = contexts.get(&handle) {
+ context
+ } else {
+ Err(Status::internal(format!(
+ "Context handle not found: {}",
+ handle
+ )))?
+ };
+ Ok(context.clone())
}
async fn prepare_statement(
@@ -146,12 +196,21 @@ impl FlightSqlServiceImpl {
) -> Result<Vec<FlightEndpoint>, Status> {
let mut fieps: Vec<_> = vec![];
for loc in completed.partition_location.iter() {
+ let (host, port) = if let Some(ref md) = loc.executor_meta {
+ (md.host.clone(), md.port)
+ } else {
+ Err(Status::internal(
+ "Invalid partition location, missing executor
metadata".to_string(),
+ ))?
+ };
let fetch = if let Some(ref id) = loc.partition_id {
let fetch = protobuf::FetchPartition {
job_id: id.job_id.clone(),
stage_id: id.stage_id,
partition_id: id.partition_id,
path: loc.path.clone(),
+ host: host.clone(),
+ port,
};
protobuf::Action {
action_type:
Some(protobuf::action::ActionType::FetchPartition(
@@ -162,23 +221,17 @@ impl FlightSqlServiceImpl {
} else {
Err(Status::internal("Error getting partition
ID".to_string()))?
};
- let authority = if let Some(ref md) = loc.executor_meta {
- format!("{}:{}", md.host, md.port)
- } else {
- Err(Status::internal(
- "Invalid partition location, missing executor
metadata".to_string(),
- ))?
- };
if let Some(ref stats) = loc.partition_stats {
*num_rows += stats.num_rows;
*num_bytes += stats.num_bytes;
} else {
Err(Status::internal("Error getting stats".to_string()))?
}
+ let authority = format!("{}:{}", &host, &port); // TODO: my host &
port
let loc = Location {
uri: format!("grpc+tcp://{}", authority),
};
- let buf = fetch.encode_to_vec();
+ let buf = fetch.as_any().encode_to_vec();
let ticket = Ticket { ticket: buf };
let fiep = FlightEndpoint {
ticket: Some(ticket),
@@ -309,33 +362,140 @@ impl FlightSqlServiceImpl {
impl FlightSqlService for FlightSqlServiceImpl {
type FlightService = FlightSqlServiceImpl;
+ async fn do_handshake(
+ &self,
+ request: Request<Streaming<HandshakeRequest>>,
+ ) -> Result<
+ Response<Pin<Box<dyn Stream<Item = Result<HandshakeResponse, Status>>
+ Send>>>,
+ Status,
+ > {
+ debug!("do_handshake");
+ for md in request.metadata().iter() {
+ debug!("{:?}", md);
+ }
+
+ let basic = "Basic ";
+ let authorization = request
+ .metadata()
+ .get("authorization")
+ .ok_or(Status::invalid_argument("authorization field not
present"))?
+ .to_str()
+ .map_err(|_| Status::invalid_argument("authorization not
parsable"))?;
+ if !authorization.starts_with(basic) {
+ Err(Status::invalid_argument(format!(
+ "Auth type not implemented: {}",
+ authorization
+ )))?;
+ }
+ let base64 = &authorization[basic.len()..];
+ let bytes = base64::decode(base64)
+ .map_err(|_| Status::invalid_argument("authorization not
parsable"))?;
+ let str = String::from_utf8(bytes)
+ .map_err(|_| Status::invalid_argument("authorization not
parsable"))?;
+ let parts: Vec<_> = str.split(":").collect();
+ if parts.len() != 2 {
+ Err(Status::invalid_argument(format!(
+ "Invalid authorization header"
+ )))?;
+ }
+ let user = parts[0];
+ let pass = parts[1];
+ if user != "admin" || pass != "password" {
+ Err(Status::unauthenticated("Invalid credentials!"))?
+ }
+
+ let token = self.create_ctx().await?;
+
+ let result = HandshakeResponse {
+ protocol_version: 0,
+ payload: token.as_bytes().to_vec(),
+ };
+ let result = Ok(result);
+ let output = futures::stream::iter(vec![result]);
+ let str = format!("Bearer {}", token.to_string());
+ let mut resp: Response<Pin<Box<dyn Stream<Item = Result<_, _>> +
Send>>> =
+ Response::new(Box::pin(output));
+ let md = MetadataValue::try_from(str)
+ .map_err(|_| Status::invalid_argument("authorization not
parsable"))?;
+ resp.metadata_mut().insert("authorization", md);
+ Ok(resp)
+ }
+
+ async fn do_get_fallback(
+ &self,
+ _request: Request<Ticket>,
+ message: prost_types::Any,
+ ) -> Result<Response<<Self as FlightService>::DoGetStream>, Status> {
+ println!("type_url: {}", message.type_url);
+ if message.is::<protobuf::Action>() {
+ println!("got action!");
+ let action: protobuf::Action = message
+ .unpack()
+ .map_err(|e| Status::internal(format!("{:?}", e)))?
+ .ok_or(Status::internal("Expected an Action but got None!"))?;
+ println!("action={:?}", action);
+ let (host, port) = match &action.action_type {
+ Some(FetchPartition(fp)) => (fp.host.clone(), fp.port),
+ None => Err(Status::internal("Expected an ActionType but got
None!"))?,
+ };
+
+ let addr = format!("http://{}:{}", host, port);
+ println!("BallistaClient connecting to {}", addr);
+ let connection =
+ create_grpc_client_connection(addr.clone())
+ .await
+ .map_err(|e| {
+ Status::internal(format!(
+ "Error connecting to Ballista scheduler or executor at
{}: {:?}",
+ addr, e
+ ))
+ })?;
+ let mut flight_client = FlightServiceClient::new(connection);
+ let buf = action.encode_to_vec();
+ let request = Request::new(Ticket { ticket: buf });
+
+ let stream = flight_client
+ .do_get(request)
+ .await
+ .map_err(|e| Status::internal(format!("{:?}", e)))?
+ .into_inner();
+ return Ok(Response::new(Box::pin(stream)));
+ }
+
+ Err(Status::unimplemented(format!(
+ "do_get: The defined request is invalid: {}",
+ message.type_url
+ )))
+ }
+
async fn get_flight_info_statement(
&self,
query: CommandStatementQuery,
- _request: Request<FlightDescriptor>,
+ request: Request<FlightDescriptor>,
) -> Result<Response<FlightInfo>, Status> {
- debug!("Got query:\n{}", query.query);
+ debug!("get_flight_info_statement query:\n{}", query.query);
- let ctx = self.create_ctx().await?;
+ let ctx = self.get_ctx(&request)?;
let plan = Self::prepare_statement(&query.query, &ctx).await?;
let resp = self.execute_plan(ctx, &plan).await?;
- debug!("Responding to query...");
+ debug!("Returning flight info...");
Ok(resp)
}
async fn get_flight_info_prepared_statement(
&self,
handle: CommandPreparedStatementQuery,
- _request: Request<FlightDescriptor>,
+ request: Request<FlightDescriptor>,
) -> Result<Response<FlightInfo>, Status> {
- let ctx = self.create_ctx().await?;
+ debug!("get_flight_info_prepared_statement");
+ let ctx = self.get_ctx(&request)?;
let handle =
Uuid::from_slice(handle.prepared_statement_handle.as_slice())
.map_err(|e| Status::internal(format!("Error decoding handle: {}",
e)))?;
let plan = self.get_plan(&handle)?;
let resp = self.execute_plan(ctx, &plan).await?;
- debug!("Responding to query...");
+ debug!("Responding to query {}...", handle);
Ok(resp)
}
@@ -344,6 +504,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetCatalogs,
_request: Request<FlightDescriptor>,
) -> Result<Response<FlightInfo>, Status> {
+ debug!("get_flight_info_catalogs");
Err(Status::unimplemented("Implement get_flight_info_catalogs"))
}
async fn get_flight_info_schemas(
@@ -351,6 +512,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetDbSchemas,
_request: Request<FlightDescriptor>,
) -> Result<Response<FlightInfo>, Status> {
+ debug!("get_flight_info_schemas");
Err(Status::unimplemented("Implement get_flight_info_schemas"))
}
async fn get_flight_info_tables(
@@ -358,6 +520,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetTables,
_request: Request<FlightDescriptor>,
) -> Result<Response<FlightInfo>, Status> {
+ debug!("get_flight_info_tables");
Err(Status::unimplemented("Implement get_flight_info_tables"))
}
async fn get_flight_info_table_types(
@@ -365,6 +528,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetTableTypes,
_request: Request<FlightDescriptor>,
) -> Result<Response<FlightInfo>, Status> {
+ debug!("get_flight_info_table_types");
Err(Status::unimplemented(
"Implement get_flight_info_table_types",
))
@@ -374,6 +538,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetSqlInfo,
_request: Request<FlightDescriptor>,
) -> Result<Response<FlightInfo>, Status> {
+ debug!("get_flight_info_sql_info");
// TODO: implement for FlightSQL JDBC to work
Err(Status::unimplemented("Implement CommandGetSqlInfo"))
}
@@ -382,6 +547,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetPrimaryKeys,
_request: Request<FlightDescriptor>,
) -> Result<Response<FlightInfo>, Status> {
+ debug!("get_flight_info_primary_keys");
Err(Status::unimplemented(
"Implement get_flight_info_primary_keys",
))
@@ -391,6 +557,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetExportedKeys,
_request: Request<FlightDescriptor>,
) -> Result<Response<FlightInfo>, Status> {
+ debug!("get_flight_info_exported_keys");
Err(Status::unimplemented(
"Implement get_flight_info_exported_keys",
))
@@ -400,6 +567,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetImportedKeys,
_request: Request<FlightDescriptor>,
) -> Result<Response<FlightInfo>, Status> {
+ debug!("get_flight_info_imported_keys");
Err(Status::unimplemented(
"Implement get_flight_info_imported_keys",
))
@@ -409,6 +577,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetCrossReference,
_request: Request<FlightDescriptor>,
) -> Result<Response<FlightInfo>, Status> {
+ debug!("get_flight_info_cross_reference");
Err(Status::unimplemented(
"Implement get_flight_info_cross_reference",
))
@@ -419,6 +588,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_ticket: TicketStatementQuery,
_request: Request<Ticket>,
) -> Result<Response<<Self as FlightService>::DoGetStream>, Status> {
+ debug!("do_get_statement");
// let handle = Uuid::from_slice(&ticket.statement_handle)
// .map_err(|e| Status::internal(format!("Error decoding ticket:
{}", e)))?;
// let statements = self.statements.try_lock()
@@ -432,6 +602,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandPreparedStatementQuery,
_request: Request<Ticket>,
) -> Result<Response<<Self as FlightService>::DoGetStream>, Status> {
+ debug!("do_get_prepared_statement");
Err(Status::unimplemented("Implement do_get_prepared_statement"))
}
async fn do_get_catalogs(
@@ -439,6 +610,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetCatalogs,
_request: Request<Ticket>,
) -> Result<Response<<Self as FlightService>::DoGetStream>, Status> {
+ debug!("do_get_catalogs");
Err(Status::unimplemented("Implement do_get_catalogs"))
}
async fn do_get_schemas(
@@ -446,6 +618,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetDbSchemas,
_request: Request<Ticket>,
) -> Result<Response<<Self as FlightService>::DoGetStream>, Status> {
+ debug!("do_get_schemas");
Err(Status::unimplemented("Implement do_get_schemas"))
}
async fn do_get_tables(
@@ -453,6 +626,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetTables,
_request: Request<Ticket>,
) -> Result<Response<<Self as FlightService>::DoGetStream>, Status> {
+ debug!("do_get_tables");
Err(Status::unimplemented("Implement do_get_tables"))
}
async fn do_get_table_types(
@@ -460,6 +634,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetTableTypes,
_request: Request<Ticket>,
) -> Result<Response<<Self as FlightService>::DoGetStream>, Status> {
+ debug!("do_get_table_types");
Err(Status::unimplemented("Implement do_get_table_types"))
}
async fn do_get_sql_info(
@@ -467,6 +642,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetSqlInfo,
_request: Request<Ticket>,
) -> Result<Response<<Self as FlightService>::DoGetStream>, Status> {
+ debug!("do_get_sql_info");
Err(Status::unimplemented("Implement do_get_sql_info"))
}
async fn do_get_primary_keys(
@@ -474,6 +650,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetPrimaryKeys,
_request: Request<Ticket>,
) -> Result<Response<<Self as FlightService>::DoGetStream>, Status> {
+ debug!("do_get_primary_keys");
Err(Status::unimplemented("Implement do_get_primary_keys"))
}
async fn do_get_exported_keys(
@@ -481,6 +658,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetExportedKeys,
_request: Request<Ticket>,
) -> Result<Response<<Self as FlightService>::DoGetStream>, Status> {
+ debug!("do_get_exported_keys");
Err(Status::unimplemented("Implement do_get_exported_keys"))
}
async fn do_get_imported_keys(
@@ -488,6 +666,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetImportedKeys,
_request: Request<Ticket>,
) -> Result<Response<<Self as FlightService>::DoGetStream>, Status> {
+ debug!("do_get_imported_keys");
Err(Status::unimplemented("Implement do_get_imported_keys"))
}
async fn do_get_cross_reference(
@@ -495,6 +674,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandGetCrossReference,
_request: Request<Ticket>,
) -> Result<Response<<Self as FlightService>::DoGetStream>, Status> {
+ debug!("do_get_cross_reference");
Err(Status::unimplemented("Implement do_get_cross_reference"))
}
// do_put
@@ -503,6 +683,7 @@ impl FlightSqlService for FlightSqlServiceImpl {
_ticket: CommandStatementUpdate,
_request: Request<Streaming<FlightData>>,
) -> Result<i64, Status> {
+ debug!("do_put_statement_update");
Err(Status::unimplemented("Implement do_put_statement_update"))
}
async fn do_put_prepared_statement_query(
@@ -510,29 +691,37 @@ impl FlightSqlService for FlightSqlServiceImpl {
_query: CommandPreparedStatementQuery,
_request: Request<Streaming<FlightData>>,
) -> Result<Response<<Self as FlightService>::DoPutStream>, Status> {
+ debug!("do_put_prepared_statement_query");
Err(Status::unimplemented(
"Implement do_put_prepared_statement_query",
))
}
async fn do_put_prepared_statement_update(
&self,
- _handle: CommandPreparedStatementUpdate,
- _request: Request<Streaming<FlightData>>,
+ handle: CommandPreparedStatementUpdate,
+ request: Request<Streaming<FlightData>>,
) -> Result<i64, Status> {
- Err(Status::unimplemented(
- "Implement do_put_prepared_statement_update",
- ))
+ debug!("do_put_prepared_statement_update");
+ let ctx = self.get_ctx(&request)?;
+ let handle =
Uuid::from_slice(handle.prepared_statement_handle.as_slice())
+ .map_err(|e| Status::internal(format!("Error decoding handle: {}",
e)))?;
+ let plan = self.get_plan(&handle)?;
+ let _ = self.execute_plan(ctx, &plan).await?;
+ debug!("Sending -1 rows affected");
+ Ok(-1)
}
async fn do_action_create_prepared_statement(
&self,
query: ActionCreatePreparedStatementRequest,
- _request: Request<Action>,
+ request: Request<Action>,
) -> Result<ActionCreatePreparedStatementResult, Status> {
- let ctx = self.create_ctx().await?;
+ debug!("do_action_create_prepared_statement");
+ let ctx = self.get_ctx(&request)?;
let plan = Self::prepare_statement(&query.query, &ctx).await?;
let schema_bytes = self.df_schema_to_arrow(plan.schema())?;
let handle = self.cache_plan(plan)?;
+ debug!("Prepared statement {}:\n{}", handle, query.query);
let res = ActionCreatePreparedStatementResult {
prepared_statement_handle: handle.as_bytes().to_vec(),
dataset_schema: schema_bytes,
@@ -546,8 +735,10 @@ impl FlightSqlService for FlightSqlServiceImpl {
handle: ActionClosePreparedStatementRequest,
_request: Request<Action>,
) {
+ debug!("do_action_close_prepared_statement");
let handle =
Uuid::from_slice(handle.prepared_statement_handle.as_slice());
let handle = if let Ok(handle) = handle {
+ debug!("Closing {}", handle);
handle
} else {
return;