Error with undefined variables while looping over a query - coldfusion

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.)

Related

ListRemoveDuplicates result has a comma at the end

When using listRemoveDuplicates in Lucee, it removes the duplicate values but still leaves the delimiter in end of the value.
Ex:
<cfset myUsers = 'sathish,sathish'>
<cfset removeDups = listRemoveDuplicates(myUsers)>
<cfdump var="#removeDups#" />
This produces the output:
sathish,
However, when checking this same function with Adobe ColdFusion, it produces the correct value (no comma at the end):
sathish
How can I omit the comma delimiter in Lucee?
Surfing the Lucee documentation, I found the ListCompact() function which returns the correct value.
<cfset myUsers = 'sathish,sathish'>
<cfset removeDups = listCompact(listRemoveDuplicates(myUsers))>
<cfdump var="#removeDups#" />
You can also create a custom function like this:
<cffunction name="UDF_listRemoveDuplicates" access="public">
<cfargument name="list" type="string" required="yes">
<!--- Hash Map to maintain the order --->
<cfset local.hashMap = createObject("java", "java.util.LinkedHashMap").init()>
<!--- Add list values as key to Hash Map --->
<cfloop list="#arguments.list#" index="local.value" delimiters=",">
<cfset local.hashMap[local.value] = 1>
</cfloop>
<!--- Return list of Keys --->
<cfreturn structKeyList(local.hashMap, ",")>
</cffunction>
<cfoutput>#UDF_listRemoveDuplicates("sathish,sathish")#</cfoutput>
This should work on both. (Not tested on Lucee)

coldfusion null pointer exception in cfloop

I'm getting an issue as "Error Occurred While Processing Request
The system has attempted to use an undefined value, which usually
indicates a programming error, either in your code or some system code.
Null Pointers are another name for undefined values."
I verified all posts related to NPException & most of the issues are related to cfquery, cfhttp......This issue is with in cfloop iteration in cfc function (Y)
I have two functions X & Y. Y is called by X multiple times with in the loop. This loop is in cflock tag with timespan=5
function X :
============
<cfset Var array = getXML()>
<cfargument name="searchOnly" type="boolean" required="false">
<cfset Var i = "">
inside loop Y method is called
<cflock scope="Session" type="exclusive" timeout="5">
<cfloop from="1" to="#ArrayLen( array )#" index="i">
<cfset fields = Y( array[i], Arguments.searchOnly )>
</cfloop>
</cflock>
===========
function Y :
============
<cffunction name=“Y”>
<cfargument name="root" required="true">
<cfargument name="searchOnly" type="boolean" required="false">
<cfset Var i = "">
<cfloop index="i" from="1" to="#ArrayLen( Arguments.root.XMLChildren )#">
<cfset childNode = Arguments.root.XMLChildren[i]> ---> this line causes an error
<cfif Arguments.root.XMLName neq "match">
<!--- Recursive call --->
<cfset Y( labeledElements, Y( childNode ) )>
</cfif>
Is there any issue with Recursive call with in the same cfloop.
</cfloop>
</cffunction>
Variable i is declared in these two cffunctions, is this raising any issue with same variable name i. Please share your thoughts
You're not var-ing childNode, so you will be overwriting the outer call's variable with the recursive call's one. This will make the process unstable, and unsurprising that you get unexpected results / errors.
I imagine that's your problem.

Can't edit/use variable in expression but I can output it

