////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001-2026 Alexey Kuryakin daqgroup@mail.ru under MIT license //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// This file is part of the CRW-DAQ project by DaqGroup - component CRWLIB.   //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Purpose:                                                                   //
// DataBase API routines for DaqPascal.                                       //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// History:                                                                   //
// 20221105 - 1st release                                                     //
// 20221119 - EngineId,EngineName in db_ctrl                                  //
// 20221120 - db_subst_connectionstring; ConnectionStringInit                 //
// 20221121 - TimeStampInit,..,TimeStampUser1/2/3,UserData,Cookies            //
// 20221211 - Recordset Source                                                //
// 20221214 - ConnectionStringInit,UserState,UserFlags,UserLink               //
// 20221216 - Driver property                                                 //
// 20231107 - Modified for FPC (A.K.)                                         //
// 20250129 - Use TAtomicCounter                                              //
// 20250620 - Add SqlDb engine, USES_SQLDB definition                         //
// 20250709 - Add Zeos engine, USES_ZEOS definition                           //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Use command to generate ADODB_TLB.PAS:                                     //
// TLIBIMP -P+ -O+ -Q- -H- -L- "%CommonProgramFiles%\System\ado\msado28.tlb"  //
////////////////////////////////////////////////////////////////////////////////

unit _crw_dbapi; // DataBase API

{$I _crw_sysdef.inc}

{$I _crw_sysmode.inc}

{$WARN 5023 off : Unit "$1" not used in $2}
{$WARN 5024 off : Parameter "$1" not used}
{$WARN 6058 off : Call to subroutine "$1" marked as inline is not inlined}
{$WARN 5027 off : Local variable "$1" is assigned but never used}

{$DEFINE USES_SQLDB}
{$DEFINE USES_ZEOS}

{$IFDEF USES_SQLDB}
// Use SQLDB engine
{$ENDIF ~USES_SQLDB}

{$IFDEF USES_ZEOS}
// Use ZEOS engine
{$ENDIF ~USES_ZEOS}

interface

uses
 //////////////////////////////////////////////////////
 {$I _crw_uses_first.inc} // NB: MUST BE FIRST USES !!!
 //////////////////////////////////////////////////////
 {$IFDEF WINDOWS} comobj, {$ENDIF}
 sysutils, classes, math, strutils, variants,
 _crw_alloc, _crw_str, _crw_rtc, _crw_fio, _crw_hl, _crw_bsencode,
 _crw_oledb, _crw_dbcon, _crw_adodb_tlb
 {$IFDEF USES_SQLDB},_crw_dblaz{$ENDIF}
 {$IFDEF USES_ZEOS},_crw_dbzeo{$ENDIF}
 ;

{$IFDEF WINDOWS}
{$DEFINE USES_ADODB}
{$ENDIF ~WINDOWS}

{$IFnDEF USES_ADODB}
type Recordset=Pointer;
type Command=Pointer;
type Error=Pointer;
type Errors=Pointer;
type Property_=Pointer;
type Properties=Pointer;
{$ENDIF ~USES_ADODB}

const //////////////////// DB entity type identifiers
 db_type_nil             = 0;  // Nil pointer, entity is not exist
 db_type_connection      = 1;  // TDbConnection
 db_type_recordset       = 2;  // TDbRecordset
 db_type_command         = 3;  // TDbCommand
 db_type_parent          = 4;  // TDbParent, abstract
 db_type_entity          = 5;  // TDbEntity, abstract
 db_type_any             = db_type_entity;

const //////////////////// ADO DB constants; ADO = ActiveX Data Objects
 db_engine_code_ado      = _crw_dbcon.db_engine_code_ado;   // ADO engine code
 db_engine_name_ado      = _crw_dbcon.db_engine_name_ado;   // ADO engine name

const //////////////////// SQLDB constants; SQLDB = default Lazarus DB engine
 db_engine_code_sqldb    = _crw_dbcon.db_engine_code_sqldb; // SQLDB Engine code
 db_engine_name_sqldb    = _crw_dbcon.db_engine_name_sqldb; // SQLDB Engine name

const //////////////////// ZEOS constants; ZEOS = optional DB engine ZEOSDBO
 db_engine_code_zeos     = _crw_dbcon.db_engine_code_zeos;  // ZEOS Engine code
 db_engine_name_zeos     = _crw_dbcon.db_engine_name_zeos;  // ZEOS Engine name

const //////////////////// DB Engine identifiers
 db_engine_def           = 0;                    // Use default engine number
 db_engine_ado           = db_engine_code_ado;   // ADO = ActiveX Data Objects
 db_engine_sqldb         = db_engine_code_sqldb; // Lazarus default SQLDB
 db_engine_zeos          = db_engine_code_zeos;  // Lazarus addon ZEOSDBO
 db_engine_default       : Integer = db_engine_ado;

const //////////////////// Comma separated list of engines
 db_engine_list_csv      = db_engine_name_ado
                      +','+db_engine_name_sqldb
                      +','+db_engine_name_zeos;

const //////////////////// Well known OLEDB/ADO providers
 db_provider_msdasql     = 'MSDASQL';                // System default
 db_provider_ibpfree     = 'LCPI.IBProvider.5.Free'; // IBProvider Free

const //////////////////// Prefix uses for BugReport
 db_bugreport_prefix_def = '@!ESoftException: '; // Default detail echo
 db_bugreport_prefix_ech = '@!EEchoException: '; // Short echo
 db_bugreport_prefix_hid = '@!EHideException: '; // Hidden
 db_bugreport_prefix     : LongString = db_bugreport_prefix_def;

const //////////////////// Use BindRoot automatically
 db_bindroot_oncreate    : Boolean = true;
 db_bindroot_onexecute   : Boolean = true;

////////////////////////////////////////////////////////////////////////////////
// Database API objects.
////////////////////////////////////////////////////////////////////////////////

type
 TDbEntity = class;     // abstract "any DB object"
 TDbParent = class;     // abstract object with childs
 TDbConnection = class; // ADODB.Connection wrapper
 TDbRecordset = class;  // ADODB.Recordset wrapper
 TDbCommand = class;    // ADODB.Command wrapper
 TDbEntity = class(TMasterObject)
 private
  myTypeId   : Integer;
  myParent   : TDbParent;
  myBgPref   : LongString;
 private
  function  GetTypeId:Integer;
  function  GetParent:TDbParent;
 private
  procedure FreeAllChilds;
  function  GetChildCount:Integer;
  function  DoGetChildCount:Integer; virtual;
  function  GetChildItems(i:Integer):TDbEntity;
  function  DoGetChildItems(i:Integer):TDbEntity; virtual;
 private
  function  GetState:Integer;
  function  DoGetState:Integer; virtual;
 private
  function  GetActive:Boolean;
  function  DoGetActive:Boolean; virtual;
 private
  function  DoClose:Boolean; virtual;
  function  DoCancel:Boolean; virtual;
  function  DoBindRoot:Boolean; virtual;
 private
  function  GetProperties:LongString;
  function  DoGetProperties:LongString; virtual;
  procedure SetProperties(const arg:LongString);
  procedure DoSetProperties(const arg:LongString); virtual;
 private
  function  GetMode:Integer;
  function  DoGetMode:Integer; virtual;
  procedure SetMode(arg:Integer);
  procedure DoSetMode(arg:Integer); virtual;
 private
  function  GetCursorLocation:Integer;
  function  DoGetCursorLocation:Integer; virtual;
  procedure SetCursorLocation(arg:Integer);
  procedure DoSetCursorLocation(arg:Integer); virtual;
 private
  function  GetAttributes:Integer;
  function  DoGetAttributes:Integer; virtual;
  procedure SetAttributes(arg:Integer);
  procedure DoSetAttributes(arg:Integer); virtual;
 private  // TDbRecordset related
  function  GetAbsolutePage:Integer;
  procedure SetAbsolutePage(arg:Integer);
  function  GetAbsolutePosition:Integer;
  procedure SetAbsolutePosition(arg:Integer);
  function  GetBof:Boolean;
  function  GetEof:Boolean;
  function  GetBookmark:Integer;
  procedure SetBookmark(arg:Integer);
  function  GetCacheSize:Integer;
  procedure SetCacheSize(arg:Integer);
  function  GetCursorType:Integer;
  procedure SetCursorType(arg:Integer);
  function  GetEditMode:Integer;
  function  GetFilter:LongString;
  procedure SetFilter(arg:LongString);
  function  GetIndex:LongString;
  procedure SetIndex(arg:LongString);
  function  GetLockType:Integer;
  procedure SetLockType(arg:Integer);
  function  GetMarshalOptions:Integer;
  procedure SetMarshalOptions(arg:Integer);
  function  GetMaxRecords:Integer;
  procedure SetMaxRecords(arg:Integer);
  function  GetPageCount:Integer;
  function  GetPageSize:Integer;
  procedure SetPageSize(arg:Integer);
  function  GetRecordCount:Integer;
  function  GetSort:LongString;
  procedure SetSort(arg:LongString);
  function  GetSource:LongString;
  procedure SetSource(arg:LongString);
  function  GetStatus:Integer;
  function  GetStayInSync:Boolean;
  procedure SetStayInSync(arg:Boolean);
 private  // TDbCommand related
  function  GetCommandType:Integer;
  procedure SetCommandType(arg:Integer);
  function  GetCommandText:LongString;
  procedure SetCommandText(arg:LongString);
 private
  function  DoOpen(opt:Integer):Boolean; virtual;
  function  DoExecute(const arg:LongString; opt:Integer):TDbRecordset; virtual;
 protected
  procedure BugReport(E:Exception; O:TObject; const S:LongString);
 public
  constructor Create(aParent:TDbParent);
  destructor  Destroy; override;
  procedure   AfterConstruction; override;
  procedure   BeforeDestruction; override;
  function    BindRoot:Boolean;
 public   // Basic properties
  property  TypeId           : Integer            read GetTypeId;
  property  Parent           : TDbParent          read GetParent;
 public   // Parent related properties
  property  ChildCount       : Integer            read GetChildCount;
  property  ChildItems[i:Integer] : TDbEntity     read GetChildItems;
 public   // Common properties
  property  State            : Integer            read GetState;
  property  Active           : Boolean            read GetActive;
  property  Properties       : LongString         read GetProperties     write SetProperties;
  property  Mode             : Integer            read GetMode           write SetMode;
  property  CursorLocation   : Integer            read GetCursorLocation write SetCursorLocation;
  property  Attributes       : Integer            read GetAttributes     write SetAttributes;
 public   // TDbRecordset related
  property  AbsolutePage     : Integer            read GetAbsolutePage       write SetAbsolutePage;
  property  AbsolutePosition : Integer            read GetAbsolutePosition   write SetAbsolutePosition;
  property  Bof              : Boolean            read GetBof;
  property  Eof              : Boolean            read GetEof;
  property  Bookmark         : Integer            read GetBookmark           write SetBookmark;
  property  CacheSize        : Integer            read GetCacheSize          write SetCacheSize;
  property  CursorType       : Integer            read GetCursorType         write SetCursorType;
  property  EditMode         : Integer            read GetEditMode;
  property  Filter           : LongString         read GetFilter             write SetFilter;
  property  Index            : LongString         read GetIndex              write SetIndex;
  property  LockType         : Integer            read GetLockType           write SetLockType;
  property  MarshalOptions   : Integer            read GetMarshalOptions     write SetMarshalOptions;
  property  MaxRecords       : Integer            read GetMaxRecords         write SetMaxRecords;
  property  PageCount        : Integer            read GetPageCount;
  property  PageSize         : Integer            read GetPageSize           write SetPageSize;
  property  RecordCount      : Integer            read GetRecordCount;
  property  Sort             : LongString         read GetSort               write SetSort;
  property  Source           : LongString         read GetSource             write SetSource;
  property  Status           : Integer            read GetStatus;
  property  StayInSync       : Boolean            read GetStayInSync         write SetStayInSync;
 public   // TDbRecordset rows/fields related
  function  MoveFirst:Boolean;
  function  MoveLast:Boolean;
  function  MoveNext:Boolean;
  function  MovePrevious:Boolean;
  function  Update:Boolean;
  function  CancelUpdate:Boolean;
  function  FieldsCount:Integer;
  function  FieldsNames(i:Integer):LongString;
  function  FieldsTypes(id:LongString):Integer;
  function  FieldsAsInt(const id:LongString; op:Char; const arg:Integer):Integer;
  function  FieldsAsFloat(const id:LongString; op:Char; const arg:Double):Double;
  function  FieldsAsString(const id:LongString; op:Char; const arg:LongString):LongString;
  function  GetString(n:Integer=1; coldel:LongString=ASCII_HT; rowdel:LongString=EOL; nullexpr:LongString=''):LongString;
  function  AddNew(arg:LongString=''):Boolean;
  function  Delete(AffectRecords:Integer):Boolean;
  function  Requery(Options:Integer):Boolean;
  function  Resync(AffectRecords,ResyncValues:Integer):Boolean;
  function  Supports(CursorOptions:Integer):Boolean;
  function  Save(Destination:LongString; PersistFormat:Integer):Boolean;
 public   // TDbCommand related
  property  CommandType      : Integer            read GetCommandType        write SetCommandType;
  property  CommandText      : LongString         read GetCommandText        write SetCommandText;
 public   // Control routines
  function  Root:TDbConnection;
  function  Open(opt:Integer):Boolean;
  function  Control(arg:LongString):LongString;
  function  Execute(const arg:LongString; opt:Integer):TDbRecordset;
  function  Cancel:Boolean;
  function  Close:Boolean;
 public // Error counters
  class function TotalBugsCount:SizeInt;
  class function TotalBugsClear:SizeInt;
  class function IncTotalBugsCount:SizeInt;
 end;
 TDbParent = class(TDbEntity)
 private
  myChilds  : TList;
 private
  procedure AppendChild(aChild:TDbEntity);
  procedure DoAppendChild(aChild:TDbEntity); virtual;
  procedure RemoveChild(aChild:TDbEntity);
  procedure DoRemoveChild(aChild:TDbEntity); virtual;
 private
  function  DoGetChildCount:Integer; override;
  function  DoGetChildItems(i:Integer):TDbEntity; override;
 public
  constructor Create(aParent:TDbParent);
  destructor  Destroy; override;
 end;
 TDbConnection = class(TDbParent)
 private
  myEngineId        : Integer;
  myEngineName      : LongString;
  myConStrng        : LongString;
  {$IFDEF USES_ADODB}
  myAdoConn         : Connection;
  {$ENDIF ~USES_ADODB}
  {$IFDEF USES_SQLDB}
  mySqlDbConn : TSqlDbConnecter;
  {$ENDIF ~USES_SQLDB}
  {$IFDEF USES_ZEOS}
  myZeosConn : TZeosConnecter;
  {$ENDIF ~USES_ZEOS}
  myBugsCount       : TAtomicCounter;
  myRecordsAffected : Integer;
  myTimeStampInit   : Double;
  myTimeStampOpen   : Double;
  myTimeStampUser1  : Double;
  myTimeStampUser2  : Double;
  myTimeStampUser3  : Double;
  myUserState       : Integer;
  myUserFlags       : Integer;
  myUserLink        : Integer;
  myUserData        : LongString;
  myCookies         : LongString;
  myPwdCryptMode    : Integer;
  myPwdCryptKey     : LongString;
  myPwdCryptIv      : LongString;
 private
  function  GetEngineId:Integer;
  procedure SetEngineId(eid:Integer);
  function  GetEngineName:LongString;
  function  GetConnectionStringInit:LongString;
  function  GetTimeStampInit:Double;
  function  GetTimeStampOpen:Double;
  function  GetTimeStampUser1:Double;
  procedure SetTimeStampUser1(arg:Double);
  function  GetTimeStampUser2:Double;
  procedure SetTimeStampUser2(arg:Double);
  function  GetTimeStampUser3:Double;
  procedure SetTimeStampUser3(arg:Double);
  function  GetUserState:Integer;
  procedure SetUserState(arg:Integer);
  function  GetUserFlags:Integer;
  procedure SetUserFlags(arg:Integer);
  function  GetUserLink:Integer;
  procedure SetUserLink(arg:Integer);
  function  GetUserData:LongString;
  procedure SetUserData(arg:LongString);
  function  GetCookies:LongString;
  procedure SetCookies(arg:LongString);
  function  GetPwdCryptMode:Integer;
  procedure SetPwdCryptMode(arg:Integer);
  function  GetPwdCryptKey:LongString;
  procedure SetPwdCryptKey(arg:LongString);
  function  GetPwdCryptIv:LongString;
  procedure SetPwdCryptIv(arg:LongString);
  function  GetVersion:LongString;
  function  GetErrors:LongString;
  function  GetErrorsCount:Integer;
  function  GetErrorsClear:Integer;
  function  GetDriver:LongString;
  function  GetProvider:LongString;
  procedure SetProvider(const arg:LongString);
  function  GetConnectionString:LongString;
  procedure SetConnectionString(const arg:LongString);
  function  DecryptConStrng(const arg:LongString):LongString;
  function  GetConnectionTimeout:Integer;
  procedure SetConnectionTimeout(arg:Integer);
  function  GetCommandTimeout:Integer;
  procedure SetCommandTimeout(arg:Integer);
  function  GetDefaultDatabase:LongString;
  procedure SetDefaultDatabase(const arg:LongString);
  function  GetIsolationLevel:Integer;
  procedure SetIsolationLevel(arg:Integer);
 private
  function  DoGetState:Integer; override;
  function  DoGetActive:Boolean; override;
  function  DoClose:Boolean; override;
  function  DoCancel:Boolean; override;
  function  DoGetProperties:LongString; override;
  procedure DoSetProperties(const arg:LongString); override;
  function  DoGetMode:Integer; override;
  procedure DoSetMode(arg:Integer); override;
  function  DoGetCursorLocation:Integer; override;
  procedure DoSetCursorLocation(arg:Integer); override;
  function  DoGetAttributes:Integer; override;
  procedure DoSetAttributes(arg:Integer); override;
  function  DoOpen(opt:Integer):Boolean; override;
  function  DoExecute(const arg:LongString; opt:Integer):TDbRecordset; override;
  function  GetRecordsAffected:Integer;
  function  GetBugsCount:SizeInt;
  function  GetBugsClear:SizeInt;
 public
  constructor Create(eid:Integer=0; const arg:LongString='');
  destructor  Destroy; override;
 public
  property  EngineId             : Integer    read GetEngineId;
  property  EngineName           : LongString read GetEngineName;
  property  ConnectionStringInit : LongString read GetConnectionStringInit;
  property  TimeStampInit        : Double     read GetTimeStampInit;
  property  TimeStampOpen        : Double     read GetTimeStampOpen;
  property  TimeStampUser1       : Double     read GetTimeStampUser1 write SetTimeStampUser1;
  property  TimeStampUser2       : Double     read GetTimeStampUser2 write SetTimeStampUser2;
  property  TimeStampUser3       : Double     read GetTimeStampUser3 write SetTimeStampUser3;
  property  UserState            : Integer    read GetUserState      write SetUserState;
  property  UserFlags            : Integer    read GetUserFlags      write SetUserFlags;
  property  UserLink             : Integer    read GetUserLink       write SetUserLink;
  property  UserData             : LongString read GetUserData       write SetUserData;
  property  Cookies              : LongString read GetCookies        write SetCookies;
  property  PwdCryptMode         : Integer    read GetPwdCryptMode   write SetPwdCryptMode;
  property  PwdCryptKey          : LongString read GetPwdCryptKey    write SetPwdCryptKey;
  property  PwdCryptIv           : LongString read GetPwdCryptIv     write SetPwdCryptIv;
  property  Version              : LongString read GetVersion;
  property  Errors               : LongString read GetErrors;
  property  ErrorsCount          : Integer    read GetErrorsCount;
  property  ErrorsClear          : Integer    read GetErrorsClear;
  property  Driver               : LongString read GetDriver;
  property  Provider             : LongString read GetProvider          write SetProvider;
  property  ConnectionString     : LongString read GetConnectionString  write SetConnectionString;
  property  ConnectionTimeout    : Integer    read GetConnectionTimeout write SetConnectionTimeout;
  property  CommandTimeout       : Integer    read GetCommandTimeout    write SetCommandTimeout;
  property  IsolationLevel       : Integer    read GetIsolationLevel    write SetIsolationLevel;
  property  DefaultDatabase      : LongString read GetDefaultDatabase   write SetDefaultDatabase;
  property  RecordsAffected      : Integer    read GetRecordsAffected;
  property  BugsCount            : SizeInt    read GetBugsCount;
  property  BugsClear            : SizeInt    read GetBugsClear;
 public
  function  IncBugsCount:SizeInt;
 public
  function  BeginTrans:Integer;
  function  CommitTrans:Boolean;
  function  RollbackTrans:Boolean;
 public
  function  CreateRecordset(arg:LongString):TDbRecordset;
  function  CreateCommand(arg:LongString):TDbCommand;
 end;
 TDbRecordset = class(TDbEntity)
 private
  {$IFDEF USES_ADODB}
  myAdoRecs : Recordset;
  {$ENDIF ~USES_ADODB}
  {$IFDEF USES_SQLDB}
  mySqlDbRecs : TSqlDbRecorder;
  {$ENDIF ~USES_SQLDB}
  {$IFDEF USES_ZEOS}
  myZeosRecs : TZeosRecorder;
  {$ENDIF ~USES_ZEOS}
 private
  function  DoBindRoot:Boolean; override;
  function  DoGetState:Integer; override;
  function  DoGetActive:Boolean; override;
  function  DoClose:Boolean; override;
  function  DoCancel:Boolean; override;
  function  DoGetCursorLocation:Integer; override;
  procedure DoSetCursorLocation(arg:Integer); override;
  function  DoOpen(opt:Integer):Boolean; override;
 public
  constructor Create(aParent:TDbParent; const aRecordset:Recordset; oRecordset:TObject);
  destructor  Destroy; override;
 end;
 TDbCommand = class(TDbEntity)
 private
  {$IFDEF USES_ADODB}
  myAdoComm  : Command;
  {$ENDIF ~USES_ADODB}
  {$IFDEF USES_SQLDB}
  mySqlDbComm : TSqlDbCommander;
  {$ENDIF ~USES_SQLDB}
  {$IFDEF USES_ZEOS}
  myZeosComm : TZeosCommander;
  {$ENDIF ~USES_ZEOS}
  myParams  : OleVariant;
 private
  function  DoBindRoot:Boolean; override;
  function  DoGetState:Integer; override;
  function  DoGetActive:Boolean; override;
  function  DoCancel:Boolean; override;
  function  DoExecute(const arg:LongString; opt:Integer):TDbRecordset; override;
 public
  constructor Create(aParent:TDbParent; const aCommand:Command; oCommand:TObject);
  destructor  Destroy; override;
 end;

