ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBSQL.pas
Revision: 104
Committed: Thu Jan 18 14:37:29 2018 UTC (6 years, 3 months ago) by tony
Content type: text/x-pascal
File size: 25616 byte(s)
Log Message:
Documentation Updates

File Contents

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