testsqldb.pas 34 KB

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