ColdFusion Query of Queries Unexpected Data Type Conversion

Posted November 1, 2006 at 4:03 PM

Tags: ColdFusion

I cam across a really strange ColdFusion query of query error this morning involving some unexpected data type conversion. The query of query seems to be ignoring BOTH the data type passed in as well as the target field's data type and converts automatically to a date. Let me demonstrate.

First, let me build a simple query using ColdFusion's QueryNew() method:

 Launch code in new window » Download code as text file »

  • <!---
  • Create new query. Set up column data types
  • to both be VARCHAR.
  • --->
  • <cfset qGirl = QueryNew(
  • "name, date",
  • "VARCHAR, VARCHAR"
  • ) />
  •  
  •  
  • <!--- Add rows. --->
  • <cfset QueryAddRow( qGirl, 3 ) />
  •  
  •  
  • <!---
  • Set row/cell data values. Use JavaCast() to make
  • sure ColdFusion doesn't have to guess at data types.
  • --->
  • <cfset qGirl[ "name" ][ 1 ] = JavaCast( "string", "Libby" ) />
  • <cfset qGirl[ "date" ][ 1 ] = JavaCast( "string", "2006-10-01" ) />
  •  
  • <cfset qGirl[ "name" ][ 2 ] = JavaCast( "string", "Sarah" ) />
  • <cfset qGirl[ "date" ][ 2 ] = JavaCast( "string", "2006-09-01" ) />
  •  
  • <cfset qGirl[ "name" ][ 3 ] = JavaCast( "string", "Cindy" ) />
  • <cfset qGirl[ "date" ][ 3 ] = JavaCast( "string", "2006-08-01" ) />

At this point, I have a 3-row query that has two columns, both of which are VARCHAR fields. When I set the data into the cells, I do so using ColdFusion's JavaCast() method. This takes out any guessing that ColdFusion has to do. We are explicitly telling ColdFusion to use STRING values in the underlying Java record set.

Nothing magical yet. But now, let's try to perform a ColdFusion query of queries getting the first girl based on the date column (NOTE: the date column is a VARCHAR field with String data):

 Launch code in new window » Download code as text file »

  • <!--- Get first girl. --->
  • <cfquery name="qFirstGirl" dbtype="query">
  • SELECT
  • name,
  • [date]
  • FROM
  • qGirl
  • WHERE
  • [date] =
  • <cfqueryparam
  • value="#qGirl.date#"
  • cfsqltype="CF_SQL_VARCHAR"
  • />
  • </cfquery>

Notice that I am not touching any data myself. I am only passing in the data that is already stored in the query (via qGirl.date). This should return one record containing the first girl in the original query, but, in fact, it returns an empty record set.

So what's the problem. Well, I thought maybe it was JavaCast() call. Even though that is a best practice, I thought maybe it was confusing the record set. So I removed. This did not fix the error. Then, I thought maybe it was the CFQueryParam tag - maybe there was something about it that was messing up the data. So I tried removing it and putting in a literal string:

 Launch code in new window » Download code as text file »

  • <!--- Get first girl. --->
  • <cfquery name="qFirstGirl" dbtype="query">
  • SELECT
  • name,
  • [date]
  • FROM
  • qGirl
  • WHERE
  • [date] = '#qGirl.date#'
  • </cfquery>

This also did NOT fix it. So, what the heck was going on?!? As a last ditch effort, I tried echoing both the original data field and the passed in data to see what was going on:

 Launch code in new window » Download code as text file »

  • <!--- Echo back data values. --->
  • <cfquery name="qEcho" dbtype="query">
  • SELECT
  • [date],
  • (
  • <cfqueryparam
  • value="#qGirl.date#"
  • cfsqltype="CF_SQL_VARCHAR"
  • />
  • ) AS target_date
  • FROM
  • qGirl
  • </cfquery>

Notice that I am selecting the existing data (in the qGirl.date column) and creating a calculated column based on the CFQueryParam of the first records date value. This gives me the following CFDump:


 
 
 

 
 
 
 
 

Very interesting! It seems the ColdFusion query of query is totally ignoring the data type that the CFQueryParam is requesting. But, while I can't see it visually, it must also be ignoring the [date] data type as well (otherwise we would be getting a match). It seems to automatically convert the value to a Date/Time stamp just because it "looks" like date/time object.

