Browse Source

Merged revisions 9165-9166,9185,9211,9236-9238,9260,9262,9266,9269-9272,9276-9278,9295,9298-9299,9307-9308,9310,9322,9337,9340,9343-9344,9359,9373-9375,9387,9396,9399,9401-9402,9434,9450-9456,9459-9463,9466,9468-9469,9472-9473,9476-9477,9480,9491-9492,9501,9504,9515,9529,9536,9550,9566-9568,9571,9573,9576-9577,9579,9583-9584,9587,9610,9632-9637,9655-9656,9658,9660,9663,9692,9694-9695,9697-9714,9720,9722,9729,9732-9733,9740,9745,9749-9750,9753-9757,9759-9766,9768-9770,9772-9774,9787,9814,9822-9823,9825,9837-9850,9852,9855-9856,9863-9864,9867,9975,10006,10047,10082,10092,10127,10129-10130,10137-10138,10140-10146,10148-10153,10160-10161,10165,10168,10170,10172,10176-10178,10180,10183-10184,10187-10188,10191-10192,10200-10201,10203-10204,10206,10232,10234,10237-10239,10242 via svnmerge from
svn+ssh://[email protected]/FPC/svn/fpc/trunk

........
r9165 | joost | 2007-11-08 22:56:11 +0100 (Thu, 08 Nov 2007) | 1 line

* Playing around with double-linked indexes
........
r9166 | joost | 2007-11-08 22:56:45 +0100 (Thu, 08 Nov 2007) | 1 line

* Playing around with double-linked indexes, part II
........
r9298 | joost | 2007-11-19 23:35:35 +0100 (Mon, 19 Nov 2007) | 1 line

* Started adding an array-approach to the already existing double-linked list mechanism in TBufDataset. The list based on an array can be enabled with the ARRAYBUF define
........
r9299 | joost | 2007-11-19 23:36:27 +0100 (Mon, 19 Nov 2007) | 1 line

* Added tests which were usefull for r9298
........
r9501 | joost | 2007-12-21 23:34:55 +0100 (Fri, 21 Dec 2007) | 1 line

* Implemented some kind of AddIndex when ArrayBuf is defined
........
r9504 | joost | 2007-12-21 23:50:45 +0100 (Fri, 21 Dec 2007) | 1 line

* Moved implementation of IndexDefs from sqldb to bufdataset
........
r9515 | joost | 2007-12-22 15:40:49 +0100 (Sat, 22 Dec 2007) | 1 line

* Implemented TBufDataset.UpdateIndexDefs
........
r9584 | joost | 2007-12-30 15:58:22 +0100 (Sun, 30 Dec 2007) | 6 lines

* FCurrentIndex is now a PBufIndex instead of an integer
* The index-implementation now also uses TBufIndex when ArrayBuf is not defined
* UpdateIndexDefs is now protected and fixed an AV in it
* CompareText0 now compares strings right when len=0
* Implemented AddIndex
* Removed AddSecondIndex
........
r9610 | joost | 2008-01-01 23:02:31 +0100 (Tue, 01 Jan 2008) | 2 lines

* Changes to be able to add an index to an open dataset. Added FMaxIndexesCount.
* Added dummy-BuildIndex
........
r9660 | joost | 2008-01-06 23:02:05 +0100 (Sun, 06 Jan 2008) | 2 lines

* Implemented mergesort BuildIndex
* Added MaxIndexesCount property
........
r9663 | joost | 2008-01-07 00:27:21 +0100 (Mon, 07 Jan 2008) | 2 lines

* Added index-support for ftSmallInt, ftInteger, ftCurrency, ftBCD, ftWord, ftBoolean, ftFloat, ftDateTime, ftDate and ftTime fieldtypes
* Removed Length() from the inner loop when building indexes while opening a dataset
........
r9759 | joost | 2008-01-14 22:12:07 +0100 (Mon, 14 Jan 2008) | 2 lines

* Use MergeSort instead of InsertSort on opening database
* Always fetch all records when there are some indexes
........
r9787 | joost | 2008-01-19 01:15:02 +0100 (Sat, 19 Jan 2008) | 4 lines

* Write results to a testsuite-digest
* Fixed a range-overflow in a testvalue
* Set the db-engine to test from the command line
* Fixed compilation of old index-test
........
r9823 | joost | 2008-01-20 18:50:25 +0100 (Sun, 20 Jan 2008) | 5 lines

* Set DEFAULT_ORDER if no index-name is given
* Set the current record to the first record for all indexes when opening a dataset
* Fixed sorting for various fieldtypes
* Added dependency on unit strutils now StringsReplace is moved there
* Added several index-tests
........
r9975 | joost | 2008-01-26 22:02:31 +0100 (Sat, 26 Jan 2008) | 1 line

* Added TSQLQuery.ServerIndexDefs, the indexes on the server are now stored in this new property instead of IndexDefs which from now on only stores the local indexes
........
r10006 | joost | 2008-01-27 00:37:04 +0100 (Sun, 27 Jan 2008) | 2 lines

* Implemented TBufDataset.IndexFieldNames (+test)
* Fixed some db-error messages
........
r10047 | joost | 2008-01-27 15:13:58 +0100 (Sun, 27 Jan 2008) | 1 line

* Do not change the current record on an index change
........
r10092 | joost | 2008-01-28 23:45:04 +0100 (Mon, 28 Jan 2008) | 3 lines