type
 EDbGetFields = class(ESoftException);

////////////////////////////////////////////////////////////////////////////////
// Utility routines.
////////////////////////////////////////////////////////////////////////////////

function  NewDbConnection(eid:Integer=0; const arg:LongString=''):TDbConnection;
procedure Kill(var TheObject:TDbConnection); overload;
procedure Kill(var TheObject:TDbRecordset); overload;
procedure Kill(var TheObject:TDbCommand); overload;

function  FormatAdoError(Error:Error; const Prefix:LongString='Error '):LongString;
function  FormatAdoErrors(Errors:Errors; const Prefix:LongString='Error '):LongString;
function  FormatAdoProperty(Prop:Property_; const Prefix:LongString=''):LongString;
function  FormatAdoProperties(Properties:Properties; const Prefix:LongString=''):LongString;
function  CreateDatabaseWithAdoxCatalog(arg:LongString):Boolean;
procedure GetOleDbProviderNamesToText(Names:TText);
function  OleDbProviderNames:TText;
function  OdbcDriverNames:TText;
function  KnownDbApiProviderNames(Engine:Integer):LongString;

 ///////////////////////////////
 // Provides the List of engines
 ///////////////////////////////
function  db_engine_count:Integer;
function  db_engine_name(n:Integer):LongString;

 /////////////////////////////////////////////////////////////////
 // Multiname substitution for ConnectionString uses because
 // same parameters may have different names (User Id,User,UID):
 // cs:=db_subst_connectionstring(cs,'User Id;User;UID','SYSDBA');
 /////////////////////////////////////////////////////////////////
function db_subst_connectionstring(cs,id,sv:LongString):LongString;

 /////////////////////////////////////////////////////////////////
 // Multiname query key value from ConnectionString uses because
 // same parameters may have different names (User Id,User,UID):
 // user:=db_query_connectionstring(cs,'User Id;User;UID');
 /////////////////////////////////////////////////////////////////
function db_query_connectionstring(cs,id:LongString):LongString;

 /////////////////////////////////////////////////////////////////
 // Multiname drop keys from ConnectionString:
 // cs:=db_drop_connectionstring(cs,'User Id;User;UID');
 /////////////////////////////////////////////////////////////////
function db_drop_connectionstring(cs,id:LongString):LongString;

////////////////////////////////////////////////////////////////////////////////
// DbApi - easy DB interface for DaqPascal.
////////////////////////////////////////////////////////////////////////////////
// db_create(arg)             Create new DB (file) specified by (arg). Example:
//                            if db_create('Provider=LCPI.IBProvider.5.Free;'
//                            +'User Id=SYSDBA;Password=masterkey;ctype=win1251;'
//                            +'Location=localhost:c:\test.fdb;')
//                            then Writeln('DB created in c:\test.fdb');
// db_connection(eid,arg)     Create new connection object using engine (eid)
//                            and parameters specified by (arg). Example:
//                       dbc:=db_connection('Provider=LCPI.IBProvider.5.Free;'
//                            +'User Id=SYSDBA;Password=masterkey;ctype=win1251;'
//                            +'Location=localhost:c:\test.fdb;');
// db_free(dbo)               Free (destroy) database object (dbo)
// db_ref(dbo)                Return database object by reference (dbo)
// db_root(dbo)               Return root object - connection by reference (dbo)
// db_type(dbo)               Return type of (dbo), see db_type_xxx constants
// db_parent(dbo)             Return parent of (dbo)
// db_engineid(dbo)           Return EngineId of (dbo), see db_engine_xxx const
// db_state(dbo)              Return state of (dbo), see adStateXXX constants
// db_active(dbo)             Check object (dbo) is active (connected/opened)
// db_close(dbo)              Close connection of object (dbo)
// db_open(dbo,opt)           Open connection (db) with options (opt)
// db_ctrl(dbo,arg)           Control database object (dbo) with arguments (arg)
//                            arg is 'name' (to read) or 'name=value' (to write)
// db_bugscount(dbo)          Return bugs counter (bugs is any errors) of object
// db_bugsclear(dbo)          Return bugs counter and clear (zero)
// db_errors(dbo)             Return provider-specific errors as long text
// db_errorscount(dbo)        Return provider-specific errors counter
// db_errorsclear(dbo)        Return provider-specific errors counter and clear
// db_execute(dbo,arg,opt)    Execute SQL command (arg) with options (opt)
//                            Return reference of new recordset object
// db_cancel(dbo)             Cancel execution of SQL command
// db_update(dbr)             Update records of recordset (dbr)
// db_cancelupdate(dbr)       Cancel update of recordset (dbr)
// db_begintrans(dbc)         Begin transaction of connection (dbc)
// db_committrans(dbc)        Commit transaction of connection (dbc)
// db_rollbacktrans(dbc)      Rollback transaction of connection (dbc)
// db_bof(dbr)                Is begin of file with recordset (dbr) ?
// db_eof(dbr)                Is end   of file with recordset (dbr) ?
// db_movefirst(dbr)          Move to first record of recordset (dbr)
// db_movelast(dbr)           Move to last  record of recordset (dbr)
// db_movenext(dbr)           Move to next  record of recordset (dbr)
// db_moveprevious(dbr)       Move to prior record of recordset (dbr)
// db_fieldscount(dbr)        Return fields count  of recordset (dbr)
// db_fieldsnames(dbr,i)      Return fields names  of recordset at index (i)
// db_fieldstypes(dbr,i)      Return fields types  of recordset at index (i)
// db_fieldsasXXX(dbr,id,op,arg) Read/write (for op=r/w) data field name (id)
//                            of recordset (dbr) with data (arg).
//                        arg is default value in reading mode (op='r').
//                        arg is data to trite in wtiting mode (op='w').
//                            XXX is data type (int,float,string).
// db_addnew(dbr,arg)         Add new record to recordset (dbr) and fields (arg)
//                        arg is EOL delimited text of 'name' or 'name=value'.
// db_delete(dbr,aff)         Delete current record from recordset (dbr).
//                        aff is affected records adAffectCurrent/adAffectGroup
//                            where group is selected by Filter property.
// db_requery(dbr,opt)        Repeat query of recordset (dbr) with options (opt)
// db_resync(dbr,aff,res)     Refresh data in recordset (dbr) with affect (aff)
//                            and resync mode (res)
// db_supports(dbr,opt)       Check if recordset (opt) supports options (opt)
// db_save(dbr,dst,fmt)       Save recordset (dbr) to destination file (dst)
//                            in format specified (fmt)
////////////////////////////////////////////////////////////////////////////////

function db_create(arg:LongString):Boolean;
function db_connection(eid:Integer; arg:LongString):Integer;
function db_recordset(dbo:Integer; arg:LongString):Integer;
function db_command(dbo:Integer; arg:LongString):Integer;
function db_free(dbo:Integer):Boolean;
function db_ref(dbo:Integer):TDbEntity;
function db_root(dbo:Integer):TDbConnection;
function db_type(dbo:Integer):Integer;
function db_parent(dbo:Integer):Integer;
function db_engineid(dbo:Integer):Integer;
function db_state(dbo:Integer):Integer;
function db_active(dbo:Integer):Boolean;
function db_close(dbo:Integer):Boolean;
function db_open(dbo:Integer; opt:Integer):Boolean;
function db_ctrl(dbo:Integer; arg:LongString):LongString;
function db_bugscount(dbo:Integer):Integer;
function db_bugsclear(dbo:Integer):Integer;
function db_errors(dbo:Integer):LongString;
function db_errorscount(dbo:Integer):Integer;
function db_errorsclear(dbo:Integer):Integer;
function db_execute(dbo:Integer; arg:LongString; opt:Integer):TDbRecordset;
function db_cancel(dbo:Integer):Boolean;
function db_update(dbr:Integer):Boolean;
function db_cancelupdate(dbr:Integer):Boolean;
function db_begintrans(dbc:Integer):Integer;
function db_committrans(dbc:Integer):Boolean;
function db_rollbacktrans(dbc:Integer):Boolean;
function db_bof(dbr:Integer):Boolean;
function db_eof(dbr:Integer):Boolean;
function db_movefirst(dbr:Integer):Boolean;
function db_movelast(dbr:Integer):Boolean;
function db_movenext(dbr:Integer):Boolean;
function db_moveprevious(dbr:Integer):Boolean;
function db_fieldscount(dbr:Integer):Integer;
function db_fieldsnames(dbr:Integer; i:Integer):LongString;
function db_fieldstypes(dbr:Integer; id:LongString):Integer;
function db_fieldsasint(dbr:Integer; id:LongString; op:Char; arg:Integer):Integer;
function db_fieldsasfloat(dbr:Integer; id:LongString; op:Char; arg:Double):Double;
function db_fieldsasstring(dbr:Integer; id:LongString; op:Char; arg:LongString):LongString;
function db_addnew(dbr:Integer; arg:LongString):Boolean;
function db_delete(dbr:Integer; aff:Integer):Boolean;
function db_requery(dbr:Integer; opt:Integer):Boolean;
function db_resync(dbr:Integer; aff,res:Integer):Boolean;
function db_supports(dbr:Integer; opt:Integer):Boolean;
function db_save(dbr:Integer; dst:LongString; fmt:Integer):Boolean;

implementation

 /////////////////////////////////////////////////////
 // Private Dictionary for fast string identification.
 /////////////////////////////////////////////////////
