"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 (https://www.freepascal.org/docs-html/fc ... .size.html)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. Более того, компоненты доступа к серверам баз данных интерпретируют поле именно так, поэтому у меня и получилось, что в 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/ |