powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Delphi [игнор отключен] [закрыт для гостей] / FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
25 сообщений из 30, страница 1 из 2
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937430
nicholaos
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Delphi Rio 10.3.3
Firebird 2.5.9, 32-х битный

Тестовая БД (page_size = 16384) с одной таблицей с одной записью с очень большим (не текстовым) BLOB'ом.

Размер исходного файла на диске - 7.57 GB (8,132,685,824 bytes)
Размер файла БД на диске - 7.73 GB (8,307,064,832 bytes)

DDL
Код: plsql
1.
2.
3.
4.
5.
6.
CREATE TABLE TEST_BLOB (
    ID     INTEGER NOT NULL,
    FILE_  BLOB SUB_TYPE 0 SEGMENT SIZE 80
);

ALTER TABLE TEST_BLOB ADD PRIMARY KEY (ID);




isql показывает размер файла некорректно:
Код: plsql
1.
2.
3.
4.
5.
SQL> select octet_length(FILE_) from TEST_BLOB;

OCTET_LENGTH
============
  -457250816



Хотя по спецификации ( https://firebirdsql.org/en/firebird-technical-specifications ) поддерживаются BLOB'ы до 32 ГБ.

Загружал в БД следующим кодом:

BLOBToDB
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
procedure TForm1.btBLOBToDBClick(Sender: TObject);
var
  fs: TFileStream;
begin
  FDConnection1.Connected := True;
  FDTransaction1.StartTransaction;

  FDQuery1.SQL.Text := 'insert into TEST_BLOB(ID, FILE_) values(1, :FILE_)';
  FDQuery1.ParamByName('FILE_').DataType := ftStream;
  FDQuery1.ParamByName('FILE_').ParamType := ptInput;
  FDQuery1.ParamByName('FILE_').StreamMode := smOpenRead;
  FDQuery1.ExecSQL;
  fs := TFileStream.Create('C:\Work\disk.iso', fmOpenRead);
  try
    FDQuery1.ParamByName('FILE_').AsStream.CopyFrom(fs, -1);
  finally
    fs.Free;
  end;
  FDQuery1.CloseStreams;

  FDTransaction1.Commit;
  FDConnection1.Connected := False;
end;




Пытаюсь загрузить из БД:

BLOBToFile
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
procedure TForm1.btBLOBToFileClick(Sender: TObject);
var
  s: TStream;
  fs: TFileStream;
begin
  FDConnection1.Connected := True;
  FDTransaction1.StartTransaction;

  FDQuery1.FetchOptions.AutoFetchAll := afDisable;
  FDQuery1.FetchOptions.Cache := [fiDetails, fiMeta];
  FDQuery1.FetchOptions.Items := [fiDetails, fiMeta];

  FDQuery1.SQL.Text := 'select ID, FILE_ from TEST_BLOB where ID = :ID';
  FDQuery1.MasterFields := 'ID';
  FDQuery1.ParamByName('ID').AsInteger := 1;
  FDQuery1.Open;
  if not FDQuery1.Eof then begin
    fs := TFileStream.Create('C:\Work\disk2.iso', fmCreate);
    try
      s := FDQuery1.CreateBlobStream(FDQuery1.FieldByName('FILE_'), bmRead);
      fs.CopyFrom(s, -1);
    finally
      fs.Free;
    end;
  end;
  FDQuery1.CloseStreams;
  FDQuery1.Close;

  FDTransaction1.Commit;
  FDConnection1.Connected := False;
end;




Падает на CreateBlobStream с ошибкой Access Violation. Стэк вызовов приводит к Lib.Fisc_get_segment в TIBBlob.Read (FireDAC.Phys.IBWrapper.pas).

Маленькие файлы сохраняются без проблем.

Вопрос - есть ли известные способы работы с большими блобами? Другие компоненты доступа, например? IBEScript.dll не справился. Известная UDF BlobSaveLoad справляется, но работает только на сервере.
Придется писать самому на ISC API?
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937431
Фотография Dmitry Arefiev
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937432
nicholaos
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Dmitry Arefiev,

Читал. С загрузкой в БД проблем нет, я писал. Проблема с загрузкой из БД и только на больших файлах - похоже на ошибку или ограничение самого FireDAC. В документации для select'а вместо вменяемого примера только SQL Server FILESTREAM.
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937438
Sinemurius
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А попробуйте написать хранимку, которая получает ID строки в таблице TEST_BLOB, считывает блоб в переменную, разбивает на куски и возвращает блоб кусками.

Клиент используя процедурку прочитает блоб кусками и соединит их уже средствами Delphi.

Так Вы сможете помимо прочего показывать прогресс считывания.
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937446
nicholaos
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Sinemurius,

У меня не текстовый блоб размером 7 ГБ. Если разбить его на куски в хранимке вообще технически возможно, то навряд ли это правильное решение.

Коллбэк для прогресс-бара, по-хорошему, должен вызывать сам стрим в момент считывания очередного куска.
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937449
ъъъъъ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
nicholaos,

Максимальный размер поля BLOB ограничен 4 ГБ. Для размера страницы 4 КБ (4096 байт) максимальный размер меньше - чуть меньше 2 ГБ.

https://firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-datatypes-bnrytypes.html
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937452
ъъъъъ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
nicholaos
Хотя по спецификации ( https://firebirdsql.org/en/firebird-technical-specifications) поддерживаются BLOB'ы до 32 ГБ.


Смотри официальную документацию, а не фик знает что.
Вот тут руководство по 2.5 скачать можно:

https://www.sql.ru/forum/1123374/ann-rukovodstvo-po-yazyku-sql-subd-firebird-2-5
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937459
Sinemurius
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
nicholaos
Sinemurius,

У меня не текстовый блоб размером 7 ГБ. Если разбить его на куски в хранимке вообще технически возможно, то навряд ли это правильное решение.

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


Бинарный блоб спокойно режится substring-ом.

Примерно так:


Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
Pos = 1;
BlobNumPart = 1;
FilePartLength = 1024 * 1024;
select length(FILE_) from test_blob where id = :id into FileLength;

while Pos <= FileLength do 
begin
  select substring(file_ from pos for FilePartLength) from test_blob where id = :id into BlobPart;
  suspend;
  Pos = Pos + FilePartLength;
  BlobNumPart = BlobNumPart + 1;
end



Где выходные параметры хранимки - BlobNumPart (номер куска) и BlobPart (кусок блоба)

Вы можете не скачивать весь файл, а скачивать кусками для использования. К примеру, если это видео, то скачиваете кусками и подкидываете куски плееру.
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937460
Sinemurius
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Можете повозиться и написать свой потомок от TStream, который будет обращаться к базе и вытаскивать блоб кусками.
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937462
nicholaos
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ъъъъъ,

Загрузил файл размером 2.78 GB - аналогичная ошибка.

Sinemurius,

Попробую в порядке эксперимента.
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937469
Vlad F
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
nicholaos,

Продублируй вопрос в форме по IB/FB. Там хватает высоких ценителей егойного API, если у FireDAC-а в данном месте возможные проблемы с его использованием.
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937473
nicholaos
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Vlad F,

Дублировать 1-в-1 не стал, но ссылку сюда написал , если вдруг кто-то читает форум Firebird и не заходит в раздел Delphi.
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937478
Vlad F
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
nicholaos,

Вижу. Однако ты тему совсем неудачную там задал, малоинформативную и непривлекательную и не по существу, имхо.
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937530
nicholaos
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Sinemurius,

В хранимке разрезать блоб не получилось, все функции, возвращающие длину, работают некорректно. Проверял так:

DDL
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
SET TERM ^ ;

create or alter procedure TEST_SPLIT (
    ID integer)
returns (
    OCTET_LENGTH_ bigint,
    BIT_LENGTH_ bigint,
    CHAR_LENGTH_ bigint)
as
begin
  select octet_length(FILE_), bit_length(FILE_), char_length(FILE_)
  from TEST_BLOB
  where ID = :ID
  into :OCTET_LENGTH_, :BIT_LENGTH_, :CHAR_LENGTH_;
  suspend;
end^

SET TERM ; ^




Результат выполнения:
Код: plsql
1.
2.
3.
4.
5.
SQL> select * from TEST_SPLIT(1);

        OCTET_LENGTH_           BIT_LENGTH_          CHAR_LENGTH_
===================== ===================== =====================
           -457250816             636960768            -457250816
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937532
nicholaos
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Vlad F,

Проверил IBX:

BLOBToFileIBX
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
procedure TForm1.btBLOBToFileIBXClick(Sender: TObject);
var
  s: TStream;
  fs: TFileStream;
begin
  IBDatabase1.Open;
  IBQuery1.SQL.Text := 'select ID, FILE_ from TEST_BLOB where ID = :ID';
  IBQuery1.ParamByName('ID').AsInteger := 1;
  IBQuery1.Open;
  if not IBQuery1.Eof then begin
    fs := TFileStream.Create('C:\Work\disk.iso', fmCreate);
    try
      s := IBQuery1.CreateBlobStream(IBQuery1.FieldByName('FILE_'), bmRead);
      fs.CopyFrom(s, -1);
    finally
      fs.Free;
    end;
  end;
  IBQuery1.Close;
  IBDatabase1.Close;
end;




Падает с Range Check Error на fs.CopyFrom. Отладчик приводит к GetBlobInfo (IBX.IBBlob.pas). Функция isc_portable_integer возвращает для TotalSize некорректное значение (см. картинку).

GetBlobInfo



Похоже все упирается в ISC API у которого аналогичные octet_length'у проблемы.
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937535
Sinemurius
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
nicholaos,

А length ?
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937538
nicholaos
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Sinemurius,

Нет такого.
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937560
Sinemurius
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Что-то смахивает на то, что блоб загружен в базу как то криво.

Вообще хранить 7 гигабайтные блобы - это как то неэлегантно что-ли. Вы уверены, что Вам именно это нужно ?

Я бы рассмотрел вопрос о том, чтобы хранить не в базе, а в файловой системе. Ну или разбивать блоб на куски в момент загрузки блоба в базе.

Ну к примеру сделать связанную с Вашим TEST_BLOB табличку для хранения кусков блоба.

Ну и рассмотреть: что у Вас за блоб такой ? что в нем хранится ? можно ли это разбить на какие то структурированные куски ?
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937570
nicholaos
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Sinemurius,

Не элегантно, согласен. Для прикладной задачи мне достаточно и 2-х ГБ.

Но даже попытка скачать 2 ГБ падает с ООМ на CreateBlobStream, что в FireDAC, что в IBX.

С бóльшим размером блоба задача носит больше академический характер - обратить внимание разработчиков на неоднозначность документации, зафиксировать баги компонентов доступа, ISC API, функции octet_length.
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937576
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
nicholaosНо даже попытка скачать 2 ГБ падает с ООМ на CreateBlobStream, что в FireDAC, что в IBX.

Потому что автор IBX был рукожоп, а FireDAC унаследовал архитектуру.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937621
nicholaos
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Мне удалось скачать блоб 7.5 GB с помощью вызова ISC API напрямую.

Полный текст программы, C++
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
#include <stdlib.h>
#include <ibase.h>
#include <fstream>
#include <iostream>

#define DATABASE_PATH    "C:\\Work\\TEST_BLOB.FDB"
#define OUTPUT_FILE_PATH "C:\\Work\\disk2.iso"
#define SQL_SELECT       "select FILE_ from TEST_BLOB where ID = '1'"

int main()
{
  char database[] = DATABASE_PATH;

  isc_db_handle db = 0;
  isc_tr_handle trans;

  ISC_STATUS status_vector[20];

  short dpb_buf_len = 20;

  char dpb_buf[] = {
				isc_dpb_version1,
				isc_dpb_user_name,
				6,
				'S','Y','S','D','B','A',
				isc_dpb_password,
				9,
				'm','a','s','t','e','r','k','e','y'
			  };

  isc_attach_database(status_vector, strlen(database), database, &db, dpb_buf_len, dpb_buf);

  if (status_vector[0] == 1 && status_vector[1])
  {
	isc_print_status(status_vector);

	return 1;
  }

  char *select = SQL_SELECT;

  XSQLDA *out_sqlda;

  out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(1));

  out_sqlda->version = SQLDA_VERSION1;

  out_sqlda->sqln = 1;

  isc_stmt_handle stmt;

  stmt = NULL;

  isc_dsql_allocate_statement(status_vector, &db, &stmt);

  char isc_tpb[] = {
				isc_tpb_version3,
				isc_tpb_read,
				isc_tpb_wait,
				isc_tpb_read_committed,
				isc_tpb_no_rec_version
			 };

  isc_start_transaction(status_vector, &trans, 1, &db, sizeof(isc_tpb), isc_tpb);
  if (status_vector[0] == 1 && status_vector[1])
  {
	isc_print_status(status_vector);

	return 1;
  }

  isc_dsql_prepare(status_vector, &trans, &stmt, 0, select, SQLDA_VERSION1, out_sqlda);

  ISC_QUAD blob_id;

  short flag;

  out_sqlda->sqlvar[0].sqldata = (char *) &blob_id;

  out_sqlda->sqlvar[0].sqltype = SQL_BLOB + 1;

  out_sqlda->sqlvar[0].sqllen = sizeof(ISC_QUAD);

  out_sqlda->sqlvar[0].sqlind = &flag;

  isc_dsql_execute(status_vector, &trans, &stmt, 1, NULL);

  ISC_STATUS fetch_stat;

  long SQLCODE;

  while ((fetch_stat = isc_dsql_fetch(status_vector, &stmt, 1, out_sqlda)) == 0) {}

  if (fetch_stat != 100L)
  {
    SQLCODE = isc_sqlcode(status_vector);

    isc_print_sqlerror(SQLCODE, status_vector);

    return 1;
  }

  isc_blob_handle blob_handle;

  blob_handle = NULL;

  char blob_segment[80];

  unsigned short actual_seg_len;

  isc_open_blob2(status_vector, &db, &trans, &blob_handle, &blob_id, 0, NULL);

  ISC_STATUS blob_stat;

  blob_stat = isc_get_segment(status_vector,
                              &blob_handle,
                              &actual_seg_len,
                              sizeof(blob_segment),
                              blob_segment);

  ofstream stream;

  stream.open(OUTPUT_FILE_PATH, std::ios_base::binary);

  while (blob_stat == 0 || status_vector[1] == isc_segment)
  {
    stream.write(&blob_segment[0], actual_seg_len);

    blob_stat = isc_get_segment(status_vector,
                                &blob_handle,
                                &actual_seg_len,
                                sizeof(blob_segment),
                                blob_segment);
  };

  stream.close();

  isc_close_blob(status_vector, &blob_handle);

  isc_commit_transaction(status_vector, &trans);

  if (db)
    isc_detach_database(status_vector, &db);

  return 0;
}




Контрольные суммы исходного файла и скаченного совпали.
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937626
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
nicholaosПолный текст программы, C++

Полагаешь, так много людей неспособны найти у Firebird каталог examples\api, что нужно его
здесь цитировать?.. Ну, наверное, ты прав.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937633
Vlad F
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
nicholaos,
Теперь нужен патч для FireDAC. Сделаешь очень доброе дело.
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937650
nicholaos
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Vlad F,

Патч - это когда можно обойтись простым фиксом. Тут нужен полноценный пул-реквест.

Во-первых, нужно отовсюду удалять использование TIBBlob.total_length - он возвращает неправильное значение.
Во-вторых, нужно в CreateBlobStream научится не аллоцировать память полностью и работать аналогично файловому стриму - читать данные по мере их запрашивания в методе CopyFrom.

Все это нужно и для IBX.
...
Рейтинг: 0 / 0
FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
    #39937652
Vlad F
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
nicholaos,

Да хрен бы с этим IBX (хотя я сам до сих пор на нем плотно сижу), - надо думать на перспективу. Сделай тикет на QC хотя бы для FireDAC, с указанием путей решения проблемы в целом.
...
Рейтинг: 0 / 0
25 сообщений из 30, страница 1 из 2
Форумы / Delphi [игнор отключен] [закрыт для гостей] / FireDAC и очень большой BLOB (Firebird) - AV при попытке скачать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]