glom r1843 - in trunk: . glom glom/libglom glom/libglom/connectionpool_backends glom/mode_design/fields



Author: arminb
Date: Mon Jan 12 19:13:05 2009
New Revision: 1843
URL: http://svn.gnome.org/viewvc/glom?rev=1843&view=rev

Log:
2009-01-12  Armin Burgmeier  <armin openismus com>

	* glom/libglom/connectionpool.h:
	* glom/libglom/connectionpool.cc: Added add_column(), change_column(),
	drop_column(), using a generic implementation via libgda's DDL API.

	* glom/libglom/connectionpool_backends/postgres.h:
	* glom/libglom/connectionpool_backends/postgres.cc:
	* glom/libglom/connectionpool_backends/Makefile.am: Added a base class
	for the two postgres backends, implementing the common functionality.

	* glom/libglom/connectionpool_backends/postgres_self.h:
	* glom/libglom/connectionpool_backends/postgres_central.h:
	* glom/libglom/connectionpool_backends/postgres_self.cc:
	* glom/libglom/connectionpool_backends/postgres_central.cc: Use the
	Postgres base class, instead of deriving directly from
	ConnectionPoolBackend.

	* glom/mode_design/fields/box_db_table_definition.h:
	* glom/mode_design/fields/box_db_table_definition.cc: Use the
	connectionpool add_column(), drop_column() and change_column()
	functions instead of using postgres-specific code from
	glom_postgres.cc. There is maybe a drawback concerning data conversion
	when changing column types, but I'll re-add the relevant code to the
	postgres connectionpool backend in the near future.

	* glom/glom_postgres.h:
	* glom/glom_postgres.cc: Removed postgres_add_column() and
	postgres_change_column_extras().

	* glom/main.cc: Adapt to
	check_postgres_gda_client_is_available_with_warning() now being in
	ConnectionPoolBackends::Postgres instead of
	ConnectionPoolBackends::PostgresCentral.


Added:
   trunk/glom/libglom/connectionpool_backends/postgres.cc
   trunk/glom/libglom/connectionpool_backends/postgres.h
Modified:
   trunk/ChangeLog
   trunk/glom/base_db.cc
   trunk/glom/base_db.h
   trunk/glom/glom_postgres.cc
   trunk/glom/glom_postgres.h
   trunk/glom/libglom/connectionpool.cc
   trunk/glom/libglom/connectionpool.h
   trunk/glom/libglom/connectionpool_backends/Makefile.am
   trunk/glom/libglom/connectionpool_backends/postgres_central.cc
   trunk/glom/libglom/connectionpool_backends/postgres_central.h
   trunk/glom/libglom/connectionpool_backends/postgres_self.cc
   trunk/glom/libglom/connectionpool_backends/postgres_self.h
   trunk/glom/main.cc
   trunk/glom/mode_design/fields/box_db_table_definition.cc
   trunk/glom/mode_design/fields/box_db_table_definition.h

Modified: trunk/glom/base_db.cc
==============================================================================
--- trunk/glom/base_db.cc	(original)
+++ trunk/glom/base_db.cc	Mon Jan 12 19:13:05 2009
@@ -1265,7 +1265,7 @@
     sharedptr<const Field> field = *iter;
     if(!get_field_exists_in_database(table_name, field->get_name()))
     {
-      const bool test = GlomPostgres::postgres_add_column(table_name, field);
+      const bool test = add_column(table_name, field, 0); /* TODO: parent_window */
       if(!test)
        return test;
     }
@@ -1274,6 +1274,167 @@
   return true;
 }
 
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+bool Base_DB::add_column(const Glib::ustring& table_name, const sharedptr<const Field>& field, Gtk::Window* parent_window) const
+{
+  ConnectionPool* connection_pool = ConnectionPool::get_instance();
+
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  try
+  {
+    connection_pool->add_column(table_name, field);
+  }
+  catch(const Glib::Error& ex)
+  {
+#else
+  std::auto_ptr<Glib::Error> error;
+  connection_pool->add_column(table_name, field, error);
+  if(error.get())
+  {
+    const Glib::Error& ex = *error;
+#endif
+    handle_error(ex);
+//    Gtk::MessageDialog window(*parent_window, Bakery::App_Gtk::util_bold_message(ex.what()), true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
+//    window.run();
+    return false;
+  }
+
+  return true;
+}
+
+bool Base_DB::drop_column(const Glib::ustring& table_name, const Glib::ustring& field_name, Gtk::Window* parent_window) const
+{
+  ConnectionPool* connection_pool = ConnectionPool::get_instance();
+
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  try
+  {
+    return connection_pool->drop_column(table_name, field_name);
+  }
+  catch(const Glib::Error& ex)
+  {
+#else
+  std::auto_ptr<Glib::Error> error;
+  connection_pool->add_column(table_name, field_name, error);
+  if(error.get())
+  {
+    const Glib::Error& ex = *error;
+#endif
+    handle_error(ex);
+//    Gtk::MessageDialog window(*parent_window, Bakery::App_Gtk::util_bold_message(ex.what()), true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
+//    window.run();
+    return false;
+  }
+
+  return true;
+}
+
+namespace
+{
+  // Check primary key and uniqueness constraints when changing a column
+  sharedptr<Field> check_field_change_constraints(const sharedptr<const Field>& field_old, const sharedptr<const Field>& field)
+  {
+    sharedptr<Field> result = glom_sharedptr_clone(field);
+    bool primary_key_was_set = false;
+    bool primary_key_was_unset = false;
+    if(field_old->get_primary_key() != field->get_primary_key())
+    {
+      //TODO: Check that there is only one primary key
+      //When unsetting a primary key, ask which one should replace it.
+
+      if(field->get_primary_key())
+      {
+        result->set_unique_key();
+        primary_key_was_set = true;
+      }
+      else
+      {
+        //Make sure the caller knows that a fields stop being unique when it
+        //stops being a primary key, because its uniqueness was just a
+        //side-effect of it being a primary key.
+        result->set_unique_key(false);
+        primary_key_was_unset = true;
+      }
+    }
+
+    if(field_old->get_unique_key() != field->get_unique_key())
+    {
+      if(!primary_key_was_unset && !field->get_unique_key())
+      {
+        if(field->get_primary_key())
+          result->set_unique_key();
+      }
+    }
+
+    return result;
+  }
+}
+
+sharedptr<Field> Base_DB::change_column(const Glib::ustring& table_name, const sharedptr<const Field>& field_old, const sharedptr<const Field>& field, Gtk::Window* parent_window) const
+{
+  ConnectionPool* connection_pool = ConnectionPool::get_instance();
+  sharedptr<Field> result = check_field_change_constraints(field_old, field);
+
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  try
+  {
+    connection_pool->change_column(table_name, field_old, result);
+  }
+  catch(const Glib::Error& ex)
+  {
+#else
+  std::auto_ptr<Glib::Error> error;
+  connection_pool->change_column(table_name, field_old, result, error);
+  if(error.get())
+  {
+    const Glib::Error& ex = *error;
+#endif
+    handle_error(ex);
+//    Gtk::MessageDialog window(*parent_window, Bakery::App_Gtk::util_bold_message(ex.what()), true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
+//    window.run();
+    return sharedptr<Field>();
+  }
+
+  return result;
+}
+
+bool Base_DB::change_columns(const Glib::ustring& table_name, const type_vecConstFields& old_fields, type_vecFields& fields, Gtk::Window* parent_window) const
+{
+  g_assert(old_fields.size() == fields.size());
+
+  type_vecConstFields pass_fields(fields.size());
+  for(unsigned int i = 0; i < fields.size(); ++i)
+  {
+    fields[i] = check_field_change_constraints(old_fields[i], fields[i]);
+    pass_fields[i] = fields[i];
+  }
+
+  ConnectionPool* connection_pool = ConnectionPool::get_instance();
+
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  try
+  {
+    connection_pool->change_columns(table_name, old_fields, pass_fields);
+  }
+  catch(const Glib::Error& ex)
+  {
+#else
+  std::auto_ptr<Glib::Error> error;
+  connection_pool->change_columns(table_name, old_fields, pass_fields, error);
+  if(error.get())
+  {
+    const Glib::Error& ex = *error;
+#endif
+    handle_error(ex);
+//    Gtk::MessageDialog window(*parent_window, Bakery::App_Gtk::util_bold_message(ex.what()), true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
+//    window.run();
+    return false;
+  }
+
+  return true;
+}
+#endif
+
 bool Base_DB::insert_example_data(const Glib::ustring& table_name) const
 {
   const Glib::ustring example_rows = get_document()->get_table_example_data(table_name);

Modified: trunk/glom/base_db.h
==============================================================================
--- trunk/glom/base_db.h	(original)
+++ trunk/glom/base_db.h	Mon Jan 12 19:13:05 2009
@@ -72,6 +72,7 @@
   virtual void load_from_document(); //View override
 
   typedef std::vector< sharedptr<Field> > type_vecFields;
+  typedef std::vector< sharedptr<const Field> > type_vecConstFields;
 
   static type_vecFields get_fields_for_table_from_database(const Glib::ustring& table_name, bool including_system_fields = false);
   static bool get_field_exists_in_database(const Glib::ustring& table_name, const Glib::ustring& field_name);
@@ -100,6 +101,18 @@
   /// Also saves the table information in the document:
   bool create_table_with_default_fields(const Glib::ustring& table_name);
 
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  // TODO: Should these functions update the document, so callers don't need
+  // to do it?
+  bool add_column(const Glib::ustring& table_name, const sharedptr<const Field>& field, Gtk::Window* parent_window) const;
+
+  bool drop_column(const Glib::ustring& table_name, const Glib::ustring& field_name, Gtk::Window* parent_window) const;
+
+  sharedptr<Field> change_column(const Glib::ustring& table_name, const sharedptr<const Field>& field_old, const sharedptr<const Field>& field, Gtk::Window* parent_window) const;
+
+  bool change_columns(const Glib::ustring& table_name, const type_vecConstFields& old_fields, type_vecFields& fields, Gtk::Window* parent_window) const;
+#endif
+
   bool insert_example_data(const Glib::ustring& table_name) const;
 
   typedef std::vector< sharedptr<LayoutItem_Field> > type_vecLayoutFields;

Modified: trunk/glom/glom_postgres.cc
==============================================================================
--- trunk/glom/glom_postgres.cc	(original)
+++ trunk/glom/glom_postgres.cc	Mon Jan 12 19:13:05 2009
@@ -23,185 +23,6 @@
 namespace Glom
 {
 
-bool GlomPostgres::postgres_add_column(const Glib::ustring& table_name, const sharedptr<const Field>& field, bool not_extras)
-{
-  sharedptr<Field> field_to_add = glom_sharedptr_clone(field);
-
-  Glib::RefPtr<Gnome::Gda::Column> field_info = field_to_add->get_field_info();
-  if((field_info->get_g_type() == G_TYPE_NONE) || (field_info->get_g_type() == GDA_TYPE_NULL))
-  {
-    field_info->set_g_type( Field::get_gda_type_for_glom_type(field_to_add->get_glom_type()) );
-    field_to_add->set_field_info(field_info);
-  }
-
-  const bool bTest = query_execute(  "ALTER TABLE \"" + table_name + "\" ADD \"" + field_to_add->get_name() + "\" " +  field_to_add->get_sql_type() );
-  if(!bTest)
-    std::cerr << "GlomPostgres::postgres_add_column(): ALTER TABLE failed." << std::endl;
-  else
-  {
-    if(not_extras)
-    {
-      //We must do this separately:
-      postgres_change_column_extras(table_name, field_to_add, field_to_add, true /* set them even though the fields are the same */);
-    }
-  }
-
-  return bTest;
-}
-
-sharedptr<Field> GlomPostgres::postgres_change_column_extras(const Glib::ustring& table_name, const sharedptr<const Field>& field_old, const sharedptr<const Field>& field, bool set_anyway)
-{
-  //Glib::RefPtr<Gnome::Gda::Column> field_info = field->get_field_info();
-  //Glib::RefPtr<Gnome::Gda::Column> field_info_old = field_old->get_field_info();
-
-  sharedptr<Field> result = glom_sharedptr_clone(field);
-
-  if(field->get_name() != field_old->get_name())
-  {
-     const bool test = query_execute( "ALTER TABLE \"" + table_name + "\" RENAME COLUMN \"" + field_old->get_name() + "\" TO \"" + field->get_name() + "\"" );
-     if(!test)
-     {
-       std::cerr << "GlomPostgres::postgres_change_column_extras(): ALTER TABLE failed." << std::endl;
-
-       handle_error();
-       return result;
-     }
-  }
-
-  bool primary_key_was_set = false;
-  bool primary_key_was_unset = false;
-  if(set_anyway || (field->get_primary_key() != field_old->get_primary_key()))
-  {
-     //TODO: Check that there is only one primary key.
-     //When unsetting a primary key, ask which one should replace it.
-
-     bool test = false;
-       
-     //TODO: Somehow discover whether these constraint names really exists, so we can be more robust in strange situations. This needs libgda 2, which has GDA_CONNECTION_SCHEMA_CONSTRAINTS.
-     //Postgres needs us to add/drop constraints explicitly when changing existing fields, though it can create them implicitly when creating the field:
-     if(field->get_primary_key())
-     {
-       //Set field as primary key :
-       test = query_execute( "ALTER TABLE \"" + table_name + "\" ADD PRIMARY KEY (\"" + field->get_name() + "\")");
-       if(!test)
-         std::cerr << "GlomPostgres::postgres_change_column_extras(): ALTER TABLE with ADD PRIMARY KEY failed." << std::endl;
-
-       primary_key_was_set = true;
-
-       //Tell the caller about a constraint:
-       result->set_unique_key(); //All primary keys are unique.
-     }
-     else
-     {
-       //Unset field as primary key:
-       test = query_execute( "ALTER TABLE \"" + table_name + "\" DROP CONSTRAINT \"" + table_name + "_pkey\"" );
-       if(!test)
-         std::cerr << "GlomPostgres::postgres_change_column_extras(): ALTER TABLE with DROP CONSTRAINT failed." << std::endl;
-
-       primary_key_was_unset = true;
-
-       //Make sure that the caller knows that a field stops being unique when it stops being a primary key, 
-       //because its uniqueness was just a side-effect of it being a primary key.
-       result->set_unique_key(false); //All primary keys are unique.
-     }
-
-     if(!test)
-     {
-       handle_error();
-       return result;
-     }
-
-     if(primary_key_was_set && field_old->get_unique_key())
-     {
-       //If the key already had a uniqueness constraint, without also being a primary key, remove that now, because it is superfluous and we will not expect it later:
-       const bool test = query_execute( "ALTER TABLE \"" + table_name + "\" DROP CONSTRAINT \"" + field->get_name() + "_key\"" );
-       if(!test)
-       {
-         std::cerr << "GlomPostgres::postgres_change_column_extras(): ALTER TABLE with DROP CONSTRAINT (2) failed." << std::endl;
-
-         handle_error();
-         return result;
-       }
-    }
-  }
-
-  if(set_anyway || (field->get_unique_key() != field_old->get_unique_key()))
-  {
-    Glib::RefPtr<Gnome::Gda::DataModel> datamodel;
-       
-    //Postgres needs us to add/drop constraints explicitly when changing existing fields, though it can create them implicitly when creating the field:
-    if(!primary_key_was_set && field->get_unique_key()) //Postgres automatically makes primary keys unique, so we do not need to do that separately if we have already made it a primary key
-    {
-      //Add uniqueness:
-      const bool test = query_execute( "ALTER TABLE \"" + table_name + "\" ADD CONSTRAINT \"" + field->get_name() + "_key\" UNIQUE (\"" + field->get_name() + "\")" );
-      if(!test)
-      {
-        std::cerr << "GlomPostgres::postgres_change_column_extras(): ALTER TABLE with ADD CONSTRAINT failed." << std::endl;
-
-        handle_error();
-        return result;
-      }
-    }
-    else if(!primary_key_was_unset && !field->get_unique_key()) //This would implicitly have removed the uniqueness constraint which was in the primary key constraint 
-    {      
-      if(field->get_primary_key())
-      {
-        //Primary keys must be unique:
-        result->set_unique_key();
-      }
-      else
-      {
-        //Remove uniqueness:
-        const bool test = query_execute( "ALTER TABLE \"" + table_name + "\" DROP CONSTRAINT \"" + field->get_name() + "_key\"" );
-        if(!test)
-        {
-          std::cerr << "GlomPostgres::postgres_change_column_extras(): ALTER TABLE with DROP CONSTRAINT (3) failed." << std::endl;
-
-          handle_error();
-          return result;
-        }
-      }
-    }
-
-
-    const Gnome::Gda::Value default_value = field->get_default_value();
-    const Gnome::Gda::Value default_value_old = field_old->get_default_value();
-
-    if(!field->get_auto_increment()) //Postgres auto-increment fields have special code as their default values.
-    {
-      if(set_anyway || (default_value != default_value_old))
-      {
-        const bool test = query_execute( "ALTER TABLE \"" + table_name + "\" ALTER COLUMN \""+ field->get_name() + "\" SET DEFAULT " + field->sql(field->get_default_value()) );
-        if(!test)
-        {
-          std::cerr << "GlomPostgres::postgres_change_column_extras(): ALTER TABLE with ALTER COLUMN failed." << std::endl;
-
-          handle_error();
-          return result;
-        }
-      }
-    }
-  }
-
-  /* This should have been dealt with by postgres_change_column_type(), because postgres uses a different ("serial") field type for auto-incrementing fields.
-  if(field_info->get_auto_increment() != field_info_old.get_auto_increment())
-  {
-
-  }
-  */
- 
-   /*
-    //If the not-nullness has changed:
-    if( set_anyway ||  (field->get_field_info().get_allow_null() != field_old->get_field_info().get_allow_null()) )
-    {
-      const Glib::ustring nullness = (field->get_field_info().get_allow_null() ? "NULL" : "NOT NULL");
-      query_execute(  "ALTER TABLE \"" + m_table_name + "\" ALTER COLUMN \"" + field->get_name() + "\"  SET " + nullness);
-    }
-  */ 
-
-  return result;
-}
-
 GlomPostgres::type_vecStrings GlomPostgres::pg_list_separate(const Glib::ustring& str)
 {
   //Remove the first { and the last }:

Modified: trunk/glom/glom_postgres.h
==============================================================================
--- trunk/glom/glom_postgres.h	(original)
+++ trunk/glom/glom_postgres.h	Mon Jan 12 19:13:05 2009
@@ -34,19 +34,6 @@
 {
 public:
 
-  /** @param not_extras If this is true, then do not set extra details, such as NOT NULL. You should do that later, when you are ready.
-   */
-  static bool postgres_add_column(const Glib::ustring& table_name, const sharedptr<const Field>& field, bool not_extras = false);
-
-  /**
-   * @param table_name The name of the table that will be affected.
-   * @param field_old The definition of the field that will be changed.
-   * @param field The new definition to give the field.
-   * @param set_anyway If this is true, then set the extra details even if @field_old has the same properties.
-   * @result The new field definition, with any necessary changes.
-   */
-  static sharedptr<Field> postgres_change_column_extras(const Glib::ustring& table_name, const sharedptr<const Field>& field_old, const sharedptr<const Field>& field, bool set_anyway = false);
-
 protected:
   //Utility functions to help with the odd formats of postgres internal catalog fields:
   static type_vecStrings pg_list_separate(const Glib::ustring& str);

Modified: trunk/glom/libglom/connectionpool.cc
==============================================================================
--- trunk/glom/libglom/connectionpool.cc	(original)
+++ trunk/glom/libglom/connectionpool.cc	Mon Jan 12 19:13:05 2009
@@ -234,6 +234,206 @@
 void ConnectionPoolBackend::cleanup(Gtk::Window* /* parent_window */)
 {}
 
+namespace
+{
+  bool set_server_operation_value(const Glib::RefPtr<Gnome::Gda::ServerOperation>& operation, const Glib::ustring& path, const Glib::ustring& value, std::auto_ptr<Glib::Error>& error)
+  {
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+    try
+    {
+      operation->set_value_at(path, value);
+      return true;
+    }
+    catch(const Glib::Error& ex)
+    {
+      error.reset(new Glib::Error(ex));
+      return false;
+    }
+#else
+    operation->set_value_at(path, value, error);
+    if(error.get()) return false;
+    return true;
+#endif
+  }
+
+  Glib::RefPtr<Gnome::Gda::ServerOperation> create_server_operation(const Glib::RefPtr<Gnome::Gda::ServerProvider>& provider, const Glib::RefPtr<Gnome::Gda::Connection>& connection, Gnome::Gda::ServerOperationType type, std::auto_ptr<Glib::Error>& error)
+  {
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+    try
+    {
+      return provider->create_operation(connection, type, Gnome::Gda::Set::create());
+    }
+    catch(const Glib::Error& ex)
+    {
+      error.reset(new Glib::Error(ex));
+      return Glib::RefPtr<Gnome::Gda::ServerOperation>();
+    }
+#else
+    return provider->create_operation(connection, type, Gnome::Gda::Set::create(), error);
+#endif
+  }
+
+  bool perform_server_operation(const Glib::RefPtr<Gnome::Gda::ServerProvider>& provider, const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::RefPtr<Gnome::Gda::ServerOperation>& operation, std::auto_ptr<Glib::Error>& error)
+  {
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+    try
+    {
+      provider->perform_operation(connection, operation);
+      return true;
+    }
+    catch(const Glib::Error& ex)
+    {
+      error.reset(new Glib::Error(ex));
+      return false;
+    }
+#else
+    provider->perform_operation(connection, operation, error);
+    if(error.get()) return false;
+    return true;
+#endif
+  }
+}
+
+bool ConnectionPoolBackend::begin_transaction(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& name, Gnome::Gda::TransactionIsolation level, std::auto_ptr<Glib::Error>& error)
+{
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  try
+  {
+    return connection->begin_transaction(name, level);
+  }
+  catch(const Glib::Error& ex)
+  {
+    error.reset(new Glib::Error(ex));
+    return false;
+  }
+#else
+  return connection->begin_transaction(name, level, error);
+#endif
+}
+
+bool ConnectionPoolBackend::commit_transaction(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& name, std::auto_ptr<Glib::Error>& error)
+{
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  try
+  {
+    return connection->commit_transaction(name);
+  }
+  catch(const Glib::Error& ex)
+  {
+    error.reset(new Glib::Error(ex));
+    return false;
+  }
+#else
+  return connection->commit_transaction(name, error);
+#endif
+}
+
+bool ConnectionPoolBackend::rollback_transaction(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& name, std::auto_ptr<Glib::Error>& error)
+{
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  try
+  {
+    return connection->rollback_transaction(name);
+  }
+  catch(const Glib::Error& ex)
+  {
+    error.reset(new Glib::Error(ex));
+    return false;
+  }
+#else
+  return connection->rollback_transaction(name, error);
+#endif
+}
+
+bool ConnectionPoolBackend::query_execute(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& sql_query, std::auto_ptr<Glib::Error>& error)
+{
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  try
+  {
+    return connection->statement_execute_non_select(sql_query) != -1;
+  }
+  catch(const Glib::Error& ex)
+  {
+    error.reset(new Glib::Error(ex));
+    return false;
+  }
+#else
+  return connection->statement_execute_non_select(sql_query, error) != -1;
+#endif
+}
+
+bool ConnectionPoolBackend::add_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const sharedptr<const Field>& field, std::auto_ptr<Glib::Error>& error)
+{
+  Glib::RefPtr<Gnome::Gda::ServerProvider> provider = connection->get_provider();
+  Glib::RefPtr<Gnome::Gda::ServerOperation> operation = create_server_operation(provider, connection, Gnome::Gda::SERVER_OPERATION_ADD_COLUMN, error);
+  if(!operation) return false;
+
+  if(!set_server_operation_value(operation, "/COLUMN_DEF_P/TABLE_NAME", table_name, error)) return false;
+  if(!set_server_operation_value(operation, "/COLUMN_DEF_P/COLUMN_NAME", field->get_name(), error)) return false;
+  if(!set_server_operation_value(operation, "/COLUMN_DEF_P/COLUMN_TYPE", field->get_sql_type(), error)) return false;
+  if(!set_server_operation_value(operation, "/COLUMN_DEF_P/COLUMN_AUTOINC", field->get_auto_increment() ? "TRUE" : "FALSE", error)) return false;
+  if(!set_server_operation_value(operation, "/COLUMN_DEF_P/COLUMN_PKEY", field->get_primary_key() ? "TRUE" : "FALSE", error)) return false;
+  if(!set_server_operation_value(operation, "/COLUMN_DEF_P/COLUMN_UNIQUE", field->get_unique_key() ? "TRUE" : "FALSE", error)) return false;
+
+  if(!perform_server_operation(provider, connection, operation, error)) return false;
+  return true;
+}
+
+bool ConnectionPoolBackend::drop_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const Glib::ustring& field_name, std::auto_ptr<Glib::Error>& error)
+{
+  Glib::RefPtr<Gnome::Gda::ServerProvider> provider = connection->get_provider();
+  Glib::RefPtr<Gnome::Gda::ServerOperation> operation = create_server_operation(provider, connection, Gnome::Gda::SERVER_OPERATION_DROP_COLUMN, error);
+  if(!operation) return false;
+
+  if(!set_server_operation_value(operation, "/COLUMN_DESC_P/TABLE_NAME", table_name, error)) return false;
+  if(!set_server_operation_value(operation, "/COLUMN_DESC_P/COLUMN_NAME", field_name, error)) return false;
+  
+  if(!perform_server_operation(provider, connection, operation, error)) return false;
+  return true;
+}
+
+bool ConnectionPoolBackend::change_columns(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const type_vecConstFields& old_fields, const type_vecConstFields& new_fields, std::auto_ptr<Glib::Error>& error)
+{
+  static const char* TRANSACTION_NAME = "glom_change_columns_transaction";
+  static const gchar* TEMP_COLUMN_NAME = "glom_temp_column"; // TODO: Find a unique name.
+
+  if(!begin_transaction(connection, TRANSACTION_NAME, Gnome::Gda::TRANSACTION_ISOLATION_UNKNOWN, error)) return false; // TODO: What does the transaction isolation do?
+
+  for(unsigned int i = 0; i < old_fields.size(); ++ i)
+  {
+    // TODO: Don't create an intermediate column if the name of the column
+    // changes anyway.
+    sharedptr<Field> temp_field = glom_sharedptr_clone(new_fields[i]);
+    temp_field->set_name(TEMP_COLUMN_NAME);
+    // The temporary column must not be a primary key, otherwise
+    // we might end up with two primary key columns temporarily, which most
+    // database systems do not allow.
+    temp_field->set_primary_key(false);
+
+    if(!add_column(connection, table_name, temp_field, error)) break;
+
+    const Glib::ustring temp_move_query = "UPDATE " + table_name + " SET " + TEMP_COLUMN_NAME + " = CAST(" + old_fields[i]->get_name() + " AS " + new_fields[i]->get_sql_type() + ")";
+    if(!query_execute(connection, temp_move_query, error)) break;
+
+    if(!drop_column(connection, table_name, old_fields[i]->get_name(), error)) return false;
+
+    if(!add_column(connection, table_name, new_fields[i], error)) break;
+
+    const Glib::ustring final_move_query = "UPDATE " + table_name + " SET " + new_fields[i]->get_name() + " = " + TEMP_COLUMN_NAME; // TODO: Do we need a cast here, even though the type matches?
+    if(!query_execute(connection, final_move_query, error)) break;
+
+    if(!drop_column(connection, table_name, TEMP_COLUMN_NAME, error)) break;
+  }
+
+  if(error.get() || !commit_transaction(connection, TRANSACTION_NAME, error))
+  {
+    std::auto_ptr<Glib::Error> rollback_error;
+    rollback_transaction(connection, TRANSACTION_NAME, rollback_error);
+    return false;
+  }
+
+  return true;
+}
 
 //init_db_details static data:
 ConnectionPool* ConnectionPool::m_instance = 0;
@@ -668,6 +868,133 @@
   previous_sig_handler = SIG_DFL; /* Arbitrary default */
 }
 
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+bool ConnectionPool::add_column(const Glib::ustring& table_name, const sharedptr<const Field>& field)
+#else
+bool ConnectionPool::add_column(const Glib::ustring& table_name, const sharedptr<const Field>& field, std::auto_ptr<Glib::Error>& error)
+#endif
+{
+  sharedptr<SharedConnection> conn;
+  if(!m_refGdaConnection)
+  {
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+    conn = connect();
+#else
+    std::auto_ptr<ExceptionConnection> local_error;
+    // TODO: We can't rethrow local_error here since ExceptionConnection does
+    // not derive from Glib::Error
+    conn = connect(local_error);
+#endif
+  }
+
+  if(!m_refGdaConnection)
+    return false;
+
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  std::auto_ptr<Glib::Error> error;
+  const bool result = m_backend->add_column(m_refGdaConnection, table_name, field, error);
+  if(error.get()) throw *error;
+
+  m_refGdaConnection->update_meta_store_table(table_name);
+#else
+  const bool result = m_backend->add_column(m_refGdaConnection, table_name, field, error);
+  if(result)
+    result = m_refGdaConnection->update_meta_store_table(table_name, error);
+#endif
+
+  return result;
+}
+
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+bool ConnectionPool::drop_column(const Glib::ustring& table_name, const Glib::ustring& field_name)
+#else
+bool ConnectionPool::drop_column(const Glib::ustring& table_name, const Glib::ustring& field_name, std::auto_ptr<Glib::Error>& error)
+#endif
+{
+  sharedptr<SharedConnection> conn;
+  if(!m_refGdaConnection)
+  {
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+    conn = connect();
+#else
+    std::auto_ptr<ExceptionConnection> local_error;
+    // TODO: We can't rethrow local_error here since ExceptionConnection does
+    // not derive from Glib::Error
+    conn = connect(local_error);
+#endif
+  }
+
+  if(!m_refGdaConnection)
+    return false;
+
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  std::auto_ptr<Glib::Error> error;
+  const bool result = m_backend->drop_column(m_refGdaConnection, table_name, field_name, error);
+  if(error.get()) throw *error;
+
+  m_refGdaConnection->update_meta_store_table(table_name);
+#else
+  const bool result = m_backend->drop_column(m_refGdaConnection, table_name, field_name, error);
+  if(result)
+    result = m_refGdaConnection->update_meta_store_table(table_name, error);
+#endif
+
+  return result;
+}
+
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+bool ConnectionPool::change_column(const Glib::ustring& table_name, const sharedptr<const Field>& field_old, const sharedptr<const Field>& field)
+#else
+bool ConnectionPool::change_column(const Glib::ustring& table_name, const sharedptr<const Field>& field_old, const sharedptr<const Field>& field, std::auto_ptr<Glib::Error>& error)
+#endif
+{
+  type_vecConstFields old_fields(1, field_old);
+  type_vecConstFields new_fields(1, field);
+
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  return change_columns(table_name, old_fields, new_fields);
+#else
+  return change_columns(table_name, old_fields, new_fields, error);
+#endif
+}
+
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+bool ConnectionPool::change_columns(const Glib::ustring& table_name, const type_vecConstFields& old_fields, const type_vecConstFields& new_fields)
+#else
+bool ConnectionPool::change_columns(const Glib::ustring& table_name, const type_vecConstFields& old_fields, const type_vecConstFields& new_fields, std::auto_ptr<Glib::Error>& error)
+#endif
+{
+  sharedptr<SharedConnection> conn;
+  if(!m_refGdaConnection)
+  {
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+    conn = connect();
+#else
+    std::auto_ptr<ExceptionConnection> local_error;
+    // TODO: We can't rethrow local_error here since ExceptionConnection does
+    // not derive from Glib::Error
+    conn = connect(local_error);
+#endif
+  }
+
+  if(!m_refGdaConnection)
+    return false;
+
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  std::auto_ptr<Glib::Error> error;
+  const bool result = m_backend->change_columns(m_refGdaConnection, table_name, old_fields, new_fields, error);
+  if(error.get()) throw *error;
+
+  m_refGdaConnection->update_meta_store_table(table_name);
+#else
+  const bool result = m_backend->change_columns(m_refGdaConnection, table_name, old_fields, new_fields, error);
+  if(result)
+    result = m_refGdaConnection->update_meta_store_table(table_name, error);
+#endif
+
+  return result;
+}
+
 bool ConnectionPool::initialize(Gtk::Window* parent_window)
 {
   if(m_backend.get())

Modified: trunk/glom/libglom/connectionpool.h
==============================================================================
--- trunk/glom/libglom/connectionpool.h	(original)
+++ trunk/glom/libglom/connectionpool.h	Mon Jan 12 19:13:05 2009
@@ -109,8 +109,17 @@
   friend class ConnectionPool;
 public:
   virtual ~ConnectionPoolBackend() {}
+  typedef std::vector<sharedptr<const Field> > type_vecConstFields;
 
 protected:
+  /** Helper functions for backend implementations to use, so that these don't
+   * need to worry whether glibmm was compiled with exceptions or not.
+   */
+  bool query_execute(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& sql_query, std::auto_ptr<Glib::Error>& error);
+  bool begin_transaction(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& name, Gnome::Gda::TransactionIsolation level, std::auto_ptr<Glib::Error>& error);
+  bool commit_transaction(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& name, std::auto_ptr<Glib::Error>& error);
+  bool rollback_transaction(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& name, std::auto_ptr<Glib::Error>& error);
+
   /* TODO: Merge create_database() and initialize() into a single function?
    */
 
@@ -149,6 +158,12 @@
     */
   virtual Glib::RefPtr<Gnome::Gda::Connection> connect(const Glib::ustring& database, const Glib::ustring& username, const Glib::ustring& password, std::auto_ptr<ExceptionConnection>& error) = 0;
 
+  virtual bool add_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const sharedptr<const Field>& field, std::auto_ptr<Glib::Error>& error);
+
+  virtual bool drop_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const Glib::ustring& field_name, std::auto_ptr<Glib::Error>& error);
+
+  virtual bool change_columns(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const type_vecConstFields& old_fields, const type_vecConstFields& new_fields, std::auto_ptr<Glib::Error>& error);
+
 #ifndef GLOM_ENABLE_CLIENT_ONLY
   /** This method is called to create a new database on the
    * database server. */
@@ -168,6 +183,7 @@
   //ConnectionPool& operator=(const ConnectionPool& src);
 
 public:
+  typedef ConnectionPoolBackend::type_vecConstFields type_vecConstFields;
 
   /** Get the singleton instance.
    * Use delete_instance() when the program quits.
@@ -189,7 +205,7 @@
    * When that SharedConnection is destroyed, or when SharedConnection::close() is called, then the ConnectionPool will be informed.
    * The connection will only be closed when all SharedConnections have finished with their connections.
    *
-   * @result a shareptr to a SharedConnection. This sharedptr will be null if the connection failed.
+   * @result a sharedptr to a SharedConnection. This sharedptr will be null if the connection failed.
    *
    * @throws an ExceptionConnection when the connection fails.
    */
@@ -224,15 +240,6 @@
   Field::sql_format get_sql_format() const;
   const FieldTypes* get_field_types() const;
 
-#if 0
-  /** Return the version number of the connected postgres server.
-   * This can be used to adapt to different server features.
-   *
-   * @result The version, or 0 if no connection has been made.
-   */
-  float get_postgres_server_version();
-#endif
-
   /** Do one-time initialization, such as  creating required database
    * files on disk for later use by their own  database server instance.
    * @param parent_window A parent window to use as the transient window when displaying errors.
@@ -250,6 +257,30 @@
    */
   void cleanup(Gtk::Window* parent_window);
 
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  bool add_column(const Glib::ustring& table_name, const sharedptr<const Field>& field);
+#else
+  bool add_column(const Glib::ustring& field_name, const sharedptr<const Field>& field, std::auto_ptr<Glib::Error>& error);
+#endif
+
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  bool drop_column(const Glib::ustring& table_name, const Glib::ustring& field_name);
+#else
+  bool drop_column(const Glib::ustring& table_name, const Glib::ustring& field_name, std::auto_ptr<Glib::Error>& error);
+#endif
+
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  bool change_column(const Glib::ustring& table_name, const sharedptr<const Field>& field_old, const sharedptr<const Field>& field);
+#else
+  bool change_column(const Glib::ustring& table_name, const sharedptr<const Field>& field_old, const sharedptr<const Field>& field, std::auto_ptr<Glib::Error>& error);
+#endif
+
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  bool change_columns(const Glib::ustring& table_name, const type_vecConstFields& old_fields, const type_vecConstFields& fields);
+#else
+  bool change_columns(const Glib::ustring& table_name, const type_vecConstFields& old_fields, const type_vecConstFields& fields, std::auto_ptr<Glib::Error>& error);
+#endif
+
   /** Specify a callback that the ConnectionPool can call to get a pointer to the document.
    * This callback avoids Connection having to link to App_Glom,
    * and avoids us worrying about whether a previously-set document (via a set_document() method) is still valid.

Modified: trunk/glom/libglom/connectionpool_backends/Makefile.am
==============================================================================
--- trunk/glom/libglom/connectionpool_backends/Makefile.am	(original)
+++ trunk/glom/libglom/connectionpool_backends/Makefile.am	Mon Jan 12 19:13:05 2009
@@ -4,6 +4,7 @@
 
 noinst_LTLIBRARIES = libconnectionpool_backends.la
 libconnectionpool_backends_la_SOURCES = \
+	postgres.h postgres.cc \
 	postgres_central.h postgres_central.cc \
 	sqlite.h sqlite.cc
 

Added: trunk/glom/libglom/connectionpool_backends/postgres.cc
==============================================================================
--- (empty file)
+++ trunk/glom/libglom/connectionpool_backends/postgres.cc	Mon Jan 12 19:13:05 2009
@@ -0,0 +1,254 @@
+/* Glom
+ *
+ * Copyright (C) 2001-2004 Murray Cumming
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <glom/libglom/connectionpool_backends/postgres.h>
+#include <glibmm/i18n.h>
+#include <bakery/bakery.h>
+
+#ifdef GLOM_ENABLE_MAEMO
+# include <hildonmm/note.h>
+#else
+# include <gtkmm/messagedialog.h>
+#endif
+
+
+// Uncomment to see debug messages
+//#define GLOM_CONNECTION_DEBUG
+
+namespace
+{
+
+static Glib::ustring create_auth_string(const Glib::ustring& username, const Glib::ustring& password)
+{
+  return "USERNAME=" + username + ";PASSWORD=" + password;
+}
+
+}
+
+namespace Glom
+{
+
+namespace ConnectionPoolBackends
+{
+
+Postgres::Postgres()
+:
+  m_postgres_server_version(0.0f)
+{
+}
+
+float Postgres::get_postgres_server_version() const
+{
+  return m_postgres_server_version;
+}
+
+Glib::RefPtr<Gnome::Gda::Connection> Postgres::attempt_connect(const Glib::ustring& host, const Glib::ustring& port, const Glib::ustring& database, const Glib::ustring& username, const Glib::ustring& password, std::auto_ptr<ExceptionConnection>& error)
+{
+  //We must specify _some_ database even when we just want to create a database.
+  //This _might_ be different on some systems. I hope not. murrayc
+  const Glib::ustring default_database = "template1";
+  //const Glib::ustring& actual_database = (!database.empty()) ? database : default_database;;
+  const Glib::ustring cnc_string_main = "HOST=" + host + ";PORT=" + port;
+  Glib::ustring cnc_string = cnc_string_main + ";DB_NAME=" + database;
+
+  //std::cout << "debug: connecting: cnc string: " << cnc_string << std::endl;
+#ifdef GLOM_CONNECTION_DEBUG          
+  std::cout << std::endl << "Glom: trying to connect on port=" << port << std::endl;
+#endif
+
+  Glib::RefPtr<Gnome::Gda::Connection> connection;
+  Glib::RefPtr<Gnome::Gda::DataModel> data_model;
+
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  try
+  {
+    Glib::ustring auth_string = create_auth_string(username, password);
+    connection = Gnome::Gda::Connection::open_from_string("PostgreSQL", cnc_string, auth_string);
+    
+    connection->statement_execute_non_select("SET DATESTYLE = 'ISO'");
+    data_model = connection->statement_execute_select("SELECT version()");
+  }
+  catch(const Glib::Error& ex)
+  {
+#else
+  std::auto_ptr<Glib::Error> error;
+  Glib::ustring auth_string = create_auth_string(username, password);
+  connection = Gnome::Gda::Connection::open_from_string("PostgreSQL", cnc_string, auth_string, Gnome::Gda::CONNECTION_OPTIONS_NONE, error);
+  
+  if(!error)
+      connection->statement_execute_non_select("SET DATESTYLE = 'ISO'", error);
+
+  if(!error)
+      data_model = connection->statement_execute_select("SELECT version()", error);
+
+  if(glib_error.get())
+  {
+    const Glib::Error& ex = *glib_error;
+#endif
+
+#ifdef GLOM_CONNECTION_DEBUG
+    std::cout << "ConnectionPoolBackends::PostgresCentralHosted::attempt_connect(): Attempt to connect to database failed on port=" << port << ", database=" << actual_database << ": " << ex.what() << std::endl;
+    std::cout << "ConnectionPoolBackends::PostgresCentralHosted::attempt_connect(): Attempting to connect without specifying the database." << std::endl;
+#endif
+
+    Glib::ustring cnc_string = cnc_string_main + ";DB_NAME=" + default_database;
+    Glib::RefPtr<Gnome::Gda::Connection> temp_conn;
+    Glib::ustring auth_string = create_auth_string(username, password);
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+    try
+    {
+      temp_conn = Gnome::Gda::Connection::open_from_string("PostgreSQL", cnc_string, auth_string);
+    } catch(const Glib::Error& ex) {}
+#else
+    temp_conn = client->open_connection_from_string("PostgreSQL", cnc_string, auth_string, Gnome::Gda::CONNECTION_OPTIONS_NONE, glib_error);
+#endif
+
+#ifdef GLOM_CONNECTION_DEBUG
+    if(temp_conn)
+      std::cout << "  (Connection succeeds, but not to the specific database,  database=" << m_database << std::endl;
+    else
+      std::cerr << "  (Could not connect even to the default database, database=" << m_database  << std::endl;
+#endif
+
+    error.reset(new ExceptionConnection(temp_conn ? ExceptionConnection::FAILURE_NO_DATABASE : ExceptionConnection::FAILURE_NO_SERVER));
+    return Glib::RefPtr<Gnome::Gda::Connection>();
+  }
+
+  if(data_model && data_model->get_n_rows() && data_model->get_n_columns())
+  {
+    Gnome::Gda::Value value = data_model->get_value_at(0, 0);
+    if(value.get_value_type() == G_TYPE_STRING)
+    {
+      const Glib::ustring version_text = value.get_string();
+      //This seems to have the format "PostgreSQL 7.4.11 on i486-pc-linux"
+      const Glib::ustring namePart = "PostgreSQL ";
+      const Glib::ustring::size_type posName = version_text.find(namePart);
+      if(posName != Glib::ustring::npos)
+      {
+        const Glib::ustring versionPart = version_text.substr(namePart.size());
+        m_postgres_server_version = strtof(versionPart.c_str(), NULL);
+
+#ifdef GLOM_CONNECTION_DEBUG
+        std::cout << "  Postgres Server version: " << postgres_server_version << std::endl;
+#endif
+      }
+    }
+  }
+
+  return connection;
+}
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+bool Postgres::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)
+{
+  Glib::RefPtr<Gnome::Gda::ServerOperation> op;
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  try
+  {
+    op = Gnome::Gda::ServerOperation::prepare_create_database("PostgreSQL",
+                                                              database_name);
+  }
+  catch(const Glib::Error& ex)
+  {
+    error.reset(new Glib::Error(ex));
+    return false;
+  }
+#else
+  std::auto_ptr<Glib::Error> error;
+  op = Gnome::Gda::ServerOperation::prepare_create_database("PostgreSQL",
+                                                            database_name,
+                                                            error);
+  if(error)
+    return false;
+
+  op = cnc->create_operation(Gnome::Gda::SERVER_OPERATION_CREATE_DB, set, error);
+  if(error)
+    return false;
+#endif
+  g_assert(op);
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  try
+  {
+    op->set_value_at("/SERVER_CNX_P/HOST", host);
+    op->set_value_at("/SERVER_CNX_P/PORT", port);
+    op->set_value_at("/SERVER_CNX_P/ADM_LOGIN", username);
+    op->set_value_at("/SERVER_CNX_P/ADM_PASSWORD", password);
+    op->perform_create_database("PostgreSQL");
+  }
+  catch(const Glib::Error& ex)
+  {
+    error.reset(new Glib::Error(ex));
+    return false;
+  }
+#else
+  op->set_value_at("/SERVER_CNX_P/HOST", host, error);
+  op->set_value_at("/SERVER_CNX_P/PORT", port, error);
+  op->set_value_at("/SERVER_CNX_P/ADM_LOGIN", username, error);
+  op->set_value_at("/SERVER_CNX_P/ADM_PASSWORD", password, error);
+
+  if(error.get() == 0)
+    op->perform_create_database("PostgreSQL");
+  else
+    return false;
+#endif
+
+  return true;
+}
+#endif
+
+bool Postgres::check_postgres_gda_client_is_available_with_warning()
+{
+  // TODO_gda:
+#if 0
+  Glib::RefPtr<Gnome::Gda::Client> gda_client = Gnome::Gda::Client::create();
+  if(gda_client)
+  {
+    //Get the list of providers:
+    typedef std::list<Gnome::Gda::ProviderInfo> type_list_of_provider_info;
+    type_list_of_provider_info providers = Gnome::Gda::Config::get_providers();
+
+    //Examine the information about each Provider:
+    for(type_list_of_provider_info::const_iterator iter = providers.begin(); iter != providers.end(); ++iter)
+    { 
+      const Gnome::Gda::ProviderInfo& info = *iter;
+      if(info.get_id() == "PostgreSQL")
+        return true;
+    }
+  }
+
+  const Glib::ustring message = _("Your installation of Glom is not complete, because the PostgreSQL libgda provider is not available on your system. This provider is needed to access Postgres database servers.\n\nPlease report this bug to your vendor, or your system administrator so it can be corrected.");
+#ifndef GLOM_ENABLE_MAEMO
+  /* The Postgres provider was not found, so warn the user: */
+  Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("Incomplete Glom Installation")), true /* use_markup */, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true /* modal */);
+  dialog.set_secondary_text(message);
+  dialog.run();
+#else
+  Hildon::Note note(Hildon::NOTE_TYPE_INFORMATION, message);
+  note.run();
+#endif
+#endif
+  return true;
+}
+
+}
+
+}