type
 TStringIdentifier = (
  sid_Unknown,
  ////////////////////// Properties Writable
  sid_ConnectionString,
  sid_ConnectionTimeout,
  sid_CommandTimeout,
  sid_IsolationLevel,
  sid_Driver,
  sid_Provider,
  sid_DefaultDatabase,
  sid_CursorLocation,
  sid_Attributes,
  sid_AbsolutePage,
  sid_AbsolutePosition,
  sid_TimeStampUser1,
  sid_TimeStampUser2,
  sid_TimeStampUser3,
  sid_UserState,
  sid_UserFlags,
  sid_UserLink,
  sid_UserData,
  sid_Cookies,
  sid_PwdCryptMode,
  sid_PwdCryptKey,
  sid_PwdCryptIv,
  sid_Bookmark,
  sid_CacheSize,
  sid_CursorType,
  sid_Mode,
  sid_Filter,
  sid_Index,
  sid_LockType,
  sid_MarshalOptions,
  sid_MaxRecords,
  sid_PageSize,
  sid_Sort,
  sid_Source,
  sid_Status,
  sid_StayInSync,
  sid_GetString,
  sid_CommandType,
  sid_CommandText,
  sid_Properties,
  ////////////////////// Properties Readable
  sid_Errors,
  sid_ErrorsCount,
  sid_ErrorsClear,
  sid_ConnectionStringInit,
  sid_TimeStampInit,
  sid_TimeStampOpen,
  sid_State,
  sid_Version,
  sid_EngineId,
  sid_EngineName,
  sid_ProviderNames,
  sid_RecordsAffected,
  sid_PageCount,
  sid_RecordCount,
  sid_EditMode,
  sid_BugsCount,
  sid_BugsClear,
  sid_TotalBugsCount,
  sid_TotalBugsClear,
  sid_BugReportPrefix,
  sid_BugReportPrefixAll,
  sid_FieldTypeToString,
  ////////////////////// Properties End
  sid_Asterisk,
  sid_Unused
 );

const
 Dictionary:THashList=nil;

procedure FreeDictionary;
begin
 Kill(Dictionary);
end;

procedure InitDictionary;
 procedure AddSid(const key:LongString; sid:TStringIdentifier);
 begin
  Dictionary.KeyedLinks[key]:=Ord(sid);
 end;
begin
 if (Dictionary<>nil) then Exit;
 Dictionary:=NewHashList(false,HashList_DefaultHasher);
 Dictionary.Master:=@Dictionary;
 Dictionary.OwnsObjects:=false;
 /////////////////////////////////////////////
 // Dictionary for fast strings identification
 /////////////////////////////////////////////
 AddSid( 'ConnectionString'     , sid_ConnectionString);
 AddSid( 'ConnectionTimeout'    , sid_ConnectionTimeout);
 AddSid( 'CommandTimeout'       , sid_CommandTimeout);
 AddSid( 'IsolationLevel'       , sid_IsolationLevel);
 AddSid( 'Driver'               , sid_Driver);
 AddSid( 'Provider'             , sid_Provider);
 AddSid( 'DefaultDatabase'      , sid_DefaultDatabase);
 AddSid( 'CursorLocation'       , sid_CursorLocation);
 AddSid( 'Attributes'           , sid_Attributes);
 AddSid( 'AbsolutePage'         , sid_AbsolutePage);
 AddSid( 'AbsolutePosition'     , sid_AbsolutePosition);
 AddSid( 'TimeStampUser1'       , sid_TimeStampUser1);
 AddSid( 'TimeStampUser2'       , sid_TimeStampUser2);
 AddSid( 'TimeStampUser3'       , sid_TimeStampUser3);
 AddSid( 'UserState'            , sid_UserState);
 AddSid( 'UserFlags'            , sid_UserFlags);
 AddSid( 'UserLink'             , sid_UserLink);
 AddSid( 'UserData'             , sid_UserData);
 AddSid( 'Cookies'              , sid_Cookies);
 AddSid( 'PasswordCryptMode'    , sid_PwdCryptMode);
 AddSid( 'PwdCryptMode'         , sid_PwdCryptMode);
 AddSid( 'PasswordCryptKey'     , sid_PwdCryptKey);
 AddSid( 'PwdCryptKey'          , sid_PwdCryptKey);
 AddSid( 'PasswordCryptIv'      , sid_PwdCryptIv);
 AddSid( 'PwdCryptIv'           , sid_PwdCryptIv);
 AddSid( 'Bookmark'             , sid_Bookmark);
 AddSid( 'CacheSize'            , sid_CacheSize);
 AddSid( 'CursorType'           , sid_CursorType);
 AddSid( 'Mode'                 , sid_Mode);
 AddSid( 'Filter'               , sid_Filter);
 AddSid( 'Index'                , sid_Index);
 AddSid( 'LockType'             , sid_LockType);
 AddSid( 'MarshalOptions'       , sid_MarshalOptions);
 AddSid( 'MaxRecords'           , sid_MaxRecords);
 AddSid( 'PageCount'            , sid_PageCount);
 AddSid( 'PageSize'             , sid_PageSize);
 AddSid( 'RecordCount'          , sid_RecordCount);
 AddSid( 'Sort'                 , sid_Sort);
 AddSid( 'Source'               , sid_Source);
 AddSid( 'Status'               , sid_Status);
 AddSid( 'StayInSync'           , sid_StayInSync);
 AddSid( 'GetString'            , sid_GetString);
 AddSid( 'CommandType'          , sid_CommandType);
 AddSid( 'CommandText'          , sid_CommandText);
 AddSid( 'Properties'           , sid_Properties);
 AddSid( 'Errors'               , sid_Errors);
 AddSid( 'ErrorsCount'          , sid_ErrorsCount);
 AddSid( 'ErrorsClear'          , sid_ErrorsClear);
 AddSid( 'ConnectionStringInit' , sid_ConnectionStringInit);
 AddSid( 'TimeStampInit'        , sid_TimeStampInit);
 AddSid( 'TimeStampOpen'        , sid_TimeStampOpen);
 AddSid( 'State'                , sid_State);
 AddSid( 'Version'              , sid_Version);
 AddSid( 'EngineId'             , sid_EngineId);
 AddSid( 'EngineName'           , sid_EngineName);
 AddSid( 'ProviderNames'        , sid_ProviderNames);
 AddSid( 'RecordsAffected'      , sid_RecordsAffected);
 AddSid( 'EditMode'             , sid_EditMode);
 AddSid( 'BugsCount'            , sid_BugsCount);
 AddSid( 'BugsClear'            , sid_BugsClear);
 AddSid( 'TotalBugsCount'       , sid_TotalBugsCount);
 AddSid( 'TotalBugsClear'       , sid_TotalBugsClear);
 AddSid( 'BugReportPrefix'      , sid_BugReportPrefix);
 AddSid( 'BugReportPrefixAll'   , sid_BugReportPrefixAll);
 AddSid( 'FieldTypeToString'    , sid_FieldTypeToString);
 AddSid( '*'                    , sid_Asterisk);
end;

function Identify(const key:LongString):TStringIdentifier;
var sid:Integer;
begin
 if (Dictionary=nil) then InitDictionary;
 sid:=Dictionary.KeyedLinks[key];
 if (sid>=Ord(Low(TStringIdentifier))) and (sid<=Ord(High(TStringIdentifier)))
 then Result:=TStringIdentifier(sid)
 else Result:=sid_Unknown;
end;

const
 sid_Control_Writable = [Succ(sid_Unknown)..Pred(sid_Errors)];
 sid_Control_Readable = [Succ(sid_Unknown)..Pred(sid_Asterisk)];

///////////////////////////
// Internal helper routines
///////////////////////////

function SafeListCount(aList:TList):Integer;
begin
 if (aList<>nil)
 then Result:=aList.Count
 else Result:=0;
end;

function SafeListItems(aList:TList; aIndex:Integer):Pointer;
begin
 if (aList<>nil) and (aIndex>=0) and (aIndex<aList.Count)
 then Result:=aList.Items[aIndex]
 else Result:=nil;
end;

procedure SafeListAdd(aList:TList; aItem:Pointer);
begin
 if (aList<>nil) and (aItem<>nil)
 then aList.Add(aItem);
end;

procedure SafeListRemove(aList:TList; aItem:Pointer);
begin
 if (aList<>nil) and (aItem<>nil)
 then aList.Remove(aItem);
end;

///////////////////
// Utility routines
///////////////////

procedure BugReport(E:Exception; O:TObject; const S:LongString);
begin
 TDbConnection.IncTotalBugsCount;
 if (StrFetch(S,1)='@')
 then _crw_alloc.BugReport(E,O,S)
 else _crw_alloc.BugReport(E,O,db_bugreport_prefix+S);
end;

function Br2sp(const s:LongString):LongString; // Line breaks to spaces
begin
 Result:=SysUtils.Trim(StringReplace(AdjustLineBreaks(s),EOL,' ',[rfReplaceAll]));
end;

function VarToStrDef(const v:Variant; const def:LongString):LongString;
begin
 Result:=def;
 try
  Result:=VarToStr(v);
 except
  on E:Exception do BugReport(E,nil,'VarToStrDef');
  //TDbConnection.IncTotalBugsCount; Result:=def;
 end;
end;

function FormatAdoError(Error:Error; const Prefix:LongString='Error '):LongString;
begin
 Result:='';
 if (Error<>nil) then
 try
  {$IFDEF USES_ADODB}
  Result:=Prefix+
          Format('Number=$%X;Description=%s;'
                +'HelpFile=%s;HelpContext=%d;'
                +'Source=%s;SQLState=%s;NativeError=$%X;',
                [Error.Number,Br2sp(WideToStr(Error.Description)),
                 Error.HelpFile,Error.HelpContext,
                 Error.Source,Error.SQLState,Error.NativeError]);
  {$ENDIF ~USES_ADODB}
 except
  on E:Exception do BugReport(E,nil,'FormatAdoError');
 end;
end;

function FormatAdoErrors(Errors:Errors; const Prefix:LongString='Error '):LongString;
{$IFDEF USES_ADODB} var i:Integer; Line:LongString; {$ENDIF ~USES_ADODB}
var Lines:TStringList;
begin
 Result:='';
 if (Errors<>nil) then
 try
  Lines:=TStringList.Create;
  try
   {$IFDEF USES_ADODB}
   for i:=0 to Errors.Count-1 do begin
    Line:=FormatAdoError(Errors.Item[i],Prefix);
    if (Line<>'') then Lines.Add(Line);
   end;
   {$ENDIF ~USES_ADODB}
   if (Lines.Count>0) then Result:=Lines.Text;
  finally
   Lines.Free;
  end;
 except
  on E:Exception do BugReport(E,nil,'FormatAdoErrors');
 end;
end;

function FormatAdoProperty(Prop:Property_; const Prefix:LongString=''):LongString;
{$IFDEF USES_ADODB} var pt,pa:Integer; pn,pv:LongString; {$ENDIF ~USES_ADODB}
begin
 Result:='';
 if (Prop<>nil) then
 try
  {$IFDEF USES_ADODB}
  pn:=WideToStr(Prop.Name); pt:=Prop.Type_; pa:=Prop.Attributes; pv:=VarToStrDef(Prop.Value,'');
  Result:=Prefix+Format('%s=%d,%d,%s',[pn,pt,pa,pv]);
  {$ENDIF ~USES_ADODB}
 except
  on E:Exception do BugReport(E,nil,'FormatAdoProperty');
 end;
end;

function FormatAdoProperties(Properties:Properties; const Prefix:LongString=''):LongString;
var Lines:TStringList; {$IFDEF USES_ADODB} i:Integer; Line:LongString; {$ENDIF ~USES_ADODB}
begin
 Result:='';
 if (Properties<>nil) then
 try
  Lines:=TStringList.Create;
  try
   {$IFDEF USES_ADODB}
   for i:=0 to Properties.Count-1 do begin
    Line:=FormatAdoProperty(Properties.Item[i],Prefix);
    if (Line<>'') then Lines.Add(Line);
   end;
   {$ENDIF ~USES_ADODB}
   if (Lines.Count>0) then Result:=Lines.Text;
  finally
   Lines.Free;
  end;
 except
  on E:Exception do BugReport(E,nil,'FormatAdoProperties');
 end;
end;

function AssignAdoProperties(Properties:Properties; const arg:LongString):Boolean;
{$IFDEF USES_ADODB} var i,p,pa:Integer; Line,sn,sv:LongString; {$ENDIF ~USES_ADODB}
var Lines:TStringList;
begin
 Result:=false;
 if (arg<>'') then
 if (Properties<>nil) then
 try
  Lines:=TStringList.Create;
  try
   {$IFDEF USES_ADODB}
   Lines.Text:=AdjustLineBreaks(arg);
   for i:=0 to Lines.Count-1 do begin
    Line:=Lines[i]; p:=Pos('=',Line); if (p=0) then Continue;
    sn:=Copy(Line,1,p-1); sv:=Copy(Line,p+1,MaxInt);
    sn:=SysUtils.Trim(sn); sv:=SysUtils.Trim(sv);
    pa:=Properties.Item[sn].Attributes;
    if ((pa and adPropWrite)=0) then Continue;
    Properties.Item[sn].Value:=sv;
   end;
   Result:=true;
   {$ENDIF ~USES_ADODB}
  finally
   Lines.Free;
  end;
 except
  on E:Exception do BugReport(E,nil,'AssignAdoProperties');
 end;
end;

function CreateDatabaseWithAdoxCatalog(arg:LongString):Boolean;
{$IFDEF USES_ADODB} var catalog:Variant; {$ENDIF ~USES_ADODB}
begin
 Result:=false;
 if (arg<>'') then
 try
  {$IFDEF USES_ADODB}
  catalog:=CreateOleObject('ADOX.Catalog');
  try
   catalog.Create(arg);
   Result:=true;
  finally
   catalog:=Unassigned;
  end;
  {$ENDIF ~USES_ADODB}
 except
  on E:Exception do BugReport(E,nil,'CreateDatabaseWithAdoxCatalog');
 end;
end;

procedure GetOleDbProviderNamesToText(Names:TText); // From AdoDb, modified
{$IFDEF USES_ADODB}
var
 RSCon: ADORecordsetConstruction;
 Rowset: IRowset;
 SourcesRowset: ISourcesRowset;
 SourcesRecordset: _Recordset;
 SourceType:Integer; SourceName:LongString;
{$ENDIF ~USES_ADODB}
begin
 if (Names<>nil) then
 try
  {$IFDEF USES_ADODB}
  // First prepare OleDb enumerator
  SourcesRecordset := CoRecordset.Create;
  RSCon := SourcesRecordset as ADORecordsetConstruction;
  SourcesRowset := CreateComObject(CLSID_OLEDB_ENUMERATOR) as ISourcesRowset;
  OleCheck(SourcesRowset.GetSourcesRowset(nil, IRowset, 0, nil, IUnknown(Rowset)));
  RSCon.Set_Rowset(RowSet);
  // Then update list
  while not  SourcesRecordset.EOF do begin
   SourceType:=SourcesRecordset.Fields.Item['SOURCES_TYPE'].Value;
   if (SourceType=DBSOURCETYPE_DATASOURCE) then begin
    SourceName:=VarToStr(SourcesRecordset.Fields.Item['SOURCES_NAME'].Value);
    Names.Addln(SourceName);
   end;
   SourcesRecordset.MoveNext;
  end;
  {$ENDIF ~USES_ADODB}
 except
  on E:Exception do BugReport(E,nil,'GetOleDbProviderNamesToText');
 end;
end;

const
 TheOleDbProviderNames:TText=nil;

function OleDbProviderNames:TText;
begin
 if (TheOleDbProviderNames=nil) then begin
  TheOleDbProviderNames:=NewText(32);
  TheOleDbProviderNames.Master:=@TheOleDbProviderNames;
 end;
 if (TheOleDbProviderNames.Count=0)
 then GetOleDbProviderNamesToText(TheOleDbProviderNames);
 Result:=TheOleDbProviderNames;
end;

function KnownDbApiProviderNames(Engine:Integer):LongString;
begin
 Result:='';
 case Engine of
  db_engine_ado: Result:=OleDbProviderNames.Text;
  {$IFDEF USES_SQLDB}
  db_engine_sqldb: Result:=SqlDbAssistant.KnownProviders;
  {$ENDIF}
  {$IFDEF USES_ZEOS}
  db_engine_zeos: Result:=ZeosAssistant.KnownProviders;
  {$ENDIF}
 end;
end;

