Return MySQL error from CFQUERY statement - mysql

Stupid problem, I have a function that runs a query several times in a block & reports/emails if/when a query fails. I'm wondering if/how I can capture the actual MySQL error & return it as part of my email/report.
So far I see no way to do this.
Any thoughts?
-thanks
-sean
UPDATE
Thanks Charlie;
I never considered using the cfcatch structure [and truthfully didn't realize it returned so much useful stuff!!]
Unfortunately the host does not allow cfdump so I had to go about it like this:
<cftry>
<some sql>
<cfcatch type="any">
<cfscript>
for (key in cfcatch) {
try{
variables.report = variables.report&"<li>"&key&"="&cfcatch[key]&"</li>";
}
catch(Any excpt) {
variables.report = variables.report&"<li>"&key&"=??</li>";
}
}
</cfscript>
<cfcatch>
<cftry>

Isn't the native database error returned as part of the cfcatch?
<cftry>
(some sql here)
<cfcatch type="any">
<cfdump var="#cfcatch#" />
</cfcatch>
</cftry>
If you run that on a page, and intentionally use some invalid SQL, what do you see in the cfdump?

Related

Migrating coldfusion application to Lucee

Our server is changing from Coldfusion to a Lucee server and I'm tasked with updating our code for a couple of web applications. I'm not a guru of Coldfusion but I can often figure my way around things, to that end my query.
The code I'm converting over is throwing this error:
Can't cast Object type [DateTime] to a value of type [Array]
I have been working through all of the queries and making sure that the output is appropriately CAST which has resolved the majority of issues but the small block of code that is stumping me throws the above error. The code is:
<cfset summaryStartDate = ArrayMin( qSummaryData["minHours"] ) />
<cfset summaryMaxDate = ArrayMax( qSummaryData["maxHours"] ) />
<cfset summaryEndDate = DateAdd("d", -(DayofWeek(#summaryMaxDate#))+6, #summaryMaxDate# ) />
minHours and maxHours are both DATETIME format. I know in the coldfusion version they output like so:
summaryStartDate: 41204
summaryMaxDate: 43465
summaryEndDate: {ts '2019-01-04 00:00:00'}
Which, to me, means Coldfusion is doing a conversion in some way and Lucee doesn't do those (or at least from what I've read). The database is mySQL and the minHours and maxHours output as dates with 00:00:00 on the hours, for reference.
I'm probably missing something obvious but I can't see it.
I preface this answer with "it is not the greatest fix" but it does work. Taking my queue from andrewdixon I looked at side stepping the use of an array (which the data wasn't suited for) and looked at alternatives.
I settled on a query of queries, extracting the min value and then setting that in a cfset. Doing the same for max value following that. My two queries were:
<cfquery name="smallestFigure" dbtype="query">SELECT CAST(MIN(minHours) AS DATETIME) as outputMin FROM qSummaryData;</cfquery>
<cfquery name="largestFigure" dbtype="query">SELECT CAST(MAX(maxHours) AS DATETIME) as outputMax FROM qSummaryData;</cfquery>
I cfset these into summaryStartDate and summaryMaxDate so that this line (mentioned in the original post) could run:
<cfset summaryEndDate = DateAdd("d", -(DayofWeek(#summaryMaxDate#))+6, #summaryMaxDate# ) />
Shawn mentioned I didn't need the #'s around summaryMaxDate but I haven't made that change as yet. andrewdixon mentioned using query.reduce() as an alternative and I imagine that would be far more succinct than what I've done so if someone comes up with a better solution, please post as an answer.
Also thank you all for the support and ideas.

How to Invoke JSON data as the correct Data Type?

Question: What data type do I use when invoking JSON data from a database using ColdFusion?
Background: My application needs to pull some JSON data from a database and parse the data into an HTML form.
Below is my code so far:
<cftry>
<cfinvoke component="UserData.cfc.data" method="editData" returnvariable="editReturn">
<cfinvokeargument name="formID" value="#URL.dataID#">
</cfinvoke>
<cfset ReBuild = DeserializeJSON(#editReturns#)>
<cfcatch type="Any">
<cfoutput>
<hr>
<h1>Other Error: #cfcatch.Type#</h1>
<ul>
<li><b>Message:</b> #cfcatch.Message#
<li><b>Detail:</b> #cfcatch.Detail#
</ul>
</cfoutput>
<cfset errorCaught = "General Exception">
</cfcatch>
</cftry>
UserData.cfc.data:
<cffunction name="editData" access="public" returntype="any">
<cfargument name="formID" required="yes">
<!--- Select --->
<cfquery name="UserData" datasource="RC">
SELECT Data
FROM USER_Forms
WHERE ID = <cfqueryparam value="#ARGUMENTS.formID#">
</cfquery>
<!-- The information pulled from the database should be a Serialized JSON data. -->
<cfreturn UserData>
</cffunction>
Error Message:
Other Error: Expression
Message: Complex object types cannot be converted to simple values.
Detail: The expression has requested a variable or an intermediate expression result as a simple value. However, the result cannot be converted to a simple value. Simple values are strings, numbers, boolean values, and date/time values. Queries, arrays, and COM objects are examples of complex values.
The most likely cause of the error is that you tried to use a complex value as a simple one. For example, you tried to use a query variable in a cfif tag.
When I added the data to the database I used the following process:
<cfset ForDBInsert = SerializeJSON(formCopy)>
<!-- Then I INSERTED ForDBInsert into the database columnn. -->
Try DeserializeJSON(editReturns.data) - notice I took out the # they are not needed when passing arguments this way. Looks like you are trying to deserialize the entire query object rather than the string itself.

MySQL Query Error Validation

I running a Mysql Query to select some data, Sometimes i get a error called
mysql_fetch_assoc() expects parameter 1 to be resource, boolean given
when i executed this following code,
$result = $this->db->execute($sql);
for ($i = 0; $data[$i + 1] = mysql_fetch_assoc($result); $i++);
array_pop($data);
how do i optimize this coding to prevent any errors ?
is there anything wrong with it ? should i ignore this error ?
That means that the query is buggy, whyever, most likely because you construct it using components from sources which you do not really check enough. A buggy statement throws an error (since no result can be computed). That error is returned as false instead of a mysql result ressource. Since you do not check if the query succeeded but blindly try to retrieve details from the result, you get this second error.
So there are four things you have to invest into:
you should always check if a query succeeded at all:
enclose your query into a conditional: if (FALSE!==($result=$this->db->execute($sql))) and only retrieve from the result ressource if that condition resolves to true.
make sure you really (really!) check all input data you use to construct your query. Checking here also means to encode and escape it correctly, also see point 4. for this.
in cases like this it is important to analyze what exactly it is that is going wrong. There is little sense in guessing what might be going wrong. So in addition to checking if the query succeeded at all (1.) you should also take a look at the error message mysql throws if this is not the case. Use the method mysql_error() for this. It is well documented just as every other function too.
you should rework your code and migrate from phps old, long deprecated mysql extension to either mysqli or PDO. Both are php extensions that offer more security against constructing buggy statements. Read about "prepared statements" and "parameter binding" for this.

define global DSN(s) in Coldfusion 10

I've been building a new coldfusion application leveraging object oriented approach and stored procedures. While everything works like a charm, the app is very fast due to SPs and optimized mysql code, I would really appreciate if you would help me clear one thing up :) I have dbcode.cfc which as you probably already guessed stores all queries with stored procedures. That said, generally what's the best approach when it comes to storing global DNS parameters?
1.this can be used for one global DSN
<cfset this.datasource ="myDB">
2.this can also be used for one global DSN
<cffunction name="onApplicationStart" returnType="boolean" output="false">
<cfset application.dsn = "myDB">
<cfset application.username = "userName">
<cfset application.password = "password">
<cfreturn true>
</cffunction>
2.1 everything as above but onRequest
3.in my case I could also create a global variables within the dbcode.cfc
<cfset variables.dsn = "myDB">
<cfset variables.username = "userName">
<cfset variables.password = "password">
4.additionally one could use something like this for setting multiple datasources
<cffunction name="onApplicationStart" returnType="boolean" output="false">
<cfset application.myDSNs = StructNew()>
<cfset application.myDSNs.1 = "myDB1">
<cfset application.myDSNs.2 = "myDB2">
.
.
.
<!---something similar for usernames and passwords--->
</cffunction>
so what's the most efficient way to handle this sort of thing?
Let me see if I can answer each part of your DSN questions.
Setting the datasource as a part the application.cfc constructor is OK
Setting up the DSN in OnApplicationStart() works too, but you need to use:
<cfset application.datasource = "myDB">
2.1 Setting the datasource on every request is pointless, unless you datasource changes with every request. I suspect is does not
You should not touch queries.cfc. That is internal to ColdFusion and has the potential to break everything on your server.
You may want to read on up on the difference between arrays and structs. It looks like what you wanted to do was:
<cfset application.myDSNs = ["MyDB1", "MyDB2]>
They would then be accessable via:
#application.myDSNs[1]#
#application.myDSNs[2]#
I would not do this approach. Databases are typically not arrays of anything. Each server has its own purpose. You DSNs should be reflective of one of more of the following:
The Server
The Database
Purpose

in a multi-language site, why would I store some text strings in a database and write other one into the markup?

I'm working on an existing multi-language site (Coldfusion/MySQL).
Why is it that on a lot of pages I'm sitting on, some text strings are always hard-coded into the markup like:
<CFIF language = "EN"><p>Hello World</p></CFIF>
while others use the database to update text like so:
<p><cfoutput>#tx_greetings#</cfoutput></p>
What is the best practice here? I thought if I'm going to use a database for translations, it would be easier to store all texts in there (long and small). If I'm not using a database, then all texts should be if-elsed. Mixing it is a little maintenance-heavy, isn't it?
Also, is there a limit on text-string-length, which I'm storing to MySQL? Maybe performance-wise?
Thanks for some inputs!
You shouldn't store strings/translations in your code, that's bad practice if you want a maintainable i18n'd site.
You should store all your string in the same location, db or a properties file per language. It doesn't matter which, but be consistent. Personally I prefer a properties file as its easy to edit.
welcome_message=Hi {0}, Welcome to my site
Load all your translations in one go in onApplicationStart(), then provide a wrapper class to access them and to format the string with supplied arguments
for example
#i18n.getString(user.getLocale(), "welcome_message", [user.getUsername()])#
You can use java.text.MessageFormat[1] to provide powerful formatting
function getString(string locale, string key, array args) {
var mf = createobject("java", "java.text.MessageFormat");
mf.init(variables.strings[arguments.locale][arguments.key]);
return mf.format(javacast("java.lang.Object[]", args));
}
The above is just an example, and you need to provide error catching and caching to this
Hope that helps point you in a productive direction
[1] http://docs.oracle.com/javase/7/docs/api/index.html?java/text/MessageFormat.html
You can use a DB or a ascii file, depends on which you prefer.
If u use a DB you can create a table with the following columns:
country_code : country code for the language (i.e. US for english)
definition_name : name of the definition or message (*i.e. db_error_msg for a generic error message for db action*)
definition value : value of the definition (i.e. Sorry, an error occurred saving your data)
Each record will be a definition.
Depending on the language the user select your app will filter the database and you will get a query of all definitions you need.
I usually use that query to set a session variable structure like:
<cfif IsDefined("session.language") IS FALSE>
<cfquery name="getDefinition" datasource="dsn">
SELECT * FROM tbl_definitions WHERE country_code = "US"
</cfquery>
<cfset session.language = structnew()>
<cfoutput query="getDefinitions">
<cfset session.language["#definition_name#"] = "#definition_value#">
</cfoutput>
</cfif>
In the code I will simply use:
<cfoutput>
<h2>#session.language.db_error_msg#</h2>
</cfoutput>
and I will get the right message for the current language.
You can also use a master definition db to be used by different websites.
Same solution can be used with different configuration files (ie US.cfg. EN.cfg, ES.cfg) where you set your definitions in simple way to get a list.
I usually use the following system:
definition_name = definition_value for each line
db_error_msg = Sorry, an error occured saving your data
db_success_msg = Record saved
Then I read the current language configuration file (i.e. US.cfg for english, ES.cfg for spanish) and get the same result
<cfif IsDefined("session.language") IS FALSE>
<cffile action="read" file="#path#\US.cfg" variable="definitions">
<cfset session.language = structnew()>
<cfloop index="i" list="#definitions#" delimiters="#chr(10)#">
<cfset definition_name = ListGetAt(i,1,"=")>
<cfset definition_value = ListGetAt(i,2,"=")>
<cfoutput>
<cfset session.language["#definition_name#"] = "#definition_value#">
</cfoutput>
<cfloop>
</cfif>
This can be done just when session starts (if you know the language you need) and in both ways your definitions will be available everywhere inside your application for the user session time duration you have defined.
You can use definitions for buttons, messages, table headers, etc. creating a multilanguage ui in a very fast way without creating localized templates or using inline translations.
I hope this will help you.
Just as some generic localization advice, be careful about the variable name you'll use to know which phrase to retrieve. Don't just make it the english phrase, but make it something that is clearly a specific variable because you'll also have to handle contextual phrases that seem the same in English, but are very different in other languages depending on context.