[glom/modification: 5/8] record_new(): Take a table_name.



commit d34e26489f1c3401777eb005bb64375872d2765d
Author: Murray Cumming <murrayc murrayc com>
Date:   Fri May 8 00:01:09 2009 +0200

    record_new(): Take a table_name.
    
    * glom/base_db_table_data.[h|cc]: record_new(): Take a table_name,
    so we can use this to add related records too, to make sure we get all
    the necessary default values, lookups, calculatoins, and extra field
    values, and generally reduce duplication.
    add_related_record_for_field():
    * glom/dialog_import_csv_progress.cc: on_idle_import():
    * glom/mode_data/box_data_details.cc: on_button_new(),
    on_flowtable_field_edited():
    * glom/utility_widgets/db_adddel/db_adddel.cc: user_added(): Adapt.
---
 ChangeLog                                   |   11 ++
 glom/base_db.cc                             |    5 +-
 glom/base_db_table_data.cc                  |  133 +++++++++++++++++++++------
 glom/base_db_table_data.h                   |    9 ++-
 glom/dialog_import_csv_progress.cc          |    6 +-
 glom/mode_data/box_data_details.cc          |    6 +-
 glom/utility_widgets/db_adddel/db_adddel.cc |    2 +-
 7 files changed, 135 insertions(+), 37 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 13b5a02..65883fe 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -6,6 +6,17 @@
 	Added private update_go_to_details_button_sensitivity() method and 
 	called it whenever the value is changed by the user or set via 
 	set_value(). Fixes bug #565023.
+	record_new(): Take a table_name.
+
+	* glom/base_db_table_data.[h|cc]: record_new(): Take a table_name, 
+	so we can use this to add related records too, to make sure we get all 
+	the necessary default values, lookups, calculatoins, and extra field 
+	values, and generally reduce duplication.
+	add_related_record_for_field():
+	* glom/dialog_import_csv_progress.cc: on_idle_import():
+	* glom/mode_data/box_data_details.cc: on_button_new(), 
+	on_flowtable_field_edited():
+	* glom/utility_widgets/db_adddel/db_adddel.cc: user_added(): Adapt.
 
 2009-05-07  Murray Cumming  <murrayc murrayc-x61>
 
diff --git a/glom/base_db.cc b/glom/base_db.cc
index 932436b..c07b90e 100644
--- a/glom/base_db.cc
+++ b/glom/base_db.cc
@@ -92,7 +92,7 @@ private:
 };
 
 //Intializing static members:
-Base_DB::type_extra_field_values Base_DB::m_extra_field_values;
+Base_DB::type_extra_field_values Base_DB::m_extra_modification_field_values;
 
 
 Base_DB::Base_DB()
@@ -1651,7 +1651,7 @@ bool Base_DB::insert_example_data(const Glib::ustring& table_name) const
 
       strNames += field->get_name();
 
-      Gnome::Gda::Value value = row_data[i];
+      const Gnome::Gda::Value value = row_data[i];
       //std::cout << "  DEBUG: example: field=" << field->get_name() << ", value=" << value.to_string() << std::endl;
 
       //Add a SQL parameter for the value:
@@ -1682,6 +1682,7 @@ bool Base_DB::insert_example_data(const Glib::ustring& table_name) const
     if((*iter)->get_auto_increment())
       recalculate_next_auto_increment_value(table_name, (*iter)->get_name());
   }
+
   return insert_succeeded;
 }
 
diff --git a/glom/base_db_table_data.cc b/glom/base_db_table_data.cc
index e1e870d..e75ff3a 100644
--- a/glom/base_db_table_data.cc
+++ b/glom/base_db_table_data.cc
@@ -68,20 +68,91 @@ Gtk::TreeModel::iterator Base_DB_Table_Data::get_row_selected()
   return Gtk::TreeModel::iterator();
 }
 
