Skip to content

Commit 9a292fc

Browse files
committed
impl with configurable string to number
1 parent 028d9ca commit 9a292fc

File tree

6 files changed

+268
-49
lines changed

6 files changed

+268
-49
lines changed

enginetest/queries/variable_queries.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,153 @@ var VariableQueries = []ScriptTest{
633633
},
634634
},
635635
},
636+
{
637+
Name: "mysql_string_to_number system variable",
638+
Assertions: []ScriptTestAssertion{
639+
{
640+
Query: "SELECT @@mysql_string_to_number",
641+
Expected: []sql.Row{{int8(0)}},
642+
},
643+
{
644+
Query: "SET @@mysql_string_to_number = 1",
645+
Expected: []sql.Row{{types.NewOkResult(0)}},
646+
},
647+
{
648+
Query: "SELECT @@mysql_string_to_number",
649+
Expected: []sql.Row{{int8(1)}},
650+
},
651+
{
652+
Query: "SET @@mysql_string_to_number = 0",
653+
Expected: []sql.Row{{types.NewOkResult(0)}},
654+
},
655+
{
656+
Query: "SELECT @@mysql_string_to_number",
657+
Expected: []sql.Row{{int8(0)}},
658+
},
659+
{
660+
Query: "SET @@session.mysql_string_to_number = 1",
661+
Expected: []sql.Row{{types.NewOkResult(0)}},
662+
},
663+
{
664+
Query: "SELECT @@session.mysql_string_to_number",
665+
Expected: []sql.Row{{int8(1)}},
666+
},
667+
{
668+
Query: "SET @@global.mysql_string_to_number = 0",
669+
Expected: []sql.Row{{types.NewOkResult(0)}},
670+
},
671+
{
672+
Query: "SELECT @@global.mysql_string_to_number",
673+
Expected: []sql.Row{{int8(0)}},
674+
},
675+
},
676+
},
677+
{
678+
Name: "mysql_string_to_number conversion behavior",
679+
SetUpScript: []string{
680+
"CREATE TABLE test_table (id INT PRIMARY KEY, int_col INT, float_col FLOAT)",
681+
"INSERT INTO test_table VALUES (1, 10, 10.5)",
682+
},
683+
Assertions: []ScriptTestAssertion{
684+
// Variable disabled - original behavior
685+
{
686+
Query: "SET @@mysql_string_to_number = 0",
687+
Expected: []sql.Row{{types.NewOkResult(0)}},
688+
},
689+
{
690+
Query: "SELECT CAST('123abc' AS SIGNED)",
691+
Expected: []sql.Row{{int64(0)}},
692+
},
693+
{
694+
Query: "SELECT CAST('_123_' AS SIGNED)",
695+
Expected: []sql.Row{{int64(0)}},
696+
},
697+
{
698+
Query: "SELECT CAST('abc123' AS SIGNED)",
699+
Expected: []sql.Row{{int64(0)}},
700+
},
701+
// Variable enabled - extract numeric prefixes
702+
{
703+
Query: "SET @@mysql_string_to_number = 1",
704+
Expected: []sql.Row{{types.NewOkResult(0)}},
705+
},
706+
{
707+
Query: "SELECT CAST('123abc' AS SIGNED)",
708+
Expected: []sql.Row{{int64(123)}},
709+
},
710+
{
711+
Query: "SELECT CAST('_123_' AS SIGNED)",
712+
Expected: []sql.Row{{int64(0)}},
713+
},
714+
{
715+
Query: "SELECT CAST('123_' AS SIGNED)",
716+
Expected: []sql.Row{{int64(123)}},
717+
},
718+
{
719+
Query: "SELECT CAST('abc123' AS SIGNED)",
720+
Expected: []sql.Row{{int64(0)}},
721+
},
722+
{
723+
Query: "SELECT CAST(' 123 ' AS SIGNED)",
724+
Expected: []sql.Row{{int64(123)}},
725+
},
726+
{
727+
Query: "SELECT CAST(' +45.67e2' AS SIGNED)",
728+
Expected: []sql.Row{{int64(45)}},
729+
},
730+
{
731+
Query: "SELECT CAST('-789.123' AS SIGNED)",
732+
Expected: []sql.Row{{int64(-789)}},
733+
},
734+
{
735+
Query: "SELECT CAST('123%' AS SIGNED)",
736+
Expected: []sql.Row{{int64(123)}},
737+
},
738+
{
739+
Query: "SELECT CAST('#123' AS SIGNED)",
740+
Expected: []sql.Row{{int64(0)}},
741+
},
742+
{
743+
Query: "SELECT CAST('123.456' AS SIGNED)",
744+
Expected: []sql.Row{{int64(123)}},
745+
},
746+
// Test decimal conversions
747+
{
748+
Query: "SELECT CAST('123.456abc' AS DECIMAL(10,3))",
749+
Expected: []sql.Row{{"123.456"}},
750+
},
751+
{
752+
Query: "SELECT CAST('_123.456_' AS DECIMAL(10,3))",
753+
Expected: []sql.Row{{"0.000"}},
754+
},
755+
{
756+
Query: "SELECT CAST('45.67e2' AS DECIMAL(10,2))",
757+
Expected: []sql.Row{{"4567.00"}},
758+
},
759+
// Test unsigned type handling
760+
{
761+
Query: "SELECT CAST('123abc' AS UNSIGNED)",
762+
Expected: []sql.Row{{uint64(123)}},
763+
},
764+
{
765+
Query: "SELECT CAST('-123abc' AS UNSIGNED)",
766+
Expected: []sql.Row{{uint64(18446744073709551493)}},
767+
},
768+
// Test comparison operations from issue #7128
769+
{
770+
Query: "SELECT * FROM test_table WHERE int_col = '10abc'",
771+
Expected: []sql.Row{{1, 10, float32(10.5)}},
772+
},
773+
{
774+
Query: "SELECT * FROM test_table WHERE float_col = '10.5xyz'",
775+
Expected: []sql.Row{{1, 10, float32(10.5)}},
776+
},
777+
{
778+
Query: "SELECT * FROM test_table WHERE int_col = '_10_'",
779+
Expected: []sql.Row{},
780+
},
781+
},
782+
},
636783
//TODO: do not override tables with user-var-like names...but why would you do this??
637784
//{
638785
// Name: "user var table name no conflict",

