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

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

unit DISQLite3Database;

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

interface

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

{$IFDEF COMPILER_16_UP}
const
  DISQLITE3_PLATFORMS = pidWin32 or pidWin64;
  {$ENDIF COMPILER_16_UP}

type
  TDISQLite3Statement = class;

  TDISQLite3StatementClass = class of TDISQLite3Statement;

  TDISQLite3Option = (

    oUriFileName);

  TDISQLite3OptionSet = set of TDISQLite3Option;

  TDISQLite3AccessOption = (

    aoDefault,

    aoReadOnly,

    aoMemory);

  TDISQLite3CacheOption = (

    coDefault,

    coSharedCache,

    coPrivateCache);

  TDISQLite3MutexOption = (

    moDefault,

    moNoMutex,

    moFullMutex);

  TDISQLite3TransactionType = (
    ttDeferred,
    ttImmediate,
    ttExclusive);

  {$IFDEF COMPILER_16_UP}
  [ComponentPlatformsAttribute(DISQLITE3_PLATFORMS)]
  {$ENDIF COMPILER_16_UP}
  TDISQLite3Database = class(TComponent)
  private
    FAfterConnect: TNotifyEvent;
    FAfterCreateDatabase: TNotifyEvent;
    FAfterDisconnect: TNotifyEvent;
    FBeforeConnect: TNotifyEvent;
    FBeforeCreateDatabase: TNotifyEvent;
    FBeforeDisconnect: TNotifyEvent;
    FDatabaseName: UnicodeString;
    FHandle: sqlite3_ptr;
    FInitDatabase: TNotifyEvent;
    FOpenFlags: C_int;
    FOptions: TDISQLite3OptionSet;
    {$IFDEF SQLITE_HAS_CODEC}
    FPassword: RawByteString;
    {$ENDIF SQLITE_HAS_CODEC}
    FStatements: TList;
    {$IFDEF SQLITE_THREADSAFE}
    FStatementsMutex: sqlite3_mutex_ptr;
    {$ENDIF SQLITE_THREADSAFE}
    FStreamedConnected: Boolean;
    FTransactionCount: Integer;
    FVfs: UnicodeString;

    procedure CheckActive;

    procedure CheckDatabaseName(
      const DatabaseName: UnicodeString);

    procedure CheckInactive;

    function GetAutoVacuum: Boolean;
    procedure SetAutoVacuum(
      const AValue: Boolean);

    function GetBusyTimeout: C_int;
    procedure SetBusyTimeout(
      const Value: C_int);

    function GetCacheSize: Integer;
    procedure SetCacheSize(
      const AValue: Integer);

    function GetCaseSensitiveLike: Boolean;
    procedure SetCaseSensitiveLike(
      const AValue: Boolean);

    function GetConnected: Boolean;
    procedure SetConnected(
      const Value: Boolean);

    function GetCountChanges: Boolean;
    procedure SetCountChanges(
      const AValue: Boolean);

    function GetEmptyResultCallbacks: Boolean;
    procedure SetEmptyResultCallbacks(
      const AValue: Boolean);

    function GetEncoding: UnicodeString;
    procedure SetEncoding(
      const AValue: UnicodeString);

    function GetInTransaction: Boolean;

    function GetSQLiteVersion: UnicodeString;
    procedure SetSQLiteVersion(
      const Value: UnicodeString);

    function GetDefaultCacheSize: Integer;
    procedure SetDefaultCacheSize(
      const AValue: Integer);

    procedure SetDatabaseName(
      const AValue: UnicodeString);

    function GetPageSize: Integer;
    procedure SetPageSize(
      const AValue: Integer);

    function GetCache: TDISQLite3CacheOption;
    procedure SetCache(
      const Value: TDISQLite3CacheOption);

    function GetMutex: TDISQLite3MutexOption;
    procedure SetMutex(
      const Value: TDISQLite3MutexOption);

    function GetAccess: TDISQLite3AccessOption;
    procedure SetAccess(
      const Value: TDISQLite3AccessOption);

    {$IFDEF SQLITE_HAS_CODEC}
    procedure SetPassword(
      const AValue: RawByteString);
    {$ENDIF SQLITE_HAS_CODEC}

  protected

    procedure CloseStatements;

    procedure DoAfterConnect; dynamic;

    procedure DoAfterCreateDatabase; dynamic;

    procedure DoAfterDisconnect; dynamic;

    procedure DoBeforeConnect; dynamic;

    procedure DoBeforeCreateDatabase; dynamic;

    procedure DoBeforeDisconnect; dynamic;

    procedure DoConnect;

    procedure DoDisconnect;

    procedure DoInitDatabase; dynamic;

    function GetStatementClass: TDISQLite3StatementClass; virtual;

    procedure Loaded; override;

    procedure RegisterStatement(
      const AStatement: TDISQLite3Statement);

    procedure UnRegisterStatement(
      const AStatement: TDISQLite3Statement);

    property StreamedConnected: Boolean
      read FStreamedConnected
      write FStreamedConnected;

  public

    constructor Create(
      AOwner: TComponent); override;

    destructor Destroy; override;

    procedure AttachDataBase16(
      const FileName: UnicodeString;
      DatabaseName: UnicodeString);

    function Changes: Integer;

    function Check(
      const AErrorCode: Integer): Integer;

    procedure Close;

    procedure CreateDatabase; dynamic;

    procedure Commit;

    procedure DetachDatabase(const DatabaseName: Utf8String);

    procedure DetachDatabase16(
      const DatabaseName: UnicodeString);

    procedure Execute(
      const SQL: Utf8String);

    procedure Execute16(
      const SQL: UnicodeString);

    function ReadOnly(
      const DatabaseName: Utf8String = 'main'): C_int;

    function ReadOnly16(
      const DatabaseName: UnicodeString = 'main'): C_int;

    function IntegrityCheck: Integer;

    procedure Interrupt;

    function LastInsertRowID: Int64;

    procedure Open;

    function Prepare(
      const SQL: Utf8String): TDISQLite3Statement;

    function Prepare16(
      const SQL: UnicodeString): TDISQLite3Statement;

    {$IFDEF SQLITE_HAS_CODEC}

    procedure ReKey(
      const ANewPassword: RawByteString);

    {$ENDIF SQLITE_HAS_CODEC}

    procedure Rollback;

    procedure StartTransaction(
      const TransactionType: TDISQLite3TransactionType{$IFDEF SUPPORTS_DEFAULTPARAMS} = ttDeferred{$ENDIF});

    function TotalChanges: Integer;

    procedure Vacuum;

    property AutoVacuum: Boolean
      read GetAutoVacuum
      write SetAutoVacuum;

    property BusyTimeout: C_int
      read GetBusyTimeout
      write SetBusyTimeout;

    property CacheSize: Integer
      read GetCacheSize
      write SetCacheSize;

    property CaseSensitiveLike: Boolean
      read GetCaseSensitiveLike
      write SetCaseSensitiveLike;

    property DefaultCacheSize: Integer
      read GetDefaultCacheSize
      write SetDefaultCacheSize;

    property CountChanges: Boolean
      read GetCountChanges
      write SetCountChanges;

    property EmptyResultCallbacks: Boolean
      read GetEmptyResultCallbacks
      write SetEmptyResultCallbacks;

    property Encoding: UnicodeString
      read GetEncoding
      write SetEncoding;

    property Handle: sqlite3_ptr
      read FHandle;

    property InTransaction: Boolean
      read GetInTransaction;

    property PageSize: Integer
      read GetPageSize
      write SetPageSize;

  published

    property Access: TDISQLite3AccessOption
      read GetAccess
      write SetAccess
      default aoDefault;

    property Cache: TDISQLite3CacheOption
      read GetCache
      write SetCache
      default coDefault;

    property Connected: Boolean
      read GetConnected
      write SetConnected
      default False;

    property DatabaseName: UnicodeString
      read FDatabaseName
      write SetDatabaseName;

    property Mutex: TDISQLite3MutexOption
      read GetMutex
      write SetMutex
      default moDefault;

    property Options: TDISQLite3OptionSet
      read FOptions
      write FOptions
      default [];

    {$IFDEF SQLITE_HAS_CODEC}

    property Password: RawByteString
      read FPassword
      write SetPassword;
    {$ENDIF SQLITE_HAS_CODEC}

    property VFS: UnicodeString
      read FVfs
      write FVfs;

    property SQLiteVersion: UnicodeString
      read GetSQLiteVersion
      write SetSQLiteVersion
      stored False;

    property AfterConnect: TNotifyEvent
      read FAfterConnect
      write FAfterConnect;

    property AfterCreateDatabase: TNotifyEvent
      read FAfterCreateDatabase
      write FAfterCreateDatabase;

    property AfterDisconnect: TNotifyEvent
      read FAfterDisconnect
      write FAfterDisconnect;

    property BeforeConnect: TNotifyEvent
      read FBeforeConnect
      write FBeforeConnect;

    property BeforeCreateDatabase: TNotifyEvent
      read FBeforeCreateDatabase
      write FBeforeCreateDatabase;

    property BeforeDisconnect: TNotifyEvent
      read FBeforeDisconnect
      write FBeforeDisconnect;

    property InitDatabase: TNotifyEvent
      read FInitDatabase
      write FInitDatabase;

  end;

  TDISQLite3Cell8 = packed record

    CellType: Byte;
    case Byte of
      1: (CellInteger: Int64);
      2: (CellFloat: Double);
      3: (CellText: packed record l: Integer; p: PUtf8Char; end);
      4: (CellBlob: packed record l: Integer; p: Pointer; end);
  end;

  PDISQLite3Cell8 = ^TDISQLite3Cell8;

  TDISQLite3Cell16 = packed record

    CellType: Byte;
    case Byte of
      1: (CellInteger: Int64);
      2: (CellFloat: Double);
      3: (CellText: packed record l: Integer; p: PWideChar; end);
      4: (CellBlob: packed record l: Integer; p: Pointer; end);
  end;

  PDISQLite3Cell16 = ^TDISQLite3Cell16;

  TDISQLite3Statement = class
  private
    FAfterClose: TNotifyEvent;
    FAfterOpen: TNotifyEvent;
    FBeforeClose: TNotifyEvent;
    FBeforeOpen: TNotifyEvent;
    FHandle: sqlite3_stmt_ptr;
    FDatabase: TDISQLite3Database;
    FSql8: Utf8String;
    function GetActive: Boolean;

    function GetSql16: UnicodeString;

    procedure SetActive(const AValue: Boolean);

    procedure SetDatabase(const AValue: TDISQLite3Database);

    procedure SetSql8(const AValue: Utf8String);

    procedure SetSql16(const AValue: UnicodeString);

  protected

    function Check(const AErrorCode: Integer): Integer;

    procedure CheckActive;

    procedure CheckInactive;

    procedure DoAfterClose;

    procedure DoAfterOpen;

    procedure DoBeforeClose;

    procedure DoBeforeOpen;

  public

    destructor Destroy; override;

    procedure Assign(const ASource: TDISQLite3Statement);

    procedure Bind_Blob(
      const AParamIdx: Integer;
      const ABlobData: Pointer;
      const ABlobSize: Integer;
      const ADestroy: TSQLite3_Bind_Destructor);

    procedure Bind_Blob_By_Name(
      const AParamName: Utf8String;
      const ABlobData: Pointer;
      const ABlobSize: Integer;
      const ADestroy: TSQLite3_Bind_Destructor);

    procedure Bind_Blob_By_Name16(
      const AParamName: UnicodeString;
      const ABlobData: Pointer;
      const ABlobSize: Integer;
      const ADestroy: TSQLite3_Bind_Destructor);

    procedure Bind_Double(
      const AParamIdx: Integer;
      const AValue: Double);

    procedure Bind_Double_By_Name(
      const AParamName: Utf8String;
      const AValue: Double);

    procedure Bind_Double_By_Name16(
      const AParamName: UnicodeString;
      const AValue: Double);

    procedure Bind_Int(
      const AParamIdx: Integer;
      const AValue: Integer);

    procedure Bind_Int_By_Name(
      const AParamName: Utf8String;
      const AValue: Integer);

    procedure Bind_Int_By_Name16(
      const AParamName: UnicodeString;
      const AValue: Integer);

    procedure Bind_Int64(
      const AParamIdx: Integer;
      const AValue: Int64);

    procedure Bind_Int64_By_Name(
      const AParamName: Utf8String;
      const AValue: Int64);

    procedure Bind_Int64_By_Name16(
      const AParamName: UnicodeString;
      const AValue: Int64);

    procedure Bind_Null(
      const AParamIdx: Integer);

    procedure Bind_Null_By_Name(
      const AParamName: Utf8String);

    procedure Bind_Null_By_Name16(
      const AParamName: UnicodeString);

    function Bind_Parameter_Count: Integer;

    function Bind_Parameter_Index(
      const AParamName: Utf8String): Integer;

    function Bind_Parameter_Index16(
      const ParamName: UnicodeString): Integer;

    function bind_Parameter_Name(
      const AParamIdx: Integer): Utf8String;

    function Bind_Parameter_Name16(
      const AParamIdx: Integer): UnicodeString;

    procedure Bind_Str(
      const ParamIdx: C_int;
      const Value: Utf8String);

    procedure Bind_Str_By_Name(
      const ParamName: Utf8String;
      const Value: Utf8String);

    procedure Bind_Str_By_Name16(
      const AParamName: UnicodeString;
      const AValue: Utf8String);

    procedure Bind_Str16(
      const AParamIdx: Integer;
      const AValue: UnicodeString);

    procedure Bind_Str16_By_Name(
      const AParamName: Utf8String;
      const AValue: UnicodeString);

    procedure Bind_Str16_By_Name16(
      const AParamName: UnicodeString;
      const AValue: UnicodeString);

    procedure Bind_Text(
      const AParamIdx: Integer;
      const AValue: PUtf8Char;
      const AValueLen: Integer;
      const xDestroy: TSQLite3_Bind_Destructor);

    procedure Bind_Text_By_Name(
      const AParamName: Utf8String;
      const AValue: PUtf8Char;
      const AValueLen: Integer;
      const xDestroy: TSQLite3_Bind_Destructor);

    procedure Bind_Text_By_Name16(
      const AParamName: UnicodeString;
      const AValue: PUtf8Char;
      const AValueLen: Integer;
      const xDestroy: TSQLite3_Bind_Destructor);

    {$IFNDEF SQLITE_OMIT_UTF16}

    procedure Bind_Text16(
      const AParamIdx: Integer;
      const AValue: PWideChar;
      const AValueLen: Integer;
      const xDestroy: TSQLite3_Bind_Destructor);
    {$ENDIF SQLITE_OMIT_UTF16}

    {$IFNDEF SQLITE_OMIT_UTF16}

    procedure Bind_Text16_By_Name(
      const AParamName: Utf8String;
      const AValue: PWideChar;
      const AValueLen: Integer;
      const xDestroy: TSQLite3_Bind_Destructor);
    {$ENDIF SQLITE_OMIT_UTF16}

    {$IFNDEF SQLITE_OMIT_UTF16}

    procedure Bind_Text16_By_Name16(
      const AParamName: UnicodeString;
      const AValue: PWideChar;
      const AValueLen: Integer;
      const xDestroy: TSQLite3_Bind_Destructor);
    {$ENDIF SQLITE_OMIT_UTF16}

    procedure Bind_Variant(
      const AParamIdx: Integer;
      const AValue: Variant);

    procedure Bind_Variant_By_Name(
      const AParamName: Utf8String;
      const AValue: Variant);

    procedure Bind_Variant_By_Name16(
      const AParamName: UnicodeString;
      const AValue: Variant);

    procedure Bind_ZeroBlob(
      const ParamIdx: C_int;
      const BlobSize: C_int);

    procedure Bind_ZeroBlob64(
      const ParamIdx: C_int;
      const BlobSize: sqlite3_uint64);

    procedure Clear_Bindings;

    procedure Close;

    function Column_Blob(
      const ColumnIdx: C_int): C_void_ptr;

    function Column_Bytes(
      const AColumnIdx: Integer): Integer;

    {$IFNDEF SQLITE_OMIT_UTF16}

    function Column_Bytes16(
      const AColumnIdx: Integer): Integer;
    {$ENDIF ~SQLITE_OMIT_UTF16}

    function Column_Cell8(
      const AColumnIdx: Integer): TDISQLite3Cell8;

    procedure Column_Cell16(
      const ColumnIdx: Integer;
      out Cell: TDISQLite3Cell16);

    function Column_Count: Integer;

    {$IFDEF SQLITE_ENABLE_COLUMN_METADATA}

    function Column_Database_Name(
      const AColumnIdx: Integer): Utf8String;
    {$ENDIF SQLITE_ENABLE_COLUMN_METADATA}

    {$IFDEF SQLITE_ENABLE_COLUMN_METADATA}

    function Column_Database_Name16(
      const AColumnIdx: Integer): UnicodeString;
    {$ENDIF SQLITE_ENABLE_COLUMN_METADATA}

    {$IFNDEF SQLITE_OMIT_DECLTYPE}

    function Column_DeclType(
      const AColumnIdx: Integer): Utf8String;
    {$ENDIF ~SQLITE_OMIT_DECLTYPE}

    {$IFNDEF SQLITE_OMIT_DECLTYPE}

    function Column_DeclType16(
      const AColumnIdx: Integer): UnicodeString;
    {$ENDIF ~SQLITE_OMIT_DECLTYPE}

    function Column_Double(
      const AColumnIdx: Integer): Double;

    function Column_Int(
      const AColumnIdx: Integer): Integer;

    function Column_Int64(
      const AColumnIdx: Integer): Int64;

    function Column_Name(
      const AColumnIdx: Integer): Utf8String;

    function Column_Name16(
      const AColumnIdx: Integer): UnicodeString;

    {$IFDEF SQLITE_ENABLE_COLUMN_METADATA}

    function Column_Origin_Name(
      const AColumnIdx: Integer): Utf8String;
    {$ENDIF SQLITE_ENABLE_COLUMN_METADATA}

    {$IFDEF SQLITE_ENABLE_COLUMN_METADATA}

    function Column_Origin_Name16(
      const AColumnIdx: Integer): UnicodeString;
    {$ENDIF SQLITE_ENABLE_COLUMN_METADATA}

    function Column_Str(
      const AColumnIdx: Integer): Utf8String;

    function Column_Str16(
      const AColumnIdx: Integer): UnicodeString;

    {$IFDEF SQLITE_ENABLE_COLUMN_METADATA}

    function Column_Table_Name(
      const AColumnIdx: Integer): Utf8String;
    {$ENDIF SQLITE_ENABLE_COLUMN_METADATA}

    {$IFDEF SQLITE_ENABLE_COLUMN_METADATA}

    function Column_Table_Name16(
      const AColumnIdx: Integer): UnicodeString;
    {$ENDIF SQLITE_ENABLE_COLUMN_METADATA}

    function Column_Text(
      const AColumnIdx: Integer): PUtf8Char;

    {$IFNDEF SQLITE_OMIT_UTF16}

    function Column_Text16(
      const AColumnIdx: Integer): PWideChar;
    {$ENDIF ~SQLITE_OMIT_UTF16}

    function Column_Type(
      const ColumnIdx: C_int): C_int;

    procedure Open;

    function Step: Integer;

    function StepAndReset: Integer;

    {$IFDEF SQLITE_ENABLE_COLUMN_METADATA}

    function TableName(
      const AQuoteChar: AnsiChar{$IFDEF SUPPORTS_DEFAULTPARAMS} = SQLITE3_IDENTIFIER_QUOTE_CHAR{$ENDIF}): Utf8String;

    {$ENDIF SQLITE_ENABLE_COLUMN_METADATA}

    {$IFDEF SQLITE_ENABLE_COLUMN_METADATA}

    function TableName16(
      const AQuoteChar: WideChar{$IFDEF SUPPORTS_DEFAULTPARAMS} = SQLITE3_IDENTIFIER_QUOTE_CHAR{$ENDIF}): UnicodeString;

    {$ENDIF SQLITE_ENABLE_COLUMN_METADATA}

    function Reset: Integer;

    property Active: Boolean read GetActive write SetActive default False;

    property Database: TDISQLite3Database read FDatabase write SetDatabase;

    property Handle: sqlite3_stmt_ptr read FHandle;

    property SQL8: Utf8String read FSql8 write SetSql8;

    property SQL16: UnicodeString read GetSql16 write SetSql16 stored False;

    property AfterClose: TNotifyEvent read FAfterClose write FAfterClose;

    property AfterOpen: TNotifyEvent read FAfterOpen write FAfterOpen;

    property BeforeClose: TNotifyEvent read FBeforeClose write FBeforeClose;

    property BeforeOpen: TNotifyEvent read FBeforeOpen write FBeforeOpen;

  end;

  {$IFNDEF SQLITE_OMIT_INCRBLOB}

  TDISQLite3IncrBlobStream = class(TStream)
  private
    FBlobHandle: sqlite3_blob_ptr;
    FFlags: Integer;
    FOffset: Integer;
  protected
    procedure SetSize(NewSize: Integer); override;
  public

    constructor Create(
      const ADBHandle: sqlite3_ptr;
      const ADatabaseName: UnicodeString;
      const ATableName: UnicodeString;
      const AColumnName: UnicodeString;
      const ARowID: Int64;
      const AFlags: Integer); {$IFDEF SUPPORTS_OVERLOAD} overload; {$ENDIF}

    constructor{$IFDEF SUPPORTS_OVERLOAD}Create{$ELSE}CreateDB{$ENDIF}(
      const ADB: TDISQLite3Database;
      const ADatabaseName: UnicodeString;
      const ATableName: UnicodeString;
      const AColumnName: UnicodeString;
      const ARowID: Int64;
      const AFlags: Integer); {$IFDEF SUPPORTS_OVERLOAD} overload; {$ENDIF}
    destructor Destroy; override;
    function Read(var Buffer; Count: Integer): Integer; override;
    function Seek(Offset: Integer; Origin: Word): Integer; override;
    function Write(const Buffer; Count: Integer): Integer; override;
  end;

  {$ENDIF ~SQLITE_OMIT_INCRBLOB}

  TDISQLite3_Callback_ = function(
    const AStmt: sqlite3_stmt_ptr;
    const AUserData: Pointer): Boolean;

