Browse Source

fcl-db: oracle: adjust buffer for BLOBs, when reading CLOBs,NCLOBs. Bug #32377

git-svn-id: trunk@37184 -
lacak 8 years ago
parent
commit
950082f371

+ 28 - 9
packages/fcl-db/src/sqldb/oracle/oracleconnection.pp

@@ -383,19 +383,24 @@ var
   ConnectString : string;
   ConnectString : string;
   TempServiceContext : POCISvcCtx;
   TempServiceContext : POCISvcCtx;
   IsConnected : boolean;
   IsConnected : boolean;
+  CharSetId: ub2;
 begin
 begin
 {$IfDef LinkDynamically}
 {$IfDef LinkDynamically}
   InitialiseOCI;
   InitialiseOCI;
 {$EndIf}
 {$EndIf}
 
 
   inherited DoInternalConnect;
   inherited DoInternalConnect;
-  //todo: get rid of FUserMem, as it isn't used
+  //ToDo: get rid of FUserMem, as it isn't used
   FUserMem := nil;
   FUserMem := nil;
   IsConnected := false;
   IsConnected := false;
 
 
   try
   try
+    case GetConnectionCharSet of
+      'utf8': CharSetId := 873;
+      else    CharSetId := 0; // if it is 0, the NLS_LANG and NLS_NCHAR environment variables are used
+    end;
     // Create environment handle
     // Create environment handle
-    if OCIEnvCreate(FOciEnvironment,OCI_DEFAULT,nil,nil,nil,nil,0,FUserMem) <> OCI_SUCCESS then
+    if OCIEnvNlsCreate(FOciEnvironment,OCI_DEFAULT,nil,nil,nil,nil,0,FUserMem,CharSetId,CharSetId) <> OCI_SUCCESS then
       DatabaseError(SErrEnvCreateFailed,self);
       DatabaseError(SErrEnvCreateFailed,self);
     // Create error handle
     // Create error handle
     if OciHandleAlloc(FOciEnvironment,FOciError,OCI_HTYPE_ERROR,0,FUserMem) <> OCI_SUCCESS then
     if OciHandleAlloc(FOciEnvironment,FOciError,OCI_HTYPE_ERROR,0,FUserMem) <> OCI_SUCCESS then
@@ -405,10 +410,10 @@ begin
       DatabaseError(SErrHandleAllocFailed,self);
       DatabaseError(SErrHandleAllocFailed,self);
 
 
     // Initialize server handle
     // Initialize server handle
-    if hostname='' then
-      connectstring := databasename
+    if HostName='' then
+      ConnectString := DatabaseName
     else
     else
-      connectstring := '//'+hostname+'/'+databasename;
+      ConnectString := '//'+HostName+'/'+DatabaseName;
     if OCIServerAttach(FOciServer,FOciError,@(ConnectString[1]),Length(ConnectString),OCI_DEFAULT) <> OCI_SUCCESS then
     if OCIServerAttach(FOciServer,FOciError,@(ConnectString[1]),Length(ConnectString),OCI_DEFAULT) <> OCI_SUCCESS then
       HandleError();
       HandleError();
 
 
@@ -453,7 +458,7 @@ begin
     if not IsConnected then
     if not IsConnected then
     begin
     begin
       if assigned(FOciServer) then
       if assigned(FOciServer) then
-      OCIHandleFree(FOciServer,OCI_HTYPE_SERVER);
+        OCIHandleFree(FOciServer,OCI_HTYPE_SERVER);
       if assigned(FOciError) then
       if assigned(FOciError) then
         OCIHandleFree(FOciError,OCI_HTYPE_ERROR);
         OCIHandleFree(FOciError,OCI_HTYPE_ERROR);
       if assigned(FOciEnvironment) then
       if assigned(FOciEnvironment) then
@@ -1120,7 +1125,7 @@ end;
 procedure TOracleConnection.LoadBlobIntoBuffer(FieldDef: TFieldDef; ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction: TSQLTransaction);
 procedure TOracleConnection.LoadBlobIntoBuffer(FieldDef: TFieldDef; ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction: TSQLTransaction);
 var LobLocator: pointer;
 var LobLocator: pointer;
     LobCharSetForm: ub1;
     LobCharSetForm: ub1;
