Работа с BLOB через IBX

IBX, FIBPlus, UIB, ADO, .Net и прочее-прочее-прочее, в общем все, что относится к созданию приложений, работающих с InterBase, Firebird и Yaffil - клиент-серверных, трехзвенных, консольных и т.п.

Модератор: kdv

kostyl
Сообщения: 47
Зарегистрирован: 06 фев 2008, 10:24

Работа с BLOB через IBX

Сообщение kostyl » 28 фев 2008, 12:00

Использую IBQUERY для вставки записи:
пишу в .SQL:
insert into FILES (FILE_ID,FILE_DATA) values (:FILE_ID,:FILE_DATA)'
где FILE_DATA BLOB SUB_TYPE 0
Значение FILE_ID получаю темже IBQUERY чуть выше из генератора в
var NewFileID:Int64;
Передаю "динамически" в запрос:
IBQuery1.ParamByName('FILE_ID').AsInteger:=NewFileID;
А вот что делать с FILE_DATA - понятия не имею, потому как у меня он в TIBBlobStream.
Да можно попросту создать запись а потом "модифнуть" его IBDATASET ом. Но хочеться одним запросом и FILE_ID и FILE_DATA.
Как это реализовать?
PS1:TIBBlobStream изпользую потому как шифрую данные.
PS1: И вообще где достать нормальную документацию по использованию IBX 7.11. Во всех книгах одна фигня...

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 28 фев 2008, 12:42


kostyl
Сообщения: 47
Зарегистрирован: 06 фев 2008, 10:24

Сообщение kostyl » 28 фев 2008, 13:20

WildSery писал(а):Тут читал?
Читал перед тем как задал тему.
Ну вот от туда:
IBQuery1.ParamByName('blb').asBlob:=blobvar;
Ну и как мне в строку записать 1Гб из потока?

kostyl
Сообщения: 47
Зарегистрирован: 06 фев 2008, 10:24

Сообщение kostyl » 28 фев 2008, 15:45

А понял!
Прокритикуйте плиз:

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

procedure TForm1.BitBtn6Click(Sender: TObject);
var NewFileID:int64;
begin
 if IBDatabase1.Connected then
  if OpenDialog1.Execute then
    begin
     try
      IBTransaction1.RollbackRetaining;
      //Получаем значения генератора
      IBQuery1.Close;
      IBQuery1.SQL.Clear;
      IBQuery1.SQL.Add('select GEN_ID(GEN_FILE_ID,1) from RDB$DATABASE');
      IBQuery1.Open;
      NewFileID:=IBQuery1.FieldByName('GEN_ID').AsVariant;
      //Загружаем в поток данные файла
      ActiveBlob:=TIBBlobStream.Create;
      ActiveBlob.Mode:=bmReadWrite;
      ActiveBlob.LoadFromFile(OpenDialog1.FileName);
      //Делаем всякую фигню
      //формируем запрос на добавление записи
      IBQuery1.Close;
      IBQuery1.SQL.Clear;
      IBQuery1.SQL.Add('insert into FILES (FILE_ID,FILE_DATA) values (:FILE_ID,:FILE_DATA)');
      IBQuery1.ParamByName('FILE_ID').AsInteger:=NewFileID;
      IBQuery1.ParamByName('FILE_DATA').LoadFromStream(ActiveBlob,ftBlob);
      IBQuery1.Open;
      IBTransaction1.CommitRetaining;
      ActiveBlob.Free;
     except
      IBTransaction1.RollbackRetaining;
      ActiveBlob.Free;
     end;
    end;
end;

Attid
Спец
Сообщения: 377
Зарегистрирован: 14 ноя 2006, 09:58

Сообщение Attid » 28 фев 2008, 18:30

критикую. надо пользоваться тегами [code][/code] , а то никто код читать не будет.

Кузнецов Евгений
Сообщения: 144
Зарегистрирован: 16 фев 2006, 22:36

