zeoslib  UNKNOWN
 All Files
ZDbcOracleResultSet.pas
Go to the documentation of this file.
1 {*********************************************************}
2 { }
3 { Zeos Database Objects }
4 { Oracle Database Connectivity Classes }
5 { }
6 { Originally written by Sergey Seroukhov }
7 { }
8 {*********************************************************}
9 
10 {@********************************************************}
11 { Copyright (c) 1999-2012 Zeos Development Group }
12 { }
13 { License Agreement: }
14 { }
15 { This library is distributed in the hope that it will be }
16 { useful, but WITHOUT ANY WARRANTY; without even the }
17 { implied warranty of MERCHANTABILITY or FITNESS FOR }
18 { A PARTICULAR PURPOSE. See the GNU Lesser General }
19 { Public License for more details. }
20 { }
21 { The source code of the ZEOS Libraries and packages are }
22 { distributed under the Library GNU General Public }
23 { License (see the file COPYING / COPYING.ZEOS) }
24 { with the following modification: }
25 { As a special exception, the copyright holders of this }
26 { library give you permission to link this library with }
27 { independent modules to produce an executable, }
28 { regardless of the license terms of these independent }
29 { modules, and to copy and distribute the resulting }
30 { executable under terms of your choice, provided that }
31 { you also meet, for each linked independent module, }
32 { the terms and conditions of the license of that module. }
33 { An independent module is a module which is not derived }
34 { from or based on this library. If you modify this }
35 { library, you may extend this exception to your version }
36 { of the library, but you are not obligated to do so. }
37 { If you do not wish to do so, delete this exception }
38 { statement from your version. }
39 { }
40 { }
41 { The project web site is located on: }
42 { http://zeos.firmos.at (FORUM) }
43 { http://sourceforge.net/p/zeoslib/tickets/ (BUGTRACKER)}
44 { svn://svn.code.sf.net/p/zeoslib/code-0/trunk (SVN) }
45 { }
46 { http://www.sourceforge.net/projects/zeoslib. }
47 { }
48 { }
49 { Zeos Development Group. }
50 {********************************************************@}
51 
52 unit ZDbcOracleResultSet;
53 
54 interface
55 
56 {$I ZDbc.inc}
57 
58 uses
59  {$IFDEF WITH_TOBJECTLIST_INLINE}System.Types, System.Contnrs{$ELSE}Types{$ENDIF},
60  Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils,
61  ZSysUtils, ZDbcIntfs, ZDbcOracle, ZDbcResultSet, ZPlainOracleDriver,
62  ZDbcResultSetMetadata, ZDbcLogging, ZCompatibility, ZDbcOracleUtils,
63  ZPlainOracleConstants;
64 
65 type
66 
67  {** Implements Oracle ResultSet. }
68  TZOracleAbstractResultSet = class(TZAbstractResultSet)
69  private
70  FSQL: string;
71  FStmtHandle: POCIStmt;
72  FErrorHandle: POCIError;
73  FPlainDriver: IZOraclePlainDriver;
74  FConnection: IZOracleConnection;
75  FOutVars: PZSQLVars;
76  protected
77  function GetSQLVarHolder(ColumnIndex: Integer): PZSQLVar;
78  function GetAsStringValue(ColumnIndex: Integer;
79  SQLVarHolder: PZSQLVar): RawByteString;
80  function GetAsLongIntValue(ColumnIndex: Integer;
81  SQLVarHolder: PZSQLVar): LongInt;
82  function GetAsDoubleValue(ColumnIndex: Integer;
83  SQLVarHolder: PZSQLVar): Double;
84  function GetAsDateTimeValue(ColumnIndex: Integer;
85  SQLVarHolder: PZSQLVar): TDateTime;
86  function InternalGetString(ColumnIndex: Integer): RawByteString; override;
87  function GetFinalObject(Obj: POCIObject): POCIObject;
88  public
89  constructor Create(PlainDriver: IZOraclePlainDriver;
90  Statement: IZStatement; SQL: string; StmtHandle: POCIStmt;
91  ErrorHandle: POCIError);
92 
93  function IsNull(ColumnIndex: Integer): Boolean; override;
94  function GetBoolean(ColumnIndex: Integer): Boolean; override;
95  function GetByte(ColumnIndex: Integer): Byte; override;
96  function GetShort(ColumnIndex: Integer): SmallInt; override;
97  function GetInt(ColumnIndex: Integer): Integer; override;
98  function GetLong(ColumnIndex: Integer): Int64; override;
99  function GetFloat(ColumnIndex: Integer): Single; override;
100  function GetDouble(ColumnIndex: Integer): Double; override;
101  function GetBigDecimal(ColumnIndex: Integer): Extended; override;
102  function GetBytes(ColumnIndex: Integer): TByteDynArray; override;
103  function GetDate(ColumnIndex: Integer): TDateTime; override;
104  function GetTime(ColumnIndex: Integer): TDateTime; override;
105  function GetTimestamp(ColumnIndex: Integer): TDateTime; override;
106  function GetDataSet(ColumnIndex: Integer): IZDataSet; override;
107  function GetBlob(ColumnIndex: Integer): IZBlob; override;
108  end;
109 
110  TZOracleResultSet = class(TZOracleAbstractResultSet)
111  protected
112  procedure Open; override;
113  public
114  procedure Close; override;
115  function Next: Boolean; override;
116  end;
117 
118  TZOracleCallableResultSet = Class(TZOracleAbstractResultSet)
119  private
120  FFieldNames: TStringDynArray;
121  function PrepareOracleOutVars(Statement: IZStatement; InVars: PZSQLVars;
122  const OracleParams: TZOracleParams): PZSQLVars;
123  protected
124  procedure Open; override;
125  public
126  constructor Create(PlainDriver: IZOraclePlainDriver;
127  Statement: IZStatement; SQL: string; StmtHandle: POCIStmt;
128  ErrorHandle: POCIError; OutVars: PZSQLVars; const OracleParams: TZOracleParams);
129  procedure Close; override;
130  function Next: Boolean; override;
131  End;
132 
133  {** Represents an interface, specific for Oracle blobs. }
134  IZOracleBlob = interface(IZBlob)
135  ['{3D861AAC-B263-42F1-B359-2A188D1D986A}']
136  function GetLobLocator: POCILobLocator;
137  procedure CreateBlob;
138  procedure ReadBlob;
139  procedure WriteBlob;
140  end;
141 
142  {** Implements external blob wrapper object for Oracle. }
143  TZOracleBlob = class(TZAbstractBlob, IZOracleBlob)
144  private
145  FHandle: IZConnection;
146  FLobLocator: POCILobLocator;
147  FPlainDriver: IZOraclePlainDriver;
148  FBlobType: TZSQLType;
149  FTemporary: Boolean;
150  FChunkSize: Integer;
151  protected
152  procedure InternalSetData(AData: Pointer; ASize: Integer);
153  public
154  constructor Create(PlainDriver: IZOraclePlainDriver; Data: Pointer;
155  Size: Integer; Handle: IZConnection; LobLocator: POCILobLocator;
156  BlobType: TZSQLType; ChunkSize: Integer);
157  destructor Destroy; override;
158 
159  function GetLobLocator: POCILobLocator;
160  procedure CreateBlob;
161  procedure ReadBlob;
162  procedure WriteBlob;
163 
164  function Length: LongInt; override;
165  function IsEmpty: Boolean; override;
166  function Clone: IZBlob; override;
167 
168  function GetString: RawByteString; override;
169  function GetBytes: TByteDynArray; override;
170  function GetStream: TStream; override;
171  end;
172 
173 implementation
174 
175 uses
176  Math, ZMessages, ZDbcUtils, ZEncoding;
177 
178 { TZOracleAbstractResultSet }
179 
180 {**
181  Constructs this object, assignes main properties and
182  opens the record set.
183  @param PlainDriver a Oracle plain driver.
184  @param Statement a related SQL statement object.
185  @param SQL a SQL statement.
186  @param Handle a Oracle specific query handle.
187 }
188 constructor TZOracleAbstractResultSet.Create(PlainDriver: IZOraclePlainDriver;
189  Statement: IZStatement; SQL: string; StmtHandle: POCIStmt;
190  ErrorHandle: POCIError);
191 begin
192  inherited Create(Statement, SQL, nil, Statement.GetConnection.GetConSettings);
193 
194  FSQL := SQL;
195  FStmtHandle := StmtHandle;
196  FErrorHandle := ErrorHandle;
197  FPlainDriver := PlainDriver;
198  ResultSetConcurrency := rcReadOnly;
199  FConnection := Statement.GetConnection as IZOracleConnection;
200 
201  Open;
202 end;
203 
204 {**
205  Indicates if the value of the designated column in the current row
206  of this <code>ResultSet</code> object is Null.
207 
208  @param columnIndex the first column is 1, the second is 2, ...
209  @return if the value is SQL <code>NULL</code>, the
210  value returned is <code>true</code>. <code>false</code> otherwise.
211 }
212 function TZOracleAbstractResultSet.IsNull(ColumnIndex: Integer): Boolean;
213 var
214  CurrentVar: PZSQLVar;
215 begin
216 {$IFNDEF DISABLE_CHECKING}
217  CheckClosed;
218  if (RowNo < 1) or (RowNo > LastRowNo) then
219  raise EZSQLException.Create(SRowDataIsNotAvailable);
220  if (ColumnIndex <=0) or (ColumnIndex > FOutVars.ActualNum) then
221  begin
222  raise EZSQLException.Create(
223  Format(SColumnIsNotAccessable, [ColumnIndex]));
224  end;
225 {$ENDIF}
226 
227  CurrentVar := @FOutVars.Variables[ColumnIndex];
228  Result := (CurrentVar.Indicator < 0);
229 end;
230 
231 {**
232  Gets a holder for SQL output variable.
233  @param ColumnIndex an index of the column to read.
234  @returns an output variable holder or <code>nil</code> if column is empty.
235 }
236 function TZOracleAbstractResultSet.GetSQLVarHolder(ColumnIndex: Integer): PZSQLVar;
237 begin
238 {$IFNDEF DISABLE_CHECKING}
239  CheckClosed;
240  if (RowNo < 1) or (RowNo > LastRowNo) then
241  raise EZSQLException.Create(SRowDataIsNotAvailable);
242 {$ENDIF}
243 
244  Result := @FOutVars.Variables[ColumnIndex];
245  LastWasNull := (Result.Indicator < 0) or (Result.Data = nil);
246  if LastWasNull then
247  Result := nil;
248 end;
249 
250 {**
251  Gets the value of the designated column in the current row
252  of this <code>ResultSet</code> object as a <code>String</code>.
253 
254  @param ColumnIndex the first column is 1, the second is 2, ...
255  @param SQLVarHolder a reference to SQL variable holder or <code>nil</code>
256  to force retrieving the variable.
257  @return the column value; if the value is SQL <code>NULL</code>, the
258  value returned is <code>null</code>
259 }
260 function TZOracleAbstractResultSet.GetAsStringValue(ColumnIndex: Integer;
261  SQLVarHolder: PZSQLVar): RawByteString;
262 var
263  OldSeparator: Char;
264  Blob: IZBlob;
265 begin
266  if SQLVarHolder = nil then
267  SQLVarHolder := GetSQLVarHolder(ColumnIndex);
268  if SQLVarHolder <> nil then
269  begin
270  case SQLVarHolder.TypeCode of
271  SQLT_INT:
272  Result := AnsiString(IntToStr(PLongInt(SQLVarHolder.Data)^));
273  SQLT_FLT:
274  begin
275  OldSeparator := {$IFDEF WITH_FORMATSETTINGS}FormatSettings.{$ENDIF}DecimalSeparator;
276  {$IFDEF WITH_FORMATSETTINGS}FormatSettings.{$ENDIF}DecimalSeparator := '.';
277  Result := AnsiString(FloatToSqlStr(PDouble(SQLVarHolder.Data)^));
278  {$IFDEF WITH_FORMATSETTINGS}FormatSettings.{$ENDIF}DecimalSeparator := OldSeparator;
279  end;
280  SQLT_STR:
281  Result := PAnsiChar(SQLVarHolder.Data);
282  SQLT_LVB, SQLT_LVC, SQLT_BIN:
283  begin
284  Result := AnsiString(BufferToStr(PAnsiChar(SQLVarHolder.Data) + SizeOf(Integer),
285  PInteger(SQLVarHolder.Data)^));
286  end;
287  SQLT_DAT, SQLT_TIMESTAMP:
288  begin
289  Result := AnsiString(DateTimeToAnsiSQLDate(
290  GetAsDateTimeValue(ColumnIndex, SQLVarHolder)));
291  end;
292  SQLT_BLOB, SQLT_CLOB:
293  begin
294  Blob := GetBlob(ColumnIndex);
295  Result := Blob.GetString;
296  end;
297  end;
298  end
299  else
300  Result := '';
301 end;
302 
303 {**
304  Gets the value of the designated column in the current row
305  of this <code>ResultSet</code> object as a <code>LongInt</code>.
306 
307  @param ColumnIndex the first column is 1, the second is 2, ...
308  @param SQLVarHolder a reference to SQL variable holder or <code>nil</code>
309  to force retrieving the variable.
310  @return the column value; if the value is SQL <code>NULL</code>, the
311  value returned is <code>0</code>
312 }
313 function TZOracleAbstractResultSet.GetAsLongIntValue(ColumnIndex: Integer;
314  SQLVarHolder: PZSQLVar): LongInt;
315 begin
316  if SQLVarHolder = nil then
317  SQLVarHolder := GetSQLVarHolder(ColumnIndex);
318  if SQLVarHolder <> nil then
319  begin
320  case SQLVarHolder.TypeCode of
321  SQLT_INT:
322  Result := PLongInt(SQLVarHolder.Data)^;
323  SQLT_FLT:
324  Result := Trunc(PDouble(SQLVarHolder.Data)^);
325  else
326  begin
327  Result := Trunc(SqlStrToFloatDef(
328  GetAsStringValue(ColumnIndex, SQLVarHolder), 0));
329  end;
330  end;
331  end
332  else
333  Result := 0;
334 end;
335 
336 {**
337  Gets the value of the designated column in the current row
338  of this <code>ResultSet</code> object as a <code>Double</code>.
339 
340  @param ColumnIndex the first column is 1, the second is 2, ...
341  @param SQLVarHolder a reference to SQL variable holder or <code>nil</code>
342  to force retrieving the variable.
343  @return the column value; if the value is SQL <code>NULL</code>, the
344  value returned is <code>0.0</code>
345 }
346 function TZOracleAbstractResultSet.GetAsDoubleValue(ColumnIndex: Integer;
347  SQLVarHolder: PZSQLVar): Double;
348 begin
349  if SQLVarHolder = nil then
350  SQLVarHolder := GetSQLVarHolder(ColumnIndex);
351  if SQLVarHolder <> nil then
352  begin
353  case SQLVarHolder.TypeCode of
354  SQLT_INT:
355  Result := PLongInt(SQLVarHolder.Data)^;
356  SQLT_FLT:
357  Result := PDouble(SQLVarHolder.Data)^;
358  else
359  begin
360  Result := SqlStrToFloatDef(
361  GetAsStringValue(ColumnIndex, SQLVarHolder), 0);
362  end;
363  end;
364  end
365  else
366  Result := 0;
367 end;
368 
369 {**
370  Gets the value of the designated column in the current row
371  of this <code>ResultSet</code> object as a <code>DateTime</code>.
372 
373  @param ColumnIndex the first column is 1, the second is 2, ...
374  @param SQLVarHolder a reference to SQL variable holder or <code>nil</code>
375  to force retrieving the variable.
376  @return the column value; if the value is SQL <code>NULL</code>, the
377  value returned is <code>0</code>
378 }
379 function TZOracleAbstractResultSet.GetAsDateTimeValue(ColumnIndex: Integer;
380  SQLVarHolder: PZSQLVar): TDateTime;
381 var
382  Status: Integer;
383  Year: SmallInt;
384  Month, Day: Byte;
385  Hour, Minute, Second: Byte;
386  Millis: Integer;
387  Connection: IZOracleConnection;
388 begin
389  if SQLVarHolder = nil then
390  SQLVarHolder := GetSQLVarHolder(ColumnIndex);
391  if SQLVarHolder <> nil then
392  begin
393  case SQLVarHolder.TypeCode of
394  SQLT_DAT:
395  Result := OraDateToDateTime(SQLVarHolder.Data);
396  SQLT_TIMESTAMP:
397  begin
398  Connection := GetStatement.GetConnection as IZOracleConnection;
399  if SQLVarHolder.ColType in [stDate, stTimestamp] then
400  begin
401  Status := FPlainDriver.DateTimeGetDate(
402  Connection.GetConnectionHandle,
403  FErrorHandle, PPOCIDescriptor(SQLVarHolder.Data)^,
404  Year, Month, Day);
405  // attention : this code handles all timestamps on 01/01/0001 as a pure time value
406  // reason : oracle doesn't have a pure time datatype so all time comparisons compare
407  // TDateTime values on 30 Dec 1899 against oracle timestamps on 01 januari 0001 (negative TDateTime)
408  if (Status = OCI_SUCCESS) and
409  ((Year <> 1) or (Month <> 1) or (Day <> 1)) then
410  Result := EncodeDate(Year, Month, Day)
411  else
412  Result := 0;
413  end
414  else
415  Result := 0;
416  if SQLVarHolder.ColType in [stTime, stTimestamp] then
417  begin
418  Status := FPlainDriver.DateTimeGetTime(
419  Connection.GetConnectionHandle,
420  FErrorHandle, PPOCIDescriptor(SQLVarHolder.Data)^,
421  Hour, Minute, Second, Millis);
422  if Status = OCI_SUCCESS then
423  begin
424  Millis := Round(Millis / 1000000);
425  if Millis >= 1000 then Millis := 999;
426  Result := Result + EncodeTime(
427  Hour, Minute, Second, Millis);
428  end;
429  end;
430  end;
431  else
432  begin
433  Result := AnsiSQLDateToDateTime(
434  String(GetAsStringValue(ColumnIndex, SQLVarHolder)));
435  end;
436  end;
437  end
438  else
439  Result := 0;
440 end;
441 
442 {**
443  Gets the value of the designated column in the current row
444  of this <code>ResultSet</code> object as
445  a <code>String</code> in the Java programming language.
446 
447  @param columnIndex the first column is 1, the second is 2, ...
448  @return the column value; if the value is SQL <code>NULL</code>, the
449  value returned is <code>null</code>
450 }
451 function TZOracleAbstractResultSet.InternalGetString(ColumnIndex: Integer): RawByteString;
452 begin
453 {$IFNDEF DISABLE_CHECKING}
454  CheckColumnConvertion(ColumnIndex, stString);
455 {$ENDIF}
456  Result := GetAsStringValue(ColumnIndex, nil);
457 end;
458 
459 {**
460  Gets the final object of a type/named-collection/nested-table,array
461 
462  @param obj the parent-object
463  @return the Object which contains the final object descriptor
464 }
465 function TZOracleAbstractResultSet.GetFinalObject(Obj: POCIObject): POCIObject;
466 begin
467  if Obj.is_final_type = 1 then
468  Result := Obj
469  else
470  Result := GetFinalObject(Obj.next_subtype); //recursive call
471 end;
472 
473 {**
474  Gets the value of the designated column in the current row
475  of this <code>ResultSet</code> object as
476  a <code>boolean</code> in the Java programming language.
477 
478  @param columnIndex the first column is 1, the second is 2, ...
479  @return the column value; if the value is SQL <code>NULL</code>, the
480  value returned is <code>false</code>
481 }
482 function TZOracleAbstractResultSet.GetBoolean(ColumnIndex: Integer): Boolean;
483 var
484  Temp: string;
485 begin
486 {$IFNDEF DISABLE_CHECKING}
487  CheckColumnConvertion(ColumnIndex, stBoolean);
488 {$ENDIF}
489  Temp := String(GetAsStringValue(ColumnIndex, nil));
490  Result := (StrToIntDef(Temp, 0) <> 0) or StrToBoolEx(Temp);
491 end;
492 
493 {**
494  Gets the value of the designated column in the current row
495  of this <code>ResultSet</code> object as
496  a <code>byte</code> in the Java programming language.
497 
498  @param columnIndex the first column is 1, the second is 2, ...
499  @return the column value; if the value is SQL <code>NULL</code>, the
500  value returned is <code>0</code>
501 }
502 function TZOracleAbstractResultSet.GetByte(ColumnIndex: Integer): Byte;
503 begin
504 {$IFNDEF DISABLE_CHECKING}
505  CheckColumnConvertion(ColumnIndex, stByte);
506 {$ENDIF}
507  Result := Byte(GetAsLongIntValue(ColumnIndex, nil));
508 end;
509 
510 {**
511  Gets the value of the designated column in the current row
512  of this <code>ResultSet</code> object as
513  a <code>short</code> in the Java programming language.
514 
515  @param columnIndex the first column is 1, the second is 2, ...
516  @return the column value; if the value is SQL <code>NULL</code>, the
517  value returned is <code>0</code>
518 }
519 function TZOracleAbstractResultSet.GetShort(ColumnIndex: Integer): SmallInt;
520 begin
521 {$IFNDEF DISABLE_CHECKING}
522  CheckColumnConvertion(ColumnIndex, stShort);
523 {$ENDIF}
524  Result := SmallInt(GetAsLongIntValue(ColumnIndex, nil));
525 end;
526 
527 {**
528  Gets the value of the designated column in the current row
529  of this <code>ResultSet</code> object as
530  an <code>int</code> in the Java programming language.
531 
532  @param columnIndex the first column is 1, the second is 2, ...
533  @return the column value; if the value is SQL <code>NULL</code>, the
534  value returned is <code>0</code>
535 }
536 function TZOracleAbstractResultSet.GetInt(ColumnIndex: Integer): Integer;
537 begin
538 {$IFNDEF DISABLE_CHECKING}
539  CheckColumnConvertion(ColumnIndex, stInteger);
540 {$ENDIF}
541  Result := Integer(GetAsLongIntValue(ColumnIndex, nil));
542 end;
543 
544 {**
545  Gets the value of the designated column in the current row
546  of this <code>ResultSet</code> object as
547  a <code>long</code> in the Java programming language.
548 
549  @param columnIndex the first column is 1, the second is 2, ...
550  @return the column value; if the value is SQL <code>NULL</code>, the
551  value returned is <code>0</code>
552 }
553 function TZOracleAbstractResultSet.GetLong(ColumnIndex: Integer): Int64;
554 begin
555 {$IFNDEF DISABLE_CHECKING}
556  CheckColumnConvertion(ColumnIndex, stLong);
557 {$ENDIF}
558  Result := Trunc(GetAsDoubleValue(ColumnIndex, nil));
559 end;
560 
561 {**
562  Gets the value of the designated column in the current row
563  of this <code>ResultSet</code> object as
564  a <code>float</code> in the Java programming language.
565 
566  @param columnIndex the first column is 1, the second is 2, ...
567  @return the column value; if the value is SQL <code>NULL</code>, the
568  value returned is <code>0</code>
569 }
570 function TZOracleAbstractResultSet.GetFloat(ColumnIndex: Integer): Single;
571 begin
572 {$IFNDEF DISABLE_CHECKING}
573  CheckColumnConvertion(ColumnIndex, stFloat);
574 {$ENDIF}
575  Result := GetAsDoubleValue(ColumnIndex, nil);
576 end;
577 
578 {**
579  Gets the value of the designated column in the current row
580  of this <code>ResultSet</code> object as
581  a <code>double</code> in the Java programming language.
582 
583  @param columnIndex the first column is 1, the second is 2, ...
584  @return the column value; if the value is SQL <code>NULL</code>, the
585  value returned is <code>0</code>
586 }
587 function TZOracleAbstractResultSet.GetDouble(ColumnIndex: Integer): Double;
588 begin
589 {$IFNDEF DISABLE_CHECKING}
590  CheckColumnConvertion(ColumnIndex, stDouble);
591 {$ENDIF}
592  Result := GetAsDoubleValue(ColumnIndex, nil);
593 end;
594 
595 {**
596  Gets the value of the designated column in the current row
597  of this <code>ResultSet</code> object as
598  a <code>java.sql.BigDecimal</code> in the Java programming language.
599 
600  @param columnIndex the first column is 1, the second is 2, ...
601  @param scale the number of digits to the right of the decimal point
602  @return the column value; if the value is SQL <code>NULL</code>, the
603  value returned is <code>null</code>
604 }
605 function TZOracleAbstractResultSet.GetBigDecimal(ColumnIndex: Integer): Extended;
606 begin
607 {$IFNDEF DISABLE_CHECKING}
608  CheckColumnConvertion(ColumnIndex, stBigDecimal);
609 {$ENDIF}
610  Result := GetAsDoubleValue(ColumnIndex, nil);
611 end;
612 
613 {**
614  Gets the value of the designated column in the current row
615  of this <code>ResultSet</code> object as
616  a <code>byte</code> array in the Java programming language.
617  The bytes represent the raw values returned by the driver.
618 
619  @param columnIndex the first column is 1, the second is 2, ...
620  @return the column value; if the value is SQL <code>NULL</code>, the
621  value returned is <code>null</code>
622 }
623 function TZOracleAbstractResultSet.GetBytes(ColumnIndex: Integer): TByteDynArray;
624 begin
625 {$IFNDEF DISABLE_CHECKING}
626  CheckColumnConvertion(ColumnIndex, stBytes);
627 {$ENDIF}
628  Result := StrToBytes(GetAsStringValue(ColumnIndex, nil));
629 end;
630 
631 {**
632  Gets the value of the designated column in the current row
633  of this <code>ResultSet</code> object as
634  a <code>java.sql.Date</code> object in the Java programming language.
635 
636  @param columnIndex the first column is 1, the second is 2, ...
637  @return the column value; if the value is SQL <code>NULL</code>, the
638  value returned is <code>null</code>
639 }
640 function TZOracleAbstractResultSet.GetDate(ColumnIndex: Integer): TDateTime;
641 begin
642 {$IFNDEF DISABLE_CHECKING}
643  CheckColumnConvertion(ColumnIndex, stDate);
644 {$ENDIF}
645  Result := Trunc(GetAsDateTimeValue(ColumnIndex, nil));
646 end;
647 
648 {**
649  Gets the value of the designated column in the current row
650  of this <code>ResultSet</code> object as
651  a <code>java.sql.Time</code> object in the Java programming language.
652 
653  @param columnIndex the first column is 1, the second is 2, ...
654  @return the column value; if the value is SQL <code>NULL</code>, the
655  value returned is <code>null</code>
656 }
657 function TZOracleAbstractResultSet.GetTime(ColumnIndex: Integer): TDateTime;
658 begin
659 {$IFNDEF DISABLE_CHECKING}
660  CheckColumnConvertion(ColumnIndex, stTime);
661 {$ENDIF}
662  Result := Frac(GetAsDateTimeValue(ColumnIndex, nil));
663 end;
664 
665 {**
666  Gets the value of the designated column in the current row
667  of this <code>ResultSet</code> object as
668  a <code>java.sql.Timestamp</code> object in the Java programming language.
669 
670  @param columnIndex the first column is 1, the second is 2, ...
671  @return the column value; if the value is SQL <code>NULL</code>, the
672  value returned is <code>null</code>
673  @exception SQLException if a database access error occurs
674 }
675 function TZOracleAbstractResultSet.GetTimestamp(ColumnIndex: Integer): TDateTime;
676 begin
677 {$IFNDEF DISABLE_CHECKING}
678  CheckColumnConvertion(ColumnIndex, stTimestamp);
679 {$ENDIF}
680  Result := GetAsDateTimeValue(ColumnIndex, nil);
681 end;
682 
683 {**
684  Returns the value of the designated column in the current row
685  of this <code>ResultSet</code> object as a <code>IZResultSet</code> object
686  in the Java programming language.
687 
688  @param ColumnIndex the first column is 1, the second is 2, ...
689  @return a <code>IZResultSet</code> object representing the SQL
690  <code>IZResultSet</code> value in the specified column
691 }
692 function TZOracleAbstractResultSet.GetDataSet(ColumnIndex: Integer): IZDataSet;
693 var
694  CurrentVar: PZSQLVar;
695  type_Ref: POCIRef;
696  //tdo: POCIType;
697 begin
698  Result := nil ;
699 {$IFNDEF DISABLE_CHECKING}
700  CheckBlobColumn(ColumnIndex);
701 {$ENDIF}
702 
703  LastWasNull := IsNull(ColumnIndex);
704  if LastWasNull then
705  Exit;
706 
707  GetSQLVarHolder(ColumnIndex);
708  CurrentVar := @FOutVars.Variables[ColumnIndex];
709  Result := nil;
710  if CurrentVar.TypeCode = SQLT_NTY then
711  if CurrentVar.Indicator >= 0 then
712  begin
713  if CurrentVar._Obj.is_final_type = 1 then
714  // here we've the final object lets's read it to test it
715  // later we only need the reference-pointers to create a new dataset
716  else
717  begin
718  //create a temporary object
719  type_ref := nil;
720  CheckOracleError(FPlainDriver, FErrorHandle,
721  FPlainDriver.ObjectNew(FConnection.GetConnectionHandle,
722  FConnection.GetErrorHandle, FConnection.GetContextHandle,
723  OCI_TYPECODE_REF, nil, nil, OCI_DURATION_DEFAULT, TRUE, @type_ref),
724  lcOther, 'OCITypeByRef from OCI_ATTR_REF_TDO');
725  //Get the type reference
726  CheckOracleError(FPlainDriver, FErrorHandle,
727  FPlainDriver.ObjectGetTypeRef(FConnection.GetConnectionHandle,
728  FConnection.GetErrorHandle, CurrentVar._Obj.obj_value, type_Ref),
729  lcOther, 'OCIObjectGetTypeRef(obj_value)');
730 
731  //Now let's get the new tdo
732  //Excptions????????
733  {CheckOracleError(FPlainDriver, FErrorHandle,
734  FPlainDriver.TypeByRef(FConnection.GetConnectionHandle,
735  FConnection.GetErrorHandle, type_ref, OCI_DURATION_DEFAULT,
736  OCI_TYPEGET_ALL, @tdo),
737  lcOther, 'OCITypeByRef from OCI_ATTR_REF_TDO');}
738  //free the temporary object
739  CheckOracleError(FPlainDriver, FErrorHandle,
740  FPlainDriver.ObjectFree(FConnection.GetConnectionHandle,
741  FConnection.GetErrorHandle, type_ref, ub2(0)),
742  lcOther, 'ObjectFree()');
743  end;
744 
745 
746  {CheckOracleError(FPlainDriver, FErrorHandle,
747  FPlainDriver.ResultSetToStmt(CurrentVar._Object,
748  FErrorHandle), lcOther, 'Nested Table to Stmt handle');
749  Result := CreateOracleResultSet(FPlainDriver, GetStatement,
750  'Fetch Nested Table', CurrentVar._Object, FErrorHandle);}
751  end;
752 end;
753 
754 {**
755  Returns the value of the designated column in the current row
756  of this <code>ResultSet</code> object as a <code>Blob</code> object
757  in the Java programming language.
758 
759  @param ColumnIndex the first column is 1, the second is 2, ...
760  @return a <code>Blob</code> object representing the SQL <code>BLOB</code> value in
761  the specified column
762 }
763 function TZOracleAbstractResultSet.GetBlob(ColumnIndex: Integer): IZBlob;
764 var
765  Connection: IZOracleConnection;
766  CurrentVar: PZSQLVar;
767  LobLocator: POCILobLocator;
768  Stream: TStream;
769 begin
770  Result := nil ;
771 {$IFNDEF DISABLE_CHECKING}
772  CheckBlobColumn(ColumnIndex);
773 {$ENDIF}
774 
775  LastWasNull := IsNull(ColumnIndex);
776  if LastWasNull then
777  Exit;
778 
779  GetSQLVarHolder(ColumnIndex);
780  CurrentVar := @FOutVars.Variables[ColumnIndex];
781  if CurrentVar.TypeCode in [SQLT_BLOB, SQLT_CLOB, SQLT_BFILEE, SQLT_CFILEE] then
782  begin
783  if CurrentVar.Indicator >= 0 then
784  LobLocator := PPOCIDescriptor(CurrentVar.Data)^
785  else
786  LobLocator := nil;
787 
788  Connection := GetStatement.GetConnection as IZOracleConnection;
789  Result := TZOracleBlob.Create(FPlainDriver, nil, 0, Connection, LobLocator,
790  CurrentVar.ColType, GetStatement.GetChunkSize);
791  (Result as IZOracleBlob).ReadBlob;
792 
793  end
794  else
795  if CurrentVar.TypeCode=SQLT_NTY then
796  Result := TZAbstractBlob.CreateWithStream(nil, GetStatement.GetConnection)
797  else
798  begin
799  if CurrentVar.Indicator >= 0 then
800  begin
801  Stream := nil;
802  try
803  Stream := TStringStream.Create(
804  GetAsStringValue(ColumnIndex, CurrentVar));
805  Result := TZAbstractBlob.CreateWithStream(Stream, GetStatement.GetConnection);
806  finally
807  if Assigned(Stream) then
808  Stream.Free;
809  end;
810  end
811  else
812  Result := TZAbstractBlob.CreateWithStream(nil, GetStatement.GetConnection);
813  end;
814 end;
815 
816 { TZOracleResultSet }
817 
818 {**
819  Opens this recordset.
820 }
821 procedure TZOracleResultSet.Open;
822 var
823  I: Integer;
824  ColumnInfo: TZColumnInfo;
825  Connection: IZOracleConnection;
826  CurrentVar: PZSQLVar;
827  ColumnCount: ub4;
828  TempColumnName: PAnsiChar;
829  TempColumnNameLen, CSForm: Integer;
830 begin
831  if ResultSetConcurrency = rcUpdatable then
832  raise EZSQLException.Create(SLiveResultSetsAreNotSupported);
833 
834  if not Assigned(FStmtHandle) or not Assigned(FErrorHandle) then
835  raise EZSQLException.Create(SCanNotRetrieveResultSetData);
836 
837  Connection := GetStatement.GetConnection as IZOracleConnection;
838 
839  CheckOracleError(FPlainDriver, FErrorHandle,
840  FPlainDriver.StmtExecute(Connection.GetContextHandle, FStmtHandle,
841  FErrorHandle, 1, 0, nil, nil, OCI_DESCRIBE_ONLY), lcExecute, FSQL);
842 
843  { Resize SQLVERS structure if needed }
844  FPlainDriver.AttrGet(FStmtHandle, OCI_HTYPE_STMT, @ColumnCount, nil,
845  OCI_ATTR_PARAM_COUNT, FErrorHandle);
846  AllocateOracleSQLVars(FOutVars, ColumnCount);
847  FOutVars.ActualNum := ColumnCount;
848 
849  { Allocates memory for result set }
850  for I := 1 to FOutVars.ActualNum do
851  begin
852  CurrentVar := @FOutVars.Variables[I];
853  CurrentVar.Handle := nil;
854 
855  FPlainDriver.ParamGet(FStmtHandle, OCI_HTYPE_STMT, FErrorHandle,
856  CurrentVar.Handle, I);
857  FPlainDriver.AttrGet(CurrentVar.Handle, OCI_DTYPE_PARAM,
858  @CurrentVar.DataSize, nil, OCI_ATTR_DATA_SIZE, FErrorHandle);
859  FPlainDriver.AttrGet(CurrentVar.Handle, OCI_DTYPE_PARAM,
860  @CurrentVar.DataType, nil, OCI_ATTR_DATA_TYPE, FErrorHandle);
861  CurrentVar.Scale := 0;
862  CurrentVar.Precision := 0;
863 
864  case CurrentVar.DataType of
865  SQLT_CHR, SQLT_VCS, SQLT_AFC, SQLT_AVC, SQLT_STR, SQLT_VST:
866  CurrentVar.ColType := stString;
867  SQLT_NUM:
868  begin
869  FPlainDriver.AttrGet(CurrentVar.Handle, OCI_DTYPE_PARAM,
870  @CurrentVar.Precision, nil, OCI_ATTR_PRECISION, FErrorHandle);
871  FPlainDriver.AttrGet(CurrentVar.Handle, OCI_DTYPE_PARAM,
872  @CurrentVar.Scale, nil, OCI_ATTR_SCALE, FErrorHandle);
873 
874  {by default convert number to double}
875  CurrentVar.ColType := stDouble;
876  if (CurrentVar.Scale = 0) and (CurrentVar.Precision <> 0) then
877  begin
878  if CurrentVar.Precision <= 2 then
879  CurrentVar.ColType := stByte
880  else if CurrentVar.Precision <= 4 then
881  CurrentVar.ColType := stShort
882  else if CurrentVar.Precision <= 9 then
883  CurrentVar.ColType := stInteger
884  else if CurrentVar.Precision <= 19 then
885  CurrentVar.ColType := stLong;
886  end
887  end;
888  SQLT_BFLOAT, SQLT_BDOUBLE, SQLT_IBFLOAT, SQLT_IBDOUBLE:
889  CurrentVar.ColType := stDouble;
890  SQLT_INT, _SQLT_PLI:
891  CurrentVar.ColType := stInteger;
892  SQLT_LNG, SQLT_LVC:
893  CurrentVar.ColType := stAsciiStream;
894  SQLT_RID, SQLT_RDD:
895  begin
896  CurrentVar.ColType := stString;
897  CurrentVar.DataSize := 20;
898  end;
899  SQLT_DAT, SQLT_DATE:
900  { oracle DATE precission - 1 second}
901  CurrentVar.ColType := stTimestamp;
902  SQLT_TIME, SQLT_TIME_TZ:
903  CurrentVar.ColType := stTime;
904  SQLT_TIMESTAMP, SQLT_TIMESTAMP_TZ, SQLT_TIMESTAMP_LTZ:
905  CurrentVar.ColType := stTimestamp;
906  SQLT_BIN, SQLT_LBI:
907  begin
908  if CurrentVar.DataSize = 0 then
909  CurrentVar.ColType := stBinaryStream
910  else
911  CurrentVar.ColType := stBytes;
912  end;
913  SQLT_CLOB:
914  begin
915  CurrentVar.ColType := stAsciiStream;
916  CurrentVar.TypeCode := CurrentVar.DataType;
917  end;
918  SQLT_BLOB, SQLT_BFILEE, SQLT_CFILEE:
919  begin
920  CurrentVar.ColType := stBinaryStream;
921  CurrentVar.TypeCode := CurrentVar.DataType;
922  end;
923  SQLT_NTY:
924  begin
925  CurrentVar.ColType := stDataSet;
926  CurrentVar.TypeCode := CurrentVar.DataType;
927 
928  CurrentVar._Obj := DescribeObject(FplainDriver, FConnection,
929  CurrentVar.Handle, FStmtHandle, 0);
930 
931  if FPlainDriver.TypeTypeCode(Connection.GetConnectionHandle,
932  FerrorHandle, CurrentVar._Obj.tdo) = SQLT_NCO then
933  CurrentVar.ColType := stDataSet
934  else
935  CurrentVar.ColType := stBinaryStream;
936  end;
937  else
938  CurrentVar.ColType := stUnknown;
939  end;
940 
941  if (ConSettings.CPType = cCP_UTF16) then
942  case CurrentVar.ColType of
943  stString: CurrentVar.ColType := stUnicodeString;
944  stAsciiStream: if not ( CurrentVar.DataType in [SQLT_LNG]) then
945  CurrentVar.ColType := stUnicodeStream;
946  end;
947 
948 
949  InitializeOracleVar(FPlainDriver, Connection, CurrentVar,
950  CurrentVar.ColType, CurrentVar.TypeCode, CurrentVar.DataSize);
951 
952  if CurrentVar.ColType <> stUnknown then
953  CheckOracleError(FPlainDriver, FErrorHandle,
954  FPlainDriver.DefineByPos(FStmtHandle, CurrentVar.Define,
955  FErrorHandle, I, CurrentVar.Data, CurrentVar.Length, CurrentVar.TypeCode,
956  @CurrentVar.Indicator, nil, nil, OCI_DEFAULT), lcExecute, FSQL);
957  if CurrentVar.DataType=SQLT_NTY then
958  begin
959  //second step: http://www.csee.umbc.edu/portal/help/oracle8/server.815/a67846/obj_bind.htm
960  CheckOracleError(FPlainDriver, FErrorHandle,
961  FPlainDriver.DefineObject(CurrentVar.Define, FErrorHandle, CurrentVar._Obj.tdo,
962  @CurrentVar._Obj.obj_value, nil, nil, nil), lcExecute, FSQL);
963  end;
964  end;
965 
966  { Fills the column info. }
967  ColumnsInfo.Clear;
968  for I := 1 to FOutVars.ActualNum do
969  begin
970  CurrentVar := @FOutVars.Variables[I];
971  ColumnInfo := TZColumnInfo.Create;
972 
973  with ColumnInfo do
974  begin
975  ColumnName := '';
976  TableName := '';
977 
978  TempColumnName := nil;
979  FPlainDriver.AttrGet(CurrentVar.Handle, OCI_DTYPE_PARAM,
980  @TempColumnName, @TempColumnNameLen, OCI_ATTR_NAME, FErrorHandle);
981  if TempColumnName <> nil then
982  ColumnLabel := BufferToStr(TempColumnName, TempColumnNameLen);
983 
984  ColumnDisplaySize := 0;
985  AutoIncrement := False;
986  Signed := True;
987  Nullable := ntNullable;
988 
989  ColumnType := CurrentVar.ColType;
990  Scale := CurrentVar.Scale;
991  if (ColumnType in [stString, stUnicodeString]) then
992  begin
993  FPlainDriver.AttrGet(CurrentVar.Handle, OCI_DTYPE_PARAM,
994  @ColumnDisplaySize, nil, OCI_ATTR_DISP_SIZE, FErrorHandle);
995  FPlainDriver.AttrGet(CurrentVar.Handle, OCI_DTYPE_PARAM,
996  @CSForm, nil, OCI_ATTR_CHARSET_FORM, FErrorHandle);
997  if CSForm = SQLCS_NCHAR then //AL16UTF16 or AL16UTF16LE?? We should determine the NCHAR set on connect
998  ColumnDisplaySize := ColumnDisplaySize div 2;
999  Precision := GetFieldSize(ColumnType, ConSettings, ColumnDisplaySize,
1000  ConSettings.ClientCodePage^.CharWidth);
1001  end
1002  else
1003  if (ColumnType = stBytes ) then
1004  Precision := CurrentVar.DataSize
1005  else
1006  Precision := CurrentVar.Precision;
1007  end;
1008 
1009  ColumnsInfo.Add(ColumnInfo);
1010  end;
1011 
1012  inherited Open;
1013 end;
1014 
1015 {**
1016  Releases this <code>ResultSet</code> object's database and
1017  JDBC resources immediately instead of waiting for
1018  this to happen when it is automatically closed.
1019 
1020  <P><B>Note:</B> A <code>ResultSet</code> object
1021  is automatically closed by the
1022  <code>Statement</code> object that generated it when
1023  that <code>Statement</code> object is closed,
1024  re-executed, or is used to retrieve the next result from a
1025  sequence of multiple results. A <code>ResultSet</code> object
1026  is also automatically closed when it is garbage collected.
1027 }
1028 procedure TZOracleResultSet.Close;
1029 var
1030  ps: IZPreparedStatement;
1031 begin
1032  if assigned(FOutVars) then // else no statement anyways
1033  FreeOracleSQLVars(FPlainDriver, FOutVars, FConnection.GetConnectionHandle, FErrorHandle, ConSettings);
1034  { prepared statement own handles, so dont free them }
1035  if not Supports(GetStatement, IZPreparedStatement, ps) then
1036  FreeOracleStatementHandles(FPlainDriver, FStmtHandle, FErrorHandle);
1037  inherited Close;
1038 end;
1039 
1040 {**
1041  Moves the cursor down one row from its current position.
1042  A <code>ResultSet</code> cursor is initially positioned
1043  before the first row; the first call to the method
1044  <code>next</code> makes the first row the current row; the
1045  second call makes the second row the current row, and so on.
1046 
1047  <P>If an input stream is open for the current row, a call
1048  to the method <code>next</code> will
1049  implicitly close it. A <code>ResultSet</code> object's
1050  warning chain is cleared when a new row is read.
1051 
1052  @return <code>true</code> if the new current row is valid;
1053  <code>false</code> if there are no more rows
1054 }
1055 function TZOracleResultSet.Next: Boolean;
1056 var
1057  Status: Integer;
1058 begin
1059  { Checks for maximum row. }
1060  Result := False;
1061  if (RowNo > LastRowNo) or ((MaxRows > 0) and (RowNo >= MaxRows)) then
1062  Exit;
1063 
1064  if RowNo = 0 then
1065  Status := FPlainDriver.StmtExecute(FConnection.GetContextHandle, FStmtHandle,
1066  FErrorHandle, 1, 0, nil, nil, OCI_DEFAULT)
1067  else
1068  Status := FPlainDriver.StmtFetch(FStmtHandle, FErrorHandle,
1069  1, OCI_FETCH_NEXT, OCI_DEFAULT);
1070  if not (Status in [OCI_SUCCESS, OCI_NO_DATA]) then
1071  CheckOracleError(FPlainDriver, FErrorHandle, Status, lcOther, 'FETCH ROW');
1072 
1073  if Status in [OCI_SUCCESS, OCI_SUCCESS_WITH_INFO] then
1074  begin
1075  RowNo := RowNo + 1;
1076  if LastRowNo < RowNo then
1077  LastRowNo := RowNo;
1078  Result := True;
1079  end
1080  else
1081  begin
1082  if RowNo <= LastRowNo then
1083  RowNo := LastRowNo + 1;
1084  Result := False;
1085  end;
1086 end;
1087 
1088 { TZOracleCallableResultSet }
1089 function TZOracleCallableResultSet.PrepareOracleOutVars(Statement: IZStatement;
1090  InVars: PZSQLVars; const OracleParams: TZOracleParams): PZSQLVars;
1091 var
1092  I, J: Integer;
1093 begin
1094  J := 0;
1095  for i := 0 to High(OracleParams) do
1096  if OracleParams[I].pType in [2,3,4] then
1097  Inc(J);
1098 
1099  Result := nil;
1100  AllocateOracleSQLVars(Result, J);
1101  Result.ActualNum := J;
1102  SetLength(FFieldNames, J);
1103 
1104  for I := 1 to Length(OracleParams) do
1105  begin
1106  J := OracleParams[I-1].pOutIndex;
1107  if OracleParams[I-1].pType in [2,3,4] then //ptInOut, ptOut, ptResult
1108  begin
1109  Result.Variables[J].ColType := InVars.Variables[I].ColType;
1110  Result.Variables[J].TypeCode := InVars.Variables[I].TypeCode;
1111  Result.Variables[J].DataSize := InVars.Variables[I].DataSize;
1112  Result.Variables[J].Length := InVars.Variables[I].Length;
1113  GetMem(Result.Variables[J].Data, InVars.Variables[I].Length);
1114  Move(InVars.Variables[I].Data^, Result.Variables[J].Data^, InVars.Variables[I].Length);
1115  FFieldNames[J-1] := OracleParams[I-1].pName;
1116  end;
1117  end;
1118 end;
1119 
1120 procedure TZOracleCallableResultSet.Open;
1121 var
1122  I: Integer;
1123  ColumnInfo: TZColumnInfo;
1124  CurrentVar: PZSQLVar;
1125  Connection: IZConnection;
1126 begin
1127  Connection := GetStatement.GetConnection;
1128  { Fills the column info. }
1129  ColumnsInfo.Clear;
1130  for I := 1 to FOutVars.ActualNum do
1131  begin
1132  CurrentVar := @FOutVars.Variables[I];
1133  ColumnInfo := TZColumnInfo.Create;
1134 
1135  with ColumnInfo do
1136  begin
1137  ColumnName := '';
1138  TableName := '';
1139 
1140  ColumnLabel := FFieldNames[i-1];
1141  ColumnDisplaySize := 0;
1142  AutoIncrement := False;
1143  Signed := True;
1144  Nullable := ntNullable;
1145 
1146  ColumnType := CurrentVar.ColType;
1147  Scale := CurrentVar.Scale;
1148 
1149  {Reset the column type which can be changed by user before}
1150  if (ColumnType = stUnicodeStream) and not ( Connection.GetConSettings.CPType = cCP_UTF16) then
1151  ColumnType := stAsciiStream;
1152  if (ColumnType = stAsciiStream) and ( Connection.GetConSettings.CPType = cCP_UTF16) then
1153  ColumnType := stUnicodeStream;
1154  if (ColumnType = stUnicodeString) and not ( Connection.GetConSettings.CPType = cCP_UTF16) then
1155  ColumnType := stString;
1156  if (ColumnType = stString) and ( Connection.GetConSettings.CPType = cCP_UTF16) then
1157  ColumnType := stUnicodeString;
1158 
1159  if ( ColumnType in [stString, stUnicodeString] ) then
1160  begin
1161  ColumnDisplaySize := CurrentVar.DataSize;
1162  Precision := GetFieldSize(ColumnType, ConSettings, CurrentVar.DataSize,
1163  ConSettings.ClientCodePage^.CharWidth);
1164  end
1165  else
1166  Precision := CurrentVar.Precision;
1167  end;
1168 
1169  ColumnsInfo.Add(ColumnInfo);
1170  end;
1171 
1172  inherited Open;
1173 end;
1174 
1175 {**
1176  Constructs this object, assignes main properties and
1177  opens the record set.
1178  @param PlainDriver a Oracle plain driver.
1179  @param Statement a related SQL statement object.
1180  @param SQL a SQL statement.
1181  @param Handle a Oracle specific query handle.
1182 }
1183 constructor TZOracleCallableResultSet.Create(PlainDriver: IZOraclePlainDriver;
1184  Statement: IZStatement; SQL: string; StmtHandle: POCIStmt;
1185  ErrorHandle: POCIError; OutVars: PZSQLVars; const OracleParams: TZOracleParams);
1186 begin
1187  FOutVars := PrepareOracleOutVars(Statement, OutVars, OracleParams);
1188  inherited Create(PlainDriver, Statement, SQL, StmtHandle, ErrorHandle);
1189  FConnection := Statement.GetConnection as IZOracleConnection;
1190  MaxRows := 1;
1191 end;
1192 
1193 {**
1194  Releases this <code>ResultSet</code> object's database and
1195  JDBC resources immediately instead of waiting for
1196  this to happen when it is automatically closed.
1197 
1198  <P><B>Note:</B> A <code>ResultSet</code> object
1199  is automatically closed by the
1200  <code>Statement</code> object that generated it when
1201  that <code>Statement</code> object is closed,
1202  re-executed, or is used to retrieve the next result from a
1203  sequence of multiple results. A <code>ResultSet</code> object
1204  is also automatically closed when it is garbage collected.
1205 }
1206 procedure TZOracleCallableResultSet.Close;
1207 var
1208  I: Integer;
1209  CurrentVar: PZSQLVar;
1210 begin
1211  if FOutVars <> nil then
1212  begin
1213  { Frees allocated memory for output variables }
1214  for I := 1 to FOutVars.ActualNum do
1215  begin
1216  CurrentVar := @FOutVars.Variables[I];
1217  if CurrentVar.Data <> nil then
1218  begin
1219  CurrentVar.DupData := nil;
1220  FreeMem(CurrentVar.Data);
1221  CurrentVar.Data := nil;
1222  end;
1223  end;
1224  FreeMem(FOutVars);
1225  end;
1226  FOutVars := nil;
1227  inherited Close;
1228 end;
1229 
1230 function TZOracleCallableResultSet.Next: Boolean;
1231 begin
1232  { Checks for maximum row. }
1233  Result := False;
1234  if (RowNo >= MaxRows) then
1235  Exit;
1236  RowNo := LastRowNo + 1;
1237  Result := True;
1238 end;
1239 
1240 { TZOracleBlob }
1241 
1242 {**
1243  Constructs this class and assignes the main properties.
1244  @param PlainDriver a Oracle plain driver.
1245  @param Data a pointer to the blobdata.
1246  @param Size the size of the blobdata.
1247  @param Handle a Oracle connection reference.
1248  @param LobLocator an Oracle lob locator reference.
1249  @param BlobType a blob type.
1250 }
1251 constructor TZOracleBlob.Create(PlainDriver: IZOraclePlainDriver;
1252  Data: Pointer; Size: Integer; Handle: IZConnection;
1253  LobLocator: POCILobLocator; BlobType: TZSQLType; ChunkSize: Integer);
1254 begin
1255  inherited CreateWithData(Data, Size, Handle);
1256  FHandle := Handle;
1257  FLobLocator := LobLocator;
1258  FPlainDriver := PlainDriver;
1259  FTemporary := False;
1260  FBlobType := BlobType;
1261  FChunkSize := ChunkSize;
1262 end;
1263 
1264 {**
1265  Destroys this object and cleanups the memory.
1266 }
1267 destructor TZOracleBlob.Destroy;
1268 var
1269  Connection: IZOracleConnection;
1270 begin
1271  if FTemporary then
1272  begin
1273  Connection := FHandle as IZOracleConnection;
1274  FPlainDriver.LobFreeTemporary(Connection.GetContextHandle,
1275  Connection.GetErrorHandle, FLobLocator);
1276  end;
1277 
1278  inherited Destroy;
1279 end;
1280 
1281 {**
1282  Gets the lob locator reference.
1283  @return the lob locator reference.
1284 }
1285 function TZOracleBlob.GetLobLocator: POCILobLocator;
1286 begin
1287  Result := FLobLocator;
1288 end;
1289 
1290 {**
1291  Creates a temporary blob.
1292 }
1293 procedure TZOracleBlob.CreateBlob;
1294 var
1295  Status: Integer;
1296  Connection: IZOracleConnection;
1297  TempBlobType: ub1;
1298 begin
1299  Connection := FHandle as IZOracleConnection;
1300 
1301  if FBlobType in [stBytes, stBinaryStream] then
1302  TempBlobType := OCI_TEMP_BLOB
1303  else
1304  TempBlobType := OCI_TEMP_CLOB;
1305 
1306  Status := FPlainDriver.LobCreateTemporary(Connection.GetContextHandle,
1307  Connection.GetErrorHandle, FLobLocator, OCI_DEFAULT, OCI_DEFAULT,
1308  TempBlobType, False, OCI_DURATION_DEFAULT);
1309  CheckOracleError(FPlainDriver, Connection.GetErrorHandle,
1310  Status, lcOther, 'Create Large Object');
1311 
1312  FTemporary := True;
1313 end;
1314 
1315 {**
1316  Reads the blob by the blob handle.
1317 }
1318 procedure TZOracleBlob.ReadBlob;
1319 const
1320  MemDelta = 1 shl 12; // read page (2^...)
1321 var
1322  Status: Integer;
1323  Buf: PByteArray;
1324  ReadNumBytes, ReadNumChars, Offset, Cap: ub4;
1325  Connection: IZOracleConnection;
1326  AnsiTemp: RawByteString;
1327  Stream: TStream;
1328  csid: ub2;
1329  csfrm: ub1;
1330 
1331  procedure DoRead(const csid: ub2; const csfrm: ub1);
1332  begin
1333  FillChar(Buf^, FChunkSize+1, #0);
1334  ReadNumChars := 0;
1335  Status := FPlainDriver.LobRead(Connection.GetContextHandle,
1336  Connection.GetErrorHandle, FLobLocator, ReadNumChars, Offset + 1,
1337  Buf, FChunkSize, nil, nil, Connection.GetClientCodePageInformations^.ID, csfrm);
1338  if ReadNumChars > 0 then
1339  begin
1340  Inc(Offset, ReadNumChars);
1341  AnsiTemp := AnsiTemp+PAnsiChar(Buf);
1342  end;
1343  end;
1344 begin
1345  if not Updated and (FLobLocator <> nil)
1346  and (BlobData = nil) and (not FTemporary) then
1347  begin
1348  Connection := FHandle as IZOracleConnection;
1349 
1350  { Opens a large object or file for read. }
1351  Status := FPlainDriver.LobOpen(Connection.GetContextHandle,
1352  Connection.GetErrorHandle, FLobLocator, OCI_LOB_READONLY);
1353  CheckOracleError(FPlainDriver, Connection.GetErrorHandle,
1354  Status, lcOther, 'Open Large Object');
1355  try
1356  { Reads data in chunks by MemDelta or more }
1357  Offset := 0;
1358  Buf := nil;
1359  try
1360  case FBlobType of
1361  stBinaryStream:
1362  repeat //BLOB
1363  {Calc new progressive by 1/8 and aligned by MemDelta capacity for buffer}
1364  Cap := (Offset + (Offset shr 3) + 2 * MemDelta - 1) and not (MemDelta - 1);
1365  ReallocMem(Buf, Cap);
1366  ReadNumBytes := Cap - Offset;
1367 
1368  Status := FPlainDriver.LobRead(Connection.GetContextHandle,
1369  Connection.GetErrorHandle, FLobLocator, ReadNumBytes, Offset + 1,
1370  @Buf[Offset], ReadNumBytes, nil, nil, 0, SQLCS_IMPLICIT);
1371  CheckOracleError(FPlainDriver, Connection.GetErrorHandle,
1372  Status, lcOther, 'Read Large Object');
1373  if ReadNumBytes > 0 then
1374  Inc(Offset, ReadNumBytes);
1375  until Offset < Cap;
1376  else //CLob
1377  begin
1378  GetMem(Buf, FChunkSize+1);
1379  AnsiTemp := '';
1380  Offset := 0;
1381  csid := Connection.GetClientCodePageInformations^.ID;
1382  CheckOracleError(FPlainDriver, Connection.GetErrorHandle,
1383  FPlainDriver.LobCharSetForm(Connection.GetConnectionHandle,
1384  Connection.GetErrorHandle, FLobLocator, @csfrm), lcOther, 'Determine LOB SCFORM'); //need to determine proper CharSet-Form
1385  DoRead(csid, csfrm);
1386  if Status = OCI_NEED_DATA then
1387  while Status = OCI_NEED_DATA do
1388  DoRead(csid, csfrm);
1389  CheckOracleError(FPlainDriver, Connection.GetErrorHandle,
1390  Status, lcOther, 'Read Large Object');
1391  if FBlobType = stUnicodeStream then
1392  if OffSet = 0 then
1393  Stream := TMemoryStream.Create
1394  else
1395  Stream := ZEncoding.GetValidatedUnicodeStream(AnsiTemp, Connection.GetConSettings, True)
1396  else
1397  Stream := TStringStream.Create(GetValidatedAnsiString(AnsiTemp, Connection.GetConSettings, True));
1398  ReallocMem(Buf, Stream.Size);
1399  Move(TMemoryStream(Stream).Memory^, PAnsichar(Buf)^, Stream.Size);
1400  OffSet := Stream.Size;
1401  Stream.Free;
1402  FDecoded := FBlobType = stUnicodeStream;
1403  end;
1404  end;
1405  except
1406  FreeMem(Buf);
1407  raise;
1408  end;
1409  finally
1410  { Closes large object or file. }
1411  Status := FPlainDriver.LobClose(Connection.GetContextHandle,
1412  Connection.GetErrorHandle, FLobLocator);
1413  CheckOracleError(FPlainDriver, Connection.GetErrorHandle,
1414  Status, lcOther, 'Close Large Object');
1415  end;
1416  { Assigns data }
1417  InternalSetData(Buf, Offset);
1418  end;
1419 end;
1420 
1421 {**
1422  Writes the blob by the blob handle.
1423 }
1424 procedure TZOracleBlob.WriteBlob;
1425 var
1426  Status: sword;
1427  Connection: IZOracleConnection;
1428  ContentSize, OffSet: ub4;
1429 
1430  function DoWrite(AOffSet: ub4; AChunkSize: ub4; APiece: ub1): sword;
1431  var
1432  AContentSize: ub4;
1433  begin
1434  if Self.FBlobType = stBinaryStream then
1435  begin
1436  AContentSize := ContentSize;
1437  Result := FPlainDriver.LobWrite(Connection.GetContextHandle,
1438  Connection.GetErrorHandle, FLobLocator, AContentSize, AOffSet,
1439  (PAnsiChar(BlobData)+OffSet), AChunkSize, APiece, nil, nil, 0, SQLCS_IMPLICIT);
1440  end
1441  else
1442  begin
1443  if ContentSize > 0 then
1444  AContentSize := AChunkSize div Connection.GetClientCodePageInformations^.CharWidth
1445  else
1446  begin
1447  AContentSize := ContentSize;
1448  AChunkSize := Connection.GetClientCodePageInformations^.CharWidth;
1449  end;
1450 
1451  Result := FPlainDriver.LobWrite(Connection.GetContextHandle,
1452  Connection.GetErrorHandle, FLobLocator, AContentSize, AOffSet,
1453  (PAnsiChar(BlobData)+OffSet), AChunkSize, APiece, nil, nil, Connection.GetClientCodePageInformations^.ID, SQLCS_IMPLICIT);
1454  end;
1455  ContentSize := AContentSize;
1456  inc(OffSet, AChunkSize);
1457  end;
1458 begin
1459  Connection := FHandle as IZOracleConnection;
1460 
1461  { Opens a large object or file for read. }
1462  Status := FPlainDriver.LobOpen(Connection.GetContextHandle,
1463  Connection.GetErrorHandle, FLobLocator, OCI_LOB_READWRITE);
1464  CheckOracleError(FPlainDriver, Connection.GetErrorHandle,
1465  Status, lcOther, 'Open Large Object');
1466 
1467  { Checks for empty blob.}
1468  { This test doesn't use IsEmpty because that function does allow for zero length blobs}
1469  if (BlobSize > 0) then
1470  begin
1471  if BlobSize > FChunkSize then
1472  begin
1473  OffSet := 0;
1474  ContentSize := 0;
1475 
1476  Status := DoWrite(1, FChunkSize, OCI_FIRST_PIECE);
1477  if Status <> OCI_NEED_DATA then
1478  CheckOracleError(FPlainDriver, Connection.GetErrorHandle,
1479  Status, lcOther, 'Write Large Object');
1480 
1481  if (BlobSize - OffSet) > FChunkSize then
1482  while (BlobSize - OffSet) > FChunkSize do //take care there is room left for LastPiece
1483  begin
1484  Status := DoWrite(offset, FChunkSize, OCI_NEXT_PIECE);
1485  if Status <> OCI_NEED_DATA then
1486  CheckOracleError(FPlainDriver, Connection.GetErrorHandle,
1487  Status, lcOther, 'Write Large Object');
1488 
1489  end;
1490  Status := DoWrite(offset, BlobSize - OffSet, OCI_LAST_PIECE);
1491  end
1492  else
1493  begin
1494  ContentSize := BlobSize;
1495  Status := FPlainDriver.LobWrite(Connection.GetContextHandle,
1496  Connection.GetErrorHandle, FLobLocator, ContentSize, 1,
1497  BlobData, BlobSize, OCI_ONE_PIECE, nil, nil, 0, SQLCS_IMPLICIT);
1498  end;
1499  end
1500  else
1501  begin
1502  Status := FPlainDriver.LobTrim(Connection.GetContextHandle,
1503  Connection.GetErrorHandle, FLobLocator, 0);
1504  end;
1505  CheckOracleError(FPlainDriver, Connection.GetErrorHandle,
1506  Status, lcOther, 'Write Large Object');
1507 
1508  { Closes large object or file. }
1509  Status := FPlainDriver.LobClose(Connection.GetContextHandle,
1510  Connection.GetErrorHandle, FLobLocator);
1511  CheckOracleError(FPlainDriver, Connection.GetErrorHandle,
1512  Status, lcOther, 'Close Large Object');
1513 end;
1514 
1515 {**
1516  Replace data in blob by AData without copy (keep ref of AData)
1517 }
1518 procedure TZOracleBlob.InternalSetData(AData: Pointer; ASize: Integer);
1519 begin
1520  Clear;
1521  BlobData := AData;
1522  BlobSize := ASize;
1523 end;
1524 
1525 {**
1526  Checks if this blob has an empty content.
1527  @return <code>True</code> if this blob is empty.
1528 }
1529 function TZOracleBlob.IsEmpty: Boolean;
1530 begin
1531  ReadBlob;
1532  Result := inherited IsEmpty;
1533 end;
1534 
1535 function TZOracleBlob.Length: LongInt;
1536 begin
1537  ReadBlob;
1538  Result := inherited Length;
1539 end;
1540 
1541 {**
1542  Clones this blob object.
1543  @return a clonned blob object.
1544 }
1545 function TZOracleBlob.Clone: IZBlob;
1546 begin
1547  Result := TZOracleBlob.Create(FPlainDriver, BlobData, BlobSize,
1548  FHandle, FLobLocator, FBlobType, FChunkSize);
1549 end;
1550 
1551 {**
1552  Gets the associated stream object.
1553  @return an associated or newly created stream object.
1554 }
1555 function TZOracleBlob.GetStream: TStream;
1556 begin
1557  ReadBlob;
1558  Result := inherited GetStream;
1559 end;
1560 
1561 {**
1562  Gets the string from the stored data.
1563  @return a string which contains the stored data.
1564 }
1565 function TZOracleBlob.GetString: RawByteString;
1566 begin
1567  ReadBlob;
1568  Result := inherited GetString;
1569 end;
1570 
1571 {**
1572  Gets the byte buffer from the stored data.
1573  @return a byte buffer which contains the stored data.
1574 }
1575 function TZOracleBlob.GetBytes: TByteDynArray;
1576 begin
1577  ReadBlob;
1578  Result := inherited GetBytes;
1579 end;
1580 
1581 end.