[gnome-db] Minor API change and MySQL [update/delete]_row



Here's an alpha code for MySql updating and deleting row--only work
when there's a unique, non-null key (ahem ;-).  There are still a few
FIXME's in the code.  It also prints out the sql query for your
debugging pleasure.  These will go away in a few weeks.  I'm just
going by 'commit early, commit often' :-).

No one replied to my last e-mail about the 'const' issue, and adding
gda_data_model_get_updatable_row().  So, taking silence as
consent...I...err...I...  Don't be mad at me, OK?

There's a family of functions that I couldn't find any code using
them.  Since they sound like an old, unused updating scheme, I removed
them.
-	      gda_data_model_begin_edit(),
-	      gda_data_model_end_edit(),
-	      gda_data_model_cancel_edit(),
-	      gda_data_model_is_editing()

Other minor changes are documented in ChangeLog

There are functions that are only useful to providers, and end users
shouldn't have to learn them.  May I move this into a separate header?

Paisa



Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/libgda/ChangeLog,v
retrieving revision 1.581
diff -u -r1.581 ChangeLog
--- ChangeLog	18 Oct 2003 23:10:39 -0000	1.581
+++ ChangeLog	26 Oct 2003 00:49:25 -0000
@@ -1,3 +1,45 @@
+2003-10-25  Paisa Seeluangsawat <paisa users sf net>
+	
+	*gda-blob.c: fixed return type mismatch in gda_blob_free_data()
+
+	*gda-data-model.[hc]:
+	 providers/ldap/gda-ldap-recordset.c:
+	 providers/msql/gda-msql-recordset.c:
+	 providers/oracle/gda-oracle-recordset.c:
+         removed
+	      gda_data_model_begin_edit(),
+	      gda_data_model_end_edit(),
+	      gda_data_model_cancel_edit(),
+	      gda_data_model_is_editing()
+	 and their related codes
+
+	*gda-data-model.[hc]
+	  - inserted const's
+	  - marks functions that are only of use to providers and 
+	    hopefully will go into a separate header.
+	  - updated some doc comments.
+	  - added gda_data_model_get_updatable_row()
+	  - gda_data_model_foreach()
+	    - stop the loop when user function returns FALSE (as
+	      advertised in the spec).
+	    - GdaDataModelForeachFunc now takes 'const GdaRow*' so
+  	      stop cloning GdaRow.
+
+	*gda-row.[hc]
+	  - inserted const's
+	  - marks functions that are only of use to providers.
+	  - updated some doc comments.
+	  - added gda_row_get_updatable_value()
+
+	*gda-mysql-provider.c
+	  - get_table_fields(): removed 13 lines of error handling
+	    that would never be reached (according to mysql doc).
+
+	*gda-mysql-recordset.c
+	  - added gda_data_model_get_updatable_row()
+	  - implemented gda_data_model_get_updatable_row()
+	  - implemented gda_data_model_get_updatable_row()
+
 2003-10-19  Laurent Sansonetti <laurent datarescue be>
 
 	* doc/C/examples/full_example.c:
Index: libgda/gda-blob.c
===================================================================
RCS file: /cvs/gnome/libgda/libgda/gda-blob.c,v
retrieving revision 1.2
diff -u -r1.2 gda-blob.c
--- libgda/gda-blob.c	16 Aug 2003 20:38:55 -0000	1.2
+++ libgda/gda-blob.c	26 Oct 2003 00:49:25 -0000
@@ -158,9 +158,9 @@
 void 
 gda_blob_free_data (GdaBlob *blob)
 {
-	g_return_val_if_fail (blob != NULL, -1);
+	g_return_if_fail (blob != NULL);
 	g_return_if_fail (blob->free_data != NULL);
 
-	return blob->free_data (blob);
+	blob->free_data (blob);
 }
 
Index: libgda/gda-data-model.c
===================================================================
RCS file: /cvs/gnome/libgda/libgda/gda-data-model.c,v
retrieving revision 1.38
diff -u -r1.38 gda-data-model.c
--- libgda/gda-data-model.c	18 Oct 2003 23:03:52 -0000	1.38
+++ libgda/gda-data-model.c	26 Oct 2003 00:49:26 -0000
@@ -51,9 +51,6 @@
 	ROW_INSERTED,
 	ROW_UPDATED,
 	ROW_REMOVED,
-	BEGIN_EDIT,
-	CANCEL_EDIT,
-	END_EDIT,
 	LAST_SIGNAL
 };
 
@@ -103,40 +100,14 @@
                               NULL, NULL,
                               g_cclosure_marshal_VOID__INT,
                               G_TYPE_NONE, 1, G_TYPE_INT);
-	gda_data_model_signals[BEGIN_EDIT] =
-		g_signal_new ("begin_edit",
-                              G_TYPE_FROM_CLASS (object_class),
-                              G_SIGNAL_RUN_LAST,
-                              G_STRUCT_OFFSET (GdaDataModelClass, begin_edit),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__VOID,
-                              G_TYPE_NONE, 0);
-	gda_data_model_signals[CANCEL_EDIT] =
-		g_signal_new ("cancel_edit",
-                              G_TYPE_FROM_CLASS (object_class),
-                              G_SIGNAL_RUN_LAST,
-                              G_STRUCT_OFFSET (GdaDataModelClass, cancel_edit),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__VOID,
-                              G_TYPE_NONE, 0);
-	gda_data_model_signals[END_EDIT] =
-		g_signal_new ("end_edit",
-                              G_TYPE_FROM_CLASS (object_class),
-                              G_SIGNAL_RUN_LAST,
-                              G_STRUCT_OFFSET (GdaDataModelClass, end_edit),
-                              NULL, NULL,
-                              g_cclosure_marshal_VOID__VOID,
-                              G_TYPE_NONE, 0);
 
 	object_class->finalize = gda_data_model_finalize;
 	klass->changed = NULL;
