Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Ramasamy.

Asked: October 02, 2003 - 9:01 am UTC

Last updated: May 30, 2012 - 12:43 am UTC

Version: 9.2.0.1

Viewed 10K+ times! This question is

You Asked

Tom ,

Could you please mention what privilege i need to get.

Documentation says flashback against data dictionary view are allowed as below.

You cannot retrieve past data from a V$ view in the data dictionary.
Performing a flashback query on such a view just returns
the current data.
You can perform flashback queries on other views
in the data dictionary, such as USER_TABLES.


SQL> select * from t1 as of timestamp (sysdate -1);

N1 N2
---------- ----------
2

SQL> select text from user_source as of timestamp (sysdate -1)
2 where name like 'SP_CREATE_BOOKING_TRAN%'
3 /
select text from user_source as of timestamp (sysdate -1)
*
ERROR at line 1:
ORA-01031: insufficient privileges

SQL> select * from V$version;

BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
PL/SQL Release 9.2.0.1.0 - Production
CORE 9.2.0.1.0 Production
TNS for Linux: Version 9.2.0.1.0 - Production
NLSRTL Version 9.2.0.1.0 - Production

SQL>

I made sure i have DBA privilege. What do i need to get to have
query against user_source. As you can see it is working for normal
tables.

Thank you very much.

Ramasamy T

and Tom said...

this is related to the O7_DICTIONARY_ACCESSIBILITY init.ora parameter.


Very quickly, if you log in as SYSDBA and:

sys@ORA920> grant flashback on user_source to ops$tkyte;
Grant succeeded.

then you will observe:

1* select 'Yes' from user_source as of timestamp(sysdate-1) where rownum = 1
ops$tkyte@ORA920> /

'YE
---
Yes

ops$tkyte@ORA920> select 'Yes' from dual as of timestamp(sysdate-1) ;
select 'Yes' from dual as of timestamp(sysdate-1)
*
ERROR at line 1:
ORA-01031: insufficient privileges


(dual is a dictionary object as well). This happens because the dictionary (sys owned things) are prevented from being accessed by the "general" grants like "flashback any table", "select any table". This is for security.

This is the default when:

ops$tkyte@ORA920> show parameter o7

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
O7_DICTIONARY_ACCESSIBILITY boolean FALSE

the default setting is in place. If we change it:

ops$tkyte@ORA920> show parameter o7

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
O7_DICTIONARY_ACCESSIBILITY boolean TRUE
ops$tkyte@ORA920> select * from dual as of timestamp(sysdate-1);

D
-
X


We have full unfettered access to the data dictionary at the cost of much less security!!! i do not recommend this second approach at all!


Rating

  (22 ratings)

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

Comments

Flashback Table in 10g

reader, November 11, 2004 - 12:19 pm UTC

Is Flashback Table feature in 10g applicable ONLY for regular/heap tables? Thanks.

Tom Kyte
November 11, 2004 - 3:04 pm UTC

No:

ops$tkyte@ORA10G> create table flashback_table
  2  ( empno primary key, ename, job, hiredate, sal, comm, deptno )
  3  organization index
  4  as
  5  select empno, ename, job, hiredate, sal, comm, deptno
  6  from scott.emp;
 
Table created.
 
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> pause
 
ops$tkyte@ORA10G>
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> column scn new_val s
ops$tkyte@ORA10G> select to_char(dbms_flashback.get_system_change_number,'99999999999999999999') SCN from dual; 
SCN
---------------------
        8204828763322
 
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> update flashback_table set sal = sal * 1.5;
 
14 rows updated.
 
ops$tkyte@ORA10G> commit;
 
Commit complete.
 
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> select a.ename, a.sal new_sal, b.sal old_sal
  2    from flashback_table a, flashback_table as of SCN &S b
  3   where a.empno = b.empno
  4  /
old   2:   from flashback_table a, flashback_table as of SCN &S b
new   2:   from flashback_table a, flashback_table as of SCN         8204828763322 b
 
ENAME         NEW_SAL    OLD_SAL
---------- ---------- ----------
SMITH            1200        800
ALLEN            2400       1600
WARD             1875       1250
JONES          4462.5       2975
MARTIN           1875       1250
BLAKE            4275       2850
CLARK            3675       2450
SCOTT            4500       3000
KING             7500       5000
TURNER           2250       1500
ADAMS            1650       1100
JAMES            1425        950
FORD             4500       3000
MILLER           1950       1300
 
14 rows selected.
 
ops$tkyte@ORA10G> flashback table flashback_table to scn &S;
old   1: flashback table flashback_table to scn &S
new   1: flashback table flashback_table to scn         8204828763322
 
Flashback complete.
 
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> select ename, sal from flashback_table;
 
ENAME             SAL
---------- ----------
SMITH             800
ALLEN            1600
WARD             1250
JONES            2975
MARTIN           1250
BLAKE            2850
CLARK            2450
SCOTT            3000
KING             5000
TURNER           1500
ADAMS            1100
JAMES             950
FORD             3000
MILLER           1300
 
14 rows selected.



<b>that aside, it does not work on:

cluster's
mv's (flashback base tables, refresh works...)
aq tables
dictionary tables (seems reasonable :)
remote tables (log in remotely...)
object types
nested tables
partitions (entire table, yes, single partition -- no)</b> 

Thanks

A reader, November 11, 2004 - 4:02 pm UTC


Please clarify

Ram, August 31, 2008 - 1:56 pm UTC

Hi Tom,
Do we lose any flashback features after a database restart?


