ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/nongui/IBSQL.pas
Revision: 291
Committed: Fri Apr 17 10:26:08 2020 UTC (4 years ago) by tony
Content type: text/x-pascal
File size: 26021 byte(s)
Log Message:
Changed for 2.3.4 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: Integer;
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: Integer 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 published
234 property Database: TIBDatabase read GetDatabase write SetDatabase;
235 property CaseSensitiveParameterNames: boolean read FCaseSensitiveParameterNames
236 write FCaseSensitiveParameterNames;
237 property GenerateParamNames: Boolean read FGenerateParamNames write FGenerateParamNames;
238 property UniqueParamNames: Boolean read FUniqueParamNames write SetUniqueParamNames;
239 property GoToFirstRecordOnExecute: Boolean read FGoToFirstRecordOnExecute
240 write FGoToFirstRecordOnExecute
241 default True;
242 property ParamCheck: Boolean read FParamCheck write FParamCheck;
243 property SQL: TStrings read FSQL write SetSQL;
244 property Transaction: TIBTransaction read GetTransaction write SetTransaction;
245 property OnSQLChanging: TNotifyEvent read FOnSQLChanging write FOnSQLChanging;
246 property OnSQLChanged: TNotifyEvent read FOnSQLChanged write FOnSQLChanged;
247 end;
248
249 procedure IBAlloc(var P; OldSize, NewSize: Integer);
250
251 implementation
252
253 uses
254 Variants, IBSQLMonitor, IBMessages, IBCustomDataSet;
255
256 procedure IBAlloc(var P; OldSize, NewSize: Integer);
257 var
258 i: Integer;
259 begin
260 ReallocMem(Pointer(P), NewSize);
261 for i := OldSize to NewSize - 1 do PChar(P)[i] := #0;
262 end;
263
264 { TIBOutputDelimitedFile }
265
266 destructor TIBOutputDelimitedFile.Destroy;
267 begin
268 {$IFDEF UNIX}
269 if FHandle <> -1 then
270 fpclose(FHandle);
271 {$ELSE}
272 if FHandle <> 0 then
273 begin
274 FlushFileBuffers(FHandle);
275 CloseHandle(FHandle);
276 end;
277 {$ENDIF}
278 inherited Destroy;
279 end;
280
281 procedure TIBOutputDelimitedFile.ReadyFile;
282 var
283 i: Integer;
284 {$IFDEF UNIX}
285 BytesWritten: cint;
286 {$ELSE}
287 BytesWritten: DWORD;
288 {$ENDIF}
289 st: string;
290 begin
291 if FColDelimiter = '' then
292 FColDelimiter := TAB;
293 if FRowDelimiter = '' then
294 FRowDelimiter := CRLF;
295 {$IFDEF UNIX}
296 FHandle := FpOpen(Filename,O_WrOnly or O_Creat);
297 {$ELSE}
298 FHandle := CreateFile(PChar(Filename), GENERIC_WRITE, 0, nil, CREATE_ALWAYS,
299 FILE_ATTRIBUTE_NORMAL, 0);
300 if FHandle = INVALID_HANDLE_VALUE then
301 FHandle := 0;
302 {$ENDIF}
303 if FOutputTitles then
304 begin
305 for i := 0 to Columns.Count - 1 do
306 if i = 0 then
307 st := Columns[i].GetAliasname
308 else
309 st := st + FColDelimiter + Columns[i].GetAliasname;
310 st := st + FRowDelimiter;
311 {$IFDEF UNIX}
312 if FHandle <> -1 then
313 BytesWritten := FpWrite(FHandle,st[1],Length(st));
314 if BytesWritten = -1 then
315 raise Exception.Create('File Write Error');
316 {$ELSE}
317 WriteFile(FHandle, st[1], Length(st), BytesWritten, nil);
318 {$ENDIF}
319 end;
320 end;
321
322 function TIBOutputDelimitedFile.WriteColumns: Boolean;
323 var
324 i: Integer;
325 {$IFDEF UNIX}
326 BytesWritten: cint;
327 {$ELSE}
328 BytesWritten: DWORD;
329 {$ENDIF}
330 st: string;
331 begin
332 result := False;
333 {$IFDEF UNIX}
334 if FHandle <> -1 then
335 {$ELSE}
336 if FHandle <> 0 then
337 {$ENDIF}
338 begin
339 st := '';
340 for i := 0 to Columns.Count - 1 do
341 begin
342 if i > 0 then
343 st := st + FColDelimiter;
344 st := st + StripString(Columns[i].AsString, FColDelimiter + FRowDelimiter);
345 end;
346 st := st + FRowDelimiter;
347 {$IFDEF UNIX}
348 BytesWritten := FpWrite(FHandle,st[1],Length(st));
349 {$ELSE}
350 WriteFile(FHandle, st[1], Length(st), BytesWritten, nil);
351 {$ENDIF}
352 if BytesWritten = DWORD(Length(st)) then
353 result := True;
354 end
355 end;
356
357 { TIBInputDelimitedFile }
358
359 destructor TIBInputDelimitedFile.Destroy;
360 begin
361 FFile.Free;
362 inherited Destroy;
363 end;
364
365 function TIBInputDelimitedFile.GetColumn(var Col: string): Integer;
366 var
367 c: Char;
368 BytesRead: Integer;
369
370 procedure ReadInput;
371 begin
372 if FLookAhead <> NULL_TERMINATOR then
373 begin
374 c := FLookAhead;
375 BytesRead := 1;
376 FLookAhead := NULL_TERMINATOR;
377 end else
378 BytesRead := FFile.Read(c, 1);
379 end;
380
381 procedure CheckCRLF(Delimiter: string);
382 begin
383 if (c = CR) and (Pos(LF, Delimiter) > 0) then {mbcs ok}
384 begin
385 BytesRead := FFile.Read(c, 1);
386 if (BytesRead = 1) and (c <> #10) then
387 FLookAhead := c
388 end;
389 end;
390
391 begin
392 Col := '';
393 result := 0;
394 ReadInput;
395 while BytesRead <> 0 do begin
396 if Pos(c, FColDelimiter) > 0 then {mbcs ok}
397 begin
398 CheckCRLF(FColDelimiter);
399 result := 1;
400 break;
401 end else if Pos(c, FRowDelimiter) > 0 then {mbcs ok}
402 begin
403 CheckCRLF(FRowDelimiter);
404 result := 2;
405 break;
406 end else
407 Col := Col + c;
408 ReadInput;
409 end;
410 end;
411
412 function TIBInputDelimitedFile.ReadParameters: Boolean;
413 var
414 i, curcol: Integer;
415 Col: string;
416 begin
417 result := False;
418 if not FEOF then begin
419 curcol := 0;
420 repeat
421 i := GetColumn(Col);
422 if (i = 0) then
423 FEOF := True;
424 if (curcol < Params.Count) then
425 begin
426 try
427 if (Col = '') and
428 (ReadBlanksAsNull) then
429 Params[curcol].IsNull := True
430 else
431 Params[curcol].AsString := Col;
432 Inc(curcol);
433 except
434 on E: Exception do begin
435 if not (FEOF and (curcol = Params.Count)) then
436 raise;
437 end;
438 end;
439 end;
440 until (FEOF) or (i = 2);
441 result := ((FEOF) and (curcol = Params.Count)) or
442 (not FEOF);
443 end;
444 end;
445
446 procedure TIBInputDelimitedFile.ReadyFile;
447 begin
448 if FColDelimiter = '' then
449 FColDelimiter := TAB;
450 if FRowDelimiter = '' then
451 FRowDelimiter := CRLF;
452 FLookAhead := NULL_TERMINATOR;
453 FEOF := False;
454 if FFile <> nil then
455 FFile.Free;
456 FFile := TFileStream.Create(FFilename, fmOpenRead or fmShareDenyWrite);
457 if FSkipTitles then
458 ReadParameters;
459 end;
460
461 { TIBOutputRawFile }
462 destructor TIBOutputRawFile.Destroy;
463 begin
464 {$IFDEF UNIX}
465 if FHandle <> -1 then
466 fpclose(FHandle);
467 {$ELSE}
468 if FHandle <> 0 then
469 begin
470 FlushFileBuffers(FHandle);
471 CloseHandle(FHandle);
472 end;
473 {$ENDIF}
474 inherited Destroy;
475 end;
476
477 procedure TIBOutputRawFile.ReadyFile;
478 begin
479 {$IFDEF UNIX}
480 FHandle := FpOpen(Filename,O_WrOnly or O_Creat);
481 {$ELSE}
482 FHandle := CreateFile(PChar(Filename), GENERIC_WRITE, 0, nil, CREATE_ALWAYS,
483 FILE_ATTRIBUTE_NORMAL, 0);
484 if FHandle = INVALID_HANDLE_VALUE then
485 FHandle := 0;
486 {$ENDIF}
487 end;
488
489 function TIBOutputRawFile.WriteColumns: Boolean;
490 var
491 i: Integer;
492 BytesWritten: DWord;
493 begin
494 result := False;
495 if FHandle <> 0 then
496 begin
497 for i := 0 to Columns.Count - 1 do
498 begin
499 {$IFDEF UNIX}
500 BytesWritten := FpWrite(FHandle,Columns[i].GetAsPointer^, Columns[i].GetSize);
501 {$ELSE}
502 WriteFile(FHandle, Columns[i].GetAsPointer^, Columns[i].GetSize,
503 BytesWritten, nil);
504 {$ENDIF}
505 if BytesWritten <> DWORD(Columns[i].GetSize) then
506 exit;
507 end;
508 result := True;
509 end;
510 end;
511
512 { TIBInputRawFile }
513 destructor TIBInputRawFile.Destroy;
514 begin
515 {$IFDEF UNIX}
516 if FHandle <> -1 then
517 fpclose(FHandle);
518 {$ELSE}
519 if FHandle <> 0 then
520 CloseHandle(FHandle);
521 {$ENDIF}
522 inherited Destroy;
523 end;
524
525 function TIBInputRawFile.ReadParameters: Boolean;
526 var
527 i: Integer;
528 BytesRead: DWord;
529 begin
530 result := False;
531 {$IFDEF UNIX}
532 if FHandle <> -1 then
533 {$ELSE}
534 if FHandle <> 0 then
535 {$ENDIF}
536 begin
537 for i := 0 to Params.Count - 1 do
538 begin
539 {$IFDEF UNIX}
540 BytesRead := FpRead(FHandle,Params[i].GetAsPointer^,Params[i].GetSize);
541 {$ELSE}
542 ReadFile(FHandle, Params[i].GetAsPointer^, Params[i].GetSize,
543 BytesRead, nil);
544 {$ENDIF}
545 if BytesRead <> DWORD(Params[i].GetSize) then
546 exit;
547 end;
548 result := True;
549 end;
550 end;
551
552 procedure TIBInputRawFile.ReadyFile;
553 begin
554 {$IFDEF UNIX}
555 if FHandle <> -1 then
556 fpclose(FHandle);
557 FHandle := FpOpen(Filename,O_RdOnly);
558 if FHandle = -1 then
559 raise Exception.CreateFmt('Unable to open file %s',[Filename]);
560 {$ELSE}
561 if FHandle <> 0 then
562 CloseHandle(FHandle);
563 FHandle := CreateFile(PChar(Filename), GENERIC_READ, 0, nil, OPEN_EXISTING,
564 FILE_FLAG_SEQUENTIAL_SCAN, 0);
565 if FHandle = INVALID_HANDLE_VALUE then
566 FHandle := 0;
567 {$ENDIF}
568 end;
569
570 { TIBSQL }
571 constructor TIBSQL.Create(AOwner: TComponent);
572 begin
573 inherited Create(AOwner);
574 FGenerateParamNames := False;
575 FGoToFirstRecordOnExecute := True;
576 FBase := TIBBase.Create(Self);
577 FBase.BeforeDatabaseDisconnect := DoBeforeDatabaseDisconnect;
578 FBase.BeforeTransactionEnd := BeforeTransactionEnd;
579 FRecordCount := 0;
580 FSQL := TStringList.Create;
581 TStringList(FSQL).OnChanging := SQLChanging;
582 TStringList(FSQL).OnChange := SQLChanged;
583 FParamCheck := True;
584 if AOwner is TIBDatabase then
585 Database := TIBDatabase(AOwner)
586 else
587 if AOwner is TIBTransaction then
588 Transaction := TIBTransaction(AOwner);
589 end;
590
591 destructor TIBSQL.Destroy;
592 begin
593 FreeHandle;
594 FSQL.Free;
595 FBase.Free;
596 inherited Destroy;
597 end;
598
599 procedure TIBSQL.BatchInput(InputObject: TIBBatchInput);
600 begin
601 if not Prepared then
602 Prepare;
603 InputObject.FParams := Self.GetSQLParams;
604 InputObject.ReadyFile;
605 if GetSQLStatementType in [SQLInsert, SQLUpdate, SQLDelete, SQLExecProcedure] then
606 while InputObject.ReadParameters do
607 ExecQuery;
608 end;
609
610 procedure TIBSQL.BatchOutput(OutputObject: TIBBatchOutput);
611 begin
612 CheckClosed;
613 if not Prepared then
614 Prepare;
615 if GetSQLStatementType = SQLSelect then begin
616 try
617 ExecQuery;
618 OutputObject.FColumns := Self.FResults;
619 OutputObject.ReadyFile;
620 if not FGoToFirstRecordOnExecute then
621 Next;
622 while (not Eof) and (OutputObject.WriteColumns) do
623 Next;
624 finally
625 Close;
626 end;
627 end;
628 end;
629
630 procedure TIBSQL.CheckClosed;
631 begin
632 if FResultSet <> nil then IBError(ibxeSQLOpen, [nil]);
633 end;
634
635 procedure TIBSQL.CheckOpen;
636 begin
637 if FResultSet = nil then IBError(ibxeSQLClosed, [nil]);
638 end;
639
640 procedure TIBSQL.CheckValidStatement;
641 begin
642 FBase.CheckTransaction;
643 if (FStatement = nil) then
644 IBError(ibxeInvalidStatementHandle, [nil]);
645 end;
646
647 procedure TIBSQL.Close;
648 begin
649 if FResults <> nil then
650 FResults.SetRetainInterfaces(false);
651 FResultSet := nil;
652 FResults := nil;
653 FBOF := false;
654 FEOF := false;
655 FRecordCount := 0;
656 end;
657
658 function TIBSQL.GetFieldCount: integer;
659 begin
660 if FResults <> nil then
661 Result := FResults.GetCount
662 else
663 if FMetaData <> nil then
664 Result := FMetaData.GetCount
665 else
666 Result := 0;
667 end;
668
669 function TIBSQL.GetOpen: Boolean;
670 begin
671 Result := FResultSet <> nil;
672 end;
673
674 function TIBSQL.GetPrepared: Boolean;
675 begin
676 Result := (FStatement <> nil) and FStatement.IsPrepared;
677 end;
678
679 function TIBSQL.GetSQLStatementType: TIBSQLStatementTypes;
680 begin
681 if FStatement = nil then
682 Result := SQLUnknown
683 else
684 Result := FStatement.GetSQLStatementType;
685 end;
686
687 procedure TIBSQL.SetUniqueParamNames(AValue: Boolean);
688 begin
689 if FUniqueParamNames = AValue then Exit;
690 FreeHandle;
691 FUniqueParamNames := AValue;
692 end;
693
694 procedure TIBSQL.DoBeforeDatabaseDisconnect(Sender: TObject);
695 begin
696 FreeHandle;
697 end;
698
699 procedure TIBSQL.ExecQuery;
700 {$IFDEF IBXQUERYSTATS}
701 var
702 stats: TPerfCounters;
703 {$ENDIF}
704 {$IFDEF IBXQUERYTIME}
705 var
706 tmsecs: comp;
707 {$ENDIF}
708 begin
709 CheckClosed;
710 if not Prepared then Prepare;
711 CheckValidStatement;
712 {$IFDEF IBXQUERYTIME}
713 tmsecs := TimeStampToMSecs(DateTimeToTimeStamp(Now));
714 {$ENDIF}
715 if SQLStatementType = SQLSelect then
716 begin
717 FResultSet := FStatement.OpenCursor;
718 FResults := FResultSet;
719 FResults.SetRetainInterfaces(true);
720 FBOF := True;
721 FEOF := False;
722 FRecordCount := 0;
723 if not (csDesigning in ComponentState) then
724 MonitorHook.SQLExecute(Self);
725 if FGoToFirstRecordOnExecute then
726 Next;
727 end
728 else
729 begin
730 FResults := FStatement.Execute;
731 if not (csDesigning in ComponentState) then
732 MonitorHook.SQLExecute(Self);
733 end;
734 {$IFDEF IBXQUERYTIME}
735 writeln('Executing ',FStatement.GetSQLText,
736 ' Response time= ',Format('%f msecs',[TimeStampToMSecs(DateTimeToTimeStamp(Now)) - tmsecs]));
737 {$ENDIF}
738 {$IFDEF IBXQUERYSTATS}
739 if FStatement.GetPerfStatistics(stats) then
740 writeln('Executing ',FStatement.GetSQLText,
741 ' Elapsed time= ', FormatFloat('#0.000',stats[psRealTime]/1000),' sec');
742 {$ENDIF}
743 FBase.DoAfterExecQuery(self);
744 end;
745
746 function TIBSQL.HasField(FieldName: String): boolean;
747 var i: integer;
748 begin
749 if MetaData = nil then
750 IBError(ibxeNoFieldAccess,[nil]);
751
752 Result := false;
753 for i := 0 to MetaData.Count - 1 do
754 begin
755 if MetaData.ColMetaData[i].Name = FieldName then
756 begin
757 Result := true;
758 Exit;
759 end;
760 end;
761 end;
762
763 function TIBSQL.GetEOF: Boolean;
764 begin
765 result := FEOF or (FResultSet = nil);
766 end;
767
768 function TIBSQL.FieldByName(FieldName: String): ISQLData;
769 begin
770 if FResults = nil then
771 IBError(ibxeNoFieldAccess,[nil]);
772
773 Result := FResults.ByName(FieldName);
774
775 if Result = nil then
776 IBError(ibxeFieldNotFound, [FieldName]);
777 end;
778
779 function TIBSQL.ParamByName(ParamName: String): ISQLParam;
780 begin
781 Result := Params.ByName(ParamName);
782 end;
783
784 function TIBSQL.GetFields(const Idx: Integer): ISQLData;
785 begin
786 if FResults = nil then
787 IBError(ibxeNoFieldAccess,[nil]);
788
789 if (Idx < 0) or (Idx >= FResults.GetCount) then
790 IBError(ibxeFieldNotFound, [IntToStr(Idx)]);
791 result := FResults[Idx];
792 end;
793
794 function TIBSQL.GetFieldIndex(FieldName: String): Integer;
795 var Field: IColumnMetaData;
796 begin
797 if FMetaData = nil then
798 IBError(ibxeNoFieldAccess,[nil]);
799
800 Field := FMetaData.ByName(FieldName);
801
802 if Field = nil then
803 result := -1
804 else
805 result := Field.GetIndex;
806 end;
807
808 function TIBSQL.Next: boolean;
809 begin
810 result := false;
811 if not FEOF then
812 begin
813 CheckOpen;
814 try
815 Result := FResultSet.FetchNext;
816 except
817 Close;
818 raise;
819 end;
820
821 if Result then
822 begin
823 Inc(FRecordCount);
824 FBOF := False;
825 end
826 else
827 FEOF := true;
828
829 if not (csDesigning in ComponentState) then
830 MonitorHook.SQLFetch(Self);
831 end;
832 end;
833
834 procedure TIBSQL.FreeHandle;
835 begin
836 if FStatement <> nil then
837 FStatement.SetRetainInterfaces(false);
838 Close;
839 FStatement := nil;
840 FResults := nil;
841 FResultSet := nil;
842 FMetaData := nil;
843 FSQLParams := nil;
844 end;
845
846 function TIBSQL.GetDatabase: TIBDatabase;
847 begin
848 result := FBase.Database;
849 end;
850
851 function TIBSQL.GetPlan: String;
852 begin
853 if (not Prepared) or
854 (not (GetSQLStatementType in [SQLSelect, SQLSelectForUpdate,
855 {TODO: SQLExecProcedure, }
856 SQLUpdate, SQLDelete])) then
857 result := ''
858 else
859 Result := FStatement.GetPlan;
860 end;
861
862 function TIBSQL.GetRecordCount: Integer;
863 begin
864 Result := FRecordCount;
865 end;
866
867 function TIBSQL.GetRowsAffected: Integer;
868 var
869 SelectCount, InsertCount, UpdateCount, DeleteCount: integer;
870 begin
871 if not Prepared then
872 Result := -1
873 else
874 begin
875 FStatement.GetRowsAffected(SelectCount, InsertCount, UpdateCount, DeleteCount);
876 Result := InsertCount + UpdateCount + DeleteCount;
877 end;
878 end;
879
880 function TIBSQL.GetSQLParams: ISQLParams;
881 begin
882 if not Prepared then
883 Prepare;
884 result := Statement.SQLParams;
885 end;
886
887 function TIBSQL.GetTransaction: TIBTransaction;
888 begin
889 result := FBase.Transaction;
890 end;
891
892 procedure TIBSQL.SetDatabase(Value: TIBDatabase);
893 begin
894 if Value = FBase.Database then Exit;
895 FBase.Database := Value;
896 FreeHandle;
897 end;
898
899 procedure TIBSQL.Prepare;
900 begin
901 CheckClosed;
902 FBase.CheckDatabase;
903 FBase.CheckTransaction;
904 Close;
905 if Prepared then
906 exit;
907 if (FSQL.Text = '') then
908 IBError(ibxeEmptyQuery, [nil]);
909
910 if FStatement <> nil then
911 FStatement.Prepare(Transaction.TransactionIntf)
912 else
913 if not ParamCheck then
914 FStatement := Database.Attachment.Prepare(Transaction.TransactionIntf,SQL.Text)
915 else
916 FStatement := Database.Attachment.PrepareWithNamedParameters(
917 Transaction.TransactionIntf,
918 SQL.Text,
919 GenerateParamNames,
920 CaseSensitiveParameterNames);
921 {$IFDEF IBXQUERYSTATS}
922 FStatement.EnableStatistics(true);
923 {$ENDIF}
924 FMetaData := FStatement.GetMetaData;
925 FSQLParams := FStatement.GetSQLParams;
926 FStatement.SetRetainInterfaces(true);
927 if not (csDesigning in ComponentState) then
928 MonitorHook.SQLPrepare(Self);
929 end;
930
931 function TIBSQL.GetUniqueRelationName: String;
932 begin
933 if Prepared and (GetSQLStatementType = SQLSelect) then
934 result := FMetaData.GetUniqueRelationName
935 else
936 result := '';
937 end;
938
939 procedure TIBSQL.SetSQL(Value: TStrings);
940 begin
941 if FSQL.Text <> Value.Text then
942 begin
943 FSQL.BeginUpdate;
944 try
945 FSQL.Assign(Value);
946 finally
947 FSQL.EndUpdate;
948 end;
949 end;
950 end;
951
952 procedure TIBSQL.SetTransaction(Value: TIBTransaction);
953 begin
954 if FBase.Transaction = Value then Exit;
955 FreeHandle;
956 FBase.Transaction := Value;
957 end;
958
959 procedure TIBSQL.SQLChanging(Sender: TObject);
960 begin
961 if Assigned(OnSQLChanging) then
962 OnSQLChanging(Self);
963
964 FreeHandle;
965 end;
966
967 procedure TIBSQL.SQLChanged(Sender: TObject);
968 begin
969 if assigned(OnSQLChanged) then
970 OnSQLChanged(self);
971 end;
972
973 procedure TIBSQL.BeforeTransactionEnd(Sender: TObject;
974 Action: TTransactionAction);
975 begin
976 if not (Owner is TIBCustomDataSet) then
977 FreeHandle;
978 end;
979
980 end.