To test this, I tried passing in data that did not look exactly like a date:

 Launch code in new window » Download code as text file »

  • <!--- Echo back data values. --->
  • <cfquery name="qEcho" dbtype="query">
  • SELECT
  • [date],
  • (
  • <cfqueryparam
  • value="-#qGirl.date#-"
  • cfsqltype="CF_SQL_VARCHAR"
  • />
  • ) AS target_date
  • FROM
  • qGirl
  • </cfquery>

Notice that the value attribute of the CFQueryParam tag now has "-" in it. This gives us the following CFDump:


 
 
 

 
 
 
 
 

Interesting! Why is it doing this? Why is it ignoring my requests and converting data on it own?

To get around this, I tried to cast the passed in CFQueryParam value to a VARCHAR. But that DIDN'T WORK. What is going on! Finally, I got the whole thing to work if I CAST both values to date:

 Launch code in new window » Download code as text file »

  • <!--- Get first girl. --->
  • <cfquery name="qFirstGirl" dbtype="query">
  • SELECT
  • name,
  • [date]
  • FROM
  • qGirl
  • WHERE
  • CAST( [date] AS DATE ) =
  • CAST(
  • <cfqueryparam
  • value="#qGirl.date#"
  • cfsqltype="CF_SQL_VARCHAR"
  • />
  • AS DATE
  • )
  • </cfquery>

This finally gives us the desired output:


 
 
 

 
 
 
 
 

This seems like a really whacky behavior.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Other Searches  |  Print Page




Learning ColdFusion 9 - ColdFusion 9 tutorials, samples, examples, demos

Reader Comments

Nov 1, 2006 at 11:11 PM // reply »
109 Comments

QofQ has some nice uses and it makes certain things easier, but it is also probably the source of more unexpected behavior and frustration than any other feature.


Nov 2, 2006 at 4:37 AM // reply »
64 Comments

Weird. But none of this surprises me of QoQ, really: it really doesn't take much effort to confuse it.

Have you raised it as a bug?

Good investigation, btw.

--
Adam


Nov 2, 2006 at 7:23 AM // reply »
7,486 Comments

Bug has been reported.

Yeah, ColdFusion query of queries does have some strange things in it, but gosh darnit, it's one of the most bad-ass features of the ColdFusion MX family.


Nov 2, 2006 at 8:28 AM // reply »
64 Comments

