The V$SESSION view contains the list of users currently logged in, but are they stored somewhere in a log table or something?
I need to get a list of yesterday users that where logged in with pl/sql (for instance)
By default Oracle doesn't store such information. You have a couple of options:
Enable auditing of system events (longon\logoff in this case).
Write system event(after longon, before logoff) trigger to gather that information.
Here are examples:
SQL> show parameter audit_trail
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
audit_trail string DB, EXTENDED
SQL> select sessionid
2 , userid
3 , decode(action#, 100, 'logon', 101, 'logoff')
4 from sys.aud$
5 where action# in (100, 101);
no rows selected
SQL> audit connect;
Audit succeeded.
SQL> conn hr/hr
Connected.
SQL> select sessionid
2 , userid
3 , decode(action#, 100, 'logon', 101, 'logoff')
4 from sys.aud$
5 where action# in (100, 101);
SESSIONID USERID DECODE
---------- ------------------------------ ------
1000712 HR logon
SQL> conn hr/hr
Connected.
SQL> select sessionid
2 , userid
3 , decode(action#, 100, 'logon', 101, 'logoff')
4 from sys.aud$
5 where action# in (100, 101);
SESSIONID USERID DECODE
---------- ------------------------------ ------
1000712 HR logon
1000712 HR logoff
1000713 HR logon
And example with triggers:
SQL> create table Logon_history(
2 sessionid number,
3 userid varchar2(31),
4 logon_date timestamp,
5 logoff_date timestamp
6 )
7 ;
Table created
SQL> create or replace trigger TR_LOGON_STAT after logon
2 on database
3 begin
4 insert into Logon_history(Sessionid, Userid, Logon_Date, Logoff_Date)
5 values(sys_context('userenv', 'sessionid'), user, systimestamp, null);
6 end;
7 /
Trigger created
SQL> create or replace trigger TR_LOGOFF_STAT before logoff
2 on database
3 begin
4 insert into Logon_history(Sessionid, Userid, Logon_Date, Logoff_Date)
5 values(sys_context('userenv', 'sessionid'), user, null,systimestamp);
6 end;
7 /
Trigger created
SQL> select sessionid
2 , userid
3 , logon_date
4 , logoff_date
5 from Logon_history
6 ;
no rows selected
SQL> conn hr/hr
Connected.
SQL> select sessionid
2 , userid
3 , logon_date
4 , logoff_date
5 from Logon_history
6 ;
Sessionid Userid Logon_Date Logoff_Date
--------------------------------------------------------------------------------
2490674 HR 01-NOV-12 11.46.23.421000 PM
2490672 HR 01-NOV-12 11.46.23.343000 PM
Related
I'm running into an oracle error,
ORA-00933: SQL command not properly ended
With the follwing.
insert into TableOne (name, description, scopeid, readonly)
Select 'access', 'Some Description', 0, 0 from dual
where not exists(SELECT * FROM Privilege WHERE name = 'access')
/
insert into TableTwo (name, uuid, description, scopeid)
Select 'Role','ROLE_UUID','Another description.', 0 from dual
where not exists(SELECT * FROM Role WHERE uuid = 'ROLE_UUID')
/
I have added semicolons at the end of each statement before the '/'.
Any suggestions where I may be wrong?
You didn't post CREATE TABLE statements so I did that myself.
SQL> create table privilege as
2 select 'some name' name from dual;
Table created.
SQL> create table role as
2 select 'some UUID' uuid from dual;
Table created.
SQL> create table tableone
2 (name varchar2(10),
3 description varchar2(20),
4 scopeid number,
5 readonly number);
Table created.
SQL> create table tabletwo
2 (name varchar2(10),
3 uuid varchar2(10),
4 description varchar2(20),
5 scopeid number);
Table created.
SQL>
Let's run insert statements you posted as exact copy/paste (I didn't change anything):
SQL> insert into TableOne (name, description, scopeid, readonly)
2 Select 'access', 'Some Description', 0, 0 from dual
3 where not exists(SELECT * FROM Privilege WHERE name = 'access')
4 /
1 row created.
SQL> insert into TableTwo (name, uuid, description, scopeid)
2 Select 'Role','ROLE_UUID','Another description.', 0 from dual
3 where not exists(SELECT * FROM Role WHERE uuid = 'ROLE_UUID')
4 /
1 row created.
SQL>
Apparently, both of them work and no ORA-00933 (SQL command not properly ended) is raised. Therefore, either you didn't post everything you should have, or you're misinterpreting reality.
I have a table in oracle 11.2.0.3 that I want to capture in the redo logs. The issue is that it has an sdo_geometry field. This is a legacy table that I can not change. But the good news is I do not need that sdo_geometry field.
So I have created a materialized view as shown below.
CREATE MATERIALIZED VIEW LOG ON LEGACY_TABLE_NAME
WITH PRIMARY KEY
INCLUDING NEW VALUES;
CREATE MATERIALIZED VIEW LEGACY_TABLE_NAME_MV
NOLOGGING
NOCACHE
BUILD IMMEDIATE
REFRESH FAST ON COMMIT
WITH PRIMARY KEY
AS
SELECT <List of non sdo_gemoetry columns> FROM LEGACY_TABLE_NAME;
The issue shows up when I do an update and look at the redo logs. Instead of seeing an update statement, I see a delete and insert statements. Since I am using a primary key, I would expect to see the update statement.
Does anyone know what I need to do to ensure that I see an update statement in the redo logs.
Thanks
I think you are misunderstanding what a redolog stores with what a materiliazed view log does.
Let's try to make a test for both scenarios:
LogMiner to verify the content of the redo logfiles, something we can see using v$LOGMNR_CONTENTS.
Example of Materialized view Log and Update operations
Oracle version: 12.2
RedoLog Contents
SQL> create table cpl_rep.test_redo_logs ( c1 number primary key , c2 number ) ;
Table created.
SQL> insert into cpl_rep.test_redo_logs values ( 1 , 1 );
1 row created.
SQL> insert into cpl_rep.test_redo_logs values ( 2 , 2 );
1 row created.
SQL> commit ;
Commit complete.
SQL> update cpl_rep.test_redo_logs set c1=3 , c2=3 where c1 = 2 ;
1 row updated.
SQL> commit ;
Commit complete.
SQL> select * from cpl_rep.test_redo_logs ;
C1 C2
---------- ----------
1 1
3 3
SQL> exit
Disconnected from Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit
Production
$ sqlplus / as sysdba
SQL*Plus: Release 12.2.0.1.0 Production on Sat Aug 8 21:53:05 2020
Copyright (c) 1982, 2016, Oracle. All rights reserved.
Connected to:
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
SQL> alter system switch logfile ;
System altered.
SQL> exit
Disconnected from Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit
Production
Now let's start a LogMiner session by loading the redo log files into LogMiner:
SQL> exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo1/redo11.ora' , 1);
SQL> exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo2/redo21.ora' , 1);
SQL> exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo1/redo12.ora' , 1);
SQL> exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo2/redo22.ora' , 1);
SQL> exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo1/redo13.ora' , 1);
SQL> exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo2/redo23.ora' , 1);
PL/SQL procedure successfully completed.
PL/SQL procedure successfully completed.
PL/SQL procedure successfully completed.
PL/SQL procedure successfully completed.
PL/SQL procedure successfully completed.
PL/SQL procedure successfully completed.
SQL> exec dbms_logmnr.start_logmnr(options=>dbms_logmnr.dict_from_online_catalog);
PL/SQL procedure successfully completed.
SQL> select count(*) from v$logmnr_contents where seg_name = upper('test_redo_logs') ;
COUNT(*)
----------
4
SQL> select sql_redo , seg_name from v$logmnr_contents where seg_name = upper('test_redo_logs') ;
SQL_REDO
--------------------------------------------------------------------------------
SEG_NAME
--------------------------------------------------------------------------------
create table cpl_rep.test_redo_logs ( c1 number primary key , c2 number ) ;
TEST_REDO_LOGS
insert into "CPL_REP"."TEST_REDO_LOGS"("C1","C2") values ('1','1');
TEST_REDO_LOGS
insert into "CPL_REP"."TEST_REDO_LOGS"("C1","C2") values ('2','2');
TEST_REDO_LOGS
SQL_REDO
--------------------------------------------------------------------------------
SEG_NAME
--------------------------------------------------------------------------------
update "CPL_REP"."TEST_REDO_LOGS" set "C1" = '3', "C2" = '3' where "C1" = '2' an
d "C2" = '2' and ROWID = 'AAGKh2AAAAAJIH1AAB';
TEST_REDO_LOGS
As you can see above, the UPDATE appears normally as any other DML operation in the SQL_REDO column of V$LOGMNR_CONTENTS. So, obviously the REDO files store any update operation as long as the operation is done in Logging mode, or the database is in FORCE LOGGING MODE, in which case it doesn't matter what mode the operation is done, because it will always be stored.
Materialized View Log
Let's create a materialized view log and a materialized view as you did in your question. However, to verify the content of the MLOG$ tables, I will put the refresh on demand, instead of on commit.
SQL> create table x ( c1 number primary key , c2 number ) ;
Table created.
SQL> insert into x values ( 1 , 1 ) ;
1 row created.
SQL> insert into x values ( 2 , 2 );
1 row created.
SQL> commit ;
Commit complete.
SQL> create materialized view log on x with primary key including new values ;
Materialized view log created.
SQL> create materialized view mv_x nologging nocache build immediate refresh fast on demand with primary key as select c1 , c2 from x ;
Materialized view created.
SQL> select * from x ;
C1 C2
---------- ----------
1 1
2 2
SQL> select * from mv_x ;
C1 C2
---------- ----------
1 1
2 2
SQL> insert into x values ( 3 , 3 );
1 row created.
SQL> commit ;
Commit complete.
SQL> update x set c1=4 , c2=4 where c1=3 ;
1 row updated.
SQL> commit ;
Commit complete.
As we did create the materialized view with refresh on demand, now let's the content of the MLOG$ table
SQL> select * from x ;
C1 C2
---------- ----------
1 1
2 2
4 4
SQL> select * from mv_x ;
C1 C2
---------- ----------
1 1
2 2
SQL> select * from mlog$_x
C1 SNAPTIME$ D O CHANGE_VEC XID$$
---------- --------- - - ---------- ----------------------------
3 01-JAN-00 I N FE 39406677128122001
3 01-JAN-00 D O 00 44473269658586765
4 01-JAN-00 I N FF 44473269658586765
Then I refresh
SQL> select * from x ;
C1 C2
---------- ----------
1 1
2 2
4 4
SQL> select * from mv_x ;
C1 C2
---------- ----------
1 1
2 2
SQL> exec dbms_mview.refresh('MV_X') ;
PL/SQL procedure successfully completed.
SQL> select * from mv_x ;
C1 C2
---------- ----------
1 1
2 2
4 4
SQL> select * from mlog$_x
2 ;
no rows selected
The reason why you don't see UPDATE on DMLTYPE$$ is because you choose Primary Key as WITH clause in your Materialized View creation. In that case, only D or I will appear in the column DMLTYPE$$ , but when it is an update, you will get two rows with the same transaction ID ( XID$$ field in the example above has the same value )
However, check what happen when I use ROWID instead of PRIMARY KEY
SQL> create materialized view log on x with rowid including new values ;
Materialized view log created.
SQL> create materialized view mv_x nologging nocache build immediate refresh fast on demand with rowid as select c1 , c2 from cpl_rep.x ;
Materialized view created.
SQL> select * from cpl_rep.mv_x ;
C1 C2
---------- ----------
1 1
2 2
3 3
SQL> select * from x ;
C1 C2
---------- ----------
1 1
2 2
3 3
SQL> insert into x values ( 4 , 4 ) ;
1 row created.
SQL> commit ;
Commit complete.
SQL> insert into x values ( 5 , 5 );
1 row created.
SQL> commit ;
Commit complete.
SQL> update x set c1=6 , c2=6 where c1=5 ;
1 row updated.
SQL> commit ;
Commit complete.
SQL> select * from x ;
C1 C2
---------- ----------
1 1
2 2
3 3
4 4
6 6
SQL> select * from mv_x ;
C1 C2
---------- ----------
1 1
2 2
3 3
Let' see the content of the M$LOG table now
SQL> col m_row$$ for a18
SQL> select * from mlog$_x ;
M_ROW$$ SNAPTIME$ D O CHANGE_VEC XID$$
------------------ --------- - - ---------- ----------------------------
AAGKh/AAAAAJJWnAAD 01-JAN-00 I N FE 3659458165104006
AAGKh/AAAAAJJWnAAE 01-JAN-00 I N FE 44754731750395757
AAGKh/AAAAAJJWnAAE 01-JAN-00 U U 06 12948119511653544
AAGKh/AAAAAJJWnAAE 01-JAN-00 U N 06 12948119511653544
I have now 4 rows, 2 for the inserts, 1 update for the field C1 and another for the field C2, which are in fact the same transaction ( field XID$$ )
I hope it clarifies how the MLOG$ tables are populated when you choose ROWID or PRIMARY KEYY. Note that materialized view log tables using primary keys also have rupd$_ tables. The rupd$_ table supports updateable materialized views, which are only possible on log tables with primary keys.
My requirement is to write a query in oracle
"which fetch userids from users table & insert into USERQueries table"
USERS
USERID GPID GROUP
1682 1026 IBMSDL2S
1882 1028 IBMSDL2S
1573 1029 IBMSDL2S
1342 1124 IBMSDL2S
1976 2576 IBMSDL2S
1883 2575 IBMSDL2S
1854 2574 IBMSDL2S
2222 2573 IBMSDL2S
2207 2572 IBMSDL2S
USERQueries
APP CLAUSENAME USERID DEFAULTQUERYID OWNER
SR Assgined_SRs_To_Me 1249 545 MAXADMIN
SR Assgined_SRs_To_Me 1682 543 MAXADMIN
I am able to insert one userid as below
insert into USERSQUERIES
(APP,CLAUSENAME,USERID,DEFAULTQUERYID,OWNER)
values
('SR','Assgined_SRs_To_Me',(select userid from USERS where groupname='IBMSDL2S' and userid='1249
),DEFAULTQUERYSEQ.NEXTVAL,'MAXADMIN')
but didn't understand how it will work for all userids
You'd write a SELECT statement that fetches data you're interested in, and then use it in INSERT statement. Have a look at the following example:
Test case first:
SQL> create table users as
2 select 1682 userid, 1026 gpid, 'IBMSDL25' groupname from dual union all
3 select 1882 userid, 1028 gpid, 'IBMSDL25' groupname from dual union all
4 select 2222 userid, 2222 gpid, 'XXXXXX25' groupname from dual; --> will NOT be inserted
Table created.
SQL> create table usersqueries (app varchar2(2),
2 clausename varchar2(20), userid number, defaultqueryid number, owner
3 varchar2(20));
Table created.
SQL> create sequence defaultqueryseq;
Sequence created.
SQL>
Testing: starting at line #3 is the SELECT statement I mentioned previously.
SQL> insert into usersqueries
2 (app, clausename, userid, defaultqueryid, owner)
3 select 'SR',
4 'Assigned_SRs_to_me',
5 u.userid,
6 defaultqueryseq.nextval,
7 'MAXADMIN'
8 from users u
9 where u.groupname = 'IBMSDL25';
2 rows created.
SQL> select * From usersqueries;
AP CLAUSENAME USERID DEFAULTQUERYID OWNER
-- -------------------- ---------- -------------- --------------------
SR Assigned_SRs_to_me 1682 1 MAXADMIN
SR Assigned_SRs_to_me 1882 2 MAXADMIN
SQL>
Like to get some views from you all, regarding one scenario i'm struggling with currently. Below is a problem statement
I have Table A , B , C
A has below column
user|modified date| wokred_on A | ..some more related to user operation
B has columns
user | modified date | worked on B | ..some other columns
C has columsn
user | modified date | worked on C| ..some other columns
these tables are not have any direct relation except then user.
we have to pull data from these tables for a user between given dates with the count op action or work he has done between a given date range?
my struggle here is these each table has it's own date modified if a date range selected which is not in other column but still i need to pull the data as user has worked on it in between dates.
can it be possible to select these dates and have the in one column so that one can put that in where clause and having outer joins to pull other records ?
Sorry for this big problem statement. any suggestions are very much appreciated
Below is a use case.just extending the assumption given by littlefoot
First, test case:
SQL> create table a (cuser varchar2(10), modified_date date,action );
varchar2 (10) )
Table created.
SQL> create table b (
Table created.cuser varchar2(10), modified_date date,action
varchar2 (10) );
SQL> create table c (cuser varchar2(10), modified_date date,action
varchar2 (10) ));
Table created.
SQL> insert into a values ('lf', date '2018-05-01', 'issue raised');
1 row created.
SQL> insert into a values ('mc', date '2018-05-01', 'issue raised ');
1 row created.
SQL> insert into b values ('lf', date '2018-05-01',issue raised');
1 row created.
SQL> insert into b values ('lf', date '2018-05-01','issue resolved');
1 row created.
SQL> insert into c values ('if', date '2018-05-28',' issue resolved');
1 row created.
SQL> insert into c values ('mc', date '2018-05-13','issue raised');
1 row created.
SQL> insert into c values ('mc', date '2018-05-13','issue resolved');
1 row created.
SQL> alter session set nls_date_format = 'yyyy-mm-dd';
Session altered.
SQL> select * from a;
CUSER MODIFIED_D. ACTION
---------- ----------
lf 2018-05-01 issue raised
mc 2018-05-01 issue raised
SQL> select * from b;
CUSER MODIFIED_D ACTION
---------- ----------. ______________
lf 2018-05-01 issue raised
lf 2018-05-01. issue resolve
SQL> select * from c;
CUSER MODIFIED_D. ACTION
---------- ----------
If 2018-05-28. issue resolve
mc 2018-05-13. issue raised
mc 2018-05-13. issue resolve
CUSER DATE CNT_ISSUE_RAISED CNT_ISSUE_RESOLVED
------ ------- --------------- -------------------
if 2018-05-01 2 1
lf 2018-05-28 0 1
mc 2018-05-01 0 1
mc 2018-05-13 1 1
This is how I understood the question.
First, test case:
SQL> create table a (cuser varchar2(10), modified_date date);
Table created.
SQL> create table b (cuser varchar2(10), modified_date date);
Table created.
SQL> create table c (cuser varchar2(10), modified_date date);
Table created.
SQL> insert into a values ('lf', date '2018-05-01');
1 row created.
SQL> insert into a values ('mc', date '2018-05-15');
1 row created.
SQL> insert into b values ('lf', date '2018-05-07');
1 row created.
SQL> insert into b values ('lf', date '2018-05-08');
1 row created.
SQL> insert into c values ('jw', date '2018-05-28');
1 row created.
SQL> insert into c values ('mc', date '2018-05-13');
1 row created.
SQL> insert into c values ('mc', date '2018-05-22');
1 row created.
SQL> alter session set nls_date_format = 'yyyy-mm-dd';
Session altered.
SQL> select * from a;
CUSER MODIFIED_D
---------- ----------
lf 2018-05-01
mc 2018-05-15
SQL> select * from b;
CUSER MODIFIED_D
---------- ----------
lf 2018-05-07
lf 2018-05-08
SQL> select * from c;
CUSER MODIFIED_D
---------- ----------
jw 2018-05-28
mc 2018-05-13
mc 2018-05-22
Query which returns desired result - number of rows per each user in every table, in desired date period. As I use SQL*Plus, variables are preceded by && to avoid multiple insert requests. In a tool you use, that might be a colon (:).
SQL> select nvl(nvl(a.cuser, b.cuser), c.cuser) cuser,
2 count(distinct a.modified_date) cnt_a,
3 count(distinct b.modified_date) cnt_b,
4 count(distinct c.modified_date) cnt_c
5 from a full outer join b on a.cuser = b.cuser
6 full outer join c on a.cuser = c.cuser
7 where a.modified_date between &&date_from and &&date_to
8 or b.modified_date between &&date_from and &&date_to
9 or c.modified_date between &&date_from and &&date_to
10 group by nvl(nvl(a.cuser, b.cuser), c.cuser)
11 order by 1;
Enter value for date_from: '2018-05-01'
Enter value for date_to: '2018-06-01'
CUSER CNT_A CNT_B CNT_C
---------- ---------- ---------- ----------
jw 0 0 1
lf 1 2 0
mc 1 0 2
SQL>
Using Oracle VPD, after adding a policy and creating a function, I was able to hide a column from unauthorized users.
But instead of (null) how can i show something like 'xxxxxx'
Also in the function I am validation for the user login, like
if sys_context( 'userenv', 'session_user' ) = 'USER1'
what is the best approach to remove this hard coding in the function?
Thanks in advance.
in order to return text in the place of not null, you'd have to create a view over top of the table to change null into the static literal you wanted, as the only option in VPD would be to hide the rows or set the secret columns to NULL.
for your second part of your question, if you are using that check to determine who has access to the sensitive columns, you can use a role instead and have the VPD function check this like:
return 'exists (select null from session_roles where role = ''XXXXXX'')';
i.e. whomever has the role XXXXXX (just create an appropriate role and grant it to your privileged users) set in their session can see the data. That way you don't need to hard code a bunch of user ids.
e.g:
if we create a role and grant it to a test user:
SQL> create role ACCESS_TABLEA_SEC_COL;
Role created.
SQL> grant ACCESS_TABLEA_SEC_COL to test;
Grant succeeded.
for my set up ive created a simple test table + a policy that stops people reading the your_sec_col column.
SQL> create or replace package pkg_security_control
2 as
3 function apply_access(p_owner in varchar2, p_obj_name in varchar2) return varchar2;
4 end;
5 /
Package created.
SQL> create or replace package body pkg_security_control
2 as
3 function apply_access(p_owner in varchar2, p_obj_name in varchar2)
4 return varchar2
5 is
6 begin
7 return 'exists (select null from session_roles where role = ''ACCESS_TABLEA_SEC_COL'')';
8 end;
9 end;
10 /
Package body created.
SQL> create table TABLEA
2 (
3 id number primary key,
4 your_sec_col varchar2(30)
5 );
Table created.
SQL> insert into tablea values (1, 'secret text1');
1 row created.
SQL> insert into tablea values (2, 'secret text2');
1 row created.
now if we select from that table and we don't have the ACCESS_TABLEA_SEC_COL role, we'd get:
SQL> select *
2 from tablea;
ID YOUR_SEC_COL
---------- ------------------------------
1
2
but you want a string like xxxxx. VPD itself cannot do this, but a view could decode NULL to that string.
SQL> create view v_tablea
2 as
3 select id, case when your_sec_col is null then 'xxxxxx' else your_sec_col end your_sec_col
4 from TABLEA;
View created.
now selecting from the view will , depending on whether the role is set:
SQL> set role none;
Role set.
SQL> select *
2 from tablea;
ID YOUR_SEC_COL
---------- ------------------------------
1
2
SQL> select *
2 from v_tablea;
ID YOUR_SEC_COL
---------- ------------------------------
1 xxxxxx
2 xxxxxx
SQL> set role all;
Role set.
SQL> select *
2 from v_tablea;
ID YOUR_SEC_COL
---------- ------------------------------
1 secret text1
2 secret text2
SQL> select *
2 from tablea;
ID YOUR_SEC_COL
---------- ------------------------------
1 secret text1
2 secret text2
so VPD still protects your table against anyone selecting from it, but you'd have clients select from the view to get the literal string instead. If your protected strings can contain NULL, and you want to differentiate those from no access, you can put the role check in the view instead.
create view v_tablea
as
select id,
case (select 'A' from session_roles where role = 'ACCESS_TABLEA_SEC_COL')
when 'A' then your_sec_col else 'xxxxxxxx' end your_sec_col
from TABLEA;