FB3. PACKAGES

Привет всем.

В Firebird v3, как я тут уже упоминал, появилась новая штука — PACKAGE. В PACKAGE упаковываются функции и процедуры. Эта головная боль для разработчиков компонент доступа и средств администрирования БД была оплачена в рамках «5th Brazilian Firebird Developers Day». Пропил Осваивал эти средства, по всей видимости, Adriano dos Santos Fernandes — автор реализации поддержки PACKAGE на уровне сервера.

PACKAGE состоит из двух частей — заголовок и тело. Сначала определяется заголовок, который можно рассматривать как интерфейс PACKAGE, а потом определяется его тело. Синтаксис DDL запросов для управления пакетами можно посмотреть в README.packages.txt. Здесь вкратце:

К заголовку PACKAGE применяются DDL запросы вида:

  1. CREATE PACKAGE <name> …
  2. CREATE OR ALTER PACKAGE <name> …
  3. ALTER PACKAGE <name> …
  4. RECREATE PACKAGE <name> …
  5. DROP PACKAGE <name> …

К телу PACKAGE применяются DDL-запросы

  1. CREATE PACKAGE BODY <name> …
  2. RECREATE PACKAGE BODY <name> …
  3. 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>. Например:

  1. «execute procedure pack1.proc1»
  2. «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"

Вывод
Output

One Comment

Kovalenko  on 19 сентября, 2013

Произошли небольшие изменения в синтаксисе для «PACKAGE BODY».

Теперь его элементы (определения процедур и функций) не надо разделять точками с запятой.

Номер сборки IBProvider’а (v3), которая понимает новый синтаксис — 15362.

Leave a Comment