[glom/feature_backup2: 6/6] Improve backup saving and add backup restoring.



commit fe557320ae35d9d0164b505ee5da039dacfd9d36
Author: Murray Cumming <murrayc murrayc com>
Date:   Fri Jul 2 17:05:48 2010 +0200

    Improve backup saving and add backup restoring.
    
    	* glom/libglom/document/document.[h|cc]: Added get/set_is_backup_file(),
    	saved in the XML document..
      on_menu_developer_export_backup(): Mark the .glom file as a backup file.
    	Rename recreate_database() to recreate_database_from_example().
    	on_document_load(): Handle backup .glom files similarly to examples files,
    	but using the backup file instead of example data.
    	* glom/frame_glom.cc:
    	* glom/libglom/connection_pool.[h|cc]:
    	* glom/libglom/connectionpool_backends/backend.[h|cc]: startup(): Return a
    	StartupErrors enum, to provide more clues, for instance to say that backup
    	data was found.
    	Add get/set_database_directory_uri(), replacing the one in sqlite.[h|cc] and
    	replacing set_self_hosting_data_uri() in postgres_self.[hc|cc].
    	save_backup(): Don't take a filepath - use get_database_directory_uri()
    	instead.
    	Added convert_backup() to restore from a backup.
    	* glom/libglom/connectionpool_backends/sqlite.[h|cc]:
    	* glom/libglom/connectionpool_backends/postgres.[h|cc]:
    	get_path_to_postgres_executable(): Catch exceptions from Glib::build_filename().
    	save_backup(): Use --format=c with pg_dump, because the default one can't be
    	used with pg_restore.
    	Implement convert_backup(), using pg_restore.
    	Added get_self_hosting_config_path(), get_self_hosting_data_path() and
    	get_self_hosting_backup_path() to avoid copy/pasting magic paths.
    	* glom/libglom/connectionpool_backends/postgres_self.[h|cc]:
    	Adapted.
    	* glom/application.[h|cc]:
    	* tests/test_selfhosting_new_empty.cc: Adapted to check the enum result from
    	startup().

 ChangeLog                                          |   34 ++++
 glom/application.cc                                |   86 +++++++--
 glom/application.h                                 |    2 +-
 glom/frame_glom.cc                                 |   22 ++-
 glom/libglom/connectionpool.cc                     |   50 ++++-
 glom/libglom/connectionpool.h                      |   21 ++-
 glom/libglom/connectionpool_backends/backend.cc    |   15 ++-
 glom/libglom/connectionpool_backends/backend.h     |   30 +++-
 glom/libglom/connectionpool_backends/postgres.cc   |  203 +++++++++++++++++++-
 glom/libglom/connectionpool_backends/postgres.h    |   29 +++-
 .../connectionpool_backends/postgres_self.cc       |  116 +++++------
 .../connectionpool_backends/postgres_self.h        |   12 +-
 glom/libglom/connectionpool_backends/sqlite.cc     |   19 +-
 glom/libglom/connectionpool_backends/sqlite.h      |    9 +-
 glom/libglom/document/document.cc                  |   21 ++-
 glom/libglom/document/document.h                   |    4 +
 tests/test_selfhosting_new_empty.cc                |    2 +-
 17 files changed, 536 insertions(+), 139 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index cad2a5d..0139106 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,37 @@
+2010-07-03  Murray Cumming  <murrayc murrayc com>
+
+	Improve backup saving and add backup restoring.
+	
+	* glom/libglom/document/document.[h|cc]: Added get/set_is_backup_file(), 
+	saved in the XML document..
+  on_menu_developer_export_backup(): Mark the .glom file as a backup file.
+	Rename recreate_database() to recreate_database_from_example().
+	on_document_load(): Handle backup .glom files similarly to examples files, 
+	but using the backup file instead of example data.
+	* glom/frame_glom.cc:
+	* glom/libglom/connection_pool.[h|cc]:
+	* glom/libglom/connectionpool_backends/backend.[h|cc]: startup(): Return a 
+	StartupErrors enum, to provide more clues, for instance to say that backup 
+	data was found.
+	Add get/set_database_directory_uri(), replacing the one in sqlite.[h|cc] and 
+	replacing set_self_hosting_data_uri() in postgres_self.[hc|cc].
+	save_backup(): Don't take a filepath - use get_database_directory_uri() 
+	instead.
+	Added convert_backup() to restore from a backup.
+	* glom/libglom/connectionpool_backends/sqlite.[h|cc]:
+	* glom/libglom/connectionpool_backends/postgres.[h|cc]:
+	get_path_to_postgres_executable(): Catch exceptions from Glib::build_filename().
+	save_backup(): Use --format=c with pg_dump, because the default one can't be 
+	used with pg_restore.
+	Implement convert_backup(), using pg_restore.
+	Added get_self_hosting_config_path(), get_self_hosting_data_path() and 
+	get_self_hosting_backup_path() to avoid copy/pasting magic paths.
+	* glom/libglom/connectionpool_backends/postgres_self.[h|cc]:
+	Adapted.
+	* glom/application.[h|cc]:
+	* tests/test_selfhosting_new_empty.cc: Adapted to check the enum result from 
+	startup().
+	
 2010-07-02  Murray Cumming  <murrayc murrayc com>
 
 	Document: Avoid a crash when creating the parent directory.
diff --git a/glom/application.cc b/glom/application.cc
index 4150641..e74b723 100644
--- a/glom/application.cc
+++ b/glom/application.cc
@@ -1023,26 +1023,39 @@ bool Application::on_document_load()
     pDocument->set_allow_autosave(false);
 #endif // !GLOM_ENABLE_CLIENT_ONLY
 
-    // Example files are not supported in client only mode because they
+    // Example files and backup files are not supported in client only mode because they
     // would need to be saved, but saving support is disabled.
 #ifndef GLOM_ENABLE_CLIENT_ONLY
     const bool is_example = pDocument->get_is_example_file();
+    const bool is_backup = pDocument->get_is_backup_file(); 
 #endif // !GLOM_ENABLE_CLIENT_ONLY
-    if(pDocument->get_is_example_file())
+    const std::string original_uri = pDocument->get_file_uri();
+
+    if(is_example || is_backup)
     {
-#ifndef GLOM_ENABLE_CLIENT_ONLY
+#ifndef GLOM_ENABLE_CLIENT_ONLY  
       // Remember the URI to the example file to be able to prevent
       // adding the URI to the recently used files in document_history_add.
       // We want to add the document that is created from the example
       // instead of the example itself.
       // TODO: This is a weird hack. Find a nicer way. murrayc.
-      m_example_uri = pDocument->get_file_uri();
+      m_example_uri = original_uri;  
 
       pDocument->set_file_uri(Glib::ustring()); //Prevent it from defaulting to the read-only examples directory when offering saveas.
       //m_ui_save_extra_* are used by offer_saveas() if it's not empty:
       m_ui_save_extra_showextras = true;
-      m_ui_save_extra_title = _("Creating From Example File");
-      m_ui_save_extra_message = _("To use this example file you must save an editable copy of the file. A new database will also be created on the server.");
+      
+      if(is_example)
+      {
+        m_ui_save_extra_title = _("Creating From Example File");
+        m_ui_save_extra_message = _("To use this example file you must save an editable copy of the file. A new database will also be created on the server.");
+      }
+      else if(is_backup)
+      {
+        m_ui_save_extra_title = _("Creating From Backup File");
+        m_ui_save_extra_message = _("To use this backup file you must save an editable copy of the file. A new database will also be created on the server.");
+      }
+    
       m_ui_save_extra_newdb_title = "TODO";
       m_ui_save_extra_newdb_hosting_mode = Document::HOSTING_MODE_DEFAULT;
 
@@ -1063,6 +1076,7 @@ bool Application::on_document_load()
         pDocument->set_hosting_mode(m_ui_save_extra_newdb_hosting_mode);
         m_ui_save_extra_newdb_hosting_mode = Document::HOSTING_MODE_DEFAULT;
         pDocument->set_is_example_file(false);
+        pDocument->set_is_backup_file(false);
 
         // For self-hosting, we will choose a port later. For central
         // hosting, try several default ports. Don't use the values that
@@ -1128,7 +1142,7 @@ bool Application::on_document_load()
       bool test = false;
 
 #ifndef GLOM_ENABLE_CLIENT_ONLY
-      if(is_example)
+      if(is_example || is_backup)
       {
         //The user has already had the chance to specify a new filename and database name.
         test = m_pFrame->connection_request_password_and_choose_new_database_name();
@@ -1182,7 +1196,7 @@ bool Application::on_document_load()
         //Create the example database:
         //connection_request_password_and_choose_new_database_name() has already change the database name to a new unused one:
         bool user_cancelled = false;
-        const bool test = recreate_database(user_cancelled);
+        const bool test = recreate_database_from_example(user_cancelled);
         if(!test)
         {
           // TODO: Do we need to call connection_pool->cleanup() here, for
@@ -1194,7 +1208,7 @@ bool Application::on_document_load()
             //Let the user try again.
             //A warning has already been shown.
             //TODO: No, I don't think there is a warning.
-            std::cerr << "Application::on_document_load(): recreate_database() failed." << std::endl;
+            std::cerr << "Application::on_document_load(): recreate_database_from_example() failed." << std::endl;
             return offer_new_or_existing();
           }
           else
@@ -1210,6 +1224,37 @@ bool Application::on_document_load()
           pDocument->set_userlevel(user_level); //Change it back.
         }
       }
+      else if(is_backup)
+      {
+        std::string original_dir_path;
+         
+        Glib::RefPtr<Gio::File> gio_file = Gio::File::create_for_uri(original_uri);
+        if(gio_file)
+        {
+          Glib::RefPtr<Gio::File> parent = gio_file->get_parent();
+          if(parent)
+          {
+            try
+            {
+              original_dir_path = Glib::filename_from_uri(parent->get_uri());
+            }
+            catch(const Glib::Error& ex)
+            {
+              std::cerr << G_STRFUNC << ": Glib::filename_from_uri() failed: " << ex.what() << std::endl;
+            }
+          }
+        }
+        
+        if(original_dir_path.empty())
+        {
+          std::cerr << G_STRFUNC << ": original_dir_path is empty." << std::endl;
+          return false;
+        }
+      
+        //Restore the database from the backup:
+        std::cout << "DEBUG: original_dir_path=" << original_dir_path << std::endl;
+        const bool restored = connection_pool->convert_backup(ConnectionPool::SlotProgress() /* TODO */, original_dir_path);
+      }
 #endif // !GLOM_ENABLE_CLIENT_ONLY
 
       //Switch to operator mode when opening new documents:
@@ -1587,7 +1632,7 @@ void Application::on_menu_help_contents()
 #endif //GLOM_ENABLE_MAEMO
 
 #ifndef GLOM_ENABLE_CLIENT_ONLY
-bool Application::recreate_database(bool& user_cancelled)
+bool Application::recreate_database_from_example(bool& user_cancelled)
 {
   //Create a database, based on the information in the current document:
   Document* pDocument = static_cast<Document*>(get_document());
@@ -1618,7 +1663,7 @@ bool Application::recreate_database(bool& user_cancelled)
     if(!error.get())
     {
 #endif // GLIBMM_EXCEPTIONS_ENABLED
-      g_warning("Application::recreate_database(): Failed because database exists already.");
+      g_warning("Application::recreate_database_from_example(): Failed because database exists already.");
 
       return false; //Connection to the database succeeded, because no exception was thrown. so the database exists already.
 #ifndef GLIBMM_EXCEPTIONS_ENABLED
@@ -1639,7 +1684,7 @@ bool Application::recreate_database(bool& user_cancelled)
       if(ex.get_failure_type() == ExceptionConnection::FAILURE_NO_SERVER)
       {
         user_cancelled = true; //Eventually, the user will cancel after retrying.
-        g_warning("Application::recreate_database(): Failed because connection to server failed, without specifying a database.");
+        g_warning("Application::recreate_database_from_example(): Failed because connection to server failed, without specifying a database.");
         return false;
       }
 #ifndef GLIBMM_EXCEPTIONS_ENABLED
@@ -1700,7 +1745,7 @@ bool Application::recreate_database(bool& user_cancelled)
   {
     const std::exception& ex = *error.get();
 #endif // GLIBMM_EXCEPTIONS_ENABLED
-    g_warning("Application::recreate_database(): Failed to connect to the newly-created database.");
+    g_warning("Application::recreate_database_from_example(): Failed to connect to the newly-created database.");
     return false;
   }
 
@@ -1721,7 +1766,7 @@ bool Application::recreate_database(bool& user_cancelled)
     dialog_progress->pulse();
     if(!table_creation_succeeded)
     {
-      g_warning("Application::recreate_database(): CREATE TABLE failed with the newly-created database.");
+      g_warning("Application::recreate_database_from_example(): CREATE TABLE failed with the newly-created database.");
       return false;
     }
   }
@@ -1749,13 +1794,13 @@ bool Application::recreate_database(bool& user_cancelled)
 
       if(!table_insert_succeeded)
       {
-        g_warning("Application::recreate_database(): INSERT of example data failed with the newly-created database.");
+        g_warning("Application::recreate_database_from_example(): INSERT of example data failed with the newly-created database.");
         return false;
       }
     //}
     //catch(const std::exception& ex)
     //{
-    //  std::cerr << "Application::recreate_database(): exception: " << ex.what() << std::endl;
+    //  std::cerr << "Application::recreate_database_from_example(): exception: " << ex.what() << std::endl;
       //HandleError(ex);
     //}
 
@@ -2171,6 +2216,7 @@ void Application::on_menu_file_save_as_example()
 
       const bool bTest = document->save();
       document->set_is_example_file(false);
+      document->set_is_backup_file(false);
       document->set_file_uri(file_uriOld);
       document->set_allow_autosave(true);
 
@@ -2509,17 +2555,17 @@ void Application::on_menu_developer_export_backup()
   document->set_allow_autosave(false); //Prevent saving while we modify the document:
   const Glib::ustring fileuri_old = document->get_file_uri();
   document->set_file_uri(Glib::filename_to_uri(filepath_document), true); //true = enforce file extension;
+  document->set_is_backup_file(true);
   bool saved = document->save();
+  
   document->set_file_uri(fileuri_old);
+  document->set_is_backup_file(false);
   document->set_allow_autosave(true);
 
   if(saved)
   {
-    //Save a backup of the data there:
-    const std::string& filepath_output = Glib::build_filename(path_dir, "backup");
-
     ConnectionPool* connection_pool = ConnectionPool::get_instance();
-    saved = connection_pool->save_backup(ConnectionPool::SlotProgress() /* TODO */, filepath_output);
+    saved = connection_pool->save_backup(ConnectionPool::SlotProgress() /* TODO */, path_dir);
   }
 
   if(!saved)
diff --git a/glom/application.h b/glom/application.h
index 04c90c0..3b70e49 100644
--- a/glom/application.h
+++ b/glom/application.h
@@ -179,7 +179,7 @@ private:
 
   Document* on_connection_pool_get_document();
 
-  bool recreate_database(bool& user_cancelled); //return indicates success.
+  bool recreate_database_from_example(bool& user_cancelled); //return indicates success.
   void stop_self_hosting_of_document_database();
 
   void on_connection_avahi_begin();
diff --git a/glom/frame_glom.cc b/glom/frame_glom.cc
index 1859316..3ea7506 100644
--- a/glom/frame_glom.cc
+++ b/glom/frame_glom.cc
@@ -1113,7 +1113,13 @@ void Frame_Glom::on_menu_file_toggle_share(const Glib::RefPtr<Gtk::ToggleAction>
     }
 
     connectionpool->set_network_shared(sigc::mem_fun(*this, &Frame_Glom::on_connection_startup_progress), shared);
-    connectionpool->startup( sigc::mem_fun(*this, &Frame_Glom::on_connection_startup_progress) );
+    ConnectionPool::StartupErrors started = connectionpool->startup( sigc::mem_fun(*this, &Frame_Glom::on_connection_startup_progress) );
+    if(started != ConnectionPool::Backend::STARTUPERROR_NONE)
+    {
+      std::cerr << G_STRFUNC << ": startup() failed." << std::endl;
+      //TODO: Tell the user.
+    }
+
     connectionpool->set_ready_to_connect();
 
     if(m_dialog_progess_connection_startup)
@@ -2145,8 +2151,13 @@ bool Frame_Glom::connection_request_password_and_choose_new_database_name()
   }
 
   // Do startup, such as starting the self-hosting database server
-  if(!connection_pool->startup( sigc::mem_fun(*this, &Frame_Glom::on_connection_startup_progress) ))
+  const ConnectionPool::StartupErrors started = 
+    connection_pool->startup( sigc::mem_fun(*this, &Frame_Glom::on_connection_startup_progress) );
+  if(started != ConnectionPool::Backend::STARTUPERROR_NONE)
+  {
+    std::cerr << G_STRFUNC << ": startup() failed." << std::endl;
     return false;
+  }  
 
   if(m_dialog_progess_connection_startup)
   {
@@ -2317,8 +2328,13 @@ bool Frame_Glom::connection_request_password_and_attempt(bool& database_not_foun
   //Start a self-hosted server if necessary:
   ConnectionPool* connection_pool = ConnectionPool::get_instance();
   connection_pool->setup_from_document(document);
-  if(!connection_pool->startup( sigc::mem_fun(*this, &Frame_Glom::on_connection_startup_progress) ))
+  const ConnectionPool::StartupErrors started = 
+    connection_pool->startup( sigc::mem_fun(*this, &Frame_Glom::on_connection_startup_progress) );
+  if(started != ConnectionPool::Backend::STARTUPERROR_NONE)
+  {
+    std::cerr << G_STRFUNC << ": startup() failed." << std::endl;
     return false;
+  }
 
   if(m_dialog_progess_connection_startup)
   {
diff --git a/glom/libglom/connectionpool.cc b/glom/libglom/connectionpool.cc
index 6027de5..f7bb540 100644
--- a/glom/libglom/connectionpool.cc
+++ b/glom/libglom/connectionpool.cc
@@ -136,7 +136,7 @@ void ConnectionPool::setup_from_document(const Document* document)
   case Document::HOSTING_MODE_POSTGRES_SELF:
     {
       ConnectionPoolBackends::PostgresSelfHosted* backend = new ConnectionPoolBackends::PostgresSelfHosted;
-      backend->set_self_hosting_data_uri(document->get_connection_self_hosted_directory_uri());
+      backend->set_database_directory_uri(document->get_connection_self_hosted_directory_uri());
       set_backend(std::auto_ptr<ConnectionPool::Backend>(backend));
     }
     break;
@@ -415,10 +415,43 @@ void ConnectionPool::set_user(const Glib::ustring& value)
   invalidate_connection();
 }
 
-bool ConnectionPool::save_backup(const SlotProgress& slot_progress, const std::string& filepath_output)
+bool ConnectionPool::save_backup(const SlotProgress& slot_progress, const std::string& path_dir)
+{
+  g_assert(m_backend.get());
+
+  const std::string old_uri = m_backend->get_database_directory_uri();
+  
+  std::string uri;
+  try
+  {
+    //TODO: Avoid the copy/paste of glom_postgres_data and make it work for sqlite too.
+    const std::string subdir = Glib::build_filename(path_dir, "glom_postgres_data"); 
+    uri = Glib::filename_to_uri(subdir);
+  }
+  catch(const Glib::Error& ex)
+  {
+    std::cerr << G_STRFUNC << ": exception from Glib::build_filename(): " << ex.what() << std::endl;
+    return false;
+  }
+  
+  m_backend->set_database_directory_uri(uri);
+  const bool result = m_backend->save_backup(slot_progress, m_user, m_password, m_database);
+  m_backend->set_database_directory_uri(old_uri);
+  return result;
+}
+
+bool ConnectionPool::convert_backup(const SlotProgress& slot_progress, const std::string& path_dir)
 {
   g_assert(m_backend.get());
-  return m_backend->save_backup(slot_progress, filepath_output, m_user, m_password, m_database);
+  
+  //TODO: Avoid this copy/paste of the directory name:
+  std::string path_dir_to_use = path_dir;
+  if(!path_dir_to_use.empty())
+  {
+    path_dir_to_use = Glib::build_filename(path_dir, "glom_postgres_data"); 
+  }
+
+  return m_backend->convert_backup(slot_progress, path_dir_to_use, m_user, m_password);
 }
   
 void ConnectionPool::set_password(const Glib::ustring& value)
@@ -574,13 +607,14 @@ static void on_linux_signal(int signum)
   }
 }
 
-bool ConnectionPool::startup(const SlotProgress& slot_progress, bool network_shared)
+ConnectionPool::StartupErrors ConnectionPool::startup(const SlotProgress& slot_progress, bool network_shared)
 {
   if(!m_backend.get())
-    return false;
+    return Backend::STARTUPERROR_FAILED_UNKNOWN_REASON;
 
-  if(!m_backend->startup(slot_progress, network_shared))
-    return false;
+  const Backend::StartupErrors started = m_backend->startup(slot_progress, network_shared);
+  if(started != Backend::STARTUPERROR_NONE)
+    return started;
 
 #ifndef G_OS_WIN32
   //Let clients discover this server via avahi:
@@ -591,7 +625,7 @@ bool ConnectionPool::startup(const SlotProgress& slot_progress, bool network_sha
   //Comment this out if you want to see the backtrace in a debugger.
   //previous_sig_handler = signal(SIGSEGV, &on_linux_signal);
 
-  return true;
+  return started;
 }
 
 bool ConnectionPool::cleanup(const SlotProgress& slot_progress)
diff --git a/glom/libglom/connectionpool.h b/glom/libglom/connectionpool.h
index 18c59e9..67737a3 100644
--- a/glom/libglom/connectionpool.h
+++ b/glom/libglom/connectionpool.h
@@ -163,7 +163,20 @@ public:
   void create_database(const Glib::ustring& database_name, std::auto_ptr<Glib::Error>& error);
 #endif
 
-  bool save_backup(const SlotProgress& slot_progress, const std::string& filepath_output);
+  /** Save a backup of the database in a tarball.
+   * This backup can later be used to recreate the database,
+   * for instance with a later version of PostgreSQL.
+   * See @convert_backup().
+   * @param path_dir The top-level directory for the backup file, using the normal directory structure.
+   * 
+   */
+  bool save_backup(const SlotProgress& slot_progress, const std::string& path_dir);
+  
+  /** Use a backup of the database in a tarball to create a new database.
+   * @param path_dir The top-level directory for the backup file, using the normal directory structure.
+   * See save_backup().
+   */
+  bool convert_backup(const SlotProgress& slot_progress, const std::string& path_dir);
 
   void set_user(const Glib::ustring& value);
   void set_password(const Glib::ustring& value);
@@ -189,7 +202,9 @@ public:
    */
   InitErrors initialize(const SlotProgress& slot_progress, bool network_shared = false);
 
-  /** Start a database server instance for the exisiting database files.
+  typedef Backend::StartupErrors StartupErrors;
+
+  /** Start a database server instance for the existing database files.
    *
    * @param slot_progress A callback to call while the work is still happening.
    * @param network_shared Whether the database (and document) should be available to other users over the network, 
@@ -197,7 +212,7 @@ public:
    * @param parent_window The parent window (transient for) of any dialogs shown during this operation.
    * @result Whether the operation was successful.
    */
-  bool startup(const SlotProgress& slot_progress, bool network_shared = false);
+  StartupErrors startup(const SlotProgress& slot_progress, bool network_shared = false);
 
   /** Stop the database server instance for the database files.
    *
diff --git a/glom/libglom/connectionpool_backends/backend.cc b/glom/libglom/connectionpool_backends/backend.cc
index 201786c..c65449f 100644
--- a/glom/libglom/connectionpool_backends/backend.cc
+++ b/glom/libglom/connectionpool_backends/backend.cc
@@ -50,9 +50,9 @@ Backend::InitErrors Backend::initialize(const SlotProgress& /* slot_progress */,
   return INITERROR_NONE;
 }
 
