I am creating a query using cfscript syntax, and I have two query parameters that are dates. I created the date string the first time using
queryservice.addParam(
name="last_update",
value="createODBCDate(now())",
cfsqltype="cf_sql_date");
I would assume this would an analogue to:
<cfqueryparam value="#createODBCDate(now())#" cfsqltype="cf_sql_date">
So, when I run the query, I'm getting:
The cause of this output exception was that: coldfusion.runtime.Cast$DateStringConversionException: The value createODBCDate(now()) cannot be converted to a date.
Fine. So I created a variable,
var currentDate = createODBCDate(now());
added it to
queryservice.addParam(
name="last_update",
value="createODBCDate(now())",
cfsqltype="cf_sql_date");
and got
The cause of this output exception was that: coldfusion.runtime.Cast$DateStringConversionException: The value currentDate cannot be converted to a date.
When I created the query using the standard <cfquery ... syntax it worked fine.
So, I'm assuming that I am doing something wrong, but I can't for the life of me figure out what that is.
By the way, this is really my first time trying to create a query using the <cfscript> syntax.
value="createODBCDate(now())"
You forgot the # signs around the function. Without those, it is just a string. So the function is never invoked and you end up passing in the literal characters "createODBCDate(now())" as the date value.
Update:
As an aside, cf_sql_date automatically removes any time portion. So while using createODBCDate will not hurt anything, it is redundant. You could simply write:
queryservice.addParam(
name="last_update",
value="#now()#",
cfsqltype="cf_sql_date");
Your second attempt needs #'s just like #Leigh mentioned and also didn't reference the variable "currentDate" that you created.
Related
To protect against sql injection, I read in the introduction to ColdFusion that we are to use the cfqueryparam tag.
But when using stored procedures, I am passing my variables to corresponding variable declarations in SQL Server:
DROP PROC Usr.[Save]
GO
CREATE PROC Usr.[Save]
(#UsrID Int
,#UsrName varchar(max)
) AS
UPDATE Usr
SET UsrName = #UsrName
WHERE UsrID=#UsrID
exec Usr.[get] #UsrID
Q: Is there any value in including cfSqlType when I call a stored procedure?
Here's how I'm currently doing it in Lucee:
storedproc procedure='Usr.[Save]' {
procparam value=Val(form.UsrID);
procparam value=form.UsrName;
procresult name='Usr';
}
This question came up indirectly on another thread. That thread was about query parameters, but the same issues apply to procedures. To summarize, yes you should always type query and proc parameters. Paraphrasing the other answer:
Since cfsqltype is optional, its importance is often underestimated:
Validation:
ColdFusion uses the selected cfsqltype (date, number, etcetera) to validate the "value". This occurs before any sql is ever sent to
the database. So if the "value" is invalid, like "ABC" for type
cf_sql_integer, you do not waste a database call on sql that was never
going to work anyway. When you omit the cfsqltype, everything is
submitted as a string and you lose the extra validation.
Accuracy:
Using an incorrect type may cause CF to submit the wrong value to the database. Selecting the proper cfsqltype ensures you are
sending the correct value - and - sending it in a non-ambiguous format
the database will interpret the way you expect.
Again, technically you can omit the cfsqltype. However, that
means CF will send everything to the database as a string.
Consequently, the database will perform implicit conversion
(usually undesirable). With implicit conversion, the interpretation
of the strings is left entirely up to the database - and it might
not always come up with the answer you would expect.
Submitting dates as strings, rather than date objects, is a
prime example. How will your database interpret a date string like
"05/04/2014"? As April 5th or a May 4th? Well, it depends. Change the
database or the database settings and the result may be completely
different.
The only way to ensure consistent results is to specify the
appropriate cfsqltype. It should match the data type of the target
column/function (or at least an equivalent type).
I am writing simple SELECT queries which involve parsing out date from a string.
The dates are typed in by users manually in a web application and are recorded as string in database.
I am having CASE statement to handle various date formats and use correct format specifier accordingly in TO_DATE function.
However, sometimes, users enter something that's not a valid date(e.g. 13-31-2013) by mistake and then the entire query fails. Is there any way to handle such rougue records and replace them with some default date in query so that the entire query does not fail due to single invalid date record?
I have already tried regular expressions but they are not quite reliable when it comes to handling leap years and 30/31 days in months AFAIK.
I don't have privileges to store procedures or anything like that. Its just plain simple SELECT query executed from my application.
This is a client task..
The DB will give you an error for an invalid date (the DB does not have a "TO_DATE_AND_FIX_IF_NOT_CORRECT" function).
If you've got this error- it means you already tried to cast something to an invalid date.
I recommend doing the migration to date on your application server, and in the case of exception from your code - send a default date to the DB.
Also, that way you send to the DB an object of type DbDate and not a string.
That way you achieve two goals:
1. The dates will always be what you want them to be (from the client).
2. You close the door for SQL Injection attacks.
It sounds like in your case you should write the function I mentioned...
it should look something like that:
Create or replace function TO_DATE_SPECIAL(in_date in varchar2) return DATE is
ret_val date;
begin
ret_val := to_date(in_date,'MM-DD-YYYY');
return ret_val;
exception
when others then
return to_date('01-01-2000','MM-DD-YYYY');
end;
within the query - instead of using "to_date" use the new function.
that way instead of failing - it will give you back a default date.
-> There is not IsDate function .. so you'll have to create an object for it...
I hope you've got the idea and how to use it, if not - let me know.
I ended up using crazy regex that checks leap years, 30/31 days as well.
Here it is:
((^(0?[13578]|1[02])[\/.-]?(0?[1-9]|[12][0-9]|3[01])[\/.-]?(18|19|20){0,1}[0-9]{2}$)|(^(0?[469]|11)[\/.-]?(0?[1-9]|[12][0-9]|30)[\/.-]?(18|19|20){0,1}[0-9]{2}$)|(^([0]?2)[\/.-]?(0?[1-9]|1[0-9]|2[0-8])[\/.-]?(18|19|20){0,1}[0-9]{2}$)|(^([0]?2)[\/.-]?29[\/.-]?(((18|19|20){0,1}(04|08|[2468][048]|[13579][26]))|2000|00)$))
It is modified version of the answer by McKay here.
Not the most efficient but it works. I'll wait to see if I get a better alternative.
I'm attempting the use QuerySetCell to change value of a specific column in a query object, and have been receiving this error:
Column names must be valid variable names. They must start with a letter and can only include letters, numbers, and underscores.
The reason for this error, and the complication here, is that the columns I am trying to update have some integers as names, taken from a separate record's key/ID. For example, the query may contain three columns with names: "6638, 6639, 6640".
Now, I understand why this error is occurring (though not necessarily why CF has this limitation), however cannot come up with a work around. The further complications are that I cannot make any changes as to how the initial query set's column names, and need to preserve the column names and their order for when I convert the query to a JSON string and update my results table using the JSONified query.
Has anyone encountered this issue before, and if so how were you able to work around it, or were you forced to change how the columns were named in your initial query?
Using CF8 and have the ability to edit the JSONified query after it is returned from my Ajax handler if that makes a difference.
You can use bracket notation to set the values in a query (at least you can in CF9 - I do not have CF8 installed to test).
The sytax is pretty simple:
<cfset queryName[columnName][row] = "some new value" />
From your example, you could use this:
<cfset myQuery["6638"][1] = "moo" />
This will set the value of the '6638' column in the first row to 'moo'. If you have multiple rows being returned, you would need to set each row.
I have a query that I am calling to update an email service. Most times it will have data in it, but in testing I came across the situation of it not returning any data because there was no data to return. In the case of no data it returns the error "Variable EDITEDACCTS is undefined".
I have tried wrapping the query in a <cftry> but it doesn't "fail" per se so it does not trip the <cfcatch>. I have also tried defining the variable
var EditedAccts = QueryNew("")
as well as simply trying
<cfif NOT isDefined(#EditedAccts#)>
and it always returns "Variable EDITEDACCTS is undefined".
I need a production ready solution to this and I'm hoping somewhere here on SO can help me out.
Thanks in advance for your help.
I have just found the answer. You set the "result" parameter in the query call and then you can check the recordcount field returned.
<cfquery name="EditedAccts" datasource="mydatasource" result="queryResult">
...query goes here...
</cfquery>
When using the "result" parameter you get a struct returned with the sql used, the cached setting, the execution time and the record count.
Now I can check the record count and proceed from there.
Hopefully this will help someone in the future.
I tried using result="queryResult" but when I tried to reference the query name I got something like this error - "The value of the attribute query, which is currently EditedAccts, is invalid". Instead, I used something like IsDefined("#EditedAccts#") - including the value in quotes did the trick for me. I am only new to ColdFusion but I am learning quickly that values in quotes are entirely different to values not in quotes, in terms of how a function will interpret a parameter.
Given a query (pseudo-code):
<cfquery name="myquery">SELECT * FROM stuff</cfquery>
How do I get rid of the first record? In this case, altering the SQL is not an option.
I have tried: myquery.RemoveRows(0,1); but received an error:
No matching Method/Function for Query.REMOVEROWS(numeric, numeric) found
I'm on Railo 3 BTW
Lo and behold:
myquery.RemoveRow(1);
Does exactly what I wanted. Leave it to Railo to do things a little differently!
Can't think of a way offhand to remove a row from the original object. Two things I can think of are:
do a query of queries. That assumes you'd be able to identify the records you don't want and specify them in the WHERE.
construct a new query with queryNew(). Loop over the original query doing a querySetCell() into the new query for the records that you want. This functionality could be incorporated into a UDF pretty easily. In fact, stating that made me think to check cflib.org. See #3
Check cflib.org :) See http://www.cflib.org/udf/QueryDeleteRows
"RemoveRows" is actually a call to the underlying Java method, so you have to cast those numbers. like so:
myquery.RemoveRows(
JavaCast( "int", 0 ) // starting row, zero-based
,JavaCast( "int", 1 ) // number to delete, returns error if too many
)
So probably the method is "int,int", and if you don't cast it, it looks like "numeric,numeric". One might argue that this is undocumented, but it's so succinct that you could (as I did) wrap it in a function, so that if it changes you just need to replace the contents of the function with an alternative workaround.
Railo has added removeRows, see here. My ACF code that uses this method now runs on Railo too, no changes.
With this, the Railo implementation now matches ACF. (Note also that the original Railo implementation was 0 based, while the new one and the ACF version are both 1 based.)
The way I would normally do it is with a query of queries such as the following:
SELECT * FROM myquery
LIMIT {1,10000}
That should work in Railo. What it does is offset the query by one and pulls 10000 records.
You could also do this:
SELECT * FROM myquery
WHERE {primarykey} <> {value}
Where it selects all records except for the primary key value that you pass in.
The awesome thing about ColdFusion is that there are tons of ways to do exactly what you are looking for.
You could just skip the row when you process the results:
<cfoutput query="myquery">
<cfif myquery.currentrow GT 1>
<!---Do the work here. --->
</cfif>
</cfoutput>