Skip to main content

Command Palette

Search for a command to run...

utl_recomp and Automatic Statistics Gathering

Updated
2 min read
U

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: