libgda r3216 - in trunk: . doc/C/tmpl libgda libgda-xslt libgda/sqlite providers/postgres testing tests/data-models tests/providers tools



Author: vivien
Date: Thu Sep 25 18:55:33 2008
New Revision: 3216
URL: http://svn.gnome.org/viewvc/libgda?rev=3216&view=rev

Log:
2008-09-25  Vivien Malerba <malerba gnome-db org>

	* libgda/gda-server-provider-extra.[ch]: new gda_select_alter_select_for_empty()
	functions
	* tools/gda-sql.c:
	* tools/information-schema-doc.c:
	* testing/gda-provider-status.c:
	* libgda-xslt/sql_backend.c:
	* libgda/gda-util.c:
	* libgda/gda-server-provider.c: use xmlNewTextChild instead of xmlNewChild when necessary
	* libgda/gda-statement.h: new GDA_STATEMENT_MODEL_ALLOW_NOPARAM flag
	* libgda/sqlite/gda-sqlite-provider.c:
	* libgda/sqlite/gda-sqlite-recordset.[ch]:
	* providers/postgres/gda-postgres-provider.c:
	* libgda/gda-data-select.c: allow SELECT statement execution with invalid parameters
	to return an empty data model (if GDA_STATEMENT_MODEL_ALLOW_NOPARAM is set), in which
	case changing a parameter will re-run the SELECT, for bug #552708
	* libgda/gda-server-operation.c: bug fixed
	* libgda/gda-data-model-iter.c: bug fixed in GdaHolder's properties
	* tests/: tests updates
	* libgda/gda-connection.c:
	* doc/C: doc. updates


Added:
   trunk/tests/data-models/check_empty_rs.c
Modified:
   trunk/ChangeLog
   trunk/doc/C/tmpl/gda-statement.sgml
   trunk/libgda-xslt/sql_backend.c
   trunk/libgda/gda-connection.c
   trunk/libgda/gda-data-model-iter.c
   trunk/libgda/gda-data-select.c
   trunk/libgda/gda-server-operation.c
   trunk/libgda/gda-server-provider-extra.c
   trunk/libgda/gda-server-provider-extra.h
   trunk/libgda/gda-server-provider.c
   trunk/libgda/gda-statement.h
   trunk/libgda/gda-util.c
   trunk/libgda/sqlite/gda-sqlite-provider.c
   trunk/libgda/sqlite/gda-sqlite-recordset.c
   trunk/libgda/sqlite/gda-sqlite-recordset.h
   trunk/providers/postgres/gda-postgres-provider.c
   trunk/testing/gda-provider-status.c
   trunk/tests/data-models/   (props changed)
   trunk/tests/data-models/Makefile.am
   trunk/tests/data-models/check_pmodel.c
   trunk/tests/providers/TYPES_SCHEMA_PostgreSQL.xml
   trunk/tests/providers/TYPES_SCHEMA_SQLite.xml
   trunk/tests/providers/check_postgres.c
   trunk/tests/providers/check_sqlite.c
   trunk/tests/providers/prov-test-common.c
   trunk/tests/providers/prov-test-common.h
   trunk/tools/gda-sql.c
   trunk/tools/information-schema-doc.c

Modified: trunk/doc/C/tmpl/gda-statement.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-statement.sgml	(original)
+++ trunk/doc/C/tmpl/gda-statement.sgml	Thu Sep 25 18:55:33 2008
@@ -82,7 +82,8 @@
 @GDA_STATEMENT_MODEL_RANDOM_ACCESS: access to the data model will be random (usually this will result in a data model completely stored in memory)
 @GDA_STATEMENT_MODEL_CURSOR_FORWARD: access to the data model will be done using a cursor moving forward
 @GDA_STATEMENT_MODEL_CURSOR_BACKWARD: access to the data model will be done using a cursor moving backward
- GDA_STATEMENT_MODEL_CURSOR: 
+ GDA_STATEMENT_MODEL_CURSOR: access to the data model will be done using a cursor (moving both forward and backward)
+ GDA_STATEMENT_MODEL_ALLOW_NOPARAM: specifies that the data model should be executed even if some parameters required to execute it are invalid (in this case the data model will have no row, and will automatically be re-run when the missing parameters are once again valid)
 
 <!-- ##### ENUM GdaStatementError ##### -->
 <para>

Modified: trunk/libgda-xslt/sql_backend.c
==============================================================================
--- trunk/libgda-xslt/sql_backend.c	(original)
+++ trunk/libgda-xslt/sql_backend.c	Thu Sep 25 18:55:33 2008
@@ -423,8 +423,7 @@
 		xmlNodePtr row, field;
 		gint r, c;
 		for (r = 0; r < nrows; r++) {
-			row = xmlNewChild (mainnode, NULL,
-					   (xmlChar *) "row", NULL);
+			row = xmlNewChild (mainnode, NULL, (xmlChar *) "row", NULL);
 			for (c = 0; c < rnb_cols; c++) {
 				GValue *value;
 				xmlChar *str = NULL;
@@ -441,7 +440,7 @@
 					isnull = TRUE;
 				else 
 					str = value_to_xmlchar (value);
-				field = xmlNewChild (row, NULL, (xmlChar *) "column", (xmlChar *) str);
+				field = xmlNewTextChild (row, NULL, (xmlChar *) "column", (xmlChar *) str);
 				xmlSetProp (field, (xmlChar *) "name", (xmlChar *) col_ids[c]);
 				if (isnull)
 					xmlSetProp (field, (xmlChar *) "isnull", (xmlChar *) "true");

Modified: trunk/libgda/gda-connection.c
==============================================================================
--- trunk/libgda/gda-connection.c	(original)
+++ trunk/libgda/gda-connection.c	Thu Sep 25 18:55:33 2008
@@ -1505,6 +1505,7 @@
  * @cnc: a #GdaConnection
  * @stmt: a #GdaStatement object
  * @params: a #GdaSet object (which can be obtained using gda_statement_get_parameters()), or %NULL
+ * @model_usage: in the case where @stmt is a SELECT statement, specifies how the returned data model will be used
  * @last_insert_row: a place to store a new #GdaSet object which contains the values of the last inserted row, or %NULL
  * @error: a place to store errors, or %NULL
  *
@@ -1534,8 +1535,22 @@
  *   <listitem><para>one with the '+0' ID which may for example contain 1 (note that its "name" property should be "id")</para></listitem>
  *   <listitem><para>one with the '+1' ID which will contain 'joe' (note that its "name" property should be "name")</para></listitem>
  * </itemizedlist>
- * See the <link linkend="limitations">provider's limitations</link> section for more details about this feature
- * depending on which database is accessed.
+ *
+ * Note1: If @stmt is a SELECT statement which has some parameters and  if @params is %NULL, then the statement can't
+ * be executed and this method will return %NULL.
+ *
+ * Note2: If @stmt is a SELECT statement which has some parameters and  if @params is not %NULL but contains some
+ * invalid parameters, then the statement can't be executed and this method will return %NULL, unless the
+ * @model_usage has the GDA_STATEMENT_MODEL_ALLOW_NOPARAM flag.
+ *
+ * Note3: If @stmt is a SELECT statement which has some parameters and  if @params is not %NULL but contains some
+ * invalid parameters and if @model_usage has the GDA_STATEMENT_MODEL_ALLOW_NOPARAM flag, then the returned
+ * data model will contain no row but will have all the correct columns (even though some of the columns might
+ * report as GDA_TYPE_NULL). In this case, if (after this method call) any of @params' parameters change
+ * then the resulting data model will re-run itself, see the GdaDataSelect's 
+ * <link linkend="GdaDataSelect--auto-reset">auto-reset</link> property for more information.
+ *
+ * Also see the <link linkend="limitations">provider's limitations</link> section.
  *
  * Returns: a #GObject, or %NULL if an error occurred 
  */

Modified: trunk/libgda/gda-data-model-iter.c
==============================================================================
--- trunk/libgda/gda-data-model-iter.c	(original)
+++ trunk/libgda/gda-data-model-iter.c	Thu Sep 25 18:55:33 2008
@@ -390,7 +390,7 @@
 	GSList *fields, *holders;
 	for (fields = select->expr_list, holders = GDA_SET (iter)->holders; 
 	     fields && holders; 
-	     fields = fields->next, holders = holders->next) {
+	     fields = fields->next) {
 		GdaSqlSelectField *selfield = (GdaSqlSelectField*) fields->data;
 		if (selfield->validity_meta_table_column) {
 			GdaMetaTableColumn *tcol = selfield->validity_meta_table_column;
@@ -403,8 +403,10 @@
 				gda_holder_set_default_value (GDA_HOLDER (holders->data), dvalue);
 				gda_value_free (dvalue);
 			}
+			holders = holders->next;
 		}
-		else if (selfield->validity_meta_object && (selfield->validity_meta_object->obj_type == GDA_META_DB_TABLE) &&
+		else if (selfield->validity_meta_object && 
+			 (selfield->validity_meta_object->obj_type == GDA_META_DB_TABLE) &&
 			 selfield->expr && selfield->expr->value && !selfield->expr->param_spec && 
 			 (G_VALUE_TYPE (selfield->expr->value) == G_TYPE_STRING) &&
 			 !strcmp (g_value_get_string (selfield->expr->value), "*")) {
@@ -421,10 +423,12 @@
 					gda_holder_set_default_value (GDA_HOLDER (holders->data), dvalue);
 					gda_value_free (dvalue);
 				}
-				if (tmplist != mtable->columns)
+				if (tmplist)
 					holders = holders->next;
 			}
 		}
+		else
+			holders = holders->next;
 	}
 	if (fields || holders)
 		g_warning ("Internal error: GdaDataModelIter has %d GdaHolders, and SELECT statement has %d expressions",

Modified: trunk/libgda/gda-data-select.c
==============================================================================
--- trunk/libgda/gda-data-select.c	(original)
+++ trunk/libgda/gda-data-select.c	Thu Sep 25 18:55:33 2008
@@ -360,7 +360,7 @@
 		}
 		new_model = (GdaDataSelect*) gda_connection_statement_execute_select_full (model->priv->cnc, select, 
 											   model->priv->ext_params, 
-											   model->priv->usage_flags,
+											   model->priv->usage_flags | GDA_STATEMENT_MODEL_ALLOW_NOPARAM,
 											   types, 
 											   &error);
 		g_free (types);
@@ -369,6 +369,7 @@
 				   error && error->message ? error->message : _("No detail"));
 			if (error)
 				g_error_free (error);
+			/* FIXME: clear all the rows in @model, and emit the "reset" signal */
 			return;
 		}
 
@@ -399,6 +400,20 @@
 		old_model->priv->sel_stmt = model->priv->sel_stmt;
 		model->priv->sel_stmt = (GdaStatement*) copy;
 
+		/* keep the same GdaColumn pointers */
+		GSList *l1, *l2;
+		l1 = old_model->priv->columns;
+		old_model->priv->columns = model->priv->columns;
+		model->priv->columns = l1;
+		for (l1 = model->priv->columns, l2 = old_model->priv->columns;
+		     l1 && l2;
+		     l1 = l1->next, l2 = l2->next) {
+			if ((gda_column_get_g_type ((GdaColumn*) l1->data) == GDA_TYPE_NULL) &&
+			    (gda_column_get_g_type ((GdaColumn*) l2->data) != GDA_TYPE_NULL))
+				gda_column_set_g_type ((GdaColumn*) l1->data, 
+						       gda_column_get_g_type ((GdaColumn*) l2->data));
+		}
+
 		g_object_unref (old_model);
 
 		/* copy all the param's holders' values from model->priv->ext_params to 
@@ -408,14 +423,18 @@
 			GdaHolder *h;
 			h = gda_set_get_holder (model->priv->modif_internals->exec_set,
 						gda_holder_get_id (list->data));
-			if (h) 
-				if (! gda_holder_set_value (h, gda_holder_get_value (GDA_HOLDER (list->data)), &error)) {
+			if (h) {
+				if (!gda_holder_is_valid (GDA_HOLDER (list->data))) 
+					gda_holder_set_value (h, gda_holder_get_value (GDA_HOLDER (list->data)), NULL);
+				else if (! gda_holder_set_value (h, gda_holder_get_value (GDA_HOLDER (list->data)),
+								 &error)) {
 					g_warning (_("An error has occurred, the value returned by the \"exec-params\" "
 						     "property will be wrong: %s"),
 						   error && error->message ? error->message : _("No detail"));
 					if (error)
 						g_error_free (error);
 				}
+			}
 		}
 
 		/* signal a reset */
@@ -1942,9 +1961,30 @@
 	     plist;
 	     i++, plist = plist->next) {
 		const GValue *value;
+		GError *error = NULL;
 		value = gda_row_get_value (prow, i);
-		if (! gda_holder_set_value ((GdaHolder*) plist->data, value, NULL))
-			retval = FALSE;
+		if (! gda_holder_set_value ((GdaHolder*) plist->data, value, &error)) {
+			if (gda_holder_get_not_null ((GdaHolder*) plist->data) &&
+			    gda_value_is_null (value)) {
+				gda_holder_set_not_null ((GdaHolder*) plist->data, FALSE);
+				if (! gda_holder_set_value ((GdaHolder*) plist->data, value, NULL)) {
+					retval = FALSE;
+					g_warning (_("Could not change iter's value for column %d: %s"), i,
+						   error && error->message ? error->message : _("No detail"));
+					gda_holder_set_not_null ((GdaHolder*) plist->data, TRUE);
+				}
+				else 
+					g_warning (_("Allowed GdaHolder's value to be NULL for the iterator "
+						     "to be updated"));
+			}
+			else {
+				retval = FALSE;
+				g_warning (_("Could not change iter's value for column %d: %s"), i,
+					   error && error->message ? error->message : _("No detail"));
+			}
+			if (error)
+				g_error_free (error);
+		}
         }
 
 	g_object_set (G_OBJECT (iter), "current-row", imodel->priv->iter_row, NULL);

Modified: trunk/libgda/gda-server-operation.c
==============================================================================
--- trunk/libgda/gda-server-operation.c	(original)
+++ trunk/libgda/gda-server-operation.c	Thu Sep 25 18:55:33 2008
@@ -442,8 +442,7 @@
 		return NULL;
 
 	string = g_string_new ("");
-	lnode = node;
-	while (lnode) {
+	for (lnode = node; lnode; lnode = lnode->parent) {
 		if (lnode->type == GDA_SERVER_OPERATION_NODE_SEQUENCE_ITEM) {
 			gchar *str;
 
@@ -456,7 +455,6 @@
 		else 
 			g_string_prepend (string, lnode->path_name);
 		g_string_prepend_c (string, '/');
-		lnode = lnode->parent;
 	}
 
 	retval = string->str;
@@ -474,7 +472,7 @@
 {
 	gchar *path, *seq_path;
 	Node *new_node;
-	GSList *seq_item_nodes;
+	GSList *seq_item_nodes, *list;
 
 	g_assert (node);
 	g_assert (node->type == GDA_SERVER_OPERATION_NODE_SEQUENCE);
@@ -492,6 +490,8 @@
 	node->d.seq.seq_items = g_slist_append (node->d.seq.seq_items, new_node);
 
 	new_node->d.seq_item_nodes = seq_item_nodes;
+	for (list = seq_item_nodes; list; list = list->next)
+		((Node*) list->data)->parent = new_node;
 
 	clean_nodes_info_cache (op);
 #ifdef GDA_DEBUG_signal
@@ -702,6 +702,9 @@
         g_object_set_data (G_OBJECT (op), "xmlerror", newerr);
 }
 
+/*
+ * Warning: the new nodes' parent is not set!
+ */
 static GSList *
 load_xml_spec (GdaServerOperation *op, xmlNodePtr specnode, const gchar *root, GError **error)
 {
@@ -1213,7 +1216,7 @@
 				else
 					str = gda_value_stringify (value);
 			}
-			node = xmlNewChild (parent, NULL, BAD_CAST "op_data", (xmlChar*)str);
+			node = xmlNewTextChild (parent, NULL, BAD_CAST "op_data", (xmlChar*)str);
 			g_free (str);
 
 			path = g_strdup_printf ("%s/%s", complete_path, gda_holder_get_id (GDA_HOLDER (list->data)));
@@ -1242,7 +1245,7 @@
 			else
 				str = gda_value_stringify (value);
 		}
-		node = xmlNewChild (parent, NULL, BAD_CAST "op_data", (xmlChar*)str);
+		node = xmlNewTextChild (parent, NULL, BAD_CAST "op_data", (xmlChar*)str);
 		g_free (str);
 		xmlSetProp(node, (xmlChar*)"path", (xmlChar*)complete_path);
 		break;

Modified: trunk/libgda/gda-server-provider-extra.c
==============================================================================
--- trunk/libgda/gda-server-provider-extra.c	(original)
+++ trunk/libgda/gda-server-provider-extra.c	Thu Sep 25 18:55:33 2008
@@ -180,6 +180,75 @@
 	return dh;
 }
 
