This is the code I've got at the moment:
procedure TfrmLogin.performQuery;
begin
query.SQL.Text := 'SELECT * FROM LoginDatabase';
query.Open;
end;
procedure TfrmLogin.FormCreate(Sender: TObject);
begin
con := TFDConnection.Create(nil);
query := TFDQuery.Create(con);
con.LoginPrompt := false;
con.Open('DriverID=SQLite;Database=C:\Users\katiee\Documents\Embarcadero\' +
'Studio\Projects\ProgramDatabase;');
query.Connection := con;
performQuery;
username := query.FieldByName('Username').AsString;
passcode := query.FieldByName('Passcode').AsString;
end;
procedure TfrmLogin.btnLoginClick(Sender: TObject);
begin
if (edtUsername.Text = username) and (edtPasscode.Text = passcode) then
frmPayInfo.show
else
if MessageDlg('The username or password entered is incorrect.', mtWarning,
[mbOK, mbCancel], 0) = mrCancel then
frmLogin.Close;
end;
Right now the code, as it doesn't read all database entries, only allows the first database entry as correct answer.
I need to be able to enter any correct combination of username and pass-code from the database and open the form.
Why not call the .Locate method on the dataset? This way you will not be opening and closing the dataset, as you would need to with using a parameterised query.
Using the .Locate method will allow you to check (based off one, or many fields) if a certain record can be found.
If the record is found .Locate will return true.
For example.
if query.Locate('username;passcode', VarArrayOf([query.FieldByName('Username').AsString, query.FieldByName('Passcode').AsString]), [loPartialKey]) then
begin
frmPayInfo.show;
end
else
begin
Showmessage('User not found, or password incorrect');
end;
I haven't got Delphi on this machine, so syntax may not be 100%.
Related
I was trying to prevent SQL injection in my Delphi application by securing user inputs using the following query:
procedure TForm1.Button1Click(Sender: TObject);
var userNameID : string;
begin
userNameID := edit1.Text;
with adoquery1 do
begin
sql.Clear;
sql.Add('select * from users where id = :'''+userNameID+''';');
Open;
end;
end;
but it's not returning any results.
Can you tell me what's wrong in my code please?
Your code doesn't do anything to prevent SQL injection, because you're still directly concatenating text to the query. Your SQL syntax is also invalid.
Something like this will work:
procedure TForm1.Button1Click(Sender: TObject);
begin
AdoQuery1.SQL.Text := 'select * from users where id = :ID');
AdoQuery1.Parameters.ParamByName('ID').AsString := edit1.Text;
AdoQuery1.Open;
end;
I have a little piece of code that gets a corresponding value from database when one is entered, that is, in a database like this
Code Name
1 A
2 B
3 C
When you enter 1 in the code area, it returns A, and if you enter B in the Name area, it returns 2.
I can get this to work just fine with the following code but an error message would pop out showing "Cannot perform this operation on a closed dataset"
Does anyone know how to solve this problem? Any help would be appreciated. Thanks
procedure TStock_Bookkeeping.Stock_Code_ValueChange(Sender: TObject);
begin
Price_Per_Share_Value.Text := GetCurrentPrice(Stock_Code_Value.text);
ExecuteSQL_CodeToName(Stock_Code_Value.Text);
end;
procedure TStock_Bookkeeping.Stock_Name_ValueChange(Sender: TObject);
begin
ExecuteSQL_NametoCode(Stock_Name_Value.Text);
end;
procedure TStock_Bookkeeping.ExecuteSQL_CodeToName(const inCode: string);
var
query : TFDQuery;
begin
query := TFDQuery.Create(nil);
try
ConnectToSQLite;
query.Connection := FDConnection1;
query.SQL.Text := 'SELECT * FROM Code_Name where Code =' + '"' + inCode + '"' ;
query.Active := True;
query.Open();
while not query.Eof do
begin
Stock_Name_Value.text := query.FieldByName('Name').AsString;
query.Next;
end;
finally
query.Close;
query.DisposeOf;
end;
end;
procedure TStock_Bookkeeping.ExecuteSQL_NameToCode(const inName: string);
var
query : TFDQuery;
memTable : TFDMemtable;
begin
query := TFDQuery.Create(nil);
try
ConnectToSQLite;
query.Connection := FDConnection1;
query.SQL.Text := 'SELECT * FROM Code_Name where Name ='+'"'+ inName+'"';
query.Active := True;
query.Open();
while not query.Eof do
begin
Stock_Code_Value.text := query.FieldByName('Code').AsString;
query.Next;
end;
finally
query.Close;
query.DisposeOf;
end;
end;
Set a breakpoint and walk the code (F7):
If the Stock_Code_ValueChange event is connected to the Stock_Code_Value edit box when you change the value of Stock_Code_Value.Text you are changing the value of Stock_Name_Value.Text which then fires the Stock_Name_ValueChange event but you have not completed the Stock_Code_ValueChange event.
Add a Stock_Name_ValueOnChange := nil as the first line of the Stock_Code_ValueChange procedure and a Stock_Name_ValueOnChange := Stock_Name_ValueChange as the last line of the procedure to stop the Stock_Name_ValueChange event from firing while the Stock_Code_ValueChange event is processing.
Then do the opposite for the Stock_Name_ValueChange event.
My query,when run takes about 7 seconds to do what is supposed to.But,since its inserting about 30 records,I think it is too slow.Now,either I am running the query that is not written well or it does actually takes this much time. But that would be strange. The underlying database is SQLite and the query looks like this :
procedure TForm1.cxButton1Click(Sender: TObject);
begin
with UNIquery2 do begin
Close;
SQL.Clear;
UNIQuery1.First;
while Uniquery1.EOF = false do begin
SQL.Text:= 'INSERT INTO MYTABLE (FIELD1,FIELD2,FIELD3,FIELD4) VALUES (:a1,:a2,:a3,:a4)';
ParamByName('a1').asString := AdvOfficeStatusBar1.Panels[0].Text;
ParamByName('a2').asString := UniTable1.FieldByName('FIELD2').asString;
ParamByName('a3').asString := Uniquery1.FieldByName(',FIELD3').asString;
ParamByName('a4').Value := Uniquery1.FieldByName('FIELD4').Value;//boolean field true/false
Uniquery1.Next;
ExecSQL;
end;
end;
end;
So can someone tell me if this is OK or am I missing something ?
All fields are text except the 'a4' which is boolean (true/false).
The answer,modified (based on suuggestion from LS_dev):
procedure TForm1.cxButton1Click(Sender: TObject);
begin
with UNIquery2 do begin
Close;
SQL.Clear;
SQL.Add('INSERT INTO MYTABLE (FIELD1,FIELD2,FIELD3,FIELD4) VALUES (:a1,:a2,:a3,:a4)');
SQL.Prepare;
UniTransaction.AddConnection(UniConnection2);
UniTransaction.StartTransaction;
try
UNIQuery1.First;
while Uniquery1.EOF = false do begin
Params[0].asString := AdvOfficeStatusBar1.Panels[0].Text;
Params[1].asString := UniTable1.FieldByName('FIELD2').asString;
Params[2].asString := Uniquery1.FieldByName(',FIELD3').asString;
Params[3].Value := Uniquery1.FieldByName('FIELD4').Value;//boolean field true/false
Uniquery1.Next;
ExecSQL;
end;
UniTransaction.Commit;
finally
if UNIquery2.Connection.InTransaction then
UNIquery2.Connection.Rollback;
end;
end;
end;
Don't know Delphi, but will suggest some improvements:
You are not using a transaction. You should have something like something like auto-commit disabled and COMMIT command after all insertions;
Your SQL.Text:=... should probably be out of while. If this property set compiles SQL statement, putting it out of while will prevent unnecessary VDBE compilations;
If your intent is copying rows from one table to another (with a static field), you may doing using a single SQL command like INSERT INTO MYTABLE SELECT :a1, FIELD2, FIEDL3, FIELD4 FROM source_table, setting ParamByName('a1').asString := AdvOfficeStatusBar1.Panels[0].Text
This is generic DB usage improvement, hope gives you some direction.
Suggestion using unique SQL:
procedure TForm1.cxButton1Click(Sender: TObject);
begin
with UNIquery2 do
begin
SQL.Clear;
SQL.Add('INSERT INTO MYTABLE (FIELD1,FIELD2,FIELD3,FIELD4) SELECT ?,FIELD2,FIELD3,FIELD4 FROM UNIquery1_source_table');
Params[0].asString := AdvOfficeStatusBar1.Panels[0].Text;
ExecSQL;
end;
end;
Suggestion using improved DB handling:
procedure TForm1.cxButton1Click(Sender: TObject);
begin
with UNIquery2 do
begin
Close;
SQL.Clear;
SQL.Add('INSERT INTO MYTABLE (FIELD1,FIELD2,FIELD3,FIELD4) VALUES (:a1,:a2,:a3,:a4)');
SQL.Prepare;
UniTransaction.AddConnection(UniConnection2);
UniTransaction.StartTransaction;
UNIQuery1.First;
while Uniquery1.EOF = false do
begin
Params[0].asString := AdvOfficeStatusBar1.Panels[0].Text;
Params[1].asString := UniTable1.FieldByName('FIELD2').asString;
Params[2].asString := Uniquery1.FieldByName(',FIELD3').asString;
Params[3].Value := Uniquery1.FieldByName('FIELD4').Value;//boolean field true/false
Uniquery1.Next;
ExecSQL;
end;
UniTransaction.Commit;
end;
end;
If the SQL INSERT itself is slow, I recommend to test its execution speed in an interactive client first. Or write a simple test application which performs one hard-coded INSERT and measures its execution time.
Also you can use the debugger, logging or a profiler to find out the operation in your code which consumes time - it could be the Uniquery1.Next or the ExecSQL for example.
I have an empty table in access and add data with a SQL INSERT statement.There after I use a procedure to go through the table and use ADO to put all the values into an array of objects.
After debugging i can see that the procedure doesnt find any values in the table and says that the table is at EOF and doesnt extract any values out of the table.
However if i terminate my application (close the program) and run it again, it seems to have "inserted" and "saved" the table in access and then does find values in the table and extracs it with ADO, and inserts it into the array of objects.
somehow i need to het the access database "saved" through delphi
//inserts the new(first and following) records
begin
qryVote.Active := False;
qryVote.SQL.Text := 'insert INTO tblkandidate ([Leerder nr],Van,Naam,geboortedatum,[id nr],geslag,[sel nr],debietpunte,voogklas,deelname,stemme) VALUES ("'+leerdernr+'","'+van+'","'+naam+'",#'+gebdatum+'#,"'+idnr+'","'+geslag+'","'+selnr+'",'+inttostr(debiete)+',"'+voogklas+'","'+booltostr(bsport)+'",'+inttostr(stemme)+') ';
qryVote.ExecSQL;
qryVote.SQL.Text := 'select * from tblkandidate';
qryVote.Active := true;
KandidateNaSkik; //procedure that goes through the table and puts every record into the array of objects (see below foe precedure)
showmessage('Jou pesoonlike data is gestoor');
end;
procedure Tfrmvote.KandidateNaSkik;
var
leerdernr,naam,van,idnr,selnr,voogklas,gebdatum,geslag : STRING;
stemme, debiete: integer;
bsport, gestem : boolean;
begin
frmvote.ADOTableVotek.open;
with dbgviewp.DataSource.DataSet do
begin
frmvote.ADOTableVotek.first;
iaantkan :=0;
while not frmvote.ADOTableVotek.EOF do
begin
inc(iaantkan);
leerdernr := frmvote.ADOTableVotek['LEERDER NR'];
van := frmvote.ADOTableVotek['VAN'];
naam := frmvote.ADOTableVotek['NAAM'];
gebdatum := frmvote.ADOTableVotek['geboortedatum'];
idnr := frmvote.ADOTableVotek['id nr'];
geslag := frmvote.ADOTableVotek['geslag'];
selnr := frmvote.ADOTableVotek['sel nr'];
debiete := frmvote.ADOTableVotek['debietpunte'];
voogklas := frmvote.ADOTableVotek['voogklas'];
bsport := frmvote.ADOTableVotek['deelname'];
stemme := frmvote.ADOTableVotek['stemme'];
gestem := frmvote.ADOTableVotek['gestem'];
//the above variables get sent to the array below
arrkandidaat[iaantkan] := tVerkiesing.create(leerdernr, van, naam,
gebdatum,idnr,geslag,selnr,debiete,voogklas,bsport,gestem,stemme);
frmvote.ADOTableVotek.Next;
end;{while}
end;{with}
end;
Since you insert a record via qryVote.ExecSQL, the ADOTableVotek dataset dose not "know" that a new record was added to the DB, so either you call:
frmvote.ADOTableVotek.Requery;
e.g.:
procedure Tfrmvote.KandidateNaSkik;
begin
...
if not frmvote.ADOTableVotek.Active then
frmvote.ADOTableVotek.Open
else
frmvote.ADOTableVotek.Requery;
...
end;
Or add the new record via ADOTableVotek itself like this:
ADOTableVotek.Append; // add new record
ADOTableVotek.FieldByName('Leerder nr').AsString := leerdernr;
// ADOTableVotek.FieldByName('etc..')...
// etc...
ADOTableVotek.Post; // post new record to the DB
The changes will reflect imedeatlly into ADOTableVotek and in your dbgviewp.
This way the ADOTableVotek can be always active. You need to call ADOTableVotek.Open only once (or set ADOTableVotek.Active := True). And you don't need to fetch all records from your DB each time you insert a new record.
In order to reflect the changes try closing and reopening the dataset, like so
frmvote.ADOTableVotek.Close;
frmvote.ADOTableVotek.Open;
i am trying to print specifics detail for a specific record from a database by the following code
procedure TReportsForm.BitBtn1Click(Sender: TObject);
begin
if edit1.Text = '' then
begin
ShowMessage('Please enter the serial number first');
Edit1.SetFocus;
end
else
begin
with AddIndividualsAccountADOQuery do
begin
SQL.Clear;
SQL.Add('Select * from IndividualAccount where [Serial Number]=:Sno');
Parameters.ParamByName('Sno').Value:=edit1.Text;
Open;
Active := True;
singlememberreportform.QuickRep1.Preview;
end;
if AddIndividualsAccountADOQuery.RecordCount = 0 then
begin
ShowMessage('Empty Jonathan database to view records.');
end
end;
end;
the problem is that when i run the program, the quickreport displays all the records in the database instead of printing only records with edit1.text.in other words the report that runs is similar to just typing
procedure TReportsForm.BitBtn1Click(Sender: TObject);
begin
singlememberreportform.QuickRep1.Preview;
end;
Any idea of the problem?