Unknown column in field list - mysql

I'm trying to insert some information to MySQL with Pascal, but when I run the program I get the error
unknown column 'mohsen' in field list
This is my code
procedure TForm1.Button1Click(Sender: TObject);
var
aSQLText: string;
aSQLCommand: string;
namee:string;
family:string;
begin
namee:='mohsen';
family:='dolatshah';
aSQLText:= 'INSERT INTO b_tbl(Name,Family) VALUES (%s,%s)';
aSQLCommand := Format(aSQLText, [namee, family]);
SQLConnector1.ExecuteDirect(aSQLCommand);
SQLTransaction1.Commit;
end;
How can I solve this problem?

It's because your
VALUES (%s,%s)
isn't surrounding the namee and family variable contents by quotes. Therefore, your back-end Sql engine thinks your mohsen is a column name, not a value.
Instead, use, e.g.
VALUES (''%s'',''%s'')
as in
Namee := 'mohsen';
Family := 'dolatshah';
aSQLText:= 'INSERT INTO b_tbl(Name,Family) VALUES (''%s'',''%s'')';
aSQLCommand := Format(aSQLText,[namee,family]);
In the original version of my answer, I explained how to fix your problem by "doubling up" single quotes in the Sql you were trying to build, because it seemed to me that you were having difficulty seeing (literally) what was wrong with what you were doing.
An alternative (and better) way to avoid your problem (and the one I always use in real life) is to use the QuotedStr() function. The same code would then become
aSQLText := 'INSERT INTO b_tbl (Name, Family) VALUES (%s, %s)';
aSQLCommand := Format(aSQLText, [QuotedStr(namee), QuotedStr(family)]);
According to the Online Help:
Use QuotedStr to convert the string S to a quoted string. A single quote character (') >is inserted at the beginning and end of S, and each single quote character in the string is >repeated.
What it means by "repeated" is what I've referred to as "doubling up". Why that's important, and the main reason I use QuotedStr is to avoid the Sql db-engine throwing an error when the value you want to send contains a single quote character as in O'Reilly.
Try adding a row containing that name to your table using MySql Workbench and you'll see what I mean.
So, not only does using QuotedStr make constructing SQL statements as strings in Delphi code less error-prone, but it also avoid problems at the back-end, too.

Just in case this will help anybody else I had the same error when I was parsing a python variable with a sql statement and it had an if statement in i.e.
sql="select bob,steve, if(steve>50,'y','n') from table;"
try as I might it coming up with this "unknown column y" - so I tried everything and then I was about to get rid of it and give it up as a bad job until I thought I would swap the " for ' and ' for "..... Hoooraaahh it works!
This is the statement that worked
sql='select bob,steve, if(steve>50,"y","n") from table;'
Hope it helps...

To avoid this sort of problem and SQL injection you should really look into using SQL parameters for this, not the Pascal format statement.

Related

cfm websql queries error

I have this websql script (http://pastebin.com/gvJseBAn) which doesn't perform correctly.
If I run the statement select * from news where id=0772348890 , I get the error The conversion of the varchar value ' 0017707787068' overflowed an int column.
If I run the statement select * from news where id='0772348890' , I get the error Incorrect syntax near '0772348890'.
If I run the statement select * from news where id="0772348890" , I get Invalid column name '0772348890'
Any other variation of '#0772348890#' or #0772348890# or "#0772348890#" I have tried gives the error "incorrect column" or "incorrect syntax near ..."
Any ideas on how to fix this error, or a better method of creating a simple websql query form?
A) the issue here is that db column will not under any conditions accept "0772348890" as a valid input because it is mismatched. The column is an "int" type (according to your first error), but your value has a padded 0 prependedto the front as in 0 772...
What is the purpose of this zero? Ordinarily prepended zeros appear in fixed length character fields where a space is not allowed. Should the value not be "772348890"?
B) Remember that ColdFusion will escape your single quotes in your query. In your second error example (where you use single quotes), this code:
<cfquery name="runsql" datasource="#Form.datasource#" timeout="30">
#Form.sql#
</cfquery>
Produces this SQL statement:
select * from news where id=''0772348890''
Which would give you your syntax error. If you wish to successfully test your second example you will need to alter your code to:
<cfquery name="runsql" datasource="#Form.datasource#" timeout="30">
#preservesinglequotes(Form.sql)#
</cfquery>
Preservesinglequotes() gets you past the second error issue and MSSQL's implicit conversion may strip off the prepended zero and allow the query to succeed - though I'm not sure will give you what you want.
C) Finally you should probably never do what you are trying to do - at least not in this fashion (sorry to be so direct!). Your opening up your DB to arbitrary queries from a web form. The resulting damage from even casual mistakes could be catastrophic to your data, let alone a malicious user bent on stealing or altering or using your site for malicious purposes. That's my take. :)