procedure DatabaseError(
  const AResStrRec: PResStringRec;
  const AObject: TObject{$IFDEF SUPPORTS_DEFAULTPARAMS} = nil{$ENDIF});

procedure DatabaseErrorFmt(
  const AResStrRec: PResStringRec;
  const AArgs: array of const;
  const AObject: TObject{$IFDEF SUPPORTS_DEFAULTPARAMS} = nil{$ENDIF});

function sqlite3_cell16_as_ansistring(
  const Cell: TDISQLite3Cell16): AnsiString;

function sqlite3_cell16_as_date(
  const ACell: TDISQLite3Cell16): TDateTime;

function sqlite3_cell16_as_datetime(
  const ACell: TDISQLite3Cell16): TDateTime;

function sqlite3_cell16_as_float(
  const ACell: TDISQLite3Cell16): Double;

function sqlite3_cell16_as_integer(
  const Cell: TDISQLite3Cell16): Integer;

function sqlite3_cell16_as_int64(
  const Cell: TDISQLite3Cell16): Int64;

function sqlite3_cell16_as_time(
  const ACell: TDISQLite3Cell16): TDateTime;

function sqlite3_cell16_as_widestring(
  const Cell: TDISQLite3Cell16): UnicodeString;

function sqlite3_exec_with_callback(
  const ADBHandle: sqlite3_ptr;
  const ASQL: Utf8String;
  const ACallback: TDISQLite3_Callback_{$IFDEF SUPPORTS_DEFAULTPARAMS} = nil{$ENDIF};
  const AUserData: Pointer{$IFDEF SUPPORTS_DEFAULTPARAMS} = nil{$ENDIF}): Integer;

function sqlite3_exec_with_callback16(
  const ADBHandle: sqlite3_ptr;
  const ASQL: UnicodeString;
  const ACallback: TDISQLite3_Callback_{$IFDEF SUPPORTS_DEFAULTPARAMS} = nil{$ENDIF};
  const AUserData: Pointer{$IFDEF SUPPORTS_DEFAULTPARAMS} = nil{$ENDIF}): Integer;

function sqlite3_get_boolean(
  const ADB: sqlite3_ptr;
  const ASQL: Utf8String;
  out ABoolean: Boolean): Integer;

function sqlite3_get_boolean16(
  const ADB: sqlite3_ptr;
  const ASQL: UnicodeString;
  out ABoolean: Boolean): Integer;

function sqlite3_get_int(
  const ADB: sqlite3_ptr;
  const ASQL: Utf8String;
  out AInteger: Integer): Integer;

function sqlite3_get_int16(
  const ADB: sqlite3_ptr;
  const ASQL: UnicodeString;
  out AInteger: Integer): Integer;

function sqlite3_get_str(
  const ADB: sqlite3_ptr;
  const ASQL: Utf8String;
  out AValue: Utf8String): Integer;

function sqlite3_get_str16(
  const ADB: sqlite3_ptr;
  const ASQL: UnicodeString;
  out AValue: UnicodeString): Integer;

procedure sqlite3_init_cell8(
  out Cell: TDISQLite3Cell8);

procedure sqlite3_init_cell16(
  out Cell: TDISQLite3Cell16);

