Posted on

Editioning – The Missing Views, part 2

I bet you’re really wanting to hear about the other missing view — well here we are with part 2. The SYS.DBA_TAB_PRIVS_AE view seems a bit odd since it’s well documented that grants are not editioned in the database. That is true in a strict sense that a grant is not an object (versus a synonym, which is an object). A grant is more like a property of an object, if you delete the object, the grant also disappears (again, unlike a synonym). So though a grant does not have an edition associated with it, it does belong to an object and that object can be editioned.

If an object is newly created, it has no object grants. But if you do either an “alter compile” or “replace” (as in create or replace), then the grants are left from before. This applies across editions as well. If you instantiate an object in a child edition (with either compile or replace), it inherits all grants from the parent edition’s object. However, it does not maintain that relationship for future grants. After you instantiate an object in a child edition, it is de-coupled from the parent object and you need to maintain consistency of the grants between the editions (if that is important to you). I do this with a query on the SYS.DBA_TAB_PRIVS_AE_DH, the new DD view I will show you how to create, but first the SQL to fix any missing grants. This accepts 2 edition names and compares the grants for the objects in those 2 editions.

ACCEPT Edition1 CHAR PROMPT ‘First Edition Name: ‘
ACCEPT Edition2 CHAR PROMPT ‘Second Edition Name: ‘
PROMPT alter system set edition=&Edition1;;
SELECT ‘grant ‘||privilege||’ on ‘||owner||’.’||table_name||’ to ‘||grantee||’;’ FROM sys.dba_tab_privs_ae_dh WHERE edition_name = UPPER(‘&Edition2’)
MINUS
SELECT ‘grant ‘||privilege||’ on ‘||owner||’.’||table_name||’ to ‘||grantee||’;’ FROM sys.dba_tab_privs_ae_dh WHERE edition_name = UPPER(‘&Edition1’);
PROMPT
PROMPT alter system set edition=&Edition2;;
SELECT ‘grant ‘||privilege||’ on ‘||owner||’.’||table_name||’ to ‘||grantee||’;’ FROM sys.dba_tab_privs_ae_dh WHERE edition_name = UPPER(‘&Edition1’)
MINUS
SELECT ‘grant ‘||privilege||’ on ‘||owner||’.’||table_name||’ to ‘||grantee||’;’ FROM sys.dba_tab_privs_ae_dh WHERE edition_name = UPPER(‘&Edition2’);

Of course to run this, you will need to first create the sys.dba_tab_privs_ae_dh view like this:

create or replace view sys.dba_tab_privs_ae_dh
(grantee, owner, table_name, grantor, privilege, grantable, hierarchy, edition_name)
as
select ue.name, u.name, o.name, ur.name, tpm.name,
decode(mod(oa.option$,2), 1, ‘YES’, ‘NO’),
decode(bitand(oa.option$,2), 2, ‘YES’, ‘NO’), o.defining_edition
from sys.objauth$ oa, sys.”_ACTUAL_EDITION_OBJ” o, sys.user$ u, sys.user$ ur,
sys.user$ ue, table_privilege_map tpm
where oa.obj# = o.obj#
and oa.grantor# = ur.user#
and oa.grantee# = ue.user#
and oa.col# is null
and oa.privilege# = tpm.privilege
and u.user# = o.owner#;
comment on table SYS.dba_tab_privs_ae_dh is ‘All grants on objects in the database with edition’;
comment on column SYS.dba_tab_privs_ae_dh.GRANTEE is ‘User to whom access was granted’;
comment on column SYS.dba_tab_privs_ae_dh.OWNER is ‘Owner of the object’;
comment on column SYS.dba_tab_privs_ae_dh.TABLE_NAME is ‘Name of the object’;
comment on column SYS.dba_tab_privs_ae_dh.GRANTOR is ‘Name of the user who performed the grant’;
comment on column SYS.dba_tab_privs_ae_dh.PRIVILEGE is ‘Table Privilege’;
comment on column SYS.dba_tab_privs_ae_dh.GRANTABLE is ‘Privilege is grantable’;
comment on column SYS.dba_tab_privs_ae_dh.HIERARCHY is ‘Privilege is with hierarchy option’;
comment on column SYS.dba_tab_privs_ae_dh.edition_name  is ‘Name of the edition in which the object is actual’;
create or replace public synonym dba_tab_privs_ae_dh for sys.dba_tab_privs_ae_dh;

Posted on

Editioning – The Missing Views, part 1

In an earlier post, I mentioned the new data dictionary views that support editioning.  Here is a list of the DBA views (they also exist as USER and ALL, as most other DD views):

SYS.DBA_VIEWS_AE
SYS.DBA_SOURCE_AE
SYS.DBA_OBJECTS_AE
SYS.DBA_ERRORS_AE
SYS.DBA_EDITIONING_VIEW_COLS_AE
SYS.DBA_EDITIONING_VIEWS_AE

