Skip to Main Content
  • Questions
  • Value gets overwritten when using application context to be accessed by multiple sessions

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Maurice.

Asked: June 11, 2019 - 11:59 am UTC

Last updated: June 11, 2019 - 5:00 pm UTC

Version: 11.2.0.4.0

Viewed 1000+ times

You Asked

Hi Tom!

Running on Oracle 11g (11.2.0.4.0) I try to use a global context to share parameters between user sessions.

Like the docs says:
"username set to a value - client_id set to NULL" means:
This combination enables an application context to be accessed by multiple sessions, as long as the username setting is the same throughout.


With this knowledge I try this as the dba user MG:

create or replace context my_ctx using my_pkg accessed globally;

-- + --

create OR replace package my_pkg is

  procedure set_parameter ( p_parameter in varchar2, p_value in varchar2 );

  function get_parameter ( f_parameter in varchar2 ) return varchar2;

end;
/

-- + --

create OR replace package body my_pkg is

pk_ctx constant varchar2(30) := 'my_ctx';

procedure set_parameter ( p_parameter in varchar2, p_value in varchar2 ) is

begin

dbms_session.set_context ( namespace => pk_ctx, attribute => p_parameter, value => p_value, username => USER );

end set_parameter;

-- + --

function get_parameter ( f_parameter in varchar2 ) return varchar2 is

begin

return ( sys_context ( pk_ctx, f_parameter ) );

end get_parameter;

-- + --

end my_pkg;
/

-- + --

As user MG (the context-admin and package owner), I execute:

execute mg.my_pkg.set_parameter ( 'Ask-Tom', 'MG says: always' );


Now looking into global_context with

select attribute, value, username
from global_context
where namespace = 'MY_CTX'
/


shows

ATTRIBUTE            VALUE        USERNAME
-------------------- ------------ ----------
ASK-TOM              always       MG

Okay.

With another SQL*Plus session as user "TOM" I execute the set_parameter package procedure:

execute mg.my_pkg.set_parameter ( 'Ask-Tom', 'Tom says: what else' );


Back in the first SQL*Plus session as the package owner MG the query

select attribute, value, username
from global_context
where namespace = 'MY_CTX'
/


now shows

ATTRIBUTE            VALUE        USERNAME
-------------------- ------------ ----------
ASK-TOM              what else    TOM


Why the data from the user MG gets overwritten?
I would expect that there is another entry in the global context where the "owner" is TOM.


Regards and thanks in advance!

and Chris said...

There is a global application context per client identifier. Not per username!

When you call set_parameter for a different user, you've transferred ownership of that key to the other user. So only they can access it via sys_context/get_parameter.

So when you connect back as MG, using these methods to access the context returns null. Setting it again as MG switches ownership back to MG. So all MG's session can see the value. But none of TOM's sessions can.

If you want:

* Each database user to have their own values for a given key across all their sessions
* To use the same key for different users that connect at the same time

you need to set the client identifier to the same value. Such as their username.

create OR replace package my_pkg is

  procedure set_parameter ( 
    p_parameter in varchar2, 
    p_value     in varchar2
  );

  function get_parameter ( f_parameter in varchar2 ) return varchar2;

end;
/

-- + --
create or replace package body my_pkg is

  pk_ctx constant varchar2 (30) := 'MY_CTX';
  g_user constant varchar2 (30) := user;

  procedure set_parameter (
    p_parameter   in   varchar2,
    p_value       in   varchar2
  ) is
  begin
  
    dbms_session.set_identifier( g_user );
    
    dbms_session.set_context (
      namespace => pk_ctx,attribute => p_parameter,
      value => p_value, username => g_user,
      client_id => g_user
    );

  end set_parameter;

-- + --

  function get_parameter (
    f_parameter in varchar2
  ) return varchar2 is
  begin
    dbms_session.set_identifier( g_user );
    return (sys_context ( pk_ctx,f_parameter));
  end get_parameter;

-- + --

