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 (3 years, 3 months ago) by tony
Content type: text/x-pascal
File size: 27241 byte(s)
Log Message:
Merged into public release

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 tony 345 function GetRowsAffected: Int64;
192 tony 209 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 tony 345 property RowsAffected: Int64 read GetRowsAffected;
229 tony 209 property SQLStatementType: TIBSQLStatementTypes read GetSQLStatementType;
230     property UniqueRelationName: String read GetUniqueRelationName;
231     property Statement: IStatement read FStatement;
232     property MetaData: IMetaData read FMetaData;
233 tony 345 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 tony 209 published
244     property Database: TIBDatabase read GetDatabase write SetDatabase;
245 tony 270 property CaseSensitiveParameterNames: boolean read FCaseSensitiveParameterNames
246     write FCaseSensitiveParameterNames;
247 tony 209 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 tony 291 Variants, IBSQLMonitor, IBMessages, IBCustomDataSet;
265 tony 209
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 tony 229 var i: integer;
758 tony 209 begin
759 tony 229 if MetaData = nil then
760 tony 209 IBError(ibxeNoFieldAccess,[nil]);
761    
762 tony 229 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 tony 209 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 tony 345 function TIBSQL.GetRowsAffected: Int64;
878 tony 209 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 tony 270 GenerateParamNames,
930     CaseSensitiveParameterNames);
931 tony 209 {$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 tony 345 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 tony 209 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.