{-------------------------------------------------------------------------------
 
 Copyright (c) 1999-2021 Ralf Junker, Yunqa
 Internet: https://www.yunqa.de
 E-Mail:   delphi@yunqa.de

-------------------------------------------------------------------------------}

unit DISQLite3Functions;

{$I DICompilers.inc}
{$I DISQLite3.inc}

{$IFDEF DISQLite3_Personal}
!!! This file requires functionality unavailable in DISQLite3 Personal. !!!
!!! To compile, download DISQLite3 Pro from www.yunqa.de.               !!!
{$ENDIF DISQLite3_Personal}

interface

uses
  DISystemCompat,
  {$IFDEF HAS_UNITSCOPE}
  System.SysUtils,
  {$ELSE HAS_UNIT_SCOPE}
  SysUtils,
  {$ENDIF HAS_UNIT_SCOPE}
  DISQLite3Api;

procedure sqlite3_create_math_functions(
  const DB: sqlite3_ptr);

procedure sqlite3_function_acos(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_asin(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_atan(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_atan2(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_ceiling(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_cos(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_cot(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_degrees(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_exp(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_floor(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_ln(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_logN(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_log2(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_log10(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_mod(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_pi(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_pow(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_radians(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_sign(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_sin(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_sqrt(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_tan(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

procedure sqlite3_function_truncate(
  pCtx: sqlite3_context_ptr;
  nArgs: Integer;
  Args: sqlite3_value_ptr_array_ptr);

implementation

uses
  {$IFDEF HAS_UNITSCOPE}
  System.SysConst, System.Math
  {$ELSE HAS_UNIT_SCOPE}
  SysConst, Math
  {$ENDIF HAS_UNIT_SCOPE}
  ;

function ModDouble(const x, y: Double): Double;
var
  z: Double;
begin
  Result := x / y;
  z := System.Trunc(Result);
  if Frac(Result) < 0.0 then
    z := z - 1.0;
  Result := x - y * z;
end;

const
  WRONG_ARGUMENT_TYPE = 'Wrong argument type';

procedure sqlite3_result_argument_error(const pCtx: sqlite3_context_ptr);
begin
  sqlite3_result_error(pCtx, WRONG_ARGUMENT_TYPE, Length(WRONG_ARGUMENT_TYPE));
end;

procedure sqlite3_function_acos(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p: Pointer;
  v: Double;
begin
  p := Args[0];
  case sqlite3_value_type(p) of
    SQLITE_INTEGER, SQLITE_FLOAT:
      begin
        v := sqlite3_value_double(p);
        if (v < -1.0) or (v > 1.0) then
          sqlite3_result_null(pCtx)
        else
          sqlite3_result_double(pCtx, ArcCos(v));
      end;
  else
    sqlite3_result_argument_error(pCtx);
  end;
end;

procedure sqlite3_function_asin(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p: Pointer;
  v: Double;
begin
  p := Args[0];
  case sqlite3_value_type(p) of
    SQLITE_INTEGER, SQLITE_FLOAT:
      begin
        v := sqlite3_value_double(p);
        if (v < -1.0) or (v > 1.0) then
          sqlite3_result_null(pCtx)
        else
          sqlite3_result_double(pCtx, ArcSin(v));
      end;
  else
    sqlite3_result_argument_error(pCtx);
  end;
end;

procedure sqlite3_function_atan(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p: Pointer;
begin
  p := Args[0];
  case sqlite3_value_type(p) of
    SQLITE_INTEGER, SQLITE_FLOAT:
      begin
        sqlite3_result_double(pCtx, ArcTan(sqlite3_value_double(p)));
      end;
  else
    sqlite3_result_argument_error(pCtx);
  end;
end;

procedure sqlite3_function_atan2(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p1, p2: Pointer;
begin
  p1 := Args[0];
  case sqlite3_value_type(p1) of
    SQLITE_INTEGER, SQLITE_FLOAT:
      begin
        p2 := Args[1];
        case sqlite3_value_type(p2) of
          SQLITE_INTEGER, SQLITE_FLOAT:
            begin
              sqlite3_result_double(pCtx, ArcTan2(sqlite3_value_double(p1), sqlite3_value_double(p2)));
              Exit;
            end;
        end;
      end;
  end;
  sqlite3_result_argument_error(pCtx);
end;

procedure sqlite3_function_ceiling(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p: Pointer;
begin
  p := Args[0];
  case sqlite3_value_type(p) of
    SQLITE_INTEGER, SQLITE_FLOAT:
      sqlite3_result_int64(pCtx, Ceil(sqlite3_value_double(p)));
  else
    sqlite3_result_argument_error(pCtx);
  end;
end;

procedure sqlite3_function_cos(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p: Pointer;
begin
  p := Args[0];
  case sqlite3_value_type(p) of
    SQLITE_INTEGER, SQLITE_FLOAT:
      sqlite3_result_double(pCtx, Cos(sqlite3_value_double(p)));
  else
    sqlite3_result_argument_error(pCtx);
  end;
end;

procedure sqlite3_function_cot(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p: Pointer;
begin
  p := Args[0];
  case sqlite3_value_type(p) of
    SQLITE_INTEGER, SQLITE_FLOAT:
      try
        sqlite3_result_double(pCtx, CoTan(sqlite3_value_double(p)));
      except
        on e: EMathError do
          sqlite3_result_null(pCtx);
      end;
  else
    sqlite3_result_argument_error(pCtx);
  end;
end;

procedure sqlite3_function_degrees(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p: Pointer;
begin
  p := Args[0];
  case sqlite3_value_type(p) of
    SQLITE_INTEGER, SQLITE_FLOAT:
      sqlite3_result_double(pCtx, sqlite3_value_double(p) * 180.0 / Pi);
  else
    sqlite3_result_argument_error(pCtx);
  end;
end;

procedure sqlite3_function_exp(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p: Pointer;
begin
  p := Args[0];
  case sqlite3_value_type(p) of
    SQLITE_INTEGER, SQLITE_FLOAT:
      sqlite3_result_double(pCtx, Exp(sqlite3_value_double(p)))
  else
    sqlite3_result_argument_error(pCtx);
  end;
end;

procedure sqlite3_function_floor(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p: Pointer;
begin
  p := Args[0];
  case sqlite3_value_type(p) of
    SQLITE_INTEGER, SQLITE_FLOAT:
      sqlite3_result_int64(pCtx, Floor(sqlite3_value_double(p)));
  else
    sqlite3_result_argument_error(pCtx);
  end;
end;

procedure sqlite3_function_ln(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p: Pointer;
  v: Double;
begin
  p := Args[0];
  case sqlite3_value_type(p) of
    SQLITE_INTEGER, SQLITE_FLOAT:
      begin
        v := sqlite3_value_double(p);
        if v > 0.0 then
          sqlite3_result_double(pCtx, Ln(v))
        else
          sqlite3_result_null(pCtx);
      end;
  else
    sqlite3_result_argument_error(pCtx);
  end;
end;

procedure sqlite3_function_log10(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p: Pointer;
  v: Double;
begin
  p := Args[0];
  case sqlite3_value_type(p) of
    SQLITE_INTEGER, SQLITE_FLOAT:
      begin
        v := sqlite3_value_double(p);
        if v > 0.0 then
          sqlite3_result_double(pCtx, log10(v))
        else
          sqlite3_result_null(pCtx);
      end;
  else
    sqlite3_result_argument_error(pCtx);
  end;
end;

procedure sqlite3_function_log2(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p: Pointer;
  v: Double;
begin
  p := Args[0];
  case sqlite3_value_type(p) of
    SQLITE_INTEGER, SQLITE_FLOAT:
      begin
        v := sqlite3_value_double(p);
        if v > 0.0 then
          sqlite3_result_double(pCtx, Log2(v))
        else
          sqlite3_result_null(pCtx);
      end;
  else
    sqlite3_result_argument_error(pCtx);
  end;
end;

procedure sqlite3_function_logN(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
label
  ResultNull;
var
  p1, p2: Pointer;
  v1, v2: Double;
begin
  p1 := Args[0];
  if sqlite3_value_type(p1) in [SQLITE_INTEGER, SQLITE_FLOAT] then
    begin
      v1 := sqlite3_value_double(p1);
      if (v1 > 0.0) and (v1 <> 1.0) then
        begin
          p2 := Args[1];
          if sqlite3_value_type(p2) in [SQLITE_INTEGER, SQLITE_FLOAT] then
            begin
              v2 := sqlite3_value_double(p2);
              if v2 > 0.0 then
                sqlite3_result_double(pCtx, LogN(v1, v2))
              else
                ResultNull:
                sqlite3_result_null(pCtx);
              Exit;
            end;
        end
      else
        goto ResultNull;
    end;
  sqlite3_result_argument_error(pCtx);
end;

procedure sqlite3_function_mod(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
label
  1;
var
  p1, p2: Pointer;
begin
  p1 := Args[0];
  case sqlite3_value_type(p1) of

    SQLITE_INTEGER:
      begin
        p2 := Args[1];
        case sqlite3_value_type(p2) of
          SQLITE_INTEGER:
            begin
              sqlite3_result_int64(pCtx, sqlite3_value_int64(p1) mod sqlite3_value_int64(p2));
              Exit;
            end;
          SQLITE_FLOAT:
            goto 1;
        end;
      end;

    SQLITE_FLOAT:
      begin
        p2 := Args[1];
        case sqlite3_value_type(p2) of
          SQLITE_INTEGER, SQLITE_FLOAT:
            begin
              1:
              sqlite3_result_double(pCtx, ModDouble(sqlite3_value_double(p1), sqlite3_value_double(p2)));
              Exit;
            end;
        end;
      end;
  end;
  sqlite3_result_argument_error(pCtx);
end;

procedure sqlite3_function_pi(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
begin
  sqlite3_result_double(pCtx, Pi);
end;

procedure sqlite3_function_pow(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
label
  ResultNull;
var
  p1, p2: Pointer;
begin
  p1 := Args[0];
  if sqlite3_value_type(p1) in [SQLITE_INTEGER, SQLITE_FLOAT] then
    begin
      p2 := Args[1];
      if sqlite3_value_type(p2) in [SQLITE_INTEGER, SQLITE_FLOAT] then
        begin
          try
            sqlite3_result_double(pCtx, Power(sqlite3_value_double(p1), sqlite3_value_double(p2)));
          except
            on e: EMathError do
              sqlite3_result_null(pCtx);
          end;
          Exit;
        end;
    end;
  sqlite3_result_argument_error(pCtx);
end;

procedure sqlite3_function_radians(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p: Pointer;
begin
  p := Args[0];
  case sqlite3_value_type(p) of
    SQLITE_INTEGER, SQLITE_FLOAT:
      sqlite3_result_double(pCtx, sqlite3_value_double(p) * Pi / 180.0);
  else
    sqlite3_result_argument_error(pCtx);
  end;
end;

procedure sqlite3_function_sign(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p: Pointer;
  d: Double;
  i: Int64;
  r: Integer;
begin
  p := Args[0];
  case sqlite3_value_type(p) of

    SQLITE_INTEGER:
      begin
        i := sqlite3_value_int64(p);
        if i < 0 then
          r := -1
        else
          if i > 0 then
            r := 1
          else
            r := 0;
        sqlite3_result_int(pCtx, r);
      end;

    SQLITE_FLOAT:
      begin
        d := sqlite3_value_double(p);
        if d < 0 then
          r := -1
        else
          if d > 0 then
            r := 1
          else
            r := 0;
        sqlite3_result_int(pCtx, r);
      end;

  else
    sqlite3_result_argument_error(pCtx);
  end;
end;

procedure sqlite3_function_sin(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p: Pointer;
begin
  p := Args[0];
  case sqlite3_value_type(p) of
    SQLITE_INTEGER, SQLITE_FLOAT:
      sqlite3_result_double(pCtx, Sin(sqlite3_value_double(p)));
  else
    sqlite3_result_argument_error(pCtx);
  end;
end;

procedure sqlite3_function_sqrt(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p: Pointer;
  v: Double;
begin
  p := Args[0];
  case sqlite3_value_type(p) of
    SQLITE_INTEGER, SQLITE_FLOAT:
      begin
        v := sqlite3_value_double(p);
        if v >= 0.0 then
          sqlite3_result_double(pCtx, Sqrt(v))
        else
          sqlite3_result_null(pCtx);
      end;
  else
    sqlite3_result_argument_error(pCtx);
  end;
end;

procedure sqlite3_function_tan(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
var
  p: Pointer;
begin
  p := Args[0];
  case sqlite3_value_type(p) of
    SQLITE_INTEGER, SQLITE_FLOAT:
      sqlite3_result_double(pCtx, Tan(sqlite3_value_double(p)));
  else
    sqlite3_result_argument_error(pCtx);
  end;
end;

procedure sqlite3_function_truncate(pCtx: sqlite3_context_ptr; nArgs: Integer; Args: sqlite3_value_ptr_array_ptr);
const
  PowerInt10: array[1..18] of Int64 = (
    10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000,
    10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000,
    1000000000000000, 10000000000000000, 100000000000000000, 1000000000000000000);
var
  p: Pointer;
  e, e10: Extended;
  i, i10: Int64;
  d: Integer;
begin
  p := Args[0];
  case sqlite3_value_type(p) of
    SQLITE_NULL:
      begin
        sqlite3_result_null(pCtx);
      end;

    SQLITE_INTEGER:
      begin
        i := sqlite3_value_int(p);
        if nArgs = 2 then
          begin
            d := -sqlite3_value_int(Args[1]);
            if d > 0 then
              begin
                if d > High(PowerInt10) then
                  d := High(PowerInt10);
                i10 := PowerInt10[d];
                i := i div i10 * i10;
              end;
          end;
        sqlite3_result_int64(pCtx, i);
      end;

  else
    e := sqlite3_value_double(p);
    if nArgs = 2 then
      begin
        d := sqlite3_value_int(Args[1]);
        if d > 20 then
          d := 20
        else
          if d < -20 then
            d := -20;
      end
    else
      begin
        d := 0;
      end;

    if d = 0 then
      e := System.Trunc(e)
    else
      begin
        e10 := Power10(d);
        e := System.Trunc(e * e10) / e10
      end;
    sqlite3_result_double(pCtx, e);
  end;
end;

procedure sqlite3_create_math_functions(const DB: sqlite3_ptr);
begin
  if Assigned(DB) then
    begin
      sqlite3_check(sqlite3_create_function(DB, 'acos', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_acos, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'asin', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_asin, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'atan', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_atan, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'atan', 2,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_atan2, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'atan2', 2,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_atan2, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'ceil', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_ceiling, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'ceiling', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_ceiling, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'cos', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_cos, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'cot', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_cot, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'degrees', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_degrees, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'exp', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_exp, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'floor', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_floor, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'ln', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_ln, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'log', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_ln, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'log', 2,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_logN, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'log2', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_log2, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'log10', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_log10, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'mod', 2,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_mod, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'pi', 0,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_pi, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'pow', 2,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_pow, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'radians', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_radians, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'sign', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_sign, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'sin', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_sin, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'sqrt', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_sqrt, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'tan', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_tan, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'truncate', 1,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_truncate, nil, nil), DB);
      sqlite3_check(sqlite3_create_function(DB, 'truncate', 2,
        SQLITE_DETERMINISTIC, nil, sqlite3_function_truncate, nil, nil), DB);
    end;
end;

end.