+static gboolean
+param_to_null_foreach (GdaSqlAnyPart *part, gpointer data, GError **error)
+{
+	if (part->type == GDA_SQL_ANY_EXPR) {
+		GdaSqlExpr *expr = (GdaSqlExpr*) part;
+		if (expr->param_spec) {
+			GType type = expr->param_spec->g_type;
+			gda_sql_param_spec_free (expr->param_spec);
+			expr->param_spec = NULL;
+
+			if (!expr->value) {
+				if (type != GDA_TYPE_NULL)
+					expr->value = gda_value_new_from_string ("0", type);
+				else
+					g_value_set_int ((expr->value = gda_value_new (G_TYPE_INT)), 0);
+			}
+		}
+	}
+	return TRUE;
+}
+
+/**
+ * gda_select_alter_select_for_empty
+ *
+ * Returns: a new #GdaStatement
+ */
+GdaStatement *
+gda_select_alter_select_for_empty (GdaStatement *stmt, GError **error)
+{
+	GdaStatement *estmt;
+	GdaSqlStatement *sqlst;
+	GdaSqlStatementSelect *stsel;
+
+	g_assert (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT);
+	g_object_get (G_OBJECT (stmt), "structure", &sqlst, NULL);
+	g_assert (sqlst);
+
+	if (sqlst->sql) {
+		g_free (sqlst->sql);
+		sqlst->sql = NULL;
+	}
+	stsel = (GdaSqlStatementSelect*) sqlst->contents;
+
+	/* set the WHERE condition to "1 = 0" */
+	GdaSqlExpr *expr, *cond = stsel->where_cond;
+	GdaSqlOperation *op;
+	if (cond)
+		gda_sql_expr_free (cond);
+	cond = gda_sql_expr_new (GDA_SQL_ANY_PART (stsel));
+	stsel->where_cond = cond;
+	op = gda_sql_operation_new (GDA_SQL_ANY_PART (cond));
+	cond->cond = op;
+	op->operator_type = GDA_SQL_OPERATOR_TYPE_EQ;
+	expr = gda_sql_expr_new (GDA_SQL_ANY_PART (op));
+	op->operands = g_slist_prepend (NULL, expr);
+	g_value_set_int ((expr->value = gda_value_new (G_TYPE_INT)), 1);
+	expr = gda_sql_expr_new (GDA_SQL_ANY_PART (op));
+	op->operands = g_slist_prepend (op->operands, expr);
+	g_value_set_int ((expr->value = gda_value_new (G_TYPE_INT)), 0);
+
+	/* replace any selected field which has a parameter with NULL */
+	gda_sql_any_part_foreach (GDA_SQL_ANY_PART (stsel), (GdaSqlForeachFunc) param_to_null_foreach,
+				  NULL, NULL);
+
+	/* create new statement */
+	estmt = g_object_new (GDA_TYPE_STATEMENT, "structure", sqlst, NULL);
+	gda_sql_statement_free (sqlst);
+	return estmt;
+}
 
 /**
  * gda_server_provider_get_schema_nb_columns

Modified: trunk/libgda/gda-server-provider-extra.h
==============================================================================
--- trunk/libgda/gda-server-provider-extra.h	(original)
+++ trunk/libgda/gda-server-provider-extra.h	Thu Sep 25 18:55:33 2008
@@ -50,6 +50,11 @@
 GdaDataHandler *gda_server_provider_get_data_handler_default (GdaServerProvider *provider, GdaConnection *cnc,
 							      GType type, const gchar *dbms_type);
 
+/* Convert a SELECT statement with potentially some parameters to another SELECT statement
+ * where all the parameters are removed and where the WHERE condition is set to "0 = 1"
+ * to make sure no row will ever be returned
+ */
+GdaStatement *gda_select_alter_select_for_empty (GdaStatement *stmt, GError **error);
 
 /*
  * Help to implement the GdaDataHandler retreiving for the providers

Modified: trunk/libgda/gda-server-provider.c
==============================================================================
--- trunk/libgda/gda-server-provider.c	(original)
+++ trunk/libgda/gda-server-provider.c	Thu Sep 25 18:55:33 2008
@@ -500,7 +500,7 @@
 					value = gda_holder_get_value (GDA_HOLDER (list->data));
 					if (value)
 						str = gda_value_stringify (value);
-					node = xmlNewChild (top, NULL, BAD_CAST "op_data", BAD_CAST str);
+					node = xmlNewTextChild (top, NULL, BAD_CAST "op_data", BAD_CAST str);
 					g_free (str);
 					xmlSetProp (node, BAD_CAST "path", BAD_CAST id);
 				}

Modified: trunk/libgda/gda-statement.h
==============================================================================
--- trunk/libgda/gda-statement.h	(original)
+++ trunk/libgda/gda-statement.h	Thu Sep 25 18:55:33 2008
@@ -51,7 +51,8 @@
 	GDA_STATEMENT_MODEL_RANDOM_ACCESS   = 1 << 0,
 	GDA_STATEMENT_MODEL_CURSOR_FORWARD  = 1 << 1,
 	GDA_STATEMENT_MODEL_CURSOR_BACKWARD = 1 << 2,
-	GDA_STATEMENT_MODEL_CURSOR          = GDA_STATEMENT_MODEL_CURSOR_FORWARD | GDA_STATEMENT_MODEL_CURSOR_BACKWARD
+	GDA_STATEMENT_MODEL_CURSOR          = GDA_STATEMENT_MODEL_CURSOR_FORWARD | GDA_STATEMENT_MODEL_CURSOR_BACKWARD,
+	GDA_STATEMENT_MODEL_ALLOW_NOPARAM   = 1 << 3
 } GdaStatementModelUsage;
 
 typedef enum {

Modified: trunk/libgda/gda-util.c
==============================================================================
--- trunk/libgda/gda-util.c	(original)
+++ trunk/libgda/gda-util.c	Thu Sep 25 18:55:33 2008
@@ -356,12 +356,12 @@
 				}
 				if (!use_col_ids) {
 					if (str && *str) 
-						field = xmlNewChild (row, NULL,  (xmlChar*)"gda_value", (xmlChar*)str);
+						field = xmlNewTextChild (row, NULL,  (xmlChar*)"gda_value", (xmlChar*)str);
 					else
 						field = xmlNewChild (row, NULL,  (xmlChar*)"gda_value", NULL);
 				}
 				else {
-					field = xmlNewChild (row, NULL,  (xmlChar*)"gda_array_value", (xmlChar*)str);
+					field = xmlNewTextChild (row, NULL,  (xmlChar*)"gda_array_value", (xmlChar*)str);
 					xmlSetProp(field, (xmlChar*)"colid",  (xmlChar*)col_ids [c]);
 				}
 

Modified: trunk/libgda/sqlite/gda-sqlite-provider.c
==============================================================================
--- trunk/libgda/sqlite/gda-sqlite-provider.c	(original)
+++ trunk/libgda/sqlite/gda-sqlite-provider.c	Thu Sep 25 18:55:33 2008
@@ -1819,6 +1819,9 @@
 	GdaSqlitePStmt *ps;
 	SqliteConnectionData *cdata;
 	gboolean new_ps = FALSE;
+	gboolean allow_noparam;
+	gboolean empty_rs = FALSE; /* TRUE when @allow_noparam is TRUE and there is a problem with @params
+				      => resulting data model will be empty (0 row) */
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
@@ -1830,6 +1833,13 @@
                 return NULL;
 	}
 
+	if (! (model_usage & GDA_STATEMENT_MODEL_RANDOM_ACCESS) &&
+	    ! (model_usage & GDA_STATEMENT_MODEL_CURSOR_FORWARD))
+		model_usage |= GDA_STATEMENT_MODEL_RANDOM_ACCESS;
+	
+	allow_noparam = (model_usage & GDA_STATEMENT_MODEL_ALLOW_NOPARAM) &&
+		(gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT);
+
 	if (last_inserted_row)
 		*last_inserted_row = NULL;
 
@@ -1936,7 +1946,8 @@
 	int i;
 	for (i = 1, list = _GDA_PSTMT (ps)->param_ids; list; list = list->next, i++) {
 		const gchar *pname = (gchar *) list->data;
-		
+		GdaHolder *h = NULL;		
+
 		if (!params) {
 			event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
 			gda_connection_event_set_description (event, _("Missing parameter(s) to execute query"));
@@ -1946,34 +1957,52 @@
 			break;
 		}
 
-		GdaHolder *h;
-		h = gda_set_get_holder (params, pname);
-		if (!h) {
-			gchar *tmp = gda_alphanum_to_text (g_strdup (pname + 1));
-			if (tmp) {
-				h = gda_set_get_holder (params, tmp);
-				g_free (tmp);
+		if (params) {
+			h = gda_set_get_holder (params, pname);
+			if (!h) {
+				gchar *tmp = gda_alphanum_to_text (g_strdup (pname + 1));
+				if (tmp) {
+					h = gda_set_get_holder (params, tmp);
+					g_free (tmp);
+				}
 			}
 		}
 		if (!h) {
-			gchar *str;
-			str = g_strdup_printf (_("Missing parameter '%s' to execute query"), pname);
-			event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
-			gda_connection_event_set_description (event, str);
-			g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
-				     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, str);
-			g_free (str);
-			break;
+			if (! allow_noparam) {
+				gchar *str;
+				str = g_strdup_printf (_("Missing parameter '%s' to execute query"), pname);
+				event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
+				gda_connection_event_set_description (event, str);
+				g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+					     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, str);
+				g_free (str);
+				break;
+			}
+			else {
+				/* bind param to NULL */
+				sqlite3_bind_null (ps->sqlite_stmt, i);
+				empty_rs = TRUE;
+				continue;
+			}
 		}
+
 		if (!gda_holder_is_valid (h)) {
-			gchar *str;
-			str = g_strdup_printf (_("Parameter '%s' is invalid"), pname);
-			event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
-			gda_connection_event_set_description (event, str);
-			g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
-				     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, str);
-			g_free (str);
-			break;
+			if (! allow_noparam) {
+				gchar *str;
+				str = g_strdup_printf (_("Parameter '%s' is invalid"), pname);
+				event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
+				gda_connection_event_set_description (event, str);
+				g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+					     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, str);
+				g_free (str);
+				break;
+			}
+			else {
+				/* bind param to NULL */
+				sqlite3_bind_null (ps->sqlite_stmt, i);
+				empty_rs = TRUE;
+				continue;
+			}
 		}
 		/*g_print ("BINDING param '%s' to %p\n", pname, h);*/
 		