* Added support for indexes based on more then one field (+test)
* Some refactoring
* Added test to test edits on indexed datasets
........
r10127 | joost | 2008-01-31 23:17:44 +0100 (Thu, 31 Jan 2008) | 2 lines

* Typo in DBCompareStruct
* Keep indexes valid upon updates
........
r10165 | joost | 2008-02-03 00:06:18 +0100 (Sun, 03 Feb 2008) | 1 line

* If the deleted record is the first record in the index, delete it properly
........
r10237 | joost | 2008-02-07 21:32:04 +0100 (Thu, 07 Feb 2008) | 1 line

* Fixed the deletion of records in indexed datasets
........
r10242 | joost | 2008-02-07 23:00:12 +0100 (Thu, 07 Feb 2008) | 1 line

* Refactored: extracted AddRecordToIndex
........

git-svn-id: branches/fixes_2_2@10253 -

joost 17 years ago
parent
commit
aac2a16f10

File diff suppressed because it is too large
+ 747 - 92
packages/fcl-db/src/base/bufdataset.pas


+ 2 - 2
packages/fcl-db/src/base/db.pas

@@ -936,7 +936,7 @@ type
     Function  GetItem(Index: Integer): TIndexDef;
     Procedure SetItem(Index: Integer; Value: TIndexDef);
   public
-    constructor Create(ADataSet: TDataSet); overload;
+    constructor Create(ADataSet: TDataSet); virtual; overload;
     destructor Destroy; override;
     procedure Add(const Name, Fields: string; Options: TIndexOptions);
     Function AddIndexDef: TIndexDef;
@@ -944,7 +944,7 @@ type
     function FindIndexForFields(const Fields: string): TIndexDef;
     function GetIndexForFields(const Fields: string;
       CaseInsensitive: Boolean): TIndexDef;
-    procedure Update; overload;
+    procedure Update; overload; virtual;
     Property Items[Index: Integer] : TIndexDef read GetItem write SetItem; default;
   end;
 

+ 5 - 0
packages/fcl-db/src/base/dbconst.pas

@@ -40,12 +40,14 @@ Resourcestring
   SErrIndexBasedOnUnkField = 'Index based on unknown field "%s".';
   SErrConnTransactionnSet  = 'Transaction of connection not set';
   SErrNotASQLConnection    = '"%s" is not a TSQLConnection';
+  SErrNotASQLQuery         = '"%s" is not a TCustomSQLQuery';
   STransNotActive          = 'Operation cannot be performed on an inactive transaction';
   STransActive             = 'Operation cannot be performed on an active transaction';
   SFieldNotFound           = 'Field not found : "%s"';
   SInactiveDataset         = 'Operation cannot be performed on an inactive dataset';
   SInvalidDisplayValues    = '"%s" are not valid boolean displayvalues';
   SInvalidFieldKind        = '%s : invalid field kind : ';
+  SInvalidBookmark         = 'Invalid bookmark';
   SInvalidFieldSize        = 'Invalid field size : %d';
   SInvalidTypeConversion   = 'Invalid type conversion to %s in field %s';
   SNeedField               = 'Field %s is required, but not supplied.';
@@ -90,12 +92,15 @@ Resourcestring
   SNoUpdateFields          = 'There are no fields found to include in the update- or insert-clause';
   SNotSupported            = 'Operation is not supported by this type of database';
   SDBCreateDropFailed      = 'Creation or dropping of database failed';
+  SMaxIndexes              = 'The maximum amount of indexes is reached';
+  SMinIndexes              = 'The minimum amount of indexes is 1';
 // These are added for Delphi-compatilility, but not used by the fcl:
   SFieldIndexError         = 'Field index out of range';
   SIndexFieldMissing       = 'Cannot access index field ''%s''';
   SNoFieldIndexes          = 'No index currently active';
   SNotIndexField           = 'Field ''%s'' is not indexed and cannot be modified';
   SErrUnknownConnectorType = 'Unknown connector type';
+  SNoIndexFieldNameGiven   = 'There are no fields selected to base the index on';
   SErrCircularDataSourceReferenceNotAllowed = 'Circular datasource references are not allowed.';
   
 

+ 0 - 1
packages/fcl-db/src/sqldb/interbase/ibconnection.pp

@@ -1031,7 +1031,6 @@ begin
               'ind.rdb$index_name;');
     open;
     end;
-  IndexDefs.Clear;
   while not qry.eof do with IndexDefs.AddIndexDef do
     begin
     Name := trim(qry.fields[0].asstring);

+ 0 - 1
packages/fcl-db/src/sqldb/mysql/mysqlconn.inc

@@ -844,7 +844,6 @@ begin
     sql.add('show index from ' +  TableName);
     open;
     end;
-  IndexDefs.Clear;
   while not qry.eof do with IndexDefs.AddIndexDef do
     begin
     Name := trim(qry.fieldbyname('Key_name').asstring);

+ 0 - 1
packages/fcl-db/src/sqldb/odbc/odbcconn.pas

@@ -1098,7 +1098,6 @@ begin
       ODBCCheckResult(SQLBindCol(StmtHandle, 10, SQL_C_CHAR  , @AscOrDesc , 1, @AscOrDescIndOrLen ), SQL_HANDLE_STMT, StmtHandle, 'Could not bind index metadata column ASC_OR_DESC.');
 
       // clear index defs
-      IndexDefs.Clear;
       IndexDef:=nil;
 
       // fetch result

+ 0 - 1
packages/fcl-db/src/sqldb/postgres/pqconnection.pp

