Bugzilla – Attachment 154151 Details for
Bug 116936
Firebird SDBC should implement XRowUpdate interface
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
First patch draft
tdf116936.diff (text/plain), 20.64 KB, created by
Julien Nabet
on 2019-09-13 08:54:21 UTC
(
hide
)
Description:
First patch draft
Filename:
MIME Type:
Creator:
Julien Nabet
Created:
2019-09-13 08:54:21 UTC
Size:
20.64 KB
patch
obsolete
>diff --git a/connectivity/source/drivers/firebird/ResultSet.cxx b/connectivity/source/drivers/firebird/ResultSet.cxx >index dd0e56afb9af..c9084e55acaf 100644 >--- a/connectivity/source/drivers/firebird/ResultSet.cxx >+++ b/connectivity/source/drivers/firebird/ResultSet.cxx >@@ -786,6 +786,549 @@ Any SAL_CALL OResultSet::getObject( sal_Int32, const uno::Reference< css::contai > return Any(); > } > >+namespace { >+ >+/** >+ * Take out the number part of a fix point decimal without >+ * the information of where is the fractional part from a >+ * string representation of a number. (e.g. 54.654 -> 54654) >+ */ >+sal_Int64 toNumericWithoutDecimalPlace(const OUString& sSource) >+{ >+ OUString sNumber(sSource); >+ >+ // cut off leading 0 eventually ( eg. 0.567 -> .567) >+ (void)sSource.startsWith("0", &sNumber); >+ >+ sal_Int32 nDotIndex = sNumber.indexOf('.'); >+ >+ if( nDotIndex < 0) >+ { >+ return sNumber.toInt64(); // no dot -> it's an integer >+ } >+ else >+ { >+ // remove dot >+ OUStringBuffer sBuffer(15); >+ if(nDotIndex > 0) >+ { >+ sBuffer.append(std::u16string_view(sNumber).substr(0, nDotIndex)); >+ } >+ sBuffer.append(std::u16string_view(sNumber).substr(nDotIndex + 1)); >+ return sBuffer.makeStringAndClear().toInt64(); >+ } >+} >+ >+} >+// void OPreaparedStatement::set >+void OResultSet::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 >+ nullptr); // Blob parameter buffer handle >+ >+ if (aErr) >+ { >+ evaluateStatusVector(m_statusVector, >+ "setBlob failed", >+ *this); >+ assert(false); >+ } >+} >+ >+void OResultSet::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); >+ } >+} >+ >+// XRowUpdate >+void OResultSet::updateParameterNull(sal_Int32 nParameterIndex, >+ bool bSetNull) >+{ >+ XSQLVAR* pVar = m_pSqlda->sqlvar + (nParameterIndex - 1); >+ if (bSetNull) >+ { >+ pVar->sqltype |= 1; >+ *pVar->sqlind = -1; >+ } >+ else >+ *pVar->sqlind = 0; >+} >+ >+ >+void OResultSet::updateClob( sal_Int32 column, const OUString& rStr ) >+{ >+ MutexGuard aGuard( m_rMutex ); >+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed); >+ >+ checkColumnIndex(column); >+ checkRowIndex(); >+ >+#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 there are errors, hence evaluate >+ // errors after closing. >+ closeBlobAfterWriting(aBlobHandle); >+ >+ if (aErr) >+ { >+ evaluateStatusVector(m_statusVector, >+ "isc_put_segment failed", >+ *this); >+ assert(false); >+ } >+ >+ updateValue< ISC_QUAD >(column, aBlobId, SQL_BLOB); >+} >+ >+template <typename T> >+void OResultSet::updateValue(sal_Int32 column, const T& nValue, ISC_SHORT nType) >+{ >+ MutexGuard aGuard( m_rMutex ); >+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed); >+ >+ checkColumnIndex(column); >+ checkRowIndex(); >+ >+ updateParameterNull(column, false); >+ XSQLVAR* pVar = m_pSqlda->sqlvar + (column - 1); >+ >+ if ((pVar->sqltype & ~1) != nType) >+ { >+ ::dbtools::throwSQLException( >+ "Incorrect type for setValue", >+ ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE, >+ *this); >+ } >+ >+ memcpy(pVar->sqldata, &nValue, sizeof(nValue)); >+} >+ >+void SAL_CALL OResultSet::updateNull(sal_Int32 column) >+{ >+ MutexGuard aGuard( m_rMutex ); >+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed); >+ >+ checkColumnIndex(column); >+ checkRowIndex(); >+ updateParameterNull(column, true); >+} >+ >+void SAL_CALL OResultSet::updateBoolean(sal_Int32 column, sal_Bool x) >+{ >+ updateValue< sal_Bool >(column, x, SQL_BOOLEAN); >+} >+ >+void SAL_CALL OResultSet::updateByte(sal_Int32 column, sal_Int8 x) >+{ >+ // there's no TINYINT or equivalent on Firebird, >+ // so do the same as updateShort >+ updateValue< sal_Int16 >(column, x, SQL_SHORT); >+} >+ >+void SAL_CALL OResultSet::updateShort(sal_Int32 column, sal_Int16 x) >+{ >+ updateValue< sal_Int16 >(column, x, SQL_SHORT); >+} >+ >+void SAL_CALL OResultSet::updateInt(sal_Int32 column, sal_Int32 x) >+{ >+ updateValue< sal_Int32 >(column, x, SQL_LONG); >+} >+ >+void SAL_CALL OResultSet::updateLong(sal_Int32 column, sal_Int64 x) >+{ >+ updateValue< sal_Int64 >(column, x, SQL_INT64); >+} >+ >+void SAL_CALL OResultSet::updateFloat(sal_Int32 column, float x) >+{ >+ updateValue< float >(column, x, SQL_FLOAT); >+} >+ >+void SAL_CALL OResultSet::updateDouble(sal_Int32 column, double x) >+{ >+ MutexGuard aGuard( m_rMutex ); >+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed); >+ >+ checkColumnIndex(column); >+ checkRowIndex(); >+ >+ XSQLVAR* pVar = m_pSqlda->sqlvar + (column - 1); >+ short dType = (pVar->sqltype & ~1); // drop flag bit for now >+ short dSubType = pVar->sqlsubtype; >+ // Assume it is a sub type of a number. >+ if(dSubType < 0 || dSubType > 2) >+ { >+ ::dbtools::throwSQLException( >+ "Incorrect number sub type", >+ ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE, >+ *this); >+ } >+ // firebird stores scale as a negative number >+ ColumnTypeInfo columnType{ dType, dSubType, >+ static_cast<short>(-pVar->sqlscale) }; >+ >+ // Caller might try to set an integer type here. It makes sense to convert >+ // it instead of throwing an error. >+ switch(columnType.getSdbcType()) >+ { >+ case DataType::SMALLINT: >+ updateValue< sal_Int16 >(column, >+ static_cast<sal_Int16>(x), >+ dType); >+ break; >+ case DataType::INTEGER: >+ updateValue< sal_Int32 >(column, >+ static_cast<sal_Int32>(x), >+ dType); >+ break; >+ case DataType::BIGINT: >+ updateValue< sal_Int64 >(column, >+ static_cast<sal_Int64>(x), >+ dType); >+ break; >+ case DataType::NUMERIC: >+ case DataType::DECIMAL: >+ // take decimal places into account, later on they are removed in makeNumericString >+ updateNumericObject(column, css::uno::Any{x}, columnType.getScale()); >+ break; >+ default: >+ updateValue< double >(column, x, SQL_DOUBLE); // TODO: SQL_D_FLOAT? >+ } >+} >+ >+void SAL_CALL OResultSet::updateString(sal_Int32 column, const OUString& x) >+{ >+ MutexGuard aGuard( m_rMutex ); >+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed); >+ >+ checkColumnIndex(column); >+ checkRowIndex(); >+ updateParameterNull(column, false); >+ >+ OString str = OUStringToOString(x , RTL_TEXTENCODING_UTF8 ); >+ >+ XSQLVAR* pVar = m_pSqlda->sqlvar + (column - 1); >+ >+ int dtype = (pVar->sqltype & ~1); // drop flag bit for now >+ >+ if (str.getLength() > pVar->sqllen) >+ str = str.copy(0, pVar->sqllen); >+ >+ switch (dtype) { >+ case SQL_VARYING: >+ { >+ const sal_Int32 max_varchar_len = 0xFFFF; >+ // First 2 bytes indicate string size >+ if (str.getLength() > max_varchar_len) >+ { >+ str = str.copy(0, max_varchar_len); >+ } >+ const auto nLength = str.getLength(); >+ memcpy(pVar->sqldata, &nLength, 2); >+ // Actual data >+ memcpy(pVar->sqldata + 2, str.getStr(), str.getLength()); >+ break; >+ } >+ case SQL_TEXT: >+ memcpy(pVar->sqldata, str.getStr(), str.getLength()); >+ // Fill remainder with spaces >+ memset(pVar->sqldata + str.getLength(), ' ', pVar->sqllen - str.getLength()); >+ break; >+ case SQL_BLOB: // Clob >+ assert( pVar->sqlsubtype == static_cast<short>(BlobSubtype::Clob) ); >+ updateClob(column, x ); >+ break; >+ default: >+ ::dbtools::throwSQLException( >+ "Incorrect type for setString", >+ ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE, >+ *this); >+ } >+} >+ >+void SAL_CALL OResultSet::updateBytes(sal_Int32 column, const css::uno::Sequence<sal_Int8>& x) >+{ >+ MutexGuard aGuard( m_rMutex ); >+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed); >+ >+ checkColumnIndex(column); >+ checkRowIndex(); >+ >+ XSQLVAR* pVar = m_pSqlda->sqlvar + (column - 1); >+ int dType = (pVar->sqltype & ~1); // drop flag bit for now >+ >+ if( dType == SQL_BLOB ) >+ { >+#if SAL_TYPES_SIZEOFPOINTER == 8 >+ isc_blob_handle aBlobHandle = 0; >+#else >+ isc_blob_handle aBlobHandle = nullptr; >+#endif >+ ISC_QUAD aBlobId; >+ >+ openBlobForWriting(aBlobHandle, aBlobId); >+ >+ ISC_STATUS aErr = 0; >+ const sal_Int32 nBytesLen = x.getLength(); >+ if (nBytesLen > 0) >+ { >+ // Max write size is 0xFFFF == SAL_MAX_UINT16 >+ sal_uInt32 nDataWritten = 0; >+ while (sal::static_int_cast<sal_uInt32>(nBytesLen) > nDataWritten) >+ { >+ sal_uInt32 nDataRemaining = nBytesLen - nDataWritten; >+ sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt32(SAL_MAX_UINT16)); >+ aErr = isc_put_segment(m_statusVector, >+ &aBlobHandle, >+ nWriteSize, >+ reinterpret_cast<const char*>(x.getConstArray()) + nDataWritten); >+ nDataWritten += nWriteSize; >+ >+ if (aErr) >+ break; >+ } >+ } >+ >+ // We need to make sure we close the Blob even if there are errors, hence evaluate >+ // errors after closing. >+ closeBlobAfterWriting(aBlobHandle); >+ >+ if (aErr) >+ { >+ evaluateStatusVector(m_statusVector, >+ "isc_put_segment failed", >+ *this); >+ assert(false); >+ } >+ >+ updateValue< ISC_QUAD >(column, aBlobId, SQL_BLOB); >+ } >+ else if( dType == SQL_VARYING ) >+ { >+ updateParameterNull(column, false); >+ const sal_Int32 nMaxSize = 0xFFFF; >+ Sequence<sal_Int8> xCopy(x); >+ if (xCopy.getLength() > nMaxSize) >+ { >+ xCopy.realloc( nMaxSize ); >+ } >+ const auto nSize = xCopy.getLength(); >+ // 8000 corresponds to value from lcl_addDefaultParameters >+ // in dbaccess/source/filter/hsqldb/createparser.cxx >+ if (nSize > 8000) >+ { >+ free(pVar->sqldata); >+ pVar->sqldata = static_cast<char *>(malloc(sizeof(char) * nSize + 2)); >+ } >+ // First 2 bytes indicate string size >+ memcpy(pVar->sqldata, &nSize, 2); >+ // Actual data >+ memcpy(pVar->sqldata + 2, xCopy.getConstArray(), nSize); >+ } >+ else if( dType == SQL_TEXT ) >+ { >+ updateParameterNull(column, false); >+ memcpy(pVar->sqldata, x.getConstArray(), x.getLength() ); >+ // Fill remainder with spaces >+ memset(pVar->sqldata + x.getLength(), 0, pVar->sqllen - x.getLength()); >+ } >+ else >+ { >+ ::dbtools::throwSQLException( >+ "Incorrect type for setBytes", >+ ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE, >+ *this); >+ } >+} >+ >+void SAL_CALL OResultSet::updateDate(sal_Int32 column, const css::util::Date& x) >+{ >+ struct tm aCTime; >+ aCTime.tm_mday = x.Day; >+ aCTime.tm_mon = x.Month -1; >+ aCTime.tm_year = x.Year -1900; >+ >+ ISC_DATE aISCDate; >+ isc_encode_sql_date(&aCTime, &aISCDate); >+ >+ updateValue< ISC_DATE >(column, aISCDate, SQL_TYPE_DATE); >+} >+ >+void SAL_CALL OResultSet::updateTime(sal_Int32 column, const css::util::Time& x) >+{ >+ struct tm aCTime; >+ aCTime.tm_sec = x.Seconds; >+ aCTime.tm_min = x.Minutes; >+ aCTime.tm_hour = x.Hours; >+ >+ ISC_TIME aISCTime; >+ isc_encode_sql_time(&aCTime, &aISCTime); >+ >+ // Here we "know" that ISC_TIME is simply in units of seconds/ISC_TIME_SECONDS_PRECISION with no >+ // other funkiness, so we can simply add the fraction of a second. >+ aISCTime += x.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION); >+ >+ updateValue< ISC_TIME >(column, aISCTime, SQL_TYPE_TIME); >+} >+ >+void SAL_CALL OResultSet::updateTimestamp(sal_Int32 column, const css::util::DateTime& x) >+{ >+ struct tm aCTime; >+ aCTime.tm_sec = x.Seconds; >+ aCTime.tm_min = x.Minutes; >+ aCTime.tm_hour = x.Hours; >+ aCTime.tm_mday = x.Day; >+ aCTime.tm_mon = x.Month - 1; >+ aCTime.tm_year = x.Year - 1900; >+ >+ ISC_TIMESTAMP aISCTimestamp; >+ isc_encode_timestamp(&aCTime, &aISCTimestamp); >+ >+ // As in previous function >+ aISCTimestamp.timestamp_time += x.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION); >+ >+ updateValue< ISC_TIMESTAMP >(column, aISCTimestamp, SQL_TIMESTAMP); >+} >+ >+void SAL_CALL OResultSet::updateBinaryStream(sal_Int32 column, >+ const css::uno::Reference<css::io::XInputStream>&, >+ sal_Int32) >+{ >+// TODO ? >+ MutexGuard aGuard( m_rMutex ); >+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed); >+ >+ checkColumnIndex(column); >+ checkRowIndex(); >+} >+ >+void SAL_CALL OResultSet::updateCharacterStream(sal_Int32 column, >+ const css::uno::Reference<css::io::XInputStream>&, >+ sal_Int32) >+{ >+// TODO ? >+ MutexGuard aGuard( m_rMutex ); >+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed); >+ >+ checkColumnIndex(column); >+ checkRowIndex(); >+} >+ >+void SAL_CALL OResultSet::updateObject(sal_Int32 column, const css::uno::Any&) >+{ >+// TODO ? >+ MutexGuard aGuard( m_rMutex ); >+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed); >+ >+ checkColumnIndex(column); >+ checkRowIndex(); >+} >+ >+void SAL_CALL OResultSet::updateNumericObject(sal_Int32 column, const css::uno::Any& x, sal_Int32 scale) >+{ >+// TODO ? >+ MutexGuard aGuard( m_rMutex ); >+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed); >+ >+ checkColumnIndex(column); >+ checkRowIndex(); >+ >+ updateParameterNull(column, false); >+ >+ XSQLVAR* pVar = m_pSqlda->sqlvar + (column - 1); >+ int dType = (pVar->sqltype & ~1); // drop null flag >+ >+ double dbValue =0.0; >+ OUString sValue; >+ if( x >>= dbValue ) >+ { >+ // truncate and round to 'scale' number of decimal places >+ sValue = OUString::number( std::floor((dbValue * pow10Integer(scale)) + .5) / pow10Integer(scale) ); >+ } >+ else >+ { >+ x >>= sValue; >+ } >+ >+ // fill in the number with nulls in fractional part. >+ // We need this because e.g. 0.450 != 0.045 despite >+ // their scale is equal >+ OUStringBuffer sBuffer(15); >+ sBuffer.append(sValue); >+ if(sValue.indexOf('.') != -1) // there is a dot >+ { >+ for(sal_Int32 i=sValue.copy(sValue.indexOf('.')+1).getLength(); i<scale;i++) >+ { >+ sBuffer.append('0'); >+ } >+ } >+ else >+ { >+ for (sal_Int32 i=0; i<scale; i++) >+ { >+ sBuffer.append('0'); >+ } >+ } >+ >+ sValue = sBuffer.makeStringAndClear(); >+ switch(dType) >+ { >+ case SQL_SHORT: >+ updateValue< sal_Int16 >(column, >+ static_cast<sal_Int16>( toNumericWithoutDecimalPlace(sValue) ), >+ dType); >+ break; >+ case SQL_LONG: >+ case SQL_DOUBLE: >+ updateValue< sal_Int32 >(column, >+ static_cast<sal_Int32>( toNumericWithoutDecimalPlace(sValue) ), >+ dType); >+ break; >+ case SQL_INT64: >+ updateValue< sal_Int64 >(column, >+ toNumericWithoutDecimalPlace(sValue), >+ dType); >+ break; >+ default: >+ SAL_WARN("connectivity.firebird", >+ "No Firebird sql type found for numeric or decimal types"); >+ assert(false); >+ } >+} >+ > > void SAL_CALL OResultSet::close() > { >diff --git a/connectivity/source/drivers/firebird/ResultSet.hxx b/connectivity/source/drivers/firebird/ResultSet.hxx >index 000f749af9f8..4b9a6a502ccc 100644 >--- a/connectivity/source/drivers/firebird/ResultSet.hxx >+++ b/connectivity/source/drivers/firebird/ResultSet.hxx >@@ -38,6 +38,7 @@ > #include <com/sun/star/sdbc/XResultSet.hpp> > #include <com/sun/star/sdbc/XRow.hpp> > #include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> >+#include <com/sun/star/sdbc/XRowUpdate.hpp> > > namespace connectivity > { >@@ -48,6 +49,7 @@ namespace connectivity > */ > typedef ::cppu::WeakComponentImplHelper< css::sdbc::XResultSet, > css::sdbc::XRow, >+ css::sdbc::XRowUpdate, > css::sdbc::XResultSetMetaDataSupplier, > css::util::XCancellable, > css::sdbc::XCloseable, >@@ -174,6 +176,35 @@ namespace connectivity > virtual css::uno::Reference< css::sdbc::XBlob > SAL_CALL getBlob( sal_Int32 columnIndex ) override; > virtual css::uno::Reference< css::sdbc::XClob > SAL_CALL getClob( sal_Int32 columnIndex ) override; > virtual css::uno::Reference< css::sdbc::XArray > SAL_CALL getArray( sal_Int32 columnIndex ) override; >+ >+ // XRowUpdate >+ void openBlobForWriting(isc_blob_handle& rBlobHandle, ISC_QUAD& rBlobId); >+ void closeBlobAfterWriting(isc_blob_handle& rBlobHandle); >+ void updateParameterNull(sal_Int32 nParameterIndex, bool bSetNull); >+ void updateClob( sal_Int32 column, const OUString& rStr ); >+ template <typename T> void updateValue(sal_Int32 nIndex, const T& nValue, ISC_SHORT nType); >+ virtual void SAL_CALL updateNull(sal_Int32 column) override; >+ virtual void SAL_CALL updateBoolean(sal_Int32 column, sal_Bool x) override; >+ virtual void SAL_CALL updateByte(sal_Int32 column, sal_Int8 x) override; >+ virtual void SAL_CALL updateShort(sal_Int32 column, sal_Int16 x) override; >+ virtual void SAL_CALL updateInt(sal_Int32 column, sal_Int32 x) override; >+ virtual void SAL_CALL updateLong(sal_Int32 column, sal_Int64 x) override; >+ virtual void SAL_CALL updateFloat(sal_Int32 column, float x) override; >+ virtual void SAL_CALL updateDouble(sal_Int32 column, double x) override; >+ virtual void SAL_CALL updateString(sal_Int32 column, const OUString& x) override; >+ virtual void SAL_CALL updateBytes(sal_Int32 column, const css::uno::Sequence<sal_Int8>& x) override; >+ virtual void SAL_CALL updateDate(sal_Int32 column, const css::util::Date& x) override; >+ virtual void SAL_CALL updateTime(sal_Int32 column, const css::util::Time& x) override; >+ virtual void SAL_CALL updateTimestamp(sal_Int32 column, const css::util::DateTime& x) override; >+ virtual void SAL_CALL updateBinaryStream(sal_Int32 column, >+ const css::uno::Reference<css::io::XInputStream>& x, >+ sal_Int32 length) override; >+ virtual void SAL_CALL updateCharacterStream(sal_Int32 column, >+ const css::uno::Reference<css::io::XInputStream>& x, >+ sal_Int32 length) override; >+ virtual void SAL_CALL updateObject(sal_Int32 column, const css::uno::Any& x) override; >+ virtual void SAL_CALL updateNumericObject(sal_Int32 column, const css::uno::Any& x, sal_Int32 scale) override; >+ > // XResultSetMetaDataSupplier > virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override; > // XCancellable
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 116936
: 154151