ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBSQL.pas
Revision: 5
Committed: Fri Feb 18 16:26:16 2011 UTC (13 years, 9 months ago) by tony
Content type: text/x-pascal
File size: 71942 byte(s)
Log Message:
Committing updates for Release pre-release

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