-	klass->begin_edit = NULL;
-	klass->cancel_edit = NULL;
-	klass->end_edit = NULL;
 	klass->get_n_rows = NULL;
 	klass->get_n_columns = NULL;
 	klass->describe_column = NULL;
 	klass->get_row = NULL;
+	klass->get_updatable_row = NULL;
 	klass->get_value_at = NULL;
 	klass->is_editable = NULL;
 	klass->append_row = NULL;
@@ -413,7 +384,7 @@
 
 			fa = gda_data_model_describe_column (model, col);
 			if (fa) {
-				gda_data_model_set_column_title (model, col, title);
+				gda_data_model_set_column_title ((GdaDataModel*) model, col, title);
 				gda_field_attributes_free (fa);
 
 				return g_hash_table_lookup (model->priv->column_titles,
@@ -499,7 +470,8 @@
  * @model: a #GdaDataModel object.
  * @row: row number.
  *
- * Retrieves a given row from a data model.
+ * Retrieves a given row from a data model.  If you want non-const
+ * #GdaRow, use #gda_data_model_get_row.
  *
  * Returns: a #GdaRow object.
  */
@@ -513,6 +485,25 @@
 }
 
 /**
+ * gda_data_model_get_updatable_row
+ * @model:	a #GdaDataModel object.
+ * @row:	row number.
+ *
+ * Retrieves an editable row from a data model.
+ *
+ * Returns: a #GdaRow object.  #NULL if fail.
+ */
+GdaRow *
+gda_data_model_get_updatable_row (GdaDataModel *model, gint row)
+{
+	g_return_val_if_fail (GDA_IS_DATA_MODEL (model), NULL);
+	g_return_val_if_fail (CLASS (model)->get_updatable_row != NULL, NULL);
+
+	return CLASS (model)->get_updatable_row (model, row);
+}
+
+
+/**
  * gda_data_model_get_value_at
  * @model: a #GdaDataModel object.
  * @col: column number.
@@ -539,9 +530,9 @@
  * gda_data_model_is_editable
  * @model: a #GdaDataModel object.
  *
- * Checks whether the given data model can be edited or not.
+ * Cursoriry checks whether the given data model can be edited or not.
  *
- * Returns: %TRUE if it can be edited, %FALSE if not.
+ * Returns: %TRUE you might (just might) be able to update or insert rows, %FALSE if you can not.
  */
 gboolean
 gda_data_model_is_editable (GdaDataModel *model)
@@ -555,7 +546,9 @@
 /**
  * gda_data_model_append_row
  * @model: a #GdaDataModel object.
- * @values: the row to add.
+ * @values: #GList of #GdaValue* representing the row to add.  The
+ *          length must match model's column count.  These #GdaValue
+ *	    are value-copied.  The user is still responsible for freeing them.
  *
  * Appends a row to the given data model.
  *
@@ -573,15 +566,16 @@
 /**
  * gda_data_model_remove_row
  * @model: a #GdaDataModel object.
- * @row: the #GdaRow to be removed.
+ * @row: a #GdaRow returned from #gda_data_model_get_updatable_row.
  *
  * Removes a row from the data model. This results in the underlying
- * database row being removed in the database.
+ * database row being removed in the database.  If succeed, row is no
+ * longer useable.
  *
  * Returns: %TRUE if successful, %FALSE otherwise.
  */
 gboolean
-gda_data_model_remove_row (GdaDataModel *model, const GdaRow *row)
+gda_data_model_remove_row (GdaDataModel *model, GdaRow *row)
 {
 	g_return_val_if_fail (GDA_IS_DATA_MODEL (model), FALSE);
 	g_return_val_if_fail (row != NULL, FALSE);
@@ -593,15 +587,16 @@
 /**
  * gda_data_model_update_row
  * @model: a #GdaDataModel object.
- * @row: the #GdaRow to be updated.
+ * @row: a #GdaRow returned from #gda_data_model_get_updatable_row.
  *
  * Updates a row data model. This results in the underlying
- * database row's values being changed.
+ * database row's values being changed.  If succeed, row is no
+ * longer useable.
  *
  * Returns: %TRUE if successful, %FALSE otherwise.
  */
 gboolean
-gda_data_model_update_row (GdaDataModel *model, const GdaRow *row)
+gda_data_model_update_row (GdaDataModel *model, GdaRow *row)
 {
 	g_return_val_if_fail (GDA_IS_DATA_MODEL (model), FALSE);
 	g_return_val_if_fail (row != NULL, FALSE);
@@ -610,6 +605,8 @@
 	return CLASS (model)->update_row (model, row);
 }
 
+
+
 /**
  * gda_data_model_foreach
  * @model: a #GdaDataModel object.
@@ -634,118 +631,23 @@
 			GdaDataModelForeachFunc func,
 			gpointer user_data)
 {
-	gint cols;
 	gint rows;
-	gint c, r;
-	GdaRow *row;
+	gint r;
+	const GdaRow *row;
 
 	g_return_if_fail (GDA_IS_DATA_MODEL (model));
 	g_return_if_fail (func != NULL);
 
 	rows = gda_data_model_get_n_rows (model);
-	cols = gda_data_model_get_n_columns (model);
-
-	for (r = 0; r < rows; r++) {
-		row = gda_row_new (model, cols);
-		for (c = 0; c < cols; c++) {
-			GdaValue *value;
-			value = gda_value_copy (gda_data_model_get_value_at (model, c, r));
-			memcpy (gda_row_get_value (row, c), value, sizeof (GdaValue));
-		}
-
+	gboolean more = TRUE;
+	for (r = 0; more && r < rows ; r++) {
+		row = gda_data_model_get_row (model, r);
 		/* call the callback function */
-		func (model, row, user_data);
-
-		gda_row_free (row);
+		more = func (model, row, user_data);
 	}
 }
 
