ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBSQL.pas
Revision: 21
Committed: Thu Feb 26 10:33:34 2015 UTC (9 years, 9 months ago) by tony
Content type: text/x-pascal
File size: 77327 byte(s)
Log Message:
Committing updates for Release R1-2-0

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 { IBSQL param names in dialect 3 quoted format (e.g. :"MyParam") are by default disabled.
39
40 Dialect 3 quoted format parameter names represent a significant overhead and are of
41 limited value - especially for users that use only TIBSQL or TIBCustomDataset
42 descendents. They were previously used internally by IBX to simplify SQL generation
43 for TTable components in Master/Slave relationships which are linked by
44 Dialect 3 names. They were also generated by TStoredProc when the original
45 parameter names are quoted.
46
47 However, for some users they do cause a big processing overhead. The TTable/TStoredProc
48 code has been re-written so that they are no required by IBX internally.
49 The code to support quoted parameter names is now subject to conditional compilation.
50 To enable support, ALLOWDIALECT3PARAMNAMES should be defined when IBX is compiled.
51
52 Hint: deleting the space between the brace and the dollar sign below
53
54 }
55
56 { $define ALLOWDIALECT3PARAMNAMES}
57
58 {$ifndef ALLOWDIALECT3PARAMNAMES}
59
60 { Even when dialect 3 quoted format parameter names are not supported, IBX still processes
61 parameter names case insensitive. This does result in some additional overhead
62 due to a call to "AnsiUpperCase". This can be avoided by undefining
63 "UseCaseSensitiveParamName" below.
64
65 Note: do not define "UseCaseSensitiveParamName" when "ALLOWDIALECT3PARAMNAMES"
66 is defined. This will not give a useful result.
67 }
68 {$define UseCaseSensitiveParamName}
69 {$endif}
70
71 interface
72
73 uses
74 {$IFDEF WINDOWS }
75 Windows,
76 {$ELSE}
77 baseunix, unix,
78 {$ENDIF}
79 SysUtils, Classes, Forms, Controls, IBHeader,
80 IBErrorCodes, IBExternals, DB, IB, IBDatabase, IBUtils, IBXConst;
81
82 const
83 sSQLErrorSeparator = ' When Executing: ';
84
85 type
86 TIBSQL = class;
87 TIBXSQLDA = class;
88
89 { TIBXSQLVAR }
90 TIBXSQLVAR = class(TObject)
91 private
92 FParent: TIBXSQLDA;
93 FSQL: TIBSQL;
94 FIndex: Integer;
95 FModified: Boolean;
96 FName: String;
97 FUniqueName: boolean;
98 FXSQLVAR: PXSQLVAR; { Point to the PXSQLVAR in the owner object }
99
100 function AdjustScale(Value: Int64; Scale: Integer): Double;
101 function AdjustScaleToInt64(Value: Int64; Scale: Integer): Int64;
102 function AdjustScaleToCurrency(Value: Int64; Scale: Integer): Currency;
103 function GetAsCurrency: Currency;
104 function GetAsInt64: Int64;
105 function GetAsDateTime: TDateTime;
106 function GetAsDouble: Double;
107 function GetAsFloat: Float;
108 function GetAsLong: Long;
109 function GetAsPointer: Pointer;
110 function GetAsQuad: TISC_QUAD;
111 function GetAsShort: Short;
112 function GetAsString: String;
113 function GetAsVariant: Variant;
114 function GetAsXSQLVAR: PXSQLVAR;
115 function GetIsNull: Boolean;
116 function GetIsNullable: Boolean;
117 function GetSize: Integer;
118 function GetSQLType: Integer;
119 procedure SetAsCurrency(Value: Currency);
120 procedure SetAsInt64(Value: Int64);
121 procedure SetAsDate(Value: TDateTime);
122 procedure SetAsTime(Value: TDateTime);
123 procedure SetAsDateTime(Value: TDateTime);
124 procedure SetAsDouble(Value: Double);
125 procedure SetAsFloat(Value: Float);
126 procedure SetAsLong(Value: Long);
127 procedure SetAsPointer(Value: Pointer);
128 procedure SetAsQuad(Value: TISC_QUAD);
129 procedure SetAsShort(Value: Short);
130 procedure SetAsString(Value: String);
131 procedure SetAsVariant(Value: Variant);
132 procedure SetAsXSQLVAR(Value: PXSQLVAR);
133 procedure SetIsNull(Value: Boolean);
134 procedure SetIsNullable(Value: Boolean);
135 procedure xSetAsCurrency(Value: Currency);
136 procedure xSetAsInt64(Value: Int64);
137 procedure xSetAsDate(Value: TDateTime);
138 procedure xSetAsTime(Value: TDateTime);
139 procedure xSetAsDateTime(Value: TDateTime);
140 procedure xSetAsDouble(Value: Double);
141 procedure xSetAsFloat(Value: Float);
142 procedure xSetAsLong(Value: Long);
143 procedure xSetAsPointer(Value: Pointer);
144 procedure xSetAsQuad(Value: TISC_QUAD);
145 procedure xSetAsShort(Value: Short);
146 procedure xSetAsString(Value: String);
147 procedure xSetAsVariant(Value: Variant);
148 procedure xSetAsXSQLVAR(Value: PXSQLVAR);
149 procedure xSetIsNull(Value: Boolean);
150 procedure xSetIsNullable(Value: Boolean);
151 public
152 constructor Create(Parent: TIBXSQLDA; Query: TIBSQL);
153 procedure Assign(Source: TIBXSQLVAR);
154 procedure Clear;
155 procedure LoadFromFile(const FileName: String);
156 procedure LoadFromStream(Stream: TStream);
157 procedure SaveToFile(const FileName: String);
158 procedure SaveToStream(Stream: TStream);
159 property AsDate: TDateTime read GetAsDateTime write SetAsDate;
160 property AsTime: TDateTime read GetAsDateTime write SetAsTime;
161 property AsDateTime: TDateTime read GetAsDateTime write SetAsDateTime;
162 property AsDouble: Double read GetAsDouble write SetAsDouble;
163 property AsFloat: Float read GetAsFloat write SetAsFloat;
164 property AsCurrency: Currency read GetAsCurrency write SetAsCurrency;
165 property AsInt64: Int64 read GetAsInt64 write SetAsInt64;
166 property AsInteger: Integer read GetAsLong write SetAsLong;
167 property AsLong: Long read GetAsLong write SetAsLong;
168 property AsPointer: Pointer read GetAsPointer write SetAsPointer;
169 property AsQuad: TISC_QUAD read GetAsQuad write SetAsQuad;
170 property AsShort: Short read GetAsShort write SetAsShort;
171 property AsString: String read GetAsString write SetAsString;
172 property AsVariant: Variant read GetAsVariant write SetAsVariant;
173 property AsXSQLVAR: PXSQLVAR read GetAsXSQLVAR write SetAsXSQLVAR;
174 property Data: PXSQLVAR read FXSQLVAR write FXSQLVAR;
175 property IsNull: Boolean read GetIsNull write SetIsNull;
176 property IsNullable: Boolean read GetIsNullable write SetIsNullable;
177 property Index: Integer read FIndex;
178 property Modified: Boolean read FModified write FModified;
179 property Name: String read FName;
180 property Size: Integer read GetSize;
181 property SQLType: Integer read GetSQLType;
182 property Value: Variant read GetAsVariant write SetAsVariant;
183 end;
184
185 TIBXSQLVARArray = Array of TIBXSQLVAR;
186
187 TIBXSQLDAType = (daInput,daOutput);
188
189 { TIBXSQLDA }
190
191 TIBXSQLDA = class(TObject)
192 protected
193 FSQL: TIBSQL;
194 FCount: Integer;
195 FSize: Integer;
196 FInputSQLDA: boolean;
197 FXSQLDA: PXSQLDA;
198 FXSQLVARs: TIBXSQLVARArray; { array of IBXQLVARs }
199 FUniqueRelationName: String;
200 function GetModified: Boolean;
201 function GetRecordSize: Integer;
202 function GetXSQLDA: PXSQLDA;
203 function GetXSQLVAR(Idx: Integer): TIBXSQLVAR;
204 function GetXSQLVARByName(Idx: String): TIBXSQLVAR;
205 procedure Initialize;
206 procedure SetCount(Value: Integer);
207 public
208 constructor Create(Query: TIBSQL; sqldaType: TIBXSQLDAType);
209 destructor Destroy; override;
210 procedure SetParamName(FieldName: String; Idx: Integer; UniqueName: boolean = false);
211 function ByName(Idx: String): TIBXSQLVAR;
212 property AsXSQLDA: PXSQLDA read GetXSQLDA;
213 property Count: Integer read FCount write SetCount;
214 property Modified: Boolean read GetModified;
215 property RecordSize: Integer read GetRecordSize;
216 property Vars[Idx: Integer]: TIBXSQLVAR read GetXSQLVAR; default;
217 property UniqueRelationName: String read FUniqueRelationName;
218 end;
219
220 { TIBBatch }
221
222 TIBBatch = class(TObject)
223 protected
224 FFilename: String;
225 FColumns: TIBXSQLDA;
226 FParams: TIBXSQLDA;
227 public
228 procedure ReadyFile; virtual; abstract;
229 property Columns: TIBXSQLDA read FColumns;
230 property Filename: String read FFilename write FFilename;
231 property Params: TIBXSQLDA read FParams;
232 end;
233
234 TIBBatchInput = class(TIBBatch)
235 public
236 function ReadParameters: Boolean; virtual; abstract;
237 end;
238
239 TIBBatchOutput = class(TIBBatch)
240 public
241 function WriteColumns: Boolean; virtual; abstract;
242 end;
243
244
245 { TIBOutputDelimitedFile }
246 TIBOutputDelimitedFile = class(TIBBatchOutput)
247 protected
248 {$IFDEF UNIX}
249 FHandle: cint;
250 {$ELSE}
251 FHandle: THandle;
252 {$ENDIF}
253 FOutputTitles: Boolean;
254 FColDelimiter,
255 FRowDelimiter: string;
256 public
257 destructor Destroy; override;
258 procedure ReadyFile; override;
259 function WriteColumns: Boolean; override;
260 property ColDelimiter: string read FColDelimiter write FColDelimiter;
261 property OutputTitles: Boolean read FOutputTitles
262 write FOutputTitles;
263 property RowDelimiter: string read FRowDelimiter write FRowDelimiter;
264 end;
265
266 { TIBInputDelimitedFile }
267 TIBInputDelimitedFile = class(TIBBatchInput)
268 protected
269 FColDelimiter,
270 FRowDelimiter: string;
271 FEOF: Boolean;
272 FFile: TFileStream;
273 FLookAhead: Char;
274 FReadBlanksAsNull: Boolean;
275 FSkipTitles: Boolean;
276 public
277 destructor Destroy; override;
278 function GetColumn(var Col: string): Integer;
279 function ReadParameters: Boolean; override;
280 procedure ReadyFile; override;
281 property ColDelimiter: string read FColDelimiter write FColDelimiter;
282 property ReadBlanksAsNull: Boolean read FReadBlanksAsNull
283 write FReadBlanksAsNull;
284 property RowDelimiter: string read FRowDelimiter write FRowDelimiter;
285 property SkipTitles: Boolean read FSkipTitles write FSkipTitles;
286 end;
287
288 { TIBOutputRawFile }
289 TIBOutputRawFile = class(TIBBatchOutput)
290 protected
291 {$IFDEF UNIX}
292 FHandle: cint;
293 {$ELSE}
294 FHandle: THandle;
295 {$ENDIF}
296 public
297 destructor Destroy; override;
298 procedure ReadyFile; override;
299 function WriteColumns: Boolean; override;
300 end;
301
302 { TIBInputRawFile }
303 TIBInputRawFile = class(TIBBatchInput)
304 protected
305 {$IFDEF UNIX}
306 FHandle: cint;
307 {$ELSE}
308 FHandle: THandle;
309 {$ENDIF}
310 public
311 destructor Destroy; override;
312 function ReadParameters: Boolean; override;
313 procedure ReadyFile; override;
314 end;
315
316 { TIBSQL }
317 TIBSQLTypes = (SQLUnknown, SQLSelect, SQLInsert,
318 SQLUpdate, SQLDelete, SQLDDL,
319 SQLGetSegment, SQLPutSegment,
320 SQLExecProcedure, SQLStartTransaction,
321 SQLCommit, SQLRollback,
322 SQLSelectForUpdate, SQLSetGenerator);
323
324 TIBSQL = class(TComponent)
325 private
326 FIBLoaded: Boolean;
327 FUniqueParamNames: Boolean;
328 function GetFieldCount: integer;
329 procedure SetUniqueParamNames(AValue: Boolean);
330 protected
331 FBase: TIBBase;
332 FBOF, { At BOF? }
333 FEOF, { At EOF? }
334 FGoToFirstRecordOnExecute, { Automatically position record on first record after executing }
335 FOpen, { Is a cursor open? }
336 FPrepared: Boolean; { Has the query been prepared? }
337 FRecordCount: Integer; { How many records have been read so far? }
338 FCursor: String; { Cursor name...}
339 FHandle: TISC_STMT_HANDLE; { Once prepared, this accesses the SQL Query }
340 FOnSQLChanging: TNotifyEvent; { Call this when the SQL is changing }
341 FSQL: TStrings; { SQL Query (by user) }
342 FParamCheck: Boolean; { Check for parameters? (just like TQuery) }
343 FProcessedSQL: TStrings; { SQL Query (pre-processed for param labels) }
344 FSQLParams, { Any parameters to the query }
345 FSQLRecord: TIBXSQLDA; { The current record }
346 FSQLType: TIBSQLTypes; { Select, update, delete, insert, create, alter, etc...}
347 FGenerateParamNames: Boolean; { Auto generate param names ?}
348 procedure DoBeforeDatabaseDisconnect(Sender: TObject);
349 function GetDatabase: TIBDatabase;
350 function GetDBHandle: PISC_DB_HANDLE;
351 function GetEOF: Boolean;
352 function GetFields(const Idx: Integer): TIBXSQLVAR;
353 function GetFieldIndex(FieldName: String): Integer;
354 function GetPlan: String;
355 function GetRecordCount: Integer;
356 function GetRowsAffected: Integer;
357 function GetSQLParams: TIBXSQLDA;
358 function GetTransaction: TIBTransaction;
359 function GetTRHandle: PISC_TR_HANDLE;
360 procedure PreprocessSQL;
361 procedure SetDatabase(Value: TIBDatabase);
362 procedure SetSQL(Value: TStrings);
363 procedure SetTransaction(Value: TIBTransaction);
364 procedure SQLChanging(Sender: TObject);
365 procedure BeforeTransactionEnd(Sender: TObject);
366 public
367 constructor Create(AOwner: TComponent); override;
368 destructor Destroy; override;
369 procedure BatchInput(InputObject: TIBBatchInput);
370 procedure BatchOutput(OutputObject: TIBBatchOutput);
371 function Call(ErrCode: ISC_STATUS; RaiseError: Boolean): ISC_STATUS;
372 procedure CheckClosed; { raise error if query is not closed. }
373 procedure CheckOpen; { raise error if query is not open.}
374 procedure CheckValidStatement; { raise error if statement is invalid.}
375 procedure Close;
376 function Current: TIBXSQLDA;
377 procedure ExecQuery;
378 function FieldByName(FieldName: String): TIBXSQLVAR;
379 function ParamByName(ParamName: String): TIBXSQLVAR;
380 procedure FreeHandle;
381 function Next: TIBXSQLDA;
382 procedure Prepare;
383 function GetUniqueRelationName: String;
384 property Bof: Boolean read FBOF;
385 property DBHandle: PISC_DB_HANDLE read GetDBHandle;
386 property Eof: Boolean read GetEOF;
387 property Fields[const Idx: Integer]: TIBXSQLVAR read GetFields;
388 property FieldIndex[FieldName: String]: Integer read GetFieldIndex;
389 property FieldCount: integer read GetFieldCount;
390 property Open: Boolean read FOpen;
391 property Params: TIBXSQLDA read GetSQLParams;
392 property Plan: String read GetPlan;
393 property Prepared: Boolean read FPrepared;
394 property RecordCount: Integer read GetRecordCount;
395 property RowsAffected: Integer read GetRowsAffected;
396 property SQLType: TIBSQLTypes read FSQLType;
397 property TRHandle: PISC_TR_HANDLE read GetTRHandle;
398 property Handle: TISC_STMT_HANDLE read FHandle;
399 property UniqueRelationName: String read GetUniqueRelationName;
400 published
401 property Database: TIBDatabase read GetDatabase write SetDatabase;
402 property GenerateParamNames: Boolean read FGenerateParamNames write FGenerateParamNames;
403 property UniqueParamNames: Boolean read FUniqueParamNames write SetUniqueParamNames;
404 property GoToFirstRecordOnExecute: Boolean read FGoToFirstRecordOnExecute
405 write FGoToFirstRecordOnExecute
406 default True;
407 property ParamCheck: Boolean read FParamCheck write FParamCheck;
408 property SQL: TStrings read FSQL write SetSQL;
409 property Transaction: TIBTransaction read GetTransaction write SetTransaction;
410 property OnSQLChanging: TNotifyEvent read FOnSQLChanging write FOnSQLChanging;
411 end;
412
413 implementation
414
415 uses
416 IBIntf, IBBlob, Variants , IBSQLMonitor;
417
418 { TIBXSQLVAR }
419 constructor TIBXSQLVAR.Create(Parent: TIBXSQLDA; Query: TIBSQL);
420 begin
421 inherited Create;
422 FParent := Parent;
423 FSQL := Query;
424 end;
425
426 procedure TIBXSQLVAR.Assign(Source: TIBXSQLVAR);
427 var
428 szBuff: PChar;
429 s_bhandle, d_bhandle: TISC_BLOB_HANDLE;
430 bSourceBlob, bDestBlob: Boolean;
431 iSegs: Int64;
432 iMaxSeg: Int64;
433 iSize: Int64;
434 iBlobType: Short;
435 begin
436 szBuff := nil;
437 bSourceBlob := True;
438 bDestBlob := True;
439 s_bhandle := nil;
440 d_bhandle := nil;
441 try
442 if (Source.IsNull) then
443 begin
444 IsNull := True;
445 exit;
446 end
447 else
448 if (FXSQLVAR^.sqltype and (not 1) = SQL_ARRAY) or
449 (Source.FXSQLVAR^.sqltype and (not 1) = SQL_ARRAY) then
450 exit; { arrays not supported }
451 if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) and
452 (Source.FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
453 begin
454 AsXSQLVAR := Source.AsXSQLVAR;
455 exit;
456 end
457 else
458 if (Source.FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
459 begin
460 szBuff := nil;
461 IBAlloc(szBuff, 0, Source.FXSQLVAR^.sqllen);
462 Move(Source.FXSQLVAR^.sqldata[0], szBuff[0], Source.FXSQLVAR^.sqllen);
463 bSourceBlob := False;
464 iSize := Source.FXSQLVAR^.sqllen;
465 end
466 else
467 if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
468 bDestBlob := False;
469
470 if bSourceBlob then
471 begin
472 { read the blob }
473 Source.FSQL.Call(isc_open_blob2(StatusVector, Source.FSQL.DBHandle,
474 Source.FSQL.TRHandle, @s_bhandle, PISC_QUAD(Source.FXSQLVAR.sqldata),
475 0, nil), True);
476 try
477 IBBlob.GetBlobInfo(@s_bhandle, iSegs, iMaxSeg, iSize,
478 iBlobType);
479 szBuff := nil;
480 IBAlloc(szBuff, 0, iSize);
481 IBBlob.ReadBlob(@s_bhandle, szBuff, iSize);
482 finally
483 Source.FSQL.Call(isc_close_blob(StatusVector, @s_bhandle), True);
484 end;
485 end;
486
487 if bDestBlob then
488 begin
489 { write the blob }
490 FSQL.Call(isc_create_blob2(StatusVector, FSQL.DBHandle,
491 FSQL.TRHandle, @d_bhandle, PISC_QUAD(FXSQLVAR.sqldata),
492 0, nil), True);
493 try
494 IBBlob.WriteBlob(@d_bhandle, szBuff, iSize);
495 isNull := false
496 finally
497 FSQL.Call(isc_close_blob(StatusVector, @d_bhandle), True);
498 end;
499 end
500 else
501 begin
502 { just copy the buffer }
503 FXSQLVAR.sqltype := SQL_TEXT;
504 FXSQLVAR.sqllen := iSize;
505 IBAlloc(FXSQLVAR.sqldata, iSize, iSize);
506 Move(szBuff[0], FXSQLVAR^.sqldata[0], iSize);
507 end;
508 finally
509 FreeMem(szBuff);
510 end;
511 end;
512
513 function TIBXSQLVAR.AdjustScale(Value: Int64; Scale: Integer): Double;
514 var
515 Scaling : Int64;
516 i: Integer;
517 Val: Double;
518 begin
519 Scaling := 1; Val := Value;
520 if Scale > 0 then
521 begin
522 for i := 1 to Scale do
523 Scaling := Scaling * 10;
524 result := Val * Scaling;
525 end
526 else
527 if Scale < 0 then
528 begin
529 for i := -1 downto Scale do
530 Scaling := Scaling * 10;
531 result := Val / Scaling;
532 end
533 else
534 result := Val;
535 end;
536
537 function TIBXSQLVAR.AdjustScaleToInt64(Value: Int64; Scale: Integer): Int64;
538 var
539 Scaling : Int64;
540 i: Integer;
541 Val: Int64;
542 begin
543 Scaling := 1; Val := Value;
544 if Scale > 0 then begin
545 for i := 1 to Scale do Scaling := Scaling * 10;
546 result := Val * Scaling;
547 end else if Scale < 0 then begin
548 for i := -1 downto Scale do Scaling := Scaling * 10;
549 result := Val div Scaling;
550 end else
551 result := Val;
552 end;
553
554 function TIBXSQLVAR.AdjustScaleToCurrency(Value: Int64; Scale: Integer): Currency;
555 var
556 Scaling : Int64;
557 i : Integer;
558 FractionText, PadText, CurrText: string;
559 begin
560 Result := 0;
561 Scaling := 1;
562 if Scale > 0 then
563 begin
564 for i := 1 to Scale do
565 Scaling := Scaling * 10;
566 result := Value * Scaling;
567 end
568 else
569 if Scale < 0 then
570 begin
571 for i := -1 downto Scale do
572 Scaling := Scaling * 10;
573 FractionText := IntToStr(abs(Value mod Scaling));
574 for i := Length(FractionText) to -Scale -1 do
575 PadText := '0' + PadText;
576 if Value < 0 then
577 CurrText := '-' + IntToStr(Abs(Value div Scaling)) + DecimalSeparator + PadText + FractionText
578 else
579 CurrText := IntToStr(Abs(Value div Scaling)) + DecimalSeparator + PadText + FractionText;
580 try
581 result := StrToCurr(CurrText);
582 except
583 on E: Exception do
584 IBError(ibxeInvalidDataConversion, [nil]);
585 end;
586 end
587 else
588 result := Value;
589 end;
590
591 function TIBXSQLVAR.GetAsCurrency: Currency;
592 begin
593 result := 0;
594 if FSQL.Database.SQLDialect < 3 then
595 result := GetAsDouble
596 else begin
597 if not IsNull then
598 case FXSQLVAR^.sqltype and (not 1) of
599 SQL_TEXT, SQL_VARYING: begin
600 try
601 result := StrtoCurr(AsString);
602 except
603 on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
604 end;
605 end;
606 SQL_SHORT:
607 result := AdjustScaleToCurrency(Int64(PShort(FXSQLVAR^.sqldata)^),
608 FXSQLVAR^.sqlscale);
609 SQL_LONG:
610 result := AdjustScaleToCurrency(Int64(PLong(FXSQLVAR^.sqldata)^),
611 FXSQLVAR^.sqlscale);
612 SQL_INT64:
613 result := AdjustScaleToCurrency(PInt64(FXSQLVAR^.sqldata)^,
614 FXSQLVAR^.sqlscale);
615 SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
616 result := Trunc(AsDouble);
617 else
618 IBError(ibxeInvalidDataConversion, [nil]);
619 end;
620 end;
621 end;
622
623 function TIBXSQLVAR.GetAsInt64: Int64;
624 begin
625 result := 0;
626 if not IsNull then
627 case FXSQLVAR^.sqltype and (not 1) of
628 SQL_TEXT, SQL_VARYING: begin
629 try
630 result := StrToInt64(AsString);
631 except
632 on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
633 end;
634 end;
635 SQL_SHORT:
636 result := AdjustScaleToInt64(Int64(PShort(FXSQLVAR^.sqldata)^),
637 FXSQLVAR^.sqlscale);
638 SQL_LONG:
639 result := AdjustScaleToInt64(Int64(PLong(FXSQLVAR^.sqldata)^),
640 FXSQLVAR^.sqlscale);
641 SQL_INT64:
642 result := AdjustScaleToInt64(PInt64(FXSQLVAR^.sqldata)^,
643 FXSQLVAR^.sqlscale);
644 SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
645 result := Trunc(AsDouble);
646 else
647 IBError(ibxeInvalidDataConversion, [nil]);
648 end;
649 end;
650
651 function TIBXSQLVAR.GetAsDateTime: TDateTime;
652 var
653 tm_date: TCTimeStructure;
654 msecs: word;
655 begin
656 result := 0;
657 if not IsNull then
658 case FXSQLVAR^.sqltype and (not 1) of
659 SQL_TEXT, SQL_VARYING: begin
660 try
661 result := StrToDate(AsString);
662 except
663 on E: EConvertError do IBError(ibxeInvalidDataConversion, [nil]);
664 end;
665 end;
666 SQL_TYPE_DATE: begin
667 isc_decode_sql_date(PISC_DATE(FXSQLVAR^.sqldata), @tm_date);
668 try
669 result := EncodeDate(Word(tm_date.tm_year + 1900), Word(tm_date.tm_mon + 1),
670 Word(tm_date.tm_mday));
671 except
672 on E: EConvertError do begin
673 IBError(ibxeInvalidDataConversion, [nil]);
674 end;
675 end;
676 end;
677 SQL_TYPE_TIME: begin
678 isc_decode_sql_time(PISC_TIME(FXSQLVAR^.sqldata), @tm_date);
679 try
680 msecs := (PISC_TIME(FXSQLVAR^.sqldata)^ mod 10000) div 10;
681 result := EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
682 Word(tm_date.tm_sec), msecs)
683 except
684 on E: EConvertError do begin
685 IBError(ibxeInvalidDataConversion, [nil]);
686 end;
687 end;
688 end;
689 SQL_TIMESTAMP: begin
690 isc_decode_date(PISC_QUAD(FXSQLVAR^.sqldata), @tm_date);
691 try
692 result := EncodeDate(Word(tm_date.tm_year + 1900), Word(tm_date.tm_mon + 1),
693 Word(tm_date.tm_mday));
694 msecs := (PISC_TIMESTAMP(FXSQLVAR^.sqldata)^.timestamp_time mod 10000) div 10;
695 if result >= 0 then
696 result := result + EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
697 Word(tm_date.tm_sec), msecs)
698 else
699 result := result - EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
700 Word(tm_date.tm_sec), msecs)
701 except
702 on E: EConvertError do begin
703 IBError(ibxeInvalidDataConversion, [nil]);
704 end;
705 end;
706 end;
707 else
708 IBError(ibxeInvalidDataConversion, [nil]);
709 end;
710 end;
711
712 function TIBXSQLVAR.GetAsDouble: Double;
713 begin
714 result := 0;
715 if not IsNull then begin
716 case FXSQLVAR^.sqltype and (not 1) of
717 SQL_TEXT, SQL_VARYING: begin
718 try
719 result := StrToFloat(AsString);
720 except
721 on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
722 end;
723 end;
724 SQL_SHORT:
725 result := AdjustScale(Int64(PShort(FXSQLVAR^.sqldata)^),
726 FXSQLVAR^.sqlscale);
727 SQL_LONG:
728 result := AdjustScale(Int64(PLong(FXSQLVAR^.sqldata)^),
729 FXSQLVAR^.sqlscale);
730 SQL_INT64:
731 result := AdjustScale(PInt64(FXSQLVAR^.sqldata)^, FXSQLVAR^.sqlscale);
732 SQL_FLOAT:
733 result := PFloat(FXSQLVAR^.sqldata)^;
734 SQL_DOUBLE, SQL_D_FLOAT:
735 result := PDouble(FXSQLVAR^.sqldata)^;
736 else
737 IBError(ibxeInvalidDataConversion, [nil]);
738 end;
739 if FXSQLVAR^.sqlscale <> 0 then
740 result :=
741 StrToFloat(FloatToStrF(result, fffixed, 15,
742 Abs(FXSQLVAR^.sqlscale) ));
743 end;
744 end;
745
746 function TIBXSQLVAR.GetAsFloat: Float;
747 begin
748 result := 0;
749 try
750 result := AsDouble;
751 except
752 on E: EOverflow do
753 IBError(ibxeInvalidDataConversion, [nil]);
754 end;
755 end;
756
757 function TIBXSQLVAR.GetAsLong: Long;
758 begin
759 result := 0;
760 if not IsNull then
761 case FXSQLVAR^.sqltype and (not 1) of
762 SQL_TEXT, SQL_VARYING: begin
763 try
764 result := StrToInt(AsString);
765 except
766 on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
767 end;
768 end;
769 SQL_SHORT:
770 result := Trunc(AdjustScale(Int64(PShort(FXSQLVAR^.sqldata)^),
771 FXSQLVAR^.sqlscale));
772 SQL_LONG:
773 result := Trunc(AdjustScale(Int64(PLong(FXSQLVAR^.sqldata)^),
774 FXSQLVAR^.sqlscale));
775 SQL_INT64:
776 result := Trunc(AdjustScale(PInt64(FXSQLVAR^.sqldata)^, FXSQLVAR^.sqlscale));
777 SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
778 result := Trunc(AsDouble);
779 else
780 IBError(ibxeInvalidDataConversion, [nil]);
781 end;
782 end;
783
784 function TIBXSQLVAR.GetAsPointer: Pointer;
785 begin
786 if not IsNull then
787 result := FXSQLVAR^.sqldata
788 else
789 result := nil;
790 end;
791
792 function TIBXSQLVAR.GetAsQuad: TISC_QUAD;
793 begin
794 result.gds_quad_high := 0;
795 result.gds_quad_low := 0;
796 if not IsNull then
797 case FXSQLVAR^.sqltype and (not 1) of
798 SQL_BLOB, SQL_ARRAY, SQL_QUAD:
799 result := PISC_QUAD(FXSQLVAR^.sqldata)^;
800 else
801 IBError(ibxeInvalidDataConversion, [nil]);
802 end;
803 end;
804
805 function TIBXSQLVAR.GetAsShort: Short;
806 begin
807 result := 0;
808 try
809 result := AsLong;
810 except
811 on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
812 end;
813 end;
814
815
816 function TIBXSQLVAR.GetAsString: String;
817 var
818 sz: PChar;
819 str_len: Integer;
820 ss: TStringStream;
821 begin
822 result := '';
823 { Check null, if so return a default string }
824 if not IsNull then
825 case FXSQLVar^.sqltype and (not 1) of
826 SQL_ARRAY:
827 result := '(Array)'; {do not localize}
828 SQL_BLOB: begin
829 ss := TStringStream.Create('');
830 try
831 SaveToStream(ss);
832 result := ss.DataString;
833 finally
834 ss.Free;
835 end;
836 end;
837 SQL_TEXT, SQL_VARYING: begin
838 sz := FXSQLVAR^.sqldata;
839 if (FXSQLVar^.sqltype and (not 1) = SQL_TEXT) then
840 str_len := FXSQLVar^.sqllen
841 else begin
842 str_len := isc_vax_integer(FXSQLVar^.sqldata, 2);
843 Inc(sz, 2);
844 end;
845 SetString(result, sz, str_len);
846 if ((FXSQLVar^.sqltype and (not 1)) = SQL_TEXT) then
847 result := TrimRight(result);
848 end;
849 SQL_TYPE_DATE:
850 case FSQL.Database.SQLDialect of
851 1 : result := DateTimeToStr(AsDateTime);
852 3 : result := DateToStr(AsDateTime);
853 end;
854 SQL_TYPE_TIME :
855 result := TimeToStr(AsDateTime);
856 SQL_TIMESTAMP:
857 result := DateTimeToStr(AsDateTime);
858 SQL_SHORT, SQL_LONG:
859 if FXSQLVAR^.sqlscale = 0 then
860 result := IntToStr(AsLong)
861 else if FXSQLVAR^.sqlscale >= (-4) then
862 result := CurrToStr(AsCurrency)
863 else
864 result := FloatToStr(AsDouble);
865 SQL_INT64:
866 if FXSQLVAR^.sqlscale = 0 then
867 result := IntToStr(AsInt64)
868 else if FXSQLVAR^.sqlscale >= (-4) then
869 result := CurrToStr(AsCurrency)
870 else
871 result := FloatToStr(AsDouble);
872 SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
873 result := FloatToStr(AsDouble);
874 else
875 IBError(ibxeInvalidDataConversion, [nil]);
876 end;
877 end;
878
879 function TIBXSQLVAR.GetAsVariant: Variant;
880 begin
881 if IsNull then
882 result := NULL
883 { Check null, if so return a default string }
884 else case FXSQLVar^.sqltype and (not 1) of
885 SQL_ARRAY:
886 result := '(Array)'; {do not localize}
887 SQL_BLOB:
888 result := '(Blob)'; {do not localize}
889 SQL_TEXT, SQL_VARYING:
890 result := AsString;
891 SQL_TIMESTAMP, SQL_TYPE_DATE, SQL_TYPE_TIME:
892 result := AsDateTime;
893 SQL_SHORT, SQL_LONG:
894 if FXSQLVAR^.sqlscale = 0 then
895 result := AsLong
896 else if FXSQLVAR^.sqlscale >= (-4) then
897 result := AsCurrency
898 else
899 result := AsDouble;
900 SQL_INT64:
901 if FXSQLVAR^.sqlscale = 0 then
902 result := AsInt64
903 else if FXSQLVAR^.sqlscale >= (-4) then
904 result := AsCurrency
905 else
906 result := AsDouble;
907 SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
908 result := AsDouble;
909 else
910 IBError(ibxeInvalidDataConversion, [nil]);
911 end;
912 end;
913
914 function TIBXSQLVAR.GetAsXSQLVAR: PXSQLVAR;
915 begin
916 result := FXSQLVAR;
917 end;
918
919 function TIBXSQLVAR.GetIsNull: Boolean;
920 begin
921 result := IsNullable and (FXSQLVAR^.sqlind^ = -1);
922 end;
923
924 function TIBXSQLVAR.GetIsNullable: Boolean;
925 begin
926 result := (FXSQLVAR^.sqltype and 1 = 1);
927 end;
928
929 procedure TIBXSQLVAR.LoadFromFile(const FileName: String);
930 var
931 fs: TFileStream;
932 begin
933 fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
934 try
935 LoadFromStream(fs);
936 finally
937 fs.Free;
938 end;
939 end;
940
941 procedure TIBXSQLVAR.LoadFromStream(Stream: TStream);
942 var
943 bs: TIBBlobStream;
944 begin
945 bs := TIBBlobStream.Create;
946 try
947 bs.Mode := bmWrite;
948 bs.Database := FSQL.Database;
949 bs.Transaction := FSQL.Transaction;
950 Stream.Seek(0, soFromBeginning);
951 bs.LoadFromStream(Stream);
952 bs.Finalize;
953 AsQuad := bs.BlobID;
954 finally
955 bs.Free;
956 end;
957 end;
958
959 procedure TIBXSQLVAR.SaveToFile(const FileName: String);
960 var
961 fs: TFileStream;
962 begin
963 fs := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
964 try
965 SaveToStream(fs);
966 finally
967 fs.Free;
968 end;
969 end;
970
971 procedure TIBXSQLVAR.SaveToStream(Stream: TStream);
972 var
973 bs: TIBBlobStream;
974 begin
975 bs := TIBBlobStream.Create;
976 try
977 bs.Mode := bmRead;
978 bs.Database := FSQL.Database;
979 bs.Transaction := FSQL.Transaction;
980 bs.BlobID := AsQuad;
981 bs.SaveToStream(Stream);
982 finally
983 bs.Free;
984 end;
985 end;
986
987 function TIBXSQLVAR.GetSize: Integer;
988 begin
989 result := FXSQLVAR^.sqllen;
990 end;
991
992 function TIBXSQLVAR.GetSQLType: Integer;
993 begin
994 result := FXSQLVAR^.sqltype and (not 1);
995 end;
996
997 procedure TIBXSQLVAR.xSetAsCurrency(Value: Currency);
998 begin
999 if IsNullable then
1000 IsNull := False;
1001 FXSQLVAR^.sqltype := SQL_INT64 or (FXSQLVAR^.sqltype and 1);
1002 FXSQLVAR^.sqlscale := -4;
1003 FXSQLVAR^.sqllen := SizeOf(Int64);
1004 IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1005 PCurrency(FXSQLVAR^.sqldata)^ := Value;
1006 FModified := True;
1007 end;
1008
1009 procedure TIBXSQLVAR.SetAsCurrency(Value: Currency);
1010 var
1011 i: Integer;
1012 begin
1013 if FSQL.Database.SQLDialect < 3 then
1014 AsDouble := Value
1015 else
1016 begin
1017
1018 if FUniqueName then
1019 xSetAsCurrency(Value)
1020 else
1021 for i := 0 to FParent.FCount - 1 do
1022 if FParent[i].FName = FName then
1023 FParent[i].xSetAsCurrency(Value);
1024 end;
1025 end;
1026
1027 procedure TIBXSQLVAR.xSetAsInt64(Value: Int64);
1028 begin
1029 if IsNullable then
1030 IsNull := False;
1031
1032 FXSQLVAR^.sqltype := SQL_INT64 or (FXSQLVAR^.sqltype and 1);
1033 FXSQLVAR^.sqlscale := 0;
1034 FXSQLVAR^.sqllen := SizeOf(Int64);
1035 IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1036 PInt64(FXSQLVAR^.sqldata)^ := Value;
1037 FModified := True;
1038 end;
1039
1040 procedure TIBXSQLVAR.SetAsInt64(Value: Int64);
1041 var
1042 i: Integer;
1043 begin
1044 if FUniqueName then
1045 xSetAsInt64(Value)
1046 else
1047 for i := 0 to FParent.FCount - 1 do
1048 if FParent[i].FName = FName then
1049 FParent[i].xSetAsInt64(Value);
1050 end;
1051
1052 procedure TIBXSQLVAR.xSetAsDate(Value: TDateTime);
1053 var
1054 tm_date: TCTimeStructure;
1055 Yr, Mn, Dy: Word;
1056 begin
1057 if IsNullable then
1058 IsNull := False;
1059
1060 FXSQLVAR^.sqltype := SQL_TYPE_DATE or (FXSQLVAR^.sqltype and 1);
1061 DecodeDate(Value, Yr, Mn, Dy);
1062 with tm_date do begin
1063 tm_sec := 0;
1064 tm_min := 0;
1065 tm_hour := 0;
1066 tm_mday := Dy;
1067 tm_mon := Mn - 1;
1068 tm_year := Yr - 1900;
1069 end;
1070 FXSQLVAR^.sqllen := SizeOf(ISC_DATE);
1071 IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1072 isc_encode_sql_date(@tm_date, PISC_DATE(FXSQLVAR^.sqldata));
1073 FModified := True;
1074 end;
1075
1076 procedure TIBXSQLVAR.SetAsDate(Value: TDateTime);
1077 var
1078 i: Integer;
1079 begin
1080 if FSQL.Database.SQLDialect < 3 then
1081 begin
1082 AsDateTime := Value;
1083 exit;
1084 end;
1085
1086 if FUniqueName then
1087 xSetAsDate(Value)
1088 else
1089 for i := 0 to FParent.FCount - 1 do
1090 if FParent[i].FName = FName then
1091 FParent[i].xSetAsDate(Value);
1092 end;
1093
1094 procedure TIBXSQLVAR.xSetAsTime(Value: TDateTime);
1095 var
1096 tm_date: TCTimeStructure;
1097 Hr, Mt, S, Ms: Word;
1098 begin
1099 if IsNullable then
1100 IsNull := False;
1101
1102 FXSQLVAR^.sqltype := SQL_TYPE_TIME or (FXSQLVAR^.sqltype and 1);
1103 DecodeTime(Value, Hr, Mt, S, Ms);
1104 with tm_date do begin
1105 tm_sec := S;
1106 tm_min := Mt;
1107 tm_hour := Hr;
1108 tm_mday := 0;
1109 tm_mon := 0;
1110 tm_year := 0;
1111 end;
1112 FXSQLVAR^.sqllen := SizeOf(ISC_TIME);
1113 IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1114 isc_encode_sql_time(@tm_date, PISC_TIME(FXSQLVAR^.sqldata));
1115 if Ms > 0 then
1116 Inc(PISC_TIME(FXSQLVAR^.sqldata)^,Ms*10);
1117 FModified := True;
1118 end;
1119
1120 procedure TIBXSQLVAR.SetAsTime(Value: TDateTime);
1121 var
1122 i: Integer;
1123 begin
1124 if FSQL.Database.SQLDialect < 3 then
1125 begin
1126 AsDateTime := Value;
1127 exit;
1128 end;
1129
1130 if FUniqueName then
1131 xSetAsTime(Value)
1132 else
1133 for i := 0 to FParent.FCount - 1 do
1134 if FParent[i].FName = FName then
1135 FParent[i].xSetAsTime(Value);
1136 end;
1137
1138 procedure TIBXSQLVAR.xSetAsDateTime(Value: TDateTime);
1139 var
1140 tm_date: TCTimeStructure;
1141 Yr, Mn, Dy, Hr, Mt, S, Ms: Word;
1142 begin
1143 if IsNullable then
1144 IsNull := False;
1145
1146 FXSQLVAR^.sqltype := SQL_TIMESTAMP or (FXSQLVAR^.sqltype and 1);
1147 DecodeDate(Value, Yr, Mn, Dy);
1148 DecodeTime(Value, Hr, Mt, S, Ms);
1149 with tm_date do begin
1150 tm_sec := S;
1151 tm_min := Mt;
1152 tm_hour := Hr;
1153 tm_mday := Dy;
1154 tm_mon := Mn - 1;
1155 tm_year := Yr - 1900;
1156 end;
1157 FXSQLVAR^.sqllen := SizeOf(TISC_QUAD);
1158 IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1159 isc_encode_date(@tm_date, PISC_QUAD(FXSQLVAR^.sqldata));
1160 if Ms > 0 then
1161 Inc(PISC_TIMESTAMP(FXSQLVAR^.sqldata)^.timestamp_time,Ms*10);
1162 FModified := True;
1163 end;
1164
1165 procedure TIBXSQLVAR.SetAsDateTime(Value: TDateTime);
1166 var
1167 i: Integer;
1168 begin
1169 if FUniqueName then
1170 xSetAsDateTime(value)
1171 else
1172 for i := 0 to FParent.FCount - 1 do
1173 if FParent[i].FName = FName then
1174 FParent[i].xSetAsDateTime(Value);
1175 end;
1176
1177 procedure TIBXSQLVAR.xSetAsDouble(Value: Double);
1178 begin
1179 if IsNullable then
1180 IsNull := False;
1181
1182 FXSQLVAR^.sqltype := SQL_DOUBLE or (FXSQLVAR^.sqltype and 1);
1183 FXSQLVAR^.sqllen := SizeOf(Double);
1184 FXSQLVAR^.sqlscale := 0;
1185 IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1186 PDouble(FXSQLVAR^.sqldata)^ := Value;
1187 FModified := True;
1188 end;
1189
1190 procedure TIBXSQLVAR.SetAsDouble(Value: Double);
1191 var
1192 i: Integer;
1193 begin
1194 if FUniqueName then
1195 xSetAsDouble(Value)
1196 else
1197 for i := 0 to FParent.FCount - 1 do
1198 if FParent[i].FName = FName then
1199 FParent[i].xSetAsDouble(Value);
1200 end;
1201
1202 procedure TIBXSQLVAR.xSetAsFloat(Value: Float);
1203 begin
1204 if IsNullable then
1205 IsNull := False;
1206
1207 FXSQLVAR^.sqltype := SQL_FLOAT or (FXSQLVAR^.sqltype and 1);
1208 FXSQLVAR^.sqllen := SizeOf(Float);
1209 FXSQLVAR^.sqlscale := 0;
1210 IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1211 PSingle(FXSQLVAR^.sqldata)^ := Value;
1212 FModified := True;
1213 end;
1214
1215 procedure TIBXSQLVAR.SetAsFloat(Value: Float);
1216 var
1217 i: Integer;
1218 begin
1219 if FUniqueName then
1220 xSetAsFloat(Value)
1221 else
1222 for i := 0 to FParent.FCount - 1 do
1223 if FParent[i].FName = FName then
1224 FParent[i].xSetAsFloat(Value);
1225 end;
1226
1227 procedure TIBXSQLVAR.xSetAsLong(Value: Long);
1228 begin
1229 if IsNullable then
1230 IsNull := False;
1231
1232 FXSQLVAR^.sqltype := SQL_LONG or (FXSQLVAR^.sqltype and 1);
1233 FXSQLVAR^.sqllen := SizeOf(Long);
1234 FXSQLVAR^.sqlscale := 0;
1235 IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1236 PLong(FXSQLVAR^.sqldata)^ := Value;
1237 FModified := True;
1238 end;
1239
1240 procedure TIBXSQLVAR.SetAsLong(Value: Long);
1241 var
1242 i: Integer;
1243 begin
1244 if FUniqueName then
1245 xSetAsLong(Value)
1246 else
1247 for i := 0 to FParent.FCount - 1 do
1248 if FParent[i].FName = FName then
1249 FParent[i].xSetAsLong(Value);
1250 end;
1251
1252 procedure TIBXSQLVAR.xSetAsPointer(Value: Pointer);
1253 begin
1254 if IsNullable and (Value = nil) then
1255 IsNull := True
1256 else begin
1257 IsNull := False;
1258 FXSQLVAR^.sqltype := SQL_TEXT or (FXSQLVAR^.sqltype and 1);
1259 Move(Value^, FXSQLVAR^.sqldata^, FXSQLVAR^.sqllen);
1260 end;
1261 FModified := True;
1262 end;
1263
1264 procedure TIBXSQLVAR.SetAsPointer(Value: Pointer);
1265 var
1266 i: Integer;
1267 begin
1268 if FUniqueName then
1269 xSetAsPointer(Value)
1270 else
1271 for i := 0 to FParent.FCount - 1 do
1272 if FParent[i].FName = FName then
1273 FParent[i].xSetAsPointer(Value);
1274 end;
1275
1276 procedure TIBXSQLVAR.xSetAsQuad(Value: TISC_QUAD);
1277 begin
1278 if IsNullable then
1279 IsNull := False;
1280 if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) and
1281 (FXSQLVAR^.sqltype and (not 1) <> SQL_ARRAY) then
1282 IBError(ibxeInvalidDataConversion, [nil]);
1283 FXSQLVAR^.sqllen := SizeOf(TISC_QUAD);
1284 IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1285 PISC_QUAD(FXSQLVAR^.sqldata)^ := Value;
1286 FModified := True;
1287 end;
1288
1289 procedure TIBXSQLVAR.SetAsQuad(Value: TISC_QUAD);
1290 var
1291 i: Integer;
1292 begin
1293 if FUniqueName then
1294 xSetAsQuad(Value)
1295 else
1296 for i := 0 to FParent.FCount - 1 do
1297 if FParent[i].FName = FName then
1298 FParent[i].xSetAsQuad(Value);
1299 end;
1300
1301 procedure TIBXSQLVAR.xSetAsShort(Value: Short);
1302 begin
1303 if IsNullable then
1304 IsNull := False;
1305
1306 FXSQLVAR^.sqltype := SQL_SHORT or (FXSQLVAR^.sqltype and 1);
1307 FXSQLVAR^.sqllen := SizeOf(Short);
1308 FXSQLVAR^.sqlscale := 0;
1309 IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1310 PShort(FXSQLVAR^.sqldata)^ := Value;
1311 FModified := True;
1312 end;
1313
1314 procedure TIBXSQLVAR.SetAsShort(Value: Short);
1315 var
1316 i: Integer;
1317 begin
1318 if FUniqueName then
1319 xSetAsShort(Value)
1320 else
1321 for i := 0 to FParent.FCount - 1 do
1322 if FParent[i].FName = FName then
1323 FParent[i].xSetAsShort(Value);
1324 end;
1325
1326 procedure TIBXSQLVAR.xSetAsString(Value: String);
1327 var
1328 stype: Integer;
1329 ss: TStringStream;
1330
1331 procedure SetStringValue;
1332 var
1333 i: Integer;
1334 begin
1335 if (FXSQLVAR^.sqlname = 'DB_KEY') or {do not localize}
1336 (FXSQLVAR^.sqlname = 'RDB$DB_KEY') then {do not localize}
1337 Move(Value[1], FXSQLVAR^.sqldata^, FXSQLVAR^.sqllen)
1338 else begin
1339 FXSQLVAR^.sqltype := SQL_TEXT or (FXSQLVAR^.sqltype and 1);
1340 FXSQLVAR^.sqllen := Length(Value);
1341 IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen + 1);
1342 if (Length(Value) > 0) then
1343 Move(Value[1], FXSQLVAR^.sqldata^, FXSQLVAR^.sqllen);
1344 end;
1345 FModified := True;
1346 end;
1347
1348 begin
1349 if IsNullable then
1350 IsNull := False;
1351
1352 stype := FXSQLVAR^.sqltype and (not 1);
1353 if (stype = SQL_TEXT) or (stype = SQL_VARYING) then
1354 SetStringValue
1355 else begin
1356 if (stype = SQL_BLOB) then
1357 begin
1358 ss := TStringStream.Create(Value);
1359 try
1360 LoadFromStream(ss);
1361 finally
1362 ss.Free;
1363 end;
1364 end
1365 else if Value = '' then
1366 IsNull := True
1367 else if (stype = SQL_TIMESTAMP) or (stype = SQL_TYPE_DATE) or
1368 (stype = SQL_TYPE_TIME) then
1369 xSetAsDateTime(StrToDateTime(Value))
1370 else
1371 SetStringValue;
1372 end;
1373 end;
1374
1375 procedure TIBXSQLVAR.SetAsString(Value: String);
1376 var
1377 i: integer;
1378 begin
1379 if FUniqueName then
1380 xSetAsString(Value)
1381 else
1382 for i := 0 to FParent.FCount - 1 do
1383 if FParent[i].FName = FName then
1384 FParent[i].xSetAsString(Value);
1385 end;
1386
1387 procedure TIBXSQLVAR.xSetAsVariant(Value: Variant);
1388 begin
1389 if VarIsNull(Value) then
1390 IsNull := True
1391 else case VarType(Value) of
1392 varEmpty, varNull:
1393 IsNull := True;
1394 varSmallint, varInteger, varByte,
1395 varWord, varShortInt:
1396 AsLong := Value;
1397 varInt64:
1398 AsInt64 := Value;
1399 varSingle, varDouble:
1400 AsDouble := Value;
1401 varCurrency:
1402 AsCurrency := Value;
1403 varBoolean:
1404 if Value then
1405 AsLong := ISC_TRUE
1406 else
1407 AsLong := ISC_FALSE;
1408 varDate:
1409 AsDateTime := Value;
1410 varOleStr, varString:
1411 AsString := Value;
1412 varArray:
1413 IBError(ibxeNotSupported, [nil]);
1414 varByRef, varDispatch, varError, varUnknown, varVariant:
1415 IBError(ibxeNotPermitted, [nil]);
1416 end;
1417 end;
1418
1419 procedure TIBXSQLVAR.SetAsVariant(Value: Variant);
1420 var
1421 i: integer;
1422 begin
1423 if FUniqueName then
1424 xSetAsVariant(Value)
1425 else
1426 for i := 0 to FParent.FCount - 1 do
1427 if FParent[i].FName = FName then
1428 FParent[i].xSetAsVariant(Value);
1429 end;
1430
1431 procedure TIBXSQLVAR.xSetAsXSQLVAR(Value: PXSQLVAR);
1432 var
1433 sqlind: PShort;
1434 sqldata: PChar;
1435 local_sqllen: Integer;
1436 begin
1437 sqlind := FXSQLVAR^.sqlind;
1438 sqldata := FXSQLVAR^.sqldata;
1439 Move(Value^, FXSQLVAR^, SizeOf(TXSQLVAR));
1440 FXSQLVAR^.sqlind := sqlind;
1441 FXSQLVAR^.sqldata := sqldata;
1442 if (Value^.sqltype and 1 = 1) then
1443 begin
1444 if (FXSQLVAR^.sqlind = nil) then
1445 IBAlloc(FXSQLVAR^.sqlind, 0, SizeOf(Short));
1446 FXSQLVAR^.sqlind^ := Value^.sqlind^;
1447 end
1448 else
1449 if (FXSQLVAR^.sqlind <> nil) then
1450 ReallocMem(FXSQLVAR^.sqlind, 0);
1451 if ((FXSQLVAR^.sqltype and (not 1)) = SQL_VARYING) then
1452 local_sqllen := FXSQLVAR^.sqllen + 2
1453 else
1454 local_sqllen := FXSQLVAR^.sqllen;
1455 FXSQLVAR^.sqlscale := Value^.sqlscale;
1456 IBAlloc(FXSQLVAR^.sqldata, 0, local_sqllen);
1457 Move(Value^.sqldata[0], FXSQLVAR^.sqldata[0], local_sqllen);
1458 FModified := True;
1459 end;
1460
1461 procedure TIBXSQLVAR.SetAsXSQLVAR(Value: PXSQLVAR);
1462 var
1463 i: Integer;
1464 begin
1465 if FUniqueName then
1466 xSetAsXSQLVAR(Value)
1467 else
1468 for i := 0 to FParent.FCount - 1 do
1469 if FParent[i].FName = FName then
1470 FParent[i].xSetAsXSQLVAR(Value);
1471 end;
1472
1473 procedure TIBXSQLVAR.xSetIsNull(Value: Boolean);
1474 begin
1475 if Value then
1476 begin
1477 if not IsNullable then
1478 IsNullable := True;
1479
1480 if Assigned(FXSQLVAR^.sqlind) then
1481 FXSQLVAR^.sqlind^ := -1;
1482 FModified := True;
1483 end
1484 else
1485 if ((not Value) and IsNullable) then
1486 begin
1487 if Assigned(FXSQLVAR^.sqlind) then
1488 FXSQLVAR^.sqlind^ := 0;
1489 FModified := True;
1490 end;
1491 end;
1492
1493 procedure TIBXSQLVAR.SetIsNull(Value: Boolean);
1494 var
1495 i: Integer;
1496 begin
1497 if FUniqueName then
1498 xSetIsNull(Value)
1499 else
1500 for i := 0 to FParent.FCount - 1 do
1501 if FParent[i].FName = FName then
1502 FParent[i].xSetIsNull(Value);
1503 end;
1504
1505 procedure TIBXSQLVAR.xSetIsNullable(Value: Boolean);
1506 begin
1507 if (Value <> IsNullable) then
1508 begin
1509 if Value then
1510 begin
1511 FXSQLVAR^.sqltype := FXSQLVAR^.sqltype or 1;
1512 IBAlloc(FXSQLVAR^.sqlind, 0, SizeOf(Short));
1513 end
1514 else
1515 begin
1516 FXSQLVAR^.sqltype := FXSQLVAR^.sqltype and (not 1);
1517 ReallocMem(FXSQLVAR^.sqlind, 0);
1518 end;
1519 end;
1520 end;
1521
1522 procedure TIBXSQLVAR.SetIsNullable(Value: Boolean);
1523 var
1524 i: Integer;
1525 begin
1526 if FUniqueName then
1527 xSetIsNullable(Value)
1528 else
1529 for i := 0 to FParent.FCount - 1 do
1530 if FParent[i].FName = FName then
1531 FParent[i].xSetIsNullable(Value);
1532 end;
1533
1534 procedure TIBXSQLVAR.Clear;
1535 begin
1536 IsNull := true;
1537 end;
1538
1539
1540 { TIBXSQLDA }
1541 constructor TIBXSQLDA.Create(Query: TIBSQL; sqldaType: TIBXSQLDAType);
1542 begin
1543 inherited Create;
1544 FSQL := Query;
1545 FSize := 0;
1546 FUniqueRelationName := '';
1547 FInputSQLDA := sqldaType = daInput;
1548 end;
1549
1550 destructor TIBXSQLDA.Destroy;
1551 var
1552 i: Integer;
1553 begin
1554 if FXSQLDA <> nil then
1555 begin
1556 for i := 0 to FSize - 1 do
1557 begin
1558 FreeMem(FXSQLVARs[i].FXSQLVAR^.sqldata);
1559 FreeMem(FXSQLVARs[i].FXSQLVAR^.sqlind);
1560 FXSQLVARs[i].Free ;
1561 end;
1562 FreeMem(FXSQLDA);
1563 FXSQLDA := nil;
1564 FXSQLVARs := nil;
1565 end;
1566 inherited Destroy;
1567 end;
1568
1569 procedure TIBXSQLDA.SetParamName(FieldName: String; Idx: Integer;
1570 UniqueName: boolean);
1571 var
1572 fn: string;
1573 begin
1574 {$ifdef UseCaseSensitiveParamName}
1575 FXSQLVARs[Idx].FName := AnsiUpperCase(FieldName);
1576 {$else}
1577 FXSQLVARs[Idx].FName := FieldName;
1578 {$endif}
1579 FXSQLVARs[Idx].FIndex := Idx;
1580 FXSQLVARs[Idx].FUniqueName := UniqueName
1581 end;
1582
1583 function TIBXSQLDA.GetModified: Boolean;
1584 var
1585 i: Integer;
1586 begin
1587 result := False;
1588 for i := 0 to FCount - 1 do
1589 if FXSQLVARs[i].Modified then
1590 begin
1591 result := True;
1592 exit;
1593 end;
1594 end;
1595
1596 function TIBXSQLDA.GetRecordSize: Integer;
1597 begin
1598 result := SizeOf(TIBXSQLDA) + XSQLDA_LENGTH(FSize);
1599 end;
1600
1601 function TIBXSQLDA.GetXSQLDA: PXSQLDA;
1602 begin
1603 result := FXSQLDA;
1604 end;
1605
1606 function TIBXSQLDA.GetXSQLVAR(Idx: Integer): TIBXSQLVAR;
1607 begin
1608 if (Idx < 0) or (Idx >= FCount) then
1609 IBError(ibxeXSQLDAIndexOutOfRange, [nil]);
1610 result := FXSQLVARs[Idx]
1611 end;
1612
1613 function TIBXSQLDA.ByName(Idx: String): TIBXSQLVAR;
1614 begin
1615 result := GetXSQLVARByName(Idx);
1616 if result = nil then
1617 IBError(ibxeFieldNotFound, [Idx]);
1618 end;
1619
1620 function TIBXSQLDA.GetXSQLVARByName(Idx: String): TIBXSQLVAR;
1621 var
1622 s: String;
1623 i: Integer;
1624 begin
1625 {$ifdef ALLOWDIALECT3PARAMNAMES}
1626 s := FormatIdentifierValueNC(FSQL.Database.SQLDialect, Idx);
1627 {$else}
1628 {$ifdef UseCaseSensitiveParamName}
1629 s := AnsiUpperCase(Idx);
1630 {$else}
1631 s := Idx;
1632 {$endif}
1633 {$endif}
1634 for i := 0 to FCount - 1 do
1635 if Vars[i].FName = s then
1636 begin
1637 Result := FXSQLVARs[i];
1638 Exit;
1639 end;
1640 Result := nil;
1641 end;
1642
1643 procedure TIBXSQLDA.Initialize;
1644
1645 function VarByName(idx: string; limit: integer): TIBXSQLVAR;
1646 var
1647 k: integer;
1648 begin
1649 for k := 0 to limit do
1650 if FXSQLVARs[k].FName = idx then
1651 begin
1652 Result := FXSQLVARs[k];
1653 Exit;
1654 end;
1655 Result := nil;
1656 end;
1657
1658 var
1659 i, j, j_len: Integer;
1660 st: String;
1661 bUnique: Boolean;
1662 sBaseName: string;
1663 begin
1664 bUnique := True;
1665 if FXSQLDA <> nil then
1666 begin
1667 for i := 0 to FCount - 1 do
1668 begin
1669 with FXSQLVARs[i].Data^ do
1670 begin
1671
1672 {First get the unique relation name, if any}
1673
1674 if bUnique and (strpas(relname) <> '') then
1675 begin
1676 if FUniqueRelationName = '' then
1677 FUniqueRelationName := strpas(relname)
1678 else
1679 if strpas(relname) <> FUniqueRelationName then
1680 begin
1681 FUniqueRelationName := '';
1682 bUnique := False;
1683 end;
1684 end;
1685
1686 {If an output SQLDA then copy the aliasnames to the FName list. Ensure
1687 that they are all upper case only and disambiguated.
1688 }
1689
1690 if not FInputSQLDA then
1691 begin
1692 st := Space2Underscore(AnsiUppercase(strpas(aliasname)));
1693 if st = '' then
1694 begin
1695 sBaseName := 'F_'; {do not localize}
1696 aliasname_length := 2;
1697 j := 1; j_len := 1;
1698 st := sBaseName + IntToStr(j);
1699 end
1700 else
1701 begin
1702 j := 0; j_len := 0;
1703 sBaseName := st;
1704 end;
1705
1706 {Look for other columns with the same name and make unique}
1707
1708 while VarByName(st,i-1) <> nil do
1709 begin
1710 Inc(j);
1711 j_len := Length(IntToStr(j));
1712 if j_len + Length(sBaseName) > 31 then
1713 st := Copy(sBaseName, 1, 31 - j_len) + IntToStr(j)
1714 else
1715 st := sBaseName + IntToStr(j);
1716 end;
1717
1718 FXSQLVARs[i].FName := st;
1719 end;
1720
1721 {Finally initialise the XSQLVAR}
1722
1723 FXSQLVARs[i].FIndex := i;
1724
1725 case sqltype and (not 1) of
1726 SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
1727 SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT,
1728 SQL_LONG, SQL_INT64, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT: begin
1729 if (sqllen = 0) then
1730 { Make sure you get a valid pointer anyway
1731 select '' from foo }
1732 IBAlloc(sqldata, 0, 1)
1733 else
1734 IBAlloc(sqldata, 0, sqllen)
1735 end;
1736 SQL_VARYING: begin
1737 IBAlloc(sqldata, 0, sqllen + 2);
1738 end;
1739 else
1740 IBError(ibxeUnknownSQLDataType, [sqltype and (not 1)])
1741 end;
1742 if (sqltype and 1 = 1) then
1743 IBAlloc(sqlind, 0, SizeOf(Short))
1744 else
1745 if (sqlind <> nil) then
1746 ReallocMem(sqlind, 0);
1747 end;
1748 end;
1749 end;
1750 end;
1751
1752 procedure TIBXSQLDA.SetCount(Value: Integer);
1753 var
1754 i, OldSize: Integer;
1755 p : PXSQLVAR;
1756 begin
1757 FCount := Value;
1758 if FCount = 0 then
1759 FUniqueRelationName := ''
1760 else
1761 begin
1762 if FSize > 0 then
1763 OldSize := XSQLDA_LENGTH(FSize)
1764 else
1765 OldSize := 0;
1766 if FCount > FSize then
1767 begin
1768 IBAlloc(FXSQLDA, OldSize, XSQLDA_LENGTH(FCount));
1769 SetLength(FXSQLVARs, FCount);
1770 FXSQLDA^.version := SQLDA_VERSION1;
1771 p := @FXSQLDA^.sqlvar[0];
1772 for i := 0 to FCount - 1 do
1773 begin
1774 if i >= FSize then
1775 FXSQLVARs[i] := TIBXSQLVAR.Create(self, FSQL);
1776 FXSQLVARs[i].FXSQLVAR := p;
1777 p := Pointer(PChar(p) + sizeof(FXSQLDA^.sqlvar));
1778 end;
1779 FSize := FCount;
1780 end;
1781 if FSize > 0 then
1782 begin
1783 FXSQLDA^.sqln := Value;
1784 FXSQLDA^.sqld := Value;
1785 end;
1786 end;
1787 end;
1788
1789 { TIBOutputDelimitedFile }
1790
1791 destructor TIBOutputDelimitedFile.Destroy;
1792 begin
1793 {$IFDEF UNIX}
1794 if FHandle <> -1 then
1795 fpclose(FHandle);
1796 {$ELSE}
1797 if FHandle <> 0 then
1798 begin
1799 FlushFileBuffers(FHandle);
1800 CloseHandle(FHandle);
1801 end;
1802 {$ENDIF}
1803 inherited Destroy;
1804 end;
1805
1806 procedure TIBOutputDelimitedFile.ReadyFile;
1807 var
1808 i: Integer;
1809 {$IFDEF UNIX}
1810 BytesWritten: cint;
1811 {$ELSE}
1812 BytesWritten: DWORD;
1813 {$ENDIF}
1814 st: string;
1815 begin
1816 if FColDelimiter = '' then
1817 FColDelimiter := TAB;
1818 if FRowDelimiter = '' then
1819 FRowDelimiter := CRLF;
1820 {$IFDEF UNIX}
1821 FHandle := FpOpen(Filename,O_WrOnly or O_Creat);
1822 {$ELSE}
1823 FHandle := CreateFile(PChar(Filename), GENERIC_WRITE, 0, nil, CREATE_ALWAYS,
1824 FILE_ATTRIBUTE_NORMAL, 0);
1825 if FHandle = INVALID_HANDLE_VALUE then
1826 FHandle := 0;
1827 {$ENDIF}
1828 if FOutputTitles then
1829 begin
1830 for i := 0 to Columns.Count - 1 do
1831 if i = 0 then
1832 st := strpas(Columns[i].Data^.aliasname)
1833 else
1834 st := st + FColDelimiter + strpas(Columns[i].Data^.aliasname);
1835 st := st + FRowDelimiter;
1836 {$IFDEF UNIX}
1837 if FHandle <> -1 then
1838 BytesWritten := FpWrite(FHandle,st[1],Length(st));
1839 if BytesWritten = -1 then
1840 raise Exception.Create('File Write Error');
1841 {$ELSE}
1842 WriteFile(FHandle, st[1], Length(st), BytesWritten, nil);
1843 {$ENDIF}
1844 end;
1845 end;
1846
1847 function TIBOutputDelimitedFile.WriteColumns: Boolean;
1848 var
1849 i: Integer;
1850 {$IFDEF UNIX}
1851 BytesWritten: cint;
1852 {$ELSE}
1853 BytesWritten: DWORD;
1854 {$ENDIF}
1855 st: string;
1856 begin
1857 result := False;
1858 {$IFDEF UNIX}
1859 if FHandle <> -1 then
1860 {$ELSE}
1861 if FHandle <> 0 then
1862 {$ENDIF}
1863 begin
1864 st := '';
1865 for i := 0 to Columns.Count - 1 do
1866 begin
1867 if i > 0 then
1868 st := st + FColDelimiter;
1869 st := st + StripString(Columns[i].AsString, FColDelimiter + FRowDelimiter);
1870 end;
1871 st := st + FRowDelimiter;
1872 {$IFDEF UNIX}
1873 BytesWritten := FpWrite(FHandle,st[1],Length(st));
1874 {$ELSE}
1875 WriteFile(FHandle, st[1], Length(st), BytesWritten, nil);
1876 {$ENDIF}
1877 if BytesWritten = DWORD(Length(st)) then
1878 result := True;
1879 end
1880 end;
1881
1882 { TIBInputDelimitedFile }
1883
1884 destructor TIBInputDelimitedFile.Destroy;
1885 begin
1886 FFile.Free;
1887 inherited Destroy;
1888 end;
1889
1890 function TIBInputDelimitedFile.GetColumn(var Col: string): Integer;
1891 var
1892 c: Char;
1893 BytesRead: Integer;
1894
1895 procedure ReadInput;
1896 begin
1897 if FLookAhead <> NULL_TERMINATOR then
1898 begin
1899 c := FLookAhead;
1900 BytesRead := 1;
1901 FLookAhead := NULL_TERMINATOR;
1902 end else
1903 BytesRead := FFile.Read(c, 1);
1904 end;
1905
1906 procedure CheckCRLF(Delimiter: string);
1907 begin
1908 if (c = CR) and (Pos(LF, Delimiter) > 0) then {mbcs ok}
1909 begin
1910 BytesRead := FFile.Read(c, 1);
1911 if (BytesRead = 1) and (c <> #10) then
1912 FLookAhead := c
1913 end;
1914 end;
1915
1916 begin
1917 Col := '';
1918 result := 0;
1919 ReadInput;
1920 while BytesRead <> 0 do begin
1921 if Pos(c, FColDelimiter) > 0 then {mbcs ok}
1922 begin
1923 CheckCRLF(FColDelimiter);
1924 result := 1;
1925 break;
1926 end else if Pos(c, FRowDelimiter) > 0 then {mbcs ok}
1927 begin
1928 CheckCRLF(FRowDelimiter);
1929 result := 2;
1930 break;
1931 end else
1932 Col := Col + c;
1933 ReadInput;
1934 end;
1935 end;
1936
1937 function TIBInputDelimitedFile.ReadParameters: Boolean;
1938 var
1939 i, curcol: Integer;
1940 Col: string;
1941 begin
1942 result := False;
1943 if not FEOF then begin
1944 curcol := 0;
1945 repeat
1946 i := GetColumn(Col);
1947 if (i = 0) then
1948 FEOF := True;
1949 if (curcol < Params.Count) then
1950 begin
1951 try
1952 if (Col = '') and
1953 (ReadBlanksAsNull) then
1954 Params[curcol].IsNull := True
1955 else
1956 Params[curcol].AsString := Col;
1957 Inc(curcol);
1958 except
1959 on E: Exception do begin
1960 if not (FEOF and (curcol = Params.Count)) then
1961 raise;
1962 end;
1963 end;
1964 end;
1965 until (FEOF) or (i = 2);
1966 result := ((FEOF) and (curcol = Params.Count)) or
1967 (not FEOF);
1968 end;
1969 end;
1970
1971 procedure TIBInputDelimitedFile.ReadyFile;
1972 begin
1973 if FColDelimiter = '' then
1974 FColDelimiter := TAB;
1975 if FRowDelimiter = '' then
1976 FRowDelimiter := CRLF;
1977 FLookAhead := NULL_TERMINATOR;
1978 FEOF := False;
1979 if FFile <> nil then
1980 FFile.Free;
1981 FFile := TFileStream.Create(FFilename, fmOpenRead or fmShareDenyWrite);
1982 if FSkipTitles then
1983 ReadParameters;
1984 end;
1985
1986 { TIBOutputRawFile }
1987 destructor TIBOutputRawFile.Destroy;
1988 begin
1989 {$IFDEF UNIX}
1990 if FHandle <> -1 then
1991 fpclose(FHandle);
1992 {$ELSE}
1993 if FHandle <> 0 then
1994 begin
1995 FlushFileBuffers(FHandle);
1996 CloseHandle(FHandle);
1997 end;
1998 {$ENDIF}
1999 inherited Destroy;
2000 end;
2001
2002 procedure TIBOutputRawFile.ReadyFile;
2003 begin
2004 {$IFDEF UNIX}
2005 FHandle := FpOpen(Filename,O_WrOnly or O_Creat);
2006 {$ELSE}
2007 FHandle := CreateFile(PChar(Filename), GENERIC_WRITE, 0, nil, CREATE_ALWAYS,
2008 FILE_ATTRIBUTE_NORMAL, 0);
2009 if FHandle = INVALID_HANDLE_VALUE then
2010 FHandle := 0;
2011 {$ENDIF}
2012 end;
2013
2014 function TIBOutputRawFile.WriteColumns: Boolean;
2015 var
2016 i: Integer;
2017 BytesWritten: DWord;
2018 begin
2019 result := False;
2020 if FHandle <> 0 then
2021 begin
2022 for i := 0 to Columns.Count - 1 do
2023 begin
2024 {$IFDEF UNIX}
2025 BytesWritten := FpWrite(FHandle,Columns[i].Data^.sqldata^, Columns[i].Data^.sqllen);
2026 {$ELSE}
2027 WriteFile(FHandle, Columns[i].Data^.sqldata^, Columns[i].Data^.sqllen,
2028 BytesWritten, nil);
2029 {$ENDIF}
2030 if BytesWritten <> DWORD(Columns[i].Data^.sqllen) then
2031 exit;
2032 end;
2033 result := True;
2034 end;
2035 end;
2036
2037 { TIBInputRawFile }
2038 destructor TIBInputRawFile.Destroy;
2039 begin
2040 {$IFDEF UNIX}
2041 if FHandle <> -1 then
2042 fpclose(FHandle);
2043 {$ELSE}
2044 if FHandle <> 0 then
2045 CloseHandle(FHandle);
2046 {$ENDIF}
2047 inherited Destroy;
2048 end;
2049
2050 function TIBInputRawFile.ReadParameters: Boolean;
2051 var
2052 i: Integer;
2053 BytesRead: DWord;
2054 begin
2055 result := False;
2056 {$IFDEF UNIX}
2057 if FHandle <> -1 then
2058 {$ELSE}
2059 if FHandle <> 0 then
2060 {$ENDIF}
2061 begin
2062 for i := 0 to Params.Count - 1 do
2063 begin
2064 {$IFDEF UNIX}
2065 BytesRead := FpRead(FHandle,Params[i].Data^.sqldata^,Params[i].Data^.sqllen);
2066 {$ELSE}
2067 ReadFile(FHandle, Params[i].Data^.sqldata^, Params[i].Data^.sqllen,
2068 BytesRead, nil);
2069 {$ENDIF}
2070 if BytesRead <> DWORD(Params[i].Data^.sqllen) then
2071 exit;
2072 end;
2073 result := True;
2074 end;
2075 end;
2076
2077 procedure TIBInputRawFile.ReadyFile;
2078 begin
2079 {$IFDEF UNIX}
2080 if FHandle <> -1 then
2081 fpclose(FHandle);
2082 FHandle := FpOpen(Filename,O_RdOnly);
2083 if FHandle = -1 then
2084 raise Exception.CreateFmt('Unable to open file %s',[Filename]);
2085 {$ELSE}
2086 if FHandle <> 0 then
2087 CloseHandle(FHandle);
2088 FHandle := CreateFile(PChar(Filename), GENERIC_READ, 0, nil, OPEN_EXISTING,
2089 FILE_FLAG_SEQUENTIAL_SCAN, 0);
2090 if FHandle = INVALID_HANDLE_VALUE then
2091 FHandle := 0;
2092 {$ENDIF}
2093 end;
2094
2095 { TIBSQL }
2096 constructor TIBSQL.Create(AOwner: TComponent);
2097 begin
2098 inherited Create(AOwner);
2099 FIBLoaded := False;
2100 CheckIBLoaded;
2101 FIBLoaded := True;
2102 FGenerateParamNames := False;
2103 FGoToFirstRecordOnExecute := True;
2104 FBase := TIBBase.Create(Self);
2105 FBase.BeforeDatabaseDisconnect := DoBeforeDatabaseDisconnect;
2106 FBase.BeforeTransactionEnd := BeforeTransactionEnd;
2107 FBOF := False;
2108 FEOF := False;
2109 FPrepared := False;
2110 FRecordCount := 0;
2111 FSQL := TStringList.Create;
2112 TStringList(FSQL).OnChanging := SQLChanging;
2113 FProcessedSQL := TStringList.Create;
2114 FHandle := nil;
2115 FSQLParams := TIBXSQLDA.Create(self,daInput);
2116 FSQLRecord := TIBXSQLDA.Create(self,daOutput);
2117 FSQLType := SQLUnknown;
2118 FParamCheck := True;
2119 FCursor := Name + RandomString(8);
2120 if AOwner is TIBDatabase then
2121 Database := TIBDatabase(AOwner)
2122 else
2123 if AOwner is TIBTransaction then
2124 Transaction := TIBTransaction(AOwner);
2125 end;
2126
2127 destructor TIBSQL.Destroy;
2128 begin
2129 if FIBLoaded then
2130 begin
2131 if (FOpen) then
2132 Close;
2133 if (FHandle <> nil) then
2134 FreeHandle;
2135 FSQL.Free;
2136 FProcessedSQL.Free;
2137 FBase.Free;
2138 FSQLParams.Free;
2139 FSQLRecord.Free;
2140 end;
2141 inherited Destroy;
2142 end;
2143
2144 procedure TIBSQL.BatchInput(InputObject: TIBBatchInput);
2145 begin
2146 if not Prepared then
2147 Prepare;
2148 InputObject.FParams := Self.FSQLParams;
2149 InputObject.ReadyFile;
2150 if FSQLType in [SQLInsert, SQLUpdate, SQLDelete, SQLExecProcedure] then
2151 while InputObject.ReadParameters do
2152 ExecQuery;
2153 end;
2154
2155 procedure TIBSQL.BatchOutput(OutputObject: TIBBatchOutput);
2156 begin
2157 CheckClosed;
2158 if not Prepared then
2159 Prepare;
2160 if FSQLType = SQLSelect then begin
2161 try
2162 ExecQuery;
2163 OutputObject.FColumns := Self.FSQLRecord;
2164 OutputObject.ReadyFile;
2165 if not FGoToFirstRecordOnExecute then
2166 Next;
2167 while (not Eof) and (OutputObject.WriteColumns) do
2168 Next;
2169 finally
2170 Close;
2171 end;
2172 end;
2173 end;
2174
2175 procedure TIBSQL.CheckClosed;
2176 begin
2177 if FOpen then IBError(ibxeSQLOpen, [nil]);
2178 end;
2179
2180 procedure TIBSQL.CheckOpen;
2181 begin
2182 if not FOpen then IBError(ibxeSQLClosed, [nil]);
2183 end;
2184
2185 procedure TIBSQL.CheckValidStatement;
2186 begin
2187 FBase.CheckTransaction;
2188 if (FHandle = nil) then
2189 IBError(ibxeInvalidStatementHandle, [nil]);
2190 end;
2191
2192 procedure TIBSQL.Close;
2193 var
2194 isc_res: ISC_STATUS;
2195 begin
2196 try
2197 if (FHandle <> nil) and (SQLType = SQLSelect) and FOpen then begin
2198 isc_res := Call(
2199 isc_dsql_free_statement(StatusVector, @FHandle, DSQL_close),
2200 False);
2201 if (StatusVector^ = 1) and (isc_res > 0) and
2202 not CheckStatusVector(
2203 [isc_bad_stmt_handle, isc_dsql_cursor_close_err]) then
2204 IBDatabaseError;
2205 end;
2206 finally
2207 FEOF := False;
2208 FBOF := False;
2209 FOpen := False;
2210 FRecordCount := 0;
2211 end;
2212 end;
2213
2214 function TIBSQL.Call(ErrCode: ISC_STATUS; RaiseError: Boolean): ISC_STATUS;
2215 begin
2216 result := 0;
2217 if Transaction <> nil then
2218 result := Transaction.Call(ErrCode, RaiseError)
2219 else
2220 if RaiseError and (ErrCode > 0) then
2221 IBDataBaseError;
2222 end;
2223
2224 function TIBSQL.Current: TIBXSQLDA;
2225 begin
2226 result := FSQLRecord;
2227 end;
2228
2229 function TIBSQL.GetFieldCount: integer;
2230 begin
2231 Result := FSQLRecord.Count
2232 end;
2233
2234 procedure TIBSQL.SetUniqueParamNames(AValue: Boolean);
2235 begin
2236 if FUniqueParamNames = AValue then Exit;
2237 FreeHandle;
2238 FUniqueParamNames := AValue;
2239 end;
2240
2241 procedure TIBSQL.DoBeforeDatabaseDisconnect(Sender: TObject);
2242 begin
2243 if (FHandle <> nil) then begin
2244 Close;
2245 FreeHandle;
2246 end;
2247 end;
2248
2249 procedure TIBSQL.ExecQuery;
2250 var
2251 fetch_res: ISC_STATUS;
2252 begin
2253 CheckClosed;
2254 if not Prepared then Prepare;
2255 CheckValidStatement;
2256 case FSQLType of
2257 SQLSelect: begin
2258 Call(isc_dsql_execute2(StatusVector,
2259 TRHandle,
2260 @FHandle,
2261 Database.SQLDialect,
2262 FSQLParams.AsXSQLDA,
2263 nil), True);
2264 Call(
2265 isc_dsql_set_cursor_name(StatusVector, @FHandle, PChar(FCursor), 0),
2266 True);
2267 FOpen := True;
2268 FBOF := True;
2269 FEOF := False;
2270 FRecordCount := 0;
2271 if FGoToFirstRecordOnExecute then
2272 Next;
2273 end;
2274 SQLExecProcedure: begin
2275 fetch_res := Call(isc_dsql_execute2(StatusVector,
2276 TRHandle,
2277 @FHandle,
2278 Database.SQLDialect,
2279 FSQLParams.AsXSQLDA,
2280 FSQLRecord.AsXSQLDA), True);
2281 (* if (fetch_res <> 0) and (fetch_res <> isc_deadlock) then
2282 begin
2283 { Sometimes a prepared stored procedure appears to get
2284 off sync on the server ....This code is meant to try
2285 to work around the problem simply by "retrying". This
2286 need to be reproduced and fixed.
2287 }
2288 isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0,
2289 PChar(FProcessedSQL.Text), 1, nil);
2290 Call(isc_dsql_execute2(StatusVector,
2291 TRHandle,
2292 @FHandle,
2293 Database.SQLDialect,
2294 FSQLParams.AsXSQLDA,
2295 FSQLRecord.AsXSQLDA), True);
2296 end; *)
2297 end
2298 else
2299 Call(isc_dsql_execute(StatusVector,
2300 TRHandle,
2301 @FHandle,
2302 Database.SQLDialect,
2303 FSQLParams.AsXSQLDA), True)
2304 end;
2305 if not (csDesigning in ComponentState) then
2306 MonitorHook.SQLExecute(Self);
2307 end;
2308
2309 function TIBSQL.GetEOF: Boolean;
2310 begin
2311 result := FEOF or not FOpen;
2312 end;
2313
2314 function TIBSQL.FieldByName(FieldName: String): TIBXSQLVAR;
2315 var
2316 i: Integer;
2317 begin
2318 i := GetFieldIndex(FieldName);
2319 if (i < 0) then
2320 IBError(ibxeFieldNotFound, [FieldName]);
2321 result := GetFields(i);
2322 end;
2323
2324 function TIBSQL.ParamByName(ParamName: String): TIBXSQLVAR;
2325 begin
2326 Result := Params.ByName(ParamName);
2327 end;
2328
2329 function TIBSQL.GetFields(const Idx: Integer): TIBXSQLVAR;
2330 begin
2331 if (Idx < 0) or (Idx >= FSQLRecord.Count) then
2332 IBError(ibxeFieldNotFound, [IntToStr(Idx)]);
2333 result := FSQLRecord[Idx];
2334 end;
2335
2336 function TIBSQL.GetFieldIndex(FieldName: String): Integer;
2337 begin
2338 if (FSQLRecord.GetXSQLVarByName(FieldName) = nil) then
2339 result := -1
2340 else
2341 result := FSQLRecord.GetXSQLVarByName(FieldName).Index;
2342 end;
2343
2344 function TIBSQL.Next: TIBXSQLDA;
2345 var
2346 fetch_res: ISC_STATUS;
2347 begin
2348 result := nil;
2349 if not FEOF then begin
2350 CheckOpen;
2351 { Go to the next record... }
2352 fetch_res :=
2353 Call(isc_dsql_fetch(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.AsXSQLDA), False);
2354 if (fetch_res = 100) or (CheckStatusVector([isc_dsql_cursor_err])) then begin
2355 FEOF := True;
2356 end else if (fetch_res > 0) then begin
2357 try
2358 IBDataBaseError;
2359 except
2360 Close;
2361 raise;
2362 end;
2363 end else begin
2364 Inc(FRecordCount);
2365 FBOF := False;
2366 result := FSQLRecord;
2367 end;
2368 if not (csDesigning in ComponentState) then
2369 MonitorHook.SQLFetch(Self);
2370 end;
2371 end;
2372
2373 procedure TIBSQL.FreeHandle;
2374 var
2375 isc_res: ISC_STATUS;
2376 begin
2377 try
2378 { The following two lines merely set the SQLDA count
2379 variable FCount to 0, but do not deallocate
2380 That way the allocations can be reused for
2381 a new query sring in the same SQL instance }
2382 FSQLRecord.Count := 0;
2383 FSQLParams.Count := 0;
2384 if FHandle <> nil then begin
2385 isc_res :=
2386 Call(isc_dsql_free_statement(StatusVector, @FHandle, DSQL_drop), False);
2387 if (StatusVector^ = 1) and (isc_res > 0) and (isc_res <> isc_bad_stmt_handle) then
2388 IBDataBaseError;
2389 end;
2390 finally
2391 FPrepared := False;
2392 FHandle := nil;
2393 end;
2394 end;
2395
2396 function TIBSQL.GetDatabase: TIBDatabase;
2397 begin
2398 result := FBase.Database;
2399 end;
2400
2401 function TIBSQL.GetDBHandle: PISC_DB_HANDLE;
2402 begin
2403 result := FBase.DBHandle;
2404 end;
2405
2406 function TIBSQL.GetPlan: String;
2407 var
2408 result_buffer: array[0..16384] of Char;
2409 result_length, i: Integer;
2410 info_request: Char;
2411 begin
2412 if (not Prepared) or
2413 (not (FSQLType in [SQLSelect, SQLSelectForUpdate,
2414 {TODO: SQLExecProcedure, }
2415 SQLUpdate, SQLDelete])) then
2416 result := ''
2417 else begin
2418 info_request := Char(isc_info_sql_get_plan);
2419 Call(isc_dsql_sql_info(StatusVector, @FHandle, 2, @info_request,
2420 SizeOf(result_buffer), result_buffer), True);
2421 if (result_buffer[0] <> Char(isc_info_sql_get_plan)) then
2422 IBError(ibxeUnknownError, [nil]);
2423 result_length := isc_vax_integer(@result_buffer[1], 2);
2424 SetString(result, nil, result_length);
2425 for i := 1 to result_length do
2426 result[i] := result_buffer[i + 2];
2427 result := Trim(result);
2428 end;
2429 end;
2430
2431 function TIBSQL.GetRecordCount: Integer;
2432 begin
2433 result := FRecordCount;
2434 end;
2435
2436 function TIBSQL.GetRowsAffected: Integer;
2437 var
2438 result_buffer: array[0..1048] of Char;
2439 info_request: Char;
2440 begin
2441 if not Prepared then
2442 result := -1
2443 else begin
2444 info_request := Char(isc_info_sql_records);
2445 if isc_dsql_sql_info(StatusVector, @FHandle, 1, @info_request,
2446 SizeOf(result_buffer), result_buffer) > 0 then
2447 IBDatabaseError;
2448 if (result_buffer[0] <> Char(isc_info_sql_records)) then
2449 result := -1
2450 else
2451 case SQLType of
2452 SQLUpdate: Result := isc_vax_integer(@result_buffer[6], 4);
2453 SQLDelete: Result := isc_vax_integer(@result_buffer[13], 4);
2454 SQLInsert: Result := isc_vax_integer(@result_buffer[27], 4);
2455 else Result := -1 ;
2456 end ;
2457 end;
2458 end;
2459
2460 function TIBSQL.GetSQLParams: TIBXSQLDA;
2461 begin
2462 if not Prepared then
2463 Prepare;
2464 result := FSQLParams;
2465 end;
2466
2467 function TIBSQL.GetTransaction: TIBTransaction;
2468 begin
2469 result := FBase.Transaction;
2470 end;
2471
2472 function TIBSQL.GetTRHandle: PISC_TR_HANDLE;
2473 begin
2474 result := FBase.TRHandle;
2475 end;
2476
2477 {
2478 Preprocess SQL
2479 Using FSQL, process the typed SQL and put the process SQL
2480 in FProcessedSQL and parameter names in FSQLParams
2481 }
2482 procedure TIBSQL.PreprocessSQL;
2483 var
2484 cCurChar, cNextChar, cQuoteChar: Char;
2485 sSQL, sProcessedSQL, sParamName: String;
2486 i, iLenSQL, iSQLPos: Integer;
2487 iCurState {$ifdef ALLOWDIALECT3PARAMNAMES}, iCurParamState {$endif}: Integer;
2488 iParamSuffix: Integer;
2489 slNames: TStrings;
2490
2491 const
2492 DefaultState = 0;
2493 CommentState = 1;
2494 QuoteState = 2;
2495 ParamState = 3;
2496 {$ifdef ALLOWDIALECT3PARAMNAMES}
2497 ParamDefaultState = 0;
2498 ParamQuoteState = 1;
2499 {$endif}
2500
2501 procedure AddToProcessedSQL(cChar: Char);
2502 begin
2503 sProcessedSQL[iSQLPos] := cChar;
2504 Inc(iSQLPos);
2505 end;
2506
2507 begin
2508 slNames := TStringList.Create;
2509 try
2510 { Do some initializations of variables }
2511 iParamSuffix := 0;
2512 cQuoteChar := '''';
2513 sSQL := FSQL.Text;
2514 iLenSQL := Length(sSQL);
2515 SetString(sProcessedSQL, nil, iLenSQL + 1);
2516 i := 1;
2517 iSQLPos := 1;
2518 iCurState := DefaultState;
2519 {$ifdef ALLOWDIALECT3PARAMNAMES}
2520 iCurParamState := ParamDefaultState;
2521 {$endif}
2522 { Now, traverse through the SQL string, character by character,
2523 picking out the parameters and formatting correctly for InterBase }
2524 while (i <= iLenSQL) do begin
2525 { Get the current token and a look-ahead }
2526 cCurChar := sSQL[i];
2527 if i = iLenSQL then
2528 cNextChar := #0
2529 else
2530 cNextChar := sSQL[i + 1];
2531 { Now act based on the current state }
2532 case iCurState of
2533 DefaultState: begin
2534 case cCurChar of
2535 '''', '"': begin
2536 cQuoteChar := cCurChar;
2537 iCurState := QuoteState;
2538 end;
2539 '?', ':': begin
2540 iCurState := ParamState;
2541 AddToProcessedSQL('?');
2542 end;
2543 '/': if (cNextChar = '*') then begin
2544 AddToProcessedSQL(cCurChar);
2545 Inc(i);
2546 iCurState := CommentState;
2547 end;
2548 end;
2549 end;
2550 CommentState: begin
2551 if (cNextChar = #0) then
2552 IBError(ibxeSQLParseError, [SEOFInComment])
2553 else if (cCurChar = '*') then begin
2554 if (cNextChar = '/') then
2555 iCurState := DefaultState;
2556 end;
2557 end;
2558 QuoteState: begin
2559 if cNextChar = #0 then
2560 IBError(ibxeSQLParseError, [SEOFInString])
2561 else if (cCurChar = cQuoteChar) then begin
2562 if (cNextChar = cQuoteChar) then begin
2563 AddToProcessedSQL(cCurChar);
2564 Inc(i);
2565 end else
2566 iCurState := DefaultState;
2567 end;
2568 end;
2569 ParamState:
2570 begin
2571 { collect the name of the parameter }
2572 {$ifdef ALLOWDIALECT3PARAMNAMES}
2573 if iCurParamState = ParamDefaultState then
2574 begin
2575 if cCurChar = '"' then
2576 iCurParamState := ParamQuoteState
2577 else
2578 {$endif}
2579 if (cCurChar in ['A'..'Z', 'a'..'z', '0'..'9', '_', '$']) then
2580 sParamName := sParamName + cCurChar
2581 else if FGenerateParamNames then
2582 begin
2583 sParamName := 'IBXParam' + IntToStr(iParamSuffix); {do not localize}
2584 Inc(iParamSuffix);
2585 iCurState := DefaultState;
2586 slNames.AddObject(sParamName,self); //Note local convention
2587 //add pointer to self to mark entry
2588 sParamName := '';
2589 end
2590 else
2591 IBError(ibxeSQLParseError, [SParamNameExpected]);
2592 {$ifdef ALLOWDIALECT3PARAMNAMES}
2593 end
2594 else begin
2595 { determine if Quoted parameter name is finished }
2596 if cCurChar = '"' then
2597 begin
2598 Inc(i);
2599 slNames.Add(sParamName);
2600 SParamName := '';
2601 iCurParamState := ParamDefaultState;
2602 iCurState := DefaultState;
2603 end
2604 else
2605 sParamName := sParamName + cCurChar
2606 end;
2607 {$endif}
2608 { determine if the unquoted parameter name is finished }
2609 if {$ifdef ALLOWDIALECT3PARAMNAMES}(iCurParamState <> ParamQuoteState) and {$endif}
2610 (iCurState <> DefaultState) then
2611 begin
2612 if not (cNextChar in ['A'..'Z', 'a'..'z',
2613 '0'..'9', '_', '$']) then begin
2614 Inc(i);
2615 iCurState := DefaultState;
2616 slNames.Add(sParamName);
2617 sParamName := '';
2618 end;
2619 end;
2620 end;
2621 end;
2622 if iCurState <> ParamState then
2623 AddToProcessedSQL(sSQL[i]);
2624 Inc(i);
2625 end;
2626 AddToProcessedSQL(#0);
2627 FSQLParams.Count := slNames.Count;
2628 for i := 0 to slNames.Count - 1 do
2629 FSQLParams.SetParamName(slNames[i], i,FUniqueParamNames or (slNames.Objects[i] <> nil));
2630 FProcessedSQL.Text := sProcessedSQL;
2631 finally
2632 slNames.Free;
2633 end;
2634 end;
2635
2636 procedure TIBSQL.SetDatabase(Value: TIBDatabase);
2637 begin
2638 FBase.Database := Value;
2639 end;
2640
2641 procedure TIBSQL.Prepare;
2642 var
2643 stmt_len: Integer;
2644 res_buffer: array[0..7] of Char;
2645 type_item: Char;
2646 begin
2647 CheckClosed;
2648 FBase.CheckDatabase;
2649 FBase.CheckTransaction;
2650 if FPrepared then
2651 exit;
2652 if (FSQL.Text = '') then
2653 IBError(ibxeEmptyQuery, [nil]);
2654 if not ParamCheck then
2655 FProcessedSQL.Text := FSQL.Text
2656 else
2657 PreprocessSQL;
2658 if (FProcessedSQL.Text = '') then
2659 IBError(ibxeEmptyQuery, [nil]);
2660 try
2661 Call(isc_dsql_alloc_statement2(StatusVector, DBHandle,
2662 @FHandle), True);
2663 Call(isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0,
2664 PChar(FProcessedSQL.Text), Database.SQLDialect, nil), True);
2665 { After preparing the statement, query the stmt type and possibly
2666 create a FSQLRecord "holder" }
2667 { Get the type of the statement }
2668 type_item := Char(isc_info_sql_stmt_type);
2669 Call(isc_dsql_sql_info(StatusVector, @FHandle, 1, @type_item,
2670 SizeOf(res_buffer), res_buffer), True);
2671 if (res_buffer[0] <> Char(isc_info_sql_stmt_type)) then
2672 IBError(ibxeUnknownError, [nil]);
2673 stmt_len := isc_vax_integer(@res_buffer[1], 2);
2674 FSQLType := TIBSQLTypes(isc_vax_integer(@res_buffer[3], stmt_len));
2675 { Done getting the type }
2676 case FSQLType of
2677 SQLGetSegment,
2678 SQLPutSegment,
2679 SQLStartTransaction: begin
2680 FreeHandle;
2681 IBError(ibxeNotPermitted, [nil]);
2682 end;
2683 SQLCommit,
2684 SQLRollback,
2685 SQLDDL, SQLSetGenerator,
2686 SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
2687 SQLExecProcedure: begin
2688 { We already know how many inputs there are, so... }
2689 if (FSQLParams.FXSQLDA <> nil) and
2690 (Call(isc_dsql_describe_bind(StatusVector, @FHandle, Database.SQLDialect,
2691 FSQLParams.FXSQLDA), False) > 0) then
2692 IBDataBaseError;
2693 FSQLParams.Initialize;
2694 if FSQLType in [SQLSelect, SQLSelectForUpdate,
2695 SQLExecProcedure] then begin
2696 { Allocate an initial output descriptor (with one column) }
2697 FSQLRecord.Count := 1;
2698 { Using isc_dsql_describe, get the right size for the columns... }
2699 Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True);
2700 if FSQLRecord.FXSQLDA^.sqld > FSQLRecord.FXSQLDA^.sqln then begin
2701 FSQLRecord.Count := FSQLRecord.FXSQLDA^.sqld;
2702 Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True);
2703 end else if FSQLRecord.FXSQLDA^.sqld = 0 then
2704 FSQLRecord.Count := 0;
2705 FSQLRecord.Initialize;
2706 end;
2707 end;
2708 end;
2709 FPrepared := True;
2710 if not (csDesigning in ComponentState) then
2711 MonitorHook.SQLPrepare(Self);
2712 except
2713 on E: Exception do begin
2714 if (FHandle <> nil) then
2715 FreeHandle;
2716 if E is EIBInterBaseError then
2717 raise EIBInterBaseError.Create(EIBInterBaseError(E).SQLCode,
2718 EIBInterBaseError(E).IBErrorCode,
2719 EIBInterBaseError(E).Message +
2720 sSQLErrorSeparator + FProcessedSQL.Text)
2721 else
2722 raise;
2723 end;
2724 end;
2725 end;
2726
2727 function TIBSQL.GetUniqueRelationName: String;
2728 begin
2729 if FPrepared and (FSQLType = SQLSelect) then
2730 result := FSQLRecord.UniqueRelationName
2731 else
2732 result := '';
2733 end;
2734
2735 procedure TIBSQL.SetSQL(Value: TStrings);
2736 begin
2737 if FSQL.Text <> Value.Text then
2738 begin
2739 FSQL.BeginUpdate;
2740 try
2741 FSQL.Assign(Value);
2742 finally
2743 FSQL.EndUpdate;
2744 end;
2745 end;
2746 end;
2747
2748 procedure TIBSQL.SetTransaction(Value: TIBTransaction);
2749 begin
2750 FBase.Transaction := Value;
2751 end;
2752
2753 procedure TIBSQL.SQLChanging(Sender: TObject);
2754 begin
2755 if Assigned(OnSQLChanging) then
2756 OnSQLChanging(Self);
2757 if FHandle <> nil then FreeHandle;
2758 end;
2759
2760 procedure TIBSQL.BeforeTransactionEnd(Sender: TObject);
2761 begin
2762 if (FOpen) then
2763 Close;
2764 end;
2765
2766 end.