diff --git a/sbroad/sbroad-core/src/frontend/sql.rs b/sbroad/sbroad-core/src/frontend/sql.rs index f063cda6b854d7bf0918b78acf918e2f9b196221..d0ecb2b22e98ccebd6091386db96054f223ed764 100644 --- a/sbroad/sbroad-core/src/frontend/sql.rs +++ b/sbroad/sbroad-core/src/frontend/sql.rs @@ -2395,16 +2395,22 @@ fn cast_type_from_pair(type_pair: Pair<Rule>) -> Result<CastType, SbroadError> { } 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)) + let type_cast = type_pairs_inner.next().map_or_else( + || Ok(CastType::Text), + |varchar_length| { + varchar_length + .as_str() + .parse::<usize>() + .map(CastType::Varchar) + .map_err(|e| { + SbroadError::ParsingError( + Entity::Value, + format_smolstr!("Failed to parse varchar length: {e:?}."), + ) + }) + }, + )?; + Ok(type_cast) } /// Function responsible for parsing expressions using Pratt parser. diff --git a/sbroad/sbroad-core/src/frontend/sql/query.pest b/sbroad/sbroad-core/src/frontend/sql/query.pest index 5b6554b4319c483675f0275a67624abfe79dfae5..21648aaf6f164ad48fc8bae72e2464ef401e04a9 100644 --- a/sbroad/sbroad-core/src/frontend/sql/query.pest +++ b/sbroad/sbroad-core/src/frontend/sql/query.pest @@ -414,7 +414,7 @@ Expr = ${ ExprAtomValue ~ (ExprInfixOpo ~ ExprAtomValue)* } TypeText = { ^"text" } TypeUuid = { ^"uuid" } TypeUnsigned = { ^"unsigned" } - TypeVarchar = !{ ^"varchar" ~ "(" ~ Unsigned ~ ")" } + TypeVarchar = { ^"varchar" ~ ("(" ~ WO ~ Unsigned ~ WO ~ ")")? } UnaryOperator = _{ Exists } Exists = ${ (NotFlag ~ W)? ~ ^"exists" ~ W ~ SubQuery } Row = !{ "(" ~ Expr ~ ("," ~ Expr)* ~ ")" } diff --git a/sbroad/sbroad-core/src/ir/expression/cast.rs b/sbroad/sbroad-core/src/ir/expression/cast.rs index 92a5da90ed28fbb8db6d5407ae2a123b8fd9323e..e6200123770f8699643835a42e54fb27fadd872e 100644 --- a/sbroad/sbroad-core/src/ir/expression/cast.rs +++ b/sbroad/sbroad-core/src/ir/expression/cast.rs @@ -97,7 +97,10 @@ impl From<&Type> for SmolStr { Type::Text => "text".to_smolstr(), Type::Uuid => "uuid".to_smolstr(), Type::Unsigned => "unsigned".to_smolstr(), - Type::Varchar(length) => format_smolstr!("varchar({length})"), + Type::Varchar(length) => match length { + 0 => "varchar".to_smolstr(), + _ => format_smolstr!("varchar({length})"), + }, } } } diff --git a/test/int/test_sql.py b/test/int/test_sql.py index 332891baf511e290c2cb0056e53e1a887f79f3e2..be10ce89f9864d3ccc70306192132df960d5b402 100644 --- a/test/int/test_sql.py +++ b/test/int/test_sql.py @@ -5928,3 +5928,37 @@ Exceeded maximum number of rows (1) in virtual table: 2""" i1.sql( f"SELECT * FROM (VALUES (1), (2)) OPTION (VTABLE_MAX_ROWS = {new_vtable_max_rows})" ) + + +def test_varchar_cast(cluster: Cluster): + cluster.deploy(instance_count=1) + i1 = cluster.instances[0] + + ddl = i1.sql( + """ + create table t (a varchar not null, primary key (a)) + using memtx + distributed by (a) + """ + ) + assert ddl["row_count"] == 1 + + dml = i1.sql("""insert into t values ('test_string')""") + assert dml["row_count"] == 1 + + data = i1.sql("""select cast(a as varchar(20)) from t""") + assert data == [["test_string"]] + + # VARCHAR cast without length + data = i1.sql("""select cast(a as varchar) from t""") + assert data == [["test_string"]] + + # VARCHAR cast with string literal + data = i1.sql("""select cast('direct_string' as varchar)""") + assert data == [["direct_string"]] + + data = i1.sql( + """select cast(a as varchar) from t where a = 'test_string'""", + strip_metadata=False, + ) + assert data["metadata"] == [{"name": "col_1", "type": "string"}]