libgda r3190 - in trunk: . doc/C doc/C/tmpl libgda libgda/providers-support libgda/sql-parser libgda/sqlite providers/mysql providers/postgres providers/skel-implementation/capi tests tests/data-models tests/meta-store
- From: vivien svn gnome org
- To: svn-commits-list gnome org
- Subject: libgda r3190 - in trunk: . doc/C doc/C/tmpl libgda libgda/providers-support libgda/sql-parser libgda/sqlite providers/mysql providers/postgres providers/skel-implementation/capi tests tests/data-models tests/meta-store
- Date: Fri, 15 Aug 2008 19:41:52 +0000 (UTC)
Author: vivien
Date: Fri Aug 15 19:41:51 2008
New Revision: 3190
URL: http://svn.gnome.org/viewvc/libgda?rev=3190&view=rev
Log:
2008-08-15 Vivien Malerba <malerba gnome-db org>
* providers/mysql: updates to the MySQL provider (Carlos Savoretti)
* libgda/gda-set.c:
* libgda/gda-connection.c:
* libgda/sql-parser/gda-statement-struct.c: fixes memory leaks
for bug #545977
* libgda/gda-statement.c:
* libgda/gda-util.c:
* libgda/sql-parser/gda-statement-struct-pspec.[ch]: removed the "type" attribute from
the GdaSqlParamSpec structure as it was redundant with the "g_type" one
* providers/postgres/gda-postgres-provider.c:
* providers/mysql/gda-mysql-provider.c:
* providers/skel-implementation/capi/gda-capi-provider.c:
* libgda/sqlite/gda-sqlite-provider.c: check that parameters are valid when executing
a statement
* libgda/gda-data-model.h: added GdaDataModel error code
* libgda/gda-connection-private.h:
* libgda/gda-connection.c:
* libgda/sqlite/gda-sqlite-provider.c:
* providers/mysql/gda-mysql-provider.c:
* providers/postgres/gda-postgres-provider.c:
* providers/skel-implementation/capi/gda-capi-provider.c:
explicitely declare prepared statements as GdaPStmt objects
instead of generic gpointer
* tests/test-cnc-utils.[ch]: added a function to load data
from a file into a table
* tests/meta-store.c: test corrections due to meta store internal's schema changes
* libgda/providers-support/gda-pmodel.[ch]:
* libgda/sqlite/gda-sqlite-recordset.[ch]:
* providers/mysql/gda-mysql-recordset.[ch]:
* providers/postgres/gda-postgres-recordset.[ch]:
* providers/skel-implementation/capi/gda-capi-recordset.[ch]:
* tests/data-models/Makefile.am:
* tests/data-models/check_pmodel.c: new test for the GdaPModel
data model (the base class for all the data models returned
by providers after a SELECT) for the updatable property
* doc/C:
* misc files: doc updates
Added:
trunk/tests/data-models/check_pmodel.c
Modified:
trunk/ChangeLog
trunk/doc/C/libgda-4.0-sections.txt
trunk/doc/C/tmpl/gda-pmodel.sgml
trunk/doc/C/tmpl/gda-pstmt.sgml
trunk/libgda/gda-connection-private.h
trunk/libgda/gda-connection.c
trunk/libgda/gda-data-model.c
trunk/libgda/gda-data-model.h
trunk/libgda/gda-meta-store.c
trunk/libgda/gda-set.c
trunk/libgda/gda-statement.c
trunk/libgda/gda-util.c
trunk/libgda/gda-value.c
trunk/libgda/providers-support/gda-pmodel.c
trunk/libgda/providers-support/gda-pmodel.h
trunk/libgda/providers-support/gda-pstmt.c
trunk/libgda/providers-support/gda-pstmt.h
trunk/libgda/sql-parser/gda-statement-struct-pspec.c
trunk/libgda/sql-parser/gda-statement-struct-pspec.h
trunk/libgda/sql-parser/gda-statement-struct.c
trunk/libgda/sqlite/gda-sqlite-provider.c
trunk/libgda/sqlite/gda-sqlite-recordset.c
trunk/libgda/sqlite/gda-sqlite-recordset.h
trunk/libgda/sqlite/utils.c
trunk/providers/mysql/gda-mysql-provider.c
trunk/providers/mysql/gda-mysql-recordset.c
trunk/providers/mysql/gda-mysql-recordset.h
trunk/providers/mysql/libmain.c
trunk/providers/postgres/gda-postgres-provider.c
trunk/providers/postgres/gda-postgres-recordset.c
trunk/providers/postgres/gda-postgres-recordset.h
trunk/providers/skel-implementation/capi/gda-capi-provider.c
trunk/providers/skel-implementation/capi/gda-capi-recordset.c
trunk/providers/skel-implementation/capi/gda-capi-recordset.h
trunk/tests/data-models/ (props changed)
trunk/tests/data-models/Makefile.am
trunk/tests/meta-store/common.c
trunk/tests/meta-store/data_routines.csv
trunk/tests/test-cnc-utils.c
trunk/tests/test-cnc-utils.h
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 Fri Aug 15 19:41:51 2008
@@ -1161,6 +1161,7 @@
<SECTION>
<FILE>gda-sql-statement</FILE>
<TITLE>GdaSqlStatement</TITLE>
+<INCLUDE>sql-parser/gda-sql-statement.h</INCLUDE>
GdaSqlStatement
GdaSqlStatementType
gda_sql_statement_new
@@ -1383,8 +1384,8 @@
gda_pmodel_take_row
gda_pmodel_get_stored_row
gda_pmodel_get_connection
-gda_pmodel_set_modification_query
-gda_pmodel_compute_modification_queries
+gda_pmodel_set_modification_statement
+gda_pmodel_compute_modification_statements
<SUBSECTION Standard>
GDA_IS_PMODEL
GDA_PMODEL
@@ -1399,6 +1400,7 @@
<INCLUDE>providers-support/gda-pstmt.h</INCLUDE>
GdaPStmt
gda_pstmt_set_gda_statement
+gda_pstmt_get_gda_statement
gda_pstmt_copy_contents
<SUBSECTION Standard>
GDA_IS_PSTMT
Modified: trunk/doc/C/tmpl/gda-pmodel.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-pmodel.sgml (original)
+++ trunk/doc/C/tmpl/gda-pmodel.sgml Fri Aug 15 19:41:51 2008
@@ -41,7 +41,6 @@
@prep_stmt: SELECT prepared statement for which the execution gave this object
@nb_stored_rows:
@advertized_nrows: initially set to -1, set to a value >= 0 when the number of rows in the data model is known
- cnc:
<!-- ##### STRUCT GdaPModelClass ##### -->
<para>
@@ -85,7 +84,7 @@
@Returns:
-<!-- ##### FUNCTION gda_pmodel_set_modification_query ##### -->
+<!-- ##### FUNCTION gda_pmodel_set_modification_statement ##### -->
<para>
</para>
@@ -96,14 +95,13 @@
@Returns:
-<!-- ##### FUNCTION gda_pmodel_compute_modification_queries ##### -->
+<!-- ##### FUNCTION gda_pmodel_compute_modification_statements ##### -->
<para>
</para>
@model:
- target:
- use_all_fields_if_no_pk:
+ require_pk:
@error:
@Returns:
Modified: trunk/doc/C/tmpl/gda-pstmt.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-pstmt.sgml (original)
+++ trunk/doc/C/tmpl/gda-pstmt.sgml Fri Aug 15 19:41:51 2008
@@ -2,16 +2,32 @@
GdaPStmt
<!-- ##### SECTION Short_Description ##### -->
-Prepared statement base class
+Prepared statement's base class
<!-- ##### SECTION Long_Description ##### -->
<para>
-
+ The #GdaPStmt represents the association between a #GdaStatement statement and a <emphasis>prepared statement</emphasis>
+ which is database dependant and is an in-memory representation of a statement. Using prepared statement has the
+ following advantages:
+ <itemizedlist>
+ <listitem><para>the parsing of the SQL has to be done only once, which improves performances if the statement
+ has to be executed more than once</para></listitem>
+ <listitem><para>if a statement has been prepared, then it means it is syntaxically correct and has been
+ <emphasis>understood</emphasis> by the database's API</para></listitem>
+ <listitem><para>it is possible to use variables in prepared statement which eliminates the risk
+ of SQL code injection</para></listitem>
+ </itemizedlist>
+</para>
+<para>
+ The #GdaPStmt is not intended to be instanciated, but subclassed by database provider's implementation.
+ Once created, the database provider's implementation can decide to associate (for future lookup) to
+ a #GdaStatement object in a connection using
+ <link linkend="gda-connection-add-prepared-statement">gda_connection_add_prepared_statement()</link>.
</para>
<!-- ##### SECTION See_Also ##### -->
<para>
-
+
</para>
<!-- ##### SECTION Stability_Level ##### -->
@@ -23,7 +39,7 @@
</para>
@object: base object
- stmt:
+ priv:
@sql: actual SQL code used for this prepared statement, its memory is freed by the object itself
@param_ids: list of parameters' IDs (as gchar *), the memory is freed by object itself
@ncols: number of columns in the returned data model (if the prepared statement is a SELECT statement)
@@ -39,6 +55,15 @@
@stmt:
+<!-- ##### FUNCTION gda_pstmt_get_gda_statement ##### -->
+<para>
+
+</para>
+
+ pstmt:
+ Returns:
+
+
<!-- ##### FUNCTION gda_pstmt_copy_contents ##### -->
<para>
Modified: trunk/libgda/gda-connection-private.h
==============================================================================
--- trunk/libgda/gda-connection-private.h (original)
+++ trunk/libgda/gda-connection-private.h Fri Aug 15 19:41:51 2008
@@ -24,6 +24,7 @@
#define __GDA_CONNECTION_PRIVATE_H_
#include <libgda/gda-meta-store.h>
+#include <providers-support/gda-pstmt.h>
G_BEGIN_DECLS
@@ -52,9 +53,9 @@
/*
* prepared statements support
*/
-void gda_connection_add_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt, gpointer prepared_stmt);
-void gda_connection_del_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt);
-gpointer gda_connection_get_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt);
+void gda_connection_add_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt, GdaPStmt *prepared_stmt);
+void gda_connection_del_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt);
+GdaPStmt *gda_connection_get_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt);
G_END_DECLS
Modified: trunk/libgda/gda-connection.c
==============================================================================
--- trunk/libgda/gda-connection.c (original)
+++ trunk/libgda/gda-connection.c Fri Aug 15 19:41:51 2008
@@ -264,6 +264,7 @@
if (cnc->priv->events_list) {
g_list_foreach (cnc->priv->events_list, (GFunc) g_object_unref, NULL);
+ g_slist_free (cnc->priv->events_list);
cnc->priv->events_list = NULL;
}
@@ -1269,6 +1270,7 @@
gda_connection_lock ((GdaLockable*) cnc);
if (cnc->priv->events_list != NULL) {
g_list_foreach (cnc->priv->events_list, (GFunc) g_object_unref, NULL);
+ g_list_free (cnc->priv->events_list);
cnc->priv->events_list = NULL;
}
gda_connection_unlock ((GdaLockable*) cnc);
@@ -3694,11 +3696,25 @@
gda_connection_unlock ((GdaLockable*) cnc);
}
+
+/**
+ * gda_connection_add_prepared_statement
+ * @cnc: a #GdaConnection object
+ * @gda_stmt: a #GdaStatement object
+ * @prepared_stmt: a prepared statement object (as a #GdaPStmt object, or more likely a descendant)
+ *
+ * Declares that @prepared_stmt is a prepared statement object associated to @gda_stmt within the connection
+ * (meaning the connection increments the reference counter of @prepared_stmt).
+ *
+ * If @gda_stmt changes or is destroyed, the the association will be lost and the connection will lose the
+ * reference it has on @prepared_stmt.
+ */
void
-gda_connection_add_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt, gpointer prepared_stmt)
+gda_connection_add_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt, GdaPStmt *prepared_stmt)
{
g_return_if_fail (GDA_IS_CONNECTION (cnc));
g_return_if_fail (cnc->priv);
+ g_return_if_fail (GDA_IS_PSTMT (prepared_stmt));
gda_connection_lock ((GdaLockable*) cnc);
@@ -3714,10 +3730,20 @@
gda_connection_unlock ((GdaLockable*) cnc);
}
-gpointer
+/**
+ * gda_connection_get_prepared_statement
+ * @cnc: a #GdaConnection object
+ * @gda_stmt: a #GdaStatement object
+ *
+ * Retreives a pointer to an object representing a prepared statement for @gda_stmt within @cnc. The
+ * association must have been done using gda_connection_add_prepared_statement().
+ *
+ * Returns: the prepared statement, or %NULL if no association exists
+ */
+GdaPStmt *
gda_connection_get_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt)
{
- gpointer retval = NULL;
+ GdaPStmt *retval = NULL;
g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
g_return_val_if_fail (cnc->priv, NULL);
@@ -3730,6 +3756,14 @@
return retval;
}
+/**
+ * gda_connection_del_prepared_statement
+ * @cnc: a #GdaConnection object
+ * @gda_stmt: a #GdaStatement object
+ *
+ * Removes any prepared statement associated to @gda_stmt in @cnc: this undoes what
+ * gda_connection_add_prepared_statement() does.
+ */
void
gda_connection_del_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt)
{
@@ -3738,7 +3772,8 @@
gda_connection_lock ((GdaLockable*) cnc);
g_return_if_fail (GDA_IS_CONNECTION (cnc));
- prepared_stms_stmt_destroyed_cb (gda_stmt, cnc);
+ if (gda_connection_get_prepared_statement (cnc, gda_stmt))
+ prepared_stms_stmt_destroyed_cb (gda_stmt, cnc);
gda_connection_unlock ((GdaLockable*) cnc);
}
Modified: trunk/libgda/gda-data-model.c
==============================================================================
--- trunk/libgda/gda-data-model.c (original)
+++ trunk/libgda/gda-data-model.c Fri Aug 15 19:41:51 2008
@@ -55,6 +55,15 @@
static guint gda_data_model_signals[LAST_SIGNAL];
+/* module error */
+GQuark gda_data_model_error_quark (void)
+{
+ static GQuark quark;
+ if (!quark)
+ quark = g_quark_from_static_string ("gda_data_model_error");
+ return quark;
+}
+
GType
gda_data_model_get_type (void)
{
@@ -552,7 +561,8 @@
* occur if you do so). Also that value may become invalid as soon as any Libgda part is executed again,
* which means if you want to keep the value, a copy must be made.
*
- * If you want to modify a value stored in a #GdaDataModel, use the gda_data_model_set_value() method.
+ * If you want to modify a value stored in a #GdaDataModel, use the gda_data_model_set_value_at() or
+ * gda_data_model_set_values() methods.
*
* Returns: a #GValue containing the value stored in the given
* position, or %NULL on error (out-of-bound position, etc).
@@ -939,7 +949,8 @@
* @model: a #GdaDataModel object.
* @error: a place to store errors, or %NULL
*
- * Appends a row to the data model.
+ * Appends a row to the data model (the new row will possibliy have NULL values for all columns,
+ * or some other values depending on the data model implementation)
*
* Returns: the number of the added row, or -1 if an error occurred
*/
Modified: trunk/libgda/gda-data-model.h
==============================================================================
--- trunk/libgda/gda-data-model.h (original)
+++ trunk/libgda/gda-data-model.h Fri Aug 15 19:41:51 2008
@@ -39,6 +39,10 @@
#define GDA_IS_DATA_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_DATA_MODEL))
#define GDA_DATA_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GDA_TYPE_DATA_MODEL, GdaDataModelClass))
+/* error reporting */
+extern GQuark gda_data_model_error_quark (void);
+#define GDA_DATA_MODEL_ERROR gda_data_model_error_quark ()
+
typedef enum {
GDA_DATA_MODEL_ACCESS_RANDOM = 1 << 0,
GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD = 1 << 1,
Modified: trunk/libgda/gda-meta-store.c
==============================================================================
--- trunk/libgda/gda-meta-store.c (original)
+++ trunk/libgda/gda-meta-store.c Fri Aug 15 19:41:51 2008
@@ -1316,7 +1316,6 @@
ptype = ctype ? gda_g_type_from_string ((gchar *) ctype) : G_TYPE_STRING;
pspec->name = g_strdup_printf ("+%d", colindex);
pspec->g_type = ptype;
- pspec->type = g_strdup (ctype ? (gchar *) ctype : "string");
pspec->nullok = nullok;
expr = gda_sql_expr_new (GDA_SQL_ANY_PART (ist));
expr->param_spec = pspec;
@@ -1325,7 +1324,6 @@
pspec = g_new0 (GdaSqlParamSpec, 1);
pspec->name = g_strdup_printf ("+%d", colindex);
pspec->g_type = ptype;
- pspec->type = g_strdup (ctype ? (gchar *) ctype : "string");
pspec->nullok = nullok;
expr = gda_sql_expr_new (GDA_SQL_ANY_PART (ust));
expr->param_spec = pspec;
@@ -1589,7 +1587,6 @@
pspec = g_new0 (GdaSqlParamSpec, 1);
pspec->name = g_strdup_printf ("-%d", index);
pspec->g_type = ptype;
- pspec->type = g_strdup (type ? (gchar *) type : "string");
pspec->nullok = nullok;
expr = gda_sql_expr_new (GDA_SQL_ANY_PART (op));
expr->param_spec = pspec;
Modified: trunk/libgda/gda-set.c
==============================================================================
--- trunk/libgda/gda-set.c (original)
+++ trunk/libgda/gda-set.c Fri Aug 15 19:41:51 2008
@@ -312,6 +312,7 @@
copy = g_object_new (GDA_TYPE_SET, "holders", holders, NULL);
g_slist_foreach (holders, (GFunc) g_object_unref, NULL);
+ g_slist_free (holders);
return copy;
}
Modified: trunk/libgda/gda-statement.c
==============================================================================
--- trunk/libgda/gda-statement.c (original)
+++ trunk/libgda/gda-statement.c Fri Aug 15 19:41:51 2008
@@ -477,8 +477,6 @@
(pspec = ((GdaSqlExpr*) node)->param_spec)) {
GdaHolder *h;
- if ((pspec->g_type == 0) && pspec->type)
- pspec->g_type = gda_g_type_from_string (pspec->type);
if (pspec->g_type == 0) {
g_set_error (error, GDA_STATEMENT_ERROR, GDA_STATEMENT_PARAM_TYPE_ERROR,
_("Could not determine GType for parameter '%s'"),
@@ -1246,13 +1244,13 @@
g_string_append (string, " /* ");
g_string_append_printf (string, "name:%s", quoted_pname);
- if (pspec->type) {
- str = _add_quotes (pspec->type);
+ if (pspec->g_type) {
+ str = _add_quotes (gda_g_type_to_string (pspec->g_type));
g_string_append_printf (string, " type:%s", str);
g_free (str);
}
if (pspec->descr) {
- str = _add_quotes (pspec->type);
+ str = _add_quotes (pspec->descr);
g_string_append_printf (string, " descr:%s", str);
g_free (str);
}
@@ -1264,9 +1262,9 @@
else {
g_string_append (string, "##");
g_string_append (string, pspec->name);
- if (pspec->type) {
+ if (pspec->g_type != G_TYPE_INVALID) {
g_string_append (string, "::");
- g_string_append (string, pspec->type);
+ g_string_append (string, gda_g_type_to_string (pspec->g_type));
if (pspec->nullok)
g_string_append (string, "::NULL");
}
Modified: trunk/libgda/gda-util.c
==============================================================================
--- trunk/libgda/gda-util.c (original)
+++ trunk/libgda/gda-util.c Fri Aug 15 19:41:51 2008
@@ -714,7 +714,6 @@
pspec = g_new0 (GdaSqlParamSpec, 1);
pspec->name = g_strdup_printf ("-%d", mtable->pk_cols_array[i]);
pspec->g_type = tcol->gtype != G_TYPE_INVALID ? tcol->gtype: G_TYPE_STRING;
- pspec->type = g_strdup (gda_g_type_to_string (pspec->g_type));
pspec->nullok = tcol->nullok;
opexpr->param_spec = pspec;
op->operands = g_slist_append (op->operands, opexpr);
@@ -851,7 +850,6 @@
GdaSqlParamSpec *pspec = g_new0 (GdaSqlParamSpec, 1);
pspec->name = g_strdup_printf ("+%d", colindex);
pspec->g_type = tcol->gtype != G_TYPE_INVALID ? tcol->gtype: G_TYPE_STRING;
- pspec->type = g_strdup (gda_g_type_to_string (pspec->g_type));
pspec->nullok = tcol->nullok;
expr = gda_sql_expr_new (GDA_SQL_ANY_PART (ist));
expr->param_spec = pspec;
@@ -861,7 +859,6 @@
GdaSqlParamSpec *pspec = g_new0 (GdaSqlParamSpec, 1);
pspec->name = g_strdup_printf ("+%d", colindex);
pspec->g_type = tcol->gtype != G_TYPE_INVALID ? tcol->gtype: G_TYPE_STRING;
- pspec->type = g_strdup (gda_g_type_to_string (pspec->g_type));
pspec->nullok = tcol->nullok;
expr = gda_sql_expr_new (GDA_SQL_ANY_PART (ust));
expr->param_spec = pspec;
Modified: trunk/libgda/gda-value.c
==============================================================================
--- trunk/libgda/gda-value.c (original)
+++ trunk/libgda/gda-value.c Fri Aug 15 19:41:51 2008
@@ -1834,7 +1834,7 @@
* @value1: a #GValue to compare.
* @value2: the other #GValue to be compared to @value1.
*
- * Tells if two values are equal or not
+ * Tells if two values are equal or not, by comparing memory representations.
*
* Returns: 0 if @value1 and @value2 are equal, and something else otherwise
*/
Modified: trunk/libgda/providers-support/gda-pmodel.c
==============================================================================
--- trunk/libgda/providers-support/gda-pmodel.c (original)
+++ trunk/libgda/providers-support/gda-pmodel.c Fri Aug 15 19:41:51 2008
@@ -31,23 +31,26 @@
#include <libgda/gda-statement.h>
#include <libgda/gda-holder.h>
#include <libgda/gda-connection.h>
+#include <libgda/gda-util.h>
+#include <sql-parser/gda-sql-statement.h>
#define CLASS(x) (GDA_PMODEL_CLASS (G_OBJECT_GET_CLASS (x)))
-enum
+typedef enum
{
FIRST_QUERY = 0,
INS_QUERY = 0,
UPD_QUERY = 1,
DEL_QUERY = 2,
NB_QUERIES = 3
-};
+} ModType;
/*
* Getting a GdaRow from a model row:
* model row ==(model->index)==> model->rows index ==(model->rows)==> GdaRow
*/
struct _GdaPModelPrivate {
+ GdaConnection *cnc;
GSList *columns; /* list of GdaColumn objects */
GArray *rows; /* Array of GdaRow pointers */
GHashTable *index; /* key = model row number + 1, value = index in @rows array + 1*/
@@ -58,7 +61,11 @@
GdaDataModelAccessFlags usage_flags;
+ GdaSet *exec_set; /* owned by this object (copied) */
+ GdaSet *modif_set; /* owned by this object */
GdaStatement *modif_stmts[NB_QUERIES];
+
+ GdaStatement **upd_stmts; /* one stmt per data model column */
};
/* properties */
@@ -69,11 +76,21 @@
PROP_PREP_STMT,
PROP_FLAGS,
PROP_ALL_STORED,
+ PROP_PARAMS,
PROP_INS_QUERY,
PROP_UPD_QUERY,
PROP_DEL_QUERY
};
+/* module error */
+GQuark gda_pmodel_error_quark (void)
+{
+ static GQuark quark;
+ if (!quark)
+ quark = g_quark_from_static_string ("gda_pmodel_error");
+ return quark;
+}
+
static void gda_pmodel_class_init (GdaPModelClass *klass);
static void gda_pmodel_init (GdaPModel *model, GdaPModelClass *klass);
static void gda_pmodel_dispose (GObject *object);
@@ -102,6 +119,13 @@
static gboolean gda_pmodel_iter_prev (GdaDataModel *model, GdaDataModelIter *iter);
static gboolean gda_pmodel_iter_at_row (GdaDataModel *model, GdaDataModelIter *iter, gint row);
+static gboolean gda_pmodel_set_value_at (GdaDataModel *model, gint col, gint row,
+ const GValue *value, GError **error);
+static gboolean gda_pmodel_set_values (GdaDataModel *model, gint row, GList *values,
+ GError **error);
+static gint gda_pmodel_append_values (GdaDataModel *model, const GList *values, GError **error);
+static gboolean gda_pmodel_remove_row (GdaDataModel *model, gint row, GError **error);
+
static GObjectClass *parent_class = NULL;
/**
@@ -136,7 +160,7 @@
g_static_mutex_lock (®istering);
if (type == 0) {
- type = g_type_register_static (G_TYPE_OBJECT, "GdaPModel", &info, 0);
+ type = g_type_register_static (G_TYPE_OBJECT, "GdaPModel", &info, G_TYPE_FLAG_ABSTRACT);
g_type_add_interface_static (type, GDA_TYPE_DATA_MODEL, &data_model_info);
}
g_static_mutex_unlock (®istering);
@@ -171,6 +195,11 @@
g_param_spec_boolean ("store-all-rows", "Store all the rows",
"Tells if model has analysed all the rows", FALSE,
G_PARAM_READABLE | G_PARAM_WRITABLE));
+ g_object_class_install_property (object_class, PROP_PARAMS,
+ g_param_spec_object ("exec-params", NULL,
+ _("GdaSet used when the SELECT statement was executed"),
+ GDA_TYPE_SET,
+ G_PARAM_WRITABLE | G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_INS_QUERY,
g_param_spec_object ("insert_query", "INSERT query",
"INSERT Query to be executed to add data",
@@ -209,11 +238,11 @@
iface->i_iter_next = gda_pmodel_iter_next;
iface->i_iter_prev = gda_pmodel_iter_prev;
- iface->i_set_value_at = NULL;
- iface->i_set_values = NULL;
- iface->i_append_values = NULL;
+ iface->i_set_value_at = gda_pmodel_set_value_at;
+ iface->i_set_values = gda_pmodel_set_values;
+ iface->i_append_values = gda_pmodel_append_values;
iface->i_append_row = NULL;
- iface->i_remove_row = NULL;
+ iface->i_remove_row = gda_pmodel_remove_row;
iface->i_find_row = NULL;
iface->i_set_notify = NULL;
@@ -226,7 +255,7 @@
{
g_return_if_fail (GDA_IS_PMODEL (model));
model->priv = g_new0 (GdaPModelPrivate, 1);
- model->cnc = NULL;
+ model->priv->cnc = NULL;
model->priv->rows = g_array_new (FALSE, FALSE, sizeof (GdaRow *));
model->priv->index = g_hash_table_new (g_direct_hash, g_direct_equal);
model->prep_stmt = NULL;
@@ -235,6 +264,10 @@
model->priv->iter_row = G_MININT;
model->priv->iter = NULL;
+
+ model->priv->modif_set = NULL;
+ model->priv->exec_set = NULL;
+ model->priv->upd_stmts = NULL;
}
static void
@@ -247,9 +280,10 @@
/* free memory */
if (model->priv) {
gint i;
- if (model->cnc) {
- g_object_unref (model->cnc);
- model->cnc = NULL;
+
+ if (model->priv->cnc) {
+ g_object_unref (model->priv->cnc);
+ model->priv->cnc = NULL;
}
if (model->prep_stmt) {
g_object_unref (model->prep_stmt);
@@ -274,12 +308,30 @@
model->priv->columns = NULL;
}
+ if (model->priv->modif_set) {
+ g_object_unref (model->priv->modif_set);
+ model->priv->modif_set = NULL;
+ }
+
+ if (model->priv->exec_set) {
+ g_object_unref (model->priv->exec_set);
+ model->priv->exec_set = NULL;
+ }
+
for (i = FIRST_QUERY; i < NB_QUERIES; i++) {
if (model->priv->modif_stmts [i]) {
g_object_unref (model->priv->modif_stmts [i]);
model->priv->modif_stmts [i] = NULL;
}
}
+ if (model->priv->upd_stmts) {
+ gint j, ncols;
+ ncols = gda_pmodel_get_n_columns ((GdaDataModel *) model);
+ for (j = 0; j < ncols; j++)
+ if (model->priv->upd_stmts [j])
+ g_object_unref (model->priv->upd_stmts [j]);
+ g_free (model->priv->upd_stmts);
+ }
}
/* chain to parent class */
@@ -346,9 +398,9 @@
if (model->priv) {
switch (param_id) {
case PROP_CNC:
- model->cnc = g_value_get_object (value);
- if (model->cnc)
- g_object_ref (model->cnc);
+ model->priv->cnc = g_value_get_object (value);
+ if (model->priv->cnc)
+ g_object_ref (model->priv->cnc);
break;
case PROP_PREP_STMT:
if (model->prep_stmt)
@@ -375,6 +427,13 @@
CLASS (model)->store_all (model, NULL);
}
break;
+ case PROP_PARAMS: {
+ GdaSet *set;
+ set = g_value_get_object (value);
+ if (set)
+ model->priv->exec_set = gda_set_copy (set);
+ break;
+ }
case PROP_INS_QUERY:
if (model->priv->modif_stmts [INS_QUERY])
g_object_unref (model->priv->modif_stmts [INS_QUERY]);
@@ -412,7 +471,7 @@
if (model->priv) {
switch (param_id) {
case PROP_CNC:
- g_value_set_object (value, model->cnc);
+ g_value_set_object (value, model->priv->cnc);
break;
case PROP_PREP_STMT:
g_value_set_object (value, model->prep_stmt);
@@ -429,6 +488,9 @@
g_value_set_boolean (value, model->nb_stored_rows == model->advertized_nrows);
}
break;
+ case PROP_PARAMS:
+ g_value_set_object (value, model->priv->exec_set);
+ break;
case PROP_INS_QUERY:
g_value_set_object (value, model->priv->modif_stmts [INS_QUERY]);
break;
@@ -505,7 +567,78 @@
g_return_val_if_fail (GDA_IS_PMODEL (model), NULL);
g_return_val_if_fail (model->priv, NULL);
- return model->cnc;
+ return model->priv->cnc;
+}
+
+/*
+ * Add the +/-<col num> holders to model->priv->modif_set
+ */
+static gboolean
+compute_modif_set (GdaPModel *model, GError **error)
+{
+ gint i;
+
+ if (model->priv->modif_set)
+ g_object_unref (model->priv->modif_set);
+ if (model->priv->exec_set)
+ model->priv->modif_set = gda_set_copy (model->priv->exec_set);
+ else
+ model->priv->modif_set = gda_set_new (NULL);
+
+ for (i = 0; i < NB_QUERIES; i++) {
+ GdaSet *set;
+ if (! model->priv->modif_stmts [i])
+ continue;
+ if (! gda_statement_get_parameters (model->priv->modif_stmts [i], &set, error)) {
+ g_object_unref (model->priv->modif_set);
+ model->priv->modif_set = NULL;
+ return FALSE;
+ }
+
+ gda_set_merge_with_set (model->priv->modif_set, set);
+ g_object_unref (set);
+ }
+
+#ifdef GDA_DEBUG_NO
+ GSList *list;
+ g_print ("-------\n");
+ for (list = model->priv->modif_set->holders; list; list = list->next) {
+ GdaHolder *h = GDA_HOLDER (list->data);
+ g_print ("=> holder '%s'\n", gda_holder_get_id (h));
+ }
+#endif
+
+ return TRUE;
+}
+
+/*
+ * converts "[+-]<num>" to <num> and returns FALSE if @pname is not like
+ * "[+-]<num>". <num> is stored in @result, and the +/- signed is stored in @old_val
+ * (@old_val is set to TRUE if there is a "-")
+ */
+static gboolean
+param_name_to_int (const gchar *pname, gint *result, gboolean *old_val)
+{
+ gint sum = 0;
+ const gchar *ptr;
+
+ if (!pname || ((*pname != '-') && (*pname != '+')))
+ return FALSE;
+
+ ptr = pname + 1;
+ while (*ptr) {
+ if ((*ptr > '9') || (*ptr < '0'))
+ return FALSE;
+ sum = sum * 10 + *ptr - '0';
+ ptr++;
+ }
+
+ if (result)
+ *result = sum;
+ if (old_val)
+ *old_val = (*pname == '-') ? TRUE : FALSE;
+
+ return TRUE;
}
/**
@@ -520,39 +653,158 @@
* Returns: TRUE if no error occurred.
*/
gboolean
-gda_pmodel_set_modification_query (GdaPModel *model, GdaStatement *mod_stmt, GError **error)
+gda_pmodel_set_modification_statement (GdaPModel *model, GdaStatement *mod_stmt, GError **error)
{
+ ModType mtype = NB_QUERIES;
+
g_return_val_if_fail (GDA_IS_PMODEL (model), FALSE);
g_return_val_if_fail (model->priv, FALSE);
g_return_val_if_fail (GDA_IS_STATEMENT (mod_stmt), FALSE);
- TO_IMPLEMENT;
+ switch (gda_statement_get_statement_type (mod_stmt)) {
+ case GDA_SQL_STATEMENT_INSERT:
+ mtype = INS_QUERY;
+ break;
+ case GDA_SQL_STATEMENT_DELETE:
+ mtype = DEL_QUERY;
+ break;
+ case GDA_SQL_STATEMENT_UPDATE:
+ mtype = UPD_QUERY;
+ break;
+ default:
+ break;
+ }
+
+ if (mtype != NB_QUERIES) {
+ if (! gda_sql_statement_check_structure (mod_stmt, error))
+ return FALSE;
- return FALSE;
+ if (model->priv->modif_stmts[mtype]) {
+ g_object_unref (model->priv->modif_stmts[mtype]);
+ model->priv->modif_stmts[mtype] = NULL;
+ }
+
+ /* prepare model->priv->modif_set */
+ if (!compute_modif_set (model, error))
+ return FALSE;
+
+ /* check that all the parameters required to execute @mod_stmt are in model->priv->modif_set */
+ GdaSet *params;
+ GSList *list;
+ if (! gda_statement_get_parameters (mod_stmt, ¶ms, error))
+ return FALSE;
+ for (list = params->holders; list; list = list->next) {
+ GdaHolder *holder = GDA_HOLDER (list->data);
+ GdaHolder *eholder;
+ eholder = gda_set_get_holder (model->priv->modif_set, gda_holder_get_id (holder));
+ if (!eholder) {
+ gint num;
+ gboolean is_old;
+
+ if (!param_name_to_int (gda_holder_get_id (holder), &num, &is_old)) {
+ g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MODIFICATION_STATEMENT_ERROR,
+ _("Modification statement uses an unknown '%s' parameter"),
+ gda_holder_get_id (holder));
+ g_object_unref (params);
+ return FALSE;
+ }
+ if (num > gda_pmodel_get_n_columns ((GdaDataModel*) model)) {
+ g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MISSING_MODIFICATION_STATEMENT_ERROR,
+ _("Column %d out of range (0-%d)"), num,
+ gda_pmodel_get_n_columns ((GdaDataModel*) model)-1);
+ g_object_unref (params);
+ return FALSE;
+ }
+ gda_set_add_holder (model->priv->modif_set, holder);
+ }
+ else if (gda_holder_get_g_type (holder) != gda_holder_get_g_type (eholder)) {
+ g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MODIFICATION_STATEMENT_ERROR,
+ _("Modification statement's '%s' parameter is a %s when it should be a %s"),
+ gda_holder_get_id (holder),
+ gda_g_type_to_string (gda_holder_get_g_type (holder)),
+ gda_g_type_to_string (gda_holder_get_g_type (eholder)));
+ g_object_unref (params);
+ return FALSE;
+ }
+ }
+ g_object_unref (params);
+
+ model->priv->modif_stmts[mtype] = mod_stmt;
+ g_object_ref (mod_stmt);
+ }
+ else {
+ g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MODIFICATION_STATEMENT_ERROR,
+ _("Modification statement must be an INSERT, UPDATE or DELETE statement"));
+ return FALSE;
+ }
+
+#ifdef GDA_DEBUG_NO
+ GSList *hlist;
+ g_print ("SET MODIF QUERY\n");
+ if (model->priv->modif_set) {
+ for (hlist = model->priv->modif_set->holders; hlist; hlist = hlist->next) {
+ GdaHolder *h = GDA_HOLDER (hlist->data);
+ g_print (" %s type=> %s (%d)\n", gda_holder_get_id (h), g_type_name (gda_holder_get_g_type (h)),
+ gda_holder_get_g_type (h));
+ }
+ }
+#endif
+
+ return TRUE;
}
/**
* gda_pmodel_compute_modification_queries
* @model: a #GdaPModel data model
- * @target: the name of the target to modify (a table name or alias)
- * @use_all_fields_if_no_pk: set to TRUE if all fields must be used in the WHERE condition when no primary key exists
+ * @require_pk: FALSE if all fields can be used in the WHERE condition when no primary key exists
* @error: a place to store errors, or %NULL
*
- * Makes @model try to compute INSERT, UPDATE and DELETE statements to be used when modifying @model's contents
+ * Makes @model try to compute INSERT, UPDATE and DELETE statements to be used when modifying @model's contents.
+ * Note: any modification statement set using gda_pmodel_set_modification_statement() will first be unset
*
- * Returns: TRUE if no error occurred.
+ * Returns: TRUE if no error occurred. If FALSE is returned, then some modification statement may still have been
+ * computed
*/
gboolean
-gda_pmodel_compute_modification_queries (GdaPModel *model, const gchar *target, gboolean use_all_fields_if_no_pk, GError **error)
+gda_pmodel_compute_modification_statements (GdaPModel *model, gboolean require_pk, GError **error)
{
+ GdaStatement *stmt;
+ ModType mtype;
g_return_val_if_fail (GDA_IS_PMODEL (model), FALSE);
g_return_val_if_fail (model->priv, FALSE);
- TO_IMPLEMENT;
-
- return FALSE;
+ stmt = gda_pstmt_get_gda_statement (model->prep_stmt);
+ if (!stmt) {
+ g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MISSING_MODIFICATION_STATEMENT_ERROR,
+ _("No SELECT statement to use"));
+ return FALSE;
+ }
+ if (!model->priv->cnc) {
+ g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_CONNECTION_ERROR,
+ _("No connection to use"));
+ return FALSE;
+ }
+ for (mtype = FIRST_QUERY; mtype < NB_QUERIES; mtype++)
+ if (model->priv->modif_stmts[mtype]) {
+ g_object_unref (model->priv->modif_stmts[mtype]);
+ model->priv->modif_stmts[mtype] = NULL;
+ }
+ return gda_compute_dml_statements (model->priv->cnc, stmt, require_pk,
+ &(model->priv->modif_stmts[INS_QUERY]),
+ &(model->priv->modif_stmts[UPD_QUERY]),
+ &(model->priv->modif_stmts[DEL_QUERY]), error);
+ if (!compute_modif_set (model, error)) {
+ for (mtype = FIRST_QUERY; mtype < NB_QUERIES; mtype++)
+ if (model->priv->modif_stmts[mtype]) {
+ g_object_unref (model->priv->modif_stmts[mtype]);
+ model->priv->modif_stmts[mtype] = NULL;
+ }
+ return FALSE;
+ }
+ return TRUE;
}
+
/*
* GdaDataModel interface implementation
*/
@@ -601,18 +853,29 @@
gda_pmodel_get_access_flags (GdaDataModel *model)
{
GdaPModel *imodel;
+ GdaDataModelAccessFlags flags = 0;
+
g_return_val_if_fail (GDA_IS_PMODEL (model), 0);
imodel = GDA_PMODEL (model);
g_return_val_if_fail (imodel->priv, 0);
if (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
- return GDA_DATA_MODEL_ACCESS_RANDOM;
+ flags = GDA_DATA_MODEL_ACCESS_RANDOM;
else if (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD) {
if (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD)
- return GDA_DATA_MODEL_ACCESS_CURSOR;
- return GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
+ flags = GDA_DATA_MODEL_ACCESS_CURSOR;
+ else
+ flags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
}
- g_assert_not_reached ();
+
+ if (imodel->priv->modif_stmts [UPD_QUERY])
+ flags |= GDA_DATA_MODEL_ACCESS_UPDATE;
+ if (imodel->priv->modif_stmts [INS_QUERY])
+ flags |= GDA_DATA_MODEL_ACCESS_INSERT;
+ if (imodel->priv->modif_stmts [DEL_QUERY])
+ flags |= GDA_DATA_MODEL_ACCESS_DELETE;
+
+ return flags;
}
static const GValue *
@@ -660,8 +923,8 @@
imodel = (GdaPModel *) model;
g_return_val_if_fail (imodel->priv, 0);
- /*FIXME: depending on modification queries being set or not */
- flags = GDA_VALUE_ATTR_NO_MODIF;
+ if (! imodel->priv->modif_stmts [UPD_QUERY])
+ flags = GDA_VALUE_ATTR_NO_MODIF;
return flags;
}
@@ -856,3 +1119,210 @@
if (update_model)
g_object_set (G_OBJECT (iter), "update_model", update_model, NULL);
}
+
+/*
+ * creates a derivative of the model->priv->modif_stmts [UPD_QUERY] statement
+ * where only the column @col is updated.
+ *
+ * Returns: a new #GdaStatement, or %NULL
+ */
+static GdaStatement *
+compute_single_update_stmt (GdaPModel *model, gint col, GError **error)
+{
+ GdaSqlStatement *sqlst;
+ GdaSqlStatementUpdate *upd;
+ GdaStatement *updstmt = NULL;
+
+ /* get a copy of complete UPDATE stmt */
+ g_assert (model->priv->modif_stmts [UPD_QUERY]);
+ g_object_get (G_OBJECT (model->priv->modif_stmts [UPD_QUERY]), "structure", &sqlst, NULL);
+ g_assert (sqlst);
+ g_assert (sqlst->stmt_type == GDA_SQL_STATEMENT_UPDATE);
+ upd = (GdaSqlStatementUpdate*) sqlst->contents;
+
+ /* remove non necessary fields to update */
+ GSList *elist, *flist;
+ gboolean field_found = FALSE;
+ for (elist = upd->expr_list, flist = upd->fields_list; elist && flist; ) {
+ GdaSqlExpr *expr = (GdaSqlExpr *) elist->data;
+ gint num;
+ gboolean old_val;
+ if (! expr->param_spec || !param_name_to_int (expr->param_spec->name, &num, &old_val) || old_val) {
+ /* ignore this field to be updated */
+ elist = elist->next;
+ flist = flist->next;
+ continue;
+ }
+ if (num == col) {
+ /* this field is the field to be updated */
+ field_found = TRUE;
+ elist = elist->next;
+ flist = flist->next;
+ continue;
+ }
+ /* remove that field */
+ GSList *nelist, *nflist;
+ nelist = elist->next;
+ nflist = flist->next;
+ gda_sql_expr_free (expr);
+ gda_sql_field_free ((GdaSqlField*) flist->data);
+
+ upd->expr_list = g_slist_delete_link (upd->expr_list, elist);
+ upd->fields_list = g_slist_delete_link (upd->fields_list, flist);
+ elist = nelist;
+ flist = nflist;
+ }
+
+ /* create a new GdaStatement */
+ if (field_found)
+ updstmt = (GdaStatement *) g_object_new (GDA_TYPE_STATEMENT, "structure", sqlst, NULL);
+ else
+ g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MISSING_MODIFICATION_STATEMENT_ERROR,
+ _("Column %d can't be modified"), col);
+ gda_sql_statement_free (sqlst);
+
+#ifdef GDA_DEBUG_NO
+ if (updstmt) {
+ gchar *sql;
+ sql = gda_statement_serialize (updstmt);
+ g_print ("UPDATE for col %d => %s\n", col, sql);
+ g_free (sql);
+ }
+ else
+ g_print ("UPDATE for col %d: ERROR\n", col);
+#endif
+
+ return updstmt;
+}
+
+static gboolean
+gda_pmodel_set_value_at (GdaDataModel *model, gint col, gint row, const GValue *value, GError **error)
+{
+ GdaPModel *imodel;
+ gint i, ncols;
+ GdaHolder *holder;
+ gchar *str;
+
+ imodel = (GdaPModel *) model;
+
+ g_return_val_if_fail (imodel->priv, FALSE);
+
+ if (! (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)) {
+ g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
+ _("Data model does not support random access"));
+ return FALSE;
+ }
+ if (! imodel->priv->modif_stmts [UPD_QUERY]) {
+ g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MISSING_MODIFICATION_STATEMENT_ERROR,
+ _("No UPDATE statement provided"));
+ return FALSE;
+ }
+
+ if (row >= gda_pmodel_get_n_rows (model)) {
+ g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MISSING_MODIFICATION_STATEMENT_ERROR,
+ _("Row %d out of range (0-%d)"), row, gda_pmodel_get_n_rows (model)-1);
+ return FALSE;
+ }
+
+ ncols = gda_pmodel_get_n_columns (model);
+ if (col >= ncols) {
+ g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MISSING_MODIFICATION_STATEMENT_ERROR,
+ _("Column %d out of range (0-%d)"), col, ncols-1);
+ return FALSE;
+ }
+
+ if (! imodel->priv->upd_stmts)
+ imodel->priv->upd_stmts = g_new0 (GdaStatement *, ncols);
+ if (! imodel->priv->upd_stmts [col]) {
+ imodel->priv->upd_stmts [col] = compute_single_update_stmt (imodel, col, error);
+ if (!imodel->priv->upd_stmts [col])
+ return FALSE;
+ }
+
+ str = g_strdup_printf ("+%d", col);
+ holder = gda_set_get_holder (imodel->priv->modif_set, str);
+ g_free (str);
+ if (! holder) {
+ g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MISSING_MODIFICATION_STATEMENT_ERROR,
+ _("Column %d can't be modified"), col);
+ return FALSE;
+ }
+ if (! gda_holder_set_value (holder, value)) {
+ g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_VALUE_ERROR,
+ _("Wrong value type"));
+ return FALSE;
+ }
+
+ for (i = 0; i < ncols; i++) {
+ str = g_strdup_printf ("-%d", i);
+ holder = gda_set_get_holder (imodel->priv->modif_set, str);
+ g_free (str);
+ if (holder) {
+ if (! gda_holder_set_value (holder,
+ gda_data_model_get_value_at (model, i, row))) {
+ g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_VALUE_ERROR,
+ _("Wrong value type"));
+ return FALSE;
+ }
+ }
+ }
+
+#ifdef GDA_DEBUG_NO
+ gchar *sql;
+ GError *lerror = NULL;
+ sql = gda_statement_to_sql_extended (imodel->priv->upd_stmts [col],
+ imodel->priv->cnc,
+ imodel->priv->modif_set,
+ GDA_STATEMENT_SQL_PRETTY, NULL,
+ &lerror);
+ g_print ("%s(): SQL=> %s\n", __FUNCTION__, sql);
+ if (!sql)
+ g_print ("\tERR: %s\n", lerror && lerror->message ? lerror->message : "No detail");
+ g_free (sql);
+#endif
+
+ if (gda_connection_statement_execute_non_select (imodel->priv->cnc,
+ imodel->priv->upd_stmts [col],
+ imodel->priv->modif_set, NULL, error) == -1)
+ return FALSE;
+
+ TO_IMPLEMENT;
+
+ return TRUE;
+}
+
+static gboolean
+gda_pmodel_set_values (GdaDataModel *model, gint row, GList *values, GError **error)
+{
+ GdaPModel *imodel;
+ imodel = (GdaPModel *) model;
+
+ g_return_val_if_fail (imodel->priv, FALSE);
+ TO_IMPLEMENT;
+
+ return FALSE;
+}
+
+static gint
+gda_pmodel_append_values (GdaDataModel *model, const GList *values, GError **error)
+{
+ GdaPModel *imodel;
+ imodel = (GdaPModel *) model;
+
+ g_return_val_if_fail (imodel->priv, -1);
+ TO_IMPLEMENT;
+
+ return -1;
+}
+
+static gboolean
+gda_pmodel_remove_row (GdaDataModel *model, gint row, GError **error)
+{
+ GdaPModel *imodel;
+ imodel = (GdaPModel *) model;
+
+ g_return_val_if_fail (imodel->priv, FALSE);
+ TO_IMPLEMENT;
+
+ return FALSE;
+}
Modified: trunk/libgda/providers-support/gda-pmodel.h
==============================================================================
--- trunk/libgda/providers-support/gda-pmodel.h (original)
+++ trunk/libgda/providers-support/gda-pmodel.h Fri Aug 15 19:41:51 2008
@@ -39,14 +39,25 @@
typedef struct _GdaPModelClass GdaPModelClass;
typedef struct _GdaPModelPrivate GdaPModelPrivate;
+/* error reporting */
+extern GQuark gda_pmodel_error_quark (void);
+#define GDA_PMODEL_ERROR gda_pmodel_error_quark ()
+
+enum {
+ GDA_PMODEL_MODIFICATION_STATEMENT_ERROR,
+ GDA_PMODEL_MISSING_MODIFICATION_STATEMENT_ERROR,
+ GDA_PMODEL_CONNECTION_ERROR,
+ GDA_PMODEL_VALUE_ERROR
+};
+
struct _GdaPModel {
GObject object;
GdaPModelPrivate *priv;
+
/* read only information */
GdaPStmt *prep_stmt; /* use the "prepared-stmt" property to set this */
gint nb_stored_rows; /* number of GdaRow objects currently stored */
gint advertized_nrows; /* set when the number of rows becomes known */
- GdaConnection *cnc;
};
/*
@@ -80,9 +91,8 @@
GdaRow *gda_pmodel_get_stored_row (GdaPModel *model, gint rownum);
GdaConnection *gda_pmodel_get_connection (GdaPModel *model);
-gboolean gda_pmodel_set_modification_query (GdaPModel *model, GdaStatement *mod_stmt, GError **error);
-gboolean gda_pmodel_compute_modification_queries (GdaPModel *model, const gchar *target,
- gboolean use_all_fields_if_no_pk, GError **error);
+gboolean gda_pmodel_set_modification_statements (GdaPModel *model, GdaStatement *mod_stmt, GError **error);
+gboolean gda_pmodel_compute_modification_statements (GdaPModel *model, gboolean require_pk, GError **error);
G_END_DECLS
Modified: trunk/libgda/providers-support/gda-pstmt.c
==============================================================================
--- trunk/libgda/providers-support/gda-pstmt.c (original)
+++ trunk/libgda/providers-support/gda-pstmt.c Fri Aug 15 19:41:51 2008
@@ -32,6 +32,10 @@
static GObjectClass *parent_class = NULL;
+struct _GdaPStmtPrivate {
+ GdaStatement *gda_stmt; /* GdaPStmt object holds a weak reference on this stmt object, may be NULL */
+};
+
/**
* gda_pstmt_get_type
*
@@ -58,7 +62,7 @@
g_static_mutex_lock (®istering);
if (type == 0)
- type = g_type_register_static (G_TYPE_OBJECT, "GdaPStmt", &info, 0);
+ type = g_type_register_static (G_TYPE_OBJECT, "GdaPStmt", &info, G_TYPE_FLAG_ABSTRACT);
g_static_mutex_unlock (®istering);
}
return type;
@@ -79,6 +83,8 @@
gda_pstmt_init (GdaPStmt *pstmt, GdaPStmtClass *klass)
{
g_return_if_fail (GDA_IS_PSTMT (pstmt));
+ pstmt->priv = g_new0 (GdaPStmtPrivate, 1);
+ pstmt->priv->gda_stmt = NULL;
pstmt->sql = NULL;
pstmt->param_ids = NULL;
pstmt->ncols = -1;
@@ -87,14 +93,21 @@
}
static void
+gda_stmt_reset_cb (GdaStatement *stmt, GdaPStmt *pstmt)
+{
+ g_signal_handlers_disconnect_by_func (G_OBJECT (stmt),
+ G_CALLBACK (gda_stmt_reset_cb), pstmt);
+ g_object_remove_weak_pointer ((GObject*) pstmt->priv->gda_stmt, (gpointer*) &(pstmt->priv->gda_stmt));
+ pstmt->priv->gda_stmt = NULL;
+}
+
+static void
gda_pstmt_dispose (GObject *object)
{
GdaPStmt *pstmt = (GdaPStmt *) object;
- if (pstmt->stmt) {
- g_object_remove_weak_pointer ((GObject*) pstmt->stmt, (gpointer*) &(pstmt->stmt));
- pstmt->stmt = NULL;
- }
+ if (pstmt->priv->gda_stmt)
+ gda_stmt_reset_cb (pstmt->priv->gda_stmt, pstmt);
/* chain to parent class */
parent_class->dispose (object);
@@ -106,6 +119,8 @@
GdaPStmt *pstmt = (GdaPStmt *) object;
/* free memory */
+ g_free (pstmt->priv);
+
if (pstmt->sql) {
g_free (pstmt->sql);
pstmt->sql = NULL;
@@ -141,13 +156,16 @@
g_return_if_fail (GDA_IS_PSTMT (pstmt));
g_return_if_fail (!stmt || GDA_IS_STATEMENT (stmt));
- if (pstmt->stmt == stmt)
+ if (pstmt->priv->gda_stmt == stmt)
return;
- if (pstmt->stmt)
- g_object_unref (pstmt->stmt);
- pstmt->stmt = stmt;
- if (stmt)
- g_object_add_weak_pointer ((GObject*) stmt, (gpointer*) &(pstmt->stmt));
+ if (pstmt->priv->gda_stmt)
+ gda_stmt_reset_cb (pstmt->priv->gda_stmt, pstmt);
+
+ pstmt->priv->gda_stmt = stmt;
+ if (stmt) {
+ g_object_add_weak_pointer ((GObject*) stmt, (gpointer*) &(pstmt->priv->gda_stmt));
+ g_signal_connect (G_OBJECT (stmt), "reset", G_CALLBACK (gda_stmt_reset_cb), pstmt);
+ }
}
/**
@@ -189,4 +207,21 @@
dest->tmpl_columns = g_slist_append (dest->tmpl_columns,
gda_column_copy (GDA_COLUMN (list->data)));
}
+ if (src->priv->gda_stmt)
+ gda_pstmt_set_gda_statement (dest, src->priv->gda_stmt);
+}
+
+/**
+ * gda_pstmt_get_gda_statement
+ * @pstmt: a #GdaPStmt object
+ *
+ * Get a pointer to the #GdaStatement which led to the creation of this prepared statement
+ *
+ * Returns: the #GdaStatement
+ */
+GdaStatement *
+gda_pstmt_get_gda_statement (GdaPStmt *pstmt)
+{
+ g_return_val_if_fail (GDA_IS_PSTMT (pstmt), NULL);
+ return pstmt->priv->gda_stmt;
}
Modified: trunk/libgda/providers-support/gda-pstmt.h
==============================================================================
--- trunk/libgda/providers-support/gda-pstmt.h (original)
+++ trunk/libgda/providers-support/gda-pstmt.h Fri Aug 15 19:41:51 2008
@@ -35,12 +35,13 @@
#define GDA_IS_PSTMT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GDA_TYPE_PSTMT))
typedef struct _GdaPStmt GdaPStmt;
+typedef struct _GdaPStmtPrivate GdaPStmtPrivate;
typedef struct _GdaPStmtClass GdaPStmtClass;
struct _GdaPStmt {
GObject object;
- GdaStatement *stmt; /* GdaPStmt object holds a reference on this stmt object, may be NULL */
+ GdaPStmtPrivate *priv;
gchar *sql; /* actual SQL code used for this prepared statement, mem freed by GdaPStmt */
GSList *param_ids; /* list of parameters' IDs (as gchar *), mem freed by GdaPStmt */
@@ -55,9 +56,10 @@
GObjectClass parent_class;
};
-GType gda_pstmt_get_type (void) G_GNUC_CONST;
-void gda_pstmt_set_gda_statement (GdaPStmt *pstmt, GdaStatement *stmt);
-void gda_pstmt_copy_contents (GdaPStmt *src, GdaPStmt *dest);
+GType gda_pstmt_get_type (void) G_GNUC_CONST;
+void gda_pstmt_set_gda_statement (GdaPStmt *pstmt, GdaStatement *stmt);
+void gda_pstmt_copy_contents (GdaPStmt *src, GdaPStmt *dest);
+GdaStatement *gda_pstmt_get_gda_statement (GdaPStmt *pstmt);
G_END_DECLS
Modified: trunk/libgda/sql-parser/gda-statement-struct-pspec.c
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-pspec.c (original)
+++ trunk/libgda/sql-parser/gda-statement-struct-pspec.c Fri Aug 15 19:41:51 2008
@@ -75,16 +75,15 @@
void
gda_sql_param_spec_take_type (GdaSqlParamSpec *pspec, GValue *value)
{
- if (pspec->type) {
- g_free (pspec->type);
- pspec->type = NULL;
- }
+ pspec->g_type = 0;
if (value) {
- pspec->type = _remove_quotes (g_value_dup_string (value));
+ gchar *tmp;
+ tmp = _remove_quotes (g_value_dup_string (value));
g_value_unset (value);
g_free (value);
- pspec->g_type = gda_g_type_from_string (pspec->type);
+ pspec->g_type = gda_g_type_from_string (tmp);
+ g_free (tmp);
}
}
@@ -96,7 +95,6 @@
pspec =g_new0 (GdaSqlParamSpec, 1);
pspec->is_param = TRUE;
pspec->nullok = FALSE;
- pspec->type = NULL;
if (value) {
gchar *str = (gchar *) g_value_get_string (value);
@@ -114,8 +112,7 @@
pspec->name = g_strdup (str);
break;
case 1:
- g_free (pspec->type);
- pspec->type = g_strdup (str);
+ pspec->g_type = gda_g_type_from_string (str);
break;
case 2:
pspec->nullok = (*str == 'n') || (*str == 'N') ? TRUE : FALSE;
@@ -145,8 +142,7 @@
copy->name = g_strdup (pspec->name);
if (pspec->descr)
copy->descr = g_strdup (pspec->descr);
- if (pspec->type)
- copy->type = g_strdup (pspec->type);
+ copy->g_type = pspec->g_type;
copy->is_param = pspec->is_param;
copy->nullok = pspec->nullok;
@@ -160,7 +156,6 @@
g_free (pspec->name);
g_free (pspec->descr);
- g_free (pspec->type);
g_free (pspec);
}
@@ -183,9 +178,13 @@
g_string_append_printf (string, ",\"descr\":%s", str);
g_free (str);
- str = _json_quote_string (pspec->type);
- g_string_append_printf (string, ",\"type\":%s", str);
- g_free (str);
+ if (pspec->g_type != G_TYPE_INVALID) {
+ str = _json_quote_string (gda_g_type_to_string (pspec->g_type));
+ g_string_append_printf (string, ",\"type\":%s", str);
+ g_free (str);
+ }
+ else
+ g_string_append_printf (string, ",\"type\":null");
g_string_append_printf (string, ",\"is_param\":%s", pspec->is_param ? "true" : "false");
g_string_append_printf (string, ",\"nullok\":%s", pspec->nullok ? "true" : "false");
Modified: trunk/libgda/sql-parser/gda-statement-struct-pspec.h
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-pspec.h (original)
+++ trunk/libgda/sql-parser/gda-statement-struct-pspec.h Fri Aug 15 19:41:51 2008
@@ -32,7 +32,6 @@
{
gchar *name;
gchar *descr;
- gchar *type;
gboolean is_param;
gboolean nullok;
Modified: trunk/libgda/sql-parser/gda-statement-struct.c
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct.c (original)
+++ trunk/libgda/sql-parser/gda-statement-struct.c Fri Aug 15 19:41:51 2008
@@ -109,14 +109,20 @@
return NULL;
infos = gda_sql_statement_get_contents_infos (stmt->stmt_type);
- copy = gda_sql_statement_new (stmt->stmt_type);
+ copy = g_new0 (GdaSqlStatement, 1);
+ copy->stmt_type = stmt->stmt_type;
+
if (stmt->sql)
copy->sql = g_strdup (stmt->sql);
if (infos && infos->copy) {
copy->contents = infos->copy (stmt->contents);
GDA_SQL_ANY_PART (copy->contents)->type = GDA_SQL_ANY_PART (stmt->contents)->type;
}
- else
+ else if (infos && infos->construct) {
+ copy->contents = infos->construct ();
+ GDA_SQL_ANY_PART (copy->contents)->type = stmt->stmt_type;
+ }
+ else
TO_IMPLEMENT;
if (stmt->validity_meta_struct) {
copy->validity_meta_struct = stmt->validity_meta_struct;
Modified: trunk/libgda/sqlite/gda-sqlite-provider.c
==============================================================================
--- trunk/libgda/sqlite/gda-sqlite-provider.c (original)
+++ trunk/libgda/sqlite/gda-sqlite-provider.c Fri Aug 15 19:41:51 2008
@@ -1576,7 +1576,7 @@
g_return_val_if_fail (GDA_IS_STATEMENT (stmt), FALSE);
/* fetch prepares stmt if already done */
- ps = gda_connection_get_prepared_statement (cnc, stmt);
+ ps = (GdaSqlitePStmt *) gda_connection_get_prepared_statement (cnc, stmt);
if (ps)
return TRUE;
@@ -1584,7 +1584,7 @@
if (!ps)
return FALSE;
else {
- gda_connection_add_prepared_statement (cnc, stmt, ps);
+ gda_connection_add_prepared_statement (cnc, stmt, (GdaPStmt *) ps);
return TRUE;
}
}
@@ -1821,7 +1821,7 @@
/* get SQLite's private data */
cdata = (SqliteConnectionData*) gda_connection_internal_get_provider_data (cnc);
if (!cdata)
- return FALSE;
+ return NULL;
/* HACK: force SQLite to reparse the schema and thus discover new tables if necessary */
{
@@ -1835,7 +1835,7 @@
}
/* get/create new prepared statement */
- ps = gda_connection_get_prepared_statement (cnc, stmt);
+ ps = (GdaSqlitePStmt *) gda_connection_get_prepared_statement (cnc, stmt);
if (!ps) {
if (!gda_sqlite_provider_statement_prepare (provider, cnc, stmt, NULL)) {
/* try to use the SQL when parameters are rendered with their values */
@@ -1867,7 +1867,7 @@
new_ps = TRUE;
}
else
- ps = gda_connection_get_prepared_statement (cnc, stmt);
+ ps = (GdaSqlitePStmt *) gda_connection_get_prepared_statement (cnc, stmt);
}
else if (ps->stmt_used) {
/* Don't use @ps => prepare stmt again */
@@ -1950,7 +1950,16 @@
g_free (str);
break;
}
-
+ 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;
+ }
/*g_print ("BINDING param '%s' to %p\n", pname, h);*/
const GValue *value = gda_holder_get_value (h);
@@ -2063,7 +2072,7 @@
else
flags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
- data_model = (GObject *) gda_sqlite_recordset_new (cnc, ps, flags, col_types);
+ data_model = (GObject *) gda_sqlite_recordset_new (cnc, ps, params, flags, col_types);
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 Fri Aug 15 19:41:51 2008
@@ -179,7 +179,8 @@
* this function
*/
GdaDataModel *
-gda_sqlite_recordset_new (GdaConnection *cnc, GdaSqlitePStmt *ps, GdaDataModelAccessFlags flags, GType *col_types)
+gda_sqlite_recordset_new (GdaConnection *cnc, GdaSqlitePStmt *ps, GdaSet *exec_params,
+ GdaDataModelAccessFlags flags, GType *col_types)
{
GdaSqliteRecordset *model;
SqliteConnectionData *cdata;
@@ -288,7 +289,8 @@
/* create data model */
model = g_object_new (GDA_TYPE_SQLITE_RECORDSET, "connection", cnc,
- "prepared-stmt", ps, "model-usage", rflags, NULL);
+ "prepared-stmt", ps, "model-usage", rflags,
+ "exec-params", exec_params, NULL);
/* fill the data model */
read_rows_to_init_col_types (model);
Modified: trunk/libgda/sqlite/gda-sqlite-recordset.h
==============================================================================
--- trunk/libgda/sqlite/gda-sqlite-recordset.h (original)
+++ trunk/libgda/sqlite/gda-sqlite-recordset.h Fri Aug 15 19:41:51 2008
@@ -52,8 +52,8 @@
};
GType gda_sqlite_recordset_get_type (void) G_GNUC_CONST;
-GdaDataModel *gda_sqlite_recordset_new (GdaConnection *cnc, GdaSqlitePStmt *ps, GdaDataModelAccessFlags flags,
- GType *col_types);
+GdaDataModel *gda_sqlite_recordset_new (GdaConnection *cnc, GdaSqlitePStmt *ps, GdaSet *exec_params,
+ GdaDataModelAccessFlags flags, GType *col_types);
G_END_DECLS
Modified: trunk/libgda/sqlite/utils.c
==============================================================================
--- trunk/libgda/sqlite/utils.c (original)
+++ trunk/libgda/sqlite/utils.c Fri Aug 15 19:41:51 2008
@@ -57,6 +57,7 @@
}
g_hash_table_insert (types, g_strdup ("integer"), GINT_TO_POINTER (G_TYPE_INT));
+ g_hash_table_insert (types, g_strdup ("int"), GINT_TO_POINTER (G_TYPE_INT));
g_hash_table_insert (types, g_strdup ("boolean"), GINT_TO_POINTER (G_TYPE_BOOLEAN));
g_hash_table_insert (types, g_strdup ("date"), GINT_TO_POINTER (G_TYPE_DATE));
g_hash_table_insert (types, g_strdup ("time"), GINT_TO_POINTER (GDA_TYPE_TIME));
Modified: trunk/providers/mysql/gda-mysql-provider.c
==============================================================================
--- trunk/providers/mysql/gda-mysql-provider.c (original)
+++ trunk/providers/mysql/gda-mysql-provider.c Fri Aug 15 19:41:51 2008
@@ -155,7 +155,7 @@
GType *col_types,
GdaSet **last_inserted_row,
guint *task_id,
- GdaServerProviderAsyncCallback async_cb,
+ GdaServerProviderExecCallback async_cb,
gpointer cb_data,
GError **error);
@@ -313,6 +313,7 @@
}
else
provider_class->limiting_thread = NULL;
+
}
static void
@@ -1166,7 +1167,7 @@
g_return_val_if_fail (GDA_IS_STATEMENT (stmt), FALSE);
/* fetch prepares stmt if already done */
- ps = gda_connection_get_prepared_statement (cnc, stmt);
+ ps = (GdaMysqlPStmt*) gda_connection_get_prepared_statement (cnc, stmt);
if (ps)
return TRUE;
@@ -1228,7 +1229,7 @@
_GDA_PSTMT(ps)->param_ids = param_ids;
_GDA_PSTMT(ps)->sql = sql;
- gda_connection_add_prepared_statement (cnc, stmt, ps);
+ gda_connection_add_prepared_statement (cnc, stmt, (GdaPStmt*) ps);
return TRUE;
}
@@ -1288,7 +1289,7 @@
GType *col_types,
GdaSet **last_inserted_row,
guint *task_id,
- GdaServerProviderAsyncCallback async_cb,
+ GdaServerProviderExecCallback async_cb,
gpointer cb_data,
GError **error)
{
@@ -1312,7 +1313,7 @@
/* get/create new prepared statement */
- ps = gda_connection_get_prepared_statement (cnc, stmt);
+ ps = (GdaMysqlPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
if (!ps) {
if (!gda_mysql_provider_statement_prepare (provider, cnc, stmt, NULL)) {
/* this case can appear for example if some variables are used in places
@@ -1327,21 +1328,19 @@
if (!sql)
return NULL;
ps = prepare_stmt_simple (cdata, sql, error);
+ g_free (sql);
if (!ps)
return NULL;
}
else
- ps = gda_connection_get_prepared_statement (cnc, stmt);
+ ps = (GdaMysqlPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
}
g_assert (ps);
/* optionnally reset the prepared statement if required by the API */
// TO_IMPLEMENT;
-
- g_print (" %s: SQL=%s\n", __func__, _GDA_PSTMT(ps)->sql);
-
/* bind statement's parameters */
GSList *list;
GdaConnectionEvent *event = NULL;
@@ -1351,7 +1350,7 @@
char **param_values = g_new0 (char *, nb_params + 1);
int *param_lengths = g_new0 (int, nb_params + 1);
int *param_formats = g_new0 (int, nb_params + 1);
- g_print ("NB=%d, SQL=%s\n", nb_params, _GDA_PSTMT(ps)->sql);
+ /* g_print ("NB=%d, SQL=%s\n", nb_params, _GDA_PSTMT(ps)->sql); */
MYSQL_BIND *mysql_bind_param = g_new0 (MYSQL_BIND, nb_params);
@@ -1386,6 +1385,16 @@
g_free (str);
break;
}
+ 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;
+ }
/* actual binding using the C API, for parameter at position @i */
const GValue *value = gda_holder_get_value (h);
@@ -1403,7 +1412,7 @@
param_values[i] = gda_handler_time_get_no_locale_str_from_value (handler_time,
value);
- g_print ("--- TIME=%s\n", param_values[i]);
+ /* g_print ("--- TIME=%s\n", param_values[i]); */
} else {
GdaDataHandler *data_handler = gda_server_provider_get_data_handler_gtype
(provider, cnc, G_VALUE_TYPE(value));
@@ -1412,7 +1421,7 @@
else
param_values[i] = gda_data_handler_get_str_from_value (data_handler,
value);
- g_print ("--- PV=%s\n", param_values[i]);
+ /* g_print ("--- PV=%s\n", param_values[i]); */
mysql_bind_param[i].buffer_type = MYSQL_TYPE_STRING;
mysql_bind_param[i].buffer = g_strdup (param_values[i]);
@@ -1420,11 +1429,6 @@
mysql_bind_param[i].length = g_malloc0 (sizeof(unsigned long));
}
-
- gchar *str = gda_value_stringify (value);
- g_print (" %s: %s=%s\n", __func__, pname, str);
- g_free (str);
-
}
@@ -1443,6 +1447,24 @@
return NULL;
}
+
+
+ /* use cursor when retrieving result */
+ if ((model_usage & GDA_STATEMENT_MODEL_RANDOM_ACCESS) == 0 &&
+ gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT) {
+#if MYSQL_VERSION_ID >= 50002
+ const unsigned long cursor_type = CURSOR_TYPE_READ_ONLY;
+ if (mysql_stmt_attr_set (cdata->mysql_stmt, STMT_ATTR_CURSOR_TYPE, (void *) &cursor_type)) {
+ _gda_mysql_make_error (cnc, NULL, cdata->mysql_stmt, NULL);
+ return NULL;
+ }
+#else
+ model_usage = GDA_STATEMENT_MODEL_RANDOM_ACCESS;
+ g_warning (_("Could not use CURSOR. Mysql version 5.0 at least is required. "
+ "Using random access anyway."));
+#endif
+ }
+
/* add a connection event for the execution */
event = gda_connection_event_new (GDA_CONNECTION_EVENT_COMMAND);
@@ -1471,7 +1493,7 @@
else
flags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
- return_value = (GObject *) gda_mysql_recordset_new (cnc, ps, flags, col_types);
+ return_value = (GObject *) gda_mysql_recordset_new (cnc, ps, params, flags, col_types);
gda_connection_internal_statement_executed (cnc, stmt, params, NULL); /* required: help @cnc keep some stats */
}
Modified: trunk/providers/mysql/gda-mysql-recordset.c
==============================================================================
--- trunk/providers/mysql/gda-mysql-recordset.c (original)
+++ trunk/providers/mysql/gda-mysql-recordset.c Fri Aug 15 19:41:51 2008
@@ -58,22 +58,22 @@
gda_mysql_recordset_fetch_nb_rows (GdaPModel *model);
static gboolean
gda_mysql_recordset_fetch_random (GdaPModel *model,
- GdaRow **prow,
+ GdaRow **row,
gint rownum,
GError **error);
static gboolean
gda_mysql_recordset_fetch_next (GdaPModel *model,
- GdaRow **prow,
+ GdaRow **row,
gint rownum,
GError **error);
static gboolean
gda_mysql_recordset_fetch_prev (GdaPModel *model,
- GdaRow **prow,
+ GdaRow **row,
gint rownum,
GError **error);
static gboolean
gda_mysql_recordset_fetch_at (GdaPModel *model,
- GdaRow **prow,
+ GdaRow **row,
gint rownum,
GError **error);
@@ -104,12 +104,52 @@
/* initialize specific information */
// TO_IMPLEMENT;
- recset->priv->chunk_size = 10;
+ recset->priv->chunk_size = 1;
recset->priv->chunks_read = 0;
}
+gint
+gda_mysql_recordset_get_chunk_size (GdaMysqlRecordset *recset)
+{
+ g_return_if_fail (GDA_IS_MYSQL_RECORDSET (recset));
+ return recset->priv->chunk_size;
+}
+
+void
+gda_mysql_recordset_set_chunk_size (GdaMysqlRecordset *recset,
+ gint chunk_size)
+{
+ g_return_if_fail (GDA_IS_MYSQL_RECORDSET (recset));
+
+ if (recset->priv->mysql_stmt == NULL) // Creation is in progress so it's not set.
+ return;
+
+#if MYSQL_VERSION_ID >= 50002
+ const unsigned long prefetch_rows = chunk_size;
+ if (mysql_stmt_attr_set (recset->priv->mysql_stmt, STMT_ATTR_PREFETCH_ROWS,
+ (void *) &prefetch_rows)) {
+ _gda_mysql_make_error (recset->priv->cnc, NULL, recset->priv->mysql_stmt, NULL);
+ return;
+ }
+ recset->priv->chunk_size = chunk_size;
+ g_object_notify (G_OBJECT(recset), "chunk-size");
+#else
+ g_warning (_("Could not use CURSOR. Mysql version 5.0 at least is required. "
+ "Chunk size ignored."));
+#endif
+
+}
+
+gint
+gda_mysql_recordset_get_chunks_read (GdaMysqlRecordset *recset)
+{
+ g_return_if_fail (GDA_IS_MYSQL_RECORDSET (recset));
+ return recset->priv->chunks_read;
+}
+
+
static void
gda_mysql_recordset_set_property (GObject *object,
guint param_id,
@@ -126,7 +166,8 @@
switch (param_id) {
case PROP_CHUNK_SIZE:
- record_set->priv->chunk_size = g_value_get_int (value);
+ gda_mysql_recordset_set_chunk_size (record_set,
+ g_value_get_int (value));
break;
case PROP_CHUNKS_READ:
break;
@@ -187,7 +228,7 @@
PROP_CHUNK_SIZE,
g_param_spec_int ("chunk-size", _("Number of rows fetched at a time"),
NULL,
- 1, G_MAXINT - 1, 10,
+ 1, G_MAXINT - 1, 1,
(G_PARAM_CONSTRUCT | G_PARAM_WRITABLE | G_PARAM_READABLE)));
g_object_class_install_property
@@ -259,6 +300,7 @@
GdaDataModel *
gda_mysql_recordset_new (GdaConnection *cnc,
GdaMysqlPStmt *ps,
+ GdaSet *exec_params,
GdaDataModelAccessFlags flags,
GType *col_types)
{
@@ -417,6 +459,7 @@
model = g_object_new (GDA_TYPE_MYSQL_RECORDSET,
"prepared-stmt", ps,
"model-usage", rflags,
+ "exec-params", exec_params,
NULL);
model->priv->cnc = cnc;
g_object_ref (G_OBJECT(cnc));
@@ -459,18 +502,18 @@
new_row_from_mysql_stmt (GdaMysqlRecordset *imodel,
gint rownum)
{
- g_print ("*** %s -- %d -- %d\n", __func__, ((GdaPModel *) imodel)->prep_stmt->ncols, rownum);
+ /* g_print ("*** %s -- %d -- %d\n", __func__, ((GdaPModel *) imodel)->prep_stmt->ncols, rownum); */
MYSQL_BIND *mysql_bind_result = ((GdaMysqlPStmt *) ((GdaPModel *) imodel)->prep_stmt)->mysql_bind_result;
g_assert (mysql_bind_result);
- GdaRow *prow = gda_row_new (((GdaPModel *) imodel)->prep_stmt->ncols);
+ GdaRow *row = gda_row_new (((GdaPModel *) imodel)->prep_stmt->ncols);
gint col;
for (col = 0; col < ((GdaPModel *) imodel)->prep_stmt->ncols; ++col) {
gint i = col;
- GValue *value = gda_row_get_value (prow, i);
+ GValue *value = gda_row_get_value (row, i);
GType type = ((GdaPModel *) imodel)->prep_stmt->types[i];
gda_value_reset_with_type (value, type);
@@ -607,20 +650,20 @@
g_free (strvalue);
/* gchar *str = gda_value_stringify (value); */
- /* g_print (" V%d=%s\n", i, str); */
+ /* g_print ("***V%d=%s\n", i, str); */
/* g_free (str); */
- //
+
}
- return prow;
+ return row;
}
/*
- * Create a new filled #GdaRow object for the row at position @rownum, and put it into *prow.
+ * Create a new filled #GdaRow object for the row at position @rownum, and put it into *row.
*
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- * - If *prow is NULL then a new #GdaRow object has to be created,
- * - and otherwise *prow contains a #GdaRow object which has already been created
+ * WARNING: @row will NOT be NULL, but *row may or may not be NULL:
+ * - If *row is NULL then a new #GdaRow object has to be created,
+ * - and otherwise *row contains a #GdaRow object which has already been created
* (through a call to this very function), and in this case it should not be modified
* but the function may return FALSE if an error occurred.
*
@@ -632,7 +675,7 @@
*/
static gboolean
gda_mysql_recordset_fetch_random (GdaPModel *model,
- GdaRow **prow,
+ GdaRow **row,
gint rownum,
GError **error)
{
@@ -642,7 +685,7 @@
// TO_IMPLEMENT;
- if (*prow)
+ if (*row)
return TRUE;
if (imodel->priv->mysql_stmt == NULL) {
@@ -655,13 +698,14 @@
if (mysql_stmt_fetch (imodel->priv->mysql_stmt))
return FALSE;
- *prow = new_row_from_mysql_stmt (imodel, rownum);
- gda_pmodel_take_row (model, *prow, rownum);
-
- /* if (model->nb_stored_rows == model->advertized_nrows) { */
- /* g_print (" All the row have been converted..."); */
- /* } */
+ *row = new_row_from_mysql_stmt (imodel, rownum);
+ gda_pmodel_take_row (model, *row, rownum);
+ if (model->nb_stored_rows == model->advertized_nrows) {
+ /* All the row have been converted. We could free result now */
+ /* but it was done before provided no field-based API functions */
+ /* that process result set meta data was needed in the middle. */
+ }
return TRUE;
}
@@ -670,7 +714,8 @@
* Create and "give" filled #GdaRow object for all the rows in the model
*/
static gboolean
-gda_mysql_recordset_store_all (GdaPModel *model, GError **error)
+gda_mysql_recordset_store_all (GdaPModel *model,
+ GError **error)
{
GdaMysqlRecordset *imodel;
gint i;
@@ -679,19 +724,19 @@
/* default implementation */
for (i = 0; i < model->advertized_nrows; i++) {
- GdaRow *prow;
- if (! gda_mysql_recordset_fetch_random (model, &prow, i, error))
+ GdaRow *row;
+ if (! gda_mysql_recordset_fetch_random (model, &row, i, error))
return FALSE;
}
return TRUE;
}
/*
- * Create a new filled #GdaRow object for the next cursor row, and put it into *prow.
+ * Create a new filled #GdaRow object for the next cursor row, and put it into *row.
*
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- * - If *prow is NULL then a new #GdaRow object has to be created,
- * - and otherwise *prow contains a #GdaRow object which has already been created
+ * WARNING: @row will NOT be NULL, but *row may or may not be NULL:
+ * - If *row is NULL then a new #GdaRow object has to be created,
+ * - and otherwise *row contains a #GdaRow object which has already been created
* (through a call to this very function), and in this case it should not be modified
* but the function may return FALSE if an error occurred.
*
@@ -699,21 +744,26 @@
* can use gda_pmodel_take_row().
*/
static gboolean
-gda_mysql_recordset_fetch_next (GdaPModel *model, GdaRow **prow, gint rownum, GError **error)
+gda_mysql_recordset_fetch_next (GdaPModel *model,
+ GdaRow **row,
+ gint rownum,
+ GError **error)
{
GdaMysqlRecordset *imodel = (GdaMysqlRecordset*) model;
- TO_IMPLEMENT;
+ // TO_IMPLEMENT;
+ //
- return TRUE;
+ // gda_pmodel_iter_next increments rownum
+ return /* TRUE */ gda_mysql_recordset_fetch_random (model, row, rownum, error);
}
/*
- * Create a new filled #GdaRow object for the previous cursor row, and put it into *prow.
+ * Create a new filled #GdaRow object for the previous cursor row, and put it into *row.
*
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- * - If *prow is NULL then a new #GdaRow object has to be created,
- * - and otherwise *prow contains a #GdaRow object which has already been created
+ * WARNING: @row will NOT be NULL, but *row may or may not be NULL:
+ * - If *row is NULL then a new #GdaRow object has to be created,
+ * - and otherwise *row contains a #GdaRow object which has already been created
* (through a call to this very function), and in this case it should not be modified
* but the function may return FALSE if an error occurred.
*
@@ -721,7 +771,10 @@
* can use gda_pmodel_take_row().
*/
static gboolean
-gda_mysql_recordset_fetch_prev (GdaPModel *model, GdaRow **prow, gint rownum, GError **error)
+gda_mysql_recordset_fetch_prev (GdaPModel *model,
+ GdaRow **row,
+ gint rownum,
+ GError **error)
{
GdaMysqlRecordset *imodel = (GdaMysqlRecordset*) model;
@@ -731,11 +784,11 @@
}
/*
- * Create a new filled #GdaRow object for the cursor row at position @rownum, and put it into *prow.
+ * Create a new filled #GdaRow object for the cursor row at position @rownum, and put it into *row.
*
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- * - If *prow is NULL then a new #GdaRow object has to be created,
- * - and otherwise *prow contains a #GdaRow object which has already been created
+ * WARNING: @row will NOT be NULL, but *row may or may not be NULL:
+ * - If *row is NULL then a new #GdaRow object has to be created,
+ * - and otherwise *row contains a #GdaRow object which has already been created
* (through a call to this very function), and in this case it should not be modified
* but the function may return FALSE if an error occurred.
*
@@ -743,7 +796,10 @@
* can use gda_pmodel_take_row().
*/
static gboolean
-gda_mysql_recordset_fetch_at (GdaPModel *model, GdaRow **prow, gint rownum, GError **error)
+gda_mysql_recordset_fetch_at (GdaPModel *model,
+ GdaRow **row,
+ gint rownum,
+ GError **error)
{
GdaMysqlRecordset *imodel = (GdaMysqlRecordset*) model;
Modified: trunk/providers/mysql/gda-mysql-recordset.h
==============================================================================
--- trunk/providers/mysql/gda-mysql-recordset.h (original)
+++ trunk/providers/mysql/gda-mysql-recordset.h Fri Aug 15 19:41:51 2008
@@ -53,9 +53,21 @@
GdaDataModel *
gda_mysql_recordset_new (GdaConnection *cnc,
GdaMysqlPStmt *ps,
+ GdaSet *exec_params,
GdaDataModelAccessFlags flags,
GType *col_types);
+
+gint
+gda_mysql_recordset_get_chunk_size (GdaMysqlRecordset *recset);
+
+void
+gda_mysql_recordset_set_chunk_size (GdaMysqlRecordset *recset,
+ gint chunk_size);
+
+gint
+gda_mysql_recordset_get_chunks_read (GdaMysqlRecordset *recset);
+
G_END_DECLS
#endif
Modified: trunk/providers/mysql/libmain.c
==============================================================================
--- trunk/providers/mysql/libmain.c (original)
+++ trunk/providers/mysql/libmain.c Fri Aug 15 19:41:51 2008
@@ -2,7 +2,7 @@
* Copyright (C) 2008 The GNOME Foundation
*
* AUTHORS:
- * Carlos Savoretti <csavoretti gmail com>
+ * TO_ADD: your name and email
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
Modified: trunk/providers/postgres/gda-postgres-provider.c
==============================================================================
--- trunk/providers/postgres/gda-postgres-provider.c (original)
+++ trunk/providers/postgres/gda-postgres-provider.c Fri Aug 15 19:41:51 2008
@@ -1629,7 +1629,7 @@
g_return_val_if_fail (GDA_IS_STATEMENT (stmt), FALSE);
/* fetch prepares stmt if already done */
- ps = gda_connection_get_prepared_statement (cnc, stmt);
+ ps = (GdaPostgresPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
if (ps)
return TRUE;
@@ -1694,7 +1694,7 @@
_GDA_PSTMT (ps)->param_ids = param_ids;
_GDA_PSTMT (ps)->sql = sql;
- gda_connection_add_prepared_statement (cnc, stmt, ps);
+ gda_connection_add_prepared_statement (cnc, stmt, (GdaPStmt *) ps);
return TRUE;
out_err:
@@ -1962,13 +1962,13 @@
PQclear (pg_res);
/* create data model in CURSOR mode */
- recset = gda_postgres_recordset_new_cursor (cnc, ps, cursor_name, col_types);
+ recset = gda_postgres_recordset_new_cursor (cnc, ps, params, cursor_name, col_types);
gda_connection_internal_statement_executed (cnc, stmt, params, NULL); /* required: help @cnc keep some stats */
return (GObject*) recset;
}
/* get/create new prepared statement */
- ps = gda_connection_get_prepared_statement (cnc, stmt);
+ ps = (GdaPostgresPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
if (!ps) {
if (!gda_postgres_provider_statement_prepare (provider, cnc, stmt, NULL)) {
/* this case can appear for example if some variables are used in places
@@ -1987,7 +1987,7 @@
return NULL;
}
else
- ps = gda_connection_get_prepared_statement (cnc, stmt);
+ ps = (GdaPostgresPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
}
g_assert (ps);
@@ -2037,6 +2037,16 @@
g_free (str);
break;
}
+ 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;
+ }
/* actual binding using the C API, for parameter at position @i */
const GValue *value = gda_holder_get_value (h);
@@ -2144,7 +2154,7 @@
PQclear (pg_res);
}
else if (status == PGRES_TUPLES_OK)
- retval = (GObject*) gda_postgres_recordset_new_random (cnc, ps, pg_res, col_types);
+ retval = (GObject*) gda_postgres_recordset_new_random (cnc, ps, params, pg_res, col_types);
else {
PQclear (pg_res);
retval = (GObject *) gda_data_model_array_new (0);
Modified: trunk/providers/postgres/gda-postgres-recordset.c
==============================================================================
--- trunk/providers/postgres/gda-postgres-recordset.c (original)
+++ trunk/providers/postgres/gda-postgres-recordset.c Fri Aug 15 19:41:51 2008
@@ -319,7 +319,8 @@
}
GdaDataModel *
-gda_postgres_recordset_new_random (GdaConnection *cnc, GdaPostgresPStmt *ps, PGresult *pg_res, GType *col_types)
+gda_postgres_recordset_new_random (GdaConnection *cnc, GdaPostgresPStmt *ps, GdaSet *exec_params,
+ PGresult *pg_res, GType *col_types)
{
GdaPostgresRecordset *model;
PostgresConnectionData *cdata;
@@ -337,7 +338,8 @@
/* create data model */
model = g_object_new (GDA_TYPE_POSTGRES_RECORDSET, "connection", cnc,
"prepared-stmt", ps,
- "model-usage", GDA_DATA_MODEL_ACCESS_RANDOM, NULL);
+ "model-usage", GDA_DATA_MODEL_ACCESS_RANDOM,
+ "exec-params", exec_params, NULL);
model->priv->pg_res = pg_res;
((GdaPModel*) model)->advertized_nrows = PQntuples (model->priv->pg_res);
@@ -348,7 +350,8 @@
* Takes ownership of @cursor_name
*/
GdaDataModel *
-gda_postgres_recordset_new_cursor (GdaConnection *cnc, GdaPostgresPStmt *ps, gchar *cursor_name, GType *col_types)
+gda_postgres_recordset_new_cursor (GdaConnection *cnc, GdaPostgresPStmt *ps, GdaSet *exec_params,
+ gchar *cursor_name, GType *col_types)
{
GdaPostgresRecordset *model;
PostgresConnectionData *cdata;
@@ -393,7 +396,8 @@
/* create model */
model = g_object_new (GDA_TYPE_POSTGRES_RECORDSET, "connection", cnc,
"prepared-stmt", ps, "model-usage",
- GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD | GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD, NULL);
+ GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD | GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD,
+ "exec-params", exec_params, NULL);
model->priv->pconn = cdata->pconn;
model->priv->cursor_name = cursor_name;
gboolean fetch_error;
@@ -727,7 +731,7 @@
if (thevalue && (*thevalue != '\0' ? FALSE : PQgetisnull (imodel->priv->pg_res, pg_res_rownum, col)))
gda_value_set_null (gda_row_get_value (prow, col));
else
- set_value (((GdaPModel*) imodel)->cnc,
+ set_value (gda_pmodel_get_connection ((GdaPModel*) imodel),
gda_row_get_value (prow, col),
((GdaPModel*) imodel)->prep_stmt->types [col],
thevalue,
@@ -771,7 +775,7 @@
status = PQresultStatus (model->priv->pg_res);
model->priv->chunks_read ++;
if (status != PGRES_TUPLES_OK) {
- _gda_postgres_make_error (((GdaPModel*) model)->cnc,
+ _gda_postgres_make_error (gda_pmodel_get_connection ((GdaPModel*) model),
model->priv->pconn, model->priv->pg_res, error);
PQclear (model->priv->pg_res);
model->priv->pg_res = NULL;
@@ -863,7 +867,7 @@
status = PQresultStatus (model->priv->pg_res);
model->priv->chunks_read ++;
if (status != PGRES_TUPLES_OK) {
- _gda_postgres_make_error (((GdaPModel*) model)->cnc,
+ _gda_postgres_make_error (gda_pmodel_get_connection ((GdaPModel*) model),
model->priv->pconn, model->priv->pg_res, error);
PQclear (model->priv->pg_res);
model->priv->pg_res = NULL;
@@ -937,7 +941,7 @@
status = PQresultStatus (model->priv->pg_res);
model->priv->chunks_read ++; /* Not really correct, because we are only fetching 1 row, not a whole chunk of rows. */
if (status != PGRES_TUPLES_OK) {
- _gda_postgres_make_error (((GdaPModel*) model)->cnc,
+ _gda_postgres_make_error (gda_pmodel_get_connection ((GdaPModel*) model),
model->priv->pconn, model->priv->pg_res, error);
PQclear (model->priv->pg_res);
model->priv->pg_res = NULL;
Modified: trunk/providers/postgres/gda-postgres-recordset.h
==============================================================================
--- trunk/providers/postgres/gda-postgres-recordset.h (original)
+++ trunk/providers/postgres/gda-postgres-recordset.h Fri Aug 15 19:41:51 2008
@@ -49,8 +49,8 @@
};
GType gda_postgres_recordset_get_type (void) G_GNUC_CONST;
-GdaDataModel *gda_postgres_recordset_new_random (GdaConnection *cnc, GdaPostgresPStmt *ps, PGresult *pg_res, GType *col_types);
-GdaDataModel *gda_postgres_recordset_new_cursor (GdaConnection *cnc, GdaPostgresPStmt *ps, gchar *cursor_name, GType *col_types);
+GdaDataModel *gda_postgres_recordset_new_random (GdaConnection *cnc, GdaPostgresPStmt *ps, GdaSet *exec_params, PGresult *pg_res, GType *col_types);
+GdaDataModel *gda_postgres_recordset_new_cursor (GdaConnection *cnc, GdaPostgresPStmt *ps, GdaSet *exec_params, gchar *cursor_name, GType *col_types);
G_END_DECLS
Modified: trunk/providers/skel-implementation/capi/gda-capi-provider.c
==============================================================================
--- trunk/providers/skel-implementation/capi/gda-capi-provider.c (original)
+++ trunk/providers/skel-implementation/capi/gda-capi-provider.c Fri Aug 15 19:41:51 2008
@@ -913,7 +913,7 @@
g_return_val_if_fail (GDA_IS_STATEMENT (stmt), FALSE);
/* fetch prepares stmt if already done */
- ps = gda_connection_get_prepared_statement (cnc, stmt);
+ ps = (GdaCapiPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
if (ps)
return TRUE;
@@ -922,7 +922,7 @@
if (!ps)
return FALSE;
else {
- gda_connection_add_prepared_statement (cnc, stmt, ps);
+ gda_connection_add_prepared_statement (cnc, stmt, (GdaPStmt *) ps);
return TRUE;
}
}
@@ -968,7 +968,7 @@
/* get/create new prepared statement */
- ps = gda_connection_get_prepared_statement (cnc, stmt);
+ ps = (GdaCapiPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
if (!ps) {
if (!gda_capi_provider_statement_prepare (provider, cnc, stmt, NULL)) {
/* this case can appear for example if some variables are used in places
@@ -980,7 +980,7 @@
return NULL;
}
else
- ps = gda_connection_get_prepared_statement (cnc, stmt);
+ ps = (GdaCapiPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
}
g_assert (ps);
@@ -1023,6 +1023,16 @@
g_free (str);
break;
}
+ 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;
+ }
/* actual binding using the C API, for parameter at position @i */
const GValue *value = gda_holder_get_value (h);
@@ -1050,7 +1060,7 @@
else
flags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
- data_model = (GObject *) gda_capi_recordset_new (cnc, ps, flags, col_types);
+ data_model = (GObject *) gda_capi_recordset_new (cnc, ps, params, flags, col_types);
gda_connection_internal_statement_executed (cnc, stmt, params, NULL); /* required: help @cnc keep some stats */
return data_model;
}
Modified: trunk/providers/skel-implementation/capi/gda-capi-recordset.c
==============================================================================
--- trunk/providers/skel-implementation/capi/gda-capi-recordset.c (original)
+++ trunk/providers/skel-implementation/capi/gda-capi-recordset.c Fri Aug 15 19:41:51 2008
@@ -138,7 +138,8 @@
* this function
*/
GdaDataModel *
-gda_capi_recordset_new (GdaConnection *cnc, GdaCapiPStmt *ps, GdaDataModelAccessFlags flags, GType *col_types)
+gda_capi_recordset_new (GdaConnection *cnc, GdaCapiPStmt *ps, GdaSet *exec_params,
+ GdaDataModelAccessFlags flags, GType *col_types)
{
GdaCapiRecordset *model;
CapiConnectionData *cdata;
@@ -202,7 +203,10 @@
rflags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
/* create data model */
- model = g_object_new (GDA_TYPE_CAPI_RECORDSET, "prepared-stmt", ps, "model-usage", rflags, NULL);
+ model = g_object_new (GDA_TYPE_CAPI_RECORDSET,
+ "prepared-stmt", ps,
+ "model-usage", rflags,
+ "exec-params", exec_params, NULL);
model->priv->cnc = cnc;
g_object_ref (cnc);
Modified: trunk/providers/skel-implementation/capi/gda-capi-recordset.h
==============================================================================
--- trunk/providers/skel-implementation/capi/gda-capi-recordset.h (original)
+++ trunk/providers/skel-implementation/capi/gda-capi-recordset.h Fri Aug 15 19:41:51 2008
@@ -49,8 +49,8 @@
};
GType gda_capi_recordset_get_type (void) G_GNUC_CONST;
-GdaDataModel *gda_capi_recordset_new (GdaConnection *cnc, GdaCapiPStmt *ps, GdaDataModelAccessFlags flags,
- GType *col_types);
+GdaDataModel *gda_capi_recordset_new (GdaConnection *cnc, GdaCapiPStmt *ps, GdaSet *exec_params,
+ GdaDataModelAccessFlags flags, GType *col_types);
G_END_DECLS
Modified: trunk/tests/data-models/Makefile.am
==============================================================================
--- trunk/tests/data-models/Makefile.am (original)
+++ trunk/tests/data-models/Makefile.am Fri Aug 15 19:41:51 2008
@@ -5,8 +5,8 @@
$(LIBGDA_CFLAGS) \
-DCHECK_FILES=\""$(top_srcdir)"\"
-check_PROGRAMS = check_model_import check_virtual check_data_proxy check_model_copy
-TESTS = check_model_import check_virtual check_data_proxy check_model_copy
+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
common_sources =
@@ -33,6 +33,15 @@
$(top_builddir)/libgda/libgda-4.0.la \
$(LIBGDA_LIBS)
+check_pmodel_SOURCES = $(common_sources) check_pmodel.c
+check_pmodel_CFLAGS = \
+ -I$(top_srcdir)/libgda/sqlite \
+ -I$(top_srcdir)/libgda/sqlite/pmodel
+check_pmodel_LDADD = \
+ $(top_builddir)/libgda/libgda-4.0.la \
+ $(top_builddir)/tests/libgda-test-4.0.la \
+ $(LIBGDA_LIBS)
+
EXTRA_DIST = \
check_virtual.csv \
city.csv \
Added: trunk/tests/data-models/check_pmodel.c
==============================================================================
--- (empty file)
+++ trunk/tests/data-models/check_pmodel.c Fri Aug 15 19:41:51 2008
@@ -0,0 +1,363 @@
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <libgda/libgda.h>
+#include <sql-parser/gda-sql-parser.h>
+#include <sql-parser/gda-sql-statement.h>
+#include <providers-support/gda-pmodel.h>
+#include <tests/gda-ddl-creator.h>
+#include <glib/gstdio.h>
+#include "../test-cnc-utils.h"
+
+#define fail(x) g_warning (x)
+#define fail_if(x,y) if (x) g_warning (y)
+#define fail_unless(x,y) if (!(x)) g_warning (y)
+
+#define CHECK_EXTRA_INFO
+
+static GdaConnection *setup_connection (void);
+static GdaStatement *stmt_from_string (const gchar *sql);
+static void load_data_from_file (GdaConnection *cnc, const gchar *table, const gchar *file);
+
+typedef gboolean (*TestFunc) (GdaConnection *);
+static gint test1 (GdaConnection *cnc);
+static gint test2 (GdaConnection *cnc);
+static gint test3 (GdaConnection *cnc);
+
+TestFunc tests[] = {
+ test1,
+ test2,
+ test3
+};
+
+int
+main (int argc, char **argv)
+{
+ gint i, ntests = 0, number_failed = 0;
+ GdaConnection *cnc;
+
+ gda_init ();
+
+ g_unlink ("pmodel.db");
+ cnc = setup_connection ();
+
+ for (i = 0; i < sizeof (tests) / sizeof (TestFunc); i++) {
+ g_print ("---------- test %d ----------\n", i);
+ gint n = tests[i] (cnc);
+ number_failed += n;
+ if (n > 0)
+ g_print ("Test %d failed\n", i);
+ ntests ++;
+ }
+
+ g_object_unref (cnc);
+ if (number_failed == 0)
+ g_print ("Ok.\n");
+ else
+ g_print ("%d failed\n", number_failed);
+
+ return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+static GdaConnection *
+setup_connection (void)
+{
+ GdaDDLCreator *ddl;
+ GError *error = NULL;
+ GdaConnection *cnc;
+ gchar *str;
+
+ /* open a connection */
+ cnc = gda_connection_open_from_string ("SQLite", "DB_DIR=.;DB_NAME=pmodel", NULL, GDA_CONNECTION_OPTIONS_NONE, &error);
+ if (!cnc) {
+ g_print ("Error opening connection: %s\n", error && error->message ? error->message : "No detail");
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
+
+ /* Setup structure */
+ ddl = gda_ddl_creator_new ();
+ gda_ddl_creator_set_connection (ddl, cnc);
+ str = g_build_filename (CHECK_FILES, "tests", "data-models", "pmodel_dbstruct.xml", NULL);
+ if (!gda_ddl_creator_set_dest_from_file (ddl, str, &error)) {
+ g_print ("Error creating GdaDDLCreator: %s\n", error && error->message ? error->message : "No detail");
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
+ g_free (str);
+
+#ifdef SHOW_SQL
+ str = gda_ddl_creator_get_sql (ddl, &error);
+ if (!str) {
+ g_print ("Error getting SQL: %s\n", error && error->message ? error->message : "No detail");
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
+ g_print ("%s\n", str);
+ g_free (str);
+#endif
+
+ if (!gda_ddl_creator_execute (ddl, &error)) {
+ g_print ("Error creating database objects: %s\n", error && error->message ? error->message : "No detail");
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
+
+ if (! gda_connection_update_meta_store (cnc, NULL, &error)) {
+ g_print ("Error fetching meta data: %s\n", error && error->message ? error->message : "No detail");
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
+
+ /* load some data */
+ load_data_from_file (cnc, "locations", "pmodel_data_locations.xml");
+ load_data_from_file (cnc, "customers", "pmodel_data_customers.xml");
+
+ g_object_unref (ddl);
+ return cnc;
+}
+
+static void
+load_data_from_file (GdaConnection *cnc, const gchar *table, const gchar *file)
+{
+ gchar *str;
+ GError *error = NULL;
+
+ str = g_build_filename (CHECK_FILES, "tests", "data-models", file, NULL);
+ if (! test_cnc_load_data_from_file (cnc, table, str, &error)) {
+ g_print ("Error loading data into table '%s' from file '%s': %s\n", table, str,
+ error && error->message ? error->message : "No detail");
+ exit (EXIT_FAILURE);
+ }
+ g_free (str);
+}
+
+static GdaStatement *
+stmt_from_string (const gchar *sql)
+{
+ GdaStatement *stmt;
+ GError *error = NULL;
+
+ static GdaSqlParser *parser = NULL;
+ if (!parser)
+ parser = gda_sql_parser_new ();
+
+ stmt = gda_sql_parser_parse_string (parser, sql, NULL, &error);
+ if (!stmt) {
+ g_print ("Cound not parse SQL: %s\nSQL was: %s\n",
+ error && error->message ? error->message : "No detail",
+ sql);
+ exit (EXIT_FAILURE);
+ }
+ return stmt;
+}
+
+/* Returns the number of failures */
+static gint
+test1 (GdaConnection *cnc)
+{
+ GError *error = NULL;
+ GdaDataModel *model;
+ GdaStatement *stmt;
+ gint nfailed = 0;
+
+ /* create GdaPModel */
+ stmt = stmt_from_string ("SELECT * FROM customers");
+ model = gda_connection_statement_execute_select (cnc, stmt, NULL, &error);
+ if (!model) {
+ nfailed++;
+#ifdef CHECK_EXTRA_INFO
+ g_print ("Could not execute SELECT!\n");
+#endif
+ goto out;
+ }
+ if (!GDA_IS_PMODEL (model)) {
+ g_print ("Data model should be a GdaPModel!\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* test non INSERT, UPDATE or DELETE stmts */
+ GdaStatement *mod_stmt;
+
+ if (gda_pmodel_set_modification_statement (GDA_PMODEL (model), stmt, &error)) {
+ nfailed++;
+#ifdef CHECK_EXTRA_INFO
+ g_print ("gda_pmodel_set_modification_statement() should have failed\n");
+#endif
+ goto out;
+ }
+#ifdef CHECK_EXTRA_INFO
+ g_print ("Got expected error: %s\n", error && error->message ? error->message : "No detail");
+#endif
+ g_error_free (error);
+ error = NULL;
+
+ mod_stmt = stmt_from_string ("BEGIN");
+ if (gda_pmodel_set_modification_statement (GDA_PMODEL (model), mod_stmt, &error)) {
+ nfailed++;
+#ifdef CHECK_EXTRA_INFO
+ g_print ("gda_pmodel_set_modification_statement() should have failed\n");
+#endif
+ goto out;
+ }
+#ifdef CHECK_EXTRA_INFO
+ g_print ("Got expected error: %s\n", error && error->message ? error->message : "No detail");
+#endif
+ g_error_free (error);
+ error = NULL;
+ g_object_unref (mod_stmt);
+
+ /* test INSERT with undefined params */
+ mod_stmt = stmt_from_string ("INSERT INTO customers (name) VALUES (##aname::string)");
+ if (gda_pmodel_set_modification_statement (GDA_PMODEL (model), mod_stmt, &error)) {
+ nfailed++;
+#ifdef CHECK_EXTRA_INFO
+ g_print ("gda_pmodel_set_modification_statement() should have failed\n");
+#endif
+ goto out;
+ }
+#ifdef CHECK_EXTRA_INFO
+ g_print ("Got expected error: %s\n", error && error->message ? error->message : "No detail");
+#endif
+ g_error_free (error);
+ error = NULL;
+
+ out:
+ g_object_unref (model);
+ g_object_unref (stmt);
+
+ return nfailed;
+}
+
+/* Returns the number of failures */
+static gint
+test2 (GdaConnection *cnc)
+{
+ GError *error = NULL;
+ GdaDataModel *model;
+ GdaStatement *stmt, *mod_stmt;
+ gint nfailed = 0;
+ GdaSet *params;
+
+ /* create GdaPModel */
+ stmt = stmt_from_string ("SELECT * FROM customers WHERE country = ##country::string");
+ if (!gda_statement_get_parameters (stmt, ¶ms, &error)) {
+ nfailed++;
+#ifdef CHECK_EXTRA_INFO
+ g_print ("Could not get SELECT's parameters!\n");
+#endif
+ goto out;
+ }
+ gda_set_set_holder_value (params, "country", "SP");
+ model = gda_connection_statement_execute_select (cnc, stmt, params, &error);
+ g_object_unref (params);
+ if (!model) {
+ nfailed++;
+#ifdef CHECK_EXTRA_INFO
+ g_print ("Could not execute SELECT!\n");
+#endif
+ goto out;
+ }
+ if (!GDA_IS_PMODEL (model)) {
+ g_print ("Data model should be a GdaPModel!\n");
+ exit (EXIT_FAILURE);
+ }
+
+
+ /* test INSERT with params of the wrong type */
+ mod_stmt = stmt_from_string ("INSERT INTO customers (name, country) VALUES (##+1::string, ##country::date)");
+ if (gda_pmodel_set_modification_statement (GDA_PMODEL (model), mod_stmt, &error)) {
+ nfailed++;
+#ifdef CHECK_EXTRA_INFO
+ g_print ("gda_pmodel_set_modification_statement() should have failed\n");
+#endif
+ goto out;
+ }
+#ifdef CHECK_EXTRA_INFO
+ g_print ("Got expected error: %s\n", error && error->message ? error->message : "No detail");
+#endif
+ g_error_free (error);
+ error = NULL;
+ g_object_unref (mod_stmt);
+
+ /* test correct INSERT */
+ mod_stmt = stmt_from_string ("INSERT INTO customers (name, country) VALUES (##+1::string, ##country::string)");
+ if (!gda_pmodel_set_modification_statement (GDA_PMODEL (model), mod_stmt, &error)) {
+ nfailed++;
+#ifdef CHECK_EXTRA_INFO
+ g_print ("gda_pmodel_set_modification_statement() should have succedded, error: %s\n",
+ error && error->message ? error->message : "No detail");
+#endif
+ goto out;
+ }
+
+ out:
+ g_object_unref (model);
+ g_object_unref (stmt);
+
+ return nfailed;
+}
+
+/* Returns the number of failures */
+static gint
+test3 (GdaConnection *cnc)
+{
+ GError *error = NULL;
+ GdaDataModel *model;
+ GdaStatement *stmt, *mod_stmt;
+ gint nfailed = 0;
+
+ /* create GdaPModel */
+ stmt = stmt_from_string ("SELECT * FROM customers");
+ model = gda_connection_statement_execute_select (cnc, stmt, NULL, &error);
+ if (!model) {
+ nfailed++;
+#ifdef CHECK_EXTRA_INFO
+ g_print ("Could not execute SELECT!\n");
+#endif
+ goto out;
+ }
+ if (!GDA_IS_PMODEL (model)) {
+ g_print ("Data model should be a GdaPModel!\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* test INSERT with undefined params */
+ mod_stmt = stmt_from_string ("UPDATE customers SET name = ##+1::string WHERE id = ##-0::gint");
+ if (!gda_pmodel_set_modification_statement (GDA_PMODEL (model), mod_stmt, &error)) {
+ nfailed++;
+#ifdef CHECK_EXTRA_INFO
+ g_print ("gda_pmodel_set_modification_statement() should have succedded, error: %s\n",
+ error && error->message ? error->message : "No detail");
+#endif
+ goto out;
+ }
+ GValue *value;
+ g_value_set_string ((value = gda_value_new (G_TYPE_STRING)), "Jack");
+ if (! gda_data_model_set_value_at (model, 1, 0, value, &error)) {
+ nfailed++;
+#ifdef CHECK_EXTRA_INFO
+ g_print ("gda_data_model_set_value_at failed: %s\n",
+ error && error->message ? error->message : "No detail");
+#endif
+ goto out;
+ }
+ if (gda_data_model_set_value_at (model, 0, 0, value, &error)) {
+ nfailed++;
+#ifdef CHECK_EXTRA_INFO
+ g_print ("gda_data_model_set_value_at should have failed\n");
+#endif
+ goto out;
+ }
+#ifdef CHECK_EXTRA_INFO
+ g_print ("Got expected error: %s\n", error && error->message ? error->message : "No detail");
+#endif
+ g_error_free (error);
+ gda_value_free (value);
+
+ out:
+ g_object_unref (model);
+ g_object_unref (stmt);
+
+ return nfailed;
+}
Modified: trunk/tests/meta-store/common.c
==============================================================================
--- trunk/tests/meta-store/common.c (original)
+++ trunk/tests/meta-store/common.c Fri Aug 15 19:41:51 2008
@@ -107,9 +107,9 @@
/*
* Loading a CSV file
- * ... is a (-1 terminated) list of triplets composed of:
+ * ... is a (-1 terminated) list of pairs composed of:
* - a column number (gint)
- * - the column (gchar *)
+ * - the column type (gchar *)
*/
GdaDataModel *
common_load_csv_file (const gchar *data_file, ...)
@@ -511,15 +511,15 @@
TEST_HEADER;
/* load CSV file */
- import = common_load_csv_file ("data_routines.csv", 8, "boolean", 14, "boolean", 16, "boolean", -1);
+ import = common_load_csv_file ("data_routines.csv", 8, "boolean", 9, "gint", 15, "boolean", 17, "boolean", -1);
common_declare_expected_insertions_from_model (TNAME, import);
TEST_MODIFY (store, TNAME, import, NULL, &error, NULL);
TEST_END (import);
/* remove some lines */
- DECL_CHANGE (TNAME, GDA_META_STORE_REMOVE, "-0", "meta", "-1", "information_schema", "-2", "_pg_numeric_precision_radix_10632", NULL);
+ DECL_CHANGE (TNAME, GDA_META_STORE_REMOVE, "-0", "meta", "-1", "information_schema", "-2", "_pg_numeric_precision_radix_11324", NULL);
TEST_MODIFY (store, TNAME, NULL,
- "specific_name='_pg_numeric_precision_radix_10632'", &error, NULL);
+ "specific_name='_pg_numeric_precision_radix_11324'", &error, NULL);
TEST_END (NULL);
#undef TNAME
}
Modified: trunk/tests/meta-store/data_routines.csv
==============================================================================
--- trunk/tests/meta-store/data_routines.csv (original)
+++ trunk/tests/meta-store/data_routines.csv Fri Aug 15 19:41:51 2008
@@ -1,6 +1,6 @@
-"meta","information_schema","_pg_truetypid_10627","meta","information_schema","_pg_truetypid","FUNCTION","pg_catalog.oid",FALSE,"SQL","SELECT CASE WHEN $2.typtype = 'd' THEN $2.typbasetype ELSE $1.atttypid END","NULL","SQL","GENERAL",TRUE,"MODIFIES",TRUE,"NULL","information_schema._pg_truetypid","information_schema._pg_truetypid","postgres"
-"meta","information_schema","_pg_truetypmod_10628","meta","information_schema","_pg_truetypmod","FUNCTION","pg_catalog.int4",FALSE,"SQL","SELECT CASE WHEN $2.typtype = 'd' THEN $2.typtypmod ELSE $1.atttypmod END","NULL","SQL","GENERAL",TRUE,"MODIFIES",TRUE,"NULL","information_schema._pg_truetypmod","information_schema._pg_truetypmod","postgres"
-"meta","information_schema","_pg_char_max_length_10629","meta","information_schema","_pg_char_max_length","FUNCTION","pg_catalog.int4",FALSE,"SQL","SELECT
+"meta","information_schema","_pg_truetypid_11319","meta","information_schema","_pg_truetypid","FUNCTION","pg_catalog.oid",FALSE,"2","SQL","SELECT CASE WHEN $2.typtype = 'd' THEN $2.typbasetype ELSE $1.atttypid END","NULL","SQL","GENERAL",TRUE,"MODIFIES",TRUE,"NULL","information_schema._pg_truetypid","information_schema._pg_truetypid","postgres"
+"meta","information_schema","_pg_truetypmod_11320","meta","information_schema","_pg_truetypmod","FUNCTION","pg_catalog.int4",FALSE,"2","SQL","SELECT CASE WHEN $2.typtype = 'd' THEN $2.typtypmod ELSE $1.atttypmod END","NULL","SQL","GENERAL",TRUE,"MODIFIES",TRUE,"NULL","information_schema._pg_truetypmod","information_schema._pg_truetypmod","postgres"
+"meta","information_schema","_pg_char_max_length_11321","meta","information_schema","_pg_char_max_length","FUNCTION","pg_catalog.int4",FALSE,"2","SQL","SELECT
CASE WHEN $2 = -1 /* default typmod */
THEN null
WHEN $1 IN (1042, 1043) /* char, varchar */
@@ -9,12 +9,12 @@
THEN $2
ELSE null
END","NULL","SQL","GENERAL",TRUE,"MODIFIES",TRUE,"NULL","information_schema._pg_char_max_length","information_schema._pg_char_max_length","postgres"
-"meta","information_schema","_pg_char_octet_length_10630","meta","information_schema","_pg_char_octet_length","FUNCTION","pg_catalog.int4",FALSE,"SQL","SELECT
+"meta","information_schema","_pg_char_octet_length_11322","meta","information_schema","_pg_char_octet_length","FUNCTION","pg_catalog.int4",FALSE,"2","SQL","SELECT
CASE WHEN $1 IN (25, 1042, 1043) /* text, char, varchar */
THEN CAST(2^30 AS integer)
ELSE null
END","NULL","SQL","GENERAL",TRUE,"MODIFIES",TRUE,"NULL","information_schema._pg_char_octet_length","information_schema._pg_char_octet_length","postgres"
-"meta","information_schema","_pg_numeric_precision_10631","meta","information_schema","_pg_numeric_precision","FUNCTION","pg_catalog.int4",FALSE,"SQL","SELECT
+"meta","information_schema","_pg_numeric_precision_11323","meta","information_schema","_pg_numeric_precision","FUNCTION","pg_catalog.int4",FALSE,"2","SQL","SELECT
CASE $1
WHEN 21 /*int2*/ THEN 16
WHEN 23 /*int4*/ THEN 32
@@ -28,7 +28,7 @@
WHEN 701 /*float8*/ THEN 53 /*DBL_MANT_DIG*/
ELSE null
END","NULL","SQL","GENERAL",TRUE,"MODIFIES",TRUE,"NULL","information_schema._pg_numeric_precision","information_schema._pg_numeric_precision","postgres"
-"meta","information_schema","_pg_numeric_precision_radix_10632","meta","information_schema","_pg_numeric_precision_radix","FUNCTION","pg_catalog.int4",FALSE,"SQL","SELECT
+"meta","information_schema","_pg_numeric_precision_radix_11324","meta","information_schema","_pg_numeric_precision_radix","FUNCTION","pg_catalog.int4",FALSE,"2","SQL","SELECT
CASE WHEN $1 IN (21, 23, 20, 700, 701) THEN 2
WHEN $1 IN (1700) THEN 10
ELSE null
Modified: trunk/tests/test-cnc-utils.c
==============================================================================
--- trunk/tests/test-cnc-utils.c (original)
+++ trunk/tests/test-cnc-utils.c Fri Aug 15 19:41:51 2008
@@ -321,3 +321,129 @@
TO_IMPLEMENT;
return FALSE;
}
+
+/*
+ * Load data from file @file into table @table
+ */
+gboolean
+test_cnc_load_data_from_file (GdaConnection *cnc, const gchar *table, const gchar *full_file, GError **error)
+{
+ GdaStatement *stmt;
+ GdaSet *params = NULL;
+ GdaDataModel *import;
+ gint nrows, ncols, i;
+ GdaMetaStruct *mstruct = NULL;
+ GSList *list;
+ gboolean retval = TRUE;
+
+ /* loading XML file */
+ import = gda_data_model_import_new_file (full_file, TRUE, NULL);
+ if (gda_data_model_import_get_errors (GDA_DATA_MODEL_IMPORT (import))) {
+ g_set_error (error, 0, 0, "Error loading '%s' file", full_file);
+ return FALSE;
+ }
+
+ /* retreiving meta data info */
+ GdaMetaDbObject *table_dbo;
+ GValue *name_value;
+ g_value_set_string ((name_value = gda_value_new (G_TYPE_STRING)), table);
+ mstruct = gda_meta_struct_new (gda_connection_get_meta_store (cnc), GDA_META_STRUCT_FEATURE_NONE);
+ table_dbo = gda_meta_struct_complement (mstruct, GDA_META_DB_TABLE,
+ NULL, NULL, name_value, error);
+ gda_value_free (name_value);
+ if (! table_dbo) {
+ retval = FALSE;
+ goto out;
+ }
+
+ /* creating INSERT statement */
+ GdaSqlStatement *st;
+ GdaSqlStatementInsert *ist;
+ GSList *insert_values_list = NULL;
+
+ ist = g_new0 (GdaSqlStatementInsert, 1);
+ GDA_SQL_ANY_PART (ist)->type = GDA_SQL_ANY_STMT_INSERT;
+ ist->table = gda_sql_table_new (GDA_SQL_ANY_PART (ist));
+ ist->table->table_name = g_strdup (table);
+
+ GdaMetaTable *mtable = GDA_META_TABLE (table_dbo);
+ for (list = mtable->columns; list; list = list->next) {
+ GdaMetaTableColumn *tcol = GDA_META_TABLE_COLUMN (list->data);
+ GdaSqlField *field;
+
+ /* field */
+ field = gda_sql_field_new (GDA_SQL_ANY_PART (ist));
+ field->field_name = g_strdup (tcol->column_name);
+ ist->fields_list = g_slist_append (ist->fields_list, field);
+
+ /* value */
+ GdaSqlParamSpec *pspec = g_new0 (GdaSqlParamSpec, 1);
+ GdaSqlExpr *expr;
+ pspec->name = g_strdup (tcol->column_name);
+ pspec->g_type = tcol->gtype;
+ pspec->nullok = tcol->nullok;
+ expr = gda_sql_expr_new (GDA_SQL_ANY_PART (ist));
+ expr->param_spec = pspec;
+ insert_values_list = g_slist_append (insert_values_list, expr);
+ }
+
+ ist->values_list = g_slist_append (NULL, insert_values_list);
+ st = gda_sql_statement_new (GDA_SQL_STATEMENT_INSERT);
+ st->contents = ist;
+ stmt = g_object_new (GDA_TYPE_STATEMENT, "structure", st, NULL);
+ gda_sql_statement_free (st);
+ g_object_unref (mstruct);
+
+ if (! gda_statement_get_parameters (stmt, ¶ms, error)) {
+ retval = FALSE;
+ goto out;
+ }
+
+ /* executing inserts */
+ nrows = gda_data_model_get_n_rows (import);
+ ncols = gda_data_model_get_n_columns (import);
+ if (!gda_connection_begin_transaction (cnc, NULL, GDA_TRANSACTION_ISOLATION_UNKNOWN, error)) {
+ retval = FALSE;
+ goto out;
+ }
+ for (i = 0; i < nrows; i++) {
+ gint j;
+ GSList *list;
+ for (list = params->holders, j = 0; list && (j < ncols); list = list->next, j++) {
+ const GValue *cvalue = gda_data_model_get_value_at (import, j, i);
+ if (! gda_holder_set_value (GDA_HOLDER (list->data), cvalue)) {
+ g_set_error (error, 0, 0, "Error using value at col=>%d and row=>%d", j, i);
+ gda_connection_rollback_transaction (cnc, NULL, NULL);
+ retval = FALSE;
+ goto out;
+ }
+ }
+
+ if (list || (j < ncols)) {
+ g_set_error (error, 0, 0,
+ "Incoherent number of columns in table and imported data");
+ gda_connection_rollback_transaction (cnc, NULL, NULL);
+ retval = FALSE;
+ goto out;
+ }
+
+ if (gda_connection_statement_execute_non_select (cnc, stmt, params, NULL, error) == -1) {
+ gda_connection_rollback_transaction (cnc, NULL, NULL);
+ retval = FALSE;
+ goto out;
+ }
+ }
+
+ if (! gda_connection_commit_transaction (cnc, NULL, error))
+ retval = FALSE;
+
+ out:
+ if (import)
+ g_object_unref (import);
+ if (stmt)
+ g_object_unref (stmt);
+ if (params)
+ g_object_unref (params);
+
+ return retval;
+}
Modified: trunk/tests/test-cnc-utils.h
==============================================================================
--- trunk/tests/test-cnc-utils.h (original)
+++ trunk/tests/test-cnc-utils.h Fri Aug 15 19:41:51 2008
@@ -10,4 +10,6 @@
gboolean test_cnc_setup_db_contents (GdaConnection *cnc, const gchar *data_file, GError **error);
gboolean test_cnc_clean_connection (GdaConnection *cnc, GError **error);
+gboolean test_cnc_load_data_from_file (GdaConnection *cnc, const gchar *table, const gchar *full_file, GError **error);
+
#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]