querywindow.pas 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186
  1. unit QueryWindow;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, IBConnection, db, sqldb, FileUtil, LResources, Forms,
  6. Controls, Graphics, Dialogs, ExtCtrls, PairSplitter, StdCtrls, Buttons,
  7. DBGrids, Menus, ComCtrls, SynEdit, SynHighlighterSQL, Reg,
  8. SynEditTypes, SynCompletion, Clipbrd, grids, DbCtrls, types, LCLType,
  9. dbugintf, turbocommon, variants, strutils;
  10. type
  11. TQueryTypes = (
  12. qtUnknown=0,
  13. qtSelectable=1,
  14. qtExecute=2,
  15. qtScript=3);
  16. TQueryActions = (
  17. qaCommit,
  18. qaCommitRet,
  19. qaRollBack,
  20. qaRollbackRet,
  21. qaOpen,
  22. qaDDL,
  23. qaExec );
  24. { TQueryThread }
  25. TQueryThread = class(TThread)
  26. private
  27. FSQLQuery: TSQLQuery;
  28. FTrans: TSQLTransaction;
  29. FConnection: TIBConnection;
  30. public
  31. Error: Boolean;
  32. ErrorMsg: string;
  33. fTerminated: Boolean;
  34. fType: TQueryActions;
  35. fStatement: string;
  36. property Query: TSQLQuery read FSQLQuery write FSQLQuery;
  37. property Trans: TSQLTransaction read FTrans write FTrans;
  38. property Connection: TIBConnection read FConnection write FConnection;
  39. property Statement: String read fStatement write fStatement;
  40. procedure DoJob;
  41. procedure Execute; override;
  42. constructor Create(aType: TQueryActions);
  43. end;
  44. { TfmQueryWindow }
  45. TfmQueryWindow = class(TForm)
  46. cxAutoCommit: TCheckBox;
  47. FindDialog1: TFindDialog;
  48. bbClose: TSpeedButton;
  49. FontDialog1: TFontDialog;
  50. MenuItem4: TMenuItem;
  51. toolbarImages: TImageList;
  52. imTools: TImageList;
  53. imTabs: TImageList;
  54. lmCloseTab: TMenuItem;
  55. lmCopy: TMenuItem;
  56. lmPaste: TMenuItem;
  57. lmSelectAll: TMenuItem;
  58. lmUndo: TMenuItem;
  59. MainMenu1: TMainMenu;
  60. MenuItem1: TMenuItem;
  61. MenuItem10: TMenuItem;
  62. lmCut: TMenuItem;
  63. lmExport: TMenuItem;
  64. lmCommaDelimited: TMenuItem;
  65. lmHTML: TMenuItem;
  66. lmRedo: TMenuItem;
  67. MenuItem2: TMenuItem;
  68. lmFind: TMenuItem;
  69. lmFindAgain: TMenuItem;
  70. MenuItem3: TMenuItem;
  71. lmCopyCell: TMenuItem;
  72. lmExportAsComma: TMenuItem;
  73. lmExportAsHTML: TMenuItem;
  74. lmCopyAll: TMenuItem;
  75. MenuItem5: TMenuItem;
  76. lmRun: TMenuItem;
  77. lmRunSelect: TMenuItem;
  78. lmRunExec: TMenuItem;
  79. lmRunScript: TMenuItem;
  80. OpenDialog1: TOpenDialog;
  81. pgOutputPageCtl: TPageControl;
  82. Panel1: TPanel;
  83. pnlOutputPanel: TPanel;
  84. pmTab: TPopupMenu;
  85. pmMemo: TPopupMenu;
  86. pmGrid: TPopupMenu;
  87. SaveDialog1: TSaveDialog;
  88. Splitter1: TSplitter;
  89. meQuery: TSynEdit;
  90. SynCompletion1: TSynCompletion;
  91. SynSQLSyn1: TSynSQLSyn;
  92. ToolBar1: TToolBar;
  93. tbNew: TToolButton;
  94. tbOpen: TToolButton;
  95. tbSave: TToolButton;
  96. tbRun: TToolButton;
  97. tbCommit: TToolButton;
  98. tbRollback: TToolButton;
  99. tbCommitRetaining: TToolButton;
  100. tbRollbackRetaining: TToolButton;
  101. ToolButton1: TToolButton;
  102. ToolButton2: TToolButton;
  103. ToolButton3: TToolButton;
  104. tbHistory: TToolButton;
  105. ToolButton5: TToolButton;
  106. tbMenu: TToolButton;
  107. procedure bbRunClick(Sender: TObject);
  108. procedure DBGrid1DblClick(Sender: TObject);
  109. procedure DBGridTitleClick(column: TColumn);
  110. procedure FindDialog1Find(Sender: TObject);
  111. procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
  112. procedure FormCreate(Sender: TObject);
  113. procedure FormDestroy(Sender: TObject);
  114. procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  115. procedure FormShow(Sender: TObject);
  116. procedure lmCloseTabClick(Sender: TObject);
  117. procedure lmCommaDelimitedClick(Sender: TObject);
  118. procedure lmCopyAllClick(Sender: TObject);
  119. procedure lmCopyCellClick(Sender: TObject);
  120. procedure lmCopyClick(Sender: TObject);
  121. procedure lmCutClick(Sender: TObject);
  122. procedure lmExportAsCommaClick(Sender: TObject);
  123. procedure lmExportAsHTMLClick(Sender: TObject);
  124. procedure lmHTMLClick(Sender: TObject);
  125. procedure lmPasteClick(Sender: TObject);
  126. procedure lmRedoClick(Sender: TObject);
  127. procedure lmRunClick(Sender: TObject);
  128. procedure lmRunExecClick(Sender: TObject);
  129. procedure lmRunScriptClick(Sender: TObject);
  130. procedure lmRunSelectClick(Sender: TObject);
  131. procedure lmSelectAllClick(Sender: TObject);
  132. procedure lmUndoClick(Sender: TObject);
  133. procedure lmFindClick(Sender: TObject);
  134. procedure lmFindAgainClick(Sender: TObject);
  135. procedure MenuItem4Click(Sender: TObject);
  136. procedure meQueryKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  137. procedure SQLScript1Exception(Sender: TObject; Statement: TStrings;
  138. TheException: Exception; var Continue: boolean);
  139. procedure SynCompletion1CodeCompletion(var Value: string;
  140. SourceValue: string; var SourceStart, SourceEnd: TPoint;
  141. KeyChar: TUTF8Char; Shift: TShiftState);
  142. procedure tbCloseClick(Sender: TObject);
  143. procedure tbCommitClick(Sender: TObject);
  144. procedure tbCommitRetainingClick(Sender: TObject);
  145. procedure tbHistoryClick(Sender: TObject);
  146. procedure tbMenuClick(Sender: TObject);
  147. procedure tbNewClick(Sender: TObject);
  148. procedure tbOpenClick(Sender: TObject);
  149. procedure tbRollbackClick(Sender: TObject);
  150. procedure tbRollbackRetainingClick(Sender: TObject);
  151. procedure tbRunClick(Sender: TObject);
  152. procedure tbSaveClick(Sender: TObject);
  153. private
  154. { private declarations }
  155. FDBIndex: Integer; // Index of selected registered database
  156. FRegRec: TRegisteredDatabase;
  157. FOptions: set of TSynSearchOption;
  158. FIBConnection: TIBConnection;
  159. FSQLTrans: TSQLTransaction;
  160. FCanceled: Boolean;
  161. FStartLine: Integer;
  162. FQuery: TStringList; //query text
  163. FOrigQueryType: TQueryTypes;
  164. FFinished: Boolean;
  165. FQT: TQueryThread;
  166. FQueryPart: string;
  167. FTab: TTabSheet;
  168. FResultMemo: TMemo;
  169. FSQLScript: TSQLScript;
  170. // Text for caption
  171. FAText: string;
  172. FModifyCount: Integer;
  173. FCounter: Integer;
  174. OutputTabsList: TStrings;
  175. // Makes commit button in current tabsheet visible
  176. procedure EnableCommitButton;
  177. procedure ExecuteQuery;
  178. function GetNewTabNum: string;
  179. // Gets TSQLQuery of current result tabsheet - only if it is a select query
  180. function GetCurrentSelectQuery: TSQLQuery;
  181. // Gets both querytype and whether SQL is DML or DDL
  182. // Investigates QueryList[LookAtIndex] to find out
  183. function GetQuerySQLType(QueryList: TStringList; var LookAtIndex: Integer;
  184. var IsDDL: Boolean): TQueryTypes;
  185. procedure NewCommitButton(const Pan: TPanel; var ATab: TTabSheet);
  186. procedure RemoveComments(QueryList: TStringList; StartLine: Integer;
  187. var RealStartLine: Integer);
  188. procedure RemoveAllSingleLineComments(QueryList: TStringList);
  189. procedure RemoveEmptyLines(QueryList: TStringList;
  190. var SecondRealStart: Integer; const RealStartLine: Integer);
  191. procedure ApplyClick(Sender: TObject);
  192. procedure EnableApplyButton;
  193. function GetTableName(SQLText: string): string;
  194. procedure CommitResultClick(Sender: TObject);
  195. procedure RemovePreviousResultTabs;
  196. protected
  197. // This procedure will receive the events that are logged by the connection:
  198. procedure GetLogEvent(Sender: TSQLConnection; EventType: TDBEventType; Const Msg : String);
  199. public
  200. OnCommit: TNotifyEvent;
  201. procedure Init(dbIndex: Integer);
  202. function GetQueryType(AQuery: string): TQueryTypes;
  203. // Get query text from GUI/memo into
  204. // QueryContents
  205. function GetQuery(QueryContents: tstrings): boolean;
  206. function CreateResultTab(QueryType: TQueryTypes; var aSqlQuery: TSQLQuery; var aSQLScript: TSQLScript;
  207. var meResult: TMemo; AdditionalTitle: string = ''): TTabSheet;
  208. // Runs SQL script; returns result
  209. function ExecuteScript(Script: string): Boolean;
  210. // Create a new Apply button in the specified panel
  211. procedure NewApplyButton(var Pan: TPanel; var ATab: TTabSheet);
  212. // Returns whether query is DDL or DML
  213. function GetSQLType(Query: string; var Command: string): string;
  214. // Tries to split up text into separate queries
  215. function GetSQLSegment(QueryList: TStringList; StartLine: Integer;
  216. var QueryType: TQueryTypes; var EndLine: Integer;
  217. var SQLSegment: string; var IsDDL: Boolean): Boolean;
  218. procedure QueryAfterPost(DataSet: TDataSet);
  219. procedure QueryAfterScroll(DataSet: TDataSet);
  220. // Run query; use aQueryType to force running as e.g. script or open query
  221. procedure CallExecuteQuery(aQueryType: TQueryTypes);
  222. procedure SortSynCompletion;
  223. procedure ThreadTerminated(Sender: TObject);
  224. procedure EnableButtons;
  225. { public declarations }
  226. end;
  227. var
  228. fmQueryWindow: TfmQueryWindow;
  229. implementation
  230. uses main, SQLHistory;
  231. { TfmQueryWindow }
  232. { NewCommitButton: Create commit button for editable query result }
  233. procedure TfmQueryWindow.NewCommitButton(const Pan: TPanel; var ATab: TTabSheet);
  234. var
  235. Commit: TBitBtn;
  236. begin
  237. Commit:= TBitBtn.Create(self);
  238. Commit.Parent:= Pan;
  239. Commit.Caption:= 'Commit'; //don't change this; code looks for this exact caption
  240. Commit.Left:= 400;
  241. Commit.Visible:= False;
  242. Commit.OnClick:= @CommitResultClick;
  243. Commit.Tag:= ATab.TabIndex;
  244. end;
  245. { RemoveComments: Remove comments from Query window }
  246. procedure TfmQueryWindow.RemoveComments(QueryList: TStringList; StartLine: Integer; var RealStartLine: Integer);
  247. var
  248. Comment: Boolean;
  249. i: Integer;
  250. MultiComment: Boolean;
  251. begin
  252. MultiComment:= False;
  253. for i:= StartLine to QueryList.Count - 1 do
  254. begin
  255. if Pos('/*', Trim(QueryList[i])) = 1 then
  256. begin
  257. MultiComment:= True;
  258. Comment:= False;
  259. end;
  260. // Avoid checking for comments if there's any chance they're within
  261. // a string literal e.g. select 'this is -- no -- comment' from rdb$database
  262. if (not MultiComment) and (pos('''',QueryList[i])=0) then
  263. Comment:= Pos('--', Trim(QueryList[i])) = 1;
  264. if (Trim(QueryList[i]) <> '') and (not Comment) and (not MultiComment) then
  265. begin
  266. RealStartLine:= i;
  267. Break;
  268. end;
  269. if MultiComment and (Pos('*/', QueryList[i]) > 0) then // End of multi-line comment
  270. begin
  271. QueryList[i]:= Trim(Copy(QueryList[i], Pos('*/', QueryList[i]) + 2, Length
  272. (QueryList[i])));
  273. RealStartLine:= i;
  274. MultiComment:= False;
  275. Comment:= False;
  276. if (i = QueryList.Count - 1) or
  277. ((Trim(QueryList[i + 1]) <> '') and (Pos('/*', Trim(QueryList[i + 1])
  278. ) <> 1) and
  279. (Pos('--', Trim(QueryList[i + 1])) <> 1)) then
  280. Break;
  281. end;
  282. end;
  283. end;
  284. { RemoveAllSingleLineComments: remove single line comments from query }
  285. procedure TfmQueryWindow.RemoveAllSingleLineComments(QueryList: TStringList);
  286. var
  287. i: Integer;
  288. begin
  289. for i:= QueryList.Count - 1 downto 0 do
  290. begin
  291. if Pos('--', QueryList[i]) > 0 then
  292. begin
  293. if Pos('--', Trim(QueryList[i])) = 1 then
  294. QueryList.Delete(i);
  295. {
  296. else
  297. // this will also pick up -- within string literals which is wrong
  298. QueryList[i]:= Copy(QueryList[i], 1, Pos('--', QueryList[i]) - 1);
  299. }
  300. end;
  301. end;
  302. end;
  303. { RemoveEmptyLines: remove empty lines in query }
  304. procedure TfmQueryWindow.RemoveEmptyLines(QueryList: TStringList; var SecondRealStart: Integer;
  305. const RealStartLine: Integer);
  306. var
  307. i: integer;
  308. begin
  309. for i:= RealStartLine to QueryList.Count - 1 do
  310. begin
  311. if Trim(QueryList[i]) <> '' then
  312. begin
  313. SecondRealStart:= i;
  314. Break;
  315. end;
  316. end;
  317. end;
  318. { ApplyClick: Save Updates for the query }
  319. procedure TfmQueryWindow.ApplyClick(Sender: TObject);
  320. var
  321. i, x: Integer;
  322. TableName: string;
  323. UpdateQuery: TSQLQuery;
  324. PKIndexName: string;
  325. ConstraintName: string;
  326. KeyList, FieldsList: TStringList;
  327. WhereClause: string;
  328. UserData: TSQLQuery;
  329. TabIndex: Integer;
  330. FieldsSQL: string;
  331. begin
  332. try
  333. TabIndex:= pgOutputPageCtl.TabIndex;
  334. UserData:= nil;
  335. UserData:= GetCurrentSelectQuery;
  336. // Better safe than sorry
  337. if not(Assigned(UserData)) then
  338. begin
  339. ShowMessage('Error getting query from tabsheet.');
  340. {$IFDEF DEBUG}
  341. SendDebug('ApplyClick: GetRecordSet call returned nil recordset');
  342. {$ENDIF}
  343. exit;
  344. end;
  345. UserData.ApplyUpdates; // lets query run InsertSQL, UpdateSQL, DeleteSQL
  346. (Sender as TBitBtn).Visible:= False;
  347. // Auto commit
  348. if cxAutoCommit.Checked then
  349. FSQLTrans.Commit
  350. else
  351. EnableCommitButton;
  352. UserData.EnableControls;
  353. except
  354. on E: Exception do
  355. begin
  356. ShowMessage('Error trying to save data: ' + e.Message);
  357. end;
  358. end;
  359. end;
  360. { EnableApplyButton: enable save updates button on current tab when records have been modified }
  361. procedure TfmQueryWindow.EnableApplyButton;
  362. var
  363. i: Integer;
  364. Ctl: TControl;
  365. ParentPanel: TPanel;
  366. begin
  367. // The page has a panel that contains the button
  368. ParentPanel:=nil;
  369. for i:= 0 to pgOutputPageCtl.ActivePage.ControlCount-1 do
  370. begin
  371. Ctl:=pgOutputPageCtl.ActivePage.Controls[i];
  372. if Ctl is TPanel then
  373. begin
  374. ParentPanel:= TPanel(Ctl); //found
  375. break;
  376. end;
  377. end;
  378. // Found the hosting panel; this should have the Apply button
  379. // as well as the commit button and the tdbnavigator
  380. if assigned(ParentPanel) then
  381. begin
  382. for i:= 0 to ParentPanel.ControlCount-1 do
  383. begin
  384. Ctl:=ParentPanel.Controls[i];
  385. if (Ctl is TBitBtn) and
  386. ((Ctl as TBitBtn).Caption = 'Apply') then
  387. begin
  388. (Ctl as TBitBtn).Visible:= true;
  389. Break;
  390. end;
  391. end;
  392. end;
  393. end;
  394. { EnableCommitButton: enable commit button after applying updates }
  395. procedure TfmQueryWindow.EnableCommitButton;
  396. var
  397. i: Integer;
  398. Ctl: TControl;
  399. ParentPanel: TPanel;
  400. begin
  401. // The page has a panel that contains the button
  402. ParentPanel:=nil;
  403. for i:= 0 to pgOutputPageCtl.ActivePage.ControlCount-1 do
  404. begin
  405. Ctl:=pgOutputPageCtl.ActivePage.Controls[i];
  406. if Ctl is TPanel then
  407. begin
  408. ParentPanel:= TPanel(Ctl); //found
  409. break;
  410. end;
  411. end;
  412. // Found the hosting panel; this should have the Apply, Commit button
  413. // as well as the navigator
  414. if assigned(ParentPanel) then
  415. begin
  416. for i:= 0 to ParentPanel.ControlCount-1 do
  417. begin
  418. Ctl:=ParentPanel.Controls[i];
  419. if (Ctl is TBitBtn) and
  420. ((Ctl as TBitBtn).Caption = 'Commit') then
  421. begin
  422. (Ctl as TBitBtn).Visible:= true;
  423. Break;
  424. end;
  425. end;
  426. end;
  427. end;
  428. { GetTableName: get table name from query text }
  429. function TfmQueryWindow.GetTableName(SQLText: string): string;
  430. begin
  431. SQLText:= Trim(Copy(SQLText, Pos('from', LowerCase(SQLText)) + 4, Length(SQLText)));
  432. if Pos('"', SQLText) = 1 then
  433. begin
  434. Delete(SQLText, 1, 1);
  435. Result:= Copy(SQLText, 1, Pos('"', SQLText) - 1);
  436. end
  437. else
  438. begin
  439. if Pos(' ', SQLText) > 0 then
  440. Result:= Copy(SQLText, 1, Pos(' ', SQLText) - 1)
  441. else
  442. Result:= SQLText;
  443. end;
  444. if Pos(';', Result) > 0 then
  445. Delete(Result, Pos(';', Result), 1);
  446. end;
  447. { CommitResultClick: commit current transaction }
  448. procedure TfmQueryWindow.CommitResultClick(Sender: TObject);
  449. begin
  450. FSQLTrans.CommitRetaining;
  451. (Sender as TBitBtn).Visible:= False;
  452. end;
  453. procedure TfmQueryWindow.RemovePreviousResultTabs;
  454. var
  455. i: Integer;
  456. begin
  457. for i:= OutputTabsList.Count - 1 downto 0 do
  458. begin
  459. OutputTabsList.Objects[i].Free;
  460. OutputTabsList.Delete(i);
  461. end;
  462. end;
  463. procedure TfmQueryWindow.GetLogEvent(Sender: TSQLConnection;
  464. EventType: TDBEventType; const Msg: String);
  465. // Used to log everything sent through the connection
  466. var
  467. Source: string;
  468. begin
  469. case EventType of
  470. detCustom: Source:='Custom: ';
  471. detPrepare: Source:='Prepare: ';
  472. detExecute: Source:='Execute: ';
  473. detFetch: Source:='Fetch: ';
  474. detCommit: Source:='Commit: ';
  475. detRollBack: Source:='Rollback:';
  476. else Source:='Unknown event. Please fix program code.';
  477. end;
  478. SendDebug(Source + Msg);
  479. end;
  480. { GetCurrentSelectQuery: return result recordset of a page tab }
  481. function TfmQueryWindow.GetCurrentSelectQuery: TSQLQuery;
  482. var
  483. i: Integer;
  484. Ctl: TControl;
  485. begin
  486. // Tabsheet's tag property should point to any select query
  487. Result:= nil;
  488. if (pgOutputPageCtl.PageCount > 0) then
  489. begin
  490. if (pgOutputPageCtl.ActivePage.Tag<>0) then
  491. begin
  492. Result:= TSQLQuery(pgOutputPageCtl.ActivePage.Tag);
  493. end;
  494. end;
  495. end;
  496. { GetQuerySQLType: get query type: select, script, execute from current string list }
  497. function TfmQueryWindow.GetQuerySQLType(QueryList: TStringList; var LookAtIndex: Integer; var IsDDL: Boolean): TQueryTypes;
  498. var
  499. MassagedSQL: string;
  500. begin
  501. Result:= qtUnknown;
  502. IsDDL:= False; //default
  503. if LookAtIndex < QueryList.Count then
  504. begin
  505. MassagedSQL:= LowerCase(Trim(QueryList[LookAtIndex]));
  506. // Script overrides rest
  507. if Pos('set term', MassagedSQL) = 1 then
  508. begin
  509. // Using set term does not mean the SQL you're running has to be
  510. // DDL (could be an execute block or something) but it most probably is
  511. IsDDL:= true;
  512. exit(qtScript);
  513. end;
  514. if (Pos('select', MassagedSQL) = 1) then
  515. { todo: (low priority) misses insert...returning,
  516. update...returning, merge.. returning...}
  517. Result:= qtSelectable
  518. else
  519. begin
  520. Result:= qtExecute;
  521. IsDDL:= (Pos('alter', MassagedSQL) = 1) or
  522. (Pos('create', MassagedSQL) = 1) or
  523. (Pos('drop', MassagedSQL) = 1) or
  524. (Pos('grant', MassagedSQL) = 1) {actually DCL} or
  525. (Pos('revoke', MassagedSQL) = 1) {actually DCL};
  526. end;
  527. end;
  528. end;
  529. { TQueryThread }
  530. { DoJob: Execute thread job: open query, execute, commit, rollback, etc }
  531. procedure TQueryThread.DoJob;
  532. begin
  533. try
  534. if fType = qaOpen then
  535. FSQLQuery.Open
  536. else
  537. if fType = qaExec then
  538. FSQLQuery.ExecSQL
  539. else
  540. if fType = qaDDL then
  541. FConnection.ExecuteDirect(fStatement)
  542. else
  543. if fType = qaCommit then
  544. FTrans.Commit
  545. else
  546. if fType = qaCommitRet then
  547. FTrans.CommitRetaining
  548. else
  549. if fType = qaRollBack then
  550. FTrans.Rollback
  551. else
  552. if fType = qaRollbackRet then
  553. FTrans.RollbackRetaining;
  554. Error:= False;
  555. fTerminated:= True;
  556. except
  557. on E: Exception do
  558. begin
  559. Error:= True;
  560. ErrorMsg:= e.Message;
  561. fTerminated:= True;
  562. end;
  563. end;
  564. end;
  565. { Execute: Query thread main loop }
  566. procedure TQueryThread.Execute;
  567. begin
  568. try
  569. fTerminated:= False;
  570. Error:= False;
  571. DoJob;
  572. fTerminated:= True;
  573. except
  574. on E: Exception do
  575. begin
  576. Error:= True;
  577. ErrorMsg:= e.Message;
  578. fTerminated:= True;
  579. end;
  580. end;
  581. end;
  582. { Create query thread }
  583. constructor TQueryThread.Create(aType: TQueryActions);
  584. begin
  585. inherited Create(True);
  586. fType:= aType;
  587. FreeOnTerminate:= False;
  588. end;
  589. { Display SQL script exception message }
  590. procedure TfmQueryWindow.SQLScript1Exception(Sender: TObject;
  591. Statement: TStrings; TheException: Exception; var Continue: boolean);
  592. begin
  593. ShowMessage('Error running script: '+TheException.Message);
  594. end;
  595. procedure TfmQueryWindow.SynCompletion1CodeCompletion(var Value: string;
  596. SourceValue: string; var SourceStart, SourceEnd: TPoint; KeyChar: TUTF8Char;
  597. Shift: TShiftState);
  598. begin
  599. SynCompletion1.Deactivate;
  600. end;
  601. { Close button pressed: close current Query window and free parent page tab }
  602. procedure TfmQueryWindow.tbCloseClick(Sender: TObject);
  603. begin
  604. Close;
  605. Parent.Free;
  606. end;
  607. { Commit current transaction }
  608. procedure TfmQueryWindow.tbCommitClick(Sender: TObject);
  609. var
  610. meResult: TMemo;
  611. SqlQuery: TSQLQuery;
  612. SqlScript: TSQLScript;
  613. ATab: TTabSheet;
  614. QT: TQueryThread;
  615. begin
  616. ATab:= CreateResultTab(qtExecute, SqlQuery, SqlScript, meResult);
  617. QT:= TQueryThread.Create(qaCommit);
  618. try
  619. QT.Trans:= FSQLTrans;
  620. ATab.ImageIndex:= 6;
  621. // Run thread
  622. QT.Resume;
  623. repeat
  624. application.ProcessMessages;
  625. until QT.fTerminated;
  626. if QT.Error then
  627. begin
  628. ATab.ImageIndex:= 3;
  629. meResult.Lines.Text:= QT.ErrorMsg;
  630. meResult.Font.Color:= clRed;
  631. end
  632. else
  633. begin
  634. ATab.ImageIndex:= 4;
  635. meResult.Lines.Add('Commited');
  636. meResult.Font.Color:= clGreen;
  637. // Call OnCommit procedure if assigned, it is used to refresh table management view
  638. if OnCommit <> nil then
  639. OnCommit(self);
  640. OnCommit:= nil;
  641. end;
  642. finally
  643. QT.Free;
  644. end;
  645. end;
  646. { Commit retaining for current transaction }
  647. procedure TfmQueryWindow.tbCommitRetainingClick(Sender: TObject);
  648. var
  649. QT: TQueryThread;
  650. begin
  651. QT:= TQueryThread.Create(qaCommitRet);
  652. try
  653. QT.Trans:= FSQLTrans;
  654. // Run thread
  655. QT.Resume;
  656. repeat
  657. application.ProcessMessages;
  658. until QT.fTerminated;
  659. if QT.Error then
  660. ShowMessage('Error trying commit retaining: '+QT.ErrorMsg)
  661. else
  662. begin
  663. // Call OnCommit procedure if assigned, it is used to refresh table management view
  664. if OnCommit <> nil then
  665. OnCommit(self);
  666. OnCommit:= nil;
  667. end;
  668. finally
  669. QT.Free;
  670. end;
  671. end;
  672. {HistoryClick: show SQL history form }
  673. procedure TfmQueryWindow.tbHistoryClick(Sender: TObject);
  674. begin
  675. fmSQLHistory.Init(FRegRec.Title, Self);
  676. fmSQLHistory.Show;
  677. end;
  678. { Display popup menu }
  679. procedure TfmQueryWindow.tbMenuClick(Sender: TObject);
  680. begin
  681. pmTab.PopUp;
  682. end;
  683. { display New SQL Window tab }
  684. procedure TfmQueryWindow.tbNewClick(Sender: TObject);
  685. var
  686. i: Integer;
  687. begin
  688. // Get a free number to be assigned to the new Query window
  689. for i:= 1 to 1000 do
  690. begin
  691. if fmMain.FindQueryWindow(FRegRec.Title + ': Query Window # ' + IntToStr(i)) = nil then
  692. begin
  693. fmMain.ShowCompleteQueryWindow(FDBIndex, 'Query Window # ' + IntToStr(i), '');
  694. Break;
  695. end;
  696. end;
  697. end;
  698. { Read SQL query from text file }
  699. procedure TfmQueryWindow.tbOpenClick(Sender: TObject);
  700. begin
  701. OpenDialog1.DefaultExt:= '.sql';
  702. if OpenDialog1.Execute then
  703. meQuery.Lines.LoadFromFile(OpenDialog1.FileName);
  704. end;
  705. { RollBack current transaction }
  706. procedure TfmQueryWindow.tbRollbackClick(Sender: TObject);
  707. var
  708. meResult: TMemo;
  709. SqlQuery: TSQLQuery;
  710. SqlScript: TSQLScript;
  711. ATab: TTabSheet;
  712. QT: TQueryThread;
  713. begin
  714. ATab:= CreateResultTab(qtExecute, SqlQuery, SqlScript, meResult);
  715. QT:= TQueryThread.Create(qaRollBack);
  716. try
  717. QT.Trans:= FSQLTrans;
  718. ATab.ImageIndex:= 6;
  719. QT.Resume;
  720. repeat
  721. application.ProcessMessages;
  722. until QT.fTerminated;
  723. if QT.Error then
  724. begin
  725. ATab.ImageIndex:= 3;
  726. meResult.Lines.Text:= QT.ErrorMsg;
  727. meResult.Font.Color:= clRed;
  728. end
  729. else
  730. begin
  731. ATab.ImageIndex:= 4;
  732. meResult.Lines.Add('Rollback');
  733. meResult.Font.Color:= clGreen;
  734. if OnCommit <> nil then
  735. OnCommit(self);
  736. OnCommit:= nil;
  737. meResult.Font.Color:= $AA6666;
  738. end;
  739. finally
  740. QT.Free;
  741. end;
  742. end;
  743. { Rollback retaning for current transaction }
  744. procedure TfmQueryWindow.tbRollbackRetainingClick(Sender: TObject);
  745. var
  746. QT: TQueryThread;
  747. begin
  748. QT:= TQueryThread.Create(qaRollbackRet);
  749. try
  750. QT.Trans:= FSQLTrans;
  751. QT.Resume;
  752. repeat
  753. application.ProcessMessages;
  754. until QT.fTerminated or (FCanceled);
  755. if QT.Error then
  756. ShowMessage('Error trying rollback retaining: '+QT.ErrorMsg);
  757. finally
  758. QT.Free;
  759. end;
  760. end;
  761. { Run current SQL, auto-detect type }
  762. procedure TfmQueryWindow.tbRunClick(Sender: TObject);
  763. begin
  764. CallExecuteQuery(qtUnknown);
  765. end;
  766. { Save current SQL in a text file }
  767. procedure TfmQueryWindow.tbSaveClick(Sender: TObject);
  768. begin
  769. SaveDialog1.DefaultExt:= '.sql';
  770. if SaveDialog1.Execute then
  771. meQuery.Lines.SaveToFile(SaveDialog1.FileName);
  772. end;
  773. {GetNewTabNum: get last tab number and increase result by one }
  774. function TfmQueryWindow.GetNewTabNum: string;
  775. var
  776. i: Integer;
  777. Cnt: Integer;
  778. begin
  779. Cnt:= 0;
  780. for i:= 0 to pgOutputPageCtl.ControlCount - 1 do
  781. if pgOutputPageCtl.Pages[i].TabVisible then
  782. Inc(Cnt);
  783. Result:= IntToStr(Cnt);
  784. end;
  785. { Initialize query window: fill connection parameters from selected registered database }
  786. procedure TfmQueryWindow.Init(dbIndex: Integer);
  787. begin
  788. FDBIndex:= dbIndex;
  789. FRegRec:= fmMain.RegisteredDatabases[dbIndex].RegRec;
  790. // Remove old tabs in case of opening the same QueryWindow
  791. if Assigned(OutputTabsList) then
  792. RemovePreviousResultTabs
  793. else
  794. OutputTabsList:= TStringList.Create;
  795. // Set instances of FIBConnection and SQLTransaction for the current Query Window
  796. SetTransactionIsolation(FSQLTrans.Params);
  797. FSQLTrans.DataBase:= FIBConnection;
  798. // Set connection parameters to FIBConnection
  799. with fmMain.RegisteredDatabases[dbIndex] do
  800. begin
  801. Self.FIBConnection.DatabaseName:= RegRec.DatabaseName;
  802. Self.FIBConnection.UserName:= RegRec.UserName;
  803. Self.FIBConnection.Password:= RegRec.Password;
  804. Self.FIBConnection.CharSet:= RegRec.Charset;
  805. Self.FIBConnection.Role:= RegRec.Role;
  806. end;
  807. // Get current database tables to be highlighted in SQL query editor
  808. SynSQLSyn1.TableNames.CommaText:= fmMain.GetTableNames(dbIndex);
  809. SynCompletion1.ItemList.AddStrings(SynSQLSyn1.TableNames);
  810. SortSynCompletion;
  811. end;
  812. (************* Is Selectable (Check statement type Select, Update, Alter, etc) *******************)
  813. function TfmQueryWindow.GetQueryType(AQuery: string): TQueryTypes;
  814. var
  815. List: TStringList;
  816. i: Integer;
  817. Line: string;
  818. StartPos, EndPos: Integer;
  819. begin
  820. List:= TStringList.Create;
  821. try
  822. List.Text:= AQuery;
  823. Result:= qtExecute; // Default Execute
  824. for i:= 0 to List.Count - 1 do
  825. begin
  826. Line:= List[i];
  827. // Remove comments
  828. if Pos('--', Line) > 0 then
  829. Line:= Copy(Line, 1, Pos('--', Line) - 1);
  830. if (Pos('/*', Line) > 0) and (Pos('*/', Line) > 0) then
  831. begin
  832. StartPos:= (Pos('/*', Line));
  833. EndPos:= (Pos('*/', Line));
  834. Delete(Line, StartPos, EndPos - StartPos + 1);
  835. end;
  836. if (Pos('select', LowerCase(Trim(Line))) = 1) then
  837. begin
  838. Result:= qtSelectable; // Selectable
  839. Break;
  840. end
  841. else
  842. if Pos('set term', LowerCase(Trim(Line))) = 1 then
  843. begin
  844. Result:= qtScript;
  845. Break;
  846. end;
  847. if Trim(Line) <> '' then
  848. begin
  849. Result:= qtExecute; // Executable
  850. Break;
  851. end;
  852. end;
  853. finally
  854. List.Free;
  855. end;
  856. end;
  857. { GetQuery: get query text from editor }
  858. function TfmQueryWindow.GetQuery(QueryContents: tstrings): boolean;
  859. var
  860. Seltext: string;
  861. begin
  862. Result:= false;
  863. if assigned(QueryContents) then
  864. begin
  865. SelText:= trim(meQuery.SelText);
  866. if SelTExt<>'' then
  867. QueryContents.Text:= SelText
  868. else
  869. QueryContents.Text:= trim(meQuery.Lines.Text);
  870. Result:= true;
  871. end;
  872. end;
  873. { Create new result tab depending on query type }
  874. function TfmQueryWindow.CreateResultTab(QueryType: TQueryTypes;
  875. var aSqlQuery: TSQLQuery; var aSQLScript: TSQLScript; var meResult: TMemo;
  876. AdditionalTitle: string): TTabSheet;
  877. var
  878. ATab: TTabSheet;
  879. DBGrid: TDBGrid;
  880. DataSource: TDataSource;
  881. StatusBar: TStatusBar;
  882. Nav: TDBNavigator;
  883. Pan: TPanel;
  884. begin
  885. ATab:= TTabSheet.Create(nil);
  886. OutputTabsList.AddObject('', ATab);
  887. BeginUpdateBounds;
  888. Result:= ATab;
  889. ATab.Parent:= pgOutputPageCtl;
  890. pgOutputPageCtl.ActivePage:= ATab; //set focus to new tab
  891. ATab.Caption:= 'Result # ' + GetNewTabNum + ' ' + AdditionalTitle;
  892. if QueryType = qtSelectable then // Select, need record set result
  893. begin
  894. // Query
  895. // Clean up any existing object to avoid memory leak
  896. if assigned(aSQLQuery) then
  897. aSQLQuery.Free;
  898. aSqlQuery:= TSQLQuery.Create(self);
  899. aSqlQuery.DataBase:= FIBConnection;
  900. aSqlQuery.Transaction:= FSQLTrans;
  901. aSqlQuery.AfterPost:= @QueryAfterPost; //detect user-edited grid
  902. aSqlQuery.AfterScroll:= @QueryAfterScroll;
  903. aSqlQuery.Tag:= ATab.TabIndex; //Query points to tabsheet number
  904. {Tab points to query object so we can look it up more easily via the
  905. tab sheet if we need to enable Apply/Commit buttons etc}
  906. ATab.Tag:= PtrInt(aSQLQuery);
  907. // Status Bar
  908. StatusBar:= TStatusBar.Create(ATab);
  909. StatusBar.Parent:= ATab;
  910. StatusBar.Tag:= aSqlQuery.Tag;
  911. // Datasource
  912. DataSource:= TDataSource.Create(self);
  913. DataSource.DataSet:= aSqlQuery;
  914. // Panel
  915. pan:= TPanel.Create(self);
  916. pan.Parent:= ATab;
  917. Pan.Height:= 30;
  918. Pan.Align:= alTop;
  919. // Query result Grid
  920. DBGrid:= TDBGrid.Create(self);
  921. DBGrid.Parent:= ATab;
  922. DBGrid.DataSource:= DataSource;
  923. DBGrid.Align:= alClient;
  924. DBGrid.OnDblClick:= @DBGrid1DblClick;
  925. DBGrid.Tag:= ATab.TabIndex;
  926. DBGrid.ReadOnly:= False;
  927. DBGrid.AutoEdit:= True;
  928. DBGrid.PopupMenu:= pmGrid;
  929. DBGrid.TitleStyle:= tsNative;
  930. DBGrid.Options:= DBGrid.Options + [dgAutoSizeColumns, dgHeaderHotTracking, dgHeaderPushedLook, dgAnyButtonCanSelect];
  931. DBGrid.OnTitleClick:= @DBGridTitleClick;
  932. // Navigator
  933. Nav:= TDBNavigator.Create(self);
  934. Nav.Parent:= Pan;
  935. Nav.VisibleButtons:= [nbFirst, nbNext, nbPrior, nbLast];
  936. Nav.DataSource:= DataSource;
  937. // Apply button
  938. NewApplyButton(Pan, ATab);
  939. // Commit button
  940. NewCommitButton(Pan, ATab);
  941. end
  942. else
  943. if QueryType in [qtExecute, qtScript] then
  944. begin
  945. meResult:= TMemo.Create(self);
  946. meResult.Parent:= ATab;
  947. meResult.ReadOnly:= True;
  948. meResult.Align:= alClient;
  949. case QueryType of
  950. qtExecute:
  951. begin
  952. aSqlQuery:= TSQLQuery.Create(self);
  953. aSqlQuery.DataBase:= FIBConnection;
  954. aSqlQuery.Transaction:= FSQLTrans;
  955. end;
  956. qtScript: // Script
  957. begin
  958. // Clean up to avoid memory leak
  959. if assigned(aSQLScript) then
  960. aSQLScript.Free;
  961. aSQLScript:= TSQLScript.Create(self);
  962. aSQLScript.DataBase:= FIBConnection;
  963. aSQLScript.Transaction:= FSQLTrans;
  964. aSQLScript.CommentsInSQL:= true;
  965. aSQLScript.UseSetTerm:= true; //needed if set term is used, e.g. for stored procedures
  966. end;
  967. end;
  968. end;
  969. end;
  970. (*************** Execute Query ******************)
  971. procedure TfmQueryWindow.ExecuteQuery;
  972. var
  973. StartTime: TDateTime;
  974. SqlType: string;
  975. EndLine: Integer;
  976. Command: string;
  977. IsDDL: Boolean;
  978. Affected: Integer;
  979. fQueryType: TQueryTypes;
  980. TempQuery: TSQLQuery;
  981. SanitizedSQL: string;
  982. i: integer;
  983. fSQLQuery: TSQLQuery;
  984. begin
  985. try
  986. // Script
  987. if (FOrigQueryType = qtScript) then
  988. begin // script
  989. ExecuteScript(FQuery.Text);
  990. Inc(FModifyCount);
  991. SqlType:= GetSQLType(FQuery.Text, Command);
  992. fmMain.AddToSQLHistory(FRegRec.Title, SqlType, FQuery.Text);
  993. FFinished:= True;
  994. FQuery.Clear;
  995. end
  996. else // normal statement / Multi statements
  997. begin
  998. Inc(FCounter);
  999. if not GetSQLSegment(FQuery, FStartLine, fQueryType, EndLine, FQueryPart, IsDDL) then
  1000. begin
  1001. FFinished:= True;
  1002. Exit;
  1003. end;
  1004. {if EndLine < FStartLine then
  1005. FStartLine:= FStartLine + 1
  1006. else}
  1007. FStartLine:= EndLine + 1;
  1008. if Trim(FQueryPart) <> '' then // Select
  1009. if fQueryType = qtSelectable then
  1010. begin
  1011. FTab:= nil;
  1012. try
  1013. fSQLQuery:= TSQLQuery.Create(self);
  1014. fSQLQuery.DataBase:= FIBConnection;
  1015. fSQLQuery.Transaction:= FSQLTrans;
  1016. if cxAutoCommit.Checked then
  1017. FSQLTrans.Commit;
  1018. FTab:= CreateResultTab(qtSelectable, FSQLQuery, FSQLScript, FResultMemo);
  1019. FTab.ImageIndex:= 6;
  1020. FTab.Hint:= FQueryPart;
  1021. FTab.ShowHint:= True;
  1022. FSQLQuery.SQL.Text:= FQueryPart;
  1023. // Work around sqldb not detecting insert/updatesql for FIRST x queries
  1024. // Massage the SQL, assign it to a temp query and use the insertquery
  1025. // etc generated by sqldb.
  1026. // Support for ROWS x TO y at the end of the statement could be
  1027. // added perhaps
  1028. if (pos('select first ',lowercase(FQueryPart))=1) then
  1029. begin
  1030. // Get rid of the select first x part by copying everything after
  1031. // the third word
  1032. SanitizedSQL:= ExtractWordPos(3, FQueryPart, StdWordDelims, i);
  1033. if i > 0 then
  1034. begin
  1035. SanitizedSQL:= 'select ' + trim(copy(FQueryPart, i+length(SanitizedSQL), maxint));
  1036. TempQuery:= TSQLQuery.Create(nil);
  1037. try
  1038. TempQuery.ParseSQL:= true;
  1039. FSQLQuery.InsertSQL:= TempQuery.InsertSQL;
  1040. FSQLQuery.UpdateSQL:= TempQuery.UpdateSQL;
  1041. FSQLQuery.DeleteSQL:= TempQuery.DeleteSQL;
  1042. finally
  1043. TempQuery.Free;
  1044. end;
  1045. end;
  1046. end;
  1047. // Create thread to open dataset
  1048. FQT:= TQueryThread.Create(qaOpen);
  1049. FQT.Query:= FSQLQuery;
  1050. FQT.Trans:= FSQLTrans;
  1051. // FQT.OnTerminate:= @ThreadTerminated;
  1052. FAText:= FTab.Caption;
  1053. FTab.Caption:= 'Running..';
  1054. FQT.Resume;
  1055. // Wait for the thread to complete
  1056. repeat
  1057. Sleep(100);
  1058. application.ProcessMessages; // This prevents display freeze
  1059. until FQT.fTerminated;
  1060. // Raise exception if an error occured during thread execution (Open)
  1061. if FQT.Error then
  1062. raise Exception.Create(FQT.ErrorMsg);
  1063. FQT.Free;
  1064. FTab.Caption:= FAText;
  1065. FTab.ImageIndex:= 0;
  1066. fmMain.AddToSQLHistory(FRegRec.Title, 'SELECT', FQueryPart);
  1067. except
  1068. on e: Exception do
  1069. begin
  1070. if Assigned(FTab) then
  1071. FTab.TabVisible:= False;
  1072. FTab:= CreateResultTab(qtExecute, FSQLQuery, FSQLScript, FResultMemo);
  1073. pgOutputPageCtl.ActivePage:= FTab;
  1074. FResultMemo.Text:= e.message;
  1075. FResultMemo.Lines.Add(FQueryPart);
  1076. FResultMemo.Font.Color:= clRed;
  1077. FTab.Font.Color:= clRed;
  1078. FTab.ImageIndex:= 3;
  1079. end;
  1080. end;
  1081. end
  1082. else // Execute
  1083. if fQueryType = qtExecute then
  1084. begin
  1085. FTab:= nil;
  1086. FTab:= CreateResultTab(qtExecute, FSQLQuery, FSQLScript, FResultMemo);
  1087. FTab.ImageIndex:= 1;
  1088. SqlType:= GetSQLType(FQueryPart, Command);
  1089. StartTime:= Now;
  1090. Affected:= 0;
  1091. try
  1092. if IsDDL then
  1093. begin
  1094. // Execute the statement in thread
  1095. FQT:= TQueryThread.Create(qaDDL);
  1096. FQT.Connection:= FIBConnection;
  1097. FQT.Trans:= FSQLTrans;
  1098. FQT.Statement:= FQueryPart;
  1099. FQT.Resume;
  1100. FAText:= FTab.Caption;
  1101. FTab.Caption:= 'Running..';
  1102. // Wait for thread completion
  1103. repeat
  1104. application.ProcessMessages;
  1105. until (FQT.fTerminated) or (FCanceled);
  1106. // Raise exception if an error occured during thread execution (ExecProc)
  1107. if FQT.Error then
  1108. raise Exception.Create(FQT.ErrorMsg);
  1109. FTab.Caption:= FAText;
  1110. // Auto commit
  1111. if cxAutoCommit.Checked then
  1112. FSQLTrans.CommitRetaining;
  1113. FQT.Free;
  1114. end
  1115. else
  1116. begin // DML
  1117. FSQLQuery.Close;
  1118. FSQLQuery.SQL.Text:= FQueryPart;
  1119. FTab.ImageIndex:= 6;
  1120. FTab.Hint:= FQueryPart;
  1121. FTab.ShowHint:= True;
  1122. FSQLQuery.SQL.Text:= FQueryPart;
  1123. // Execute the statement in thread
  1124. FQT:= TQueryThread.Create(qaExec);
  1125. try
  1126. FQT.Query:= FSQLQuery;
  1127. FQT.Trans:= FSQLTrans;
  1128. FQT.Resume;
  1129. FAText:= FTab.Caption;
  1130. FTab.Caption:= 'Running..';
  1131. // Wait for thread completion
  1132. repeat
  1133. application.ProcessMessages;
  1134. until (FQT.fTerminated) or (FCanceled);
  1135. // Raise exception if an error occured during thread execution (ExecProc)
  1136. if FQT.Error then
  1137. raise Exception.Create(FQT.ErrorMsg);
  1138. // Auto commit
  1139. if cxAutoCommit.Checked then
  1140. FSQLTrans.CommitRetaining;
  1141. finally
  1142. FQT.Free;
  1143. end;
  1144. FTab.Caption:= FAText;
  1145. FTab.ImageIndex:= 1;
  1146. Affected:= FSQLQuery.RowsAffected;
  1147. end;
  1148. Inc(FModifyCount);
  1149. fmMain.AddToSQLHistory(FRegRec.Title, SQLType, FQueryPart);
  1150. FResultMemo.Visible:= True;
  1151. FResultMemo.Clear;
  1152. FResultMemo.Lines.Add('statement #' + IntToStr(FCounter));
  1153. if IsDDL then
  1154. FResultMemo.Lines.Add(FormatDateTime('hh:nn:ss.z', Now) + ' - DDL Executed. Takes (H:M:S.MS) ' +
  1155. FormatDateTime('HH:nn:ss.z', Now - StartTime))
  1156. else // DML
  1157. begin
  1158. FResultMemo.Lines.Add(FormatDateTime('hh:nn:ss.z', Now) + ' - DML Executed. Takes (H:M:S.MS) ' +
  1159. FormatDateTime('HH:nn:ss.z', Now - StartTime));
  1160. FResultMemo.Lines.Add('Rows affected: ' + Format('%3.0n', [Affected / 1]));
  1161. end;
  1162. FResultMemo.Lines.Add('----');
  1163. FResultMemo.Lines.Add(FQueryPart);
  1164. except
  1165. on E: Exception do
  1166. begin
  1167. if Assigned(FTab) then
  1168. FTab.TabVisible:= False;
  1169. FTab:= CreateResultTab(qtExecute, FSQLQuery, FSQLScript, FResultMemo);
  1170. pgOutputPageCtl.ActivePage:= FTab;
  1171. FResultMemo.Text:= e.message;
  1172. FResultMemo.Lines.Add(FQueryPart);
  1173. FResultMemo.Font.Color:= clRed;
  1174. FTab.Font.Color:= clRed;
  1175. FTab.ImageIndex:= 3;
  1176. end;
  1177. end;
  1178. end
  1179. else // Script
  1180. begin
  1181. try
  1182. if ExecuteScript(FQueryPart) then
  1183. begin
  1184. Inc(FModifyCount);
  1185. SqlType:= GetSQLType(FQueryPart, Command);
  1186. fmMain.AddToSQLHistory(FRegRec.Title, SqlType, FQueryPart);
  1187. end;
  1188. except
  1189. on E: Exception do
  1190. begin
  1191. if Assigned(FTab) then
  1192. FTab.TabVisible:= False;
  1193. FTab:= CreateResultTab(qtExecute, FSQLQuery, FSQLScript, FResultMemo);
  1194. pgOutputPageCtl.ActivePage:= FTab;
  1195. FResultMemo.Text:= e.message;
  1196. FResultMemo.Lines.Add(FQueryPart);
  1197. FResultMemo.Lines.Add('--------');
  1198. FResultMemo.Font.Color:= clRed;
  1199. FTab.Font.Color:= clRed;
  1200. FTab.ImageIndex:= 3;
  1201. end;
  1202. end;
  1203. end;
  1204. if (FModifyCount > 50) then
  1205. begin
  1206. if (MessageDlg('Commit', 'There are too many transactions, do you want to commit',
  1207. mtConfirmation, [mbYes, mbNo], 0) = mrYes) then
  1208. begin
  1209. FSQLTrans.CommitRetaining;
  1210. FModifyCount:= 0;
  1211. end
  1212. else
  1213. begin
  1214. FModifyCount:= 0;
  1215. end;
  1216. end;
  1217. if FStartLine >= FQuery.Count then
  1218. FFinished:= True;
  1219. end;
  1220. except
  1221. on E: Exception do
  1222. begin
  1223. if Assigned(FTab) then
  1224. FTab.TabVisible:= False;
  1225. FTab:= CreateResultTab(qtExecute, FSQLQuery, FSQLScript, FResultMemo);
  1226. FTab.ImageIndex:= 2;
  1227. pgOutputPageCtl.ActivePage:= FTab;
  1228. FResultMemo.Text:= e.message;
  1229. FResultMemo.Lines.Add('--------');
  1230. FResultMemo.Lines.Add(FQueryPart);
  1231. FResultMemo.Font.Color:= clRed;
  1232. FFinished:= True;
  1233. end;
  1234. end;
  1235. end;
  1236. { Execute script }
  1237. function TfmQueryWindow.ExecuteScript(Script: string): Boolean;
  1238. var
  1239. StartTime: TDateTime;
  1240. SqlQuery: TSQLQuery;
  1241. SqlScript: TSQLScript;
  1242. meResult: TMemo;
  1243. ATab: TTabSheet;
  1244. begin
  1245. StartTime:= Now;
  1246. ATab:= nil;
  1247. SQLScript:= nil;
  1248. try
  1249. // CreateResultTab creates the SQLScript object for us.
  1250. ATab:= CreateResultTab(qtScript, SqlQuery, SQLScript, meResult);
  1251. try
  1252. ATab.ImageIndex:= 2;
  1253. SQLScript.Script.Text:= Script;
  1254. {$IFDEF DEBUG}
  1255. SendDebug('going to run script: '+SQLScript.Script.Text);
  1256. {$Endif}
  1257. SQLScript.ExecuteScript;
  1258. // Auto commit
  1259. if cxAutoCommit.Checked then
  1260. FSQLTrans.CommitRetaining;
  1261. Result:= True;
  1262. meResult.Lines.Text:= FormatDateTime('hh:nn:ss.z', Now) + ' - Script Executed. It took (H:M:S.MS) ' +
  1263. FormatDateTime('HH:nn:ss.z', Now - StartTime);
  1264. meResult.Lines.Add('--------');
  1265. meResult.Lines.Add(Script);
  1266. finally
  1267. SQLScript.Free;
  1268. end;
  1269. except
  1270. on E: Exception do
  1271. begin
  1272. {$IFDEF DEBUG}
  1273. SendDebug('ExecuteScript failed; error '+E.Message);
  1274. {$Endif}
  1275. Result:= False;
  1276. if Assigned(ATab) then
  1277. ATab.TabVisible:= False;
  1278. ATab:= CreateResultTab(qtExecute, SqlQuery, SqlScript, meResult);
  1279. pgOutputPageCtl.ActivePage:= ATab;
  1280. meResult.Text:= e.Message;
  1281. meResult.Lines.Add('--------');
  1282. meResult.Lines.Add(Script);
  1283. meResult.Font.Color:= clRed;
  1284. ATab.Font.Color:= clRed;
  1285. ATab.ImageIndex:= 3;
  1286. end;
  1287. end;
  1288. end;
  1289. { Display new Save/Apply button for current query result been edited }
  1290. procedure TfmQueryWindow.NewApplyButton(var Pan: TPanel; var ATab: TTabSheet);
  1291. var
  1292. Apply: TBitBtn;
  1293. begin
  1294. Apply:= TBitBtn.Create(self);
  1295. Apply.Parent:= Pan;
  1296. Apply.Caption:= 'Apply'; //don't change this; code looks for this exact caption
  1297. Apply.Left:= 300;
  1298. Apply.Visible:= False;
  1299. Apply.OnClick:= @ApplyClick;
  1300. Apply.Tag:= ATab.TabIndex;
  1301. end;
  1302. { GetSQLType: get SQL type of current SQL text }
  1303. function TfmQueryWindow.GetSQLType(Query: string; var Command: string): string;
  1304. begin
  1305. Result:= 'DML'; //default
  1306. Query:= Trim(Query);
  1307. if (Query <> '') and (Pos(' ', Query) > 0) then
  1308. begin
  1309. // to do: this does not take comments into account...
  1310. Command:= Copy(Query, 1, Pos(' ', Query) - 1);
  1311. Command:= LowerCase(Command);
  1312. if (Command = 'alter') or
  1313. (Command = 'create') or
  1314. (Command = 'drop') or
  1315. (Command = 'grant') {actually DCL} or
  1316. (Command = 'revoke') {actually DCL} then
  1317. Result:= 'DDL';
  1318. end;
  1319. end;
  1320. { GetSQLSeqment: read part of SQL end by ; }
  1321. function TfmQueryWindow.GetSQLSegment(QueryList: TStringList; StartLine: Integer;
  1322. var QueryType: TQueryTypes; var EndLine: Integer;
  1323. var SQLSegment: string; var IsDDL: Boolean): Boolean;
  1324. var
  1325. i: Integer;
  1326. RealStartLine: Integer;
  1327. SecondRealStart: Integer;
  1328. BeginExists: Boolean;
  1329. begin
  1330. // Get start
  1331. SQLSegment:= '';
  1332. RealStartLine:= StartLine;
  1333. SecondRealStart:= RealStartLine;
  1334. Result:= False;
  1335. // Remove comments
  1336. RemoveAllSingleLineComments(QueryList);
  1337. RemoveComments(QueryList, StartLine, RealStartLine);
  1338. SecondRealStart:= RealStartLine;
  1339. // remove empty lines
  1340. RemoveEmptyLines(QueryList, SecondRealStart, RealStartLine);
  1341. // Get SQL type
  1342. QueryType:= GetQuerySQLType(QueryList, SecondRealStart, IsDDL);
  1343. // Concatenate
  1344. SQLSegment:= '';
  1345. BeginExists:= False;
  1346. for i:= SecondRealStart to QueryList.Count - 1 do
  1347. begin
  1348. if Pos('begin', Trim(LowerCase(QueryList[i]))) > 0 then
  1349. BeginExists:= True;
  1350. SQLSegment:= SQLSegment + QueryList[i] + LineEnding;
  1351. if (QueryType in [qtSelectable, qtExecute]) and
  1352. (((Pos(';', QueryList[i]) > 0) and (Not BeginExists)) or
  1353. ((Pos('end', LowerCase(Trim(QueryList[i]))) = 1) and BeginExists)
  1354. or (i = QueryList.Count - 1)) then
  1355. begin
  1356. Result:= True;
  1357. if (not BeginExists) and (Pos(';', QueryList[i]) > 0) then
  1358. begin
  1359. QueryList[i]:= Trim(Copy(QueryList[i], Pos(';', QueryList[i]) + 1, Length(QueryList[i])));
  1360. if QueryList[i] = '' then
  1361. EndLine:= i
  1362. else
  1363. begin
  1364. EndLine:= i - 1;
  1365. SQLSegment:= Trim(Copy(SQLSegment, 1, Pos(';', SQLSegment)));
  1366. end;
  1367. end
  1368. else
  1369. EndLine:= i;
  1370. Break;
  1371. end
  1372. else
  1373. if (QueryType = qtScript) and
  1374. ((i > SecondRealStart) and (Pos('set term', LowerCase(Trim(QueryList[i]))) = 1)) or
  1375. (i = QueryList.Count - 1) then
  1376. begin
  1377. Result:= True;
  1378. EndLine:= i;
  1379. Break;
  1380. end;
  1381. end;
  1382. end;
  1383. procedure TfmQueryWindow.QueryAfterPost(DataSet: TDataSet);
  1384. begin
  1385. // User has edited cells, so let him save
  1386. EnableApplyButton;
  1387. end;
  1388. { Run query, 0 for auto-detect query type }
  1389. procedure TfmQueryWindow.bbRunClick(Sender: TObject);
  1390. begin
  1391. CallExecuteQuery(qtUnknown);
  1392. end;
  1393. { Display Blob contents in a message box }
  1394. procedure TfmQueryWindow.DBGrid1DblClick(Sender: TObject);
  1395. begin
  1396. ShowMessage('Field contents: ' + LineEnding +
  1397. (Sender as TDBGrid).SelectedField.AsString)
  1398. end;
  1399. { Sort by columns }
  1400. procedure TfmQueryWindow.DBGridTitleClick(column: TColumn);
  1401. var
  1402. SqlQuery: TSQLQuery;
  1403. // indexoption : TIndexOptions;
  1404. begin
  1405. SQLQuery:= nil;
  1406. SqlQuery:= GetCurrentSelectQuery;
  1407. if (assigned(SqlQuery)) and
  1408. (SqlQuery.IndexFieldNames = Column.Field.FieldName) then
  1409. SqlQuery.IndexFieldNames := Column.Field.FieldName //+ 'DESC'
  1410. // indexoption :=[ixDescending];
  1411. // SqlQuery.AddIndex('',Column.Field.FieldName,indexoption,'');
  1412. else
  1413. SqlQuery.IndexFieldNames := Column.Field.FieldName
  1414. end;
  1415. { Find text }
  1416. procedure TfmQueryWindow.FindDialog1Find(Sender: TObject);
  1417. begin
  1418. FOptions:= [];
  1419. if frMatchCase in FindDialog1.Options then
  1420. FOptions:= FOptions + [ssoMatchCase];
  1421. if frWholeWord in FindDialog1.Options then
  1422. FOptions:= FOptions + [ssoWholeWord];
  1423. if not (frDown in FindDialog1.Options) then
  1424. FOptions:= FOptions + [ssoBackwards];
  1425. if frEntireScope in FindDialog1.Options then
  1426. FOptions:= FOptions + [ssoEntireScope];
  1427. meQuery.SearchReplace(FindDialog1.FindText, '', FOptions);
  1428. end;
  1429. { QueryWindow onClose event, commit active transaction, remove controls }
  1430. procedure TfmQueryWindow.FormClose(Sender: TObject;
  1431. var CloseAction: TCloseAction);
  1432. begin
  1433. // Check if the transaction is active; then commit it
  1434. if FSQLTrans.Active then
  1435. begin
  1436. FSQLTrans.Commit;
  1437. if OnCommit <> nil then
  1438. OnCommit(self);
  1439. OnCommit:= nil;
  1440. end;
  1441. FIBConnection.Close;
  1442. OutputTabsList.Free;
  1443. RemovePreviousResultTabs;
  1444. CloseAction:= caFree;
  1445. end;
  1446. { Initialize auto-completion text in QueryWindow OnCreate event }
  1447. procedure TfmQueryWindow.FormCreate(Sender: TObject);
  1448. begin
  1449. OutputTabsList:= nil;
  1450. {$IFNDEF DEBUG}
  1451. // Do not log to debug server if built as release instead of debug
  1452. SetDebuggingEnabled(false);
  1453. {$ENDIF}
  1454. FQuery:= TStringList.Create;
  1455. // Initialize new instance of FIBConnection and SQLTransaction
  1456. FIBConnection:= TIBConnection.Create(nil);
  1457. {$IFDEF DEBUG}
  1458. FIBConnection.OnLog:=@GetLogEvent;
  1459. FIBConnection.LogEvents:=[detCustom,detExecute,detCommit,detRollBack];
  1460. {$ENDIF DEBUG}
  1461. FSQLTrans:= TSQLTransaction.Create(nil);
  1462. SynCompletion1.ItemList.CommaText:= 'create,table,Select,From,INTEGER,FLOAT';
  1463. SortSynCompletion;
  1464. end;
  1465. procedure TfmQueryWindow.FormDestroy(Sender: TObject);
  1466. begin
  1467. // Clean up resources to avoid memory leaks
  1468. FSQLTrans.Free;
  1469. FIBConnection.Free;
  1470. FQuery.Free;
  1471. end;
  1472. procedure TfmQueryWindow.FormKeyDown(Sender: TObject; var Key: Word;
  1473. Shift: TShiftState);
  1474. begin
  1475. if (ssCtrl in Shift) and
  1476. ((Key=VK_F4) or (Key=VK_W)) then
  1477. begin
  1478. if ((Trim(meQuery.Lines.Text) = '') or
  1479. (MessageDlg('Do you want to close this query window?', mtConfirmation, [mbNo, mbYes], 0) = mrYes))
  1480. then
  1481. begin
  1482. // Close when pressing Ctrl-W or Ctrl-F4 (Cmd-W/Cmd-F4 on OSX)
  1483. Close;
  1484. Parent.Free;
  1485. end;
  1486. end;
  1487. end;
  1488. { focus on Query SQL window editor on form show }
  1489. procedure TfmQueryWindow.FormShow(Sender: TObject);
  1490. begin
  1491. meQuery.SetFocus;
  1492. end;
  1493. { Close current Query window }
  1494. procedure TfmQueryWindow.lmCloseTabClick(Sender: TObject);
  1495. begin
  1496. if (Trim(meQuery.Lines.Text) = '') or
  1497. (MessageDlg('Do you want to close this query window?', mtConfirmation, [mbNo, mbYes], 0) = mrYes) then
  1498. begin
  1499. Close;
  1500. Parent.Free;
  1501. end;
  1502. end;
  1503. { Save query result in a comma delimited file }
  1504. procedure TfmQueryWindow.lmCommaDelimitedClick(Sender: TObject);
  1505. var
  1506. i: Integer;
  1507. F: TextFile;
  1508. SqlQuery: TSQLQuery;
  1509. begin
  1510. SaveDialog1.DefaultExt:= '.txt';
  1511. SqlQuery:= nil;
  1512. SqlQuery:= GetCurrentSelectQuery;
  1513. if not(assigned(SqlQuery)) then
  1514. begin
  1515. ShowMessage('There is no recordset in result');
  1516. Exit;
  1517. end;
  1518. if (not SqlQuery.Active) or (SqlQuery.RecordCount = 0) then
  1519. MessageDlg('No data', mtError, [mbOk], 0)
  1520. else
  1521. if SaveDialog1.Execute then
  1522. begin
  1523. SqlQuery.DisableControls;
  1524. SqlQuery.First;
  1525. AssignFile(F, SaveDialog1.FileName);
  1526. Rewrite(F);
  1527. for i:= 0 to SqlQuery.FieldCount - 1 do
  1528. begin
  1529. Write(F, '"', SqlQuery.Fields[i].FieldName, '"');
  1530. if i = SqlQuery.FieldCount - 1 then
  1531. Writeln(F)
  1532. else
  1533. Write(F, ', ');
  1534. end;
  1535. while not SqlQuery.EOF do
  1536. begin
  1537. for i:= 0 to SqlQuery.FieldCount - 1 do
  1538. begin
  1539. Write(F, '"', SqlQuery.Fields[i].AsString, '"');
  1540. if i = SqlQuery.FieldCount - 1 then
  1541. Writeln(F)
  1542. else
  1543. Write(F, ', ');
  1544. end;
  1545. SqlQuery.Next;
  1546. end;
  1547. CloseFile(F);
  1548. SqlQuery.EnableControls;
  1549. end;
  1550. end;
  1551. { Copy query result in Clipboard }
  1552. procedure TfmQueryWindow.lmCopyAllClick(Sender: TObject);
  1553. var
  1554. Grid: TDBGrid;
  1555. i: Integer;
  1556. List: TStringList;
  1557. Line: string;
  1558. begin
  1559. Grid:= TDBGrid(pmGrid.PopupComponent);
  1560. try
  1561. Grid.DataSource.DataSet.DisableControls;
  1562. Grid.DataSource.DataSet.First;
  1563. List:= TStringList.Create;
  1564. try
  1565. Line:= '';
  1566. // Copy fields header
  1567. with Grid.DataSource.DataSet do
  1568. for i:= 0 to FieldCount - 1 do
  1569. begin
  1570. Line:= Line + '"' + Fields[i].FieldName + '"';
  1571. if i + 1 < FieldCount then
  1572. Line:= Line + ',';
  1573. end;
  1574. List.Add(Line);
  1575. // Copy table data
  1576. with Grid.DataSource.DataSet do
  1577. while not Eof do
  1578. begin
  1579. Line:= '';
  1580. for i:= 0 to FieldCount - 1 do
  1581. begin
  1582. Line:= Line + '"' + Trim(Fields[i].AsString) + '"';
  1583. if i + 1 < FieldCount then
  1584. Line:= Line + ',';
  1585. end;
  1586. List.Add(Line);
  1587. Next;
  1588. end;
  1589. Clipboard.AsText:= List.Text;
  1590. finally
  1591. List.Free;
  1592. end;
  1593. except
  1594. on E: Exception do
  1595. ShowMessage('Error trying to copy: '+e.Message);
  1596. end;
  1597. grid.DataSource.DataSet.EnableControls;
  1598. end;
  1599. { Copy cell in clipboard }
  1600. procedure TfmQueryWindow.lmCopyCellClick(Sender: TObject);
  1601. begin
  1602. Clipboard.AsText:= TdbGrid(pmGrid.PopupComponent).SelectedField.AsString;
  1603. end;
  1604. { Copy query text into clipboard }
  1605. procedure TfmQueryWindow.lmCopyClick(Sender: TObject);
  1606. begin
  1607. meQuery.CopyToClipboard;
  1608. end;
  1609. { Cut query text into clipboard}
  1610. procedure TfmQueryWindow.lmCutClick(Sender: TObject);
  1611. begin
  1612. meQuery.CutToClipboard;
  1613. end;
  1614. { Export to comma delimited file }
  1615. procedure TfmQueryWindow.lmExportAsCommaClick(Sender: TObject);
  1616. begin
  1617. lmCommaDelimitedClick(nil);
  1618. end;
  1619. { Export as HTML }
  1620. procedure TfmQueryWindow.lmExportAsHTMLClick(Sender: TObject);
  1621. begin
  1622. lmHTMLClick(nil);
  1623. end;
  1624. { Save query result as HTML }
  1625. procedure TfmQueryWindow.lmHTMLClick(Sender: TObject);
  1626. var
  1627. i: Integer;
  1628. F: TextFile;
  1629. SqlQuery: TSQLQuery;
  1630. begin
  1631. SaveDialog1.DefaultExt:= '.htm';
  1632. SqlQuery:= nil;
  1633. SqlQuery:= GetCurrentSelectQuery;
  1634. // Check for results being present:
  1635. if not(assigned(SqlQuery)) then
  1636. begin
  1637. MessageDlg('There is no record set in result', mtError, [mbOk], 0);
  1638. exit;
  1639. end;
  1640. if (not SQLQuery.Active) or (SQLQuery.RecordCount = 0) then
  1641. begin
  1642. MessageDlg('No data', mtError, [mbOk], 0);
  1643. exit;
  1644. end;
  1645. // We know there's valid data, let's try to save:
  1646. if SaveDialog1.Execute then
  1647. begin
  1648. SQLQuery.DisableControls;
  1649. SQLQuery.First;
  1650. AssignFile(F, SaveDialog1.FileName);
  1651. Rewrite(F);
  1652. Writeln(F, '<table border=0><tr bgcolor="DDDDDD">');
  1653. for i:= 0 to SQLQuery.FieldCount - 1 do
  1654. begin
  1655. Write(F, '<th>', SQLQuery.Fields[i].FieldName, '</th>');
  1656. if i = SQLQuery.FieldCount - 1 then
  1657. Writeln(F, '</tr>');
  1658. end;
  1659. while not SQLQuery.EOF do
  1660. begin
  1661. Write(f, '<tr bgcolor="');
  1662. // Zebra stripes in output:
  1663. if SQLQuery.RecNo mod 2 = 0 then
  1664. Write(F, '#EEDDFF">')
  1665. else
  1666. Write(F, '#FFFFFF">');
  1667. for i:= 0 to SQLQuery.FieldCount - 1 do
  1668. begin
  1669. Write(F, '<td>', SQLQuery.Fields[i].AsString, '</td>');
  1670. if i = SQLQuery.FieldCount - 1 then
  1671. Writeln(F, '</tr>');
  1672. end;
  1673. SQLQuery.Next;
  1674. end;
  1675. Writeln(F, '</table>');
  1676. CloseFile(F);
  1677. SQLQuery.EnableControls;
  1678. end;
  1679. end;
  1680. { Paste from clipboard into SQL editor }
  1681. procedure TfmQueryWindow.lmPasteClick(Sender: TObject);
  1682. begin
  1683. meQuery.PasteFromClipboard;
  1684. end;
  1685. { SQL Editor Redo }
  1686. procedure TfmQueryWindow.lmRedoClick(Sender: TObject);
  1687. begin
  1688. meQuery.Redo;
  1689. end;
  1690. { Run Query, auto type detection }
  1691. procedure TfmQueryWindow.lmRunClick(Sender: TObject);
  1692. begin
  1693. CallExecuteQuery(qtUnknown);
  1694. end;
  1695. { Run query and force its type as executable statement}
  1696. procedure TfmQueryWindow.lmRunExecClick(Sender: TObject);
  1697. begin
  1698. CallExecuteQuery(qtExecute);
  1699. end;
  1700. { Run query, and force its type as script }
  1701. procedure TfmQueryWindow.lmRunScriptClick(Sender: TObject);
  1702. begin
  1703. CallExecuteQuery(qtScript);
  1704. end;
  1705. { Run query, force its type as select statement }
  1706. procedure TfmQueryWindow.lmRunSelectClick(Sender: TObject);
  1707. begin
  1708. CallExecuteQuery(qtSelectable);
  1709. end;
  1710. { select all in SQL Editor }
  1711. procedure TfmQueryWindow.lmSelectAllClick(Sender: TObject);
  1712. begin
  1713. meQuery.SelectAll;
  1714. end;
  1715. { SQL Editor undo }
  1716. procedure TfmQueryWindow.lmUndoClick(Sender: TObject);
  1717. begin
  1718. meQuery.Undo;
  1719. end;
  1720. { Search in SQL Editor }
  1721. procedure TfmQueryWindow.lmFindClick(Sender: TObject);
  1722. begin
  1723. FindDialog1.Execute;
  1724. end;
  1725. { Find again }
  1726. procedure TfmQueryWindow.lmFindAgainClick(Sender: TObject);
  1727. begin
  1728. meQuery.SearchReplace(FindDialog1.FindText, '', FOptions);
  1729. end;
  1730. procedure TfmQueryWindow.MenuItem4Click(Sender: TObject);
  1731. begin
  1732. if FontDialog1.Execute then
  1733. meQuery.Font := FontDialog1.Font;
  1734. end;
  1735. { Run query by pressing Ctrl + Enter }
  1736. procedure TfmQueryWindow.meQueryKeyDown(Sender: TObject; var Key: Word;
  1737. Shift: TShiftState);
  1738. begin
  1739. // Execute query by pressing Ctrl + Enter
  1740. if (ssCtrl in shift) and (key = VK_RETURN) then
  1741. begin
  1742. CallExecuteQuery(qtUnknown);
  1743. key:= 0;
  1744. end;
  1745. end;
  1746. { Scrolling in query result recordset }
  1747. procedure TfmQueryWindow.QueryAfterScroll(DataSet: TDataSet);
  1748. var
  1749. Ctl: TControl;
  1750. TabSheet: TTabSheet;
  1751. i: Integer;
  1752. begin
  1753. TabSheet:= nil;
  1754. // Get DataSet's TTabsheet
  1755. // The query object's tag should be the tab index number
  1756. if (Dataset is TSQLQuery) then
  1757. TabSheet:= pgOutputPageCtl.Pages[TSQLQuery(DataSet).Tag];
  1758. if assigned(TabSheet) then
  1759. begin
  1760. for i:= 0 to TabSheet.ControlCount-1 do
  1761. begin
  1762. Ctl:= TabSheet.Controls[i];
  1763. if (Ctl is TStatusBar) then
  1764. begin
  1765. // Display current record and number of total records in status bar
  1766. TStatusBar(Ctl).SimpleText:= IntToStr(DataSet.RecordCount) +
  1767. ' records fetched. At record # ' + IntToStr(DataSet.RecNo);
  1768. break;
  1769. end;
  1770. end;
  1771. end;
  1772. end;
  1773. { Execute query according to passed query type }
  1774. procedure TfmQueryWindow.CallExecuteQuery(aQueryType: TQueryTypes);
  1775. begin
  1776. // Get query text from memo
  1777. if not(GetQuery(FQuery)) then
  1778. begin
  1779. ShowMessage('Could not get valid query');
  1780. exit;
  1781. end;
  1782. FStartLine:= 0;
  1783. RemovePreviousResultTabs;
  1784. // Disable buttons to prevent query interrupt
  1785. tbRun.Enabled:= False;
  1786. tbCommit.Enabled:= False;
  1787. tbCommitRetaining.Enabled:= False;
  1788. tbRollback.Enabled:= False;
  1789. tbRollbackRetaining.Enabled:= False;
  1790. FModifyCount:= 0;
  1791. // Get initial query type; this can be changed later in the next parts
  1792. if aQueryType = qtUnknown then // Auto
  1793. FOrigQueryType:= GetQueryType(FQuery.Text)
  1794. else
  1795. FOrigQueryType:= aQueryType;
  1796. // Call execute query for each part until finished
  1797. FCounter:= 0;
  1798. FFinished:= False;
  1799. repeat
  1800. ExecuteQuery;
  1801. until FFinished;
  1802. EnableButtons;
  1803. end;
  1804. { sort auto completion options }
  1805. procedure TfmQueryWindow.SortSynCompletion;
  1806. var
  1807. SortingList: TStringList;
  1808. i: Integer;
  1809. begin
  1810. SortingList:=TStringList.Create;
  1811. try
  1812. for i:=0 to SynCompletion1.ItemList.Count-1 do
  1813. SortingList.Add(SynCompletion1.ItemList.Strings[i]);
  1814. SortingList.Sort;
  1815. SynCompletion1.ItemList.Clear;
  1816. for i:=0 to SortingList.Count-1 do
  1817. SynCompletion1.ItemList.Add(SortingList.Strings[i]);
  1818. finally
  1819. SortingList.Free;
  1820. end;
  1821. end;
  1822. { SQL thread termination }
  1823. procedure TfmQueryWindow.ThreadTerminated(Sender: TObject);
  1824. var
  1825. aSQLQuery: TSQLQuery;
  1826. begin
  1827. // Raise exception if an error occured during thread execution (Open)
  1828. if FQT.Error then
  1829. begin
  1830. if Assigned(FTab) then
  1831. FTab.TabVisible:= False;
  1832. aSQLQuery:= (Sender as TQueryThread).Query;
  1833. FTab:= CreateResultTab(qtExecute, aSQLQuery, FSQLScript, FResultMemo);
  1834. pgOutputPageCtl.ActivePage:= FTab;
  1835. FResultMemo.Text:= FQT.ErrorMsg;
  1836. FResultMemo.Lines.Add(FQueryPart);
  1837. FResultMemo.Font.Color:= clRed;
  1838. FTab.Font.Color:= clRed;
  1839. FTab.ImageIndex:= 3;
  1840. end
  1841. else
  1842. begin
  1843. FTab.Caption:= FAText;
  1844. FTab.ImageIndex:= 0;
  1845. fmMain.AddToSQLHistory(FRegRec.Title, 'SELECT', FQueryPart);
  1846. end;
  1847. FQT.Free;
  1848. if FFinished then
  1849. EnableButtons;
  1850. if not FFinished then
  1851. ExecuteQuery;
  1852. end;
  1853. { Enable SQL buttons: Run, Commit, Rollbak after thread termination }
  1854. procedure TfmQueryWindow.EnableButtons;
  1855. begin
  1856. tbRun.Enabled:= True;
  1857. tbCommit.Enabled:= True;
  1858. tbCommitRetaining.Enabled:= True;
  1859. tbRollback.Enabled:= True;
  1860. tbRollbackRetaining.Enabled:= True;
  1861. end;
  1862. initialization
  1863. {$I querywindow.lrs}
  1864. end.