Updating date with cfquery in coldfusion - coldfusion

Can somebody please suggest what am I doing wrong in the query below:
Data type of imageUpdateDate column is date. I also tried with datetime with no luck.
Any help would be greatly appreciated.

imageUpdateDate ='#now()#'
The query is failing because of the single quotes around #now()#.
The reason is that the string representation of #now()# already contains single quotes:
{ts '2022-03-31 22:35:14'}
By wrapping that value in single quotes, you're creating an invalid sql statement:
UPDATE used_listings_v2
SET [image] = 'new_file_name.ext'
, imageUpdateDate ='{ts '2022-03-31 22:35:14'}'
WHERE id = '1'
Which when executed, fails with an error:
Error Executing Database Query. [Macromedia][SQLServer JDBC
Driver][SQLServer]Incorrect syntax near '2022'.
Although removing the single quotes, or using getDate() instead, would resolve the error, the fact that single quotes can so easily break the query means that anyone attempting to attack your database with malicious sql can do the exact same thing!. That's dangerous for any application. Especially one running on the EOL'd CF10, which no longer receives security updates. Consider upgrading or switching to Lucee.
Anyway, the correct way to resolve the error is by using cfqueryparam on all of the query variables. Not only does it eliminate the need for using single quotes around parameter values, it also helps protect your database against sql injection. (I don't know your column data types, so modify the cfsqltypes as needed)
<cfquery datasource="...">
UPDATE used_listings_v2
SET [image] = <cfqueryparam value="#dbFilename#" cfsqltype="cf_sql_varchar">
, imageUpdateDate = <cfqueryparam value="#now()#" cfsqltype="cf_sql_timestamp">
WHERE id = <cfqueryparam value="#edit#" cfsqltype="cf_sql_integer">
</cfquery>
*Note: It's a good practice to always scope your variables. For example, use FORM.dbFileName instead of just dbFileNameity updates.

Related

Can we use convert(int,Employeenumber) function in a ColdFusion Query of Query?

Can we use convert(int,Employeenumber) function in ColdFusion query of query feature? When I try it I am getting some error.
I am getting records from a stored procedure. When I try to re-query those records with a query of query, using the convert() function, it is throwing an error.
Actually the column is of type varchar and I want to convert that column into an int. Then I want to order by that column, but it is not working. Here is the QofQ:
<cfquery name="qEmployees" dbtype="query">
Select convert(int,employeenumber) as employeenumber
...
</cfquery>
From Conversation:
CONVERT() is usually language-dependent SQL, whereas CAST() is a more general method of essentially doing the same thing, but in this case, it's also what the QoQ engine chose to use.
It's important to remember that the version of SQL in ColdFusion Query of Query is not the same as traditional SQL.There are a lot of things you can't do with a Query of Query, and I definitely wouldn't recommend it for large initial data sets, since it's an in-memory operation.
Also, this issue doesn't appear to be a data type conversion issue. It seems to be an issue with NULL in the data. ColdFusion, especially anything older than the ACF 2018 version, doesn't play nice with NULLs. QoQ doesn't have an ISNULL() or COALESCE() functionality and work-arounds would probably be more trouble than they're worth. The easiest thing would be to modify the original base query to return the needed values.

Working with ColdFusion - Looping a procedure

I'm wondering if there is a better way to do what I'm doing. It works but I feel there should be a better way. If my query result in 20K records for example, I'm getting "The request has exceeded the allowable time limit Tag: CFQUERY"
<cfquery name="GetMyRecords" datasource="MyDSN">
SELECT idnumber,PrefAddr,...more colums
FROM um_valid
WHERE userid = <cfqueryparam cfsqltype="cf_sql_varchar"
value="#session.userid#">
AND session_id = <cfqueryparam cfsqltype="cf_sql_numeric"
value="#session.Session_Id#">
AND status NOT IN (<cfqueryparam cfsqltype="cf_sql_varchar"
value="X,C">)
</cfquery>
I also have an existing store procedure that expect some values from the query to do what it's supposed to do. So I'm looping it like this:
<cfloop query="GetMyRecords">
<cfstoredproc procedure="MyProc" datasource="MyDSN">
<cfprocparam type="In" cfsqltype="CF_SQL_VARCHAR"
dbvarname="#id_number" value="#Trim(idnumber)#">
<cfprocparam type="In" cfsqltype="CF_SQL_VARCHAR"
dbvarname="#Aaddr_pref_ind" value="#Trim(PrefAddr)#">
----- still more params to be passed----
</cfstoredproc>
</cfloop>
Does ColdFusion has a better technique to avoid either time out error or 500 error?
Like another poster mentioned, reducing the number of database calls should be a priority to you. I suggest joining data (if possible) in your first query rather than looping your query and querying again.
To fix the time issue, you can put requestTimeout in your page to override the default timeout. The time is in seconds.
<cfsetting requestTimeOut = "600">
See this explanation.
Your current approach is to make 1 query, which contains n records. Then loop over that record set, calling a query for each record. This results in your calling n + 1 queries per request. As the volume of data returned by your first query increases, so too does the volume of overall queries made to the database. In your case, you're trying to make 20,001 calls to the database in a single request.
Ideally, you need a solution that involves one call to a stored procedure with a properly optimized query that can return all of your data in a single record set. If you don't need to dump all the data onto the page at the same time, then create a paginated query that will return x number of records per page. Then the user can go page by page through the query or provide a search form with additional filters to allow the user to reduce the overall size of the records returned.
Need to see full queries to give you an example, but in general, this is a really useful thing in CF to help you out!
You need to look into the attribute called group. This lets you specify a column to group your output by. This actually will eliminate the need for the stored proc you are calling entirely.
The way it works is letting you create sub-set outputs based on grouping. So for example, you could do this with your id's and output that group.
You can also have multiple of these and have header and footer sections for each one for display purposes or just logic manipulation.
This lets you query the entire dataset and then manipulate it in the loop without having subqueries which is ultra inefficient and cringe worthy.
This is something quite unique to ColdFusion, check it out!
http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7ff6.html

Is it possible to cache a ColdFusion Query of Query

I've got query that is returning all translations for a site. It does this by getting all translations that are in the users desired language, then the remaining that are in the site default language, then any other strings that have not been translated. I'm using cachedwithin on that query since the data doesn't change often, and I'm resetting that queries cache if translations are modified. I'm then using ColdFusion's Query of Query to get the individual record that I'm after. This has increased performance considerably.
I was wondering if it's possible to further cache the Query of Query query to further increase performance. It appears to work as page load is 1/6 faster, however are there any gotchas with this technique?
The Query of Query is below.
<cfquery name="qryTranslation" dbtype="query">
SELECT
TranslationString
FROM
qryGetText
WHERE
TranslationHash = <cfqueryparam value="#StringHash#" cfsqltype="cf_sql_varchar">
AND DesiredLanguageID = <cfqueryparam value="#Arguments.LanguageID#" cfsqltype="cf_sql_bigint">
</cfquery>
Is it possible to cache a ColdFusion Query of Query
Yes, it is possible.
however are there any gotchas with this technique?
You queries will be cached based on its signature, so in your case the StringHash and Arguments.LanguageID. If you have a cached QofQ for every translation on a page, on many pages on your site, then you could potentially max out the "Maximum number of cached queries" value. If this happens other, potentially larger and more important, cached queries in the query cache could be evicted.
Calculating a suitable "Maximum number of cached queries" could be determined by load testing and using the build in server monitor to monitor the number of queries in the cache.
There is one big gotcha with caching a query of query.
The documentation for caching a query states that:
To use cached data, the current query must use the same SQL statement, data source, query name, user name, and password.
However a Query of Query does not have a data source, user name or password, so you lose a lot of "over cache" protection. The query as it stands in your question will conflict with any other queries on your server that have the same name and formatting. So if you have more than one website that uses this code then the first website that is loaded will dictate the translations used on the rest of the websites.
A quick way around this is to trick the query into being more constrained.
<cfquery name="qryTranslation" dbtype="query">
SELECT
TranslationString
FROM
qryGetText
WHERE
TranslationHash = <cfqueryparam value="#StringHash#" cfsqltype="cf_sql_varchar">
AND DesiredLanguageID = <cfqueryparam value="#Arguments.LanguageID#" cfsqltype="cf_sql_bigint">
AND '#Variables.DSN#' = '#Variables.DSN#'
</cfquery>
Change Variables.DSN to be the value of the datasource attribute in the main query. If you don't trust that variable, then also make it a cfqueryparam on both sides of the operator.

Methods for preventing SQL Injection in ColdFusion

I'm wondering if the # symbol is enough.
This is a part of the sql command that I'm using
WHERE login='#FORM.login#' AND password COLLATE Latin1_General_CS_AS = '#FORM.password#'
I'm trying to test it with user names such as ' OR 1=1 and variants of it, but even though it's not working I don't want to have a false sense of security.
I've read that using <cfqueryparam> can prevent this form of attack, are there any other ways?
The way to go is <cfqueryparam>. It's simple, straight-forward, datatype-safe, can handle lists (for use with IN (...)) and can handle conditional NULLs. Plus you get a benefit out of it in loops - the query text itself is sent to the server only once, with each further loop iteration only parameter values are transferred.
You can use '#var#' and be relatively safe. In the context of a <cfquery> tag ColdFusion will expand the value of var with single quotes escaped, so there is some kind of automatic defense against SQL injection. But beware: This will — by design — not happen with function return values: For example, in '#Trim(var)#' single quotes won't be escaped. This is easily overlooked and therefore dangerous.
Also, it has a disadvantage when run in a loop: Since variable interpolation happens before the SQL is sent to the server, ColdFusion will generate a new query text with every iteration of a loop. This means more bytes over the wire and no query plan caching on the server, as every query text is different.
In short: Use <cfqueryparam> wherever you can:
WHERE
login = <cfqueryparam value="#FORM.login#" cfsqltype="CF_SQL_VARCHAR">
AND password = <cfqueryparam value='#Hash(FORM.password, "SHA-512")#' cfsqltype="CF_SQL_VARCHAR">
Instead of a simple Hash(), you should indeed use a salted hash, as #SLaks pointed out in his comment.
An even better way to go would be to use stored procedures for everything.

How can I use query-of-query UNION on n-recordsets when var scoping is needed?

I would like to be able to do a query of a query to UNION an unknown number of recordset. However when doing a query-of-query dots or brackets are not allowed in record set names.
For example this fails:
<cfquery name="allRecs" dbtype="query">
SELECT * FROM recordset[1]
UNION
SELECT * FROM recordset[2]
</cfquery>
Using dynamic variable names such as "recordset1" work but this is in a function and needs to be var-scoped so I can't build up the variable names dynamically without producing memory leaks in a persisted object.
Any other ideas?
After posting the question I came up with a couple solutions but there might be a better one out there
I could write dynamically named variables to the arguments scope and then reference them without their scope in query
Create a function that accepts 2 recordsets as arguments and returns one combined recordset. This could be looped over to progressively add a recordset at a time. I'm sure this is very inefficient compared to doing all UNIONs in one query though.
Difficult task. I could imagine a solution with a nested loop based on GetColumnNames(), using QueryAddRow() and QuerySetCell(). It won't be the most efficient one, but it is not really slow. Depends on the size of the task, of course.
Your "create a function that combines two recordsets" could be made much more efficient when you create it to accept, say, ten arguments. Modify the SQL on the fly:
<cfset var local = StructNew()>
<cfquery name="local.union" dbtype="query">
SELECT * FROM argument1
<cfloop from="2" to="#ArrayLen(arguments)#" index="local.i">
<cfif IsQuery(arguments[local.i])>
UNION
SELECT * FROM argument#local.i#
</cfif>
</cfloop>
</cfquery>
<cfreturn local.union>
After a quick bit of poking around, I found this:
queryConcat at CFLib.org. It uses queryaddrow/querysetcell to concatenate two queries.
I added a quick function (with no error checking, or data validation, so I wouldn't use it as-is):
<cffunction name="concatenate">
<cfset var result = arguments[1]>
<cfloop from="2" to="#arraylen(arguments)#" index="i">
<cfset result=queryconcat(result, arguments[i])>
</cfloop>
<cfreturn result>
</cffunction>
As a test, I threw this together:
Which does, in fact, give you fred/sammy/fred.
It's probably not the most efficient implementation, but you can always alter the insert/union code to make it faster if you wanted. Mostly, I was aiming to write as little code as possible by myself. :-)
all of the solutions added here should work for you, but I would also mention that depending on how much data you are working with and the database you are using, you might be better off trying to find a way to do this on the database side. With very large record sets, it might be beneficial to write the records to a temporary table and select them out again, but either way, if you can in any way rewrite the queries to let the database handle this in the first place you will be better off.