Bad bind variable error in Oracle 10g developer form - oracle

I have created a table named password
CREATE TABLE PASSWORD (USER_ID NUMBER(10) CONSTRAINT PASSWORD_USER_ID_PK PRIMARY KEY,
PASSWD VARCHAR2(20) NOT NULL);
INSERT INTO PASSWD (USER_ID,PASSWD) VALUES (1,100);
INSERT INTO PASSWD (USER_ID,PASSWD) VALUES (2,200);
And created a Login form in an Oracle form developer 10g. And I used this code into Login button.
DECLARE
CURSOR login_cursor IS
SELECT user_id,
passwd
FROM password;
user_id_var password.user_id%TYPE;
passwd_var password.passwd%TYPE;
login_flag BOOLEAN := FALSE;
BEGIN
OPEN login_cursor;
<<check_records>>
LOOP
FETCH login_cursor INTO user_id_var, passwd_var;
IF( :login_user_id = user_id_var
AND :login_passwd = passwd_var ) THEN
Message('You are in');
login_flag := TRUE;
exit check_records;
END IF;
EXIT WHEN login_cursor%NOTFOUND;
END LOOP;
CLOSE login_cursor;
IF( NOT login_flag ) THEN
Message('INVALID LOGIN');
END IF;
clear_form;
END;
But error message appeared like
bad bind variable 'login_user_id'
bad bind variable 'login_passwd'
What's the solution for this?

Form variables are referenced using :block_name.item_name. You're using :login_user_id which seems to be missing the reference to block, hence Forms is not able to reference the variable and seems to thing it's a bind variable, which it's not.
Correct the syntax referencing the variables

Related

issues with package syntax

