1 unit TestSQLDB;
2 
3 {
4   Unit tests which are specific to the sqlDB components like TSQLQuery, TSQLConnection.
5 }
6 
7 {$mode objfpc}{$H+}
8 
9 interface
10 
11 uses
12   Classes, sqldb, SysUtils, fpcunit, testregistry,
13   sqldbtoolsunit,toolsunit, db;
14 
15 type
16 
17   { TSQLDBTestCase }
18 
19   TSQLDBTestCase = class(TTestCase)
20     private
GetSQLDBConnectornull21       function GetSQLDBConnector: TSQLDBConnector;
22     protected
23       procedure SetUp; override;
24       procedure TearDown; override;
25       Property SQLDBConnector : TSQLDBConnector Read GetSQLDBConnector;
26   end;
27 
28   { TTestTSQLQuery }
29 
30   TTestTSQLQuery = class(TSQLDBTestCase)
31   private
32     FMyQ: TSQLQuery;
33     FPrepareCount:Integer;
34     procedure DoAfterPost(DataSet: TDataSet);
35     Procedure DoApplyUpdates;
36     procedure DoCount(Sender: TSQLConnection; EventType: TDBEventType; const Msg: String);
37     Procedure TrySetQueryOptions;
38     Procedure TrySetPacketRecords;
39   Protected
40     Procedure Setup; override;
41   published
42     procedure TestMasterDetail;
43     procedure TestUpdateServerIndexDefs;
44     Procedure TestKeepOpenOnCommit;
45     Procedure TestKeepOpenOnCommitPacketRecords;
46     Procedure TestCheckSettingsOnlyWhenInactive;
47     Procedure TestAutoApplyUpdatesPost;
48     Procedure TestAutoApplyUpdatesDelete;
49     Procedure TestCheckRowsAffected;
50     Procedure TestAutoCommit;
51     Procedure TestGeneratedRefreshSQL;
52     Procedure TestGeneratedRefreshSQL1Field;
53     Procedure TestGeneratedRefreshSQLNoKey;
54     Procedure TestRefreshSQL;
55     Procedure TestRefreshSQLMultipleRecords;
56     Procedure TestRefreshSQLNoRecords;
57     Procedure TestFetchAutoInc;
58     procedure TestSequence;
59     procedure TestReturningInsert;
60     procedure TestReturningUpdate;
61     procedure TestMacros;
62     Procedure TestPrepareCount;
63     Procedure TestNullTypeParam;
64   end;
65 
66   { TTestTSQLConnection }
67 
68   TTestTSQLConnection = class(TSQLDBTestCase)
69   private
70     procedure SetImplicit;
71     procedure TestImplicitTransaction;
72     procedure TestImplicitTransaction2;
73     procedure TestImplicitTransactionNotAssignable;
74     procedure TestImplicitTransactionOK;
75     procedure TryOpen;
76   published
77     procedure TestUseImplicitTransaction;
78     procedure TestUseExplicitTransaction;
79     procedure TestExplicitConnect;
80     procedure TestGetStatementInfo;
81     procedure TestGetNextValue;
82   end;
83 
84   { TTestTSQLScript }
85 
86   TTestTSQLScript = class(TSQLDBTestCase)
87   published
88     procedure TestExecuteScript;
89     procedure TestScriptColon; //bug 25334
90     procedure TestUseCommit; //E.g. Firebird cannot use COMMIT RETAIN if mixing DDL and DML in a script
91   end;
92 
93 implementation
94 
95 
96 
97 { TTestTSQLQuery }
98 
99 procedure TTestTSQLQuery.Setup;
100 begin
101   inherited Setup;
102   FPrepareCount:=0;
103   SQLDBConnector.Connection.Options:=[];
104 end;
105 
106 procedure TTestTSQLQuery.TestMasterDetail;
107 var MasterQuery, DetailQuery: TSQLQuery;
108     MasterSource: TDataSource;
109 begin
110   with SQLDBConnector do
111   try
112     MasterQuery := GetNDataset(10) as TSQLQuery;
113     MasterSource := TDatasource.Create(nil);
114     MasterSource.DataSet := MasterQuery;
115     DetailQuery := Query;
116     DetailQuery.SQL.Text := 'select NAME from FPDEV where ID=:ID';
117     DetailQuery.DataSource := MasterSource;
118 
119     MasterQuery.Open;
120     DetailQuery.Open;
121     CheckEquals('TestName1', DetailQuery.Fields[0].AsString);
122     MasterQuery.MoveBy(3);
123     CheckEquals('TestName4', DetailQuery.Fields[0].AsString);
124 
125     MasterQuery.Close;
126     CheckTrue(DetailQuery.Active, 'Detail dataset should remain intact, when master dataset is closed');
127   finally
128     MasterSource.Free;
129   end;
130 end;
131 
132 procedure TTestTSQLQuery.TestUpdateServerIndexDefs;
133 var Q: TSQLQuery;
134     name1, name2, name3: string;
135 begin
136   // Test retrieval of information about indexes on unquoted and quoted table names
137   //  (tests also case-sensitivity for DB's that support case-sensitivity of quoted identifiers)
138   // For ODBC Firebird/Interbase we must define primary key as named constraint and
139   //  in ODBC driver must be set: "quoted identifiers" and "sensitive identifier"
140   // See also: TTestFieldTypes.TestUpdateIndexDefs
141   with SQLDBConnector do
142   begin
143     // SQLite ignores case-sensitivity of quoted table names
144     // MS SQL Server case-sensitivity of identifiers depends on the case-sensitivity of default collation of the database
145     // MySQL case-sensitivity depends on case-sensitivity of server's file system
146     if SQLServerType in [ssMSSQL,ssSQLite{$IFDEF WINDOWS},ssMySQL{$ENDIF}] then
147       name1 := Connection.FieldNameQuoteChars[0]+'fpdev 2'+Connection.FieldNameQuoteChars[1]
148     else
149       name1 := 'FPDEV2';
150     ExecuteDirect('create table '+name1+' (id integer not null, constraint PK_FPDEV21 primary key(id))');
151     // same but quoted table name
152     name2 := Connection.FieldNameQuoteChars[0]+'FPdev2'+Connection.FieldNameQuoteChars[1];
153     ExecuteDirect('create table '+name2+' (ID2 integer not null, constraint PK_FPDEV22 primary key(ID2))');
154     // embedded quote in table name
155     if SQLServerType in [ssMySQL] then
156       name3 := '`FPdev``2`'
157     else
158       name3 := Connection.FieldNameQuoteChars[0]+'FPdev""2'+Connection.FieldNameQuoteChars[1];
159     ExecuteDirect('create table '+name3+' (Id3 integer not null, constraint PK_FPDEV23 primary key(Id3))');
160     CommitDDL;
161   end;
162 
163   try
164     Q := SQLDBConnector.Query;
165     Q.SQL.Text:='select * from '+name1;
166     Q.Prepare;
167     Q.ServerIndexDefs.Update;
168     CheckEquals(1, Q.ServerIndexDefs.Count);
169 
170     Q.SQL.Text:='select * from '+name2;
171     Q.Prepare;
172     Q.ServerIndexDefs.Update;
173     CheckEquals(1, Q.ServerIndexDefs.Count, '2.1');
174     CheckTrue(CompareText('ID2', Q.ServerIndexDefs[0].Fields)=0, '2.2'+Q.ServerIndexDefs[0].Fields);
175     CheckTrue(Q.ServerIndexDefs[0].Options=[ixPrimary,ixUnique], '2.3');
176 
177     Q.SQL.Text:='select * from '+name3;
178     Q.Prepare;
179     Q.ServerIndexDefs.Update;
180     CheckEquals(1, Q.ServerIndexDefs.Count, '3.1');
181     CheckTrue(CompareText('ID3', Q.ServerIndexDefs[0].Fields)=0, '3.2');
182     CheckTrue(Q.ServerIndexDefs[0].Options=[ixPrimary,ixUnique], '3.3');
183   finally
184     Q.UnPrepare;
185     with SQLDBConnector do
186     begin
187       ExecuteDirect('DROP TABLE '+name1);
188       ExecuteDirect('DROP TABLE '+name2);
189       ExecuteDirect('DROP TABLE '+name3);
190       CommitDDL;
191     end;
192   end;
193 end;
194 
195 procedure TTestTSQLQuery.TestKeepOpenOnCommit;
196 var Q: TSQLQuery;
197     I: Integer;
198 begin
199   // Test that for a SQL query with Options=sqoKeepOpenOnCommit, calling commit does not close the dataset.
200   // Test also that an edit still works.
201   with SQLDBConnector do
202     begin
203     ExecuteDirect('create table FPDEV2 (id integer not null, a varchar(10), constraint PK_FPDEV2 primary key(id))');
204     Transaction.Commit;
205     for I:=1 to 20 do
206       ExecuteDirect(Format('INSERT INTO FPDEV2 values (%d,''%.6d'')',[i,i]));
207     Transaction.Commit;
208 
209     Q := SQLDBConnector.Query;
210     Q.SQL.Text:='select * from FPDEV2';
211     Q.Options:=[sqoKeepOpenOnCommit,sqoRefreshUsingSelect];
212     AssertEquals('PacketRecords forced to -1',-1,Q.PacketRecords);
213     Q.Open;
214     AssertEquals('Got all records',20,Q.RecordCount);
215     Q.SQLTransaction.Commit;
216     AssertTrue('Still open after transaction',Q.Active);
217 
218     // Now check editing
219     Q.Locate('id',20,[]);
220     Q.Edit;
221     Q.FieldByName('a').AsString:='abc';
222     Q.Post;
223     AssertTrue('Have updates pending',Q.UpdateStatus=usModified);
224     Q.ApplyUpdates;
225     AssertTrue('Have no more updates pending',Q.UpdateStatus=usUnmodified);
226     Q.Close;
227     Q.SQL.Text:='select * from FPDEV2 where (id=20) and (a=''abc'')';
228     Q.Open;
229     AssertTrue('Have modified data record in database', not (Q.EOF AND Q.BOF));
230     end;
231 end;
232 
233 procedure TTestTSQLQuery.TrySetPacketRecords;
234 begin
235   FMyQ.PacketRecords:=10;
236 end;
237 
238 procedure TTestTSQLQuery.TestKeepOpenOnCommitPacketRecords;
239 begin
240   with SQLDBConnector do
241     begin
242     FMyQ := SQLDBConnector.Query;
243     FMyQ.Options:=[sqoKeepOpenOnCommit];
244     AssertException('Cannot set PacketRecords when sqoKeepOpenOnCommit is active',EDatabaseError,@TrySetPacketRecords);
245     end;
246 end;
247 
248 procedure TTestTSQLQuery.TrySetQueryOptions;
249 begin
250   FMyQ.Options:=[sqoKeepOpenOnCommit];
251 end;
252 
253 procedure TTestTSQLQuery.TestCheckSettingsOnlyWhenInactive;
254 begin
255   // Check that we can only set QueryOptions when the query is inactive.
256   with SQLDBConnector do
257     begin
258     ExecuteDirect('create table FPDEV2 (id integer not null, a varchar(10), constraint PK_FPDEV2 primary key(id))');
259     Transaction.Commit;
260     ExecuteDirect(Format('INSERT INTO FPDEV2 values (%d,''%.6d'')',[1,1]));
261     Transaction.Commit;
262     FMyQ := SQLDBConnector.Query;
263     FMyQ.SQL.Text:='select * from FPDEV2';
264     FMyQ := SQLDBConnector.Query;
265     FMyQ.Open;
266     AssertException('Cannot set Options when query is active',EDatabaseError,@TrySetQueryOptions);
267     end;
268 end;
269 
270 procedure TTestTSQLQuery.DoAfterPost(DataSet: TDataSet);
271 begin
272   AssertTrue('Have modifications in after post',FMyq.UpdateStatus=usModified)
273 end;
274 
275 procedure TTestTSQLQuery.TestAutoApplyUpdatesPost;
276 var Q: TSQLQuery;
277     I: Integer;
278 begin
279   // Test that if sqoAutoApplyUpdates is in QueryOptions, then POST automatically does an ApplyUpdates
280   // Test also that POST afterpost event is backwards compatible.
281   with SQLDBConnector do
282     begin
283     ExecuteDirect('create table FPDEV2 (id integer not null, a varchar(10), constraint PK_FPDEV2 primary key(id))');
284     Transaction.COmmit;
285     for I:=1 to 2 do
286       ExecuteDirect(Format('INSERT INTO FPDEV2 values (%d,''%.6d'')',[i,i]));
287     Transaction.COmmit;
288     Q := SQLDBConnector.Query;
289     FMyQ:=Q; // so th event handler can reach it.
290     Q.SQL.Text:='select * from FPDEV2';
291     Q.Options:=[sqoAutoApplyUpdates];
292     // We must test that in AfterPost, the modification is still there, for backwards compatibilty
293     Q.AfterPost:=@DoAfterPost;
294     Q.Open;
295     AssertEquals('Got all records',2,Q.RecordCount);
296     // Now check editing
297     Q.Locate('id',2,[]);
298     Q.Edit;
299     Q.FieldByName('a').AsString:='abc';
300     Q.Post;
301     AssertTrue('Have no more updates pending',Q.UpdateStatus=usUnmodified);
302     Q.Close;
303     Q.SQL.Text:='select * from FPDEV2 where (id=2) and (a=''abc'')';
304     Q.Open;
305     AssertTrue('Have modified data record in database',not (Q.EOF AND Q.BOF));
306     end;
307 
308 end;
309 
310 procedure TTestTSQLQuery.TestAutoApplyUpdatesDelete;
311 
312 var Q: TSQLQuery;
313     I: Integer;
314 begin
315   // Test that if sqoAutoApplyUpdates is in QueryOptions, then Delete automatically does an ApplyUpdates
316   with SQLDBConnector do
317     begin
318     ExecuteDirect('create table FPDEV2 (id integer not null, a varchar(10), constraint PK_FPDEV2 primary key(id))');
319     Transaction.COmmit;
320     for I:=1 to 2 do
321       ExecuteDirect(Format('INSERT INTO FPDEV2 values (%d,''%.6d'')',[i,i]));
322     Transaction.COmmit;
323     Q := SQLDBConnector.Query;
324     FMyQ:=Q; // so th event handler can reach it.
325     Q.SQL.Text:='select * from FPDEV2';
326     Q.Options:=[sqoAutoApplyUpdates];
327     // We must test that in AfterPost, the modification is still there, for backwards compatibilty
328     Q.AfterPost:=@DoAfterPost;
329     Q.Open;
330     AssertEquals('Got all records',2,Q.RecordCount);
331     // Now check editing
332     Q.Locate('id',2,[]);
333     Q.Delete;
334     AssertTrue('Have no more updates pending',Q.UpdateStatus=usUnmodified);
335     Q.Close;
336     Q.SQL.Text:='select * from FPDEV2 where (id=2)';
337     Q.Open;
338     AssertTrue('Data record is deleted in database', (Q.EOF AND Q.BOF));
339     end;
340 end;
341 
342 procedure TTestTSQLQuery.DoApplyUpdates;
343 
344 begin
345   FMyQ.ApplyUpdates();
346 end;
347 
348 procedure TTestTSQLQuery.DoCount(Sender: TSQLConnection; EventType: TDBEventType; const Msg: String);
349 begin
350   If (EventType=detPrepare) then
351     Inc(FPrepareCount);
352 end;
353 
354 procedure TTestTSQLQuery.TestCheckRowsAffected;
355 var Q: TSQLQuery;
356     I: Integer;
357 begin
358   // Test that if sqoAutoApplyUpdates is in QueryOptions, then Delete automatically does an ApplyUpdates
359   with SQLDBConnector do
360     begin
361     ExecuteDirect('create table FPDEV2 (id integer not null, a varchar(10), constraint PK_FPDEV2 primary key(id))');
362     Transaction.COmmit;
363     for I:=1 to 2 do
364       ExecuteDirect(Format('INSERT INTO FPDEV2 values (%d,''%.6d'')',[i,i]));
365     Transaction.COmmit;
366     SQLDBConnector.Connection.Options:=[scoApplyUpdatesChecksRowsAffected];
367     Q := SQLDBConnector.Query;
368     Q.SQL.Text:='select * from FPDEV2';
369     Q.DeleteSQL.Text:='delete from FPDEV2';
370     Q.Open;
371     AssertEquals('Got all records',2,Q.RecordCount);
372     // Now check editing
373     Q.Delete;
374     FMyQ:=Q;
375     AssertException('RowsAffected > 1 raises exception',EUpdateError,@DoApplyUpdates);
376     end;
377 end;
378 
379 procedure TTestTSQLQuery.TestAutoCommit;
380 var
381   I : Integer;
382 begin
383   with SQLDBConnector do
384     begin
385     ExecuteDirect('create table FPDEV2 (id integer not null, a varchar(10), constraint PK_FPDEV2 primary key(id))');
386     if Transaction.Active then
387       Transaction.Commit;
388 
389     Query.Options:=[sqoAutoCommit];
390     for I:=1 to 2 do
391       begin
392       Query.SQL.Text:=Format('INSERT INTO FPDEV2 values (%d,''%.6d'');',[i,i]);
393       Query.Prepare;
394       Query.ExecSQL;
395       // We do not commit anything explicitly.
396       end;
397 
398     AssertFalse('Transaction is still active after expected auto commit', Transaction.Active);
399 
400     Connection.Close;
401     Connection.Open;
402 
403     Query.SQL.Text:='SELECT COUNT(*) from FPDEV2';
404     Query.Open;
405     AssertEquals('Records haven''t been committed to database', 2, Query.Fields[0].AsInteger);
406     end;
407 end;
408 
409 procedure TTestTSQLQuery.TestGeneratedRefreshSQL;
410 
411 var
412   Q: TSQLQuery;
413 
414 begin
415   with SQLDBConnector do
416     begin
417     ExecuteDirect('create table FPDEV2 (id integer not null, a varchar(10) default ''abcde'', b varchar(5) default ''fgh'', constraint PK_FPDEV2 primary key(id))');
418     if Transaction.Active then
419       Transaction.Commit;
420     end;
421   Q:=SQLDBConnector.Query;
422   Q.SQL.Text:='select * from FPDEV2';
423   Q.InsertSQL.Text:='insert into FPDEV2 (id) values (:id)';
424   Q.Options:=Q.Options+[sqoRefreshUsingSelect];
425   Q.Open;
426   With Q.FieldByName('id') do
427     ProviderFlags:=ProviderFlags+[pfInKey];
428   With Q.FieldByName('a') do
429     ProviderFlags:=ProviderFlags+[pfRefreshOnInsert,pfRefreshOnUpdate];
430   With Q.FieldByName('b') do
431     ProviderFlags:=ProviderFlags+[pfRefreshOnInsert,pfRefreshOnUpdate];
432   Q.Insert;
433   Q.FieldByName('id').AsInteger:=1;
434   Q.Post;
435   AssertTrue('Field value has not been fetched after post',Q.FieldByName('a').IsNull);
436   Q.ApplyUpdates(0);
437   AssertEquals('Still on correct field',1,Q.FieldByName('id').AsInteger);
438   AssertEquals('Field value has been fetched from the database ','abcde',Q.FieldByName('a').AsString);
439   AssertEquals('Field value has been fetched from the database ','fgh',Q.FieldByName('b').AsString);
440 end;
441 
442 procedure TTestTSQLQuery.TestGeneratedRefreshSQL1Field;
443 var
444   Q: TSQLQuery;
445 
446 begin
447   with SQLDBConnector do
448     begin
449     ExecuteDirect('create table FPDEV2 (id integer not null, a varchar(10) default ''abcde'', b varchar(5) default ''fgh'', constraint PK_FPDEV2 primary key(id))');
450     if Transaction.Active then
451       Transaction.Commit;
452     end;
453   Q:=SQLDBConnector.Query;
454   Q.SQL.Text:='select * from FPDEV2';
455   Q.InsertSQL.Text:='insert into FPDEV2 (id) values (:id)';
456   Q.Options:=Q.Options+[sqoRefreshUsingSelect];
457   Q.Open;
458   With Q.FieldByName('id') do
459     ProviderFlags:=ProviderFlags+[pfInKey];
460   With Q.FieldByName('a') do
461     ProviderFlags:=ProviderFlags+[pfRefreshOnInsert,pfRefreshOnUpdate];
462   Q.Insert;
463   Q.FieldByName('id').AsInteger:=1;
464   Q.Post;
465   AssertTrue('Field value has not been fetched after post',Q.FieldByName('a').IsNull);
466   Q.ApplyUpdates(0);
467   AssertEquals('Still on correct field',1,Q.FieldByName('id').AsInteger);
468   AssertEquals('Field value a has been fetched from the database ','abcde',Q.FieldByName('a').AsString);
469   AssertEquals('Field value b has NOT been fetched from the database ','',Q.FieldByName('b').AsString);
470 end;
471 
472 procedure TTestTSQLQuery.TestGeneratedRefreshSQLNoKey;
473 begin
474   with SQLDBConnector do
475     begin
476     ExecuteDirect('create table FPDEV2 (id integer not null, a varchar(10) default ''abcde'', b varchar(5) default ''fgh'', constraint PK_FPDEV2 primary key(id))');
477     if Transaction.Active then
478       Transaction.Commit;
479     end;
480   FMyQ:=SQLDBConnector.Query;
481   FMyQ.SQL.Text:='select * from FPDEV2';
482   FMyQ.InsertSQL.Text:='insert into FPDEV2 (id) values (:id)';
483   FMyQ.Options:=FMyQ.Options+[sqoRefreshUsingSelect];
484   FMyQ.Open;
485   With FMyQ.FieldByName('id') do
486     ProviderFlags:=ProviderFlags-[pfInKey];
487   With FMyQ.FieldByName('a') do
488     ProviderFlags:=ProviderFlags+[pfRefreshOnInsert,pfRefreshOnUpdate];
489   FMyQ.Insert;
490   FMyQ.FieldByName('id').AsInteger:=1;
491   FMyQ.Post;
492   AssertException('Cannot refresh without primary key',EUpdateError,@DoApplyUpdates);
493 end;
494 
495 procedure TTestTSQLQuery.TestRefreshSQL;
496 var
497   Q: TSQLQuery;
498 
499 begin
500   with SQLDBConnector do
501     begin
502     ExecuteDirect('create table FPDEV2 (id integer not null primary key, a varchar(5) default ''abcde'', b integer default 1)');
503     if Transaction.Active then
504       Transaction.Commit;
505     end;
506   Q:=SQLDBConnector.Query;
507   Q.SQL.Text:='select * from FPDEV2';
508   Q.InsertSQL.Text:='insert into FPDEV2 (id) values (:id)';
509   Q.RefreshSQL.Text:='SELECT a,b FROM FPDEV2 WHERE (id=:id)';
510   Q.Open;
511   Q.Insert;  // #1 record
512   Q.FieldByName('id').AsInteger:=1;
513   Q.Post;
514   Q.Append;  // #2 record
515   Q.FieldByName('id').AsInteger:=2;
516   Q.Post;
517   AssertTrue('Field value has not been fetched after Post', Q.FieldByName('a').IsNull);
518   Q.ApplyUpdates(0);
519   // #2 record:
520   AssertEquals('Still on correct field', 2, Q.FieldByName('id').AsInteger);
521   AssertEquals('Field value has been fetched from the database', 'abcde', Q.FieldByName('a').AsString);
522   AssertEquals('Field value has been fetched from the database', 1, Q.FieldByName('b').AsInteger);
523   Q.Prior;
524   // #1 record:
525   AssertEquals('Still on correct field', 1, Q.FieldByName('id').AsInteger);
526   AssertEquals('Field value has been fetched from the database', 'abcde', Q.FieldByName('a').AsString);
527   AssertEquals('Field value has been fetched from the database', 1, Q.FieldByName('b').AsInteger);
528 end;
529 
530 procedure TTestTSQLQuery.TestRefreshSQLMultipleRecords;
531 
532 begin
533   with SQLDBConnector do
534     begin
535     ExecuteDirect('create table FPDEV2 (id integer not null, a varchar(10) default ''abcde'', b varchar(5) default ''fgh'', constraint PK_FPDEV2 primary key(id))');
536     if Transaction.Active then
537       Transaction.Commit;
538     ExecuteDirect('insert into FPDEV2 (id) values (123)');
539     if Transaction.Active then
540       Transaction.Commit;
541     end;
542   FMyQ:=SQLDBConnector.Query;
543   FMyQ.SQL.Text:='select * from FPDEV2';
544   FMyQ.InsertSQL.Text:='insert into FPDEV2 (id) values (:id)';
545   FMyQ.RefreshSQL.Text:='select * from FPDEV2';
546   FMyQ.Open;
547   With FMyQ.FieldByName('id') do
548     ProviderFlags:=ProviderFlags+[pfInKey];
549   With FMyQ.FieldByName('a') do
550     ProviderFlags:=ProviderFlags+[pfRefreshOnInsert,pfRefreshOnUpdate];
551   FMyQ.Insert;
552   FMyQ.FieldByName('id').AsInteger:=1;
553   FMyQ.Post;
554   AssertException('Multiple records returned by RefreshSQL gives an error',EUpdateError,@DoApplyUpdates);
555 end;
556 
557 procedure TTestTSQLQuery.TestRefreshSQLNoRecords;
558 begin
559   with SQLDBConnector do
560     begin
561     ExecuteDirect('create table FPDEV2 (id integer not null, a varchar(10) default ''abcde'', b varchar(5) default ''fgh'', constraint PK_FPDEV2 primary key(id))');
562     if Transaction.Active then
563       Transaction.Commit;
564     ExecuteDirect('insert into FPDEV2 (id) values (123)');
565     if Transaction.Active then
566       Transaction.Commit;
567     end;
568   FMyQ:=SQLDBConnector.Query;
569   FMyQ.SQL.Text:='select * from FPDEV2';
570   FMyQ.InsertSQL.Text:='insert into FPDEV2 (id) values (:id)';
571   FMyQ.RefreshSQL.Text:='select * from FPDEV2 where 1=2';
572   FMyQ.Open;
573   With FMyQ.FieldByName('id') do
574     ProviderFlags:=ProviderFlags+[pfInKey];
575   With FMyQ.FieldByName('a') do
576     ProviderFlags:=ProviderFlags+[pfRefreshOnInsert,pfRefreshOnUpdate];
577   FMyQ.Insert;
578   FMyQ.FieldByName('id').AsInteger:=1;
579   FMyQ.Post;
580   AssertException('No records returned by RefreshSQL gives an error',EUpdateError,@DoApplyUpdates);
581 end;
582 
583 procedure TTestTSQLQuery.TestFetchAutoInc;
584 var datatype: string;
585     id: largeint;
586 begin
587   with SQLDBConnector do
588     begin
589     case SQLServerType of
590       ssMySQL:
591         datatype := 'integer auto_increment';
592       ssMSSQL, ssSybase:
593         datatype := 'integer identity';
594       ssSQLite:
595         datatype := 'integer';
596       else
597         Ignore(STestNotApplicable);
598     end;
599     ExecuteDirect('create table FPDEV2 (id '+datatype+' primary key, f varchar(5))');
600     CommitDDL;
601     end;
602 
603   with SQLDBConnector.Query do
604     begin
605     SQL.Text:='select * from FPDEV2';
606     Open;
607     Insert;
608     FieldByName('f').AsString:='a';
609     Post;  // #1 record
610     Append;
611     FieldByName('f').AsString:='b';
612     Post;  // #2 record
613     AssertTrue('ID field is not null after Post', FieldByName('id').IsNull);
614     First; // #1 record
615     ApplyUpdates(0);
616     AssertTrue('ID field is still null after ApplyUpdates', Not FieldByName('id').IsNull);
617     // Should be 1 after the table was created, but this is not guaranteed... So we just test positive values.
618     id := FieldByName('id').AsLargeInt;
619     AssertTrue('ID field has not positive value', id>0);
620     Next;  // #2 record
621     AssertTrue('Next ID value is not greater than previous', FieldByName('id').AsLargeInt>id);
622     end;
623 end;
624 
625 procedure TTestTSQLQuery.TestSequence;
626 var SequenceNames : TStringList;
627 begin
628   case SQLServerType of
629     ssFirebird:
630       SQLDBConnector.ExecuteDirect('create sequence FPDEV_SEQ1');
631     ssMSSQL, ssOracle, ssPostgreSQL:
632       SQLDBConnector.ExecuteDirect('create sequence FPDEV_SEQ1 MINVALUE 1');
633     else
634       Ignore(STestNotApplicable);
635   end;
636   SQLDBConnector.ExecuteDirect('create table FPDEV2 (id integer)');
637   SQLDBConnector.CommitDDL;
638 
639   with SQLDBConnector.Query do
640     begin
641     SQL.Text := 'select * from FPDEV2';
642     Sequence.FieldName:='id';
643     Sequence.SequenceName:='FPDEV_SEQ1';
644     Open;
645     // default is get next value on new record
646     Append;
647     AssertEquals(1, FieldByName('id').AsInteger);
648 
649     Sequence.ApplyEvent:=saeOnPost;
650     Append;
651     AssertTrue('Field ID must be null after Append', FieldByName('id').IsNull);
652     Post;
653     AssertEquals(2, FieldByName('id').AsInteger);
654     end;
655 
656   // test GetSequenceNames
657   SequenceNames := TStringList.Create;
658   try
659     SQLDBConnector.Connection.GetSequenceNames(SequenceNames);
660     AssertTrue(SequenceNames.IndexOf('FPDEV_SEQ1') >= 0);
661   finally
662     SequenceNames.Free;
663   end;
664 
665   SQLDBConnector.ExecuteDirect('drop sequence FPDEV_SEQ1');
666   SQLDBConnector.CommitDDL;
667 end;
668 
669 procedure TTestTSQLQuery.TestReturningInsert;
670 
671 begin
672   with SQLDBConnector do
673     begin
674     if not (sqSupportReturning in Connection.ConnOptions) then
675       Ignore(STestNotApplicable);
676     ExecuteDirect('create table FPDEV2 (id integer not null, a varchar(10) default ''abcde'', b varchar(5) default ''fgh'', constraint PK_FPDEV2 primary key(id))');
677     if Transaction.Active then
678       Transaction.Commit;
679     ExecuteDirect('insert into FPDEV2 (id) values (123)');
680     if Transaction.Active then
681       Transaction.Commit;
682     end;
683   FMyQ:=SQLDBConnector.Query;
684   FMyQ.SQL.Text:='select * from FPDEV2';
685 //  FMyQ.InsertSQL.Text:='insert into FPDEV2 (id) values (:id)';
686   FMyQ.Open;
687   With FMyQ.FieldByName('id') do
688     ProviderFlags:=ProviderFlags+[pfInKey];
689   With FMyQ.FieldByName('a') do
690     ProviderFlags:=ProviderFlags+[pfRefreshOnInsert];
691   With FMyQ.FieldByName('b') do
692     ProviderFlags:=[];
693   FMyQ.Insert;
694   FMyQ.FieldByName('id').AsInteger:=1;
695   FMyQ.Post;
696   FMyQ.ApplyUpdates;
697   AssertEquals('a updated','abcde',FMyQ.FieldByName('a').AsString);
698   AssertEquals('b not updated','',FMyQ.FieldByName('b').AsString);
699 end;
700 
701 procedure TTestTSQLQuery.TestReturningUpdate;
702 
703 begin
704   with SQLDBConnector do
705     begin
706     if not (sqSupportReturning in Connection.ConnOptions) then
707       Ignore(STestNotApplicable);
708     ExecuteDirect('create table FPDEV2 (id integer not null, a varchar(10) default ''abcde'', b varchar(5) default ''fgh'', constraint PK_FPDEV2 primary key(id))');
709     CommitDDL;
710     ExecuteDirect('insert into FPDEV2 (id) values (1)');
711     ExecuteDirect('insert into FPDEV2 (id) values (2)');
712     end;
713   FMyQ:=SQLDBConnector.Query;
714   FMyQ.SQL.Text:='select * from FPDEV2';
715   FMyQ.Open;
716   With FMyQ.FieldByName('id') do
717     ProviderFlags:=ProviderFlags+[pfInKey];
718   With FMyQ.FieldByName('b') do
719     ProviderFlags:=[pfRefreshOnUpdate];  // Do not update, just fetch new value
720   SQLDBConnector.ExecuteDirect('update FPDEV2 set b=''b1'' where id=1');
721   SQLDBConnector.ExecuteDirect('update FPDEV2 set b=''b2'' where id=2');
722   FMyQ.Edit;
723   FMyQ.FieldByName('a').AsString:='a1';
724   FMyQ.Post;  // #1 record
725   FMyQ.Next;
726   FMyQ.Edit;
727   FMyQ.FieldByName('a').AsString:='a2';
728   FMyQ.Post;  // #2 record
729   FMyQ.ApplyUpdates;
730   FMyQ.First;
731   AssertEquals('#1.a updated', 'a1', FMyQ.FieldByName('a').AsString);
732   AssertEquals('#1.b updated', 'b1', FMyQ.FieldByName('b').AsString);
733   FMyQ.Next;
734   AssertEquals('#2.a updated', 'a2', FMyQ.FieldByName('a').AsString);
735   AssertEquals('#2.b updated', 'b2', FMyQ.FieldByName('b').AsString);
736 end;
737 
738 procedure TTestTSQLQuery.TestMacros;
739 begin
740   with SQLDBConnector do
741     begin
742     ExecuteDirect('create table FPDEV2 (id integer not null, constraint PK_FPDEV2 primary key(id))');
743     CommitDDL;
744     ExecuteDirect('insert into FPDEV2 (id) values (1)');
745     ExecuteDirect('insert into FPDEV2 (id) values (2)');
746     end;
747 
748   With SQLDBConnector.Query do
749     begin
750     SQL.Text:='Select ID from FPDEV2 '+
751       '%WHERE_CL' +sLineBreak+
752       '%ORDER_CL' +sLineBreak;
753     MacroCheck:=true;
754     MacroByName('WHERE_CL').AsString:='where 1=1';
755     MacroByName('ORDER_CL').AsString:='order by 1';
756     Open;
757     AssertEquals('Correct SQL executed, macros substituted: ',1,Fields[0].AsInteger);
758     Close;
759     MacroByName('ORDER_CL').AsString := 'Order by 1 DESC';
760     Open;
761     AssertEquals('Correct SQL executed, macro value changed: ',2,Fields[0].AsInteger);
762     end;
763 end;
764 
765 procedure TTestTSQLQuery.TestPrepareCount;
766 
767 begin
768   with SQLDBConnector do
769     begin
770     ExecuteDirect('create table FPDEV2 (id integer not null, constraint PK_FPDEV2 primary key(id))');
771     CommitDDL;
772     ExecuteDirect('insert into FPDEV2 (id) values (1)');
773     ExecuteDirect('insert into FPDEV2 (id) values (2)');
774     Connection.OnLog:=@DoCount;
775     Connection.LogEvents:=[detPrepare];
776     end;
777   try
778     With SQLDBConnector.Query do
779       begin
780       Unidirectional:=True; // Disable server index defs etc
781       UsePrimaryKeyAsKey:=False; // Idem
782       SQL.Text:='Select ID from FPDEV2 where (ID=:ID)';
783       ParamByname('ID').AsInteger:=1;
784       Prepare;
785       Open;
786       AssertEquals('Correct record count param 1',1,RecordCount);
787       AssertEquals('Correct SQL executed, correct paramete: ',1,Fields[0].AsInteger);
788       Close;
789       ParamByname('ID').AsInteger:=2;
790       Open;
791       AssertEquals('Correct record count param 2',1,RecordCount);
792       AssertEquals('Correct SQL executed, macro value changed: ',2,Fields[0].AsInteger);
793       end;
794     AssertEquals('Prepare called only once ',1,FPrepareCount);
795   finally
796     SQLDBConnector.Connection.OnLog:=Nil;
797   end;
798 
799 end;
800 
801 procedure TTestTSQLQuery.TestNullTypeParam;
802 begin
803   if not (SQLServerType in [ssSQLite, ssFirebird]) then
804     Ignore(STestNotApplicable);
805   CreateAndFillIDField;
806   try
807     With SQLDBConnector.Query do
808       begin
809       UsePrimaryKeyAsKey:=False; // Disable server index defs etc
810       SQL.Text:='Select ID from FPDEV2 where (:ID IS NULL or ID = :ID)';
811       Open;
812       AssertEquals('Correct record count param NULL',10,RecordCount);
813       Close;
814       ParamByname('ID').AsInteger:=1;
815       Open;
816       AssertEquals('Correct record count param 1',1,RecordCount);
817       AssertEquals('Correct field value: ',1,Fields[0].AsInteger);
818       Close;
819       end;
820   finally
821     SQLDBConnector.Connection.OnLog:=Nil;
822   end;
823 end;
824 
825 
826 { TTestTSQLConnection }
827 
828 procedure TTestTSQLConnection.TestImplicitTransaction;
829 
830 Var
831   T : TSQLTransaction;
832 
833 begin
834   T:=TSQLTransaction.Create(Nil);
835   try
836     T.Options:=[stoUseImplicit];
837     T.DataBase:=SQLDBConnector.Connection;
838   finally
839     T.Free;
840   end;
841 end;
842 
843 procedure TTestTSQLConnection.TestImplicitTransaction2;
844 
845 Var
846   T : TSQLTransaction;
847 
848 begin
849   T:=TSQLTransaction.Create(Nil);
850   try
851     T.Options:=[stoUseImplicit];
852     SQLDBConnector.Connection.Transaction:=T;
853   finally
854     T.Free;
855   end;
856 end;
857 
858 procedure TTestTSQLConnection.SetImplicit;
859 
860 begin
861   SQLDBConnector.Transaction.Options:=[stoUseImplicit];
862 end;
863 
864 procedure TTestTSQLConnection.TestImplicitTransactionNotAssignable;
865 
866 begin
867   AssertException('Cannot set toUseImplicit option if database does not allow it',EDatabaseError,@SetImplicit);
868   AssertException('Cannot assign database to transaction with toUseImplicit, if database does not allow it',EDatabaseError,@TestImplicitTransaction);
869   AssertException('Cannot assign transaction with toUseImplicit to database, if database does not allow it',EDatabaseError,@TestImplicitTransaction2);
870 end;
871 
872 procedure TTestTSQLConnection.TestImplicitTransactionOK;
873 
874 var
875   Q : TSQLQuery;
876   T : TSQLTransaction;
877   I : Integer;
878 begin
879   with SQLDBConnector do
880     begin
881     ExecuteDirect('create table FPDEV2 (id integer not null, a varchar(10), constraint PK_FPDEV2 primary key(id))');
882     if Transaction.Active then
883       Transaction.Commit;
884     end;
885   SetImplicit;
886   Q:=SQLDBConnector.Query;
887   for I:=1 to 2 do
888     begin
889     Q.SQL.Text:=Format('INSERT INTO FPDEV2 values (%d,''%.6d'');',[i,i]);
890     Q.Prepare;
891     Q.ExecSQL;
892     // We do not commit anything explicitly.
893     end;
894   Q:=Nil;
895   T:=Nil;
896   try
897     T:=TSQLTransaction.Create(Nil);
898     Q:=TSQLQuery.Create(Nil);
899     Q.Transaction:=T;
900     Q.Database:=SQLDBConnector.Connection;
901     T.Database:=SQLDBConnector.Connection;
902     Q.SQL.text:='SELECT COUNT(*) from FPDEV2';
903     Q.Open;
904     AssertEquals('Records have been committed to database',2,Q.Fields[0].AsInteger);
905   finally
906     Q.Free;
907     T.Free;
908   end;
909 end;
910 
911 procedure TTestTSQLConnection.TestUseImplicitTransaction;
912 begin
913   if (sqImplicitTransaction in SQLDBConnector.Connection.ConnOptions) then
914     TestImplicitTransactionOK
915   else
916     TestImplicitTransactionNotAssignable;
917 end;
918 
919 procedure TTestTSQLConnection.TryOpen;
920 
921 begin
922   SQLDBConnector.Query.Open;
923 end;
924 
925 procedure TTestTSQLConnection.TestUseExplicitTransaction;
926 begin
927   SQLDBConnector.Transaction.Active:=False;
928   SQLDBConnector.Transaction.Options:=[stoExplicitStart];
929   SQLDBConnector.Query.SQL.Text:='select * from FPDEV';
930   AssertException('toExplicitStart raises exception on implicit start',EDatabaseError,@TryOpen)
931 end;
932 
933 procedure TTestTSQLConnection.TestExplicitConnect;
934 begin
935   SQLDBConnector.Transaction.Active:=False;
936   SQLDBConnector.Connection.Options:=[scoExplicitConnect];
937   SQLDBConnector.Connection.Connected:=False;
938   SQLDBConnector.Query.SQL.Text:='select * from FPDEV';
939   AssertException('toExplicitStart raises exception on implicit start',EDatabaseError,@TryOpen)
940 end;
941 
942 procedure TTestTSQLConnection.TestGetStatementInfo;
943 var StmtInfo: TSQLStatementInfo;
944 begin
945   // single table
946   StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM tab1');
947   AssertEquals('StatementType', ord(stSELECT), ord(StmtInfo.StatementType));
948   AssertEquals('TableName', 'tab1', StmtInfo.TableName);
949   AssertEquals('Updateable', True, StmtInfo.Updateable);
950   StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM tab2 WHERE col1=1');
951   AssertEquals('TableName', 'tab2', StmtInfo.TableName);
952   AssertEquals('Updateable', True, StmtInfo.Updateable);
953   // single table with schema
954   StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM dbo.tab2 WHERE col1=1');
955   AssertEquals('TableName', 'dbo.tab2', StmtInfo.TableName);
956   AssertEquals('Updateable', True, StmtInfo.Updateable);
957   // single table with quoted schema
958   StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM "dbo".tab2 WHERE col1=1');
959   AssertEquals('TableName', '"dbo".tab2', StmtInfo.TableName);
960   AssertEquals('Updateable', True, StmtInfo.Updateable);
961   StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM "dbo"."tab2" WHERE col1=1');
962   AssertEquals('TableName', '"dbo"."tab2"', StmtInfo.TableName);
963   AssertEquals('Updateable', True, StmtInfo.Updateable);
964   // multiple tables
965   StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM tab3,tab4 WHERE col1=1');
966   AssertEquals('TableName', '', StmtInfo.TableName);
967   AssertEquals('Updateable', False, StmtInfo.Updateable);
968   // function
StmtInfonull969   StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM dbo.fn1(1)');
970   AssertEquals('TableName', '', StmtInfo.TableName);
971   AssertEquals('Updateable', False, StmtInfo.Updateable);
972 end;
973 
974 procedure TTestTSQLConnection.TestGetNextValue;
975 begin
976   if not (sqSequences in SQLDBConnector.Connection.ConnOptions) then
977     Ignore('Connector '+SQLDBConnector.Connection.ClassName+' does not support sequences');
978   if SQLServerType=ssSQLite then
979     begin
980     SQLDBConnector.TryDropIfExist('me');
981     SQLDBConnector.ExecuteDirect('create table me (a integer primary key autoincrement,b int)');
982     SQLDBConnector.ExecuteDirect('insert into me (b) values (1)');// Will create table sqlite_sequence if it didn't exist yet
983     SQLDBConnector.ExecuteDirect('drop table me');
984     end;
985   SQLDBConnector.TryDropSequence('me');
986   SQLDBConnector.TryCreateSequence('me');
987   AssertTrue('Get value',SQLDBConnector.Connection.GetNextValue('me',1)>0);
988 end;
989 
990 
991 { TTestTSQLScript }
992 
993 procedure TTestTSQLScript.TestExecuteScript;
994 var Ascript : TSQLScript;
995 begin
996   Ascript := TSQLScript.Create(nil);
997   try
998     with Ascript do
999       begin
1000       DataBase := SQLDBConnector.Connection;
1001       Transaction := SQLDBConnector.Transaction;
1002       Script.Clear;
1003       Script.Append('create table FPDEV_A (id int);');
1004       Script.Append('create table FPDEV_B (id int);');
1005       ExecuteScript;
1006       // Firebird/Interbase need a commit after a DDL statement. Not necessary for the other connections
1007       SQLDBConnector.CommitDDL;
1008       end;
1009   finally
1010     AScript.Free;
1011     SQLDBConnector.ExecuteDirect('drop table FPDEV_A');
1012     SQLDBConnector.ExecuteDirect('drop table FPDEV_B');
1013     // Firebird/Interbase need a commit after a DDL statement. Not necessary for the other connections
1014     SQLDBConnector.CommitDDL;
1015   end;
1016 end;
1017 
1018 procedure TTestTSQLScript.TestScriptColon;
1019 // Bug 25334: TSQLScript incorrectly treats : in scripts as sqldb query parameter markers
1020 // Firebird-only test; can be extended for other dbs that use : in SQL
1021 var
1022   Ascript : TSQLScript;
1023 begin
1024   if not(SQLConnType in [interbase]) then Ignore(STestNotApplicable);
1025   Ascript := TSQLScript.Create(nil);
1026   try
1027     with Ascript do
1028       begin
1029       DataBase := SQLDBConnector.Connection;
1030       Transaction := SQLDBConnector.Transaction;
1031       Script.Clear;
1032       UseSetTerm := true;
1033       // Example procedure that selects table names
1034       Script.Append(
1035         'SET TERM ^ ; '+LineEnding+
1036         'CREATE PROCEDURE FPDEV_TESTCOLON '+LineEnding+
1037         'RETURNS (tblname VARCHAR(31)) '+LineEnding+
1038         'AS '+LineEnding+
1039         'begin '+LineEnding+
1040         '/*  Show tables. Note statement uses colon */ '+LineEnding+
1041         'FOR '+LineEnding+
1042         '  SELECT RDB$RELATION_NAME  '+LineEnding+
1043         '    FROM RDB$RELATIONS '+LineEnding+
1044         '    ORDER BY RDB$RELATION_NAME '+LineEnding+
1045         '    INTO :tblname '+LineEnding+
1046         'DO  '+LineEnding+
1047         '  SUSPEND; '+LineEnding+
1048         'end^ '+LineEnding+
1049         'SET TERM ; ^'
1050         );
1051       ExecuteScript;
1052       // Firebird/Interbase need a commit after a DDL statement. Not necessary for the other connections
1053       SQLDBConnector.CommitDDL;
1054       end;
1055   finally
1056     AScript.Free;
1057     SQLDBConnector.ExecuteDirect('DROP PROCEDURE FPDEV_TESTCOLON');
1058     // Firebird/Interbase need a commit after a DDL statement. Not necessary for the other connections
1059     SQLDBConnector.CommitDDL;
1060   end;
1061 end;
1062 
1063 procedure TTestTSQLScript.TestUseCommit;
1064 // E.g. Firebird needs explicit COMMIT sometimes, e.g. if mixing DDL and DML
1065 // statements in a script.
1066 // Probably same as bug 17829 Error executing SQL script
1067 const
1068   TestValue='Some text';
1069 var
1070   Ascript : TSQLScript;
1071   CheckQuery : TSQLQuery;
1072 begin
1073   Ascript := TSQLScript.Create(nil);
1074   try
1075     with Ascript do
1076       begin
1077       DataBase := SQLDBConnector.Connection;
1078       Transaction := SQLDBConnector.Transaction;
1079       Script.Clear;
1080       UseCommit:=true;
1081       // Example procedure that selects table names
1082       Script.Append('CREATE TABLE fpdev_scriptusecommit (logmessage VARCHAR(255));');
1083       Script.Append('COMMIT;'); //needed for table to show up
1084       Script.Append('INSERT INTO fpdev_scriptusecommit (logmessage) VALUES('''+TestValue+''');');
1085       Script.Append('COMMIT;');
1086       ExecuteScript;
1087       // This line should not run, as the commit above should have taken care of it:
1088       //SQLDBConnector.CommitDDL;
1089       // Test whether second line of script executed, just to be sure
1090       CheckQuery:=SQLDBConnector.Query;
1091       CheckQuery.SQL.Text:='SELECT logmessage FROM fpdev_scriptusecommit ';
1092       CheckQuery.Open;
1093       CheckEquals(TestValue, CheckQuery.Fields[0].AsString, 'Insert script line should have inserted '+TestValue);
1094       CheckQuery.Close;
1095       end;
1096   finally
1097     AScript.Free;
1098     SQLDBConnector.ExecuteDirect('DROP TABLE fpdev_scriptusecommit');
1099     SQLDBConnector.Transaction.Commit;
1100   end;
1101 end;
1102 
1103 { TSQLDBTestCase }
1104 
GetSQLDBConnectornull1105 function TSQLDBTestCase.GetSQLDBConnector: TSQLDBConnector;
1106 begin
1107   Result := DBConnector as TSQLDBConnector;
1108 end;
1109 
1110 procedure TSQLDBTestCase.SetUp;
1111 begin
1112   inherited SetUp;
1113   InitialiseDBConnector;
1114   DBConnector.StartTest(TestName);
1115 end;
1116 
1117 procedure TSQLDBTestCase.TearDown;
1118 begin
1119   DBConnector.StopTest(TestName);
1120   if assigned(DBConnector) then
1121     with SQLDBConnector do
1122       if Assigned(Transaction) and Transaction.Active and not (stoUseImplicit in Transaction.Options) then
1123         Transaction.Rollback;
1124   FreeDBConnector;
1125   inherited TearDown;
1126 end;
1127 
1128 
1129 initialization
1130   if uppercase(dbconnectorname)='SQL' then
1131   begin
1132     RegisterTest(TTestTSQLQuery);
1133     RegisterTest(TTestTSQLConnection);
1134     RegisterTest(TTestTSQLScript);
1135   end;
1136 end.
1137