ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBSQL.pas
Revision: 39
Committed: Tue May 17 08:14:52 2016 UTC (7 years, 11 months ago) by tony
Content type: text/x-pascal
File size: 79492 byte(s)
Log Message:
Committing updates for Release R1-4-1

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