@@ -2089,7 +2118,7 @@
 		else
 			flags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
 
-                data_model = (GObject *) gda_sqlite_recordset_new (cnc, ps, params, flags, col_types);
+                data_model = (GObject *) gda_sqlite_recordset_new (cnc, ps, params, flags, col_types, empty_rs);
 		gda_connection_internal_statement_executed (cnc, stmt, params, NULL);
 		if (new_ps)
 			g_object_unref (ps);

Modified: trunk/libgda/sqlite/gda-sqlite-recordset.c
==============================================================================
--- trunk/libgda/sqlite/gda-sqlite-recordset.c	(original)
+++ trunk/libgda/sqlite/gda-sqlite-recordset.c	Thu Sep 25 18:55:33 2008
@@ -48,7 +48,8 @@
 static GdaRow *fetch_next_sqlite_row (GdaSqliteRecordset *model, gboolean do_store, GError **error);
 
 struct _GdaSqliteRecordsetPrivate {
-	gint           next_row_num;
+	gboolean      empty_forced;
+	gint          next_row_num;
 	GdaRow       *tmp_row; /* used in cursor mode */
 };
 static GObjectClass *parent_class = NULL;
@@ -63,6 +64,7 @@
 	g_return_if_fail (GDA_IS_SQLITE_RECORDSET (recset));
 	recset->priv = g_new0 (GdaSqliteRecordsetPrivate, 1);
 	recset->priv->next_row_num = 0;
+	recset->priv->empty_forced = FALSE;
 }
 
 static void
@@ -180,7 +182,7 @@
  */
 GdaDataModel *
 gda_sqlite_recordset_new (GdaConnection *cnc, GdaSqlitePStmt *ps, GdaSet *exec_params,
-			  GdaDataModelAccessFlags flags, GType *col_types)
+			  GdaDataModelAccessFlags flags, GType *col_types, gboolean force_empty)
 {
 	GdaSqliteRecordset *model;
         SqliteConnectionData *cdata;
@@ -255,7 +257,8 @@
 	/* create data model */
         model = g_object_new (GDA_TYPE_SQLITE_RECORDSET, "connection", cnc, 
 			      "prepared-stmt", ps, "model-usage", rflags, 
-			      "exec-params", exec_params, NULL);
+			      "exec-params", exec_params, 
+			      "auto-reset", force_empty, NULL);
 
         /* fill the data model */
         read_rows_to_init_col_types (model);
@@ -295,7 +298,10 @@
 		return NULL;
 	ps = GDA_SQLITE_PSTMT (GDA_DATA_SELECT (model)->prep_stmt);
 
-	rc = sqlite3_step (ps->sqlite_stmt);
+	if (model->priv->empty_forced)
+		rc = SQLITE_DONE;
+	else
+		rc = sqlite3_step (ps->sqlite_stmt);
 	switch (rc) {
 	case  SQLITE_ROW: {
 		gint col;

Modified: trunk/libgda/sqlite/gda-sqlite-recordset.h
==============================================================================
--- trunk/libgda/sqlite/gda-sqlite-recordset.h	(original)
+++ trunk/libgda/sqlite/gda-sqlite-recordset.h	Thu Sep 25 18:55:33 2008
@@ -53,7 +53,8 @@
 
 GType         gda_sqlite_recordset_get_type  (void) G_GNUC_CONST;
 GdaDataModel *gda_sqlite_recordset_new       (GdaConnection *cnc, GdaSqlitePStmt *ps, GdaSet *exec_params,
-					      GdaDataModelAccessFlags flags, GType *col_types);
+					      GdaDataModelAccessFlags flags, GType *col_types, 
+					      gboolean force_empty);
 
 G_END_DECLS
 

Modified: trunk/providers/postgres/gda-postgres-provider.c
==============================================================================
--- trunk/providers/postgres/gda-postgres-provider.c	(original)
+++ trunk/providers/postgres/gda-postgres-provider.c	Thu Sep 25 18:55:33 2008
@@ -1912,6 +1912,9 @@
 {
 	GdaPostgresPStmt *ps;
 	PostgresConnectionData *cdata;
+	gboolean allow_noparam;
+	gboolean empty_rs = FALSE; /* TRUE when @allow_noparam is TRUE and there is a problem with @params
+				      => resulting data model will be empty (0 row) */
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
@@ -1924,6 +1927,13 @@
                 return NULL;
 	}
 
+	if (! (model_usage & GDA_STATEMENT_MODEL_RANDOM_ACCESS) &&
+	    ! (model_usage & GDA_STATEMENT_MODEL_CURSOR_FORWARD))
+		model_usage |= GDA_STATEMENT_MODEL_RANDOM_ACCESS;
+
+	allow_noparam = (model_usage & GDA_STATEMENT_MODEL_ALLOW_NOPARAM) &&
+		(gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT);
+
 	if (last_inserted_row)
 		*last_inserted_row = NULL;
 
@@ -2020,7 +2030,7 @@
 
 	for (i = 0, list = _GDA_PSTMT (ps)->param_ids; list; list = list->next, i++) {
 		const gchar *pname = (gchar *) list->data;
-		GdaHolder *h;
+		GdaHolder *h = NULL;
 		
 		/* find requested parameter */
 		if (!params) {
@@ -2032,33 +2042,51 @@
 			break;
 		}
 
-		h = gda_set_get_holder (params, pname);
-		if (!h) {
-			gchar *tmp = gda_alphanum_to_text (g_strdup (pname + 1));
-			if (tmp) {
-				h = gda_set_get_holder (params, tmp);
-				g_free (tmp);
+		if (params) {
+			h = gda_set_get_holder (params, pname);
+			if (!h) {
+				gchar *tmp = gda_alphanum_to_text (g_strdup (pname + 1));
+				if (tmp) {
+					h = gda_set_get_holder (params, tmp);
+					g_free (tmp);
+				}
 			}
 		}
 		if (!h) {
-			gchar *str;
-			str = g_strdup_printf (_("Missing parameter '%s' to execute query"), pname);
-			event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
-			gda_connection_event_set_description (event, str);
-			g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
-				     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, str);
-			g_free (str);
-			break;
+			if (! allow_noparam) {
+				gchar *str;
+				str = g_strdup_printf (_("Missing parameter '%s' to execute query"), pname);
+				event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
+				gda_connection_event_set_description (event, str);
+				g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+					     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, str);
+				g_free (str);
+				break;
+			}
+			else {
+				/* bind param to NULL */
+				param_values [i] = NULL;
+				empty_rs = TRUE;
+				continue;
+			}
 		}
 		if (!gda_holder_is_valid (h)) {
-			gchar *str;
-			str = g_strdup_printf (_("Parameter '%s' is invalid"), pname);
-			event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
-			gda_connection_event_set_description (event, str);
-			g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
-				     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, str);
-			g_free (str);
-			break;
+			if (! allow_noparam) {
+				gchar *str;
+				str = g_strdup_printf (_("Parameter '%s' is invalid"), pname);
+				event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
+				gda_connection_event_set_description (event, str);
+				g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+					     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, str);
+				g_free (str);
+				break;
+			}
+			else {
+				/* bind param to NULL */
+				param_values [i] = NULL;
+				empty_rs = TRUE;
+				continue;
+			}
 		}
 
 		/* actual binding using the C API, for parameter at position @i */
@@ -2134,8 +2162,24 @@
 	/* execute prepared statement using C API: random access based */
 	PGresult *pg_res;
 	GObject *retval = NULL;
-	pg_res = PQexecPrepared (cdata->pconn, ps->prep_name, nb_params, (const char * const *) param_values,
-				 param_lengths, param_formats, 0);
+
+	if (empty_rs) {
+		GdaStatement *estmt;
+		gchar *esql;
+		estmt = gda_select_alter_select_for_empty (stmt, error);
+		if (!estmt)
+			return NULL;
+		esql = gda_statement_to_sql (estmt, NULL, error);
+		g_object_unref (estmt);
+		if (!esql) 
+			return NULL;
+
+		pg_res = PQexec (cdata->pconn, esql);
+		g_free (esql);
+	}
+	else
+		pg_res = PQexecPrepared (cdata->pconn, ps->prep_name, nb_params, (const char * const *) param_values,
+					 param_lengths, param_formats, 0);
 
 	g_strfreev (param_values);
 	g_free (param_lengths);
