[glom] libglom: Use escape_sql_id() for column add/crop/change.



commit 1d381de4ba0fc2d6991651f1f8f89366cd4e3e94
Author: Murray Cumming <murrayc murrayc com>
Date:   Tue Nov 8 14:06:36 2011 +0100

    libglom: Use escape_sql_id() for column add/crop/change.
    
    	* glom/libglom/connectionpool.cc: add_column(), drop_column(),
    
    	* glom/libglom/connectionpool_backends/backend.[h|cc]:
    	* glom/libglom/connectionpool_backends/postgres.cc
    	* glom/libglom/connectionpool_backends/sqlite.[h|cc]:
    	add_column(), drop_column(): Return a bool to indicate failure.
    	change_columns(): Report false return values from these.
    
    	* Makefile_tests.am
    	* tests/test_selfhosting_new_then_change_columns.cc: Add a simple
    	test, though we do not yet test that the expected change really
    	happened.

 ChangeLog                                         |   17 +++
 Makefile_tests.am                                 |    6 +
 glom/libglom/connectionpool.cc                    |    8 +-
 glom/libglom/connectionpool_backends/backend.cc   |    8 +-
 glom/libglom/connectionpool_backends/backend.h    |    4 +-
 glom/libglom/connectionpool_backends/postgres.cc  |   31 +++--
 glom/libglom/connectionpool_backends/sqlite.cc    |   55 ++++----
 glom/libglom/connectionpool_backends/sqlite.h     |    4 +-
 tests/test_selfhosting_new_then_change_columns.cc |  146 +++++++++++++++++++++
 9 files changed, 229 insertions(+), 50 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 0fe1f65..0f63ff1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,22 @@
 2011-11-08  Murray Cumming  <murrayc murrayc com>
 
+	libglom: Use escape_sql_id() for column add/crop/change.
+
+	* glom/libglom/connectionpool.cc: add_column(), drop_column(),
+	
+	* glom/libglom/connectionpool_backends/backend.[h|cc]:
+	* glom/libglom/connectionpool_backends/postgres.cc
+	* glom/libglom/connectionpool_backends/sqlite.[h|cc]:
+	add_column(), drop_column(): Return a bool to indicate failure.
+	change_columns(): Report false return values from these.
+
+	* Makefile_tests.am
+	* tests/test_selfhosting_new_then_change_columns.cc: Add a simple
+	test, though we do not yet test that the expected change really
+	happened.
+
+2011-11-08  Murray Cumming  <murrayc murrayc com>
+
 	libglom: Add escape_sql_id() and use it for table add/drop/rename.
 
 	* glom/libglom/db_utils.[h|cc]: Add escape_sql_id(), using 
diff --git a/Makefile_tests.am b/Makefile_tests.am
index ecea00b..a554577 100644
--- a/Makefile_tests.am
+++ b/Makefile_tests.am
@@ -32,6 +32,7 @@ check_PROGRAMS =						\
 	tests/test_selfhosting_new_then_backup_restore \
 	tests/test_selfhosting_new_then_get_privs \
 	tests/test_selfhosting_new_then_alter_table \
+	tests/test_selfhosting_new_then_change_columns \
 	tests/test_selfhosting_sqlinjection \
 	tests/import/test_parsing \
 	tests/import/test_signals
@@ -52,6 +53,7 @@ TESTS =	tests/test_document_load	\
 	tests/test_selfhosting_new_then_image \
 	tests/test_selfhosting_new_then_get_privs \
 	tests/test_selfhosting_new_then_alter_table \
+	tests/test_selfhosting_new_then_change_columns \
 	tests/test_selfhosting_sqlinjection \
 	tests/import/test_parsing \
 	tests/import/test_signals
@@ -166,6 +168,10 @@ tests_test_selfhosting_new_then_alter_table_SOURCES = tests/test_selfhosting_new
 tests_test_selfhosting_new_then_alter_table_LDADD = $(tests_ldadd)
 tests_test_selfhosting_new_then_alter_table_CPPFLAGS = $(tests_cppflags)
 
+tests_test_selfhosting_new_then_change_columns_SOURCES = tests/test_selfhosting_new_then_change_columns.cc $(sources_test_selfhosting_utils)
+tests_test_selfhosting_new_then_change_columns_LDADD = $(tests_ldadd)
+tests_test_selfhosting_new_then_change_columns_CPPFLAGS = $(tests_cppflags)
+
 tests_test_selfhosting_sqlinjection_SOURCES = tests/test_selfhosting_sqlinjection.cc $(sources_test_selfhosting_utils)
 tests_test_selfhosting_sqlinjection_LDADD = $(tests_ldadd)
 tests_test_selfhosting_sqlinjection_CPPFLAGS = $(tests_cppflags)
diff --git a/glom/libglom/connectionpool.cc b/glom/libglom/connectionpool.cc
index 0685c54..dae8533 100644
--- a/glom/libglom/connectionpool.cc
+++ b/glom/libglom/connectionpool.cc
@@ -676,9 +676,9 @@ bool ConnectionPool::add_column(const Glib::ustring& table_name, const sharedptr
 
   try
   {
-    m_backend->add_column(m_refGdaConnection, table_name, field);
+    const bool result = m_backend->add_column(m_refGdaConnection, table_name, field);
     m_refGdaConnection->update_meta_store_table(table_name, m_backend->get_public_schema_name());
-    return true;
+    return result;
   }
   catch(const Glib::Error& ex)
   {
@@ -701,9 +701,9 @@ bool ConnectionPool::drop_column(const Glib::ustring& table_name, const Glib::us
 
   try
   {
-    m_backend->drop_column(m_refGdaConnection, table_name, field_name);
+    const bool result = m_backend->drop_column(m_refGdaConnection, table_name, field_name);
     m_refGdaConnection->update_meta_store_table(table_name, m_backend->get_public_schema_name());
-    return true;
+    return result;
   }
   catch(const Glib::Error& ex)
   {
diff --git a/glom/libglom/connectionpool_backends/backend.cc b/glom/libglom/connectionpool_backends/backend.cc
index a90bcd5..9058bf5 100644
--- a/glom/libglom/connectionpool_backends/backend.cc
+++ b/glom/libglom/connectionpool_backends/backend.cc
@@ -65,7 +65,7 @@ bool Backend::set_network_shared(const SlotProgress& /* slot_progress */, bool /
   return true; //Success at doing nothing.
 }
 
-void Backend::add_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const sharedptr<const Field>& field)
+bool Backend::add_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const sharedptr<const Field>& field)
 {
   Glib::RefPtr<Gnome::Gda::ServerProvider> provider = connection->get_provider();
   Glib::RefPtr<Gnome::Gda::ServerOperation> operation = provider->create_operation(connection, Gnome::Gda::SERVER_OPERATION_ADD_COLUMN);
@@ -77,10 +77,10 @@ void Backend::add_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection,
   operation->set_value_at("/COLUMN_DEF_P/COLUMN_PKEY", field->get_primary_key());
   operation->set_value_at("/COLUMN_DEF_P/COLUMN_UNIQUE", field->get_unique_key());
 
-  provider->perform_operation(connection, operation);
+  return provider->perform_operation(connection, operation);
 }
 
-void Backend::drop_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const Glib::ustring& field_name)
+bool Backend::drop_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const Glib::ustring& field_name)
 {
   Glib::RefPtr<Gnome::Gda::ServerProvider> provider = connection->get_provider();
   Glib::RefPtr<Gnome::Gda::ServerOperation> operation = provider->create_operation(connection, Gnome::Gda::SERVER_OPERATION_DROP_COLUMN);
@@ -88,7 +88,7 @@ void Backend::drop_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection
   //TODO: Quote table name and column name?
   operation->set_value_at("/COLUMN_DESC_P/TABLE_NAME", table_name);
   operation->set_value_at("/COLUMN_DESC_P/COLUMN_NAME", field_name);
-  provider->perform_operation(connection, operation);
+  return provider->perform_operation(connection, operation);
 }
 
 void Backend::set_database_directory_uri(const std::string& directory_uri)