-/**
- * gda_data_model_is_editing
- * @model: a #GdaDataModel object.
- *
- * Checks whether this data model is in editing mode or not. Editing
- * mode is set to %TRUE when @gda_data_model_begin_edit has been
- * called successfully, and is not set back to %FALSE until either
- * @gda_data_model_cancel_edit or @gda_data_model_end_edit have
- * been called.
- *
- * Returns: %TRUE if editing mode, %FALSE otherwise.
- */
-gboolean
-gda_data_model_is_editing (GdaDataModel *model)
-{
-	g_return_val_if_fail (GDA_IS_DATA_MODEL (model), FALSE);
-	return model->priv->editing;
-}
 
-/**
- * gda_data_model_begin_edit
- * @model: a #GdaDataModel object.
- *
- * Starts edition of this data model. This function should be the
- * first called when modifying the data model.
- *
- * Returns: %TRUE on success, %FALSE if there was an error.
- */
-gboolean
-gda_data_model_begin_edit (GdaDataModel *model)
-{
-	g_return_val_if_fail (GDA_IS_DATA_MODEL (model), FALSE);
-	g_return_val_if_fail (model->priv->editing == FALSE, FALSE);
-
-	if (!gda_data_model_is_editable (model)) {
-		gda_log_error (_("Data model %p is not editable"), model);
-		return FALSE;
-	}
-
-	model->priv->editing = TRUE;
-	g_signal_emit (G_OBJECT (model), gda_data_model_signals[BEGIN_EDIT], 0);
-
-	return model->priv->editing;
-}
-
-/**
- * gda_data_model_cancel_edit
- * @model: a #GdaDataModel object.
- *
- * Cancels edition of this data model. This means that all changes
- * will be discarded, and the old data put back in the model.
- *
- * Returns: %TRUE on success, %FALSE if there was an error.
- */
-gboolean
-gda_data_model_cancel_edit (GdaDataModel *model)
-{
-	g_return_val_if_fail (GDA_IS_DATA_MODEL (model), FALSE);
-	g_return_val_if_fail (model->priv->editing, FALSE);
-
-	g_signal_emit (G_OBJECT (model), gda_data_model_signals[CANCEL_EDIT], 0);
-	model->priv->editing = FALSE;
-
-	return TRUE;
-}
-
-/**
- * gda_data_model_end_edit
- * @model: a #GdaDataModel object.
- *
- * Approves all modifications and send them to the underlying
- * data source/store.
- *
- * Returns: %TRUE on success, %FALSE if there was an error.
- */
-gboolean
-gda_data_model_end_edit (GdaDataModel *model)
-{
-	g_return_val_if_fail (GDA_IS_DATA_MODEL (model), FALSE);
-	g_return_val_if_fail (model->priv->editing, FALSE);
-
-	g_signal_emit (G_OBJECT (model), gda_data_model_signals[END_EDIT], 0);
-	model->priv->editing = FALSE;
-
-	return TRUE;
-}
 
 static gchar *
 export_to_separated (GdaDataModel *model, gchar sep)
Index: libgda/gda-data-model.h
===================================================================
RCS file: /cvs/gnome/libgda/libgda/gda-data-model.h,v
retrieving revision 1.22
diff -u -r1.22 gda-data-model.h
--- libgda/gda-data-model.h	18 Oct 2003 23:03:52 -0000	1.22
+++ libgda/gda-data-model.h	26 Oct 2003 00:49:26 -0000
@@ -55,29 +55,22 @@
 	void (* row_updated) (GdaDataModel *model, gint row);
 	void (* row_removed) (GdaDataModel *model, gint row);
 
-	void (* begin_edit) (GdaDataModel *model);
-	void (* cancel_edit) (GdaDataModel *model);
-	void (* end_edit) (GdaDataModel *model);
-
 	/* virtual methods */
 	gint (* get_n_rows) (GdaDataModel *model);
 	gint (* get_n_columns) (GdaDataModel *model);
 	GdaFieldAttributes * (* describe_column) (GdaDataModel *model, gint col);
 	const GdaRow * (* get_row) (GdaDataModel *model, gint row);
+	GdaRow * (* get_updatable_row) (GdaDataModel *model, gint row);
 	const GdaValue * (* get_value_at) (GdaDataModel *model, gint col, gint row);
 
 	gboolean (* is_editable) (GdaDataModel *model);
 	const GdaRow * (* append_row) (GdaDataModel *model, const GList *values);
-	gboolean (* remove_row) (GdaDataModel *model, const GdaRow *row);
-	gboolean (* update_row) (GdaDataModel *model, const GdaRow *row);
+	gboolean (* remove_row) (GdaDataModel *model, GdaRow *row);
+	gboolean (* update_row) (GdaDataModel *model, GdaRow *row);
 };
 
 GType               gda_data_model_get_type (void);
 
-void                gda_data_model_changed (GdaDataModel *model);
-void                gda_data_model_row_inserted (GdaDataModel *model, gint row);
-void                gda_data_model_row_updated (GdaDataModel *model, gint row);
-void                gda_data_model_row_removed (GdaDataModel *model, gint row);
 void                gda_data_model_freeze (GdaDataModel *model);
 void                gda_data_model_thaw (GdaDataModel *model);
 
@@ -85,29 +78,25 @@
 gint                gda_data_model_get_n_columns (GdaDataModel *model);
 GdaFieldAttributes *gda_data_model_describe_column (GdaDataModel *model, gint col);
 const gchar        *gda_data_model_get_column_title (GdaDataModel *model, gint col);
