I am currently migrating from oracle to DB2. I have actively used sys_context in most of the applications to get the userID of logged in user through sessions(IIS and .net framework).
I am looking to convert the following set of scripts from oracle to DB2. So far not able to find any equivalent for oracle context in DB2.
SQL> create context my_ctx
2 using pkg_ctx;
Context created.
SQL> create package pkg_ctx
2 as
3 procedure set_context;
4 end;
5 /
Package created.
SQL> create or replace package body pkg_ctx
2 as
3 procedure set_context
4 as
5 begin
6 dbms_session.set_context( 'MY_CTX', 'USERNAME', 'test' );
7 end;
8 end;
9 /
Package body created.
SQL> exec pkg_ctx.set_context;
PL/SQL procedure successfully completed.
SQL> select sys_context( 'MY_CTX', 'USERNAME' )
2 from dual;
SYS_CONTEXT('MY_CTX','USERNAME')
-------------------------------------------------------------------------------
test
Assuming you work with Db2 for LUW, you can use one of the CURRENT CLIENT_* registry variables to pass session context information from the client application to the server. For example, during the session initialization on the client side you would issue SET CURRENT CLIENT_USERID='stacky', then on the server that variable can be read by a routine or a trigger.
It worked like this, haven't tried it at application level. But from the database tried to register 2 users from 2 different instances of the same database on DB2.
call sysproc.wlm_set_client_info('stacky',null,null,null,null);
select wlm_set_client_info from sysibm.sysdummy1;
returns stacky
from another instance of the same database
call sysproc.wlm_set_client_info('test',null,null,null,null);
select wlm_set_client_info from sysibm.sysdummy1;
returns test.
Will try this from the application level through creating sessions of the users, will monitor how exactly this works with the special registers.
Not sure if this is the exact way to do this, will explore more and keep updated.
For replicating a similar situation in our environment, while doing the migration from oracle to DB2 LUW.
The below procedure sets up the all the context values in user_id (limited to varchar 255) field which we need as as 'key1:value1|key2:value2|'
Procedure to setup the context values
CREATE OR REPLACE PROCEDURE setup_context(pValues IN VARCHAR)
IS
lValues VARCHAR2(255) := pValues;
BEGIN
--save the as is
wlm_set_client_info(lValues, NULL, NULL, NULL, NULL);
END;
Procedure to set the value of the desired key
CREATE OR REPLACE FUNCTION sys_context(prefix IN VARCHAR) RETURN VARCHAR IS
lValues VARCHAR2(255);
lParam VARCHAR2(255);
lKey VARCHAR2(255);
lValue VARCHAR2(255);
lIndex PLS_INTEGER;
BEGIN
--get the data from the current session
SELECT CURRENT CLIENT_USERID INTO lValues FROM dual;
LOOP
EXIT WHEN lValues IS NULL OR LENGTH(lValues) = 0;
lIndex := instr(lValues, '|');
IF lIndex > 0 THEN
lParam := substr(lValues, 1, lIndex-1);
lValues := substr(lValues, lIndex+1);
ELSE
lParam := lValues;
lValues := NULL;
END IF;
lIndex := instr(lParam, ':');
lKey := substr(lParam, 1, lIndex-1);
lValue := substr(lParam, lIndex+1);
--get the matching value
IF(lKey = prefix ) Then
RETURN lValue;
END IF;
END LOOP;
RETURN '';
END;
reference : https://www.ibm.com/support/knowledgecenter/en/SSEPEK_11.0.0/sqlref/src/tpc/db2z_sp_wlmsetclientinfo.html
Related
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;
/
Is there a possibility to connect to Oracle (via OCI) from one process, then connect on the same database session from another process?
In my current app, there are two ways to access the database: a synchronous one and an asynchronous one (by using a separate process, communicating via sockets).
The problem is the two methods implement distinct sessions.
If I attempt e.g. an update on one session, then try to update the same table from the other session without committing, I get a hang on the OCI call.
Worse, if a session variable is set from one session - the other session does not see it (which is exactly what the name says...).
If you are using an 11g database, you could use the DBMS_XA package to allow one session to to join a transaction started by the first session. As Tim Hall deomonstrates, you can start a transaction in one session, join that transaction from another session, and read the uncommitted changes made in the transaction. Unfortunately, however, that is not going to help with session variables (assuming that "session variable" means package variable that have session scope).
Create the package and the table:
CREATE TABLE foo( col1 NUMBER );
create or replace package pkg_foo
as
g_var number;
procedure set_var( p_in number );
end;
create or replace package body pkg_foo
as
procedure set_var( p_in number )
as
begin
g_var := p_in;
end;
end;
In Session 1, we start a global transaction, set the package variable, and insert a row into the table before suspending the global transaction (which allows another session to resume it)
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_xid dbms_xa_xid := dbms_xa_xid( 1 );
3 l_ret integer;
4 begin
5 l_ret := dbms_xa.xa_start( l_xid, dbms_xa.tmnoflags );
6 pkg_foo.set_var(42);
7 dbms_output.put_line( 'Set pkg_foo.g_var to ' || pkg_foo.g_var );
8 insert into foo values( 42 );
9 l_ret := dbms_xa.xa_end( l_xid, dbms_xa.tmsuspend );
10* end;
SQL> /
Set pkg_foo.g_var to 42
PL/SQL procedure successfully completed.
In session 2, we resume the global transaction, read from the table, read the session variable, and end the global transaction. Note that the query against the table sees the row we inserted but the package variable change is not visible.
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_xid dbms_xa_xid := dbms_xa_xid( 1 );
3 l_ret integer;
4 l_col1 integer;
5 begin
6 l_ret := dbms_xa.xa_start( l_xid, dbms_xa.tmresume );
7 dbms_output.put_line( 'Read pkg_foo.g_var as ' || pkg_foo.g_var );
8 select col1 into l_col1 from foo;
9 dbms_output.put_line( 'Read COL1 from FOO as ' || l_col1 );
10 l_ret := dbms_xa.xa_end( l_xid, dbms_xa.tmsuccess );
11* end;
SQL> /
Read pkg_foo.g_var as
Read COL1 from FOO as 42
PL/SQL procedure successfully completed.
To share session state between the sessions, would it be possible to use a global application context rather than using package variables? You could combine that with the DBMS_XA packages if you want to read both database tables and session state.
Create the context and the package with the getter and setter
CREATE CONTEXT my_context
USING pkg_foo
ACCESSED GLOBALLY;
create or replace package pkg_foo
as
procedure set_var( p_session_id in number,
p_in in number );
function get_var( p_session_id in number )
return number;
end;
create or replace package body pkg_foo
as
procedure set_var( p_session_id in number,
p_in in number )
as
begin
dbms_session.set_identifier( p_session_id );
dbms_session.set_context( 'MY_CONTEXT', 'G_VAR', p_in, null, p_session_id );
end;
function get_var( p_session_id in number )
return number
is
begin
dbms_session.set_identifier( p_session_id );
return sys_context('MY_CONTEXT', 'G_VAR');
end;
end;
In session 1, set the value of the context variable G_VAR to 47 for session 12345
begin
pkg_foo.set_var( 12345, 47 );
end;
Now, session 2 can read the value from the context
1* select pkg_foo.get_var( 12345 ) from dual
SQL> /
PKG_FOO.GET_VAR(12345)
----------------------
47
I have two methods of my Oracle type with name myType:
create or replace type myType as object (
dummy varchar2(1),
constructor function myType(something varchar2) return self as result,
member procedure mp(self in myType,force boolean := false),
static procedure sp
);
create or replace type body myType is
constructor function myType(something varchar2) return self as result
is
begin
dummy := something;
return;
end;
member procedure mp(self in myType,force boolean := false) is
begin
if self.dummy is null and force then
myType.sp();
end if;
end;
static procedure sp is
begin
null;
end;
end;
This causes an Oracle error: myType is out of scope (PLS-00225). If I remove "myType." it thinks I am trying to call sm() as a member method and also gives an error (PLS-00587).
I've found out how to solve the issue:
create or replace synonym mySynonym for myType;
and then call instead of myType.sp(); -> mySynonym.sp(); and it works fine.
Still I would prefer to find some solution without a garbage synonym if one exists.
Oracle version: 11.2
Important: the constructor causes the fail and it can't be eliminated in my case
Sorry, can't reproduce this, on Oracle XE 11.2:
SQL> create or replace type myType as object (
2 z char(1),
3 member procedure mp,
4 static procedure sp
5 );
6 /
Type created.
SQL> create or replace type body myType is
2 member procedure mp is
3 begin
4 myType.sp();
5 end;
6
7 static procedure sp is
8 begin
9 dbms_output.put_line('We''re here');
10 end;
11 end;
12 /
Type body created.
SQL> set serveroutput on
SQL> declare
2 x myType;
3 begin
4 x := myType(z => 'X');
5 x.mp();
6 end;
7 /
We're here
PL/SQL procedure successfully completed.
SQL>
How are you defining your type header?
EDIT: Now that you've added the constructor, I can reproduce the error.
One way to fix the problem is to qualify myType with the schema owner when calling the static procedure:
member procedure mp(self in myType,force boolean := false) is
begin
if self.dummy is null and force then
luke.myType.sp();
end if;
end;
This change allowed me to compile the type successfully.
I created your procedure while connected to my 11g XE database as user luke. This username is very likely to be different for your system.
Of course, your project may have different usernames for dev, test and production, so this might not be the ideal approach for you. If so, creating the synonym is probably the best thing to do.
This is my first (edited) stackoverflow question, so please bear with me.
In Oracle 11g, I have a need to describe/interrogate the underlying columns of a reference cursor returned from a procedure call on another database over a dblink, in which the actual SQL is not always "explicit", but sometimes dynamically generated.
For example:
declare
v_ref_cur sys_refcursor;
v_cur_num number;
v_col_count number;
v_col_table dbms_sql.desc_tab3;
begin
myProc#myDblink(v_ref_cur, 'myvalue');
v_cur_num := dbms_sql.to_cursor_number(v_ref_cur);
dbms_sql.describe_columns3(v_cur_num, v_col_count, v_col_table);
...
end
If myProc() on the other database has an "explicit" SQL statement like:
open cursor for select * from foo where bar = myParam;
The cursor conversion and description (still) work just fine - I can determine the column names, types, lengths, etc returned by the procedure.
BUT, if myProc() on the other database involves dynamic SQL, like:
v_sql := 'select * from foo where bar = ''' || myParam || '''';
open cursor for v_sql;
I get an ORA-01001 invalid cursor error when attempting to call dbms_sql.to_cursor_number().
Is there a way to convert/describe a reference cursor derived from dynamic SQL as called from a remote procedure? If so, how? If not, why not?
Thanks for any/all assistance!
Using DBMS_SQL across a database link raises many different errors, at least some of which are Oracle bugs. Those problems can be avoided by putting all of the logic in a function compiled on the remote server. Then call that function remotely.
--Create and test a database link
create database link myself connect to <schema> identified by "<password>"
using '<connect string or service name>';
select * from dual#myself;
--myProc
create procedure myProc(p_cursor in out sys_refcursor, p_value varchar2) is
begin
--open p_cursor for select * from dual where dummy = p_value;
open p_cursor for 'select * from dual where dummy = '''||p_value||'''';
end;
/
--Simple function that counts and displays the columns. Expected value is 1.
create or replace function count_columns return number is
v_ref_cur sys_refcursor;
v_cur_num number;
v_col_count number;
v_col_table dbms_sql.desc_tab3;
begin
--ORA-01001: invalid cursor
--myProc#myself(v_ref_cur, 'myvalue');
myProc(v_ref_cur, 'myvalue');
--ORA-03113: end-of-file on communication channel
--v_cur_num := dbms_sql.to_cursor_number#myself(v_ref_cur);
v_cur_num := dbms_sql.to_cursor_number(v_ref_cur);
--Compilation error: PLS-00306:
-- wrong number or types of arguments in call to 'DESCRIBE_COLUMNS3'
--dbms_sql.describe_columns3#myself(v_cur_num, v_col_count, v_col_table);
dbms_sql.describe_columns3(v_cur_num, v_col_count, v_col_table);
return v_col_count;
end;
/
begin
dbms_output.put_line('Number of columns: '||count_columns#myself());
end;
/
Number of columns: 1
the procedure Im working has an input variable that is comma delimited. As of right now when I go to run a test script, I dont get any values back. Here is what I have so far.
procedure get_patient(
p_statusmnemonic_in in membermedicalreconcilationhdr.reconciliationstatusmnemonic%type,
p_return_cur_out out sys_refcursor,
p_err_code_out out number,
p_err_mesg_out out varchar2)
is
begin
open p_return_cur_out for
select h.primarymemberplanid,
h.assigneduserid,
h.accountorgid,
h.reconciliationstatusmnemonic,
h.estimatedenddt,
h.actualenddt,
h.inserteddt,
h.insertedby,
h.updateddt,
h.updatedby
from membermedicalreconcilationhdr h
where h.reconciliationstatusmnemonic in (p_statusmnemonic_in);
p_err_code_out := 0;
exception
when others then
p_err_code_out := -1;
p_err_mesg_out := 'error in get_patient=> ' || sqlerrm;
end get_patient;
Here is the test script:
set serveroutput on
declare
type tempcursor is ref cursor;
v_cur_result tempcursor;
errcode number;
errmesg varchar2(1000);
p_primarymemberplanid_in membermedicalreconcilationhdr.primarymemberplanid%type;
p_assigneduserid_in membermedicalreconcilationhdr.assigneduserid%type;
p_accountorgid_in membermedicalreconcilationhdr.accountorgid%type;
p_reconstatusmnemonic_in membermedicalreconcilationhdr.reconciliationstatusmnemonic%type;
p_estimatedenddt_in membermedicalreconcilationhdr.estimatedenddt%type;
p_actualenddt_in membermedicalreconcilationhdr.actualenddt%type;
p_inserteddate_in membermedicalreconcilationhdr.inserteddt%type;
p_insertedby_in membermedicalreconcilationhdr.insertedby%type;
p_updateddate_in membermedicalreconcilationhdr.updateddt%type;
p_updatedby_in membermedicalreconcilationhdr.updatedby%type;
begin
get_patient
('COMPLETE,SUSPENDED_PRIOR_TO_COMPARE',v_cur_result, errcode, errmesg);
--('COMPLETE',v_cur_result, errcode, errmesg);
loop
fetch v_cur_result into p_primarymemberplanid_in,p_assigneduserid_in,p_accountorgid_in,p_reconstatusmnemonic_in,
p_estimatedenddt_in,p_actualenddt_in,p_inserteddate_in,p_insertedby_in,
p_updateddate_in,p_updatedby_in;
dbms_output.put_line(' planid '||p_primarymemberplanid_in||' userid '||p_assigneduserid_in);
exit when v_cur_result%notfound;
end loop;
dbms_output.put_line(' error code '||errcode||' message '||errmesg);
end;
As of right now I get values back when I just have one input value, but when I try to do two I dont get anything. Ive done research and it looks like my select statement is correct so Im at a loss as to what Im doing wrong. Any help is appreciated, thanks.
If you can change the definition of the procedure, you are better served passing in a proper collection.
CREATE TYPE status_tbl IS TABLE OF VARCHAR2(100);
procedure get_patient(
p_statusmnemonic_in in status_tbl,
p_return_cur_out out sys_refcursor,
p_err_code_out out number,
p_err_mesg_out out varchar2)
is
begin
open p_return_cur_out for
select h.primarymemberplanid,
h.assigneduserid,
h.accountorgid,
h.reconciliationstatusmnemonic,
h.estimatedenddt,
h.actualenddt,
h.inserteddt,
h.insertedby,
h.updateddt,
h.updatedby
from membermedicalreconcilationhdr h
where h.reconciliationstatusmnemonic in (SELECT *
FROM TABLE(p_statusmnemonic_in));
...
Otherwise, you would either have to resort to using dynamic SQL (which would have security and performance implications) or you would need to write code to parse the comma-separated string into a collection and then use the TABLE operator to use that collection in the query.
Assuming you modify the signature of the procedure, the call will also have to change so that you are passing in a collection.
get_patient
(status_tbl('COMPLETE','SUSPENDED_PRIOR_TO_COMPARE'),
v_cur_result,
errcode,
errmesg);
And just to point it out, writing procedures that have error code and error message OUT parameters rather than throwing exceptions is generally highly frowned upon. It makes far more sense to eliminate those parameters and to just throw exceptions when you encounter an error. Otherwise, you are relying on every caller to every procedure to correctly check the returned status code and message (which your sample code does not do). And you are losing a ton of valuable information about things like exactly what line an error occurred on, what the error stack was, etc.
Since you don't post your table definitions or your sample data, it is impossible for us to test this code. Here is a quick demonstration, though, of how it would work
SQL> create table patient (
2 patient_id number primary key,
3 status varchar2(10),
4 name varchar2(100)
5 );
Table created.
SQL> insert into patient values( 1, 'COMPLETE', 'Justin' );
1 row created.
SQL> insert into patient values( 2, 'SUSPENDED', 'Bob' );
1 row created.
SQL> insert into patient values( 3, 'NEW', 'Kerry' );
1 row created.
SQL> commit;
Commit complete.
SQL> CREATE TYPE status_tbl IS TABLE OF VARCHAR2(100);
2 /
Type created.
SQL> ed
Wrote file afiedt.buf
1 create or replace procedure get_patients( p_statuses in status_tbl,
2 p_cursor out sys_refcursor )
3 as
4 begin
5 open p_cursor
6 for select *
7 from patient
8 where status in (select *
9 from table( p_statuses ));
10* end;
SQL> /
Procedure created.
SQL> variable rc refcursor;
SQL> exec get_patients( status_tbl('COMPLETE', 'SUSPENDED'), :rc );
PL/SQL procedure successfully completed.
SQL> print rc;
PATIENT_ID STATUS
---------- ----------
NAME
--------------------------------------------------------------------------------
1 COMPLETE
Justin
2 SUSPENDED
Bob