@@ -2166,8 +2210,11 @@
 
                                 PQclear (pg_res);
                         }
-                        else if (status == PGRES_TUPLES_OK) 
+                        else if (status == PGRES_TUPLES_OK) {
 				retval = (GObject*) gda_postgres_recordset_new_random (cnc, ps, params, pg_res, col_types);
+				if (allow_noparam)
+					g_object_set (retval, "auto-reset", TRUE, NULL);
+			}
                         else {
                                 PQclear (pg_res);
                                 retval = (GObject *) gda_data_model_array_new (0);

Modified: trunk/testing/gda-provider-status.c
==============================================================================
--- trunk/testing/gda-provider-status.c	(original)
+++ trunk/testing/gda-provider-status.c	Thu Sep 25 18:55:33 2008
@@ -344,13 +344,13 @@
 	table = xmlNewChild (file->body, NULL, "table", NULL);
 	xmlSetProp (table, "width", (xmlChar*)"100%");
 	tr = xmlNewChild (table, NULL, "tr", NULL);
-	td = xmlNewChild (tr, NULL, "th", header_str);
+	td = xmlNewTextChild (tr, NULL, "th", header_str);
 	xmlSetProp (td, "colspan", (xmlChar*)"4");
 
 	/* line 1 */
 	tr = xmlNewChild (table, NULL, "tr", NULL);
 	xmlNewChild (tr, NULL, "td", "Provider's name:");
-	td = xmlNewChild (tr, NULL, "td", gda_server_provider_get_name (prov));
+	td = xmlNewTextChild (tr, NULL, "td", gda_server_provider_get_name (prov));
 	xmlSetProp (td, "width", (xmlChar*)"35%");
 	xmlNewChild (tr, NULL, "td", "Provider is virtual:");
 	td = xmlNewChild (tr, NULL, "td", is_virt ? "Yes (uses the SQLite engine)" : "No");
@@ -359,16 +359,16 @@
 	/* line 2 */
 	tr = xmlNewChild (table, NULL, "tr", NULL);
 	xmlNewChild (tr, NULL, "td", "Provider's version:");
-	xmlNewChild (tr, NULL, "td", gda_server_provider_get_version (prov));
+	xmlNewTextChild (tr, NULL, "td", gda_server_provider_get_version (prov));
 	xmlNewChild (tr, NULL, "td", "Provider's server version:");
-	xmlNewChild (tr, NULL, "td", cnc ? gda_server_provider_get_server_version (prov, cnc) : "(non connected)");
+	xmlNewTextChild (tr, NULL, "td", cnc ? gda_server_provider_get_server_version (prov, cnc) : "(non connected)");
 
 	/* line 3 */
 	tr = xmlNewChild (table, NULL, "tr", NULL);
 	xmlNewChild (tr, NULL, "td", "Provider's description:");
-	xmlNewChild (tr, NULL, "td", pinfo->description);
+	xmlNewTextChild (tr, NULL, "td", pinfo->description);
 	xmlNewChild (tr, NULL, "td", "Filename:");
-	xmlNewChild (tr, NULL, "td", pinfo->location);
+	xmlNewTextChild (tr, NULL, "td", pinfo->location);
 
 	/* line 4 */
 	parser = gda_server_provider_create_parser (prov, cnc);
@@ -391,7 +391,7 @@
 			str = g_strdup_printf (", %s()", pf->name);
 		else
 			str = g_strdup_printf ("%s()", pf->name);
-		span = xmlNewChild (td, NULL, "span", str);
+		span = xmlNewTextChild (td, NULL, "span", str);
 		g_free (str);
 		if (pf->should_be)
 			xmlSetProp (span, "class", (xmlChar*)"error");
@@ -415,7 +415,7 @@
 			str = g_strdup_printf (", %s()", pf->name);
 		else
 			str = g_strdup_printf ("%s()", pf->name);
-		span = xmlNewChild (td, NULL, "span", str);
+		span = xmlNewTextChild (td, NULL, "span", str);
 		g_free (str);
 		if (pf->should_be)
 			xmlSetProp (span, "class", (xmlChar*)"error");
@@ -454,7 +454,7 @@
 					str = g_strdup_printf (", %s()", pf->name);
 				else
 					str = g_strdup_printf ("%s()", pf->name);
-				span = xmlNewChild (td, NULL, "span", str);
+				span = xmlNewTextChild (td, NULL, "span", str);
 				g_free (str);
 				if (pf->should_be)
 					xmlSetProp (span, "class", (xmlChar*)"error");
@@ -465,7 +465,7 @@
 	}
 	else {
 		if (has_xa) {
-			td = xmlNewChild (tr, NULL, "td", 
+			td = xmlNewTextChild (tr, NULL, "td", 
 					  (xmlChar*) "The provider does not have the 'xa_funcs' part but "
 					  "reports that distributed transactions are "
 					  "supported.");
@@ -491,7 +491,7 @@
 			else
 				str = g_strdup (gda_holder_get_id (holder));
 			g_free (descr);
-			div = xmlNewChild (td, NULL, "div", str);
+			div = xmlNewTextChild (td, NULL, "div", str);
 			g_free (str);
 		}
 	}
@@ -514,7 +514,7 @@
 				else
 					str = g_strdup (gda_holder_get_id (holder));
 				g_free (descr);
-				div = xmlNewChild (td, NULL, "div", str);
+				div = xmlNewTextChild (td, NULL, "div", str);
 				g_free (str);
 			}
 		}
@@ -543,7 +543,7 @@
 		}
 	}
 	if (string) {
-		xmlNewChild (tr, NULL, "td", string->str);
+		xmlNewTextChild (tr, NULL, "td", string->str);
 		g_string_free (string, TRUE);
 	}
 	else
@@ -563,7 +563,7 @@
 		}
 	}
 	if (string) {
-		xmlNewChild (tr, NULL, "td", string->str);
+		xmlNewTextChild (tr, NULL, "td", string->str);
 		g_string_free (string, TRUE);
 	}
 	else

Modified: trunk/tests/data-models/Makefile.am
==============================================================================
--- trunk/tests/data-models/Makefile.am	(original)
+++ trunk/tests/data-models/Makefile.am	Thu Sep 25 18:55:33 2008
@@ -7,8 +7,8 @@
 	-DTOP_SRC_DIR=\""$(top_srcdir)"\" \
 	-DTOP_BUILD_DIR=\""$(top_builddir)"\"
 
-check_PROGRAMS = check_model_import check_virtual check_data_proxy check_model_copy check_pmodel
-TESTS = check_model_import check_virtual check_data_proxy check_model_copy check_pmodel
+check_PROGRAMS = check_model_import check_virtual check_data_proxy check_model_copy check_pmodel check_empty_rs
+TESTS = check_model_import check_virtual check_data_proxy check_model_copy check_pmodel check_empty_rs
 
 common_sources = 
 
@@ -43,6 +43,11 @@
 	$(top_builddir)/tests/libgda-test-4.0.la \
 	$(LIBGDA_LIBS)
 
+check_empty_rs_SOURCES = $(common_sources) check_empty_rs.c
+check_empty_rs_LDADD = \
+	$(top_builddir)/libgda/libgda-4.0.la \
+	$(LIBGDA_LIBS)
+
 EXTRA_DIST = \
 	check_virtual.csv \
 	city.csv \