I've got the following code in a method:
<cffunction name="serviceTicketValidate" access="public" output="yes" returntype="void" hint="Validate the service ticket">
<cfargument name="service_ticket" type="string" required="yes" hint="The ST to validate" />
<!--- Contact the CAS server to validate the ticket --->
<cfhttp url="#Variables.cas_server#serviceValidate" method="get">
<cfhttpparam name="ticket" value="#Arguments.service_ticket#" type="url" />
<cfhttpparam name="service" value="#Variables.service#" type="url" />
</cfhttp>
<!--- Received a valid XML response --->
<cfif IsXML(cfhttp.FileContent)>
<cfset XMLobj = XmlParse(cfhttp.fileContent)>
<!--- Check for the cas:user tag --->
<cfset CASuser = XmlSearch(XMLobj, "cas:serviceResponse/cas:authenticationSuccess/cas:user")>
<!--- Set the username to the value --->
<cftry>
<cfif variables.username NEQ ''>
<cfdump var="#Variables.username#" /><cfreturn/>
</cfif>
<cfif ArrayLen(CASuser)>
<cfset Variables['username'] = CASuser[1].XmlText />
</cfif>
<cfcatch>
<cfdump var="#cfcatch#" /><cfabort/>
</cfcatch>
</cftry>
<!--- Search for cas:attributes --->
<cfset CASattributes = XmlSearch(XMLobj, "cas:serviceResponse/cas:authenticationSuccess/cas:attributes")>
<!--- Go through all the attributes and add them to the attributes struct --->
<cfif ArrayLen(CASattributes)>
<cfloop array=#CASattributes[1].XmlChildren# index="attribute">
<cfset StructInsert(Variables.attributes,RemoveChars(attribute.XmlName,1,Find(":",attribute.XmlName)),attribute.XmlText)/>
</cfloop>
</cfif>
</cfif>
Note I added the cftry and cfcatch to see what is going on exactly. I've also added the if username != blank to debug as well. This method is called in another method like so:
<cfinvoke method="serviceTicketValidate">
<cfinvokeargument name="service_ticket" value="#service_ticket#" />
</cfinvoke>
<cfdump var="test2" /><cfabort/>
Again I've added the dump and abort for testing. The variable.username is defied and set to an empty string when the component is initiated and the component is initiated into a session variable.
So get this... when the whole process runs the first time I get output on my screen test2 as expected. Then, the next time the same thing is run, the session exists, thus the variable.username is set to something. In the first code block I can dump variables.username and see the username. However if I try to use variables.username in a conditional expression (like in that if statement) or if I remove the if statement and let the script try to change the value of variable.username, there are no errors, it just breaks out of the script completely. It ends that method, and the method that called it and I don't see test2 like I would think. It all just ends for some reason.
If you need further details I can provide more code but I tried to trim out as much as I thought was relevant. All methods are in the same component, all methods are public. Why can't I change the value of variables.username and why is there no error?
EDIT:
I think it may have something to do with the cflock but I'm debugging some stuff right now. I had a redirect inside the code block that is inside the lock. So I guess it never unlocks. But I even waited after the timeout and it still remained locked. I thought the lock was supposed to expire after the timeout.
I'm a little confused but it seems like you're trying to use a cfc's variables scope to set caller variables. The variables scope is not available to the caller the way it seems you are trying to use it.
index.cfm
<cfoutput>
<cfset objTest = createObject("component", "testscope").init()><br><Br>
<cfset objTest.checkValue()><br><br>
Calling page is checking the existence of testvar: #isDefined("variables.testvar")#
</cfoutput>
testscope.cfm
<cfcomponent displayname="testscope">
<cffunction name="init" access="public">
init() just set variables.testvar.
<cfset variables.testvar = "Okay, set">
<cfreturn This>
</cffunction>
<!--- Set some more variables --->
<cffunction name="checkValue" access="public">=
<cfoutput>Checkvalue is checking the value of testvar: #variables.testvar#</cfoutput>
</cffunction>
</cfcomponent>
The output is
init() just set variables.testvar.
Checkvalue is checking the value of testvar: Okay, set
Calling page is checking the existence of testvar: false

Coldfusion 10 - Element [n] is undefined in a Java object of type class coldfusion.runtime.Array