////////////////////////////////////////////////////////////////////////////////
// SQLGetInstalledDrivers reads the [ODBC Drivers] section of the system information
// and returns a list of descriptions of the installed drivers.
//
// Location: odbccp32.dll
//
// BOOL SQLGetInstalledDrivers(LPSTR lpszBuf, WORD cbBufMax, WORD *  pcbBufOut);
//
// pszBuf    [Output] List of descriptions of the installed drivers.
// cbBufMax  [Input]  Length of lpszBuf.
// pcbBufOut [Output] Total number of bytes (excluding the null-termination byte)
//           returned in lpszBuf. If the number of bytes available to return
//           is greater than or equal to cbBufMax, the list of driver descriptions
//           in lpszBuf is truncated to cbBufMax minus the null-termination character.
//           The pcbBufOut argument can be a null pointer.
// The function returns TRUE if it is successful, FALSE if it fails.
////////////////////////////////////////////////////////////////////////////////
// https://github.com/MicrosoftDocs/sql-docs/blob/live/docs/odbc/reference/syntax/sqlgetinstalleddrivers-function.md
////////////////////////////////////////////////////////////////////////////////

{$IFDEF USES_ADODB}
function SQLGetInstalledDrivers(lpszBuf:PChar; cbBufMax:WORD; pcbBufOut:PWORD):BOOL; stdcall;
type FunSQLGetInstalledDrivers=function(lpszBuf:PChar; cbBufMax:WORD; pcbBufOut:PWORD):BOOL; stdcall;
const F:FunSQLGetInstalledDrivers=nil; hDll:THandle=0; Flag:Boolean=false;
const sDll='odbccp32.dll'; sFun='SQLGetInstalledDrivers';
begin
 Result:=false;
 try
  if not Flag then begin
   hDll:=GetModuleHandle(sDll);
   if (hDll=0) then hDll:=SafeLoadLibrary(sDll);
   if (hDll<>0) then @F:=GetProcAddress(hDll,sFun);
   Flag:=true;
  end;
  if (@F<>nil) then Result:=F(lpszBuf,cbBufMax,pcbBufOut);
 except
  on E:Exception do BugReport(E,nil,'SQLGetInstalledDrivers');
 end;
end;
{$ENDIF ~USES_ADODB}

function GetInstalledOdbcDrivers:LongString;
{$IFDEF USES_ADODB}
var lpszBuf:PChar; cbBufMax,pcbBufOut:WORD; n,len:Integer;
{$ENDIF ~USES_ADODB}
var Lines:TStringList; Buf,Line:LongString;
begin
 Result:='';
 try
  Lines:=TStringList.Create;
  try
   {$IFDEF USES_ADODB}
   Buf:=StringOfChar(#0,1024*48); n:=0;
   lpszBuf:=PChar(Buf); cbBufMax:=Length(Buf); pcbBufOut:=0;
   if SQLGetInstalledDrivers(lpszBuf,cbBufMax,@pcbBufOut) then
   if (lpszBuf<>nil) and (pcbBufOut>0) then
   while (StrLen(lpszBuf)>0) and (n<pcbBufOut) do begin
    Line:=lpszBuf; Line:=SysUtils.Trim(Line);
    if (Length(Line)>0) then Lines.Add(Line);
    len:=StrLen(lpszBuf);
    inc(lpszBuf,len+1);
    inc(n,len+1);
   end;
   {$ENDIF ~USES_ADODB}
   {$IFDEF USES_SQLDB}
   if IsUnix then Lines.Text:=ReadUnixOdbcDriverList;
   {$ENDIF ~USES_SQLDB}
   {$IFDEF USES_ZEOS}
   //if IsUnix then Lines.Text:=ReadUnixOdbcDriverList;
   {$ENDIF ~USES_ZEOS}
   Result:=Lines.Text;
  finally
   Lines.Free;
   Line:='';
   Buf:='';
  end;
 except
  on E:Exception do BugReport(E,nil,'GetInstalledOdbcDrivers');
 end;
end;

const
 TheOdbcDriverNames:TText=nil;

function OdbcDriverNames:TText;
begin
 if (TheOdbcDriverNames=nil) then begin
  TheOdbcDriverNames:=NewText(64);
  TheOdbcDriverNames.Master:=@TheOdbcDriverNames;
 end;
 if (TheOdbcDriverNames.Count=0)
 then TheOdbcDriverNames.Text:=GetInstalledOdbcDrivers;
 Result:=TheOdbcDriverNames;
end;

function db_engine_count:Integer;
begin
 Result:=WordCount(db_engine_list_csv,ScanSpaces);
end;

function db_engine_name(n:Integer):LongString;
begin
 if (n=db_engine_def) then n:=db_engine_default;
 Result:=ExtractWord(n,db_engine_list_csv,ScanSpaces);
end;

function db_subst_connectionstring(cs,id,sv:LongString):LongString;
begin
 Result:=TDbEngineAssistant.SubstParam(cs,id,sv);
end;

function db_query_connectionstring(cs,id:LongString):LongString;
begin
 Result:=TDbEngineAssistant.FetchParam(cs,id);
end;

function db_drop_connectionstring(cs,id:LongString):LongString;
begin
 Result:=TDbEngineAssistant.DropParam(cs,id);
end;

function CookieDropItems(const Buff,ids:LongString; Sep:Char=';'):LongString;
var Lines:TStringList; i,iw,n:Integer; id,sn,sv:LongString;
begin
 Result:=Buff;
 try
  Lines:=TStringList.Create;
  try
   Lines.Text:=StringReplace(Buff,Sep,EOL,[rfReplaceAll]);
   n:=Lines.Count;
   for iw:=1 to WordCount(ids,ScanSpaces) do begin
    id:=ExtractWord(iw,ids,ScanSpaces);
    for i:=Lines.Count-1 downto 0 do begin
     if ExtractNameValuePair(Lines[i],sn,sv)>0 then begin
      if IsSameText(sn,id) then Lines.Delete(i);
     end else Lines.Delete(i);
    end;
   end;
   if (Lines.Count<>n)
   then Result:=StringReplace(Lines.Text,EOL,Sep,[rfReplaceAll]);
  finally
   Kill(Lines);
  end;
 except
  on E:Exception do BugReport(E,nil,'CookieDropItems');
 end;
end;

function CreateDatabase(arg:LongString):Boolean;
var Engine,dbc:Integer; sEngine:LongString;
const EngineNames='Engine,EngineId,EngineName';
begin
 Result:=False;
 try
  Engine:=0;
  sEngine:=Trim(CookieScanAlter(arg,EngineNames,Ord(';')));
  if not TryStrToInt(sEngine,Engine) then Engine:=WordIndex(sEngine,db_engine_list_csv,ScanSpaces);
  if (Engine<=0) and IsWindows then Engine:=db_engine_ado;
  if (Engine<=0) then Exit;
  arg:=CookieDropItems(arg,EngineNames);
  case Engine of
   db_engine_ado: begin
    Result:=CreateDatabaseWithAdoxCatalog(arg);
    Exit;
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    dbc:=db_connection(Engine,arg);
    try
     if (db_ref(dbc) is TDbConnection) then begin
      with (db_ref(dbc) as TDbConnection) do
      Result:=mySqlDbConn.CreateDatabase;
     end;
    finally
     db_free(dbc);
    end;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    dbc:=db_connection(Engine,arg);
    try
     if (db_ref(dbc) is TDbConnection) then begin
      with (db_ref(dbc) as TDbConnection) do
      Result:=myZeosConn.CreateDatabase;
     end;
    finally
     db_free(dbc);
    end;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,nil,'CreateDatabase');
 end;
end;

///////////////////////////
// TDbEntity implementation
///////////////////////////

constructor TDbEntity.Create(aParent:TDbParent);
begin
 inherited Create;
 myTypeId:=db_type_entity;
 myParent:=aParent;
 myBgPref:=db_bugreport_prefix;
end;

destructor TDbEntity.Destroy;
begin
 myBgPref:='';
 myParent:=nil;
 inherited Destroy;
end;

procedure TDbEntity.AfterConstruction;
begin
 inherited AfterConstruction;
 Parent.AppendChild(Self);
 if db_bindroot_oncreate
 then BindRoot;
end;

procedure TDbEntity.BeforeDestruction;
begin
 FreeAllChilds;
 Parent.RemoveChild(Self);
 inherited BeforeDestruction;
end;

procedure TDbEntity.FreeAllChilds;
var i:Integer;
begin
 for i:=ChildCount-1 downto 0 do ChildItems[i].Free;
end;

function TDbEntity.GetChildCount:Integer;
begin
 if (Self<>nil)
 then Result:=DoGetChildCount
 else Result:=0;
end;

function TDbEntity.DoGetChildCount:Integer;
begin
 Result:=0;
end;

function TDbEntity.GetChildItems(i:Integer):TDbEntity;
begin
 if (Self<>nil)
 then Result:=DoGetChildItems(i)
 else Result:=nil;
end;

function TDbEntity.DoGetChildItems(i:Integer):TDbEntity;
begin
 Result:=nil;
end;

procedure TDbEntity.BugReport(E:Exception; O:TObject; const S:LongString);
begin
 Root.IncBugsCount;
 if (Self=nil)
 then _crw_dbapi.BugReport(E,O,S)
 else _crw_dbapi.BugReport(E,O,myBgPref+S);
end;

function TDbEntity.GetTypeId:Integer;
begin
 if (Self<>nil)
 then Result:=myTypeId
 else Result:=0;
end;

function TDbEntity.GetParent:TDbParent;
begin
 if (Self<>nil)
 then Result:=myParent
 else Result:=nil;
end;

function TDbEntity.Root:TDbConnection;
var E:TDbEntity;
begin
 Result:=nil;
 if (Self=nil) then Exit;
 E:=Self; while (E<>nil) and (E.TypeId<>db_type_connection) do E:=E.Parent;
 if (E is TDbConnection) then Result:=TDbConnection(E);
end;

function TDbEntity.BindRoot:Boolean;
begin
 if (Self<>nil)
 then Result:=DoBindRoot
 else Result:=false;
end;

function TDbEntity.DoBindRoot:Boolean;
begin
 Result:=false;
end;

function TDbEntity.GetState:Integer;
begin
 if (Self<>nil)
 then Result:=DoGetState
 else Result:=0;
end;

function TDbEntity.GetActive:Boolean;
begin
 if (Self<>nil)
 then Result:=DoGetActive
 else Result:=False;
end;

function TDbEntity.DoGetState:Integer;
begin
 Result:=0;
end;

function TDbEntity.DoGetActive:Boolean;
begin
 Result:=False;
end;

function TDbEntity.Close:Boolean;
begin
 if (Self<>nil)
 then Result:=DoClose
 else Result:=false;
end;

function TDbEntity.DoClose:Boolean;
begin
 Result:=false;
end;

function TDbEntity.Cancel:Boolean;
begin
 if (Self<>nil)
 then Result:=DoCancel
 else Result:=false;
end;

function TDbEntity.DoCancel:Boolean;
begin
 Result:=false;
end;

function TDbEntity.GetProperties:LongString;
begin
 if (Self<>nil)
 then Result:=DoGetProperties
 else Result:='';
end;

function TDbEntity.DoGetProperties:LongString;
begin
 Result:=''
end;

procedure TDbEntity.SetProperties(const arg:LongString);
begin
 if (Self=nil) then Exit;
 DoSetProperties(arg);
end;

procedure TDbEntity.DoSetProperties(const arg:LongString);
begin
end;

function TDbEntity.Open(opt:Integer):Boolean;
begin
 if (Self<>nil)
 then Result:=DoOpen(opt)
 else Result:=false;
end;

function TDbEntity.DoOpen(opt:Integer):Boolean;
begin
 Result:=false;
end;

function TDbEntity.GetMode:Integer;
begin
 if (Self<>nil)
 then Result:=DoGetMode
 else Result:=0;
end;

function TDbEntity.DoGetMode:Integer;
begin
 Result:=0;
end;

procedure TDbEntity.SetMode(arg:Integer);
begin
 if (Self=nil) then Exit;
 DoSetMode(arg);
end;

procedure TDbEntity.DoSetMode(arg:Integer);
begin
end;

function TDbEntity.GetCursorLocation:Integer;
begin
 if (Self<>nil)
 then Result:=DoGetCursorLocation
 else Result:=0;
end;

function TDbEntity.DoGetCursorLocation:Integer;
begin
 Result:=0;
end;

procedure TDbEntity.SetCursorLocation(arg:Integer);
begin
 if (Self=nil) then Exit;
 DoSetCursorLocation(arg);
end;

procedure TDbEntity.DoSetCursorLocation(arg:Integer);
begin
end;

function TDbEntity.GetAttributes:Integer;
begin
 if (Self<>nil)
 then Result:=DoGetAttributes
 else Result:=0;
end;

function TDbEntity.DoGetAttributes:Integer;
begin
 Result:=0;
end;

procedure TDbEntity.SetAttributes(arg:Integer);
begin
 if (Self=nil) then Exit;
 DoSetAttributes(arg);
end;

procedure TDbEntity.DoSetAttributes(arg:Integer);
begin
end;

function TDbEntity.GetAbsolutePage:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbRecordset).myAdoRecs.AbsolutePage;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.AbsolutePage;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.AbsolutePage;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetAbsolutePage');
 end;
end;

procedure TDbEntity.SetAbsolutePage(arg:Integer);
begin
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.AbsolutePage:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbRecordset).mySqlDbRecs.AbsolutePage:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbRecordset).myZeosRecs.AbsolutePage:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetAbsolutePage');
 end;
end;

function TDbEntity.GetAbsolutePosition:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbRecordset).myAdoRecs.AbsolutePosition;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.AbsolutePosition;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.AbsolutePosition;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetAbsolutePosition');
 end;
end;

procedure TDbEntity.SetAbsolutePosition(arg:Integer);
begin
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.AbsolutePosition:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbRecordset).mySqlDbRecs.AbsolutePosition:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbRecordset).myZeosRecs.AbsolutePosition:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetAbsolutePosition');
 end;
end;

function TDbEntity.GetBof:Boolean;
begin
 Result:=true;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbRecordset).myAdoRecs.BOF;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.BOF;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.BOF;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetBof');
 end;
end;

function TDbEntity.GetEof:Boolean;
begin
 Result:=true;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbRecordset).myAdoRecs.EOF;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.EOF;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.EOF;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetEof');
 end;
end;

function TDbEntity.GetBookmark:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbRecordset).myAdoRecs.Bookmark;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.Bookmark;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.Bookmark;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetBookmark');
 end;
end;

procedure TDbEntity.SetBookmark(arg:Integer);
begin
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.Bookmark:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbRecordset).mySqlDbRecs.Bookmark:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbRecordset).myZeosRecs.Bookmark:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetBookmark');
 end;
end;

function TDbEntity.GetCacheSize:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbRecordset).myAdoRecs.CacheSize;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.CacheSize;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.CacheSize;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetCacheSize');
 end;
end;

procedure TDbEntity.SetCacheSize(arg:Integer);
begin
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.CacheSize:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbRecordset).mySqlDbRecs.CacheSize:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbRecordset).myZeosRecs.CacheSize:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetCacheSize');
 end;
end;

function TDbEntity.GetCursorType:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbRecordset).myAdoRecs.CursorType;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.CursorType;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.CursorType;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetCursorType');
 end;
end;

procedure TDbEntity.SetCursorType(arg:Integer);
begin
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.CursorType:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbRecordset).mySqlDbRecs.CursorType:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbRecordset).myZeosRecs.CursorType:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetCursorType');
 end;
end;

function TDbEntity.GetEditMode:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbRecordset).myAdoRecs.EditMode;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
     Result:=(Self as TDbRecordset).mySqlDbRecs.EditMode;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
     Result:=(Self as TDbRecordset).myZeosRecs.EditMode;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetEditMode');
 end;
end;

function TDbEntity.GetFilter:LongString;
begin
 Result:='';
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=VarToStr((Self as TDbRecordset).myAdoRecs.Filter);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.Filter;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.Filter;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetFilter');
 end;
end;

procedure TDbEntity.SetFilter(arg:LongString);
begin
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.Filter:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbRecordset).mySqlDbRecs.Filter:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbRecordset).myZeosRecs.Filter:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetFilter');
 end;
end;

