Introduction
Cet article propose les requêtes indispensables pour préparer les comptes, rôles, privilèges, etc. dans une instance Oracle qui va être chargée à partir d’un export. Dans cet exemple, les instances source et cible sont appelées respectivement INSTANCE1 (Oracle 10g) et INSTANCE2 (Oracle 10g). La procédure d’export/import a été réalisée dans le cadre d’une migration de plateforme de Sun Solaris SPARC vers Sun Solaris X86 (AMD), la méthode des tablespaces transportables (transportable tablespaces ou TTS) n’a pas pu être appliquée car des tables et indexes partitionnés en mode IOT (Index Organized Tables) sont présents dans les schémas et ces fonctionnalités de partitionnement ne sont pas supportées dans la technologie TTS 10g.
Si les profils, comptes, rôles, synonymes publics, etc. sont prêts à
l’avance dans l’instance de destination, cela permet d’éviter dans le fichier
de log de l’import les erreurs IMP-00017
et IMP-00003
et devoir réattribuer par
la suite les droits et privilèges nécessaires : en voici des exemples
Compte inexistant :
IMP-00017: following statement failed with ORACLE error 1917:
"GRANT SELECT ON "AMRECON_FORMAT_POS_PERTINENCE" TO "RISK_CONSULT""
IMP-00003: ORACLE error 1917 encountered
ORA-01917: user or role 'RISK_CONSULT' does not exist
Privilèges insuffisants :
IMP-00003: ORACLE error 1950 encountered
ORA-01950: no privileges on tablespace 'USERS'
La création de la base de données de l’instance 2 n’est pas évoquée ici.
L’export de l’instance 1 a été réalisé avec la commande exp ci-dessous :
oracle[INSTANCE1]> exp "'/ as sysdba'" FILE=INSTANCE1.dmp LOG=INSTANCE1.log FULL=Y
Il s’agit d’un export complet (full) de l’instance 1, export pour lequel l’option CONSISTENT=Y n’a pas été appliquée car les tablespaces ont été mis en mode lecture seule (READ ONLY) durant l’export.
L’import dans l’instance 2 est réalisé schéma par schéma avec le commande imp ci-dessous :
oracle[INSTANCE2]> imp "'/ as sysdba'" FILE=INSTANCE1.dmp LOG=<schema>.log \
FROMUSER=<schema> TOUSER=<schema> COMMIT=Y
Les schémas systèmes ne sont pas importés.
2 méthodes sont étudiées pour réaliser le reverse :
- Reverse à partir des vues du dictionnaire Oracle dans l’instance source INSTANCE1.
- Reverse à partir d’un export "squelette" de l’instance source INSTANCE1. Pour cette méthode, la plateforme est de type Unix.
Un script shell pour générer automatiquement les scripts SQL de création des profils, des comptes, des rôles, des privilèges, des synonymes publics et privés, des droits, etc. est proposé en annexe pour la méthode s’appuyant sur les vues du dictionnaire.
Création d’un export squelette ou export DDL de l’instance source
En spécifiant ROWS=N
dans la commande d’export d’une instance en mode FULL,
l’export obtenu ne contient que les commandes DDL (Data Definition Language)
comme CREATE USER
, CREATE TABLE
, etc.
oracle[INSTANCE1]> exp "'/ as sysdba'" FILE=INSTANCE1_SKELETON.dmp \
LOG=INSTANCE1_SKELETON.log FULL=Y ROWS=N
L’export des commandes DDL est lisible et exploitable avec des commandes
Unix comme awk, sed, grep
, etc. pour extraire les informations
nécessaires.
Génération des profils, "reverse" des commandes CREATE PROFILE
Le script de création des profils est exécuté sur l’instance cible avant l’import.
À partir des vues du dictionnaire
Les définitions des profils personnalisés sont récupérées à partir de la vue
DBA_PROFILES
en écartant la génération pour le profil système DEFAULT
.
Les commandes CREATE PROFILE
sont d’abord générées avec l’option LIMIT
CPU_PER_SESSION DEFAULT
car une option LIMIT
au moins est obligatoire lors de
la commande CREATE PROFILE
. Les valeurs des autres options pour les profils
sont ensuite mises à jour avec les commandes ALTER PROFILE
générées également
depuis la vue DBA_PROFILES
.
oracle[INSTANCE1]> sqlplus "/ as sysdba"
set heading off; set feedback off; set linesize 800; set pagesize 500; select distinct 'CREATE PROFILE "'||profile||'" LIMIT CPU_PER_SESSION DEFAULT;' from dba_profiles where profile != 'DEFAULT'; select 'ALTER PROFILE "'||profile||'" LIMIT '||RESOURCE_NAME||' '||LIMIT||';' from dba_profiles where profile !='DEFAULT' order by profile, resource_name;
… CREATE PROFILE "SOP_PROFILE" LIMIT CPU_PER_SESSION DEFAULT; … ALTER PROFILE "SOP_PROFILE" LIMIT IDLE_TIME 2880; …
À partir de l’export DDL
À partir de l’export, il suffit de rechercher le mot clé CREATE PROFILE
(la
création du profil DEFAULT
n’est pas consignée dans l’export).
oracle[INSTANCE1]> cat INSTANCE1_SKELETON.dmp | grep -i 'create profile' | awk '{print $0";"}'
CREATE PROFILE "SOP_PROFILE" LIMIT COMPOSITE_LIMIT DEFAULT SESSIONS_PER_USER DEFAULT CPU_PER_SESSION DEFAULT CPU_PER_CALL DEFAULT LOGICAL_READS_PER_SESSION DEFAULT LOGICAL_READS_PER_CALL DEFAULT IDLE_TIME 2880 CONNECT_TIME DEFAULT PRIVATE_SGA DEFAULT FAILED_LOGIN_ATTEMPTS DEFAULT PASSWORD_LIFE_TIME DEFAULT PASSWORD_REUSE_TIME DEFAULT PASSWORD_REUSE_MAX DEFAULT PASSWORD_LOCK_TIME DEFAULT PASSWORD_GRACE_TIME DEFAULT;
Génération des comptes, "reverse" des commandes CREATE USER
Le script de création des comptes est exécuté sur l’instance cible avant l’import.
À partir des vues du dictionnaire
La vue DBA_USERS
est utilisée sur l’environnement source pour réaliser le
reverse SQL de création des comptes avec les commandes CREATE USER
en
conservant les mots de passe, le tablespace de données par défaut, le
tablespace temporaire par défaut, le profil. Les comptes systèmes Oracle sont
écartés dans la génération :
oracle[INSTANCE1]> sqlplus "/ as sysdba"
set heading off; set feedback off; set linesize 800; set pagesize 500; select 'CREATE USER "'||username||'" IDENTIFIED BY VALUES '''||password||''' DEFAULT TABLESPACE "'||default_tablespace||'" TEMPORARY TABLESPACE "'||temporary_tablespace||'" ' ||decode(profile,'DEFAULT','','PROFILE "'||profile||'"') ||' ;' from dba_users where username not in ( 'SYS', 'OUTLN', 'SYSTEM', 'CTXSYS', 'DBSNMP', 'LOGSTDBY_ADMINISTRATOR', 'ORDSYS', 'ORDPLUGINS', 'OEM_MONITOR', 'WKSYS', 'WKPROXY', 'WK_TEST', 'WKUSER', 'MDSYS', 'LBACSYS', 'DMSYS', 'WMSYS', 'OLAPDBA', 'OLAPSVR', 'OLAP_USER', 'OLAPSYS', 'EXFSYS', 'SYSMAN', 'MDDATA', 'SI_INFORMTN_SCHEMA', 'XDB', 'ODM','ORACLE_OCM','DIP', 'TSMSYS','TRACESVR','PERFSTAT' )
… CREATE USER "SOP_MODIF" IDENTIFIED BY VALUES 'C632850999096E40' DEFAULT TABLESPACE "USERS" TEMPORARY TABLESPACE "TEMP" PROFILE "SOP_PROFILE" ; …
À partir de l’export DDL
À partir de l’export DDL, le mot clé CREATE USER
est recherché en écartant
les comptes systèmes :
oracle[INSTANCE1]> cat INSTANCE1_SKELETON.dmp | grep -i 'create user' | \ egrep -iv 'SYS|OUTLN|SYSTEM|CTXSYS|DBSNMP|\ LOGSTDBY_ADMINISTRATOR|ORDSYS|\ ORDPLUGINS|OEM_MONITOR|WKSYS|WKPROXY|\ WK_TEST|WKUSER|MDSYS|LBACSYS|DMSYS|\ WMSYS|OLAPDBA|OLAPSVR|OLAP_USER|\ OLAPSYS|EXFSYS|SYSMAN|MDDATA|\ SI_INFORMTN_SCHEMA|XDB|ODM|ORACLE_OCM|DIP|\ TSMSYS|TRACESVR|PERFSTAT'|\ awk '{print $0";"}'
… CREATE USER "RISK" IDENTIFIED BY VALUES '301A0E1D8E9EB50E' DEFAULT TABLESPACE "USERS" TEMPORARY TABLESPACE "TEMP" PROFILE "SOP_PROFILE"; …
Génération des rôles, "reverse" des commandes CREATE ROLE
Le script de création des rôles est exécuté sur l’instance cible avant l’import.
À partir des vues du dictionnaire
Pour réaliser le reverse SQL des rôles applicatifs, les vues DBA_ROLES
,
ROLE_SYS_PRIVS
et ROLE_TAB_PRIVS
sont interrogées :
oracle[INSTANCE1]> sqlplus "/ as sysdba"
set heading off; set feedback off; set linesize 800; set pagesize 500; select 'CREATE ROLE "'||role||'";' from dba_roles where role not in (select distinct role from role_sys_privs) and role not in (select distinct role from role_tab_privs) and role != 'GLOBAL_AQ_USER_ROLE'
CREATE ROLE "RISK_MODIF"; CREATE ROLE "RISK_CONSULT";
À partir de l’export DDL
Le mot clé CREATE ROLE
est recherché en écartant les rôles systèmes. Dans
l’exemple ci-dessous, aucun rôle utilisateur n’a une nomenclature qui
s’apparente à la nomenclature des rôles systèmes (AQ_, _CATALOG_, etc.).
oracle[INSTANCE1]> cat INSTANCE1_SKELETON.dmp | grep -i 'create role' | \ egrep -iv 'SECURITY_|LOGSTDBY_|SCHEDULER|_CATALOG_|AQ_|OEM_|HS_|PLUSTRACE|STATISTICS' | \ awk '{print $0";"}'
… CREATE ROLE "RISK_MODIF"; …
Génération des privilèges pour les rôles et comptes, "reverse" des commandes GRANT <role> to <account|role>
Le script d’attribution des privilèges est exécuté sur l’instance cible avant l’import.
Pour réaliser l’attribution des privilèges de type CONNECT
, RESOURCE
, etc.
aux rôles et comptes, le reverse peut être réalisé depuis la vue DBA_ROLE_PRIVS
en écartant les rôles et comptes systèmes Oracle :
oracle[INSTANCE1]> sqlplus "/ as sysdba"
set heading off; set feedback off; set linesize 800; set pagesize 2000; select 'GRANT "'||granted_role||'" TO "'||grantee||'";' from dba_role_privs where grantee not in (select distinct role from role_sys_privs) and grantee not in (select distinct role from role_tab_privs) and grantee not in ( 'SYS', 'OUTLN', 'SYSTEM', 'CTXSYS', 'DBSNMP', 'LOGSTDBY_ADMINISTRATOR', 'ORDSYS', 'ORDPLUGINS', 'OEM_MONITOR', 'WKSYS', 'WKPROXY', 'WK_TEST', 'WKUSER', 'MDSYS', 'LBACSYS', 'DMSYS', 'WMSYS', 'OLAPDBA', 'OLAPSVR', 'OLAP_USER', 'OLAPSYS', 'EXFSYS', 'SYSMAN', 'MDDATA', 'SI_INFORMTN_SCHEMA', 'XDB', 'ODM','ORACLE_OCM','DIP', 'TSMSYS','TRACESVR','PERFSTAT')
GRANT "CONNECT" TO "SOP_TRT"; GRANT "CONNECT" TO "SOP_AHEAD"; …
La génération des privilèges attribués aux comptes et rôles est très ardue depuis un fichier d’export DDL.
Génération des synonymes publics, "reverse" des commandes CREATE PUBLIC SYNONYM
Le script de création des synonymes publics est exécuté après l’import dans l’instance cible.
À partir des vues du dictionnaire
Les synonymes publiques sont générés automatiquement à partir de la vue
DBA_SYNONYMS
en sélectionnant les synonymes dont le propriétaire est PUBLIC et
en écartant les objets (table_name) appartenant aux comptes systèmes Oracle. La
taille de page (set pagesize) doit être ajustée en fonction du nombre de
synonymes publics :
oracle[INSTANCE1]> sqlplus "/ as sysdba"
set heading off; set feedback off; set linesize 800; set pagesize 50000; select 'CREATE PUBLIC SYNONYM "'||synonym_name||'" FOR "'||table_owner||'"."'||table_name||'";' from dba_synonyms where owner ='PUBLIC' and table_owner not in ( 'SYS', 'OUTLN', 'SYSTEM', 'CTXSYS', 'DBSNMP', 'LOGSTDBY_ADMINISTRATOR', 'ORDSYS', 'ORDPLUGINS', 'OEM_MONITOR', 'WKSYS', 'WKPROXY', 'WK_TEST', 'WKUSER', 'MDSYS', 'LBACSYS', 'DMSYS', 'WMSYS', 'OLAPDBA', 'OLAPSVR', 'OLAP_USER', 'OLAPSYS', 'EXFSYS', 'SYSMAN', 'MDDATA', 'SI_INFORMTN_SCHEMA', 'XDB', 'ODM','ORACLE_OCM','DIP', 'TSMSYS','TRACESVR','PERFSTAT')
CREATE PUBLIC SYNONYM "TITRES" FOR "RISK"."TITRES"; …
À partir de l’export DDL
À partir de l’export DDL, le mot clé CREATE PUBLIC SYNONYM
est recherché en
écartant les synonymes publics pour les compes SYS, SYSTEM, PERFSTAT
ainsi que
les synonymes publics ayant une nomenclature OL$%, %$LOB
, etc.
oracle[INSTANCE1]> cat INSTANCE1_SKELETON.dmp | grep -i 'create public synonym ' |\ egrep -iv '"SYS".|"SYSTEM".|"PERFSTAT".|OL\$|\$LOB|"PRODUCT_PRIVS"' |\ awk '{print $0";"}'
… CREATE PUBLIC SYNONYM "TITRES" FOR "RISK"."TITRES"; …
Génération des synonymes privés, "reverse" des commandes CREATE SYNONYM
Les synonymes privés sont créés lors de l’import des schémas. La génération des commandes de création des synonymes privés est donnée ici à titre informatif.
Comme pour les synonymes publics, les synonymes privés sont générés à partir
de la vue DBA_SYNONYMS
en sélectionnant les synonymes pour lesquels les
propriétaire du synonyme et de l’objet (table_name) ne sont pas PUBLIC ou un
compte système Oracle. La taille de page (set pagesize) doit être ajustée en
fonction du nombre de synonymes privés :
oracle[INSTANCE1]> sqlplus "/ as sysdba"
set heading off; set feedback off; set linesize 800; set pagesize 50000; select 'CREATE SYNONYM "'||owner||'"."'||synonym_name||'" for "'||table_owner||'"."'||table_name|| '";' from dba_synonyms where owner not in ( 'SYS', 'OUTLN', 'SYSTEM', 'CTXSYS', 'DBSNMP', 'LOGSTDBY_ADMINISTRATOR', 'ORDSYS', 'ORDPLUGINS', 'OEM_MONITOR', 'WKSYS', 'WKPROXY', 'WK_TEST', 'WKUSER', 'MDSYS', 'LBACSYS', 'DMSYS', 'WMSYS', 'OLAPDBA', 'OLAPSVR', 'OLAP_USER', 'OLAPSYS', 'EXFSYS', 'SYSMAN', 'MDDATA', 'SI_INFORMTN_SCHEMA', 'XDB', 'ODM','ORACLE_OCM','DIP', 'TSMSYS','TRACESVR','PERFSTAT' ) and table_owner not in ( 'SYS', 'OUTLN', 'SYSTEM', 'CTXSYS', 'DBSNMP', 'LOGSTDBY_ADMINISTRATOR', 'ORDSYS', 'ORDPLUGINS', 'OEM_MONITOR', 'WKSYS', 'WKPROXY', 'WK_TEST', 'WKUSER', 'MDSYS', 'LBACSYS', 'DMSYS', 'WMSYS', 'OLAPDBA', 'OLAPSVR', 'OLAP_USER', 'OLAPSYS', 'EXFSYS', 'SYSMAN', 'MDDATA', 'SI_INFORMTN_SCHEMA', 'XDB', 'ODM','ORACLE_OCM','DIP', 'TSMSYS','TRACESVR','PERFSTAT' ) and owner !='PUBLIC';
CREATE SYNONYM "RISK_PURGE"."EXTRNL_REFERENCES_AUDIT" for "RISK"."EXTRNL_REFERENCES_AUDIT"; …
La génération des synonymes privés à partir de l’export DDL est nettement
moins aisée. La commande CONNECT <schema>;
est en effet lancée en amont
des commandes CREATE SYNONYM
lors d’un import, ainsi le synonyme n’est pas
préfixé par le nom du schéma à sa création.
CONNECT RISK
...
CREATE SYNONYM "MAJHISTOTAUX" FOR "RISK"."MAJHISTOTAUX"
Génération des droits, "reverse" des commandes GRANT SELECT, INSERT.... ON
Comme pour les synonymes privés, les droits (SELECT, INSERT, EXECUTE
…)
sont attribués lors de l’import des schémas. La génération des commandes GRANT
est donnée ici à titre informatif.
Les droits sont très facilement générés à partir de la vue DBA_TAB_PRIVS
en
écartant les comptes Oracle systèmes. Malgré la nomenclature _TAB_ qui peut
faire penser aux privilèges sur les tables, cette vue contient également les
privilèges sur les vues, procédures, fonctions, packages, etc. Cette fois
encore le paramètre pagesize dans la session SQL*Plus doit être adapté au
nombre d’objets et de privilèges.
oracle[INSTANCE1]> sqlplus "/ as sysdba"
set heading off; set feedback off; set linesize 800; set pagesize 50000; select 'GRANT '||privilege||' ON "'||owner||'"."'||table_name||'" TO "'||grantee||'"'||decode(grantable, 'YES', ' WITH GRANT OPTION') from dba_tab_privs where owner not in ( 'SYS', 'OUTLN', 'SYSTEM', 'CTXSYS', 'DBSNMP', 'LOGSTDBY_ADMINISTRATOR', 'ORDSYS', 'ORDPLUGINS', 'OEM_MONITOR', 'WKSYS', 'WKPROXY', 'WK_TEST', 'WKUSER', 'MDSYS', 'LBACSYS', 'DMSYS', 'WMSYS', 'OLAPDBA', 'OLAPSVR', 'OLAP_USER', 'OLAPSYS', 'EXFSYS', 'SYSMAN', 'MDDATA', 'SI_INFORMTN_SCHEMA', 'XDB', 'ODM','ORACLE_OCM','DIP', 'TSMSYS','TRACESVR','PERFSTAT')
GRANT SELECT ON "RISK"."EXTRNL_REFERENCES_AUDIT" TO "RISK_PURGE"; GRANT UPDATE ON "RISK"."EXTRNL_REFERENCES_AUDIT" TO "RISK_PURGE"; … GRANT SELECT ON "RISK"."EXTRNL_REFERENCES_AUDIT" TO "RISK_CONSULT"; … GRANT EXECUTE ON "RISK"."INSERTDIVIDENDS2" TO "RISK_CONSULT"; …
Comme pour les synonymes privés, la génération des droits à partir de
l’export DDL est difficile. Les objets dans les commandes GRANT <priv> TO
lors d’un import ne sont pas préfixés par le nom du schéma.
CONNECT RISK
…
GRANT EXECUTE ON "NUM_TO_DATE" TO "RISK_CONSULT"
rvaccountsprivs.ksh
#!/bin/ksh
# SPA
# 08/2009
# Outil de reverse pour les exports/imports
# Reverse des comptes, roles et privileges
# Reverse des synonymes
INST_SOURCE=$1
if [ ! -f ${DBA}/etc/${INST_SOURCE}.inst ]
then
echo "Instance non existante"
exit 3
fi
# Prise de l’environnement de l’instance
. $DBA/etc/${INST_SOURCE}.inst
revprofiles="rev_profiles_${INST_SOURCE}.sql"
revusers="rev_users_${INST_SOURCE}.sql"
revroles="rev_roles_${INST_SOURCE}.sql"
revroleprivs="rev_roleprivs_${INST_SOURCE}.sql"
revpubsyns="rev_pubsyns_${INST_SOURCE}.sql"
revprivsyns="rev_privsyns_${INST_SOURCE}.sql"
revgrants="rev_grants_${INST_SOURCE}.sql"
# Definition d’une liste des comptes systemes Oracle
SYSORAACCOUNTS="'SYS', 'OUTLN', 'SYSTEM', 'CTXSYS', 'DBSNMP', 'LOGSTDBY_ADMINISTRATOR', 'ORDSYS', 'ORDPLUGINS', 'OEM_MONITOR', 'WKSYS', 'WKPROXY', 'WK_TEST', 'WKUSER', 'MDSYS', 'LBACSYS', 'DMSYS', 'WMSYS', 'OLAPDBA', 'OLAPSVR', 'OLAP_USER', 'OLAPSYS', 'EXFSYS', 'SYSMAN', 'MDDATA', 'SI_INFORMTN_SCHEMA', 'XDB', 'ODM','ORACLE_OCM','DIP', 'TSMSYS','TRACESVR','PERFSTAT'"
# 1- Reverse des profils
sqlplus -s "/ as sysdba" EOF > ${SCRIPT}/${revprofiles}
set heading off;
set feedback off;
set pagesize 1000;
set linesize 800;
select distinct 'CREATE PROFILE "'||profile||'" LIMIT CPU_PER_SESSION DEFAULT;'
from dba_profiles
where profile != 'DEFAULT';
select 'ALTER PROFILE "'||profile||'" LIMIT '||RESOURCE_NAME||' '||LIMIT||';'
from dba_profiles
where profile !='DEFAULT'
order by profile, resource_name;
EOF
# 2- Reverse des comptes
sqlplus -s "/ as sysdba" EOF > ${SCRIPT}/${revusers}
set heading off;
set feedback off;
set pagesize 1000;
set linesize 800;
select 'CREATE USER "'||username||'" IDENTIFIED BY VALUES '''||password||''' DEFAULT TABLESPACE "'||default_tablespace||'" TEMPORARY TABLESPACE
"'||temporary_tablespace||'" ' ||decode(profile,'DEFAULT','','PROFILE "'||profile||'"')
||' ;' from dba_users
where username not in (${SYSORAACCOUNTS});
EOF
# 3- Reverse des roles applicatifs
sqlplus -s "/ as sysdba" EOF > ${SCRIPT}/${revroles}
set heading off;
set feedback off;
set pagesize 1000;
set linesize 800;
select 'CREATE ROLE "'||role||'";' from dba_roles
where role not in (select distinct role from role_sys_privs)
and role not in (select distinct role from role_tab_privs)
and role != 'GLOBAL_AQ_USER_ROLE';
EOF
# 4- Reverse des privileges des roles
sqlplus -s "/ as sysdba" EOF > ${SCRIPT}/${revroleprivs}
set heading off;
set feedback off;
set pagesize 1000;
set linesize 800;
select 'GRANT "'||granted_role||'" TO "'||grantee||'";' from dba_role_privs
where grantee not in (select distinct role from role_sys_privs)
and grantee not in (select distinct role from role_tab_privs)
and grantee not in (${SYSORAACCOUNTS});
EOF
# 5- Reverse des synonymes publics
sqlplus -s "/ as sysdba" EOF > ${SCRIPT}/${revpubsyns}
set heading off;
set feedback off;
set pagesize 50000;
set linesize 800;
select 'CREATE PUBLIC SYNONYM "'||synonym_name||'" for "'||table_owner||'"."'||table_name||'";'
from dba_synonyms
where owner = 'PUBLIC'
and table_owner not in (${SYSORAACCOUNTS});
EOF
# 6- Reverse des synonymes prives
sqlplus -s "/ as sysdba" EOF > ${SCRIPT}/${revprivsyns}
set heading off;
set feedback off;
set pagesize 50000;
set linesize 800;
select 'CREATE SYNONYM "'||owner||'"."'||synonym_name||'" for "'||table_owner||'"."'||table_name||'";'
from dba_synonyms
where owner not in (${SYSORAACCOUNTS})
and table_owner not in (${SYSORAACCOUNTS})
and owner !='PUBLIC';
EOF
# 7- Reverse des droits
sqlplus -s "/ as sysdba" EOF > ${SCRIPT}/${revgrants}
set heading off;
set feedback off;
set pagesize 50000;
set linesize 800;
select 'GRANT '||privilege||' ON "'||owner||'"."'||table_name||'" TO "'||grantee||'"'||decode(grantable, 'YES', ' WITH GRANT OPTION')||';'
from dba_tab_privs
where owner not in (${SYSORAACCOUNTS});
EOF