[Libreoffice-commits] core.git: 2 commits - connectivity/source

Andrzej Hunt andrzej.hunt at collabora.com
Wed Jan 1 11:11:52 PST 2014


 connectivity/source/drivers/firebird/Blob.cxx              |  241 +++++++------
 connectivity/source/drivers/firebird/Blob.hxx              |   48 ++
 connectivity/source/drivers/firebird/PreparedStatement.cxx |  128 ++++++
 connectivity/source/drivers/firebird/PreparedStatement.hxx |    8 
 4 files changed, 316 insertions(+), 109 deletions(-)

New commits:
commit 2b24eec3d4ca50e676c0c3af86ecb92a8eb0a8a2
Author: Andrzej Hunt <andrzej.hunt at collabora.com>
Date:   Sat Dec 7 20:32:55 2013 +0000

    fdo#70664 Allow reading firebird Blob as InputStream.
    
    Change-Id: Ie0cb93a902961b3f37daf435828263478c9d2997

diff --git a/connectivity/source/drivers/firebird/Blob.cxx b/connectivity/source/drivers/firebird/Blob.cxx
index f27835d..e1968ba 100644
--- a/connectivity/source/drivers/firebird/Blob.cxx
+++ b/connectivity/source/drivers/firebird/Blob.cxx
@@ -13,6 +13,8 @@
 
 #include "connectivity/dbexception.hxx"
 
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
 using namespace ::connectivity::firebird;
 
 using namespace ::cppu;
@@ -32,7 +34,8 @@ Blob::Blob(isc_db_handle* pDatabaseHandle,
     m_blobID(aBlobID),
     m_blobHandle(0),
     m_bBlobOpened(false),
-    m_blobData(0)
+    m_nBlobLength(0),
+    m_nBlobPosition(0)
 {
 }
 
@@ -52,12 +55,41 @@ void Blob::ensureBlobIsOpened()
                           &m_blobID,
                           0,
                           NULL);
+
     if (aErr)
         evaluateStatusVector(m_statusVector, "isc_open_blob2", *this);
 
+    m_bBlobOpened = true;
+    m_nBlobPosition = 0;
+
+    char aBlobItems[] = {
+        isc_info_blob_total_length
+    };
+    char aResultBuffer[20];
+
+    isc_blob_info(m_statusVector,
+                  &m_blobHandle,
+                  sizeof(aBlobItems),
+                  aBlobItems,
+                  sizeof(aResultBuffer),
+                  aResultBuffer);
+
+    if (aErr)
+        evaluateStatusVector(m_statusVector, "isc_blob_info", *this);
+
+    if (*aResultBuffer == isc_info_blob_total_length)
+    {
+        short aResultLength = (short) isc_vax_integer(aResultBuffer+1, 2);
+        m_nBlobLength =  isc_vax_integer(aResultBuffer+3, aResultLength);
+    }
+    else
+    {
+        assert(false);
+    }
 }
 
-void SAL_CALL Blob::disposing(void)
+void Blob::closeBlob()
+    throw (SQLException)
 {
     MutexGuard aGuard(m_aMutex);
 
@@ -67,20 +99,26 @@ void SAL_CALL Blob::disposing(void)
         aErr = isc_close_blob(m_statusVector,
                               &m_blobHandle);
         if (aErr)
-        {
-            try
-            {
-                evaluateStatusVector(m_statusVector, "isc_close_blob", *this);
-            }
-            catch (SQLException e)
-            {
-                // we cannot throw any exceptions here anyway
-                SAL_WARN("connectivity.firebird", "isc_close_blob failed\n" <<
-                         e.Message);
-            }
-        }
+            evaluateStatusVector(m_statusVector, "isc_close_blob", *this);
+
+        m_bBlobOpened = false;
+        m_blobHandle = 0;
     }
+}
 
+void SAL_CALL Blob::disposing(void)
+{
+    try
+    {
+         closeBlob();
+    }
+    catch (SQLException e)
+    {
+        // we cannot throw any exceptions here...
+        SAL_WARN("connectivity.firebird", "isc_close_blob failed\n" <<
+                 e.Message);
+        assert(false);
+    }
     Blob_BASE::disposing();
 }
 
@@ -91,118 +129,131 @@ sal_Int64 SAL_CALL Blob::length()
     checkDisposed(Blob_BASE::rBHelper.bDisposed);
     ensureBlobIsOpened();
 
-    char aBlobItems[] = {
-        isc_info_blob_total_length
-    };
-    char aResultBuffer[20];
-
-    isc_blob_info(m_statusVector,
-                  &m_blobHandle,
-                  sizeof(aBlobItems),
-                  aBlobItems,
-                  sizeof(aResultBuffer),
-                  aResultBuffer);
-
-    evaluateStatusVector(m_statusVector, "isc_blob_info", *this);
-    if (*aResultBuffer == isc_info_blob_total_length)
-    {
-        short aResultLength = (short) isc_vax_integer(aResultBuffer, 2);
-        return isc_vax_integer(aResultBuffer+2, aResultLength);
-    }
-    return 0;
+    return m_nBlobLength;
 }
 
-uno::Sequence< sal_Int8 > SAL_CALL  Blob::getBytes(sal_Int64 aPosition, sal_Int32 aLength)
+uno::Sequence< sal_Int8 > SAL_CALL  Blob::getBytes(sal_Int64 nPosition,
+                                                   sal_Int32 nBytes)
     throw(SQLException, RuntimeException)
 {
     MutexGuard aGuard(m_aMutex);
     checkDisposed(Blob_BASE::rBHelper.bDisposed);
     ensureBlobIsOpened();
 
-    sal_Int64 aTotalLength = length();
+    if (nPosition > m_nBlobLength)
+        throw lang::IllegalArgumentException("nPosition out of range", *this, 0);
+    // We only have to read as many bytes as are available, i.e. nPosition+nBytes
+    // can legally be greater than the total length, hence we don't bother to check.
 
-    if (!(aPosition + aLength < aTotalLength))
+    if (nPosition > m_nBlobPosition)
     {
-        throw SQLException("Byte array requested outwith valid range", *this, OUString(), 1, Any() );
+        // Resets to the beginning (we can't seek these blobs)
+        closeBlob();
+        ensureBlobIsOpened();
     }
 
-    if (aTotalLength != m_blobData.getLength())
-    {
-        m_blobData = uno::Sequence< sal_Int8 >(aTotalLength);
-        char* pArray = (char*) m_blobData.getArray();
-        sal_Int64 aBytesRead = 0;
-
-        unsigned short aLengthRead; // The amount read in in a isc_get_segment call
-
-        ISC_STATUS aErr;
-        do
-        {
-            aErr = isc_get_segment(m_statusVector,
-                                   &m_blobHandle,
-                                   &aLengthRead,
-                                   aTotalLength - aBytesRead,
-                                   pArray + aBytesRead);
-        }
-        while (aErr == 0 || m_statusVector[1] == isc_segment);
-        // Denotes either sucessful read, or only part of segment read successfully.
-        if (aErr)
-        {
-            m_blobData = uno::Sequence< sal_Int8 >(0);
-            evaluateStatusVector(m_statusVector, "isc_get_segment", *this);
-        }
-    }
+    skipBytes(nPosition - m_nBlobPosition);
 
-    if (aLength<aTotalLength)
-    {
-        uno::Sequence< sal_Int8 > aRet(aLength);
-        memcpy(aRet.getArray(), m_blobData.getArray() + aLength, aLength);
-        return aRet;
-    }
-    else
-    {
-        return m_blobData; // TODO: subsequence
-    }
+    // Don't bother preallocating: readBytes does the appropriate calculations
+    // and reallocates for us.
+    uno::Sequence< sal_Int8 > aBytes;
+    readBytes(aBytes, nBytes);
+    return aBytes;
 }
 
 uno::Reference< XInputStream > SAL_CALL  Blob::getBinaryStream()
     throw(SQLException, RuntimeException)
 {
-//     MutexGuard aGuard(m_aMutex);
-//     checkDisposed(Blob_BASE::rBHelper.bDisposed);
-//     ensureBlobIsOpened();
-
-    ::dbtools::throwFeatureNotImplementedException("Blob::positionOfBlob", *this);
-    return NULL;
+    return this;
 }
 