See anything missing? Most of the other views you might want are easily derivable by joining the traditional view (say dba_triggers) to the dba_objects_ae view. It still might be useful to have something like a dba_triggers_ae view, but I haven’t found it hard to work around that.Usually, I switch my session to the edition I want to work in if I really need to see something like the state of the triggers.

There are 2 views that I really wish were available, dba_dependencies_ae and dba_tab_privs_ae. I missed them so much that I ended up writing my own DD views for these. Switching between editions just doesn’t give me the ability to compare the way that I want to.

To create these new views, I compared the source for dba_objects with dba_objects_ae to see that changes would be needed, then created a new view to serve the purpose of dba_dependencies_ae. Do not use this name because it is very likely that a future upgrade from Oracle will recognize this shortcoming and create the view. Having a view with the same name already present could cause the upgrade to fail, so it’s better to make a name they won’t likely use, let’s say dba_dependencies_ae_dh.

Once you have this view, you can query for cross-edition dependencies with something like this:  SELECT * FROM sys.dba_dependencies_ae_dh WHERE edition_name <> referenced_edition_name;
Cross-edition dependencies are a “feature” of editioning that seems to have been a root cause of much of the instability that I experienced in the early days of implementing editioning. If you think about this a bit you can probably envision scenarios where the dependencies can get a bit confusing. My policy has become to never allow cross-edition dependencies, but to fully instantiate all the objects in each edition. That has the downside of increasing the object counts in your data dictionary, but I will cover how I deal with that in some future post.

Now for the source to create the view. Just copy all this and run it as SYS in your db.

create or replace view dba_dependencies_ae_dh
(owner, name, type, edition_name, object_id, referenced_owner, referenced_name, referenced_type, referenced_edition_name, referenced_object_id, referenced_link_name, dependency_type)
as
select u.name, o.name,
decode(o.type#, 0, ‘NEXT OBJECT’, 1, ‘INDEX’, 2, ‘TABLE’, 3, ‘CLUSTER’,
4, ‘VIEW’, 5, ‘SYNONYM’, 6, ‘SEQUENCE’, 7, ‘PROCEDURE’,
8, ‘FUNCTION’, 9, ‘PACKAGE’, 10, ‘NON-EXISTENT’,
11, ‘PACKAGE BODY’, 12, ‘TRIGGER’,
13, ‘TYPE’, 14, ‘TYPE BODY’, 22, ‘LIBRARY’,
28, ‘JAVA SOURCE’, 29, ‘JAVA CLASS’,
32, ‘INDEXTYPE’, 33, ‘OPERATOR’,
42, ‘MATERIALIZED VIEW’, 43, ‘DIMENSION’,
46, ‘RULE SET’, 55, ‘XML SCHEMA’, 56, ‘JAVA DATA’,
59, ‘RULE’, 62, ‘EVALUATION CONTXT’, 87, ‘ASSEMBLY’,
92, ‘CUBE DIMENSION’, 93, ‘CUBE’,
94, ‘MEASURE FOLDER’, 95, ‘CUBE BUILD PROCESS’,
‘UNDEFINED’),
o.defining_edition,
o.obj#,
decode(po.linkname, null, pu.name, po.remoteowner), po.name,
decode(po.type#, 0, ‘NEXT OBJECT’, 1, ‘INDEX’, 2, ‘TABLE’, 3, ‘CLUSTER’,
4, ‘VIEW’, 5, ‘SYNONYM’, 6, ‘SEQUENCE’, 7, ‘PROCEDURE’,
8, ‘FUNCTION’, 9, ‘PACKAGE’, 10, ‘NON-EXISTENT’,
11, ‘PACKAGE BODY’, 12, ‘TRIGGER’,
13, ‘TYPE’, 14, ‘TYPE BODY’, 22, ‘LIBRARY’,
28, ‘JAVA SOURCE’, 29, ‘JAVA CLASS’,
32, ‘INDEXTYPE’, 33, ‘OPERATOR’,
42, ‘MATERIALIZED VIEW’, 43, ‘DIMENSION’,
46, ‘RULE SET’, 55, ‘XML SCHEMA’, 56, ‘JAVA DATA’,
59, ‘RULE’, 62, ‘EVALUATION CONTXT’, 87, ‘ASSEMBLY’,
92, ‘CUBE DIMENSION’, 93, ‘CUBE’,
94, ‘MEASURE FOLDER’, 95, ‘CUBE BUILD PROCESS’,
‘UNDEFINED’),
po.editionname,
po.obj#,
po.linkname,
decode(bitand(d.property, 3), 2, ‘REF’, ‘HARD’)
from sys.”_ACTUAL_EDITION_OBJ” o, sys.dependency$ d, sys.user$ u, sys.user$ pu,
(select co.obj#, co.owner#, co.name, co.type#, co.remoteowner, co.linkname, co.defining_edition editionname from sys.”_ACTUAL_EDITION_OBJ” co
union all
select kobjn_kqfp, 0, name_kqfp, decode(type_kqfp, 1, 9, 2, 11, 3, 10, 0), NULL, NULL, NULL from sys.x$kqfp
union all
select kqftaobj, 0, kqftanam, 2, NULL, NULL, NULL from sys.x$kqfta
union all
select kqfviobj, 0, kqfvinam, 4, NULL, NULL, NULL from sys.x$kqfvi) po
where o.obj# = d.d_obj#
and o.owner# = u.user#
and po.obj# = d.p_obj#
and po.owner# = pu.user#;
comment on column dba_dependencies_ae_dh.OWNER is ‘Owner of the object’;
comment on column dba_dependencies_ae_dh.NAME is ‘Name of the object’;
comment on column dba_dependencies_ae_dh.TYPE is ‘Type of the object’;
comment on column dba_dependencies_ae_dh.EDITION_NAME is ‘Name of edition for which this object is active’;
comment on column dba_dependencies_ae_dh.OBJECT_ID is ‘ID of this object’;
comment on column dba_dependencies_ae_dh.REFERENCED_OWNER is ‘Owner of referenced object (remote owner if remote object)’;
comment on column dba_dependencies_ae_dh.REFERENCED_NAME is ‘Name of referenced object’;
comment on column dba_dependencies_ae_dh.REFERENCED_TYPE is ‘Type of referenced object’;
comment on column dba_dependencies_ae_dh.REFERENCED_EDITION_NAME is ‘Name of edition for which referenced object is active’;
comment on column dba_dependencies_ae_dh.REFERENCED_OBJECT_ID is ‘ID of referenced object’;
comment on column dba_dependencies_ae_dh.REFERENCED_LINK_NAME is ‘Name of dblink if this is a remote object’;
create or replace public synonym dba_dependencies_ae_dh for sys.dba_dependencies_ae_dh;

