How to add a new user in a PL/SQL table using a function? - oracle

I am fairly new to PL/SQL and one of the doubts I have is to create and declare functions.
Specifically this one:
Create a function that creates a new user:
Use a sequence togive the new User a new ID
Pass the name, address, etc... as IN arguments
Return as OUT arguments the ID created and a O_ERROR_MESSAGE
Function returns TRUE if the user is added, otherwise returns FALSE
Handle exceptions
Create a PL/SQL block and test the created function
This is my code so far:
CREATE OR REPLACE FUNCTION DSB_ADD_NEW_USERS (I_NAME IN VARCHAR2,
I_ADDRESS IN VARCHAR2,
I_BIRTHDATE IN DATE,
I_COUNTRY IN VARCHAR2)
RETURN NUMBER IS
O_ERROR_MESSAGE EXCEPTION;
CURRENT_USER NUMBER;
BEGIN
DSB_NB_SEQ_USER_ID.NEXTVAL;
SELECT COUNT(USER_ID) INTO CURRENT_USER
FROM DSB_NB_USERS;
WHILE CURRENT_USER != 0
LOOP
DSB_NB_SEQ_USER_ID.NEXTVAL;
SELECT COUNT(USER_ID) INTO CURRENT_USER
FROM DSB_NB_USERS;
END LOOP;
INSERT INTO DSB_NB_USERS (USER_ID, NAME, ADDRESS, BIRTHDATE, COUNTRY_ID_FK) VALUES (CURRENT_USER, I_NAME, I_ADDRESS, TO_DATE('I_BIRTHDATE', 'DD/MM/YYYY'), I_COUNTRY);
RETURN CURRET_USER;
EXCEPTION
WHEN O_ERROR_MESSAGE THEN
RETURN NULL;
WHEN OTHERS THEN
RETURN NULL;
END;
DECLARE
I_NEW_USER NUMBER;
BEGIN
I_NEW_USER := DSB_ADD_NEW_USERS(I_NAME => 'Arnaldo Amaral',
I_ADDRESS => 'Rua da Agra',
I_BIRTHDATE => '03/05/1959',
I_COUNTRY => 'PT');
END;
Am i too far from the truth?
I know there's still a lot to add.
Thank you for the help!!!