end my_pkg;
/

grant create session to u 
  identified by u;
  
grant execute on my_pkg to u;

execute my_pkg.set_parameter ( 'ASK-TOM', 'MG says: always' );

select *
from   global_context
where  namespace = 'MY_CTX'
/

NAMESPACE    ATTRIBUTE    VALUE              USERNAME    CLIENT_IDENTIFIER   
MY_CTX       ASK-TOM      MG says: always    CHRIS       CHRIS     

select chris.my_pkg.get_parameter ( 'ASK-TOM' ) from dual;

CHRIS.MY_PKG.GET_PARAMETER('ASK-TOM')   
MG says: always  

conn u/u

select chris.my_pkg.get_parameter ( 'ASK-TOM' ) from dual;

CHRIS.MY_PKG.GET_PARAMETER('ASK-TOM')   
<null>   

execute chris.my_pkg.set_parameter ( 'ASK-TOM', 'Chris says: per client id' );

select chris.my_pkg.get_parameter ( 'ASK-TOM' ) from dual;

CHRIS.MY_PKG.GET_PARAMETER('ASK-TOM')   
Chris says: per client id    

select *
from   global_context
where  namespace = 'MY_CTX'
/

NAMESPACE    ATTRIBUTE    VALUE                        USERNAME    CLIENT_IDENTIFIER   
MY_CTX       ASK-TOM      Chris says: per client id    U           U        

conn chris/chris

select chris.my_pkg.get_parameter ( 'ASK-TOM' ) from dual;

CHRIS.MY_PKG.GET_PARAMETER('ASK-TOM')   
MG says: always  

select *
from   global_context
where  namespace = 'MY_CTX'
/

NAMESPACE    ATTRIBUTE    VALUE              USERNAME    CLIENT_IDENTIFIER   
MY_CTX       ASK-TOM      MG says: always    CHRIS       CHRIS  

