Заморочки от Oracle, или знать бы, где упасть…Владимир Пржиялковский
"...да вот веревкой хочу море морщить Оглавление
ВведениеБольшинство специалистов склоняется к тому, что СУБД Oracle представляет собой наиболее полноценную и эффективную SQL-машину среди прочих. К сожалению, это не означает автоматически, что Oracle безгрешна. С одной стороны, работа с Oracle нередко доставляет истинное удовольствие (особенно любителям найти красивое решение), но с другой - разочарование и раздражение. Эта заметка продолжает серию статей о возможностях Oracle, но выбор самих возможностей необычен. Речь пойдет о неудобных или даже неприятных возможностях - хотя бы и потенциально неприятных. Признаться, некоторые колебания в целесообразности подобной публикации у меня были, но ведь многие читатели доверяют СУБД Oracle самое святое - бизнес, и поэтому знать, откуда ждать беду они просто обязаны. В более безобидном варианте знание некоторых "заморочек от Oracle" способно улучшить ваше понимание этой системы. Рекурсивные вызовыPL/SQL в Oracle относится к языкам, в которых рекурсивные вызовы подпрограмм разрешены . В теле одной подпрограммы можно обратиться к самой себе, или же, например, к другой, а та, в свою очередь к первой. Рекурсия иногда удобна. Пример из учебников - вычисление факториала. Более жизненный пример - размечивание весами древовидной структуры, позволяющее организовать быстрый доступ к хранимому в БД справочнику без привлечения нестандартных и несовместимых конструкций (в Oracle это конструкция CONNECT BY). Особенность в том, что машина PL/SQL в СУБД Oracle никак не регламентирует глубину повторных обращений, что оставляет лазейку "бесконечного" зацикливания. Ответственность за его возникновение СУБД перекладывает на программистов. Хуже того, организовать такое зацикливание может любой пользователь, обладающий всего только-то привилегией CREATE SESSION:
Прежде чем двигаться дальше, удостоверьтесь, что вы готовы перезагрузить свою БД.
Дальнейшее лучше наблюдать какой-нибудь программой ОС, показывающей использование процессорного времени и оперативной памяти. Во-первых, сеанс пользователя ADAM начинает жадно расходовать процессорное время. Другие сеансы связи с СУБД это сразу почувствуют. Во-вторых, сеанс пользователя ADAM начинает неумолимо захватывать оперативную память во все больших и больших количествах. Пусть не сразу, но другие сеансы связи с СУБД почувствуют и это. Если ваша база загружена какой-то реальной работой, проделанная простая операция - эффективный способ дезорганизовать эту работу. У меня ни разу не хватило терпения дождаться, когда Oracle исчерпает всю оперативную память, и проверить его способность самому переварить проблему. Но если не предпринять заблаговременных мер по нейтрализации подобных диверсий, даже естественная попытка убить вредоносный сеанс не будет простой. Сначала из-за страшно медленной реакции СУБД на ваши действия от имени SYS, а потом из-за страшно медленного освобождения памяти после команды ALTER SYSTEM KILL SESSION. Вопрос, стоило ли разработчикам Oracle оставлять возможность неограниченной рекурсии, способен разжечь спор. Но сделано то, что сделано: Oracle награждает нас здесь одновременно со свободой действий и риском потери нормальной работы СУБД. Как сделать функцию невидимойСледующий пример показывает архитектурное упущение создателей Oracle. Оно обязано одному из недальновидных решений, принятых в первых версиях Oracle, очевидно в угоду скорости утверждения на рынке. Позже, подобно некоторым другим сходным в этом отношении "ранним" решениям Oracle, исправить положение дел уже не представилось возможным. К счастью, проблема неожидаемой маскировки объекта в БД, о которой пойдет речь, довольно специфична. Однако ж она вполне реальна. Рассмотрим пример функции в схеме SCOTT:
Это не запрещено, несмотря на наличия поля ENAME в одной из таблиц схемы, так как пространства имен функций и столбцов не пересекаются (и правильно!). Но что произойдет, если к ней обратиться при выборке данных из таблицы EMP? Если выполнить следующий запрос, что мы увидим: столбец или функцию?
Правильный ответ дает документация, где в одном из бесчисленных закоулков перечислена последовательность обработки имен объектов в предложениях SQL. Однако в данном случае гораздо проще изматывающего поиска в документации поставить эксперимент и убедиться, что по принципу "своя рубашка ближе к телу" СУБД выдаст значения столбца таблицы. Закономерно возникает вопрос: а как при обращении к EMP выдать значение функции ENAME? Несложные эксперименты приводят к необходимости указать полное имя функции, снабдив его именем схемы:
Или же (что в некоторых смыслах более правильно)
Крайне неудачная система обозначений (в SCOTT.ENAME SCOTT - это имя схемы, а в такой же по виду записи EMP.ENAME EMP - это имя таблицы), но свое дело она делает. К сожалению, этим дело не кончается. Пространства имен схем и пакетов тоже разные, что тоже правильно. Рассмотрим теперь следующее:
Убедитесь, что получим 'I am a package function'. Но теперь, обращаясь к EMP мы никогда не увидим результат самостоятельной (не пакетированной) функции ENAME! Если в вашем приложении были запросы типа SELECT scott.ename FROM emp, то после создания пакета они начнут выдавать попросту другой ответ. Способ формирования составных имен не идеален и в других языках. Например, даже в таком архитектурно продуманном языке, как Java, понять по тексту смысл каждой компоненты в имени java.lang.System.out.println без дополнительной информации невозможно. Но другой системы, кроме Oracle, где допускалось бы исчезновение видимости имени одного объекта вследствие вполне законного заведения других, мне неизвестно. Имена объектов, ключевые и зарезервированные словаИстория этой задачки началась с форума Oracle на www.sql.ru, куда я однажды случайно забрел. Один из участников форума пытался перенести в Oracle схему БД, подготовленную в другой системе. В таблице было поле, названное NUMBER. Очевидно, в нем хранилось какое-то число; с этим-то полем в Oracle и случилась незадача. Ее раскрывает следующий (уже мой) пример:
Автор вопроса правильно сообразил, что NUMBER - это зарезервированное в Oracle слово (мы помним, что перед обработкой предложения SQL Oracle повышает регистр букв в именах объектов), и ошибка возникает при синтаксическом разборе. В документации про ошибку ORA-00904 сказано: "… [column name] may not be a reserved word". Раз уж говорим о заморочках, обратите внимание на текст сообщения: между двумя двоеточиями по замыслу разработчиков должно стоять то самое неправильное имя, а в нашем случае оно оказалось настолько неправильным, что не попало в результат синтаксического анализатора! Не самый большой грех в Oracle, но ведь по теме статьи. Неужели перенося базу в Oracle придется править схему, а значит и приложение? Если не выход, то лазейку из положения автор вопроса в форум нашел правильно. Заключив имя объекта в Oracle в двойные кавычки, мы приобретем право использовать абсолютно любые доступные в кодировке символы, хоть звездочки, хоть точки, хоть пробелы:
Если смириться с неудобством двойных кавычек, это вполне рабочий вариант:
Беда в том, что точно та же команда UPDATE, равно как и любая другая, обращающаяся к столбцу "NUMBER", не работает в PL/SQL:
Это и вызвало недоумение автора вопроса в форум. В Oracle уверяют, что, начиная с версии 9 (которая помогала готовить эту статью) обработка запросов в SQL и в PL/SQL ведется одним и тем же модулем СУБД. И тем не менее факт налицо: SQL терпит обращение к полю "NUMBER", а PL/SQL - нет. Заинтересовавшись, после серии экспериментов я нащупал очередную уступку Oracle, позволившую достичь компромисса, но сообщить найденное решение автору вопроса уже не смог, так как sql.ru оказался для меня слишком сложно устроен: он не только требует регистрации (а придуманные себе кличку и пароль я назавтра же забыл), но и содержит чересчур много "нитей", среди которых "свою" я безнадежно потерял. Вот что выяснилось: PL/SQL начинает все обрабатывать правильно, как только в названии столбца появляется хотя бы одна малая буква, например, не "NUMBER", а "NuMBER". Лучше, конечно, "number". Возникает вопрос: в чем причина такого странного поведения? Переписка с разработчиками PL/SQL несколько прояснила ситуацию. Можно посочувствовать: им приходится расплачиваться по счетам самой логики жизни. В любом языке есть множество зарезервированных слов, которые нельзя использовать для имен объектов. В Oracle первую очередь это ключевые слова: BEGIN, SELECT и другие. Заметьте кстати, что множества зарезервированных слов Oracle в SQL и PL/SQL не совпадают:
(Но, конечно же:
) Однако жизнь бывает сложнее, чем иногда хотелось бы. Вопреки утверждению документации по Oracle (сказали разработчики PL/SQL) не все ключевые слова являются зарезервированными. Вызвано это тем, что язык живет, и в нем могут появляться новые ключевые слова, которые кто-то, когда они еще не были ключевыми, мог использовать для названий объектов. Запрет вдруг их такового употребления способен был бы вызвать возмущение пользователей, принужденных переделывать имеющиеся БД и приложения. Отсюда и отклонения от идеального равенства "ключевые слова" = "зарезервированные слова". Наглядный пример: до версии 9 в Oracle не было типа TIMESTAMP, и многие использовали это слово для именования поля в таблице. В версии 9 эту вольность (по законам новой версии) употребления пришлось оставить, хотя и удалось это не совсем последовательно:
но, правда
Есть в Oracle и другое правило, уже наблюдавшееся выше по тексту: и в SQL, и в PL/SQL заключение имени объекта в двойные кавычки сравнение со списком зарезервированных слов отменяет:
Это и объясняет срабатывание примера, с которого начался этот раздел, при замене "NUMBER" вместо NUMBER. Все это можно понять. А остался ли за Oracle какой-нибудь грех? Не без того. Разработчики PL/SQL признались: несмотря на то, что с версии 9 обработка SQL в PL/SQL и в качестве самостоятельных команд происходит одинаково, список зарезервированных слов в PL/SQL свой (по-прежнему) собственный, и, как оказалось, не всегда совпадает с аналогичным списком в SQL там, где он бы должен совпадать. Это и привело к непредусмотренному архитекторами Oracle результату при попытке обратиться к полю "NUMBER" в PL/SQL. Иными словами, это ошибка разработчиков, которая сохранилась в нынешней версии 10.1, и которую пообещали исправить в будущем. ЗаключениеПо сути, это заключение является лирическим отступлением, на мой взгляд, способным несколько сгладить то негативное мнение о СУБД Oracle, которое способна породить эта статья. Дело в том, что среди писем от разработчиков, которые я получил, расследуя последнюю описанную проблему, неожиданно оказалось письмо от Кена Джекобса. Напомню, это вице-президент компании Oracle, второй по размеру в области разработки ПО. Конечно, не он дал исчерпывающий ответ, это сделал другой специалист (к слову сказать, тоже не низшей должности), но он обратил внимание на проблему. Сам факт такой реакции мне показался крайне симпатичным и симптоматичным. Представьте себе компанию с оборотами в десятки миллиардов долларов, на втором уровне иерархии управления которой имеются технические специалисты, а не специалисты по рекламе, связям с общественностью и организации денежных потоков! Это так непохоже на некоторые соразмерные Oracle однопрофильные компании и (увы) на все отечественные (за отсутствием таковых, соответственно другого профиля). Конкретно меня такое положение дел побуждает смотреть сквозь пальцы на грехи разработчиков Oracle (по крайней мере, отчасти). И все же: если у вас есть что добавить в приведенную выше коллекцию, буду рад получить письмо. |