sqlscript.pp 20 KB

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