Added: trunk/glom/libglom/connectionpool_backends/postgres.h
==============================================================================
--- (empty file)
+++ trunk/glom/libglom/connectionpool_backends/postgres.h	Mon Jan 12 19:13:05 2009
@@ -0,0 +1,76 @@
+/* Glom
+ *
+ * Copyright (C) 2001-2004 Murray Cumming
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GLOM_BACKEND_POSTGRES_H
+#define GLOM_BACKEND_POSTGRES_H
+
+#include <libgdamm.h>
+#include <glom/libglom/connectionpool.h>
+
+#include "config.h" // For GLOM_ENABLE_CLIENT_ONLY
+
+namespace Glom
+{
+
+namespace ConnectionPoolBackends
+{
+
+class Postgres : public ConnectionPoolBackend
+{
+public:
+  Postgres();
+
+  /** Return the version number of the connected postgres server.
+   * This can be used to adapt to different server features.
+   * 
+   * @result The version, or 0 if no connection has been made.
+   */
+  float get_postgres_server_version() const;
+
+  /** Check whether the libgda postgres provider is really available, 
+   * so we can connect to postgres servers,
+   * in case the distro package has incorrect dependencies.
+   *
+   * @results True if everything is OK.
+   */
+  static bool check_postgres_gda_client_is_available_with_warning();
+
+protected:
+  virtual Field::sql_format get_sql_format() const { return Field::SQL_FORMAT_POSTGRES; }
+  virtual bool supports_remote_access() const { return true; }
+
+  /** Creates a new database.
+   */
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  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);
+#endif
+
+  Glib::RefPtr<Gnome::Gda::Connection> attempt_connect(const Glib::ustring& host, const Glib::ustring& port, const Glib::ustring& database, const Glib::ustring& username, const Glib::ustring& password, std::auto_ptr<ExceptionConnection>& error);
+
+private:
+  float m_postgres_server_version;
+};
+
+} //namespace ConnectionPoolBackends
+
+} //namespace Glom
+
+#endif //GLOM_BACKEND_POSTGRES_H
+

