glom r1775 - in trunk: . glom glom/libglom/data_structure



Author: arminb
Date: Fri Dec  5 17:05:10 2008
New Revision: 1775
URL: http://svn.gnome.org/viewvc/glom?rev=1775&view=rev

Log:
2008-12-05  Armin Burgmeier  <armin openismus com>

	* glom/libglom/data_structure/glomconversions.h: 
	* glom/libglom/data_structure/glomconversions.cc: Replaced
	get_escaped_binary_data by escape_binary_data_postgres() and
	escape_binary_data_sqlite(). SQLite uses a different format for binary
	data.
	(get_text_for_gda_value): Allow DATE and TIME values to be strings,
	and parse them always in ISO format. This is used for SQLite within
	which we store dates and times as ISO-formatted strings.

	* glom/libglom/data_structure/field.cc (glom_unescape_text): Don't
	unescape double quotes, as they are not escaped in glom_escape_text
	either.
	(sql): Use the escape_binary_data_postgres or
	escape_binary_data_sqlite functions from glomconversions, depending on
	the preferred format.
	(from_sql): Use the unescape_binary_data_postgres or
	unescape_binary_data_sqlite functions from glomconversions, depending
	on the preferred format.


Modified:
   trunk/ChangeLog
   trunk/glom/base_db.cc
   trunk/glom/libglom/data_structure/field.cc
   trunk/glom/libglom/data_structure/glomconversions.cc
   trunk/glom/libglom/data_structure/glomconversions.h

