ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBSQL.pas
Revision: 35
Committed: Tue Jan 26 14:38:47 2016 UTC (8 years, 10 months ago) by tony
Content type: text/x-pascal
File size: 79414 byte(s)
Log Message:
Committing updates for Release R1-3-2

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