From 5e466722cba7fe53787e445674164f1fad19bf9a Mon Sep 17 00:00:00 2001 From: Yijun Zhao Date: Mon, 27 Nov 2023 21:41:20 +0800 Subject: [PATCH 1/8] impl qualify clause --- src/query/ast/src/ast/format/ast_format.rs | 10 + src/query/ast/src/ast/query.rs | 2 + src/query/ast/src/parser/query.rs | 6 + src/query/ast/src/parser/token.rs | 4 + src/query/ast/src/visitors/visitor.rs | 12 + src/query/ast/src/visitors/visitor_mut.rs | 13 + src/query/ast/src/visitors/walk_mut.rs | 29 ++ .../sql/src/planner/binder/bind_context.rs | 1 + src/query/sql/src/planner/binder/mod.rs | 1 + src/query/sql/src/planner/binder/qualify.rs | 250 ++++++++++++++++++ src/query/sql/src/planner/binder/select.rs | 15 ++ src/query/sql/src/planner/binder/table.rs | 1 + src/query/sql/src/planner/binder/window.rs | 47 +++- .../semantic/aggregating_index_visitor.rs | 2 +- .../planner/semantic/distinct_to_groupby.rs | 3 + src/tests/sqlsmith/src/sql_gen/query.rs | 2 + 16 files changed, 395 insertions(+), 3 deletions(-) create mode 100644 src/query/sql/src/planner/binder/qualify.rs diff --git a/src/query/ast/src/ast/format/ast_format.rs b/src/query/ast/src/ast/format/ast_format.rs index 7ff26add24c9c..7f4e16c8c7b3c 100644 --- a/src/query/ast/src/ast/format/ast_format.rs +++ b/src/query/ast/src/ast/format/ast_format.rs @@ -2777,6 +2777,16 @@ impl<'ast> Visitor<'ast> for AstFormatVisitor { children.push(window_list_node); } + if let Some(qualify) = &stmt.qualify { + self.visit_expr(qualify); + let qualify_child = self.children.pop().unwrap(); + let qualify_name = "Qualify".to_string(); + let qualify_format_ctx = AstFormatContext::with_children(qualify_name, 1); + let qualify_node = + FormatTreeNode::with_children(qualify_format_ctx, vec![qualify_child]); + children.push(qualify_node); + } + let name = "SelectQuery".to_string(); let format_ctx = AstFormatContext::with_children(name, children.len()); let node = FormatTreeNode::with_children(format_ctx, children); diff --git a/src/query/ast/src/ast/query.rs b/src/query/ast/src/ast/query.rs index 8edd507dc13dd..cc029d61d4872 100644 --- a/src/query/ast/src/ast/query.rs +++ b/src/query/ast/src/ast/query.rs @@ -92,6 +92,8 @@ pub struct SelectStmt { pub having: Option, // `WINDOW` clause pub window_list: Option>, + // `QUALIFY` clause + pub qualify: Option, } /// Group by Clause. diff --git a/src/query/ast/src/parser/query.rs b/src/query/ast/src/parser/query.rs index 67de78a59d91c..4c6ef9a2261ca 100644 --- a/src/query/ast/src/parser/query.rs +++ b/src/query/ast/src/parser/query.rs @@ -58,6 +58,7 @@ pub enum SetOperationElement { group_by: Option, having: Box>, window_list: Option>, + qualify: Box>, }, SetOperation { op: SetOperator, @@ -104,6 +105,7 @@ pub fn set_operation_element(i: Input) -> IResult> ~ ( GROUP ~ ^BY ~ ^#group_by_items )? ~ ( HAVING ~ ^#expr )? ~ ( WINDOW ~ ^#comma_separated_list1(window_clause) )? + ~ ( QUALIFY ~ ^#expr )? }, |( _select, @@ -115,6 +117,7 @@ pub fn set_operation_element(i: Input) -> IResult> opt_group_by_block, opt_having_block, opt_window_block, + opt_qualify_block, )| { SetOperationElement::SelectStmt { hints: opt_hints, @@ -129,6 +132,7 @@ pub fn set_operation_element(i: Input) -> IResult> group_by: opt_group_by_block.map(|(_, _, group_by)| group_by), having: Box::new(opt_having_block.map(|(_, having)| having)), window_list: opt_window_block.map(|(_, windows)| windows), + qualify: Box::new(opt_qualify_block.map(|(_, qualify)| qualify)), } }, ); @@ -262,6 +266,7 @@ impl<'a, I: Iterator>> PrattParser group_by, having, window_list, + qualify, } => SetExpr::Select(Box::new(SelectStmt { span: transform_span(input.span.0), hints, @@ -272,6 +277,7 @@ impl<'a, I: Iterator>> PrattParser group_by, having: *having, window_list, + qualify: *qualify, })), SetOperationElement::Values(values) => SetExpr::Values { span: transform_span(input.span.0), diff --git a/src/query/ast/src/parser/token.rs b/src/query/ast/src/parser/token.rs index 4e694f114621b..3cffd758522a4 100644 --- a/src/query/ast/src/parser/token.rs +++ b/src/query/ast/src/parser/token.rs @@ -807,6 +807,8 @@ pub enum TokenKind { PRESIGN, #[token("PRIVILEGES", ignore(ascii_case))] PRIVILEGES, + #[token("QUALIFY", ignore(ascii_case))] + QUALIFY, #[token("REMOVE", ignore(ascii_case))] REMOVE, #[token("RETAIN", ignore(ascii_case))] @@ -1301,6 +1303,7 @@ impl TokenKind { | TokenKind::OF | TokenKind::ORDER | TokenKind::OVER + | TokenKind::QUALIFY | TokenKind::ROWS // | TokenKind::PRECISION // | TokenKind::RETURNING @@ -1421,6 +1424,7 @@ impl TokenKind { | TokenKind::ORDER | TokenKind::OVER | TokenKind::PARTITION + | TokenKind::QUALIFY | TokenKind::ROWS | TokenKind::RANGE // | TokenKind::OVERLAPS diff --git a/src/query/ast/src/visitors/visitor.rs b/src/query/ast/src/visitors/visitor.rs index dc814041d31d6..4551e0498dcc5 100644 --- a/src/query/ast/src/visitors/visitor.rs +++ b/src/query/ast/src/visitors/visitor.rs @@ -646,6 +646,8 @@ pub trait Visitor<'ast>: Sized { selection, group_by, having, + window_list, + qualify, .. } = stmt; @@ -680,6 +682,16 @@ pub trait Visitor<'ast>: Sized { if let Some(having) = having { walk_expr(self, having); } + + if let Some(window_list) = window_list { + for window_def in window_list { + walk_window_definition(self, window_def); + } + } + + if let Some(qualify) = qualify { + walk_expr(self, qualify); + } } fn visit_select_target(&mut self, target: &'ast SelectTarget) { diff --git a/src/query/ast/src/visitors/visitor_mut.rs b/src/query/ast/src/visitors/visitor_mut.rs index 5c417f67cabed..8fc5f32e0fbba 100644 --- a/src/query/ast/src/visitors/visitor_mut.rs +++ b/src/query/ast/src/visitors/visitor_mut.rs @@ -28,6 +28,7 @@ use super::walk_mut::walk_statement_mut; use super::walk_mut::walk_table_reference_mut; use super::walk_stream_point_mut; use super::walk_time_travel_point_mut; +use super::walk_window_definition_mut; use crate::ast::*; use crate::visitors::walk_column_id_mut; @@ -659,6 +660,8 @@ pub trait VisitorMut: Sized { selection, group_by, having, + window_list, + qualify, .. } = stmt; @@ -693,6 +696,16 @@ pub trait VisitorMut: Sized { if let Some(having) = having { Self::visit_expr(self, having); } + + if let Some(window_list) = window_list { + for window_def in window_list { + walk_window_definition_mut(self, window_def); + } + } + + if let Some(qualify) = qualify { + Self::visit_expr(self, qualify); + } } fn visit_select_target(&mut self, target: &mut SelectTarget) { diff --git a/src/query/ast/src/visitors/walk_mut.rs b/src/query/ast/src/visitors/walk_mut.rs index a0379f0fa0772..673b9a030030e 100644 --- a/src/query/ast/src/visitors/walk_mut.rs +++ b/src/query/ast/src/visitors/walk_mut.rs @@ -198,6 +198,35 @@ pub fn walk_set_expr_mut(visitor: &mut V, set_expr: &mut SetExpr) } } +pub fn walk_window_definition_mut( + visitor: &mut V, + window_definition: &mut WindowDefinition, +) { + let WindowDefinition { name, spec: window } = window_definition; + + visitor.visit_identifier(name); + + let WindowSpec { + partition_by, + order_by, + window_frame, + .. + } = window; + + for expr in partition_by { + visitor.visit_expr(expr); + } + + for order_by in order_by { + visitor.visit_order_by(order_by); + } + + if let Some(frame) = window_frame { + visitor.visit_frame_bound(&mut frame.start_bound); + visitor.visit_frame_bound(&mut frame.end_bound); + } +} + pub fn walk_select_target_mut(visitor: &mut V, target: &mut SelectTarget) { match target { SelectTarget::AliasedExpr { expr, alias } => { diff --git a/src/query/sql/src/planner/binder/bind_context.rs b/src/query/sql/src/planner/binder/bind_context.rs index a4ab5ba3a3e19..ee2fb64fcbac1 100644 --- a/src/query/sql/src/planner/binder/bind_context.rs +++ b/src/query/sql/src/planner/binder/bind_context.rs @@ -56,6 +56,7 @@ pub enum ExprContext { WhereClause, GroupClaue, HavingClause, + QualifyClause, OrderByClause, LimitClause, diff --git a/src/query/sql/src/planner/binder/mod.rs b/src/query/sql/src/planner/binder/mod.rs index 9b23c8c78bb7a..4ed733df8e857 100644 --- a/src/query/sql/src/planner/binder/mod.rs +++ b/src/query/sql/src/planner/binder/mod.rs @@ -36,6 +36,7 @@ mod merge_into; mod presign; mod project; mod project_set; +mod qualify; mod replace; mod scalar; mod scalar_common; diff --git a/src/query/sql/src/planner/binder/qualify.rs b/src/query/sql/src/planner/binder/qualify.rs new file mode 100644 index 0000000000000..07146102aa702 --- /dev/null +++ b/src/query/sql/src/planner/binder/qualify.rs @@ -0,0 +1,250 @@ +// Copyright 2021 Datafuse Labs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::sync::Arc; + +use common_ast::ast::Expr; +use common_exception::ErrorCode; +use common_exception::Result; +use common_exception::Span; + +use super::Finder; +use crate::binder::split_conjunctions; +use crate::binder::window::WindowRewriter; +use crate::binder::ColumnBindingBuilder; +use crate::binder::ExprContext; +use crate::binder::ScalarBinder; +use crate::binder::Visibility; +use crate::optimizer::SExpr; +use crate::planner::semantic::GroupingChecker; +use crate::plans::BoundColumnRef; +use crate::plans::CastExpr; +use crate::plans::Filter; +use crate::plans::FunctionCall; +use crate::plans::LambdaFunc; +use crate::plans::ScalarExpr; +use crate::plans::UDFLambdaCall; +use crate::plans::UDFServerCall; +use crate::plans::Visitor; +use crate::plans::VisitorMut; +use crate::BindContext; +use crate::Binder; + +impl Binder { + /// Analyze window in qualify clause, this will rewrite window functions. + /// See `WindowRewriter` for more details. + #[async_backtrace::framed] + pub async fn analyze_window_qualify<'a>( + &mut self, + bind_context: &mut BindContext, + aliases: &[(String, ScalarExpr)], + qualify: &Expr, + ) -> Result { + dbg!("analyze window qualify", qualify); + bind_context.set_expr_context(ExprContext::QualifyClause); + let mut scalar_binder = ScalarBinder::new( + bind_context, + self.ctx.clone(), + &self.name_resolution_ctx, + self.metadata.clone(), + aliases, + self.m_cte_bound_ctx.clone(), + self.ctes_map.clone(), + ); + let (mut scalar, _) = scalar_binder.bind(qualify).await?; + let mut rewriter = WindowRewriter::new(bind_context, self.metadata.clone()); + rewriter.visit(&mut scalar)?; + Ok(scalar) + } + + #[async_backtrace::framed] + pub async fn bind_qualify( + &mut self, + bind_context: &mut BindContext, + qualify: ScalarExpr, + span: Span, + child: SExpr, + ) -> Result { + dbg!("bind qualify", &qualify); + bind_context.set_expr_context(ExprContext::QualifyClause); + + let f = |scalar: &ScalarExpr| matches!(scalar, ScalarExpr::AggregateFunction(_)); + let mut finder = Finder::new(&f); + finder.visit(&qualify)?; + if !finder.scalars().is_empty() { + return Err(ErrorCode::SemanticError( + "Qualify clause must not contain aggregate functions".to_string(), + ) + .set_span(qualify.span())); + } + + let scalar = if bind_context.in_grouping { + // If we are in grouping context, we will perform the grouping check + let grouping_checker = GroupingChecker::new(bind_context); + grouping_checker.resolve(&qualify, span)? + } else { + let qualify_checker = QualifyChecker::new(bind_context); + qualify_checker.resolve(&qualify)? + }; + + dbg!(&scalar); + + let predicates = split_conjunctions(&scalar); + + let filter = Filter { predicates }; + + Ok(SExpr::create_unary( + Arc::new(filter.into()), + Arc::new(child), + )) + } +} + +pub struct QualifyChecker<'a> { + bind_context: &'a BindContext, +} + +impl<'a> QualifyChecker<'a> { + pub fn new(bind_context: &'a BindContext) -> Self { + Self { bind_context } + } + + pub fn resolve(&self, scalar: &ScalarExpr) -> Result { + match scalar { + ScalarExpr::BoundColumnRef(_) + | ScalarExpr::ConstantExpr(_) + | ScalarExpr::SubqueryExpr(_) => Ok(scalar.clone()), + ScalarExpr::FunctionCall(func) => { + let args = func + .arguments + .iter() + .map(|arg| self.resolve(arg)) + .collect::>>()?; + Ok(FunctionCall { + span: func.span, + params: func.params.clone(), + arguments: args, + func_name: func.func_name.clone(), + } + .into()) + } + + ScalarExpr::LambdaFunction(lambda_func) => { + let args = lambda_func + .args + .iter() + .map(|arg| self.resolve(arg)) + .collect::>>()?; + Ok(LambdaFunc { + span: lambda_func.span, + func_name: lambda_func.func_name.clone(), + display_name: lambda_func.display_name.clone(), + args, + params: lambda_func.params.clone(), + lambda_expr: lambda_func.lambda_expr.clone(), + return_type: lambda_func.return_type.clone(), + } + .into()) + } + + ScalarExpr::CastExpr(cast) => Ok(CastExpr { + span: cast.span, + is_try: cast.is_try, + argument: Box::new(self.resolve(&cast.argument)?), + target_type: cast.target_type.clone(), + } + .into()), + + ScalarExpr::WindowFunction(win) => { + if let Some(column) = self + .bind_context + .windows + .window_functions_map + .get(&win.display_name) + { + // The exprs in `win` has already been rewrittern to `BoundColumnRef` in `WindowRewriter`. + // So we need to check the exprs in `bind_context.windows` + let window_info = &self.bind_context.windows.window_functions[*column]; + + let column_binding = ColumnBindingBuilder::new( + win.display_name.clone(), + window_info.index, + Box::new(window_info.func.return_type()), + Visibility::Visible, + ) + .build(); + return Ok(BoundColumnRef { + span: None, + column: column_binding, + } + .into()); + } + Err(ErrorCode::Internal( + "Qualify check: Invalid window function", + )) + } + + ScalarExpr::AggregateFunction(agg) => { + if let Some(column) = self + .bind_context + .aggregate_info + .aggregate_functions_map + .get(&agg.display_name) + { + let agg_func = &self.bind_context.aggregate_info.aggregate_functions[*column]; + let column_binding = ColumnBindingBuilder::new( + agg.display_name.clone(), + agg_func.index, + Box::new(agg_func.scalar.data_type()?), + Visibility::Visible, + ) + .build(); + return Ok(BoundColumnRef { + span: None, + column: column_binding, + } + .into()); + } + Err(ErrorCode::Internal("Invalid aggregate function")) + } + ScalarExpr::UDFServerCall(udf) => { + let args = udf + .arguments + .iter() + .map(|arg| self.resolve(arg)) + .collect::>>()?; + Ok(UDFServerCall { + span: udf.span, + func_name: udf.func_name.clone(), + display_name: udf.display_name.clone(), + server_addr: udf.server_addr.clone(), + arg_types: udf.arg_types.clone(), + return_type: udf.return_type.clone(), + arguments: args, + } + .into()) + } + ScalarExpr::UDFLambdaCall(udf) => { + let scalar = &udf.scalar; + let new_scalar = self.resolve(scalar)?; + Ok(UDFLambdaCall { + span: udf.span, + func_name: udf.func_name.clone(), + scalar: Box::new(new_scalar), + } + .into()) + } + } + } +} diff --git a/src/query/sql/src/planner/binder/select.rs b/src/query/sql/src/planner/binder/select.rs index ad6681c7552d8..e06cbf45c09e5 100644 --- a/src/query/sql/src/planner/binder/select.rs +++ b/src/query/sql/src/planner/binder/select.rs @@ -212,6 +212,15 @@ impl Binder { None }; + let qualify = if let Some(qualify) = &stmt.qualify { + Some( + self.analyze_window_qualify(&mut from_context, &aliases, qualify) + .await?, + ) + } else { + None + }; + let order_items = self .analyze_order_items( &mut from_context, @@ -253,6 +262,12 @@ impl Binder { s_expr = self.bind_window_function(window_info, s_expr).await?; } + if let Some(qualify) = qualify { + s_expr = self + .bind_qualify(&mut from_context, qualify, span, s_expr) + .await?; + } + if stmt.distinct { s_expr = self.bind_distinct( stmt.span, diff --git a/src/query/sql/src/planner/binder/table.rs b/src/query/sql/src/planner/binder/table.rs index 3b066caea5e3a..8887916f4508e 100644 --- a/src/query/sql/src/planner/binder/table.rs +++ b/src/query/sql/src/planner/binder/table.rs @@ -566,6 +566,7 @@ impl Binder { group_by: None, having: None, window_list: None, + qualify: None, }; let (srf_expr, mut bind_context) = self .bind_select_stmt(bind_context, &select_stmt, &[], 0) diff --git a/src/query/sql/src/planner/binder/window.rs b/src/query/sql/src/planner/binder/window.rs index b05b709828575..088596c5d4d89 100644 --- a/src/query/sql/src/planner/binder/window.rs +++ b/src/query/sql/src/planner/binder/window.rs @@ -22,6 +22,7 @@ use common_exception::Result; use common_exception::Span; use super::select::SelectList; +use crate::binder::ColumnBinding; use crate::binder::ColumnBindingBuilder; use crate::optimizer::SExpr; use crate::plans::walk_expr_mut; @@ -344,8 +345,8 @@ impl<'a> WindowRewriter<'a> { frame: window.frame.clone(), }; - let window_infos = &mut self.bind_context.windows; // push window info to BindContext + let window_infos = &mut self.bind_context.windows; window_infos.window_functions.push(window_info); window_infos.window_functions_map.insert( window.display_name.clone(), @@ -423,7 +424,7 @@ impl<'a> WindowRewriter<'a> { } } - fn as_window_aggregate_rewriter(&self) -> WindowAggregateRewriter { + pub fn as_window_aggregate_rewriter(&self) -> WindowAggregateRewriter { WindowAggregateRewriter { bind_context: self.bind_context, } @@ -431,6 +432,23 @@ impl<'a> WindowRewriter<'a> { } impl<'a> VisitorMut<'a> for WindowRewriter<'a> { + fn visit(&mut self, expr: &'a mut ScalarExpr) -> Result<()> { + if let ScalarExpr::WindowFunction(window) = expr { + *expr = { + let window_infos = &self.bind_context.windows; + + if let Some(column) = + find_replaced_window_function(window_infos, window, &window.display_name) + { + BoundColumnRef { span: None, column }.into() + } else { + self.replace_window_function(window)?.into() + } + }; + return Ok(()); + } + walk_expr_mut(self, expr) + } fn visit_window_function(&mut self, window: &'a mut WindowFunc) -> Result<()> { *window = self.replace_window_function(window)?; Ok(()) @@ -480,6 +498,31 @@ impl<'a> VisitorMut<'a> for WindowAggregateRewriter<'a> { } } +/// Replace [`WindowFunction`] with a [`ColumnBinding`] if the function is already replaced. +pub fn find_replaced_window_function( + window_info: &WindowInfo, + window: &WindowFunc, + new_name: &str, +) -> Option { + window_info + .window_functions_map + .get(&window.display_name) + .map(|i| { + let window_func_info = &window_info.window_functions[*i]; + debug_assert_eq!( + window_func_info.func.return_type(), + window.func.return_type() + ); + ColumnBindingBuilder::new( + new_name.to_string(), + window_func_info.index, + Box::new(window.func.return_type()), + Visibility::Visible, + ) + .build() + }) +} + impl Binder { /// Analyze windows in select clause, this will rewrite window functions. /// See [`WindowRewriter`] for more details. diff --git a/src/query/sql/src/planner/semantic/aggregating_index_visitor.rs b/src/query/sql/src/planner/semantic/aggregating_index_visitor.rs index c8e18047cf2c3..300560df7cd3d 100644 --- a/src/query/sql/src/planner/semantic/aggregating_index_visitor.rs +++ b/src/query/sql/src/planner/semantic/aggregating_index_visitor.rs @@ -197,7 +197,7 @@ impl<'ast> Visitor<'ast> for AggregatingIndexChecker { if self.not_support { return; } - if stmt.having.is_some() || stmt.window_list.is_some() { + if stmt.having.is_some() || stmt.window_list.is_some() || stmt.qualify.is_some() { self.not_support = true; return; } diff --git a/src/query/sql/src/planner/semantic/distinct_to_groupby.rs b/src/query/sql/src/planner/semantic/distinct_to_groupby.rs index f3c04152fb962..40ec105c67eae 100644 --- a/src/query/sql/src/planner/semantic/distinct_to_groupby.rs +++ b/src/query/sql/src/planner/semantic/distinct_to_groupby.rs @@ -36,6 +36,7 @@ impl VisitorMut for DistinctToGroupBy { group_by, having, window_list, + qualify, .. } = stmt; @@ -76,6 +77,7 @@ impl VisitorMut for DistinctToGroupBy { group_by: Some(GroupBy::Normal(args.clone())), having: None, window_list: None, + qualify: None, })), order_by: vec![], limit: vec![], @@ -121,6 +123,7 @@ impl VisitorMut for DistinctToGroupBy { group_by: None, having: having.clone(), window_list: window_list.clone(), + qualify: qualify.clone(), }; *stmt = new_stmt; diff --git a/src/tests/sqlsmith/src/sql_gen/query.rs b/src/tests/sqlsmith/src/sql_gen/query.rs index 042a9f44b7b94..68419545d2177 100644 --- a/src/tests/sqlsmith/src/sql_gen/query.rs +++ b/src/tests/sqlsmith/src/sql_gen/query.rs @@ -120,6 +120,7 @@ impl<'a, R: Rng> SqlGenerator<'a, R> { group_by: None, having: None, window_list: None, + qualify: None, }; let body = SetExpr::Select(Box::new(select)); @@ -311,6 +312,7 @@ impl<'a, R: Rng> SqlGenerator<'a, R> { group_by, having: self.gen_selection(), window_list: self.gen_window_list(), + qualify: None, // todo: add qualify. } } From 138c7ae2c862f74ed38953a98b7f6e0bf914cce5 Mon Sep 17 00:00:00 2001 From: Yijun Zhao Date: Wed, 29 Nov 2023 12:45:12 +0800 Subject: [PATCH 2/8] fix ut --- .../ast/tests/it/testdata/query-error.txt | 2 +- src/query/ast/tests/it/testdata/query.txt | 63 ++ .../ast/tests/it/testdata/statement-error.txt | 745 ------------------ src/query/ast/tests/it/testdata/statement.txt | 57 ++ src/query/sql/src/planner/binder/qualify.rs | 3 +- 5 files changed, 122 insertions(+), 748 deletions(-) delete mode 100644 src/query/ast/tests/it/testdata/statement-error.txt diff --git a/src/query/ast/tests/it/testdata/query-error.txt b/src/query/ast/tests/it/testdata/query-error.txt index 1ca0c1963a0eb..bd09b7c29256c 100644 --- a/src/query/ast/tests/it/testdata/query-error.txt +++ b/src/query/ast/tests/it/testdata/query-error.txt @@ -79,6 +79,6 @@ error: --> SQL:1:10 | 1 | select 1 1 - | ^ unexpected `1`, expecting , , `AS`, `,`, `FROM`, `WHERE`, `GROUP`, `HAVING`, `WINDOW`, `(`, `WITH`, `UNION`, `EXCEPT`, `INTERSECT`, `SELECT`, `VALUES`, `ORDER`, `LIMIT`, `OFFSET`, or `IGNORE_RESULT` + | ^ unexpected `1`, expecting , , `AS`, `,`, `FROM`, `WHERE`, `GROUP`, `HAVING`, `WINDOW`, `QUALIFY`, `(`, `WITH`, `UNION`, `EXCEPT`, `INTERSECT`, `SELECT`, `VALUES`, `ORDER`, `LIMIT`, `OFFSET`, or `IGNORE_RESULT` diff --git a/src/query/ast/tests/it/testdata/query.txt b/src/query/ast/tests/it/testdata/query.txt index 2960ab2baf841..5f896a0ea2bb1 100644 --- a/src/query/ast/tests/it/testdata/query.txt +++ b/src/query/ast/tests/it/testdata/query.txt @@ -174,6 +174,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -444,6 +445,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -533,6 +535,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -661,6 +664,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -789,6 +793,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -926,6 +931,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -1033,6 +1039,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -1159,6 +1166,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -1264,6 +1272,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -1374,6 +1383,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -1479,6 +1489,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -1589,6 +1600,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -1686,6 +1698,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -1796,6 +1809,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -1901,6 +1915,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -1972,6 +1987,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -2083,6 +2099,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -2283,6 +2300,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -2395,6 +2413,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), right: Select( @@ -2449,6 +2468,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), }, @@ -2522,6 +2542,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -2735,6 +2756,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -3137,6 +3159,7 @@ Query { ), having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -3183,6 +3206,7 @@ Query { ), having: None, window_list: None, + qualify: None, }, ), order_by: [ @@ -3346,6 +3370,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), right: Select( @@ -3391,6 +3416,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), }, @@ -3462,6 +3488,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), right: Select( @@ -3507,6 +3534,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), }, @@ -3585,6 +3613,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), right: Select( @@ -3630,6 +3659,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), }, @@ -3677,6 +3707,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), }, @@ -3755,6 +3786,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), right: Select( @@ -3800,6 +3832,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), }, @@ -3847,6 +3880,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), }, @@ -3918,6 +3952,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), right: SetOperation( @@ -3970,6 +4005,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), right: Select( @@ -4015,6 +4051,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), }, @@ -4095,6 +4132,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), right: Select( @@ -4140,6 +4178,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), }, @@ -4187,6 +4226,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), }, @@ -4258,6 +4298,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), right: SetOperation( @@ -4310,6 +4351,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), right: Select( @@ -4355,6 +4397,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), }, @@ -4439,6 +4482,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), right: Select( @@ -4465,6 +4509,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), }, @@ -4492,6 +4537,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -4572,6 +4618,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), right: Select( @@ -4598,6 +4645,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), }, @@ -4625,6 +4673,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -4717,6 +4766,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [ @@ -4783,6 +4833,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -4923,6 +4974,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [ @@ -5053,6 +5105,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [ @@ -5151,6 +5204,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -5308,6 +5362,7 @@ Query { }, ], ), + qualify: None, }, ), order_by: [], @@ -5634,6 +5689,7 @@ Query { }, ], ), + qualify: None, }, ), order_by: [ @@ -5747,6 +5803,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [ @@ -5813,6 +5870,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -5957,6 +6015,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -6171,6 +6230,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -6217,6 +6277,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -6231,6 +6292,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -6348,6 +6410,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], diff --git a/src/query/ast/tests/it/testdata/statement-error.txt b/src/query/ast/tests/it/testdata/statement-error.txt deleted file mode 100644 index 8f0a78885764d..0000000000000 --- a/src/query/ast/tests/it/testdata/statement-error.txt +++ /dev/null @@ -1,745 +0,0 @@ ----------- Input ---------- -create table a.b (c integer not null 1, b float(10)) ----------- Output --------- -error: - --> SQL:1:38 - | -1 | create table a.b (c integer not null 1, b float(10)) - | ------ ^ unexpected `1`, expecting `)`, `NULL`, `NOT`, `DEFAULT`, `GENERATED`, `AS`, `COMMENT`, or `,` - | | - | while parsing `CREATE TABLE [IF NOT EXISTS] [.] [] []` - - ----------- Input ---------- -create table a (c float(10)) ----------- Output --------- -error: - --> SQL:1:24 - | -1 | create table a (c float(10)) - | ------ ^ unexpected `(`, expecting `)`, `NULL`, `NOT`, `DEFAULT`, `GENERATED`, `AS`, `COMMENT`, or `,` - | | - | while parsing `CREATE TABLE [IF NOT EXISTS] [.]
[] []` - - ----------- Input ---------- -create table a (c varch) ----------- Output --------- -error: - --> SQL:1:19 - | -1 | create table a (c varch) - | ------ - ^^^^^ unexpected `varch`, expecting `VARCHAR`, `CHAR`, `VARIANT`, `CHARACTER`, `VARBINARY`, `ARRAY`, `BINARY`, `MAP`, `DATE`, `STRING`, `FLOAT32`, `FLOAT64`, `DECIMAL`, `SMALLINT`, `DATETIME`, `NULLABLE`, `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT`, `DOUBLE`, `BITMAP`, `TUPLE`, `TIMESTAMP`, `TEXT`, or `JSON` - | | | - | | while parsing ` [DEFAULT ] [AS () VIRTUAL] [AS () STORED] [COMMENT '']` - | while parsing `CREATE TABLE [IF NOT EXISTS] [.]
[] []` - - ----------- Input ---------- -create table a (c tuple()) ----------- Output --------- -error: - --> SQL:1:25 - | -1 | create table a (c tuple()) - | ------ - ----- ^ unexpected `)`, expecting `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `SMALLINT`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT32`, `FLOAT`, `FLOAT64`, `DOUBLE`, `DECIMAL`, `ARRAY`, `MAP`, `BITMAP`, `TUPLE`, `DATE`, `DATETIME`, `TIMESTAMP`, `STRING`, `VARCHAR`, `CHAR`, `CHARACTER`, `TEXT`, `BINARY`, `VARBINARY`, `VARIANT`, `JSON`, `NULLABLE`, , or - | | | | - | | | while parsing type name - | | while parsing ` [DEFAULT ] [AS () VIRTUAL] [AS () STORED] [COMMENT '']` - | while parsing `CREATE TABLE [IF NOT EXISTS] [.]
[] []` - - ----------- Input ---------- -create table a (c decimal) ----------- Output --------- -error: - --> SQL:1:26 - | -1 | create table a (c decimal) - | ------ - -------^ unexpected `)`, expecting `(` - | | | | - | | | while parsing type name - | | while parsing ` [DEFAULT ] [AS () VIRTUAL] [AS () STORED] [COMMENT '']` - | while parsing `CREATE TABLE [IF NOT EXISTS] [.]
[] []` - - ----------- Input ---------- -create table a (b tuple(c int, uint64)); ----------- Output --------- -error: - --> SQL:1:38 - | -1 | create table a (b tuple(c int, uint64)); - | ------ - ----- ^ unexpected `)`, expecting `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `SMALLINT`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT32`, `FLOAT`, `FLOAT64`, `DOUBLE`, `DECIMAL`, `ARRAY`, `MAP`, `BITMAP`, `TUPLE`, `DATE`, `DATETIME`, `TIMESTAMP`, `STRING`, `VARCHAR`, `CHAR`, `CHARACTER`, `TEXT`, `BINARY`, `VARBINARY`, `VARIANT`, `JSON`, or `NULLABLE` - | | | | - | | | while parsing TUPLE( , ...) - | | | while parsing type name - | | while parsing ` [DEFAULT ] [AS () VIRTUAL] [AS () STORED] [COMMENT '']` - | while parsing `CREATE TABLE [IF NOT EXISTS] [.]
[] []` - - ----------- Input ---------- -CREATE TABLE t(c1 NULLABLE(int) NOT NULL); ----------- Output --------- -error: - --> SQL:1:41 - | -1 | CREATE TABLE t(c1 NULLABLE(int) NOT NULL); - | ------ ^ ambiguous NOT NULL constraint - | | - | while parsing `CREATE TABLE [IF NOT EXISTS] [.]
[] []` - - ----------- Input ---------- -drop table if a.b ----------- Output --------- -error: - --> SQL:1:15 - | -1 | drop table if a.b - | ---- ^ unexpected `a`, expecting `EXISTS` - | | - | while parsing `DROP TABLE [IF EXISTS] [.]
` - - ----------- Input ---------- -truncate table a.b.c.d ----------- Output --------- -error: - --> SQL:1:21 - | -1 | truncate table a.b.c.d - | ^ unexpected `.`, expecting `FORMAT` or `;` - - ----------- Input ---------- -truncate a ----------- Output --------- -error: - --> SQL:1:10 - | -1 | truncate a - | -------- ^ unexpected `a`, expecting `TABLE` - | | - | while parsing `TRUNCATE TABLE [.]
` - - ----------- Input ---------- -drop a ----------- Output --------- -error: - --> SQL:1:6 - | -1 | drop a - | ^ unexpected `a`, expecting `TASK`, `TABLE`, `MASKING`, `CATALOG`, `DATABASE`, `AGGREGATING`, `SCHEMA`, `NETWORK`, `VIEW`, `STREAM`, `VIRTUAL`, `USER`, `ROLE`, `FUNCTION`, `STAGE`, `FILE`, `SHARE`, `PIPE`, or `CONNECTION` - - ----------- Input ---------- -insert into t format ----------- Output --------- -error: - --> SQL:1:21 - | -1 | insert into t format - | ------ ^ unexpected end of line, expecting or - | | - | while parsing `INSERT INTO [TABLE]
[(, ...)] (FORMAT | VALUES | )` - - ----------- Input ---------- -show tables format ----------- Output --------- -error: - --> SQL:1:19 - | -1 | show tables format - | ^ unexpected end of line, expecting or - - ----------- Input ---------- -alter database system x rename to db ----------- Output --------- -error: - --> SQL:1:23 - | -1 | alter database system x rename to db - | ----- ^ unexpected `x`, expecting `RENAME` or `.` - | | - | while parsing `ALTER DATABASE [IF EXISTS] ` - - ----------- Input ---------- -create user 'test-e' identified bi 'password'; ----------- Output --------- -error: - --> SQL:1:33 - | -1 | create user 'test-e' identified bi 'password'; - | ^^ unexpected `bi`, expecting `BY`, `WITH`, `FORMAT`, or `;` - - ----------- Input ---------- -create user 'test-e'@'localhost' identified by 'password'; ----------- Output --------- -error: - --> SQL:1:22 - | -1 | create user 'test-e'@'localhost' identified by 'password'; - | ------ ^^^^^^^^^^^ unexpected `'localhost'`, expecting `'%'` or `IDENTIFIED` - | | - | while parsing `CREATE USER [IF NOT EXISTS] ''@'hostname' IDENTIFIED [WITH ] [BY ] [WITH , ...]` - - ----------- Input ---------- -drop usar if exists 'test-j'; ----------- Output --------- -error: - --> SQL:1:6 - | -1 | drop usar if exists 'test-j'; - | ^^^^ unexpected `usar`, expecting `USER`, `SHARE`, `STREAM`, `STAGE`, `AGGREGATING`, `ROLE`, `TABLE`, `SCHEMA`, `NETWORK`, `VIRTUAL`, `CATALOG`, `DATABASE`, `FUNCTION`, `TASK`, `MASKING`, `VIEW`, `FILE`, `PIPE`, or `CONNECTION` - - ----------- Input ---------- -alter user 'test-e' identifies by 'new-password'; ----------- Output --------- -error: - --> SQL:1:21 - | -1 | alter user 'test-e' identifies by 'new-password'; - | ^^^^^^^^^^ unexpected `identifies`, expecting `IDENTIFIED`, `WITH`, `FORMAT`, `@`, or `;` - - ----------- Input ---------- -create role 'test'@'%'; ----------- Output --------- -error: - --> SQL:1:19 - | -1 | create role 'test'@'%'; - | ^ unexpected `@`, expecting `FORMAT` or `;` - - ----------- Input ---------- -drop role 'test'@'%'; ----------- Output --------- -error: - --> SQL:1:17 - | -1 | drop role 'test'@'%'; - | ^ unexpected `@`, expecting `FORMAT` or `;` - - ----------- Input ---------- -SHOW GRANT FOR ROLE 'role1'; ----------- Output --------- -error: - --> SQL:1:6 - | -1 | SHOW GRANT FOR ROLE 'role1'; - | ^^^^^ unexpected `GRANT`, expecting `GRANTS`, `CREATE`, `NETWORK`, `STREAMS`, `CATALOGS`, `FUNCTIONS`, `DATABASES`, `CONNECTIONS`, `TABLE_FUNCTIONS`, `DROP`, `TABLE`, `ROLES`, `SHARE`, `TASKS`, `INDEXES`, `COLUMNS`, `PROCESSLIST`, `STAGES`, `TABLES`, `SHARES`, `ENGINES`, `METRICS`, `SETTINGS`, `SCHEMAS`, `FIELDS`, `USERS`, `FILE`, or `FULL` - - ----------- Input ---------- -GRANT ROLE 'test' TO ROLE test-user; ----------- Output --------- -error: - --> SQL:1:31 - | -1 | GRANT ROLE 'test' TO ROLE test-user; - | ^ unexpected `-`, expecting `FORMAT` or `;` - - ----------- Input ---------- -GRANT SELECT, ALL PRIVILEGES, CREATE ON * TO 'test-grant'; ----------- Output --------- -error: - --> SQL:1:15 - | -1 | GRANT SELECT, ALL PRIVILEGES, CREATE ON * TO 'test-grant'; - | ----- ------ ^^^ unexpected `ALL`, expecting `ALTER`, `SELECT`, `DELETE`, `USAGE`, `INSERT`, `UPDATE`, `SUPER`, `CREATE`, `DROP`, `GRANT`, `SET`, or `OWNERSHIP` - | | | - | | while parsing ON - | while parsing `GRANT { ROLE | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON } TO { [ROLE ] | [USER] }` - - ----------- Input ---------- -GRANT SELECT, CREATE ON *.c TO 'test-grant'; ----------- Output --------- -error: - --> SQL:1:27 - | -1 | GRANT SELECT, CREATE ON *.c TO 'test-grant'; - | ----- ^ unexpected `c`, expecting `TO` or `*` - | | - | while parsing `GRANT { ROLE | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON } TO { [ROLE ] | [USER] }` - - ----------- Input ---------- -GRANT select ON UDF a TO 'test-grant'; ----------- Output --------- -error: - --> SQL:1:21 - | -1 | GRANT select ON UDF a TO 'test-grant'; - | ----- ^ unexpected `a`, expecting `TO` or `.` - | | - | while parsing `GRANT { ROLE | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON } TO { [ROLE ] | [USER] }` - - ----------- Input ---------- -REVOKE SELECT, CREATE, ALL PRIVILEGES ON * FROM 'test-grant'; ----------- Output --------- -error: - --> SQL:1:24 - | -1 | REVOKE SELECT, CREATE, ALL PRIVILEGES ON * FROM 'test-grant'; - | ------ ------ ^^^ unexpected `ALL`, expecting `ALTER`, `SELECT`, `DELETE`, `USAGE`, `INSERT`, `UPDATE`, `SUPER`, `CREATE`, `DROP`, `GRANT`, `SET`, or `OWNERSHIP` - | | | - | | while parsing ON - | while parsing `REVOKE { ROLE | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON } FROM { [ROLE ] | [USER] }` - - ----------- Input ---------- -REVOKE SELECT, CREATE ON * TO 'test-grant'; ----------- Output --------- -error: - --> SQL:1:28 - | -1 | REVOKE SELECT, CREATE ON * TO 'test-grant'; - | ------ ^^ unexpected `TO`, expecting `FROM` or `.` - | | - | while parsing `REVOKE { ROLE | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON } FROM { [ROLE ] | [USER] }` - - ----------- Input ---------- -COPY INTO mytable FROM 's3://bucket' CREDENTIAL = (); ----------- Output --------- -error: - --> SQL:1:38 - | -1 | COPY INTO mytable FROM 's3://bucket' CREDENTIAL = (); - | ^^^^^^^^^^ unexpected `CREDENTIAL`, expecting `CREDENTIALS`, `DISABLE_VARIANT_CHECK`, `RETURN_FAILED_ONLY`, `CONNECTION`, `PURGE`, `VALIDATION_MODE`, `FORCE`, `LOCATION_PREFIX`, `FORMAT`, `PATTERN`, `FILES`, `MAX_FILES`, `SIZE_LIMIT`, `FILE_FORMAT`, `ON_ERROR`, `SPLIT_SIZE`, or `;` - - ----------- Input ---------- -COPY INTO mytable FROM @mystage CREDENTIALS = (); ----------- Output --------- -error: - --> SQL:1:33 - | -1 | COPY INTO mytable FROM @mystage CREDENTIALS = (); - | ^^^^^^^^^^^ unexpected `CREDENTIALS`, expecting `DISABLE_VARIANT_CHECK`, `RETURN_FAILED_ONLY`, `MAX_FILES`, `PURGE`, `VALIDATION_MODE`, `FORCE`, `FORMAT`, `PATTERN`, `FILES`, `SIZE_LIMIT`, `SPLIT_SIZE`, `FILE_FORMAT`, `ON_ERROR`, or `;` - - ----------- Input ---------- -CALL system$test ----------- Output --------- -error: - --> SQL:1:17 - | -1 | CALL system$test - | ---- ^ unexpected end of line, expecting `(` - | | - | while parsing `CALL (, ...)` - - ----------- Input ---------- -CALL system$test(a ----------- Output --------- -error: - --> SQL:1:19 - | -1 | CALL system$test(a - | ---- ^ unexpected end of line, expecting `)` or `,` - | | - | while parsing `CALL (, ...)` - - ----------- Input ---------- -show settings ilike 'enable%' ----------- Output --------- -error: - --> SQL:1:15 - | -1 | show settings ilike 'enable%' - | ^^^^^ unexpected `ilike`, expecting `LIKE`, `LIMIT`, `WHERE`, `FORMAT`, or `;` - - ----------- Input ---------- -PRESIGN INVALID @my_stage/path/to/file ----------- Output --------- -error: - --> SQL:1:9 - | -1 | PRESIGN INVALID @my_stage/path/to/file - | ------- ^^^^^^^ unexpected `INVALID`, expecting `DOWNLOAD`, `AtString`, `UPLOAD`, or - | | - | while parsing `PRESIGN [{DOWNLOAD | UPLOAD}] [EXPIRE = 3600]` - - ----------- Input ---------- -SELECT c a as FROM t ----------- Output --------- -error: - --> SQL:1:12 - | -1 | SELECT c a as FROM t - | ^^ an alias without `AS` keyword has already been defined before this one, please remove one of them - - ----------- Input ---------- -SELECT c a as b FROM t ----------- Output --------- -error: - --> SQL:1:12 - | -1 | SELECT c a as b FROM t - | ^^ an alias without `AS` keyword has already been defined before this one, please remove one of them - - ----------- Input ---------- -SELECT * FROM t GROUP BY GROUPING SETS a, b ----------- Output --------- -error: - --> SQL:1:35 - | -1 | SELECT * FROM t GROUP BY GROUPING SETS a, b - | ^^^^ unexpected `SETS`, expecting `SELECT`, `INTERSECT`, `WITH`, `EXCEPT`, `VALUES`, `OFFSET`, `IGNORE_RESULT`, `,`, `HAVING`, `WINDOW`, `(`, `UNION`, `FROM`, `ORDER`, `LIMIT`, `FORMAT`, or `;` - - ----------- Input ---------- -SELECT * FROM t GROUP BY GROUPING SETS () ----------- Output --------- -error: - --> SQL:1:41 - | -1 | SELECT * FROM t GROUP BY GROUPING SETS () - | ------ ^ unexpected `)`, expecting `(`, `IS`, `IN`, `EXISTS`, `BETWEEN`, `+`, `-`, `*`, `/`, `//`, `DIV`, `%`, `||`, `<->`, `>`, `<`, `>=`, `<=`, `=`, `<>`, `!=`, `^`, `AND`, `OR`, `XOR`, `LIKE`, `NOT`, `REGEXP`, `RLIKE`, `SOUNDS`, , , , , , `->`, `->>`, `#>`, `#>>`, `?`, `?|`, `?&`, `@>`, `<@`, , , , , , `CAST`, `TRY_CAST`, `DATE_ADD`, `DATE_SUB`, `DATE_TRUNC`, `DATE`, `TIMESTAMP`, `INTERVAL`, `::`, `EXTRACT`, `DATE_PART`, or 24 more ... - | | - | while parsing `SELECT ...` - - ----------- Input ---------- -select * from aa.bb limit 10 order by bb; ----------- Output --------- -error: - --> SQL:1:30 - | -1 | select * from aa.bb limit 10 order by bb; - | ------ ^^^^^ ORDER BY must appear before LIMIT - | | - | while parsing `SELECT ...` - - ----------- Input ---------- -select * from aa.bb offset 10 order by bb; ----------- Output --------- -error: - --> SQL:1:31 - | -1 | select * from aa.bb offset 10 order by bb; - | ------ ^^^^^ ORDER BY must appear before OFFSET - | | - | while parsing `SELECT ...` - - ----------- Input ---------- -select * from aa.bb offset 10 limit 1; ----------- Output --------- -error: - --> SQL:1:31 - | -1 | select * from aa.bb offset 10 limit 1; - | ------ ^^^^^ LIMIT must appear before OFFSET - | | - | while parsing `SELECT ...` - - ----------- Input ---------- -select * from aa.bb order by a order by b; ----------- Output --------- -error: - --> SQL:1:32 - | -1 | select * from aa.bb order by a order by b; - | ------ ^^^^^ duplicated ORDER BY clause - | | - | while parsing `SELECT ...` - - ----------- Input ---------- -select * from aa.bb offset 10 offset 20; ----------- Output --------- -error: - --> SQL:1:31 - | -1 | select * from aa.bb offset 10 offset 20; - | ------ ^^^^^^ duplicated OFFSET clause - | | - | while parsing `SELECT ...` - - ----------- Input ---------- -select * from aa.bb limit 10 limit 20; ----------- Output --------- -error: - --> SQL:1:30 - | -1 | select * from aa.bb limit 10 limit 20; - | ------ ^^^^^ duplicated LIMIT clause - | | - | while parsing `SELECT ...` - - ----------- Input ---------- -select * from aa.bb limit 10,2 offset 2; ----------- Output --------- -error: - --> SQL:1:32 - | -1 | select * from aa.bb limit 10,2 offset 2; - | ------ ^^^^^^ LIMIT n,m should not appear OFFSET - | | - | while parsing `SELECT ...` - - ----------- Input ---------- -select * from aa.bb limit 10,2,3; ----------- Output --------- -error: - --> SQL:1:21 - | -1 | select * from aa.bb limit 10,2,3; - | ------ ^^^^^ [LIMIT n OFFSET m] or [LIMIT n,m] - | | - | while parsing `SELECT ...` - - ----------- Input ---------- -with a as (select 1) with b as (select 2) select * from aa.bb; ----------- Output --------- -error: - --> SQL:1:43 - | -1 | with a as (select 1) with b as (select 2) select * from aa.bb; - | ---- ^^^^^^ duplicated WITH clause - | | - | while parsing `SELECT ...` - - ----------- Input ---------- -with as t2(tt) as (select a from t) select t2.tt from t2 ----------- Output --------- -error: - --> SQL:1:6 - | -1 | with as t2(tt) as (select a from t) select t2.tt from t2 - | ---- ^^ unexpected `as`, expecting , , or `RECURSIVE` - | | - | while parsing `SELECT ...` - - ----------- Input ---------- -copy into t1 from "" FILE ----------- Output --------- -error: - --> SQL:1:19 - | -1 | copy into t1 from "" FILE - | ---- ^^ unexpected `""`, expecting , `AtString`, or `(` - | | - | while parsing `COPY - INTO { [.] { ( ) } } - FROM { internalStage | externalStage | externalLocation | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] - [ FILES = ( '' [ , '' ] [ , ... ] ) ] - [ PATTERN = '' ] - [ VALIDATION_MODE = RETURN_ROWS ] - [ copyOptions ]` - - ----------- Input ---------- -select $1 from @data/csv/books.csv (file_format => 'aa' bad_arg => 'x', pattern => 'bb') ----------- Output --------- -error: - --> SQL:1:57 - | -1 | select $1 from @data/csv/books.csv (file_format => 'aa' bad_arg => 'x', pattern => 'bb') - | ------ ^^^^^^^ unexpected `bad_arg`, expecting `PATTERN`, `FILE_FORMAT`, `)`, `,`, `FILES`, or `CONNECTION` - | | - | while parsing `SELECT ...` - - ----------- Input ---------- -copy into t1 from "" FILE_FORMAT ----------- Output --------- -error: - --> SQL:1:19 - | -1 | copy into t1 from "" FILE_FORMAT - | ---- ^^ unexpected `""`, expecting , `AtString`, or `(` - | | - | while parsing `COPY - INTO { [.] { ( ) } } - FROM { internalStage | externalStage | externalLocation | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] - [ FILES = ( '' [ , '' ] [ , ... ] ) ] - [ PATTERN = '' ] - [ VALIDATION_MODE = RETURN_ROWS ] - [ copyOptions ]` - - ----------- Input ---------- -copy into t1 from "" FILE_FORMAT = ----------- Output --------- -error: - --> SQL:1:19 - | -1 | copy into t1 from "" FILE_FORMAT = - | ---- ^^ unexpected `""`, expecting , `AtString`, or `(` - | | - | while parsing `COPY - INTO { [.] { ( ) } } - FROM { internalStage | externalStage | externalLocation | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] - [ FILES = ( '' [ , '' ] [ , ... ] ) ] - [ PATTERN = '' ] - [ VALIDATION_MODE = RETURN_ROWS ] - [ copyOptions ]` - - ----------- Input ---------- -copy into t1 from "" FILE_FORMAT = ( ----------- Output --------- -error: - --> SQL:1:19 - | -1 | copy into t1 from "" FILE_FORMAT = ( - | ---- ^^ unexpected `""`, expecting , `AtString`, or `(` - | | - | while parsing `COPY - INTO { [.] { ( ) } } - FROM { internalStage | externalStage | externalLocation | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] - [ FILES = ( '' [ , '' ] [ , ... ] ) ] - [ PATTERN = '' ] - [ VALIDATION_MODE = RETURN_ROWS ] - [ copyOptions ]` - - ----------- Input ---------- -copy into t1 from "" FILE_FORMAT = (TYPE ----------- Output --------- -error: - --> SQL:1:19 - | -1 | copy into t1 from "" FILE_FORMAT = (TYPE - | ---- ^^ unexpected `""`, expecting , `AtString`, or `(` - | | - | while parsing `COPY - INTO { [.] { ( ) } } - FROM { internalStage | externalStage | externalLocation | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] - [ FILES = ( '' [ , '' ] [ , ... ] ) ] - [ PATTERN = '' ] - [ VALIDATION_MODE = RETURN_ROWS ] - [ copyOptions ]` - - ----------- Input ---------- -copy into t1 from "" FILE_FORMAT = (TYPE = ----------- Output --------- -error: - --> SQL:1:19 - | -1 | copy into t1 from "" FILE_FORMAT = (TYPE = - | ---- ^^ unexpected `""`, expecting , `AtString`, or `(` - | | - | while parsing `COPY - INTO { [.] { ( ) } } - FROM { internalStage | externalStage | externalLocation | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] - [ FILES = ( '' [ , '' ] [ , ... ] ) ] - [ PATTERN = '' ] - [ VALIDATION_MODE = RETURN_ROWS ] - [ copyOptions ]` - - ----------- Input ---------- -copy into t1 from "" FILE_FORMAT = (TYPE = ----------- Output --------- -error: - --> SQL:1:19 - | -1 | copy into t1 from "" FILE_FORMAT = (TYPE = - | ---- ^^ unexpected `""`, expecting , `AtString`, or `(` - | | - | while parsing `COPY - INTO { [.] { ( ) } } - FROM { internalStage | externalStage | externalLocation | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] - [ FILES = ( '' [ , '' ] [ , ... ] ) ] - [ PATTERN = '' ] - [ VALIDATION_MODE = RETURN_ROWS ] - [ copyOptions ]` - - ----------- Input ---------- -COPY INTO t1 FROM "" PATTERN = '.*[.]csv' FILE_FORMAT = (type = TSV field_delimiter = '\t' skip_headerx = 0); ----------- Output --------- -error: - --> SQL:1:19 - | -1 | COPY INTO t1 FROM "" PATTERN = '.*[.]csv' FILE_FORMAT = (type = TSV field_delimiter = '\t' skip_headerx = 0); - | ---- ^^ unexpected `""`, expecting , `AtString`, or `(` - | | - | while parsing `COPY - INTO { [.] { ( ) } } - FROM { internalStage | externalStage | externalLocation | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] - [ FILES = ( '' [ , '' ] [ , ... ] ) ] - [ PATTERN = '' ] - [ VALIDATION_MODE = RETURN_ROWS ] - [ copyOptions ]` - - ----------- Input ---------- -COPY INTO mytable - FROM @my_stage - FILE_FORMAT = ( - type = CSV, - error_on_column_count_mismatch = 1 - ) ----------- Output --------- -error: - --> SQL:5:54 - | -1 | COPY INTO mytable - | ---- while parsing `COPY - INTO { [.] { ( ) } } - FROM { internalStage | externalStage | externalLocation | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] - [ FILES = ( '' [ , '' ] [ , ... ] ) ] - [ PATTERN = '' ] - [ VALIDATION_MODE = RETURN_ROWS ] - [ copyOptions ]` -2 | FROM @my_stage -3 | FILE_FORMAT = ( -4 | type = CSV, -5 | error_on_column_count_mismatch = 1 - | ^ unexpected `1`, expecting `TRUE` or `FALSE` - - ----------- Input ---------- -CREATE CONNECTION IF NOT EXISTS my_conn ----------- Output --------- -error: - --> SQL:1:40 - | -1 | CREATE CONNECTION IF NOT EXISTS my_conn - | ------ ^ unexpected end of line, expecting `STORAGE_TYPE` - | | - | while parsing `CREATE CONNECTION [IF NOT EXISTS] STORAGE_TYPE = ` - - diff --git a/src/query/ast/tests/it/testdata/statement.txt b/src/query/ast/tests/it/testdata/statement.txt index e2de666599ad7..58c174f33b02d 100644 --- a/src/query/ast/tests/it/testdata/statement.txt +++ b/src/query/ast/tests/it/testdata/statement.txt @@ -556,6 +556,7 @@ Explain { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -632,6 +633,7 @@ Explain { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -988,6 +990,7 @@ CreateTable( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -2047,6 +2050,7 @@ CreateView( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -2161,6 +2165,7 @@ AlterView( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -2305,6 +2310,7 @@ CreateView( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -2427,6 +2433,7 @@ AlterView( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -3066,6 +3073,7 @@ CreateTable( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -3497,6 +3505,7 @@ Query( }, ), window_list: None, + qualify: None, }, ), order_by: [], @@ -3561,6 +3570,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -3633,6 +3643,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -3733,6 +3744,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -3833,6 +3845,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [ @@ -4022,6 +4035,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -4168,6 +4182,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -4314,6 +4329,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -4460,6 +4476,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -4606,6 +4623,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -4752,6 +4770,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -4898,6 +4917,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -5044,6 +5064,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -5190,6 +5211,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -5336,6 +5358,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -5482,6 +5505,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -5583,6 +5607,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -5684,6 +5709,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -5785,6 +5811,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -5886,6 +5913,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -6048,6 +6076,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -6061,6 +6090,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -6223,6 +6253,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -6236,6 +6267,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -6398,6 +6430,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -6411,6 +6444,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -6571,6 +6605,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -6584,6 +6619,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -6693,6 +6729,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -6803,6 +6840,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -6868,6 +6906,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -6914,6 +6953,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -6960,6 +7000,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -7006,6 +7047,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -7195,6 +7237,7 @@ Insert( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -7291,6 +7334,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -12132,6 +12176,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -12229,6 +12274,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -12381,6 +12427,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -12475,6 +12522,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -12655,6 +12703,7 @@ Query( ), having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -12794,6 +12843,7 @@ Query( ), having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -12949,6 +12999,7 @@ Query( ), having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -13087,6 +13138,7 @@ Query( ), having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -13204,6 +13256,7 @@ Query( ), having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -13321,6 +13374,7 @@ Query( ), having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -14333,6 +14387,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -14380,6 +14435,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], @@ -14427,6 +14483,7 @@ Query( group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], diff --git a/src/query/sql/src/planner/binder/qualify.rs b/src/query/sql/src/planner/binder/qualify.rs index 07146102aa702..121c6f7ec51ef 100644 --- a/src/query/sql/src/planner/binder/qualify.rs +++ b/src/query/sql/src/planner/binder/qualify.rs @@ -149,10 +149,9 @@ impl<'a> QualifyChecker<'a> { Ok(LambdaFunc { span: lambda_func.span, func_name: lambda_func.func_name.clone(), - display_name: lambda_func.display_name.clone(), args, - params: lambda_func.params.clone(), lambda_expr: lambda_func.lambda_expr.clone(), + lambda_display: lambda_func.lambda_display.clone(), return_type: lambda_func.return_type.clone(), } .into()) From af369a8552abf3b6b348f6a24f774d72c65f05c7 Mon Sep 17 00:00:00 2001 From: Yijun Zhao Date: Wed, 29 Nov 2023 17:21:27 +0800 Subject: [PATCH 3/8] fix --- .../sql/src/planner/binder/copy_into_table.rs | 7 +++- src/query/sql/src/planner/binder/project.rs | 6 +++ src/query/sql/src/planner/binder/select.rs | 7 +++- src/query/sql/src/planner/dataframe.rs | 40 ++++++++++++------- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/query/sql/src/planner/binder/copy_into_table.rs b/src/query/sql/src/planner/binder/copy_into_table.rs index 0cec396b2b71f..facffa70bc67c 100644 --- a/src/query/sql/src/planner/binder/copy_into_table.rs +++ b/src/query/sql/src/planner/binder/copy_into_table.rs @@ -331,8 +331,11 @@ impl<'a> Binder { let select_list = self .normalize_select_list(&mut from_context, select_list) .await?; - let (scalar_items, projections) = - self.analyze_projection(&from_context.aggregate_info, &select_list)?; + let (scalar_items, projections) = self.analyze_projection( + &from_context.aggregate_info, + &from_context.windows, + &select_list, + )?; if projections.len() != plan.required_source_schema.num_fields() { return Err(ErrorCode::BadArguments(format!( diff --git a/src/query/sql/src/planner/binder/project.rs b/src/query/sql/src/planner/binder/project.rs index 7e10ff86fa1c9..58c4e16b6ed2e 100644 --- a/src/query/sql/src/planner/binder/project.rs +++ b/src/query/sql/src/planner/binder/project.rs @@ -40,6 +40,8 @@ use super::AggregateInfo; use crate::binder::aggregate::find_replaced_aggregate_function; use crate::binder::select::SelectItem; use crate::binder::select::SelectList; +use crate::binder::window::find_replaced_window_function; +use crate::binder::window::WindowInfo; use crate::binder::ExprContext; use crate::binder::Visibility; use crate::optimizer::SExpr; @@ -73,6 +75,7 @@ impl Binder { pub fn analyze_projection( &mut self, agg_info: &AggregateInfo, + window_info: &WindowInfo, select_list: &SelectList, ) -> Result<(HashMap, Vec)> { let mut columns = Vec::with_capacity(select_list.items.len()); @@ -94,6 +97,9 @@ impl Binder { debug_assert!(!is_grouping_sets_item); find_replaced_aggregate_function(agg_info, agg, &item.alias).unwrap() } + ScalarExpr::WindowFunction(win) => { + find_replaced_window_function(window_info, win, &item.alias).unwrap() + } _ => { self.create_derived_column_binding(item.alias.clone(), item.scalar.data_type()?) } diff --git a/src/query/sql/src/planner/binder/select.rs b/src/query/sql/src/planner/binder/select.rs index e06cbf45c09e5..eb53d45035142 100644 --- a/src/query/sql/src/planner/binder/select.rs +++ b/src/query/sql/src/planner/binder/select.rs @@ -200,8 +200,11 @@ impl Binder { }; // `analyze_projection` should behind `analyze_aggregate_select` because `analyze_aggregate_select` will rewrite `grouping`. - let (mut scalar_items, projections) = - self.analyze_projection(&from_context.aggregate_info, &select_list)?; + let (mut scalar_items, projections) = self.analyze_projection( + &from_context.aggregate_info, + &from_context.windows, + &select_list, + )?; let having = if let Some(having) = &stmt.having { Some( diff --git a/src/query/sql/src/planner/dataframe.rs b/src/query/sql/src/planner/dataframe.rs index 38a7837a72fd6..d9703625d7dd9 100644 --- a/src/query/sql/src/planner/dataframe.rs +++ b/src/query/sql/src/planner/dataframe.rs @@ -144,9 +144,11 @@ impl Dataframe { .normalize_select_list(bind_context, select_list) .await?; - let (scalar_items, projections) = self - .binder - .analyze_projection(&bind_context.aggregate_info, &select_list)?; + let (scalar_items, projections) = self.binder.analyze_projection( + &bind_context.aggregate_info, + &bind_context.windows, + &select_list, + )?; self.s_expr = self.binder.bind_projection( &mut self.bind_context, @@ -192,9 +194,11 @@ impl Dataframe { self.binder .analyze_aggregate_select(&mut self.bind_context, &mut select_list)?; - let (scalar_items, projections) = self - .binder - .analyze_projection(&self.bind_context.aggregate_info, &select_list)?; + let (scalar_items, projections) = self.binder.analyze_projection( + &self.bind_context.aggregate_info, + &self.bind_context.windows, + &select_list, + )?; self.s_expr = self .binder @@ -266,9 +270,11 @@ impl Dataframe { .await?; } - let (scalar_items, projections) = self - .binder - .analyze_projection(&self.bind_context.aggregate_info, &select_list)?; + let (scalar_items, projections) = self.binder.analyze_projection( + &self.bind_context.aggregate_info, + &self.bind_context.windows, + &select_list, + )?; self.s_expr = self.binder.bind_projection( &mut self.bind_context, @@ -305,9 +311,11 @@ impl Dataframe { .await?; self.binder .analyze_aggregate_select(&mut self.bind_context, &mut select_list)?; - let (mut scalar_items, projections) = self - .binder - .analyze_projection(&self.bind_context.aggregate_info, &select_list)?; + let (mut scalar_items, projections) = self.binder.analyze_projection( + &self.bind_context.aggregate_info, + &self.bind_context.windows, + &select_list, + )?; self.s_expr = self.binder.bind_distinct( None, &self.bind_context, @@ -387,9 +395,11 @@ impl Dataframe { .iter() .map(|item| (item.alias.clone(), item.scalar.clone())) .collect::>(); - let (mut scalar_items, projections) = self - .binder - .analyze_projection(&self.bind_context.aggregate_info, &select_list)?; + let (mut scalar_items, projections) = self.binder.analyze_projection( + &self.bind_context.aggregate_info, + &self.bind_context.windows, + &select_list, + )?; let order_items = self .binder .analyze_order_items( From 358380b54507749a552d0497b38c38f09ed41b54 Mon Sep 17 00:00:00 2001 From: Yijun Zhao Date: Wed, 29 Nov 2023 18:54:52 +0800 Subject: [PATCH 4/8] fix --- .../mode/standalone/explain/window.test | 61 +++++++++---------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/tests/sqllogictests/suites/mode/standalone/explain/window.test b/tests/sqllogictests/suites/mode/standalone/explain/window.test index 5d7fe30e5c766..14770c0dec514 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain/window.test +++ b/tests/sqllogictests/suites/mode/standalone/explain/window.test @@ -13,29 +13,25 @@ CREATE TABLE empsalary (depname string, empno bigint, salary int, enroll_date da query explain SELECT depname, empno, salary, sum(salary) OVER (PARTITION BY depname ORDER BY empno) FROM empsalary ORDER BY depname, empno ---- -EvalScalar -├── output columns: [empsalary.depname (#0), empsalary.empno (#1), empsalary.salary (#2), sum(salary) over (partition by depname order by empno) (#5)] -├── expressions: [sum(salary) OVER (PARTITION BY depname ORDER BY empno) (#4)] +Sort +├── output columns: [empsalary.depname (#0), empsalary.empno (#1), empsalary.salary (#2), sum(salary) OVER (PARTITION BY depname ORDER BY empno) (#4)] +├── sort keys: [depname ASC NULLS LAST, empno ASC NULLS LAST] ├── estimated rows: 0.00 -└── Sort +└── Window ├── output columns: [empsalary.depname (#0), empsalary.empno (#1), empsalary.salary (#2), sum(salary) OVER (PARTITION BY depname ORDER BY empno) (#4)] - ├── sort keys: [depname ASC NULLS LAST, empno ASC NULLS LAST] - ├── estimated rows: 0.00 - └── Window - ├── output columns: [empsalary.depname (#0), empsalary.empno (#1), empsalary.salary (#2), sum(salary) OVER (PARTITION BY depname ORDER BY empno) (#4)] - ├── aggregate function: [sum(salary)] - ├── partition by: [depname] - ├── order by: [empno] - ├── frame: [Range: Preceding(None) ~ CurrentRow] - └── TableScan - ├── table: default.test_explain_window.empsalary - ├── output columns: [depname (#0), empno (#1), salary (#2)] - ├── read rows: 0 - ├── read bytes: 0 - ├── partitions total: 0 - ├── partitions scanned: 0 - ├── push downs: [filters: [], limit: NONE] - └── estimated rows: 0.00 + ├── aggregate function: [sum(salary)] + ├── partition by: [depname] + ├── order by: [empno] + ├── frame: [Range: Preceding(None) ~ CurrentRow] + └── TableScan + ├── table: default.test_explain_window.empsalary + ├── output columns: [depname (#0), empno (#1), salary (#2)] + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + ├── push downs: [filters: [], limit: NONE] + └── estimated rows: 0.00 statement ok set max_threads=4; @@ -44,15 +40,14 @@ query explain pipeline SELECT depname, empno, salary, sum(salary) OVER (PARTITION BY depname ORDER BY empno) FROM empsalary ORDER BY depname, empno; ---- CompoundBlockOperator(Project) × 1 processor - CompoundBlockOperator(Map) × 1 processor - Merge (SortMergeTransform × 4 processors) to (CompoundBlockOperator(Map) × 1) - SortMergeTransform × 4 processors - SortPartialTransform × 4 processors - Merge (Transform Window × 1 processor) to (SortPartialTransform × 4) - Transform Window × 1 processor - Merge (SortMergeTransform × 4 processors) to (Transform Window × 1) - SortMergeTransform × 4 processors - SortPartialTransform × 4 processors - Merge (DeserializeDataTransform × 1 processor) to (SortPartialTransform × 4) - DeserializeDataTransform × 1 processor - SyncReadParquetDataSource × 1 processor + Merge (SortMergeTransform × 4 processors) to (CompoundBlockOperator(Project) × 1) + SortMergeTransform × 4 processors + SortPartialTransform × 4 processors + Merge (Transform Window × 1 processor) to (SortPartialTransform × 4) + Transform Window × 1 processor + Merge (SortMergeTransform × 4 processors) to (Transform Window × 1) + SortMergeTransform × 4 processors + SortPartialTransform × 4 processors + Merge (DeserializeDataTransform × 1 processor) to (SortPartialTransform × 4) + DeserializeDataTransform × 1 processor + SyncReadParquetDataSource × 1 processor From 1abca85d5383f8acf8f63f2aba1e75d06581432b Mon Sep 17 00:00:00 2001 From: Yijun Zhao Date: Wed, 29 Nov 2023 22:13:02 +0800 Subject: [PATCH 5/8] add sqllogictests --- .../query/window_function/window_qualify.test | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 tests/sqllogictests/suites/query/window_function/window_qualify.test diff --git a/tests/sqllogictests/suites/query/window_function/window_qualify.test b/tests/sqllogictests/suites/query/window_function/window_qualify.test new file mode 100644 index 0000000000000..f4336c690d3b3 --- /dev/null +++ b/tests/sqllogictests/suites/query/window_function/window_qualify.test @@ -0,0 +1,64 @@ +statement ok +CREATE DATABASE IF NOT EXISTS test_window_qualify + +statement ok +USE test_window_qualify + +statement ok +DROP TABLE IF EXISTS qt + +statement ok +CREATE TABLE qt (i INTEGER, p CHAR(1), o INTEGER) + +statement ok +INSERT INTO qt (i, p, o) VALUES (1, 'A', 1), (2, 'A', 2), (3, 'B', 1), (4, 'B', 2) + +# window function in qualify clause +query ITI +SELECT i, p, o FROM qt QUALIFY ROW_NUMBER() OVER (PARTITION BY p ORDER BY o) = 1 +---- +1 A 1 +3 B 1 + +# named window, other same as above +query ITI +SELECT i, p, o FROM qt WINDOW w AS (PARTITION BY p ORDER BY o) QUALIFY ROW_NUMBER() OVER w = 1 +---- +1 A 1 +3 B 1 + +# window function in select list +query ITII +SELECT i, p, o, ROW_NUMBER() OVER (PARTITION BY p ORDER BY o) AS rn FROM qt QUALIFY rn = 1 +---- +1 A 1 1 +3 B 1 1 + +# named window, other same as above +query ITII +SELECT i, p, o, ROW_NUMBER() OVER w AS rn FROM qt WINDOW w AS (PARTITION BY p ORDER BY o) QUALIFY rn = 1 +---- +1 A 1 1 +3 B 1 1 + +# qualify with subquery +query ITII +SELECT i, p, o, ROW_NUMBER() OVER w AS rn FROM qt WINDOW w AS (PARTITION BY p ORDER BY o) QUALIFY rn = (SELECT i FROM qt LIMIT 1) +---- +1 A 1 1 +3 B 1 1 + +# without qualify +query ITII +SELECT i, p, o, ROW_NUMBER() OVER (PARTITION BY p ORDER BY o) AS row_num FROM qt +---- +1 A 1 1 +2 A 2 2 +3 B 1 1 +4 B 2 2 + +statement ok +USE default + +statement ok +DROP DATABASE test_window_qualify \ No newline at end of file From 285e159a353306c6bc03ee2f8b9421c214d3582b Mon Sep 17 00:00:00 2001 From: Yijun Zhao Date: Thu, 30 Nov 2023 11:59:29 +0800 Subject: [PATCH 6/8] fix --- src/query/sql/src/planner/binder/qualify.rs | 207 +++++++------------- src/query/sql/src/planner/binder/select.rs | 2 +- 2 files changed, 72 insertions(+), 137 deletions(-) diff --git a/src/query/sql/src/planner/binder/qualify.rs b/src/query/sql/src/planner/binder/qualify.rs index 121c6f7ec51ef..56977cc14f7a5 100644 --- a/src/query/sql/src/planner/binder/qualify.rs +++ b/src/query/sql/src/planner/binder/qualify.rs @@ -17,7 +17,6 @@ use std::sync::Arc; use common_ast::ast::Expr; use common_exception::ErrorCode; use common_exception::Result; -use common_exception::Span; use super::Finder; use crate::binder::split_conjunctions; @@ -28,14 +27,11 @@ use crate::binder::ScalarBinder; use crate::binder::Visibility; use crate::optimizer::SExpr; use crate::planner::semantic::GroupingChecker; +use crate::plans::walk_expr_mut; use crate::plans::BoundColumnRef; -use crate::plans::CastExpr; use crate::plans::Filter; -use crate::plans::FunctionCall; -use crate::plans::LambdaFunc; use crate::plans::ScalarExpr; -use crate::plans::UDFLambdaCall; -use crate::plans::UDFServerCall; +use crate::plans::SubqueryExpr; use crate::plans::Visitor; use crate::plans::VisitorMut; use crate::BindContext; @@ -51,7 +47,6 @@ impl Binder { aliases: &[(String, ScalarExpr)], qualify: &Expr, ) -> Result { - dbg!("analyze window qualify", qualify); bind_context.set_expr_context(ExprContext::QualifyClause); let mut scalar_binder = ScalarBinder::new( bind_context, @@ -73,10 +68,8 @@ impl Binder { &mut self, bind_context: &mut BindContext, qualify: ScalarExpr, - span: Span, child: SExpr, ) -> Result { - dbg!("bind qualify", &qualify); bind_context.set_expr_context(ExprContext::QualifyClause); let f = |scalar: &ScalarExpr| matches!(scalar, ScalarExpr::AggregateFunction(_)); @@ -89,17 +82,19 @@ impl Binder { .set_span(qualify.span())); } - let scalar = if bind_context.in_grouping { - // If we are in grouping context, we will perform the grouping check - let grouping_checker = GroupingChecker::new(bind_context); - grouping_checker.resolve(&qualify, span)? - } else { - let qualify_checker = QualifyChecker::new(bind_context); - qualify_checker.resolve(&qualify)? + let scalar = { + let mut qualify = qualify; + if bind_context.in_grouping { + // If we are in grouping context, we will perform the grouping check + let mut grouping_checker = GroupingChecker::new(bind_context); + grouping_checker.visit(&mut qualify)?; + } else { + let mut qualify_checker = QualifyChecker::new(bind_context); + qualify_checker.visit(&mut qualify)?; + } + qualify }; - dbg!(&scalar); - let predicates = split_conjunctions(&scalar); let filter = Filter { predicates }; @@ -119,131 +114,71 @@ impl<'a> QualifyChecker<'a> { pub fn new(bind_context: &'a BindContext) -> Self { Self { bind_context } } +} - pub fn resolve(&self, scalar: &ScalarExpr) -> Result { - match scalar { - ScalarExpr::BoundColumnRef(_) - | ScalarExpr::ConstantExpr(_) - | ScalarExpr::SubqueryExpr(_) => Ok(scalar.clone()), - ScalarExpr::FunctionCall(func) => { - let args = func - .arguments - .iter() - .map(|arg| self.resolve(arg)) - .collect::>>()?; - Ok(FunctionCall { - span: func.span, - params: func.params.clone(), - arguments: args, - func_name: func.func_name.clone(), - } - .into()) - } - - ScalarExpr::LambdaFunction(lambda_func) => { - let args = lambda_func - .args - .iter() - .map(|arg| self.resolve(arg)) - .collect::>>()?; - Ok(LambdaFunc { - span: lambda_func.span, - func_name: lambda_func.func_name.clone(), - args, - lambda_expr: lambda_func.lambda_expr.clone(), - lambda_display: lambda_func.lambda_display.clone(), - return_type: lambda_func.return_type.clone(), +impl<'a> VisitorMut<'_> for QualifyChecker<'a> { + fn visit(&mut self, expr: &mut ScalarExpr) -> Result<()> { + if let ScalarExpr::WindowFunction(window) = expr { + if let Some(column) = self + .bind_context + .windows + .window_functions_map + .get(&window.display_name) + { + // The exprs in `win` has already been rewrittern to `BoundColumnRef` in `WindowRewriter`. + // So we need to check the exprs in `bind_context.windows` + let window_info = &self.bind_context.windows.window_functions[*column]; + + let column_binding = ColumnBindingBuilder::new( + window.display_name.clone(), + window_info.index, + Box::new(window_info.func.return_type()), + Visibility::Visible, + ) + .build(); + *expr = BoundColumnRef { + span: None, + column: column_binding, } - .into()) + .into(); + return Ok(()); } + return Err(ErrorCode::Internal( + "Qualify check: Invalid window function", + )); + } - ScalarExpr::CastExpr(cast) => Ok(CastExpr { - span: cast.span, - is_try: cast.is_try, - argument: Box::new(self.resolve(&cast.argument)?), - target_type: cast.target_type.clone(), - } - .into()), - - ScalarExpr::WindowFunction(win) => { - if let Some(column) = self - .bind_context - .windows - .window_functions_map - .get(&win.display_name) - { - // The exprs in `win` has already been rewrittern to `BoundColumnRef` in `WindowRewriter`. - // So we need to check the exprs in `bind_context.windows` - let window_info = &self.bind_context.windows.window_functions[*column]; - - let column_binding = ColumnBindingBuilder::new( - win.display_name.clone(), - window_info.index, - Box::new(window_info.func.return_type()), - Visibility::Visible, - ) - .build(); - return Ok(BoundColumnRef { - span: None, - column: column_binding, - } - .into()); + if let ScalarExpr::AggregateFunction(agg) = expr { + if let Some(column) = self + .bind_context + .aggregate_info + .aggregate_functions_map + .get(&agg.display_name) + { + let agg_func = &self.bind_context.aggregate_info.aggregate_functions[*column]; + let column_binding = ColumnBindingBuilder::new( + agg.display_name.clone(), + agg_func.index, + Box::new(agg_func.scalar.data_type()?), + Visibility::Visible, + ) + .build(); + *expr = BoundColumnRef { + span: None, + column: column_binding, } - Err(ErrorCode::Internal( - "Qualify check: Invalid window function", - )) + .into(); + return Ok(()); } - ScalarExpr::AggregateFunction(agg) => { - if let Some(column) = self - .bind_context - .aggregate_info - .aggregate_functions_map - .get(&agg.display_name) - { - let agg_func = &self.bind_context.aggregate_info.aggregate_functions[*column]; - let column_binding = ColumnBindingBuilder::new( - agg.display_name.clone(), - agg_func.index, - Box::new(agg_func.scalar.data_type()?), - Visibility::Visible, - ) - .build(); - return Ok(BoundColumnRef { - span: None, - column: column_binding, - } - .into()); - } - Err(ErrorCode::Internal("Invalid aggregate function")) - } - ScalarExpr::UDFServerCall(udf) => { - let args = udf - .arguments - .iter() - .map(|arg| self.resolve(arg)) - .collect::>>()?; - Ok(UDFServerCall { - span: udf.span, - func_name: udf.func_name.clone(), - display_name: udf.display_name.clone(), - server_addr: udf.server_addr.clone(), - arg_types: udf.arg_types.clone(), - return_type: udf.return_type.clone(), - arguments: args, - } - .into()) - } - ScalarExpr::UDFLambdaCall(udf) => { - let scalar = &udf.scalar; - let new_scalar = self.resolve(scalar)?; - Ok(UDFLambdaCall { - span: udf.span, - func_name: udf.func_name.clone(), - scalar: Box::new(new_scalar), - } - .into()) - } + return Err(ErrorCode::Internal("Invalid aggregate function")); } + + walk_expr_mut(self, expr) + } + + fn visit_subquery_expr(&mut self, _: &mut SubqueryExpr) -> Result<()> { + // TODO(leiysky): check subquery in the future + Ok(()) } } diff --git a/src/query/sql/src/planner/binder/select.rs b/src/query/sql/src/planner/binder/select.rs index eb53d45035142..4d2fef7057150 100644 --- a/src/query/sql/src/planner/binder/select.rs +++ b/src/query/sql/src/planner/binder/select.rs @@ -267,7 +267,7 @@ impl Binder { if let Some(qualify) = qualify { s_expr = self - .bind_qualify(&mut from_context, qualify, span, s_expr) + .bind_qualify(&mut from_context, qualify, s_expr) .await?; } From c367e9cf177cf1e18b7b4eaafb165e9ba03237a5 Mon Sep 17 00:00:00 2001 From: Yijun Zhao Date: Thu, 30 Nov 2023 12:45:58 +0800 Subject: [PATCH 7/8] fix ut --- src/query/ast/tests/it/testdata/query.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/query/ast/tests/it/testdata/query.txt b/src/query/ast/tests/it/testdata/query.txt index 5f896a0ea2bb1..637cd41325671 100644 --- a/src/query/ast/tests/it/testdata/query.txt +++ b/src/query/ast/tests/it/testdata/query.txt @@ -356,6 +356,7 @@ Query { group_by: None, having: None, window_list: None, + qualify: None, }, ), order_by: [], From d3eb784af3e1a582e1812073bc3dddd9385480c4 Mon Sep 17 00:00:00 2001 From: Yijun Zhao Date: Thu, 30 Nov 2023 13:48:04 +0800 Subject: [PATCH 8/8] fix --- src/query/ast/src/parser/query.rs | 3 + .../ast/tests/it/testdata/statement-error.txt | 745 ++++++++++++++++++ 2 files changed, 748 insertions(+) create mode 100644 src/query/ast/tests/it/testdata/statement-error.txt diff --git a/src/query/ast/src/parser/query.rs b/src/query/ast/src/parser/query.rs index 4c6ef9a2261ca..dbeb1f1b131a5 100644 --- a/src/query/ast/src/parser/query.rs +++ b/src/query/ast/src/parser/query.rs @@ -146,6 +146,7 @@ pub fn set_operation_element(i: Input) -> IResult> ~ ( GROUP ~ ^BY ~ ^#group_by_items )? ~ ( HAVING ~ ^#expr )? ~ ( WINDOW ~ ^#comma_separated_list1(window_clause) )? + ~ ( QUALIFY ~ ^#expr )? }, |( opt_from_block, @@ -157,6 +158,7 @@ pub fn set_operation_element(i: Input) -> IResult> opt_group_by_block, opt_having_block, opt_window_block, + opt_qualify_block, )| { SetOperationElement::SelectStmt { hints: opt_hints, @@ -171,6 +173,7 @@ pub fn set_operation_element(i: Input) -> IResult> group_by: opt_group_by_block.map(|(_, _, group_by)| group_by), having: Box::new(opt_having_block.map(|(_, having)| having)), window_list: opt_window_block.map(|(_, windows)| windows), + qualify: Box::new(opt_qualify_block.map(|(_, qualify)| qualify)), } }, ); diff --git a/src/query/ast/tests/it/testdata/statement-error.txt b/src/query/ast/tests/it/testdata/statement-error.txt new file mode 100644 index 0000000000000..87f6bb7dd94e1 --- /dev/null +++ b/src/query/ast/tests/it/testdata/statement-error.txt @@ -0,0 +1,745 @@ +---------- Input ---------- +create table a.b (c integer not null 1, b float(10)) +---------- Output --------- +error: + --> SQL:1:38 + | +1 | create table a.b (c integer not null 1, b float(10)) + | ------ ^ unexpected `1`, expecting `)`, `NULL`, `NOT`, `DEFAULT`, `GENERATED`, `AS`, `COMMENT`, or `,` + | | + | while parsing `CREATE TABLE [IF NOT EXISTS] [.]
[] []` + + +---------- Input ---------- +create table a (c float(10)) +---------- Output --------- +error: + --> SQL:1:24 + | +1 | create table a (c float(10)) + | ------ ^ unexpected `(`, expecting `)`, `NULL`, `NOT`, `DEFAULT`, `GENERATED`, `AS`, `COMMENT`, or `,` + | | + | while parsing `CREATE TABLE [IF NOT EXISTS] [.]
[] []` + + +---------- Input ---------- +create table a (c varch) +---------- Output --------- +error: + --> SQL:1:19 + | +1 | create table a (c varch) + | ------ - ^^^^^ unexpected `varch`, expecting `VARCHAR`, `CHAR`, `VARIANT`, `CHARACTER`, `VARBINARY`, `ARRAY`, `BINARY`, `MAP`, `DATE`, `STRING`, `FLOAT32`, `FLOAT64`, `DECIMAL`, `SMALLINT`, `DATETIME`, `NULLABLE`, `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT`, `DOUBLE`, `BITMAP`, `TUPLE`, `TIMESTAMP`, `TEXT`, or `JSON` + | | | + | | while parsing ` [DEFAULT ] [AS () VIRTUAL] [AS () STORED] [COMMENT '']` + | while parsing `CREATE TABLE [IF NOT EXISTS] [.]
[] []` + + +---------- Input ---------- +create table a (c tuple()) +---------- Output --------- +error: + --> SQL:1:25 + | +1 | create table a (c tuple()) + | ------ - ----- ^ unexpected `)`, expecting `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `SMALLINT`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT32`, `FLOAT`, `FLOAT64`, `DOUBLE`, `DECIMAL`, `ARRAY`, `MAP`, `BITMAP`, `TUPLE`, `DATE`, `DATETIME`, `TIMESTAMP`, `STRING`, `VARCHAR`, `CHAR`, `CHARACTER`, `TEXT`, `BINARY`, `VARBINARY`, `VARIANT`, `JSON`, `NULLABLE`, , or + | | | | + | | | while parsing type name + | | while parsing ` [DEFAULT ] [AS () VIRTUAL] [AS () STORED] [COMMENT '']` + | while parsing `CREATE TABLE [IF NOT EXISTS] [.]
[] []` + + +---------- Input ---------- +create table a (c decimal) +---------- Output --------- +error: + --> SQL:1:26 + | +1 | create table a (c decimal) + | ------ - -------^ unexpected `)`, expecting `(` + | | | | + | | | while parsing type name + | | while parsing ` [DEFAULT ] [AS () VIRTUAL] [AS () STORED] [COMMENT '']` + | while parsing `CREATE TABLE [IF NOT EXISTS] [.]
[] []` + + +---------- Input ---------- +create table a (b tuple(c int, uint64)); +---------- Output --------- +error: + --> SQL:1:38 + | +1 | create table a (b tuple(c int, uint64)); + | ------ - ----- ^ unexpected `)`, expecting `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `SMALLINT`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT32`, `FLOAT`, `FLOAT64`, `DOUBLE`, `DECIMAL`, `ARRAY`, `MAP`, `BITMAP`, `TUPLE`, `DATE`, `DATETIME`, `TIMESTAMP`, `STRING`, `VARCHAR`, `CHAR`, `CHARACTER`, `TEXT`, `BINARY`, `VARBINARY`, `VARIANT`, `JSON`, or `NULLABLE` + | | | | + | | | while parsing TUPLE( , ...) + | | | while parsing type name + | | while parsing ` [DEFAULT ] [AS () VIRTUAL] [AS () STORED] [COMMENT '']` + | while parsing `CREATE TABLE [IF NOT EXISTS] [.]
[] []` + + +---------- Input ---------- +CREATE TABLE t(c1 NULLABLE(int) NOT NULL); +---------- Output --------- +error: + --> SQL:1:41 + | +1 | CREATE TABLE t(c1 NULLABLE(int) NOT NULL); + | ------ ^ ambiguous NOT NULL constraint + | | + | while parsing `CREATE TABLE [IF NOT EXISTS] [.]
[] []` + + +---------- Input ---------- +drop table if a.b +---------- Output --------- +error: + --> SQL:1:15 + | +1 | drop table if a.b + | ---- ^ unexpected `a`, expecting `EXISTS` + | | + | while parsing `DROP TABLE [IF EXISTS] [.]
` + + +---------- Input ---------- +truncate table a.b.c.d +---------- Output --------- +error: + --> SQL:1:21 + | +1 | truncate table a.b.c.d + | ^ unexpected `.`, expecting `FORMAT` or `;` + + +---------- Input ---------- +truncate a +---------- Output --------- +error: + --> SQL:1:10 + | +1 | truncate a + | -------- ^ unexpected `a`, expecting `TABLE` + | | + | while parsing `TRUNCATE TABLE [.]
` + + +---------- Input ---------- +drop a +---------- Output --------- +error: + --> SQL:1:6 + | +1 | drop a + | ^ unexpected `a`, expecting `TASK`, `TABLE`, `MASKING`, `CATALOG`, `DATABASE`, `AGGREGATING`, `SCHEMA`, `NETWORK`, `VIEW`, `STREAM`, `VIRTUAL`, `USER`, `ROLE`, `FUNCTION`, `STAGE`, `FILE`, `SHARE`, `PIPE`, or `CONNECTION` + + +---------- Input ---------- +insert into t format +---------- Output --------- +error: + --> SQL:1:21 + | +1 | insert into t format + | ------ ^ unexpected end of line, expecting or + | | + | while parsing `INSERT INTO [TABLE]
[(, ...)] (FORMAT | VALUES | )` + + +---------- Input ---------- +show tables format +---------- Output --------- +error: + --> SQL:1:19 + | +1 | show tables format + | ^ unexpected end of line, expecting or + + +---------- Input ---------- +alter database system x rename to db +---------- Output --------- +error: + --> SQL:1:23 + | +1 | alter database system x rename to db + | ----- ^ unexpected `x`, expecting `RENAME` or `.` + | | + | while parsing `ALTER DATABASE [IF EXISTS] ` + + +---------- Input ---------- +create user 'test-e' identified bi 'password'; +---------- Output --------- +error: + --> SQL:1:33 + | +1 | create user 'test-e' identified bi 'password'; + | ^^ unexpected `bi`, expecting `BY`, `WITH`, `FORMAT`, or `;` + + +---------- Input ---------- +create user 'test-e'@'localhost' identified by 'password'; +---------- Output --------- +error: + --> SQL:1:22 + | +1 | create user 'test-e'@'localhost' identified by 'password'; + | ------ ^^^^^^^^^^^ unexpected `'localhost'`, expecting `'%'` or `IDENTIFIED` + | | + | while parsing `CREATE USER [IF NOT EXISTS] ''@'hostname' IDENTIFIED [WITH ] [BY ] [WITH , ...]` + + +---------- Input ---------- +drop usar if exists 'test-j'; +---------- Output --------- +error: + --> SQL:1:6 + | +1 | drop usar if exists 'test-j'; + | ^^^^ unexpected `usar`, expecting `USER`, `SHARE`, `STREAM`, `STAGE`, `AGGREGATING`, `ROLE`, `TABLE`, `SCHEMA`, `NETWORK`, `VIRTUAL`, `CATALOG`, `DATABASE`, `FUNCTION`, `TASK`, `MASKING`, `VIEW`, `FILE`, `PIPE`, or `CONNECTION` + + +---------- Input ---------- +alter user 'test-e' identifies by 'new-password'; +---------- Output --------- +error: + --> SQL:1:21 + | +1 | alter user 'test-e' identifies by 'new-password'; + | ^^^^^^^^^^ unexpected `identifies`, expecting `IDENTIFIED`, `WITH`, `FORMAT`, `@`, or `;` + + +---------- Input ---------- +create role 'test'@'%'; +---------- Output --------- +error: + --> SQL:1:19 + | +1 | create role 'test'@'%'; + | ^ unexpected `@`, expecting `FORMAT` or `;` + + +---------- Input ---------- +drop role 'test'@'%'; +---------- Output --------- +error: + --> SQL:1:17 + | +1 | drop role 'test'@'%'; + | ^ unexpected `@`, expecting `FORMAT` or `;` + + +---------- Input ---------- +SHOW GRANT FOR ROLE 'role1'; +---------- Output --------- +error: + --> SQL:1:6 + | +1 | SHOW GRANT FOR ROLE 'role1'; + | ^^^^^ unexpected `GRANT`, expecting `GRANTS`, `CREATE`, `NETWORK`, `STREAMS`, `CATALOGS`, `FUNCTIONS`, `DATABASES`, `CONNECTIONS`, `TABLE_FUNCTIONS`, `DROP`, `TABLE`, `ROLES`, `SHARE`, `TASKS`, `INDEXES`, `COLUMNS`, `PROCESSLIST`, `STAGES`, `TABLES`, `SHARES`, `ENGINES`, `METRICS`, `SETTINGS`, `SCHEMAS`, `FIELDS`, `USERS`, `FILE`, or `FULL` + + +---------- Input ---------- +GRANT ROLE 'test' TO ROLE test-user; +---------- Output --------- +error: + --> SQL:1:31 + | +1 | GRANT ROLE 'test' TO ROLE test-user; + | ^ unexpected `-`, expecting `FORMAT` or `;` + + +---------- Input ---------- +GRANT SELECT, ALL PRIVILEGES, CREATE ON * TO 'test-grant'; +---------- Output --------- +error: + --> SQL:1:15 + | +1 | GRANT SELECT, ALL PRIVILEGES, CREATE ON * TO 'test-grant'; + | ----- ------ ^^^ unexpected `ALL`, expecting `ALTER`, `SELECT`, `DELETE`, `USAGE`, `INSERT`, `UPDATE`, `SUPER`, `CREATE`, `DROP`, `GRANT`, `SET`, or `OWNERSHIP` + | | | + | | while parsing ON + | while parsing `GRANT { ROLE | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON } TO { [ROLE ] | [USER] }` + + +---------- Input ---------- +GRANT SELECT, CREATE ON *.c TO 'test-grant'; +---------- Output --------- +error: + --> SQL:1:27 + | +1 | GRANT SELECT, CREATE ON *.c TO 'test-grant'; + | ----- ^ unexpected `c`, expecting `TO` or `*` + | | + | while parsing `GRANT { ROLE | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON } TO { [ROLE ] | [USER] }` + + +---------- Input ---------- +GRANT select ON UDF a TO 'test-grant'; +---------- Output --------- +error: + --> SQL:1:21 + | +1 | GRANT select ON UDF a TO 'test-grant'; + | ----- ^ unexpected `a`, expecting `TO` or `.` + | | + | while parsing `GRANT { ROLE | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON } TO { [ROLE ] | [USER] }` + + +---------- Input ---------- +REVOKE SELECT, CREATE, ALL PRIVILEGES ON * FROM 'test-grant'; +---------- Output --------- +error: + --> SQL:1:24 + | +1 | REVOKE SELECT, CREATE, ALL PRIVILEGES ON * FROM 'test-grant'; + | ------ ------ ^^^ unexpected `ALL`, expecting `ALTER`, `SELECT`, `DELETE`, `USAGE`, `INSERT`, `UPDATE`, `SUPER`, `CREATE`, `DROP`, `GRANT`, `SET`, or `OWNERSHIP` + | | | + | | while parsing ON + | while parsing `REVOKE { ROLE | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON } FROM { [ROLE ] | [USER] }` + + +---------- Input ---------- +REVOKE SELECT, CREATE ON * TO 'test-grant'; +---------- Output --------- +error: + --> SQL:1:28 + | +1 | REVOKE SELECT, CREATE ON * TO 'test-grant'; + | ------ ^^ unexpected `TO`, expecting `FROM` or `.` + | | + | while parsing `REVOKE { ROLE | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON } FROM { [ROLE ] | [USER] }` + + +---------- Input ---------- +COPY INTO mytable FROM 's3://bucket' CREDENTIAL = (); +---------- Output --------- +error: + --> SQL:1:38 + | +1 | COPY INTO mytable FROM 's3://bucket' CREDENTIAL = (); + | ^^^^^^^^^^ unexpected `CREDENTIAL`, expecting `CREDENTIALS`, `DISABLE_VARIANT_CHECK`, `RETURN_FAILED_ONLY`, `CONNECTION`, `PURGE`, `VALIDATION_MODE`, `FORCE`, `LOCATION_PREFIX`, `FORMAT`, `PATTERN`, `FILES`, `MAX_FILES`, `SIZE_LIMIT`, `FILE_FORMAT`, `ON_ERROR`, `SPLIT_SIZE`, or `;` + + +---------- Input ---------- +COPY INTO mytable FROM @mystage CREDENTIALS = (); +---------- Output --------- +error: + --> SQL:1:33 + | +1 | COPY INTO mytable FROM @mystage CREDENTIALS = (); + | ^^^^^^^^^^^ unexpected `CREDENTIALS`, expecting `DISABLE_VARIANT_CHECK`, `RETURN_FAILED_ONLY`, `MAX_FILES`, `PURGE`, `VALIDATION_MODE`, `FORCE`, `FORMAT`, `PATTERN`, `FILES`, `SIZE_LIMIT`, `SPLIT_SIZE`, `FILE_FORMAT`, `ON_ERROR`, or `;` + + +---------- Input ---------- +CALL system$test +---------- Output --------- +error: + --> SQL:1:17 + | +1 | CALL system$test + | ---- ^ unexpected end of line, expecting `(` + | | + | while parsing `CALL (, ...)` + + +---------- Input ---------- +CALL system$test(a +---------- Output --------- +error: + --> SQL:1:19 + | +1 | CALL system$test(a + | ---- ^ unexpected end of line, expecting `)` or `,` + | | + | while parsing `CALL (, ...)` + + +---------- Input ---------- +show settings ilike 'enable%' +---------- Output --------- +error: + --> SQL:1:15 + | +1 | show settings ilike 'enable%' + | ^^^^^ unexpected `ilike`, expecting `LIKE`, `LIMIT`, `WHERE`, `FORMAT`, or `;` + + +---------- Input ---------- +PRESIGN INVALID @my_stage/path/to/file +---------- Output --------- +error: + --> SQL:1:9 + | +1 | PRESIGN INVALID @my_stage/path/to/file + | ------- ^^^^^^^ unexpected `INVALID`, expecting `DOWNLOAD`, `AtString`, `UPLOAD`, or + | | + | while parsing `PRESIGN [{DOWNLOAD | UPLOAD}] [EXPIRE = 3600]` + + +---------- Input ---------- +SELECT c a as FROM t +---------- Output --------- +error: + --> SQL:1:12 + | +1 | SELECT c a as FROM t + | ^^ an alias without `AS` keyword has already been defined before this one, please remove one of them + + +---------- Input ---------- +SELECT c a as b FROM t +---------- Output --------- +error: + --> SQL:1:12 + | +1 | SELECT c a as b FROM t + | ^^ an alias without `AS` keyword has already been defined before this one, please remove one of them + + +---------- Input ---------- +SELECT * FROM t GROUP BY GROUPING SETS a, b +---------- Output --------- +error: + --> SQL:1:35 + | +1 | SELECT * FROM t GROUP BY GROUPING SETS a, b + | ^^^^ unexpected `SETS`, expecting `SELECT`, `INTERSECT`, `WITH`, `EXCEPT`, `VALUES`, `OFFSET`, `IGNORE_RESULT`, `,`, `HAVING`, `WINDOW`, `QUALIFY`, `(`, `UNION`, `FROM`, `ORDER`, `LIMIT`, `FORMAT`, or `;` + + +---------- Input ---------- +SELECT * FROM t GROUP BY GROUPING SETS () +---------- Output --------- +error: + --> SQL:1:41 + | +1 | SELECT * FROM t GROUP BY GROUPING SETS () + | ------ ^ unexpected `)`, expecting `(`, `IS`, `IN`, `EXISTS`, `BETWEEN`, `+`, `-`, `*`, `/`, `//`, `DIV`, `%`, `||`, `<->`, `>`, `<`, `>=`, `<=`, `=`, `<>`, `!=`, `^`, `AND`, `OR`, `XOR`, `LIKE`, `NOT`, `REGEXP`, `RLIKE`, `SOUNDS`, , , , , , `->`, `->>`, `#>`, `#>>`, `?`, `?|`, `?&`, `@>`, `<@`, , , , , , `CAST`, `TRY_CAST`, `DATE_ADD`, `DATE_SUB`, `DATE_TRUNC`, `DATE`, `TIMESTAMP`, `INTERVAL`, `::`, `EXTRACT`, `DATE_PART`, or 24 more ... + | | + | while parsing `SELECT ...` + + +---------- Input ---------- +select * from aa.bb limit 10 order by bb; +---------- Output --------- +error: + --> SQL:1:30 + | +1 | select * from aa.bb limit 10 order by bb; + | ------ ^^^^^ ORDER BY must appear before LIMIT + | | + | while parsing `SELECT ...` + + +---------- Input ---------- +select * from aa.bb offset 10 order by bb; +---------- Output --------- +error: + --> SQL:1:31 + | +1 | select * from aa.bb offset 10 order by bb; + | ------ ^^^^^ ORDER BY must appear before OFFSET + | | + | while parsing `SELECT ...` + + +---------- Input ---------- +select * from aa.bb offset 10 limit 1; +---------- Output --------- +error: + --> SQL:1:31 + | +1 | select * from aa.bb offset 10 limit 1; + | ------ ^^^^^ LIMIT must appear before OFFSET + | | + | while parsing `SELECT ...` + + +---------- Input ---------- +select * from aa.bb order by a order by b; +---------- Output --------- +error: + --> SQL:1:32 + | +1 | select * from aa.bb order by a order by b; + | ------ ^^^^^ duplicated ORDER BY clause + | | + | while parsing `SELECT ...` + + +---------- Input ---------- +select * from aa.bb offset 10 offset 20; +---------- Output --------- +error: + --> SQL:1:31 + | +1 | select * from aa.bb offset 10 offset 20; + | ------ ^^^^^^ duplicated OFFSET clause + | | + | while parsing `SELECT ...` + + +---------- Input ---------- +select * from aa.bb limit 10 limit 20; +---------- Output --------- +error: + --> SQL:1:30 + | +1 | select * from aa.bb limit 10 limit 20; + | ------ ^^^^^ duplicated LIMIT clause + | | + | while parsing `SELECT ...` + + +---------- Input ---------- +select * from aa.bb limit 10,2 offset 2; +---------- Output --------- +error: + --> SQL:1:32 + | +1 | select * from aa.bb limit 10,2 offset 2; + | ------ ^^^^^^ LIMIT n,m should not appear OFFSET + | | + | while parsing `SELECT ...` + + +---------- Input ---------- +select * from aa.bb limit 10,2,3; +---------- Output --------- +error: + --> SQL:1:21 + | +1 | select * from aa.bb limit 10,2,3; + | ------ ^^^^^ [LIMIT n OFFSET m] or [LIMIT n,m] + | | + | while parsing `SELECT ...` + + +---------- Input ---------- +with a as (select 1) with b as (select 2) select * from aa.bb; +---------- Output --------- +error: + --> SQL:1:43 + | +1 | with a as (select 1) with b as (select 2) select * from aa.bb; + | ---- ^^^^^^ duplicated WITH clause + | | + | while parsing `SELECT ...` + + +---------- Input ---------- +with as t2(tt) as (select a from t) select t2.tt from t2 +---------- Output --------- +error: + --> SQL:1:6 + | +1 | with as t2(tt) as (select a from t) select t2.tt from t2 + | ---- ^^ unexpected `as`, expecting , , or `RECURSIVE` + | | + | while parsing `SELECT ...` + + +---------- Input ---------- +copy into t1 from "" FILE +---------- Output --------- +error: + --> SQL:1:19 + | +1 | copy into t1 from "" FILE + | ---- ^^ unexpected `""`, expecting , `AtString`, or `(` + | | + | while parsing `COPY + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] + [ FILES = ( '' [ , '' ] [ , ... ] ) ] + [ PATTERN = '' ] + [ VALIDATION_MODE = RETURN_ROWS ] + [ copyOptions ]` + + +---------- Input ---------- +select $1 from @data/csv/books.csv (file_format => 'aa' bad_arg => 'x', pattern => 'bb') +---------- Output --------- +error: + --> SQL:1:57 + | +1 | select $1 from @data/csv/books.csv (file_format => 'aa' bad_arg => 'x', pattern => 'bb') + | ------ ^^^^^^^ unexpected `bad_arg`, expecting `PATTERN`, `FILE_FORMAT`, `)`, `,`, `FILES`, or `CONNECTION` + | | + | while parsing `SELECT ...` + + +---------- Input ---------- +copy into t1 from "" FILE_FORMAT +---------- Output --------- +error: + --> SQL:1:19 + | +1 | copy into t1 from "" FILE_FORMAT + | ---- ^^ unexpected `""`, expecting , `AtString`, or `(` + | | + | while parsing `COPY + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] + [ FILES = ( '' [ , '' ] [ , ... ] ) ] + [ PATTERN = '' ] + [ VALIDATION_MODE = RETURN_ROWS ] + [ copyOptions ]` + + +---------- Input ---------- +copy into t1 from "" FILE_FORMAT = +---------- Output --------- +error: + --> SQL:1:19 + | +1 | copy into t1 from "" FILE_FORMAT = + | ---- ^^ unexpected `""`, expecting , `AtString`, or `(` + | | + | while parsing `COPY + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] + [ FILES = ( '' [ , '' ] [ , ... ] ) ] + [ PATTERN = '' ] + [ VALIDATION_MODE = RETURN_ROWS ] + [ copyOptions ]` + + +---------- Input ---------- +copy into t1 from "" FILE_FORMAT = ( +---------- Output --------- +error: + --> SQL:1:19 + | +1 | copy into t1 from "" FILE_FORMAT = ( + | ---- ^^ unexpected `""`, expecting , `AtString`, or `(` + | | + | while parsing `COPY + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] + [ FILES = ( '' [ , '' ] [ , ... ] ) ] + [ PATTERN = '' ] + [ VALIDATION_MODE = RETURN_ROWS ] + [ copyOptions ]` + + +---------- Input ---------- +copy into t1 from "" FILE_FORMAT = (TYPE +---------- Output --------- +error: + --> SQL:1:19 + | +1 | copy into t1 from "" FILE_FORMAT = (TYPE + | ---- ^^ unexpected `""`, expecting , `AtString`, or `(` + | | + | while parsing `COPY + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] + [ FILES = ( '' [ , '' ] [ , ... ] ) ] + [ PATTERN = '' ] + [ VALIDATION_MODE = RETURN_ROWS ] + [ copyOptions ]` + + +---------- Input ---------- +copy into t1 from "" FILE_FORMAT = (TYPE = +---------- Output --------- +error: + --> SQL:1:19 + | +1 | copy into t1 from "" FILE_FORMAT = (TYPE = + | ---- ^^ unexpected `""`, expecting , `AtString`, or `(` + | | + | while parsing `COPY + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] + [ FILES = ( '' [ , '' ] [ , ... ] ) ] + [ PATTERN = '' ] + [ VALIDATION_MODE = RETURN_ROWS ] + [ copyOptions ]` + + +---------- Input ---------- +copy into t1 from "" FILE_FORMAT = (TYPE = +---------- Output --------- +error: + --> SQL:1:19 + | +1 | copy into t1 from "" FILE_FORMAT = (TYPE = + | ---- ^^ unexpected `""`, expecting , `AtString`, or `(` + | | + | while parsing `COPY + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] + [ FILES = ( '' [ , '' ] [ , ... ] ) ] + [ PATTERN = '' ] + [ VALIDATION_MODE = RETURN_ROWS ] + [ copyOptions ]` + + +---------- Input ---------- +COPY INTO t1 FROM "" PATTERN = '.*[.]csv' FILE_FORMAT = (type = TSV field_delimiter = '\t' skip_headerx = 0); +---------- Output --------- +error: + --> SQL:1:19 + | +1 | COPY INTO t1 FROM "" PATTERN = '.*[.]csv' FILE_FORMAT = (type = TSV field_delimiter = '\t' skip_headerx = 0); + | ---- ^^ unexpected `""`, expecting , `AtString`, or `(` + | | + | while parsing `COPY + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] + [ FILES = ( '' [ , '' ] [ , ... ] ) ] + [ PATTERN = '' ] + [ VALIDATION_MODE = RETURN_ROWS ] + [ copyOptions ]` + + +---------- Input ---------- +COPY INTO mytable + FROM @my_stage + FILE_FORMAT = ( + type = CSV, + error_on_column_count_mismatch = 1 + ) +---------- Output --------- +error: + --> SQL:5:54 + | +1 | COPY INTO mytable + | ---- while parsing `COPY + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] + [ FILES = ( '' [ , '' ] [ , ... ] ) ] + [ PATTERN = '' ] + [ VALIDATION_MODE = RETURN_ROWS ] + [ copyOptions ]` +2 | FROM @my_stage +3 | FILE_FORMAT = ( +4 | type = CSV, +5 | error_on_column_count_mismatch = 1 + | ^ unexpected `1`, expecting `TRUE` or `FALSE` + + +---------- Input ---------- +CREATE CONNECTION IF NOT EXISTS my_conn +---------- Output --------- +error: + --> SQL:1:40 + | +1 | CREATE CONNECTION IF NOT EXISTS my_conn + | ------ ^ unexpected end of line, expecting `STORAGE_TYPE` + | | + | while parsing `CREATE CONNECTION [IF NOT EXISTS] STORAGE_TYPE = ` + +