Tom Kyte
September 01, 2008 - 12:57 pm UTC

no

What prerequisites are necessary for Flashback?

Noe Hoyos, May 27, 2009 - 10:08 am UTC

Is ROW MOVEMENT required to be active to use flashback in Oracle 10g?
Tom Kyte
May 27, 2009 - 11:41 am UTC

flashback covers many "things", you'd need to be more specific...

flashback query - select * from table "as of sometime in the past". This does not require row movement, only required undo that was generated at that point in time in the past still be present (in 10g and before, in 11g with the flashback data archive - that is not a requirement anymore)


flashback versions query - same as above

flashback transaction query - needs a grant " SELECT ANY TRANSACTION" and the above (undo needs be present)

flashback table TO PRIOR POINT IN TIME - needs the undo AND row movement. This works by deleting data that doesn't belong in the table anymore and inserting data that needs to be there - hence a rowid could change for a row - rows move.

flashback table to before drop - needs the table to be in the recycle bind, no row movement, no undo needed

flashback database - needs sufficient data (block images and redo) in the flash recovery area, no row movement.

related privilege confusion

steve, October 08, 2009 - 3:57 pm UTC

I'm confused on where the ORA-01031 is coming from and why using a view alleviates it. Is there really a privilege missing somewhere? flbk_tab is another schema, is that the issue?
TRD@dev>select trunc(sysdate) + (flashback_timestamp - trunc(flashback_timestamp)) as hr
  2  from flbk_tab.flshbk_user_time
  3  where user_label = 'WWW';

HR
-----------------
10/08/09 13:50:05

TRD@dev>select trunc(sysdate) + (flashback_timestamp - trunc(flashback_timestamp)) as hr
  2  from flbk_tab.flshbk_user_time as of timestamp to_timestamp('10/08/2009 10:45:00', 'MM/DD/YYYY HH24:MI:SS')
  3  where user_label = 'WWW';
from flbk_tab.flshbk_user_time as of timestamp to_timestamp('10/08/2009 10:45:00', 'MM/DD/YYYY HH24:MI:SS')
                  *
ERROR at line 2:
ORA-01031: insufficient privileges


TRD@dev>create view ccc as
  2  select trunc(sysdate) + (flashback_timestamp - trunc(flashback_timestamp)) as hr
  3  from flbk_tab.flshbk_user_time
  4  where user_label = 'WWW';

View created.

TRD@dev>select *
  2  from ccc as of timestamp to_timestamp('10/08/2009 10:45:00', 'MM/DD/YYYY HH24:MI:SS');

HR
-----------------
10/08/09 00:45:00

TRD@dev>


Thanks,
Steve

flashback table: which one is flashed back ?

Sokrates, November 05, 2009 - 5:43 am UTC

SQL> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
PL/SQL Release 11.2.0.1.0 - Production
CORE    11.2.0.1.0      Production
TNS for Linux: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production

SQL> select object_name, original_name from user_recyclebin;

no rows selected

SQL> alter session set recyclebin=on;

Session altered.

SQL> create table whichone( first int );

Table created.

SQL> drop table whichone;

Table dropped.

SQL> create table whichone( second int );

Table created.

SQL> drop table whichone;

Table dropped.

SQL> create table whichone( third int );

Table created.

SQL> drop table whichone;

Table dropped.

SQL> select object_name, original_name from user_recyclebin;

OBJECT_NAME                    ORIGINAL_NAME
------------------------------ --------------------------------
BIN$d58eC3Dd1trgQBwK3yg0dQ==$0 WHICHONE
BIN$d58eC3Dc1trgQBwK3yg0dQ==$0 WHICHONE
BIN$d58eC3Db1trgQBwK3yg0dQ==$0 WHICHONE

SQL> flashback table whichone to before drop;

Flashback complete.

SQL> describe whichone
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 THIRD                                              NUMBER(38)



So, the lastly dropped table was flashbacked.

Questions:
- is this behaviour deterministic ? (couldn't find anything about this in the Docs)
- how to flashback the second table in one statement ? how to flashback the first table in one statement ?


Tom Kyte
November 11, 2009 - 9:41 am UTC

funny, my documentation seemed unambiguous on this :)

http://docs.oracle.com/docs/cd/B19306_01/server.102/b14200/statements_9012.htm#sthref8941

<quote>

You can specify either the original user-specified name of the table or the system-generated name Oracle Database assigned to the object when it was dropped.

*

System-generated recycle bin object names are unique. Therefore, if you specify the system-generated name, then the database retrieves that specified object. tom says: therefore, if you want to go back to the oldest table in a single flashback command, use the recyclebin name - not the table name...

To see the contents of your recycle bin, query the USER_RECYCLEBIN data dictionary review. You can use the RECYCLEBIN synonym instead. The following two statements return the same rows:

SELECT * FROM RECYCLEBIN;
SELECT * FROM USER_RECYCLEBIN;


*

If you specify the user-specified name, and if the recycle bin contains more than one object of that name, then the database retrieves the object that was moved to the recycle bin most recently. If you want to retrieve an older version of the table, do one of these things:
o

Specify the system-generated recycle bin name of the table you want to retrieve.
o

Issue additional FLASHBACK TABLE ... TO BEFORE DROP statements until you retrieve the table you want.


</quote>

sorry

Sokrates, November 12, 2009 - 2:58 am UTC

I was blind

Can we Undo users activities using flashback?

Giridhar, January 31, 2011 - 9:14 am UTC

Tom,