Modified: trunk/glom/libglom/connectionpool_backends/postgres_central.cc
==============================================================================
--- trunk/glom/libglom/connectionpool_backends/postgres_central.cc	(original)
+++ trunk/glom/libglom/connectionpool_backends/postgres_central.cc	Mon Jan 12 19:13:05 2009
@@ -24,13 +24,6 @@
 #include <glibmm/i18n.h>
 #include <bakery/bakery.h>
 
-#ifdef GLOM_ENABLE_MAEMO
-# include <hildonmm/note.h>
-#else
-# include <gtkmm/messagedialog.h>
-#endif
-
-
 // Uncomment to see debug messages
 //#define GLOM_CONNECTION_DEBUG
 
@@ -59,8 +52,7 @@
 PostgresCentralHosted::PostgresCentralHosted()
 :
   m_port(0),
-  m_try_other_ports(true),
-  m_postgres_server_version(0.0f)
+  m_try_other_ports(true)
 {
   m_list_ports.push_back("5432"); //Ubuntu Breezy seems to default to this for Postgres 7.4, and this is probably the default for most postgres installations, including Fedora.
 
@@ -105,107 +97,6 @@
   return m_try_other_ports;
 }
 
-float PostgresCentralHosted::get_postgres_server_version() const
-{
-  return m_postgres_server_version;
-}
-
-Glib::RefPtr<Gnome::Gda::Connection> PostgresCentralHosted::attempt_connect(const Glib::ustring& host, const Glib::ustring& port, const Glib::ustring& database, const Glib::ustring& username, const Glib::ustring& password, float& postgres_server_version, std::auto_ptr<ExceptionConnection>& error)
-{
-  //We must specify _some_ database even when we just want to create a database.
-  //This _might_ be different on some systems. I hope not. murrayc
-  const Glib::ustring default_database = "template1";
-  //const Glib::ustring& actual_database = (!database.empty()) ? database : default_database;;
-  const Glib::ustring cnc_string_main = "HOST=" + host + ";PORT=" + port;
-  Glib::ustring cnc_string = cnc_string_main + ";DB_NAME=" + database;
-
-  //std::cout << "debug: connecting: cnc string: " << cnc_string << std::endl;
-#ifdef GLOM_CONNECTION_DEBUG          
-  std::cout << std::endl << "Glom: trying to connect on port=" << port << std::endl;
-#endif
-
-  Glib::RefPtr<Gnome::Gda::Connection> connection;
-  Glib::RefPtr<Gnome::Gda::DataModel> data_model;
-
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-  try
-  {
-    Glib::ustring auth_string = create_auth_string(username, password);
-    connection = Gnome::Gda::Connection::open_from_string("PostgreSQL", cnc_string, auth_string);
-    
-    connection->statement_execute_non_select("SET DATESTYLE = 'ISO'");
-    data_model = connection->statement_execute_select("SELECT version()");
-  }
-  catch(const Glib::Error& ex)
-  {
-#else
-  std::auto_ptr<Glib::Error> error;
-  Glib::ustring auth_string = create_auth_string(username, password);
-  connection = Gnome::Gda::Connection::open_from_string("PostgreSQL", cnc_string, auth_string, Gnome::Gda::CONNECTION_OPTIONS_NONE, error);
-  
-  if(!error)
-      connection->statement_execute_non_select("SET DATESTYLE = 'ISO'", error);
-
-  if(!error)
-      data_model = connection->statement_execute_select("SELECT version()", error);
-
-  if(glib_error.get())
-  {
-    const Glib::Error& ex = *glib_error;
-#endif
-
-#ifdef GLOM_CONNECTION_DEBUG
-    std::cout << "ConnectionPoolBackends::PostgresCentralHosted::attempt_connect(): Attempt to connect to database failed on port=" << port << ", database=" << actual_database << ": " << ex.what() << std::endl;
-    std::cout << "ConnectionPoolBackends::PostgresCentralHosted::attempt_connect(): Attempting to connect without specifying the database." << std::endl;
-#endif
-
-    Glib::ustring cnc_string = cnc_string_main + ";DB_NAME=" + default_database;
-    Glib::RefPtr<Gnome::Gda::Connection> temp_conn;
-    Glib::ustring auth_string = create_auth_string(username, password);
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-    try
-    {
-      temp_conn = Gnome::Gda::Connection::open_from_string("PostgreSQL", cnc_string, auth_string);
-    } catch(const Glib::Error& ex) {}
-#else
-    temp_conn = client->open_connection_from_string("PostgreSQL", cnc_string, auth_string, Gnome::Gda::CONNECTION_OPTIONS_NONE, glib_error);
-#endif
-
-#ifdef GLOM_CONNECTION_DEBUG
-    if(temp_conn)
-      std::cout << "  (Connection succeeds, but not to the specific database,  database=" << m_database << std::endl;
-    else
-      std::cerr << "  (Could not connect even to the default database, database=" << m_database  << std::endl;
-#endif
-
-    error.reset(new ExceptionConnection(temp_conn ? ExceptionConnection::FAILURE_NO_DATABASE : ExceptionConnection::FAILURE_NO_SERVER));
-    return Glib::RefPtr<Gnome::Gda::Connection>();
-  }
-
-  if(data_model && data_model->get_n_rows() && data_model->get_n_columns())
-  {
-    Gnome::Gda::Value value = data_model->get_value_at(0, 0);
-    if(value.get_value_type() == G_TYPE_STRING)
-    {
-      const Glib::ustring version_text = value.get_string();
-      //This seems to have the format "PostgreSQL 7.4.11 on i486-pc-linux"
-      const Glib::ustring namePart = "PostgreSQL ";
-      const Glib::ustring::size_type posName = version_text.find(namePart);
-      if(posName != Glib::ustring::npos)
-      {
-        const Glib::ustring versionPart = version_text.substr(namePart.size());
-        postgres_server_version = strtof(versionPart.c_str(), NULL);
-
-#ifdef GLOM_CONNECTION_DEBUG
-        std::cout << "  Postgres Server version: " << postgres_server_version << std::endl;
-#endif
-      }
-    }
-  }
-
-  return connection;
-}
-
 Glib::RefPtr<Gnome::Gda::Connection> PostgresCentralHosted::connect(const Glib::ustring& database, const Glib::ustring& username, const Glib::ustring& password, std::auto_ptr<ExceptionConnection>& error)
 {
   Glib::RefPtr<Gnome::Gda::Connection> connection;
@@ -218,7 +109,7 @@
   if(port == 0)
     port = *iter_port ++;
 
-  connection = attempt_connect(m_host, port, database, username, password, m_postgres_server_version, error);
+  connection = attempt_connect(m_host, port, database, username, password, error);
 
   // Remember port if only the database was missing
   bool connection_possible = false;
@@ -234,7 +125,7 @@
     while(!connection && iter_port != m_list_ports.end())
     {
       port = *iter_port;
-      connection = attempt_connect(m_host, port, database, username, password, m_postgres_server_version, error);
+      connection = attempt_connect(m_host, port, database, username, password, error);
 
       // Remember port if only the database was missing
       if(error.get() && error->get_failure_type() == ExceptionConnection::FAILURE_NO_DATABASE)
@@ -265,101 +156,9 @@
   return connection;
 }
 
-#ifndef GLOM_ENABLE_CLIENT_ONLY
 bool PostgresCentralHosted::create_database(const Glib::ustring& database_name, const Glib::ustring& username, const Glib::ustring& password, std::auto_ptr<Glib::Error>& error)
 {
-  Glib::RefPtr<Gnome::Gda::ServerOperation> op;
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-  try
-  {
-    op = Gnome::Gda::ServerOperation::prepare_create_database("PostgreSQL",
-                                                              database_name);
-  }
-  catch(const Glib::Error& ex)
-  {
-    error.reset(new Glib::Error(ex));
-    return false;
-  }
-#else
-  std::auto_ptr<Glib::Error> error;
-  op = Gnome::Gda::ServerOperation::prepare_create_database("PostgreSQL",
-                                                            database_name,
-                                                            error);
-  if(error)
-    return false;
-
-  op = cnc->create_operation(Gnome::Gda::SERVER_OPERATION_CREATE_DB, set, error);
-  if(error)
-    return false;
-#endif
-  g_assert(op);
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-  try
-  {
-    op->set_value_at("/SERVER_CNX_P/HOST", get_host());
-    op->set_value_at("/SERVER_CNX_P/PORT", port_as_string(m_port));
-    op->set_value_at("/SERVER_CNX_P/ADM_LOGIN", username);
-    op->set_value_at("/SERVER_CNX_P/ADM_PASSWORD", password);
-    op->perform_create_database("PostgreSQL");
-  }
-  catch(const Glib::Error& ex)
-  {
-    error.reset(new Glib::Error(ex));
-    return false;
-  }
-#else
-  op->set_value_at("/SERVER_CNX_P/HOST", get_host(), error);
-  op->set_value_at("/SERVER_CNX_P/PORT", port_as_string(m_port), error);
-  op->set_value_at("/SERVER_CNX_P/ADM_LOGIN", username, error);
-  op->set_value_at("/SERVER_CNX_P/ADM_PASSWORD", password, error);
-
-  if(error.get() == 0)
-    op->perform_create_database("PostgreSQL");
-  else
-    return false;
-#endif
-
-  return true;
-}
-#endif
-
-bool PostgresCentralHosted::check_postgres_gda_client_is_available_with_warning()
-{
-  // TODO_gda:
-#if 0
-  Glib::RefPtr<Gnome::Gda::Client> gda_client = Gnome::Gda::Client::create();
-  if(gda_client)
-  {
-    //Get the list of providers:
-    typedef std::list<Gnome::Gda::ProviderInfo> type_list_of_provider_info;
-    type_list_of_provider_info providers = Gnome::Gda::Config::get_providers();
-
-    //Examine the information about each Provider:
-    for(type_list_of_provider_info::const_iterator iter = providers.begin(); iter != providers.end(); ++iter)
-    { 
-      const Gnome::Gda::ProviderInfo& info = *iter;
-      if(info.get_id() == "PostgreSQL")
-        return true;
-    }
-  }
-
-  const Glib::ustring message = _("Your installation of Glom is not complete, because the PostgreSQL libgda provider is not available on your system. This provider is needed to access Postgres database servers.\n\nPlease report this bug to your vendor, or your system administrator so it can be corrected.");
-#ifndef GLOM_ENABLE_MAEMO
-  /* The Postgres provider was not found, so warn the user: */
-  Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("Incomplete Glom Installation")), true /* use_markup */, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true /* modal */);
-  dialog.set_secondary_text(message);
-  dialog.run();
-#else
-  Hildon::Note note(Hildon::NOTE_TYPE_INFORMATION, message);
-  note.run();
-#endif
-#endif
-  return true;
-}
-
-Glib::ustring PostgresCentralHosted::create_auth_string (const Glib::ustring& username, const Glib::ustring& password)
-{
-  return "USERNAME=" + username + ";PASSWORD=" + password;
+  return attempt_create_database(database_name, get_host(), port_as_string(m_port), username, password, error);
 }
 
 }