function TDbEntity.GetIndex:LongString;
begin
 Result:='';
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=WideToStr((Self as TDbRecordset).myAdoRecs.Index);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.Index;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.Index;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetIndex');
 end;
end;

procedure TDbEntity.SetIndex(arg:LongString);
begin
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.Index:=StrToWide(arg);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbRecordset).mySqlDbRecs.Index:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbRecordset).myZeosRecs.Index:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetIndex');
 end;
end;

function TDbEntity.GetLockType:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbRecordset).myAdoRecs.LockType;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.LockType;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.LockType;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetLockType');
 end;
end;

procedure TDbEntity.SetLockType(arg:Integer);
begin
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.LockType:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbRecordset).mySqlDbRecs.LockType:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbRecordset).myZeosRecs.LockType:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetLockType');
 end;
end;

function TDbEntity.GetMarshalOptions:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbRecordset).myAdoRecs.MarshalOptions;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.MarshalOptions;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.MarshalOptions;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetMarshalOptions');
 end;
end;

procedure TDbEntity.SetMarshalOptions(arg:Integer);
begin
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.MarshalOptions:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbRecordset).mySqlDbRecs.MarshalOptions:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbRecordset).myZeosRecs.MarshalOptions:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetMarshalOptions');
 end;
end;

function TDbEntity.GetMaxRecords:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbRecordset).myAdoRecs.MaxRecords;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.MaxRecords;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.MaxRecords;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetMaxRecords');
 end;
end;

procedure TDbEntity.SetMaxRecords(arg:Integer);
begin
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.MaxRecords:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbRecordset).mySqlDbRecs.MaxRecords:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbRecordset).myZeosRecs.MaxRecords:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetMaxRecords');
 end;
end;

function TDbEntity.GetPageCount:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbRecordset).myAdoRecs.PageCount;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.PageCount;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.PageCount;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetPageCount');
 end;
end;

function TDbEntity.GetPageSize:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbRecordset).myAdoRecs.PageSize;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.PageSize;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.PageSize;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetPageSize');
 end;
end;

procedure TDbEntity.SetPageSize(arg:Integer);
begin
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.PageSize:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbRecordset).mySqlDbRecs.PageSize:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbRecordset).myZeosRecs.PageSize:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetPageSize');
 end;
end;

function TDbEntity.GetRecordCount:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbRecordset).myAdoRecs.RecordCount;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.RecordCount;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.RecordCount;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetRecordCount');
 end;
end;

function TDbEntity.GetSort:LongString;
begin
 Result:='';
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=WideToStr((Self as TDbRecordset).myAdoRecs.Sort);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.Sort;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.Sort;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetSort');
 end;
end;

procedure TDbEntity.SetSort(arg:LongString);
begin
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.Sort:=StrToWide(arg);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbRecordset).mySqlDbRecs.Sort:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbRecordset).myZeosRecs.Sort:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetSort');
 end;
end;

function TDbEntity.GetSource:LongString;
begin
 Result:='';
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=VarToStr((Self as TDbRecordset).myAdoRecs.Get_Source);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.Source;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.Source;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetSource');
 end;
end;

procedure TDbEntity.SetSource(arg:LongString);
begin
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs._Set_Source(StrToWide(arg));
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbRecordset).mySqlDbRecs.Source:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbRecordset).myZeosRecs.Source:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetSource');
 end;
end;

function TDbEntity.GetStatus:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbRecordset).myAdoRecs.Status;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.Status;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.Status;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetStatus');
 end;
end;

function TDbEntity.GetStayInSync:Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbRecordset).myAdoRecs.StayInSync;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.StayInSync;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.StayInSync;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetStayInSync');
 end;
end;

procedure TDbEntity.SetStayInSync(arg:Boolean);
begin
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.StayInSync:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbRecordset).mySqlDbRecs.StayInSync:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbRecordset).myZeosRecs.StayInSync:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetStayInSync');
 end;
end;

function TDbEntity.MoveFirst:Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.MoveFirst;
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.MoveFirst;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.MoveFirst;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'MoveFirst');
 end;
end;

function TDbEntity.MoveLast:Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.MoveLast;
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.MoveLast;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.MoveLast;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'MoveLast');
 end;
end;

function TDbEntity.MoveNext:Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.MoveNext;
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.MoveNext;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.MoveNext;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'MoveNext');
 end;
end;

function TDbEntity.MovePrevious:Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.MovePrevious;
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.MovePrevious;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.MovePrevious;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'MovePrevious');
 end;
end;

function TDbEntity.Update:Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.Update(EmptyParam,EmptyParam);
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.Update;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.Update;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'Update');
 end;
end;

function TDbEntity.CancelUpdate:Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.CancelUpdate;
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.CancelUpdate;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.CancelUpdate;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'CancelUpdate');
 end;
end;

function TDbEntity.Requery(Options:Integer):Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.Requery(Options);
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbRecordset).mySqlDbRecs.Requery(Options);
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbRecordset).myZeosRecs.Requery(Options);
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'Requery');
 end;
end;

function TDbEntity.Resync(AffectRecords,ResyncValues:Integer):Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.Resync(AffectRecords,ResyncValues);
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbRecordset).mySqlDbRecs.Resync(AffectRecords,ResyncValues);
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbRecordset).myZeosRecs.Resync(AffectRecords,ResyncValues);
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'Resync');
 end;
end;

function TDbEntity.Supports(CursorOptions:Integer):Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.Supports(CursorOptions);
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbRecordset).mySqlDbRecs.Supports(CursorOptions);
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbRecordset).myZeosRecs.Supports(CursorOptions);
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'Supports');
 end;
end;

function TDbEntity.Save(Destination:LongString; PersistFormat:Integer):Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.Save(Destination,PersistFormat);
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.Save(Destination,PersistFormat);
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.Save(Destination,PersistFormat);
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'Save');
 end;
end;

function TDbEntity.FieldsCount:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbRecordset).myAdoRecs.Fields.Count;
    {$ENDIF  ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.FieldsCount;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.FieldsCount;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'FieldsCount');
 end;
end;

function TDbEntity.FieldsNames(i:Integer):LongString;
begin
 Result:='';
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    if (i<0) or (i>=FieldsCount) then Exit;
    Result:=WideToStr((Self as TDbRecordset).myAdoRecs.Fields.Item[i].Name);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    if (i<0) or (i>=FieldsCount) then Exit;
    Result:=(Self as TDbRecordset).mySqlDbRecs.FieldsNames[i];
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    if (i<0) or (i>=FieldsCount) then Exit;
    Result:=(Self as TDbRecordset).myZeosRecs.FieldsNames[i];
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'FieldsNames');
 end;
end;

function TDbEntity.FieldsTypes(id:LongString):Integer;
var i:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    if (id='') then Exit;
    Result:=(Self as TDbRecordset).myAdoRecs.Fields.Item[id].Type_;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    if (id='') then Exit;
    i:=(Self as TDbRecordset).mySqlDbRecs.IndexOfField(id);
    if (i<0) then Exit;
    Result:=(Self as TDbRecordset).mySqlDbRecs.FieldsTypes[i];
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    if (id='') then Exit;
    i:=(Self as TDbRecordset).myZeosRecs.IndexOfField(id);
    if (i<0) then Exit;
    Result:=(Self as TDbRecordset).myZeosRecs.FieldsTypes[i];
    {$ENDIF ~USES_ZEOS}
   end;
  end;
  Exit; i:=0; FakeNop(i);
 except
  on E:Exception do BugReport(E,Self,'FieldsTypes');
 end;
end;

function TDbEntity.FieldsAsInt(const id:LongString; op:Char; const arg:Integer):Integer;
var i:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  Result:=arg;
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    if (id='') then Exit;
    case UpCase(op) of
     'R','?','<' : begin
      Result:=(Self as TDbRecordset).myAdoRecs.Fields.Item[id].Value;
     end;
     'W','=','>' : begin
      (Self as TDbRecordset).myAdoRecs.Fields.Item[id].Value:=arg;
     end;
     else raise EDbGetFields.Create('Invalid operation ('+op+') in FieldsAsInt.');
    end;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    if (id='') then Exit;
    i:=(Self as TDbRecordset).mySqlDbRecs.IndexOfField(id);
    if (i<0) then Exit;
    case UpCase(op) of
     'R','?','<' : begin
      Result:=(Self as TDbRecordset).mySqlDbRecs.FieldsAsInteger[i];
     end;
     'W','=','>' : begin
      (Self as TDbRecordset).mySqlDbRecs.FieldsAsInteger[i]:=arg;
     end;
     else raise EDbGetFields.Create('Invalid operation ('+op+') in FieldsAsInt.');
    end;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    if (id='') then Exit;
    i:=(Self as TDbRecordset).myZeosRecs.IndexOfField(id);
    if (i<0) then Exit;
    case UpCase(op) of
     'R','?','<' : begin
      Result:=(Self as TDbRecordset).myZeosRecs.FieldsAsInteger[i];
     end;
     'W','=','>' : begin
      (Self as TDbRecordset).myZeosRecs.FieldsAsInteger[i]:=arg;
     end;
     else raise EDbGetFields.Create('Invalid operation ('+op+') in FieldsAsInt.');
    end;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
  Exit; i:=0; FakeNop(i);
 except
  on E:Exception do BugReport(E,Self,'FieldsAsInt');
 end;
end;

function TDbEntity.FieldsAsFloat(const id:LongString; op:Char; const arg:Double):Double;
var i:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  Result:=arg;
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    if (id='') then Exit;
    case UpCase(op) of
     'R','?','<' : begin
      Result:=(Self as TDbRecordset).myAdoRecs.Fields.Item[id].Value;
     end;
     'W','=','>' : begin
      (Self as TDbRecordset).myAdoRecs.Fields.Item[id].Value:=arg;
     end;
     else raise EDbGetFields.Create('Invalid operation ('+op+') in FieldsAsFloat.');
    end;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    if (id='') then Exit;
    i:=(Self as TDbRecordset).mySqlDbRecs.IndexOfField(id);
    if (i<0) then Exit;
    case UpCase(op) of
     'R','?','<' : begin
      Result:=(Self as TDbRecordset).mySqlDbRecs.FieldsAsFloat[i];
     end;
     'W','=','>' : begin
      (Self as TDbRecordset).mySqlDbRecs.FieldsAsFloat[i]:=arg;
     end;
     else raise EDbGetFields.Create('Invalid operation ('+op+') in FieldsAsFloat.');
    end;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    if (id='') then Exit;
    i:=(Self as TDbRecordset).myZeosRecs.IndexOfField(id);
    if (i<0) then Exit;
    case UpCase(op) of
     'R','?','<' : begin
      Result:=(Self as TDbRecordset).myZeosRecs.FieldsAsFloat[i];
     end;
     'W','=','>' : begin
      (Self as TDbRecordset).myZeosRecs.FieldsAsFloat[i]:=arg;
     end;
     else raise EDbGetFields.Create('Invalid operation ('+op+') in FieldsAsFloat.');
    end;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
  Exit; i:=0; FakeNop(i);
 except
  on E:Exception do BugReport(E,Self,'FieldsAsFloat');
 end;
end;

function TDbEntity.FieldsAsString(const id:LongString; op:Char; const arg:LongString):LongString;
var i:Integer;
begin
 Result:='';
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  Result:=arg;
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    if (id='') then Exit;
    case UpCase(op) of
     'R','?','<' : begin
      Result:=VarToStrDef((Self as TDbRecordset).myAdoRecs.Fields.Item[id].Value,arg);
     end;
     'W','=','>' : begin
      (Self as TDbRecordset).myAdoRecs.Fields.Item[id].Value:=arg;
     end;
     else raise EDbGetFields.Create('Invalid operation ('+op+') in FieldsAsString.');
    end;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    if (id='') then Exit;
    i:=(Self as TDbRecordset).mySqlDbRecs.IndexOfField(id);
    if (i<0) then Exit;
    case UpCase(op) of
     'R','?','<' : begin
      Result:=(Self as TDbRecordset).mySqlDbRecs.FieldsAsString[i];
     end;
     'W','=','>' : begin
      (Self as TDbRecordset).mySqlDbRecs.FieldsAsString[i]:=arg;
     end;
     else raise EDbGetFields.Create('Invalid operation ('+op+') in FieldsAsString.');
    end;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    if (id='') then Exit;
    i:=(Self as TDbRecordset).myZeosRecs.IndexOfField(id);
    if (i<0) then Exit;
    case UpCase(op) of
     'R','?','<' : begin
      Result:=(Self as TDbRecordset).myZeosRecs.FieldsAsString[i];
     end;
     'W','=','>' : begin
      (Self as TDbRecordset).myZeosRecs.FieldsAsString[i]:=arg;
     end;
     else raise EDbGetFields.Create('Invalid operation ('+op+') in FieldsAsString.');
    end;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
  Exit; i:=0; FakeNop(i);
 except
  on E:Exception do BugReport(E,Self,'FieldsAsString');
 end;
end;

function TDbEntity.GetString(n:Integer=1; coldel:LongString=ASCII_HT; rowdel:LongString=EOL; nullexpr:LongString=''):LongString;
begin
 Result:='';
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    if (n>0) and (coldel<>'') and (rowdel<>'') then
    Result:=WideToStr((Self as TDbRecordset).myAdoRecs.GetString(adClipString,n,
                       StrToWide(coldel),StrToWide(rowdel),StrToWide(nullexpr)));
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    if (n>0) and (coldel<>'') and (rowdel<>'') then
    Result:=(Self as TDbRecordset).mySqlDbRecs.GetString(n,coldel,rowdel,nullexpr);
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    if (n>0) and (coldel<>'') and (rowdel<>'') then
    Result:=(Self as TDbRecordset).myZeosRecs.GetString(n,coldel,rowdel,nullexpr);
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetString');
 end;
end;

function TDbEntity.AddNew(arg:LongString=''):Boolean;
var Fields,Values:OleVariant; Lines:TStringList;
 procedure PrepareFieldsValues;
 var i,p:Integer; sn,sv,sl:LongString;
 begin
  if (arg<>'') then begin
   Lines:=TStringList.Create;
   try
    Lines.Text:=AdjustLineBreaks(arg);
    for i:=Lines.Count-1 downto 0 do begin
     sl:=Lines[i]; sn:=sl; sv:=''; p:=Pos('=',sl);
     if (p>0) then begin sn:=Copy(sl,1,p-1); sv:=Copy(sl,p+1,MaxInt); end;
     sn:=SysUtils.Trim(sn); if (sn='') then Lines.Delete(i);
    end;
    if Lines.Count=0 then Exit;
    Fields:=VarArrayCreate([0,Lines.Count-1],varVariant);
    Values:=VarArrayCreate([0,Lines.Count-1],varVariant);
    for i:=0 to Lines.Count-1 do begin
     sl:=Lines[i]; sn:=sl; sv:=''; p:=Pos('=',sl);
     if (p>0) then begin sn:=Copy(sl,1,p-1); sv:=Copy(sl,p+1,MaxInt); end;
     sn:=SysUtils.Trim(sn);
     Fields[i]:=sn;
     Values[i]:=sv;
    end;
   finally
    Kill(Lines);
   end;
  end;
 end;
begin
 Result:=false;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Fields:=EmptyParam;
    Values:=EmptyParam;
    PrepareFieldsValues;
    try
     (Self as TDbRecordset).myAdoRecs.AddNew(Fields,Values);
    finally
     Fields:=Unassigned;
     Values:=Unassigned;
    end;
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.AddNew(Trim(arg));
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.AddNew(Trim(arg));
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'AddNew');
 end;
end;

function TDbEntity.Delete(AffectRecords:Integer):Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 if (Self is TDbRecordset) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbRecordset).myAdoRecs.Delete(AffectRecords);
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbRecordset).mySqlDbRecs.Delete(AffectRecords);
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbRecordset).myZeosRecs.Delete(AffectRecords);
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'Delete');
 end;
end;

function TDbEntity.GetCommandType:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 if (Self is TDbCommand) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=(Self as TDbCommand).myAdoComm.CommandType;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbCommand).mySqlDbComm.CommandType;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbCommand).myZeosComm.CommandType;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetCommandType');
 end;
end;

procedure TDbEntity.SetCommandType(arg:Integer);
begin
 if (Self=nil) then Exit;
 if (Self is TDbCommand) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbCommand).myAdoComm.CommandType:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbCommand).mySqlDbComm.CommandType:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbCommand).myZeosComm.CommandType:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetCommandType');
 end;