+/** A predicate for use with std::find_if() to find a std::pair whose 
+ * first item is the same field.
+ */
+class predicate_pair_has_field
+{
+public:
+  predicate_pair_has_field(const sharedptr<const Field>& field)
+  {
+    m_field = field;
+  }
+
+  template <class T_Second>
+  bool operator() (const std::pair<sharedptr<Field>, T_Second>& element)
+  {
+    sharedptr<Field> field = element.first;
+
+    if(!field && !m_field)
+      return true;
 
-bool Base_DB_Table_Data::record_new(bool use_entered_data, const Gnome::Gda::Value& primary_key_value)
+    if(!field || !m_field)
+      return false;
+
+    //TODO: Check more than just the name:
+    return (m_field->get_name() == field->get_name());
+  }
+    
+private:
+  sharedptr<const Field> m_field;
+};
+
+
+bool Base_DB_Table_Data::record_new(const Glib::ustring& table_name, bool use_entered_data, const Gnome::Gda::Value& primary_key_value, const type_field_values& field_values)
 {
-  sharedptr<const Field> fieldPrimaryKey = get_field_primary_key();
+  //TODO: Remove these ugly optimizations:
+  sharedptr<const Field> fieldPrimaryKey;
+  if(table_name == m_table_name)
+  {
+    //An optimization:
+    fieldPrimaryKey = get_field_primary_key();
+  }
+  else
+    fieldPrimaryKey = get_field_primary_key_for_table(table_name);
+
 
   const Glib::ustring primary_key_name = fieldPrimaryKey->get_name();
 
-  type_vecLayoutFields fieldsToAdd = m_FieldsShown;
-  if(m_TableFields.empty())
-    m_TableFields = get_fields_for_table(m_table_name);
+  //Default to adding data for all fields that are on the layout,
+  //if they contain data:
+  type_vecLayoutFields fieldsToAdd;
+  if(table_name == m_table_name)
+  {
+    //An optimization:
+    //get_table_fields_to_show() needs knowledge of a layout, 
+    //which is only in a derived class,
+    //but that class will have already set m_FieldsShown.
+    //if(!m_FieldsShown.empty())
+    //  m_FieldsShown = get_table_fields_to_show(table_name);
+    
+    fieldsToAdd = m_FieldsShown;
+  }
+  
+  //use_entered_data is not expected to work if table_name != m_table_name:
+  //if(fieldsToAdd
+  //  fieldsToAdd = get_table_fields_to_show(table_name);
+
+
+  //Get a list of all fields in the table, 
+  //not just the ones on the layout:
+  type_vec_fields all_fields;
+  if(table_name == m_table_name)
+  {
+    //An optimization:
+    if(m_TableFields.empty())
+      m_TableFields = get_fields_for_table(table_name);
+    
+    all_fields = m_TableFields;
+  }
+  
+  if(all_fields.empty())
+    all_fields = get_fields_for_table(table_name);
 
-  //Add values for all fields, not just the shown ones:
+
+  //Add values for all fields that default to something, not just the shown ones:
   //For instance, we must always add the primary key, and fields with default/calculated/lookup values:
-  for(type_vec_fields::const_iterator iter = m_TableFields.begin(); iter != m_TableFields.end(); ++iter)
+  for(type_vec_fields::const_iterator iter = all_fields.begin(); iter != all_fields.end(); ++iter)
   {
     //TODO: Search for the non-related field with the name, not just the field with the name:
     type_vecLayoutFields::const_iterator iterFind = std::find_if(fieldsToAdd.begin(), fieldsToAdd.end(), predicate_FieldHasName<LayoutItem_Field>((*iter)->get_name()));
@@ -101,20 +172,34 @@ bool Base_DB_Table_Data::record_new(bool use_entered_data, const Gnome::Gda::Val
   for(type_vecLayoutFields::const_iterator iter = fieldsToAdd.begin(); iter != fieldsToAdd.end(); ++iter)
   {
     sharedptr<LayoutItem_Field> layout_item = *iter;
+    if(!layout_item)
+      continue;
+
+    const sharedptr<const Field>& field = layout_item->get_full_field_details();
+    if(!field)
+      continue;
     
     //If the user did not enter something in this field:
-    Gnome::Gda::Value value = get_entered_field_data(layout_item);
+    Gnome::Gda::Value value;
+
+    //If the caller supplied a field value the use it,
+    //otherwise try to get it from the UI:
+    type_field_values::const_iterator iterFind = 
+      std::find_if(field_values.begin(), field_values.end(), predicate_pair_has_field(field));
+    if(iterFind != field_values.end())
+      value = iterFind->second;
+    else
+      value = get_entered_field_data(layout_item);
 
     if(Conversions::value_is_empty(value)) //This deals with empty strings too.
     {
-      const sharedptr<const Field>& field = layout_item->get_full_field_details();
-      if(field)
+      if(field) //TODO: Remove this check: we already check it above.
       {
         //If the default value should be calculated, then calculate it:
         if(field->get_has_calculation())
         {
           const Glib::ustring calculation = field->get_calculation();
-          const type_map_fields field_values = get_record_field_values_for_calculation(m_table_name, fieldPrimaryKey, primary_key_value);
+          const type_map_fields field_values = get_record_field_values_for_calculation(table_name, fieldPrimaryKey, primary_key_value);
 
           //We need the connection when we run the script, so that the script may use it.
 #ifdef GLIBMM_EXCEPTIONS_ENABLED
@@ -128,7 +213,7 @@ bool Base_DB_Table_Data::record_new(bool use_entered_data, const Gnome::Gda::Val
             // Don't evaluate function on error
 #endif // GLIBMM_EXCEPTIONS_ENABLED
 
-            const Gnome::Gda::Value value = glom_evaluate_python_function_implementation(field->get_glom_type(), calculation, field_values, document, m_table_name, sharedconnection->get_gda_connection());
+            const Gnome::Gda::Value value = glom_evaluate_python_function_implementation(field->get_glom_type(), calculation, field_values, document, table_name, sharedconnection->get_gda_connection());
             set_entered_field_data(layout_item, value);
 #ifndef GLIBMM_EXCEPTIONS_ENABLED
           }
@@ -206,7 +291,7 @@ bool Base_DB_Table_Data::record_new(bool use_entered_data, const Gnome::Gda::Val
           //Check whether the value meets uniqueness constraints:
           if(field->get_primary_key() || field->get_unique_key())
           {
-            if(!get_field_value_is_unique(m_table_name, layout_item, value))
+            if(!get_field_value_is_unique(table_name, layout_item, value))
             {
               //Ignore this field value. TODO: Warn the user about it.
             } 
@@ -235,7 +320,7 @@ bool Base_DB_Table_Data::record_new(bool use_entered_data, const Gnome::Gda::Val
   //Put it all together to create the record with these field values:
   if(!strNames.empty() && !strValues.empty())
   {
-    const Glib::ustring strQuery = "INSERT INTO \"" + m_table_name + "\" (" + strNames + ") VALUES (" + strValues + ")";
+    const Glib::ustring strQuery = "INSERT INTO \"" + table_name + "\" (" + strNames + ") VALUES (" + strValues + ")";
     const bool test = query_execute(strQuery, params);
     if(!test)
       std::cerr << "Box_Data::record_new(): INSERT failed." << std::endl;
@@ -252,7 +337,7 @@ bool Base_DB_Table_Data::record_new(bool use_entered_data, const Gnome::Gda::Val
         //TODO_Performance: We just set this with set_entered_field_data() above. Maybe we could just remember it.
         const Gnome::Gda::Value field_value = get_entered_field_data(layout_item);
 
-        LayoutFieldInRecord field_in_record(layout_item, m_table_name, fieldPrimaryKey, primary_key_value);
+        LayoutFieldInRecord field_in_record(layout_item, table_name, fieldPrimaryKey, primary_key_value);
 
         //Get-and-set values for lookup fields, if this field triggers those relationships:
         do_lookups(field_in_record, row, field_value);
@@ -368,9 +453,6 @@ bool Base_DB_Table_Data::add_related_record_for_field(const sharedptr<const Layo
     }
     else
     {
-      //TODO: Calculate values, and do lookups?
-      //TODO: Extra creation fields.
-
       //Create the related record:
       if(key_is_auto_increment)
       {
@@ -379,20 +461,15 @@ bool Base_DB_Table_Data::add_related_record_for_field(const sharedptr<const Layo
         //Generate the new key value.
       }
 
-      //TODO: Use record_new() to avoid (incomplete) duplication?
-      Glib::RefPtr<Gnome::Gda::Set> params = Gnome::Gda::Set::create();
-      params->add_holder(primary_key_field->get_holder(primary_key_value));
-      const Glib::ustring strQuery = "INSERT INTO \"" + relationship->get_to_table() + "\" (\"" + primary_key_field->get_name() + "\") VALUES (" + primary_key_field->get_gda_holder_string() + ")";
-      const bool test = query_execute(strQuery, params);
-      if(!test)
+      const bool added = record_new(relationship->get_to_table(), false /* use_entered_field_data */, primary_key_value);
+      if(!added)
       {
-        std::cerr << "Base_DB_Table_Data::add_related_record_for_field(): INSERT failed." << std::endl;
-        return false;
+        std::cerr << "Base_DB_Table_Data::add_related_record_for_field(): record_new() failed." << std::endl;
       }
 
       if(key_is_auto_increment)
       {
-        //Set the key in the parent table
+        //Set the key in the parent table:
         sharedptr<LayoutItem_Field> item_from_key = sharedptr<LayoutItem_Field>::create();
         item_from_key->set_name(relationship->get_from_field());
 
@@ -423,6 +500,8 @@ bool Base_DB_Table_Data::add_related_record_for_field(const sharedptr<const Layo
           }
           else
           {
+            Glib::RefPtr<Gnome::Gda::Set> params = Gnome::Gda::Set::create();
+            params->add_holder(primary_key_field->get_holder(primary_key_value));
             params->add_holder(parent_primary_key_field->get_holder(parent_primary_key_value));
             const Glib::ustring strQuery = "UPDATE \"" + relationship->get_from_table() + "\" SET \"" + relationship->get_from_field() + "\" = " + primary_key_field->get_gda_holder_string() +
               " WHERE \"" + relationship->get_from_table() + "\".\"" + parent_primary_key_field->get_name() + "\" = " +  parent_primary_key_field->get_gda_holder_string();
diff --git a/glom/base_db_table_data.h b/glom/base_db_table_data.h
index ce6882a..1f47a5e 100644
--- a/glom/base_db_table_data.h
+++ b/glom/base_db_table_data.h
@@ -51,10 +51,17 @@ public:
 
 protected:
 
+  typedef std::pair< sharedptr<Field>, Gnome::Gda::Value> type_field_and_value;
+  typedef std::list<type_field_and_value> type_field_values; 
+
   /** Create a new record with all the entered field values from the currently-active details/row.
+   * @param The table to which to add a new record.
+   * @param use_entered_data Whether the record should contain data already entered in the UI by the user, if table_name is m_table_name.
+   * @param primary_key_value The new primary key value for the new record. Otherwise the primary key value must be in the entered data or in the @a field_values parameter.
+   * @param field_values Values to use for fields, instead of entered data.
    * @result true if the record was added to the database.
    */
-  bool record_new(bool use_entered_data = true, const Gnome::Gda::Value& primary_key_value = Gnome::Gda::Value()); 
+  bool record_new(const Glib::ustring& table_name, bool use_entered_data = true, const Gnome::Gda::Value& primary_key_value = Gnome::Gda::Value(), const type_field_values& field_values = type_field_values()); 
 
   Gnome::Gda::Value get_entered_field_data_field_only(const sharedptr<const Field>& field) const;
   virtual Gnome::Gda::Value get_entered_field_data(const sharedptr<const LayoutItem_Field>& field) const;
diff --git a/glom/dialog_import_csv_progress.cc b/glom/dialog_import_csv_progress.cc
index ec73835..9e68870 100644
--- a/glom/dialog_import_csv_progress.cc
+++ b/glom/dialog_import_csv_progress.cc
@@ -57,7 +57,7 @@ void Dialog_Import_CSV_Progress::import(Dialog_Import_CSV& data_source)
   {
   case Dialog_Import_CSV::PARSING:
     // Wait for the parsing to finish. We do not start importing before the file has been
-    // parsed completely since we would not to rollback our changes in case of a
+    // parsed completely since we would not be able to roll back our changes in case of a
     // parsing error.
     m_progress_bar->set_text(Glib::ustring::compose(_("Parsing CSV file %1"), data_source.get_filename()));
     m_ready_connection = data_source.signal_state_changed().connect(sigc::mem_fun(*this, &Dialog_Import_CSV_Progress::on_state_changed));
@@ -195,13 +195,13 @@ bool Dialog_Import_CSV_Progress::on_idle_import()
   
   if(Glom::Conversions::value_is_empty(primary_key_value))
   {
-    Glib::ustring message(Glib::ustring::compose(_("Error importing row %1: Cannot import the row because the primary key is empty.\n"), m_current_row + 1));
+    const Glib::ustring message(Glib::ustring::compose(_("Error importing row %1: Cannot import the row because the primary key is empty.\n"), m_current_row + 1));
     add_text(message);
   }
   else
   {
     std::cout << "Dialog_Import_CSV_Progress::on_idle_import(): Calling record_new() with primary_key_value=" << primary_key_value.to_string() << " ..." << std::endl;
-    record_new(true /* use_entered_data */, primary_key_value);
+    record_new(m_table_name, true /* use_entered_data */, primary_key_value);
     std::cout << "Dialog_Import_CSV_Progress::on_idle_import(): ... Finished calling record_new()" << std::endl;
   }
 
diff --git a/glom/mode_data/box_data_details.cc b/glom/mode_data/box_data_details.cc
index b7917e4..98d9c83 100644
--- a/glom/mode_data/box_data_details.cc
+++ b/glom/mode_data/box_data_details.cc
@@ -419,7 +419,7 @@ void Box_Data_Details::on_button_new()
     //Just make a new record, and show it:
     Gnome::Gda::Value primary_key_value = get_next_auto_increment_value(m_table_name, m_field_primary_key->get_name()); //TODO: This should return a Gda::Value
 
-    record_new(false /* use entered field data */, primary_key_value);
+    record_new(m_table_name, false /* use entered field data */, primary_key_value);
     refresh_data_from_database_with_primary_key(primary_key_value);
   }
   else
@@ -849,7 +849,7 @@ void Box_Data_Details::on_flowtable_field_edited(const sharedptr<const LayoutIte
         //Make a new record, and show it:
         Gnome::Gda::Value primary_key_value = get_next_auto_increment_value(m_table_name, m_field_primary_key->get_name());
 
-        record_new(true /* use entered field data */, primary_key_value);
+        record_new(m_table_name, true /* use entered field data */, primary_key_value);
         refresh_data_from_database_with_primary_key(primary_key_value);
       }
     }
@@ -870,7 +870,7 @@ void Box_Data_Details::on_flowtable_field_edited(const sharedptr<const LayoutIte
           //Create new record with this primary key,
           //and all the other field values too.
           //see comments after 'else':
-          record_new(true /* use entered field data */);
+          record_new(m_table_name, true /* use entered field data */);
         }
       }
       else
diff --git a/glom/utility_widgets/db_adddel/db_adddel.cc b/glom/utility_widgets/db_adddel/db_adddel.cc
index 5cbf1ff..8429460 100644
--- a/glom/utility_widgets/db_adddel/db_adddel.cc
+++ b/glom/utility_widgets/db_adddel/db_adddel.cc
@@ -2382,7 +2382,7 @@ void DbAddDel::user_added(const Gtk::TreeModel::iterator& row)
       if(m_find_mode)
         return;
     
-      const bool added = record_new(true /* use entered field data*/, primary_key_value);
+      const bool added = record_new(m_table_name, true /* use entered field data*/, primary_key_value);
       if(added)
       {
         //Save the primary key value for later use:



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