While reading this article given below, I was thinking on how can we implement "undo" feature of any users transaction, if required. Can Flashback features be used to achieve "undo" of any user actions? Say user deleted a row and if we give a button next to delete similar to gmail "undo", and if user clicks that undo button,can flashback feature help us to get data back all the time?

" http://www.alistapart.com/articles/neveruseawarning"

Thanks


Tom Kyte
February 01, 2011 - 4:40 pm UTC

Yes, you can implement an "undo this operation" - in fact we do it for you:

http://docs.oracle.com/docs/cd/E11882_01/appdev.112/e17125/adfns_flashback.htm#ADFNS1010

Shirley, March 01, 2011 - 4:20 pm UTC

Hi Tom,

I was given a task to write a procedure to flashback all tables under one schema to a predefined restore point. One thing comes up is how Oracle takes care of two tables with foreign key relationship. So I went ahead to do some testing. Our database is 11.1.0.6.0.

1. Two tables are created (syntax may not correct):
Dept
(Dept_id integer primary key,
Dept_name varchar2(30),
Loc varchar2(30))
Emp
(emp_id number(6),
First_name varchar2(30),
Last_name varchar2(30),
Dept_id integer reference dept(dept_id)

2. Insert testing data:
Insert into dept values (1, ‘DEPT 1’,’LOC 1’);
Insert into dept values (2, ‘DEPT 2’,’LOC 2’);
Insert into emp values (1,’first name 1’, ‘last name 1’,1);
Insert into emp values (2,’first name 2’, ‘last name 2’,1);
Insert into emp values (3,’first name 3’, ‘last name 3’,2);
Insert into emp values (4,’first name 4’, ‘last name 4’,2);
Commit;

3. Create restore point:
Create restore point test_save1;

4. Make some data changes:
Insert into dept values (3, ‘DEPT 3’,’LOC 3’);
Insert into emp values (5,’first name 5’, ‘last name 5’,3);
Delete from emp where dept_id =1;
Delete from dept where dept_id =1;
Commit;

5. Tried to flashback both dept and emp tables to test_save1 but failed. I tried to change the order of the flashback emp and dept but no luck. Then I tried to disabled the foreign key constraint in emp table and I was able to flashback dept table but could not flashback emp table since “ORA-01466: table definition has changed”.

Flashback table dept to restore point test_save1;
Flashback table emp to restore point test_save1;

Could you please let me know if I have missed anything? Is there any way to work around and to accomplish what I am trying to do?

Thank you so much!

Shirley

Tom Kyte
March 01, 2011 - 5:18 pm UTC

ops$tkyte%ORA11GR2> create restore point test_save1;

Restore point created.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> select count(*) from scott.emp;

  COUNT(*)
----------
        14

ops$tkyte%ORA11GR2> select count(*) from scott.dept;

  COUNT(*)
----------
         4

ops$tkyte%ORA11GR2> insert into scott.emp(empno,deptno) values ( 123, 10 );

1 row created.

ops$tkyte%ORA11GR2> delete from scott.emp where deptno = 20;

5 rows deleted.

ops$tkyte%ORA11GR2> delete from scott.dept where deptno = 20;

1 row deleted.

ops$tkyte%ORA11GR2> commit;

Commit complete.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> select count(*) from scott.emp;

  COUNT(*)
----------
        10

ops$tkyte%ORA11GR2> select count(*) from scott.dept;

  COUNT(*)
----------
         3

ops$tkyte%ORA11GR2> flashback table scott.emp, scott.dept to restore point test_save1;

Flashback complete.

ops$tkyte%ORA11GR2> select count(*) from scott.emp;

  COUNT(*)
----------
        14

ops$tkyte%ORA11GR2> select count(*) from scott.dept;

  COUNT(*)
----------
         4

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> drop restore point test_save1;

Restore point dropped.



flashback the tables in a single command - all integrity constraints are known to be "ok" if they are all flashed back to the same point in time.

flashback table

Shirley, March 01, 2011 - 8:20 pm UTC

Thank you very much!

It is good to know you can use this method to avoid foreign key errors when doing flashback tables.


flashback tables

Shirley, March 02, 2011 - 9:03 am UTC

Just one more question. Do you have a limit on how many tables can be included in the table list or how long this table list string can be in this flashback statement?

Thank you!
Tom Kyte
March 02, 2011 - 10:13 am UTC

no documented limit - here is a 1000 table test


ops$tkyte%ORA11GR2> set echo off
ops$tkyte%ORA11GR2> spool off
ops$tkyte%ORA11GR2> spool x
ops$tkyte%ORA11GR2> @test 1000
ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> declare
  2          l_stmt long;
  3  begin
  4          for i in 1 .. &1
  5          loop
  6                  l_stmt := 'create table t' ||
  7                  to_char(i,'fm0000000000000000000000000000' ) ||
  8                  '( x int ) enable row movement';
  9                  execute immediate l_stmt;
 10          end loop;
 11  end;
 12  /
old   4:        for i in 1 .. &1
new   4:        for i in 1 .. 1000

PL/SQL procedure successfully completed.

ops$tkyte%ORA11GR2> exec dbms_lock.sleep(20);

PL/SQL procedure successfully completed.

ops$tkyte%ORA11GR2> create restore point test;

Restore point created.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> declare
  2          l_stmt long;
  3  begin
  4          for i in 1 .. &1
  5          loop
  6                  l_stmt := 'insert into t' ||
  7                  to_char(i,'fm0000000000000000000000000000' ) ||
  8                  ' values ( 1 )';
  9                  execute immediate l_stmt;
 10          end loop;
 11          commit;
 12  end;
 13  /
old   4:        for i in 1 .. &1
new   4:        for i in 1 .. 1000

PL/SQL procedure successfully completed.

ops$tkyte%ORA11GR2> select * from t0000000000000000000000000001;

         X
----------
         1

ops$tkyte%ORA11GR2> declare
  2          l_stmt clob := 'flashback table ops$tkyte.t0000000000000000000000000001';
  3  begin
  4          for i in 2 .. &1
  5          loop
  6                  l_stmt := l_stmt || ', ops$tkyte.t' ||
  7                  to_char(i,'fm0000000000000000000000000000' );
  8          end loop;
  9          l_stmt := l_stmt || ' to restore point test';
 10          dbms_output.put_line( 'length = ' || length(l_stmt) );
 11      execute immediate l_stmt;
 12  end;
 13  /
old   4:        for i in 2 .. &1
new   4:        for i in 2 .. 1000
length = 41036

PL/SQL procedure successfully completed.

ops$tkyte%ORA11GR2> select * from t0000000000000000000000000001;

no rows selected

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> drop restore point test;

Restore point dropped.

Flashback tables

Shirley, March 02, 2011 - 4:23 pm UTC

I have used the same code that you are using (clob) but got the following error message: ORA-00997: illegal use of LONG datatype. Do you think that 11Gr1 does not support clob for execute immediate statement?

Thanks,
Tom Kyte
March 02, 2011 - 5:43 pm UTC

that was new in 11gr2 - you will have to use a long (32k) in PLSQL or use a plsql index by table of varchar2's and use DBMS_SQL to execute it (dbms_sql was able to execute SQL of any length in 11gr1 and before using an array of text).



Sean, March 02, 2011 - 4:36 pm UTC

ops$tkyte%ORA11GR2> set echo off
ops$tkyte%ORA11GR2> spool off
ops$tkyte%ORA11GR2> spool x
ops$tkyte%ORA11GR2> @test 1000

What the test script?
Tom Kyte
March 02, 2011 - 5:44 pm UTC

it is there there, after @test 1000

see the &1 getting replace with 1000 - that is the script during execution.

Sean, March 02, 2011 - 8:08 pm UTC

Tom,
A couple.
1) why exec dbms_lock.sleep(20) ? why did you want to suspening the session?
2) the following bolock does not work on 10gR2
works on 11g
sbu@test1> declare
2 l_stmt clob := 'flashback table sbu.t0000000000000000000000000001';
3 begin
4 for i in 2 .. &1
5 loop
6 l_stmt := l_stmt || ', sbu.t' ||
7 to_char(i,'fm0000000000000000000000000000' );
8 end loop;
9 l_stmt := l_stmt || ' to restore point test';
10 dbms_output.put_line( 'length = ' || length(l_stmt) );
11 execute immediate l_stmt;
12 end;
13 /
old 4: for i in 2 .. &1
new 4: for i in 2 .. 1000
execute immediate l_stmt;
*
ERROR at line 11:
ORA-06550: line 11, column 26:
PLS-00382: expression is of wrong type
ORA-06550: line 11, column 8:
PL/SQL: Statement ignored

