libgda r3373 - in trunk: . doc/C doc/C/tmpl libgda libgda/providers-support libgda/sqlite libgda/thread-wrapper po providers/postgres tests/multi-threading tools
- From: vivien svn gnome org
- To: svn-commits-list gnome org
- Subject: libgda r3373 - in trunk: . doc/C doc/C/tmpl libgda libgda/providers-support libgda/sqlite libgda/thread-wrapper po providers/postgres tests/multi-threading tools
- Date: Tue, 7 Apr 2009 19:10:50 +0000 (UTC)
Author: vivien
Date: Tue Apr 7 19:10:50 2009
New Revision: 3373
URL: http://svn.gnome.org/viewvc/libgda?rev=3373&view=rev
Log:
2009-04-07 Vivien Malerba <malerba gnome-db org>
* configure.in:
* libgda/Makefile.am:
* libgda/thread-wrapper/Makefile.am: new directory
* libgda/thread-wrapper/gda-thread-wrapper.[ch]: new object which
allows one to execute functions in a private sub thread from any
other thread.
* libgda/thread-wrapper/gda-thread-provider.[ch]:
* libgda/thread-wrapper/gda-thread-recordset.[ch]: new built-in
pseudo provider to constrain a GdaConnection's usage within a private
sub thread, making its usage completely thread safe
* libgda/gda-connection-sqlite.h: file renamed to
libgda/gda-connection-internal.h
* libgda/gda-connection.[ch]:
- added a GDA_CONNECTION_OPTIONS_THREAD_SAFE flag to use when opening
a connection
- added asynchronous statement execution methods:
gda_connection_async_statement_execute(), gda_connection_async_fetch_result()
and gda_connection_async_cancel()
- the GdaConnection can now be created by the new GdaThreadProvider pseudo
provider to wrap a real GdaConnection object in a private sub thread
* libgda/providers-support/gda-data-select-priv.h:
* libgda/gda-data-select.c: adaptations to be used by the new GdaThreadRecordset
* libgda/gda-meta-store.c:
- now thread safe
- corrections to gda_meta_store_schema_get_structure()
* libgda/gda-server-provider.[ch]: new virtual method to handle async. requests
* libgda/sqlite/gda-sqlite-recordset.c: minor correction
* po/POTFILES.in: added new files
* providers/postgres/gda-postgres-util.c:
* providers/postgres/gda-postgres.h: make sure a PostgreSQL connection is only
used by one statement at a time
* doc/C: doc. updates
* tests/multi-threading/dummy-object.[ch]:
* tests/multi-threading/check_wrapper.c: new test for the GdaThreadWrapper object
* tests/multi-threading/check_threaded_cnc: new test for connections opened with
the GDA_CONNECTION_OPTIONS_THREAD_SAFE flag
* tools/web-server.c: don't check for uint8_t
Added:
trunk/doc/C/tmpl/gda-thread-connection.sgml
trunk/doc/C/tmpl/gda-thread-wrapper.sgml
trunk/libgda/gda-connection-internal.h (contents, props changed)
- copied, changed from r3371, /trunk/libgda/gda-connection-sqlite.h
trunk/libgda/thread-wrapper/ (props changed)
trunk/libgda/thread-wrapper/Makefile.am
trunk/libgda/thread-wrapper/gda-thread-provider.c
trunk/libgda/thread-wrapper/gda-thread-provider.h
trunk/libgda/thread-wrapper/gda-thread-recordset.c
trunk/libgda/thread-wrapper/gda-thread-recordset.h
trunk/libgda/thread-wrapper/gda-thread-wrapper.c
trunk/libgda/thread-wrapper/gda-thread-wrapper.h
trunk/libgda/thread-wrapper/thread-wrapper.dia (contents, props changed)
trunk/tests/multi-threading/check_threaded_cnc.c
trunk/tests/multi-threading/check_wrapper.c
trunk/tests/multi-threading/dummy-object.c
trunk/tests/multi-threading/dummy-object.h
Removed:
trunk/libgda/gda-connection-sqlite.h
Modified:
trunk/ChangeLog
trunk/configure.in
trunk/doc/C/libgda-4.0-docs.sgml
trunk/doc/C/libgda-4.0-sections.txt
trunk/doc/C/libgda-4.0.types.in
trunk/doc/C/tmpl/gda-connection.sgml
trunk/doc/C/tmpl/gda-data-model-iter.sgml
trunk/doc/C/tmpl/gda-meta-store.sgml
trunk/doc/C/tmpl/gda-server-provider.sgml
trunk/doc/C/tmpl/gda-sql-parser.sgml
trunk/libgda/Makefile.am
trunk/libgda/gda-connection.c
trunk/libgda/gda-connection.h
trunk/libgda/gda-data-select.c
trunk/libgda/gda-meta-store.c
trunk/libgda/gda-server-provider.c
trunk/libgda/gda-server-provider.h
trunk/libgda/libgda.symbols
trunk/libgda/providers-support/gda-data-select-priv.h
trunk/libgda/sqlite/gda-sqlite-recordset.c
trunk/po/POTFILES.in
trunk/providers/postgres/gda-postgres-util.c
trunk/providers/postgres/gda-postgres.h
trunk/tests/multi-threading/ (props changed)
trunk/tests/multi-threading/Makefile.am
trunk/tests/multi-threading/common.c
trunk/tests/multi-threading/common.h
trunk/tools/web-server.c
Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in (original)
+++ trunk/configure.in Tue Apr 7 19:10:50 2009
@@ -50,6 +50,7 @@
AC_SUBST(INTLTOOL_XML_RULE)
AC_CHECK_SIZEOF(unsigned int,0)
AC_CHECK_SIZEOF(unsigned long int,0)
+AC_CHECK_TYPE(uint8_t, unsigned char)
dnl **********************
dnl Cross compilation test
@@ -1485,6 +1486,7 @@
libgda/sqlite/Makefile
libgda/sqlite/sqlite-src/Makefile
libgda/sqlite/virtual/Makefile
+libgda/thread-wrapper/Makefile
providers/Makefile
providers/bdb/Makefile
providers/bdb/libgda-bdb-4.0.pc
Modified: trunk/doc/C/libgda-4.0-docs.sgml
==============================================================================
--- trunk/doc/C/libgda-4.0-docs.sgml (original)
+++ trunk/doc/C/libgda-4.0-docs.sgml Tue Apr 7 19:10:50 2009
@@ -124,6 +124,7 @@
<!ENTITY libgda-GdaTreeMgrTables SYSTEM "xml/gda-tree-mgr-tables.xml">
<!ENTITY libgda-GdaTreeMgrColumns SYSTEM "xml/gda-tree-mgr-columns.xml">
<!ENTITY libgda-GdaSqlBuilder SYSTEM "xml/gda-sql-builder.xml">
+<!ENTITY libgda-GdaThreadWrapper SYSTEM "xml/gda-thread-wrapper.xml">
]>
<book id="index">
@@ -1110,12 +1111,17 @@
&libgda-GdaServerOperationSequences;
</chapter>
+ <chapter id="multi-threading">
+ <title>Multi threading</title>
+ &libgda-GdaMutex;
+ &libgda-GdaLockable;
+ &libgda-GdaThreadWrapper;
+ </chapter>
+
<chapter id="misc">
<title>Miscellaneous</title>
&libgda-util;
&libgda-log;
- &libgda-GdaMutex;
- &libgda-GdaLockable;
&GdaAttributesManager;
</chapter>
</part>
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 Tue Apr 7 19:10:50 2009
@@ -131,6 +131,10 @@
gda_connection_rollback_savepoint
gda_connection_delete_savepoint
<SUBSECTION>
+gda_connection_async_statement_execute
+gda_connection_async_fetch_result
+gda_connection_async_cancel
+<SUBSECTION>
gda_connection_get_transaction_status
gda_connection_get_options
gda_connection_get_provider
@@ -1654,3 +1658,26 @@
GDA_TYPE_SQL_BUILDER
gda_sql_builder_get_type
</SECTION>
+
+<SECTION>
+<FILE>gda-thread-wrapper</FILE>
+<TITLE>GdaThreadWrapper</TITLE>
+GdaThreadWrapper
+gda_thread_wrapper_new
+GdaThreadWrapperFunc
+gda_thread_wrapper_execute
+GdaThreadWrapperVoidFunc
+gda_thread_wrapper_execute_void
+gda_thread_wrapper_iterate
+gda_thread_wrapper_fetch_result
+gda_thread_wrapper_get_waiting_size
+GdaThreadWrapperCallback
+gda_thread_wrapper_connect_raw
+gda_thread_wrapper_disconnect
+<SUBSECTION Standard>
+GDA_THREAD_WRAPPER
+GDA_THREAD_WRAPPER_GET_CLASS
+GDA_IS_THREAD_WRAPPER
+GDA_TYPE_THREAD_WRAPPER
+gda_thread_wrapper_get_type
+</SECTION>
Modified: trunk/doc/C/libgda-4.0.types.in
==============================================================================
--- trunk/doc/C/libgda-4.0.types.in (original)
+++ trunk/doc/C/libgda-4.0.types.in Tue Apr 7 19:10:50 2009
@@ -1,6 +1,7 @@
#include <libgda/libgda.h>
#include <libgda/gda-blob-op.h>
#include <libgda/sql-parser/gda-sql-parser.h>
+#include <libgda/thread-wrapper/gda-thread-wrapper.h>
#include <virtual/libgda-virtual.h>
#include <engine/gda-report-engine.h>
#include <gda-report-document.h>
@@ -64,3 +65,4 @@
gda_tree_mgr_tables_get_type
gda_tree_node_get_type
gda_sql_builder_get_type
+gda_thread_wrapper_get_type
\ No newline at end of file
Modified: trunk/doc/C/tmpl/gda-connection.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-connection.sgml (original)
+++ trunk/doc/C/tmpl/gda-connection.sgml Tue Apr 7 19:10:50 2009
@@ -15,7 +15,34 @@
Use the connection object to execute statements, use transactions, get meta data information, ...
</para>
<para>
- The #GdaConnection object implements its own locking mechanism so it is thread-safe.
+ If supported by the database provider being used, statements can be executed asynchronously instead of
+ blocking the execution thread untill the execution of a statement is finished. Each database provider
+ is free to implement this feature as it wishes (using the API or using threads). The steps involved to
+ execute a statement are then:
+ <itemizedlist>
+ <listitem><para>Request the statement execution using
+ <link linkend="gda-connection-async-statement-execute">gda_connection_async_statement_execute() which returns an
+ execution ID to be used to identify a specific request</link></para></listitem>
+ <listitem><para>Do some usefull things (that is why async. statements' excution are for)</para></listitem>
+ <listitem><para>Use one or more times
+ <link linkend="gda-connection-async-fetch-result">gda_connection_async_fetch_result()</link> to see
+ if the execution is finished, using the request ID</para></listitem>
+ <listitem><para>Use <link linkend="gda-connection-async-cancel">gda_connection_async_cancel()</link> to cancel
+ the execution of a statement</para></listitem>
+ </itemizedlist>
+</para>
+<para>
+ The #GdaConnection object implements its own locking mechanism so it is thread-safe. However ad some database
+ providers rely on an API which does not support threads or supports it only partially, the connections
+ opened using those providers will only be accessible from the thread which created them (any other thread will
+ be blocked trying to access the connection, use the
+ <link linkend="gda-lockable-try-lock">gda_lockable_try_lock()</link> method to check it the connection
+ is useable from a thread).
+</para>
+<para>
+ If a connection really needs to be accessed by several threads at once, then it is possible to pass the
+ #GDA_CONNECTION_OPTIONS_THREAD_SAFE flag when opening it. This flag requests that the real connection
+ be created and really accessed in a <emphasis>private</emphasis> sub thread.
</para>
<!-- ##### SECTION See_Also ##### -->
@@ -90,6 +117,11 @@
</para>
+<!-- ##### ARG GdaConnection:is-wrapper ##### -->
+<para>
+
+</para>
+
<!-- ##### ARG GdaConnection:meta-store ##### -->
<para>
@@ -112,11 +144,12 @@
<!-- ##### ENUM GdaConnectionOptions ##### -->
<para>
-
+ Specifies some aspects of a connection when opening it.
</para>
- GDA_CONNECTION_OPTIONS_NONE:
- GDA_CONNECTION_OPTIONS_READ_ONLY:
+ GDA_CONNECTION_OPTIONS_NONE: no specific aspect
+ GDA_CONNECTION_OPTIONS_READ_ONLY: the connection to open should be in a read-only mode (this policy is not correctly enforced at the moment)
+ GDA_CONNECTION_OPTIONS_THREAD_SAFE: this flag specifies that the connection to open will be used by several threads at once so it has to be thread safe
<!-- ##### ENUM GdaConnectionError ##### -->
<para>
@@ -130,6 +163,8 @@
@GDA_CONNECTION_NO_PROVIDER_SPEC_ERROR:
@GDA_CONNECTION_OPEN_ERROR:
@GDA_CONNECTION_STATEMENT_TYPE_ERROR:
+ GDA_CONNECTION_CANT_LOCK_ERROR:
+ GDA_CONNECTION_TASK_NOT_FOUND_ERROR:
<!-- ##### MACRO GDA_CONNECTION_ERROR ##### -->
<para>
@@ -389,6 +424,44 @@
@Returns:
+<!-- ##### FUNCTION gda_connection_async_statement_execute ##### -->
+<para>
+
+</para>
+
+ cnc:
+ stmt:
+ params:
+ model_usage:
+ col_types:
+ need_last_insert_row:
+ error:
+ Returns:
+
+
+<!-- ##### FUNCTION gda_connection_async_fetch_result ##### -->
+<para>
+
+</para>
+
+ cnc:
+ task_id:
+ last_insert_row:
+ error:
+ Returns:
+
+
+<!-- ##### FUNCTION gda_connection_async_cancel ##### -->
+<para>
+
+</para>
+
+ cnc:
+ task_id:
+ error:
+ Returns:
+
+
<!-- ##### FUNCTION gda_connection_get_transaction_status ##### -->
<para>
Modified: trunk/doc/C/tmpl/gda-data-model-iter.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-data-model-iter.sgml (original)
+++ trunk/doc/C/tmpl/gda-data-model-iter.sgml Tue Apr 7 19:10:50 2009
@@ -33,7 +33,7 @@
gda_data_model_iter_move_next() which moves to the first row).
</para>
<para>
- The gda_data_model_iter_move_at_row() method, if the iterator can be moved both forward and backwards, can move the
+ The gda_data_model_iter_move_to_row() method, if the iterator can be moved both forward and backwards, can move the
iterator to a specific row (sometimes faster than moving it forward or backwards a number of times).
</para>
<para>
Modified: trunk/doc/C/tmpl/gda-meta-store.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-meta-store.sgml (original)
+++ trunk/doc/C/tmpl/gda-meta-store.sgml Tue Apr 7 19:10:50 2009
@@ -18,6 +18,9 @@
single file (using an SQLite database), an entirely in memory database (also using an SQLite database), or
a more conventional backend such as a PostgreSQL database for a shared dictionary on a server.
</para>
+<para>
+ The #GdaMetaStore object is thread safe.
+</para>
<!-- ##### SECTION See_Also ##### -->
<para>
Modified: trunk/doc/C/tmpl/gda-server-provider.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-server-provider.sgml (original)
+++ trunk/doc/C/tmpl/gda-server-provider.sgml Tue Apr 7 19:10:50 2009
@@ -65,7 +65,7 @@
@create_connection:
@meta_funcs:
@xa_funcs:
- _gda_reserved1:
+ handle_async:
@_gda_reserved2:
@_gda_reserved3:
@_gda_reserved4:
Modified: trunk/doc/C/tmpl/gda-sql-parser.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-sql-parser.sgml (original)
+++ trunk/doc/C/tmpl/gda-sql-parser.sgml Tue Apr 7 19:10:50 2009
@@ -85,7 +85,7 @@
The #GdaSqlParser object internally uses a LEMON generated parser (the same as the one used by SQLite).
</para>
<para>
- The #GdaConnection object implements its own locking mechanism so it is thread-safe.
+ The #GdaSqlParser object implements its own locking mechanism so it is thread-safe.
</para>
<!-- ##### SECTION See_Also ##### -->
Added: trunk/doc/C/tmpl/gda-thread-connection.sgml
==============================================================================
--- (empty file)
+++ trunk/doc/C/tmpl/gda-thread-connection.sgml Tue Apr 7 19:10:50 2009
@@ -0,0 +1,45 @@
+<!-- ##### SECTION Title ##### -->
+GdaThreadConnection
+
+<!-- ##### SECTION Short_Description ##### -->
+
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### STRUCT GdaThreadConnection ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### FUNCTION gda_thread_connection_update_meta_store_async ##### -->
+<para>
+
+</para>
+
+ cnc:
+
+
+<!-- ##### FUNCTION gda_connection_get_meta_store_data_async ##### -->
+<para>
+
+</para>
+
+ cnc:
+ meta_type:
+ error:
+ nb_filters:
+ Varargs:
+
+
Added: trunk/doc/C/tmpl/gda-thread-wrapper.sgml
==============================================================================
--- (empty file)
+++ trunk/doc/C/tmpl/gda-thread-wrapper.sgml Tue Apr 7 19:10:50 2009
@@ -0,0 +1,155 @@
+<!-- ##### SECTION Title ##### -->
+GdaThreadWrapper
+
+<!-- ##### SECTION Short_Description ##### -->
+Execute functions in a sub thread
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+ The purpose of the #GdaThreadWrapper object is to execute functions in an isolated sub thread. As the
+ #GdaThreadWrapper is thread safe, one is able to isolate some code's execution is a <emphasis>private</emphasis> thread, and
+ make a non thread safe code thread safe.
+</para>
+<para>
+ The downside of this is that the actual execution of the code will be slower as it requires
+ threads to be synchronized.
+</para>
+<para>
+ Each thread using a #GdaThreadWrapper object can use it as if it was the only user: the #GdaThreadWrapper will
+ simply dispatch all the execution requests to its <emphasis>private</emphasis> sub thread and report the
+ execution's status only to the thread which made the request.
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### STRUCT GdaThreadWrapper ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### FUNCTION gda_thread_wrapper_new ##### -->
+<para>
+
+</para>
+
+ Returns:
+
+
+<!-- ##### USER_FUNCTION GdaThreadWrapperFunc ##### -->
+<para>
+ Specifies the type of function to be passed to gda_thread_wrapper_execute().
+</para>
+
+ arg: pointer to the data (which is the @arg argument passed to gda_thread_wrapper_execute_void())
+ error: a place to store errors
+ Returns: a pointer to some data which will be returned by gda_thread_wrapper_fetch_result()
+
+
+<!-- ##### FUNCTION gda_thread_wrapper_execute ##### -->
+<para>
+
+</para>
+
+ wrapper:
+ func:
+ arg:
+ arg_destroy_func:
+ error:
+ Returns:
+
+
+<!-- ##### USER_FUNCTION GdaThreadWrapperVoidFunc ##### -->
+<para>
+ Specifies the type of function to be passed to gda_thread_wrapper_execute_void().
+</para>
+
+ arg: a pointer to the data (which is the @arg argument passed to gda_thread_wrapper_execute_void())
+ error: a place to store errors
+
+
+<!-- ##### FUNCTION gda_thread_wrapper_execute_void ##### -->
+<para>
+
+</para>
+
+ wrapper:
+ func:
+ arg:
+ arg_destroy_func:
+ error:
+ Returns:
+
+
+<!-- ##### FUNCTION gda_thread_wrapper_iterate ##### -->
+<para>
+
+</para>
+
+ wrapper:
+ may_block:
+
+
+<!-- ##### FUNCTION gda_thread_wrapper_fetch_result ##### -->
+<para>
+
+</para>
+
+ wrapper:
+ may_lock:
+ out_id:
+ error:
+ Returns:
+
+
+<!-- ##### FUNCTION gda_thread_wrapper_get_waiting_size ##### -->
+<para>
+
+</para>
+
+ wrapper:
+ Returns:
+
+
+<!-- ##### USER_FUNCTION GdaThreadWrapperCallback ##### -->
+<para>
+ Specifies the type of function to be passed to gda_thread_wrapper_connect_raw().
+</para>
+
+ wrapper: the #GdaThreadWrapper
+ instance: a pointer to the instance which emitted the signal
+ signame: the name of the signal being emitted
+ n_param_values: number of GValue in @param_values
+ param_values: array of @n_param_values GValue
+ gda_reserved: reserved
+ data: a pointer to the data (which is the @data argument passed to gda_thread_wrapper_connect_raw())
+
+
+<!-- ##### FUNCTION gda_thread_wrapper_connect_raw ##### -->
+<para>
+
+</para>
+
+ wrapper:
+ instance:
+ sig_name:
+ callback:
+ data:
+ Returns:
+
+
+<!-- ##### FUNCTION gda_thread_wrapper_disconnect ##### -->
+<para>
+
+</para>
+
+ wrapper:
+ id:
+
+
Modified: trunk/libgda/Makefile.am
==============================================================================
--- trunk/libgda/Makefile.am (original)
+++ trunk/libgda/Makefile.am Tue Apr 7 19:10:50 2009
@@ -1,6 +1,6 @@
lib_LTLIBRARIES = libgda-4.0.la
-SUBDIRS = sqlite handlers binreloc sql-parser providers-support
+SUBDIRS = sqlite handlers binreloc sql-parser providers-support thread-wrapper
if BDB
GDA_BDB_H=gda-data-model-bdb.h
@@ -99,7 +99,7 @@
gda-config.c \
gda-connection.c \
gda-connection-event.c \
- gda-connection-sqlite.h \
+ gda-connection-internal.h \
gda-custom-marshal.c \
gda-custom-marshal.h \
gda-data-comparator.c \
@@ -208,6 +208,7 @@
handlers/libgda_handlers-4.0.la \
binreloc/libgda_binreloc-4.0.la \
sqlite/libgda-sqlite.la \
+ thread-wrapper/libgda_threadwrapper-4.0.la \
$(LIBGDA_LIBS) $(FAM_LIBS) \
$(GIO_LIBS) $(GNOMEVFS_LIBS)
Copied: trunk/libgda/gda-connection-internal.h (from r3371, /trunk/libgda/gda-connection-sqlite.h)
==============================================================================
--- /trunk/libgda/gda-connection-sqlite.h (original)
+++ trunk/libgda/gda-connection-internal.h Tue Apr 7 19:10:50 2009
@@ -20,10 +20,11 @@
* Boston, MA 02111-1307, USA.
*/
-#ifndef __GDA_CONNECTION_SQLITE_H_
-#define __GDA_CONNECTION_SQLITE_H_
+#ifndef __GDA_CONNECTION_INTERNAL_H_
+#define __GDA_CONNECTION_INTERNAL_H_
#include <libgda/gda-decl.h>
+#include <libgda/thread-wrapper/gda-thread-wrapper.h>
G_BEGIN_DECLS
@@ -34,6 +35,19 @@
*/
GdaConnection *_gda_open_internal_sqlite_connection (const gchar *cnc_string);
+
+/*
+ * Functions dedicated to implementing a GdaConnection which uses a GdaThreadWrapper around
+ * another connection to make it thread safe.
+ */
+typedef struct {
+ GdaConnection *sub_connection;
+ GdaServerProvider *cnc_provider;
+ GdaThreadWrapper *wrapper;
+ GArray *handlers_ids; /* array of gulong */
+} ThreadConnectionData; /* Per connection private data for */
+void _gda_connection_force_transaction_status (GdaConnection *cnc, GdaConnection *wrapped_cnc);
+
G_END_DECLS
#endif
Modified: trunk/libgda/gda-connection.c
==============================================================================
--- trunk/libgda/gda-connection.c (original)
+++ trunk/libgda/gda-connection.c Tue Apr 7 19:10:50 2009
@@ -28,6 +28,7 @@
#include <libgda/gda-config.h>
#include <libgda/gda-connection.h>
#include <libgda/gda-connection-private.h>
+#include <libgda/gda-connection-internal.h>
#include <libgda/gda-connection-event.h>
#include <glib/gi18n-lib.h>
#include <libgda/gda-log.h>
@@ -44,6 +45,7 @@
#include <libgda/gda-util.h>
#include <libgda/gda-mutex.h>
#include <libgda/gda-lockable.h>
+#include <libgda/thread-wrapper/gda-thread-provider.h>
#define PROV_CLASS(provider) (GDA_SERVER_PROVIDER_CLASS (G_OBJECT_GET_CLASS (provider)))
@@ -54,6 +56,7 @@
gchar *cnc_string;
gchar *auth_string;
gboolean is_open;
+ gboolean is_thread_wrapper;
GdaMetaStore *meta_store;
@@ -71,8 +74,36 @@
GCond *unique_possible_cond;
GMutex *unique_possible_mutex;
GdaMutex *mutex;
+
+ /* Asynchronous statement execution */
+ guint next_task_id; /* starts at 1 as 0 is an error */
+ GArray *waiting_tasks; /* array of CncTask pointers to tasks to be executed */
+ GArray *completed_tasks; /* array of CncTask pointers to tasks already executed */
};
+/* represents an asynchronous execution task */
+typedef struct {
+ guint task_id; /* ID assigned by GdaConnection object */
+ guint prov_task_id; /* ID assigned by GdaServerProvider */
+ gboolean being_processed; /* TRUE if currently being processed */
+ GMutex *mutex;
+ GdaStatement *stmt; /* statement to execute */
+ GdaStatementModelUsage model_usage;
+ GType *col_types;
+ GdaSet *params;
+ gboolean need_last_insert_row;
+ GdaSet *last_insert_row;
+ GObject *result;
+ GError *error;
+} CncTask;
+#define CNC_TASK(x) ((CncTask*)(x))
+
+static CncTask *cnc_task_new (guint id, GdaStatement *stmt, GdaStatementModelUsage model_usage,
+ GType *col_types, GdaSet *params, gboolean need_last_insert_row);
+static void cnc_task_free (CncTask *task);
+#define cnc_task_lock(task) g_mutex_lock ((task)->mutex)
+#define cnc_task_unlock(task) g_mutex_unlock ((task)->mutex)
+
static void gda_connection_class_init (GdaConnectionClass *klass);
static void gda_connection_init (GdaConnection *cnc, GdaConnectionClass *klass);
static void gda_connection_dispose (GObject *object);
@@ -115,11 +146,13 @@
PROP_AUTH_STRING,
PROP_OPTIONS,
PROP_META_STORE,
- PROP_THREAD_OWNER
+ PROP_THREAD_OWNER,
+ PROP_IS_THREAD_WRAPPER
};
static GObjectClass *parent_class = NULL;
extern GdaServerProvider *_gda_config_sqlite_provider; /* defined in gda-config.c */
+static GdaServerProvider *_gda_thread_wrapper_provider = NULL;
/*
* GdaConnection class implementation
@@ -255,6 +288,12 @@
"This should only be modified by the database providers' implementation"),
(G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (object_class, PROP_IS_THREAD_WRAPPER,
+ g_param_spec_boolean ("is-wrapper", NULL,
+ _("Tells if the connection acts as a thread wrapper around another connection, making it completely thread safe"),
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)));
+
object_class->dispose = gda_connection_dispose;
object_class->finalize = gda_connection_finalize;
@@ -287,6 +326,10 @@
cnc->priv->events_list = NULL;
cnc->priv->trans_status = NULL; /* no transaction yet */
cnc->priv->prepared_stmts = NULL;
+
+ cnc->priv->next_task_id = 1;
+ cnc->priv->waiting_tasks = g_array_new (FALSE, FALSE, sizeof (gpointer));
+ cnc->priv->completed_tasks = g_array_new (FALSE, FALSE, sizeof (gpointer));
}
static void prepared_stms_foreach_func (GdaStatement *gda_stmt, GdaPStmt *prepared_stmt, GdaConnection *cnc);
@@ -330,6 +373,22 @@
cnc->priv->meta_store = NULL;
}
+ if (cnc->priv->waiting_tasks) {
+ gint i, len = cnc->priv->waiting_tasks->len;
+ for (i = 0; i < len; i++)
+ cnc_task_free (CNC_TASK (g_array_index (cnc->priv->waiting_tasks, gpointer, i)));
+ g_array_free (cnc->priv->waiting_tasks, TRUE);
+ cnc->priv->waiting_tasks = NULL;
+ }
+
+ if (cnc->priv->completed_tasks) {
+ gint i, len = cnc->priv->completed_tasks->len;
+ for (i = 0; i < len; i++)
+ cnc_task_free (CNC_TASK (g_array_index (cnc->priv->completed_tasks, gpointer, i)));
+ g_array_free (cnc->priv->completed_tasks, TRUE);
+ cnc->priv->completed_tasks = NULL;
+ }
+
/* chain to parent class */
parent_class->dispose (object);
}
@@ -525,8 +584,18 @@
cnc->priv->meta_store = g_value_get_object (value);
if (cnc->priv->meta_store)
g_object_ref (cnc->priv->meta_store);
+ if (cnc->priv->is_thread_wrapper) {
+ ThreadConnectionData *cdata;
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (cdata)
+ g_object_set (G_OBJECT (cdata->sub_connection), "meta-store",
+ cnc->priv->meta_store, NULL);
+ }
gda_mutex_unlock (cnc->priv->mutex);
break;
+ case PROP_IS_THREAD_WRAPPER:
+ cnc->priv->is_thread_wrapper = g_value_get_boolean (value);
+ break;
}
}
}
@@ -561,11 +630,84 @@
case PROP_META_STORE:
g_value_set_object (value, cnc->priv->meta_store);
break;
+ case PROP_IS_THREAD_WRAPPER:
+ g_value_set_boolean (value, cnc->priv->is_thread_wrapper);
+ break;
}
gda_mutex_unlock (cnc->priv->mutex);
}
}
+/*
+ * helper functions to manage CncTask
+ */
+static void task_stmt_reset_cb (GdaStatement *stmt, CncTask *task);
+static CncTask *
+cnc_task_new (guint id, GdaStatement *stmt, GdaStatementModelUsage model_usage, GType *col_types,
+ GdaSet *params, gboolean need_last_insert_row)
+{
+ CncTask *task;
+
+ task = g_new0 (CncTask, 1);
+ task->being_processed = FALSE;
+ task->task_id = id;
+ task->stmt = g_object_ref (stmt);
+ g_signal_connect (stmt, "reset", /* monitor statement changes */
+ G_CALLBACK (task_stmt_reset_cb), task);
+ task->model_usage = model_usage;
+ if (col_types) {
+ gint i;
+ for (i = 0; ; i++) {
+ if (col_types [i] == G_TYPE_NONE)
+ break;
+ }
+ task->col_types = g_new (GType, i+1);
+ memcpy (task->col_types, col_types, (i+1) * sizeof (GType));
+ }
+ if (params)
+ task->params = gda_set_copy (params);
+ task->need_last_insert_row = need_last_insert_row;
+
+ task->mutex = g_mutex_new ();
+ return task;
+}
+
+static void
+task_stmt_reset_cb (GdaStatement *stmt, CncTask *task)
+{
+ g_mutex_lock (task->mutex);
+ g_signal_handlers_disconnect_by_func (task->stmt,
+ G_CALLBACK (task_stmt_reset_cb), task);
+ g_object_unref (task->stmt);
+ task->stmt = NULL;
+ g_mutex_unlock (task->mutex);
+}
+
+static void
+cnc_task_free (CncTask *task)
+{
+ g_mutex_lock (task->mutex);
+ if (task->stmt) {
+ g_signal_handlers_disconnect_by_func (task->stmt,
+ G_CALLBACK (task_stmt_reset_cb), task);
+ g_object_unref (task->stmt);
+ }
+ if (task->params)
+ g_object_unref (task->params);
+ if (task->col_types)
+ g_free (task->col_types);
+ if (task->last_insert_row)
+ g_object_unref (task->last_insert_row);
+ if (task->result)
+ g_object_unref (task->result);
+ if (task->error)
+ g_error_free (task->error);
+
+ g_mutex_unlock (task->mutex);
+ g_mutex_free (task->mutex);
+ g_free (task);
+}
+
/**
* gda_connection_open_from_dsn
* @dsn: data source name.
@@ -643,6 +785,13 @@
prov = gda_config_get_provider (dsn_info->provider, error);
if (prov) {
+ if (options & GDA_CONNECTION_OPTIONS_THREAD_SAFE) {
+ if (!_gda_thread_wrapper_provider)
+ _gda_thread_wrapper_provider =
+ GDA_SERVER_PROVIDER (g_object_new (GDA_TYPE_THREAD_PROVIDER,
+ NULL));
+ prov = _gda_thread_wrapper_provider;
+ }
if (PROV_CLASS (prov)->create_connection) {
cnc = PROV_CLASS (prov)->create_connection (prov);
if (cnc)
@@ -770,6 +919,19 @@
prov = gda_config_get_provider (provider_name ? provider_name : real_provider, error);
if (prov) {
+ if (options & GDA_CONNECTION_OPTIONS_THREAD_SAFE) {
+ gchar *tmp;
+ tmp = g_strdup_printf ("%s;PROVIDER_NAME=%s",
+ real_cnc, gda_server_provider_get_name (prov));
+ g_free (real_cnc);
+ real_cnc = tmp;
+ if (!_gda_thread_wrapper_provider)
+ _gda_thread_wrapper_provider =
+ GDA_SERVER_PROVIDER (g_object_new (GDA_TYPE_THREAD_PROVIDER,
+ NULL));
+ prov = _gda_thread_wrapper_provider;
+ }
+
if (PROV_CLASS (prov)->create_connection) {
cnc = PROV_CLASS (prov)->create_connection (prov);
if (cnc)
@@ -792,7 +954,7 @@
cnc = NULL;
}
}
- }
+ }
g_free (real_cnc);
g_free (user);
@@ -1556,6 +1718,347 @@
return types;
}
+
+/*
+ * No locking is done here must be done before calling
+ *
+ * Returns: -1 if task not found
+ */
+static gint
+get_task_index (GdaConnection *cnc, guint task_id, gboolean *out_completed, gboolean id_is_prov)
+{
+ gint i, len;
+ CncTask *task;
+ len = cnc->priv->completed_tasks->len;
+ for (i = 0; i < len; i++) {
+ task = CNC_TASK (g_array_index (cnc->priv->completed_tasks, gpointer, i));
+ if ((! id_is_prov && (task->task_id == task_id)) ||
+ (id_is_prov && (task->prov_task_id == task_id))) {
+ *out_completed = TRUE;
+ return i;
+ }
+ }
+
+ len = cnc->priv->waiting_tasks->len;
+ for (i = 0; i < len; i++) {
+ task = CNC_TASK (g_array_index (cnc->priv->waiting_tasks, gpointer, i));
+ if ((! id_is_prov && (task->task_id == task_id)) ||
+ (id_is_prov && (task->prov_task_id == task_id))) {
+ *out_completed = FALSE;
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * This callback is called from the GdaServerProvider object
+ */
+static void
+async_stmt_exec_cb (GdaServerProvider *provider, GdaConnection *cnc, guint task_id,
+ GObject *result_obj, const GError *error, CncTask *task)
+{
+ gint i;
+ gboolean is_completed;
+
+ gda_connection_lock (GDA_LOCKABLE (cnc));
+
+ i = get_task_index (cnc, task_id, &is_completed, FALSE);
+ if (i >= 0) {
+ CncTask *task;
+ g_assert (!is_completed);
+
+ /* complete @task and free some memory */
+ task = CNC_TASK (g_array_index (cnc->priv->waiting_tasks, gpointer, i));
+ cnc_task_lock (task);
+
+ task->being_processed = FALSE;
+ if (error)
+ task->error = g_error_copy (error);
+ if (result_obj)
+ task->result = g_object_ref (result_obj);
+ if (task->stmt) {
+ g_signal_handlers_disconnect_by_func (task->stmt,
+ G_CALLBACK (task_stmt_reset_cb), task);
+ g_object_unref (task->stmt);
+ task->stmt = NULL;
+ }
+ if (task->params) {
+ g_object_unref (task->params);
+ task->params = NULL;
+ }
+ if (task->col_types) {
+ g_free (task->col_types);
+ task->col_types = NULL;
+ }
+
+ g_array_remove_index (cnc->priv->waiting_tasks, i);
+ g_array_append_val (cnc->priv->completed_tasks, task);
+
+ cnc_task_unlock (task);
+
+ /* execute next waiting task if there is one */
+ while (cnc->priv->waiting_tasks->len >= 1) {
+ /* execute statement now as there are no other ones to be executed */
+ GError *lerror = NULL;
+ task = CNC_TASK (g_array_index (cnc->priv->waiting_tasks, gpointer, 0));
+ task->being_processed = TRUE;
+ cnc_task_lock (task);
+ PROV_CLASS (cnc->priv->provider_obj)->statement_execute (cnc->priv->provider_obj, cnc,
+ task->stmt,
+ task->params,
+ task->model_usage,
+ task->col_types,
+ &(task->last_insert_row),
+ &(task->prov_task_id),
+ (GdaServerProviderExecCallback) async_stmt_exec_cb,
+ task, &lerror);
+ if (lerror) {
+ /* task execution failed => move it to completed tasks array */
+ task->error = lerror;
+ task->being_processed = FALSE;
+ g_array_remove_index (cnc->priv->waiting_tasks, 0);
+ g_array_append_val (cnc->priv->completed_tasks, task);
+ }
+ cnc_task_unlock (task);
+ }
+ }
+ g_print ("%s() called!!!\n", __FUNCTION__);
+
+ gda_connection_lock (GDA_LOCKABLE (cnc));
+}
+
+/**
+ * gda_connection_async_statement_execute
+ * @cnc: a #GdaConnection
+ * @stmt: a #GdaStatement object
+ * @params: a #GdaSet object (which can be obtained using gda_statement_get_parameters()), or %NULL
+ * @model_usage: in the case where @stmt is a SELECT statement, specifies how the returned data model will be used
+ * @col_types: an array of GType to request each returned #GdaDataModel's column's GType, terminated with the G_TYPE_NONE
+ * @need_last_insert_row: TRUE if the values of the last interted row must be computed
+ * @error: a place to store errors, or %NULL
+ *
+ * This method is somilar to gda_connection_statement_execute() but is done asynchronously as this method returns
+ * immediately a task ID. It's up to the caller to use gda_connection_async_fetch_result() regularly to check
+ * if the statement's execution is finished.
+ *
+ * It is possible to call the method several times to request several statements to be executed asynchronously, the
+ * statements will be executed in the order in which they were requested.
+ *
+ * The parameters, if present, are copied and can be discaded or modified before the statement is actually executed.
+ * The @stmt object is not copied but simply referenced (for performance reasons), and if it is modified before
+ * it is actually executed, then its execution will not occur. It is however safe to call g_object_unref() on it if
+ * it's not needed anymore.
+ *
+ * The execution failure of any statement has no impact on the execution of other statements except for example if
+ * the connection has a transaction started and the failure invalidates the transaction (as decided by the database
+ * server).
+ *
+ * Returns: a task ID, or 0 if an error occurred (not an error regarding @stmt itself as its execution has not yet started
+ * but any other error)
+ *
+ * Since: 4.2
+ */
+guint
+gda_connection_async_statement_execute (GdaConnection *cnc, GdaStatement *stmt, GdaSet *params,
+ GdaStatementModelUsage model_usage, GType *col_types,
+ gboolean need_last_insert_row,
+ GError **error)
+{
+ guint id;
+ CncTask *task;
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), 0);
+ g_return_val_if_fail (cnc->priv, 0);
+ g_return_val_if_fail (cnc->priv->provider_obj, 0);
+ g_return_val_if_fail (GDA_IS_STATEMENT (stmt), 0);
+ g_return_val_if_fail (PROV_CLASS (cnc->priv->provider_obj)->statement_execute, 0);
+
+ if (! gda_connection_trylock ((GdaLockable*) cnc)) {
+ g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_CANT_LOCK_ERROR,
+ _("Can't obtain connection lock"));
+ return 0;
+ }
+ id = cnc->priv->next_task_id ++;
+ task = cnc_task_new (id, stmt, model_usage, col_types, params, need_last_insert_row);
+ g_array_append_val (cnc->priv->waiting_tasks, task);
+
+ if (cnc->priv->waiting_tasks->len == 1) {
+ /* execute statement now as there are no other ones to be executed */
+ GError *lerror = NULL;
+ task->being_processed = TRUE;
+ cnc_task_lock (task);
+ PROV_CLASS (cnc->priv->provider_obj)->statement_execute (cnc->priv->provider_obj, cnc,
+ task->stmt, task->params,
+ task->model_usage, task->col_types,
+ &(task->last_insert_row),
+ &(task->prov_task_id),
+ (GdaServerProviderExecCallback) async_stmt_exec_cb,
+ task, &lerror);
+ if (lerror) {
+ /* task execution failed => move it to completed tasks array */
+ gint i;
+ gboolean is_completed;
+
+ task->error = lerror;
+ task->being_processed = FALSE;
+ i = get_task_index (cnc, id, &is_completed, FALSE);
+ g_assert ((i >= 0) && !is_completed);
+ g_array_remove_index (cnc->priv->waiting_tasks, i);
+ g_array_append_val (cnc->priv->completed_tasks, task);
+ }
+ cnc_task_unlock (task);
+ }
+
+ gda_connection_unlock ((GdaLockable*) cnc);
+
+ return id;
+}
+
+/**
+ * gda_connection_async_fetch_result
+ * @cnc: a #GdaConnection
+ * @task_id: a task ID retuned by gda_connection_async_statement_execute()
+ * @last_insert_row: a place to store a new #GdaSet object which contains the values of the last inserted row, or %NULL
+ * @error: a place to store errors, or %NULL
+ *
+ * Use this method to obtain the result of the execution of a statement which has been executed asynchronously by
+ * calling gda_connection_async_statement_execute(). This function is non locking and will return %NULL (and no
+ * error will be set) if the statement has not been executed yet.
+ *
+ * If the statement has been executed, this method returns the same value as gda_connection_statement_execute()
+ * would have if the statement had been
+ * executed synchronously.
+ *
+ * Returns: a #GObject, or %NULL if an error occurred
+ *
+ * Since: 4.2
+ */
+GObject *
+gda_connection_async_fetch_result (GdaConnection *cnc, guint task_id, GdaSet **last_insert_row, GError **error)
+{
+ gint i;
+ gboolean is_completed;
+ GObject *obj = NULL;
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (cnc->priv, NULL);
+
+ if (! gda_connection_trylock ((GdaLockable*) cnc)) {
+ g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_CANT_LOCK_ERROR,
+ _("Can't obtain connection lock"));
+ return NULL;
+ }
+
+ /* if provider needs to be awaken, then do it now */
+ if (PROV_CLASS (cnc->priv->provider_obj)->handle_async) {
+ if (! (PROV_CLASS (cnc->priv->provider_obj)->handle_async (cnc->priv->provider_obj, cnc, error))) {
+ gda_connection_unlock ((GdaLockable*) cnc);
+ return NULL;
+ }
+ }
+
+ i = get_task_index (cnc, task_id, &is_completed, FALSE);
+ if (i < 0) {
+ g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_TASK_NOT_FOUND_ERROR,
+ _("Can't find task %u"), task_id);
+ }
+ else if (is_completed) {
+ /* task completed */
+ CncTask *task;
+ task = CNC_TASK (g_array_index (cnc->priv->completed_tasks, gpointer, i));
+ g_array_remove_index (cnc->priv->completed_tasks, i);
+
+ cnc_task_lock (task);
+ if (task->result)
+ obj = g_object_ref (task->result);
+ if (task->error) {
+ g_propagate_error (error, task->error);
+ task->error = NULL;
+ }
+ if (last_insert_row) {
+ if (task->last_insert_row)
+ *last_insert_row = g_object_ref (task->last_insert_row);
+ else
+ *last_insert_row = NULL;
+ }
+ cnc_task_unlock (task);
+ cnc_task_free (task);
+ }
+ else {
+ /* task not yet completed */
+ /* nothing to do */
+ }
+
+ gda_connection_unlock ((GdaLockable*) cnc);
+ return obj;
+}
+
+/**
+ * gda_connection_async_cancel
+ * @cnc: a #GdaConnection
+ * @task_id: a task ID retuned by gda_connection_async_statement_execute()
+ * @error: a place to store errors, or %NULL
+ *
+ * Requests that a task be cancelled. This operation may of may not have any effect
+ * depending on the task's status, even if it returns %TRUE. If it returns %FALSE,
+ * then the task has not been cancelled.
+ *
+ * Returns: TRUE if no error occurred
+ *
+ * Since: 4.2
+ */
+gboolean
+gda_connection_async_cancel (GdaConnection *cnc, guint task_id, GError **error)
+{
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (cnc->priv, FALSE);
+ g_return_val_if_fail (cnc->priv->provider_obj, FALSE);
+
+ if (! gda_connection_trylock ((GdaLockable*) cnc)) {
+ g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_CANT_LOCK_ERROR,
+ _("Can't obtain connection lock"));
+ return FALSE;
+ }
+
+ gint i;
+ gboolean is_completed;
+ gboolean retval = TRUE;
+ i = get_task_index (cnc, task_id, &is_completed, FALSE);
+ if ((i >= 0) && (!is_completed)) {
+ CncTask *task;
+ task = CNC_TASK (g_array_index (cnc->priv->waiting_tasks, gpointer, i));
+ if (task->being_processed) {
+ if (PROV_CLASS (cnc->priv->provider_obj)->cancel) {
+ retval = PROV_CLASS (cnc->priv->provider_obj)->cancel (cnc->priv->provider_obj, cnc,
+ task->prov_task_id, error);
+ if (retval) {
+ /* cancellation may have succeeded => remove this task from the tasks to execute */
+ g_array_remove_index (cnc->priv->waiting_tasks, i);
+ cnc_task_free (task);
+ }
+ }
+ else {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_METHOD_NON_IMPLEMENTED_ERROR,
+ "%s", _("Provider does not support asynchronous server operation"));
+ retval = FALSE;
+ }
+ }
+ else {
+ /* simply remove this task from the tasks to execute */
+ g_array_remove_index (cnc->priv->waiting_tasks, i);
+ cnc_task_free (task);
+ }
+ }
+ else if (i < 0) {
+ g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_TASK_NOT_FOUND_ERROR,
+ _("Can't find task %u"), task_id);
+ retval = FALSE;
+ }
+
+ gda_connection_unlock ((GdaLockable*) cnc);
+ return retval;
+}
+
/*
* Wrapper which adds @...
*/
@@ -1910,6 +2413,37 @@
return model;
}
+/*
+ * Forces the GdaTransactionStatus. This is reserved to connections which are thread wrappers
+ *
+ * @trans_status may be NULL
+ * if @trans_status is not NULL, then its ref count is increased.
+ */
+void
+_gda_connection_force_transaction_status (GdaConnection *cnc, GdaConnection *wrapped_cnc)
+{
+ g_assert (cnc->priv->is_thread_wrapper);
+ if (cnc->priv->trans_status)
+ g_object_unref (cnc->priv->trans_status);
+
+ cnc->priv->trans_status = wrapped_cnc->priv->trans_status;
+ if (cnc->priv->trans_status)
+ g_object_ref (cnc->priv->trans_status);
+
+#ifdef GDA_DEBUG_signal
+ g_print (">> 'TRANSACTION_STATUS_CHANGED' from %s\n", __FUNCTION__);
+#endif
+ g_signal_emit (G_OBJECT (cnc), gda_connection_signals[TRANSACTION_STATUS_CHANGED], 0);
+#ifdef GDA_DEBUG_signal
+ g_print ("<< 'TRANSACTION_STATUS_CHANGED' from %s\n", __FUNCTION__);
+#endif
+
+#ifdef GDA_DEBUG_NO
+ if (cnc->priv->trans_status)
+ gda_transaction_status_dump (cnc->priv->trans_status, 5);
+#endif
+}
+
/**
* gda_connection_begin_transaction
* @cnc: a #GdaConnection object.
@@ -2977,6 +3511,21 @@
return NULL;
}
+typedef struct {
+ GdaConnection *cnc;
+ GdaMetaContext *context;
+} UpdateMetaStoreData;
+
+static gpointer
+sub_thread_update_meta_store (UpdateMetaStoreData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval;
+ retval = gda_connection_update_meta_store (data->cnc, data->context, error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval ? 1 : 0);
+}
+
/**
* gda_connection_update_meta_store
* @cnc: a #GdaConnection object.
@@ -3027,6 +3576,24 @@
gda_connection_lock ((GdaLockable*) cnc);
+ if (cnc->priv->is_thread_wrapper) {
+ ThreadConnectionData *cdata;
+ UpdateMetaStoreData data;
+ gpointer res;
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ data.cnc = cdata->sub_connection;
+ data.context = context;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_update_meta_store, &data, NULL, error);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, error);
+ return GPOINTER_TO_INT (res) ? TRUE : FALSE;
+ }
+
/* Get or create the GdaMetaStore object */
store = gda_connection_get_meta_store (cnc);
g_assert (store);
@@ -4131,14 +4698,30 @@
GdaMetaStore *
gda_connection_get_meta_store (GdaConnection *cnc)
{
- GdaMetaStore *store;
+ GdaMetaStore *store = NULL;
g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
g_return_val_if_fail (cnc->priv, NULL);
gda_connection_lock ((GdaLockable*) cnc);
- if (!cnc->priv->meta_store)
- cnc->priv->meta_store = gda_meta_store_new (NULL);
+ if (!cnc->priv->meta_store) {
+ ThreadConnectionData *cdata = NULL;
+ if (cnc->priv->is_thread_wrapper) {
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ g_assert (cdata);
+
+ if (cdata->sub_connection->priv->meta_store) {
+ cnc->priv->meta_store = g_object_ref (cdata->sub_connection->priv->meta_store);
+ store = cnc->priv->meta_store;
+ }
+ }
+ if (!store) {
+ cnc->priv->meta_store = gda_meta_store_new (NULL);
+ if (cnc->priv->is_thread_wrapper)
+ cdata->sub_connection->priv->meta_store =
+ g_object_ref (cnc->priv->meta_store);
+ }
+ }
store = cnc->priv->meta_store;
gda_connection_unlock ((GdaLockable*) cnc);
Modified: trunk/libgda/gda-connection.h
==============================================================================
--- trunk/libgda/gda-connection.h (original)
+++ trunk/libgda/gda-connection.h Tue Apr 7 19:10:50 2009
@@ -53,7 +53,9 @@
GDA_CONNECTION_NO_CNC_SPEC_ERROR,
GDA_CONNECTION_NO_PROVIDER_SPEC_ERROR,
GDA_CONNECTION_OPEN_ERROR,
- GDA_CONNECTION_STATEMENT_TYPE_ERROR
+ GDA_CONNECTION_STATEMENT_TYPE_ERROR,
+ GDA_CONNECTION_CANT_LOCK_ERROR,
+ GDA_CONNECTION_TASK_NOT_FOUND_ERROR
} GdaConnectionError;
#define GDA_CONNECTION_NONEXIST_DSN_ERROR GDA_CONNECTION_DSN_NOT_FOUND_ERROR
@@ -82,7 +84,8 @@
typedef enum {
GDA_CONNECTION_OPTIONS_NONE = 0,
- GDA_CONNECTION_OPTIONS_READ_ONLY = 1 << 0
+ GDA_CONNECTION_OPTIONS_READ_ONLY = 1 << 0,
+ GDA_CONNECTION_OPTIONS_THREAD_SAFE = 1 << 1
} GdaConnectionOptions;
typedef enum {
@@ -168,6 +171,7 @@
gchar *gda_connection_statement_to_sql (GdaConnection *cnc,
GdaStatement *stmt, GdaSet *params, GdaStatementSqlFlag flags,
GSList **params_used, GError **error);
+/* synchronous exec */
gboolean gda_connection_statement_prepare (GdaConnection *cnc,
GdaStatement *stmt, GError **error);
GObject *gda_connection_statement_execute (GdaConnection *cnc, GdaStatement *stmt, GdaSet *params,
@@ -184,6 +188,15 @@
gint gda_connection_statement_execute_non_select (GdaConnection *cnc, GdaStatement *stmt,
GdaSet *params, GdaSet **last_insert_row, GError **error);
+/* Async. execution */
+guint gda_connection_async_statement_execute (GdaConnection *cnc, GdaStatement *stmt, GdaSet *params,
+ GdaStatementModelUsage model_usage, GType *col_types,
+ gboolean need_last_insert_row,
+ GError **error);
+GObject *gda_connection_async_fetch_result (GdaConnection *cnc, guint task_id, GdaSet **last_insert_row, GError **error);
+gboolean gda_connection_async_cancel (GdaConnection *cnc, guint task_id, GError **error);
+
+
gboolean gda_connection_begin_transaction (GdaConnection *cnc, const gchar *name,
GdaTransactionIsolation level, GError **error);
gboolean gda_connection_commit_transaction (GdaConnection *cnc, const gchar *name, GError **error);
Modified: trunk/libgda/gda-data-select.c
==============================================================================
--- trunk/libgda/gda-data-select.c (original)
+++ trunk/libgda/gda-data-select.c Tue Apr 7 19:10:50 2009
@@ -58,19 +58,13 @@
*/
static gint external_to_internal_row (GdaDataSelect *model, gint ext_row, GError **error);
-/*
- * Getting a GdaRow from a model row:
- * [model row] ==(model->index)==> [index in model->rows] ==(model->rows)==> [GdaRow pointer]
- */
-struct _GdaDataSelectPrivate {
- GdaConnection *cnc;
+typedef struct {
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*/
/* Internal iterator's information, if GDA_DATA_MODEL_CURSOR_* based access */
gint iter_row; /* G_MININT if at start, G_MAXINT if at end, "external" row number */
- GdaDataModelIter *iter;
GdaStatement *sel_stmt;
GdaSet *ext_params;
@@ -84,6 +78,18 @@
GArray *del_rows; /* array[index] = number of the index'th deleted row,
* sorted by row number (row numbers are internal row numbers )*/
GHashTable *upd_rows; /* key = internal row number + 1, value = a DelayedSelectStmt pointer */
+
+ gboolean ref_count; /* when drop to 0 => free can be done */
+} PrivateShareable;
+
+/*
+ * Getting a GdaRow from a model row:
+ * [model row] ==(model->index)==> [index in model->rows] ==(model->rows)==> [GdaRow pointer]
+ */
+struct _GdaDataSelectPrivate {
+ GdaConnection *cnc;
+ GdaDataModelIter *iter;
+ PrivateShareable *sh;
};
/* properties */
@@ -308,40 +314,43 @@
g_return_if_fail (GDA_IS_DATA_SELECT (model));
model->priv = g_new0 (GdaDataSelectPrivate, 1);
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->priv->sh = g_new0 (PrivateShareable, 1);
+ model->priv->sh->rows = g_array_new (FALSE, FALSE, sizeof (GdaRow *));
+ model->priv->sh->index = g_hash_table_new (g_direct_hash, g_direct_equal);
model->prep_stmt = NULL;
- model->priv->columns = NULL;
+ model->priv->sh->columns = NULL;
model->nb_stored_rows = 0;
model->advertized_nrows = -1; /* unknown number of rows */
- model->priv->sel_stmt = NULL;
- model->priv->ext_params = NULL;
- model->priv->reset_with_ext_params_change = FALSE;
+ model->priv->sh->sel_stmt = NULL;
+ model->priv->sh->ext_params = NULL;
+ model->priv->sh->reset_with_ext_params_change = FALSE;
- model->priv->iter_row = G_MININT;
+ model->priv->sh->iter_row = G_MININT;
model->priv->iter = NULL;
- model->priv->modif_internals = g_new0 (GdaDataSelectInternals, 1);
- model->priv->modif_internals->safely_locked = FALSE;
- model->priv->modif_internals->unique_row_condition = NULL;
- model->priv->modif_internals->insert_to_select_mapping = NULL;
- model->priv->modif_internals->modif_set = NULL;
- model->priv->modif_internals->exec_set = NULL;
+ model->priv->sh->modif_internals = g_new0 (GdaDataSelectInternals, 1);
+ model->priv->sh->modif_internals->safely_locked = FALSE;
+ model->priv->sh->modif_internals->unique_row_condition = NULL;
+ model->priv->sh->modif_internals->insert_to_select_mapping = NULL;
+ model->priv->sh->modif_internals->modif_set = NULL;
+ model->priv->sh->modif_internals->exec_set = NULL;
for (i = FIRST_QUERY; i < NB_QUERIES; i++)
- model->priv->modif_internals->modif_stmts[i] = NULL;
- model->priv->modif_internals->upd_stmts = NULL;
- model->priv->modif_internals->ins_stmts = NULL;
- model->priv->modif_internals->one_row_select_stmt = NULL;
+ model->priv->sh->modif_internals->modif_stmts[i] = NULL;
+ model->priv->sh->modif_internals->upd_stmts = NULL;
+ model->priv->sh->modif_internals->ins_stmts = NULL;
+ model->priv->sh->modif_internals->one_row_select_stmt = NULL;
- model->priv->upd_rows = NULL;
- model->priv->del_rows = NULL;
+ model->priv->sh->upd_rows = NULL;
+ model->priv->sh->del_rows = NULL;
+
+ model->priv->sh->ref_count = 1;
}
static void
ext_params_holder_changed_cb (GdaSet *paramlist, GdaHolder *param, GdaDataSelect *model)
{
- if (model->priv->reset_with_ext_params_change) {
+ if (model->priv->sh->reset_with_ext_params_change) {
GdaDataSelect *new_model;
GdaStatement *select;
GError *error = NULL;
@@ -362,8 +371,8 @@
types [model->prep_stmt->ncols] = G_TYPE_NONE;
}
new_model = (GdaDataSelect*) gda_connection_statement_execute_select_full (model->priv->cnc, select,
- model->priv->ext_params,
- model->priv->usage_flags | GDA_STATEMENT_MODEL_ALLOW_NOPARAM,
+ model->priv->sh->ext_params,
+ model->priv->sh->usage_flags | GDA_STATEMENT_MODEL_ALLOW_NOPARAM,
types,
&error);
g_free (types);
@@ -394,21 +403,21 @@
GdaDataSelect *old_model = new_model; /* renamed for code's readability */
GdaDataSelectInternals *mi;
- model->priv->reset_with_ext_params_change = old_model->priv->reset_with_ext_params_change;
- mi = old_model->priv->modif_internals;
- old_model->priv->modif_internals = model->priv->modif_internals;
- model->priv->modif_internals = mi;
-
- copy = old_model->priv->sel_stmt;
- old_model->priv->sel_stmt = model->priv->sel_stmt;
- model->priv->sel_stmt = (GdaStatement*) copy;
+ model->priv->sh->reset_with_ext_params_change = old_model->priv->sh->reset_with_ext_params_change;
+ mi = old_model->priv->sh->modif_internals;
+ old_model->priv->sh->modif_internals = model->priv->sh->modif_internals;
+ model->priv->sh->modif_internals = mi;
+
+ copy = old_model->priv->sh->sel_stmt;
+ old_model->priv->sh->sel_stmt = model->priv->sh->sel_stmt;
+ model->priv->sh->sel_stmt = (GdaStatement*) copy;
/* keep the same GdaColumn pointers */
GSList *l1, *l2;
- l1 = old_model->priv->columns;
- old_model->priv->columns = model->priv->columns;
- model->priv->columns = l1;
- for (l1 = model->priv->columns, l2 = old_model->priv->columns;
+ l1 = old_model->priv->sh->columns;
+ old_model->priv->sh->columns = model->priv->sh->columns;
+ model->priv->sh->columns = l1;
+ for (l1 = model->priv->sh->columns, l2 = old_model->priv->sh->columns;
l1 && l2;
l1 = l1->next, l2 = l2->next) {
if ((gda_column_get_g_type ((GdaColumn*) l1->data) == GDA_TYPE_NULL) &&
@@ -419,12 +428,12 @@
g_object_unref (old_model);
- /* copy all the param's holders' values from model->priv->ext_params to
- to model->priv->modif_internals->exec_set */
+ /* copy all the param's holders' values from model->priv->sh->ext_params to
+ to model->priv->sh->modif_internals->exec_set */
GSList *list;
- for (list = model->priv->ext_params->holders; list; list = list->next) {
+ for (list = model->priv->sh->ext_params->holders; list; list = list->next) {
GdaHolder *h;
- h = gda_set_get_holder (model->priv->modif_internals->exec_set,
+ h = gda_set_get_holder (model->priv->sh->modif_internals->exec_set,
gda_holder_get_id (list->data));
if (h) {
if (!gda_holder_is_valid (GDA_HOLDER (list->data)))
@@ -446,77 +455,116 @@
}
static void
-gda_data_select_dispose (GObject *object)
+free_private_shared_data (GdaDataSelect *model)
{
- GdaDataSelect *model = (GdaDataSelect *) object;
-
- g_return_if_fail (GDA_IS_DATA_SELECT (model));
-
- /* free memory */
- if (model->priv) {
+ if (!model->priv->sh)
+ return;
+ model->priv->sh->ref_count --;
+ if (model->priv->sh->ref_count == 0) {
gint i;
- if (model->priv->sel_stmt) {
- g_object_unref (model->priv->sel_stmt);
- model->priv->sel_stmt = NULL;
+ if (model->priv->sh->sel_stmt) {
+ g_object_unref (model->priv->sh->sel_stmt);
+ model->priv->sh->sel_stmt = NULL;
}
- if (model->priv->ext_params) {
- g_signal_handlers_disconnect_by_func (model->priv->ext_params,
+ if (model->priv->sh->ext_params) {
+ g_signal_handlers_disconnect_by_func (model->priv->sh->ext_params,
G_CALLBACK (ext_params_holder_changed_cb), model);
- g_object_unref (model->priv->ext_params);
- model->priv->ext_params = NULL;
+ g_object_unref (model->priv->sh->ext_params);
+ model->priv->sh->ext_params = NULL;
}
- if (model->priv->modif_internals) {
- _gda_data_select_internals_free (model->priv->modif_internals);
- model->priv->modif_internals = NULL;
+ if (model->priv->sh->modif_internals) {
+ _gda_data_select_internals_free (model->priv->sh->modif_internals);
+ model->priv->sh->modif_internals = NULL;
}
- if (model->priv->upd_rows) {
- g_hash_table_destroy (model->priv->upd_rows);
- model->priv->upd_rows = NULL;
+ if (model->priv->sh->upd_rows) {
+ g_hash_table_destroy (model->priv->sh->upd_rows);
+ model->priv->sh->upd_rows = NULL;
}
- if (model->priv->del_rows) {
- g_array_free (model->priv->del_rows, TRUE);
- model->priv->del_rows = NULL;
+ if (model->priv->sh->del_rows) {
+ g_array_free (model->priv->sh->del_rows, TRUE);
+ model->priv->sh->del_rows = NULL;
+ }
+ if (model->priv->sh->rows) {
+ for (i = 0; i < model->priv->sh->rows->len; i++) {
+ GdaRow *prow;
+ prow = g_array_index (model->priv->sh->rows, GdaRow *, i);
+ g_object_unref (prow);
+ }
+ g_array_free (model->priv->sh->rows, TRUE);
+ model->priv->sh->rows = NULL;
+ }
+ if (model->priv->sh->index) {
+ g_hash_table_destroy (model->priv->sh->index);
+ model->priv->sh->index = NULL;
}
+ if (model->priv->sh->columns) {
+ g_slist_foreach (model->priv->sh->columns, (GFunc) g_object_unref, NULL);
+ g_slist_free (model->priv->sh->columns);
+ model->priv->sh->columns = NULL;
+ }
+
+ g_free (model->priv->sh);
+ }
+ model->priv->sh = NULL;
+}
+
+static void
+gda_data_select_dispose (GObject *object)
+{
+ GdaDataSelect *model = (GdaDataSelect *) object;
+
+ g_return_if_fail (GDA_IS_DATA_SELECT (model));
+ /* free memory */
+ if (model->priv) {
if (model->priv->cnc) {
g_object_unref (model->priv->cnc);
model->priv->cnc = NULL;
}
-
+ if (model->priv->iter) {
+ g_object_unref (model->priv->iter);
+ model->priv->iter = NULL;
+ }
if (model->prep_stmt) {
g_object_unref (model->prep_stmt);
model->prep_stmt = NULL;
}
-
- if (model->priv->rows) {
- for (i = 0; i < model->priv->rows->len; i++) {
- GdaRow *prow;
- prow = g_array_index (model->priv->rows, GdaRow *, i);
- g_object_unref (prow);
- }
- g_array_free (model->priv->rows, TRUE);
- model->priv->rows = NULL;
- }
- if (model->priv->index) {
- g_hash_table_destroy (model->priv->index);
- model->priv->index = NULL;
- }
- if (model->priv->columns) {
- g_slist_foreach (model->priv->columns, (GFunc) g_object_unref, NULL);
- g_slist_free (model->priv->columns);
- model->priv->columns = NULL;
- }
+ free_private_shared_data (model);
+ g_free (model->priv);
+ model->priv = NULL;
}
/* chain to parent class */
parent_class->dispose (object);
}
+/*
+ * Allows 2 GdaDataSelect objects to safely share the same private data (PrivateShareable pointer).
+ * NOTE: nothing is done to prevent the master and the slave to modify the provate data at the same
+ * time: this must be done by the user implementing the GdaDataSelect objects.
+ *
+ * This API is used by the GdaThreadRecordset object
+ *
+ * On the master side (the one from which the private data is shared), nothing special happens, except
+ * that master->priv->sh->ref_count is increased by 1.
+ *
+ * On the slave side, what happens is:
+ * - "free" slave->priv->sh
+ * - slave->priv->sh = master->priv->sh
+ */
+void
+_gda_data_select_share_private_data (GdaDataSelect *master, GdaDataSelect *slave)
+{
+ master->priv->sh->ref_count ++;
+ free_private_shared_data (slave);
+ slave->priv->sh = master->priv->sh;
+}
+
void
_gda_data_select_internals_free (GdaDataSelectInternals *inter)
{
@@ -586,8 +634,8 @@
_gda_data_select_internals_steal (GdaDataSelect *model)
{
GdaDataSelectInternals *inter;
- inter = model->priv->modif_internals;
- model->priv->modif_internals = NULL;
+ inter = model->priv->sh->modif_internals;
+ model->priv->sh->modif_internals = NULL;
return inter;
}
@@ -595,19 +643,19 @@
void
_gda_data_select_internals_paste (GdaDataSelect *model, GdaDataSelectInternals *inter)
{
- if (model->priv->modif_internals)
- _gda_data_select_internals_free (model->priv->modif_internals);
- model->priv->modif_internals = inter;
+ if (model->priv->sh->modif_internals)
+ _gda_data_select_internals_free (model->priv->sh->modif_internals);
+ model->priv->sh->modif_internals = inter;
}
static void
create_columns (GdaDataSelect *model)
{
gint i;
- if (model->priv->columns) {
- g_slist_foreach (model->priv->columns, (GFunc) g_object_unref, NULL);
- g_slist_free (model->priv->columns);
- model->priv->columns = NULL;
+ if (model->priv->sh->columns) {
+ g_slist_foreach (model->priv->sh->columns, (GFunc) g_object_unref, NULL);
+ g_slist_free (model->priv->sh->columns);
+ model->priv->sh->columns = NULL;
}
if (!model->prep_stmt)
return;
@@ -619,7 +667,7 @@
/* copy template columns */
GSList *list;
for (list = model->prep_stmt->tmpl_columns; list; list = list->next)
- model->priv->columns = g_slist_append (model->priv->columns, g_object_ref (list->data));
+ model->priv->sh->columns = g_slist_append (model->priv->sh->columns, g_object_ref (list->data));
}
else {
/* create columns */
@@ -628,7 +676,7 @@
gda_col = gda_column_new ();
if (model->prep_stmt->types)
gda_column_set_g_type (gda_col, model->prep_stmt->types [i]);
- model->priv->columns = g_slist_append (model->priv->columns, gda_col);
+ model->priv->sh->columns = g_slist_append (model->priv->sh->columns, gda_col);
}
}
}
@@ -657,7 +705,7 @@
sel_stmt = gda_pstmt_get_gda_statement (model->prep_stmt);
if (sel_stmt &&
gda_statement_get_statement_type (sel_stmt) == GDA_SQL_STATEMENT_SELECT)
- model->priv->sel_stmt = gda_statement_copy (sel_stmt);
+ model->priv->sh->sel_stmt = gda_statement_copy (sel_stmt);
}
create_columns (model);
break;
@@ -666,7 +714,7 @@
if (!(flags & GDA_DATA_MODEL_ACCESS_RANDOM) &&
(flags & GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD))
flags = GDA_DATA_MODEL_ACCESS_CURSOR;
- model->priv->usage_flags = flags;
+ model->priv->sh->usage_flags = flags;
break;
}
case PROP_ALL_STORED:
@@ -684,36 +732,36 @@
GdaSet *set;
set = g_value_get_object (value);
if (set) {
- model->priv->ext_params = g_object_ref (set);
- g_signal_connect (model->priv->ext_params, "holder-changed",
+ model->priv->sh->ext_params = g_object_ref (set);
+ g_signal_connect (model->priv->sh->ext_params, "holder-changed",
G_CALLBACK (ext_params_holder_changed_cb), model);
- model->priv->modif_internals->exec_set = gda_set_copy (set);
+ model->priv->sh->modif_internals->exec_set = gda_set_copy (set);
}
break;
}
case PROP_INS_QUERY:
- if (model->priv->modif_internals->modif_stmts [INS_QUERY])
- g_object_unref (model->priv->modif_internals->modif_stmts [INS_QUERY]);
- model->priv->modif_internals->modif_stmts [INS_QUERY] = g_value_get_object (value);
- if (model->priv->modif_internals->modif_stmts [INS_QUERY])
- g_object_ref (model->priv->modif_internals->modif_stmts [INS_QUERY]);
+ if (model->priv->sh->modif_internals->modif_stmts [INS_QUERY])
+ g_object_unref (model->priv->sh->modif_internals->modif_stmts [INS_QUERY]);
+ model->priv->sh->modif_internals->modif_stmts [INS_QUERY] = g_value_get_object (value);
+ if (model->priv->sh->modif_internals->modif_stmts [INS_QUERY])
+ g_object_ref (model->priv->sh->modif_internals->modif_stmts [INS_QUERY]);
break;
case PROP_DEL_QUERY:
- if (model->priv->modif_internals->modif_stmts [DEL_QUERY])
- g_object_unref (model->priv->modif_internals->modif_stmts [DEL_QUERY]);
- model->priv->modif_internals->modif_stmts [DEL_QUERY] = g_value_get_object (value);
- if (model->priv->modif_internals->modif_stmts [DEL_QUERY])
- g_object_ref (model->priv->modif_internals->modif_stmts [DEL_QUERY]);
+ if (model->priv->sh->modif_internals->modif_stmts [DEL_QUERY])
+ g_object_unref (model->priv->sh->modif_internals->modif_stmts [DEL_QUERY]);
+ model->priv->sh->modif_internals->modif_stmts [DEL_QUERY] = g_value_get_object (value);
+ if (model->priv->sh->modif_internals->modif_stmts [DEL_QUERY])
+ g_object_ref (model->priv->sh->modif_internals->modif_stmts [DEL_QUERY]);
break;
case PROP_UPD_QUERY:
- if (model->priv->modif_internals->modif_stmts [UPD_QUERY])
- g_object_unref (model->priv->modif_internals->modif_stmts [UPD_QUERY]);
- model->priv->modif_internals->modif_stmts [UPD_QUERY] = g_value_get_object (value);
- if (model->priv->modif_internals->modif_stmts [UPD_QUERY])
- g_object_ref (model->priv->modif_internals->modif_stmts [UPD_QUERY]);
+ if (model->priv->sh->modif_internals->modif_stmts [UPD_QUERY])
+ g_object_unref (model->priv->sh->modif_internals->modif_stmts [UPD_QUERY]);
+ model->priv->sh->modif_internals->modif_stmts [UPD_QUERY] = g_value_get_object (value);
+ if (model->priv->sh->modif_internals->modif_stmts [UPD_QUERY])
+ g_object_ref (model->priv->sh->modif_internals->modif_stmts [UPD_QUERY]);
break;
case PROP_RESET_WITH_EXT_PARAM:
- model->priv->reset_with_ext_params_change = g_value_get_boolean (value);
+ model->priv->sh->reset_with_ext_params_change = g_value_get_boolean (value);
break;
default:
break;
@@ -737,10 +785,10 @@
g_value_set_object (value, model->prep_stmt);
break;
case PROP_FLAGS:
- g_value_set_uint (value, model->priv->usage_flags);
+ g_value_set_uint (value, model->priv->sh->usage_flags);
break;
case PROP_ALL_STORED:
- if (!model->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
+ if (!model->priv->sh->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
g_warning ("Cannot set the 'store-all-rows' property when acces mode is cursor based");
else {
if ((model->advertized_nrows < 0) && CLASS (model)->fetch_nb_rows)
@@ -749,22 +797,22 @@
}
break;
case PROP_PARAMS:
- g_value_set_object (value, model->priv->modif_internals->exec_set);
+ g_value_set_object (value, model->priv->sh->modif_internals->exec_set);
break;
case PROP_INS_QUERY:
- g_value_set_object (value, model->priv->modif_internals->modif_stmts [INS_QUERY]);
+ g_value_set_object (value, model->priv->sh->modif_internals->modif_stmts [INS_QUERY]);
break;
case PROP_DEL_QUERY:
- g_value_set_object (value, model->priv->modif_internals->modif_stmts [DEL_QUERY]);
+ g_value_set_object (value, model->priv->sh->modif_internals->modif_stmts [DEL_QUERY]);
break;
case PROP_UPD_QUERY:
- g_value_set_object (value, model->priv->modif_internals->modif_stmts [UPD_QUERY]);
+ g_value_set_object (value, model->priv->sh->modif_internals->modif_stmts [UPD_QUERY]);
break;
case PROP_SEL_STMT:
g_value_set_object (value, check_acceptable_statement (model, NULL));
break;
case PROP_RESET_WITH_EXT_PARAM:
- g_value_set_boolean (value, model->priv->reset_with_ext_params_change);
+ g_value_set_boolean (value, model->priv->sh->reset_with_ext_params_change);
break;
default:
break;
@@ -787,13 +835,13 @@
g_return_if_fail (GDA_IS_DATA_SELECT (model));
g_return_if_fail (GDA_IS_ROW (row));
- if (g_hash_table_lookup (model->priv->index, GINT_TO_POINTER (rownum + 1)))
+ if (g_hash_table_lookup (model->priv->sh->index, GINT_TO_POINTER (rownum + 1)))
g_error ("INTERNAL error: row %d already exists, aborting", rownum);
- g_hash_table_insert (model->priv->index, GINT_TO_POINTER (rownum + 1),
- GINT_TO_POINTER (model->priv->rows->len + 1));
- g_array_append_val (model->priv->rows, row);
- model->nb_stored_rows = model->priv->rows->len;
+ g_hash_table_insert (model->priv->sh->index, GINT_TO_POINTER (rownum + 1),
+ GINT_TO_POINTER (model->priv->sh->rows->len + 1));
+ g_array_append_val (model->priv->sh->rows, row);
+ model->nb_stored_rows = model->priv->sh->rows->len;
}
/**
@@ -812,11 +860,11 @@
g_return_val_if_fail (GDA_IS_DATA_SELECT (model), NULL);
g_return_val_if_fail (model->priv, NULL);
- irow = GPOINTER_TO_INT (g_hash_table_lookup (model->priv->index, GINT_TO_POINTER (rownum + 1)));
+ irow = GPOINTER_TO_INT (g_hash_table_lookup (model->priv->sh->index, GINT_TO_POINTER (rownum + 1)));
if (irow <= 0)
return NULL;
else
- return g_array_index (model->priv->rows, GdaRow *, irow - 1);
+ return g_array_index (model->priv->sh->rows, GdaRow *, irow - 1);
}
/**
@@ -838,38 +886,38 @@
}
/*
- * Add the +/-<col num> holders to model->priv->modif_internals->modif_set
+ * Add the +/-<col num> holders to model->priv->sh->modif_internals->modif_set
*/
static gboolean
compute_modif_set (GdaDataSelect *model, GError **error)
{
gint i;
- if (model->priv->modif_internals->modif_set)
- g_object_unref (model->priv->modif_internals->modif_set);
- if (model->priv->modif_internals->exec_set)
- model->priv->modif_internals->modif_set = gda_set_copy (model->priv->modif_internals->exec_set);
+ if (model->priv->sh->modif_internals->modif_set)
+ g_object_unref (model->priv->sh->modif_internals->modif_set);
+ if (model->priv->sh->modif_internals->exec_set)
+ model->priv->sh->modif_internals->modif_set = gda_set_copy (model->priv->sh->modif_internals->exec_set);
else
- model->priv->modif_internals->modif_set = gda_set_new (NULL);
+ model->priv->sh->modif_internals->modif_set = gda_set_new (NULL);
for (i = 0; i < NB_QUERIES; i++) {
GdaSet *set;
- if (! model->priv->modif_internals->modif_stmts [i])
+ if (! model->priv->sh->modif_internals->modif_stmts [i])
continue;
- if (! gda_statement_get_parameters (model->priv->modif_internals->modif_stmts [i], &set, error)) {
- g_object_unref (model->priv->modif_internals->modif_set);
- model->priv->modif_internals->modif_set = NULL;
+ if (! gda_statement_get_parameters (model->priv->sh->modif_internals->modif_stmts [i], &set, error)) {
+ g_object_unref (model->priv->sh->modif_internals->modif_set);
+ model->priv->sh->modif_internals->modif_set = NULL;
return FALSE;
}
- gda_set_merge_with_set (model->priv->modif_internals->modif_set, set);
+ gda_set_merge_with_set (model->priv->sh->modif_internals->modif_set, set);
g_object_unref (set);
}
#ifdef GDA_DEBUG_NO
GSList *list;
g_print ("-------\n");
- for (list = model->priv->modif_internals->modif_set->holders; list; list = list->next) {
+ for (list = model->priv->sh->modif_internals->modif_set->holders; list; list = list->next) {
GdaHolder *h = GDA_HOLDER (list->data);
g_print ("=> holder '%s'\n", gda_holder_get_id (h));
}
@@ -964,8 +1012,8 @@
{
GdaStatement *sel_stmt;
- if (model->priv->sel_stmt)
- return model->priv->sel_stmt;
+ if (model->priv->sh->sel_stmt)
+ return model->priv->sh->sel_stmt;
if (! model->prep_stmt) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MODIFICATION_STATEMENT_ERROR,
@@ -986,8 +1034,8 @@
return NULL;
}
- model->priv->sel_stmt = gda_statement_copy (sel_stmt);
- return model->priv->sel_stmt;
+ model->priv->sh->sel_stmt = gda_statement_copy (sel_stmt);
+ return model->priv->sh->sel_stmt;
}
/**
@@ -1071,14 +1119,14 @@
mtype = DEL_QUERY;
- /* if there is no WHERE part, then use model->priv->modif_internals->unique_row_condition if set */
+ /* if there is no WHERE part, then use model->priv->sh->modif_internals->unique_row_condition if set */
g_object_get (G_OBJECT (mod_stmt), "structure", &sqlst, NULL);
g_assert (sqlst);
del = (GdaSqlStatementDelete*) sqlst->contents;
if (!del->cond) {
- if (model->priv->modif_internals->unique_row_condition) {
- /* copy model->priv->modif_internals->unique_row_condition */
- del->cond = gda_sql_expr_copy (model->priv->modif_internals->unique_row_condition);
+ if (model->priv->sh->modif_internals->unique_row_condition) {
+ /* copy model->priv->sh->modif_internals->unique_row_condition */
+ del->cond = gda_sql_expr_copy (model->priv->sh->modif_internals->unique_row_condition);
GDA_SQL_ANY_PART (del->cond)->parent = GDA_SQL_ANY_PART (del);
g_object_set (G_OBJECT (mod_stmt), "structure", sqlst, NULL);
}
@@ -1090,10 +1138,10 @@
}
}
else {
- if (model->priv->modif_internals->unique_row_condition) {
- /* replace WHERE with model->priv->modif_internals->unique_row_condition */
+ if (model->priv->sh->modif_internals->unique_row_condition) {
+ /* replace WHERE with model->priv->sh->modif_internals->unique_row_condition */
gda_sql_expr_free (del->cond);
- del->cond = gda_sql_expr_copy (model->priv->modif_internals->unique_row_condition);
+ del->cond = gda_sql_expr_copy (model->priv->sh->modif_internals->unique_row_condition);
GDA_SQL_ANY_PART (del->cond)->parent = GDA_SQL_ANY_PART (del);
g_object_set (G_OBJECT (mod_stmt), "structure", sqlst, NULL);
}
@@ -1111,14 +1159,14 @@
mtype = UPD_QUERY;
- /* if there is no WHERE part, then use model->priv->modif_internals->unique_row_condition if set */
+ /* if there is no WHERE part, then use model->priv->sh->modif_internals->unique_row_condition if set */
g_object_get (G_OBJECT (mod_stmt), "structure", &sqlst, NULL);
g_assert (sqlst);
upd = (GdaSqlStatementUpdate*) sqlst->contents;
if (!upd->cond) {
- if (model->priv->modif_internals->unique_row_condition) {
- /* copy model->priv->modif_internals->unique_row_condition */
- upd->cond = gda_sql_expr_copy (model->priv->modif_internals->unique_row_condition);
+ if (model->priv->sh->modif_internals->unique_row_condition) {
+ /* copy model->priv->sh->modif_internals->unique_row_condition */
+ upd->cond = gda_sql_expr_copy (model->priv->sh->modif_internals->unique_row_condition);
GDA_SQL_ANY_PART (upd->cond)->parent = GDA_SQL_ANY_PART (upd);
g_object_set (G_OBJECT (mod_stmt), "structure", sqlst, NULL);
}
@@ -1130,10 +1178,10 @@
}
}
else {
- if (model->priv->modif_internals->unique_row_condition) {
- /* replace WHERE with model->priv->modif_internals->unique_row_condition */
+ if (model->priv->sh->modif_internals->unique_row_condition) {
+ /* replace WHERE with model->priv->sh->modif_internals->unique_row_condition */
gda_sql_expr_free (upd->cond);
- upd->cond = gda_sql_expr_copy (model->priv->modif_internals->unique_row_condition);
+ upd->cond = gda_sql_expr_copy (model->priv->sh->modif_internals->unique_row_condition);
GDA_SQL_ANY_PART (upd->cond)->parent = GDA_SQL_ANY_PART (upd);
g_object_set (G_OBJECT (mod_stmt), "structure", sqlst, NULL);
}
@@ -1153,16 +1201,16 @@
if (! gda_statement_check_structure (mod_stmt, error))
return FALSE;
- if (model->priv->modif_internals->modif_stmts[mtype]) {
- g_object_unref (model->priv->modif_internals->modif_stmts[mtype]);
- model->priv->modif_internals->modif_stmts[mtype] = NULL;
+ if (model->priv->sh->modif_internals->modif_stmts[mtype]) {
+ g_object_unref (model->priv->sh->modif_internals->modif_stmts[mtype]);
+ model->priv->sh->modif_internals->modif_stmts[mtype] = NULL;
}
- /* prepare model->priv->modif_internals->modif_set */
+ /* prepare model->priv->sh->modif_internals->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_internals->modif_set */
+ /* check that all the parameters required to execute @mod_stmt are in model->priv->sh->modif_internals->modif_set */
GdaSet *params;
GSList *list;
if (! gda_statement_get_parameters (mod_stmt, ¶ms, error))
@@ -1170,7 +1218,7 @@
for (list = params->holders; list; list = list->next) {
GdaHolder *holder = GDA_HOLDER (list->data);
GdaHolder *eholder;
- eholder = gda_set_get_holder (model->priv->modif_internals->modif_set, gda_holder_get_id (holder));
+ eholder = gda_set_get_holder (model->priv->sh->modif_internals->modif_set, gda_holder_get_id (holder));
if (!eholder) {
gint num;
gboolean is_old;
@@ -1189,7 +1237,7 @@
g_object_unref (params);
return FALSE;
}
- gda_set_add_holder (model->priv->modif_internals->modif_set, holder);
+ gda_set_add_holder (model->priv->sh->modif_internals->modif_set, holder);
}
else if (gda_holder_get_g_type (holder) != gda_holder_get_g_type (eholder)) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MODIFICATION_STATEMENT_ERROR,
@@ -1203,7 +1251,7 @@
}
g_object_unref (params);
- model->priv->modif_internals->modif_stmts[mtype] = mod_stmt;
+ model->priv->sh->modif_internals->modif_stmts[mtype] = mod_stmt;
g_object_ref (mod_stmt);
}
else {
@@ -1215,8 +1263,8 @@
#ifdef GDA_DEBUG_NO
GSList *hlist;
g_print ("SET MODIF QUERY\n");
- if (model->priv->modif_internals->modif_set) {
- for (hlist = model->priv->modif_internals->modif_set->holders; hlist; hlist = hlist->next) {
+ if (model->priv->sh->modif_internals->modif_set) {
+ for (hlist = model->priv->sh->modif_internals->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));
@@ -1258,9 +1306,9 @@
return FALSE;
}
for (mtype = FIRST_QUERY; mtype < NB_QUERIES; mtype++)
- if (model->priv->modif_internals->modif_stmts[mtype]) {
- g_object_unref (model->priv->modif_internals->modif_stmts[mtype]);
- model->priv->modif_internals->modif_stmts[mtype] = NULL;
+ if (model->priv->sh->modif_internals->modif_stmts[mtype]) {
+ g_object_unref (model->priv->sh->modif_internals->modif_stmts[mtype]);
+ model->priv->sh->modif_internals->modif_stmts[mtype] = NULL;
}
retval = gda_compute_dml_statements (model->priv->cnc, stmt, TRUE,
@@ -1330,7 +1378,7 @@
if (!check_acceptable_statement (model, error))
return FALSE;
- if (model->priv->modif_internals->unique_row_condition) {
+ if (model->priv->sh->modif_internals->unique_row_condition) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MODIFICATION_STATEMENT_ERROR,
"%s", _("Unique row condition has already been specified"));
return FALSE;
@@ -1342,7 +1390,7 @@
if (!valid)
return FALSE;
- model->priv->modif_internals->unique_row_condition = gda_sql_expr_copy (expr);
+ model->priv->sh->modif_internals->unique_row_condition = gda_sql_expr_copy (expr);
return TRUE;
}
@@ -1385,7 +1433,7 @@
if (!check_acceptable_statement (model, error))
return FALSE;
- if (model->priv->modif_internals->unique_row_condition) {
+ if (model->priv->sh->modif_internals->unique_row_condition) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MODIFICATION_STATEMENT_ERROR,
"%s", _("Unique row condition has already been specified"));
return FALSE;
@@ -1517,12 +1565,12 @@
retval = imodel->advertized_nrows;
if ((imodel->advertized_nrows < 0) &&
- (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM) &&
+ (imodel->priv->sh->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM) &&
CLASS (model)->fetch_nb_rows)
retval = CLASS (model)->fetch_nb_rows (imodel);
- if ((retval > 0) && (imodel->priv->del_rows))
- retval -= imodel->priv->del_rows->len;
+ if ((retval > 0) && (imodel->priv->sh->del_rows))
+ retval -= imodel->priv->sh->del_rows->len;
return retval;
}
@@ -1548,7 +1596,7 @@
imodel = GDA_DATA_SELECT (model);
g_return_val_if_fail (imodel->priv, NULL);
- return g_slist_nth_data (imodel->priv->columns, col);
+ return g_slist_nth_data (imodel->priv->sh->columns, col);
}
static GdaDataModelAccessFlags
@@ -1561,21 +1609,21 @@
imodel = GDA_DATA_SELECT (model);
g_return_val_if_fail (imodel->priv, 0);
- if (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
+ if (imodel->priv->sh->usage_flags & 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)
+ else if (imodel->priv->sh->usage_flags & GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD) {
+ if (imodel->priv->sh->usage_flags & GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD)
flags = GDA_DATA_MODEL_ACCESS_CURSOR;
else
flags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
}
- if (! imodel->priv->modif_internals->safely_locked) {
- if (imodel->priv->modif_internals->modif_stmts [UPD_QUERY])
+ if (! imodel->priv->sh->modif_internals->safely_locked) {
+ if (imodel->priv->sh->modif_internals->modif_stmts [UPD_QUERY])
flags |= GDA_DATA_MODEL_ACCESS_UPDATE;
- if (imodel->priv->modif_internals->modif_stmts [INS_QUERY])
+ if (imodel->priv->sh->modif_internals->modif_stmts [INS_QUERY])
flags |= GDA_DATA_MODEL_ACCESS_INSERT;
- if (imodel->priv->modif_internals->modif_stmts [DEL_QUERY])
+ if (imodel->priv->sh->modif_internals->modif_stmts [DEL_QUERY])
flags |= GDA_DATA_MODEL_ACCESS_DELETE;
}
@@ -1593,10 +1641,10 @@
gint int_row = ext_row;
/* row number alteration: deleted rows */
- if (model->priv->del_rows) {
+ if (model->priv->sh->del_rows) {
gint i;
- for (i = 0; i < model->priv->del_rows->len; i++) {
- gint indexed = g_array_index (model->priv->del_rows, gint, i);
+ for (i = 0; i < model->priv->sh->del_rows->len; i++) {
+ gint indexed = g_array_index (model->priv->sh->del_rows, gint, i);
if (indexed <= ext_row + i)
int_row += 1;
else
@@ -1624,9 +1672,9 @@
}
static void dump_d (GdaDataSelect *model)
{
- if (model->priv->upd_rows) {
+ if (model->priv->sh->upd_rows) {
g_print ("Delayed SELECT for %p:\n", model);
- g_hash_table_foreach (model->priv->upd_rows, foreach_func_dump, NULL);
+ g_hash_table_foreach (model->priv->sh->upd_rows, foreach_func_dump, NULL);
}
}
#endif
@@ -1643,7 +1691,7 @@
g_return_val_if_fail (imodel->priv, NULL);
/* available only if GDA_DATA_MODEL_ACCESS_RANDOM */
- if (! (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)) {
+ if (! (imodel->priv->sh->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)) {
g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
"%s", _("Data model does only support random access"));
return NULL;
@@ -1667,15 +1715,15 @@
#ifdef GDA_DEBUG_NO
dump_d (imodel);
#endif
- if (imodel->priv->upd_rows)
- dstmt = g_hash_table_lookup (imodel->priv->upd_rows, &int_row);
+ if (imodel->priv->sh->upd_rows)
+ dstmt = g_hash_table_lookup (imodel->priv->sh->upd_rows, &int_row);
if (dstmt) {
if (! dstmt->row) {
GdaDataModel *tmpmodel;
if (!dstmt->select || !dstmt->params) {
g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
"%s", _("Unable to retreive data after modifications, no further modification will be allowed"));
- imodel->priv->modif_internals->safely_locked = TRUE;
+ imodel->priv->sh->modif_internals->safely_locked = TRUE;
return NULL;
}
tmpmodel = gda_connection_statement_execute_select (imodel->priv->cnc,
@@ -1684,7 +1732,7 @@
if (!tmpmodel) {
g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
"%s", _("Unable to retreive data after modifications, no further modification will be allowed"));
- imodel->priv->modif_internals->safely_locked = TRUE;
+ imodel->priv->sh->modif_internals->safely_locked = TRUE;
return NULL;
}
@@ -1692,7 +1740,7 @@
g_object_unref (tmpmodel);
g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
"%s", _("Unable to retreive data after modifications, no further modification will be allowed"));
- imodel->priv->modif_internals->safely_locked = TRUE;
+ imodel->priv->sh->modif_internals->safely_locked = TRUE;
return NULL;
}
@@ -1714,7 +1762,7 @@
g_object_unref (prow);
g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
"%s", _("Unable to retreive data after modifications, no further modification will be allowed"));
- imodel->priv->modif_internals->safely_locked = TRUE;
+ imodel->priv->sh->modif_internals->safely_locked = TRUE;
return NULL;
}
}
@@ -1729,7 +1777,7 @@
prow = dstmt->row;
}
else {
- irow = GPOINTER_TO_INT (g_hash_table_lookup (imodel->priv->index,
+ irow = GPOINTER_TO_INT (g_hash_table_lookup (imodel->priv->sh->index,
GINT_TO_POINTER (int_row + 1)));
if (irow <= 0) {
prow = NULL;
@@ -1738,7 +1786,7 @@
return NULL;
}
else
- prow = g_array_index (imodel->priv->rows, GdaRow *, irow - 1);
+ prow = g_array_index (imodel->priv->sh->rows, GdaRow *, irow - 1);
}
g_assert (prow);
@@ -1760,9 +1808,9 @@
imodel = (GdaDataSelect *) model;
g_return_val_if_fail (imodel->priv, 0);
- if (imodel->priv->modif_internals->safely_locked || !imodel->priv->modif_internals->modif_stmts [UPD_QUERY])
+ if (imodel->priv->sh->modif_internals->safely_locked || !imodel->priv->sh->modif_internals->modif_stmts [UPD_QUERY])
flags = GDA_VALUE_ATTR_NO_MODIF;
- GdaColumn *gdacol = g_slist_nth_data (imodel->priv->columns, col);
+ GdaColumn *gdacol = g_slist_nth_data (imodel->priv->sh->columns, col);
if (gdacol) {
if (gda_column_get_allow_null (gdacol))
flags |= GDA_VALUE_ATTR_CAN_BE_NULL;
@@ -1780,7 +1828,7 @@
imodel = (GdaDataSelect *) model;
g_return_val_if_fail (imodel->priv, 0);
- if (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
+ if (imodel->priv->sh->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
return (GdaDataModelIter *) g_object_new (GDA_TYPE_DATA_MODEL_ITER,
"data-model", model, NULL);
else {
@@ -1788,7 +1836,7 @@
if (! imodel->priv->iter) {
imodel->priv->iter = (GdaDataModelIter *) g_object_new (GDA_TYPE_DATA_MODEL_ITER,
"data-model", model, NULL);
- imodel->priv->iter_row = -1;
+ imodel->priv->sh->iter_row = -1;
}
g_object_ref (imodel->priv->iter);
return imodel->priv->iter;
@@ -1807,25 +1855,26 @@
g_return_val_if_fail (GDA_IS_DATA_SELECT (model), FALSE);
imodel = (GdaDataSelect *) model;
g_return_val_if_fail (imodel->priv, FALSE);
- g_return_val_if_fail (CLASS (model)->fetch_next, FALSE);
- if (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
+ if (imodel->priv->sh->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
return gda_data_model_iter_move_next_default (model, iter);
+ g_return_val_if_fail (CLASS (model)->fetch_next, FALSE);
+
g_return_val_if_fail (iter, FALSE);
g_return_val_if_fail (imodel->priv->iter == iter, FALSE);
- if (imodel->priv->iter_row == G_MAXINT)
+ if (imodel->priv->sh->iter_row == G_MAXINT)
return FALSE;
- else if (imodel->priv->iter_row == G_MININT)
+ else if (imodel->priv->sh->iter_row == G_MININT)
target_iter_row = 0;
else
- target_iter_row = imodel->priv->iter_row + 1;
+ target_iter_row = imodel->priv->sh->iter_row + 1;
int_row = external_to_internal_row (imodel, target_iter_row, NULL);
- irow = GPOINTER_TO_INT (g_hash_table_lookup (imodel->priv->index, GINT_TO_POINTER (int_row + 1)));
+ irow = GPOINTER_TO_INT (g_hash_table_lookup (imodel->priv->sh->index, GINT_TO_POINTER (int_row + 1)));
if (irow > 0)
- prow = g_array_index (imodel->priv->rows, GdaRow *, irow - 1);
+ prow = g_array_index (imodel->priv->sh->rows, GdaRow *, irow - 1);
else if (!CLASS (model)->fetch_next (imodel, &prow, int_row, NULL)) {
/* an error occurred */
g_object_set (G_OBJECT (iter), "current-row", target_iter_row, NULL);
@@ -1834,13 +1883,13 @@
}
if (prow) {
- imodel->priv->iter_row = target_iter_row;
+ imodel->priv->sh->iter_row = target_iter_row;
return update_iter (imodel, prow);
}
else {
g_signal_emit_by_name (iter, "end-of-data");
g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
- imodel->priv->iter_row = G_MAXINT;
+ imodel->priv->sh->iter_row = G_MAXINT;
return FALSE;
}
}
@@ -1856,28 +1905,29 @@
g_return_val_if_fail (GDA_IS_DATA_SELECT (model), FALSE);
imodel = (GdaDataSelect *) model;
g_return_val_if_fail (imodel->priv, FALSE);
- g_return_val_if_fail (CLASS (model)->fetch_prev, FALSE);
- if (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
+ if (imodel->priv->sh->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
return gda_data_model_iter_move_prev_default (model, iter);
+ g_return_val_if_fail (CLASS (model)->fetch_prev, FALSE);
+
g_return_val_if_fail (iter, FALSE);
g_return_val_if_fail (imodel->priv->iter == iter, FALSE);
- if (imodel->priv->iter_row <= 0)
+ if (imodel->priv->sh->iter_row <= 0)
goto prev_error;
- else if (imodel->priv->iter_row == G_MAXINT) {
+ else if (imodel->priv->sh->iter_row == G_MAXINT) {
g_assert (imodel->advertized_nrows >= 0);
target_iter_row = imodel->advertized_nrows - 1;
}
else
- target_iter_row = imodel->priv->iter_row - 1;
+ target_iter_row = imodel->priv->sh->iter_row - 1;
int_row = external_to_internal_row (imodel, target_iter_row, NULL);
- irow = GPOINTER_TO_INT (g_hash_table_lookup (imodel->priv->index, GINT_TO_POINTER (int_row + 1)));
+ irow = GPOINTER_TO_INT (g_hash_table_lookup (imodel->priv->sh->index, GINT_TO_POINTER (int_row + 1)));
if (irow > 0)
- prow = g_array_index (imodel->priv->rows, GdaRow *, irow - 1);
+ prow = g_array_index (imodel->priv->sh->rows, GdaRow *, irow - 1);
else if (!CLASS (model)->fetch_prev (imodel, &prow, int_row, NULL)) {
/* an error occurred */
g_object_set (G_OBJECT (iter), "current-row", target_iter_row, NULL);
@@ -1886,13 +1936,13 @@
}
if (prow) {
- imodel->priv->iter_row = target_iter_row;
+ imodel->priv->sh->iter_row = target_iter_row;
return update_iter (imodel, prow);
}
prev_error:
g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
- imodel->priv->iter_row = G_MININT;
+ imodel->priv->sh->iter_row = G_MININT;
return FALSE;
}
@@ -1908,16 +1958,16 @@
g_return_val_if_fail (imodel->priv, FALSE);
int_row = external_to_internal_row (imodel, row, NULL);
- if (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
+ if (imodel->priv->sh->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
return gda_data_model_iter_move_to_row_default (model, iter, row);
g_return_val_if_fail (iter, FALSE);
g_return_val_if_fail (imodel->priv->iter == iter, FALSE);
- irow = GPOINTER_TO_INT (g_hash_table_lookup (imodel->priv->index,
+ irow = GPOINTER_TO_INT (g_hash_table_lookup (imodel->priv->sh->index,
GINT_TO_POINTER (int_row + 1)));
if (irow > 0)
- prow = g_array_index (imodel->priv->rows, GdaRow *, irow - 1);
+ prow = g_array_index (imodel->priv->sh->rows, GdaRow *, irow - 1);
if (CLASS (model)->fetch_at) {
if (!CLASS (model)->fetch_at (imodel, &prow, int_row, NULL)) {
@@ -1928,18 +1978,18 @@
}
if (prow) {
- imodel->priv->iter_row = row;
+ imodel->priv->sh->iter_row = row;
return update_iter (imodel, prow);
}
else {
g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
- imodel->priv->iter_row = G_MININT;
+ imodel->priv->sh->iter_row = G_MININT;
return FALSE;
}
}
else {
if (prow) {
- imodel->priv->iter_row = row;
+ imodel->priv->sh->iter_row = row;
return update_iter (imodel, prow);
}
else {
@@ -2001,7 +2051,7 @@
}
}
- g_object_set (G_OBJECT (iter), "current-row", imodel->priv->iter_row, NULL);
+ g_object_set (G_OBJECT (iter), "current-row", imodel->priv->sh->iter_row, NULL);
if (update_model)
g_object_set (G_OBJECT (iter), "update-model", update_model, NULL);
@@ -2009,7 +2059,7 @@
}
/*
- * creates a derivative of the model->priv->modif_internals->modif_stmts [UPD_QUERY] statement
+ * creates a derivative of the model->priv->sh->modif_internals->modif_stmts [UPD_QUERY] statement
* where only the columns where @bv->data[colnum] is not 0 are updated.
*
* Returns: a new #GdaStatement, or %NULL
@@ -2022,8 +2072,8 @@
GdaStatement *updstmt = NULL;
/* get a copy of complete UPDATE stmt */
- g_assert (model->priv->modif_internals->modif_stmts [UPD_QUERY]);
- g_object_get (G_OBJECT (model->priv->modif_internals->modif_stmts [UPD_QUERY]), "structure", &sqlst, NULL);
+ g_assert (model->priv->sh->modif_internals->modif_stmts [UPD_QUERY]);
+ g_object_get (G_OBJECT (model->priv->sh->modif_internals->modif_stmts [UPD_QUERY]), "structure", &sqlst, NULL);
g_assert (sqlst);
g_free (sqlst->sql);
sqlst->sql = NULL;
@@ -2102,7 +2152,7 @@
}
/*
- * creates a derivative of the model->priv->modif_internals->modif_stmts [INS_QUERY] statement
+ * creates a derivative of the model->priv->sh->modif_internals->modif_stmts [INS_QUERY] statement
* where only the columns where @bv->data[colnum] is not 0 are not mentionned.
*
* Returns: a new #GdaStatement, or %NULL
@@ -2115,8 +2165,8 @@
GdaStatement *insstmt = NULL;
/* get a copy of complete INSERT stmt */
- g_assert (model->priv->modif_internals->modif_stmts [INS_QUERY]);
- g_object_get (G_OBJECT (model->priv->modif_internals->modif_stmts [INS_QUERY]), "structure", &sqlst, NULL);
+ g_assert (model->priv->sh->modif_internals->modif_stmts [INS_QUERY]);
+ g_object_get (G_OBJECT (model->priv->sh->modif_internals->modif_stmts [INS_QUERY]), "structure", &sqlst, NULL);
g_assert (sqlst);
g_free (sqlst->sql);
sqlst->sql = NULL;
@@ -2212,20 +2262,20 @@
GdaSqlStatement *sel_sqlst;
GdaSqlExpr *row_cond = NULL;
- sel_stmt = model->priv->sel_stmt;
+ sel_stmt = model->priv->sh->sel_stmt;
if (! sel_stmt) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MODIFICATION_STATEMENT_ERROR,
"%s", _("Internal error: can't get the prepared statement's actual statement"));
return NULL;
}
- if (model->priv->modif_internals->unique_row_condition)
- row_cond = gda_sql_expr_copy (model->priv->modif_internals->unique_row_condition);
- else if (model->priv->modif_internals->modif_stmts [DEL_QUERY]) {
+ if (model->priv->sh->modif_internals->unique_row_condition)
+ row_cond = gda_sql_expr_copy (model->priv->sh->modif_internals->unique_row_condition);
+ else if (model->priv->sh->modif_internals->modif_stmts [DEL_QUERY]) {
GdaStatement *del_stmt;
GdaSqlStatement *del_sqlst;
GdaSqlStatementDelete *del;
- del_stmt = model->priv->modif_internals->modif_stmts [DEL_QUERY];
+ del_stmt = model->priv->sh->modif_internals->modif_stmts [DEL_QUERY];
g_object_get (G_OBJECT (del_stmt), "structure", &del_sqlst, NULL);
del = (GdaSqlStatementDelete*) del_sqlst->contents;
@@ -2237,11 +2287,11 @@
row_cond = NULL;
}
}
- else if (model->priv->modif_internals->modif_stmts [UPD_QUERY]) {
+ else if (model->priv->sh->modif_internals->modif_stmts [UPD_QUERY]) {
GdaStatement *upd_stmt;
GdaSqlStatement *upd_sqlst;
GdaSqlStatementUpdate *upd;
- upd_stmt = model->priv->modif_internals->modif_stmts [UPD_QUERY];
+ upd_stmt = model->priv->sh->modif_internals->modif_stmts [UPD_QUERY];
g_object_get (G_OBJECT (upd_stmt), "structure", &upd_sqlst, NULL);
upd = (GdaSqlStatementUpdate*) upd_sqlst->contents;
@@ -2341,17 +2391,17 @@
/* arguments check */
g_assert (bv);
- if (imodel->priv->modif_internals->safely_locked) {
+ if (imodel->priv->sh->modif_internals->safely_locked) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_SAFETY_LOCKED_ERROR,
"%s", _("Modifications are not allowed anymore"));
return FALSE;
}
- if (!iter && ! (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)) {
+ if (!iter && ! (imodel->priv->sh->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)) {
g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
"%s", _("Data model does only support random access"));
return FALSE;
}
- if (! imodel->priv->modif_internals->modif_stmts [UPD_QUERY]) {
+ if (! imodel->priv->sh->modif_internals->modif_stmts [UPD_QUERY]) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
"%s", _("No UPDATE statement provided"));
return FALSE;
@@ -2365,15 +2415,15 @@
return FALSE;
/* compute UPDATE statement */
- if (! imodel->priv->modif_internals->upd_stmts)
- imodel->priv->modif_internals->upd_stmts = g_hash_table_new_full ((GHashFunc) bvector_hash, (GEqualFunc) bvector_equal,
+ if (! imodel->priv->sh->modif_internals->upd_stmts)
+ imodel->priv->sh->modif_internals->upd_stmts = g_hash_table_new_full ((GHashFunc) bvector_hash, (GEqualFunc) bvector_equal,
(GDestroyNotify) bvector_free, g_object_unref);
- stmt = g_hash_table_lookup (imodel->priv->modif_internals->upd_stmts, bv);
+ stmt = g_hash_table_lookup (imodel->priv->sh->modif_internals->upd_stmts, bv);
if (! stmt) {
stmt = compute_single_update_stmt (imodel, bv, error);
if (stmt) {
free_bv = FALSE;
- g_hash_table_insert (imodel->priv->modif_internals->upd_stmts, bv, stmt);
+ g_hash_table_insert (imodel->priv->sh->modif_internals->upd_stmts, bv, stmt);
}
else {
bvector_free (bv);
@@ -2385,7 +2435,7 @@
ncols = gda_data_select_get_n_columns ((GdaDataModel*) imodel);
for (i = 0; i < ncols; i++) {
str = g_strdup_printf ("-%d", i);
- holder = gda_set_get_holder (imodel->priv->modif_internals->modif_set, str);
+ holder = gda_set_get_holder (imodel->priv->sh->modif_internals->modif_set, str);
g_free (str);
if (holder) {
const GValue *cvalue;
@@ -2417,7 +2467,7 @@
GError *lerror = NULL;
sql = gda_statement_to_sql_extended (stmt,
imodel->priv->cnc,
- imodel->priv->modif_internals->modif_set,
+ imodel->priv->sh->modif_internals->modif_set,
GDA_STATEMENT_SQL_PRETTY, NULL,
&lerror);
g_print ("%s(): SQL=> %s\n", __FUNCTION__, sql);
@@ -2427,23 +2477,23 @@
#endif
if (gda_connection_statement_execute_non_select (imodel->priv->cnc, stmt,
- imodel->priv->modif_internals->modif_set, NULL, error) == -1)
+ imodel->priv->sh->modif_internals->modif_set, NULL, error) == -1)
return FALSE;
/* mark that this row has been modified */
DelayedSelectStmt *dstmt;
dstmt = g_new0 (DelayedSelectStmt, 1);
- if (! imodel->priv->modif_internals->one_row_select_stmt)
- imodel->priv->modif_internals->one_row_select_stmt = compute_single_select_stmt (imodel, error);
- if (imodel->priv->modif_internals->one_row_select_stmt) {
- dstmt->select = g_object_ref (imodel->priv->modif_internals->one_row_select_stmt);
+ if (! imodel->priv->sh->modif_internals->one_row_select_stmt)
+ imodel->priv->sh->modif_internals->one_row_select_stmt = compute_single_select_stmt (imodel, error);
+ if (imodel->priv->sh->modif_internals->one_row_select_stmt) {
+ dstmt->select = g_object_ref (imodel->priv->sh->modif_internals->one_row_select_stmt);
gda_statement_get_parameters (dstmt->select, &(dstmt->params), NULL);
if (dstmt->params) {
GSList *list;
gboolean allok = TRUE;
/* overwrite old values with new values if some have been provided */
- for (list = imodel->priv->modif_internals->modif_set->holders; list; list = list->next) {
+ for (list = imodel->priv->sh->modif_internals->modif_set->holders; list; list = list->next) {
GdaHolder *h = (GdaHolder*) list->data;
gint res;
gboolean old;
@@ -2451,7 +2501,7 @@
param_name_to_int (gda_holder_get_id (h), &res, &old) &&
!old) {
str = g_strdup_printf ("-%d", res);
- holder = gda_set_get_holder (imodel->priv->modif_internals->modif_set, str);
+ holder = gda_set_get_holder (imodel->priv->sh->modif_internals->modif_set, str);
g_free (str);
if (holder &&
! gda_holder_set_value (holder, gda_holder_get_value (h), error)) {
@@ -2464,7 +2514,7 @@
for (list = dstmt->params->holders; list && allok; list = list->next) {
GdaHolder *holder = GDA_HOLDER (list->data);
GdaHolder *eholder;
- eholder = gda_set_get_holder (imodel->priv->modif_internals->modif_set,
+ eholder = gda_set_get_holder (imodel->priv->sh->modif_internals->modif_set,
gda_holder_get_id (holder));
if (!eholder ||
! gda_holder_set_value (holder, gda_holder_get_value (eholder), NULL)) {
@@ -2480,13 +2530,13 @@
}
}
dstmt->row = NULL;
- if (! imodel->priv->upd_rows)
- imodel->priv->upd_rows = g_hash_table_new_full (g_int_hash, g_int_equal,
+ if (! imodel->priv->sh->upd_rows)
+ imodel->priv->sh->upd_rows = g_hash_table_new_full (g_int_hash, g_int_equal,
g_free,
(GDestroyNotify) delayed_select_stmt_free);
gint *tmp = g_new (gint, 1);
*tmp = int_row;
- g_hash_table_insert (imodel->priv->upd_rows, tmp, dstmt);
+ g_hash_table_insert (imodel->priv->sh->upd_rows, tmp, dstmt);
#ifdef GDA_DEBUG_NO
dump_d (imodel);
#endif
@@ -2508,17 +2558,17 @@
imodel = (GdaDataSelect *) model;
g_return_val_if_fail (imodel->priv, FALSE);
- if (imodel->priv->modif_internals->safely_locked) {
+ if (imodel->priv->sh->modif_internals->safely_locked) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_SAFETY_LOCKED_ERROR,
"%s", _("Modifications are not allowed anymore"));
return FALSE;
}
- if (! (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)) {
+ if (! (imodel->priv->sh->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)) {
g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
"%s", _("Data model does only support random access"));
return FALSE;
}
- if (! imodel->priv->modif_internals->modif_stmts [UPD_QUERY]) {
+ if (! imodel->priv->sh->modif_internals->modif_stmts [UPD_QUERY]) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
"%s", _("No UPDATE statement provided"));
return FALSE;
@@ -2532,9 +2582,9 @@
return FALSE;
}
- /* invalidate all the imodel->priv->modif_internals->modif_set's value holders */
+ /* invalidate all the imodel->priv->sh->modif_internals->modif_set's value holders */
GSList *list;
- for (list = imodel->priv->modif_internals->modif_set->holders; list; list = list->next) {
+ for (list = imodel->priv->sh->modif_internals->modif_set->holders; list; list = list->next) {
GdaHolder *h = (GdaHolder*) list->data;
if (param_name_to_int (gda_holder_get_id (h), NULL, NULL))
gda_holder_force_invalid ((GdaHolder*) list->data);
@@ -2542,7 +2592,7 @@
/* give values to params for new value */
str = g_strdup_printf ("+%d", col);
- holder = gda_set_get_holder (imodel->priv->modif_internals->modif_set, str);
+ holder = gda_set_get_holder (imodel->priv->sh->modif_internals->modif_set, str);
g_free (str);
if (! holder) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
@@ -2574,12 +2624,12 @@
imodel = (GdaDataSelect *) model;
g_return_val_if_fail (imodel->priv, FALSE);
- if (imodel->priv->modif_internals->safely_locked) {
+ if (imodel->priv->sh->modif_internals->safely_locked) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_SAFETY_LOCKED_ERROR,
"%s", _("Modifications are not allowed anymore"));
return FALSE;
}
- if (! imodel->priv->modif_internals->modif_stmts [UPD_QUERY]) {
+ if (! imodel->priv->sh->modif_internals->modif_stmts [UPD_QUERY]) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
"%s", _("No UPDATE statement provided"));
return FALSE;
@@ -2593,9 +2643,9 @@
return FALSE;
}
- /* invalidate all the imodel->priv->modif_internals->modif_set's value holders */
+ /* invalidate all the imodel->priv->sh->modif_internals->modif_set's value holders */
GSList *list;
- for (list = imodel->priv->modif_internals->modif_set->holders; list; list = list->next) {
+ for (list = imodel->priv->sh->modif_internals->modif_set->holders; list; list = list->next) {
GdaHolder *h = (GdaHolder*) list->data;
if (param_name_to_int (gda_holder_get_id (h), NULL, NULL))
gda_holder_force_invalid ((GdaHolder*) list->data);
@@ -2603,7 +2653,7 @@
/* give values to params for new value */
str = g_strdup_printf ("+%d", col);
- holder = gda_set_get_holder (imodel->priv->modif_internals->modif_set, str);
+ holder = gda_set_get_holder (imodel->priv->sh->modif_internals->modif_set, str);
g_free (str);
if (! holder) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
@@ -2649,17 +2699,17 @@
/* arguments check */
g_return_val_if_fail (imodel->priv, FALSE);
- if (imodel->priv->modif_internals->safely_locked) {
+ if (imodel->priv->sh->modif_internals->safely_locked) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_SAFETY_LOCKED_ERROR,
"%s", _("Modifications are not allowed anymore"));
return FALSE;
}
- if (! (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)) {
+ if (! (imodel->priv->sh->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)) {
g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
"%s", _("Data model does only support random access"));
return FALSE;
}
- if (! imodel->priv->modif_internals->modif_stmts [UPD_QUERY]) {
+ if (! imodel->priv->sh->modif_internals->modif_stmts [UPD_QUERY]) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
"%s", _("No UPDATE statement provided"));
return FALSE;
@@ -2692,9 +2742,9 @@
return TRUE;
}
- /* invalidate all the imodel->priv->modif_internals->modif_set's value holders */
+ /* invalidate all the imodel->priv->sh->modif_internals->modif_set's value holders */
GSList *slist;
- for (slist = imodel->priv->modif_internals->modif_set->holders; slist; slist = slist->next) {
+ for (slist = imodel->priv->sh->modif_internals->modif_set->holders; slist; slist = slist->next) {
GdaHolder *h = (GdaHolder*) slist->data;
if (param_name_to_int (gda_holder_get_id (h), NULL, NULL))
gda_holder_force_invalid ((GdaHolder*) slist->data);
@@ -2706,7 +2756,7 @@
continue;
str = g_strdup_printf ("+%d", i);
- holder = gda_set_get_holder (imodel->priv->modif_internals->modif_set, str);
+ holder = gda_set_get_holder (imodel->priv->sh->modif_internals->modif_set, str);
g_free (str);
if (! holder) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
@@ -2736,17 +2786,17 @@
g_return_val_if_fail (imodel->priv, -1);
- if (imodel->priv->modif_internals->safely_locked) {
+ if (imodel->priv->sh->modif_internals->safely_locked) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_SAFETY_LOCKED_ERROR,
"%s", _("Modifications are not allowed anymore"));
return -1;
}
- if (! (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)) {
+ if (! (imodel->priv->sh->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)) {
g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
"%s", _("Data model does only support random access"));
return -1;
}
- if (! imodel->priv->modif_internals->modif_stmts [INS_QUERY]) {
+ if (! imodel->priv->sh->modif_internals->modif_stmts [INS_QUERY]) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
"%s", _("No INSERT statement provided"));
return -1;
@@ -2787,17 +2837,17 @@
/* compute INSERT statement */
GdaStatement *stmt;
- if (! imodel->priv->modif_internals->ins_stmts)
- imodel->priv->modif_internals->ins_stmts = g_hash_table_new_full ((GHashFunc) bvector_hash,
+ if (! imodel->priv->sh->modif_internals->ins_stmts)
+ imodel->priv->sh->modif_internals->ins_stmts = g_hash_table_new_full ((GHashFunc) bvector_hash,
(GEqualFunc) bvector_equal,
(GDestroyNotify) bvector_free,
g_object_unref);
- stmt = g_hash_table_lookup (imodel->priv->modif_internals->ins_stmts, bv);
+ stmt = g_hash_table_lookup (imodel->priv->sh->modif_internals->ins_stmts, bv);
if (! stmt) {
stmt = compute_single_insert_stmt (imodel, bv, error);
if (stmt) {
free_bv = FALSE;
- g_hash_table_insert (imodel->priv->modif_internals->ins_stmts, bv, stmt);
+ g_hash_table_insert (imodel->priv->sh->modif_internals->ins_stmts, bv, stmt);
}
else {
bvector_free (bv);
@@ -2811,7 +2861,7 @@
continue;
str = g_strdup_printf ("+%d", i);
- holder = gda_set_get_holder (imodel->priv->modif_internals->modif_set, str);
+ holder = gda_set_get_holder (imodel->priv->sh->modif_internals->modif_set, str);
g_free (str);
if (! holder) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
@@ -2836,7 +2886,7 @@
GError *lerror = NULL;
sql = gda_statement_to_sql_extended (stmt,
imodel->priv->cnc,
- imodel->priv->modif_internals->modif_set,
+ imodel->priv->sh->modif_internals->modif_set,
GDA_STATEMENT_SQL_PRETTY, NULL,
&lerror);
g_print ("%s(): SQL=> %s\n", __FUNCTION__, sql);
@@ -2850,30 +2900,30 @@
g_free (sql);
#endif
- if (! imodel->priv->modif_internals->one_row_select_stmt) {
- imodel->priv->modif_internals->one_row_select_stmt = compute_single_select_stmt (imodel, error);
- if (!imodel->priv->modif_internals->one_row_select_stmt)
+ if (! imodel->priv->sh->modif_internals->one_row_select_stmt) {
+ imodel->priv->sh->modif_internals->one_row_select_stmt = compute_single_select_stmt (imodel, error);
+ if (!imodel->priv->sh->modif_internals->one_row_select_stmt)
return -1;
}
GdaSet *last_insert;
if (gda_connection_statement_execute_non_select (imodel->priv->cnc, stmt,
- imodel->priv->modif_internals->modif_set, &last_insert, error) == -1)
+ imodel->priv->sh->modif_internals->modif_set, &last_insert, error) == -1)
return -1;
/* mark that this row has been modified */
DelayedSelectStmt *dstmt;
dstmt = g_new0 (DelayedSelectStmt, 1);
- if (last_insert && imodel->priv->modif_internals->one_row_select_stmt) {
- dstmt->select = g_object_ref (imodel->priv->modif_internals->one_row_select_stmt);
+ if (last_insert && imodel->priv->sh->modif_internals->one_row_select_stmt) {
+ dstmt->select = g_object_ref (imodel->priv->sh->modif_internals->one_row_select_stmt);
gda_statement_get_parameters (dstmt->select, &(dstmt->params), NULL);
if (dstmt->params) {
GSList *list;
- if (! imodel->priv->modif_internals->insert_to_select_mapping)
- imodel->priv->modif_internals->insert_to_select_mapping =
+ if (! imodel->priv->sh->modif_internals->insert_to_select_mapping)
+ imodel->priv->sh->modif_internals->insert_to_select_mapping =
compute_insert_select_params_mapping (dstmt->params, last_insert,
- imodel->priv->modif_internals->unique_row_condition);
- if (imodel->priv->modif_internals->insert_to_select_mapping) {
+ imodel->priv->sh->modif_internals->unique_row_condition);
+ if (imodel->priv->sh->modif_internals->insert_to_select_mapping) {
for (list = dstmt->params->holders; list; list = list->next) {
GdaHolder *holder = GDA_HOLDER (list->data);
GdaHolder *eholder;
@@ -2882,7 +2932,7 @@
g_assert (param_name_to_int (gda_holder_get_id (holder), &pos, NULL));
eholder = g_slist_nth_data (last_insert->holders,
- imodel->priv->modif_internals->insert_to_select_mapping[pos]);
+ imodel->priv->sh->modif_internals->insert_to_select_mapping[pos]);
if (!eholder ||
! gda_holder_set_value (holder, gda_holder_get_value (eholder), error)) {
g_object_unref (dstmt->params);
@@ -2897,13 +2947,13 @@
g_object_unref (last_insert);
dstmt->row = NULL;
- if (! imodel->priv->upd_rows)
- imodel->priv->upd_rows = g_hash_table_new_full (g_int_hash, g_int_equal,
+ if (! imodel->priv->sh->upd_rows)
+ imodel->priv->sh->upd_rows = g_hash_table_new_full (g_int_hash, g_int_equal,
g_free,
(GDestroyNotify) delayed_select_stmt_free);
gint *tmp = g_new (gint, 1);
*tmp = int_row;
- g_hash_table_insert (imodel->priv->upd_rows, tmp, dstmt);
+ g_hash_table_insert (imodel->priv->sh->upd_rows, tmp, dstmt);
#ifdef GDA_DEBUG_NO
dump_d (imodel);
#endif
@@ -2927,17 +2977,17 @@
g_return_val_if_fail (imodel->priv, FALSE);
- if (imodel->priv->modif_internals->safely_locked) {
+ if (imodel->priv->sh->modif_internals->safely_locked) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_SAFETY_LOCKED_ERROR,
"%s", _("Modifications are not allowed anymore"));
return FALSE;
}
- if (! (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)) {
+ if (! (imodel->priv->sh->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)) {
g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
"%s", _("Data model does only support random access"));
return FALSE;
}
- if (! imodel->priv->modif_internals->modif_stmts [DEL_QUERY]) {
+ if (! imodel->priv->sh->modif_internals->modif_stmts [DEL_QUERY]) {
g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
"%s", _("No DELETE statement provided"));
return FALSE;
@@ -2950,7 +3000,7 @@
ncols = gda_data_select_get_n_columns (model);
for (i = 0; i < ncols; i++) {
str = g_strdup_printf ("-%d", i);
- holder = gda_set_get_holder (imodel->priv->modif_internals->modif_set, str);
+ holder = gda_set_get_holder (imodel->priv->sh->modif_internals->modif_set, str);
g_free (str);
if (holder) {
const GValue *cvalue;
@@ -2965,9 +3015,9 @@
#ifdef GDA_DEBUG_NO
gchar *sql;
GError *lerror = NULL;
- sql = gda_statement_to_sql_extended (imodel->priv->modif_internals->modif_stmts [DEL_QUERY],
+ sql = gda_statement_to_sql_extended (imodel->priv->sh->modif_internals->modif_stmts [DEL_QUERY],
imodel->priv->cnc,
- imodel->priv->modif_internals->modif_set,
+ imodel->priv->sh->modif_internals->modif_set,
GDA_STATEMENT_SQL_PRETTY, NULL,
&lerror);
g_print ("%s(): SQL=> %s\n", __FUNCTION__, sql);
@@ -2976,19 +3026,19 @@
g_free (sql);
#endif
if (gda_connection_statement_execute_non_select (imodel->priv->cnc,
- imodel->priv->modif_internals->modif_stmts [DEL_QUERY],
- imodel->priv->modif_internals->modif_set, NULL, error) == -1)
+ imodel->priv->sh->modif_internals->modif_stmts [DEL_QUERY],
+ imodel->priv->sh->modif_internals->modif_set, NULL, error) == -1)
return FALSE;
/* mark that this row has been removed */
- if (!imodel->priv->del_rows)
- imodel->priv->del_rows = g_array_new (FALSE, FALSE, sizeof (gint));
- for (index = 0, i = 0; i < imodel->priv->del_rows->len; i++, index++) {
- if (g_array_index (imodel->priv->del_rows, gint, i) >= int_row)
+ if (!imodel->priv->sh->del_rows)
+ imodel->priv->sh->del_rows = g_array_new (FALSE, FALSE, sizeof (gint));
+ for (index = 0, i = 0; i < imodel->priv->sh->del_rows->len; i++, index++) {
+ if (g_array_index (imodel->priv->sh->del_rows, gint, i) >= int_row)
break;
}
- g_array_insert_val (imodel->priv->del_rows, index, int_row);
+ g_array_insert_val (imodel->priv->sh->del_rows, index, int_row);
gda_data_model_row_removed (model, row);
return TRUE;
@@ -3001,7 +3051,7 @@
*
* The way of preceeding is:
* - for each parameter required by model->one_row_select_stmt statement (the @sel_params argument),
- * use the model->priv->modif_internals->unique_row_condition to get the name of the corresponding column (the GdaHolder's ID
+ * use the model->priv->sh->modif_internals->unique_row_condition to get the name of the corresponding column (the GdaHolder's ID
* is "-<num1>" )
* - from the column name get the GdaHolder in the GdaSet retruned after the INSERT statement (the
* @ins_values argument) using the "name" property of each GdaHolder in the GdaSet (the GdaHolder's ID
@@ -3151,7 +3201,7 @@
/* FIXME: also set some column attributes using gda_column_set_attribute() */
- for (fields = select->expr_list, columns = model->priv->columns;
+ for (fields = select->expr_list, columns = model->priv->sh->columns;
fields && columns;
fields = fields->next) {
GdaSqlSelectField *selfield = (GdaSqlSelectField*) fields->data;
@@ -3195,7 +3245,7 @@
}
if (fields || columns)
g_warning ("Internal error: GdaDataSelect has %d GdaColumns, and SELECT statement has %d expressions",
- g_slist_length (model->priv->columns), g_slist_length (select->expr_list));
+ g_slist_length (model->priv->sh->columns), g_slist_length (select->expr_list));
out:
gda_sql_statement_free (sqlst);
Modified: trunk/libgda/gda-meta-store.c
==============================================================================
--- trunk/libgda/gda-meta-store.c (original)
+++ trunk/libgda/gda-meta-store.c Tue Apr 7 19:10:50 2009
@@ -1,6 +1,6 @@
/* gda-meta-store.c
*
- * Copyright (C) 2008 Vivien Malerba
+ * Copyright (C) 2008 - 2009 Vivien Malerba
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
@@ -39,7 +39,9 @@
#include <libgda/gda-util.h>
#include <libgda/gda-meta-struct.h>
#include <libgda/gda-connection.h>
-#include <libgda/gda-connection-sqlite.h>
+#include <libgda/gda-connection-internal.h>
+#include <libgda/gda-lockable.h>
+#include <libgda/gda-mutex.h>
#include "gda-types.h"
/*
@@ -215,6 +217,8 @@
gint max_extract_stmt; /* 0 => don't keep GdaStatement */
gint current_extract_stmt;
GHashTable *extract_stmt_hash; /* key = a SQL string, value = a #GdaStatement */
+
+ GdaMutex *mutex;
};
static void db_object_free (DbObject *dbobj);
@@ -497,6 +501,8 @@
store->priv->max_extract_stmt = 10;
store->priv->current_extract_stmt = 0;
store->priv->extract_stmt_hash = NULL;
+
+ store->priv->mutex = gda_mutex_new ();
}
static GObject *
@@ -619,6 +625,15 @@
g_object_unref (store);
store = NULL;
}
+ else {
+ if (gda_lockable_trylock (GDA_LOCKABLE (store->priv->cnc)))
+ gda_lockable_unlock (GDA_LOCKABLE (store->priv->cnc));
+ else {
+ g_warning (_("Can't obtain connection lock"));
+ g_object_unref (store);
+ store = NULL;
+ }
+ }
return store;
}
@@ -658,6 +673,8 @@
g_object_unref (G_OBJECT (store->priv->cnc));
store->priv->cnc = NULL;
}
+
+ gda_mutex_free (store->priv->mutex);
}
/* parent class */
@@ -699,7 +716,9 @@
cnc_string = g_value_get_string (value);
if (cnc_string) {
GdaConnection *cnc;
- cnc = gda_connection_open_from_string (NULL, cnc_string, NULL, 0, NULL);
+ cnc = gda_connection_open_from_string (NULL, cnc_string, NULL,
+ GDA_CONNECTION_OPTIONS_NONE,
+ NULL);
if (!cnc) {
if (g_ascii_strcasecmp (cnc_string, "sqlite")) {
/* use _gda_config_sqlite_provider */
@@ -713,7 +732,7 @@
break;
case PROP_CNC_OBJECT:
if (!store->priv->cnc)
- store->priv->cnc = g_value_get_object (value);
+ store->priv->cnc = g_value_dup_object (value);
break;
case PROP_CATALOG:
g_free (store->priv->catalog);
@@ -2010,6 +2029,8 @@
g_return_val_if_fail (GDA_IS_META_STORE (store), NULL);
g_return_val_if_fail (store->priv, NULL);
+ gda_mutex_lock (store->priv->mutex);
+
if ((store->priv->max_extract_stmt > 0) && !store->priv->extract_stmt_hash)
store->priv->extract_stmt_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
@@ -2024,12 +2045,15 @@
klass = (GdaMetaStoreClass *) G_OBJECT_GET_CLASS (store);
stmt = gda_sql_parser_parse_string (klass->cpriv->parser, select_sql, &remain, error);
- if (!stmt)
+ if (!stmt) {
+ gda_mutex_unlock (store->priv->mutex);
return NULL;
+ }
if (remain) {
g_set_error (error, GDA_META_STORE_ERROR, GDA_META_STORE_EXTRACT_SQL_ERROR,
"%s", _("More than one SQL statement"));
g_object_unref (stmt);
+ gda_mutex_unlock (store->priv->mutex);
return NULL;
}
@@ -2042,6 +2066,7 @@
/* parameters */
if (!gda_statement_get_parameters (stmt, ¶ms, error)) {
g_object_unref (stmt);
+ gda_mutex_unlock (store->priv->mutex);
return NULL;
}
if (params) {
@@ -2062,6 +2087,7 @@
g_object_unref (params);
va_end (ap);
g_slist_free (params_set);
+ gda_mutex_unlock (store->priv->mutex);
return NULL;
}
params_set = g_slist_prepend (params_set, h);
@@ -2082,6 +2108,8 @@
g_object_unref (stmt);
if (params)
g_object_unref (params);
+
+ gda_mutex_unlock (store->priv->mutex);
return model;
}
@@ -2215,11 +2243,15 @@
g_return_val_if_fail (gda_connection_is_opened (store->priv->cnc), FALSE);
g_return_val_if_fail (!new_data || GDA_IS_DATA_MODEL (new_data), FALSE);
+ gda_mutex_lock (store->priv->mutex);
+
/* get the correct TableInfo */
prep = prepare_tables_infos (store, &schema_set, &custom_set, &with_cond, table_name,
condition, error, nvalues, value_names, values);
- if (!prep)
+ if (!prep) {
+ gda_mutex_unlock (store->priv->mutex);
return FALSE;
+ }
GdaDataModel *current = NULL;
gboolean *rows_to_del = NULL;
@@ -2234,8 +2266,10 @@
with_cond ? custom_set->params : NULL,
GDA_STATEMENT_MODEL_RANDOM_ACCESS,
schema_set->type_cols_array, error);
- if (!current)
+ if (!current) {
+ gda_mutex_unlock (store->priv->mutex);
return FALSE;
+ }
current_n_rows = gda_data_model_get_n_rows (current);
current_n_cols = gda_data_model_get_n_columns (current);
rows_to_del = g_new (gboolean, current_n_rows);
@@ -2259,8 +2293,10 @@
/* remove everything from table */
if (gda_connection_statement_execute_non_select (store->priv->cnc,
schema_set->delete_all, NULL,
- NULL, error) == -1)
+ NULL, error) == -1) {
+ gda_mutex_unlock (store->priv->mutex);
return FALSE;
+ }
}
/* treat rows to insert / update */
@@ -2543,6 +2579,8 @@
g_print ("------- ROLLBACK\n");
#endif
}
+
+ gda_mutex_unlock (store->priv->mutex);
return retval;
}
@@ -2727,18 +2765,23 @@
if (store->priv->override_mode)
return TRUE;
+ gda_mutex_lock (store->priv->mutex);
if (! gda_connection_get_transaction_status (store->priv->cnc)) {
if (!gda_connection_begin_transaction (store->priv->cnc, NULL,
- GDA_TRANSACTION_ISOLATION_UNKNOWN, error))
+ GDA_TRANSACTION_ISOLATION_UNKNOWN, error)) {
+ gda_mutex_unlock (store->priv->mutex);
return FALSE;
+ }
}
else {
g_set_error (error, GDA_META_STORE_ERROR, GDA_META_STORE_TRANSACTION_ALREADY_STARTED_ERROR,
"%s", _("A transaction has already been started"));
+ gda_mutex_unlock (store->priv->mutex);
return FALSE;
}
store->priv->override_mode = TRUE;
+ gda_mutex_unlock (store->priv->mutex);
return TRUE;
}
@@ -2754,13 +2797,19 @@
gboolean
_gda_meta_store_cancel_data_reset (GdaMetaStore *store, GError **error)
{
+ gboolean retval;
g_return_val_if_fail (GDA_IS_META_STORE (store), FALSE);
- if (!store->priv->override_mode)
+ gda_mutex_lock (store->priv->mutex);
+ if (!store->priv->override_mode) {
+ gda_mutex_unlock (store->priv->mutex);
return TRUE;
+ }
store->priv->override_mode = FALSE;
- return gda_connection_rollback_transaction (store->priv->cnc, NULL, error);
+ retval = gda_connection_rollback_transaction (store->priv->cnc, NULL, error);
+ gda_mutex_unlock (store->priv->mutex);
+ return retval;
}
/**
@@ -2777,14 +2826,20 @@
{
g_return_val_if_fail (GDA_IS_META_STORE (store), FALSE);
- if (!store->priv->override_mode)
+ gda_mutex_lock (store->priv->mutex);
+ if (!store->priv->override_mode) {
+ gda_mutex_unlock (store->priv->mutex);
return TRUE;
+ }
store->priv->override_mode = FALSE;
- if (!gda_connection_commit_transaction (store->priv->cnc, NULL, error))
+ if (!gda_connection_commit_transaction (store->priv->cnc, NULL, error)) {
+ gda_mutex_unlock (store->priv->mutex);
return FALSE;
+ }
else {
g_signal_emit (store, gda_meta_store_signals[META_RESET], 0);
+ gda_mutex_unlock (store->priv->mutex);
return TRUE;
}
}
@@ -2831,14 +2886,18 @@
g_return_val_if_fail (GDA_IS_META_STORE (store), FALSE);
g_return_val_if_fail (table_name && *table_name, FALSE);
+ gda_mutex_lock (store->priv->mutex);
+
klass = (GdaMetaStoreClass *) G_OBJECT_GET_CLASS (store);
dbobj = g_hash_table_lookup (store->priv->p_db_objects_hash, table_name);
if (!dbobj) {
g_warning ("Table '%s' is not known by the GdaMetaStore", table_name);
+ gda_mutex_unlock (store->priv->mutex);
return NULL;
}
if (dbobj->obj_type != GDA_SERVER_OPERATION_CREATE_TABLE) {
g_warning ("Table '%s' is not a database table in the GdaMetaStore", table_name);
+ gda_mutex_unlock (store->priv->mutex);
return NULL;
}
@@ -2852,6 +2911,8 @@
gda_column_set_g_type (col, tcol->gtype);
gda_column_set_name (col, tcol->column_name);
}
+
+ gda_mutex_unlock (store->priv->mutex);
return model;
}
@@ -2874,6 +2935,8 @@
g_return_val_if_fail (GDA_IS_META_STORE (store), NULL);
+ gda_mutex_lock (store->priv->mutex);
+
klass = (GdaMetaStoreClass *) G_OBJECT_GET_CLASS (store);
for (ret = NULL, list = klass->cpriv->db_objects; list; list = list->next) {
DbObject *dbobj = DB_OBJECT (list->data);
@@ -2886,6 +2949,8 @@
ret = g_slist_prepend (ret, dbobj->obj_name);
}
+ gda_mutex_unlock (store->priv->mutex);
+
return g_slist_reverse (ret);
}
@@ -2914,9 +2979,13 @@
g_return_val_if_fail (table_name && *table_name, NULL);
klass = (GdaMetaStoreClass *) G_OBJECT_GET_CLASS (store);
+
+ gda_mutex_lock (store->priv->mutex);
dbo = g_hash_table_lookup (store->priv->p_db_objects_hash, table_name);
- if (!dbo)
+ if (!dbo) {
+ gda_mutex_unlock (store->priv->mutex);
return NULL;
+ }
for (ret = NULL, list = dbo->depend_list; list; list = list->next) {
DbObject *dbobj = DB_OBJECT (list->data);
@@ -2924,6 +2993,8 @@
ret = g_slist_prepend (ret, dbobj->obj_name);
}
+ gda_mutex_unlock (store->priv->mutex);
+
return g_slist_reverse (ret);
}
@@ -2933,7 +3004,7 @@
* @store: a #GdaMetaStore object
* @error: a place to store errors, or %NULL
*
- * Creates a new #GdaMetaStruct object representing @store's interal database structure.
+ * Creates a new #GdaMetaStruct object representing @store's internal database structure.
*
* Returns: a new #GdaMetaStruct object, or %NULL if an error occurred
*/
@@ -2943,33 +3014,52 @@
GdaMetaStruct *mstruct;
GdaDataModel *model;
gint i, nrows;
+ GdaMetaStore *real_store;
g_return_val_if_fail (GDA_IS_META_STORE (store), NULL);
+ gda_mutex_lock (store->priv->mutex);
+
/* make sure the private connection's meta store is up to date */
- if (! gda_connection_update_meta_store (store->priv->cnc, NULL, error))
+ if (! gda_connection_update_meta_store (store->priv->cnc, NULL, error)) {
+ gda_mutex_unlock (store->priv->mutex);
return NULL;
+ }
/* create a GdaMetaStruct */
- model = gda_meta_store_extract (store, "SELECT table_catalog, table_schema, table_name FROM _tables",
+ real_store = gda_connection_get_meta_store (store->priv->cnc);
+ model = gda_meta_store_extract (real_store,
+ "SELECT table_catalog, table_schema, table_name FROM _tables",
error, NULL);
- if (!model)
+ if (!model) {
+ gda_mutex_unlock (store->priv->mutex);
return NULL;
+ }
- mstruct = gda_meta_struct_new (store, GDA_META_STRUCT_FEATURE_ALL);
+ mstruct = gda_meta_struct_new (real_store, GDA_META_STRUCT_FEATURE_ALL);
nrows = gda_data_model_get_n_rows (model);
for (i = 0; i < nrows; i++) {
/* FIXME: only take into account the database objects which have a corresponding DbObject */
const GValue *cv0, *cv1, *cv2;
cv0 = gda_data_model_get_value_at (model, 0, i, error);
- if (!cv0) return NULL;
+ if (!cv0) {
+ gda_mutex_unlock (store->priv->mutex);
+ return NULL;
+ }
cv1 = gda_data_model_get_value_at (model, 1, i, error);
- if (!cv1) return NULL;
+ if (!cv1) {
+ gda_mutex_unlock (store->priv->mutex);
+ return NULL;
+ }
cv2 = gda_data_model_get_value_at (model, 2, i, error);
- if (!cv2) return NULL;
+ if (!cv2) {
+ gda_mutex_unlock (store->priv->mutex);
+ return NULL;
+ }
if (!gda_meta_struct_complement (mstruct, GDA_META_DB_UNKNOWN, cv0, cv1, cv2, error)) {
g_object_unref (mstruct);
g_object_unref (model);
+ gda_mutex_unlock (store->priv->mutex);
return NULL;
}
}
@@ -2981,8 +3071,9 @@
klass = (GdaMetaStoreClass *) G_OBJECT_GET_CLASS (store);
all_db_obj_list = g_slist_copy (klass->cpriv->db_objects);
- if (store->priv->p_db_objects)
- all_db_obj_list = g_slist_concat (all_db_obj_list, g_slist_copy (store->priv->p_db_objects));
+ if (real_store->priv->p_db_objects)
+ all_db_obj_list = g_slist_concat (all_db_obj_list,
+ g_slist_copy (real_store->priv->p_db_objects));
for (list = all_db_obj_list; list; list = list->next) {
DbObject *dbobj = DB_OBJECT (list->data);
@@ -3010,6 +3101,8 @@
}
}
g_slist_free (all_db_obj_list);
+
+ gda_mutex_unlock (store->priv->mutex);
return mstruct;
}
@@ -3043,13 +3136,17 @@
g_return_val_if_fail (att_name && *att_name, FALSE);
g_return_val_if_fail (att_value, FALSE);
+ gda_mutex_lock (store->priv->mutex);
+
*att_value = NULL;
g_value_set_string ((value = gda_value_new (G_TYPE_STRING)), att_name);
model = gda_meta_store_extract (store, "SELECT att_value FROM _attributes WHERE att_name = ##n::string", error,
"n", value, NULL);
gda_value_free (value);
- if (!model)
+ if (!model) {
+ gda_mutex_unlock (store->priv->mutex);
return FALSE;
+ }
nrows = gda_data_model_get_n_rows (model);
if (nrows < 1)
g_set_error (error, GDA_META_STORE_ERROR, GDA_META_STORE_ATTRIBUTE_NOT_FOUND_ERROR,
@@ -3060,16 +3157,20 @@
att_name, nrows);
else {
value = (GValue*) gda_data_model_get_value_at (model, 0, 0, error);
- if (!value)
+ if (!value) {
+ gda_mutex_unlock (store->priv->mutex);
return FALSE;
+ }
if (G_VALUE_TYPE (value) == G_TYPE_STRING) {
const gchar *val;
val = g_value_get_string (value);
if (val)
*att_value = g_strdup (val);
}
+ gda_mutex_unlock (store->priv->mutex);
return TRUE;
}
+ gda_mutex_unlock (store->priv->mutex);
return FALSE;
}
@@ -3103,18 +3204,23 @@
return FALSE;
}
+ gda_mutex_lock (store->priv->mutex);
+
klass = (GdaMetaStoreClass *) G_OBJECT_GET_CLASS (store);
g_static_mutex_lock (&set_mutex);
if (!set) {
if (!gda_statement_get_parameters (klass->cpriv->prep_stmts [STMT_SET_ATT_VALUE], &set, error)) {
g_static_mutex_unlock (&set_mutex);
+ gda_mutex_unlock (store->priv->mutex);
return FALSE;
}
}
g_static_mutex_unlock (&set_mutex);
- if (!gda_set_set_holder_value (set, error, "name", att_name))
+ if (!gda_set_set_holder_value (set, error, "name", att_name)) {
+ gda_mutex_unlock (store->priv->mutex);
return FALSE;
+ }
/* start a transaction if possible */
if (! gda_connection_get_transaction_status (store->priv->cnc))
@@ -3143,6 +3249,7 @@
}
if (started_transaction)
gda_connection_commit_transaction (store->priv->cnc, NULL, NULL);
+ gda_mutex_unlock (store->priv->mutex);
return TRUE;
onerror:
@@ -3235,6 +3342,8 @@
}
node = xmlDocGetRootElement (doc);
+ gda_mutex_lock (store->priv->mutex);
+
klass = (GdaMetaStoreClass *) G_OBJECT_GET_CLASS (store);
/* check that object name does not start with '_' */
@@ -3367,6 +3476,7 @@
}
}
+ gda_mutex_unlock (store->priv->mutex);
return TRUE;
onerror:
@@ -3386,11 +3496,13 @@
}
g_slist_free (current_objects);
}
+ gda_mutex_unlock (store->priv->mutex);
g_slist_free (pre_p_db_objects);
if (pstore)
g_object_unref (pstore);
if (mstruct)
g_object_unref (mstruct);
+
return FALSE;
}
@@ -3411,7 +3523,10 @@
g_return_val_if_fail (GDA_IS_META_STORE (store), FALSE);
g_return_val_if_fail (obj_name && *obj_name, FALSE);
+ gda_mutex_lock (store->priv->mutex);
TO_IMPLEMENT;
+ gda_mutex_unlock (store->priv->mutex);
+
return FALSE;
}
@@ -3430,20 +3545,27 @@
GSList *list, *retlist = NULL;
TableInfo *tinfo;
+ gda_mutex_lock (store->priv->mutex);
+
/* find the associated DbObject */
dbo = g_hash_table_lookup (store->priv->p_db_objects_hash, context->table_name);
if (!dbo) {
g_set_error (error, GDA_META_STORE_ERROR, GDA_META_STORE_SCHEMA_OBJECT_NOT_FOUND_ERROR,
_("Unknown database object '%s'"), context->table_name);
+ gda_mutex_unlock (store->priv->mutex);
return NULL;
}
- if (dbo->obj_type != GDA_SERVER_OPERATION_CREATE_TABLE)
+ if (dbo->obj_type != GDA_SERVER_OPERATION_CREATE_TABLE) {
+ gda_mutex_unlock (store->priv->mutex);
return NULL;
+ }
tinfo = TABLE_INFO (dbo);
- if (!tinfo->fk_list)
+ if (!tinfo->fk_list) {
/* this is not an error, just that there are no dependency */
+ gda_mutex_unlock (store->priv->mutex);
return NULL;
+ }
/* Identify the TableFKey if @context permits it */
if (context->size > 0) {
@@ -3500,6 +3622,8 @@
retlist = g_slist_prepend (retlist, ct);
}
}
+
+ gda_mutex_unlock (store->priv->mutex);
return g_slist_reverse (retlist);
}
@@ -3518,20 +3642,27 @@
GSList *list, *retlist = NULL;
TableInfo *tinfo;
+ gda_mutex_lock (store->priv->mutex);
+
/* find the associated DbObject */
dbo = g_hash_table_lookup (store->priv->p_db_objects_hash, context->table_name);
if (!dbo) {
g_set_error (error, GDA_META_STORE_ERROR, GDA_META_STORE_SCHEMA_OBJECT_NOT_FOUND_ERROR,
_("Unknown database object '%s'"), context->table_name);
+ gda_mutex_unlock (store->priv->mutex);
return NULL;
}
- if (dbo->obj_type != GDA_SERVER_OPERATION_CREATE_TABLE)
+ if (dbo->obj_type != GDA_SERVER_OPERATION_CREATE_TABLE) {
+ gda_mutex_unlock (store->priv->mutex);
return NULL;
+ }
tinfo = TABLE_INFO (dbo);
- if (!tinfo->reverse_fk_list)
+ if (!tinfo->reverse_fk_list) {
/* this is not an error, just that there are no dependency */
+ gda_mutex_unlock (store->priv->mutex);
return NULL;
+ }
for (list = tinfo->reverse_fk_list; list; list = list->next) {
TableFKey *tfk = (TableFKey*) list->data;
@@ -3545,5 +3676,6 @@
retlist = g_slist_prepend (retlist, ct);
}
+ gda_mutex_unlock (store->priv->mutex);
return g_slist_reverse (retlist);
}
Modified: trunk/libgda/gda-server-provider.c
==============================================================================
--- trunk/libgda/gda-server-provider.c (original)
+++ trunk/libgda/gda-server-provider.c Tue Apr 7 19:10:50 2009
@@ -112,6 +112,8 @@
klass->is_busy = NULL;
klass->cancel = NULL;
+ klass->handle_async = NULL;
+
klass->create_connection = NULL;
memset (&(klass->meta_funcs), 0, sizeof (GdaServerProviderMeta));
klass->xa_funcs = NULL;
Modified: trunk/libgda/gda-server-provider.h
==============================================================================
--- trunk/libgda/gda-server-provider.h (original)
+++ trunk/libgda/gda-server-provider.h Tue Apr 7 19:10:50 2009
@@ -294,8 +294,9 @@
/* distributed transaction */
GdaServerProviderXa *xa_funcs; /* it is a pointer! => set to %NULL if unsupported by provider */
+ gboolean (*handle_async) (GdaServerProvider *provider, GdaConnection *cnc, GError **error);
+
/* Padding for future expansion */
- void (*_gda_reserved1) (void);
void (*_gda_reserved2) (void);
void (*_gda_reserved3) (void);
void (*_gda_reserved4) (void);
Modified: trunk/libgda/libgda.symbols
==============================================================================
--- trunk/libgda/libgda.symbols (original)
+++ trunk/libgda/libgda.symbols Tue Apr 7 19:10:50 2009
@@ -86,6 +86,9 @@
gda_connection_add_event_string
gda_connection_add_prepared_statement
gda_connection_add_savepoint
+ gda_connection_async_statement_execute
+ gda_connection_async_fetch_result
+ gda_connection_async_cancel
gda_connection_batch_execute
gda_connection_begin_transaction
gda_connection_clear_events_list
@@ -703,6 +706,15 @@
gda_string_to_binary
gda_string_to_blob
gda_text_to_alphanum
+ gda_thread_wrapper_connect_raw
+ gda_thread_wrapper_disconnect
+ gda_thread_wrapper_execute
+ gda_thread_wrapper_execute_void
+ gda_thread_wrapper_get_type
+ gda_thread_wrapper_fetch_result
+ gda_thread_wrapper_get_waiting_size
+ gda_thread_wrapper_iterate
+ gda_thread_wrapper_new
gda_time_copy
gda_time_free
gda_time_get_type
Modified: trunk/libgda/providers-support/gda-data-select-priv.h
==============================================================================
--- trunk/libgda/providers-support/gda-data-select-priv.h (original)
+++ trunk/libgda/providers-support/gda-data-select-priv.h Tue Apr 7 19:10:50 2009
@@ -39,6 +39,10 @@
GdaRow *gda_data_select_get_stored_row (GdaDataSelect *model, gint rownum);
GdaConnection *gda_data_select_get_connection (GdaDataSelect *model);
+
+/* internal API */
+void _gda_data_select_share_private_data (GdaDataSelect *master, GdaDataSelect *slave);
+
G_END_DECLS
#endif
Modified: trunk/libgda/sqlite/gda-sqlite-recordset.c
==============================================================================
--- trunk/libgda/sqlite/gda-sqlite-recordset.c (original)
+++ trunk/libgda/sqlite/gda-sqlite-recordset.c Tue Apr 7 19:10:50 2009
@@ -565,6 +565,11 @@
return FALSE;
}
}
+ if (! *prow) {
+ *prow = gda_data_select_get_stored_row (model, rownum);
+ if (!*prow)
+ return FALSE;
+ }
return TRUE;
}
Added: trunk/libgda/thread-wrapper/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/libgda/thread-wrapper/Makefile.am Tue Apr 7 19:10:50 2009
@@ -0,0 +1,20 @@
+noinst_LTLIBRARIES = libgda_threadwrapper-4.0.la
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) -I$(srcdir)/.. \
+ -I$(top_builddir) \
+ $(LIBGDA_CFLAGS)
+
+libgda_threadwrapper_headers = \
+ gda-thread-wrapper.h
+
+libgda_threadwrapperincludedir=$(includedir)/libgda-$(GDA_ABI_MAJOR_VERSION).$(GDA_ABI_MINOR_VERSION)/libgda/thread-wrapper
+libgda_threadwrapperinclude_HEADERS=$(libgda_threadwrapper_headers)
+
+libgda_threadwrapper_4_0_la_SOURCES = \
+ $(libgda_threadwrapper_headers) \
+ gda-thread-provider.h \
+ gda-thread-provider.c \
+ gda-thread-recordset.h \
+ gda-thread-recordset.c \
+ gda-thread-wrapper.c
\ No newline at end of file
Added: trunk/libgda/thread-wrapper/gda-thread-provider.c
==============================================================================
--- (empty file)
+++ trunk/libgda/thread-wrapper/gda-thread-provider.c Tue Apr 7 19:10:50 2009
@@ -0,0 +1,1681 @@
+/* GDA Thread provider
+ * Copyright (C) 2008 The GNOME Foundation.
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include <glib/gstdio.h>
+#include <libgda/libgda.h>
+#include <libgda/gda-data-model-private.h>
+#include <libgda/gda-server-provider-extra.h>
+#include <libgda/gda-statement-extra.h>
+#include <sql-parser/gda-sql-parser.h>
+#include <libgda/gda-connection-internal.h>
+#include "gda-thread-provider.h"
+#include "gda-thread-wrapper.h"
+#include "gda-thread-recordset.h"
+
+#define PROV_CLASS(provider) (GDA_SERVER_PROVIDER_CLASS (G_OBJECT_GET_CLASS (provider)))
+
+/*
+ * Per connection private data is defined as ThreadConnectionData
+ */
+
+/*
+ * GObject methods
+ */
+static void gda_thread_provider_class_init (GdaThreadProviderClass *klass);
+static void gda_thread_provider_init (GdaThreadProvider *provider,
+ GdaThreadProviderClass *klass);
+
+static GObjectClass *parent_class = NULL;
+
+/*
+ * GdaServerProvider's virtual methods
+ */
+/* connection management */
+static GdaConnection *gda_thread_provider_create_connection (GdaServerProvider *provider);
+static gboolean gda_thread_provider_open_connection (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaQuarkList *params, GdaQuarkList *auth,
+ guint *task_id, GdaServerProviderAsyncCallback async_cb, gpointer cb_data);
+static gboolean gda_thread_provider_close_connection (GdaServerProvider *provider, GdaConnection *cnc);
+static const gchar *gda_thread_provider_get_server_version (GdaServerProvider *provider, GdaConnection *cnc);
+static const gchar *gda_thread_provider_get_database (GdaServerProvider *provider, GdaConnection *cnc);
+
+/* DDL operations */
+static gboolean gda_thread_provider_supports_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperationType type, GdaSet *options);
+static GdaServerOperation *gda_thread_provider_create_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperationType type,
+ GdaSet *options, GError **error);
+static gchar *gda_thread_provider_render_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error);
+
+static gboolean gda_thread_provider_perform_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, guint *task_id,
+ GdaServerProviderAsyncCallback async_cb, gpointer cb_data,
+ GError **error);
+/* transactions */
+static gboolean gda_thread_provider_begin_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GdaTransactionIsolation level, GError **error);
+static gboolean gda_thread_provider_commit_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error);
+static gboolean gda_thread_provider_rollback_transaction (GdaServerProvider *provider, GdaConnection * cnc,
+ const gchar *name, GError **error);
+static gboolean gda_thread_provider_add_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error);
+static gboolean gda_thread_provider_rollback_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error);
+static gboolean gda_thread_provider_delete_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error);
+
+/* information retreival */
+static const gchar *gda_thread_provider_get_version (GdaServerProvider *provider);
+static gboolean gda_thread_provider_supports_feature (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaConnectionFeature feature);
+
+static const gchar *gda_thread_provider_get_name (GdaServerProvider *provider);
+
+static GdaDataHandler *gda_thread_provider_get_data_handler (GdaServerProvider *provider, GdaConnection *cnc,
+ GType g_type, const gchar *dbms_type);
+
+static const gchar* gda_thread_provider_get_default_dbms_type (GdaServerProvider *provider, GdaConnection *cnc,
+ GType type);
+/* statements */
+static GdaSqlParser *gda_thread_provider_create_parser (GdaServerProvider *provider, GdaConnection *cnc);
+static gchar *gda_thread_provider_statement_to_sql (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GdaSet *params,
+ GdaStatementSqlFlag flags,
+ GSList **params_used, GError **error);
+static gboolean gda_thread_provider_statement_prepare (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GError **error);
+static GObject *gda_thread_provider_statement_execute (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GdaSet *params,
+ GdaStatementModelUsage model_usage,
+ GType *col_types, GdaSet **last_inserted_row,
+ guint *task_id, GdaServerProviderExecCallback async_cb,
+ gpointer cb_data, GError **error);
+
+/* distributed transactions */
+static gboolean gda_thread_provider_xa_start (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error);
+
+static gboolean gda_thread_provider_xa_end (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error);
+static gboolean gda_thread_provider_xa_prepare (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error);
+
+static gboolean gda_thread_provider_xa_commit (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error);
+static gboolean gda_thread_provider_xa_rollback (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error);
+
+static GList *gda_thread_provider_xa_recover (GdaServerProvider *provider, GdaConnection *cnc,
+ GError **error);
+
+static void gda_thread_free_cnc_data (ThreadConnectionData *cdata);
+
+/*
+ * GdaThreadProvider class implementation
+ */
+static void
+gda_thread_provider_class_init (GdaThreadProviderClass *klass)
+{
+ GdaServerProviderClass *provider_class = GDA_SERVER_PROVIDER_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ provider_class->get_version = gda_thread_provider_get_version;
+ provider_class->get_server_version = gda_thread_provider_get_server_version;
+ provider_class->get_name = gda_thread_provider_get_name;
+ provider_class->supports_feature = gda_thread_provider_supports_feature;
+
+ provider_class->get_data_handler = gda_thread_provider_get_data_handler;
+ provider_class->get_def_dbms_type = gda_thread_provider_get_default_dbms_type;
+
+ provider_class->open_connection = gda_thread_provider_open_connection;
+ provider_class->close_connection = gda_thread_provider_close_connection;
+ provider_class->get_database = gda_thread_provider_get_database;
+
+ provider_class->supports_operation = gda_thread_provider_supports_operation;
+ provider_class->create_operation = gda_thread_provider_create_operation;
+ provider_class->render_operation = gda_thread_provider_render_operation;
+ provider_class->perform_operation = gda_thread_provider_perform_operation;
+
+ provider_class->begin_transaction = gda_thread_provider_begin_transaction;
+ provider_class->commit_transaction = gda_thread_provider_commit_transaction;
+ provider_class->rollback_transaction = gda_thread_provider_rollback_transaction;
+ provider_class->add_savepoint = gda_thread_provider_add_savepoint;
+ provider_class->rollback_savepoint = gda_thread_provider_rollback_savepoint;
+ provider_class->delete_savepoint = gda_thread_provider_delete_savepoint;
+
+ provider_class->create_parser = gda_thread_provider_create_parser;
+ provider_class->statement_to_sql = gda_thread_provider_statement_to_sql;
+ provider_class->statement_prepare = gda_thread_provider_statement_prepare;
+ provider_class->statement_execute = gda_thread_provider_statement_execute;
+
+ provider_class->is_busy = NULL;
+ provider_class->cancel = NULL;
+ provider_class->create_connection = gda_thread_provider_create_connection;
+
+ memset (&(provider_class->meta_funcs), 0, sizeof (GdaServerProviderMeta));
+
+ /* distributed transactions: if not supported, then provider_class->xa_funcs should be set to NULL */
+ provider_class->xa_funcs = g_new0 (GdaServerProviderXa, 1);
+ provider_class->xa_funcs->xa_start = gda_thread_provider_xa_start;
+ provider_class->xa_funcs->xa_end = gda_thread_provider_xa_end;
+ provider_class->xa_funcs->xa_prepare = gda_thread_provider_xa_prepare;
+ provider_class->xa_funcs->xa_commit = gda_thread_provider_xa_commit;
+ provider_class->xa_funcs->xa_rollback = gda_thread_provider_xa_rollback;
+ provider_class->xa_funcs->xa_recover = gda_thread_provider_xa_recover;
+}
+
+static void
+gda_thread_provider_init (GdaThreadProvider *thread_prv, GdaThreadProviderClass *klass)
+{
+}
+
+GType
+gda_thread_provider_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+ static GTypeInfo info = {
+ sizeof (GdaThreadProviderClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gda_thread_provider_class_init,
+ NULL, NULL,
+ sizeof (GdaThreadProvider),
+ 0,
+ (GInstanceInitFunc) gda_thread_provider_init
+ };
+ g_static_mutex_lock (®istering);
+ if (type == 0)
+ type = g_type_register_static (GDA_TYPE_SERVER_PROVIDER, "GdaThreadProvider", &info, 0);
+ g_static_mutex_unlock (®istering);
+ }
+
+ return type;
+}
+
+
+/*
+ * Get provider name request
+ */
+static const gchar *
+gda_thread_provider_get_name (GdaServerProvider *provider)
+{
+ return "ThreadWrapper";
+}
+
+/*
+ * Get provider's version, no need to change this
+ */
+static const gchar *
+gda_thread_provider_get_version (GdaServerProvider *provider)
+{
+ return PACKAGE_VERSION;
+}
+
+static GdaConnection *
+gda_thread_provider_create_connection (GdaServerProvider *provider)
+{
+ GdaConnection *cnc;
+ g_return_val_if_fail (GDA_IS_THREAD_PROVIDER (provider), NULL);
+
+ cnc = g_object_new (GDA_TYPE_CONNECTION, "provider", provider, "is-wrapper", TRUE, NULL);
+
+ return cnc;
+}
+
+/*
+ * Open connection request
+ *
+ * Returns: TRUE if no error occurred, or FALSE otherwise (and an ERROR connection event must be added to @cnc)
+ */
+typedef struct {
+ const gchar *dsn;
+
+ const gchar *prov_name;
+ const gchar *cnc_string;
+
+ const gchar *auth_string;
+
+ GdaConnectionOptions options;
+
+ GdaServerProvider *out_cnc_provider;
+} OpenConnectionData;
+
+static GdaConnection *
+sub_thread_open_connection (OpenConnectionData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ GdaConnection *cnc;
+ if (data->dsn)
+ cnc = gda_connection_open_from_dsn (data->dsn, data->auth_string, data->options, error);
+ else
+ cnc = gda_connection_open_from_string (data->prov_name, data->cnc_string,
+ data->auth_string, data->options, error);
+ if (cnc)
+ data->out_cnc_provider = gda_connection_get_provider (cnc);
+ g_print ("/%s() => %p\n", __FUNCTION__, cnc);
+ return cnc;
+}
+
+static void setup_signals (GdaConnection *cnc, ThreadConnectionData *cdata);
+
+static gboolean
+gda_thread_provider_open_connection (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaQuarkList *params, GdaQuarkList *auth,
+ guint *task_id, GdaServerProviderAsyncCallback async_cb, gpointer cb_data)
+{
+ g_return_val_if_fail (GDA_IS_THREAD_PROVIDER (provider), FALSE);
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+
+ /* If asynchronous connection opening is not supported, then exit now */
+ if (async_cb) {
+ gda_connection_add_event_string (cnc,
+ _("Provider does not support asynchronous connection open"));
+ return FALSE;
+ }
+
+ /* test if connection has to be opened using a DSN or a connection string */
+ gchar *dsn, *auth_string, *cnc_string;
+ GdaConnectionOptions options;
+ OpenConnectionData *data = NULL;
+ g_object_get (cnc, "dsn", &dsn,
+ "auth_string", &auth_string,
+ "cnc-string", &cnc_string,
+ "options", &options,
+ NULL);
+ if (dsn) {
+ data = g_new0 (OpenConnectionData, 1);
+ data->dsn = dsn;
+ }
+ else if (cnc_string) {
+ data = g_new0 (OpenConnectionData, 1);
+ data->prov_name = gda_quark_list_find (params, "PROVIDER_NAME");
+ data->cnc_string = cnc_string;
+ }
+
+ /* open sub connection */
+ GdaThreadWrapper *wr;
+ GdaConnection *sub_cnc;
+ GError *error = NULL;
+ g_assert (data);
+ data->auth_string = auth_string;
+ data->options = options & (~GDA_CONNECTION_OPTIONS_THREAD_SAFE);
+ wr = gda_thread_wrapper_new ();
+ gda_thread_wrapper_execute (wr, (GdaThreadWrapperFunc) sub_thread_open_connection, data, NULL, NULL);
+ sub_cnc = gda_thread_wrapper_fetch_result (wr, TRUE, NULL, &error);
+ g_free (dsn);
+ g_free (cnc_string);
+ g_free (auth_string);
+ if (!sub_cnc) {
+ TO_IMPLEMENT; /* create a GdaConnectionEvent from @error using
+ gda_connection_add_event_string () */
+ g_object_unref (wr);
+ g_free (data);
+ return FALSE;
+ }
+
+ ThreadConnectionData *cdata;
+ cdata = g_new0 (ThreadConnectionData, 1);
+ cdata->sub_connection = sub_cnc;
+ cdata->cnc_provider = data->out_cnc_provider;
+ cdata->wrapper = wr;
+ cdata->handlers_ids = g_array_sized_new (FALSE, FALSE, sizeof (gulong), 2);
+ g_free (data);
+ gda_connection_internal_set_provider_data (cnc, cdata, (GDestroyNotify) gda_thread_free_cnc_data);
+ setup_signals (cnc, cdata);
+
+ return TRUE;
+}
+
+static void
+sub_cnc_error_cb (GdaThreadWrapper *wrapper, GdaConnection *sub_cnc, const gchar *signal,
+ gint n_param_values, const GValue *param_values,
+ gpointer gda_reserved, GdaConnection *wrapper_cnc)
+{
+ GdaConnectionEvent *ev;
+ g_assert (n_param_values == 1);
+ ev = g_value_get_object (param_values);
+ g_object_ref (ev);
+ gda_connection_add_event (wrapper_cnc, ev);
+}
+
+static void
+sub_cnc_transaction_status_changed_cb (GdaThreadWrapper *wrapper, GdaConnection *sub_cnc, const gchar *signal,
+ gint n_param_values, const GValue *param_values,
+ gpointer gda_reserved, GdaConnection *wrapper_cnc)
+{
+ _gda_connection_force_transaction_status (wrapper_cnc, sub_cnc);
+}
+
+static void
+setup_signals (GdaConnection *cnc, ThreadConnectionData *cdata)
+{
+ gulong hid;
+ hid = gda_thread_wrapper_connect_raw (cdata->wrapper, cdata->sub_connection, "error",
+ (GdaThreadWrapperCallback) sub_cnc_error_cb, cnc);
+ g_array_prepend_val (cdata->handlers_ids, hid);
+ hid = gda_thread_wrapper_connect_raw (cdata->wrapper, cdata->sub_connection, "transaction-status-changed",
+ (GdaThreadWrapperCallback) sub_cnc_transaction_status_changed_cb, cnc);
+ g_array_prepend_val (cdata->handlers_ids, hid);
+}
+
+/*
+ * Close connection request
+ *
+ * In this function, the following _must_ be done:
+ * - Actually close the connection to the database using @cnc's associated ThreadConnectionData structure
+ * - Free the ThreadConnectionData structure and its contents
+ *
+ * Returns: TRUE if no error occurred, or FALSE otherwise (and an ERROR gonnection event must be added to @cnc)
+ */
+typedef struct {
+ GdaServerProvider *prov;
+ GdaConnection *cnc;
+} CncProvData;
+
+static gpointer
+sub_thread_close_connection (CncProvData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval;
+ retval = PROV_CLASS (data->prov)->close_connection (data->prov, data->cnc);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval ? 1 : 0);
+}
+
+static gboolean
+gda_thread_provider_close_connection (GdaServerProvider *provider, GdaConnection *cnc)
+{
+ ThreadConnectionData *cdata;
+ CncProvData wdata;
+ gpointer res;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_close_connection, &wdata, NULL, NULL);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+ return GPOINTER_TO_INT (res) ? TRUE : FALSE;
+}
+
+/*
+ * Server version request
+ *
+ * Returns the server version as a string, which should be stored in @cnc's associated ThreadConnectionData structure
+ */
+
+static const gchar *
+sub_thread_get_server_version (CncProvData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ const gchar *retval;
+ retval = PROV_CLASS (data->prov)->get_server_version (data->prov, data->cnc);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval);
+ return retval;
+}
+static const gchar *
+gda_thread_provider_get_server_version (GdaServerProvider *provider, GdaConnection *cnc)
+{
+ ThreadConnectionData *cdata;
+ CncProvData wdata;
+
+ if (!cnc)
+ return NULL;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return NULL;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_get_server_version, &wdata, NULL, NULL);
+ return (const gchar*) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+}
+
+/*
+ * Get database request
+ *
+ * Returns the database name as a string, which should be stored in @cnc's associated ThreadConnectionData structure
+ */
+static const gchar *
+sub_thread_get_database (CncProvData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ const gchar *retval;
+ retval = PROV_CLASS (data->prov)->get_database (data->prov, data->cnc);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval);
+ return retval;
+}
+
+static const gchar *
+gda_thread_provider_get_database (GdaServerProvider *provider, GdaConnection *cnc)
+{
+ ThreadConnectionData *cdata;
+ CncProvData wdata;
+
+ if (!cnc)
+ return NULL;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return NULL;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_get_database, &wdata, NULL, NULL);
+ return (const gchar *) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+}
+
+/*
+ * Support operation request
+ *
+ * Tells what the implemented server operations are. To add support for an operation, the following steps are required:
+ * - create a thread_specs_....xml.in file describing the required and optional parameters for the operation
+ * - add it to the Makefile.am
+ * - make this method return TRUE for the operation type
+ * - implement the gda_thread_provider_render_operation() and gda_thread_provider_perform_operation() methods
+ *
+ * In this example, the GDA_SERVER_OPERATION_CREATE_TABLE is implemented
+ */
+typedef struct {
+ GdaServerProvider *prov;
+ GdaConnection *cnc;
+ GdaServerOperationType type;
+ GdaSet *options;
+} SupportsOperationData;
+
+static gpointer
+sub_thread_supports_operation (SupportsOperationData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval;
+ retval = PROV_CLASS (data->prov)->supports_operation (data->prov, data->cnc, data->type, data->options);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval ? 1 : 0);
+}
+
+static gboolean
+gda_thread_provider_supports_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperationType type, GdaSet *options)
+{
+ ThreadConnectionData *cdata;
+ SupportsOperationData wdata;
+ gpointer res;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_supports_operation, &wdata, NULL, NULL);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+ return GPOINTER_TO_INT (res) ? TRUE : FALSE;
+}
+
+/*
+ * Create operation request
+ *
+ * Creates a #GdaServerOperation. The following code is generic and should only be changed
+ * if some further initialization is required, or if operation's contents is dependant on @cnc
+ */
+typedef struct {
+ GdaServerProvider *prov;
+ GdaConnection *cnc;
+ GdaServerOperationType type;
+ GdaSet *options;
+} CreateOperationData;
+
+static GdaServerOperation *
+sub_thread_create_operation (CreateOperationData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ GdaServerOperation *op;
+ op = PROV_CLASS (data->prov)->create_operation (data->prov,
+ data->cnc,
+ data->type,
+ data->options,
+ error);
+ g_print ("/%s() => %p\n", __FUNCTION__, op);
+ return op;
+}
+
+static GdaServerOperation *
+gda_thread_provider_create_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperationType type, GdaSet *options, GError **error)
+{
+ ThreadConnectionData *cdata;
+ CreateOperationData wdata;
+
+ if (!cnc) {
+ g_set_error (error, 0, 0, _("A connection is required"));
+ return NULL;
+ }
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return NULL;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.type = type;
+ wdata.options = options;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_create_operation, &wdata, NULL, NULL);
+ return (GdaServerOperation*) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, error);
+}
+
+/*
+ * Render operation request
+ */
+typedef struct {
+ GdaServerProvider *prov;
+ GdaConnection *cnc;
+ GdaServerOperation *op;
+} RenderOperationData;
+
+static gchar *
+sub_thread_render_operation (RenderOperationData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gchar *str;
+ str = PROV_CLASS (data->prov)->render_operation (data->prov,
+ data->cnc,
+ data->op,
+ error);
+ g_print ("/%s() => %s\n", __FUNCTION__, str);
+ return str;
+}
+
+static gchar *
+gda_thread_provider_render_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error)
+{
+ ThreadConnectionData *cdata;
+ RenderOperationData wdata;
+
+ if (!cnc) {
+ g_set_error (error, 0, 0, _("A connection is required"));
+ return NULL;
+ }
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return NULL;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.op = op;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_render_operation, &wdata, NULL, NULL);
+ return (gchar*) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, error);
+}
+
+/*
+ * Perform operation request
+ */
+typedef struct {
+ GdaServerProvider *prov;
+ GdaConnection *cnc;
+ GdaServerOperation *op;
+} PerformOperationData;
+
+static gpointer
+sub_thread_perform_operation (PerformOperationData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval;
+ retval = PROV_CLASS (data->prov)->perform_operation (data->prov, data->cnc, data->op,
+ NULL, NULL, NULL, error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval ? 1 : 0);
+}
+
+static gboolean
+gda_thread_provider_perform_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, guint *task_id,
+ GdaServerProviderAsyncCallback async_cb, gpointer cb_data, GError **error)
+{
+ ThreadConnectionData *cdata;
+ PerformOperationData wdata;
+ gpointer res;
+
+ /* If asynchronous connection opening is not supported, then exit now */
+ if (async_cb) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_METHOD_NON_IMPLEMENTED_ERROR,
+ "%s", _("Provider does not support asynchronous server operation"));
+ return FALSE;
+ }
+
+ if (!cnc) {
+ g_set_error (error, 0, 0, _("A connection is required"));
+ return FALSE;
+ }
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.op = op;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_perform_operation, &wdata, NULL, error);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+ return GPOINTER_TO_INT (res) ? TRUE : FALSE;
+}
+
+/*
+ * Begin transaction request
+ */
+typedef struct {
+ GdaServerProvider *prov;
+ GdaConnection *cnc;
+ const gchar *name;
+ GdaTransactionIsolation level;
+} BeginTransactionData;
+
+static gpointer
+sub_thread_begin_transaction (BeginTransactionData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval;
+ retval = PROV_CLASS (data->prov)->begin_transaction (data->prov, data->cnc, data->name,
+ data->level, error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval ? 1 : 0);
+}
+
+static gboolean
+gda_thread_provider_begin_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GdaTransactionIsolation level,
+ GError **error)
+{
+ ThreadConnectionData *cdata;
+ BeginTransactionData wdata;
+ gpointer res;
+
+ if (!cnc) {
+ g_set_error (error, 0, 0, _("A connection is required"));
+ return FALSE;
+ }
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.name = name;
+ wdata.level = level;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_begin_transaction, &wdata, NULL, error);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, error);
+ return GPOINTER_TO_INT (res) ? TRUE : FALSE;
+}
+
+/*
+ * Commit transaction request
+ */
+typedef struct {
+ GdaServerProvider *prov;
+ GdaConnection *cnc;
+ const gchar *name;
+ GdaTransactionIsolation level;
+} TransactionData;
+
+static gpointer
+sub_thread_commit_transaction (TransactionData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval;
+ retval = PROV_CLASS (data->prov)->commit_transaction (data->prov, data->cnc, data->name,
+ error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval ? 1 : 0);
+}
+
+static gboolean
+gda_thread_provider_commit_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error)
+{
+ ThreadConnectionData *cdata;
+ TransactionData wdata;
+ gpointer res;
+
+ if (!cnc) {
+ g_set_error (error, 0, 0, _("A connection is required"));
+ return FALSE;
+ }
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.name = name;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_commit_transaction, &wdata, NULL, error);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+ return GPOINTER_TO_INT (res) ? TRUE : FALSE;
+}
+
+/*
+ * Rollback transaction request
+ */
+static gpointer
+sub_thread_rollback_transaction (TransactionData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval;
+ retval = PROV_CLASS (data->prov)->rollback_transaction (data->prov, data->cnc, data->name,
+ error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval ? 1 : 0);
+}
+
+static gboolean
+gda_thread_provider_rollback_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error)
+{
+ ThreadConnectionData *cdata;
+ TransactionData wdata;
+ gpointer res;
+
+ if (!cnc) {
+ g_set_error (error, 0, 0, _("A connection is required"));
+ return FALSE;
+ }
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.name = name;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_rollback_transaction, &wdata, NULL, error);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+ return GPOINTER_TO_INT (res) ? TRUE : FALSE;
+}
+
+/*
+ * Add savepoint request
+ */
+static gpointer
+sub_thread_add_savepoint (TransactionData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval;
+ retval = PROV_CLASS (data->prov)->add_savepoint (data->prov, data->cnc, data->name,
+ error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval ? 1 : 0);
+}
+
+static gboolean
+gda_thread_provider_add_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error)
+{
+ ThreadConnectionData *cdata;
+ TransactionData wdata;
+ gpointer res;
+
+ if (!cnc) {
+ g_set_error (error, 0, 0, _("A connection is required"));
+ return FALSE;
+ }
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.name = name;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_add_savepoint, &wdata, NULL, error);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+ return GPOINTER_TO_INT (res) ? TRUE : FALSE;
+}
+
+/*
+ * Rollback savepoint request
+ */
+static gpointer
+sub_thread_rollback_savepoint (TransactionData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval;
+ retval = PROV_CLASS (data->prov)->rollback_savepoint (data->prov, data->cnc, data->name,
+ error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval ? 1 : 0);
+}
+
+static gboolean
+gda_thread_provider_rollback_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error)
+{
+ ThreadConnectionData *cdata;
+ TransactionData wdata;
+ gpointer res;
+
+ if (!cnc) {
+ g_set_error (error, 0, 0, _("A connection is required"));
+ return FALSE;
+ }
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.name = name;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_rollback_savepoint, &wdata, NULL, error);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+ return GPOINTER_TO_INT (res) ? TRUE : FALSE;
+}
+
+/*
+ * Delete savepoint request
+ */
+static gpointer
+sub_thread_delete_savepoint (TransactionData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval;
+ retval = PROV_CLASS (data->prov)->delete_savepoint (data->prov, data->cnc, data->name,
+ error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval ? 1 : 0);
+}
+
+static gboolean
+gda_thread_provider_delete_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error)
+{
+ ThreadConnectionData *cdata;
+ TransactionData wdata;
+ gpointer res;
+
+ if (!cnc) {
+ g_set_error (error, 0, 0, _("A connection is required"));
+ return FALSE;
+ }
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.name = name;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_delete_savepoint, &wdata, NULL, error);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+ return GPOINTER_TO_INT (res) ? TRUE : FALSE;
+}
+
+/*
+ * Feature support request
+ */
+static gboolean
+gda_thread_provider_supports_feature (GdaServerProvider *provider, GdaConnection *cnc, GdaConnectionFeature feature)
+{
+ if (cnc) {
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+ }
+
+ switch (feature) {
+ case GDA_CONNECTION_FEATURE_SQL :
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/*
+ * Get data handler request
+ *
+ * This method allows one to obtain a pointer to a #GdaDataHandler object specific to @type or @dbms_type (@dbms_type
+ * must be considered only if @type is not a valid GType).
+ *
+ * A data handler allows one to convert a value between its different representations which are a human readable string,
+ * an SQL representation and a GValue.
+ *
+ * The recommended method is to create GdaDataHandler objects only when they are needed and to keep a reference to them
+ * for further usage, using the gda_server_provider_handler_declare() method.
+ *
+ * The implementation shown here does not define any specific data handler, but there should be some for at least
+ * binary and time related types.
+ */
+typedef struct {
+ GdaServerProvider *prov;
+ GdaConnection *cnc;
+ GType type;
+ const gchar *dbms_type;
+} GetDataHandlerData;
+
+static GdaDataHandler *
+sub_thread_get_data_handler (GetDataHandlerData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ GdaDataHandler *retval;
+ retval = PROV_CLASS (data->prov)->get_data_handler (data->prov, data->cnc, data->type, data->dbms_type);
+ g_print ("/%s() => %p\n", __FUNCTION__, retval);
+ return retval;
+}
+
+static GdaDataHandler *
+gda_thread_provider_get_data_handler (GdaServerProvider *provider, GdaConnection *cnc,
+ GType type, const gchar *dbms_type)
+{
+ ThreadConnectionData *cdata;
+ GetDataHandlerData wdata;
+ GdaDataHandler *res;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.type = type;
+ wdata.dbms_type = dbms_type;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_get_data_handler, &wdata, NULL, NULL);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+ return res;
+}
+
+/*
+ * Get default DBMS type request
+ *
+ * This method returns the "preferred" DBMS type for GType
+ */
+typedef struct {
+ GdaServerProvider *prov;
+ GdaConnection *cnc;
+ GType type;
+} GetDefaultDbmsTypeData;
+
+static const gchar *
+sub_thread_get_default_dbms_type (GetDefaultDbmsTypeData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ const gchar *retval;
+ retval = PROV_CLASS (data->prov)->get_def_dbms_type (data->prov, data->cnc, data->type);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval);
+ return retval;
+}
+
+static const gchar*
+gda_thread_provider_get_default_dbms_type (GdaServerProvider *provider, GdaConnection *cnc, GType type)
+{
+ ThreadConnectionData *cdata;
+ GetDefaultDbmsTypeData wdata;
+ const gchar *res;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.type = type;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_get_default_dbms_type, &wdata, NULL, NULL);
+ res = (const gchar *) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+ return res;
+}
+
+/*
+ * Create parser request
+ *
+ * This method is responsible for creating a #GdaSqlParser object specific to the SQL dialect used
+ * by the database. See the PostgreSQL provider implementation for an example.
+ */
+
+static GdaSqlParser *
+sub_thread_create_parser (CncProvData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ GdaSqlParser *parser;
+ parser = PROV_CLASS (data->prov)->create_parser (data->prov, data->cnc);
+ g_print ("/%s() => %p\n", __FUNCTION__, parser);
+ return parser;
+}
+
+static GdaSqlParser *
+gda_thread_provider_create_parser (GdaServerProvider *provider, GdaConnection *cnc)
+{
+ ThreadConnectionData *cdata;
+ CncProvData wdata;
+
+ if (!cnc)
+ return NULL;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return NULL;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_create_parser, &wdata, NULL, NULL);
+ return (GdaSqlParser *) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+}
+
+/*
+ * GdaStatement to SQL request
+ *
+ * This method renders a #GdaStatement into its SQL representation.
+ *
+ * The implementation show here simply calls gda_statement_to_sql_extended() but the rendering
+ * can be specialized to the database's SQL dialect, see the implementation of gda_statement_to_sql_extended()
+ * and SQLite's specialized rendering for more details
+ */
+typedef struct {
+ GdaServerProvider *prov;
+ GdaConnection *cnc;
+ GdaStatement *stmt;
+ GdaSet *params;
+ GdaStatementSqlFlag flags;
+ GSList **params_used;
+} StmtToSqlData;
+
+static const gchar *
+sub_thread_statement_to_sql (StmtToSqlData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ const gchar *retval;
+ retval = PROV_CLASS (data->prov)->statement_to_sql (data->prov, data->cnc, data->stmt,
+ data->params, data->flags, data->params_used, error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval);
+ return retval;
+}
+
+static gchar *
+gda_thread_provider_statement_to_sql (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GdaSet *params, GdaStatementSqlFlag flags,
+ GSList **params_used, GError **error)
+{
+ ThreadConnectionData *cdata;
+ StmtToSqlData wdata;
+
+ if (!cnc)
+ return NULL;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return NULL;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.stmt = stmt;
+ wdata.params = params;
+ wdata.flags = flags;
+ wdata.params_used = params_used;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_statement_to_sql, &wdata, NULL, NULL);
+ return (gchar *) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+}
+
+/*
+ * Statement prepare request
+ *
+ * This methods "converts" @stmt into a prepared statement. A prepared statement is a notion
+ * specific in its implementation details to the C API used here. If successfull, it must create
+ * a new #GdaThreadPStmt object and declare it to @cnc.
+ */
+typedef struct {
+ GdaServerProvider *prov;
+ GdaConnection *cnc;
+ GdaStatement *stmt;
+} PrepareStatementData;
+
+static gpointer
+sub_thread_prepare_statement (PrepareStatementData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval;
+ retval = PROV_CLASS (data->prov)->statement_prepare (data->prov,
+ data->cnc,
+ data->stmt,
+ error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval ? 1 : 0);
+}
+
+static gboolean
+gda_thread_provider_statement_prepare (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GError **error)
+{
+ ThreadConnectionData *cdata;
+ PrepareStatementData wdata;
+ gpointer res;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+ g_return_val_if_fail (GDA_IS_STATEMENT (stmt), FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.stmt = stmt;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_prepare_statement, &wdata, NULL, NULL);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, error);
+ return GPOINTER_TO_INT (res) ? TRUE : FALSE;
+}
+
+/*
+ * Execute statement request
+ *
+ * Executes a statement. This method should do the following:
+ * - try to prepare the statement if not yet done
+ * - optionnally reset the prepared statement
+ * - bind the variables's values (which are in @params)
+ * - add a connection event to log the execution
+ * - execute the prepared statement
+ *
+ * If @stmt is an INSERT statement and @last_inserted_row is not NULL then additional actions must be taken to return the
+ * actual inserted row
+ */
+typedef struct {
+ GdaServerProvider *prov;
+ GdaConnection *cnc;
+ GdaStatement *stmt;
+ GdaSet *params;
+ GdaStatementModelUsage model_usage;
+ GType *col_types;
+
+ GdaConnection *real_cnc;
+ GdaThreadWrapper *wrapper;
+
+ GdaSet **last_inserted_row;
+} ExecuteStatementData;
+
+static gpointer
+sub_thread_execute_statement (ExecuteStatementData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ GObject *retval;
+ retval = PROV_CLASS (data->prov)->statement_execute (data->prov,
+ data->cnc,
+ data->stmt,
+ data->params,
+ data->model_usage,
+ data->col_types,
+ data->last_inserted_row,
+ NULL, NULL, NULL,
+ error);
+ g_print ("/%s() => %p\n", __FUNCTION__, retval);
+
+ if (GDA_IS_DATA_MODEL (retval)) {
+ /* substitute the GdaDataSelect with a GdaThreadRecordset */
+ GdaDataModel *model;
+ model = _gda_thread_recordset_new (data->real_cnc, data->wrapper, GDA_DATA_MODEL (retval));
+ g_object_unref (retval);
+ retval = (GObject*) model;
+ }
+
+ return retval;
+}
+static GObject *
+gda_thread_provider_statement_execute (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GdaSet *params,
+ GdaStatementModelUsage model_usage,
+ GType *col_types, GdaSet **last_inserted_row,
+ guint *task_id,
+ GdaServerProviderExecCallback async_cb, gpointer cb_data, GError **error)
+{
+ ThreadConnectionData *cdata;
+ ExecuteStatementData wdata;
+
+ /* FIXME: handle async requests */
+ if (async_cb) {
+ TO_IMPLEMENT;
+ return NULL;
+ }
+
+ GObject *res;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+ g_return_val_if_fail (GDA_IS_STATEMENT (stmt), FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.stmt = stmt;
+ wdata.params = params;
+ wdata.model_usage = model_usage;
+ wdata.col_types = col_types;
+ wdata.last_inserted_row = last_inserted_row;
+
+ wdata.real_cnc = cnc;
+ wdata.wrapper = cdata->wrapper;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_execute_statement, &wdata, NULL, NULL);
+ res = (GObject*) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, error);
+
+ return res;
+}
+
+/*
+ * starts a distributed transaction: put the XA transaction in the ACTIVE state
+ */
+typedef struct {
+ GdaServerProvider *prov;
+ GdaConnection *cnc;
+ const GdaXaTransactionId *xid;
+} XaTransactionData;
+
+static gpointer
+sub_thread_xa_start (XaTransactionData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval = FALSE;
+ retval = PROV_CLASS (data->prov)->xa_funcs->xa_start (data->prov, data->cnc, data->xid,
+ error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval ? 1 : 0);
+}
+
+static gboolean
+gda_thread_provider_xa_start (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error)
+{
+ ThreadConnectionData *cdata;
+ XaTransactionData wdata;
+ gpointer res;
+
+ if (!cnc) {
+ g_set_error (error, 0, 0, _("A connection is required"));
+ return FALSE;
+ }
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+ g_return_val_if_fail (xid, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.xid = xid;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_xa_start, &wdata, NULL, error);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+ return GPOINTER_TO_INT (res) ? TRUE : FALSE;
+}
+
+/*
+ * put the XA transaction in the IDLE state: the connection won't accept any more modifications.
+ * This state is required by some database providers before actually going to the PREPARED state
+ */
+static gpointer
+sub_thread_xa_end (XaTransactionData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval = FALSE;
+ retval = PROV_CLASS (data->prov)->xa_funcs->xa_end (data->prov, data->cnc, data->xid,
+ error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval ? 1 : 0);
+}
+
+static gboolean
+gda_thread_provider_xa_end (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error)
+{
+ ThreadConnectionData *cdata;
+ XaTransactionData wdata;
+ gpointer res;
+
+ if (!cnc) {
+ g_set_error (error, 0, 0, _("A connection is required"));
+ return FALSE;
+ }
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+ g_return_val_if_fail (xid, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.xid = xid;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_xa_end, &wdata, NULL, error);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+ return GPOINTER_TO_INT (res) ? TRUE : FALSE;
+}
+
+/*
+ * prepares the distributed transaction: put the XA transaction in the PREPARED state
+ */
+static gpointer
+sub_thread_xa_prepare (XaTransactionData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval = FALSE;
+ retval = PROV_CLASS (data->prov)->xa_funcs->xa_prepare (data->prov, data->cnc, data->xid,
+ error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval ? 1 : 0);
+}
+
+static gboolean
+gda_thread_provider_xa_prepare (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error)
+{
+ ThreadConnectionData *cdata;
+ XaTransactionData wdata;
+ gpointer res;
+
+ if (!cnc) {
+ g_set_error (error, 0, 0, _("A connection is required"));
+ return FALSE;
+ }
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+ g_return_val_if_fail (xid, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.xid = xid;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_xa_prepare, &wdata, NULL, error);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+ return GPOINTER_TO_INT (res) ? TRUE : FALSE;
+}
+
+/*
+ * commits the distributed transaction: actually write the prepared data to the database and
+ * terminates the XA transaction
+ */
+static gpointer
+sub_thread_xa_commit (XaTransactionData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval = FALSE;
+ retval = PROV_CLASS (data->prov)->xa_funcs->xa_commit (data->prov, data->cnc, data->xid,
+ error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval ? 1 : 0);
+}
+
+static gboolean
+gda_thread_provider_xa_commit (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error)
+{
+ ThreadConnectionData *cdata;
+ XaTransactionData wdata;
+ gpointer res;
+
+ if (!cnc) {
+ g_set_error (error, 0, 0, _("A connection is required"));
+ return FALSE;
+ }
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+ g_return_val_if_fail (xid, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.xid = xid;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_xa_commit, &wdata, NULL, error);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+ return GPOINTER_TO_INT (res) ? TRUE : FALSE;
+}
+
+/*
+ * Rolls back an XA transaction, possible only if in the ACTIVE, IDLE or PREPARED state
+ */
+static gpointer
+sub_thread_xa_rollback (XaTransactionData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval = FALSE;
+ retval = PROV_CLASS (data->prov)->xa_funcs->xa_rollback (data->prov, data->cnc, data->xid,
+ error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval ? 1 : 0);
+}
+
+static gboolean
+gda_thread_provider_xa_rollback (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error)
+{
+ ThreadConnectionData *cdata;
+ XaTransactionData wdata;
+ gpointer res;
+
+ if (!cnc) {
+ g_set_error (error, 0, 0, _("A connection is required"));
+ return FALSE;
+ }
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+ g_return_val_if_fail (xid, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+ wdata.xid = xid;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_xa_rollback, &wdata, NULL, error);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+ return GPOINTER_TO_INT (res) ? TRUE : FALSE;
+}
+
+/*
+ * Lists all XA transactions that are in the PREPARED state
+ *
+ * Returns: a list of GdaXaTransactionId structures, which will be freed by the caller
+ */
+static GList *
+sub_thread_xa_recover (CncProvData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ GList *retval;
+ retval = PROV_CLASS (data->prov)->xa_funcs->xa_recover (data->prov, data->cnc, error);
+ g_print ("/%s() => %p\n", __FUNCTION__, retval);
+ return GINT_TO_POINTER (retval ? 1 : 0);
+}
+
+static GList *
+gda_thread_provider_xa_recover (GdaServerProvider *provider, GdaConnection *cnc,
+ GError **error)
+{
+ ThreadConnectionData *cdata;
+ CncProvData wdata;
+ GList *res;
+
+ if (!cnc) {
+ g_set_error (error, 0, 0, _("A connection is required"));
+ return FALSE;
+ }
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ wdata.prov = cdata->cnc_provider;
+ wdata.cnc = cdata->sub_connection;
+
+ gda_thread_wrapper_execute (cdata->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_xa_recover, &wdata, NULL, error);
+ res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+ return res;
+}
+
+/*
+ * Free connection's specific data
+ */
+static void
+gda_thread_free_cnc_data (ThreadConnectionData *cdata)
+{
+ if (!cdata)
+ return;
+
+ /*disconnect signals handlers */
+ gint i;
+ for (i = 0; i < cdata->handlers_ids->len; i++) {
+ gulong hid = g_array_index (cdata->handlers_ids, gulong, i);
+ gda_thread_wrapper_disconnect (cdata->wrapper, hid);
+ }
+
+ /* unref cdata->sub_connection in sub thread */
+ gda_thread_wrapper_execute_void (cdata->wrapper,
+ (GdaThreadWrapperVoidFunc) g_object_unref,
+ cdata->sub_connection, NULL, NULL);
+ g_object_unref (cdata->wrapper);
+ g_free (cdata);
+}
Added: trunk/libgda/thread-wrapper/gda-thread-provider.h
==============================================================================
--- (empty file)
+++ trunk/libgda/thread-wrapper/gda-thread-provider.h Tue Apr 7 19:10:50 2009
@@ -0,0 +1,57 @@
+/* GDA Thread provider
+ * Copyright (C) 1998 - 2009 The GNOME Foundation.
+ *
+ * AUTHORS:
+ * Rodrigo Moya <rodrigo gnome-db org>
+ * Carlos Perellórí<carlos gnome-db org>
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GDA_THREAD_PROVIDER_H__
+#define __GDA_THREAD_PROVIDER_H__
+
+#include <libgda/gda-server-provider.h>
+
+#define GDA_TYPE_THREAD_PROVIDER (gda_thread_provider_get_type())
+#define GDA_THREAD_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_THREAD_PROVIDER, GdaThreadProvider))
+#define GDA_THREAD_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_THREAD_PROVIDER, GdaThreadProviderClass))
+#define GDA_IS_THREAD_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_THREAD_PROVIDER))
+#define GDA_IS_THREAD_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDA_TYPE_THREAD_PROVIDER))
+
+typedef struct _GdaThreadProvider GdaThreadProvider;
+typedef struct _GdaThreadProviderClass GdaThreadProviderClass;
+
+struct _GdaThreadProvider {
+ GdaServerProvider provider;
+};
+
+struct _GdaThreadProviderClass {
+ GdaServerProviderClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_gda_reserved1) (void);
+ void (*_gda_reserved2) (void);
+};
+
+G_BEGIN_DECLS
+
+GType gda_thread_provider_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
Added: trunk/libgda/thread-wrapper/gda-thread-recordset.c
==============================================================================
--- (empty file)
+++ trunk/libgda/thread-wrapper/gda-thread-recordset.c Tue Apr 7 19:10:50 2009
@@ -0,0 +1,331 @@
+/* GDA Library
+ * Copyright (C) 2009 The GNOME Foundation.
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdarg.h>
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include "gda-thread-wrapper.h"
+#include "gda-thread-recordset.h"
+#include "gda-thread-provider.h"
+#include <libgda/providers-support/gda-data-select-priv.h>
+//#include "gda-thread-blob-op.h"
+
+static void gda_thread_recordset_class_init (GdaThreadRecordsetClass *klass);
+static void gda_thread_recordset_init (GdaThreadRecordset *recset,
+ GdaThreadRecordsetClass *klass);
+static void gda_thread_recordset_dispose (GObject *object);
+
+/* virtual methods */
+static gint gda_thread_recordset_fetch_nb_rows (GdaDataSelect *model);
+static gboolean gda_thread_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
+
+static gboolean gda_thread_recordset_fetch_prev (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
+static gboolean gda_thread_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
+static gboolean gda_thread_recordset_fetch_at (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
+
+struct _GdaThreadRecordsetPrivate {
+ GdaDataModel *sub_model;
+ GdaThreadWrapper *wrapper;
+};
+static GObjectClass *parent_class = NULL;
+
+#define DATA_SELECT_CLASS(model) (GDA_DATA_SELECT_CLASS (G_OBJECT_GET_CLASS (model)))
+#define COPY_PUBLIC_DATA(from,to) \
+ (((GdaDataSelect *) (to))->nb_stored_rows = ((GdaDataSelect *) (from))->nb_stored_rows, \
+ ((GdaDataSelect *) (to))->prep_stmt = ((GdaDataSelect *) (from))->prep_stmt, \
+ ((GdaDataSelect *) (to))->advertized_nrows = ((GdaDataSelect *) (from))->advertized_nrows)
+
+/*
+ * Object init and finalize
+ */
+static void
+gda_thread_recordset_init (GdaThreadRecordset *recset,
+ GdaThreadRecordsetClass *klass)
+{
+ g_return_if_fail (GDA_IS_THREAD_RECORDSET (recset));
+ recset->priv = g_new0 (GdaThreadRecordsetPrivate, 1);
+ recset->priv->sub_model = NULL;
+ recset->priv->wrapper = NULL;
+}
+
+static void
+gda_thread_recordset_class_init (GdaThreadRecordsetClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GdaDataSelectClass *pmodel_class = GDA_DATA_SELECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->dispose = gda_thread_recordset_dispose;
+ pmodel_class->fetch_nb_rows = gda_thread_recordset_fetch_nb_rows;
+ pmodel_class->fetch_random = gda_thread_recordset_fetch_random;
+ pmodel_class->store_all = NULL;
+
+ pmodel_class->fetch_next = gda_thread_recordset_fetch_next;
+ pmodel_class->fetch_prev = gda_thread_recordset_fetch_prev;
+ pmodel_class->fetch_at = gda_thread_recordset_fetch_at;
+}
+
+static void
+gda_thread_recordset_dispose (GObject *object)
+{
+ GdaThreadRecordset *recset = (GdaThreadRecordset *) object;
+
+ g_return_if_fail (GDA_IS_THREAD_RECORDSET (recset));
+
+ if (recset->priv) {
+ if (recset->priv->sub_model) {
+ /* unref recset->priv->sub_model in sub thread */
+ gda_thread_wrapper_execute_void (recset->priv->wrapper,
+ (GdaThreadWrapperFunc) g_object_unref,
+ recset->priv->sub_model, NULL, NULL);
+ recset->priv->sub_model = NULL;
+ g_object_unref (recset->priv->wrapper);
+ recset->priv->wrapper = NULL;
+ }
+ g_free (recset->priv);
+ recset->priv = NULL;
+
+ ((GdaDataSelect*) recset)->prep_stmt = NULL; /* don't unref since we don't hold a reference to it */
+ }
+
+ parent_class->dispose (object);
+}
+
+/*
+ * Public functions
+ */
+
+GType
+_gda_thread_recordset_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+ static const GTypeInfo info = {
+ sizeof (GdaThreadRecordsetClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gda_thread_recordset_class_init,
+ NULL,
+ NULL,
+ sizeof (GdaThreadRecordset),
+ 0,
+ (GInstanceInitFunc) gda_thread_recordset_init
+ };
+ g_static_mutex_lock (®istering);
+ if (type == 0)
+ type = g_type_register_static (GDA_TYPE_DATA_SELECT, "GdaThreadRecordset", &info, 0);
+ g_static_mutex_unlock (®istering);
+ }
+
+ return type;
+}
+
+
+/*
+ * executed in the sub thread (the one which can manipulate @sub_model)
+ */
+GdaDataModel *
+_gda_thread_recordset_new (GdaConnection *cnc, GdaThreadWrapper *wrapper, GdaDataModel *sub_model)
+{
+ GdaThreadRecordset *model;
+
+ model = GDA_THREAD_RECORDSET (g_object_new (GDA_TYPE_THREAD_RECORDSET,
+ "connection", cnc, NULL));
+
+ _gda_data_select_share_private_data (GDA_DATA_SELECT (sub_model),
+ GDA_DATA_SELECT (model));
+ model->priv->wrapper = g_object_ref (wrapper);
+ model->priv->sub_model = g_object_ref (sub_model);
+
+ COPY_PUBLIC_DATA (sub_model, model);
+
+ return GDA_DATA_MODEL (model);
+}
+
+/*
+ * GdaDataSelect virtual methods
+ */
+
+
+/*
+ * fetch nb rows
+ */
+static gpointer
+sub_thread_fetch_nb_rows (GdaDataSelect *model, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gint retval;
+ retval = DATA_SELECT_CLASS (model)->fetch_nb_rows (model);
+ g_print ("/%s() => %d\n", __FUNCTION__, retval);
+ return GINT_TO_POINTER (retval);
+}
+
+static gint
+gda_thread_recordset_fetch_nb_rows (GdaDataSelect *model)
+{
+ GdaThreadRecordset *rs = (GdaThreadRecordset*) model;
+ gint nb;
+ gda_thread_wrapper_execute (rs->priv->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_fetch_nb_rows,
+ rs->priv->sub_model, NULL, NULL);
+ nb = GPOINTER_TO_INT (gda_thread_wrapper_fetch_result (rs->priv->wrapper, TRUE, NULL, NULL));
+ COPY_PUBLIC_DATA (rs->priv->sub_model, rs);
+ return nb;
+}
+
+/*
+ * fetch random
+ */
+typedef struct {
+ GdaDataSelect *select;
+ gint rownum;
+ GdaRow **row;
+} ThreadData;
+
+static gpointer
+sub_thread_fetch_random (ThreadData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval;
+ retval = DATA_SELECT_CLASS (data->select)->fetch_random (data->select, data->row, data->rownum, error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval);
+}
+
+static gboolean
+gda_thread_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
+{
+ GdaThreadRecordset *rs = (GdaThreadRecordset*) model;
+ ThreadData wdata;
+ gboolean retval;
+
+ wdata.select = (GdaDataSelect *) rs->priv->sub_model;
+ wdata.rownum = rownum;
+ wdata.row = prow;
+ gda_thread_wrapper_execute (rs->priv->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_fetch_random,
+ &wdata, NULL, NULL);
+
+ retval = GPOINTER_TO_INT (gda_thread_wrapper_fetch_result (rs->priv->wrapper, TRUE, NULL, error)) ?
+ TRUE : FALSE;
+ COPY_PUBLIC_DATA (rs->priv->sub_model, rs);
+ return retval;
+}
+
+/*
+ * fetch next
+ */
+static gpointer
+sub_thread_fetch_next (ThreadData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval;
+ retval = DATA_SELECT_CLASS (data->select)->fetch_next (data->select, data->row, data->rownum, error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval);
+}
+
+static gboolean
+gda_thread_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
+{
+ GdaThreadRecordset *rs = (GdaThreadRecordset*) model;
+ ThreadData wdata;
+ gboolean retval;
+
+ wdata.select = (GdaDataSelect *) rs->priv->sub_model;
+ wdata.rownum = rownum;
+ wdata.row = prow;
+ gda_thread_wrapper_execute (rs->priv->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_fetch_next,
+ &wdata, NULL, NULL);
+ retval = GPOINTER_TO_INT (gda_thread_wrapper_fetch_result (rs->priv->wrapper, TRUE, NULL, error)) ?
+ TRUE : FALSE;
+ COPY_PUBLIC_DATA (rs->priv->sub_model, rs);
+ return retval;
+}
+
+/*
+ * fetch prev
+ */
+static gpointer
+sub_thread_fetch_prev (ThreadData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval;
+ retval = DATA_SELECT_CLASS (data->select)->fetch_prev (data->select, data->row, data->rownum, error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval);
+}
+
+static gboolean
+gda_thread_recordset_fetch_prev (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
+{
+ GdaThreadRecordset *rs = (GdaThreadRecordset*) model;
+ ThreadData wdata;
+ gboolean retval;
+
+ wdata.select = (GdaDataSelect *) rs->priv->sub_model;
+ wdata.rownum = rownum;
+ wdata.row = prow;
+ gda_thread_wrapper_execute (rs->priv->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_fetch_prev,
+ &wdata, NULL, NULL);
+ retval = GPOINTER_TO_INT (gda_thread_wrapper_fetch_result (rs->priv->wrapper, TRUE, NULL, error)) ?
+ TRUE : FALSE;
+ COPY_PUBLIC_DATA (rs->priv->sub_model, rs);
+ return retval;
+}
+
+/*
+ * fetch at
+ */
+static gpointer
+sub_thread_fetch_at (ThreadData *data, GError **error)
+{
+ /* WARNING: function executed in sub thread! */
+ gboolean retval;
+ retval = DATA_SELECT_CLASS (data->select)->fetch_at (data->select, data->row, data->rownum, error);
+ g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+ return GINT_TO_POINTER (retval);
+}
+
+static gboolean
+gda_thread_recordset_fetch_at (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
+{
+ GdaThreadRecordset *rs = (GdaThreadRecordset*) model;
+ ThreadData wdata;
+ gboolean retval;
+
+ wdata.select = (GdaDataSelect *) rs->priv->sub_model;
+ wdata.rownum = rownum;
+ wdata.row = prow;
+ gda_thread_wrapper_execute (rs->priv->wrapper,
+ (GdaThreadWrapperFunc) sub_thread_fetch_at,
+ &wdata, NULL, NULL);
+ retval = GPOINTER_TO_INT (gda_thread_wrapper_fetch_result (rs->priv->wrapper, TRUE, NULL, error)) ?
+ TRUE : FALSE;
+ COPY_PUBLIC_DATA (rs->priv->sub_model, rs);
+ return retval;
+}
Added: trunk/libgda/thread-wrapper/gda-thread-recordset.h
==============================================================================
--- (empty file)
+++ trunk/libgda/thread-wrapper/gda-thread-recordset.h Tue Apr 7 19:10:50 2009
@@ -0,0 +1,57 @@
+/* GDA Library
+ * Copyright (C) 2009 The GNOME Foundation.
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GDA_THREAD_RECORDSET_H__
+#define __GDA_THREAD_RECORDSET_H__
+
+#include <libgda/gda-connection.h>
+#include <libgda/gda-data-select.h>
+#include <thread-wrapper/gda-thread-wrapper.h>
+
+G_BEGIN_DECLS
+
+#define GDA_TYPE_THREAD_RECORDSET (_gda_thread_recordset_get_type())
+#define GDA_THREAD_RECORDSET(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_THREAD_RECORDSET, GdaThreadRecordset))
+#define GDA_THREAD_RECORDSET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_THREAD_RECORDSET, GdaThreadRecordsetClass))
+#define GDA_IS_THREAD_RECORDSET(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_THREAD_RECORDSET))
+#define GDA_IS_THREAD_RECORDSET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDA_TYPE_THREAD_RECORDSET))
+
+typedef struct _GdaThreadRecordset GdaThreadRecordset;
+typedef struct _GdaThreadRecordsetClass GdaThreadRecordsetClass;
+typedef struct _GdaThreadRecordsetPrivate GdaThreadRecordsetPrivate;
+
+struct _GdaThreadRecordset {
+ GdaDataSelect model;
+ GdaThreadRecordsetPrivate *priv;
+};
+
+struct _GdaThreadRecordsetClass {
+ GdaDataSelectClass parent_class;
+};
+
+GType _gda_thread_recordset_get_type (void) G_GNUC_CONST;
+GdaDataModel *_gda_thread_recordset_new (GdaConnection *cnc, GdaThreadWrapper *wrapper,
+ GdaDataModel *sub_model);
+
+G_END_DECLS
+
+#endif
Added: trunk/libgda/thread-wrapper/gda-thread-wrapper.c
==============================================================================
--- (empty file)
+++ trunk/libgda/thread-wrapper/gda-thread-wrapper.c Tue Apr 7 19:10:50 2009
@@ -0,0 +1,958 @@
+/* GDA library
+ * Copyright (C) 2009 The GNOME Foundation.
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include "gda-thread-wrapper.h"
+#include <libgda/gda-mutex.h>
+#include <gobject/gvaluecollector.h>
+#include <libgda/gda-debug-macros.h>
+#include <libgda/gda-value.h>
+
+typedef struct _ThreadData ThreadData;
+typedef struct _SharedData SharedData;
+typedef struct _Job Job;
+typedef struct _Result Result;
+typedef struct _SignalSpec SignalSpec;
+typedef struct _SignalEmissionData SignalEmissionData;
+
+struct _GdaThreadWrapperPrivate {
+ GdaMutex *mutex;
+ guint next_job_id;
+ GThread *sub_thread;
+ GAsyncQueue *to_sub_thread;
+
+ GHashTable *threads_hash; /* key = a GThread, value = a #ThreadData pointer */
+};
+
+/*
+ * Data shared between the GdaThreadWrapper (and the threads which use it) and its sub thread.
+ * It implements its own locking mechanism.
+ */
+struct _SharedData {
+ gint nb_waiting;
+ Job *current_job;
+ GThread *wrapper_sub_thread;
+
+ gint ref_count;
+ GMutex *mutex;
+};
+
+SharedData *
+shared_data_new (void)
+{
+ SharedData *shd = g_new0 (SharedData, 1);
+ shd->ref_count = 1;
+ shd->nb_waiting = 0;
+ shd->mutex = g_mutex_new ();
+ return shd;
+}
+
+static SharedData *
+shared_data_ref (SharedData *shd)
+{
+ g_mutex_lock (shd->mutex);
+ shd->ref_count++;
+ g_mutex_unlock (shd->mutex);
+ return shd;
+}
+static void
+shared_data_unref (SharedData *shd)
+{
+ g_mutex_lock (shd->mutex);
+ shd->ref_count--;
+ if (shd->ref_count == 0) {
+ g_mutex_unlock (shd->mutex);
+ g_mutex_free (shd->mutex);
+ g_free (shd);
+ }
+ else
+ g_mutex_unlock (shd->mutex);
+}
+
+static void
+shared_data_add_nb_waiting (SharedData *shd, gint to_add)
+{
+ g_mutex_lock (shd->mutex);
+ shd->nb_waiting += to_add;
+ g_mutex_unlock (shd->mutex);
+}
+
+
+/*
+ * Jobs.
+ * Created and destroyed exclusively by the thread(s) using the GdaThreadWrapper object,
+ * except for the job where job->type == JOB_TYPE_DESTROY which is destroyed by the sub thread.
+ *
+ * Passed to the sub job through obj->to_sub_thread
+ */
+typedef enum {
+ JOB_TYPE_EXECUTE,
+ JOB_TYPE_DESTROY
+} JobType;
+struct _Job {
+ JobType type;
+ guint job_id;
+ GdaThreadWrapperFunc func;
+ GdaThreadWrapperVoidFunc void_func;
+ gpointer arg;
+ GDestroyNotify arg_destroy_func;
+ GAsyncQueue *reply_queue; /* holds a ref to it */
+ SharedData *shared; /* holds a ref to it */
+};
+static void
+job_free (Job *job)
+{
+ if (job->arg && job->arg_destroy_func)
+ job->arg_destroy_func (job->arg);
+ if (job->reply_queue)
+ g_async_queue_unref (job->reply_queue);
+ if (job->shared)
+ shared_data_unref (job->shared);
+ g_free (job);
+}
+
+/*
+ * Signal specification, created when using _connect().
+ *
+ * A SignalSpec only exists as long as the correcponding ThreadData exists.
+ */
+struct _SignalSpec {
+ GSignalQuery sigprop; /* must be first */
+ gpointer instance;
+ gulong signal_id;
+
+ GdaThreadWrapperCallback callback;
+ gpointer data;
+
+ ThreadData *td;
+};
+
+struct _SignalEmissionData {
+ guint n_param_values;
+ GValue *param_values; /* array of GValue structures */
+};
+
+static void
+signal_emission_data_free (SignalEmissionData *sd)
+{
+ gint i;
+ for (i = 0; i < sd->n_param_values; i++) {
+ GValue *value = sd->param_values + i;
+ if (G_VALUE_TYPE (value) != GDA_TYPE_NULL)
+ g_value_reset (value);
+ }
+ g_free (sd->param_values);
+ g_free (sd);
+}
+
+/*
+ * Result of a job executed by the sub thread.
+ * Created exclusively by the sub thread, and destroyed by the thread(s) using the GdaThreadWrapper object
+ *
+ * Passed from the sub thread through obj->from_sub_thread
+ */
+typedef enum {
+ RESULT_TYPE_EXECUTE,
+ RESULT_TYPE_SIGNAL
+} ResultType;
+struct _Result {
+ Job *job;
+ ResultType type;
+ union {
+ struct {
+ gpointer result;
+ GError *error;
+ } exe;
+ struct {
+ SignalSpec *spec;
+ SignalEmissionData *data;
+ } signal;
+ } u;
+};
+#define RESULT(x) ((Result*)(x))
+static void
+result_free (Result *res)
+{
+ if (res->job)
+ job_free (res->job);
+ if (res->type == RESULT_TYPE_EXECUTE) {
+ if (res->u.exe.error)
+ g_error_free (res->u.exe.error);
+ }
+ else if (res->type == RESULT_TYPE_SIGNAL)
+ signal_emission_data_free (res->u.signal.data);
+ else
+ g_assert_not_reached ();
+ g_free (res);
+}
+
+/*
+ * Per thread accounting data.
+ * Each new job increases the ref count
+ */
+struct _ThreadData {
+ GThread *owner;
+ GSList *signals_list; /* list of SignalSpec pointers, owns all the structures */
+ GAsyncQueue *from_sub_thread; /* holds a ref to it */
+ GSList *results; /* list of Result pointers */
+ SharedData *shared; /* number of jobs waiting from that thread, holds a ref to it */
+};
+#define THREAD_DATA(x) ((ThreadData*)(x))
+
+static ThreadData *
+get_thread_data (GdaThreadWrapper *wrapper)
+{
+ ThreadData *td;
+
+ td = g_hash_table_lookup (wrapper->priv->threads_hash, g_thread_self());
+ if (!td) {
+ td = g_new0 (ThreadData, 1);
+ td->owner = g_thread_self();
+ td->from_sub_thread = g_async_queue_new ();
+ td->results = NULL;
+ td->shared = shared_data_new ();
+ td->shared->wrapper_sub_thread = wrapper->priv->sub_thread;
+
+ g_hash_table_insert (wrapper->priv->threads_hash, g_thread_self(), td);
+ }
+ return td;
+}
+
+static void
+thread_data_free (ThreadData *td)
+{
+ g_async_queue_unref (td->from_sub_thread);
+ if (td->results) {
+ g_slist_foreach (td->results, (GFunc) result_free, NULL);
+ g_slist_free (td->results);
+ }
+ if (td->signals_list) {
+ GSList *list;
+ for (list = td->signals_list; list; list = list->next) {
+ /* free each SignalSpec */
+ SignalSpec *sigspec = (SignalSpec*) list->data;
+ g_signal_handler_disconnect (sigspec->instance, sigspec->signal_id);
+ g_free (sigspec);
+ }
+ g_slist_free (td->signals_list);
+ }
+ shared_data_unref (td->shared);
+ g_free (td);
+}
+
+static void gda_thread_wrapper_class_init (GdaThreadWrapperClass *klass);
+static void gda_thread_wrapper_init (GdaThreadWrapper *wrapper, GdaThreadWrapperClass *klass);
+static void gda_thread_wrapper_dispose (GObject *object);
+static void gda_thread_wrapper_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gda_thread_wrapper_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+enum {
+ LAST_SIGNAL
+};
+
+static gint gda_thread_wrapper_signals[LAST_SIGNAL] = { };
+
+/* properties */
+enum {
+ PROP_0
+};
+
+static GObjectClass *parent_class = NULL;
+
+/*
+ * GdaThreadWrapper class implementation
+ * @klass:
+ */
+static void
+gda_thread_wrapper_class_init (GdaThreadWrapperClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ /* Properties */
+ object_class->set_property = gda_thread_wrapper_set_property;
+ object_class->get_property = gda_thread_wrapper_get_property;
+
+ object_class->dispose = gda_thread_wrapper_dispose;
+}
+
+/*
+ * Executed in the sub thread:
+ * takes a Job in (from the wrapper->priv->to_sub_thread queue) and creates a new Result which
+ * it pushed to Job->reply_queue
+ */
+static gpointer
+sub_thread_entry_point (GAsyncQueue *to_sub_thread)
+{
+ GAsyncQueue *in;
+
+ in = to_sub_thread;
+
+ /* don't use @priv anymore */
+ while (1) {
+ Job *job;
+
+ job = g_async_queue_pop (in);
+
+ if (job->type == JOB_TYPE_DESTROY) {
+ g_assert (! job->arg_destroy_func);
+ job_free (job);
+ /*g_print ("... exit sub thread for wrapper\n");*/
+ /* exit sub thread */
+ break;
+ }
+ else if (job->type == JOB_TYPE_EXECUTE) {
+ Result *res = g_new0 (Result, 1);
+ res->job = job;
+ res->type = RESULT_TYPE_EXECUTE;
+ job->shared->current_job = job;
+ if (job->func)
+ res->u.exe.result = job->func (job->arg, &(res->u.exe.error));
+ else {
+ res->u.exe.result = NULL;
+ job->void_func (job->arg, &(res->u.exe.error));
+ }
+ job->shared->current_job = NULL;
+ shared_data_add_nb_waiting (job->shared, -1);
+ g_async_queue_push (job->reply_queue, res);
+ }
+ else
+ g_assert_not_reached ();
+ }
+
+ g_async_queue_unref (in);
+
+ return NULL;
+}
+
+static void
+gda_thread_wrapper_init (GdaThreadWrapper *wrapper, GdaThreadWrapperClass *klass)
+{
+ g_return_if_fail (GDA_IS_THREAD_WRAPPER (wrapper));
+
+ wrapper->priv = g_new0 (GdaThreadWrapperPrivate, 1);
+ wrapper->priv->mutex = gda_mutex_new ();
+ wrapper->priv->next_job_id = 1;
+
+ wrapper->priv->threads_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) thread_data_free);
+
+ wrapper->priv->to_sub_thread = g_async_queue_new ();
+ wrapper->priv->sub_thread = g_thread_create ((GThreadFunc) sub_thread_entry_point,
+ g_async_queue_ref (wrapper->priv->to_sub_thread), /* inc. ref for sub thread usage */
+ FALSE, NULL);
+ /*g_print ("... new wrapper %p, sub_thread=%p\n", wrapper, wrapper->priv->sub_thread);*/
+}
+
+static void
+gda_thread_wrapper_dispose (GObject *object)
+{
+ GdaThreadWrapper *wrapper = (GdaThreadWrapper *) object;
+
+ g_return_if_fail (GDA_IS_THREAD_WRAPPER (wrapper));
+
+ if (wrapper->priv) {
+ Job *job = g_new0 (Job, 1);
+ job->type = JOB_TYPE_DESTROY;
+ g_async_queue_push (wrapper->priv->to_sub_thread, job);
+ /*g_print ("... pushed JOB_TYPE_DESTROY for wrapper %p\n", wrapper);*/
+
+ g_async_queue_unref (wrapper->priv->to_sub_thread);
+ wrapper->priv->sub_thread = NULL; /* side note: don't wait for sub thread to terminate */
+
+ if (wrapper->priv->threads_hash)
+ g_hash_table_destroy (wrapper->priv->threads_hash);
+
+ gda_mutex_free (wrapper->priv->mutex);
+
+ g_free (wrapper->priv);
+ wrapper->priv = NULL;
+ }
+
+ /* chain to parent class */
+ parent_class->dispose (object);
+}
+
+/* module error */
+GQuark gda_thread_wrapper_error_quark (void)
+{
+ static GQuark quark;
+ if (!quark)
+ quark = g_quark_from_static_string ("gda_thread_wrapper_error");
+ return quark;
+}
+
+/**
+ * gda_thread_wrapper_get_type
+ *
+ * Registers the #GdaThreadWrapper class on the GLib type system.
+ *
+ * Returns: the GType identifying the class.
+ */
+GType
+gda_thread_wrapper_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+ static const GTypeInfo info = {
+ sizeof (GdaThreadWrapperClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gda_thread_wrapper_class_init,
+ NULL,
+ NULL,
+ sizeof (GdaThreadWrapper),
+ 0,
+ (GInstanceInitFunc) gda_thread_wrapper_init
+ };
+
+ g_static_mutex_lock (®istering);
+ if (type == 0)
+ type = g_type_register_static (G_TYPE_OBJECT, "GdaThreadWrapper", &info, 0);
+ g_static_mutex_unlock (®istering);
+ }
+ return type;
+}
+
+static void
+gda_thread_wrapper_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdaThreadWrapper *wrapper;
+
+ wrapper = GDA_THREAD_WRAPPER (object);
+ if (wrapper->priv) {
+ switch (param_id) {
+ }
+ }
+}
+
+static void
+gda_thread_wrapper_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdaThreadWrapper *wrapper;
+
+ wrapper = GDA_THREAD_WRAPPER (object);
+ if (wrapper->priv) {
+ switch (param_id) {
+ }
+ }
+}
+
+/**
+ * gda_thread_wrapper_new
+ *
+ * Creates a new #GdaThreadWrapper object
+ *
+ * Returns: a new #GdaThreadWrapper object
+ *
+ * Since: 4.2
+ */
+GdaThreadWrapper *
+gda_thread_wrapper_new (void)
+{
+ return (GdaThreadWrapper *) g_object_new (GDA_TYPE_THREAD_WRAPPER, NULL);
+}
+
+/**
+ * gda_thread_wrapper_execute
+ * @wrapper: a #GdaThreadWrapper object
+ * @func: the function to execute
+ * @arg: argument to pass to @func
+ * @arg_destroy_func: function to be called when the execution has finished, to destroy @arg
+ * @error: a place to store errors, for errors occurring in this method, not errors occurring while @func
+ * is executed, or %NULL
+ *
+ * Make @wrapper execute the @func function with the @arg argument (along with a #GError which is not @error)
+ * in the sub thread managed by @wrapper. To execute a function which does not return anything,
+ * use gda_thread_wrapper_execute_void().
+ *
+ * This method returns immediately, and the caller then needs to use gda_thread_wrapper_fetch_result() to
+ * check if the execution has finished and get the result.
+ *
+ * Once @func's execution is finished, if it is not %NULL, the @arg_destroy_func destruction function is called
+ * on @arg. This occurs in the thread calling gda_thread_wrapper_fetch_result().
+ *
+ * Returns: the job ID, or 0 if an error occurred
+ *
+ * Since: 4.2
+ */
+guint
+gda_thread_wrapper_execute (GdaThreadWrapper *wrapper, GdaThreadWrapperFunc func,
+ gpointer arg, GDestroyNotify arg_destroy_func, GError **error)
+{
+ Job *job;
+ guint id;
+ ThreadData *td;
+
+ g_return_val_if_fail (GDA_IS_THREAD_WRAPPER (wrapper), 0);
+ g_return_val_if_fail (wrapper->priv, 0);
+ g_return_val_if_fail (func, 0);
+
+ gda_mutex_lock (wrapper->priv->mutex);
+
+ td = get_thread_data (wrapper);
+ shared_data_add_nb_waiting (td->shared, 1);
+
+ job = g_new0 (Job, 1);
+ job->type = JOB_TYPE_EXECUTE;
+ job->job_id = wrapper->priv->next_job_id++;
+ job->func = func;
+ job->void_func = NULL;
+ job->arg = arg;
+ job->arg_destroy_func = arg_destroy_func;
+ job->reply_queue = g_async_queue_ref (td->from_sub_thread);
+ job->shared = shared_data_ref (td->shared);
+
+ id = job->job_id;
+ gda_mutex_unlock (wrapper->priv->mutex);
+
+ g_async_queue_push (wrapper->priv->to_sub_thread, job);
+
+ return id;
+}
+
+/**
+ * gda_thread_wrapper_execute_void
+ * @wrapper: a #GdaThreadWrapper object
+ * @func: the function to execute
+ * @arg: argument to pass to @func
+ * @arg_destroy_func: function to be called when the execution has finished, to destroy @arg
+ * @error: a place to store errors, for errors occurring in this method, not errors occurring while @func
+ * is executed, or %NULL
+ *
+ * Make @wrapper execute the @func function with the @arg argument (along with a #GError which is not @error)
+ * in the sub thread managed by @wrapper. To execute a function which returns some pointer,
+ * use gda_thread_wrapper_execute().
+ *
+ * This method returns immediately. Calling gda_thread_wrapper_fetch_result() is not necessary as @func
+ * does not return any result. However, it may be necessary to call gda_thread_wrapper_iterate() to give @wrapper a
+ * chance to execute the @arg_destroy_func function if not %NULL (note that gda_thread_wrapper_iterate() is
+ * called by gda_thread_wrapper_fetch_result() itself).
+ *
+ * Once @func's execution is finished, if it is not %NULL, the @arg_destroy_func destruction function is called
+ * on @arg. This occurs in the thread calling gda_thread_wrapper_fetch_result().
+ *
+ * Returns: the job ID, or 0 if an error occurred
+ *
+ * Since: 4.2
+ */
+guint
+gda_thread_wrapper_execute_void (GdaThreadWrapper *wrapper, GdaThreadWrapperVoidFunc func,
+ gpointer arg, GDestroyNotify arg_destroy_func, GError **error)
+{
+ Job *job;
+ guint id;
+ ThreadData *td;
+
+ g_return_val_if_fail (GDA_IS_THREAD_WRAPPER (wrapper), 0);
+ g_return_val_if_fail (wrapper->priv, 0);
+ g_return_val_if_fail (func, 0);
+
+ gda_mutex_lock (wrapper->priv->mutex);
+
+ td = get_thread_data (wrapper);
+ shared_data_add_nb_waiting (td->shared, 1);
+
+ job = g_new0 (Job, 1);
+ job->type = JOB_TYPE_EXECUTE;
+ job->job_id = wrapper->priv->next_job_id++;
+ job->func = NULL;
+ job->void_func = func;
+ job->arg = arg;
+ job->arg_destroy_func = arg_destroy_func;
+ job->reply_queue = g_async_queue_ref (td->from_sub_thread);
+ job->shared = shared_data_ref (td->shared);
+
+ id = job->job_id;
+ gda_mutex_unlock (wrapper->priv->mutex);
+
+ g_async_queue_push (wrapper->priv->to_sub_thread, job);
+
+ return id;
+}
+
+/**
+ * gda_thread_wrapper_iterate
+ * @wrapper: a #GdaThreadWrapper object
+ * @may_block: whether the call may block
+ *
+ * This method gives @wrapper a chance to check if some functions to be executed have finished
+ * <emphasis>in the calling thread</emphasis>. It handles one function's execution result, and
+ * if @may_block is %TRUE, then it will block untill there is one (functions returning void are
+ * ignored).
+ *
+ * Since: 4.2
+ */
+void
+gda_thread_wrapper_iterate (GdaThreadWrapper *wrapper, gboolean may_block)
+{
+ ThreadData *td;
+ Result *res;
+
+ g_return_if_fail (GDA_IS_THREAD_WRAPPER (wrapper));
+ g_return_if_fail (wrapper->priv);
+
+ gda_mutex_lock (wrapper->priv->mutex);
+
+ td = g_hash_table_lookup (wrapper->priv->threads_hash, g_thread_self());
+ if (!td) {
+ /* nothing to be done for this thread */
+ gda_mutex_unlock (wrapper->priv->mutex);
+ return;
+ }
+
+ again:
+ if (may_block)
+ res = g_async_queue_pop (td->from_sub_thread);
+ else
+ res = g_async_queue_try_pop (td->from_sub_thread);
+ if (res) {
+ gboolean do_again = FALSE;
+ if (res->type == RESULT_TYPE_EXECUTE) {
+ if (!res->job->func) {
+ result_free (res); /* ignore as there is no result */
+ do_again = TRUE;
+ }
+ else
+ td->results = g_slist_append (td->results, res);
+ }
+ else if (res->type == RESULT_TYPE_SIGNAL) {
+ /* run callback now */
+ SignalSpec *spec = res->u.signal.spec;
+ SignalEmissionData *sd = res->u.signal.data;
+
+ spec->callback (wrapper, spec->instance, ((GSignalQuery*)spec)->signal_name,
+ sd->n_param_values, sd->param_values, NULL,
+ spec->data);
+ result_free (res);
+ do_again = TRUE;
+ }
+ else
+ g_assert_not_reached ();
+
+ if (do_again)
+ goto again;
+ }
+
+ gda_mutex_unlock (wrapper->priv->mutex);
+}
+
+/**
+ * gda_thread_wrapper_fetch_result
+ * @wrapper: a #GdaThreadWrapper object
+ * @may_lock: TRUE if this funct must lock the caller untill a result is available
+ * @id: a place to store the job ID of the function which execution has finished
+ * @error: a place to store errors, for errors which may have occurred during the execution, or %NULL
+ *
+ * Use this method to check if the execution of a function is finished. The function's execution must have
+ * been requested using gda_thread_wrapper_execute().
+ *
+ * If @id is not %NULL, then it will contain the ID of the request if a function's execution has finished, or
+ * %0 otherwise.
+ *
+ * Returns: the pointer returned by the execution, or %NULL if no result is available
+ *
+ * Since: 4.2
+ */
+gpointer
+gda_thread_wrapper_fetch_result (GdaThreadWrapper *wrapper, gboolean may_lock, guint *out_id, GError **error)
+{
+ ThreadData *td;
+ Result *res = NULL;
+ gpointer retval = NULL;
+
+ g_return_val_if_fail (GDA_IS_THREAD_WRAPPER (wrapper), NULL);
+ g_return_val_if_fail (wrapper->priv, NULL);
+
+ gda_mutex_lock (wrapper->priv->mutex);
+ td = g_hash_table_lookup (wrapper->priv->threads_hash, g_thread_self());
+ if (!td) {
+ /* nothing to be done for this thread */
+ gda_mutex_unlock (wrapper->priv->mutex);
+ return NULL;
+ }
+
+ gda_thread_wrapper_iterate (wrapper, may_lock);
+ if (td->results) {
+ res = RESULT (td->results->data);
+ td->results = g_slist_delete_link (td->results, td->results);
+ if (!(td->results) &&
+ (td->shared->nb_waiting == 0) &&
+ (g_async_queue_length (td->from_sub_thread) == 0) &&
+ !td->signals_list) {
+ /* remove this ThreadData */
+ g_hash_table_remove (wrapper->priv->threads_hash, g_thread_self());
+ }
+ }
+
+ if (res) {
+ g_assert (res->type == RESULT_TYPE_EXECUTE);
+ if (out_id)
+ *out_id = res->job->job_id;
+ if (res->u.exe.error) {
+ g_propagate_error (error, res->u.exe.error);
+ res->u.exe.error = NULL;
+ }
+ retval = res->u.exe.result;
+ res->u.exe.result = NULL;
+ result_free (res);
+ }
+
+ gda_mutex_unlock (wrapper->priv->mutex);
+ return retval;
+}
+
+/**
+ * gda_thread_wrapper_get_waiting_size
+ * @wrapper: a #GdaThreadWrapper object
+ *
+ * Use this method to query the number of functions which have been queued to be executed
+ * but which have not yet been executed.
+ *
+ * Returns: the number of jobs not yet executed
+ *
+ * Since: 4.2
+ */
+gint
+gda_thread_wrapper_get_waiting_size (GdaThreadWrapper *wrapper)
+{
+ gint size;
+ ThreadData *td;
+
+ g_return_val_if_fail (GDA_IS_THREAD_WRAPPER (wrapper), 0);
+ g_return_val_if_fail (wrapper->priv, 0);
+
+ gda_mutex_lock (wrapper->priv->mutex);
+ td = g_hash_table_lookup (wrapper->priv->threads_hash, g_thread_self());
+ if (!td) {
+ /* nothing to be done for this thread */
+ gda_mutex_unlock (wrapper->priv->mutex);
+ return 0;
+ }
+ size = td->shared->nb_waiting;
+ gda_mutex_unlock (wrapper->priv->mutex);
+ return size;
+}
+
+/*
+ * Executed in sub thread (or potentially in other threads, in which case will be ignored)
+ * pushes data into the queue
+ */
+static void
+sub_thread_closure_marshal (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ SignalSpec *sigspec = (SignalSpec *) closure->data;
+
+ /* if the signal is not emitted from the sub thread then don't do anything */
+ if (g_thread_self () != sigspec->td->shared->wrapper_sub_thread)
+ return;
+
+ /* if we are not currently doing a job, then don't do anything */
+ if (!sigspec->td->shared->current_job)
+ return;
+
+ gint i;
+ /*
+ for (i = 1; i < n_param_values; i++) {
+ g_print ("\t%d => %s\n", i, gda_value_stringify (param_values + i));
+ }
+ */
+ SignalEmissionData *sdata;
+ sdata = g_new0 (SignalEmissionData, 1);
+ sdata->n_param_values = n_param_values - 1;
+ sdata->param_values = g_new0 (GValue, sdata->n_param_values);
+ for (i = 1; i < n_param_values; i++) {
+ const GValue *src;
+ GValue *dest;
+
+ src = param_values + i;
+ dest = sdata->param_values + i - 1;
+
+ if (G_VALUE_TYPE (src) != GDA_TYPE_NULL) {
+ g_value_init (dest, G_VALUE_TYPE (src));
+ g_value_copy (src, dest);
+ }
+ }
+
+ Result *res = g_new0 (Result, 1);
+ g_assert (sigspec->td->shared);
+ res->job = NULL;
+ res->type = RESULT_TYPE_SIGNAL;
+ res->u.signal.spec = sigspec;
+ res->u.signal.data = sdata;
+ g_async_queue_push (sigspec->td->shared->current_job->reply_queue, res);
+}
+
+/**
+ * gda_thread_wrapper_connect_raw
+ * @wrapper: a #GdaThreadWrapper object
+ * @instance: the instance to connect to
+ * @sig_name: a string of the form "signal-name::detail"
+ * @callback: a #GdaThreadWrapperCallback function
+ * @data: data to pass to @callback's calls
+ *
+ * Connects a callbeck function to a signal for a particular object. The difference with g_signal_connect() and
+ * similar functions are:
+ * <itemizedlist>
+ * <listitem><para>the @callback argument is not a #GCallback function, so the callback signature is not
+ * dependant on the signal itself</para></listitem>
+ * <listitem><para>the signal handler must not have to return any value</para></listitem>
+ * <listitem><para>the @callback function will be called asynchronously, the caller may need to use
+ * gda_thread_wrapper_iterate() to get the notification</para></listitem>
+ * <listitem><para>the @callback function will be called only if the signal has been emitted by @instance
+ * while being used in @wrapper's private sub thread (ie. used when @wrapper is executing some functions
+ * specified by
+ * gda_thread_wrapper_execute() or gda_thread_wrapper_execute_void() have been used)</para></listitem>
+ * </itemizedlist>
+ *
+ * To disconnect a signal handler, don't use any of the g_signal_handler_*() functions but the
+ * gda_thread_wrapper_disconnect() method.
+ *
+ * Returns: the handler ID
+ *
+ * Since: 4.2
+ */
+gulong
+gda_thread_wrapper_connect_raw (GdaThreadWrapper *wrapper,
+ gpointer instance,
+ const gchar *sig_name,
+ GdaThreadWrapperCallback callback,
+ gpointer data)
+{
+ guint sigid;
+ SignalSpec *sigspec;
+ ThreadData *td;
+
+ g_return_val_if_fail (GDA_IS_THREAD_WRAPPER (wrapper), 0);
+ g_return_val_if_fail (wrapper->priv, 0);
+
+ gda_mutex_lock (wrapper->priv->mutex);
+
+ td = get_thread_data (wrapper);
+
+ sigid = g_signal_lookup (sig_name, /* FIXME: use g_signal_parse_name () */
+ G_TYPE_FROM_INSTANCE (instance));
+ if (sigid == 0) {
+ g_warning (_("Signal does not exist\n"));
+ return 0;
+ }
+
+ sigspec = g_new0 (SignalSpec, 1);
+ g_signal_query (sigid, (GSignalQuery*) sigspec);
+
+ if (((GSignalQuery*) sigspec)->return_type != G_TYPE_NONE) {
+ g_warning (_("Signal to connect to must not have a return value\n"));
+ g_free (sigspec);
+ return 0;
+ }
+ sigspec->instance = instance;
+ sigspec->callback = callback;
+ sigspec->data = data;
+
+ GClosure *cl;
+ cl = g_closure_new_simple (sizeof (GClosure), sigspec);
+ g_closure_set_marshal (cl, (GClosureMarshal) sub_thread_closure_marshal);
+ sigspec->signal_id = g_signal_connect_closure (instance, sig_name, cl, FALSE);
+
+ sigspec->td = td;
+ td->signals_list = g_slist_append (td->signals_list, sigspec);
+
+ gda_mutex_unlock (wrapper->priv->mutex);
+
+ return sigspec->signal_id;
+}
+
+/**
+ * gda_thread_wrapper_disconnect
+ * @wrapper: a #GdaThreadWrapper object
+ * @id: a handler ID, as returned by gda_thread_wrapper_connect_raw()
+ *
+ * Disconnects the emission of a signal, does the opposite of gda_thread_wrapper_connect_raw()
+ *
+ * Since: 4.2
+ */
+void
+gda_thread_wrapper_disconnect (GdaThreadWrapper *wrapper, gulong id)
+{
+ SignalSpec *sigspec = NULL;
+ ThreadData *td;
+ GSList *list;
+
+ g_return_if_fail (GDA_IS_THREAD_WRAPPER (wrapper));
+ g_return_if_fail (wrapper->priv);
+
+ gda_mutex_lock (wrapper->priv->mutex);
+
+ td = g_hash_table_lookup (wrapper->priv->threads_hash, g_thread_self());
+ if (!td) {
+ g_warning (_("Signal does not exist\n"));
+ gda_mutex_unlock (wrapper->priv->mutex);
+ return;
+ }
+
+ for (list = td->signals_list; list; list = list->next) {
+ if (((SignalSpec*) list->data)->signal_id == id) {
+ sigspec = (SignalSpec*) list->data;
+ break;
+ }
+ }
+
+ if (!sigspec) {
+ g_warning (_("Signal does not exist\n"));
+ gda_mutex_unlock (wrapper->priv->mutex);
+ return;
+ }
+
+ td->signals_list = g_slist_remove (td->signals_list, sigspec);
+ g_signal_handler_disconnect (sigspec->instance, sigspec->signal_id);
+ g_free (sigspec);
+
+ if (!(td->results) &&
+ (td->shared->nb_waiting == 0) &&
+ (g_async_queue_length (td->from_sub_thread) == 0) &&
+ !td->signals_list) {
+ /* remove this ThreadData */
+ g_hash_table_remove (wrapper->priv->threads_hash, g_thread_self());
+ }
+
+ gda_mutex_unlock (wrapper->priv->mutex);
+}
Added: trunk/libgda/thread-wrapper/gda-thread-wrapper.h
==============================================================================
--- (empty file)
+++ trunk/libgda/thread-wrapper/gda-thread-wrapper.h Tue Apr 7 19:10:50 2009
@@ -0,0 +1,91 @@
+/* GDA library
+ * Copyright (C) 2009 The GNOME Foundation.
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GDA_THREAD_WRAPPER_H__
+#define __GDA_THREAD_WRAPPER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GDA_TYPE_THREAD_WRAPPER (gda_thread_wrapper_get_type())
+#define GDA_THREAD_WRAPPER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_THREAD_WRAPPER, GdaThreadWrapper))
+#define GDA_THREAD_WRAPPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_THREAD_WRAPPER, GdaThreadWrapperClass))
+#define GDA_IS_THREAD_WRAPPER(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, GDA_TYPE_THREAD_WRAPPER))
+#define GDA_IS_THREAD_WRAPPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GDA_TYPE_THREAD_WRAPPER))
+
+typedef struct _GdaThreadWrapper GdaThreadWrapper;
+typedef struct _GdaThreadWrapperClass GdaThreadWrapperClass;
+typedef struct _GdaThreadWrapperPrivate GdaThreadWrapperPrivate;
+
+/* error reporting */
+extern GQuark gda_thread_wrapper_error_quark (void);
+#define GDA_THREAD_WRAPPER_ERROR gda_thread_wrapper_error_quark ()
+
+typedef enum {
+ GDA_THREAD_WRAPPER_UNKNOWN_ERROR
+} GdaThreadWrapperError;
+
+struct _GdaThreadWrapper {
+ GObject object;
+ GdaThreadWrapperPrivate *priv;
+};
+
+struct _GdaThreadWrapperClass {
+ GObjectClass object_class;
+
+ /* Padding for future expansion */
+ void (*_gda_reserved1) (void);
+ void (*_gda_reserved2) (void);
+ void (*_gda_reserved3) (void);
+ void (*_gda_reserved4) (void);
+};
+
+typedef gpointer (*GdaThreadWrapperFunc) (gpointer arg, GError **error);
+typedef void (*GdaThreadWrapperVoidFunc) (gpointer arg, GError **error);
+typedef void (*GdaThreadWrapperCallback) (GdaThreadWrapper *wrapper, gpointer instance, const gchar *signame,
+ gint n_param_values, const GValue *param_values, gpointer gda_reserved,
+ gpointer data);
+
+GType gda_thread_wrapper_get_type (void) G_GNUC_CONST;
+GdaThreadWrapper *gda_thread_wrapper_new (void);
+
+guint gda_thread_wrapper_execute (GdaThreadWrapper *wrapper, GdaThreadWrapperFunc func,
+ gpointer arg, GDestroyNotify arg_destroy_func, GError **error);
+guint gda_thread_wrapper_execute_void (GdaThreadWrapper *wrapper, GdaThreadWrapperVoidFunc func,
+ gpointer arg, GDestroyNotify arg_destroy_func, GError **error);
+void gda_thread_wrapper_iterate (GdaThreadWrapper *wrapper, gboolean may_block);
+gpointer gda_thread_wrapper_fetch_result (GdaThreadWrapper *wrapper, gboolean may_lock,
+ guint *out_id, GError **error);
+
+gint gda_thread_wrapper_get_waiting_size (GdaThreadWrapper *wrapper);
+
+gulong gda_thread_wrapper_connect_raw (GdaThreadWrapper *wrapper,
+ gpointer instance,
+ const gchar *sig_name,
+ GdaThreadWrapperCallback callback,
+ gpointer data);
+void gda_thread_wrapper_disconnect (GdaThreadWrapper *wrapper, gulong id);
+
+G_END_DECLS
+
+#endif
Added: trunk/libgda/thread-wrapper/thread-wrapper.dia
==============================================================================
Binary file. No diff available.
Modified: trunk/po/POTFILES.in
==============================================================================
--- trunk/po/POTFILES.in (original)
+++ trunk/po/POTFILES.in Tue Apr 7 19:10:50 2009
@@ -63,6 +63,8 @@
libgda/sql-parser/gda-statement-struct-parts.c
libgda/sql-parser/gda-statement-struct-select.c
libgda/sql-parser/gda-statement-struct-update.c
+libgda/thread-wrapper/gda-thread-provider.c
+libgda/thread-wrapper/gda-thread-wrapper.c
libgda-report/DocBook/gda-report-docbook-document.c
libgda-report/engine/gda-report-engine.c
libgda-report/gda-report-document.c
Modified: trunk/providers/postgres/gda-postgres-util.c
==============================================================================
--- trunk/providers/postgres/gda-postgres-util.c (original)
+++ trunk/providers/postgres/gda-postgres-util.c Tue Apr 7 19:10:50 2009
@@ -104,7 +104,11 @@
return error_ev;
}
-/* to be used only while initializing a connection */
+/*
+ * Returns NULL if an error occurred
+ *
+ * WARNING: it does not check that @pconn can safely be used
+ */
PGresult *
_gda_postgres_PQexec_wrap (GdaConnection *cnc, PGconn *pconn, const char *query)
{
Modified: trunk/providers/postgres/gda-postgres.h
==============================================================================
--- trunk/providers/postgres/gda-postgres.h (original)
+++ trunk/providers/postgres/gda-postgres.h Tue Apr 7 19:10:50 2009
@@ -51,6 +51,7 @@
typedef struct {
GdaConnection *cnc;
PGconn *pconn;
+ gboolean pconn_is_busy;
gint ntypes;
GdaPostgresTypeOid *type_data;
GHashTable *h_table;
Modified: trunk/tests/multi-threading/Makefile.am
==============================================================================
--- trunk/tests/multi-threading/Makefile.am (original)
+++ trunk/tests/multi-threading/Makefile.am Tue Apr 7 19:10:50 2009
@@ -6,8 +6,8 @@
-DROOT_DIR=\""$(top_srcdir)"\" \
-DTOP_BUILD_DIR=\""$(top_builddir)"\"
-TESTS = check_mutex check_parser check_cnc_lock
-check_PROGRAMS = check_mutex check_parser check_cnc_lock
+TESTS = check_mutex check_parser check_cnc_lock check_threaded_cnc check_wrapper
+check_PROGRAMS = check_mutex check_parser check_cnc_lock check_threaded_cnc check_wrapper
check_mutex_SOURCES = check_mutex.c
check_mutex_LDADD = \
@@ -24,6 +24,16 @@
$(top_builddir)/libgda/libgda-4.0.la \
$(LIBGDA_LIBS)
+check_threaded_cnc_SOURCES = check_threaded_cnc.c common.c common.h
+check_threaded_cnc_LDADD = \
+ $(top_builddir)/libgda/libgda-4.0.la \
+ $(LIBGDA_LIBS)
+
+check_wrapper_SOURCES = check_wrapper.c dummy-object.c dummy-object.h
+check_wrapper_LDADD = \
+ $(top_builddir)/libgda/libgda-4.0.la \
+ $(LIBGDA_LIBS)
+
EXTRA_DIST = testdb.sql
-DISTCLEANFILES = testdb.db
+DISTCLEANFILES = testdb.db storedb.db
Added: trunk/tests/multi-threading/check_threaded_cnc.c
==============================================================================
--- (empty file)
+++ trunk/tests/multi-threading/check_threaded_cnc.c Tue Apr 7 19:10:50 2009
@@ -0,0 +1,676 @@
+#include <stdio.h>
+#include <string.h>
+#include <libgda/libgda.h>
+#include <sql-parser/gda-sql-parser.h>
+#include <thread-wrapper/gda-thread-recordset.h>
+#include "common.h"
+
+#define DBDIR "."
+
+static void test_cnc_obj (GdaConnection *tcnc);
+static void test_cnc_status (GdaConnection *tcnc, gboolean opened);
+
+static gint test_select (GdaConnection *cnc, GdaConnection *tcnc);
+static gint test_select_cursor (GdaConnection *cnc, GdaConnection *tcnc);
+static gint test_insert (GdaConnection *tcnc);
+static gint test_update (GdaConnection *tcnc);
+static gint test_delete (GdaConnection *tcnc);
+static gint test_meta_store (GdaConnection *cnc);
+static gint test_meta_data (GdaConnection *cnc, GdaConnection *tcnc);
+static gint test_signals (GdaConnection *cnc, GdaConnection *tcnc);
+
+int
+main (int argc, char** argv)
+{
+ GdaConnection *cnc, *tcnc;
+ GError *error = NULL;
+ gint failures = 0;
+ gint ntests = 0;
+ gchar *fname;
+
+ /* set up test environment */
+ g_setenv ("GDA_TOP_BUILD_DIR", TOP_BUILD_DIR, 0);
+ g_setenv ("GDA_TOP_SRC_DIR", ROOT_DIR, 0);
+ gda_init ();
+
+ /* create test DB */
+ fname = g_build_filename (ROOT_DIR, "tests", "multi-threading", "testdb.sql", NULL);
+ if (!create_sqlite_db (DBDIR, "testdb", fname, &error)) {
+ g_print ("Cannot create test database: %s\n", error && error->message ?
+ error->message : "no detail");
+ return 1;
+ }
+ g_free (fname);
+
+
+ /* create a connection in threaded mode */
+ cnc = gda_connection_open_from_string ("SQlite", "DB_DIR=.;DB_NAME=testdb", NULL,
+ GDA_CONNECTION_OPTIONS_NONE, &error);
+ if (!cnc) {
+ g_print ("ERROR opening connection: %s\n",
+ error && error->message ? error->message : "No detail");
+ return 1;
+ }
+ tcnc = gda_connection_open_from_string ("SQlite", "DB_DIR=.;DB_NAME=testdb", NULL,
+ GDA_CONNECTION_OPTIONS_THREAD_SAFE, &error);
+ if (!tcnc) {
+ g_print ("ERROR opening connection in thread safe mode: %s\n",
+ error && error->message ? error->message : "No detail");
+ return 1;
+ }
+
+ test_cnc_obj (tcnc);
+ test_cnc_status (tcnc, TRUE);
+ gda_connection_close (tcnc);
+ test_cnc_status (tcnc, FALSE);
+ gda_connection_open (tcnc, NULL);
+ test_cnc_status (tcnc, TRUE);
+
+ ntests ++;
+ failures += test_select (cnc, tcnc);
+
+ ntests ++;
+ failures += test_select_cursor (cnc, tcnc);
+
+ ntests ++;
+ failures += test_insert (tcnc);
+ failures += test_select (cnc, tcnc);
+ failures += test_select_cursor (cnc, tcnc);
+
+ ntests ++;
+ failures += test_update (tcnc);
+ failures += test_select (cnc, tcnc);
+ failures += test_select_cursor (cnc, tcnc);
+
+ ntests ++;
+ failures += test_delete (tcnc);
+ failures += test_select (cnc, tcnc);
+ failures += test_select_cursor (cnc, tcnc);
+
+ ntests ++;
+ failures += test_meta_data (cnc, tcnc);
+
+ ntests ++;
+ failures += test_meta_store (cnc);
+
+ ntests ++;
+ failures += test_signals (cnc, tcnc);
+
+ /* get rid of connection */
+ g_object_unref (cnc);
+ g_object_unref (tcnc);
+ g_usleep (300000);
+
+ g_print ("TESTS COUNT: %d\n", ntests);
+ g_print ("FAILURES: %d\n", failures);
+
+ return failures != 0 ? 1 : 0;
+}
+
+static void
+test_cnc_obj (GdaConnection *tcnc)
+{
+ gboolean is_wrapper;
+ g_object_get (G_OBJECT (tcnc), "is-wrapper", &is_wrapper, NULL);
+ if (!is_wrapper) {
+ g_print ("ERROR: connection is not a connection wrapper\n");
+ exit (1);
+ }
+}
+
+static void
+test_cnc_status (GdaConnection *tcnc, gboolean opened)
+{
+ if (gda_connection_is_opened (tcnc) != opened) {
+ g_print ("ERROR: connection is %s, expecting it %s\n",
+ gda_connection_is_opened (tcnc) ? "Opened" : "Closed",
+ opened ? "Opened" : "Closed");
+ exit (1);
+ }
+}
+
+static gint
+test_select (GdaConnection *cnc, GdaConnection *tcnc)
+{
+ /* execute statement */
+ gint failures = 0;
+ GdaDataModel *model, *tmodel;
+
+ model = run_sql_select (cnc, "SELECT * FROM person");
+ tmodel = run_sql_select (tcnc, "SELECT * FROM person");
+ if (! data_models_equal (model, tmodel))
+ failures ++;
+
+ if (strcmp (G_OBJECT_TYPE_NAME (tmodel), "GdaThreadRecordset")) {
+ g_print ("ERROR: data model from SELECT's type is '%s' and not 'GdaThreadRecordset'\n",
+ G_OBJECT_TYPE_NAME (tmodel));
+ exit (1);
+ }
+ if (gda_data_select_get_connection (GDA_DATA_SELECT (tmodel)) != tcnc) {
+ g_print ("ERROR: %s() returned wrong result\n", "gda_data_select_get_connection");
+ failures ++;
+ }
+
+ GdaDataModelIter *iter;
+ GdaDataModelIter *titer;
+ iter = gda_data_model_create_iter (model);
+ titer = gda_data_model_create_iter (tmodel);
+ if (!titer) {
+ g_print ("ERROR: %s() returned NULL\n", "gda_data_model_create_iter");
+ failures ++;
+ }
+ else {
+ GdaDataModel *m1, *m2;
+ g_object_get (G_OBJECT (titer), "data-model", &m1, "forced-model", &m2, NULL);
+ if (m1 != tmodel) {
+ g_print ("ERROR: \"data-model\" property of created iterator is wrong\n");
+ failures ++;
+ }
+ if (m2 != tmodel) {
+ g_print ("ERROR: \"forced-model\" property of created iterator is wrong\n");
+ failures ++;
+ }
+ g_object_unref (m1);
+ g_object_unref (m2);
+
+ /* check iter's contents */
+ gint ncols;
+ ncols = g_slist_length (GDA_SET (titer)->holders);
+ if (ncols != g_slist_length (GDA_SET (iter)->holders)) {
+ g_print ("ERROR: threaded iterator is at the wrong number of columns: "
+ "%d when it should be at %d\n",
+ ncols, g_slist_length (GDA_SET (iter)->holders));
+ failures ++;
+ ncols = -1;
+ }
+ else
+ g_assert (ncols > 0);
+
+ /* forward */
+ if (gda_data_model_iter_is_valid (titer)) {
+ g_print ("ERROR: threaded iterator is valid before any read\n");
+ failures ++;
+ }
+ if (gda_data_model_iter_is_valid (iter)) {
+ g_print ("ERROR: iterator is valid before any read\n");
+ failures ++;
+ }
+ gint row;
+ gboolean iterok;
+ iterok = gda_data_model_iter_move_next (iter);
+ iterok = gda_data_model_iter_move_next (titer) | iterok;
+ for (row = 0; iterok; row++) {
+ if (gda_data_model_iter_get_row (titer) != gda_data_model_iter_get_row (iter)) {
+ g_print ("ERROR: threaded iterator is at the wrong row: %d when it should be at %d\n",
+ gda_data_model_iter_get_row (titer), gda_data_model_iter_get_row (iter));
+ failures ++;
+ break;
+ }
+ if (ncols > 0) {
+ gint i;
+ for (i = 0; i < ncols; i++) {
+ const GValue *cv, *tcv;
+ cv = gda_data_model_iter_get_value_at (iter, i);
+ tcv = gda_data_model_iter_get_value_at (titer, i);
+ if (gda_value_differ (cv, tcv)) {
+ g_print ("ERROR: values in iterators differ at line %d, colunm %d\n",
+ row, i);
+ failures ++;
+ }
+ //g_print ("%d,%d: %s\n", row, i, gda_value_stringify (tcv));
+ }
+ }
+ gboolean v, tv;
+ v = gda_data_model_iter_move_next (iter);
+ tv = gda_data_model_iter_move_next (titer);
+ if (!v || !tv)
+ break;
+ }
+ if (gda_data_model_iter_is_valid (titer)) {
+ g_print ("ERROR: threaded iterator is still valid\n");
+ failures ++;
+ }
+ if (gda_data_model_iter_is_valid (iter)) {
+ g_print ("ERROR: iterator is still valid\n");
+ failures ++;
+ }
+
+ /* backward */
+ for (; row >= 0; row--) {
+ iterok = gda_data_model_iter_move_next (iter);
+ iterok = gda_data_model_iter_move_next (titer) | iterok;
+ if (!iterok)
+ break;
+ }
+ for (row = 0; iterok; row++) {
+ if (gda_data_model_iter_get_row (titer) != gda_data_model_iter_get_row (iter)) {
+ g_print ("ERROR: threaded iterator is at the wrong row: %d when it should be at %d\n",
+ gda_data_model_iter_get_row (titer), gda_data_model_iter_get_row (iter));
+ failures ++;
+ break;
+ }
+ if (ncols > 0) {
+ gint i;
+ for (i = 0; i < ncols; i++) {
+ const GValue *cv, *tcv;
+ cv = gda_data_model_iter_get_value_at (iter, i);
+ tcv = gda_data_model_iter_get_value_at (titer, i);
+ if (gda_value_differ (cv, tcv)) {
+ g_print ("ERROR: values in iterators differ at line %d, colunm %d\n",
+ row, i);
+ failures ++;
+ }
+ //g_print ("%d,%d: %s\n", row, i, gda_value_stringify (tcv));
+ }
+ }
+ gboolean v, tv;
+ v = gda_data_model_iter_move_prev (iter);
+ tv = gda_data_model_iter_move_prev (titer);
+ if (!v || !tv)
+ break;
+ }
+ if (gda_data_model_iter_is_valid (titer)) {
+ g_print ("ERROR: threaded iterator is still valid\n");
+ failures ++;
+ }
+ if (gda_data_model_iter_is_valid (iter)) {
+ g_print ("ERROR: iterator is still valid\n");
+ failures ++;
+ }
+
+ g_object_unref (iter);
+ g_object_unref (titer);
+ }
+
+ g_object_unref (model);
+ g_object_unref (tmodel);
+
+ return failures;
+}
+
+static gint
+test_select_cursor (GdaConnection *cnc, GdaConnection *tcnc)
+{
+ /* execute statement */
+ gint failures = 0;
+ GdaDataModel *model, *tmodel;
+
+ model = run_sql_select_cursor (cnc, "SELECT * FROM person");
+ tmodel = run_sql_select_cursor (tcnc, "SELECT * FROM person");
+
+ if (gda_data_model_get_access_flags (model) !=
+ gda_data_model_get_access_flags (tmodel)) {
+ g_print ("ERROR: data models' access flags differ: %d and %d for threaded model\n",
+ gda_data_model_get_access_flags (model),
+ gda_data_model_get_access_flags (tmodel));
+ failures ++;
+ return failures;
+ }
+
+ if (strcmp (G_OBJECT_TYPE_NAME (tmodel), "GdaThreadRecordset")) {
+ g_print ("ERROR: data model from SELECT's type is '%s' and not 'GdaThreadRecordset'\n",
+ G_OBJECT_TYPE_NAME (tmodel));
+ exit (1);
+ }
+ if (gda_data_select_get_connection (GDA_DATA_SELECT (tmodel)) != tcnc) {
+ g_print ("ERROR: %s() returned wrong result\n", "gda_data_select_get_connection");
+ failures ++;
+ }
+
+ GdaDataModelIter *iter, *titer;
+ iter = gda_data_model_create_iter (model);
+ titer = gda_data_model_create_iter (tmodel);
+ if (!titer) {
+ g_print ("ERROR: %s() returned NULL\n", "gda_data_model_create_iter");
+ failures ++;
+ }
+ else {
+ /* iter's properties */
+ GdaDataModel *m1, *m2;
+ g_object_get (G_OBJECT (titer), "data-model", &m1, "forced-model", &m2, NULL);
+ if (m1 != tmodel) {
+ g_print ("ERROR: \"data-model\" property of created iterator is wrong\n");
+ failures ++;
+ }
+ if (m2 != tmodel) {
+ g_print ("ERROR: \"forced-model\" property of created iterator is wrong\n");
+ failures ++;
+ }
+ g_object_unref (m1);
+ g_object_unref (m2);
+
+ /* check iter's contents */
+ gint ncols;
+ ncols = g_slist_length (GDA_SET (titer)->holders);
+ if (ncols != g_slist_length (GDA_SET (iter)->holders)) {
+ g_print ("ERROR: threaded iterator is at the wrong number of columns: "
+ "%d when it should be at %d\n",
+ ncols, g_slist_length (GDA_SET (iter)->holders));
+ failures ++;
+ ncols = -1;
+ }
+ else
+ g_assert (ncols > 0);
+
+
+ if (gda_data_model_iter_is_valid (titer)) {
+ g_print ("ERROR: threaded iterator is valid before any read\n");
+ failures ++;
+ }
+ if (gda_data_model_iter_is_valid (iter)) {
+ g_print ("ERROR: iterator is valid before any read\n");
+ failures ++;
+ }
+ gint row;
+ gda_data_model_iter_move_next (iter);
+ gda_data_model_iter_move_next (titer);
+ for (row = 0; ; row++) {
+ if (gda_data_model_iter_get_row (titer) != gda_data_model_iter_get_row (iter)) {
+ g_print ("ERROR: threaded iterator is at the wrong row: %d when it should be at %d\n",
+ gda_data_model_iter_get_row (titer), gda_data_model_iter_get_row (iter));
+ failures ++;
+ break;
+ }
+ if (ncols > 0) {
+ gint i;
+ for (i = 0; i < ncols; i++) {
+ const GValue *cv, *tcv;
+ cv = gda_data_model_iter_get_value_at (iter, i);
+ tcv = gda_data_model_iter_get_value_at (titer, i);
+ if (gda_value_differ (cv, tcv)) {
+ g_print ("ERROR: values in iterators differ at line %d, colunm %d\n",
+ row, i);
+ failures ++;
+ }
+ //g_print ("%d,%d: %s\n", row, i, gda_value_stringify (tcv));
+ }
+ }
+ gboolean v, tv;
+ v = gda_data_model_iter_move_next (iter);
+ tv = gda_data_model_iter_move_next (titer);
+ if (!v || !tv)
+ break;
+ }
+ if (gda_data_model_iter_is_valid (titer)) {
+ g_print ("ERROR: threaded iterator is still valid\n");
+ failures ++;
+ }
+ if (gda_data_model_iter_is_valid (iter)) {
+ g_print ("ERROR: iterator is still valid\n");
+ failures ++;
+ }
+
+ g_object_unref (iter);
+ g_object_unref (titer);
+ }
+
+ g_object_unref (model);
+ g_object_unref (tmodel);
+
+ return failures;
+}
+
+static gint
+test_insert (GdaConnection *tcnc)
+{
+ gint failures = 0;
+ if (!run_sql_non_select (tcnc, "INSERT INTO person (name, age) VALUES ('Alice', 12)")) {
+ g_print ("ERROR: can't INSERT into threaded connection\n");
+ failures ++;
+ }
+ return failures;
+}
+
+static gint
+test_update (GdaConnection *tcnc)
+{
+ gint failures = 0;
+ if (!run_sql_non_select (tcnc, "UPDATE person set name='Alf' where name='Grat'")) {
+ g_print ("ERROR: can't UPDATE threaded connection\n");
+ failures ++;
+ }
+ return failures;
+}
+
+static gint
+test_delete (GdaConnection *tcnc)
+{
+ gint failures = 0;
+ if (!run_sql_non_select (tcnc, "DELETE FROM person WHERE name='Alf'")) {
+ g_print ("ERROR: can't DELETE from threaded connection\n");
+ failures ++;
+ }
+ return failures;
+}
+
+static gint
+test_meta_store (GdaConnection *cnc)
+{
+ GdaMetaStore *store;
+ GdaConnection *tcnc;
+ GError *error = NULL;
+
+ g_print ("=== Starting test where threaded connection is used internally by a meta store\n");
+ tcnc = gda_connection_open_from_string ("SQlite", "DB_DIR=.;DB_NAME=storedb", NULL,
+ GDA_CONNECTION_OPTIONS_THREAD_SAFE, &error);
+ if (!tcnc) {
+ g_print ("ERROR opening connection in thread safe mode: %s\n",
+ error && error->message ? error->message : "No detail");
+ return 1;
+ }
+
+ store = GDA_META_STORE (g_object_new (GDA_TYPE_META_STORE, "cnc", tcnc, NULL));
+ g_object_unref (tcnc);
+ g_object_set (G_OBJECT (cnc), "meta-store", store, NULL);
+ g_object_unref (store);
+
+ if (!gda_connection_update_meta_store (cnc, NULL, &error)) {
+ g_print ("ERROR in gda_connection_update_meta_store(): %s\n",
+ error && error->message ? error->message : "No detail");
+ return 1;
+ }
+
+ GdaDataModel *model;
+ model = gda_meta_store_extract (store, "SELECT * FROM _tables", NULL);
+ if (gda_data_model_get_n_rows (model) != 1) {
+ g_print ("ERROR in gda_connection_update_meta_store(): the _tables table "
+ "should have exactly 1 row\n");
+ g_object_unref (model);
+ return 1;
+ }
+ g_object_unref (model);
+
+ return 0;
+}
+
+static gint
+compare_meta_data (GdaMetaStore *store1, GdaMetaStruct *mstruct1,
+ GdaMetaStore *store2, GdaMetaStruct *mstruct2)
+{
+ GSList *all_dbo_list1, *all_dbo_list2, *list;
+
+ all_dbo_list1 = gda_meta_struct_get_all_db_objects (mstruct1);
+ all_dbo_list2 = gda_meta_struct_get_all_db_objects (mstruct2);
+
+ for (list = all_dbo_list1; list; list = list->next) {
+ GdaMetaDbObject *dbo1, *dbo2;
+ GValue *v1, *v2, *v3;
+ dbo1 = GDA_META_DB_OBJECT (list->data);
+
+ g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), dbo1->obj_catalog);
+ g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), dbo1->obj_schema);
+ g_value_set_string ((v3 = gda_value_new (G_TYPE_STRING)), dbo1->obj_name);
+ dbo2 = gda_meta_struct_get_db_object (mstruct2, v1, v2, v3);
+ gda_value_free (v1);
+ gda_value_free (v2);
+ gda_value_free (v3);
+ if (!dbo2) {
+ g_print ("Error: Could not find object %s.%s.%s\n",
+ dbo1->obj_catalog,
+ dbo1->obj_schema,
+ dbo1->obj_name);
+ return 1;
+ }
+
+ if (dbo1->obj_type != GDA_META_DB_TABLE)
+ continue;
+
+ g_print ("Checking meta store's table: %s\n", dbo1->obj_name);
+
+ GdaDataModel *m1, *m2;
+ gchar *str;
+ str = g_strdup_printf ("SELECT * FROM %s", dbo1->obj_name);
+ m1 = gda_meta_store_extract (store1, str, NULL);
+ m2 = gda_meta_store_extract (store2, str, NULL);
+ g_free (str);
+
+ GdaDataComparator *cmp;
+ GError *error = NULL;
+ cmp = (GdaDataComparator*) gda_data_comparator_new (m1, m2);
+ g_object_unref (m1);
+ g_object_unref (m2);
+ if (! gda_data_comparator_compute_diff (cmp, &error)) {
+ g_print ("Can't compute data model differences for %s.%s.%s: %s\n",
+ dbo1->obj_catalog,
+ dbo1->obj_schema,
+ dbo1->obj_name,
+ error && error->message ? error->message : "No detail");
+ g_object_unref (cmp);
+ return 1;
+ }
+ if (gda_data_comparator_get_n_diffs (cmp) != 0) {
+ g_print ("There are some data model differences!\n");
+ g_object_unref (cmp);
+ return 1;
+ }
+ g_object_unref (cmp);
+ }
+
+ g_slist_free (all_dbo_list1);
+ g_slist_free (all_dbo_list2);
+
+ return 0;
+}
+
+static gint
+test_meta_data (GdaConnection *cnc, GdaConnection *tcnc)
+{
+ GError *error = NULL;
+
+ g_print ("=== Starting test where updating the threaded connection's meta data\n");
+ if (!gda_connection_update_meta_store (tcnc, NULL, &error)) {
+ g_print ("ERROR in gda_connection_update_meta_store() applied to threaded connection: %s\n",
+ error && error->message ? error->message : "No detail");
+ return 1;
+ }
+ if (!gda_connection_update_meta_store (cnc, NULL, &error)) {
+ g_print ("ERROR in gda_connection_update_meta_store() applied to non threaded connection: %s\n",
+ error && error->message ? error->message : "No detail");
+ return 1;
+ }
+
+ GdaMetaStruct *mstruct, *tmstruct;
+ mstruct = gda_meta_store_schema_get_structure (gda_connection_get_meta_store (cnc), NULL);
+ tmstruct = gda_meta_store_schema_get_structure (gda_connection_get_meta_store (tcnc), NULL);
+ g_assert (mstruct);
+ g_assert (tmstruct);
+
+ return compare_meta_data (gda_connection_get_meta_store (cnc), mstruct,
+ gda_connection_get_meta_store (tcnc), tmstruct);
+}
+
+
+static void test_sig_error_cb (GdaConnection *cnc, GdaConnectionEvent *error, GdaConnectionEvent **ev);
+static void test_sig_bool_cb (GdaConnection *cnc, gboolean *out_called);
+static gint
+test_signals (GdaConnection *cnc, GdaConnection *tcnc)
+{
+ gint failures = 0;
+
+ /* test the "error" signal */
+ GdaConnectionEvent *ev = NULL, *tev = NULL;
+ g_signal_connect (G_OBJECT (cnc), "error",
+ G_CALLBACK (test_sig_error_cb), &ev);
+ g_signal_connect (G_OBJECT (tcnc), "error",
+ G_CALLBACK (test_sig_error_cb), &tev);
+ run_sql_non_select (cnc, "DELETE FROM person WHERE name = ##name::string");
+ run_sql_non_select (tcnc, "DELETE FROM person WHERE name = ##name::string");
+
+ g_assert (ev);
+ if (!tev) {
+ g_print ("ERROR: the threaded connection does not emit the \"error\" signal\n");
+ return 1;
+ }
+
+ /* test the "conn_to_close" and "conn-closed" signal */
+ gboolean called = FALSE, called2 = FALSE;
+ g_signal_connect (G_OBJECT (tcnc), "conn-to-close",
+ G_CALLBACK (test_sig_bool_cb), &called);
+ g_signal_connect (G_OBJECT (tcnc), "conn-closed",
+ G_CALLBACK (test_sig_bool_cb), &called2);
+ gda_connection_close (tcnc);
+ if (!called) {
+ g_print ("ERROR: the threaded connection does not emit the \"conn-to-close\" signal\n");
+ return 1;
+ }
+ if (!called2) {
+ g_print ("ERROR: the threaded connection does not emit the \"conn-closed\" signal\n");
+ return 1;
+ }
+ g_signal_handlers_disconnect_by_func (G_OBJECT (tcnc),
+ G_CALLBACK (test_sig_bool_cb), &called);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (tcnc),
+ G_CALLBACK (test_sig_bool_cb), &called2);
+
+ /* test the "conn-opened" signal */
+ called = FALSE;
+ g_signal_connect (G_OBJECT (tcnc), "conn-opened",
+ G_CALLBACK (test_sig_bool_cb), &called);
+ gda_connection_open (tcnc, NULL);
+ g_assert (gda_connection_is_opened (tcnc));
+ if (!called) {
+ g_print ("ERROR: the threaded connection does not emit the \"conn-opened\" signal\n");
+ return 1;
+ }
+ g_signal_handlers_disconnect_by_func (G_OBJECT (tcnc),
+ G_CALLBACK (test_sig_bool_cb), &called);
+
+ /* test the "transaction-status-changed" signal */
+ called = FALSE;
+ g_signal_connect (G_OBJECT (tcnc), "transaction-status-changed",
+ G_CALLBACK (test_sig_bool_cb), &called);
+ g_assert (gda_connection_begin_transaction (tcnc, NULL, GDA_TRANSACTION_ISOLATION_UNKNOWN, NULL));
+ if (!called) {
+ g_print ("ERROR: the threaded connection does not emit the \"transaction-status-changed\" signal\n");
+ return 1;
+ }
+ g_signal_handlers_disconnect_by_func (G_OBJECT (tcnc),
+ G_CALLBACK (test_sig_bool_cb), &called);
+
+ /* test the "dsn_changed" signal */
+ called = FALSE;
+ gda_connection_close (tcnc);
+ g_signal_connect (G_OBJECT (tcnc), "dsn-changed",
+ G_CALLBACK (test_sig_bool_cb), &called);
+ g_object_set (G_OBJECT (tcnc), "dsn", "SalesTest", NULL);
+ if (!called) {
+ g_print ("ERROR: the threaded connection does not emit the \"dsn-changed\" signal\n");
+ return 1;
+ }
+ g_signal_handlers_disconnect_by_func (G_OBJECT (tcnc),
+ G_CALLBACK (test_sig_bool_cb), &called);
+
+ return failures;
+}
+
+static void
+test_sig_error_cb (GdaConnection *cnc, GdaConnectionEvent *error, GdaConnectionEvent **ev)
+{
+ *ev = error;
+}
+
+static void
+test_sig_bool_cb (GdaConnection *cnc, gboolean *out_called)
+{
+ *out_called = TRUE;
+}
Added: trunk/tests/multi-threading/check_wrapper.c
==============================================================================
--- (empty file)
+++ trunk/tests/multi-threading/check_wrapper.c Tue Apr 7 19:10:50 2009
@@ -0,0 +1,650 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <libgda/libgda.h>
+#include <thread-wrapper/gda-thread-wrapper.h>
+#include "dummy-object.h"
+#include <stdarg.h>
+
+#define NTHREADS 10
+#define NCALLS 10
+#define PRINT_CALLS
+#undef PRINT_CALLS
+
+static gint test1 (void);
+static gint test2 (void);
+
+GdaThreadWrapper *wrapper; /* global object */
+
+int
+main (int argc, char** argv)
+{
+ gint failures = 0;
+
+ gda_init ();
+
+ failures += test1 ();
+ failures += test2 ();
+
+ g_print ("Threads COUNT: %d, CALLS per thread:%d\n", NTHREADS, NCALLS*2 + 1);
+ g_print ("FAILURES: %d\n", failures);
+
+ return failures != 0 ? 1 : 0;
+}
+
+
+
+/*
+ * This test creates a single GdaThreadWrapper and runs NTHREADS threads which use the
+ * same GdaThreadWrapper object (each thread executes 2*NCALLS function calls plus a random
+ * number of function calls which return void).
+ *
+ * No signal is handled in this test
+ */
+static gpointer main_thread_func (gpointer int_id);
+static gpointer exec_in_wrapped_thread (gpointer int_in, GError **error);
+static gchar *exec_in_wrapped_thread2 (gchar *str, GError **error);
+static void exec_in_wrapped_thread_v (gchar *str, GError **error);
+static gint
+test1 (void)
+{
+ gint failures = 0;
+ gint i;
+ GSList *threads = NULL;
+
+ wrapper = gda_thread_wrapper_new ();
+
+ /* use GdaThreadWrapper from several threads */
+ for (i = 0; i < NTHREADS; i++) {
+ GThread *th;
+ th = g_thread_create (main_thread_func, GINT_TO_POINTER (i), TRUE, NULL);
+ if (!th) {
+ g_print ("Can't create thread %d (not part of the test)\n", i);
+ exit (1);
+ }
+ threads = g_slist_prepend (threads, th);
+ }
+
+ /* join the threads */
+ GSList *list;
+ for (list = threads; list; list = list->next)
+ failures += GPOINTER_TO_INT (g_thread_join ((GThread*) list->data));
+ g_slist_free (threads);
+
+ g_object_unref (wrapper);
+ return failures;
+}
+
+static gpointer
+exec_in_wrapped_thread (gpointer int_in, GError **error)
+{
+ g_print ("function_1 (%d)\n", GPOINTER_TO_INT (int_in));
+ return int_in;
+}
+
+static gchar *
+exec_in_wrapped_thread2 (gchar *str, GError **error)
+{
+ g_print ("function_2 (%s)\n", str);
+ return g_strdup (str);
+}
+
+static void
+exec_in_wrapped_thread_v (gchar *str, GError **error)
+{
+ g_print ("function_VOID (%s)\n", str);
+}
+
+static void
+free_arg (gchar *str)
+{
+ g_print ("\tFreeing: %s\n", str);
+ g_free (str);
+}
+
+static gpointer
+main_thread_func (gpointer int_id)
+{
+ gint i;
+ guint *ids;
+ gchar *estr;
+
+ gint total_jobs = 0;
+ gint nfailed = 0;
+
+ g_print ("NEW thread %p <=> %d\n", g_thread_self(), GPOINTER_TO_INT (int_id));
+
+ ids = g_new0 (guint, 2*NCALLS);
+ for (i = 0; i < NCALLS; i++) {
+ guint id;
+ GError *error = NULL;
+
+ /* func1 */
+ id = gda_thread_wrapper_execute (wrapper, (GdaThreadWrapperFunc) exec_in_wrapped_thread,
+ GINT_TO_POINTER (i + GPOINTER_TO_INT (int_id) * 1000), NULL, &error);
+ if (id == 0) {
+ g_print ("Error in %s() for thread %d: %s\n", __FUNCTION__, GPOINTER_TO_INT (int_id),
+ error && error->message ? error->message : "No detail");
+ if (error)
+ g_error_free (error);
+ return GINT_TO_POINTER (1);
+ }
+ ids[2*i] = id;
+#ifdef PRINT_CALLS
+ g_print ("--> Thread %d jobID %u arg %d\n", GPOINTER_TO_INT (int_id), id, i + GPOINTER_TO_INT (int_id) * 1000);
+#endif
+ if (rand () >= RAND_MAX / 2.)
+ g_thread_yield ();
+
+
+ if (rand () >= RAND_MAX / 2.) {
+ id = gda_thread_wrapper_execute_void (wrapper, (GdaThreadWrapperVoidFunc) exec_in_wrapped_thread_v,
+ g_strdup_printf ("perturbator %d.%d", GPOINTER_TO_INT (int_id), i),
+ (GDestroyNotify) free_arg, &error);
+ if (id == 0) {
+ g_print ("Error in %s() for thread %d: %s\n", __FUNCTION__, GPOINTER_TO_INT (int_id),
+ error && error->message ? error->message : "No detail");
+ if (error)
+ g_error_free (error);
+ return GINT_TO_POINTER (1);
+ }
+ total_jobs ++;
+ }
+ if (rand () >= RAND_MAX / 2.)
+ g_thread_yield ();
+
+ /* func2 */
+ id = gda_thread_wrapper_execute (wrapper, (GdaThreadWrapperFunc) exec_in_wrapped_thread2,
+ g_strdup_printf ("Hello %d.%d", GPOINTER_TO_INT (int_id), i),
+ (GDestroyNotify) free_arg, &error);
+ if (id == 0) {
+ g_print ("Error in %s() for thread %d: %s\n", __FUNCTION__, GPOINTER_TO_INT (int_id),
+ error && error->message ? error->message : "No detail");
+ if (error)
+ g_error_free (error);
+ return GINT_TO_POINTER (1);
+ }
+ ids[2*i+1] = id;
+ estr = g_strdup_printf ("Hello %d.%d", GPOINTER_TO_INT (int_id), i);
+#ifdef PRINT_CALLS
+ g_print ("--> Thread %d jobID %u arg %s\n", GPOINTER_TO_INT (int_id), id, estr);
+#endif
+ g_free (estr);
+ if (rand () >= RAND_MAX / 2.)
+ g_thread_yield ();
+ }
+ total_jobs += 2*NCALLS;
+ g_thread_yield ();
+
+ /* pick up results */
+ for (i = 0; i < NCALLS; i++) {
+ guint id;
+ gpointer res;
+ res = gda_thread_wrapper_fetch_result (wrapper, TRUE, &id, NULL);
+ if (GPOINTER_TO_INT (res) != i + GPOINTER_TO_INT (int_id) * 1000) {
+ g_print ("Error in %s() for thread %d: sub thread's exec result is wrong\n",
+ __FUNCTION__, GPOINTER_TO_INT (int_id));
+ nfailed ++;
+ }
+ if (id != ids[2*i]) {
+ g_print ("Error in %s() for thread %d: sub thread's exec result is for wrong ID\n",
+ __FUNCTION__, GPOINTER_TO_INT (int_id));
+ nfailed ++;
+ }
+#ifdef PRINT_CALLS
+ g_print ("<-- Thread %d jobID %u arg %d\n", GPOINTER_TO_INT (int_id), id, i + GPOINTER_TO_INT (int_id) * 1000);
+#endif
+ if (rand () >= RAND_MAX / 2.)
+ g_thread_yield ();
+
+ gchar *str;
+ estr = g_strdup_printf ("Hello %d.%d", GPOINTER_TO_INT (int_id), i);
+ str = gda_thread_wrapper_fetch_result (wrapper, TRUE, &id, NULL);
+ if (!str || strcmp (str, estr)) {
+ g_print ("Error in %s() for thread %d: sub thread's exec result is wrong: got %s, exp: %s\n",
+ __FUNCTION__, GPOINTER_TO_INT (int_id), str, estr);
+ nfailed ++;
+ }
+ g_free (estr);
+ if (id != ids[2*i+1]) {
+ g_print ("Error in %s() for thread %d: sub thread's exec result is for wrong ID\n",
+ __FUNCTION__, GPOINTER_TO_INT (int_id));
+ nfailed ++;
+ }
+#ifdef PRINT_CALLS
+ g_print ("<-- Thread %d jobID %u arg %s\n", GPOINTER_TO_INT (int_id), id, str);
+#endif
+ g_free (str);
+ if (rand () >= RAND_MAX / 2.)
+ g_thread_yield ();
+ }
+ g_free (ids);
+
+ return GINT_TO_POINTER (nfailed);
+}
+
+
+/*
+ * This test creates a single #GdaThreadWrapper object and a single dummy object (DummyObject) which is
+ * used to emit signals by functions executed in sub threads.
+ */
+typedef struct {
+ DummyObject *dummy;
+ GSList *signals_sent; /* list of TestSignal structures */
+} t2ExecData;
+static gpointer t2_main_thread_func (DummyObject *dummy);
+static gpointer t2_exec_in_wrapped_thread (t2ExecData *data, GError **error);
+static void t2_exec_in_wrapped_thread_v (t2ExecData *data, GError **error);
+#define INT_TOKEN 1034
+
+static gint
+test2 (void)
+{
+ gint failures = 0;
+ gint i;
+ GSList *threads = NULL;
+ DummyObject *dummy;
+
+ wrapper = gda_thread_wrapper_new ();
+ dummy = dummy_object_new ();
+
+ /* use GdaThreadWrapper from several threads */
+ for (i = 0; i < NTHREADS; i++) {
+ GThread *th;
+ th = g_thread_create ((GThreadFunc) t2_main_thread_func, dummy, TRUE, NULL);
+ if (!th) {
+ g_print ("Can't create thread %d (not part of the test)\n", i);
+ exit (1);
+ }
+ threads = g_slist_prepend (threads, th);
+ }
+
+ /* join the threads */
+ GSList *list;
+ for (list = threads; list; list = list->next) {
+ g_signal_emit_by_name (dummy, "sig0", NULL); /* this signal should be ignored */
+ failures += GPOINTER_TO_INT (g_thread_join ((GThread*) list->data));
+ }
+ g_slist_free (threads);
+
+ g_object_unref (wrapper);
+ g_object_unref (dummy);
+
+ return failures;
+}
+
+/*
+ * structure to hold information about either a signal sent or received
+ */
+typedef struct {
+ gchar *name;
+ guint n_param_values;
+ GValue *param_values; /* array of GValue structures */
+} TestSignal;
+
+/*
+ * ... is a list of GType and value as native type (there must be exactly @n_param_values pairs)
+ */
+static TestSignal *
+test_signal_new (const gchar *signame, guint n_param_values, ...)
+{
+ TestSignal *ts;
+ va_list ap;
+ guint i;
+
+ ts = g_new0 (TestSignal, 1);
+ ts->name = g_strdup (signame);
+ ts->n_param_values = n_param_values;
+ ts->param_values = g_new0 (GValue, n_param_values);
+
+ va_start (ap, n_param_values);
+ for (i = 0; i < n_param_values; i++) {
+ GValue *value;
+ GType type;
+ type = va_arg (ap, GType);
+
+ value = ts->param_values + i;
+ g_value_init (value, type);
+ if (type == G_TYPE_INT) {
+ gint v;
+ v = va_arg (ap, gint);
+ g_value_set_int (value, v);
+ }
+ else if (type == G_TYPE_STRING) {
+ gchar *v;
+ v = va_arg (ap, gchar *);
+ g_value_set_string (value, v);
+ }
+ else
+ g_assert_not_reached ();
+ }
+ va_end (ap);
+
+ return ts;
+}
+
+/*
+ * Returns: TRUE if lists are equal
+ */
+static gboolean
+compare_signals_lists (GSList *explist, GSList *gotlist)
+{
+ GSList *elist, *glist;
+ for (elist = explist, glist = gotlist;
+ elist && glist;
+ elist = elist->next, glist = glist->next) {
+ TestSignal *es = (TestSignal*) elist->data;
+ TestSignal *gs = (TestSignal*) glist->data;
+
+ if (strcmp (es->name, gs->name)) {
+ g_print ("Error: expected signal '%s', got signal '%s'\n", es->name, gs->name);
+ return FALSE;
+ }
+ if (es->n_param_values != gs->n_param_values) {
+ g_print ("Error: expected %d arguments in '%s', got %d\n", es->n_param_values,
+ es->name, gs->n_param_values);
+ return FALSE;
+ }
+ gint i;
+ for (i = 0; i < es->n_param_values; i++) {
+ GValue *ev, *gv;
+ ev = es->param_values + i;
+ gv = gs->param_values + i;
+ if (gda_value_differ (ev, gv)) {
+ g_print ("Error: expected value %s in '%s', got %s\n",
+ gda_value_stringify (ev), es->name, gda_value_stringify (gv));
+ return FALSE;
+ }
+ }
+ /*g_print ("Checked signal %s\n", es->name);*/
+ }
+
+ if (elist) {
+ g_print ("Error: Some signals have not been received\n");
+ return FALSE;
+ }
+
+ if (glist) {
+ g_print ("Error: Received too many signals\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gpointer
+t2_exec_in_wrapped_thread (t2ExecData *data, GError **error)
+{
+#ifdef PRINT_CALLS
+ g_print ("TH %p ** emit sig0 (dummy=>%p)\n", g_thread_self (), data->dummy);
+#endif
+ g_signal_emit_by_name (data->dummy, "sig0", NULL);
+ data->signals_sent = g_slist_append (data->signals_sent,
+ test_signal_new ("sig0", 0));
+
+#ifdef PRINT_CALLS
+ g_print ("TH %p ** emit sig1 (dummy=>%p, i=>123)\n", g_thread_self (), data->dummy);
+#endif
+ g_signal_emit_by_name (data->dummy, "sig1", 123, NULL);
+ data->signals_sent = g_slist_append (data->signals_sent,
+ test_signal_new ("sig1", 1, G_TYPE_INT, 123));
+
+#ifdef PRINT_CALLS
+ g_print ("TH %p ** emit sig0 (dummy=>%p)\n", g_thread_self (), data->dummy);
+#endif
+ g_signal_emit_by_name (data->dummy, "sig0", NULL);
+ data->signals_sent = g_slist_append (data->signals_sent,
+ test_signal_new ("sig0", 0));
+
+#ifdef PRINT_CALLS
+ g_print ("TH %p ** emit sig2 (dummy=>%p, i=>456 str=>Hello)\n", g_thread_self (), data->dummy);
+#endif
+ g_signal_emit_by_name (data->dummy, "sig2", 456, "Hello", NULL);
+ data->signals_sent = g_slist_append (data->signals_sent,
+ test_signal_new ("sig2", 2, G_TYPE_INT, 456, G_TYPE_STRING, "Hello"));
+
+#ifdef PRINT_CALLS
+ g_print ("TH %p ** emit sig0 (dummy=>%p)\n", g_thread_self (), data->dummy);
+#endif
+ g_signal_emit_by_name (data->dummy, "sig0", NULL);
+ data->signals_sent = g_slist_append (data->signals_sent,
+ test_signal_new ("sig0", 0));
+
+#ifdef PRINT_CALLS
+ g_print ("TH %p ** emit sig1 (dummy=>%p, i=>789)\n", g_thread_self (), data->dummy);
+#endif
+ g_signal_emit_by_name (data->dummy, "sig1", 789, NULL);
+ data->signals_sent = g_slist_append (data->signals_sent,
+ test_signal_new ("sig1", 1, G_TYPE_INT, 789));
+
+#ifdef PRINT_CALLS
+ g_print ("TH %p ** emit sig2 (dummy=>%p, i=>32 str=>World)\n", g_thread_self (), data->dummy);
+#endif
+ g_signal_emit_by_name (data->dummy, "sig2", 32, "World", NULL);
+ data->signals_sent = g_slist_append (data->signals_sent,
+ test_signal_new ("sig2", 2, G_TYPE_INT, 32, G_TYPE_STRING, "World"));
+
+ return GINT_TO_POINTER (INT_TOKEN);
+}
+
+static void
+t2_exec_in_wrapped_thread_v (t2ExecData *data, GError **error)
+{
+#ifdef PRINT_CALLS
+ g_print ("TH %p ** emit sig0 (dummy=>%p)\n", g_thread_self (), data->dummy);
+#endif
+ g_signal_emit_by_name (data->dummy, "sig0", NULL);
+ data->signals_sent = g_slist_append (data->signals_sent,
+ test_signal_new ("sig0", 0));
+
+#ifdef PRINT_CALLS
+ g_print ("TH %p ** emit sig1 (dummy=>%p, i=>321)\n", g_thread_self (), data->dummy);
+#endif
+ g_signal_emit_by_name (data->dummy, "sig1", 321, NULL);
+ data->signals_sent = g_slist_append (data->signals_sent,
+ test_signal_new ("sig1", 1, G_TYPE_INT, 321));
+
+#ifdef PRINT_CALLS
+ g_print ("TH %p ** emit sig0 (dummy=>%p)\n", g_thread_self (), data->dummy);
+#endif
+ g_signal_emit_by_name (data->dummy, "sig0", NULL);
+ data->signals_sent = g_slist_append (data->signals_sent,
+ test_signal_new ("sig0", 0));
+
+#ifdef PRINT_CALLS
+ g_print ("TH %p ** emit sig2 (dummy=>%p, i=>654 str=>Thread)\n", g_thread_self (), data->dummy);
+#endif
+ g_signal_emit_by_name (data->dummy, "sig2", 654, "Thread", NULL);
+ data->signals_sent = g_slist_append (data->signals_sent,
+ test_signal_new ("sig2", 2, G_TYPE_INT, 654, G_TYPE_STRING, "Thread"));
+
+#ifdef PRINT_CALLS
+ g_print ("TH %p ** emit sig0 (dummy=>%p)\n", g_thread_self (), data->dummy);
+#endif
+ g_signal_emit_by_name (data->dummy, "sig0", NULL);
+ data->signals_sent = g_slist_append (data->signals_sent,
+ test_signal_new ("sig0", 0));
+
+#ifdef PRINT_CALLS
+ g_print ("TH %p ** emit sig1 (dummy=>%p, i=>987)\n", g_thread_self (), data->dummy);
+#endif
+ g_signal_emit_by_name (data->dummy, "sig1", 987, NULL);
+ data->signals_sent = g_slist_append (data->signals_sent,
+ test_signal_new ("sig1", 1, G_TYPE_INT, 987));
+
+#ifdef PRINT_CALLS
+ g_print ("TH %p ** emit sig2 (dummy=>%p, i=>23 str=>Thread)\n", g_thread_self (), data->dummy);
+#endif
+ g_signal_emit_by_name (data->dummy, "sig2", 23, "Thread", NULL);
+ data->signals_sent = g_slist_append (data->signals_sent,
+ test_signal_new ("sig2", 2, G_TYPE_INT, 23, G_TYPE_STRING, "Thread"));
+}
+
+static void
+wrapper_callback (GdaThreadWrapper *wrapper, gpointer instance, const gchar *signame,
+ gint n_param_values, const GValue *param_values, gpointer gda_reserved, GSList **sig_list)
+{
+ gint i;
+ /*g_print ("RECEIVED signal '%s' on thread %p\n", signame, g_thread_self ());
+ for (i = 0; i < n_param_values; i++) {
+ gchar *str = gda_value_stringify (param_values + i);
+ g_print ("\tParam %d: %s\n", i, str);
+ g_free (str);
+ }
+ */
+
+ TestSignal *ts;
+
+ ts = g_new0 (TestSignal, 1);
+ ts->name = g_strdup (signame);
+ ts->n_param_values = n_param_values;
+ ts->param_values = g_new0 (GValue, n_param_values);
+ for (i = 0; i < n_param_values; i++) {
+ GValue *dest = ts->param_values + i;
+ const GValue *src = param_values + i;
+ if (G_VALUE_TYPE (src) != GDA_TYPE_NULL) {
+ g_value_init (dest, G_VALUE_TYPE (src));
+ g_value_copy (src, dest);
+ }
+ }
+ *sig_list = g_slist_append (*sig_list, ts);
+}
+
+static gpointer
+t2_main_thread_func (DummyObject *dummy)
+{
+ gint i;
+ guint *ids;
+
+ gint total_jobs = 0;
+ gint nfailed = 0;
+
+ gulong sigid[3];
+ t2ExecData edata;
+ GSList *received_list = NULL;
+
+ /*g_print ("NEW test thread: %p\n", g_thread_self());*/
+ sigid[0] = gda_thread_wrapper_connect_raw (wrapper, dummy, "sig0",
+ (GdaThreadWrapperCallback) wrapper_callback,
+ &received_list);
+ sigid[1] = gda_thread_wrapper_connect_raw (wrapper, dummy, "sig1",
+ (GdaThreadWrapperCallback) wrapper_callback,
+ &received_list);
+ sigid[2] = gda_thread_wrapper_connect_raw (wrapper, dummy, "sig2",
+ (GdaThreadWrapperCallback) wrapper_callback,
+ &received_list);
+
+ edata.dummy = dummy;
+ edata.signals_sent = NULL;
+
+ ids = g_new0 (guint, NCALLS);
+ for (i = 0; i < NCALLS; i++) {
+ guint id;
+ GError *error = NULL;
+
+ /* func1 */
+ id = gda_thread_wrapper_execute (wrapper,
+ (GdaThreadWrapperFunc) t2_exec_in_wrapped_thread,
+ &edata, NULL, &error);
+ if (id == 0) {
+ g_print ("Error in %s() for thread %p: %s\n", __FUNCTION__, g_thread_self (),
+ error && error->message ? error->message : "No detail");
+ if (error)
+ g_error_free (error);
+ return GINT_TO_POINTER (1);
+ }
+ ids[i] = id;
+#ifdef PRINT_CALLS
+ g_print ("--> Thread %p jobID %u\n", g_thread_self (), id);
+#endif
+ if (rand () >= RAND_MAX / 2.)
+ g_thread_yield ();
+
+
+ if (rand () >= RAND_MAX / 2.) {
+ id = gda_thread_wrapper_execute_void (wrapper,
+ (GdaThreadWrapperVoidFunc) t2_exec_in_wrapped_thread_v,
+ &edata,
+ NULL, &error);
+ if (id == 0) {
+ g_print ("Error in %s() for thread %p: %s\n", __FUNCTION__, g_thread_self (),
+ error && error->message ? error->message : "No detail");
+ if (error)
+ g_error_free (error);
+ return GINT_TO_POINTER (1);
+ }
+ total_jobs ++;
+ }
+ if (rand () >= RAND_MAX / 2.)
+ g_thread_yield ();
+ }
+ total_jobs += NCALLS;
+ g_thread_yield ();
+
+ /* pick up results */
+ for (i = 0; i < NCALLS; i++) {
+ guint id;
+ gpointer res;
+
+ g_signal_emit_by_name (dummy, "sig0", NULL); /* this signal should be ignored */
+
+ res = gda_thread_wrapper_fetch_result (wrapper, TRUE, &id, NULL);
+ if (GPOINTER_TO_INT (res) != INT_TOKEN) {
+ g_print ("Error in %s() for thread %p: sub thread's exec result is wrong\n",
+ __FUNCTION__, g_thread_self ());
+ nfailed ++;
+ }
+ if (id != ids[i]) {
+ g_print ("Error in %s() for thread %p: sub thread's exec result is for wrong ID\n",
+ __FUNCTION__, g_thread_self ());
+ nfailed ++;
+ }
+#ifdef PRINT_CALLS
+ g_print ("<-- Thread %p jobID %u arg %d\n", g_thread_self (), id, INT_TOKEN);
+#endif
+ if (rand () >= RAND_MAX / 2.)
+ g_thread_yield ();
+ g_signal_emit_by_name (dummy, "sig1", 666, NULL); /* this signal should be ignored */
+ }
+
+ gda_thread_wrapper_iterate (wrapper, FALSE);
+ if (! compare_signals_lists (edata.signals_sent, received_list))
+ nfailed++;
+
+
+#ifdef PRINT_CALLS
+ g_print ("Disconnected signals\n");
+#endif
+ gda_thread_wrapper_disconnect (wrapper, sigid[0]);
+ gda_thread_wrapper_disconnect (wrapper, sigid[1]);
+ gda_thread_wrapper_disconnect (wrapper, sigid[2]);
+
+ g_free (ids);
+
+ /* we don't care about mem leaks here... */
+ received_list = NULL;
+ guint id;
+ GError *error = NULL;
+ id = gda_thread_wrapper_execute_void (wrapper,
+ (GdaThreadWrapperVoidFunc) t2_exec_in_wrapped_thread_v,
+ &edata,
+ NULL, &error);
+ if (id == 0) {
+ g_print ("Error in %s() for thread %p: %s\n", __FUNCTION__, g_thread_self (),
+ error && error->message ? error->message : "No detail");
+ if (error)
+ g_error_free (error);
+ return GINT_TO_POINTER (1);
+ }
+ total_jobs ++;
+
+ gda_thread_wrapper_iterate (wrapper, FALSE);
+ g_usleep (100000);
+
+ if (received_list) {
+ g_print ("Error: signals should not be received anymore...\n");
+ nfailed ++;
+ }
+
+ return GINT_TO_POINTER (nfailed);
+}
Modified: trunk/tests/multi-threading/common.c
==============================================================================
--- trunk/tests/multi-threading/common.c (original)
+++ trunk/tests/multi-threading/common.c Tue Apr 7 19:10:50 2009
@@ -73,7 +73,7 @@
return retval;
}
-void
+gboolean
run_sql_non_select (GdaConnection *cnc, const gchar *sql)
{
GdaStatement *stmt;
@@ -82,13 +82,20 @@
GdaSqlParser *parser;
parser = gda_connection_create_parser (cnc);
+ if (!parser)
+ parser = gda_sql_parser_new ();
stmt = gda_sql_parser_parse_string (parser, sql, NULL, NULL);
g_object_unref (parser);
nrows = gda_connection_statement_execute_non_select (cnc, stmt, NULL, NULL, &error);
g_object_unref (stmt);
- if (nrows == -1)
+ if (nrows == -1) {
g_print ("NON SELECT error: %s\n", error && error->message ? error->message : "no detail");
+ if (error)
+ g_error_free (error);
+ return FALSE;
+ }
+ return TRUE;
}
GdaDataModel *
@@ -100,6 +107,9 @@
GdaSqlParser *parser;
parser = gda_connection_create_parser (cnc);
+ if (!parser)
+ parser = gda_sql_parser_new ();
+
stmt = gda_sql_parser_parse_string (parser, sql, NULL, NULL);
g_object_unref (parser);
@@ -110,3 +120,46 @@
error && error->message ? error->message : "no detail");
return res;
}
+
+GdaDataModel *
+run_sql_select_cursor (GdaConnection *cnc, const gchar *sql)
+{
+ GdaStatement *stmt;
+ GError *error = NULL;
+ GdaDataModel *res;
+ GdaSqlParser *parser;
+
+ parser = gda_connection_create_parser (cnc);
+ stmt = gda_sql_parser_parse_string (parser, sql, NULL, NULL);
+ g_object_unref (parser);
+
+ res = gda_connection_statement_execute_select_full (cnc, stmt, NULL, GDA_STATEMENT_MODEL_CURSOR_FORWARD,
+ NULL, &error);
+ g_object_unref (stmt);
+ if (!res)
+ g_print ("Could not execute query in cursor mode: %s\n",
+ error && error->message ? error->message : "no detail");
+ return res;
+}
+
+gboolean
+data_models_equal (GdaDataModel *m1, GdaDataModel *m2)
+{
+ GdaDataComparator *cmp;
+ GError *error = NULL;
+ cmp = (GdaDataComparator*) gda_data_comparator_new (m1, m2);
+ if (! gda_data_comparator_compute_diff (cmp, &error)) {
+ g_print ("Can't compare data models: %s\n", error && error->message ? error->message : "no detail");
+ if (error)
+ g_error_free (error);
+ g_object_unref (cmp);
+ return FALSE;
+ }
+ if (gda_data_comparator_get_n_diffs (cmp) != 0) {
+ g_print ("Data models differ: %d differences\n", gda_data_comparator_get_n_diffs (cmp));
+ g_object_unref (cmp);
+ return FALSE;
+ }
+ g_object_unref (cmp);
+ return TRUE;
+}
Modified: trunk/tests/multi-threading/common.h
==============================================================================
--- trunk/tests/multi-threading/common.h (original)
+++ trunk/tests/multi-threading/common.h Tue Apr 7 19:10:50 2009
@@ -5,6 +5,8 @@
gboolean create_sqlite_db (const gchar *dir, const gchar *dbname, const gchar *sqlfile, GError **error);
GdaDataModel *run_sql_select (GdaConnection *cnc, const gchar *sql);
-void run_sql_non_select (GdaConnection *cnc, const gchar *sql);
+GdaDataModel *run_sql_select_cursor (GdaConnection *cnc, const gchar *sql);
+gboolean run_sql_non_select (GdaConnection *cnc, const gchar *sql);
+gboolean data_models_equal (GdaDataModel *m1, GdaDataModel *m2);
#endif
Added: trunk/tests/multi-threading/dummy-object.c
==============================================================================
--- (empty file)
+++ trunk/tests/multi-threading/dummy-object.c Tue Apr 7 19:10:50 2009
@@ -0,0 +1,161 @@
+/* dummy-object.c
+ *
+ * Copyright (C) 2009 Vivien Malerba
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "dummy-object.h"
+#include <glib-object.h>
+
+/*
+ * Main static functions
+ */
+static void dummy_object_class_init (DummyObjectClass * class);
+static void dummy_object_init (DummyObject *object);
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass *parent_class = NULL;
+
+/* signals */
+enum
+{
+ SIG0,
+ SIG1,
+ SIG2,
+ LAST_SIGNAL
+};
+
+static gint dummy_object_signals[LAST_SIGNAL] = { 0, 0, 0 };
+
+GType
+dummy_object_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+ static const GTypeInfo info = {
+ sizeof (DummyObjectClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) dummy_object_class_init,
+ NULL,
+ NULL,
+ sizeof (DummyObject),
+ 0,
+ (GInstanceInitFunc) dummy_object_init
+ };
+
+ g_static_mutex_lock (®istering);
+ if (type == 0)
+ type = g_type_register_static (G_TYPE_OBJECT, "DummyObject", &info, 0);
+ g_static_mutex_unlock (®istering);
+ }
+
+ return type;
+}
+
+/* VOID:STRING,INT (gda-marshal.list:39) */
+#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
+#define g_marshal_value_peek_int(v) g_value_get_int (v)
+static void
+local_marshal_VOID__STRING_INT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__STRING_INT) (gpointer data1,
+ gpointer arg_1,
+ gint arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__STRING_INT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__STRING_INT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_string (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ data2);
+}
+
+static void
+dummy_object_class_init (DummyObjectClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ parent_class = g_type_class_peek_parent (class);
+
+ dummy_object_signals[SIG0] =
+ g_signal_new ("sig0",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (DummyObjectClass, sig0),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+ dummy_object_signals[SIG1] =
+ g_signal_new ("sig1",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (DummyObjectClass, sig1),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
+ dummy_object_signals[SIG1] =
+ g_signal_new ("sig2",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (DummyObjectClass, sig1),
+ NULL, NULL,
+ local_marshal_VOID__STRING_INT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_STRING);
+
+ class->sig1 = NULL;
+ class->sig0 = NULL;
+ class->sig2 = NULL;
+}
+
+static void
+dummy_object_init (DummyObject *object)
+{
+}
+
+/**
+ * dummy_object_new
+ *
+ * Creates a new object of type @type
+ *
+ * Returns: a new #DummyObject object
+ */
+DummyObject *
+dummy_object_new (void)
+{
+ return (DummyObject *) g_object_new (DUMMY_TYPE_OBJECT, NULL);
+}
Added: trunk/tests/multi-threading/dummy-object.h
==============================================================================
--- (empty file)
+++ trunk/tests/multi-threading/dummy-object.h Tue Apr 7 19:10:50 2009
@@ -0,0 +1,58 @@
+/* dummy-object.h
+ *
+ * Copyright (C) 2009 Vivien Malerba
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __DUMMY_OBJECT_H_
+#define __DUMMY_OBJECT_H_
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define DUMMY_TYPE_OBJECT (dummy_object_get_type())
+#define DUMMY_OBJECT(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, dummy_object_get_type(), DummyObject)
+#define DUMMY_OBJECT_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, dummy_object_get_type (), DummyObjectClass)
+#define DUMMY_IS_OBJECT(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, dummy_object_get_type ())
+
+typedef struct _DummyObject DummyObject;
+typedef struct _DummyObjectClass DummyObjectClass;
+
+/* struct for the object's data */
+struct _DummyObject
+{
+ GObject object;
+};
+
+
+/* struct for the object's class */
+struct _DummyObjectClass
+{
+ GObjectClass parent_class;
+ void (*sig0) (DummyObject *object);
+ void (*sig1) (DummyObject *object, gint i);
+ void (*sig2) (DummyObject *object, gint i, gchar *str);
+};
+
+GType dummy_object_get_type (void) G_GNUC_CONST;
+DummyObject *dummy_object_new (void);
+
+G_END_DECLS
+
+#endif
Modified: trunk/tools/web-server.c
==============================================================================
--- trunk/tools/web-server.c (original)
+++ trunk/tools/web-server.c Tue Apr 7 19:10:50 2009
@@ -33,6 +33,8 @@
#include "global.h"
#include "md5.h"
+#include <inttypes.h>
+
#define MAX_CHALLENGES 10
#define MAX_AUTH_COOKIES 10
@@ -475,10 +477,6 @@
return TRUE;
}
-#ifdef G_OS_WIN32
-typedef guint8 uint8_t;
-#endif
-
#define PAD_LEN 64 /* PAD length */
#define SIG_LEN 16 /* MD5 digest length */
/*
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]