将业务从Oracle数据库迁移至PolarDB PostgreSQL版(兼容Oracle)时,需要完整地复制原有的安全体系。本文介绍两种迁移方式:推荐使用用户与权限迁移工具(polardb-schema-migration)自动完成迁。若运行环境无法同时连接Oracle和PolarDB PostgreSQL版(兼容Oracle),可使用SQL脚本方式手动迁移。
方案选择
本文提供以下两种迁移方案,请根据您的运行环境条件选择。
对比项 | 运行环境要求 | 操作效率 | 适用场景 |
方案一:(推荐)使用迁移工具 | 需要一台可以同时连接Oracle和PolarDB PostgreSQL版(兼容Oracle)的机器(例如ECS实例),并安装Java 1.8。 | 高。命令行一键执行,自动完成全部迁移动作。 | 绝大多数场景。推荐优先选用。 |
方案二:使用SQL脚本 | 无需网络打通。仅需在Oracle端通过SQLPlus客户端导出SQL,再将SQL文件传输到可连接PolarDB PostgreSQL版(兼容Oracle)的机器上,通过psql客户端导入即可。 | 中。需手动执行多个SQL脚本,流程相对繁琐。 | 环境因网络隔离等原因,无法提供同时连接Oracle和PolarDB PostgreSQL版(兼容Oracle)的机器。 |
方案一:(推荐)使用迁移工具
注意事项
Oracle源库要求:连接到Oracle的账号需要至少拥有
CONNECT、CREATE SESSION、SELECT_CATALOG_ROLE权限,授权语句如下:GRANT CONNECT, CREATE SESSION, SELECT_CATALOG_ROLE TO myuser;PolarDB目标库要求:
需要先使用数据传输服务(DTS)完成Oracle到PolarDB的结构迁移。
连接到PolarDB的账号需要是高权限账号。
工具运行环境要求:
迁移工具需要部署在一个能够同时连接Oracle和PolarDB的位置,例如一台ECS实例。
运行环境需要安装Java运行环境,版本为Java 1.8。
工具部署
下载迁移工具JAR文件polardb-schema-migration-20260827.jar,将其上传到能够同时连接Oracle和PolarDB的机器上。
运行如下命令确认工具版本:
java -jar polardb-schema-migration.jar --version新建一个
config.json文件,写入如下内容。其中数据库连接串和日志目录需要根据实际情况调整。后续迁移过程需要使用该配置文件。{ "source": { "url": "jdbc:oracle:thin:@//<Oracle主机IP>:<端口>/<服务名>", "username": "<Oracle用户名>", "password": "<Oracle密码>" }, "destination": { "url": "jdbc:polardb://<PolarDB集群连接地址>:<端口>/<数据库名>", "username": "<PolarDB用户名>", "password": "<PolarDB密码>" }, "log": { "dir": "/path/to/log" } }
迁移步骤
创建内置对象
Oracle中存在上百个内置角色以及若干个内置用户,这些内置对象自带一些权限,例如DBA角色可读写所有表,SELECT_CATALOG_ROLE角色可查看系统目录。业务可按需将这些内置角色授予自建的角色和用户。
执行如下命令,在PolarDB中创建Oracle的内置角色和内置用户。该步骤通常是必需的,因为后续权限迁移阶段可能需要将这些内置角色授予给用户。
# JAR文件和config文件的路径需要根据实际情况修改,下同
java -jar polardb-schema-migration.jar --config=/path/to/config.json --create-polar-builtin-objects创建非内置角色
执行如下命令,从Oracle导出非内置角色(ROLE)定义并创建到PolarDB中。
java -jar polardb-schema-migration.jar --config=/path/to/config.json --create-roles创建非内置用户
执行如下命令,从Oracle导出用户(USER)定义并创建到PolarDB中。
java -jar polardb-schema-migration.jar --config=/path/to/config.json --create-users由于源库存储的密码是加密的,暂时不支持迁移密码信息。所有用户迁移到PolarDB后都没有密码,需要手动修改密码:
ALTER USER myuser PASSWORD '<新密码>';授予对象权限
执行如下命令,从Oracle导出对象权限(OBJECT PRIVILEGE)的授予记录,并在PolarDB中执行。主要是将表的增删改查权限、函数的执行权限等授予给角色和用户。
java -jar polardb-schema-migration.jar --config=/path/to/config.json --grant-object-privileges授予角色
执行如下命令,从Oracle导出角色权限的授予记录,并在PolarDB中执行。其原理是将角色授予给用户,用户就拥有了该角色的权限。
java -jar polardb-schema-migration.jar --config=/path/to/config.json --grant-rolesSCHEMA权限授予
执行如下命令,将SCHEMA的USAGE权限开放给普通用户,与Oracle对齐。Oracle中不检查该项权限,相当于默认授予了该项权限。
java -jar polardb-schema-migration.jar --config=/path/to/config.json --grant-schema-usage修改对象所有者
Oracle的SCHEMA和USER/OWNER是相同的概念,而在PolarDB中二者是独立的。执行如下命令,将对象的OWNER改为其所属SCHEMA的同名用户,在PolarDB中也实现SCHEMA与OWNER的统一。
java -jar polardb-schema-migration.jar --config=/path/to/config.json --change-object-owner(可选)授予系统权限
系统权限属于Oracle的高阶用法,主要控制DDL的执行权限。在PolarDB中建议使用高权限账号进行DDL运维操作,因此通常不需要迁移系统权限。
执行此命令前,需要先在PolarDB控制台的集群参数配置中开启polar_enable_system_privilege参数。
java -jar polardb-schema-migration.jar --config=/path/to/config.json --grant-system-privileges(可选)设置默认角色
默认角色属于Oracle的高阶用法,控制用户登录时默认拥有的角色权限。PolarDB的用户登录时默认加载所拥有的所有角色的权限,与Oracle默认值一致,因此通常不需要执行该迁移操作。
执行此命令前,需要先在PolarDB控制台的集群参数配置中开启polar_enable_default_role参数。
java -jar polardb-schema-migration.jar --config=/path/to/config.json --set-default-roles(可选)创建PROFILE
PROFILE属于Oracle的高阶用法,用于控制密码复杂度校验、过期时间等。PolarDB的用户和密码通常在控制台设置,因此通常不需要迁移PROFILE。
java -jar polardb-schema-migration.jar --config=/path/to/config.json --create-profiles方案二:使用SQL脚本
本方案先在Oracle端通过SQLPlus客户端执行PL/SQL程序,将Oracle中的角色、用户、权限信息导出到SQL脚本中;然后通过psql等客户端在PolarDB中执行该SQL脚本。本方案不要求Oracle与PolarDB之间网络打通。
注意事项
Oracle源库要求:需在Oracle中准备一个迁移账号,该账号至少拥有连接权限和查询系统表的权限。授权语句如下:
CREATE USER <Oracle用户名> IDENTIFIED BY <Oracle密码>; GRANT CONNECT TO <Oracle用户名>; GRANT CREATE SESSION TO <Oracle用户名>; GRANT SELECT_CATALOG_ROLE TO <Oracle用户名>;PolarDB目标库要求:需在PolarDB中准备一个高权限账号,在PolarDB控制台创建即可。
密码迁移限制:由于Oracle中存储的密码是加密的,本方案不支持迁移密码信息,所有用户迁移到PolarDB后均无密码,需要手动重新设置。
Oracle版本说明:本文档中的脚本针对Oracle 12及以上版本和Oracle 11版本分别提供。
Oracle 12及以上版本可以使用系统视图中的
ORACLE_MAINTAINED='N'列高效过滤Oracle系统用户和角色。Oracle 11版本因系统视图缺少该列,需要通过显式列出常见系统对象名的方式过滤,实际环境中如有遗漏需根据实际情况动态调整过滤条件。
迁移步骤
创建内置对象
Oracle中存在上百个内置角色以及若干个内置用户,这些内置对象自带一些权限,例如DBA角色可读写所有表,SELECT_CATALOG_ROLE角色可查看系统目录。业务可按需将这些内置角色授予自建的角色和用户。
在PolarDB侧通过psql客户端(需使用高权限账号)执行如下SQL,创建Oracle的内置角色和内置用户。该步骤通常是必需的,因为后续权限迁移阶段可能需要将这些内置角色授予给用户。
-- 创建Oracle内置角色扩展(提供DBA、CONNECT、RESOURCE等内置角色)
CREATE EXTENSION IF NOT EXISTS polar_builtin_role;
-- 创建PUBLIC用户,可以作为PUBLIC SYNONYM等对象的owner
CREATE USER "PUBLIC";
-- 创建system用户
CREATE USER system;
-- 创建system模式
CREATE SCHEMA IF NOT EXISTS system;
-- 修改system模式的owner
ALTER SCHEMA system OWNER TO system;创建非内置角色
执行如下PL/SQL脚本,从Oracle导出非内置角色(ROLE)的CREATE ROLE语句到create_role.sql文件(可通过SPOOL路径指定输出位置),将该文件传输到PolarDB侧通过psql客户端执行。
Oracle 12及以上版本
SET HEADING OFF
SET TRIMSPOOL ON
SET TRIMOUT ON
SET PAGESIZE 0
SET LINESIZE 2000
SET SERVEROUTPUT ON
-- 执行结果实际存储位置,需要根据实际情况修改
SPOOL /my/path/to/create_role.sql
BEGIN
FOR rec IN (
SELECT 'CREATE ROLE ' || ROLE || ';' AS ddl_script
FROM DBA_ROLES
WHERE ORACLE_MAINTAINED = 'N'
ORDER BY ROLE
) LOOP
DBMS_OUTPUT.PUT_LINE(rec.ddl_script);
END LOOP;
END;
/
SPOOL OFF
EXITOracle 11版本
SET HEADING OFF
SET TRIMSPOOL ON
SET TRIMOUT ON
SET PAGESIZE 0
SET LINESIZE 2000
SET SERVEROUTPUT ON
SPOOL /my/path/to/create_role.sql
BEGIN
FOR rec IN (
SELECT 'CREATE ROLE ' || ROLE || ';' AS ddl_script
FROM DBA_ROLES
WHERE ROLE NOT IN (
'CONNECT', 'RESOURCE', 'DBA', 'SELECT_CATALOG_ROLE',
'EXECUTE_CATALOG_ROLE', 'DELETE_CATALOG_ROLE', 'EXP_FULL_DATABASE',
'IMP_FULL_DATABASE', 'LOGSTDBY_ADMINISTRATOR', 'RECOVERY_CATALOG_OWNER',
'SCHEDULER_ADMIN', 'HS_ADMIN_SELECT_ROLE', 'HS_ADMIN_EXECUTE_ROLE',
'HS_ADMIN_ROLE', 'GLOBAL_AQ_USER_ROLE', 'OEM_ADVISOR', 'OEM_MONITOR',
'MGMT_USER', 'DATAPUMP_EXP_FULL_DATABASE', 'DATAPUMP_IMP_FULL_DATABASE',
'ADM_PARALLEL_EXECUTE_TASK', 'GATHER_SYSTEM_STATISTICS',
'OPTIMIZER_PROCESSING_RATE', 'ACCESS_CATALOG_ROLE', 'JAVAUSERPRIV',
'JAVAIDPRIV', 'JAVASYSPRIV', 'JAVADEBUGPRIV', 'EJBCLIENT', 'JMXSERVER',
'JAVA_ADMIN', 'JAVA_DEPLOY', 'AUTHENTICATEDUSER', 'XDB_SET_INVOKER',
'XDB_WEBSERVICES', 'XDB_WEBSERVICES_WITH_PUBLIC', 'XDB_WEBSERVICES_OVER_HTTP',
'XDBADMIN', 'XDB_WEBSERVICES_OVER_HTTPS', 'CTXAPP', 'ORDADMIN',
'WM_ADMIN_ROLE', 'OLAP_USER', 'OLAP_DBA', 'OLAP_XS_ADMIN',
'CSW_USR_ROLE', 'WFS_USR_ROLE', 'SPATIAL_CSW_ADMIN',
'SPATIAL_WFS_ADMIN', 'LBAC_DBA', 'APEX_ADMINISTRATOR_ROLE',
'FLOWS_FILES_ROLE', 'AQ_ADMINISTRATOR_ROLE', 'AQ_USER_ROLE',
'CWM_USER', 'DBFS_ROLE', 'OLAPI_TRACE_USER', 'OWB$CLIENT',
'OWB_DESIGNCENTER_VIEW', 'OWB_USER'
)
AND ROLE NOT LIKE 'APEX\_%' ESCAPE '\'
AND ROLE NOT LIKE 'FLOWS\_%' ESCAPE '\'
AND ROLE NOT LIKE 'ORA\_%' ESCAPE '\'
AND ROLE NOT LIKE 'ORACLE\_%' ESCAPE '\'
AND ROLE NOT LIKE 'SYS\_%' ESCAPE '\'
ORDER BY ROLE
) LOOP
DBMS_OUTPUT.PUT_LINE(rec.ddl_script);
END LOOP;
END;
/
SPOOL OFF
EXIT创建非内置用户
执行如下PL/SQL脚本,从Oracle导出非内置用户(USER)的CREATE USER语句,将SQL文件传输到PolarDB侧通过psql客户端执行。
Oracle 12及以上版本
SET HEADING OFF
SET TRIMSPOOL ON
SET TRIMOUT ON
SET PAGESIZE 0
SET LINESIZE 2000
SET SERVEROUTPUT ON
SPOOL /my/path/to/create_user.sql
BEGIN
FOR rec IN (
SELECT 'CREATE USER ' || USERNAME || ';' AS ddl_script
FROM DBA_USERS
WHERE ORACLE_MAINTAINED = 'N'
ORDER BY USERNAME
) LOOP
DBMS_OUTPUT.PUT_LINE(rec.ddl_script);
END LOOP;
END;
/
SPOOL OFF
EXITOracle 11版本
SET HEADING OFF
SET TRIMSPOOL ON
SET TRIMOUT ON
SET PAGESIZE 0
SET LINESIZE 2000
SET SERVEROUTPUT ON
SPOOL /my/path/to/create_user.sql
BEGIN
FOR rec IN (
SELECT 'CREATE USER ' || USERNAME || ';' AS ddl_script
FROM DBA_USERS
WHERE USERNAME NOT IN (
'SYS', 'SYSTEM', 'DBSNMP', 'SYSMAN', 'OUTLN',
'CTXSYS', 'MDSYS', 'ORDSYS', 'ORDDATA', 'ORDPLUGINS', 'SI_INFORMTN_SCHEMA',
'WMSYS', 'EXFSYS', 'LBACSYS', 'XDB', 'ANONYMOUS', 'XS$NULL',
'OLAPSYS', 'MDDATA', 'OWBSYS', 'OWBSYS_AUDIT',
'APEX_030200', 'APEX_040000', 'APEX_040100', 'APEX_040200', 'APEX_050000',
'APEX_PUBLIC_USER', 'FLOWS_FILES', 'FLOWS_030000', 'FLOWS_040000',
'SPATIAL_CSW_ADMIN_USR', 'SPATIAL_WFS_ADMIN_USR',
'DIP', 'ORACLE_OCM', 'APPQOSSYS', 'MGMT_VIEW', 'SCOTT', 'HR',
'TSMSYS', 'WKPROXY', 'WK_TEST', 'PERFSTAT', 'TRACESVR',
'GSMADMIN_INTERNAL', 'GSMCATUSER', 'GSMUSER', 'SYSBACKUP', 'SYSDG', 'SYSKM'
)
AND USERNAME NOT LIKE 'APEX\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'FLOWS\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'ORA\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'ORACLE\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'SYS\_%' ESCAPE '\'
ORDER BY USERNAME
) LOOP
DBMS_OUTPUT.PUT_LINE(rec.ddl_script);
END LOOP;
END;
/
SPOOL OFF
EXIT由于Oracle中存储的密码是加密的,导出的CREATE USER语句中不包含密码,所有用户迁移到PolarDB后均无密码,需要手动为每个用户设置密码:
ALTER USER <用户名> WITH PASSWORD '<新密码>';授予对象权限
执行如下PL/SQL脚本,从Oracle导出对象权限(OBJECT PRIVILEGE)的GRANT语句到grant_object.sql文件,将文件传输到PolarDB侧通过psql客户端执行,用于将表的增删改查权限、函数的执行权限等授予给角色和用户。
导出脚本使用LISTAGG将同一对象上对同一GRANTEE的多个权限合并为一条GRANT语句(例如SELECT、INSERT、UPDATE合并为GRANT SELECT, INSERT, UPDATE ON ...),并按GRANTABLE(YES/NO)分组,因为WITH GRANT OPTION只能整条生效。
当某个对象上权限非常多时,LISTAGG结果可能超过4000字节导致ORA-01489错误。此时可以去掉LISTAGG,改为每个PRIVILEGE单独一条GRANT语句。
Oracle 12及以上版本
SET HEADING OFF
SET TRIMSPOOL ON
SET TRIMOUT ON
SET PAGESIZE 0
SET LINESIZE 2000
SET DEFINE OFF
SET SERVEROUTPUT ON
SPOOL /my/path/to/grant_object.sql
BEGIN
FOR rec IN (
SELECT
'GRANT ' || LISTAGG(p.PRIVILEGE, ', ') WITHIN GROUP (ORDER BY p.PRIVILEGE) || ' ON ' ||
CASE WHEN MIN(o.OBJECT_TYPE) IN ('PROCEDURE', 'FUNCTION', 'TYPE') THEN MIN(o.OBJECT_TYPE) || ' '
ELSE '' END ||
'"' || p.OWNER || '"."' || p.TABLE_NAME || '" TO ' || p.GRANTEE ||
CASE WHEN p.GRANTABLE = 'YES' THEN ' WITH GRANT OPTION' ELSE '' END ||
';' AS ddl_script
FROM DBA_TAB_PRIVS p
JOIN DBA_OBJECTS o ON p.TABLE_NAME = o.OBJECT_NAME AND o.OWNER = p.OWNER
WHERE p.OWNER IN (SELECT USERNAME FROM DBA_USERS WHERE ORACLE_MAINTAINED = 'N')
AND p.TABLE_NAME NOT LIKE 'ISEQ$$%'
AND p.TABLE_NAME NOT LIKE 'AQ$%'
AND o.OBJECT_TYPE NOT IN ('QUEUE', 'PACKAGE BODY', 'TYPE BODY')
AND p.GRANTEE IN (
SELECT USERNAME FROM DBA_USERS WHERE ORACLE_MAINTAINED = 'N'
UNION
SELECT ROLE FROM DBA_ROLES
UNION
SELECT 'PUBLIC' FROM DUAL
)
GROUP BY p.GRANTEE, p.OWNER, p.TABLE_NAME, p.GRANTABLE
ORDER BY p.GRANTEE, p.OWNER, p.TABLE_NAME, p.GRANTABLE
) LOOP
DBMS_OUTPUT.PUT_LINE(rec.ddl_script);
END LOOP;
END;
/
SPOOL OFF
EXITOracle 11版本
SET HEADING OFF
SET TRIMSPOOL ON
SET TRIMOUT ON
SET PAGESIZE 0
SET LINESIZE 2000
SET DEFINE OFF
SET SERVEROUTPUT ON
SPOOL /my/path/to/grant_object.sql
BEGIN
FOR rec IN (
SELECT
'GRANT ' || LISTAGG(p.PRIVILEGE, ', ') WITHIN GROUP (ORDER BY p.PRIVILEGE) || ' ON ' ||
CASE WHEN MIN(o.OBJECT_TYPE) IN ('PROCEDURE', 'FUNCTION', 'TYPE') THEN MIN(o.OBJECT_TYPE) || ' '
ELSE '' END ||
'"' || p.OWNER || '"."' || p.TABLE_NAME || '" TO ' || p.GRANTEE ||
CASE WHEN p.GRANTABLE = 'YES' THEN ' WITH GRANT OPTION' ELSE '' END ||
';' AS ddl_script
FROM DBA_TAB_PRIVS p
JOIN DBA_OBJECTS o ON p.TABLE_NAME = o.OBJECT_NAME AND o.OWNER = p.OWNER
WHERE p.OWNER IN (
SELECT USERNAME FROM DBA_USERS WHERE USERNAME NOT IN (
'SYS', 'SYSTEM', 'DBSNMP', 'SYSMAN', 'OUTLN',
'CTXSYS', 'MDSYS', 'ORDSYS', 'ORDDATA', 'ORDPLUGINS', 'SI_INFORMTN_SCHEMA',
'WMSYS', 'EXFSYS', 'LBACSYS', 'XDB', 'ANONYMOUS', 'XS$NULL',
'OLAPSYS', 'MDDATA', 'OWBSYS', 'OWBSYS_AUDIT',
'APEX_030200', 'APEX_040000', 'APEX_040100', 'APEX_040200', 'APEX_050000',
'APEX_PUBLIC_USER', 'FLOWS_FILES', 'FLOWS_030000', 'FLOWS_040000',
'SPATIAL_CSW_ADMIN_USR', 'SPATIAL_WFS_ADMIN_USR',
'DIP', 'ORACLE_OCM', 'APPQOSSYS', 'MGMT_VIEW', 'SCOTT', 'HR',
'TSMSYS', 'WKPROXY', 'WK_TEST', 'PERFSTAT', 'TRACESVR',
'GSMADMIN_INTERNAL', 'GSMCATUSER', 'GSMUSER', 'SYSBACKUP', 'SYSDG', 'SYSKM'
)
AND USERNAME NOT LIKE 'APEX\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'FLOWS\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'ORA\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'ORACLE\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'SYS\_%' ESCAPE '\'
)
AND p.TABLE_NAME NOT LIKE 'ISEQ$$%'
AND p.TABLE_NAME NOT LIKE 'AQ$%'
AND o.OBJECT_TYPE NOT IN ('QUEUE', 'PACKAGE BODY', 'TYPE BODY')
AND p.GRANTEE IN (
SELECT USERNAME FROM DBA_USERS WHERE USERNAME NOT IN (
'SYS', 'SYSTEM', 'DBSNMP', 'SYSMAN', 'OUTLN',
'CTXSYS', 'MDSYS', 'ORDSYS', 'ORDDATA', 'ORDPLUGINS', 'SI_INFORMTN_SCHEMA',
'WMSYS', 'EXFSYS', 'LBACSYS', 'XDB', 'ANONYMOUS', 'XS$NULL',
'OLAPSYS', 'MDDATA', 'OWBSYS', 'OWBSYS_AUDIT',
'APEX_030200', 'APEX_040000', 'APEX_040100', 'APEX_040200', 'APEX_050000',
'APEX_PUBLIC_USER', 'FLOWS_FILES', 'FLOWS_030000', 'FLOWS_040000',
'SPATIAL_CSW_ADMIN_USR', 'SPATIAL_WFS_ADMIN_USR',
'DIP', 'ORACLE_OCM', 'APPQOSSYS', 'MGMT_VIEW', 'SCOTT', 'HR',
'TSMSYS', 'WKPROXY', 'WK_TEST', 'PERFSTAT', 'TRACESVR',
'GSMADMIN_INTERNAL', 'GSMCATUSER', 'GSMUSER', 'SYSBACKUP', 'SYSDG', 'SYSKM'
)
AND USERNAME NOT LIKE 'APEX\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'FLOWS\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'ORA\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'ORACLE\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'SYS\_%' ESCAPE '\'
UNION
SELECT ROLE FROM DBA_ROLES
UNION
SELECT 'PUBLIC' FROM DUAL
)
GROUP BY p.GRANTEE, p.OWNER, p.TABLE_NAME, p.GRANTABLE
ORDER BY p.GRANTEE, p.OWNER, p.TABLE_NAME, p.GRANTABLE
) LOOP
DBMS_OUTPUT.PUT_LINE(rec.ddl_script);
END LOOP;
END;
/
SPOOL OFF
EXIT授予角色
执行如下PL/SQL脚本,从Oracle导出角色授予关系的GRANT语句,将SQL文件传输到PolarDB侧通过psql客户端执行。将角色授予给用户后,用户即拥有该角色的权限。
Oracle 12及以上版本
SET HEADING OFF
SET TRIMSPOOL ON
SET TRIMOUT ON
SET PAGESIZE 0
SET LINESIZE 2000
SET SERVEROUTPUT ON
SPOOL /my/path/to/grant_role.sql
BEGIN
FOR rec IN (
WITH NON_ORACLE_USERS_AND_ROLES AS (
SELECT USERNAME AS NAME FROM DBA_USERS WHERE ORACLE_MAINTAINED = 'N'
UNION
SELECT ROLE AS NAME FROM DBA_ROLES WHERE ORACLE_MAINTAINED = 'N'
),
FILTERED_ROLE_PRIVS AS (
SELECT GRANTEE, GRANTED_ROLE, ADMIN_OPTION
FROM DBA_ROLE_PRIVS
WHERE GRANTEE IN (SELECT NAME FROM NON_ORACLE_USERS_AND_ROLES)
)
SELECT
CASE
WHEN ADMIN_OPTION = 'YES' THEN
'GRANT ' || GRANTED_ROLE || ' TO ' || GRANTEE || ' WITH ADMIN OPTION;'
ELSE
'GRANT ' || GRANTED_ROLE || ' TO ' || GRANTEE || ';'
END AS ddl_script
FROM FILTERED_ROLE_PRIVS
ORDER BY GRANTEE, GRANTED_ROLE
) LOOP
DBMS_OUTPUT.PUT_LINE(rec.ddl_script);
END LOOP;
END;
/
SPOOL OFF
EXITOracle 11版本
SET HEADING OFF
SET TRIMSPOOL ON
SET TRIMOUT ON
SET PAGESIZE 0
SET LINESIZE 2000
SET SERVEROUTPUT ON
SPOOL /my/path/to/grant_role.sql
BEGIN
FOR rec IN (
WITH NON_ORACLE_USERS_AND_ROLES AS (
SELECT USERNAME AS NAME FROM DBA_USERS WHERE USERNAME NOT IN (
'SYS', 'SYSTEM', 'DBSNMP', 'SYSMAN', 'OUTLN',
'CTXSYS', 'MDSYS', 'ORDSYS', 'ORDDATA', 'ORDPLUGINS', 'SI_INFORMTN_SCHEMA',
'WMSYS', 'EXFSYS', 'LBACSYS', 'XDB', 'ANONYMOUS', 'XS$NULL',
'OLAPSYS', 'MDDATA', 'OWBSYS', 'OWBSYS_AUDIT',
'APEX_030200', 'APEX_040000', 'APEX_040100', 'APEX_040200', 'APEX_050000',
'APEX_PUBLIC_USER', 'FLOWS_FILES', 'FLOWS_030000', 'FLOWS_040000',
'SPATIAL_CSW_ADMIN_USR', 'SPATIAL_WFS_ADMIN_USR',
'DIP', 'ORACLE_OCM', 'APPQOSSYS', 'MGMT_VIEW', 'SCOTT', 'HR',
'TSMSYS', 'WKPROXY', 'WK_TEST', 'PERFSTAT', 'TRACESVR',
'GSMADMIN_INTERNAL', 'GSMCATUSER', 'GSMUSER', 'SYSBACKUP', 'SYSDG', 'SYSKM'
)
AND USERNAME NOT LIKE 'APEX\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'FLOWS\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'ORA\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'ORACLE\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'SYS\_%' ESCAPE '\'
UNION
SELECT ROLE AS NAME FROM DBA_ROLES WHERE ROLE NOT IN (
'CONNECT', 'RESOURCE', 'DBA', 'SELECT_CATALOG_ROLE',
'EXECUTE_CATALOG_ROLE', 'DELETE_CATALOG_ROLE', 'EXP_FULL_DATABASE',
'IMP_FULL_DATABASE', 'LOGSTDBY_ADMINISTRATOR', 'RECOVERY_CATALOG_OWNER',
'SCHEDULER_ADMIN', 'HS_ADMIN_SELECT_ROLE', 'HS_ADMIN_EXECUTE_ROLE',
'HS_ADMIN_ROLE', 'GLOBAL_AQ_USER_ROLE', 'OEM_ADVISOR', 'OEM_MONITOR',
'MGMT_USER', 'DATAPUMP_EXP_FULL_DATABASE', 'DATAPUMP_IMP_FULL_DATABASE',
'ADM_PARALLEL_EXECUTE_TASK', 'GATHER_SYSTEM_STATISTICS',
'OPTIMIZER_PROCESSING_RATE', 'ACCESS_CATALOG_ROLE', 'JAVAUSERPRIV',
'JAVAIDPRIV', 'JAVASYSPRIV', 'JAVADEBUGPRIV', 'EJBCLIENT', 'JMXSERVER',
'JAVA_ADMIN', 'JAVA_DEPLOY', 'AUTHENTICATEDUSER', 'XDB_SET_INVOKER',
'XDB_WEBSERVICES', 'XDB_WEBSERVICES_WITH_PUBLIC', 'XDB_WEBSERVICES_OVER_HTTP',
'XDBADMIN', 'XDB_WEBSERVICES_OVER_HTTPS', 'CTXAPP', 'ORDADMIN',
'WM_ADMIN_ROLE', 'OLAP_USER', 'OLAP_DBA', 'OLAP_XS_ADMIN',
'CSW_USR_ROLE', 'WFS_USR_ROLE', 'SPATIAL_CSW_ADMIN',
'SPATIAL_WFS_ADMIN', 'LBAC_DBA', 'APEX_ADMINISTRATOR_ROLE',
'FLOWS_FILES_ROLE', 'AQ_ADMINISTRATOR_ROLE', 'AQ_USER_ROLE',
'CWM_USER', 'DBFS_ROLE', 'OLAPI_TRACE_USER', 'OWB$CLIENT',
'OWB_DESIGNCENTER_VIEW', 'OWB_USER'
)
AND ROLE NOT LIKE 'APEX\_%' ESCAPE '\'
AND ROLE NOT LIKE 'FLOWS\_%' ESCAPE '\'
AND ROLE NOT LIKE 'ORA\_%' ESCAPE '\'
AND ROLE NOT LIKE 'ORACLE\_%' ESCAPE '\'
AND ROLE NOT LIKE 'SYS\_%' ESCAPE '\'
),
FILTERED_ROLE_PRIVS AS (
SELECT GRANTEE, GRANTED_ROLE, ADMIN_OPTION
FROM DBA_ROLE_PRIVS
WHERE GRANTEE IN (SELECT NAME FROM NON_ORACLE_USERS_AND_ROLES)
)
SELECT
CASE
WHEN ADMIN_OPTION = 'YES' THEN
'GRANT ' || GRANTED_ROLE || ' TO ' || GRANTEE || ' WITH ADMIN OPTION;'
ELSE
'GRANT ' || GRANTED_ROLE || ' TO ' || GRANTEE || ';'
END AS ddl_script
FROM FILTERED_ROLE_PRIVS
ORDER BY GRANTEE, GRANTED_ROLE
) LOOP
DBMS_OUTPUT.PUT_LINE(rec.ddl_script);
END LOOP;
END;
/
SPOOL OFF
EXITSCHEMA权限授予
在Oracle中,访问某个对象时,只需要校验当前用户是否拥有该对象的权限。在PolarDB中,则需要先校验当前用户是否拥有该对象所属的模式(SCHEMA)的权限,然后再校验是否拥有该对象的权限。为了与Oracle保持统一,在PolarDB中执行如下PL/pgSQL脚本,将所有用户SCHEMA的USAGE权限开放给所有用户:
DO $$
DECLARE
schema_rec RECORD;
sql_stmt TEXT;
BEGIN
FOR schema_rec IN
SELECT nspname FROM pg_namespace n
WHERE nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast',
'polar_catalog', 'sys', 'cron', 'wmsys', 'polar_feature_utils',
'pg_bitmapindex', 'polar_dbms_stats', 'public')
AND nspname NOT LIKE 'pg_temp_%' AND nspname NOT LIKE 'pg_toast_temp_%'
AND nsppkgns = 0
ORDER BY nspname
LOOP
sql_stmt := format('GRANT USAGE ON SCHEMA %I TO PUBLIC', schema_rec.nspname);
RAISE NOTICE '%', sql_stmt;
EXECUTE sql_stmt;
END LOOP;
END $$;修改对象所有者
在Oracle中,用户(USER)和模式(SCHEMA)是等价的,每个用户都默认拥有其同名模式中所有对象的权限。在PolarDB中,用户和模式则是相互独立的概念,用户不一定拥有其同名模式的权限。为了与Oracle保持统一,在PolarDB中通过psql客户端执行如下PL/pgSQL脚本(需使用高权限账号),将SCHEMA及其下所有对象的OWNER改为同名用户:
DO $$
DECLARE
obj_rec RECORD;
sql_stmt TEXT;
processed INTEGER := 0;
BEGIN
-- 1. 修改 SCHEMA 所有者
FOR obj_rec IN
SELECT nspname FROM pg_namespace n
WHERE nspname IN (SELECT rolname FROM pg_roles WHERE rolsuper = false)
AND EXISTS (SELECT 1 FROM information_schema.schemata s WHERE s.schema_name = n.nspname)
AND nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast',
'polar_catalog', 'sys', 'cron', 'wmsys', 'polar_feature_utils',
'pg_bitmapindex', 'polar_dbms_stats', 'public')
AND nspname NOT LIKE 'pg_temp_%' AND nspname NOT LIKE 'pg_toast_temp_%'
AND nsppkgns = 0
AND (SELECT rolname FROM pg_roles WHERE oid = n.nspowner) != n.nspname
ORDER BY nspname
LOOP
sql_stmt := format('ALTER SCHEMA %I OWNER TO %I', obj_rec.nspname, obj_rec.nspname);
RAISE NOTICE '%', sql_stmt;
EXECUTE sql_stmt;
processed := processed + 1;
END LOOP;
-- 2. 修改 TABLE 所有者
FOR obj_rec IN
SELECT schemaname, tablename FROM pg_tables t
WHERE schemaname IN (SELECT rolname FROM pg_roles WHERE rolsuper = false)
AND EXISTS (SELECT 1 FROM information_schema.schemata s WHERE s.schema_name = t.schemaname)
AND schemaname NOT IN ('information_schema', 'pg_catalog', 'pg_toast',
'polar_catalog', 'sys', 'cron', 'wmsys', 'polar_feature_utils',
'pg_bitmapindex', 'polar_dbms_stats', 'public')
AND t.tableowner != t.schemaname
ORDER BY schemaname, tablename
LOOP
sql_stmt := format('ALTER TABLE %I.%I OWNER TO %I', obj_rec.schemaname, obj_rec.tablename, obj_rec.schemaname);
RAISE NOTICE '%', sql_stmt;
EXECUTE sql_stmt;
processed := processed + 1;
END LOOP;
-- 2.1 修改 TABLE PARTITION 所有者
-- ALTER TABLE 父表不会级联修改分区的 owner,需要单独修改每个分区
FOR obj_rec IN
SELECT n.nspname AS schemaname, c.relname AS tablename
FROM pg_class c
JOIN pg_namespace n ON c.relnamespace = n.oid
JOIN pg_inherits i ON c.oid = i.inhrelid
WHERE n.nspname IN (SELECT rolname FROM pg_roles WHERE rolsuper = false)
AND EXISTS (SELECT 1 FROM information_schema.schemata s WHERE s.schema_name = n.nspname)
AND n.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast',
'polar_catalog', 'sys', 'cron', 'wmsys', 'polar_feature_utils',
'pg_bitmapindex', 'polar_dbms_stats', 'public')
AND c.relkind = 'r'
AND c.relispartition = true
AND (SELECT rolname FROM pg_roles WHERE oid = c.relowner) != n.nspname
ORDER BY n.nspname, c.relname
LOOP
sql_stmt := format('ALTER TABLE %I.%I OWNER TO %I', obj_rec.schemaname, obj_rec.tablename, obj_rec.schemaname);
RAISE NOTICE '%', sql_stmt;
EXECUTE sql_stmt;
processed := processed + 1;
END LOOP;
-- 3. 修改 VIEW 所有者
FOR obj_rec IN
SELECT schemaname, viewname FROM pg_views v
WHERE schemaname IN (SELECT rolname FROM pg_roles WHERE rolsuper = false)
AND EXISTS (SELECT 1 FROM information_schema.schemata s WHERE s.schema_name = v.schemaname)
AND schemaname NOT IN ('information_schema', 'pg_catalog', 'pg_toast',
'polar_catalog', 'sys', 'cron', 'wmsys', 'polar_feature_utils',
'pg_bitmapindex', 'polar_dbms_stats', 'public')
AND v.viewowner != v.schemaname
ORDER BY schemaname, viewname
LOOP
sql_stmt := format('ALTER VIEW %I.%I OWNER TO %I', obj_rec.schemaname, obj_rec.viewname, obj_rec.schemaname);
RAISE NOTICE '%', sql_stmt;
EXECUTE sql_stmt;
processed := processed + 1;
END LOOP;
-- 4. 修改 MATERIALIZED VIEW 所有者
FOR obj_rec IN
SELECT schemaname, matviewname FROM pg_matviews mv
WHERE schemaname IN (SELECT rolname FROM pg_roles WHERE rolsuper = false)
AND EXISTS (SELECT 1 FROM information_schema.schemata s WHERE s.schema_name = mv.schemaname)
AND schemaname NOT IN ('information_schema', 'pg_catalog', 'pg_toast',
'polar_catalog', 'sys', 'cron', 'wmsys', 'polar_feature_utils',
'pg_bitmapindex', 'polar_dbms_stats', 'public')
AND mv.matviewowner != mv.schemaname
ORDER BY schemaname, matviewname
LOOP
sql_stmt := format('ALTER MATERIALIZED VIEW %I.%I OWNER TO %I', obj_rec.schemaname, obj_rec.matviewname, obj_rec.schemaname);
RAISE NOTICE '%', sql_stmt;
EXECUTE sql_stmt;
processed := processed + 1;
END LOOP;
-- 5. 修改 SEQUENCE 所有者
FOR obj_rec IN
SELECT schemaname, sequencename FROM pg_sequences s
WHERE schemaname IN (SELECT rolname FROM pg_roles WHERE rolsuper = false)
AND EXISTS (SELECT 1 FROM information_schema.schemata s2 WHERE s2.schema_name = s.schemaname)
AND schemaname NOT IN ('information_schema', 'pg_catalog', 'pg_toast',
'polar_catalog', 'sys', 'cron', 'wmsys', 'polar_feature_utils',
'pg_bitmapindex', 'polar_dbms_stats', 'public')
AND s.sequenceowner != s.schemaname
ORDER BY schemaname, sequencename
LOOP
sql_stmt := format('ALTER SEQUENCE %I.%I OWNER TO %I', obj_rec.schemaname, obj_rec.sequencename, obj_rec.schemaname);
RAISE NOTICE '%', sql_stmt;
EXECUTE sql_stmt;
processed := processed + 1;
END LOOP;
-- 6. 修改 FUNCTION 所有者(排除 extension 和 package 内函数)
FOR obj_rec IN
SELECT n.nspname AS schemaname, p.proname AS funcname,
pg_get_function_identity_arguments(p.oid) AS args,
CASE WHEN n.nspname = 'public' THEN 'PUBLIC' ELSE n.nspname END AS new_owner
FROM pg_proc p JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE EXISTS (SELECT 1 FROM information_schema.schemata s WHERE s.schema_name = n.nspname)
AND n.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast',
'polar_catalog', 'sys', 'cron', 'wmsys', 'polar_feature_utils',
'pg_bitmapindex', 'polar_dbms_stats')
AND n.nspname NOT LIKE 'pg_temp_%' AND n.nspname NOT LIKE 'pg_toast_temp_%'
AND nsppkgns = 0 AND p.prokind = 'f'
AND NOT EXISTS (SELECT 1 FROM pg_depend d WHERE d.objid = p.oid AND d.deptype = 'e')
AND (SELECT rolname FROM pg_roles WHERE oid = p.proowner) !=
CASE WHEN n.nspname = 'public' THEN 'PUBLIC' ELSE n.nspname END
ORDER BY n.nspname, p.proname
LOOP
sql_stmt := format('ALTER FUNCTION %I.%I(%s) OWNER TO %I',
obj_rec.schemaname, obj_rec.funcname, obj_rec.args, obj_rec.new_owner);
RAISE NOTICE '%', sql_stmt;
EXECUTE sql_stmt;
processed := processed + 1;
END LOOP;
-- 7. 修改 PROCEDURE 所有者(排除 package 内存储过程)
FOR obj_rec IN
SELECT n.nspname AS schemaname, p.proname AS procname,
pg_get_function_identity_arguments(p.oid) AS args
FROM pg_proc p JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE n.nspname IN (SELECT rolname FROM pg_roles WHERE rolsuper = false)
AND EXISTS (SELECT 1 FROM information_schema.schemata s WHERE s.schema_name = n.nspname)
AND n.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast',
'polar_catalog', 'sys', 'cron', 'wmsys', 'polar_feature_utils',
'pg_bitmapindex', 'polar_dbms_stats', 'public')
AND n.nspname NOT LIKE 'pg_temp_%' AND n.nspname NOT LIKE 'pg_toast_temp_%'
AND nsppkgns = 0 AND p.prokind = 'p'
AND (SELECT rolname FROM pg_roles WHERE oid = p.proowner) != n.nspname
ORDER BY n.nspname, p.proname
LOOP
sql_stmt := format('ALTER PROCEDURE %I.%I(%s) OWNER TO %I',
obj_rec.schemaname, obj_rec.procname, obj_rec.args, obj_rec.schemaname);
RAISE NOTICE '%', sql_stmt;
EXECUTE sql_stmt;
processed := processed + 1;
END LOOP;
-- 8. 修改 PACKAGE 所有者
FOR obj_rec IN
SELECT n.nspname AS schemaname, p.pkgname AS pkgname
FROM pg_pkg p JOIN pg_namespace n ON p.pkgnamespace = n.oid
WHERE n.nspname IN (SELECT rolname FROM pg_roles WHERE rolsuper = false)
AND EXISTS (SELECT 1 FROM information_schema.schemata s WHERE s.schema_name = n.nspname)
AND n.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast',
'polar_catalog', 'sys', 'cron', 'wmsys', 'polar_feature_utils',
'pg_bitmapindex', 'polar_dbms_stats', 'public')
AND (SELECT rolname FROM pg_roles WHERE oid = p.pkgowner) != n.nspname
ORDER BY n.nspname, p.pkgname
LOOP
sql_stmt := format('ALTER PACKAGE %I.%I OWNER TO %I',
obj_rec.schemaname, obj_rec.pkgname, obj_rec.schemaname);
RAISE NOTICE '%', sql_stmt;
EXECUTE sql_stmt;
processed := processed + 1;
END LOOP;
-- 9. 修改 TYPE 所有者(仅用户自定义复合类型)
FOR obj_rec IN
SELECT n.nspname AS schemaname, t.typname AS typename
FROM pg_type t JOIN pg_namespace n ON t.typnamespace = n.oid
WHERE n.nspname IN (SELECT rolname FROM pg_roles WHERE rolsuper = false)
AND EXISTS (SELECT 1 FROM information_schema.schemata s WHERE s.schema_name = n.nspname)
AND n.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast',
'polar_catalog', 'sys', 'cron', 'wmsys', 'polar_feature_utils',
'pg_bitmapindex', 'polar_dbms_stats', 'public')
AND n.nsppkgns = 0
AND t.typname NOT LIKE '\_%'
AND t.typtype IN ('c', 'b')
AND (t.typrelid = 0 OR EXISTS (SELECT 1 FROM pg_class c WHERE c.oid = t.typrelid AND c.relkind = 'c'))
AND (SELECT rolname FROM pg_roles WHERE oid = t.typowner) != n.nspname
ORDER BY n.nspname, t.typname
LOOP
sql_stmt := format('ALTER TYPE %I.%I OWNER TO %I',
obj_rec.schemaname, obj_rec.typename, obj_rec.schemaname);
RAISE NOTICE '%', sql_stmt;
EXECUTE sql_stmt;
processed := processed + 1;
END LOOP;
-- 10. 修改 SYNONYM 所有者
FOR obj_rec IN
SELECT n.nspname AS schemaname, s.synname AS synname,
CASE WHEN n.nspname = 'public' THEN 'PUBLIC' ELSE n.nspname END AS new_owner
FROM pg_synonym s JOIN pg_namespace n ON s.synnamespace = n.oid
WHERE n.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast',
'polar_catalog', 'sys', 'cron', 'wmsys', 'polar_feature_utils',
'pg_bitmapindex', 'polar_dbms_stats')
AND n.nsppkgns = 0
AND (SELECT rolname FROM pg_roles WHERE oid = s.owner) !=
CASE WHEN n.nspname = 'public' THEN 'PUBLIC' ELSE n.nspname END
ORDER BY n.nspname, s.synname
LOOP
sql_stmt := format('ALTER SYNONYM %I.%I OWNER TO %I',
obj_rec.schemaname, obj_rec.synname, obj_rec.new_owner);
RAISE NOTICE '%', sql_stmt;
EXECUTE sql_stmt;
processed := processed + 1;
END LOOP;
RAISE NOTICE 'Total objects processed: %', processed;
END $$;(可选)授予系统权限
系统权限属于Oracle的高阶用法,主要控制DDL的执行权限。在PolarDB中建议使用高权限账号进行DDL运维操作,因此通常不需要迁移系统权限。如有需要,可在SQLPlus中执行如下PL/SQL,通过DBA_SYS_PRIVS视图获取所有用户和角色拥有的系统权限,导出GRANT语句到grant_sys_priv.sql文件,再到PolarDB中执行。
查询中GRANTEE包含了所有角色(而不仅是非系统角色),因为Oracle系统角色也可能被用户手动授予一些权限,这些权限也需要迁移。同时包含PUBLIC虚拟角色(代表所有用户)。
执行此命令前,需要先在PolarDB控制台的集群参数配置中开启
polar_enable_system_privilege参数。
Oracle 12及以上版本
SET HEADING OFF
SET TRIMSPOOL ON
SET TRIMOUT ON
SET PAGESIZE 0
SET LINESIZE 2000
SET SERVEROUTPUT ON
SPOOL /my/path/to/grant_sys_priv.sql
BEGIN
FOR rec IN (
SELECT
'GRANT ' || PRIVILEGE || ' TO ' || GRANTEE ||
CASE WHEN ADMIN_OPTION = 'YES' THEN ' WITH ADMIN OPTION' END || ';' AS ddl_script
FROM DBA_SYS_PRIVS
WHERE GRANTEE IN (
SELECT USERNAME FROM DBA_USERS WHERE ORACLE_MAINTAINED = 'N'
UNION
SELECT ROLE FROM DBA_ROLES
UNION
SELECT 'PUBLIC' FROM DUAL
)
ORDER BY GRANTEE, PRIVILEGE
) LOOP
DBMS_OUTPUT.PUT_LINE(rec.ddl_script);
END LOOP;
END;
/
SPOOL OFF
EXITOracle 11版本
SET HEADING OFF
SET TRIMSPOOL ON
SET TRIMOUT ON
SET PAGESIZE 0
SET LINESIZE 2000
SET SERVEROUTPUT ON
SPOOL /my/path/to/grant_sys_priv.sql
BEGIN
FOR rec IN (
SELECT
'GRANT ' || PRIVILEGE || ' TO ' || GRANTEE ||
CASE WHEN ADMIN_OPTION = 'YES' THEN ' WITH ADMIN OPTION' END || ';' AS ddl_script
FROM DBA_SYS_PRIVS
WHERE GRANTEE IN (
SELECT USERNAME FROM DBA_USERS WHERE USERNAME NOT IN (
'SYS', 'SYSTEM', 'DBSNMP', 'SYSMAN', 'OUTLN',
'CTXSYS', 'MDSYS', 'ORDSYS', 'ORDDATA', 'ORDPLUGINS', 'SI_INFORMTN_SCHEMA',
'WMSYS', 'EXFSYS', 'LBACSYS', 'XDB', 'ANONYMOUS', 'XS$NULL',
'OLAPSYS', 'MDDATA', 'OWBSYS', 'OWBSYS_AUDIT',
'APEX_030200', 'APEX_040000', 'APEX_040100', 'APEX_040200', 'APEX_050000',
'APEX_PUBLIC_USER', 'FLOWS_FILES', 'FLOWS_030000', 'FLOWS_040000',
'SPATIAL_CSW_ADMIN_USR', 'SPATIAL_WFS_ADMIN_USR',
'DIP', 'ORACLE_OCM', 'APPQOSSYS', 'MGMT_VIEW', 'SCOTT', 'HR',
'TSMSYS', 'WKPROXY', 'WK_TEST', 'PERFSTAT', 'TRACESVR',
'GSMADMIN_INTERNAL', 'GSMCATUSER', 'GSMUSER', 'SYSBACKUP', 'SYSDG', 'SYSKM'
)
AND USERNAME NOT LIKE 'APEX\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'FLOWS\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'ORA\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'ORACLE\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'SYS\_%' ESCAPE '\'
UNION
SELECT ROLE FROM DBA_ROLES
UNION
SELECT 'PUBLIC' FROM DUAL
)
ORDER BY GRANTEE, PRIVILEGE
) LOOP
DBMS_OUTPUT.PUT_LINE(rec.ddl_script);
END LOOP;
END;
/
SPOOL OFF
EXIT(可选)设置默认角色
默认角色属于Oracle的高阶用法,控制用户登录时默认拥有的角色权限。PolarDB的用户登录时默认加载所拥有的所有角色的权限,与Oracle默认值一致,因此通常不需要执行该迁移操作。
如有需要,可在SQLPlus中执行如下PL/SQL,导出ALTER USER ... DEFAULT ROLE语句到default_role.sql文件,再到PolarDB中执行。脚本会智能判断:
如果用户的默认角色数 = 0,生成
ALTER USER ... DEFAULT ROLE NONE。如果用户的默认角色数 = 总角色数,生成
ALTER USER ... DEFAULT ROLE ALL。否则,生成具体的角色列表。
执行此命令前,需要先在PolarDB控制台的集群参数配置中开启polar_enable_default_role参数。
Oracle 12及以上版本
SET HEADING OFF
SET TRIMSPOOL ON
SET TRIMOUT ON
SET PAGESIZE 0
SET LINESIZE 2000
SET SERVEROUTPUT ON
SPOOL /my/path/to/default_role.sql
BEGIN
FOR rec IN (
WITH user_list AS (
SELECT ROLE AS name FROM DBA_ROLES WHERE ORACLE_MAINTAINED = 'N'
UNION
SELECT USERNAME AS name FROM DBA_USERS WHERE ORACLE_MAINTAINED = 'N'
),
role_stats AS (
SELECT
GRANTEE,
COUNT(*) AS total_roles,
SUM(CASE WHEN DEFAULT_ROLE = 'YES' THEN 1 ELSE 0 END) AS default_roles,
LISTAGG(CASE WHEN DEFAULT_ROLE = 'YES' THEN GRANTED_ROLE END, ', ')
WITHIN GROUP (ORDER BY GRANTED_ROLE) AS default_role_list
FROM DBA_ROLE_PRIVS
WHERE GRANTEE IN (SELECT name FROM user_list)
AND GRANTEE NOT IN ('PDBADMIN')
GROUP BY GRANTEE
)
SELECT
'ALTER USER ' || GRANTEE || ' DEFAULT ROLE ' ||
CASE
WHEN default_roles = 0 THEN 'NONE'
WHEN default_roles = total_roles THEN 'ALL'
ELSE default_role_list
END || ';' AS ddl_script
FROM role_stats
ORDER BY GRANTEE
) LOOP
DBMS_OUTPUT.PUT_LINE(rec.ddl_script);
END LOOP;
END;
/
SPOOL OFF
EXITOracle 11版本
SET HEADING OFF
SET TRIMSPOOL ON
SET TRIMOUT ON
SET PAGESIZE 0
SET LINESIZE 2000
SET SERVEROUTPUT ON
SPOOL /my/path/to/default_role.sql
BEGIN
FOR rec IN (
WITH user_list AS (
SELECT ROLE AS name FROM DBA_ROLES WHERE ROLE NOT IN (
'CONNECT', 'RESOURCE', 'DBA', 'SELECT_CATALOG_ROLE',
'EXECUTE_CATALOG_ROLE', 'DELETE_CATALOG_ROLE', 'EXP_FULL_DATABASE',
'IMP_FULL_DATABASE', 'LOGSTDBY_ADMINISTRATOR', 'RECOVERY_CATALOG_OWNER',
'SCHEDULER_ADMIN', 'HS_ADMIN_SELECT_ROLE', 'HS_ADMIN_EXECUTE_ROLE',
'HS_ADMIN_ROLE', 'GLOBAL_AQ_USER_ROLE', 'OEM_ADVISOR', 'OEM_MONITOR',
'MGMT_USER', 'DATAPUMP_EXP_FULL_DATABASE', 'DATAPUMP_IMP_FULL_DATABASE',
'ADM_PARALLEL_EXECUTE_TASK', 'GATHER_SYSTEM_STATISTICS',
'OPTIMIZER_PROCESSING_RATE', 'ACCESS_CATALOG_ROLE', 'JAVAUSERPRIV',
'JAVAIDPRIV', 'JAVASYSPRIV', 'JAVADEBUGPRIV', 'EJBCLIENT', 'JMXSERVER',
'JAVA_ADMIN', 'JAVA_DEPLOY', 'AUTHENTICATEDUSER', 'XDB_SET_INVOKER',
'XDB_WEBSERVICES', 'XDB_WEBSERVICES_WITH_PUBLIC', 'XDB_WEBSERVICES_OVER_HTTP',
'XDBADMIN', 'XDB_WEBSERVICES_OVER_HTTPS', 'CTXAPP', 'ORDADMIN',
'WM_ADMIN_ROLE', 'OLAP_USER', 'OLAP_DBA', 'OLAP_XS_ADMIN',
'CSW_USR_ROLE', 'WFS_USR_ROLE', 'SPATIAL_CSW_ADMIN',
'SPATIAL_WFS_ADMIN', 'LBAC_DBA', 'APEX_ADMINISTRATOR_ROLE',
'FLOWS_FILES_ROLE', 'AQ_ADMINISTRATOR_ROLE', 'AQ_USER_ROLE',
'CWM_USER', 'DBFS_ROLE', 'OLAPI_TRACE_USER', 'OWB$CLIENT',
'OWB_DESIGNCENTER_VIEW', 'OWB_USER'
)
AND ROLE NOT LIKE 'APEX\_%' ESCAPE '\'
AND ROLE NOT LIKE 'FLOWS\_%' ESCAPE '\'
AND ROLE NOT LIKE 'ORA\_%' ESCAPE '\'
AND ROLE NOT LIKE 'ORACLE\_%' ESCAPE '\'
AND ROLE NOT LIKE 'SYS\_%' ESCAPE '\'
UNION
SELECT USERNAME AS name FROM DBA_USERS WHERE USERNAME NOT IN (
'SYS', 'SYSTEM', 'DBSNMP', 'SYSMAN', 'OUTLN',
'CTXSYS', 'MDSYS', 'ORDSYS', 'ORDDATA', 'ORDPLUGINS', 'SI_INFORMTN_SCHEMA',
'WMSYS', 'EXFSYS', 'LBACSYS', 'XDB', 'ANONYMOUS', 'XS$NULL',
'OLAPSYS', 'MDDATA', 'OWBSYS', 'OWBSYS_AUDIT',
'APEX_030200', 'APEX_040000', 'APEX_040100', 'APEX_040200', 'APEX_050000',
'APEX_PUBLIC_USER', 'FLOWS_FILES', 'FLOWS_030000', 'FLOWS_040000',
'SPATIAL_CSW_ADMIN_USR', 'SPATIAL_WFS_ADMIN_USR',
'DIP', 'ORACLE_OCM', 'APPQOSSYS', 'MGMT_VIEW', 'SCOTT', 'HR',
'TSMSYS', 'WKPROXY', 'WK_TEST', 'PERFSTAT', 'TRACESVR',
'GSMADMIN_INTERNAL', 'GSMCATUSER', 'GSMUSER', 'SYSBACKUP', 'SYSDG', 'SYSKM'
)
AND USERNAME NOT LIKE 'APEX\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'FLOWS\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'ORA\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'ORACLE\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'SYS\_%' ESCAPE '\'
),
role_stats AS (
SELECT
GRANTEE,
COUNT(*) AS total_roles,
SUM(CASE WHEN DEFAULT_ROLE = 'YES' THEN 1 ELSE 0 END) AS default_roles,
LISTAGG(CASE WHEN DEFAULT_ROLE = 'YES' THEN GRANTED_ROLE END, ', ')
WITHIN GROUP (ORDER BY GRANTED_ROLE) AS default_role_list
FROM DBA_ROLE_PRIVS
WHERE GRANTEE IN (SELECT name FROM user_list)
AND GRANTEE NOT IN ('PDBADMIN')
GROUP BY GRANTEE
)
SELECT
'ALTER USER ' || GRANTEE || ' DEFAULT ROLE ' ||
CASE
WHEN default_roles = 0 THEN 'NONE'
WHEN default_roles = total_roles THEN 'ALL'
ELSE default_role_list
END || ';' AS ddl_script
FROM role_stats
ORDER BY GRANTEE
) LOOP
DBMS_OUTPUT.PUT_LINE(rec.ddl_script);
END LOOP;
END;
/
SPOOL OFF
EXIT(可选)创建PROFILE
PROFILE属于Oracle的高阶用法,用于控制密码复杂度校验、过期时间等。PolarDB的用户和密码通常在控制台设置,因此通常不需要迁移PROFILE。
如有需要,可在SQLPlus中执行如下PL/SQL获取所有用户的PROFILE,执行结果分别保存在profile_verify_functions.sql、create_profiles.sql、alter_default_profile.sql、alter_user_profile.sql 4个文件,将这些文件传输到PolarDB侧,通过psql客户端按顺序执行,即可将Oracle的PROFILE迁移到PolarDB。
仅迁移密码策略参数:脚本只迁移PASSWORD类参数(
PASSWORD_LIFE_TIME、PASSWORD_REUSE_TIME、PASSWORD_REUSE_MAX、PASSWORD_VERIFY_FUNCTION、PASSWORD_GRACE_TIME、PASSWORD_LOCK_TIME、FAILED_LOGIN_ATTEMPTS),不迁移资源限制参数(如SESSIONS_PER_USER、CPU_PER_SESSION、CONNECT_TIME等),因为PolarDB内核目前不支持这些参数。排除系统PROFILE:
ORA_CIS_PROFILE、ORA_STIG_PROFILE、MONITORING_PROFILE为Oracle内置PROFILE,不需迁移。排除内置验证函数:
ORA12C_VERIFY_FUNCTION和ORA12C_STIG_VERIFY_FUNCTION是Oracle内置函数,不需导出,PolarDB已内置。执行顺序:必须按Step 1 → 2 → 3 → 4顺序执行,因为Step 2可能引用Step 1创建的验证函数。
Oracle 12及以上版本
-- ==================================================================
-- Step 1: 导出 Profile 引用的密码验证函数
-- 说明:查询 DBA_SOURCE 获取被 Profile 引用的自定义验证函数的完整定义
-- 排除 Oracle 内置的验证函数(ORA12C_VERIFY_FUNCTION, ORA12C_STIG_VERIFY_FUNCTION)
-- ==================================================================
SET SERVEROUTPUT ON SIZE UNLIMITED
SET LINESIZE 32767
SET LONG 2000000000
SET LONGCHUNKSIZE 2000000000
SET PAGESIZE 0
SET FEEDBACK OFF
SET HEADING OFF
SPOOL profile_verify_functions.sql
SELECT 'CREATE OR REPLACE ' || LISTAGG(TEXT, CHR(10)) WITHIN GROUP (ORDER BY LINE) AS ddl_script
FROM DBA_SOURCE
WHERE NAME IN (
SELECT DISTINCT LIMIT
FROM DBA_PROFILES
WHERE RESOURCE_NAME = 'PASSWORD_VERIFY_FUNCTION'
AND LIMIT IS NOT NULL
AND LIMIT <> 'NULL'
AND LIMIT NOT IN ('ORA12C_VERIFY_FUNCTION', 'ORA12C_STIG_VERIFY_FUNCTION')
)
AND TYPE = 'FUNCTION'
AND OWNER = 'SYS'
GROUP BY NAME, TYPE, OWNER;
SPOOL OFF
-- ==================================================================
-- Step 2: 创建非 DEFAULT 的 Profile
-- 说明:从 DBA_PROFILES 中读取密码相关参数,生成 CREATE PROFILE 语句
-- 排除系统 Profile(DEFAULT, ORA_CIS_PROFILE, ORA_STIG_PROFILE, MONITORING_PROFILE)
-- 仅迁移密码策略参数,不迁移资源限制参数
-- ==================================================================
SPOOL create_profiles.sql
SELECT DISTINCT 'CREATE PROFILE ' || PROFILE || ' LIMIT '
|| LISTAGG(RESOURCE_NAME || ' ' || LIMIT, ' ') WITHIN GROUP (ORDER BY RESOURCE_NAME)
|| ';' AS ddl_script
FROM DBA_PROFILES WHERE RESOURCE_NAME IN (
'PASSWORD_LIFE_TIME',
'PASSWORD_REUSE_TIME',
'PASSWORD_REUSE_MAX',
'PASSWORD_VERIFY_FUNCTION',
'PASSWORD_GRACE_TIME',
'PASSWORD_LOCK_TIME',
'FAILED_LOGIN_ATTEMPTS'
) AND PROFILE NOT IN ('DEFAULT', 'ORA_CIS_PROFILE', 'ORA_STIG_PROFILE', 'MONITORING_PROFILE')
GROUP BY PROFILE;
SPOOL OFF
-- ==================================================================
-- Step 3: 修改 DEFAULT Profile
-- 说明:目标端已存在 DEFAULT profile,因此使用 ALTER PROFILE 而非 CREATE
-- ==================================================================
SPOOL alter_default_profile.sql
SELECT DISTINCT 'ALTER PROFILE DEFAULT LIMIT '
|| LISTAGG(RESOURCE_NAME || ' ' || LIMIT, ' ') WITHIN GROUP (ORDER BY RESOURCE_NAME)
|| ';' AS ddl_script
FROM DBA_PROFILES WHERE RESOURCE_NAME IN (
'PASSWORD_LIFE_TIME',
'PASSWORD_REUSE_TIME',
'PASSWORD_REUSE_MAX',
'PASSWORD_VERIFY_FUNCTION',
'PASSWORD_GRACE_TIME',
'PASSWORD_LOCK_TIME',
'FAILED_LOGIN_ATTEMPTS'
) AND PROFILE = 'DEFAULT'
GROUP BY PROFILE;
SPOOL OFF
-- ==================================================================
-- Step 4: 绑定用户与 Profile
-- 说明:将源端非系统用户的 Profile 绑定关系迁移到目标端
-- 过滤条件:ORACLE_MAINTAINED = 'N' 排除系统用户
-- ==================================================================
SPOOL alter_user_profile.sql
SELECT 'ALTER USER ' || USERNAME || ' PROFILE ' || PROFILE || ';' AS ddl_script
FROM DBA_USERS
WHERE PROFILE NOT IN ('ORA_CIS_PROFILE', 'ORA_STIG_PROFILE', 'MONITORING_PROFILE')
AND ORACLE_MAINTAINED = 'N'
ORDER BY USERNAME;
SPOOL OFFOracle 11版本
Oracle 11g 没有ORACLE_MAINTAINED列,因此 Step 4 使用预设名单过滤系统用户。Step 1、2、3 与 Oracle 12及以上版本完全相同,仅 Step 4 不同。
-- ==================================================================
-- Step 1~3: 与 Oracle 12及以上版本完全相同,不再重复
-- ==================================================================
-- ==================================================================
-- Step 4: 绑定用户与 Profile(11g 版本)
-- 说明:使用系统用户名单替代 ORACLE_MAINTAINED 列
-- ==================================================================
SPOOL alter_user_profile.sql
SELECT 'ALTER USER ' || USERNAME || ' PROFILE ' || PROFILE || ';' AS ddl_script
FROM DBA_USERS
WHERE PROFILE NOT IN ('ORA_CIS_PROFILE', 'ORA_STIG_PROFILE', 'MONITORING_PROFILE')
AND USERNAME NOT IN (
'SYS','SYSTEM','DBSNMP','SYSMAN','OUTLN',
'CTXSYS','MDSYS','ORDSYS','ORDDATA','ORDPLUGINS','SI_INFORMTN_SCHEMA',
'WMSYS','EXFSYS','LBACSYS','XDB','ANONYMOUS','XS$NULL',
'OLAPSYS','MDDATA',
'OWBSYS','OWBSYS_AUDIT',
'APEX_030200','APEX_040000','APEX_040100','APEX_040200','APEX_050000',
'APEX_PUBLIC_USER','FLOWS_FILES','FLOWS_030000','FLOWS_040000',
'SPATIAL_CSW_ADMIN_USR','SPATIAL_WFS_ADMIN_USR',
'DIP','ORACLE_OCM','APPQOSSYS','MGMT_VIEW','SCOTT','HR','TSMSYS',
'WKPROXY','WK_TEST','PERFSTAT','TRACESVR',
'GSMADMIN_INTERNAL','GSMCATUSER','GSMUSER','SYSBACKUP','SYSDG','SYSKM'
)
AND USERNAME NOT LIKE 'APEX\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'FLOWS\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'ORA\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'ORACLE\_%' ESCAPE '\'
AND USERNAME NOT LIKE 'SYS\_%' ESCAPE '\'
ORDER BY USERNAME;
SPOOL OFF后续步骤
完成角色、用户及权限迁移后,建议使用一致性校验工具比较 Oracle 源库与PolarDB目标库的用户、角色和权限是否一致。如果还通过 DTS 完成了对象结构迁移,也可以使用该工具校验表、视图、索引、序列、函数、存储过程、包等对象结构,定位差异。具体部署和报告解读方式,请参见PolarDB 结构与权限校验工具。