[glom] Moved some lookup functions into DbUtils and added a test for them.



commit f94998a4f3f7c1d5b2ac037d3befc59b52969735
Author: Murray Cumming <murrayc murrayc com>
Date:   Thu Dec 1 14:23:06 2011 +0100

    Moved some lookup functions into DbUtils and added a test for them.
    
    	* glom/libglom/db_utils.[h|cc]:
    	* glom/base_db.[h|cc]: Moved get_fields_for_table(),
    	get_fields_for_table_one_field(), get_lookup_fields(), and
    	get_lookup_value() to the DbUtils namespace, taking an extra
    	Document parameter.
    	* glom/libglom/document/document.[h|cc]: Moved get_lookup_fields() to
    	DbUtils too.
    
    	* glom/base_db_table_data.cc
    	* glom/mode_data/box_data.cc
    	* glom/mode_data/box_data_calendar_related.cc
    	* glom/mode_data/box_data_details.cc
    	* glom/mode_data/box_data_list_related.cc
    	* glom/mode_data/db_adddel/db_adddel.cc
    	* glom/mode_design/box_db_table_relationships.cc
    	* glom/mode_design/fields/box_db_table_definition.cc
    	* glom/mode_design/fields/dialog_fielddefinition.cc
    	* glom/mode_design/layout/dialog_layout_list_related.cc
    	* glom/print_layout/canvas_print_layout.cc: Adapted. This shows how
    	often we call this very inefficient function. It would be better to
    	just make sure that the document has up-to-date information from the
    	database and just use the Document's information.
    
    	* Makefile_tests.am:
    	* tests/test_selfhosting_new_then_lookup.cc: Added a new test of
    	these functions, including retrieval of a lookup value.

 ChangeLog                                          |   31 +++
 Makefile_tests.am                                  |    6 +
 glom/base_db.cc                                    |  184 ++---------------
 glom/base_db.h                                     |   29 ---
 glom/base_db_table_data.cc                         |   10 +-
 glom/libglom/db_utils.cc                           |  118 +++++++++++
 glom/libglom/db_utils.h                            |   20 ++
 glom/libglom/document/document.cc                  |   34 +++-
 glom/libglom/document/document.h                   |   10 +
 glom/mode_data/box_data.cc                         |    8 +-
 glom/mode_data/box_data_calendar_related.cc        |    6 +-
 glom/mode_data/box_data_details.cc                 |    2 +-
 glom/mode_data/box_data_list_related.cc            |    5 +-
 glom/mode_data/db_adddel/db_adddel.cc              |    3 +-
 glom/mode_design/box_db_table_relationships.cc     |    7 +-
 glom/mode_design/fields/box_db_table_definition.cc |    5 +-
 glom/mode_design/fields/dialog_fielddefinition.cc  |    3 +-
 .../layout/dialog_layout_list_related.cc           |    5 +-
 glom/print_layout/canvas_print_layout.cc           |    4 +-
 tests/test_selfhosting_new_then_lookup.cc          |  220 ++++++++++++++++++++
 20 files changed, 495 insertions(+), 215 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 75c916b..59f373f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,34 @@
+2011-12-01  Murray Cumming  <murrayc murrayc com>
+
+	Moved some lookup functions into DbUtils and added a test for them.
+
+	* glom/libglom/db_utils.[h|cc]:
+	* glom/base_db.[h|cc]: Moved get_fields_for_table(), 
+	get_fields_for_table_one_field(), get_lookup_fields(), and 
+	get_lookup_value() to the DbUtils namespace, taking an extra 
+	Document parameter.
+	* glom/libglom/document/document.[h|cc]: Moved get_lookup_fields() to 
+	DbUtils too.
+
+	* glom/base_db_table_data.cc
+	* glom/mode_data/box_data.cc
+	* glom/mode_data/box_data_calendar_related.cc
+	* glom/mode_data/box_data_details.cc
+	* glom/mode_data/box_data_list_related.cc
+	* glom/mode_data/db_adddel/db_adddel.cc
+	* glom/mode_design/box_db_table_relationships.cc
+	* glom/mode_design/fields/box_db_table_definition.cc
+	* glom/mode_design/fields/dialog_fielddefinition.cc
+	* glom/mode_design/layout/dialog_layout_list_related.cc
+	* glom/print_layout/canvas_print_layout.cc: Adapted. This shows how
+	often we call this very inefficient function. It would be better to
+	just make sure that the document has up-to-date information from the 
+	database and just use the Document's information.
+
+	* Makefile_tests.am:
+	* tests/test_selfhosting_new_then_lookup.cc: Added a new test of
+	these functions, including retrieval of a lookup value.
+
 2011-11-30  Murray Cumming  <murrayc murrayc com>
 
 	Remove unused parameternamegenerator source file.
diff --git a/Makefile_tests.am b/Makefile_tests.am
index 1516c55..ca2691f 100644
--- a/Makefile_tests.am
+++ b/Makefile_tests.am
@@ -36,6 +36,7 @@ check_PROGRAMS =						\
 	tests/test_selfhosting_new_from_example_strangepath \
 	tests/test_selfhosting_new_then_report \
 	tests/test_selfhosting_new_then_image \
+	tests/test_selfhosting_new_then_lookup \
 	tests/test_selfhosting_new_then_backup_restore \
 	tests/test_selfhosting_new_then_get_privs \
 	tests/test_selfhosting_new_then_alter_table \
@@ -63,6 +64,7 @@ TESTS =	tests/test_document_load	\
 	tests/test_selfhosting_new_then_report \
 	tests/test_selfhosting_new_then_backup_restore \
 	tests/test_selfhosting_new_then_image \
+	tests/test_selfhosting_new_then_lookup \
 	tests/test_selfhosting_new_then_get_privs \
 	tests/test_selfhosting_new_then_alter_table \
 	tests/test_selfhosting_new_then_change_columns \
@@ -185,6 +187,10 @@ tests_test_selfhosting_new_then_image_SOURCES = tests/test_selfhosting_new_then_
 tests_test_selfhosting_new_then_image_LDADD = $(tests_ldadd)
 tests_test_selfhosting_new_then_image_CPPFLAGS = $(tests_cppflags) $(glom_test_image_defines)
 
+tests_test_selfhosting_new_then_lookup_SOURCES = tests/test_selfhosting_new_then_lookup.cc $(sources_test_selfhosting_utils)
+tests_test_selfhosting_new_then_lookup_LDADD = $(tests_ldadd)
+tests_test_selfhosting_new_then_lookup_CPPFLAGS = $(tests_cppflags)
+
 tests_test_selfhosting_new_then_backup_restore_SOURCES = tests/test_selfhosting_new_then_backup_restore.cc $(sources_test_selfhosting_utils)
 tests_test_selfhosting_new_then_backup_restore_LDADD = $(tests_ldadd)
 tests_test_selfhosting_new_then_backup_restore_CPPFLAGS = $(tests_cppflags)
