libgda r3281 - in trunk: . doc/C doc/C/examples doc/C/tmpl libgda libgda/sql-parser providers/postgres samples/SimpleExample
- From: vivien svn gnome org
- To: svn-commits-list gnome org
- Subject: libgda r3281 - in trunk: . doc/C doc/C/examples doc/C/tmpl libgda libgda/sql-parser providers/postgres samples/SimpleExample
- Date: Mon, 12 Jan 2009 19:42:07 +0000 (UTC)
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 <table> (<column_name> [,...]) VALUES (<column_name> = <new_value> [,...]).
*
- * 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 <table> SET <column_name> = <new_value> [,...] WHERE <condition_column_name> = <condition_value>.
*
* 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 <table> WHERE <condition_column_name> = <condition_value>.
*
* 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]