Не обращая внимания на то, что это хорошая идея ... вы получаете эту ошибку, потому что вы переосмысливаете свои манипуляции со строками. То, что вы кладете в свой стол, выглядит хорошо. Проблема в том, когда вы получаете его обратно. Значение в таблице уже является строкой, поэтому вам не нужно заключать ее в другой набор кавычек.
То, что вы на самом деле используете, эквивалентно:
EXECUTE IMMEDIATE '''GRANT SELECT ON MYUSER.CR_STATUS_TABLE TO SOMEUSER''';
, который также будет выдавать «ORA-00900: неверный SQL оператор», а не отдельную рабочую версию:
EXECUTE IMMEDIATE 'GRANT SELECT ON MYUSER.CR_STATUS_TABLE TO SOMEUSER';
Если вы поменяли местами порядок EXECUTE IMMEDIATE
и DBMS_OUTPUT
вызовы, которые вы увидели бы с оператором задачи до его выполнения, что было бы более полезно - вы бы увидели эти кавычки как часть строки.
Так что во втором триггере вместо делать:
FOR loop_counter IN (select '''' || privilege_x || '''' AS privilege_x from temp_priv)
LOOP
EXECUTE IMMEDIATE loop_counter.privilege_x;
DBMS_OUTPUT.PUT_LINE(loop_counter.privilege_x);
END LOOP;
просто делать:
FOR loop_counter IN (select privilege_x from temp_priv)
LOOP
DBMS_OUTPUT.PUT_LINE(loop_counter.privilege_x);
EXECUTE IMMEDIATE loop_counter.privilege_x;
END LOOP;
Однако это все равно не сработает; теперь он получит ORA-30511: недопустимая операция DDL в системных триггерах. Вероятно, это связано с ограничениями , показанными в документации :
Триггер не может выполнять операции DDL над объектом, вызвавшим генерирование события.
DDL для других Объекты ограничены компиляцией объекта, созданием триггера, созданием, изменением и удалением таблицы.
Вы сказали, что «легко забыть сделать резервную копию и восстановить их впоследствии», но вы » нам нужно будет создать надежный процесс вокруг ваших обновлений, чтобы убедиться, что это происходит.
Вы можете изменить свой процесс, чтобы он имел отдельный шаг в конце каждого запускаемого всегда обновления, которое выполняет все эти сохраненные операторы для всех объектов - возможно, пропускающие или игнорирующие ошибки из всего, что не было воссоздано, - а затем удаляющее таблицу temp_priv
.
Но вы не хотите (пытаться) создать этот временный Таблица в триггере в любом случае - если два представления отброшены, первое создает его, второе завершается ошибкой, потому что оно уже существует. Возможно, более реалистичный c подход может состоять в том, чтобы создать эту таблицу один раз сейчас:
create table TEMP_PRIV (PRIVILEGE_X VARCHAR2(4000));
и затем использовать ее для всех последующих обновлений, либо заполнив ее всеми грантами для всех рассматривает как один шаг перед началом обновления:
INSERT INTO TEMP_PRIVS (PRIVILEGE_X)
SELECT 'GRANT ' || PRIVILEGE || ' ON MYUSER.' || TABLE_NAME || ' TO ' || GRANTEE
FROM USER_VIEWS UV
JOIN USER_TAB_PRIVS UTP ON UTP.TABLE_NAME = UV.VIEW_NAME
WHERE UTP.GRANTEE not in ('MYUSER','PUBLIC');
или если вы все еще беспокоитесь о том, что можете забыть об этом шаге, то с помощью триггера, чтобы сделать его одним видом за раз, когда они отброшены:
create or replace TRIGGER RECORD_GRANTS_ONDROP
BEFORE DROP ON MYUSER.SCHEMA
BEGIN
IF ora_dict_obj_owner = 'MYUSER' and ora_dict_obj_name not like 'TEMP_PRIV%' and ora_dict_obj_type='VIEW' then
INSERT INTO TEMP_PRIV
SELECT 'GRANT ' || PRIVILEGE || ' ON MYUSER.' || TABLE_NAME || ' TO ' || GRANTEE
FROM USER_TAB_PRIVS
WHERE GRANTEE not in ('MYUSER','PUBLIC')
AND TABLE_NAME = ora_dict_obj_name;
END IF;
END;
/
Затем в конце процесса обновления еще раз введите все операторы в таблицу и очистите их, готовые к следующему разу:
DECLARE
missing_view EXCEPTION;
PRAGMA EXCEPTION_INIT(missing_view, -942);
BEGIN
FOR loop_counter IN (select privilege_x from temp_priv)
LOOP
BEGIN
DBMS_OUTPUT.PUT_LINE(loop_counter.privilege_x);
EXECUTE IMMEDIATE loop_counter.privilege_x;
EXCEPTION
WHEN missing_view THEN
-- report but otherwise ignore
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
END LOOP;
END;
/
TRUNCATE TABLE temp_priv;
Если вы go с более простым триггерный подход, тогда он повторно предоставит существующие привилегии, но это нормально. А обработчик исключений означает, что он сообщит, но пропустит любые просмотры, которые были отброшены и не воссозданы, если это когда-либо произойдет. (Конечно, вам все равно придется иметь дело с любым новым представлением; ваш триггер после создания в любом случае не помог бы с этим.) И обратите внимание, что я обрезал таблицу, а не отбросил ее - так что он все еще там, пустой, когда приходит следующее обновление и хочет его заполнить.