-sal_Int64 SAL_CALL  Blob::position(const uno::Sequence< sal_Int8 >& rPattern,
-                                   sal_Int64 aStart)
+sal_Int64 SAL_CALL  Blob::position(const uno::Sequence< sal_Int8 >& /*rPattern*/,
+                                   sal_Int64 /*nStart*/)
     throw(SQLException, RuntimeException)
 {
-//     MutexGuard aGuard(m_aMutex);
-//     checkDisposed(Blob_BASE::rBHelper.bDisposed);
-//     ensureBlobIsOpened();
-
-    (void) rPattern;
-    (void) aStart;
-    ::dbtools::throwFeatureNotImplementedException("Blob::positionOfBlob", *this);
+    ::dbtools::throwFeatureNotImplementedException("Blob::position", *this);
     return 0;
 }
 
-sal_Int64 SAL_CALL  Blob::positionOfBlob(const uno::Reference< XBlob >& rPattern,
-                                         sal_Int64 aStart)
+sal_Int64 SAL_CALL  Blob::positionOfBlob(const uno::Reference< XBlob >& /*rPattern*/,
+                                         sal_Int64 /*aStart*/)
     throw(SQLException, RuntimeException)
 {
-//     MutexGuard aGuard(m_aMutex);
-//     checkDisposed(Blob_BASE::rBHelper.bDisposed);
-//     ensureBlobIsOpened();
-
-    (void) rPattern;
-    (void) aStart;
     ::dbtools::throwFeatureNotImplementedException("Blob::positionOfBlob", *this);
     return 0;
 }
 
+// ---- XInputStream ----------------------------------------------------------
+
+sal_Int32 SAL_CALL Blob::readBytes(uno::Sequence< sal_Int8 >& rDataOut,
+                                   sal_Int32 nBytes)
+    throw (NotConnectedException, BufferSizeExceededException, IOException, RuntimeException)
+{
+    MutexGuard aGuard(m_aMutex);
+    checkDisposed(Blob_BASE::rBHelper.bDisposed);
+    ensureBlobIsOpened();
+
+    // Ensure we have enough space for the amount of data we can actually read.
+    const sal_Int64 nBytesAvailable = m_nBlobLength - m_nBlobPosition;
+    const sal_Int32 nBytesToRead = nBytes < nBytesAvailable ? nBytes : nBytesAvailable;
+
+    if (rDataOut.getLength() < nBytesToRead)
+        rDataOut.realloc(nBytesToRead);
 
+    sal_Int32 nTotalBytesRead = 0;
+    ISC_STATUS aErr;
+    while (nTotalBytesRead < nBytesToRead)
+    {
+        sal_uInt16 nBytesRead = 0;
+        sal_uInt64 nDataRemaining = nBytesToRead - nTotalBytesRead;
+        sal_uInt16 nReadSize = (nDataRemaining > SAL_MAX_UINT16) ? SAL_MAX_UINT16 : nDataRemaining;
+        aErr = isc_get_segment(m_statusVector,
+                               &m_blobHandle,
+                               &nBytesRead,
+                               nReadSize,
+                               (char*) rDataOut.getArray() + nTotalBytesRead);
+        if (aErr)
+            evaluateStatusVector(m_statusVector, "isc_get_segment", *this);
+        nTotalBytesRead += nBytesRead;
+        m_nBlobPosition += nBytesRead;
+    }
+
+    return nTotalBytesRead;
+}
+
+sal_Int32 SAL_CALL Blob::readSomeBytes(uno::Sequence< sal_Int8 >& rDataOut,
+                                sal_Int32 nMaximumBytes)
+    throw (NotConnectedException, BufferSizeExceededException, IOException, RuntimeException)
+{
+    // We don't have any way of verifying how many bytes are immediately available,
+    // hence we just pass through direct to readBytes
+    // (Spec: "reads the available number of bytes, at maximum nMaxBytesToRead.")
+    return readBytes(rDataOut, nMaximumBytes);
+}
+
+void SAL_CALL Blob::skipBytes(sal_Int32 nBytesToSkip)
+    throw (NotConnectedException, BufferSizeExceededException, IOException, RuntimeException)
+{
+    // There is no way of directly skipping, hence we have to pretend to skip
+    // by reading & discarding the data.
+    uno::Sequence< sal_Int8 > aBytes;
+    readBytes(aBytes, nBytesToSkip);
+}
+
+sal_Int32 SAL_CALL Blob::available()
+    throw (NotConnectedException,  IOException, RuntimeException)
+{
+    MutexGuard aGuard(m_aMutex);
+    checkDisposed(Blob_BASE::rBHelper.bDisposed);
+    ensureBlobIsOpened();
+
+    return m_nBlobLength - m_nBlobPosition;
+}
+
+void SAL_CALL Blob::closeInput()
+    throw(NotConnectedException, IOException, RuntimeException)
+{
+    closeBlob();
+}
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file
diff --git a/connectivity/source/drivers/firebird/Blob.hxx b/connectivity/source/drivers/firebird/Blob.hxx
index f4eb792..3814f59 100644
--- a/connectivity/source/drivers/firebird/Blob.hxx
+++ b/connectivity/source/drivers/firebird/Blob.hxx
@@ -12,15 +12,17 @@
 
 #include <ibase.h>
 
