tdf#104734 Firebird improve XClob implementation
Create a more effective implementation of XClob::length() and XClob::getSubString() methods, where string is read segment-by-segment instead of reading the whole underlying blob. That way it is possible to handle big texts which would not fit into memory. Also allow reading Clob data from a resultset with getString() and writing it in a prepared statement with setString(). Implement XPreparedStatement::setClob(). Also implement a private version of setClob() for creating a clob from OUString: Allow the creation of a clob column with GUI by adding a new type in ODataBaseMetaData::getTypeInfo(). Change-Id: Ibcbbdd80e8eed5e2a3fe55b0fa196401f1bcbcdf Reviewed-on: https://gerrit.libreoffice.org/47093 Reviewed-by: Tamás Bunth <btomi96@gmail.com> Tested-by: Tamás Bunth <btomi96@gmail.com>
This commit is contained in:
@@ -70,9 +70,14 @@ void Blob::ensureBlobIsOpened()
|
|||||||
m_nBlobPosition = 0;
|
m_nBlobPosition = 0;
|
||||||
|
|
||||||
char aBlobItems[] = {
|
char aBlobItems[] = {
|
||||||
isc_info_blob_total_length
|
isc_info_blob_total_length,
|
||||||
|
isc_info_blob_max_segment
|
||||||
};
|
};
|
||||||
char aResultBuffer[20];
|
|
||||||
|
// Assuming a data (e.g. legth of blob) is maximum 64 bit.
|
||||||
|
// That means we need 8 bytes for data + 2 for length of data + 1 for item
|
||||||
|
// identifier for each item.
|
||||||
|
char aResultBuffer[11 + 11];
|
||||||
|
|
||||||
aErr = isc_blob_info(m_statusVector,
|
aErr = isc_blob_info(m_statusVector,
|
||||||
&m_blobHandle,
|
&m_blobHandle,
|
||||||
@@ -84,17 +89,63 @@ void Blob::ensureBlobIsOpened()
|
|||||||
if (aErr)
|
if (aErr)
|
||||||
evaluateStatusVector(m_statusVector, "isc_blob_info", *this);
|
evaluateStatusVector(m_statusVector, "isc_blob_info", *this);
|
||||||
|
|
||||||
if (*aResultBuffer == isc_info_blob_total_length)
|
char* pIt = aResultBuffer;
|
||||||
|
while( *pIt != isc_info_end ) // info is in clusters
|
||||||
{
|
{
|
||||||
short aResultLength = (short) isc_vax_integer(aResultBuffer+1, 2);
|
char item = *pIt++;
|
||||||
m_nBlobLength = isc_vax_integer(aResultBuffer+3, aResultLength);
|
short aResultLength = (short) isc_vax_integer(pIt, 2);
|
||||||
}
|
|
||||||
else
|
pIt += 2;
|
||||||
{
|
switch(item)
|
||||||
assert(false);
|
{
|
||||||
|
case isc_info_blob_total_length:
|
||||||
|
m_nBlobLength = isc_vax_integer(pIt, aResultLength);
|
||||||
|
break;
|
||||||
|
case isc_info_blob_max_segment:
|
||||||
|
m_nMaxSegmentSize = isc_vax_integer(pIt, aResultLength);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pIt += aResultLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sal_uInt16 Blob::getMaximumSegmentSize()
|
||||||
|
{
|
||||||
|
ensureBlobIsOpened();
|
||||||
|
|
||||||
|
return m_nMaxSegmentSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Blob::readOneSegment(uno::Sequence< sal_Int8 >& rDataOut)
|
||||||
|
{
|
||||||
|
checkDisposed(Blob_BASE::rBHelper.bDisposed);
|
||||||
|
ensureBlobIsOpened();
|
||||||
|
|
||||||
|
sal_uInt16 nMaxSize = getMaximumSegmentSize();
|
||||||
|
|
||||||
|
if(rDataOut.getLength() < nMaxSize)
|
||||||
|
rDataOut.realloc(nMaxSize);
|
||||||
|
|
||||||
|
sal_uInt16 nActualSize = 0;
|
||||||
|
ISC_STATUS aRet = isc_get_segment(m_statusVector,
|
||||||
|
&m_blobHandle,
|
||||||
|
&nActualSize,
|
||||||
|
nMaxSize,
|
||||||
|
reinterpret_cast<char*>(rDataOut.getArray()) );
|
||||||
|
|
||||||
|
if (aRet && aRet != isc_segstr_eof && IndicatesError(m_statusVector))
|
||||||
|
{
|
||||||
|
OUString sError(StatusVectorToString(m_statusVector, "isc_get_segment"));
|
||||||
|
throw IOException(sError, *this);
|
||||||
|
}
|
||||||
|
m_nBlobPosition += nActualSize;
|
||||||
|
return aRet == isc_segstr_eof; // last segment read
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Blob::closeBlob()
|
void Blob::closeBlob()
|
||||||
{
|
{
|
||||||
MutexGuard aGuard(m_aMutex);
|
MutexGuard aGuard(m_aMutex);
|
||||||
|
@@ -41,6 +41,7 @@ namespace connectivity
|
|||||||
|
|
||||||
bool m_bBlobOpened;
|
bool m_bBlobOpened;
|
||||||
sal_Int64 m_nBlobLength;
|
sal_Int64 m_nBlobLength;
|
||||||
|
sal_uInt16 m_nMaxSegmentSize;
|
||||||
sal_Int64 m_nBlobPosition;
|
sal_Int64 m_nBlobPosition;
|
||||||
|
|
||||||
ISC_STATUS_ARRAY m_statusVector;
|
ISC_STATUS_ARRAY m_statusVector;
|
||||||
@@ -54,12 +55,15 @@ namespace connectivity
|
|||||||
* @throws css::sdbc::SQLException
|
* @throws css::sdbc::SQLException
|
||||||
*/
|
*/
|
||||||
void closeBlob();
|
void closeBlob();
|
||||||
|
sal_uInt16 getMaximumSegmentSize();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Blob(isc_db_handle* pDatabaseHandle,
|
Blob(isc_db_handle* pDatabaseHandle,
|
||||||
isc_tr_handle* pTransactionHandle,
|
isc_tr_handle* pTransactionHandle,
|
||||||
ISC_QUAD const & aBlobID);
|
ISC_QUAD const & aBlobID);
|
||||||
|
|
||||||
|
bool readOneSegment(css::uno::Sequence< sal_Int8 >& rDataOut);
|
||||||
|
|
||||||
// ---- XBlob ----------------------------------------------------
|
// ---- XBlob ----------------------------------------------------
|
||||||
virtual sal_Int64 SAL_CALL
|
virtual sal_Int64 SAL_CALL
|
||||||
length() override;
|
length() override;
|
||||||
|
@@ -28,7 +28,8 @@ Clob::Clob(isc_db_handle* pDatabaseHandle,
|
|||||||
isc_tr_handle* pTransactionHandle,
|
isc_tr_handle* pTransactionHandle,
|
||||||
ISC_QUAD const & aBlobID):
|
ISC_QUAD const & aBlobID):
|
||||||
Clob_BASE(m_aMutex),
|
Clob_BASE(m_aMutex),
|
||||||
m_aBlob(new connectivity::firebird::Blob(pDatabaseHandle, pTransactionHandle, aBlobID))
|
m_aBlob(new connectivity::firebird::Blob(pDatabaseHandle, pTransactionHandle, aBlobID)),
|
||||||
|
m_nCharCount(-1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,13 +45,27 @@ sal_Int64 SAL_CALL Clob::length()
|
|||||||
MutexGuard aGuard(m_aMutex);
|
MutexGuard aGuard(m_aMutex);
|
||||||
checkDisposed(Clob_BASE::rBHelper.bDisposed);
|
checkDisposed(Clob_BASE::rBHelper.bDisposed);
|
||||||
|
|
||||||
// read the entire blob
|
if( m_nCharCount >= 0 )
|
||||||
// TODO FIXME better solution?
|
return m_nCharCount;
|
||||||
uno::Sequence < sal_Int8 > aEntireBlob = m_aBlob->getBytes( 1, m_aBlob->length());
|
m_nCharCount = 0;
|
||||||
OUString sEntireClob ( reinterpret_cast< sal_Char *>( aEntireBlob.getArray() ),
|
|
||||||
aEntireBlob.getLength(),
|
// Read each segment, and calculate it's size by interpreting it as a
|
||||||
|
// character stream. Assume that no characters are split by the segments.
|
||||||
|
bool bLastSegmRead = false;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
uno::Sequence < sal_Int8 > aSegmentBytes;
|
||||||
|
bLastSegmRead = m_aBlob->readOneSegment( aSegmentBytes );
|
||||||
|
OUString sSegment ( reinterpret_cast< sal_Char *>( aSegmentBytes.getArray() ),
|
||||||
|
aSegmentBytes.getLength(),
|
||||||
RTL_TEXTENCODING_UTF8 );
|
RTL_TEXTENCODING_UTF8 );
|
||||||
return sEntireClob.getLength();
|
|
||||||
|
if( !bLastSegmRead)
|
||||||
|
m_nCharCount += sSegment.getLength();
|
||||||
|
}while( !bLastSegmRead );
|
||||||
|
|
||||||
|
m_aBlob->closeInput(); // reset position
|
||||||
|
return m_nCharCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
OUString SAL_CALL Clob::getSubString(sal_Int64 nPosition,
|
OUString SAL_CALL Clob::getSubString(sal_Int64 nPosition,
|
||||||
@@ -58,19 +73,58 @@ OUString SAL_CALL Clob::getSubString(sal_Int64 nPosition,
|
|||||||
{
|
{
|
||||||
MutexGuard aGuard(m_aMutex);
|
MutexGuard aGuard(m_aMutex);
|
||||||
checkDisposed(Clob_BASE::rBHelper.bDisposed);
|
checkDisposed(Clob_BASE::rBHelper.bDisposed);
|
||||||
|
// TODO do not reset position if it is not necessary
|
||||||
|
m_aBlob->closeInput(); // reset position
|
||||||
|
|
||||||
// read the entire blob
|
OUStringBuffer sSegmentBuffer;
|
||||||
// TODO FIXME better solution?
|
sal_Int64 nActPos = 1;
|
||||||
// TODO FIXME Assume indexing of nPosition starts at position 1.
|
sal_Int32 nActLen = 0;
|
||||||
uno::Sequence < sal_Int8 > aEntireBlob = m_aBlob->getBytes( 1, m_aBlob->length());
|
|
||||||
OUString sEntireClob ( reinterpret_cast< sal_Char *>( aEntireBlob.getArray() ),
|
// skip irrelevant parts
|
||||||
aEntireBlob.getLength(),
|
while( nActPos < nPosition )
|
||||||
|
{
|
||||||
|
uno::Sequence < sal_Int8 > aSegmentBytes;
|
||||||
|
bool bLastRead = m_aBlob->readOneSegment( aSegmentBytes );
|
||||||
|
if( bLastRead )
|
||||||
|
throw lang::IllegalArgumentException("nPosition out of range", *this, 0);
|
||||||
|
|
||||||
|
OUString sSegment ( reinterpret_cast< sal_Char *>( aSegmentBytes.getArray() ),
|
||||||
|
aSegmentBytes.getLength(),
|
||||||
RTL_TEXTENCODING_UTF8 );
|
RTL_TEXTENCODING_UTF8 );
|
||||||
|
sal_Int32 nStrLen = sSegment.getLength();
|
||||||
|
nActPos += nStrLen;
|
||||||
|
if( nActPos > nPosition )
|
||||||
|
{
|
||||||
|
sal_Int32 nCharsToCopy = static_cast<sal_Int32>(nActPos - nPosition);
|
||||||
|
if( nCharsToCopy > nLength )
|
||||||
|
nCharsToCopy = nLength;
|
||||||
|
// append relevant part of first segment
|
||||||
|
sSegmentBuffer.append( sSegment.copy(0, nCharsToCopy ) );
|
||||||
|
nActLen += sSegmentBuffer.getLength();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if( nPosition + nLength - 1 > sEntireClob.getLength() )
|
// read nLength characters
|
||||||
throw lang::IllegalArgumentException("nPosition out of range", *this, 0);
|
while( nActLen < nLength )
|
||||||
|
{
|
||||||
|
uno::Sequence < sal_Int8 > aSegmentBytes;
|
||||||
|
bool bLastRead = m_aBlob->readOneSegment( aSegmentBytes );
|
||||||
|
|
||||||
return sEntireClob.copy(nPosition - 1 , nLength);
|
OUString sSegment ( reinterpret_cast< sal_Char *>( aSegmentBytes.getArray() ),
|
||||||
|
aSegmentBytes.getLength(),
|
||||||
|
RTL_TEXTENCODING_UTF8 );
|
||||||
|
sal_Int32 nStrLen = sSegment.getLength();
|
||||||
|
if( nActLen + nStrLen > nLength )
|
||||||
|
sSegmentBuffer.append(sSegment.copy(0, nLength - nActLen) );
|
||||||
|
else
|
||||||
|
sSegmentBuffer.append(sSegment);
|
||||||
|
nActLen += nStrLen;
|
||||||
|
|
||||||
|
if( bLastRead && nActLen < nLength )
|
||||||
|
throw lang::IllegalArgumentException("out of range", *this, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sSegmentBuffer.makeStringAndClear();
|
||||||
}
|
}
|
||||||
|
|
||||||
uno::Reference< XInputStream > SAL_CALL Clob::getCharacterStream()
|
uno::Reference< XInputStream > SAL_CALL Clob::getCharacterStream()
|
||||||
|
@@ -38,6 +38,8 @@ namespace connectivity
|
|||||||
*/
|
*/
|
||||||
rtl::Reference<connectivity::firebird::Blob> m_aBlob;
|
rtl::Reference<connectivity::firebird::Blob> m_aBlob;
|
||||||
|
|
||||||
|
sal_Int64 m_nCharCount;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Clob(isc_db_handle* pDatabaseHandle,
|
Clob(isc_db_handle* pDatabaseHandle,
|
||||||
isc_tr_handle* pTransactionHandle,
|
isc_tr_handle* pTransactionHandle,
|
||||||
|
@@ -870,6 +870,15 @@ uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTypeInfo()
|
|||||||
aRow[6] = new ORowSetValueDecorator(OUString("length")); // Create Params
|
aRow[6] = new ORowSetValueDecorator(OUString("length")); // Create Params
|
||||||
aRow[9] = new ORowSetValueDecorator(
|
aRow[9] = new ORowSetValueDecorator(
|
||||||
sal_Int16(ColumnSearch::NONE)); // Searchable
|
sal_Int16(ColumnSearch::NONE)); // Searchable
|
||||||
|
|
||||||
|
// Clob (SQL_BLOB)
|
||||||
|
aRow[1] = new ORowSetValueDecorator(OUString("BLOB")); // BLOB, with subtype 1
|
||||||
|
aRow[2] = new ORowSetValueDecorator(DataType::CLOB);
|
||||||
|
aRow[3] = new ORowSetValueDecorator(sal_Int16(2147483647)); // Precision = max length
|
||||||
|
aRow[6] = new ORowSetValueDecorator(); // Create Params
|
||||||
|
aRow[9] = new ORowSetValueDecorator(
|
||||||
|
sal_Int16(ColumnSearch::FULL)); // Searchable
|
||||||
|
aRow[12] = new ORowSetValueDecorator(false); // Autoincrement
|
||||||
aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
|
aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
|
||||||
aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
|
aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
|
||||||
aResults.push_back(aRow);
|
aResults.push_back(aRow);
|
||||||
|
@@ -178,10 +178,10 @@ void SAL_CALL OPreparedStatement::disposing()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SAL_CALL OPreparedStatement::setString(sal_Int32 nParameterIndex,
|
void SAL_CALL OPreparedStatement::setString(sal_Int32 nParameterIndex,
|
||||||
const OUString& x)
|
const OUString& sInput)
|
||||||
{
|
{
|
||||||
SAL_INFO("connectivity.firebird",
|
SAL_INFO("connectivity.firebird",
|
||||||
"setString(" << nParameterIndex << " , " << x << ")");
|
"setString(" << nParameterIndex << " , " << sInput << ")");
|
||||||
|
|
||||||
MutexGuard aGuard( m_aMutex );
|
MutexGuard aGuard( m_aMutex );
|
||||||
checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
|
checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
|
||||||
@@ -190,7 +190,7 @@ void SAL_CALL OPreparedStatement::setString(sal_Int32 nParameterIndex,
|
|||||||
checkParameterIndex(nParameterIndex);
|
checkParameterIndex(nParameterIndex);
|
||||||
setParameterNull(nParameterIndex, false);
|
setParameterNull(nParameterIndex, false);
|
||||||
|
|
||||||
OString str = OUStringToOString(x , RTL_TEXTENCODING_UTF8 );
|
OString str = OUStringToOString(sInput , RTL_TEXTENCODING_UTF8 );
|
||||||
|
|
||||||
XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
|
XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
|
||||||
|
|
||||||
@@ -219,6 +219,10 @@ void SAL_CALL OPreparedStatement::setString(sal_Int32 nParameterIndex,
|
|||||||
// Fill remainder with spaces
|
// Fill remainder with spaces
|
||||||
memset(pVar->sqldata + str.getLength(), ' ', pVar->sqllen - str.getLength());
|
memset(pVar->sqldata + str.getLength(), ' ', pVar->sqllen - str.getLength());
|
||||||
break;
|
break;
|
||||||
|
case SQL_BLOB: // Clob
|
||||||
|
assert( pVar->sqlsubtype == static_cast<short>(BlobSubtype::Clob) );
|
||||||
|
setClob(nParameterIndex, sInput );
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
::dbtools::throwSQLException(
|
::dbtools::throwSQLException(
|
||||||
"Incorrect type for setString",
|
"Incorrect type for setString",
|
||||||
@@ -504,21 +508,105 @@ void OPreparedStatement::closeBlobAfterWriting(isc_blob_handle& rBlobHandle)
|
|||||||
ISC_STATUS aErr;
|
ISC_STATUS aErr;
|
||||||
|
|
||||||
aErr = isc_close_blob(m_statusVector,
|
aErr = isc_close_blob(m_statusVector,
|
||||||
&rBlobHandle);
|
&rBlobHandle);
|
||||||
if (aErr)
|
if (aErr)
|
||||||
{
|
{
|
||||||
evaluateStatusVector(m_statusVector,
|
evaluateStatusVector(m_statusVector,
|
||||||
"isc_close_blob failed",
|
"isc_close_blob failed",
|
||||||
*this);
|
*this);
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SAL_CALL OPreparedStatement::setClob( sal_Int32, const Reference< XClob >& )
|
void SAL_CALL OPreparedStatement::setClob(sal_Int32 nParameterIndex, const Reference< XClob >& xClob )
|
||||||
{
|
{
|
||||||
::osl::MutexGuard aGuard( m_aMutex );
|
::osl::MutexGuard aGuard( m_aMutex );
|
||||||
checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
|
checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
|
||||||
|
|
||||||
|
#if SAL_TYPES_SIZEOFPOINTER == 8
|
||||||
|
isc_blob_handle aBlobHandle = 0;
|
||||||
|
#else
|
||||||
|
isc_blob_handle aBlobHandle = nullptr;
|
||||||
|
#endif
|
||||||
|
ISC_QUAD aBlobId;
|
||||||
|
|
||||||
|
openBlobForWriting(aBlobHandle, aBlobId);
|
||||||
|
|
||||||
|
|
||||||
|
// Max segment size is 2^16 == SAL_MAX_UINT16
|
||||||
|
// SAL_MAX_UINT16 / 4 is surely enough for UTF-8
|
||||||
|
// TODO apply max segment size to character encoding
|
||||||
|
sal_Int64 nCharWritten = 1; // XClob is indexed from 1
|
||||||
|
ISC_STATUS aErr = 0;
|
||||||
|
sal_Int64 nLen = xClob->length();
|
||||||
|
while ( nLen > nCharWritten )
|
||||||
|
{
|
||||||
|
sal_Int64 nCharRemain = nLen - nCharWritten;
|
||||||
|
constexpr sal_uInt16 MAX_SIZE = SAL_MAX_UINT16 / 4;
|
||||||
|
sal_uInt16 nWriteSize = (nCharRemain > MAX_SIZE) ? MAX_SIZE : nCharRemain;
|
||||||
|
OString sData = OUStringToOString(
|
||||||
|
xClob->getSubString(nCharWritten, nWriteSize),
|
||||||
|
RTL_TEXTENCODING_UTF8);
|
||||||
|
aErr = isc_put_segment( m_statusVector,
|
||||||
|
&aBlobHandle,
|
||||||
|
sData.getLength(),
|
||||||
|
sData.getStr() );
|
||||||
|
nCharWritten += 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 OPreparedStatement::setClob( sal_Int32 nParameterIndex, const OUString& rStr )
|
||||||
|
{
|
||||||
|
::osl::MutexGuard aGuard( m_aMutex );
|
||||||
|
checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
|
||||||
|
|
||||||
|
#if SAL_TYPES_SIZEOFPOINTER == 8
|
||||||
|
isc_blob_handle aBlobHandle = 0;
|
||||||
|
#else
|
||||||
|
isc_blob_handle aBlobHandle = nullptr;
|
||||||
|
#endif
|
||||||
|
ISC_QUAD aBlobId;
|
||||||
|
|
||||||
|
openBlobForWriting(aBlobHandle, aBlobId);
|
||||||
|
|
||||||
|
OString sData = OUStringToOString(
|
||||||
|
rStr,
|
||||||
|
RTL_TEXTENCODING_UTF8);
|
||||||
|
ISC_STATUS aErr = isc_put_segment( m_statusVector,
|
||||||
|
&aBlobHandle,
|
||||||
|
sData.getLength(),
|
||||||
|
sData.getStr() );
|
||||||
|
|
||||||
|
// 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::setBlob(sal_Int32 nParameterIndex,
|
void SAL_CALL OPreparedStatement::setBlob(sal_Int32 nParameterIndex,
|
||||||
|
@@ -78,6 +78,7 @@ namespace connectivity
|
|||||||
* Assumes that all necessary mutexes have been taken.
|
* Assumes that all necessary mutexes have been taken.
|
||||||
*/
|
*/
|
||||||
void closeBlobAfterWriting(isc_blob_handle& rBlobHandle);
|
void closeBlobAfterWriting(isc_blob_handle& rBlobHandle);
|
||||||
|
void setClob(sal_Int32 nParamIndex, const OUString& rStr);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,
|
virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,
|
||||||
|
@@ -604,6 +604,11 @@ OUString OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT
|
|||||||
return OUString(); // never reached
|
return OUString(); // never reached
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(aSqlType == SQL_BLOB && aSqlSubType == static_cast<short>(BlobSubtype::Clob) )
|
||||||
|
{
|
||||||
|
uno::Reference<XClob> xClob = getClob(nColumnIndex);
|
||||||
|
return xClob->getSubString( 0, xClob->length() );
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return retrieveValue< ORowSetValue >(nColumnIndex, 0);
|
return retrieveValue< ORowSetValue >(nColumnIndex, 0);
|
||||||
|
@@ -106,6 +106,13 @@ OUString Tables::createStandardColumnPart(const Reference< XPropertySet >& xColP
|
|||||||
aSql.append(" ");
|
aSql.append(" ");
|
||||||
aSql.append("CHARACTER SET OCTETS");
|
aSql.append("CHARACTER SET OCTETS");
|
||||||
}
|
}
|
||||||
|
else if(aType == DataType::CLOB)
|
||||||
|
{
|
||||||
|
// CLOB is a special type of blob in Firebird context.
|
||||||
|
// Subtype number 1 always refers to CLOB
|
||||||
|
aSql.append(" ");
|
||||||
|
aSql.append("SUB_TYPE 1");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( bIsAutoIncrement && !sAutoIncrementValue.isEmpty())
|
if ( bIsAutoIncrement && !sAutoIncrementValue.isEmpty())
|
||||||
|
@@ -118,13 +118,14 @@ sal_Int32 firebird::ColumnTypeInfo::getSdbcType() const
|
|||||||
short aSubType = m_aSubType;
|
short aSubType = m_aSubType;
|
||||||
if( m_nScale > 0 )
|
if( m_nScale > 0 )
|
||||||
{
|
{
|
||||||
// scale makes sense only for decimal and numeric types
|
// numeric / decimal
|
||||||
assert(aType == SQL_SHORT || aType == SQL_LONG || aType == SQL_DOUBLE
|
if(aType == SQL_SHORT || aType == SQL_LONG || aType == SQL_DOUBLE
|
||||||
|| aType == SQL_INT64);
|
|| aType == SQL_INT64)
|
||||||
|
{
|
||||||
// if scale is set without subtype then imply numeric
|
// if scale is set without subtype then imply numeric
|
||||||
if( static_cast<NumberSubType>(aSubType) == NumberSubType::Other )
|
if( static_cast<NumberSubType>(aSubType) == NumberSubType::Other )
|
||||||
aSubType = static_cast<short>(NumberSubType::Numeric);
|
aSubType = static_cast<short>(NumberSubType::Numeric);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (aType)
|
switch (aType)
|
||||||
|
Reference in New Issue
Block a user