From e3f986ee9d7bb2a94855f51e3190c2452ea27ef4 Mon Sep 17 00:00:00 2001 From: Ruihang Xia Date: Wed, 11 Dec 2024 20:47:32 +0800 Subject: [PATCH] feat: implement `v1/sql/parse` endpoint to parse GreptimeDB's SQL dialect (#5144) * derive ser/de Signed-off-by: Ruihang Xia * impl method Signed-off-by: Ruihang Xia * fix typo Signed-off-by: Ruihang Xia --------- Signed-off-by: Ruihang Xia --- Cargo.lock | 2 ++ Cargo.toml | 1 + src/servers/src/error.rs | 10 ++++++++- src/servers/src/http.rs | 4 ++++ src/servers/src/http/handler.rs | 28 +++++++++++++++++++++++- src/sql/Cargo.toml | 1 + src/sql/src/statements/admin.rs | 3 ++- src/sql/src/statements/alter.rs | 11 +++++----- src/sql/src/statements/copy.rs | 11 +++++----- src/sql/src/statements/create.rs | 21 +++++++++--------- src/sql/src/statements/cursor.rs | 7 +++--- src/sql/src/statements/delete.rs | 3 ++- src/sql/src/statements/describe.rs | 3 ++- src/sql/src/statements/drop.rs | 9 ++++---- src/sql/src/statements/explain.rs | 3 ++- src/sql/src/statements/insert.rs | 3 ++- src/sql/src/statements/option_map.rs | 4 +++- src/sql/src/statements/query.rs | 3 ++- src/sql/src/statements/set_variables.rs | 3 ++- src/sql/src/statements/show.rs | 29 +++++++++++++------------ src/sql/src/statements/statement.rs | 7 ++++-- src/sql/src/statements/tql.rs | 9 ++++---- src/sql/src/statements/truncate.rs | 3 ++- tests-integration/tests/http.rs | 13 +++++++++++ 24 files changed, 133 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 628c6a582418..311caafcb2fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11295,6 +11295,7 @@ dependencies = [ "jsonb", "lazy_static", "regex", + "serde", "serde_json", "snafu 0.8.5", "sqlparser 0.45.0 (git+https://github.com/GreptimeTeam/sqlparser-rs.git?rev=54a267ac89c09b11c0c88934690530807185d3e7)", @@ -11371,6 +11372,7 @@ dependencies = [ "lazy_static", "log", "regex", + "serde", "sqlparser 0.45.0 (registry+https://github.com/rust-lang/crates.io-index)", "sqlparser_derive 0.2.2 (git+https://github.com/GreptimeTeam/sqlparser-rs.git?rev=54a267ac89c09b11c0c88934690530807185d3e7)", ] diff --git a/Cargo.toml b/Cargo.toml index d1d360850e70..990bc71a907b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -180,6 +180,7 @@ sysinfo = "0.30" # on branch v0.44.x sqlparser = { git = "https://github.com/GreptimeTeam/sqlparser-rs.git", rev = "54a267ac89c09b11c0c88934690530807185d3e7", features = [ "visitor", + "serde", ] } strum = { version = "0.25", features = ["derive"] } tempfile = "3" diff --git a/src/servers/src/error.rs b/src/servers/src/error.rs index 6682a1c78967..071de93683cc 100644 --- a/src/servers/src/error.rs +++ b/src/servers/src/error.rs @@ -189,6 +189,13 @@ pub enum Error { location: Location, }, + #[snafu(display("Failed to parse query"))] + FailedToParseQuery { + #[snafu(implicit)] + location: Location, + source: sql::error::Error, + }, + #[snafu(display("Failed to parse InfluxDB line protocol"))] InfluxdbLineProtocol { #[snafu(implicit)] @@ -651,7 +658,8 @@ impl ErrorExt for Error { | OpenTelemetryLog { .. } | UnsupportedJsonDataTypeForTag { .. } | InvalidTableName { .. } - | PrepareStatementNotFound { .. } => StatusCode::InvalidArguments, + | PrepareStatementNotFound { .. } + | FailedToParseQuery { .. } => StatusCode::InvalidArguments, Catalog { source, .. } => source.status_code(), RowWriter { source, .. } => source.status_code(), diff --git a/src/servers/src/http.rs b/src/servers/src/http.rs index d8d07ed31fa0..1107870c9a25 100644 --- a/src/servers/src/http.rs +++ b/src/servers/src/http.rs @@ -755,6 +755,10 @@ impl HttpServer { fn route_sql(api_state: ApiState) -> Router { Router::new() .route("/sql", routing::get(handler::sql).post(handler::sql)) + .route( + "/sql/parse", + routing::get(handler::sql_parse).post(handler::sql_parse), + ) .route( "/promql", routing::get(handler::promql).post(handler::promql), diff --git a/src/servers/src/http/handler.rs b/src/servers/src/http/handler.rs index 15a1a0e16c73..153b824d6ef1 100644 --- a/src/servers/src/http/handler.rs +++ b/src/servers/src/http/handler.rs @@ -30,8 +30,13 @@ use query::parser::{PromQuery, DEFAULT_LOOKBACK_STRING}; use serde::{Deserialize, Serialize}; use serde_json::Value; use session::context::{Channel, QueryContext, QueryContextRef}; +use snafu::ResultExt; +use sql::dialect::GreptimeDbDialect; +use sql::parser::{ParseOptions, ParserContext}; +use sql::statements::statement::Statement; use super::header::collect_plan_metrics; +use crate::error::{FailedToParseQuerySnafu, InvalidQuerySnafu, Result}; use crate::http::result::arrow_result::ArrowResponse; use crate::http::result::csv_result::CsvResponse; use crate::http::result::error_result::ErrorResponse; @@ -146,10 +151,31 @@ pub async fn sql( resp.with_execution_time(start.elapsed().as_millis() as u64) } +/// Handler to parse sql +#[axum_macros::debug_handler] +#[tracing::instrument(skip_all, fields(protocol = "http", request_type = "sql"))] +pub async fn sql_parse( + Query(query_params): Query, + Form(form_params): Form, +) -> Result>> { + let Some(sql) = query_params.sql.or(form_params.sql) else { + return InvalidQuerySnafu { + reason: "sql parameter is required.", + } + .fail(); + }; + + let stmts = + ParserContext::create_with_dialect(&sql, &GreptimeDbDialect {}, ParseOptions::default()) + .context(FailedToParseQuerySnafu)?; + + Ok(stmts.into()) +} + /// Create a response from query result pub async fn from_output( outputs: Vec>, -) -> Result<(Vec, HashMap), ErrorResponse> { +) -> std::result::Result<(Vec, HashMap), ErrorResponse> { // TODO(sunng87): this api response structure cannot represent error well. // It hides successful execution results from error response let mut results = Vec::with_capacity(outputs.len()); diff --git a/src/sql/Cargo.toml b/src/sql/Cargo.toml index e3340a8f6c90..3cb81d6dd494 100644 --- a/src/sql/Cargo.toml +++ b/src/sql/Cargo.toml @@ -30,6 +30,7 @@ itertools.workspace = true jsonb.workspace = true lazy_static.workspace = true regex.workspace = true +serde.workspace = true serde_json.workspace = true snafu.workspace = true sqlparser.workspace = true diff --git a/src/sql/src/statements/admin.rs b/src/sql/src/statements/admin.rs index bbe805a4c163..d68ea829d190 100644 --- a/src/sql/src/statements/admin.rs +++ b/src/sql/src/statements/admin.rs @@ -14,12 +14,13 @@ use std::fmt::Display; +use serde::{Deserialize, Serialize}; use sqlparser_derive::{Visit, VisitMut}; use crate::ast::Function; /// `ADMIN` statement to execute some administration commands. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub enum Admin { /// Run a admin function. Func(Function), diff --git a/src/sql/src/statements/alter.rs b/src/sql/src/statements/alter.rs index cf59257e8931..d9d28d26fd38 100644 --- a/src/sql/src/statements/alter.rs +++ b/src/sql/src/statements/alter.rs @@ -18,10 +18,11 @@ use api::v1; use common_query::AddColumnLocation; use datatypes::schema::FulltextOptions; use itertools::Itertools; +use serde::{Deserialize, Serialize}; use sqlparser::ast::{ColumnDef, DataType, Ident, ObjectName, TableConstraint}; use sqlparser_derive::{Visit, VisitMut}; -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct AlterTable { pub table_name: ObjectName, pub alter_operation: AlterTableOperation, @@ -56,7 +57,7 @@ impl Display for AlterTable { } } -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub enum AlterTableOperation { /// `ADD ` AddConstraint(TableConstraint), @@ -151,7 +152,7 @@ impl Display for AlterTableOperation { } } -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct KeyValueOption { pub key: String, pub value: String, @@ -166,7 +167,7 @@ impl From for v1::Option { } } -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct AlterDatabase { pub database_name: ObjectName, pub alter_operation: AlterDatabaseOperation, @@ -197,7 +198,7 @@ impl Display for AlterDatabase { } } -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub enum AlterDatabaseOperation { SetDatabaseOption { options: Vec }, UnsetDatabaseOption { keys: Vec }, diff --git a/src/sql/src/statements/copy.rs b/src/sql/src/statements/copy.rs index c68b9d8c0321..626cce789740 100644 --- a/src/sql/src/statements/copy.rs +++ b/src/sql/src/statements/copy.rs @@ -14,12 +14,13 @@ use std::fmt::Display; +use serde::{Deserialize, Serialize}; use sqlparser::ast::ObjectName; use sqlparser_derive::{Visit, VisitMut}; use crate::statements::OptionMap; -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub enum Copy { CopyTable(CopyTable), CopyDatabase(CopyDatabase), @@ -34,7 +35,7 @@ impl Display for Copy { } } -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub enum CopyTable { To(CopyTableArgument), From(CopyTableArgument), @@ -65,7 +66,7 @@ impl Display for CopyTable { } } -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub enum CopyDatabase { To(CopyDatabaseArgument), From(CopyDatabaseArgument), @@ -96,7 +97,7 @@ impl Display for CopyDatabase { } } -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct CopyDatabaseArgument { pub database_name: ObjectName, pub with: OptionMap, @@ -104,7 +105,7 @@ pub struct CopyDatabaseArgument { pub location: String, } -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct CopyTableArgument { pub table_name: ObjectName, pub with: OptionMap, diff --git a/src/sql/src/statements/create.rs b/src/sql/src/statements/create.rs index 20ed7b555965..74528fe34dee 100644 --- a/src/sql/src/statements/create.rs +++ b/src/sql/src/statements/create.rs @@ -18,6 +18,7 @@ use std::fmt::{Display, Formatter}; use common_catalog::consts::FILE_ENGINE; use datatypes::schema::FulltextOptions; use itertools::Itertools; +use serde::{Deserialize, Serialize}; use snafu::ResultExt; use sqlparser::ast::{ColumnOptionDef, DataType, Expr, Query}; use sqlparser_derive::{Visit, VisitMut}; @@ -58,7 +59,7 @@ fn format_table_constraint(constraints: &[TableConstraint]) -> String { } /// Table constraint for create table statement. -#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut)] +#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize, Deserialize)] pub enum TableConstraint { /// Primary key constraint. PrimaryKey { columns: Vec }, @@ -84,7 +85,7 @@ impl Display for TableConstraint { } } -#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut)] +#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize, Deserialize)] pub struct CreateTable { /// Create if not exists pub if_not_exists: bool, @@ -100,7 +101,7 @@ pub struct CreateTable { } /// Column definition in `CREATE TABLE` statement. -#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut)] +#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize, Deserialize)] pub struct Column { /// `ColumnDef` from `sqlparser::ast` pub column_def: ColumnDef, @@ -109,7 +110,7 @@ pub struct Column { } /// Column extensions for greptimedb dialect. -#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Default)] +#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Default, Serialize, Deserialize)] pub struct ColumnExtensions { /// Fulltext options. pub fulltext_options: Option, @@ -172,7 +173,7 @@ impl ColumnExtensions { } } -#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut)] +#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize, Deserialize)] pub struct Partitions { pub column_list: Vec, pub exprs: Vec, @@ -244,7 +245,7 @@ impl Display for CreateTable { } } -#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut)] +#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize, Deserialize)] pub struct CreateDatabase { pub name: ObjectName, /// Create if not exists @@ -278,7 +279,7 @@ impl Display for CreateDatabase { } } -#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut)] +#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize, Deserialize)] pub struct CreateExternalTable { /// Table name pub name: ObjectName, @@ -309,7 +310,7 @@ impl Display for CreateExternalTable { } } -#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut)] +#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize, Deserialize)] pub struct CreateTableLike { /// Table name pub table_name: ObjectName, @@ -325,7 +326,7 @@ impl Display for CreateTableLike { } } -#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut)] +#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize, Deserialize)] pub struct CreateFlow { /// Flow name pub flow_name: ObjectName, @@ -367,7 +368,7 @@ impl Display for CreateFlow { } /// Create SQL view statement. -#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut)] +#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize, Deserialize)] pub struct CreateView { /// View name pub name: ObjectName, diff --git a/src/sql/src/statements/cursor.rs b/src/sql/src/statements/cursor.rs index 72ef4cdcae98..e92ebdca2173 100644 --- a/src/sql/src/statements/cursor.rs +++ b/src/sql/src/statements/cursor.rs @@ -14,6 +14,7 @@ use std::fmt::Display; +use serde::{Deserialize, Serialize}; use sqlparser::ast::ObjectName; use sqlparser_derive::{Visit, VisitMut}; @@ -22,7 +23,7 @@ use super::query::Query; /// Represents a DECLARE CURSOR statement /// /// This statement will carry a SQL query -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct DeclareCursor { pub cursor_name: ObjectName, pub query: Box, @@ -35,7 +36,7 @@ impl Display for DeclareCursor { } /// Represents a FETCH FROM cursor statement -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct FetchCursor { pub cursor_name: ObjectName, pub fetch_size: u64, @@ -48,7 +49,7 @@ impl Display for FetchCursor { } /// Represents a CLOSE cursor statement -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct CloseCursor { pub cursor_name: ObjectName, } diff --git a/src/sql/src/statements/delete.rs b/src/sql/src/statements/delete.rs index 4346610b7d19..18a804925cd0 100644 --- a/src/sql/src/statements/delete.rs +++ b/src/sql/src/statements/delete.rs @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +use serde::{Deserialize, Serialize}; use sqlparser::ast::Statement; use sqlparser_derive::{Visit, VisitMut}; -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct Delete { pub inner: Statement, } diff --git a/src/sql/src/statements/describe.rs b/src/sql/src/statements/describe.rs index 743f2b0123c2..25cf9b2fdbca 100644 --- a/src/sql/src/statements/describe.rs +++ b/src/sql/src/statements/describe.rs @@ -14,11 +14,12 @@ use std::fmt::Display; +use serde::{Deserialize, Serialize}; use sqlparser::ast::ObjectName; use sqlparser_derive::{Visit, VisitMut}; /// SQL structure for `DESCRIBE TABLE`. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct DescribeTable { name: ObjectName, } diff --git a/src/sql/src/statements/drop.rs b/src/sql/src/statements/drop.rs index a46450db78f7..2ad204edcf86 100644 --- a/src/sql/src/statements/drop.rs +++ b/src/sql/src/statements/drop.rs @@ -14,11 +14,12 @@ use std::fmt::Display; +use serde::{Deserialize, Serialize}; use sqlparser::ast::ObjectName; use sqlparser_derive::{Visit, VisitMut}; /// DROP TABLE statement. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct DropTable { table_names: Vec, @@ -62,7 +63,7 @@ impl Display for DropTable { } /// DROP DATABASE statement. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct DropDatabase { name: ObjectName, /// drop table if exists @@ -99,7 +100,7 @@ impl Display for DropDatabase { } /// DROP FLOW statement. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct DropFlow { flow_name: ObjectName, /// drop flow if exists @@ -138,7 +139,7 @@ impl Display for DropFlow { } /// `DROP VIEW` statement. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct DropView { // The view name pub view_name: ObjectName, diff --git a/src/sql/src/statements/explain.rs b/src/sql/src/statements/explain.rs index 5b3a2671f939..72afa4e8f22f 100644 --- a/src/sql/src/statements/explain.rs +++ b/src/sql/src/statements/explain.rs @@ -14,13 +14,14 @@ use std::fmt::{Display, Formatter}; +use serde::{Deserialize, Serialize}; use sqlparser::ast::Statement as SpStatement; use sqlparser_derive::{Visit, VisitMut}; use crate::error::Error; /// Explain statement. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct Explain { pub inner: SpStatement, } diff --git a/src/sql/src/statements/insert.rs b/src/sql/src/statements/insert.rs index 4eae7f1e1874..192e0169aa6a 100644 --- a/src/sql/src/statements/insert.rs +++ b/src/sql/src/statements/insert.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use serde::{Deserialize, Serialize}; use sqlparser::ast::{ObjectName, Query, SetExpr, Statement, UnaryOperator, Values}; use sqlparser::parser::ParserError; use sqlparser_derive::{Visit, VisitMut}; @@ -20,7 +21,7 @@ use crate::ast::{Expr, Value}; use crate::error::Result; use crate::statements::query::Query as GtQuery; -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct Insert { // Can only be sqlparser::ast::Statement::Insert variant pub inner: Statement, diff --git a/src/sql/src/statements/option_map.rs b/src/sql/src/statements/option_map.rs index 9ff8d94312fd..645fa16eee19 100644 --- a/src/sql/src/statements/option_map.rs +++ b/src/sql/src/statements/option_map.rs @@ -16,14 +16,16 @@ use std::collections::{BTreeMap, HashMap}; use std::ops::ControlFlow; use common_base::secrets::{ExposeSecret, ExposeSecretMut, SecretString}; +use serde::{Deserialize, Serialize}; use sqlparser::ast::{Visit, VisitMut, Visitor, VisitorMut}; const REDACTED_OPTIONS: [&str; 2] = ["access_key_id", "secret_access_key"]; /// Options hashmap. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct OptionMap { options: BTreeMap, + #[serde(skip_serializing)] secrets: BTreeMap, } diff --git a/src/sql/src/statements/query.rs b/src/sql/src/statements/query.rs index 3b571a1a0ba1..5b270cb7bf15 100644 --- a/src/sql/src/statements/query.rs +++ b/src/sql/src/statements/query.rs @@ -14,13 +14,14 @@ use std::fmt; +use serde::{Deserialize, Serialize}; use sqlparser::ast::Query as SpQuery; use sqlparser_derive::{Visit, VisitMut}; use crate::error::Error; /// Query statement instance. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct Query { pub inner: SpQuery, } diff --git a/src/sql/src/statements/set_variables.rs b/src/sql/src/statements/set_variables.rs index 7a2a94a531df..78609bb40ff7 100644 --- a/src/sql/src/statements/set_variables.rs +++ b/src/sql/src/statements/set_variables.rs @@ -14,11 +14,12 @@ use std::fmt::Display; +use serde::{Deserialize, Serialize}; use sqlparser::ast::{Expr, ObjectName}; use sqlparser_derive::{Visit, VisitMut}; /// SET variables statement. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct SetVariables { pub variable: ObjectName, pub value: Vec, diff --git a/src/sql/src/statements/show.rs b/src/sql/src/statements/show.rs index f6a8dab72897..1dbb34e242e4 100644 --- a/src/sql/src/statements/show.rs +++ b/src/sql/src/statements/show.rs @@ -14,12 +14,13 @@ use std::fmt::{self, Display}; +use serde::{Deserialize, Serialize}; use sqlparser_derive::{Visit, VisitMut}; use crate::ast::{Expr, Ident, ObjectName}; /// Show kind for SQL expressions like `SHOW DATABASE` or `SHOW TABLE` -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub enum ShowKind { All, Like(Ident), @@ -46,14 +47,14 @@ macro_rules! format_kind { } /// SQL structure for `SHOW DATABASES`. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct ShowDatabases { pub kind: ShowKind, pub full: bool, } /// The SQL `SHOW COLUMNS` statement -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct ShowColumns { pub kind: ShowKind, pub table: String, @@ -77,7 +78,7 @@ impl Display for ShowColumns { } /// The SQL `SHOW INDEX` statement -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct ShowIndex { pub kind: ShowKind, pub table: String, @@ -118,7 +119,7 @@ impl Display for ShowDatabases { } /// SQL structure for `SHOW TABLES`. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct ShowTables { pub kind: ShowKind, pub database: Option, @@ -142,7 +143,7 @@ impl Display for ShowTables { } /// SQL structure for `SHOW TABLE STATUS`. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct ShowTableStatus { pub kind: ShowKind, pub database: Option, @@ -162,7 +163,7 @@ impl Display for ShowTableStatus { } /// SQL structure for `SHOW CREATE DATABASE`. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct ShowCreateDatabase { pub database_name: ObjectName, } @@ -175,7 +176,7 @@ impl Display for ShowCreateDatabase { } /// SQL structure for `SHOW CREATE TABLE`. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct ShowCreateTable { pub table_name: ObjectName, } @@ -188,7 +189,7 @@ impl Display for ShowCreateTable { } /// SQL structure for `SHOW CREATE FLOW`. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct ShowCreateFlow { pub flow_name: ObjectName, } @@ -201,7 +202,7 @@ impl Display for ShowCreateFlow { } /// SQL structure for `SHOW FLOWS`. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct ShowFlows { pub kind: ShowKind, pub database: Option, @@ -220,7 +221,7 @@ impl Display for ShowFlows { } /// SQL structure for `SHOW CREATE VIEW`. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct ShowCreateView { pub view_name: ObjectName, } @@ -233,7 +234,7 @@ impl Display for ShowCreateView { } /// SQL structure for `SHOW VIEWS`. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct ShowViews { pub kind: ShowKind, pub database: Option, @@ -252,7 +253,7 @@ impl Display for ShowViews { } /// SQL structure for `SHOW VARIABLES xxx`. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct ShowVariables { pub variable: ObjectName, } @@ -265,7 +266,7 @@ impl Display for ShowVariables { } /// SQL structure for "SHOW STATUS" -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct ShowStatus {} impl Display for ShowStatus { diff --git a/src/sql/src/statements/statement.rs b/src/sql/src/statements/statement.rs index 8ad391a00dd2..18649f6cf303 100644 --- a/src/sql/src/statements/statement.rs +++ b/src/sql/src/statements/statement.rs @@ -15,12 +15,14 @@ use std::fmt::Display; use datafusion_sql::parser::Statement as DfStatement; +use serde::{Deserialize, Serialize}; use sqlparser::ast::Statement as SpStatement; use sqlparser_derive::{Visit, VisitMut}; use crate::error::{ConvertToDfStatementSnafu, Error}; use crate::statements::admin::Admin; use crate::statements::alter::{AlterDatabase, AlterTable}; +use crate::statements::copy::Copy; use crate::statements::create::{ CreateDatabase, CreateExternalTable, CreateFlow, CreateTable, CreateTableLike, CreateView, }; @@ -42,7 +44,7 @@ use crate::statements::truncate::TruncateTable; /// Tokens parsed by `DFParser` are converted into these values. #[allow(clippy::large_enum_variant)] -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub enum Statement { // Query Query(Box), @@ -107,7 +109,8 @@ pub enum Statement { // EXPLAIN QUERY Explain(Explain), // COPY - Copy(crate::statements::copy::Copy), + Copy(Copy), + // Telemetry Query Language Tql(Tql), // TRUNCATE TABLE TruncateTable(TruncateTable), diff --git a/src/sql/src/statements/tql.rs b/src/sql/src/statements/tql.rs index 0f7a85f95ab8..1b7712da551f 100644 --- a/src/sql/src/statements/tql.rs +++ b/src/sql/src/statements/tql.rs @@ -14,9 +14,10 @@ use std::fmt::Display; +use serde::{Deserialize, Serialize}; use sqlparser_derive::{Visit, VisitMut}; -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub enum Tql { Eval(TqlEval), Explain(TqlExplain), @@ -49,7 +50,7 @@ fn format_tql( } /// TQL EVAL (, , , [lookback]) -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct TqlEval { pub start: String, pub end: String, @@ -74,7 +75,7 @@ impl Display for TqlEval { /// TQL EXPLAIN [VERBOSE] [, , , [lookback]] /// doesn't execute the query but tells how the query would be executed (similar to SQL EXPLAIN). -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct TqlExplain { pub start: String, pub end: String, @@ -103,7 +104,7 @@ impl Display for TqlExplain { /// TQL ANALYZE [VERBOSE] (, , , [lookback]) /// executes the plan and tells the detailed per-step execution time (similar to SQL ANALYZE). -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct TqlAnalyze { pub start: String, pub end: String, diff --git a/src/sql/src/statements/truncate.rs b/src/sql/src/statements/truncate.rs index c1a063f959ce..6a8cfdfc24ad 100644 --- a/src/sql/src/statements/truncate.rs +++ b/src/sql/src/statements/truncate.rs @@ -14,11 +14,12 @@ use std::fmt::Display; +use serde::{Deserialize, Serialize}; use sqlparser::ast::ObjectName; use sqlparser_derive::{Visit, VisitMut}; /// TRUNCATE TABLE statement. -#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize, Deserialize)] pub struct TruncateTable { table_name: ObjectName, } diff --git a/tests-integration/tests/http.rs b/tests-integration/tests/http.rs index 4da65f0b21f5..3a3ed5d5be52 100644 --- a/tests-integration/tests/http.rs +++ b/tests-integration/tests/http.rs @@ -40,6 +40,7 @@ use servers::http::result::influxdb_result_v1::{InfluxdbOutput, InfluxdbV1Respon use servers::http::test_helpers::{TestClient, TestResponse}; use servers::http::GreptimeQueryOutput; use servers::prom_store; +use sql::statements::statement::Statement; use tests_integration::test_util::{ setup_test_http_app, setup_test_http_app_with_frontend, setup_test_http_app_with_frontend_and_user_provider, setup_test_prom_app_with_frontend, @@ -361,6 +362,18 @@ pub async fn test_sql_api(store_type: StorageType) { let body = serde_json::from_str::(&res.text().await).unwrap(); assert_eq!(body.code(), ErrorCode::DatabaseNotFound as u32); + // test parse method + let res = client.get("/v1/sql/parse?sql=desc table t").send().await; + assert_eq!(res.status(), StatusCode::OK); + let body = serde_json::from_str::>(&res.text().await).unwrap(); + assert_eq!( + body, + serde_json::from_value::>(json!( + [{"DescribeTable":{"name":[{"value":"t","quote_style":null}]}}] + )) + .unwrap() + ); + // test timezone header let res = client .get("/v1/sql?&sql=show variables system_time_zone")