unit __SQLiteUpper;

interface

Uses
  Classes,
  SysUtils;

{
  2024.02.22; roamer55.ru.
  По следам статьи
  «Полноценный upper (или lower) в SQLite при работе с unicode» (автор, к сожалению, не известен)
  Ссылка на статью: https://habr.com/ru/sandbox/98493/
}

//----------------
//формирование SQL-запроса (UPPER) для списка заданных поисковых слов по значениям заданного поля таблицы БД (LIKE)
function ListWords_Filter_Create(fn:string; ListWords:TStrings; ListRes:TStrings; nrAndOr:boolean=true; tnAlias: string = 't9z'):integer;
//Сформировать фильтр для одного слова
function WordFilter_Create(fn:string; sWord:string; NrVar:integer=0; tnAlias: string = 't9z'):string;
//----------------
//----------------
//Формирование части SQL-запроса для одного поля таблицы БД.
//Идея взята отсюда:  https://habr.com/ru/sandbox/98493/
function SQLite_Upper(fn:string; ListRes:TStrings; tnAlias:string='t9z'):integer; overload;
//Формирование части SQL-запроса для одного поля таблицы БД.
function SQLite_Upper(fn:string; tnAlias: string = 't9z'):string; overload;
//----------------
//----------------
//Вспомогательная
function SQLite_Upper_Char(sChar:string; sUnion:string='union'):string;
//нормализовать строку для SQL-запроса
function PrepareForSQL(Str: String): String;
//----------------

implementation

function SQLite_Upper(fn:string; tnAlias: string = 't9z'):string;
//Формирование части SQL-запроса для одного поля таблицы БД.
Var
  ListRes:TStrings;
begin
  Result:='';
  ListRes := TStringList.Create;
  TRY
     if SQLite_Upper(fn, ListRes, tnAlias)>0 then begin
        Result:=trim(ListRes.Text);
     end;
  FINALLY
    FreeAndNil(ListRes);
  END;
end;

