Не компилируется пакет oracle
Я пытаюсь создать список зависимостей пакетов PL/SQL, чтобы помочь настроить автоматический сценарий сборки для моих пакетов для запуска на тестовом сервере. Есть ли способ начать с одного пакета ("корневой" пакет, идентифицированный по имени, в идеале), а затем найти все зависимости и порядок их компиляции? Зависимости уже полностью разрешены в моей личной схеме (так что, по крайней мере, мне есть с чего начать - но куда мне идти дальше?).
EDIT:
используемый инструмент сборки будет использовать порядок сборки и будет возвращать эти файлы в порядке из системы управления версиями, а затем передавать их Oracle для компиляции (сам инструмент сборки написан на Python или Java или обоих - у меня нет доступа к источнику). В принципе, инструмент сборки должен ввести список файлов для компиляции в том порядке, в котором они должны быть скомпилированы в, и доступ к этим файлам в системе управления версиями. Если так, то все будет хорошо.
EDIT:
Спасибо за аккуратные скрипты. К сожалению, процесс сборки в основном не в моих руках. Процесс основан на инструменте сборки, который был построен поставщиком продукта, с которым мы интегрируемся, поэтому единственными входными данными, которые я могу дать процессу сборки, являются список файлов в том порядке, в котором они должны быть встроены. Если есть ошибка компилятора, инструмент сборки терпит неудачу, мы должны вручную отправьте запрос на новую сборку. Поэтому важен список файлов в том порядке, в котором они должны быть скомпилированы.
EDIT:
EDIT: (С кодом!)
Я знаю, что в целом этот что-то вроде этого не нужно для Oracle, но для всех, кто все еще заинтересован.
я собрал небольшой скрипт, который, кажется, может получить порядок сборки, так что все пакеты будут построены в правильном порядке без ошибок, связанных с зависимостями (по отношению к pacakges) в первый раз:
Я знаю, что это не самый красивый код (глобалы повсюду и т. д. ugh), и я, вероятно, перепечатаю его, если у меня будет возможность сегодня днем очистите его, но прямо сейчас он создает порядок сборки, который, кажется, запускается в первый раз без проблем.
:OBJECT_NAME должен быть корневым объектом, который вы хотите отслеживать все зависимости и порядок построения. Для меня это основной пакет с одним методом, который является точкой входа в остальную часть системы.
:OBJECT_TYPE Я в основном ограничился PACKAGE BODY , но не должно быть слишком много работы для включения других типов, таких как триггеры.
последнее, объект, указанный :OBJECT_NAME не будет отображаться в выходных данных, но это должен быть последний элемент, поэтому вам придется добавить его в список сборки вручную.
обновление: Я только что обнаружил user_dependencies и all_dependencies , этот код, вероятно, можно было бы сделать намного проще.
если вы действительно имеете дело только с пакетами PL/SQL, вам не нужно потеть порядок сборки. Просто сначала создайте все спецификации пакета. Затем вы можете развернуть все тела пакетов, и они будут компилироваться, потому что их зависимости являются спецификациями пакета.
если у вас есть некоторые спецификации пакета, которые зависят от других спецификаций - если у вас есть пакеты, которые объявляют, скажем, константы, подтипы или курсоры ref, которые используются в подписях упакованных процедур - затем вам нужно сначала создать эти спецификации пакета. Но их должно быть достаточно мало, чтобы вы могли расположить их в скрипте сборки вручную.
редактировать
похоже, что они будут делать инкрементные и" чистые " сборки, поэтому порядок сборки будет иметь наибольшее значение когда они очистят окружающая среда и перестроить его.
это ничего не меняет.
вот расширенный пример. Я есть схема с тремя пакетами.
интересно то, что процедура в PKG1 вызывает процедуру из PKG2, процедура в PKG2 вызывает процедуру из PKG3, а процедура в PKG3 вызывает процедуру из PKG1.
вопрос: как работает эта циклическая зависимость?
А. это не циклическая зависимость.
все зависимые объекты являются телами пакетов, все ссылки объекты-это упакованные спецификации. Следовательно, если я trash'n'Rebuild схему это действительно не имеет значения, какой порядок я использую. Сначала мы мусор .
затем мы восстановим .
порядок отдельных объектов не имеет значения. Просто создайте спецификации пакета перед телами пакетов. Хотя даже это не имеет значения.
PKG4 недопустимо, потому что мы не построили CONSTANTS_PKG еще.
все, что построено с помощью CREATE OR REPLACE всегда создается, он просто помечается как недопустимый, если есть ошибки. Как только мы выполняем его, прямо или косвенно, база данных компилирует его для нас. Так что порядок не имеет значения. На самом деле это не так.
если идея завершения сборки с недействительными объектами касается вас - и у меня есть некоторая симпатия к этому, нам говорят не жить с разбитыми окнами-вы можете использовать utlrp скрипт или в 11g в UTL_RECOMP пакет; любой подход требует учетной записи SYSDBA.
edit 2
это политическая проблема, а не техническим. Это не означает, что политические проблемы не могут быть решены с помощью технического решения, просто техническое решение не является лучшим инструментом для работы. Удача.
Предупреждения во время компиляции способны существенно упростить сопровождение вашего кода и снизить вероятность ошибок. Не путайте предупреждения компилятора с ошибками; с предупреждениями ваша программа все равно будет компилироваться и работать. Тем не менее при выполнения кода, для которого выдавались предупреждения, возможно неожиданное поведение или снижение производительности.
В этой заметке моего блога вы узнаете, как работают предупреждения компилятора и какие проблемы выявляются в текущих версиях. Начнем с краткого примера применения предупреждений времени компиляции в сеансе.
Директива $ERROR
Предположим, я хочу задать для некоторой программной единицы уровень оптимизации 1, чтобы ускорить компиляцию. В следующем примере директива $$ используется для проверки уровня оптимизации, предоставляемого средой компиляции. Далее при необходимости программа инициирует ошибку директивой $ERROR :
Директива $IF
Конструкция выбора, реализованная в виде директивы $IF , используется для передачи шага условной компиляции препроцессору. Общий синтаксис этой директивы:
Здесь логическое выражение представляет собой статическое выражение (которое может вычисляться на момент компиляции), результатом которого является TRUE , FALSE или NULL . Фрагмент кода содержит произвольную последовательность команд PL/ SQL , которая передается компилятору для обработки (в соответствии с правилами вычисления выражений).
Статические выражения могут строиться из следующих элементов:
- Логические значения, PLS_INTEGER и NULL , а также комбинации этих литералов.
- Статические выражения с логическими значениями, PLS_INTEGER и VARCHAR2 .
- Директивы получения информации (то есть идентификаторы с префиксом $$ ). Такие директивы могут предоставляться Oracle (например, $$PLSQL_0PTIMIZE_LEVEL ; полный список приведен в статье «Оптимизирующий компилятор») или задаваться параметром компиляции PLSQL_CCFLAGS .
- Статические константы, определенные в пакете PL/SQL .
- Большинство операторов сравнения (>, допустимы, но использовать выражение IN нельзя), логические операции — такие, как AND и OR , конкатенации статических символьных выражений и проверки на NULL .
Несколько примеров статических выражений в директивах $IF :
- Если пользовательская директива получения информации, управляющая отладкой, отлична от NULL , инициализировать подсистему отладки:
- Проверка значения пользовательской пакетной константы и уровня оптимизации:
Строковые литералы и конкатенация строк разрешены только в директиве $ERROR ; в директиве $IF их присутствие недопустимо.
Синхронизация кода с использованием пакетных констант
Использование пакетных констант в директиве выбора позволяет легко синхронизировать несколько программных единиц по некоторому параметру условной компиляции. Такая синхронизация возможна благодаря тому, что автоматическое управление зависимостями Oracle распространяется на директивы выбора. Иначе говоря, если программная единица PROG содержит директиву выбора, которая ссылается на пакет PKG , то она помечается как зависящая от PKG . Когда спецификация PKG перекомпилируется, все программные единицы, использующие пакетную константу, помечаются как INVALID и должны быть перекомпилированы.
Допустим, я хочу использовать условную компиляцию для автоматического включения или исключения логики отладки и трассировки в своей кодовой базе. Я определяю спецификацию пакета для хранения необходимых констант:
Затем эти константы используются в процедуре calc_totals :
В процессе разработки константа debug_active инициализируется значением TRUE . Когда нужно переводить код в окончательную версию, я меняю значение флага на FALSE и перекомпилирую пакет. Программа calc_totals и все остальные программы с похожими директивами выбора объявляются неработоспособными и должны пройти перекомпиляцию.
PLW-05001: предыдущее использование строки противоречит этому использованию
Предупреждение проявляется при объявлении нескольких переменных или констант с одинаковыми именами. Оно также может проявиться в том случае, если список параметров программы, определенный в спецификации пакета, отличается от списка в определении из тела пакета.
Возможно, вы скажете: «Да, я видел эту ошибку, но это именно ошибка компиляции, а не предупреждение». Собственно, вы правы — следующая программа не откомпилируется:
Компилятор выдает ошибку PLS-00371 (в разделе объявлений разрешено не более одного объявления ‘A’).
Почему же для этой ситуации создано предупреждение? Попробуем удалить присваивание переменной с именем a:
Программа откомпилируется! База данных не выдает ошибку PLS-00371 , потому что я не использую ни одну из переменных в своем коде. Предупреждение PLW -05001 устраняет этот недостаток, сообщая о том, что я объявляю одноименные переменные без использования:
Примеры условной компиляции
Начнем с примеров использования разных видов условной компиляции.
Использование констант пакета приложения в директиве $IF
Директива $IF может обращаться к константам, определенным в ваших пакетах. В следующем примере начисляемая премия изменяется в зависимости от того, соответствует ли место установки стороннего приложения положениям закона Сарбейнза-Оксли. Скорее всего, эта настройка будет оставаться неизменной в течение долгого времени. Если я положусь на традиционную условную команду, в приложении останется ветвь логики, которая никогда не будет применяться. С условной компиляцией лишний код удаляется перед компиляцией:
Переключение состояния трассировки с использованием флагов условной компиляции
Теперь я могу создать собственные механизмы отладки/трассировки и выполнить их условную компиляцию в своем коде. Это означает, что в окончательной версии кода этот код будет полностью удален, чтобы избежать непроизводительных затрат на выполнение этой логики. Следует отметить, что специальный параметр компилятора PLSQL_CCFLAGS может задавать как логические значения, так и значения PLS_INTEGER :
Директива получения информации
Директива получения информации запрашивает информацию о среде компиляции. Конечно, по такому описанию трудно понять что-то определенное, поэтому мы должны поближе познакомиться с синтаксисом директив получения информации и разными источниками информации для таких директив.
Синтаксис директивы получения информации выглядит так:
$$идентификатор — здесь идентификатор представляет собой любой идентификатор PL/SQL , который может представлять:
- Параметры среды компиляции — значения из представления словаря данных USER_PLSQL_OBJECT_SETTINGS .
- Пользовательская директива — определяется командой ALTER. SET PLSQL_CCFLAGS (см. далее «Использование параметра PLSQL_CCFLAGS »).
- Предопределенные директивы — $$PLSQL_LINE и $$PLSQL_UNIT ; возвращают номер строки и имя программы.
Директивы получения информации предназначены для использования в конструкциях условной компиляции, но они также могут использоваться в других местах кода PL/ SQL . Например, для вывода текущего номера строки в моей программе я могу использовать следующий код:
Также директивы получения информации могут использоваться для определения и применения констант уровня приложения. Предположим, максимальный период хранения данных в моем приложении составляет 100 лет. Вместо того чтобы жестко фиксировать это значение в своем коде, я делаю следующее:
Что еще важнее, директивы получения информации могут использоваться в тех местах, где переменные запрещены. Пара примеров:
Встроенный пакет DBMS_DB_VERSION предоставляет набор констант для получения абсолютной и относительной информации о версии установленной базы данных. Константы, определенные в версии 12.1, приведены в табл. 1. В каждой новой версии Oracle добавляются две новые относительные константы, а значения, возвращаемые константами VERSION и RELEASE , обновляются.
Таблица 1. Константы DBMS_DB_VERSION
Имя пакетной константы | Описание | Значение в Oracle Database 12c Release 1 |
DBMS_DB_VERSION.VERSION | Номер версии базы данных | 12 |
DBMS_DB_VERSION.RELEASE | Номер выпуска базы данных | 1 |
DBMS_DB_VERSION.VER_LE_9 | TRUE, если текущая версия меньше либо равна Oracle9i | FALSE |
DBMS_DB_VERSION.VER_LE_9_1 | TRUE, если текущая версия меньше либо равна 9.1 | FALSE |
DBMS_DB_VERSION.VER_LE_9_2 | TRUE, если текущая версия меньше либо равна 9.2 | FALSE |
DBMS_DB_VERSION.VER_LE_10 | TRUE, если текущая версия меньше либо равна Oracle10g | FALSE |
DBMS_DB_VERSION.VER_LE_10_1 | TRUE, если текущая версия меньше либо равна 10.1 | FALSE |
DBMS_DB_VERSION.VER_LE_10_2 | TRUE, если текущая версия меньше либо равна 10.2 | FALSE |
DBMS_DB_VERSION.VER_LE_11_1 | TRUE, если текущая версия меньше либо равна 11.1 | FALSE |
DBMS_DB_VERSION.VER_LE_11_2 | TRUE, если текущая версия меньше либо равна 11.2 | FALSE |
DBMS_DB_VERSION.VER_LE_12 | TRUE, если текущая версия меньше либо равна 12.1 | TRUE |
DBMS_DB_VERSION.VER_LE_12_1 | TRUE, если текущая версия меньше либо равна 12.1 | TRUE |
Этот пакет проектировался для использования с условной компиляцией, но разумеется, ничто не мешает вам использовать его для любых других целей.
Интересно, что вы можете писать выражения со ссылками на еще не определенные константы пакета DBMS_DB_VERSI0N . Если они не будут обрабатываться, как в следующем примере, это не приведет к ошибкам. Пример:
Определение параметров среды компиляции
Следующая информация (соответствующая значениям из представления словаря данных USER_PLSQL_OBJECT_SETTlNGS ) доступна в директивах получения информации:
- $$PLSQL_DEBUG — режим отладки для единицы компиляции.
- $$PLSQL_OPTIMIZE_LEVEL — уровень оптимизации для единицы компиляции.
- $$PLSQL_CODE_TYPE — режим компиляции для единицы компиляции.
- $$PLSQL_WARNINGS — режим предупреждений компиляции для единицы компиляции.
- $$NLS_LENGTH_SEMANTICS — значение, заданное для семантики длины NLS .
Примеры использования всех перечисленных параметров представлены в файле cc_plsql_parameters.sql на сайте книги.
имя и номер строки единицы компиляции
Oracle неявно определяет четыре очень полезные директивы получения информации, предназначенные для использования в директивах $IF и $ERROR :
- $$PLSQL_UNIT — имя единицы компиляции, в которой находится ссылка. Если этой единицей является анонимный блок, то $$PLSQL_UNIT содержит NULL .
- $$PLSQL_LINE — номер строки в единице компиляции, в которой находится ссылка.
- $$PLSQL_UNIT_0WNER (Oracle Database 12c) — имя владельца текущей программной единицы PL/SQL . Если этой единицей является анонимный блок, то $$PLSQL_UNIT_ OWNER содержит NULL .
- $$PLSQL_UNIT_TYPE ( Oracle Database 12c ) — тип текущей программной единицы PL/ SQL : ANONYMOUS BLOCK , FUNCTION , PACKAGE , PACKAGE BODY , PROCEDURE , TRIGGER , TYPE или TYPE BODY . В анонимных блоках или триггерах, не являющихся триггерами DML , $$plsql_unit_type имеет значение ANONYMOUS BLOCK .
Для получения текущих номеров строк можно использовать встроенные функции DBMS_UTILITY.FORMAT_CALL_STACK и DBMS_UTILITY.FORMAT_ERROR_BACKTRACE , но тогда вам придется разбирать эти строки для получения имени строки и имени программной единицы. Директивы получения информации позволяют проще получить нужные сведения. Пример:
Использование последних двух директив продемонстрировано в файле cc_line_unit.sql на сайте книги.
Обратите внимание: если директива $$PLSQL_UNIT находится внутри пакета, то она возвращает имя пакета, а не имя отдельной процедуры или функции в пакете.
Использование параметра PLSQL_CCFLAGS
Oracle также предоставляет параметр PLSQL_CCFLAGS , который может использоваться с условной компиляцией. По сути он позволяет определять пары «имя/значение», причем имя в дальнейшем может использоваться в директивах получения информации в логике условной компиляции. Пример:
Имя флага представляет собой любой допустимый идентификатор PL/SQL , включая зарезервированные и ключевые слова (перед идентификатором ставится префикс $$ , исключающий путаницу с обычным кодом PL/SQL ). Имени может присваиваться один из следующих литералов: TRUE , FALSE , NULL или PLS_INTEGER .
Значение PLSQL_CCFLAGS будет ассоциироваться с каждой программой, компилируемой в этом сеансе. Если вы хотите сохранить свои настройки в программе, то будущие компиляции командой alter. compile должны включать секцию REUSE SETTINGS .
Так как вы можете изменить значение параметра, а затем откомпилировать выбранные программные единицы, это позволяет легко определять разные наборы директив получения информации для разных программ.
Обратите внимание: директива позволяет ссылаться на флаги, не определенные в PLSQL_ CCFLAGS ; результат будет равен NULL . Если включить предупреждения компилятора, при ссылке на неопределенный флаг база данных вернет предупреждение PLW-06003 .
PLW-07204: преобразование типа столбца может привести к построению неоптимального плана запроса
Предупреждение выдается при вызове команд SQL из PL/SQL, при котором происходят неявные преобразования. Пример:
С этим предупреждением тесно связано предупреждение PLW-7202 (тип передаваемого параметра приводит к преобразованию типа столбца).
4 ответы
Не то, чтобы я в курсе. Как вы можете скомпилировать что-то с несуществующими объектами? Oracle не знает, ошибочно ли вы ввели имя таблицы, пытаясь сослаться на существующую таблицу, или надеетесь создать таблицу позже.
Почему бы сначала не создать свои таблицы, а затем создать / скомпилировать свои пакеты?
Я пытаюсь перенести встроенный SQL для внутреннего инструмента в хранимые процедуры Oracle. Инструмент создает множество таблиц, поскольку он преобразует данные и выполняет вычисления. Таким образом, будет много хранимых процедур, которые просто создают таблицы, а некоторые будут извлекать данные из этих таблиц. Ожидание того, что таблицы, используемые хранимыми процедурами, существуют до создания хранимых процедур, кажется немного ограничивающим, нет? - тундал45
Думаю, вы можете возразить, что это ограничение, но в то же время идея компиляции состоит в том, чтобы проверить синтаксис и убедиться, что все будет работать так, как написано. Один из способов, который я не рекомендую, - использовать EXECUTE IMMEDIATE. Например: EXECUTE IMMEDIATE 'SELECT x, y FROM table_which_is_hopefully_here WHERE blah =?' ИСПОЛЬЗОВАНИЕ myBlahVariable; Не зная вашего точного варианта использования, помогут ли временные таблицы? - гудокгудокгудок
Для некоторых промежуточных таблиц я все равно собирался использовать GLOBAL TEMPORARY таблицы, но я чувствую, что эта ситуация почти требует этого. Кажется, мне придется пойти по этому пути и создать пустые оболочки для таблиц, которые я хочу сохранить. Спасибо! - тундал45
Вообще говоря, вы не должны создавать и удалять таблицы в PL / SQL. Oracle лучше работает с постоянными структурами данных (и надлежащим образом собранной статистикой). - Гэри Майерс
Вы можете создать пакет заранее (это означает, что он будет существовать как объект в базе данных), но он будет помечен Oracle как недопустимый. Oracle попытается перекомпилировать объект при первом обращении к нему, поэтому, если ваши таблицы существуют в это время, все будет в порядке.
Однако вы можете столкнуться с проблемами, когда зависимости находятся на более чем одном уровне - Oracle не будет проникать в цепочку зависимостей, чтобы перекомпилировать все необходимые недопустимые объекты, а сброс состояния пакета путем перекомпиляции может вызвать проблему, если предыдущий состояние использовалось другим пакетом.
@dpbradley и @redcayuga: определенно кажется ограничивающим условием. Я собирался сказать, что SQL Server позволяет компилировать хранимые процедуры без существующих таблиц, но я не уверен, что это так. Есть ли способ программно попросить перекомпилировать пакеты? - тундал45
@ tundal45 - вы можете программно перекомпилировать с помощью "EXECUTE IMMEDIATE . " стандартного оператора компиляции, но, очевидно, не из самого пакета. Создание таблиц в пакетах - не обычная практика в Oracle по многим причинам, которые, вероятно, выходят за рамки этого вопроса. - дпбрэдли
Все объекты, на которые есть ссылки, включая таблицы и представления, должны существовать при компиляции пакета. Если таблица будет изменена или удалена, пакет станет недействительным, и его придется перекомпилировать. Поэтому сначала создайте таблицу в схеме ашиша.
Это верно даже для пакетов «AUTHID CURRENT_USER». Это забавная ситуация, потому что, когда процедура в пакете запускается, процедура будет искать таблицу в схеме вызывающего, а не в схеме владельца пакета, поэтому таблица может не существовать в этой схеме, и Oracle вызовет исключение времени выполнения. Но владелец пакета может его скомпилировать, потому что таблица существует в его схеме. Забавно, но так оно и есть.
У нас есть пакет, который является зависимостью другого пакета, иногда внесение изменений в "родительский" пакет приводит к тому, что зависимый пакет становится недействительным, но иногда это не так.
это застало нас врасплох раньше.
было бы очень полезно просто понять, что вызывает недействительность, чтобы я мог предсказать/спланировать ее.
изменение любого объекта, на который опирается пакет (например, таблицы, представления, триггеры, другие пакеты), автоматически помечает пакет как недопустимый. Как отмечает tuinstoel выше, Oracle достаточно умен, чтобы перекомпилировать пакет при его первом использовании.
или вы можете запросить следующую таблицу, чтобы узнать, какие зависимости у вас есть
Это покажет все зависимости. Для ваших объектов запросите user_dependencies.
кстати, если я полностью ошибаюсь в ситуации. заранее извиняюсь
Не уверен, каковы последствия этого.
что-то сломалось в производстве?
Что именно произошло?
опять же я подозреваю, что это так, и если да, давайте просто разберемся с этим вместо списка изменений, которые, как я поставил в комментарии, специфичны для версии. (Например, 11g отслеживает зависимость до столбца таблицы вместо таблицы в целом).
Это не может показаться вам важной ошибкой если вы не используете пакет государство. Если бы это было так, это была бы важная ошибка, и вы бы не удивились, так что, полагаю, это не так.
Так как вы не эта ошибка нормально игнорировать. Поскольку вы можете безопасно игнорировать его, вы можете закодировать клиентское приложение, чтобы игнорировать эту ошибку и повторить вызов, потому что, как указывали другие, Oracle перекомпилирует ваш пакет для вас. Это полезное упражнение. Потому что вместо того, чтобы знать все возможные вещи, вам нужно беспокоиться о том, когда вы делаете изменения, и затем в экстренном исправлении вы забудете один из них, ваше приложение просто справится с ним и двинется дальше, не волнуясь.
Я согласен с Томасом Джонсом-низким, однако есть еще пара вопросов, связанных с длинными сессиями и перекомпиляцией.
Если вы ссылаетесь на пакет в сеансе, и этот пакет (или зависимый пакет) перекомпилируется во время того же сеанса, вы получите ошибку oracle "ORA-06508: PL/SQL: не удалось найти программный блок, который называется"
Как только вы ссылаетесь на пакет в сеансе, вы обычно не можете изменить пакет, не сделав его недействительным для этого сеанса. Это особая проблема для сред разработки, где пакеты часто меняются, но также проблема для производственных сред, где вы хотите сделать небольшой патч, не снимая всю среду. Обратите внимание, что эта ошибка возникает даже при отсутствии ошибок в измененных пакетах.
в дополнение к ответу Томаса Джонса-Лоу, если вы только измените тело пакета, зависимый объект не может быть помечен как недопустимый.
однако, как только вы измените спецификацию пакета, это обязательно произойдет.
Если попытаться выполнить недопустимый пакет Oracle, Oracle попытается его скомпилировать. Только если он остается недействительным после компиляции Oracle выдаст исключение.
PLW-05005: функция возвращает управление без значения
Очень полезное предупреждение — функция, не возвращающая значение, явно очень плохо спроектирована. Это одно из предупреждений, которые я бы рекомендовал интерпретировать как ошибку (синтаксис « ERROR:5005 ») в настройках PLSQL_WARNINGS . Мы уже рассматривали один пример такой функции: no_return. Тот код был тривиальным; во всем исполняемом разделе не было ни одной команды RETURN . Конечно, код может быть и более сложным. Тот факт, что команда RETURN не выполняется, может быть скрыт за завесой сложной условной логики.
Впрочем, по крайней мере иногда в подобных ситуациях база данных способна обнаружить проблему, как в следующей программе:
База данных обнаружила логическую ветвь, не приводящую к выполнению RETURN , поэтому для программы выдается предупреждение. Файл plw5005.sql на сайте github содержит более сложную условную логику, которая демонстрирует, что предупреждение выдается и в более сложных программных структурах.
Включение предупреждений компилятора PL/SQL
Oracle позволяет включать и отключать предупреждения компилятора, а также указывать, какие виды предупреждений представляют интерес. Предупреждения делятся на три категории:
- Критичные — ситуации, которые могут привести к неожиданному поведению или получению неверных результатов (как, например, проблемы с псевдонимами параметров).
- Производительные — ситуации, способные вызвать проблемы с производительностью (например, указание значения VARCHAR2 для столбца NUMBER в команде UPDATE ).
- Информационные — ситуации, не влияющие на производительность или правильность выполнения кода, но которые стоит изменить ради того, чтобы упростить сопровождение.
Oracle позволяет включать и отключать предупреждения конкретной категории, всех категорий и даже конкретные предупреждения. Для этого используется команда ALTER DDL или встроенный пакет DBMS_WARNING .
Следующая команда включает предупреждения компиляции для системы в целом:
А следующая команда, например, включает предупреждения в вашей системе для всех категорий:
Это значение особенно полезно во время разработки, потому что оно позволит обнаружить наибольшее количество потенциальных проблем в вашем коде.
Чтобы включить предупреждения в сеансе только для критичных проблем, введите следующую команду:
А для изменения настройки предупреждений компилятора для конкретной, уже откомпилированной программы вводится команда следующего вида:
Обязательно включите секцию REUSE SETTINGS , чтобы команда ALTER не влияла на все остальные настройки (например, уровень оптимизации).
Объединяя разные параметры, можно уточнять настройки с очень высоким уровнем детализации. Допустим, я хочу знать обо всех проблемах, относящихся к производительности, на данный момент не желаю отвлекаться на серверные проблемы, а предупреждение PLW-05005 (выход из функции без RETURN ) должно рассматриваться как ошибка компиляции. Для этого вводится следующая команда:
Особенно полезна возможность интерпретации предупреждений как ошибок. Возьмем предупреждение PLW-05005; если оставить его без внимания при компиляции функции no_return (см. ниже), программа откомпилируется, и я смогу использовать ее в приложении:
Если теперь изменить интерпретацию ошибки приведенной выше командой ALTER SESSION и перекомпилировать no_return , компилятор немедленно остановит попытку:
Кстати говоря, настройки также можно изменить только для конкретной программы и пометить предупреждение как ошибку командой следующего вида:
Во всех этих разновидностях команды ALTER ключевое слово ALL может использоваться как простое и удобное обозначение всех категорий предупреждений:
Oracle также предоставляет пакет DBMS_WARNING со сходными возможностями по установке и изменению параметров компиляции через PL/SQL API . В отличие от команды ALTER, DBMS_WARNING позволяет вносить изменения в конфигурацию тех предупреждений, которые вас интересуют, оставляя другие без изменений. Также после завершения работы можно легко восстановить исходные настройки.
Пакет DBMS_WARNING проектировался для использования в установочных сценариях, в которых может возникнуть необходимость отключения некоторых предупреждений или интерпретации предупреждения как ошибки для отдельных компилируемых программ. Может оказаться, что некоторые сценарии (внешние по отношению к тем, за которые вы отвечаете) вам неподконтрольны. Автор каждого сценария должен иметь возможность задать нужную конфигурацию предупреждений, наследуя более широкий спектр настроек из глобальной области действия.
PLW-05003: фактический параметр с IN и NOCOPY может иметь побочные эффекты
Используя NOCOPY с параметром IN OUT , вы приказываете PL/SQL передавать аргумент по ссылке, а не по значению. Это означает, что любые изменения в аргументе вносятся непосредственно в переменную во внешней области действия. С другой стороны, при передаче «по значению» (ключевое слово NOCOPY отсутствует, или компилятор игнорирует рекомендацию NOCOPY ) изменения вносятся в локальную копию параметра IN OUT . Когда программа завершается, изменения копируются в фактический параметр. (Если произойдет ошибка, измененные значения не копируются в фактический параметр.) Рекомендация NOCOPY повышает вероятность совмещения имен аргументов, то есть ссылки на один блок памяти по двум разным именам.
Совмещение имен усложняет понимание и отладку кода; предупреждение компилятора, выявляющее эту ситуацию, будет чрезвычайно полезным.
Возьмем следующую программу:
Программа достаточно проста: передаются три строки, две из которых объявлены как IN OUT ; аргументам IN OUT присваиваются значения; после каждого присваивания выводится значение первого аргумента IN . Теперь я запускаю процедуру и передаю одну локальную переменную во всех трех параметрах:
Хотя процедура very_confusing продолжает выполняться, присваивание arg2 не отражается на значении аргумента arg1 . Однако когда значение присваивается arg3 , значение arg1 (аргумент IN) заменяется на « Third value »! Более того, при завершении very_confusing присваивание arg2 было применено к переменной str . Таким образом, при возврате управления во внешний блок переменной str присваивается значение « Second value », фактически заменяющее результат присваивания « Third value ».
Как говорилось ранее, совмещение имен параметров может порождать очень запутанные ситуации. Если включить предупреждения компилятора, в таких программах, как plw5003, могут быть выявлены потенциальные проблемы совмещения имен:
Применение директив получения информации для определения конфигурации конкретных программ
Пакетные константы удобны для координации настроек между несколькими программными единицами. Директивы получения информации лучше подходят в ситуациях, когда требуется применить разные настройки для разных программ.
После того как программа будет откомпилирована с некоторым набором значений, она сохраняет эти значения до следующей компиляции (из файла или простой перекомпиляции командой ALTER. COMPILE ). Кроме того, программа будет заведомо перекомпилирована с тем же исходным кодом, который был выбран при предыдущей компиляции, если истинны все следующие условия:
- Ни одна из директив условной компиляции не ссылается на пакетные константы (а только на директивы получения информации).
- При перекомпиляции программы используется секция REUSE SETTINGS , а параметр PLSQL_CCFLAGS не включен в команду ALTER. COMPILE .
Данная возможность продемонстрирована в сценарии cc_reuse_settings.sql (на сайте книги); результат выполнения этого сценария приведен ниже. Сначала я присваиваю app_debug значение TRUE , а затем перекомпилирую программу с этим значением. Запрос к USER_PLSQL_OBJECT_SETTlNGS показывает, что это значение теперь связано с программной единицей:
Далее я изменяю настройки сеанса так, чтобы вычисление $$app_debug давало FALSE . Новая программа компилируется с этой настройкой:
Затем существующая программа перекомпилируется c секцией REUSE SETTINGS :
Запрос к представлению словаря данных показывает, что теперь в разных программах используются разные настройки:
Проблема, с которой я столкнулся, заключается в том, что мой пакет не будет компилироваться, потому что таблица в настоящее время не существует. Конечно, я должен иметь возможность заранее создавать хранимые процедуры для таблиц, которых в настоящее время не существует в Oracle, верно? Я здесь что-то не так делаю?
Версия сервера - Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bit.
задан 30 июн '10, 14:06
Раньше, когда я публиковал вопрос, в котором люди думали, что вопрос необходимо отредактировать по какой-то причине, мне задавали противоположный вопрос: почему нет этой вики сообщества? Так что с тех пор я просто проверяю вики сообщества по всем своим вопросам. Я хотел бы знать, когда вопрос должен или не должен быть вики сообщества. - tundal45
Насколько я понимаю, вики - хорошее место для вопросов, на которые нет ответа «серебряной пули» (стиль программирования, передовой опыт, личные предпочтения, первая написанная вами программа и т. Д. Темы, обсуждение которых может продолжаться и продолжаться - никогда не прийти к выводу, что действительно может быть "принятым" решением проблемы). - eyescream
Спасибо @eyescream. Я буду более осторожен в определении того, когда мне следует и не следует проверять опцию вики сообщества. - tundal45
PLW-05004: идентификатор также объявлен в пакете STANDARD или является встроенным в SQL
Многие разработчики PL/SQL не знают о пакете STANDARD и его влиянии на код PL/ SQL . Например, многие программисты считают, что такие имена, как INTEGER и TO_CHAR , являются зарезервированными словами языка PL/SQL . Однако на самом деле это тип данных и функция, объявленные в пакете STANDARD .
standard — один из двух пакетов по умолчанию в PL/SQL (другой — DBMS_STANDARD ). Поскольку STANDARD является пакетом по умолчанию, вам не нужно уточнять ссылки на такие типы данных, как INTEGER, NUMBER, PLS_INTEGER и т. д., именем STANDARD — хотя при желании это можно сделать.
Предупреждение PLW-5004 сообщает об объявлении идентификатора с таким же именем, как у элемента STANDARD (или встроенным именем SQL; многие встроенные имена, хотя и не все, объявляются в STANDARD ).
Рассмотрим эту процедуру:
Для этой процедуры компилятор выводит следующие предупреждения:
Старайтесь избегать использования имен элементов, определенных в пакете STANDARD , если только у вас нет для этого очень веских причин.
PLW-06002: недостижимый код
База данных Oracle теперь умеет проводить статический анализ программы для выявления строк кода, которые ни при каких условиях не получат управление во время выполнения. Это исключительно ценная информация, но иногда компилятор предупреждает о наличии проблемы в строках, которые на первый взгляд недостижимыми вовсе не являются. Более того, в описании действий, предпринимаемых для этой ошибки, говорится, что «предупреждение следует отключить, если большой объем кода был сделан недостижимым намеренно, а предупреждение приносит больше раздражения, чем пользы».
Пример такого предупреждения приводился ранее в разделе «Пример». Теперь рассмотрим следующий код:
В Oracle Database 10g и выше для этой программы выдаются следующие предупреждения:
Понятно, почему строка 7 помечена как недостижимая: l_checking присваивается значение FALSE , поэтому строка 7 выполняться не будет. Но почему строка 5 помечена как недостижимая? На первый взгляд этот код будет выполняться всегда! Более того, строка 13 тоже должна выполняться всегда, потому что GOTO передает управление этой строке по метке. И все же эта строка тоже помечена как недостижимая.
Такое поведение объясняется тем, что до выхода Oracle Database 11g предупреждение о недостижимости кода генерируется после его оптимизации. В Oracle Database 11g и выше анализ недостижимого кода стал намного более понятным и полезным.
Компилятор не вводит вас в заблуждение; говоря, что строка N недостижима, он сообщает, что она никогда не будет выполняться в соответствии со структурой оптимизированного кода.
Некоторые ситуации с недостижимым кодом не обнаруживаются компилятором. Пример:
Разумеется, вызов DBMS_OUTPUT.PUT_LINE недостижим, но в настоящее время компилятор не обнаруживает это обстоятельство — до версии 12.1.
Пример
Очень полезное предупреждением компилятора PLW-06002 сообщает о наличии недостижимого кода. Рассмотрим следующую программу. Так как переменная salary инициализируется значением 10 000, условная команда всегда будет отправлять меня на строку 9. Строка 7 выполняться не будет:
Если откомпилировать этот код в любой версии до Oracle Database 10g , компилятор просто сообщит о том, что процедура создана. Но если включить предупреждения компиляции в сеансе этой или более поздней версии, то при попытке откомпилировать процедуру будет получен следующий ответ от компилятора:
С этим предупреждением я могу вернуться к указанной строке, определить, почему она недостижима, и внести необходимые исправления.
Некоторые полезные предупреждения PL/SQL
В следующих разделах я представлю небольшую подборку предупреждений, реализованных Oracle , — с примерами кода, для которого они выдаются, и описаниями особенно интересного поведения.
Чтобы просмотреть полный список предупреждений для любой конкретной версии Oracle , найдите раздел PLW в книге "Error Messages" документации Oracle.
PLW-05000: несовпадение в NOCOPY между спецификацией и телом
Рекомендация NOCOPY сообщает базе данных Oracle, что вы, если это возможно, предпочли бы не создавать копии аргументов IN OUT . Отказ от копирования может повысить производительность программ, передающих большие структуры данных — например, коллекции или CLOB .
Рекомендация NOCOPY должна быть включена как в спецификацию, так и в тело программы (актуально для пакетов и объектных типов). Если рекомендация не присутствует в обоих местах, база данных применяет настройку, указанную в спецификации.
Пример кода, генерирующего это предупреждение:
Предупреждения компилятора отображаются в следующем виде:
PLW-06009: обработчик OTHERS не завершается вызовом RAISE или RAISE_APPLICATION_ERROR
Это предупреждение (добавленное в Oracle Database 11g) выводится тогда, когда в обработчике исключений OTHERS не выполняется та или иная форма RAISE (повторное инициирование того же исключения или инициирование другого исключения), и не вызывается RAISE_APPLICATI0N_ERR0R . Другими словами, существует большая вероятность того, что программа «поглощает» исключение и игнорирует его. Ситуации, в которых ошибки действительно должны игнорироваться, встречаются довольно редко. Чаще исключение должно передаваться во внешний блок:
Механизм условной компиляции, появившийся в Oracle Database 10g Release 2 , позволяет организовать избирательную компиляцию частей программы в зависимости от условий, заданных в директиве $IF .
Например, условная компиляция будет очень полезна, если вам потребуется:
- Написать программу, которая работает в разных версиях Oracle и использует специфические возможности этих версий. А конкретнее — если вы хотите использовать новые возможности Oracle там, где они доступны, но при этом ваша программа также должна компилироваться и работать в старых версиях. Без условной компиляции вам придется вести несколько разных файлов или использовать сложную логику подстановки переменных в SQL*Plus .
- Выполнять некоторый код во время тестирования и отладки, но пропускать его в окончательной версии. До появления условной компиляции вам пришлось бы либо закрывать нежелательные строки комментариями, либо добавлять лишние проверки в логику приложения — даже в окончательную версию.
- Устанавливать/компилировать разные элементы приложения в зависимости от требований (например, набора компонентов, на которые у пользователя имеется лицензия). Условная компиляция очень сильно упрощает ведение сложной кодовой базы. Условная компиляция реализуется посредством включения директив компилятора в исходный код. При компиляции программы препроцессор PL/SQL обрабатывает директивы и выбирает те части кода, которые требуется откомпилировать. «Урезанный» исходный код передается компилятору для дальнейшей обработки.
Директивы делятся на три типа:
- Директивы выбора — директива $IF проверяет выражение и определяет, какой код следует включить в обработку (или исключить из нее).
- Директивы получения информации — синтаксис $$identifier используется для обращения к флагам условной компиляции. К директивам получения информации можно обращаться в директиве $IF или использовать их независимо в коде.
- Директивы ошибок — директива $ERROR используется для вывода ошибок компиляции на основании условий, вычисленных при подготовке кода препроцессором. Сначала я приведу несколько примеров, а затем мы более подробно рассмотрим каждую директиву. Вы также научитесь пользоваться двумя пакетами, связанными с условной компиляцией: DBMS_DB_VERSION и DBMS_PREPROCESSOR.
PLW-07203: рекомендация NOCOPY может принести пользу в объявлении параметра
Как упоминалось ранее в отношении PLW-05005 , использование NOCOPY для сложных, больших параметров IN OUT может улучшить производительность программ в некоторых условиях. Это предупреждение выдается для программ, у которых включение NOCOPY для параметров IN OUT может повысить эффективность выполнения. Пример такой программы:
Это еще одно предупреждение, которое будет генерироваться во многих программах и вскоре начнет раздражать. Безусловно, предупреждение вполне справедливо, но в большинстве случаев последствия такой оптимизации останутся незамеченными. Более того, вряд ли вам удастся переключиться на NOCOPY без внесения изменений для обработки ситуаций с аварийным завершением программы, при котором данные остаются в неопределенном состоянии.
Читайте также: