ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/nongui/IBSQL.pas
Revision: 270
Committed: Fri Jan 18 11:10:37 2019 UTC (5 years, 10 months ago) by tony
Content type: text/x-pascal
File size: 26021 byte(s)
Log Message:
Fixes merged

File Contents

# User Rev Content
1 tony 209 {************************************************************************}
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 tony 270 FCaseSensitiveParameterNames: boolean;
162 tony 209 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 tony 229 function HasField(FieldName: String): boolean; {Note: case sensitive match}
211 tony 209 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 tony 270 property CaseSensitiveParameterNames: boolean read FCaseSensitiveParameterNames
236     write FCaseSensitiveParameterNames;
237 tony 209 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, FBMessages, 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 tony 229 var i: integer;
748 tony 209 begin
749 tony 229 if MetaData = nil then
750 tony 209 IBError(ibxeNoFieldAccess,[nil]);
751    
752 tony 229 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 tony 209 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 tony 270 GenerateParamNames,
920     CaseSensitiveParameterNames);
921 tony 209 {$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.