ColdFusion 2016 upgrade giving stored procedure error - sql-server-2008

We are in the process of upgrading to ColdFusion 2016 to work with SqlServer 2017. Our current version (ColdFusion 10/SqlServer 2008) is working fine.
We are testing our code in a development environment to find any issues with it and I keep coming across the same error that is a little cryptic. I'm not sure if it's the combination of ColdFusion 2016 and SqlServer2008 or just an upgrade from CF10 to CF2016. It involves CFStoredProc commands.
The code is:
<CFSTOREDPROC DATASOURCE="MedScribeSQL" PROCEDURE="dbo.usp_ChartRestrictionCheck">
<CFPROCPARAM CFSQLTYPE="CF_SQL_VARCHAR" DBVARNAME="Org" TYPE="In" VALUE="#Cookie.Org#">
<CFPROCPARAM CFSQLTYPE="CF_SQL_VARCHAR" DBVARNAME="Chartnum" TYPE="In" VALUE="#TRIM(Variables.PTChartnum)#">
<CFPROCPARAM CFSQLTYPE="CF_SQL_VARCHAR" DBVARNAME="Username" TYPE="In" VALUE="#Cookie.Username#">
<CFPROCPARAM CFSQLTYPE="CF_SQL_VARCHAR" DBVARNAME="ReturnChart" TYPE="Out" VARIABLE="RestrictionReturnChart">
<CFPROCPARAM CFSQLTYPE="CF_SQL_DATE" DBVARNAME="ReturnExpiryDate" TYPE="Out" VARIABLE="RestrictionReturnExpiryDate">
</CFSTOREDPROC>
When the page runs I am getting the following error:
Error Executing Database Query.
[Macromedia][SQLServer JDBC Driver][SQLServer]Incorrect syntax near '='.
The error occurred in D:/Websites/AcumenEHR/health/CustomTags/checkMRAccess.cfm: line 78
Called from D:/Websites/AcumenEHR/health/HCNHeader.cfm: line 127
Called from D:/Websites/AcumenEHR/health/HCNHeader.cfm: line 1
Called from D:/Websites/AcumenEHR/health/records/Visit_summary/hpsummary2.cfm: line 23
Called from D:/Websites/AcumenEHR/health/CustomTags/checkMRAccess.cfm: line 78
Called from D:/Websites/AcumenEHR/health/HCNHeader.cfm: line 127
Called from D:/Websites/AcumenEHR/health/HCNHeader.cfm: line 1
Called from D:/Websites/AcumenEHR/health/records/Visit_summary/hpsummary2.cfm: line 23
76 : <CFPROCPARAM CFSQLTYPE="CF_SQL_VARCHAR" DBVARNAME="Username" TYPE="In" VALUE="#Cookie.Username#">
77 : <CFPROCPARAM CFSQLTYPE="CF_SQL_VARCHAR" DBVARNAME="ReturnChart" TYPE="Out" VARIABLE="RestrictionReturnChart">
78 : <CFPROCPARAM CFSQLTYPE="CF_SQL_DATE" DBVARNAME="ReturnExpiryDate" TYPE="Out" VARIABLE="RestrictionReturnExpiryDate">
79 : </CFSTOREDPROC>
80 :
I can't find any information in the documentation about why this would be throwing an error. I'm wondering if anyone has had a similar experience with this and can point me in the direction of a fix since this same error occurs in a number of places.
Thanks

There were some changes made in CF11 on how the DBVARNAME attributes are handled. In CF10 they were essentially ignored, and since CF11 update 3 that has been 'fixed'.
You will need to make sure the values in the DBVARNAME attributes match the names of the parameters in the stored procedures exactly, in this case with SQL Server that likely means that you will need to prefix them with an #.
Here's a blog post outlining this change:
https://coldfusion.adobe.com/2015/07/coldfusion-11-and-dbvarname-attribute/

Related

Is there a better approach than using stored procedure?

