SQL Data Types Returned From GetMetaData() Are Not Available For QueryNew() And Why That Should Scare You

Posted November 15, 2006 at 9:40 AM by Ben Nadel

Tags: ColdFusion

When you dump out a ColdFusion query's meta data:

  • <!--- Get the query meta data. --->
  • <cfdump var="#GetMetaData( qData )#" />

... you get information about the columns in the query. What I just noticed though is that the column data types returned from a standard SQL query do not line up with the data types that are available for ColdFusion's QueryNew() method for manually creating queries.

When I do this for my blog:

  • <!--- Get a single record for the demo. --->
  • <cfquery name="qTopEntry" datasource="...">
  • SELECT TOP 1
  • *
  • FROM
  • blog_entry
  • </cfquery>
  •  
  • <!--- Display meta data. --->
  • <cfdump var="#GetMetaData( qTopEntry )#" />

... among other column data types, I get:

INT
VARCHAR
TEXT
TINYINT
DATETIME

This completely lines up with the data types as defined in MS SQL Server. However, let's take a look at what is available for the QueryNew() method (as taken out of the help files):

Integer: 32-bit integer
BigInt: 64-bit integer
Double: 64-bit decimal number
Decimal: Variable length decimal, as specified by java.math.BigDecimal
VarChar: String
Binary: Byte array
Bit: Boolean (1=True, 0=False)
Time: Time
Date: Date (can include time information

Notice that there is no TEXT, TINYINT, or DATETIME data types available.

Now, most of the time this has no bearing on how we do anything at all. But, the second we start messing with queries manually, it's a whole new ball game. There are a lot of ColdFusion user defined functions (UDFs) out there that append queries or select random rows from queries or simulate queries. These generally require setting query cells manually, and this can cause runtime errors if the result query has strange data type values.

The ultimate problem is that the ColdFusion query is a custom Java result set of some sort underneath the covers. And while ColdFusion is very lenient about data types, Java is just the opposite. Stuff better be the correct data type or else. When you set a value in query cell ColdFusion does its best to "guess" which Java data type it should cast to, but a lot of the time it simply doesn't guess correctly. This will cause errors during a future query or query.

To overcome this, it is best practice to use JavaCast() when manually setting a query cell:

  • <!--- Set ID to 1. --->
  • <cfset qData[ "id" ][ 1 ] = JavaCast( "int", 1 ) />

But this raises another issue. The data types available for JavaCast() are:

boolean
int
long
float
double
string
null

As you can see, these data types are different from the QueryNew() data types.

To get all of this to play nicely, you need to start mapping your SQL data types to Query data types and Query data types to Java data types. Then, when you mess with a query, you need to pass in the appropriate data type based on the mapped association. This already sounds confusing right? Well it should, because it's fairly complicated.

And furthermore, you have to account for different databases. For instance, I might have a query that comes back from MS SQL Server with the data types:

BIT
MONEY
SQL_VARIANT

Are these universal data types across databases? I am guessing they are not. This means that your data mapping has to map data types for different databases. And what about custom data types? Don't some databases allow for custom data types? If not now, then maybe in the future. The problem is a bit out of hand.

What really needs to happens is ColdFusion needs to somehow force the casting of passed in data types to the underlying Java data types of the query. But this is almost impossible. If you look at my post on using the query object as a complex object iterator, you will see that I am passing in custom ColdFusion objects to query cells. How can this be accommodated? Or is this a hack that will eventually be killed.

And, this raises another issue: coupling. Your data maps now have to be directly tied to databases. This is obvious (I mean isn't that what data mapping is anyway?). But think about it, if your application gets a query from somewhere and then messes with it, your application is now coupled to the database implementation.

Now, you might think I am crazy right? Out of my gourde? Well, check this out:

  • <cfquery name="qTest" datasource="...">
  • SELECT TOP 1
  • id
  • FROM
  • blog_entry
  • </cfquery>
  •  
  •  
  • <!--- Add a row. --->
  • <cfset QueryAddRow( qTest, 1 ) />
  •  
  • <!---
  • Set the value and let ColdFusion do
  • the data type casting.
  • --->
  • <cfset qTest[ "id" ][ 2 ] = 3 />
  •  
  •  
  • <!--- Order by the id. --->
  • <cfquery name="qOrderBy" dbtype="query">
  • SELECT
  • id
  • FROM
  • qTest
  • ORDER BY
  • id ASC
  • </cfquery>

This throws the error:

null null <br>The error occurred on line 113.

The problem is that ColdFusion stores "3" as a string. It then passes this string into a field (id) which is actually an "int" in the database. Then, when you do the order by, the sorting method gets screwed because now it is sorting apples and oranges ("string"s and "int"s). To fix this, you could do:

  • <!---
  • Set the value and tell ColdFusion how to
  • cast the data for the data type.
  • --->
  • <cfset qTest[ "id" ][ 2 ] = JavaCast( "int", 3 ) />

This would work perfectly.

Are you seeing what is going on here? I need to know that the data type is an "INT" before I set the value. That means that this code is HIGHLY COUPLED to the database implementation.

Now, I may be taking this waaaaaay too seriously, but please, this should be throwing red flags about using any UDFs that manipulate queries.



Reader Comments

Nov 15, 2006 at 11:23 AM // reply »
48 Comments

Ben - You can also use the cfqueryparam sql types when using queryNew - they would likely match up better, no?

http://livedocs.macromedia.com/coldfusion/7/htmldocs/wwhelp/wwhimpl/common/html/wwhelp.htm?context=ColdFusion_Documentation&file=00001270.htm


Nov 15, 2006 at 11:32 AM // reply »
10,640 Comments

Todd,

AWESOME post. I was totally not aware of this. I didn't realize that CFSQLTYPES were available for use. This will certainly help some of the data mapping. Thank you very much for posting this.

However, there is still the issue of coupling of the code to the database implementation. Not every CFSQLTYPE maps to a database's data type. For instance, MS SQL Server uses CFSQLTIMESTAMP properly but will sometimes error out when using CFSQLDATE. Not that this is a huge problem....

I am more curious to see what the Framework people have to say about this. I know zero about frameworks, but I am pretty sure that most frameworks attempt to de-couple the database implementation to the rest of the code... or maybe I am way off here.


Nov 15, 2006 at 11:58 AM // reply »
48 Comments

No prob... I KNOW i've seen a udf somewhere that mapped the cf sql types to the proper db type, but I can't seem to find it.

Maybe someone else knows?


Nov 15, 2006 at 12:02 PM // reply »
10,640 Comments

That would be cool to see. I do it on a need-to-know basis for my code, but it would be really cool to see how someone does it on a "global" scale.


Nov 17, 2006 at 8:31 AM // reply »
48 Comments

Check this out - there is a matrix here that shows the mappings:

http://livedocs.macromedia.com/coldfusion/7/htmldocs/wwhelp/wwhimpl/common/html/wwhelp.htm?context=ColdFusion_Documentation&file=00000317.htm


Nov 17, 2006 at 9:52 PM // reply »
10,640 Comments

Todd,

Excellent link... but as you can see, not all databases support all the differnt CFSQL... types. Take that and talk about casting to Java data types, and things are still complicated.


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Feb 12, 2012 at 3:37 AM
Learning ColdFusion 8: CFImage Part III - Watermarks And Transparency
Hi Ben, Just to ask currently it is placed bottom right corner, if i need to replace the same rendered image on the bottom left side or in the bottom center, how that can be calculated. bottom ce ... read »
Feb 11, 2012 at 9:29 PM
Use jQuery's SlideDown() With Fixed-Width Elements To Prevent Jumping
I can't say how glad I am that I found your post. Thank you very much. ... read »
Feb 10, 2012 at 7:21 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
Update! Instead of $(eval(options.insertAfter)).after(data['insertData']); I now use: var ajaxNode = document.createElement('span'); var parent = $(eval(options.insertAfter))[0].parentNode; ... read »
Feb 10, 2012 at 6:18 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
encountered this same, what I consider, jQuery bug last week. I'm building a site in which I load some content via AJAX. This content contains Linkedin share button placeholders which Linkedin API ne ... read »
Feb 10, 2012 at 11:30 AM
Cross-Origin Resource Sharing (CORS) AJAX Requests Between jQuery And Node.js
After you understand the concepts here, this is an awesome cheatsheet for enabling CORS in just about anything http://enable-cors.org/ ... read »
JM
Feb 10, 2012 at 9:10 AM
My Safari Browser SQLite Database Hello World Example
@Amy, Here is a very good tutorial on how to use JOIN: http://www.sqltutorial.org/sqljoin-innerjoin.aspx ... read »
Feb 10, 2012 at 4:42 AM
Building A Twitter-Inspired RESTful API Architecture In ColdFusion
This is great, very useful Ben. I spotted a small typo in the api.cgm listing: <cfthrow type="Unauthroized" /> Cheers Stefan ... read »
Feb 9, 2012 at 10:35 PM
CFDirectory Filtering Uses Pipe Character For Multiple Filters (Thanks Steve Withington)
I was wondering if there would be a filter you could apply so that you got everything but what you included in the filter. As in show me all docs that are not a .pdf. ... read »