function sqlite3_integrity_check(
  const ADB: sqlite3_ptr;
  const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;

{$IFNDEF SQLITE_OMIT_UTF16}
function sqlite3_integrity_check16(
  const ADB: sqlite3_ptr;
  const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
{$ENDIF ~SQLITE_OMIT_UTF16}

procedure sqlite3_finalize_cell8(
  var Cell: TDISQLite3Cell8);

procedure sqlite3_finalize_cell16(
  var Cell: TDISQLite3Cell16);

function sqlite3_pragma_database(
  const ADataBase: Utf8String): Utf8String;

function sqlite3_pragma_database16(
  const ADataBase: UnicodeString): UnicodeString;

function sqlite3_pragma_get_encoding(
  const ADB: sqlite3_ptr;
  out AEncoding: Utf8String;
  const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;

const

  SQLITE3_AFFINITY_TO_TYPE: array[TDISQLite3Affinity] of Integer = (
    SQLITE_INTEGER,
    SQLITE_TEXT,
    SQLITE_BLOB,
    SQLITE_FLOAT,
    SQLITE_FLOAT);

resourcestring

  SCannotWriteStreamError = 'Cannot write to a read-only stream';
  SDatabaseClosed = 'Cannot perform this operation on a closed database';
  SDatabaseNameNotInConnection = 'Database name "%s" not in connection';
  SInvalidDatabaseName = 'Invalid DatabaseName';
  SDatabaseOpen = 'Cannot perform this operation on an open database';
  SEmptySQLStatement = 'Empty SQL Statement';
  SMissingDatabase = 'Missing dabatase';
  SMissingDataSet = 'Missing dataset';
  SStatementClosed = 'Cannot perform this operation on a closed statement';
  SStatementOpen = 'Cannot perform this operation on an open statement';

  {$IFNDEF COMPILER_6_UP}
  SStreamSetSize = 'Error setting stream size';
  {$ENDIF ~COMPILER_6_UP}

implementation

uses
  {$IFDEF HAS_UNITSCOPE}
  {$IFDEF COMPILER_17_UP}System.Types, {$ENDIF}
  Winapi.Windows, System.RTLConsts, System.SysUtils,
  {$ELSE HAS_UNITSCOPE}
  Windows, {$IFDEF HAS_UNIT_RTLCONSTS}RTLConsts{$ELSE}Consts{$ENDIF}, SysUtils,
  {$ENDIF HAS_UNITSCOPE}
  DIUtils;

const
  BOOLEAN_TO_WIDECHAR: array[Boolean] of WideChar = ('0', '1');

function sqlite3_cell16_as_ansistring(const Cell: TDISQLite3Cell16): AnsiString;
var
  l: Integer;
begin
  case Cell.CellType of
    SQLITE_INTEGER:
      Result := AnsiString({$IFDEF SUPPORTS_INT64}IntToStr(Cell.CellInteger){$ELSE}FloatToSqlStr(Cell.CellInteger, '.', 15, 0){$ENDIF});
    SQLITE_FLOAT:
      Result := AnsiString(FloatToSqlStr(Cell.CellFloat{$IFNDEF SUPPORTS_DEFAULTPARAMS}, '.', 15, 9999{$ENDIF}));
    SQLITE_TEXT:
      begin
        l := WideCharToMultiByte(CP_ACP, 0, Cell.CellText.p, Cell.CellText.l, nil, 0, nil, nil);
        SetLength(Result, l);
        WideCharToMultiByte(CP_ACP, 0, Cell.CellText.p, Cell.CellText.l, PAnsiChar(Result), l, nil, nil);
      end;
  else
    Result := '';
  end;
end;

function sqlite3_cell16_as_date(const ACell: TDISQLite3Cell16): TDateTime;
begin
  Result := DISQLite3Api.JulianDateToDate(sqlite3_cell16_as_float(ACell));
end;

function sqlite3_cell16_as_datetime(const ACell: TDISQLite3Cell16): TDateTime;
begin
  Result := DISQLite3Api.JulianDateToDateTime(sqlite3_cell16_as_float(ACell));
end;

function sqlite3_cell16_as_float(const ACell: TDISQLite3Cell16): Double;
begin
  with ACell do
    case CellType of
      SQLITE_INTEGER:
        Result := CellInteger;
      SQLITE_FLOAT:
        Result := CellFloat;
      SQLITE_TEXT:
        Result := SqlBufToFloatDef16(CellText.p, CellText.l, 0{$IFNDEF SUPPORTS_DEFAULTPARAMS}, '.'{$ENDIF});
    else
      Result := 0;
    end;
end;

function sqlite3_cell16_as_integer(const Cell: TDISQLite3Cell16): Integer;
begin
  case Cell.CellType of
    SQLITE_INTEGER:
      Result := {$IFDEF SUPPORTS_INT64}Cell.CellInteger{$ELSE}System.Trunc(Cell.CellInteger){$ENDIF};
    SQLITE_FLOAT:
      Result := System.Trunc(Cell.CellFloat);
    SQLITE_TEXT:
      Result := StrToIntDef(Cell.CellText.p, 0);
  else
    Result := 0;
  end;
end;

function sqlite3_cell16_as_int64(const Cell: TDISQLite3Cell16): Int64;
begin
  case Cell.CellType of
    SQLITE_INTEGER:
      Result := Cell.CellInteger;
    SQLITE_FLOAT:
      Result := System.Trunc(Cell.CellFloat);
    SQLITE_TEXT:
      Result := {$IFDEF SUPPORTS_INT64}StrToInt64Def(Cell.CellText.p, 0)
      {$ELSE}SqlBufToFloatDef16(Cell.CellText.p, Cell.CellText.l, 0, '.'){$ENDIF};
  else
    Result := 0;
  end;
end;

function sqlite3_cell16_as_time(const ACell: TDISQLite3Cell16): TDateTime;
begin
  Result := DISQLite3Api.JulianDateToTime(sqlite3_cell16_as_float(ACell));
end;

function sqlite3_cell16_as_widestring(const Cell: TDISQLite3Cell16): UnicodeString;
begin
  case Cell.CellType of
    SQLITE_INTEGER:
      Result := {$IFDEF SUPPORTS_INT64}IntToStr(Cell.CellInteger){$ELSE}FloatToSqlStr(Cell.CellInteger, '.', 15, 0){$ENDIF};
    SQLITE_FLOAT:
      Result := sqlite3_decode_utf8(FloatToSqlStr(Cell.CellFloat{$IFNDEF SUPPORTS_DEFAULTPARAMS}, '.', 15, 9999{$ENDIF}));
    SQLITE_TEXT:
      SetString(Result, Cell.CellText.p, Cell.CellText.l);
  else
    Result := '';
  end;
end;

function sqlite3_pragma_database(const ADataBase: Utf8String): Utf8String;
begin
  Result := 'PRAGMA ';
  if sqlite3_database_type(ADataBase) <> dtMain then
    Result := Result + DISQLite3Api.QuotedStr(ADataBase{$IFNDEF SUPPORTS_DEFAULTPARAMS}, SQLITE3_IDENTIFIER_QUOTE_CHAR{$ENDIF}) + '.';
end;

function sqlite3_pragma_database16(const ADataBase: UnicodeString): UnicodeString;
begin
  Result := 'PRAGMA ';
  if sqlite3_database_type16(ADataBase) <> dtMain then
    Result := Result + DISQLite3Api.QuotedStr16(ADataBase{$IFNDEF SUPPORTS_DEFAULTPARAMS}, SQLITE3_IDENTIFIER_QUOTE_CHAR{$ENDIF}) + '.';
end;

function sqlite3_exec_with_callback(
  const ADBHandle: sqlite3_ptr;
  const ASQL: Utf8String;
  const ACallback: TDISQLite3_Callback_{$IFDEF SUPPORTS_DEFAULTPARAMS} = nil{$ENDIF};
  const AUserData: Pointer{$IFDEF SUPPORTS_DEFAULTPARAMS} = nil{$ENDIF}): Integer;
var
  CallbackResult: Boolean;
  e: C_int;
  SqlStart, SqlTail: PUtf8Char;
  SqlLength: C_int;
  Stmt: sqlite3_stmt_ptr;
begin
  if not Assigned(ADBHandle) then
    begin
      Result := SQLITE_MISUSE;
      Exit;
    end;

  SqlLength := Length(ASQL);
  if SqlLength = 0 then
    begin
      Result := SQLITE_OK;
      Exit;
    end;

  SqlStart := Pointer(ASQL);
  repeat

    Result := sqlite3_prepare_v2(ADBHandle, SqlStart, SqlLength + 1, @Stmt, @SqlTail);
    if Result <> SQLITE_OK then
      Break;

    if Assigned(Stmt) then
      begin
        try
          CallbackResult := True;
          Result := sqlite3_step(Stmt);
          if Assigned(ACallback) and (Result and $FF = SQLITE_ROW) then

            repeat
              CallbackResult := ACallback(Stmt, AUserData);
              if not CallbackResult then
                Break;
              Result := sqlite3_step(Stmt);
            until Result and $FF <> SQLITE_ROW;
          case Result and $FF of
            SQLITE_ROW, SQLITE_DONE:
              Result := SQLITE_OK;
          end;
        finally
          e := sqlite3_finalize(Stmt);
        end;
        if e <> SQLITE_OK then
          Result := e;
        if not CallbackResult then
          Result := SQLITE_ABORT;
      end;

    if Result <> SQLITE_OK then
      Break;

    Dec(SqlLength, SqlTail - SqlStart);
    if SqlLength <= 0 then
      Break;
    SqlStart := SqlTail;
  until False;
end;

function sqlite3_exec_with_callback16(
  const ADBHandle: sqlite3_ptr;
  const ASQL: UnicodeString;
  const ACallback: TDISQLite3_Callback_{$IFDEF SUPPORTS_DEFAULTPARAMS} = nil{$ENDIF};
  const AUserData: Pointer{$IFDEF SUPPORTS_DEFAULTPARAMS} = nil{$ENDIF}): Integer;
begin
  Result := sqlite3_exec_with_callback(
    ADBHandle,
    sqlite3_encode_utf8(ASQL),
    ACallback,
    AUserData);
end;

function sqlite3_exec_once(const ADB: sqlite3_ptr; const ASQL: Utf8String): Integer;
var
  e: Integer;
  Stmt: sqlite3_stmt_ptr;
begin
  if Assigned(ADB) then
    begin
      Result := sqlite3_prepare_v2(ADB, Pointer(ASQL), Length(ASQL) + 1, @Stmt, nil);
      if Result = SQLITE_OK then
        begin
          Result := sqlite3_step(Stmt);
          case Result and $FF of
            SQLITE_ROW, SQLITE_DONE:
              Result := SQLITE_OK;
          end;
          e := sqlite3_finalize(Stmt);
          if e <> SQLITE_OK then
            Result := e;
        end;
    end
  else
    Result := SQLITE_MISUSE;
end;

{$IFNDEF SQLITE_OMIT_UTF16}

function sqlite3_exec_once16(const ADB: sqlite3_ptr; const ASQL: UnicodeString): Integer;
var
  e: Integer;
  Stmt: sqlite3_stmt_ptr;
begin
  if Assigned(ADB) then
    begin
      Result := sqlite3_prepare16_v2(ADB, Pointer(ASQL), (Length(ASQL) + 1) * SizeOf(ASQL[1]), @Stmt, nil);
      if Result = SQLITE_OK then
        begin
          Result := sqlite3_step(Stmt);
          case Result and $FF of
            SQLITE_ROW, SQLITE_DONE:
              Result := SQLITE_OK;
          end;
          e := sqlite3_finalize(Stmt);
          if e <> SQLITE_OK then
            Result := e;
        end;
    end
  else
    Result := SQLITE_MISUSE;
end;

{$ENDIF ~SQLITE_OMIT_UTF16}

procedure sqlite3_init_cell8(out Cell: TDISQLite3Cell8);
begin
  Cell.CellType := 0;
  Cell.CellInteger := 0;
end;

procedure sqlite3_init_cell16(out Cell: TDISQLite3Cell16);
begin
  Cell.CellType := 0;
  Cell.CellInteger := 0;
end;

procedure sqlite3_finalize_cell8(var Cell: TDISQLite3Cell8);
begin

  case Cell.CellType of
    SQLITE_BLOB:
      FreeMem(Cell.CellBlob.p);
    SQLITE_TEXT:
      FreeMem(Cell.CellText.p);
  end;

  Cell.CellType := 0;
  Cell.CellInteger := 0;
end;

procedure sqlite3_finalize_cell16(var Cell: TDISQLite3Cell16);
begin

  case Cell.CellType of
    SQLITE_BLOB:
      FreeMem(Cell.CellBlob.p);
    SQLITE_TEXT:
      FreeMem(Cell.CellText.p);
  end;

  Cell.CellType := 0;
  Cell.CellInteger := 0;
end;

function sqlite3_get_boolean_callback(const AStmt: sqlite3_stmt_ptr; const AUserData: Pointer): Boolean; {$IFNDEF COMPILER_6_UP}type PBoolean = ^Boolean; {$ENDIF}
begin
  PBoolean(AUserData)^ := sqlite3_column_int(AStmt, 0) <> 0;
  Result := False;
end;

function sqlite3_get_boolean(const ADB: sqlite3_ptr; const ASQL: Utf8String; out ABoolean: Boolean): Integer;
begin
  Result := sqlite3_exec_with_callback(ADB, ASQL, sqlite3_get_boolean_callback, @ABoolean);
  if Result and $FF = SQLITE_ABORT then
    Result := SQLITE_OK
  else
    Result := SQLITE_NOTFOUND;
end;

function sqlite3_get_boolean16(const ADB: sqlite3_ptr; const ASQL: UnicodeString; out ABoolean: Boolean): Integer;
begin
  Result := sqlite3_exec_with_callback16(ADB, ASQL, sqlite3_get_boolean_callback, @ABoolean);
  if Result and $FF = SQLITE_ABORT then
    Result := SQLITE_OK
  else
    Result := SQLITE_NOTFOUND;
end;

function sqlite3_get_int_callback(const AStmt: sqlite3_stmt_ptr; const AUserData: Pointer): Boolean; begin
  PInteger(AUserData)^ := sqlite3_column_int(AStmt, 0);
  Result := False;
end;

function sqlite3_get_int(const ADB: sqlite3_ptr; const ASQL: Utf8String; out AInteger: Integer): Integer;
begin
  Result := sqlite3_exec_with_callback(ADB, ASQL, sqlite3_get_int_callback, @AInteger);
  if Result and $FF = SQLITE_ABORT then
    Result := SQLITE_OK
  else
    Result := SQLITE_NOTFOUND;
end;

function sqlite3_get_int16(const ADB: sqlite3_ptr; const ASQL: UnicodeString; out AInteger: Integer): Integer;
begin
  Result := sqlite3_exec_with_callback16(ADB, ASQL, sqlite3_get_int_callback, @AInteger);
  if Result and $FF = SQLITE_ABORT then
    Result := SQLITE_OK
  else
    Result := SQLITE_NOTFOUND;
end;

function sqlite3_get_str_callback(const AStmt: sqlite3_stmt_ptr; const AUserData: Pointer): Boolean;
begin
  PUtf8String(AUserData)^ := sqlite3_column_str(AStmt, 0);
  Result := False;
end;

function sqlite3_get_str(const ADB: sqlite3_ptr; const ASQL: Utf8String; out AValue: Utf8String): Integer;
begin
  Result := sqlite3_exec_with_callback(ADB, ASQL, sqlite3_get_str_callback, @AValue);
  if Result and $FF = SQLITE_ABORT then
    Result := SQLITE_OK
  else
    Result := SQLITE_NOTFOUND;
end;

function sqlite3_get_str16_callback(const AStmt: sqlite3_stmt_ptr; const AUserData: Pointer): Boolean;
begin
  {$IFDEF SQLITE_OMIT_UTF16}
  PUnicodeString(AUserData)^ := sqlite3_decode_utf8(sqlite3_column_str(AStmt, 0));
  {$ELSE SQLITE_OMIT_UTF16}
  PUnicodeString(AUserData)^ := sqlite3_column_str16(AStmt, 0);
  {$ENDIF SQLITE_OMIT_UTF16}
  Result := False;
end;

function sqlite3_get_str16(const ADB: sqlite3_ptr; const ASQL: UnicodeString; out AValue: UnicodeString): Integer;
begin
  Result := sqlite3_exec_with_callback16(ADB, ASQL, sqlite3_get_str16_callback, @AValue);
  if Result and $FF = SQLITE_ABORT then
    Result := SQLITE_OK
  else
    Result := SQLITE_NOTFOUND;
end;

function sqlite3_get_pragma_boolean(const ADB: sqlite3_ptr; const APragma: Utf8String; out APragmaValue: Boolean; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_boolean(ADB, sqlite3_pragma_database(ADatabaseName) + APragma + ';', APragmaValue);
end;

function sqlite3_set_pragma_boolean(const ADB: sqlite3_ptr; const APragma: Utf8String; const APragmaValue: Boolean; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
const
  BOOLEAN_TO_UTF8STRING: array[Boolean] of Utf8String = ('0', '1');
begin
  Result := sqlite3_exec_once(ADB, sqlite3_pragma_database(ADatabaseName) + APragma + '=' + BOOLEAN_TO_UTF8STRING[APragmaValue] + ';');
end;

{$IFNDEF SQLITE_OMIT_UTF16}

function sqlite3_get_pragma_boolean16(const ADB: sqlite3_ptr; const APragma: UnicodeString; out APragmaValue: Boolean; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_boolean16(ADB, sqlite3_pragma_database16(ADatabaseName) + APragma + ';', APragmaValue);
end;

function sqlite3_set_pragma_boolean16(const ADB: sqlite3_ptr; const APragma: UnicodeString; const APragmaValue: Boolean; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_exec_once16(ADB, sqlite3_pragma_database16(ADatabaseName) + APragma + UnicodeString('=') + UnicodeString(BOOLEAN_TO_WIDECHAR[APragmaValue]) + ';');
end;
{$ENDIF ~SQLITE_OMIT_UTF16}

function sqlite3_get_pragma_int(const ADB: sqlite3_ptr; const APragma: Utf8String; out APragmaValue: Integer; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_int(ADB, sqlite3_pragma_database(ADatabaseName) + APragma + ';', APragmaValue);
end;

function sqlite3_set_pragma_int(const ADB: sqlite3_ptr; const APragma: Utf8String; const APragmaValue: Integer; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_exec_once(ADB, sqlite3_pragma_database(ADatabaseName) + APragma + '=' + sqlite3_encode_utf8(IntToStr(APragmaValue)) + ';');
end;

{$IFNDEF SQLITE_OMIT_UTF16}

function sqlite3_get_pragma_int16(const ADB: sqlite3_ptr; const APragma: UnicodeString; out APragmaValue: Integer; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_int16(ADB, sqlite3_pragma_database16(ADatabaseName) + APragma + ';', APragmaValue);
end;

function sqlite3_set_pragma_int16(const ADB: sqlite3_ptr; const APragma: UnicodeString; const APragmaValue: Integer; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_exec_once16(ADB, sqlite3_pragma_database16(ADatabaseName) + APragma + '=' + IntToStr(APragmaValue) + ';');
end;

{$ENDIF ~SQLITE_OMIT_UTF16}

function sqlite3_get_pragma_str(const ADB: sqlite3_ptr; const APragma: Utf8String; out APragmaValue: Utf8String; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_str(ADB, sqlite3_pragma_database(ADatabaseName) + APragma + ';', APragmaValue);
end;

function sqlite3_set_pragma_str(const ADB: sqlite3_ptr; const APragma: Utf8String; const APragmaValue: Utf8String; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_exec_once(ADB, sqlite3_pragma_database(ADatabaseName) + APragma + '=' + DISQLite3Api.QuotedStr(APragmaValue, '''') + ';');
end;

{$IFNDEF SQLITE_OMIT_UTF16}

function sqlite3_get_pragma_str16(const ADB: sqlite3_ptr; const APragma: UnicodeString; out APragmaValue: UnicodeString; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_str16(ADB, sqlite3_pragma_database16(ADatabaseName) + APragma + ';', APragmaValue);
end;

function sqlite3_pragma_set_str16(const ADB: sqlite3_ptr; const APragma: UnicodeString; const APragmaValue: UnicodeString; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_exec_once16(ADB,
    sqlite3_pragma_database16(ADatabaseName) + APragma + '=' + DISQLite3Api.QuotedStr16(APragmaValue, '''') + ';');
end;

{$ENDIF ~SQLITE_OMIT_UTF16}

function sqlite3_get_default_cache_size(const ADB: sqlite3_ptr; out ADefaultCacheSize: Integer; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_pragma_int(ADB, 'default_cache_size', ADefaultCacheSize, ADatabaseName);
end;

function sqlite3_set_default_cache_size(const ADB: sqlite3_ptr; const ADefaultCacheSize: Integer; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_set_pragma_int(ADB, 'default_cache_size', ADefaultCacheSize, ADatabaseName);
end;

{$IFNDEF SQLITE_OMIT_UTF16}

function sqlite3_get_default_cache_size16(const ADB: sqlite3_ptr; out ADefaultCacheSize: Integer; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_pragma_int16(ADB, 'default_cache_size', ADefaultCacheSize, ADatabaseName);
end;

function sqlite3_set_default_cache_size16(const ADB: sqlite3_ptr; const ADefaultCacheSize: Integer; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_set_pragma_int16(ADB, 'default_cache_size', ADefaultCacheSize, ADatabaseName);
end;

{$ENDIF ~SQLITE_OMIT_UTF16}

function sqlite3_get_empty_result_callbacks(const ADB: sqlite3_ptr; out AEmptyResultCallbacks: Boolean): Integer;
begin
  Result := sqlite3_get_pragma_boolean(ADB, 'empty_result_callbacks', AEmptyResultCallbacks{$IFNDEF SUPPORTS_DEFAULTPARAMS}, ''{$ENDIF});
end;

function sqlite3_set_empty_result_callbacks(const ADB: sqlite3_ptr; const AEmptyResultCallbacks: Boolean): Integer;
begin
  Result := sqlite3_set_pragma_boolean(ADB, 'empty_result_callbacks', AEmptyResultCallbacks{$IFNDEF SUPPORTS_DEFAULTPARAMS}, ''{$ENDIF});
end;

function sqlite3_pragma_get_encoding(const ADB: sqlite3_ptr; out AEncoding: Utf8String; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_pragma_str(ADB, 'encoding', AEncoding, ADatabaseName);
end;

function sqlite3_set_encoding(const ADB: sqlite3_ptr; const AEncoding: Utf8String; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_set_pragma_str(ADB, 'encoding', AEncoding, ADatabaseName);
end;

{$IFNDEF SQLITE_OMIT_UTF16}

function sqlite3_pragma_get_encoding16(const ADB: sqlite3_ptr; out AEncoding: UnicodeString; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_pragma_str16(ADB, 'encoding', AEncoding, ADatabaseName);
end;

function sqlite3_set_encoding16(const ADB: sqlite3_ptr; const AEncoding: UnicodeString; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_pragma_set_str16(ADB, 'encoding', AEncoding, ADatabaseName);
end;

{$ENDIF ~SQLITE_OMIT_UTF16}

function sqlite3_get_schema_version(const ADB: sqlite3_ptr; out ASchemaVersion: Integer; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_pragma_int(ADB, 'schema_version', ASchemaVersion, ADatabaseName);
end;

function sqlite3_set_schema_version(const ADB: sqlite3_ptr; const ASchemaVersion: Integer; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_set_pragma_int(ADB, 'schema_version', ASchemaVersion, ADatabaseName);
end;

{$IFNDEF SQLITE_OMIT_UTF16}

function sqlite3_get_schema_version16(const ADB: sqlite3_ptr; out ASchemaVersion: Integer; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_pragma_int16(ADB, 'schema_version', ASchemaVersion, ADatabaseName);
end;

function sqlite3_set_schema_version16(const ADB: sqlite3_ptr; const ASchemaVersion: Integer; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_set_pragma_int16(ADB, 'schema_version', ASchemaVersion, ADatabaseName);
end;

{$ENDIF ~SQLITE_OMIT_UTF16}

function sqlite3_get_user_version(const ADB: sqlite3_ptr; out AUserVersion: Integer; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_pragma_int(ADB, 'user_version', AUserVersion, ADatabaseName);
end;

function sqlite3_set_user_version(const ADB: sqlite3_ptr; const AUserVersion: Integer; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_set_pragma_int(ADB, 'user_version', AUserVersion, ADatabaseName);
end;

{$IFNDEF SQLITE_OMIT_UTF16}

function sqlite3_get_user_version16(const ADB: sqlite3_ptr; out AUserVersion: Integer; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_pragma_int16(ADB, 'user_version', AUserVersion, ADatabaseName);
end;

function sqlite3_pragma_set_user_version16(const ADB: sqlite3_ptr; const AUserVersion: Integer; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_set_pragma_int16(ADB, 'user_version', AUserVersion, ADatabaseName);
end;

{$ENDIF ~SQLITE_OMIT_UTF16}

function sqlite3_get_auto_vacuum(const ADB: sqlite3_ptr; out AAutoVacuum: Boolean; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_pragma_boolean(ADB, 'auto_vacuum', AAutoVacuum, ADatabaseName);
end;

function sqlite3_set_auto_vacuum(const ADB: sqlite3_ptr; const AAutoVacuum: Boolean; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_set_pragma_boolean(ADB, 'auto_vacuum', AAutoVacuum, ADatabaseName);
end;

{$IFNDEF SQLITE_OMIT_UTF16}

function sqlite3_get_auto_vacuum16(const ADB: sqlite3_ptr; out AAutoVacuum: Boolean; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_pragma_boolean16(ADB, 'auto_vacuum', AAutoVacuum, ADatabaseName);
end;

function sqlite3_set_auto_vacuum16(const ADB: sqlite3_ptr; const AAutoVacuum: Boolean; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_set_pragma_boolean16(ADB, 'auto_vacuum', AAutoVacuum, ADatabaseName);
end;

{$ENDIF ~SQLITE_OMIT_UTF16}

function sqlite3_get_cache_size(const ADB: sqlite3_ptr; out ACacheSize: Integer; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_pragma_int(ADB, 'cache_size', ACacheSize, ADatabaseName);
end;

function sqlite3_pragma_set_cache_size(const ADB: sqlite3_ptr; const ACacheSize: Integer; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_set_pragma_int(ADB, 'cache_size', ACacheSize, ADatabaseName);
end;

{$IFNDEF SQLITE_OMIT_UTF16}

function sqlite3_get_cache_size16(const ADB: sqlite3_ptr; out ACacheSize: Integer; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_get_pragma_int16(ADB, 'cache_size', ACacheSize, ADatabaseName);
end;

function sqlite3_pragma_set_cache_size16(const ADB: sqlite3_ptr; const ACacheSize: Integer; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
begin
  Result := sqlite3_set_pragma_int16(ADB, 'cache_size', ACacheSize, ADatabaseName);
end;

{$ENDIF ~SQLITE_OMIT_UTF16}

function sqlite3_pragma_get_case_sensitive_like(const ADB: sqlite3_ptr; out ACaseSensitiveLike: Boolean): Integer;
begin
  Result := sqlite3_get_pragma_boolean(ADB, 'case_sensitive_like', ACaseSensitiveLike{$IFNDEF SUPPORTS_DEFAULTPARAMS}, ''{$ENDIF});
end;

function sqlite3_pragma_set_case_sensitive_like(const ADB: sqlite3_ptr; const ACaseSensitiveLike: Boolean): Integer;
begin
  Result := sqlite3_set_pragma_boolean(ADB, 'case_sensitive_like', ACaseSensitiveLike{$IFNDEF SUPPORTS_DEFAULTPARAMS}, ''{$ENDIF});
end;

function sqlite3_pragma_get_count_changes(const ADB: sqlite3_ptr; out ACountChanges: Boolean): Integer;
begin
  Result := sqlite3_get_pragma_boolean(ADB, 'count_changes', ACountChanges{$IFNDEF SUPPORTS_DEFAULTPARAMS}, ''{$ENDIF});
end;

function sqlite3_pragma_set_count_changes(const ADB: sqlite3_ptr; const ACountChanges: Boolean): Integer;
begin
  Result := sqlite3_set_pragma_boolean(ADB, 'count_changes', ACountChanges{$IFNDEF SUPPORTS_DEFAULTPARAMS}, ''{$ENDIF});
end;

function sqlite3_integrity_check(const ADB: sqlite3_ptr; const ADatabaseName: Utf8String{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
var
  s: Utf8String;
begin
  Result := sqlite3_get_pragma_str(ADB, 'integrity_check', s, ADatabaseName);
  if (Result = SQLITE_OK) and (s <> 'ok') then
    Result := SQLITE_ERROR;
end;

{$IFNDEF SQLITE_OMIT_UTF16}

function sqlite3_integrity_check16(const ADB: sqlite3_ptr; const ADatabaseName: UnicodeString{$IFDEF SUPPORTS_DEFAULTPARAMS} = ''{$ENDIF}): Integer;
var
  s: UnicodeString;
begin
  Result := sqlite3_get_pragma_str16(ADB, 'integrity_check', s, ADatabaseName);
  if (Result = SQLITE_OK) and (s <> 'ok') then
    Result := SQLITE_ERROR;
end;

{$ENDIF ~SQLITE_OMIT_UTF16}

procedure DatabaseError(const AResStrRec: PResStringRec; const AObject: TObject{$IFDEF SUPPORTS_DEFAULTPARAMS} = nil{$ENDIF});
begin
  if AObject is TComponent then
    with AObject as TComponent do
      if Name <> '' then
        begin
          raise ESQLite3.Create(SQLITE_ERROR, Format('%s: %s', [Name, LoadResString(AResStrRec)]));
          Exit;
        end;
  {$IFDEF COMPILER_5_UP}
  raise ESQLite3.CreateRes(AResStrRec);
  {$ELSE}
  raise ESQLite3.Create(SQLITE_ERROR, LoadResString(AResStrRec));
  {$ENDIF}
end;

procedure DatabaseErrorFmt(const AResStrRec: PResStringRec; const AArgs: array of const; const AObject: TObject{$IFDEF SUPPORTS_DEFAULTPARAMS} = nil{$ENDIF});
begin
  if AObject is TComponent then
    with AObject as TComponent do
      if Name <> '' then
        begin
          raise ESQLite3.Create(SQLITE_ERROR, Format('%s: %s', [Name, Format(LoadResString(AResStrRec), AArgs)]));
          Exit;
        end;
  {$IFDEF COMPILER_5_UP}
  raise ESQLite3.CreateResFmt(AResStrRec, AArgs);
  {$ELSE}
  raise ESQLite3.CreateFmt(LoadResString(AResStrRec), AArgs);
  {$ENDIF}
end;

constructor TDISQLite3Database.Create(AOwner: TComponent);
begin
  inherited;
  FStatements := TList.Create;
  {$IFDEF SQLITE_THREADSAFE}
  FStatementsMutex := sqlite3_mutex_alloc(SQLITE_MUTEX_RECURSIVE);
  {$ENDIF SQLITE_THREADSAFE}
end;

destructor TDISQLite3Database.Destroy;
var
  i: Integer;
  Stmt: TDISQLite3Statement;
begin
  {$IFDEF SQLITE_THREADSAFE}
  sqlite3_mutex_enter(FStatementsMutex);
  try
    {$ENDIF SQLITE_THREADSAFE}

    i := FStatements.Count;
    while i > 0 do
      begin
        Dec(i);
        Stmt := TDISQLite3Statement(FStatements[i]);
        Stmt.Active := False;
        Stmt.Database := nil;
      end;
    Connected := False;
    FStatements.Free;
    {$IFDEF SQLITE_THREADSAFE}
  finally
    sqlite3_mutex_leave(FStatementsMutex);
    sqlite3_mutex_free(FStatementsMutex);
  end;
  {$ENDIF SQLITE_THREADSAFE}

  inherited;
end;

procedure TDISQLite3Database.AttachDataBase16(const FileName: UnicodeString; DatabaseName: UnicodeString);
var
  Stmt: TDISQLite3Statement;
begin
  CheckActive;

  if not (oUriFileName in FOptions) then
    begin
      CheckDatabaseName(FileName);

      if not sqlite3_is_memory_name(FileName) and not DIUtils.FileExistsW(FileName) then
        raise EFOpenError.CreateFmt(SFOpenError, [DIUtils.ExpandFileNameW(FileName)]);

      if DatabaseName = '' then
        DatabaseName := DIUtils.ChangeFileExtW(DIUtils.ExtractFileNameW(FileName), '');
    end;

  Stmt := Prepare('ATTACH ? AS ?');
  try
    Stmt.Bind_Str16(1, FileName);
    Stmt.Bind_Str16(2, DatabaseName);
    Stmt.Step;
  finally
    Stmt.Free;
  end;
end;

function TDISQLite3Database.Changes: Integer;
begin
  CheckActive;
  Result := sqlite3_changes(FHandle);
end;

function TDISQLite3Database.Check(const AErrorCode: Integer): Integer;
begin
  Result := AErrorCode;
  if Result <> SQLITE_OK then
    sqlite3_raise_exception(Result, FHandle);
end;

procedure TDISQLite3Database.CheckActive;
begin
  if not Assigned(FHandle) then
    DatabaseError(PResStringRec(@SDatabaseClosed), Self);
end;

procedure TDISQLite3Database.CheckDatabaseName(const DatabaseName: UnicodeString);
var
  l: Integer;
  p: PWideChar;
begin
  l := Length(DatabaseName);

  if l = 0 then Exit;

  p := Pointer(DatabaseName);
  repeat
    if p^ > WC_SPACE then Exit;
    Inc(p); Dec(l);
  until l = 0;

  DatabaseError(PResStringRec(@SInvalidDatabaseName), Self);
end;

procedure TDISQLite3Database.CheckInactive;
begin
  if Assigned(FHandle) then
    DatabaseError(PResStringRec(@SDatabaseOpen), Self);
end;

procedure TDISQLite3Database.Close;
begin
  SetConnected(False);
end;

procedure TDISQLite3Database.CloseStatements;
var
  i: Integer;
  Stmt: TDISQLite3Statement;
begin
  {$IFDEF SQLITE_THREADSAFE}
  sqlite3_mutex_enter(FStatementsMutex);
  try
    {$ENDIF SQLITE_THREADSAFE}
    i := FStatements.Count;
    while i > 0 do
      begin
        Dec(i);
        Stmt := TDISQLite3Statement(FStatements[i]);
        Stmt.Active := False;
      end;
    {$IFDEF SQLITE_THREADSAFE}
  finally
    sqlite3_mutex_leave(FStatementsMutex);
  end;
  {$ENDIF SQLITE_THREADSAFE}
end;

procedure TDISQLite3Database.Commit;
begin
  if InTransaction then
    begin
      if FTransactionCount = 1 then
        begin
          CheckActive;
          Check(sqlite3_exec_once(FHandle, 'COMMIT TRANSACTION;'));
        end;
      Dec(FTransactionCount);
    end;
end;

procedure TDISQLite3Database.DetachDatabase(const DatabaseName: Utf8String);
begin
  CheckActive;
  with Prepare('DETACH ?') do
    try
      Bind_Str(1, DatabaseName);
      Step;
    finally
      Free;
    end;
end;

procedure TDISQLite3Database.DetachDatabase16(const DatabaseName: UnicodeString);
begin
  DetachDatabase(sqlite3_encode_utf8(DatabaseName));
end;

procedure TDISQLite3Database.DoAfterConnect;
begin
  if Assigned(FAfterConnect) then
    FAfterConnect(Self);
end;

procedure TDISQLite3Database.DoAfterCreateDatabase;
begin
  if Assigned(FAfterCreateDatabase) then
    FAfterCreateDatabase(Self);
end;

procedure TDISQLite3Database.DoAfterDisconnect;
begin
  if Assigned(FAfterDisconnect) then
    FAfterDisconnect(Self);
end;

procedure TDISQLite3Database.DoBeforeCreateDatabase;
begin
  if Assigned(FBeforeCreateDatabase) then
    FBeforeCreateDatabase(Self);
end;

procedure TDISQLite3Database.DoBeforeConnect;
begin
  if Assigned(FBeforeConnect) then
    FBeforeConnect(Self);
end;

procedure TDISQLite3Database.DoBeforeDisconnect;
begin
  if Assigned(FBeforeDisconnect) then
    FBeforeDisconnect(Self);
end;

procedure TDISQLite3Database.DoConnect;
var
  f: Integer;
  h: sqlite3_ptr;
  DatabaseName8, Vfs8: RawByteString;
begin
  if Connected then Exit;

  CheckDatabaseName(FDatabaseName);

  DatabaseName8 := sqlite3_encode_utf8(FDatabaseName);

  f := FOpenFlags;
  if f and SQLITE_OPEN_READONLY = 0 then
    f := f or SQLITE_OPEN_READWRITE;
  if oUriFileName in FOptions then
    f := f or SQLITE_OPEN_URI;

  Vfs8 := sqlite3_encode_utf8(FVfs);

  try

    Check(sqlite3_open_v2(PAnsiChar(DatabaseName8), @FHandle, f, Pointer(Vfs8)));
  except

    h := FHandle; FHandle := nil; sqlite3_close(h);

    raise;
  end;

  FTransactionCount := 0;

  {$IFDEF SQLITE_HAS_CODEC}
  if FPassword <> '' then
    Check(sqlite3_key(FHandle, Pointer(FPassword), Length(FPassword)));
  {$ENDIF SQLITE_HAS_CODEC}
end;

procedure TDISQLite3Database.DoDisconnect;
var
  h: sqlite3_ptr;
begin
  if Assigned(FHandle) then
    begin
      CloseStatements;
      h := FHandle;
      FHandle := nil;
      Check(sqlite3_close(h));
    end;
end;

procedure TDISQLite3Database.DoInitDatabase;
begin
  if Assigned(FInitDatabase) then
    FInitDatabase(Self);
end;

function TDISQLite3Database.GetAccess: TDISQLite3AccessOption;
begin
  if FOpenFlags and SQLITE_OPEN_READONLY <> 0 then
    Result := aoReadOnly
  else
    if FOpenFlags and SQLITE_OPEN_MEMORY <> 0 then
      Result := aoMemory
    else
      Result := aoDefault;
end;

procedure TDISQLite3Database.SetAccess(const Value: TDISQLite3AccessOption);
begin
  FOpenFlags := FOpenFlags and not (SQLITE_OPEN_READONLY or SQLITE_OPEN_MEMORY);
  case Value of
    aoDefault:
      ;
    aoReadOnly:
      FOpenFlags := FOpenFlags or SQLITE_OPEN_READONLY;
    aoMemory:
      FOpenFlags := FOpenFlags or SQLITE_OPEN_MEMORY;
  else
    Assert(False);
  end;
end;

function TDISQLite3Database.GetAutoVacuum: Boolean;
begin
  CheckActive;
  Check(sqlite3_get_auto_vacuum(FHandle, Result{$IFNDEF SUPPORTS_DEFAULTPARAMS}, ''{$ENDIF}))
end;

procedure TDISQLite3Database.SetAutoVacuum(const AValue: Boolean);
begin
  CheckActive;
  Check(sqlite3_set_auto_vacuum(FHandle, AValue{$IFNDEF SUPPORTS_DEFAULTPARAMS}, ''{$ENDIF}));
end;

function TDISQLite3Database.GetBusyTimeout: C_int;
begin
  CheckActive;
  Result := sqlite3_int_ptr(FHandle)^.BusyTimeout;
end;

procedure TDISQLite3Database.SetBusyTimeout(const Value: C_int);
begin
  CheckActive;
  Check(sqlite3_busy_timeout(Handle, Value));
end;

function TDISQLite3Database.GetCache: TDISQLite3CacheOption;
begin
  if FOpenFlags and SQLITE_OPEN_SHAREDCACHE <> 0 then
    Result := coSharedCache
  else
    if FOpenFlags and SQLITE_OPEN_PRIVATECACHE <> 0 then
      Result := coPrivateCache
    else
      Result := coDefault;
end;

procedure TDISQLite3Database.SetCache(const Value: TDISQLite3CacheOption);
begin
  FOpenFlags := FOpenFlags and not (SQLITE_OPEN_SHAREDCACHE or SQLITE_OPEN_PRIVATECACHE);
  case Value of
    coDefault:
      ;
    coSharedCache:
      FOpenFlags := FOpenFlags or SQLITE_OPEN_SHAREDCACHE;
    coPrivateCache:
      FOpenFlags := FOpenFlags or SQLITE_OPEN_PRIVATECACHE;
  else
    Assert(False);
  end;
end;

function TDISQLite3Database.GetCacheSize: Integer;
begin
  CheckActive;
  Check(sqlite3_get_cache_size(FHandle, Result{$IFNDEF SUPPORTS_DEFAULTPARAMS}, ''{$ENDIF}));
end;

procedure TDISQLite3Database.SetCacheSize(const AValue: Integer);
begin
  CheckActive;
  Check(sqlite3_pragma_set_cache_size(FHandle, AValue{$IFNDEF SUPPORTS_DEFAULTPARAMS}, ''{$ENDIF}));
end;

function TDISQLite3Database.GetCaseSensitiveLike: Boolean;
begin
  CheckActive;
  Check(sqlite3_pragma_get_case_sensitive_like(FHandle, Result));
end;

procedure TDISQLite3Database.SetCaseSensitiveLike(const AValue: Boolean);
begin
  CheckActive;
  Check(sqlite3_pragma_set_case_sensitive_like(FHandle, AValue));
end;

function TDISQLite3Database.GetConnected: Boolean;
begin
  Result := Assigned(FHandle);
end;

function TDISQLite3Database.GetCountChanges: Boolean;
begin
  CheckActive;
  Check(sqlite3_pragma_get_count_changes(FHandle, Result))
end;

procedure TDISQLite3Database.SetCountChanges(const AValue: Boolean);
begin
  CheckActive;
  Check(sqlite3_pragma_set_count_changes(FHandle, AValue));
end;

function TDISQLite3Database.GetDefaultCacheSize: Integer;
begin
  CheckActive;
  Check(sqlite3_get_default_cache_size(FHandle, Result{$IFNDEF SUPPORTS_DEFAULTPARAMS}, ''{$ENDIF}))
end;

procedure TDISQLite3Database.SetDefaultCacheSize(const AValue: Integer);
begin
  CheckActive;
  Check(sqlite3_set_default_cache_size(FHandle, AValue{$IFNDEF SUPPORTS_DEFAULTPARAMS}, ''{$ENDIF}));
end;

function TDISQLite3Database.GetEmptyResultCallbacks: Boolean;
begin
  CheckActive;
  Check(sqlite3_get_empty_result_callbacks(FHandle, Result))
end;

procedure TDISQLite3Database.SetEmptyResultCallbacks(const AValue: Boolean);
begin
  CheckActive;
  Check(sqlite3_set_empty_result_callbacks(FHandle, AValue));
end;

function TDISQLite3Database.GetEncoding: UnicodeString;
{$IFDEF SQLITE_OMIT_UTF16}
var
  s8: Utf8String;
  {$ENDIF SQLITE_OMIT_UTF16}
begin
  CheckActive;
  {$IFDEF SQLITE_OMIT_UTF16}
  Check(sqlite3_pragma_get_encoding(FHandle, s8));
  Result := sqlite3_decode_utf8(s8);
  {$ELSE SQLITE_OMIT_UTF16}
  Check(sqlite3_pragma_get_encoding16(FHandle, Result{$IFNDEF SUPPORTS_DEFAULTPARAMS}, ''{$ENDIF}));
  {$ENDIF SQLITE_OMIT_UTF16}
end;

procedure TDISQLite3Database.SetEncoding(const AValue: UnicodeString);
begin
  CheckActive;
  {$IFDEF SQLITE_OMIT_UTF16}
  Check(sqlite3_set_encoding(FHandle, sqlite3_encode_utf8(AValue)));
  {$ELSE SQLITE_OMIT_UTF16}
  Check(sqlite3_set_encoding16(FHandle, AValue{$IFNDEF SUPPORTS_DEFAULTPARAMS}, ''{$ENDIF}));
  {$ENDIF SQLITE_OMIT_UTF16}
end;

function TDISQLite3Database.GetInTransaction: Boolean;
begin
  Result := FTransactionCount > 0;

end;

function TDISQLite3Database.GetMutex: TDISQLite3MutexOption;
begin
  if FOpenFlags and SQLITE_OPEN_NOMUTEX <> 0 then
    Result := moNoMutex
  else
    if FOpenFlags and SQLITE_OPEN_FULLMUTEX <> 0 then
      Result := moFullMutex
    else
      Result := moDefault;
end;

procedure TDISQLite3Database.SetMutex(const Value: TDISQLite3MutexOption);
begin
  FOpenFlags := FOpenFlags and not (SQLITE_OPEN_NOMUTEX or SQLITE_OPEN_FULLMUTEX);
  case Value of
    moDefault:
      ;
    moNoMutex:
      FOpenFlags := FOpenFlags or SQLITE_OPEN_NOMUTEX;
    moFullMutex:
      FOpenFlags := FOpenFlags or SQLITE_OPEN_FULLMUTEX;
  else
    Assert(False);
  end;
end;

function TDISQLite3Database.GetPageSize: Integer;
begin
  CheckActive;
  Check(sqlite3_get_pragma_int(Handle, 'page_size', Result{$IFNDEF SUPPORTS_DEFAULTPARAMS}, ''{$ENDIF}));
end;

procedure TDISQLite3Database.SetPageSize(const AValue: Integer);
begin
  CheckActive;
  Check(sqlite3_set_pragma_int(Handle, 'page_size', AValue{$IFNDEF SUPPORTS_DEFAULTPARAMS}, ''{$ENDIF}));
end;

function TDISQLite3Database.GetStatementClass: TDISQLite3StatementClass;
begin
  Result := TDISQLite3Statement;
end;

function TDISQLite3Database.GetSQLiteVersion: UnicodeString;
begin
  Result := sqlite3_decode_utf8(sqlite3_libversion);
end;

procedure TDISQLite3Database.CreateDatabase;
var
  f: Integer;
  h: sqlite3_ptr;
  DatabaseName8, Vfs8: Utf8String;
begin
  CheckInactive;

  if not (oUriFileName in FOptions) then
    begin
      CheckDatabaseName(FDatabaseName);

      if not sqlite3_is_memory_name(FDatabaseName) and
        DIUtils.FileExistsW(FDatabaseName) and not DIUtils.DeleteFileW(FDatabaseName) then
        raise EFCreateError.CreateFmt(SFCreateError, [DIUtils.ExpandFileNameW(FDatabaseName)]);
    end;

  DoBeforeConnect;
  DoBeforeCreateDatabase;

  DatabaseName8 := sqlite3_encode_utf8(FDatabaseName);

  f := FOpenFlags or SQLITE_OPEN_READWRITE or SQLITE_OPEN_CREATE;
  if oUriFileName in FOptions then
    f := f or SQLITE_OPEN_URI;

  Vfs8 := sqlite3_encode_utf8(FVfs);

  try

    Check(sqlite3_open_v2(PUtf8Char(DatabaseName8), @FHandle, f, Pointer(Vfs8)));
  except

    h := FHandle; FHandle := nil; sqlite3_close(h);
    raise;
  end;

  {$IFDEF SQLITE_HAS_CODEC}
  if FPassword <> '' then
    Check(sqlite3_key(FHandle, Pointer(FPassword), Length(FPassword)));
  {$ENDIF SQLITE_HAS_CODEC}

  DoInitDatabase;

  DoAfterCreateDatabase;
  DoAfterConnect;
end;

procedure TDISQLite3Database.Execute(const SQL: Utf8String);
begin
  CheckActive;
  Check(sqlite3_exec_with_callback(FHandle, SQL{$IFNDEF SUPPORTS_DEFAULTPARAMS}, nil, nil{$ENDIF}));
end;

procedure TDISQLite3Database.Execute16(const SQL: UnicodeString);
begin
  CheckActive;
  Check(sqlite3_exec_with_callback16(FHandle, SQL{$IFNDEF SUPPORTS_DEFAULTPARAMS}, nil, nil{$ENDIF}));
end;

function TDISQLite3Database.IntegrityCheck: Integer;
begin
  CheckActive;
  Result := sqlite3_integrity_check(FHandle{$IFNDEF SUPPORTS_DEFAULTPARAMS}, ''{$ENDIF});
end;

procedure TDISQLite3Database.Interrupt;
begin
  CheckActive;
  sqlite3_interrupt(FHandle);
end;

function TDISQLite3Database.LastInsertRowID: Int64;
begin
  CheckActive;
  Result := sqlite3_last_insert_rowid(FHandle);
end;

procedure TDISQLite3Database.Loaded;
begin
  inherited;
  try
    if FStreamedConnected then
      SetConnected(True);
  except
    if csDesigning in ComponentState then
      {$IFDEF COMPILER_6_UP}
      if Assigned({$IFDEF HAS_UNITSCOPE}System.{$ENDIF}Classes.ApplicationHandleException) then
        {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}Classes.ApplicationHandleException(ExceptObject)
      else
        {$ENDIF}
        ShowException(ExceptObject, ExceptAddr)
    else
      raise;
  end;
end;

procedure TDISQLite3Database.Open;
begin
  SetConnected(True);
end;

function TDISQLite3Database.Prepare(const SQL: Utf8String): TDISQLite3Statement;
begin
  Result := GetStatementClass.Create;
  Result.Database := Self;
  Result.SQL8 := SQL;
  try
    Result.Open;
  except
    Result.Free;
    raise;
  end;
end;

function TDISQLite3Database.Prepare16(const SQL: UnicodeString): TDISQLite3Statement;
begin
  Result := GetStatementClass.Create;
  Result.Database := Self;
  Result.SQL16 := SQL;
  try
    Result.Open;
  except
    Result.Free;
    raise;
  end;
end;

function TDISQLite3Database.ReadOnly(const DatabaseName: Utf8String = 'main'): C_int;
begin
  Result := sqlite3_db_readonly(FHandle, PUtf8Char(DatabaseName));
end;

function TDISQLite3Database.ReadOnly16(const DatabaseName: UnicodeString = 'main'): C_int;
var
  s8: Utf8String;
begin
  s8 := sqlite3_encode_utf8(DatabaseName);
  Result := sqlite3_db_readonly(FHandle, PUtf8Char(s8));
end;

procedure TDISQLite3Database.RegisterStatement(const AStatement: TDISQLite3Statement);
begin
  if Assigned(AStatement) then
    begin
      {$IFDEF SQLITE_THREADSAFE}
      sqlite3_mutex_enter(FStatementsMutex);
      try
        {$ENDIF SQLITE_THREADSAFE}
        FStatements.Add(AStatement);
        {$IFDEF SQLITE_THREADSAFE}
      finally
        sqlite3_mutex_leave(FStatementsMutex);
      end;
      {$ENDIF SQLITE_THREADSAFE}
    end;
end;

{$IFDEF SQLITE_HAS_CODEC}

procedure TDISQLite3Database.ReKey(const ANewPassword: RawByteString);
begin
  CheckActive;
  Check(sqlite3_rekey(FHandle, Pointer(ANewPassword), Length(ANewPassword)));
  FPassword := ANewPassword;
end;

{$ENDIF SQLITE_HAS_CODEC}

procedure TDISQLite3Database.Rollback;
begin
  if InTransaction then
    begin
      if FTransactionCount = 1 then
        begin
          CheckActive;
          Check(sqlite3_exec_once(FHandle, 'ROLLBACK TRANSACTION;'));
        end;
      Dec(FTransactionCount);
    end;
end;

procedure TDISQLite3Database.SetConnected(const Value: Boolean);
begin
  if (csReading in ComponentState) and Value then
    FStreamedConnected := True
  else
    if Value <> GetConnected then
      if Value then
        begin
          DoBeforeConnect;
          DoConnect;
          DoInitDatabase;
          DoAfterConnect;
        end
      else
        begin
          DoBeforeDisconnect;
          DoDisconnect;
          DoAfterDisconnect;
        end;
end;

procedure TDISQLite3Database.SetDatabaseName(const AValue: UnicodeString);
begin
  CheckInactive;
  FDatabaseName := AValue;
end;

{$IFDEF SQLITE_HAS_CODEC}

procedure TDISQLite3Database.SetPassword(const AValue: RawByteString);
begin
  if Connected then
    begin
      Check(sqlite3_key(FHandle, Pointer(AValue), Length(AValue)));
    end;
  FPassword := AValue;
end;

{$ENDIF SQLITE_HAS_CODEC}

procedure TDISQLite3Database.SetSQLiteVersion(const Value: UnicodeString);
begin

end;

procedure TDISQLite3Database.StartTransaction(const TransactionType: TDISQLite3TransactionType{$IFDEF SUPPORTS_DEFAULTPARAMS} = ttDeferred{$ENDIF});
const
  tt: array[TDISQLite3TransactionType] of Utf8String =
    ('DEFERRED', 'IMMEDIATE', 'EXCLUSIVE');
begin
  if not InTransaction then
    begin
      CheckActive;
      Check(sqlite3_exec_once(FHandle,
        'BEGIN ' + tt[TransactionType] + ' TRANSACTION;'));
    end;
  Inc(FTransactionCount);
end;

function TDISQLite3Database.TotalChanges: Integer;
begin
  CheckActive;
  Result := sqlite3_total_changes(FHandle);
end;

procedure TDISQLite3Database.UnRegisterStatement(const AStatement: TDISQLite3Statement);
begin
  if Assigned(AStatement) then
    begin
      {$IFDEF SQLITE_THREADSAFE}
      sqlite3_mutex_enter(FStatementsMutex);
      try
        {$ENDIF SQLITE_THREADSAFE}
        FStatements.Remove(AStatement);
        {$IFDEF SQLITE_THREADSAFE}
      finally
        sqlite3_mutex_leave(FStatementsMutex);
      end;
      {$ENDIF SQLITE_THREADSAFE}
    end;
end;

procedure TDISQLite3Database.Vacuum;
begin
  CheckActive;
  Check(sqlite3_exec_once(FHandle, 'VACUUM;'));
end;

destructor TDISQLite3Statement.Destroy;
begin

  if Assigned(FHandle) then
    begin
      DoBeforeClose;

      sqlite3_finalize(FHandle);
      DoAfterClose;
    end;

  if Assigned(FDatabase) then
    FDatabase.UnRegisterStatement(Self);

  inherited;
end;

procedure TDISQLite3Statement.Assign(const ASource: TDISQLite3Statement);
begin
  Database := ASource.Database;
  SQL8 := ASource.SQL8;
end;

procedure TDISQLite3Statement.Bind_Blob(
  const AParamIdx: Integer;
  const ABlobData: Pointer;
  const ABlobSize: Integer;
  const ADestroy: TSQLite3_Bind_Destructor);
begin
  CheckActive;
  Check(sqlite3_bind_blob(FHandle, AParamIdx, ABlobData, ABlobSize, ADestroy));
end;

procedure TDISQLite3Statement.Bind_Blob_By_Name(
  const AParamName: Utf8String;
  const ABlobData: Pointer;
  const ABlobSize: Integer;
  const ADestroy: TSQLite3_Bind_Destructor);
begin
  CheckActive;
  Check(sqlite3_bind_blob(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(AParamName)), ABlobData, ABlobSize, ADestroy));
end;

procedure TDISQLite3Statement.Bind_Blob_By_Name16(
  const AParamName: UnicodeString;
  const ABlobData: Pointer;
  const ABlobSize: Integer;
  const ADestroy: TSQLite3_Bind_Destructor);
begin
  CheckActive;
  Check(sqlite3_bind_blob(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(sqlite3_encode_utf8(AParamName))), ABlobData, ABlobSize, ADestroy));
end;

procedure TDISQLite3Statement.Bind_Double(const AParamIdx: Integer; const AValue: Double);
begin
  CheckActive;
  Check(sqlite3_bind_double(FHandle, AParamIdx, AValue));
end;

procedure TDISQLite3Statement.Bind_Double_By_Name(const AParamName: Utf8String; const AValue: Double);
begin
  CheckActive;
  Check(sqlite3_bind_double(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(AParamName)), AValue));
end;

procedure TDISQLite3Statement.Bind_Double_By_Name16(const AParamName: UnicodeString; const AValue: Double);
begin
  CheckActive;
  Check(sqlite3_bind_double(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(sqlite3_encode_utf8(AParamName))), AValue));
end;

procedure TDISQLite3Statement.Bind_Int(const AParamIdx: Integer; const AValue: Integer);
begin
  CheckActive;
  Check(sqlite3_bind_int(FHandle, AParamIdx, AValue));
end;

procedure TDISQLite3Statement.Bind_Int_By_Name(const AParamName: Utf8String; const AValue: Integer);
begin
  CheckActive;
  Check(sqlite3_bind_int(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(AParamName)), AValue));
end;

procedure TDISQLite3Statement.Bind_Int_By_Name16(const AParamName: UnicodeString; const AValue: Integer);
begin
  CheckActive;
  Check(sqlite3_bind_int(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(sqlite3_encode_utf8(AParamName))), AValue));
end;

procedure TDISQLite3Statement.Bind_Int64(const AParamIdx: Integer; const AValue: Int64);
begin
  CheckActive;
  Check(sqlite3_bind_int64(FHandle, AParamIdx, AValue));
end;

procedure TDISQLite3Statement.Bind_Int64_By_Name(const AParamName: Utf8String; const AValue: Int64);
begin
  CheckActive;
  Check(sqlite3_bind_int64(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(AParamName)), AValue));
end;

procedure TDISQLite3Statement.Bind_Int64_By_Name16(const AParamName: UnicodeString; const AValue: Int64);
begin
  CheckActive;
  Check(sqlite3_bind_int64(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(sqlite3_encode_utf8(AParamName))), AValue));
end;

procedure TDISQLite3Statement.Bind_Null(const AParamIdx: Integer);
begin
  CheckActive;
  Check(sqlite3_bind_null(FHandle, AParamIdx));
end;

procedure TDISQLite3Statement.Bind_Null_By_Name(const AParamName: Utf8String);
begin
  CheckActive;
  Check(sqlite3_bind_null(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(AParamName))));
end;

procedure TDISQLite3Statement.Bind_Null_By_Name16(const AParamName: UnicodeString);
begin
  CheckActive;
  Check(sqlite3_bind_null(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(sqlite3_encode_utf8(AParamName)))));
end;

function TDISQLite3Statement.Bind_Parameter_Count: Integer;
begin
  CheckActive;
  Result := sqlite3_bind_parameter_count(FHandle);
end;

function TDISQLite3Statement.bind_Parameter_Name(const AParamIdx: Integer): Utf8String;
begin
  CheckActive;
  Result := sqlite3_bind_parameter_name(FHandle, AParamIdx);
end;

function TDISQLite3Statement.Bind_Parameter_Name16(const AParamIdx: Integer): UnicodeString;
begin
  CheckActive;
  Result := sqlite3_decode_utf8(sqlite3_bind_parameter_name(FHandle, AParamIdx));
end;

function TDISQLite3Statement.Bind_Parameter_Index(const AParamName: Utf8String): Integer;
begin
  CheckActive;
  Result := sqlite3_bind_parameter_index(FHandle, PUtf8Char(AParamName));
end;

function TDISQLite3Statement.Bind_Parameter_Index16(const ParamName: UnicodeString): C_int;
begin
  CheckActive;
  Result := sqlite3_bind_parameter_index(
    FHandle, C_char_ptr(sqlite3_encode_utf8(ParamName)));
end;

procedure TDISQLite3Statement.Bind_Str(const ParamIdx: C_int; const Value: Utf8String);
begin
  CheckActive;
  Check(sqlite3_bind_str(FHandle, ParamIdx, Value));
end;

procedure TDISQLite3Statement.Bind_Str_By_Name(const ParamName: Utf8String; const Value: Utf8String);
begin
  CheckActive;
  Check(sqlite3_bind_str(FHandle, Bind_Parameter_Index(ParamName), Value));
end;

procedure TDISQLite3Statement.Bind_Str_By_Name16(const AParamName: UnicodeString; const AValue: Utf8String);
begin
  CheckActive;
  Check(sqlite3_bind_str(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(sqlite3_encode_utf8(AParamName))), AValue));
end;

procedure TDISQLite3Statement.Bind_Str16(const AParamIdx: Integer; const AValue: UnicodeString);
begin
  CheckActive;
  {$IFDEF SQLITE_OMIT_UTF16}
  Check(sqlite3_bind_str(FHandle, AParamIdx, sqlite3_encode_utf8(AValue)));
  {$ELSE SQLITE_OMIT_UTF16}
  Check(sqlite3_bind_str16(FHandle, AParamIdx, AValue));
  {$ENDIF SQLITE_OMIT_UTF16}
end;

procedure TDISQLite3Statement.Bind_Str16_By_Name(const AParamName: Utf8String; const AValue: UnicodeString);
begin
  CheckActive;
  {$IFDEF SQLITE_OMIT_UTF16}
  Check(sqlite3_bind_str(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(AParamName)), sqlite3_encode_utf8(AValue)));
  {$ELSE SQLITE_OMIT_UTF16}
  Check(sqlite3_bind_str16(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(AParamName)), AValue));
  {$ENDIF SQLITE_OMIT_UTF16}
end;

procedure TDISQLite3Statement.Bind_Str16_By_Name16(const AParamName: UnicodeString; const AValue: UnicodeString);
begin
  CheckActive;
  {$IFDEF SQLITE_OMIT_UTF16}
  Check(sqlite3_bind_str(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(sqlite3_encode_utf8(AParamName))), sqlite3_encode_utf8(AValue)));
  {$ELSE SQLITE_OMIT_UTF16}
  Check(sqlite3_bind_str16(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(sqlite3_encode_utf8(AParamName))), AValue));
  {$ENDIF SQLITE_OMIT_UTF16}
end;

procedure TDISQLite3Statement.Bind_Text(
  const AParamIdx: Integer;
  const AValue: PUtf8Char;
  const AValueLen: Integer;
  const xDestroy: TSQLite3_Bind_Destructor);
begin
  CheckActive;
  Check(sqlite3_bind_text(FHandle, AParamIdx, AValue, AValueLen, xDestroy));
end;

procedure TDISQLite3Statement.Bind_Text_By_Name(
  const AParamName: Utf8String;
  const AValue: PUtf8Char;
  const AValueLen: Integer;
  const xDestroy: TSQLite3_Bind_Destructor);
begin
  CheckActive;
  Check(sqlite3_bind_text(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(AParamName)), AValue, AValueLen, xDestroy));
end;

procedure TDISQLite3Statement.Bind_Text_By_Name16(
  const AParamName: UnicodeString;
  const AValue: PUtf8Char;
  const AValueLen: Integer;
  const xDestroy: TSQLite3_Bind_Destructor);
begin
  CheckActive;
  Check(sqlite3_bind_text(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(sqlite3_encode_utf8(AParamName))), AValue, AValueLen, xDestroy));
end;

{$IFNDEF SQLITE_OMIT_UTF16}
procedure TDISQLite3Statement.Bind_Text16(
  const AParamIdx: Integer;
  const AValue: PWideChar;
  const AValueLen: Integer;
  const xDestroy: TSQLite3_Bind_Destructor);
begin
  CheckActive;
  Check(sqlite3_bind_text16(FHandle, AParamIdx, AValue, AValueLen, xDestroy));
end;
{$ENDIF SQLITE_OMIT_UTF16}

{$IFNDEF SQLITE_OMIT_UTF16}
procedure TDISQLite3Statement.Bind_Text16_By_Name(
  const AParamName: Utf8String;
  const AValue: PWideChar;
  const AValueLen: Integer;
  const xDestroy: TSQLite3_Bind_Destructor);
begin
  CheckActive;
  Check(sqlite3_bind_text16(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(AParamName)), AValue, AValueLen, xDestroy));
end;
{$ENDIF SQLITE_OMIT_UTF16}

{$IFNDEF SQLITE_OMIT_UTF16}
procedure TDISQLite3Statement.Bind_Text16_By_Name16(
  const AParamName: UnicodeString;
  const AValue: PWideChar;
  const AValueLen: Integer;
  const xDestroy: TSQLite3_Bind_Destructor);
begin
  CheckActive;
  Check(sqlite3_bind_text16(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(sqlite3_encode_utf8(AParamName))), AValue, AValueLen, xDestroy));
end;
{$ENDIF SQLITE_OMIT_UTF16}

procedure TDISQLite3Statement.Bind_Variant(const AParamIdx: Integer; const AValue: Variant);
begin
  CheckActive;
  Check(sqlite3_bind_variant(FHandle, AParamIdx, AValue));
end;

procedure TDISQLite3Statement.Bind_Variant_By_Name(const AParamName: Utf8String; const AValue: Variant);
begin
  CheckActive;
  Check(sqlite3_bind_variant(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(AParamName)), AValue));
end;

procedure TDISQLite3Statement.Bind_Variant_By_Name16(const AParamName: UnicodeString; const AValue: Variant);
begin
  CheckActive;
  Check(sqlite3_bind_variant(FHandle, sqlite3_bind_parameter_index(FHandle, PUtf8Char(sqlite3_encode_utf8(AParamName))), AValue));
end;

procedure TDISQLite3Statement.Bind_ZeroBlob(const ParamIdx: C_int; const BlobSize: C_int);
begin
  CheckActive;
  Check(sqlite3_bind_zeroblob(FHandle, ParamIdx, BlobSize));
end;

procedure TDISQLite3Statement.Bind_ZeroBlob64(const ParamIdx: C_int; const BlobSize: sqlite3_uint64);
begin
  CheckActive;
  Check(sqlite3_bind_zeroblob64(FHandle, ParamIdx, BlobSize));
end;

function TDISQLite3Statement.Check(const AErrorCode: Integer): Integer;
begin
  Result := AErrorCode;
  if Result <> SQLITE_OK then
    sqlite3_raise_exception(Result, FDatabase.Handle);
end;

procedure TDISQLite3Statement.CheckActive;
begin
  if not Assigned(FHandle) then
    DatabaseError(PResStringRec(@SStatementClosed), Self)
end;

procedure TDISQLite3Statement.CheckInactive;
begin
  if Assigned(FHandle) then
    DatabaseError(PResStringRec(@SStatementOpen), Self);
end;

procedure TDISQLite3Statement.Clear_Bindings;
begin
  CheckActive;
  Check(sqlite3_clear_bindings(FHandle));
end;

procedure TDISQLite3Statement.Close;
begin
  SetActive(False);
end;

function TDISQLite3Statement.Column_Blob(const ColumnIdx: C_int): C_void_ptr;
begin
  CheckActive;
  Result := sqlite3_column_blob(FHandle, ColumnIdx);
end;

function TDISQLite3Statement.Column_Bytes(
  const AColumnIdx: Integer): Integer;
begin
  CheckActive;
  Result := sqlite3_column_bytes(FHandle, AColumnIdx);
end;

{$IFNDEF SQLITE_OMIT_UTF16}

function TDISQLite3Statement.Column_Bytes16(
  const AColumnIdx: Integer): Integer;
begin
  CheckActive;
  Result := sqlite3_column_bytes16(FHandle, AColumnIdx);
end;

{$ENDIF ~SQLITE_OMIT_UTF16}

function TDISQLite3Statement.Column_Cell8(const AColumnIdx: Integer): TDISQLite3Cell8;
var
  ColLen: Integer;
  ColPtr: Pointer;
begin
  CheckActive;

  Result.CellType := sqlite3_column_type(FHandle, AColumnIdx);
  case Result.CellType of

    SQLITE_INTEGER:
      begin
        Result.CellInteger := sqlite3_column_int64(FHandle, AColumnIdx);
      end;

    SQLITE_FLOAT:
      begin
        Result.CellFloat := sqlite3_column_double(FHandle, AColumnIdx);
      end;

    SQLITE_TEXT:
      begin
        ColPtr := sqlite3_column_text(FHandle, AColumnIdx);
        ColLen := sqlite3_column_bytes(FHandle, AColumnIdx);
        Result.CellText.l := ColLen;
        GetMem(Result.CellText.p, ColLen);
        Move(ColPtr^, Result.CellText.p^, ColLen);
      end;

    SQLITE_BLOB:
      begin
        ColPtr := sqlite3_column_blob(FHandle, AColumnIdx);
        ColLen := sqlite3_column_bytes(FHandle, AColumnIdx);
        Result.CellBlob.l := ColLen;
        GetMem(Result.CellBlob.p, ColLen);
        Move(ColPtr^, Result.CellBlob.p^, ColLen);
      end;

    SQLITE_NULL:
      begin
        Result.CellInteger := 0;
      end;
  else
    sqlite3_init_cell8(Result);
  end;
end;

procedure TDISQLite3Statement.Column_Cell16(const ColumnIdx: Integer; out Cell: TDISQLite3Cell16);
var
  ColLen: Integer;
  ColPtr: Pointer;
  {$IFDEF SQLITE_OMIT_UTF16}
  s: UnicodeString;
  {$ENDIF SQLITE_OMIT_UTF16}
begin
  CheckActive;

  with Cell do
    begin
      CellType := sqlite3_column_type(FHandle, ColumnIdx);
      case CellType of

        SQLITE_INTEGER:
          begin
            CellInteger := sqlite3_column_int64(FHandle, ColumnIdx);
          end;

        SQLITE_FLOAT:
          begin
            CellFloat := sqlite3_column_double(FHandle, ColumnIdx);
          end;

        SQLITE_TEXT:
          begin
            {$IFDEF SQLITE_OMIT_UTF16}
            s := sqlite3_column_str16(FHandle, ColumnIdx);
            ColLen := Length(s);
            CellText.l := ColLen;
            ColLen := ColLen shl 1;
            GetMem(CellText.p, ColLen);
            Move(Pointer(s)^, CellText.p^, ColLen);
            {$ELSE SQLITE_OMIT_UTF16}
            ColPtr := sqlite3_column_text16(FHandle, ColumnIdx);
            ColLen := sqlite3_column_bytes16(FHandle, ColumnIdx);
            CellText.l := ColLen shr 1;
            GetMem(CellText.p, ColLen);
            Move(ColPtr^, CellText.p^, ColLen);
            {$ENDIF SQLITE_OMIT_UTF16}
          end;

        SQLITE_BLOB:
          begin
            ColPtr := sqlite3_column_blob(FHandle, ColumnIdx);
            ColLen := sqlite3_column_bytes(FHandle, ColumnIdx);
            CellBlob.l := ColLen;
            GetMem(CellBlob.p, ColLen);
            Move(ColPtr^, CellBlob.p^, ColLen);
          end;

        SQLITE_NULL:
          begin
            CellInteger := 0;
          end;
      else
        sqlite3_init_cell16(Cell);
      end;
    end;
end;

function TDISQLite3Statement.Column_Count: Integer;
begin
  CheckActive;
  Result := sqlite3_column_count(FHandle);
end;

{$IFDEF SQLITE_ENABLE_COLUMN_METADATA}

function TDISQLite3Statement.Column_Database_Name(const AColumnIdx: Integer): Utf8String;
begin
  CheckActive;
  Result := sqlite3_column_database_name(FHandle, AColumnIdx);
end;

{$ENDIF SQLITE_ENABLE_COLUMN_METADATA}

{$IFDEF SQLITE_ENABLE_COLUMN_METADATA}

function TDISQLite3Statement.Column_Database_Name16(const AColumnIdx: Integer): UnicodeString;
begin
  {$IFDEF SQLITE_OMIT_UTF16}
  Result := sqlite3_decode_utf8(Column_Database_Name(AColumnIdx));
  {$ELSE SQLITE_OMIT_UTF16}
  CheckActive;
  Result := sqlite3_column_database_name16(FHandle, AColumnIdx);
  {$ENDIF SQLITE_OMIT_UTF16}
end;

{$ENDIF SQLITE_ENABLE_COLUMN_METADATA}

{$IFNDEF SQLITE_OMIT_DECLTYPE}
function TDISQLite3Statement.Column_DeclType(const AColumnIdx: Integer): Utf8String;
begin
  Result := sqlite3_column_decltype(FHandle, AColumnIdx);
end;
{$ENDIF ~SQLITE_OMIT_DECLTYPE}

{$IFNDEF SQLITE_OMIT_DECLTYPE}
function TDISQLite3Statement.Column_DeclType16(const AColumnIdx: Integer): UnicodeString;
begin
  {$IFDEF SQLITE_OMIT_UTF16}
  Result := sqlite3_decode_utf8(Column_DeclType(AColumnIdx));
  {$ELSE SQLITE_OMIT_UTF16}
  CheckActive;
  Result := sqlite3_column_decltype16(FHandle, AColumnIdx);
  {$ENDIF SQLITE_OMIT_UTF16}
end;
{$ENDIF ~SQLITE_OMIT_DECLTYPE}

function TDISQLite3Statement.Column_Double(const AColumnIdx: Integer): Double;
begin
  CheckActive;
  Result := sqlite3_column_double(FHandle, AColumnIdx);
end;

function TDISQLite3Statement.Column_Int(const AColumnIdx: Integer): Integer;
begin
  CheckActive;
  Result := sqlite3_column_int(FHandle, AColumnIdx);
end;

function TDISQLite3Statement.Column_Int64(const AColumnIdx: Integer): Int64;
begin
  CheckActive;
  Result := sqlite3_column_int64(FHandle, AColumnIdx);
end;

function TDISQLite3Statement.Column_Name(const AColumnIdx: Integer): Utf8String;
begin
  CheckActive;
  Result := sqlite3_column_name(FHandle, AColumnIdx);
end;

function TDISQLite3Statement.Column_Name16(const AColumnIdx: Integer): UnicodeString;
begin
  {$IFDEF SQLITE_OMIT_UTF16}
  Result := sqlite3_decode_utf8(Column_Name(AColumnIdx));
  {$ELSE SQLITE_OMIT_UTF16}
  CheckActive;
  Result := sqlite3_column_name16(FHandle, AColumnIdx);
  {$ENDIF SQLITE_OMIT_UTF16}
end;

{$IFDEF SQLITE_ENABLE_COLUMN_METADATA}
function TDISQLite3Statement.Column_Origin_Name(const AColumnIdx: Integer): Utf8String;
begin
  CheckActive;
  Result := sqlite3_column_origin_name(FHandle, AColumnIdx);
end;
{$ENDIF SQLITE_ENABLE_COLUMN_METADATA}

{$IFDEF SQLITE_ENABLE_COLUMN_METADATA}

function TDISQLite3Statement.Column_Origin_Name16(const AColumnIdx: Integer): UnicodeString;
begin
  {$IFDEF SQLITE_OMIT_UTF16}
  Result := sqlite3_decode_utf8(Column_Origin_Name(AColumnIdx));
  {$ELSE SQLITE_OMIT_UTF16}
  CheckActive;
  Result := sqlite3_column_origin_name16(FHandle, AColumnIdx);
  {$ENDIF SQLITE_OMIT_UTF16}
end;

{$ENDIF SQLITE_ENABLE_COLUMN_METADATA}

function TDISQLite3Statement.Column_Str(const AColumnIdx: Integer): Utf8String;
begin
  CheckActive;
  Result := sqlite3_column_str(FHandle, AColumnIdx);
end;

function TDISQLite3Statement.Column_Str16(const AColumnIdx: Integer): UnicodeString;
begin
  CheckActive;
  Result := sqlite3_column_str16(FHandle, AColumnIdx);
end;

{$IFDEF SQLITE_ENABLE_COLUMN_METADATA}

function TDISQLite3Statement.Column_Table_Name(const AColumnIdx: Integer): Utf8String;
begin
  CheckActive;
  Result := sqlite3_column_table_name(FHandle, AColumnIdx);
end;
{$ENDIF SQLITE_ENABLE_COLUMN_METADATA}

{$IFDEF SQLITE_ENABLE_COLUMN_METADATA}

function TDISQLite3Statement.Column_Table_Name16(const AColumnIdx: Integer): UnicodeString;
begin
  {$IFDEF SQLITE_OMIT_UTF16}
  Result := sqlite3_decode_utf8(Column_Table_Name(AColumnIdx));
  {$ELSE SQLITE_OMIT_UTF16}
  CheckActive;
  Result := sqlite3_column_table_name16(FHandle, AColumnIdx);
  {$ENDIF SQLITE_OMIT_UTF16}
end;

{$ENDIF SQLITE_ENABLE_COLUMN_METADATA}

function TDISQLite3Statement.Column_Text(const AColumnIdx: Integer): PUtf8Char;
begin
  CheckActive;
  Result := sqlite3_column_text(FHandle, AColumnIdx);
end;

{$IFNDEF SQLITE_OMIT_UTF16}
function TDISQLite3Statement.Column_Text16(const AColumnIdx: Integer): PWideChar;
begin
  CheckActive;
  Result := sqlite3_column_text16(FHandle, AColumnIdx);
end;
{$ENDIF ~SQLITE_OMIT_UTF16}

function TDISQLite3Statement.Column_Type(const ColumnIdx: C_int): C_int;
begin
  CheckActive;
  Result := sqlite3_column_type(FHandle, ColumnIdx);
end;

procedure TDISQLite3Statement.DoAfterClose;
begin
  if Assigned(FAfterClose) then
    FAfterClose(Self);
end;

procedure TDISQLite3Statement.DoAfterOpen;
begin
  if Assigned(FAfterOpen) then
    FAfterOpen(Self);
end;

procedure TDISQLite3Statement.DoBeforeClose;
begin
  if Assigned(FBeforeClose) then
    FBeforeClose(Self);
end;

procedure TDISQLite3Statement.DoBeforeOpen;
begin
  if Assigned(FBeforeOpen) then
    FBeforeOpen(Self);
end;

function TDISQLite3Statement.GetActive: Boolean;
begin
  Result := Assigned(FHandle);
end;

function TDISQLite3Statement.GetSql16: UnicodeString;
begin
  Result := sqlite3_decode_utf8(FSql8);
end;

{$IFDEF SQLITE_ENABLE_COLUMN_METADATA}

function TDISQLite3Statement.TableName(const AQuoteChar: AnsiChar{$IFDEF SUPPORTS_DEFAULTPARAMS} = SQLITE3_IDENTIFIER_QUOTE_CHAR{$ENDIF}): Utf8String;
var
  c, i: Integer;
  d, t: Utf8String;
begin
  Result := '';
  CheckActive;

  c := sqlite3_column_count(FHandle);
  if c > 0 then
    begin
      i := 0;
      d := sqlite3_column_database_name(FHandle, i);
      t := sqlite3_column_table_name(FHandle, i);

      Inc(i);
      while i < c do
        begin
          if d <> Utf8String(sqlite3_column_database_name(FHandle, i)) then
            Break;
          if t <> Utf8String(sqlite3_column_table_name(FHandle, i)) then
            Break;

          Inc(i);
        end;

      if i = c then
        begin
          if sqlite3_database_type(d) <> dtMain then
            Result := DISQLite3Api.QuotedStr(d{$IFNDEF SUPPORTS_DEFAULTPARAMS}, SQLITE3_IDENTIFIER_QUOTE_CHAR{$ENDIF}) + '.'
          else
            Result := '';
          Result := Result + DISQLite3Api.QuotedStr(t{$IFNDEF SUPPORTS_DEFAULTPARAMS}, SQLITE3_IDENTIFIER_QUOTE_CHAR{$ENDIF});
        end;
    end;
end;

{$ENDIF SQLITE_ENABLE_COLUMN_METADATA}

{$IFDEF SQLITE_ENABLE_COLUMN_METADATA}

function TDISQLite3Statement.TableName16(const AQuoteChar: WideChar{$IFDEF SUPPORTS_DEFAULTPARAMS} = SQLITE3_IDENTIFIER_QUOTE_CHAR{$ENDIF}): UnicodeString;
{$IFNDEF SQLITE_OMIT_UTF16}
var
  c, i: Integer;
  d, t: UnicodeString;
  {$ENDIF SQLITE_OMIT_UTF16}
begin
  {$IFDEF SQLITE_OMIT_UTF16}
  Result := sqlite3_decode_utf8(TableName(AnsiChar(AQuoteChar)));
  {$ELSE SQLITE_OMIT_UTF16}
  Result := '';
  CheckActive;

  c := sqlite3_column_count(FHandle);
  if c > 0 then
    begin
      i := 0;
      d := sqlite3_column_database_name16(FHandle, i);
      t := sqlite3_column_table_name16(FHandle, i);

      Inc(i);
      while i < c do
        begin
          if d <> UnicodeString(sqlite3_column_database_name16(FHandle, i)) then
            Break;
          if t <> UnicodeString(sqlite3_column_table_name16(FHandle, i)) then
            Break;

          Inc(i);
        end;

      if i = c then
        begin
          if sqlite3_database_type16(d) <> dtMain then
            Result := DISQLite3Api.QuotedStr16(d{$IFNDEF SUPPORTS_DEFAULTPARAMS}, SQLITE3_IDENTIFIER_QUOTE_CHAR{$ENDIF}) + '.'
          else
            Result := '';
          Result := Result + DISQLite3Api.QuotedStr16(t{$IFNDEF SUPPORTS_DEFAULTPARAMS}, SQLITE3_IDENTIFIER_QUOTE_CHAR{$ENDIF});
        end;
    end;
  {$ENDIF SQLITE_OMIT_UTF16}
end;

{$ENDIF SQLITE_ENABLE_COLUMN_METADATA}

procedure TDISQLite3Statement.Open;
begin
  SetActive(True);
end;

function TDISQLite3Statement.Reset: Integer;
begin
  CheckActive;

  Result := sqlite3_reset(FHandle);
end;

procedure TDISQLite3Statement.SetActive(const AValue: Boolean);
var
  h: sqlite3_stmt_ptr;
begin
  if AValue <> Assigned(FHandle) then
    if AValue then
      begin
        if not Assigned(FDatabase) then
          DatabaseError(PResStringRec(@SMissingDatabase){$IFNDEF SUPPORTS_DEFAULTPARAMS}, nil{$ENDIF});
        FDatabase.Connected := True;
        DoBeforeOpen;
        Check(sqlite3_prepare_v2(FDatabase.Handle, PUtf8Char(FSql8), Length(FSql8) + 1, @FHandle, nil));
        if not Assigned(FHandle) then
          DatabaseError(PResStringRec(@SEmptySQLStatement), Self);
        DoAfterOpen;
      end
    else
      begin

        DoBeforeClose;
        h := FHandle;
        FHandle := nil;

        sqlite3_finalize(h);
        DoAfterClose;
      end;
end;

procedure TDISQLite3Statement.SetDatabase(const AValue: TDISQLite3Database);
begin
  CheckInactive;
  if Assigned(FDatabase) then
    FDatabase.UnRegisterStatement(Self);
  FDatabase := AValue;
  if Assigned(AValue) then
    AValue.RegisterStatement(Self);
end;

procedure TDISQLite3Statement.SetSql8(const AValue: Utf8String);
begin
  CheckInactive;
  FSql8 := AValue;
end;

procedure TDISQLite3Statement.SetSql16(const AValue: UnicodeString);
begin
  SetSql8(sqlite3_encode_utf8(AValue));
end;

function TDISQLite3Statement.Step: Integer;
var
  l: Integer;
begin
  CheckActive;

  l := 8;
  repeat
    Result := sqlite3_step(FHandle);
    case Result and $FF of
      SQLITE_OK, SQLITE_ROW, SQLITE_DONE:
        Exit;
      SQLITE_BUSY, SQLITE_LOCKED:
        Sleep(10);
    else
      Break;
    end;
    Dec(l);
  until l = 0;

  sqlite3_raise_exception(Result, FDatabase.Handle);
end;

function TDISQLite3Statement.StepAndReset: Integer;
var
  eReset, l: Integer;
begin
  CheckActive;

  l := 8;
  repeat
    Result := sqlite3_step(FHandle);
    if not (Result and $FF in [SQLITE_BUSY, SQLITE_LOCKED]) then
      Break;
    Sleep(10);
    Dec(l);
  until l = 0;

  eReset := sqlite3_reset(FHandle);

  if (Result and $FF in [SQLITE_OK, SQLITE_ERROR])
    and (eReset <> SQLITE_OK) then
    Result := eReset;

  case Result and $FF of
    SQLITE_OK, SQLITE_ROW, SQLITE_DONE:
      ;
  else
    sqlite3_raise_exception(Result, FDatabase.Handle);
  end;
end;

{$IFNDEF SQLITE_OMIT_INCRBLOB}

constructor TDISQLite3IncrBlobStream.Create(
  const ADBHandle: sqlite3_ptr;
  const ADatabaseName: UnicodeString;
  const ATableName: UnicodeString;
  const AColumnName: UnicodeString;
  const ARowID: Int64;
  const AFlags: Integer);
begin
  inherited Create;
  sqlite3_check(sqlite3_blob_open(
    ADBHandle,
    PUtf8Char(sqlite3_encode_utf8(ADatabaseName)),
    PUtf8Char(sqlite3_encode_utf8(ATableName)),
    PUtf8Char(sqlite3_encode_utf8(AColumnName)),
    ARowID,
    AFlags,
    @FBlobHandle), ADBHandle);
  FFlags := AFlags;
end;

constructor TDISQLite3IncrBlobStream.{$IFDEF SUPPORTS_OVERLOAD}Create{$ELSE}CreateDB{$ENDIF}(
  const ADB: TDISQLite3Database;
  const ADatabaseName: UnicodeString;
  const ATableName: UnicodeString;
  const AColumnName: UnicodeString;
  const ARowID: Int64;
  const AFlags: Integer);
begin
  Create(ADB.Handle, ADatabaseName, ATableName, AColumnName, ARowID, AFlags);
end;

destructor TDISQLite3IncrBlobStream.Destroy;
begin

  sqlite3_blob_close(FBlobHandle);
  inherited;
end;

function TDISQLite3IncrBlobStream.Read(var Buffer; Count: Integer): Integer;
var
  BlobSize: Integer;
begin
  Result := Count;
  BlobSize := sqlite3_blob_bytes(FBlobHandle);
  if Result + FOffset > BlobSize then
    Result := BlobSize - FOffset;
  if (Count > 0) and (sqlite3_blob_read(FBlobHandle, @Buffer, Count, FOffset) = SQLITE_OK) then
    Inc(FOffset, Result)
  else
    Result := 0;
end;

function TDISQLite3IncrBlobStream.Seek(Offset: Integer; Origin: Word): Integer;
begin
  case Origin of
    soFromBeginning: FOffset := Offset;
    soFromCurrent: Inc(FOffset, Offset);
    soFromEnd: FOffset := sqlite3_blob_bytes(FBlobHandle) + Offset;
  end;
  Result := FOffset;
end;

procedure TDISQLite3IncrBlobStream.SetSize(NewSize: Integer);
begin
  {$IFDEF COMPILER_5_UP}
  raise EStreamError.CreateRes(PResStringRec(@SStreamSetSize));
  {$ELSE COMPILER_5_UP}
  raise EStreamError.Create(SStreamSetSize);
  {$ENDIF COMPILER_5_UP}
end;

function TDISQLite3IncrBlobStream.Write(const Buffer; Count: Integer): Integer;
var
  BlobSize: Integer;
begin
  if FFlags <> 0 then
    begin
      Result := Count;
      BlobSize := sqlite3_blob_bytes(FBlobHandle);
      if FOffset + Result > BlobSize then
        Result := BlobSize - FOffset;
      if (Result > 0) and (sqlite3_blob_write(FBlobHandle, @Buffer, Result, FOffset) = SQLITE_OK) then
        Inc(FOffset, Result)
      else
        Result := 0;
    end
  else
    begin
      {$IFDEF COMPILER_5_UP}
      raise EStreamError.CreateRes(PResStringRec(@SCannotWriteStreamError));
      {$ELSE COMPILER_5_UP}
      raise EStreamError.Create(SCannotWriteStreamError);
      {$ENDIF COMPILER_5_UP}
    end;
end;

{$ENDIF ~SQLITE_OMIT_INCRBLOB}

end.