Please consider the following code which I am using to get the data posted by Sendgrid.
<cftry>
<cfset incomingData = toString(getHttpRequestData().content) />
<cfset djs = DeserializeJSON(incomingData)/>
<cfset a = "0">
<cfset b = "">
<cfset c = "0">
<cfset d = "0">
<cfset e = "">
<cfset f = "">
<cfset g = "">
<cfset h = "">
<cfset i = "">
<cfset k = "#NOW()#">
<cfset l = "">
<cfset m = "">
<cfset n = "">
<cfoutput>
<cfloop from="1" to="#arraylen(djs)#" index="i">
<cfset a = "0">
<cfset b = "">
<cfset c = "0">
<cfset d = "0">
<cfset e = "">
<cfset f = "">
<cfset g = "">
<cfset h = "">
<cfset i = "">
<cfset k = "#NOW()#">
<cfset l = "">
<cfset m = "">
<cfset n = "">
<cfif StructKeyExists(djs[i],'p')>
<cfset a = djs[i].p />
</cfif>
<cfif StructKeyExists(djs[i],'q')>
<cfset b = djs[i].q />
</cfif>
<cfif StructKeyExists(djs[i],'r')>
<cfset c = djs[i].r />
</cfif>
<cfif StructKeyExists(djs[i],'s')>
<cfset d = djs[i].s />
</cfif>
<cfif StructKeyExists(djs[i],'t')>
<cfset e = djs[i].t />
</cfif>
<cfif StructKeyExists(djs[i],'u')>
<cfset f = djs[i].u />
</cfif>
<cfif StructKeyExists(djs[i],'v')>
<cfset g = djs[i].v />
</cfif>
<cfif StructKeyExists(djs[i],'w')>
{
<cfset i = djs[i].w />
<cfset k = dateAdd("s", i, createDateTime(1970, 1, 1, 0, 0, 0))/>
}
</cfif>
<cfif StructKeyExists(djs[i],'x')>
<cfset l = djs[i].x />
</cfif>
<cfif StructKeyExists(djs[i],'y')>
<cfset m = djs[i].y />
</cfif>
<cfif StructKeyExists(djs[i],'z')>
<cfset n = djs[i].z />
</cfif>
<cfstoredproc procedure="sp1" datasource="db1">
<cfprocparam cfsqltype="cf_sql_bigint" value="#a#">
<cfprocparam cfsqltype="cf_sql_varchar" value="#left(b,199)#">
<cfprocparam cfsqltype="cf_sql_integer" value="#c#">
<cfprocparam cfsqltype="cf_sql_integer" value="#d#">
<cfprocparam cfsqltype="cf_sql_varchar" value="#left(e,199)#">
<cfprocparam cfsqltype="cf_sql_varchar" value="#left(f,199)#">
<cfprocparam cfsqltype="cf_sql_varchar" value="#left(g,499)#">
<cfprocparam cfsqltype="cf_sql_varchar" value="#left(h,199)#">
<cfprocparam cfsqltype="cf_sql_timestamp" value="#k#">
<cfprocparam cfsqltype="cf_sql_varchar" value="#left(l,199)#">
<cfprocparam cfsqltype="cf_sql_varchar" value="#LEFT(m,499)#">
<cfprocparam cfsqltype="cf_sql_varchar" value="#left(n,99)#">
<cfprocparam cfsqltype="cf_sql_varchar" value="XX.XX.X.XX">
</cfstoredproc>
<cfstoredproc procedure="sp2" datasource="db2">
<cfprocparam cfsqltype="cf_sql_bigint" value="#a#">
<cfprocparam cfsqltype="cf_sql_varchar" value="#left(b,199)#">
<cfprocparam cfsqltype="cf_sql_integer" value="#c#">
<cfprocparam cfsqltype="cf_sql_integer" value="#d#">
<cfprocparam cfsqltype="cf_sql_varchar" value="#left(e,199)#">
<cfprocparam cfsqltype="cf_sql_varchar" value="#left(f,199)#">
<cfprocparam cfsqltype="cf_sql_varchar" value="#left(g,499)#">
<cfprocparam cfsqltype="cf_sql_varchar" value="#left(h,199)#">
<cfprocparam cfsqltype="cf_sql_timestamp" value="#k#">
<cfprocparam cfsqltype="cf_sql_varchar" value="#left(l,199)#">
<cfprocparam cfsqltype="cf_sql_varchar" value="#LEFT(m,499)#">
<cfprocparam cfsqltype="cf_sql_varchar" value="#left(n,99)#">
<cfprocparam cfsqltype="cf_sql_varchar" value="XX.XX.X.XX">
</cfstoredproc>
</cfloop>
</cfoutput>
</cftry>
As clear from the above, my code is dependent on the stored procedure on the MySQL Database. I am thinking of getting rid of stored procedure and find a different way where I can accomplish what I am looking for without any stored procedure.Is there a better way other than using Stored Procedure to store the incoming data into database?
Is there a better way other than using Stored Procedure to store the incoming data into database? No, not really. A better question is why you feel the need to stop using the stored procedures?
You really only have three options when interacting with a database; stored procedures, in-line queries (parameterized), or Object Relational Mapping (ORM). Sure you could replace your stored procedure calls with in-line queries or ORM but I don't think you will really gain anything.
Some of the benefits of using stored procedures rather than in-line queries are:
Reduced server/client network traffic
The commands in a procedure are executed as a single batch of code. This can significantly reduce network traffic between the server and client because only the call to execute the procedure is sent across the network. Without the code encapsulation provided by a procedure, every individual line of code would have to cross the network.
Stronger security
Multiple users and client programs can perform operations on underlying database objects through a procedure, even if the users and programs do not have direct permissions on those underlying objects. The procedure controls what processes and activities are performed and protects the underlying database objects. This eliminates the requirement to grant permissions at the individual object level and simplifies the security layers.
The EXECUTE AS clause can be specified in the CREATE PROCEDURE statement to enable impersonating another user, or enable users or applications to perform certain database activities without needing direct permissions on the underlying objects and commands. For example, some actions such as TRUNCATE TABLE, do not have grantable permissions. To execute TRUNCATE TABLE, the user must have ALTER permissions on the specified table. Granting a user ALTER permissions on a table may not be ideal because the user will effectively have permissions well beyond the ability to truncate a table. By incorporating the TRUNCATE TABLE statement in a module and specifying that module execute as a user who has permissions to modify the table, you can extend the permissions to truncate the table to the user that you grant EXECUTE permissions on the module.
When calling a procedure over the network, only the call to execute the procedure is visible. Therefore, malicious users cannot see table and database object names, embed Transact-SQL statements of their own, or search for critical data.
Using procedure parameters helps guard against SQL injection attacks. Since parameter input is treated as a literal value and not as executable code, it is more difficult for an attacker to insert a command into the Transact-SQL statement(s) inside the procedure and compromise security.
Procedures can be encrypted, helping to obfuscate the source code. For more information, see SQL Server Encryption.
Reuse of code
The code for any repetitious database operation is the perfect candidate for encapsulation in procedures. This eliminates needless rewrites of the same code, decreases code inconsistency, and allows the code to be accessed and executed by any user or application possessing the necessary permissions.
Easier maintenance
When client applications call procedures and keep database operations in the data tier, only the procedures must be updated for any changes in the underlying database. The application tier remains separate and does not have to know how about any changes to database layouts, relationships, or processes.
Improved performance
By default, a procedure compiles the first time it is executed and creates an execution plan that is reused for subsequent executions. Since the query processor does not have to create a new plan, it typically takes less time to process the procedure.
If there has been significant change to the tables or data referenced by the procedure, the precompiled plan may actually cause the procedure to perform slower. In this case, recompiling the procedure and forcing a new execution plan can improve performance.
Some of this is specific to SQL Server but most applies to any database
Reference - Stored Procedures (Database Engine)
It depends what that stored proc does. Does it only insert one row of data?
I suggest that you use the loop to put your data into an array or struct then make ONE database call (either via cfquery or cfstoredproc) to insert the data.

Can I call a stored procedure in a cfloop and output dynamic out-parameters in Coldfusion?

Last question for tonight, still using Coldfusion8 and MySQL.
I have a table with products, each with Price A, B and C. I need to retrieve the min and max values for A,B,C across all prices (A_min, A_max, B_min, B_max, C_min, C_max)
I thought I would create a stored procedure and loop through A,B,C like so:
<cfloop list="A,B,C" index="what" delimiters=",">
<cfstoredproc procedure="proc_search_select_minmax" datasource="dtb">
<cfprocparam type="in" value="#what#" cfsqltype="cf_sql_varchar" maxlength="15">
<cfprocparam type="in" value="#variables.xxx#" cfsqltype="cf_sql_varchar" maxlength="13">
<cfprocparam type="in" value="#variables.yyy#" cfsqltype="cf_sql_varchar" maxlength="13">
<cfprocparam type="in" value="#variables.zzz#" cfsqltype="cf_sql_text" maxlength="4">
<cfprocparam type="out" cfsqltype="cf_sql_decimal" variable="#what#_min">
<cfprocparam type="out" cfsqltype="cf_sql_decimal" variable="#what#_max">
</cfstoredproc>
</cfloop>
So the idea was to run this three times for A,B and C and get variables A_min, A_max, B_min... out of the loop.
But I'm having trouble with my out-parameters, which inside MySQL, I'm declaring like:
CREATE ... PROCEDURE `proc_search_select_minmax`(..., OUT `outputMin` DECIMAL(12,2), OUT `outputMax` DECIMAL(12,2))
....
SET outputMin = min(what);
SET outputMax = max(what);
Coldfusion error says:
Error Executing Database Query
#
<cfprocparam type="out" cfsqltype="cf_sql_decimal" variable="#what#_min">
<cfprocparam type="out" cfsqltype="cf_sql_decimal" variable="#what#_max">
Questions:
Do I have to give my out parameters the same name as inside MySQL or is the correct order enough?
More importantly, can I set output variables dynamically like this? If not, are there any other ways except calling the stored procedure three separate times?
I never liked the variable return way of doing this. Useful but often difficult (depends on order etc).
I have 2 suggestions for you.
First, make the output a data set. In your stored procedure create a temp table (#myMinMax or whatever) with 2 columns minimum and maximum - populate the table with an insert and then select it out returning it as a <cfstoredprocresult..>
Secondly I would probably create a stored proc that does the looping and returns a whole dataset with a "type" column ... so you would end up with a dataset having type (as in A) minimum (as in 10) and maximum (as in 100) ... one row for A, one for B and one for C. A single connection to the datasource could return this dataset for you - avoiding 3 (or more) DB calls.
In SQL Server, we set our variables with the # sign in the dbvarname attribute.
<cfprocparam cfsqltype="cf_sql_integer"
value="#LOCAL.User_ID#"
dbvarname="#User_ID">
Give that a try.
UPDATE ~ I just checked the CF docs, the advice above won't help you
http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=Tags_p-q_14.html
Changed the dbvarname attribute behavior: it is now ignored for all drivers. ColdFusion uses JDBC 2.2 and does not support named parameters.
I agree with Mark about output parameters. For a single value, you could go either way. But beyond that it is simpler to just return a resultset.
Error Executing Database Query
That said - your code works fine with the sample procedure below in CF8/MySQL5. Based on the partial error message, I suspect the real problem is a syntax error in your procedure. Did you test it directly in your database?
<cfloop>
<cfstoredproc ...>
....
</cfstoredproc>
</cfloop>
<cfoutput>
#a_min# #a_max# <br />
#b_min# #b_max# <br />
#c_min# #c_max# <br />
</cfoutput>
CREATE PROCEDURE `test`.`proc_search_select_minmax` (
what varchar(50)
, xxx varchar(50)
, yyy varchar(50)
, zzz varchar(50)
, OUT outputMin decimal(12,2)
, OUT outputMax decimal(12,2)
)
BEGIN
SET outputMin = 1;
SET outputMax = 20;
END

Coldfusion 8 Multi Thread Concurrency

I had a problem with Coldfusion 8 that I posted on Stack Overflow not too long ago Coldfusion 8 doing both CFIf and the CFElse statement
that I thought I had narrowed down to a mysql problem, but with (much) further investigation, I have narrowed it down to a Multi threading / Multiple requests per session issue. (That may still be a MYSQL problem, but I have no idea how to fix it)
What is really going wrong with the code :
<cfif isValid("email", form.email)>
<cfquery name="check_user" datasource="#request.dsn#">
SELECT var_username, var_password
FROM tbl_users
WHERE var_username = <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#FORM.EMAIL#">
AND var_password = <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#FORM.PASSWORD#">
</cfquery>
<cfif check_user.recordcount EQ 0>
<cfquery datasource="#request.dsn#" name="insertuser">
INSERT INTO tbl_users (var_username, var_password) VALUES
(<cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#FORM.EMAIL#">, <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#FORM.PASSWORD#">)
</cfquery>
<cflogin idletimeout="1800">
<cfloginuser
name = "#FORM.email#"
password ="#FORM.password#"
roles = "0">
</cflogin>
<cflocation addtoken="No" url="#request.secure_url#checkout.cfm">
<cfelse>
<cfset client.error_message = "Your Email Address is already registered.">
<cflocation addtoken="No" url="#request.site_url#New-Account.html">
</cfif>
<cfelse>
<cfset client.error_message = "Your Email Address is not Valid.">
<cflocation addtoken="No" url="#request.site_url#New-Account.html">
</cfif>
is that there are 2 concurrent requests being made by the same user. If the requests are slightly staggered, Request A will insert the user into the database, but before Request A can CFLOGIN and CFLOCATION, Request B gets to the CFIF Notices that there is already a user in the database, and creats the CLIENT.ERROR_MESSAGE and CFLOCATION's to New-Account.html .
However, if the requests are not staggered, what happens is that the code appears to work, and sends the user to the checkout.cfm page, however in the database, the user is inserted twice.
The steps I have taken to try and resolve this:
1: Using different Databases within the same MYSQL Server (by changing the datasource to one of our other sites that have a similar/identical tbl_users). Same results.
2: Putting the website on a different coldfusion 8/windows 2003 server (but used the same MYSQL Server). Same results.
3: Put a
<cflock name="NewUser" timeout="30" type="EXCLUSIVE">
at the beginning of the code and a
</cflock>
at the end of the code. Same Results.
I really thought that putting a CFLOCK on the code would fix the issue, but it didn't, and now I have no idea what to do next (but it could be because I have never used CFLOCK before, and am using it wrong). Does anyone have any Ideas how to fix this issue so that only one request is sent? Or any ideas why 2 requests are being sent? (I don't think its pebkac, because I am the one doing the testing, and I am not hitting the submit button twice)
Also, I am using a windows 2003 web server, with coldfusion 8. And a seperate windows 2003 server with MYSQL 5.
Sorry my rank isn't high enough to just post a comment below your question...
I'm wondering why you are getting two or more concurrent requests for a user for that section of code. Are you accessing other CFM files using AJAX or the ColdFusion AJAX functions?
I think that your solution will be to track down why one user will hit that section of code multiple times.
You could use <cflog> to track how this section of code is called.
You're building a concurrent application. Be happy you found this problem so early in your development cycle. Concurrent applications work best when they use DBMS constraints to manage their concurrency rather than the kind of code you've shown in your example.
I suggest you switch to InnoDB if you aren't using it already, then set up your tbl_users.var_username column as a primary key with a unique constraint in the database.
Then, don't SELECT from your table. Just do the INSERT. You'll get an exception from the <cfquery> insert operation if the username is already there. Handle it appropriately. If the INSERT works correctly, you just added a new user and all is well. If it fails, you tried to add a duplicate user, and you can present an appropriate response on your web user interface.

migrating ms access to mysql -- changing CF coding (from newbie)

I am migrating my coldfusion9 query code from ms access 2003 to mySql. My MySql knowledge is limited, so this is a beginner's question :)
In my MS Access code, I used simple cfqueries... here's an example:
<cfquery name="catalog" datasource="mydatasource">
SELECT TableID, DateListed, FirstColor, SecondColor
FROM mytable
WHERE FirstColor='blue' OR SecondColor='blue'
ORDER BY DateListed DESC
</cfquery>
I understand from online reading that one needs to use cfqueryparam with mySql to protect from injected malicious code. I'm not sure how the malicious code is injected .... as online website users don't interact with my database via forms, will I still need to use cfqueryparam?
If so, could you give me an example of an way to add cfqueryparam to the above code? -or- suggest a good, simple how-to resource for writing mySql code (in my searches online most of the coding info presupposes a higher level of knowledge than I have)
In addition to protecting from SQL injection, CFQUERYPARAM lets you use bind variables, which can give you performance gains because queries that are the same except for some variables only need to be compiled once and can be cached.
From your sample, this is how you'd used cfqueryparam:
<cfquery name="catalog" datasource="mydatasource">
SELECT TableID, DateListed, FirstColor, SecondColor
FROM mytable
WHERE FirstColor=<cfqueryparam cfsqltype="cf_sql_varchar" value="blue">
OR SecondColor=<cfqueryparam cfsqltype="cf_sql_varchar" value="blue">
ORDER BY DateListed DESC
</cfquery>
That doesn't buy you much, however. The payoff is in situations like this:
<cfquery name="catalog" datasource="mydatasource">
SELECT TableID, DateListed, FirstColor, SecondColor
FROM mytable
WHERE FirstColor=<cfqueryparam cfsqltype="cf_sql_varchar" value="#form.color#">
OR SecondColor=<cfqueryparam cfsqltype="cf_sql_varchar" value="#form.color#">
ORDER BY DateListed DESC
</cfquery>
Now whether the color is blue or red or chartreuse this query is compiled in the database and will be faster on subsequent calls. Further, a malicious user could change form.color to have the value of blue';drop table users; and you'll be protected from the SQL injection.
There are at least three good reasons to use cfqueryparam
1) Security from injection or other bad intent from users.
2) Queries execute faster because you explicitly state their type.
3) Its just a good idea, plus similar to other language features, like
$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);
We used examples like this all over of DAO objects (these are the cfcs that have the code in them that speak to the database)
<cfqueryparam cfsqltype="CF_SQL_INTEGER" value=#arguments.assessmentID# />
<cfqueryparam cfsqltype="CF_SQL_CHAR" value='%#arguments.searchString#%' />
<cfqueryparam cfsqltype="CF_SQL_DATE" value=#arguments.assessmentDate# />
Where the #arguments.varname# was passed into the method containing the SQL.