Modified: trunk/glom/libglom/connectionpool_backends/postgres_central.h
==============================================================================
--- trunk/glom/libglom/connectionpool_backends/postgres_central.h	(original)
+++ trunk/glom/libglom/connectionpool_backends/postgres_central.h	Mon Jan 12 19:13:05 2009
@@ -21,8 +21,7 @@
 #ifndef GLOM_BACKEND_POSTGRES_CENTRAL_H
 #define GLOM_BACKEND_POSTGRES_CENTRAL_H
 
-#include <libgdamm.h>
-#include <glom/libglom/connectionpool.h>
+#include <glom/libglom/connectionpool_backends/postgres.h>
 
 #include "config.h" // For GLOM_ENABLE_CLIENT_ONLY
 
@@ -32,7 +31,7 @@
 namespace ConnectionPoolBackends
 {
 
-class PostgresCentralHosted : public ConnectionPoolBackend
+class PostgresCentralHosted : public Postgres
 {
 public:
   PostgresCentralHosted();
@@ -48,31 +47,9 @@
   int get_port() const;
   bool get_try_other_ports() const;
 
-  /** Return the version number of the connected postgres server.
-   * This can be used to adapt to different server features.
-   *
-   * @result The version, or 0 if no connection has been made.
-   */
-  float get_postgres_server_version() const;
-
-  /** Check whether the libgda postgres provider is really available, 
-   * so we can connect to postgres servers,
-   * in case the distro package has incorrect dependencies.
-   *
-   * @results True if everything is OK.
-   */
-  static bool check_postgres_gda_client_is_available_with_warning();
-
-  static Glib::RefPtr<Gnome::Gda::Connection> attempt_connect(const Glib::ustring& host, const Glib::ustring& port, const Glib::ustring& database, const Glib::ustring& username, const Glib::ustring& password, float& postgres_server_version, std::auto_ptr<ExceptionConnection>& error);
-
 protected:
-
-  virtual Field::sql_format get_sql_format() const { return Field::SQL_FORMAT_POSTGRES; }
-  virtual bool supports_remote_access() const { return true; }
-
   virtual Glib::RefPtr<Gnome::Gda::Connection> connect(const Glib::ustring& database, const Glib::ustring& username, const Glib::ustring& password, std::auto_ptr<ExceptionConnection>& error);
-  /** Creates a new database.
-   */
+
 #ifndef GLOM_ENABLE_CLIENT_ONLY
   virtual bool create_database(const Glib::ustring& database_name, const Glib::ustring& username, const Glib::ustring& password, std::auto_ptr<Glib::Error>& error);
 #endif
@@ -84,10 +61,6 @@
   Glib::ustring m_host;
   int m_port;
   bool m_try_other_ports;
-
-  float m_postgres_server_version;
-  
-  static Glib::ustring create_auth_string(const Glib::ustring& username, const Glib::ustring& password);
 };
 
 } //namespace ConnectionPoolBackends

