[glom] glom_create_from_example: Support central-hosting too.



commit 0d2a94361d1ed353485adeb99c981a7ed48d94b2
Author: Murray Cumming <murrayc murrayc com>
Date:   Mon Oct 10 15:45:07 2011 +0200

    glom_create_from_example: Support central-hosting too.
    
    * glom/glom_create_from_example.cc: Allow a host name, port, and username to
    be specified, and the password to be entered on stdin.
    * glom/libglom/db_utils.[h|cc]: Added get_unused_database_name():
    * glom/frame_glom.cc: Added a TODO that we should use it here too instead of
    the similar code that is mixed up with the dialog code.
    * glom/libglom/connectionpool.h: Remove unused m_host member variable.

 ChangeLog                        |   11 ++++
 glom/frame_glom.cc               |    1 +
 glom/glom_create_from_example.cc |  104 ++++++++++++++++++++++++++++++++++----
 glom/libglom/connectionpool.h    |    2 +-
 glom/libglom/db_utils.cc         |   92 +++++++++++++++++++++++++++++----
 glom/libglom/db_utils.h          |    8 +++
 6 files changed, 194 insertions(+), 24 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 0d45589..23cd799 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
 2011-10-10  Murray Cumming  <murrayc murrayc com>
 
+	glom_create_from_example: Support central-hosting too.
+
+	* glom/glom_create_from_example.cc: Allow a host name, port, and username to 
+	be specified, and the password to be entered on stdin.
+	* glom/libglom/db_utils.[h|cc]: Added get_unused_database_name():
+	* glom/frame_glom.cc: Added a TODO that we should use it here too instead of 
+	the similar code that is mixed up with the dialog code.
+	* glom/libglom/connectionpool.h: Remove unused m_host member variable.
+
+2011-10-10  Murray Cumming  <murrayc murrayc com>
+
 	Added the glom_create_from_example command-line utility.
 
 	* Makefile_libglom.am:
diff --git a/glom/frame_glom.cc b/glom/frame_glom.cc
index a57b45d..76748b2 100644
--- a/glom/frame_glom.cc
+++ b/glom/frame_glom.cc
@@ -2012,6 +2012,7 @@ bool Frame_Glom::connection_request_password_and_choose_new_database_name()
   //std::cout << "debug: database_name to create=" << database_name << std::endl;
 
 
+  //TODO: Use DbUtils::get_unused_database_name() instead:
   bool keep_trying = true;
   size_t extra_num = 0;
   while(keep_trying)
diff --git a/glom/glom_create_from_example.cc b/glom/glom_create_from_example.cc
index cda42f0..6c96189 100644
--- a/glom/glom_create_from_example.cc
+++ b/glom/glom_create_from_example.cc
@@ -26,6 +26,7 @@
 #include <libglom/document/document.h>
 #include <libglom/connectionpool.h>
 #include <libglom/connectionpool_backends/postgres_self.h>
+#include <libglom/connectionpool_backends/postgres_central.h>
 #include <libglom/init.h>
 #include <libglom/privs.h>
 #include <libglom/db_utils.h>
@@ -46,11 +47,18 @@ public:
   std::string m_arg_filepath_dir_output;
   std::string m_arg_filepath_name_output;
   bool m_arg_version;
+  
+  //If not using self-hosting:
+  Glib::ustring m_arg_server_hostname;
+  double m_arg_server_port;
+  Glib::ustring m_arg_server_username;
+  Glib::ustring m_arg_server_password;
 };
 
 GlomCreateOptionGroup::GlomCreateOptionGroup()
 : Glib::OptionGroup("glom_create_from_example", _("Glom options"), _("Command-line options")),