-void                gda_data_model_set_column_title (GdaDataModel *model, gint col, const gchar *title);
 gint                gda_data_model_get_column_position (GdaDataModel *model, const gchar *title);
+
 const GdaRow       *gda_data_model_get_row (GdaDataModel *model, gint row);
+GdaRow		   *gda_data_model_get_updatable_row (GdaDataModel *model, gint row);
 const GdaValue     *gda_data_model_get_value_at (GdaDataModel *model, gint col, gint row);
 
 gboolean            gda_data_model_is_editable (GdaDataModel *model);
 const GdaRow       *gda_data_model_append_row (GdaDataModel *model, const GList *values);
-gboolean            gda_data_model_remove_row (GdaDataModel *model, const GdaRow *row);
-gboolean            gda_data_model_update_row (GdaDataModel *model, const GdaRow *row);
+gboolean            gda_data_model_remove_row (GdaDataModel *model, GdaRow *row);
+gboolean            gda_data_model_update_row (GdaDataModel *model, GdaRow *row);
 
 typedef gboolean (* GdaDataModelForeachFunc) (GdaDataModel *model,
-					      GdaRow *row,
+					      const GdaRow *row,
 					      gpointer user_data);
 
 void                gda_data_model_foreach (GdaDataModel *model,
 					    GdaDataModelForeachFunc func,
 					    gpointer user_data);
 
-gboolean            gda_data_model_is_editing (GdaDataModel *model);
-gboolean            gda_data_model_begin_edit (GdaDataModel *model);
-gboolean            gda_data_model_cancel_edit (GdaDataModel *model);
-gboolean            gda_data_model_end_edit (GdaDataModel *model);
-
 gchar              *gda_data_model_to_comma_separated (GdaDataModel *model);
 gchar              *gda_data_model_to_tab_separated (GdaDataModel *model);
 gchar              *gda_data_model_to_xml (GdaDataModel *model, gboolean standalone);
@@ -115,10 +104,21 @@
 gboolean            gda_data_model_add_data_from_xml_node (GdaDataModel *model, xmlNodePtr node);
 
 const gchar        *gda_data_model_get_command_text (GdaDataModel *model);
-void                gda_data_model_set_command_text (GdaDataModel *model, const gchar *txt);
 GdaCommandType      gda_data_model_get_command_type (GdaDataModel *model);
-void                gda_data_model_set_command_type (GdaDataModel *model,
-						     GdaCommandType type);
+
+
+// FIXME: These hopefully will go into *_impl.h ================
+
+void                gda_data_model_changed (GdaDataModel *model);
+void                gda_data_model_row_inserted (GdaDataModel *model, gint row);
+void                gda_data_model_row_updated (GdaDataModel *model, gint row);
+void                gda_data_model_row_removed (GdaDataModel *model, gint row);
+
+void                gda_data_model_set_column_title (GdaDataModel *model, gint col, const gchar *title);
+
+void                gda_data_model_set_command_text (GdaDataModel *model, const gchar *txt);
+void                gda_data_model_set_command_type (GdaDataModel *model, GdaCommandType type);
+
 
 G_END_DECLS
 
Index: libgda/gda-row.c
===================================================================
RCS file: /cvs/gnome/libgda/libgda/gda-row.c,v
retrieving revision 1.35
diff -u -r1.35 gda-row.c
--- libgda/gda-row.c	18 Oct 2003 23:03:52 -0000	1.35
+++ libgda/gda-row.c	26 Oct 2003 00:49:26 -0000
@@ -63,7 +63,8 @@
  * @model: a #GdaDataModel.
  * @values: a list of #GdaValue's.
  *
- * Creates a #GdaRow from a list of #GdaValue's.
+ * Creates a #GdaRow from a list of #GdaValue's.  These GdaValue's are
+ * value-copied and the user are still resposible for freeing them.
  *
  * Returns: the newly created row.
  */
@@ -76,12 +77,12 @@
 
 	row = gda_row_new (model, g_list_length ((GList *) values));
 	for (i = 0, l = values; l != NULL; l = l->next, i++) {
-		const GdaValue *value = (const GdaValue *) l->data;
+		GdaValue *value = (GdaValue *) l->data;
 
 		if (value)
-			gda_value_set_from_value (gda_row_get_value (row, i), value);
+			gda_value_set_from_value (gda_row_get_updatable_value (row, i), value);
 		else
-			gda_value_set_null (gda_row_get_value (row, i));
+			gda_value_set_null (gda_row_get_updatable_value (row, i));
 	}
 
 	return row;
@@ -116,7 +117,7 @@
  * Returns: a #GdaDataModel.
  */
 GdaDataModel *