Сообщение Кузнецов Евгений » 28 фев 2008, 18:39

kostyl писал(а):Прокритикуйте плиз:
Это пожалуйста.
1) Конечно, ничто так не повышает читаемость кода, как отсутствие тега Code
2)

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

     try
      IBTransaction1.RollbackRetaining;
И зачем начинать с отката неподтвержденных изменений - не понимаю.
3)

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

 
   NewFileID:=IBQuery1.FieldByName('GEN_ID').AsVariant;
   ...
  IBQuery1.ParamByName('FILE_ID').AsInteger:=NewFileID;
Ну и зачем Int64, если в итоге оно приводится к Integer?
Вместо IBQuery здесь лучше использовать IBSQL - он легче, и есть поддержка BIGINT. Если NewFileID в коде больше нигде не используется, можно от него избавиться:

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

  insert into FILES (FILE_ID,FILE_DATA) 
  values ( GEN_ID(GEN_FILE_ID,1),:FILE_DATA)
4)

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

     try
        ...  
     except
      IBTransaction1.RollbackRetaining;
      ActiveBlob.Free;
     end;
Не стоит молча гасить исключение - пользователь будет пребывать в уверенности, что все данные сохранены и в случае отката. Лучше вернуть исключение к жизни с помощью ключевого слова raise.

5) С ActiveBlob лучше поступить так:

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

   ActiveBlob:=TIBBlobStream.Create;
   try
    // Работаем с  ActiveBlob
   finally
      FreeAndNil(ActiveBlob);
   end;
   ...
   IBTransaction1.CommitRetaining;
6) Стоит отделять логику от интерфейса

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

procedure TForm1.BitBtn6Click(Sender: TObject);
begin
   if IBDatabase1.Connected then
     if OpenDialog1.Execute then
        DoSaveBlob(OpenDialog1.FileName);
end;
Ну а за Retaining Вас KDV поругает, когда вернется.

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 29 фев 2008, 00:46

тут и критиковать нечего. ужасные действия в коде.
кроме того, нифига ж не читал, если сначала Post, а потом Free.
И вообще где достать нормальную документацию по использованию IBX 7.11. Во всех книгах одна фигня...
документация не нужна. есть ibx.htm.
кстати, была дока - в хелпе D7, отдельным файлом. Но увы.
Кстати. как работать - есть описание в developer guide по дельфям.

Кузнецов Евгений
Сообщения: 144
Зарегистрирован: 16 фев 2006, 22:36

Сообщение Кузнецов Евгений » 29 фев 2008, 20:40

Доброго времени суток!
kdv писал(а):кроме того, нифига ж не читал, если сначала Post, а потом Free.
Загрузка через LoadFromStream ведется в параметр (т.е. TIBXSQLVAR), а не в TIBBlobStream непосредственно.
В TIBXSQLVAR.LoadFromStream создается и уничтожается временный TIBBlobStream, так что вроде бы ничего страшного.

Кстати, а в силу чего такой запрет (статью я читал :) )?
Физическая запись BLOB происходит при Post, и если мы убъем TIBBlobStream раньше, то он будет закрыт и не сохранен в БД?

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 03 мар 2008, 10:03

Физическая запись BLOB происходит при Post, и если мы убъем TIBBlobStream раньше, то он будет закрыт и не сохранен в БД?
а пофиг. есть такое правило кодирования - ресурсы освобождаются в том порядке, в котором они аллокируются.
Физическая запись BLOB происходит при Post, и если мы убъем TIBBlobStream раньше
ты уверен? код IBX смотрел?

Кузнецов Евгений
Сообщения: 144
Зарегистрирован: 16 фев 2006, 22:36

Сообщение Кузнецов Евгений » 03 мар 2008, 12:03

Доброго времени суток!
kdv писал(а):а пофиг. есть такое правило кодирования - ресурсы освобождаются в том порядке, в котором они аллокируются.
В обратном :) Правда, не вижу связи с данным случаем.
ты уверен?

