testsqldb.pas 38 KB

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