libgda r3281 - in trunk: . doc/C doc/C/examples doc/C/tmpl libgda libgda/sql-parser providers/postgres samples/SimpleExample



Author: vivien
Date: Mon Jan 12 19:42:06 2009
New Revision: 3281
URL: http://svn.gnome.org/viewvc/libgda?rev=3281&view=rev

Log:
2009-01-12  Vivien Malerba <malerba gnome-db org>

	* libgda/sql-parser/gda-statement-struct-parts.c:
	* doc/C: doc. improvements
	* libgda/libgda.symbols:
	* libgda/gda-easy.[ch]: implementation of INSERT/UPDATE/DELETE convenience
	functions (with some API Corrections!)
	* samples/SimpleExample/example.c: use the convenience API
	* libgda/gda-util.c: bug fixed
	* providers/postgres/gda-postgres-meta.c: bug fixed (on partial meta data update)


Added:
   trunk/doc/C/howto-exec.png   (contents, props changed)
   trunk/doc/C/howto-exec.svg   (contents, props changed)
Modified:
   trunk/ChangeLog
   trunk/NEWS
   trunk/doc/C/Makefile.am
   trunk/doc/C/examples/full_example.c
   trunk/doc/C/gettingstarted.xml
   trunk/doc/C/howto.xml
   trunk/doc/C/libgda-4.0-docs.sgml
   trunk/doc/C/libgda-4.0-sections.txt
   trunk/doc/C/tmpl/gda-convenient.sgml
   trunk/libgda/gda-easy.c
   trunk/libgda/gda-easy.h
   trunk/libgda/gda-util.c
   trunk/libgda/libgda.symbols
   trunk/libgda/sql-parser/gda-statement-struct-parts.c
   trunk/providers/postgres/gda-postgres-meta.c
   trunk/samples/SimpleExample/example.c

Modified: trunk/NEWS
==============================================================================
--- trunk/NEWS	(original)
+++ trunk/NEWS	Mon Jan 12 19:42:06 2009
@@ -1,3 +1,10 @@
+libgda 3.99.8, 2009-01-12
+
+ - misc corrections
+ - SQL console: better useability in the web console
+ - SQL console: man page added
+ - Win32 build fixes
+
 libgda 3.99.7, 2008-12-31
 
  - API corrections

Modified: trunk/doc/C/Makefile.am
==============================================================================
--- trunk/doc/C/Makefile.am	(original)
+++ trunk/doc/C/Makefile.am	Mon Jan 12 19:42:06 2009
@@ -53,7 +53,7 @@
 	writable_data_model.png GdaDataModelIter.png \
 	data_validation_holder.png data_validation_proxy.png data_validation_set.png \
 	data_proxy1.png data_proxy2.png data_proxy3.png data_proxy4.png data_proxy5.png \
-	gda-sql-graph.png
+	gda-sql-graph.png howto-exec.png
 
 # Extra options to supply to gtkdoc-fixref
 FIXXREF_OPTIONS=
@@ -66,7 +66,7 @@
 	prov-writing.xml i_s_doc.xml howto.xml gda-sql-manual.xml data_validation.xml data_select.xml \
 	DataModels.svg \
 	architecture.svg parts.svg stmt-unknown.svg stmt-select.svg stmt-insert1.svg stmt-insert2.svg \
-	stmt-update.svg stmt-compound.svg information_schema.svg
+	stmt-update.svg stmt-compound.svg information_schema.svg howto-exec.svg
 
 # Files not to distribute
 # for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types

Modified: trunk/doc/C/examples/full_example.c
==============================================================================
--- trunk/doc/C/examples/full_example.c	(original)
+++ trunk/doc/C/examples/full_example.c	Mon Jan 12 19:42:06 2009
@@ -4,6 +4,10 @@
 GdaConnection *open_connection (void);
 void display_products_contents (GdaConnection *cnc);
 void create_table (GdaConnection *cnc);
+void insert_data (GdaConnection *cnc);
+void update_data (GdaConnection *cnc);
+void delete_data (GdaConnection *cnc);
+
 void run_sql_non_select (GdaConnection *cnc, const gchar *sql);
 
 int
@@ -16,7 +20,16 @@
 	/* open connections */
 	cnc = open_connection ();
 	create_table (cnc);
+
+	insert_data (cnc);
+	display_products_contents (cnc);
+
+	update_data (cnc);
 	display_products_contents (cnc);
+
+	delete_data (cnc);
+	display_products_contents (cnc);
+
         gda_connection_close (cnc);
 
         return 0;
@@ -26,7 +39,7 @@
  * Open a connection to the example.db file
  */
 GdaConnection *
-open_connection (void)
+open_connection ()
 {
         GdaConnection *cnc;
         GError *error = NULL;
@@ -34,7 +47,8 @@
 
 	/* open connection */
         cnc = gda_connection_open_from_string ("SQLite", "DB_DIR=.;DB_NAME=example_db", NULL,
-					       GDA_CONNECTION_OPTIONS_NONE, &error);
+					       GDA_CONNECTION_OPTIONS_NONE,
+					       &error);
         if (!cnc) {
                 g_print ("Could not open connection to SQLite database in example_db.db file: %s\n",
                          error && error->message ? error->message : "No detail");
@@ -60,11 +74,114 @@
 	run_sql_non_select (cnc, "DROP table IF EXISTS products");
         run_sql_non_select (cnc, "CREATE table products (ref string not null primary key, "
                             "name string not null, price real)");
-	run_sql_non_select (cnc, "INSERT INTO products VALUES ('p1', 'chair', 2.0)");
-	run_sql_non_select (cnc, "INSERT INTO products VALUES ('p2', 'table', 5.0)");
-	run_sql_non_select (cnc, "INSERT INTO products VALUES ('p3', 'glass', 1.1)");
 }
 
