Upgraded to ColdFusion 9 getting strange cfif error - coldfusion

I have a couple of places that have some code like this:
<cfinvoke component="#application.path#cfc/eval_faculty" method="getPresentations" returnvariable="presentations">
<cfinvokeargument name="id" value="#eval_id#">
<cfinvokeargument name="evalYear" value="#eval_semester#">
<cfinvokeargument name="department" value="#general.dept#">
</cfinvoke>
<cfset prescheck = 0>
<cfloop query="presentations">
<cfif local eq "" and regional eq "" and national eq "" and international eq "">
<cfset prescheck = prescheck+1>
</cfif>
</cfloop>
I get this error:
Complex object types cannot be converted to simple values.
None of these values in the cfif statement is a complex object.
This worked fine in ColdFusion 8. We just upgraded to ColdFusion 9...
The error occurs on the line with <cfif local eq "" ... >
Any ideas?

<cfif local eq ""
It could be that LOCAL is now a system scope in CF9, like FORM, URL, etecetera. So CF complains when you try to perform a string comparison on it because it is a structure. If LOCAL represents a simple variable in your old code, try using a different variable name.
Update: From the comments, if LOCAL is the name of a column in your query, you could either use a sql alias to give it another name:
SELECT Local AS LocalAlias FROM Table
... or use a fully qualified variable name:
<cfif queryName.local ...>

Related

ColdFusion 10 Error occurring only in IE9

My customer is encountering the error The 2 parameter of the Left function, which is now -1, must be a positive integer after migrating from ColdFusion 8 to ColdFusion 10. The page performs as expected in Firefox 31 and Chrome 36 on Windows 7. However, this error occurs in IE9 but not IE7. Are there different security settings in IE9 that prohibit the processing of the code?
The snippet of code in question is:
<!--- Populate the UserInfo struct --->
<cfloop INDEX="item" LIST="#COOKIE.MYELVIS_USERINFO#" DELIMITERS="&">
<cfset delim = #FindOneOf("=",item)#>
<cfif (len(item)-delim)>
<cfset UserInfo[UCase(left(item,delim-1))] = right(item,len(item)-delim)>
<cfelse>
<cfset UserInfo[UCase(left(item,delim-1))] = "">
</cfif>
</cfloop>
I'm torn to say it's a code error when it works in other browsers, just not IE9. Any thoughts? Thank you.
Perhaps have him clear cookies, he might have a holdover from cf 8 and perhaps the two versions encode differently. It looks like, if this is possible, one of the values doesn't contain a =.
Upon reviewing your code, I think this is what you need.
<cfloop INDEX="item" LIST="#COOKIE.MYELVIS_USERINFO#" DELIMITERS="&">
<cfset delim = #FindOneOf("=",item)#>
<cfif (delim) and (len(item)-delim)>
<cfset UserInfo[UCase(left(item,delim-1))] = right(item,len(item)-delim)>
<cfelse>
<cfset UserInfo[UCase(item)] = "">
</cfif>
</cfloop>

Scoping: Local vs Var

