FB3. PACKAGES
Привет всем.
В Firebird v3, как я тут уже упоминал, появилась новая штука — PACKAGE. В PACKAGE упаковываются функции и процедуры. Эта головная боль для разработчиков компонент доступа и средств администрирования БД была оплачена в рамках «5th Brazilian Firebird Developers Day». Пропил Осваивал эти средства, по всей видимости, Adriano dos Santos Fernandes — автор реализации поддержки PACKAGE на уровне сервера.
PACKAGE состоит из двух частей — заголовок и тело. Сначала определяется заголовок, который можно рассматривать как интерфейс PACKAGE, а потом определяется его тело. Синтаксис DDL запросов для управления пакетами можно посмотреть в README.packages.txt. Здесь вкратце:
К заголовку PACKAGE применяются DDL запросы вида:
- CREATE PACKAGE <name> …
- CREATE OR ALTER PACKAGE <name> …
- ALTER PACKAGE <name> …
- RECREATE PACKAGE <name> …
- DROP PACKAGE <name> …
К телу PACKAGE применяются DDL-запросы
- CREATE PACKAGE BODY <name> …
- RECREATE PACKAGE BODY <name> …
- DROP PACKAGE BODY <name> …
PACKAGE представляет собой неделимый набор процедур и функций. DDL запросов для определения/модификации/удаления отдельных элементов PACKAGE (процедур и функций) не предусмотрено.
Тело PACKAGE должно содержать определения для всех элементов, перечисленных в заголовке. Плюс внутренние (private) элементы, к которым можно обращаться только в рамках процедур и функций самого PACKAGE.
Последнее, лично я не тестировал — поверил Адриано на слово: » If a routine is not declared on the package header (interface) and is implemented in the body (implementation), it becomes a private routine. A private routine can only be called from inside its package.»
Пакеты, определенные в рамках базы данных FB3 (ODS 12.0), перечисляются в системной таблице RDB$PACKAGES. В системных таблицах RDB$PROCEDURES, RDB$PROCEDURE_PARAMETERS, RDB$FUNCTIONS, RDB$FUNCTION_ARGUMENTS появилась новая колонка RDB$PACKAGE_NAME.
Обращение к процедурам и функциям PACKAGE осуществляется через составное имя — <package_name>.<stored_procedure_name>. Например:
- «execute procedure pack1.proc1»
- «select * from pack1.proc2»
Текущая поддержка PACKAGE со стороны IBProvider (сборка 3.8.2.12723).
Провайдер корректно распознает DDL-запросы, связанные с PACKAGE. Без помощи SET TERM. Это весьма актуально, поскольку провайдер текст любой команды рассматривает как скрипт с SQL-запросами.
В DML запросах «exec SP», «{call SP}» провайдер корректно распознает и обрабатывает свободные процедуры и процедуры определенные в составе пакета. То есть путаницы из-за многократного использования одного и того же имени для разных процедур не возникнет.
В стандартных схемах PROCEDURES, PROCEDURE_PARAMETERS, PROCEDURE_COLUMNS исключены сведения о процедурах и функциях из пакетов. Поскольку в этих схемах нет поддержки для PACKAGE. Попросту говоря — нет колонки PACKAGE_NAME. Есть мысль о том, чтобы завести альтернативные схемы, где эта проблема будет решена. Мысль есть и она думается.
Оставшиеся проблемы. Грубый анализ внутреннего кода, показывает что есть проблема с определением имени домена для параметра хранимой процедуры (функции). То есть, для колонок запроса «select x.out_param1, x.out_param2 from pack.proc x» провайдер не будет учитывать имя пакета (pack). И может некорректно отработать. Надо заметить, что у самого сервера тут тоже пока недоработка — надо бы предоставлять имя пакета для колонок. В XSQLVAR такой возможности нет, значит надо через информационные свойства запроса. А лучше всего предоставлять имя домена, если такое у колонки результирующего множества есть. Провайдер использует имя домена в эмуляторе типов BOOLEAN и GUID. Если эмуляция типов не используется, то проблемы нет.
Пожалуй, я добавлю этот пункт, про package_name, в свою черную книжечку с названием «Предложения по доработке клиентского API в FB3» 🙂
Пример работы с PACKAGE на VBScript (ADO).
option explicit
dim cn
set cn=createobject("ADODB.Connection")
cn.Provider="LCPI.IBProvider.3"
cn.Properties("location")="vxpsp2-fb30:e:\database\ibp_test_fb30_d3.gdb"
cn.Properties("user id")="sysdba"
cn.Properties("password")="masterkey"
cn.Properties("dbclient_library")="fbclient_30.dll"
cn.Properties("ctype")="win1251" ' This is RUSSIAN code page!
cn.Properties("auto_commit")=true
cn.Properties("exec_sp_named_param")=true
call cn.Open("")
wscript.echo "Provider Version: "&cn.Properties("Provider Version").Value
wscript.echo "DBMS Name : "&cn.Properties("DBMS Name").Value
wscript.echo "DBMS Version : "&cn.Properties("DBMS Version").Value
wscript.echo ""
'----------------
dim cmd
dim rs
set cmd=createobject("ADODB.Command")
cmd.ActiveConnection=cn
'----------------
wscript.echo "create packages"
cmd.CommandText = _
"recreate package test_pack1 as"&vbCrLf& _
"begin"&vbCrLf& _
" procedure proc(in1 integer)"&vbCrLf& _
" returns (out1 integer);"&vbCrLf& _
"end;"&vbCrLf& _
""&vbCrLf& _
"recreate package body test_pack1 as"&vbCrLf& _
"begin"&vbCrLf& _
" procedure proc(in1 integer)"&vbCrLf& _
" returns (out1 integer)"&vbCrLf& _
" as"&vbCrLf& _
" begin"&vbCrLf& _
" out1=-in1;"&vbCrLf& _
" end;"&vbCrLf& _
"end;"&vbCrLf& _
""&vbCrLf& _
"recreate package test_pack2 as"&vbCrLf& _
"begin"&vbCrLf& _
" procedure proc(in1_str varchar(64) character set ascii)"&vbCrLf& _
" returns (out1_str varchar(64) character set ascii);"&vbCrLf& _
"end;"&vbCrLf& _
""&vbCrLf& _
"recreate package body test_pack2 as"&vbCrLf& _
"begin"&vbCrLf& _
" procedure proc(in1_str varchar(64) character set ascii)"&vbCrLf& _
" returns (out1_str varchar(64) character set ascii)"&vbCrLf& _
" as"&vbCrLf& _
" begin"&vbCrLf& _
" out1_str=upper(in1_str);"&vbCrLf& _
" suspend;"&vbCrLf& _
" end;"&vbCrLf& _
"end;"&vbCrLf& _
""&vbCrLf& _
"recreate procedure proc"&vbCrLf& _
"returns (x integer)"&vbCrLf& _
"as"&vbCrLf& _
"begin"&vbCrLf& _
"x=1234;"&vbCrLf& _
"end;"
set rs=cmd.Execute()
'check errors
while(not (rs is nothing))
set rs=rs.NextRecordset()
wend
wscript.echo ""
'----------------
wscript.echo "exec test_pack1.proc"
cmd.CommandText="exec test_pack1.proc"
cmd("in1")=2
call cmd.Execute()
wscript.echo "out1: "&cstr(cmd("out1").value)
wscript.echo ""
'----------------
wscript.echo "exec test_pack2.proc"
cmd.CommandText="exec test_pack2.proc"
cmd("in1_str")="qwerty"
set rs=cmd.Execute()
wscript.echo "out1_str: """&rs("out1_str").value&""""
wscript.echo ""
set rs=nothing
'----------------
wscript.echo "exec proc"
cmd.CommandText="exec proc"
call cmd.Parameters.Refresh()
call cmd.Execute()
wscript.echo "x: "&cstr(cmd("x").value)
wscript.echo ""
'----------------
wscript.echo "OK"
Вывод

Kovalenko on 19 сентября, 2013
Произошли небольшие изменения в синтаксисе для «PACKAGE BODY».
Теперь его элементы (определения процедур и функций) не надо разделять точками с запятой.
Номер сборки IBProvider’а (v3), которая понимает новый синтаксис — 15362.