end;

function TDbEntity.GetCommandText:LongString;
begin
 Result:='';
 if (Self=nil) then Exit;
 if (Self is TDbCommand) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=WideToStr((Self as TDbCommand).myAdoComm.CommandText);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=(Self as TDbCommand).mySqlDbComm.CommandText;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=(Self as TDbCommand).myZeosComm.CommandText;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetCommandText');
 end;
end;

procedure TDbEntity.SetCommandText(arg:LongString);
begin
 if (Self=nil) then Exit;
 if (Self is TDbCommand) then
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    (Self as TDbCommand).myAdoComm.CommandText:=StrToWide(arg);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    (Self as TDbCommand).mySqlDbComm.CommandText:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    (Self as TDbCommand).myZeosComm.CommandText:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetCommandText');
 end;
end;

function TDbEntity.Control(arg:LongString):LongString;
var i,pe,iv,si:Integer; sn,sv,sc,sr,sz:LongString; rv:Double;
begin
 Result:='';
 if (Self=nil) then Exit;
 if (arg='') then Exit;
 try
  pe:=ExtractNameValuePair(arg,sn,sv,'=',0);
  case Identify(sn) of
   sid_State: begin
    Result:=IntToStr(State);
   end;
   sid_Version: begin
    Result:=Root.Version;
   end;
   sid_EngineId: begin
    Result:=IntToStr(Root.EngineId);
   end;
   sid_EngineName: begin
    Result:=Root.EngineName;
   end;
   sid_Errors: begin
    Result:=Root.Errors;
   end;
   sid_ErrorsCount: begin
    Result:=IntToStr(Root.ErrorsCount);
   end;
   sid_ErrorsClear: begin
    Result:=IntToStr(Root.ErrorsClear);
   end;
   sid_Driver: begin
    Result:=Root.Driver;
   end;
   sid_Provider: begin
    if (pe>0) then Root.Provider:=SysUtils.Trim(sv);
    Result:=Root.Provider;
   end;
   sid_DefaultDatabase: begin
    if (pe>0) then Root.DefaultDatabase:=SysUtils.Trim(sv);
    Result:=Root.DefaultDatabase;
   end;
   sid_Properties: begin
    if (pe>0) then Properties:=SysUtils.Trim(sv);
    Result:=Properties;
   end;
   sid_CursorLocation: begin
    if (pe>0) and Str2Int(sv,iv) then CursorLocation:=iv;
    Result:=IntToStr(CursorLocation);
   end;
   sid_Attributes: begin
    if (pe>0) and Str2Int(sv,iv) then Attributes:=iv;
    Result:=IntToStr(Attributes);
   end;
   sid_AbsolutePage: begin
    if (pe>0) and Str2Int(sv,iv) then AbsolutePage:=iv;
    Result:=IntToStr(AbsolutePage);
   end;
   sid_AbsolutePosition: begin
    if (pe>0) and Str2Int(sv,iv) then AbsolutePosition:=iv;
    Result:=IntToStr(AbsolutePosition);
   end;
   sid_Bookmark: begin
    if (pe>0) and Str2Int(sv,iv) then Bookmark:=iv;
    Result:=IntToStr(Bookmark);
   end;
   sid_CacheSize: begin
    if (pe>0) and Str2Int(sv,iv) then CacheSize:=iv;
    Result:=IntToStr(CacheSize);
   end;
   sid_CursorType: begin
    if (pe>0) and Str2Int(sv,iv) then CursorType:=iv;
    Result:=IntToStr(CursorType);
   end;
   sid_Mode: begin
    if (pe>0) and Str2Int(sv,iv) then Mode:=iv;
    Result:=IntToStr(Mode);
   end;
   sid_EditMode: begin
    Result:=IntToStr(EditMode);
   end;
   sid_Filter: begin
    if (pe>0) then Filter:=SysUtils.Trim(sv);
    Result:=Filter;
   end;
   sid_Index: begin
    if (pe>0) then Index:=SysUtils.Trim(sv);
    Result:=Index;
   end;
   sid_LockType: begin
    if (pe>0) and Str2Int(sv,iv) then LockType:=iv;
    Result:=IntToStr(LockType);
   end;
   sid_MarshalOptions: begin
    if (pe>0) and Str2Int(sv,iv) then MarshalOptions:=iv;
    Result:=IntToStr(MarshalOptions);
   end;
   sid_MaxRecords: begin
    if (pe>0) and Str2Int(sv,iv) then MaxRecords:=iv;
    Result:=IntToStr(MaxRecords);
   end;
   sid_PageCount: begin
    Result:=IntToStr(PageCount);
   end;
   sid_PageSize: begin
    if (pe>0) and Str2Int(sv,iv) then PageSize:=iv;
    Result:=IntToStr(PageSize);
   end;
   sid_RecordCount: begin
    Result:=IntToStr(RecordCount);
   end;
   sid_Sort: begin
    if (pe>0) then Sort:=SysUtils.Trim(sv);
    Result:=Sort;
   end;
   sid_Source: begin
    if (pe>0) then Source:=SysUtils.Trim(sv);
    Result:=Source;
   end;
   sid_StayInSync: begin
    if (pe>0) and Str2Int(sv,iv) then StayInSync:=(iv<>0);
    Result:=IntToStr(Ord(StayInSync));
   end;
   sid_GetString: begin
    if (pe>0) then begin
     if Str2Int(ExtractWord(1,sv,ScanSpaces),iv) then iv:=max(1,iv) else iv:=1;
     sc:=Backslash_Decode(ExtractWord(2,sv,ScanSpaces)); if (sc='') then sc:=ASCII_HT;
     sr:=Backslash_Decode(ExtractWord(3,sv,ScanSpaces)); if (sr='') then sr:=EOL;
     sz:=Backslash_Decode(ExtractWord(4,sv,ScanSpaces));
    end else begin
     iv:=1; sc:=ASCII_HT; sr:=EOL; sz:='';
    end;
    Result:=GetString(iv,sc,sr,sz);
   end;
   sid_CommandType: begin
    if (pe>0) and Str2Int(sv,iv) then CommandType:=iv;
    Result:=IntToStr(CommandType);
   end;
   sid_CommandText: begin
    if (pe>0) then CommandText:=SysUtils.Trim(sv);
    Result:=CommandText;
   end;
   sid_ConnectionStringInit: begin
    Result:=Root.ConnectionStringInit;
    if (Root.PwdCryptMode in pem_Secure)
    then Result:=TDbEngineAssistant.EncryptConnectionString(Result,
                 Root.PwdCryptMode,Root.PwdCryptKey,Root.PwdCryptIv);
   end;
   sid_ConnectionString: begin
    if (pe>0) then Root.ConnectionString:=SysUtils.Trim(sv);
    Result:=Root.ConnectionString;
    if (Root.PwdCryptMode in pem_Secure)
    then Result:=TDbEngineAssistant.EncryptConnectionString(Result,
                 Root.PwdCryptMode,Root.PwdCryptKey,Root.PwdCryptIv);
   end;
   sid_ConnectionTimeout: begin
    if (pe>0) and Str2Int(sv,iv) then Root.ConnectionTimeout:=iv;
    Result:=IntToStr(Root.ConnectionTimeout);
   end;
   sid_CommandTimeout: begin
    if (pe>0) and Str2Int(sv,iv) then Root.CommandTimeout:=iv;
    Result:=IntToStr(Root.CommandTimeout);
   end;
   sid_IsolationLevel: begin
    if (pe>0) and Str2Int(sv,iv) then Root.IsolationLevel:=iv;
    Result:=IntToStr(Root.IsolationLevel);
   end;
   sid_ProviderNames: begin
    Result:=KnownDbApiProviderNames(Root.EngineId);
   end;
   sid_RecordsAffected: begin
    Result:=IntToStr(Root.RecordsAffected);
   end;
   sid_BugsCount: begin
    Result:=IntToStr(Root.BugsCount);
   end;
   sid_BugsClear: begin
    Result:=IntToStr(Root.BugsClear);
   end;
   sid_TotalBugsCount: begin
    Result:=IntToStr(Root.TotalBugsCount);
   end;
   sid_TotalBugsClear: begin
    Result:=IntToStr(Root.TotalBugsClear);
   end;
   sid_BugReportPrefix: begin
    if (pe>0) then myBgPref:=sv;
    Result:=myBgPref;
   end;
   sid_BugReportPrefixAll: begin
    if (pe>0) then db_bugreport_prefix:=sv;
    Result:=db_bugreport_prefix;
   end;
   sid_TimeStampInit: if Root.Ok then begin
    Result:=FloatToStr(Root.TimeStampInit);
   end;
   sid_TimeStampOpen: if Root.Ok then begin
    Result:=FloatToStr(Root.TimeStampOpen);
   end;
   sid_TimeStampUser1: if Root.Ok then begin
    if (pe>0) and Str2Real(sv,rv) then Root.TimeStampUser1:=rv;
    Result:=FloatToStr(Root.TimeStampUser1);
   end;
   sid_TimeStampUser2: if Root.Ok then begin
    if (pe>0) and Str2Real(sv,rv) then Root.TimeStampUser2:=rv;
    Result:=FloatToStr(Root.TimeStampUser2);
   end;
   sid_TimeStampUser3: if Root.Ok then begin
    if (pe>0) and Str2Real(sv,rv) then Root.TimeStampUser3:=rv;
    Result:=FloatToStr(Root.TimeStampUser3);
   end;
   sid_UserState: if Root.Ok then begin
    if (pe>0) and Str2Int(sv,iv) then Root.UserState:=iv;
    Result:=IntToStr(Root.UserState);
   end;
   sid_UserFlags: if Root.Ok then begin
    if (pe>0) and Str2Int(sv,iv) then Root.UserFlags:=iv;
    Result:=IntToStr(Root.UserFlags);
   end;
   sid_UserLink: if Root.Ok then begin
    if (pe>0) and Str2Int(sv,iv) then Root.UserLink:=iv;
    Result:=IntToStr(Root.UserLink);
   end;
   sid_UserData: if Root.Ok then begin
    if (pe>0) then Root.UserData:=sv;
    Result:=Root.UserData;
   end;
   sid_Cookies: if Root.Ok then begin
    if (pe>0) then Root.Cookies:=sv;
    Result:=Root.Cookies;
   end;
   sid_PwdCryptMode: if Root.Ok then begin
    if (pe>0) and Str2Int(sv,iv) then Root.PwdCryptMode:=iv;
    Result:=IntToStr(Root.PwdCryptMode);
   end;
   sid_PwdCryptKey: if Root.Ok then begin
    if (pe>0) then Root.PwdCryptKey:=sv;
    Result:=Root.PwdCryptKey;
   end;
   sid_PwdCryptIv: if Root.Ok then begin
    if (pe>0) then Root.PwdCryptIv:=sv;
    Result:=Root.PwdCryptIv;
   end;
   sid_FieldTypeToString: begin
    if (pe>0) and Str2Int(sv,iv)
    then Result:=DbCon.FieldTypeCodeToString(iv,Root.EngineId)
    else Result:='';
   end;
   sid_Asterisk: begin
    if (Dictionary<>nil) then
    for i:=0 to Dictionary.Count-1 do begin
     si:=Dictionary.Links[i];
     if (si<=Ord(sid_Unknown)) then continue;
     if (si>=Ord(sid_Asterisk)) then continue;
     if not (TStringIdentifier(si) in sid_Control_Readable) then continue;
     if (pe>0) and not (TStringIdentifier(si) in sid_Control_Writable) then continue;
     Result:=Result+Dictionary.Keys[i]+EOL;
    end;
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'Control');
 end;
end;

function TDbEntity.Execute(const arg:LongString; opt:Integer):TDbRecordset;
begin
 if (Self<>nil)
 then Result:=DoExecute(arg,opt)
 else Result:=nil;
end;

function TDbEntity.DoExecute(const arg:LongString; opt:Integer):TDbRecordset;
begin
 Result:=nil;
end;

const
 TheTotalBugsCount : TAtomicCounter = nil;

procedure InitDbCounters;
begin
 LockedInit(TheTotalBugsCount);
end;

procedure FreeDbCounters;
begin
 LockedFree(TheTotalBugsCount);
end;

class function TDbEntity.TotalBugsCount:SizeInt;
begin
 Result:=LockedGet(TheTotalBugsCount);
end;

class function TDbEntity.TotalBugsClear:SizeInt;
begin
 Result:=LockedExchange(TheTotalBugsCount,0);
end;

class function TDbEntity.IncTotalBugsCount:SizeInt;
begin
 Result:=LockedInc(TheTotalBugsCount);
end;

///////////////////////////
// TDbParent implementation
///////////////////////////

constructor TDbParent.Create(aParent:TDbParent);
begin
 inherited Create(aParent);
 myTypeId:=db_type_parent;
 myChilds:=TList.Create;
end;

destructor TDbParent.Destroy;
begin
 Kill(myChilds);
 inherited Destroy;
end;

procedure TDbParent.AppendChild(aChild:TDbEntity);
begin
 if Assigned(Self) then DoAppendChild(aChild);
end;

procedure TDbParent.DoAppendChild(aChild:TDbEntity);
begin
 if (aChild=nil) then Exit;
 SafeListAdd(myChilds,aChild);
end;

procedure TDbParent.RemoveChild(aChild:TDbEntity);
begin
 if Assigned(Self) then DoRemoveChild(aChild);
end;

procedure TDbParent.DoRemoveChild(aChild:TDbEntity);
begin
 if (aChild=nil) then Exit;
 SafeListRemove(myChilds,aChild);
end;

function TDbParent.DoGetChildCount:Integer;
begin
 Result:=SafeListCount(myChilds);
end;

function TDbParent.DoGetChildItems(i:Integer):TDbEntity;
begin
 Result:=SafeListItems(myChilds,i);
end;

///////////////////////////////
// TDbConnection implementation
///////////////////////////////

constructor TDbConnection.Create(eid:Integer=0; const arg:LongString='');
begin
 inherited Create(nil);
 myTypeId:=db_type_connection;
 myConStrng:=DecryptConStrng(arg);
 myEngineId:=0;
 myEngineName:='';
 {$IFDEF USES_ADODB}
 myAdoConn:=nil;
 {$ENDIF ~USES_ADODB}
 {$IFDEF USES_SQLDB}
 mySqlDbConn:=nil;
 {$ENDIF ~USES_SQLDB}
 {$IFDEF USES_ZEOS}
 myZeosConn:=nil;
 {$ENDIF ~USES_ZEOS}
 LockedInit(myBugsCount);
 myRecordsAffected:=0;
 myTimeStampInit:=0;
 myTimeStampOpen:=0;
 myTimeStampUser1:=0;
 myTimeStampUser2:=0;
 myTimeStampUser3:=0;
 myUserState:=0;
 myUserFlags:=0;
 myUserLink:=0;
 myUserData:='';
 myCookies:='';
 SetEngineId(eid);
end;

destructor TDbConnection.Destroy;
begin
 myEngineName:='';
 myConStrng:='';
 {$IFDEF USES_ADODB}
 myAdoConn:=nil;
 {$ENDIF ~USES_ADODB}
 {$IFDEF USES_SQLDB}
 Kill(mySqlDbConn);
 {$ENDIF ~USES_SQLDB}
 {$IFDEF USES_ZEOS}
 Kill(myZeosConn);
 {$ENDIF ~USES_ZEOS}
 myUserData:='';
 myCookies:='';
 myPwdCryptKey:='';
 myPwdCryptIv:='';
 LockedFree(myBugsCount);
 inherited;
end;

procedure TDbConnection.SetEngineId(eid:Integer);
 procedure ExtractParam(sn,id:LongString);
 var sv:LongString;
 begin
  id:=SysUtils.Trim(id);
  sn:=SysUtils.Trim(sn);
  sv:=CookieScan(myConStrng,id,Ord(';'));
  if (id<>'') and (sn<>'') and (sv<>'')
  then Control(sn+'='+sv);
 end;