If it worked properly, you'd be right. However it's so flaky that it's really only useful for doing the most basic things. It feels like it's only a beta (which is sad, given it's been around since... when did CF5 come out... 2001?).

So I agree than the words "bad" and "ass" (although I'd say "arse") are suitable to describe it. Just not quite the way you meant ;-)

--
Adam

PS: much prefer your "De-spam" approach to all that "captcha" nonsense that's the current trend.


Nov 2, 2006 at 8:57 AM // reply »
45 Comments

It would be interesting to see a getMetaData() for your examples just to confirm the data conversions you are seeing.


Nov 2, 2006 at 12:58 PM // reply »
7,486 Comments

Adam,

Glad you like the de-spamming technique.


Apr 13, 2007 at 2:21 PM // reply »
1 Comments

This may sound really trite... but did you try changing the query equivalency?

from

[date] = '#qGirl.date#'

to

[date] like #qGirl.date#

?

It's just a thought, because date types are not like other data types. They are weird objects, so it's possible that using the = operator doesn't work with them.


Apr 13, 2007 at 2:26 PM // reply »
7,486 Comments

I did not try that [no suggestion is trite ;) ]. I am not sure it can work, though, I cannot be sure until I test it.


Mar 31, 2008 at 3:37 PM // reply »
4 Comments

This is just what I needed! I retrieved a query and modified it using QueryAddColumn:
<cfset ArraySet(MyArray,1,themembers.recordcount,JavaCast("boolean",true))>
<cfset QueryAddColumn(themembers,"Signer","bit",MyArray)>

Notice that I used JavaCast AND set the column type in QueryAddColumn. That wasn't enough. To pull data out I had to do:

<cfquery dbtype="QUERY" name="thequery">
Select * from this.members
where cast(signer as bit) = cast(<cfqueryparam value="true" cfsqltype="cf_sql_bit"> as bit)
</cfquery>

Otherwise it kept telling my column didn't exist.


Apr 1, 2008 at 9:00 AM // reply »
7,486 Comments

@Steve,

It's probably because you were using SELECT *. If you took out the * and wrote in the column names, it probably would work fine without the casting. It's just caching the query structure (my guess).


Feb 24, 2009 at 12:20 AM // reply »
1 Comments

Can I just say that this absolutely saved me! Thank you so much for posting this! This little bug was driving me nuts!


May 27, 2009 at 11:10 AM // reply »
1 Comments

I just stumbled upon this entry after a google search. I was having a single digit integer (which was stored on the database as a varchar) being called a double in a QoQ.

I tried pblecha's suggestion to use a like and unfortunately that did not work. However, I then tried to CAST as VARCHAR and that did work.

Here is the original query:

SELECT frmtm, totm
FROM ProfileStruct.DOWControls
WHERE dow = <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#DayValue#">
AND seq = <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="2">;

And I changed it to:

SELECT frmtm, totm
FROM ProfileStruct.DOWControls
WHERE CAST(dow AS VARCHAR) = <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#DayValue#">
AND CAST(seq AS VARCHAR) = <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="2">;

I was actually having problems with both conditions of the predicate and now they work fine with CAST. For the record, all the fields are stored as VARCHAR2 on an Oracle database. dow is a 3 character representation of the weekday (MON, TUE, WED, ...) and seq is either 1 or 2.

It just shows that there is still a bunch of flakiness with CF7's QoQ.


Jun 1, 2009 at 1:59 PM // reply »
7,486 Comments

@Frank,

Yeah, I would like going forward for QoQ to enforce more appropriate data types. The problem with that, however, is that it is currently quite flexible (you can put *anything* into a query). Forcing more data constraints might break a lot of code. We'll see. At least this error only pops up from time to time.


Aug 14, 2009 at 1:40 AM // reply »
11 Comments

I wish there was a way to change the column data types on the fly. I've been playing around with jQuery AJAX and CFWDDX and I've noticed that when I pass a query object as a WDDX packet and re-construct it using CFWDDX the data types can get all messed up -- specifically when dealing with dates. They get even more messed up if the first row of a query contains a NULL value.


Aug 17, 2009 at 2:17 PM // reply »
7,486 Comments

@David,

Yeah, that would be cool. That's why I like converting query data to array/struct data, so I know how it's all going back and I can do any massaging that needs to be done.


Nov 4, 2009 at 11:56 AM // reply »
1 Comments

I had the exact same problem with a regular query on a varchar data type. I used the CAST() function and it solved the problem, but it only errored when certain strings were passed in. E.G. 'mfrscan' worked fine, but 'mfrbillback' threw sql conversion error. Very strange?


Nov 4, 2009 at 1:03 PM // reply »
7,486 Comments

@Charlie,

Yeah, that is strange - especially with values that are not too different. Query of queries is awesome, but can definitely be frustrating from time to time.


Nov 4, 2009 at 1:33 PM // reply »
11 Comments

@Charlie

Query of Queries is like a 3 year old with ADHD. You should never leave it alone to make any decisions for you. I highly recommend using a combination of JavaCast (when building your QofQ manually) and Cast() in order to ensure the data matches your expectations.


Nov 4, 2009 at 1:35 PM // reply »
7,486 Comments

@David,

Yeah, JavaCast() is awesome; but, I've even had *some* issues with that. Sometimes, ColdFusion just wants to ignore everything about your column and cast data!!


Jan 4, 2010 at 5:23 PM // reply »
3 Comments

I noticed (with CF8) similar QofQ date bizareness, trying to test for date X being before or on now().

If I have a query "foo" and it cfdumps and the "dateX" col looks and smells like a date field, I'd try "SELECT * from foo where cast([dateX] as date) <= cast(<cfqueryparam cfsqltype="CF_SQL_DATE" value="#now()#"/> as date)" with no luck. But what worked? Doing the cast as integer. "SELECT * from foo where cast([dateX] as integer) <= cast(<cfqueryparam cfsqltype="CF_SQL_DATE" value="#now()#"/> as integer)" got me the correct results

Yeah, CF QofQ is very cool, but in a maddening unpredictable way. Argh.


Jan 4, 2010 at 6:49 PM // reply »
7,486 Comments

@Alan,

That's frustrating when not even a CAST() call will fix your problem (or at least, not with the more appropriate data type :)).


Feb 10, 2010 at 12:27 PM // reply »
3 Comments

Ben, I'll pass along another QofQ datatype disaster I went through a couple days ago. An app that uses QofQ started throwing "cannot conver value '' to number" errors for no apparent reason. The QofQ had been CASTed to death, and I could not come to a solution, but did come up with a strange but successful jury-rig.

I seralized the original query with CFWDDX, then deseralized it back to a query again. After that, the subsequent QofQ worked fine. Go figure.

Anyhow, I'm not trying to hijack this thread or anything, but doing a google of "cf query of query" brings this page up so it's become a good resource for QofQ victims. Thanks for your excellent blog.


Feb 10, 2010 at 10:45 PM // reply »
7,486 Comments

@Alan,

Please, never feel bad about contributing to the conversation; every time someone brings up a new point, it's an opportunity for us to learn something new.

The converting "" to numeric, maybe that happens if the value is NULL? Although I think QoQ should handle NULLs properly... although who knows, right??


Feb 24, 2010 at 3:33 PM // reply »
1 Comments

I display usage quarterly for various products in tabular format. I displays products in rows and monthly usage in column. When I query my sql database, I may get 1-3 records based on the entry. I create a coldfusion query which has 0 as usage for all month for a given product. I union both the query and sum up usage grouping date so I will get a record for every month irrespective for data in database. My problem starts while merging data as date format are different in cf query ans sql query. Data do not group due to date difference (sql 2010-10-01 00:00:00.0 do not match with {ts '2010-10-01 00:00:00'}). I cast sql date while merging data and bingo... it works.. thanks for the solution given it this article.

select start, cast(usagedate as Date) as usagedate from sql.qrymaxrow
union all select start, usagedate from cfm.rowscounter


Post Comment  |  Ask Ben

Recent Blog Comments
Mar 11, 2010 at 9:29 PM
Tim Cracked The GMail - CFMailPart Puzzle!
I've been wrestling with the CFMAIL tag and CFMAILPARTS for several days now and have found issues with the CF implementation even in CF9! What I have learned so far is: 1. Using only one CFMAILPART ... read »
Mar 11, 2010 at 6:09 PM
Ask Ben: Building An AJAX, jQuery, And ColdFusion Powered Application
@Eric, Neat trick, I was able to get rid of most of the lines of whitespace following your advice. Some whitespace still remains. With a bit of playing around, I found that the remaining whitespa ... read »
Mar 11, 2010 at 4:56 PM
Ask Ben: Building An AJAX, jQuery, And ColdFusion Powered Application
I've struggled with returning JSON from ColdFusion CFCs for a while because I (mysteriously) get lots of white space/new lines that appear before the actual JSON result (check the response in Firebug ... read »
Mar 11, 2010 at 3:24 PM
Ask Ben: Using jQuery To Act On A Click Event Based On The Target Element
@TripeL, Awesome :) Glad it was helpful. ... read »
Mar 11, 2010 at 3:23 PM
Ask Ben: Using jQuery To Act On A Click Event Based On The Target Element
WOW...that's what I'm looking for. The code examples are very helpful. Thanks ... read »
Mar 11, 2010 at 1:20 PM
What Is The Best Time Of Day To Workout?
Well I am glad I stick to mid afternoon / evening work outs. Interesting find! ... read »
Mar 11, 2010 at 1:13 PM
CFHTTPSession.cfc For Multi-CFHttp Requests With Maintained Session
It worked for what I needed perfectly the first try... this is huge, you have made my week! ... read »
Mar 11, 2010 at 12:54 PM
Using Appropriate Status Codes With Each API Response
I forgot to mention that using this application stack allows me to separate as much of the core/business logic into the API Library which leaves the web applications just to handle presentation layer ... read »