I'm new to CF so this may be a basic question. But I've heard I should use local for objects inside functions due to how scoping works in CF. But what about 'var'? Is var the same as using local?
e.g.
function MyFunction()
{
local.obj = {};
}
Is this the same as:
function MyFunction()
{
var obj = {};
}
If they aren't the same, what is the difference between them? And when should I be using either of them?
They are very similar, but not exactly the same. Both only exist inside of a function but they work slightly differently.
The var version works it way through all the default variable scopes. See http://help.adobe.com/en_US/ColdFusion/9.0/Developing/WSc3ff6d0ea77859461172e0811cbec09af4-7fdf.html
Local will match only a variable in a local scope. Consider the following
<cffunction name="himom">
<cfoutput>
<p><b>try 0:</b> #request_method#</p>
<!--- you might think that the variable does not exist,
but it does because it came from cgi scope --->
</cfoutput>
<cfquery name="myData" datasource="Scorecard3">
SELECT 'This is via query' AS request_method
</cfquery>
<!--- Data is now being loaded into a query --->
<cfoutput query="myData">
<p><b>try 1:</b> #request_method#</p>
</cfoutput>
<!--- This one now came from the query --->
<cfset var request_method = "This is Var">
<!--- We now declare via var --->
<cfoutput query="myData">
<p><b>try 2:</b> #request_method#</p>
</cfoutput>
<!--- the query version disappears and now
the var version takes precedence --->
<cfset local.request_method = "This is local">
<!--- now we declare via local --->
<cfoutput query="myData">
<p><b>try 3:</b> #request_method#</p>
</cfoutput>
<!--- The local method takes precedence --->
<cfoutput>
<p><b>try 4:</b> #request_method#</p>
<!--- in fact it even takes precedence over the var --->
<p><b>try 5:</b> #local.request_method#</p>
<!--- there is no question where this comes from --->
</cfoutput>
</cffunction>
<cfset himom()>
Results of the above
try 0: GET
try 1: This is via query
try 2: This is Var
try 3: This is local
try 4: This is local
try 5: This is local
In summary
When developing, you could use either to make sure that variables only exist inside of a function, but always prefixing your variables with local goes a long way in making sure that your code is clearly understood
In ColdFusion 9+, using the local scope and the var directive in a ColdFusion CFC provide the same result.
Ben Forta explains it here:
http://forta.com/blog/index.cfm/2009/6/21/The-New-ColdFusion-LOCAL-Scope
I would recommend using the local. notation as it is more explicit.

Error with undefined variables while looping over a query