sql/expression/convert.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,11 +339,24 @@ func convertValue(ctx *sql.Context, val interface{}, castTo string, originType s
339339
}
340340
return d, nil
341341
case ConvertToDecimal:
342+
dt := createConvertedDecimalType(typeLength, typeScale, false)
343+
344+
// Handle string-to-decimal conversion for mysql_string_to_number mode
345+
if strVal, ok := val.(string); ok && sql.ValidateStringToNumberMode(ctx) {
346+
if convertedVal, _, err := types.Float64.Convert(ctx, strVal); err == nil {
347+
d, _, err := dt.Convert(ctx, convertedVal)
348+
if err != nil {
349+
return dt.Zero(), nil
350+
}
351+
return d, nil
352+
}
353+
}
354+
342355
value, err := convertHexBlobToDecimalForNumericContext(val, originType)
343356
if err != nil {
344357
return nil, err
345358
}
346-
dt := createConvertedDecimalType(typeLength, typeScale, false)
359+
347360
d, _, err := dt.Convert(ctx, value)
348361
if err != nil {
349362
return dt.Zero(), nil

sql/sql_mode.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,16 @@ func ValidateStrictMode(ctx *Context) bool {
128128
sqlMode := LoadSqlMode(ctx)
129129
return sqlMode.ModeEnabled("STRICT_TRANS_TABLES") || sqlMode.ModeEnabled("STRICT_ALL_TABLES")
130130
}
131+
132+
// ValidateStringToNumberMode returns true if mysql_string_to_number is enabled
133+
func ValidateStringToNumberMode(ctx *Context) bool {
134+
if ctx == nil {
135+
return false
136+
}
137+
if val, err := ctx.GetSessionVariable(ctx, "mysql_string_to_number"); err == nil {
138+
if mysqlCompat, ok := val.(int8); ok {
139+
return mysqlCompat != 0
140+
}
141+
}
142+
return false
143+
}

0 commit comments

Comments
 (0)