如何将对所有数据库对象的只读访问权限授予 oracle 中的另一个用户?

How to grant read-only access to all the DB objects to another user in oracle?

我尝试将对所有数据库对象的只读访问权限授予另一个可能不存在的用户。这就是我尝试这样做的方式:

DECLARE
    user_exists NUMBER;

BEGIN

    SELECT COUNT(*) INTO user_exists FROM ALL_USERS WHERE USERNAME = '${user}';

    IF user_exists > 0 THEN

        FOR obj IN (SELECT object_name, object_type
                  FROM all_objects
                  WHERE owner = '${owner}'
                    AND object_type IN ('TABLE', 'VIEW', 'PROCEDURE', 'FUNCTION', 'PACKAGE'))
            LOOP
                IF obj.object_type IN ('TABLE', 'VIEW') THEN
                    EXECUTE IMMEDIATE 'GRANT SELECT ON ${owner}.' || obj.object_name ||
                                      ' TO ${user}';
                ELSIF obj.object_type IN ('PROCEDURE', 'FUNCTION', 'PACKAGE') THEN
                    EXECUTE IMMEDIATE 'GRANT EXECUTE ON ${owner}.' || obj.object_name || ' TO ${user}';
                END IF;
            END LOOP;

    END IF;

END;

脚本失败并显示以下消息:

  Message    : ORA-00942: table or view does not exist
  ORA-06512: at line 16
  ORA-06512: at line 16

我做错了什么?

除非我错了,所有 在你的代码中看起来像 ${something} 的东西都是错误的。

它应该做什么?您真的查询过 ALL_OBJECTS 并查看其中的内容吗?看起来好像你认为它们会自动被某种存在的东西所取代......呵呵,只是在你的想象中?

如果它是一个程序,那么您可以将所有者(授权人)和另一个用户(被授权人)作为参数传递。您编写的代码可以重复使用,其中大部分:

SQL> CREATE OR REPLACE PROCEDURE p_grant (par_owner    IN VARCHAR2,
  2                                       par_grantee  IN VARCHAR2)
  3  AS
  4     user_exists  NUMBER;
  5     l_str        VARCHAR2 (1000);
  6  BEGIN
  7     SELECT COUNT (*)
  8       INTO user_exists
  9       FROM all_users
 10      WHERE username = DBMS_ASSERT.schema_name (par_grantee);
 11
 12     IF user_exists > 0
 13     THEN
 14        FOR obj IN (SELECT object_name, object_type
 15                      FROM all_objects
 16                     WHERE     owner = DBMS_ASSERT.schema_name (par_owner)
 17                           AND object_type IN ('TABLE',
 18                                               'VIEW',
 19                                               'PROCEDURE',
 20                                               'FUNCTION',
 21                                               'PACKAGE')
 22                           AND STATUS = 'VALID')
 23        LOOP
 24           IF obj.object_type IN ('TABLE', 'VIEW')
 25           THEN
 26              l_str :=
 27                    'GRANT SELECT ON '
 28                 || par_owner
 29                 || '.'
 30                 || obj.object_name
 31                 || ' TO '
 32                 || par_grantee;
 33           ELSIF obj.object_type IN ('PROCEDURE', 'FUNCTION', 'PACKAGE')
 34           THEN
 35              l_str :=
 36                    ' grant execute on '
 37                 || par_owner
 38                 || '.'
 39                 || obj.object_name
 40                 || ' to '
 41                 || par_grantee;
 42           END IF;
 43
 44           DBMS_OUTPUT.put_line (l_str);
 45
 46           EXECUTE IMMEDIATE l_str;
 47        END LOOP;
 48     END IF;
 49  END;
 50  /

Procedure created.

测试:

SQL> EXEC p_grant('SCOTT', 'MIKE');
grant execute on SCOTT.F_TEST to MIKE
GRANT SELECT ON SCOTT.CALENDAR TO MIKE
grant execute on SCOTT.EXPORTTABLECONTENT to MIKE
<snip>
GRANT SELECT ON SCOTT.SALGRADE TO MIKE
GRANT SELECT ON SCOTT.LINKS TO MIKE

PL/SQL procedure successfully completed.

这是一个 PL/SQL 匿名块,因此您需要使用 PL/SQL 变量和绑定替换。试试这个:

DECLARE
    user_exists NUMBER;
    l_user VARCHAR2(30);
    l_owner VARCHAR2(30);
BEGIN
    l_user := 'USER1';
    l_owner := 'USER2';

    SELECT COUNT(*) INTO user_exists FROM ALL_USERS WHERE USERNAME = l_user;

    IF user_exists > 0 THEN

        FOR obj IN (SELECT object_name, object_type
                  FROM all_objects
                  WHERE owner = l_owner
                    AND object_type IN ('TABLE', 'VIEW', 'PROCEDURE', 'FUNCTION', 'PACKAGE'))
            LOOP
                IF obj.object_type IN ('TABLE', 'VIEW') THEN
                    EXECUTE IMMEDIATE 'GRANT SELECT ON :1 TO :2' USING obj.owner||'.'||obj.object_name, l_user;
                ELSIF obj.object_type IN ('PROCEDURE', 'FUNCTION', 'PACKAGE') THEN
                    EXECUTE IMMEDIATE 'GRANT EXECUTE ON :1 TO :2' USING obj.owner||'.'||obj.object_name, l_user;
                END IF;
            END LOOP;

    END IF;

END;

当 table 为“flyway_schema_history”时,脚本将在授予对象 select 时失败...我不知道为什么会这样,但一种解决方案是跳过 table.

这是更新后的工作脚本:

DECLARE
    user_exists NUMBER;

BEGIN

    SELECT COUNT(*) INTO user_exists FROM ALL_USERS WHERE USERNAME = '${user}';

    IF user_exists > 0 THEN

        FOR obj IN (SELECT object_name, object_type
                  FROM all_objects
                  WHERE owner = '${owner}'
                    AND object_type IN ('TABLE', 'VIEW', 'PROCEDURE', 'FUNCTION', 'PACKAGE')
                    AND object_name NOT LIKE 'flyway_schema_history')
            LOOP
                IF obj.object_type IN ('TABLE', 'VIEW') THEN
                    EXECUTE IMMEDIATE 'GRANT SELECT ON ${owner}.' || obj.object_name ||
                                      ' TO ${user}';
                ELSIF obj.object_type IN ('PROCEDURE', 'FUNCTION', 'PACKAGE') THEN
                    EXECUTE IMMEDIATE 'GRANT EXECUTE ON ${owner}.' || obj.object_name || ' TO ${user}';
                END IF;
            END LOOP;

    END IF;

END;