"EhLib.Com"
https://forum.ehlib.com/ru/

TMemTableEh: длина строковых полей в UTF-8
https://forum.ehlib.com/ru/viewtopic.php?f=4&t=3302
Страница 1 из 1

Автор:  edgen [ 23 янв 2023, 14:39 ]
Заголовок сообщения:  TMemTableEh: длина строковых полей в UTF-8

Добрый день!

Lazarus 2.2.4, EhLib 11.0.10.

Копирую данные в MemTableEh примерно таким кодом:
Код:
function CopyStructureWithData(ASource: TDataSet; ADestination: TMemTableEh): boolean;
var
  j: integer;
begin
  if not ASource.Active then exit(false);
  ASource.First;
  with ADestination do begin
    if Active then Close;
    CopyStructure(ASource);
    CreateDataSet;
    Active:=true;
    while not ASource.Eof do begin
      Append;
      for j:=0 to ASource.Fields.Count-1 do
        Fields[j].Value:=ASource.Fields[j].Value;
      Post;
      ASource.Next;
    end;
    First;
  end;
  Result:=true;
end;


Кодировка БД — UTF-8, СУБД — PostgreSQL 12. Если в исходном наборе данных есть строковое поле, то оно обрезается. То есть, для varchar(50) будет DataSet.Field.Size=50, MemTableEh.Field.Size=50, а строка обрежется до 50 байт, а не символов.

По-моему, это неправильное поведение компонента.
Подскажите, пожалуйста, можно поправить?


С уважением
Евгений.

Автор:  EhLibSupport [ 24 янв 2023, 15:04 ]
Заголовок сообщения:  Re: TMemTableEh: длина строковых полей в UTF-8

Добрый день.

В документации по FPC - TFieldDefs.Add
https://www.freepascal.org/docs-html/fc ... s.add.html
нет точного определения понятия Size (Байты, Символы) для типа ftString.

Если считать, что TBufDataset это стандартный DataSet, который работает правильно.
То следующий код:

Код:
procedure TForm1.Button1Click(Sender: TObject);
var
  BufDataset1: TBufDataset;
begin
  BufDataset1 := TBufDataset.Create(Self);

  BufDataset1.FieldDefs.Add('Str5Field', ftString, 5);
  BufDataset1.CreateDataset;

  BufDataset1.Append;
  BufDataset1.FieldByName('Str5Field').AsString := 'АБВГДЕЁЖЗ';
  BufDataset1.Post;

  ShowMessage(BufDataset1.FieldByName('Str5Field').AsString);

end;


Дает результат 'АБ'

Значит, можно предположить, что Size задает размер в байтах.

Автор:  edgen [ 24 янв 2023, 16:36 ]
Заголовок сообщения:  Re: TMemTableEh: длина строковых полей в UTF-8

Всё так, в определении FieldDefs есть неопределённость. Однако в определении свойства Size типа TStringField указано чётко (выделено мной):
Цитата:
Description

