sqlscript.pp 20 KB

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