@@ -18,6 +18,7 @@ import (
1818 "context"
1919 "fmt"
2020 "math"
21+ "strings"
2122 "testing"
2223 "time"
2324
@@ -1953,3 +1954,160 @@ func Benchmark_strToSigned_Binary(b *testing.B) {
19531954 })
19541955 }
19551956}
1957+
1958+ // Test_strToStr_TextToCharVarchar tests that TEXT type can be cast to CHAR/VARCHAR
1959+ // without length validation errors, even when the string length exceeds the target length.
1960+ // This is important for UPDATE operations on TEXT columns with CONCAT operations.
1961+ func Test_strToStr_TextToCharVarchar (t * testing.T ) {
1962+ ctx := context .Background ()
1963+ mp := mpool .MustNewZero ()
1964+
1965+ // Helper function to create long strings
1966+ longString260 := strings .Repeat ("a" , 260 ) // 260 characters
1967+ longString100 := strings .Repeat ("b" , 100 ) // 100 characters
1968+
1969+ tests := []struct {
1970+ name string
1971+ inputs []string
1972+ nulls []uint64
1973+ fromType types.Type
1974+ toType types.Type
1975+ want []string
1976+ wantNulls []uint64
1977+ wantErr bool
1978+ errMsg string
1979+ }{
1980+ {
1981+ name : "TEXT to CHAR(255) with length 260 - should succeed" ,
1982+ inputs : []string {longString260 },
1983+ fromType : types .T_text .ToType (),
1984+ toType : types .New (types .T_char , 255 , 0 ),
1985+ want : []string {longString260 }, // Should keep original length
1986+ wantErr : false ,
1987+ },
1988+ {
1989+ name : "TEXT to VARCHAR(255) with length 260 - should succeed" ,
1990+ inputs : []string {longString260 },
1991+ fromType : types .T_text .ToType (),
1992+ toType : types .New (types .T_varchar , 255 , 0 ),
1993+ want : []string {longString260 }, // Should keep original length
1994+ wantErr : false ,
1995+ },
1996+ {
1997+ name : "TEXT to CHAR(255) with NULL - should handle NULL" ,
1998+ inputs : []string {"" , "test" },
1999+ nulls : []uint64 {0 },
2000+ fromType : types .T_text .ToType (),
2001+ toType : types .New (types .T_char , 255 , 0 ),
2002+ want : []string {"" , "test" },
2003+ wantNulls : []uint64 {0 },
2004+ wantErr : false ,
2005+ },
2006+ {
2007+ name : "VARCHAR to CHAR(10) with length 100 - should fail (normal behavior)" ,
2008+ inputs : []string {longString100 },
2009+ fromType : types .New (types .T_varchar , 100 , 0 ),
2010+ toType : types .New (types .T_char , 10 , 0 ),
2011+ wantErr : true ,
2012+ errMsg : "larger than Dest length" ,
2013+ },
2014+ {
2015+ name : "TEXT to CHAR(1) with length > 1 - should fail (explicit CAST)" ,
2016+ inputs : []string {"ab" },
2017+ fromType : types .T_text .ToType (),
2018+ toType : types .New (types .T_char , 1 , 0 ),
2019+ wantErr : true ,
2020+ errMsg : "larger than Dest length" ,
2021+ },
2022+ {
2023+ name : "TEXT to CHAR(10) with length 100 - should fail (explicit CAST to small width)" ,
2024+ inputs : []string {longString100 },
2025+ fromType : types .T_text .ToType (),
2026+ toType : types .New (types .T_char , 10 , 0 ),
2027+ wantErr : true ,
2028+ errMsg : "larger than Dest length" ,
2029+ },
2030+ {
2031+ name : "TEXT to VARCHAR(10) with length 100 - should fail (explicit CAST to small width)" ,
2032+ inputs : []string {longString100 },
2033+ fromType : types .T_text .ToType (),
2034+ toType : types .New (types .T_varchar , 10 , 0 ),
2035+ wantErr : true ,
2036+ errMsg : "larger than Dest length" ,
2037+ },
2038+ {
2039+ name : "TEXT to TEXT - should succeed" ,
2040+ inputs : []string {"test text" },
2041+ fromType : types .T_text .ToType (),
2042+ toType : types .T_text .ToType (),
2043+ want : []string {"test text" },
2044+ wantErr : false ,
2045+ },
2046+ {
2047+ name : "TEXT to CHAR(255) with multiple values" ,
2048+ inputs : []string {"short" , longString260 , "medium length string" },
2049+ fromType : types .T_text .ToType (),
2050+ toType : types .New (types .T_char , 255 , 0 ),
2051+ want : []string {"short" , longString260 , "medium length string" },
2052+ wantErr : false ,
2053+ },
2054+ }
2055+
2056+ for _ , tt := range tests {
2057+ t .Run (tt .name , func (t * testing.T ) {
2058+ // Create input vector based on source type
2059+ var inputVec * vector.Vector
2060+ if tt .fromType .Oid == types .T_text {
2061+ inputVec = testutil .MakeTextVector (tt .inputs , tt .nulls )
2062+ } else {
2063+ inputVec = testutil .MakeVarcharVector (tt .inputs , tt .nulls )
2064+ // Set the type explicitly for non-TEXT types
2065+ inputVec .SetType (tt .fromType )
2066+ }
2067+ defer inputVec .Free (mp )
2068+
2069+ from := vector .GenerateFunctionStrParameter (inputVec )
2070+
2071+ resultType := tt .toType
2072+ to := vector .NewFunctionResultWrapper (resultType , mp ).(* vector.FunctionResult [types.Varlena ])
2073+ defer to .Free ()
2074+ err := to .PreExtendAndReset (len (tt .inputs ))
2075+ require .NoError (t , err )
2076+
2077+ err = strToStr (ctx , from , to , len (tt .inputs ), tt .toType )
2078+
2079+ if tt .wantErr {
2080+ require .Error (t , err )
2081+ if tt .errMsg != "" {
2082+ require .Contains (t , err .Error (), tt .errMsg )
2083+ }
2084+ return
2085+ }
2086+ require .NoError (t , err )
2087+
2088+ resultVec := to .GetResultVector ()
2089+ r := vector .GenerateFunctionStrParameter (resultVec )
2090+
2091+ for i := 0 ; i < len (tt .want ); i ++ {
2092+ want := tt .want [i ]
2093+ get , null := r .GetStrValue (uint64 (i ))
2094+
2095+ if contains (tt .wantNulls , uint64 (i )) {
2096+ require .True (t , null , "row %d should be null" , i )
2097+ } else {
2098+ require .False (t , null , "row %d should not be null" , i )
2099+ require .Equal (t , want , string (get ), "row %d value not match" , i )
2100+ }
2101+ }
2102+
2103+ resultNulls := to .GetResultVector ().GetNulls ()
2104+ if len (tt .wantNulls ) > 0 {
2105+ for _ , pos := range tt .wantNulls {
2106+ require .True (t , resultNulls .Contains (pos ))
2107+ }
2108+ } else {
2109+ require .True (t , resultNulls .IsEmpty ())
2110+ }
2111+ })
2112+ }
2113+ }
0 commit comments