Modified: trunk/glom/base_db.cc
==============================================================================
--- trunk/glom/base_db.cc	(original)
+++ trunk/glom/base_db.cc	Fri Dec  5 17:05:10 2008
@@ -1253,7 +1253,7 @@
       {
         const type_vecStrings vec_values = Utils::string_separate(row_data, ",", true /* ignore , inside quotes */);
         if(true) //vec_values.size() == fields_count)
-	{
+        {
           //Do not allow any unescaped newlines in the row data.
           //Note that this is checking for newlines ("\n"), not escaped newlines ("\\n").
           Glib::ustring converted_row_data;
@@ -1277,10 +1277,10 @@
                 bool success;
                 const Gnome::Gda::Value value = vec_fields[i]->from_sql(vec_values[i], Field::SQL_FORMAT_POSTGRES, success);
 
-		if(success)
+                if(success)
                   converted_row_data += vec_fields[i]->sql(value, connection_pool->get_sql_format());
-		else
-		  converted_row_data += "''";
+                else
+                  converted_row_data += "''";
               }
 
               actual_row_data = &converted_row_data;
@@ -1291,7 +1291,7 @@
             if(query_execute(strQuery))
               //std::cout << "debug: after query: " << strQuery << std::endl;
               insert_succeeded = true;
-	    else
+            else
             {
               insert_succeeded = false;
               break;
@@ -1988,12 +1988,12 @@
 #ifdef GLIBMM_EXCEPTIONS_ENABLED
           sharedptr<SharedConnection> sharedconnection = connect_to_server(0 /* parent window */);
 #else
-	  std::auto_ptr<ExceptionConnection> error;
-	  sharedptr<SharedConnection> sharedconnection = connect_to_server(0 /* parent window */, error);
-	  // TODO: Rethrow?
+          std::auto_ptr<ExceptionConnection> error;
+          sharedptr<SharedConnection> sharedconnection = connect_to_server(0 /* parent window */, error);
+          // TODO: Rethrow?
 #endif
 
-	  g_assert(sharedconnection);
+          g_assert(sharedconnection);
 
           refCalcProgress.m_value = glom_evaluate_python_function_implementation(field->get_glom_type(), field->get_calculation(), field_values, get_document(), field_in_record.m_table_name, sharedconnection->get_gda_connection());
 

Modified: trunk/glom/libglom/data_structure/field.cc
==============================================================================
--- trunk/glom/libglom/data_structure/field.cc	(original)
+++ trunk/glom/libglom/data_structure/field.cc	Fri Dec  5 17:05:10 2008
@@ -291,6 +291,8 @@
       result += '\'';
       ++ iter;
     }
+    /* double quotes are not escaped, so don't unescape them. */
+#if 0
     // Unescape "" to ".
     else if(*iter == '\"')
     {
@@ -299,6 +301,7 @@
       result += '\"';
       ++ iter;
     }
+#endif
     // Escape sequence beginning with backslash.
     else if(*iter == '\\')
     {
@@ -425,15 +428,21 @@
         const guchar* buffer = value.get_binary(buffer_length);
         if(buffer && buffer_length > 0)
         {
-          //Get the escaped text that represents the binary data:
-          const std::string escaped_binary_data = Conversions::get_escaped_binary_data((guint8*)buffer, buffer_length);
-          //Now escape that text (to convert \ to \\, for instance):
-          //The E prefix indicates ""escape" string constants, which are an extension to the SQL standard"
-          //Otherwise, we get a warning when using large escaped strings:
           if(format == SQL_FORMAT_POSTGRES)
-            str = "E" + glom_escape_text(escaped_binary_data) /* has quotes */ + "::bytea";
+          {
+            //Get the escaped text that represents the binary data:
+            const std::string escaped_binary_data = Conversions::escape_binary_data_postgres((guint8*)buffer, buffer_length);
+            //Now escape that text (to convert \ to \\, for instance):
+            //The E prefix indicates ""escape" string constants, which are an extension to the SQL standard"
+            //Otherwise, we get a warning when using large escaped strings:
+            if(format == SQL_FORMAT_POSTGRES)
+              str = "E" + glom_escape_text(escaped_binary_data) /* has quotes */ + "::bytea";
+          }
           else
-            str = glom_escape_text(escaped_binary_data);
+          {
+            const std::string escaped_binary_data = Conversions::escape_binary_data_sqlite((guint8*)buffer, buffer_length);
+            str = "x'" + escaped_binary_data + "'";
+          }
         }
       }
       else
@@ -502,27 +511,58 @@
       // is expensive, and we only check for ASCII stuff anyway.
       const std::string& raw = str.raw();
 
+      Glib::ustring unescaped;
       switch(format)
       {
       case SQL_FORMAT_POSTGRES:
         if(raw.length() >= 10 &&
            raw.compare(0, 2, "E'") == 0 && raw.compare(raw.length() - 8, 8, "'::bytea") == 0)
         {
-          std::string unescaped = glom_unescape_text(raw.substr(1, raw.length() - 8));
-          NumericFormat format_ignored; //Because we use ISO format.
-          return Conversions::parse_value(m_glom_type, unescaped, format_ignored, success, true);
+          unescaped = glom_unescape_text(raw.substr(1, raw.length() - 8));
+	}
+	// The E can be omitted, and it is in some example files, such as
+	// example_smallbusiness.glom. We need to parse this to convert the
+	// data into sqlite format, when the small bussiness example is loaded
+	// into a sqlite database.
+	else if(raw.length() >= 9 &&
+	        raw.compare(0, 1, "'") == 0 && raw.compare(raw.length() - 8, 8, "'::bytea") == 0)
+	{
+          unescaped = glom_unescape_text(raw.substr(0, raw.length() - 7));
         }
-        else
+
+        if(!unescaped.empty())
         {
-          success = false;
-          return Gnome::Gda::Value();
+          gsize length;
+          guint8* binary_data = Conversions::unescape_binary_data_postgres(unescaped, length);
+          if(binary_data)
+          {
+            Gnome::Gda::Value value;
+            value.set(binary_data, length);
+            g_free(binary_data);
+            return value;
+          }
         }
+
+        success = false;
+        return Gnome::Gda::Value();
       case SQL_FORMAT_SQLITE:
+        if(raw.length() >= 3 &&
+           (raw.compare(0, 2, "x'") == 0 || raw.compare(0, 2, "X'")) &&
+           raw.compare(raw.length() - 1, 1, "'") == 0)
         {
-          std::string unescaped = glom_unescape_text(raw);
-          NumericFormat format_ignored; //Because we use ISO format.
-          return Conversions::parse_value(m_glom_type, unescaped, format_ignored, success, true);
+          gsize length;
+          guint8* binary_data = Conversions::unescape_binary_data_sqlite(raw.substr(2, raw.length()), length);
+          if(binary_data)
+          {
+            Gnome::Gda::Value value;
+            value.set(binary_data, length);
+            g_free(binary_data);
+            return value;
+          }
         }
+
+        success = false;
+        return Gnome::Gda::Value();
       default:
         g_assert_not_reached();
         break;

Modified: trunk/glom/libglom/data_structure/glomconversions.cc
==============================================================================
--- trunk/glom/libglom/data_structure/glomconversions.cc	(original)
+++ trunk/glom/libglom/data_structure/glomconversions.cc	Fri Dec  5 17:05:10 2008
@@ -35,6 +35,24 @@
 #include <iomanip>
 #include <string.h> // for strlen, memset, strcmp
 
+namespace
+{
+  char hextochar(guint8 hex)
+  {
+    if(hex < 10) return '0' + hex;
+    if(hex < 16) return 'a' - 10 + hex;
+    return '\0';
+  }
+
+  guint8 chartohex(char c)
+  {
+    if(c >= '0' && c <= '9') return c - '0';
+    if(c >= 'a' && c <= 'f') return c - 'a' + 10;
+    if(c >= 'A' && c <= 'F') return c - 'A' + 10;
+    return 0;
+  }
+}
+
 namespace Glom
 {
 
@@ -300,18 +318,39 @@
     return "";
   }
 
-  if( (glom_type == Field::TYPE_DATE) && (value.get_value_type() == G_TYPE_DATE))
+  if(glom_type == Field::TYPE_DATE)
   {
-    Glib::Date gda_date = value.get_date();
-    //Gnome::Gda::Date gda_date = value.get_date();
-
-    //tm the_c_time = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     tm the_c_time;
     memset(&the_c_time, 0, sizeof(the_c_time));
 
-    the_c_time.tm_year = gda_date.get_year() - 1900; //C years start are the AD year - 1900. So, 01 is 1901.
-    the_c_time.tm_mon = gda_date.get_month() - 1; //C months start at 0.
-    the_c_time.tm_mday = gda_date.get_day(); //starts at 1
+    if(value.get_value_type() == G_TYPE_STRING)
+    {
+      // If a date is contained in a string type instead of a date type,
+      // which can happen if the database system does not support dates
+      // natively, then the string representation is always in ISO format.
+      bool success;
+      the_c_time = parse_date(value.get_string(), std::locale::classic(), success);
+      if(!success)
+        std::cerr << "Conversions::get_text_for_gda_value(): Failed to convert string-represented date value" << std::endl;
+    }
+    else if(value.get_value_type() == G_TYPE_DATE)
+    {
+      Glib::Date gda_date = value.get_date();
+      //Gnome::Gda::Date gda_date = value.get_date();
+
+      //tm the_c_time = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+      the_c_time.tm_year = gda_date.get_year() - 1900; //C years start are the AD year - 1900. So, 01 is 1901.
+      the_c_time.tm_mon = gda_date.get_month() - 1; //C months start at 0.
+      the_c_time.tm_mday = gda_date.get_day(); //starts at 1
+    }
+    else
+    {
+      std::cerr << "Conversions::get_text_for_gda_value(): glom field type is DATE but GdaValue type is: " << g_type_name(value.get_value_type()) << std::endl;
+
+      // Default
+      the_c_time.tm_mday = 1;
+    }
 
     return format_date(the_c_time, locale, iso_format);
 
@@ -323,15 +362,32 @@
   }
   else if((glom_type == Field::TYPE_TIME) && (value.get_value_type() == GDA_TYPE_TIME))
   {
-    Gnome::Gda::Time gda_time = value.get_time();
-
-    //tm the_c_time = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     tm the_c_time;
     memset(&the_c_time, 0, sizeof(the_c_time));
+    if(value.get_value_type() == G_TYPE_STRING)
+    {
+      // If a time is contained in a string type instead of a gda time type,
+      // which can happen if the database system does not support times
+      // natively, then the string representation is always in ISO format.
+      bool success;
+      the_c_time = parse_time(value.get_string(), std::locale::classic(), success);
+      if(!success)
+        std::cerr << "Conversions::get_text_for_gda_value(): Failed to convert string-represented time value" << std::endl;
+    }
+    else if(value.get_value_type() == GDA_TYPE_TIME)
+    {
+      Gnome::Gda::Time gda_time = value.get_time();
+
+      //tm the_c_time = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 
-    the_c_time.tm_hour = gda_time.hour;
-    the_c_time.tm_min = gda_time.minute;
-    the_c_time.tm_sec = gda_time.second;
+      the_c_time.tm_hour = gda_time.hour;
+      the_c_time.tm_min = gda_time.minute;
+      the_c_time.tm_sec = gda_time.second;
+    }
+    else
+    {
+      std::cerr << "Conversions::get_text_for_gda_value(): glom field type is TIME but GdaValue type is: " << g_type_name(value.get_value_type()) << std::endl;
+    }
 
     return format_time(the_c_time, locale, iso_format);
   }
@@ -394,12 +450,13 @@
   }
   else if(glom_type == Field::TYPE_IMAGE)
   {
-    //Return the binary-as-escaped-text format, suitable for use in the document. 
+    //Return the binary-as-escaped-text format, suitable for use in the document.
+    //TODO: Where do we need this? Do we need to have this in SQLite format sometimes?
     std::string result;
     long buffer_length;
     const guchar* buffer = value.get_binary(buffer_length);
     if(buffer && buffer_length > 0)
-      result = Conversions::get_escaped_binary_data((guint8*)buffer, buffer_length);
+      result = Conversions::escape_binary_data_postgres((guint8*)buffer, buffer_length);
 
     return result;
   }
@@ -552,10 +609,11 @@
   {
     //We assume that the text is the same (escaped text) format that we use in the document when saving images:
     //(The SQL format).
+    // TODO: For what do we need this? Does this have to be in SQLite format sometimes?
     Gnome::Gda::Value result;
 
     size_t buffer_binary_length = 0;
-    guchar* buffer_binary = Glom_PQunescapeBytea((const guchar*)text.c_str() /* must be null-terminated */, &buffer_binary_length); //freed by us later.
+    guchar* buffer_binary = Conversions::unescape_binary_data_postgres(text, buffer_binary_length); //freed by us later.
     if(buffer_binary)
     {
       result.set(buffer_binary, buffer_binary_length);
@@ -1043,7 +1101,7 @@
   return tmpbuf;
 }
 
-Glib::ustring Conversions::get_escaped_binary_data(guint8* buffer, size_t buffer_size)
+Glib::ustring Conversions::escape_binary_data_postgres(guint8* buffer, size_t buffer_size)
 {
   //g_warning("Conversions::get_escaped_binary_data: debug: buffer ");
   //for(int i = 0; i < 10; ++i)
@@ -1085,6 +1143,44 @@
   return result;
 }
 
+Glib::ustring Conversions::escape_binary_data_sqlite(guint8* buffer, size_t buffer_size)
+{
+  // The sqlite format is two characters representing a byte in hexadecimal
+  // notation.
+  Glib::ustring result;
+  result.reserve(buffer_size*2);
+  for(unsigned int i = 0; i < buffer_size; ++ i)
+  {
+    result += hextochar((buffer[i] & 0xf0) >> 4);
+    result += hextochar((buffer[i] & 0x0f)     );
+  }
+
+  return result;
+}
+
+guint8* Conversions::unescape_binary_data_postgres(const Glib::ustring& escaped_binary_data, size_t& length)
+{
+  return Glom_PQunescapeBytea((const guchar*)escaped_binary_data.c_str(), &length);
+}
+
+guint8* Conversions::unescape_binary_data_sqlite(const Glib::ustring& escaped_binary_data, size_t& length)
+{
+  g_assert(escaped_binary_data.bytes() % 2 == 0);
+
+  length = escaped_binary_data.bytes()/2;
+  const char* in_data = escaped_binary_data.c_str();
+  // Use malloc here, since unescape_binary_data_postgres also uses malloc
+  // in Glom_PQunescapeBytea and we want the API to stay consistent.
+  guint8* out_data = static_cast<guint8*>(malloc(length));
+  if(!out_data) { length = 0; return NULL; }
+
+  // The sqlite format is two characters representing a byte in hexadecimal
+  // notation.
+  for(unsigned int i = 0; i < length; ++i)
+    out_data[i] = chartohex(in_data[2*i] << 4) | chartohex(in_data[2*i+1]);
+  return out_data;
+}
+
 Gnome::Gda::Value Conversions::convert_value(const Gnome::Gda::Value& value, Field::glom_field_type target_glom_type)
 {
   const GType gvalue_type = value.get_value_type();

Modified: trunk/glom/libglom/data_structure/glomconversions.h
==============================================================================
--- trunk/glom/libglom/data_structure/glomconversions.h	(original)
+++ trunk/glom/libglom/data_structure/glomconversions.h	Fri Dec  5 17:05:10 2008
@@ -81,7 +81,10 @@
   Gnome::Gda::Value get_example_value(Field::glom_field_type field_type);
 
   ///Get a string representing binary data, for SQL
-  Glib::ustring get_escaped_binary_data(guint8* buffer, size_t buffer_size);
+  Glib::ustring escape_binary_data_postgres(guint8* buffer, size_t buffer_size);
+  Glib::ustring escape_binary_data_sqlite(guint8* buffer, size_t buffer_size);
+  guint8* unescape_binary_data_postgres(const Glib::ustring& escaped_binary_data, size_t& length);
+  guint8* unescape_binary_data_sqlite(const Glib::ustring& escaped_binary_data, size_t& length);
 
   Gnome::Gda::Value convert_value(const Gnome::Gda::Value& value, Field::glom_field_type target_glom_type);
 



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