REP-501: Unable to connect to the specified database. - oracle

I need to implement the functionality of generating a report from some parameters that the user introduces through an Oracle Forms application.
The utility is built into a code unit inside the form:
PACKAGE BODY INVOICES_MANAGEMENT IS
-- Generate Invoices Report
PROCEDURE GENERATE_INVOICES_REPORT (
P_REPORTNAME IN VARCHAR2,
P_FORMAT IN VARCHAR2,
P_REPORT_SERVER IN VARCHAR2,
P_MINYEAR IN INTEGER,
P_MAXYEAR IN INTEGER) IS
v_report_id Report_Object;
-- Unique id for each Report request
v_report_server_job VARCHAR2(100);
-- Status of the Report job
v_rep_status VARCHAR2(100);
-- job_id as number only string
v_job_id VARCHAR2(100);
-- Param List
v_tmp_plid ParamList;
v_hidden_action VARCHAR2(200);
BEGIN
v_tmp_plid := Get_Parameter_List('RepData');
IF NOT ID_NULL(v_tmp_plid) THEN
DESTROY_PARAMETER_LIST( v_tmp_plid );
END IF;
v_tmp_plid := CREATE_PARAMETER_LIST ('RepData');
-- Get a handle to the Report Object
v_report_id := FIND_REPORT_OBJECT(P_REPORTNAME);
/* Define the report output format and the name of the Reports Server as well as a user-defined parameter. */
SET_REPORT_OBJECT_PROPERTY(v_report_id, REPORT_DESFORMAT, P_FORMAT);
SET_REPORT_OBJECT_PROPERTY(v_report_id, REPORT_DESTYPE, CACHE);
SET_REPORT_OBJECT_PROPERTY(v_report_id, REPORT_COMM_MODE, SYNCHRONOUS);
SET_REPORT_OBJECT_PROPERTY(v_report_id, REPORT_SERVER, P_REPORT_SERVER);
--SET_REPORT_OBJECT_PROPERTY(v_report_id, REPORT_OTHER, 'MIN_YEAR='||P_MINYEAR||' MAX_YEAR=' || P_MAXYEAR || ' paramform=no');
Add_Parameter(v_tmp_plid, 'MIN_YEAR', TEXT_PARAMETER, P_MINYEAR );
Add_Parameter(v_tmp_plid, 'MAX_YEAR', TEXT_PARAMETER, P_MAXYEAR );
v_hidden_action := 'userid=' ||
--get_application_property(username)
'sergio11' || '/' ||
--get_application_property(password)
'bisite00' || '#' || 'XE';
--get_application_property(connect_string);
MESSAGE('HIDE ACTION -> ' || v_hidden_action);
SET_REPORT_OBJECT_PROPERTY (v_report_id, REPORT_OTHER, v_hidden_action);
-- Run Report
v_report_server_job := RUN_REPORT_OBJECT(v_report_id, v_tmp_plid);
v_job_id := substr(v_report_server_job, instr(v_report_server_job, '_' , -1)+1);
LOOP
v_rep_status := REPORT_OBJECT_STATUS(v_report_server_job);
EXIT WHEN v_rep_status IN ('FINISHED', 'TERMINATED_WITH_ERROR');
END LOOP;
IF v_rep_status = 'FINISHED' THEN
/* Call the Reports output to be displayed in the browser. The URL for relative addressing is valid
only when the Reports Server resides on the same host as the Forms Server and is accessed via the same port.
For accessing a remote Reports environment, you must use a fully qualified URL (i.e. http://hostname:port ) */
WEB.SHOW_DOCUMENT ('/reports/rwservlet/getjobid'|| v_job_id || '?server='|| P_REPORT_SERVER, '_blank');
ELSE
MESSAGE('Report failed with error message '|| v_rep_status);
END IF;
END;
END;
This procedure will be executed when using the "Generate" button, then the user should be able to download the pdf file with the list of invoices.
Next I expose the code for SmartTrigger 'WHEN-BUTTON-PRESSED'
DECLARE
v_minyear INTEGER;
v_maxyear INTEGER;
v_reportname VARCHAR2(30) DEFAULT 'INVOICES_REPORT';
v_format VARCHAR2(10) DEFAULT 'PDF';
v_reportserver VARCHAR2(15) DEFAULT 'rep_ie8win7';
BEGIN
v_minyear := :MIN_YEAR_TEXT;
v_maxyear := :MAX_YEAR_TEXT;
-- Generate Invoice Report
INVOICES_MANAGEMENT.GENERATE_INVOICES_REPORT ( v_reportname, v_format,
v_reportserver, v_minyear, v_maxyear);
END;
The answer is immediate, "FRM - 41214 Unable to run report" is throw with a TERMINATED_WITH_ERRORS state.
When consulting the status of the "job" launched from the Oracle Forms application, I find that the source of the problem is an error "REP-501: Unable to connect to the specified database."
Considerations
I have a TNS_ADMIN environment variable configured to locate the
files for the Oracle NET layer (tnsnames.ora)
TNS_ADMIN -> C:\oraclexe\app\oracle\product\11.2.0\server\network\ADMIN
From Oracle Reports Builder I can correctly generate the report, I
have exported this report as a binary file ".rep" to include it as a
"report" object in the form.
I have tried to provide the complete connection string through the
userid parameter.
SET_REPORT_OBJECT_PROPERTY (v_report_id, REPORT_OTHER, 'userid=' || get_application_property(username) || '/' || get_application_property(password) || '#' || get_application_property(connect_string));
I am using Oracle Application Server 10g.
Can someone help me solve this problem?
Thank you

