External procedures in multi-home environment

Обсуждение возникших проблем, предложений и ошибок ODAC компонентов
Закрыто
Vovick
Сообщения: 1
Зарегистрирован: Ср 16 июл 2014 11:08

External procedures in multi-home environment

Сообщение Vovick » Пн 21 июл 2014 13:47

Здравствуйте.

Тут находится официальная инструкция по написанию внешних процедур с помощью ODAC.

Ниже я привожу пример исходников, который будет работать с несколькими Oracle Home.
Работоспособность проверена в окружении Delphi XE5, ODAC 9.3.9 и двух OracleHome, в одном сервер Оракл 10g, а во втором - клиент Оракл 10g. Оба физически установлены на одном компьютере на разных дисках. Надеюсь, это вам поможет.

Да, чуть не забыл: dll-ку совсем не обязательно укладывать в папку bin - в моем случае она находилась вообще вне папки с установкой Ораклов, хотя и на той же машине. Но вот путь к dll-ке при регистрации библиотеки в Оракл нужно указывать полный.

Код: Выделить всё

library AISDAUtils;

uses
  SysUtils,
  OraCall,
  OraClasses,
  OraError;

{$R *.res}

procedure ExportLob(Context: POCIExtProcContext; Locator: POCILobLocator; Params: PAnsiChar); cdecl;
var
  OraPath: string;
  OraEnv: TOCIEnvironment;
  OraSvcCtx: TOCISvcCtx;
  OraLob: TOraLob;
  I: Integer;
  hEnv: POCIEnv;
  hSvcCtx: POCISvcCtx;
  hError: POCIError;
begin
  // Инициируем OracleHomes.Default на тот OracleHome, из которого был осуществлен вызов процедуры по пути основного процесса, который залинковал мою библиотеку
  OraPath := ExtractFilePath(ParamStr(0));
  for I := 0 to OracleHomes.Count - 1 do
    if Pos(OracleHomes[I].Path, OraPath) = 1 then
    begin
      OracleHomes.Default := OracleHomes[I];
      Break;
    end;

  // Инициируем OracleHomes.Default, с которым далее и будем работать
  if not OracleHomes.Default.Inited then
    OracleHomes.Default.Init;

  try
    // Получаем указатели на окружение, контекст и ошибки
    OracleHomes.Default.OCI8.OCIExtProcGetEnv(Context, hEnv, hSvcCtx, hError);

    // Инициализируем окружение
    OraEnv := OracleHomes.Default.AllocEnvironment(hEnv);

    // Создаем и инициализируем контекст
    OraSvcCtx := TOCISvcCtx.Create(OraEnv, hSvcCtx, OraEnv.UnicodeEnv);
    try
      // ВНИМАНИЕ! В в официальном примере тут в параметре почему-то передается указатель на контекст hSvcCtx, а не на экземпляр класса TOCISvcCtx
      OraLob := TOraLob.Create(OraSvcCtx);

      // Далее все по примеру за исключением того, что при обработке ошибок необходимо приводить сообщение к Ansi-строке
      try
        OraLob.OCILobLocator := Locator;
        OraLob.ReadLob;
        // Далее делаем то, что нам нужно с полученным LOB
        // ...
      finally
        OraLob.Free;
      end;
    finally
      // Мы контекс создали, мы же его и освобождаем
      OraSvcCtx.Free;
    end;
  except
    on E: EOraError do
      OracleHomes.Default.OCI8.OCIExtProcRaiseExcpWithMsg(Context, E.ErrorCode, PAnsiChar(AnsiString(E.Message)), Length(E.Message));
    on E: Exception do
      OracleHomes.Default.OCI8.OCIExtProcRaiseExcpWithMsg(Context, 20000, PAnsiChar(AnsiString(E.Message)), Length(E.Message));
  end;
end;

exports
  ExportLob;

begin
  OracleHomes.Init;
end.
В БД можно объявлять внешнюю функцию как на CLOB, так и на BLOB - выше приведенный код при этом менять не нужно.

И да... если есть необходимость экспортировать NULL/EMPTY_CLOB/EMPTY_BLOB, то в Оракле надо бы вначале проинициализировать локатор с помощью dbms_lob.createtemporary и, после вызова внешней процедуры, финализировать его с помощью dbms_lob.freetemporary. В противном случае, во внешней процедуре можно поймать исключительную ситуацию с "Invalid LOB locator".

С уважением,
Владимир.
Последний раз редактировалось Vovick Вс 27 июл 2014 20:34, всего редактировалось 1 раз.

Alexp
Devart Team
Сообщения: 349
Зарегистрирован: Пн 27 дек 2010 10:34

Re: External procedures in multi-home environment

Сообщение Alexp » Чт 24 июл 2014 10:07

Добрый день,

Спасибо, мы внесем необходимые изменения в примеры.

Закрыто