Там знак вопроса стоял.
код IBX смотрел?
В TIBBlobStream.Destroy есть только isc_close_blob - закрытие BLOB. isc_put_segment встречается только в IBBlob.WriteBlob
WriteBlob вызывается только в TIBXSQLVAR.Assign (кажется, не используется) и в TIBBlobStream.Finalize. Последний вызывается в TIBCustomDataSet.InternalPostRecord, TIBBlobStream.CloseBlob, TIBXSQLVAR.LoadFromStream.
CloseBlob не используется, InternalPostRecord вызывается при Post,
LoadFromStream вызывается в TIBCustomDataSet.InternalSetParamsFromCursor,
TIBXSQLVAR.SetAsString
Поэтому неясно, рабочий ли код:

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

  IBDataSet1.Edit;   
  B:=IBDataSet1.CreateBlobStream(IBDataSet1.FieldByName('BLB') as TBlobField, bmWrite);   
  B.LoadFromFile('c:\blob.bin');   
  B.Free;   
  IBDataSet1.Post;
Возможно, я что-то не учел?

kostyl
Сообщения: 47
Зарегистрирован: 06 фев 2008, 10:24

Сообщение kostyl » 03 мар 2008, 12:17

Огромное спасибо за коментарии. Почувствовать себя немощьным новичком - великое благо ибо оно стимулирует на большие подвиги.
kdv писал(а):
нифига ж не читал, если сначала Post, а потом Free
Читал, да наверно значение на предал. Виноват.
Кузнецов Евгений писал(а):
Ну а за Retaining Вас KDV поругает, когда вернется
А. Так и не поругал.... :)
P.S.:А вообщем хорошо, что есть такой сайт как этот, хотя всё равно трудно идёт "разбор" с клиент-сервером, как с непознанным доселе. Сейчас активно изучал статью про транзакции. Довольно сложно разобраться со всеми блокировками, параметрами, хоть там и даны маленькие примеры в конце, которые могут подойти, но хотелось бы по конкретнее - на реальном примере. Все равно большое спасибо. Вот подучусь и напишу новую версию кода.

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 03 мар 2008, 13:17

А. Так и не поругал...
а мне уже до лампочки. в статьях по транзакциям на сайте про retaining как на каждом столбе написано.
но хотелось бы по конкретнее - на реальном примере.
на реальном примере нужны только 2 типа транзакций, это по минимуму. read_committed rec_version nowait для просмотра и
concurrency nowait например для отчетов

все остальное - по вкусу и по ситуации. Читать - это правильно, причем не один раз, а пока дойдет. Я исходную доку по транзакциям IB 4.0 читал раз 5-6, пока дошло. Это при том что там страниц 10 всего (или меньше).

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 03 мар 2008, 13:32

Поэтому неясно, рабочий ли код:
перед вставкой в FAQ я код проверял. ты тоже можешь проверить - всего-то две строчки местами переставить. Проверил-бы и уличил меня, если я неправ. Мало-ли что.
Я вот почему-то помню, что если Free ставили после Post, то были глюки, особенно в BDE. Отсюда и рекомендация.

Кузнецов Евгений
Сообщения: 144
Зарегистрирован: 16 фев 2006, 22:36

Сообщение Кузнецов Евгений » 03 мар 2008, 22:44

Доброго времени суток!
kdv писал(а):перед вставкой в FAQ я код проверял. ты тоже можешь проверить - всего-то две строчки местами переставить. Проверил-бы и уличил меня, если я неправ. Мало-ли что.
Не в чем Вас уличать :) Написано же,
Статья писал(а):В предыдущих версиях IBX (до 4.42 включительно) можно было работать с blob иначе.
Естественно, копание в IBX 6.08 ничего не скажет о работоспособности ранних версий.
Насколько я понял, в 6.08 CreateBlobStream возвращает не TIBBlobStream как раньше, а его достаточно беззубую обертку TIBDSBlobStream. LoadFromFile в последнем не реализован, поэтому для чтения из файла придется создавать дополнительный поток

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