function SQLite_Upper(fn:string; ListRes:TStrings; tnAlias:string='t9z'):integer;
//Формирование части SQL-запроса для одного поля таблицы БД.
//Идея взята отсюда:  https://habr.com/ru/sandbox/98493/
begin
  Result:=-1;
  if Assigned(ListRes) then begin
     ListRes.Clear;
     fn:=trim(fn);
     tnAlias:=trim(tnAlias);
     if tnAlias='' then tnAlias := 't9z';
     ListRes.Add('(');
     ListRes.Add('WITH RECURSIVE');
     ListRes.Add('under_name('+fn+', char, level) as');
     ListRes.Add('(');
     ListRes.Add('select '+tnAlias+'.'+fn+', '+#39+#39+', 0');
     ListRes.Add('union');
     ListRes.Add('select '+fn+', coalesce(lu.u,substr('+fn+',level,1)), under_name.level+1');
     ListRes.Add('from under_name');
     ListRes.Add('left join ');
     ListRes.Add('(');
     ListRes.Add(SQLite_Upper_Char('А', ''));
     ListRes.Add(SQLite_Upper_Char('Б','union'));
     ListRes.Add(SQLite_Upper_Char('В','union'));
     ListRes.Add(SQLite_Upper_Char('Г','union'));
     ListRes.Add(SQLite_Upper_Char('Д','union'));
     ListRes.Add(SQLite_Upper_Char('Е','union'));
     ListRes.Add(SQLite_Upper_Char('Ё','union'));
     ListRes.Add(SQLite_Upper_Char('Ж','union'));
     ListRes.Add(SQLite_Upper_Char('З','union'));
     ListRes.Add(SQLite_Upper_Char('И','union'));
     ListRes.Add(SQLite_Upper_Char('Й','union'));
     ListRes.Add(SQLite_Upper_Char('К','union'));
     ListRes.Add(SQLite_Upper_Char('Л','union'));
     ListRes.Add(SQLite_Upper_Char('М','union'));
     ListRes.Add(SQLite_Upper_Char('Н','union'));
     ListRes.Add(SQLite_Upper_Char('О','union'));
     ListRes.Add(SQLite_Upper_Char('П','union'));
     ListRes.Add(SQLite_Upper_Char('Р','union'));
     ListRes.Add(SQLite_Upper_Char('С','union'));
     ListRes.Add(SQLite_Upper_Char('Т','union'));
     ListRes.Add(SQLite_Upper_Char('У','union'));
     ListRes.Add(SQLite_Upper_Char('Ф','union'));
     ListRes.Add(SQLite_Upper_Char('Х','union'));
     ListRes.Add(SQLite_Upper_Char('Ц','union'));
     ListRes.Add(SQLite_Upper_Char('Ч','union'));
     ListRes.Add(SQLite_Upper_Char('Ш','union'));
     ListRes.Add(SQLite_Upper_Char('Щ','union'));
     ListRes.Add(SQLite_Upper_Char('Ъ','union'));
     ListRes.Add(SQLite_Upper_Char('Ы','union'));
     ListRes.Add(SQLite_Upper_Char('Ь','union'));
     ListRes.Add(SQLite_Upper_Char('Э','union'));
     ListRes.Add(SQLite_Upper_Char('Ю','union'));
     ListRes.Add(SQLite_Upper_Char('Я','union'));
     ListRes.Add(')');
     ListRes.Add('lu on substr('+fn+',level,1)=lu.l');
     ListRes.Add('where level <= length('+fn+')');
     ListRes.Add(')');
     ListRes.Add('select group_concat(char,'+#39+''+#39+') from under_name');
     ListRes.Add(')');
     Result:=ListRes.Count;
  end;
end;

function ListWords_Filter_Create(fn:string; ListWords:TStrings; ListRes:TStrings; nrAndOr:boolean=true; tnAlias: string = 't9z'):integer;
//формирование SQL-запроса (UPPER) для списка заданных поисковых слов по значениям заданного поля таблицы БД (LIKE)
Var
  Sx, sWord:string;
  iWord:integer;
begin
  Result:=-1;
  if Assigned(ListRes) then begin
     ListRes.Clear;
     fn:=trim(fn);
     if fn<>'' then begin
        iWord:=-1;
        while iWord<(ListWords.Count-1) do
         begin
          iWord:=iWord+1;
          sWord:=trim(ListWords[iWord]);
          if sWord<>'' then begin
             Sx:=trim(WordFilter_Create(fn, sWord, 0)); //Используем LIKE
             if Sx<>'' then begin
                if nrAndOr then begin
                   if ListRes.Count>0 then ListRes.Add(' and ');
                end
                else begin
                   if ListRes.Count>0 then ListRes.Add(' or ');
                end;
                ListRes.Add(' '+Sx+' ');
             end;
          end;
        end;
     end;
     Result:=ListRes.Count
  end;
end;

function WordFilter_Create(fn:string; sWord:string; NrVar:integer=0; tnAlias: string = 't9z'):string;
//Сформировать фильтр для одного слова
begin
  Result:='';
  sWord:=trim(sWord);
  if sWord<>'' then begin
     fn:=trim(fn);
     if fn<>'' then begin
        Result:='(';
        sWord:=AnsiUpperCase(sWord);
        fn:=SQLite_Upper(fn, tnAlias); //вызов функции SQLite_Upper !!!
        sWord:=PrepareForSQL(sWord);   //обработка одинарных кавычек
        if (NrVar =0) then begin //LIKE
           Result:=Result+' '+fn + ' like '+#39+sWord+#39;
        end
        else begin // =
           Result:=Result+' '+fn + ' = '+#39+sWord+#39;
        end;
        Result:=Result+')';
     end;
  end;
end;

function SQLite_Upper_Char(sChar:string; sUnion:string='union'):string;
//Вспомогательная
begin
  Result:='';
  if length(sChar)=1 then begin
     sUnion:=trim(sUnion);
     Result:=sUnion+' select '+#39+AnsiUpperCase(sChar)+#39 + ' as u, ' + #39+AnsiLowerCase(sChar)+#39+' as l';
  end;
end;

function ReplaceSymbol(str: string; oldsym, newsym: string): String;
//Замена символов в строке
var i, j: integer;
  repstr, R: string;
  s1, s2: String;
begin
  s1 := oldsym;
  s2 := Str;
  i := pos(s1, s2);
  if i > 0 then begin
    R := '';
    RepStr := Str;
    while i > 0 do begin
      R := R + copy(Repstr, 1, i - 1) + newsym;
      repstr := copy(repstr, i + length(oldsym), length(Repstr));
      i := pos(oldsym, Repstr);
    end;
    Result := R + RepStr;
  end else
    Result := Str;
end;

function PrepareForSQL(Str: String): String;
//нормализовать строку для SQL-запроса
begin
  Result := ReplaceSymbol(Str, '''', '''''');
end;



end.