-bool Backend::startup(const SlotProgress& /* slot_progress */, bool /* network_shared */)
+Backend::StartupErrors Backend::startup(const SlotProgress& /* slot_progress */, bool /* network_shared */)
 {
-  return true;
+  return STARTUPERROR_NONE;
 }
 
 bool Backend::cleanup(const SlotProgress& /* slot_progress */)
@@ -296,6 +296,17 @@ bool Backend::change_columns(const Glib::RefPtr<Gnome::Gda::Connection>& connect
   return true;
 }
 
+void Backend::set_database_directory_uri(const std::string& directory_uri)
+{
+  m_database_directory_uri = directory_uri;
+}
+
+std::string Backend::get_database_directory_uri() const
+{
+  return m_database_directory_uri;
+}
+
+
 } // namespace ConnectionPoolBackends
 
 } // namespace Glom
diff --git a/glom/libglom/connectionpool_backends/backend.h b/glom/libglom/connectionpool_backends/backend.h
index ad71fc5..2a51d84 100644
--- a/glom/libglom/connectionpool_backends/backend.h
+++ b/glom/libglom/connectionpool_backends/backend.h
@@ -77,6 +77,14 @@ public:
      INITERROR_OTHER
   };
   
+  enum StartupErrors
+  {
+    STARTUPERROR_NONE, /*< The database is ready for use. */
+    STARTUPERROR_FAILED_NO_DATA, /*< There is no data for the database. */
+    STARTUPERROR_FAILED_NO_DATA_HAS_BACKUP_DATA, /*< There is no data for the database, but there is a backup file instead. */
+    STARTUPERROR_FAILED_UNKNOWN_REASON /*< Something else failed. */
+  };
+  
 protected:
   /** Helper functions for backend implementations to use, so that these don't
    * need to worry whether glibmm was compiled with exceptions or not.
@@ -116,6 +124,14 @@ protected:
    */
   virtual const char* get_public_schema_name() const = 0;
 
+  /** This specifies that Glom should start its own database server instance (if it's PostgreSQL)
+   * for this database, using the database files stored at the specified uri,
+   * or just use that file (if it's sqlite).
+   * Or it can be used temporarily when calling save_backup() to provide the top-level directory path.
+   */
+  void set_database_directory_uri(const std::string& directory_uri);
+  std::string get_database_directory_uri() const;
+  
   /** This callback should show UI to indicate that work is still happening.
    * For instance, a pulsing ProgressBar.
    */
@@ -130,7 +146,7 @@ protected:
    * if possible. 
    */
   virtual InitErrors initialize(const SlotProgress& slot_progress, const Glib::ustring& initial_username, const Glib::ustring& password, bool network_shared = false);
-
+  
   /** This method is called before the backend is used otherwise. This can
    * be used to start a self-hosted database server. There is no need to implement
    * this function if there is no need for extra startup code.
@@ -139,7 +155,7 @@ protected:
    * @param network_shared Whether the database (and document) should be available to other users over the network, 
    * if possible. 
    */
-  virtual bool startup(const SlotProgress& slot_progress, bool network_shared = false);
+  virtual StartupErrors startup(const SlotProgress& slot_progress, bool network_shared = false);
 
   /** This method is called when the backend is no longer used. This can be
    * used to shut down a self-hosted database server. There is no need to
@@ -185,7 +201,15 @@ protected:
    * This backup can later be used to recreate the database,
    * for instance with a later version of PostgreSQL.
    */
-  virtual bool save_backup(const SlotProgress& slot_progress, const std::string& filepath_output, const Glib::ustring& username, const Glib::ustring& password, const Glib::ustring& database_name) = 0;
+  virtual bool save_backup(const SlotProgress& slot_progress, const Glib::ustring& username, const Glib::ustring& password, const Glib::ustring& database_name) = 0;
+  
+  /** Use a backup of the database in a tarball to create a new database.
+   * See save_backup().
+   */
+  virtual bool convert_backup(const SlotProgress& slot_progress, const std::string& base_directory_uri, const Glib::ustring& username, const Glib::ustring& password) = 0;
+  
+protected:
+  std::string m_database_directory_uri;
 };
 
 } // namespace ConnectionPoolBackends
diff --git a/glom/libglom/connectionpool_backends/postgres.cc b/glom/libglom/connectionpool_backends/postgres.cc
index b51413c..5065d0b 100644
--- a/glom/libglom/connectionpool_backends/postgres.cc
+++ b/glom/libglom/connectionpool_backends/postgres.cc
@@ -24,6 +24,7 @@
 #include <libglom/connectionpool_backends/postgres.h>
 #include <libglom/spawn_with_feedback.h>
 #include <libglom/utils.h>
+#include <giomm.h>
 #include <glibmm/i18n.h>
 
 // Uncomment to see debug messages
@@ -476,7 +477,18 @@ std::string Postgres::get_path_to_postgres_executable(const std::string& program
   // from the other shared data. We can perhaps still change this later by
   // building postgres with another prefix than /local/pgsql.
   gchar* installation_directory = g_win32_get_package_installation_directory_of_module(0);
-  std::string test = Glib::build_filename(installation_directory, Glib::build_filename("bin", real_program));
+  std::string test;
+  
+  try
+  {
+    test = Glib::build_filename(installation_directory, Glib::build_filename("bin", real_program));
+  }
+  catch(const Glib::Error& ex)
+  {
+    std::cerr << G_STRFUNC << ": exception from Glib::build_filename(): " << ex.what() << std::endl;
+    return std::string();
+  }
+
   g_free(installation_directory);
 
   if(Glib::file_test(test, Glib::FILE_TEST_IS_EXECUTABLE))
@@ -486,7 +498,15 @@ std::string Postgres::get_path_to_postgres_executable(const std::string& program
   return Glib::find_program_in_path(real_program);
 #else // G_OS_WIN32
   // POSTGRES_UTILS_PATH is defined in config.h, based on the configure.
-  return Glib::build_filename(POSTGRES_UTILS_PATH, program + EXEEXT);
+  try
+  {
+    return Glib::build_filename(POSTGRES_UTILS_PATH, program + EXEEXT);
+  }
+  catch(const Glib::Error& ex)
+  {
+    std::cerr << G_STRFUNC << ": exception from Glib::build_filename(): " << ex.what() << std::endl;
+    return std::string();
+  }
 #endif // !G_OS_WIN32
 }
 
@@ -502,7 +522,7 @@ Glib::ustring Postgres::port_as_string(int port_num)
   return result;
 }
 