and last one
3) on 11gR1 running my laptop and the flashback block has been runing over 40 mins and still going. How can I find out how much time more to have it completed?

Thanks a lot
Tom Kyte
March 03, 2011 - 7:28 am UTC

1) because you have to wait a bit on a newly created segment before you can start flashing back on it. If I just let it run - the flashback would fail as the tables are "too new"

2) because it uses a clob. See above about using dbms_sql and a table of varchar2 in earlier releases.

3) never said it would be "fast" :) flashing back 1,000 tables involves lots of work.

You might be able to enable tracing of that session from another session using dbms_monitor and see what SQL it is executing. If you enable it with binds=> true, ou'll see the table names fly by from time to time. As well as SQL such as:

/* Flashback Table */ INSERT /*+ PARALLEL(S, DEFAULT) PARALLEL(T, DEFAULT) */ INTO "OPS$TKYTE"."T0000000000000000000000000500" SELECT /*+ USE_NL(S) ORDERED PARALLEL(S, DEFAULT) PARALLEL(T, DEFAULT) */ S.* FROM SYS_TEMP_FBT T , "OPS$TKYTE"."T0000000000000000000000000500" as of SCN :1 S WHERE T.rid = S.rowid and T.action = 'I' and T.object# = :2 


You could also peek at the output of:

select sql_text from v$sql where sql_text like '% Flashback Table %' order by 1



Flashback tables

Shirley, March 03, 2011 - 7:51 am UTC

Good morning Tom,

I tried to execute my procedure by using varchar2(32767) for flashback table list still got the following error. Do you consider varchar2(32767) as LONG? So I have to use dbms_sql in order to accomplish this? Thank you very much for your help!


ORACLE before exec ORA-00997: illegal use of LONG datatype

The following is the procedure code:

PROCEDURE flashback_tables (p_restore_point varchar2)
AS


CURSOR tables_cur IS
SELECT table_name
FROM dba_tables WHERE owner ='XXXX' AND temporary = 'N' ;

v_sql varchar2(32767) := 'FLASHBACK TABLE';

loc varchar2(100);

BEGIN

loc:= 'before loop';
FOR rec in tables_cur
LOOP
IF v_sql = 'FLASHBACK TABLE' THEN
v_sql := v_sql ||' '||rec.table_name;
ELSE
v_sql := v_sql ||','||rec.table_name;
END IF;

END LOOP;

loc := 'before v_sql';
v_sql := v_sql ||' TO RESTORE POINT '||p_restore_point;