Related

Access Oracle Apex Application Settings from PL/SQL

I've created an apex application setting FOO with value bar following these instructions.
When I try to access the setting from the SQL Workshop > SQL Commands page I see
Requested Application Setting #FOO# is not defined. Here is the SQL command I am running to retrieve the value:
declare
l_value varchar2(4000);
begin
l_value := APEX_APP_SETTING.GET_VALUE( p_name => 'FOO');
dbms_output.put_line(l_value);
end;
Any idea why this won't work?
Since you are not executing the code from within an APEX session, you need create an APEX session or attach yourself to to an existing APEX session otherwise there is no way to know what workspace/application to get the setting from. If the code was running from within a page, then your existing code would work.
Try executing the code below, but modify your APP ID and PAGE ID to an actual app/page within your APEX application. The username does not matter.
DECLARE
l_value VARCHAR2 (4000);
BEGIN
apex_session.create_session (p_app_id => 100, p_page_id => 2, p_username => 'any_username');
l_value := APEX_APP_SETTING.GET_VALUE (p_name => 'FOO');
DBMS_OUTPUT.put_line (l_value);
apex_session.delete_session;
END;
If you want to try building a function to return the setting value, I would recommend building a function like this
CREATE OR REPLACE FUNCTION FN_APP_CONFIG (
p_setting_name APEX_190200.WWV_FLOW_APP_SETTINGS.NAME%TYPE,
p_app_id APEX_190200.WWV_FLOW_APP_SETTINGS.FLOW_ID%TYPE DEFAULT APEX_APPLICATION.g_flow_id)
RETURN APEX_190200.WWV_FLOW_APP_SETTINGS.VALUE%TYPE
IS
l_value APEX_190200.WWV_FLOW_APP_SETTINGS.VALUE%TYPE;
BEGIN
SELECT VALUE
INTO l_value
FROM APEX_190200.WWV_FLOW_APP_SETTINGS
WHERE flow_id = p_app_id AND name = p_setting_name;
RETURN l_value;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
RETURN NULL;
WHEN TOO_MANY_ROWS
THEN
RETURN NULL;
END;
/

Oracle Function required with certain logic to implement in Spotfire