-bool Postgres::save_backup(const SlotProgress& slot_progress, const std::string& filepath_output, const Glib::ustring& username, const Glib::ustring& password, const Glib::ustring& database_name)
+bool Postgres::save_backup(const SlotProgress& slot_progress, const Glib::ustring& username, const Glib::ustring& password, const Glib::ustring& database_name)
 {
 /* TODO:
   if(m_network_shared && !running)
@@ -539,10 +559,15 @@ bool Postgres::save_backup(const SlotProgress& slot_progress, const std::string&
 
   //TODO: Save the password to .pgpass
 
+  const std::string path_backup = get_self_hosting_backup_path(std::string(), true /* create parent directory if necessary */);
+  if(path_backup.empty())
+    return false;
+  
   // Make sure to use double quotes for the executable path, because the
   // CreateProcess() API used on Windows does not support single quotes.
   const std::string command_dump = "\"" + get_path_to_postgres_executable("pg_dump") + "\"" +
-    " --create --file=\"" + filepath_output + "\"" +
+    " --format=c " + // The default (plain) format cannot be used with pg_restore.
+    " --create --file=\"" + path_backup + "\"" +
     " --host=\"" + m_host + "\"" +
     " --port=" + port_as_string(m_port) +
     " --username=\"" + username + "\"" +
@@ -562,6 +587,176 @@ bool Postgres::save_backup(const SlotProgress& slot_progress, const std::string&
   return result;
 }
 
+bool Postgres::convert_backup(const SlotProgress& slot_progress, const std::string& base_directory, const Glib::ustring& username, const Glib::ustring& password)
+{
+/* TODO:
+  if(m_network_shared && !running)
+  {
+    std::cerr << G_STRFUNC << ": The self-hosted database is not running." << std::endl;
+    return;
+  }
+*/
+
+  if(m_host.empty())
+  {
+    std::cerr << G_STRFUNC << ": m_host is empty." << std::endl;
+    return false;
+  }
+
+  if(m_port == 0)
+  {
+    std::cerr << G_STRFUNC << ": m_port is empty." << std::endl;
+    return false;
+  }
+
+  //TODO: Remember the existing username and password?
+  if(username.empty())
+  {
+    std::cerr << G_STRFUNC << ": username is empty." << std::endl;
+    return false;
+  }
+
+  if(password.empty())
+  {
+    std::cerr << G_STRFUNC << ": password is empty." << std::endl;
+    return false;
+  }
+
+  //TODO: Save the password to .pgpass
+
+  //Make sure the path exists:
+  const std::string path_backup = get_self_hosting_backup_path(base_directory);
+  if(path_backup.empty() || !file_exists_filepath(path_backup))
+  {
+    std::cerr << G_STRFUNC << ": Backup file not found: " << path_backup << std::endl;
+    return false;
+  }
+  
+  // Make sure to use double quotes for the executable path, because the
+  // CreateProcess() API used on Windows does not support single quotes.
+  const std::string command_restore = "\"" + get_path_to_postgres_executable("pg_restore") + "\"" +
+    " --create -d template1 " +
+    " --host=\"" + m_host + "\"" +
+    " --port=" + port_as_string(m_port) +
+    " --username=\"" + username + "\"" +
+    " " + path_backup;
+
+  std::cout << "DEBUG: command_restore=" << command_restore << std::endl;
+  
+  //TODO: Put the password in .pgpass
+
+  const bool result = Glom::Spawn::execute_command_line_and_wait(command_restore, slot_progress);
+  if(!result)
+  {
+    std::cerr << "Error while attempting to call pg_restore." << std::endl;
+  }
+
+  return result;
+}
+
+std::string Postgres::get_self_hosting_path(bool create, const std::string& child_directory)
+{
+  //Get the filepath of the directory that we should create:
+  const std::string dbdir_uri = m_database_directory_uri;
+  //std::cout << "debug: dbdir_uri=" << dbdir_uri << std::endl;
+
+  std::string dbdir;
+  try
+  {
+    dbdir = Glib::build_filename(
+      Glib::filename_from_uri(dbdir_uri), child_directory);
+  }
+  catch(const Glib::Error& ex)
+  {
+    std::cerr << G_STRFUNC << ": exception from Glib::build_filename(): " << ex.what() << std::endl; 
+  }
+
+  if(file_exists_filepath(dbdir))
+    return dbdir;
+  else if(!create)
+    return std::string();
+
+  //Create the directory:
+  
+  //std::cout << "debug: dbdir=" << dbdir << std::endl;
+  g_assert(!dbdir.empty());
+
+  if(create_directory_filepath(dbdir))
+    return dbdir;
+  else
+    return std::string();
+}
+
+std::string Postgres::get_self_hosting_config_path(bool create)
+{
+  return get_self_hosting_path(create, "config");
+}
+
+std::string Postgres::get_self_hosting_data_path(bool create)
+{
+  return get_self_hosting_path(create, "data");
+}
+
+std::string Postgres::get_self_hosting_backup_path(const std::string& base_directory, bool create_parent_dir)
+{ 
+  //This is a file, not a directory, so we don't use get_self_hosting_path("backup");
+  std::string dbdir;
+  if(base_directory.empty())
+    dbdir = get_self_hosting_path(create_parent_dir);
+  else
+  {
+    dbdir = base_directory;
+  }
+  
+  if(dbdir.empty())
+    return std::string();
+
+  try
+  {
+    return Glib::build_filename(dbdir, "backup");
+  }
+  catch(const Glib::Error& ex)
+  {
+    std::cerr << G_STRFUNC << ": exception from Glib::build_filename(): " << ex.what() << std::endl; 
+    return std::string();
+  }
+}
+
+bool Postgres::create_directory_filepath(const std::string& filepath)
+{
+  if(filepath.empty())
+    return false;
+
+  const int mkdir_succeeded = g_mkdir_with_parents(filepath.c_str(), 0770);
+  if(mkdir_succeeded == -1)
+  {
+    std::cerr << G_STRFUNC << "Error from g_mkdir_with_parents() while trying to create directory: " << filepath << std::endl;
+    perror("Error from g_mkdir_with_parents");
+
+    return false;
+  }
+  
+  return true;
+}
+
+bool Postgres::file_exists_filepath(const std::string& filepath)
+{
+  if(filepath.empty())
+    return false;
+
+  const Glib::RefPtr<Gio::File> file = Gio::File::create_for_path(filepath);
+  return file && file->query_exists();
+}
+
+bool Postgres::file_exists_uri(const std::string& uri) const
+{
+  if(uri.empty())
+    return false;
+
+  const Glib::RefPtr<Gio::File> file = Gio::File::create_for_uri(uri);
+  return file && file->query_exists();
+}
+
 } //namespace ConnectionPoolBackends
 
 } //namespace Glom
diff --git a/glom/libglom/connectionpool_backends/postgres.h b/glom/libglom/connectionpool_backends/postgres.h
index a887c12..6878f70 100644
--- a/glom/libglom/connectionpool_backends/postgres.h
+++ b/glom/libglom/connectionpool_backends/postgres.h
@@ -52,9 +52,15 @@ public:
    */
   static bool check_postgres_gda_client_is_available();
 
-  virtual bool save_backup(const SlotProgress& slot_progress, const std::string& filepath_output, const Glib::ustring& username, const Glib::ustring& password, const Glib::ustring& database_name);
+  /** Save a backup file, using the same directory layout as used by self-hosting.
+   */
+  virtual bool save_backup(const SlotProgress& slot_progress, const Glib::ustring& username, const Glib::ustring& password, const Glib::ustring& database_name);
+  
+  virtual bool convert_backup(const SlotProgress& slot_progress, const std::string& base_directory, const Glib::ustring& username, const Glib::ustring& password);
 
   static std::string get_path_to_postgres_executable(const std::string& program);
+  
+  
 
 private:
   virtual Field::sql_format get_sql_format() const { return Field::SQL_FORMAT_POSTGRES; }