diff --git a/glom/libglom/connectionpool_backends/backend.h b/glom/libglom/connectionpool_backends/backend.h
index 787a2c9..46a5666 100644
--- a/glom/libglom/connectionpool_backends/backend.h
+++ b/glom/libglom/connectionpool_backends/backend.h
@@ -177,11 +177,11 @@ protected:
 
   /** @throws Glib::Error (from libgdamm)
    */
-  virtual void add_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const sharedptr<const Field>& field);
+  virtual bool add_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const sharedptr<const Field>& field);
 
   /** @throws Glib::Error (from libgdamm)
    */
-  virtual void drop_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const Glib::ustring& field_name);
+  virtual bool drop_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const Glib::ustring& field_name);
 
   virtual bool change_columns(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const type_vec_const_fields& old_fields, const type_vec_const_fields& new_fields) throw() = 0;
 
diff --git a/glom/libglom/connectionpool_backends/postgres.cc b/glom/libglom/connectionpool_backends/postgres.cc
index 5572dd6..b2baa7a 100644
--- a/glom/libglom/connectionpool_backends/postgres.cc
+++ b/glom/libglom/connectionpool_backends/postgres.cc
@@ -24,6 +24,7 @@
 #include <libglom/connectionpool_backends/postgres.h>
 #include <libglom/spawn_with_feedback.h>
 #include <libglom/utils.h>
+#include <libglom/db_utils.h>
 #include <libgdamm/config.h>
 #include <giomm/file.h>
 #include <glibmm/convert.h>
@@ -176,10 +177,15 @@ bool Postgres::change_columns(const Glib::RefPtr<Gnome::Gda::Connection>& connec
 		    // primary key columns.
 		    temp_field->set_primary_key(false);
 
-		    add_column(connection, table_name, temp_field);
+		    const bool added = add_column(connection, table_name, temp_field);
+                    if(!added)
+                    {
+                      std::cerr << G_STRFUNC << ": add_column() failed." << std::endl;
+                      //TODO: Stop the transaction and return?
+                    }
 
 		    Glib::ustring conversion_command;
-		    const Glib::ustring field_name_old_quoted = "\"" + old_fields[i]->get_name() + "\"";
+		    const Glib::ustring field_name_old_quoted = DbUtils::escape_sql_id(old_fields[i]->get_name());
 		    const Field::glom_field_type old_field_type = old_fields[i]->get_glom_type();
 
 		    if(Field::get_conversion_possible(old_fields[i]->get_glom_type(), new_fields[i]->get_glom_type()))
@@ -263,7 +269,8 @@ bool Postgres::change_columns(const Glib::RefPtr<Gnome::Gda::Connection>& connec
 		        }
 		      }
 