Added: trunk/tests/data-models/check_empty_rs.c
==============================================================================
--- (empty file)
+++ trunk/tests/data-models/check_empty_rs.c	Thu Sep 25 18:55:33 2008
@@ -0,0 +1,90 @@
+#include <libgda/libgda.h>
+#include <sql-parser/gda-sql-parser.h>
+#include <libgda/gda-server-provider-extra.h>
+#include <stdlib.h>
+#include <string.h>
+
+static gboolean run_test (const gchar *sql, const gchar *empty_rs_serial, GError **error);
+
+typedef struct {
+	gchar *sql;
+	gchar *empty_rs_serial;
+} ATestData;
+
+ATestData tests [] = {
+	{"SELECT * FROM table WHERE id=3", "{\"statement\":{\"sql\":null,\"stmt_type\":\"SELECT\",\"contents\":{\"distinct\":\"false\",\"fields\":[{\"expr\":{\"value\":\"*\"}}],\"from\":{\"targets\":[{\"expr\":{\"value\":\"table\"},\"table_name\":\"table\"}]},\"where\":{\"operation\":{\"operator\":\"=\",\"operand0\":{\"value\":\"0\"},\"operand1\":{\"value\":\"1\"}}}}}}"},
+	{"SELECT id, func1 (3, func2('rr'), ##aparam::string), NULL FROM a, b", "{\"statement\":{\"sql\":null,\"stmt_type\":\"SELECT\",\"contents\":{\"distinct\":\"false\",\"fields\":[{\"expr\":{\"value\":\"id\"},\"field_name\":\"id\"},{\"expr\":{\"func\":{\"function_name\":\"func1\",\"function_args\":[{\"value\":\"3\"},{\"func\":{\"function_name\":\"func2\",\"function_args\":[{\"value\":\"'rr'\"}]}},{\"value\":\"0\"}]}}},{\"expr\":{\"value\":null}}],\"from\":{\"targets\":[{\"expr\":{\"value\":\"a\"},\"table_name\":\"a\"},{\"expr\":{\"value\":\"b\"},\"table_name\":\"b\"}],\"joins\":[{\"join_type\":\"CROSS\",\"join_pos\":\"1\"}]},\"where\":{\"operation\":{\"operator\":\"=\",\"operand0\":{\"value\":\"0\"},\"operand1\":{\"value\":\"1\"}}}}}}"},
+	{"SELECT ##p1 + ##p2", "{\"statement\":{\"sql\":null,\"stmt_type\":\"SELECT\",\"contents\":{\"distinct\":\"false\",\"fields\":[{\"expr\":{\"operation\":{\"operator\":\"+\",\"operand0\":{\"value\":\"0\"},\"operand1\":{\"value\":\"0\"}}}}],\"where\":{\"operation\":{\"operator\":\"=\",\"operand0\":{\"value\":\"0\"},\"operand1\":{\"value\":\"1\"}}}}}}"},
+	{NULL, NULL}
+};
+
+int 
+main (int argc, char **argv)
+{
+	GError *error = NULL;	
+	gint i, nfailed;
+
+	gda_init ();
+	nfailed = 0;
+	for (i = 0; ; i++) {
+		ATestData *td = &(tests[i]);
+		if (!td->sql)
+			break;
+		if (!run_test (td->sql, td->empty_rs_serial, &error)) {
+			g_print ("Test %d failed: %s\n", i, 
+				 error && error->message ? error->message : "No detail");
+			nfailed++;
+			if (error)
+				g_error_free (error);
+			error = NULL;
+		}
+	}
+
+	if (nfailed == 0)
+		g_print ("Ok.\n");
+	return nfailed == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+static gboolean
+run_test (const gchar *sql, const gchar *empty_rs_serial, GError **error)
+{
+	static GdaSqlParser *parser = NULL;
+	GdaStatement *orig, *trans;
+	gboolean retval = FALSE;
+	gchar *tsql;
+
+	if (!parser)
+		parser = gda_sql_parser_new ();
+	orig = gda_sql_parser_parse_string (parser, sql, NULL, error);
+	if (!orig)
+		return FALSE;
+
+	trans = gda_select_alter_select_for_empty (orig, error);
+	if (!trans) 
+		goto out;
+	
+	tsql = gda_statement_to_sql_extended (trans, NULL, NULL, 0, NULL, error);
+	if (!tsql)
+		goto out;
+	g_print ("SQL: %s\nTRA: %s\n", sql, tsql);
+	g_free (tsql);
+
+	tsql = gda_statement_serialize (trans);
+	if (!empty_rs_serial)
+		g_print ("Missing test data!\n  SQL: %s\n  SER: %s\n", sql, tsql);
+	else if (strcmp (tsql, empty_rs_serial)) {
+		g_print ("Test failed!\n  SQL: %s\n  EXP: %s\n  GOT: %s\n", sql, empty_rs_serial, tsql);
+		g_set_error (error, 0, 0,
+			     "Failed serialized comparison");
+		g_free (tsql);
+		goto out;
+	}
+
+	retval = TRUE;
+ out:
+
+	if (trans)
+		g_object_unref (trans);
+	g_object_unref (orig);
+	return retval;
+}

Modified: trunk/tests/data-models/check_pmodel.c
==============================================================================
--- trunk/tests/data-models/check_pmodel.c	(original)
+++ trunk/tests/data-models/check_pmodel.c	Thu Sep 25 18:55:33 2008
@@ -41,6 +41,7 @@
 				  GdaConnection *cnc, GdaStatement *stmt, GdaSet *stmt_params);
 static gint check_append_values (GdaDataModel *model, GList *set_values,
 				 GdaConnection *cnc, GdaStatement *stmt, GdaSet *stmt_params);
+static void dump_data_model (GdaDataModel *model);
 
 typedef gboolean (*TestFunc) (GdaConnection *);
 static gint test1 (GdaConnection *cnc);
@@ -58,6 +59,7 @@
 static gint test13 (GdaConnection *cnc);
 static gint test14 (GdaConnection *cnc);
 static gint test15 (GdaConnection *cnc);
+static gint test16 (GdaConnection *cnc);
 
 TestFunc tests[] = {
 	test1,
@@ -74,7 +76,8 @@
 	test12,
 	test13,
 	test14,
-	test15
+	test15,
+	test16
 };
 
 int