It seems to me you are asking for a lifeline, that needs more than just a code response. First off look at the answer by
#Tejash. His solution is a far cry from what you have, and is correct for your function definition (although that is itself incorrect given your listed requirements.) Also, #EdStevens is correct that is is a very poor use of a function. However a function is your assignment's requirement.
So lets dissect your code and see how it satisfies each of your requirements. (I cannot stand all caps, so I'm lower casing it.)
Fails. While you have a sequence it is used improperly. You can not
just code a standalone name. It must be used in either a SQL
statement or an assignment statement.
Passes.
Fails. Your function passes the IN parameters correctly. However
there are no OUT parameters.
Your function returns Number not the required True/False which
requires either Boolean or varchar2 for literal.
Code section for requirements 3,4.
create or replace function dsb_add_new_users (i_name in varchar2,
i_address in varchar2,
i_birthdate in date,
i_country in varchar2) <<< where are the 2 required out parameters
return number is <<< Should be True or False so either Boolean or varchar2
o_error_message exception; <<< Should be an Out parameter not an exception
Yes you have the Exception Section, which is required for handling
exceptions. However you do not handle exceptions so much as suppress
them, indicating to Oracle, and to the calling routine, that they didn't actually happen.
exception
when o_error_message then <<< as coded this is a developer defined exception, yet is not raised so it cannot happen.
return null; <<< even if it were raised you have thrown it away, the calling routine will never it happened
when others then <<< One of the worst pieces of code that can be written. I tend to remember seeing a discussion in
return null; <<< Oracle that would make this a compile time error. To bad the supporters lost the argument.
Passed, mostly. Correct for posted code, but insufficient per requirements.
So all-in-all not highly successful when viewed against the requirements.
A couple other items not specific the requirements, but disaster to successful implementation.
While technically you might get away with the following it is very bad practice. Current_User is an Oracle reserved word.
current_user number; <<< NO, NO bad verifiable name.
The following completely decimates your function. It creates a never ending loop. Well not quite -- it runs 1 time, but never afterward.
I believe you intended for the current_user variable to contain the ID assigned to the user. But it actually contains the number of rows in the table. Further what would the number of rows (count) have to do with Inserting a row? SO what does the code actually Do?
dsb_nb_seq_user_id.nextval; <<< systax error.
select count(user_id) into current_user <<< count the number of rows in the table
from dsb_nb_users;
while current_user != 0 <<< if there are any rows in the table
loop
dsb_nb_seq_user_id.nextval; <<< syntax error
select count(user_id) into current_user <<< try counting then again until there are none.
from dsb_nb_users;
end loop;
So correcting for the above what do we wind up with. I hesitate here in fear you will just copy the below and submit it. Please don't do that but study and understand what it's doing, then rewrite you routine. But after tearing you initial effort up, I guess I owe a corrected solution.
create or replace function dsb_add_new_users
( i_name in varchar2
, i_address in varchar2
, i_birthdate in date
, i_country in varchar2
, o_new_user_id out number
, o_error_message out varchar2
return boolean
is
country_id_l number;
begin
-- initialize out variables
o_new_user_id := null;
o_error_message := null;
-- get country id from input parameter
select country_id
into country_id_l
from country
where name = i_country;
-- create user and get the assigned user_id
insert into dsb_nb_users (user_id, name, address, birthdate, country_id_fk)
values dsb_nb_seq_user_id.nextval
, i_name
, i_address
, i_birthdate
, country_id_l
)
returning user_id
into o_new_user_id;
return true;
exception
when no_date_found then
o_output_message = 'ERROR: Specified country name ''' || country_id_l || ''' Not Found.';
return false;
when others then
o_output_message := 'ERROR: ' || sqlerrm;
return false
end dsb_add_new_users ;
--------------- Test Driver ----------------
declare
i_new_user number;
new_user_created boolean;
error_message varchar2(255);
begin
new_user_created := dsb_add_new_users( i_name => 'Arnaldo Amaral',
, i_address => 'Rua da Agra'
, i_birthdate => to_date('03/05/1959','mm/dd/yyyy') -- or is it 'dd/mm/yyyy'
, i_country => 'PT'
, o_new_user_id => i_new_user
, o_error_message => error_message);
if not new_user_created
then
dbms_output.put_line (error_messag);
end if ;
end;
Disclaimer: As you did not post table DDL nor test data the routine has not been tested nor even compiled. Any syntax error(s) is for you to resolve. Further, I follow Tejash in the assumption you actually have a countries table and there is a FK to it. That may be an erroneous assumption.

Why don't you directly use the sequence value to insert new user as follows:
CREATE OR REPLACE FUNCTION DSB_ADD_NEW_USERS (
I_NAME IN VARCHAR2,
I_ADDRESS IN VARCHAR2,
I_BIRTHDATE IN DATE,
I_COUNTRY IN VARCHAR2
) RETURN NUMBER IS
CURRENT_USER_ID NUMBER;
BEGIN
CURRENT_USER_ID := DSB_NB_SEQ_USER_ID.NEXTVAL; -- returns new and unique number
INSERT INTO DSB_NB_USERS (
USER_ID,
NAME,
ADDRESS,
BIRTHDATE,
COUNTRY_ID_FK
) VALUES (
CURRENT_USER_ID,
I_NAME,
I_ADDRESS,
I_BIRTHDATE,
(
SELECT
COUNTRY_ID
FROM
COUNTRIES
WHERE
NAME = I_COUNTRY
) -- it is FK so need to use SELECT sub-query to find COUNTRY_ID
);
RETURN CURRENT_USER_ID; -- returning the new user ID
EXCEPTION
WHEN OTHERS THEN
RETURN -1; -- returning -1 in case there is any error
END;
-1 will be returned when there is an error while adding the new user.

Came up with this much simpler answer after asking to a few people for help.
create or replace FUNCTION DSB_ADD_NEW_USERS (I_NAME IN VARCHAR2,
I_ADDRESS IN VARCHAR2,
I_BIRTHDATE IN DATE,
I_COUNTRY IN VARCHAR2)
RETURN NUMBER IS
CURRENT_USER NUMBER;
BEGIN
CURRENT_USER := DSB_NB_SEQ_USER_ID.NEXTVAL;
DBMS_OUTPUT.PUT_LINE('Hello World');
INSERT INTO DSB_NB_USERS (USER_ID, NAME, ADDRESS, BIRTHDATE, COUNTRY_ID_FK) VALUES (CURRENT_USER, I_NAME, I_ADDRESS, TO_DATE(I_BIRTHDATE, 'DD/MM/YYYY'), I_COUNTRY);
RETURN CURRENT_USER;
EXCEPTION
WHEN OTHERS THEN
RETURN -1;
END;
SET SERVEROUT ON
DECLARE
I_NEW_USER NUMBER;
BEGIN
I_NEW_USER := DSB_ADD_NEW_USERS(I_NAME => 'Arnaldo Amaral',
I_ADDRESS => 'Rua da Agra',
I_BIRTHDATE => '03/MAY/1959',
I_COUNTRY => 'PT');
commit;
END;
/
Thank you all!!!

Related

What do these errors mean and how do you suggest I fix them?

I am new to learning SQL and currently learning it in class. I am trying to write a code that fulfills the following requirements:
Make a reservation: Input parameters: Hotel, guest’s name, start date, end dates, room type, date of reservation. Output: reservation ID. NOTE: Only one guest per reservation. However, the same guest can make multiple reservations.
Find a reservation: Input is the guest’s name and date, hotel ID. The output is reservation ID
I am still somewhat new to deciphering error codes and tried to look up what they mean. However, I'm still not quite sure why my code is wrong.
CREATE OR REPLACE PACKAGE hotelmanagement AS
FUNCTION make(rsrv_id VARCHAR2
,hotel_name VARCHAR2
,guest VARCHAR2
,start_date VARCHAR2
,end_date VARCHAR2
,room_type VARCHAR2
,rsrv_date VARCHAR2)
RETURN NUMBER IS
rsrv_id NUMBER;
BEGIN
SELECT rsrv_seq.nextval INTO reserve_id FROM dual;
INSERT INTO reservations
VALUES
(reserve_id, 'Four Seasons', 'Amanda', 'July-30-2019', 'Aug-8-2019',
'King', 'July-18-2019');
tot_rsrv := tot_rsrv + 1;
RETURN(rsrv_id);
END;
FUNCTION find(guest VARCHAR2
,rsrv_date VARCHAR2)
RETURN NUMBER IS
rsrv_id NUMBER;
BEGIN
SELECT rsrv_id
INTO guest
FROM reservations
WHERE rsrv_date = find_rsrv_date;
END;
RETURN(rsrv_id);
END hotelmanagement;
I have these error messages(two of them look the same?):
Error(4,1): PLS-00103: Encountered the symbol "SELECT" when expecting one of the following: begin function pragma procedure subtype type current cursor delete exists prior The symbol "begin" was substituted for "SELECT" to continue.
Error(5,1): PLS-00103: Encountered the symbol "RSRV_ID" when expecting one of the following: language
Error(5,1): PLS-00103: Encountered the symbol "RSRV_ID" when expecting one of the following: language
The requirements are a quite vague, so it is difficult to provide the desired procedure. Anyway, your code has some weak items.
Your procedure has several input parameters but you don't use them inside the procedure.
Use appropriate data types, i.e. never(!) store DATE or number values in string, i.e. VARCHAR2. Use VARCHAR2 only for string data. At least you use four digit year, which is the proper way of doing it.
RETURN command does not use brackets.
Taking all this into account your code should be more or less like this:
CREATE OR REPLACE PACKAGE BODY hotelmanagement AS
FUNCTION make( -- you can't declare variable "rsrv_id" twice
hotel_name IN VARCHAR2
,guest IN VARCHAR2
,start_date IN DATE
,end_date IN DATE
,room_type IN VARCHAR2
,rsrv_date IN DATE)
RETURN NUMBER IS
rsrv_id NUMBER;
BEGIN
INSERT INTO reservations
VALUES
(rsrv_seq.nextval, hotel_name, guest, start_date, end_date,
room_type, rsrv_date)
RETURNING reserve_id INTO rsrv_id ;
-- tot_rsrv := tot_rsrv + 1; -> I don't see any use for it, variable tot_rsrv is not declared
RETURN rsrv_id;
END;
FUNCTION find(v_guest IN VARCHAR2
,rsrv_date IN DATE) RETURN NUMBER IS
rsrv_id NUMBER;
BEGIN
SELECT rsrv_id
INTO rsrv_id
FROM reservations
WHERE rsrv_date = find_rsrv_date
and guest = v_guest; -- don't use "guest = guest" because this will select all rows.
RETURN rsrv_id;
END;
END hotelmanagement;

Optimizing a simple procedure

Below is my procedure, takes 51 sec to execute, I want to return cursor only if one count is found, in case anything else will return message and cursor as null. In case cursor is found message as null..
I am first taking count by query and populating data by the same query later but only in case of count being one.
is their anyway in which this could be optimized in terms of time.?
create or replace PROCEDURE sp_cp_getcrnnofrmmobdob(P_MobileNo IN VARCHAR2,
P_Dob IN VARCHAR2,
p_Output out SYS_REFCURSOR,
p_Message OUT VARCHAR2) IS
vCRN Varchar2(50) := '';
vCustid varchar2(50) := '';
vMobno varchar2(50) := '';
vCustname varchar2(400) := '';
vCustDob varchar2(50) := '';
vcount int := 0;
BEGIN
p_Message := '';
OPEN p_Output FOR
select 1 from dual;
Select count(*)
into vcount
FROM (select distinct(C.fw_customer_id_c) crn,
C.Cust_Id_n custid,
c.customername custname,
c.dob custdob,
A.MOBILE mobileno
from FCH_CASTRANSACTION.NBFC_CUSTOMER_M C,
FCH_CASMASTER.nbfc_address_m A
where A.BPID = C.Cust_Id_n and
A.mobile = P_MobileNo and
TO_CHAR(TO_DATE(C.DOB, 'DD-MON-YY'),'DD-MON-YY')=TO_CHAR(TO_DATE(P_Dob,'DD/MM/YYYY'),'DD-MON-YY'));
if (vcount = 1) then
select B.crn,
B.custid,
B.mobileno,
B.custname,
B.custdob
into vCRN, vCustid, vMobno, vCustname, vCustDob
from (select distinct(C.fw_customer_id_c) crn,
C.Cust_Id_n custid,
c.customername custname,
c.dob custdob,
A.MOBILE mobileno
from FCH_CASTRANSACTION.NBFC_CUSTOMER_M C,
FCH_CASMASTER.nbfc_address_m A
where A.BPID = C.Cust_Id_n and
A.mobile = P_MobileNo and
TO_CHAR(TO_DATE(C.DOB, 'DD-MON-YY'),'DD-MON-YY')=TO_CHAR(TO_DATE(P_Dob,'DD/MM/YYYY'),'DD-MON-YY')) B;
if ((vCRN = '') OR (vCRN IS Null)) then
p_Message := 'No data found for entered details';
else
if ((vMobno <> P_MobileNo) OR (vMobno IS Null)) then
p_Message := 'Entered mobile number is not registered with us.Please contact customer care.';
else
if ((vCustDob <> TO_CHAR(TO_DATE(P_Dob,'DD/MM/YYYY'),'DD-MON-YY')) OR (vCustDob IS Null)) then
p_Message := 'Entered date of birth is not registered with us.Please contact customer care.';
else
OPEN p_Output FOR
select vCRN as "CrnNum", vCustid as "CustId", vMobno as "MobNo", vCustname as "CustName", vCustDob as "CustDob"
from dual;
End if;
End if;
End if;
else
p_Message := 'Inconsistent details for entered data found. Please contact customer care';
End if;
EXCEPTION
WHEN NO_DATA_FOUND THEN
p_Message := 'Unable to process your request.Please contact customer care.';
OPEN p_Output FOR
SELECT 1 FROM dual;
END;
Would really appreciate if someone can help.
You can just use one SELECT ... INTO ... and catch the exception TOO_MANY_ROWS.
'' and NULL are the same thing.
It will not return a row if the mobile number does not match (or is null) so that check is redundant.
Same for the date of birth check.
DISTINCT is NOT a function - it is a keyword that applies to all the rows.
You assigning a cursor to p_output twice. Also, some systems may not like that the function can return different numbers of columns to your cursor.
So, something like this:
create or replace PROCEDURE sp_cp_getcrnnofrmmobdob(
P_MobileNo IN VARCHAR2,
P_Dob IN VARCHAR2,
p_Output out SYS_REFCURSOR,
p_Message OUT VARCHAR2
)
IS
v_dob DATE := TO_DATE( p_dob, 'DD/MM/YYYY' );
vCRN FCH_CASTRANSACTION.NBFC_CUSTOMER_M.fw_customer_id_c%TYPE;
vCustid FCH_CASTRANSACTION.NBFC_CUSTOMER_M.Cust_Id_n%TYPE;
vMobno FCH_CASMASTER.nbfc_address_m.MOBILE%TYPE;
vCustname FCH_CASTRANSACTION.NBFC_CUSTOMER_M.customername%TYPE;
vCustDob FCH_CASTRANSACTION.NBFC_CUSTOMER_M.dob%TYPE;
BEGIN
p_Message := '';
select distinct
C.fw_customer_id_c,
C.Cust_Id_n,
c.customername,
c.dob,
A.MOBILE
into vCRN, vCustid, vMobno, vCustname, vCustDob
from FCH_CASTRANSACTION.NBFC_CUSTOMER_M C
INNER JOIN FCH_CASMASTER.nbfc_address_m A
ON ( A.BPID = C.Cust_Id_n )
WHERE A.mobile = P_MobileNo
AND TO_DATE( C.DOB, 'DD-MON-YY') = v_dob;
IF vCRN IS NULL THEN
p_Message := 'No data found for entered details';
OPEN p_Output FOR
select 1 from dual;
RETURN;
END IF;
OPEN p_Output FOR
select vCRN as "CrnNum", vCustid as "CustId", vMobno as "MobNo", vCustname as "CustName", vCustDob as "CustDob"
from dual;
EXCEPTION
WHEN NO_DATA_FOUND THEN
p_Message := 'Unable to process your request.Please contact customer care.';
OPEN p_Output FOR
SELECT 1 FROM dual;
WHEN TOO_MANY_ROWS THEN
p_Message := 'Inconsistent details for entered data found. Please contact customer care';
OPEN p_Output FOR
SELECT 1 FROM dual;
END;
Looking at TO_CHAR(TO_DATE(C.DOB, 'DD-MON-YY'),'DD-MON-YY')=TO_CHAR(TO_DATE(P_Dob,'DD/MM/YYYY'),'DD-MON-YY')) in both of your queries, I suggest.
Based on this logic, you are storing the date of birth (dob) as a string. It is a shame if you do, it should be in the database as a DATE.
You are converting the strings to a date and then back to a string. In Oracle, you can compare dates, so only convert from column to DATE, not again back to a string. Such as TO_DATE(column,'column format')=TO_DATE(variable,'variable format')
Or, better yet, for your data model, consider just converting the variable input date string to match the column string format. Like column = TO_CHAR(TO_DATE(variable, 'variable format'),'column format'). There are two possible advantages here. First, the conversion will only happen once for the supplied value, but the query never has to perform a function on the column value. If the table is big. Also, since there is no function to be performed on the column value, IF there is an index on this value, the optimizer can use it (although this may not help your example based on my guess on your data model). Performance improvement here would depend on how the dob is used by your query. If Oracle is lookup up records by mobile number, then filter by dob, it shouldn't make much difference, but if it goes the other way around, looking up by dob then filtering on mobile number, this could help immensely.
tl;dr Store dates as dates, compare dates as dates, avoid functions on the column values when possible

How can implement a procedure in ESQL (an internal procedure) from Oracle database

I create a store procedure in Oracle db that insert Customer to my Table
this is my code :
CREATE OR REPLACE PROCEDURE THP.INSERT_CUSTOMER(
P_CUSTNAME IN VARCHAR2,
P_CUSTLAST IN VARCHAR2,
P_CUSTFATHER IN VARCHAR2,
P_NATIONNO IN NUMBER,
P_BIRTHDAY IN VARCHAR2,
P_BIRHTPLACE IN VARCHAR2,
P_EMAIL IN VARCHAR2,
P_CUSTENAME IN VARCHAR2,
P_CUSTELAST IN VARCHAR2,
P_OWNID IN NUMBER,
P_CUSTTYPEID IN NUMBER,
P_GENDERID IN NUMBER,
P_BILLSTID IN NUMBER,
P_BILLSPID IN NUMBER,
P_IDNO IN varchar2,
RESULT OUT INTEGER) IS
CNT NUMBER;
BEGIN
RESULT := 1;
CNT := 0;
SELECT COUNT(1) INTO CNT FROM THP.TBCUSTOMER WHERE NATIONNO=P_NATIONNO ;
IF CNT=1 THEN
COMMIT;
RESULT := 1; --IF RECORD is EXIST
ELSE
BEGIN
INSERT INTO TBCUSTOMER(CUSTID,CUSTNAME,CUSTLAST,CUSTFATHER,NATIONNO,BIRTHDAY,BIRHTPLACE,EMAIL,CUSTENAME,CUSTELAST,OWNID,CUSTTYPEID,GENDERID,BILLSTID,BILLSPID,IDNO)
VALUES(CUSTID_SEQ.NEXTVAL,P_CUSTNAME,P_CUSTLAST,P_CUSTFATHER,P_NATIONNO,P_BIRTHDAY,P_BIRHTPLACE,P_EMAIL,P_CUSTENAME,P_CUSTELAST,P_OWNID,P_CUSTTYPEID,P_GENDERID,P_BILLSTID,P_BILLSPID,P_IDNO);
COMMIT;
RESULT :=0; --IF INSERT NEW COLUMN
END;
END IF;
END INSERT_CUSTOMER;
/
now I want use this procedure in ESQL and create it directly in ESQL not CALL it from Oracle database or other DB
would you please guide me a bout it...
General comments, not an answer ...
count(1)
count(1) = count(*), which is the standard form for "count the number of rows". count(1) has no advantages, so best to use count(*).
RESULT := 1
is redundant at the beginning of the procedure
CNT := 0
... also redundant. The variable name is not very meaningful, and might make people think of a rude word, so perhaps change it to rows_found.
Prefixing the arguments with P_ is not required. If you use one of them in a SQL statement and need to deconflict it from a database object name then prefix it with the procedure name, so you have:
WHERE NATIONNO= INSERT_CUSTOMER.NATIONNO
Is NATIONNO constrained to be unique in the customer table? If not, use:
SELECT COUNT(*)
INTO CNT
FROM THP.TBCUSTOMER
WHERE NATIONNO=INSERT_CUSTOMER.NATIONNO AND
ROWNUM = 1;
(12g will introduce the LIMIT SQL syntax, by the way).
Commiting in a procedure is often held to be bad practice, as the procedure often becomes part of a longer business transaction (eg. inserting a new customer and address) and the commit should be controlled by the application code.
Upper case code is harder to read than lower case -- that's why direction signs on motorways are not uppercase.
The begin-end block for the insert is not required at all.
"birhtplace" is spelled wrong.
So I'd suggest that what you want to convert to ESQL is actually:
create or replace procedure thp.insert_customer(
custname in varchar2,
custlast in varchar2,
custfather in varchar2,
nationno in number ,
birthday in varchar2,
birhtplace in varchar2,
email in varchar2,
custename in varchar2,
custelast in varchar2,
ownid in number ,
custtypeid in number ,
genderid in number ,
billstid in number ,
billspid in number ,
idno in varchar2,
result out integer) is
rows_found number;
begin
select count(*)
into rows_found
from thp.tbcustomer
where nationno=insert_customer.nationno;
if rows_found = 1 then
result := 1;
else
insert into
tbcustomer(
custid ,
custname ,
custlast ,
custfather,
nationno ,
birthday ,
birthplace,
email ,
custename ,
custelast ,
ownid ,
custtypeid,
genderid ,
billstid ,
billspid ,
idno)
values(
custid_seq.nextval,
custname ,
custlast ,
custfather,
nationno ,
birthday ,
birthplace,
email ,
custename ,
custelast ,
ownid ,
custtypeid,
genderid ,
billstid ,
billspid ,
idno);
result :=0;
end if;
end insert_customer;
/
To call an external procedure via ESQL, you need to first declare the external function.
Do it as below:
CREATE PROCEDURE DoSomething (IN in1 CHARACTER, OUT out1 CHARACTER, OUT out2 CHARACTER)
LANGUAGE DATABASE
EXTERNAL NAME "DoSomething";
Now you can call this function from your ESQL function as below:
CALL DoSomething(in,out1,out2) IN Database.yourschema.yourDB;
P.S. The parameters you are passing must be compatible with the parameters of your stored procedure.

Calling a function within a procedure

I apologize ahead of time for the probably basic question. I am student and it is crunch time!
I am using Oracle 10g Express.
I created a function:
create or replace FUNCTION test_glaccounts_description
(
account_description_param VARCHAR2
)
RETURN NUMBER
AS
description_dup_var NUMBER;
BEGIN
SELECT 1
INTO description_dup_var
FROM general_ledger_accounts
WHERE account_description = account_description_param;
RETURN description_dup_var;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN 0;
END;
And would like to use that function in a procedure.
I've tried:
PROCEDURE insert_gla_with_test
(
account_number_param NUMBER,
account_description_param VARCHAR2
)
AS
BEGIN
IF test_glaccounts_description = 1 THEN
INSERT INTO general_ledger_accounts
VALUES (account_number_param, account_description_param);
ELSE raise_application_error (-20001, 'Duplicate account description');
END IF;
END;
But it doesn't like the "test_gla_accounts" line.....what am I doing wrong?
To my understanding, the function returns a value of 1 or 0, in the procedure, if the function returned a 1, I would like the param fields added to the table. If the function returned a 0, I would like the procedure to raise the error.
test_glaccounts_description takes a parameter (account_description_param) and returns a NUMBER. In order to call the function, therefore, you need to pass in a parameter. Assuming that you want to pass in the account_description_param that is passed in to the insert_gla_with_test procedure
CREATE OR REPLACE PROCEDURE insert_gla_with_test
(
account_number_param NUMBER,
account_description_param VARCHAR2
)
AS
BEGIN
IF test_glaccounts_description( account_description_param ) = 1 THEN
INSERT INTO general_ledger_accounts
VALUES (account_number_param, account_description_param);
ELSE
raise_application_error (-20001, 'Duplicate account description');
END IF;
END;

Compilation error in Apex package

I have created one package in oracle apex. While compiling i got an error msg like this..
please help me.
create or replace package body "PKG_APP_SECURITY" is
procedure ADD_USER(P_PASSWORD IN VARCHAR2
,P_USERNAME IN VARCHAR2
)
as
begin
INSERT INTO P_USERS(username, password)
VALUES (UPPER (p_username),get_hash(TRIM(p_username), p_password));
COMMIT;
EXCEPTION
WHEN OTHERS THEN ROLLBACK; RAISE;
end ADD_USER;
function VALID_USER(P_PASSWORD IN VARCHAR2
,P_USERNAME IN VARCHAR2
) return BOOLEAN
as
begin
VALID_USER2(UPPER(p_username),p_password);
RETURN TRUE;
EXCEPTION
WHEN OTHERS THEN RETURN FALSE;
end VALID_USER;
function GET_HASH(P_PASSWORD IN VARCHAR2
,P_USERNAME IN VARCHAR2
) RETURN VARCHAR2 AS
BEGIN
RETURN
DBMS_OBFUSCATION_TOOLKIT.md5(input_string => UPPER (p_username) || '/' || UPPER (p_password));
end GET_HASH;
procedure LOGIN(P_FLOW_PAGE IN VARCHAR2
,P_PASSWORD IN VARCHAR2
,P_SESSION_ID IN VARCHAR2
,P_USERNAME IN VARCHAR2
)
is
begin
-- THIS PROVIDES AUTHENTICATION
wwv_flow_custom_auth_std.login
(p_uname => p_uname
,p_password => p_password
,p_session_id => p_session_id
,p_flow_page => p_flow_page || ':' || 1);
end LOGIN;
procedure VALID_USER2( P_PASSWORD IN VARCHAR2
,P_USERNAME IN VARCHAR2
)
as
begin
SELECT '1'
INTO v_dummy
FROM P_USERS
WHERE UPPER(username) = UPPER (p_username)
AND password= get_hash (p_username, p_password);
EXCEPTION
WHEN NO_DATA_FOUND THEN
raise_application_error(-20000, 'Invalid username / password.');
end VALID_USER2;
end "PKG_APP_SECURITY";​
An educated guess. The most likely source of a compilation error is the call to VALID_USER2() in the VALID_USER() procedure. If, as I suspect, VALID_USER2() is not declared in the package spec this will hurl a PLS-00313 exception, "not declared in this scope".
Private functions must be declared before they are invoked. The alternate is forward declaration, but that has always struck me as unnecessary duplication.
There are a number of things which are troubling about your implementation. In ascending order of severity:
Those calls to UPPER() and TRIM() in
the calls to GET_HASH() are
misplaced. Just use them in the
body of GET_HASH() itself.
The procedure GET_HASH() has a
signature of (P_PASSWORD IN
VARCHAR2,P_USERNAME IN VARCHAR2)
but you always invoke it as
get_hash (p_username, p_password).
Of course, being consistently wrong
means you will get the "right"
result but it's still an error.
The most worrisome thing is that you
appear to be hand-rolling an
authentication scheme instead of
using the built-in Oracle
Application Express Account
Credentials. Why are you doing
that?

Resources