-		      connection->statement_execute_non_select("UPDATE \"" + table_name + "\" SET \"" + TEMP_COLUMN_NAME + "\" = " + conversion_command);
+                      //TODO: Use SqlBuilder here?
+		      connection->statement_execute_non_select("UPDATE " + DbUtils::escape_sql_id(table_name) + " SET " + DbUtils::escape_sql_id(TEMP_COLUMN_NAME) + " = " + conversion_command);
 		    }
 		    else
 		    {
@@ -272,12 +279,12 @@ bool Postgres::change_columns(const Glib::RefPtr<Gnome::Gda::Connection>& connec
 
 		    drop_column(connection, table_name, old_fields[i]->get_name());
 
-		    connection->statement_execute_non_select("ALTER TABLE \"" + table_name + "\" RENAME COLUMN \"" + TEMP_COLUMN_NAME + "\" TO \"" + new_fields[i]->get_name() + "\"");
+		    connection->statement_execute_non_select("ALTER TABLE " + DbUtils::escape_sql_id(table_name) + " RENAME COLUMN " + DbUtils::escape_sql_id(TEMP_COLUMN_NAME) + " TO " + DbUtils::escape_sql_id(new_fields[i]->get_name()));
 
 		    // Read primary key constraint
 		    if(new_fields[i]->get_primary_key())
 		    {
-		      connection->statement_execute_non_select("ALTER TABLE \"" + table_name + "\" ADD PRIMARY KEY (\"" + new_fields[i]->get_name() + "\")");
+		      connection->statement_execute_non_select("ALTER TABLE  " + DbUtils::escape_sql_id(table_name) + " ADD PRIMARY KEY (" + DbUtils::escape_sql_id(new_fields[i]->get_name()) + ")");
 		    }
 		  }
 		  else
@@ -298,19 +305,19 @@ bool Postgres::change_columns(const Glib::RefPtr<Gnome::Gda::Connection>& connec
 		        primary_key_was_set = true;
 
 		        // Primary key was added
-		       connection->statement_execute_non_select("ALTER TABLE \"" + table_name + "\" ADD PRIMARY KEY (\"" + old_fields[i]->get_name() + "\")");
+		       connection->statement_execute_non_select("ALTER TABLE " + DbUtils::escape_sql_id(table_name) + " ADD PRIMARY KEY (" + DbUtils::escape_sql_id(old_fields[i]->get_name()) + ")");
 
 		        // Remove unique key constraint, because this is already implied in
 		        // the field being primary key.
 		        if(old_fields[i]->get_unique_key())
-		          connection->statement_execute_non_select("ALTER TABLE \"" + table_name + "\" DROP CONSTRAINT \"" + old_fields[i]->get_name() + "_key");
+		          connection->statement_execute_non_select("ALTER TABLE " + DbUtils::escape_sql_id(table_name) + " DROP CONSTRAINT " + DbUtils::escape_sql_id(old_fields[i]->get_name() + "_key"));
 		      }
 		      else
 		      {
 		        primary_key_was_unset = true;
 
 		        // Primary key was removed
-		        connection->statement_execute_non_select("ALTER TABLE \"" + table_name + "\" DROP CONSTRAINT \"" + table_name + "_pkey\"");
+		        connection->statement_execute_non_select("ALTER TABLE " + DbUtils::escape_sql_id(table_name) + " DROP CONSTRAINT " + DbUtils::escape_sql_id(table_name + "_pkey"));
 		      }
 		    }
 
@@ -321,11 +328,11 @@ bool Postgres::change_columns(const Glib::RefPtr<Gnome::Gda::Connection>& connec
 		      // to do that separately if we already made it a primary key
 		      if(!primary_key_was_set && new_fields[i]->get_unique_key())
 		      {
-		        connection->statement_execute_non_select("ALTER TABLE \"" + table_name + "\" ADD CONSTRAINT \"" + old_fields[i]->get_name() + "_key\" UNIQUE (\"" + old_fields[i]->get_name() + "\")");
+		        connection->statement_execute_non_select("ALTER TABLE " + DbUtils::escape_sql_id(table_name) + " ADD CONSTRAINT " + DbUtils::escape_sql_id(old_fields[i]->get_name() + "_key") + " UNIQUE (" + DbUtils::escape_sql_id(old_fields[i]->get_name()) + ")");
 		      }
 		      else if(!primary_key_was_unset && !new_fields[i]->get_unique_key() && !new_fields[i]->get_primary_key())
 		      {
-		        connection->statement_execute_non_select("ALTER TABLE \"" + table_name + "\" DROP CONSTRAINT \"" + old_fields[i]->get_name() + "_key\"");
+		        connection->statement_execute_non_select("ALTER TABLE " + DbUtils::escape_sql_id(table_name) + " DROP CONSTRAINT " + DbUtils::escape_sql_id(old_fields[i]->get_name() + "_key"));
 		      }
 		    }
 
