I have the unfortunate task of cleaning up a bunch of old ColdFusion code. Queries are all over the place, I am working on moving them all to common CFCs for easier maintenance.
I am running into a problem because cfquery is automatically converting the single quotes to double-single-quotes. How can I override that behavior?
More specific information is below.
So here is the query I started with:
<cfquery name="getObjectInfo" datasource="#BaseDS#">
SELECT groupName AS lastname, '[Group]' AS firstname
FROM groups
WHERE groups.group_id = #objectreference_id#
</cfquery>
The weird thing here is that a literal is being "selected", because of the way we want it displayed (again, I didn't write this, I'm just trying to clean it up a little). So in the common function, there is an optional parameter for the select clause:
<cffunction name="fSelGroup" access="public" returntype="query"
hint="Returns query selecting given group.">
<cfargument name="intGroupID" type="numeric" required="true"
hint="ID of group to be returned." />
<cfargument name="strSelectAttributes" type="string" required="false"
hint="Attributes to be selected in query"
default="*" />
<cfquery name="getObjectInfo" datasource="#Application.DataSource#">
SELECT #Arguments.strSelectAttributes#
FROM Groups
WHERE Group_ID = #Arguments.intGroupID#
</cfquery>
<cfreturn getObjectInfo />
</cffunction>
Here is the problem: When I pass in "GroupName AS LastName, '[Group]' AS FirstName" for the strSelectAttributes parameter, the query that is sent to the database is:
SELECT GroupName AS LastName, ''[Group]'' AS FirstName
FROM Groups
WHERE Group_ID = 4
You see, my quotes got "sanitized" into an invalid query.
ColdFusion does not escape all single quotes, but only those that arrive in the query through variable interpolation. This is the offender:
SELECT #Arguments.strSelectAttributes#
This is usually a helpful thing and a small line of defense against SQL injection attacks. So rule number one is (here and everywhere else): Don't build your SQL string from variables.
If you positively have to use variables to build an SQL string, despite all the possible negative effects, use the PreserveSingleQuotes() function:
SELECT #PreserveSingleQuotes(Arguments.strSelectAttributes)#
This function stops ColdFusion from auto-escaping the single quotes.
And any other function call does the same thing, by the way. Try:
SELECT #LCase(Arguments.strSelectAttributes)#
which means that PreserveSingleQuotes() is really just a no-op that turns a string into a function result, preventing the automatic variable interpolation routine from happening.
Put a call to preserveSingleQuotes() around your variable. It's made specifically for writing dynamic SQL. Also, you really, really should use cfqueryparam for your values, and I hope you're sanitizing your input somehow so that arguments.strSelectAttributes can't contain something like ';drop table groups; in it.
<cfquery name="getObjectInfo" datasource="#Application.DataSource#">
SELECT #preserveSingleQuotes(Arguments.strSelectAttributes)#
FROM Groups
WHERE Group_ID = <cfqueryparam value="#Arguments.intGroupID#" cfsqltype="cf_sql_integer"/>
</cfquery>
If you really wanting to clean up the code step two is converting that spaghetti into stored procedures.
Related
I'm an "old dog" and largely self-taught on this stuff and can usually make things work (primative and convoluted as it might be), but this is the first thing that has me really stymied.
I didn't want to burden everyone with a lot of stuff to try to explain, but, here is perhaps a better explanation and example:
(#Leigh - and thank you for your time and help!) - The query is dynamic because what I desire to have is a single "universal" page combination (form page plus accompanying action page) that is used to edit multiple different (but fairly similar) record sets (so that I don't have to write a whole bunch of individual form/action page pairs).
When this "universal" "change" form page is invoked, it is passed the "ID" variable for the particular record to be edited, along with a "listID" variable unique to the particular record set containing the record to be edited.
Using the "URL.listID", the form page then looks at a pre-defined included list of record set variables (datasource, query table, field for column 1, field for column 2, etc.) pertaining to the value of the "listID" and sets (using ) the dynamic variables. Example - if "listID" is "5", which has only one column:
<cfif #URL.listID# EQ 5>
<cfset page_title = 'Change Member Role Picklist'>
<cfset datasource = '#Session.db_docs#'>
<cfset query_tbl = 'tblMemberRole'>
<cfset columns = 1>
<cfset column1_label = 'Member Role'>
<cfset column1_field = "role">
<cfset column1_input_type = "text">
<cfset column1_input_size = "100">
<cfset column1_input_maxlength = "100">
</cfif>
The query uses those variables ("ID" plus the others it got from the above list) to retrieve the individual record to be edited, and populate the "change" form.
Run query:
<cfquery name="cfqGetItem" datasource="#datasource#">
SELECT *
FROM #query_tbl#
WHERE ID = <cfqueryparam value="#URL.ID#" cfsqltype="cf_sql_integer">
</cfquery>
My "change" form (abbreviated here without table HTML) to be populated would be:
<form name="form_item_chg" action="chg_item2.cfm" method="post" enctype="multipart/form-data">
<input type="text" name="#column1_field#" maxlength="#column1_input_maxlength#" size="#column1_input_size#" value="#cfqGetItem[column1_field][currentRow]#">
<input type="Submit" value="Post Changes">
</form>
However, instead of the "change" form being populated with the VALUE for field "role", it instead is trying to use the variable name "column1_field", which it says is (true, of course) undefined in the query.
When I tried "#cfqGetItem[column1_field][currentRow]#", it says "Variable currentrow is not defined".
When I tried "#cfqGetItem.column1_field#", it says "Element column1_field is not defined in query cfqGetItem".
I apologize in advance for not knowing/using all the correct terminology, and hope I am explaining this reasonably clearly. I suspect I will have to revert to writing individual form-page/action-page pairs. Thank you to all for your time and help!
ORIGINAL POST:
I'm not highly technical, and this is probably something simple, but here is
my dilemma, where I am attempting to retrieve a single record using a variable name in the query.
First, I define some variables:
<cfset ID = #Form.ID#<!--- the single record I want to retrieve, passed from a form --->
<cfset datasource = 'MyDatabase'>
<cfset query_tbl = 'MyDatabaseTable'>
<cfset field1 = 'actual_fieldname1'><!--- field in MyDatabaseTable --->
<cfset field2 = 'actual_fieldname2'><!--- field in MyDatabaseTable --->
ETC.
Then, to retrieve this single record, I run a query using those variables:
<cfquery name="cfqGetItem" datasource="#datasource#">
SELECT *
FROM #query_tbl#
WHERE ID = #ID#
</cfquery>
Then, I attempt to display the query output:
EITHER AS
<cfoutput>
<p>#cfqGetItem.field1#
<p>#cfqGetItem.field2#
</cfoutput>
OR, AS
<cfoutput>
<p>#cfqGetItem[field1][currentRow]#
<p>#cfqGetItem[field2][currentRow]#
</cfoutput>
In each case, I get a similar CF error message: "Element field1 is not defined in query cfqGetItem", or "Variable currentrow is not defined".
How can I get the query output to generate the actual values for the record instead of the variable names?
Thank you very much for any help!
If you change this:
<cfoutput>
<p>#cfqGetItem[field1][currentRow]#
<p>#cfqGetItem[field2][currentRow]#
</cfoutput>
to this:
<cfoutput>
<cfloop query="cfqGetItem">
<p>#cfqGetItem[field1][currentRow]#
<p>#cfqGetItem[field2][currentRow]#
</cfloop>
</cfoutput>
You should be ok. However, this is the simplest way
<cfoutput query="cfqGetItem">
<p>#actual_fieldname1#
<p>#actual_fieldname1#
</cfoutput>
Setting the table and field names to variables complicates matters. Unless they really can vary, don't do it. Also, the cfqueryparam tag is your friend. Use it in places like this:
where id = <cfqueryparam
cfsqltype = "cf_sql_integer"
value = "#id#">
You can try out like this also:
<cfquery
name="GetParks" datasource="cfdocexamples"
>
SELECT PARKNAME, REGION, STATE
FROM Parks
ORDER BY ParkName, State
</cfquery>
<cfoutput>
<p>#GetParks.PARKNAME#
<p>#GetParks.REGION#
</cfoutput>
<cfoutput >
#GetParks['state'][GetParks.RecordCount]#
</cfoutput>
<cfoutput >
#GetParks['state'][2]#
</cfoutput>
I am new to ColdFusion and want to rewrite my project from ASP.NET to learn ColdFusion step by step.
My SQL Server stored procedure for authentication (ASP.NET) looks like this:
CREATE PROCEDURE sp_auth
#suser VARCHAR(20),
#spswd VARCHAR(20)
AS
SELECT COUNT(*) FROM Users.SUsers WHERE suname=#suser AND supasswd=#spswd AND off_bit=0
GO
Application.cfc snippet.
<cfstoredproc procedure="sp_auth" datasource="cftraderelay">
<cfprocparam value="#cflogin.name#" cfsqltype="CF_SQL_VARCHAR" maxlength="20" type="in">
<cfprocparam value="#cflogin.password#" cfsqltype="CF_SQL_VARCHAR" maxlength="20" type="in">
<cfprocresult name="auth_pass" maxrows="1">
</cfstoredproc>
<cfif auth_pass GTE 1>
<cfloginuser name="#cflogin.name#" password = "#cflogin.password#" roles="#roles#">
<cfelse>
<cfoutput>
<h2>Your login information is not valid.<br> Please Try again</h2>
</cfoutput>
<cfinclude template="login.cfm">
<cfabort>
</cfif>
What is right form of 'cfif auth_pass GTE 1' logic? How to access the value of 'auth_pass'.
Cos I get "Complex object types cannot be converted to simple values" error by far.
Am I strictly supposed to use out type of cfprocparam in this case, not cfprocresult (modify SQL query for the OUTPUT variable)?
Can I access cfprocresult value right in the cfif statement?
auth_pass contains a recordset, not a number, so you cannot compare it to 1. You perhaps want auth_pass.recordCount?
The best tool for examining the contents of a variable is <cfdump>. That and familiairising yourself with the docs for the code you're using, eg: <cfprocresult>, <cfstoredproc>, <cfquery> (the latter for details on query objects) if you run into problems and can't guess the solution.
This question already has answers here:
Coldfusion - variable field name when looping through database query results
(2 answers)
Closed 9 years ago.
Edit: The QUESTION is a duplicate, but the ANSWER is not!
I have the following code:
<cfquery name="contact" datasource="thesource">
SELECT * FROM #table# WHERE foo = '#bar#'
</cfquery>
and then later on (this is the problem part):
<cfloop from="0" to="9" index="i">
<cfset thisvar = Evaluate("contact.check" & i) />
<cfoutput>
#thisvar#
</cfoutput>
</cfloop>
Upon execution, it throws a nice big "Variable contact.check0 is Undefined". However, #contact.check0# will output just fine if hardcoded.
Any ideas on how to fix this?
Note:
I have seen Coldfusion - variable field name when looping through database query results, and although the problem seems to be exactly the same, the solutions do not work. As per the comments, I am also getting a "cannot be converted to a number" error. I notice Tomalak mentions "a little catch", but never says what it is. In the article he links to, I have tried every syntactically equivalent form, and it all throws an error...either cannot convert to a number or is not defined.
Also, I'm aware Evaluate() has overhead and "should not be used". I'll take any solution that works, whether it has Evaluate in it or not.
This is on ColdFusion 9.
Thank you
Edit: while a similar question already had an answer, this question had a different cause. See accepted answer below.
First of all, standard blub about escaping inputs. cfqueryparam exists for a reason. That aside you can do this without evaluate. Give this a try instead, including a check to make sure there is actually a result
<!--- make sure that there is actually a result --->
<cfif contact.RecordCount EQ 0>
NO RESULT
<cfelse>
<cfloop from="0" to="9" index="i">
<!--- assumes that you want row 1 --->
<!--- check.CurrentRow could also be used instead of 1 --->
<cfset thisvar = contact['check'&i][1] />
<cfoutput>
#thisvar#
</cfoutput>
</cfloop>
</cfif>
I suspect that it is attempting to Evaluate the column without specification of the row number, while when hard-coding it you'll find it uses the current row.
edit as Dan Bracuk pointed out this is an exact duplicate of the question you linked in your question, Coldfusion - variable field name when looping through database query results
This is ridiculous and absurd. Working code (moved things around a bit):
<cfoutput>
<cfloop from="0" to="9" index="i">
<cfset thisvar = contact["check"&i][1] />
#thisvar#
</cfloop>
</cfoutput>
NOT working code:
<cfoutput query="contact"> <!--- this line here --->
<cfloop from="0" to="9" index="i">
<cfset thisvar = contact["check"&i][1] />
#thisvar#
</cfloop>
</cfoutput>
Someone else had put cfoutput tags around the whole page. While this normally wouldn't be a problem, they ALSO declared the query attribute. This somehow overwrote the original query from the db. Calling contact.check0 worked just fine, but building the variable name dynamically, I think, was attempting to reference the cfoutput's query attribute.
Thanks to Peter Boughton for the suggestion to extract the problem code...which led me to this realization, and Simon for posting the syntactically correct code. Points go to them.
Via this question I've been told to start using cfqueryparam for my data, to prevent SQL injection attacks.
How do I use it for my forms? Right now I've been going over Ben Forta's book, Vol 1 and been passing data to my form, then to a form processor that calls a CFC. The CFC takes them in as a cfargument then injects that into the database with any type="x" validation.
Io use the cfqueryparam, I use that on the query itself and not even declare cfargument?
You can still use a CFC, but remember that string data passed as a function argument will still need <cfqueryparam>. Here is an example:
<cffunction name="saveData" access="public" returntype="void" output="false">
<cfargument name="formVar" type="string" required="true" />
<cfquery name="LOCAL.qSave" datasource="myDSN">
insert into myTable (col1)
values (<cfqueryparam cfsqltype="cf_sql_varchar" value="#ARGUMENTS.formVar#" />)
</cfquery>
</cffunction>
The important habit to get into is to always use <cfqueryparam>, even in CFCs.
Here is some more info on those edge-cases where you might find it hard to use <cfqueryparam>.
Hope that helps!
A form field value like 45,234 is to be inserted into the DB as 45234. When I try to do insert for 45,234, all it gets into the DB is 0.Note:- I am able to insert 45.234as 45.234 in the SQL DB.
The form field is named costOfShares, which is to be inserted into the table ShareVales (SQL Server 2005). I am using CF8.
SQL
table:-ShareVales; field:-costOfShares; DataType:-float
ColdFusion (form page)
<li>
<cfinput size="8" maxlength="20" name="costOfShares"
id="costOfShares" value="#share.GetcostOfShares()#">
</li>
Share.cfc:-
<cfcomponent>
<cfscript> Variables.CostOfShare </cfscript>
<cffunction name="GetCostOfShare" returntype="numeric" access="public" output="false">
<cfscript> variables.CostOfShare; </cfscript>
</cffunction>
<cffunction name="SetCostOfShare" retuntype="void" access="public" output="false">
<cfargument name="costOfShare" type="string" required="true">
<cfscript> variables. costOfShare = arguments. costOfShare; </cfscript>
</cffunction>
</cfcomponent>
ColdFusion (query page)
<cffunction>
<cfargumnet>
<cfquery …>
INSERT INTO ShareVales(shareUnitId, costOfShares)
VALUES (
<cfqueryparam cfsqltype="cf_sql_integer"
value="#arguments.share.GetshareUnitId()#">,
<cfqueryparam cfsqltype="cf_sql_float"
value="#arguments.share.GetcostOfShares()#">);
</cfquery>
</cffunction>
When I use the following directly in the Query Editor:
INSERT into share(setCostOfShare)
values(Cast(replace('5,322,444',',','') as float))
it succeeds. The error is while using cfquery/cfqueryparam in the ColdFusion template.
So, how do insert the 45234 when the form field costOfShares contains a value with commas?
If they type a comma, strip it out first before trying to insert.
LSParseNumber("45,234", "French (standard)")
Or if "French (standard)" is already your system standard system default locale, just use:
LSParseNumber("45,234")
Or...
<cfset setLocale("French (standard)")>
<cfset value = LSParseNumber("45,234")>
They'll return 45.234 (if you want 45234 instead, use US locale, see doc!)
I'd suggest you to store costOfShares as a valid ColdFusion numeric value (aka, use '.' instead of
','), and only use LSNumberFormat() or LSLSCurrencyFormat() to display the value on the view. That way you don't even have to LSParse the number, unless the number is created by the user, and he uses ','.
presumably the datatype is a float or something. comma's are not valid characters in a float (unless for European locales). You probably want instead to get the value out of the DB with a comma (but not necessarily store it with one). That would probably mean using the COVERT function during a SELECT:
http://technet.microsoft.com/en-us/library/ms174450.aspx
Not sure if this will help or not, but just put the replace inside of cfqueryparam. That way it hands SQL Server the "already cleaned up version".
CAST(<cfqueryparam cfsqltype="cf_sql_float"
value="#REPLACE(arguments.share.GetcostOfShares()',',','')#"> AS FLOAT)
Or, have CF run a replace on the variable and clean it up before you even try the query.
Or, have CF run a replace on the variable and clean it up before you even pass it in as an argument.