Modified: trunk/glom/libglom/connectionpool_backends/postgres_self.cc
==============================================================================
--- trunk/glom/libglom/connectionpool_backends/postgres_self.cc	(original)
+++ trunk/glom/libglom/connectionpool_backends/postgres_self.cc	Mon Jan 12 19:13:05 2009
@@ -19,7 +19,6 @@
  */
 
 #include <glom/libglom/connectionpool_backends/postgres_self.h>
-#include <glom/libglom/connectionpool_backends/postgres_central.h> // For PostgresCentralHosted::attempt_connect
 #include <glom/libglom/utils.h>
 #include <glom/libglom/spawn_with_feedback.h>
 #include <glib/gstdio.h> // For g_remove
@@ -120,8 +119,7 @@
 
 PostgresSelfHosted::PostgresSelfHosted()
 :
-  m_port(0),
-  m_postgres_server_version(0.0f)
+  m_port(0)
 {
 }
 
@@ -140,11 +138,6 @@
   return m_port != 0;
 }
 
-float PostgresSelfHosted::get_postgres_server_version() const
-{
-  return m_postgres_server_version;
-}
-
 int PostgresSelfHosted::get_port() const
 {
   return m_port;
@@ -474,67 +467,14 @@
   if(!get_self_hosting_active())
     return Glib::RefPtr<Gnome::Gda::Connection>();
 
-  return PostgresCentralHosted::attempt_connect("localhost", port_as_string(m_port), database, username, password, m_postgres_server_version, error);
+  return attempt_connect("localhost", port_as_string(m_port), database, username, password, error);
 
 }
 
