Description
MySQLBindParameter>>detectParamType handles Floats (and Fractions) as follows:
((paramValue isKindOf: Float) or: [paramValue isKindOf: Fraction])
ifTrue: [
(paramValue asFloat at: 2) = 0
ifTrue: [^ MySQLTypes typeFLOAT]
ifFalse: [^ MySQLTypes typeDOUBLE] ].
Firstly I'm not sure this is a valid test for floats versus doubles. For example, 4.25 is detected as typeFLOAT whereas 4.2 is detected as typeDOUBLE.
More generally, I think the approach of detecting the parameter type based on the value is incorrect. Test case (where c is a connected MySQLDriver):
c query: 'drop table if exists float_test'.
c query: 'create table float_test (f float)'.
r := c prepare: 'insert into float_test(f) values(?);'.
s := (MySQLDriverStatement onConnection: c) stmtId: r prepareOkay stmtHandlerId; cursoredFetch: false; yourself.
s addBinding: 4.2.
s execute.
This fails at the execute stage with an Out of Range error, since 4.2 is detected as typeDOUBLE, but is being assigned to a FLOAT parameter.
Fundamentally, I think it's flawed to try to deduce the parameter type based on the value being assigned; the type should come from the column definition for the parameter (I don't know if MySQL provides an easy way to do this however).
As a partial aside, running the above test with 4.25 (which is picked up as typeFLOAT) results in a returned value of 2.26562 - this looks to be due to an issue with MySQLBindParameter>>floatBytes which (I'm guessing) is due to a change in the internal representation of Floats in Pharo at some point in the past.
Running the test with a DOUBLE column also results in incorrect results even when using a typeDOUBLE value:
c query: 'drop table if exists double_test'.
c query: 'create table double_test (d double)'.
r := c prepare: 'insert into double_test(d) values(?);'.
s := (MySQLDriverStatement onConnection: c) stmtId: r prepareOkay stmtHandlerId; cursoredFetch: false; yourself.
s addBinding: 4.2.
s execute.
(c query: 'select * from double_test') rows first at: 1. " '-9.255965342982487e61' "
This issue can be fixed by reversing the order in which the paramValue's bytes are considered in MySQLBindParameter>>doubleBytes:
doubleBytes
| storable |
ByteArray
streamContents: [ :strm |
storable := paramValue asFloat at: 2.
strm
nextPut: (storable byteAt: 1);
nextPut: (storable byteAt: 2);
nextPut: (storable byteAt: 3);
nextPut: (storable byteAt: 4).
storable := paramValue asFloat at: 1.
strm
nextPut: (storable byteAt: 1);
nextPut: (storable byteAt: 2);
nextPut: (storable byteAt: 3);
nextPut: (storable byteAt: 4).
^ strm contents ]
All tests carried out with 64bit Pharo 7.0 on OSX, connecting to MySQL (actually MariaDB) on Raspberry Pi 4.