utl_recomp and Automatic Statistics Gathering
DBA Team Lead and Oracle ACE Associate. Building database tools and helping teams manage Oracle infrastructure.
When recompiling invalid objects—often after patches, upgrades, or invalid dependency chains—you might call utl_recomp.recomp_serial. Since it's a serial procedure, one could reasonably expect it to avoid any parallel or background job interaction. But Oracle can be persuaded to behave a bit differently…
SQL> exec utl_recomp.recomp_serial('HR');
BEGIN utl_recomp.recomp_serial('HR'); END;
*
ERROR at line 1:
ORA-20000: Unable to gather statistics concurrently: the job_queue_processes parameter is less than 4
How does this make sense—serial recompilation wants to do concurrent statistics gathering? Also, the UTL_RECOMP documentation does not mention anything regarding statistics (I tried searching for the 'stat' keyword in this doc). So, what and why is it being gathered?
A quick spoiler, this happens if you set both of these:
alter system set job_queue_processes=0;
exec dbms_stats.set_global_prefs('concurrent', 'true');
Changing either one will make utl_recomp.recomp_serial() succeed.
Also, perhaps worth mentioning, this test case can be reproduced even if the example HR schema is empty.
Stats Gathering
This is the full stack trace:
SQL> exec utl_recomp.recomp_serial('HR');
BEGIN utl_recomp.recomp_serial('HR'); END;
*
ERROR at line 1:
ORA-20000: Unable to gather statistics concurrently: the job_queue_processes
parameter is less than 4
ORA-06512: at "SYS.UTL_RECOMP", line 927
ORA-06512: at "SYS.DBMS_STATS", line 40799
ORA-06512: at "SYS.DBMS_STATS", line 5065
ORA-06512: at "SYS.DBMS_STATS", line 40725
ORA-06512: at "SYS.UTL_RECOMP", line 260
ORA-06512: at "SYS.UTL_RECOMP", line 830
ORA-06512: at "SYS.UTL_RECOMP", line 940
ORA-06512: at line 1
According to the unwrapped utl_recomp source, those lines refer to the following functions:
recomp_serial()
-> recomp_parallel() line 940
-> select_invalid_parallel_objs() line 830
-> dbms_stats.gather_table_stats() line 260
Where, at line 260, there is:
260 DBMS_STATS.GATHER_TABLE_STATS('SYS', 'UTL_RECOMP_COMPILED',
261 ESTIMATE_PERCENT => DBMS_STATS.AUTO_SAMPLE_SIZE,
262 NO_INVALIDATE => FALSE);
So, utl_recomp.recomp_serial truly invokes dbms_stats.gather_table_stats with the intention of gathering statistics for a table called SYS.UTL_RECOMP_COMPILED:
SQL> desc SYS.UTL_RECOMP_COMPILED;
Name Null? Type
----------------- -------- ------------
OBJ# NOT NULL NUMBER
BATCH# NUMBER
COMPILED_AT TIMESTAMP(6)
COMPLETED_AT TIMESTAMP(6)
COMPILED_BY VARCHAR2(64)
which contains the list of compiled objects; it tracks which objects were compiled and in which batch.
Anyway, the dbms_stats.gather_table_stats must honor its global preferences, which can be set to use concurrency.
References
This blog post was tested on 19.29.0-EE.
Links to the official Oracle documentation: