testsqldb.pas 37 KB

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