/Gedemin/IBX/IBScript.pas

http://gedemin.googlecode.com/ · Pascal · 1064 lines · 930 code · 81 blank · 53 comment · 106 complexity · 2a634c357d0bfcf4c6f799d397dfa106 MD5 · raw file

  1. {*************************************************************}
  2. { }
  3. { Borland Delphi Visual Component Library }
  4. { InterBase Express core components }
  5. { }
  6. { Copyright (c) 2001 Jeff Overcash }
  7. { }
  8. { }
  9. {*************************************************************}
  10. unit IBScript;
  11. interface
  12. uses
  13. SysUtils, Classes, IBDatabase, IBCustomDataset, IBSQL, IBDatabaseInfo;
  14. type
  15. TIBScript = class;
  16. TIBParseKind = (stmtDDL, stmtDML, stmtSET, stmtCONNECT, stmtDrop,
  17. stmtCREATE, stmtINPUT, stmtUNK, stmtEMPTY,
  18. stmtTERM, stmtERR, stmtCOMMIT, stmtROLLBACK);
  19. TIBSQLParseError = procedure(Sender: TObject; Error: string; SQLText: string;
  20. LineIndex: Integer) of object;
  21. TIBSQLExecuteError = procedure(Sender: TObject; Error: string; SQLText:
  22. string;
  23. LineIndex: Integer; var Ignore: Boolean) of object;
  24. TIBSQLParseStmt = procedure(Sender: TObject; AKind: TIBParseKind; SQLText:
  25. string) of object;
  26. TIBScriptParamCheck = procedure(Sender: TIBScript; var Pause: Boolean) of
  27. object;
  28. TIBSQLParser = class(TComponent)
  29. private
  30. FOnError: TIBSQLParseError;
  31. FOnParse: TIBSQLParseStmt;
  32. FScript, FInput: TStrings;
  33. FTerminator: string;
  34. FPaused: Boolean;
  35. FFinished: Boolean;
  36. procedure SetScript(const Value: TStrings);
  37. procedure SetPaused(const Value: Boolean);
  38. { Private declarations }
  39. private
  40. FTokens: TStrings;
  41. FWork: string;
  42. ScriptIndex, LineIndex, ImportIndex: Integer;
  43. InInput: Boolean;
  44. //Get Tokens plus return the actual SQL to execute
  45. function TokenizeNextLine: string;
  46. // Return the Parse Kind for the Current tokenized statement
  47. function IsValidStatement: TIBParseKind;
  48. procedure RemoveComment;
  49. function AppendNextLine: Boolean;
  50. procedure LoadInput;
  51. protected
  52. { Protected declarations }
  53. procedure DoOnParse(AKind: TIBParseKind; SQLText: string); virtual;
  54. procedure DoOnError(Error: string; SQLText: string); virtual;
  55. procedure DoParser;
  56. public
  57. { Public declarations }
  58. constructor Create(AOwner: TComponent); override;
  59. destructor Destroy; override;
  60. procedure Parse;
  61. property CurrentLine: Integer read LineIndex;
  62. property CurrentTokens: TStrings read FTokens;
  63. published
  64. { Published declarations }
  65. property Finished: Boolean read FFinished;
  66. property Paused: Boolean read FPaused write SetPaused;
  67. property Script: TStrings read FScript write SetScript;
  68. property Terminator: string read FTerminator write FTerminator;
  69. property OnParse: TIBSQLParseStmt read FOnParse write FOnParse;
  70. property OnError: TIBSQLParseError read FOnError write FOnError;
  71. end;
  72. TIBScriptStats = class
  73. private
  74. FBuffers: int64;
  75. FReadIdx: int64;
  76. FWrites: int64;
  77. FFetches: int64;
  78. FSeqReads: int64;
  79. FReads: int64;
  80. FDeltaMem: int64;
  81. FStartBuffers: int64;
  82. FStartReadIdx: int64;
  83. FStartWrites: int64;
  84. FStartFetches: int64;
  85. FStartSeqReads: int64;
  86. FStartReads: int64;
  87. FStartingMem : Int64;
  88. FDatabase: TIBDatabase;
  89. FInfoStats : TIBDatabaseInfo;
  90. procedure SetDatabase(const Value: TIBDatabase);
  91. function AddStringValues( list : TStrings) : int64;
  92. public
  93. constructor Create;
  94. destructor Destroy; override;
  95. procedure Start;
  96. procedure Clear;
  97. procedure Stop;
  98. property Database : TIBDatabase read FDatabase write SetDatabase;
  99. property Buffers : int64 read FBuffers;
  100. property Reads : int64 read FReads;
  101. property Writes : int64 read FWrites;
  102. property SeqReads : int64 read FSeqReads;
  103. property Fetches : int64 read FFetches;
  104. property ReadIdx : int64 read FReadIdx;
  105. property DeltaMem : int64 read FDeltaMem;
  106. property StartingMem : int64 read FStartingMem;
  107. end;
  108. TIBScript = class(TComponent)
  109. private
  110. FSQLParser: TIBSQLParser;
  111. FAutoDDL: Boolean;
  112. FStatsOn: boolean;
  113. FDataset: TIBDataset;
  114. FDatabase: TIBDatabase;
  115. FOnError: TIBSQLParseError;
  116. FOnParse: TIBSQLParseStmt;
  117. FDDLTransaction: TIBTransaction;
  118. FTransaction: TIBTransaction;
  119. FTerminator: string;
  120. FDDLQuery, FDMLQuery: TIBSQL;
  121. FContinue: Boolean;
  122. FOnExecuteError: TIBSQLExecuteError;
  123. FOnParamCheck: TIBScriptParamCheck;
  124. FValidate, FValidating: Boolean;
  125. FStats: TIBScriptStats;
  126. FSQLDialect : Integer;
  127. FCurrentStmt: TIBParseKind;
  128. FExecuting : Boolean;
  129. function GetPaused: Boolean;
  130. procedure SetPaused(const Value: Boolean);
  131. procedure SetTerminator(const Value: string);
  132. procedure SetupNewConnection;
  133. procedure SetDatabase(const Value: TIBDatabase);
  134. procedure SetTransaction(const Value: TIBTransaction);
  135. function StripQuote(const Text: string): string;
  136. function GetScript: TStrings;
  137. procedure SetScript(const Value: TStrings);
  138. function GetSQLParams: TIBXSQLDA;
  139. procedure SetStatsOn(const Value: boolean);
  140. function GetTokens: TStrings;
  141. protected
  142. procedure Notification(AComponent: TComponent;
  143. Operation: TOperation); override;
  144. procedure DoDML(const Text: string); virtual;
  145. procedure DoDDL(const Text: string); virtual;
  146. procedure DoSET(const Text: string); virtual;
  147. procedure DoConnect(const SQLText: string); virtual;
  148. procedure DoCreate(const SQLText: string); virtual;
  149. procedure DropDatabase(const SQLText: string); virtual;
  150. procedure ParserError(Sender: TObject; Error, SQLText: string;
  151. LineIndex: Integer);
  152. procedure ParserParse(Sender: TObject; AKind: TIBParseKind;
  153. SQLText: string);
  154. public
  155. constructor Create(AOwner: TComponent); override;
  156. destructor Destroy; override;
  157. function ValidateScript: Boolean;
  158. procedure ExecuteScript;
  159. function ParamByName(Idx : String) : TIBXSQLVAR;
  160. property Paused: Boolean read GetPaused write SetPaused;
  161. property Params: TIBXSQLDA read GetSQLParams;
  162. property Stats : TIBScriptStats read FStats;
  163. property CurrentTokens : TStrings read GetTokens;
  164. property Validating : Boolean read FValidating;
  165. published
  166. property AutoDDL: Boolean read FAutoDDL write FAutoDDL default true;
  167. property Dataset: TIBDataset read FDataset write FDataset;
  168. property Database: TIBDatabase read FDatabase write SetDatabase;
  169. property Transaction: TIBTransaction read FTransaction write SetTransaction;
  170. property Terminator: string read FTerminator write SetTerminator;
  171. property Script: TStrings read GetScript write SetScript;
  172. property Statistics: boolean read FStatsOn write SetStatsOn default true;
  173. property OnParse: TIBSQLParseStmt read FOnParse write FOnParse;
  174. property OnParseError: TIBSQLParseError read FOnError write FOnError;
  175. property OnExecuteError: TIBSQLExecuteError read FOnExecuteError write
  176. FOnExecuteError;
  177. property OnParamCheck: TIBScriptParamCheck read FOnParamCheck write
  178. FOnParamCheck;
  179. end;
  180. implementation
  181. uses IBUtils, IB;
  182. const
  183. QUOTE = '''';
  184. DBL_QUOTE = '"';
  185. { TIBSQLParser }
  186. function TIBSQLParser.AppendNextLine: Boolean;
  187. var
  188. FStrings: TStrings;
  189. FIndex: ^Integer;
  190. begin
  191. if (FInput.Count > ImportIndex) then
  192. begin
  193. InInput := true;
  194. FStrings := FInput;
  195. FIndex := @ImportIndex;
  196. end
  197. else
  198. begin
  199. InInput := false;
  200. FStrings := FScript;
  201. FIndex := @ScriptIndex;
  202. end;
  203. { if (not InInput) and (FInput.Count <> ImportIndex) then
  204. begin
  205. FStrings := FInput;
  206. FIndex := @ImportIndex;
  207. end
  208. else
  209. begin
  210. FStrings := FScript;
  211. FIndex := @ScriptIndex;
  212. end; }
  213. if FIndex^ = FStrings.Count then
  214. Result := false
  215. else
  216. begin
  217. Result := true;
  218. repeat
  219. FWork := FWork + CRLF + FStrings[FIndex^];
  220. Inc(FIndex^);
  221. until (FIndex^ = FStrings.Count) or
  222. (Trim(FWork) <> '');
  223. end;
  224. end;
  225. constructor TIBSQLParser.Create(AOwner: TComponent);
  226. begin
  227. inherited;
  228. FScript := TStringList.Create;
  229. FTokens := TStringList.Create;
  230. FInput := TStringList.Create;
  231. ImportIndex := 0;
  232. FTerminator := ';'; {do not localize}
  233. end;
  234. destructor TIBSQLParser.Destroy;
  235. begin
  236. FScript.Free;
  237. FTokens.Free;
  238. FInput.Free;
  239. inherited;
  240. end;
  241. procedure TIBSQLParser.DoOnError(Error, SQLText: string);
  242. begin
  243. if Assigned(FOnError) then
  244. FOnError(Self, Error, SQLText, LineIndex);
  245. end;
  246. procedure TIBSQLParser.DoOnParse(AKind: TIBParseKind; SQLText: string);
  247. begin
  248. if Assigned(FOnParse) then
  249. FOnParse(Self, AKind, SQLText);
  250. end;
  251. procedure TIBSQLParser.DoParser;
  252. var
  253. Stmt: TIBParseKind;
  254. Statement: string;
  255. i: Integer;
  256. begin
  257. while ((ScriptIndex < FScript.Count) or
  258. (Trim(FWork) <> '') or
  259. (ImportIndex < FInput.Count)) and
  260. not FPaused do
  261. begin
  262. Statement := TokenizeNextLine;
  263. Stmt := IsValidStatement;
  264. case Stmt of
  265. stmtERR:
  266. DoOnError('Invalid statement', Statement);
  267. stmtTERM:
  268. begin
  269. DoOnParse(Stmt, FTokens[2]);
  270. FTerminator := FTokens[2];
  271. end;
  272. stmtINPUT:
  273. try
  274. LoadInput;
  275. except
  276. on E: Exception do
  277. DoOnError(E.Message, Statement);
  278. end;
  279. stmtEMPTY:
  280. Continue;
  281. stmtSET:
  282. begin
  283. Statement := '';
  284. for i := 1 to FTokens.Count - 1 do
  285. Statement := Statement + FTokens[i] + ' ';
  286. Statement := TrimRight(Statement);
  287. DoOnParse(Stmt, Statement);
  288. end;
  289. else
  290. DoOnParse(stmt, Statement);
  291. end;
  292. end;
  293. end;
  294. function TIBSQLParser.IsValidStatement: TIBParseKind;
  295. var
  296. Token, Token1 : String;
  297. begin
  298. if FTokens.Count = 0 then
  299. begin
  300. Result := stmtEmpty;
  301. Exit;
  302. end;
  303. Token := AnsiUpperCase(FTokens[0]);
  304. if Token = 'COMMIT' then {do not localize}
  305. begin
  306. Result := stmtCOMMIT;
  307. exit;
  308. end;
  309. if Token = 'ROLLBACK' then {do not localize}
  310. begin
  311. Result := stmtROLLBACK;
  312. Exit;
  313. end;
  314. Token1 := AnsiUpperCase(FTokens[1]);
  315. if FTokens.Count < 2 then
  316. begin
  317. Result := stmtERR;
  318. Exit;
  319. end;
  320. if (Token = 'INSERT') or (Token = 'DELETE') or {do not localize}
  321. (Token = 'SELECT') or (Token = 'UPDATE') or {do not localize}
  322. (Token = 'EXECUTE') or {do not localize}
  323. ((Token = 'EXECUTE') and (Token1 = 'PROCEDURE')) then {do not localize}
  324. Result := stmtDML
  325. else
  326. if Token = 'INPUT' then {do not localize}
  327. Result := stmtINPUT
  328. else
  329. if Token = 'CONNECT' then {do not localize}
  330. Result := stmtCONNECT
  331. else
  332. if (Token = 'CREATE') and
  333. ((Token1 = 'DATABASE') or (Token1 = 'SCHEMA')) then {do not localize}
  334. Result := stmtCREATE
  335. else
  336. if (Token = 'DROP') and (Token1 = 'DATABASE') then {do not localize}
  337. Result := stmtDROP
  338. else
  339. if (Token = 'DECLARE') or (Token = 'CREATE') or (Token = 'ALTER') or {do not localize}
  340. (Token = 'GRANT') or (Token = 'REVOKE') or (Token = 'DROP') or {do not localize}
  341. ((Token = 'SET') and ((Token1 = 'GENERATOR'))) then {do not localize}
  342. Result := stmtDDL
  343. else
  344. if (Token = 'SET') then {do not localize}
  345. begin
  346. if (Token1 = 'TERM') then {do not localize}
  347. if FTokens.Count = 3 then
  348. Result := stmtTERM
  349. else
  350. Result := stmtERR
  351. else
  352. if (Token1 = 'SQL') then {do not localize}
  353. if (FTokens.Count = 4) and
  354. (AnsiUpperCase(FTokens[2]) = 'DIALECT') then {do not localize}
  355. Result := stmtSET
  356. else
  357. Result := stmtERR
  358. else
  359. if (Token1 = 'AUTODDL') or (Token1 = 'STATISTICS') then {do not localize}
  360. if FTokens.Count = 3 then
  361. Result := stmtSET
  362. else
  363. Result := stmtERR
  364. else
  365. Result := stmtERR;
  366. end
  367. else
  368. Result := stmtERR;
  369. end;
  370. procedure TIBSQLParser.LoadInput;
  371. var
  372. FileName: string;
  373. begin
  374. FInput.Clear;
  375. ImportIndex := 0;
  376. FileName := FTokens[1];
  377. if FileName[1] in [QUOTE, DBL_QUOTE] then
  378. Delete(FileName, 1, 1);
  379. if FileName[Length(FileName)] in [QUOTE, DBL_QUOTE] then
  380. Delete(FileName, Length(FileName), 1);
  381. FInput.LoadFromFile(FileName);
  382. end;
  383. procedure TIBSQLParser.Parse;
  384. begin
  385. ScriptIndex := 0;
  386. ImportIndex := 0;
  387. FInput.Clear;
  388. FPaused := false;
  389. DoParser;
  390. end;
  391. procedure TIBSQLParser.RemoveComment;
  392. var
  393. Start, Ending: Integer;
  394. begin
  395. FWork := TrimLeft(FWork);
  396. Start := AnsiPos('/*', FWork); {do not localize}
  397. while Start = 1 do
  398. begin
  399. Ending := AnsiPos('*/', FWork); {do not localize}
  400. while Ending < Start do
  401. begin
  402. if AppendNextLine = false then
  403. raise Exception.Create('Invalid Comment');
  404. Ending := AnsiPos('*/', FWork); {do not localize}
  405. end;
  406. Delete(FWork, Start, Ending - Start + 2);
  407. FWork := TrimLeft(FWork);
  408. if FWork = '' then
  409. AppendNextLine;
  410. FWork := TrimLeft(FWork);
  411. Start := AnsiPos('/*', FWork); {do not localize}
  412. end;
  413. FWork := TrimLeft(FWork);
  414. end;
  415. procedure TIBSQLParser.SetPaused(const Value: Boolean);
  416. begin
  417. if FPaused <> Value then
  418. begin
  419. FPaused := Value;
  420. if not FPaused then
  421. DoParser;
  422. end;
  423. end;
  424. procedure TIBSQLParser.SetScript(const Value: TStrings);
  425. begin
  426. FScript.Assign(Value);
  427. FPaused := false;
  428. ScriptIndex := 0;
  429. ImportIndex := 0;
  430. FInput.Clear;
  431. end;
  432. { Note on TokenizeNextLine. This is not intended to actually tokenize in
  433. terms of SQL tokens. It has two goals. First is to get the primary statement
  434. type in FTokens[0]. These are items like SELECT, UPDATE, CREATE, SET, IMPORT.
  435. The secondary function is to correctly find the end of a statement. So if the
  436. terminator is ; and the statement is "SELECT 'FDR'';' from Table1;" while
  437. correct SQL tokenization is SELECT, 'FDR'';', FROM, Table1 but this is more
  438. than needed. The Tokenizer will tokenize this as SELECT, 'FDR', ';', FROM,
  439. Table1. We get that it is a SELECT statement and get the correct termination
  440. and whole statement in the case where the terminator is embedded inside
  441. a ' or ". }
  442. function TIBSQLParser.TokenizeNextLine: string;
  443. var
  444. InQuote, InDouble, InComment, Done: Boolean;
  445. NextWord: string;
  446. Index: Integer;
  447. procedure ScanToken;
  448. var
  449. SDone: Boolean;
  450. begin
  451. NextWord := '';
  452. SDone := false;
  453. Index := 1;
  454. while (Index <= Length(FWork)) and (not SDone) do
  455. begin
  456. { Hit the terminator, but it is not embedded in a single or double quote
  457. or inside a comment }
  458. if ((not InQuote) and (not InDouble) and (not InComment)) and
  459. (CompareStr(FTerminator, Copy(FWork, Index, Length(FTerminator))) = 0)
  460. then
  461. begin
  462. Done := true;
  463. Result := Result + NextWord;
  464. Delete(FWork, 1, Length(NextWord) + Length(FTerminator));
  465. NextWord := Trim(AnsiUpperCase(NextWord));
  466. if NextWord <> '' then
  467. FTokens.Add(AnsiUpperCase(NextWord));
  468. Exit;
  469. end;
  470. { Are we entering or exiting an inline comment? }
  471. if (Index < Length(FWork)) and ((not Indouble) or (not InQuote)) and
  472. (FWork[Index] = '/') and (FWork[Index + 1] = '*') then {do not localize}
  473. InComment := true;
  474. if InComment and (Index <> 1) and
  475. (FWork[Index] = '/') and (FWork[Index - 1] = '*') then {do not localize}
  476. InComment := false;
  477. if not InComment then
  478. { Handle case when the character is a single quote or a double quote }
  479. case FWork[Index] of
  480. QUOTE:
  481. if not InDouble then
  482. begin
  483. if InQuote then
  484. begin
  485. InQuote := false;
  486. SDone := true;
  487. end
  488. else
  489. InQuote := true;
  490. end;
  491. DBL_QUOTE:
  492. if not InQuote then
  493. begin
  494. if InDouble then
  495. begin
  496. Indouble := false;
  497. SDone := true;
  498. end
  499. else
  500. InDouble := true;
  501. end;
  502. ' ': {do not localize}
  503. if (not InDouble) and (not InQuote) then
  504. SDone := true;
  505. end;
  506. NextWord := NextWord + FWork[Index];
  507. Inc(Index);
  508. end;
  509. { copy over the remaining non character or spaces until the next word }
  510. while (Index <= Length(FWork)) and (FWork[Index] <= #32) do
  511. begin
  512. NextWord := NextWord + FWork[Index];
  513. Inc(Index);
  514. end;
  515. Result := Result + NextWord;
  516. Delete(FWork, 1, Length(NextWord));
  517. NextWord := Trim(NextWord);
  518. if NextWord <> '' then
  519. FTokens.Add(NextWord);
  520. end;
  521. begin
  522. FTokens.Clear;
  523. if FWork = '' then
  524. AppendNextLine;
  525. if not InInput then
  526. LineIndex := ScriptIndex;
  527. try
  528. RemoveComment;
  529. except
  530. on E: Exception do
  531. begin
  532. DoOnError(E.Message, '');
  533. exit;
  534. end
  535. end;
  536. InQuote := false;
  537. InDouble := false;
  538. InComment := false;
  539. Done := false;
  540. Result := '';
  541. while not Done do
  542. begin
  543. { Check the work queue, if it is empty get the next line to process }
  544. if FWork = '' then
  545. if not AppendNextLine then
  546. exit;
  547. ScanToken;
  548. end;
  549. end;
  550. { TIBScript }
  551. constructor TIBScript.Create(AOwner: TComponent);
  552. begin
  553. inherited;
  554. FSQLParser := TIBSQLParser.Create(self);
  555. FSQLParser.OnError := ParserError;
  556. FSQLParser.OnParse := ParserParse;
  557. Terminator := ';'; {do not localize}
  558. FDDLTransaction := TIBTransaction.Create(self);
  559. FDDLQuery := TIBSQL.Create(self);
  560. FDDLQuery.ParamCheck := false;
  561. FAutoDDL := true;
  562. FStatsOn := true;
  563. FStats := TIBScriptStats.Create;
  564. FStats.Database := FDatabase;
  565. FSQLDialect := 3;
  566. end;
  567. destructor TIBScript.Destroy;
  568. begin
  569. FStats.Free;
  570. inherited;
  571. end;
  572. procedure TIBScript.DoConnect(const SQLText: string);
  573. var
  574. i: integer;
  575. Param: string;
  576. begin
  577. SetupNewConnection;
  578. if Database.Connected then
  579. Database.Connected := false;
  580. Database.SQLDialect := FSQLDialect;
  581. Database.Params.Clear;
  582. Database.DatabaseName := StripQuote(FSQLParser.CurrentTokens[1]);
  583. i := 2;
  584. while i < FSQLParser.CurrentTokens.Count - 1 do
  585. begin
  586. if AnsiCompareText(FSQLParser.CurrentTokens[i], 'USER') = 0 then {do not localize}
  587. Param := 'user_name'; {do not localize}
  588. if AnsiCompareText(FSQLParser.CurrentTokens[i], 'PASSWORD') = 0 then {do not localize}
  589. Param := 'password'; {do not localize}
  590. if AnsiCompareText(FSQLParser.CurrentTokens[i], 'ROLE') = 0 then {do not localize}
  591. Param := 'user_role'; {do not localize}
  592. Database.Params.Add(Param + '=' + StripQuote(FSQLParser.CurrentTokens[i +
  593. 1]));
  594. Inc(i, 2);
  595. end;
  596. Database.Connected := true;
  597. end;
  598. procedure TIBScript.DoCreate(const SQLText: string);
  599. var
  600. i: Integer;
  601. begin
  602. SetupNewConnection;
  603. FDatabase.DatabaseName := StripQuote(FSQLParser.CurrentTokens[2]);
  604. i := 3;
  605. while i < FSQLParser.CurrentTokens.Count - 1 do
  606. begin
  607. Database.Params.Add(FSQLParser.CurrentTokens[i] + ' ' +
  608. FSQLParser.CurrentTokens[i + 1]);
  609. Inc(i, 2);
  610. end;
  611. FDatabase.SQLDialect := FSQLDialect;
  612. FDatabase.CreateDatabase;
  613. if FStatsOn and Assigned(FDatabase) and FDatabase.Connected then
  614. FStats.Start;
  615. end;
  616. procedure TIBScript.DoDDL(const Text: string);
  617. begin
  618. if AutoDDL then
  619. FDDLQuery.Transaction := FDDLTransaction
  620. else
  621. FDDLQuery.Transaction := FTransaction;
  622. if not FDDLQuery.Transaction.InTransaction then
  623. FDDLQuery.Transaction.StartTransaction;
  624. FDDLQuery.SQL.Text := Text;
  625. {!!!!!!!!!!!! ??????????. ?????? ???? ?/? ????? ??????? ???????????,
  626. ?? ???????????? ?????? ??, ? ?? ???? ?????? ?? ?????. Julia
  627. FDDLQuery.ExecQuery;
  628. if AutoDDL then
  629. FDDLTransaction.Commit;}
  630. {!!!} // ?????????? ?????? ? ???????????. JKL.
  631. try
  632. FDDLQuery.ExecQuery;
  633. if AutoDDL then
  634. FDDLTransaction.Commit;
  635. except
  636. if AutoDDL then
  637. // FDDLTransaction.Rollback
  638. FDDLTransaction.Commit
  639. else
  640. raise;
  641. end;
  642. {!!!!!!!!!!!!!!!!!!!}
  643. end;
  644. procedure TIBScript.DoDML(const Text: string);
  645. var
  646. FPaused : Boolean;
  647. begin
  648. FPaused := false;
  649. if Assigned(FDataSet) then
  650. begin
  651. if FDataSet.Active then
  652. FDataSet.Close;
  653. FDataSet.SelectSQL.Text := Text;
  654. FDataset.Prepare;
  655. if (FDataSet.Params.Count <> 0) and Assigned(FOnParamCheck) then
  656. begin
  657. FOnParamCheck(self, FPaused);
  658. if FPaused then
  659. begin
  660. FSQLParser.Paused := true;
  661. exit;
  662. end;
  663. end;
  664. if FDataset.SQLType = SQLSelect then
  665. FDataSet.Open
  666. else
  667. FDataset.ExecSQL;
  668. end
  669. else
  670. begin
  671. if FDMLQuery.Open then
  672. FDMLQuery.Close;
  673. FDMLQuery.SQL.Text := Text;
  674. FDMLQuery.Prepare;
  675. if (FDMLQuery.Params.Count <> 0) and Assigned(FOnParamCheck) then
  676. begin
  677. FOnParamCheck(self, FPaused);
  678. if FPaused then
  679. begin
  680. FSQLParser.Paused := true;
  681. exit;
  682. end;
  683. end;
  684. FDMLQuery.ExecQuery;
  685. end;
  686. end;
  687. procedure TIBScript.DoSET(const Text: string);
  688. begin
  689. if AnsiCompareText('AUTODDL', FSQLParser.CurrentTokens[1]) = 0 then {do not localize}
  690. FAutoDDL := FSQLParser.CurrentTokens[2] = 'ON' {do not localize}
  691. else
  692. if AnsiCompareText('STATISTICS', FSQLParser.CurrentTokens[1]) = 0 then {do not localize}
  693. Statistics := FSQLParser.CurrentTokens[2] = 'ON' {do not localize}
  694. else
  695. if (AnsiCompareText('SQL', FSQLParser.CurrentTokens[1]) = 0) and {do not localize}
  696. (AnsiCompareText('DIALECT', FSQLParser.CurrentTokens[2]) = 0) then {do not localize}
  697. begin
  698. FSQLDialect := StrToInt(FSQLParser.CurrentTokens[3]);
  699. if Database.SQLDialect <> FSQLDialect then
  700. begin
  701. if Database.Connected then
  702. begin
  703. Database.Close;
  704. Database.SQLDialect := FSQLDialect;
  705. Database.Open;
  706. end
  707. else
  708. Database.SQLDialect := FSQLDialect;
  709. end;
  710. end;
  711. end;
  712. procedure TIBScript.DropDatabase(const SQLText: string);
  713. begin
  714. FDatabase.DropDatabase;
  715. end;
  716. procedure TIBScript.ExecuteScript;
  717. begin
  718. FContinue := true;
  719. FExecuting := true;
  720. if not Assigned(FDataset) then
  721. FDMLQuery := TIBSQL.Create(FDatabase);
  722. try
  723. FStats.Clear;
  724. if FStatsOn and Assigned(FDatabase) and FDatabase.Connected then
  725. FStats.Start;
  726. FSQLParser.Parse;
  727. if FStatsOn then
  728. FStats.Stop;
  729. finally
  730. FExecuting := false;
  731. if Assigned(FDMLQuery) then
  732. FreeAndNil(FDMLQuery);
  733. end;
  734. end;
  735. function TIBScript.GetPaused: Boolean;
  736. begin
  737. Result := FSQLParser.Paused;
  738. end;
  739. function TIBScript.GetScript: TStrings;
  740. begin
  741. Result := FSQLParser.Script;
  742. end;
  743. function TIBScript.GetSQLParams: TIBXSQLDA;
  744. begin
  745. if Assigned(FDataset) then
  746. Result := FDataset.Params
  747. else
  748. Result := FDMLQuery.Params;
  749. end;
  750. function TIBScript.GetTokens: TStrings;
  751. begin
  752. Result := FSQLParser.CurrentTokens;
  753. end;
  754. procedure TIBScript.Notification(AComponent: TComponent;
  755. Operation: TOperation);
  756. begin
  757. inherited;
  758. if Operation = opRemove then
  759. begin
  760. if AComponent = FDataset then
  761. FDataset := nil
  762. else
  763. if AComponent = FDatabase then
  764. FDatabase := nil
  765. else
  766. if AComponent = FTransaction then
  767. FTransaction := nil;
  768. end;
  769. end;
  770. function TIBScript.ParamByName(Idx: String): TIBXSQLVAR;
  771. begin
  772. if Assigned(FDataset) then
  773. Result := FDataset.ParamByName(Idx)
  774. else
  775. Result := FDMLQuery.ParamByName(Idx);
  776. end;
  777. procedure TIBScript.ParserError(Sender: TObject; Error,
  778. SQLText: string; LineIndex: Integer);
  779. begin
  780. if Assigned(FOnError) then
  781. FOnError(Self, Error, SQLText, LineIndex);
  782. FValidate := false;
  783. FSQLParser.Paused := true;
  784. end;
  785. procedure TIBScript.ParserParse(Sender: TObject; AKind: TIBParseKind;
  786. SQLText: string);
  787. begin
  788. try
  789. FCurrentStmt := AKind;
  790. if not FValidating then
  791. case AKind of
  792. stmtDrop : DropDatabase(SQLText);
  793. stmtDDL : DoDDL(SQLText);
  794. stmtDML: DoDML(SQLText);
  795. stmtSET: DoSET(SQLText);
  796. stmtCONNECT: DoConnect(SQLText);
  797. stmtCREATE: DoCreate(SQLText);
  798. stmtTERM: FTerminator := Trim(SQLText);
  799. stmtCOMMIT:
  800. if FTransaction.InTransaction then
  801. FTransaction.Commit;
  802. stmtROLLBACK:
  803. if FTransaction.InTransaction then
  804. FTransaction.Rollback
  805. end;
  806. if Assigned(FOnParse) then
  807. FOnParse(self, AKind, SQLText);
  808. except
  809. on E: EIBError do
  810. begin
  811. FContinue := false;
  812. FValidate := false;
  813. FSQLParser.Paused := true;
  814. if Assigned(FOnExecuteError) then
  815. FOnExecuteError(Self, E.Message, SQLText, FSQLParser.CurrentLine,
  816. FContinue)
  817. else
  818. raise;
  819. if FContinue then
  820. FSQLParser.Paused := false;
  821. end;
  822. end;
  823. end;
  824. procedure TIBScript.SetDatabase(const Value: TIBDatabase);
  825. begin
  826. if FDatabase <> Value then
  827. begin
  828. FDatabase := Value;
  829. FDDLQuery.Database := Value;
  830. FDDLTransaction.DefaultDatabase := Value;
  831. FStats.Database := Value;
  832. if Assigned(FDMLQuery) then
  833. FDMLQuery.Database := Value;
  834. end;
  835. end;
  836. procedure TIBScript.SetPaused(const Value: Boolean);
  837. begin
  838. if FSQLParser.Paused and (FCurrentStmt = stmtDML) then
  839. if Assigned(FDataSet) then
  840. begin
  841. if FDataset.SQLType = SQLSelect then
  842. FDataSet.Open
  843. else
  844. FDataset.ExecSQL;
  845. end
  846. else
  847. begin
  848. FDMLQuery.ExecQuery;
  849. end;
  850. FSQLParser.Paused := Value;
  851. end;
  852. procedure TIBScript.SetScript(const Value: TStrings);
  853. begin
  854. FSQLParser.Script.Assign(Value);
  855. end;
  856. procedure TIBScript.SetStatsOn(const Value: boolean);
  857. begin
  858. if FStatsOn <> Value then
  859. begin
  860. FStatsOn := Value;
  861. if FExecuting then
  862. begin
  863. if FStatsOn then
  864. FStats.Start
  865. else
  866. FStats.Stop;
  867. end;
  868. end;
  869. end;
  870. procedure TIBScript.SetTerminator(const Value: string);
  871. begin
  872. if FTerminator <> Value then
  873. begin
  874. FTerminator := Value;
  875. FSQLParser.Terminator := Value;
  876. end;
  877. end;
  878. procedure TIBScript.SetTransaction(const Value: TIBTransaction);
  879. begin
  880. FTransaction := Value;
  881. if Assigned(FDMLQuery) then
  882. FDMLQuery.Transaction := Value;
  883. end;
  884. procedure TIBScript.SetupNewConnection;
  885. begin
  886. FDDLTransaction.RemoveDatabase(FDDLTransaction.FindDatabase(FDatabase));
  887. if FDatabase.Owner = self then
  888. FDatabase.Free;
  889. Database := TIBDatabase.Create(self);
  890. if FTransaction.Owner = self then
  891. FTransaction.Free;
  892. FTransaction := TIBTransaction.Create(self);
  893. FDatabase.DefaultTransaction := FTransaction;
  894. FTransaction.DefaultDatabase := FDatabase;
  895. FDDLTransaction.DefaultDatabase := FDatabase;
  896. FDDLQuery.Database := FDatabase;
  897. if Assigned(FDataset) then
  898. begin
  899. FDataset.Database := FDatabase;
  900. FDataset.Transaction := FTransaction;
  901. end;
  902. end;
  903. function TIBScript.StripQuote(const Text: string): string;
  904. begin
  905. Result := Text;
  906. if Result[1] in [Quote, DBL_QUOTE] then
  907. begin
  908. Delete(Result, 1, 1);
  909. Delete(Result, Length(Result), 1);
  910. end;
  911. end;
  912. function TIBScript.ValidateScript: Boolean;
  913. begin
  914. FValidating := true;
  915. FValidate := true;
  916. FSQLParser.Parse;
  917. Result := FValidate;
  918. FValidating := false;
  919. end;
  920. { TIBScriptStats }
  921. function TIBScriptStats.AddStringValues(list: TStrings): int64;
  922. var
  923. i : integer;
  924. index : integer;
  925. begin
  926. try
  927. Result := 0;
  928. for i := 0 to list.count-1 do
  929. begin
  930. index := Pos('=', list.strings[i]); {do not localize}
  931. if index > 0 then
  932. Result := Result + StrToInt(Copy(list.strings[i], index + 1, 255));
  933. end;
  934. except
  935. Result := 0;
  936. end;
  937. end;
  938. procedure TIBScriptStats.Clear;
  939. begin
  940. FBuffers := 0;
  941. FReads := 0;
  942. FWrites := 0;
  943. FSeqReads := 0;
  944. FFetches := 0;
  945. FReadIdx := 0;
  946. FDeltaMem := 0;
  947. end;
  948. constructor TIBScriptStats.Create;
  949. begin
  950. FInfoStats := TIBDatabaseInfo.Create(nil);
  951. end;
  952. destructor TIBScriptStats.Destroy;
  953. begin
  954. FInfoStats.Destroy;
  955. inherited;
  956. end;
  957. procedure TIBScriptStats.SetDatabase(const Value: TIBDatabase);
  958. begin
  959. FDatabase := Value;
  960. FInfoStats.Database := Value;
  961. end;
  962. procedure TIBScriptStats.Start;
  963. begin
  964. FStartBuffers := FInfoStats.NumBuffers;
  965. FStartReads := FInfoStats.Reads;
  966. FStartWrites := FInfoStats.Writes;
  967. FStartSeqReads := AddStringValues(FInfoStats.ReadSeqCount);
  968. FStartFetches := FInfoStats.Fetches;
  969. FStartReadIdx := AddStringValues(FInfoStats.ReadIdxCount);
  970. FStartingMem := FInfoStats.CurrentMemory;
  971. end;
  972. procedure TIBScriptStats.Stop;
  973. begin
  974. FBuffers := FInfoStats.NumBuffers - FStartBuffers + FBuffers;
  975. FReads := FInfoStats.Reads - FStartReads + FReads;
  976. FWrites := FInfoStats.Writes - FStartWrites + FWrites;
  977. FSeqReads := AddStringValues(FInfoStats.ReadSeqCount) - FStartSeqReads + FSeqReads;
  978. FReadIdx := AddStringValues(FInfoStats.ReadIdxCount) - FStartReadIdx + FReadIdx;
  979. FFetches := FInfoStats.Fetches - FStartFetches + FFetches;
  980. FDeltaMem := FInfoStats.CurrentMemory - FStartingMem + FDeltaMem;
  981. end;
  982. end.