@@ -803,7 +803,6 @@ begin
               'ic.relname;');
     open;
     end;
-  IndexDefs.Clear;
   while not qry.eof do with IndexDefs.AddIndexDef do
     begin
     Name := trim(qry.fields[0].asstring);

+ 50 - 19
packages/fcl-db/src/sqldb/sqldb.pp

@@ -60,6 +60,17 @@ const
                   'start','commit','rollback', '?'
                  );
 
+type
+
+  { TServerIndexDefs }
+
+  TServerIndexDefs = class(TIndexDefs)
+  Private
+  public
+    constructor Create(ADataSet: TDataSet); override;
+    procedure Update; override;
+  end;
+
 
 { TSQLConnection }
 type
@@ -179,7 +190,6 @@ type
     FDeleteSQL           : TStringList;
     FIsEOF               : boolean;
     FLoadingFieldDefs    : boolean;
-    FIndexDefs           : TIndexDefs;
     FReadOnly            : boolean;
     FUpdateMode          : TUpdateMode;
     FParams              : TParams;
@@ -193,13 +203,15 @@ type
 
     FServerFilterText    : string;
     FServerFiltered      : Boolean;
+    
+    FServerIndexDefs     : TServerIndexDefs;
 
     FUpdateQry,
     FDeleteQry,
     FInsertQry           : TCustomSQLQuery;
 
     procedure FreeFldBuffers;
-    function GetIndexDefs : TIndexDefs;
+    function GetServerIndexDefs: TServerIndexDefs;
     function GetStatementType : TStatementType;
     procedure SetReadOnly(AValue : Boolean);
     procedure SetParseSQL(AValue : Boolean);