begin
 if (Self=nil) then Exit;
 if (eid=0) then eid:=db_engine_default;
 try
  case eid of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoConn:=CoConnection.Create;
    if (myConStrng<>'') then begin
     ExtractParam('Provider','Provider');
     myAdoConn.ConnectionString:=StrToWide(myConStrng);
    end;
    myEngineId:=db_engine_ado;
    myEngineName:=db_engine_name_ado;
    myTimeStampInit:=msecnow;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    if (myConStrng<>'') then begin
     mySqlDbConn:=NewSqlDbConnecter(myConStrng);
     if Assigned(mySqlDbConn) then begin
      myEngineId:=db_engine_sqldb;
      myEngineName:=db_engine_name_sqldb;
      myTimeStampInit:=msecnow;
      mySqlDbConn.Parent:=Self;
     end;
    end;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    if (myConStrng<>'') then begin
     myZeosConn:=NewZeosConnecter(myConStrng);
     if Assigned(myZeosConn) then begin
      myEngineId:=db_engine_zeos;
      myEngineName:=db_engine_name_zeos;
      myTimeStampInit:=msecnow;
      myZeosConn.Parent:=Self;
     end;
    end;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetEngineId');
 end;
end;

function TDbConnection.GetEngineId:Integer;
begin
 if (Self=nil)
 then Result:=0
 else Result:=myEngineId;
end;

function TDbConnection.GetEngineName:LongString;
begin
 if (Self=nil)
 then Result:=''
 else Result:=myEngineName;
end;

function TDbConnection.GetConnectionStringInit:LongString;
begin
 if (Self=nil)
 then Result:=''
 else Result:=myConStrng;
end;

function TDbConnection.GetTimeStampInit:Double;
begin
 if (Self=nil)
 then Result:=0
 else Result:=myTimeStampInit;
end;

function TDbConnection.GetTimeStampOpen:Double;
begin
 if (Self=nil)
 then Result:=0
 else Result:=myTimeStampOpen;
end;

function TDbConnection.GetTimeStampUser1:Double;
begin
 if (Self=nil)
 then Result:=0
 else Result:=myTimeStampUser1;
end;

procedure TDbConnection.SetTimeStampUser1(arg:Double);
begin
 if (Self=nil) then Exit;
 myTimeStampUser1:=arg;
end;

function TDbConnection.GetTimeStampUser2:Double;
begin
 if (Self=nil)
 then Result:=0
 else Result:=myTimeStampUser2;
end;

procedure TDbConnection.SetTimeStampUser2(arg:Double);
begin
 if (Self=nil) then Exit;
 myTimeStampUser2:=arg;
end;

function TDbConnection.GetTimeStampUser3:Double;
begin
 if (Self=nil)
 then Result:=0
 else Result:=myTimeStampUser3;
end;

procedure TDbConnection.SetTimeStampUser3(arg:Double);
begin
 if (Self=nil) then Exit;
 myTimeStampUser3:=arg;
end;

function TDbConnection.GetUserState:Integer;
begin
 if (Self=nil)
 then Result:=0
 else Result:=myUserState;
end;

procedure TDbConnection.SetUserState(arg:Integer);
begin
 if (Self=nil) then Exit;
 myUserState:=arg;
end;

function TDbConnection.GetUserFlags:Integer;
begin
 if (Self=nil)
 then Result:=0
 else Result:=myUserFlags;
end;

procedure TDbConnection.SetUserFlags(arg:Integer);
begin
 if (Self=nil) then Exit;
 myUserFlags:=arg;
end;

function TDbConnection.GetUserLink:Integer;
begin
 if (Self=nil)
 then Result:=0
 else Result:=myUserLink;
end;

procedure TDbConnection.SetUserLink(arg:Integer);
begin
 if (Self=nil) then Exit;
 myUserLink:=arg;
end;

function TDbConnection.GetUserData:LongString;
begin
 if (Self=nil)
 then Result:=''
 else Result:=myUserData;
end;

procedure TDbConnection.SetUserData(arg:LongString);
begin
 if (Self=nil) then Exit;
 myUserData:=arg;
end;

function TDbConnection.GetCookies:LongString;
begin
 if (Self=nil)
 then Result:=''
 else Result:=myCookies;
end;

procedure TDbConnection.SetCookies(arg:LongString);
begin
 if (Self=nil) then Exit;
 myCookies:=arg;
end;

function TDbConnection.GetPwdCryptMode:Integer;
begin
 if (Self=nil)
 then Result:=0
 else Result:=myPwdCryptMode;
end;

procedure TDbConnection.SetPwdCryptMode(arg:Integer);
begin
 if (Self=nil) then Exit;
 myPwdCryptMode:=EnsureRange(arg,pem_MIN,pem_MAX);
end;

function TDbConnection.GetPwdCryptKey:LongString;
begin
 if (Self=nil)
 then Result:=''
 else Result:=myPwdCryptKey;
end;

procedure TDbConnection.SetPwdCryptKey(arg:LongString);
begin
 if (Self=nil) then Exit;
 myPwdCryptKey:=Trim(arg);
end;

function TDbConnection.GetPwdCryptIv:LongString;
begin
 if (Self=nil)
 then Result:=''
 else Result:=myPwdCryptIv;
end;

procedure TDbConnection.SetPwdCryptIv(arg:LongString);
begin
 if (Self=nil) then Exit;
 myPwdCryptIv:=Trim(arg);
end;

function TDbConnection.GetDriver:LongString;
begin
 Result:='';
 if (Self=nil) then Exit;
 try
  Result:=SysUtils.Trim(CookieScan(ConnectionStringInit,'Driver',Ord(';')));
 except
  on E:Exception do BugReport(E,Self,'GetDriver');
 end;
end;

function TDbConnection.GetProvider:LongString;
begin
 Result:='';
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=WideToStr(myAdoConn.Provider);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.Provider;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.Provider;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetProvider');
 end;
end;

procedure TDbConnection.SetProvider(const arg:LongString);
begin
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoConn.Provider:=StrToWide(arg);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    mySqlDbConn.Provider:=Trim(arg);
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    myZeosConn.Provider:=Trim(arg);
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetProvider');
 end;
end;

function TDbConnection.GetConnectionString:LongString;
begin
 Result:='';
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=WideToStr(myAdoConn.ConnectionString);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.Api.ConStrng;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.Api.ConStrng;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetConnectionString');
 end;
end;

procedure TDbConnection.SetConnectionString(const arg:LongString);
begin
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoConn.ConnectionString:=StrToWide(DecryptConStrng(arg));
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    mySqlDbConn.Api.ConStrng:=DecryptConStrng(arg);
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    myZeosConn.Api.ConStrng:=DecryptConStrng(arg);
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetConnectionString');
 end;
end;

function TDbConnection.DecryptConStrng(const arg:LongString):LongString;
var pcm:Integer; pck,pci:LongString;
begin
 pcm:=0; pck:=''; pci:=''; // Decrypt ConnectionString if one encrypted
 Result:=TDbEngineAssistant.DecryptConnectionString(Trim(arg),pcm,pck,pci);
 PwdCryptMode:=pcm; PwdCryptKey:=pck; PwdCryptIv:=pci;
end;

function TDbConnection.GetConnectionTimeout:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=myAdoConn.ConnectionTimeout;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.ConnectionTimeout;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.ConnectionTimeout;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetConnectionTimeout');
 end;
end;

procedure TDbConnection.SetConnectionTimeout(arg:Integer);
begin
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoConn.ConnectionTimeout:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    mySqlDbConn.ConnectionTimeout:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    myZeosConn.ConnectionTimeout:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetConnectionTimeout');
 end;
end;

function TDbConnection.GetCommandTimeout:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=myAdoConn.CommandTimeout;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.CommandTimeout;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.CommandTimeout;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetCommandTimeout');
 end;
end;

procedure TDbConnection.SetCommandTimeout(arg:Integer);
begin
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoConn.CommandTimeout:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    mySqlDbConn.CommandTimeout:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    myZeosConn.CommandTimeout:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetCommandTimeout');
 end;
end;

function TDbConnection.GetIsolationLevel:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=myAdoConn.IsolationLevel;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.IsolationLevel;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.IsolationLevel;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetIsolationLevel');
 end;
end;

procedure TDbConnection.SetIsolationLevel(arg:Integer);
begin
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoConn.IsolationLevel:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    mySqlDbConn.IsolationLevel:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    myZeosConn.IsolationLevel:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetIsolationLevel');
 end;
end;

function TDbConnection.GetDefaultDatabase:LongString;
begin
 Result:='';
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=WideToStr(myAdoConn.DefaultDatabase);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.DefaultDatabase;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.DefaultDatabase;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetDefaultDatabase');
 end;
end;

procedure TDbConnection.SetDefaultDatabase(const arg:LongString);
begin
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoConn.DefaultDatabase:=StrToWide(arg);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    mySqlDbConn.DefaultDatabase:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    myZeosConn.DefaultDatabase:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'SetDefaultDatabase');
 end;
end;

function TDbConnection.DoGetState:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=myAdoConn.State;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.State;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.State;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoGetState');
 end;
end;

function TDbConnection.DoGetActive:Boolean;
begin
 Result:=False;
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=myAdoConn.State in [adStateOpen];
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.Active;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.Active;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoGetActive');
 end;
end;

function TDbConnection.DoClose:Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoConn.Close;
    myTimeStampOpen:=0;
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.Close;
    myTimeStampOpen:=0;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.Close;
    myTimeStampOpen:=0;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoGetClose');
 end;
end;

function TDbConnection.DoCancel:Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoConn.Cancel;
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.Cancel;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.Cancel;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoGetCancel');
 end;
end;

function TDbConnection.GetVersion:LongString;
begin
 Result:='';
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=WideToStr(myAdoConn.Version);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.Version;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.Version;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoGetVersion');
 end;
end;

function TDbConnection.GetErrors:LongString;
begin
 Result:='';
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=FormatAdoErrors(myAdoConn.Errors);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.GetErrors;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.GetErrors;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetErrors');
 end;
end;

function TDbConnection.GetErrorsCount:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=myAdoConn.Errors.Count;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.GetErrorsCount(False);
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.GetErrorsCount(False);
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetErrorsCount');
 end;
end;

function TDbConnection.GetErrorsClear:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=myAdoConn.Errors.Count;
    myAdoConn.Errors.Clear;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.GetErrorsCount(True);
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.GetErrorsCount(True);
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'GetErrorsClear');
 end;
end;

function TDbConnection.DoGetProperties:LongString;
begin
 Result:='';
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=FormatAdoProperties(myAdoConn.Properties);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.Properties;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.Properties;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoGetProperties');
 end;
end;

procedure TDbConnection.DoSetProperties(const arg:LongString);
begin
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    AssignAdoProperties(myAdoConn.Properties,arg);
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    mySqlDbConn.Properties:=Trim(arg);
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    myZeosConn.Properties:=Trim(arg);
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoSetProperties');
 end;
end;

function TDbConnection.DoGetMode:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=myAdoConn.Mode;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.Mode;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.Mode;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoGetMode');
 end;
end;

procedure TDbConnection.DoSetMode(arg:Integer);
begin
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoConn.Mode:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    mySqlDbConn.Mode:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    myZeosConn.Mode:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoSetMode');
 end;
end;

function TDbConnection.DoGetCursorLocation:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=myAdoConn.CursorLocation;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.CursorLocation;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.CursorLocation;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoGetCursorLocation');
 end;
end;

procedure TDbConnection.DoSetCursorLocation(arg:Integer);
begin
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoConn.CursorLocation:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    mySqlDbConn.CursorLocation:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    myZeosConn.CursorLocation:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoSetCursorLocation');
 end;
end;

function TDbConnection.DoGetAttributes:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=myAdoConn.Attributes;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.Attributes;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.Attributes;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoGetAttributes');
 end;
end;

procedure TDbConnection.DoSetAttributes(arg:Integer);
begin
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoConn.Attributes:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    mySqlDbConn.Attributes:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    myZeosConn.Attributes:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoSetAttributes');
 end;
end;

function TDbConnection.DoOpen(opt:Integer):Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoConn.Open('','','',opt);
    myTimeStampOpen:=msecnow;
    myRecordsAffected:=0;
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.Open(opt);
    myTimeStampOpen:=IfThen(Result,msecnow,0);
    myRecordsAffected:=0;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.Open(opt);
    myTimeStampOpen:=IfThen(Result,msecnow,0);
    myRecordsAffected:=0;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoOpen');
 end;
end;

function TDbConnection.DoExecute(const arg:LongString; opt:Integer):TDbRecordset;
var ra:OleVariant;
begin
 Result:=nil;
 if (Self=nil) then Exit;
 try
  case EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    ra:=Unassigned;
    Result:=TDbRecordset.Create(Self,myAdoConn.Execute(StrToWide(arg),ra,opt),nil);
    myRecordsAffected:=ra;
    {$ENDIF ~USES_ADODB}
    ra:=Unassigned;
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=TDbRecordset.Create(Self,nil,mySqlDbConn.Execute(arg,opt));
    myRecordsAffected:=0;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=TDbRecordset.Create(Self,nil,myZeosConn.Execute(arg,opt));
    myRecordsAffected:=0;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoExecute');
 end;
end;

function TDbConnection.BeginTrans:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=myAdoConn.BeginTrans;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.BeginTrans;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.BeginTrans;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'BeginTrans');
 end;
end;

function TDbConnection.CommitTrans:Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoConn.CommitTrans;
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.CommitTrans;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.CommitTrans;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'CommitTrans');
 end;
end;

function TDbConnection.RollbackTrans:Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoConn.RollbackTrans;
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbConn.RollbackTrans;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosConn.RollbackTrans;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'RollbackTrans');
 end;
end;

function TDbConnection.GetRecordsAffected:Integer;
begin
 if (Self<>nil)
 then Result:=myRecordsAffected
 else Result:=0;
end;

function TDbConnection.CreateRecordset(arg:LongString):TDbRecordset;
begin
 Result:=nil;
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    arg:=SysUtils.Trim(arg);
    Result:=TDbRecordset.Create(Self,CoRecordset.Create,nil);
    if (arg<>'') then Result.Source:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    arg:=SysUtils.Trim(arg);
    Result:=TDbRecordset.Create(Self,nil,mySqlDbConn.Recorder);
    if (arg<>'') then Result.Source:=arg;
    if Result.mySqlDbRecs.Connecter.Api.IsLinked
    then Result.mySqlDbRecs.Connecter.Api.Query.ReadOnly:=False;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    arg:=SysUtils.Trim(arg);
    Result:=TDbRecordset.Create(Self,nil,myZeosConn.Recorder);
    if (arg<>'') then Result.Source:=arg;
    if Result.myZeosRecs.Connecter.Api.IsLinked
    then Result.myZeosRecs.Connecter.Api.Query.ReadOnly:=False;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'CreateRecordset');
 end;
end;

function TDbConnection.CreateCommand(arg:LongString):TDbCommand;
begin
 Result:=nil;
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    arg:=SysUtils.Trim(arg);
    Result:=TDbCommand.Create(Self,CoCommand.Create,nil);
    if (arg<>'') then Result.CommandText:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    arg:=SysUtils.Trim(arg);
    Result:=TDbCommand.Create(Self,nil,mySqlDbConn.Commander);
    if (arg<>'') then Result.CommandText:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    arg:=SysUtils.Trim(arg);
    Result:=TDbCommand.Create(Self,nil,myZeosConn.Commander);
    if (arg<>'') then Result.CommandText:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'CreateCommand');
 end;
end;
 
function TDbConnection.GetBugsCount:SizeInt;
begin
 if (Self<>nil)
 then Result:=LockedGet(myBugsCount)
 else Result:=0;
end;

function TDbConnection.GetBugsClear:SizeInt;
begin
 if (Self<>nil)
 then Result:=LockedExchange(myBugsCount,0)
 else Result:=0;
end;

function TDbConnection.IncBugsCount:SizeInt;
begin
 if (Self<>nil)
 then Result:=LockedInc(myBugsCount)
 else Result:=0;
end;

////////////////////////////////
// TDbConnection helper routines
////////////////////////////////

function NewDbConnection(eid:Integer=0; const arg:LongString=''):TDbConnection;
begin
 Result:=nil;
 try
  Result:=TDbConnection.Create(eid,arg);
  if (Result.EngineId=0) then Kill(Result);
 except
  on E:Exception do BugReport(E,nil,'NewDbConnection');
 end;
end;

procedure Kill(var TheObject:TDbConnection); overload;
begin
 try
  FreeAndNil(TheObject);
 except
  on E:Exception do BugReport(E,nil,'Kill');
 end;
