How do I use an ID throughout a Delphi app - delphi

I have a MySql database which has a users table and all the other tables are connected to the users table.
If we log in to an user that has id = 1. We want to make all the requests to mysql using the user_id = 1, so it'll be displayed only the data from this specific user.
I have a login screen in my Delphi app where the user can type his username and password. After a successfully login I want to take thie user_id to use and throughout this application to make the calls. How should I do it?
After the login in successfully, should I sotre this ID in Delphi somehow and access it whenever we want to make some call? If so, how do I save this id in Delphi so I can use it in several forms?

A way to do this is to add a DataModule to your project and make the UserID a
field of this.
So, using Delphi's default naming, you would have
type
TDataModule1 = class(TDataModule)
[...]
public
property UserID : Integer read FUserID;
end;
[...]
var
DataModule1 : TDataModule1;
Then, add to TDataModule1 the db-access components your project uses, e.g. a Query
component to access the Users table where user details are stored and a method to
try to login in a given user, and also a db-connection component of the type your db-access components use. So you could add a method like this:
function TDataModule1.LogInUser(const UserName : String) : Boolean;
begin
try
if qUsers.Active then
qUsers.Close;
FUserID := -1; // deliberately set an invalid UserID
Assert(Trim(UserName) <> '')); // raise exception if UserName is blank
// qUsers Sql.Text property would be something like 'select * from users where username = :username'
qUsers.ParamByName(UserName).Value := UserName;
qUsers.Open;
if qUsers.Locate('UserName', UserName, []) then
FUserID := qUsers.FieldByName('UserID').AsInteger;
finally
Result := FUserID >= 0; // assuming zero is a valid UserID
end;
end;
and save the module under the name, say, of DataModule1u. Notice that this method omits any reference to the user's password: This is because if you leave your db connection component's LoginPrompt property at the default True value, it will pop up a prompt for the user's password when the LogInUser method executes.
Once you've got that far, go to your Login form's unit and edit it to USE DataModule1u.
Then you can add code to it to use the LogInUser method like so:
if DataModule1.LogInUser(UserName) then begin
Caption := 'User: ' + IntToStr(DataModule1.UserID);
// do something else with DataModule1.UserID's value
end;
Obviously you could (and usually should) add all your db-access components to the datamodule
and USE it in your forms which do db-access.
Hope this is all clear, if not then ask.

Related

Accept user input and set assign to variable PL/SQL

I am trying to accept user input inside of a stored procedure and assign it to a VARCHAR variable. For some reason I get error
PLS-00201: identifier 'userinput' must be declared.
Any ideas? I have to use this later down the line to see how many times the input appears in a table.
CREATE OR REPLACE PROCEDURE nums
AS
x_num VARCHAR(20);
BEGIN
x_num := &input;
dbms_output.put_line('You entered: ' || x_num);
END;
/
Procedures cannot receive user input in response to a prompt, PLSQL is NOT interactive. When you have that you are not actually communicating with the database. What is actually happening is symbol substitution were SQLPLUS or other interface (Toad , SQL Developer, ...) is actually intercepting symbol, requesting the input, and physically changing the script before submitting it to the database. If you want a stored procedure you will need to use a parameter as #HereGoes suggested and then provide the user a script as follows:
Begin
nums(pInput => &Input);
end ;
Or provide an application interface to receive the input value and call the procedure or allow user access through SQLPLUS or other interface and let them enter the script - not recommended.
I suggest making the input a parameter.
CREATE OR REPLACE PROCEDURE nums (pInput IN VARCHAR2)
AS
x_num VARCHAR(20);
BEGIN
x_num := pInput ;
dbms_output.put_line('You entered: ' || x_num);
END;
/

Login Program in Delphi XE7 which gives an error: Parameter object is improperly defined. Inconsistent or incomplete information was provided

