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 (2 years, 6 months ago) by tony
Content type: text/x-pascal
File size: 29304 byte(s)
Log Message:
Fixes Merged

File Contents

# Content
1 {************************************************************************}
2 { }
3 { Borland Delphi Visual Component Library }
4 { InterBase Express core components }
5 { }
6 { Copyright (c) 1998-2000 Inprise Corporation }
7 { }
8 { InterBase Express is based in part on the product }
9 { Free IB Components, written by Gregory H. Deatz for }
10 { Hoagland, Longo, Moran, Dunst & Doukas Company. }
11 { Free IB Components is used under license. }
12 { }
13 { The contents of this file are subject to the InterBase }
14 { Public License Version 1.0 (the "License"); you may not }
15 { use this file except in compliance with the License. You }
16 { may obtain a copy of the License at http://www.Inprise.com/IPL.html }
17 { Software distributed under the License is distributed on }
18 { an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either }
19 { express or implied. See the License for the specific language }
20 { governing rights and limitations under the License. }
21 { The Original Code was created by InterBase Software Corporation }
22 { and its successors. }
23 { Portions created by Inprise Corporation are Copyright (C) Inprise }
24 { Corporation. All Rights Reserved. }
25 { Contributor(s): Jeff Overcash }
26 { }
27 { IBX For Lazarus (Firebird Express) }
28 { Contributor: Tony Whyman, MWA Software http://www.mwasoftware.co.uk }
29 { Portions created by MWA Software are copyright McCallum Whyman }
30 { Associates Ltd 2011 - 2014 }
31 { }
32 {************************************************************************}
33
34 unit IBSQL;
35
36 {$Mode Delphi}
37
38 {$codepage UTF8}
39
40 (* Define IBXQUERYSTATS to write to stdout a summary of query execution
41 statistics each time a query is executed
42
43 Define IBXQUERYTIME to write to stdout The local execution time for each
44 query
45 *)
46
47 { $DEFINE IBXQUERYSTATS}
48 { $DEFINE IBXQUERYTIME}
49
50 interface
51
52 uses
53 {$IFDEF WINDOWS }
54 Windows,
55 {$ELSE}
56 baseunix, unix,
57 {$ENDIF}
58 SysUtils, Classes, IB, IBDatabase, IBUtils;
59
60 type
61 { TIBBatch }
62
63 TIBBatch = class(TObject)
64 protected
65 FFilename: String;
66 FColumns: IResults;
67 FParams: ISQLParams;
68 public
69 procedure ReadyFile; virtual; abstract;
70 property Columns: IResults read FColumns;
71 property Filename: String read FFilename write FFilename;
72 property Params: ISQLParams read FParams;
73 end;
74
75 TIBBatchInput = class(TIBBatch)
76 public
77 function ReadParameters: Boolean; virtual; abstract;
78 end;
79
80 TIBBatchOutput = class(TIBBatch)
81 public
82 function WriteColumns: Boolean; virtual; abstract;
83 end;
84
85
86 { TIBOutputDelimitedFile }
87 TIBOutputDelimitedFile = class(TIBBatchOutput)
88 protected
89 {$IFDEF UNIX}
90 FHandle: cint;
91 {$ELSE}
92 FHandle: THandle;
93 {$ENDIF}
94 FOutputTitles: Boolean;
95 FColDelimiter,
96 FRowDelimiter: string;
97 public
98 destructor Destroy; override;
99 procedure ReadyFile; override;
100 function WriteColumns: Boolean; override;
101 property ColDelimiter: string read FColDelimiter write FColDelimiter;
102 property OutputTitles: Boolean read FOutputTitles
103 write FOutputTitles;
104 property RowDelimiter: string read FRowDelimiter write FRowDelimiter;
105 end;
106
107 { TIBInputDelimitedFile }
108 TIBInputDelimitedFile = class(TIBBatchInput)
109 protected
110 FColDelimiter,
111 FRowDelimiter: string;
112 FEOF: Boolean;
113 FFile: TFileStream;
114 FLookAhead: Char;
115 FReadBlanksAsNull: Boolean;
116 FSkipTitles: Boolean;
117 public
118 destructor Destroy; override;
119 function GetColumn(var Col: string): Integer;
120 function ReadParameters: Boolean; override;
121 procedure ReadyFile; override;
122 property ColDelimiter: string read FColDelimiter write FColDelimiter;
123 property ReadBlanksAsNull: Boolean read FReadBlanksAsNull
124 write FReadBlanksAsNull;
125 property RowDelimiter: string read FRowDelimiter write FRowDelimiter;
126 property SkipTitles: Boolean read FSkipTitles write FSkipTitles;
127 end;
128
129 { TIBOutputRawFile }
130 TIBOutputRawFile = class(TIBBatchOutput)
131 protected
132 {$IFDEF UNIX}
133 FHandle: cint;
134 {$ELSE}
135 FHandle: THandle;
136 {$ENDIF}
137 public
138 destructor Destroy; override;
139 procedure ReadyFile; override;
140 function WriteColumns: Boolean; override;
141 end;
142
143 { TIBInputRawFile }
144 TIBInputRawFile = class(TIBBatchInput)
145 protected
146 {$IFDEF UNIX}
147 FHandle: cint;
148 {$ELSE}
149 FHandle: THandle;
150 {$ENDIF}
151 public
152 destructor Destroy; override;
153 function ReadParameters: Boolean; override;
154 procedure ReadyFile; override;
155 end;
156
157 { TIBSQL }
158
159 TIBSQL = class(TComponent)
160 private
161 FCaseSensitiveParameterNames: boolean;
162 FMetaData: IMetaData;
163 FScrollable: boolean;
164 FSQLParams: ISQLParams;
165 FStatement: IStatement;
166 FOnSQLChanged: TNotifyEvent;
167 FUniqueParamNames: Boolean;
168 function GetBOF: Boolean;
169 function GetFieldCount: integer;
170 function GetOpen: Boolean;
171 function GetPrepared: Boolean;
172 function GetSQLStatementType: TIBSQLStatementTypes;
173 procedure SetUniqueParamNames(AValue: Boolean);
174 protected
175 FBase: TIBBase;
176 FGoToFirstRecordOnExecute: boolean; { Automatically position record on first record after executing }
177 FRecordCount: Integer; { How many records have been read so far? }
178 FOnSQLChanging: TNotifyEvent; { Call this when the SQL is changing }
179 FSQL: TStrings; { SQL Query (by user) }
180 FParamCheck: Boolean; { Check for parameters? (just like TQuery) }
181 FResults: IResults; {Single row results from exec}
182 FResultSet: IResultSet; {Multi-row results from open cursor}
183 FGenerateParamNames: Boolean; { Auto generate param names ?}
184 procedure DoBeforeDatabaseDisconnect(Sender: TObject);
185 function GetDatabase: TIBDatabase;
186 function GetEOF: Boolean;
187 function GetFields(const Idx: Integer): ISQLData;
188 function GetFieldIndex(FieldName: String): Integer;
189 function GetPlan: String;
190 function GetRecordCount: Integer;
191 function GetRowsAffected: Int64;
192 function GetSQLParams: ISQLParams;
193 function GetTransaction: TIBTransaction;
194 procedure SetDatabase(Value: TIBDatabase);
195 procedure SetSQL(Value: TStrings);
196 procedure SetTransaction(Value: TIBTransaction);
197 procedure SQLChanging(Sender: TObject);
198 procedure SQLChanged(Sender: TObject);
199 procedure BeforeTransactionEnd(Sender: TObject; Action: TTransactionAction);
200 public
201 constructor Create(AOwner: TComponent); override;
202 destructor Destroy; override;
203 procedure BatchInput(InputObject: TIBBatchInput);
204 procedure BatchOutput(OutputObject: TIBBatchOutput);
205 procedure CheckClosed; { raise error if query is not closed. }
206 procedure CheckOpen; { raise error if query is not open.}
207 procedure CheckValidStatement; { raise error if statement is invalid.}
208 procedure Close;
209 procedure ExecQuery;
210 function HasField(FieldName: String): boolean; {Note: case sensitive match}
211 function HasScollableCursors: boolean;
212 function FieldByName(FieldName: String): ISQLData;
213 function ParamByName(ParamName: String): ISQLParam;
214 procedure FreeHandle;
215 function Next: boolean;
216 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 procedure Prepare;
223 function GetUniqueRelationName: String;
224 property Bof: Boolean read GetBOF;
225 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 property RowsAffected: Int64 read GetRowsAffected;
236 property SQLStatementType: TIBSQLStatementTypes read GetSQLStatementType;
237 property UniqueRelationName: String read GetUniqueRelationName;
238 property Statement: IStatement read FStatement;
239 property MetaData: IMetaData read FMetaData;
240 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 published
251 property Database: TIBDatabase read GetDatabase write SetDatabase;
252 property CaseSensitiveParameterNames: boolean read FCaseSensitiveParameterNames
253 write FCaseSensitiveParameterNames;
254 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 property Scrollable: boolean read FScrollable write FScrollable;
262 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 Variants, IBSQLMonitor, IBMessages, IBCustomDataSet;
273
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 function TIBSQL.GetBOF: Boolean;
686 begin
687 Result := (FResultSet = nil) or FResultSet.IsBof;
688 end;
689
690 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 FResultSet := FStatement.OpenCursor(Scrollable);
739 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 var i: integer;
767 begin
768 if MetaData = nil then
769 IBError(ibxeNoFieldAccess,[nil]);
770
771 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 end;
781
782 function TIBSQL.HasScollableCursors: boolean;
783 begin
784 Result := Database.Attachment.HasScollableCursors;
785 end;
786
787 function TIBSQL.GetEOF: Boolean;
788 begin
789 result := (FResultSet = nil) or FResultSet.IsEof;
790 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 Result := FetchNext;
835 end;
836
837 function TIBSQL.FetchNext: boolean;
838 begin
839 result := false;
840 if not EOF then
841 begin
842 CheckOpen;
843 try
844 Result := FResultSet.FetchNext;
845 except
846 Close;
847 raise;
848 end;
849
850 if Result and not Scrollable then
851 Inc(FRecordCount);
852
853 if not (csDesigning in ComponentState) then
854 MonitorHook.SQLFetch(Self);
855 end;
856 end;
857
858 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 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 function TIBSQL.GetRowsAffected: Int64;
970 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 GenerateParamNames,
1022 CaseSensitiveParameterNames);
1023 {$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 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 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.