testsqldb.pas 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037
  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. end;
  51. { TTestTSQLConnection }
  52. TTestTSQLConnection = class(TSQLDBTestCase)
  53. private
  54. procedure SetImplicit;
  55. procedure TestImplicitTransaction;
  56. procedure TestImplicitTransaction2;
  57. procedure TestImplicitTransactionNotAssignable;
  58. procedure TestImplicitTransactionOK;
  59. procedure TryOpen;
  60. published
  61. procedure TestUseImplicitTransaction;
  62. procedure TestUseExplicitTransaction;
  63. procedure TestExplicitConnect;
  64. procedure TestGetStatementInfo;
  65. procedure TestGetNextValue;
  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. { TTestTSQLConnection }
  659. procedure TTestTSQLConnection.TestImplicitTransaction;
  660. Var
  661. T : TSQLTransaction;
  662. begin
  663. T:=TSQLTransaction.Create(Nil);
  664. try
  665. T.Options:=[stoUseImplicit];
  666. T.DataBase:=SQLDBConnector.Connection;
  667. finally
  668. T.Free;
  669. end;
  670. end;
  671. procedure TTestTSQLConnection.TestImplicitTransaction2;
  672. Var
  673. T : TSQLTransaction;
  674. begin
  675. T:=TSQLTransaction.Create(Nil);
  676. try
  677. T.Options:=[stoUseImplicit];
  678. SQLDBConnector.Connection.Transaction:=T;
  679. finally
  680. T.Free;
  681. end;
  682. end;
  683. procedure TTestTSQLConnection.SetImplicit;
  684. begin
  685. SQLDBConnector.Transaction.Options:=[stoUseImplicit];
  686. end;
  687. procedure TTestTSQLConnection.TestImplicitTransactionNotAssignable;
  688. begin
  689. AssertException('Cannot set toUseImplicit option if database does not allow it',EDatabaseError,@SetImplicit);
  690. AssertException('Cannot assign database to transaction with toUseImplicit, if database does not allow it',EDatabaseError,@TestImplicitTransaction);
  691. AssertException('Cannot assign transaction with toUseImplicit to database, if database does not allow it',EDatabaseError,@TestImplicitTransaction2);
  692. end;
  693. procedure TTestTSQLConnection.TestImplicitTransactionOK;
  694. var
  695. Q : TSQLQuery;
  696. T : TSQLTransaction;
  697. I : Integer;
  698. begin
  699. with SQLDBConnector do
  700. begin
  701. ExecuteDirect('create table FPDEV2 (id integer not null, a varchar(10), constraint PK_FPDEV2 primary key(id))');
  702. if Transaction.Active then
  703. Transaction.Commit;
  704. end;
  705. SetImplicit;
  706. Q:=SQLDBConnector.Query;
  707. for I:=1 to 2 do
  708. begin
  709. Q.SQL.Text:=Format('INSERT INTO FPDEV2 values (%d,''%.6d'');',[i,i]);
  710. Q.Prepare;
  711. Q.ExecSQL;
  712. // We do not commit anything explicitly.
  713. end;
  714. Q:=Nil;
  715. T:=Nil;
  716. try
  717. T:=TSQLTransaction.Create(Nil);
  718. Q:=TSQLQuery.Create(Nil);
  719. Q.Transaction:=T;
  720. Q.Database:=SQLDBConnector.Connection;
  721. T.Database:=SQLDBConnector.Connection;
  722. Q.SQL.text:='SELECT COUNT(*) from FPDEV2';
  723. Q.Open;
  724. AssertEquals('Records have been committed to database',2,Q.Fields[0].AsInteger);
  725. finally
  726. Q.Free;
  727. T.Free;
  728. end;
  729. end;
  730. procedure TTestTSQLConnection.TestUseImplicitTransaction;
  731. begin
  732. if (sqImplicitTransaction in SQLDBConnector.Connection.ConnOptions) then
  733. TestImplicitTransactionOK
  734. else
  735. TestImplicitTransactionNotAssignable;
  736. end;
  737. procedure TTestTSQLConnection.TryOpen;
  738. begin
  739. SQLDBConnector.Query.Open;
  740. end;
  741. procedure TTestTSQLConnection.TestUseExplicitTransaction;
  742. begin
  743. SQLDBConnector.Transaction.Active:=False;
  744. SQLDBConnector.Transaction.Options:=[stoExplicitStart];
  745. SQLDBConnector.Query.SQL.Text:='select * from FPDEV';
  746. AssertException('toExplicitStart raises exception on implicit start',EDatabaseError,@TryOpen)
  747. end;
  748. procedure TTestTSQLConnection.TestExplicitConnect;
  749. begin
  750. SQLDBConnector.Transaction.Active:=False;
  751. SQLDBConnector.Connection.Options:=[scoExplicitConnect];
  752. SQLDBConnector.Connection.Connected:=False;
  753. SQLDBConnector.Query.SQL.Text:='select * from FPDEV';
  754. AssertException('toExplicitStart raises exception on implicit start',EDatabaseError,@TryOpen)
  755. end;
  756. procedure TTestTSQLConnection.TestGetStatementInfo;
  757. var StmtInfo: TSQLStatementInfo;
  758. begin
  759. // single table
  760. StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM tab1');
  761. AssertEquals('StatementType', ord(stSELECT), ord(StmtInfo.StatementType));
  762. AssertEquals('TableName', 'tab1', StmtInfo.TableName);
  763. AssertEquals('Updateable', True, StmtInfo.Updateable);
  764. StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM tab2 WHERE col1=1');
  765. AssertEquals('TableName', 'tab2', StmtInfo.TableName);
  766. AssertEquals('Updateable', True, StmtInfo.Updateable);
  767. // single table with schema
  768. StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM dbo.tab2 WHERE col1=1');
  769. AssertEquals('TableName', 'dbo.tab2', StmtInfo.TableName);
  770. AssertEquals('Updateable', True, StmtInfo.Updateable);
  771. // single table with quoted schema
  772. StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM "dbo".tab2 WHERE col1=1');
  773. AssertEquals('TableName', '"dbo".tab2', StmtInfo.TableName);
  774. AssertEquals('Updateable', True, StmtInfo.Updateable);
  775. StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM "dbo"."tab2" WHERE col1=1');
  776. AssertEquals('TableName', '"dbo"."tab2"', StmtInfo.TableName);
  777. AssertEquals('Updateable', True, StmtInfo.Updateable);
  778. // multiple tables
  779. StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM tab3,tab4 WHERE col1=1');
  780. AssertEquals('TableName', '', StmtInfo.TableName);
  781. AssertEquals('Updateable', False, StmtInfo.Updateable);
  782. // function
  783. StmtInfo := SQLDBConnector.Connection.GetStatementInfo('SELECT * FROM dbo.fn1(1)');
  784. AssertEquals('TableName', '', StmtInfo.TableName);
  785. AssertEquals('Updateable', False, StmtInfo.Updateable);
  786. end;
  787. procedure TTestTSQLConnection.TestGetNextValue;
  788. begin
  789. if not (sqSequences in SQLDBConnector.Connection.ConnOptions) then
  790. Ignore('Connector '+SQLDBConnector.Connection.ClassName+' does not support sequences');
  791. if SQLServerType=ssSQLite then
  792. begin
  793. SQLDBConnector.TryDropIfExist('me');
  794. SQLDBConnector.ExecuteDirect('create table me (a integer primary key autoincrement,b int)');
  795. SQLDBConnector.ExecuteDirect('insert into me (b) values (1)');// Will create table sqlite_sequence if it didn't exist yet
  796. SQLDBConnector.ExecuteDirect('drop table me');
  797. end;
  798. SQLDBConnector.TryDropSequence('me');
  799. SQLDBConnector.TryCreateSequence('me');
  800. AssertTrue('Get value',SQLDBConnector.Connection.GetNextValue('me',1)>0);
  801. end;
  802. { TTestTSQLScript }
  803. procedure TTestTSQLScript.TestExecuteScript;
  804. var Ascript : TSQLScript;
  805. begin
  806. Ascript := TSQLScript.Create(nil);
  807. try
  808. with Ascript do
  809. begin
  810. DataBase := SQLDBConnector.Connection;
  811. Transaction := SQLDBConnector.Transaction;
  812. Script.Clear;
  813. Script.Append('create table FPDEV_A (id int);');
  814. Script.Append('create table FPDEV_B (id int);');
  815. ExecuteScript;
  816. // Firebird/Interbase need a commit after a DDL statement. Not necessary for the other connections
  817. SQLDBConnector.CommitDDL;
  818. end;
  819. finally
  820. AScript.Free;
  821. SQLDBConnector.ExecuteDirect('drop table FPDEV_A');
  822. SQLDBConnector.ExecuteDirect('drop table FPDEV_B');
  823. // Firebird/Interbase need a commit after a DDL statement. Not necessary for the other connections
  824. SQLDBConnector.CommitDDL;
  825. end;
  826. end;
  827. procedure TTestTSQLScript.TestScriptColon;
  828. // Bug 25334: TSQLScript incorrectly treats : in scripts as sqldb query parameter markers
  829. // Firebird-only test; can be extended for other dbs that use : in SQL
  830. var
  831. Ascript : TSQLScript;
  832. begin
  833. if not(SQLConnType in [interbase]) then Ignore(STestNotApplicable);
  834. Ascript := TSQLScript.Create(nil);
  835. try
  836. with Ascript do
  837. begin
  838. DataBase := SQLDBConnector.Connection;
  839. Transaction := SQLDBConnector.Transaction;
  840. Script.Clear;
  841. UseSetTerm := true;
  842. // Example procedure that selects table names
  843. Script.Append(
  844. 'SET TERM ^ ; '+LineEnding+
  845. 'CREATE PROCEDURE FPDEV_TESTCOLON '+LineEnding+
  846. 'RETURNS (tblname VARCHAR(31)) '+LineEnding+
  847. 'AS '+LineEnding+
  848. 'begin '+LineEnding+
  849. '/* Show tables. Note statement uses colon */ '+LineEnding+
  850. 'FOR '+LineEnding+
  851. ' SELECT RDB$RELATION_NAME '+LineEnding+
  852. ' FROM RDB$RELATIONS '+LineEnding+
  853. ' ORDER BY RDB$RELATION_NAME '+LineEnding+
  854. ' INTO :tblname '+LineEnding+
  855. 'DO '+LineEnding+
  856. ' SUSPEND; '+LineEnding+
  857. 'end^ '+LineEnding+
  858. 'SET TERM ; ^'
  859. );
  860. ExecuteScript;
  861. // Firebird/Interbase need a commit after a DDL statement. Not necessary for the other connections
  862. SQLDBConnector.CommitDDL;
  863. end;
  864. finally
  865. AScript.Free;
  866. SQLDBConnector.ExecuteDirect('DROP PROCEDURE FPDEV_TESTCOLON');
  867. // Firebird/Interbase need a commit after a DDL statement. Not necessary for the other connections
  868. SQLDBConnector.CommitDDL;
  869. end;
  870. end;
  871. procedure TTestTSQLScript.TestUseCommit;
  872. // E.g. Firebird needs explicit COMMIT sometimes, e.g. if mixing DDL and DML
  873. // statements in a script.
  874. // Probably same as bug 17829 Error executing SQL script
  875. const
  876. TestValue='Some text';
  877. var
  878. Ascript : TSQLScript;
  879. CheckQuery : TSQLQuery;
  880. begin
  881. Ascript := TSQLScript.Create(nil);
  882. try
  883. with Ascript do
  884. begin
  885. DataBase := SQLDBConnector.Connection;
  886. Transaction := SQLDBConnector.Transaction;
  887. Script.Clear;
  888. UseCommit:=true;
  889. // Example procedure that selects table names
  890. Script.Append('CREATE TABLE fpdev_scriptusecommit (logmessage VARCHAR(255));');
  891. Script.Append('COMMIT;'); //needed for table to show up
  892. Script.Append('INSERT INTO fpdev_scriptusecommit (logmessage) VALUES('''+TestValue+''');');
  893. Script.Append('COMMIT;');
  894. ExecuteScript;
  895. // This line should not run, as the commit above should have taken care of it:
  896. //SQLDBConnector.CommitDDL;
  897. // Test whether second line of script executed, just to be sure
  898. CheckQuery:=SQLDBConnector.Query;
  899. CheckQuery.SQL.Text:='SELECT logmessage FROM fpdev_scriptusecommit ';
  900. CheckQuery.Open;
  901. CheckEquals(TestValue, CheckQuery.Fields[0].AsString, 'Insert script line should have inserted '+TestValue);
  902. CheckQuery.Close;
  903. end;
  904. finally
  905. AScript.Free;
  906. SQLDBConnector.ExecuteDirect('DROP TABLE fpdev_scriptusecommit');
  907. SQLDBConnector.Transaction.Commit;
  908. end;
  909. end;
  910. { TSQLDBTestCase }
  911. function TSQLDBTestCase.GetSQLDBConnector: TSQLDBConnector;
  912. begin
  913. Result := DBConnector as TSQLDBConnector;
  914. end;
  915. procedure TSQLDBTestCase.SetUp;
  916. begin
  917. inherited SetUp;
  918. InitialiseDBConnector;
  919. DBConnector.StartTest(TestName);
  920. end;
  921. procedure TSQLDBTestCase.TearDown;
  922. begin
  923. DBConnector.StopTest(TestName);
  924. if assigned(DBConnector) then
  925. with SQLDBConnector do
  926. if Assigned(Transaction) and Transaction.Active and not (stoUseImplicit in Transaction.Options) then
  927. Transaction.Rollback;
  928. FreeDBConnector;
  929. inherited TearDown;
  930. end;
  931. initialization
  932. if uppercase(dbconnectorname)='SQL' then
  933. begin
  934. RegisterTest(TTestTSQLQuery);
  935. RegisterTest(TTestTSQLConnection);
  936. RegisterTest(TTestTSQLScript);
  937. end;
  938. end.