@@ -63,7 +69,7 @@ private:
   virtual const char* get_public_schema_name() const { return "public"; }
 
   virtual bool change_columns(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const type_vec_const_fields& old_fields, const type_vec_const_fields& new_fields, std::auto_ptr<Glib::Error>& error);
-
+    
 protected:
   bool attempt_create_database(const Glib::ustring& database_name, const Glib::ustring& host, const Glib::ustring& port, const Glib::ustring& username, const Glib::ustring& password, std::auto_ptr<Glib::Error>& error);
 
@@ -72,7 +78,26 @@ protected:
    */
   Glib::RefPtr<Gnome::Gda::Connection> attempt_connect(const Glib::ustring& port, const Glib::ustring& database, const Glib::ustring& username, const Glib::ustring& password, std::auto_ptr<ExceptionConnection>& error) throw();
 
+ std::string get_self_hosting_path(bool create = false, const std::string& child_directory = std::string());
+  
+  /** Get the path to the config sub-directory, optionally creating it.
+   */
+  std::string get_self_hosting_config_path(bool create = false);
+  
+  /** Get the path to the data sub-directory, optionally creating it.
+   */
+  std::string get_self_hosting_data_path(bool create = false);
+  
+  /** Get the path to the backup file, regardless of whether it exists.
+   * @param base_directory Where to find the backup file, under a normal Glom directory structure.
+   * If @a base_directory is empty then it uses get_database_directory_uri().
+   */
+  std::string get_self_hosting_backup_path(const std::string& base_directory = std::string(), bool create_parent_dir = false);
 
+  bool create_directory_filepath(const std::string& filepath);
+  bool file_exists_filepath(const std::string& filepath);
+  bool file_exists_uri(const std::string& uri) const;
+  
 protected:
   static Glib::ustring port_as_string(int port_num);
 
diff --git a/glom/libglom/connectionpool_backends/postgres_self.cc b/glom/libglom/connectionpool_backends/postgres_self.cc
index 2d67f52..e8c8771 100644
--- a/glom/libglom/connectionpool_backends/postgres_self.cc
+++ b/glom/libglom/connectionpool_backends/postgres_self.cc
@@ -105,7 +105,9 @@ DEFAULT_CONFIG_PG_HBA_REMOTE_EXTRA
 #define PORT_POSTGRESQL_SELF_HOSTED_START 5433
 #define PORT_POSTGRESQL_SELF_HOSTED_END 5500
 
-#define DEFAULT_CONFIG_PG_IDENT ""
+static const char* DEFAULT_CONFIG_PG_IDENT = "";
+static const char* FILENAME_DATA = "data";
+static const char* FILENAME_BACKUP = "backup";
 
 PostgresSelfHosted::PostgresSelfHosted()
 : m_network_shared(false)
@@ -113,16 +115,6 @@ PostgresSelfHosted::PostgresSelfHosted()
   m_host = "localhost";
 }
 
