By Franck Pachot
.
Application Container brings a new way to share data among databases, and adds a new dimension to referential integrity. A foreign key in an application PDB can reference a row belonging to a root data link table. But then, should a delete on the root validate that there are no orpheans in the application PDBs? And what if those PDBs are closed at the time of this delete? Here is a small example.
If you run this in 12.2.0.1 you will get an error because the search for parent key is done only on the current container. This is considered as a bug: 21955394: CDB:ORA-02291 WHEN FOREIGN KEY REFERS TO THE PRIMARY KEY IN DATA LINK
The example that follows is run with the patch applied to fix this bug.
I’m connecting to root where I have no user PDB yet.
SQL> connect sys/oracle@//localhost/CDB1A as sysdba
Connected.
SQL> select con_id, name, application_root application_root, application_pdb application_pdb,application_root_con_id application_root_con_id from v$containers;
CON_ID NAME APPLICATION_ROOT APPLICATION_PDB APPLICATION_ROOT_CON_ID
------ ---- ---------------- --------------- -----------------------
1 CDB$ROOT NO NO
2 PDB$SEED NO NO
I create the application container root
SQL> create pluggable database SCOTT_ROOT as application container admin user SCOTT_ADMIN identified by oracle roles=(DBA);
Pluggable database SCOTT_ROOT created.
SQL> alter pluggable database SCOTT_ROOT open;
Pluggable database SCOTT_ROOT altered.
SQL> select con_id, name, application_root application_root, application_pdb application_pdb,application_root_con_id application_root_con_id from v$containers;
CON_ID NAME APPLICATION_ROOT APPLICATION_PDB APPLICATION_ROOT_CON_ID
------ ---- ---------------- --------------- -----------------------
1 CDB$ROOT NO NO
2 PDB$SEED NO NO
I connect to this application root and start the installation of the application
SQL> connect sys/oracle@//localhost/SCOTT_ROOT as sysdba
Connected.
SQL> alter pluggable database application SCOTT begin install '1.0';
Pluggable database APPLICATION altered.
I’m installing SCOTT tables DEPT and EMP tables but I changed their definition from utlsampl.sql:
- DEPT is an EXTENDED DATA LINK where a set of row is common, inserted on application root and visible by all application PDBs
- EMP is a METADATA LINK where each application PDB has its own data isolated from others, but having the same structure
SQL> GRANT CONNECT,RESOURCE,UNLIMITED TABLESPACE TO SCOTT IDENTIFIED BY tiger container=all;
Grant succeeded.
SQL> alter session set current_schema=SCOTT;
Session altered.
SQL> CREATE TABLE DEPT sharing=extended data
2 (DEPTNO NUMBER(2) CONSTRAINT PK_DEPT PRIMARY KEY,
3 DNAME VARCHAR2(14) ,
4 LOC VARCHAR2(13) ) ;
Table DEPT created.
SQL> CREATE TABLE EMP sharing=metadata
2 (EMPNO NUMBER(4) CONSTRAINT PK_EMP PRIMARY KEY,
3 ENAME VARCHAR2(10),
4 JOB VARCHAR2(9),
5 MGR NUMBER(4),
6 HIREDATE DATE,
7 SAL NUMBER(7,2),
8 COMM NUMBER(7,2),
9 DEPTNO NUMBER(2) CONSTRAINT FK_DEPTNO REFERENCES DEPT);
Table EMP created.
SQL> INSERT INTO DEPT VALUES
2 (10,'ACCOUNTING','NEW YORK');
1 row inserted.
SQL> INSERT INTO DEPT VALUES (20,'RESEARCH','DALLAS');
1 row inserted.
SQL> INSERT INTO DEPT VALUES
2 (30,'SALES','CHICAGO');
1 row inserted.
SQL> INSERT INTO DEPT VALUES
2 (40,'OPERATIONS','BOSTON');
1 row inserted.
SQL> COMMIT;
Commit complete.
SQL> alter pluggable database application SCOTT end install '1.0';
Pluggable database APPLICATION altered.
The application root has departments 10, 20, 30 and 40 in DEPT shared with all PDBs and has defined that EMP has a foreign key to it.
I create an application PDB from this application root
SQL> create pluggable database SCOTT_ONE admin user SCOTT_ONE_ADMIN identified by covfefe;
Pluggable database SCOTT_ONE created.
SQL> alter pluggable database SCOTT_ONE open;
Pluggable database SCOTT_ONE altered.
I sync it to get common DDL and DML applied
SQL> connect sys/oracle@//localhost/SCOTT_ONE as sysdba
Connected.
SQL> alter pluggable database application SCOTT sync;
Pluggable database APPLICATION altered.
SQL> select name,con_id,application_pdb,application_root_con_id from v$containers;
NAME CON_ID APPLICATION_PDB APPLICATION_ROOT_CON_ID
---- ------ --------------- -----------------------
SCOTT_ONE 8 YES 6
Now let’s connect to the application PDB. I can see the DEPT rows inserted from root because it is a DATA LINK.
SQL> connect scott/tiger@//localhost/SCOTT_ONE
Connected.
SQL> select * from dept;
DEPTNO DNAME LOC
------ ----- ---
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
EMP is empty here
SQL> select * from emp;
no rows selected
I insert an EMP row in the application PDB which references a DEPT row in the application root:
SQL> INSERT INTO EMP VALUES
2 (7369,'SMITH','CLERK',7902,to_date('17-12-1980','dd-mm-yyyy'),800,NULL,20);
1 row inserted.
As DEPT is and EXTENDED DATA LINK, I can add new rows in my PDB:
SQL> INSERT INTO DEPT VALUES
2 (50,'MY LOCAL DEPT','LAUSANNE');
1 row inserted.
And I can have an EMP row referencing this local parent:
SQL> INSERT INTO EMP VALUES
2 (7499,'ALLEN','SALESMAN',7698,to_date('20-2-1981','dd-mm-yyyy'),1600,300,50);
1 row inserted.
SQL> commit;
Commit complete.
This looks good. Now what happens of we delete all rows from DEPT in the application root?
SQL> connect sys/oracle@//localhost/SCOTT_ROOT as sysdba
Connected.
SQL> delete from SCOTT.DEPT;
4 rows deleted.
SQL> commit;
Commit complete.
No error here. But then, I have orphans in my application PDB:
SQL> connect scott/tiger@//localhost/SCOTT_ONE
Connected.
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
50 MY LOCAL DEPT LAUSANNE
SQL> select * from emp;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 50
So what?
Referential integrity works across containers: an application PDB can reference parent key in the application root (according that bug is fixed). However, no ORA-02292 (child record found) is raised when child records are not in the current container. This one makes sense. Enforcing the verification of child records in all PDBs would require that they are opened, and may require locking the table in all containers. We must be aware that doing DML on the application root can lead to inconsistency if not done correctly.
Operations on the application root are application releases (upgrades and patches) and must be validated and tested carefully. For the example above, deleting all rows from DEPT can be done as an application patch which deletes from the EMP table as well:
SQL> connect sys/oracle@//localhost/SCOTT_ROOT as sysdba
Connected.
SQL> alter pluggable database application SCOTT begin patch 1 ;
Pluggable database APPLICATION altered.
SQL> delete from scott.emp;
0 rows deleted.
SQL> delete from scott.dept where deptno in (10,20,30,40);
4 rows deleted.
SQL> alter pluggable database application SCOTT end patch 1 ;
Pluggable database APPLICATION altered.
The delete from EMP does nothing in the application root here, but it will be done on the PDB when applying the patch:
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
50 MY LOCAL DEPT LAUSANNE
SQL> select * from emp;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 50
Note that I’ve defined exactly which rows from DEPT I wanted to delete in the where clause of
delete from scott.dept where deptno in (10,20,30,40);
You may be tempted to do something like:
delete from scott.dept where deptno in (select deptno from scott.dept);
But keep in mind that the statements you run in the root are re-played as-is in the PDBs. And when you sync the PDB, you can see no rows from DEPT because there were already purged from the root. Actually, what you want is to delete from EMP the rows which refer to the rows you have deleted from the root. It is not possible to get them with a subquery, except if you have stored them into another data link table before deleting them. Changes in the application root must be managed like application patches.