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

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.

Related

When I enter a name in a form and that name already exists in a database, I want to give a message saying "This name already exists" using queries

I'm a beginner so I Want to know how you can check if a name already exist in the database and give an alert or a message saying the name already exist in the database
but i don't want to use jquery or js just cf and the queries.
<cfif isDefined("Button")>
<cfquery name='Insert' datasource='mysql'>
INSERT INTO tbl_products_manager
(Name)
VALUES ('#name#')
</cfquery>
<cfinclude template="pr.cfm">
</cfif>
I want to know what i should write in the next cfquery or to add the message
You can do:
<cfquery name='Insert' datasource='mysql' result='local.stResult'>
INSERT INTO tbl_products_manager
(Name)
VALUES (<cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#name#" />)
WHERE NOT EXISTS (SELECT 1 FROM tbl_products_manager WHERE Name = <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#name#" />)
</cfquery>
Then you can dump local.stResult and figure out the result value that has the modified record count. (Can't remember offhand.) If the value is 0, you know it wasn't inserted.
As you indicate you are new to Coldfusion, please, please look up <cfqueryparam> and use it in every...single...query, every time.
The above is but one solution. You can also do a separate query first. Or you can have an identity (or MySQL equivalent) on the table and also get that in the stResult struct. There are a myriad of ways. Strenghten your sql-fu, and your CFML query/proc usage will get stronger, too!

Migrating coldfusion application to Lucee

Our server is changing from Coldfusion to a Lucee server and I'm tasked with updating our code for a couple of web applications. I'm not a guru of Coldfusion but I can often figure my way around things, to that end my query.
The code I'm converting over is throwing this error:
Can't cast Object type [DateTime] to a value of type [Array]
I have been working through all of the queries and making sure that the output is appropriately CAST which has resolved the majority of issues but the small block of code that is stumping me throws the above error. The code is:
<cfset summaryStartDate = ArrayMin( qSummaryData["minHours"] ) />
<cfset summaryMaxDate = ArrayMax( qSummaryData["maxHours"] ) />
<cfset summaryEndDate = DateAdd("d", -(DayofWeek(#summaryMaxDate#))+6, #summaryMaxDate# ) />
minHours and maxHours are both DATETIME format. I know in the coldfusion version they output like so:
summaryStartDate: 41204
summaryMaxDate: 43465
summaryEndDate: {ts '2019-01-04 00:00:00'}
Which, to me, means Coldfusion is doing a conversion in some way and Lucee doesn't do those (or at least from what I've read). The database is mySQL and the minHours and maxHours output as dates with 00:00:00 on the hours, for reference.
I'm probably missing something obvious but I can't see it.
I preface this answer with "it is not the greatest fix" but it does work. Taking my queue from andrewdixon I looked at side stepping the use of an array (which the data wasn't suited for) and looked at alternatives.
I settled on a query of queries, extracting the min value and then setting that in a cfset. Doing the same for max value following that. My two queries were:
<cfquery name="smallestFigure" dbtype="query">SELECT CAST(MIN(minHours) AS DATETIME) as outputMin FROM qSummaryData;</cfquery>
<cfquery name="largestFigure" dbtype="query">SELECT CAST(MAX(maxHours) AS DATETIME) as outputMax FROM qSummaryData;</cfquery>
I cfset these into summaryStartDate and summaryMaxDate so that this line (mentioned in the original post) could run:
<cfset summaryEndDate = DateAdd("d", -(DayofWeek(#summaryMaxDate#))+6, #summaryMaxDate# ) />
Shawn mentioned I didn't need the #'s around summaryMaxDate but I haven't made that change as yet. andrewdixon mentioned using query.reduce() as an alternative and I imagine that would be far more succinct than what I've done so if someone comes up with a better solution, please post as an answer.
Also thank you all for the support and ideas.

Coldfusion cfloop through checkboxes in a cfquery

I have had a good search through to see if there was something similar to what I am trying to do. Nothing specifically covers it though so without further ado.
I would like my cfloop to work through any of the checked boxes on a search page and display the appropriate results drawn from the database. This is what I have so far:
<cfquery name="joblibrary">
SELECT *
FROM tblJobLibraryRoles JOIN tblJobLibraryCategories
ON tblJobLibraryRoles.category = tblJobLibraryCategories.id
<cfloop list="#form.cbGrade#" index="i">
WHERE grade=<cfqueryparam cfsqltype="cf_sql_varchar" value="#i#"/>
</cfloop>
ORDER BY category, grade, title, heraRef;
</cfquery>
Now it works all fine if only one checkbox is ticked, so I am basically asking can I get this type of CFLOOP to work in the way I've created it, or am I barking up the wrong tree?
You don't need a loop. Just use the sql keyword "in".
where somefield in (
<cfqueryparam
cfsqltype="cf_sql_varchar" value="#form.checkboxfield#"
list="yes">
)
You just have to do something to contend with the situation where no boxes are checked.
IN () is certainly the way to handle this (works with other sql statements as well), but there may be times where you want multiple WHERE conditions that are all entirely defined by variables.
Again, IN () is the solution here, but I'll demonstrate with your query on how to do what you were originally wanting to do.
Simply add a WHERE 0=0 (or 1=1 or anystring=anysamestring. You could even say Where 'trickwhere'='trickwhere' but there's no reason to get wordy with it) and loop over the rest using an AND instead.
<cfquery name="joblibrary">
SELECT * from tblJobLibraryRoles JOIN tblJobLibraryCategories ON tblJobLibraryRoles.category=tblJobLibraryCategories.id
WHERE 0=0
<cfloop list="#form.cbGrade#" index="i">AND grade=<cfqueryparam cfsqltype="cf_sql_varchar" value="#i#"/></cfloop>
ORDER BY category, grade, title, heraRef;
</cfquery>

in a multi-language site, why would I store some text strings in a database and write other one into the markup?

I'm working on an existing multi-language site (Coldfusion/MySQL).
Why is it that on a lot of pages I'm sitting on, some text strings are always hard-coded into the markup like:
<CFIF language = "EN"><p>Hello World</p></CFIF>
while others use the database to update text like so:
<p><cfoutput>#tx_greetings#</cfoutput></p>
What is the best practice here? I thought if I'm going to use a database for translations, it would be easier to store all texts in there (long and small). If I'm not using a database, then all texts should be if-elsed. Mixing it is a little maintenance-heavy, isn't it?
Also, is there a limit on text-string-length, which I'm storing to MySQL? Maybe performance-wise?
Thanks for some inputs!
You shouldn't store strings/translations in your code, that's bad practice if you want a maintainable i18n'd site.
You should store all your string in the same location, db or a properties file per language. It doesn't matter which, but be consistent. Personally I prefer a properties file as its easy to edit.
welcome_message=Hi {0}, Welcome to my site
Load all your translations in one go in onApplicationStart(), then provide a wrapper class to access them and to format the string with supplied arguments
for example
#i18n.getString(user.getLocale(), "welcome_message", [user.getUsername()])#
You can use java.text.MessageFormat[1] to provide powerful formatting
function getString(string locale, string key, array args) {
var mf = createobject("java", "java.text.MessageFormat");
mf.init(variables.strings[arguments.locale][arguments.key]);
return mf.format(javacast("java.lang.Object[]", args));
}
The above is just an example, and you need to provide error catching and caching to this
Hope that helps point you in a productive direction
[1] http://docs.oracle.com/javase/7/docs/api/index.html?java/text/MessageFormat.html
You can use a DB or a ascii file, depends on which you prefer.
If u use a DB you can create a table with the following columns:
country_code : country code for the language (i.e. US for english)
definition_name : name of the definition or message (*i.e. db_error_msg for a generic error message for db action*)
definition value : value of the definition (i.e. Sorry, an error occurred saving your data)
Each record will be a definition.
Depending on the language the user select your app will filter the database and you will get a query of all definitions you need.
I usually use that query to set a session variable structure like:
<cfif IsDefined("session.language") IS FALSE>
<cfquery name="getDefinition" datasource="dsn">
SELECT * FROM tbl_definitions WHERE country_code = "US"
</cfquery>
<cfset session.language = structnew()>
<cfoutput query="getDefinitions">
<cfset session.language["#definition_name#"] = "#definition_value#">
</cfoutput>
</cfif>
In the code I will simply use:
<cfoutput>
<h2>#session.language.db_error_msg#</h2>
</cfoutput>
and I will get the right message for the current language.
You can also use a master definition db to be used by different websites.
Same solution can be used with different configuration files (ie US.cfg. EN.cfg, ES.cfg) where you set your definitions in simple way to get a list.
I usually use the following system:
definition_name = definition_value for each line
db_error_msg = Sorry, an error occured saving your data
db_success_msg = Record saved
Then I read the current language configuration file (i.e. US.cfg for english, ES.cfg for spanish) and get the same result
<cfif IsDefined("session.language") IS FALSE>
<cffile action="read" file="#path#\US.cfg" variable="definitions">
<cfset session.language = structnew()>
<cfloop index="i" list="#definitions#" delimiters="#chr(10)#">
<cfset definition_name = ListGetAt(i,1,"=")>
<cfset definition_value = ListGetAt(i,2,"=")>
<cfoutput>
<cfset session.language["#definition_name#"] = "#definition_value#">
</cfoutput>
<cfloop>
</cfif>
This can be done just when session starts (if you know the language you need) and in both ways your definitions will be available everywhere inside your application for the user session time duration you have defined.
You can use definitions for buttons, messages, table headers, etc. creating a multilanguage ui in a very fast way without creating localized templates or using inline translations.
I hope this will help you.
Just as some generic localization advice, be careful about the variable name you'll use to know which phrase to retrieve. Don't just make it the english phrase, but make it something that is clearly a specific variable because you'll also have to handle contextual phrases that seem the same in English, but are very different in other languages depending on context.

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.