-void PostgresSelfHosted::set_self_hosting_data_uri(const std::string& data_uri)
-{
-  if(m_self_hosting_data_uri != data_uri)
-  {
-    // Can't change data uri if we are running the server on another data uri
-    g_assert(!get_self_hosting_active());
-    m_self_hosting_data_uri = data_uri;
-  }
-}
-
 bool PostgresSelfHosted::get_self_hosting_active() const
 {
   return m_port != 0;
@@ -175,9 +167,9 @@ Backend::InitErrors PostgresSelfHosted::initialize(const SlotProgress& slot_prog
 {
   m_network_shared = network_shared;
 
-  if(m_self_hosting_data_uri.empty())
+  if(m_database_directory_uri.empty())
   {
-    std::cerr << "PostgresSelfHosted::initialize: m_self_hosting_data_uri is empty." << std::endl;
+    std::cerr << "PostgresSelfHosted::initialize: m_database_directory_uri is empty." << std::endl;
     return INITERROR_OTHER;
   }
 
@@ -188,34 +180,29 @@ Backend::InitErrors PostgresSelfHosted::initialize(const SlotProgress& slot_prog
   }
 
   //Get the filepath of the directory that we should create:
-  const std::string dbdir_uri = m_self_hosting_data_uri;
+  const std::string dbdir_uri = m_database_directory_uri;
   //std::cout << "debug: dbdir_uri=" << dbdir_uri << std::endl;
 
-  if(directory_exists_uri(dbdir_uri))
+  if(file_exists_uri(dbdir_uri))
     return INITERROR_DIRECTORY_ALREADY_EXISTS;
 
   const std::string dbdir = Glib::filename_from_uri(dbdir_uri);
   //std::cout << "debug: dbdir=" << dbdir << std::endl;
   g_assert(!dbdir.empty());
 
-  //0770 means "this user and his group can read and write and "execute" (meaning add sub-files) this non-executable file".
-  //The 0 prefix means that this is octal.
-  int mkdir_succeeded = g_mkdir_with_parents(dbdir.c_str(), 0770);
-  if(mkdir_succeeded == -1)
+  const bool dbdir_created = create_directory_filepath(dbdir);
+  if(!dbdir_created)
   {
-    std::cerr << "Error from g_mkdir_with_parents() while trying to create directory: " << dbdir << std::endl;
-    perror("Error from g_mkdir_with_parents");
+    std::cerr << "Couldn't create directory: " << dbdir << std::endl;
 
     return INITERROR_COULD_NOT_CREATE_DIRECTORY;
   }
 
   //Create the config directory:
-  const std::string dbdir_config = dbdir + "/config";
-  mkdir_succeeded = g_mkdir_with_parents(dbdir_config.c_str(), 0770);
-  if(mkdir_succeeded == -1)
+  const std::string dbdir_config = get_self_hosting_config_path(true /* create */);
+  if(dbdir_config.empty())
   {
-    std::cerr << "Error from g_mkdir_with_parents() while trying to create directory: " << dbdir_config << std::endl;
-    perror("Error from g_mkdir_with_parents");
+    std::cerr << "Couldn't create the config directory: " << dbdir << std::endl;
 
     return INITERROR_COULD_NOT_CREATE_DIRECTORY;
   }
@@ -224,10 +211,13 @@ Backend::InitErrors PostgresSelfHosted::initialize(const SlotProgress& slot_prog
   set_network_shared(slot_progress, m_network_shared); //Creates pg_hba.conf and pg_ident.conf
 
   //Check that there is not an existing data directory:
-  const std::string dbdir_data = dbdir + "/data";
-  mkdir_succeeded = g_mkdir_with_parents(dbdir_data.c_str(), 0770);
-  g_assert(mkdir_succeeded != -1);
+  const std::string dbdir_data = get_self_hosting_data_path(true /* create */);
+  if(dbdir_data.empty())
+  {
+    std::cerr << "Couldn't create the data directory: " << dbdir << std::endl;
 
+    return INITERROR_COULD_NOT_CREATE_DIRECTORY;
+  }
 
   // initdb creates a new postgres database cluster:
 
@@ -385,7 +375,7 @@ float PostgresSelfHosted::get_postgresql_utils_version_as_number(const SlotProgr
 }
 
 
-bool PostgresSelfHosted::startup(const SlotProgress& slot_progress, bool network_shared)
+Backend::StartupErrors PostgresSelfHosted::startup(const SlotProgress& slot_progress, bool network_shared)
 {
   m_network_shared = network_shared;
 
@@ -394,40 +384,51 @@ bool PostgresSelfHosted::startup(const SlotProgress& slot_progress, bool network
   //g_assert(!get_self_hosting_active());
 
   if(get_self_hosting_active())
-    return true; //Just do it once.
-
-  const std::string dbdir_uri = m_self_hosting_data_uri;
-
-  if(!(directory_exists_uri(dbdir_uri)))
   {
-    //TODO: Use a return enum or exception so we can tell the user about this:
-    std::cerr << "ConnectionPool::create_self_hosting(): The data directory could not be found: " << dbdir_uri << std::endl;
-    return false;
+    std::cerr << G_STRFUNC << ": Already started." << std::endl;
+    return STARTUPERROR_NONE; //Just do it once.
   }
+  
+  const std::string dbdir_uri = m_database_directory_uri;
 
-  //Attempt to ensure that the config files are correct:
-  set_network_shared(slot_progress, m_network_shared); //Creates pg_hba.conf and pg_ident.conf
+  if(!(file_exists_uri(dbdir_uri)))
+  {
+    std::cerr << "ConnectionPool::create_self_hosting(): The directory could not be found: " << dbdir_uri << std::endl;
+    return STARTUPERROR_FAILED_UNKNOWN_REASON;
+  }
 
   const std::string dbdir = Glib::filename_from_uri(dbdir_uri);
   g_assert(!dbdir.empty());
 
-  const std::string dbdir_data = Glib::build_filename(dbdir, "data");
-  const std::string dbdir_data_uri = Glib::filename_to_uri(dbdir_data);
-  if(!(directory_exists_uri(dbdir_data_uri)))
+  const std::string dbdir_data = Glib::build_filename(dbdir, FILENAME_DATA);
+  const Glib::ustring dbdir_data_uri = Glib::filename_to_uri(dbdir_data);
+  if(!(file_exists_uri(dbdir_data_uri)))
   {
-    //TODO: Use a return enum or exception so we can tell the user about this:
-    std::cerr << "ConnectionPool::create_self_hosting(): The data sub-directory could not be found." << dbdir_data_uri << std::endl;
-    return false;
+    const std::string dbdir_backup = Glib::build_filename(dbdir, FILENAME_BACKUP);
+    const Glib::ustring dbdir_backup_uri = Glib::filename_to_uri(dbdir_backup);
+    if(file_exists_uri(dbdir_backup_uri))
+    {
+      std::cerr << G_STRFUNC << ": There is no data, but there is backup data." << std::endl;
+      //Let the caller convert the backup to real data and then try again:
+      return STARTUPERROR_FAILED_NO_DATA_HAS_BACKUP_DATA;
+    }
+    else
+    {
+      std::cerr << "ConnectionPool::create_self_hosting(): The data sub-directory could not be found." << dbdir_data_uri << std::endl;
+      return STARTUPERROR_FAILED_NO_DATA;
+    }
   }
+  
+  //Attempt to ensure that the config files are correct:
+  set_network_shared(slot_progress, m_network_shared); //Creates pg_hba.conf and pg_ident.conf
 
 
   const int available_port = discover_first_free_port(PORT_POSTGRESQL_SELF_HOSTED_START, PORT_POSTGRESQL_SELF_HOSTED_END);
   //std::cout << "ConnectionPool::create_self_hosting():() : debug: Available port for self-hosting: " << available_port << std::endl;
   if(available_port == 0)
   {
-    //TODO: Use a return enum or exception so we can tell the user about this:
     std::cerr << "ConnectionPool::create_self_hosting(): No port was available between " << PORT_POSTGRESQL_SELF_HOSTED_START << " and " << PORT_POSTGRESQL_SELF_HOSTED_END << std::endl;
-    return false;
+    return STARTUPERROR_FAILED_UNKNOWN_REASON;
   }
 
   //TODO: Performance:
@@ -464,12 +465,12 @@ bool PostgresSelfHosted::startup(const SlotProgress& slot_progress, bool network
   if(!result)
   {
     std::cerr << "Error while attempting to self-host a database." << std::endl;
-    return false;
+    return STARTUPERROR_FAILED_UNKNOWN_REASON;
   }
 
   m_port = available_port; //Remember it for later.
 
-  return true;
+  return STARTUPERROR_NONE;
 }
 
 bool PostgresSelfHosted::cleanup(const SlotProgress& slot_progress)
@@ -481,11 +482,11 @@ bool PostgresSelfHosted::cleanup(const SlotProgress& slot_progress)
   if(!get_self_hosting_active())
     return true; //Don't try to stop it if we have not started it.
 
-  const std::string dbdir_uri = m_self_hosting_data_uri;
+  const std::string dbdir_uri = m_database_directory_uri;
   const std::string dbdir = Glib::filename_from_uri(dbdir_uri);
   g_assert(!dbdir.empty());
 
-  const std::string dbdir_data = Glib::build_filename(dbdir, "data");
+  const std::string dbdir_data = Glib::build_filename(dbdir, FILENAME_DATA);
 
 
   // TODO: Detect other instances on the same computer, and use a different port number,
@@ -527,7 +528,7 @@ bool PostgresSelfHosted::set_network_shared(const SlotProgress& slot_progress, b
 
   m_network_shared = network_shared;
 
-  const std::string dbdir_uri = m_self_hosting_data_uri;
+  const std::string dbdir_uri = m_database_directory_uri;
   const std::string dbdir = Glib::filename_from_uri(dbdir_uri);
 
   const std::string dbdir_uri_config = dbdir_uri + "/config";
@@ -777,15 +778,6 @@ bool PostgresSelfHosted::create_text_file(const std::string& file_uri, const std
   return true; //Success.
 }
 
-bool PostgresSelfHosted::directory_exists_uri(const std::string& uri)
-{
-  if(uri.empty())
-    return false;
-
-  const Glib::RefPtr<Gio::File> file = Gio::File::create_for_uri(uri);
-  return file && file->query_exists();
-}
-
 } // namespace ConnectionPoolBackends
 
 } // namespcae Glom
diff --git a/glom/libglom/connectionpool_backends/postgres_self.h b/glom/libglom/connectionpool_backends/postgres_self.h
index 6568ce8..0c1b809 100644
--- a/glom/libglom/connectionpool_backends/postgres_self.h
+++ b/glom/libglom/connectionpool_backends/postgres_self.h
@@ -36,11 +36,6 @@ class PostgresSelfHosted : public Postgres
 public:
   PostgresSelfHosted();
 
-  /** This specifies that Glom should start its own database server instance
-   * for this database, using the database files stored at the specified uri.
-   */
-  void set_self_hosting_data_uri(const std::string& data_uri);
-
   /** Return whether the self-hosted server is currently running.
    *
    * @result True if it is running, and false otherwise.
@@ -62,7 +57,7 @@ public:
 private:
   virtual InitErrors initialize(const SlotProgress& slot_progress, const Glib::ustring& initial_username, const Glib::ustring& password, bool network_shared = false);
 
-  virtual bool startup(const SlotProgress& slot_progress, bool network_shared = false);
+  virtual StartupErrors startup(const SlotProgress& slot_progress, bool network_shared = false);
   virtual bool cleanup(const SlotProgress& slot_progress);
   virtual bool set_network_shared(const SlotProgress& slot_progress, bool network_shared = true);
 
@@ -78,9 +73,6 @@ private:
 
   static bool create_text_file(const std::string& file_uri, const std::string& contents);
 
-  //bool directory_exists_filepath(const std::string& filepath);
-  bool directory_exists_uri(const std::string& uri);
-
   /** Run the command-line with the --version option to discover what version
    * of PostgreSQL is installed, so we can use the appropriate configuration
    * options when self-hosting.
@@ -89,8 +81,6 @@ private:
 
   float get_postgresql_utils_version_as_number(const SlotProgress& slot_progress);
 
-  std::string m_self_hosting_data_uri;
-
   bool m_network_shared;
 };
 
diff --git a/glom/libglom/connectionpool_backends/sqlite.cc b/glom/libglom/connectionpool_backends/sqlite.cc
index 0d95ee5..712b9be 100644
--- a/glom/libglom/connectionpool_backends/sqlite.cc
+++ b/glom/libglom/connectionpool_backends/sqlite.cc
@@ -34,16 +34,6 @@ Sqlite::Sqlite()
 {
 }
 
-void Sqlite::set_database_directory_uri(const std::string& directory_uri)
-{
-  m_database_directory_uri = directory_uri;
-}
-
-const std::string& Sqlite::get_database_directory_uri() const
-{
-  return m_database_directory_uri;
-}
-
 Glib::RefPtr<Gnome::Gda::Connection> Sqlite::connect(const Glib::ustring& database, const Glib::ustring& username, const Glib::ustring& password, std::auto_ptr<ExceptionConnection>& error)
 {
   Glib::RefPtr<Gnome::Gda::Connection> connection;
@@ -397,12 +387,19 @@ bool Sqlite::change_columns(const Glib::RefPtr<Gnome::Gda::Connection>& connecti
   return recreate_table(connection, table_name, type_vec_strings(), type_vec_const_fields(), fields_changed, error);
 }
 
-bool Sqlite::save_backup(const SlotProgress& slot_progress, const std::string& filepath_output, const Glib::ustring& username, const Glib::ustring& password, const Glib::ustring& database_name)
+bool Sqlite::save_backup(const SlotProgress& slot_progress, const Glib::ustring& username, const Glib::ustring& password, const Glib::ustring& database_name)
 {
   //TODO:
   std::cerr << G_STRFUNC << ": Not implemented.";
 }
 
+bool Sqlite::convert_backup(const SlotProgress& slot_progress, const std::string& base_directory, const Glib::ustring& username, const Glib::ustring& password)
+{
+  //TODO:
+  std::cerr << G_STRFUNC << ": Not implemented.";
+}
+
+
 } // namespace ConnectionPoolBackends
 
 } // namespace Glom
diff --git a/glom/libglom/connectionpool_backends/sqlite.h b/glom/libglom/connectionpool_backends/sqlite.h
index 2461714..06a86fe 100644
--- a/glom/libglom/connectionpool_backends/sqlite.h
+++ b/glom/libglom/connectionpool_backends/sqlite.h
@@ -37,9 +37,6 @@ class Sqlite : public Backend
 public:
   Sqlite();
 
-  void set_database_directory_uri(const std::string& directory_uri);
-  const std::string& get_database_directory_uri() const;
-
 private:
   virtual Field::sql_format get_sql_format() const { return Field::SQL_FORMAT_SQLITE; }
   virtual bool supports_remote_access() const { return false; }
@@ -65,10 +62,8 @@ private:
    */
   virtual bool create_database(const Glib::ustring& database_name, const Glib::ustring& username, const Glib::ustring& password, std::auto_ptr<Glib::Error>& error);
   
-  virtual bool save_backup(const SlotProgress& slot_progress, const std::string& filepath_output, const Glib::ustring& username, const Glib::ustring& password, const Glib::ustring& database_name);
-
-private:
-  std::string m_database_directory_uri;
+  virtual bool save_backup(const SlotProgress& slot_progress, const Glib::ustring& username, const Glib::ustring& password, const Glib::ustring& database_name);
+  virtual bool convert_backup(const SlotProgress& slot_progress, const std::string& base_directory, const Glib::ustring& username, const Glib::ustring& password);
 };
 
 } //namespace ConnectionPoolBackends
diff --git a/glom/libglom/document/document.cc b/glom/libglom/document/document.cc
index 72b2ee0..05aafe9 100644
--- a/glom/libglom/document/document.cc
+++ b/glom/libglom/document/document.cc
@@ -135,6 +135,7 @@ namespace Glom
 
 #define GLOM_ATTRIBUTE_FORMAT_VERSION "format_version"
 #define GLOM_ATTRIBUTE_IS_EXAMPLE "is_example"
+#define GLOM_ATTRIBUTE_IS_BACKUP "is_backup"
 #define GLOM_ATTRIBUTE_CONNECTION_DATABASE_TITLE "database_title"
 #define GLOM_NODE_STARTUP_SCRIPT "startup_script"
 #define GLOM_ATTRIBUTE_TRANSLATION_ORIGINAL_LOCALE "translation_original_locale"
@@ -253,6 +254,7 @@ Document::Document()
   m_block_modified_set(false),
   m_allow_auto_save(true), //Save all changes immediately, by default.
   m_is_example(false),
+  m_is_backup(false),
   m_opened_from_browse(false)
 {
   m_document_format_version = get_latest_known_document_format_version(); //Default to this for new documents.
@@ -2421,8 +2423,9 @@ bool Document::load_after(int& failure_code)
         failure_code = LOAD_FAILURE_CODE_FILE_VERSION_TOO_NEW;
         return false;
       }
-      
+
       m_is_example = get_node_attribute_value_as_bool(nodeRoot, GLOM_ATTRIBUTE_IS_EXAMPLE);
+      m_is_backup = get_node_attribute_value_as_bool(nodeRoot, GLOM_ATTRIBUTE_IS_BACKUP);
       m_database_title = get_node_attribute_value(nodeRoot, GLOM_ATTRIBUTE_CONNECTION_DATABASE_TITLE);
       
       m_startup_script = get_child_text_node(nodeRoot, GLOM_NODE_STARTUP_SCRIPT);
@@ -3367,6 +3370,7 @@ bool Document::save_before()
     set_node_attribute_value_as_decimal(nodeRoot, GLOM_ATTRIBUTE_FORMAT_VERSION, m_document_format_version);
 
     set_node_attribute_value_as_bool(nodeRoot, GLOM_ATTRIBUTE_IS_EXAMPLE, m_is_example);
+    set_node_attribute_value_as_bool(nodeRoot, GLOM_ATTRIBUTE_IS_BACKUP, m_is_backup);
     set_node_attribute_value(nodeRoot, GLOM_ATTRIBUTE_CONNECTION_DATABASE_TITLE, m_database_title);
     
     set_child_text_node(nodeRoot, GLOM_NODE_STARTUP_SCRIPT, m_startup_script);
@@ -4026,6 +4030,21 @@ void Document::set_is_example_file(bool value)
 }
 
 
+bool Document::get_is_backup_file() const
+{
+  return m_is_backup;
+}
+
+void Document::set_is_backup_file(bool value)
+{
+  if(m_is_backup != value)
+  {
+    m_is_backup = value;
+    set_modified();
+  }
+}
+
+
 void Document::set_translation_original_locale(const Glib::ustring& locale)
 {
   m_translation_original_locale = locale;
diff --git a/glom/libglom/document/document.h b/glom/libglom/document/document.h
index f60861a..f941a67 100644
--- a/glom/libglom/document/document.h
+++ b/glom/libglom/document/document.h
@@ -81,6 +81,9 @@ public:
 
   bool get_is_example_file() const;
   void set_is_example_file(bool value = true);
+  
+  bool get_is_backup_file() const;
+  void set_is_backup_file(bool value = true);
 
   /* Get version of the document format used for this document.
    *  This can increase when the file has been re-saved.
@@ -618,6 +621,7 @@ private:
   bool m_block_modified_set;
   bool m_allow_auto_save;
   bool m_is_example;
+  bool m_is_backup;
   guint m_document_format_version;
 
   bool m_opened_from_browse;
diff --git a/tests/test_selfhosting_new_empty.cc b/tests/test_selfhosting_new_empty.cc
index 562c630..9917504 100644
--- a/tests/test_selfhosting_new_empty.cc
+++ b/tests/test_selfhosting_new_empty.cc
@@ -169,7 +169,7 @@ int main()
   //Start self-hosting:
   //TODO: Let this happen automatically on first connection?
   const bool started = connection_pool->startup( sigc::ptr_fun(&on_startup_progress) );
-  g_assert(started);
+  g_assert(started == ConnectionPool::Backend::STARTUPERROR_NONE);
   
   const bool stopped = connection_pool->cleanup( sigc::ptr_fun(&on_cleanup_progress) );  
   g_assert(stopped);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]