[libgda] More gda_sql_identifier_quote() work



commit 96b82725a058691257541c5c653db5991985bc58
Author: Vivien Malerba <malerba gnome-db org>
Date:   Thu Jul 16 21:35:30 2009 +0200

    More gda_sql_identifier_quote() work
    
    * Fixed documentation
    * added NR test
    * fixed the MySQL provider (mainly meta data and stability)

 libgda/gda-data-select.c                     |    8 +-
 libgda/gda-meta-store.c                      |    3 +
 libgda/gda-server-provider.h                 |    2 +-
 libgda/gda-util.c                            |  233 ++++++++++++++++++--
 libgda/sqlite/Makefile.am                    |    5 +-
 libgda/sqlite/gda-sqlite-meta.c              |  100 ++++++---
 libgda/sqlite/gda-sqlite-provider.c          |  190 ++++++++++++++++-
 libgda/sqlite/gda-sqlite-recordset.c         |    1 +
 libgda/sqlite/{utils.c => gda-sqlite-util.c} |   25 ++-
 libgda/sqlite/gda-sqlite-util.h              |   40 ++++
 libgda/sqlite/gda-sqlite.h                   |    6 -
 providers/mysql/gda-mysql-meta.c             |   86 ++++----
 providers/mysql/gda-mysql-provider.c         |  256 +++++++++++++++++++---
 providers/mysql/gda-mysql-provider.h         |    2 +
 providers/mysql/gda-mysql-recordset.c        |  244 +++++++++++++--------
 providers/mysql/gda-mysql.h                  |    2 +-
 providers/postgres/gda-postgres-provider.c   |  308 ++++++++++++++++++--------
 tests/test-identifiers-quotes.c              |  288 ++++++++++++------------
 18 files changed, 1324 insertions(+), 475 deletions(-)
---
diff --git a/libgda/gda-data-select.c b/libgda/gda-data-select.c
index 5b16f2c..ac361d6 100644
--- a/libgda/gda-data-select.c
+++ b/libgda/gda-data-select.c
@@ -1805,8 +1805,14 @@ gda_data_select_get_value_at (GdaDataModel *model, gint col, gint row, GError **
 	GValue *retval = gda_row_get_value (prow, col);
 	if (gda_row_value_is_valid (prow, retval))
 		return retval;
-	else
+	else {
+		gchar *str;
+		str = g_strdup_printf (_("Unable to get value for row %d and column %d"), row, col);
+		g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
+			     "%s", str);
+		g_free (str);
 		return NULL;
+	}
 }
 
 static GdaValueAttribute
diff --git a/libgda/gda-meta-store.c b/libgda/gda-meta-store.c
index 6a07a92..ccce80e 100644
--- a/libgda/gda-meta-store.c
+++ b/libgda/gda-meta-store.c
@@ -2556,12 +2556,15 @@ gda_meta_store_modify_v (GdaMetaStore *store, const gchar *table_name,
 						retval = FALSE;
 						if (error && !(*error))
 							g_propagate_error (error, suggest_reports_error);
+						else
+							g_error_free (suggest_reports_error);
 						g_object_unref (wrapped_data);
 						goto out;
 					}
 				}
 			}
 		}