Size is made published by the TStringField class so it can be set in the IDE: it is the declared maximum size of the string (in characters) and is used to calculate the size of the dataset buffer.
(https://www.freepascal.org/docs-html/fc ... .size.html)

Более того, компоненты доступа к серверам баз данных интерпретируют поле именно так, поэтому у меня и получилось, что в DataSet (непосредственно в этом месте TZQuery, но попробовал и со "стандартной" TSQLQuery) хранится полная строка, а в MemTableEh — нет.

Чуть позже проверю, как себя в этом отношении ведёт MemTableEh при подключении через TSQLDataDriverEh, хотя внутренний массив записей создаётся одинаково, скорее всего.

С уважением
Евгений.

Автор:  Alexsashka [ 25 янв 2023, 11:57 ]
Заголовок сообщения:  Re: TMemTableEh: длина строковых полей в UTF-8

А вот такое не подойдет?

procedure copyQryStructure(const mt: TMemTableEh; qry: TZquery);
var
i: integer;
begin
try
for i := 0 to qry.FieldCount - 1 do
begin
mt.FieldDefs.Add(qry.Fields[i].FieldName, qry.Fielddefs[i].DataType, qry.Fields[i].Size);
mt.FieldDefs[i].Attributes := qry.Fielddefs[i].Attributes;
mt.FieldDefs[i].Required := false; //Для версий выше 4.5.62
end;
mt.CreateDataSet;
mt.append;
mt.edit;
except
on e: exception do
begin
WriteToLog(progdir + 'error.log', datetimetostr(now) + ', Error № 10 ' + e.Message);
raise;
end;
end;
end;

procedure copyQryData(const mt: TMemTableEh; qry: TZquery);
var
i: integer;
begin
if not qry.IsEmpty then
try
screen.Cursor := crAppStart;
if not mt.Active then
mt.Active := true;
mt.First;
mt.Edit;
for i := 0 to qry.FieldCount - 1 do
begin
mt.Fields[i].Value := qry.Fields[i].Value;
end;
mt.Post;
screen.Cursor := crDefault;
except
on e: exception do
begin
screen.Cursor := crDefault;
WriteToLog(progdir + 'error.log', datetimetostr(now) + ', Error № 11B ' + e.Message);
raise;
end;
end;
end;

Автор:  edgen [ 25 янв 2023, 14:34 ]
Заголовок сообщения:  Re: TMemTableEh: длина строковых полей в UTF-8

Alexsashka писал(а):
А вот такое не подойдет?

Так это то же самое получается.

Цитата:
mt.FieldDefs.Add(qry.Fields[i].FieldName, qry.Fielddefs[i].DataType, qry.Fields[i].Size);

Field.Size переносится корректно, проблема в интерпретации этого свойства. Например, родной TStringField при формировании размера блоков памяти делает так:
Код:
function TStringField.GetDataSize: Integer;
begin
  case FCodePage of
    CP_UTF8: Result := 4*Size+1;
    else     Result :=   Size+1;
  end;
end;

Пока подробно в коде EhLib не разбирался, а просто пошаговое выполнение не помогает, из-за захода в RTL (кстати, в Delphi, кажется, при пошаговом выполнении можно было в стандартные библиотеки заходить?), но, кажется, интерпретация Size как количества байт всё-таки в коде EhLib.

Цитата:
mt.FieldDefs[i].Required := false; //Для версий выше 4.5.62

А это у меня отдельная процедура делает. Иногда просто нужно очистить обязательность полей в датасете.

С уважением
Евгений.

Автор:  edgen [ 31 янв 2023, 16:46 ]
Заголовок сообщения:  Re: TMemTableEh: длина строковых полей в UTF-8

Приветствую!

К сожалению, поведение компонента при подключении через DataDriver точно такое же.
И если для "автономных" MemTable всё действительно довольно просто (установить размер строки самостоятельно при копировании структуры), то при работе через DataDriver обрезаются строки из БД и что с этим делать — непонятно.

Если изменений в логику работы со строковыми полями вносить не планируется, подскажите, пожалуйста, в каком методе производится проверка, попробую перекрыть этот метод в наследнике.
Спасибо.

С уважением
Евгений.

Автор:  Python [ 10 фев 2023, 13:11 ]
Заголовок сообщения:  Re: TMemTableEh: длина строковых полей в UTF-8

Переводите строки в WideString. Ниже напишу, как это сделать быстро.
У меня с UTF8 другая проблема - в AstraLinux - на большом объеме данных с текстовыми полями, серьёзно заполненными, при работе через MemTableEh периодически случаются вылеты при фильтрации или группировке данных в DBGridEh либо при переоткрытии набора данных (в т.ч. по установке фильтра на "базовый" источник данных - у меня ZTable и ZQuery). Под виндой всё нормально, кстати. А вот под openSUSE Leap 15.2 ошибка тоже воспроизвелась.
Судя по симптомам и месту, где вылетает ошибка, это в буферах MemTableEh некорректно выделяется память для значений строковых полей, и при копировании туда данных происходит выход за пределы выделенного сегмента. При этом если в датасете тип поля не StringField, а WideStringField, то ошибки не возникает: там используется другой механизм копирования данных (насколько я успел глянуть исходники).
Я так понимаю, что беда именно в UTF8 - переменное число байт на символ (от 1 до 4), откуда вытекает необходимость в исходнике ставить проверки на компиляцию под Linux и использовать специальные функции для работы с UTF8-строками (они есть).

Пока автор разбирается с указанными проблемами - предлагаю свои временные
РЕКОМЕНДАЦИИ ПО ОБХОДУ ПРОБЛЕМЫ
для тех, кто работает через ZeosLib: в ZConnection для свойства ControlsCodePage указываем cCP_UTF16 - и во всех связанных датасетах строковые поля автоматически будут создаваться как WideString.
Возможные траблы:
1. Если поля в датасетах созданы статически - надо пересоздать (в т.ч. в связанные MemTableEh, если используются).
2. Поля типа text из Memo преобразуются в WideMemo - и тут уже "ломается" MemTableEh, но в новой сборке автор обещал это поправить.
3. В DBGridEh надо обязательно включать опцию DrawMemoText, иначе вместо текста будем видеть "(MEMO)".
4. При работе в DBGridEh с нативной локальной фильтрацией "типа как в Excel" (выпадающие списки в титлах колонок, режим для работы через MemTableEh) - не дает поставить фильтр более чем на одну текстовую колонку. Решается путем написания собственной функции заполнения списка фильтрации и замыкании на нее событий FillSTFilterListValues для гридов. В принципе, ничего сложного там нет.

Автор:  edgen [ 16 фев 2023, 16:09 ]
Заголовок сообщения:  Re: TMemTableEh: длина строковых полей в UTF-8

Приветствую!

Python писал(а):
Судя по симптомам и месту, где вылетает ошибка, это в буферах MemTableEh некорректно выделяется память для значений строковых полей, и при копировании туда данных происходит выход за пределы выделенного сегмента. При этом если в датасете тип поля не StringField, а WideStringField, то ошибки не возникает: там используется другой механизм копирования данных (насколько я успел глянуть исходники).
Я так понимаю, что беда именно в UTF8 - переменное число байт на символ (от 1 до 4), откуда вытекает необходимость в исходнике ставить проверки на компиляцию под Linux и использовать специальные функции для работы с UTF8-строками (они есть).

Насколько я понимаю, само хранение записи в памяти осуществляется как массив вариантов, по крайней мере, именно так определён TRecDataValues. Сильно в код не углублялся. Довольно странно выглядит разное поведение под Windows и Linux, возможно как раз реализация вариантов немного разная? Или в Линуксе UTF-16 по-умолчанию?
Python писал(а):
Пока автор разбирается с указанными проблемами - предлагаю свои временные
РЕКОМЕНДАЦИИ ПО ОБХОДУ ПРОБЛЕМЫ
для тех, кто работает через ZeosLib: в ZConnection для свойства ControlsCodePage указываем cCP_UTF16 - и во всех связанных датасетах строковые поля автоматически будут создаваться как WideString.
Возможные траблы:
1. Если поля в датасетах созданы статически - надо пересоздать (в т.ч. в связанные MemTableEh, если используются).
2. Поля типа text из Memo преобразуются в WideMemo - и тут уже "ломается" MemTableEh, но в новой сборке автор обещал это поправить.
3. В DBGridEh надо обязательно включать опцию DrawMemoText, иначе вместо текста будем видеть "(MEMO)".
4. При работе в DBGridEh с нативной локальной фильтрацией "типа как в Excel" (выпадающие списки в титлах колонок, режим для работы через MemTableEh) - не дает поставить фильтр более чем на одну текстовую колонку. Решается путем написания собственной функции заполнения списка фильтрации и замыкании на нее событий FillSTFilterListValues для гридов. В принципе, ничего сложного там нет.

Спасибо! Выглядит вполне, правда, у меня получилось, что проще отказаться от работы через DataDriver. А вот включение DrawMemoText в гриде — дело при работе с Zeos обязательное, вне зависимости от ControlsCodePage. Дело в том, что, если я в запросе не произведу явное приведение типов для вычисляемой строки, Zeos посчитает это поле ftMemo. Например,
Код:
SELECT
    concat('a','b','c') AS sample_string;

даст поле sample_string типа ftMemo, а
Код:
SELECT
    cast(concat('a','b','c') as VARCHAR(5)) AS sample_string;

задаст полю тип ftString (при этом важно указать длину строки в SQL)

С уважением
Евгений

Автор:  Python [ 16 фев 2023, 19:00 ]
Заголовок сообщения:  Re: TMemTableEh: длина строковых полей в UTF-8

edgen писал(а):
Довольно странно выглядит разное поведение под Windows и Linux, возможно как раз реализация вариантов немного разная? Или в Линуксе UTF-16 по-умолчанию?

В Линуксе как раз по умолчанию UTF8, насколько я помню. Я глубоко в исходники тоже не закапывался - некогда, - но "опытным путем" пока удалось добиться приемлемого результата. Конечно, с учетом всех тех 4-х замечаний, которые я указал.
edgen писал(а):
Дело в том, что, если я в запросе не произведу явное приведение типов для вычисляемой строки, Zeos посчитает это поле ftMemo. Например,

Это не только и не столько Zeos, это из PostgreSQL ноги растут - без явного приведения типа он для строковых функций делает результат типа "text", а не "varchar".

Автор:  EhLibSupport [ 21 фев 2023, 19:08 ]
Заголовок сообщения:  Re: TMemTableEh: длина строковых полей в UTF-8

Добрый день.

В сборку EhLib.VCL 11.0 Build 11.0.013 добавили исправление:
* Исправлено: В Lazarus при создании полей через FieldDefs не учитывается свойство TFieldDef.Codepage для строковых полей. В результате вместо CP_UTF8 создаются поля с кодом 0.

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

Страница 1 из 1 Часовой пояс: UTC
Powered by phpBB® Forum Software © phpBB Group
http://www.phpbb.com/