ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/nongui/IBSQL.pas
Revision: 345
Committed: Mon Aug 23 14:22:29 2021 UTC (2 years, 8 months ago) by tony
Content type: text/x-pascal
File size: 27241 byte(s)
Log Message:
Merged into public release

File Contents

# Content
1 {************************************************************************}
2 { }
3 { Borland Delphi Visual Component Library }
4 { InterBase Express core components }
5 { }
6 { Copyright (c) 1998-2000 Inprise Corporation }
7 { }
8 { InterBase Express is based in part on the product }
9 { Free IB Components, written by Gregory H. Deatz for }
10 { Hoagland, Longo, Moran, Dunst & Doukas Company. }
11 { Free IB Components is used under license. }
12 { }
13 { The contents of this file are subject to the InterBase }
14 { Public License Version 1.0 (the "License"); you may not }
15 { use this file except in compliance with the License. You }
16 { may obtain a copy of the License at http://www.Inprise.com/IPL.html }
17 { Software distributed under the License is distributed on }
18 { an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either }
19 { express or implied. See the License for the specific language }
20 { governing rights and limitations under the License. }
21 { The Original Code was created by InterBase Software Corporation }
22 { and its successors. }
23 { Portions created by Inprise Corporation are Copyright (C) Inprise }
24 { Corporation. All Rights Reserved. }
25 { Contributor(s): Jeff Overcash }
26 { }
27 { IBX For Lazarus (Firebird Express) }
28 { Contributor: Tony Whyman, MWA Software http://www.mwasoftware.co.uk }
29 { Portions created by MWA Software are copyright McCallum Whyman }
30 { Associates Ltd 2011 - 2014 }
31 { }
32 {************************************************************************}
33
34 unit IBSQL;
35
36 {$Mode Delphi}
37
38 {$codepage UTF8}
39
40 (* Define IBXQUERYSTATS to write to stdout a summary of query execution
41 statistics each time a query is executed
42
43 Define IBXQUERYTIME to write to stdout The local execution time for each
44 query
45 *)
46
47 { $DEFINE IBXQUERYSTATS}
48 { $DEFINE IBXQUERYTIME}
49
50 interface
51
52 uses
53 {$IFDEF WINDOWS }
54 Windows,
55 {$ELSE}
56 baseunix, unix,
57 {$ENDIF}
58 SysUtils, Classes, IB, IBDatabase, IBUtils;
59
60 type
61 { TIBBatch }
62
63 TIBBatch = class(TObject)
64 protected
65 FFilename: String;
66 FColumns: IResults;
67 FParams: ISQLParams;
68 public
69 procedure ReadyFile; virtual; abstract;
70 property Columns: IResults read FColumns;
71 property Filename: String read FFilename write FFilename;
72 property Params: ISQLParams read FParams;
73 end;
74
75 TIBBatchInput = class(TIBBatch)
76 public
77 function ReadParameters: Boolean; virtual; abstract;
78 end;
79
80 TIBBatchOutput = class(TIBBatch)
81 public
82 function WriteColumns: Boolean; virtual; abstract;
83 end;
84
85
86 { TIBOutputDelimitedFile }
87 TIBOutputDelimitedFile = class(TIBBatchOutput)
88 protected
89 {$IFDEF UNIX}
90 FHandle: cint;
91 {$ELSE}
92 FHandle: THandle;
93 {$ENDIF}
94 FOutputTitles: Boolean;
95 FColDelimiter,
96 FRowDelimiter: string;
97 public
98 destructor Destroy; override;
99 procedure ReadyFile; override;
100 function WriteColumns: Boolean; override;
101 property ColDelimiter: string read FColDelimiter write FColDelimiter;
102 property OutputTitles: Boolean read FOutputTitles
103 write FOutputTitles;
104 property RowDelimiter: string read FRowDelimiter write FRowDelimiter;
105 end;
106
107 { TIBInputDelimitedFile }
108 TIBInputDelimitedFile = class(TIBBatchInput)
109 protected
110 FColDelimiter,
111 FRowDelimiter: string;
112 FEOF: Boolean;
113 FFile: TFileStream;
114 FLookAhead: Char;
115 FReadBlanksAsNull: Boolean;
116 FSkipTitles: Boolean;
117 public
118 destructor Destroy; override;
119 function GetColumn(var Col: string): Integer;
120 function ReadParameters: Boolean; override;
121 procedure ReadyFile; override;
122 property ColDelimiter: string read FColDelimiter write FColDelimiter;
123 property ReadBlanksAsNull: Boolean read FReadBlanksAsNull
124 write FReadBlanksAsNull;
125 property RowDelimiter: string read FRowDelimiter write FRowDelimiter;
126 property SkipTitles: Boolean read FSkipTitles write FSkipTitles;
127 end;
128
129 { TIBOutputRawFile }
130 TIBOutputRawFile = class(TIBBatchOutput)
131 protected
132 {$IFDEF UNIX}
133 FHandle: cint;
134 {$ELSE}
135 FHandle: THandle;
136 {$ENDIF}
137 public
138 destructor Destroy; override;
139 procedure ReadyFile; override;
140 function WriteColumns: Boolean; override;
141 end;
142
143 { TIBInputRawFile }
144 TIBInputRawFile = class(TIBBatchInput)
145 protected
146 {$IFDEF UNIX}
147 FHandle: cint;
148 {$ELSE}
149 FHandle: THandle;
150 {$ENDIF}
151 public
152 destructor Destroy; override;
153 function ReadParameters: Boolean; override;
154 procedure ReadyFile; override;
155 end;
156
157 { TIBSQL }
158
159 TIBSQL = class(TComponent)
160 private
161 FCaseSensitiveParameterNames: boolean;
162 FMetaData: IMetaData;
163 FSQLParams: ISQLParams;
164 FStatement: IStatement;
165 FOnSQLChanged: TNotifyEvent;
166 FUniqueParamNames: Boolean;
167 FBOF: boolean;
168 FEOF: boolean;
169 function GetFieldCount: integer;
170 function GetOpen: Boolean;
171 function GetPrepared: Boolean;
172 function GetSQLStatementType: TIBSQLStatementTypes;
173 procedure SetUniqueParamNames(AValue: Boolean);
174 protected
175 FBase: TIBBase;
176 FGoToFirstRecordOnExecute: boolean; { Automatically position record on first record after executing }
177 FRecordCount: Integer; { How many records have been read so far? }
178 FOnSQLChanging: TNotifyEvent; { Call this when the SQL is changing }
179 FSQL: TStrings; { SQL Query (by user) }
180 FParamCheck: Boolean; { Check for parameters? (just like TQuery) }
181 FResults: IResults; {Single row results from exec}
182 FResultSet: IResultSet; {Multi-row results from open cursor}
183 FGenerateParamNames: Boolean; { Auto generate param names ?}
184 procedure DoBeforeDatabaseDisconnect(Sender: TObject);
185 function GetDatabase: TIBDatabase;
186 function GetEOF: Boolean;
187 function GetFields(const Idx: Integer): ISQLData;
188 function GetFieldIndex(FieldName: String): Integer;
189 function GetPlan: String;
190 function GetRecordCount: Integer;
191 function GetRowsAffected: Int64;
192 function GetSQLParams: ISQLParams;
193 function GetTransaction: TIBTransaction;
194 procedure SetDatabase(Value: TIBDatabase);
195 procedure SetSQL(Value: TStrings);
196 procedure SetTransaction(Value: TIBTransaction);
197 procedure SQLChanging(Sender: TObject);
198 procedure SQLChanged(Sender: TObject);
199 procedure BeforeTransactionEnd(Sender: TObject; Action: TTransactionAction);
200 public
201 constructor Create(AOwner: TComponent); override;
202 destructor Destroy; override;
203 procedure BatchInput(InputObject: TIBBatchInput);
204 procedure BatchOutput(OutputObject: TIBBatchOutput);
205 procedure CheckClosed; { raise error if query is not closed. }
206 procedure CheckOpen; { raise error if query is not open.}
207 procedure CheckValidStatement; { raise error if statement is invalid.}
208 procedure Close;
209 procedure ExecQuery;
210 function HasField(FieldName: String): boolean; {Note: case sensitive match}
211 function FieldByName(FieldName: String): ISQLData;
212 function ParamByName(ParamName: String): ISQLParam;
213 procedure FreeHandle;
214 function Next: boolean;
215 procedure Prepare;
216 function GetUniqueRelationName: String;
217 property Bof: Boolean read FBOF;
218 property Eof: Boolean read GetEOF;
219 property Current: IResults read FResults;
220 property Fields[const Idx: Integer]: ISQLData read GetFields; default;
221 property FieldIndex[FieldName: String]: Integer read GetFieldIndex;
222 property FieldCount: integer read GetFieldCount;
223 property Open: Boolean read GetOpen;
224 property Params: ISQLParams read GetSQLParams;
225 property Plan: String read GetPlan;
226 property Prepared: Boolean read GetPrepared;
227 property RecordCount: Integer read GetRecordCount;
228 property RowsAffected: Int64 read GetRowsAffected;
229 property SQLStatementType: TIBSQLStatementTypes read GetSQLStatementType;
230 property UniqueRelationName: String read GetUniqueRelationName;
231 property Statement: IStatement read FStatement;
232 property MetaData: IMetaData read FMetaData;
233 public
234 {Batch Interface}
235 function HasBatchMode: boolean;
236 function IsInBatchMode: boolean;
237 procedure AddToBatch;
238 function ExecuteBatch: IBatchCompletion;
239 procedure CancelBatch;
240 function GetBatchCompletion: IBatchCompletion;
241 function GetBatchRowLimit: integer;
242 procedure SetBatchRowLimit(aLimit: integer);
243 published
244 property Database: TIBDatabase read GetDatabase write SetDatabase;
245 property CaseSensitiveParameterNames: boolean read FCaseSensitiveParameterNames
246 write FCaseSensitiveParameterNames;
247 property GenerateParamNames: Boolean read FGenerateParamNames write FGenerateParamNames;
248 property UniqueParamNames: Boolean read FUniqueParamNames write SetUniqueParamNames;
249 property GoToFirstRecordOnExecute: Boolean read FGoToFirstRecordOnExecute
250 write FGoToFirstRecordOnExecute
251 default True;
252 property ParamCheck: Boolean read FParamCheck write FParamCheck;
253 property SQL: TStrings read FSQL write SetSQL;
254 property Transaction: TIBTransaction read GetTransaction write SetTransaction;
255 property OnSQLChanging: TNotifyEvent read FOnSQLChanging write FOnSQLChanging;
256 property OnSQLChanged: TNotifyEvent read FOnSQLChanged write FOnSQLChanged;
257 end;
258
259 procedure IBAlloc(var P; OldSize, NewSize: Integer);
260
261 implementation
262
263 uses
264 Variants, IBSQLMonitor, IBMessages, IBCustomDataSet;
265
266 procedure IBAlloc(var P; OldSize, NewSize: Integer);
267 var
268 i: Integer;
269 begin
270 ReallocMem(Pointer(P), NewSize);
271 for i := OldSize to NewSize - 1 do PChar(P)[i] := #0;
272 end;
273
274 { TIBOutputDelimitedFile }
275
276 destructor TIBOutputDelimitedFile.Destroy;
277 begin
278 {$IFDEF UNIX}
279 if FHandle <> -1 then
280 fpclose(FHandle);
281 {$ELSE}
282 if FHandle <> 0 then
283 begin
284 FlushFileBuffers(FHandle);
285 CloseHandle(FHandle);
286 end;
287 {$ENDIF}
288 inherited Destroy;
289 end;
290
291 procedure TIBOutputDelimitedFile.ReadyFile;
292 var
293 i: Integer;
294 {$IFDEF UNIX}
295 BytesWritten: cint;
296 {$ELSE}
297 BytesWritten: DWORD;
298 {$ENDIF}
299 st: string;
300 begin
301 if FColDelimiter = '' then
302 FColDelimiter := TAB;
303 if FRowDelimiter = '' then
304 FRowDelimiter := CRLF;
305 {$IFDEF UNIX}
306 FHandle := FpOpen(Filename,O_WrOnly or O_Creat);
307 {$ELSE}
308 FHandle := CreateFile(PChar(Filename), GENERIC_WRITE, 0, nil, CREATE_ALWAYS,
309 FILE_ATTRIBUTE_NORMAL, 0);
310 if FHandle = INVALID_HANDLE_VALUE then
311 FHandle := 0;
312 {$ENDIF}
313 if FOutputTitles then
314 begin
315 for i := 0 to Columns.Count - 1 do
316 if i = 0 then
317 st := Columns[i].GetAliasname
318 else
319 st := st + FColDelimiter + Columns[i].GetAliasname;
320 st := st + FRowDelimiter;
321 {$IFDEF UNIX}
322 if FHandle <> -1 then
323 BytesWritten := FpWrite(FHandle,st[1],Length(st));
324 if BytesWritten = -1 then
325 raise Exception.Create('File Write Error');
326 {$ELSE}
327 WriteFile(FHandle, st[1], Length(st), BytesWritten, nil);
328 {$ENDIF}
329 end;
330 end;
331
332 function TIBOutputDelimitedFile.WriteColumns: Boolean;
333 var
334 i: Integer;
335 {$IFDEF UNIX}
336 BytesWritten: cint;
337 {$ELSE}
338 BytesWritten: DWORD;
339 {$ENDIF}
340 st: string;
341 begin
342 result := False;
343 {$IFDEF UNIX}
344 if FHandle <> -1 then
345 {$ELSE}
346 if FHandle <> 0 then
347 {$ENDIF}
348 begin
349 st := '';
350 for i := 0 to Columns.Count - 1 do
351 begin
352 if i > 0 then
353 st := st + FColDelimiter;
354 st := st + StripString(Columns[i].AsString, FColDelimiter + FRowDelimiter);
355 end;
356 st := st + FRowDelimiter;
357 {$IFDEF UNIX}
358 BytesWritten := FpWrite(FHandle,st[1],Length(st));
359 {$ELSE}
360 WriteFile(FHandle, st[1], Length(st), BytesWritten, nil);
361 {$ENDIF}
362 if BytesWritten = DWORD(Length(st)) then
363 result := True;
364 end
365 end;
366
367 { TIBInputDelimitedFile }
368
369 destructor TIBInputDelimitedFile.Destroy;
370 begin
371 FFile.Free;
372 inherited Destroy;
373 end;
374
375 function TIBInputDelimitedFile.GetColumn(var Col: string): Integer;
376 var
377 c: Char;
378 BytesRead: Integer;
379
380 procedure ReadInput;
381 begin
382 if FLookAhead <> NULL_TERMINATOR then
383 begin
384 c := FLookAhead;
385 BytesRead := 1;
386 FLookAhead := NULL_TERMINATOR;
387 end else
388 BytesRead := FFile.Read(c, 1);
389 end;
390
391 procedure CheckCRLF(Delimiter: string);
392 begin
393 if (c = CR) and (Pos(LF, Delimiter) > 0) then {mbcs ok}
394 begin
395 BytesRead := FFile.Read(c, 1);
396 if (BytesRead = 1) and (c <> #10) then
397 FLookAhead := c
398 end;
399 end;
400
401 begin
402 Col := '';
403 result := 0;
404 ReadInput;
405 while BytesRead <> 0 do begin
406 if Pos(c, FColDelimiter) > 0 then {mbcs ok}
407 begin
408 CheckCRLF(FColDelimiter);
409 result := 1;
410 break;
411 end else if Pos(c, FRowDelimiter) > 0 then {mbcs ok}
412 begin
413 CheckCRLF(FRowDelimiter);
414 result := 2;
415 break;
416 end else
417 Col := Col + c;
418 ReadInput;
419 end;
420 end;
421
422 function TIBInputDelimitedFile.ReadParameters: Boolean;
423 var
424 i, curcol: Integer;
425 Col: string;
426 begin
427 result := False;
428 if not FEOF then begin
429 curcol := 0;
430 repeat
431 i := GetColumn(Col);
432 if (i = 0) then
433 FEOF := True;
434 if (curcol < Params.Count) then
435 begin
436 try
437 if (Col = '') and
438 (ReadBlanksAsNull) then
439 Params[curcol].IsNull := True
440 else
441 Params[curcol].AsString := Col;
442 Inc(curcol);
443 except
444 on E: Exception do begin
445 if not (FEOF and (curcol = Params.Count)) then
446 raise;
447 end;
448 end;
449 end;
450 until (FEOF) or (i = 2);
451 result := ((FEOF) and (curcol = Params.Count)) or
452 (not FEOF);
453 end;
454 end;
455
456 procedure TIBInputDelimitedFile.ReadyFile;
457 begin
458 if FColDelimiter = '' then
459 FColDelimiter := TAB;
460 if FRowDelimiter = '' then
461 FRowDelimiter := CRLF;
462 FLookAhead := NULL_TERMINATOR;
463 FEOF := False;
464 if FFile <> nil then
465 FFile.Free;
466 FFile := TFileStream.Create(FFilename, fmOpenRead or fmShareDenyWrite);
467 if FSkipTitles then
468 ReadParameters;
469 end;
470
471 { TIBOutputRawFile }
472 destructor TIBOutputRawFile.Destroy;
473 begin
474 {$IFDEF UNIX}
475 if FHandle <> -1 then
476 fpclose(FHandle);
477 {$ELSE}
478 if FHandle <> 0 then
479 begin
480 FlushFileBuffers(FHandle);
481 CloseHandle(FHandle);
482 end;
483 {$ENDIF}
484 inherited Destroy;
485 end;
486
487 procedure TIBOutputRawFile.ReadyFile;
488 begin
489 {$IFDEF UNIX}
490 FHandle := FpOpen(Filename,O_WrOnly or O_Creat);
491 {$ELSE}
492 FHandle := CreateFile(PChar(Filename), GENERIC_WRITE, 0, nil, CREATE_ALWAYS,
493 FILE_ATTRIBUTE_NORMAL, 0);
494 if FHandle = INVALID_HANDLE_VALUE then
495 FHandle := 0;
496 {$ENDIF}
497 end;
498
499 function TIBOutputRawFile.WriteColumns: Boolean;
500 var
501 i: Integer;
502 BytesWritten: DWord;
503 begin
504 result := False;
505 if FHandle <> 0 then
506 begin
507 for i := 0 to Columns.Count - 1 do
508 begin
509 {$IFDEF UNIX}
510 BytesWritten := FpWrite(FHandle,Columns[i].GetAsPointer^, Columns[i].GetSize);
511 {$ELSE}
512 WriteFile(FHandle, Columns[i].GetAsPointer^, Columns[i].GetSize,
513 BytesWritten, nil);
514 {$ENDIF}
515 if BytesWritten <> DWORD(Columns[i].GetSize) then
516 exit;
517 end;
518 result := True;
519 end;
520 end;
521
522 { TIBInputRawFile }
523 destructor TIBInputRawFile.Destroy;
524 begin
525 {$IFDEF UNIX}
526 if FHandle <> -1 then
527 fpclose(FHandle);
528 {$ELSE}
529 if FHandle <> 0 then
530 CloseHandle(FHandle);
531 {$ENDIF}
532 inherited Destroy;
533 end;
534
535 function TIBInputRawFile.ReadParameters: Boolean;
536 var
537 i: Integer;
538 BytesRead: DWord;
539 begin
540 result := False;
541 {$IFDEF UNIX}
542 if FHandle <> -1 then
543 {$ELSE}
544 if FHandle <> 0 then
545 {$ENDIF}
546 begin
547 for i := 0 to Params.Count - 1 do
548 begin
549 {$IFDEF UNIX}
550 BytesRead := FpRead(FHandle,Params[i].GetAsPointer^,Params[i].GetSize);
551 {$ELSE}
552 ReadFile(FHandle, Params[i].GetAsPointer^, Params[i].GetSize,
553 BytesRead, nil);
554 {$ENDIF}
555 if BytesRead <> DWORD(Params[i].GetSize) then
556 exit;
557 end;
558 result := True;
559 end;
560 end;
561
562 procedure TIBInputRawFile.ReadyFile;
563 begin
564 {$IFDEF UNIX}
565 if FHandle <> -1 then
566 fpclose(FHandle);
567 FHandle := FpOpen(Filename,O_RdOnly);
568 if FHandle = -1 then
569 raise Exception.CreateFmt('Unable to open file %s',[Filename]);
570 {$ELSE}
571 if FHandle <> 0 then
572 CloseHandle(FHandle);
573 FHandle := CreateFile(PChar(Filename), GENERIC_READ, 0, nil, OPEN_EXISTING,
574 FILE_FLAG_SEQUENTIAL_SCAN, 0);
575 if FHandle = INVALID_HANDLE_VALUE then
576 FHandle := 0;
577 {$ENDIF}
578 end;
579
580 { TIBSQL }
581 constructor TIBSQL.Create(AOwner: TComponent);
582 begin
583 inherited Create(AOwner);
584 FGenerateParamNames := False;
585 FGoToFirstRecordOnExecute := True;
586 FBase := TIBBase.Create(Self);
587 FBase.BeforeDatabaseDisconnect := DoBeforeDatabaseDisconnect;
588 FBase.BeforeTransactionEnd := BeforeTransactionEnd;
589 FRecordCount := 0;
590 FSQL := TStringList.Create;
591 TStringList(FSQL).OnChanging := SQLChanging;
592 TStringList(FSQL).OnChange := SQLChanged;
593 FParamCheck := True;
594 if AOwner is TIBDatabase then
595 Database := TIBDatabase(AOwner)
596 else
597 if AOwner is TIBTransaction then
598 Transaction := TIBTransaction(AOwner);
599 end;
600
601 destructor TIBSQL.Destroy;
602 begin
603 FreeHandle;
604 FSQL.Free;
605 FBase.Free;
606 inherited Destroy;
607 end;
608
609 procedure TIBSQL.BatchInput(InputObject: TIBBatchInput);
610 begin
611 if not Prepared then
612 Prepare;
613 InputObject.FParams := Self.GetSQLParams;
614 InputObject.ReadyFile;
615 if GetSQLStatementType in [SQLInsert, SQLUpdate, SQLDelete, SQLExecProcedure] then
616 while InputObject.ReadParameters do
617 ExecQuery;
618 end;
619
620 procedure TIBSQL.BatchOutput(OutputObject: TIBBatchOutput);
621 begin
622 CheckClosed;
623 if not Prepared then
624 Prepare;
625 if GetSQLStatementType = SQLSelect then begin
626 try
627 ExecQuery;
628 OutputObject.FColumns := Self.FResults;
629 OutputObject.ReadyFile;
630 if not FGoToFirstRecordOnExecute then
631 Next;
632 while (not Eof) and (OutputObject.WriteColumns) do
633 Next;
634 finally
635 Close;
636 end;
637 end;
638 end;
639
640 procedure TIBSQL.CheckClosed;
641 begin
642 if FResultSet <> nil then IBError(ibxeSQLOpen, [nil]);
643 end;
644
645 procedure TIBSQL.CheckOpen;
646 begin
647 if FResultSet = nil then IBError(ibxeSQLClosed, [nil]);
648 end;
649
650 procedure TIBSQL.CheckValidStatement;
651 begin
652 FBase.CheckTransaction;
653 if (FStatement = nil) then
654 IBError(ibxeInvalidStatementHandle, [nil]);
655 end;
656
657 procedure TIBSQL.Close;
658 begin
659 if FResults <> nil then
660 FResults.SetRetainInterfaces(false);
661 FResultSet := nil;
662 FResults := nil;
663 FBOF := false;
664 FEOF := false;
665 FRecordCount := 0;
666 end;
667
668 function TIBSQL.GetFieldCount: integer;
669 begin
670 if FResults <> nil then
671 Result := FResults.GetCount
672 else
673 if FMetaData <> nil then
674 Result := FMetaData.GetCount
675 else
676 Result := 0;
677 end;
678
679 function TIBSQL.GetOpen: Boolean;
680 begin
681 Result := FResultSet <> nil;
682 end;
683
684 function TIBSQL.GetPrepared: Boolean;
685 begin
686 Result := (FStatement <> nil) and FStatement.IsPrepared;
687 end;
688
689 function TIBSQL.GetSQLStatementType: TIBSQLStatementTypes;
690 begin
691 if FStatement = nil then
692 Result := SQLUnknown
693 else
694 Result := FStatement.GetSQLStatementType;
695 end;
696
697 procedure TIBSQL.SetUniqueParamNames(AValue: Boolean);
698 begin
699 if FUniqueParamNames = AValue then Exit;
700 FreeHandle;
701 FUniqueParamNames := AValue;
702 end;
703
704 procedure TIBSQL.DoBeforeDatabaseDisconnect(Sender: TObject);
705 begin
706 FreeHandle;
707 end;
708
709 procedure TIBSQL.ExecQuery;
710 {$IFDEF IBXQUERYSTATS}
711 var
712 stats: TPerfCounters;
713 {$ENDIF}
714 {$IFDEF IBXQUERYTIME}
715 var
716 tmsecs: comp;
717 {$ENDIF}
718 begin
719 CheckClosed;
720 if not Prepared then Prepare;
721 CheckValidStatement;
722 {$IFDEF IBXQUERYTIME}
723 tmsecs := TimeStampToMSecs(DateTimeToTimeStamp(Now));
724 {$ENDIF}
725 if SQLStatementType = SQLSelect then
726 begin
727 FResultSet := FStatement.OpenCursor;
728 FResults := FResultSet;
729 FResults.SetRetainInterfaces(true);
730 FBOF := True;
731 FEOF := False;
732 FRecordCount := 0;
733 if not (csDesigning in ComponentState) then
734 MonitorHook.SQLExecute(Self);
735 if FGoToFirstRecordOnExecute then
736 Next;
737 end
738 else
739 begin
740 FResults := FStatement.Execute;
741 if not (csDesigning in ComponentState) then
742 MonitorHook.SQLExecute(Self);
743 end;
744 {$IFDEF IBXQUERYTIME}
745 writeln('Executing ',FStatement.GetSQLText,
746 ' Response time= ',Format('%f msecs',[TimeStampToMSecs(DateTimeToTimeStamp(Now)) - tmsecs]));
747 {$ENDIF}
748 {$IFDEF IBXQUERYSTATS}
749 if FStatement.GetPerfStatistics(stats) then
750 writeln('Executing ',FStatement.GetSQLText,
751 ' Elapsed time= ', FormatFloat('#0.000',stats[psRealTime]/1000),' sec');
752 {$ENDIF}
753 FBase.DoAfterExecQuery(self);
754 end;
755
756 function TIBSQL.HasField(FieldName: String): boolean;
757 var i: integer;
758 begin
759 if MetaData = nil then
760 IBError(ibxeNoFieldAccess,[nil]);
761
762 Result := false;
763 for i := 0 to MetaData.Count - 1 do
764 begin
765 if MetaData.ColMetaData[i].Name = FieldName then
766 begin
767 Result := true;
768 Exit;
769 end;
770 end;
771 end;
772
773 function TIBSQL.GetEOF: Boolean;
774 begin
775 result := FEOF or (FResultSet = nil);
776 end;
777
778 function TIBSQL.FieldByName(FieldName: String): ISQLData;
779 begin
780 if FResults = nil then
781 IBError(ibxeNoFieldAccess,[nil]);
782
783 Result := FResults.ByName(FieldName);
784
785 if Result = nil then
786 IBError(ibxeFieldNotFound, [FieldName]);
787 end;
788
789 function TIBSQL.ParamByName(ParamName: String): ISQLParam;
790 begin
791 Result := Params.ByName(ParamName);
792 end;
793
794 function TIBSQL.GetFields(const Idx: Integer): ISQLData;
795 begin
796 if FResults = nil then
797 IBError(ibxeNoFieldAccess,[nil]);
798
799 if (Idx < 0) or (Idx >= FResults.GetCount) then
800 IBError(ibxeFieldNotFound, [IntToStr(Idx)]);
801 result := FResults[Idx];
802 end;
803
804 function TIBSQL.GetFieldIndex(FieldName: String): Integer;
805 var Field: IColumnMetaData;
806 begin
807 if FMetaData = nil then
808 IBError(ibxeNoFieldAccess,[nil]);
809
810 Field := FMetaData.ByName(FieldName);
811
812 if Field = nil then
813 result := -1
814 else
815 result := Field.GetIndex;
816 end;
817
818 function TIBSQL.Next: boolean;
819 begin
820 result := false;
821 if not FEOF then
822 begin
823 CheckOpen;
824 try
825 Result := FResultSet.FetchNext;
826 except
827 Close;
828 raise;
829 end;
830
831 if Result then
832 begin
833 Inc(FRecordCount);
834 FBOF := False;
835 end
836 else
837 FEOF := true;
838
839 if not (csDesigning in ComponentState) then
840 MonitorHook.SQLFetch(Self);
841 end;
842 end;
843
844 procedure TIBSQL.FreeHandle;
845 begin
846 if FStatement <> nil then
847 FStatement.SetRetainInterfaces(false);
848 Close;
849 FStatement := nil;
850 FResults := nil;
851 FResultSet := nil;
852 FMetaData := nil;
853 FSQLParams := nil;
854 end;
855
856 function TIBSQL.GetDatabase: TIBDatabase;
857 begin
858 result := FBase.Database;
859 end;
860
861 function TIBSQL.GetPlan: String;
862 begin
863 if (not Prepared) or
864 (not (GetSQLStatementType in [SQLSelect, SQLSelectForUpdate,
865 {TODO: SQLExecProcedure, }
866 SQLUpdate, SQLDelete])) then
867 result := ''
868 else
869 Result := FStatement.GetPlan;
870 end;
871
872 function TIBSQL.GetRecordCount: Integer;
873 begin
874 Result := FRecordCount;
875 end;
876
877 function TIBSQL.GetRowsAffected: Int64;
878 var
879 SelectCount, InsertCount, UpdateCount, DeleteCount: integer;
880 begin
881 if not Prepared then
882 Result := -1
883 else
884 begin
885 FStatement.GetRowsAffected(SelectCount, InsertCount, UpdateCount, DeleteCount);
886 Result := InsertCount + UpdateCount + DeleteCount;
887 end;
888 end;
889
890 function TIBSQL.GetSQLParams: ISQLParams;
891 begin
892 if not Prepared then
893 Prepare;
894 result := Statement.SQLParams;
895 end;
896
897 function TIBSQL.GetTransaction: TIBTransaction;
898 begin
899 result := FBase.Transaction;
900 end;
901
902 procedure TIBSQL.SetDatabase(Value: TIBDatabase);
903 begin
904 if Value = FBase.Database then Exit;
905 FBase.Database := Value;
906 FreeHandle;
907 end;
908
909 procedure TIBSQL.Prepare;
910 begin
911 CheckClosed;
912 FBase.CheckDatabase;
913 FBase.CheckTransaction;
914 Close;
915 if Prepared then
916 exit;
917 if (FSQL.Text = '') then
918 IBError(ibxeEmptyQuery, [nil]);
919
920 if FStatement <> nil then
921 FStatement.Prepare(Transaction.TransactionIntf)
922 else
923 if not ParamCheck then
924 FStatement := Database.Attachment.Prepare(Transaction.TransactionIntf,SQL.Text)
925 else
926 FStatement := Database.Attachment.PrepareWithNamedParameters(
927 Transaction.TransactionIntf,
928 SQL.Text,
929 GenerateParamNames,
930 CaseSensitiveParameterNames);
931 {$IFDEF IBXQUERYSTATS}
932 FStatement.EnableStatistics(true);
933 {$ENDIF}
934 FMetaData := FStatement.GetMetaData;
935 FSQLParams := FStatement.GetSQLParams;
936 FStatement.SetRetainInterfaces(true);
937 if not (csDesigning in ComponentState) then
938 MonitorHook.SQLPrepare(Self);
939 end;
940
941 function TIBSQL.GetUniqueRelationName: String;
942 begin
943 if Prepared and (GetSQLStatementType = SQLSelect) then
944 result := FMetaData.GetUniqueRelationName
945 else
946 result := '';
947 end;
948
949 function TIBSQL.HasBatchMode: boolean;
950 begin
951 CheckValidStatement;
952 Result := Statement.HasBatchMode;
953 end;
954
955 function TIBSQL.IsInBatchMode: boolean;
956 begin
957 CheckValidStatement;
958 Result := Statement.IsInBatchMode;
959 end;
960
961 procedure TIBSQL.AddToBatch;
962 begin
963 CheckValidStatement;
964 Statement.AddToBatch;
965 end;
966
967 function TIBSQL.ExecuteBatch: IBatchCompletion;
968 begin
969 CheckValidStatement;
970 Result := Statement.ExecuteBatch;
971 end;
972
973 procedure TIBSQL.CancelBatch;
974 begin
975 CheckValidStatement;
976 Statement.CancelBatch;
977 end;
978
979 function TIBSQL.GetBatchCompletion: IBatchCompletion;
980 begin
981 CheckValidStatement;
982 Result := Statement.GetBatchCompletion;
983 end;
984
985 function TIBSQL.GetBatchRowLimit: integer;
986 begin
987 CheckValidStatement;
988 Result := Statement.GetBatchRowLimit;
989 end;
990
991 procedure TIBSQL.SetBatchRowLimit(aLimit: integer);
992 begin
993 CheckValidStatement;
994 Statement.SetBatchRowLimit(aLimit);
995 end;
996
997 procedure TIBSQL.SetSQL(Value: TStrings);
998 begin
999 if FSQL.Text <> Value.Text then
1000 begin
1001 FSQL.BeginUpdate;
1002 try
1003 FSQL.Assign(Value);
1004 finally
1005 FSQL.EndUpdate;
1006 end;
1007 end;
1008 end;
1009
1010 procedure TIBSQL.SetTransaction(Value: TIBTransaction);
1011 begin
1012 if FBase.Transaction = Value then Exit;
1013 FreeHandle;
1014 FBase.Transaction := Value;
1015 end;
1016
1017 procedure TIBSQL.SQLChanging(Sender: TObject);
1018 begin
1019 if Assigned(OnSQLChanging) then
1020 OnSQLChanging(Self);
1021
1022 FreeHandle;
1023 end;
1024
1025 procedure TIBSQL.SQLChanged(Sender: TObject);
1026 begin
1027 if assigned(OnSQLChanged) then
1028 OnSQLChanged(self);
1029 end;
1030
1031 procedure TIBSQL.BeforeTransactionEnd(Sender: TObject;
1032 Action: TTransactionAction);
1033 begin
1034 if not (Owner is TIBCustomDataSet) then
1035 FreeHandle;
1036 end;
1037
1038 end.