Skip to Main Content

Breadcrumb

ORA-13199: MOVE TABLE=ORA-01950

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.