+/*
+ * Insert some data
+ *
+ * Even though it is possible to use SQL text which includes the values to insert into the
+ * table, it's better to use variables (place holders), or as is done here, convenience functions
+ * to avoid SQL injection problems.
+ */
+void
+insert_data (GdaConnection *cnc)
+{
+	typedef struct {
+		gchar *ref;
+		gchar *name;
+
+		gboolean price_is_null;
+		gfloat price;
+	} RowData;
+	RowData data [] = {
+		{"p1", "chair", FALSE, 2.0},
+		{"p2", "table", FALSE, 5.0},
+		{"p3", "glass", FALSE, 1.1},
+		{"p1000", "???", TRUE, 0.},
+		{"p1001", "???", TRUE, 0.},
+	};
+	gint i;
+
+	gboolean res;
+	GError *error = NULL;
+	GValue *v1, *v2, *v3;
+
+	for (i = 0; i < sizeof (data) / sizeof (RowData); i++) {
+		v1 = gda_value_new_from_string (data[i].ref, G_TYPE_STRING);
+		v2 = gda_value_new_from_string (data[i].name, G_TYPE_STRING);
+		if (data[i].price_is_null)
+			v3 = NULL;
+		else {
+			v3 = gda_value_new (G_TYPE_FLOAT);
+			g_value_set_float (v3, data[i].price);
+		}
+		
+		res = gda_insert_row_into_table (cnc, "products", &error, "ref", v1, "name", v2, "price", v3, NULL);
+
+		if (!res) {
+			g_error ("Could not INSERT data into the 'products' table: %s\n",
+				 error && error->message ? error->message : "No detail");
+		}
+		gda_value_free (v1);
+		gda_value_free (v2);
+		if (v3)
+			gda_value_free (v3);
+	}
+}
+
+/*
+ * Update some data
+ */
+void
+update_data (GdaConnection *cnc)
+{
+	gboolean res;
+	GError *error = NULL;
+	GValue *v1, *v2, *v3;
+
+	/* update data where ref is 'p1000' */
+	v1 = gda_value_new_from_string ("p1000", G_TYPE_STRING);
+	v2 = gda_value_new_from_string ("flowers", G_TYPE_STRING);
+	v3 = gda_value_new (G_TYPE_FLOAT);
+	g_value_set_float (v3, 1.99);
+		
+	res = gda_update_row_in_table (cnc, "products", "ref", v1, &error, "name", v2, "price", v3, NULL);
+
+	if (!res) {
+		g_error ("Could not UPDATE data in the 'products' table: %s\n",
+			 error && error->message ? error->message : "No detail");
+	}
+	gda_value_free (v1);
+	gda_value_free (v2);
+	gda_value_free (v3);
+}
+
+/*
+ * Delete some data
+ */
+void
+delete_data (GdaConnection *cnc)
+{
+	gboolean res;
+	GError *error = NULL;
+	GValue *v;
+
+	/* delete data where name is 'table' */
+	v = gda_value_new_from_string ("table", G_TYPE_STRING);
+	res = gda_delete_row_from_table (cnc, "products", "name", v, &error);
+	if (!res) {
+		g_error ("Could not DELETE data from the 'products' table: %s\n",
+			 error && error->message ? error->message : "No detail");
+	}
+	gda_value_free (v);
+
+	/* delete data where price is NULL */
+	res = gda_delete_row_from_table (cnc, "products", "price", NULL, &error);
+	if (!res) {
+		g_error ("Could not DELETE data from the 'products' table: %s\n",
+			 error && error->message ? error->message : "No detail");
+	}
+}
 
 /* 
  * display the contents of the 'products' table 

Modified: trunk/doc/C/gettingstarted.xml
==============================================================================
--- trunk/doc/C/gettingstarted.xml	(original)
+++ trunk/doc/C/gettingstarted.xml	Mon Jan 12 19:42:06 2009
@@ -321,11 +321,27 @@
       </programlisting>
       and executing should output something like:
       <programlisting>> ./example 
-ref | name  | price   
-----+-------+---------
-p1  | chair | 2.000000
-p2  | table | 5.000000
-p3  | glass | 1.100000
+ref   | name  | price   
+------+-------+---------
+p1    | chair | 2.000000
+p2    | table | 5.000000
+p3    | glass | 1.100000
+p1000 | ???   | NULL    
+p1001 | ???   | NULL    
+(5 rows)
+ref   | name    | price   
+------+---------+---------
+p1    | chair   | 2.000000
+p2    | table   | 5.000000
+p3    | glass   | 1.100000
+p1000 | flowers | 1.990000
+p1001 | ???     | NULL    
+(5 rows)
+ref   | name    | price   
+------+---------+---------
+p1    | chair   | 2.000000
+p3    | glass   | 1.100000
+p1000 | flowers | 1.990000
 (3 rows)
       </programlisting>
     </para>
@@ -418,6 +434,7 @@
 	<listitem><para>in the <filename>tools/</filename> directory where &LIBGDA;'s tools exist and are
 	    also a good starting point</para></listitem>
       </itemizedlist>
+      Finally this documentation includes a <link linkend="howto">HOWTO for common tasks</link> with more code.
     </para>
   </sect1>
 </chapter>

Added: trunk/doc/C/howto-exec.png
==============================================================================
Binary file. No diff available.

Added: trunk/doc/C/howto-exec.svg
==============================================================================
Binary file. No diff available.

Modified: trunk/doc/C/howto.xml
==============================================================================
--- trunk/doc/C/howto.xml	(original)
+++ trunk/doc/C/howto.xml	Mon Jan 12 19:42:06 2009
@@ -243,6 +243,13 @@
 g_object_unref (stmt);
       </programlisting>
     </para>
+    <para>
+      Note that the example above includes in the SQL statement the values of the data to use in the UPDATE
+      (namely 'Joe' and '123'). Even though it's simple, a better practice is to use variables, as it
+      prevents SQL injection and avoids formatting problems. For more intormation, see the 
+      <link linkend="GdaSqlParser.description">GdaSqlParser</link> object's documentation; or the section
+      about <link linkend="libgda-40-Convenience-functions">convenience functions</link>
+    </para>
   </sect1>
 
   <sect1>
@@ -345,8 +352,8 @@
       </programlisting>
     </para>
     <para>
-      Please also note that &LIBGDA; provides some convenient functions to wrap this process, see the
-      <link linkend="libgda-40-Convenient-functions">Convenient functions</link> section for more
+      Please also note that &LIBGDA; provides some convenience functions to wrap this process, see the
+      <link linkend="libgda-40-Convenience-functions">Convenience functions</link> section for more
       information.
     </para>
   </sect1>

Modified: trunk/doc/C/libgda-4.0-docs.sgml
==============================================================================
--- trunk/doc/C/libgda-4.0-docs.sgml	(original)
+++ trunk/doc/C/libgda-4.0-docs.sgml	Mon Jan 12 19:42:06 2009
@@ -191,9 +191,9 @@
         </contrib>
       </author>
     </authorgroup>
-    <date>2008</date>
+    <date>January 2009</date>
     <copyright>
-      <year>1999 - 2008</year>
+      <year>1999 - 2009</year>
       <holder>The GNOME Foundation</holder>
     </copyright>
     <abstract>
@@ -503,6 +503,17 @@
 	&LIBGDA; library allows one to use either a generic SQL parser, or a parser provided by each database adaptator (database
 	provider), through the <link linkend="gda-connection-create-parser">gda_connection_create_parser()</link> method.
       </para>
+      <para>
+	The following schema illustrates how a statement is executed:
+	<mediaobject>
+	  <imageobject role="html">
+	    <imagedata fileref="howto-exec.png" format="PNG"/>
+	  </imageobject>
+	  <textobject>
+	    <phrase>Statement execution illustrated</phrase>
+	  </textobject>
+	</mediaobject>
+      </para>
       &libgda-convenient;
       &libgda-GdaConnection;
       &libgda-GdaSqlParser;
@@ -803,7 +814,7 @@
 	      <imagedata fileref="information_schema.png" format="PNG"/>
 	    </imageobject>
 	    <textobject>
-	      <phrase>Dictioanry's tables and views initially defined by &LIBGDA;</phrase>
+	      <phrase>Dictionary's tables and views initially defined by &LIBGDA;</phrase>
 	    </textobject>
 	  </mediaobject>
 	</para>

Modified: trunk/doc/C/libgda-4.0-sections.txt
==============================================================================
--- trunk/doc/C/libgda-4.0-sections.txt	(original)
+++ trunk/doc/C/libgda-4.0-sections.txt	Mon Jan 12 19:42:06 2009
@@ -925,9 +925,7 @@
 gda_perform_drop_table
 <SUBSECTION>
 gda_insert_row_into_table
-gda_insert_row_into_table_from_string
-gda_update_value_in_table
-gda_update_values_in_table
+gda_update_row_in_table
 gda_delete_row_from_table
 <SUBSECTION>
 gda_get_default_handler

Modified: trunk/doc/C/tmpl/gda-convenient.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-convenient.sgml	(original)
+++ trunk/doc/C/tmpl/gda-convenient.sgml	Mon Jan 12 19:42:06 2009
@@ -132,48 +132,21 @@
 </para>
 
 @cnc: 
- table_name: 
+ table: 
 @error: 
 @Varargs: 
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_insert_row_into_table_from_string ##### -->
+<!-- ##### FUNCTION gda_update_row_in_table ##### -->
 <para>
 
 </para>
 
 @cnc: 
- table_name: 
- error: 
- Varargs: 
- Returns: 
-
-
-<!-- ##### FUNCTION gda_update_value_in_table ##### -->
-<para>
-
-</para>
-
- cnc: 
- table_name: 
- search_for_column: 
- condition: 
- column_name: 
- new_value: 
- error: 
- Returns: 
-
-
-<!-- ##### FUNCTION gda_update_values_in_table ##### -->
-<para>
-
-</para>
-
- cnc: 
- table_name: 
+ table: 
 @condition_column_name: 
- condition: 
+ condition_value: 
 @error: 
 @Varargs: 
 @Returns: 
@@ -185,9 +158,9 @@
 </para>
 
 @cnc: 
- table_name: 
+ table: 
 @condition_column_name: 
- condition: 
+ condition_value: 
 @error: 
 @Returns: 
 

Modified: trunk/libgda/gda-easy.c
==============================================================================
--- trunk/libgda/gda-easy.c	(original)
+++ trunk/libgda/gda-easy.c	Mon Jan 12 19:42:06 2009
@@ -20,8 +20,11 @@
 #include <glib/gi18n-lib.h>
 #include <libgda/gda-easy.h>
 #include <libgda/gda-server-provider.h>
-#include <sql-parser/gda-sql-parser.h>
 #include <libgda/gda-config.h>
+#include <libgda/gda-set.h>
+#include <sql-parser/gda-sql-parser.h>
+#include <sql-parser/gda-sql-statement.h>
+#include <libgda/gda-holder.h>
 
 static GStaticMutex parser_mutex = G_STATIC_MUTEX_INIT;
 static GdaSqlParser *internal_parser = NULL;
@@ -215,7 +218,7 @@
  * @sql: a query statament must begin with "SELECT"
  * @error: a place to store errors, or %NULL
  *
- * This is a convenient function to execute a SQL command over the opened connection.
+ * This is a convenience function to execute a SQL command over the opened connection.
  *
  * Returns: the number of rows affected or -1.
  */
