testsqldb.pas 38 KB

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