Posted on

Editioning – new Data Dictionary Views

There are of course some changes to data dictionary views to make them understand editions. Often you’ll find a new column called “edition” or “edition_name” and that contains exactly what you’d expect. But in this new world where objects that used to be unique in their namespace can now be instantiated in multiple editions, how can you see what is in an edition other than the one your session is using?

That is the purpose of the “_ae” views. That stands for “all editions”, by the way. These views also contain the edition column that has appeared on the traditional views, but these views can contain multiple rows. I’ll use dba_objects and dba_objects_ae to illustrate this. The effective PK for dba_objects is owner + object_name + object_type, in other words, if you were to get counts grouping by these 3 columns, you would find them all to be 1. If you ran the same query on dba_objects_ae you might get some rows with counts > 1 because it contains rows from multiple editions. The edition shown in dba_objects rows will not always be the current edition of your session because you might be inheriting some objects from parent editions, but the edition name will never be a child edition of your current edition (children inherit from parents, never the other way). That is not true for dba_objects_ae, it will show you objects in editions that are children to your current edition, it ignores your current edition and shows you everything. A good analogy is to consider the difference between user_tables and dba_tables, the first filters out any tables you do not own, the second shows them all. Similarly, dba_objects filters out all objects that are not visible in your current edition, dba_objects_ae shows them all.

So for general purposes, use the views that you always have — they are your session’s “reality”, but to query objects across all the editions use the “_ae” views. They might seem rather pointless to you now, but get familiar with them anyway, because you will need them in the future.

You might also find that there are some missing “_ae” views. I have found two in particular that I needed and had to create myself. I will address those in future posts because they are large topics in themselves and need enough explanation to warrant their own post.

Posted on

Editioning Views

The editioning view (EV) is a new object type that sits between the table (which cannot be editioned) and the code objects that reference the table. It acts like a table in many ways – you can do DML without an instead of trigger, and you can create a normal trigger on the EV.

The main function of an EV is to allow you to alias columns, and to make those aliases editionable. It is functionally close to an editioned table. You can’t restrict rows or join tables in an EV. The set of allowed actions is really quite restrictive, but the ability to change the underlying column that a view column points to is a powerful feature that you will end up using quite a lot, so plan to switch all your tables to use EV’s in your conversion plans.

Converting a table to use an EV involves the following steps:

1. Save the source of all the triggers on the table

2. Generate and save the DDL for the grants to this table

3. Drop any public synonyms that point to the table

4. Rename the table – I added a ‘_’ to the end of each table. Any that failed because the resulting name was too long has the last char of the name replaced with a ‘_’. This is needed because the EV occupies the same Oracle namespace as the table, and you don’t want to have to change the name in all of your regular views and PL/SQL.

5. Create an EV for the table with the same name as the old table – “create editioning view mytable as select * from mytable_;”

6. Drop the triggers from the table and create them on the EV, using the source code you saved in step #1.

7. Run the grant DDL from step #2 to allow all the access to the EV that the original table had. Revoke the grants from the table, everyone must now use the EV.

8. Create private synonyms for any schemas that were using the public synonym. If you lack the time to determine this, you can create them for all the users. I addressed the synonym issues in an earlier post.

9. Recompile all the objects that are invalid.

When you are done the indexes and constraints still refer to the underlying table, but all of these will reference the EV:

triggers

views

PL/SQL program objects (packages, standalone procs and funcs)

synonyms

grants