-#include <cppuhelper/compbase1.hxx>
+#include <cppuhelper/compbase2.hxx>
 
+#include <com/sun/star/io/XInputStream.hpp>
 #include <com/sun/star/sdbc/XBlob.hpp>
 
 namespace connectivity
 {
     namespace firebird
     {
-        typedef ::cppu::WeakComponentImplHelper1< ::com::sun::star::sdbc::XBlob >
+        typedef ::cppu::WeakComponentImplHelper2< ::com::sun::star::sdbc::XBlob,
+                                                  ::com::sun::star::io::XInputStream >
             Blob_BASE;
 
         class Blob :
@@ -38,13 +40,19 @@ namespace connectivity
             isc_blob_handle     m_blobHandle;
 
             bool                m_bBlobOpened;
+            sal_Int64           m_nBlobLength;
+            sal_Int64           m_nBlobPosition;
 
             ISC_STATUS_ARRAY    m_statusVector;
 
-            ::com::sun::star::uno::Sequence< sal_Int8 > m_blobData;
-
             void ensureBlobIsOpened()
                 throw(::com::sun::star::sdbc::SQLException);
+            /**
+             * Closes the blob and cleans up resources -- can be used to reset
+             * the blob if we e.g. want to read from the beginning again.
+             */
+            void closeBlob()
+                throw(::com::sun::star::sdbc::SQLException);
 
         public:
             Blob(isc_db_handle* pDatabaseHandle,
@@ -75,6 +83,38 @@ namespace connectivity
                 throw(::com::sun::star::sdbc::SQLException,
                       ::com::sun::star::uno::RuntimeException);
 
+            // ---- XInputStream ----------------------------------------------
+            virtual sal_Int32 SAL_CALL
+                readBytes(::com::sun::star::uno::Sequence< sal_Int8 >& rDataOut,
+                          sal_Int32 nBytes)
+                throw(::com::sun::star::io::NotConnectedException,
+                      ::com::sun::star::io::BufferSizeExceededException,
+                      ::com::sun::star::io::IOException,
+                      ::com::sun::star::uno::RuntimeException);
+            virtual sal_Int32 SAL_CALL
+                readSomeBytes(::com::sun::star::uno::Sequence< sal_Int8 >& rDataOut,
+                              sal_Int32 nMaximumBytes)
+                throw(::com::sun::star::io::NotConnectedException,
+                      ::com::sun::star::io::BufferSizeExceededException,
+                        ::com::sun::star::io::IOException,
+                      ::com::sun::star::uno::RuntimeException);
+            virtual void SAL_CALL
+                skipBytes(sal_Int32 nBytes)
+                throw(::com::sun::star::io::NotConnectedException,
+                      ::com::sun::star::io::BufferSizeExceededException,
+                      ::com::sun::star::io::IOException,
+                      ::com::sun::star::uno::RuntimeException);
+            virtual sal_Int32 SAL_CALL
+                available()
+                throw(::com::sun::star::io::NotConnectedException,
+                      ::com::sun::star::io::IOException,
+                      ::com::sun::star::uno::RuntimeException);
+            virtual void SAL_CALL
+                closeInput()
+                throw(::com::sun::star::io::NotConnectedException,
+                      ::com::sun::star::io::IOException,
+                      ::com::sun::star::uno::RuntimeException);
+
             // ---- OComponentHelper ------------------------------------------
             virtual void SAL_CALL disposing();
         };
commit cb4b290bcfbc5ac73103a557f8de429c3d7d7c3b
Author: Andrzej Hunt <andrzej.hunt at collabora.com>
Date:   Thu Dec 5 13:58:59 2013 +0000

    fdo#70664 Implement Blob writing (firebird-sdbc).
    
    Change-Id: Ia95c6e1a0ede2103aae25610baeb3c7a9642113a

diff --git a/connectivity/source/drivers/firebird/PreparedStatement.cxx b/connectivity/source/drivers/firebird/PreparedStatement.cxx
index 5e02616..128f357 100644
--- a/connectivity/source/drivers/firebird/PreparedStatement.cxx
+++ b/connectivity/source/drivers/firebird/PreparedStatement.cxx
@@ -432,6 +432,43 @@ void SAL_CALL OPreparedStatement::setTimestamp(sal_Int32 nIndex, const DateTime&
 }
 // -------------------------------------------------------------------------
 
+// void OPreaparedStatement::set
+void OPreparedStatement::openBlobForWriting(isc_blob_handle& rBlobHandle, ISC_QUAD& rBlobId)
+{
+    ISC_STATUS aErr;
+
+    aErr = isc_create_blob2(m_statusVector,
+                            &m_pConnection->getDBHandle(),
+                            &m_pConnection->getTransaction(),
+                            &rBlobHandle,
+                            &rBlobId,
+                            0, // Blob parameter buffer length
+                            0); // Blob parameter buffer handle
+
+    if (aErr)
+    {
+        evaluateStatusVector(m_statusVector,
+                             "setBlob failed on " + m_sSqlStatement,
+                             *this);
+        assert(false);
+    }
+}
+
+void OPreparedStatement::closeBlobAfterWriting(isc_blob_handle& rBlobHandle)
+{
+    ISC_STATUS aErr;
+
+    aErr = isc_close_blob(m_statusVector,
+                          &rBlobHandle);
+    if (aErr)
+    {
+        evaluateStatusVector(m_statusVector,
+                             "isc_close_blob failed",
+                             *this);
+        assert(false);
+    }
+}
+
 void SAL_CALL OPreparedStatement::setClob( sal_Int32 parameterIndex, const Reference< XClob >& x ) throw(SQLException, RuntimeException)
 {
     (void) parameterIndex;
@@ -440,16 +477,53 @@ void SAL_CALL OPreparedStatement::setClob( sal_Int32 parameterIndex, const Refer
     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
 
 }
-// -------------------------------------------------------------------------
 
-void SAL_CALL OPreparedStatement::setBlob( sal_Int32 parameterIndex, const Reference< XBlob >& x ) throw(SQLException, RuntimeException)
+void SAL_CALL OPreparedStatement::setBlob(sal_Int32 nParameterIndex,
+                                          const Reference< XBlob >& xBlob)
+    throw (SQLException, RuntimeException)
 {
-    (void) parameterIndex;
-    (void) x;
-    ::osl::MutexGuard aGuard( m_aMutex );
+    ::osl::MutexGuard aGuard(m_aMutex);
     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
 
+    isc_blob_handle aBlobHandle = 0;
+    ISC_QUAD aBlobId;
+
+    openBlobForWriting(aBlobHandle, aBlobId);
+
+    // Max segment size is 2^16 == SAL_MAX_UINT16
+    sal_uInt64 nDataWritten = 0;
+    ISC_STATUS aErr;
+    while (xBlob->length() - nDataWritten > 0)
+    {
+        sal_uInt64 nDataRemaining = xBlob->length() - nDataWritten;
+        sal_uInt16 nWriteSize = (nDataRemaining > SAL_MAX_UINT16) ? SAL_MAX_UINT16 : nDataRemaining;
+        aErr = isc_put_segment(m_statusVector,
+                               &aBlobHandle,
+                               nWriteSize,
+                               (const char*) xBlob->getBytes(nDataWritten, nWriteSize).getConstArray());
+        nDataWritten += nWriteSize;
+
+
+        if (aErr)
+            break;
+
+    }
+
+    // We need to make sure we close the Blob even if their are errors, hence evaluate
+    // errors after closing.
+    closeBlobAfterWriting(aBlobHandle);
+
+    if (aErr)
+    {
+        evaluateStatusVector(m_statusVector,
+                             "isc_put_segment failed",
+                             *this);
+        assert(false);
+    }
+
+    setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
 }
+
 // -------------------------------------------------------------------------
 
 void SAL_CALL OPreparedStatement::setArray( sal_Int32 parameterIndex, const Reference< XArray >& x ) throw(SQLException, RuntimeException)
@@ -503,15 +577,49 @@ void SAL_CALL OPreparedStatement::setObject( sal_Int32 parameterIndex, const Any
     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
 
 }
-// -------------------------------------------------------------------------
 
-void SAL_CALL OPreparedStatement::setBytes( sal_Int32 parameterIndex, const Sequence< sal_Int8 >& x ) throw(SQLException, RuntimeException)
+void SAL_CALL OPreparedStatement::setBytes(sal_Int32 nParameterIndex,
+                                           const Sequence< sal_Int8 >& xBytes)
+    throw (SQLException, RuntimeException)
 {
-    (void) parameterIndex;
-    (void) x;
-    ::osl::MutexGuard aGuard( m_aMutex );
+    ::osl::MutexGuard aGuard(m_aMutex);
     checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
 
+    isc_blob_handle aBlobHandle = 0;
+    ISC_QUAD aBlobId;
+
+    openBlobForWriting(aBlobHandle, aBlobId);
+
+    // Max segment size is 2^16 == SAL_MAX_UINT16
+    sal_uInt64 nDataWritten = 0;
+    ISC_STATUS aErr;
+    while (xBytes.getLength() - nDataWritten > 0)
+    {
+        sal_uInt64 nDataRemaining = xBytes.getLength() - nDataWritten;
+        sal_uInt16 nWriteSize = (nDataRemaining > SAL_MAX_UINT16) ? SAL_MAX_UINT16 : nDataRemaining;
+        aErr = isc_put_segment(m_statusVector,
+                               &aBlobHandle,
+                               nWriteSize,
+                               (const char*) xBytes.getConstArray() + nDataWritten);
+        nDataWritten += nWriteSize;
+
+        if (aErr)
+            break;
+    }
+
+    // We need to make sure we close the Blob even if their are errors, hence evaluate
+    // errors after closing.
+    closeBlobAfterWriting(aBlobHandle);
+
+    if (aErr)
+    {
+        evaluateStatusVector(m_statusVector,
+                             "isc_put_segment failed",
+                             *this);
+        assert(false);
+    }
+
+    setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
 }
 // -------------------------------------------------------------------------
 
diff --git a/connectivity/source/drivers/firebird/PreparedStatement.hxx b/connectivity/source/drivers/firebird/PreparedStatement.hxx
index d052b7e..ea1bb05 100644
--- a/connectivity/source/drivers/firebird/PreparedStatement.hxx
+++ b/connectivity/source/drivers/firebird/PreparedStatement.hxx
@@ -87,6 +87,14 @@ namespace connectivity
             void ensurePrepared()
                 throw(::com::sun::star::sdbc::SQLException,
                       ::com::sun::star::uno::RuntimeException);
+            /**
+             * Assumes that all necessary mutexes have been taken.
+             */
+            void openBlobForWriting(isc_blob_handle& rBlobHandle, ISC_QUAD& rBlobId);
+            /**
+             * Assumes that all necessary mutexes have been taken.
+             */
+            void closeBlobAfterWriting(isc_blob_handle& rBlobHandle);
 
         protected:
             virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,


More information about the Libreoffice-commits mailing list