Using CFWheels I sometimes get an error email stating that there was a DB error [Amazon](500310) Invalid operation: table 250818 dropped by concurrent transaction. After reading about this, it seems this can happen when a second transaction starts before the first is finished. (I'm not editing data here, just SELECT statements).
The odd part is in my trace in the error email I get something like the following:
Line 85 in models\ModelA.cfc
Line 73 in controllers\ControllerA.cfc
Line 85 in models\ModelA.cfc
Line 73 in controllers\ControllerA.cfc
Like it tried to run the same code twice. FYI, you can't follow my code and go from 73 to 85 and then back to 73. You just can't. It doesn't work that way. You do go from 73 to 85 and that makes sense, but I don't see why it's doing it twice.
What would cause my code to at least appear like it's running twice in the same request as I feel that is the underlying reason this DB error is happening.
EDIT: Example where this has taken place
Controller:
<cfparam name="params.key" default="0" />
<cfset resource = model("courseResource").findByKey(params.key) />
<cfif isObject(resource)>
<cfset course = model('Course').courseInfo(resource.id) />
<cfquery dbtype="query" name="course">
SELECT * FROM course ORDER BY id
</cfquery>
<!--- HERE IS WHERE ERROR OCCURRED --->
<cfset var enrollment = model("course").getCourseEnrollment(resource.id) />
<cfset resource.handoutdate = dateFormat(resource.handoutDate,'mm/dd/yyyy') />
<cfelse>
<cfset logEvent(SESSION.user.getProperty('id'),'Editing course resource - Missing',params.key) />
<cfset redirectTo(back="true",error="Course resource could not be found") />
</cfif>
Model Function:
<cfargument name="id" type="string" required="true" />
<cfargument name="students" type="string" required="false" default="" />
<cfquery datasource="DSN" name="local.qEnrollment">
SELECT
DISTINCT *fieldnames*
FROM course_dim c
INNER JOIN course_section_dim cs ON cs.course_id = c.id
INNER JOIN enrollment_dim e ON e.course_section_id = cs.id AND e.type = 'TypeName' AND e.workflow_state = 'active'
INNER JOIN user_dim u ON u.id = e.user_id
INNER JOIN pseudonym_dim p ON p.user_id = u.id
WHERE c.id IN (<cfqueryparam value="#ARGUMENTS.id#" list="yes" />)
AND c.sis_source_id IS NOT NULL
<cfif ARGUMENTS.students NEQ ''>
AND p.unique_name IN (<cfqueryparam value="#ARGUMENTS.students#" list="yes" />)
</cfif>
AND e.workflow_state = 'active'
ORDER BY u.name
</cfquery>
<cfreturn qEnrollment />
I'm not sure what you could possibly get from that, but that is essentially it. I mean there is nothing crazy. I just don't understand how the line that calls the model function can appear twice in the trace. It doesn't make any sense.
Related
You can run this cftry gist with either the first or second indicated block of code uncommented to see the problem.
<cffunction name="alpha" returntype="boolean">
<cfargument name="boo" type="boolean" />
<cfreturn arguments.boo />
</cffunction>
<cffunction name="beta">
<cfset var temp = {} />
<cftry>
<cfset temp.userID = 1 />
<!--- This way throws an *unhandled* exception --->
<!--- --->
<cfif alpha(structAppend({userID = temp.userID}, foo))>
<cfdump var="It worked" />
</cfif>
<!--- This way works as expected --->
<!---
<cfset temp.args = {userID = temp.userID} />
<cfif alpha(structAppend(temp.args, foo))>
<cfdump var="It worked" />
</cfif>
--->
<cfcatch>
<cfdump var="#cfcatch.message#" />
</cfcatch>
</cftry>
</cffunction>
I know the struct literal notation {} will sometimes show generically named structs in my debugger, but why should assigning what's created by such syntax make the struct creation happen at a different time than if it's not assigned?
If I debug, I can set a breakpoint on the line where I set temp.userID = 1 and it skips right over it. Moreover, the exception is being thrown on a line contained in a try/catch, but it's failing to catch it.
I know in JavaScript there's the notion of 'hoisting'. I can only assume in some (but not all!) cases CF is doing something similar to struct literals.
Is this a bug, or a known behavior of CF?
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
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.)
I have a form where I can upload logos a plenty. I'm validating files (empty form fields, wrong extension, ...) inside a cftry/cfcatch statement.
When I find an error in the cftry, I do this:
<cfthrow type="FileNotFound" message="#tx_settings_app_error_create_first#" />
and inside my 'cfcatch'
<cfcatch>
<cfoutput><script type="text/javascript">window.onload = function(){alert("#cfcatch.message#");window.location.href="hs_apps.cfm"; } </script></cfoutput>
</cfcatch>
This works fine, catches all errors and alerts the user what is wrong.
Now I wanted to use the same handler on a database call where I'm checking for duplicate username. Still the same:
<cfquery datasource="#Session.datasource#" name="duplicates">
SELECT a.app_alias
FROM apps AS a
WHERE a.iln = <cfqueryparam value = "#Session.loginID#" cfsqltype="cf_sql_varchar" maxlength="13">
AND a.app_alias = <cfqueryparam value = "#form.app_basic_alias#" cfsqltype="cf_sql_varchar" maxlength="50">
</cfquery>
<cfif duplicates.recordcount GT 0>
<cfthrow type="FileNotFound" message="#tx_settings_apps_error_dup#" />
</cfif>
The cfcatch is also the same.
However. This now procudes a server error page and I'm thrown out of the application.
Question:
Any idea, why I'm struggling to get cftry/cfcatch to work here? I'm clueless.
Thanks!
EDIT:
Here is the full code
<cfif isDefined("send_basic")>
<cfset variables.timestamp = now()>
<cfset variables.orderview = "1">
<cfif form.send_basic_type EQ "new">
<cftry>
<cfif module_check.recordcount GT 0>
<cfloop query="module_check">
<cfif module_check.module_name EQ "preorder">
<cfset variables.module_name = module_check.module_name>
<cfset variables.b2b_preord_ok = "true">
</cfif>
</cfloop>
<cfif form.app_basic_orderview EQ "preo">
<cfset variables.orderview = "0">
</cfif>
</cfif>
<!--- PROBLEM HERE: DUPLICATES --->
<cfquery datasource="#Session.datasource#" name="duplicates">
SELECT a.app_alias
FROM apps AS a
WHERE a.iln = <cfqueryparam value = "#Session.loginID#" cfsqltype="cf_sql_varchar" maxlength="13">
AND a.app_alias = <cfqueryparam value = "#form.app_basic_alias#" cfsqltype="cf_sql_varchar" maxlength="50">
</cfquery>
<cfif duplicates.recordcount GT 0>
<cfthrow type="FileNotFound" message="#tx_settings_apps_error_dup#" />
</cfif>
<!--- IF PASS, CREATE/UPDATE --->
<cfquery datasource="#Session.datasource#">
INSERT INTO apps ( ... )
VALUES( ... )
</cfquery>
<cfset variables.app_action = "Applikation erstellt">
<!--- success --->
<cfoutput><script type="text/javascript">window.onload = function(){alert("#tx_settings_app_cfm_create#");}</script></cfoutput>
<cfcatch>
<cfoutput><script type="text/javascript">window.onload = function(){alert("#tx_settings_app_err_create#");}</script></cfoutput>
</cfcatch>
</cftry>
<cfelse>
<cftry>
<!--- UPDATE --->
<cfquery datasource="#Session.datasource#">
UPDATE apps
SET ... = ...
</cfquery>
<cfset variables.app_action = "Applikation aktualisiert">
<!--- success --->
<cfoutput><script type="text/javascript">window.onload = function(){alert("#tx_settings_app_cfm_update#");}</script></cfoutput>
<cfcatch>
<cfoutput><script type="text/javascript">window.onload = function(){alert("#tx_settings_app_err_update#");}</script></cfoutput>
</cfcatch>
</cftry>
</cfif>
</cfif>
The error message I'm getting it the message I specify =
<cfthrow type="FileNotFound" message="#tx_settings_apps_error_dup#" />
Which if caught should alert the text behind tx_settings_apps_error_dup. If I dump the cfcatch, cfcatch.message is my specified text, so the error gets caught allright, only I get a server error page vs. an alert. I'm using exactly the same handler for fileuploads and form submits. I don't get why it's not working here?
Thanks for helping out!
WORKAROUD:
Note nice, but suffice(s):
<cfif dups.recordcount NEQ 0>
<cfoutput><script type="text/javascript">window.onload = function(){alert("#tx_settings_apps_error_dup#"); location.href = "hs_apps_detail.cfm?create=newApp&id=none";}</script
</cfoutput>
<cfabort>
</cfif>
So when a duplicate is found I alert the user, reload the exact same page and cfabort to prevent the old page from processing further. Patch that is.
(moved this down from being just a comment)
OK, so the catch is definitely catching the exception if you're able to dump it, so it's not that the try/catch ain't working, it's something else. Bear in mind that processing will continue until the end of the request after your catch block, and only then will the output buffer be flushed, and your alert() sent to the browser. It sounds to me like after your output the alert, and processing continues, some OTHER error is occurring which is causing the server's error page to display. I recommend getting rid of the error page temporarily and eyeballing the actual error CF is raising.
NB: if you want processing to stop immediately in the catch block after you output the alert(), you're going to need to tell CF that, ie: with a <cfabort>. Otherwise, as per above, it'll just keep going.
I think exceptions that are caught by the server error page are still logged in the exception log, but I could be wrong. You could always put an onError() handler in your Application.cfc, log whatever error occurred, then rethrow the error so the error page still deals with it. That way you get the info on the error, and the punter still sees the nice error page.
With ColdFusion MX7 if we encounter an exception we send an email to the development team containing dumps of the various data scopes including the form structure.
This works great for debugging except in the case of an error when the user logs in. We end up getting the password printed out.
So, the question is, is there a way to modify the CFDUMP file so that it filters the password value out of the form object?
Naturally we could put it in the same code that sends the email, however it would be ideal to put it in the CFDUMP file so that we do not have to worry about it showing up in other spots.
I have located the CFDUMP file and it seems to be binary, so I'm guessing we can't do it.
You can copy the dump.cfm file to dumporiginal.cfm, and then make a new dump.cfm that calls dumporiginal.cfm.
<!---
So that it won't execute twice if you
have a closing slash (<cfdump ... />)
--->
<cfif thisTag.executionMode neq "start">
<cfexit method="exitTag" />
</cfif>
<!---
defaults for optional attributes, taken from the docs
http://livedocs.adobe.com/coldfusion/8/htmldocs/Tags_d-e_08.html
--->
<cfparam name="attributes.expand" default="yes" />
<cfparam name="attributes.format" default="html" />
<cfparam name="attributes.hide" default="all" />
<cfparam name="attributes.keys" default="9999" />
<cfparam name="attributes.label" default="" />
<cfparam name="attributes.metainfo" default="yes" />
<cfparam name="attributes.output" default="browser" />
<cfparam name="attributes.show" default="all" />
<cfparam name="attributes.showUDFs" default="yes" />
<cfparam name="attributes.top" default="9999" />
<!--- Hide the password, but store its value to put it back at the end --->
<cfif isStruct(attributes.var) and structKeyExists(attributes.var, 'password')>
<cfset originalPassword = attributes.var.password />
<cfset attributes.var.password = "{hidden by customized cfdump}"/>
</cfif>
<!---
Call the original cfdump.
Which attributes you pass depends on CF version.
--->
<cfswitch expression="#listFirst(server.coldfusion.productVersion)#">
<cfcase value="6">
<cfdumporiginal
var = "#attributes.var#"
expand = "#attributes.expand#"
hide = "#attributes.hide#"
label = "#attributes.label#"
>
</cfcase>
<cfcase value="7">
<cfdumporiginal
var = "#attributes.var#"
expand = "#attributes.expand#"
hide = "#attributes.hide#"
label = "#attributes.label#"
top = "#attributes.top#"
>
</cfcase>
<cfdefaultcase>
<cfdumporiginal
var = "#attributes.var#"
expand = "#attributes.expand#"
format = "#attributes.format#"
hide = "#attributes.hide#"
keys = "#attributes.keys#"
label = "#attributes.label#"
metainfo = "#attributes.metainfo#"
output = "#attributes.output#"
show = "#attributes.show#"
showUDFs = "#attributes.showUDFs#"
top = "#attributes.top#"
>
</cfdefaultcase>
</cfswitch>
<!--- Restore the password, in case it's read after cfdump call --->
<cfif isDefined("originalPassword")>
<cfset attributes.var.password = originalPassword />
</cfif>
No, I don't think there is a way to modify <cfdump>'s behavior. I can't be sure, obviously. It's thinkable that such a hack exists, though it's not necessarily recommendable.
Why not go with a simple:
<cftry>
<cfset DoSomethingThatFails()>
<cfcatch>
<cfif StructKeyExists(FORM, "Password")>
<cfset FORM.Password = "***">
</cfif>
<cfdump var="#FORM#">
</cfcatch>
</cftry>
CFDUMP began life as a custom tag (CF_DUMP) way back in the CF5 days. You could always get the code for that custom tag and modify it to your needs and use that instead of the built-in tag.
Is it only the password that is a problem of showing? If so, perhaps the solution is to salt/hash the password? That I think is good practice anyway.
http://blog.mxunit.org/2009/06/look-ma-no-password-secure-hashing-in.html