i am trying to create a package called MSGG_SESSION with a procedure authenticate that accepts two VARCHAR2 parameters for username and password. i am suppose to put an package-private NUMBER variable for the current person ID.If "authenticate" matches a username and password in MSGG_USER , put the matching PERSON_ID in the new variable. Add a function get_user_id to the package that returns the value of the variable holding the person ID.
but i get two erros saying table or view does not exits starting at the second is before not_authenticated_exception
and sql statement ignored starting at priv_number varchar2(100).
CREATE OR REPLACE PACKAGE MSGG_SESSION IS
PROCEDURE AUTHENTICATE (USERNAME_to_auth IN VARCHAR2, PASSWORD_to_use IN VARCHAR2);
FUNCTION AUTHENTICATED_USER RETURN VARCHAR2;
END MSGG_SESSION;
/
create or replace package body msgg_session is
priv_number varchar2(100);
procedure authenticate (username_to_auth in varchar2, password_to_use in varchar2)
is
not_authenticated exception;
begin
select username
into priv_number
from user_password
where lower(username) = lower(username_to_auth)
and password = password_to_use;
exception
when no_data_found then
begin
raise not_authenticated;
exception
when not_authenticated then
raise_application_error(-20000, 'Not authenticated');
end;
when others then
raise;
end authenticate;
function authenticated_user
return varchar2
is
begin
return null;
end;
function get_user_id
return varchar2
is
begin
return priv_number;
end get_user_id;
end msgg_session;
/
You don't provide table DDL or the line number of the error message so it's not clear why you would get ORA-00942: table or view does not exist. Check the spelling of the table, make sure the table and the package are in the same schema and nothing is defined in double-quotes (e.g. user_password is not the same as "user_password").
Assuming that the table looks something like this:
create table user_password
( user_id integer constraint user_password_pk primary key
, username varchar2(30) not null constraint user_password_username_uk unique
, password varchar2(30) not null );
with sample test data:
insert into user_password (user_id, username, password)
values (1, 'ndubizuacn', 'Kittens');
A fixed version of your package would look like this:
create or replace package msgg_session as
procedure authenticate
( username_to_auth in user_password.username%type
, password_to_use in user_password.password%type );
function get_user_id
return user_password.user_id%type;
end msgg_session;
/
create or replace package body msgg_session as
priv_number user_password.user_id%type;
procedure authenticate
( username_to_auth in user_password.username%type
, password_to_use in user_password.password%type )
is
begin
select user_id into priv_number
from user_password
where lower(username) = lower(username_to_auth)
and password = password_to_use;
exception
when no_data_found then
raise_application_error(-20000, 'Not authenticated');
end authenticate;
function authenticated_user
return varchar2
is
begin
return null;
end authenticated_user;
function get_user_id
return user_password.user_id%type
is
begin
return priv_number;
end get_user_id;
end msgg_session;
/
Test:
begin
msgg_session.authenticate('ndubizuacn', 'Kittens');
dbms_output.put_line(msgg_session.get_user_id);
end;
/
Assuming dbms_output is enabled, this prints the value 1.
Using a global variable for something like this doesn't make a great interface, but it's a requirement of the assignment so I guess it shows how to use one. Same goes for needing to make two calls - perhaps you could expand your authenticated_user function to provide an alternative interface (pass in user and password, get back user_id all in one shot).
Storing passwords in plain text is an obvious security risk, and it is sometimes said that you should never use any online service that can send you your password if you forget it (you don't see that too often these days, but it used to be quite common). It would be more secure not to store the password at all but instead store ora_hash(upper(username)||'~'||password)), so for example for username ndubizuacn and password Kittens you would store 2160931220. Then your authentication function might be something like:
function authenticated_user
( username_to_auth in user_password.username%type
, password_to_use in user_password.password%type )
return user_password.user_id%type
is
l_user_id user_password.user_id%type;
begin
select user_id into l_user_id
from user_password
where username = username_to_auth
and password_hash = ora_hash(upper(username_to_auth)||'~'||password_to_use);
return l_user_id;
exception
when no_data_found then
raise_application_error(-20000, 'Not authenticated');
end authenticated_user;

User-Defined Types (PL/SQL) in Oracle Forms

I'm using PL/SQL, and I've recently found out that you can do OOP with it.
The thing is that I've created an Object Type on database level with, like this:
CREATE OR REPLACE TYPE customer AS OBJECT
(
customer_id NUMBER(10)
,customer_name VARCHAR2(30)
,customer_last VARCHAR2(30)
,constructor function customer(p_id NUMBER)
RETURN SELF AS RESULT
,member procedure display
)
And the type's body:
CREATE OR REPLACE TYPE BODY customer AS
constructor function customer(p_id NUMBER)
RETURN SELF AS RESULT
AS
BEGIN
SELECT client_id,
client_name,
client_last
INTO self.customer_id,
self.customer_name,
self.customer_last
FROM clients
WHERE client_id = p_id;
RETURN;
END;
member procedure display IS
BEGIN
dbms_output.put_line('Name: ' || customer_name||' '|| customer_last);
END;
END;
This compiles great and has no problems.
The thing is when I try to use this new type on Oracle Forms.
I have a very simple form, with just a textbox to put the client_id, and a button to search for the customer (it uses the constructor function from the customer type to create a new instance and return it), and another textbox where it displays the customer name. When pressed it executes a Program Unit that goes like this:
PROCEDURE search_client IS
client customer;
BEGIN
IF :CLIENT.ID_CLIENT IS NOT NULL THEN
client := customer(p_id => :CLIENT.ID_CLIENT);
:CLIENT.NAME := client.customer_name;
END IF;
END;
When I try to compile it, I'm getting this errors:
Error 0 in Line 3, column 11
Item ignored
and a few more errors like that.
I've read somewhere that client side PLSQL does not support this kind of defined types. Is that true? Or there's any other mistakes that I'm no seen here.
By the way I'm using Oracle Forms 11g, and an Oracle 10g Data base.

PL/SQL trigger doesn't work properly

my sql trigger throws an error when added login exists in system but if not, no row is inserted.
Can somebody tell, why?
create or replace trigger user_login_exist_validator
before insert on USERS
for each row
declare
login varchar2(32 char) := :new.USER_LOGIN;
login_exists number(1,0) := 0;
begin
select 1 into login_exists from USERS where USER_LOGIN=login;
if login_exists > 0
then
RAISE_APPLICATION_ERROR(-20666, 'Użytkownik ' || login || ' już istnieje w systemie!!!');
end if;
end;
Looks like a mutating table error.
You are trying to read the same database table while you're already in the act of modifying data in it.
It might be better to take away the trigger. You can catch the exception and handle it where you do the insert statement.
https://docs.oracle.com/cd/B13789_01/appdev.101/b10807/07_errs.htm

Execute a stored procedure in oracle

I need to get the output in uu in accordance with value passed through the prompt
create or replace procedure chklg( uu out logn.username%TYPE
, pass in logn.password%TYPE)
is
begin
select username into uu from logn where password=pass;
end;
I tried executing the above procedure this way:
begin
chklg(:pass);
end
By definition a procedure doesn't return anything. You're looking for a function.
create or replace function chklg ( p_pass in logn.password%TYPE
) return varchar2 is -- assuming that logn.username%TYP is a varchar2
l_uu logn.username%type;
begin
select username into l_uu from logn where password = p_pass;
return l_uu;
-- If there-s no username that matches the password return null.
exception when no_data_found then
return null;
end;
I'm slightly worried by this as it appears as though you're storing a password as plain text. This is not best practice.
You should be storing a salted and peppered hash of your password next to the username, then apply the same salting, peppering and hashing to the password and select the hash from the database.
You can execute the function either of the following two ways:
select chklg(:pass) from dual
or
declare
l_pass logn.password%type;
begin
l_pass := chklg(:pass);
end;
/
To be complete Frank Schmitt has raised a very valid point in the comments. In addition to you storing the passwords in a very dangerous manner what happens if two users have the same password?
You will get a TOO_MANY_ROWS exception raised in your SELECT INTO .... This means that too many rows are returned to the variable. It would be better if you passed the username in as well.
This could make your function look something like the following
create or replace function chklg (
p_password_hash in logn.password%type
, p_username in logn.username%type
) return number
/* Authenticate a user, return 1/0 depending on whether they have
entered the correct password.
*/
l_yes number := 0;
begin
-- Assumes that username is unique.
select 1 into l_yes
from logn
where password_hash = p_password_hash
and username = p_username;
return l_yes;
-- If there-s no username that matches the password return 0.
exception when no_data_found then
return 0;
end;
If you're looking to only use a procedure (there's no real reason to do this at all as it unnecessarily restricts you; you're not doing any DML) then you can get the output parameter but you have to give the procedure a parameter that it can populate.
In your case it would look something like this.
declare
l_uu logn.username%type;
begin
chklg(l_uu, :pass);
dbms_output.put_line(l_uu);
end;

How to return a record from an existing table from an Oracle PL/SQL function?

I know it seems like a basic thing, but I've never done this before.
I'd like to return a single record from an existing table as the result of an Oracle PL/SQL function. I've found a few different ways of doing this already, but I'm interested in the best way to do it (read: I'm not all that happy with what I've found).
The jist of what I am doing is this... I have a table called 'users', and I want a function 'update_and_get_user' which given a UserName (as well as other trusted information about said user) will potentially perform various actions on the 'users' table, and then return either zero or one row/record from said table.
This is the basic outline of the code in my head at the moment (read: no idea if syntax is even close to correct):
CREATE FUNCTION update_and_get_user(UserName in VARCHAR2, OtherStuff in VARCHAR2)
RETURN users PIPELINED IS
TYPE ref0 IS REF CURSOR;
cur0 ref0;
output_rec users%ROWTYPE;
BEGIN
-- Do stuff
-- Return the row (or nothing)
OPEN cur0 FOR 'SELECT * FROM users WHERE username = :1'
USING UserName;
LOOP
FETCH cur0 INTO output_rec;
EXIT WHEN cur0%NOTFOUND;
PIPE ROW(output_rec);
END LOOP;
END update_and_get_user;
I've seen examples where a record or table is returned, the type of record or table having been created / declared beforehand, but it seems like if the table has already been defined, I should be able to utilize that, and thus not have to worry about syncing the type declaration code if table changes are ever made.
I'm open to all potential solutions and commentary, but I do really want to keep this in a single PL/SQL function (as opposed to code in some other language / framework that communicates with the database multiple times, finishing with some form of 'SELECT * FROM users WHERE username=blah') as the system calling the function and the database itself may be different cities. Outside of that limit, I'm open to changing my thinking.
This is how I would do it. Variables/table-names/column-names are case-insensitive in Oracle, so I would use user_name instead of UserName.
CREATE TABLE users( UserName varchar2(20), OtherStuff VARCHAR2(20) );
Function update_and_get_user. Note that I return a ROWTYPE instead of Pipelined Tables.
CREATE OR REPLACE FUNCTION update_and_get_user(
in_UserName IN users.UserName%TYPE,
in_OtherStuff IN users.OtherStuff%TYPE )
RETURN users%ROWTYPE
IS
output_rec users%ROWTYPE;
BEGIN
UPDATE users
SET OtherStuff = in_OtherStuff
WHERE UserName = in_UserName
RETURNING UserName, OtherStuff
INTO output_rec;
RETURN output_rec;
END update_and_get_user;
And this is how you would call it. You can not check a ROWTYPE to be NULL, but you can check username for example.
DECLARE
users_rec users%ROWTYPE;
BEGIN
users_rec := update_and_get_user('user', 'stuff');
IF( users_rec.username IS NOT NULL ) THEN
dbms_output.put_line('FOUND: ' || users_rec.otherstuff);
END IF;
END;
A solution using PIPED ROWS is below, but it doesn't work that way. You can not update tables inside a query.
SELECT * FROM TABLE(update_and_get_user('user', 'stuff'))
ORA-14551: cannot perform a DML operation inside a query
Solution would look like that:
CREATE OR REPLACE TYPE users_type
AS OBJECT
(
username VARCHAR2(20),
otherstuff VARCHAR2(20)
)
CREATE OR REPLACE TYPE users_tab
AS TABLE OF users_type;
CREATE OR REPLACE FUNCTION update_and_get_user(
in_UserName IN users.username%TYPE,
in_OtherStuff IN users.otherstuff%TYPE )
RETURN users_tab PIPELINED
IS
output_rec users%ROWTYPE;
BEGIN
UPDATE users
SET OtherStuff = in_OtherStuff
WHERE UserName = in_UserName
RETURNING username, otherstuff
INTO output_rec;
PIPE ROW(users_type(output_rec.username, output_rec.otherstuff));
END;

Resources