I recently upgraded a system from CF8 to CF10 and have one bug that I'm having problems tracking down. It has to do with a remote API call that gets a JSON string back and that string then gets converted to a query object. That's where I'm coming across the error:
Element [n] is undefined in a Java object of type class coldfusion.runtime.Array. The problem is in the function that converts the string to a query.
<cffunction name="CFjsonToQuery" access="public" returntype="query" output="no">
<cfargument name="cfData" required="yes" type="struct"/>
<cfset var LOCAL = {}/>
<cfset LOCAL.tmpQry = QueryNew( ArrayToList(ARGUMENTS.cfData.Data.COLUMNS) ) />
<cfloop index = "i" from = "1" to = "#ArrayLen(ARGUMENTS.cfData.Data.DATA)#">
<cfset LOCAL.Row = QueryAddRow(LOCAL.tmpQry) />
<cfloop index="k" from="1" to="#ArrayLen(ARGUMENTS.cfData.Data.DATA[i])#">
<cfset LOCAL.colName = ARGUMENTS.cfData.Data.COLUMNS[K]/>
<cfset QuerySetCell(LOCAL.tmpQry,LOCAL.colName,ARGUMENTS.cfData.Data.DATA[i][k],LOCAL.Row)/>
</cfloop>
</cfloop>
<cfreturn LOCAL.tmpQry/>
</cffunction>
Anywhere the JSON returns 'null' (i.e. "...","19107-3609",null,null,null,"...") the error is thrown. I've tried using isNull to check if it's null in the cfloop:
<cfif isNull(ARGUMENTS.cfData.Data.DATA[i][k])>
<cfset ARGUMENTS.cfData.Data.DATA[i][k] = 'I AM NULL'/>
</cfif>
EDIT - here's a simplified example - the issue is the way the newer deserializeJson() works I believe:
<cfset jstr = '{"SUCCESS":true,"ERRORS":[],"DATA":{"COLUMNS":["ID","FNAME","LNAME"],"DATA":[[390132,"steve",null]]}}'/>
<cfset cfData = deserializeJson(jstr) />
<cfloop index = "i" from = "1" to = "#ArrayLen(cfData.Data.DATA)#">
<cfset Row = QueryAddRow(tmpQry) />
<cfloop index="k" from="1" to="#ArrayLen(cfData.Data.DATA[i])#">
<cfset colName = cfData.Data.COLUMNS[K]/>
<cfset QuerySetCell(tmpQry,colName,cfData.Data.DATA[i][k],Row)/>
</cfloop>
</cfloop>
I've tried all sorts of tests for empty string, isNull etc. and I'm still not sure how to get the query object built if deserializejson returns:
[undefined array element] Element 3 is undefined in a Java object of type class coldfusion.runtime.Array.
This does seem to work:
<cfset cfData = deserializeJson(returnData,'FALSE') />
<cfset qryData = cfData.data />
This lets me then use qryData as if it were a normal cfquery.
You can do a check if the element is undefined using the CF Function ArrayIsDefined(array, elementIndex)
What I've done for now is add 'FALSE' to the deserializeJSON strictMapping flag and that seems to automatically create a query object? I'll admit though this is getting into the underpinnings of CF10 and I could be wrong on that. I'll update my code above for visual clarity.

Consuming a webservice code simplification

