ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBSQL.pas
Revision: 43
Committed: Thu Sep 22 17:10:15 2016 UTC (7 years, 7 months ago) by tony
Content type: text/x-pascal
File size: 81461 byte(s)
Log Message:
Committing updates for Release R1-4-3

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