@@ -32,6 +32,7 @@ use databend_common_expression::vectorize_with_builder_4_arg;
3232use databend_functions_scalar_decimal:: register_decimal_to_uuid;
3333use stringslice:: StringSlice ;
3434
35+ use crate :: scalars:: binary:: write_hex_lower;
3536use crate :: srfs;
3637
3738pub const ALL_STRING_FUNC_NAMES : & [ & str ] = & [
@@ -60,6 +61,7 @@ pub const ALL_STRING_FUNC_NAMES: &[&str] = &[
6061 "trim_trailing" ,
6162 "trim_both" ,
6263 "to_hex" ,
64+ "conv" ,
6365 "bin" ,
6466 "oct" ,
6567 "to_hex" ,
@@ -668,16 +670,66 @@ pub fn register(registry: &mut FunctionRegistry) {
668670 . calc_domain ( |_, _| FunctionDomain :: Full )
669671 . vectorized ( vectorize_with_builder_1_arg :: < StringType , StringType > (
670672 |val, output, _| {
671- let len = val. len ( ) * 2 ;
672- output. row_buffer . resize ( len, 0 ) ;
673- hex:: encode_to_slice ( val, & mut output. row_buffer ) . unwrap ( ) ;
673+ write_hex_lower ( val. as_bytes ( ) , output) ;
674674 output. commit_row ( ) ;
675675 } ,
676676 ) )
677677 . register ( ) ;
678678
679- // TODO: generalize them to be alias of [CONV](https://dev.mysql.com/doc/refman/8.0/en/mathematical-functions.html#function_conv)
680- // Tracking issue: https://github.com/datafuselabs/databend/issues/7242
679+ registry
680+ . scalar_builder ( "hex" )
681+ . function ( )
682+ . typed_1_arg :: < StringType , StringType > ( )
683+ . passthrough_nullable ( )
684+ . calc_domain ( |_, _| FunctionDomain :: Full )
685+ . vectorized ( vectorize_with_builder_1_arg :: < StringType , StringType > (
686+ |val, output, _| {
687+ write_hex_lower ( val. as_bytes ( ) , output) ;
688+ output. commit_row ( ) ;
689+ } ,
690+ ) )
691+ . register ( ) ;
692+
693+ registry. register_passthrough_nullable_3_arg :: <
694+ StringType ,
695+ NumberType < i64 > ,
696+ NumberType < i64 > ,
697+ StringType ,
698+ _ ,
699+ _ ,
700+ > (
701+ "conv" ,
702+ |_, _, _, _| FunctionDomain :: MayThrow ,
703+ vectorize_with_builder_3_arg :: <
704+ StringType ,
705+ NumberType < i64 > ,
706+ NumberType < i64 > ,
707+ StringType ,
708+ > ( |num, from_base, to_base, output, ctx| {
709+ conv ( num, from_base, to_base, output, ctx) ;
710+ } ) ,
711+ ) ;
712+
713+ registry. register_passthrough_nullable_3_arg :: <
714+ NumberType < i64 > ,
715+ NumberType < i64 > ,
716+ NumberType < i64 > ,
717+ StringType ,
718+ _ ,
719+ _ ,
720+ > (
721+ "conv" ,
722+ |_, _, _, _| FunctionDomain :: MayThrow ,
723+ vectorize_with_builder_3_arg :: <
724+ NumberType < i64 > ,
725+ NumberType < i64 > ,
726+ NumberType < i64 > ,
727+ StringType ,
728+ > ( |num, from_base, to_base, output, ctx| {
729+ conv ( & num. to_string ( ) , from_base, to_base, output, ctx) ;
730+ } ) ,
731+ ) ;
732+
681733 registry
682734 . scalar_builder ( "bin" )
683735 . function ( )
@@ -686,7 +738,7 @@ pub fn register(registry: &mut FunctionRegistry) {
686738 . calc_domain ( |_, _| FunctionDomain :: Full )
687739 . vectorized ( vectorize_with_builder_1_arg :: < NumberType < i64 > , StringType > (
688740 |val, output, _| {
689- write ! ( output . row_buffer , "{val:b}" ) . unwrap ( ) ;
741+ write_conv_num ( val as u64 , 2 , output ) ;
690742 output. commit_row ( ) ;
691743 } ,
692744 ) )
@@ -700,7 +752,7 @@ pub fn register(registry: &mut FunctionRegistry) {
700752 . calc_domain ( |_, _| FunctionDomain :: Full )
701753 . vectorized ( vectorize_with_builder_1_arg :: < NumberType < i64 > , StringType > (
702754 |val, output, _| {
703- write ! ( output . row_buffer , "{val:o}" ) . unwrap ( ) ;
755+ write_conv_num ( val as u64 , 8 , output ) ;
704756 output. commit_row ( ) ;
705757 } ,
706758 ) )
@@ -720,6 +772,20 @@ pub fn register(registry: &mut FunctionRegistry) {
720772 ) )
721773 . register ( ) ;
722774
775+ registry
776+ . scalar_builder ( "hex" )
777+ . function ( )
778+ . typed_1_arg :: < NumberType < i64 > , StringType > ( )
779+ . passthrough_nullable ( )
780+ . calc_domain ( |_, _| FunctionDomain :: Full )
781+ . vectorized ( vectorize_with_builder_1_arg :: < NumberType < i64 > , StringType > (
782+ |val, output, _| {
783+ write_conv_num ( val as u64 , 16 , output) ;
784+ output. commit_row ( ) ;
785+ } ,
786+ ) )
787+ . register ( ) ;
788+
723789 const MAX_REPEAT_TIMES : u64 = 1000000 ;
724790 registry. register_passthrough_nullable_2_arg :: < StringType , NumberType < u64 > , StringType , _ , _ > (
725791 "repeat" ,
@@ -1079,6 +1145,117 @@ fn substr(builder: &mut StringColumnBuilder, str: &str, pos: i64, len: u64) {
10791145 builder. commit_row ( ) ;
10801146}
10811147
1148+ fn conv (
1149+ num : & str ,
1150+ from_base : i64 ,
1151+ to_base : i64 ,
1152+ output : & mut StringColumnBuilder ,
1153+ ctx : & mut databend_common_expression:: EvalContext ,
1154+ ) {
1155+ let Some ( from_base) = validate_conv_base ( from_base) else {
1156+ ctx. set_error (
1157+ output. len ( ) ,
1158+ format ! (
1159+ "from_base absolute value must be between 2 and 36, but got {}" ,
1160+ from_base
1161+ ) ,
1162+ ) ;
1163+ output. commit_row ( ) ;
1164+ return ;
1165+ } ;
1166+
1167+ let signed_to_base = to_base < 0 ;
1168+ let Some ( to_base) = validate_conv_base ( to_base) else {
1169+ ctx. set_error (
1170+ output. len ( ) ,
1171+ format ! (
1172+ "to_base absolute value must be between 2 and 36, but got {}" ,
1173+ to_base
1174+ ) ,
1175+ ) ;
1176+ output. commit_row ( ) ;
1177+ return ;
1178+ } ;
1179+
1180+ let mut num = parse_conv_num ( num, from_base) ;
1181+ if signed_to_base {
1182+ let signed_num = num as i64 ;
1183+ if signed_num < 0 {
1184+ output. put_char ( '-' ) ;
1185+ num = ( signed_num as u64 ) . wrapping_neg ( ) ;
1186+ }
1187+ }
1188+ write_conv_num ( num, to_base, output) ;
1189+ output. commit_row ( ) ;
1190+ }
1191+
1192+ fn validate_conv_base ( base : i64 ) -> Option < u32 > {
1193+ let base = base. unsigned_abs ( ) ;
1194+ if ( 2 ..=36 ) . contains ( & base) {
1195+ Some ( base as u32 )
1196+ } else {
1197+ None
1198+ }
1199+ }
1200+
1201+ fn parse_conv_num ( num : & str , from_base : u32 ) -> u64 {
1202+ let num = num. trim_start ( ) ;
1203+ let ( negative, digits) = if let Some ( rest) = num. strip_prefix ( '-' ) {
1204+ ( true , rest)
1205+ } else if let Some ( rest) = num. strip_prefix ( '+' ) {
1206+ ( false , rest)
1207+ } else {
1208+ ( false , num)
1209+ } ;
1210+
1211+ let mut value = 0_u64 ;
1212+ for ch in digits. bytes ( ) {
1213+ let Some ( digit) = conv_digit ( ch) else {
1214+ break ;
1215+ } ;
1216+ if digit >= from_base {
1217+ break ;
1218+ }
1219+
1220+ value = value
1221+ . saturating_mul ( from_base as u64 )
1222+ . saturating_add ( digit as u64 ) ;
1223+ }
1224+
1225+ if negative {
1226+ value. wrapping_neg ( )
1227+ } else {
1228+ value
1229+ }
1230+ }
1231+
1232+ fn conv_digit ( ch : u8 ) -> Option < u32 > {
1233+ match ch {
1234+ b'0' ..=b'9' => Some ( ( ch - b'0' ) as u32 ) ,
1235+ b'a' ..=b'z' => Some ( ( ch - b'a' + 10 ) as u32 ) ,
1236+ b'A' ..=b'Z' => Some ( ( ch - b'A' + 10 ) as u32 ) ,
1237+ _ => None ,
1238+ }
1239+ }
1240+
1241+ fn write_conv_num ( mut num : u64 , to_base : u32 , output : & mut StringColumnBuilder ) {
1242+ const DIGITS : & [ u8 ; 36 ] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" ;
1243+
1244+ if num == 0 {
1245+ output. put_char ( '0' ) ;
1246+ return ;
1247+ }
1248+
1249+ let mut buf = [ 0_u8 ; 64 ] ;
1250+ let mut idx = buf. len ( ) ;
1251+ while num > 0 {
1252+ idx -= 1 ;
1253+ buf[ idx] = DIGITS [ ( num % to_base as u64 ) as usize ] ;
1254+ num /= to_base as u64 ;
1255+ }
1256+ output. put_slice ( & buf[ idx..] ) ;
1257+ }
1258+
10821259#[ inline]
10831260fn substr_ascii ( builder : & mut StringColumnBuilder , str : & str , pos : i64 , len : u64 ) {
10841261 let byte_len = str. len ( ) ;
0 commit comments