loc := 'before exec';

EXECUTE IMMEDIATE v_sql;


EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('ORACLE '||loc||' '||substr(sqlerrm,1,100));

END;

END pkg_utility;
/
Tom Kyte
March 03, 2011 - 9:00 am UTC

32765 is the subtype LONG in plsql.

 EXCEPTION
            WHEN OTHERS
            THEN
               DBMS_OUTPUT.put_line ('ORACLE '||loc||' 
'||substr(sqlerrm,1,100));
  
  END;


I cannot express how much I hate that construct. I don't care if it is a demo, I don't care if you wouldn't do it in real life - I just care that you've posted it on my site and it is the WORST PRACTICE possible - period. There is no counter argument to this - I've heard every attempt at counter arguments - they do not hold water. Please stop doing that. That code should simply be ERASED and never typed in again.


Just use the LONG type.

Or better yet, use the varchar2's that I was talking about with DBMS_SQL. That way you are not limited.

something like this:


ops$tkyte%ORA10GR2> create or replace procedure flashback_schema( p_schema in varchar2, p_restore_point in varchar2 )
  2  authid current_user
  3  as
  4      l_array  dbms_sql.varchar2s;
  5      l_sep    varchar2(1) := NULL;
  6      l_cursor integer;
  7      l_status number;
  8  begin
  9      l_array(1) := 'flashback table';
 10
 11      for x in ( select '"' || owner || '"."' || object_name || '"' tname
 12                   from dba_objects do
 13                  where owner = p_schema
 14                    and object_type = 'TABLE'
 15                    and temporary = 'N'
 16                    and not exists (select null
 17                                      from dba_external_tables et
 18                                     where et.owner = do.owner
 19                                       and et.table_name = do.object_name )
 20                    and not exists (select null
 21                                      from dba_mviews mv
 22                                     where mv.owner = do.owner
 23                                       and mv.mview_name = do.object_name )
 24                   order by object_name)
 25      loop
 26          l_array(l_array.count+1) := l_sep || x.tname;
 27          l_sep := ',';
 28      end loop;
 29      l_array(l_array.count+1) := 'to restore point ' || dbms_assert.simple_sql_name(p_restore_point);
 30
 31      l_cursor := dbms_sql.open_cursor;
 32      begin
 33          dbms_sql.parse( l_cursor, l_array, 1, l_array.count, TRUE, dbms_sql.native );
 34          l_status := dbms_sql.execute( l_cursor );
 35      exception
 36          when others
 37          then
 38              dbms_sql.close_cursor( l_cursor );
 39              RAISE;
 40      end;
 41      dbms_sql.close_cursor( l_cursor );
 42  end;
 43  /

Procedure created.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> create restore point test;

Restore point created.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> select count(*) from t;

  COUNT(*)
----------
        67

ops$tkyte%ORA10GR2> delete from t;

67 rows deleted.

ops$tkyte%ORA10GR2> commit;

Commit complete.

ops$tkyte%ORA10GR2> select count(*) from t;

  COUNT(*)
----------
         0

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> exec flashback_schema( 'OPS$TKYTE', 'TEST' );

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR2> select count(*) from t;

  COUNT(*)
----------
        67

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> drop restore point test;

Restore point dropped.


Best practice of PL/SQL

Shirley, March 03, 2011 - 9:42 am UTC

Thank you, Tom, for letting me know the worst code I have. Could you please help me further by telling me what is the BEST PRACTICE code which should be used to replace this? Or just don’t trap any error any all? Thank you very much for sharing your expertise.
Tom Kyte
March 03, 2011 - 10:11 am UTC

The only errors you should trap are:

a) those errors that are NOT actually errors. for example -

...
begin
   select x into y from t where ...
exception
   when no_data_found then x := some_valid_default;
end;
....


Here, no_data_found is not an error (it COULD be, but not in this particular case - our logic dictated otherwise). We catch IT and only IT and deal with it. All other errors are in fact TRUE ERRORS and we cannot deal with them.


b) as I did above with a when others followed by a RAISE. The exception block should be as small as possible (and you can have as many as you need in block of code, you do NOT want or need that 'catch all' exception block at the end in general). They surround some resource that needs to be manually cleaned up in the event of an error.

Looking at my example above:

...
 31      l_cursor := dbms_sql.open_cursor;
 32      begin
 33          dbms_sql.parse( l_cursor, l_array, 1, l_array.count, TRUE, dbms_sql.native );
 34          l_status := dbms_sql.execute( l_cursor );
 35      exception
 36          when others
 37          then
 38              dbms_sql.close_cursor( l_cursor );
 39              RAISE;
 40      end;
 41      dbms_sql.close_cursor( l_cursor );
...


See how the exception block is just there to protect the cursor handle. I only make sure the cursor gets closed and then RERAISE the error. You want to keep the block of code as small as possible, but as large as it needs to be.

But most importantly - you RERAISE the exception!!! the caller MUST KNOW that the procedure failed miserably. The cannot ignore the exception!!! (a return code on the other hand is easily overlooked - don't use return codes)


c) at the top most level of your code - at the original block of plsql that your client code invoked. Instead of just calling "flashback_schema", your application could:

begin
    flashback_schema( x, y );
exception
    when others 
    then
        call_error_logging_routine_that_uses_an_autonomous_transaction;
        RAISE;
end;



Your error logging routine would write diagnostic information into a logging table (using dbms_utility to get the error stack) and then re-raise the exception so that the client knows about it.

You can use raise_application_error instead - to return your own custom error message - but by all means - return an ERROR, not a return code, not a message - an honest to goodness ERROR.


and this is not just a best practice of PLSQL - but a best practice in all exception based languages like Java, C++, C# and so on.

Shirley, March 03, 2011 - 10:49 am UTC

Wow! That is really good. I have not thought all those before and it's great to know.

Thank you very much!


A reader, March 03, 2011 - 12:12 pm UTC

12:25:36 SQL> declare
12:25:58   2            l_stmt clob := 'flashback table t00000000000000000000000
00001';
12:25:58   3     begin
12:25:58   4             for i in 2 .. &1
12:25:58   5             loop
12:25:58   6                    l_stmt := l_stmt || ', t' ||
12:25:58   7                    to_char(i,'fm0000000000000000000000000000' );
12:25:58   8            end loop;
12:25:58   9            l_stmt := l_stmt || ' to restore point test';
12:25:58  10            dbms_output.put_line( 'length = ' || length(l_stmt) );
12:25:58  11         execute immediate l_stmt;
12:25:58  12     end;
12:25:58  13  /
old   4:            for i in 2 .. &1
new   4:            for i in 2 .. 1000

PL/SQL procedure successfully completed.

Elapsed: 00:22:18.17
12:48:24 SQL>


 
flashback table t0000000000000000000000000001, t0000000000000000000000000002, t0
00000000000000000000
0000003, t0000000000000000000000000004, t0000000000000000000000000005, t00000000
00000000000000000006


SQL_TEXT
--------------------------------------------------------------------------------
--------------------
, t0000000000000000000000000007, t0000000000000000000000000008, t000000000000000
0000000000009, t0000
000000000000000000000010, t0000000000000000000000000011, t0000000000000000000000
000012, t00000000000
00000000000000013, t0000000000000000000000000014, t0000000000000000000000000015,
 t000000000000000000
0000000016, t0000000000000000000000000017, t0000000000000000000000000018, t00000
00000000000000000000
019, t0000000000000000000000000020, t0000000000000000000000000021, t000000000000
0000000000000022, t0
000000000000000000000000023, t0000000000000000000000000024, t0000000000000000000
000000025, t00000000
00000000000000000026, t0000000000000000000000000027, t00000000000000000000000000
28, t000000000000000
0000000000029, t0000000000000000000000000030, t0000000000000000000000000031, t00
00000000000000000000

Tom, it took 22 minutes for the flashback 1000 tabs complete. I monitored the sql via v$sql as you directed. It got stucked for contructing the CLOB sql statement on t0..032 and then just jumped to complete the flashback all the tables.
Some kind of internal locks were going on? my db is 11gr1 on windows.

It took only 2 minutes to do the same without using CLOB's.

12:58:04 SQL> declare
13:00:26   2            l_stmt long;
13:00:26   3     begin
13:00:26   4             for i in 1 .. &1
13:00:26   5             loop
13:00:26   6                    l_stmt := 'flashback table t' ||
13:00:26   7                    to_char(i,'fm0000000000000000000000000000' ) ||
13:00:26   8              ' to restore point test';
13:00:26   9            execute immediate l_stmt;
13:00:26  10            end loop;
13:00:26  11
13:00:26  12     end;
13:00:26  13  /
old   4:            for i in 1 .. &1
new   4:            for i in 1 .. 1000

PL/SQL procedure successfully completed.

Elapsed: 00:02:02.73

Tom Kyte
March 03, 2011 - 1:11 pm UTC

you didn't monitor v$sql the way I described.


select sql_text from v$sql where sql_text like '% Flashback Table %' order by 1



you need to look at the recursive SQL being generated. You'll see a single statement for each one.


You are comparing a single statement that flashes back 1000 tables consistently to the same point in time with 1000 separate distinct statements that have the tables all at different points in time??

If you need to flash back a bunch of tables that have referencing constraints - you need to do them as a single statement.

Sean, March 03, 2011 - 8:00 pm UTC

"
you didn't monitor v$sql the way I described.

select sql_text from v$sql where sql_text like '% Flashback Table %' order by 1
"

Yes, I did use the very same query. Seemed sql_text got truncated, if not stucked on the no. 32 table.
You may get the same if you try it again.

"
you need to look at the recursive SQL being generated. You'll see a single statement for each one.
"

I may tkprof the block.

"
You are comparing a single statement that flashes back 1000 tables consistently to the same point in time with 1000 separate distinct statements that have the tables all at different points in time??
"

Yes. I was curious to see each one was faster. The single statement was running poorly. We may want to see what tkprof says about.

"
If you need to flash back a bunch of tables that have referencing constraints - you need to do them as a single statement.
"
Made the note of.

Thanks

flashback tables

Shirley, March 04, 2011 - 9:31 am UTC

Just want to give an update on the error “ORA-00997: illegal use of LONG datatype” I got when I do “execute immediate v_sql” while v_sql is VARCHAR2(32767) and starts with “flashback table …”. My team leader actually found out the reason. The error is not because v_sql is VARCHAR2(32767) or CLOB and it is because one table in the list of tables to be flashed back has a LONG datatype column which caused the execute immediate fail. After we exclude this table from flashback table list everything runs fine.

I am kind of curious how Oracle takes care of rollback transactions of a table with LONG datatype columns. I have not get a chance to search on the web yet.

Thanks a lot for your help, Tom.

trace on flashbacking of the 1000 tables

Sean, March 04, 2011 - 11:06 am UTC

Tom,
I made the traces and tkprofed the traces for 1) the single statement flashback 2) 1000 statement flashbacks