@@ -547,158 +550,372 @@
 	return dh;
 }
 
-/* Auxiliary struct to save and get the varian arguments */
-typedef struct {
-	gchar  *column_name;
-	GValue *value;
-} GdaValueArgument;
-
-/**
- * gda_insert_row_into_table_from_string
- * @cnc: an opened connection
- * @table_name:
- * @error: a place to store errors, or %NULL
- * @...: a list of strings to be converted as value, finished by %NULL
- * 
- * This is just a convenient function to insert a row with the values given as arguments.
- * The values must be strings that could be converted to the type in the corresponding
- * column. Finish the list with NULL.
- * 
- * The arguments must be pairs of column name followed by his value.
- *
- * The SQL command is like: 
- * INSERT INTO table_name (column1, column2, ...) VALUES (value1, value2, ...)
- *
- * Returns: TRUE if no error occurred, and FALSE and set error otherwise
- */
-gboolean
-gda_insert_row_into_table_from_string (GdaConnection *cnc, const gchar *table_name, GError **error, ...)
-{        
-	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
-	g_return_val_if_fail (gda_connection_is_opened (cnc), FALSE);
-	g_return_val_if_fail (table_name && *table_name, FALSE);
-	
-	TO_IMPLEMENT;
-	return FALSE;
-}
-
 /**
  * gda_insert_row_into_table
  * @cnc: an opened connection
- * @table_name:
+ * @table: table's name to insert into
  * @error: a place to store errors, or %NULL
- * @...: a list of string/@GValue pairs where the string is the name of the column
- * followed by its @GValue to set in the insert operation, finished by %NULL
- * 
- * This is just a convenient function to insert a row with the values given as argument.
- * The values must correspond with the GType of the column to set, otherwise throw to 
- * an error. Finish the list with NULL.
+ * @...: a list of string/GValue pairs with the name of the column to use and the
+ * GValue pointer containing the value to insert for the column (value can be %NULL), finished by a %NULL
  * 
- * The arguments must be pairs of column name followed by his value.
+ * This is a convenience function, which creates an INSERT statement and executes it using the values
+ * provided. It internally relies on variables which makes it immune to SQL injection problems.
+ *
+ * The equivalent SQL command is: INSERT INTO &lt;table&gt; (&lt;column_name&gt; [,...]) VALUES (&lt;column_name&gt; = &lt;new_value&gt; [,...]).
  * 
- * Returns: TRUE if no error occurred, and FALSE and set error otherwise
+ * Returns: TRUE if no error occurred
  */
 gboolean
-gda_insert_row_into_table (GdaConnection *cnc, const gchar *table_name, GError **error, ...)
+gda_insert_row_into_table (GdaConnection *cnc, const gchar *table, GError **error, ...)
 {
+	gboolean retval;
+	va_list  args;
+	gchar *col_name;
+	GSList *fields = NULL;
+	GSList *values = NULL;
+	GdaSqlStatement *sql_stm;
+	GdaSqlStatementInsert *ssi;
+	GdaStatement *insert;
+	gint i;
+
+	GSList *holders = NULL;
+	
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
-	g_return_val_if_fail (gda_connection_is_opened (cnc), FALSE);
-	g_return_val_if_fail (table_name && *table_name, FALSE);
+	g_return_val_if_fail (table && *table, FALSE);
+	
+	/* Construct insert query and list of GdaHolders */
+	va_start (args, error);
+	
+	ssi = g_new0 (GdaSqlStatementInsert, 1);
+	GDA_SQL_ANY_PART (ssi)->type = GDA_SQL_ANY_STMT_INSERT;
+	ssi->table = gda_sql_table_new (GDA_SQL_ANY_PART (ssi));
+	if (gda_sql_identifier_needs_quotes (table))
+		ssi->table->table_name = gda_sql_identifier_add_quotes (table);
+	else
+		ssi->table->table_name = g_strdup (table);
 
-	TO_IMPLEMENT;
-	return FALSE;
-}
+	i = 0;
+	while ((col_name = va_arg (args, gchar*))) {
+		GdaSqlField *field;
+		GdaSqlExpr *expr;
+		GValue *value;
+		
+		/* field */
+		field = gda_sql_field_new (GDA_SQL_ANY_PART (ssi));
+		if (gda_sql_identifier_needs_quotes (col_name))
+			field->field_name = gda_sql_identifier_add_quotes (col_name);
+		else
+			field->field_name = g_strdup (col_name);
+		fields = g_slist_prepend (fields, field);
+
+		/* value */
+		value = va_arg (args, GValue *);
+		expr = gda_sql_expr_new (GDA_SQL_ANY_PART (ssi));
+		if (value && (G_VALUE_TYPE (value) != GDA_TYPE_NULL)) {
+			/* create a GdaSqlExpr with a parameter */
+			GdaSqlParamSpec *param;
+			param = g_new0 (GdaSqlParamSpec, 1);
+			param->name = g_strdup_printf ("+%d", i);
+			param->g_type = G_VALUE_TYPE (value);
+			param->is_param = TRUE;
+			expr->param_spec = param;
+
+			GdaHolder *holder;
+			holder = (GdaHolder*)  g_object_new (GDA_TYPE_HOLDER, "g-type", G_VALUE_TYPE (value),
+							     "id", param->name, NULL);
+			g_assert (gda_holder_set_value (holder, value, NULL));
+			holders = g_slist_prepend (holders, holder);
+		}
+		else {
+			/* create a NULL GdaSqlExpr => nothing to do */
+		}
+		values = g_slist_prepend (values, expr);
+		
+		i++;
+	}
+	
+	va_end (args);
 
