zeoslib  UNKNOWN
 All Files
ZDbcOracleUtils.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 ZDbcOracleUtils;
53 
54 interface
55 
56 {$I ZDbc.inc}
57 
58 uses
59  Types, Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils,
60  ZSysUtils, ZDbcIntfs, ZVariant, ZPlainOracleDriver, ZDbcLogging,
61  ZCompatibility, ZPlainOracleConstants;
62 
63 const
64  MAX_SQLVAR_LIMIT = 1024;
65 
66 type
67  {** Declares SQL Object }
68 
69  POCIObject = ^TOCIObject;
70  TObjFields = array of POCIObject;
71  TOCIObject = Record // embedded object or table will work recursively
72  type_name: String; //object's name (TDO)
73  type_schema: String; //object's schema name (TDO)
74  parmdp: POCIParam; //Describe attributes of the object OCI_DTYPE_PARAM
75  parmap: POCIParam; //Describe attributes of the object OCI_ATTR_COLLECTION_ELEMENT OCI_ATTR_PARAM
76  tdo: POCIType; //object's TDO handle
77  typecode: OCITypeCode; //object's OCI_ATTR_TYPECODE
78  col_typecode: OCITypeCode; //if collection this is its OCI_ATTR_COLLECTION_TYPECODE
79  elem_typecode: OCITypeCode; //if collection this is its element's OCI_ATTR_TYPECODE
80  obj_ref: POCIRef; //if an embeded object this is ref handle to its TDO
81  obj_ind: POCIInd; //Null indictator for object
82  obj_value: POCIComplexObject;//the actual value from the DB
83  obj_type: POCIType; //if an embeded object this is the OCIType returned by a OCIObjectPin
84  is_final_type: ub1; //object's OCI_ATTR_IS_FINAL_TYPE
85  fields: TObjFields; //one object for each field/property
86  field_count: ub2; //The number of fields Not really needed but nice to have
87  next_subtype: POCIObject; //There is strored information about subtypes for inteherited objects
88  stmt_handle: POCIStmt; //the Statement-Handle
89  Level: Integer; //the instance level
90  Pinned: Boolean; //did we pin the obj on decribe?
91  end;
92 
93  PZSQLVar = ^TZSQLVar;
94  TZSQLVar = {$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}packed{$endif} record
95  Handle: POCIHandle;
96  Define: POCIHandle;
97  BindHandle: POCIBind;
98  Data: Pointer;
99  DupData: Pointer;
100  DataType: ub2;
101  DataSize: ub2;
102  Length: Integer;
103  Precision: Integer;
104  Scale: Integer;
105  ColType: TZSQLType;
106  TypeCode: ub2;
107  Indicator: sb2;
108  Blob: IZBlob;
109  _Obj: POCIObject;
110  end;
111 
112  TZSQLVars = {$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}packed{$endif} record
113  AllocNum: ub4;
114  ActualNum: ub4;
115  Variables: array[1..MAX_SQLVAR_LIMIT] of TZSQLVar;
116  end;
117  PZSQLVars = ^TZSQLVars;
118 
119  TZOracleParam = Record
120  pName:string;
121  pSQLType:Integer;
122  pValue: TZVariant;
123  pTypeName: String;
124  pType: ShortInt;
125  pProcIndex: Integer;
126  pParamIndex: Integer;
127  pOutIndex: Integer;
128  End;
129  TZOracleParams = array of TZOracleParam;
130 
131 {**
132  Allocates memory for Oracle SQL Variables.
133  @param Variables a pointer to array of variables.
134  @param Count a number of SQL variables.
135 }
136 procedure AllocateOracleSQLVars(var Variables: PZSQLVars; Count: Integer);
137 
138 {**
139  Frees memory Oracle SQL Variables from the memory.
140  @param PlainDriver an Oracle plain driver.
141  @param Variables a pointer to array of variables.
142 }
143 procedure FreeOracleSQLVars(const PlainDriver: IZOraclePlainDriver;
144  var Variables: PZSQLVars; const Handle: POCIEnv; const ErrorHandle: POCIError;
145  const ConSettings: PZConSettings);
146 
147 {**
148  Allocates in memory and initializes the Oracle variable.
149  @param PlainDriver an Oracle plain driver.
150  @param Connection an Oracle connection Object.
151  @param Variable an Oracle variable holder.
152  @param DataType a DBC data type.
153  @param OracleType a correspondent Oracle type.
154  @param DataSize a length for string variables.
155 }
156 procedure InitializeOracleVar(PlainDriver: IZOraclePlainDriver;
157  Connection: IZConnection; var Variable: PZSQLVar;
158  DataType: TZSQLType; OracleType: ub2; DataSize: Integer);
159 
160 {**
161  Loads Oracle variables binded to SQL statement with data.
162  @param PlainDriver an Oracle plain driver.
163  @param Connection an Oracle connection Object.
164  @param Variables Oracle variable holders.
165  @param Values a values to be loaded.
166 }
167 procedure LoadOracleVars(PlainDriver: IZOraclePlainDriver;
168  Connection: IZConnection; ErrorHandle: POCIError; Variables: PZSQLVars;
169  Values: TZVariantDynArray; ChunkSize: Integer);
170 
171 {**
172  Unloads Oracle variables binded to SQL statement with data.
173  @param Variables Oracle variable holders.
174 }
175 procedure UnloadOracleVars(Variables: PZSQLVars);
176 
177 {**
178  Convert string Oracle field type to SQLType
179  @param string field type value
180  @result the SQLType field type value
181 }
182 function ConvertOracleTypeToSQLType(TypeName: string;
183  Precision, Scale: Integer; const CtrlsCPType: TZControlsCodePage): TZSQLType;
184 
185 {**
186  Converts Oracle internal date into TDateTime
187  @param Value a pointer to Oracle internal date.
188  @return a decoded TDateTime value.
189 }
190 function OraDateToDateTime(Value: PAnsiChar): TDateTime;
191 
192 {**
193  Checks for possible SQL errors.
194  @param PlainDriver an Oracle plain driver.
195  @param Handle an Oracle error handle.
196  @param Status a command return status.
197  @param LogCategory a logging category.
198  @param LogMessage a logging message.
199 }
200 procedure CheckOracleError(PlainDriver: IZOraclePlainDriver;
201  ErrorHandle: POCIError; Status: Integer; LogCategory: TZLoggingCategory;
202  LogMessage: string);
203 
204 {**
205  Creates an Oracle result set based on the current settings.
206  @return a created result set object.
207 }
208 function CreateOracleResultSet(PlainDriver: IZOraclePlainDriver;
209  Statement: IZStatement; LogSQL: string; Handle: POCIStmt;
210  ErrorHandle: POCIError): IZResultSet; overload;
211 
212 {**
213  Creates an Oracle result set based on the current settings.
214  @return a created result set object.
215 }
216 function CreateOracleResultSet(PlainDriver: IZOraclePlainDriver;
217  Statement: IZStatement; LogSQL: string; StmtHandle: POCIStmt;
218  ErrorHandle: POCIError; OutVars: PZSQLVars;
219  Const OracleParams: TZOracleParams): IZResultSet; overload;
220 
221 {**
222  Allocates in memory Oracle handlers for Statement object.
223  @param PlainDriver an Oracle plain driver.
224  @param Connection an Oracle connection object.
225  @param Handle a holder for Statement handle.
226  @param ErrorHandle a holder for Error handle.
227 }
228 procedure AllocateOracleStatementHandles(PlainDriver: IZOraclePlainDriver;
229  Connection: IZConnection; var Handle: POCIStmt; var ErrorHandle: POCIError);
230 
231 {**
232  Frees from memory Oracle handlers for Statement object.
233  @param PlainDriver an Oracle plain driver.
234  @param Handle a holder for Statement handle.
235  @param ErrorHandle a holder for Error handle.
236 }
237 procedure FreeOracleStatementHandles(PlainDriver: IZOraclePlainDriver;
238  var Handle: POCIStmt; var ErrorHandle: POCIError);
239 
240 {**
241  Prepares an Oracle statement.
242  @param PlainDriver an Oracle plain driver.
243  @param SQL an SQL query to be prepared.
244  @param Handle a holder for Statement handle.
245  @param ErrorHandle a holder for Error handle.
246 }
247 procedure PrepareOracleStatement(PlainDriver: IZOraclePlainDriver;
248  SQL: RawByteString; LogSQL: String; Handle: POCIStmt; ErrorHandle: POCIError;
249  PrefetchCount: ub4; ConSettings: PZConSettings);
250 
251 {**
252  Executes an Oracle statement.
253  @param PlainDriver an Oracle plain driver.
254  @param Connection an Oracle connection Object.
255  @param SQL an SQL query to be prepared.
256  @param Handle a holder for Statement handle.
257  @param ErrorHandle a holder for Error handle.
258 }
259 procedure ExecuteOracleStatement(PlainDriver: IZOraclePlainDriver;
260  Connection: IZConnection; LogSQL: string; Handle: POCIStmt;
261  ErrorHandle: POCIError);
262 
263 {**
264  Gets a number of updates made by executed Oracle statement.
265  @param PlainDriver an Oracle plain driver.
266  @param Handle a holder for Statement handle.
267  @param ErrorHandle a holder for Error handle.
268  @returns a number of updates.
269 }
270 function GetOracleUpdateCount(PlainDriver: IZOraclePlainDriver;
271  Handle: POCIStmt; ErrorHandle: POCIError): ub4;
272 
273 function DescribeObject(PlainDriver: IZOraclePlainDriver; Connection: IZConnection;
274  ParamHandle: POCIParam; stmt_handle: POCIHandle; Level: ub2): POCIObject;
275 
276 implementation
277 
278 uses ZMessages, ZDbcOracle, ZDbcOracleResultSet, ZDbcCachedResultSet,
279  ZDbcGenericResolver, ZDbcUtils, ZEncoding
280  {$IFDEF WITH_UNITANSISTRINGS}, AnsiStrings{$ENDIF};
281 
282 {**
283  Calculates size of SQLVars record.
284  @param Count a number of variable.
285  @returns a record size.
286 }
287 function CalculateSQLVarsSize(Count: Integer): Integer;
288 begin
289  Result := SizeOf(TZSQLVars) + Count * SizeOf(TZSQLVar);
290 end;
291 
292 {**
293  Allocates memory for Oracle SQL Variables.
294  @param Variables a pointer to array of variables.
295  @param Count a number of SQL variables.
296 }
297 procedure AllocateOracleSQLVars(var Variables: PZSQLVars; Count: Integer);
298 var
299  Size: Integer;
300 begin
301  if Variables <> nil then
302  FreeMem(Variables);
303 
304  Size := CalculateSQLVarsSize(Count);
305  GetMem(Variables, Size);
306  FillChar(Variables^, Size, 0);
307  Variables^.AllocNum := Count;
308  Variables^.ActualNum := 0;
309 end;
310 
311 {**
312  Frees memory Oracle SQL Variables from the memory.
313  @param PlainDriver an Oracle plain driver.
314  @param Variables a pointer to array of variables.
315 }
316 procedure FreeOracleSQLVars(const PlainDriver: IZOraclePlainDriver;
317  var Variables: PZSQLVars; const Handle: POCIEnv; const ErrorHandle: POCIError;
318  const ConSettings: PZConSettings);
319 var
320  I: Integer;
321  CurrentVar: PZSQLVar;
322 
323  procedure DisposeObject(var Obj: POCIObject);
324  var
325  I: Integer;
326  begin
327  for i := 0 to High(Obj.fields) do
328  DisposeObject(Obj.fields[i]);
329  SetLength(Obj.fields, 0);
330  if Assigned(Obj.next_subtype) then
331  begin
332  DisposeObject(Obj.next_subtype);
333  Obj.next_subtype := nil;
334  end;
335  if Obj.Pinned then
336  {Unpin tdo}
337  //CheckOracleError(PlainDriver, ErrorHandle, //debug
338  PlainDriver.ObjectUnpin(Handle,ErrorHandle, CurrentVar^._Obj.tdo)
339  ;//debug, lcOther, 'OCIObjectUnpin', ConSettings);
340  if (Obj.Level = 0) and assigned(Obj.tdo) then
341  {Free Object}
342  //debugCheckOracleError(PlainDriver, ErrorHandle,
343  PlainDriver.ObjectFree(Handle,ErrorHandle, CurrentVar^._Obj.tdo, 0)
344  ;//debug, lcOther, 'OCIObjectFree', ConSettings);
345  Dispose(Obj);
346  Obj := nil;
347  end;
348 
349 begin
350  if Variables <> nil then
351  begin
352  { Frees allocated memory for output variables }
353  for I := 1 to Variables.ActualNum do
354  begin
355  CurrentVar := @Variables.Variables[I];
356  if Assigned(CurrentVar._Obj) then
357  DisposeObject(CurrentVar^._Obj);
358  if CurrentVar.Data <> nil then
359  begin
360  if CurrentVar.TypeCode in [SQLT_BLOB, SQLT_CLOB, SQLT_BFILEE, SQLT_CFILEE] then
361  begin
362  PlainDriver.DescriptorFree(PPOCIDescriptor(CurrentVar.Data)^,
363  OCI_DTYPE_LOB);
364  end
365  else if CurrentVar.TypeCode = SQLT_TIMESTAMP then
366  begin
367  PlainDriver.DescriptorFree(PPOCIDescriptor(CurrentVar.Data)^,
368  OCI_DTYPE_TIMESTAMP);
369  end;
370  FreeMem(CurrentVar.Data);
371  CurrentVar.Data := nil;
372  end;
373  end;
374 
375  FreeMem(Variables);
376  end;
377  Variables := nil;
378 end;
379 
380 {**
381  Allocates in memory and initializes the Oracle variable.
382  @param PlainDriver an Oracle plain driver.
383  @param Connection an Oracle connection Object.
384  @param Variable an Oracle variable holder.
385  @param DataType a DBC data type.
386  @param OracleType a correspondent Oracle type.
387  @param DataSize a length for string variables.
388 }
389 
390 procedure InitializeOracleVar(PlainDriver: IZOraclePlainDriver;
391  Connection: IZConnection; var Variable: PZSQLVar;
392  DataType: TZSQLType; OracleType: ub2; DataSize: Integer);
393 var
394  Length: Integer;
395  OracleConnection: IZOracleConnection;
396 begin
397  OracleConnection := Connection as IZOracleConnection;
398  Variable.ColType := DataType;
399  Variable.TypeCode := OracleType;
400  Variable.DataSize := DataSize;
401  Length := 0;
402  case Variable.ColType of
403  stByte, stShort, stInteger:
404  begin
405  Variable.TypeCode := SQLT_INT;
406  Length := SizeOf(LongInt);
407  end;
408  stFloat, stDouble, stLong:
409  begin
410  Variable.TypeCode := SQLT_FLT;
411  Length := SizeOf(Double);
412  end;
413  stDate, stTime, stTimestamp:
414  begin
415  Variable.TypeCode := SQLT_TIMESTAMP;
416  Length := SizeOf(POCIDateTime);
417  end;
418  stString, stUnicodeString:
419  begin
420  Variable.TypeCode := SQLT_STR;
421  Length := Variable.DataSize + 1;
422  end;
423  stAsciiStream, stUnicodeStream, stBinaryStream, stBytes:
424  begin
425  if not (Variable.TypeCode in [SQLT_CLOB, SQLT_BLOB, SQLT_BFILEE, SQLT_CFILEE,SQLT_NTY]) then
426  begin
427  if Variable.ColType = stAsciiStream then
428  Variable.TypeCode := SQLT_LVC
429  else
430  Variable.TypeCode := SQLT_LVB;
431  if Variable.DataSize = 0 then
432  Length := 128 * 1024 + SizeOf(Integer)
433  else
434  Length := Variable.DataSize + SizeOf(Integer);
435  end
436  else
437  Length := SizeOf(POCILobLocator);
438  end;
439  stDataSet: ; //Do nothing here!
440  stUnknown:
441  Exit;
442  end;
443 
444  Variable.Length := Length;
445  GetMem(Variable.Data, Variable.Length);
446  if Variable.TypeCode in [SQLT_BIN, SQLT_BLOB, SQLT_CLOB, SQLT_BFILEE, SQLT_CFILEE] then
447  begin
448  PlainDriver.DescriptorAlloc(OracleConnection.GetConnectionHandle,
449  PPOCIDescriptor(Variable.Data)^, OCI_DTYPE_LOB, 0, nil);
450  end
451  else
452  if Variable.TypeCode = SQLT_TIMESTAMP then
453  begin
454  PlainDriver.DescriptorAlloc(OracleConnection.GetConnectionHandle,
455  PPOCIDescriptor(Variable.Data)^, OCI_DTYPE_TIMESTAMP, 0, nil);
456  end;
457 end;
458 
459 {**
460  Loads Oracle variables binded to SQL statement with data.
461  @param PlainDriver an Oracle plain driver.
462  @param Connection an Oracle connection Object.
463  @param Variables Oracle variable holders.
464  @param Values a values to be loaded.
465 }
466 procedure LoadOracleVars(PlainDriver: IZOraclePlainDriver;
467  Connection: IZConnection; ErrorHandle: POCIError; Variables: PZSQLVars;
468  Values: TZVariantDynArray; ChunkSize: Integer);
469 var
470  I, Len: Integer;
471  Status: Integer;
472  CurrentVar: PZSQLVar;
473  TempDate: TDateTime;
474  TempBytes: TByteDynArray;
475  TempBlob: IZBlob;
476  WriteTempBlob: IZOracleBlob;
477  TempStream: TStream;
478  Year, Month, Day, Hour, Min, Sec, MSec: Word;
479  OracleConnection: IZOracleConnection;
480 begin
481  OracleConnection := Connection as IZOracleConnection;
482  for I := 0 to Variables.ActualNum - 1 do
483  begin
484  CurrentVar := @Variables.Variables[I + 1];
485  CurrentVar.DupData := CurrentVar.Data;
486  if (high(Values)<I) or DefVarManager.IsNull(Values[I]) then
487  begin
488  CurrentVar.Indicator := -1;
489  //CurrentVar.Data := nil;
490  end
491  else
492  begin
493  CurrentVar.Indicator := 0;
494  case CurrentVar.TypeCode of
495  SQLT_INT:
496  begin
497  PLongInt(CurrentVar.Data)^ :=
498  DefVarManager.GetAsInteger(Values[I]);
499  end;
500  SQLT_FLT:
501  begin
502  PDouble(CurrentVar.Data)^ :=
503  DefVarManager.GetAsFloat(Values[I]);
504  end;
505  SQLT_STR:
506  begin
507  case Values[i].VType of
508  vtUnicodeString:
509  {$IFDEF WITH_STRLCOPY_DEPRECATED}AnsiStrings.{$ENDIF}StrLCopy(PAnsiChar(CurrentVar.Data),
510  PAnsiChar(PlainDriver.ZPlainString(SoftVarManager.GetAsUnicodeString(Values[I]), Connection.GetConSettings)), 1024);
511  else
512  {$IFDEF WITH_STRLCOPY_DEPRECATED}AnsiStrings.{$ENDIF}StrLCopy(PAnsiChar(CurrentVar.Data),
513  PAnsiChar(PlainDriver.ZPlainString(SoftVarManager.GetAsString(Values[I]), Connection.GetConSettings)), 1024);
514  end;
515  end;
516  SQLT_VST:
517  begin
518  {$IFDEF WITH_STRLCOPY_DEPRECATED}AnsiStrings.{$ENDIF}StrLCopy(PAnsiChar(CurrentVar.Data), PAnsiChar(UTF8Encode(DefVarManager.GetAsUnicodeString(Values[I]))), 1024);
519  end;
520  SQLT_TIMESTAMP:
521  begin
522  TempDate := DefVarManager.GetAsDateTime(Values[I]);
523  DecodeDate(TempDate, Year, Month, Day);
524  DecodeTime(TempDate, Hour, Min, Sec, MSec);
525  Status := PlainDriver.DateTimeConstruct(
526  OracleConnection.GetConnectionHandle,
527  ErrorHandle, PPOCIDescriptor(CurrentVar.Data)^,
528  Year, Month, Day, Hour, Min, Sec, MSec * 1000000, nil, 0);
529  CheckOracleError(PlainDriver, ErrorHandle, Status, lcOther, '');
530  end;
531  SQLT_BLOB, SQLT_CLOB:
532  begin
533  if Values[I].VType = vtBytes then
534  begin
535  TempBytes := DefVarManager.GetAsBytes(Values[I]);
536  Len := Length(TempBytes);
537  TempStream := TMemoryStream.Create;
538  TempStream.Size := Len;
539  System.Move(Pointer(TempBytes)^, TMemoryStream(TempStream).Memory^, Len);
540  end
541  else
542  begin
543  TempBlob := DefVarManager.GetAsInterface(Values[I]) as IZBlob;
544  if not TempBlob.IsEmpty then
545  begin
546  if (CurrentVar.TypeCode = SQLT_CLOB) then
547  TempStream := TStringStream.Create(GetValidatedAnsiStringFromBuffer(TempBlob.GetBuffer,
548  TempBlob.Length, TempBlob.WasDecoded, Connection.GetConSettings))
549  else
550  TempStream := TempBlob.GetStream;
551  end
552  else TempStream := TMemoryStream.Create;
553  end;
554  try
555  WriteTempBlob := TZOracleBlob.Create(PlainDriver,
556  TMemoryStream(TempStream).Memory, TempStream.Size, Connection, PPOCIDescriptor(CurrentVar.Data)^,
557  CurrentVar.ColType, ChunkSize);
558  WriteTempBlob.CreateBlob;
559  WriteTempBlob.WriteBlob;
560  CurrentVar.Blob := WriteTempBlob;
561  finally
562  WriteTempBlob := nil;
563  TempStream.Free;
564  end;
565  end;
566  end;
567  end;
568  end;
569 end;
570 
571 {**
572  Unloads Oracle variables binded to SQL statement with data.
573  @param Variables Oracle variable holders.
574 }
575 procedure UnloadOracleVars(Variables: PZSQLVars);
576 var
577  I: Integer;
578  CurrentVar: PZSQLVar;
579 begin
580  for I := 1 to Variables.ActualNum do
581  begin
582  CurrentVar := @Variables.Variables[I];
583  CurrentVar.Blob := nil;
584  CurrentVar.Data := CurrentVar.DupData;
585  end;
586 end;
587 
588 {**
589  Convert string Oracle field type to SQLType
590  @param string field type value
591  @result the SQLType field type value
592 }
593 function ConvertOracleTypeToSQLType(TypeName: string;
594  Precision, Scale: Integer; const CtrlsCPType: TZControlsCodePage): TZSQLType;
595 begin
596  TypeName := UpperCase(TypeName);
597  Result := stUnknown;
598 
599  if (TypeName = 'CHAR') or (TypeName = 'VARCHAR2') then
600  Result := stString
601  else if (TypeName = 'NCHAR') or (TypeName = 'NVARCHAR2') then
602  Result := stString
603  else if (TypeName = 'FLOAT') or (TypeName = 'BINARY_FLOAT') or (TypeName = 'BINARY_DOUBLE') then
604  Result := stDouble
605  else if TypeName = 'DATE' then {precission - 1 sec, so Timestamp}
606  Result := stTimestamp
607  else if TypeName = 'BLOB' then
608  Result := stBinaryStream
609  else if (TypeName = 'RAW') then
610  Result := stBytes
611  else if (TypeName = 'LONG RAW') then
612  Result := stBinaryStream
613  else if TypeName = 'CLOB' then
614  Result := stAsciiStream
615  else if TypeName = 'NCLOB' then
616  Result := stAsciiStream
617  else if TypeName = 'LONG' then
618  Result := stAsciiStream
619  else if StartsWith(TypeName, 'TIMESTAMP') then
620  Result := stTimestamp
621  else if TypeName = 'BFILE' then
622  Result := stBinaryStream else
623  if TypeName = 'NUMBER' then
624  begin
625  Result := stDouble; { default for number types}
626  if (Scale = 0) and (Precision <> 0) then
627  begin
628  if Precision <= 2 then
629  Result := stByte
630  else if Precision <= 4 then
631  Result := stShort
632  else if Precision <= 9 then
633  Result := stInteger
634  else if Precision <= 19 then
635  Result := stLong {!!in fact, unusable}
636  end;
637  end;
638  if ( CtrlsCPType = cCP_UTF16 ) then
639  case result of
640  stString: Result := stUnicodeString;
641  stAsciiStream: if not (TypeName = 'LONG') then Result := stUnicodeStream; //fix: http://zeos.firmos.at/viewtopic.php?t=3530
642  end;
643 end;
644 
645 {**
646  Converts Oracle internal date into TDateTime
647  @param Value a pointer to Oracle internal date.
648  @return a decoded TDateTime value.
649 }
650 function OraDateToDateTime(Value: PAnsiChar): TDateTime;
651 type
652  TOraDate = array[1..7] of Byte;
653  POraDate = ^TOraDate;
654 var
655  Ptr: POraDate;
656 begin
657  Ptr := POraDate(Value);
658  Result := EncodeDate((Ptr[1] - 100) * 100 + Ptr[2] - 100, Ptr[3], Ptr[4]) +
659  EncodeTime(Ptr[5]-1, Ptr[6]-1, Ptr[7]-1, 0);
660 end;
661 
662 {**
663  Checks for possible SQL errors.
664  @param PlainDriver an Oracle plain driver.
665  @param Handle an Oracle error handle.
666  @param Status a command return status.
667  @param LogCategory a logging category.
668  @param LogMessage a logging message.
669 }
670 procedure CheckOracleError(PlainDriver: IZOraclePlainDriver;
671  ErrorHandle: POCIError; Status: Integer; LogCategory: TZLoggingCategory;
672  LogMessage: string);
673 var
674  ErrorMessage: string;
675  ErrorBuffer: array[0..255] of AnsiChar;
676  ErrorCode: SB4;
677 begin
678  ErrorMessage := '';
679  ErrorCode := Status;
680 
681  case Status of
682  OCI_SUCCESS:
683  Exit;
684  OCI_SUCCESS_WITH_INFO:
685  begin
686  PlainDriver.ErrorGet(ErrorHandle, 1, nil, ErrorCode, ErrorBuffer, 255,
687  OCI_HTYPE_ERROR);
688  ErrorMessage := 'OCI_SUCCESS_WITH_INFO: ' + String(ErrorBuffer);
689  end;
690  OCI_NEED_DATA:
691  ErrorMessage := 'OCI_NEED_DATA';
692  OCI_NO_DATA:
693  ErrorMessage := 'OCI_NO_DATA';
694  OCI_ERROR:
695  begin
696  PlainDriver.ErrorGet(ErrorHandle, 1, nil, ErrorCode, ErrorBuffer, 255,
697  OCI_HTYPE_ERROR);
698  ErrorMessage := 'OCI_ERROR: ' + String(ErrorBuffer);
699  end;
700  OCI_INVALID_HANDLE:
701  ErrorMessage := 'OCI_INVALID_HANDLE';
702  OCI_STILL_EXECUTING:
703  ErrorMessage := 'OCI_STILL_EXECUTING';
704  OCI_CONTINUE:
705  ErrorMessage := 'OCI_CONTINUE';
706  end;
707 
708  if (Status <> OCI_SUCCESS) and (Status <> OCI_SUCCESS_WITH_INFO) and (ErrorMessage <> '') then
709  begin
710  if Assigned(DriverManager) then //Thread-Safe patch
711  DriverManager.LogError(LogCategory, PlainDriver.GetProtocol, LogMessage,
712  ErrorCode, ErrorMessage);
713  if not ( ( LogCategory = lcDisconnect ) and ( ErrorCode = 3314 ) ) then //patch for disconnected Server
714  //on the other hand we can't close the connction MantisBT: #0000227
715  raise EZSQLException.CreateWithCode(ErrorCode,
716  Format(SSQLError1, [ErrorMessage]));
717  end;
718  if (Status = OCI_SUCCESS_WITH_INFO) and (ErrorMessage <> '') then
719  if Assigned(DriverManager) then //Thread-Safe patch
720  DriverManager.LogMessage(LogCategory, PlainDriver.GetProtocol, ErrorMessage);
721 end;
722 
723 {**
724  Creates an Oracle result set based on the current settings.
725  @return a created result set object.
726 }
727 function CreateOracleResultSet(PlainDriver: IZOraclePlainDriver;
728  Statement: IZStatement; LogSQL: string; Handle: POCIStmt;
729  ErrorHandle: POCIError): IZResultSet;
730 var
731  NativeResultSet: TZOracleResultSet;
732  CachedResultSet: TZCachedResultSet;
733 begin
734  NativeResultSet := TZOracleResultSet.Create(PlainDriver, Statement,
735  LogSQL, Handle, ErrorHandle);
736  NativeResultSet.SetConcurrency(rcReadOnly);
737  if (Statement.GetResultSetConcurrency = rcUpdatable)
738  or (Statement.GetResultSetType <> rtForwardOnly) then
739  begin
740  CachedResultSet := TZCachedResultSet.Create(NativeResultSet, LogSQL, nil,
741  Statement.GetConnection.GetConSettings);
742  CachedResultSet.SetConcurrency(rcUpdatable);
743  CachedResultSet.SetResolver(TZOracleCachedResolver.Create(
744  Statement, NativeResultSet.GetMetadata));
745  Result := CachedResultSet;
746  end
747  else
748  Result := NativeResultSet;
749 end;
750 
751 {**
752  Creates an Oracle result set based on the current settings.
753  @return a created result set object.
754 }
755 function CreateOracleResultSet(PlainDriver: IZOraclePlainDriver;
756  Statement: IZStatement; LogSQL: string; StmtHandle: POCIStmt;
757  ErrorHandle: POCIError; OutVars: PZSQLVars;
758  Const OracleParams: TZOracleParams): IZResultSet;
759 var
760  NativeResultSet: TZOracleCallableResultSet;
761  CachedResultSet: TZCachedResultSet;
762 begin
763  NativeResultSet := TZOracleCallableResultSet.Create(PlainDriver, Statement,
764  LogSQL, StmtHandle, ErrorHandle, OutVars, OracleParams);
765  NativeResultSet.SetConcurrency(rcReadOnly);
766  CachedResultSet := TZCachedResultSet.Create(NativeResultSet, LogSQL, nil,
767  Statement.GetConnection.GetConSettings);
768  CachedResultSet.SetConcurrency(rcReadOnly);
769  CachedResultSet.SetResolver(TZOracleCachedResolver.Create(
770  Statement, NativeResultSet.GetMetadata));
771  CachedResultSet.Last;
772  CachedResultSet.BeforeFirst;
773  Result := CachedResultSet;
774 end;
775 
776 {**
777  Allocates in memory Oracle handlers for Statement object.
778  @param PlainDriver an Oracle plain driver.
779  @param Connection an Oracle connection object.
780  @param Handle a holder for Statement handle.
781  @param ErrorHandle a holder for Error handle.
782 }
783 procedure AllocateOracleStatementHandles(PlainDriver: IZOraclePlainDriver;
784  Connection: IZConnection; var Handle: POCIStmt; var ErrorHandle: POCIError);
785 var
786  OracleConnection: IZOracleConnection;
787 begin
788  OracleConnection := Connection as IZOracleConnection;
789  ErrorHandle := nil;
790  PlainDriver.HandleAlloc(OracleConnection.GetConnectionHandle,
791  ErrorHandle, OCI_HTYPE_ERROR, 0, nil);
792  Handle := nil;
793  PlainDriver.HandleAlloc(OracleConnection.GetConnectionHandle,
794  Handle, OCI_HTYPE_STMT, 0, nil);
795 end;
796 
797 {**
798  Frees from memory Oracle handlers for Statement object.
799  @param PlainDriver an Oracle plain driver.
800  @param Handle a holder for Statement handle.
801  @param ErrorHandle a holder for Error handle.
802 }
803 procedure FreeOracleStatementHandles(PlainDriver: IZOraclePlainDriver;
804  var Handle: POCIStmt; var ErrorHandle: POCIError);
805 begin
806  if ErrorHandle <> nil then
807  begin
808  PlainDriver.HandleFree(ErrorHandle, OCI_HTYPE_ERROR);
809  ErrorHandle := nil;
810  end;
811  if Handle <> nil then
812  begin
813  PlainDriver.HandleFree(Handle, OCI_HTYPE_STMT);
814  Handle := nil;
815  end;
816 end;
817 
818 {**
819  Prepares an Oracle statement.
820  @param PlainDriver an Oracle plain driver.
821  @param SQL an SQL query to be prepared.
822  @param Handle a holder for Statement handle.
823  @param ErrorHandle a holder for Error handle.
824 }
825 procedure PrepareOracleStatement(PlainDriver: IZOraclePlainDriver;
826  SQL: RawByteString; LogSQL: String; Handle: POCIStmt;
827  ErrorHandle: POCIError; PrefetchCount: ub4; ConSettings: PZConSettings);
828 var
829  Status: Integer;
830 begin
831  PlainDriver.AttrSet(Handle, OCI_HTYPE_STMT, @PrefetchCount, SizeOf(ub4),
832  OCI_ATTR_PREFETCH_ROWS, ErrorHandle);
833  Status := PlainDriver.StmtPrepare(Handle, ErrorHandle, PAnsiChar(SQL),
834  Length(SQL)+1, OCI_NTV_SYNTAX, OCI_DEFAULT);
835  CheckOracleError(PlainDriver, ErrorHandle, Status, lcExecute, LogSQL);
836 end;
837 
838 {**
839  Executes an Oracle statement.
840  @param PlainDriver an Oracle plain driver.
841  @param Connection an Oracle connection Object.
842  @param SQL an SQL query to be prepared.
843  @param Handle a holder for Statement handle.
844  @param ErrorHandle a holder for Error handle.
845 }
846 procedure ExecuteOracleStatement(PlainDriver: IZOraclePlainDriver;
847  Connection: IZConnection; LogSQL: string; Handle: POCIStmt; ErrorHandle: POCIError);
848 var
849  Status: Integer;
850  OracleConnection: IZOracleConnection;
851 begin
852  OracleConnection := Connection as IZOracleConnection;
853  Status := PlainDriver.StmtExecute(OracleConnection.GetContextHandle,
854  Handle, ErrorHandle, 1, 0, nil, nil, OCI_DEFAULT);
855  CheckOracleError(PlainDriver, ErrorHandle, Status, lcExecute, LogSQL);
856 end;
857 
858 {**
859  Gets a number of updates made by executed Oracle statement.
860  @param PlainDriver an Oracle plain driver.
861  @param Handle a holder for Statement handle.
862  @param ErrorHandle a holder for Error handle.
863  @returns a number of updates.
864 }
865 function GetOracleUpdateCount(PlainDriver: IZOraclePlainDriver;
866  Handle: POCIStmt; ErrorHandle: POCIError): ub4;
867 begin
868  Result := 0;
869  PlainDriver.AttrGet(Handle, OCI_HTYPE_STMT, @Result, nil,
870  OCI_ATTR_ROW_COUNT, ErrorHandle);
871 end;
872 
873 {**
874  recurses down the field's TDOs and saves the little bits it need for later
875  use on a fetch SQLVar._obj
876 }
877 function DescribeObject(PlainDriver: IZOraclePlainDriver; Connection: IZConnection;
878  ParamHandle: POCIParam; stmt_handle: POCIHandle; Level: ub2): POCIObject;
879 var
880  type_ref: POCIRef;
881 
882  function AllocateObject: POCIObject;
883  begin
884  Result := New(POCIObject);
885  FillChar(Result^, SizeOf(TOCIObject), 0);
886  end;
887 
888  procedure DescribeObjectByTDO(PlainDriver: IZOraclePlainDriver;
889  Connection: IZConnection; var obj: POCIObject);
890  var
891  FConnection: IZOracleConnection;
892  list_attibutes: POCIParam;
893  name: PAnsiChar;
894  temp: RawByteString;
895  len: ub4;
896  I: ub2;
897  Fld: POCIObject;
898  begin
899  FConnection := Connection as IZOracleConnection;
900 
901  CheckOracleError(PlainDriver, FConnection.GetErrorHandle,
902  PlainDriver.DescribeAny(FConnection.GetContextHandle,
903  FConnection.GetErrorHandle, obj.tdo, 0, OCI_OTYPE_PTR, OCI_DEFAULT,
904  OCI_PTYPE_TYPE, FConnection.GetDescribeHandle),
905  lcOther, 'OCIDescribeAny(OCI_PTYPE_TYPE) of OCI_OTYPE_PTR');
906 
907  //we have the Actual TDO so lets see what it is made up of by a describe
908  Len := 0; //and we store it in the object's paramdp for now
909  CheckOracleError(PlainDriver, FConnection.GetErrorHandle,
910  PlainDriver.AttrGet(FConnection.GetDescribeHandle, OCI_HTYPE_DESCRIBE,
911  @obj.parmdp, @Len, OCI_ATTR_PARAM, FConnection.GetErrorHandle),
912  lcOther, 'OCIAttrGet(OCI_HTYPE_DESCRIBE) of OCI_ATTR_PARAM');
913 
914  //Get the SchemaName of the Object
915  CheckOracleError(PlainDriver, FConnection.GetErrorHandle,
916  PlainDriver.AttrGet(obj.parmdp, OCI_DTYPE_PARAM,
917  @name, @len, OCI_ATTR_SCHEMA_NAME, FConnection.GetErrorHandle),
918  lcOther, 'OCIAttrGet(OCI_ATTR_SCHEMA_NAME) of OCI_DTYPE_PARAM');
919 
920  SetLength(temp, len+1);
921  temp := {$IFDEF WITH_STRPLCOPY_DEPRECATED}AnsiStrings.{$ENDIF}StrPLCopy(PAnsiChar(temp), name, len);
922  Obj.type_schema := PlainDriver.ZDbcString(temp, Connection.GetConSettings);
923 
924  //Get the TypeName of the Object
925  CheckOracleError(PlainDriver, FConnection.GetErrorHandle,
926  PlainDriver.AttrGet(obj.parmdp, OCI_DTYPE_PARAM,
927  @name, @len, OCI_ATTR_NAME, FConnection.GetErrorHandle),
928  lcOther, 'OCIAttrGet(OCI_ATTR_NAME) of OCI_DTYPE_PARAM');
929 
930  SetLength(temp, len+1);
931  temp := {$IFDEF WITH_STRPLCOPY_DEPRECATED}AnsiStrings.{$ENDIF}StrPLCopy(PAnsiChar(temp), name, len);
932  Obj.type_name := PlainDriver.ZDbcString(temp, Connection.GetConSettings);
933 
934  //Get the TypeCode of the Object
935  CheckOracleError(PlainDriver, FConnection.GetErrorHandle,
936  PlainDriver.AttrGet(obj.parmdp, OCI_DTYPE_PARAM,
937  @Obj.typecode, nil, OCI_ATTR_TYPECODE, FConnection.GetErrorHandle),
938  lcOther, 'OCIAttrGet(OCI_ATTR_TYPECODE) of OCI_DTYPE_PARAM');
939 
940  if (obj.typecode = OCI_TYPECODE_OBJECT ) or ( obj.typecode = OCI_TYPECODE_OPAQUE) then
941  begin
942  //we will need a reff to the TDO for the pin operation
943  CheckOracleError(PlainDriver, FConnection.GetErrorHandle,
944  PlainDriver.AttrGet(obj.parmdp, OCI_DTYPE_PARAM,
945  @Obj.obj_ref, nil, OCI_ATTR_REF_TDO, FConnection.GetErrorHandle),
946  lcOther, 'OCIAttrGet(OCI_ATTR_REF_TDO) of OCI_DTYPE_PARAM');
947 
948  //now we'll pin the object
949  CheckOracleError(PlainDriver, FConnection.GetErrorHandle,
950  PlainDriver.ObjectPin(FConnection.GetConnectionHandle, FConnection.GetErrorHandle,
951  Obj.obj_ref, nil, OCI_PIN_LATEST, OCI_DURATION_SESSION, pub2(OCI_LOCK_NONE),
952  @obj.obj_type),
953  lcOther, 'OCIObjectPin(OCI_PIN_LATEST, OCI_DURATION_SESSION, OCI_LOCK_NONE)');
954  Obj.Pinned := True;
955 
956  //is the object the final type or an type-descriptor?
957  CheckOracleError(PlainDriver, FConnection.GetErrorHandle,
958  PlainDriver.AttrGet(obj.parmdp, OCI_DTYPE_PARAM,
959  @Obj.is_final_type, nil, OCI_ATTR_IS_FINAL_TYPE, FConnection.GetErrorHandle),
960  lcOther, 'OCIAttrGet(OCI_ATTR_IS_FINAL_TYPE) of OCI_DTYPE_PARAM(SubType)');
961 
962  //Get the FieldCount
963  CheckOracleError(PlainDriver, FConnection.GetErrorHandle,
964  PlainDriver.AttrGet(obj.parmdp, OCI_DTYPE_PARAM,
965  @Obj.field_count, nil, OCI_ATTR_NUM_TYPE_ATTRS, FConnection.GetErrorHandle),
966  lcOther, 'OCIAttrGet(OCI_ATTR_NUM_TYPE_ATTRS) of OCI_DTYPE_PARAM(SubType)');
967 
968  //now get the differnt fields of this object add one field object for property
969  SetLength(Obj.fields, Obj.field_count);
970 
971  //a field is just another instance of an obj not a new struct
972  CheckOracleError(PlainDriver, FConnection.GetErrorHandle,
973  PlainDriver.AttrGet(obj.parmdp, OCI_DTYPE_PARAM,
974  @list_attibutes, nil, OCI_ATTR_LIST_TYPE_ATTRS, FConnection.GetErrorHandle),
975  lcOther, 'OCIAttrGet(OCI_ATTR_LIST_TYPE_ATTRS) of OCI_DTYPE_PARAM(SubType)');
976 
977  if obj.field_count > 0 then
978  for I := 0 to obj.field_count-1 do
979  begin
980  Fld := AllocateObject; //allocate a new object
981  Obj.fields[i] := Fld; //assign the object to the field-list
982 
983  CheckOracleError(PlainDriver, FConnection.GetErrorHandle,
984  PlainDriver.ParamGet(list_attibutes, OCI_DTYPE_PARAM,
985  FConnection.GetErrorHandle, Fld.parmdp, I+1),
986  lcOther, 'OCIParamGet(OCI_DTYPE_PARAM) of OCI_DTYPE_PARAM(Element)');
987 
988  // get the name of the attribute
989  len := 0;
990  CheckOracleError(PlainDriver, FConnection.GetErrorHandle,
991  PlainDriver.AttrGet(Fld.parmdp, OCI_DTYPE_PARAM,
992  @name, @len, OCI_ATTR_NAME, FConnection.GetErrorHandle),
993  lcOther, 'OCIAttrGet(OCI_ATTR_NAME) of OCI_DTYPE_PARAM(Element)');
994 
995  SetLength(temp, len+1);
996  temp := {$IFDEF WITH_STRPLCOPY_DEPRECATED}AnsiStrings.{$ENDIF}StrPLCopy(PAnsiChar(temp), name, len);
997  Fld.type_name := PlainDriver.ZDbcString(temp, Connection.GetConSettings);
998 
999  // get the typeCode of the attribute
1000  CheckOracleError(PlainDriver, FConnection.GetErrorHandle,
1001  PlainDriver.AttrGet(Fld.parmdp, OCI_DTYPE_PARAM,
1002  @Fld.typecode, nil, OCI_ATTR_TYPECODE, FConnection.GetErrorHandle),
1003  lcOther, 'OCIAttrGet(OCI_ATTR_TYPECODE) of OCI_DTYPE_PARAM(Element)');
1004 
1005  if (fld.typecode = OCI_TYPECODE_OBJECT) or
1006  (fld.typecode = OCI_TYPECODE_VARRAY) or
1007  (fld.typecode = OCI_TYPECODE_TABLE) or
1008  (fld.typecode = OCI_TYPECODE_NAMEDCOLLECTION) then
1009  //this is some sort of object or collection so lets drill down some more
1010  fld.next_subtype := DescribeObject(PlainDriver, Connection, fld.parmdp,
1011  obj.stmt_handle, obj.Level+1);
1012  end;
1013  end
1014  else
1015  begin
1016  //this is an embedded table or varray of some form so find out what is in it*/
1017 
1018  CheckOracleError(PlainDriver, FConnection.GetErrorHandle,
1019  PlainDriver.AttrGet(obj.parmdp, OCI_DTYPE_PARAM,
1020  @obj.col_typecode, nil, OCI_ATTR_COLLECTION_TYPECODE, FConnection.GetErrorHandle),
1021  lcOther, 'OCIAttrGet(OCI_ATTR_COLLECTION_TYPECODE) of OCI_DTYPE_PARAM');
1022 
1023  //first get what sort of collection it is by coll typecode
1024  CheckOracleError(PlainDriver, FConnection.GetErrorHandle,
1025  PlainDriver.AttrGet(obj.parmdp, OCI_DTYPE_PARAM,
1026  @obj.parmap, nil, OCI_ATTR_COLLECTION_ELEMENT, FConnection.GetErrorHandle),
1027  lcOther, 'OCIAttrGet(OCI_ATTR_COLLECTION_ELEMENT) of OCI_DTYPE_PARAM');
1028 
1029  CheckOracleError(PlainDriver, FConnection.GetErrorHandle,
1030  PlainDriver.AttrGet(obj.parmdp, OCI_DTYPE_PARAM,
1031  @obj.elem_typecode, nil, OCI_ATTR_TYPECODE, FConnection.GetErrorHandle),
1032  lcOther, 'OCIAttrGet(OCI_ATTR_TYPECODE of Element) of OCI_DTYPE_PARAM');
1033 
1034  if (obj.elem_typecode = OCI_TYPECODE_OBJECT) or
1035  (obj.elem_typecode = OCI_TYPECODE_VARRAY) or
1036  (obj.elem_typecode = OCI_TYPECODE_TABLE) or
1037  (obj.elem_typecode = OCI_TYPECODE_NAMEDCOLLECTION) then
1038  //this is some sort of object or collection so lets drill down some more
1039  obj.next_subtype := DescribeObject(PlainDriver, Connection, obj.parmap,
1040  obj.stmt_handle, obj.Level+1);
1041  end;
1042  end;
1043 begin
1044  Result := AllocateObject;
1045 
1046  //Describe the field (OCIParm) we know it is a object or a collection
1047 
1048  //Get the Actual TDO
1049  CheckOracleError(PlainDriver, (Connection as IZOracleConnection).GetErrorHandle,
1050  PlainDriver.AttrGet(ParamHandle, OCI_DTYPE_PARAM, @type_ref, nil,
1051  OCI_ATTR_REF_TDO, (Connection as IZOracleConnection).GetErrorHandle),
1052  lcOther, 'OCIAttrGet OCI_ATTR_REF_TDO of OCI_DTYPE_PARAM');
1053 
1054  CheckOracleError(PlainDriver, (Connection as IZOracleConnection).GetErrorHandle,
1055  PlainDriver.TypeByRef((Connection as IZOracleConnection).GetConnectionHandle,
1056  (Connection as IZOracleConnection).GetErrorHandle, type_ref,
1057  OCI_DURATION_TRANS, OCI_TYPEGET_ALL, @Result.tdo),
1058  lcOther, 'OCITypeByRef from OCI_ATTR_REF_TDO');
1059  Result^.Level := Level;
1060  DescribeObjectByTDO(PlainDriver, Connection, Result);
1061 end;
1062 
1063 end.
1064