Browse Source

+ Merged from the fixbranch

michael 25 years ago
parent
commit
1089401672

+ 36 - 14
fcl/db/interbase/Makefile

@@ -62,12 +62,6 @@ else
 BASEDIR=.
 endif
 
-#####################################################################
-# Default target
-#####################################################################
-
-override CPU_TARGET:=i386
-
 #####################################################################
 # FPC version/target Detection
 #####################################################################
@@ -129,7 +123,7 @@ endif
 
 # Default FPCDIR
 ifeq ($(FPCDIR),wrong)
-override FPCDIR=../..
+override FPCDIR=../../..
 ifeq ($(wildcard $(FPCDIR)/rtl),)
 ifeq ($(wildcard $(FPCDIR)/units),)
 override FPCDIR=wrong
@@ -183,13 +177,14 @@ endif
 # Targets
 
 override UNITOBJECTS+=interbase
-override EXEOBJECTS+=testib
+override EXAMPLEOBJECTS+=testib
 
 # Clean
 
 
 # Install
 
+PACKAGENAME=interbase
 ZIPTARGET=install
 
 # Defaults
@@ -198,9 +193,8 @@ override NEEDOPT=-S2
 
 # Directories
 
-override NEEDUNITDIR=..
 ifndef TARGETDIR
-TARGETDIR=.
+TARGETDIR=../../$(OS_TARGET)
 endif
 
 # Packages
@@ -209,7 +203,6 @@ override PACKAGES+=rtl fcl ibase
 
 # Libraries
 
-override NEEDGCCLIB=1
 
 # Info
 
@@ -826,12 +819,16 @@ ifdef UNITSDIR
 override FPCOPT+=-Fu$(UNITSDIR)
 endif
 
+<<<<<<< Makefile
 # Add GCC lib path if asked
 ifdef GCCLIBDIR
 override FPCOPT+=-Fl$(GCCLIBDIR)
 endif
 
 # Target dirs and the prefix to use for clean/install
+=======
+# Target dirs and the prefix to use for clean/install
+>>>>>>> 1.1.2.8
 ifdef TARGETDIR
 override FPCOPT+=-FE$(TARGETDIR)
 ifeq ($(TARGETDIR),.)
@@ -902,6 +899,10 @@ all: fpc_all
 
 debug: fpc_debug
 
+examples: fpc_examples
+
+test: fpc_test
+
 smart: fpc_smart
 
 shared: fpc_shared
@@ -928,7 +929,7 @@ cleanall: fpc_cleanall
 
 info: fpc_info
 
-.PHONY:  all debug smart shared showinstall install sourceinstall exampleinstall zipinstall zipsourceinstall zipexampleinstall clean distclean cleanall info
+.PHONY:  all debug examples test smart shared showinstall install sourceinstall exampleinstall zipinstall zipsourceinstall zipexampleinstall clean distclean cleanall info
 
 #####################################################################
 # Units
@@ -945,11 +946,12 @@ override CLEANPPUFILES+=$(UNITPPUFILES)
 fpc_units: $(UNITPPUFILES)
 
 #####################################################################
-# Exes
+# Examples
 #####################################################################
 
-.PHONY: fpc_exes
+.PHONY: fpc_examples fpc_test
 
+<<<<<<< Makefile
 ifdef EXEOBJECTS
 override EXEFILES=$(addsuffix $(EXEEXT),$(EXEOBJECTS))
 override EXEOFILES:=$(addsuffix $(OEXT),$(EXEOBJECTS)) $(addprefix $(LIBPREFIX),$(addsuffix $(STATICLIBEXT),$(EXEOBJECTS)))
@@ -957,11 +959,31 @@ override EXEOFILES:=$(addsuffix $(OEXT),$(EXEOBJECTS)) $(addprefix $(LIBPREFIX),
 override ALLTARGET+=fpc_exes
 override INSTALLEXEFILES+=$(EXEFILES)
 override CLEANEXEFILES+=$(EXEFILES) $(EXEOFILES)
+=======
+ifdef EXAMPLEOBJECTS
+override EXAMPLESOURCEFILES:=$(addsuffix $(PASEXT),$(EXAMPLEOBJECTS))
+override EXAMPLEFILES:=$(addsuffix $(EXEEXT),$(EXAMPLEOBJECTS))
+override EXAMPLEOFILES:=$(addsuffix $(OEXT),$(EXAMPLEOBJECTS)) $(addprefix $(LIBPREFIX),$(addsuffix $(STATICLIBEXT),$(EXAMPLEOBJECTS)))
+>>>>>>> 1.1.2.8
 
+<<<<<<< Makefile
+=======
+override CLEANEXEFILES+=$(EXAMPLEFILES) $(EXAMPLEOFILES)
+>>>>>>> 1.1.2.8
 endif
+<<<<<<< Makefile
+=======
 
+fpc_examples: all $(EXAMPLEFILES) $(addsuffix _all,$(EXAMPLEDIROBJECTS))
+>>>>>>> 1.1.2.8
+
+<<<<<<< Makefile
 fpc_exes: $(EXEFILES)
 
+=======
+fpc_test: examples
+
+>>>>>>> 1.1.2.8
 #####################################################################
 # General compile rules
 #####################################################################

+ 9 - 13
fcl/db/interbase/Makefile.fpc

@@ -1,22 +1,18 @@
 #
-#   Makefile.fpc for interbase dataset
-#
+# Makefile.fpc for interbase.pp units
 
 [targets]
 units=interbase
-programs=testib
-
-[defaults]
-defaultcpu=i386
+examples=testib
 
 [require]
-options=-S2
+options=-S2 
 packages=fcl ibase
 
-[dirs]
-fpcdir=../..
-targetdir=.
-unitdir=..
+[install]
+unitssubdir=fcl
+packagename=interbase
 
-[libs]
-libgcc=1
+[dirs]
+fpcdir=../../..
+targetdir=../../$(OS_TARGET)

+ 58 - 16
fcl/db/interbase/README

@@ -1,23 +1,65 @@
-This is first working release of TDatabase and TDataset 
-implementation for Interbase SQL server.
+  Hello again
 
-Compiling the units:
+with new version of Interbase objects suite
+slightly changes the work with it. Main change
+is TIBTransaction object, which overtake transaction
+handling from TIBDatabase. TIBDataset no longer exists,
+instead of it is TIBQuery now. Work with it is (I think)
+shown in testib.pp program.
 
-Run 'make; make examples' command, if something goes wrong, look if you have
+TIBTransaction has several methods for committing and
+rollback of changes made to database.
 
-  1) unit ibase60 in compiler path
-  2) gds.so.0 library in /usr/lib
-  
-Command 'sh mkdb' creates testing database in current directory.
+  Commit, Rollback : classic action taken, both methods
+    ENDS transaction.
+  CommitRetaining, RollbackRetaining : these methods
+    both do as same as Commit or Rollback, but environment
+    of transaction remains, so you don't need start new
+    transaction. This can be of use for frequent changes 
+    to database, because it's faster than classic 
+    Commit|Rollback & StartTransaction.
+
+In short:
+
+  * Create TIBDatabase
+  * Create TIBTransaction
+  * Assign transaction to database
+  * Create TIBQuery
+  * Execute query
+  * Commit or rollback transaction, in short, end transaction
+  * Close TIBDatabase 
   
-Unit interbase.pp was made and tested on Linux, on ib60 server,
-I don't know if it's working on other OS platforms or other
-versions of IB server.
+Compiling:
+
+  Simply type 'make' for building interbase unit, if you wanna
+test program type 'make examples'. For successfull compiling 
+you must have Interbase server installed, or you must have 
+libgds.so.* in ldpath. If linker shows errors like:
+
+/usr/lib/libgds.so: undefined reference to `dlclose'
+/usr/lib/libgds.so: undefined reference to `dlopen'
+/usr/lib/libgds.so: undefined reference to `crypt'
+/usr/lib/libgds.so: undefined reference to `dlsym'
+testib.pp(92,1) Warning: Error while linking
+
+you must to program source add compiler directives
+
+{$linklib dl}
+{$linklib crypt}
+
+and all should be OK. For running testib you must have
+testing database created. To create it, edit mkdb script,
+set variable ISQL to full path to isql program (it is
+set to /opt/interbase/bin/isql by default, which will work
+on most systems) and run by typing 'sh mkdb'.
+
+This units was built and tested on Linux, and I don't
+know, if you can build it on Win32 or Dos platform.
+Anyway, if you want to use it under windoze, you can
+port it ;)
 
-Unit in these days provides objective connectivity to IB server, 
-basic SQL statement support. It's still buggy, so volunteers
-and contributors are welcome. It supports SQL dialect 1 only 
-(You cannot use date & time datatypes in tables).
+Volunteers, testers, suggestions etc. are always welcome,
+mailto address below
 
 Pavel Stingl
[email protected]
[email protected] 

+ 782 - 433
fcl/db/interbase/interbase.pp

