hi all
Предыстория.
Есть достаточно сложный .sh, извлекающий из продакшена DDL всех ХП, триггеров, вьюх, доменов, исключений, генераторов, ролей, привилегий и комментариев к полям таблиц и параметрам ХП.
Извлечение метаданных производится с пом. нескольких ХП (sys_get_xxxx_ddl, где "xxxx" = proc, view, trig, exce, doma, priv и т.д.), которые живут в продакшене и лезут в словарь базы.
В итоге, создаются .sql-файлы, которые содержат тела-пустышки программных объектов, а также (другие) .sql-файлы с заполненными телами.
Далее эти файлы в строго опр. порядке натравливаются на базу назначения (репликант-базу).
Текст процедур sys_get_xxx_ddl также переносятся в базу назначения, как и всех прочих. Никаких ошибок при этом не возникает (отслеживаю размер лога ошибок - он нулевой).
Вопрос: может ли такое быть, что одна из "словарно-парсинговых" процедур (sys_get_xxx_ddl), создаваемых в базе назначения как и все прочие, оказывается в каком-то странном состоянии: пока её не перекомпилишь заново, она вместо результата возвращает ошибку 'malformed string' ?
Вот её текст:
create or alter procedure SYS_GET_EXCE_DDL
returns (
SRC varchar(8192))
AS
begin
-- Extract DDL of EXCEPTIONS to be executed as statements in isql
-- Encloses 'create exception' in execute statement and begin..end blocks
-- to prevent errors during creation (no `recreate` exсeption present now in FB)
for
with
e as(
select xe.rdb$exception_name ex_name, xe.rdb$message ex_msg
from rdb$exceptions xe
where coalesce(xe.rdb$system_flag,0)=0 -- do NOT: is distinct from 1
)
select
case x.rt
when -30 then 'set term ^;'
when -20 then 'execute block as begin'
when -10 then 'if (not exists(select * from rdb$exceptions ex where ex.rdb$exception_name = '''||upper(trim(ex_name))||''')) then'
when 0 then
' execute statement '''
||'create exception '||trim(ex_name)||' '||''''''
||coalesce(replace(ex_msg,'''',''''''),'')||''''''
||';'';'
when 10 then 'when any do begin end'
when 20 then 'end^'
when 30 then 'set term ;^'
end src
from e
join (select -30 rt from rdb$database
union all select -20 from rdb$database
union all select -10 from rdb$database
union all select 0 from rdb$database
union all select 10 from rdb$database
union all select 20 from rdb$database
union all select 30 from rdb$database
) x on 1=1
order by ex_name,rt
into src
do suspend;
end
Результат выборки из этой ХП на продакшене (при нормальном исходе) должен быть таким:
+ |
set term ^; execute block as begin if (not exists(select * from rdb$exceptions ex where ex.rdb$exception_name = 'A_FOR_BREAK')) then execute statement 'create exception A_FOR_BREAK ''Остановить FOR SELECT'';'; when any do begin end end^ set term ;^ set term ^; execute block as begin if (not exists(select * from rdb$exceptions ex where ex.rdb$exception_name = 'DEL_ROLLBACK')) then execute statement 'create exception DEL_ROLLBACK ''Удалить запись нельзя, есть ссылки в других таблицах.'';'; when any do begin end end^ set term ;^ set term ^; execute block as begin if (not exists(select * from rdb$exceptions ex where ex.rdb$exception_name = 'ERROR_VALUTE')) then execute statement 'create exception ERROR_VALUTE ''ВНИМАНИЕ! Нельзя изменить/добавить/удалить курс валюты.'';'; when any do begin end end^ set term ;^ set term ^; execute block as begin if (not exists(select * from rdb$exceptions ex where ex.rdb$exception_name = 'USER_EXCEPTION')) then execute statement 'create exception USER_EXCEPTION '''';'; when any do begin end end^ set term ;^ set term ^; execute block as begin if (not exists(select * from rdb$exceptions ex where ex.rdb$exception_name = 'VAR_EXCEPTION')) then execute statement 'create exception VAR_EXCEPTION ''VARIABLE EXCEPTION'';'; when any do begin end end^ set term ;^ set term ^; execute block as begin if (not exists(select * from rdb$exceptions ex where ex.rdb$exception_name = 'XA_FOR_BREAKE')) then execute statement 'create exception XA_FOR_BREAKE ''ERROR!'';'; when any do begin end end^ set term ;^ set term ^; execute block as begin if (not exists(select * from rdb$exceptions ex where ex.rdb$exception_name = 'XERR_NO_PERIOD')) then execute statement 'create exception XERR_NO_PERIOD ''ERROR!'';'; when any do begin end end^ set term ;^
|
Однако, если делать её вызов из консоли с чарсетом UTF8 и соотв-щим указанием его в -ch ключе:
isql kprobe4upd.fdb -ch utf8 -i get_exc.sql -m | sed -e "s/ \{1,\}$//" > get_exc2.txt
-- то получаем облом на первом же execute statement 'create exception A_FOR_BREAK ''Остановить FOR SELECT'';'; -
Statement failed, SQLSTATE = 42000
Malformed string
-At procedure 'SYS_GET_EXCE_DDL' line: 45, col: 1
After line 1 in file get_exc.sql
В итоге, при переводе базы назначения в "боевой режим" приходится открывать эту ХП в ИБЭ и перекомпилять заново.
Отчего такое может быть ?