I have an error while looping over a query using cfloop.
When I use a cfdump on the query (inside the loop, mind you), I can see all the data just fine. But when I try to get the value of each variable as you normally do in cfloop, I get a message that says they are undefined. I then changed each variable to reference the query specifically, and now the problem is that the variable is undefined in the query. Here's the code:
<cffunction name="writeCourses">
<cfargument name="recordset" required="yes" type="query">
<cfif recordset.RecordCount NEQ 0>
<cfset temp = "">
<cfoutput>
<cfloop query="recordset">
<!--- <cfdump var="#recordset#"> <cfabort/> --->
<cfset temp = temp & "<strong>#recordset.courseType# #recordset.courseNum# ">
<cfif isDefined("recordset.courseTHM") AND recordset.courseTHM EQ 1>
<cfset temp = temp & "(#left(recordset.courseNum,3)#4) ">
</cfif>
<cfif isDefined("recordset.courseName")>
<cfset temp = temp & "#recordset.courseName# </strong><br>">
</cfif>
<cfset temp = temp & "#recordset.courseDESC#<br>">
<cfset temp = temp & "#recordset.courseHours#<br><br>">
</cfloop>
</cfoutput>
<cfelse>
<cfset temp = "">
</cfif>
<cfreturn temp>
</cffunction>
So as you can see, each variable is enclosed in ## tags. Originally none of them were proceeded by recordset. but they were still undefined. And when I uncomment the cfdump and cfabort tags, those work fine and I can see the recordset query with all the data as it should be.
Every other time I have used cfloop with a query it works as expected. Also, I did not write this code, I am having to modify it (the original author no longer works here).
Here's an example of the recordset dump:
The error message:
Detail: [empty string]
ErrNumber: 0
Message: Element COURSETYPE is undefined in RECORDSET.
Resolvedname: RECORDSET
The error line is:
<cfset temp = temp & "<strong>#recordset.courseType# #recordset.courseNum# ">
<cfif isDefined("recordset.courseTHM") AND recordset.courseTHM EQ 1>
<cfset temp = temp & "(#left(recordset.courseNum,3)#4) ">
</cfif>
<cfif isDefined("recordset.courseName")>
<cfset temp = temp & "#recordset.courseName# </strong><br>">
</cfif>
That's all one line :/
The stored procedure/function calling the above:
<cffunction name="getCoursesByDept">
<cfargument name="deptCode" required="yes" type="string">
<CFSTOREDPROC procedure="dbo.GetCourses" datasource="WebCatalog">
<CFPROCPARAM type="IN" dbvarname="#deptCode" value="#deptCode#" cfsqltype="CF_SQL_CHAR">
<CFPROCRESULT name="result">
</CFSTOREDPROC>
<cfinvoke method="writeCourses" recordset="#result#" returnvariable="output">
<cfreturn output>
</cffunction>
Your problem appears to be failure to scope. Here are your first 4 lines:
<cffunction name="writeCourses">
<cfargument name="recordset" required="yes" type="query">
<cfif recordset.RecordCount NEQ 0>
<cfset temp = "">
Try it like this:
<cffunction name="writeCourses">
<cfargument name="recordset" required="yes" type="query">
<cfset var temp = "">
<cfif arguments.recordset.RecordCount NEQ 0>
The differences are the use of the var keyword for your local variable temp, and adding the arguments scope to the recordset variable.
(In addition to Dan's comments ...)
If [the procedure] can't find anything it returns a query containing
an error message (i.e. no course found)
Then that means the COURSETYPE column does not always exist in the resultset, which is exactly what the error message is reporting. If the procedure returns any result, regardless of the contents, the code inside the cfif block will execute. Since the first line of code uses that column, without verifying it exists, it would cause the exact error you are seeing.
Also, as I mentioned in the comments, you really need to localize the function variables result, output, temp, etectera. Lack of var scoping can create problems, even within same page, if you reuse variable names. As #Dan suggested you should fully scope all variables - in particular, the function arguments.
(As an aside, I understand you are modifying existing code, but the error message should really be handled in CF, not inside the procedure. The procedure's job is just to return data. The CF code should check the recordCount and take the appropriate action if no records are found.)

ColdFusion local scope inside object literal

I'm seeing some weird behaviour when using CF's local scope in an object literal, in function arguments. But only when executed inside a loop...
Example code:
<cffunction name="f">
<cfoutput>
<cfset LOCAL.foo = 123 />
<!--- Works fine --->
#serializeJSON({blah = LOCAL.foo})#
<!--- Works fine --->
<cfloop from=1 to=1 index="i">
<cfset bar = {blah = LOCAL.foo} />
#serializeJSON(bar)#
</cfloop>
<!--- Element FOO is undefined in LOCAL --->
<cfloop from=1 to=1 index="i">
#serializeJSON({blah = LOCAL.foo})#
</cfloop>
</cfoutput>
</cffunction>
<cfset f() />
PS: serializeJSON() is just for example purposes. This is happening in any function I've tested where one of the arguments is a struct.
Works just fine in Railo.
It also doesn't make any difference if using any other container instead of local scope, also it's impossible to catch this with cftry.
If you serialize just local scope within the loop:
<cfloop from=1 to=1 index="i">
#serializeJSON(local)#
</cfloop>
Result is:
{"ARGUMENTS":{},"___IMPLICITARRYSTRUCTVAR1":{"BLAH":123},"___IMPLICITARRYSTRUCTVAR0":{"BLAH":123},"FOO":123}
Looks like a bug. Mind filing?
LOCAL is a scope used only within functions. If you try to create a LOCAL scope variable outside of a function, it will fail.
I will write up a test and prove it to you in a minute....
UPDATE Actually, I have CF 8 at work and can't test it.
In CF8 and below, you can set LOCAL.Foo, but it's not really a CF scope.
In CF9 and above, LOCAL can be set only within a function.
<cffunction>
<cfset LOCAL.foo = 1>
<cfreturn LOCAL.foo>
</cffunction>

In ColdFusion, how do I determine if a query string variable exists?

In ColdFusion, how can I determine if a variable exists within the querystring without throwing an error attempting to check it?
There are two options.
The first is to use cfparam to define a default value eg:
<cfparam name="url.varname" type="string" default="" />
This ensures that you can always refer to url.varname
The second is to use isDefined or structKeyExists to test for the presence of the variable:
<cfif isDefined("url.varname") and url.varname eq 42> do something </cfif>
or
<cfif structKeyExists(url, "varname") and url.varname eq 42> do something </cfif>
I have used this approach in many places.
At the top of the page:
<cfparam name="request.someVal" default="request.defaultVal">
Later in the page or custom tag, check for the value of the request.someVal variable, without fear of it crashing, since it has a default value.
<cfif ("request.someVal" eq "something")>
...
</cfif>
.
.
.
In <cfscript>, you can
param url.varname; // throws error if it does not exist
param url.varname = ""; // sets value it was not already set