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