-  m_arg_version(false)
+  m_arg_version(false),
+  m_arg_server_port(0)
 {
   Glib::OptionEntry entry;
   entry.set_long_name("input");
@@ -61,13 +69,13 @@ GlomCreateOptionGroup::GlomCreateOptionGroup()
   Glib::OptionEntry entry2;
   entry2.set_long_name("output-path");
   entry2.set_short_name('o');
-  entry2.set_description(_("The directory in which to save the created .glom file, or sub-directory if necessary, such as /home/someuser/"));
+  entry2.set_description(_("The directory in which to save the created .glom file, or sub-directory if necessary, such as /home/someuser/ ."));
   add_entry_filename(entry2, m_arg_filepath_dir_output);
   
   Glib::OptionEntry entry3;
   entry3.set_long_name("output-name");
   entry3.set_short_name('n');
-  entry3.set_description(_("The name for the created .glom file, such as something.glom"));
+  entry3.set_description(_("The name for the created .glom file, such as something.glom ."));
   add_entry_filename(entry3, m_arg_filepath_name_output);
 
   Glib::OptionEntry entry_version;
@@ -75,6 +83,25 @@ GlomCreateOptionGroup::GlomCreateOptionGroup()
   entry_version.set_short_name('V');
   entry_version.set_description(_("The version of this application."));
   add_entry(entry_version, m_arg_version);
+  
+  
+  Glib::OptionEntry entry4;
+  entry4.set_long_name("server-hostname");
+  entry4.set_short_name('h');
+  entry4.set_description(_("The hostname of the PostgreSQL server, such as localhost."));
+  add_entry(entry4, m_arg_server_hostname);
+  
+  Glib::OptionEntry entry5;
+  entry5.set_long_name("server-port");
+  entry5.set_short_name('p');
+  entry5.set_description(_("The port of the PostgreSQL server, such as 5434."));
+  add_entry(entry5, m_arg_server_port);
+  
+  Glib::OptionEntry entry6;
+  entry6.set_long_name("server-username");
+  entry6.set_short_name('u');
+  entry6.set_description(_("The username for the PostgreSQL server."));
+  add_entry(entry6, m_arg_server_username);
 }
 
 static void on_initialize_progress()
@@ -336,7 +363,19 @@ int main(int argc, char* argv[])
 
   document.set_file_uri(file_uri);
 
-  document.set_hosting_mode(Glom::Document::HOSTING_MODE_POSTGRES_SELF);
+
+  const bool self_hosting = group.m_arg_server_hostname.empty();
+  if(self_hosting)
+  {
+    std::cout << "Using self-hosting instead of a central PostgreSQL server." << std::endl;
+    document.set_hosting_mode(Glom::Document::HOSTING_MODE_POSTGRES_SELF);
+  }
+  else
+  {
+    std::cout << "Using the PostgreSQL server with host: " << group.m_arg_server_hostname << std::endl;
+    document.set_hosting_mode(Glom::Document::HOSTING_MODE_POSTGRES_CENTRAL);
+  }
+   
   document.set_is_example_file(false);
   document.set_network_shared(false);
   const bool saved = document.save();
@@ -346,12 +385,56 @@ int main(int argc, char* argv[])
   connection_pool->setup_from_document(&document);
 
   //We must specify a default username and password:
-  Glib::ustring password;
-  const Glib::ustring user = Glom::Privs::get_default_developer_user_name(password);
-  connection_pool->set_user(user);
-  connection_pool->set_password(password);
-
-  //Create the self-hosting files:
+  if(self_hosting)
+  {
+    Glib::ustring password;
+    const Glib::ustring user = Glom::Privs::get_default_developer_user_name(password);
+    connection_pool->set_user(user);
+    connection_pool->set_password(password);
+  }
+  else
+  {
+    //Get the password from stdin.
+    //This is not a command-line option because then it would appear in logs.
+    //Other command-line utilities such as psql don't do this either.
+    //TODO: Support alternatives such as using a file.
+    const Glib::ustring prompt = Glib::ustring::compose(
+      _("Please enter the PostgreSQL server's password for the user %1: "), group.m_arg_server_username);
+    const char* password = ::getpass(prompt.c_str());
+
+    //Central hosting:
+    connection_pool->set_user(group.m_arg_server_username);
+    connection_pool->set_password(password); //TODO: Take this from stdin instead.
+    
+    Glom::ConnectionPool::Backend* backend = connection_pool->get_backend();
+    Glom::ConnectionPoolBackends::PostgresCentralHosted* central = 
+      dynamic_cast<Glom::ConnectionPoolBackends::PostgresCentralHosted*>(backend);
+    g_assert(central);
+
+    central->set_host(group.m_arg_server_hostname);
+    
+    if(group.m_arg_server_port)
+    {
+      central->set_port(group.m_arg_server_port);
+      central->set_try_other_ports(false);
+    }
+    else
+    {
+      //Try all ports:
+      central->set_try_other_ports(false);
+    }
+    
+    const Glib::ustring database_name =
+      Glom::DbUtils::get_unused_database_name(document.get_connection_database());
+    if(database_name.empty())
+    {
+      std::cerr << G_STRFUNC << ": Could not find an unused database name" << std::endl;
+    }
+    else
+      document.set_connection_database(database_name);
+  }
+        
+  //Startup. For instance, create the self-hosting files if necessary:
   const Glom::ConnectionPool::InitErrors initialized_errors =
     connection_pool->initialize( sigc::ptr_fun(&on_initialize_progress) );
   g_assert(initialized_errors == Glom::ConnectionPool::Backend::INITERROR_NONE);
