connectivity/source/drivers/firebird/Connection.cxx |  304 ++++++++++++--------
 connectivity/source/drivers/firebird/Connection.hxx |   37 ++
 external/firebird/ExternalPackage_firebird.mk       |    1 
 3 files changed, 228 insertions(+), 114 deletions(-)

New commits:
commit 0cc1ddf2d8d6bc7df74fdd8f8f97381df681177d
Author: Wastack <btom...@gmail.com>
Date:   Thu Aug 11 12:02:56 2016 +0200

    tdf#72987 GSoC Use Firebird backup format
    
    Store embedded database files as an archive (.fbk) file.
    The firebird database file is extracted when opening an
    odb file, and archived for each saving.
    
    Change-Id: I6c985f89a0fb01b2294f728b4581053521ca0c88
    Reviewed-on: https://gerrit.libreoffice.org/28045
    Reviewed-by: Lionel Elie Mamane <lio...@mamane.lu>
    Tested-by: Jenkins <c...@libreoffice.org>

diff --git a/connectivity/source/drivers/firebird/Connection.cxx 
b/connectivity/source/drivers/firebird/Connection.cxx
index ad843c4..1218785 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 aad55e9..c35758c 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 eb9438d..1e8752d 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:
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to