1 {*********************************************************}
3 { Zeos Database Objects }
4 { Oracle Database Connectivity Classes }
6 { Originally written by Sergey Seroukhov }
8 {*********************************************************}
10 {@********************************************************}
11 { Copyright (c) 1999-2012 Zeos Development Group }
13 { License Agreement: }
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. }
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. }
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) }
46 { http://www.sourceforge.net/projects/zeoslib. }
49 { Zeos Development Group. }
50 {********************************************************@}
59 Types, Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils,
60 ZSysUtils, ZDbcIntfs, ZVariant, ZPlainOracleDriver, ZDbcLogging,
61 ZCompatibility, ZPlainOracleConstants;
64 MAX_SQLVAR_LIMIT = 1024;
67 {** Declares SQL Object }
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?
94 TZSQLVar = {$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}packed{$endif} record
112 TZSQLVars = {$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}packed{$endif} record
115 Variables: array[1..MAX_SQLVAR_LIMIT] of TZSQLVar;
117 PZSQLVars = ^TZSQLVars;
119 TZOracleParam = Record
126 pParamIndex: Integer;
129 TZOracleParams = array of TZOracleParam;
132 Allocates memory for Oracle SQL Variables.
133 @param Variables a pointer to array of variables.
134 @param Count a number of SQL variables.
136 procedure AllocateOracleSQLVars(var Variables: PZSQLVars; Count: Integer);
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.
143 procedure FreeOracleSQLVars(const PlainDriver: IZOraclePlainDriver;
144 var Variables: PZSQLVars; const Handle: POCIEnv; const ErrorHandle: POCIError;
145 const ConSettings: PZConSettings);
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.
156 procedure InitializeOracleVar(PlainDriver: IZOraclePlainDriver;
157 Connection: IZConnection; var Variable: PZSQLVar;
158 DataType: TZSQLType; OracleType: ub2; DataSize: Integer);
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.
167 procedure LoadOracleVars(PlainDriver: IZOraclePlainDriver;
168 Connection: IZConnection; ErrorHandle: POCIError; Variables: PZSQLVars;
169 Values: TZVariantDynArray; ChunkSize: Integer);
172 Unloads Oracle variables binded to SQL statement with data.
173 @param Variables Oracle variable holders.
175 procedure UnloadOracleVars(Variables: PZSQLVars);
178 Convert string Oracle field type to SQLType
179 @param string field type value
180 @result the SQLType field type value
182 function ConvertOracleTypeToSQLType(TypeName: string;
183 Precision, Scale: Integer; const CtrlsCPType: TZControlsCodePage): TZSQLType;
186 Converts Oracle internal date into TDateTime
187 @param Value a pointer to Oracle internal date.
188 @return a decoded TDateTime value.
190 function OraDateToDateTime(Value: PAnsiChar): TDateTime;
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.
200 procedure CheckOracleError(PlainDriver: IZOraclePlainDriver;
201 ErrorHandle: POCIError; Status: Integer; LogCategory: TZLoggingCategory;
205 Creates an Oracle result set based on the current settings.
206 @return a created result set object.
208 function CreateOracleResultSet(PlainDriver: IZOraclePlainDriver;
209 Statement: IZStatement; LogSQL: string; Handle: POCIStmt;
210 ErrorHandle: POCIError): IZResultSet; overload;
213 Creates an Oracle result set based on the current settings.
214 @return a created result set object.
216 function CreateOracleResultSet(PlainDriver: IZOraclePlainDriver;
217 Statement: IZStatement; LogSQL: string; StmtHandle: POCIStmt;
218 ErrorHandle: POCIError; OutVars: PZSQLVars;
219 Const OracleParams: TZOracleParams): IZResultSet; overload;
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.
228 procedure AllocateOracleStatementHandles(PlainDriver: IZOraclePlainDriver;
229 Connection: IZConnection; var Handle: POCIStmt; var ErrorHandle: POCIError);
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.
237 procedure FreeOracleStatementHandles(PlainDriver: IZOraclePlainDriver;
238 var Handle: POCIStmt; var ErrorHandle: POCIError);
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.
247 procedure PrepareOracleStatement(PlainDriver: IZOraclePlainDriver;
248 SQL: RawByteString; LogSQL: String; Handle: POCIStmt; ErrorHandle: POCIError;
249 PrefetchCount: ub4; ConSettings: PZConSettings);
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.
259 procedure ExecuteOracleStatement(PlainDriver: IZOraclePlainDriver;
260 Connection: IZConnection; LogSQL: string; Handle: POCIStmt;
261 ErrorHandle: POCIError);
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.
270 function GetOracleUpdateCount(PlainDriver: IZOraclePlainDriver;
271 Handle: POCIStmt; ErrorHandle: POCIError): ub4;
273 function DescribeObject(PlainDriver: IZOraclePlainDriver; Connection: IZConnection;
274 ParamHandle: POCIParam; stmt_handle: POCIHandle; Level: ub2): POCIObject;
278 uses ZMessages, ZDbcOracle, ZDbcOracleResultSet, ZDbcCachedResultSet,
279 ZDbcGenericResolver, ZDbcUtils, ZEncoding
280 {$IFDEF WITH_UNITANSISTRINGS}, AnsiStrings{$ENDIF};
283 Calculates size of SQLVars record.
284 @param Count a number of variable.
285 @returns a record size.
287 function CalculateSQLVarsSize(Count: Integer): Integer;
289 Result := SizeOf(TZSQLVars) + Count * SizeOf(TZSQLVar);
293 Allocates memory for Oracle SQL Variables.
294 @param Variables a pointer to array of variables.
295 @param Count a number of SQL variables.
297 procedure AllocateOracleSQLVars(var Variables: PZSQLVars; Count: Integer);
301 if Variables <> nil then
304 Size := CalculateSQLVarsSize(Count);
305 GetMem(Variables, Size);
306 FillChar(Variables^, Size, 0);
307 Variables^.AllocNum := Count;
308 Variables^.ActualNum := 0;
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.
316 procedure FreeOracleSQLVars(const PlainDriver: IZOraclePlainDriver;
317 var Variables: PZSQLVars; const Handle: POCIEnv; const ErrorHandle: POCIError;
318 const ConSettings: PZConSettings);
321 CurrentVar: PZSQLVar;
323 procedure DisposeObject(var Obj: POCIObject);
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
332 DisposeObject(Obj.next_subtype);
333 Obj.next_subtype := nil;
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
342 //debugCheckOracleError(PlainDriver, ErrorHandle,
343 PlainDriver.ObjectFree(Handle,ErrorHandle, CurrentVar^._Obj.tdo, 0)
344 ;//debug, lcOther, 'OCIObjectFree', ConSettings);
350 if Variables <> nil then
352 { Frees allocated memory for output variables }
353 for I := 1 to Variables.ActualNum do
355 CurrentVar := @Variables.Variables[I];
356 if Assigned(CurrentVar._Obj) then
357 DisposeObject(CurrentVar^._Obj);
358 if CurrentVar.Data <> nil then
360 if CurrentVar.TypeCode in [SQLT_BLOB, SQLT_CLOB, SQLT_BFILEE, SQLT_CFILEE] then
362 PlainDriver.DescriptorFree(PPOCIDescriptor(CurrentVar.Data)^,
365 else if CurrentVar.TypeCode = SQLT_TIMESTAMP then
367 PlainDriver.DescriptorFree(PPOCIDescriptor(CurrentVar.Data)^,
368 OCI_DTYPE_TIMESTAMP);
370 FreeMem(CurrentVar.Data);
371 CurrentVar.Data := nil;
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.
390 procedure InitializeOracleVar(PlainDriver: IZOraclePlainDriver;
391 Connection: IZConnection; var Variable: PZSQLVar;
392 DataType: TZSQLType; OracleType: ub2; DataSize: Integer);
395 OracleConnection: IZOracleConnection;
397 OracleConnection := Connection as IZOracleConnection;
398 Variable.ColType := DataType;
399 Variable.TypeCode := OracleType;
400 Variable.DataSize := DataSize;
402 case Variable.ColType of
403 stByte, stShort, stInteger:
405 Variable.TypeCode := SQLT_INT;
406 Length := SizeOf(LongInt);
408 stFloat, stDouble, stLong:
410 Variable.TypeCode := SQLT_FLT;
411 Length := SizeOf(Double);
413 stDate, stTime, stTimestamp:
415 Variable.TypeCode := SQLT_TIMESTAMP;
416 Length := SizeOf(POCIDateTime);
418 stString, stUnicodeString:
420 Variable.TypeCode := SQLT_STR;
421 Length := Variable.DataSize + 1;
423 stAsciiStream, stUnicodeStream, stBinaryStream, stBytes:
425 if not (Variable.TypeCode in [SQLT_CLOB, SQLT_BLOB, SQLT_BFILEE, SQLT_CFILEE,SQLT_NTY]) then
427 if Variable.ColType = stAsciiStream then
428 Variable.TypeCode := SQLT_LVC
430 Variable.TypeCode := SQLT_LVB;
431 if Variable.DataSize = 0 then
432 Length := 128 * 1024 + SizeOf(Integer)
434 Length := Variable.DataSize + SizeOf(Integer);
437 Length := SizeOf(POCILobLocator);
439 stDataSet: ; //Do nothing here!
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
448 PlainDriver.DescriptorAlloc(OracleConnection.GetConnectionHandle,
449 PPOCIDescriptor(Variable.Data)^, OCI_DTYPE_LOB, 0, nil);
452 if Variable.TypeCode = SQLT_TIMESTAMP then
454 PlainDriver.DescriptorAlloc(OracleConnection.GetConnectionHandle,
455 PPOCIDescriptor(Variable.Data)^, OCI_DTYPE_TIMESTAMP, 0, nil);
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.
466 procedure LoadOracleVars(PlainDriver: IZOraclePlainDriver;
467 Connection: IZConnection; ErrorHandle: POCIError; Variables: PZSQLVars;
468 Values: TZVariantDynArray; ChunkSize: Integer);
472 CurrentVar: PZSQLVar;
474 TempBytes: TByteDynArray;
476 WriteTempBlob: IZOracleBlob;
478 Year, Month, Day, Hour, Min, Sec, MSec: Word;
479 OracleConnection: IZOracleConnection;
481 OracleConnection := Connection as IZOracleConnection;
482 for I := 0 to Variables.ActualNum - 1 do
484 CurrentVar := @Variables.Variables[I + 1];
485 CurrentVar.DupData := CurrentVar.Data;
486 if (high(Values)<I) or DefVarManager.IsNull(Values[I]) then
488 CurrentVar.Indicator := -1;
489 //CurrentVar.Data := nil;
493 CurrentVar.Indicator := 0;
494 case CurrentVar.TypeCode of
497 PLongInt(CurrentVar.Data)^ :=
498 DefVarManager.GetAsInteger(Values[I]);
502 PDouble(CurrentVar.Data)^ :=
503 DefVarManager.GetAsFloat(Values[I]);
507 case Values[i].VType of
509 {$IFDEF WITH_STRLCOPY_DEPRECATED}AnsiStrings.{$ENDIF}StrLCopy(PAnsiChar(CurrentVar.Data),
510 PAnsiChar(PlainDriver.ZPlainString(SoftVarManager.GetAsUnicodeString(Values[I]), Connection.GetConSettings)), 1024);
512 {$IFDEF WITH_STRLCOPY_DEPRECATED}AnsiStrings.{$ENDIF}StrLCopy(PAnsiChar(CurrentVar.Data),
513 PAnsiChar(PlainDriver.ZPlainString(SoftVarManager.GetAsString(Values[I]), Connection.GetConSettings)), 1024);
518 {$IFDEF WITH_STRLCOPY_DEPRECATED}AnsiStrings.{$ENDIF}StrLCopy(PAnsiChar(CurrentVar.Data), PAnsiChar(UTF8Encode(DefVarManager.GetAsUnicodeString(Values[I]))), 1024);
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, '');
531 SQLT_BLOB, SQLT_CLOB:
533 if Values[I].VType = vtBytes then
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);
543 TempBlob := DefVarManager.GetAsInterface(Values[I]) as IZBlob;
544 if not TempBlob.IsEmpty then
546 if (CurrentVar.TypeCode = SQLT_CLOB) then
547 TempStream := TStringStream.Create(GetValidatedAnsiStringFromBuffer(TempBlob.GetBuffer,
548 TempBlob.Length, TempBlob.WasDecoded, Connection.GetConSettings))
550 TempStream := TempBlob.GetStream;
552 else TempStream := TMemoryStream.Create;
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;
562 WriteTempBlob := nil;
572 Unloads Oracle variables binded to SQL statement with data.
573 @param Variables Oracle variable holders.
575 procedure UnloadOracleVars(Variables: PZSQLVars);
578 CurrentVar: PZSQLVar;
580 for I := 1 to Variables.ActualNum do
582 CurrentVar := @Variables.Variables[I];
583 CurrentVar.Blob := nil;
584 CurrentVar.Data := CurrentVar.DupData;
589 Convert string Oracle field type to SQLType
590 @param string field type value
591 @result the SQLType field type value
593 function ConvertOracleTypeToSQLType(TypeName: string;
594 Precision, Scale: Integer; const CtrlsCPType: TZControlsCodePage): TZSQLType;
596 TypeName := UpperCase(TypeName);
599 if (TypeName = 'CHAR') or (TypeName = 'VARCHAR2') then
601 else if (TypeName = 'NCHAR') or (TypeName = 'NVARCHAR2') then
603 else if (TypeName = 'FLOAT') or (TypeName = 'BINARY_FLOAT') or (TypeName = 'BINARY_DOUBLE') then
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
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
625 Result := stDouble; { default for number types}
626 if (Scale = 0) and (Precision <> 0) then
628 if Precision <= 2 then
630 else if Precision <= 4 then
632 else if Precision <= 9 then
634 else if Precision <= 19 then
635 Result := stLong {!!in fact, unusable}
638 if ( CtrlsCPType = cCP_UTF16 ) then
640 stString: Result := stUnicodeString;
641 stAsciiStream: if not (TypeName = 'LONG') then Result := stUnicodeStream; //fix: http://zeos.firmos.at/viewtopic.php?t=3530
646 Converts Oracle internal date into TDateTime
647 @param Value a pointer to Oracle internal date.
648 @return a decoded TDateTime value.
650 function OraDateToDateTime(Value: PAnsiChar): TDateTime;
652 TOraDate = array[1..7] of Byte;
653 POraDate = ^TOraDate;
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);
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.
670 procedure CheckOracleError(PlainDriver: IZOraclePlainDriver;
671 ErrorHandle: POCIError; Status: Integer; LogCategory: TZLoggingCategory;
674 ErrorMessage: string;
675 ErrorBuffer: array[0..255] of AnsiChar;
684 OCI_SUCCESS_WITH_INFO:
686 PlainDriver.ErrorGet(ErrorHandle, 1, nil, ErrorCode, ErrorBuffer, 255,
688 ErrorMessage := 'OCI_SUCCESS_WITH_INFO: ' + String(ErrorBuffer);
691 ErrorMessage := 'OCI_NEED_DATA';
693 ErrorMessage := 'OCI_NO_DATA';
696 PlainDriver.ErrorGet(ErrorHandle, 1, nil, ErrorCode, ErrorBuffer, 255,
698 ErrorMessage := 'OCI_ERROR: ' + String(ErrorBuffer);
701 ErrorMessage := 'OCI_INVALID_HANDLE';
703 ErrorMessage := 'OCI_STILL_EXECUTING';
705 ErrorMessage := 'OCI_CONTINUE';
708 if (Status <> OCI_SUCCESS) and (Status <> OCI_SUCCESS_WITH_INFO) and (ErrorMessage <> '') then
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]));
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);
724 Creates an Oracle result set based on the current settings.
725 @return a created result set object.
727 function CreateOracleResultSet(PlainDriver: IZOraclePlainDriver;
728 Statement: IZStatement; LogSQL: string; Handle: POCIStmt;
729 ErrorHandle: POCIError): IZResultSet;
731 NativeResultSet: TZOracleResultSet;
732 CachedResultSet: TZCachedResultSet;
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
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;
748 Result := NativeResultSet;
752 Creates an Oracle result set based on the current settings.
753 @return a created result set object.
755 function CreateOracleResultSet(PlainDriver: IZOraclePlainDriver;
756 Statement: IZStatement; LogSQL: string; StmtHandle: POCIStmt;
757 ErrorHandle: POCIError; OutVars: PZSQLVars;
758 Const OracleParams: TZOracleParams): IZResultSet;
760 NativeResultSet: TZOracleCallableResultSet;
761 CachedResultSet: TZCachedResultSet;
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;
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.
783 procedure AllocateOracleStatementHandles(PlainDriver: IZOraclePlainDriver;
784 Connection: IZConnection; var Handle: POCIStmt; var ErrorHandle: POCIError);
786 OracleConnection: IZOracleConnection;
788 OracleConnection := Connection as IZOracleConnection;
790 PlainDriver.HandleAlloc(OracleConnection.GetConnectionHandle,
791 ErrorHandle, OCI_HTYPE_ERROR, 0, nil);
793 PlainDriver.HandleAlloc(OracleConnection.GetConnectionHandle,
794 Handle, OCI_HTYPE_STMT, 0, nil);
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.
803 procedure FreeOracleStatementHandles(PlainDriver: IZOraclePlainDriver;
804 var Handle: POCIStmt; var ErrorHandle: POCIError);
806 if ErrorHandle <> nil then
808 PlainDriver.HandleFree(ErrorHandle, OCI_HTYPE_ERROR);
811 if Handle <> nil then
813 PlainDriver.HandleFree(Handle, OCI_HTYPE_STMT);
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.
825 procedure PrepareOracleStatement(PlainDriver: IZOraclePlainDriver;
826 SQL: RawByteString; LogSQL: String; Handle: POCIStmt;
827 ErrorHandle: POCIError; PrefetchCount: ub4; ConSettings: PZConSettings);
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);
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.
846 procedure ExecuteOracleStatement(PlainDriver: IZOraclePlainDriver;
847 Connection: IZConnection; LogSQL: string; Handle: POCIStmt; ErrorHandle: POCIError);
850 OracleConnection: IZOracleConnection;
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);
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.
865 function GetOracleUpdateCount(PlainDriver: IZOraclePlainDriver;
866 Handle: POCIStmt; ErrorHandle: POCIError): ub4;
869 PlainDriver.AttrGet(Handle, OCI_HTYPE_STMT, @Result, nil,
870 OCI_ATTR_ROW_COUNT, ErrorHandle);
874 recurses down the field's TDOs and saves the little bits it need for later
875 use on a fetch SQLVar._obj
877 function DescribeObject(PlainDriver: IZOraclePlainDriver; Connection: IZConnection;
878 ParamHandle: POCIParam; stmt_handle: POCIHandle; Level: ub2): POCIObject;
882 function AllocateObject: POCIObject;
884 Result := New(POCIObject);
885 FillChar(Result^, SizeOf(TOCIObject), 0);
888 procedure DescribeObjectByTDO(PlainDriver: IZOraclePlainDriver;
889 Connection: IZConnection; var obj: POCIObject);
891 FConnection: IZOracleConnection;
892 list_attibutes: POCIParam;
899 FConnection := Connection as IZOracleConnection;
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');
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');
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');
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);
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');
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);
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');
940 if (obj.typecode = OCI_TYPECODE_OBJECT ) or ( obj.typecode = OCI_TYPECODE_OPAQUE) then
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');
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),
953 lcOther, 'OCIObjectPin(OCI_PIN_LATEST, OCI_DURATION_SESSION, OCI_LOCK_NONE)');
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)');
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)');
968 //now get the differnt fields of this object add one field object for property
969 SetLength(Obj.fields, Obj.field_count);
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)');
977 if obj.field_count > 0 then
978 for I := 0 to obj.field_count-1 do
980 Fld := AllocateObject; //allocate a new object
981 Obj.fields[i] := Fld; //assign the object to the field-list
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)');
988 // get the name of the attribute
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)');
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);
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)');
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);
1016 //this is an embedded table or varray of some form so find out what is in it*/
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');
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');
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');
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);
1044 Result := AllocateObject;
1046 //Describe the field (OCIParm) we know it is a object or a collection
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');
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);