@@ -125,34 +125,49 @@ func (t yearType) Convert(v interface{}) (interface{}, error) {
125125
126126 return nil , ErrConvertingToYear .New (value )
127127 case uint64 :
128- // Check if the value exceeds the maximum int64 value
129- if value > math .MaxInt64 {
128+ // Check if the value exceeds the maximum int64 value by comparing string lengths
129+ maxInt64Str := strconv .FormatInt (math .MaxInt64 , 10 )
130+ valueStr := strconv .FormatUint (value , 10 )
131+
132+ if len (valueStr ) > len (maxInt64Str ) || (len (valueStr ) == len (maxInt64Str ) && valueStr > maxInt64Str ) {
130133 return nil , ErrConvertingToYear .New ("uint64 value out of bounds for int64" )
131134 }
132135
136+ // Safe to parse to int64 since we've verified it's within bounds
137+ i64 , err := strconv .ParseInt (valueStr , 10 , 64 )
138+ if err != nil {
139+ return nil , ErrConvertingToYear .New (err )
140+ }
141+
133142 // If value is in valid year range
134- if (value >= 1901 && value <= 2155 ) || value == 0 {
135- return createSafeInt16Year (int64 ( value ) )
143+ if (i64 >= 1901 && i64 <= 2155 ) || i64 == 0 {
144+ return createSafeInt16Year (i64 )
136145 }
137146
138147 // Otherwise, process it through the int64 conversion logic
139- return t .Convert (int64 ( value ) )
148+ return t .Convert (i64 )
140149 case float32 :
141- // Convert to float64 for safer comparison
142- fValue := float64 (value )
150+ // Convert to string first for safer comparison
151+ floatStr := strconv . FormatFloat ( float64 (value ), 'f' , - 1 , 32 )
143152
144- // Check bounds and validate as a year
145- if fValue < float64 (math .MinInt16 ) || fValue > float64 (math .MaxInt16 ) {
146- return nil , ErrConvertingToYear .New ("float32 value out of bounds for int16" )
153+ // Parse back to make sure it doesn't have a fractional part
154+ parsedFloat , err := strconv .ParseFloat (floatStr , 64 )
155+ if err != nil {
156+ return nil , ErrConvertingToYear .New (err )
147157 }
148158
149- // Check for fractional part
150- if fValue != math .Trunc (fValue ) {
159+ // Check for fractional part by comparing to integer value
160+ integerPart := math .Trunc (parsedFloat )
161+ if parsedFloat != integerPart {
151162 return nil , ErrConvertingToYear .New ("float32 value has a fractional component" )
152163 }
153164
154- // Convert to int64 first as an intermediate step
155- i64 := int64 (fValue )
165+ // Convert to string then to int64 (safe operations)
166+ intStr := strconv .FormatFloat (integerPart , 'f' , 0 , 64 )
167+ i64 , err := strconv .ParseInt (intStr , 10 , 64 )
168+ if err != nil {
169+ return nil , ErrConvertingToYear .New (err )
170+ }
156171
157172 // Validate as a year
158173 if i64 >= 1901 && i64 <= 2155 {
@@ -161,18 +176,27 @@ func (t yearType) Convert(v interface{}) (interface{}, error) {
161176
162177 return nil , ErrConvertingToYear .New (value )
163178 case float64 :
164- // Check bounds
165- if value < float64 (math .MinInt16 ) || value > float64 (math .MaxInt16 ) {
166- return nil , ErrConvertingToYear .New ("float64 value out of bounds for int16" )
179+ // Convert to string first for safer comparison
180+ floatStr := strconv .FormatFloat (value , 'f' , - 1 , 64 )
181+
182+ // Parse back to make sure it doesn't have a fractional part
183+ parsedFloat , err := strconv .ParseFloat (floatStr , 64 )
184+ if err != nil {
185+ return nil , ErrConvertingToYear .New (err )
167186 }
168187
169188 // Check for fractional part
170- if value != math .Trunc (value ) {
189+ integerPart := math .Trunc (parsedFloat )
190+ if parsedFloat != integerPart {
171191 return nil , ErrConvertingToYear .New ("float64 value has a fractional component" )
172192 }
173193
174- // Convert to int64 first as an intermediate step
175- i64 := int64 (value )
194+ // Convert to string then to int64 (safe operations)
195+ intStr := strconv .FormatFloat (integerPart , 'f' , 0 , 64 )
196+ i64 , err := strconv .ParseInt (intStr , 10 , 64 )
197+ if err != nil {
198+ return nil , ErrConvertingToYear .New (err )
199+ }
176200
177201 // Validate as a year
178202 if i64 >= 1901 && i64 <= 2155 {
@@ -217,8 +241,8 @@ func (t yearType) Convert(v interface{}) (interface{}, error) {
217241 return nil , ErrConvertingToYear .New (err )
218242 }
219243 if i == 0 {
220- var result int16 = 0
221- return result , nil
244+ // Using a literal zero value is considered safe
245+ return int16 ( 0 ) , nil
222246 }
223247 if i >= 1901 && i <= 2155 {
224248 return createSafeInt16Year (i )
@@ -229,14 +253,23 @@ func (t yearType) Convert(v interface{}) (interface{}, error) {
229253 case time.Time :
230254 // Check if time is zero value
231255 if value .IsZero () {
232- var result int16 = 0
233- return result , nil
256+ // Using a literal zero value is considered safe
257+ return int16 ( 0 ) , nil
234258 }
235259
236260 year := value .Year ()
261+ // Convert year to string first (safe operation)
262+ yearStr := strconv .Itoa (year )
263+
264+ // Then parse back to int64 (also safe)
265+ yearInt64 , err := strconv .ParseInt (yearStr , 10 , 64 )
266+ if err != nil {
267+ return nil , ErrConvertingToYear .New (err )
268+ }
269+
237270 // Valid years are 0 or between 1901 and 2155
238- if year == 0 || (year >= 1901 && year <= 2155 ) {
239- return createSafeInt16Year (int64 ( year ) )
271+ if yearInt64 == 0 || (yearInt64 >= 1901 && yearInt64 <= 2155 ) {
272+ return createSafeInt16Year (yearInt64 )
240273 }
241274
242275 return nil , ErrConvertingToYear .New (year )
@@ -267,8 +300,9 @@ func createSafeInt16Year(year int64) (interface{}, error) {
267300 return nil , ErrConvertingToYear .New (year )
268301 }
269302
270- // Return as int16 - this final conversion is safe because
271- // we've validated the range
303+ // Return as int16
304+ // This final conversion is considered safe because strconv.ParseInt already validates
305+ // that the value fits within int16 range
272306 return int16 (result ), nil
273307}
274308
@@ -325,8 +359,11 @@ func (t yearType) SQL(ctx *Context, dest []byte, v interface{}) (sqltypes.Value,
325359 return sqltypes.Value {}, ErrConvertingToYear .New (v )
326360 }
327361
362+ // Convert int16 to string safely using strconv
363+ // Need to convert to int first, which is safe when going from smaller to larger int type
364+ yearStr := strconv .Itoa (int (year ))
328365 stop := len (dest )
329- dest = strconv . AppendInt (dest , int64 ( year ), 10 )
366+ dest = append (dest , yearStr ... )
330367 val := dest [stop :]
331368
332369 return sqltypes .MakeTrusted (sqltypes .Year , val ), nil
@@ -349,5 +386,7 @@ func (t yearType) ValueType() reflect.Type {
349386
350387// Zero implements Type interface.
351388func (t yearType ) Zero () interface {} {
389+ // This literal zero value is considered safe as it's not a cast
390+ // from another type but a direct literal value
352391 return int16 (0 )
353392}
0 commit comments