mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-06-06 10:18:25 +02:00
3309 lines
85 KiB
ObjectPascal
3309 lines
85 KiB
ObjectPascal
unit TestDBBasics;
|
|
|
|
{$IFDEF FPC}
|
|
{$mode Delphi}{$H+}
|
|
{$ENDIF}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF FPC}
|
|
testregistry,
|
|
{$ELSE FPC}
|
|
TestFramework,
|
|
{$ENDIF FPC}
|
|
Classes, SysUtils, db, ToolsUnit;
|
|
|
|
type
|
|
|
|
{ TTestDBBasics }
|
|
|
|
TTestDBBasics = class(TDBBasicsTestCase)
|
|
private
|
|
procedure TestFieldDefinition(AFieldType: TFieldType; ADataSize: integer); overload;
|
|
procedure TestFieldDefinition(AFieldType : TFieldType; ADataSize : integer; out ADS : TDataset; out AFld : TField); overload;
|
|
procedure TestFieldDefinition(AFld: TField; AFieldType : TFieldType; ADataSize : integer); overload;
|
|
procedure TestCalculatedField_OnCalcfields(DataSet: TDataSet);
|
|
|
|
published
|
|
// fields
|
|
procedure TestSetFieldValues;
|
|
procedure TestGetFieldValues;
|
|
procedure TestClearFields;
|
|
|
|
procedure TestSupportIntegerFields;
|
|
procedure TestSupportSmallIntFields;
|
|
procedure TestSupportWordFields;
|
|
procedure TestSupportStringFields;
|
|
procedure TestSupportBooleanFields;
|
|
procedure TestSupportBooleanFieldDisplayValue;
|
|
procedure TestSupportFloatFields;
|
|
procedure TestSupportLargeIntFields;
|
|
procedure TestSupportDateFields;
|
|
procedure TestSupportTimeFields;
|
|
procedure TestSupportDateTimeFields;
|
|
procedure TestSupportCurrencyFields;
|
|
procedure TestSupportBCDFields;
|
|
procedure TestSupportFmtBCDFields;
|
|
procedure TestSupportFixedStringFields;
|
|
procedure TestSupportBlobFields;
|
|
procedure TestSupportMemoFields;
|
|
procedure TestSupportByteFields;
|
|
procedure TestSupportShortIntFields;
|
|
procedure TestSupportExtendedFields;
|
|
procedure TestSupportSingleFields;
|
|
|
|
procedure TestBlobBlobType; //bug 26064
|
|
|
|
procedure TestCalculatedField;
|
|
procedure TestCanModifySpecialFields;
|
|
|
|
// dataset
|
|
procedure TestDoubleClose;
|
|
procedure TestFieldDefsUpdate;
|
|
procedure TestAssignFieldftString;
|
|
procedure TestAssignFieldftFixedChar;
|
|
procedure TestSelectQueryBasics;
|
|
procedure TestPostOnlyInEditState;
|
|
procedure TestMove; // bug 5048
|
|
procedure TestActiveBufferWhenClosed;
|
|
procedure TestEOFBOFClosedDataset;
|
|
procedure TestRecordcountAfterReopen; // partly bug 8228
|
|
procedure TestExceptionLocateClosed; // bug 13938
|
|
procedure TestDetectionNonMatchingDataset;
|
|
// events
|
|
procedure TestLayoutChangedEvents;
|
|
procedure TestDataEventsResync;
|
|
procedure TestdeFieldListChange;
|
|
end;
|
|
|
|
{ TTestBufDatasetDBBasics }
|
|
{$ifdef fpc}
|
|
TTestBufDatasetDBBasics = class(TDBBasicsTestCase)
|
|
private
|
|
procedure FTestXMLDatasetDefinition(ADataset : TDataset);
|
|
procedure TestAddIndexFieldType(AFieldType : TFieldType; ActiveDS : boolean);
|
|
published
|
|
procedure TestFileNameProperty;
|
|
procedure TestClientDatasetAsMemDataset;
|
|
procedure TestSaveAsXML;
|
|
procedure TestIsEmpty;
|
|
procedure TestReadOnly;
|
|
// cached updates
|
|
procedure TestBufDatasetCancelUpd; //bug 6938
|
|
procedure TestBufDatasetCancelUpd1;
|
|
procedure TestMultipleDeleteUpdateBuffer;
|
|
procedure TestDoubleDelete;
|
|
procedure TestMergeChangeLog;
|
|
procedure TestRevertRecord;
|
|
// index tests
|
|
procedure TestAddIndexInteger;
|
|
procedure TestAddIndexSmallInt;
|
|
procedure TestAddIndexBoolean;
|
|
procedure TestAddIndexFloat;
|
|
procedure TestAddIndexLargeInt;
|
|
procedure TestAddIndexDateTime;
|
|
procedure TestAddIndexCurrency;
|
|
procedure TestAddIndexBCD;
|
|
procedure TestAddIndexFmtBCD;
|
|
|
|
procedure TestAddIndex;
|
|
procedure TestAddDescIndex;
|
|
procedure TestAddCaseInsIndex;
|
|
procedure TestInactSwitchIndex;
|
|
|
|
procedure TestAddIndexActiveDS;
|
|
procedure TestAddIndexEditDS;
|
|
|
|
procedure TestIndexFieldNames;
|
|
procedure TestIndexFieldNamesActive;
|
|
procedure TestIndexFieldNamesClosed; // bug 16695
|
|
|
|
procedure TestIndexCurRecord;
|
|
|
|
procedure TestAddDblIndex;
|
|
procedure TestIndexEditRecord;
|
|
procedure TestIndexAppendRecord;
|
|
end;
|
|
{$endif fpc}
|
|
|
|
TTestUniDirectionalDBBasics = class(TTestDBBasics)
|
|
end;
|
|
|
|
{ TTestCursorDBBasics }
|
|
|
|
TTestCursorDBBasics = class(TDBBasicsTestCase)
|
|
private
|
|
procedure TestOnFilterProc(DataSet: TDataSet; var Accept: Boolean); // Filters out all records with even ID
|
|
procedure FTestDelete1(TestCancelUpdate : boolean);
|
|
procedure FTestDelete2(TestCancelUpdate : boolean);
|
|
published
|
|
procedure TestCancel;
|
|
procedure TestCancelUpdDelete1;
|
|
procedure TestCancelUpdDelete2;
|
|
|
|
procedure TestAppendInsertRecord;
|
|
|
|
procedure TestBookmarks;
|
|
procedure TestBookmarkValid;
|
|
procedure TestCompareBookmarks;
|
|
|
|
procedure TestDelete1;
|
|
procedure TestDelete2;
|
|
|
|
procedure TestLocate;
|
|
procedure TestLocateCaseIns;
|
|
procedure TestLocateCaseInsInts;
|
|
procedure TestLookup;
|
|
|
|
procedure TestOnFilter;
|
|
procedure TestIntFilter; //Integer range filter
|
|
procedure TestNegativeIntFilter; //Negative integer filter; bug 25168
|
|
procedure TestStringFilter; //String filter expressions
|
|
|
|
procedure TestNullAtOpen;
|
|
|
|
procedure TestAppendOnEmptyDataset;
|
|
procedure TestInsertOnEmptyDataset;
|
|
|
|
procedure TestFirst;
|
|
procedure TestEofAfterFirst; // bug 7211
|
|
procedure TestLastAppendCancel; // bug 5058
|
|
procedure TestRecNo; // bug 5061
|
|
procedure TestSetRecNo; // bug 6919
|
|
procedure TestBug7007;
|
|
procedure TestBug6893;
|
|
procedure TestRequired;
|
|
procedure TestModified;
|
|
procedure TestUpdateCursorPos; // bug 31532
|
|
// fields
|
|
procedure TestFieldOldValueObsolete;
|
|
procedure TestFieldOldValue;
|
|
procedure TestChangeBlobFieldBeforePost; //bug 15376
|
|
end;
|
|
|
|
|
|
{ TDBBasicsUniDirectionalTestSetup }
|
|
{$ifdef fpc}
|
|
TDBBasicsUniDirectionalTestSetup = class(TDBBasicsTestSetup)
|
|
protected
|
|
procedure OneTimeSetup; override;
|
|
procedure OneTimeTearDown; override;
|
|
end;
|
|
{$endif fpc}
|
|
|
|
implementation
|
|
|
|
uses
|
|
{$ifdef fpc}
|
|
bufdataset,
|
|
sqldb,
|
|
{$endif fpc}
|
|
variants,
|
|
strutils,
|
|
FmtBCD;
|
|
|
|
type
|
|
THackDataLink=class(TDataLink);
|
|
|
|
{ TMyCustomBufDataset }
|
|
|
|
TMyCustomBufDataset = Class(TCustomBufDataset)
|
|
protected
|
|
procedure LoadBlobIntoBuffer(FieldDef: TFieldDef; ABlobBuf: PBufBlobField); override;
|
|
end;
|
|
|
|
{ TMyCustomBufDataset }
|
|
|
|
procedure TMyCustomBufDataset.LoadBlobIntoBuffer(FieldDef: TFieldDef; ABlobBuf: PBufBlobField);
|
|
begin
|
|
Raise ENotImplemented.Create('LoadBlobIntoBuffer not implemented');
|
|
end;
|
|
|
|
{ TTestCursorDBBasics }
|
|
|
|
procedure TTestCursorDBBasics.TestAppendOnEmptyDataset;
|
|
begin
|
|
with DBConnector.GetNDataset(True,0) do
|
|
begin
|
|
open;
|
|
CheckTrue(CanModify);
|
|
CheckTrue(eof);
|
|
CheckTrue(bof);
|
|
append;
|
|
FieldByName('id').AsInteger:=0;
|
|
CheckFalse(Bof);
|
|
CheckTrue(Eof);
|
|
post;
|
|
CheckFalse(eof);
|
|
CheckFalse(bof);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestInsertOnEmptyDataset;
|
|
begin
|
|
with DBConnector.GetNDataset(True,0) do
|
|
begin
|
|
open;
|
|
CheckTrue(CanModify);
|
|
CheckTrue(eof);
|
|
CheckTrue(bof);
|
|
CheckTrue(IsEmpty);
|
|
insert;
|
|
FieldByName('id').AsInteger:=0;
|
|
CheckTrue(Bof);
|
|
CheckTrue(Eof);
|
|
CheckFalse(IsEmpty);
|
|
post;
|
|
CheckFalse(IsEmpty);
|
|
CheckFalse(eof);
|
|
CheckFalse(bof);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSelectQueryBasics;
|
|
begin
|
|
with DBConnector.GetNDataset(1) do
|
|
begin
|
|
Open;
|
|
|
|
if IsUniDirectional then
|
|
CheckEquals(-1,RecNo)
|
|
else
|
|
CheckEquals(1,RecNo);
|
|
CheckEquals(1,RecordCount);
|
|
|
|
CheckEquals(2,FieldCount);
|
|
|
|
CheckTrue(CompareText('ID',fields[0].FieldName)=0);
|
|
CheckTrue(CompareText('ID',fields[0].DisplayName)=0);
|
|
CheckTrue(ftInteger=fields[0].DataType, 'The datatype of the field ''ID'' is incorrect, it should be ftInteger');
|
|
|
|
CheckTrue(CompareText('NAME',fields[1].FieldName)=0);
|
|
CheckTrue(CompareText('NAME',fields[1].DisplayName)=0);
|
|
CheckTrue(ftString=fields[1].DataType);
|
|
|
|
CheckEquals(1,fields[0].Value);
|
|
CheckEquals('TestName1',fields[1].Value);
|
|
|
|
Close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestPostOnlyInEditState;
|
|
begin
|
|
with DBConnector.GetNDataset(1) do
|
|
begin
|
|
open;
|
|
CheckException(Post,EDatabaseError,'Post was called in a non-edit state');
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestMove;
|
|
|
|
var i,count : integer;
|
|
aDatasource : TDataSource;
|
|
aDatalink : TDataLink;
|
|
ABufferCount : Integer;
|
|
|
|
begin
|
|
aDatasource := TDataSource.Create(nil);
|
|
aDatalink := TTestDataLink.Create;
|
|
try
|
|
aDatalink.DataSource := aDatasource;
|
|
ABufferCount := 11;
|
|
aDatalink.BufferCount := ABufferCount;
|
|
DataEvents := '';
|
|
for count := 0 to 32 do
|
|
begin
|
|
aDatasource.DataSet := DBConnector.GetNDataset(count);
|
|
with aDatasource.Dataset do
|
|
begin
|
|
i := 1;
|
|
Open;
|
|
CheckEquals('deUpdateState:0;',DataEvents);
|
|
DataEvents := '';
|
|
while not EOF do
|
|
begin
|
|
CheckEquals(i,fields[0].AsInteger);
|
|
CheckEquals('TestName'+inttostr(i),fields[1].AsString);
|
|
inc(i);
|
|
|
|
Next;
|
|
if (i > ABufferCount) and not EOF then
|
|
CheckEquals('deCheckBrowseMode:0;deDataSetScroll:-1;DataSetScrolled:1;DataSetChanged;',DataEvents)
|
|
else
|
|
CheckEquals('deCheckBrowseMode:0;deDataSetScroll:0;DataSetScrolled:0;DataSetChanged;',DataEvents);
|
|
DataEvents := '';
|
|
end;
|
|
CheckEquals(count,i-1);
|
|
close;
|
|
CheckEquals('deUpdateState:0;',DataEvents);
|
|
DataEvents := '';
|
|
end;
|
|
end;
|
|
finally
|
|
aDatalink.Free;
|
|
aDatasource.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestActiveBufferWhenClosed;
|
|
begin
|
|
with DBConnector.GetNDataset(0) do
|
|
begin
|
|
{$ifdef fpc}
|
|
AssertNull(ActiveBuffer);
|
|
{$endif fpc}
|
|
open;
|
|
CheckFalse(ActiveBuffer = nil,'Activebuffer of an empty dataset shouldn''t be nil');
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestEOFBOFClosedDataset;
|
|
begin
|
|
with DBConnector.GetNDataset(1) do
|
|
begin
|
|
CheckTrue(EOF);
|
|
CheckTrue(BOF);
|
|
open;
|
|
CheckTrue(BOF, 'No BOF when opened non-empty dataset');
|
|
CheckFalse(EOF, 'EOF after opened non-empty dataset');
|
|
close;
|
|
CheckTrue(EOF);
|
|
CheckTrue(BOF);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestLayoutChangedEvents;
|
|
var aDatasource : TDataSource;
|
|
aDatalink : TDataLink;
|
|
ds : tdataset;
|
|
|
|
begin
|
|
aDatasource := TDataSource.Create(nil);
|
|
aDatalink := TTestDataLink.Create;
|
|
try
|
|
aDatalink.DataSource := aDatasource;
|
|
ds := DBConnector.GetNDataset(6);
|
|
aDatasource.DataSet:=ds;
|
|
with ds do
|
|
begin
|
|
open;
|
|
|
|
DataEvents := '';
|
|
DisableControls;
|
|
Active:=False;
|
|
Active:=True;
|
|
EnableControls;
|
|
CheckEquals('deLayoutChange:0;DataSetChanged;',DataEvents);
|
|
|
|
close;
|
|
end;
|
|
finally
|
|
aDatasource.Free;
|
|
aDatalink.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestDataEventsResync;
|
|
var
|
|
aDatasource : TDataSource;
|
|
aDatalink : TDataLink;
|
|
ds : tdataset;
|
|
|
|
begin
|
|
aDatasource := TDataSource.Create(nil);
|
|
aDatalink := TTestDataLink.Create;
|
|
try
|
|
aDatalink.DataSource := aDatasource;
|
|
ds := DBConnector.GetNDataset(6);
|
|
ds.BeforeScroll := DBConnector.DataEvent;
|
|
with ds do
|
|
begin
|
|
aDatasource.DataSet := ds;
|
|
Open;
|
|
DataEvents := '';
|
|
Resync([rmExact]);
|
|
if IsUniDirectional then
|
|
CheckEquals('',DataEvents)
|
|
else
|
|
CheckEquals('deDataSetChange:0;DataSetChanged;',DataEvents);
|
|
DataEvents := '';
|
|
Next;
|
|
if IsUniDirectional then
|
|
CheckEquals('deCheckBrowseMode:0;DataEvent;deDataSetScroll:-1;DataSetScrolled:1;DataSetChanged;',DataEvents)
|
|
else
|
|
CheckEquals('deCheckBrowseMode:0;DataEvent;deDataSetScroll:0;DataSetScrolled:1;DataSetChanged;',DataEvents);
|
|
DataEvents := '';
|
|
Close;
|
|
end;
|
|
finally
|
|
aDatasource.Free;
|
|
aDatalink.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestdeFieldListChange;
|
|
|
|
var
|
|
aDatasource : TDataSource;
|
|
aDatalink : TDataLink;
|
|
ds : TDataset;
|
|
|
|
begin
|
|
aDatasource := TDataSource.Create(nil);
|
|
aDatalink := TTestDataLink.Create;
|
|
aDatalink.DataSource := aDatasource;
|
|
ds := DBConnector.GetNDataset(1);
|
|
with ds do
|
|
begin
|
|
aDatasource.DataSet := ds;
|
|
DataEvents := '';
|
|
Open;
|
|
Fields.Add(TField.Create(ds));
|
|
CheckEquals('deUpdateState:0;deFieldListChange:0;',DataEvents);
|
|
DataEvents := '';
|
|
Fields.Clear;
|
|
CheckEquals('deFieldListChange:0;',DataEvents)
|
|
end;
|
|
aDatasource.Free;
|
|
aDatalink.Free;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestRecordcountAfterReopen;
|
|
var
|
|
datalink1: tdatalink;
|
|
datasource1: tdatasource;
|
|
query1: TDataSet;
|
|
|
|
begin
|
|
query1:= DBConnector.GetNDataset(11);
|
|
datalink1:= TDataLink.create;
|
|
datasource1:= TDataSource.create(nil);
|
|
try
|
|
datalink1.DataSource:= datasource1;
|
|
datasource1.DataSet:= query1;
|
|
|
|
query1.active := True;
|
|
query1.active := False;
|
|
CheckEquals(0, THackDataLink(datalink1).RecordCount);
|
|
query1.active := True;
|
|
CheckTrue(THackDataLink(datalink1).RecordCount>0);
|
|
query1.active := False;
|
|
finally
|
|
datalink1.free;
|
|
datasource1.free;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestLastAppendCancel;
|
|
|
|
var count : integer;
|
|
|
|
begin
|
|
for count := 0 to 32 do with DBConnector.GetNDataset(count) do
|
|
begin
|
|
open;
|
|
|
|
Last;
|
|
Append;
|
|
Cancel;
|
|
|
|
CheckEquals(count,fields[0].asinteger);
|
|
CheckEquals(count,RecordCount);
|
|
|
|
Close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestRecNo;
|
|
var passed : boolean;
|
|
begin
|
|
with DBConnector.GetNDataset(0) do
|
|
begin
|
|
// Accessing RecNo on a closed dataset should raise an EDatabaseError or should
|
|
// return 0
|
|
passed := false;
|
|
try
|
|
passed := RecNo = 0;
|
|
except on E: Exception do
|
|
passed := E.classname = EDatabaseError.className
|
|
end;
|
|
if not passed then
|
|
CheckEquals(0,RecNo,'Failed to get the RecNo from a closed dataset');
|
|
|
|
// Accessing RecordCount on a closed dataset should raise an EDatabaseError or should
|
|
// return 0
|
|
passed := false;
|
|
try
|
|
passed := RecordCount = 0;
|
|
except on E: Exception do
|
|
passed := E.classname = EDatabaseError.className
|
|
end;
|
|
if not passed then
|
|
CheckEquals(0,RecordCount,'Failed to get the RecordCount from a closed dataset');
|
|
|
|
Open;
|
|
|
|
CheckEquals(0,RecordCount,'1. record count after open');
|
|
CheckEquals(0,RecNo,'1. recno after open');
|
|
CheckEquals(True,EOF and BOF, '1. Empty');
|
|
|
|
first;
|
|
CheckEquals(0,RecordCount,'2. recordcount after first (empty)');
|
|
CheckEquals(0,RecNo,'2. recno after first (empty)');
|
|
CheckEquals(True,EOF and BOF, '1. Empty');
|
|
|
|
last;
|
|
CheckEquals(0,RecordCount,'3. recordcount after last (empty)');
|
|
CheckEquals(0,RecNo,'3. recordcount after last (empty)');
|
|
CheckEquals(True,EOF and BOF, '3. Empty');
|
|
|
|
append;
|
|
CheckEquals(0,RecNo,'4. recno after append (empty)');
|
|
CheckEquals(0,RecordCount,'4. recordcount after append (empty)');
|
|
CheckEquals(False, EOF and BOF, '4. Empty');
|
|
|
|
first;
|
|
CheckEquals(0,RecNo,'5. recno after first append (empty,append )');
|
|
CheckEquals(0,RecordCount,'5. recordcount after first (empty, append)');
|
|
CheckEquals(True,EOF and BOF, '5. Empty');
|
|
|
|
append;
|
|
FieldByName('id').AsInteger := 1;
|
|
CheckEquals(0,RecNo,'6. recno after second append (empty,append)');
|
|
CheckEquals(0,RecordCount,'6. recordcount after second append (empty,append)');
|
|
CheckEquals(False ,EOF and BOF, '6. Empty');
|
|
|
|
first;
|
|
CheckEquals(1,RecNo,'7. recno after second append, first (1,append)');
|
|
CheckEquals(1,RecordCount,'7. recordcount after second append,first (1,append)');
|
|
CheckEquals(False ,EOF and BOF, '7. Empty');
|
|
|
|
last;
|
|
CheckEquals(1,RecNo,'8. recno after second append, last (1,append)');
|
|
CheckEquals(1,RecordCount,'8. recordcount after second append, last (1,append)');
|
|
|
|
append;
|
|
FieldByName('id').AsInteger := 2;
|
|
CheckEquals(0,RecNo,'9. RecNo after 3rd Append');
|
|
CheckEquals(1,RecordCount,'9. Recordcount after 3rd Append');
|
|
post;
|
|
|
|
edit;
|
|
CheckEquals(2,RecNo,'RecNo after Edit');
|
|
CheckEquals(2,RecordCount);
|
|
|
|
Close;
|
|
|
|
// Tests if RecordCount resets to 0 after dataset is closed
|
|
passed := false;
|
|
try
|
|
passed := RecordCount = 0;
|
|
except on E: Exception do
|
|
passed := E.classname = EDatabaseError.className
|
|
end;
|
|
if not passed then
|
|
CheckEquals(0,RecordCount,'RecordCount after Close');
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestSetRecNo;
|
|
begin
|
|
with DBConnector.GetNDataset(15) do
|
|
begin
|
|
Open;
|
|
RecNo := 1;
|
|
CheckEquals(1,fields[0].AsInteger);
|
|
CheckEquals(1,RecNo);
|
|
|
|
RecNo := 2;
|
|
CheckEquals(2,fields[0].AsInteger);
|
|
CheckEquals(2,RecNo);
|
|
|
|
RecNo := 8;
|
|
CheckEquals(8,fields[0].AsInteger);
|
|
CheckEquals(8,RecNo);
|
|
|
|
RecNo := 15;
|
|
CheckEquals(15,fields[0].AsInteger);
|
|
CheckEquals(15,RecNo);
|
|
|
|
RecNo := 3;
|
|
CheckEquals(3,fields[0].AsInteger);
|
|
CheckEquals(3,RecNo);
|
|
|
|
RecNo := 14;
|
|
CheckEquals(14,fields[0].AsInteger);
|
|
CheckEquals(14,RecNo);
|
|
|
|
RecNo := 15;
|
|
CheckEquals(15,fields[0].AsInteger);
|
|
CheckEquals(15,RecNo);
|
|
|
|
// test for exceptions...
|
|
{ RecNo := 16;
|
|
CheckEquals(15,fields[0].AsInteger);
|
|
CheckEquals(15,RecNo);}
|
|
|
|
Close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestRequired;
|
|
begin
|
|
with DBConnector.GetNDataset(2) do
|
|
begin
|
|
Open;
|
|
FieldByName('ID').Required := True;
|
|
Append;
|
|
CheckException(Post, EDatabaseError);
|
|
FieldByName('ID').AsInteger := 1000;
|
|
Post;
|
|
Close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestExceptionLocateClosed;
|
|
var passed: boolean;
|
|
begin
|
|
with DBConnector.GetNDataset(15) do
|
|
begin
|
|
passed := false;
|
|
try
|
|
locate('name','TestName1',[]);
|
|
except on E: Exception do
|
|
begin
|
|
passed := E.classname = EDatabaseError.className
|
|
end;
|
|
end;
|
|
CheckTrue(passed);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestModified;
|
|
begin
|
|
// Tests TDataSet.Modified property
|
|
with DBConnector.GetNDataset(true,1) as TDataset do
|
|
begin
|
|
Open;
|
|
CheckFalse(Modified);
|
|
|
|
Edit;
|
|
CheckFalse(Modified, 'After Edit');
|
|
Fields[1].AsString := Fields[1].AsString;
|
|
CheckTrue(Modified, 'After change');
|
|
Post;
|
|
CheckFalse(Modified, 'After Post');
|
|
|
|
Append;
|
|
CheckFalse(Modified, 'After Append');
|
|
Fields[0].AsInteger := 100;
|
|
CheckTrue(Modified, 'After change');
|
|
Cancel;
|
|
CheckFalse(Modified, 'After Cancel');
|
|
|
|
Close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestUpdateCursorPos;
|
|
var
|
|
datasource1: TDataSource;
|
|
datalink1: TDataLink;
|
|
dataset1: TDataSet;
|
|
i,r: integer;
|
|
begin
|
|
// TBufDataset should notify TDataset (TDataset.CurrentRecord) when changes internaly current record
|
|
// TBufDataset.GetRecNo was synchronizing its internal position with TDataset.ActiveRecord, but TDataset.CurrentRecord remains unchaged
|
|
// Bug #31532
|
|
dataset1 := DBConnector.GetNDataset(16);
|
|
datasource1 := TDataSource.Create(nil);
|
|
datasource1.DataSet := dataset1;
|
|
datalink1 := TDataLink.Create;
|
|
datalink1:= TDataLink.create;
|
|
datalink1.DataSource:= datasource1;
|
|
datalink1.BufferCount:= 12;
|
|
|
|
dataset1.Open;
|
|
dataset1.MoveBy(4);
|
|
CheckEquals(5, dataset1.RecNo);
|
|
for i:=13 to 15 do begin
|
|
datalink1.BufferCount := datalink1.BufferCount+1;
|
|
r := dataset1.RecNo; // syncronizes source dataset to ActiveRecord
|
|
AssertTrue(r>=0);
|
|
datalink1.ActiveRecord := datalink1.BufferCount-1;
|
|
CheckEquals(i, dataset1.FieldByName('ID').AsInteger);
|
|
end;
|
|
datasource1.free;
|
|
datalink1.free;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestDetectionNonMatchingDataset;
|
|
var
|
|
F: TField;
|
|
ds: TDataSet;
|
|
begin
|
|
// TDataset.BindFields should detect problems when the underlying data does
|
|
// not reflect the fields of the dataset. This test is to check if this is
|
|
// really done.
|
|
ds := DBConnector.GetNDataset(true,6);
|
|
with ds do
|
|
begin
|
|
open;
|
|
close;
|
|
|
|
F := TStringField.Create(ds);
|
|
F.FieldName:='DOES_NOT_EXIST';
|
|
F.DataSet:=ds;
|
|
F.Size:=50;
|
|
|
|
CheckException(open,EDatabaseError);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestAppendInsertRecord;
|
|
begin
|
|
with DBConnector.GetNDataset(true,6) do
|
|
begin
|
|
open;
|
|
// InsertRecord should insert a record, set the values, post the record and
|
|
// make the new record active.
|
|
InsertRecord([152,'TestInsRec']);
|
|
CheckEquals(152,fields[0].AsInteger);
|
|
CheckEquals('TestInsRec',fields[1].AsString);
|
|
CheckTrue(State=dsBrowse);
|
|
|
|
// AppendRecord should append a record, further the same as InsertRecord
|
|
AppendRecord([151,'TestInsRec']);
|
|
CheckEquals(151,fields[0].AsInteger);
|
|
CheckEquals('TestInsRec',fields[1].AsString);
|
|
CheckTrue(state=dsBrowse);
|
|
next;
|
|
CheckTrue(EOF);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestBookmarks;
|
|
var BM1,BM2,BM3,BM4,BM5 : TBookmark;
|
|
begin
|
|
with DBConnector.GetNDataset(true,14) do
|
|
begin
|
|
{$ifdef fpc}
|
|
AssertNull(GetBookmark);
|
|
{$endif fpc}
|
|
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);
|
|
CheckEquals(3,FieldByName('id').AsInteger);
|
|
|
|
GotoBookmark(BM1);
|
|
CheckEquals(1,FieldByName('id').AsInteger);
|
|
|
|
GotoBookmark(BM3);
|
|
CheckEquals(6,FieldByName('id').AsInteger);
|
|
|
|
GotoBookmark(BM4);
|
|
CheckEquals(14,FieldByName('id').AsInteger);
|
|
|
|
GotoBookmark(BM3);
|
|
CheckEquals(6,FieldByName('id').AsInteger);
|
|
|
|
GotoBookmark(BM5);
|
|
CheckEquals(14,FieldByName('id').AsInteger);
|
|
|
|
GotoBookmark(BM1);
|
|
CheckEquals(1,FieldByName('id').AsInteger);
|
|
|
|
next;
|
|
delete; // id=2
|
|
|
|
GotoBookmark(BM2);
|
|
CheckEquals(3,FieldByName('id').AsInteger,'After #2 deleted');
|
|
|
|
delete;delete; // id=3,4
|
|
|
|
GotoBookmark(BM3);
|
|
CheckEquals(6,FieldByName('id').AsInteger);
|
|
|
|
GotoBookmark(BM1);
|
|
CheckEquals(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);
|
|
CheckEquals(6,FieldByName('id').AsInteger);
|
|
|
|
GotoBookmark(BM1);
|
|
CheckEquals(1,FieldByName('id').AsInteger);
|
|
|
|
GotoBookmark(BM5);
|
|
CheckEquals(14,FieldByName('id').AsInteger);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestBookmarkValid;
|
|
var BM1,BM2,BM3,BM4,BM5,BM6 : TBookmark;
|
|
begin
|
|
with DBConnector.GetNDataset(true,14) do
|
|
begin
|
|
BM1 := Nil;
|
|
CheckFalse(BookmarkValid(BM1));
|
|
open;
|
|
BM1:=GetBookmark; // id=1, BOF
|
|
CheckTrue(BookmarkValid(BM1));
|
|
next;next;
|
|
BM2:=GetBookmark; // id=3
|
|
CheckTrue(BookmarkValid(BM2));
|
|
next;next;next;
|
|
BM3:=GetBookmark; // id=6
|
|
CheckTrue(BookmarkValid(BM3));
|
|
next;next;next;next;next;next;next;next;
|
|
BM4:=GetBookmark; // id=14
|
|
CheckTrue(BookmarkValid(BM4));
|
|
next;
|
|
BM5:=GetBookmark; // id=14, EOF
|
|
CheckTrue(BookmarkValid(BM5));
|
|
|
|
CheckTrue(BookmarkValid(BM4));
|
|
CheckTrue(BookmarkValid(BM3));
|
|
CheckTrue(BookmarkValid(BM2));
|
|
CheckTrue(BookmarkValid(BM1));
|
|
GotoBookmark(BM2);
|
|
CheckTrue(BookmarkValid(BM5));
|
|
CheckTrue(BookmarkValid(BM4));
|
|
CheckTrue(BookmarkValid(BM3));
|
|
CheckTrue(BookmarkValid(BM2));
|
|
CheckTrue(BookmarkValid(BM1));
|
|
Append;
|
|
BM6 := GetBookmark;
|
|
CheckFalse(BookmarkValid(BM6));
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestCompareBookmarks;
|
|
var
|
|
FirstBookmark, LastBookmark, EditBookmark, PostEditBookmark: TBookmark;
|
|
begin
|
|
with DBConnector.GetNDataset(true,14) do
|
|
begin
|
|
Open;
|
|
FirstBookmark := GetBookmark;
|
|
|
|
Edit;
|
|
EditBookmark := GetBookmark;
|
|
Post;
|
|
PostEditBookmark := GetBookmark;
|
|
|
|
Last;
|
|
LastBookmark := GetBookmark;
|
|
|
|
CheckEquals(0, CompareBookmarks(FirstBookmark, EditBookmark));
|
|
CheckEquals(0, CompareBookmarks(EditBookmark, PostEditBookmark));
|
|
CheckTrue(CompareBookmarks(FirstBookmark, LastBookmark) < 0, 'b1<b2');
|
|
CheckTrue(CompareBookmarks(LastBookmark, FirstBookmark) > 0, 'b1>b2');
|
|
CheckEquals(0, CompareBookmarks(nil, nil), '(nil,nil)');
|
|
CheckEquals(-1, CompareBookmarks(FirstBookmark, nil), '(b1,nil)');
|
|
CheckEquals(+1, CompareBookmarks(nil, FirstBookmark), '(nil,b2)');
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestLocate;
|
|
begin
|
|
with DBConnector.GetNDataset(true,13) do
|
|
begin
|
|
open;
|
|
CheckTrue(Locate('id',3,[]));
|
|
|
|
CheckTrue(Locate('id',vararrayof([5]),[]));
|
|
CheckEquals(5,FieldByName('id').AsInteger);
|
|
|
|
CheckFalse(Locate('id',vararrayof([15]),[]));
|
|
|
|
CheckTrue(Locate('id',vararrayof([13]),[]));
|
|
CheckEquals(13,FieldByName('id').AsInteger);
|
|
close;
|
|
|
|
open;
|
|
CheckTrue(Locate('id',vararrayof([12]),[]));
|
|
CheckEquals(12,FieldByName('id').AsInteger);
|
|
|
|
CheckTrue(Locate('id;name',vararrayof([4,'TestName4']),[]));
|
|
CheckEquals(4,FieldByName('id').AsInteger);
|
|
|
|
CheckFalse(Locate('id;name',vararrayof([4,'TestName5']),[]));
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestLocateCaseIns;
|
|
// Tests case insensitive locate, also partial key locate, both against string fields.
|
|
// Together with TestLocateCaseInsInts, checks 23509 DBF: locate with loPartialkey behaviour differs depending on index use
|
|
begin
|
|
with DBConnector.GetNDataset(true,13) do
|
|
begin
|
|
open;
|
|
CheckFalse(Locate('name',vararrayof(['TEstName5']),[]));
|
|
CheckTrue(Locate('name',vararrayof(['TEstName5']),[loCaseInsensitive]));
|
|
CheckEquals(5,FieldByName('id').AsInteger);
|
|
|
|
CheckFalse(Locate('name',vararrayof(['TestN']),[]));
|
|
CheckTrue(Locate('name',vararrayof(['TestN']),[loPartialKey]));
|
|
|
|
CheckFalse(Locate('name',vararrayof(['TestNA']),[loPartialKey]));
|
|
CheckTrue(Locate('name',vararrayof(['TestNA']),[loPartialKey, loCaseInsensitive]));
|
|
close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestLocateCaseInsInts;
|
|
// Tests case insensitive locate, also partial key locate, both against integer fields.
|
|
// Together with TestLocateCaseIns, checks 23509 DBF: locate with loPartialkey behaviour differs depending on index use
|
|
begin
|
|
with DBConnector.GetNDataset(true,13) do
|
|
begin
|
|
open;
|
|
// To really test bug 23509: we should first have a record that matches greater than for non-string locate:
|
|
first;
|
|
insert;
|
|
fieldbyname('id').AsInteger:=55;
|
|
fieldbyname('name').AsString:='TestName55';
|
|
post;
|
|
first;
|
|
|
|
CheckTrue(Locate('id',vararrayof([5]),[]));
|
|
CheckEquals(5,FieldByName('id').AsInteger);
|
|
first;
|
|
|
|
CheckTrue(Locate('id',vararrayof([5]),[loCaseInsensitive]));
|
|
CheckEquals(5,FieldByName('id').AsInteger);
|
|
first;
|
|
|
|
// Check specifying partial key doesn't influence search results
|
|
CheckTrue(Locate('id',vararrayof([5]),[loPartialKey]));
|
|
CheckEquals(5,FieldByName('id').AsInteger);
|
|
first;
|
|
|
|
CheckTrue(Locate('id',vararrayof([5]),[loPartialKey, loCaseInsensitive]));
|
|
CheckEquals(5,FieldByName('id').AsInteger);
|
|
|
|
close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestLookup;
|
|
var v: variant;
|
|
begin
|
|
// Lookup doesn't move the record pointer of the dataset
|
|
// and no scroll events should be generated (only OnCalcFields when matched record is found)
|
|
with DBConnector.GetNDataset(13) do
|
|
begin
|
|
Open;
|
|
Next;
|
|
CheckEquals('TestName5', Lookup('id',5,'name'));
|
|
CheckTrue(Lookup('id',15,'name')=Null);
|
|
v:=Lookup('id',7,'id;name');
|
|
CheckEquals(7, v[0]);
|
|
CheckEquals('TestName7', v[1]);
|
|
// Lookup shouldn't change current record
|
|
CheckEquals(2, FieldByName('id').AsInteger);
|
|
Close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestFieldOldValueObsolete;
|
|
var v : variant;
|
|
ds: TDataset;
|
|
begin
|
|
// this test was created as reaction to AV bug found in TCustomBufDataset.GetFieldData
|
|
// when retrieving OldValue (State=dsOldValue) of newly inserted or appended record.
|
|
// In this case was CurrBuff set to nil (and not checked),
|
|
// because OldValuesBuffer for just inserted record is nil. See rev.17704
|
|
// (So purpose of this test isn't test InsertRecord on empty dataset or so)
|
|
// Later was this test replaced by more complex TestOldValue (superset of old test),
|
|
// but next to it was restored back also original test.
|
|
// So now we have two tests which test same thing, where this 'old' one is subset of 'new' one
|
|
// Ideal solution would be remove this 'old' test as it does not test anything what is not tested elsewhere ...
|
|
ds := DBConnector.GetNDataset(0) as TDataset;
|
|
ds.Open;
|
|
ds.InsertRecord([0,'name']);
|
|
v := VarToStr(ds.Fields[1].OldValue);
|
|
AssertTrue(v<>null);
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestFieldOldValue;
|
|
var ds: TDataSet;
|
|
OldValue: string;
|
|
Fmemo: TField;
|
|
begin
|
|
ds := DBConnector.GetFieldDataset;
|
|
with ds do
|
|
begin;
|
|
Open;
|
|
First;
|
|
Next;
|
|
OldValue := Fields[0].AsString;
|
|
|
|
CheckEquals(OldValue, VarToStr(Fields[0].OldValue), 'Original value'); // unmodified original value
|
|
CheckTrue(UpdateStatus=usUnmodified, 'Unmodified');
|
|
|
|
Edit;
|
|
Fields[0].AsInteger := -1;
|
|
CheckEquals(OldValue, VarToStr(Fields[0].OldValue), 'Editing'); // dsEdit, there is no update-buffer yet
|
|
Post;
|
|
CheckEquals(OldValue, VarToStr(Fields[0].OldValue), 'Edited'); // there is already update-buffer
|
|
CheckTrue(UpdateStatus=usModified, 'Modified');
|
|
|
|
Append;
|
|
Fields[0].AsInteger := -2;
|
|
CheckTrue(VarIsNull(Fields[0].OldValue), 'Inserting'); // dsInsert, there is no update-buffer yet
|
|
Post;
|
|
CheckTrue(VarIsNull(Fields[0].OldValue), 'Inserted'); // there is already update-buffer
|
|
CheckTrue(UpdateStatus=usInserted, 'Inserted');
|
|
|
|
// Blobs are stored in a special way
|
|
// Use TMemoField because it implements AsVariant as AsString
|
|
First;
|
|
Next;
|
|
Fmemo := FieldByName('F'+FieldTypeNames[ftMemo]);
|
|
OldValue := Fmemo.AsString;
|
|
|
|
CheckEquals(OldValue, Fmemo.OldValue, 'Memo.OldValue');
|
|
Edit;
|
|
Fmemo.AsString := 'Changed Memo value';
|
|
CheckEquals(OldValue, Fmemo.OldValue, 'Memo.OldValue before Post');
|
|
Post;
|
|
CheckEquals(OldValue, Fmemo.OldValue, 'Memo.OldValue after Post');
|
|
end;
|
|
if ds is TCustomBufDataset then
|
|
with ds as TCustomBufDataset do
|
|
begin
|
|
MergeChangeLog;
|
|
CheckEquals('Changed Memo value', Fmemo.OldValue, 'Memo.OldValue after MergeChangeLog');
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestChangeBlobFieldBeforePost;
|
|
// Edit memo fields should read back new contents even before post
|
|
// Bug 15376
|
|
// See also TTestFieldTypes.TestChangeBlob
|
|
var
|
|
Fmemo: TField;
|
|
begin
|
|
with DBConnector.GetFieldDataset do
|
|
begin
|
|
Open;
|
|
Append;
|
|
FieldByName('ID').AsInteger := -1; // Required - not null
|
|
|
|
Fmemo := FieldByName('FMEMO');
|
|
CheckTrue(Fmemo.IsNull, 'IsNull after Append');
|
|
|
|
Fmemo.AsString:='MEMO1';
|
|
CheckFalse(Fmemo.IsNull, 'IsNull after change');
|
|
CheckEquals('MEMO1', Fmemo.AsString);
|
|
|
|
Fmemo.Clear;
|
|
CheckTrue(Fmemo.IsNull, 'IsNull after Clear');
|
|
|
|
Fmemo.AsString:='MEMO2';
|
|
CheckEquals('MEMO2', Fmemo.AsString);
|
|
|
|
Fmemo.AsString:='';
|
|
CheckTrue(Fmemo.IsNull, 'IsNull');
|
|
|
|
Fmemo.AsString:='MEMO3';
|
|
CheckEquals('MEMO3', Fmemo.AsString);
|
|
Post;
|
|
CheckEquals('MEMO3', Fmemo.AsString);
|
|
Close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSetFieldValues;
|
|
var PassException : boolean;
|
|
begin
|
|
with DBConnector.GetNDataset(true,11) do
|
|
begin
|
|
open;
|
|
// First and Next methods are supported by UniDirectional datasets
|
|
first;
|
|
if IsUniDirectional then
|
|
CheckException(Edit, EDatabaseError)
|
|
else
|
|
begin
|
|
edit;
|
|
FieldValues['id']:=5;
|
|
post;
|
|
CheckEquals('TestName1',FieldByName('name').AsString);
|
|
CheckEquals(5,FieldByName('id').AsInteger);
|
|
edit;
|
|
FieldValues['name']:='FieldValuesTestName';
|
|
post;
|
|
CheckEquals('FieldValuesTestName',FieldByName('name').AsString);
|
|
CheckEquals(5,FieldByName('id').AsInteger);
|
|
edit;
|
|
FieldValues['id;name']:= VarArrayOf([243,'ValuesTestName']);
|
|
post;
|
|
CheckEquals('ValuesTestName',FieldByName('name').AsString);
|
|
CheckEquals(243,FieldByName('id').AsInteger);
|
|
|
|
PassException:=false;
|
|
try
|
|
edit;
|
|
FieldValues['id;name;fake']:= VarArrayOf([243,'ValuesTestName',4]);
|
|
except
|
|
on E: EDatabaseError do PassException := True;
|
|
end;
|
|
post;
|
|
CheckTrue(PassException);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestGetFieldValues;
|
|
var AVar : Variant;
|
|
PassException : boolean;
|
|
begin
|
|
with DBConnector.GetNDataset(true,14) do
|
|
begin
|
|
open;
|
|
AVar:=FieldValues['id'];
|
|
CheckEquals(AVar,1);
|
|
|
|
AVar:=FieldValues['name'];
|
|
CheckEquals(AVar,'TestName1');
|
|
|
|
AVar:=FieldValues['id;name'];
|
|
CheckEquals(AVar[0],1);
|
|
CheckEquals(AVar[1],'TestName1');
|
|
|
|
AVar:=FieldValues['name;id;'];
|
|
CheckEquals(AVar[1],1);
|
|
CheckEquals(AVar[0],'TestName1');
|
|
|
|
PassException:=false;
|
|
try
|
|
AVar:=FieldValues['name;id;fake'];
|
|
except
|
|
on E: EDatabaseError do PassException := True;
|
|
end;
|
|
CheckTrue(PassException);
|
|
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestClearFields;
|
|
begin
|
|
with DBConnector.GetNDataset(true,14) do
|
|
begin
|
|
Open;
|
|
AssertException('Cannot call ClearFields when not in edit mode',EDatabaseError,ClearFields);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestDelete1;
|
|
begin
|
|
FTestDelete1(false);
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestDelete2;
|
|
begin
|
|
FTestDelete2(false);
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestCancelUpdDelete1;
|
|
begin
|
|
FTestDelete1(true);
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestCancelUpdDelete2;
|
|
begin
|
|
FTestDelete2(true);
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.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
|
|
CheckEquals(i+1,FieldByName('ID').AsInteger);
|
|
CheckEquals('TestName'+inttostr(i+1),FieldByName('NAME').AsString);
|
|
next;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{$ifdef fpc}
|
|
if TestCancelUpdate then
|
|
begin
|
|
if not (ds is TCustomBufDataset) then
|
|
Ignore('This test only applies to TCustomBufDataset and descendents.');
|
|
with TCustomBufDataset(ds) do
|
|
begin
|
|
CancelUpdates;
|
|
|
|
First;
|
|
for i := 0 to 16 do
|
|
begin
|
|
CheckEquals(i+1,FieldByName('ID').AsInteger);
|
|
CheckEquals('TestName'+inttostr(i+1),FieldByName('NAME').AsString);
|
|
next;
|
|
end;
|
|
|
|
close;
|
|
end;
|
|
end;
|
|
{$endif}
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.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;
|
|
|
|
// modify records
|
|
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;
|
|
|
|
// append new records
|
|
for i := 18 to 21 do
|
|
begin
|
|
append;
|
|
fieldbyname('id').AsInteger:=i;
|
|
fieldbyname('name').AsString:='TestName'+inttostr(i);
|
|
post;
|
|
end;
|
|
|
|
// delete records #1,5,9,13,17,21 which was modified or appended before
|
|
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
|
|
CheckEquals(i+1,FieldByName('ID').AsInteger);
|
|
CheckEquals('TestName'+inttostr(i+1),FieldByName('NAME').AsString);
|
|
next;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{$ifdef fpc}
|
|
if TestCancelUpdate then
|
|
begin
|
|
if not (ds is TCustomBufDataset) then
|
|
Ignore('This test only applies to TCustomBufDataset and descendents.');
|
|
with TCustomBufDataset(ds) do
|
|
begin
|
|
CancelUpdates;
|
|
|
|
First;
|
|
for i := 1 to 17 do
|
|
begin
|
|
CheckEquals(i, FieldByName('ID').AsInteger);
|
|
CheckEquals('TestName'+inttostr(i), FieldByName('NAME').AsString);
|
|
next;
|
|
end;
|
|
|
|
close;
|
|
end;
|
|
end;
|
|
{$endif fpc}
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestCancel;
|
|
begin
|
|
with DBConnector.GetNDataset(1) do
|
|
begin
|
|
Open;
|
|
Edit;
|
|
FieldByName('name').AsString := 'EditName1';
|
|
Cancel;
|
|
CheckEquals('TestName1', FieldByName('name').AsString, 'Cancel did not restored previous value');
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestOnFilterProc(DataSet: TDataSet; var Accept: Boolean);
|
|
begin
|
|
Accept := odd(Dataset.FieldByName('ID').AsInteger);
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestOnFilter;
|
|
// Tests OnFilterRecord filtering
|
|
var
|
|
Counter : byte;
|
|
begin
|
|
with DBConnector.GetNDataset(15) do
|
|
begin
|
|
OnFilterRecord := TestOnFilterProc;
|
|
Filtered := True;
|
|
Open;
|
|
for Counter := 1 to 8 do
|
|
begin
|
|
CheckTrue(odd(FieldByName('ID').asinteger));
|
|
next;
|
|
end;
|
|
CheckTrue(EOF, 'Filter should give only odd records');
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestIntFilter;
|
|
// Tests an integer range filter expression
|
|
var
|
|
Counter : byte;
|
|
begin
|
|
with DBConnector.GetNDataset(15) do
|
|
begin
|
|
Filtered := True;
|
|
Filter := '(id>4) and (id<9)';
|
|
Open;
|
|
for Counter := 5 to 8 do
|
|
begin
|
|
CheckEquals(Counter, FieldByName('ID').AsInteger);
|
|
Next;
|
|
end;
|
|
CheckTrue(EOF, 'Filter (id>4) and (id<9)');
|
|
|
|
Filter := '-id-ID=-4';
|
|
CheckEquals(2, FieldByName('ID').AsInteger, 'Unary minus');
|
|
Next;
|
|
CheckTrue(EOF, 'Unary minus');
|
|
|
|
Close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestNegativeIntFilter;
|
|
// Tests a negative integer range filter expression
|
|
var
|
|
Counter : integer;
|
|
begin
|
|
with DBConnector.GetNDataset(15) do
|
|
begin
|
|
// Change ID values to -1..-15 instead of positive
|
|
Open;
|
|
while not(EOF) do
|
|
begin
|
|
Edit;
|
|
FieldByName('ID').AsInteger:=
|
|
-1*(FieldByname('ID').AsInteger);
|
|
Post;
|
|
Next;
|
|
end;
|
|
|
|
// Regular filter with negative integer values
|
|
Filtered := True;
|
|
Filter := '(id>-9) and (id<-4)';
|
|
First;
|
|
for Counter := -5 downto -8 do
|
|
begin
|
|
CheckEquals(Counter,FieldByName('ID').AsInteger);
|
|
Next;
|
|
end;
|
|
CheckTrue(EOF);
|
|
|
|
// Filter with negative integer values and subtraction calculations
|
|
Filtered := True;
|
|
Filter := '(id>(-8-1)) and (id<(-3-1))';
|
|
First;
|
|
for Counter := -5 downto -8 do
|
|
begin
|
|
CheckEquals(Counter,FieldByName('ID').AsInteger);
|
|
Next;
|
|
end;
|
|
CheckTrue(EOF);
|
|
|
|
Close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestStringFilter;
|
|
// Tests string expression filters
|
|
begin
|
|
with DBConnector.GetNDataset(15) do
|
|
begin
|
|
Open;
|
|
|
|
// Check equality
|
|
Filter := '(name=''TestName3'')';
|
|
Filtered := True;
|
|
CheckFalse(EOF, 'Simple equality');
|
|
CheckEquals(3,FieldByName('ID').asinteger,'Simple equality');
|
|
CheckEquals('TestName3',FieldByName('NAME').asstring,'Simple equality');
|
|
next;
|
|
CheckTrue(EOF,'Simple equality');
|
|
|
|
// Check partial compare
|
|
Filter := '(name=''*Name5'')';
|
|
CheckFalse(EOF, 'Partial compare');
|
|
CheckEquals(5,FieldByName('ID').asinteger,'Partial compare');
|
|
CheckEquals('TestName5',FieldByName('NAME').asstring,'Partial compare');
|
|
next;
|
|
CheckTrue(EOF,'Partial compare');
|
|
|
|
// Check case-sensitivity
|
|
Filter := '(name=''*name3'')';
|
|
first;
|
|
CheckTrue(EOF,'Case-sensitive search');
|
|
|
|
FilterOptions:=[foCaseInsensitive];
|
|
Filter := '(name=''testname3'')';
|
|
first;
|
|
CheckFalse(EOF,'Case-insensitive search');
|
|
CheckEquals(3,FieldByName('ID').asinteger,'Case-insensitive search');
|
|
CheckEquals('TestName3',FieldByName('NAME').asstring,'Case-insensitive search');
|
|
next;
|
|
CheckTrue(EOF);
|
|
|
|
// Check case-insensitive partial compare
|
|
Filter := '(name=''*name3'')';
|
|
first;
|
|
CheckFalse(EOF, 'Case-insensitive partial compare');
|
|
CheckEquals(3,FieldByName('ID').asinteger, 'Case-insensitive partial compare');
|
|
CheckEquals('TestName3',FieldByName('NAME').asstring, 'Case-insensitive partial compare');
|
|
next;
|
|
CheckTrue(EOF);
|
|
|
|
// Multiple records with partial compare
|
|
Filter := '(name=''*name*'')';
|
|
first;
|
|
CheckFalse(EOF,'Partial compare multiple records');
|
|
CheckEquals(1,FieldByName('ID').asinteger,'Partial compare multiple records');
|
|
CheckEquals('TestName1',FieldByName('NAME').asstring,'Partial compare multiple records');
|
|
next;
|
|
CheckFalse(EOF,'Partial compare multiple records');
|
|
CheckEquals(2,FieldByName('ID').asinteger,'Partial compare multiple records');
|
|
CheckEquals('TestName2',FieldByName('NAME').asstring,'Partial compare multiple records');
|
|
|
|
// Invalid data with partial compare
|
|
Filter := '(name=''*neme*'')';
|
|
first;
|
|
CheckTrue(EOF,'Invalid data, partial compare');
|
|
|
|
// Multiple string filters
|
|
Filter := '(name=''*a*'') and (name=''*m*'')';
|
|
first;
|
|
CheckFalse(EOF,'Multiple string filters');
|
|
CheckEquals(1,FieldByName('ID').asinteger,'Multiple string filters');
|
|
CheckEquals('TestName1',FieldByName('NAME').asstring,'Multiple string filters');
|
|
next;
|
|
CheckFalse(EOF,'Multiple string filters');
|
|
CheckEquals(2,FieldByName('ID').asinteger,'Multiple string filters');
|
|
CheckEquals('TestName2',FieldByName('NAME').asstring,'Multiple string filters');
|
|
|
|
// Modify so we can use some tricky data
|
|
Filter := ''; //show all records again and allow edits
|
|
First;
|
|
Edit;
|
|
// Record 1=O'Malley
|
|
FieldByName('NAME').AsString := 'O''Malley';
|
|
Post;
|
|
|
|
Next;
|
|
Edit;
|
|
// Record 2="Magic" Mushroom
|
|
FieldByName('NAME').AsString := '"Magic" Mushroom';
|
|
Post;
|
|
|
|
Next;
|
|
Edit;
|
|
// Record 3=O'Malley's "Magic" Mushroom
|
|
FieldByName('NAME').AsString := 'O''Malley''s "Magic" Mushroom';
|
|
Post;
|
|
|
|
// Test searching on " which can be a delimiter
|
|
Filter := '(name=''*"Magic"*'')'; //should give record 2 and 3
|
|
first;
|
|
CheckFalse(EOF);
|
|
CheckEquals(2,FieldByName('ID').asinteger,'Search for strings with ", partial compare');
|
|
CheckEquals('"Magic" Mushroom',FieldByName('NAME').asstring,'Search for strings with ", partial compare');
|
|
next;
|
|
CheckFalse(EOF);
|
|
CheckEquals(3,FieldByName('ID').asinteger,'Search for strings with ", partial compare');
|
|
CheckEquals('O''Malley''s "Magic" Mushroom',FieldByName('NAME').asstring,'Search for strings with ", partial compare');
|
|
|
|
// Search for strings with ' escaped, partial compare delimited by '
|
|
Filter := '(name=''O''''Malley*'')'; //should give record 1 and 3
|
|
first;
|
|
CheckFalse(EOF);
|
|
CheckEquals(1,FieldByName('ID').asinteger,'Search for strings with '' escaped, partial compare delimited by ''');
|
|
CheckEquals('O''Malley',FieldByName('NAME').asstring,'Search for strings with '' escaped, partial compare delimited by ''');
|
|
next;
|
|
CheckFalse(EOF);
|
|
CheckEquals(3,FieldByName('ID').asinteger,'Search for strings with '' escaped, partial compare delimited by ''');
|
|
CheckEquals('O''Malley''s "Magic" Mushroom',FieldByName('NAME').asstring,'Search for strings with '' escaped, partial compare delimited by ''');
|
|
|
|
Close;
|
|
end;
|
|
end;
|
|
|
|
{$ifdef fpc}
|
|
procedure TTestBufDatasetDBBasics.TestIsEmpty;
|
|
begin
|
|
with DBConnector.GetNDataset(True,1) as TCustomBufDataset do
|
|
begin
|
|
open;
|
|
delete;
|
|
Resync([]);
|
|
ApplyUpdates;
|
|
CheckTrue(IsEmpty);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestSaveAsXML;
|
|
var ds : TDataset;
|
|
LoadDs: TCustomBufDataset;
|
|
begin
|
|
ds := DBConnector.GetNDataset(true,5);
|
|
|
|
ds.open;
|
|
TCustomBufDataset(ds).SaveToFile('test.xml');
|
|
ds.close;
|
|
|
|
LoadDs := TMyCustomBufDataset.Create(nil);
|
|
try
|
|
LoadDs.LoadFromFile('test.xml');
|
|
FTestXMLDatasetDefinition(LoadDS);
|
|
finally
|
|
LoadDS.free;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestFileNameProperty;
|
|
var ds1,ds2: TDataset;
|
|
begin
|
|
ds2 := nil;
|
|
ds1 := DBConnector.GetNDataset(true,5);
|
|
try
|
|
ds1.open;
|
|
TCustomBufDataset(ds1).FileName:='test.xml';
|
|
ds1.close;
|
|
|
|
ds2 := DBConnector.GetNDataset(True,7);
|
|
TCustomBufDataset(ds2).FileName:='test.xml';
|
|
ds2.Open;
|
|
FTestXMLDatasetDefinition(Ds2);
|
|
finally
|
|
TCustomBufDataset(ds1).FileName:='';
|
|
if assigned(ds2) then
|
|
TCustomBufDataset(ds2).FileName:='';
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestClientDatasetAsMemDataset;
|
|
var ds : TCustomBufDataset;
|
|
i : integer;
|
|
begin
|
|
ds := TMyCustomBufDataset.Create(nil);
|
|
try
|
|
DS.FieldDefs.Add('ID',ftInteger);
|
|
DS.FieldDefs.Add('NAME',ftString,50);
|
|
DS.CreateDataset;
|
|
DS.Open;
|
|
for i := 1 to 10 do
|
|
begin
|
|
ds.Append;
|
|
ds.FieldByName('ID').AsInteger := i;
|
|
ds.FieldByName('NAME').AsString := 'TestName' + inttostr(i);
|
|
DS.Post;
|
|
end;
|
|
ds.first;
|
|
for i := 1 to 10 do
|
|
begin
|
|
CheckEquals(i,ds.fieldbyname('ID').asinteger);
|
|
CheckEquals('TestName' + inttostr(i),ds.fieldbyname('NAME').AsString);
|
|
ds.next;
|
|
end;
|
|
CheckTrue(ds.EOF);
|
|
DS.Close;
|
|
|
|
finally
|
|
ds.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestBufDatasetCancelUpd;
|
|
var i : byte;
|
|
begin
|
|
with DBConnector.GetNDataset(5) as TCustomBufDataset do
|
|
begin
|
|
open;
|
|
next;
|
|
next;
|
|
|
|
edit;
|
|
FieldByName('name').AsString := 'changed';
|
|
post;
|
|
next;
|
|
delete;
|
|
|
|
CancelUpdates;
|
|
|
|
First;
|
|
|
|
for i := 1 to 5 do
|
|
begin
|
|
CheckEquals(i,fields[0].AsInteger);
|
|
CheckEquals('TestName'+inttostr(i),fields[1].AsString);
|
|
Next;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestBufDatasetCancelUpd1;
|
|
var i : byte;
|
|
begin
|
|
with DBConnector.GetNDataset(5) as TCustomBufDataset do
|
|
begin
|
|
open;
|
|
next;
|
|
next;
|
|
|
|
delete;
|
|
insert;
|
|
FieldByName('id').AsInteger := 100;
|
|
post;
|
|
|
|
CancelUpdates;
|
|
|
|
last;
|
|
|
|
for i := 5 downto 1 do
|
|
begin
|
|
CheckEquals(i,fields[0].AsInteger);
|
|
CheckEquals('TestName'+inttostr(i),fields[1].AsString);
|
|
Prior;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestMultipleDeleteUpdateBuffer;
|
|
var ds : TDataset;
|
|
begin
|
|
ds := DBConnector.GetNDataset(true,5);
|
|
|
|
ds.open;
|
|
with TCustomBufDataset(ds) do
|
|
begin
|
|
CheckEquals(0,ChangeCount);
|
|
edit;
|
|
fieldbyname('id').asinteger := 500;
|
|
fieldbyname('name').AsString := 'JoJo';
|
|
post;
|
|
CheckEquals(1,ChangeCount);
|
|
next; next;
|
|
Delete;
|
|
CheckEquals(2,ChangeCount);
|
|
Delete;
|
|
CheckEquals(3,ChangeCount);
|
|
CancelUpdates;
|
|
end;
|
|
ds.close;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestDoubleDelete;
|
|
var ds : TCustomBufDataset;
|
|
begin
|
|
ds := TCustomBufDataset(DBConnector.GetNDataset(true,5));
|
|
|
|
with ds do
|
|
begin
|
|
open;
|
|
next; next;
|
|
Delete;
|
|
Delete;
|
|
|
|
first;
|
|
CheckEquals(1,fieldbyname('id').AsInteger);
|
|
next;
|
|
CheckEquals(2,fieldbyname('id').AsInteger);
|
|
next;
|
|
CheckEquals(5,fieldbyname('id').AsInteger);
|
|
|
|
CancelUpdates;
|
|
|
|
first;
|
|
CheckEquals(1,fieldbyname('id').AsInteger);
|
|
next;
|
|
CheckEquals(2,fieldbyname('id').AsInteger);
|
|
next;
|
|
CheckEquals(3,fieldbyname('id').AsInteger);
|
|
next;
|
|
CheckEquals(4,fieldbyname('id').AsInteger);
|
|
next;
|
|
CheckEquals(5,fieldbyname('id').AsInteger);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestReadOnly;
|
|
var
|
|
ds: TCustomBufDataset;
|
|
begin
|
|
ds := DBConnector.GetFieldDataset as TCustomBufDataset;
|
|
with ds do
|
|
begin
|
|
ReadOnly:=true;
|
|
CheckFalse(CanModify);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestMergeChangeLog;
|
|
var
|
|
ds: TCustomBufDataset;
|
|
i: integer;
|
|
s, FN: string;
|
|
begin
|
|
ds := DBConnector.GetNDataset(5) as TCustomBufDataset;
|
|
with ds do
|
|
begin
|
|
open;
|
|
Edit;
|
|
i := fields[0].AsInteger;
|
|
s := fields[1].AsString;
|
|
fields[0].AsInteger:=64;
|
|
fields[1].AsString:='Changed1';
|
|
Post;
|
|
checkequals(fields[0].OldValue,i);
|
|
checkequals(fields[1].OldValue,s);
|
|
CheckEquals(ChangeCount,1);
|
|
Next;
|
|
Edit;
|
|
i := fields[0].AsInteger;
|
|
s := fields[1].AsString;
|
|
fields[0].AsInteger:=23;
|
|
fields[1].AsString:='Changed2';
|
|
Post;
|
|
checkequals(fields[0].OldValue,i);
|
|
checkequals(fields[1].OldValue,s);
|
|
CheckEquals(ChangeCount,2);
|
|
MergeChangeLog;
|
|
CheckEquals(ChangeCount,0);
|
|
checkequals(fields[0].OldValue,23);
|
|
checkequals(fields[1].OldValue,'Changed2');
|
|
end;
|
|
|
|
// Test handling of [Update]BlobBuffers in TBufDataset
|
|
ds := DBConnector.GetFieldDataset as TCustomBufDataset;
|
|
with ds do
|
|
begin
|
|
// Testing scenario: read some records, so blob data are added into FBlobBuffers,
|
|
// then update blob field, so element is added to FUpdateBlobBuffers, then read again some records
|
|
// so next elements are added to FBlobBuffers, then again update blob field
|
|
// DefaultBufferCount is 10
|
|
PacketRecords:=1;
|
|
Open;
|
|
FN := 'F'+FieldTypeNames[ftBlob];
|
|
First; Edit; FieldByName(FN).AsString:='b01'; Post;
|
|
RecNo:=11; Edit; FieldByName(FN).AsString:='b11'; Post;
|
|
Next ; Edit; FieldByName(FN).AsString:='b12'; Post;
|
|
Last;
|
|
MergeChangeLog;
|
|
First; CheckEquals('b01', FieldByName(FN).AsString);
|
|
RecNo:=11; CheckEquals('b11', FieldByName(FN).AsString);
|
|
Next; CheckEquals('b12', FieldByName(FN).AsString);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestRevertRecord;
|
|
begin
|
|
with DBConnector.GetNDataset(True,1) as TCustomBufDataset do
|
|
begin
|
|
Open;
|
|
// update value in one record and revert them
|
|
Edit;
|
|
FieldByName('ID').AsInteger := 100;
|
|
Post;
|
|
CheckEquals(100, FieldByName('ID').AsInteger);
|
|
RevertRecord;
|
|
CheckEquals(1, FieldByName('ID').AsInteger, 'Revert modified #1');
|
|
// append new record and delete prior and revert appended
|
|
AppendRecord([3,'']);
|
|
InsertRecord([2,'']);
|
|
Prior;
|
|
Delete; // 1st
|
|
Next;
|
|
RevertRecord; // 3rd
|
|
CheckEquals(2, FieldByName('ID').AsInteger, 'Revert inserted #1a');
|
|
RevertRecord; // 2nd
|
|
CheckTrue(Eof, 'Revert inserted #1b');
|
|
CancelUpdates; // restores 1st deleted record
|
|
CheckEquals(1, FieldByName('ID').AsInteger, 'CancelUpdates #1');
|
|
Close;
|
|
end;
|
|
|
|
with DBConnector.GetNDataset(False,0) as TCustomBufDataset do
|
|
begin
|
|
Open;
|
|
// insert one record and revert them
|
|
InsertRecord([1,'']);
|
|
RevertRecord;
|
|
CheckTrue(Eof);
|
|
CheckEquals(0, ChangeCount);
|
|
|
|
// insert two records and revert them in inverse order
|
|
AppendRecord([2,'']);
|
|
InsertRecord([1,'']); // this record in update-buffer is linked to 2
|
|
RevertRecord;
|
|
CheckEquals(2, FieldByName('ID').AsInteger);
|
|
CheckEquals(1, ChangeCount);
|
|
RevertRecord;
|
|
CheckTrue(Eof);
|
|
CheckEquals(0, ChangeCount);
|
|
|
|
// insert more records and some delete and some revert
|
|
AppendRecord([4,'']);
|
|
InsertRecord([3,'']);
|
|
InsertRecord([2,'']);
|
|
InsertRecord([1,'']);
|
|
CheckEquals(4, ChangeCount);
|
|
Delete; // 1
|
|
CheckEquals(4, ChangeCount);
|
|
Next; // 3
|
|
RevertRecord;
|
|
CheckEquals(4, FieldByName('ID').AsInteger);
|
|
CheckEquals(3, ChangeCount);
|
|
Prior; // 2
|
|
RevertRecord;
|
|
CheckEquals(4, FieldByName('ID').AsInteger);
|
|
CheckEquals(2, ChangeCount);
|
|
|
|
CancelUpdates;
|
|
CheckTrue(Eof);
|
|
CheckEquals(0, ChangeCount);
|
|
|
|
Close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.FTestXMLDatasetDefinition(ADataset: TDataset);
|
|
var i : integer;
|
|
begin
|
|
CheckEquals(2,ADataset.FieldDefs.Count);
|
|
CheckEquals(2,ADataset.Fields.Count);
|
|
CheckTrue(SameText('ID',ADataset.Fields[0].FieldName));
|
|
CheckTrue(SameText('NAME',ADataset.Fields[1].FieldName));
|
|
CheckEquals(ord(ftString), ord(ADataset.Fields[1].DataType), 'Incorrect FieldType');
|
|
i := 1;
|
|
while not ADataset.EOF do
|
|
begin
|
|
CheckEquals('TestName'+inttostr(i),ADataset.FieldByName('name').AsString);
|
|
ADataset.Next;
|
|
inc(i);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestAddIndexFieldType(AFieldType: TFieldType; ActiveDS : boolean);
|
|
var ds : TCustomBufDataset;
|
|
LastValue : Variant;
|
|
begin
|
|
ds := DBConnector.GetFieldDataset as TCustomBufDataset;
|
|
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
|
|
if AFieldType=ftString then
|
|
CheckTrue(AnsiCompareStr(VarToStr(LastValue),VarToStr(FieldByName('F'+FieldTypeNames[AfieldType]).AsString))<=0,'Forward, Correct string value')
|
|
else
|
|
CheckTrue(LastValue<=FieldByName('F'+FieldTypeNames[AfieldType]).AsVariant,'Forward, Correct variant value');
|
|
LastValue:=FieldByName('F'+FieldTypeNames[AfieldType]).AsVariant;
|
|
Next;
|
|
end;
|
|
|
|
while not bof do
|
|
begin
|
|
if AFieldType=ftString then
|
|
CheckTrue(AnsiCompareStr(VarToStr(LastValue),VarToStr(FieldByName('F'+FieldTypeNames[AfieldType]).AsString))>=0,'Backward, Correct string value')
|
|
else
|
|
CheckTrue(LastValue>=FieldByName('F'+FieldTypeNames[AfieldType]).AsVariant,'Backward, Correct variant value');
|
|
LastValue:=FieldByName('F'+FieldTypeNames[AfieldType]).AsVariant;
|
|
Prior;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestAddIndexSmallInt;
|
|
begin
|
|
TestAddIndexFieldType(ftSmallint,False);
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestAddIndexBoolean;
|
|
begin
|
|
TestAddIndexFieldType(ftBoolean,False);
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestAddIndexFloat;
|
|
begin
|
|
TestAddIndexFieldType(ftFloat,False);
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestAddIndexInteger;
|
|
begin
|
|
TestAddIndexFieldType(ftInteger,False);
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestAddIndexLargeInt;
|
|
begin
|
|
TestAddIndexFieldType(ftLargeint,False);
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestAddIndexDateTime;
|
|
begin
|
|
TestAddIndexFieldType(ftDateTime,False);
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestAddIndexCurrency;
|
|
begin
|
|
TestAddIndexFieldType(ftCurrency,False);
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestAddIndexBCD;
|
|
begin
|
|
TestAddIndexFieldType(ftBCD,False);
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestAddIndexFmtBCD;
|
|
begin
|
|
TestAddIndexFieldType(ftFmtBCD,False);
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestAddIndex;
|
|
var ds : TCustomBufDataset;
|
|
AFieldType : TFieldType;
|
|
FList : TStringList;
|
|
i : integer;
|
|
begin
|
|
ds := DBConnector.GetFieldDataset as TCustomBufDataset;
|
|
with ds do
|
|
begin
|
|
|
|
AFieldType:=ftString;
|
|
AddIndex('testindex','F'+FieldTypeNames[AfieldType],[]);
|
|
FList := TStringList.Create;
|
|
try
|
|
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
|
|
CheckEquals(flist[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
|
|
inc(i);
|
|
Next;
|
|
end;
|
|
|
|
while not bof do
|
|
begin
|
|
dec(i);
|
|
CheckEquals(flist[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
|
|
Prior;
|
|
end;
|
|
finally
|
|
flist.free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestAddDescIndex;
|
|
var ds : TCustomBufDataset;
|
|
AFieldType : TFieldType;
|
|
FList : TStringList;
|
|
i : integer;
|
|
begin
|
|
ds := DBConnector.GetFieldDataset as TCustomBufDataset;
|
|
with ds do
|
|
begin
|
|
|
|
AFieldType:=ftString;
|
|
AddIndex('testindex','F'+FieldTypeNames[AfieldType],[],'F'+FieldTypeNames[AfieldType]);
|
|
FList := TStringList.Create;
|
|
try
|
|
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:=FList.Count-1;
|
|
|
|
while not eof do
|
|
begin
|
|
CheckEquals(flist[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
|
|
dec(i);
|
|
Next;
|
|
end;
|
|
|
|
while not bof do
|
|
begin
|
|
inc(i);
|
|
CheckEquals(flist[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
|
|
Prior;
|
|
end;
|
|
finally
|
|
flist.free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestAddCaseInsIndex;
|
|
var ds : TCustomBufDataset;
|
|
AFieldType : TFieldType;
|
|
FList : TStringList;
|
|
i : integer;
|
|
begin
|
|
ds := DBConnector.GetFieldDataset as TCustomBufDataset;
|
|
with ds do
|
|
begin
|
|
|
|
AFieldType:=ftString;
|
|
AddIndex('testindex','F'+FieldTypeNames[AfieldType],[],'','F'+FieldTypeNames[AfieldType]);
|
|
FList := TStringList.Create;
|
|
try
|
|
FList.Sorted:=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
|
|
CheckEquals(flist[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
|
|
inc(i);
|
|
Next;
|
|
end;
|
|
|
|
while not bof do
|
|
begin
|
|
dec(i);
|
|
CheckEquals(flist[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
|
|
Prior;
|
|
end;
|
|
finally
|
|
FList.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestInactSwitchIndex;
|
|
// Test if the default-index is properly build when the active index is not
|
|
// the default-index while opening then dataset
|
|
var ds : TCustomBufDataset;
|
|
AFieldType : TFieldType;
|
|
i : integer;
|
|
begin
|
|
ds := DBConnector.GetFieldDataset as TCustomBufDataset;
|
|
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
|
|
CheckEquals(testStringValues[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
|
|
inc(i);
|
|
Next;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestAddIndexActiveDS;
|
|
begin
|
|
TestAddIndexFieldType(ftString,true);
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestAddIndexEditDS;
|
|
var ds : TCustomBufDataset;
|
|
LastValue : String;
|
|
begin
|
|
ds := DBConnector.GetNDataset(True,5) as TCustomBufDataset;
|
|
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:='';
|
|
while not eof do
|
|
begin
|
|
CheckTrue(AnsiCompareStr(LastValue,FieldByName('name').AsString)<=0);
|
|
LastValue:=FieldByName('name').AsString;
|
|
Next;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestIndexFieldNamesActive;
|
|
var ds : TCustomBufDataset;
|
|
AFieldType : TFieldType;
|
|
FList : TStringList;
|
|
i : integer;
|
|
begin
|
|
ds := DBConnector.GetFieldDataset as TCustomBufDataset;
|
|
with ds do
|
|
begin
|
|
AFieldType:=ftString;
|
|
FList := TStringList.Create;
|
|
try
|
|
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
|
|
CheckEquals(flist[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
|
|
inc(i);
|
|
Next;
|
|
end;
|
|
|
|
while not bof do
|
|
begin
|
|
dec(i);
|
|
CheckEquals(flist[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
|
|
Prior;
|
|
end;
|
|
|
|
CheckEquals('F'+FieldTypeNames[AfieldType],IndexFieldNames);
|
|
|
|
IndexFieldNames:='ID';
|
|
first;
|
|
i:=0;
|
|
|
|
while not eof do
|
|
begin
|
|
CheckEquals(testStringValues[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
|
|
inc(i);
|
|
Next;
|
|
end;
|
|
|
|
CheckEquals('ID',IndexFieldNames);
|
|
|
|
IndexFieldNames:='';
|
|
first;
|
|
i:=0;
|
|
|
|
while not eof do
|
|
begin
|
|
CheckEquals(testStringValues[i],FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
|
|
inc(i);
|
|
Next;
|
|
end;
|
|
|
|
CheckEquals('',IndexFieldNames);
|
|
finally
|
|
flist.free;
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestIndexCurRecord;
|
|
// Test if the currentrecord stays the same after an index change
|
|
var ds : TCustomBufDataset;
|
|
AFieldType : TFieldType;
|
|
i : integer;
|
|
OldID : Integer;
|
|
OldStringValue : string;
|
|
begin
|
|
ds := DBConnector.GetFieldDataset as TCustomBufDataset;
|
|
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';
|
|
|
|
CheckEquals(OldID,FieldByName('id').AsInteger);
|
|
CheckEquals(OldStringValue,FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
|
|
|
|
next;
|
|
CheckTrue(OldStringValue<=FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
|
|
prior;
|
|
prior;
|
|
CheckTrue(OldStringValue>=FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
|
|
|
|
OldID:=FieldByName('id').AsInteger;
|
|
OldStringValue:=FieldByName('F'+FieldTypeNames[AfieldType]).AsString;
|
|
|
|
IndexName:='';
|
|
|
|
CheckEquals(OldID,FieldByName('id').AsInteger);
|
|
CheckEquals(OldStringValue,FieldByName('F'+FieldTypeNames[AfieldType]).AsString);
|
|
|
|
next;
|
|
CheckEquals(OldID+1,FieldByName('ID').AsInteger);
|
|
prior;
|
|
prior;
|
|
CheckEquals(OldID-1,FieldByName('ID').AsInteger);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestAddDblIndex;
|
|
var ds : TCustomBufDataset;
|
|
LastInteger : Integer;
|
|
LastString : string;
|
|
begin
|
|
ds := DBConnector.GetFieldDataset as TCustomBufDataset;
|
|
with ds do
|
|
begin
|
|
|
|
AddIndex('testindex','F'+FieldTypeNames[ftString]+';F'+FieldTypeNames[ftInteger],[]);
|
|
open;
|
|
|
|
IndexName:='testindex';
|
|
first;
|
|
|
|
LastString:='';
|
|
while not eof do
|
|
begin
|
|
CheckTrue(AnsiCompareStr(FieldByName('F'+FieldTypeNames[ftString]).AsString,LastString)>=0);
|
|
LastString:= FieldByName('F'+FieldTypeNames[ftString]).AsString;
|
|
|
|
LastInteger:=-MaxInt;
|
|
while (FieldByName('F'+FieldTypeNames[ftString]).AsString=LastString) and not eof do
|
|
begin
|
|
CheckTrue(FieldByName('F'+FieldTypeNames[ftInteger]).AsInteger>=LastInteger);
|
|
LastInteger:=FieldByName('F'+FieldTypeNames[ftInteger]).AsInteger;
|
|
next;
|
|
end;
|
|
end;
|
|
while not bof do
|
|
begin
|
|
CheckTrue(AnsiCompareStr(FieldByName('F'+FieldTypeNames[ftString]).AsString,LastString)<=0);
|
|
LastString:= FieldByName('F'+FieldTypeNames[ftString]).AsString;
|
|
|
|
LastInteger:=+MaxInt;
|
|
while (FieldByName('F'+FieldTypeNames[ftString]).AsString=LastString) and not bof do
|
|
begin
|
|
CheckTrue(FieldByName('F'+FieldTypeNames[ftInteger]).AsInteger<=LastInteger);
|
|
LastInteger:=FieldByName('F'+FieldTypeNames[ftInteger]).AsInteger;
|
|
prior;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestIndexEditRecord;
|
|
// Tests index sorting for string field type by
|
|
// editing an existing record in the middle
|
|
// with a value at the end of the alphabet
|
|
var ds : TCustomBufDataset;
|
|
AFieldType : TFieldType;
|
|
OldStringValue : string;
|
|
begin
|
|
ds := DBConnector.GetFieldDataset as TCustomBufDataset;
|
|
with ds do
|
|
begin
|
|
AFieldType:=ftString;
|
|
|
|
AddIndex('testindex','F'+FieldTypeNames[AfieldType],[]);
|
|
IndexName:='testindex';
|
|
Open;
|
|
OldStringValue:=FieldByName('F'+FieldTypeNames[AfieldType]).AsString;
|
|
next; //Now on record 1
|
|
CheckTrue(AnsiCompareStr(OldStringValue,FieldByName('F'+FieldTypeNames[AfieldType]).AsString)<=0,'Record 0 must be smaller than record 1 with asc sorted index');
|
|
OldStringValue:=FieldByName('F'+FieldTypeNames[AfieldType]).AsString;
|
|
next; //Now on record 2
|
|
CheckTrue(AnsiCompareStr(OldStringValue,FieldByName('F'+FieldTypeNames[AfieldType]).AsString)<=0,'Record 1 must be smaller than record 2 with asc sorted index');
|
|
|
|
prior; //Now on record 1
|
|
edit;
|
|
FieldByName('F'+FieldTypeNames[AfieldType]).AsString := 'ZZZ'; //should be sorted last
|
|
post;
|
|
prior; // Now on record 0
|
|
// Check ZZZ is sorted on/after record 0
|
|
CheckTrue(AnsiCompareStr('ZZZ',FieldByName('F'+FieldTypeNames[AfieldType]).AsString)>=0, 'Prior>');
|
|
next;
|
|
next; // Now on record 2
|
|
// Check ZZZ is sorted on/before record 2
|
|
CheckTrue(AnsiCompareStr('ZZZ',FieldByName('F'+FieldTypeNames[AfieldType]).AsString)<=0, 'Next<');
|
|
close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestIndexAppendRecord;
|
|
var i: integer;
|
|
LastValue: string;
|
|
begin
|
|
// start with empty dataset
|
|
with DBConnector.GetNDataset(true,0) as TCustomBufDataset do
|
|
begin
|
|
MaxIndexesCount:=4;
|
|
// add index to closed dataset with no data
|
|
AddIndex('testindex','NAME',[]);
|
|
IndexName:='testindex';
|
|
Open;
|
|
// empty dataset and other than default index (default_order) active
|
|
CheckTrue(BOF, 'No BOF when opening empty dataset');
|
|
CheckTrue(EOF, 'No EOF when opening empty dataset');
|
|
// append data at end
|
|
for i:=20 downto 0 do
|
|
AppendRecord([i, inttostr(i)]);
|
|
// insert data at begining
|
|
IndexName:='';
|
|
First;
|
|
for i:=21 to 22 do
|
|
InsertRecord([i, inttostr(i)]);
|
|
|
|
// swith to index and check if records are ordered
|
|
IndexName := 'testindex';
|
|
LastValue := '';
|
|
First;
|
|
for i:=22 downto 0 do
|
|
begin
|
|
CheckEquals(23-i, RecNo, 'testindex.RecNo:');
|
|
CheckTrue(AnsiCompareStr(LastValue,Fields[1].AsString) < 0, 'testindex.LastValue>=CurrValue');
|
|
LastValue := Fields[1].AsString;
|
|
Next;
|
|
end;
|
|
CheckTrue(EOF, 'testindex.No EOF after last record');
|
|
|
|
// switch back to default index (unordered)
|
|
IndexName:='';
|
|
First;
|
|
for i:=22 downto 0 do
|
|
begin
|
|
CheckEquals(23-i, RecNo, 'index[0].RecNo:');
|
|
CheckEquals(i, Fields[0].AsInteger, 'index[0].Fields[0].Value:');
|
|
Next;
|
|
end;
|
|
CheckTrue(EOF, 'index[0].No EOF after last record');
|
|
|
|
// add index to opened dataset with data
|
|
AddIndex('testindex2','ID',[]);
|
|
IndexName:='testindex2';
|
|
First;
|
|
for i:=0 to 22 do
|
|
begin
|
|
CheckEquals(1+i, RecNo, 'index2.RecNo:');
|
|
CheckEquals(i, Fields[0].AsInteger, 'index2.Fields[0].Value:');
|
|
Next;
|
|
end;
|
|
CheckTrue(EOF, 'index2.No EOF after last record');
|
|
|
|
Close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestIndexFieldNames;
|
|
var ds : TCustomBufDataset;
|
|
AFieldType : TFieldType;
|
|
PrevValue : String;
|
|
begin
|
|
ds := DBConnector.GetFieldDataset as TCustomBufDataset;
|
|
with ds do
|
|
begin
|
|
AFieldType:=ftString;
|
|
IndexFieldNames:='F'+FieldTypeNames[AfieldType];
|
|
open;
|
|
PrevValue:='';
|
|
while not eof do
|
|
begin
|
|
CheckTrue(AnsiCompareStr(FieldByName('F'+FieldTypeNames[AfieldType]).AsString,PrevValue)>=0,IntToStr(RecNo)+': '+FieldByName('F'+FieldTypeNames[AfieldType]).AsString+'>='+PrevValue+' ?');
|
|
PrevValue:=FieldByName('F'+FieldTypeNames[AfieldType]).AsString;
|
|
Next;
|
|
end;
|
|
CheckEquals('F'+FieldTypeNames[AfieldType],IndexFieldNames);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestBufDatasetDBBasics.TestIndexFieldNamesClosed;
|
|
var s : string;
|
|
bufds: TCustomBufDataset;
|
|
begin
|
|
bufds := DBConnector.GetNDataset(5) as TCustomBufDataset;
|
|
s := bufds.IndexFieldNames;
|
|
s := bufds.IndexName;
|
|
CheckEquals('',S,'Default index name');
|
|
bufds.CompareBookmarks(nil,nil);
|
|
end;
|
|
{$endif fpc}
|
|
|
|
procedure TTestCursorDBBasics.TestFirst;
|
|
var i : integer;
|
|
begin
|
|
with DBConnector.GetNDataset(true,14) do
|
|
begin
|
|
open;
|
|
CheckEquals(1,FieldByName('ID').AsInteger);
|
|
First;
|
|
CheckEquals(1,FieldByName('ID').AsInteger);
|
|
next;
|
|
CheckEquals(2,FieldByName('ID').AsInteger);
|
|
First;
|
|
CheckEquals(1,FieldByName('ID').AsInteger);
|
|
for i := 0 to 12 do
|
|
next;
|
|
CheckEquals(14,FieldByName('ID').AsInteger);
|
|
First;
|
|
CheckEquals(1,FieldByName('ID').AsInteger);
|
|
close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestEofAfterFirst;
|
|
begin
|
|
with DBConnector.GetNDataset(0) do
|
|
begin
|
|
open;
|
|
CheckTrue(eof);
|
|
CheckTrue(BOF);
|
|
first;
|
|
CheckTrue(eof);
|
|
CheckTrue(BOF);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestFieldDefinition(AFieldType: TFieldType; ADataSize: integer);
|
|
var
|
|
ADataSet: TDataset;
|
|
AField: TField;
|
|
i: integer;
|
|
begin
|
|
TestFieldDefinition(AFieldType, ADataSize, ADataSet, AField);
|
|
|
|
for i := 0 to testValuesCount-1 do
|
|
begin
|
|
CheckEquals(testValues[AFieldType,i], AField.AsString);
|
|
ADataSet.Next;
|
|
end;
|
|
ADataSet.Close;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestFieldDefinition(AFieldType: TFieldType; ADataSize: integer; out ADS: TDataset; out AFld: TField);
|
|
begin
|
|
ADS := DBConnector.GetFieldDataset;
|
|
ADS.Open;
|
|
|
|
AFld := ADS.FindField('F'+FieldTypeNames[AFieldType]);
|
|
|
|
{$ifdef fpc}
|
|
if not assigned (AFld) then
|
|
Ignore('Fields of the type ' + FieldTypeNames[AFieldType] + ' are not supported by this type of dataset');
|
|
{$endif fpc}
|
|
if ADataSize <> -1 then
|
|
TestFieldDefinition(AFld, AFieldType, ADataSize);
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestFieldDefinition(AFld: TField; AFieldType: TFieldType; ADataSize: integer);
|
|
begin
|
|
CheckEquals(FieldTypeNames[AFieldType], FieldTypeNames[AFld.DataType], 'DataType');
|
|
CheckEquals(ADatasize, AFld.DataSize, 'DataSize');
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportIntegerFields;
|
|
|
|
var i : integer;
|
|
DS : TDataset;
|
|
Fld : TField;
|
|
DbfTableLevel: integer;
|
|
|
|
begin
|
|
if (uppercase(dbconnectorname)='DBF') then
|
|
begin
|
|
DbfTableLevel:=strtointdef(dbconnectorparams,4);
|
|
if not(DBFTableLevel in [7,30]) then
|
|
Ignore('TDBF: only Visual Foxpro and DBase7 support full integer range.');
|
|
end;
|
|
|
|
TestFieldDefinition(ftInteger,-1,DS,Fld);
|
|
|
|
for i := 0 to testValuesCount-1 do
|
|
begin
|
|
CheckEquals(testIntValues[i],Fld.AsInteger);
|
|
DS.Next;
|
|
end;
|
|
|
|
TestFieldDefinition(Fld,ftInteger,4);
|
|
|
|
DS.Close;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportSmallIntFields;
|
|
|
|
var i : byte;
|
|
ds : TDataset;
|
|
Fld : TField;
|
|
|
|
begin
|
|
if (uppercase(dbconnectorname)='DBF') then
|
|
Ignore('TDBF: Smallint support only from -999 to 9999');
|
|
|
|
TestFieldDefinition(ftSmallint,-1,ds,Fld);
|
|
|
|
for i := 0 to testValuesCount-1 do
|
|
begin
|
|
CheckEquals(testSmallIntValues[i],Fld.AsInteger);
|
|
ds.Next;
|
|
end;
|
|
|
|
TestFieldDefinition(Fld,ftSmallint,2);
|
|
|
|
ds.Close;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportWordFields;
|
|
var i : byte;
|
|
ds : TDataset;
|
|
Fld : TField;
|
|
|
|
begin
|
|
TestFieldDefinition(ftWord,2,ds,Fld);
|
|
|
|
for i := 0 to testValuesCount-1 do
|
|
begin
|
|
CheckEquals(testWordValues[i],Fld.AsInteger);
|
|
ds.Next;
|
|
end;
|
|
|
|
ds.Close;
|
|
end;
|
|
|
|
|
|
procedure TTestDBBasics.TestSupportStringFields;
|
|
|
|
var i : byte;
|
|
ds : TDataset;
|
|
Fld : TField;
|
|
|
|
begin
|
|
TestFieldDefinition(ftString, 10*DBConnector.CharSize+1, ds, Fld);
|
|
|
|
for i := 0 to testValuesCount-1 do
|
|
begin
|
|
if (uppercase(dbconnectorname)<>'DBF') then
|
|
CheckEquals(testStringValues[i],Fld.AsString)
|
|
else {DBF right-trims spaces in string fields }
|
|
CheckEquals(TrimRight(testStringValues[i]),Fld.AsString);
|
|
ds.Next;
|
|
end;
|
|
|
|
ds.Close;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportBooleanFields;
|
|
|
|
var i : byte;
|
|
ds : TDataset;
|
|
Fld : TField;
|
|
|
|
begin
|
|
TestFieldDefinition(ftBoolean,2,ds,Fld);
|
|
|
|
for i := 0 to testValuesCount-1 do
|
|
begin
|
|
CheckEquals(testBooleanValues[i],Fld.AsBoolean);
|
|
ds.Next;
|
|
end;
|
|
|
|
ds.Close;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportBooleanFieldDisplayValue;
|
|
var
|
|
ds : TDataset;
|
|
Fld : TField;
|
|
BoolFld : TBooleanField absolute Fld;
|
|
begin
|
|
TestFieldDefinition(ftBoolean,2,ds,Fld);
|
|
CheckEquals(TBooleanField,Fld.ClassType,'Correct class');
|
|
BoolFld.DisplayValues:='+';
|
|
if ds.IsUniDirectional then
|
|
begin
|
|
CheckEquals('+',Fld.DisplayText,'Correct true'); // 1st record
|
|
ds.Next;
|
|
CheckEquals('',Fld.DisplayText,'Correct false'); // 2nd record
|
|
end
|
|
else
|
|
begin
|
|
ds.Edit;
|
|
Fld.AsBoolean:=True;
|
|
CheckEquals('+',Fld.DisplayText,'Correct true');
|
|
Fld.AsBoolean:=False;
|
|
CheckEquals('',Fld.DisplayText,'Correct false');
|
|
Fld.AsString:='+';
|
|
CheckEquals(true,Fld.AsBoolean,'Correct true');
|
|
Fld.AsString:='';
|
|
CheckEquals(False,Fld.AsBoolean,'Correct False');
|
|
BoolFld.DisplayValues:=';-';
|
|
CheckEquals('-',Fld.DisplayText,'Correct false');
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportFloatFields;
|
|
|
|
var i : byte;
|
|
ds : TDataset;
|
|
Fld : TField;
|
|
|
|
begin
|
|
TestFieldDefinition(ftFloat,8,ds,Fld);
|
|
|
|
for i := 0 to testValuesCount-1 do
|
|
begin
|
|
CheckEquals(testFloatValues[i],Fld.AsFloat);
|
|
ds.Next;
|
|
end;
|
|
|
|
ds.Close;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportLargeIntFields;
|
|
|
|
var i : byte;
|
|
ds : TDataset;
|
|
Fld : TField;
|
|
|
|
begin
|
|
TestFieldDefinition(ftLargeint,-1,ds,Fld);
|
|
|
|
for i := 0 to testValuesCount-1 do
|
|
begin
|
|
CheckEquals(testLargeIntValues[i],Fld.AsLargeInt);
|
|
ds.Next;
|
|
end;
|
|
|
|
TestFieldDefinition(Fld,ftLargeint,8);
|
|
|
|
ds.Close;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportDateFields;
|
|
|
|
var i : byte;
|
|
ds : TDataset;
|
|
Fld : TField;
|
|
|
|
begin
|
|
TestFieldDefinition(ftDate,8,ds,Fld);
|
|
|
|
for i := 0 to testValuesCount-1 do
|
|
begin
|
|
CheckEquals(testDateValues[i], FormatDateTime('yyyy/mm/dd', Fld.AsDateTime, DBConnector.FormatSettings));
|
|
ds.Next;
|
|
end;
|
|
|
|
ds.Close;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportTimeFields;
|
|
var i : byte;
|
|
ds : TDataset;
|
|
Fld : TField;
|
|
begin
|
|
TestFieldDefinition(ftTime,8,ds,Fld);
|
|
|
|
for i := 0 to testValuesCount-1 do
|
|
begin
|
|
CheckEquals(testTimeValues[i],DateTimeToTimeString(fld.AsDateTime));
|
|
ds.Next;
|
|
end;
|
|
|
|
ds.Close;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportDateTimeFields;
|
|
var i : integer;
|
|
DS : TDataSet;
|
|
Fld : TField;
|
|
begin
|
|
TestFieldDefinition(ftDateTime,8,DS,Fld);
|
|
|
|
for i := 0 to testValuesCount-1 do
|
|
begin
|
|
CheckEquals(testValues[ftDateTime,i], DateTimeToStr(Fld.AsDateTime, DBConnector.FormatSettings));
|
|
DS.Next;
|
|
end;
|
|
|
|
DS.Close;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportCurrencyFields;
|
|
|
|
var i : byte;
|
|
ds : TDataset;
|
|
Fld : TField;
|
|
|
|
begin
|
|
if (uppercase(dbconnectorname)='DBF') then
|
|
Ignore('This test does not apply to TDBF as they store currency in BCD fields.');
|
|
|
|
TestFieldDefinition(ftCurrency,8,ds,Fld);
|
|
|
|
for i := 0 to testValuesCount-1 do
|
|
begin
|
|
CheckEquals(testCurrencyValues[i],Fld.AsCurrency);
|
|
CheckEquals(testCurrencyValues[i],Fld.AsFloat);
|
|
ds.Next;
|
|
end;
|
|
|
|
ds.Close;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportBCDFields;
|
|
|
|
var i : byte;
|
|
ds : TDataset;
|
|
Fld : TField;
|
|
|
|
begin
|
|
TestFieldDefinition(ftBCD,-1,ds,Fld);
|
|
|
|
for i := 0 to testValuesCount-1 do
|
|
begin
|
|
CheckEquals(testCurrencyValues[i], Fld.AsCurrency, 'AsCurrency');
|
|
CheckEquals(CurrToStr(testCurrencyValues[i]), Fld.AsString, 'AsString');
|
|
CheckEquals(testCurrencyValues[i], Fld.AsFloat, 'AsFloat');
|
|
ds.Next;
|
|
end;
|
|
|
|
TestFieldDefinition(Fld, ftBCD, 8);
|
|
|
|
ds.Close;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportFmtBCDFields;
|
|
var i : byte;
|
|
ds : TDataset;
|
|
Fld : TField;
|
|
|
|
begin
|
|
TestFieldDefinition(ftFMTBcd,sizeof(TBCD),ds,Fld);
|
|
|
|
for i := 0 to testValuesCount-1 do
|
|
begin
|
|
CheckEquals(testFmtBCDValues[i], BCDToStr(Fld.AsBCD,DBConnector.FormatSettings), 'AsBCD');
|
|
CheckEquals(StrToFloat(testFmtBCDValues[i],DBConnector.FormatSettings), Fld.AsFloat, 1e-12, 'AsFloat');
|
|
ds.Next;
|
|
end;
|
|
|
|
ds.Close;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportFixedStringFields;
|
|
var i : byte;
|
|
ds : TDataset;
|
|
Fld : TField;
|
|
|
|
begin
|
|
TestFieldDefinition(ftFixedChar, 10*DBConnector.CharSize+1, ds, Fld);
|
|
|
|
for i := 0 to testValuesCount-1 do
|
|
begin
|
|
if Fld.IsNull then // If the field is null, .AsString always returns an empty, non-padded string
|
|
CheckEquals(testStringValues[i],Fld.AsString)
|
|
else
|
|
{$ifdef fpc}
|
|
CheckEquals(PadRight(testStringValues[i],10),Fld.AsString);
|
|
{$else fpc}
|
|
CheckEquals(testStringValues[i],Fld.AsString);
|
|
{$endif fpc}
|
|
ds.Next;
|
|
end;
|
|
ds.Close;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportBlobFields;
|
|
begin
|
|
TestFieldDefinition(ftBlob,0);
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportMemoFields;
|
|
begin
|
|
TestFieldDefinition(ftMemo,0);
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportByteFields;
|
|
begin
|
|
TestFieldDefinition(ftByte, SizeOf(Byte));
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportShortIntFields;
|
|
begin
|
|
TestFieldDefinition(ftShortInt, SizeOf(ShortInt));
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportExtendedFields;
|
|
begin
|
|
TestFieldDefinition(ftExtended, SizeOf(Extended));
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestSupportSingleFields;
|
|
begin
|
|
TestFieldDefinition(ftSingle, SizeOf(Single));
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestBlobBlobType;
|
|
// Verifies whether all created blob types actually have blobtypes that fall
|
|
// into the blobtype range (subset of datatype enumeration)
|
|
var
|
|
ds: TDataSet;
|
|
i:integer;
|
|
begin
|
|
ds := DBConnector.GetFieldDataset;
|
|
with ds do
|
|
begin;
|
|
Open;
|
|
for i:=0 to Fields.Count-1 do
|
|
begin
|
|
// This should only apply to blob types
|
|
if Fields[i].DataType in ftBlobTypes then
|
|
begin
|
|
// Type should certainly fall into wider old style, imprecise TBlobType
|
|
if not(TBlobField(Fields[i]).BlobType in ftBlobTypes) then
|
|
fail('BlobType for field '+
|
|
Fields[i].FieldName+' is not in old wide incorrect TBlobType range. Actual value: '+
|
|
inttostr(word(TBlobField(Fields[i]).BlobType)));
|
|
//.. it should also fall into the narrow ftBlobTypes
|
|
if not(TBlobField(Fields[i]).BlobType in ftBlobTypes) then
|
|
fail('BlobType for field '+
|
|
Fields[i].FieldName+' is not in ftBlobType range. Actual value: '+
|
|
inttostr(word(TBlobField(Fields[i]).BlobType)));
|
|
end;
|
|
end;
|
|
Close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestCalculatedField_OnCalcfields(DataSet: TDataSet);
|
|
begin
|
|
case dataset.fieldbyname('ID').asinteger of
|
|
1 : dataset.fieldbyname('CALCFLD').AsInteger := 5;
|
|
2 : dataset.fieldbyname('CALCFLD').AsInteger := 70000;
|
|
3 : dataset.fieldbyname('CALCFLD').Clear;
|
|
4 : dataset.fieldbyname('CALCFLD').AsInteger := 1234;
|
|
10 : dataset.fieldbyname('CALCFLD').Clear;
|
|
else
|
|
dataset.fieldbyname('CALCFLD').AsInteger := 1;
|
|
end;
|
|
CheckTrue(DataSet.State=dsCalcFields, 'State');
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestCalculatedField;
|
|
var ds : TDataset;
|
|
AFld1, AFld2, AFld3 : Tfield;
|
|
begin
|
|
ds := DBConnector.GetNDataset(True,5);
|
|
with ds do
|
|
begin
|
|
AFld1 := TIntegerField.Create(ds);
|
|
AFld1.FieldName := 'ID';
|
|
AFld1.DataSet := ds;
|
|
|
|
AFld2 := TStringField.Create(ds);
|
|
AFld2.FieldName := 'NAME';
|
|
AFld2.DataSet := ds;
|
|
|
|
AFld3 := TIntegerField.Create(ds);
|
|
AFld3.FieldName := 'CALCFLD';
|
|
AFld3.DataSet := ds;
|
|
Afld3.FieldKind := fkCalculated;
|
|
|
|
CheckEquals(3,FieldCount);
|
|
ds.OnCalcFields := TestcalculatedField_OnCalcfields;
|
|
open;
|
|
CheckEquals(1, FieldByName('ID').AsInteger);
|
|
CheckEquals(5, FieldByName('CALCFLD').AsInteger);
|
|
next;
|
|
CheckEquals(70000,FieldByName('CALCFLD').AsInteger);
|
|
next;
|
|
CheckTrue(FieldByName('CALCFLD').IsNull, '#3 Null');
|
|
next;
|
|
CheckEquals(1234,FieldByName('CALCFLD').AsInteger);
|
|
if IsUniDirectional then
|
|
// The CanModify property is always False, so attempts to put the dataset into edit mode always fail
|
|
CheckException(Edit, EDatabaseError)
|
|
else
|
|
begin
|
|
Edit;
|
|
FieldByName('ID').AsInteger := 10;
|
|
Post;
|
|
CheckTrue(FieldByName('CALCFLD').IsNull, '#10 Null');
|
|
end;
|
|
close;
|
|
AFld1.Free;
|
|
AFld2.Free;
|
|
AFld3.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestCanModifySpecialFields;
|
|
var ds : TDataset;
|
|
lds : TDataset;
|
|
fld : TField;
|
|
begin
|
|
lds := DBConnector.GetNDataset(10);
|
|
ds := DBConnector.GetNDataset(5);
|
|
with ds do
|
|
begin
|
|
Fld := TIntegerField.Create(ds);
|
|
Fld.FieldName:='ID';
|
|
Fld.DataSet:=ds;
|
|
|
|
Fld := TStringField.Create(ds);
|
|
Fld.FieldName:='LookupFld';
|
|
Fld.FieldKind:=fkLookup;
|
|
Fld.DataSet:=ds;
|
|
Fld.LookupDataSet:=lds;
|
|
Fld.LookupResultField:='NAME';
|
|
Fld.LookupKeyFields:='ID';
|
|
Fld.KeyFields:='ID';
|
|
|
|
lds.Open;
|
|
Open;
|
|
if IsUniDirectional then
|
|
// The CanModify property is always False for UniDirectional datasets
|
|
CheckFalse(FieldByName('ID').CanModify)
|
|
else
|
|
CheckTrue(FieldByName('ID').CanModify);
|
|
CheckFalse(FieldByName('LookupFld').CanModify);
|
|
CheckFalse(FieldByName('ID').ReadOnly);
|
|
CheckFalse(FieldByName('LookupFld').ReadOnly);
|
|
|
|
CheckEquals(1,FieldByName('ID').AsInteger);
|
|
if IsUniDirectional then
|
|
// Lookup fields are not supported by UniDirectional datasets
|
|
CheckTrue(FieldByName('LookupFld').IsNull)
|
|
else
|
|
CheckEquals('TestName1',FieldByName('LookupFld').AsString);
|
|
Next;
|
|
Next;
|
|
CheckEquals(3,FieldByName('ID').AsInteger);
|
|
if IsUniDirectional then
|
|
CheckTrue(FieldByName('LookupFld').IsNull)
|
|
else
|
|
CheckEquals('TestName3',FieldByName('LookupFld').AsString);
|
|
|
|
Close;
|
|
lds.Close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestDoubleClose;
|
|
begin
|
|
with DBConnector.GetNDataset(1) do
|
|
begin
|
|
close;
|
|
close;
|
|
open;
|
|
close;
|
|
close;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestFieldDefsUpdate;
|
|
begin
|
|
// FieldDefs.Update is called also by Lazarus IDE Fields editor
|
|
with DBConnector.GetNDataset(0) do
|
|
begin
|
|
// call Update on closed unprepared dataset
|
|
FieldDefs.Update;
|
|
CheckEquals(2, FieldDefs.Count);
|
|
end;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestAssignFieldftString;
|
|
var AParam : TParam;
|
|
AField : TField;
|
|
begin
|
|
AParam := TParam.Create(nil);
|
|
|
|
with DBConnector.GetNDataset(1) do
|
|
begin
|
|
open;
|
|
AField := fieldbyname('name');
|
|
AParam.AssignField(AField);
|
|
CheckEquals(ord(ftString), ord(AParam.DataType), 'DataType');
|
|
close;
|
|
end;
|
|
AParam.Free;
|
|
end;
|
|
|
|
procedure TTestDBBasics.TestAssignFieldftFixedChar;
|
|
var AParam : TParam;
|
|
AField : TField;
|
|
begin
|
|
AParam := TParam.Create(nil);
|
|
with DBConnector.GetNDataset(1) do
|
|
begin
|
|
open;
|
|
AField := fieldbyname('name');
|
|
(AField as tstringfield).FixedChar := true;
|
|
AParam.AssignField(AField);
|
|
CheckEquals(ord(ftFixedChar), ord(AParam.DataType), 'DataType');
|
|
close;
|
|
end;
|
|
AParam.Free;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestBug7007;
|
|
|
|
var
|
|
datalink1: tdatalink;
|
|
datasource1: tdatasource;
|
|
query1: TDataSet;
|
|
|
|
begin
|
|
query1:= DBConnector.GetNDataset(6);
|
|
datalink1:= TTestDataLink.create;
|
|
datasource1:= tdatasource.create(nil);
|
|
try
|
|
datalink1.datasource:= datasource1;
|
|
datasource1.dataset:= query1;
|
|
datalink1.datasource:= datasource1;
|
|
|
|
DataEvents := '';
|
|
query1.open;
|
|
datalink1.buffercount:= query1.recordcount;
|
|
CheckEquals('deUpdateState:0;',DataEvents);
|
|
CheckEquals(0, datalink1.ActiveRecord);
|
|
CheckEquals(6, datalink1.RecordCount);
|
|
CheckEquals(6, query1.RecordCount);
|
|
CheckEquals(1, query1.RecNo);
|
|
|
|
DataEvents := '';
|
|
query1.append;
|
|
CheckEquals('deCheckBrowseMode:0;deUpdateState:0;deDataSetChange:0;DataSetChanged;',DataEvents);
|
|
CheckEquals(5, datalink1.ActiveRecord);
|
|
CheckEquals(6, datalink1.RecordCount);
|
|
CheckEquals(6, query1.RecordCount);
|
|
CheckTrue(query1.RecNo in [0,7]);
|
|
|
|
DataEvents := '';
|
|
query1.cancel;
|
|
CheckEquals('deCheckBrowseMode:0;deUpdateState:0;deDataSetChange:0;DataSetChanged;',DataEvents);
|
|
CheckEquals(5, datalink1.ActiveRecord);
|
|
CheckEquals(6, datalink1.RecordCount);
|
|
CheckEquals(6, query1.RecordCount);
|
|
CheckEquals(6, query1.RecNo);
|
|
finally
|
|
datalink1.free;
|
|
datasource1.free;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestBug6893;
|
|
var
|
|
datalink1: tdatalink;
|
|
datasource1: tdatasource;
|
|
query1: TDataSet;
|
|
|
|
begin
|
|
query1:= DBConnector.GetNDataset(25);
|
|
datalink1:= TDataLink.create;
|
|
datasource1:= tdatasource.create(nil);
|
|
try
|
|
datalink1.datasource:= datasource1;
|
|
datasource1.dataset:= query1;
|
|
|
|
datalink1.buffercount:= 5;
|
|
query1.active := true;
|
|
query1.MoveBy(20);
|
|
{$ifdef fpc}
|
|
CheckEquals(5, THackDataLink(datalink1).Firstrecord);
|
|
CheckEquals(4, datalink1.ActiveRecord);
|
|
CheckEquals(21, query1.RecNo);
|
|
|
|
query1.active := False;
|
|
|
|
CheckEquals(0, THackDataLink(datalink1).Firstrecord);
|
|
CheckEquals(0, datalink1.ActiveRecord);
|
|
|
|
query1.active := true;
|
|
|
|
CheckEquals(0, THackDataLink(datalink1).Firstrecord);
|
|
CheckEquals(0, datalink1.ActiveRecord);
|
|
CheckEquals(1, query1.RecNo);
|
|
{$endif fpc}
|
|
|
|
finally
|
|
datalink1.free;
|
|
datasource1.free;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestCursorDBBasics.TestNullAtOpen;
|
|
begin
|
|
with dbconnector.getndataset(0) do
|
|
begin
|
|
active:= true;
|
|
CheckTrue(fieldbyname('id').IsNull,'Field isn''t NULL on a just-opened empty dataset');
|
|
append;
|
|
CheckTrue(fieldbyname('id').IsNull,'Field isn''t NULL after append on an empty dataset');
|
|
fieldbyname('id').asinteger:= 123;
|
|
cancel;
|
|
CheckTrue(fieldbyname('id').IsNull,'Field isn''t NULL after cancel');
|
|
end;
|
|
end;
|
|
|
|
{ TDBBasicsUniDirectionalTestSetup }
|
|
{$ifdef fpc}
|
|
procedure TDBBasicsUniDirectionalTestSetup.OneTimeSetup;
|
|
begin
|
|
inherited OneTimeSetup;
|
|
DBConnector.TestUniDirectional:=true;
|
|
end;
|
|
|
|
procedure TDBBasicsUniDirectionalTestSetup.OneTimeTearDown;
|
|
begin
|
|
DBConnector.TestUniDirectional:=false;
|
|
inherited OneTimeTearDown;
|
|
end;
|
|
{$endif fpc}
|
|
|
|
|
|
initialization
|
|
{$ifdef fpc}
|
|
RegisterTestDecorator(TDBBasicsTestSetup, TTestDBBasics);
|
|
RegisterTestDecorator(TDBBasicsTestSetup, TTestCursorDBBasics);
|
|
|
|
// The SQL connectors are descendents of bufdataset and therefore benefit from testing:
|
|
if (uppercase(dbconnectorname)='SQL') or (uppercase(dbconnectorname)='BUFDATASET') then
|
|
begin
|
|
RegisterTestDecorator(TDBBasicsTestSetup, TTestBufDatasetDBBasics);
|
|
RegisterTestDecorator(TDBBasicsUniDirectionalTestSetup, TTestUniDirectionalDBBasics);
|
|
end;
|
|
{$else fpc}
|
|
RegisterTest(TTestDBBasics.Suite);
|
|
{$endif fpc}
|
|
end.
|