I have created a Function from Oralce DB and used it as input to Spotfire report. The reason why i used a function instead of view is because the parameters had some complex logic operations and i was not able to get that done in a view.
Coming to the report i have built. Currently i am using 2 parameters in the function and i am taking both the values from Spotfire text area in Data on Demand mode. Issue is that unless i enter values for both parameters i wont get the output. My requirement is that i need to add few more parameters for the report which i can but i need to set up the function and the settings in Spotfire such that if 5 parameters are there , if users enters one value for just one parameter report should run for that parameter. So the Functions needs to be in such a way that if value is entered that should be taken and if its left empty then that should not be considered. leaving the Spotfire part if the Function is built with the specifics mentioned by me i can implement it directly.
I have got different solutions from everywhere and i am not able to implement anything properly. I am updating all the examples and need help in figuring out the right one and correcting it or to do it in a completely different manner
Code Type 1:
create or replace function Function_test(p1 varchar2='',p2 varchar2='',p3 varchar2)
return SYS_REFCURSOR as
my_cursor SYS_REFCURSOR;
begin
open my_cursor for
select distinct
x.c1
x.c2
x.c3
from x
where x.c1=p3
and (p1='' or x.c2=p1)
and (p2='' or x.c3=p2);
return my_cursor;
end;
The above code seems to be an example from MSSQL and i am able to get the logic but dont know the right way to implement in Oracle. When i tried i just got lot of errors.
Code Type 2:
create or replace function Function_delete(param1 Varchar2, param2 varchar2)
RETURN Varchar2 IS
ssql varchar2(3000);
test varchar2(1000);
begin
ssql := 'select col1,col2 from table_x';
if param1 is null and param2 is not null then
ssql := ssql || ' Where col2='''|| param2 ||'''';
end if;
if param1 is not null and param2 is null then
ssql := ssql || ' Where col3= ''' || param1 ||'''';
end if;
if param1 is not null and param2 is not null then
ssql := ssql || ' Where col3 = ''' || param1 || ''' and col2='''|| param2 ||'''';
end if;
dbms_output.put_line(ssql);
execute immediate ssql into test;
return test;
--EXCEPTION
-- WHEN OTHERS THEN
-- return 'Hello';
end Function_delete;
In the above example i am not able to implement the logic right in Spotfire cos it requires columns to get the data. Ultimately i need a code that accepts no of parameters that are given by the user rather than working only when all parameters are given. It needs to have columns visibly displayed since that way i can implement the same in Spotfire Reports.
you can try this:
create or replace function Function_test(p1 in varchar2,p2 in varchar2,p3 in varchar2)
return SYS_REFCURSOR as
my_cursor SYS_REFCURSOR;
begin
open my_cursor for
select
1
from
dual
where 1=1
and '1'=p1
and '2'=p2
and '3'=p3 ;
return my_cursor;
end;

Oracle raise_application_error error number best practice

I have a question regarding the error codes (-20000 to -20999) for Raise Application Error.
Can we use same error code (eg -20000) only for different error scenarios at multiple places in PLSQL code?
If we can use same error code in all places, why do we have 1000 codes?
What is the best practice to use error codes in Raise Application Error?
Sample Code:
create table t(id number primary key);
declare
begin
insert into t(id) values (1);
insert into t(id) values (1);
commit;
exception
when dup_val_on_index
then
raise_application_error(-20000, 'Cannot Insert duplicates');
when others
then
raise_application_error(-20000, sqlcode||'-'||sqlerrm);
end;
Can we use same error code (eg -20000) only for different error scenarios at multiple places in PLSQL code?
As Justin notes, you can certainly do that - just use one code. But it is likely to lead to confusion. I've seen it done, and usually in that case, the developers simply embed all critical information into the message, even including a code (they might, for example, already be using their own error codes that fall outside the acceptable range).
I suggest you follow Oracle's lead: assign ranges to areas of your application and then use error codes within a range when an application-specific error occurs in that part of the part.
If we can use same error code in all places, why do we have 1000 codes?
See above.
What is the best practice to use error codes in Raise Application Error?
Create a table in which you "register" error codes that are used, along with the message. Then developers can check to see "their" error is already registered and can re-use it. Or, more likely, they register a new error code and message.
Either way, you have a central point from which to organize the codes and hopefully minimize the change of two developers using the same error code.
Here's a script that does what I suggested above, along with a utility to generate a package with all of the errors defined and available for "soft-coded" reference.
CREATE TABLE msg_info (
msgcode INTEGER,
msgtype VARCHAR2(30),
msgtext VARCHAR2(2000),
msgname VARCHAR2(30),
description VARCHAR2(2000)
);
CREATE OR REPLACE PACKAGE msginfo
IS
FUNCTION text (
code_in IN INTEGER
, type_in IN VARCHAR2
, use_sqlerrm IN BOOLEAN := TRUE
)
RETURN VARCHAR2;
FUNCTION name (code_in IN INTEGER, type_in IN VARCHAR2)
RETURN VARCHAR2;
PROCEDURE genpkg (
NAME_IN IN VARCHAR2
, oradev_use IN BOOLEAN := FALSE
, to_file_in IN BOOLEAN := TRUE
, dir_in IN VARCHAR2 := 'DEMO' -- UTL_FILE directory
, ext_in IN VARCHAR2 := 'pkg'
);
END;
/
CREATE OR REPLACE PACKAGE BODY msginfo
IS
FUNCTION msgrow (code_in IN INTEGER, type_in IN VARCHAR2)
RETURN msg_info%ROWTYPE
IS
CURSOR msg_cur
IS
SELECT *
FROM msg_info
WHERE msgtype = type_in AND msgcode = code_in;
msg_rec msg_info%ROWTYPE;
BEGIN
OPEN msg_cur;
FETCH msg_cur INTO msg_rec;
CLOSE msg_cur;
RETURN msg_rec;
END;
FUNCTION text (
code_in IN INTEGER
, type_in IN VARCHAR2
, use_sqlerrm IN BOOLEAN := TRUE
)
RETURN VARCHAR2
IS
msg_rec msg_info%ROWTYPE := msgrow (code_in, type_in);
BEGIN
IF msg_rec.msgtext IS NULL AND use_sqlerrm
THEN
msg_rec.msgtext := SQLERRM (code_in);
END IF;
RETURN msg_rec.msgtext;
END;
FUNCTION NAME (code_in IN INTEGER, type_in IN VARCHAR2)
RETURN VARCHAR2
IS
msg_rec msg_info%ROWTYPE := msgrow (code_in, type_in);
BEGIN
RETURN msg_rec.msgname;
END;
PROCEDURE genpkg (
NAME_IN IN VARCHAR2
, oradev_use IN BOOLEAN := FALSE
, to_file_in IN BOOLEAN := TRUE
, dir_in IN VARCHAR2 := 'DEMO'
, ext_in IN VARCHAR2 := 'pkg'
)
IS
CURSOR exc_20000
IS
SELECT *
FROM msg_info
WHERE msgcode BETWEEN -20999 AND -20000 AND msgtype = 'EXCEPTION';
-- Send output to file or screen?
v_to_screen BOOLEAN := NVL (NOT to_file_in, TRUE);
v_file VARCHAR2 (1000) := name_in || '.' || ext_in;
-- Array of output for package
TYPE lines_t IS TABLE OF VARCHAR2 (1000)
INDEX BY BINARY_INTEGER;
output lines_t;
-- Now pl simply writes to the array.
PROCEDURE pl (str IN VARCHAR2)
IS
BEGIN
output (NVL (output.LAST, 0) + 1) := str;
END;
-- Dump to screen or file.
PROCEDURE dump_output
IS
BEGIN
IF v_to_screen
THEN
FOR indx IN output.FIRST .. output.LAST
LOOP
DBMS_OUTPUT.put_line (output (indx));
END LOOP;
ELSE
-- Send output to the specified file.
DECLARE
fid UTL_FILE.file_type;
BEGIN
fid := UTL_FILE.fopen (dir_in, v_file, 'W');
FOR indx IN output.FIRST .. output.LAST
LOOP
UTL_FILE.put_line (fid, output (indx));
END LOOP;
UTL_FILE.fclose (fid);
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ( 'Failure to write output to '
|| dir_in
|| '/'
|| v_file
);
UTL_FILE.fclose (fid);
END;
END IF;
END dump_output;
BEGIN
/* Simple generator, based on DBMS_OUTPUT. */
pl ('CREATE OR REPLACE PACKAGE ' || NAME_IN);
pl ('IS ');
FOR msg_rec IN exc_20000
LOOP
IF exc_20000%ROWCOUNT > 1
THEN
pl (' ');
END IF;
pl (' exc_' || msg_rec.msgname || ' EXCEPTION;');
pl ( ' en_'
|| msg_rec.msgname
|| ' CONSTANT INTEGER := '
|| msg_rec.msgcode
|| ';'
);
pl ( ' PRAGMA EXCEPTION_INIT (exc_'
|| msg_rec.msgname
|| ', '
|| msg_rec.msgcode
|| ');'
);
IF oradev_use
THEN
pl (' FUNCTION ' || msg_rec.msgname || ' RETURN INTEGER;');
END IF;
END LOOP;
pl ('END ' || NAME_IN || ';');
pl ('/');
IF oradev_use
THEN
pl ('CREATE OR REPLACE PACKAGE BODY ' || NAME_IN);
pl ('IS ');
FOR msg_rec IN exc_20000
LOOP
pl (' FUNCTION ' || msg_rec.msgname || ' RETURN INTEGER');
pl (' IS BEGIN RETURN en_' || msg_rec.msgname || '; END;');
pl (' ');
END LOOP;
pl ('END ' || NAME_IN || ';');
pl ('/');
END IF;
dump_output;
END;
END;
/
/* Sample data to be used in package generation. */
BEGIN
INSERT INTO msg_info
VALUES (-20100, 'EXCEPTION', 'Balance too low', 'bal_too_low'
, 'Description');
INSERT INTO msg_info
VALUES (-20200, 'EXCEPTION', 'Employee too young', 'emp_too_young'
, 'Description');
COMMIT;
END;
/
Can you use the same error code every time? Sure.
Should you? Almost certainly not. It would be rather annoying if Oracle raised an ORA-00001 error for every possible thing that went wrong-- a primary key violation, a foreign key violation, an unexpected internal error, a tablespace running out of space, a permission error, etc.-- because that makes it much more difficult for developers to handle the errors they can and propagate those they cannot. You'd have to do things like parse the text of the error string to figure out what went wrong and to figure out whether it's an error that you can handle or not. And heaven forbid that Oracle ever change the text of an error message or your parser causes you to misinterpret an error message. Similarly, it would generally be annoying if your code threw the same error code for every possible problem that was encountered.
If you are going to use a custom error code, it should communicate something above and beyond what the Oracle error code provides. It makes no sense, for example, to have a when others that converts the nice, usable Oracle error message and error stack to a pointless user-defined error. It may make perfect sense, though, for an ORA-20001 error to indicate that a foo already exists and an ORA-20002 error to indicate that a bar already exists when you have an application that deals with lots of foo's and bar's and those errors would make more sense to a user than a generic duplicate key error.
Just because you can do something doesn't mean that you should do something. Personally, as a consultant that comes in to mop up other people's messes, we (hired guns) hate developers that don't provide useful error messages in their code. Oracle does a pretty good job - most of the time. In the case of DUPS, it would be nice if in the error messaging, the developers added one more line to their error message and that would be the value being duplicated - what a concept. Maybe you don't send the text back to an end user, but should consider using dbms_system.ksdwrt - a proc that writes these errors to the alert log.
1) Can we use same error code (eg -20000) only for different error
scenarios at multiple places in PLSQL code?
You can. It is however not always useful - read on.
2) If we can use same error code in all places, why do we have 1000
codes?
To distinguish between different error conditions - read on.
3) What is the best practice to use error codes in Raise Application
Error?
Define some in-house standard, and keep following it - read on.
So, what is the point for user defined errors? They are great to show business logic problems (e.g. a newly added customer seems to be 950 years old, or born in the future), while standard Oracle errors show storage logic problems (e.g. referring to an unknown customer).
Depending on your application model, you may have the business logic implemented outside of the database, or you may prefer leveraging the powers of PL/SQL to implement the business logic inside the database.
This however is not too useful:
EXCEPTION
When Dup_val_on_index
then
Raise_application_error(-20000,'Cannot Insert duplicates');
WHEN OTHERS
THEN
Raise_application_error(-20000,SQLCODE||'-'||SQLERRM);
end;
First, you may ask yourself, what do you do in case of an error? Ok, you can print a nice call stack to the face of the end user - which is totally useless. I would say you can isolate all possible problems into something what the user can fix (i.e. pick a different login name if you hit a unique constraint), and errors what the user can not fix (for example, referring to a non-existing user because of a programming mistake).
I would say it makes sense to distinguish between errors this way. If you feel this logical, then:
wrap all exceptions to a custom BusinessError with e.g. -20000 if the user can do something with that. Use the error message to provide a useful description of what to do (i.e. "please pick a different login name" instead of "cannot insert duplicates") - and then your application is already in the top 5%.
wrap all other exceptions as a technical error, say with -20001. In case of an unexpected technical error, your application can do one thing: log it, and show a nice "something went wrong but we're trying to handle it" message to the user. Very less users can read/or interested to read Oracle call stacks :)
I am not totally sold on the standard oracle error messages. For example, a simple constraint violation error can be quite cryptic. I suggest to explicitly name all constraints, and follow a good naming convention. In that case, you don't need to actually handle any built-in Oracle error - as they will go to the application code, which will log them and show "something went wrong" to the user as it is not a business error.
Limitations:
This solution works very well if you have the business logic implemented in the database in PL/SQL. Of course, if you prefer application server business logic, e.g. Hibernate, then you'll have the fun to handle each and every constraint violation on your own.
This solution may need extensions if you're building a multi-language application. Business errors will have meaningful, human-friendly messages, and that comes in one language. Either you may need to throw a message in multiple languages (and pick the right one on the GUI), or implement your own translation file on top of it.
An example with business errors (one internal error, the dup_val_on_index is wrapped to business error, since that is caused by a wrong user input)
procedure addUser(in_birthDate date, ) is
pls_integer age=sysdate-in_birthDate;
begin
if age>100 then
raise_application_error(-20000,'Check birth date, you''re too old!');
elsif age<0 then
raise_application_error(-20000,'Birth date can not be in the future.');
end if;
insert into....
commit;
exception
when dup_val_on_index then
raise_application_error(-20000,'Please pick a different login name!');
end;
You may consider creating a procedure for throwing a business error:
CREATE OR REPLACE
Procedure throw (in_message varchar2) is
begin
raise_application_error(-20999,in_message);
end;
Which makes your code more readable:
begin
if age>100 then
throw('Check birth date, you''re too old!');

Oracle DBMS_SESSION SET_CONTEXT does not store a value

I am trying to produced a system in Oracle where a context stores a value; if a table is updated (update/insert/delete) in any session, the value should be increased. The problem I am having is that even though I'm certain I've set it up correctly, it doesn't seem to work - the context does not seem to actually store the value. I'm using Oracle 11.2.0.1.0.
For the minimum possible example:
I have a context (the ACCESSED GLOBALLY clause should make it so the values are accessible across all Oracle sessions, which is what I want):
CREATE OR REPLACE CONTEXT MM_CONTEXT USING PCKG_TESTGLOBALS ACCESSED GLOBALLY;
I have a debugging table:
CREATE TABLE DATALOG (
DATALOG_SEQ NUMBER,
AT_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
MESSAGE VARCHAR2(4000)
);
I have a sequence to support the DATALOG table:
CREATE SEQUENCE SQ_DATALOG;
Now the PCKG_TESTGLOBALS package is required which has access to the MM_CONTEXT context and can update elements of it:
CREATE OR REPLACE PACKAGE PCKG_TESTGLOBALS IS
PROCEDURE Log(FunctionName IN VARCHAR2, Msg IN VARCHAR2);
PROCEDURE SetParameter(p_name IN VARCHAR2, p_value IN VARCHAR2);
FUNCTION GetTABLEID RETURN NUMBER;
END PCKG_TESTGLOBALS;
/
CREATE OR REPLACE PACKAGE BODY PCKG_TESTGLOBALS IS
CONTEXT_NAME CONSTANT VARCHAR2(100) := 'MM_CONTEXT';
PROCEDURE Log(FunctionName IN VARCHAR2, Msg IN VARCHAR2) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO DATALOG(DATALOG_SEQ, MESSAGE) VALUES (SQ_DATALOG.NEXTVAL, FunctionName || ':' || Msg);
COMMIT;
END;
PROCEDURE SetParameter(p_name IN VARCHAR2, p_value IN VARCHAR2) IS
ActualValue VARCHAR2(10000);
BEGIN
Log('SetParameter', 'ENTERED');
Log('SetParameter', 'SETTING "' || p_name || '" TO "' || p_value || '"');
DBMS_SESSION.SET_CONTEXT(CONTEXT_NAME, p_name, p_value);
ActualValue := SYS_CONTEXT(CONTEXT_NAME, p_name);
Log('SetParameter', 'READ "' || p_name || '" AS "' || ActualValue || '"');
Log('SetParameter', 'EXITED');
END;
PROCEDURE Initialise IS
iTmp NUMBER;
BEGIN
Log('Initialise', 'ENTERED');
IF SYS_CONTEXT(CONTEXT_NAME, 'LOWNID') IS NULL THEN
iTmp := DBMS_RANDOM.RANDOM;
Log('Initialise', '"LOWNID" has no value, writing "' || iTmp || '"');
PCKG_TESTGLOBALS.SetParameter('LOWNID', iTmp);
END IF;
Log('Initialise', 'EXITED');
END;
FUNCTION GetTABLEID RETURN NUMBER IS
ReadValue VARCHAR2(32767);
BEGIN
Log('GetTABLEID', 'ENTERED');
ReadValue := SYS_CONTEXT(CONTEXT_NAME, 'LOWNID');
Log('GetTABLEID', 'READ VALUE OF "LOWNID" AS "' || ReadValue || '"');
Log('GetTABLEID', 'EXITED');
RETURN TO_NUMBER(ReadValue);
END;
BEGIN
Initialise;
END PCKG_TESTGLOBALS;
/
So to explain the functions in PCKG_TESTGLOBALS:
Log - Logs a message for debugging reasons.
SetParameter - Takes a name/value pair and stores this using DBMS_SESSION.SET_CONTEXT in the MM_CONTEXT
Initialise - For a session variable LOWNID it checks if the variable is null, and if it is, sets it to a random value, using SetParameter. Initialise is called when the package is first used in a session.
GetTABLEID - this returns the value stored in the session variable LOWNID.
Finally there is a trigger called TR_ONDML_TL_LOWN which is on the table LOWN - the structure of which doesn't matter here, any table would do - and fires after any DML, INSERT or UPDATE or DELETE.
CREATE OR REPLACE TRIGGER TR_ONDML_TL_LOWN
AFTER INSERT OR UPDATE OR DELETE ON LOWN
DECLARE
iTmp NUMBER;
BEGIN
PCKG_TESTGLOBALS.Log('TR_ONDML_TL_LOWN', 'ENTERED');
iTmp := PCKG_TESTGLOBALS.GetTABLEID;
PCKG_TESTGLOBALS.Log('TR_ONDML_TL_LOWN', 'Read Value "' || iTmp || '"');
iTmp := NVL(iTmp, 1) + 1;
PCKG_TESTGLOBALS.Log('TR_ONDML_TL_LOWN', 'Updated Value "' || iTmp || '"');
PCKG_TESTGLOBALS.SetParameter('LOWNID', iTmp);
PCKG_TESTGLOBALS.Log('TR_ONDML_TL_LOWN', 'EXITED');
END TR_ONDML_TL_LOWN;
/
The purpose of this trigger is thus: Whenever the table LOWN is updated, the value of the session variable LOWNID is read back, and has 1 added to it, and is written back in.
If in a new session I do a few consecutive UPDATEs on the LOWN table, I get these results in my debugging table (SELECT MESSAGE FROM DATALOG ORDER BY DATALOG_SEQ)
Initialise:ENTERED
Initialise:"LOWNID" has no value, writing "805223597"
SetParameter:ENTERED
SetParameter:SETTING "LOWNID" TO "805223597"
SetParameter:READ "LOWNID" AS ""
SetParameter:EXITED
Initialise:EXITED
TR_ONDML_TL_LOWN:ENTERED
GetTABLEID:ENTERED
GetTABLEID:READ VALUE OF "LOWNID" AS ""
GetTABLEID:EXITED
TR_ONDML_TL_LOWN:Read Value ""
TR_ONDML_TL_LOWN:Updated Value "2"
SetParameter:ENTERED
SetParameter:SETTING "LOWNID" TO "2"
SetParameter:READ "LOWNID" AS ""
SetParameter:EXITED
TR_ONDML_TL_LOWN:EXITED
TR_ONDML_TL_LOWN:ENTERED
GetTABLEID:ENTERED
GetTABLEID:READ VALUE OF "LOWNID" AS ""
GetTABLEID:EXITED
TR_ONDML_TL_LOWN:Read Value ""
TR_ONDML_TL_LOWN:Updated Value "2"
SetParameter:ENTERED
SetParameter:SETTING "LOWNID" TO "2"
SetParameter:READ "LOWNID" AS ""
SetParameter:EXITED
TR_ONDML_TL_LOWN:EXITED
You can see from the example that it passes in the value correctly to DBMS_SESSION.SET_CONTEXT, but it doesn't seem to store the value at all. What am I doing wrong? Thanks.
For a session to see a value of a Globally Accessible context, its client_identifier (as per SYS_CONTEXT('userenv','client_identifier')) must match the client_id parameter that was used in the call to DBMS_SESSION.set_context.
If your set_context call doesn't set client_id it defaults to NULL; in this case, the session will only see the new value if its client_identifier is also NULL.
If the session takes any particular value for client_identifier, you must use this same value in the call to set_context.
In your case, you want a global variable that is accessible by all sessions; if your sessions are getting random values for client_identifier, you may need to set it to NULL before running your code, then (perhaps) restore its value before returning control to the caller.

Oracle dynamic parameters

I'm struggling to create a dynamic sql parametrized query. It involves using 'IS NULL' or 'IS NOT NULL'
Here's a simple pl/sql query:
CREATE OR REPLACE PROCEDURE GET_ALL_INFORMATION
(
"PARAM_START_DATE" IN DATE,
"PARAM_END_DATE" IN DATE,
"PARAM_IS_SUBMITTED" IN NUMBER,
"EXTRACT_SUBMITTED_CONTACTS" OUT sys_refcursor
) IS
sql_stmt VARCHAR2(3000);
PARAM_CONDITION VARCHAR2(20);
BEGIN
IF PARAM_IS_SUBMITTED = 1 THEN
PARAM_CONDITION := 'NOT NULL';
ELSE
PARAM_CONDITION := 'NULL';
END IF;
sql_stmt := ' SELECT
REGISTRATION_NUMBER,
NAME PROVIDER_TYPE,
ORGANIZATION
FROM TABLE_A
WHERE
P.DATE_FINALIZED IS :A;
OPEN EXTRACT_SUBMITTED_CONTACTS FOR sql_stmt USING PARAM_CONDITION;
Whereas the parameter (:A) in (USING PARAM_CONDITION) should have 'NULL' or 'NOT NULL'. It does not seem to work the way I envisioned.
Am I missing something?
As explained by GriffeyDog in a comment above, bind parameters could only be used as place holder for values. Not to replace keywords or identifiers.
However, this is not really an issue here, as you are using dynamic SQL. The key idea ifs that you build your query as a string -- and it will be parsed at run-time by the PL/SQL engine when you invoke EXECUTE or OPEN .. FOR.
Simply said, you need a concatenation -- not a bound parameter:
...
sql_stmt := ' SELECT
REGISTRATION_NUMBER,
NAME PROVIDER_TYPE,
ORGANIZATION
FROM TABLE_A
WHERE
P.DATE_FINALIZED IS ' || PARAM_CONDITION;
-- ^^
OPEN EXTRACT_SUBMITTED_CONTACTS FOR sql_stmt;

Resources