@@ -1556,7 +1559,7 @@
 }
 
 /*
- * - Create a GdaDataSelect with a missing parameter
+ * - Create a GdaDataSelect with a parameter
  * - Set modification statements
  * - Refresh data model and compare with direct SELECT.
  *
@@ -1634,6 +1637,140 @@
 }
 
 /*
+ * - Create a GdaDataSelect with a missing parameter
+ * - Set modification statements
+ * - Refresh data model and compare with direct SELECT.
+ *
+ * Returns the number of failures 
+ */
+static gint
+test16 (GdaConnection *cnc)
+{
+	GError *error = NULL;
+        GdaDataModel *model, *rerun;
+        GdaStatement *stmt;
+        GdaSet *params;
+        gint nfailed = 0;
+
+	clear_signals ();
+
+        /* create GdaDataModelQuery */
+        stmt = stmt_from_string ("SELECT * FROM customers WHERE id <= ##theid::gint");
+        g_assert (gda_statement_get_parameters (stmt, &params, NULL));
+
+        model = gda_connection_statement_execute_select_full (cnc, stmt, params, GDA_STATEMENT_MODEL_ALLOW_NOPARAM,
+							      NULL, &error);
+	if (!model) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+                g_print ("Can't run a SELECT with invalid parameters and the "
+			 "GDA_STATEMENT_MODEL_ALLOW_NOPARAM flag: %s\n",
+                         error && error->message ? error->message : "No detail");
+#endif
+                goto out;
+	}
+
+	if (gda_data_model_get_n_rows (model) != 0) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+                g_print ("Data model should report 0 row and reports: %d",
+			 gda_data_model_get_n_rows (model));
+#endif
+                goto out;
+	}
+
+	dump_data_model (model);
+
+	/* set a custom data */
+	g_object_set_data (G_OBJECT (model), "mydata", "hey");
+	monitor_model_signals (model);
+
+	/**/
+	if (! gda_set_set_holder_value (params, &error, "theid", 9)) {
+                nfailed++;
+#ifdef CHECK_EXTRA_INFO
+                g_print ("Can't set 'theid' value: %s \n",
+                         error && error->message ? error->message : "No detail");
+#endif
+                goto out;
+        }
+	check_expected_signal (model, 'R', -1);
+
+	const gchar *dstr = g_object_get_data (G_OBJECT (model), "mydata");
+	if (!dstr || strcmp (dstr, "hey")) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+                g_print ("Data model lost custom added data: expected 'hey' and got '%s'\n",
+			 dstr);
+#endif
+                goto out;
+	}
+
+	dump_data_model (model);
+
+	/**/
+	if (! gda_set_set_holder_value (params, &error, "theid", 120)) {
+                nfailed++;
+#ifdef CHECK_EXTRA_INFO
+                g_print ("Can't set 'theid' value: %s \n",
+                         error && error->message ? error->message : "No detail");
+#endif
+                goto out;
+        }
+	check_expected_signal (model, 'R', -1);
+
+	dstr = g_object_get_data (G_OBJECT (model), "mydata");
+	if (!dstr || strcmp (dstr, "hey")) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+                g_print ("Data model lost custom added data: expected 'hey' and got '%s'\n",
+			 dstr);
+#endif
+                goto out;
+	}
+
+	dump_data_model (model);
+
+	rerun = gda_connection_statement_execute_select (cnc, stmt, params, &error);
+	g_assert (rerun);
+	if (! compare_data_models (model, rerun, NULL)) {
+                nfailed++;
+#ifdef CHECK_EXTRA_INFO
+                g_print ("Data model differs after a refresh\n");
+#endif
+                goto out;
+        }
+        g_object_unref (rerun);
+
+	/**/
+	gda_holder_force_invalid (GDA_HOLDER (params->holders->data));
+	check_expected_signal (model, 'R', -1);
+	dump_data_model (model);
+
+ out:
+        g_object_unref (model);
+        g_object_unref (stmt);
+
+        return nfailed;
+}
+
+static void
+dump_data_model (GdaDataModel *model)
+{
+	gint i, ncols;
+	g_print ("=== Data Model Dump ===\n");
+	ncols = gda_data_model_get_n_columns (model);
+	for (i = 0; i < ncols; i++) {
+		GdaColumn *col;
+		col = gda_data_model_describe_column (model, i);
+		if (!col)
+			g_print ("Missing column %d\n", i);
+		g_print ("Column %d: ptr=>%p type=>%s\n", i, col, g_type_name (gda_column_get_g_type (col)));
+	}
+	gda_data_model_dump (model, stdout);
+}
+
+/*
  * Checking value function:
  *  - reads the value of @model at the provided column and row and compares with the expected @set_value
  *  - if @stmt is not NULL, then re-run the statement and compares with @model

Modified: trunk/tests/providers/TYPES_SCHEMA_PostgreSQL.xml
==============================================================================
--- trunk/tests/providers/TYPES_SCHEMA_PostgreSQL.xml	(original)
+++ trunk/tests/providers/TYPES_SCHEMA_PostgreSQL.xml	Thu Sep 25 18:55:33 2008
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <gda_array id="EXPORT" name="Exported Data">
-  <gda_array_field id="FI0" name="short_type_name" title="short_type_name" gdatype="gchararray"/>
-  <gda_array_field id="FI1" name="gtype" title="gtype" gdatype="gchararray"/>
+  <gda_array_field id="FI0" name="short_type_name" title="short_type_name" gdatype="gchararray" nullok="TRUE"/>
+  <gda_array_field id="FI1" name="gtype" title="gtype" gdatype="gchararray" nullok="TRUE"/>
   <gda_array_field id="FI2" name="comments" title="comments" gdatype="gchararray" nullok="TRUE"/>
   <gda_array_field id="FI3" name="synonyms" title="synonyms" gdatype="null" nullok="TRUE"/>
   <gda_array_data>
@@ -72,6 +72,12 @@
       <gda_value isnull="t"/>
     </gda_array_row>
     <gda_array_row>
+      <gda_value>gtsvector</gda_value>
+      <gda_value>gchararray</gda_value>
+      <gda_value>GiST index internal text representation for text search</gda_value>
+      <gda_value isnull="t"/>
+    </gda_array_row>
+    <gda_array_row>
       <gda_value>inet</gda_value>
       <gda_value>gchararray</gda_value>
       <gda_value>IP address/netmask, host address, netmask optional</gda_value>
@@ -156,6 +162,18 @@
       <gda_value isnull="t"/>
     </gda_array_row>
     <gda_array_row>
+      <gda_value>regconfig</gda_value>
+      <gda_value>gchararray</gda_value>
+      <gda_value>registered text search configuration</gda_value>
+      <gda_value isnull="t"/>
+    </gda_array_row>
+    <gda_array_row>
+      <gda_value>regdictionary</gda_value>
+      <gda_value>gchararray</gda_value>
+      <gda_value>registered text search dictionary</gda_value>
+      <gda_value isnull="t"/>
+    </gda_array_row>
+    <gda_array_row>
       <gda_value>reltime</gda_value>
       <gda_value>gchararray</gda_value>
       <gda_value>relative, limited-range time interval (Unix delta time)</gda_value>
@@ -198,6 +216,30 @@
       <gda_value isnull="t"/>
     </gda_array_row>
     <gda_array_row>
+      <gda_value>tsquery</gda_value>
+      <gda_value>gchararray</gda_value>
+      <gda_value>query representation for text search</gda_value>
+      <gda_value isnull="t"/>
+    </gda_array_row>
+    <gda_array_row>
+      <gda_value>tsvector</gda_value>
+      <gda_value>gchararray</gda_value>
+      <gda_value>text representation for text search</gda_value>
+      <gda_value isnull="t"/>
+    </gda_array_row>
+    <gda_array_row>
+      <gda_value>txid_snapshot</gda_value>
+      <gda_value>gchararray</gda_value>
+      <gda_value>txid snapshot</gda_value>
+      <gda_value isnull="t"/>
+    </gda_array_row>
+    <gda_array_row>
+      <gda_value>uuid</gda_value>
+      <gda_value>gchararray</gda_value>
+      <gda_value>UUID datatype</gda_value>
+      <gda_value isnull="t"/>
+    </gda_array_row>
+    <gda_array_row>
       <gda_value>varbit</gda_value>
       <gda_value>gchararray</gda_value>
       <gda_value>variable-length bit string</gda_value>
@@ -209,5 +251,11 @@
       <gda_value>varchar(length), non-blank-padded string, variable storage length</gda_value>
       <gda_value isnull="t"/>
     </gda_array_row>
+    <gda_array_row>
+      <gda_value>xml</gda_value>
+      <gda_value>gchararray</gda_value>
+      <gda_value>XML content</gda_value>
+      <gda_value isnull="t"/>
+    </gda_array_row>
   </gda_array_data>
 </gda_array>

Modified: trunk/tests/providers/TYPES_SCHEMA_SQLite.xml
==============================================================================
--- trunk/tests/providers/TYPES_SCHEMA_SQLite.xml	(original)
+++ trunk/tests/providers/TYPES_SCHEMA_SQLite.xml	Thu Sep 25 18:55:33 2008
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <gda_array id="EXPORT" name="Exported Data">
-  <gda_array_field id="FI0" name="short_type_name" title="short_type_name" gdatype="gchararray"/>
-  <gda_array_field id="FI1" name="gtype" title="gtype" gdatype="gchararray"/>
+  <gda_array_field id="FI0" name="short_type_name" title="short_type_name" gdatype="gchararray" nullok="TRUE"/>
+  <gda_array_field id="FI1" name="gtype" title="gtype" gdatype="gchararray" nullok="TRUE"/>
   <gda_array_field id="FI2" name="comments" title="comments" gdatype="gchararray" nullok="TRUE"/>
   <gda_array_field id="FI3" name="synonyms" title="synonyms" gdatype="gchararray" nullok="TRUE"/>
   <gda_array_data>
@@ -30,12 +30,30 @@
       <gda_value>int</gda_value>
     </gda_array_row>
     <gda_array_row>
+      <gda_value>numeric(4,2)</gda_value>
+      <gda_value>gchararray</gda_value>
+      <gda_value isnull="t"/>
+      <gda_value isnull="t"/>
+    </gda_array_row>
+    <gda_array_row>
+      <gda_value>numeric(5,2)</gda_value>
+      <gda_value>gchararray</gda_value>
+      <gda_value isnull="t"/>
+      <gda_value isnull="t"/>
+    </gda_array_row>
+    <gda_array_row>
       <gda_value>real</gda_value>
       <gda_value>gdouble</gda_value>
       <gda_value>Floating point value, stored as an 8-byte IEEE floating point number</gda_value>
       <gda_value isnull="t"/>
     </gda_array_row>
     <gda_array_row>
+      <gda_value>smallint</gda_value>
+      <gda_value>gint</gda_value>
+      <gda_value isnull="t"/>
+      <gda_value isnull="t"/>
+    </gda_array_row>
+    <gda_array_row>
       <gda_value>text</gda_value>
       <gda_value>string</gda_value>
       <gda_value>Text string, stored using the database encoding</gda_value>

Modified: trunk/tests/providers/check_postgres.c
==============================================================================
--- trunk/tests/providers/check_postgres.c	(original)
+++ trunk/tests/providers/check_postgres.c	Thu Sep 25 18:55:33 2008
@@ -32,6 +32,7 @@
 		number_failed += prov_test_common_check_meta ();
 		number_failed += prov_test_common_load_data ();
 		number_failed += prov_test_common_check_cursor_models ();
+		number_failed += prov_test_common_check_data_select ();
 		number_failed += prov_test_common_clean ();
 	}
 

Modified: trunk/tests/providers/check_sqlite.c
==============================================================================
--- trunk/tests/providers/check_sqlite.c	(original)
+++ trunk/tests/providers/check_sqlite.c	Thu Sep 25 18:55:33 2008
@@ -32,6 +32,7 @@
 		number_failed += prov_test_common_check_meta ();
 		number_failed += prov_test_common_load_data ();
 		number_failed += prov_test_common_check_cursor_models ();
+		number_failed += prov_test_common_check_data_select ();
 		number_failed += prov_test_common_clean ();
 	}
 

Modified: trunk/tests/providers/prov-test-common.c
==============================================================================
--- trunk/tests/providers/prov-test-common.c	(original)
+++ trunk/tests/providers/prov-test-common.c	Thu Sep 25 18:55:33 2008
@@ -2,6 +2,7 @@
 #include <string.h>
 #include "prov-test-common.h"
 #include "prov-test-util.h"
+#include <sql-parser/gda-sql-parser.h>
 #include <sql-parser/gda-sql-statement.h>
 #include "../test-cnc-utils.h"
 
@@ -248,3 +249,118 @@
 
 	return number_failed;	
 }
+
+/*
+ *
+ * Check for the GdaDataModel returned from a SELECT combined with the GDA_STATEMENT_MODEL_ALLOW_NOPARAM
+ * flag
+ *
+ */
+int 
+prov_test_common_check_data_select ()
+{
+	GdaSqlParser *parser = NULL;
+	GdaStatement *stmt = NULL;
+	GdaSet *params = NULL;
+	GError *error = NULL;
+	int number_failed = 0;
+	GdaDataModel *model = NULL;
+	const gchar *remain;
+	GSList *columns;
+	gint i, ncols;
+
+	parser = gda_connection_create_parser (cnc);
+	if (!parser)
+		parser = gda_sql_parser_new ();
+
+	/* create statement */
+	stmt = gda_sql_parser_parse_string (parser, "SELECT * FROM actor WHERE actor_id <= ##theid::gint", 
+					    &remain, &error);
+	if (!stmt) {
+		number_failed ++;
+		goto out;
+	}
+	if (remain) {
+		g_set_error (&error, 0, 0,
+			     "Parsing error, remains: %s", remain);
+		number_failed ++;
+		goto out;
+	}
+	
+	/* get model */
+	if (! (gda_statement_get_parameters (stmt, &params, &error))) {
+		number_failed ++;
+		goto out;
+	}
+
+	model = gda_connection_statement_execute_select_full (cnc, stmt, params, GDA_STATEMENT_MODEL_ALLOW_NOPARAM,
+                                                              NULL, &error);
+	if (!model) {
+		number_failed ++;
+		goto out;
+	}
+
+	if (gda_data_model_get_n_rows (model) != 0) {
+		g_set_error (&error, 0, 0,
+			     "Data model reports %d rows when 0 expected", gda_data_model_get_n_rows (model));
+		number_failed ++;
+		goto out;
+	}
+
+	ncols = gda_data_model_get_n_columns (model);
+	for (columns = NULL, i = 0;
+	     i < ncols; i++) 
+		columns = g_slist_append (columns, gda_data_model_describe_column (model, i));
+
+	/* change param */
+	if (! gda_set_set_holder_value (params, &error, "theid", 9)) {
+                number_failed++;
+                goto out;
+        }
+
+	if (gda_data_model_get_n_rows (model) != 9) {
+		g_set_error (&error, 0, 0,
+			     "Data model reports %d rows when 9 expected", gda_data_model_get_n_rows (model));
+		number_failed ++;
+		goto out;
+	}
+
+	/* chech the columns haven't changed */
+	ncols = gda_data_model_get_n_columns (model);
+	if (i != ncols) {
+		g_set_error (&error, 0, 0,
+			     "Number of columns has changed from %d to %d\n", i, ncols);
+		number_failed ++;
+		goto out;
+	}
+
+	for (i = 0; i < ncols; i++) {
+		if (gda_data_model_describe_column (model, i) != g_slist_nth_data (columns, i)) {
+			g_set_error (&error, 0, 0,
+				     "GdaColumn %d has changed from %p to %p\n", i, 
+				     g_slist_nth_data (columns, i),
+				     gda_data_model_describe_column (model, i));
+			number_failed ++;
+			goto out;
+		}
+	}
+
+ out:
+	if (stmt)
+		g_object_unref (stmt);
+	if (params)
+		g_object_unref (params);
+	if (model)
+		g_object_unref (model);
+	g_object_unref (parser);
+
+#ifdef CHECK_EXTRA_INFO
+	g_print ("GdaDataSelect test resulted in %d error(s)\n", number_failed);
+	if (number_failed != 0) 
+		g_print ("error: %s\n", error && error->message ? error->message : "No detail");
+	if (error)
+		g_error_free (error);
+#endif
+
+	return number_failed;
+}

