6
votes

The function Duplicate of ColdFusion 9 is supposed to return a clone, also known as a deep copy, of a variable, with no reference to the original variable. That's supposed to be true for complex objects, such as structures and queries.

I'm working on a code that uses a query object that is in the APPLICATION scope. This query needs to be modified locally for use in a particular page and I need to know what the original query string was (which is in the query object's metadata). So in this case, creating a deep copy of the original query is the most sensible solution.

But unfortunately, it looks like ColdFusion doesn't clone the whole object, but only its result set, thus losing all the metadata.

That's not the behaviour I expected from Duplicate, and I don't find it consistent with what happens when duplicating other kinds of complex objects.

One solution to my problem would be passing the result set AND the sql string as separate arguments to the function.

I'd like to know, however, if you have a more elegant solution, and/or throw a bit of light on the query Duplicate issue.

Here's some code that proves the issues for query Duplicate:

<cfquery name="qry" datasource="mydatasource">
    SELECT "blue" AS colour, "pear" as fruit
</cfquery>
<cfset qry_copy = qry>
<cfset qry_deepcopy = duplicate(qry)>

<cfdump var="#qry#" label="Original query" />
<cfdump var="#qry_copy#" label="Copy of the query (by reference)" />
<cfdump var="#qry_deepcopy#" label="Deep copy of the query (by value)" />

<cfdump var="#qry.getMetaData().getExtendedMetaData()#" label="Metadata of the original query" />
<cfdump var="#qry_copy.getMetaData().getExtendedMetaData()#" label="Metadata of the copy of the query" />
<cfdump var="#qry_deepcopy.getMetaData().getExtendedMetaData()#" label="Metadata of the deep copy of the query" />

A query, its copy and its deep copy


Edit:

In a nutshell, these are my conclusions so far:

  • The metadata doesn't get duplicated because qry.getMetaData() is a JAVA class and, as mentioned in the documentation, If an array element or structure field is a COM, CORBA, or JAVA object, you cannot duplicate the array or structure.
  • The documented ColdFusion function to get the metadata of a query is getMetaData(qry).
  • I've fixed my problem by storing both qry and getMetaData(qry) in the APPLICATION scope.
1
(Edit) There is note in the documentation that you cannot duplicate java objects that may be relevant. So I do not think it makes any guarantees about copying the entire object, if portions of it consist of java objects.Leigh
Leigh - I guess I would think of any function that borrows into a CF specific type (as in query) to be a CF and not a Java class or object. But it is pretty down in the weeds so you are probably right about that.Mark A Kruger
This all makes for interesting reading, but, as Scott mentioned, the sql from the original query is still available from that object. If it's not the same every time, and it's in the application scope, you might be doing something scary.Dan Bracuk
You're calling internal methods of the Query class, not ColdFusion's getMetadata() function. Those internal methods are not supposed to be used directly, so all bets are off as far as how inbuilt CFML functions deal with them.Adam Cameron
@XeviPujol - Most likely, but as I mentioned in my deleted comment it is also an undocumented method you are using, so nothing about it is guaranteed anyway.Leigh

1 Answers

2
votes

Hmmm... that is to my way of thinking a bug. My guess would be that there is a reference to the metadata that is not pulled into the deep copy. If it was something else the "getMetaData()" function called on the deep copy would throw an error.

Have you tried adding the "result" attribute to the query tag? As in:

<cfquery name="qry" datasource="mydatasource" result="myresult">
    SELECT "blue" AS colour, "pear" as fruit
</cfquery>

That might create a property or class embedded in the result set - although "result" is actually set in the variables scope I believe. I need to think about this.