Skip to content

Commit d6b406e

Browse files
committed
Add failing test: column elements have inconsistent types
When using COALESCE it's very easy to get a column in the query-result whose elements have inconsistent types. This is outside the expected behaviour of a MySQL implementation, as MySQL will make sure that all elements in a column of results share the same type.
1 parent c216e59 commit d6b406e

File tree

1 file changed

+52
-0
lines changed

1 file changed

+52
-0
lines changed

enginetest/engine_only_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,58 @@ func TestRegex(t *testing.T) {
790790
runtime.GC()
791791
}
792792

793+
func TestCoalesceRespectsColumnSchema(t *testing.T) {
794+
harness := enginetest.NewDefaultMemoryHarness()
795+
harness.Setup(setup.SimpleSetup...)
796+
engine, err := harness.NewEngine(t)
797+
require.NoError(t, err)
798+
defer engine.Close()
799+
800+
for _, test := range []queries.ScriptTest{
801+
{
802+
Name: "Coalesce Null to `0` in a Float Column",
803+
// See SQLFiddle: https://sqlfiddle.com/mysql/online-compiler?id=ee3b322f-66a7-448a-8d6a-d3bfcc19dcc6
804+
SetUpScript: []string{
805+
"CREATE TABLE persons (name VARCHAR(50), age FLOAT);", // FLOAT is set on the column
806+
"INSERT INTO persons VALUES ('sam', 44.1), ('ben', NULL);",
807+
},
808+
Assertions: []queries.ScriptTestAssertion{
809+
{
810+
Query: "SELECT name, COALESCE(age, 0) FROM persons WHERE name = 'ben';",
811+
Expected: []sql.Row{
812+
{"ben", float64(0)},
813+
},
814+
},
815+
},
816+
},
817+
{
818+
Name: "All elements in a column should share the same type",
819+
// See SQLFiddle: https://sqlfiddle.com/mysql/online-compiler?id=11f3a12d-3d81-478e-aed3-363271097a3e
820+
SetUpScript: []string{
821+
"CREATE TABLE persons (name VARCHAR(50), age INT);",
822+
"INSERT INTO persons VALUES ('sam', 44), ('ben', NULL);",
823+
},
824+
Assertions: []queries.ScriptTestAssertion{
825+
{
826+
// the coalesced column should be DECIMAL (or possibly DOUBLE depending on the specific MySQL version and settings), effectively a floating-point type.
827+
// Here's why:
828+
// When you use COALESCE(age, 0.0), you're providing a floating-point literal (0.0) as the second argument. MySQL's type coercion rules come into play. It needs to determine a single data type for the result of the COALESCE function.
829+
// Because one of the input types is a floating-point type (0.0), MySQL will choose a type that can accommodate both integers and floating-point values without loss of precision. Simply using INT for the result would mean losing the fractional part of the 0.0 value if age happened to be NULL.
830+
// Therefore, MySQL "upgrades" the result type to DECIMAL (or DOUBLE), which can accurately represent both integers and floating-point numbers. This is a common behavior in SQL systems to ensure data integrity and avoid unexpected truncation.
831+
// If you had used 0 instead of 0.0 (i.e. COALESCE(age, 0)), the coalesced column would have remained INT because both operands would have been integers.
832+
Query: "SELECT name, COALESCE(age, 0.0) FROM persons;",
833+
Expected: []sql.Row{
834+
{"sam", 44.0}, {"ben", 0.0},
835+
},
836+
},
837+
},
838+
},
839+
} {
840+
enginetest.TestScript(t, harness, test)
841+
}
842+
runtime.GC()
843+
}
844+
793845
var _ sql.TableFunction = (*SimpleTableFunction)(nil)
794846
var _ sql.CollationCoercible = (*SimpleTableFunction)(nil)
795847
var _ sql.ExecSourceRel = (*SimpleTableFunction)(nil)

0 commit comments

Comments
 (0)