@@ -1,13 +1,10 @@
-{
-    $Id$
+{   $Id$     
+    
     Copyright (c) 2000 by Pavel Stingl
 
 
     Interbase database & dataset
     
-    Roughly based on work of FPC development team,
-    especially Michael Van Canneyt 
-
     See the file COPYING.FPC, included in this distribution,
     for details about the copyright.
 
@@ -17,45 +14,81 @@
 
  **********************************************************************}
 
-unit interbase;
+unit Interbase;
 
 {$H+}
 
-interface 
+interface
 
-uses SysUtils, Classes, ibase60, Db;
+uses SysUtils, Classes, IBase60, DB;
 
 type
 
   PInteger = ^integer;
-
+  PSmallInt= ^smallint;
+  
+  TIBDatabase = class;
+  TIBTransaction = class;
+  TIBQuery = class;
+  TIBStoredProc = class;
+    
+{ TIBDatabase }
+  
   TIBDatabase = class (TDatabase)
   private
     FIBDatabaseHandle    : pointer;
-    FIBTransactionHandle : pointer;
     FPassword            : string;
     FStatus              : array [0..19] of ISC_STATUS;
+    FTransaction         : TIBTransaction;
     FUserName            : string;
+    FDialect             : integer;
     
-    procedure CheckError(ProcName : string);
+    procedure SetDBDialect;
+    procedure SetTransaction(Value : TIBTransaction);
   protected
+    function GetHandle : pointer; virtual;
+      { This procedure makes connection to Interbase server internally.
+        Is visible only by descendants, in application programming
+        will be invisible. Connection you must establish by setting 
+        @link(Connected) property to true, or by call of Open method.
+      }
     procedure DoInternalConnect; override;
+      { This procedure disconnects object from IB server internally.
+        Is visible only by descendants, in application programming
+        will be invisible. Disconnection you must make by setting 
+        @link(Connected) property to false, or by call of Close method.
+      }
     procedure DoInternalDisconnect; override;
   public
-    constructor Create(AOwner : TComponent); override;
-
-    procedure CommitTransaction; virtual;
-    procedure RollbackTransaction; virtual;
     procedure StartTransaction; override;
     procedure EndTransaction; override;
-
-    property DatabaseHandle: pointer read FIBDatabaseHandle; 
-    property TransactionHandle: pointer read FIBTransactionHandle;
+    constructor Create(AOwner : TComponent); override;
+    destructor Destroy; override;
   published
-    property Password: string read FPassword write FPassword;
-    property UserName: string read FUserName write FUserName;
-    
+    { On connect, TIBDatabase object retrieve SQL dialect of database file,
+      and sets this property to responding value }
+    property Dialect  : integer read FDialect write FDialect;
+    { Before firing Open method you must set @link(Password),@link(DatabaseName),
+      @link(UserName) properties in order of successfull connect to database }
+    property Password : string read FPassword write FPassword;
+    { This property holds default transaction for database. You must assign it by hand
+      now, default assignment becomes handy, in next release, with transaction
+      handling and evidence }
+    property Transaction : TIBTransaction read FTransaction write SetTransaction;
+    { Before firing Open method you must set @link(Password),@link(DatabaseName),
+      @link(UserName) properties in order of successfull connect to database }
+    property UserName : string read FUserName write FUserName;
+
+    { Identifies, if connection to Interbase server is established, or not.
+      Instead of calling Open, Close methods you can connect or disconnect
+      by setting this property to true or false.
+    }
     property Connected;
+    { This property holds database connect string. On local server it will be
+      absolute path to the db file, if you wanna connect over network, this
+      path looks like this: <server_name>:<path_on_server>, where server_name
+      is absolute IP address, or name of server in DNS or hosts file, path_on_server
+      is absolute path to the file again }
     property DatabaseName;
     property KeepConnection;
     property LoginPrompt;
@@ -63,68 +96,144 @@ type
     property OnLogin;
   end;
 
+{ TIBTransaction }
+
+  {
+    Interbase has two modes for commit and rollback transactions,
+    the difference is simple. If you execute Commit or Rollback,
+    current transaction ends, and you must create new one.
+    If you, on other side, need only commit or rollback data
+    without transaction closing, execute with CommitRetaining or
+    RollbackRetaining. Transaction handle, environment etc. will be
+    as same as before action. Possible values are : caNone, caCommit, caCommitRetaining, caRollback, 
+    caRollbackRetaining
+  }
+  
+  TCommitRollbackAction = (caNone, caCommit, caCommitRetaining, caRollback, 
+    caRollbackRetaining);
+  TAccessMode = (amReadWrite, amReadOnly);
+  TIsolationLevel = (ilConcurrent, ilConsistent, ilReadCommittedRecV,
+    ilReadCommitted);
+  TLockResolution = (lrWait, lrNoWait);
+  TTableReservation = (trNone, trSharedLockRead, trSharedLockWrite, 
+    trProtectedLockRead, trProtectedLockWrite);
+  
+  TIBTransaction = class (TComponent)
+  private
+    FTransactionHandle   : pointer;               // Transaction handle
+    FAction              : TCommitRollbackAction; 
+    FActive              : boolean;
+    FTPB                 : string;                // Transaction parameter buffer
+    FDatabase            : TIBDatabase;
+    FAccessMode          : TAccessMode;
+    FIsolationLevel      : TIsolationLevel;
+    FLockResolution      : TLockResolution;
+    FTableReservation    : TTableReservation; 
+    FStatus              : array [0..19] of ISC_STATUS;
+    
+    procedure SetActive(Value : boolean);
+    procedure SetTPB;
+  protected
+    function GetHandle : pointer; virtual;
+  public
+    { Commits all actions, which was made in transaction, and closes transaction}
+    procedure Commit; virtual;
+    { Commits all actions, closes transaction, and creates new one }
+    procedure CommitRetaining; virtual;
+    { Rollbacks all actions made in transaction, and closes transaction }
+    procedure Rollback; virtual;
+    { Rollbacks all actions made in transaction, closes trans. and creates new one }
+    procedure RollbackRetaining; virtual;
+    { Creates new transaction. If transaction is active, closes it and make new one.
+      Action taken while closing responds to @link(Action) property settings }
+    procedure StartTransaction;
+    constructor Create(AOwner : TComponent); override;
+    destructor Destroy; override;
+  published
+    { Default action while closing transaction by setting 
+     @link(Active) property. For details see @link(TCommitRollbackAction)}
+    property Action : TCommitRollbackAction read FAction write FAction;
+    { Is set to true while transaction is active, false if not.
+      If you set it manually to true, object executes 
+      @link(StartTransaction) method, if transaction is
+      active, and you set Active to false, object executes
+      one of @link(Commit), @link(CommitRetaining), @link(Rollback),
+      @link(RollbackRetaining) methods, depending on @link(Action) property
+      setting.
+    }
+    property Active : boolean read FActive write SetActive;
+    { Transaction must be assigned to some database session, for which purpose
+      you must use this property}
+    property Database : TIBDatabase read FDatabase write FDatabase;
+  end;
+  
+{ TIBQuery }
+
   PIBBookmark = ^TIBBookmark;
   TIBBookmark = record
-    BookmarkData: Integer;
-    BookmarkFlag: TBookmarkFlag;
+    BookmarkData : integer;
+    BookmarkFlag : TBookmarkFlag;
   end;
   
-  // TStatementType indicates if SQL statement returns
-  // result set.
-  TStatementType = (stResult, stNoResult, stDDL);
+  TStatementType = (stNone, stSelect, stInsert, stUpdate, stDelete,
+    stDDL, stGetSegment, stPutSegment, stExecProcedure,
+    stStartTrans, stCommit, stRollback, stSelectForUpd);
   
-  TIBDataset = class (TDataset)
+  TIBQuery = class (TDBDataset)
   private
-    FBufferSize          : longint;
-    FCurrentRecord       : longint;
-    FCurrStmtType        : TStatementType;
+    FTransaction         : TIBTransaction;
     FDatabase            : TIBDatabase;
-    FFlag                : array [0..1024] of shortint;
-    FIsEOF               : boolean;
-    FLoadingFieldDefs    : boolean;
-	FSQLPrepared		 : boolean;
-    FRecordSize          : word;
+    FStatus              : array [0..19] of ISC_STATUS;
+    FFieldFlag           : array [0..1023] of shortint;
+    FBufferSize          : integer;
+    FSQLDA               : PXSQLDA;
+    FSQLDAAllocated      : integer;
+    FStatement           : pointer;
     FRecordCount         : integer;
+    FRecordSize          : word;
+    FCurrentRecord       : integer;
     FSQL                 : TStrings;
-    FSQLDA               : PXSQLDA;
-    FSQLDAAllocated      : longint;
-    FStatementHandle     : pointer;
-    FStatus              : array [0..19] of ISC_STATUS;
-    
-    FDBHandle            : pointer;
-    FTRHandle            : pointer;
-    
-    procedure CheckError(ProcName : string);
-    procedure DoAssignBuffers;
-    procedure DoExecSQL;
-    procedure DoFetch;
-    procedure DoFreeBuffers;
-    procedure DoParseSQL;
-    procedure DoSQLDAAlloc(Count : longint);
-    procedure DoStmtAlloc;
-    procedure DoStmtDealloc;
-    
-    procedure SetBufExtended(Field : TField; CurrBuff,Buffer : pointer);
-    procedure SetBufInteger(Field : TField; CurrBuff,Buffer : pointer);
-    procedure SetBufDateTime(Field : TField; CurrBuff,Buffer : pointer; AType : integer);
-    procedure SetBufString(Field : TField; CurrBuff,Buffer : pointer);
-    
-    function GetStmtType: TStatementType;
-    
-    function LoadBufferFromData(Buffer : PChar): TGetResult;
+    FPrepared            : boolean;
+    FIsEOF               : boolean;
+    FStatementType       : TStatementType;
+    FLoadingFieldDefs    : boolean;
+        
     procedure SetDatabase(Value : TIBDatabase);
-    procedure SetSizes;
-    procedure TranslateFieldType(AType, AScale: longint; 
-      var XType: TFieldType; var XScale: word);
+    procedure SetTransaction(Value : TIBTransaction);
+    procedure AllocSQLDA(Count : integer);
+    procedure AllocStatement;
+    procedure FreeStatement;
+    procedure PrepareStatement;
+    procedure DescribeStatement;
+    procedure SetUpSQLVars;
+    procedure AllocFldBuffers;
+    procedure FreeFldBuffers;
+    procedure Fetch;
+    function LoadBufferFromSQLDA(Buffer : PChar): TGetResult;
+    procedure GetStatementType;
+    procedure SetFieldSizes;
+    procedure TranslateFldType(SQLType, SQLLen : integer; var LensSet : boolean;
+      var TrType : TFieldType; var TrLen : word);
+
+    procedure ExecuteImmediate;
+    procedure ExecuteParams;
+    procedure Execute;
+    
+    // conversion methods
+    procedure GetDateTime(CurrBuff, Buffer : pointer; AType : integer);
+    procedure GetFloat(CurrBuff, Buffer : pointer; Field : TField);
+
   protected
+  
+    // abstract & virual methods of TDataset
     function AllocRecordBuffer: PChar; override;
     procedure FreeRecordBuffer(var Buffer: PChar); override;
     procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
     function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
     function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
     function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
+    function GetRecordCount: integer; override;
     function GetRecordSize: Word; override;
-	function GetRecordCount: integer; override;
     procedure InternalAddRecord(Buffer: Pointer; AAppend: Boolean); override;
     procedure InternalClose; override;
     procedure InternalDelete; override;
@@ -142,11 +251,28 @@ type
     procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
     procedure SetFieldData(Field: TField; Buffer: Pointer); override;
   public
+    { This method is used for executing sql statements, which
+      doesn't return any rows. (insert,delete,update, and DDL commands) }
+    procedure ExecSQL; virtual;
     constructor Create(AOwner : TComponent); override;
     destructor Destroy; override;
   published
-    property SQL : TStrings read FSQL write FSQL;
-    property Database : TIBDatabase read FDatabase write SetDatabase;
+    { Query must have transaction assigned. If transaction is not assigned, and database
+      is, object looks, if database have default transaction, and assigns it }
+    property Transaction : TIBTransaction read FTransaction write SetTransaction;
+    { Use this property to determine, which database session can query use }
+    property Database    : TIBDatabase read FDatabase write SetDatabase;
+    { This property holds SQL command, which you want to execute }
+    property SQL         : TStrings read FSQL write FSQL;
+  end;
+  
+{ TIBStoredProc - not implemented - yet :-/}
+  
+  TIBStoredProc = class (TDataset)
+  private
+  protected
+  public
+  published
   end;
 
 implementation
@@ -167,35 +293,71 @@ type
     __tm_zone : Pchar;
   end;
 
+procedure CheckError(ProcName : string; Status : array of ISC_STATUS);
+var
+  buf : array [0..1024] of char;
+  p   : pointer;
+  Msg : string;
+begin
+  if ((Status[0] = 1) and (Status[1] <> 0)) then
+  begin
+    p := @Status;
+    while isc_interprete(Buf, @p) > 0 do
+      Msg := Msg + #10' -' + StrPas(Buf);
+    raise Exception.Create(ProcName + ': ' + Msg);
+  end;
+end;
 
-///////////////////////////////////////////////////////////////////////
-// TIBDatabase implementation
-//
-
-// PRIVATE PART of TIBDatabase
-
-{---------------------------------------------------------------------}
-{ CheckError                                                          }
-{ This procedure checks IB status vector and, if found some error     }
-{ condition, raises exception with IB error text                      }
-{---------------------------------------------------------------------}
+{ TIBDatabase }
 
-procedure TIBDatabase.CheckError(ProcName:string);
+procedure TIBDatabase.SetDBDialect;
 var
-  buf : array [0..1024] of char;
-  P : pointer;
   x : integer;
+  Len : integer;
+  Buffer : string;
+  ResBuf : array [0..39] of byte;
+begin
+  Buffer := Chr(isc_info_db_sql_dialect) + Chr(isc_info_end);
+  if isc_database_info(@FStatus, @FIBDatabaseHandle, Length(Buffer),
+    @Buffer[1], SizeOf(ResBuf), @ResBuf) <> 0 then
+      CheckError('TIBDatabse.SetDBDialect', FStatus);
+  x := 0;
+  while x < 40 do
+    case ResBuf[x] of
+      isc_info_db_sql_dialect : 
+        begin
+          Inc(x);
+          Len := isc_vax_integer(@ResBuf[x], 2);
+          Inc(x, 2);
+          FDialect := isc_vax_integer(@ResBuf[x], Len);
+          Inc(x, Len);  
+        end;
+      isc_info_end : Break;
+    end;  
+end;
+
+procedure TIBDatabase.SetTransaction(Value : TIBTransaction);
 begin
-  if ((FStatus[0] = 1) and (FStatus[1] <> 0)) then
+  if FTransaction = nil then
   begin
-    p := @FStatus;
-    isc_interprete(Buf, @p);
-    raise Exception.Create(ProcName + ': ' + StrPas(buf));
+    FTransaction := Value;
+    FTransaction.Database := Self;
+    Exit;
   end;
+  
+  if (Value <> FTransaction) and (Value <> nil) then
+    if (not FTransaction.Active) then
+    begin
+      FTransaction := Value;
+      FTransaction.Database := Self;
+    end
+    else Exception.Create('Cannot assign transaction while old transaction active!'); 
 end;
 
-
-// PROTECTED PART of TIBDatabase
+function TIBDatabase.GetHandle: pointer;
+begin
+  Result := FIBDatabaseHandle;
+end;
 
 procedure TIBDatabase.DoInternalConnect;
 var
@@ -215,7 +377,8 @@ begin
   FIBDatabaseHandle := nil;
   if isc_attach_database(@FStatus, Length(DatabaseName), @DatabaseName[1], @FIBDatabaseHandle, 
          Length(DPB), @DPB[1]) <> 0 then
-    CheckError('TIBDatabase.Open');
+    CheckError('TIBDatabase.Open', FStatus);
+  SetDBDialect;
 end;
 
 procedure TIBDatabase.DoInternalDisconnect;
@@ -226,620 +389,806 @@ begin
     Exit;
   end;
   isc_detach_database(@FStatus[0], @FIBDatabaseHandle);
-  CheckError('TIBDatabase.Close');
+  CheckError('TIBDatabase.Close', FStatus);
 end;
 
+procedure TIBDatabase.StartTransaction;
+begin
+  if FTransaction = nil then
+    raise EDatabaseError.Create('TIBDatabase.StartTransaction: Transaction not set');
+  FTransaction.Active := True;    
+end;
 
-// PUBLIC PART of TIBDatabase
+procedure TIBDatabase.EndTransaction;
+begin
+  if FTransaction = nil then
+    raise EDatabaseError.Create('TIBDatabase.EndTransaction: Transaction not set');
+  FTransaction.Active := False;    
+end;
 
 constructor TIBDatabase.Create(AOwner : TComponent);
 begin
   inherited Create(AOwner);
-  FIBDatabaseHandle := nil;
-  FIBTransactionHandle := nil;
-  FUserName := '';
-  FPassword := '';
+  FIBDatabaseHandle    := nil;
+  FPassword            := '';
+  FTransaction         := nil;
+  FUserName            := '';
+  FillChar(FStatus, SizeOf(FStatus), #0);
 end;
 
-procedure TIBDatabase.CommitTransaction;
+destructor TIBDatabase.Destroy;
 begin
-  if FIBTransactionHandle <> nil then
-    if isc_commit_retaining(@FStatus, @FIBTransactionHandle) <> 0 then
-      CheckError('TIBDatabase.CommitTransaction');
+  if FTransaction <> nil then
+  begin
+    FTransaction.Active := False;
+    FTransaction.Database := nil;
+  end;
+  inherited Destroy;
 end;
 
-procedure TIBDatabase.RollbackTransaction;
+{ TIBTransaction }
+
+procedure TIBTransaction.SetActive(Value : boolean);
 begin
-  if FIBTransactionHandle <> nil then
-    if isc_rollback_retaining(@FStatus, FIBTransactionHandle) <> 0 then
-      CheckError('TIBDatabase.RollbackTransaction');
+  if FActive = Value then Exit;
+  if (FActive) and (not Value) then
+    case FAction of
+      caCommit            : Commit;
+      caCommitRetaining   : CommitRetaining;
+      caRollback          : Rollback;
+      caRollbackRetaining : RollbackRetaining;
+    else
+      Exception.Create('TIBTransaction.SetActive: Transaction is already active.');
+    end;
+  if (not FActive) and (Value) then
+    StartTransaction;
 end;
 
-procedure TIBDatabase.StartTransaction;
+procedure TIBTransaction.SetTPB;
 begin
-  if FIBTransactionHandle = nil then
-  begin
-    if isc_start_transaction(@FStatus, @FIBTransactionHandle, 1, [@FIBDatabaseHandle, 0, nil]) <> 0 then
-      CheckError('TIBDatabase.StartTransaction');
+  FTPB := chr(isc_tpb_version3);
+
+  case FAccessMode of
+    amReadWrite : FTPB := FTPB + chr(isc_tpb_write);
+    amReadOnly  : FTPB := FTPB + chr(isc_tpb_read);
+  end;
+  
+  case FIsolationLevel of
+    ilConsistent        : FTPB := FTPB + chr(isc_tpb_consistency);
+    ilConcurrent        : FTPB := FTPB + chr(isc_tpb_concurrency);
+    ilReadCommittedRecV : FTPB := FTPB + chr(isc_tpb_read_committed) +
+      chr(isc_tpb_rec_version);
+    ilReadCommitted     : FTPB := FTPB + chr(isc_tpb_read_committed) +
+      chr(isc_tpb_no_rec_version);
+  end;
+  
+  case FLockResolution of
+    lrWait   : FTPB := FTPB + chr(isc_tpb_wait);
+    lrNoWait : FTPB := FTPB + chr(isc_tpb_nowait);
+  end;
+  
+  case FTableReservation of
+    trSharedLockRead     : FTPB := FTPB + chr(isc_tpb_shared) + 
+      chr(isc_tpb_lock_read);
+    trSharedLockWrite    : FTPB := FTPB + chr(isc_tpb_shared) + 
+      chr(isc_tpb_lock_write);
+    trProtectedLockRead  : FTPB := FTPB + chr(isc_tpb_protected) +
+      chr(isc_tpb_lock_read);
+    trProtectedLockWrite : FTPB := FTPB + chr(isc_tpb_protected) +
+      chr(isc_tpb_lock_write);
   end;
 end;
 
-procedure TIBDatabase.EndTransaction;
+function TIBTransaction.GetHandle: pointer;
 begin
-  if FIBTransactionHandle <> nil then
-  begin
-    if isc_commit_transaction(@FStatus, @FIBTransactionHandle) <> 0 then
-      CheckError('TIBDatabase.EndTransaction');
-    FIBTransactionHandle := nil;
-  end;
+  Result := FTransactionHandle;
 end;
 
+procedure TIBTransaction.Commit;
+begin
+  if not FActive then Exit;
+  if isc_commit_transaction(@FStatus, @FTransactionHandle) <> 0 then
+    CheckError('TIBTransaction.Commit', FStatus)
+  else FActive := False;
+end;
 
-///////////////////////////////////////////////////////////////////////
-// TIBDataset implementation
-//
-
-// PRIVATE PART
-
-procedure TIBDataset.CheckError(ProcName : string);
-var
-  buf : array [0..1024] of char;
-  P : pointer;
-  Msg : string;
-  x : integer;
+procedure TIBTransaction.CommitRetaining;
 begin
-  if ((FStatus[0] = 1) and (FStatus[1] <> 0)) then
-  begin
-    p := @FStatus;
-    while isc_interprete(Buf, @p) > 0 do
-      Msg := Msg + #10' -' + StrPas(Buf);
-    raise Exception.Create(ProcName + ': ' + Msg);
-  end;
+  if not FActive then Exit;
+  if isc_commit_retaining(@FStatus, @FTransactionHandle) <> 0 then
+    CheckError('TIBTransaction.CommitRetaining', FStatus);
 end;
 
-procedure TIBDataset.DoAssignBuffers;
-var
-  Buf : PChar;
-  x   : longint;
+procedure TIBTransaction.Rollback;
 begin
-  for x := 0 to FSQLDA^.SQLD - 1 do
-  begin
-    Buf := AllocMem(FSQLDA^.SQLVar[x].SQLLen);
-    FSQLDA^.SQLVar[x].SQLData := Buf;
-    FSQLDA^.SQLVar[x].SQLInd  := @FFlag[x];
-  end;
+  if not FActive then Exit;
+  if isc_rollback_transaction(@FStatus, @FTransactionHandle) <> 0 then
+    CheckError('TIBTransaction.Rollback', FStatus)
+  else FActive := False;
 end;
 
-procedure TIBDataset.DoExecSQL;
+procedure TIBTransaction.RollbackRetaining;
 begin
-  if isc_dsql_execute(@FStatus, @FTrHandle, @FStatementHandle, 1, nil) <> 0 then
-    CheckError('TIBDataset.DoExecSQL');
+  if not FActive then Exit;
+  if isc_rollback_retaining(@FStatus, @FTransactionHandle) <> 0 then
+    CheckError('TIBTransaction.RollbackRetaining', FStatus);
 end;
 
-procedure TIBDataset.DoFetch;
+procedure TIBTransaction.StartTransaction;
 var
-  Res : longint;
+  DBHandle : pointer;
 begin
-  if FCurrStmtType <> stResult then Exit;
-  Res := isc_dsql_fetch(@FStatus, @FStatementHandle, 1, FSQLDA);
-  if (Res <> 100) then
-    CheckError('TIBDataset.DoFetch');
-  FIsEOF := (Res = 100);
+  if Active then Active := False;
+  
+  if FDatabase = nil then
+    Exception.Create('TIBTransaction.StartTransaction: Database not assigned!');
+  
+  if not Database.Connected then
+    Database.Open;
+  
+  DBHandle := Database.GetHandle;
+  SetTPB;
+  FTransactionHandle := nil;
+  
+  if isc_start_transaction(@FStatus, @FTransactionHandle, 1,
+     [@DBHandle, Length(FTPB), @FTPB[1]]) <> 0 then
+    CheckError('TIBTransaction.StartTransaction',FStatus)
+  else FActive := True;
 end;
 
-procedure TIBDataset.DoFreeBuffers;
-var
-  x   : longint;
+constructor TIBTransaction.Create(AOwner : TComponent);
 begin
-  for x := 0 to FSQLDA^.SQLD - 1 do
-    if (FSQLDA^.SQLVar[x].SQLData <> nil) then
-      FreeMem(FSQLDA^.SQLVar[x].SQLData);
+  inherited Create(AOwner);
+
+  FAction := caNone;
+  FActive := False;
+  FAccessMode := amReadWrite;
+  FIsolationLevel := ilReadCommitted;
+  FLockResolution := lrWait;
+  FTableReservation := trNone;
+  FTransactionHandle := nil;
+  FDatabase := nil;
+  
+  FillChar(FStatus, SizeOf(FStatus), #0);
 end;
 
-procedure TIBDataset.DoParseSQL;
-var
-  Buf      : string;
-  x        : longint;
+destructor TIBTransaction.Destroy;
 begin
-  if FSQL.Count < 1 then
-    raise Exception.Create('TIBDataset.DoParseSQL: Empty SQL statement');
+  if Database <> nil then
+    Database.Transaction := nil;
 
-  Buf := '';
-  for x := 0 to FSQL.Count - 1 do
-    Buf := Buf + FSQL[x] + ' ';
+{  // i really can't allow commit of transaction
+  // on destroy...
+}
+{  
+  try
+    if Active then 
+      Active := False;
+  except
+  end;
+}
+  
+  inherited Destroy;
+end;
 
-  if isc_dsql_prepare(@FStatus, @FTrHandle, @FStatementHandle, 0, @Buf[1], 1, nil) <> 0 then    CheckError('TIBDataset.DoParseSQL - Prepare');
-    
-  if isc_dsql_describe(@FStatus, @FStatementHandle, 1, FSQLDA) <> 0 then
-    CheckError('TIBDataset.DoParseSQL - Describe');
+{ TIBQuery }
 
-  if FSQLDA^.SQLN < FSQLDA^.SQLD then
+procedure TIBQuery.SetTransaction(Value : TIBTransaction);
+begin
+  CheckInactive;
+  if (FTransaction <> Value) then
+    FTransaction := Value;
+end;
+
+procedure TIBQuery.SetDatabase(Value : TIBDatabase);
+begin
+  CheckInactive;
+  if (FDatabase <> Value) then
   begin
-    x := FSQLDA^.SQLD;
-    DoSQLDAAlloc(x);
-    if isc_dsql_describe(@FStatus, @FStatementHandle, 1, FSQLDA) <> 0 then
-      CheckError('TIBDataset.DoParseSQL - Describe');
+    FDatabase := Value;
+    if (FTransaction = nil) and (Assigned(FDatabase.Transaction)) then
+      SetTransaction(FDatabase.Transaction);
   end;
-  
-  FCurrStmtType := GetStmtType;
-  FSQLPrepared := True;  
 end;
 
-procedure TIBDataset.DoSQLDAAlloc(Count : longint);
+procedure TIBQuery.AllocSQLDA(Count : integer);
 begin
   if FSQLDAAllocated > 0 then
     FreeMem(FSQLDA, XSQLDA_Length * FSQLDAAllocated);
   GetMem(FSQLDA, XSQLDA_Length * Count);
   FSQLDAAllocated := Count;
-  FSQLDA^.Version := SQLDA_VERSION1;
-  FSQLDA^.SQLN := Count;
+  FSQLDA^.Version := sqlda_version1;
+  FSQLDA^.SQLN := Count; 
 end;
 
-procedure TIBDataset.DoStmtAlloc;
+procedure TIBQuery.AllocStatement;
+var
+  dh : pointer;
 begin
   if not FDatabase.Connected then
     FDatabase.Open;
-  if FDatabase.TransactionHandle = nil then
-    FDatabase.StartTransaction;
-  FDBHandle := FDatabase.DatabaseHandle;
-  FTRHandle := FDatabase.TransactionHandle;
+  dh := FDatabase.GetHandle;
+  
+  if isc_dsql_allocate_statement(@FStatus, @dh, @FStatement) <> 0 then
+    CheckError('TIBQuery.AllocStatement', FStatus);
+end;
 
-  if isc_dsql_allocate_statement(@FStatus, @FDBHandle, @FStatementHandle) <> 0 then
-    CheckError('TIBDataset.DoStmtAlloc');
+procedure TIBQuery.FreeStatement;
+begin
+  if isc_dsql_free_statement(@FStatus, @FStatement, DSQL_Drop) <> 0 then
+    CheckError('TIBQuery.DeallocStatement', FStatus);
+  FStatement := nil;
 end;
 
-procedure TIBDataset.DoStmtDealloc;
+procedure TIBQuery.PrepareStatement;
+var
+  Buf : string;
+  x   : integer;
+  tr  : pointer;
 begin
-  if isc_dsql_free_statement(@FStatus, @FStatementHandle, DSQL_Drop) <> 0 then
-    CheckError('TIBDataset.DoStmtDealloc');
-  FStatementHandle := nil;
+  tr := FTransaction.GetHandle;
+  
+  for x := 0 to FSQL.Count - 1 do
+    Buf := Buf + FSQL[x] + ' ';
+    
+  if isc_dsql_prepare(@FStatus, @tr, @FStatement, 0, @Buf[1], 1, nil) <> 0 then
+    CheckError('TIBQuery.PrepareStatement', FStatus);
 end;
 
-function TIBDataset.GetStmtType: TStatementType;
+procedure TIBQuery.DescribeStatement;
+begin
+  if isc_dsql_describe(@FStatus, @FStatement, 1, FSQLDA) <> 0 then
+    CheckError('TIBQuery.DescribeStatement', FStatus);
+  if FSQLDA^.SQLD > FSQLDA^.SQLN then
+  begin
+    AllocSQLDA(FSQLDA^.SQLD);
+    if isc_dsql_describe(@FStatus, @FStatement, 1, FSQLDA) <> 0 then
+      CheckError('TIBQuery.DescribeStatement', FStatus);
+  end;
+end;
+
+procedure TIBQuery.SetUpSQLVars;
 var
-  ResBuf : array [0..7] of char;
   x : integer;
-  SType : integer;
 begin
-  x := isc_info_sql_stmt_type;
-  isc_dsql_sql_info(@FStatus, @FStatementHandle, SizeOf(x),
-    @x, SizeOf(ResBuf), @ResBuf);
-  if Ord(ResBuf[0]) = isc_info_sql_stmt_type then
+  for x := 0 to FSQLDA^.SQLN - 1 do
   begin
-    x := isc_vax_integer(@ResBuf[1], 2);
-    SType := isc_vax_integer(@ResBuf[3], x);
+    case FSQLDA^.SQLVar[x].SQLType of
+      sql_varying + 1: 
+        FSQLDA^.SQLVar[x].SQLType := sql_varying;
+      sql_text + 1   : 
+        FSQLDA^.SQLVar[x].SQLType := sql_text;
+      sql_short, sql_short + 1, sql_long + 1:
+        FSQLDA^.SQLVar[x].SQLType := sql_long;
+      sql_float + 1  :
+        FSQLDA^.SQLVar[x].SQLType := sql_float;
+      sql_double + 1 : 
+        FSQLDA^.SQLVar[x].SQLType := sql_double;
+      sql_blob + 1   : 
+        FSQLDA^.SQLVar[x].SQLType := sql_blob;
+      sql_type_time + 1   :
+        FSQLDA^.SQLVar[x].SQLType := sql_type_time;
+      sql_timestamp + 1:
+        FSQLDA^.SQLVar[x].SQLType := sql_timestamp;
+    end; 
   end;
-  case SType of
-    isc_info_sql_stmt_select:
-      Result := stResult;
-    isc_info_sql_stmt_insert, isc_info_sql_stmt_update,
-    isc_info_sql_stmt_delete:
-      Result := stNoResult;
-    else Result := stDDL;
+end;
+
+procedure TIBQuery.AllocFldBuffers;
+var
+  Buf: pointer;
+  x  : shortint;
+begin
+  {$R-}
+  for x := 0 to FSQLDA^.SQLD - 1 do
+  begin
+    Buf := AllocMem(FSQLDA^.SQLVar[x].SQLLen);
+    FSQLDA^.SQLVar[x].SQLData := Buf;
+    FSQLDA^.SQLVar[x].SQLInd  := @FFieldFlag[x];
   end;
+  {$R+}
 end;
 
-function TIBDataset.LoadBufferFromData(Buffer : PChar): TGetResult;
+procedure TIBQuery.FreeFldBuffers;
 var
-  x : integer;
-  p : word;
-  T : TISC_TIMESTAMP;
+  x  : integer;
+begin
+  {$R-}
+  for x := 0 to FSQLDA^.SQLD - 1 do
+  begin
+    if FSQLDA^.SQLVar[x].SQLData <> nil then
+    begin
+      FreeMem(FSQLDA^.SQLVar[x].SQLData);
+      FSQLDA^.SQLVar[x].SQLData := nil; 
+    end;
+  end;
+  {$R+}
+end;
+
+procedure TIBQuery.Fetch;
+var
+  retcode : integer;
 begin
-  DoFetch;
+  if not (FStatementType in [stSelect]) then 
+    Exit;
+
+  retcode := isc_dsql_fetch(@FStatus, @FStatement, 1, FSQLDA);
+  if (retcode <> 0) and (retcode <> 100) then
+    CheckError('TIBQuery.Fetch', FStatus);
+
+  FIsEOF := (retcode = 100); 
+end;
+
+function TIBQuery.LoadBufferFromSQLDA(Buffer : PChar): TGetResult;
+var
+  x          : integer;
+  VarcharLen : word;
+begin
+  
+  Fetch;
   if FIsEOF then
-    Result := grEOF
-  else begin
-    for x := 0 to FSQLDA^.SQLD - 1 do
+  begin
+    Result := grEOF;
+    Exit;
+  end;
+  
+  {$R-}
+  for x := 0 to FSQLDA^.SQLD - 1 do
+  begin
+    with FSQLDA^.SQLVar[x] do
     begin
-      if (FSQLDA^.SQLVar[x].SQLType = SQL_VARYING) or
-         (FSQLDA^.SQLVar[x].SQLType = SQL_VARYING + 1) then
+      if ((SQLType and not 1) = SQL_VARYING) then
       begin
-        Move(FSQLDA^.SQLVar[x].SQLData^, P, 2);
-    	Move((FSQLDA^.SQLVar[x].SQLData + 2)^, Buffer^, P);
-        PChar(Buffer+P)^ := #0;
+        Move(SQLData^, VarcharLen, 2);
+        Move((SQLData + 2)^, Buffer^, VarcharLen);
+        PChar(Buffer + VarcharLen)^ := #0;
       end
-	  else
-    	Move(FSQLDA^.SQLVar[x].SQLData^, Buffer^, FSQLDA^.SQLVar[x].SQLLen);
-      Inc(Buffer,FSQLDA^.SQLVar[x].SQLLen);
+      else Move(SQLData^, Buffer^, SQLLen);
+      Inc(Buffer, SQLLen);
     end;
-    Result := grOK;
   end;
+  {$R+} 
+  Result := grOK;
+
 end;
 
-procedure TIBDataset.SetDatabase(Value : TIBDatabase);
+procedure TIBQuery.GetStatementType;
+var
+  x : integer;
+  ResBuf : array [0..7] of char;
 begin
-  CheckInactive;
-  If Value<>FDatabase then
+  FStatementType := stNone;
+  x := isc_info_sql_stmt_type;
+  if isc_dsql_sql_info(@FStatus, @FStatement, SizeOf(X), 
+    @x, SizeOf(ResBuf), @ResBuf) <> 0 then
+    CheckError('TIBQuery.GetStatementType', FStatus);
+  if Ord(ResBuf[0]) = isc_info_sql_stmt_type then
   begin
-    if Value<>Nil Then
-      FDatabase:=Value; 
+    x := isc_vax_integer(@ResBuf[1], 2);
+    FStatementType := TStatementType(isc_vax_integer(@ResBuf[3], x));
   end;
 end;
 
-procedure TIBDataset.SetSizes;
+procedure TIBQuery.SetFieldSizes;
 var
   x : integer;
 begin
   FRecordSize := 0;
   FBufferSize := 0;
+  {$R-}
   for x := 0 to FSQLDA^.SQLD - 1 do
-  begin
     Inc(FRecordSize, FSQLDA^.SQLVar[x].SQLLen);
-  end;
+  {$R+}
   FBufferSize := FRecordSize + SizeOf(TIBBookmark);
 end;
 
-procedure TIBDataset.TranslateFieldType(AType, AScale: longint; 
-  var XType: TFieldType; var XScale: word);
+procedure TIBQuery.TranslateFldType(SQLType, SQLLen : integer; var LensSet : boolean;
+  var TrType : TFieldType; var TrLen : word);
 begin
-  case AType of
-    SQL_TEXT, SQL_VARYING, SQL_TEXT+1, SQL_VARYING+1:
+  LensSet := False;
+
+  case (SQLType and not 1) of
+    SQL_VARYING : 
+      begin
+        LensSet := True;
+        TrType := ftString;
+        TrLen := SQLLen;
+      end;
+    SQL_TEXT :      
       begin
-        XType := ftString;
-        XScale := AScale;
+        LensSet := True;
+        TrType := ftString;
+        TrLen := SQLLen;
       end;
-    SQL_DOUBLE, SQL_DOUBLE+1: 
+    SQL_TYPE_DATE :
+        TrType := ftDateTime;
+    SQL_TYPE_TIME :
+        TrType := ftDateTime;
+    SQL_TIMESTAMP :
+        TrType := ftDateTime;
+    SQL_ARRAY :
       begin
-        XType := ftFloat;
-        XScale := AScale;
       end;
-    SQL_LONG, SQL_LONG+1, SQL_SHORT, SQL_SHORT+1: 
+    SQL_BLOB : 
       begin
-        XType := ftInteger;
-        XScale := AScale;
       end;
-{    SQL_DATE, SQL_DATE+1, SQL_TIME, SQL_TIME+1,}
-    SQL_TYPE_TIME:
+    SQL_SHORT :
       begin
-        XType := ftTime;
-        XScale := AScale;
+        LensSet := True;
+        TrLen := SQLLen;
+        TrType := ftInteger;
       end;
-    SQL_TYPE_DATE:
+    SQL_LONG :
       begin
-        XType := ftDate;
-        XScale := AScale;
+        LensSet := True;
+        TrLen := SQLLen;
+        TrType := ftInteger;
       end;
-    SQL_FLOAT,SQL_FLOAT+1:
+    SQL_INT64 :
+        {TrType := ftInt64};
+    SQL_DOUBLE :
       begin
-        XType := ftFloat;
-        XScale := AScale;
+        LensSet := True;
+        TrLen := SQLLen;
+        TrType := ftFloat;
       end;
-    SQL_TIMESTAMP, SQL_TIMESTAMP+1: 
+    SQL_FLOAT :
       begin
-        XType := ftDateTime;
-        XScale := AScale;
+        LensSet := True;
+        TrLen := SQLLen;
+        TrType := ftFloat;
       end;
   end;
 end;
 
-
-// PROTECTED PART
-
-function TIBDataset.AllocRecordBuffer: PChar;
+procedure TIBQuery.ExecuteImmediate;
 begin
-  Result := AllocMem(FBufferSize);
 end;
 
-procedure TIBDataset.FreeRecordBuffer(var Buffer: PChar);
+procedure TIBQuery.ExecuteParams;
 begin
-  FreeMem(Buffer);
+  //!! to be implemented
 end;
 
-procedure TIBDataset.GetBookmarkData(Buffer: PChar; Data: Pointer);
+procedure TIBQuery.Execute;
+var
+  tr : pointer;
 begin
-  PInteger(Data)^ := PIBBookmark(Buffer + FRecordSize)^.BookmarkData;
+  tr := FTransaction.GetHandle;
+  if isc_dsql_execute(@FStatus, @tr, @FStatement, 1, nil) <> 0 then
+    CheckError('TIBQuery.Execute', FStatus);
 end;
 
-function TIBDataset.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
+procedure TIBQuery.GetDateTime(CurrBuff, Buffer : pointer; AType : integer);
+var
+  CTime : TTm;          // C struct time
+  STime : TSystemTime;  // System time
+  PTime : TDateTime;    // Pascal time
 begin
-  Result := PIBBookmark(Buffer + FRecordSize)^.BookmarkFlag;
+  case (AType and not 1) of 
+    SQL_TYPE_DATE : 
+      isc_decode_sql_date(PISC_DATE(CurrBuff), @CTime);
+    SQL_TYPE_TIME :
+      isc_decode_sql_time(PISC_TIME(CurrBuff), @CTime);
+    SQL_TIMESTAMP :
+      isc_decode_timestamp(PISC_TIMESTAMP(CurrBuff), @CTime);
+  end;
+  STime.Year        := CTime.tm_year + 1900;
+  STime.Month       := CTime.tm_mon + 1;
+  STime.Day         := CTime.tm_mday;
+  STime.Hour        := CTime.tm_hour;
+  STime.Minute      := CTime.tm_min;
+  STime.Second      := CTime.tm_sec;
+  STime.Millisecond := 0;
+  
+  PTime := SystemTimeToDateTime(STime);
+  Move(PTime, Buffer^, SizeOf(PTime));
 end;
 
-procedure TIBDataset.SetBufExtended(Field : TField; CurrBuff,Buffer : pointer);
+procedure TIBQuery.GetFloat(CurrBuff, Buffer : pointer; Field : TField);
 var
-  E    : extended;
-  D    : double;
-  S    : single;
+  Ext : extended;
+  Dbl : double;
+  Sin : single;
 begin
   case Field.Size of
-    4    : 
+    4 :
       begin
-        Move(CurrBuff^,S,4);
-        E := S;
+        Move(CurrBuff^, Sin, 4);
+        Ext := Sin;
       end;
-    8    :
+    8 :
       begin
-        Move(CurrBuff^,D,8);
-        E := D;
+        Move(CurrBuff^, Dbl, 8);
+        Ext := Dbl;
       end;
-    10   : Move(CurrBuff^,E,10);
+    10: Move(CurrBuff^, Ext, 10);
   end;
-  Move(E, Buffer^, 10);
+  Move(Ext, Buffer^, 10);
 end;
 
-procedure TIBDataset.SetBufInteger(Field : TField; CurrBuff,Buffer : pointer);
-var
-  I    : integer;
+function TIBQuery.AllocRecordBuffer: PChar;
 begin
-  I := 0;
-  Move(I, Buffer^, SizeOf(Integer));
-  Move(CurrBuff^, Buffer^, Field.Size);
+  Result := AllocMem(FBufferSize);
 end;
 
-procedure TIBDataset.SetBufDateTime(Field : TField; CurrBuff,Buffer : pointer; AType : integer);
-var
-  D    : TDateTime;
-  S    : TSystemTime;
-  TM   : TTm;
-  TT   : TIsc_timestamp;
-begin
-  case AType of
-    SQL_TYPE_DATE: 
-      isc_decode_sql_date(PISC_DATE(CurrBuff), @TM);
-    SQL_TYPE_TIME:
-      isc_decode_sql_time(PISC_TIME(CurrBuff), @TM);
-    SQL_TIMESTAMP, SQL_TIMESTAMP+1:
-      isc_decode_timestamp(PISC_TIMESTAMP(CurrBuff), @TM);
-  end;
-  S.Year := TM.tm_year + 1900;
-  S.Month := TM.tm_mon + 1;
-  S.Day := TM.tm_mday;
-  S.Hour := TM.tm_hour;
-  S.Minute := TM.tm_min;
-  S.Second := TM.tm_sec;
-  S.Millisecond := 0;
-  D := SystemTimeToDateTime(S);
-  {$warning !!! D is okay, but Field.AsDateTime returns wrong value !!! } 
-//  WriteLn(DateTimeToStr(D));
-  Move(D, Buffer^, SizeOf(D));
-end;
-
-procedure TIBDataset.SetBufString(Field : TField; CurrBuff,Buffer : pointer);
-begin
-  Move(CurrBuff^, Buffer^, Field.Size);
-  PChar(Buffer + Field.Size)^ := #0;
-end;
-
-function TIBDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
+procedure TIBQuery.FreeRecordBuffer(var Buffer: PChar);
+begin
+  FreeMem(Buffer);
+end;
+
+procedure TIBQuery.GetBookmarkData(Buffer: PChar; Data: Pointer);
+begin
+  PInteger(Data)^ := PIBBookmark(Buffer + FRecordSize)^.BookmarkData; 
+end;
+
+function TIBQuery.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
+begin
+  Result := PIBBookmark(Buffer + FRecordSize)^.BookmarkFlag;
+end;
+
+function TIBQuery.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
 var
-  x        : longint;
+  x : longint;
+  b : longint;
   CurrBuff : PChar;
 begin
   Result := False;
   CurrBuff := ActiveBuffer;
+  
   for x := 0 to FSQLDA^.SQLD - 1 do
   begin
+    {$R-}
     if (Field.FieldName = FSQLDA^.SQLVar[x].SQLName) then
     begin
-
       case Field.DataType of
-        ftFloat:  
-          SetBufExtended(Field, CurrBuff, Buffer);
-        ftString: 
-          SetBufString(Field, CurrBuff, Buffer);
-        ftDate,ftTime,ftDateTime:
-          SetBufDateTime(Field, CurrBuff, Buffer, FSQLDA^.SQLVar[x].SQLType);
-        ftInteger:
-          SetBufInteger(Field, CurrBuff, Buffer);
+        ftInteger : 
+          begin
+            b := 0;
+            Move(b, Buffer^, 4);
+            Move(CurrBuff^, Buffer^, Field.Size);
+          end;
+        ftDate, ftTime, ftDateTime:
+          GetDateTime(CurrBuff, Buffer, FSQLDA^.SQLVar[x].SQLType);
+        ftString  :
+          begin
+            Move(CurrBuff^, Buffer^, Field.Size);
+            PChar(Buffer + Field.Size)^ := #0;
+          end;
+        ftFloat   : 
+          GetFloat(CurrBuff, Buffer, Field);
       end;
-
+      
       Result := True;
-
-      break; 
+      
+      Break;
     end
     else Inc(CurrBuff, FSQLDA^.SQLVar[x].SQLLen);
+    {$R+}
   end;
 end;
 
-function TIBDataset.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
+function TIBQuery.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
 begin
-  if FCurrStmtType <> stResult then Exit;
-  if FIsEOF then 
+  if FStatementType <> stSelect then 
+  begin
+    Result := grEOF;
+    Exit;
+  end;
+  if FIsEOF then
     Result := grEOF
   else begin
-	Result := grOk;
+    Result := grOK;
     case GetMode of
-	  gmPrior: 
-		if FCurrentRecord <= 0 then
-		begin
-		  Result := grBOF;
-		  FCurrentRecord := -1;
-		end
-		else Dec(FCurrentRecord);
-	  gmCurrent:
-		if (FCurrentRecord < 0) or (FCurrentRecord >= RecordCount) then
-		  Result := grError;
-      gmNext: 
-		if FCurrentRecord >= (RecordCount - 1) then
+      gmPrior :
+        if FCurrentRecord <= 0 then
+        begin
+          Result := grBOF;
+          FCurrentRecord := -1;
+        end
+        else Dec(FCurrentRecord);
+      gmCurrent : 
+        if (FCurrentRecord < 0) or (FCurrentRecord >= RecordCount) then
+          Result := grError;
+      gmNext : 
+        if FCurrentRecord >= (RecordCount - 1) then
         begin
-		  Result := LoadBufferFromData(Buffer);
-          if Result = grOk then 
+          Result := LoadBufferFromSQLDA(Buffer);
+          if Result = grOK then
           begin
             Inc(FCurrentRecord);
             Inc(FRecordCount);
           end;
         end
-		else Inc(FCurrentRecord);
+        else Inc(FCurrentRecord);
     end;
-
-    if Result = grOK then
-    begin
-      with PIBBookmark(Buffer + FRecordSize)^ do
-      begin
-        BookmarkData := FCurrentRecord;
-        BookmarkFlag := bfCurrent;
-      end;               
-    end
-    else if (Result = grError) {and (DoCheck)} then
-      DatabaseError('No record');
   end;
+  
+  if Result = grOK then
+  begin
+    with PIBBookmark(Buffer + FRecordSize)^ do
+    begin
+      BookmarkData := FCurrentRecord;
+      BookmarkFlag := bfCurrent;
+    end;
+  end
+  else if (Result = grError) then
+    DatabaseError('No record');
 end;
 
-function TIBDataset.GetRecordCount: integer;
+function TIBQuery.GetRecordCount: integer;
 begin
   Result := FRecordCount;
 end;
 
-function TIBDataset.GetRecordSize: Word;
+function TIBQuery.GetRecordSize: Word;
 begin
   Result := FRecordSize;
 end;
 
-procedure TIBDataset.InternalAddRecord(Buffer: Pointer; AAppend: Boolean);
+procedure TIBQuery.InternalAddRecord(Buffer: Pointer; AAppend: Boolean);
 begin
+  // not implemented - sql dataset
 end;
 
-procedure TIBDataset.InternalClose;
+procedure TIBQuery.InternalClose;
 begin
-  DoFreeBuffers;
-  DoStmtDealloc;
+  FreeFldBuffers;
+  FreeStatement;
   if DefaultFields then
     DestroyFields;
   FIsEOF := False;
   FCurrentRecord := -1;
   FBufferSize := 0;
   FRecordSize := 0;
-  FRecordCount := 0;
-//  DoSQLDAAlloc(50);
+  FRecordCount:= 0;
 end;
 
-procedure TIBDataset.InternalDelete;
+procedure TIBQuery.InternalDelete;
 begin
+  // not implemented - sql dataset
 end;
 
-procedure TIBDataset.InternalFirst;
+procedure TIBQuery.InternalFirst;
 begin
   FCurrentRecord := -1;
 end;
 
-procedure TIBDataset.InternalGotoBookmark(ABookmark: Pointer);
+procedure TIBQuery.InternalGotoBookmark(ABookmark: Pointer);
 begin
   FCurrentRecord := PInteger(ABookmark)^;
 end;
 
-procedure TIBDataset.InternalHandleException;
+procedure TIBQuery.InternalHandleException;
 begin
-  // not implemented
 end;
 
-procedure TIBDataset.InternalInitFieldDefs;
+procedure TIBQuery.InternalInitFieldDefs;
 var
-  x       : longint;
-  TransFt : TFieldType;
-  TransSz : word;
+  x         : integer;
+  lenset    : boolean;
+  TransLen  : word;
+  TransType : TFieldType;
 begin
-  if FLoadingFieldDefs then 
-  begin
-    WriteLn('Loading FieldDefs...');
+  if FLoadingFieldDefs then
     Exit;
-  end;
-  
+
   FLoadingFieldDefs := True;
   
   try
-    try
-      FieldDefs.Clear;
-      for x := 0 to FSQLDA^.SQLD - 1 do
-      begin
-        TranslateFieldType(FSQLDA^.SQLVar[x].SQLType, FSQLDA^.SQLVar[x].SQLLen,
-          TransFt, TransSz);
-        TFieldDef.Create(FieldDefs,
-          FSQLDA^.SQLVar[x].SQLName, 
-          TransFt, TransSz, False, (x+1));
-      end;
-    finally
+    FieldDefs.Clear;
+    {$R-}
+    for x := 0 to FSQLDA^.SQLD - 1 do
+    begin
+      TranslateFldType(FSQLDA^.SQLVar[x].SQLType, FSQLDA^.SQLVar[x].SQLLen, lenset, 
+        TransType, TransLen);
+      TFieldDef.Create(FieldDefs, FSQLDA^.SQLVar[x].SQLName, TransType, 
+        TransLen, False, (x + 1));
     end;
+    {$R+}
   finally
     FLoadingFieldDefs := False;
   end;
 end;
 
-procedure TIBDataset.InternalInitRecord(Buffer: PChar);
+procedure TIBQuery.InternalInitRecord(Buffer: PChar);
 begin
   FillChar(Buffer^, FBufferSize, #0);
 end;
 
-procedure TIBDataset.InternalLast;
+procedure TIBQuery.InternalLast;
 begin
   FCurrentRecord := RecordCount;
 end;
 
-procedure TIBDataset.InternalOpen;
+procedure TIBQuery.InternalOpen;
 begin
   try
-    DoStmtAlloc;
-    DoParseSQL;
-    if FCurrStmtType = stResult then
+    AllocStatement;
+    PrepareStatement;
+    GetStatementType;
+    if FStatementType in [stSelect] then
     begin
-      DoAssignBuffers;
-      DoExecSQL;
+      DescribeStatement;
+      AllocFldBuffers;
+      Execute;
       InternalInitFieldDefs;
       if DefaultFields then
         CreateFields;
-      SetSizes;
+      SetFieldSizes;
       BindFields(True);
     end
-    else DoExecSQL;
+    else Execute;
   except
-	raise;
+    on E:Exception do
+      raise;
   end;
-  
 end;
 
-procedure TIBDataset.InternalPost;
+procedure TIBQuery.InternalPost;
 begin
+  // not implemented - sql dataset
 end;
 
-procedure TIBDataset.InternalSetToRecord(Buffer: PChar);
+procedure TIBQuery.InternalSetToRecord(Buffer: PChar);
 begin
   FCurrentRecord := PIBBookmark(Buffer + FRecordSize)^.BookmarkData;
 end;
 
-function TIBDataset.IsCursorOpen: Boolean;
+function TIBQuery.IsCursorOpen: Boolean;
 begin
-  Result := FStatementHandle <> nil; //??
+  Result := False;
 end;
 
-procedure TIBDataset.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
+procedure TIBQuery.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
 begin
   PIBBookmark(Buffer + FRecordSize)^.BookmarkFlag := Value;
 end;
 
-procedure TIBDataset.SetBookmarkData(Buffer: PChar; Data: Pointer);
+procedure TIBQuery.SetBookmarkData(Buffer: PChar; Data: Pointer);
 begin
   PIBBookmark(Buffer + FRecordSize)^.BookmarkData := PInteger(Data)^;
 end;
 
-procedure TIBDataset.SetFieldData(Field: TField; Buffer: Pointer);
+procedure TIBQuery.SetFieldData(Field: TField; Buffer: Pointer);
 begin
 end;
 
-// PUBLIC PART
+// public part
 
-constructor TIBDataset.Create(AOwner : TComponent);
+procedure TIBQuery.ExecSQL;
+begin
+  AllocStatement;
+  PrepareStatement;
+  GetStatementType;
+  Execute;
+  FreeStatement;
+end;
+
+constructor TIBQuery.Create(AOwner : TComponent);
 begin
   inherited Create(AOwner);
+  FillChar(FFieldFlag, SizeOf(FFieldFlag), #0);
   FSQL := TStringList.Create;
-  FIsEOF := False;
+  FStatement := nil;
   FCurrentRecord := -1;
-  FBufferSize := 0;
-  FRecordSize := 0;
-  FRecordCount := 0;
-  DoSQLDAAlloc(50);
+  FDatabase := nil;
+  FTransaction := nil;
+  FSQLDAAllocated := 0;
+  FLoadingFieldDefs := False;
+  FPrepared := False;
+  AllocSQLDA(10);
 end;
 
-destructor TIBDataset.Destroy;
+destructor TIBQuery.Destroy;
 begin
+  if Active then Close;
   FSQL.Free;
   inherited Destroy;
   FreeMem(FSQLDA, XSQLDA_Length * FSQLDAAllocated);
 end;
 
+{ TIBStoredProc }
+
 end.
 
-{
-  $Log$
-  Revision 1.2  2000-07-13 11:32:57  michael
-  + removed logs
- 
-}

+ 4 - 3
fcl/db/interbase/mkdb

@@ -5,18 +5,19 @@
 # A database to connect to. (default 'testdb')
 #
 # Collect  the database
-DATABASE=testdb.gdb
+DATABASE=test.gdb
 # Choose one of the following:
 # ISQL=isql
-ISQL=/usr/interbase/bin/isql
+ISQL=/opt/interbase/bin/isql
 #
 # Don't edit after this.
 #
 echo -n "Creating and filling table FPdev in database $DATABASE..."
 # >/dev/null 2>&1
 ${ISQL} << EOF
+set sql dialect 3;
 CREATE DATABASE "$DATABASE";
-create table FPdev ( 
+create table FPDEV ( 
 id INT NOT NULL,
 UserName varchar(50),
 InstEmail CHAR(50),

+ 77 - 60
fcl/db/interbase/testib.pp

@@ -1,82 +1,99 @@
-// $Id$
+{   $Id$     
+    
+    Copyright (c) 2000 by Pavel Stingl
 
-// Test program for interbase.pp unit
+    Interbase testing program
+    
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
 
-program testib;
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
-uses Interbase,SysUtils,db;
+ **********************************************************************}
+
+program TestIB;
 
 {$linklib dl}
 {$linklib crypt}
 
-const
-  dbpath = 'testdb.gdb';
-  
+uses Interbase, SysUtils;
+
 var
-  DBS : TIBDatabase;
-  DS : TIBDataset;
-  x  : integer;
-  S  : TSystemTime;
+  Database : TIBDatabase;
+  Trans    : TIBTransaction;
+  Query    : TIBQuery;
+  x        : integer;
 
 begin
-  DBS := TIBDatabase.Create(nil);
-  DS := TIBDataset.Create(nil);
-  DS.Database := DBS;
-  DBS.DatabaseName := dbpath;
-  DBS.UserName := 'SYSDBA';
-  DBS.Password := 'masterkey';
-  WriteLn('Clearing ''John Doe'' entry from table');
-  DS.SQL.Add('delete from fpdev where username = ''John Doe''');
-  DS.Open;
-  DS.Close;
-  DS.sql.clear;
-  WriteLn('Inserting ''John Doe'' developer to fpdev table');
-  DS.SQL.Add('insert into fpdev values (9,''John Doe'',''[email protected]'')');
-  DS.Open;
-  DS.Close;
-  DS.sql.clear;
-  WriteLn('Making list from fpdev table');
-  DS.SQL.Add('select * from fpdev');
-  DS.Open;
-  while not DS.EOF do
+  Database := TIBDatabase.Create(nil);
+  Trans    := TIBTransaction.Create(nil);
+  Query    := TIBQuery.Create(nil);
+  
+  Database.DatabaseName := 'test.gdb';
+  Database.UserName     := 'sysdba';
+  Database.Password     := 'masterkey';
+  Database.Transaction  := Trans;
+  Trans.Action          := caRollback;
+  Trans.Active          := True;
+  
+  
+  Write('Opening database... Database.Connected = ');
+  Database.Open;
+  WriteLn(Database.Connected);
+  
+  // Assigning database to dataset
+  Query.Database := Database;
+  
+  Query.SQL.Add('select * from fpdev');
+  Query.Open;
+  
+  WriteLn;
+  
+  while not Query.EOF do
   begin
-    for x := 0 to DS.FieldCount - 2 do
-      Write(DS.Fields[x].AsString,',');
-    WriteLn(DS.Fields[DS.FieldCount-1].AsString);
-    DS.Next;
+    for x := 0 to Query.FieldCount - 2 do
+      Write(Query.Fields[x].AsString,',');
+    WriteLn(Query.Fields[Query.FieldCount - 1].AsString);
+    Query.Next;
   end;
   
-  DS.Close;
-  DS.SQL.Clear;
-  DS.Free;
-
   WriteLn;
-  WriteLn('Trying to perform test of datatypes interpretation...');
-  WriteLn('Some problems with TDateTimeField, see source');
-  DS := TIBDataset.Create(nil);
-  DS.Database := DBS;
-  DS.SQL.Add('select * from test');
-  DS.Open;
-  while not DS.EOF do
-  begin
-    { Warning - TDateTimeField.AsDateTime returns wrong values,
-      but conversions in TIBDataset are OK! }
-    for x := 0 to DS.FieldCount - 1 do
-      if (DS.Fields[x].DataType = ftDateTime) then
-        WriteLn(DS.Fields[x].FieldName, ' : "',
-          FormatDateTime('DD.MM.YYYY HH:MM:SS',DS.Fields[x].AsDateTime),'"')
-      else WriteLn(DS.Fields[x].FieldName, ' : "',DS.Fields[x].AsString,'"');
-    DS.Next;
+  
+  
+  try
+    WriteLn('Trying to insert new record to table fpdev');
+    Query.Close;
+    Query.SQL.Clear;
+    Query.SQL.Add('insert into fpdev values (''9'',''John Doe'',''[email protected]'')');
+    Query.ExecSQL;
+    Trans.CommitRetaining;
+    WriteLn('Insert succeeded.');
+  except
+    on E:Exception do
+    begin
+      WriteLn(E.Message);
+      WriteLn('Error when inserting record. Transaction rollback.');
+      Trans.RollbackRetaining;
+    end;
   end;
-  DS.Free;
-  DBS.EndTransaction;
-  DBS.Close;
-  DBS.Free;
+  
+  WriteLn;
+  
+  Trans.Commit;
+  
+  Write('Closing database... Database.Connected = ');
+  Database.Close;
+  WriteLn(Database.Connected);
 end.
 
 {
   $Log$
-  Revision 1.2  2000-07-13 11:32:57  michael
+  Revision 1.3  2000-12-02 15:21:47  michael
+  + Merged from the fixbranch
+
+  Revision 1.2  2000/07/13 11:32:57  michael
   + removed logs
  
 }