An interesting bug (not a ColdFusion bug, but a user bug) cropped up in a discussion last week in the ColdFusion IRC channel. Unfortunately I don't remember his IRC name (Edited: It was Baz!), but after we worked together on this for an hour or so, he was kind enough to send me a real nice and simple code sample to replicate the issue.
So let's back up a bit and talk about what he saw. The user had a web service that returned a structure. Before he actually used it as a web service he did his testing locally like so:
<cfset Local=createobject('component','test').invokeFunction() />
<cfdump var="#Local#" label="Local">
This worked perfectly fine for him. Let's take a look at the code he was using in the component (and give yourself a pat on the back if you see the problem):
<cfcomponent output="false">
<cffunction name="invokeFunction" access="remote" returntype="struct">
<cfscript>
var MyQuery=QueryNew('ColumnA,ColumnB');
var MyStruct=structnew();
// Populate 1 row of the query
QueryAddRow(MyQuery, 1);
QuerySetCell(MyQuery, 'ColumnA', 'ValueA');
QuerySetCell(MyQuery, 'ColumnB', 'ValueB');
// Copy the values of that row to a struct
MyStruct.KeyA=MyQuery['ColumnA'];
MyStruct.KeyB=MyQuery['ColumnB'];
</cfscript>
<cfreturn MyStruct />
</cffunction>
</cfcomponent>
As you can see, the function creates a query and then copies values to a structure. His real code was obviously a bit more complex. So he then switched to using a web service:
<cfset Remote=createobject('webservice', 'http://localhost/test.cfc?WSDL').invokeFunction() />
<cfdump var="#Remote#" label="Remote">
And something odd occurred. This is the output from running both his local and remote tests:
As you can see - the remote call returned a structure of arrays - not a structure of simple values. Why? While I can't answer why one method returned different data then the other, I can point out the problem with the code in the method. Notice how he copied the values from the query:
// Copy the values of that row to a struct
MyStruct.KeyA=MyQuery['ColumnA'];
MyStruct.KeyB=MyQuery['ColumnB'];
When working with a query like this, you are supposed to specify which particular row you want. When called locally, the function was able to guess he meant the first row, but when called remotely this no longer worked. Again - I don't know why, but the fix is simple enough:
// Copy the values of that row to a struct
MyStruct.KeyA=MyQuery['ColumnA'][1];
MyStruct.KeyB=MyQuery['ColumnB'][1];
What helped diagnose this issue was inspecting the data he had in MyStruct. When he examined the particular Java class when invoked remotely, he saw that it was a QueryColumn (I don't have the full class name in front of me, but that is the approximate name), not a simple value as he would have expected.