Filter Dataset using SQL Query

I am using Zeos and mysql in my delphi project.
what I would like to do is filter dataset using a textbox.
to do that, I am using following query in textbox 'OnChange' Event:
ZGrips.Active := false;
ZGrips.SQL.Clear;
ZGrips.SQL.Add('SELECT Part_Name, Description, OrderGerman, OrderEnglish FROM Part');
ZGrips.SQL.Add('WHERE Part_Name LIKE ' + '"%' + trim(txt_search.Text) + '%"');
ZGrips.Active := true;
after I run and type first character in textbox, I get empty dataset in my DBGrid,
so DBGrid is showing nothing, then If I type second character I get some result in DBGrid. and even more strange behavior: if I will use AS Clause in my SQL Query like:
Part_Name AS blablabla,
Description AS blablabla,
OrderGerman AS OG,
OrderEnglish AS OE
in that case DBGrid is showing only 2 columns: Part_Name and Description, I dont understand why it is ignoring 3rd and 4th columns.
thanks for any help in advance.
Always use parameters
Firstly you need to use parameters, otherwise your query will break or worse when the user enters the wrong characters in the search box.
See: How does the SQL injection from the "Bobby Tables" XKCD comic work?
Parameters also makes you query faster, because the database engine only have to decode the query once.
If you change a parameter the engine will know that the query itself has not changed and will not re-decode it.
Don't use clear and add
Just supply the SQL as text in one go, it's faster.
This is esp. true in a loop, outside the loop you will not notice the difference.
Your code should read something like:
procedure TForm1.SetupSearch; //run this only once.
var
SQL: string;
begin
ZGrips.Active:= false;
SQL:= 'SELECT Part_Name, Description, OrderGerman, OrderEnglish FROM Part' +
'WHERE Part_Name LIKE :searchtext'); //note no % here.
ZGrips.SQL.Text:= SQL; //don't use clear and don't use SQL.Add.
end;
//See: http://docwiki.embarcadero.com/Libraries/XE2/en/Vcl.StdCtrls.TEdit.OnChange
procedure TForm1.Edit1Change(Sender: TObject);
begin
if Edit1.Modified then begin
Timer1.Active:= true;
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Timer1.Active:= false;
if Edit1.Text <> ZGrips.Params[0].AsString then begin
ZGrips.Params[0].AsString:= Edit1.Text + '%'
ZGrips.Active:= true;
end;
end;
Use a timer
As per #MartinA's suggestion, use a timer and start the query only ever so often.
The wierd behaviour you're getting maybe because you're stopping and reactivating a new query before the old one has had time to finish.
The Params[index: integer] property is a bit faster than the ParamsByName property.
Although this does not really matter outside a loop.
Allow the database to use an index!
Using only a trailing wildcard % is faster than using a leading wildcard because the database can only use an index is there is a trailing wildcard.
If you want to use a leading wildcard, then consider storing the data in reverse order and use a trailing wildcard instead.
Full-text indexes are much better than like
Of course if you use both a leading and a trailing wild card then you have to use a full-text index.
In MySQL you'll than use the MATCH AGAINST syntax,
see: Differences between INDEX, PRIMARY, UNIQUE, FULLTEXT in MySQL?
and: Which SQL query is better, MATCH AGAINST or LIKE?
The lastest versions of MySQL support full-text indexes in InnoDB.
Remember to never use MyISAM, it's unreliable.

Sql inserting with delphi xe3 and mysql database

