04.05.2014

UPPER в SQLite и киррилица

Вчера писал маленькую программку на заказ и столкнулся с проблемой, которая, как оказалось, волнует многих программистов, программирующих с использованием SQLite. Речь пойдет о работе SQL-функции UPPER с кириллическими буквами (на самом деле это не столько важно, правильнее будет сказать "с юникодом").
Начну с того, что я использую Delphi XE3. В данной версии (и последующих) реализована поддержка баз данных SQLite при помощи dbExpress. Т.е. никакие сторонние компоненты и модули уже не нужны. Достаточно воспользоваться "родными" компонентами с вкладки dbExpress. Более детально о том, как работать с SQLite в Delphi XE3 описано на webdelphi.ru (есть и другие источники, достаточно спросить у Google). Я же хочу остановиться конкретно на функции UPPER.
SQLite поддерживает стандартную SQL-функцию UPPER. Но, как видно из описания, поддерживаются только ASCII-символы. Т.е. не о какой кириллице речи нет. Это похоже на стандартную Delphi функцию UpperCase - латиницу преобразуем в верхний регистр, кириллицу - нет. Но в Delphi есть еще одна похожая функция - AnsiUpperCase, которая умеет работать и с кириллицей. В SQLite такой функции нет. Что же делать? Решение есть! Нужно научить SQLIte понимать функцию UPPER так, как нужно нам.
В SQLite есть возможность добавлять свои функции и реализовывать их. Делается это при помощи функции sqlite3_create_function. Данная функция описана в модуле System.Sqlite и вызывать ее необходимо только после того, как установлено соединение с базой данных. Я делаю это так:
sqlite3_create_function(Db, 'UPPER', 1, SQLITE_ANY, nil, @SQLiteUpper, nil, nil);
  • Первый параметр имеет тип sqlite3, но как мы можем увидеть в модуле System.Sqlite - это обычный Pointer. По сути это указатель на установленное соединение с базой данных (именно поэтому и необходимо чтобы соединение уже было установлено). Получить данный указатель можно, воспользовавшись классом TDBXSqliteConnection, описанным в модуле Data.DbxSqlite. У данного класса есть свойство ConnectionHandle, которое и возвращает нужный нам указатель. Получить экземпляр TDBXSqliteConnection можно через свойство DBXConnection у TSQLiteConnection. Таким образом:
    Db := (Connection.DBXConnection as TDBXSqliteConnection).ConnectionHandle;
    где Connection: TSQLiteConnection
  • Второй параметр - это и есть имя той функции, которую мы добавляем и которую мы потом можем использовать в SQL-запросах.
  • Шестой параметр - это ссылка на код, который будет непосредственно обрабатывать нашу добавляемую функцию. В нашем случае это должен быть код, который преобразует символы к верхнему регистру. Но тут так же есть свои нюансы. Я не будут вдаваться в подробности, а просто опишу тут этот код:
    procedure SQLiteUpper(Context: Pointer; Arg: Integer; Args: PPChar); cdecl;
    var
      S: string;
    begin
      S := UTF8ToWideString(sqlite3_value_text(Args^));
      CharUpperBuff(@S[1], S.Length);
      sqlite3_result_text16(Context, PChar(S), S.Length, nil);
    end;
    
Теперь можно пользоваться функцией UPPER в SQL-запросах и для кириллических символов.


2 комментария:

  1. почему бросили писать статьи? у вас хорошо получается.

    ОтветитьУдалить
    Ответы
    1. Сменил ориентацию, так сказать :)
      Delphi перестал быть популярным (хоть и не перестал для меня быть любимым). Пришлось перепрофилироваться на мобильную разработку под Swift и на 1С.

      Удалить