-#ifndef GLOM_ENABLE_CLIENT_ONLY
 bool PostgresSelfHosted::create_database(const Glib::ustring& database_name, const Glib::ustring& username, const Glib::ustring& password, std::auto_ptr<Glib::Error>& error)
 {
-  Glib::RefPtr<Gnome::Gda::ServerOperation> op;
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-  try
-  {
-    op = Gnome::Gda::ServerOperation::prepare_create_database("PostgreSQL",
-                                                              database_name);
-  }
-  catch(const Glib::Error& ex)
-  {
-    error.reset(new Glib::Error(ex));
-    return false;
-  }
-#else
-  std::auto_ptr<Glib::Error> error;
-  op = Gnome::Gda::ServerOperation::prepare_create_database("PostgreSQL",
-                                                            database_name,
-                                                            error);
-  if(error)
-    return false;
-
-  op = cnc->create_operation(Gnome::Gda::SERVER_OPERATION_CREATE_DB, set, error);
-  if(error)
-    return false;
-#endif
-  g_assert(op);
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-  try
-  {
-    op->set_value_at("/SERVER_CNX_P/HOST", "localhost");
-    op->set_value_at("/SERVER_CNX_P/PORT", port_as_string(m_port));
-    op->set_value_at("/SERVER_CNX_P/ADM_LOGIN", username);
-    op->set_value_at("/SERVER_CNX_P/ADM_PASSWORD", password);
-    op->perform_create_database("PostgreSQL");
-  }
-  catch(const Glib::Error& ex)
-  {
-    error.reset(new Glib::Error(ex));
-    return false;
-  }
-#else
-  op->set_value_at("/SERVER_CNX_P/HOST", "localhost", error);
-  op->set_value_at("/SERVER_CNX_P/PORT", port_as_string(m_port), error);
-  op->set_value_at("/SERVER_CNX_P/ADM_LOGIN", username, error);
-  op->set_value_at("/SERVER_CNX_P/ADM_PASSWORD", password, error);
-
-  if(error.get() == 0)
-    op->perform_create_database("PostgreSQL");
-  else
-    return false;
-#endif
-
-  return true;
+  return attempt_create_database(database_name, "localhost", port_as_string(m_port), username, password, error);
 }