@@ -216,7 +228,7 @@ type
     function Fetch : boolean; override;
     function LoadField(FieldDef : TFieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; override;
     // abstract & virtual methods of TDataset
-    procedure UpdateIndexDefs; override;
+    procedure UpdateServerIndexDefs; virtual;
     procedure SetDatabase(Value : TDatabase); override;
     Procedure SetTransaction(Value : TDBTransaction); override;
     procedure InternalAddRecord(Buffer: Pointer; AAppend: Boolean); override;
@@ -281,7 +293,6 @@ type
     property UpdateSQL : TStringlist read FUpdateSQL write FUpdateSQL;
     property InsertSQL : TStringlist read FInsertSQL write FInsertSQL;
     property DeleteSQL : TStringlist read FDeleteSQL write FDeleteSQL;
-    property IndexDefs : TIndexDefs read GetIndexDefs;
     property Params : TParams read FParams write FParams;
     property UpdateMode : TUpdateMode read FUpdateMode write SetUpdateMode;
     property UsePrimaryKeyAsKey : boolean read FUsePrimaryKeyAsKey write SetUsePrimaryKeyAsKey;
@@ -290,6 +301,7 @@ type
     Property DataSource : TDatasource Read GetDataSource Write SetDatasource;
     property ServerFilter: string read FServerFilterText write SetServerFilterText;
     property ServerFiltered: Boolean read FServerFiltered write SetServerFiltered default False;
+    property ServerIndexDefs : TServerIndexDefs read GetServerIndexDefs;
   end;
 
 { TSQLQuery }
@@ -339,6 +351,7 @@ type
     Property DataSource;
     property ServerFilter;
     property ServerFiltered;
+    property ServerIndexDefs;
   end;
 
 { TSQLScript }
@@ -907,6 +920,11 @@ begin
   if assigned(FCursor) then TSQLConnection(Database).FreeFldBuffers(FCursor);
 end;
 
+function TCustomSQLQuery.GetServerIndexDefs: TServerIndexDefs;
+begin
+  Result := FServerIndexDefs;
+end;
+
 function TCustomSQLQuery.Fetch : boolean;
 begin
   if not (Fcursor.FStatementType in [stSelect]) then
@@ -1153,14 +1171,13 @@ begin
           begin
           if FusePrimaryKeyAsKey then
             begin
-            UpdateIndexDefs;
-            for tel := 0 to indexdefs.count-1 do {with indexdefs[tel] do}
+            UpdateServerIndexDefs;
+            for tel := 0 to ServerIndexDefs.count-1 do
               begin
-              if ixPrimary in indexdefs[tel].options then
+              if ixPrimary in ServerIndexDefs[tel].options then
                 begin
-                // Todo: If there is more then one field in the key, that must be parsed
                   IndexFields := TStringList.Create;
-                  ExtractStrings([';'],[' '],pchar(indexdefs[tel].fields),IndexFields);
+                  ExtractStrings([';'],[' '],pchar(ServerIndexDefs[tel].fields),IndexFields);
                   for fieldc := 0 to IndexFields.Count-1 do
                     begin
                     F := Findfield(IndexFields[fieldc]);
@@ -1219,7 +1236,8 @@ begin
   FDeleteSQL := TStringList.Create;
   FDeleteSQL.OnChange := @OnChangeModifySQL;
 
-  FIndexDefs := TIndexDefs.Create(Self);
+  FServerIndexDefs := TServerIndexDefs.Create(Self);
+
   FReadOnly := false;
   FParseSQL := True;
   
@@ -1243,7 +1261,7 @@ begin
   FreeAndNil(FInsertSQL);
   FreeAndNil(FDeleteSQL);
   FreeAndNil(FUpdateSQL);
-  FreeAndNil(FIndexDefs);
+  FServerIndexDefs.Free;
   inherited Destroy;
 end;
 
@@ -1284,11 +1302,12 @@ begin
     end;
 end;
 
-Procedure TCustomSQLQuery.UpdateIndexDefs;
+Procedure TCustomSQLQuery.UpdateServerIndexDefs;
 
 begin
+  FServerIndexDefs.Clear;
   if assigned(DataBase) and (FTableName<>'') then
-    TSQLConnection(DataBase).UpdateIndexDefs(FIndexDefs,FTableName);
+    TSQLConnection(DataBase).UpdateIndexDefs(ServerIndexDefs,FTableName);
 end;
 
 Procedure TCustomSQLQuery.ApplyRecUpdate(UpdateKind : TUpdateKind);
@@ -1419,12 +1438,6 @@ begin
     Result := False;
 end;
 
-function TCustomSQLQuery.GetIndexDefs : TIndexDefs;
-
-begin
-  Result := FIndexDefs;
-end;
-
 procedure TCustomSQLQuery.SetUpdateMode(AValue : TUpdateMode);
 
 begin
@@ -1902,6 +1915,24 @@ begin
   AConnection.Params.Assign(Params);
 end;
 
+{ TServerIndexDefs }
+
+constructor TServerIndexDefs.create(ADataset: TDataset);
+begin
+  if not (ADataset is TCustomSQLQuery) then
+    DatabaseErrorFmt(SErrNotASQLQuery,[ADataset.Name]);
+  inherited create(ADataset);
+end;
+
+procedure TServerIndexDefs.Update;
+begin
+  if (not updated) and assigned(Dataset) then
+    begin
+    TCustomSQLQuery(Dataset).UpdateServerIndexDefs;
+    updated := True;
+    end;
+end;
+
 Initialization
 
 Finalization

+ 698 - 0
packages/fcl-db/tests/testdbbasics.pas

@@ -19,14 +19,48 @@ type
     procedure TestOnFilterProc(DataSet: TDataSet; var Accept: Boolean);
     procedure TestfieldDefinition(AFieldType : TFieldType;ADatasize : integer;var ADS : TDataset; var AFld: TField);
     procedure TestcalculatedField_OnCalcfields(DataSet: TDataSet);
+
+    procedure FTestDelete1(TestCancelUpdate : boolean);
+    procedure FTestDelete2(TestCancelUpdate : boolean);
+    procedure TestAddIndexFieldType(AFieldType : TFieldType; ActiveDS : boolean);
   protected
     procedure SetUp; override;
     procedure TearDown; override;
   published
+    procedure TestCancelUpdDelete1;
+    procedure TestCancelUpdDelete2;
+    procedure TestBookmarks;
+
+    procedure TestFirst;
+    procedure TestDelete1;
+    procedure TestDelete2;
     procedure TestIntFilter;
     procedure TestOnFilter;
     procedure TestStringFilter;
 
+    procedure TestAddIndex;
+    procedure TestInactSwitchIndex;
+
+    procedure TestAddIndexInteger;
+    procedure TestAddIndexSmallInt;
+    procedure TestAddIndexBoolean;
+    procedure TestAddIndexFloat;
+    procedure TestAddIndexLargeInt;
+    procedure TestAddIndexDateTime;
+    procedure TestAddIndexCurrency;
+    procedure TestAddIndexBCD;
+
+    procedure TestAddIndexActiveDS;
+    procedure TestAddIndexEditDS;
+
+    procedure TestIndexFieldNames;
+    procedure TestIndexFieldNamesAct;
+    
+    procedure TestIndexCurRecord;
+
+    procedure TestAddDblIndex;
+    procedure TestIndexEditRecord;
+
     procedure TestNullAtOpen;
 
     procedure TestSupportIntegerFields;
@@ -453,6 +487,237 @@ begin
   DBConnector.StopTest;
 end;
 
+procedure TTestDBBasics.TestBookmarks;
+var BM1,BM2,BM3,BM4,BM5 : TBookmark;
+begin
+  with DBConnector.GetNDataset(true,14) do
+    begin
+    AssertNull(GetBookmark);
+    open;
+    BM1:=GetBookmark; // id=1, BOF
+    next;next;
+    BM2:=GetBookmark; // id=3
+    next;next;next;
+    BM3:=GetBookmark; // id=6
+    next;next;next;next;next;next;next;next;
+    BM4:=GetBookmark; // id=14
+    next;
+    BM5:=GetBookmark; // id=14, EOF
+    
+    GotoBookmark(BM2);
+    AssertEquals(3,FieldByName('id').AsInteger);
+
+    GotoBookmark(BM1);
+    AssertEquals(1,FieldByName('id').AsInteger);
+
+    GotoBookmark(BM3);
+    AssertEquals(6,FieldByName('id').AsInteger);
+
+    GotoBookmark(BM4);
+    AssertEquals(14,FieldByName('id').AsInteger);
+
+    GotoBookmark(BM3);
+    AssertEquals(6,FieldByName('id').AsInteger);
+
+    GotoBookmark(BM5);
+    AssertEquals(14,FieldByName('id').AsInteger);
+
+    GotoBookmark(BM1);
+    AssertEquals(1,FieldByName('id').AsInteger);
+
+    next;
+    delete;
+
+    GotoBookmark(BM2);
+    AssertEquals(3,FieldByName('id').AsInteger);
+    
+    delete;delete;
+
+    GotoBookmark(BM3);
+    AssertEquals(6,FieldByName('id').AsInteger);
+
+    GotoBookmark(BM1);
+    AssertEquals(1,FieldByName('id').AsInteger);
+    insert;
+    fieldbyname('id').AsInteger:=20;
+    insert;
+    fieldbyname('id').AsInteger:=21;
+    insert;
+    fieldbyname('id').AsInteger:=22;
+    insert;
+    fieldbyname('id').AsInteger:=23;
+    post;
+    
+    GotoBookmark(BM3);
+    AssertEquals(6,FieldByName('id').AsInteger);
+
+    GotoBookmark(BM1);
+    AssertEquals(1,FieldByName('id').AsInteger);
+
+    GotoBookmark(BM5);
+    AssertEquals(14,FieldByName('id').AsInteger);
+    end;
+end;
+
+procedure TTestDBBasics.TestFirst;
+var i : integer;
+begin
+  with DBConnector.GetNDataset(true,14) do
+    begin
+    open;
+    AssertEquals(1,FieldByName('ID').AsInteger);
+    First;
+    AssertEquals(1,FieldByName('ID').AsInteger);
+    next;
+    AssertEquals(2,FieldByName('ID').AsInteger);
+    First;
+    AssertEquals(1,FieldByName('ID').AsInteger);
+    for i := 0 to 12 do
+      next;
+    AssertEquals(14,FieldByName('ID').AsInteger);
+    First;
+    AssertEquals(1,FieldByName('ID').AsInteger);
+    close;
+    end;
+end;
+
+procedure TTestDBBasics.TestDelete1;
+begin
+  FTestDelete1(false);
+end;
+
+procedure TTestDBBasics.TestDelete2;
+begin
+  FTestDelete2(false);
+end;
+
+procedure TTestDBBasics.TestCancelUpdDelete1;
+begin
+  FTestDelete1(true);
+end;
+
+procedure TTestDBBasics.TestCancelUpdDelete2;
+begin
+  FTestDelete2(true);
+end;
+
+procedure TTestDBBasics.FTestDelete1(TestCancelUpdate : boolean);
+// Test the deletion of records, including the first and the last one
+var i  : integer;
+    ds : TDataset;
+begin
+  ds := DBConnector.GetNDataset(true,17);
+  with ds do
+    begin
+    Open;
+
+    for i := 0 to 16 do if i mod 4=0 then
+      delete
+    else
+       next;
+
+    First;
+    for i := 0 to 16 do
+      begin
+      if i mod 4<>0 then
+        begin
+        AssertEquals(i+1,FieldByName('ID').AsInteger);
+        AssertEquals('TestName'+inttostr(i+1),FieldByName('NAME').AsString);
+        next;
+        end;
+      end;
+    end;
+    
+  if TestCancelUpdate then
+    begin
+    if not (ds is TBufDataset) then
+      Ignore('This test only applies to TBufDataset and descendents.');
+    with TBufDataset(ds) do
+      begin
+      CancelUpdates;
+
+      First;
+      for i := 0 to 16 do
+        begin
+        AssertEquals(i+1,FieldByName('ID').AsInteger);
+        AssertEquals('TestName'+inttostr(i+1),FieldByName('NAME').AsString);
+        next;
+        end;
+
+      close;
+      end;
+    end;
+end;
+
+procedure TTestDBBasics.FTestDelete2(TestCancelUpdate : boolean);
+// Test the deletion of edited and appended records
+var i : integer;
+    ds : TDataset;
+begin
+  ds := DBConnector.GetNDataset(true,17);
+  with ds do
+    begin
+    Open;
+
+    for i := 0 to 16 do
+      begin
+      if i mod 4=0 then
+        begin
+        edit;
+        fieldbyname('name').AsString:='this record will be gone soon';
+        post;
+        end;
+      next;
+      end;
+
+    for i := 17 to 20 do
+      begin
+      append;
+      fieldbyname('id').AsInteger:=i+1;
+      fieldbyname('name').AsString:='TestName'+inttostr(i+1);
+      post;
+      end;
+
+    first;
+    for i := 0 to 20 do if i mod 4=0 then
+      delete
+    else
+       next;
+
+    First;
+    i := 0;
+    for i := 0 to 20 do
+      begin
+      if i mod 4<>0 then
+        begin
+        AssertEquals(i+1,FieldByName('ID').AsInteger);
+        AssertEquals('TestName'+inttostr(i+1),FieldByName('NAME').AsString);
+        next;
+        end;
+      end;
+    end;
+
+  if TestCancelUpdate then
+    begin
+    if not (ds is TBufDataset) then
+      Ignore('This test only applies to TBufDataset and descendents.');
+    with TBufDataset(ds) do
+      begin
+      CancelUpdates;
+
+      First;
+      for i := 0 to 16 do
+        begin
+        AssertEquals(i+1,FieldByName('ID').AsInteger);
+        AssertEquals('TestName'+inttostr(i+1),FieldByName('NAME').AsString);
+        next;
+        end;
+
+      close;
+      end;
+    end;
+end;
+
 procedure TTestDBBasics.TestOnFilterProc(DataSet: TDataSet; var Accept: Boolean);
 
 var a : TDataSetState;
@@ -540,6 +805,439 @@ begin
     end;
 end;
 
+procedure TTestDBBasics.TestAddIndexFieldType(AFieldType: TFieldType; ActiveDS : boolean);
+var ds : TBufDataset;
+    FList : TStringList;
+    LastValue : Variant;
+begin
+  ds := DBConnector.GetFieldDataset as TBufDataset;
+  with ds do
+    begin
+    
+    if not ActiveDS then
+      begin
+      AddIndex('testindex','F'+FieldTypeNames[AfieldType]);
+      IndexName:='testindex';
+      end
+    else
+      MaxIndexesCount := 3;
+
+    try
+      open;
+    except
+      if not assigned(ds.FindField('F'+FieldTypeNames[AfieldType])) then
+        Ignore('Fields of the type ' + FieldTypeNames[AfieldType] + ' are not supported by this type of dataset')
+      else
+        raise;
+    end;
+
+    if ActiveDS then
+      begin
+      if not assigned(ds.FindField('F'+FieldTypeNames[AfieldType])) then
+        Ignore('Fields of the type ' + FieldTypeNames[AfieldType] + ' are not supported by this type of dataset');
+      AddIndex('testindex','F'+FieldTypeNames[AfieldType]);
+      IndexName:='testindex';
+      First;
+      end;
+
+    LastValue:=null;
+    while not eof do
+      begin
+      AssertTrue(LastValue<=FieldByName('F'+FieldTypeNames[AfieldType]).AsVariant);
+      LastValue:=FieldByName('F'+FieldTypeNames[AfieldType]).AsVariant;
+      Next;
+      end;
+
+    while not bof do
+      begin
+      AssertTrue(LastValue>=FieldByName('F'+FieldTypeNames[AfieldType]).AsVariant);
+      LastValue:=FieldByName('F'+FieldTypeNames[AfieldType]).AsVariant;
+      Prior;
+      end;
+    end;
+end;
+
+procedure TTestDBBasics.TestAddIndexSmallInt;
+begin
+  TestAddIndexFieldType(ftSmallint,False);
+end;
+
+procedure TTestDBBasics.TestAddIndexBoolean;
+begin
+  TestAddIndexFieldType(ftBoolean,False);
+end;
+
+procedure TTestDBBasics.TestAddIndexFloat;
+begin
+  TestAddIndexFieldType(ftFloat,False);
+end;
+
+procedure TTestDBBasics.TestAddIndexInteger;
+begin
+  TestAddIndexFieldType(ftInteger,False);
+end;
+
+procedure TTestDBBasics.TestAddIndexLargeInt;
+begin
+  TestAddIndexFieldType(ftLargeint,False);
+end;
+
+procedure TTestDBBasics.TestAddIndexDateTime;
+begin
+  TestAddIndexFieldType(ftDateTime,False);
+end;
+
+procedure TTestDBBasics.TestAddIndexCurrency;
+begin
+  TestAddIndexFieldType(ftCurrency,False);
+end;
+
+procedure TTestDBBasics.TestAddIndexBCD;
+begin
+  TestAddIndexFieldType(ftBCD,False);
+end;
+
+procedure TTestDBBasics.TestAddIndex;
+var ds : TBufDataset;
+    AFieldType : TFieldType;
+    FList : TStringList;
+    i : integer;
+begin
+  ds := DBConnector.GetFieldDataset as TBufDataset;
+  with ds do
+    begin
+
+    AFieldType:=ftString;
+    AddIndex('testindex','F'+FieldTypeNames[AfieldType]);
+    FList := TStringList.Create;
+    FList.Sorted:=true;
+    FList.CaseSensitive:=True;
+    FList.Duplicates:=dupAccept;
+    open;
+
+    while not eof do
+      begin
+      flist.Add(FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
+      Next;
+      end;
+
+    IndexName:='testindex';
+    first;
+    i:=0;
+
+    while not eof do
+      begin
+      AssertEquals(flist[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
+      inc(i);
+      Next;
+      end;
+
+    while not bof do
+      begin
+      dec(i);
+      AssertEquals(flist[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
+      Prior;
+      end;
+    end;
+end;
+
+procedure TTestDBBasics.TestInactSwitchIndex;
+// Test if the default-index is properly build when the active index is not
+// the default-index while opening then dataset
+var ds : TBufDataset;
+    AFieldType : TFieldType;
+    i : integer;
+begin
+  ds := DBConnector.GetFieldDataset as TBufDataset;
+  with ds do
+    begin
+
+    AFieldType:=ftString;
+    AddIndex('testindex','F'+FieldTypeNames[AfieldType]);
+    IndexName:='testindex';
+    open;
+    IndexName:=''; // This should set the default index (default_order)
+    first;
+    
+    i := 0;
+
+    while not eof do
+      begin
+      AssertEquals(testStringValues[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
+      inc(i);
+      Next;
+      end;
+    end;
+end;
+
+procedure TTestDBBasics.TestAddIndexActiveDS;
+var ds   : TBufDataset;
+    I    : integer;
+begin
+  TestAddIndexFieldType(ftString,true);
+end;
+
+procedure TTestDBBasics.TestAddIndexEditDS;
+var ds        : TBufDataset;
+    I         : integer;
+    LastValue : String;
+begin
+  ds := DBConnector.GetNDataset(True,5) as TBufDataset;
+  with ds do
+    begin
+    MaxIndexesCount:=3;
+    open;
+    edit;
+    FieldByName('name').asstring := 'Zz';
+    post;
+    next;
+    next;
+    edit;
+    FieldByName('name').asstring := 'aA';
+    post;
+
+    AddIndex('test','name');
+
+    first;
+    ds.IndexName:='test';
+    first;
+    LastValue:=FieldByName('name').AsString;
+    while not eof do
+      begin
+      AssertTrue(LastValue<=FieldByName('name').AsString);
+      Next;
+      end;
+    end;
+end;
+
+procedure TTestDBBasics.TestIndexFieldNamesAct;
+var ds : TBufDataset;
+    AFieldType : TFieldType;
+    FList : TStringList;
+    i : integer;
+begin
+  ds := DBConnector.GetFieldDataset as TBufDataset;
+  with ds do
+    begin
+    AFieldType:=ftString;
+    FList := TStringList.Create;
+    FList.Sorted:=true;
+    FList.CaseSensitive:=True;
+    FList.Duplicates:=dupAccept;
+    open;
+
+    while not eof do
+      begin
+      flist.Add(FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
+      Next;
+      end;
+
+    IndexFieldNames:='F'+FieldTypeNames[AfieldType];
+    first;
+    i:=0;
+
+    while not eof do
+      begin
+      AssertEquals(flist[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
+      inc(i);
+      Next;
+      end;
+
+    while not bof do
+      begin
+      dec(i);
+      AssertEquals(flist[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
+      Prior;
+      end;
+
+    AssertEquals('F'+FieldTypeNames[AfieldType],IndexFieldNames);
+
+    IndexFieldNames:='ID';
+    first;
+    i:=0;
+
+    while not eof do
+      begin
+      AssertEquals(testStringValues[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
+      inc(i);
+      Next;
+      end;
+
+    AssertEquals('ID',IndexFieldNames);
+
+    IndexFieldNames:='';
+    first;
+    i:=0;
+
+    while not eof do
+      begin
+      AssertEquals(testStringValues[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
+      inc(i);
+      Next;
+      end;
+
+    AssertEquals('',IndexFieldNames);
+
+    end;
+end;
+
+procedure TTestDBBasics.TestIndexCurRecord;
+// Test if the currentrecord stays the same after an index change
+var ds : TBufDataset;
+    AFieldType : TFieldType;
+    i : integer;
+    OldID : Integer;
+    OldStringValue : string;
+begin
+  ds := DBConnector.GetFieldDataset as TBufDataset;
+  with ds do
+    begin
+    AFieldType:=ftString;
+    AddIndex('testindex','F'+FieldTypeNames[AfieldType]);
+    open;
+
+    for i := 0 to (testValuesCount div 3) do
+      Next;
+
+    OldID:=FieldByName('id').AsInteger;
+    OldStringValue:=FieldByName('F'+FieldTypeNames[AfieldType]).AsString;
+
+    IndexName:='testindex';
+
+    AssertEquals(OldID,FieldByName('id').AsInteger);
+    AssertEquals(OldStringValue,FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
+
+    next;
+    AssertTrue(OldStringValue<=FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
+    prior;
+    prior;
+    AssertTrue(OldStringValue>=FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
+
+    OldID:=FieldByName('id').AsInteger;
+    OldStringValue:=FieldByName('F'+FieldTypeNames[AfieldType]).AsString;
+
+    IndexName:='';
+
+    AssertEquals(OldID,FieldByName('id').AsInteger);
+    AssertEquals(OldStringValue,FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
+    
+    next;
+    AssertEquals(OldID+1,FieldByName('ID').AsInteger);
+    prior;
+    prior;
+    AssertEquals(OldID-1,FieldByName('ID').AsInteger);
+    end;
+end;
+
+procedure TTestDBBasics.TestAddDblIndex;
+var ds : TBufDataset;
+    FList : TStringList;
+    i : integer;
+begin
+  ds := DBConnector.GetFieldDataset as TBufDataset;
+  with ds do
+    begin
+
+    AddIndex('testindex','F'+FieldTypeNames[ftString]+', F'+FieldTypeNames[ftInteger]);
+    FList := TStringList.Create;
+    FList.Sorted:=true;
+    FList.CaseSensitive:=True;
+    FList.Duplicates:=dupAccept;
+    open;
+
+    while not eof do
+      begin
+      // If the first field of the index is null then the compound string in
+      // FList isn't sorted right...
+      if FieldByName('F'+FieldTypeNames[ftString]).IsNull then
+        flist.Add('         -'+ Format('%.12d',[FieldByName('F'+FieldTypeNames[ftInteger]).AsInteger]))
+      else
+        flist.Add(FieldByName('F'+FieldTypeNames[ftString]).AsString+'-'+ Format('%.12d',[FieldByName('F'+FieldTypeNames[ftInteger]).AsInteger]));
+      Next;
+      end;
+
+    IndexName:='testindex';
+    first;
+    i:=0;
+
+    while not eof do
+      begin
+      if (not FieldByName('F'+FieldTypeNames[ftString]).IsNull) then
+        AssertEquals(flist[i],FieldByName('F'+FieldTypeNames[ftString]).AsString+'-'+ Format('%.12d',[FieldByName('F'+FieldTypeNames[ftInteger]).AsInteger]));
+      inc(i);
+      Next;
+      end;
+    while not bof do
+      begin
+      dec(i);
+      if not FieldByName('F'+FieldTypeNames[ftString]).IsNull then
+        AssertEquals(flist[i],FieldByName('F'+FieldTypeNames[ftString]).AsString+'-'+ Format('%.12d',[FieldByName('F'+FieldTypeNames[ftInteger]).AsInteger]));
+      Prior;
+      end;
+    end;
+end;
+
+procedure TTestDBBasics.TestIndexEditRecord;
+var ds : TBufDataset;
+    AFieldType : TFieldType;
+    i : integer;
+    OldID : Integer;
+    OldStringValue : string;
+begin
+  ds := DBConnector.GetFieldDataset as TBufDataset;
+  with ds do
+    begin
+    AFieldType:=ftString;
+    AddIndex('testindex','F'+FieldTypeNames[AfieldType]);
+    IndexName:='testindex';
+    open;
+    OldStringValue:=FieldByName('F'+FieldTypeNames[AfieldType]).AsString;
+    next;
+    AssertTrue(OldStringValue<=FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
+    OldStringValue:=FieldByName('F'+FieldTypeNames[AfieldType]).AsString;
+    next;
+    AssertTrue(OldStringValue<=FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
+    prior;
+    
+    edit;
+    FieldByName('F'+FieldTypeNames[AfieldType]).AsString := 'ZZZ';
+    post;
+    prior;
+    AssertTrue('ZZZ'>=FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
+    next;
+    next;
+    AssertTrue('ZZZ'<=FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
+    close;
+    end;
+end;
+
+procedure TTestDBBasics.TestIndexFieldNames;
+var ds : TBufDataset;
+    AFieldType : TFieldType;
+    PrevValue : String;
+begin
+  ds := DBConnector.GetFieldDataset as TBufDataset;
+  with ds do
+    begin
+    AFieldType:=ftString;
+    
+    IndexFieldNames:='F'+FieldTypeNames[AfieldType];
+
+    open;
+    PrevValue:='';
+    while not eof do
+      begin
+      AssertTrue(FieldByName('F'+FieldTypeNames[AfieldType]).AsString>=PrevValue);
+      PrevValue:=FieldByName('F'+FieldTypeNames[AfieldType]).AsString;
+      Next;
+      end;
+
+    AssertEquals('F'+FieldTypeNames[AfieldType],IndexFieldNames);
+
+    end;
+end;
+
+
 procedure TTestDBBasics.TestcalculatedField_OnCalcfields(DataSet: TDataSet);
 begin
   case dataset.fieldbyname('ID').asinteger of

+ 1 - 1
packages/fcl-db/tests/testsqlfieldtypes.pas

@@ -74,7 +74,7 @@ type
 
 implementation
 
-uses sqldbtoolsunit,toolsunit, variants, sqldb, bufdataset;
+uses sqldbtoolsunit,toolsunit, variants, sqldb, bufdataset, strutils;
 
 Type HackedDataset = class(TDataset);
 

+ 6 - 2
packages/fcl-db/tests/toolsunit.pas

@@ -83,7 +83,7 @@ const
   testCurrencyValues : Array[0..testValuesCount-1] of currency = (-100,-65.5,-54.34,-43.34,-2.50,-0.2,45.40,0.3,45.4,127,128,255,256,45,0.3,45.4,127,128,255,256,45,1234.56,43.23,43.43,99.88);
   testIntValues : Array[0..testValuesCount-1] of integer = (-maxInt,-maxInt+1,-maxSmallint-1,-maxSmallint,-256,-255,-128,-127,-1,0,1,127,128,255,256,maxSmallint,maxSmallint+1,MaxInt-1,MaxInt,100,130,150,-150,-132,234);
   testSmallIntValues : Array[0..testValuesCount-1] of smallint = (-maxSmallint,-maxSmallint+1,-256,-255,-128,-127,-1,0,1,127,128,255,256,maxSmallint,maxSmallint-1,100,110,120,130,150,-150,-132,234,231,42);
-  testLargeIntValues : Array[0..testValuesCount-1] of smallint = (-MaxSIntValue,MaxSIntValue+1,-maxInt-1,-maxInt+1,-maxSmallint,-maxSmallint+1,-256,-255,-128,-127,-1,0,1,127,128,255,256,maxSmallint,maxSmallint-1,maxSmallint+1,MaxInt-1,MaxInt,MaxSIntValue-1,MaxSIntValue,235253244);
+  testLargeIntValues : Array[0..testValuesCount-1] of smallint = (-MaxSIntValue,-MaxSIntValue+1,-maxInt-1,-maxInt+1,-maxSmallint,-maxSmallint+1,-256,-255,-128,-127,-1,0,1,127,128,255,256,maxSmallint,maxSmallint-1,maxSmallint+1,MaxInt-1,MaxInt,MaxSIntValue-1,MaxSIntValue,235253244);
   testBooleanValues : Array[0..testValuesCount-1] of boolean = (true,false,false,true,true,false,false,true,false,true,true,true,false,false,false,false,true,true,true,true,false,true,true,false,false);
   testStringValues : Array[0..testValuesCount-1] of string = (
     '',
@@ -207,7 +207,11 @@ var IniFile : TIniFile;
 
 begin
   IniFile := TIniFile.Create(getcurrentdir + PathDelim + 'database.ini');
-  dbtype := IniFile.ReadString('Database','Type','');
+  dbtype:='';
+  if Paramcount>0 then
+    dbtype := ParamStr(1);
+  if (dbtype='') or not inifile.SectionExists(dbtype) then
+    dbtype := IniFile.ReadString('Database','Type','');
   dbconnectorname := IniFile.ReadString(dbtype,'Connector','');
   dbname := IniFile.ReadString(dbtype,'Name','');
   dbuser := IniFile.ReadString(dbtype,'User','');

Some files were not shown because too many files changed in this diff