@@ -333,13 +340,13 @@ bool Postgres::change_columns(const Glib::RefPtr<Gnome::Gda::Connection>& connec
 		    {
 		      if(old_fields[i]->get_default_value() != new_fields[i]->get_default_value())
 		      {
-		        connection->statement_execute_non_select("ALTER TABLE \"" + table_name + "\" ALTER COLUMN \"" + old_fields[i]->get_name() + "\" SET DEFAULT " + new_fields[i]->sql(new_fields[i]->get_default_value(), connection));
+		        connection->statement_execute_non_select("ALTER TABLE " + DbUtils::escape_sql_id(table_name) + " ALTER COLUMN " + DbUtils::escape_sql_id(old_fields[i]->get_name()) + " SET DEFAULT " + new_fields[i]->sql(new_fields[i]->get_default_value(), connection));
 		      }
 		    }
 
 		    if(old_fields[i]->get_name() != new_fields[i]->get_name())
 		    {
-		      connection->statement_execute_non_select("ALTER TABLE \"" + table_name + "\" RENAME COLUMN \"" + old_fields[i]->get_name() + "\" TO \"" + new_fields[i]->get_name() + "\"");
+		      connection->statement_execute_non_select("ALTER TABLE " + DbUtils::escape_sql_id(table_name) + " RENAME COLUMN " + DbUtils::escape_sql_id(old_fields[i]->get_name()) + " TO " + DbUtils::escape_sql_id(new_fields[i]->get_name()));
 		    }
 		  }
 		}
diff --git a/glom/libglom/connectionpool_backends/sqlite.cc b/glom/libglom/connectionpool_backends/sqlite.cc
index 12e8470..fa4eff1 100644
--- a/glom/libglom/connectionpool_backends/sqlite.cc
+++ b/glom/libglom/connectionpool_backends/sqlite.cc
@@ -21,6 +21,7 @@
 #include <libglom/libglom_config.h>
 #include <libglom/connectionpool_backends/sqlite.h>
 #include <libglom/utils.h>
+#include <libglom/db_utils.h>
 #include <giomm/file.h>
 #include <libgdamm/metastore.h>
 