diff --git a/glom/base_db.cc b/glom/base_db.cc
index 0fc9ada..936eeed 100644
--- a/glom/base_db.cc
+++ b/glom/base_db.cc
@@ -221,69 +221,6 @@ Base_DB::type_vec_strings Base_DB::util_vecStrings_from_Fields(const type_vec_fi
   return vecNames;
 }
 
-Base_DB::type_vec_fields Base_DB::get_fields_for_table(const Glib::ustring& table_name, bool including_system_fields) const
-{
-  //Get field definitions from the database:
-  type_vec_fields fieldsDatabase = DbUtils::get_fields_for_table_from_database(table_name, including_system_fields);
-
-  const Document* pDoc = dynamic_cast<const Document*>(get_document());
-  if(!pDoc)
-    return fieldsDatabase; //This should never happen.
-  else
-  {
-    type_vec_fields result;
-
-    type_vec_fields fieldsDocument = pDoc->get_table_fields(table_name);
-
-    //Look at each field in the database:
-    for(type_vec_fields::iterator iter = fieldsDocument.begin(); iter != fieldsDocument.end(); ++iter)
-    {
-      sharedptr<Field> field = *iter;
-      const Glib::ustring field_name = field->get_name();
-
-      //Get the field info from the database:
-      //This is in the document as well, but it _might_ have changed.
-      type_vec_fields::const_iterator iterFindDatabase = std::find_if(fieldsDatabase.begin(), fieldsDatabase.end(), predicate_FieldHasName<Field>(field_name));
-
-      if(iterFindDatabase != fieldsDatabase.end() ) //Ignore fields that don't exist in the database anymore.
-      {
-        Glib::RefPtr<Gnome::Gda::Column> field_info_document = field->get_field_info();
-
-        //Update the Field information that _might_ have changed in the database.
-        Glib::RefPtr<Gnome::Gda::Column> field_info = (*iterFindDatabase)->get_field_info();
-
-        //libgda does not tell us whether the field is auto_incremented, so we need to get that from the document.
-        field_info->set_auto_increment( field_info_document->get_auto_increment() );
-
-        //libgda does not tell us whether the field is auto_incremented, so we need to get that from the document.
-        //TODO_gda:field_info->set_primary_key( field_info_document->get_primary_key() );
-
-        //libgda does yet tell us correct default_value information so we need to get that from the document.
-        field_info->set_default_value( field_info_document->get_default_value() );
-
-        field->set_field_info(field_info);
-
-        result.push_back(*iter);
-      }
-    }
-
-    //Add any fields that are in the database, but not in the document:
-    for(type_vec_fields::iterator iter = fieldsDatabase.begin(); iter != fieldsDatabase.end(); ++iter)
-    {
-      Glib::ustring field_name = (*iter)->get_name();
-
-       //Look in the result so far:
-       type_vec_fields::const_iterator iterFind = std::find_if(result.begin(), result.end(), predicate_FieldHasName<Field>(field_name));
-
-       //Add it if it is not there:
-       if(iterFind == result.end() )
-         result.push_back(*iter);
-    }
-
-    return result;
-  }
-}
-
 #ifndef GLOM_ENABLE_CLIENT_ONLY
 
 namespace
@@ -600,24 +537,6 @@ sharedptr<LayoutItem_Notebook> Base_DB::offer_notebook(const sharedptr<LayoutIte
 }
 #endif // !GLOM_ENABLE_CLIENT_ONLY
 
