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.