Individual Parameters or Build Where Clause

I'm building an object to search orders in my database. There are a bunch of possible parameters that the user can set, and they can set as many as then want for each search. I've created setter methods to collect all the parameters needed for the search.
My question is this. What would be "best practice"
Storing the parameters, and building the WHERE clause when the doSearch method is called
Building the WHERE clause as parameters are set
I'd like to understand the reason behind any recommendation.
Note that the object is instatiated for each search, so I don't have to worry about a second search with different parameters.
You should separate the code for your order search from the code that builds the SQL. The SQL should be built in a derivative (or Strategy derivative) of the OrderSearch class. Once you have made this separation, it doesn't really matter when you build the SQL.
To make this a bit more plain. Given a class named OrderSearch which has a bunch of setter methods for the search criteria, you'd like to have a subclass named OrderSearchSQLBuilder. Notice that the subclass depends on the base class, and that the base class is independent of the subclass. This is very important. This independence allows you to disregard whether the SQL is built in the setter methods, or in the search method. See The Dependency Inversion Principle (DIP).
Once you have this kind of seperation, you can replace the derivative with other strategies. For example, if you wanted to test your application without connecting it to the SQL database, you could create a dummy in-ram database and create a derivative of OrderSearch that dealt with that dummy database. The rest of the application would be blissfully unaware, and your tests would then be independent of the horrors of database connections, pre-existing data, etc.
In your method, simply use the parameters in your dynamic SQL that does the search. That way the where clause is built just prior to the SQL getting run. You will simply pass the search parameters into your method as arguments.
Something like this...
<cffunction name="getByAttributesQuery" access="public" output="false" returntype="query">
<cfargument name="id" type="numeric" required="false" />
<cfargument name="userName" type="string" required="false" />
<cfargument name="firstName" type="string" required="false" />
<cfargument name="lastName" type="string" required="false" />
<cfargument name="createdAt" type="date" required="false" />
<cfargument name="updatedAt" type="date" required="false" />
<cfargument name="orderby" type="string" required="false" />
<cfset var qList = "" />
<cfquery name="qList" datasource="#variables.dsn#">
SELECT
id,
userName,
firstName,
lastName,
createdAt,
updatedAt
FROM users
WHERE 0=0
<cfif structKeyExists(arguments,"id") and len(arguments.id)>
AND id = <cfqueryparam value="#arguments.id#" CFSQLType="cf_sql_integer" />
</cfif>
<cfif structKeyExists(arguments,"userName") and len(arguments.userName)>
AND userName = <cfqueryparam value="#arguments.userName#" CFSQLType="cf_sql_varchar" />
</cfif>
<cfif structKeyExists(arguments,"firstName") and len(arguments.firstName)>
AND firstName = <cfqueryparam value="#arguments.firstName#" CFSQLType="cf_sql_varchar" />
</cfif>
<cfif structKeyExists(arguments,"lastName") and len(arguments.lastName)>
AND lastName = <cfqueryparam value="#arguments.lastName#" CFSQLType="cf_sql_varchar" />
</cfif>
<cfif structKeyExists(arguments,"createdAt") and len(arguments.createdAt)>
AND createdAt = <cfqueryparam value="#arguments.createdAt#" CFSQLType="cf_sql_timestamp" />
</cfif>
<cfif structKeyExists(arguments,"updatedAt") and len(arguments.updatedAt)>
AND updatedAt = <cfqueryparam value="#arguments.updatedAt#" CFSQLType="cf_sql_timestamp" />
</cfif>
<cfif structKeyExists(arguments, "orderby") and len(arguments.orderBy)>
ORDER BY #arguments.orderby#
</cfif>
</cfquery>
<cfreturn qList />
</cffunction>
Don't build the where clause before it is needed to execute the search. You may end up with a user interface that feeds parameters in iterations, and you don't know when you have everything. Also, you may never execute the search, so why worry about the where clause.
I don't think it makes much difference, but I think it seems better practice to build the WHERE clause when you doSearch. I don't think it should be the responsibility of a setter for a parameter to add to a WHERE clause string somewhere.
On an SQLServer database it is more efficient to include all parameters in the where clause rather than building it on the fly. Then have an database index which includes all the columns you will search on. This ensures the index is always used when executing the statement.
Dosn't quite sound like your abstraction is quite right.
Search may be better as a method of a "orders" object. pass in the parameters to the search function and build the query manually as russ suggested. Anything which is specific to orders rather than the search can then be set on the orders init method.
It is possible that you may want to build an orders search object but this should be done via an orders object, to keep your front end code simple.
Option 1 is your best bet. Option 2 sounds dangerous. What if a parameter is updated? How do you replace it in your WHERE clause?
I would do something like this:
<cffunction name="doSearch" access="public" output="false" returntype="query">
<cfset var qList = "" />
<cfquery name="qList" datasource="#variables.dsn#">
SELECT
...
FROM
...
WHERE 0=0
<cfif len(getID())>
AND id = <cfqueryparam value="#getID()#" CFSQLType="cf_sql_integer" />
</cfif>
<cfif len(getUserName())>
AND userName = <cfqueryparam value="#getUserName()#" CFSQLType="cf_sql_varchar" />
</cfif>
</cfquery>
<cfreturn qList />
</cffunction>