Fehler beim datapatch Ausführung beim Oktober 2020 CPU
Beim Einspielen des aktuellen CPU auf meiner Oracle 19c Datenbank bin ich über folgenden Fehler beim datapatch gestolpert:
Patch 31771877 apply: WITH ERRORS
logfile: /u01/app/oracle/cfgtoollogs/sqlpatch/31771877/23869227/31771877_apply_DB19EE_2020Oct23_14_23_23.log (errors)
-> Error at line 69823: script md/admin/prvtimd.plb
- ORA-13199: MOVE TABLE=ORA-01950: keine Berechtigungen fur Tablespace 'DATA1'
Analyse
Also sehen wir einmal in das Logfile an. Anmerkung: anscheinend lieben es die Entwickler bei Oracle, unnötige Leerzeilen in PLSQL Blöcken zu haben. Aus Platzgründen in diesem Artikel haben wir nur die relevanten Code-Teile übernommen, weit über 100 Leerzeilen und für das Problem nicht relevanter Code wurde entfernt.
Die genaue Fehlermeldung
FEHLER in Zeile 1:
ORA-13199: MOVE TABLE=ORA-01950: keine Berechtigungen fur Tablespace 'DATA1'
ORA-06512: in "MDSYS.MD", Zeile 1723
ORA-06512: in "MDSYS.MDERR", Zeile 17
ORA-06512: in Zeile 171
Der Fehler ist ein PLSQL Block wo ein CREATE Statement für eine Tabelle MDSYS.SDO_TXN_JOURNAL_REG zusammengebastelt wird.
Relevante Code Teile
Was passiert in dem PLSQL Block, der den Fehler auslöst?
DECLARE
2 stmt varchar2(4000);
3 tblspc varchar2(260);
4 tblspc_quoted varchar2(262);
5 ptn_clause varchar2(1000);
6 local_clause varchar2(1000);
7 block_size number;
8 e_no_feature EXCEPTION;
9 PRAGMA EXCEPTION_INIT(e_no_feature, -439);
10 e_block_size EXCEPTION;
11 PRAGMA EXCEPTION_INIT(e_block_size, -14520);
12 BEGIN
13 tblspc := 'SYSAUX';
14 tblspc_quoted := sys.dbms_assert.enquote_name(tblspc, false);
15 ptn_clause := 'partition by range (sid) interval (50) ' ||
16 '(partition p1 values less than (50)) ';
17 local_clause := 'LOCAL';
18 for i in 1..2
19 loop
20 stmt :=
21 'CREATE TABLE MDSYS.SDO_TXN_JOURNAL_REG ' ||
22 '(SID NUMBER, ' ||
23 'SDO_TXN_IDX_ID varchar2(130), ' ||
24 'OPERATION number, ' ||
25 'RID varchar2(24), ' ||
26 'INDEXPTNIDEN number, ' ||
27 'TABLEPTNIDEN number, ' ||
28 'START_1 binary_double, ' ||
29 'END_1 binary_double, ' ||
30 'START_2 binary_double, ' ||
31 'END_2 binary_double, ' ||
32 'START_3 binary_double, ' ||
33 'END_3 binary_double, ' ||
34 'START_4 binary_double, ' ||
35 'END_4 binary_double ' ||
36 ') ' ||
37 ptn_clause ||
38 'TABLESPACE ' || tblspc_quoted;
39
40 begin
41 execute immediate stmt;
42 exit;
43
44 exception
45
46 when e_no_feature then
47 ptn_clause := '';
48 local_clause := '';
49 when others then
50 MDERR.RAISE_MD_ERROR('MD','SDO',-13199, 'CREATE INDEX=' || SQLERRM);
51 end;
52 end loop;
Somit wird die Tabelle einmal im SYSAUX Tablespace angelegt, aber dann kommt´s! Oracle schaut nach, welcher andere PERMANENTE, ONLINE Tablespace mit LOCAL EXTENT MANAGEMENT und gleicher Blocksize existiert und sucht sich einen aus. In meinem Fall gewinnt der Tablespace DATA1.
127 stmt := 'select tablespace_name from sys.dba_tablespaces ' ||
128 'where contents = ''PERMANENT'' ' ||
129 'and tablespace_name <> ''SYSTEM'' ' ||
130 'and status = ''ONLINE'' ' ||
131 'and extent_management = ''LOCAL'' ' ||
132 'and allocation_type = ''SYSTEM'' ' ||
133 'and block_size = :blk ' ||
134 'and rownum = 1 ' ||
135 'order by tablespace_name ';
136 begin
137
138 execute immediate stmt into tblspc using block_size;
139 exception
140 when others then
141 tblspc := 'SYSAUX';
142 end;
Und jetzt wird es absolut schräg ...
153 stmt := 'ALTER TABLE "MDSYS"."SDO_TXN_JOURNAL_REG" ' ||
154 'MOVE PARTITION "P1" TABLESPACE ' || tblspc_quoted;
155 execute immediate stmt;
156
163 stmt := 'ALTER TABLE "MDSYS"."SDO_TXN_JOURNAL_REG" ' ||
164 'MODIFY DEFAULT ATTRIBUTES TABLESPACE ' || tblspc_quoted;
165 execute immediate stmt;
Da wird doch glatt die Tabelle (besteht nur aus einer Partition) einfach so in diesen (falschen) Tablespace verschoben, und – oh Wunder – dort hat MDSYS keine QUOTAS und fliegt mit dem Fehler:
ORA-13199: MOVE TABLE=ORA-01950: keine Berechtigungen fur Tablespace 'DATA1'
ORA-06512: in "MDSYS.MD", Zeile 1723
ORA-06512: in "MDSYS.MDERR", Zeile 17
ORA-06512: in Zeile 171
Fragen, die sich stellen
- Wie kommt man auf die Idee, eine Data Dictionary Tabelle einfach in IRGENDEINEN Tablespace zu verschieben?
- Wofür soll das gut sein?
- Anscheinend ist dem Schreiber dieses Codes nicht bekannt, dass es in der Datenbank so etwas wie Tablespace Quotas gibt und sich auch Oracle Schemas daran halten müssen?
- Warum lässt man den Ferialpraktikanten an so einen Code?
- Dass das Statement, mit dem der Tablespace ermittelt wird, auch noch offensichtlich fehlerhaft ist (dazu mehr am Ende des Artikels)?
Workaround zu dem Problem
Zuerst herausfinden, welchen Tablespace MDSYS vermutlich wählen wird - das kann man auch schon BEVOR man datapatch erstmalig startet!
select tablespace_name
from sys.dba_tablespaces
where contents = 'PERMANENT'
and tablespace_name <> 'SYSTEM'
and status = 'ONLINE'
and extent_management = 'LOCAL'
and allocation_type = 'SYSTEM'
and block_size = (select BLOCK_SIZE from sys.dba_tablespaces where tablespace_name='SYSAUX')
and rownum = 1
order by tablespace_name;
TABLESPACE_NAME
---------------
DATA1
Und dann für entsprechende Quotas sorgen:
alter user MDSYS quota unlimited on DATA1;
Und datapatch erneut starten
$ORACLE_HOME/OPatch/datapatch -verbose
Nachdem das Patching abgeschlossen ist, empfehlen wir Ihnen, die Tabelle wieder in den SYSAUX Tablespace zurück zu schieben. Sollten Sie Ihren Tablespace irgendwann READ ONLY setzen oder löschen, könnt das sonst massive Probleme erzeugen.
ALTER TABLE "MDSYS"."SDO_TXN_JOURNAL_REG" MOVE PARTITION "P1" TABLESPACE SYSAUX;
ALTER TABLE "MDSYS"."SDO_TXN_JOURNAL_REG" MODIFY DEFAULT ATTRIBUTES TABLESPACE SYSAUX;
ALTER USER MDSYS QUOTA 0 ON DATA1;
Jetzt sollte alles wieder im grünen Bereich sein, vorausgesetzt, dass der Oracle Optimizer sich keinen anderen Execution Plan überlegt, weil das „rownum = 1“ in diesem Statement definitiv nicht dort steht, wo es sinnvoll wäre. Damit bekommt man nicht garantierter Weise den ersten nach dem ORDER BY, sondern den ersten, den die Query überhaupt findet!
Die inhaltlich korrekte Query wäre vermutlich:
select tablespace_name
from sys.dba_tablespaces
where contents = 'PERMANENT'
and tablespace_name <> 'SYSTEM'
and status = 'ONLINE'
and extent_management = 'LOCAL'
and allocation_type = 'SYSTEM'
and block_size = (select BLOCK_SIZE from sys.dba_tablespaces where tablespace_name='SYSAUX')
order by tablespace_name
fetch first 1 row only;
Update seitens Oracle vom 13. Nov 2020
Oracle hat das Problem als Bug 32150854 - DATAPATCH FAILS WITH ORA-13199: MOVE TABLE=ORA-01950: NO PRIVILEGES ON TABLESPACE akzeptiert und arbeitet an einer Behebung.
Update seitens Oracle vom 17. Dez 2020
Der Bug ist behoben und wir Teil vom April 2021 CPU sein. Für alle, die den von uns entwickelten Workaround nicht umsetzen wollen, gibt es die Möglichkeit einen Service Request mit einem Backport/OneOff zu beantragen, sofern dieser seitens Oracle nicht präventiv zur Verfügung gestellt wird.