Поиск:

Ответ в темуСоздание новой темы Создание опроса
> (FB/IB, IBX) - Ошибка в IBX! Нашел и исправил 
:(
    Опции темы
Петрович
  Дата 11.2.2005, 11:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1000
Регистрация: 2.12.2003
Где: Москва

Репутация: 6
Всего: 55



Найдена ошибка в IBX.

Всем известно что метод Locate у любых наследников tDataSet не должен возбуждать исключительную ситуацию (Exception) если не нашел искомую запись.
Однако в IBX, иногда, он это делает.
А именно, если выполнить присваивание закладки (Bookmark) ссылающейся на несуществующую запись, то он это сделает "молча", не возбуждая исключения. Естественно, после этого реальная позиция в наборе данных не отражает требования пользователя. Уже это можно считать ошибкой. Но, если после этого еще и вызвать Locate ищущий несуществующую в наборе данных запись, то вместо того что-бы получить результат False, мы получаем исключительную ситуацию EDatabaseError с текстом "Record not found".
Проще всего, это можно продемонстрировать на следующем примере:
Код

procedure Test;
var
 db :TIBDatabase;
 Tr :TIBTransaction;
 qs :TIBQuery;
 qe :TIBQuery;
 s :String;
begin //
 // создание необходимых компонент
 db := TIBDatabase.Create(nil);
 Tr := TIBTransaction.Create(nil);
 qs := TIBQuery.Create(nil);
 qe := TIBQuery.Create(nil);
 try
   // настройка компонентов
   db.DatabaseName := 'C:\$Prg\Договора\v4\###\DB.GDB';
   db.Params.Values['user_name' ] := 'SYSDBA';
   db.Params.Values['password'  ] := 'masterkey';
   db.LoginPrompt := False;

   Tr.DefaultDatabase := db;

   qs.Database := db;
   qs.Transaction := tr;

   qe.Database := db;
   qe.Transaction := tr;

   // создание тестовой таблицы
   db.Open;
   tr.StartTransaction;
   try
     qe.SQL.Text := 'CREATE TABLE Test_Table (F INTEGER)';
     qe.ExecSQL;
   except
   end;
   tr.Commit;

   // добавление записи в тестовую таблицу
   tr.StartTransaction;
   qe.SQL.Text := 'INSERT INTO Test_Table VALUES (1)';
   qe.ExecSQL;

   // получение первой записи
   qs.SQL.Text := 'SELECT * FROM Test_Table';
   qs.Open;

   // удаление всех записей
   qe.SQL.Text := 'DELETE FROM Test_Table';
   qe.ExecSQL;

   // подтвердить транзакцию
   s := qs.Bookmark;  // посвольку Commit закроет qs, сохраним позицию в нем
   tr.Commit;
   qs.Open;           // откроем закрытый Commit'ом qs
   qs.Bookmark := s;  // вернем его в прежнюю позицию

   // попытаться найти любую запись
   ShowMessage('Сейчас будем выполнять Locate');
   if  qs.Locate('F',1,[])  then
     ShowMessage('Locate нашел запись')
   else
     ShowMessage('Locate не нашел запись')
  ;

 finally
   // удалить тестовую таблицу
   if  not tr.InTransaction  then  tr.StartTransaction;
   try
     qe.SQL.Text := 'DROP TABLE Test_Table';
     qe.ExecSQL;
   except
   end;
   tr.Commit;

   // "грохнуть" все созданные компоненты
   qs.Free;
   qe.Free;
   tr.Free;
   db.Free;
 end;
end;


В нем, вместо последовательно получения сообщений:
'Сейчас будем выполнять Locate'
'Locate не нашел запись'

Мы получим:
'Сейчас будем выполнять Locate'
'Record not found' <- результат возникшего в Locate исключения EDatabaseError.
Не стану "нагружать" Вас рассказами почему это происходит, а сразу перейду к тому как исправить.
Сделать это не сложно. Но, поскольку в мире "бродят" несколько версий IBX, я не стану выкладывать сюда исправленный модуль, а приведу алгоритм исправления для версии v6.03. Если Вы используете другую версию IBX, то Вы сможете подправить ее самостоятельно.
Итак. Исправления нужно внести в файл IBCustomDataSet.pas. Там, нужно добавить одну строчку в тело метода TIBCustomDataSet.InternalGetRecord.
У себя, в v6.03, я сделал это так:
Код

function TIBCustomDataSet.InternalGetRecord(Buffer: PChar; GetMode: TGetMode;
 DoCheck: Boolean): TGetResult;
begin
 result := grError;
 case GetMode of
   gmCurrent:
   begin
     if (FCurrentRecord >= 0) then
     begin
       if FCurrentRecord < FRecordCount then
         ReadRecordCache(FCurrentRecord, Buffer, False)
       else
       begin
         while (not FQSelect.EOF) and
               (FCurrentRecord >= FRecordCount) do
         begin
           if FQSelect.Next = nil then
             break;
           FetchCurrentRecordToBuffer(FQSelect, FRecordCount, Buffer);
           Inc(FRecordCount);
         end;
         FCurrentRecord := FRecordCount - 1;
         if (FCurrentRecord >= 0) then
           ReadRecordCache(FCurrentRecord, Buffer, False);
       end;
       result := grOk;
{$IfDef NoChangesBySAP} // before changes by SAP
{$Else}                 // after changes by SAP
       if (FCurrentRecord < 0) then  result := grBOF; // <<<<<<<<< добавленная мною строка
{$EndIf}
     end
     else
       result := grBOF;
   end;
   gmNext:
   begin
     result := grOk;
     if FCurrentRecord = FRecordCount then
       result := grEOF
     else
     if FCurrentRecord = FRecordCount - 1 then
     begin
       if (not FQSelect.EOF) then
       begin
         FQSelect.Next;
         Inc(FCurrentRecord);
       end;
       if (FQSelect.EOF) then
       begin
         result := grEOF;
       end
       else
       begin
         Inc(FRecordCount);
         FetchCurrentRecordToBuffer(FQSelect, FCurrentRecord, Buffer);
       end;
     end
     else
       if (FCurrentRecord < FRecordCount) then
       begin
         Inc(FCurrentRecord);
         ReadRecordCache(FCurrentRecord, Buffer, False);
       end;
   end;
   else { gmPrior }
   begin
     if (FCurrentRecord = 0) then
     begin
       Dec(FCurrentRecord);
       result := grBOF;
     end
     else
       if (FCurrentRecord > 0) and
                   (FCurrentRecord <= FRecordCount) then
       begin
         Dec(FCurrentRecord);
         ReadRecordCache(FCurrentRecord, Buffer, False);
         result := grOk;
       end
       else
         if (FCurrentRecord = -1) then
           result := grBOF;
   end;
 end;
 if result = grOk then
   result := AdjustCurrentRecord(Buffer, GetMode);
 if result = grOk then
   with PRecordData(Buffer)^ do
   begin
     rdBookmarkFlag := bfCurrent;
     GetCalcFields(Buffer);
   end
 else
   if (result = grEOF) then
   begin
     CopyRecordBuffer(FModelBuffer, Buffer);
     PRecordData(Buffer)^.rdBookmarkFlag := bfEOF;
   end
   else
     if (result = grBOF) then
     begin
       CopyRecordBuffer(FModelBuffer, Buffer);
       PRecordData(Buffer)^.rdBookmarkFlag := bfBOF;
     end
     else
       if (result = grError) then
       begin
         CopyRecordBuffer(FModelBuffer, Buffer);
         PRecordData(Buffer)^.rdBookmarkFlag := bfEOF;
       end;
end;

В версии IBX v6.08, текст этого метода идентичен, поэтому Вы можете его туда просто скопировать. Про другие версии не знаю, но думаю что он уже давно не меняется.

Однако, стоит обратить внимание, что после исправления этой ошибки, в приведенном выше примере, Вы вообще не увидите сообщение 'Сейчас будем выполнять Locate'. Вместо него Вы сразу получите 'Record not found' - результат возникшего исключения EDatabaseError при выполнении строки:
Код
   qs.Bookmark := s;  // вернем его в прежнюю позицию

Но это естественно правильно. Ведь присваиваемая закладка ссылается на уже не существующую запись. А что бы у Вас не оставалось сомнения в правильности этого, скажу что теперь, поведение IBX становится таким-же как и поведение ADO в подобных ситуациях - проверено smile.

Добавлено @ 11:20
Ой, совсем забыл:
  • После внесения иправления в IBCustomDataSet.pas обеспечте его обязательную перекомпиляцию
  • Если Вы используете компиляцию своих приложений с пакетами, то естественно, Вам обязательно надо будеит пересобрать пакет IBX



--------------------
Все знать невозможно, но хочется
PM ICQ   Вверх
Alex
Дата 11.2.2005, 23:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 4147
Регистрация: 25.3.2002
Где: Москва

Репутация: 17
Всего: 162



smile а все так безобидно начиналось. smile


--------------------
Написать можно все - главное четко представлять, что ты хочешь получить в конце. 
PM Skype   Вверх
Golden Hands
Дата 13.2.2005, 02:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Золотой
****


Профиль
Группа: Участник Клуба
Сообщений: 2023
Регистрация: 23.1.2005
Где: Екатеринбург

Репутация: 2
Всего: 83



А в Борланд написал?


--------------------
Мы обречены... но только на победу!
Настанет день, и мы построим новый дом.
Внесем в него тепло, что сохранить сумели,
И воскресим все то, что в нас когда-то умерло... © Тень Света
PM MAIL ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Базы данных и репортинг"
Vit
Петрович

Запрещено:

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами


Обязательно указание:

1. Базы данных (Paradox, Oracle и т.п.)

2. Способа доступа (ADO, BDE и т.д.)


  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи
  • Вопросы по SQL и вопросы по базам данных не связанные с Дельфи задавать здесь

FAQ раздела лежит здесь!


Если Вам помогли и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, Vit, Петрович.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Базы данных и репортинг | Следующая тема »


 




[ Время генерации скрипта: 0.0565 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.