Skip to content

Commit 5566414

Browse files
committed
chore: fix ci fail
Signed-off-by: Kould <[email protected]>
1 parent 043493b commit 5566414

File tree

3 files changed

+137
-27
lines changed

3 files changed

+137
-27
lines changed

src/common/exception/src/exception_into.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
use std::error::Error;
16+
use std::fmt;
1617
use std::fmt::Debug;
1718
use std::fmt::Display;
1819
use std::fmt::Formatter;
@@ -430,3 +431,9 @@ impl From<redis::RedisError> for ErrorCode {
430431
ErrorCode::DictionarySourceError(format!("Dictionary Redis Error, cause: {}", error))
431432
}
432433
}
434+
435+
impl From<fmt::Error> for ErrorCode {
436+
fn from(error: fmt::Error) -> Self {
437+
ErrorCode::from_std_error(error)
438+
}
439+
}

src/query/sql/src/planner/binder/bind_table_reference/bind_table.rs

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
use std::fmt::Write;
1516
use std::sync::Arc;
1617

1718
use databend_common_ast::ast::Identifier;
@@ -402,12 +403,13 @@ impl Binder {
402403
if index == 0 {
403404
let is_created = true;
404405

406+
let path = Self::convert_array_index(&virtual_field.name)?;
405407
index = self.metadata.write().add_virtual_column(
406408
&base_column,
407409
column_id,
408410
name.clone(),
409411
table_data_type,
410-
Scalar::String(name.clone()),
412+
Scalar::String(path),
411413
None,
412414
is_created,
413415
);
@@ -422,4 +424,90 @@ impl Binder {
422424

423425
Ok(())
424426
}
427+
428+
fn convert_array_index(input: &str) -> Result<String> {
429+
let mut chars = input.chars().peekable();
430+
let mut path = "{".to_string();
431+
432+
while chars.peek().is_some() {
433+
if chars.next() != Some('[') {
434+
return Err(ErrorCode::InvalidArgument("Expected '['".to_string()));
435+
}
436+
if let Some('\'') = chars.peek() {
437+
let _ = chars.next();
438+
path.push('"');
439+
440+
let mut is_emtpy = true;
441+
for c in chars.by_ref() {
442+
is_emtpy = false;
443+
if c == '\'' {
444+
break;
445+
}
446+
path.write_char(c)?;
447+
}
448+
if is_emtpy {
449+
let _ = path.pop();
450+
} else {
451+
path.push('"')
452+
}
453+
if chars.next() != Some(']') {
454+
return Err(ErrorCode::InvalidArgument("Expected ']' after string"));
455+
}
456+
} else {
457+
while let Some(&c) = chars.peek() {
458+
if c == ']' {
459+
break;
460+
}
461+
path.write_char(c)?;
462+
chars.next();
463+
}
464+
if chars.next() != Some(']') {
465+
return Err(ErrorCode::InvalidArgument(
466+
"Expected ']' after index".to_string(),
467+
));
468+
}
469+
};
470+
471+
path.write_char(',')?;
472+
}
473+
if path.len() > 1 {
474+
let _ = path.pop();
475+
}
476+
path.write_char('}')?;
477+
478+
if chars.next().is_some() {
479+
return Err(ErrorCode::InvalidArgument("Unexpected trailing characters"));
480+
}
481+
Ok(path)
482+
}
483+
}
484+
485+
#[cfg(test)]
486+
mod test {
487+
use databend_common_exception::Result;
488+
489+
use crate::Binder;
490+
491+
#[test]
492+
fn test_convert_array_index() -> Result<()> {
493+
let success = vec![
494+
("['a']", "{\"a\"}"),
495+
("['a'][0]", "{\"a\",0}"),
496+
("['a']['b']", "{\"a\",\"b\"}"),
497+
("['a']['some_word']", "{\"a\",\"some_word\"}"),
498+
("['a'][42]['long_string']", "{\"a\",42,\"long_string\"}"),
499+
("['a'][0]['b']['c']", "{\"a\",0,\"b\",\"c\"}"),
500+
("['key']['value'][123]", "{\"key\",\"value\",123}"),
501+
("[0]", "{0}"),
502+
];
503+
let failure = vec!["invalid", "['a'", "['a']extra"];
504+
for (case, result) in success {
505+
assert_eq!(result, Binder::convert_array_index(case)?);
506+
}
507+
for case in failure {
508+
assert!(Binder::convert_array_index(case).is_err());
509+
}
510+
511+
Ok(())
512+
}
425513
}

src/query/sql/src/planner/semantic/type_check.rs

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -598,33 +598,10 @@ impl<'a> TypeChecker<'a> {
598598
// as we cast JSON null to SQL NULL.
599599
let target_type = if data_type.remove_nullable() == DataType::Variant {
600600
let target_type = checked_expr.data_type().nest_wrap_nullable();
601-
if let ScalarExpr::BoundColumnRef(column) = &mut scalar {
602-
let mut guard = self.metadata.write();
603-
if let ColumnEntry::VirtualColumn(virtual_column) =
604-
guard.column(column.column.index).clone()
605-
{
606-
if let ColumnEntry::BaseTableColumn(source_column) =
607-
guard.column(virtual_column.source_column_index).clone()
608-
{
609-
if let Some(table_ty) = Option::<TableDataType>::from(&target_type)
610-
{
611-
// Tips: column id from source virtual_column
612-
column.column.index = guard.add_virtual_column(
613-
&source_column,
614-
virtual_column.column_id,
615-
virtual_column.column_name,
616-
table_ty,
617-
virtual_column.key_paths,
618-
Some(virtual_column.column_index),
619-
virtual_column.is_created,
620-
);
621-
column.column.data_type = Box::new(target_type.clone());
622-
return Ok(Box::new((scalar, target_type)));
623-
}
624-
}
625-
}
626-
}
627601

602+
if self.try_rewrite_virtual_column_cast(&mut scalar, &target_type) {
603+
return Ok(Box::new((scalar, target_type)));
604+
}
628605
target_type
629606
// if the source type is nullable, cast target type should also be nullable.
630607
} else if data_type.is_nullable_or_null() {
@@ -1199,6 +1176,44 @@ impl<'a> TypeChecker<'a> {
11991176
Ok(Box::new((scalar, data_type)))
12001177
}
12011178

1179+
fn try_rewrite_virtual_column_cast(
1180+
&mut self,
1181+
scalar: &mut ScalarExpr,
1182+
target_type: &DataType,
1183+
) -> bool {
1184+
let ScalarExpr::BoundColumnRef(column) = scalar else {
1185+
return false;
1186+
};
1187+
let mut guard = self.metadata.write();
1188+
if column.column.table_index.is_none() {
1189+
return false;
1190+
}
1191+
let ColumnEntry::VirtualColumn(virtual_column) = guard.column(column.column.index).clone()
1192+
else {
1193+
return false;
1194+
};
1195+
let ColumnEntry::BaseTableColumn(source_column) =
1196+
guard.column(virtual_column.source_column_index).clone()
1197+
else {
1198+
return false;
1199+
};
1200+
if let Some(table_ty) = Option::<TableDataType>::from(target_type) {
1201+
// Tips: column id from source virtual_column
1202+
column.column.index = guard.add_virtual_column(
1203+
&source_column,
1204+
virtual_column.column_id,
1205+
virtual_column.column_name,
1206+
table_ty,
1207+
virtual_column.key_paths,
1208+
Some(virtual_column.column_index),
1209+
virtual_column.is_created,
1210+
);
1211+
column.column.data_type = Box::new(target_type.clone());
1212+
return true;
1213+
}
1214+
false
1215+
}
1216+
12021217
// TODO: remove this function
12031218
fn rewrite_substring(args: &mut [ScalarExpr]) {
12041219
if let ScalarExpr::ConstantExpr(expr) = &args[1] {

0 commit comments

Comments
 (0)