I am using Delphi Xe5 and ZeosLib to connect to a remote database on a web server.
I am using the following code to insert a record into a table. but everytime i insert and there is a (') in the name, i get an error.
The error says that my syntax is wrong,the (') in the variable messes with the sql statement.
How can i solve this problem.
Code:
Data.personel.Active:=false;
sqltext:=data.personel.SQL.Text;
data.personel.SQL.Text:='Insert Into personel (name,surname,id_number,gender,company_name,nature_of_business,position_at_company,type_of_post,renumeration,company_size,duties,benefits,document_id,date_created,date_record_added) ' +
'VALUES ('''+name1+''','''+surname+''','''+idnumber+''','''+gender+''','''+companyname+''','''+natureofbusiness+''','''+positionatcompany+''','''+typeofpost+''','''+renumeration+''','''+companysize+''','''+duties+''','''+benefits+''','''+DokID+''',+'''+FormatDateTime('yyyy-mm-dd',Date_Created)+''','''+FormatDateTime('yyyy-mm-dd',Date_added)+''')';
Data.personel.ExecSQL;
I know my method is not of the most neat, but i just need to solve the (') problem. Thank you for your assistance
For goodness sake, don't concatenate SQL. It leaves the door open for SQL injection, and it causes problems like the one you're experiencing now. Use parameterized SQL statements instead (see notes that follow):
data.personel.Active := False;
data.personel.SQL.Text := 'Insert Into personel'#13 +
'(name, surname, id_number, gender, company_name, nature_of_business,'#13 +
'position_at_company, type_of_post, renumeration, company_size,'#13 +
'duties, benefits, document_id, date_created, date_record_added)'#13 +
'values'#13 +
'(:name, :surname, :id_number, :gender, :company_name, :nature_of_business, '#13 +
':position_at_company, :type_of_post, :renumeration, :company_size,'#13 +
':duties, :benefits, :document_id, :date_created, :date_record_added)';
data.personel.ParamByName('name').AsString := name1;
data.personel.ParamByName('surname').AsString := surname;
data.personel.ParamByName('id_number').AsString := idnumber;
data.personel.ParamByName('gender').AsString := gender;
// repeat for remaining values
data.personel.ExecSQL;
Notes:
The #13 at the end of each portion of the SQL statement is a carriage return. It makes it so you don't have to worry about a space at the start or end of each line. The server will ignore them, as extra white space is meaningless in SQL statements. It's the same as hitting the enter key at the end of each line when you're testing your query in a database management tool.
I use the column name as the parameter name, preceeding it with the : that indicates it's a parameter. It makes it easy to tell which one goes with which - the :surname parameter goes with the surname column.
If you put the SQL statement into it's own query component, you can put all of the SQL in at designtime, instead of supplying it at runtime. This means that the server can cache the compiled statement in case you use it again in a short time, making your queries execute faster if you're using them in a loop. You just change the value assigned to the parameters in the loop, leaving the SQL.Text alone.
Because your app is only used internally and is not exposed to the web doesn't mean you should ignore the risks of SQL injection. All it takes is one disgruntled employee who decides to get even with you or your company and learns about the possibility - when they decide to type something you didn't intend into the right edit control and drop or change an important database or table, the damage is just as severe.

MySQL insert to bit(1) column via ODBC 5.2

I've searched and can't seem to find quite what I'm looking for.
I'm running a PL/SQL script in Oracle, and attempting to insert records into a table in MySQL via database link using MySQL ODBC 5.2 Unicode Driver.
The link works fine, I can do complex queries in Oracle using it, and do various inserts and updates on records there.
Where it fails is in trying to insert a record into a MySQL table that has a column of type bit(1).
It is basically a cursor for loop, with the insert statement looking something like:
INSERT INTO "app_user"#mobileapi (USERNAME, VERSION, ACCOUNT_EXPIRED, ACCOUNT_LOCKED, PASSWD, PASSWORD_EXPIRED)
VALUES (CU_rec.USERNAME, CU_rec.VERSION, CU_rec.ACCOUNT_EXPIRED, CU_rec.ACCOUNT_LOCKED, CU_rec.PASSWD, CU_rec.PASSWORD_EXPIRED)
Some of the target columns, like ACCOUNT_EXPIRED, ACCOUNT_LOCKED, etc. are the bit(1) columns in MySQL. Given that I can convert the data types in the cursor CU_rec to pretty much anything I want in Oracle, how can I get them inserted into the target? I've tried everything I can think of, and I just keep getting:
Error report:
ORA-28500: connection from ORACLE to a non-Oracle system returned this message:
[MySQL][ODBC 5.2(w) Driver][mysqld-5.6.10]Data too long for column 'ACCOUNT_EXPIRED' at row 1 {HY000,NativeErr = 1406}
ORA-02063: preceding 2 lines from MOBILEAPI
ORA-06512: at line 44
28500. 00000 - "connection from ORACLE to a non-Oracle system returned this message:"
*Cause: The cause is explained in the forwarded message.
*Action: See the non-Oracle system's documentation of the forwarded
message.
Any help at all would be greatly appreciated.
Your problem is Oracle's default datatype conversion over ODBC; according to their own documentation they convert SQL_BINARY to a raw. Although not directly related, Oracle's comparison of MySQL and Oracle within SQL Developer also alludes to the fact that the automatic conversion from a MySQL bit is to an Oracle raw.
Extremely confusingly, MySQL's documentation indicates that a bit is converted to a SQL_BIT or a SQL_CHAR, which implies that it may work in the other direction1.
According to Microsoft's ODBC docs you should, theoretically, be able to use the CONVERT() function to transform this into a character, which should, theoretically, be translatable by MySQL.
insert into some_table#some_db (bit_col)
values( {fn convert(some_col, SQL_CHAR)} );
Failing that there's another couple of options, but it does depend on what you're attempting to insert into the MySQL database from Oracle and what the datatype is in Oracle. For instance you could use the Oracle CAST() function to convert between datatypes. For instance, the following would convert an integer to a binary double.
select cast(1 as binary_double) from dual
Unfortunately, you can't cast an integer to a raw, only a character or a rowid, so in order to convert to a raw you'd have to do the following:
select cast(to_char(1) as raw(1)) from dual
I've no idea whether MySQL will accept this but with some testing you should be able to work it out.
1. For clarity, I've never tried it in either direction.
Hah! I found a solution. Dropping it here in case it helps someone else. It's not pretty, but it works.
I used the old EXECUTE IMMEDIATE trick.
Basically, I created a variable sql_stmt varchar2(4000) and wrote code like:
sql_stmt := 'insert into "app_user"#mobileapi (USERNAME, VERSION, ACCOUNT_EXPIRED, ACCOUNT_LOCKED, CIPHER_PASSPHRASE, ENABLED, PASSWD, PASSWORD_EXPIRED)
values ('''||CU_rec.USERNAME||'','||CU_rec.VERSION||', '||CU_rec.ACCOUNT_EXPIRED||', '||CU_rec.ACCOUNT_LOCKED||', '''||CU_rec.CIPHER_PASSPHRASE||''', '||
CU_rec.ENABLED||', '''||CU_rec.PASSWD||''', '||CU_rec.PASSWORD_EXPIRED||')';
EXECUTE IMMEDIATE sql_stmt;
Something like that anyway (the quotes might not line up, as I hacked this a bit from the actual code). Looking at the contents of sql_stmt, I get:
insert into "app_user"#mobileapi (USERNAME, VERSION, ACCOUNT_EXPIRED, ACCOUNT_LOCKED, CIPHER_PASSPHRASE, ENABLED, PASSWD,PASSWORD_EXPIRED)
values ('user#email.com', 0, 0, 0, 'asdfastrwaebawavgansdhnsgjsjsh', 1, 'awercbcakwjerhcawuerawieubkahbewvkruh', 0)
The EXECUTE IMMEDIATE completes, and checking the target table, the values are there.
Possibly a crappy solution, but better than nothing.

Sanitisation of input for a stored MySQL database procedure

I am using a database someone else produced (and I am not really authorised to change it). However, as I was looking into the stored procedures within the database I noticed the following procedure:
DELIMITER $$
CREATE PROCEDURE `logIn`(userName varChar(50), userPass varChar(50))
BEGIN
declare userID int;
SELECT
u.userID INTO userID
FROM
users u
WHERE
u.userName=userName
AND u.userPassword=MD5(userPass);
IF (IFNULL(uID,-1) > 0) THEN
select 1 as outMsg;
ELSE
select 0 as outMsg;
END IF;
END$$
with the corresponding table users having three columns: userID INT, userName VARCHAR(50) and userPassword VARCHAR(50).
As I am not very good at this, could someone let me know whether the input for such a function needs to be sanitised as to not allow any SQL injections and if not - why? A general rule of thumb would be very much appreciated.
P.S. This function will be called from a JS script on a form submit.
There are a few rules of thumb here that depend on the underlying datatype and how it's inserted into the database.
First, Parameterized queries are always best for SQL Injection protection.. but.. if you can't change that..
String type:
Remove any single quotes
OR
Replace any single quotes with the single quote twice.
Replace any of the following characters with their encoded alternative;
>
<
"
;
(chr 34)
)
(
For example.. ) is replaced with & #x29;
-(the space in the above example is so you'll see the code, remove it to get ")")
For a datatype other then string, check that the datatype is sane and remove any character that shouldn't be in the datatype. If it's an integer, make sure the string that you're passing in is an integer. This can commonly be done by casting to the type in code. The cast will either work.. or cause an error. It's also good to check that the datatype min and maxes have not been exceeded. For example.. If I was checking for an integer, I might use code similar to this:
var myInt = parseInt(param);
Then I might check it's bounds to be sure it's less then the maximum integer value and greater then the minimum integer value.
That should be good enough to prevent a SQL Injection attack...
And.. since you have not posted the code that actually interfaces with the database... As an added precaution.. you may also want to remove --,`,%,",", "".
You only want 'sane' values getting to the database call.. so an integer like, $309 wouldn't make sense, you'd want to remove the $.. . probably by using a regex replace for any non numeric characters a comma and a period.
[^[0-9,.]]
Be extra cautious.
Yes, the input must be sanitized before trying to run the procedure.
You might want to share the actual calling point for the procedure to get more help here, since there is no way that the procedure is called directly from JS on form submit. You probably have a Servlet, PHP page or some HTTP friendly intermediary to make the database call somehow.