diff --git a/sbroad-core/src/frontend/sql.rs b/sbroad-core/src/frontend/sql.rs index c76de1c43f5fb911ccc865d1898bacc228dca6a0..266a56642e63e8c4b5ab2519adf2978863c17c02 100644 --- a/sbroad-core/src/frontend/sql.rs +++ b/sbroad-core/src/frontend/sql.rs @@ -1201,63 +1201,6 @@ fn parse_option<M: Metadata>( Ok(value) } -fn parse_cast_expr<M: Metadata>( - pair: Pair<Rule>, - referred_relation_ids: &[usize], - worker: &mut ExpressionsWorker<M>, - plan: &mut Plan, -) -> Result<ParseExpression, SbroadError> { - let mut inner_pairs = pair.into_inner(); - let expr_pair = inner_pairs.next().expect("Cast has no expr child."); - let child_parse_expr = - parse_expr_pratt(expr_pair.into_inner(), referred_relation_ids, worker, plan)?; - - let mut cast_types = Vec::with_capacity(inner_pairs.len()); - for type_pairs in inner_pairs { - let cast_type = if type_pairs.as_rule() == Rule::ColumnDefType { - let mut column_def_type_pairs = type_pairs.into_inner(); - let column_def_type = column_def_type_pairs - .next() - .expect("concrete type expected under ColumnDefType"); - if column_def_type.as_rule() == Rule::TypeVarchar { - let mut type_pairs_inner = column_def_type.into_inner(); - let varchar_length = type_pairs_inner - .next() - .expect("Length is missing under Varchar"); - let len = varchar_length.as_str().parse::<usize>().map_err(|e| { - SbroadError::ParsingError( - Entity::Value, - format_smolstr!("failed to parse varchar length: {e:?}"), - ) - })?; - Ok(CastType::Varchar(len)) - } else { - CastType::try_from(&column_def_type.as_rule()) - } - } else { - // TypeAny. - CastType::try_from(&type_pairs.as_rule()) - }?; - - cast_types.push(cast_type); - } - - assert!(!cast_types.is_empty(), "cast expression has no cast types"); - - if let ParseExpression::PlanId { plan_id } = child_parse_expr { - let node = plan.get_mut_node(plan_id)?; - if let Node::Parameter(..) = node { - // Assign parameter type from the cast, just like Postgres. - *node = Node::Parameter(Some(cast_types[0].as_relation_type())); - } - } - - Ok(ParseExpression::Cast { - cast_types, - child: Box::new(child_parse_expr), - }) -} - enum ParameterSource<'parameter> { AstNode { ast: &'parameter AbstractSyntaxTree, @@ -1372,7 +1315,7 @@ fn parse_param<M: Metadata>( lazy_static::lazy_static! { static ref PRATT_PARSER: PrattParser<Rule> = { use pest::pratt_parser::{Assoc::{Left, Right}, Op}; - use Rule::{Add, And, Between, ConcatInfixOp, Divide, Eq, Gt, GtEq, In, IsNullPostfix, Lt, LtEq, Multiply, NotEq, Or, Subtract, UnaryNot}; + use Rule::{Add, And, Between, ConcatInfixOp, Divide, Eq, Gt, GtEq, In, IsNullPostfix, CastPostfix, Lt, LtEq, Multiply, NotEq, Or, Subtract, UnaryNot}; // Precedence is defined lowest to highest. PrattParser::new() @@ -1388,6 +1331,7 @@ lazy_static::lazy_static! { .op(Op::infix(Add, Left) | Op::infix(Subtract, Left)) .op(Op::infix(Multiply, Left) | Op::infix(Divide, Left) | Op::infix(ConcatInfixOp, Left)) .op(Op::postfix(IsNullPostfix)) + .op(Op::postfix(CastPostfix)) }; } @@ -1527,7 +1471,7 @@ enum ParseExpression { child: Box<ParseExpression>, }, Cast { - cast_types: Vec<CastType>, + cast_type: CastType, child: Box<ParseExpression>, }, Case { @@ -1643,12 +1587,9 @@ impl ParseExpression { plan.add_covered_with_parentheses(child_plan_id) } } - ParseExpression::Cast { cast_types, child } => { - let mut child_plan_id = child.populate_plan(plan, worker)?; - for cast_type in cast_types { - child_plan_id = plan.add_cast(child_plan_id, cast_type.clone())?; - } - child_plan_id + ParseExpression::Cast { cast_type, child } => { + let child_plan_id = child.populate_plan(plan, worker)?; + plan.add_cast(child_plan_id, *cast_type)? } ParseExpression::Case { search_expr, @@ -1953,6 +1894,33 @@ fn find_interim_between(mut expr: &mut ParseExpression) -> Option<(&mut ParseExp } } +fn cast_type_from_pair(type_pair: Pair<Rule>) -> Result<CastType, SbroadError> { + if type_pair.as_rule() != Rule::ColumnDefType { + // TypeAny. + return CastType::try_from(&type_pair.as_rule()); + } + + let mut column_def_type_pairs = type_pair.into_inner(); + let column_def_type = column_def_type_pairs + .next() + .expect("concrete type expected under ColumnDefType"); + if column_def_type.as_rule() != Rule::TypeVarchar { + return CastType::try_from(&column_def_type.as_rule()); + } + + let mut type_pairs_inner = column_def_type.into_inner(); + let varchar_length = type_pairs_inner + .next() + .expect("Length is missing under Varchar"); + let len = varchar_length.as_str().parse::<usize>().map_err(|e| { + SbroadError::ParsingError( + Entity::Value, + format_smolstr!("Failed to parse varchar length: {e:?}."), + ) + })?; + Ok(CastType::Varchar(len)) +} + /// Function responsible for parsing expressions using Pratt parser. /// /// Parameters: @@ -2202,8 +2170,19 @@ where ParseExpression::Exists { is_not: first_is_not, child: Box::new(child_parse_expr)} } Rule::Trim => parse_trim(primary, referred_relation_ids, worker, plan)?, - Rule::CastOp | Rule::CastExpr => { - parse_cast_expr(primary, referred_relation_ids, worker, plan)? + Rule::CastOp => { + let mut inner_pairs = primary.into_inner(); + let expr_pair = inner_pairs.next().expect("Cast has no expr child."); + let child_parse_expr = parse_expr_pratt( + expr_pair.into_inner(), + referred_relation_ids, + worker, + plan + )?; + let type_pair = inner_pairs.next().expect("CastOp has no type child"); + let cast_type = cast_type_from_pair(type_pair)?; + + ParseExpression::Cast { cast_type, child: Box::new(child_parse_expr) } } Rule::Case => { let mut inner_pairs = primary.into_inner(); @@ -2358,14 +2337,21 @@ where Ok(ParseExpression::Prefix { op, child: Box::new(child?)}) }) .map_postfix(|child, op| { + let child = child?; match op.as_rule() { + Rule::CastPostfix => { + let ty_pair = op.into_inner().next() + .expect("Expected ColumnDefType under CastPostfix."); + let cast_type = cast_type_from_pair(ty_pair)?; + Ok(ParseExpression::Cast { child: Box::new(child), cast_type }) + } Rule::IsNullPostfix => { let is_not = match op.into_inner().len() { 1 => true, 0 => false, _ => unreachable!("IsNull must have 0 or 1 children") }; - Ok(ParseExpression::IsNull { is_not, child: Box::new(child?)}) + Ok(ParseExpression::IsNull { is_not, child: Box::new(child)}) }, rule => unreachable!("Expr::parse expected postfix operator, found {:?}", rule), } diff --git a/sbroad-core/src/frontend/sql/query.pest b/sbroad-core/src/frontend/sql/query.pest index 65a9e16a811cd4b3ee076216bef04921b3d08348..151a4bd00f5e492f3ab7073313d7e9d4eaf0a6d9 100644 --- a/sbroad-core/src/frontend/sql/query.pest +++ b/sbroad-core/src/frontend/sql/query.pest @@ -267,8 +267,9 @@ Expr = { ExprAtomValue ~ (ExprInfixOp ~ ExprAtomValue)* } LtEq = { "<=" } NotEq = { "<>" | "!=" } In = { NotFlag? ~ ^"in" } - ExprAtomValue = _{ CastExpr | (UnaryNot* ~ AtomicExpr ~ IsNullPostfix?) } + ExprAtomValue = _{ UnaryNot* ~ AtomicExpr ~ CastPostfix* ~ IsNullPostfix? } UnaryNot = @{ NotFlag } + CastPostfix = { "::" ~ ColumnDefType } IsNullPostfix = { ^"is" ~ NotFlag? ~ ^"null" } AtomicExpr = _{ Literal | Parameter | CastOp | Trim | CurrentDate | IdentifierWithOptionalContinuation | ExpressionInParentheses | UnaryOperator | Case | SubQuery | Row } Literal = { True | False | Null | Double | Decimal | Unsigned | Integer | SingleQuotedString } @@ -330,10 +331,6 @@ Expr = { ExprAtomValue ~ (ExprInfixOp ~ ExprAtomValue)* } UnaryOperator = _{ Exists } Exists = { NotFlag? ~ ^"exists" ~ SubQuery } Row = { "(" ~ Expr ~ ("," ~ Expr)* ~ ")" } - CastExpr = { AtomicExprWrapped ~ ("::" ~ ColumnDefType)+ } - // In CastOp rule, AtomicExpr is stored as a child of Expr, so we try to imitate - // this in order parse CastExpr and CastOp rules in the same way. - AtomicExprWrapped = { AtomicExpr } Distinct = { ^"distinct" } NotFlag = { ^"not" } diff --git a/sbroad-core/src/ir/explain.rs b/sbroad-core/src/ir/explain.rs index c832e3bc038e875c3f336dfb1671e5e0435c18ca..da65e2d744421bb9b3c2b55f742f9f1306132160 100644 --- a/sbroad-core/src/ir/explain.rs +++ b/sbroad-core/src/ir/explain.rs @@ -135,7 +135,7 @@ impl ColExpr { "stack is empty while processing CAST expression".to_smolstr(), ) })?; - let cast_expr = ColExpr::Cast(Box::new(expr), to.clone()); + let cast_expr = ColExpr::Cast(Box::new(expr), *to); stack.push((cast_expr, id)); } Expression::Case { diff --git a/sbroad-core/src/ir/expression/cast.rs b/sbroad-core/src/ir/expression/cast.rs index 807c364c71781b3ce9755c4c25567d2a2ddc398c..4005fad34298bdc0bc59da625bf448850622f8d4 100644 --- a/sbroad-core/src/ir/expression/cast.rs +++ b/sbroad-core/src/ir/expression/cast.rs @@ -8,7 +8,7 @@ use crate::ir::{Node, Plan}; use serde::{Deserialize, Serialize}; use smol_str::{format_smolstr, SmolStr, ToSmolStr}; -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)] pub enum Type { Any, Map, @@ -136,6 +136,12 @@ impl Plan { to: to_type, }; let cast_id = self.nodes.push(Node::Expression(cast_expr)); + + let child_plan_node = self.get_mut_node(expr_id)?; + if let Node::Parameter(ref mut ty) = child_plan_node { + *ty = Some(to_type.as_relation_type()); + } + Ok(cast_id) } }