-gda_row_get_model (GdaRow *row)
+gda_row_get_model (const GdaRow *row)
 {
 	g_return_val_if_fail (row != NULL, NULL);
 	return row->model;
@@ -132,7 +133,7 @@
  * Returns: the row number, or -1 if there was an error.
  */
 gint
-gda_row_get_number (GdaRow *row)
+gda_row_get_number (const GdaRow *row)
 {
 	g_return_val_if_fail (row != NULL, -1);
 	return row->number;
@@ -166,7 +167,7 @@
  * Returns: the unique identifier for this row.
  */
 const gchar *
-gda_row_get_id (GdaRow *row)
+gda_row_get_id (const GdaRow *row)
 {
 	g_return_val_if_fail (row != NULL, NULL);
 	return (const gchar *) row->id;
@@ -195,7 +196,9 @@
  * @row: a #GdaRow (which contains #GdaValue).
  * @num: field index.
  *
- * Gets a pointer to a #GdaValue stored in a #GdaRow.
+ * Gets a const pointer to a #GdaValue stored in a #GdaRow.  If you
+ * want non-const values (possible only from non-const #GdaRow), use
+ * #gda_row_get_updatable_value.
  *
  * This is a pointer to the internal array of values. Don't try to free
  * or modify it!
@@ -203,7 +206,27 @@
  * Returns: a pointer to the #GdaValue in the position @num of @row.
  */
 G_CONST_RETURN GdaValue *
-gda_row_get_value (GdaRow *row, gint num)
+gda_row_get_value (const GdaRow *row, gint num)
+{
+	g_return_val_if_fail (row != NULL, NULL);
+	g_return_val_if_fail (num >= 0 && num < row->nfields, NULL);
+
+	return &row->fields[num];
+}
+
+/**
+ * gda_row_get_updatable_value
+ * @row: a #GdaRow (which contains #GdaValue).
+ * @num: field index.
+ *
+ * Gets a pointer to a #GdaValue stored in a #GdaRow.
+ *
+ * This is a pointer to the internal array of values. Don't try to free it!
+ * 
+ * Returns: a pointer to the #GdaValue in the position @num of @row.
+ */
+GdaValue *
+gda_row_get_updatable_value (GdaRow *row, gint num)
 {
 	g_return_val_if_fail (row != NULL, NULL);
 	g_return_val_if_fail (num >= 0 && num < row->nfields, NULL);
@@ -211,6 +234,7 @@
 	return &row->fields[num];
 }
 
+
 /**
  * gda_row_get_length
  * @row: a #GdaRow.
@@ -218,7 +242,7 @@
  * Returns: the number of columns that the @row has.
  */
 gint
-gda_row_get_length (GdaRow *row)
+gda_row_get_length (const GdaRow *row)
 {
 	g_return_val_if_fail (row != NULL, 0);
 	return row->nfields;
Index: libgda/gda-row.h
===================================================================
RCS file: /cvs/gnome/libgda/libgda/gda-row.h,v
retrieving revision 1.26
diff -u -r1.26 gda-row.h
--- libgda/gda-row.h	12 May 2003 22:58:05 -0000	1.26
+++ libgda/gda-row.h	26 Oct 2003 00:49:26 -0000
@@ -31,16 +31,22 @@
 typedef struct _GdaDataModel GdaDataModel;
 typedef struct _GdaRow GdaRow;
 
-GdaRow                  *gda_row_new (GdaDataModel *model, gint count);                                                                                
-GdaRow                  *gda_row_new_from_list (GdaDataModel *model, const GList *values);                                                             
-void                     gda_row_free (GdaRow *row);                                                                                                   
-GdaDataModel            *gda_row_get_model (GdaRow *row);                                                                                              
-gint                     gda_row_get_number (GdaRow *row);                                                                                             
-void                     gda_row_set_number (GdaRow *row, gint number);                                                                                
-const gchar             *gda_row_get_id (GdaRow *row);                                                                                                 
-void                     gda_row_set_id (GdaRow *row, const gchar *id);                                                                                
-G_CONST_RETURN GdaValue *gda_row_get_value (GdaRow *row, gint num);                                                                                    
-gint	                 gda_row_get_length (GdaRow *row);                                                                                             
+GdaDataModel            *gda_row_get_model (const GdaRow *row); 
+gint                     gda_row_get_number (const GdaRow *row); 
+gint	                 gda_row_get_length (const GdaRow *row); 
+G_CONST_RETURN GdaValue *gda_row_get_value (const GdaRow *row, gint num); 
+GdaValue		*gda_row_get_updatable_value (GdaRow *row, gint num); 
+
+
+// FIXME These hopefully will go into *_impl.h ================
+
+GdaRow                  *gda_row_new (GdaDataModel *model, gint count); 
+GdaRow                  *gda_row_new_from_list (GdaDataModel *model, const GList *values); 
+void                     gda_row_free (GdaRow *row); 
+void                     gda_row_set_number (GdaRow *row, gint number); 
+
+void                     gda_row_set_id (GdaRow *row, const gchar *id); 
+const gchar             *gda_row_get_id (const GdaRow *row); 
 
 G_END_DECLS
 
Index: providers/ldap/gda-ldap-recordset.c
===================================================================
RCS file: /cvs/gnome/libgda/providers/ldap/gda-ldap-recordset.c,v
retrieving revision 1.2
diff -u -r1.2 gda-ldap-recordset.c
--- providers/ldap/gda-ldap-recordset.c	16 Jan 2003 00:10:33 -0000	1.2
+++ providers/ldap/gda-ldap-recordset.c	26 Oct 2003 00:49:26 -0000
@@ -274,7 +274,6 @@
 	g_return_val_if_fail (GDA_IS_LDAP_RECORDSET (recset), NULL);
 	g_return_val_if_fail (values != NULL, NULL);
 	g_return_val_if_fail (gda_data_model_is_editable (model), NULL);
-	g_return_val_if_fail (gda_data_model_is_editing (model), NULL);
 
 /*	cols = ldap_num_fields (recset->ldap_res);*/
 	if (cols != g_list_length ((GList *) values)) {
Index: providers/msql/gda-msql-recordset.c
===================================================================
RCS file: /cvs/gnome/libgda/providers/msql/gda-msql-recordset.c,v
retrieving revision 1.3
diff -u -r1.3 gda-msql-recordset.c
--- providers/msql/gda-msql-recordset.c	19 Sep 2003 18:43:08 -0000	1.3
+++ providers/msql/gda-msql-recordset.c	26 Oct 2003 00:49:26 -0000
@@ -219,10 +219,9 @@
   GdaMsqlRecordset *rs=(GdaMsqlRecordset*)model;
   
   if ((!GDA_IS_MSQL_RECORDSET(rs)) ||
-      (!values) || (!gda_data_model_is_editable(model)) ||
-      (!gda_data_model_is_editing(model))) {
+      (!values) || (!gda_data_model_is_editable(model)))
     return NULL;
-  }
+
   cols=msqlNumFields(rs->res);
   if (cols!=g_list_length((GList*)values)) {
     gda_connection_add_error_string(rs->cnc,
Index: providers/mysql/gda-mysql-provider.c
===================================================================
RCS file: /cvs/gnome/libgda/providers/mysql/gda-mysql-provider.c,v
retrieving revision 1.46
diff -u -r1.46 gda-mysql-provider.c
--- providers/mysql/gda-mysql-provider.c	16 Oct 2003 22:58:06 -0000	1.46
+++ providers/mysql/gda-mysql-provider.c	26 Oct 2003 00:49:27 -0000
@@ -1138,20 +1138,7 @@
 
 		mysql_data_seek (mysql_res, r);
 		mysql_row = mysql_fetch_row (mysql_res);
-		if (!mysql_row) {
-			mysql_free_result (mysql_res);
-			g_object_unref (G_OBJECT (recset));
-
-			return NULL;
-		}
-
 		value_list = field_row_to_value_list (mysql_row);
-		if (!value_list) {
-			mysql_free_result (mysql_res);
-			g_object_unref (G_OBJECT (recset));
-
-			return NULL;
-		}
 
 		gda_data_model_append_row (GDA_DATA_MODEL (recset),
 					   (const GList *) value_list);
Index: providers/mysql/gda-mysql-recordset.c
===================================================================
RCS file: /cvs/gnome/libgda/providers/mysql/gda-mysql-recordset.c,v
retrieving revision 1.27
diff -u -r1.27 gda-mysql-recordset.c
--- providers/mysql/gda-mysql-recordset.c	16 Oct 2003 22:58:06 -0000	1.27
+++ providers/mysql/gda-mysql-recordset.c	26 Oct 2003 00:49:27 -0000
@@ -35,6 +35,13 @@
 					    GdaMysqlRecordsetClass *klass);
 static void gda_mysql_recordset_finalize   (GObject *object);
 
+void	    fill_gda_value (GdaValue *gda_value, enum enum_field_types type, 
+			    gchar *value, unsigned long length);
+void	    find_key_columns   (GdaMysqlRecordset *recset);
+void	    printfa_cols (GString *str, GdaMysqlRecordset *recset, 
+			  GdaRow *row, gint cols[], gboolean where);
+void	    set_id (GdaMysqlRecordset *recset, GdaRow *row);
+
 static GObjectClass *parent_class = NULL;
 
 /*
@@ -257,6 +264,20 @@
 	return (const GdaRow *) fields;
 }
 
+static GdaRow *
+gda_mysql_recordset_get_updatable_row (GdaDataModel *model, gint row)
+{
+	g_return_val_if_fail (GDA_IS_MYSQL_RECORDSET (model), NULL);
+	GdaMysqlRecordset *recset = (GdaMysqlRecordset*) model;
+
+	find_key_columns (recset);
+	if (recset->key[0]==0)  return NULL;  // not updatable
+	GdaRow *gda_row = (GdaRow*) gda_mysql_recordset_get_row (model, row);
+	if (gda_row && !gda_row_get_id (gda_row))
+		set_id (recset, gda_row);
+	return gda_row;
+}
+
 static const GdaValue *
 gda_mysql_recordset_get_value_at (GdaDataModel *model, gint col, gint row)
 {
@@ -300,7 +321,6 @@
 	g_return_val_if_fail (GDA_IS_MYSQL_RECORDSET (recset), NULL);
 	g_return_val_if_fail (values != NULL, NULL);
 	g_return_val_if_fail (gda_data_model_is_editable (model), NULL);
-	g_return_val_if_fail (gda_data_model_is_editing (model), NULL);
 
 	cols = mysql_num_fields (recset->mysql_res);
 	if (cols != g_list_length ((GList *) values)) {
@@ -354,7 +374,7 @@
 	sql = g_string_append (sql, ")");
 
 	/* execute the UPDATE command */
-	rc = mysql_real_query (recset->mysql_res->handle, sql->str, strlen (sql->str));
+	rc = mysql_real_query (recset->mysql_res->handle, sql->str, sql->len);
 	g_string_free (sql, TRUE);
 	if (rc != 0) {
 		gda_connection_add_error (
@@ -370,15 +390,90 @@
 }
 
 static gboolean
-gda_mysql_recordset_remove_row (GdaDataModel *model, const GdaRow *row)
+gda_mysql_recordset_remove_row (GdaDataModel *model, GdaRow *row)
 {
-	return FALSE;
+	GString *str;
+	const gchar *id;
+	gint rc;
+
+	g_return_val_if_fail (GDA_IS_MYSQL_RECORDSET (model), FALSE);
+	g_return_val_if_fail (model == gda_row_get_model(row), FALSE);
+
+	GdaMysqlRecordset *recset = (GdaMysqlRecordset*) model;
+	id = gda_row_get_id(row);
+	g_return_val_if_fail (id != NULL, FALSE);
+
+	str = g_string_sized_new (200);
+	g_string_sprintf (str, "DELETE from %s WHERE %s", recset->table_name, id);
+	
+	g_printerr ("\nquery:  %s\n", str->str);  //FIXME: just debugging
+	rc = mysql_real_query (recset->mysql, str->str, str->len);
+	g_string_free (str, TRUE);
+	if (rc!=0 || mysql_affected_rows(recset->mysql)!=1) {
+		gda_connection_add_error (
+			recset->cnc, gda_mysql_make_error (recset->mysql_res->handle));
+		return FALSE;
+	}
+	// FIXME what to do with the record now?
+	return TRUE;
 }
 
 static gboolean
-gda_mysql_recordset_update_row (GdaDataModel *model, const GdaRow *row)
+gda_mysql_recordset_update_row (GdaDataModel *model, GdaRow *row)
 {
-	return FALSE;
+	GString *str;
+	const gchar *id;
+	gint n;
+	gint rc;
+	gint *all;
+
+	g_return_val_if_fail (GDA_IS_MYSQL_RECORDSET (model), FALSE);
+	g_return_val_if_fail (model == gda_row_get_model(row), FALSE);
+
+	GdaMysqlRecordset *recset = (GdaMysqlRecordset*) model;
+	id = gda_row_get_id (row);
+	g_return_val_if_fail (id != NULL, FALSE);
+
+	str = g_string_sized_new (200);
+	g_string_sprintf (str, "UPDATE %s SET ", recset->table_name);
+	n = gda_row_get_length (row);
+	all = g_new (gint, n+1);
+	all[0] = n;
+	int i;
+	for (i=0; i<n; i++)  all[i+1] = i;
+	printfa_cols (str, recset, row, all, FALSE);
+	g_free (all);
+	g_string_sprintfa (str, " WHERE %s", id);
+
+	g_printerr ("\nquery:  %s\n", str->str);  //FIXME: just debugging
+	rc = mysql_real_query (recset->mysql, str->str, str->len);
+	g_string_free (str, TRUE);
+	if (rc!=0 || mysql_affected_rows(recset->mysql)!=1) {
+		gda_connection_add_error (
+			recset->cnc, gda_mysql_make_error (recset->mysql_res->handle));
+		return FALSE;
+	}
+	
+	// In NOT NULL columns, MySQL quietly converts NULL to 0, 0.0, or "".
+	// Our column is UNIQUE so this probably won't happen often, but handle
+	// it anyway.
+	for (i=1; i<=recset->key[0]; i++) {
+		gint c = recset->key[i];
+		GdaValue *value = gda_row_get_updatable_value (row, c);
+		if (value->type != GDA_VALUE_TYPE_NULL) continue;
+		MYSQL_FIELD *field = mysql_fetch_field_direct (recset->mysql_res, i);
+		GdaValueType type = gda_mysql_type_to_gda (field->type);
+		if (!gda_value_set_from_string (value, "", type) &&
+		    !gda_value_set_from_string (value, "0", type))
+			// should never get to here, but reluctant to use g_assert()
+			g_printerr ("You've just found a bug.  Please let us know how to get to this message.  -- gnome-db-list gnome org");
+		// note that we'd never get TIMESTAMP in our key[] since it's always
+		// a NULL-ok column.
+	}
+
+	// FIXME: should we refetch this record for the new AUTO INCREMENT, TIMESTAMP values?
+	set_id (recset, row);	// our key column might have changed
+	return TRUE;
 }
 
 static void
@@ -394,6 +489,7 @@
 	model_class->get_n_columns = gda_mysql_recordset_get_n_columns;
 	model_class->describe_column = gda_mysql_recordset_describe_column;
 	model_class->get_row = gda_mysql_recordset_get_row;
+	model_class->get_updatable_row = gda_mysql_recordset_get_updatable_row;
 	model_class->get_value_at = gda_mysql_recordset_get_value_at;
 	model_class->is_editable = gda_mysql_recordset_is_editable;
 	model_class->append_row = gda_mysql_recordset_append_row;
@@ -420,6 +516,8 @@
 
 	mysql_free_result (recset->mysql_res);
 	recset->mysql_res = NULL;
+	g_free (recset->table_name);
+	g_free (recset->key);
 
 	while (recset->rows->len > 0) {
 		GdaRow * row = (GdaRow *) g_ptr_array_index (recset->rows, 0);
@@ -469,10 +567,13 @@
 	recset = g_object_new (GDA_TYPE_MYSQL_RECORDSET, NULL);
 	recset->cnc = cnc;
 	recset->mysql_res = mysql_res;
+	recset->mysql = mysql;
 	if (mysql_res == NULL) {
 		recset->affected_rows = mysql_affected_rows (mysql);
 		return recset;
 	}
+	recset->key = NULL;
+	recset->table_name = NULL;
 
 	mysql_fields = mysql_fetch_fields (recset->mysql_res);
 	if (mysql_fields != NULL) {
@@ -485,4 +586,150 @@
 	}
 
 	return recset;
+}
+
+
+
+
+
+
+
+/*
+ * initialize recset->key
+ *
+ * We can't simply look at unique, non_null, or primary_key flags because
+ * a key might contain more than one column.
+ */
+void find_key_columns (GdaMysqlRecordset *recset)
+{
+	if (recset->key)  return;  // do it only once
+
+	MYSQL_RES *mysql_res;
+	const char *table;
+	gint i, n;
+
+
+	{// check if all columns come from the same table
+		MYSQL_FIELD *mysql_fields = mysql_fetch_fields (recset->mysql_res);
+		// use char and strcmp (not glib's equivalences) so they match mysql api
+		table = mysql_fields[0].table;
+		n = mysql_num_fields (recset->mysql_res);
+		for (i=0; i<n; i++)
+			if (!mysql_fields[i].table || strcmp (table, mysql_fields[i].table))
+				break;
+		if (i<n) {
+			recset->key = g_new0 (gint, 1);  // {0}
+			return;
+		}
+		recset->table_name = g_strdup (table);
+	}
+	
+	{// query for all keys
+		gchar *cmd_str = g_strdup_printf ("SHOW INDEX FROM %s", table);
+		gint rc = mysql_real_query (recset->mysql, cmd_str, strlen (cmd_str));
+		g_free (cmd_str);
+		if (rc != 0) {
+			gda_connection_add_error (recset->cnc, gda_mysql_make_error (recset->mysql));
+			return;
+		}
+		mysql_res = mysql_store_result (recset->mysql);
+	}
+	
+	{// look for usable key
+		GdaDataModel *model = (GdaDataModel*) recset;
+		gboolean usable = FALSE;
+		char *key_name = "";
+		int i0 = 0;
+		n = mysql_num_rows (mysql_res);
+		for (i=0; i<n; i++) {
+			MYSQL_ROW row;
+
+			mysql_data_seek (mysql_res, i);
+			row = mysql_fetch_row (mysql_res);
+			if (strcmp (key_name, row[2])) { // 'Key_name' column
+				if (usable) break;
+				key_name = row[2];
+				usable = TRUE;
+				i0 = i;
+			}
+			if (strcmp (row[1], "0")			// not unique
+			    || (row[9] && 0==strcmp (row[9], "YES"))	// allow null
+			    || -1==gda_data_model_get_column_position (model, row[4]) ) // column not selected
+				usable = FALSE ;
+		}
+		if (usable) {
+			gint *t = g_new(int, i-i0+1);
+			t[0] = i-i0;
+			int j;
+			for (j=0; j<t[0]; j++) {
+				mysql_data_seek (mysql_res, i0+j);
+				MYSQL_ROW row = mysql_fetch_row (mysql_res);
+				t[j+1] = gda_data_model_get_column_position (model, row[4]);
+			}
+			recset->key = t;
+		} else
+			recset->key = g_new0 (gint, 1);  // {0}
+	}
+
+	mysql_free_result (mysql_res);
+}
+
+
+
+/* append str with
+ *
+ *     cols[1]="...", cols[2]="...", ... cols[n]="..."
+ *
+ * with n:=cols[0], and using row's data
+ *
+ * @where:  TRUE,  delimiter = " AND "      use " IS NULL"
+ *          FALSE, delimiter = ", "         use "=NULL"
+ */
+void
+printfa_cols (GString *str, GdaMysqlRecordset *recset, GdaRow *row, gint cols[], gboolean where)
+{
+	MYSQL_FIELD *fields = mysql_fetch_fields (recset->mysql_res);
+	int i;
+	for (i = 1; i <= cols[0]; i++) {
+		int c = cols[i];
+		const GdaValue *value = gda_row_get_value (row, c);
+		gchar *val, *val2;
+		glong len;
+
+		g_string_append (str, fields[c].name);
+
+		switch (gda_value_get_type (value)) {
+		case GDA_VALUE_TYPE_NULL:
+			g_string_sprintfa (str, where?" IS NULL":"=NULL");
+			break;
+		case GDA_VALUE_TYPE_BINARY:
+			val = gda_value_get_binary (value, &len);
+			// size according to mysql_real_escape_string doc
+			val2 = g_new(gchar, 2*len+1);  
+			mysql_real_escape_string (recset->mysql, val2, val, len);
+			g_string_sprintfa (str, "='%s'", val2);
+			g_free (val2);
+			break;
+		// FIXME: haven't figured out what to do with GDA_VALUE_TYPE_BLOB
+		default:
+			val = gda_value_stringify (value);
+			len = strlen(val);
+			val2 = g_new(gchar, 2*len+1);  
+			mysql_real_escape_string (recset->mysql, val2, val, len);
+			g_free (val);
+			g_string_sprintfa (str, "='%s'", val2);
+			g_free (val2);
+		}
+
+		if (i < cols[0])  g_string_append (str, where?" AND ":", ");
+	}
+}
+
+void
+set_id (GdaMysqlRecordset *recset, GdaRow *row)
+{
+	GString *str = g_string_sized_new (100);
+	printfa_cols (str, recset, row, recset->key, TRUE);
+	gda_row_set_id (row, str->str);
+	g_string_free (str, TRUE);
 }
Index: providers/mysql/gda-mysql-recordset.h
===================================================================
RCS file: /cvs/gnome/libgda/providers/mysql/gda-mysql-recordset.h,v
retrieving revision 1.7
diff -u -r1.7 gda-mysql-recordset.h
--- providers/mysql/gda-mysql-recordset.h	15 Aug 2003 12:53:40 -0000	1.7
+++ providers/mysql/gda-mysql-recordset.h	26 Oct 2003 00:49:27 -0000
@@ -44,7 +44,15 @@
 	GPtrArray *rows;
 	GdaConnection *cnc;
 	MYSQL_RES *mysql_res;
+	MYSQL *mysql;
 	gint affected_rows; /* when mysql_res is NULL, get_n_rows returns this number */
+	
+	// these are set only if the table is updatable 
+	gchar *table_name;
+	// FIXME: better use prepared statement if both server and C api's versions >= 4.1.0 
+	gint *key;	// column(s) of a non-null unique key.  Used for update, delete.
+			// keys[0] == number of columns in that key == size of key[]-1
+			// NULL means uninitialized,  {0} means can't update
 };
 
 struct _GdaMysqlRecordsetClass {
Index: providers/oracle/gda-oracle-recordset.c
===================================================================
RCS file: /cvs/gnome/libgda/providers/oracle/gda-oracle-recordset.c,v
retrieving revision 1.10
diff -u -r1.10 gda-oracle-recordset.c
--- providers/oracle/gda-oracle-recordset.c	3 Sep 2003 10:17:31 -0000	1.10
+++ providers/oracle/gda-oracle-recordset.c	26 Oct 2003 00:49:28 -0000
@@ -458,7 +458,6 @@
 	g_return_val_if_fail (GDA_IS_ORACLE_RECORDSET (recset), NULL);
 	g_return_val_if_fail (values != NULL, NULL);
 	g_return_val_if_fail (gda_data_model_is_editable (model), NULL);
-	g_return_val_if_fail (gda_data_model_is_editing (model), NULL);
 	g_return_val_if_fail (recset->priv != NULL, 0);
 
 	priv_data = recset->priv;



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