I have the following problem with a simple and basic program that I am writing in Delphi. It is a Login program where the user enters a username and password. The program will then get the password from an access database where the username equals to the username that the user entered. Then the program will compare the password that it got from the access database with the password that the user entered.
Here is a copy of my code:
(I have the following variables: Password, Username, sPassword)
Username := edtUsername.Text;
Password := edtPassword.Text;
UserQuery.SQL.Add('Select Password as Password1 from Users where Username = :Username');
UserQuery.Parameters.ParamByName('Username').Value := Username;
UserQuery.Open;
sPassword := UserQuery['Password1'];
if sPassword = Password then
begin
showmessage('Correct');
end
else
begin
showmessage('Incorrect');
end;
It saves the query value to a variable. If the username is correct and the password that the user entered is correct the program works fine.
My problem is that the second time or if anything like the username or password was typed in wrong by the user it gives me an error : Parameter object is improperly defined. Inconsistent or incomplete information was provided. I think it has to check if the query exists but I do not know how to do it. How can I solve this problem?
(I am still a learner)
The problem is, that every time your method is executed, you're adding a SQL statement into your query. If you inspect while debugging for its value, or just ShowMessage(UserQuery.SQL.Text) you'll clearly see it
It will look like this:
Select Password as Password1 from Users where Username = :Username
Select Password as Password1 from Users where Username = :Username
Select Password as Password1 from Users where Username = :Username
...
The query then fails because from the second to the last :Username parameter, no value is provided
There are a few ways to fix it. One of the may involve Clearing the Query then assign the SQL statement again:
UserQuery.Clear; // clear the query before adding the SQL statement
UserQuery.SQL.Add('Select Password as Password1 from Users where Username = :Username');
UserQuery.Parameters.ParamByName('Username').Value := Username;
UserQuery.Open;
Or you can just assign directly to the Text property which will replace the whole string with the new supplied value:
UserQuery.SQL.Text := 'Select Password as Password1 from Users where Username = :Username';
UserQuery.Parameters.ParamByName('Username').Value := Username;
UserQuery.Open;
Since you are using the same query over and over, the most ideal setup would be initialize it (on a constructor for example) and leave it on a Prepared state. A prepared SQL statement is preparsed and sent to the DB engine, leaving it ready to execute. It should be used when you have a query or command that you need to execute over and over and the only thing you change are the parameters values (just your case)
procedure TForm1.Create(Sender: TObject);
begin
UserQuery.SQL.Text := 'Select Password as Password1 from Users where Username = :Username';
// it's a good practice to set the parameter type
UserQuery.Parameters.ParamByName('Username').DataType := ftString;
// prepares the query: preparses sql, sends it to the DB engine, etc
UserQuery.Prepared := True;
end;
// usage
procedure TForm1.YourLoginMethod;
begin
UserQuery.Parameters.ParamByName('Username').Value := Username;
UserQuery.Open;
try
sPassword := UserQuery.FieldByName('Password1').AsString;
// perform login logic
finally
UserQuery.Close;
end;
end;
Just a couple things more to note. I recommend that, similar to set a parameters DataType, you use type-safe TField properties, aka .AsString, .AsInteger, etc
The default property of TDataSet will return a Variant for the given field value, and the perform an implicit conversion. I suggest being explicit since you know your data types better than the RTL
Also a try-finally block is needed here. When using prepared querys, every time you execute them, you need to have a closed query, set the parameters and then call open. The try-finally will grant that every time you open the query, it will be closed regardless of execptions that may ocurr

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.

Database Username and Password in Delphi?

I would like to create a type of login window in Delphi. Unfortunately I can't get it to match the username and password.
I have a basic .mdb database, with an Users table. In this table, there is a Username and Password. I want Delphi to check the username and password in the database and if it matches those in the edit boxes, it goes to the next form or shows a message, otherwise it does nothing. When I enter the database's first row, username and password values, I get success, but with the second, nothing. I feel like I need a way to get it to move on to the second rows values and check those and so on. There is also currently no relationships in the database.
This is my layout of Data Access: ADOConnection -> ADOTable -> DataSource
Thanks in advance!
As per your guess, one solution can be to move record by record to check each one. Example:
function MatchPass(Table: TADOTable; const Name, Pass: string): Boolean;
begin
Result := False;
Table.First;
while not Table.Eof do begin
if Table.FieldByName('Username').AsString = Name then begin
Result := Table.FieldByName('Password').AsString = Pass;
Exit;
end;
Table.Next;
end;
end;
Can be called like:
if MatchPass(ADOTable1, Edit1.Text, Edit2.Text) then
..
Another solution can be to let the ADOTable search for a corresponding record:
function MatchPass(Table: TADOTable; const Name, Pass: string): Boolean;
begin
Result := Table.Locate('Username;Password', VarArrayOf([Name, Pass]), []);
end;

Access violation when affecting a value to variable?

The code below is written in unit2 ( form2), it calls the values entered in the email and password boxes ( in form1 ), yesterday the code was working perfectly, i made some changes and now : This code doesn't work, it raises an Access Violation error when i click the Button COMMENCER:
procedure TForm2.Btn_commencerClick(Sender: TObject);
begin
email := form1.ed_Email.Text;// <----- LOOK HERE
password := form1.Ed_typedpass.Text; // <-----AND HERE
MD5 := GetMD5;
MD5.Init;
MD5.Update(TByteDynArray(RawByteString(password)), Length(password));
password := LowerCase(MD5.AsString);
etc.......
But this code works :
email := 'myemail#yahoo.com';
password := 'mypass';
MD5 := GetMD5;
MD5.Init;
etc etc......
The question :
Why ?
Where are you creating your form1 object? Sounds like it haven't initialized before you access it and therefore you get AV.
Your second code works, because you don't have to initialize string variables before accessing or assigning values to them and you are assigning them directly, not through the form1 variable.
But breakpoint to email := form1.ed_Email.Text; and look if form1 is nil or not.
i think you have to create the form1 (as i think its available form in your case)..and you may have closed and freed the form so, your
email := form1.ed_Email.Text;
is giving AV, as form1 doesnt exist(as its freed now) ,so u cannot have the ed_Email.Text value.
make sure your not closing the form1 (freeing) before pressing Btn_commencer
You can always check if the form has been created first to avoid access violations.
if assigned(Form1) then
begin
// assignments
end;

Resources