Is this a ColdFusion Bug in cfscript? - coldfusion

This is a valid query to return emails surrounded by single quotes from the users table.
SELECT '''' +email + '''' as email
FROM users
where fname = #fname
Yet when I try to do this in a cfscript (cf9) query like this :
var q = new Query(datasource="warewithal");
q.setSQL("SELECT '''' +email + '''' as email
FROM users where firstName= :firstName ");
q.addParam(name="firstName", value=trim(firstName), cfsqltype="cf_sql_varchar");
what I end up with is
Email
+email+
when I expected (and get by running the query in an analyzer)
Email
'bozo#clowns.com'
the cfscript is doing a perserveSingleQuote and not letting me add the single quote to the output.
Is this a bug or what am I doing wrong?

To answer the question in your headline: no, this is not a bug in CFScript. What you are demonstrating has nothing to do with CFScript at all.
However to answer the question you mean to be asking: yes, you have found a bug in Query.cfc.
Here's some code that demonstrates the bug (and demonstrates that it has nothing to do with CFScript, and everything to do with Query.cfc).
This code works fine:
<cfset query = new Query(datasource="scratch_mssql")>
<cfset query.setSql("
SELECT '''' + email + '''' as email
FROM users
WHERE firstName = '#firstName#'
")>
<cfset emailAddresses = query.execute().getResult()>
<cfdump var="#emailAddresses#">
Note that I have hard-coded the filter value into the SQL string. Yuck.
This code errors:
<cfset query = new Query(datasource="scratch_mssql")>
<cfset query.setSql("
SELECT '''' + email + '''' as email
FROM users
WHERE firstName = :firstname
")>
<cfset query.addParam(name="firstname", value=firstName, cfsqltype="CF_SQL_VARCHAR")>
<cfset emailAddresses = query.execute().getResult()>
<cfdump var="#emailAddresses#">
The error for me is:
[Macromedia][SQLServer JDBC Driver][SQLServer]An object or column name is missing or empty. For SELECT INTO statements, verify each column has a name. For other statements,
look for empty alias names. Aliases defined as "" or [] are not allowed. Change the alias to a valid name.
FYI: The SQL that CF had passed to the DB was:
SELECT '' '' + email + '' '' as email FROM users WHERE firstName = (param 1)
To prove that it's caused by ColdFusion mishandling your single quotes, this works:
<cfset query = new Query(datasource="scratch_mssql")>
<cfset query.setSql("
SELECT email as email
FROM users
WHERE firstName = :firstname
")>
<cfset query.addParam(name="firstname", value=firstName, cfsqltype="CF_SQL_VARCHAR")>
<cfset emailAddresses = query.execute().getResult()>
<cfdump var="#emailAddresses#">
So I'd raise a bug for this if I was you. Note: it's the same on CF9.0.2 and CF10.0.7
But, equally... if I was you I'd not be putting those quotes in there anyhow. Unless there's a very good reason, stick 'em in when you're doing the display, not when you're doing the data processing. I presume they're there for display purposes?

For what it's worth, I just got bit by this issue was well. What seems to work is a simple fix to your Query.cfc object (/[YOUR-CF-INSTALL]/CustomTags/com/adobe/coldfusion/Query.cfc). In the replaceDelimsWithMarkers() function, update this line (LN 341 for me)
newSql = newSql & SINGLEQUOTE & sqlArray[i] & SINGLEQUOTE & " ";
by removing the quoted space at the end, so it is:
newSql = newSql & SINGLEQUOTE & sqlArray[i] & SINGLEQUOTE & "";
Then it works great. I was looking up users, and needed to find one where last name is "O'Neill". No matter what I did I was getting "O' 'Neill" which as you state fails.
Can you think of any cases where this change/fix would have a negative impact? I can't off the top of my head, but for now we are going to copy the query.cfc file, rename it and use it as a custom tag ONLY when we need to run queries where we suspect extra apostrophe's might be used. The only difference will be this one little change.

Related

Splitting an address string into separate variables - Coldfusion

I've been struggle with splitting an address field in my database into it's separate components.
I am pulling address data from my database that is stored in column #company_address# . The data looks like this when I output it:
Address1
Address2 (Not always present)
City, State Zip (Sometimes there is a comma, sometimes there is not)
I would like to break up the string in #company_address# and assign each part of the address to it's own variable:
variable1 - address1
variable2 - address2
variable3 - city
variable4 - state
variable5 - zip
Once I have that data, I will enter it back into the db in its seperated form.
I have tried using the listtoarray function but it assigns the whole string to one array element and seems to skip the address2 line entirely.
Here is the code I am using:
<!--- Select ticket record --->
<cfquery name="get_ticket" datasource="#datasource#">
SELECT *
FROM closed_tickets
where ticket_id = #url.ticket_id#
</cfquery>
<cfoutput>
<cfset list = "#get_ticket.company_address#">
<cfset arr = listToArray (list, 'ch(13)' ,false,true)>
<cfdump var="#arr#">
</cfoutput>
Can anyone help??
The problem is that you are using chr(13) as a string. Use the following.
<cfset arr = listToArray (list, chr(13) , false, true)>

Fill spaces with dash in ColdFusion

I need to create a CFM page I that will have hyperlinks that are built from data from a database. For example, my database contains a column "Title" and a column "Subject. From "Title" and "Subject" I will create a hyperlink (because all of the pages follow the same scheme: This-is-the-title-this-is-the-subject.html
So the issue is that I need to enter "-" in the spaces. Because right now my CFOUTPUT is just this: This is the title this is the subject.html
Is there a simple way to convert this to This-is-the-title-this-is-the-subject.html?
i think this will solve your problem
<cfset pageName = "This is the title this is the subject.html" />
<cfset seoPageName = replace(pageName," ","-","all") />
I like to use listChangeDelims() for these tasks since you can efficiently add additional chars to change.
seo = listChangeDelims(original, '-', ' ,\' );

Coldfusion Query loop works in cf10 but not 9

Why does the following work in CF10 but not CF9?
<cfset out="">
<cfif isQuery( arguments.values ) >
<cfloop query="#arguments.values#" >
<cfset out = '#out#<option value="#value#">#label#</option>'>
</cfloop>
</cfif>
CF9 states that "Complex object types cannot be converted to simple values." for the line containing the cfloop. I'm using the Coldbox framework and it's debugger information shows that arguments.values is a query with Label & Value columns.
Prior to CF10, the query attribute of cfloop can only be a string - the name of the query - not the variable itself.
So, when you put #arguments.values# it is trying to convert the complex query object to a string, to obtain a name, which is where the error comes from.
It works in CF10 because the attribute has been updated to also allow a query value.
side notes:
This line of code can be simplified:
<cfset out = '#out#<option value="#value#">#label#</option>'>
to:
<cfset out &= '<option value="#value#">#label#</option>'>
Also you very likely should be using HtmlEditFormat* on at least label, and perhaps value too.
*(or encodeForHtml if it only needs to work in CF10+)

Why can't I use the same cfqueryparam in two locations in the same query when using CFScript?

I'm not sure if I am doing something wrong, but it seems that you can not use a cfqueryparam more than once in a single query if you write it in CFScript.
This behavior is not consistent with CFML. I just re-wrote a similar query from CFML to CFScript and I'm getting the following error: cfsqlparam 'id' is not defined
local.query = new Query();
local.query.setSql("
SELECT id
FROM myTable
WHERE myTable.id = :id OR myTable.parentId = :id
");
local.query.addParam(name="id", cfsqltype="CF_SQL_INTEGER", value=arguments.id, maxlength=10);
local.query.execute().getResult();
If I take out the OR myTable.parentId = :id it works perfectly well. Do I have to create a param for every location I intend to use one?
If it was a CFQuery you would have one cfqueryparam for each value:
where myTable.id = <cfqueryparam... value="#arguments.id#" />
or myTable.parentid = <cfqueryparam ... value="#arguments.id#" />
so I'm guessing you need to do the same thing in script:
local.query.setSql("
SELECT id
FROM myTable
WHERE myTable.id = :id OR myTable.parentId = :pid ");
local.query.addParam(name="id", cfsqltype="CF_SQL_INTEGER",value=arguments.id,maxlength=10);
local.query.addParam(name="pid",cfsqltype="CF_SQL_INTEGER",value=arguments.id,maxlength=10);
As for the question "why can't the one addParam address both placeholders" I'm guessing it comes down to how the query is parsed by ColdFusion - because you specify two param placeholders the parser probably expects to find two params defined.

Session Variables, welcome messages

Why does this not work? My welcome message, it just doesn't show up:
<p>Welcome <cfoutput>#Recordset1.UserID#</cfoutput>.</p>
The session variable on the login page I created is:
<cflock timeout=999 scope="Session" type="Exclusive">
<cfset Session.IDUsers =''>
</cflock>
is this incorrect? On the index page where I'm trying to display my welcome message I have:
<cfquery name="Recordset1" datasource="cfGossip">
SELECT *
FROM users
WHERE users.IDUsers = <cfqueryparam value="#Session.IDUsers#">
</cfquery>
I'm not sure if this works, or is necessary?
If you set the userid stored in the session to be the empty string, when you query on it, you will only get users for whom the id is the empty string, which shouldn't be any of them. Therefore, the query is returning an empty set, and your page is (correctly) not displaying a user id.
How are you initially identifying the user? Are you querying against a database when they log in? Are you storing a cookie? Reading Tarot cards? For this to work, at some point, you have to store the correct userid, probably in the session. To do that, you need to first identify who the user is.
Also, if you are using CF6+, you probably do not need the cflock. It is now used to prevent race conditions, as CF is now thread-safe.
Looks like you're just starting with CF, welcome to the community.
My understanding of your code makes the structure look like the following, if I'm understanding you correctly:
<cfset session.idUsers = '' />
<cfquery datasource = "cfgossip" name = "recordset1">
SELECT * FROM USERS WHERE USERS.ID_USERS = <cfqueryparam cfsqltype = "cf_sql_integer" value = "#session.idUsers# />
</cfquery>
<cfoutput>Welcome #recordset1.userID#</cfoutput>
The reason this doesn't work is because your session.idUsers value is blank. Assuming you have a user in your database with an ID_USERS value of 1, you could change the CFSET to look like and it should return results.
Additionally, while it's great to see you using CFQUERYPARAM, I'd recommend including a CFSQLTYPE attribute in the tag whenever possible to provide an added line of defense against injection attacks. You can check out http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=Tags_p-q_18.html to see the list of available types.
Is there anywhere in your code where you set your session.IDUsers? You initialize it as a blank ''. Coldfusion does not populate it for you. The session scope is a place that will remember things for that user that you put there for a specified period of time inactivity, usually 20 minutes. So hopefully, somewhere before you run your query you have additional logic that fills that in, otherwise you are asking the database for a user named, ''.
This is just a point of style, but the following may work better for you:
<cfset Session.IDUsers =''>
<!--- Do something here to populate Session.IDUsers --->
<!--- Creates a blank query - not necessary, but can reduce errors later --->
<cfset Recordset1 = queryNew("UserID")>
<!--- Populate the query from the database --->
<cfquery name="Recordset1" datasource="cfGossip">
SELECT *
FROM users
WHERE users.IDUsers = <cfqueryparam value="#Session.IDUsers#">
</cfquery>
<!--- If the query has data, use it, otherwise show generic message --->
<cfoutput>
<cfif Recordset1.recordcount>
<p>Welcome #Recordset1.UserID#.</p>
<cfelse>
<p>Welcome new user!</p>
</cfif>
</cfoutput>
<!--- OR since we used queryNew("userID"), we can simplify without throwing an error. ---->
<cfoutput>
<p>Welcome <cfif len(Recordset1.userID)>#Recordset1.userID#.<cfelse>new user!</cfif></p>
</cfoutput>
Putting the cfoutput outside the paragraph block will make it easier if you have additional variables to insert into the text. (but will work either way)
Regardless of all that, unless you forgot to share a bit more of the code, I think the issue is that the session.IDUsers is blank and needs to be populated before the query. I hope this helps!