Skip to content

Detection of Float query parameter types is problematic #6

Open
@rko281

Description

@rko281

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions