We reveal Oracle Internals with SQL Trace, and shows approach to detect such Invalid.
Note: Tested in Oracle 19c
1. Test Code
At first, we create one table and one Plsql package using it.
drop table test_tab;
create table test_tab(x1 number(2));
insert into test_tab (x1) values (1);
commit;
select * from test_tab;
drop package test_pkg;
create or replace package test_pkg as
--b_ret test_tab.x1%type; -- this makes both Package Spec and Body INVALID
function func1 (p_x number) return number;
end;
/
create or replace package body test_pkg as
function func1 (p_x number) return number as
l_ret number;
begin
-- comment out this query to make Package Body not depend on any table
select x1 into l_ret from test_tab where x1 = p_x;
return l_ret;
end;
end;
/
2. Modify Table to trigger Package Body Invalid
We query dba_dependencies to list the dependencies:
select name, type, referenced_name, referenced_type, dependency_type from dba_dependencies where name = 'TEST_PKG';
NAME TYPE REFERENCED REFERENCED DEPENDENCY
---------- --------------- ---------- ---------- ----------
TEST_PKG PACKAGE STANDARD PACKAGE HARD
TEST_PKG PACKAGE BODY STANDARD PACKAGE HARD
TEST_PKG PACKAGE BODY TEST_TAB TABLE HARD
TEST_PKG PACKAGE BODY TEST_PKG PACKAGE HARD
Modify test_tab to invalidate package body:
alter table test_tab modify x1 number(9);
-- alter table test_tab add x2 number(9);
-- alter table test_tab drop column x2;
ALTER SESSION SET NLS_DATE_FORMAT ='YYYY-MON-DD HH24:MI:SS';
ALTER SESSION SET NLS_TIMESTAMP_FORMAT ='YYYY-MON-DD HH24:MI:SS';
select object_name, object_id, data_object_id, created, last_ddl_time, timestamp, status
from dba_objects where object_name in ( 'TEST_PKG', 'TEST_TAB');
OBJECT_NAM OBJECT_ID DATA_OBJECT_ID CREATED LAST_DDL_TIME TIMESTAMP STATUS
---------- ---------- -------------- -------------------- -------------------- ------------------- ----------
TEST_TAB 6256609 6256609 2026-AUG-25 13:41:38 2026-AUG-25 13:46:02 2026-08-25:13:46:02 VALID
TEST_PKG 6256610 2026-AUG-25 13:43:24 2026-AUG-25 13:43:24 2026-08-25:13:43:24 VALID
TEST_PKG 6256611 2026-AUG-25 13:43:24 2026-AUG-25 13:43:24 2026-08-25:13:43:24 INVALID
-- dba_objects.status= decode(o.status, 0, 'N/A', 1, 'VALID', 'INVALID')
select obj#, name, type#, ctime, mtime, stime, status from sys.obj$ o where name = 'TEST_PKG';
OBJ# NAME TYPE# CTIME MTIME STIME STATUS
------- ---------- ---------- -------------------- -------------------- -------------------- ------
6256610 TEST_PKG 9 2026-AUG-25 13:43:24 2026-AUG-25 13:43:24 2026-AUG-25 13:43:24 1
6256611 TEST_PKG 11 2026-AUG-25 13:43:24 2026-AUG-25 13:43:24 2026-AUG-25 13:43:24 5
When we query dba_errors, no entries found:
select * from dba_errors where name = 'TEST_PKG';
no rows selected
select * from sys.error$ e, dba_objects o
where e.obj# = o.object_id and object_name in ( 'TEST_PKG', 'TEST_TAB');
no rows selected
If we invoke package program, Package Body becomes VALID:
declare
l_ret number;
begin
l_ret := test_pkg.func1(1);
dbms_output.put_line('l_ret = '||l_ret);
end;
/
l_ret = 1
select object_name, object_id, data_object_id, created, last_ddl_time, timestamp, status
from dba_objects where object_name in ( 'TEST_PKG', 'TEST_TAB');
OBJECT_NAM OBJECT_ID DATA_OBJECT_ID CREATED LAST_DDL_TIME TIMESTAMP STATUS
---------- ---------- -------------- -------------------- -------------------- ------------------- -------
TEST_TAB 6256609 6256609 2026-AUG-25 13:41:38 2026-AUG-25 14:11:44 2026-08-25:14:11:44 VALID
TEST_PKG 6256610 2026-AUG-25 13:43:24 2026-AUG-25 13:43:24 2026-08-25:13:43:24 VALID
TEST_PKG 6256611 2026-AUG-25 13:43:24 2026-AUG-25 14:12:08 2026-08-25:14:12:08 VALID
3. SQL Trace to Reveal Dependency and Invalid
We modify the table again with SQL Trace level 12 (contains bind variables):
alter session set max_dump_file_size=unlimited;
alter session set tracefile_identifier = 'trc_1';
alter session set events '10046 trace name context forever, level 12';
alter table test_tab add x1 number(11);
alter session set events '10046 trace name context off';
TEST_PKG Body is INVALID:
select object_name, object_id, data_object_id, created, last_ddl_time, timestamp, status
from dba_objects where object_name in ( 'TEST_PKG', 'TEST_TAB');
OBJECT_NAM OBJECT_ID DATA_OBJECT_ID CREATED LAST_DDL_TIME TIMESTAMP STATUS
---------- ---------- -------------- -------------------- -------------------- ------------------- -------
TEST_TAB 6256609 6256609 2026-AUG-25 13:41:38 2026-AUG-25 14:16:41 2026-08-25:14:16:41 VALID
TEST_PKG 6256610 2026-AUG-25 13:43:24 2026-AUG-25 13:43:24 2026-08-25:13:43:24 VALID
TEST_PKG 6256611 2026-AUG-25 13:43:24 2026-AUG-25 14:12:08 2026-08-25:14:12:08 INVALID
-- dba_objects.status= decode(o.status, 0, 'N/A', 1, 'VALID', 'INVALID')
-- set test_pkg package body sys.OBJ$.status = 5, i.e, dba_objects.status = 'INVALID'
-- no any modification of sys.ERROR$ for test_pkg package body
Open trc file, we can see that TIMESTAMP (Bind#4 stime) of TEST_TAB is updated as value="8/25/2026 14:16:41":
=== Update Table: TEST_TAB (OBJECT_ID = 6256609), set mtime(Bind#4)="8/25/2026 14:16:41", set stime(Bind#4)="8/25/2026 14:16:41", status(Bind#5)=1 (VALID)
PARSING IN CURSOR #139943683342520 len=409 dep=1 uid=0 oct=6 lid=0 tim=843833534857 hv=696165170 ad='9ecdf3e8' sqlid='c3utnxsnrx8tk'
update obj$ set obj#=:4, type#=:5,ctime=:6,mtime=:7,stime=:8,status=:9,
dataobj#=:10,flags=:11,oid$=:12,spare1=:13,spare2=:14,spare3=:15,signature=
:16,spare7=:17,spare8=:18,spare9=:19, dflcollid=decode(:20,0,null,:20),
creappid=:21,creverid=:22, modappid=:23,modverid=:24,crepatchid=:25,
modpatchid=:26
where
owner#=:1 and name=:2 and namespace=:3 and remoteowner is null and linkname
is null and subname is null
BINDS #139943683342520:
Bind#0
oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00
oacflg=00 fl2=1000001 frm=00 csi=00 siz=48 off=0
kxsbbbfp=7f472d7acfe8 bln=22 avl=05 flg=05
value=6256609
Bind#1
oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00
oacflg=00 fl2=1000001 frm=00 csi=00 siz=0 off=24
kxsbbbfp=7f472d7ad000 bln=22 avl=02 flg=01
value=2
Bind#2
oacdty=12 mxl=07(07) mxlc=00 mal=00 scl=00 pre=00
oacflg=10 fl2=0001 frm=00 csi=00 siz=8 off=0
kxsbbbfp=989ee31d bln=07 avl=07 flg=09
value="8/25/2026 13:41:38"
Bind#3
oacdty=12 mxl=07(07) mxlc=00 mal=00 scl=00 pre=00
oacflg=10 fl2=0001 frm=00 csi=00 siz=8 off=0
kxsbbbfp=989ee324 bln=07 avl=07 flg=09
value="8/25/2026 14:16:41"
Bind#4
oacdty=12 mxl=07(07) mxlc=00 mal=00 scl=00 pre=00
oacflg=10 fl2=0001 frm=00 csi=00 siz=8 off=0
kxsbbbfp=989ee32b bln=07 avl=07 flg=09
value="8/25/2026 14:16:41"
Bind#5
oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00
oacflg=00 fl2=1000001 frm=00 csi=00 siz=72 off=0
kxsbbbfp=7f472d7acf88 bln=22 avl=02 flg=05
value=1
......
Bind#25
oacdty=01 mxl=32(08) mxlc=00 mal=00 scl=00 pre=00
oacflg=10 fl2=0001 frm=01 csi=873 siz=32 off=0
kxsbbbfp=989ee106 bln=32 avl=08 flg=09
value="TEST_TAB"
Bind#26
oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00
oacflg=00 fl2=1000001 frm=00 csi=00 siz=24 off=0
kxsbbbfp=7f472d7ace68 bln=22 avl=02 flg=05
value=1
For Package Body TEST_PKG (OBJECT_ID = 6256611), its status(Bind#5) is updated as value=5 (INVALID):
=== update package body: TEST_PKG (OBJECT_ID = 6256611), set status(Bind#5)=5 (INVALID)
update obj$ set obj#=:4, type#=:5,ctime=:6,mtime=:7,stime=:8,status=:9,
dataobj#=:10,flags=:11,oid$=:12,spare1=:13,spare2=:14,spare3=:15,signature=:16,spare7=:17,spare8=:18,spare9=:19,
dflcollid=decode(:20,0,null,:20),creappid=:21,creverid=:22, modappid=:23,modverid=:24,crepatchid=:25,modpatchid=:26
where owner#=:1 and name=:2 and namespace=:3 and remoteowner is null and linkname is null and subname is null
BINDS #139943683342520:
Bind#0
oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00
oacflg=00 fl2=1000001 frm=00 csi=00 siz=48 off=0
kxsbbbfp=7f472e0e19a8 bln=22 avl=05 flg=05
value=6256611
Bind#1
oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00
oacflg=00 fl2=1000001 frm=00 csi=00 siz=0 off=24
kxsbbbfp=7f472e0e19c0 bln=22 avl=02 flg=01
value=11
Bind#2
oacdty=12 mxl=07(07) mxlc=00 mal=00 scl=00 pre=00
oacflg=10 fl2=0001 frm=00 csi=00 siz=8 off=0
kxsbbbfp=988e02ad bln=07 avl=07 flg=09
value="8/25/2026 13:43:24"
Bind#3
oacdty=12 mxl=07(07) mxlc=00 mal=00 scl=00 pre=00
oacflg=10 fl2=0001 frm=00 csi=00 siz=8 off=0
kxsbbbfp=988e02b4 bln=07 avl=07 flg=09
value="8/25/2026 14:12:8"
Bind#4
oacdty=12 mxl=07(07) mxlc=00 mal=00 scl=00 pre=00
oacflg=10 fl2=0001 frm=00 csi=00 siz=8 off=0
kxsbbbfp=988e02bb bln=07 avl=07 flg=09
value="8/25/2026 14:12:8"
Bind#5
oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00
oacflg=00 fl2=1000001 frm=00 csi=00 siz=72 off=0
kxsbbbfp=7f472e0e1948 bln=22 avl=02 flg=05
value=5
......
Bind#25
oacdty=01 mxl=32(08) mxlc=00 mal=00 scl=00 pre=00
oacflg=10 fl2=0001 frm=01 csi=873 siz=32 off=0
kxsbbbfp=988e0096 bln=32 avl=08 flg=09
value="TEST_PKG"
Bind#26
oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00
oacflg=00 fl2=1000001 frm=00 csi=00 siz=24 off=0
kxsbbbfp=7f472e0e1828 bln=22 avl=02 flg=05
value=2
Package Body TEST_PKG INVALID is due to the difference between p_timestamp (25-AUG-2026 14:11:44) of its dependency and stime (25-AUG-2026 14:16:41) of its dependent Table TEST_TAB.>In fact, Oracle has one query to reveal such difference.>
(sys.dependency$ contains column p_timestamp, which is not exposed in dba_dependencies).
select do.obj# d_obj,
do.name d_name,
do.type# d_type,
po.obj# p_obj,
po.name p_name,
to_char (d.p_timestamp, 'DD-MON-YYYY HH24:MI:SS') "P_Timestamp",
to_char (po.stime, 'DD-MON-YYYY HH24:MI:SS') "STIME",
decode (sign (po.stime - d.p_timestamp), 0, 'SAME', '*DIFFER*') "CHECK"
from sys.obj$ do, sys.dependency$ d, sys.obj$ po
where d.p_obj# = po.obj#(+) and d.d_obj# = do.obj#
--and do.status=1 /*dependent is valid*/
--and po.status=1 /*parent is valid*/
--and po.stime != d.p_timestamp /*parent timestamp not match*/
and do.name = 'TEST_PKG'
order by 2, 1;
D_OBJ D_NAME D_TYPE P_OBJ P_NAME P_Timestamp STIME CHECK
------- ---------- ------ ---------- ---------- ----------------------------- ----------------------------- ----------
6256610 TEST_PKG 9 1219 STANDARD 16-JUL-2018 00:00:00 16-JUL-2018 00:00:00 SAME
6256611 TEST_PKG 11 1219 STANDARD 16-JUL-2018 00:00:00 16-JUL-2018 00:00:00 SAME
6256611 TEST_PKG 11 6256609 TEST_TAB 25-AUG-2026 14:11:44 25-AUG-2026 14:16:41 *DIFFER*
6256611 TEST_PKG 11 6256610 TEST_PKG 25-AUG-2026 13:43:24 25-AUG-2026 13:43:24 SAME
4. Invoke Makes Valid
We use SQL trace again to track how Oracle makes Package Body Valid when package programs are invoked:
alter session set max_dump_file_size=unlimited;
alter session set tracefile_identifier = 'trc_2';
alter session set events '10046 trace name context forever, level 12';
declare
l_ret number;
begin
l_ret := test_pkg.func1(1);
dbms_output.put_line('l_ret = '||l_ret);
end;
/
alter session set events '10046 trace name context off';
We can see at first that the entry in dependency$ for d_obj#: TEST_PKG (OBJECT_ID = 6256611) and p_obj#: TEST_TAB (OBJECT_ID = 6256609) is updated to p_timestamp(Bind#0)="8/25/2026 14:16:41".
=== update dependency$ for d_obj#: TEST_PKG (OBJECT_ID = 6256611) and p_obj#: TEST_TAB (OBJECT_ID = 6256609), set p_timestamp(Bind#0)="8/25/2026 14:16:41"
PARSING IN CURSOR #139943671609592 len=93 dep=1 uid=0 oct=6 lid=0 tim=845811925428 hv=1207594200 ad='979c4718' sqlid='2hztt6j3znv6s'
update dependency$ set p_timestamp = :1, d_attrs = :2
where
d_obj# = :3 and p_obj# = :4
BINDS #139943671609592:
Bind#0
oacdty=12 mxl=07(07) mxlc=00 mal=00 scl=00 pre=00
oacflg=18 fl2=0001 frm=00 csi=00 siz=8 off=0
kxsbbbfp=989ee68c bln=08 avl=07 flg=09
value="8/25/2026 14:16:41"
Bind#1
oacdty=23 mxl=32(05) mxlc=00 mal=00 scl=00 pre=00
oacflg=18 fl2=0001 frm=00 csi=00 siz=32 off=0
kxsbbbfp=7f472d7aa1d0 bln=32 avl=05 flg=09
value=00030000
Bind#2
oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00
oacflg=08 fl2=1000001 frm=00 csi=00 siz=24 off=0
kxsbbbfp=7f472d3a1850 bln=22 avl=05 flg=05
value=6256611
Bind#3
oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00
oacflg=08 fl2=1000001 frm=00 csi=00 siz=24 off=0
kxsbbbfp=7f472d3a1820 bln=24 avl=05 flg=05
value=6256609
Then status of package body: TEST_PKG (OBJECT_ID = 6256611) is updated to status(Bind#5)=1 (VALID):
=== update package body: TEST_PKG (OBJECT_ID = 6256611), set status(Bind#5)=1 (VALID)
PARSING IN CURSOR #139943671609432 len=409 dep=1 uid=0 oct=6 lid=0 tim=845811927174 hv=696165170 ad='9ecdf3e8' sqlid='c3utnxsnrx8tk'
update obj$ set obj#=:4, type#=:5,ctime=:6,mtime=:7,stime=:8,status=:9,
dataobj#=:10,flags=:11,oid$=:12,spare1=:13,spare2=:14,spare3=:15,signature=
:16,spare7=:17,spare8=:18,spare9=:19, dflcollid=decode(:20,0,null,:20),
creappid=:21,creverid=:22, modappid=:23,modverid=:24,crepatchid=:25,
modpatchid=:26
where
owner#=:1 and name=:2 and namespace=:3 and remoteowner is null and linkname
is null and subname is null
BINDS #139943671609432:
Bind#0
oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00
oacflg=00 fl2=1000001 frm=00 csi=00 siz=48 off=0
kxsbbbfp=7f472d709e00 bln=22 avl=05 flg=05
value=6256611
Bind#1
oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00
oacflg=00 fl2=1000001 frm=00 csi=00 siz=0 off=24
kxsbbbfp=7f472d709e18 bln=22 avl=02 flg=01
value=11
Bind#2
oacdty=12 mxl=07(07) mxlc=00 mal=00 scl=00 pre=00
oacflg=10 fl2=0001 frm=00 csi=00 siz=8 off=0
kxsbbbfp=988e02ad bln=07 avl=07 flg=09
value="8/25/2026 13:43:24"
Bind#3
oacdty=12 mxl=07(07) mxlc=00 mal=00 scl=00 pre=00
oacflg=10 fl2=0001 frm=00 csi=00 siz=8 off=0
kxsbbbfp=988e02b4 bln=07 avl=07 flg=09
value="8/25/2026 14:12:8"
Bind#4
oacdty=12 mxl=07(07) mxlc=00 mal=00 scl=00 pre=00
oacflg=10 fl2=0001 frm=00 csi=00 siz=8 off=0
kxsbbbfp=988e02bb bln=07 avl=07 flg=09
value="8/25/2026 14:12:8"
Bind#5
oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00
oacflg=00 fl2=1000001 frm=00 csi=00 siz=72 off=0
kxsbbbfp=7f472d8bb938 bln=22 avl=02 flg=05
value=1
......
Bind#25
oacdty=01 mxl=32(08) mxlc=00 mal=00 scl=00 pre=00
oacflg=10 fl2=0001 frm=01 csi=873 siz=32 off=0
kxsbbbfp=988e0096 bln=32 avl=08 flg=09
value="TEST_PKG"
Bind#26
oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00
oacflg=00 fl2=1000001 frm=00 csi=00 siz=24 off=0
kxsbbbfp=7f472d8bb770 bln=22 avl=02 flg=05
value=2
We re-run the query, it shows P_Timestamp of Package Body TEST_PKG is SAME as STIME of TEST_TAB, both are "25-AUG-2026 14:16:41".
Therefore, Package Body TEST_PKG becomes VALID.
select do.obj# d_obj,
do.name d_name,
do.type# d_type,
po.obj# p_obj,
po.name p_name,
to_char (d.p_timestamp, 'DD-MON-YYYY HH24:MI:SS') "P_Timestamp",
to_char (po.stime, 'DD-MON-YYYY HH24:MI:SS') "STIME",
decode (sign (po.stime - d.p_timestamp), 0, 'SAME', '*DIFFER*') "CHECK"
from sys.obj$ do, sys.dependency$ d, sys.obj$ po
where d.p_obj# = po.obj#(+) and d.d_obj# = do.obj#
--and do.status=1 /*dependent is valid*/
--and po.status=1 /*parent is valid*/
--and po.stime != d.p_timestamp /*parent timestamp not match*/
and do.name = 'TEST_PKG'
order by 2, 1;
D_OBJ D_NAME D_TYPE P_OBJ P_NAME P_Timestamp STIME CHECK
------- ---------- ------ ---------- ---------- ----------------------------- ----------------------------- ----------
6256610 TEST_PKG 9 1219 STANDARD 16-JUL-2018 00:00:00 16-JUL-2018 00:00:00 SAME
6256611 TEST_PKG 11 1219 STANDARD 16-JUL-2018 00:00:00 16-JUL-2018 00:00:00 SAME
6256611 TEST_PKG 11 6256609 TEST_TAB 25-AUG-2026 14:16:41 25-AUG-2026 14:16:41 SAME
6256611 TEST_PKG 11 6256610 TEST_PKG 25-AUG-2026 13:43:24 25-AUG-2026 13:43:24 SAME