-#endif
 
 int PostgresSelfHosted::discover_first_free_port(int start_port, int end_port)
 {

Modified: trunk/glom/libglom/connectionpool_backends/postgres_self.h
==============================================================================
--- trunk/glom/libglom/connectionpool_backends/postgres_self.h	(original)
+++ trunk/glom/libglom/connectionpool_backends/postgres_self.h	Mon Jan 12 19:13:05 2009
@@ -23,7 +23,7 @@
 
 #include "config.h" // For GLOM_ENABLE_CLIENT_ONLY
 
-#include <glom/libglom/connectionpool.h>
+#include <glom/libglom/connectionpool_backends/postgres.h>
 
 namespace Glom
 {
@@ -31,7 +31,7 @@
 namespace ConnectionPoolBackends
 {
 
-class PostgresSelfHosted : public ConnectionPoolBackend
+class PostgresSelfHosted : public Postgres
 {
 public:
   PostgresSelfHosted();
@@ -41,13 +41,6 @@
    */
   void set_self_hosting_data_uri(const std::string& data_uri);
 
-  /** Return the version number of the connected postgres server.
-   * This can be used to adapt to different server features.
-   *
-   * @result The version, or 0 if no connection has been made.
-   */
-  float get_postgres_server_version() const;
-
   /** Return whether the self-hosted server is currently running.
    *
    * @result True if it is running, and false otherwise.
@@ -74,9 +67,6 @@
   static bool install_postgres(Gtk::Window* parent_window);
 
 protected:
-  virtual Field::sql_format get_sql_format() const { return Field::SQL_FORMAT_POSTGRES; }
-  virtual bool supports_remote_access() const { return true; }
-
   virtual bool initialize(Gtk::Window* parent_window, const Glib::ustring& initial_username, const Glib::ustring& password);
 
   virtual bool startup(Gtk::Window* parent_window);
@@ -84,7 +74,9 @@
 
   virtual Glib::RefPtr<Gnome::Gda::Connection> connect(const Glib::ustring& database, const Glib::ustring& username, const Glib::ustring& password, std::auto_ptr<ExceptionConnection>& error);
 
+#ifndef GLOM_ENABLE_CLIENT_ONLY
   virtual bool create_database(const Glib::ustring& database_name, const Glib::ustring& username, const Glib::ustring& password, std::auto_ptr<Glib::Error>& error);
+#endif
 
 private:
   /** Examine ports one by one, starting at @a starting_port, in increasing
@@ -99,8 +91,6 @@
 
   std::string m_self_hosting_data_uri;
   int m_port;
-
-  float m_postgres_server_version;
 };
 
 } // namespace ConnectionPoolBackends

Modified: trunk/glom/main.cc
==============================================================================
--- trunk/glom/main.cc	(original)
+++ trunk/glom/main.cc	Mon Jan 12 19:13:05 2009
@@ -28,7 +28,7 @@
 #include <giomm.h>
 
 // For postgres availability checks:
-#include <glom/libglom/connectionpool_backends/postgres_central.h>
+#include <glom/libglom/connectionpool_backends/postgres.h>
 #include <glom/libglom/connectionpool_backends/postgres_self.h>
 
 // For sanity checks:
@@ -263,7 +263,7 @@
 #endif // !GLOM_ENABLE_CLIENT_ONLY
 
     //Check that the libgda postgres provider is really available:
-    install_complete = Glom::ConnectionPoolBackends::PostgresCentralHosted::check_postgres_gda_client_is_available_with_warning();
+    install_complete = Glom::ConnectionPoolBackends::Postgres::check_postgres_gda_client_is_available_with_warning();
     if(!install_complete)
       return -1; //There is no point in going further because Glom would not be able to connect to any Postgres servers.
 

Modified: trunk/glom/mode_design/fields/box_db_table_definition.cc
==============================================================================
--- trunk/glom/mode_design/fields/box_db_table_definition.cc	(original)
+++ trunk/glom/mode_design/fields/box_db_table_definition.cc	Mon Jan 12 19:13:05 2009
@@ -22,7 +22,6 @@
 #include <glom/frame_glom.h>
 #include <glom/libglom/glade_utils.h>
 #include <bakery/App/App_Gtk.h> //For util_bold_message().
-#include <glom/glom_postgres.h>
 #include "../../../config.h"
 #include <glibmm/i18n.h>
 
@@ -165,27 +164,28 @@
   Glib::ustring name = m_AddDel.get_value(row, m_colName);
   if(!name.empty())
   {
-    const bool bTest = query_execute( "ALTER TABLE \"" + m_table_name + "\" ADD \"" + name + "\" NUMERIC", get_app_window()); //TODO: Get schema type for Field::TYPE_NUMERIC
+    sharedptr<Field> field(new Field());
+    field->set_name(name);
+    field->set_title( Utils::title_from_string(name) ); //Start with a title that might be useful.
+    field->set_glom_type(Field::TYPE_NUMERIC);
+
+    Glib::RefPtr<Gnome::Gda::Column> field_info = field->get_field_info();
+    field_info->set_g_type( Field::get_gda_type_for_glom_type(Field::TYPE_NUMERIC) );
+    field->set_field_info(field_info);
+
+    //TODO: Warn about a delay before actually doing this when the backend
+    //needs to recreate the whole table.
+    const bool bTest = add_column(m_table_name, field, get_app_window()); //TODO: Get schema type for Field::TYPE_NUMERIC
     if(bTest)
     {
       //Show the new field (fill in the other cells):
 
       //Update our list of database fields.
-      update_gda_metastore_for_table(m_table_name);
+      //update_gda_metastore_for_table(m_table_name); // already done by add_column()
       fill_fields();
 
       //fill_from_database(); //We cannot change the structure in a cell renderer signal handler.
 
-      //This must match the SQL statement above:
-      sharedptr<Field> field(new Field());
-      field->set_name(name);
-      field->set_title( Utils::title_from_string(name) ); //Start with a title that might be useful.
-      field->set_glom_type(Field::TYPE_NUMERIC);
-
-      Glib::RefPtr<Gnome::Gda::Column> field_info = field->get_field_info();
-      field_info->set_g_type( Field::get_gda_type_for_glom_type(Field::TYPE_NUMERIC) );
-      field->set_field_info(field_info);
-
       fill_field_row(row, field);
 
       //Store the generated title in the document:
@@ -207,10 +207,12 @@
     Glib::ustring name = m_AddDel.get_value_key(iter);
     if(!name.empty())
     {
-      const bool test = query_execute( "ALTER TABLE \"" + m_table_name + "\" DROP COLUMN \"" + name + "\"", get_app_window());
+      //TODO: Warn about a delay before actually doing this when the backend
+      //needs to recreate the whole table.
+      const bool test = drop_column(m_table_name, name, get_app_window());
       if(test)
       {
-        update_gda_metastore_for_table(m_table_name);
+        //update_gda_metastore_for_table(m_table_name); // already done in drop_column().
 
         //Remove it from all layouts, reports, etc:
         get_document()->remove_field(m_table_name, name);
@@ -346,6 +348,9 @@
           sharedptr<Field> fieldNewWithModifications = change_definition(m_Field_BeingEdited, fieldNew);
 
           //Update the row to show any extra changes (such as unique being set/unset whenever the primary key is set/unset) 
+	  // TODO: When change_definition decides to unset another column from
+	  // being primary key, then we also need to refill that row, so that
+	  // the user interface does not show two primary keys.
           fill_field_row(row, fieldNewWithModifications);
         }
         else
@@ -476,6 +481,9 @@
   if(!fieldOld || !field)
     return result;
 
+  type_vecConstFields old_fields;
+  type_vecFields new_fields;
+
   if(fieldOld->get_primary_key() != field->get_primary_key())
   {
     //Note: We have already checked whether this change of primary key is likely to succeed.
@@ -489,12 +497,8 @@
       {
         sharedptr<Field> existing_primary_key_unset = glom_sharedptr_clone(existing_primary_key);
         existing_primary_key_unset->set_primary_key(false);
-        sharedptr<Field> changed = change_definition(existing_primary_key, existing_primary_key_unset);
-        if(!changed)
-        {
-          std::cerr << "Box_DB_Table_Definition::change_definition(): Failed to unset the old primary key before setting the new primary key." << std::endl;
-          return result;
-        }
+	old_fields.push_back(existing_primary_key);
+	new_fields.push_back(existing_primary_key_unset);
       }
     }
 
@@ -504,41 +508,29 @@
     document->forget_layout_record_viewed(m_table_name);
   }
 
-  try
-  {
-    result = postgres_change_column(fieldOld, field);
-  }
-  catch(const Glib::Exception& ex) //In case the database reports an error.
-  {
-    handle_error(ex);
-
-    //Give up. Don't update the document. Hope that we can read the current field properties from the database.
-    fill_fields();
-    //fill_from_database(); //We should not change the database definition in a cell renderer signal handler.
-
-    //Select the same field again:
-    m_AddDel.select_item(field->get_name(), m_colName, false);
+  old_fields.push_back(fieldOld);
+  new_fields.push_back(glom_sharedptr_clone(field));
 
-    return glom_sharedptr_clone(field); 
+  //TODO: Warn about a delay, and possible loss of precision, before actually
+  //changing types or when the backend needs to recreate the whole column or
+  //table.
+  // TODO: Don't call change_columns if only the field title has changed,
+  // since the title is only stored in the document anyway.
+  if(change_columns(m_table_name, old_fields, new_fields, get_app_window()))
+  {
+    result = new_fields.back();
   }
-  catch(const std::exception& ex) //In case the database reports an error.
+  else
   {
-    handle_error(ex);
-
     //Give up. Don't update the document. Hope that we can read the current field properties from the database.
     fill_fields();
     //fill_from_database(); //We should not change the database definition in a cell renderer signal handler.
 
     //Select the same field again:
     m_AddDel.select_item(field->get_name(), m_colName, false);
-
-    return glom_sharedptr_clone(field); 
+    return glom_sharedptr_clone(old_fields.back());
   }
 
-   //MySQL does this all with ALTER_TABLE, with "CHANGE" followed by the same details used with "CREATE TABLE",
-   //MySQL also makes it easier to change the type.
-   // but Postgres uses various subcommands, such as  "ALTER COLUMN", and "RENAME".
-
   //Extra Glom field definitions:
   Document_Glom* pDoc = static_cast<Document_Glom*>(get_document());
   if(pDoc)
@@ -546,39 +538,44 @@
     //Get Table's fields:
     Document_Glom::type_vecFields vecFields = pDoc->get_table_fields(m_table_name);
 
-    //Find old field:
-    const Glib::ustring field_name_old = fieldOld->get_name();
-    Document_Glom::type_vecFields::iterator iterFind = std::find_if( vecFields.begin(), vecFields.end(), predicate_FieldHasName<Field>(field_name_old) );
-    if(iterFind != vecFields.end()) //If it was found:
-    {
-      //Change it to the new Fields's value:
-      sharedptr<Field> refField = *iterFind;
-      *refField = *result; //Remember, result is field with any necessary changes due to constraints.
-    }
-    else
+    for(unsigned int i = 0; i < old_fields.size(); ++ i)
     {
-      //Add it, because it's not there already:
-      vecFields.push_back( glom_sharedptr_clone(result) );
-    }
+      //Find old field:
+      const Glib::ustring field_name_old = old_fields[i]->get_name();
+      Document_Glom::type_vecFields::iterator iterFind = std::find_if( vecFields.begin(), vecFields.end(), predicate_FieldHasName<Field>(field_name_old) );
+      if(iterFind != vecFields.end()) //If it was found:
+      {
+        //Change it to the new Fields's value:
+	*iterFind = glom_sharedptr_clone(new_fields[i]);
+      }
+      else
+      {
+        //Add it, because it's not there already:
+        vecFields.push_back( glom_sharedptr_clone(new_fields[i]) );
+      }
 
-    pDoc->set_table_fields(m_table_name, vecFields);
+      // TODO_Performance: Can we do this at the end, after the loop? Or do
+      // the following operations depend on this?
+      pDoc->set_table_fields(m_table_name, vecFields);
 
-    //Update field names where they are used in relationships or on layouts:
-    if(field_name_old != field->get_name())
-    {
-      pDoc->change_field_name(m_table_name, field_name_old, field->get_name());
-    }
+      //Update field names where they are used in relationships or on layouts:
+      if(field_name_old != new_fields[i]->get_name())
+      {
+        pDoc->change_field_name(m_table_name, field_name_old, new_fields[i]->get_name());
+      }
 
-    //Recalculate if necessary:
-    if(field->get_has_calculation())
-    {
-      const Glib::ustring calculation = field->get_calculation();
-      if(calculation != fieldOld->get_calculation())
-        calculate_field_in_all_records(m_table_name, field);
+      // TODO_Performance: Do we even need to do this if only the primary key
+      // flag changed, such as for the first entry in the new_fields vector?
+      //Recalculate if necessary:
+      if(new_fields[i]->get_has_calculation())
+      {
+        const Glib::ustring calculation = new_fields[i]->get_calculation();
+        if(calculation != old_fields[i]->get_calculation())
+          calculate_field_in_all_records(m_table_name, new_fields[i]);
+      }
     }
   }
 
-
   //Update UI:
 
   fill_fields();
@@ -597,29 +594,8 @@
   m_vecFields = get_fields_for_table(m_table_name);
 }
 
-sharedptr<Field> Box_DB_Table_Definition::postgres_change_column(const sharedptr<const Field>& field_old, const sharedptr<const Field>& field)
-{
-  //std::cout << "DEBUG: Box_DB_Table_Definition::postgres_change_column()" << std::endl;
-  const Glib::RefPtr<const Gnome::Gda::Column> field_info = field->get_field_info();
-  const Glib::RefPtr<const Gnome::Gda::Column> field_info_old = field_old->get_field_info();
-
-  //If the underlying data type has changed:
-  if(field_info->get_g_type() != field_info_old->get_g_type() )
-  {
-    postgres_change_column_type(field_old, field); //This will also change everything else at the same time.
-    update_gda_metastore_for_table(m_table_name);
-    return glom_sharedptr_clone(field);
-  }
-  else
-  {
-    //Change other stuff, without changing the type:
-    sharedptr<Field> result = GlomPostgres::postgres_change_column_extras(m_table_name, field_old, field);
-    update_gda_metastore_for_table(m_table_name);
-    return result;
-  }
-}
-
-
+// TODO: Move this to ConnectionPoolBackends::Postgres:
+#if 0
 void Box_DB_Table_Definition::postgres_change_column_type(const sharedptr<const Field>& field_old, const sharedptr<const Field>& field)
 {
   Gtk::Window* parent_window = get_app_window();
@@ -802,6 +778,7 @@
     //Callers should call this: update_gda_metastore_for_table();
   }
 }
+#endif
 
 bool Box_DB_Table_Definition::field_has_null_values(const sharedptr<const Field>& field)
 {

Modified: trunk/glom/mode_design/fields/box_db_table_definition.h
==============================================================================
--- trunk/glom/mode_design/fields/box_db_table_definition.h	(original)
+++ trunk/glom/mode_design/fields/box_db_table_definition.h	Mon Jan 12 19:13:05 2009
@@ -58,9 +58,10 @@
 
   bool check_field_change(const sharedptr<const Field>& field_old, const sharedptr<const Field>& field_new);
 
+#if 0
   //Postgres needs some complex stuff:
-  virtual sharedptr<Field> postgres_change_column(const sharedptr<const Field>& field_old, const sharedptr<const Field>& field);
   virtual void postgres_change_column_type(const sharedptr<const Field>& field_old, const sharedptr<const Field>& field);
+#endif
 
   mutable AddDel_WithButtons m_AddDel; //mutable because its get_ methods aren't const.
 



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