-/**
- * gda_delete_row_from_table
- * @cnc: an opened connection
- * @table_name:
- * @condition_column_name: the name of the column to used in the WHERE condition clause
- * @condition: a GValue to used to find the row to be deleted 
- * @error: a place to store errors, or %NULL
- *
- * This is just a convenient function to delete the row fitting the given condition
- * from the given table.
- *
- * @condition must be a valid GValue and must correspond with the GType of the column to use
- * in the WHERE clause.
- *
- * The SQL command is like: DELETE FROM table_name WHERE contition_column_name = condition
- *
- * Returns: TRUE if no error occurred, and FALSE and set error otherwise
- */
-gboolean              
-gda_delete_row_from_table (GdaConnection *cnc, const gchar *table_name, const gchar *condition_column_name, 
-			   const GValue *condition, GError **error)
-{
-	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
-	g_return_val_if_fail (gda_connection_is_opened (cnc), FALSE);
-	g_return_val_if_fail (table_name && *table_name, FALSE);
+	ssi->fields_list = g_slist_reverse (fields);
+	ssi->values_list = g_slist_prepend (NULL, g_slist_reverse (values));
+	
+	sql_stm = gda_sql_statement_new (GDA_SQL_STATEMENT_INSERT);
+	sql_stm->contents = ssi;
+	
+	insert = gda_statement_new ();
+	g_object_set (G_OBJECT (insert), "structure", sql_stm, NULL);
+	gda_sql_statement_free (sql_stm);
 
-	TO_IMPLEMENT;
-	return FALSE;
+	/* execute statement */
+	GdaSet *set = NULL;
+	if (holders) {
+		set = gda_set_new (holders);
+		g_slist_foreach (holders, (GFunc) g_object_unref, NULL);
+		g_slist_free (holders);
+	}
+
+	retval = (gda_connection_statement_execute_non_select (cnc, insert, set, NULL, error) == -1) ? FALSE : TRUE;
+
+	if (set)
+		g_object_unref (set);
+	g_object_unref (insert);
+
+	return retval;
 }
 
+
+
 /**
- * gda_update_value_in_table
+ * gda_update_row_in_table
  * @cnc: an opened connection
- * @table_name:
- * @search_for_column: the name of the column to used in the WHERE condition clause
- * @condition: a GValue to used to find the value to be updated; it must correspond with the GType
- * of the column used to search
- * @column_name: the column containing the value to be updated
- * @new_value: the new value to update to; the @GValue must correspond with the GType of the column to update
+ * @table: the table's name with the row's values to be updated
+ * @condition_column_name: the name of the column to used in the WHERE condition clause
+ * @condition_value: the @condition_column_type's GType
  * @error: a place to store errors, or %NULL
+ * ...: a list of string/GValue pairs with the name of the column to use and the
+ * GValue pointer containing the value to update the column to (value can be %NULL), finished by a %NULL
  * 
- * This is just a convenient function to update values in a table on a given column where
- * the row is fitting the given condition.
+ * This is a convenience function, which creates an UPDATE statement and executes it using the values
+ * provided. It internally relies on variables which makes it immune to SQL injection problems.
  *
- * The SQL command is like: UPDATE INTO table_name SET column_name = new_value WHERE search_for_column = condition
+ * The equivalent SQL command is: UPDATE &lt;table&gt; SET &lt;column_name&gt; = &lt;new_value&gt; [,...] WHERE &lt;condition_column_name&gt; = &lt;condition_value&gt;.
  *
  * Returns: TRUE if no error occurred
  */