1)
SQL> declare
  2            l_stmt clob := 'flashback table t0000000000000000000000000001';
  3     begin
  4             for i in 2 .. &1
  5             loop
  6                    l_stmt := l_stmt || ', t' ||
  7                    to_char(i,'fm0000000000000000000000000000' );
  8            end loop;
  9            l_stmt := l_stmt || ' to restore point test';
 10            dbms_output.put_line( 'length = ' || length(l_stmt) );
 11         execute immediate l_stmt;
 12     end;
 13  /
Enter value for 1: 1000
old   4:            for i in 2 .. &1
new   4:            for i in 2 .. 1000
length = 31036

PL/SQL procedure successfully completed.

Elapsed: 00:21:13.93


********************************************************************************

OVERALL TOTALS FOR ALL NON-RECURSIVE STATEMENTS

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse       15      0.00       0.03          0          0          0           0
Execute     15      0.59       0.79          0      14665      91302           9
Fetch        2      0.00       0.03          0          0          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       32      0.59       0.86          0      14665      91302          10

Misses in library cache during parse: 3
Misses in library cache during execute: 1

Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                      11        0.00          0.00
  SQL*Net message from client                    10      561.35        734.43
  Disk file operations I/O                        1        0.00          0.00
  latch: shared pool                              3        0.00          0.00
  control file sequential read                    7        0.01          0.02
  asynch descriptor resize                        2        0.00          0.00
  SQL*Net break/reset to client                   2        0.00          0.00


OVERALL TOTALS FOR ALL RECURSIVE STATEMENTS

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse     6311    485.65     493.37          2         18          0           0
Execute   8649     28.46     444.07          0      15004      10073        2005
Fetch     6357     30.64     335.85     758155     332132          0        4924
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    21317    544.76    1273.30     758157     347154      10073        6929

Misses in library cache during parse: 3074
Misses in library cache during execute: 3055

Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  Disk file operations I/O                        1        0.00          0.00
  latch: shared pool                             34        0.00          0.00
  db file sequential read                      8157        0.19         17.86
  control file sequential read                   49        0.02          0.08
  os thread startup                               4        0.01          0.05
  PX Deq: Join ACK                            11837        0.05         21.14
  PX Deq: Parse Reply                          8454        0.12         39.28
  resmgr:cpu quantum                           1761        0.07         17.70
  PX Deq: Execute Reply                      113840        0.74        311.24
  PX Deq: Signal ACK RSG                       5300        0.10          1.37
  direct path write temp                        995        0.17          3.88
  PX Deq: Signal ACK EXT                      10396        1.99          6.37
  PX Deq: Slave Session Stats                  7078        0.01          1.49
  PX qref latch                                2482        0.01          0.41
  enq: PS - contention                         1238        0.00          0.27
  asynch descriptor resize                      970        0.00          0.00
  library cache: mutex X                          5        0.00          0.00
  latch free                                     10        0.00          0.00
  buffer busy waits                               8        0.00          0.00
  latch: row cache objects                        1        0.00          0.00
  log file switch completion                      1        0.48          0.48
  latch: enqueue hash chains                      6        0.00          0.00
  latch: call allocation                         14        0.00          0.00
  db file scattered read                      94000        0.32        289.55
  reliable message                              984        0.53          1.43
  enq: RO - fast object reuse                  1390        0.19          2.26
  PX Deq: Table Q Normal                       3213        0.00          0.39
  latch: parallel query alloc buffer              1        0.00          0.00
  log file switch (checkpoint incomplete)         1        0.05          0.05

 5039  user  SQL statements in session.
 3573  internal SQL statements in session.
 8612  SQL statements in session.
********************************************************************************
Trace file: C:\oracle\11.2.0\diag\rdbms\b1z050\b1z050\trace\b1z050_ora_7100.trc
Trace file compatibility: 11.1.0.7
Sort options: default

       1  session in tracefile.
    5039  user  SQL statements in trace file.
    3573  internal SQL statements in trace file.
    8612  SQL statements in trace file.
    3084  unique SQL statements in trace file.
  542066  lines in trace file.
    2008  elapsed seconds in trace file.


2) 
SQL> declare
  2            l_stmt long;
  3     begin
  4             for i in 1 .. &1
  5             loop
  6                    l_stmt := 'flashback table t' ||
  7                    to_char(i,'fm0000000000000000000000000000' ) ||
  8      ' to restore point test';
  9    execute immediate l_stmt;
 10            end loop;
 11         
 12     end;
 13  /
Enter value for 1: 1000
old   4:            for i in 1 .. &1
new   4:            for i in 1 .. 1000

PL/SQL procedure successfully completed.

Elapsed: 00:02:56.81



********************************************************************************

OVERALL TOTALS FOR ALL NON-RECURSIVE STATEMENTS

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse       15      0.00       0.00          0          0          0           0
Execute     15      0.32       0.39          0          0          0           9
Fetch        2      0.00       0.00          0          7          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       32      0.32       0.39          0          7          0          10

Misses in library cache during parse: 2
Misses in library cache during execute: 1

Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                      11        0.00          0.00
  SQL*Net message from client                    10       67.09        109.43
  latch: call allocation                          1        0.00          0.00
  log file sync                                   1        0.00          0.00
  SQL*Net break/reset to client                   2        0.00          0.00


OVERALL TOTALS FOR ALL RECURSIVE STATEMENTS

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse    29423      3.68       3.66          0          0          0           0
Execute  34419     31.29     167.53          0      12304      16655        6001
Fetch    24720      4.51       5.17        306     179988          0       21610
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    88562     39.50     176.37        306     192292      16655       27611

Misses in library cache during parse: 4304
Misses in library cache during execute: 2302

Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  db file sequential read                       306        0.29          1.00
  Disk file operations I/O                        3        0.00          0.00
  control file sequential read                53000        0.51         28.83
  os thread startup                               4        0.01          0.05
  PX Deq: Join ACK                            11543        0.03         25.56
  PX Deq: Parse Reply                         11736        0.21         48.52
  PX Deq: Execute Reply                       15526        0.38         14.91
  PX Deq: Signal ACK RSG                       3142        0.10          3.16
  direct path write temp                        998        0.04          0.26
  PX Deq: Signal ACK EXT                       8175        0.01          1.36
  PX Deq: Slave Session Stats                  7748        0.00          1.58
  reliable message                             2060        0.01          0.44
  enq: RO - fast object reuse                  1781        0.32          2.98
  PX Deq: Table Q Normal                       4688        0.02          0.67
  PX qref latch                                 521        0.00          0.29
  enq: PS - contention                         1330        0.01          0.30
  resmgr:cpu quantum                            204        0.05          2.02
  latch: shared pool                             68        0.00          0.01
  latch free                                     14        0.00          0.00
  asynch descriptor resize                      969        0.00          0.00
  latch: call allocation                         25        0.00          0.00
  buffer busy waits                              16        0.00          0.00
  latch: enqueue hash chains                     10        0.00          0.00
  log file switch (checkpoint incomplete)         1        0.19          0.19
  enq: CF - contention                            1        0.04          0.04
  library cache: mutex X                          1        0.00          0.00

25318  user  SQL statements in session.
 9117  internal SQL statements in session.
34435  SQL statements in session.
********************************************************************************
Trace file: C:\oracle\11.2.0\diag\rdbms\b1z050\b1z050\trace\b1z050_ora_5372.trc
Trace file compatibility: 11.1.0.7
Sort options: default

       1  session in tracefile.
   25318  user  SQL statements in trace file.
    9117  internal SQL statements in trace file.
   34435  SQL statements in trace file.
    4050  unique SQL statements in trace file.
  840624  lines in trace file.
     286  elapsed seconds in trace file.

Comment:
It took a huge toll for the internal activities for the single statement flashback vs the sumed of 1000 statement flashbacks.

Using Oracle Flashback Version Query

Rajeshwaran, Jeyabal, May 29, 2012 - 3:08 am UTC

Tom:

I was reading and working on "Using Oracle Flashback Version Query " from product docs and ended up with this doubts, Can you help me to understand this?

1) I dont see that DELETE plus INSERT happening when updating an Indexed column.
http://docs.oracle.com/cd/E11882_01/appdev.112/e25518/adfns_flashback.htm#i1019938
<quote>
Operation performed by the transaction: I for insertion, D for deletion, or U for update. The version is that of the row that was inserted, deleted, or updated; that is, the row after an INSERT operation, the row before a DELETE operation, or the row affected by an UPDATE operation.
For user updates of an index key, Oracle Flashback Version Query might treat an UPDATE operation as two operations, DELETE plus INSERT, represented as two version rows with a D followed by an I VERSIONS_OPERATION
</quote>
rajesh@ORA11GR2>
rajesh@ORA11GR2> create table t (x number,y number, z date);

Table created.

Elapsed: 00:00:00.00
rajesh@ORA11GR2> create index t_ind on t(x);

Index created.

Elapsed: 00:00:00.00
rajesh@ORA11GR2>
rajesh@ORA11GR2> insert into t
  2  select rownum,rownum,sysdate - rownum
  3  from all_users;

32 rows created.

Elapsed: 00:00:00.00
rajesh@ORA11GR2> commit;

Commit complete.

Elapsed: 00:00:00.00
rajesh@ORA11GR2>
rajesh@ORA11GR2> select t.*,versions_starttime,
  2     versions_endtime,
  3     versions_operation,
  4     versions_xid
  5  from t
  6  versions between
  7  timestamp minvalue and maxvalue
  8  where y = 1;

         X          Y Z         VERSIONS_STARTTIME   VERSIONS_ENDTIME     VERSI VERSIONS_XID
---------- ---------- --------- -------------------- -------------------- ----- --------------------
         1          1 28-MAY-12

Elapsed: 00:00:00.00
rajesh@ORA11GR2>
rajesh@ORA11GR2> exec dbms_lock.sleep(4);

PL/SQL procedure successfully completed.

Elapsed: 00:00:04.00
rajesh@ORA11GR2> update t
  2  set x = x+1;

32 rows updated.

Elapsed: 00:00:00.00
rajesh@ORA11GR2> commit;

Commit complete.

Elapsed: 00:00:00.01
rajesh@ORA11GR2> select t.*,versions_starttime,
  2     versions_endtime,
  3     versions_operation,
  4     versions_xid
  5  from t
  6  versions between
  7  timestamp minvalue and maxvalue
  8  where y = 1;

         X          Y Z         VERSIONS_STARTTIME   VERSIONS_ENDTIME     VERSI VERSIONS_XID
---------- ---------- --------- -------------------- -------------------- ----- --------------------
         2          1 28-MAY-12 29-MAY-12 01.26.43 P                      U     09001A00F6090000
         1          1 28-MAY-12                      29-MAY-12 01.26.43 P

Elapsed: 00:00:00.00
rajesh@ORA11GR2>



Tom Kyte
May 30, 2012 - 12:43 am UTC

keyword = might.

might means "might", as in maybe.