CAUTION: Silent Value Truncation In CFQueryParam Tag In Lucee CFML 5.3.7.47
I just ran into a highly-problematic behavior with the CFQueryParam
tag in Lucee CFML 5.3.7.47. As it stands now, if you pass a value via the CF_SQL_INTEGER
SQL type that is larger than the max signed-value (2147483647), Lucee will silently truncate it, passing the max signed-value to the underlying SQL query. To fix this, you have to use CF_SQL_BIGINT
in your query parameter binding.
At first, they may not sound like a problem since an Int is an Int is an Int. But, it's not that simple. In the (MySQL) database, there are two types of 4-byte integers:
SIGNED INT
→ Range from -2,147,483,648 to 2,147,483,647.UNSIGNED INT
→ Range from 0 to 4,294,967,295.
ASIDE: There are more than two types of integers, but for this conversation, we're talking about 4-byte integers. I am not sure if the silent truncation happens for the other types (but I assume it would).
What this means is that an UNSIGNED INT
column in MySQL can legitimately store a value that it outside the range of the values that fit into the CFQueryParam
tag's CF_SQL_INTEGER
handling. Silently truncating this value means that the wrong value is passed-through to the underlying SQL query.
We can test this with a simple SELECT
-only query:
<cfscript> | |
// Largest signed-INT value that can fit in a 4-byte Java integer. | |
maxInt = 2147483647; | |
// Value that it outside of Java's signed INT space, but could be inside of the | |
// database's "UNSIGNED INT" column type. | |
largerInt = ( maxInt + 1 ); | |
``` | |
<cfquery name="test" result="result"> | |
SELECT | |
( <cfqueryparam value="#largerInt#" sqltype="integer" /> ) AS paramInt, | |
( <cfqueryparam value="#largerInt#" sqltype="bigint" /> ) AS paramBigInt, | |
( #largerInt# ) AS rawValue | |
; | |
</cfquery> | |
``` | |
// Output results of MySQL query and parameter binding. | |
dump( test ); | |
echo( "<br />" ); | |
dump( result ); | |
</cfscript> |
Note that we are taking a value that doesn't fit in Java's 32-bit signed Integer and we're using it in two CFQueryParam
bindings, one with CF_SQL_INTEGER
and one with CF_SQL_BIGINT
. And, when we run this Lucee CFML code, we get the following:

Notice that with the CF_SQL_INTEGER
type, the value went into the parameter binding as 2147483648, but was passed-through to the underlying SQL query as 2147483647 (as a silently-truncated value). The CF_SQL_BIGINT
binding, on the other hand, allowed the value through as is (2147483648).
The ramifications of this are very un-good! It means that you run the risk of pulling the wrong record back from the database. Granted, the scope of damage is limited in that every value over the CF_SQL_INTEGER
range would be silently truncated to the same, consistent value. But, it's still the wrong record being accessed.
I tried running this test in Adobe ColdFusion 2021 instance:
<cfscript> | |
// Largest signed-INT value that can fit in a 4-byte Java integer. | |
maxInt = 2147483647; | |
// Value that it outside of Java's signed INT space, but should be inside of the | |
// database's "UNSIGNED INT" column type. | |
largerInt = ( maxInt + 1 ); | |
test = queryExecute( | |
" | |
SELECT | |
( :param1 ) AS paramInt, | |
( :param2 ) AS paramBigInt, | |
( #largerInt# ) AS rawValue | |
; | |
", | |
{ | |
param1: { | |
value: largerInt, | |
cfsqltype: "cf_sql_integer" | |
}, | |
param2: { | |
value: largerInt, | |
cfsqltype: "cf_sql_bigint" | |
} | |
}, | |
{ | |
result: "result" | |
} | |
); | |
// Output results of MySQL query and parameter binding. | |
writeDump( test ); | |
writeOutput( "<br />" ); | |
writeDump( result ); | |
</cfscript> |
We still run into the same issue that a MySQL UNSIGNED INT
won't fit inside of the CFQueryParam
range using CF_SQL_INTEGER
; but, at least this throws an error instead of truncating:
Invalid data 2.147483648E9 for CFSQLTYPE CF_SQL_INTEGER.
Regardless of the problematic silent truncation in Lucee CFML or the thrown error in Adobe ColdFusion, what strikes me as the most problematic is that I didn't know about this; and, that it doesn't appear to be a [well] documented consideration. As I was looking into this, I found a CFSearching post from 2010 that talks about bindings and type-selection:
When you use
CF_SQL_INTEGER
you are using the java sql typeINTEGER
which "represents a 32-bit signed integer value ranging between -2147483648 and 2147483647". So obviously it does not have the capacity to represent the upper ranges of an unsignedINT
. For values larger than 2147483647 you would need to useCF_SQL_BIGINT
instead.
Call me crazy, but I think this should be messaged hard on the official documentation for the <cfqueryparam>
tag. This is information the people need to know!
Epilogue on Lucee Jira Ticket
After writing this, I took a look in the Lucee Jira install and I found a ticket that discusses the binding truncation. So, at least it's on their radar!
Want to use code from this post? Check out the license.
Reader Comments