-gboolean              
-gda_update_value_in_table (GdaConnection *cnc, 
-			   const gchar *table_name, const gchar *search_for_column, 
-			   const GValue *condition, 
-			   const gchar *column_name, const GValue *new_value, GError **error)
-{
+gboolean
+gda_update_row_in_table (GdaConnection *cnc, const gchar *table, 
+			 const gchar *condition_column_name, 
+			 GValue *condition_value, GError **error, ...)
+{
+	gboolean retval;
+	va_list  args;
+	gchar *col_name;
+	GSList *fields = NULL;
+	GSList *values = NULL;
+	GdaSqlStatement *sql_stm;
+	GdaSqlStatementUpdate *ssu;
+	GdaStatement *update;
+	gint i;
+
+	GSList *holders = NULL;
+	
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
-	g_return_val_if_fail (gda_connection_is_opened (cnc), FALSE);
-	g_return_val_if_fail (table_name && *table_name, FALSE);
+	g_return_val_if_fail (table && *table, FALSE);
+	
+	/* Construct insert query and list of GdaHolders */
+	ssu = g_new0 (GdaSqlStatementUpdate, 1);
+	GDA_SQL_ANY_PART (ssu)->type = GDA_SQL_ANY_STMT_UPDATE;
+	ssu->table = gda_sql_table_new (GDA_SQL_ANY_PART (ssu));
+	if (gda_sql_identifier_needs_quotes (table))
+		ssu->table->table_name = gda_sql_identifier_add_quotes (table);
+	else
+		ssu->table->table_name = g_strdup (table);
+
+	if (condition_column_name) {
+		GdaSqlExpr *where, *op;
+		where = gda_sql_expr_new (GDA_SQL_ANY_PART (ssu));
+		ssu->cond = where;
+
+		where->cond = gda_sql_operation_new (GDA_SQL_ANY_PART (where));
+		where->cond->operator_type = GDA_SQL_OPERATOR_TYPE_EQ;
+
+		op = gda_sql_expr_new (GDA_SQL_ANY_PART (where->cond));
+		where->cond->operands = g_slist_prepend (NULL, op);
+		op->value = gda_value_new (G_TYPE_STRING);
+		if (gda_sql_identifier_needs_quotes (condition_column_name))
+			g_value_take_string (op->value, gda_sql_identifier_add_quotes (condition_column_name));
+		else
+			g_value_set_string (op->value, condition_column_name);
+
+		op = gda_sql_expr_new (GDA_SQL_ANY_PART (where->cond));
+		where->cond->operands = g_slist_append (where->cond->operands, op);
+		if (condition_value) {
+			GdaSqlParamSpec *param;
+			param = g_new0 (GdaSqlParamSpec, 1);
+			param->name = g_strdup ("cond");
+			param->g_type = G_VALUE_TYPE (condition_value);
+			param->is_param = TRUE;
+			op->param_spec = param;
+
+			GdaHolder *holder;
+			holder = (GdaHolder*)  g_object_new (GDA_TYPE_HOLDER, "g-type", G_VALUE_TYPE (condition_value),
+							     "id", param->name, NULL);
+			g_assert (gda_holder_set_value (holder, condition_value, NULL));
+			holders = g_slist_prepend (holders, holder);
+		}
+		else {
+			/* nothing to do: NULL */
+		}
+	}
+
+	va_start (args, error);
+	i = 0;
+	while ((col_name = va_arg (args, gchar*))) {
+		GdaSqlField *field;
+		GdaSqlExpr *expr;
+		GValue *value;
+		
+		/* field */
+		field = gda_sql_field_new (GDA_SQL_ANY_PART (ssu));
+		if (gda_sql_identifier_needs_quotes (col_name))
+			field->field_name = gda_sql_identifier_add_quotes (col_name);
+		else
+			field->field_name = g_strdup (col_name);
+		fields = g_slist_prepend (fields, field);
+
+		/* value */
+		value = va_arg (args, GValue *);
+		expr = gda_sql_expr_new (GDA_SQL_ANY_PART (ssu));
+		if (value && (G_VALUE_TYPE (value) != GDA_TYPE_NULL)) {
+			/* create a GdaSqlExpr with a parameter */
+			GdaSqlParamSpec *param;
+			param = g_new0 (GdaSqlParamSpec, 1);
+			param->name = g_strdup_printf ("+%d", i);
+			param->g_type = G_VALUE_TYPE (value);
+			param->is_param = TRUE;
+			expr->param_spec = param;
+
+			GdaHolder *holder;
+			holder = (GdaHolder*)  g_object_new (GDA_TYPE_HOLDER, "g-type", G_VALUE_TYPE (value),
+							     "id", param->name, NULL);
+			g_assert (gda_holder_set_value (holder, value, NULL));
+			holders = g_slist_prepend (holders, holder);
+		}
+		else {
+			/* create a NULL GdaSqlExpr => nothing to do */
+		}
+		values = g_slist_prepend (values, expr);
+		
+		i++;
+	}
+	
+	va_end (args);
+
+	ssu->fields_list = g_slist_reverse (fields);
+	ssu->expr_list = g_slist_reverse (values);
+	
+	sql_stm = gda_sql_statement_new (GDA_SQL_STATEMENT_UPDATE);
+	sql_stm->contents = ssu;
+	
+	update = gda_statement_new ();
+	g_object_set (G_OBJECT (update), "structure", sql_stm, NULL);
+	gda_sql_statement_free (sql_stm);
+
+	/* execute statement */
+	GdaSet *set = NULL;
+	if (holders) {
+		set = gda_set_new (holders);
+		g_slist_foreach (holders, (GFunc) g_object_unref, NULL);
+		g_slist_free (holders);
+	}
 
-	TO_IMPLEMENT;
-	return FALSE;
+	retval = (gda_connection_statement_execute_non_select (cnc, update, set, NULL, error) == -1) ? FALSE : TRUE;
+
+	if (set)
+		g_object_unref (set);
+	g_object_unref (update);
+
+	return retval;
 }
 
 /**
- * gda_update_values_in_table
+ * gda_delete_row_from_table
  * @cnc: an opened connection
- * @table_name: the name of the table where the update will be done
+ * @table: the table's name with the row's values to be updated
  * @condition_column_name: the name of the column to used in the WHERE condition clause
- * @condition: a GValue to used to find the values to be updated; it must correspond with the
- * column's @GType
+ * @condition_value: the @condition_column_type's GType
  * @error: a place to store errors, or %NULL
- * @...: a list of string/@GValue pairs where the string is the name of the column to be 
- * updated followed by the new @GValue to set, finished by %NULL
  * 
- * This is just a convenient function to update values in a table on a given column where
- * the row is fitting the given condition.
+ * This is a convenience function, which creates a DELETE statement and executes it using the values
+ * provided. It internally relies on variables which makes it immune to SQL injection problems.
  *
- * The SQL command is like: 
- * UPDATE INTO table_name SET column1 = new_value1, column2 = new_value2 ... WHERE condition_column_name = condition
+ * The equivalent SQL command is: DELETE FROM &lt;table&gt; WHERE &lt;condition_column_name&gt; = &lt;condition_value&gt;.
  *
  * Returns: TRUE if no error occurred
  */
-gboolean              
-gda_update_values_in_table (GdaConnection *cnc, const gchar *table_name, 
-			    const gchar *condition_column_name, const GValue *condition, 
-			    GError **error, ...)
-{
+gboolean
+gda_delete_row_from_table (GdaConnection *cnc, const gchar *table, 
+			   const gchar *condition_column_name, 
+			   GValue *condition_value, GError **error)
+{
+	gboolean retval;
+	gchar *col_name;
+	GdaSqlStatement *sql_stm;
+	GdaSqlStatementDelete *ssd;
+	GdaStatement *delete;
+
+	GSList *holders = NULL;
+	
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
-	g_return_val_if_fail (gda_connection_is_opened (cnc), FALSE);
-	g_return_val_if_fail (table_name && *table_name, FALSE);
+	g_return_val_if_fail (table && *table, FALSE);
+	
+	/* Construct insert query and list of GdaHolders */
+	ssd = g_new0 (GdaSqlStatementDelete, 1);
+	GDA_SQL_ANY_PART (ssd)->type = GDA_SQL_ANY_STMT_DELETE;
+	ssd->table = gda_sql_table_new (GDA_SQL_ANY_PART (ssd));
+	if (gda_sql_identifier_needs_quotes (table))
+		ssd->table->table_name = gda_sql_identifier_add_quotes (table);
+	else
+		ssd->table->table_name = g_strdup (table);
+
+	if (condition_column_name) {
+		GdaSqlExpr *where, *op;
+		where = gda_sql_expr_new (GDA_SQL_ANY_PART (ssd));
+		ssd->cond = where;
+
+		where->cond = gda_sql_operation_new (GDA_SQL_ANY_PART (where));
+		where->cond->operator_type = GDA_SQL_OPERATOR_TYPE_EQ;
+
+		op = gda_sql_expr_new (GDA_SQL_ANY_PART (where->cond));
+		where->cond->operands = g_slist_prepend (NULL, op);
+		op->value = gda_value_new (G_TYPE_STRING);
+		if (gda_sql_identifier_needs_quotes (condition_column_name))
+			g_value_take_string (op->value, gda_sql_identifier_add_quotes (condition_column_name));
+		else
+			g_value_set_string (op->value, condition_column_name);
+
+		op = gda_sql_expr_new (GDA_SQL_ANY_PART (where->cond));
+		where->cond->operands = g_slist_append (where->cond->operands, op);
+		if (condition_value) {
+			GdaSqlParamSpec *param;
+			param = g_new0 (GdaSqlParamSpec, 1);
+			param->name = g_strdup ("cond");
+			param->g_type = G_VALUE_TYPE (condition_value);
+			param->is_param = TRUE;
+			op->param_spec = param;
+
+			GdaHolder *holder;
+			holder = (GdaHolder*)  g_object_new (GDA_TYPE_HOLDER, "g-type", G_VALUE_TYPE (condition_value),
+							     "id", param->name, NULL);
+			g_assert (gda_holder_set_value (holder, condition_value, NULL));
+			holders = g_slist_prepend (holders, holder);
+		}
+		else {
+			/* nothing to do: NULL */
+		}
+	}
+
+	sql_stm = gda_sql_statement_new (GDA_SQL_STATEMENT_DELETE);
+	sql_stm->contents = ssd;
+	
+	delete = gda_statement_new ();
+	g_object_set (G_OBJECT (delete), "structure", sql_stm, NULL);
+	gda_sql_statement_free (sql_stm);
+
+	/* execute statement */
+	GdaSet *set = NULL;
+	if (holders) {
+		set = gda_set_new (holders);
+		g_slist_foreach (holders, (GFunc) g_object_unref, NULL);
+		g_slist_free (holders);
+	}
+
+	retval = (gda_connection_statement_execute_non_select (cnc, delete, set, NULL, error) == -1) ? FALSE : TRUE;
+
+	if (set)
+		g_object_unref (set);
+	g_object_unref (delete);
 
-	TO_IMPLEMENT;
-	return FALSE;
+	return retval;
 }
+

Modified: trunk/libgda/gda-easy.h
==============================================================================
--- trunk/libgda/gda-easy.h	(original)
+++ trunk/libgda/gda-easy.h	Mon Jan 12 19:42:06 2009
@@ -87,20 +87,13 @@
 /*
  * Data in tables manipulation
  */
-gboolean            gda_insert_row_into_table         (GdaConnection *cnc, const gchar *table_name, GError **error, ...);
-gboolean            gda_insert_row_into_table_from_string  (GdaConnection *cnc, const gchar *table_name, GError **error, ...);
-gboolean            gda_update_value_in_table         (GdaConnection *cnc, const gchar *table_name, 
-						       const gchar *search_for_column, 
-						       const GValue *condition, 
-						       const gchar *column_name, 
-						       const GValue *new_value, GError **error);
-gboolean            gda_update_values_in_table        (GdaConnection *cnc, const gchar *table_name, 
-						       const gchar *condition_column_name, 
-						       const GValue *condition, 
-						       GError **error, ...);
-gboolean            gda_delete_row_from_table         (GdaConnection *cnc, const gchar *table_name, 
-						       const gchar *condition_column_name, const GValue *condition, 
-						       GError **error);
+gboolean            gda_insert_row_into_table        (GdaConnection *cnc, const gchar *table, GError **error, ...);
+gboolean            gda_update_row_in_table          (GdaConnection *cnc, const gchar *table, 
+						      const gchar *condition_column_name, 
+						      GValue *condition_value, GError **error, ...);
+gboolean            gda_delete_row_from_table        (GdaConnection *cnc, const gchar *table, 
+						      const gchar *condition_column_name, 
+						      GValue *condition_value, GError **error);
 
 
 G_END_DECLS

Modified: trunk/libgda/gda-util.c
==============================================================================
--- trunk/libgda/gda-util.c	(original)
+++ trunk/libgda/gda-util.c	Mon Jan 12 19:42:06 2009
@@ -1058,6 +1058,7 @@
 	
 	ust = (GdaSqlStatementUpdate*) upd_stmt->contents;
 	sst = g_new0 (GdaSqlStatementSelect, 1);
+	GDA_SQL_ANY_PART (sst)->type = GDA_SQL_ANY_STMT_SELECT;
 
 	if (!ust->table || !ust->table->table_name) {
 		g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,

Modified: trunk/libgda/libgda.symbols
==============================================================================
--- trunk/libgda/libgda.symbols	(original)
+++ trunk/libgda/libgda.symbols	Mon Jan 12 19:42:06 2009
@@ -380,7 +380,6 @@
 	gda_identifier_hash
 	gda_init
 	gda_insert_row_into_table
-	gda_insert_row_into_table_from_string
 	gda_lang_locale
 	gda_lockable_get_type
 	gda_lockable_lock
@@ -771,8 +770,7 @@
 	gda_transaction_status_get_type
 	gda_transaction_status_new
 	gda_transaction_status_state_get_type
-	gda_update_value_in_table
-	gda_update_values_in_table
+	gda_update_row_in_table
 	gda_ushort_get_type
 	gda_utility_check_data_model
 	gda_utility_data_model_dump_data_to_xml

Modified: trunk/libgda/sql-parser/gda-statement-struct-parts.c
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-parts.c	(original)
+++ trunk/libgda/sql-parser/gda-statement-struct-parts.c	Mon Jan 12 19:42:06 2009
@@ -402,7 +402,7 @@
  * @value: a #GValue holding a string to take from
  * 
  * Sets the table's name using the string holded by @value. When call, @value is freed using
- * #gda_value_free().
+ * gda_value_free().
  *
  */
 void

Modified: trunk/providers/postgres/gda-postgres-meta.c
==============================================================================
--- trunk/providers/postgres/gda-postgres-meta.c	(original)
+++ trunk/providers/postgres/gda-postgres-meta.c	Mon Jan 12 19:42:06 2009
@@ -893,7 +893,7 @@
 		if (type != G_TYPE_STRING) {
 			GValue *v;
 			g_value_set_string (v = gda_value_new (G_TYPE_STRING), g_type_name (type));
-			retval = gda_data_model_set_value_at (proxy, 7, i, v, error);
+			retval = gda_data_model_set_value_at (proxy, 9, i, v, error);
 			gda_value_free (v);
 			if (!retval)
 				break;

Modified: trunk/samples/SimpleExample/example.c
==============================================================================
--- trunk/samples/SimpleExample/example.c	(original)
+++ trunk/samples/SimpleExample/example.c	Mon Jan 12 19:42:06 2009
@@ -4,6 +4,10 @@
 GdaConnection *open_connection (void);
 void display_products_contents (GdaConnection *cnc);
 void create_table (GdaConnection *cnc);
+void insert_data (GdaConnection *cnc);
+void update_data (GdaConnection *cnc);
+void delete_data (GdaConnection *cnc);
+
 void run_sql_non_select (GdaConnection *cnc, const gchar *sql);
 
 int
@@ -16,7 +20,16 @@
 	/* open connections */
 	cnc = open_connection ();
 	create_table (cnc);
+
+	insert_data (cnc);
+	display_products_contents (cnc);
+
+	update_data (cnc);
 	display_products_contents (cnc);
+
+	delete_data (cnc);
+	display_products_contents (cnc);
+
         gda_connection_close (cnc);
 
         return 0;
@@ -61,12 +74,114 @@
 	run_sql_non_select (cnc, "DROP table IF EXISTS products");
         run_sql_non_select (cnc, "CREATE table products (ref string not null primary key, "
                             "name string not null, price real)");
-	run_sql_non_select (cnc, "INSERT INTO products VALUES ('p1', 'chair', 2.0)");
-	run_sql_non_select (cnc, "INSERT INTO products VALUES ('p2', 'table', 5.0)");
+}
+
+/*
+ * Insert some data
+ *
+ * Even though it is possible to use SQL text which includes the values to insert into the
+ * table, it's better to use variables (place holders), or as is done here, convenience functions
+ * to avoid SQL injection problems.
+ */
+void
+insert_data (GdaConnection *cnc)
+{
+	typedef struct {
+		gchar *ref;
+		gchar *name;
+
+		gboolean price_is_null;
+		gfloat price;
+	} RowData;
+	RowData data [] = {
+		{"p1", "chair", FALSE, 2.0},
+		{"p2", "table", FALSE, 5.0},
+		{"p3", "glass", FALSE, 1.1},
+		{"p1000", "???", TRUE, 0.},
+		{"p1001", "???", TRUE, 0.},
+	};
+	gint i;
+
+	gboolean res;
+	GError *error = NULL;
+	GValue *v1, *v2, *v3;
 
-	run_sql_non_select (cnc, "INSERT INTO products VALUES ('p3', 'glass', 1.1)");
+	for (i = 0; i < sizeof (data) / sizeof (RowData); i++) {
+		v1 = gda_value_new_from_string (data[i].ref, G_TYPE_STRING);
+		v2 = gda_value_new_from_string (data[i].name, G_TYPE_STRING);
+		if (data[i].price_is_null)
+			v3 = NULL;
+		else {
+			v3 = gda_value_new (G_TYPE_FLOAT);
+			g_value_set_float (v3, data[i].price);
+		}
+		
+		res = gda_insert_row_into_table (cnc, "products", &error, "ref", v1, "name", v2, "price", v3, NULL);
+
+		if (!res) {
+			g_error ("Could not INSERT data into the 'products' table: %s\n",
+				 error && error->message ? error->message : "No detail");
+		}
+		gda_value_free (v1);
+		gda_value_free (v2);
+		if (v3)
+			gda_value_free (v3);
+	}
 }
 
+/*
+ * Update some data
+ */
+void
+update_data (GdaConnection *cnc)
+{
+	gboolean res;
+	GError *error = NULL;
+	GValue *v1, *v2, *v3;
+
+	/* update data where ref is 'p1000' */
+	v1 = gda_value_new_from_string ("p1000", G_TYPE_STRING);
+	v2 = gda_value_new_from_string ("flowers", G_TYPE_STRING);
+	v3 = gda_value_new (G_TYPE_FLOAT);
+	g_value_set_float (v3, 1.99);
+		
+	res = gda_update_row_in_table (cnc, "products", "ref", v1, &error, "name", v2, "price", v3, NULL);
+
+	if (!res) {
+		g_error ("Could not UPDATE data in the 'products' table: %s\n",
+			 error && error->message ? error->message : "No detail");
+	}
+	gda_value_free (v1);
+	gda_value_free (v2);
+	gda_value_free (v3);
+}
+
+/*
+ * Delete some data
+ */
+void
+delete_data (GdaConnection *cnc)
+{
+	gboolean res;
+	GError *error = NULL;
+	GValue *v;
+
+	/* delete data where name is 'table' */
+	v = gda_value_new_from_string ("table", G_TYPE_STRING);
+	res = gda_delete_row_from_table (cnc, "products", "name", v, &error);
+	if (!res) {
+		g_error ("Could not DELETE data from the 'products' table: %s\n",
+			 error && error->message ? error->message : "No detail");
+	}
+	gda_value_free (v);
+
+	/* delete data where price is NULL */
+	res = gda_delete_row_from_table (cnc, "products", "price", NULL, &error);
+	if (!res) {
+		g_error ("Could not DELETE data from the 'products' table: %s\n",
+			 error && error->message ? error->message : "No detail");
+	}
+}
 
 /* 
  * display the contents of the 'products' table 



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