-sharedptr<Field> Base_DB::get_fields_for_table_one_field(const Glib::ustring& table_name, const Glib::ustring& field_name) const
-{
-  //Initialize output parameter:
-  sharedptr<Field> result;
-
-  if(field_name.empty() || table_name.empty())
-    return result;
-
-  type_vec_fields fields = get_fields_for_table(table_name);
-  type_vec_fields::iterator iter = std::find_if(fields.begin(), fields.end(), predicate_FieldHasName<Field>(field_name));
-  if(iter != fields.end()) //TODO: Handle error?
-  {
-    return *iter;
-  }
-
-  return sharedptr<Field>();
-}
-
 //static:
 bool Base_DB::get_field_primary_key_index_for_fields(const type_vec_fields& fields, guint& field_column)
 {
@@ -694,6 +613,8 @@ sharedptr<Field> Base_DB::get_field_primary_key_for_table(const Glib::ustring& t
 
 void Base_DB::get_table_fields_to_show_for_sequence_add_group(const Glib::ustring& table_name, const Privileges& table_privs, const type_vec_fields& all_db_fields, const sharedptr<LayoutGroup>& group, Base_DB::type_vecConstLayoutFields& vecFields) const
 {
+  const Document* document = dynamic_cast<const Document*>(get_document());
+
   //g_warning("Box_Data::get_table_fields_to_show_for_sequence_add_group(): table_name=%s, all_db_fields.size()=%d, group->name=%s", table_name.c_str(), all_db_fields.size(), group->get_name().c_str());
 
   LayoutGroup::type_list_items items = group->get_items();
@@ -710,7 +631,7 @@ void Base_DB::get_table_fields_to_show_for_sequence_add_group(const Glib::ustrin
       if(item_field->get_has_relationship_name()) //If it's a field in a related table.
       {
         //TODO_Performance: get_fields_for_table_one_field() is probably very inefficient
-        sharedptr<Field> field = get_fields_for_table_one_field(item_field->get_table_used(table_name), item->get_name());
+        sharedptr<Field> field = DbUtils::get_fields_for_table_one_field(document, item_field->get_table_used(table_name), item->get_name());
         if(field)
         {
           sharedptr<LayoutItem_Field> layout_item = item_field;
@@ -772,14 +693,15 @@ void Base_DB::get_table_fields_to_show_for_sequence_add_group(const Glib::ustrin
 
 Base_DB::type_vecConstLayoutFields Base_DB::get_table_fields_to_show_for_sequence(const Glib::ustring& table_name, const Document::type_list_layout_groups& mapGroupSequence) const
 {
+  const Document* pDoc = dynamic_cast<const Document*>(get_document());
+
   //Get field definitions from the database, with corrections from the document:
-  type_vec_fields all_fields = get_fields_for_table(table_name);
+  type_vec_fields all_fields = DbUtils::get_fields_for_table(pDoc, table_name);
 
   const Privileges table_privs = Privs::get_current_privs(table_name);
 
   //Get fields that the document says we should show:
   type_vecConstLayoutFields result;
-  const Document* pDoc = dynamic_cast<const Document*>(get_document());
   if(pDoc)
   {
     if(mapGroupSequence.empty())
@@ -1442,12 +1364,16 @@ Base_DB::type_list_const_field_items Base_DB::get_calculation_fields(const Glib:
 
 void Base_DB::do_lookups(const LayoutFieldInRecord& field_in_record, const Gtk::TreeModel::iterator& row, const Gnome::Gda::Value& field_value)
 {
+  Document* document = get_document();
+  if(!document)
+    return;
+
    //Get values for lookup fields, if this field triggers those relationships:
    //TODO_performance: There is a LOT of iterating and copying here.
    const Glib::ustring strFieldName = field_in_record.m_field->get_name();
-   const type_list_lookups lookups = get_lookup_fields(field_in_record.m_table_name, strFieldName);
+   const Document::type_list_lookups lookups = document->get_lookup_fields(field_in_record.m_table_name, strFieldName);
    //std::cout << "debug: " << G_STRFUNC << ": lookups size=" << lookups.size() << std::endl;
-   for(type_list_lookups::const_iterator iter = lookups.begin(); iter != lookups.end(); ++iter)
+   for(Document::type_list_lookups::const_iterator iter = lookups.begin(); iter != lookups.end(); ++iter)
    {
      sharedptr<const LayoutItem_Field> layout_item = iter->first;
 
@@ -1457,10 +1383,10 @@ void Base_DB::do_lookups(const LayoutFieldInRecord& field_in_record, const Gtk::
      const sharedptr<const Field> field_lookup = layout_item->get_full_field_details();
      if(field_lookup)
      {
-      sharedptr<const Field> field_source = get_fields_for_table_one_field(relationship->get_to_table(), field_lookup->get_lookup_field());
+      sharedptr<const Field> field_source = DbUtils::get_fields_for_table_one_field(document, relationship->get_to_table(), field_lookup->get_lookup_field());
       if(field_source)
       {
-        const Gnome::Gda::Value value = get_lookup_value(field_in_record.m_table_name, iter->second /* relationship */,  field_source /* the field to look in to get the value */, field_value /* Value of to and from fields */);
+        const Gnome::Gda::Value value = DbUtils::get_lookup_value(document, field_in_record.m_table_name, iter->second /* relationship */,  field_source /* the field to look in to get the value */, field_value /* Value of to and from fields */);
 
         const Gnome::Gda::Value value_converted = Conversions::convert_value(value, layout_item->get_glom_type());
 
@@ -1479,87 +1405,11 @@ void Base_DB::do_lookups(const LayoutFieldInRecord& field_in_record, const Gtk::
   }
 }
 
-
-/** Get the fields whose values should be looked up when @a field_name changes, with
- * the relationship used to lookup the value.
- */
-Base_DB::type_list_lookups Base_DB::get_lookup_fields(const Glib::ustring& table_name, const Glib::ustring& field_name) const
-{
-  type_list_lookups result;
-
-  const Document* document = dynamic_cast<const Document*>(get_document());
-  if(document)
-  {
-    //Examine all fields, not just the the shown fields (m_Fields):
-    const type_vec_fields fields = document->get_table_fields(table_name); //TODO_Performance: Cache this?
-    //Examine all fields, not just the the shown fields.
-    for(type_vec_fields::const_iterator iter = fields.begin(); iter != fields.end();  ++iter)
-    {
-      sharedptr<Field> field = *iter;
-
-      //Examine each field that looks up its data from a relationship:
-      if(field && field->get_is_lookup())
-      {
-        //Get the relationship information:
-        sharedptr<Relationship> relationship = field->get_lookup_relationship();
-        if(relationship)
-        {
-          //If the relationship is triggererd by the specified field:
-          if(relationship->get_from_field() == field_name)
-          {
-            //Add it:
-            sharedptr<LayoutItem_Field> item = sharedptr<LayoutItem_Field>::create();
-            item->set_full_field_details(field);
-            result.push_back( type_pairFieldTrigger(item, relationship) );
-          }
-        }
-      }
-    }
-  }
-
-  return result;
-}
-
 void Base_DB::refresh_related_fields(const LayoutFieldInRecord& /* field_in_record_changed */, const Gtk::TreeModel::iterator& /* row */, const Gnome::Gda::Value& /* field_value */)
 {
   //overridden in Box_Data.
 }
 
-Gnome::Gda::Value Base_DB::get_lookup_value(const Glib::ustring& /* table_name */, const sharedptr<const Relationship>& relationship, const sharedptr<const Field>& source_field, const Gnome::Gda::Value& key_value)
-{
-  Gnome::Gda::Value result;
-
-  sharedptr<Field> to_key_field = get_fields_for_table_one_field(relationship->get_to_table(), relationship->get_to_field());
-  if(to_key_field)
-  {
-    //Convert the value, in case the from and to fields have different types:
-    const Gnome::Gda::Value value_to_key_field = Conversions::convert_value(key_value, to_key_field->get_glom_type());
-
-    const Glib::ustring target_table = relationship->get_to_table();
-    Glib::RefPtr<Gnome::Gda::SqlBuilder> builder =
-      Gnome::Gda::SqlBuilder::create(Gnome::Gda::SQL_STATEMENT_SELECT);
-    builder->select_add_field(source_field->get_name(), target_table );
-    builder->select_add_target(target_table );
-    builder->set_where(
-      builder->add_cond(Gnome::Gda::SQL_OPERATOR_TYPE_EQ,
-        builder->add_field_id(to_key_field->get_name(), target_table),
-        builder->add_expr(value_to_key_field)));
-
-    Glib::RefPtr<Gnome::Gda::DataModel> data_model = DbUtils::query_execute_select(builder);
-    if(data_model && data_model->get_n_rows())
-    {
-      //There should be only 1 row. Well, there could be more but we will ignore them.
-      result = data_model->get_value_at(0, 0);
-    }
-    else
-    {
-      handle_error();
-    }
-  }
-
-  return result;
-}
-
 bool Base_DB::get_field_value_is_unique(const Glib::ustring& table_name, const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value)
 {
   bool result = true;  //Arbitrarily default to saying it's unique if we can't get any result.
@@ -1702,8 +1552,10 @@ void Base_DB::set_found_set_where_clause_for_portal(FoundSet& found_set, const s
   // The WHERE clause mentions the first-related table (though by the alias defined in extra_join)
   // and we add an extra JOIN to mention the second-related table.
 
+  Document* document = get_document();
+
   Glib::ustring where_clause_to_table_name = relationship->get_to_table();
-  sharedptr<Field> where_clause_to_key_field = get_fields_for_table_one_field(relationship->get_to_table(), relationship->get_to_field());
+  sharedptr<Field> where_clause_to_key_field = DbUtils::get_fields_for_table_one_field(document, relationship->get_to_table(), relationship->get_to_field());
 
   found_set.m_table_name = portal->get_table_used(Glib::ustring() /* parent table - not relevant */);
 
@@ -1719,7 +1571,7 @@ void Base_DB::set_found_set_where_clause_for_portal(FoundSet& found_set, const s
     where_clause_to_table_name = uses_rel_temp->get_sql_join_alias_name();
 
     const Glib::ustring to_field_name = uses_rel_temp->get_to_field_used();
-    where_clause_to_key_field = get_fields_for_table_one_field(relationship->get_to_table(), to_field_name);
+    where_clause_to_key_field = DbUtils::get_fields_for_table_one_field(document, relationship->get_to_table(), to_field_name);
     //std::cout << "extra_join=" << found_set.m_extra_join << std::endl;
 
     //std::cout << "extra_join where_clause_to_key_field=" << where_clause_to_key_field->get_name() << std::endl;
diff --git a/glom/base_db.h b/glom/base_db.h
index 5ccb1ad..7436460 100644
--- a/glom/base_db.h
+++ b/glom/base_db.h
@@ -120,22 +120,6 @@ protected:
 
   bool get_relationship_exists(const Glib::ustring& table_name, const Glib::ustring& relationship_name);
 
-  /** Get all the fields for a table, including any from the datasbase that are not yet known in the document.
-   *
-   * @param table_name The name of the table whose fields should be listed.
-   * @param including_system_fields Whether extra non-user-visible fields should be included in the list.
-   * @result A list of fields.
-   */
-  type_vec_fields get_fields_for_table(const Glib::ustring& table_name, bool including_system_fields = false) const;
-
-  /** Get a single field definition for a table, even if the field is in the datasbase but not yet known in the document.
-   *
-   * @param table_name The name of the table whose fields should be listed.
-   * @param field_name The name of the field for which to get the definition.
-   * @result The field definition.
-   */
-  sharedptr<Field> get_fields_for_table_one_field(const Glib::ustring& table_name, const Glib::ustring& field_name) const;
-
   sharedptr<Field> get_field_primary_key_for_table(const Glib::ustring& table_name) const;
 
   //Methods to be overridden by derived classes:
@@ -258,19 +242,6 @@ protected:
 
   void do_lookups(const LayoutFieldInRecord& field_in_record, const Gtk::TreeModel::iterator& row, const Gnome::Gda::Value& field_value);
 
-  typedef std::pair< sharedptr<LayoutItem_Field>, sharedptr<Relationship> > type_pairFieldTrigger;
-  typedef std::list<type_pairFieldTrigger> type_list_lookups;
-
-  /** Get the fields whose values should be looked up when @a field_name changes, with
-   * the relationship used to lookup the value.
-   */
-  type_list_lookups get_lookup_fields(const Glib::ustring& table_name, const Glib::ustring& field_name) const;
-
-
-  /** Get the value of the @a source_field from the @a relationship, using the @a key_value.
-   */
-  Gnome::Gda::Value get_lookup_value(const Glib::ustring& table_name, const sharedptr<const Relationship>& relationship, const sharedptr<const Field>& source_field, const Gnome::Gda::Value & key_value);
-
 
   virtual void refresh_related_fields(const LayoutFieldInRecord& field_in_record_changed, const Gtk::TreeModel::iterator& row, const Gnome::Gda::Value& field_value);
 
diff --git a/glom/base_db_table_data.cc b/glom/base_db_table_data.cc
index edac7b7..aaa51cb 100644
--- a/glom/base_db_table_data.cc
+++ b/glom/base_db_table_data.cc
@@ -67,13 +67,15 @@ Gtk::TreeModel::iterator Base_DB_Table_Data::get_row_selected()
 
 bool Base_DB_Table_Data::record_new(bool use_entered_data, const Gnome::Gda::Value& primary_key_value)
 {
+  Document* document = get_document();
+
   sharedptr<const Field> fieldPrimaryKey = get_field_primary_key();
 
   const Glib::ustring primary_key_name = fieldPrimaryKey->get_name();
 
   type_vecConstLayoutFields fieldsToAdd = m_FieldsShown;
   if(m_TableFields.empty())
-    m_TableFields = get_fields_for_table(m_table_name);
+    m_TableFields = DbUtils::get_fields_for_table(document, m_table_name);
 
   //Add values for all fields, not just the shown ones:
   //For instance, we must always add the primary key, and fields with default/calculated/lookup values:
@@ -90,8 +92,6 @@ bool Base_DB_Table_Data::record_new(bool use_entered_data, const Gnome::Gda::Val
     }
   }
 
-  Document* document = get_document();
-
   //Calculate any necessary field values and enter them:
   for(type_vecConstLayoutFields::const_iterator iter = fieldsToAdd.begin(); iter != fieldsToAdd.end(); ++iter)
   {
@@ -324,7 +324,9 @@ bool Base_DB_Table_Data::add_related_record_for_field(const sharedptr<const Layo
         set_entered_field_data(item_from_key, primary_key_value);
 
         //Set it in the database too:
-        sharedptr<Field> field_from_key = get_fields_for_table_one_field(relationship->get_from_table(), relationship->get_from_field()); //TODO_Performance.
+        Document* document = get_document();
+        sharedptr<Field> field_from_key = DbUtils::get_fields_for_table_one_field(document,
+          relationship->get_from_table(), relationship->get_from_field()); //TODO_Performance.
         if(!field_from_key)
         {
           std::cerr << G_STRFUNC << ": get_fields_for_table_one_field() failed." << std::endl;
diff --git a/glom/libglom/db_utils.cc b/glom/libglom/db_utils.cc
index 3a9e0b5..07c2224 100644
--- a/glom/libglom/db_utils.cc
+++ b/glom/libglom/db_utils.cc
@@ -940,6 +940,89 @@ type_vec_fields get_fields_for_table_from_database(const Glib::ustring& table_na
   return result;
 }
 
+//TODO: This is very inefficient, because it is called so often. Just update the document with whatever is really in the database:
+type_vec_fields get_fields_for_table(const Document* document, const Glib::ustring& table_name, bool including_system_fields)
+{
+  //Get field definitions from the database:
+  type_vec_fields fieldsDatabase = get_fields_for_table_from_database(table_name, including_system_fields);
+
+  if(!document)
+  {
+    std::cerr << G_STRFUNC << ": document is null" << std::endl;
+    return fieldsDatabase; //This should never happen.
+  }
+
+  type_vec_fields result;
+
+  type_vec_fields fieldsDocument = document->get_table_fields(table_name);
+
+  //Look at each field in the database:
+  for(type_vec_fields::iterator iter = fieldsDocument.begin(); iter != fieldsDocument.end(); ++iter)
+  {
+    sharedptr<Field> field = *iter;
+    const Glib::ustring field_name = field->get_name();
+
+    //Get the field info from the database:
+    //This is in the document as well, but it _might_ have changed.
+    type_vec_fields::const_iterator iterFindDatabase = 
+      std::find_if(fieldsDatabase.begin(), fieldsDatabase.end(), predicate_FieldHasName<Field>(field_name));
+
+    if(iterFindDatabase != fieldsDatabase.end() ) //Ignore fields that don't exist in the database anymore.
+    {
+      Glib::RefPtr<Gnome::Gda::Column> field_info_document = field->get_field_info();
+
+      //Update the Field information that _might_ have changed in the database.
+      Glib::RefPtr<Gnome::Gda::Column> field_info = (*iterFindDatabase)->get_field_info();
+
+      //libgda does not tell us whether the field is auto_incremented, so we need to get that from the document.
+      field_info->set_auto_increment( field_info_document->get_auto_increment() );
+
+      //libgda does not tell us whether the field is auto_incremented, so we need to get that from the document.
+      //TODO_gda:field_info->set_primary_key( field_info_document->get_primary_key() );
+
+      //libgda does yet tell us correct default_value information so we need to get that from the document.
+      field_info->set_default_value( field_info_document->get_default_value() );
+
+      field->set_field_info(field_info);
+
+      result.push_back(*iter);
+    }
+  }
+
+  //Add any fields that are in the database, but not in the document:
+  for(type_vec_fields::iterator iter = fieldsDatabase.begin(); iter != fieldsDatabase.end(); ++iter)
+  {
+    const Glib::ustring field_name = (*iter)->get_name();
+
+    //Look in the result so far:
+    type_vec_fields::const_iterator iterFind = std::find_if(result.begin(), result.end(), predicate_FieldHasName<Field>(field_name));
+
+    //Add it if it is not there:
+    if(iterFind == result.end() )
+      result.push_back(*iter);
+  }
+
+  return result;
+}
+
+sharedptr<Field> get_fields_for_table_one_field(const Document* document, const Glib::ustring& table_name, const Glib::ustring& field_name)
+{
+  //Initialize output parameter:
+  sharedptr<Field> result;
+
+  if(field_name.empty() || table_name.empty())
+    return result;
+
+  type_vec_fields fields = get_fields_for_table(document, table_name); //TODO_Performance
+  type_vec_fields::iterator iter = std::find_if(fields.begin(), fields.end(), predicate_FieldHasName<Field>(field_name));
+  if(iter != fields.end()) //TODO: Handle error?
+  {
+    return *iter;
+  }
+
+  return sharedptr<Field>();
+}
+
 //TODO_Performance: Avoid calling this so often.
 //TODO: Put this in libgdamm.
 type_vec_strings get_table_names_from_database(bool ignore_system_tables)
@@ -1921,6 +2004,41 @@ void set_fake_connection()
   connection_pool->set_fake_connection();
 }
 
+Gnome::Gda::Value get_lookup_value(Document* document, const Glib::ustring& /* table_name */, const sharedptr<const Relationship>& relationship, const sharedptr<const Field>& source_field, const Gnome::Gda::Value& key_value)
+{
+  Gnome::Gda::Value result;
+
+  sharedptr<Field> to_key_field = get_fields_for_table_one_field(document, relationship->get_to_table(), relationship->get_to_field());
+  if(to_key_field)
+  {
+    //Convert the value, in case the from and to fields have different types:
+    const Gnome::Gda::Value value_to_key_field = Conversions::convert_value(key_value, to_key_field->get_glom_type());
+
+    const Glib::ustring target_table = relationship->get_to_table();
+    Glib::RefPtr<Gnome::Gda::SqlBuilder> builder =
+      Gnome::Gda::SqlBuilder::create(Gnome::Gda::SQL_STATEMENT_SELECT);
+    builder->select_add_field(source_field->get_name(), target_table );
+    builder->select_add_target(target_table );
+    builder->set_where(
+      builder->add_cond(Gnome::Gda::SQL_OPERATOR_TYPE_EQ,
+        builder->add_field_id(to_key_field->get_name(), target_table),
+        builder->add_expr(value_to_key_field)));
+
+    Glib::RefPtr<Gnome::Gda::DataModel> data_model = query_execute_select(builder);
+    if(data_model && data_model->get_n_rows())
+    {
+      //There should be only 1 row. Well, there could be more but we will ignore them.
+      result = data_model->get_value_at(0, 0);
+    }
+    else
+    {
+      handle_error();
+    }
+  }
+
+  return result;
+}
+
 } //namespace DbUtils
 
 } //namespace Glom
diff --git a/glom/libglom/db_utils.h b/glom/libglom/db_utils.h
index fa97683..c706f57 100644
--- a/glom/libglom/db_utils.h
+++ b/glom/libglom/db_utils.h
@@ -51,6 +51,22 @@ typedef std::vector< sharedptr<Field> > type_vec_fields;
 type_vec_fields get_fields_for_table_from_database(const Glib::ustring& table_name, bool including_system_fields = false);
 bool get_field_exists_in_database(const Glib::ustring& table_name, const Glib::ustring& field_name);
 
+/** Get all the fields for a table, including any from the database that are not yet known in the document.
+ *
+ * @param table_name The name of the table whose fields should be listed.
+ * @param including_system_fields Whether extra non-user-visible fields should be included in the list.
+ * @result A list of fields.
+ */
+type_vec_fields get_fields_for_table(const Document* document, const Glib::ustring& table_name, bool including_system_fields = false);
+
+/** Get a single field definition for a table, even if the field is in the datasbase but not yet known in the document.
+ *
+ * @param table_name The name of the table whose fields should be listed.
+ * @param field_name The name of the field for which to get the definition.
+ * @result The field definition.
+ */
+sharedptr<Field> get_fields_for_table_one_field(const Document* document, const Glib::ustring& table_name, const Glib::ustring& field_name);
+
 //TODO: Is this used directly?
 typedef std::vector<Glib::ustring> type_vec_strings;
 type_vec_strings get_table_names_from_database(bool ignore_system_tables = false);
@@ -160,6 +176,10 @@ Glib::ustring build_query_create_group(const Glib::ustring& group, bool superuse
 
 Glib::ustring build_query_add_user_to_group(const Glib::ustring& group, const Glib::ustring& user);
 
+/** Get the value of the @a source_field from the @a relationship, using the @a key_value.
+ */
+Gnome::Gda::Value get_lookup_value(Document* document, const Glib::ustring& table_name, const sharedptr<const Relationship>& relationship, const sharedptr<const Field>& source_field, const Gnome::Gda::Value & key_value);
+  
 /** Allow a fake connection, so sqlbuilder_get_full_query() can work.
  */
 void set_fake_connection();
diff --git a/glom/libglom/document/document.cc b/glom/libglom/document/document.cc
index b9f1493..a11f3a5 100644
--- a/glom/libglom/document/document.cc
+++ b/glom/libglom/document/document.cc
@@ -4682,6 +4682,38 @@ Glib::ustring Document::restore_backup_file(const Glib::ustring& backup_uri, con
 
   return untarred_uri;
 }
-  
+
+
+Document::type_list_lookups Document::get_lookup_fields(const Glib::ustring& table_name, const Glib::ustring& field_name) const
+{
+  type_list_lookups result;
+
+  //Examine all fields for this table:
+  const type_vec_fields fields = get_table_fields(table_name); //TODO_Performance: Cache this?
+  for(type_vec_fields::const_iterator iter = fields.begin(); iter != fields.end();  ++iter)
+  {
+    sharedptr<Field> field = *iter;
+
+    //Examine each field that looks up its data from a relationship:
+    if(field && field->get_is_lookup())
+    {
+      //Get the relationship information:
+      sharedptr<Relationship> relationship = field->get_lookup_relationship();
+      if(relationship)
+      {
+        //If the relationship is triggererd by the specified field:
+        if(relationship->get_from_field() == field_name)
+        {
+          //Add it:
+          sharedptr<LayoutItem_Field> item = sharedptr<LayoutItem_Field>::create();
+          item->set_full_field_details(field);
+          result.push_back( type_pairFieldTrigger(item, relationship) );
+        }
+      }
+    }
+  }
+
+  return result;
+}
 
 } //namespace Glom
diff --git a/glom/libglom/document/document.h b/glom/libglom/document/document.h
index db64044..79e2d1a 100644
--- a/glom/libglom/document/document.h
+++ b/glom/libglom/document/document.h
@@ -199,6 +199,16 @@ public:
    * so that it is not used anymore in relationships, layouts, reports, etc.
    */
   void remove_field(const Glib::ustring& table_name, const Glib::ustring& field_name);
+  
+  
+  typedef std::pair< sharedptr<LayoutItem_Field>, sharedptr<Relationship> > type_pairFieldTrigger;
+  typedef std::list<type_pairFieldTrigger> type_list_lookups;
+  
+  /** Get the fields whose values should be looked up when @a field_name changes, with
+   * the relationship used to lookup the value.
+   */
+  type_list_lookups get_lookup_fields(const Glib::ustring& table_name, const Glib::ustring& field_name) const;
+
 
 
   typedef std::vector< sharedptr<LayoutGroup> > type_list_layout_groups;
diff --git a/glom/mode_data/box_data.cc b/glom/mode_data/box_data.cc
index bee7bb4..efbb3a6 100644
--- a/glom/mode_data/box_data.cc
+++ b/glom/mode_data/box_data.cc
@@ -24,6 +24,7 @@
 #include <libglom/data_structure/glomconversions.h>
 #include <glom/utils_ui.h>
 #include <libglom/data_structure/layout/layoutitem_field.h>
+#include <libglom/db_utils.h>
 #include <libglom/privs.h>
 #include <glom/python_embed/glom_python.h>
 #include <glom/python_embed/python_ui_callbacks.h>
@@ -171,7 +172,8 @@ void Box_Data::create_layout()
   set_unstored_data(false);
 
   //Cache the table information, for performance:
-  m_TableFields = get_fields_for_table(m_table_name);
+  const Document* document = dynamic_cast<const Document*>(get_document());
+  m_TableFields = DbUtils::get_fields_for_table(document, m_table_name);
 }
 
 bool Box_Data::fill_from_database()
@@ -298,7 +300,7 @@ void Box_Data::fill_layout_group_field_info(const sharedptr<LayoutGroup>& group,
         sharedptr<const Relationship> relationship = document->get_relationship(m_table_name, relationship_name);
         if(relationship)
         {
-          sharedptr<Field> field = get_fields_for_table_one_field(relationship->get_to_table(), item->get_name());
+          sharedptr<Field> field = DbUtils::get_fields_for_table_one_field(document, relationship->get_to_table(), item->get_name());
           if(field)
           {
             item_field->set_full_field_details(field);
@@ -313,7 +315,7 @@ void Box_Data::fill_layout_group_field_info(const sharedptr<LayoutGroup>& group,
       else
       {
         //Get the field info:
-        sharedptr<Field> field = get_fields_for_table_one_field(m_table_name, item_field->get_name());
+        sharedptr<Field> field = DbUtils::get_fields_for_table_one_field(document, m_table_name, item_field->get_name());
         if(field)
         {
           item_field->set_full_field_details(field); //TODO_Performance: Just use this as the output arg?
diff --git a/glom/mode_data/box_data_calendar_related.cc b/glom/mode_data/box_data_calendar_related.cc
index 37f6248..c752472 100644
--- a/glom/mode_data/box_data_calendar_related.cc
+++ b/glom/mode_data/box_data_calendar_related.cc
@@ -108,7 +108,11 @@ bool Box_Data_Calendar_Related::init_db_details(const Glib::ustring& parent_tabl
   }
 
   if(m_portal)
-    m_key_field = get_fields_for_table_one_field(LayoutWidgetBase::m_table_name, m_portal->get_to_field_used());
+  {
+    Document* document = get_document();    
+    m_key_field = DbUtils::get_fields_for_table_one_field(document,
+      LayoutWidgetBase::m_table_name, m_portal->get_to_field_used());
+  }
   else
     m_key_field.clear();
 
diff --git a/glom/mode_data/box_data_details.cc b/glom/mode_data/box_data_details.cc
index 0b509be..df1a3a7 100644
--- a/glom/mode_data/box_data_details.cc
+++ b/glom/mode_data/box_data_details.cc
@@ -735,7 +735,7 @@ void Box_Data_Details::on_flowtable_field_edited(const sharedptr<const LayoutIte
         table_name = relationship->get_to_table();
         const Glib::ustring to_field_name = relationship->get_to_field();
         //Get the key field in the other table (the table that we will change)
-        primary_key_field = get_fields_for_table_one_field(table_name, to_field_name); //TODO_Performance.
+        primary_key_field = DbUtils::get_fields_for_table_one_field(document, table_name, to_field_name); //TODO_Performance.
         if(primary_key_field)
         {
           //Get the value of the corresponding key in the current table (that identifies the record in the table that we will change)
diff --git a/glom/mode_data/box_data_list_related.cc b/glom/mode_data/box_data_list_related.cc
index bde1f9c..1af2d61 100644
--- a/glom/mode_data/box_data_list_related.cc
+++ b/glom/mode_data/box_data_list_related.cc
@@ -108,7 +108,10 @@ bool Box_Data_List_Related::init_db_details(const Glib::ustring& parent_table, b
   }
 
   if(m_portal)
-    m_key_field = get_fields_for_table_one_field(LayoutWidgetBase::m_table_name, m_portal->get_to_field_used());
+  {
+    m_key_field = DbUtils::get_fields_for_table_one_field(get_document(),
+      LayoutWidgetBase::m_table_name, m_portal->get_to_field_used());
+  }
   else
     m_key_field.clear();
 
diff --git a/glom/mode_data/db_adddel/db_adddel.cc b/glom/mode_data/db_adddel/db_adddel.cc
index 4453c33..db91b77 100644
--- a/glom/mode_data/db_adddel/db_adddel.cc
+++ b/glom/mode_data/db_adddel/db_adddel.cc
@@ -2150,7 +2150,8 @@ void DbAddDel::user_changed(const Gtk::TreeModel::iterator& row, guint col)
           table_name = relationship->get_to_table();
           const Glib::ustring to_field_name = relationship->get_to_field();
           //Get the key field in the other table (the table that we will change)
-          primary_key_field = get_fields_for_table_one_field(table_name, to_field_name); //TODO_Performance.
+          primary_key_field = DbUtils::get_fields_for_table_one_field(document, 
+            table_name, to_field_name); //TODO_Performance.
           if(primary_key_field)
           {
             //Get the value of the corresponding key in the current table (that identifies the record in the table that we will change)
diff --git a/glom/mode_design/box_db_table_relationships.cc b/glom/mode_design/box_db_table_relationships.cc
index 205cfd9..ab4ca41 100644
--- a/glom/mode_design/box_db_table_relationships.cc
+++ b/glom/mode_design/box_db_table_relationships.cc
@@ -20,6 +20,7 @@
 
 //#include <gtkmm/builder.h>
 #include "box_db_table_relationships.h"
+#include <libglom/db_utils.h>
 #include <algorithm>
 #include <glibmm/i18n.h>
 
@@ -90,7 +91,8 @@ bool Box_DB_Table_Relationships::fill_from_database()
     Glib::RefPtr<Gnome::Gda::Connection> connection = sharedconnection->get_gda_connection();
 
     //Set combo choices:
-    m_AddDel.set_column_choices(m_colFromField, util_vecStrings_from_Fields(get_fields_for_table(m_table_name)));
+    m_AddDel.set_column_choices(m_colFromField, util_vecStrings_from_Fields(
+      DbUtils::get_fields_for_table(document, m_table_name)));
 
     type_vec_strings vecTableNames = document->get_table_names(false /* ignore_system_tables */);
     type_vec_strings vecTableNames_ustring(vecTableNames.begin(), vecTableNames.end());
@@ -245,7 +247,8 @@ void Box_DB_Table_Relationships::on_adddel_user_activated(const Gtk::TreeModel::
       {
         Glib::RefPtr<Gnome::Gda::Connection> connection = sharedconnection->get_gda_connection();
 
-        type_vec_strings vecFields = util_vecStrings_from_Fields(get_fields_for_table(table_name));
+        Document* document = get_document();
+        type_vec_strings vecFields = util_vecStrings_from_Fields(DbUtils::get_fields_for_table(document, table_name));
 
         //This would cause a lot of tedious re-filling:
         //m_AddDel.set_column_choices(m_colToField, vecFields);
diff --git a/glom/mode_design/fields/box_db_table_definition.cc b/glom/mode_design/fields/box_db_table_definition.cc
index b770832..c223115 100644
--- a/glom/mode_design/fields/box_db_table_definition.cc
+++ b/glom/mode_design/fields/box_db_table_definition.cc
@@ -432,7 +432,8 @@ sharedptr<Field> Box_DB_Table_Definition::get_field_definition(const Gtk::TreeMo
 
   //Start with original definitions, so that we preserve things like UNSIGNED.
   //TODO maybe use document's fieldinfo instead of m_vecFields.
-  sharedptr<const Field> field_temp = get_fields_for_table_one_field(m_table_name, strFieldNameBeforeEdit);
+  sharedptr<const Field> field_temp = 
+    DbUtils::get_fields_for_table_one_field(pDoc, m_table_name, strFieldNameBeforeEdit);
   if(field_temp)
   {
     Glib::RefPtr<Gnome::Gda::Column> fieldInfo = field_temp->get_field_info()->copy();
@@ -618,7 +619,7 @@ void Box_DB_Table_Definition::fill_fields()
   //std::cout << "DEBUG: Box_DB_Table_Definition::fill_fields()" << std::endl;
 
   //Update the fields (also checking the actual database):
-  m_vecFields = get_fields_for_table(m_table_name);
+  m_vecFields = DbUtils::get_fields_for_table(get_document(), m_table_name);
 }
 
 bool Box_DB_Table_Definition::field_has_null_values(const sharedptr<const Field>& field)
diff --git a/glom/mode_design/fields/dialog_fielddefinition.cc b/glom/mode_design/fields/dialog_fielddefinition.cc
index 5be6478..868bdd7 100644
--- a/glom/mode_design/fields/dialog_fielddefinition.cc
+++ b/glom/mode_design/fields/dialog_fielddefinition.cc
@@ -23,6 +23,7 @@
 #include <glom/glade_utils.h>
 #include <glom/utils_ui.h>
 #include "../../box_db_table.h"
+#include <libglom/db_utils.h>
 //#include <libgnome/gnome-i18n.h>
 #include <glibmm/i18n.h>
 
@@ -362,7 +363,7 @@ void Dialog_FieldDefinition::on_combo_lookup_relationship_changed()
       if(!to_table.empty())
       {
         //Get the fields in the other table, and add them to the combo:
-        const type_vec_fields fields_in_to_table = get_fields_for_table(to_table);
+        const type_vec_fields fields_in_to_table = DbUtils::get_fields_for_table(document, to_table);
         for(type_vec_fields::const_iterator iter = fields_in_to_table.begin(); iter != fields_in_to_table.end(); ++iter)
         {
           m_pCombo_LookupField->append((*iter)->get_name());
diff --git a/glom/mode_design/layout/dialog_layout_list_related.cc b/glom/mode_design/layout/dialog_layout_list_related.cc
index a3ef120..1098e96 100644
--- a/glom/mode_design/layout/dialog_layout_list_related.cc
+++ b/glom/mode_design/layout/dialog_layout_list_related.cc
@@ -22,6 +22,7 @@
 #include <glom/mode_design/layout/dialog_choose_field.h>
 #include <glom/mode_design/layout/layout_item_dialogs/dialog_field_layout.h>
 #include <libglom/utils.h> //For bold_message().
+#include <libglom/db_utils.h>
 #include <glom/utils_ui.h> //For show_ok_dialog().
 
 //#include <libgnome/gnome-i18n.h>
@@ -397,8 +398,8 @@ void Dialog_Layout_List_Related::on_combo_relationship_changed()
   //The relationship's to field may not be a unique field, because that would
   //prevent the portal from having multiple records.
   sharedptr<Field> to_key_field =
-    get_fields_for_table_one_field(relationship->get_to_table(),
-      relationship->get_to_field());
+    DbUtils::get_fields_for_table_one_field(get_document(), 
+      relationship->get_to_table(), relationship->get_to_field());
   bool relationship_invalid = false;
   if(!to_key_field)
   {
diff --git a/glom/print_layout/canvas_print_layout.cc b/glom/print_layout/canvas_print_layout.cc
index 8233104..d6a1433 100644
--- a/glom/print_layout/canvas_print_layout.cc
+++ b/glom/print_layout/canvas_print_layout.cc
@@ -795,7 +795,9 @@ void Canvas_PrintLayout::fill_with_data(const Glib::RefPtr<Goocanvas::Group>& ca
         sharedptr<const Relationship> relationship = layoutitem_portal->get_relationship();
         if(relationship)
         {
-          const sharedptr<Field> from_field = get_fields_for_table_one_field(relationship->get_from_table(), relationship->get_from_field());
+          const Document* document = get_document();
+          const sharedptr<Field> from_field = DbUtils::get_fields_for_table_one_field(document,
+            relationship->get_from_table(), relationship->get_from_field());
           const Gnome::Gda::Value from_key_value = get_field_value_in_database(from_field, found_set, 0 /* TODO: window */);
           fill_with_data_portal(canvas_item, from_key_value);
         }
diff --git a/tests/test_selfhosting_new_then_lookup.cc b/tests/test_selfhosting_new_then_lookup.cc
new file mode 100644
index 0000000..93d79a1
--- /dev/null
+++ b/tests/test_selfhosting_new_then_lookup.cc
@@ -0,0 +1,220 @@
+/* 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 <libglom/data_structure/glomconversions.h>
+#include <glibmm/fileutils.h>
+#include <glibmm/miscutils.h>
+#include <glib.h> //For g_assert()
+#include <iostream>
+#include <cstdlib> //For EXIT_SUCCESS and EXIT_FAILURE
+
+static Glom::sharedptr<const Glom::LayoutItem_Field> get_lookup_field(const Glom::Document::type_list_lookups& container, const Glib::ustring& table_name, const Glib::ustring& field_name, Glom::sharedptr<const Glom::Relationship>& relationship)
+{
+  relationship.clear();
+  Glom::sharedptr<const Glom::LayoutItem_Field> result;
+
+  for(Glom::Document::type_list_lookups::const_iterator iter = container.begin(); iter != container.end(); ++iter)
+  {
+    const Glom::sharedptr<const Glom::LayoutItem_Field> layout_item = iter->first;
+    if(!layout_item)
+      return result;
+
+    const Glom::sharedptr<const Glom::Relationship> this_relationship = iter->second;
+    if(!this_relationship)
+      return result;
+
+    if(layout_item->get_table_used(table_name) != table_name)
+      return result;
+
+    if(layout_item->get_name() == field_name)
+    {
+      relationship = this_relationship;
+      return layout_item;
+    }
+  }
+
+  return result;
+}
+
+static bool contains_field(const Glom::Document::type_list_lookups& container, const Glib::ustring& table_name, const Glib::ustring& field_name)
+{
+  Glom::sharedptr<const Glom::Relationship> relationship;
+  return get_lookup_field(container, table_name, field_name, relationship);
+}
+
+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 = "invoice_lines";
+  Glom::sharedptr<const Glom::Field> primary_key_field = document.get_field_primary_key(table_name);
+  if(!primary_key_field)
+  {
+    std::cerr << "Failure: primary_key_field is empty." << std::endl;
+    return false;
+  }
+
+
+  // Get the fields whose values should be looked up when a field changes:
+  const Glom::Document::type_list_lookups lookups = document.get_lookup_fields(table_name, "product_id");
+  if(lookups.size() != 3)
+  {
+    std::cerr << "Failure: Unexpected number of lookups: " << lookups.size() << std::endl;
+    return false;
+  }
+
+  if(!contains_field(lookups, table_name, "product_price"))
+  {
+    std::cerr << "Failure: Expected lookup field not found." << std::endl;
+    return false;
+  }
+
+  if(!contains_field(lookups, table_name, "product_name"))
+  {
+    std::cerr << "Failure: Expected lookup field not found." << std::endl;
+    return false;
+  }
+
+  if(!contains_field(lookups, table_name, "vat_percentage"))
+  {
+    std::cerr << "Failure: Expected lookup field not found." << std::endl;
+    return false;
+  }
+
+  const Glib::ustring field_name = "product_price";
+  Glom::sharedptr<const Glom::Relationship> relationship;
+  const Glom::sharedptr<const Glom::LayoutItem_Field> layout_field = 
+    get_lookup_field(lookups, table_name, field_name, relationship);
+  if(!layout_field)
+  {
+    std::cerr << "Failure: The lookup field is empty." << std::endl;
+    return false;
+  }
+ 
+  if(!relationship)
+  {
+    std::cerr << "Failure: The lookup relationship is empty." << std::endl;
+    return false;
+  }
+
+  if(relationship->get_to_table() != "products")
+  {
+    std::cerr << "Failure: The relationship's to table is unexpected." << std::endl;
+    return false;
+  }
+
+  if(layout_field->get_table_used(table_name) != table_name)
+  {
+    std::cerr << "Failure: The lookup field's table is unexpected" << std::endl;
+    return false;
+  }
+
+  if(layout_field->get_name() != field_name)
+  {
+    std::cerr << "Failure: The lookup field's name is unexpected." << std::endl;
+    return false;
+  }
+
+  const Glom::sharedptr<const Glom::Field> field = layout_field->get_full_field_details();
+  if(!field)
+  {
+    std::cerr << "Failure: The lookup item's field is empty." << std::endl;
+    return false;
+  }
+
+  if(field->get_name() != field_name)
+  {
+    std::cerr << "Failure: The lookup item's field name is unexpected." << std::endl;
+    return false;
+  }
+
+  if(!field->get_is_lookup())
+  {
+    std::cerr << "Failure: The lookup field is not a lookup." << std::endl;
+    return false;
+  }
+
+  if(relationship != field->get_lookup_relationship())
+  {
+    std::cerr << "Failure: The lookup field's relationship is not expected." << std::endl;
+    return false;
+  }
+
+  //Lookup the value from the related record.
+  //TODO: 
+  const Glom::sharedptr<Glom::Field> field_source = 
+    document.get_field(relationship->get_to_table(), "price");
+  const Gnome::Gda::Value value = Glom::DbUtils::get_lookup_value(&document, 
+    table_name, relationship, field_source, Gnome::Gda::Value(2));
+
+  const GType expected_type = 
+    (hosting_mode != Glom::Document::HOSTING_MODE_SQLITE ? GDA_TYPE_NUMERIC : G_TYPE_DOUBLE);
+  if(value.get_value_type() != expected_type)
+  {
+    std::cerr << "Failure: The value has an unexpected type: " << 
+      g_type_name(value.get_value_type()) << std::endl;
+    return false;
+  }
+
+  if(Glom::Conversions::get_double_for_gda_value_numeric(value) != 3.5f)
+  {
+    std::cerr << "Failure: The value has an unexpected value: " << value.to_string() << 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]