sqlscript.pp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2008 by the Free Pascal development team
  4. Abstract SQL scripting engine.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit SQLScript;
  12. {$mode objfpc}{$H+}
  13. interface
  14. uses
  15. Classes, SysUtils;
  16. Const
  17. MinSQLSeps = 5; // Default, minimum number of standard SQL separators.
  18. type
  19. TSQLScriptStatementEvent = procedure(Sender: TObject; Statement: TStrings; var StopExecution: Boolean) of object;
  20. TSQLScriptDirectiveEvent = procedure(Sender: TObject; Directive, Argument: AnsiString; var StopExecution: Boolean) of object;
  21. TSQLScriptExceptionEvent = procedure(Sender: TObject; Statement: TStrings; TheException: Exception; var Continue: boolean) of object;
  22. TSQLSkipMode = (smNone, smIfBranch, smElseBranch, smAll);
  23. { TCustomSQLScript }
  24. TCustomSQLScript = class(TComponent)
  25. private
  26. FAutoCommit: Boolean;
  27. FDollarStrings: Tstrings;
  28. FLine: Integer;
  29. FCol: Integer;
  30. FDefines: TStrings;
  31. FOnException: TSQLScriptExceptionEvent;
  32. FSkipMode: TSQLSkipMode;
  33. FIsSkipping: Boolean;
  34. FSkipStackIndex: Integer;
  35. FSkipModeStack: array[0..255] of TSQLSkipMode;
  36. FIsSkippingStack: array[0..255] of Boolean;
  37. FAborted: Boolean;
  38. FUseDollarString: Boolean;
  39. FUseSetTerm, FUseDefines, FUseCommit,
  40. FCommentsInSQL: Boolean;
  41. FTerminator: AnsiString;
  42. FSQL: TStrings;
  43. FCurrentStripped,
  44. FCurrentStatement: TStrings;
  45. FDirectives: TStrings;
  46. FComment,
  47. FEmitLine: Boolean;
  48. FSeps : Array of string;
  49. procedure SetDefines(const Value: TStrings);
  50. function FindNextSeparator(ASeps: Array of string; Out IsExtended : Boolean): AnsiString;
  51. procedure SetDirectives(value: TStrings);
  52. procedure SetDollarStrings(AValue: TStrings);
  53. procedure SetSQL(value: TStrings);
  54. procedure SetTerminator(AValue: AnsiString);
  55. procedure SetUseDollarString(AValue: Boolean);
  56. procedure SQLChange(Sender: TObject);
  57. procedure DollarStringsChange(Sender : TObject);
  58. Procedure RecalcSeps;
  59. function GetLine: Integer;
  60. protected
  61. procedure InternalStatement (Statement: TStrings; var StopExecution: Boolean); virtual;
  62. procedure InternalDirective (Directive, Argument: String; var StopExecution: Boolean); virtual;
  63. // Runs commit. If ComitRetaining, use CommitRetraining if possible, else stop/starttransaction
  64. procedure InternalCommit(CommitRetaining: boolean=true); virtual;
  65. Function ProcessConditional(const Directive : String; const Param : String) : Boolean; virtual;
  66. procedure ProcessStatement; virtual;
  67. procedure DefaultDirectives; virtual;
  68. procedure ExecuteStatement (Statement: TStrings; var StopExecution: Boolean); virtual; abstract;
  69. procedure ExecuteDirective (Directive, Argument: String; var StopExecution: Boolean); virtual; abstract;
  70. // Executes commit. If possible and CommitRetaining, use CommitRetaining, else
  71. procedure ExecuteCommit(CommitRetaining: boolean=true); virtual; abstract;
  72. // Useful when you want to add your own parsing routines.
  73. // Get next statement. This must also use AddToCurrentStatement to add the statement.
  74. function NextStatement: AnsiString; virtual;
  75. // Add text to current statement. If InComment is false, strippedstatement will also be updated.
  76. procedure AddToCurrentStatement(value: AnsiString; ForceNewLine : boolean); virtual;
  77. // Clear current statement
  78. procedure ClearStatement; virtual;
  79. // Is a next statement available ?
  80. function Available: Boolean; virtual;
  81. // Current state
  82. Property CurrentStatement : TStrings Read FCurrentStatement;
  83. Property CurrentStrippedStatement : TStrings Read FCurrentStripped;
  84. Property InComment : Boolean Read FComment Write FComment;
  85. Property EmitLine : Boolean Read FEmitline Write FEmitline;
  86. public
  87. constructor Create (AnOwner: TComponent); override;
  88. destructor Destroy; override;
  89. procedure Execute; virtual;
  90. protected
  91. property Aborted: Boolean read FAborted;
  92. property Line: Integer read GetLine;
  93. Property AutoCommit : Boolean Read FAutoCommit Write FAutoCommit default false;
  94. property CommentsInSQL: Boolean read FCommentsInSQL write FCommentsInSQL default true;
  95. property UseSetTerm: Boolean read FUseSetTerm write FUseSetTerm default true;
  96. property UseCommit: Boolean read FUseCommit write FUseCommit Default true;
  97. property UseDefines: Boolean read FUseDefines write FUseDefines default true;
  98. Property UseDollarString : Boolean Read FUseDollarString Write SetUseDollarString default false;
  99. Property DollarStrings : TStrings Read FDollarStrings Write SetDollarStrings;
  100. property Defines : TStrings Read FDefines Write SetDefines;
  101. property Directives: TStrings read FDirectives write SetDirectives;
  102. property Script: TStrings read FSQL write SetSQL; // script to execute
  103. property Terminator: AnsiString read FTerminator write SetTerminator;
  104. property OnException : TSQLScriptExceptionEvent read FOnException write FOnException;
  105. end;
  106. { TEventSQLScript }
  107. TEventSQLScript = class (TCustomSQLScript)
  108. private
  109. FAfterExec: TNotifyEvent;
  110. FBeforeExec: TNotifyEvent;
  111. FOnCommit: TNotifyEvent;
  112. FOnSQLStatement: TSQLScriptStatementEvent;
  113. FOnDirective: TSQLScriptDirectiveEvent;
  114. protected
  115. procedure ExecuteStatement (SQLStatement: TStrings; var StopExecution: Boolean); override;
  116. procedure ExecuteDirective (Directive, Argument: String; var StopExecution: Boolean); override;
  117. procedure ExecuteCommit(CommitRetaining: boolean=true); override;
  118. public
  119. procedure Execute; override;
  120. property Aborted;
  121. property Line;
  122. published
  123. Property UseDollarString;
  124. Property DollarStrings;
  125. property Directives;
  126. property Defines;
  127. property Script;
  128. property Terminator;
  129. property CommentsinSQL;
  130. property UseSetTerm;
  131. property UseCommit;
  132. property UseDefines;
  133. property OnException;
  134. property OnSQLStatement: TSQLScriptStatementEvent read FOnSQLStatement write FOnSQLStatement;
  135. property OnDirective: TSQLScriptDirectiveEvent read FOnDirective write FOnDirective;
  136. property OnCommit: TNotifyEvent read FOnCommit write FOnCommit;
  137. property BeforeExecute : TNotifyEvent read FBeforeExec write FBeforeExec;
  138. property AfterExecute : TNotifyEvent read FAfterExec write FAfterExec;
  139. end;
  140. ESQLScript = Class(Exception);
  141. implementation
  142. Resourcestring
  143. SErrIfXXXNestingLimitReached = '#IFDEF nesting limit reached';
  144. SErrInvalidEndif = '#ENDIF without #IFDEF';
  145. SErrInvalidElse = '#ELSE without #IFDEF';
  146. { ---------------------------------------------------------------------
  147. Auxiliary Functions
  148. ---------------------------------------------------------------------}
  149. function StartsWith(S1, S2: AnsiString): Boolean;
  150. var
  151. L1,L2 : Integer;
  152. begin
  153. Result:=False;
  154. L1:=Length(S1);
  155. L2:=Length(S2);
  156. if (L2=0) or (L1<L2) then
  157. Exit;
  158. Result:=(AnsiCompareStr(Copy(s1,1,L2),S2)=0);
  159. Result := Result and ((L2 = L1) or (s1[L2+1] = ' '));
  160. end;
  161. function GetFirstSeparator(S: AnsiString; Sep: array of string): integer;
  162. var
  163. i, C, M: Integer;
  164. begin
  165. M:=length(S) + 1;
  166. Result:=-1;
  167. for i:=0 to high(Sep) do
  168. begin
  169. C:=Pos(Sep[i],S);
  170. if (C<>0) and (C<M) then
  171. begin
  172. M:=C;
  173. Result:=i;
  174. end;
  175. end;
  176. end;
  177. Function ConvertWhiteSpace(const S : String) : String;
  178. begin
  179. Result:=StringReplace(S,#13,' ',[rfReplaceAll]);
  180. Result:=StringReplace(Result,#10,' ',[rfReplaceAll]);
  181. Result:=Trim(Result);
  182. end;
  183. { ---------------------------------------------------------------------
  184. TSQLScript
  185. ---------------------------------------------------------------------}
  186. procedure TCustomSQLScript.SQLChange(Sender: TObject);
  187. begin
  188. FLine:=1;
  189. FCol:=1;
  190. end;
  191. procedure TCustomSQLScript.DollarStringsChange(Sender: TObject);
  192. begin
  193. RecalcSeps;
  194. end;
  195. procedure TCustomSQLScript.RecalcSeps;
  196. Var
  197. L : Integer;
  198. begin
  199. L:=MinSQLSeps;
  200. If UseDollarString then
  201. L:=L+1+DollarStrings.Count;
  202. SetLength(FSeps,L);
  203. FSeps[0]:=FTerminator;
  204. FSeps[1]:='/*';
  205. FSeps[2]:='"';
  206. FSeps[3]:='''';
  207. FSeps[4]:='--';
  208. If UseDollarString then
  209. begin
  210. FSeps[MinSQLSeps]:='$$';
  211. For L:=0 to FDollarStrings.Count-1 do
  212. FSeps[MinSQLSeps+1+L]:='$'+FDollarStrings[L]+'$';
  213. end;
  214. end;
  215. procedure TCustomSQLScript.SetDirectives(value: TStrings);
  216. var
  217. i : Integer;
  218. S : AnsiString;
  219. begin
  220. FDirectives.Clear();
  221. if (Value<>Nil) then
  222. begin
  223. for i:=0 to value.Count - 1 do
  224. begin
  225. S:=UpperCase(ConvertWhiteSpace(value[i]));
  226. if Length(S)>0 then
  227. FDirectives.Add(S);
  228. end;
  229. end;
  230. DefaultDirectives;
  231. end;
  232. procedure TCustomSQLScript.SetDollarStrings(AValue: TStrings);
  233. begin
  234. if FDollarStrings=AValue then Exit;
  235. FDollarStrings.Assign(AValue);
  236. If FUseDollarString then
  237. RecalcSeps;
  238. end;
  239. procedure TCustomSQLScript.SetSQL(value: TStrings);
  240. begin
  241. FSQL.Assign(value);
  242. FLine:=1;
  243. FCol:=1;
  244. end;
  245. procedure TCustomSQLScript.SetTerminator(AValue: AnsiString);
  246. begin
  247. if FTerminator=AValue then Exit;
  248. FTerminator:=AValue;
  249. if Length(FSeps)>0 then
  250. FSeps[0]:=FTerminator;
  251. end;
  252. procedure TCustomSQLScript.SetUseDollarString(AValue: Boolean);
  253. begin
  254. if FUseDollarString=AValue then Exit;
  255. FUseDollarString:=AValue;
  256. RecalcSeps;
  257. end;
  258. function TCustomSQLScript.GetLine: Integer;
  259. begin
  260. Result:=FLine - 1;
  261. end;
  262. procedure TCustomSQLScript.AddToCurrentStatement(value: AnsiString; ForceNewLine: boolean);
  263. Procedure DA(L : TStrings);
  264. begin
  265. With L do
  266. if ForceNewLine or (Count=0) then
  267. Add(value)
  268. else
  269. Strings[Count-1]:=Strings[Count-1] + value;
  270. end;
  271. begin
  272. DA(FCurrentStatement);
  273. if Not FComment then
  274. DA(FCurrentStripped);
  275. end;
  276. function TCustomSQLScript.FindNextSeparator(ASeps: array of string; out
  277. IsExtended: Boolean): AnsiString;
  278. var
  279. S: AnsiString;
  280. I : Integer;
  281. begin
  282. Result:='';
  283. while (FLine<=FSQL.Count) do
  284. begin
  285. S:=FSQL.Strings[FLine-1];
  286. if (FCol>1) then
  287. begin
  288. S:=Copy(S,FCol,length(S));
  289. end;
  290. I:=GetFirstSeparator(S,ASeps);
  291. if (I=-1) then
  292. begin
  293. if FEmitLine then
  294. AddToCurrentStatement(S,(FCol<=1));
  295. FCol:=1;
  296. FLine:=FLine+1;
  297. end
  298. else
  299. begin
  300. Result:=ASeps[i];
  301. IsExtended:=I>=MinSQLSeps;
  302. if FEmitLine then
  303. AddToCurrentStatement(Copy(S,1,Pos(Result,S)-1),(FCol=1));
  304. FCol:=(FCol-1)+Pos(Result,S);
  305. break;
  306. end;
  307. end;
  308. end;
  309. function TCustomSQLScript.Available: Boolean;
  310. begin
  311. With FSQL do
  312. Result:=(FLine<Count) or
  313. (
  314. ( FLine = Count ) and
  315. ( FCol < Length(Strings[Count-1] ) )
  316. );
  317. end;
  318. procedure TCustomSQLScript.InternalStatement(Statement: TStrings; var StopExecution: Boolean);
  319. var
  320. cont : boolean;
  321. begin
  322. try
  323. ExecuteStatement(Statement, StopExecution);
  324. except
  325. on E : Exception do
  326. begin
  327. cont := false;
  328. if assigned (FOnException) then
  329. FOnException (self, Statement, E, cont);
  330. if not cont then
  331. Raise;
  332. end;
  333. end;
  334. end;
  335. procedure TCustomSQLScript.InternalDirective(Directive, Argument: String; var StopExecution: Boolean);
  336. var
  337. cont : boolean;
  338. l : TStrings;
  339. begin
  340. try
  341. ExecuteDirective(Directive, Argument, StopExecution);
  342. except
  343. on E : Exception do
  344. begin
  345. cont := false;
  346. if assigned (FOnException) then
  347. begin
  348. l := TStringlist.Create;
  349. try
  350. L.Add(Directive);
  351. if Argument <> '' then
  352. L.Add(Argument);
  353. FOnException (self, l, E, cont);
  354. finally
  355. L.Free;
  356. end;
  357. end;
  358. if not cont then
  359. Raise;
  360. end;
  361. end;
  362. end;
  363. procedure TCustomSQLScript.InternalCommit(CommitRetaining: boolean=true);
  364. var
  365. cont : boolean;
  366. l : TStrings;
  367. begin
  368. try
  369. ExecuteCommit(CommitRetaining);
  370. except
  371. on E : Exception do
  372. begin
  373. cont := false;
  374. if assigned (FOnException) then
  375. begin
  376. l := TStringlist.Create;
  377. try
  378. L.Add('COMMIT');
  379. FOnException (self, l, E, cont);
  380. finally
  381. L.Free;
  382. end;
  383. end;
  384. if not cont then
  385. Raise;
  386. end;
  387. end;
  388. end;
  389. procedure TCustomSQLScript.ClearStatement;
  390. begin
  391. FCurrentStatement.Clear;
  392. FCurrentStripped.Clear;
  393. end;
  394. procedure TCustomSQLScript.ProcessStatement;
  395. Var
  396. S,
  397. Directive : String;
  398. I : longint;
  399. begin
  400. if (FCurrentStatement.Count=0) then
  401. Exit;
  402. S:=Trim(FCurrentStripped.Text);
  403. I:=0;
  404. Directive:='';
  405. While (i<FDirectives.Count) and (Directive='') do
  406. begin
  407. If StartsWith(AnsiUpperCase(S), FDirectives[i]) Then
  408. Directive:=FDirectives[i];
  409. Inc(I);
  410. end;
  411. If (Directive<>'') then
  412. begin
  413. S:=Trim(Copy(S,Length(Directive)+1,length(S)));
  414. If (Directive[1]='#') then
  415. begin
  416. if not FUseDefines or not ProcessConditional(Directive,S) then
  417. if Not FIsSkipping then
  418. InternalDirective (Directive, S, FAborted);
  419. end
  420. else If Not FIsSkipping then
  421. begin
  422. // If AutoCommit, skip any explicit commits.
  423. if FUseCommit
  424. and ((Directive = 'COMMIT') or (Directive = 'COMMIT WORK' {SQL standard}))
  425. and not FAutoCommit then
  426. InternalCommit(false) //explicit commit, no commit retaining
  427. else if FUseCommit
  428. and (Directive = 'COMMIT RETAIN') {at least Firebird syntax}
  429. and not FAutoCommit then
  430. InternalCommit(true)
  431. else if FUseSetTerm
  432. and (Directive = 'SET TERM' {Firebird/Interbase only}) then
  433. begin
  434. FTerminator:=S;
  435. RecalcSeps;
  436. end
  437. else
  438. InternalDirective (Directive,S,FAborted)
  439. end
  440. end
  441. else
  442. if (not FIsSkipping) then
  443. begin
  444. InternalStatement(FCurrentStatement,FAborted);
  445. If FAutoCommit and not FAborted then
  446. InternalCommit;
  447. end;
  448. end;
  449. procedure TCustomSQLScript.Execute;
  450. begin
  451. FSkipMode:=smNone;
  452. FIsSkipping:=False;
  453. FSkipStackIndex:=0;
  454. Faborted:=False;
  455. FLine:=1;
  456. FCol:=1;
  457. DefaultDirectives;
  458. Repeat
  459. NextStatement();
  460. if Length(Trim(FCurrentStripped.Text))>0 then
  461. ProcessStatement;
  462. Until FAborted or Not Available;
  463. end;
  464. function TCustomSQLScript.NextStatement: AnsiString;
  465. var
  466. pnt: AnsiString;
  467. b,isExtra: Boolean;
  468. begin
  469. ClearStatement;
  470. while FLine <= FSQL.Count do
  471. begin
  472. pnt:=FindNextSeparator(FSeps,isExtra);
  473. if (pnt=FTerminator) then
  474. begin
  475. FCol:=FCol + length(pnt);
  476. break;
  477. end
  478. else if pnt = '/*' then
  479. begin
  480. FComment:=True;
  481. if FCommentsInSQL then
  482. AddToCurrentStatement(pnt,false)
  483. else
  484. FEmitLine:=False;
  485. FCol:=FCol + length(pnt);
  486. pnt:=FindNextSeparator(['*/'],b);
  487. if FCommentsInSQL then
  488. AddToCurrentStatement(pnt,false)
  489. else
  490. FEmitLine:=True;
  491. FCol:=FCol + length(pnt);
  492. FComment:=False;
  493. end
  494. else if pnt = '--' then
  495. begin
  496. FComment:=True;
  497. if FCommentsInSQL then
  498. AddToCurrentStatement(Copy(FSQL[FLine-1],FCol,Length(FSQL[FLine-1])-FCol+1),False);
  499. Inc(Fline);
  500. FCol:=1;
  501. FComment:=False;
  502. end
  503. else if pnt = '"' then
  504. begin
  505. AddToCurrentStatement(pnt,false);
  506. FCol:=FCol + length(pnt);
  507. pnt:=FindNextSeparator(['"'],b);
  508. AddToCurrentStatement(pnt,false);
  509. FCol:=FCol + length(pnt);
  510. end
  511. else if pnt = '''' then
  512. begin
  513. AddToCurrentStatement(pnt,False);
  514. FCol:=FCol + length(pnt);
  515. pnt:=FindNextSeparator([''''],b);
  516. AddToCurrentStatement(pnt,false);
  517. FCol:=FCol + length(pnt);
  518. end
  519. else if IsExtra then
  520. begin
  521. AddToCurrentStatement(pnt,false);
  522. FCol:=FCol + length(pnt);
  523. pnt:=FindNextSeparator([pnt],b);
  524. AddToCurrentStatement(pnt,false);
  525. FCol:=FCol + length(pnt);
  526. end;
  527. end;
  528. while (FCurrentStatement.Count > 0) and (trim(FCurrentStatement.Strings[0]) = '') do
  529. FCurrentStatement.Delete(0);
  530. while (FCurrentStripped.Count > 0) and (trim(FCurrentStripped.Strings[0]) = '') do
  531. FCurrentStripped.Delete(0);
  532. Result:=FCurrentStatement.Text;
  533. end;
  534. constructor TCustomSQLScript.Create(AnOwner: TComponent);
  535. Var
  536. L : TStringList;
  537. begin
  538. inherited;
  539. L:=TStringList.Create;
  540. With L do
  541. begin
  542. Sorted:=True;
  543. Duplicates:=dupIgnore;
  544. end;
  545. FDefines:=L;
  546. FCommentsInSQL:=True;
  547. FTerminator:=';';
  548. L:=TStringList.Create();
  549. L.OnChange:=@SQLChange;
  550. FSQL:=L;
  551. L:=TStringList.Create();
  552. L.OnChange:=@DollarStringsChange;
  553. FDollarStrings:=L;
  554. ReCalcSeps;
  555. FDirectives:=TStringList.Create();
  556. FCurrentStripped:=TStringList.Create();
  557. FCurrentStatement:=TStringList.Create();
  558. FLine:=1;
  559. FCol:=1;
  560. FEmitLine:=True;
  561. FUseCommit := true;
  562. FUseDefines := True;
  563. FUseSetTerm := True;
  564. DefaultDirectives;
  565. end;
  566. destructor TCustomSQLScript.Destroy;
  567. begin
  568. FreeAndNil(FCurrentStripped);
  569. FreeAndNil(FCurrentStatement);
  570. FreeAndNil(FSQL);
  571. FreeAndNil(FDirectives);
  572. FreeAndNil(FDefines);
  573. FreeAndNil(FDollarStrings);
  574. inherited Destroy;
  575. end;
  576. procedure TCustomSQLScript.SetDefines(const Value: TStrings);
  577. begin
  578. FDefines.Assign(Value);
  579. end;
  580. procedure TCustomSQLScript.DefaultDirectives;
  581. Procedure Add(const S : String);
  582. begin
  583. if FDirectives.IndexOf(S)=-1 then
  584. FDirectives.Add(S);
  585. end;
  586. begin
  587. // Insertion order matters as testing for directives will be done with StartsWith
  588. if FUseSetTerm then
  589. Add('SET TERM');
  590. if FUseCommit then
  591. begin
  592. Add('COMMIT WORK'); {SQL Standard, equivalent to commit}
  593. Add('COMMIT RETAIN'); {Firebird/Interbase; probably won't hurt on other dbs}
  594. Add('COMMIT'); {Shorthand used in many dbs, e.g. Firebird}
  595. end;
  596. if FUseDefines then
  597. begin
  598. Add('#IFDEF');
  599. Add('#IFNDEF');
  600. Add('#ELSE');
  601. Add('#ENDIF');
  602. Add('#DEFINE');
  603. Add('#UNDEF');
  604. Add('#UNDEFINE');
  605. end;
  606. end;
  607. function TCustomSQLScript.ProcessConditional(const Directive: String; const Param: String
  608. ): Boolean;
  609. Procedure PushSkipMode;
  610. begin
  611. if FSkipStackIndex=High(FSkipModeStack) then
  612. Raise ESQLScript.Create(SErrIfXXXNestingLimitReached);
  613. FSkipModeStack[FSkipStackIndex]:=FSkipMode;
  614. FIsSkippingStack[FSkipStackIndex]:=FIsSkipping;
  615. Inc(FSkipStackIndex);
  616. end;
  617. Procedure PopSkipMode;
  618. begin
  619. if FSkipStackIndex = 0 then
  620. Raise ESQLScript.Create(SErrInvalidEndif);
  621. Dec(FSkipStackIndex);
  622. FSkipMode := FSkipModeStack[FSkipStackIndex];
  623. FIsSkipping := FIsSkippingStack[FSkipStackIndex];
  624. end;
  625. Var
  626. Index : Integer;
  627. begin
  628. Result:=True;
  629. if (Directive='#DEFINE') then
  630. begin
  631. if not FIsSkipping then
  632. FDefines.Add(Param);
  633. end
  634. else if (Directive='#UNDEF') or (Directive='#UNDEFINE') then
  635. begin
  636. if not FIsSkipping then
  637. begin
  638. Index:=FDefines.IndexOf(Param);
  639. if (Index>=0) then
  640. FDefines.Delete(Index);
  641. end;
  642. end
  643. else if (Directive='#IFDEF') or (Directive='#IFNDEF') then
  644. begin
  645. PushSkipMode;
  646. if FIsSkipping then
  647. begin
  648. FSkipMode:=smAll;
  649. FIsSkipping:=true;
  650. end
  651. else
  652. begin
  653. Index:=FDefines.IndexOf(Param);
  654. if ((Directive='#IFDEF') and (Index<0)) or
  655. ((Directive='#IFNDEF') and (Index>=0)) then
  656. begin
  657. FSkipMode:=smIfBranch;
  658. FIsSkipping:=true;
  659. end
  660. else
  661. FSkipMode := smElseBranch;
  662. end;
  663. end
  664. else if (Directive='#ELSE') then
  665. begin
  666. if (FSkipStackIndex=0) then
  667. Raise ESQLScript.Create(SErrInvalidElse);
  668. if (FSkipMode=smIfBranch) then
  669. FIsSkipping:=false
  670. else if (FSkipMode=smElseBranch) then
  671. FIsSkipping:=true;
  672. end
  673. else if (Directive='#ENDIF') then
  674. PopSkipMode
  675. else
  676. Result:=False;
  677. end;
  678. { TEventSQLScript }
  679. procedure TEventSQLScript.ExecuteStatement(SQLStatement: TStrings;
  680. var StopExecution: Boolean);
  681. begin
  682. if assigned (FOnSQLStatement) then
  683. FOnSQLStatement (self, SQLStatement, StopExecution);
  684. end;
  685. procedure TEventSQLScript.ExecuteDirective(Directive, Argument: String;
  686. var StopExecution: Boolean);
  687. begin
  688. if assigned (FOnDirective) then
  689. FOnDirective (Self, Directive, Argument, StopExecution);
  690. end;
  691. procedure TEventSQLScript.ExecuteCommit(CommitRetaining: boolean=true);
  692. begin
  693. if assigned (FOnCommit) then
  694. FOnCommit (Self);
  695. end;
  696. procedure TEventSQLScript.Execute;
  697. begin
  698. if assigned (FBeforeExec) then
  699. FBeforeExec (Self);
  700. inherited Execute;
  701. if assigned (FAfterExec) then
  702. FAfterExec (Self);
  703. end;
  704. end.