@@ -569,6 +569,77 @@ class CometExpressionSuite extends CometTestBase with AdaptiveSparkPlanHelper {
569569 }
570570 }
571571
572+ test(" RIGHT function" ) {
573+ withParquetTable((0 until 10 ).map(i => (s " test $i" , i)), " tbl" ) {
574+ checkSparkAnswerAndOperator(" SELECT _1, RIGHT(_1, 2) FROM tbl" )
575+ checkSparkAnswerAndOperator(" SELECT _1, RIGHT(_1, 4) FROM tbl" )
576+ checkSparkAnswerAndOperator(" SELECT _1, RIGHT(_1, 0) FROM tbl" )
577+ checkSparkAnswerAndOperator(" SELECT _1, RIGHT(_1, -1) FROM tbl" )
578+ checkSparkAnswerAndOperator(" SELECT _1, RIGHT(_1, 100) FROM tbl" )
579+ checkSparkAnswerAndOperator(" SELECT RIGHT(CAST(NULL AS STRING), 2) FROM tbl LIMIT 1" )
580+ }
581+ }
582+
583+ test(" RIGHT function with unicode" ) {
584+ val data = Seq (" café" , " hello世界" , " 😀emoji" , " తెలుగు" )
585+ withParquetTable(data.zipWithIndex, " unicode_tbl" ) {
586+ checkSparkAnswerAndOperator(" SELECT _1, RIGHT(_1, 2) FROM unicode_tbl" )
587+ checkSparkAnswerAndOperator(" SELECT _1, RIGHT(_1, 3) FROM unicode_tbl" )
588+ checkSparkAnswerAndOperator(" SELECT _1, RIGHT(_1, 0) FROM unicode_tbl" )
589+ }
590+ }
591+
592+ test(" RIGHT function equivalence with SUBSTRING negative pos" ) {
593+ withParquetTable((0 until 20 ).map(i => Tuple1 (s " test $i" )), " equiv_tbl" ) {
594+ val df = spark.sql("""
595+ SELECT _1,
596+ RIGHT(_1, 3) as right_result,
597+ SUBSTRING(_1, -3, 3) as substring_result
598+ FROM equiv_tbl
599+ """ )
600+ checkAnswer(
601+ df.filter(
602+ " right_result != substring_result OR " +
603+ " (right_result IS NULL AND substring_result IS NOT NULL) OR " +
604+ " (right_result IS NOT NULL AND substring_result IS NULL)" ),
605+ Seq .empty)
606+ }
607+ }
608+
609+ test(" RIGHT function with dictionary" ) {
610+ val data = (0 until 1000 )
611+ .map(_ % 5 )
612+ .map(i => s " value $i" )
613+ withParquetTable(data.zipWithIndex, " dict_tbl" ) {
614+ checkSparkAnswerAndOperator(" SELECT _1, RIGHT(_1, 3) FROM dict_tbl" )
615+ }
616+ }
617+
618+ test(" RIGHT function NULL handling" ) {
619+ // Test NULL propagation with len = 0 (critical edge case)
620+ withParquetTable((0 until 5 ).map(i => (s " test $i" , i)), " null_tbl" ) {
621+ checkSparkAnswerAndOperator(" SELECT RIGHT(CAST(NULL AS STRING), 0) FROM null_tbl LIMIT 1" )
622+ checkSparkAnswerAndOperator(" SELECT RIGHT(CAST(NULL AS STRING), -1) FROM null_tbl LIMIT 1" )
623+ checkSparkAnswerAndOperator(" SELECT RIGHT(CAST(NULL AS STRING), -5) FROM null_tbl LIMIT 1" )
624+ }
625+
626+ // Test non-NULL strings with len <= 0 (should return empty string)
627+ withParquetTable((0 until 5 ).map(i => (s " test $i" , i)), " edge_tbl" ) {
628+ checkSparkAnswerAndOperator(" SELECT _1, RIGHT(_1, 0) FROM edge_tbl" )
629+ checkSparkAnswerAndOperator(" SELECT _1, RIGHT(_1, -1) FROM edge_tbl" )
630+ }
631+
632+ // Test mixed NULL and non-NULL values with a table
633+ val table = " right_null_edge"
634+ withTable(table) {
635+ sql(s " create table $table(str string) using parquet " )
636+ sql(s " insert into $table values('hello'), (NULL), (''), ('world') " )
637+ checkSparkAnswerAndOperator(s " SELECT str, RIGHT(str, 0) FROM $table" )
638+ checkSparkAnswerAndOperator(s " SELECT str, RIGHT(str, -1) FROM $table" )
639+ checkSparkAnswerAndOperator(s " SELECT str, RIGHT(str, 2) FROM $table" )
640+ }
641+ }
642+
572643 test(" hour, minute, second" ) {
573644 Seq (true , false ).foreach { dictionaryEnabled =>
574645 withTempDir { dir =>
0 commit comments