@@ -370,7 +453,6 @@ int main(int argc, char* argv[])
   if(!recreated)
     cleanup();
   g_assert(recreated);
-  
 
   //Tell the user where the file is:
   std::string output_path_used;
diff --git a/glom/libglom/connectionpool.h b/glom/libglom/connectionpool.h
index 5678ec6..2cc2181 100644
--- a/glom/libglom/connectionpool.h
+++ b/glom/libglom/connectionpool.h
@@ -299,7 +299,7 @@ private:
   Glib::RefPtr<Gnome::Gda::Connection> m_refGdaConnection;
   guint m_sharedconnection_refcount;
   bool m_ready_to_connect;
-  Glib::ustring m_host, m_user, m_password, m_database;
+  Glib::ustring m_user, m_password, m_database;
 
   FieldTypes* m_pFieldTypes;
   bool m_show_debug_output, m_auto_server_shutdown;
diff --git a/glom/libglom/db_utils.cc b/glom/libglom/db_utils.cc
index fb93f6c..f71de1e 100644
--- a/glom/libglom/db_utils.cc
+++ b/glom/libglom/db_utils.cc
@@ -21,6 +21,7 @@
 #include <libglom/db_utils.h>
 #include <libglom/connectionpool.h>
 #include <libglom/data_structure/glomconversions.h>
+#include <libglom/connectionpool_backends/postgres_central.h>
 #include <libglom/standard_table_prefs_fields.h>
 #include <libglom/privs.h>
 #include <libglom/data_structure/parameternamegenerator.h>
@@ -171,6 +172,15 @@ bool create_database(Document* document, const Glib::ustring& database_name, con
     }
 
     progress();
+    
+    //Save the port, if appropriate, so the document can be used to connect again:
+    Glom::ConnectionPool::Backend* backend = connection_pool->get_backend();
+    Glom::ConnectionPoolBackends::PostgresCentralHosted* central = 
+      dynamic_cast<Glom::ConnectionPoolBackends::PostgresCentralHosted*>(backend);
+    if(central)
+    {
+      document->set_connection_port( central->get_port() );
+    }
 
     return true;
   }