+		g_object_unref (wrapped_data);
 	}
 	
 	if (!store->priv->override_mode) {
diff --git a/libgda/gda-server-provider.h b/libgda/gda-server-provider.h
index 1e98141..e794aa9 100644
--- a/libgda/gda-server-provider.h
+++ b/libgda/gda-server-provider.h
@@ -297,7 +297,7 @@ struct _GdaServerProviderClass {
 	/* SQL identifiers quoting */
 	gchar                  *(* identifier_quote)    (GdaServerProvider *provider, GdaConnection *cnc,
 							 const gchar *id,
-							 gboolean meta_store_convention, gboolean force_quotes);
+							 gboolean for_meta_store, gboolean force_quotes);
 
 	/* Async. handling@ */
 	gboolean                (*handle_async)          (GdaServerProvider *provider, GdaConnection *cnc, GError **error);
diff --git a/libgda/gda-util.c b/libgda/gda-util.c
index 66df7a2..3833a56 100644
--- a/libgda/gda-util.c
+++ b/libgda/gda-util.c
@@ -862,7 +862,7 @@ gda_compute_unique_table_row_condition_with_cnc (GdaConnection *cnc, GdaSqlState
 				/* left operand */
 				gchar *str;
 				opexpr = gda_sql_expr_new (GDA_SQL_ANY_PART (op));
-				str = gda_sql_identifier_quote (tcol->column_name, cnc, NULL, TRUE, FALSE);
+				str = gda_sql_identifier_quote (tcol->column_name, cnc, NULL, FALSE, FALSE);
 				g_value_take_string (opexpr->value = gda_value_new (G_TYPE_STRING), str);
 
 				op->operands = g_slist_append (op->operands, opexpr);
@@ -970,7 +970,7 @@ gda_compute_dml_statements (GdaConnection *cnc, GdaStatement *select_stmt, gbool
 	
 	/* actual statement structure's computation */
 	gchar *tmp;
-	tmp = gda_sql_identifier_quote (target->table_name, cnc, NULL, TRUE, FALSE);
+	tmp = gda_sql_identifier_quote (target->table_name, cnc, NULL, FALSE, FALSE);
 	if (insert_stmt) {
 		sql_ist = gda_sql_statement_new (GDA_SQL_STATEMENT_INSERT);
 		ist = (GdaSqlStatementInsert*) sql_ist->contents;
@@ -1034,7 +1034,7 @@ gda_compute_dml_statements (GdaConnection *cnc, GdaStatement *select_stmt, gbool
 		g_hash_table_insert (fields_hash, selfield->field_name, GINT_TO_POINTER (1));
 
 		gchar *str;
-		str = gda_sql_identifier_quote (selfield->field_name, cnc, NULL, TRUE, FALSE);
+		str = gda_sql_identifier_quote (selfield->field_name, cnc, NULL, FALSE, FALSE);
 		if (insert_stmt) {
 			GdaSqlField *field;
 			field = gda_sql_field_new (GDA_SQL_ANY_PART (ist));
@@ -1577,37 +1577,181 @@ gda_sql_identifier_split (const gchar *id)
 
 static gboolean _sql_identifier_needs_quotes (const gchar *str);
 
-
 /**
  * gda_sql_identifier_quote
  * @id: an SQL identifier
  * @cnc: a #GdaConnection object, or %NULL
  * @prov: a #GdaServerProvider object, or %NULL
- * @meta_store_convention: set to %TRUE if @id respects the #GdaMetaStore
- *                         representation of SQL identifiers, or if it is a user input
+ * @for_meta_store set to %TRUE if the returned string will be used in a #GdaMetaStore
  * @force_quotes: set to %TRUE to force the returned string to be quoted
  *
- * Use this function for any SQL identifier to make sure it is correctly formatted
- * to be used with @cnc (if @cnc is %NULL, then the standard SQL quoting rules will be applied).
- *
- * If @id already has quotes, then this function returns a copy of @id, except that the quotes may be
- * replaced by database specific characters (such as the backquote for MySQL).
+ * Use this function for any SQL identifier to make sure that:
+ * <itemizedlist>
+ *   <listitem>
+ *     <para>it is correctly formatted
+ *           to be used with @cnc (if @cnc is %NULL, then the standard SQL quoting rules will be applied) if
+ *           @for_meta_store is %FALSE;
+ *     </para>
+ *   </listitem>
+ *   <listitem>
+ *     <para>it is correctly formatted to be used with the #GdaMetaStore's object associated to @cnc
+ *           is @for_meta_store is %TRUE.
+ *     </para>
+ *   </listitem>
+ * </itemizedlist>
  *
- * If @id has no quotes, if none are requited, and if @force_quotes if %FALSE, 
- * then this function returns a copy if @id. The criteria to
- * determine if @id needs quotes depends on the database provider associated to @cnc, and if @cnc is %NULL, then
- * in all the following cases quotes are required:
+ * The @force_quotes allow some control of how to interpret @id: if %FALSE, then @id will be left
+ * unchanged most of the time (except for example if it's a reserved keyword), otherwise
+ * if @force_quotes is %TRUE, then the returned string will most probably have quotes around it
+ * to request that the database keep the case sensitiveness (but again, this may vary depending
+ * on the database being accessed through @cnc).
+ *
+ * For example, the following table gives the result of this function depending on the arguments
+ * when @cnc is %NULL (and @prov is also %NULL):
+ * <table frame="all">
+ *  <tgroup cols="6" colsep="1" rowsep="1" align="justify">
+ *    <thead>
+ *      <row>
+ *        <entry>id</entry>
+ *        <entry>for_meta_store=%FALSE, force_quotes=%FALSE</entry>
+ *        <entry>for_meta_store=%TRUE, force_quotes=%FALSE</entry>
+ *        <entry>for_meta_store=%FALSE, force_quotes=%TRUE</entry>
+ *        <entry>for_meta_store=%TRUE, force_quotes=%TRUE</entry>
+ *        <entry>remark</entry>
+ *      </row>
+ *    </thead>
+ *    <tbody>
+ *      <row>
+ *        <entry>"double word"</entry>
+ *        <entry>"double word"</entry>
+ *        <entry>"double word"</entry>
+ *        <entry>"double word"</entry>
+ *        <entry>"double word"</entry>
+ *        <entry>non allowed character in SQL identifier</entry>
+ *      </row>
+ *      <row>
+ *        <entry>"CapitalTest"</entry>
+ *        <entry>"CapitalTest"</entry>
+ *        <entry>"CapitalTest"</entry>
+ *        <entry>"CapitalTest"</entry>
+ *        <entry>"CapitalTest"</entry>
+ *        <entry>Mixed case SQL identifier, already quoted</entry>
+ *      </row>
+ *      <row>
+ *        <entry>CapitalTest</entry>
+ *        <entry>CapitalTest</entry>
+ *        <entry>capitaltest</entry>
+ *        <entry>"CapitalTest"</entry>
+ *        <entry>"CapitalTest"</entry>
+ *        <entry>Mixed case SQL identifier, non quoted</entry>
+ *      </row>
+ *      <row>
+ *        <entry>"mytable"</entry>
+ *        <entry>"mytable"</entry>
+ *        <entry>mytable</entry>
+ *        <entry>"mytable"</entry>
+ *        <entry>mytable</entry>
+ *        <entry>All lowser case, quoted</entry>
+ *      </row>
+ *      <row>
+ *        <entry>mytable</entry>
+ *        <entry>mytable</entry>
+ *        <entry>mytable</entry>
+ *        <entry>"mytable"</entry>
+ *        <entry>mytable</entry>
+ *        <entry>All lowser case</entry>
+ *      </row>
+ *      <row>
+ *        <entry>MYTABLE</entry>
+ *        <entry>MYTABLE</entry>
+ *        <entry>mytable</entry>
+ *        <entry>"MYTABLE"</entry>
+ *        <entry>"MYTABLE"</entry>
+ *        <entry>All upper case</entry>
+ *      </row>
+ *      <row>
+ *        <entry>"MYTABLE"</entry>
+ *        <entry>"MYTABLE"</entry>
+ *        <entry>"MYTABLE"</entry>
+ *        <entry>"MYTABLE"</entry>
+ *        <entry>"MYTABLE"</entry>
+ *        <entry>All upper case, quoted</entry>
+ *      </row>
+ *      <row>
+ *        <entry>desc</entry>
+ *        <entry>"desc"</entry>
+ *        <entry>"desc"</entry>
+ *        <entry>"desc"</entry>
+ *        <entry>"desc"</entry>
+ *        <entry>SQL reserved keyword</entry>
+ *      </row>
+ *      <row>
+ *        <entry>5ive</entry>
+ *        <entry>"5ive"</entry>
+ *        <entry>"5ive"</entry>
+ *        <entry>"5ive"</entry>
+ *        <entry>"5ive"</entry>
+ *        <entry>SQL identifier starting with a digit</entry>
+ *      </row>
+ *    </tbody>
+ *  </tgroup>
+ * </table>
+ *
+ * Here are a few examples of when and how to use this function:
  * <itemizedlist>
- *  <listitem><para>If @id's 1st character is a digit</para></listitem>
- *  <listitem><para>If @id contains other characters than digits, letters and the '_', '$' and '#'</para></listitem>
- *  <listitem><para>If @id is an SQL reserved keyword</para></listitem>
+ *   <listitem>
+ *     <para>
+ *       When creating a table, the user has entered the table name, this function can be used to
+ *       create a valid SQL identifier from the user provided table name:
+ *       <programlisting>
+ * gchar *user_sqlid=...
+ * gchar *valid_sqlid = gda_sql_identifier_quote (user_sqlid, cnc, NULL, FALSE, FALSE);
+ * gchar *sql = g_strdup_printf ("CREATE TABLE %s ...", valid_sqlid);
+ * g_free (valid_sqlid);
+ *       </programlisting>
+ *       Note that this is an illustration and creating a table should be sone using a #GdaServerOperation
+ *       object.
+ *     </para>
+ *   </listitem>
+ *   <listitem>
+ *     <para>
+ *      When updating the meta data associated to a table which has been created with the code
+ *      above:
+ *      <programlisting>
+ * GValue table_name_value = { 0 };
+ * gchar* column_names[] = { (gchar*)"table_name" };
+ * GValue* column_values[] = { &table_name_value };
+ * GdaMetaContext mcontext = { (gchar*)"_tables", 1, column_names, column_values };
+ * g_value_init (&amp;table_name_value, G_TYPE_STRING);
+ * g_value_take_string (&amp;table_name_value, gda_sql_identifier_quote (user_sqlid, cnc, NULL, TRUE, FALSE);
+ * gda_connection_update_meta_store (cnc, &amp;mcontext, NULL);
+ * g_value_reset (&amp;table_name_value);
+ *       </programlisting>
+ *     </para>
+ *   </listitem>
+ *   <listitem>
+ *     <para>
+ *      When using a #GdaMetaStruct object to fetch information about a table (which has been created with
+ *      the code above):
+ *      <programlisting>
+ * GValue table_name_value = { 0 };
+ * g_value_init (&amp;table_name_value, G_TYPE_STRING);
+ * g_value_take_string (&amp;table_name_value, gda_sql_identifier_quote (user_sqlid, cnc, NULL, TRUE, FALSE);
+ * GdaMetaDbObject *dbo;
+ * dbo = gda_meta_struct_complement (mstruct, GDA_META_DB_TABLE, NULL, NULL, &amp;table_name_value, NULL);
+ * g_value_reset (&amp;table_name_value);
+ *       </programlisting>
+ *     </para>
+ *   </listitem>
  * </itemizedlist>
  *
- * If @force_quotes is %TRUE, then the this function always returns a quoted SQL identifier.
  *
  * Note that @id must not be a composed SQL identifier (such as "mytable.mycolumn" which should be
  * treated as the "mytable" and "mycolumn" SQL identifiers). If unsure, use gda_sql_identifier_split().
  *
+ * Also note that if @cnc is %NULL, then it's possible to pass an non %NULL @prov to have a result specific
+ * to @prov.
+ *
  * For more information, see the <link linkend="gen:sql_identifiers">SQL identifiers and abstraction</link> and
  * <link linkend="information_schema:sql_identifiers">SQL identifiers in meta data</link> sections.
  *
@@ -1618,7 +1762,7 @@ static gboolean _sql_identifier_needs_quotes (const gchar *str);
  */
 gchar *
 gda_sql_identifier_quote (const gchar *id, GdaConnection *cnc, GdaServerProvider *prov,
-			  gboolean meta_store_convention, gboolean force_quotes)
+			  gboolean for_meta_store, gboolean force_quotes)
 {
 	g_return_val_if_fail (id && *id, NULL);
 	if (prov)
@@ -1631,9 +1775,56 @@ gda_sql_identifier_quote (const gchar *id, GdaConnection *cnc, GdaServerProvider
 			prov = gda_connection_get_provider (cnc);
 	}
 
-	if (prov && PROV_CLASS (prov)->identifier_quote) {
+	if (prov && PROV_CLASS (prov)->identifier_quote)
 		return PROV_CLASS (prov)->identifier_quote (prov, cnc, id,
-							    meta_store_convention, force_quotes);
+							    for_meta_store, force_quotes);
+
+	if (for_meta_store) {
+		gchar *tmp, *ptr;
+		tmp = _remove_quotes (g_strdup (id));
+		if (is_keyword (tmp)) {
+			ptr = gda_sql_identifier_add_quotes (tmp);
+			g_free (tmp);
+			return ptr;
+		}
+		else if (force_quotes) {
+			/* quote if non LC characters or digits at the 1st char or non allowed characters */
+			for (ptr = tmp; *ptr; ptr++) {
+				if (((*ptr >= 'a') && (*ptr <= 'z')) ||
+				    ((*ptr >= '0') && (*ptr <= '9') && (ptr != tmp)) ||
+				    (*ptr == '_'))
+					continue;
+				else {
+					ptr = gda_sql_identifier_add_quotes (tmp);
+					g_free (tmp);
+					return ptr;
+				}
+			}
+			return tmp;
+		}
+		else {
+			for (ptr = tmp; *ptr; ptr++) {
+				if (*id == '"') {
+					if (((*ptr >= 'a') && (*ptr <= 'z')) ||
+					    ((*ptr >= '0') && (*ptr <= '9') && (ptr != tmp)) ||
+					    (*ptr == '_'))
+						continue;
+					else {
+						ptr = gda_sql_identifier_add_quotes (tmp);
+						g_free (tmp);
+						return ptr;
+					}
+				}
+				else if ((*ptr >= 'A') && (*ptr <= 'Z'))
+					*ptr += 'a' - 'A';
+				else if ((*ptr >= '0') && (*ptr <= '9') && (ptr == tmp)) {
+					ptr = gda_sql_identifier_add_quotes (tmp);
+					g_free (tmp);
+					return ptr;
+				}
+			}
+			return tmp;
+		}
 	}
 	else {
 		/* default SQL standard */
diff --git a/libgda/sqlite/Makefile.am b/libgda/sqlite/Makefile.am
index d64c5d7..dd2a66f 100644
--- a/libgda/sqlite/Makefile.am
+++ b/libgda/sqlite/Makefile.am
@@ -51,9 +51,10 @@ libgda_sqlite_la_SOURCES = \
 	gda-sqlite-pstmt.c \
 	gda-sqlite-recordset.c \
 	gda-sqlite-recordset.h \
+	gda-sqlite-util.c \
+	gda-sqlite-util.h \
 	gda-sqlite.h \
-	keywords_hash.h \
-	utils.c
+	keywords_hash.h
 
 libgda_sqlite_la_LDFLAGS = -export-dynamic -module -avoid-version $(NO_UNDEFINED)
 libgda_sqlite_la_LIBADD = \
diff --git a/libgda/sqlite/gda-sqlite-meta.c b/libgda/sqlite/gda-sqlite-meta.c
index 0c80fda..53dd02f 100644
--- a/libgda/sqlite/gda-sqlite-meta.c
+++ b/libgda/sqlite/gda-sqlite-meta.c
@@ -24,6 +24,7 @@
 #include <string.h>
 #include "gda-sqlite.h"
 #include "gda-sqlite-meta.h"
+#include "gda-sqlite-util.h"
 #include "gda-sqlite-provider.h"
 #include <libgda/gda-meta-store.h>
 #include <libgda/sql-parser/gda-sql-parser.h>
@@ -33,9 +34,6 @@
 #include <libgda/gda-data-model-array.h>
 #include <libgda/gda-set.h>
 
-#include <libgda/sqlite/keywords_hash.h>
-#include "keywords_hash.c" /* this one is dynamically generated */
-
 static gboolean append_a_row (GdaDataModel *to_model, GError **error, gint nb, ...);
 
 /*
@@ -98,6 +96,31 @@ static GValue       *zero_value;
 static GValue       *rule_value;
 static GdaSet       *pragma_set;
 
+static GValue *
+new_caseless_value (const GValue *cvalue)
+{
+	GValue *newvalue;
+	gchar *str, *ptr;
+	str = g_value_dup_string (cvalue);
+	for (ptr = str; *ptr; ptr++) {
+		if ((*ptr >= 'A') && (*ptr <= 'Z'))
+			*ptr += 'a' - 'A';
+		if (((*ptr >= 'a') && (*ptr <= 'z')) ||
+		    ((*ptr >= '0') && (*ptr <= '9')) ||
+		    (*ptr >= '_'))
+			continue;
+		else {
+			g_free (str);
+			newvalue = gda_value_new (G_TYPE_STRING);
+			g_value_set_string (newvalue, g_value_get_string (cvalue));
+			return newvalue;
+		}
+	}
+	newvalue = gda_value_new (G_TYPE_STRING);
+	g_value_take_string (newvalue, str);
+	return newvalue;
+}
+
 /*
  * Meta initialization
  */
@@ -202,7 +225,7 @@ _gda_sqlite_meta__info (GdaServerProvider *prov, GdaConnection *cnc,
 
 	retval = append_a_row (model, error, 1, FALSE, catalog_value);
 	if (retval) {
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify (store, context->table_name, model, NULL, error, NULL);
 	}
 	g_object_unref (model);
@@ -261,7 +284,7 @@ _gda_sqlite_meta__btypes (GdaServerProvider *prov, GdaConnection *cnc,
 		}
 	}
 	if (retval) {
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify (store, context->table_name, mod_model, NULL, error, NULL);
 	}
 	g_object_unref (mod_model);
@@ -445,7 +468,7 @@ _gda_sqlite_meta__udt (GdaServerProvider *prov, GdaConnection *cnc,
 
 	/* actually use mod_model */
 	if (retval) {
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify (store, context->table_name, mod_model, NULL, error, NULL);
 	}
 	g_object_unref (mod_model);
@@ -481,7 +504,7 @@ _gda_sqlite_meta_udt (GdaServerProvider *prov, GdaConnection *cnc,
 
 	/* actually use mod_model */
 	if (retval) {
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify (store, context->table_name, mod_model, NULL, error, NULL);
 	}
 	g_object_unref (mod_model);
@@ -653,7 +676,7 @@ _gda_sqlite_meta_schemata (GdaServerProvider *prov, GdaConnection *cnc,
 			const gchar *cstr;
 			GValue *v1;
 
-			cstr = g_value_get_string (cvalue);
+			cstr = g_value_get_string (cvalue); /* VMA */
 			if (!cstr || !strncmp (cstr, TMP_DATABASE_NAME, 4))
 				continue;
 
@@ -667,7 +690,7 @@ _gda_sqlite_meta_schemata (GdaServerProvider *prov, GdaConnection *cnc,
 	}
 	g_object_unref (tmpmodel);
 	if (retval){
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify_with_context (store, context, model, error);
 	}
 	g_object_unref (model);
@@ -713,60 +736,68 @@ fill_tables_views_model (GdaConnection *cnc,
         nrows = gda_data_model_get_n_rows (tmpmodel);
         for (i = 0; (i < nrows) && retval; i++) {
                 const GValue *cvalue;
+		GValue *ncvalue;
 
                 cvalue = gda_data_model_get_value_at (tmpmodel, 0, i, error);
 		if (!cvalue) {
 			retval = FALSE;
                         break;
 		}
+		ncvalue = new_caseless_value (cvalue);
+		
                 if (!p_table_name ||
-                    !gda_value_compare (p_table_name, cvalue)) {
+                    !gda_value_compare (p_table_name, ncvalue)) {
                         GValue *v1, *v2 = NULL;
                         const GValue *tvalue;
                         const GValue *dvalue;
                         gboolean is_view = FALSE;
                         const gchar *this_table_name;
 
-                        this_table_name = g_value_get_string (cvalue);
+                        this_table_name = g_value_get_string (ncvalue);
                         g_assert (this_table_name);
-			if (!strcmp (this_table_name, "sqlite_sequence"))
+			if (!strcmp (this_table_name, "sqlite_sequence")) {
+				gda_value_free (ncvalue);
                                 continue; /* ignore that table */
+			}
 
                         tvalue = gda_data_model_get_value_at (tmpmodel, 1, i, error);
 			if (!tvalue) {
 				retval = FALSE;
+				gda_value_free (ncvalue);
 				break;
 			}
                         dvalue = gda_data_model_get_value_at (tmpmodel, 2, i, error);
 			if (!dvalue) {
 				retval = FALSE;
+				gda_value_free (ncvalue);
 				break;
 			}
                         if (*(g_value_get_string (tvalue)) == 'v')
                                 is_view = TRUE;
                         g_value_set_boolean ((v1 = gda_value_new (G_TYPE_BOOLEAN)), TRUE);
-			str = g_strdup_printf ("%s.%s", schema_name, g_value_get_string (cvalue));
+			str = g_strdup_printf ("%s.%s", schema_name, g_value_get_string (ncvalue));
 			g_value_take_string ((v2 = gda_value_new (G_TYPE_STRING)), str);
                         if (! append_a_row (to_tables_model, error, 9,
                                             FALSE, catalog_value, /* table_catalog */
                                             FALSE, p_table_schema, /* table_schema */
-                                            FALSE, cvalue, /* table_name */
+                                            FALSE, ncvalue, /* table_name */
                                             FALSE, is_view ? view_type_value : table_type_value, /* table_type */
                                             TRUE, v1, /* is_insertable_into */
                                             FALSE, NULL, /* table_comments */
-                                            FALSE, cvalue, /* table_short_name */
+                                            FALSE, ncvalue, /* table_short_name */
                                             TRUE, v2, /* table_full_name */
                                             FALSE, NULL)) /* table_owner */
                                 retval = FALSE;
                         if (is_view && ! append_a_row (to_views_model, error, 6,
                                                        FALSE, catalog_value,
                                                        FALSE, p_table_schema,
-                                                       FALSE, cvalue,
+                                                       FALSE, ncvalue,
                                                        FALSE, dvalue,
                                                        FALSE, view_check_option,
                                                        FALSE, false_value))
                                 retval = FALSE;
                 }
+		gda_value_free (ncvalue);
         }
         g_object_unref (tmpmodel);
 
@@ -814,12 +845,12 @@ _gda_sqlite_meta__tables_views (GdaServerProvider *prov, GdaConnection *cnc,
 	c2 = *context; /* copy contents, just because we need to modify @context->table_name */
 	if (retval) {
 		c2.table_name = "_tables";
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify_with_context (store, &c2, tables_model, error);
 	}
 	if (retval) {
 		c2.table_name = "_views";
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify_with_context (store, &c2, views_model, error);
 	}
 	g_object_unref (tables_model);
@@ -848,12 +879,12 @@ _gda_sqlite_meta_tables_views (GdaServerProvider *prov, GdaConnection *cnc,
 	c2 = *context; /* copy contents, just because we need to modify @context->table_name */
 	if (retval) {
 		c2.table_name = "_tables";
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify_with_context (store, &c2, tables_model, error);
 	}
 	if (retval) {
 		c2.table_name = "_views";
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify_with_context (store, &c2, views_model, error);
 	}
 	g_object_unref (tables_model);
@@ -897,6 +928,7 @@ fill_columns_model (GdaConnection *cnc, SqliteConnectionData *cdata,
 		const gchar *this_table_name;
 		const gchar *this_col_name;
 		const GValue *this_col_pname;
+		GValue *nthis_col_pname;
 		GType gtype = 0;
 		const GValue *cvalue;
 		
@@ -905,12 +937,14 @@ fill_columns_model (GdaConnection *cnc, SqliteConnectionData *cdata,
 			retval = FALSE;
 			break;
 		}
+		nthis_col_pname = new_caseless_value (this_col_pname);
+
 		this_table_name = g_value_get_string (p_table_name);
 		g_assert (this_table_name);
 		if (!strcmp (this_table_name, "sqlite_sequence"))
 			continue; /* ignore that table */
 		
-		this_col_name = g_value_get_string (this_col_pname);
+		this_col_name = g_value_get_string (nthis_col_pname);
 		if (sqlite3_table_column_metadata (cdata->connection, g_value_get_string (p_table_schema), 
 						   this_table_name, this_col_name,
 						   &pzDataType, &pzCollSeq, &pNotNull, &pPrimaryKey, &pAutoinc)
@@ -919,6 +953,7 @@ fill_columns_model (GdaConnection *cnc, SqliteConnectionData *cdata,
 			cvalue = gda_data_model_get_value_at (tmpmodel, 2, i, error);
 			if (!cvalue) {
 				retval = FALSE;
+				gda_value_free (nthis_col_pname);
 				break;
 			}	
 			pzDataType = g_value_get_string (cvalue);
@@ -926,12 +961,14 @@ fill_columns_model (GdaConnection *cnc, SqliteConnectionData *cdata,
 			cvalue = gda_data_model_get_value_at (tmpmodel, 3, i, error);
 			if (!cvalue) {
 				retval = FALSE;
+				gda_value_free (nthis_col_pname);
 				break;
 			}
 			pNotNull = g_value_get_int (cvalue);
 			cvalue = gda_data_model_get_value_at (tmpmodel, 5, i, error);
 			if (!cvalue) {
 				retval = FALSE;
+				gda_value_free (nthis_col_pname);
 				break;
 			}
 			pPrimaryKey = g_value_get_boolean (cvalue);
@@ -941,6 +978,7 @@ fill_columns_model (GdaConnection *cnc, SqliteConnectionData *cdata,
 		cvalue = gda_data_model_get_value_at (tmpmodel, 0, i, error);
 		if (!cvalue) {
 			retval = FALSE;
+			gda_value_free (nthis_col_pname);
 			break;
 		}
 		v1 = gda_value_copy (cvalue);
@@ -974,7 +1012,7 @@ fill_columns_model (GdaConnection *cnc, SqliteConnectionData *cdata,
 				    FALSE, catalog_value, /* table_catalog */
 				    FALSE, p_table_schema, /* table_schema */
 				    FALSE, p_table_name, /* table_name */
-				    FALSE, this_col_pname, /* column name */
+				    TRUE, nthis_col_pname, /* column name */
 				    TRUE, v1, /* ordinal_position */
 				    FALSE, cvalue, /* column default */
 				    TRUE, v3, /* is_nullable */
@@ -1076,7 +1114,7 @@ _gda_sqlite_meta__columns (GdaServerProvider *prov, GdaConnection *cnc,
 	g_object_unref (tmpmodel);
 
 	if (retval) {
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify_with_context (store, context, mod_model, error);
 	}
 	g_object_unref (mod_model);
@@ -1102,7 +1140,7 @@ _gda_sqlite_meta_columns (GdaServerProvider *prov, GdaConnection *cnc,
 
 	retval = fill_columns_model (cnc, cdata, mod_model, table_schema, table_name, error);	
 	if (retval) {
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify_with_context (store, context, mod_model, error);
 	}
 	g_object_unref (mod_model);
@@ -1417,7 +1455,7 @@ _gda_sqlite_meta__constraints_tab (GdaServerProvider *prov, GdaConnection *cnc,
 	g_object_unref (tmpmodel);
 
 	if (retval) {
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify_with_context (store, context, mod_model, error);
 	}
 	g_object_unref (mod_model);
@@ -1444,7 +1482,7 @@ _gda_sqlite_meta_constraints_tab (GdaServerProvider *prov, GdaConnection *cnc,
 
 	retval = fill_constraints_tab_model (cnc, cdata, mod_model, table_schema, table_name, constraint_name_n, error);	
 	if (retval) {
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify_with_context (store, context, mod_model, error);
 	}
 	g_object_unref (mod_model);
@@ -1608,7 +1646,7 @@ _gda_sqlite_meta__constraints_ref (GdaServerProvider *prov, GdaConnection *cnc,
 	g_object_unref (tmpmodel);
 
 	if (retval) {
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify_with_context (store, context, mod_model, error);
 	}
 	g_object_unref (mod_model);
@@ -1635,7 +1673,7 @@ _gda_sqlite_meta_constraints_ref (GdaServerProvider *prov, GdaConnection *cnc,
 
 	retval = fill_constraints_ref_model (cnc, cdata, mod_model, table_schema, table_name, constraint_name, error);
 	if (retval) {
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify_with_context (store, context, mod_model, error);
 	}
 	g_object_unref (mod_model);
@@ -1946,7 +1984,7 @@ _gda_sqlite_meta__key_columns (GdaServerProvider *prov, GdaConnection *cnc,
 	g_object_unref (const_model);
 
 	if (retval) {
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify_with_context (store, context, mod_model, error);
 	}
 	g_object_unref (mod_model);
@@ -1973,7 +2011,7 @@ _gda_sqlite_meta_key_columns (GdaServerProvider *prov, GdaConnection *cnc,
 
 	retval = fill_key_columns_model (cnc, cdata, mod_model, table_schema, table_name, constraint_name, error);
 	if (retval) {
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify_with_context (store, context, mod_model, error);
 	}
 	g_object_unref (mod_model);
@@ -2123,7 +2161,7 @@ _gda_sqlite_meta_routines (GdaServerProvider *prov, GdaConnection *cnc,
 	}
 	
 	if (retval) {
-		gda_meta_store_set_reserved_keywords_func (store, is_keyword);
+		gda_meta_store_set_reserved_keywords_func (store, _gda_sqlite_get_reserved_keyword_func());
 		retval = gda_meta_store_modify_with_context (store, context, mod_model, error);
 	}
 	g_object_unref (mod_model);
diff --git a/libgda/sqlite/gda-sqlite-provider.c b/libgda/sqlite/gda-sqlite-provider.c
index c716b77..bebb910 100644
--- a/libgda/sqlite/gda-sqlite-provider.c
+++ b/libgda/sqlite/gda-sqlite-provider.c
@@ -36,6 +36,7 @@
 #include "gda-sqlite-provider.h"
 #include "gda-sqlite-recordset.h"
 #include "gda-sqlite-ddl.h"
+#include "gda-sqlite-util.h"
 #include "gda-sqlite-meta.h"
 #include "gda-sqlite-handler-bin.h"
 #include "gda-sqlite-blob-op.h"
@@ -258,7 +259,10 @@ static GObject             *gda_sqlite_provider_statement_execute (GdaServerProv
 								   GType *col_types, GdaSet **last_inserted_row, 
 								   guint *task_id, GdaServerProviderExecCallback async_cb, 
 								   gpointer cb_data, GError **error);
-
+/* Quoting */
+static gchar               *gda_sqlite_identifier_quote    (GdaServerProvider *provider, GdaConnection *cnc,
+							    const gchar *id,
+							    gboolean meta_store_convention, gboolean force_quotes);
 /* 
  * private connection data destroy 
  */
@@ -349,6 +353,7 @@ gda_sqlite_provider_class_init (GdaSqliteProviderClass *klass)
 	provider_class->get_def_dbms_type = gda_sqlite_provider_get_default_dbms_type;
 
 	provider_class->create_connection = NULL;
+	provider_class->identifier_quote = gda_sqlite_identifier_quote;
 	provider_class->open_connection = gda_sqlite_provider_open_connection;
 	provider_class->close_connection = gda_sqlite_provider_close_connection;
 	provider_class->get_database = gda_sqlite_provider_get_database;
@@ -2572,6 +2577,189 @@ scalar_gda_file_exists_func (sqlite3_context *context, int argc, sqlite3_value *
 		sqlite3_result_int (context, 0);
 }
 
+static gchar *
+identifier_add_quotes (const gchar *str)
+{
+        gchar *retval, *rptr;
+        const gchar *sptr;
+        gint len;
+
+        if (!str)
+                return NULL;
+
+        len = strlen (str);
+        retval = g_new (gchar, 2*len + 3);
+        *retval = '"';
+        for (rptr = retval+1, sptr = str; *sptr; sptr++, rptr++) {
+                if (*sptr == '"') {
+                        *rptr = '\\';
+                        rptr++;
+                        *rptr = *sptr;
+                }
+                else
+                        *rptr = *sptr;
+        }
+        *rptr = '"';
+        rptr++;
+        *rptr = 0;
+        return retval;
+}
+
+static gboolean
+_sql_identifier_needs_quotes (const gchar *str)
+{
+	const gchar *ptr;
+
+	g_return_val_if_fail (str, FALSE);
+	for (ptr = str; *ptr; ptr++) {
+		/* quote if 1st char is a number */
+		if ((*ptr <= '9') && (*ptr >= '0')) {
+			if (ptr == str)
+				return TRUE;
+			continue;
+		}
+		if (((*ptr >= 'A') && (*ptr <= 'Z')) ||
+		    ((*ptr >= 'a') && (*ptr <= 'z')))
+			continue;
+
+		if ((*ptr != '$') && (*ptr != '_') && (*ptr != '#'))
+			return TRUE;
+	}
+	return FALSE;
+}
+
+/* Returns: @str */
+static gchar *
+sqlite_remove_quotes (gchar *str)
+{
+        glong total;
+        gchar *ptr;
+        glong offset = 0;
+	char delim;
+	
+	if (!str)
+		return NULL;
+	delim = *str;
+	if ((delim != '[') && (delim != '"') && (delim != '\'') && (delim != '`'))
+		return str;
+
+        total = strlen (str);
+        if ((str[total-1] == delim) || ((delim == '[') && (str[total-1] == ']'))) {
+		/* string is correclty terminated */
+		g_memmove (str, str+1, total-2);
+		total -=2;
+	}
+	else {
+		/* string is _not_ correclty terminated */
+		g_memmove (str, str+1, total-1);
+		total -=1;
+	}
+        str[total] = 0;
+
+	if ((delim == '"') || (delim == '\'')) {
+		ptr = (gchar *) str;
+		while (offset < total) {
+			/* we accept the "''" as a synonym of "\'" */
+			if (*ptr == delim) {
+				if (*(ptr+1) == delim) {
+					g_memmove (ptr+1, ptr+2, total - offset);
+					offset += 2;
+				}
+				else {
+					*str = 0;
+					return str;
+				}
+			}
+			if (*ptr == '\\') {
+				if (*(ptr+1) == '\\') {
+					g_memmove (ptr+1, ptr+2, total - offset);
+					offset += 2;
+				}
+				else {
+					if (*(ptr+1) == delim) {
+						*ptr = delim;
+						g_memmove (ptr+1, ptr+2, total - offset);
+						offset += 2;
+					}
+					else {
+						*str = 0;
+						return str;
+					}
+				}
+			}
+			else
+				offset ++;
+			
+			ptr++;
+		}
+	}
+
+        return str;
+}
+
+static gchar *
+gda_sqlite_identifier_quote (GdaServerProvider *provider, GdaConnection *cnc,
+			     const gchar *id,
+			     gboolean for_meta_store, gboolean force_quotes)
+{
+        GdaSqlReservedKeywordsFunc kwfunc;
+        SqliteConnectionData *cdata = NULL;
+
+        if (cnc) {
+                cdata = (SqliteConnectionData*) gda_connection_internal_get_provider_data (cnc);
+                if (!cdata)
+                        return NULL;
+        }
+
+        kwfunc = _gda_sqlite_get_reserved_keyword_func ();
+
+	if (for_meta_store) {
+		gchar *tmp, *ptr;
+		tmp = sqlite_remove_quotes (g_strdup (id));
+		if (kwfunc (tmp)) {
+			ptr = gda_sql_identifier_add_quotes (tmp);
+			g_free (tmp);
+			return ptr;
+		}
+		else {
+			/* if only alphanum => don't quote */
+			for (ptr = tmp; *ptr; ptr++) {
+				if ((*ptr >= 'A') && (*ptr <= 'Z'))
+					*ptr += 'a' - 'A';
+				if (((*ptr >= 'a') && (*ptr <= 'z')) ||
+				    ((*ptr >= '0') && (*ptr <= '9') && (ptr != tmp)) ||
+				    (*ptr >= '_'))
+					continue;
+				else {
+					ptr = gda_sql_identifier_add_quotes (tmp);
+					g_free (tmp);
+					return ptr;
+				}
+			}
+			return tmp;
+		}
+	}
+	else {
+		if (*id == '"') {
+			/* there are already some quotes */
+			return g_strdup (id);
+		}
+		else if ((*id == '[') || (*id == '`')) {
+			/* there are already some quotes */
+			gchar *tmp, *ptr;
+			tmp = sqlite_remove_quotes (g_strdup (id));
+			ptr = gda_sql_identifier_add_quotes (tmp);
+			g_free (tmp);
+			return ptr;
+		}
+		if (kwfunc (id) || _sql_identifier_needs_quotes (id) || force_quotes)
+			return identifier_add_quotes (id);
+
+		/* nothing to do */
+		return g_strdup (id);
+	}
+}
+
 static void
 scalar_gda_hex_print_func (sqlite3_context *context, int argc, sqlite3_value **argv)
 {
diff --git a/libgda/sqlite/gda-sqlite-recordset.c b/libgda/sqlite/gda-sqlite-recordset.c
index 2d3ec04..b6094e1 100644
--- a/libgda/sqlite/gda-sqlite-recordset.c
+++ b/libgda/sqlite/gda-sqlite-recordset.c
@@ -26,6 +26,7 @@
 #include <string.h>
 #include <glib/gi18n-lib.h>
 #include "gda-sqlite.h"
+#include "gda-sqlite-util.h"
 #include "gda-sqlite-recordset.h"
 #include "gda-sqlite-provider.h"
 #include "gda-sqlite-blob-op.h"
diff --git a/libgda/sqlite/utils.c b/libgda/sqlite/gda-sqlite-util.c
similarity index 89%
rename from libgda/sqlite/utils.c
rename to libgda/sqlite/gda-sqlite-util.c
index 9342bbe..29a55be 100644
--- a/libgda/sqlite/utils.c
+++ b/libgda/sqlite/gda-sqlite-util.c
@@ -1,9 +1,7 @@
-/* GNOME DB Postgres Provider
- * Copyright (C) 1998 - 2009 The GNOME Foundation
+/* GDA sqlite provider
+ * Copyright (C) 2009 The GNOME Foundation.
  *
  * AUTHORS:
- *         Gonzalo Paniagua Javier <gonzalo gnome-db org>
- *         Carlos Perello Marin <carlos gnome-db org>
  *         Vivien Malerba <malerba gnome-db org>
  *
  * This Library is free software; you can redistribute it and/or
@@ -23,12 +21,16 @@
  */
 
 #include <glib/gi18n-lib.h>
+#include "gda-sqlite-util.h"
 #include <stdlib.h>
 #include <string.h>
 #include "gda-sqlite.h"
 #include <libgda/gda-connection-private.h>
 #include "gda-sqlite-recordset.h"
 
+#include <libgda/sqlite/keywords_hash.h>
+#include "keywords_hash.c" /* this one is dynamically generated */
+
 static guint
 nocase_str_hash (gconstpointer v)
 {
@@ -95,3 +97,18 @@ _gda_sqlite_compute_g_type (int sqlite_type)
 	}
 }
 
+
+
+#ifdef GDA_DEBUG
+void
+_gda_sqlite_test_keywords (void)
+{
+        test_keywords();
+}
+#endif
+
+GdaSqlReservedKeywordsFunc
+_gda_sqlite_get_reserved_keyword_func (void)
+{
+        return is_keyword;
+}
diff --git a/libgda/sqlite/gda-sqlite-util.h b/libgda/sqlite/gda-sqlite-util.h
new file mode 100644
index 0000000..15e1676
--- /dev/null
+++ b/libgda/sqlite/gda-sqlite-util.h
@@ -0,0 +1,40 @@
+/* GDA sqlite provider
+ * Copyright (C) 2009 The GNOME Foundation.
+ *
+ * AUTHORS:
+ *         Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GDA_SQLITE_UTIL_H__
+#define __GDA_SQLITE_UTIL_H__
+
+#include "gda-sqlite.h"
+
+G_BEGIN_DECLS
+
+void  _gda_sqlite_compute_types_hash (SqliteConnectionData *scnc);
+GType _gda_sqlite_compute_g_type (int sqlite_type);
+
+#ifdef GDA_DEBUG
+void                _gda_sqlite_test_keywords (void);
+#endif
+GdaSqlReservedKeywordsFunc _gda_sqlite_get_reserved_keyword_func (void);
+
+G_END_DECLS
+
+#endif
+
diff --git a/libgda/sqlite/gda-sqlite.h b/libgda/sqlite/gda-sqlite.h
index 557e3d9..c9f3237 100644
--- a/libgda/sqlite/gda-sqlite.h
+++ b/libgda/sqlite/gda-sqlite.h
@@ -46,10 +46,4 @@ typedef struct {
 	GHashTable   *types; /* key = type name, value = GType */
 } SqliteConnectionData;
 
-/* 
- * Utility functions
- */
-void  _gda_sqlite_compute_types_hash (SqliteConnectionData *scnc);
-GType _gda_sqlite_compute_g_type (int sqlite_type);
-
 #endif
diff --git a/providers/mysql/gda-mysql-meta.c b/providers/mysql/gda-mysql-meta.c
index 9f6b823..1fb8148 100644
--- a/providers/mysql/gda-mysql-meta.c
+++ b/providers/mysql/gda-mysql-meta.c
@@ -3,6 +3,7 @@
  *
  * AUTHORS:
  *      Carlos Savoretti <csavoretti gmail com>
+ *      Vivien Malerba <malerba gnome-db org>
  *
  * This Library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public License as
@@ -34,12 +35,6 @@
 #include <libgda/gda-set.h>
 #include <libgda/gda-holder.h>
 
-static gboolean
-append_a_row (GdaDataModel  *to_model,
-	      GError       **error,
-	      gint           nb,
-	      ...);
-
 /*
  * predefined statements' IDs
  */
@@ -111,49 +106,49 @@ static gchar *internal_sql[] = {
 	"SELECT IFNULL(catalog_name, schema_name) AS catalog_name, schema_name, NULL, CASE WHEN schema_name = 'information_schema' OR schema_name = 'mysql' THEN TRUE ELSE FALSE END AS schema_internal FROM INFORMATION_SCHEMA.schemata WHERE schema_name = ##name::string",
 
         /* I_STMT_TABLES */
-	"SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, CASE WHEN table_type LIKE '%VIEW%' THEN 'VIEW' ELSE table_type END, CASE table_type WHEN 'BASE TABLE' THEN TRUE ELSE FALSE END AS table_type, table_comment, " SHORT_NAME("table_schema", "table_name", "CONCAT(table_schema, '.', table_name)") " AS short_name, CONCAT(table_schema, '.', table_name) AS table_full_name, NULL AS table_owner FROM INFORMATION_SCHEMA.tables WHERE table_catalog = ##cat::string AND schema_name = ##schema::string",
+	"SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, CASE WHEN table_type LIKE '%VIEW%' THEN 'VIEW' ELSE table_type END, CASE table_type WHEN 'BASE TABLE' THEN TRUE ELSE FALSE END AS table_type, table_comment, " SHORT_NAME("table_schema", "table_name", "CONCAT(table_schema, '.', table_name)") " AS short_name, CONCAT(table_schema, '.', table_name) AS table_full_name, NULL AS table_owner FROM INFORMATION_SCHEMA.tables WHERE IFNULL(table_catalog, table_schema) = BINARY ##cat::string AND table_schema = BINARY ##schema::string",
 
         /* I_STMT_TABLES_ALL */
 	"SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, CASE WHEN table_type LIKE '%VIEW%' THEN 'VIEW' ELSE table_type END, CASE table_type WHEN 'BASE TABLE' THEN TRUE ELSE FALSE END AS table_type, table_comment, " SHORT_NAME("table_schema", "table_name", "CONCAT(table_schema, '.', table_name)") " AS short_name, CONCAT(table_schema, '.', table_name) AS table_full_name, NULL AS table_owner FROM INFORMATION_SCHEMA.tables",
 
         /* I_STMT_TABLE_NAMED */
-	"SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, CASE WHEN table_type LIKE '%VIEW%' THEN 'VIEW' ELSE table_type END, CASE table_type WHEN 'BASE TABLE' THEN TRUE ELSE FALSE END AS table_type, table_comment, " SHORT_NAME("table_schema", "table_name", "CONCAT(table_schema, '.', table_name)") " as short_name, CONCAT(table_schema, '.', table_name) AS table_full_name, NULL AS table_owner FROM FROM INFORMATION_SCHEMA.tables WHERE table_catalog = ##cat::string AND schema_name = ##schema::string",
+	"SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, CASE WHEN table_type LIKE '%VIEW%' THEN 'VIEW' ELSE table_type END, CASE table_type WHEN 'BASE TABLE' THEN TRUE ELSE FALSE END AS table_type, table_comment, " SHORT_NAME("table_schema", "table_name", "CONCAT(table_schema, '.', table_name)") " as short_name, CONCAT(table_schema, '.', table_name) AS table_full_name, NULL AS table_owner FROM INFORMATION_SCHEMA.tables WHERE IFNULL(table_catalog, table_schema) = BINARY ##cat::string AND table_schema = BINARY ##schema::string AND table_name = BINARY ##name::string",
 
         /* I_STMT_VIEWS_ALL */
 	"SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, view_definition, check_option, is_updatable FROM INFORMATION_SCHEMA.views UNION SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, NULL, NULL, FALSE FROM INFORMATION_SCHEMA.tables WHERE table_schema='information_schema' and table_type LIKE '%VIEW%'",
 
         /* I_STMT_VIEW_NAMED */
-	"SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, view_definition, check_option, is_updatable FROM INFORMATION_SCHEMA.views WHERE table_catalog = ##cat::string AND table_schema = ##schema::string  UNION SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, NULL, NULL, FALSE FROM INFORMATION_SCHEMA.tables WHERE table_schema='information_schema' AND table_type LIKE '%VIEW%' AND table_catalog = ##cat::string AND table_schema = ##schema::string",
+	"SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, view_definition, check_option, is_updatable FROM INFORMATION_SCHEMA.views WHERE table_catalog = BINARY ##cat::string AND table_schema = BINARY ##schema::string  UNION SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, NULL, NULL, FALSE FROM INFORMATION_SCHEMA.tables WHERE table_schema='information_schema' AND table_type LIKE '%VIEW%' AND IFNULL(table_catalog, table_schema) = BINARY ##cat::string AND table_schema = BINARY ##schema::string",
 
         /* I_STMT_COLUMNS_OF_TABLE */
-	"SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, 'gchararray', character_maximum_length,character_octet_length, numeric_precision, numeric_scale, 0, character_set_name, character_set_name, character_set_name, collation_name, collation_name, collation_name, CASE WHEN extra = 'auto_increment' then '" GDA_EXTRA_AUTO_INCREMENT "' ELSE extra END, 1, column_comment FROM INFORMATION_SCHEMA.columns WHERE table_catalog = ##cat::string AND table_schema = ##schema::string AND table_name = ##name::string",
+	"SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, NULL, 'gchararray', character_maximum_length,character_octet_length, numeric_precision, numeric_scale, 0, character_set_name, character_set_name, character_set_name, collation_name, collation_name, collation_name, CASE WHEN extra = 'auto_increment' then '" GDA_EXTRA_AUTO_INCREMENT "' ELSE extra END, 1, column_comment FROM INFORMATION_SCHEMA.columns WHERE IFNULL(table_catalog, table_schema) = BINARY ##cat::string AND table_schema = BINARY ##schema::string AND table_name = BINARY ##name::string",
 
         /* I_STMT_COLUMNS_ALL */
-	"SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, column_name, ordinal_position, column_default, CASE is_nullable WHEN 'YES' THEN TRUE ELSE FALSE END AS is_nullable, data_type, 'gchararray', '', character_maximum_length, character_octet_length, numeric_precision, numeric_scale, 0, character_set_name, character_set_name, character_set_name, collation_name, collation_name, collation_name, CASE WHEN extra = 'auto_increment' then '" GDA_EXTRA_AUTO_INCREMENT "' ELSE extra END, IF(FIND_IN_SET('insert', privileges) != 0 OR FIND_IN_SET('update', privileges) != 0, TRUE, FALSE) AS is_updatable, column_comment FROM INFORMATION_SCHEMA.columns",
+	"SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, column_name, ordinal_position, column_default, CASE is_nullable WHEN 'YES' THEN TRUE ELSE FALSE END AS is_nullable, data_type, NULL, 'gchararray', '', character_maximum_length, character_octet_length, numeric_precision, numeric_scale, 0, character_set_name, character_set_name, character_set_name, collation_name, collation_name, collation_name, CASE WHEN extra = 'auto_increment' then '" GDA_EXTRA_AUTO_INCREMENT "' ELSE extra END, IF(FIND_IN_SET('insert', privileges) != 0 OR FIND_IN_SET('update', privileges) != 0, TRUE, FALSE) AS is_updatable, column_comment FROM INFORMATION_SCHEMA.columns",
 
         /* I_STMT_TABLES_CONSTRAINTS */
-	"SELECT IFNULL(constraint_catalog, constraint_schema) AS constraint_catalog, constraint_schema, constraint_name, IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, constraint_type, NULL, FALSE, FALSE FROM INFORMATION_SCHEMA.table_constraints WHERE constraint_catalog = ##cat::string AND constraint_schema = ##schema::string AND table_name = ##name::string",
+	"SELECT IFNULL(constraint_catalog, constraint_schema) AS constraint_catalog, constraint_schema, constraint_name, IFNULL(constraint_catalog, constraint_schema) AS table_catalog, table_schema, table_name, constraint_type, NULL, FALSE, FALSE FROM INFORMATION_SCHEMA.table_constraints WHERE constraint_catalog = BINARY ##cat::string AND constraint_schema = BINARY ##schema::string AND table_name = BINARY ##name::string",
 
         /* I_STMT_TABLES_CONSTRAINTS_ALL */
 	"SELECT IFNULL(constraint_catalog, constraint_schema) AS constraint_catalog, constraint_schema, constraint_name, IFNULL(constraint_catalog, constraint_schema) AS onstraint_catalog, table_schema, table_name, constraint_type, NULL, FALSE, FALSE FROM INFORMATION_SCHEMA.table_constraints",
 
         /* I_STMT_TABLES_CONSTRAINTS_NAMED */
-	"SELECT IFNULL(constraint_catalog, constraint_schema) AS constraint_catalog, constraint_schema, constraint_name, IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, constraint_type, NULL, FALSE, FALSE FROM INFORMATION_SCHEMA.table_constraints WHERE constraint_catalog = ##cat::string AND constraint_schema = ##schema::string AND table_name = ##name::string AND constraint_name = ##name2::string",
+	"SELECT IFNULL(constraint_catalog, constraint_schema) AS constraint_catalog, constraint_schema, constraint_name, IFNULL(constraint_catalog, constraint_schema) AS table_catalog, table_schema, table_name, constraint_type, NULL, FALSE, FALSE FROM INFORMATION_SCHEMA.table_constraints WHERE constraint_catalog = BINARY ##cat::string AND constraint_schema = BINARY ##schema::string AND table_name = BINARY ##name::string AND constraint_name = BINARY ##name2::string",
 
         /* I_STMT_REF_CONSTRAINTS */
-	"SELECT IFNULL(t.constraint_catalog, t.constraint_schema) AS constraint_catalog, t.constraint_schema, r.constraint_name, IFNULL(r.constraint_catalog, r.constraint_schema) AS constraint_catalog, r.constraint_schema, r.match_option, r.update_rule, delete_rule FROM INFORMATION_SCHEMA.referential_constraints r INNER JOIN INFORMATION_SCHEMA.table_constraints t ON r.constraint_schema=t.constraint_schema AND r.constraint_name=t.constraint_name AND r.table_name=t.table_name WHERE r.constraint_catalog = ##cat::string AND r.constraint_schema = ##schema::string AND r.table_name = ##name AND r.constraint_name = ##name2::string",
+	"SELECT IFNULL(t.constraint_catalog, t.constraint_schema) AS constraint_catalog, t.constraint_schema, r.constraint_name, IFNULL(r.constraint_catalog, r.constraint_schema) AS constraint_catalog, r.constraint_schema, r.match_option, r.update_rule, delete_rule FROM INFORMATION_SCHEMA.referential_constraints r INNER JOIN INFORMATION_SCHEMA.table_constraints t ON r.constraint_schema=t.constraint_schema AND r.constraint_name=t.constraint_name AND r.table_name=t.table_name WHERE r.constraint_catalog = BINARY ##cat::string AND r.constraint_schema = BINARY ##schema::string AND r.table_name = BINARY ##name AND r.constraint_name = BINARY ##name2::string",
 
         /* I_STMT_REF_CONSTRAINTS_ALL */
 	"SELECT IFNULL(t.constraint_catalog, t.constraint_schema) AS constraint_catalog, t.constraint_schema, r.constraint_name, IFNULL(r.constraint_catalog, r.constraint_schema) AS constraint_catalog, r.constraint_schema, r.match_option, r.update_rule, delete_rule FROM INFORMATION_SCHEMA.referential_constraints r INNER JOIN INFORMATION_SCHEMA.table_constraints t ON r.constraint_schema=t.constraint_schema AND r.constraint_name=t.constraint_name AND r.table_name=t.table_name",
 
         /* I_STMT_KEY_COLUMN_USAGE */
-	"SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, constraint_name, column_name, ordinal_position FROM INFORMATION_SCHEMA.key_column_usage WHERE table_catalog = ##cat::string AND table_schema = ##schema::string AND table_name = ##name::string AND constraint_name = ##name2::string",
+	"SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, constraint_name, column_name, ordinal_position FROM INFORMATION_SCHEMA.key_column_usage WHERE IFNULL(table_catalog, table_schema) = BINARY ##cat::string AND table_schema = BINARY ##schema::string AND table_name = BINARY ##name::string AND constraint_name = BINARY ##name2::string",
 
         /* I_STMT_KEY_COLUMN_USAGE_ALL */
 	"SELECT IFNULL(table_catalog, table_schema) AS table_catalog, table_schema, table_name, constraint_name, column_name, ordinal_position FROM INFORMATION_SCHEMA.key_column_usage",
 
         /* I_STMT_CHARACTER_SETS */
-	"SELECT DATABASE() AS character_set_catalog, DATABASE() AS character_set_schema, character_set_name, default_collate_name, default_collate_name, default_collate_name, description, character_set_name, character_set_name FROM INFORMATION_SCHEMA.character_sets WHERE character_set_catalog = ##cat::string AND character_set_schema = ##schema::string AND character_set_name = ##name::string",
+	"SELECT DATABASE() AS character_set_catalog, DATABASE() AS character_set_schema, character_set_name, default_collate_name, default_collate_name, default_collate_name, description, character_set_name, character_set_name FROM INFORMATION_SCHEMA.character_sets WHERE character_set_catalog = BINARY ##cat::string AND character_set_schema = BINARY ##schema::string AND character_set_name = BINARY ##name::string",
 
         /* I_STMT_CHARACTER_SETS_ALL */
 	"SELECT DATABASE() AS character_set_catalog, DATABASE() AS character_set_schema, character_set_name, default_collate_name, default_collate_name, default_collate_name, description, character_set_name, character_set_name FROM INFORMATION_SCHEMA.character_sets",
@@ -175,13 +170,13 @@ static gchar *internal_sql[] = {
         /* I_STMT_DOMAINS_CONSTRAINTS_ALL */
 
         /* I_STMT_VIEWS_COLUMNS */
-	"SELECT IFNULL(v.table_catalog, v.table_schema) AS table_catalog, v.table_schema, v.table_name, IFNULL(c.table_catalog, c.table_schema) AS table_catalog, c.table_schema, c.table_name, c.column_name FROM INFORMATION_SCHEMA.columns c INNER JOIN INFORMATION_SCHEMA.views v ON c.table_schema=v.table_schema AND c.table_name=v.table_name WHERE v.table_catalog = ##cat::string AND v.table_schema = ##schema::string AND v.table_name = ##name::string",
+	"SELECT IFNULL(v.table_catalog, v.table_schema) AS table_catalog, v.table_schema, v.table_name, IFNULL(c.table_catalog, c.table_schema) AS table_catalog, c.table_schema, c.table_name, c.column_name FROM INFORMATION_SCHEMA.columns c INNER JOIN INFORMATION_SCHEMA.views v ON c.table_schema=v.table_schema AND c.table_name=v.table_name WHERE IFNULL(v.table_catalog, v.table_schema) = BINARY ##cat::string AND v.table_schema = BINARY ##schema::string AND v.table_name = BINARY ##name::string",
 
         /* I_STMT_VIEWS_COLUMNS_ALL */
 	"SELECT IFNULL(v.table_catalog, v.table_schema) AS table_catalog, v.table_schema, v.table_name, IFNULL(c.table_catalog, c.table_schema) AS table_catalog, c.table_schema, c.table_name, c.column_name FROM INFORMATION_SCHEMA.columns c INNER JOIN INFORMATION_SCHEMA.views v ON c.table_schema=v.table_schema AND c.table_name=v.table_name",
 
         /* I_STMT_TRIGGERS */
-	"SELECT IFNULL(trigger_catalog, trigger_schema) AS trigger_catalog, trigger_schema, trigger_name, event_manipulation, IFNULL(event_object_catalog, event_object_schema) AS event_object_catalog, event_object_schema, event_object_table, action_statement, action_orientation, action_timing, NULL, trigger_name, trigger_name FROM INFORMATION_SCHEMA.triggers WHERE trigger_catalog = ##cat::string AND trigger_schema =  ##schema::string AND trigger_name = ##name::string",
+	"SELECT IFNULL(trigger_catalog, trigger_schema) AS trigger_catalog, trigger_schema, trigger_name, event_manipulation, IFNULL(event_object_catalog, event_object_schema) AS event_object_catalog, event_object_schema, event_object_table, action_statement, action_orientation, action_timing, NULL, trigger_name, trigger_name FROM INFORMATION_SCHEMA.triggers WHERE trigger_catalog = BINARY ##cat::string AND trigger_schema =  BINARY ##schema::string AND trigger_name = BINARY ##name::string",
 
         /* I_STMT_TRIGGERS_ALL */
 	"SELECT IFNULL(trigger_catalog, trigger_schema) AS trigger_catalog, trigger_schema, trigger_name, event_manipulation, IFNULL(event_object_catalog, event_object_schema) AS event_object_catalog, event_object_schema, event_object_table, action_statement, action_orientation, action_timing, NULL, trigger_name, trigger_name FROM INFORMATION_SCHEMA.triggers",
@@ -198,16 +193,16 @@ static gchar *internal_sql[] = {
 	"SELECT IFNULL(routine_catalog, routine_schema) AS specific_catalog, routine_schema AS specific_schema, routine_name AS specific_name, IFNULL(routine_catalog, routine_schema) AS routine_catalog, routine_schema, routine_name, routine_type, dtd_identifier AS return_type, FALSE AS returns_set, 0 AS nb_args, routine_body, routine_definition, external_name, external_language, parameter_style, CASE is_deterministic WHEN 'YES' THEN TRUE ELSE FALSE END AS is_deterministic, sql_data_access, FALSE AS is_null_call, routine_comment, routine_name, routine_name, definer FROM INFORMATION_SCHEMA.routines",
 
         /* I_STMT_ROUTINES */
-	"SELECT IFNULL(routine_catalog, routine_schema) AS specific_catalog, routine_schema AS specific_schema, routine_name AS specific_name, IFNULL(routine_catalog, routine_schema) AS routine_catalog, routine_schema, routine_name, routine_type, dtd_identifier AS return_type, FALSE AS returns_set, 0 AS nb_args, routine_body, routine_definition, external_name, external_language, parameter_style, CASE is_deterministic WHEN 'YES' THEN TRUE ELSE FALSE END AS is_deterministic, sql_data_access, FALSE AS is_null_call, routine_comment, routine_name, routine_name, definer FROM INFORMATION_SCHEMA.routines WHERE routine_catalog = ##cat::string AND routine_schema =  ##schema::string",
+	"SELECT IFNULL(routine_catalog, routine_schema) AS specific_catalog, routine_schema AS specific_schema, routine_name AS specific_name, IFNULL(routine_catalog, routine_schema) AS routine_catalog, routine_schema, routine_name, routine_type, dtd_identifier AS return_type, FALSE AS returns_set, 0 AS nb_args, routine_body, routine_definition, external_name, external_language, parameter_style, CASE is_deterministic WHEN 'YES' THEN TRUE ELSE FALSE END AS is_deterministic, sql_data_access, FALSE AS is_null_call, routine_comment, routine_name, routine_name, definer FROM INFORMATION_SCHEMA.routines WHERE routine_catalog = BINARY ##cat::string AND routine_schema =  BINARY ##schema::string",
 
         /* I_STMT_ROUTINES_ONE */
-	"SELECT IFNULL(routine_catalog, routine_schema) AS specific_catalog, routine_schema AS specific_schema, routine_name AS specific_name, IFNULL(routine_catalog, routine_schema) AS routine_catalog, routine_schema, routine_name, routine_type, dtd_identifier AS return_type, FALSE AS returns_set, 0 AS nb_args, routine_body, routine_definition, external_name, external_language, parameter_style, CASE is_deterministic WHEN 'YES' THEN TRUE ELSE FALSE END AS is_deterministic, sql_data_access, FALSE AS is_null_call, routine_comment, routine_name, routine_name, definer FROM INFORMATION_SCHEMA.routines WHERE routine_catalog = ##cat::string AND routine_schema =  ##schema::string AND routine_name = ##name::string",
+	"SELECT IFNULL(routine_catalog, routine_schema) AS specific_catalog, routine_schema AS specific_schema, routine_name AS specific_name, IFNULL(routine_catalog, routine_schema) AS routine_catalog, routine_schema, routine_name, routine_type, dtd_identifier AS return_type, FALSE AS returns_set, 0 AS nb_args, routine_body, routine_definition, external_name, external_language, parameter_style, CASE is_deterministic WHEN 'YES' THEN TRUE ELSE FALSE END AS is_deterministic, sql_data_access, FALSE AS is_null_call, routine_comment, routine_name, routine_name, definer FROM INFORMATION_SCHEMA.routines WHERE routine_catalog = BINARY ##cat::string AND routine_schema =  BINARY ##schema::string AND routine_name = BINARY ##name::string",
 
         /* I_STMT_ROUTINES_PAR_ALL */
 	"SELECT IFNULL(routine_catalog, routine_schema) AS specific_catalog, routine_schema AS specific_schema, routine_name AS specific_name, IFNULL(routine_catalog, routine_schema) AS routine_catalog, routine_schema, routine_name, routine_type, dtd_identifier AS return_type, FALSE AS returns_set, 0 AS nb_args, routine_body, routine_definition, external_name, external_language, parameter_style, CASE is_deterministic WHEN 'YES' THEN TRUE ELSE FALSE END AS is_deterministic, sql_data_access, FALSE AS is_null_call, routine_comment, routine_name, routine_name, definer FROM INFORMATION_SCHEMA.routines",
 
         /* I_STMT_ROUTINES_PAR */
-	"SELECT IFNULL(routine_catalog, routine_schema) AS specific_catalog, routine_schema AS specific_schema, routine_name AS specific_name, IFNULL(routine_catalog, routine_schema) AS routine_catalog, routine_schema, routine_name, routine_type, dtd_identifier AS return_type, FALSE AS returns_set, 0 AS nb_args, routine_body, routine_definition, external_name, external_language, parameter_style, CASE is_deterministic WHEN 'YES' THEN TRUE ELSE FALSE END AS is_deterministic, sql_data_access, FALSE AS is_null_call, routine_comment, routine_name, routine_name, definer FROM INFORMATION_SCHEMA.routines WHERE routine_catalog = ##cat::string AND routine_schema =  ##schema::string AND routine_name = ##name::string",
+	"SELECT IFNULL(routine_catalog, routine_schema) AS specific_catalog, routine_schema AS specific_schema, routine_name AS specific_name, IFNULL(routine_catalog, routine_schema) AS routine_catalog, routine_schema, routine_name, routine_type, dtd_identifier AS return_type, FALSE AS returns_set, 0 AS nb_args, routine_body, routine_definition, external_name, external_language, parameter_style, CASE is_deterministic WHEN 'YES' THEN TRUE ELSE FALSE END AS is_deterministic, sql_data_access, FALSE AS is_null_call, routine_comment, routine_name, routine_name, definer FROM INFORMATION_SCHEMA.routines WHERE routine_catalog = BINARY ##cat::string AND routine_schema =  BINARY ##schema::string AND routine_name = BINARY ##name::string",
 
         /* I_STMT_ROUTINES_COL_ALL */
 
@@ -277,7 +272,7 @@ _gda_mysql_meta__info (GdaServerProvider  *prov,
 	else {
 		gda_meta_store_set_reserved_keywords_func (store, _gda_mysql_get_reserved_keyword_func (cdata));
 		retval = gda_meta_store_modify (store, context->table_name, model, NULL, error, NULL);
-		g_object_unref (G_OBJECT(model));
+		g_object_unref (G_OBJECT (model));
 	}
 
         return retval;
@@ -714,11 +709,11 @@ _gda_mysql_meta__tables_views (GdaServerProvider  *prov,
 {	
 	GType col_types_tables[] = {
 		0, 0, 0, 0,
-		G_TYPE_BOOLEAN, G_TYPE_NONE
+		G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_NONE
 	};
 	GType col_types_views[] = {
 		G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
-		G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_NONE
+		G_TYPE_STRING, G_TYPE_STRING, G_TYPE_NONE
 	};
 	GdaDataModel *model_tables, *model_views;
 	gboolean retval;
@@ -772,8 +767,8 @@ _gda_mysql_meta_tables_views (GdaServerProvider  *prov,
 			      const GValue       *table_name_n)
 {
 	GType col_types_tables[] = {
-		0, 0, 0, 0,
-		G_TYPE_BOOLEAN, G_TYPE_NONE
+		G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+		G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_NONE
 	};
 	GType col_types_views[] = {
 		G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
@@ -807,20 +802,25 @@ _gda_mysql_meta_tables_views (GdaServerProvider  *prov,
 		if (model_tables == NULL)
 			retval = FALSE;
 		else {
+			copy.table_name = "_tables";
 			gda_meta_store_set_reserved_keywords_func (store, _gda_mysql_get_reserved_keyword_func (cdata));
-			retval = gda_meta_store_modify_with_context (store, &copy, model_tables, NULL);
-			g_object_unref (G_OBJECT(model_tables));
+			retval = gda_meta_store_modify_with_context (store, &copy, model_tables, error);
+			g_object_unref (G_OBJECT (model_tables));
 		}
 
+		if (!retval)
+			return FALSE;
+
 		model_views = gda_connection_statement_execute_select_full (cnc, internal_stmt[I_STMT_VIEWS_ALL], i_set, 
 									    GDA_STATEMENT_MODEL_RANDOM_ACCESS, col_types_views,
 									    error);
 		if (model_views == NULL)
 			retval = FALSE;
 		else {
+			copy.table_name = "_views";
 			gda_meta_store_set_reserved_keywords_func (store, _gda_mysql_get_reserved_keyword_func (cdata));
-			retval = gda_meta_store_modify_with_context (store, &copy, model_views, NULL);
-			g_object_unref (G_OBJECT(model_views));
+			retval = gda_meta_store_modify_with_context (store, &copy, model_views, error);
+			g_object_unref (G_OBJECT (model_views));
 		}
 
 	} else {
@@ -838,6 +838,8 @@ _gda_mysql_meta_tables_views (GdaServerProvider  *prov,
 			g_object_unref (G_OBJECT(model_tables));
 		}
 
+		if (!retval)
+			return FALSE;
 		model_views = gda_connection_statement_execute_select_full (cnc, internal_stmt[I_STMT_VIEW_NAMED], i_set, 
 									    GDA_STATEMENT_MODEL_RANDOM_ACCESS, col_types_views,
 									    error);
@@ -991,8 +993,8 @@ _gda_mysql_meta__columns (GdaServerProvider  *prov,
 	GType col_types[] = {
 		0, 0, 0, 0, G_TYPE_INT, G_TYPE_STRING,
 		G_TYPE_BOOLEAN,
-		0, 0, 0, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, 0, 0, 0, 0, 0, 0, 0,
-		G_TYPE_BOOLEAN,
+		G_TYPE_STRING, 0, 0, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, 0, 0, 0, 0, 0, 0, G_TYPE_STRING, G_TYPE_BOOLEAN,
+		G_TYPE_STRING,
 		G_TYPE_NONE
 	};
 	GdaDataModel *model, *proxy;
@@ -1035,7 +1037,7 @@ _gda_mysql_meta__columns (GdaServerProvider  *prov,
 
 		if (retval) {
 			gda_meta_store_set_reserved_keywords_func (store, _gda_mysql_get_reserved_keyword_func (cdata));
-			retval = gda_meta_store_modify_with_context (store, context, proxy/* model */, error);
+			retval = gda_meta_store_modify_with_context (store, context, proxy, error);
 		}
 
 		g_object_unref (G_OBJECT(proxy));
@@ -1058,8 +1060,8 @@ _gda_mysql_meta_columns (GdaServerProvider  *prov,
 	GType col_types[] = {
 		0, 0, 0, 0, G_TYPE_INT, G_TYPE_STRING,
 		G_TYPE_BOOLEAN,
-		0, 0, 0, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, 0, 0, 0, 0, 0, 0, 0,
-		G_TYPE_BOOLEAN,
+		G_TYPE_STRING, 0, 0, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, 0, 0, 0, 0, 0, 0, G_TYPE_STRING, G_TYPE_BOOLEAN,
+		G_TYPE_STRING,
 		G_TYPE_NONE
 	};
 	GdaDataModel *model, *proxy;
@@ -1082,6 +1084,7 @@ _gda_mysql_meta_columns (GdaServerProvider  *prov,
 		return FALSE;
 	if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), table_name, error))
 		return FALSE;
+
 	model = gda_connection_statement_execute_select_full (cnc, internal_stmt[I_STMT_COLUMNS_OF_TABLE], i_set,
 							      GDA_STATEMENT_MODEL_RANDOM_ACCESS, col_types, error);
 	if (model == NULL)
@@ -1228,8 +1231,8 @@ _gda_mysql_meta_constraints_tab (GdaServerProvider  *prov,
 				 const GValue       *table_catalog,
 				 const GValue       *table_schema, 
 				 const GValue       *table_name,
-				 const GValue       *constraint_name_n)
-{
+				 const GValue       *constraint_name_n){
+
 	GdaDataModel *model;
 	gboolean retval;
 	MysqlConnectionData *cdata;
@@ -1469,8 +1472,6 @@ _gda_mysql_meta__triggers (GdaServerProvider  *prov,
 			   GdaMetaContext     *context,
 			   GError            **error)
 {
-	// TO_IMPLEMENT;
-
 	GdaDataModel *model;
 	gboolean retval;
 	/* Check correct mysql server version. */
@@ -1506,8 +1507,6 @@ _gda_mysql_meta_triggers (GdaServerProvider  *prov,
 			  const GValue       *table_schema, 
 			  const GValue       *table_name)
 {
-	// TO_IMPLEMENT;
-
 	GdaDataModel *model;
 	gboolean retval;
 	/* Check correct mysql server version. */
@@ -1547,8 +1546,6 @@ _gda_mysql_meta__routines (GdaServerProvider  *prov,
 			   GdaMetaContext     *context,
 			   GError            **error)
 {
-	//TO_IMPLEMENT;
-
 	GdaDataModel *model;
 	gboolean retval;
 	MysqlConnectionData *cdata;
@@ -1578,9 +1575,6 @@ _gda_mysql_meta_routines (GdaServerProvider  *prov,
 			  const GValue       *routine_schema, 
 			  const GValue       *routine_name_n)
 {
-	//TO_IMPLEMENT;
-	//
-
 	GdaDataModel *model;
 	gboolean retval;
 	/* Check correct mysql server version. */
diff --git a/providers/mysql/gda-mysql-provider.c b/providers/mysql/gda-mysql-provider.c
index 2f9cbe5..08bd6aa 100644
--- a/providers/mysql/gda-mysql-provider.c
+++ b/providers/mysql/gda-mysql-provider.c
@@ -49,8 +49,23 @@
 static void gda_mysql_provider_class_init (GdaMysqlProviderClass  *klass);
 static void gda_mysql_provider_init       (GdaMysqlProvider       *provider,
 					   GdaMysqlProviderClass  *klass);
+static void gda_mysql_provider_set_property (GObject *object,
+					     guint param_id,
+					     const GValue *value,
+					     GParamSpec *pspec);
+static void gda_mysql_provider_get_property (GObject *object,
+					     guint param_id,
+					     GValue *value,
+					     GParamSpec *pspec);
 static GObjectClass *parent_class = NULL;
 
+/* properties */
+enum
+{
+        PROP_0,
+        PROP_IDENT_CASE_SENSITIVE, /* used for tests */
+};
+
 /*
  * GdaServerProvider's virtual methods
  */
@@ -221,9 +236,20 @@ static void
 gda_mysql_provider_class_init (GdaMysqlProviderClass  *klass)
 {
 	GdaServerProviderClass *provider_class = GDA_SERVER_PROVIDER_CLASS (klass);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
 	parent_class = g_type_class_peek_parent (klass);
 
+	/* Properties */
+        object_class->set_property = gda_mysql_provider_set_property;
+        object_class->get_property = gda_mysql_provider_get_property;
+
+	g_object_class_install_property (object_class, PROP_IDENT_CASE_SENSITIVE,
+                                         g_param_spec_boolean ("identifiers-case-sensitive", NULL, NULL, TRUE,
+							       G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+
+	/* virtual methods */
 	provider_class->get_version = gda_mysql_provider_get_version;
 	provider_class->get_server_version = gda_mysql_provider_get_server_version;
 	provider_class->get_name = gda_mysql_provider_get_name;
@@ -340,7 +366,9 @@ gda_mysql_provider_init (GdaMysqlProvider       *mysql_prv,
 	/* meta data init */
 	_gda_mysql_provider_meta_init ((GdaServerProvider*) mysql_prv);
 
-	/* TO_ADD: any other provider's init should be added here */
+	/* for tests */
+	mysql_prv->test_mode = FALSE;
+	mysql_prv->test_identifiers_case_sensitive = TRUE;
 }
 
 GType
@@ -369,12 +397,42 @@ gda_mysql_provider_get_type (void)
 	return type;
 }
 
+static void
+gda_mysql_provider_set_property (GObject *object,
+				 guint param_id,
+				 const GValue *value,
+				 GParamSpec *pspec)
+{
+	GdaMysqlProvider *mysql_prv;
+	mysql_prv = GDA_MYSQL_PROVIDER (object);
+	switch (param_id) {
+	case PROP_IDENT_CASE_SENSITIVE:
+		mysql_prv->test_identifiers_case_sensitive = g_value_get_boolean (value);
+		mysql_prv->test_mode = TRUE;
+		break;
+	}
+}
+
+static void
+gda_mysql_provider_get_property (GObject *object,
+				 guint param_id,
+				 GValue *value,
+				 GParamSpec *pspec)
+{
+	GdaMysqlProvider *mysql_prv;
+	mysql_prv = GDA_MYSQL_PROVIDER (object);
+	switch (param_id) {
+	case PROP_IDENT_CASE_SENSITIVE:
+		g_value_set_boolean (value, mysql_prv->test_identifiers_case_sensitive);
+		break;
+	}
+}
 
 /*
  * Get provider name request
  */
 static const gchar *
-gda_mysql_provider_get_name (GdaServerProvider  *provider)
+gda_mysql_provider_get_name (GdaServerProvider *provider)
 {
 	return MYSQL_PROVIDER_NAME;
 }
@@ -619,7 +677,7 @@ gda_mysql_provider_open_connection (GdaServerProvider               *provider,
 	cdata->version_long = mysql_get_server_version (mysql);
 	cdata->version = get_mysql_version (mysql);
 	cdata->short_version = get_mysql_short_version (mysql);
-	cdata->tables_case_sensitive = case_sensitive;
+	cdata->identifiers_case_sensitive = case_sensitive;
 
 	return TRUE;
 }
@@ -1484,7 +1542,7 @@ gda_mysql_provider_statement_execute (GdaServerProvider               *provider,
                 nps = real_prepare (provider, cnc, stmt, error);
                 if (!nps)
                         return NULL;
-                gda_pstmt_copy_contents ((GdaPStmt *) ps, (GdaPStmt *) nps);
+                //gda_pstmt_copy_contents ((GdaPStmt *) ps, (GdaPStmt *) nps);
 		g_object_unref (ps);
 		ps = nps;
 	}
@@ -1994,54 +2052,192 @@ _sql_identifier_needs_quotes (const gchar *str)
 	return FALSE;
 }
 
+/* Returns: @str */
+static gchar *
+my_remove_quotes (gchar *str)
+{
+        glong total;
+        gchar *ptr;
+        glong offset = 0;
+	char delim;
+	
+	if (!str)
+		return NULL;
+	delim = *str;
+	if ((delim != '`') && (delim != '"'))
+		return str;
+
+        total = strlen (str);
+        if (str[total-1] == delim) {
+		/* string is correclty terminated */
+		g_memmove (str, str+1, total-2);
+		total -=2;
+	}
+	else {
+		/* string is _not_ correclty terminated */
+		g_memmove (str, str+1, total-1);
+		total -=1;
+	}
+        str[total] = 0;
+
+        ptr = (gchar *) str;
+        while (offset < total) {
+                /* we accept the "''" as a synonym of "\'" */
+                if (*ptr == delim) {
+                        if (*(ptr+1) == delim) {
+                                g_memmove (ptr+1, ptr+2, total - offset);
+                                offset += 2;
+                        }
+                        else {
+                                *str = 0;
+                                return str;
+                        }
+                }
+                if (*ptr == '\\') {
+                        if (*(ptr+1) == '\\') {
+                                g_memmove (ptr+1, ptr+2, total - offset);
+                                offset += 2;
+                        }
+                        else {
+                                if (*(ptr+1) == delim) {
+                                        *ptr = delim;
+                                        g_memmove (ptr+1, ptr+2, total - offset);
+                                        offset += 2;
+                                }
+                                else {
+                                        *str = 0;
+                                        return str;
+                                }
+                        }
+                }
+                else
+                        offset ++;
+
+                ptr++;
+        }
+
+        return str;
+}
+
 static gchar *
 gda_mysql_identifier_quote (GdaServerProvider *provider, GdaConnection *cnc,
 			    const gchar *id,
-			    gboolean meta_store_convention, gboolean force_quotes)
+			    gboolean for_meta_store, gboolean force_quotes)
 {
 	GdaSqlReservedKeywordsFunc kwfunc;
 	MysqlConnectionData *cdata = NULL;
+	gboolean case_sensitive = TRUE;
 
 	if (cnc) {
 		cdata = (MysqlConnectionData*) gda_connection_internal_get_provider_data (cnc);
 		if (!cdata) 
 			return NULL;
+		case_sensitive = cdata->identifiers_case_sensitive;
 	}
+	else if (((GdaMysqlProvider*) provider)->test_mode)
+		case_sensitive = ((GdaMysqlProvider*) provider)->test_identifiers_case_sensitive;
 
 	kwfunc = _gda_mysql_get_reserved_keyword_func (cdata);
-	if (meta_store_convention) {
-		/* @id can either:
-		 * - be enquoted between double quotes
-		 * - not enquoted and in lower case
+
+	if (case_sensitive) {
+		/*
+		 * case sensitive mode
 		 */
-		if (*id == '"') {
-			/* must be quoted, replace '"' with '`' */
-			gchar *ptr, *retval;
-			retval = g_strdup (id);
-			*retval = '`';
-			for (ptr = retval + 1; *ptr; ptr++) {
-				if (*ptr == '"')
-					*ptr = '`';
+		if (for_meta_store) {
+			gchar *tmp, *ptr;
+			tmp = my_remove_quotes (g_strdup (id));
+			if (kwfunc (tmp)) {
+				ptr = gda_sql_identifier_add_quotes (tmp);
+				g_free (tmp);
+				return ptr;
+			}
+			for (ptr = tmp; *ptr; ptr++) {
+				if (((*ptr >= 'a') && (*ptr <= 'z')) ||
+				    ((*ptr >= '0') && (*ptr <= '9') && (ptr != tmp)) ||
+				    (*ptr == '_'))
+					continue;
+				else {
+					ptr = gda_sql_identifier_add_quotes (tmp);
+					g_free (tmp);
+					return ptr;
+				}
 			}
-			return retval;
+			return tmp;
+			/*			ptr = gda_sql_identifier_add_quotes (tmp);
+			g_free (tmp);
+			return ptr;*/
 		}
-		
-		if (kwfunc (id) || _sql_identifier_needs_quotes (id) || force_quotes)
+		else {
+			if ((*id == '`') || (*id == '"'))  {
+				/* there are already some quotes */
+				gchar *ptr, *tmp = g_strdup (id);
+				for (ptr = tmp; *ptr; ptr++)
+					if (*ptr == '"')
+						*ptr = '`';
+				return tmp;
+			}
 			return identifier_add_quotes (id);
-
-		/* nothing to do */
-		return g_strdup (id);
+		}
 	}
 	else {
-		if (*id == '`') {
-			/* there are already some quotes */
+		/*
+		 * case insensitive mode
+		 */
+		if (for_meta_store) {
+			gchar *tmp, *ptr;
+			tmp = my_remove_quotes (g_strdup (id));
+			if (kwfunc (tmp)) {
+				ptr = gda_sql_identifier_add_quotes (tmp);
+				g_free (tmp);
+				return ptr;
+			}
+			else if (0 && force_quotes) {
+				/* quote if non LC characters or digits at the 1st char or non allowed characters */
+				for (ptr = tmp; *ptr; ptr++) {
+					if (((*ptr >= 'a') && (*ptr <= 'z')) ||
+					    ((*ptr >= '0') && (*ptr <= '9') && (ptr != tmp)) ||
+					    (*ptr == '_'))
+						continue;
+					else {
+						ptr = gda_sql_identifier_add_quotes (tmp);
+						g_free (tmp);
+						return ptr;
+					}
+				}
+				return tmp;
+			}
+			else {
+				for (ptr = tmp; *ptr; ptr++) {
+					if ((*ptr >= 'A') && (*ptr <= 'Z'))
+						*ptr += 'a' - 'A';
+					else if (((*ptr >= 'a') && (*ptr <= 'z')) ||
+					    ((*ptr >= '0') && (*ptr <= '9') && (ptr != tmp)) ||
+					    (*ptr == '_'))
+						continue;
+					else {
+						ptr = gda_sql_identifier_add_quotes (tmp);
+						g_free (tmp);
+						return ptr;
+					}
+				}
+				return tmp;
+			}
+		}
+		else {
+			if ((*id == '`') || (*id == '"'))  {
+				/* there are already some quotes */
+				gchar *ptr, *tmp = g_strdup (id);
+				for (ptr = tmp; *ptr; ptr++)
+					if (*ptr == '"')
+						*ptr = '`';
+				return tmp;
+			}
+			if (kwfunc (id) || _sql_identifier_needs_quotes (id) || force_quotes)
+				return identifier_add_quotes (id);
+			
+			/* nothing to do */
 			return g_strdup (id);
 		}
-		if (kwfunc (id) || _sql_identifier_needs_quotes (id) || force_quotes)
-			return identifier_add_quotes (id);
-		
-		/* nothing to do */
-		return g_strdup (id);
 	}
 }
 
diff --git a/providers/mysql/gda-mysql-provider.h b/providers/mysql/gda-mysql-provider.h
index af8f03f..826baee 100644
--- a/providers/mysql/gda-mysql-provider.h
+++ b/providers/mysql/gda-mysql-provider.h
@@ -36,6 +36,8 @@ typedef struct _GdaMysqlProviderClass GdaMysqlProviderClass;
 
 struct _GdaMysqlProvider {
 	GdaServerProvider      provider;
+	gboolean               test_mode; /* for tests ONLY */
+	gboolean               test_identifiers_case_sensitive; /* for tests ONLY */
 };
 
 struct _GdaMysqlProviderClass {
diff --git a/providers/mysql/gda-mysql-recordset.c b/providers/mysql/gda-mysql-recordset.c
index aae08cf..2729afe 100644
--- a/providers/mysql/gda-mysql-recordset.c
+++ b/providers/mysql/gda-mysql-recordset.c
@@ -1,8 +1,9 @@
 /* GDA Mysql provider
- * Copyright (C) 2008 The GNOME Foundation.
+ * Copyright (C) 2008 - 2009 The GNOME Foundation.
  *
  * AUTHORS:
  *      Carlos Savoretti <csavoretti gmail com>
+ *      Vivien Malerba <malerba gnome-db org>
  *
  * This Library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public License as
@@ -28,6 +29,7 @@
 #include "gda-mysql.h"
 #include "gda-mysql-recordset.h"
 #include "gda-mysql-provider.h"
+#include "gda-mysql-util.h"
 
 #ifdef HAVE_LOCALE_H
 #include <locale.h>
@@ -411,7 +413,6 @@ gda_mysql_recordset_new (GdaConnection            *cnc,
         ps->stmt_used = TRUE;
         if (!_GDA_PSTMT (ps)->types && (_GDA_PSTMT (ps)->ncols > 0)) {
 		/* create prepared statement's columns */
-		GSList *list;
 		for (i = 0; i < _GDA_PSTMT (ps)->ncols; i++)
 			_GDA_PSTMT (ps)->tmpl_columns = g_slist_prepend (_GDA_PSTMT (ps)->tmpl_columns, 
 									 gda_column_new ());
@@ -432,88 +433,99 @@ gda_mysql_recordset_new (GdaConnection            *cnc,
 				}
 			}
 		}
+	}
 
-		
-		MYSQL_RES *mysql_res = mysql_stmt_result_metadata (ps->mysql_stmt);
-		MYSQL_FIELD *mysql_fields = mysql_fetch_fields (mysql_res);
-		
-		MYSQL_BIND *mysql_bind_result = g_new0 (MYSQL_BIND, GDA_PSTMT (ps)->ncols);
-
-		/* fill GdaColumn's data */
-		for (i=0, list = _GDA_PSTMT (ps)->tmpl_columns; 
-		     i < GDA_PSTMT (ps)->ncols; 
-		     i++, list = list->next) {
-			GdaColumn *column = GDA_COLUMN (list->data);
+	/* get rid of old bound result if any */
+	if (ps->mysql_bind_result) {
+		gint i;
+		for (i = 0; i < ((GdaPStmt *) ps)->ncols; ++i) {
+			g_free (ps->mysql_bind_result[i].buffer);
+			g_free (ps->mysql_bind_result[i].is_null);
+			g_free (ps->mysql_bind_result[i].length);
+		}
+		g_free (ps->mysql_bind_result);
+		ps->mysql_bind_result = NULL;
+	}
 
-			/* use C API to set columns' information using gda_column_set_*() */
-			MYSQL_FIELD *field = &mysql_fields[i];
+	/* fill bind result */
+	MYSQL_RES *mysql_res = mysql_stmt_result_metadata (ps->mysql_stmt);
+	MYSQL_FIELD *mysql_fields = mysql_fetch_fields (mysql_res);
+	
+	MYSQL_BIND *mysql_bind_result = g_new0 (MYSQL_BIND, GDA_PSTMT (ps)->ncols);
+	GSList *list;
 
-			GType gtype = _GDA_PSTMT(ps)->types[i];
-			if (gtype == 0) {
-				gtype = _gda_mysql_type_to_gda (cdata, field->type);
-				_GDA_PSTMT(ps)->types[i] = gtype;
-			}
-			gda_column_set_g_type (column, gtype);
-			gda_column_set_name (column, field->name);
-			gda_column_set_description (column, field->name);
-			
-			/* binding results with types */
-			mysql_bind_result[i].buffer_type = field->type;
-			mysql_bind_result[i].is_unsigned = field->flags & UNSIGNED_FLAG ? TRUE : FALSE;
-			mysql_bind_result[i].is_null = g_malloc0 (sizeof(my_bool));
-
-			switch (mysql_bind_result[i].buffer_type) {
-			case MYSQL_TYPE_TINY:
-			case MYSQL_TYPE_SHORT:
-			case MYSQL_TYPE_INT24:
-			case MYSQL_TYPE_LONG:
-			case MYSQL_TYPE_YEAR:
-				mysql_bind_result[i].buffer = g_malloc0 (sizeof(int));
-				break;
-			case MYSQL_TYPE_LONGLONG:
-				mysql_bind_result[i].buffer = g_malloc0 (sizeof(long long));
-				break;
-			case MYSQL_TYPE_NULL:
-				break;
-			case MYSQL_TYPE_TIME:
-			case MYSQL_TYPE_DATE:
-			case MYSQL_TYPE_DATETIME:
-			case MYSQL_TYPE_TIMESTAMP:
-				mysql_bind_result[i].buffer = g_malloc0 (sizeof(MYSQL_TIME));
-				break;
-			case MYSQL_TYPE_FLOAT:
-			case MYSQL_TYPE_DOUBLE:
-				mysql_bind_result[i].buffer = g_malloc0 (sizeof(double));
-				break;
-			case MYSQL_TYPE_STRING:
-			case MYSQL_TYPE_VAR_STRING:
-			case MYSQL_TYPE_BLOB:
-			case MYSQL_TYPE_TINY_BLOB:
-			case MYSQL_TYPE_MEDIUM_BLOB:
-			case MYSQL_TYPE_LONG_BLOB:
-			case MYSQL_TYPE_NEWDECIMAL:
-			case MYSQL_TYPE_BIT:
-				mysql_bind_result[i].buffer = g_malloc0 (field->max_length + 1);
-				mysql_bind_result[i].buffer_length = field->max_length + 1;
-				mysql_bind_result[i].length = g_malloc0 (sizeof(unsigned long));
-				break;
-			default:
-				g_warning (_("Invalid column bind data type. %d\n"),
-					   mysql_bind_result[i].buffer_type);
-			}
-			/*g_print ("%s(): NAME=%s, TYPE=%d, GTYPE=%s\n", 
-			  __FUNCTION__, field->name, field->type, g_type_name (gtype));*/
+	for (i=0, list = _GDA_PSTMT (ps)->tmpl_columns; 
+	     i < GDA_PSTMT (ps)->ncols; 
+	     i++, list = list->next) {
+		GdaColumn *column = GDA_COLUMN (list->data);
+		
+		/* use C API to set columns' information using gda_column_set_*() */
+		MYSQL_FIELD *field = &mysql_fields[i];
+		
+		GType gtype = _GDA_PSTMT(ps)->types[i];
+		if (gtype == 0) {
+			gtype = _gda_mysql_type_to_gda (cdata, field->type);
+			_GDA_PSTMT(ps)->types[i] = gtype;
 		}
+		gda_column_set_g_type (column, gtype);
+		gda_column_set_name (column, field->name);
+		gda_column_set_description (column, field->name);
 		
-                if (mysql_stmt_bind_result (ps->mysql_stmt, mysql_bind_result)) {
-                        g_warning ("mysql_stmt_bind_result failed: %s\n",
-				   mysql_stmt_error (ps->mysql_stmt));
-                }
+		/* binding results with types */
+		mysql_bind_result[i].buffer_type = field->type;
+		mysql_bind_result[i].is_unsigned = field->flags & UNSIGNED_FLAG ? TRUE : FALSE;
+		mysql_bind_result[i].is_null = g_malloc0 (sizeof(my_bool));
 		
-		mysql_free_result (mysql_res);
-
-		ps->mysql_bind_result = mysql_bind_result;
-        }
+		switch (mysql_bind_result[i].buffer_type) {
+		case MYSQL_TYPE_TINY:
+		case MYSQL_TYPE_SHORT:
+		case MYSQL_TYPE_INT24:
+		case MYSQL_TYPE_LONG:
+		case MYSQL_TYPE_YEAR:
+			mysql_bind_result[i].buffer = g_malloc0 (sizeof(int));
+			break;
+		case MYSQL_TYPE_LONGLONG:
+			mysql_bind_result[i].buffer = g_malloc0 (sizeof(long long));
+			break;
+		case MYSQL_TYPE_NULL:
+			break;
+		case MYSQL_TYPE_TIME:
+		case MYSQL_TYPE_DATE:
+		case MYSQL_TYPE_DATETIME:
+		case MYSQL_TYPE_TIMESTAMP:
+			mysql_bind_result[i].buffer = g_malloc0 (sizeof(MYSQL_TIME));
+			break;
+		case MYSQL_TYPE_FLOAT:
+		case MYSQL_TYPE_DOUBLE:
+			mysql_bind_result[i].buffer = g_malloc0 (sizeof(double));
+			break;
+		case MYSQL_TYPE_STRING:
+		case MYSQL_TYPE_VAR_STRING:
+		case MYSQL_TYPE_BLOB:
+		case MYSQL_TYPE_TINY_BLOB:
+		case MYSQL_TYPE_MEDIUM_BLOB:
+		case MYSQL_TYPE_LONG_BLOB:
+		case MYSQL_TYPE_NEWDECIMAL:
+		case MYSQL_TYPE_BIT:
+			mysql_bind_result[i].buffer = g_malloc0 (field->max_length + 1);
+			mysql_bind_result[i].buffer_length = field->max_length + 1;
+			mysql_bind_result[i].length = g_malloc0 (sizeof(unsigned long));
+			break;
+		default:
+			g_warning (_("Invalid column bind data type. %d\n"),
+				   mysql_bind_result[i].buffer_type);
+		}
+		/*g_print ("%s(): NAME=%s, TYPE=%d, GTYPE=%s\n", 
+		  __FUNCTION__, field->name, field->type, g_type_name (gtype));*/
+	}
+	
+	if (mysql_stmt_bind_result (ps->mysql_stmt, mysql_bind_result)) {
+		g_warning ("mysql_stmt_bind_result failed: %s\n",
+			   mysql_stmt_error (ps->mysql_stmt));
+	}
+	
+	mysql_free_result (mysql_res);
+	ps->mysql_bind_result = mysql_bind_result;
 
 	/* determine access mode: RANDOM or CURSOR FORWARD are the only supported */
 	if (flags & GDA_DATA_MODEL_ACCESS_RANDOM)
@@ -537,7 +549,7 @@ gda_mysql_recordset_new (GdaConnection            *cnc,
 	model->priv->mysql_stmt = ps->mysql_stmt;
 
 	((GdaDataSelect *) model)->advertized_nrows = mysql_stmt_affected_rows (ps->mysql_stmt);
-	
+
         return GDA_DATA_MODEL (model);
 }
 
@@ -563,20 +575,48 @@ gda_mysql_recordset_fetch_nb_rows (GdaDataSelect *model)
 }
 
 static GdaRow *
-new_row_from_mysql_stmt (GdaMysqlRecordset  *imodel, gint  rownum, GError **error)
+new_row_from_mysql_stmt (GdaMysqlRecordset *imodel, gint rownum, GError **error)
 {
 	//g_print ("%s(): NCOLS=%d  ROWNUM=%d\n", __func__, ((GdaDataSelect *) imodel)->prep_stmt->ncols, rownum);
-
+	int res;
+	MYSQL_BIND *mysql_bind_result;
 	g_return_val_if_fail (imodel->priv->mysql_stmt != NULL, NULL);
 
-	if (mysql_stmt_fetch (imodel->priv->mysql_stmt)) {
-		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_DATA_ERROR,
-			     _("Can't fetch data from server"));
+	mysql_bind_result = ((GdaMysqlPStmt *) ((GdaDataSelect *) imodel)->prep_stmt)->mysql_bind_result;
+	g_assert (mysql_bind_result);
+
+	res = mysql_stmt_fetch (imodel->priv->mysql_stmt);
+	if (res == MYSQL_NO_DATA) {
+		/* should not happen */
+		g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
+			     "No more data, please report this bug to "
+			     "http://bugzilla.gnome.org/ for the \"libgda\" product and the MySQL provider.");
+	}
+	else if (res == MYSQL_DATA_TRUNCATED) {
+		GString *string;
+
+		string = g_string_new ("Truncated data, please report this bug to "
+				       "http://bugzilla.gnome.org/ for the \"libgda\" product and the MySQL provider.");
+
+		gint col;
+		for (col = 0; col < ((GdaDataSelect *) imodel)->prep_stmt->ncols; ++col) {
+			my_bool truncated;
+			mysql_bind_result[col].error = &truncated;
+			mysql_stmt_fetch_column (imodel->priv->mysql_stmt, &(mysql_bind_result[col]),
+						 (unsigned int)col, 0);
+			if (truncated)
+				g_string_append_printf (string, "\n  column %d is truncated\n", col);
+			mysql_bind_result[col].error = NULL;
+		}
+		g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR, "%s", string->str);
+		g_string_free (string, TRUE);
+
+		return NULL;
+	}
+	else if (res) {
+		_gda_mysql_make_error (imodel->priv->cnc, NULL, imodel->priv->mysql_stmt, error);
 		return NULL;
 	}
-	
-	MYSQL_BIND *mysql_bind_result = ((GdaMysqlPStmt *) ((GdaDataSelect *) imodel)->prep_stmt)->mysql_bind_result;
-	g_assert (mysql_bind_result);
 	
 	GdaRow *row = gda_row_new (((GdaDataSelect *) imodel)->prep_stmt->ncols);
 	gint col;
@@ -744,12 +784,33 @@ new_row_from_mysql_stmt (GdaMysqlRecordset  *imodel, gint  rownum, GError **erro
 				gda_value_set_binary (value, &binary);
 			}
 			else if (type == G_TYPE_DOUBLE) {
-				setlocale (LC_NUMERIC, "C");
-				g_value_set_double (value, atof (strvalue));
-				setlocale (LC_NUMERIC, gda_numeric_locale);
+				if (length > 0) {
+					setlocale (LC_NUMERIC, "C");
+					g_value_set_double (value, atof (strvalue));
+					setlocale (LC_NUMERIC, gda_numeric_locale);
+				}
+				else {
+					/* error: wrong column type */
+					gda_row_invalidate_value (row, value);
+					g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+						     GDA_SERVER_PROVIDER_DATA_ERROR,
+						     _("Invalid column bind data type. %d\n"),
+						     mysql_bind_result[i].buffer_type);
+					break;
+				}
 			}
 			else if (type == G_TYPE_BOOLEAN) {
-				g_value_set_boolean (value, atoi (strvalue));
+				if (length > 0)
+					g_value_set_boolean (value, atoi (strvalue));
+				else {
+					/* error: wrong column type */
+					gda_row_invalidate_value (row, value);
+					g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+						     GDA_SERVER_PROVIDER_DATA_ERROR,
+						     _("Invalid column bind data type. %d\n"),
+						     mysql_bind_result[i].buffer_type);
+					break;
+				}
 			}
 			else {
 				gda_row_invalidate_value (row, value);
@@ -799,7 +860,6 @@ gda_mysql_recordset_fetch_random (GdaDataSelect  *model,
 
 	imodel = GDA_MYSQL_RECORDSET (model);
 
-	// TO_IMPLEMENT;
 	if (*row)
 		return TRUE;
 	
diff --git a/providers/mysql/gda-mysql.h b/providers/mysql/gda-mysql.h
index c601771..88b830b 100644
--- a/providers/mysql/gda-mysql.h
+++ b/providers/mysql/gda-mysql.h
@@ -51,7 +51,7 @@ typedef struct {
 	gchar            *short_version;
 
 	/* specifies how case sensitiveness is */
-	gboolean          tables_case_sensitive;
+	gboolean          identifiers_case_sensitive;
 	
 } MysqlConnectionData;
 
diff --git a/providers/postgres/gda-postgres-provider.c b/providers/postgres/gda-postgres-provider.c
index 37f3d6f..ad9955a 100644
--- a/providers/postgres/gda-postgres-provider.c
+++ b/providers/postgres/gda-postgres-provider.c
@@ -75,7 +75,7 @@ static gchar              *gda_postgres_provider_render_operation (GdaServerProv
 								   GdaServerOperation *op, GError **error);
 
 static gboolean            gda_postgres_provider_perform_operation (GdaServerProvider *provider, GdaConnection *cnc,
-								    GdaServerOperation *op, guint *task_id, 
+								    GdaServerOperation *op, guint *task_id,
 								    GdaServerProviderAsyncCallback async_cb, gpointer cb_data,
 								    GError **error);
 /* transactions */
@@ -107,16 +107,16 @@ static const gchar*        gda_postgres_provider_get_default_dbms_type (GdaServe
 /* statements */
 static GdaSqlParser        *gda_postgres_provider_create_parser (GdaServerProvider *provider, GdaConnection *cnc);
 static gchar               *gda_postgres_provider_statement_to_sql  (GdaServerProvider *provider, GdaConnection *cnc,
-								     GdaStatement *stmt, GdaSet *params, 
+								     GdaStatement *stmt, GdaSet *params,
 								     GdaStatementSqlFlag flags,
 								     GSList **params_used, GError **error);
 static gboolean             gda_postgres_provider_statement_prepare (GdaServerProvider *provider, GdaConnection *cnc,
 								     GdaStatement *stmt, GError **error);
 static GObject             *gda_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnection *cnc,
 								     GdaStatement *stmt, GdaSet *params,
-								     GdaStatementModelUsage model_usage, 
-								     GType *col_types, GdaSet **last_inserted_row, 
-								     guint *task_id, GdaServerProviderExecCallback async_cb, 
+								     GdaStatementModelUsage model_usage,
+								     GType *col_types, GdaSet **last_inserted_row,
+								     guint *task_id, GdaServerProviderExecCallback async_cb,
 								     gpointer cb_data, GError **error);
 
 /* Quoting */
@@ -125,24 +125,24 @@ static gchar               *gda_postgresql_identifier_quote    (GdaServerProvide
 								gboolean meta_store_convention, gboolean force_quotes);
 
 /* distributed transactions */
-static gboolean gda_postgres_provider_xa_start    (GdaServerProvider *provider, GdaConnection *cnc, 
+static gboolean gda_postgres_provider_xa_start    (GdaServerProvider *provider, GdaConnection *cnc,
 						   const GdaXaTransactionId *xid, GError **error);
 
-static gboolean gda_postgres_provider_xa_end      (GdaServerProvider *provider, GdaConnection *cnc, 
+static gboolean gda_postgres_provider_xa_end      (GdaServerProvider *provider, GdaConnection *cnc,
 						   const GdaXaTransactionId *xid, GError **error);
-static gboolean gda_postgres_provider_xa_prepare  (GdaServerProvider *provider, GdaConnection *cnc, 
+static gboolean gda_postgres_provider_xa_prepare  (GdaServerProvider *provider, GdaConnection *cnc,
 						   const GdaXaTransactionId *xid, GError **error);
 
-static gboolean gda_postgres_provider_xa_commit   (GdaServerProvider *provider, GdaConnection *cnc, 
+static gboolean gda_postgres_provider_xa_commit   (GdaServerProvider *provider, GdaConnection *cnc,
 						   const GdaXaTransactionId *xid, GError **error);
-static gboolean gda_postgres_provider_xa_rollback (GdaServerProvider *provider, GdaConnection *cnc, 
+static gboolean gda_postgres_provider_xa_rollback (GdaServerProvider *provider, GdaConnection *cnc,
 						   const GdaXaTransactionId *xid, GError **error);
 
-static GList   *gda_postgres_provider_xa_recover  (GdaServerProvider *provider, GdaConnection *cnc, 
+static GList   *gda_postgres_provider_xa_recover  (GdaServerProvider *provider, GdaConnection *cnc,
 						   GError **error);
 
-/* 
- * private connection data destroy 
+/*
+ * private connection data destroy
  */
 static void gda_postgres_free_cnc_data (PostgresConnectionData *cdata);
 
@@ -258,7 +258,7 @@ gda_postgres_provider_class_init (GdaPostgresProviderClass *klass)
 	provider_class->meta_funcs.routine_col = _gda_postgres_meta_routine_col;
 	provider_class->meta_funcs._routine_par = _gda_postgres_meta__routine_par;
 	provider_class->meta_funcs.routine_par = _gda_postgres_meta_routine_par;
-	
+
 	provider_class->xa_funcs = g_new0 (GdaServerProviderXa, 1);
 	provider_class->xa_funcs->xa_start = gda_postgres_provider_xa_start;
 	provider_class->xa_funcs->xa_end = gda_postgres_provider_xa_end;
@@ -288,7 +288,7 @@ gda_postgres_provider_init (GdaPostgresProvider *postgres_prv, GdaPostgresProvid
 	internal_stmt = g_new0 (GdaStatement *, sizeof (internal_sql) / sizeof (gchar*));
 	for (i = I_STMT_BEGIN; i < sizeof (internal_sql) / sizeof (gchar*); i++) {
 		internal_stmt[i] = gda_sql_parser_parse_string (parser, internal_sql[i], NULL, NULL);
-		if (!internal_stmt[i]) 
+		if (!internal_stmt[i])
 			g_error ("Could not parse internal statement: %s\n", internal_sql[i]);
 	}
 
@@ -332,7 +332,7 @@ gda_postgres_provider_get_name (GdaServerProvider *provider)
 	return POSTGRES_PROVIDER_NAME;
 }
 
-/* 
+/*
  * Get provider's version, no need to change this
  */
 static const gchar *
@@ -386,7 +386,7 @@ pq_notice_processor (PostgresConnectionData *cdata, const char *message)
         gda_connection_event_set_description (error, message);
         gda_connection_event_set_code (error, -1);
         gda_connection_event_set_source (error, gda_connection_get_provider_name (cdata->cnc));
-        gda_connection_event_set_sqlstate (error, "-1"); 
+        gda_connection_event_set_sqlstate (error, "-1");
 
         gda_connection_add_event (cdata->cnc, error);
 }
@@ -504,13 +504,13 @@ get_connection_type_list (PostgresConnectionData *cdata)
 		g_free (query);
 
 		/* query to fetch the oid of the 'any' data type */
-		pg_res_anyoid = _gda_postgres_PQexec_wrap (cdata->cnc, cdata->pconn, 
+		pg_res_anyoid = _gda_postgres_PQexec_wrap (cdata->cnc, cdata->pconn,
 							   "SELECT t.oid FROM pg_catalog.pg_type t WHERE t.typname = 'any'");
 	}
 
 	if (!pg_res || (PQresultStatus (pg_res) != PGRES_TUPLES_OK) ||
 	    !pg_res_avoid || (PQresultStatus (pg_res_avoid) != PGRES_TUPLES_OK) ||
-	    ((cdata->version_float >= 7.3) && 
+	    ((cdata->version_float >= 7.3) &&
 	     (!pg_res_anyoid || (PQresultStatus (pg_res_anyoid) != PGRES_TUPLES_OK)))) {
 		if (pg_res)
 			PQclear (pg_res);
@@ -548,7 +548,7 @@ get_connection_type_list (PostgresConnectionData *cdata)
 	string = NULL;
 	nrows = PQntuples (pg_res_avoid);
 	for (i = 0; i < nrows; i++) {
-		if (!string) 
+		if (!string)
 			string = g_string_new (PQgetvalue (pg_res_avoid, i, 0));
 		else
 			g_string_append_printf (string, ", %s", PQgetvalue (pg_res_avoid, i, 0));
@@ -570,7 +570,7 @@ get_connection_type_list (PostgresConnectionData *cdata)
 	return 0;
 }
 
-/* 
+/*
  * Open connection request
  *
  * In this function, the following _must_ be done:
@@ -674,7 +674,7 @@ gda_postgres_provider_open_connection (GdaServerProvider *provider, GdaConnectio
                                    pq_requiressl ? " requiressl=" : "",
                                    pq_requiressl ? pq_requiressl : "",
                                    NULL);
-	
+
 	/* open the real connection to the database */
 	PGconn *pconn;
 	pconn = PQconnectdb (conn_string);
@@ -685,7 +685,7 @@ gda_postgres_provider_open_connection (GdaServerProvider *provider, GdaConnectio
                 PQfinish (pconn);
                 return FALSE;
         }
-	
+
 	/* Create a new instance of the provider specific data associated to a connection (PostgresConnectionData),
 	 * and set its contents */
 	PostgresConnectionData *cdata;
@@ -761,7 +761,7 @@ gda_postgres_provider_open_connection (GdaServerProvider *provider, GdaConnectio
                         return FALSE;
                 }
         }
-	
+
 	/* attach connection data */
 	gda_connection_internal_set_provider_data (cnc, cdata, (GDestroyNotify) gda_postgres_free_cnc_data);
 
@@ -778,7 +778,7 @@ gda_postgres_provider_open_connection (GdaServerProvider *provider, GdaConnectio
 	return TRUE;
 }
 
-/* 
+/*
  * Close connection request
  *
  * In this function, the following _must_ be done:
@@ -797,10 +797,10 @@ gda_postgres_provider_close_connection (GdaServerProvider *provider, GdaConnecti
 
 	/* Close the connection using the C API */
 	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
-	if (!cdata) 
+	if (!cdata)
 		return FALSE;
 
-	/* Free the PostgresConnectionData structure and its contents 
+	/* Free the PostgresConnectionData structure and its contents
 	 * (will also actually close the connection to the server )*/
 	gda_postgres_free_cnc_data (cdata);
 	gda_connection_internal_set_provider_data (cnc, NULL, NULL);
@@ -822,7 +822,7 @@ gda_postgres_provider_get_server_version (GdaServerProvider *provider, GdaConnec
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
 
 	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
-	if (!cdata) 
+	if (!cdata)
 		return FALSE;
 
 	return cdata->version;
@@ -842,7 +842,7 @@ gda_postgres_provider_get_database (GdaServerProvider *provider, GdaConnection *
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
 
 	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
-	if (!cdata) 
+	if (!cdata)
 		return NULL;
 
 	return (const char *) PQdb ((const PGconn *) cdata->pconn);
@@ -1015,7 +1015,7 @@ gda_postgres_provider_render_operation (GdaServerProvider *provider, GdaConnecti
  */
 static gboolean
 gda_postgres_provider_perform_operation (GdaServerProvider *provider, GdaConnection *cnc,
-					 GdaServerOperation *op, guint *task_id, 
+					 GdaServerOperation *op, guint *task_id,
 					 GdaServerProviderAsyncCallback async_cb, gpointer cb_data, GError **error)
 {
         GdaServerOperationType optype;
@@ -1032,14 +1032,14 @@ gda_postgres_provider_perform_operation (GdaServerProvider *provider, GdaConnect
 		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
 	}
         optype = gda_server_operation_get_op_type (op);
-	if (!cnc && 
+	if (!cnc &&
 	    ((optype == GDA_SERVER_OPERATION_CREATE_DB) ||
 	     (optype == GDA_SERVER_OPERATION_DROP_DB))) {
 		GValue *value;
 		PGconn *pconn;
 		PGresult *pg_res;
 		GString *string;
-		
+
 		const gchar *pq_host = NULL;
 		const gchar *pq_db = NULL;
 		gint         pq_port = -1;
@@ -1051,7 +1051,7 @@ gda_postgres_provider_perform_operation (GdaServerProvider *provider, GdaConnect
 		value = (GValue *) gda_server_operation_get_value_at (op, "/SERVER_CNX_P/HOST");
 		if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value))
 			pq_host = g_value_get_string (value);
-		
+
 		value = (GValue *) gda_server_operation_get_value_at (op, "/SERVER_CNX_P/PORT");
 		if (value && G_VALUE_HOLDS (value, G_TYPE_INT) && (g_value_get_int (value) > 0))
 			pq_port = g_value_get_int (value);
@@ -1113,7 +1113,7 @@ gda_postgres_provider_perform_operation (GdaServerProvider *provider, GdaConnect
 				PQfinish (pconn);
 				return FALSE;
 			}
-			
+
 			PQfinish (pconn);
 			return TRUE;
 		}
@@ -1138,7 +1138,7 @@ gda_postgres_provider_begin_transaction (GdaServerProvider *provider, GdaConnect
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
 
 	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
-	if (!cdata) 
+	if (!cdata)
 		return FALSE;
 
 	/* transaction's parameters */
@@ -1164,13 +1164,13 @@ gda_postgres_provider_begin_transaction (GdaServerProvider *provider, GdaConnect
                 case GDA_TRANSACTION_ISOLATION_READ_UNCOMMITTED:
 			g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR,
 				     "%s", _("Transactions are not supported in read uncommitted isolation level"));
-                        gda_connection_add_event_string (cnc, 
+                        gda_connection_add_event_string (cnc,
 							 _("Transactions are not supported in read uncommitted isolation level"));
                         return FALSE;
                 case GDA_TRANSACTION_ISOLATION_REPEATABLE_READ:
 			g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR,
 				     "%s", _("Transactions are not supported in repeatable read isolation level"));
-                        gda_connection_add_event_string (cnc, 
+                        gda_connection_add_event_string (cnc,
 							 _("Transactions are not supported in repeatable read isolation level"));
                         return FALSE;
                 case GDA_TRANSACTION_ISOLATION_SERIALIZABLE :
@@ -1222,7 +1222,7 @@ gda_postgres_provider_commit_transaction (GdaServerProvider *provider, GdaConnec
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
 
 	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
-	if (!cdata) 
+	if (!cdata)
 		return FALSE;
 
 	/* COMMIT statement */
@@ -1246,7 +1246,7 @@ gda_postgres_provider_rollback_transaction (GdaServerProvider *provider,
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
 
 	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
-	if (!cdata) 
+	if (!cdata)
 		return FALSE;
 
 	/* ROLLBACK statement */
@@ -1270,7 +1270,7 @@ gda_postgres_provider_add_savepoint (GdaServerProvider *provider, GdaConnection
 	g_return_val_if_fail (name && *name, FALSE);
 
 	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
-	if (!cdata) 
+	if (!cdata)
 		return FALSE;
 
 	/* Statement */
@@ -1320,7 +1320,7 @@ gda_postgres_provider_rollback_savepoint (GdaServerProvider *provider, GdaConnec
 	g_return_val_if_fail (name && *name, FALSE);
 
 	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
-	if (!cdata) 
+	if (!cdata)
 		return FALSE;
 
 	/* Statement */
@@ -1370,7 +1370,7 @@ gda_postgres_provider_delete_savepoint (GdaServerProvider *provider, GdaConnecti
 	g_return_val_if_fail (name && *name, FALSE);
 
 	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
-	if (!cdata) 
+	if (!cdata)
 		return FALSE;
 
 	/* Statement */
@@ -1438,7 +1438,7 @@ gda_postgres_provider_supports_feature (GdaServerProvider *provider, GdaConnecti
 			PostgresConnectionData *cdata;
 
 			cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
-			if (!cdata) 
+			if (!cdata)
 				return FALSE;
                         if (cdata->version_float >= 7.3)
                                 return TRUE;
@@ -1464,7 +1464,7 @@ gda_postgres_provider_supports_feature (GdaServerProvider *provider, GdaConnecti
  * The recommended method is to create GdaDataHandler objects only when they are needed and to keep a reference to them
  * for further usage, using the gda_server_provider_handler_declare() method.
  *
- * The implementation shown here does not define any specific data handler, but there should be some for at least 
+ * The implementation shown here does not define any specific data handler, but there should be some for at least
  * binary and time related types.
  */
 static GdaDataHandler *
@@ -1593,7 +1593,7 @@ gda_postgres_provider_create_parser (GdaServerProvider *provider, GdaConnection
 
 /*
  * GdaStatement to SQL request
- * 
+ *
  * This method renders a #GdaStatement into its SQL representation.
  *
  * The implementation show here simply calls gda_statement_to_sql_extended() but the rendering
@@ -1640,7 +1640,7 @@ gda_postgres_provider_statement_prepare (GdaServerProvider *provider, GdaConnect
 
 	/* get private connection data */
 	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
-	if (!cdata) 
+	if (!cdata)
 		return FALSE;
 
 	/* render as SQL understood by PostgreSQL */
@@ -1698,7 +1698,7 @@ gda_postgres_provider_statement_prepare (GdaServerProvider *provider, GdaConnect
 	gda_pstmt_set_gda_statement (_GDA_PSTMT (ps), stmt);
         _GDA_PSTMT (ps)->param_ids = param_ids;
         _GDA_PSTMT (ps)->sql = sql;
-	
+
 	gda_connection_add_prepared_statement (cnc, stmt, (GdaPStmt *) ps);
 	g_object_unref (ps);
 
@@ -1730,7 +1730,7 @@ check_transaction_started (GdaConnection *cnc, gboolean *out_started)
 }
 
 /*
- * Creates a "simple" prepared statement which has no variable, but does not call 
+ * Creates a "simple" prepared statement which has no variable, but does not call
  * gda_connection_add_prepared_statement()
  */
 static GdaPostgresPStmt *
@@ -1859,7 +1859,7 @@ make_last_inserted_set (GdaConnection *cnc, GdaStatement *stmt, Oid last_id)
 			return NULL;
 		}
 		else if (nrows > 1) {
-			g_warning (_("SELECT statement to get last inserted row returned too many (%d) rows"), 
+			g_warning (_("SELECT statement to get last inserted row returned too many (%d) rows"),
 				   nrows);
 			return NULL;
 		}
@@ -1905,7 +1905,7 @@ make_last_inserted_set (GdaConnection *cnc, GdaStatement *stmt, Oid last_id)
  *  - all the *param_values
  *  - param_values
  *  - param_mem
- * 
+ *
  */
 static void
 params_freev (gchar **param_values, gboolean *param_mem, gint size)
@@ -1936,9 +1936,9 @@ params_freev (gchar **param_values, gboolean *param_mem, gint size)
 static GObject *
 gda_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnection *cnc,
 					 GdaStatement *stmt, GdaSet *params,
-					 GdaStatementModelUsage model_usage, 
-					 GType *col_types, GdaSet **last_inserted_row, 
-					 guint *task_id, 
+					 GdaStatementModelUsage model_usage,
+					 GType *col_types, GdaSet **last_inserted_row,
+					 guint *task_id,
 					 GdaServerProviderExecCallback async_cb, gpointer cb_data, GError **error)
 {
 	GdaPostgresPStmt *ps;
@@ -1968,11 +1968,11 @@ gda_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnect
 		*last_inserted_row = NULL;
 
 	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
-	if (!cdata) 
+	if (!cdata)
 		return NULL;
 
-	/* 
-	 * execute prepared statement using C API: CURSOR based 
+	/*
+	 * execute prepared statement using C API: CURSOR based
 	 */
 	if (!(model_usage & GDA_STATEMENT_MODEL_RANDOM_ACCESS) &&
 	    (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT)) {
@@ -2013,7 +2013,7 @@ gda_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnect
 			return NULL;
 		}
 		PQclear (pg_res);
-		
+
 		/* create data model in CURSOR mode */
 		recset = gda_postgres_recordset_new_cursor (cnc, ps, params, cursor_name, col_types);
 		gda_connection_internal_statement_executed (cnc, stmt, params, NULL); /* required: help @cnc keep some stats */
@@ -2054,7 +2054,7 @@ gda_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnect
 	gboolean *param_mem = NULL;
 	gint nb_params;
 	gboolean transaction_started = FALSE;
-	
+
 	nb_params = g_slist_length (_GDA_PSTMT (ps)->param_ids);
 	param_values = g_new0 (char *, nb_params);
 	param_lengths = g_new0 (int, nb_params);
@@ -2064,7 +2064,7 @@ gda_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnect
 	for (i = 0, list = _GDA_PSTMT (ps)->param_ids; list; list = list->next, i++) {
 		const gchar *pname = (gchar *) list->data;
 		GdaHolder *h = NULL;
-		
+
 		/* find requested parameter */
 		if (!params) {
 			event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
@@ -2128,7 +2128,7 @@ gda_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnect
 			/* specific BLOB treatment */
 			GdaBlob *blob = (GdaBlob*) gda_value_get_blob ((GValue *) value);
 			GdaPostgresBlobOp *op;
-			
+
 			/* Postgres requires that a transaction be started for LOB operations */
 			if (!check_transaction_started (cnc, &transaction_started)) {
 				event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
@@ -2137,10 +2137,10 @@ gda_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnect
 					     "%s", _("Cannot start transaction"));
 				break;
 			}
-			
+
 			/* create GdaBlobOp */
 			op = (GdaPostgresBlobOp *) gda_postgres_blob_op_new (cnc);
-			
+
 			/* always create a new blob as there is no way to truncate an existing blob */
 			if (gda_postgres_blob_op_declare_blob (op) &&
 			    gda_blob_op_write ((GdaBlobOp*) op, blob, 0))
@@ -2157,12 +2157,12 @@ gda_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnect
 			param_formats [i] = 1; /* binary format */
 			param_mem [i] = TRUE; /* don't free later */
 		}
-		else if ((G_VALUE_TYPE (value) == G_TYPE_DATE) || 
+		else if ((G_VALUE_TYPE (value) == G_TYPE_DATE) ||
 			 (G_VALUE_TYPE (value) == GDA_TYPE_TIMESTAMP) ||
 			 (G_VALUE_TYPE (value) == GDA_TYPE_TIME)) {
 			GdaHandlerTime *timdh;
-			
-			timdh = GDA_HANDLER_TIME (gda_server_provider_get_data_handler_g_type (provider, cnc, 
+
+			timdh = GDA_HANDLER_TIME (gda_server_provider_get_data_handler_g_type (provider, cnc,
 											      G_VALUE_TYPE (value)));
 			g_assert (timdh);
 			param_values [i] = gda_handler_time_get_no_locale_str_from_value (timdh, value);
@@ -2177,7 +2177,7 @@ gda_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnect
 				param_values [i] = NULL;
 		}
 	}
-		
+
 	if (event) {
 		gda_connection_add_event (cnc, event);
 		params_freev (param_values, param_mem, nb_params);
@@ -2187,7 +2187,7 @@ gda_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnect
 			gda_connection_rollback_transaction (cnc, NULL, NULL);
 		return NULL;
 	}
-	
+
 	/* add a connection event for the execution */
 	event = gda_connection_event_new (GDA_CONNECTION_EVENT_COMMAND);
         gda_connection_event_set_description (event, _GDA_PSTMT (ps)->sql);
@@ -2225,7 +2225,7 @@ gda_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnect
 	g_free (param_lengths);
 	g_free (param_formats);
 
-	if (!pg_res) 
+	if (!pg_res)
 		event = _gda_postgres_make_error (cnc, cdata->pconn, NULL, error);
 	else {
 		int status;
@@ -2236,7 +2236,7 @@ gda_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnect
                         if (status == PGRES_COMMAND_OK) {
                                 gchar *str;
                                 GdaConnectionEvent *event;
-                                
+
                                 event = gda_connection_event_new (GDA_CONNECTION_EVENT_NOTICE);
                                 str = g_strdup (PQcmdStatus (pg_res));
                                 gda_connection_event_set_description (event, str);
@@ -2244,8 +2244,8 @@ gda_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnect
                                 gda_connection_add_event (cnc, event);
                                 retval = (GObject *) gda_set_new_inline (1, "IMPACTED_ROWS", G_TYPE_INT,
 									 atoi (PQcmdTuples (pg_res)));
-                           
-                                if ((PQoidValue (pg_res) != InvalidOid) && last_inserted_row) 
+
+                                if ((PQoidValue (pg_res) != InvalidOid) && last_inserted_row)
 					*last_inserted_row = make_last_inserted_set (cnc, stmt, PQoidValue (pg_res));
 
                                 PQclear (pg_res);
@@ -2260,12 +2260,12 @@ gda_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnect
                                 retval = (GObject *) gda_data_model_array_new (0);
                         }
                 }
-		else 
+		else
 			event = _gda_postgres_make_error (cnc, cdata->pconn, NULL, error);
 	}
 
 	gda_connection_internal_statement_executed (cnc, stmt, params, NULL); /* required: help @cnc keep some stats */
-	if (transaction_started) 
+	if (transaction_started)
 		gda_connection_commit_transaction (cnc, NULL, NULL);
 
 	return retval;
@@ -2275,14 +2275,14 @@ gda_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnect
  * starts a distributed transaction: put the XA transaction in the ACTIVE state
  */
 static gboolean
-gda_postgres_provider_xa_start (GdaServerProvider *provider, GdaConnection *cnc, 
+gda_postgres_provider_xa_start (GdaServerProvider *provider, GdaConnection *cnc,
 				const GdaXaTransactionId *xid, GError **error)
 {
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
 	g_return_val_if_fail (xid, FALSE);
 
-	return gda_postgres_provider_begin_transaction (provider, cnc, NULL, 
+	return gda_postgres_provider_begin_transaction (provider, cnc, NULL,
 							GDA_TRANSACTION_ISOLATION_READ_COMMITTED, error);
 }
 
@@ -2291,7 +2291,7 @@ gda_postgres_provider_xa_start (GdaServerProvider *provider, GdaConnection *cnc,
  * This state is required by some database providers before actually going to the PREPARED state
  */
 static gboolean
-gda_postgres_provider_xa_end (GdaServerProvider *provider, GdaConnection *cnc, 
+gda_postgres_provider_xa_end (GdaServerProvider *provider, GdaConnection *cnc,
 			      const GdaXaTransactionId *xid, GError **error)
 {
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
@@ -2306,7 +2306,7 @@ gda_postgres_provider_xa_end (GdaServerProvider *provider, GdaConnection *cnc,
  * prepares the distributed transaction: put the XA transaction in the PREPARED state
  */
 static gboolean
-gda_postgres_provider_xa_prepare (GdaServerProvider *provider, GdaConnection *cnc, 
+gda_postgres_provider_xa_prepare (GdaServerProvider *provider, GdaConnection *cnc,
 				  const GdaXaTransactionId *xid, GError **error)
 {
 	GdaSet *params;
@@ -2328,7 +2328,7 @@ gda_postgres_provider_xa_prepare (GdaServerProvider *provider, GdaConnection *cn
 		return FALSE;
 	}
 	g_free (str);
-	affected = gda_connection_statement_execute_non_select (cnc, internal_stmt [I_STMT_XA_PREPARE], params, 
+	affected = gda_connection_statement_execute_non_select (cnc, internal_stmt [I_STMT_XA_PREPARE], params,
 								NULL, error);
 	g_object_unref (params);
 	if (affected == -1)
@@ -2342,7 +2342,7 @@ gda_postgres_provider_xa_prepare (GdaServerProvider *provider, GdaConnection *cn
  * terminates the XA transaction
  */
 static gboolean
-gda_postgres_provider_xa_commit (GdaServerProvider *provider, GdaConnection *cnc, 
+gda_postgres_provider_xa_commit (GdaServerProvider *provider, GdaConnection *cnc,
 				 const GdaXaTransactionId *xid, GError **error)
 {
 	GdaSet *params;
@@ -2364,7 +2364,7 @@ gda_postgres_provider_xa_commit (GdaServerProvider *provider, GdaConnection *cnc
 		return FALSE;
 	}
 	g_free (str);
-	affected = gda_connection_statement_execute_non_select (cnc, internal_stmt [I_STMT_XA_COMMIT], params, 
+	affected = gda_connection_statement_execute_non_select (cnc, internal_stmt [I_STMT_XA_COMMIT], params,
 								NULL, error);
 	g_object_unref (params);
 	if (affected == -1)
@@ -2377,7 +2377,7 @@ gda_postgres_provider_xa_commit (GdaServerProvider *provider, GdaConnection *cnc
  * Rolls back an XA transaction, possible only if in the ACTIVE, IDLE or PREPARED state
  */
 static gboolean
-gda_postgres_provider_xa_rollback (GdaServerProvider *provider, GdaConnection *cnc, 
+gda_postgres_provider_xa_rollback (GdaServerProvider *provider, GdaConnection *cnc,
 				   const GdaXaTransactionId *xid, GError **error)
 {
 	GdaSet *params;
@@ -2399,7 +2399,7 @@ gda_postgres_provider_xa_rollback (GdaServerProvider *provider, GdaConnection *c
 		return FALSE;
 	}
 	g_free (str);
-	affected = gda_connection_statement_execute_non_select (cnc, internal_stmt [I_STMT_XA_ROLLBACK], params, 
+	affected = gda_connection_statement_execute_non_select (cnc, internal_stmt [I_STMT_XA_ROLLBACK], params,
 								NULL, error);
 	g_object_unref (params);
 	if (affected == -1)
@@ -2428,12 +2428,12 @@ gda_postgres_provider_xa_recover (GdaServerProvider *provider, GdaConnection *cn
 	else {
 		GList *list = NULL;
 		gint i, nrows;
-		
+
 		nrows = gda_data_model_get_n_rows (model);
 		for (i = 0; i < nrows; i++) {
 			const GValue *cvalue = gda_data_model_get_value_at (model, 0, i, NULL);
 			if (cvalue && !gda_value_is_null (cvalue))
-				list = g_list_prepend (list, 
+				list = g_list_prepend (list,
 						       gda_xa_transaction_string_to_id (g_value_get_string (cvalue)));
 		}
 		g_object_unref (model);
@@ -2492,10 +2492,78 @@ _sql_identifier_needs_quotes (const gchar *str)
 	return FALSE;
 }
 
+/* Returns: @str */
+static gchar *
+pg_remove_quotes (gchar *str)
+{
+        glong total;
+        gchar *ptr;
+        glong offset = 0;
+	char delim;
+	
+	if (!str)
+		return NULL;
+	delim = *str;
+	if ((delim != '\'') && (delim != '"'))
+		return str;
+
+
+        total = strlen (str);
+        if (str[total-1] == delim) {
+		/* string is correclty terminated */
+		g_memmove (str, str+1, total-2);
+		total -=2;
+	}
+	else {
+		/* string is _not_ correclty terminated */
+		g_memmove (str, str+1, total-1);
+		total -=1;
+	}
+        str[total] = 0;
+
+        ptr = (gchar *) str;
+        while (offset < total) {
+                /* we accept the "''" as a synonym of "\'" */
+                if (*ptr == delim) {
+                        if (*(ptr+1) == delim) {
+                                g_memmove (ptr+1, ptr+2, total - offset);
+                                offset += 2;
+                        }
+                        else {
+                                *str = 0;
+                                return str;
+                        }
+                }
+                if (*ptr == '\\') {
+                        if (*(ptr+1) == '\\') {
+                                g_memmove (ptr+1, ptr+2, total - offset);
+                                offset += 2;
+                        }
+                        else {
+                                if (*(ptr+1) == delim) {
+                                        *ptr = delim;
+                                        g_memmove (ptr+1, ptr+2, total - offset);
+                                        offset += 2;
+                                }
+                                else {
+                                        *str = 0;
+                                        return str;
+                                }
+                        }
+                }
+                else
+                        offset ++;
+
+                ptr++;
+        }
+
+        return str;
+}
+
 static gchar *
 gda_postgresql_identifier_quote (GdaServerProvider *provider, GdaConnection *cnc,
 				 const gchar *id,
-				 gboolean meta_store_convention, gboolean force_quotes)
+				 gboolean for_meta_store, gboolean force_quotes)
 {
         GdaSqlReservedKeywordsFunc kwfunc;
         PostgresConnectionData *cdata = NULL;
@@ -2507,15 +2575,65 @@ gda_postgresql_identifier_quote (GdaServerProvider *provider, GdaConnection *cnc
         }
 
         kwfunc = _gda_postgres_get_reserved_keyword_func (cdata);
-	if (*id == '"') {
-		/* there are already some quotes */
+
+	if (for_meta_store) {
+		gchar *tmp, *ptr;
+		tmp = pg_remove_quotes (g_strdup (id));
+		if (kwfunc (tmp)) {
+			ptr = gda_sql_identifier_add_quotes (tmp);
+			g_free (tmp);
+			return ptr;
+		}
+		else if (force_quotes) {
+			/* quote if non LC characters or digits at the 1st char or non allowed characters */
+			for (ptr = tmp; *ptr; ptr++) {
+				if (((*ptr >= 'a') && (*ptr <= 'z')) ||
+				    ((*ptr >= '0') && (*ptr <= '9') && (ptr != tmp)) ||
+				    (*ptr == '_'))
+					continue;
+				else {
+					ptr = gda_sql_identifier_add_quotes (tmp);
+					g_free (tmp);
+					return ptr;
+				}
+			}
+			return tmp;
+		}
+		else {
+			for (ptr = tmp; *ptr; ptr++) {
+				if (*id == '"') {
+					if (((*ptr >= 'a') && (*ptr <= 'z')) ||
+					    ((*ptr >= '0') && (*ptr <= '9') && (ptr != tmp)) ||
+					    (*ptr == '_'))
+						continue;
+					else {
+						ptr = gda_sql_identifier_add_quotes (tmp);
+						g_free (tmp);
+						return ptr;
+					}
+				}
+				else if ((*ptr >= 'A') && (*ptr <= 'Z'))
+					*ptr += 'a' - 'A';
+				else if ((*ptr >= '0') && (*ptr <= '9') && (ptr == tmp)) {
+					ptr = gda_sql_identifier_add_quotes (tmp);
+					g_free (tmp);
+					return ptr;
+				}
+			}
+			return tmp;
+		}
+	}
+	else {
+		if (*id == '"') {
+			/* there are already some quotes */
+			return g_strdup (id);
+		}
+		if (kwfunc (id) || _sql_identifier_needs_quotes (id) || force_quotes)
+			return identifier_add_quotes (id);
+
+		/* nothing to do */
 		return g_strdup (id);
 	}
-	if (kwfunc (id) || _sql_identifier_needs_quotes (id) || force_quotes)
-		return identifier_add_quotes (id);
-	
-	/* nothing to do */
-	return g_strdup (id);
 }
 
 
@@ -2528,7 +2646,7 @@ gda_postgres_free_cnc_data (PostgresConnectionData *cdata)
 	if (!cdata)
 		return;
 
-	if (cdata->pconn) 
+	if (cdata->pconn)
                 PQfinish (cdata->pconn);
 
 	if (cdata->type_data) {
@@ -2540,10 +2658,10 @@ gda_postgres_free_cnc_data (PostgresConnectionData *cdata)
 		}
 		g_free (cdata->type_data);
 	}
-	
+
 	if (cdata->h_table)
 		g_hash_table_destroy (cdata->h_table);
-	
+
 	g_free (cdata->version);
 	g_free (cdata->short_version);
 	g_free (cdata->avoid_types_oids);
diff --git a/tests/test-identifiers-quotes.c b/tests/test-identifiers-quotes.c
index 3e5078b..79f6792 100644
--- a/tests/test-identifiers-quotes.c
+++ b/tests/test-identifiers-quotes.c
@@ -23,123 +23,129 @@
 #include <libgda/libgda.h>
 
 typedef struct {
-	gchar *sql_identifier;
 	gchar *provider;
-	gboolean meta_store_convention;
-	gboolean force_quotes;
-	gchar *expected_result;
+	gchar *sql_identifier;
+	gchar *result1; /* for_meta_store=>FALSE  force_quotes=>FALSE */
+	gchar *result2; /* for_meta_store=>TRUE  force_quotes=>FALSE */
+	gchar *result3; /* for_meta_store=>FALSE  force_quotes=>TRUE */
+	gchar *result4; /* for_meta_store=>TRUE  force_quotes=>TRUE */
 } ATest;
 
 ATest tests[] = {
-	/* generic SQL */
-	{"\"Solution\"", NULL, FALSE, FALSE, "\"Solution\""},
-	{"\"Solution\"", NULL, FALSE, TRUE, "\"Solution\""},
-	{"\"solution\"", NULL, FALSE, FALSE, "\"solution\""},
-	{"\"solution\"", NULL, FALSE, TRUE, "\"solution\""},
-	{"solution", NULL, FALSE, FALSE, "solution"},
-	{"solution", NULL, FALSE, TRUE, "\"solution\""},
-	{"solution", NULL, TRUE, FALSE, "solution"},
-	{"solution", NULL, TRUE, TRUE, "\"solution\""},
-
-	{"SOLUTION", NULL, FALSE, TRUE, "\"SOLUTION\""},
-	{"SOLUTION", NULL, FALSE, FALSE, "SOLUTION"},
-
-	{"Solution", NULL, FALSE, FALSE, "Solution"}, /* mixed case */
-	{"Solution", NULL, FALSE, TRUE, "\"Solution\""},
-
-	{"5olution", NULL, FALSE, FALSE, "\"5olution\""}, /* test 1st digit */
-	{"5olution", NULL, FALSE, TRUE, "\"5olution\""},
-	{"5olution", NULL, TRUE, FALSE, "\"5olution\""}, /* this should not happen, but just in case */
-	{"5olution", NULL, TRUE, TRUE, "\"5olution\""}, /* this should not happen, but just in case */
-
-	{"$solu_t#ion", NULL, FALSE, FALSE, "$solu_t#ion"}, /* other allowed characters */
-	{"$solu_t#ion", NULL, FALSE, TRUE, "\"$solu_t#ion\""},
-	{"$solu_t#ion", NULL, TRUE, FALSE, "$solu_t#ion"},
-	{"$solu_t#ion", NULL, TRUE, TRUE, "\"$solu_t#ion\""},
-	
-	{"solu tion", NULL, FALSE, FALSE, "\"solu tion\""}, /* non allowed characters */
-	{"solu tion", NULL, FALSE, TRUE, "\"solu tion\""},
-	{"solu tion", NULL, TRUE, FALSE, "\"solu tion\""},
-	{"solu tion", NULL, TRUE, TRUE, "\"solu tion\""},
-
-	{"select", NULL, FALSE, FALSE, "\"select\""}, /* SQL reserved keyword */
-	{"select", NULL, FALSE, TRUE, "\"select\""},
-	{"select", NULL, TRUE, FALSE, "\"select\""},
-	{"select", NULL, TRUE, TRUE, "\"select\""},
-
-	/* MySQL specific */
-	{"`Solution`", "MySQL", FALSE, FALSE, "`Solution`"},
-	{"`Solution`", "MySQL", FALSE, TRUE, "`Solution`"},
-	{"`solution`", "MySQL", FALSE, FALSE, "`solution`"},
-	{"`solution`", "MySQL", FALSE, TRUE, "`solution`"},
-	{"solution", "MySQL", FALSE, FALSE, "solution"},
-	{"solution", "MySQL", FALSE, TRUE, "`solution`"},
-	{"solution", "MySQL", TRUE, FALSE, "solution"},
-	{"solution", "MySQL", TRUE, TRUE, "`solution`"},
-
-	{"SOLUTION", "MySQL", FALSE, TRUE, "`SOLUTION`"},
-	{"SOLUTION", "MySQL", FALSE, FALSE, "SOLUTION"},
-
-	{"Solution", "MySQL", FALSE, FALSE, "Solution"}, /* mixed case */
-	{"Solution", "MySQL", FALSE, TRUE, "`Solution`"},
-
-	{"5olution", "MySQL", FALSE, FALSE, "`5olution`"}, /* test 1st digit */
-	{"5olution", "MySQL", FALSE, TRUE, "`5olution`"},
-	{"5olution", "MySQL", TRUE, FALSE, "`5olution`"}, /* this should not happen, but just in case */
-	{"5olution", "MySQL", TRUE, TRUE, "`5olution`"}, /* this should not happen, but just in case */
-
-	{"$solu_t#ion", "MySQL", FALSE, FALSE, "$solu_t#ion"}, /* other allowed characters */
-	{"$solu_t#ion", "MySQL", FALSE, TRUE, "`$solu_t#ion`"},
-	{"$solu_t#ion", "MySQL", TRUE, FALSE, "$solu_t#ion"},
-	{"$solu_t#ion", "MySQL", TRUE, TRUE, "`$solu_t#ion`"},
-	
-	{"solu tion", "MySQL", FALSE, FALSE, "`solu tion`"}, /* non allowed characters */
-	{"solu tion", "MySQL", FALSE, TRUE, "`solu tion`"},
-	{"solu tion", "MySQL", TRUE, FALSE, "`solu tion`"},
-	{"solu tion", "MySQL", TRUE, TRUE, "`solu tion`"},
-
-	{"select", "MySQL", FALSE, FALSE, "`select`"}, /* SQL reserved keyword */
-	{"select", "MySQL", FALSE, TRUE, "`select`"},
-	{"select", "MySQL", TRUE, FALSE, "`select`"},
-	{"select", "MySQL", TRUE, TRUE, "`select`"},
-
-	/* PostgreSQL specific */
-	{"\"Solution\"", "PostgreSQL", FALSE, FALSE, "\"Solution\""},
-	{"\"Solution\"", "PostgreSQL", FALSE, TRUE, "\"Solution\""},
-	{"\"solution\"", "PostgreSQL", FALSE, FALSE, "\"solution\""},
-	{"\"solution\"", "PostgreSQL", FALSE, TRUE, "\"solution\""},
-	{"solution", "PostgreSQL", FALSE, FALSE, "solution"},
-	{"solution", "PostgreSQL", FALSE, TRUE, "\"solution\""},
-	{"solution", "PostgreSQL", TRUE, FALSE, "solution"},
-	{"solution", "PostgreSQL", TRUE, TRUE, "\"solution\""},
-
-	{"SOLUTION", "PostgreSQL", FALSE, TRUE, "\"SOLUTION\""},
-	{"SOLUTION", "PostgreSQL", FALSE, FALSE, "SOLUTION"},
-
-	{"Solution", "PostgreSQL", FALSE, FALSE, "Solution"}, /* mixed case */
-	{"Solution", "PostgreSQL", FALSE, TRUE, "\"Solution\""},
-
-	{"5olution", "PostgreSQL", FALSE, FALSE, "\"5olution\""}, /* test 1st digit */
-	{"5olution", "PostgreSQL", FALSE, TRUE, "\"5olution\""},
-	{"5olution", "PostgreSQL", TRUE, FALSE, "\"5olution\""}, /* this should not happen, but just in case */
-	{"5olution", "PostgreSQL", TRUE, TRUE, "\"5olution\""}, /* this should not happen, but just in case */
-
-	{"$solu_t#ion", "PostgreSQL", FALSE, FALSE, "$solu_t#ion"}, /* other allowed characters */
-	{"$solu_t#ion", "PostgreSQL", FALSE, TRUE, "\"$solu_t#ion\""},
-	{"$solu_t#ion", "PostgreSQL", TRUE, FALSE, "$solu_t#ion"},
-	{"$solu_t#ion", "PostgreSQL", TRUE, TRUE, "\"$solu_t#ion\""},
-	
-	{"solu tion", "PostgreSQL", FALSE, FALSE, "\"solu tion\""}, /* non allowed characters */
-	{"solu tion", "PostgreSQL", FALSE, TRUE, "\"solu tion\""},
-	{"solu tion", "PostgreSQL", TRUE, FALSE, "\"solu tion\""},
-	{"solu tion", "PostgreSQL", TRUE, TRUE, "\"solu tion\""},
-
-	{"select", "PostgreSQL", FALSE, FALSE, "\"select\""}, /* SQL reserved keyword */
-	{"select", "PostgreSQL", FALSE, TRUE, "\"select\""},
-	{"select", "PostgreSQL", TRUE, FALSE, "\"select\""},
-	{"select", "PostgreSQL", TRUE, TRUE, "\"select\""},
+	/* generic SQL rules */
+	{NULL, "\"double word\"", "\"double word\"", "\"double word\"", "\"double word\"", "\"double word\""},
+	{NULL, "\"CapitalTest\"", "\"CapitalTest\"", "\"CapitalTest\"", "\"CapitalTest\"", "\"CapitalTest\""},
+	{NULL, "CapitalTest", "CapitalTest", "capitaltest", "\"CapitalTest\"", "\"CapitalTest\""},
+	{NULL, "\"mytable\"", "\"mytable\"", "mytable", "\"mytable\"", "mytable"},
+	{NULL, "mytable", "mytable", "mytable", "\"mytable\"", "mytable"},
+	{NULL, "MYTABLE", "MYTABLE", "mytable", "\"MYTABLE\"", "\"MYTABLE\""},
+	{NULL, "\"MYTABLE\"", "\"MYTABLE\"", "\"MYTABLE\"", "\"MYTABLE\"", "\"MYTABLE\""},
+	{NULL, "desc", "\"desc\"", "\"desc\"", "\"desc\"", "\"desc\""},
+	{NULL, "\"desc\"", "\"desc\"", "\"desc\"", "\"desc\"", "\"desc\""},
+	{NULL, "5ive", "\"5ive\"", "\"5ive\"", "\"5ive\"", "\"5ive\""},
+	{NULL, "\"5ive\"", "\"5ive\"", "\"5ive\"", "\"5ive\"", "\"5ive\""},
+
+	{"PostgreSQL", "\"double word\"", "\"double word\"", "\"double word\"", "\"double word\"", "\"double word\""},
+	{"PostgreSQL", "\"CapitalTest\"", "\"CapitalTest\"", "\"CapitalTest\"", "\"CapitalTest\"", "\"CapitalTest\""},
+	{"PostgreSQL", "CapitalTest", "CapitalTest", "capitaltest", "\"CapitalTest\"", "\"CapitalTest\""},
+	{"PostgreSQL", "\"mytable\"", "\"mytable\"", "mytable", "\"mytable\"", "mytable"},
+	{"PostgreSQL", "mytable", "mytable", "mytable", "\"mytable\"", "mytable"},
+	{"PostgreSQL", "MYTABLE", "MYTABLE", "mytable", "\"MYTABLE\"", "\"MYTABLE\""},
+	{"PostgreSQL", "\"MYTABLE\"", "\"MYTABLE\"", "\"MYTABLE\"", "\"MYTABLE\"", "\"MYTABLE\""},
+	{"PostgreSQL", "desc", "\"desc\"", "\"desc\"", "\"desc\"", "\"desc\""},
+	{"PostgreSQL", "5ive", "\"5ive\"", "\"5ive\"", "\"5ive\"", "\"5ive\""},
+	{"PostgreSQL", "\"5ive\"", "\"5ive\"", "\"5ive\"", "\"5ive\"", "\"5ive\""},
+
+	/* case insensitive MySQL */
+	{"iMySQL", "\"double word\"", "`double word`", "\"double word\"", "`double word`", "\"double word\""},
+	{"iMySQL", "\"CapitalTest\"", "`CapitalTest`", "capitaltest", "`CapitalTest`", "capitaltest"},
+	{"iMySQL", "`CapitalTest`", "`CapitalTest`", "capitaltest", "`CapitalTest`", "capitaltest"},
+	{"iMySQL", "CapitalTest", "CapitalTest", "capitaltest", "`CapitalTest`", "capitaltest"},
+	{"iMySQL", "\"mytable\"", "`mytable`", "mytable", "`mytable`", "mytable"},
+	{"iMySQL", "`mytable`", "`mytable`", "mytable", "`mytable`", "mytable"},
+	{"iMySQL", "mytable", "mytable", "mytable", "`mytable`", "mytable"},
+	{"iMySQL", "MYTABLE", "MYTABLE", "mytable", "`MYTABLE`", "mytable"},
+	{"iMySQL", "\"MYTABLE\"", "`MYTABLE`", "mytable", "`MYTABLE`", "mytable"},
+	{"iMySQL", "`MYTABLE`", "`MYTABLE`", "mytable", "`MYTABLE`", "mytable"},
+	{"iMySQL", "desc", "`desc`", "\"desc\"", "`desc`", "\"desc\""},
+	{"iMySQL", "`desc`", "`desc`", "\"desc\"", "`desc`", "\"desc\""},
+	{"iMySQL", "5ive", "`5ive`", "\"5ive\"", "`5ive`", "\"5ive\""},
+	{"iMySQL", "\"5ive\"", "`5ive`", "\"5ive\"", "`5ive`", "\"5ive\""},
+	{"iMySQL", "`5ive`", "`5ive`", "\"5ive\"", "`5ive`", "\"5ive\""},
+
+	/* case sensitive MySQL */
+	{"sMySQL", "\"double word\"", "`double word`", "\"double word\"", "`double word`", "\"double word\""},
+	{"sMySQL", "\"CapitalTest\"", "`CapitalTest`", "\"CapitalTest\"", "`CapitalTest`", "\"CapitalTest\""},
+	{"sMySQL", "`CapitalTest`", "`CapitalTest`", "\"CapitalTest\"", "`CapitalTest`", "\"CapitalTest\""},
+	{"sMySQL", "CapitalTest", "`CapitalTest`", "\"CapitalTest\"", "`CapitalTest`", "\"CapitalTest\""},
+	{"sMySQL", "\"mytable\"", "`mytable`", "mytable", "`mytable`", "mytable"},
+	{"sMySQL", "`mytable`", "`mytable`", "mytable", "`mytable`", "mytable"},
+	{"sMySQL", "mytable", "`mytable`", "mytable", "`mytable`", "mytable"},
+	{"sMySQL", "MYTABLE", "`MYTABLE`", "\"MYTABLE\"", "`MYTABLE`", "\"MYTABLE\""},
+	{"sMySQL", "\"MYTABLE\"", "`MYTABLE`", "\"MYTABLE\"", "`MYTABLE`", "\"MYTABLE\""},
+	{"sMySQL", "`MYTABLE`", "`MYTABLE`", "\"MYTABLE\"", "`MYTABLE`", "\"MYTABLE\""},
+	{"sMySQL", "desc", "`desc`", "\"desc\"", "`desc`", "\"desc\""},
+	{"sMySQL", "`desc`", "`desc`", "\"desc\"", "`desc`", "\"desc\""},
+	{"sMySQL", "5ive", "`5ive`", "\"5ive\"", "`5ive`", "\"5ive\""},
+	{"sMySQL", "\"5ive\"", "`5ive`", "\"5ive\"", "`5ive`", "\"5ive\""},
+	{"sMySQL", "`5ive`", "`5ive`", "\"5ive\"", "`5ive`", "\"5ive\""},
+
+	{"SQLite", "\"double word\"", "\"double word\"", "\"double word\"", "\"double word\"", "\"double word\""},
+	{"SQLite", "[double word]", "\"double word\"", "\"double word\"", "\"double word\"", "\"double word\""},
+	{"SQLite", "\"CapitalTest\"", "\"CapitalTest\"", "capitaltest", "\"CapitalTest\"", "capitaltest"},
+	{"SQLite", "`CapitalTest`", "\"CapitalTest\"", "capitaltest", "\"CapitalTest\"", "capitaltest"},
+	{"SQLite", "[CapitalTest]", "\"CapitalTest\"", "capitaltest", "\"CapitalTest\"", "capitaltest"},
+	{"SQLite", "CapitalTest", "CapitalTest", "capitaltest", "\"CapitalTest\"", "capitaltest"},
+	{"SQLite", "\"mytable\"", "\"mytable\"", "mytable", "\"mytable\"", "mytable"},
+	{"SQLite", "[mytable]", "\"mytable\"", "mytable", "\"mytable\"", "mytable"},
+	{"SQLite", "`mytable`", "\"mytable\"", "mytable", "\"mytable\"", "mytable"},
+	{"SQLite", "mytable", "mytable", "mytable", "\"mytable\"", "mytable"},
+	{"SQLite", "MYTABLE", "MYTABLE", "mytable", "\"MYTABLE\"", "mytable"},
+	{"SQLite", "\"MYTABLE\"", "\"MYTABLE\"", "mytable", "\"MYTABLE\"", "mytable"},
+	{"SQLite", "[MYTABLE]", "\"MYTABLE\"", "mytable", "\"MYTABLE\"", "mytable"},
+	{"SQLite", "`MYTABLE`", "\"MYTABLE\"", "mytable", "\"MYTABLE\"", "mytable"},
+	{"SQLite", "desc", "\"desc\"", "\"desc\"", "\"desc\"", "\"desc\""},
+	{"SQLite", "[desc]", "\"desc\"", "\"desc\"", "\"desc\"", "\"desc\""},
+	{"SQLite", "`desc`", "\"desc\"", "\"desc\"", "\"desc\"", "\"desc\""},
+	{"SQLite", "5ive", "\"5ive\"", "\"5ive\"", "\"5ive\"", "\"5ive\""},
+	{"SQLite", "\"5ive\"", "\"5ive\"", "\"5ive\"", "\"5ive\"", "\"5ive\""},
+	{"SQLite", "[5ive]", "\"5ive\"", "\"5ive\"", "\"5ive\"", "\"5ive\""},
+	{"SQLite", "`5ive`", "\"5ive\"", "\"5ive\"", "\"5ive\"", "\"5ive\""},
+
 };
 
+static gboolean
+check_result (ATest *test, const gchar *result, const gchar *expected, gboolean for_meta_store, gboolean force_quotes)
+{
+	gchar *str = NULL;
+	if (!result) {
+		if (expected)
+			str = g_strdup_printf ("Failed for %s: result is NULL when %s was expected\n",
+					       test->sql_identifier,
+					       expected);
+	}
+	else {
+		if (!expected)
+			str = g_strdup_printf ("Failed for %s: result is %s when NULL was expected\n",
+					       test->sql_identifier,
+					       result);
+		else if (strcmp (result, expected))
+			str = g_strdup_printf ("Failed for %s: result is %s when %s was expected\n",
+					       test->sql_identifier,
+					       result, expected);
+	}
+	
+	if (str) {
+		g_print ("\tTEST failed, (provider %s%s%s): %s\n", test->provider,
+			 for_meta_store ? " for Meta store" : "",
+			 force_quotes ? " force quotes" : "", str);
+		g_free (str);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+
 int
 main (int argc, char** argv)
 {
@@ -149,49 +155,43 @@ main (int argc, char** argv)
 		ATest *test = &(tests [i]);
 		gchar *result;
 		GdaServerProvider *prov = NULL;
-		gboolean fail = FALSE;
+
 		if (test->provider) {
-			prov = gda_config_get_provider (test->provider, NULL);
+			if ((*test->provider == 'i') || (*test->provider == 's'))
+				prov = gda_config_get_provider (test->provider + 1, NULL);
+			else
+				prov = gda_config_get_provider (test->provider, NULL);
 			if (!prov) {
 				g_print ("Can't find provider for %s, ignoring test\n", test->provider);
 				continue;
 			}
+			if (*test->provider == 'i')
+				g_object_set (G_OBJECT (prov), "identifiers-case-sensitive", FALSE, NULL);
+			else if (*test->provider == 's')
+				g_object_set (G_OBJECT (prov), "identifiers-case-sensitive", TRUE, NULL);
 		}
-		result = gda_sql_identifier_quote (test->sql_identifier, NULL, prov, test->meta_store_convention,
-						   test->force_quotes);
-		if (!result) {
-			if (test->expected_result) {
-				g_print ("Failed for %s: result is NULL when %s was expected\n",
-					 test->sql_identifier,
-					 test->expected_result);
-				fail = TRUE;
-			}
-		}
-		else {
-			if (!test->expected_result) {
-				g_print ("Failed for %s: result is %s when NULL was expected\n",
-					 test->sql_identifier,
-					 result);
-				fail = TRUE;
-			}
-			else if (strcmp (result, test->expected_result)) {
-				g_print ("Failed for %s: result is %s when %s was expected\n",
-					 test->sql_identifier,
-					 result, test->expected_result);
-				fail = TRUE;
-			}
-			g_free (result);
-		}
+		result = gda_sql_identifier_quote (test->sql_identifier, NULL, prov, FALSE, FALSE);
+		if (!check_result (test, result, test->result1, FALSE, FALSE))
+			nfailed++;
+		g_free (result);
 
-		if (fail) {
-			g_print ("\tprovider: %s\n", test->provider);
-			g_print ("\tmeta store convention: %s\n", test->meta_store_convention ? "Yes" : "No");
-			g_print ("\tforce quotes: %s\n", test->force_quotes ? "Yes" : "No");
+		result = gda_sql_identifier_quote (test->sql_identifier, NULL, prov, TRUE, FALSE);
+		if (!check_result (test, result, test->result2, TRUE, FALSE))
 			nfailed++;
-		}
+		g_free (result);
+
+		result = gda_sql_identifier_quote (test->sql_identifier, NULL, prov, FALSE, TRUE);
+		if (!check_result (test, result, test->result3, FALSE, TRUE))
+			nfailed++;
+		g_free (result);
+
+		result = gda_sql_identifier_quote (test->sql_identifier, NULL, prov, TRUE, TRUE);
+		if (!check_result (test, result, test->result4, TRUE, TRUE))
+			nfailed++;
+		g_free (result);
 	}
 
-	g_print ("%d tests executed, ", i);
+	g_print ("%d tests executed, ", i * 4);
 	if (nfailed > 0)
 		g_print ("%d failed\n", nfailed);
 	else



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