Modified: trunk/tests/providers/prov-test-common.h
==============================================================================
--- trunk/tests/providers/prov-test-common.h	(original)
+++ trunk/tests/providers/prov-test-common.h	Thu Sep 25 18:55:33 2008
@@ -14,6 +14,7 @@
 int prov_test_common_load_data ();
 int prov_test_common_check_meta ();
 int prov_test_common_check_cursor_models ();
+int prov_test_common_check_data_select ();
 int prov_test_common_clean ();
 
 #endif

Modified: trunk/tools/gda-sql.c
==============================================================================
--- trunk/tools/gda-sql.c	(original)
+++ trunk/tools/gda-sql.c	Thu Sep 25 18:55:33 2008
@@ -1211,7 +1211,7 @@
 		
 		if (g_object_get_data (G_OBJECT (model), "name")) {
 			node = xmlNewChild (table, NULL, BAD_CAST "caption", NULL);
-			xmlNewChild (node, NULL, BAD_CAST "big", g_object_get_data (G_OBJECT (model), "name"));
+			xmlNewTextChild (node, NULL, BAD_CAST "big", g_object_get_data (G_OBJECT (model), "name"));
 		}
 
 		ncols = gda_data_model_get_n_columns (model);
@@ -1221,7 +1221,7 @@
 		for (j = 0; j < ncols; j++) {
 			const gchar *cstr;
 			cstr = gda_data_model_get_column_title (model, j);
-			col_node = xmlNewChild (row_node, NULL, BAD_CAST "th", BAD_CAST cstr);
+			col_node = xmlNewTextChild (row_node, NULL, BAD_CAST "th", BAD_CAST cstr);
 			xmlSetProp (col_node, BAD_CAST "align", BAD_CAST "center");
 		}
 
@@ -1237,7 +1237,7 @@
 				}
 				else {
 					str = gda_value_stringify (value);
-					col_node = xmlNewChild (row_node, NULL, BAD_CAST "td", BAD_CAST str);
+					col_node = xmlNewTextChild (row_node, NULL, BAD_CAST "td", BAD_CAST str);
 					xmlSetProp (col_node, BAD_CAST "align", BAD_CAST "left");
 					g_free (str);
 				}

Modified: trunk/tools/information-schema-doc.c
==============================================================================
--- trunk/tools/information-schema-doc.c	(original)
+++ trunk/tools/information-schema-doc.c	Thu Sep 25 18:55:33 2008
@@ -82,7 +82,7 @@
 				g_free (str);
 
 				str = g_strdup_printf ("%s table", (gchar *) prop);
-				xmlNewChild (snode, NULL, BAD_CAST "title", BAD_CAST str);
+				xmlNewTextChild (snode, NULL, BAD_CAST "title", BAD_CAST str);
 				g_free (str);
 				xmlFree (prop);
 			}
@@ -91,7 +91,7 @@
 
 			prop = xmlGetProp (node, BAD_CAST "descr");
 			if (prop) {
-				xmlNewChild (snode, NULL, BAD_CAST "para", prop);
+				xmlNewTextChild (snode, NULL, BAD_CAST "para", prop);
 				xmlFree (prop);
 			}
 			table = xmlNewChild (snode, NULL, BAD_CAST "para", BAD_CAST "The following table describes the columns:");
@@ -136,7 +136,7 @@
 					if (prop) xmlFree (prop);
 
 					prop = xmlGetProp (child, BAD_CAST "descr");
-					xmlNewChild (row, NULL, BAD_CAST "entry", prop ? prop : BAD_CAST "");
+					xmlNewTextChild (row, NULL, BAD_CAST "entry", prop ? prop : BAD_CAST "");
 					if (prop) xmlFree (prop);
 				}
 				else if (!strcmp ((gchar *) child->name, "fkey")) {
@@ -190,7 +190,7 @@
 							g_string_free (fk_str, TRUE);
 						}
 						xmlNodeAddContent (fkey, BAD_CAST "references ");
-						link = xmlNewChild (fkey, NULL, BAD_CAST "link", prop);
+						link = xmlNewTextChild (fkey, NULL, BAD_CAST "link", prop);
 						str = g_strdup_printf ("is:%s", prop);
 						xmlSetProp (link, BAD_CAST "linkend", BAD_CAST str);
 						g_free (str);
@@ -219,7 +219,7 @@
 				g_free (str);
 
 				str = g_strdup_printf ("%s view", (gchar *) prop);
-				xmlNewChild (snode, NULL, BAD_CAST "title", BAD_CAST str);
+				xmlNewTextChild (snode, NULL, BAD_CAST "title", BAD_CAST str);
 				g_free (str);
 				xmlFree (prop);
 			}
@@ -228,7 +228,7 @@
 
 			prop = xmlGetProp (node, BAD_CAST "descr");
 			if (prop) {
-				xmlNewChild (snode, NULL, BAD_CAST "para", prop);
+				xmlNewTextChild (snode, NULL, BAD_CAST "para", prop);
 				xmlFree (prop);
 			}
 			for (child = node->children; child; child = child->next) {
@@ -237,7 +237,7 @@
 
 					def = xmlNodeGetContent (child);
 					para = xmlNewChild (snode, NULL, BAD_CAST "para", BAD_CAST "Definition is:");
-					para = xmlNewChild (para, NULL, BAD_CAST "programlisting", def);
+					para = xmlNewTextChild (para, NULL, BAD_CAST "programlisting", def);
 					xmlSetProp (para, BAD_CAST "width", BAD_CAST "80");
 					xmlFree (def);
 					break;



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