Can't query a string from an SQL table - delphi

I am trying to pass the value of a group from the database into a TEdit when an item is selected in a TComboBox. However, the value returned should be a string, not an integer. In the text field for the group, the value returned is 0. Can anyone help me with this?
This is the code for the text field that should return the group data based on the item selected in a TComboBox:
ADOQuery1.SQL.Clear;
rf := ADOQuery1.SQL.Add('SELECT grouppp FROM f3_sheet WHERE holder = "' +cb1.Text +'"');
gpp.Text := rf;

The TADOQuery.SQL property is a TStrings object. Its Add() method returns the index of the string you just added to the list. That is why the return value is an integer 0 in your example.
But that is not what you want in this situation. After you fill in the SQL statement as needed, you need to then actually execute that SQL on the database by calling the TADOQuery.Open() method, and then you can read the retrieved field value from the TADOQuery.Fields collection, eg:
ADOQuery1.SQL.Text := 'SELECT grouppp FROM f3_sheet WHERE holder = ' + AnsiQuotedStr(cb1.Text, '"');
ADOQuery1.Open;
try
if not ADOQuery1.Eof then
gpp.Text := ADOQuery1.Fields[0].AsString
else
gpp.Text := '';
finally
ADOQuery1.Close;
end;
That being said, notice how I changed your SQL to use AnsiQuotedStr() instead of wrapping cb1.Text with quotation marks manually. Your original code suffers from a potential SQL Injection Attack, if the user is allowed to enter arbitrary text into the TComboBox.
For example, if the user were to enter something like "; DELETE FROM f3_sheet; -- into the TComboBox, your original code would end up executing this SQL:
SELECT grouppp FROM f3_sheet WHERE holder = ""; DELETE FROM f3_sheet; --"
And the contents of your database table would go bye-bye!
Making the TComboBox read-only is one way to mitigate that attack, so that only your code is allowed to specify valid strings that won't corrupt the SQL.
Using AnsiQuotedStr() is another way, by escaping embedded quotation marks in the user's text, eg:
SELECT grouppp FROM f3_sheet WHERE holder = """; DELETE FROM f3_sheet; --"
Now the SQL will search the holder field for the literal string "; DELETE FROM f3_sheet; -- and not find any result.
However, the best way to avoid such an attack is to simply not create SQL statements by hand in the first place, use Parameterized Queries or Stored Procedures instead. For example, the above example can be re-written to use Parameters like this:
// make sure to set ADOQuery1.ParamCheeck=true beforehand...
ADOQuery1.SQL.Text := 'SELECT grouppp FROM f3_sheet WHERE holder = :PHolder';
ADOQuery1.Parameters.ParamByName('PHolder').Value := cb1.Text;
ADOQuery1.Open;
try
if not ADOQuery1.Eof then
gpp.Text := ADOQuery1.Fields[0].AsString
else
gpp.Text := '';
finally
ADOQuery1.Close;
end;
Let the database handle any quoting and escaping requirements for you.

Related

How can i append record to TAdoQuery without clearing its fields?

I am using TAdoQuery with BatchOptimistic lock type. If the select command has some fields that are calculated on database server the returned fields has property ReadOnly = true, so i must change them to false so i can modify them in my query.
I am actually never attempting to post to database, but i must use TAdoQuery.
Its all good to the point I append or insert some record, set its fields, and then call for example TAdoQuery.Last, Next or First.. The appended records fields change to null. Please, is there a way that these records could stay as they were?
I am attaching a simple code here, where the problem is presented:
// .. lockType = Batchoptimistic so TAdoQuery.first or TAdoQuery.last do NOT post do database
ADOQuery1.LockType := ltBatchOptimistic;
ADOQuery1.SQL.Text := 'SELECT 10 AS id, 20 AS sid ';
ADOQuery1.Open;
// .. readOnly = false so i can modify these two fields in appended record
ADOQuery1.FieldByName('id').ReadOnly := false;
ADOQuery1.FieldByName('sid').ReadOnly := false;
ADOQuery1.Append;
ADOQuery1.FieldByName('id').AsInteger := 5;
ADOQuery1.FieldByName('sId').AsInteger := 5;
// if use last, first etc. the appended record fields will change to 0 (null)
ADOQuery1.Last;

How to use a SQL where statement in delphi properly?

I have a problem with issuing a SQL statement. I know that the English value should be a string on its own and I've tried that but it keeps throwing me one of these errors
procedure TfrmPetersonGroup.btnEnglishClick(Sender: TObject);
var
sSqlQuery:string;
begin
//2.4
dmoBandB.qryQuery.SQL.Clear;
sSqlQuery:='DELETE FROM tblClients WHERE Nationality =' + ' English';
dmoBandB.qryQuery.SQL.Text := sSqlQuery;
dmoBandB.qryQuery.active := true;
end;
I suggest you get a safe query. As below:
procedure SafeDeleteReq(SQLQuery: TSQLQuery; del: string);
begin
SQLQuery.SQL.Text := 'DELETE FROM tblClients WHERE Nationality=:Nationality';
SQLQuery.ParamByName('Nationality').AsString := del;
SQLQuery.ExecSQL();
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
SafeDeleteReq(SQLQuery1, 'English');
end;
You could change your sSql to
sSqlQuery:='DELETE FROM tblClients WHERE Nationality = ' + QuotedStr('English');
just to get it working, but that's not the best idea, see below.
Your version of it caused the error because, without quotes around it, the Sql parser thought that English was an identifier, e.g. another column name like Nationality.
Using QuotedStr around column values ensures that single-quote characters embedded in the value, like
O'Brien
are escaped correctly.
The other thing is that you should replace
dmoBandB.qryQuery.active := true;
by
dmoBandB.qryQuery.ExecSql;
The reason is that setting Active to True is equivalent to calling .Open, which is invalid in this context because .Open only works if the Sql query returns a result set and DELETE does not (sorry, I should have noticed this problem first time around). Once you've called ExecSql, you can reopen the table by setting qryQuery's Sql.Text to a valid SELECT statement and then calling .Open.
However, a betteer way to avoid your initial problem would be to get into the habit of using parameterised Sql statements - see http://docwiki.embarcadero.com/RADStudio/Rio/en/Using_Parameters_in_Queries, which is applicable to all Sql DML statements (Insert, Delete, Update, Select, etc). Apart from anything else, this may help you avoid Sql Injection exploits (https://en.wikipedia.org/wiki/SQL_injection).

Filter Query/DataSource records onto cxgrid

Using Delphi XE2.
Building a software package which connects to a database via query/datasource.
I would like to implement a filter option for records in a table, so at a click of a button a cxgrid will then display the records which match the filter selections.
I cant quite work out how to do this. Any help will be appreciated.
Have this so far but I honestly don't if this is close to what im trying to achieve.
procedure TFilter.btnClick(Sender: TObject);
begin
with aQry do
begin
SQL.Clear;
Close;
SQL.Text := 'select * from TABLE where record_name like'+QuotedStr(name.Text+'%');
SQL.Text := 'and record_type like '+QuotedStr(type.Text+'%');
SQL.Text := 'and record_type2 like '+QuotedStr(type2.Text+'%');
SQL.Text := 'and record_type3 like '+QuotedStr(type3.Text+'%');
SQL.Text := 'and record_type4 like '+QuotedStr(type4.Text+'%');
Open;
end;
end;
In your code you are building a invalid SQL sentence, because you are overwritten the content of the SQL each time which the Text property is set, So you must use the Add method to build the SQL sentence. Also you must consider use parameters.
Try the next sample which build and run a parameterized SQL sentence depending of the values entered on the filters (maybe you will need modify the source in order to run).
AQry.Close;
AQry.SQL.Clear;
AQry.SQL.Add('select * from TABLE where 1=1');
if name.Text<>'' then
AQry.SQL.Add('and record_name like :record_name');
if Edittype.Text<>'' then
AQry.SQL.Add('and record_type like :record_type');
if type2.Text<>'' then
AQry.SQL.Add('and record_type2 like :record_type2');
if type3.Text<>'' then
AQry.SQL.Add('and record_type3 like :record_type3');
if type4.Text<>'' then
AQry.SQL.Add('and record_type4 like :record_type4');
if name.Text<>'' then
Aqry.Parameters.ParamByName('record_name').Value := name.Text+ '%';
if Edittype.Text<>'' then
Aqry.Parameters.ParamByName('record_type').Value := Edittype.Text+ '%';
if type2.Text<>'' then
Aqry.Parameters.ParamByName('record_type2').Value := type2.Text+ '%';
if type3.Text<>'' then
Aqry.Parameters.ParamByName('record_type3').Value := type3.Text+ '%';
if type4.Text<>'' then
Aqry.Parameters.ParamByName('record_type4').Value := type4.Text+ '%';
AQry.Open;
You can do what you want using the Query's Filter property. First, execute the query to get all records that could be displayed.
aQry.SQL.Text := 'select * from TABLE';
aQry.Open;
Then when the values to filter on are defined, define and activate the Filter
aQry.Filtered := false;
aQry.Filter := 'record_name like'+QuotedStr(name.Text+'%') '+
'and record_type like '+QuotedStr(type.Text+'%') '+
'and record_type2 like '+QuotedStr(type2.Text+'%') '+
'and record_type3 like '+QuotedStr(type3.Text+'%') '+
'and record_type4 like '+QuotedStr(type4.Text+'%');
aQry.Filtered := true;
If any of the Text values are changed, you will have to execute the above again.
This approach does the filtering in your program's memory, and does not refresh the data from the database. If you expect the data in the database to change between filter changes, RRUZ's approach may be better for you.
An alternative to Query's Filter, is the cxGrid.TableView.DataSource.Filter. Defining it in code is a bit more complicated than the Query's Filter. But it can be easily be defined by the user without any code if you make TableView's Navigator and Navigator.Filter visible and enabled.

I have a syntax error in my insert into statement

I'm using a MS Access database, with the following columns in the Admins table:
Column Type
====== ====
Name Text
Surname Text
Dateadded Date/time
Adminnumber Number(long integer)
Password Text
ID type Autonumber (Not sure if ID is relevant)
This is my code but it keeps giving me a syntax error.
ADOquery1.Active := false;
adoquery1.sql.Text := 'insert into Admins(Name, surname, Adminnumber, Dateadded,password)Values('''+edit11.Text+''', '''+edit12.text+''', '''+edit13.Text+''', '''+edit14.Text+''', '''+edit15.text+''')';
ADOquery1.ExecSQL;
Adoquery1.SQL.Text := 'select * from Admins';
ADOquery1.Active := true;
i have been trying for a day to figure it out but its the same error no matter what code i use. The error is
Project project1.exe raised exception class eoleException
with message 'Syntax error in INSERT INTO statement'.
i have also tried:
ADOquery1.SQL.Add('Insert into admins');
ADOquery1.SQL.Add('(Name , Surname, Dateadded, Adminnumber, Password)');
ADOquery1.SQL.Add('Values :Name, :Surname, :Dateadded, :adminnumber :Password)');
ADOquery1.Parameters.ParamByName('Name').Value := edit11.Text;
ADOquery1.Parameters.ParamByName('Surname').Value := edit12.Text;
ADOquery1.Parameters.ParamByName('Dateadded').Value := edit13.Text;
ADOquery1.Parameters.ParamByName('Password').Value := edit14.Text;
ADOquery1.Parameters.ParamByName('Adminnumber').Value := edit15.Text;
ADOquery1.ExecSQL;
ADOquery1.SQL.Text := 'Select * from admins';
ADOquery1.Open ;
But this code gives me a problem with the from clause
The problem is that Name (and possibly Password) is a reserved word in MS Access. It's a poor choice for a column name, but if you must use it you should escape it by enclosing it in square brackets ([]). You're also missing an opening parenthesis (() after your VALUES statement, and a comma after the :adminnumber parameter.
ADOquery1.SQL.Add('Insert into admins');
ADOquery1.SQL.Add('([Name] , [Surname], [Dateadded], [Adminnumber], [Password])');
ADOquery1.SQL.Add('Values (:Name, :Surname, :Dateadded, :adminnumber, :Password)');
ADOquery1.Parameters.ParamByName('Name').Value := edit11.Text;
ADOquery1.Parameters.ParamByName('Surname').Value := edit12.Text;
ADOquery1.Parameters.ParamByName('Dateadded').Value := edit13.Text;
ADOquery1.Parameters.ParamByName('Password').Value := edit14.Text;
ADOquery1.Parameters.ParamByName('Adminnumber').Value := edit15.Text;
ADOquery1.ExecSQL;
ADOquery1.SQL.Text := 'Select * from admins';
ADOquery1.Open;
(The error can't be moving around, as you say in the comments to your question. The only line that can possibly cause the problem is the ADOQuery1.ExecSQL; line, as it's the only one that executes the INSERT statement. It's impossible for any other line to raise the exception.)
You should make some changes here that are pretty important to the maintainability of your code.
First, break the habit immediately of using the default names for controls, especially those you need to access from your code later. You change the name by changing the Name property for the control in the Object Inspector.
It's much easier in the code to use NameEdit.Text than it is to use Edit1.Text, especially by the time you get to Edit14. It would be much clearer if Edit14 was named PasswordEdit instead, and you'll be happy you did six months from now when you have to change the code.
Second, you should avoid using the default variant conversion from string that happens when you use ParamByName().Value. It works fine when you're assigning to a text column, but isn't really good when the type isn't text (such as when using dates or numbers). In those cases, you should convert to the proper data type before doing the assignment, so that you're sure it's done correctly.
ADOQuery1.ParamByName('DateAdded').Value := StrToDate(DateEdit.Text);
ADOQuery1.ParamByName('AdminNumber').Value := StrToInt(AdminNum.Text);
Finally, you should never, ever use string concatenation such as 'SOME SQL ''' + Edit1.Text + ''','''. This can lead to a severe security issue called SQL injection that can allow a malicious user to delete your data, drop tables, or reset user ids and passwords and giving them free access to your data. A Google search will find tons of information about the vulnerabilities that it can create. You shouldn't even do it in code you think is safe, because things can change in the future or you can get a disgruntled employee who decides to cause problems on the way out.
As an example, if a user decides to put John';DROP TABLE Admins; into edit14 in your application, and you call ExecSQL with that SQL, you will no longer have an Admins table. What happens if they instead use John';UPDATE Admins SET PASSWORD = NULL; instead? You now have no password for any of your admin users.

how to show Only relevant information in dbgrid delphi

information:
I have an order form.
With "keuze" and "aantal" it wright a new line. The Orderline gets an OrderID.
But the user may only see the orderline from his OrderID.
How can i make it work that it only shows, for example the OrderID "47" ?
procedure TfmOrder.btInvoerenClick(Sender: TObject);
begin
dm.atOrder.open;
dm.atOrder.Append;
dm.atOrder ['OrderStatus'] := ('Aangemeld');
dm.atOrder ['klantID'] := fminloggen.userid;
dm.atOrder ['OrderDatum'] := Kalender.date;
dm.atOrder ['Opmerkingen'] := leOpmerkingen.text;
dm.atOrder.post;
cbkeuze.Visible := true;
dbRegel.Visible := true;
leAantal.visible := true;
btOpslaan.Visible:= true;
end;
This is the code for making a new Order
procedure TfmOrder.btOpslaanClick(Sender: TObject);
var orderid:string;
begin
dm.atOrderregel.Open;
dm.atDier.open;
dm.atorderregel.Append;
dm.atOrderregel ['AantalDieren'] := leAantal.text;
dm.atOrderregel ['OrderID'] := dm.atOrder ['OrderID'];
dm.atOrderregel ['Diernaam'] := cbKeuze.Text;
dm.atOrderregel.Post;
leaantal.clear;
cbkeuze.ClearSelection;
end;
And this for a new orderline
thanks in advance
I know got a different error using this code:
begin
dm.atorder.Open;
dm.atorder.filter := 'KlantID = ' + (fminloggen.userid);
dm.atorder.filtered := true;
while not dm.atorder.Eof do
begin
cbOrder.Items.Add (dm.atorder['OrderID']);
dm.atOrder.Next;
end;
dm.atOrder.Close;
end;
It gives an error: The arguments are from the wrong type, or doesn't have right reach or are in conflict with each other.
here is userid declared.
var Gevonden: boolean;
userid : string;
begin
dm.atInlog.open;
Gevonden := false;
while (not Gevonden) and (not dm.atInlog.eof) do
begin
if dm.atInlog['email'] = leUser.Text
then
begin
Gevonden := true ;
inlognaam := dm.atInlog['email'];
userid := dm.atInlog['KlantID'];
end
else
dm.atInlog.Next
end;
this is obviously in another form
You can use the Filter property of the data set:
atOrderregel.Filter := 'OrderID = 47';
atOrderregel.Filtered := True;
You can add the grid's columns property statically in the object inspector, showing only the fields you need. If the columns list is empty (default) it is filled with all available fields.
Just add as many columns as you need and link each column to the corresponding field. You can reorder the columns and set the widths and titles individually. There are still some more properties available which are worth to explore.
Im assuming your grid is bound to a datasource component. This datasource is then linked with a TDataset descendant. There are a couple of ways you could acheive the desired filtering of the dataset to display only orderid 47.
Firstly, you could set the Datasets SQL property to contain a (server side) SQL query such as:
SELECT * from table WHERE OrderID = #OrderID
You would also need to create a parameter in the dataset to pass the (changing) value for the required OrderID. So add a new Parameter to the dataset (#OrderID), and then at runtime you can set this parameter value in code, something like:
DataSet.Parameters['#OrderID'].Value := ParameterValue;
Alternatively, you could also FILTER the dataset (client side) to just show the correct data:
Set your SQL property of the dataset to retrive the entire table, something like:
SELECT * FROM table
And then at runtime you could set the Filter property of the dataset to only get OrderID 47:
Dataset.Filter := 'OrderID = '+InttoStr(ParameterValue);
Depending on your needs one method may suit better (performance/memory) wise.
As Najem has commented, there is also a third method - using a Master-Detail dataset relationship. This method works using two datasets, one is the master of the other. When the master table record is changed, the detail dataset is then filtered using the value defined in the Key or MasterFields property of the M-D relatioship.
If you are connected to some datasource you could always create a SQL Query. Something like:
SELECT * FROM YourDBTable WHERE OrderID=47

Resources