diff options
-rw-r--r-- | connectivity/source/drivers/firebird/Connection.cxx | 304 | ||||
-rw-r--r-- | connectivity/source/drivers/firebird/Connection.hxx | 37 | ||||
-rw-r--r-- | external/firebird/ExternalPackage_firebird.mk | 1 |
3 files changed, 228 insertions, 114 deletions
diff --git a/connectivity/source/drivers/firebird/Connection.cxx b/connectivity/source/drivers/firebird/Connection.cxx index ad843c4ae012..121878572933 100644 --- a/connectivity/source/drivers/firebird/Connection.cxx +++ b/connectivity/source/drivers/firebird/Connection.cxx @@ -58,6 +58,13 @@ #include <unotools/localfilehelper.hxx> #include <unotools/ucbstreamhelper.hxx> +#include <rtl/strbuf.hxx> + +#ifdef _WIN32 +// for ADD_SPB_NUMERIC +#pragma warning(disable: 4310) // cast truncates data +#endif + using namespace connectivity::firebird; using namespace connectivity; @@ -79,7 +86,11 @@ using namespace ::com::sun::star::uno; * Location within the .odb that an embedded .fdb will be stored. * Only relevant for embedded dbs. */ -static const OUStringLiteral our_sDBLocation( "firebird.fdb" ); +static const OUStringLiteral our_sFDBLocation( "firebird.fdb" ); +/** + * Older version of LO may store the database in a .fdb file + */ +static const OUStringLiteral our_sFBKLocation( "firebird.fbk" ); Connection::Connection(FirebirdDriver* _pDriver) : Connection_BASE(m_aMutex) @@ -141,6 +152,9 @@ void Connection::construct(const ::rtl::OUString& url, const Sequence< PropertyV m_sConnectionURL = url; bool bIsNewDatabase = false; + // the database may be stored as an + // fdb file in older versions + bool bIsFdbStored = false; OUString aStorageURL; if (url == "sdbc:embedded:firebird") { @@ -174,35 +188,36 @@ void Connection::construct(const ::rtl::OUString& url, const Sequence< PropertyV bIsNewDatabase = !m_xEmbeddedStorage->hasElements(); - m_pExtractedFDBFile.reset(new ::utl::TempFile(nullptr, true)); - m_sFirebirdURL = m_pExtractedFDBFile->GetFileName() + "/firebird.fdb"; + m_pDatabaseFileDir.reset(new ::utl::TempFile(nullptr, true)); + m_pDatabaseFileDir->EnableKillingFile(); + m_sFirebirdURL = m_pDatabaseFileDir->GetFileName() + "/firebird.fdb"; + m_sFBKPath = m_pDatabaseFileDir->GetFileName() + "/firebird.fbk"; SAL_INFO("connectivity.firebird", "Temporary .fdb location: " << m_sFirebirdURL); if (!bIsNewDatabase) { - SAL_INFO("connectivity.firebird", "Extracting .fdb from .odb" ); - if (!m_xEmbeddedStorage->isStreamElement(our_sDBLocation)) + if (m_xEmbeddedStorage->hasByName(our_sFBKLocation) && + m_xEmbeddedStorage->isStreamElement(our_sFBKLocation)) { - ::connectivity::SharedResources aResources; - const OUString sMessage = aResources.getResourceString(STR_ERROR_NEW_VERSION); - ::dbtools::throwGenericSQLException(sMessage ,*this); + SAL_INFO("connectivity.firebird", "Extracting* .fbk from .odb" ); + loadDatabaseFile(our_sFBKLocation, m_sFBKPath); } - - Reference< XStream > xDBStream(m_xEmbeddedStorage->openStreamElement(our_sDBLocation, - ElementModes::READ)); - - uno::Reference< ucb::XSimpleFileAccess2 > xFileAccess( - ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ), - uno::UNO_QUERY ); - if ( !xFileAccess.is() ) + else if(m_xEmbeddedStorage->hasByName(our_sFDBLocation) && + m_xEmbeddedStorage->isStreamElement(our_sFDBLocation)) + { + SAL_INFO("connectivity.firebird", "Found .fdb instead of .fbk"); + bIsFdbStored = true; + loadDatabaseFile(our_sFDBLocation, m_sFirebirdURL); + } + else { ::connectivity::SharedResources aResources; + // TODO FIXME: this does _not_ look like the right error message const OUString sMessage = aResources.getResourceString(STR_ERROR_NEW_VERSION); ::dbtools::throwGenericSQLException(sMessage ,*this); - } - xFileAccess->writeFile(m_sFirebirdURL,xDBStream->getInputStream()); + } } // TODO: Get DB properties from XML @@ -289,6 +304,11 @@ void Connection::construct(const ::rtl::OUString& url, const Sequence< PropertyV } else { + if (m_bIsEmbedded && !bIsFdbStored) // We need to restore the .fbk first + { + runBackupService(isc_action_svc_restore); + } + aErr = isc_attach_database(status, m_sFirebirdURL.getLength(), OUStringToOString(m_sFirebirdURL, RTL_TEXTENCODING_UTF8).getStr(), @@ -303,12 +323,6 @@ void Connection::construct(const ::rtl::OUString& url, const Sequence< PropertyV if (m_bIsEmbedded) // Add DocumentEventListener to save the .fdb as needed { - // TODO: this is only needed when we change icu versions, so ideally - // we somehow keep track of which icu version we have. There might - // be something db internal that we can check, or we might have to store - // it in the .odb. - rebuildIndexes(); - // We need to attach as a document listener in order to be able to store // the temporary db back into the .odb when saving uno::Reference<XDocumentEventBroadcaster> xBroadcaster(m_xParentDocument, UNO_QUERY); @@ -553,6 +567,140 @@ void SAL_CALL Connection::commit() throw(SQLException, RuntimeException, std::ex } } +void Connection::loadDatabaseFile(const OUString& srcLocation, const OUString& tmpLocation) +{ + Reference< XStream > xDBStream(m_xEmbeddedStorage->openStreamElement(srcLocation, + ElementModes::READ)); + + uno::Reference< ucb::XSimpleFileAccess2 > xFileAccess( + ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ), + uno::UNO_QUERY ); + if ( !xFileAccess.is() ) + { + ::connectivity::SharedResources aResources; + // TODO FIXME: this does _not_ look like the right error message + const OUString sMessage = aResources.getResourceString(STR_ERROR_NEW_VERSION); + ::dbtools::throwGenericSQLException(sMessage ,*this); + } + xFileAccess->writeFile(tmpLocation,xDBStream->getInputStream()); +} + +isc_svc_handle Connection::attachServiceManager() +{ + ISC_STATUS_ARRAY aStatusVector; + isc_svc_handle aServiceHandle = 0; + + char aSPBBuffer[256]; + char* pSPB = aSPBBuffer; + *pSPB++ = isc_spb_version; + *pSPB++ = isc_spb_current_version; + *pSPB++ = isc_spb_user_name; + OUString sUserName("SYSDBA"); + char aLength = (char) sUserName.getLength(); + *pSPB++ = aLength; + strncpy(pSPB, + OUStringToOString(sUserName, + RTL_TEXTENCODING_UTF8).getStr(), + aLength); + pSPB += aLength; + // TODO: do we need ", isc_dpb_trusted_auth, 1, 1" -- probably not but ... + if (isc_service_attach(aStatusVector, + 0, // Denotes null-terminated string next + "service_mgr", + &aServiceHandle, + pSPB - aSPBBuffer, + aSPBBuffer)) + { + evaluateStatusVector(aStatusVector, + "isc_service_attach", + *this); + } + + return aServiceHandle; +} + +void Connection::detachServiceManager(isc_svc_handle aServiceHandle) +{ + ISC_STATUS_ARRAY aStatusVector; + if (isc_service_detach(aStatusVector, + &aServiceHandle)) + { + evaluateStatusVector(aStatusVector, + "isc_service_detach", + *this); + } +} + +void Connection::runBackupService(const short nAction) +{ + assert(nAction == isc_action_svc_backup + || nAction == isc_action_svc_restore); + + ISC_STATUS_ARRAY aStatusVector; + + // convert paths to 8-Bit strings + OString sFDBPath = OUStringToOString(m_sFirebirdURL, RTL_TEXTENCODING_UTF8); + OString sFBKPath = OUStringToOString(m_sFBKPath, RTL_TEXTENCODING_UTF8); + + + OStringBuffer aRequest; // byte array + + + aRequest.append((char) nAction); + + aRequest.append((char) isc_spb_dbname); // .fdb + sal_uInt16 nFDBLength = sFDBPath.getLength(); + aRequest.append((char) (nFDBLength & 0xFF)); // least significant byte first + aRequest.append((char) ((nFDBLength >> 8) & 0xFF)); + aRequest.append(sFDBPath); + + aRequest.append((char) isc_spb_bkp_file); // .fbk + sal_uInt16 nFBKLength = sFBKPath.getLength(); + aRequest.append((char) (nFBKLength & 0xFF)); + aRequest.append((char) ((nFBKLength >> 8) & 0xFF)); + aRequest.append(sFBKPath); + + if (nAction == isc_action_svc_restore) + { + aRequest.append((char) isc_spb_options); // 4-Byte bitmask + char sOptions[4]; + char * pOptions = sOptions; + ADD_SPB_NUMERIC(pOptions, isc_spb_res_create); + aRequest.append(sOptions, 4); + } + + isc_svc_handle aServiceHandle; + aServiceHandle = attachServiceManager(); + + if (isc_service_start(aStatusVector, + &aServiceHandle, + nullptr, + aRequest.getLength(), + aRequest.getStr())) + { + evaluateStatusVector(aStatusVector, "isc_service_start", *this); + } + + char aInfoSPB = isc_info_svc_line; + char aResults[256]; + + // query blocks until success or error + if(isc_service_query(aStatusVector, + &aServiceHandle, + nullptr, // Reserved null + 0,nullptr, // "send" spb -- size and spb -- not needed? + 1, + &aInfoSPB, + sizeof(aResults), + aResults)) + { + evaluateStatusVector(aStatusVector, "isc_service_query", *this); + } + + detachServiceManager(aServiceHandle); +} + + void SAL_CALL Connection::rollback() throw(SQLException, RuntimeException, std::exception) { MutexGuard aGuard( m_aMutex ); @@ -690,22 +838,35 @@ void SAL_CALL Connection::documentEventOccured( const DocumentEvent& Event ) commit(); // Commit and close transaction if ( m_bIsEmbedded && m_xEmbeddedStorage.is() ) { - SAL_INFO("connectivity.firebird", "Writing .fdb into .odb" ); + SAL_INFO("connectivity.firebird", "Writing .fbk from running db"); + runBackupService(isc_action_svc_backup); - Reference< XStream > xDBStream(m_xEmbeddedStorage->openStreamElement(our_sDBLocation, + Reference< XStream > xDBStream(m_xEmbeddedStorage->openStreamElement(our_sFBKLocation, ElementModes::WRITE)); + // TODO: verify the backup actually exists -- the backup service + // can fail without giving any sane error messages / telling us + // that it failed. using namespace ::comphelper; Reference< XComponentContext > xContext = comphelper::getProcessComponentContext(); Reference< XInputStream > xInputStream; if (xContext.is()) + { xInputStream = - OStorageHelper::GetInputStreamFromURL(m_sFirebirdURL, xContext); - if (xInputStream.is()) - OStorageHelper::CopyInputToOutput( xInputStream, + OStorageHelper::GetInputStreamFromURL(m_sFBKPath, xContext); + if (xInputStream.is()) + OStorageHelper::CopyInputToOutput( xInputStream, xDBStream->getOutputStream()); - // TODO: ensure db is in safe state + + // remove old fdb file if exists + uno::Reference< ucb::XSimpleFileAccess > xFileAccess( + ucb::SimpleFileAccess::create(xContext), + uno::UNO_QUERY); + if (xFileAccess->exists(m_sFirebirdURL)) + xFileAccess->kill(m_sFirebirdURL); + } } + } } // XEventListener @@ -795,10 +956,10 @@ void Connection::disposing() cppu::WeakComponentImplHelperBase::disposing(); m_xDriver.clear(); - if (m_pExtractedFDBFile) + if (m_pDatabaseFileDir) { - ::utl::removeTree(m_pExtractedFDBFile->GetURL()); - m_pExtractedFDBFile.reset(); + ::utl::removeTree((m_pDatabaseFileDir)->GetURL()); + m_pDatabaseFileDir.reset(); } } @@ -833,81 +994,4 @@ uno::Reference< XTablesSupplier > Connection::createCatalog() } -void Connection::rebuildIndexes() throw (SQLException, RuntimeException, std::exception) -{ - MutexGuard aGuard(m_aMutex); - - try - { - // We only need to do this for character based columns on user-created tables. - - // Ideally we'd use a FOR SELECT ... INTO .... DO ..., but that seems to - // only be possible using PSQL, i.e. using a stored procedure. - OUString sSql( - // multiple columns possible per index, only select once - "SELECT DISTINCT indices.RDB$INDEX_NAME " - "FROM RDB$INDICES indices " - "JOIN RDB$INDEX_SEGMENTS index_segments " - "ON (indices.RDB$INDEX_NAME = index_segments.RDB$INDEX_NAME) " - "JOIN RDB$RELATION_FIELDS relation_fields " - "ON (index_segments.RDB$FIELD_NAME = relation_fields.RDB$FIELD_NAME) " - "JOIN RDB$FIELDS fields " - "ON (relation_fields.RDB$FIELD_SOURCE = fields.RDB$FIELD_NAME) " - - "WHERE (indices.RDB$SYSTEM_FLAG = 0) " - // TODO: what about blr_text2 etc. ? - "AND ((fields.RDB$FIELD_TYPE = " + OUString::number((int) blr_text) + ") " - " OR (fields.RDB$FIELD_TYPE = " + OUString::number((int) blr_varying) + ")) " - "AND (indices.RDB$INDEX_INACTIVE IS NULL OR indices.RDB$INDEX_INACTIVE = 0) " - ); - - uno::Reference< XStatement > xCharIndicesStatement = createStatement(); - uno::Reference< XResultSet > xCharIndices = - xCharIndicesStatement->executeQuery(sSql); - uno::Reference< XRow > xRow(xCharIndices, UNO_QUERY_THROW); - - uno::Reference< XStatement > xAlterIndexStatement = createStatement(); - - // ALTER is a DDL statement, hence using Statement will cause a commit - // after every alter -- in this case this is inappropriate (xCharIndicesStatement - // and its ResultSet become invalidated) hence we use the native api. - while (xCharIndices->next()) - { - OUString sIndexName(sanitizeIdentifier(xRow->getString(1))); - SAL_INFO("connectivity.firebird", "rebuilding index " + sIndexName); - OString sAlterIndex = "ALTER INDEX \"" - + OUStringToOString(sIndexName, RTL_TEXTENCODING_UTF8) - + "\" ACTIVE"; - - ISC_STATUS_ARRAY aStatusVector; - ISC_STATUS aErr; - - aErr = isc_dsql_execute_immediate(aStatusVector, - &getDBHandle(), - &getTransaction(), - 0, // Length: 0 for null terminated - sAlterIndex.getStr(), - FIREBIRD_SQL_DIALECT, - nullptr); - if (aErr) - evaluateStatusVector(aStatusVector, - "rebuildIndexes:isc_dsql_execute_immediate", - *this); - } - commit(); - } - catch (const Exception&) - { - throw; - } - catch (const std::exception&) - { - throw; - } - catch (...) // const Firebird::Exception& firebird throws this, but doesn't install the fb_exception.h that declares it - { - throw std::runtime_error("Generic Firebird::Exception"); - } -} -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/drivers/firebird/Connection.hxx b/connectivity/source/drivers/firebird/Connection.hxx index aad55e9fb94d..c35758c4fcbb 100644 --- a/connectivity/source/drivers/firebird/Connection.hxx +++ b/connectivity/source/drivers/firebird/Connection.hxx @@ -89,10 +89,17 @@ namespace connectivity ::rtl::OUString m_sFirebirdURL; /* EMBEDDED MODE DATA */ - /** Denotes that we have a .fdb stored within a .odb file. */ + /** Denotes that we have a database stored within a .odb file. */ bool m_bIsEmbedded; /** + * Denotes that the database stored in the .odb file is an + * archive file (.fbk). Older version of LO had a .fdb file, not a + * .fbk. + * (Only used if m_bIsEmbedded is true). + */ + bool m_bIsFbkStored; + /** * Handle for the parent DatabaseDocument. We need to notify this * whenever any data is written to our temporary database so that * the user is able to save this back to the .odb file. @@ -103,17 +110,39 @@ namespace connectivity m_xParentDocument; /** - * Handle for the folder within the .odb where we store our .fdb + * Handle for the folder within the .odb where we store our .fbk * (Only used if m_bIsEmbedded is true). */ css::uno::Reference< css::embed::XStorage > m_xEmbeddedStorage; /** - * The temporary folder where we extract the .fdb from a .odb. + * The temporary folder where we extract the .fbk from a .odb, + * and also store the temporary .fdb * It is only valid if m_bIsEmbedded is true. + * + * The extracted .fbk is written in firebird.fbk, the temporary + * .fdb is stored as firebird.fdb. + */ + std::unique_ptr< ::utl::TempFile > m_pDatabaseFileDir; + /** + * Path for our extracted .fbk file. + * + * (The temporary .fdb is our m_sFirebirdURL.) + */ + ::rtl::OUString m_sFBKPath; + + void loadDatabaseFile(const OUString& pSrcLocation, const OUString& pTmpLocation); + + /** + * Run the backup service, use nAction = + * isc_action_svc_backup to backup, nAction = isc_action_svc_restore + * to restore. */ - std::unique_ptr< ::utl::TempFile > m_pExtractedFDBFile; + void runBackupService(const short nAction); + + isc_svc_handle attachServiceManager(); + void detachServiceManager(isc_svc_handle pServiceHandle); /** We are using an external (local) file */ bool m_bIsFile; diff --git a/external/firebird/ExternalPackage_firebird.mk b/external/firebird/ExternalPackage_firebird.mk index eb9438d267ea..1e8752d548eb 100644 --- a/external/firebird/ExternalPackage_firebird.mk +++ b/external/firebird/ExternalPackage_firebird.mk @@ -23,5 +23,6 @@ $(eval $(call gb_ExternalPackage_add_file,firebird,$(LIBO_LIB_FOLDER)/libEngine1 endif $(eval $(call gb_ExternalPackage_add_file,firebird,$(LIBO_SHARE_FOLDER)/firebird/firebird.msg,gen/Release/firebird/firebird.msg)) +$(eval $(call gb_ExternalPackage_add_file,firebird,$(LIBO_SHARE_FOLDER)/firebird/security3.fdb,gen/Release/firebird/security3.fdb)) # vim: set noet sw=4 ts=4: |