use flaredb_client::RdbClient; use flaredb_proto::sqlrpc::sql_service_server::SqlService as SqlServiceTrait; use flaredb_proto::sqlrpc::{ DdlResult, DmlResult, QueryResult as ProtoQueryResult, Row, SqlRequest, SqlResponse, SqlValue as ProtoValue, }; use flaredb_sql::executor::{ExecutionResult, SqlExecutor}; use flaredb_sql::types::Value; use std::sync::Arc; use tokio::sync::Mutex; use tonic::{Request, Response, Status}; pub struct SqlServiceImpl { /// Address of the local FlareDB server server_addr: String, /// ChainFire/PD endpoints used for region-aware routing pd_endpoints: Vec, } impl SqlServiceImpl { pub fn new(server_addr: String, pd_endpoints: Vec) -> Self { Self { server_addr, pd_endpoints, } } fn value_to_proto(value: &Value) -> ProtoValue { match value { Value::Null => ProtoValue { value: None, is_null: true, }, Value::Integer(i) => ProtoValue { value: Some(flaredb_proto::sqlrpc::sql_value::Value::IntValue(*i)), is_null: false, }, Value::Text(s) => ProtoValue { value: Some(flaredb_proto::sqlrpc::sql_value::Value::TextValue( s.clone(), )), is_null: false, }, Value::Boolean(b) => ProtoValue { value: Some(flaredb_proto::sqlrpc::sql_value::Value::BoolValue(*b)), is_null: false, }, Value::Timestamp(ts) => ProtoValue { value: Some(flaredb_proto::sqlrpc::sql_value::Value::TimestampValue(*ts)), is_null: false, }, } } } #[tonic::async_trait] impl SqlServiceTrait for SqlServiceImpl { async fn execute(&self, request: Request) -> Result, Status> { let req = request.into_inner(); // Use PD-backed routing so SQL works for both strong and eventual namespaces. let client = RdbClient::connect_with_pd_namespace( self.server_addr.clone(), self.pd_endpoints.join(","), req.namespace.clone(), ) .await .map_err(|e| Status::internal(format!("Failed to connect to FlareDB: {}", e)))?; // Create executor let executor = SqlExecutor::new(Arc::new(Mutex::new(client))); // Execute SQL let result = executor .execute(&req.sql) .await .map_err(|e| Status::internal(format!("SQL execution error: {}", e)))?; // Convert result to protobuf response let response = match result { ExecutionResult::DdlSuccess(message) => SqlResponse { result: Some(flaredb_proto::sqlrpc::sql_response::Result::DdlResult( DdlResult { message }, )), }, ExecutionResult::DmlSuccess(rows_affected) => SqlResponse { result: Some(flaredb_proto::sqlrpc::sql_response::Result::DmlResult( DmlResult { rows_affected }, )), }, ExecutionResult::Query(query_result) => { let rows: Vec = query_result .rows .iter() .map(|row_values| Row { values: row_values.iter().map(Self::value_to_proto).collect(), }) .collect(); SqlResponse { result: Some(flaredb_proto::sqlrpc::sql_response::Result::QueryResult( ProtoQueryResult { columns: query_result.columns.to_vec(), rows, }, )), } } // Errors are returned via Result::Err and handled by .map_err() above }; Ok(Response::new(response)) } }