var B: TIBDSBlobStream;
    fs:TFileStream;
begin
    IBDataSet1.Edit;
    B:=IBDataSet1.CreateBlobStream(IBDataSet1.FieldByName('DATA') as TBlobField, bmWrite) as TIBDSBlobStream;
    try
      fs:=TFileStream.Create('c:\blob.bin',fmOpenRead);
      try
        B.CopyFrom(fs,fs.Size)
      finally
        FreeAndNil(fs);
      end;
      IBDataSet1.Post;
    finally
      FreeAndNil(B);
    end;
    //IBDataSet1.Post;
end;
Так как в деструкторе класса TIBDSBlobStream сам TIBBlobStream не уничтожается (утечка памяти тут не грозит, поскольку TIBBlobStream хранится во внутреннем списке IBDataset.FBlobStreamList, который убивается при закрытии IBDataset), то особой разницы, где именно вызывать Post, нет - эксперимент показал успешность обоих вариантов.

Единственное отличие может быть только, если используются DB-Aware компоненты для отображения BLOB - деструктор TIBDSBlobStream посылает событие об изменении BLOB-поля, поэтому в этом случае логичнее делать Post после освобождения B.

kostyl
Сообщения: 47
Зарегистрирован: 06 фев 2008, 10:24

Сообщение kostyl » 04 мар 2008, 11:17

Так пост потом фри или фри потом пост. Вы уж договоритесь как и когда, а то я "непонимать" чё когда там вызывается, уничтожается и т. д. Очень хочеться узнать :wink:

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 04 мар 2008, 12:25

мне больше интересно, почему "вызывается" без мягкого знака, а "хочеться" - с мягким знаком. Ты уж определись, пожалуйста. Или ставь его везде, или только там где надо.

kostyl
Сообщения: 47
Зарегистрирован: 06 фев 2008, 10:24

Сообщение kostyl » 04 мар 2008, 13:43

kdv писал(а):мне больше интересно, почему "вызывается" без мягкого знака, а "хочеться" - с мягким знаком. Ты уж определись, пожалуйста. Или ставь его везде, или только там где надо.
"хочеться" с мягким знаком, потому как "непонимать" в инфинитиве и слитно. Вот. А еще такой вопрос - что вы подразумеваете под словом "АЛЛОКИРУЕТСЯ" - привязывается или как. лок вроде замок. а значит замыкается. или как?

stix-s
Заслуженный разработчик
Сообщения: 557
Зарегистрирован: 13 дек 2005, 11:52

Сообщение stix-s » 04 мар 2008, 14:28

kostyl писал(а):
kdv писал(а):мне больше интересно, почему "вызывается" без мягкого знака, а "хочеться" - с мягким знаком. Ты уж определись, пожалуйста. Или ставь его везде, или только там где надо.
"хочеться" с мягким знаком, потому как "непонимать" в инфинитиве и слитно. Вот. А еще такой вопрос - что вы подразумеваете под словом "АЛЛОКИРУЕТСЯ" - привязывается или как. лок вроде замок. а значит замыкается. или как?
с мягким знаком пишут хотеться, а не хочется :)
аллокировать в данном случае - выделять

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 04 мар 2008, 14:38

"хочеться" с мягким знаком, потому как "непонимать" в инфинитиве и слитно.
бред. ты вслух произнеси "хочеться" и "хочется". Поймешь в чем разниться. :-)
АЛЛОКИРУЕТСЯ
это от английского слова allocate. см. словарь. lingvo.yandex.ru.

kostyl
Сообщения: 47
Зарегистрирован: 06 фев 2008, 10:24

Сообщение kostyl » 04 мар 2008, 15:50

Ну ладно отпечатался и всё...
allocate - выделять ресурсы под объект вроде. Да? А какая разница когда выделять и убивать? Типа чтоб память не фрагментировалсь?

Ответить