-    LobLength: ub4;
+    LobLength, LobSize: ub4;
 begin
 begin
   LobLocator := (cursor as TOracleCursor).FieldBuffers[FieldDef.FieldNo-1].Buffer;
   LobLocator := (cursor as TOracleCursor).FieldBuffers[FieldDef.FieldNo-1].Buffer;
   //if OCILobLocatorIsInit(TOracleTrans(ATransaction.Handle).FOciSvcCtx, FOciError, LobLocator, @is_init) = OCI_ERROR then
   //if OCILobLocatorIsInit(TOracleTrans(ATransaction.Handle).FOciSvcCtx, FOciError, LobLocator, @is_init) = OCI_ERROR then
@@ -1130,10 +1135,24 @@ begin
     HandleError;
     HandleError;
   if OCILobCharSetForm(FOciEnvironment, FOciError, LobLocator, @LobCharSetForm) = OCI_ERROR then
   if OCILobCharSetForm(FOciEnvironment, FOciError, LobLocator, @LobCharSetForm) = OCI_ERROR then
     HandleError;
     HandleError;
+  // Adjust initial buffer size (in bytes), while LobLength can be in characters
+  case LobCharSetForm of
+    0: ;            // BLOB
+    SQLCS_IMPLICIT, // CLOB
+    SQLCS_NCHAR:    // NCLOB
+      LobLength := LobLength*4;
+  end;
   ReAllocMem(ABlobBuf^.BlobBuffer^.Buffer, LobLength);
   ReAllocMem(ABlobBuf^.BlobBuffer^.Buffer, LobLength);
-  ABlobBuf^.BlobBuffer^.Size := LobLength;
-  if (LobLength > 0) and (OciLobRead(TOracleTrans(ATransaction.Handle).FOciSvcCtx, FOciError, LobLocator, @LobLength, 1, ABlobBuf^.BlobBuffer^.Buffer, LobLength, nil, nil, 0, LobCharSetForm) = OCI_ERROR) then
+  LobSize := 0;
+  // For CLOBs and NCLOBs the total amount of data which should be readed is on input in characters, but on output is in bytes if client character set is varying-width
+  // The application must call OCILobRead() (in streamed mode) over and over again to read more pieces of the LOB until the OCI_NEED_DATA error code is not returned.
+  // If the LOB is a BLOB, the csid and csfrm parameters are ignored.
+  if (LobLength > 0) and (OciLobRead(TOracleTrans(ATransaction.Handle).FOciSvcCtx, FOciError, LobLocator, @LobSize, 1, ABlobBuf^.BlobBuffer^.Buffer, LobLength, nil, nil, 0, LobCharSetForm) = OCI_ERROR) then
     HandleError;
     HandleError;
+  // Shrink initial buffer if needed (we assume that LobSize is in bytes, what is true for CLOB,NCLOB if client character set is varying-width, but if client character set is fixed-width then it is in characters)
+  if LobSize <> LobLength then
+    ReAllocMem(ABlobBuf^.BlobBuffer^.Buffer, LobSize);
+  ABlobBuf^.BlobBuffer^.Size := LobSize;
 end;
 end;
 
 
 procedure TOracleConnection.FreeFldBuffers(cursor: TSQLCursor);
 procedure TOracleConnection.FreeFldBuffers(cursor: TSQLCursor);

+ 1 - 1
packages/fcl-db/src/sqldb/sqldb.pp

@@ -1403,7 +1403,7 @@ function TSQLConnection.GetConnectionCharSet: string;
 begin
 begin
   // default implementation returns user supplied FCharSet
   // default implementation returns user supplied FCharSet
   // (can be overriden by descendants, which are able retrieve current connection charset using client API)
   // (can be overriden by descendants, which are able retrieve current connection charset using client API)
-  Result := FCharSet;
+  Result := LowerCase(FCharSet);
 end;
 end;
 
 
 function TSQLConnection.RowsAffected(cursor: TSQLCursor): TRowsCount;
 function TSQLConnection.RowsAffected(cursor: TSQLCursor): TRowsCount;