@@ -183,7 +193,7 @@ bool create_database(Document* document, const Glib::ustring& database_name, con
 
 bool recreate_database_from_document(Document* document, const sigc::slot<void>& progress)
 {
- ConnectionPool* connection_pool = ConnectionPool::get_instance();
+  ConnectionPool* connection_pool = ConnectionPool::get_instance();
   if(!connection_pool)
     return false; //Impossible anyway.
 
@@ -197,23 +207,23 @@ bool recreate_database_from_document(Document* document, const sigc::slot<void>&
   {
     connection_pool->set_ready_to_connect(); //This has succeeded already.
     sharedptr<SharedConnection> sharedconnection = connection_pool->connect();
-      g_warning("Application::recreate_database(): Failed because database exists already.");
+    std::cerr << G_STRFUNC << ": Failed because database exists already." << std::endl;
 
-      return false; //Connection to the database succeeded, because no exception was thrown. so the database exists already.
+    return false; //Connection to the database succeeded, because no exception was thrown. so the database exists already.
   }
   catch(const ExceptionConnection& ex)
   {
-      if(ex.get_failure_type() == ExceptionConnection::FAILURE_NO_SERVER)
-      {
-        g_warning("Application::recreate_database(): Failed because connection to server failed even without specifying a database.");
-        return false;
-      }
+    if(ex.get_failure_type() == ExceptionConnection::FAILURE_NO_SERVER)
+    {
+      std::cerr << G_STRFUNC << ": Application::recreate_database(): Failed because connection to server failed even without specifying a database." << std::endl;
+      return false;
+    }
 
     //Otherwise continue, because we _expected_ connect() to fail if the db does not exist yet.
   }
 
 
-  //Create the database: (This will show a connection dialog)
+  //Create the database:
   progress();
   connection_pool->set_database( Glib::ustring() );
   const bool db_created = create_database(document, db_name, document->get_database_title(), progress);
@@ -235,7 +245,7 @@ bool recreate_database_from_document(Document* document, const sigc::slot<void>&
   }
   catch(const ExceptionConnection& ex)
   {
-    g_warning("Application::recreate_database(): Failed to connect to the newly-created database.");
+    std::cerr << G_STRFUNC << ": Failed to connect to the newly-created database." << std::endl;
     return false;
   }
 
@@ -256,7 +266,7 @@ bool recreate_database_from_document(Document* document, const sigc::slot<void>&
     progress();
     if(!table_creation_succeeded)
     {
-      g_warning("Application::recreate_database(): CREATE TABLE failed with the newly-created database.");
+      std::cerr << G_STRFUNC << ": CREATE TABLE failed with the newly-created database." << std::endl;
       return false;
     }
   }
@@ -284,7 +294,7 @@ bool recreate_database_from_document(Document* document, const sigc::slot<void>&
 
       if(!table_insert_succeeded)
       {
-        g_warning("Application::recreate_database(): INSERT of example data failed with the newly-created database.");
+        std::cerr << G_STRFUNC << ": INSERT of example data failed with the newly-created database." << std::endl;
         return false;
       }
     //}
@@ -1665,6 +1675,64 @@ bool layout_field_should_have_navigation(const Glib::ustring& table_name, const
   return field_used_in_relationship_to_one || field_is_related_primary_key;
 }
 
+Glib::ustring get_unused_database_name(const Glib::ustring& base_name)
+{ 
+  Glom::ConnectionPool* connection_pool = Glom::ConnectionPool::get_instance();
+  if(!connection_pool)
+    return Glib::ustring();
+
+  bool keep_trying = true;
+  size_t extra_num = 0;
+  while(keep_trying)
+  {
+    Glib::ustring database_name_possible;
+    if(extra_num == 0)
+    {
+      //Try the original name first,
+      //removing any characters that are likely to cause problems when used in a SQL identifier name:
+      database_name_possible = Utils::trim_whitespace(base_name);
+      database_name_possible = Utils::string_replace(database_name_possible, "\"", "");
+      database_name_possible = Utils::string_replace(database_name_possible, "'", "");
+      database_name_possible = Utils::string_replace(database_name_possible, "\t", "");
+      database_name_possible = Utils::string_replace(database_name_possible, "\n", "");
+    }
+    else
+    {
+      //Create a new database name by appending a number to the original name:
+      const Glib::ustring pchExtraNum = Glib::ustring::compose("%1", extra_num);
+      database_name_possible = (base_name + pchExtraNum);
+    }
+    ++extra_num;
+    
+    connection_pool->set_database(database_name_possible);
+    connection_pool->set_ready_to_connect();
+
+    Glom::sharedptr<Glom::SharedConnection> connection;
+
+    try
+    {
+      connection = ConnectionPool::get_and_connect();
+    }
+    catch(const ExceptionConnection& ex)
+    {
+      if(ex.get_failure_type() == ExceptionConnection::FAILURE_NO_SERVER)
+      {
+        //We couldn't even connect to the server,
+        //regardless of what database we try to connect to:
+        std::cerr << G_STRFUNC << ": Could not connect to the server." << std::endl;
+        return Glib::ustring();
+      }
+      else
+      {
+        //We assume that the connection failed because the database does not exist.
+        std::cout << "debug: " << G_STRFUNC << ": unused database name successfully found: " << database_name_possible << std::endl;
+        return database_name_possible;
+      }
+    }
+  }
+  
+  return Glib::ustring();    
+}
 
 } //namespace DbUtils
 
diff --git a/glom/libglom/db_utils.h b/glom/libglom/db_utils.h
index a3439df..bb84037 100644
--- a/glom/libglom/db_utils.h
+++ b/glom/libglom/db_utils.h
@@ -117,6 +117,14 @@ void layout_item_fill_field_details(Document* document, const Glib::ustring& par
  */
 bool layout_field_should_have_navigation(const Glib::ustring& table_name, const sharedptr<const LayoutItem_Field>& layout_item, const Document* document, sharedptr<Relationship>& field_used_in_relationship_to_one);
 
+/** Discover a database name that is not yet used.
+ * This assumes that all other connection details are correctly set.
+ *
+ * @param base_name The wished-for name, to be modified until an unused name is found.
+ * @result A database name that does not yet exist on the server.
+ */
+Glib::ustring get_unused_database_name(const Glib::ustring& base_name);
+
 } //namespace DbUtils
 
 } //namespace Glom



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