UPDATED CODE TO LATEST ITERATION
The following function consumes a webservice that returns address details based on zip code (CEP). I'm using this function to parse the xml and populate an empty query with the address details. I would like to know if there is a more elegant way to achieve the same result. It seems to be a waste to create an empty query and populate it...
Any ideas could my method be modified or the code factored/simplified?
<!--- ****** ACTION: getAddress (consumes web-service to retrieve address details) --->
<cffunction name="getAddress" access="remote" returntype="any" output="false">
<!--- Defaults: strcep (cep (Brazilian zip-code) string webservice would look for), search result returned from webservice --->
<cfargument name="cep" type="string" default="00000000">
<cfset var searchResult = "">
<cfset var nodes = "">
<cfset var cfhttp = "">
<cfset var stateid = 0>
<cfset var tmp = structNew()>
<!--- Validate cep string --->
<cfif IsNumeric(arguments.cep) AND Len(arguments.cep) EQ 8>
<cftry>
<!--- Consume webservice --->
<cfhttp method="get" url="http://www.bronzebusiness.com.br/webservices/wscep.asmx/cep?strcep=#arguments.cep#"></cfhttp>
<cfset searchResult = xmlparse(cfhttp.FileContent)>
<cfset nodes = xmlSearch(searchResult, "//tbCEP")>
<!--- If result insert address data into session struct --->
<cfif arrayLen(nodes)>
<cfset tmp.streetType = nodes[1].logradouro.XmlText>
<cfset tmp.streetName = nodes[1].nome.XmlText>
<cfset tmp.area = nodes[1].bairro.XmlText>
<cfset tmp.city = nodes[1].cidade.XmlText>
<cfset tmp.state = nodes[1].uf.XmlText>
<cfset tmp.cep = arguments.cep>
<!--- Get state id and add to struct --->
<cfset stateid = model("state").findOneByStateInitials(tmp.state)>
<cfset tmp.stateid = stateid.id>
<cfreturn tmp>
</cfif>
<!--- Display error if any --->
<cfcatch type="any">
<cfoutput>
<h3>Sorry, but there was an error.</h3>
<p>#cfcatch.message#</p>
</cfoutput>
</cfcatch>
</cftry>
</cfif>
</cffunction>
<!--- ****** END ACTION getAddress --->
The calling code:
<!--- Get address data based on CEP --->
<cfset session.addressData = getAddress(cep=params.newMember.cep)>
I can't test this because I don't have an example XML file / CEP to test with, but here is a minor rewrite that addresses four things:
Instead of using cfparam and some strange "params" structure, you should pass the CEP into the function as an argument.
The function shouldn't directly modify session data. Instead, you should return the result and let the calling code assign it to the session (or wherever else it might be needed). I'll show this in a 2nd code example.
Cache the xml result per CEP -- assuming this doesn't change often. (You'll have to improve it further if you want time-based manual cache invalidation, but I can help add that if necessary)
Don't use StructInsert. It's not necessary and you're just writing it the long way for the sake of writing it the long way. There is no benefit.
Again, this isn't tested, but hopefully it's helpful:
<cffunction name="getAddress" access="remote" returntype="any" output="false">
<cfargument name="cep" type="string" default="00000000" /><!--- (cep (Brazilian zip-code) string webservice would look for) --->
<cfset var searchResult = "">
<cfset var nodes = "">
<cfset var cfhttp = "">
<cfset var stateid = 0 />
<cfset var tmp = structNew()>
<!--- Validate cep string --->
<cfif IsNumeric(arguments.cep) AND Len(arguments.cep) EQ 8>
<cfif not structKeyExists(application.cepCache, arguments.cep)><!--- or cache is expired: you'd have to figure this part out --->
<!--- Consume webservice --->
<cftry>
<cfhttp method="get" url="http://www.bronzebusiness.com.br/webservices/wscep.asmx/cep?strcep=#arguments.cep#" />
<cfset searchResult = xmlparse(cfhttp.FileContent)>
<cfset nodes = xmlSearch(searchResult, "//tbCEP")>
<!--- If result insert address data into session struct --->
<cfif arrayLen(nodes)>
<cfset tmp.streetType = nodes[1].logradouro.XmlText />
<cfset tmp.streetName = nodes[1].nome.XmlText />
<cfset tmp.area = nodes[1].bairro.XmlText />
<cfset tmp.city = nodes[1].cidade.XmlText />
<cfset tmp.state = nodes[1].uf.XmlText />
<cfset tmp.cep = arguments.cep />
<!--- Get state id and add to struct --->
<cfset stateid = model("state").findOneByStateInitials(session.addressData.state)>
<cfset tmp.stateid = stateid.id />
</cfif>
<cfreturn duplicate(tmp) />
<!--- Display error if any --->
<cfcatch type="any">
<h3>Sorry, but there was an error.</h3>
<p>#cfcatch.message#</p>
</cfcatch>
</cftry>
<cfelse>
<!--- cache exists and is not expired, so use it --->
<cfreturn duplicate(application.cepCache[arguments.cep]) />
</cfif>
</cfif>
<!---
<!--- Redirect to page two of the sign up process --->
<cfset redirectTo(controller="assine", action="perfil")>
--->
</cffunction>
Notice that I commented out the redirect you had at the end. That's because with my function, you'll be returning a value, and the redirect should be done after that, like so:
<cfset session.addressData = getAddress("some-CEP-value") />
<cfset redirectTo(controller="assine", action="perfil")>
If you're going to leave out the caching (As you say in a comment you will), then here is a version that makes no attempt at caching:
<cffunction name="getAddress" access="remote" returntype="any" output="false">
<cfargument name="cep" type="string" default="00000000" /><!--- (cep (Brazilian zip-code) string webservice would look for) --->
<cfset var searchResult = "">
<cfset var nodes = "">
<cfset var cfhttp = "">
<cfset var stateid = 0 />
<cfset var tmp = structNew()>
<!--- Validate cep string --->
<cfif IsNumeric(arguments.cep) AND Len(arguments.cep) EQ 8>
<!--- Consume webservice --->
<cftry>
<cfhttp method="get" url="http://www.bronzebusiness.com.br/webservices/wscep.asmx/cep?strcep=#arguments.cep#" />
<cfset searchResult = xmlparse(cfhttp.FileContent)>
<cfset nodes = xmlSearch(searchResult, "//tbCEP")>
<!--- If result insert address data into session struct --->
<cfif arrayLen(nodes)>
<cfset tmp.streetType = nodes[1].logradouro.XmlText />
<cfset tmp.streetName = nodes[1].nome.XmlText />
<cfset tmp.area = nodes[1].bairro.XmlText />
<cfset tmp.city = nodes[1].cidade.XmlText />
<cfset tmp.state = nodes[1].uf.XmlText />
<cfset tmp.cep = arguments.cep />
<!--- Get state id and add to struct --->
<cfset stateid = model("state").findOneByStateInitials(session.addressData.state)>
<cfset tmp.stateid = stateid.id />
</cfif>
<cfreturn duplicate(tmp) />
<!--- Display error if any --->
<cfcatch type="any">
<h3>Sorry, but there was an error.</h3>
<p>#cfcatch.message#</p>
</cfcatch>
</cftry>
</cfif>
<!---
<!--- Redirect to page two of the sign up process --->
<cfset redirectTo(controller="assine", action="perfil")>
--->
</cffunction>
Note that I did leave in the use of duplicate(). What this does is return a duplicate of the object (in this case, the struct). This is much more important when you start to work on applications where you're passing complex values into and out of functions over and over again. Using duplicate() causes things to be passed by value instead of by reference. It may not bite you in this case, but it's a good habit to get into.
I would also still use the function argument and return a value -- but it's arguable that this is my personal preference. In a way it is. I believe that a function should be fully encapsulated; a total "black box". You give it some input and it gives you back some output. It should not modify anything outside of itself. (Again, just my opinion.)
So assuming you're using this function as part of a larger multi-step process, you should still use it the same way I've described above. The only difference is that you're setting the session variable outside of the function body. Just as previously:
<cfset session.addressData = getAddress("some-CEP-value") />
<cfset redirectTo(controller="assine", action="perfil")>
That looks pretty straightforward. CF doesn't (yet?) have any magical XML-to-Query functions, but that would be pretty cool. If you wanted, you could probably write up an XSL transform to go from XML to WDDX so that you could use the cfwddx tag ... but that's probably putting the cart before the horse.
You need to move your arrayLen() if block into the try block. As it stands, if the cfhttp tag throws an error, the nodes variable will be a string and not an array, thus causing the arrayLen() to throw another error.
Minor nitpick: I wouldn't add a row to the query until inside the arrayLen() block. That way, the calling code can check recordCount to see if the result was a success.
Beyond that ... that's pretty much how it's done.