@@ -204,35 +205,35 @@ bool Sqlite::recreate_table(const Glib::RefPtr<Gnome::Gda::Connection>& connecti
       {
       case Field::TYPE_TEXT:
         if(column->gtype == G_TYPE_BOOLEAN)
-	  trans_fields += Glib::ustring("(CASE WHEN \"") + column->column_name + "\" = 1 THEN 'true' "
-                                              "WHEN \""  + column->column_name + "\" = 0 THEN 'false' "
-                                              "WHEN \""  + column->column_name + "\" IS NULL THEN 'false' END)";
+	  trans_fields += "(CASE WHEN "+ DbUtils::escape_sql_id(column->column_name) + " = 1 THEN 'true' "
+                                              "WHEN " + DbUtils::escape_sql_id(column->column_name) + " = 0 THEN 'false' "
+                                              "WHEN " + DbUtils::escape_sql_id(column->column_name) + " IS NULL THEN 'false' END)";
 	else if(column->gtype == GDA_TYPE_BLOB)
 	  trans_fields += "''";
 	else
           // Make sure we don't insert NULL strings, as we ignore that concept in Glom.
-          trans_fields += Glib::ustring("(CASE WHEN \"") + column->column_name + "\" IS NULL THEN '' "
-                                              "WHEN \""  + column->column_name + "\" IS NOT NULL THEN \"" + column->column_name + "\" END)";
+          trans_fields += "(CASE WHEN "+ DbUtils::escape_sql_id(column->column_name) + " IS NULL THEN '' "
+                                              "WHEN " + DbUtils::escape_sql_id(column->column_name) + " IS NOT NULL THEN " + DbUtils::escape_sql_id(column->column_name) + " END)";
 	break;
       case Field::TYPE_NUMERIC:
         if(column->gtype == G_TYPE_BOOLEAN)
-          trans_fields += Glib::ustring("(CASE WHEN \"") + column->column_name + "\" = 0 THEN 0 "
-                                              "WHEN \""  + column->column_name + "\" != 0 THEN 1 "
-                                              "WHEN \""  + column->column_name + "\" IS NULL THEN 0 END)";
+          trans_fields += "(CASE WHEN "+ DbUtils::escape_sql_id(column->column_name) + " = 0 THEN 0 "
+                                              "WHEN " + DbUtils::escape_sql_id(column->column_name) + " != 0 THEN 1 "
+                                              "WHEN " + DbUtils::escape_sql_id(column->column_name) + " IS NULL THEN 0 END)";
         else if(column->gtype == GDA_TYPE_BLOB || column->gtype == G_TYPE_DATE || column->gtype == GDA_TYPE_TIME)
           trans_fields += '0';
         else
-          trans_fields += Glib::ustring("CAST(") + column->column_name + " AS real)";
+          trans_fields += Glib::ustring("CAST(")+ DbUtils::escape_sql_id(column->column_name) + " AS real)";
         break;
       case Field::TYPE_BOOLEAN:
         if(column->gtype == G_TYPE_STRING)
-          trans_fields += Glib::ustring("(CASE WHEN \"") + column->column_name + "\" = 'true' THEN 1 "
-                                              "WHEN \""  + column->column_name + "\" = 'false' THEN 0 "
-                                              "WHEN \""  + column->column_name + "\" IS NULL THEN 0 END)";
+          trans_fields += "(CASE WHEN "+ DbUtils::escape_sql_id(column->column_name) + " = 'true' THEN 1 "
+                                              "WHEN " + DbUtils::escape_sql_id(column->column_name) + " = 'false' THEN 0 "
+                                              "WHEN " + DbUtils::escape_sql_id(column->column_name) + " IS NULL THEN 0 END)";
         else if(column->gtype == G_TYPE_DOUBLE)
-          trans_fields += Glib::ustring("(CASE WHEN \"") + column->column_name + "\" = 0 THEN 0 "
-                                              "WHEN \""  + column->column_name + "\" != 0 THEN 1 "
-                                              "WHEN \""  + column->column_name + "\" IS NULL THEN 0 END)";
+          trans_fields += "(CASE WHEN "+ DbUtils::escape_sql_id(column->column_name) + " = 0 THEN 0 "
+                                              "WHEN " + DbUtils::escape_sql_id(column->column_name) + " != 0 THEN 1 "
+                                              "WHEN " + DbUtils::escape_sql_id(column->column_name) + " IS NULL THEN 0 END)";
         else if(column->gtype == G_TYPE_BOOLEAN)
           trans_fields += column->column_name;
         else
@@ -242,13 +243,13 @@ bool Sqlite::recreate_table(const Glib::RefPtr<Gnome::Gda::Connection>& connecti
         if(column->gtype == G_TYPE_BOOLEAN || column->gtype == GDA_TYPE_BLOB || column->gtype == G_TYPE_DOUBLE)
           trans_fields += "NULL";
         else
-          trans_fields += Glib::ustring("date(") + column->column_name + ')';
+          trans_fields += Glib::ustring("date(")+ DbUtils::escape_sql_id(column->column_name) + ')';
         break;
       case Field::TYPE_TIME:
         if(column->gtype == G_TYPE_BOOLEAN || column->gtype == GDA_TYPE_BLOB || column->gtype == G_TYPE_DOUBLE)
           trans_fields += "NULL";
         else
-          trans_fields += Glib::ustring("time(") + column->column_name + ')';
+          trans_fields += Glib::ustring("time(")+ DbUtils::escape_sql_id(column->column_name) + ')';
         break;
       case Field::TYPE_IMAGE:
         if(column->gtype == GDA_TYPE_BLOB)
@@ -282,7 +283,7 @@ bool Sqlite::recreate_table(const Glib::RefPtr<Gnome::Gda::Connection>& connecti
 
       if(!trans_fields.empty())
         trans_fields += ',';
-      Gnome::Gda::Value default_value = field->get_default_value();
+      const Gnome::Gda::Value default_value = field->get_default_value();
       if(default_value.get_value_type() != G_TYPE_NONE && !default_value.is_null())
         trans_fields += field->sql(default_value, connection);
       else
@@ -324,9 +325,11 @@ bool Sqlite::recreate_table(const Glib::RefPtr<Gnome::Gda::Connection>& connecti
 
     if(!trans_fields.empty())
     {
-      connection->statement_execute_non_select(Glib::ustring("INSERT INTO \"") + TEMPORARY_TABLE_NAME + "\" SELECT " + trans_fields + " FROM \"" + table_name + "\"");
-      connection->statement_execute_non_select("DROP TABLE " + table_name);
-      connection->statement_execute_non_select(Glib::ustring("ALTER TABLE \"") + TEMPORARY_TABLE_NAME + "\" RENAME TO \"" + table_name + "\"");
+      const Glib::ustring query_insert = "INSERT INTO " + DbUtils::escape_sql_id(TEMPORARY_TABLE_NAME) + " SELECT " + trans_fields + " FROM " + DbUtils::escape_sql_id(table_name);
+      //std::cout << "debug: query_insert=" << query_insert << std::endl;
+      connection->statement_execute_non_select(query_insert);
+      connection->statement_execute_non_select("DROP TABLE " + DbUtils::escape_sql_id(table_name));
+      connection->statement_execute_non_select("ALTER TABLE " + DbUtils::escape_sql_id(TEMPORARY_TABLE_NAME) + " RENAME TO " + DbUtils::escape_sql_id(table_name));
 
       connection->commit_transaction(TRANSACTION_NAME);
 
@@ -351,23 +354,23 @@ bool Sqlite::recreate_table(const Glib::RefPtr<Gnome::Gda::Connection>& connecti
   return false;
 }
 
-void Sqlite::add_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const sharedptr<const Field>& field)
+bool Sqlite::add_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const sharedptr<const Field>& field)
 {
   // Sqlite does not support adding primary key columns. So recreate the table
   // in that case.
   if(!field->get_primary_key())
   {
-    Backend::add_column(connection, table_name, field);
+    return Backend::add_column(connection, table_name, field);
   }
   else
   {
-    recreate_table(connection, table_name, type_vec_strings(), type_vec_const_fields(1, field), type_mapFieldChanges());
+    return recreate_table(connection, table_name, type_vec_strings(), type_vec_const_fields(1, field), type_mapFieldChanges());
   }
 }
 
-void Sqlite::drop_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const Glib::ustring& field_name)
+bool Sqlite::drop_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const Glib::ustring& field_name)
 {
-  recreate_table(connection, table_name, type_vec_strings(1, field_name), type_vec_const_fields(), type_mapFieldChanges());
+  return recreate_table(connection, table_name, type_vec_strings(1, field_name), type_vec_const_fields(), type_mapFieldChanges());
 }
 
 bool Sqlite::change_columns(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const type_vec_const_fields& old_fields, const type_vec_const_fields& new_fields) throw()
diff --git a/glom/libglom/connectionpool_backends/sqlite.h b/glom/libglom/connectionpool_backends/sqlite.h
index 3917874..52c0850 100644
--- a/glom/libglom/connectionpool_backends/sqlite.h
+++ b/glom/libglom/connectionpool_backends/sqlite.h
@@ -52,8 +52,8 @@ private:
 
   bool recreate_table(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const type_vec_strings& fields_removed, const type_vec_const_fields& fields_added, const type_mapFieldChanges& fields_changed) throw();
 
-  virtual void add_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const sharedptr<const Field>& field);
-  virtual void drop_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const Glib::ustring& field_name);
+  virtual bool add_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const sharedptr<const Field>& field);
+  virtual bool drop_column(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const Glib::ustring& field_name);
   virtual bool change_columns(const Glib::RefPtr<Gnome::Gda::Connection>& connection, const Glib::ustring& table_name, const type_vec_const_fields& old_fields, const type_vec_const_fields& new_fields) throw();
 
   virtual Glib::RefPtr<Gnome::Gda::Connection> connect(const Glib::ustring& database, const Glib::ustring& username, const Glib::ustring& password);