Rating

  (2 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

I think I'm on the way to get it ...

Maurice Gottlieb, June 11, 2019 - 3:43 pm UTC

Hi Chris, hi team!

I'm really impressed that you find the time to answer my question so quickly. Thank you so much!

I think I'm on the way to get it ...

I red the docs again and it seems I misinterpreted the words "as long as the username setting is the same throughout".

Our weekend was long here and Ask-Tom was "closed".

So I already tried to use the USER function as the client_id, but without the username context parameter and it works. But I don't know, if I'm on the right way.

With the changed package procedure in my_pkg:

procedure set_parameter ( p_parameter in varchar2, p_value in varchar2 ) is

begin

dbms_session.set_context ( namespace => pk_ctx, attribute => p_parameter, value => p_value, client_id => USER );

end set_parameter;


starting as user mg:
execute mg.my_pkg.set_parameter ( 'Ask-Tom', 'MG says: always' );

conn tom/tom
execute mg.my_pkg.set_parameter ( 'Ask-Tom', 'Tom says: what else' );

conn chris/chris
execute mg.my_pkg.set_parameter ( 'ASK-TOM', 'Chris says: per client id' );

col namespace for A20
col attribute for A20
col value     for A30
col username  for A10
col client_id for A16

select client_identifier client_id, namespace, attribute, value, username
from sys.V_$GLOBALCONTEXT
order by namespace, client_identifier, attribute
/


shows:

CLIENT_ID        NAMESPACE            ATTRIBUTE            VALUE                          USERNAME
---------------- -------------------- -------------------- ------------------------------ ----------
CHRIS            MY_CTX               ASK-TOM              Chris says: per client id
MG               MY_CTX               ASK-TOM              MG says: always
TOM              MY_CTX               ASK-TOM              Tom says: what else


and it seems I do not need to set the username context parameter.

Also I'm setting the session identifier
dbms_session.set_identifier( user );
once in the init section of the package. And this also works fine.

Thanks again and thumbs up for Ask-Tom!
Chris Saxon
June 11, 2019 - 4:20 pm UTC

You're welcome.

The key difference is that when you set the user, only that user can retrieve the values! Regardless of what the client_id is:

create OR replace package my_pkg is

  procedure set_ask_tom ( 
    p_value     in varchar2
  );
  
  procedure set_answer_tom ( 
    p_value     in varchar2
  );

  function get_parameter ( f_parameter in varchar2 ) return varchar2;

end;
/

-- + --
create or replace package body my_pkg is

  pk_ctx constant varchar2 (30) := 'MY_CTX';
  g_user constant varchar2 (30) := user;

  procedure set_ask_tom (
    p_value       in   varchar2
  ) is
  begin
  
    dbms_session.set_context (
      namespace => pk_ctx,attribute => 'ASK-TOM',
      value => p_value, 
      client_id => sys_context ( 'USERENV', 'CLIENT_IDENTIFIER' )
    );

  end set_ask_tom;
  
  procedure set_answer_tom (
    p_value       in   varchar2
  ) is
  begin
      
    dbms_session.set_context (
      namespace => pk_ctx,attribute => 'ANSWER-TOM',
      username => g_user,
      value => p_value, 
      client_id => sys_context ( 'USERENV', 'CLIENT_IDENTIFIER' )
    );

  end set_answer_tom;
  
-- + --

  function get_parameter (
    f_parameter in varchar2
  ) return varchar2 is
  begin
    return (sys_context ( pk_ctx,f_parameter));
  end get_parameter;

-- + --

end my_pkg;
/

grant create session to u 
  identified by u;
  
grant execute on my_pkg to u;

exec dbms_session.set_identifier( user );
    
execute my_pkg.set_ask_tom ( 'MG says: always' );
execute my_pkg.set_answer_tom ( 'Chris says: sometimes' );

select *
from   global_context
where  namespace = 'MY_CTX'
/

NAMESPACE   ATTRIBUTE    VALUE                   USERNAME   CLIENT_IDENTIFIER   
MY_CTX      ANSWER-TOM   Chris says: sometimes   CHRIS      CHRIS                
MY_CTX      ASK-TOM      MG says: always         <null>     CHRIS   

select chris.my_pkg.get_parameter ( 'ASK-TOM' ) from dual;

CHRIS.MY_PKG.GET_PARAMETER('ASK-TOM')   
MG says: always 

select chris.my_pkg.get_parameter ( 'ANSWER-TOM' ) from dual;

CHRIS.MY_PKG.GET_PARAMETER('ANSWER-TOM')   
Chris says: sometimes   

conn u/u

exec dbms_session.set_identifier ( 'CHRIS' );

select chris.my_pkg.get_parameter ( 'ASK-TOM' ) from dual;

CHRIS.MY_PKG.GET_PARAMETER('ASK-TOM')   
MG says: always   

select chris.my_pkg.get_parameter ( 'ANSWER-TOM' ) from dual;

CHRIS.MY_PKG.GET_PARAMETER('ANSWER-TOM')   
<null>    

select *
from   global_context
where  namespace = 'MY_CTX'
/

NAMESPACE   ATTRIBUTE    VALUE                   USERNAME   CLIENT_IDENTIFIER   
MY_CTX      ANSWER-TOM   Chris says: sometimes   CHRIS      CHRIS                
MY_CTX      ASK-TOM      MG says: always         <null>     CHRIS     


This gives an added level of security, ensuring only that specific user can access the values. Without this users could read each-others values by setting the client ID.

the DBMS_SESSION.SET_IDENTIFIER procedure ...

Maurice Gottlieb, June 11, 2019 - 4:45 pm UTC

Again thank you so much for your effort!

Because of the knowledge that only the context package is able to change any state of the context, I totally forgot that everybody can use
DBMS_SESSION.SET_IDENTIFIER
outside the package whenever she/he wants.

You've opened my eyes, Chris! Thank you!
Chris Saxon
June 11, 2019 - 5:00 pm UTC

You've got it! Glad this helped.

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library