end;

//////////////////////////////
// TDbRecordset implementation
//////////////////////////////

constructor TDbRecordset.Create(aParent:TDbParent; const aRecordset:Recordset; oRecordset:TObject);
begin
 inherited Create(aParent);
 myTypeId:=db_type_recordset;
 {$IFDEF USES_ADODB}
 myAdoRecs:=aRecordset;
 {$ENDIF ~USES_ADODB}
 {$IFDEF USES_SQLDB}
 if (oRecordset is TSqlDbRecorder) then begin
  mySqlDbRecs:=(oRecordset as TSqlDbRecorder);
  mySqlDbRecs.Parent:=aParent;
 end;
 {$ENDIF ~USES_SQLDB}
 {$IFDEF USES_ZEOS}
 if (oRecordset is TZeosRecorder) then begin
  myZeosRecs:=(oRecordset as TZeosRecorder);
  myZeosRecs.Parent:=aParent;
 end;
 {$ENDIF ~USES_ZEOS}
end;

destructor TDbRecordset.Destroy;
begin
 {$IFDEF USES_ADODB}
 myAdoRecs:=nil;
 {$ENDIF ~USES_ADODB}
 {$IFDEF USES_SQLDB}
 // No need to kill: mySqlDbRecs belongs to Connector
 // Kill(mySqlDbRecs);
 {$ENDIF ~USES_SQLDB}
 {$IFDEF USES_ZEOS}
 // No need to kill: myZeosRecs belongs to Connector
 // Kill(myZeosRecs);
 {$ENDIF ~USES_ZEOS}
 inherited Destroy;
end;

function TDbRecordset.DoBindRoot:Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    if VarIsEmpty(myAdoRecs.Get_ActiveConnection) then begin
     if (Root.State in [adStateOpen]) then begin
      myAdoRecs.Set_ActiveConnection(Root.myAdoConn);
      Result:=true;
     end else
     if (Root.State in [adStateClosed]) then begin
      myAdoRecs._Set_ActiveConnection(Root.ConnectionStringInit);
      Result:=true;
     end;
    end;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=Root.mySqlDbConn.Api.IsLinked;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=Root.myZeosConn.Api.IsLinked;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoBindRoot');
 end;
end;

function TDbRecordSet.DoGetState:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=myAdoRecs.State;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbRecs.State;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosRecs.State;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoGetState');
 end;
end;

function TDbRecordSet.DoGetActive:Boolean;
begin
 Result:=False;
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=myAdoRecs.State in [adStateOpen];
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbRecs.Active;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosRecs.Active;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoGetActive');
 end;
end;

function TDbRecordset.DoClose:Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoRecs.Close;
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbRecs.Close;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosRecs.Close;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoClose');
 end;
end;

function TDbRecordset.DoCancel:Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoRecs.Cancel;
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbRecs.Cancel;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosRecs.Cancel;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoCancel');
 end;
end;

function TDbRecordset.DoGetCursorLocation:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=myAdoRecs.CursorLocation;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbRecs.CursorLocation;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosRecs.CursorLocation;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoGetCursorLocation');
 end;
end;

procedure TDbRecordset.DoSetCursorLocation(arg:Integer);
begin
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoRecs.CursorLocation:=arg;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    mySqlDbRecs.CursorLocation:=arg;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    myZeosRecs.CursorLocation:=arg;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoSetCursorLocation');
 end;
end;

function TDbRecordset.DoOpen(opt:Integer):Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    if (Root.State in [adStateClosed])
    then myAdoRecs.Open(Source,Root.ConnectionStringInit,CursorType,LockType,opt)
    else myAdoRecs.Open(Source,Root.myAdoConn,CursorType,LockType,opt);
    Root.myRecordsAffected:=0;
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbRecs.Open(opt);
    Root.myRecordsAffected:=0;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosRecs.Open(opt);
    Root.myRecordsAffected:=0;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoOpen');
 end;
end;

///////////////////////////////
// TDbRecordset helper routines
///////////////////////////////

procedure Kill(var TheObject:TDbRecordset); overload;
begin
 try
  FreeAndNil(TheObject);
 except
  on E:Exception do BugReport(E,nil,'Kill');
 end;
end;

////////////////////////////
// TDbCommand implementation
////////////////////////////

constructor TDbCommand.Create(aParent:TDbParent; const aCommand:Command; oCommand:TObject);
begin
 inherited Create(aParent);
 myTypeId:=db_type_command;
 myParams:=EmptyParam;
 {$IFDEF USES_ADODB}
 myAdoComm:=aCommand;
 {$ENDIF ~USES_ADODB}
 {$IFDEF USES_SQLDB}
 if (oCommand is TSqlDbCommander) then begin
  mySqlDbComm:=(oCommand as TSqlDbCommander);
  mySqlDbComm.Parent:=aParent;
 end;
 {$ENDIF ~USES_SQLDB}
 {$IFDEF USES_ZEOS}
 if (oCommand is TZeosCommander) then begin
  myZeosComm:=(oCommand as TZeosCommander);
  myZeosComm.Parent:=aParent;
 end;
 {$ENDIF ~USES_ZEOS}
end;

destructor TDbCommand.Destroy;
begin
 {$IFDEF USES_ADODB}
 myAdoComm:=nil;
 {$ENDIF ~USES_ADODB}
 {$IFDEF USES_SQLDB}
 // No need to kill: mySqlDbComm belongs to Connector
 // Kill(mySqlDbComm);
 {$ENDIF ~USES_SQLDB}
 {$IFDEF USES_ZEOS}
 // No need to kill: myZeosComm belongs to Connector
 // Kill(myZeosComm);
 {$ENDIF ~USES_ZEOS}
 myParams:=Unassigned;
 inherited Destroy;
end;

function TDbCommand.DoBindRoot:Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    if VarIsEmpty(myAdoComm.Get_ActiveConnection) then begin
     if (Root.State in [adStateOpen]) then begin
      myAdoComm.Set_ActiveConnection(Root.myAdoConn);
      Result:=true;
     end else
     if (Root.State in [adStateClosed]) then begin
      myAdoComm._Set_ActiveConnection(Root.ConnectionStringInit);
      Result:=true;
     end;
    end;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=Root.mySqlDbConn.Api.IsLinked;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=Root.myZeosConn.Api.IsLinked;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoBindRoot');
 end;
end;

function TDbCommand.DoGetState:Integer;
begin
 Result:=0;
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=myAdoComm.State;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbComm.State;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosComm.State;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoGetState');
 end;
end;

function TDbCommand.DoGetActive:Boolean;
begin
 Result:=False;
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    Result:=myAdoComm.State in [adStateOpen];
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbComm.Active;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosComm.Active;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoGetState');
 end;
end;

function TDbCommand.DoCancel:Boolean;
begin
 Result:=false;
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    myAdoComm.Cancel;
    Result:=true;
    {$ENDIF ~USES_ADODB}
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    Result:=mySqlDbComm.Cancel;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    Result:=myZeosComm.Cancel;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoCancel');
 end;
end;

function TDbCommand.DoExecute(const arg:LongString; opt:Integer):TDbRecordset;
var ra:OleVariant; cmd:LongString;
begin
 Result:=nil;
 if (Self=nil) then Exit;
 try
  case Root.EngineId of
   db_engine_ado: begin
    {$IFDEF USES_ADODB}
    ra:=Unassigned;
    if db_bindroot_onexecute then BindRoot;
    if (arg<>'') then myAdoComm.CommandText:=StrToWide(arg);
    Result:=TDbRecordset.Create(Root,myAdoComm.Execute(ra,myParams,opt),nil);
    Root.myRecordsAffected:=ra;
    {$ENDIF ~USES_ADODB}
    ra:=Unassigned;
   end;
   db_engine_sqldb: begin
    {$IFDEF USES_SQLDB}
    if db_bindroot_onexecute then BindRoot;
    if (arg<>'') then cmd:=arg else cmd:=mySqlDbComm.CommandText;
    Result:=TDbRecordset.Create(Root,nil,Root.mySqlDbConn.Execute(cmd,opt));
    Root.myRecordsAffected:=0;
    {$ENDIF ~USES_SQLDB}
   end;
   db_engine_zeos: begin
    {$IFDEF USES_ZEOS}
    if db_bindroot_onexecute then BindRoot;
    if (arg<>'') then cmd:=arg else cmd:=myZeosComm.CommandText;
    Result:=TDbRecordset.Create(Root,nil,Root.myZeosConn.Execute(cmd,opt));
    Root.myRecordsAffected:=0;
    {$ENDIF ~USES_ZEOS}
   end;
  end;
 except
  on E:Exception do BugReport(E,Self,'DoExecute');
 end;
end;

/////////////////////////////
// TDbCommand helper routines
/////////////////////////////

procedure Kill(var TheObject:TDbCommand); overload;
begin
 try
  FreeAndNil(TheObject);
 except
  on E:Exception do BugReport(E,nil,'Kill');
 end;
end;

////////////////////////////////////////
// DbApi easy DB interface for DaqPascal
////////////////////////////////////////

function db_create(arg:LongString):Boolean;
begin
 Result:=CreateDatabase(arg);
end;

function db_connection(eid:Integer; arg:LongString):Integer;
begin
 Result:=0;
 try
  Result:=NewDbConnection(eid,arg).Ref;
 except
  on E:Exception do BugReport(E,nil,'db_connection');
 end;
end;

function db_recordset(dbo:Integer; arg:LongString):Integer;
var con:TDbConnection;
begin
 Result:=0;
 try
  con:=db_ref(dbo).Root;
  if (con=nil) then Exit;
  Result:=con.CreateRecordset(arg).Ref;
 except
  on E:Exception do BugReport(E,nil,'db_recordset');
 end;
end;

function db_command(dbo:Integer; arg:LongString):Integer;
var con:TDbConnection;
begin
 Result:=0;
 try
  con:=db_ref(dbo).Root;
  if (con=nil) then Exit;
  Result:=con.CreateCommand(arg).Ref;
 except
  on E:Exception do BugReport(E,nil,'db_command');
 end;
end;

function db_free(dbo:Integer):Boolean;
var obj:TObject;
begin
 Result:=false;
 if (dbo<>0) then
 try
  obj:=db_ref(dbo);
  if (obj is TDbEntity) then begin
   FreeAndNil(obj);
   Result:=true;
  end;
 except
  on E:Exception do BugReport(E,nil,'db_free');
 end;
end;

function db_ref(dbo:Integer):TDbEntity;
var obj:TObject;
begin
 Result:=nil;
 if (dbo=0) then Exit;
 obj:=ObjectRegistry[dbo];
 if (obj is TDbEntity) then Result:=TDbEntity(obj);
end;

function db_root(dbo:Integer):TDbConnection;
begin
 Result:=db_ref(dbo).Root;
end;

function db_type(dbo:Integer):Integer;
begin
 Result:=db_ref(dbo).TypeId;
end;

function db_parent(dbo:Integer):Integer;
begin
 Result:=db_ref(dbo).Parent.Ref;
end;

function db_engineid(dbo:Integer):Integer;
begin
 Result:=db_ref(dbo).Root.EngineId;
end;

function db_state(dbo:Integer):Integer;
begin
 Result:=db_ref(dbo).State;
end;

function db_active(dbo:Integer):Boolean;
begin
 Result:=db_ref(dbo).Active;
end;

function db_close(dbo:Integer):Boolean;
begin
 Result:=db_ref(dbo).Close;
end;

function db_open(dbo:Integer; opt:Integer):Boolean;
begin
 Result:=db_ref(dbo).Open(opt);
end;

function db_ctrl(dbo:Integer; arg:LongString):LongString;
begin
 Result:=db_ref(dbo).Control(arg);
end;

function db_bugscount(dbo:Integer):Integer;
begin
 Result:=db_ref(dbo).Root.BugsCount;
end;

function db_bugsclear(dbo:Integer):Integer;
begin
 Result:=db_ref(dbo).Root.BugsClear;
end;

function db_errors(dbo:Integer):LongString;
begin
 Result:=db_ref(dbo).Root.Errors;
end;

function db_errorscount(dbo:Integer):Integer;
begin
 Result:=db_ref(dbo).Root.ErrorsCount;
end;

function db_errorsclear(dbo:Integer):Integer;
begin
 Result:=db_ref(dbo).Root.ErrorsClear;
end;

function db_execute(dbo:Integer; arg:LongString; opt:Integer):TDbRecordset;
begin
 Result:=db_ref(dbo).Execute(arg,opt);
end;

function db_cancel(dbo:Integer):Boolean;
begin
 Result:=db_ref(dbo).Cancel;
end;

function db_update(dbr:Integer):Boolean;
begin
 Result:=db_ref(dbr).Update;
end;

function db_cancelupdate(dbr:Integer):Boolean;
begin
 Result:=db_ref(dbr).CancelUpdate;
end;

function db_begintrans(dbc:Integer):Integer;
begin
 Result:=db_ref(dbc).Root.BeginTrans;
end;

function db_committrans(dbc:Integer):Boolean;
begin
 Result:=db_ref(dbc).Root.CommitTrans;
end;

function db_rollbacktrans(dbc:Integer):Boolean;
begin
 Result:=db_ref(dbc).Root.RollbackTrans;
end;

function db_bof(dbr:Integer):Boolean;
begin
 Result:=db_ref(dbr).Bof;
end;

function db_eof(dbr:Integer):Boolean;
begin
 Result:=db_ref(dbr).Eof;
end;

function db_movefirst(dbr:Integer):Boolean;
begin
 Result:=db_ref(dbr).MoveFirst;
end;

function db_movelast(dbr:Integer):Boolean;
begin
 Result:=db_ref(dbr).MoveLast;
end;

function db_movenext(dbr:Integer):Boolean;
begin
 Result:=db_ref(dbr).MoveNext;
end;

function db_moveprevious(dbr:Integer):Boolean;
begin
 Result:=db_ref(dbr).MovePrevious;
end;

function db_fieldscount(dbr:Integer):Integer;
begin
 Result:=db_ref(dbr).FieldsCount;
end;

function db_fieldsnames(dbr:Integer; i:Integer):LongString;
begin
 Result:=db_ref(dbr).FieldsNames(i);
end;

function db_fieldstypes(dbr:Integer; id:LongString):Integer;
begin
 Result:=db_ref(dbr).FieldsTypes(id);
end;

function db_fieldsasint(dbr:Integer; id:LongString; op:Char; arg:Integer):Integer;
begin
 Result:=db_ref(dbr).FieldsAsInt(id,op,arg);
end;

function db_fieldsasfloat(dbr:Integer; id:LongString; op:Char; arg:Double):Double;
begin
 Result:=db_ref(dbr).FieldsAsFloat(id,op,arg);
end;

function db_fieldsasstring(dbr:Integer; id:LongString; op:Char; arg:LongString):LongString;
begin
 Result:=db_ref(dbr).FieldsAsString(id,op,arg);
end;

function db_addnew(dbr:Integer; arg:LongString):Boolean;
begin
 Result:=db_ref(dbr).AddNew(arg);
end;

function db_delete(dbr:Integer; aff:Integer):Boolean;
begin
 Result:=db_ref(dbr).Delete(aff);
end;

function db_requery(dbr:Integer; opt:Integer):Boolean;
begin
 Result:=db_ref(dbr).Requery(opt);
end;

function db_resync(dbr:Integer; aff,res:Integer):Boolean;
begin
 Result:=db_ref(dbr).Resync(aff,res);
end;

function db_supports(dbr:Integer; opt:Integer):Boolean;
begin
 Result:=db_ref(dbr).Supports(opt);
end;

function db_save(dbr:Integer; dst:LongString; fmt:Integer):Boolean;
begin
 Result:=db_ref(dbr).Save(dst,fmt);
end;

///////////////////////////////////////
// Unit initialization and finalization
///////////////////////////////////////

procedure Init_crw_dbapi;
begin
 InitDbCounters;
 InitDictionary;
end;

procedure Free_crw_dbapi;
begin
 FreeDictionary;
 Kill(TheOleDbProviderNames);
 Kill(TheOdbcDriverNames);
 FreeDbCounters;
end;

initialization

 Init_crw_dbapi;

finalization

 Free_crw_dbapi;

end.

//////////////
// END OF FILE
//////////////