diff --git a/tests/test_selfhosting_new_then_change_columns.cc b/tests/test_selfhosting_new_then_change_columns.cc
new file mode 100644
index 0000000..3a7bd5f
--- /dev/null
+++ b/tests/test_selfhosting_new_then_change_columns.cc
@@ -0,0 +1,146 @@
+/* Glom
+ *
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * 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
+71 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "tests/test_selfhosting_utils.h"
+#include <libglom/init.h>
+#include <libglom/utils.h>
+#include <libglom/db_utils.h>
+#include <libglom/connectionpool.h>
+#include <glibmm/fileutils.h>
+#include <glibmm/miscutils.h>
+#include <libgda/gda-blob-op.h>
+#include <glib.h> //For g_assert()
+#include <iostream>
+#include <cstdlib> //For EXIT_SUCCESS and EXIT_FAILURE
+#include <cstring> //For memcmp().
+
+static bool test(Glom::Document::HostingMode hosting_mode)
+{
+  Glom::Document document;
+  const bool recreated = 
+    test_create_and_selfhost_from_example("example_smallbusiness.glom", document, hosting_mode);
+  if(!recreated)
+  {
+    std::cerr << "Recreation failed." << std::endl;
+    return false;
+  }
+  
+  const Glib::ustring table_name = "contacts";
+  const Glom::sharedptr<const Glom::Field> field = document.get_field(table_name, "date_of_birth");
+  if(!field)
+  {
+    std::cerr << "Failure: Could not get field." << std::endl;
+    return false;
+  }
+
+  Glom::sharedptr<Glom::Field> field_new = Glom::glom_sharedptr_clone(field);
+  if(!field_new)
+  {
+    std::cerr << "Failure: field_new is null." << std::endl;
+    return false;
+  }
+  field_new->set_glom_type(Glom::Field::TYPE_TEXT);
+
+  Glom::ConnectionPool* connection_pool = Glom::ConnectionPool::get_instance();
+  if(!connection_pool)
+  {
+    std::cerr << "Failure: connection_pool is null." << std::endl;
+    return false;
+  }
+
+  //Test that change_column() does not fail horribly:
+  //TODO: Start with some data that can be converted meaningfully,
+  //and check that the result is as expected:
+  try
+  {
+    const bool test = connection_pool->change_column(table_name, field, field_new);
+    if(!test)
+    {
+      std::cerr << "Failure: change_column() failed." << std::endl;
+      return false;
+    }
+  }
+  catch(const Glib::Error& ex)
+  { 
+    std::cerr << "Failure: change_column() threw an exception: " << ex.what() << std::endl;
+    return false;
+  }
+
+  //Try another change:
+  field_new->set_glom_type(Glom::Field::TYPE_NUMERIC);
+  try
+  {
+    const bool test = connection_pool->change_column(table_name, field, field_new);
+    if(!test)
+    {
+      std::cerr << "Failure: change_column() failed." << std::endl;
+      return false;
+    }
+  }
+  catch(const Glib::Error& ex)
+  { 
+    std::cerr << "Failure: change_column() threw an exception: " << ex.what() << std::endl;
+    return false;
+  }
+
+  //Try another change:
+  field_new->set_name("somenewfieldname");
+  try
+  {
+    const bool test = connection_pool->change_column(table_name, field, field_new);
+    if(!test)
+    {
+      std::cerr << "Failure: change_column() failed." << std::endl;
+      return false;
+    }
+  }
+  catch(const Glib::Error& ex)
+  { 
+    std::cerr << "Failure: change_column() threw an exception: " << ex.what() << std::endl;
+    return false;
+  }
+
+  test_selfhosting_cleanup();
+ 
+  return true; 
+}
+
+int main()
+{
+  Glom::libglom_init();
+
+  if(!test(Glom::Document::HOSTING_MODE_POSTGRES_SELF))
+  {
+    std::cerr << "Failed with PostgreSQL" << std::endl;
+    test_selfhosting_cleanup();
+    return EXIT_FAILURE;
+  }
+  
+  if(!test(Glom::Document::HOSTING_MODE_SQLITE))
+  {
+    std::cerr << "Failed with SQLite" << std::endl;
+    test_selfhosting_cleanup();
+    return EXIT_FAILURE;
+  }
+
+  Glom::libglom_deinit();
+
+  return EXIT_SUCCESS;
+}



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