[libgda] Initial Web provider implementation
- From: Vivien Malerba <vivien src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [libgda] Initial Web provider implementation
- Date: Sun, 15 Nov 2009 21:57:31 +0000 (UTC)
commit 6ead78dd2f0a35810139d3c67b245c96c3dad5c4
Author: Vivien Malerba <malerba gnome-db org>
Date: Sun Nov 15 22:56:01 2009 +0100
Initial Web provider implementation
this new provider opens a connection through a distant
web server (tested with apache and lighthttpd) where some
PHP scripts are executed
configure.in | 7 +
doc/C/libgda-sections.txt | 3 +
doc/C/tmpl/gda-connection.sgml | 1 +
doc/C/tmpl/provider-support.sgml | 19 +
libgda/gda-connection-internal.h | 1 +
libgda/gda-connection-private.h | 6 +
libgda/gda-connection.c | 162 ++-
libgda/gda-connection.h | 3 +-
libgda/gda-util.c | 16 +-
libgda/gda-value.c | 11 +-
libgda/libgda.symbols | 2 +
libgda/sqlite/gda-sqlite-provider.c | 14 +-
libgda/sqlite/keywords_hash.h | 6 +
libgda/thread-wrapper/gda-thread-provider.c | 43 +-
libgda/thread-wrapper/gda-thread-wrapper.c | 6 +-
po/POTFILES.in | 11 +-
providers/Makefile.am | 8 +-
providers/postgres/Makefile.am | 39 +-
providers/postgres/gda-postgres-meta.c | 1760 -----------------
providers/postgres/gda-postgres-provider.c | 243 +---
providers/postgres/gda-postgres-recordset.c | 2 +-
providers/postgres/gda-postgres-util.c | 44 -
providers/postgres/gda-postgres-util.h | 8 +-
providers/postgres/gda-postgres.h | 21 +-
providers/reuseable/Makefile.am | 19 +
providers/reuseable/gda-provider-reuseable.h | 69 +
providers/reuseable/postgres/Makefile.am | 49 +
providers/reuseable/postgres/gda-postgres-meta.c | 2042 ++++++++++++++++++++
.../{ => reuseable}/postgres/gda-postgres-meta.h | 0
.../{ => reuseable}/postgres/gda-postgres-parser.c | 0
.../{ => reuseable}/postgres/gda-postgres-parser.h | 0
.../reuseable/postgres/gda-postgres-reuseable.c | 432 +++++
.../reuseable/postgres/gda-postgres-reuseable.h | 78 +
providers/{ => reuseable}/postgres/gen_def.c | 0
.../{ => reuseable}/postgres/keywords_82.list | 0
.../{ => reuseable}/postgres/keywords_83.list | 0
.../{ => reuseable}/postgres/keywords_84.list | 0
providers/{ => reuseable}/postgres/parser.y | 0
providers/reuseable/reuse-all.c | 55 +
providers/reuseable/reuse-all.h | 37 +
providers/web/Makefile.am | 69 +
providers/web/README | 63 +
providers/web/gda-web-blob-op.c | 226 +++
providers/web/gda-web-blob-op.h | 57 +
providers/web/gda-web-ddl.c | 145 ++
providers/web/gda-web-ddl.h | 34 +
providers/web/gda-web-meta.c | 1382 +++++++++++++
providers/web/gda-web-meta.h | 197 ++
providers/web/gda-web-provider.c | 1686 ++++++++++++++++
providers/web/gda-web-provider.h | 51 +
providers/web/gda-web-pstmt.c | 143 ++
providers/web/gda-web-pstmt.h | 57 +
providers/web/gda-web-recordset.c | 631 ++++++
providers/web/gda-web-recordset.h | 60 +
providers/web/gda-web-util.c | 657 +++++++
providers/web/gda-web-util.h | 59 +
providers/web/gda-web.h | 67 +
providers/web/libgda-web-4.0.pc.in | 9 +
providers/web/libmain.c | 104 +
providers/web/php/README | 35 +
providers/web/php/gda-clean.php | 23 +
providers/web/php/gda-config.php | 28 +
providers/web/php/gda-exception.php | 9 +
providers/web/php/gda-front.php | 68 +
providers/web/php/gda-meta.php | 648 +++++++
providers/web/php/gda-setup.php | 57 +
providers/web/php/gda-tester.php | 50 +
providers/web/php/gda-utils.php | 107 +
providers/web/php/gda-worker.php | 654 +++++++
providers/web/protocol.dia | Bin 0 -> 3480 bytes
providers/web/web_specs_auth.xml.in | 6 +
providers/web/web_specs_dsn.xml.in | 11 +
tools/gda-sql.c | 30 +-
73 files changed, 10526 insertions(+), 2114 deletions(-)
---
diff --git a/configure.in b/configure.in
index 3ccf941..567397d 100644
--- a/configure.in
+++ b/configure.in
@@ -9,6 +9,8 @@ AC_PREREQ(2.59)
AC_CONFIG_SRCDIR(libgda/libgda.h.in)
AM_INIT_AUTOMAKE(1.8 -Wall no-define dist-bzip2)
+m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
+
AC_SUBST(GDA_VERSION, major.minor.micro)
@@ -1708,6 +1710,8 @@ libgda/sqlite/sqlite-src/Makefile
libgda/sqlite/virtual/Makefile
libgda/thread-wrapper/Makefile
providers/Makefile
+providers/reuseable/Makefile
+providers/reuseable/postgres/Makefile
providers/bdb/Makefile
providers/bdb/libgda-bdb-4.0.pc
providers/mdb/Makefile
@@ -1722,6 +1726,8 @@ providers/sqlite/Makefile
providers/sqlite/libgda-sqlite-4.0.pc
providers/jdbc/Makefile
providers/jdbc/libgda-jdbc-4.0.pc
+providers/web/Makefile
+providers/web/libgda-web-4.0.pc
providers/skel-implementation/Makefile
providers/skel-implementation/capi/Makefile
providers/skel-implementation/capi/libgda-capi-4.0.pc
@@ -1808,6 +1814,7 @@ dnl echo " Sybase = `if test x$sybasedir != x; then echo yes; else echo no;
dnl echo " xBase (dBase, Clipper, FoxPro) = `if test x$xbasedir != x; then echo yes; else echo no; fi`"
dnl echo " LDAP = `if test x$ldapdir != x; then echo yes; else echo no; fi`"
echo " JDBC = `if test x$have_java = xyes; then echo yes; else echo no; fi`"
+echo " WEB = `if test x$have_libsoup = xyes; then echo yes; else echo no; fi`"
if test x"$br_cv_binreloc" != "xyes" -a x"$platform_win32" != "xyes"
then
echo " Binreloc support is disabled: Libgda will not be relocatable. To enable binreloc support re-run with --enable-binreloc (see http://autopackage.org/docs/binreloc for more information)"
diff --git a/doc/C/libgda-sections.txt b/doc/C/libgda-sections.txt
index 114997a..33d1799 100644
--- a/doc/C/libgda-sections.txt
+++ b/doc/C/libgda-sections.txt
@@ -1462,6 +1462,9 @@ gda_connection_internal_savepoint_added
gda_connection_internal_savepoint_rolledback
gda_connection_internal_savepoint_removed
gda_connection_internal_change_transaction_state
+gda_connection_internal_reset_transaction_status
+<SUBSECTION>
+gda_connection_open_sqlite
<SUBSECTION>
gda_meta_store_modify
gda_meta_store_modify_with_context
diff --git a/doc/C/tmpl/gda-connection.sgml b/doc/C/tmpl/gda-connection.sgml
index 98eb8fa..3515eaa 100644
--- a/doc/C/tmpl/gda-connection.sgml
+++ b/doc/C/tmpl/gda-connection.sgml
@@ -172,6 +172,7 @@ A connection to a database
@GDA_CONNECTION_CANT_LOCK_ERROR:
@GDA_CONNECTION_TASK_NOT_FOUND_ERROR:
@GDA_CONNECTION_UNSUPPORTED_THREADS_ERROR:
+ GDA_CONNECTION_CLOSED_ERROR:
<!-- ##### MACRO GDA_CONNECTION_ERROR ##### -->
<para>
diff --git a/doc/C/tmpl/provider-support.sgml b/doc/C/tmpl/provider-support.sgml
index 147ac38..937bd21 100644
--- a/doc/C/tmpl/provider-support.sgml
+++ b/doc/C/tmpl/provider-support.sgml
@@ -323,6 +323,25 @@ Methods dedicated to implementing providers
@newstate:
+<!-- ##### FUNCTION gda_connection_internal_reset_transaction_status ##### -->
+<para>
+
+</para>
+
+ cnc:
+
+
+<!-- ##### FUNCTION gda_connection_open_sqlite ##### -->
+<para>
+
+</para>
+
+ directory:
+ filename:
+ auto_unlink:
+ Returns:
+
+
<!-- ##### FUNCTION gda_meta_store_modify ##### -->
<para>
diff --git a/libgda/gda-connection-internal.h b/libgda/gda-connection-internal.h
index 737eb5c..40edfeb 100644
--- a/libgda/gda-connection-internal.h
+++ b/libgda/gda-connection-internal.h
@@ -62,6 +62,7 @@ void _ThreadConnectionAsyncTask_free (ThreadConnectionAsyncTask *atd);
*/
typedef struct {
GdaConnection *sub_connection;
+ gboolean sub_connection_has_closed;
GdaServerProvider *cnc_provider;
GdaThreadWrapper *wrapper;
GArray *handlers_ids; /* array of gulong */
diff --git a/libgda/gda-connection-private.h b/libgda/gda-connection-private.h
index af5f7dd..f849f10 100644
--- a/libgda/gda-connection-private.h
+++ b/libgda/gda-connection-private.h
@@ -56,6 +56,7 @@ void gda_connection_internal_savepoint_rolledback (GdaConnection *cnc, const gch
void gda_connection_internal_savepoint_removed (GdaConnection *cnc, const gchar *svp_name);
void gda_connection_internal_change_transaction_state (GdaConnection *cnc,
GdaTransactionStatusState newstate);
+void gda_connection_internal_reset_transaction_status (GdaConnection *cnc);
/*
* prepared statements support
@@ -64,6 +65,11 @@ void gda_connection_add_prepared_statement (GdaConnection *cnc, GdaStatemen
void gda_connection_del_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt);
GdaPStmt *gda_connection_get_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt);
+/*
+ * Open an SQLite connection even if the SQLite provider is not installed
+ */
+GdaConnection *gda_connection_open_sqlite (const gchar *directory, const gchar *filename, gboolean auto_unlink);
+
G_END_DECLS
#endif
diff --git a/libgda/gda-connection.c b/libgda/gda-connection.c
index baac0a1..359ce6f 100644
--- a/libgda/gda-connection.c
+++ b/libgda/gda-connection.c
@@ -48,6 +48,10 @@
#include <libgda/thread-wrapper/gda-thread-provider.h>
#include <libgda/gda-repetitive-statement.h>
+#include <glib/gstdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+
#define PROV_CLASS(provider) (GDA_SERVER_PROVIDER_CLASS (G_OBJECT_GET_CLASS (provider)))
struct _GdaConnectionPrivate {
@@ -1127,7 +1131,7 @@ _gda_open_internal_sqlite_connection (const gchar *cnc_string)
GdaServerProvider *prov = _gda_config_sqlite_provider;
gchar *user, *pass, *real_cnc, *real_provider;
- g_print ("%s(%s)\n", __FUNCTION__, cnc_string);
+ /*g_print ("%s(%s)\n", __FUNCTION__, cnc_string);*/
gda_connection_string_split (cnc_string, &real_cnc, &real_provider, &user, &pass);
if (!real_cnc) {
g_free (user);
@@ -1162,6 +1166,68 @@ _gda_open_internal_sqlite_connection (const gchar *cnc_string)
return cnc;
}
+static void
+sqlite_connection_closed_cb (GdaConnection *cnc, gpointer data)
+{
+ gchar *filename;
+ filename = g_object_get_data (G_OBJECT (cnc), "__gda_fname");
+ g_assert (filename && *filename);
+ g_unlink (filename);
+}
+
+/**
+ * gda_connection_open_sqlite
+ * @cnc_string: a connection string
+ * @directory: the directory the database file will be in, or %NULL for the default TMP directory
+ * @filename: the database file name
+ * @auto_unlink: if %TRUE, then the database file will be removed afterwards
+ *
+ * Opens an SQLite connection even if the SQLite provider is not installed,
+ * to be used by database providers which need a temporary database to store
+ * some information.
+ *
+ * Returns: a new #GdaConnection, or %NULL if an error occurred
+ */
+GdaConnection *
+gda_connection_open_sqlite (const gchar *directory, const gchar *filename, gboolean auto_unlink)
+{
+ GdaConnection *cnc;
+ gchar *fname;
+ gint fd;
+
+ if (!directory)
+ directory = g_get_tmp_dir();
+ else
+ g_return_val_if_fail (*directory, NULL);
+ g_return_val_if_fail (filename && *filename, NULL);
+
+ fname = g_build_filename (directory, filename, NULL);
+ fd = g_open (fname, O_WRONLY | O_CREAT | O_NOCTTY | O_TRUNC,
+ S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ g_free (fname);
+ return NULL;
+ }
+ close (fd);
+
+ gchar *tmp1, *tmp2, *cncstring;
+ tmp1 = gda_rfc1738_encode (directory);
+ tmp2 = gda_rfc1738_encode (filename);
+ cncstring = g_strdup_printf ("SQLite://DB_DIR=%s;DB_NAME=%s", tmp1, tmp2);
+ g_free (tmp1);
+ g_free (tmp2);
+
+ cnc = _gda_open_internal_sqlite_connection (cncstring);
+ g_free (cncstring);
+
+ if (auto_unlink) {
+ g_object_set_data_full (G_OBJECT (cnc), "__gda_fname", fname, g_free);
+ g_signal_connect (cnc, "conn-closed",
+ G_CALLBACK (sqlite_connection_closed_cb), NULL);
+ }
+ return cnc;
+}
+
/**
* gda_connection_open
* @cnc: a #GdaConnection object
@@ -1251,12 +1317,15 @@ gda_connection_open (GdaConnection *cnc, GError **error)
/* try to open the connection */
auth = gda_quark_list_new_from_string (real_auth_string);
+ cnc->priv->is_open = TRUE; /* consider the connection opened to allow the open_connection()
+ * virtual method to execute statements of needed */
if (PROV_CLASS (cnc->priv->provider_obj)->open_connection (cnc->priv->provider_obj, cnc, params, auth,
NULL, NULL, NULL))
cnc->priv->is_open = TRUE;
else {
const GList *events;
+ cnc->priv->is_open = FALSE;
events = gda_connection_get_events (cnc);
if (events) {
GList *l;
@@ -1341,6 +1410,7 @@ gda_connection_close_no_warning (GdaConnection *cnc)
{
g_return_if_fail (GDA_IS_CONNECTION (cnc));
+ g_object_ref (cnc);
gda_connection_lock ((GdaLockable*) cnc);
if (cnc->priv->monitor_id > 0) {
@@ -1349,6 +1419,7 @@ gda_connection_close_no_warning (GdaConnection *cnc)
}
if (! cnc->priv->is_open) {
+ g_object_unref (cnc);
gda_connection_unlock ((GdaLockable*) cnc);
return;
}
@@ -1375,6 +1446,7 @@ gda_connection_close_no_warning (GdaConnection *cnc)
cnc->priv->provider_data = NULL;
}
+ gda_connection_unlock ((GdaLockable*) cnc);
#ifdef GDA_DEBUG_signal
g_print (">> 'CONN_CLOSED' from %s\n", __FUNCTION__);
#endif
@@ -1382,8 +1454,7 @@ gda_connection_close_no_warning (GdaConnection *cnc)
#ifdef GDA_DEBUG_signal
g_print ("<< 'CONN_CLOSED' from %s\n", __FUNCTION__);
#endif
-
- gda_connection_unlock ((GdaLockable*) cnc);
+ g_object_unref (cnc);
}
@@ -1913,6 +1984,7 @@ async_stmt_exec_cb (GdaServerProvider *provider, GdaConnection *cnc, guint task_
gint i;
gboolean is_completed;
+ g_object_ref ((GObject*) cnc);
gda_connection_lock (GDA_LOCKABLE (cnc));
i = get_task_index (cnc, task_id, &is_completed, TRUE);
@@ -1982,6 +2054,7 @@ async_stmt_exec_cb (GdaServerProvider *provider, GdaConnection *cnc, guint task_
"ignored.\n", task_id);
gda_connection_unlock (GDA_LOCKABLE (cnc));
+ g_object_unref ((GObject*) cnc);
}
/**
@@ -2029,11 +2102,22 @@ gda_connection_async_statement_execute (GdaConnection *cnc, GdaStatement *stmt,
g_return_val_if_fail (PROV_CLASS (cnc->priv->provider_obj)->statement_execute, 0);
+ g_object_ref ((GObject*) cnc);
if (! gda_connection_trylock ((GdaLockable*) cnc)) {
g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_CANT_LOCK_ERROR,
_("Can't obtain connection lock"));
+ g_object_unref ((GObject*) cnc);
return 0;
}
+
+ if (!cnc->priv->is_open) {
+ g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_CLOSED_ERROR,
+ _("Connection is closed"));
+ gda_connection_unlock (GDA_LOCKABLE (cnc));
+ g_object_unref ((GObject*) cnc);
+ 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);
@@ -2071,6 +2155,7 @@ gda_connection_async_statement_execute (GdaConnection *cnc, GdaStatement *stmt,
}
gda_connection_unlock ((GdaLockable*) cnc);
+ g_object_unref ((GObject*) cnc);
return id;
}
@@ -2231,12 +2316,21 @@ gda_connection_statement_execute_v (GdaConnection *cnc, GdaStatement *stmt, GdaS
types = make_col_types_array (10, ap);
va_end (ap);
+ g_object_ref ((GObject*) cnc);
gda_connection_lock ((GdaLockable*) cnc);
if (last_inserted_row)
*last_inserted_row = NULL;
if (cnc->priv->auto_clear_events_list)
_clear_events_list (cnc);
+ if (!cnc->priv->is_open) {
+ g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_CLOSED_ERROR,
+ _("Connection is closed"));
+ gda_connection_unlock (GDA_LOCKABLE (cnc));
+ g_object_unref ((GObject*) cnc);
+ return NULL;
+ }
+
if (! (model_usage & GDA_STATEMENT_MODEL_RANDOM_ACCESS) &&
! (model_usage & GDA_STATEMENT_MODEL_CURSOR_FORWARD))
model_usage |= GDA_STATEMENT_MODEL_RANDOM_ACCESS;
@@ -2247,6 +2341,8 @@ gda_connection_statement_execute_v (GdaConnection *cnc, GdaStatement *stmt, GdaS
NULL, NULL, NULL, error);
g_free (types);
gda_connection_unlock ((GdaLockable*) cnc);
+ g_object_unref ((GObject*) cnc);
+
return obj;
}
@@ -2491,10 +2587,19 @@ gda_connection_statement_execute_select_fullv (GdaConnection *cnc, GdaStatement
types = make_col_types_array (10, ap);
va_end (ap);
+ g_object_ref ((GObject*) cnc);
gda_connection_lock ((GdaLockable*) cnc);
if (cnc->priv->auto_clear_events_list)
_clear_events_list (cnc);
+ if (!cnc->priv->is_open) {
+ g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_CLOSED_ERROR,
+ _("Connection is closed"));
+ gda_connection_unlock (GDA_LOCKABLE (cnc));
+ g_object_unref ((GObject*) cnc);
+ return NULL;
+ }
+
if (! (model_usage & GDA_STATEMENT_MODEL_RANDOM_ACCESS) &&
! (model_usage & GDA_STATEMENT_MODEL_CURSOR_FORWARD))
model_usage |= GDA_STATEMENT_MODEL_RANDOM_ACCESS;
@@ -2505,6 +2610,7 @@ gda_connection_statement_execute_select_fullv (GdaConnection *cnc, GdaStatement
types, NULL, NULL,
NULL, NULL, error);
gda_connection_unlock ((GdaLockable*) cnc);
+ g_object_unref ((GObject*) cnc);
g_free (types);
if (model && !GDA_IS_DATA_MODEL (model)) {
g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_STATEMENT_TYPE_ERROR,
@@ -2552,10 +2658,19 @@ gda_connection_statement_execute_select_full (GdaConnection *cnc, GdaStatement *
g_return_val_if_fail (GDA_IS_STATEMENT (stmt), NULL);
g_return_val_if_fail (PROV_CLASS (cnc->priv->provider_obj)->statement_execute, NULL);
+ g_object_ref ((GObject*) cnc);
gda_connection_lock ((GdaLockable*) cnc);
if (cnc->priv->auto_clear_events_list)
_clear_events_list (cnc);
+ if (!cnc->priv->is_open) {
+ g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_CLOSED_ERROR,
+ _("Connection is closed"));
+ gda_connection_unlock (GDA_LOCKABLE (cnc));
+ g_object_unref ((GObject*) cnc);
+ return NULL;
+ }
+
if (! (model_usage & GDA_STATEMENT_MODEL_RANDOM_ACCESS) &&
! (model_usage & GDA_STATEMENT_MODEL_CURSOR_FORWARD))
model_usage |= GDA_STATEMENT_MODEL_RANDOM_ACCESS;
@@ -2566,6 +2681,8 @@ gda_connection_statement_execute_select_full (GdaConnection *cnc, GdaStatement *
model_usage, col_types, NULL,
NULL, NULL, NULL, error);
gda_connection_unlock ((GdaLockable*) cnc);
+ g_object_unref ((GObject*) cnc);
+
if (model && !GDA_IS_DATA_MODEL (model)) {
g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_STATEMENT_TYPE_ERROR,
"%s", _("Statement is not a selection statement"));
@@ -2612,8 +2729,17 @@ gda_connection_repetitive_statement_execute (GdaConnection *cnc, GdaRepetitiveSt
g_object_get (rstmt, "statement", &stmt, NULL);
g_return_val_if_fail (stmt, NULL);
+ g_object_ref ((GObject*) cnc);
gda_connection_lock ((GdaLockable*) cnc);
+ if (!cnc->priv->is_open) {
+ g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_CLOSED_ERROR,
+ _("Connection is closed"));
+ gda_connection_unlock (GDA_LOCKABLE (cnc));
+ g_object_unref ((GObject*) cnc);
+ return NULL;
+ }
+
if (! (model_usage & GDA_STATEMENT_MODEL_RANDOM_ACCESS) &&
! (model_usage & GDA_STATEMENT_MODEL_CURSOR_FORWARD))
model_usage |= GDA_STATEMENT_MODEL_RANDOM_ACCESS;
@@ -2645,7 +2771,7 @@ gda_connection_repetitive_statement_execute (GdaConnection *cnc, GdaRepetitiveSt
g_slist_free (sets_list);
gda_connection_unlock ((GdaLockable*) cnc);
-
+ g_object_unref ((GObject*) cnc);
g_object_unref (stmt);
return g_slist_reverse (retlist);
@@ -4687,7 +4813,7 @@ gda_connection_internal_statement_executed (GdaConnection *cnc, GdaStatement *st
* @cnc: a #GdaConnection
* @newstate: the new state
*
- * Internal functions to be called by database providers to force a transaction status
+ * Internal function to be called by database providers to force a transaction status
* change.
*/
void
@@ -4714,6 +4840,32 @@ gda_connection_internal_change_transaction_state (GdaConnection *cnc,
gda_connection_unlock ((GdaLockable*) cnc);
}
+/**
+ * gda_connection_internal_reset_transaction_status
+ * @cnc: a #GdaConnection
+ *
+ * Internal function to be called by database providers to reset the transaction status.
+ */
+void
+gda_connection_internal_reset_transaction_status (GdaConnection *cnc)
+{
+ g_return_if_fail (GDA_IS_CONNECTION (cnc));
+
+ gda_connection_lock ((GdaLockable*) cnc);
+ if (cnc->priv->trans_status) {
+ g_object_unref (cnc->priv->trans_status);
+ cnc->priv->trans_status = NULL;
+#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
+ }
+ gda_connection_unlock ((GdaLockable*) cnc);
+}
+
/*
* Prepared statements handling
*/
diff --git a/libgda/gda-connection.h b/libgda/gda-connection.h
index 134e65c..6aa531a 100644
--- a/libgda/gda-connection.h
+++ b/libgda/gda-connection.h
@@ -56,7 +56,8 @@ typedef enum {
GDA_CONNECTION_STATEMENT_TYPE_ERROR,
GDA_CONNECTION_CANT_LOCK_ERROR,
GDA_CONNECTION_TASK_NOT_FOUND_ERROR,
- GDA_CONNECTION_UNSUPPORTED_THREADS_ERROR
+ GDA_CONNECTION_UNSUPPORTED_THREADS_ERROR,
+ GDA_CONNECTION_CLOSED_ERROR
} GdaConnectionError;
#define GDA_CONNECTION_NONEXIST_DSN_ERROR GDA_CONNECTION_DSN_NOT_FOUND_ERROR
diff --git a/libgda/gda-util.c b/libgda/gda-util.c
index 3c30774..5b6e6e8 100644
--- a/libgda/gda-util.c
+++ b/libgda/gda-util.c
@@ -41,12 +41,9 @@
#include <libgda/binreloc/gda-binreloc.h>
-/* we don't want to duplicate the symbols in <libgda/sqlite/keywords_hash.h>, so simply
- * declare them as external
- */
-extern const unsigned char UpperToLower[];
#define charMap(X) UpperToLower[(unsigned char)(X)]
-extern int casecmp(const char *zLeft, const char *zRight, int N);
+#define KEYWORDS_HASH_NO_STATIC
+#include <libgda/sqlite/keywords_hash.h>
#include "keywords_hash.c" /* this one is dynamically generated */
extern gchar *gda_lang_locale;
@@ -547,6 +544,15 @@ gda_utility_holder_load_attributes (GdaHolder *holder, xmlNodePtr node, GSList *
str = xmlGetProp (node, BAD_CAST "source");
if (str)
g_object_set_data_full (G_OBJECT (holder), "source", str, xmlFree);
+ str = xmlGetProp (node, BAD_CAST "plugin");
+ if (str) {
+ GValue *value;
+#define GDAUI_ATTRIBUTE_PLUGIN "__gdaui_attr_plugin"
+ value = gda_value_new_from_string ((gchar*) str, G_TYPE_STRING);
+ gda_holder_set_attribute_static (holder, GDAUI_ATTRIBUTE_PLUGIN, value);
+ gda_value_free (value);
+ xmlFree (str);
+ }
/* set restricting source if specified */
if (str && sources) {
diff --git a/libgda/gda-value.c b/libgda/gda-value.c
index 1dba034..c5dcc55 100644
--- a/libgda/gda-value.c
+++ b/libgda/gda-value.c
@@ -63,18 +63,11 @@ set_from_string (GValue *value, const gchar *as_string)
/* custom transform function */
retval = FALSE;
if (type == G_TYPE_BOOLEAN) {
- if (((as_string[0] == 't') || (as_string[0] == 'T')) &&
- ((as_string[1] == 'r') || (as_string[1] == 'R')) &&
- ((as_string[2] == 'u') || (as_string[2] == 'U')) &&
- ((as_string[3] == 'e') || (as_string[3] == 'E'))) {
+ if ((as_string[0] == 't') || (as_string[0] == 'T')) {
g_value_set_boolean (value, TRUE);
retval = TRUE;
}
- else if (((as_string[0] == 'f') || (as_string[0] == 'F')) &&
- ((as_string[1] == 'a') || (as_string[1] == 'A')) &&
- ((as_string[2] == 'l') || (as_string[2] == 'L')) &&
- ((as_string[3] == 's') || (as_string[3] == 'S')) &&
- ((as_string[4] == 'e') || (as_string[4] == 'E'))) {
+ else if ((as_string[0] == 'f') || (as_string[0] == 'F')) {
g_value_set_boolean (value, FALSE);
retval = TRUE;
}
diff --git a/libgda/libgda.symbols b/libgda/libgda.symbols
index 2a77133..0aaab9c 100644
--- a/libgda/libgda.symbols
+++ b/libgda/libgda.symbols
@@ -134,6 +134,7 @@
gda_connection_get_type
gda_connection_internal_change_transaction_state
gda_connection_internal_get_provider_data
+ gda_connection_internal_reset_transaction_status
gda_connection_internal_savepoint_added
gda_connection_internal_savepoint_removed
gda_connection_internal_savepoint_rolledback
@@ -147,6 +148,7 @@
gda_connection_open
gda_connection_open_from_dsn
gda_connection_open_from_string
+ gda_connection_open_sqlite
gda_connection_options_get_type
gda_connection_perform_operation
gda_connection_quote_sql_identifier
diff --git a/libgda/sqlite/gda-sqlite-provider.c b/libgda/sqlite/gda-sqlite-provider.c
index 9cef424..fa11f11 100644
--- a/libgda/sqlite/gda-sqlite-provider.c
+++ b/libgda/sqlite/gda-sqlite-provider.c
@@ -646,11 +646,17 @@ gda_sqlite_provider_open_connection (GdaServerProvider *provider, GdaConnection
/* set SQLite library options */
GObject *obj;
+ GError *lerror = NULL;
obj = gda_connection_statement_execute (cnc, internal_stmt[INTERNAL_PRAGMA_EMPTY_RESULT],
- NULL, GDA_STATEMENT_MODEL_RANDOM_ACCESS, NULL, NULL);
- if (!obj)
- gda_connection_add_event_string (cnc, _("Could not set empty_result_callbacks SQLite option"));
- g_object_unref (obj);
+ NULL, GDA_STATEMENT_MODEL_RANDOM_ACCESS, NULL, &lerror);
+ if (!obj) {
+ gda_connection_add_event_string (cnc,
+ _("Could not set empty_result_callbacks SQLite option: %s"),
+ lerror && lerror->message ? lerror->message : _("no detail"));
+ g_clear_error (&lerror);
+ }
+ else
+ g_object_unref (obj);
/* make sure the internals are completely initialized now */
{
diff --git a/libgda/sqlite/keywords_hash.h b/libgda/sqlite/keywords_hash.h
index 091884c..1d0e5e2 100644
--- a/libgda/sqlite/keywords_hash.h
+++ b/libgda/sqlite/keywords_hash.h
@@ -10,6 +10,9 @@
#include <string.h>
+#ifndef KEYWORDS_HASH_NO_STATIC
+static
+#endif
const unsigned char UpperToLower[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
@@ -28,6 +31,9 @@ const unsigned char UpperToLower[] = {
252,253,254,255
};
#define charMap(X) UpperToLower[(unsigned char)X]
+#ifndef KEYWORDS_HASH_NO_STATIC
+static
+#endif
int
casecmp (const char *zLeft, const char *zRight, int N)
{
diff --git a/libgda/thread-wrapper/gda-thread-provider.c b/libgda/thread-wrapper/gda-thread-provider.c
index 51e65d1..5c06789 100644
--- a/libgda/thread-wrapper/gda-thread-provider.c
+++ b/libgda/thread-wrapper/gda-thread-provider.c
@@ -286,6 +286,20 @@ _gda_thread_provider_get_type (void)
return type;
}
+/*
+ * Check if the wrapped connection has been closed in the meanwhile
+ *
+ * Returns: %TRUE if @cnc is now also closed (WARNING: @cnc may not exist anymore it %TRUE is returned)
+ */
+static gboolean
+check_cnc_closed (GdaConnection *cnc, ThreadConnectionData *cdata)
+{
+ if (cdata->sub_connection_has_closed) {
+ gda_connection_close_no_warning (cnc);
+ return TRUE;
+ }
+ return FALSE;
+}
/*
* Get provider name request
@@ -516,13 +530,31 @@ sub_cnc_transaction_status_changed_cb (GdaThreadWrapper *wrapper, GdaConnection
}
static void
+sub_cnc_closed_cb (GdaThreadWrapper *wrapper, GdaConnection *sub_cnc, const gchar *signal,
+ gint n_param_values, const GValue *param_values,
+ gpointer gda_reserved, GdaConnection *wrapper_cnc)
+{
+ ThreadConnectionData *cdata;
+ cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (wrapper_cnc);
+ if (!cdata)
+ return;
+ cdata->sub_connection_has_closed = TRUE;
+}
+
+static void
setup_signals (GdaConnection *cnc, ThreadConnectionData *cdata)
{
gulong hid;
hid = gda_thread_wrapper_connect_raw (cdata->wrapper, cdata->sub_connection,
+ "conn-closed", FALSE,
+ (GdaThreadWrapperCallback) sub_cnc_closed_cb, cnc);
+ g_array_prepend_val (cdata->handlers_ids, hid);
+
+ hid = gda_thread_wrapper_connect_raw (cdata->wrapper, cdata->sub_connection,
"error", FALSE,
(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", FALSE,
(GdaThreadWrapperCallback) sub_cnc_transaction_status_changed_cb,
@@ -1556,7 +1588,13 @@ gda_thread_provider_statement_execute (GdaServerProvider *provider, GdaConnectio
cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
if (!cdata)
- return FALSE;
+ return NULL;
+
+ if (check_cnc_closed (cnc, cdata)) {
+ g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_CLOSED_ERROR,
+ _("Connection is closed"));
+ return NULL;
+ }
if (async_cb) {
ExecuteStatementData *wdata;
@@ -1624,6 +1662,9 @@ gda_thread_provider_handle_async (GdaServerProvider *provider, GdaConnection *cn
if (!cdata)
return FALSE;
+ if (check_cnc_closed (cnc, cdata))
+ return TRUE;
+
if (!cdata->async_tasks)
return TRUE;
diff --git a/libgda/thread-wrapper/gda-thread-wrapper.c b/libgda/thread-wrapper/gda-thread-wrapper.c
index 3c84034..bcbb675 100644
--- a/libgda/thread-wrapper/gda-thread-wrapper.c
+++ b/libgda/thread-wrapper/gda-thread-wrapper.c
@@ -759,16 +759,16 @@ gda_thread_wrapper_iterate (GdaThreadWrapper *wrapper, gboolean may_block)
/* run callback now */
SignalSpec *spec = job->u.signal.spec;
- signal_spec_lock (spec);
if (spec->callback)
spec->callback (wrapper, spec->instance, ((GSignalQuery*)spec)->signal_name,
job->u.signal.n_param_values, job->u.signal.param_values, NULL,
spec->data);
else
g_print ("Not propagating signal %s\n", ((GSignalQuery*)spec)->signal_name);
- signal_spec_unref (spec);
job->u.signal.spec = NULL;
job_free (job);
+ signal_spec_lock (spec);
+ signal_spec_unref (spec);
do_again = TRUE;
}
else
@@ -1119,7 +1119,7 @@ gda_thread_wrapper_disconnect (GdaThreadWrapper *wrapper, gulong id)
sigspec->instance = NULL;
sigspec->signal_id = 0;
g_async_queue_unref (sigspec->reply_queue);
- sigspec->reply_queue = 0;
+ sigspec->reply_queue = NULL;
sigspec->callback = NULL;
sigspec->data = NULL;
signal_spec_unref (sigspec);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 665bbc3..970febd 100755
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -164,7 +164,6 @@ providers/oracle/oracle_specs_create_table.xml.in
providers/postgres/gda-postgres-blob-op.c
providers/postgres/gda-postgres-ddl.c
providers/postgres/gda-postgres-handler-bin.c
-providers/postgres/gda-postgres-meta.c
providers/postgres/gda-postgres-provider.c
providers/postgres/gda-postgres-pstmt.c
providers/postgres/gda-postgres-recordset.c
@@ -182,6 +181,7 @@ providers/postgres/postgres_specs_drop_table.xml.in
providers/postgres/postgres_specs_drop_view.xml.in
providers/postgres/postgres_specs_dsn.xml.in
providers/postgres/postgres_specs_rename_table.xml.in
+providers/reuseable/postgres/gda-postgres-meta.c
providers/sqlite/libmain.c
providers/sqlite/sqlite_specs_add_column.xml.in
providers/sqlite/sqlite_specs_create_db.xml.in
@@ -194,6 +194,15 @@ providers/sqlite/sqlite_specs_drop_table.xml.in
providers/sqlite/sqlite_specs_drop_view.xml.in
providers/sqlite/sqlite_specs_dsn.xml.in
providers/sqlite/sqlite_specs_rename_table.xml.in
+providers/web/gda-web-ddl.c
+providers/web/gda-web-provider.c
+providers/web/gda-web-recordset.c
+providers/web/gda-web-util.c
+providers/web/libmain.c
+providers/web/gda-web-meta.c
+providers/web/gda-web-provider.c
+providers/web/web_specs_auth.xml.in
+providers/web/web_specs_dsn.xml.in
testing/gda-test-blob.c
testing/gda-test-connection.c
testing/gdaui-test-data-entries.c
diff --git a/providers/Makefile.am b/providers/Makefile.am
index cf5c18f..62967c7 100644
--- a/providers/Makefile.am
+++ b/providers/Makefile.am
@@ -54,7 +54,12 @@ if JAVA
GDA_JAVA_SERVER=jdbc
endif
+if LIBSOUP
+GDA_WEB_SERVER=web
+endif
+
SUBDIRS = \
+ reuseable \
sqlite \
skel-implementation \
$(GDA_BDB_SERVER) \
@@ -62,7 +67,8 @@ SUBDIRS = \
$(GDA_POSTGRES_SERVER) \
$(GDA_MYSQL_SERVER) \
$(GDA_JAVA_SERVER) \
- $(GDA_ORACLE_SERVER)
+ $(GDA_ORACLE_SERVER) \
+ $(GDA_WEB_SERVER)
# $(GDA_FREETDS_SERVER) \
# $(GDA_IBMDB2_SERVER) \
# $(GDA_FIREBIRD_SERVER) \
diff --git a/providers/postgres/Makefile.am b/providers/postgres/Makefile.am
index 52acf67..da6a176 100644
--- a/providers/postgres/Makefile.am
+++ b/providers/postgres/Makefile.am
@@ -5,31 +5,9 @@ AM_CPPFLAGS = \
-I$(top_srcdir) \
-I$(top_srcdir)/libgda \
-I$(top_builddir) \
+ -I$(top_srcdir)/providers/reuseable/postgres \
$(LIBGDA_CFLAGS) $(POSTGRES_CFLAGS)
-keyword_files=keywords_82.list keywords_83.list keywords_84.list
-pkeyword_files=$(addprefix $(top_srcdir)/providers/postgres/,$(keyword_files))
-
-mkkeywordhash$(EXEEXT_FOR_BUILD): $(top_srcdir)/libgda/sqlite/mkkeywordhash.c
- $(CC_FOR_BUILD) -g -o mkkeywordhash$(EXEEXT_FOR_BUILD) $(GDA_DEBUG_FLAGS) $<
-
-keywords_hash.c: mkkeywordhash$(EXEEXT_FOR_BUILD) $(pkeyword_files)
- ./mkkeywordhash$(EXEEXT_FOR_BUILD) $(top_srcdir)/providers/postgres/keywords_82.list V82 > keywords_hash.c
- ./mkkeywordhash$(EXEEXT_FOR_BUILD) $(top_srcdir)/providers/postgres/keywords_83.list V83 >> keywords_hash.c
- ./mkkeywordhash$(EXEEXT_FOR_BUILD) $(top_srcdir)/providers/postgres/keywords_84.list V84 >> keywords_hash.c
-
-# parser generation
-parser.c parser.h: parser.y $(top_builddir)/libgda/sql-parser/lemon$(EXEEXT_FOR_BUILD)
- - $(top_builddir)/libgda/sql-parser/lemon$(EXEEXT_FOR_BUILD) -q -d $(srcdir)/parser.y $(top_srcdir)/libgda/sql-parser/lempar.c
-
-gen_def$(EXEEXT_FOR_BUILD): gen_def.c
- $(CC_FOR_BUILD) -o gen_def$(EXEEXT_FOR_BUILD) -DIMPOSED_HEADER=\""$(top_builddir)/libgda/sql-parser/token_types.h"\" $(srcdir)/gen_def.c
-
-postgres_token_types.h: gen_def$(EXEEXT_FOR_BUILD) parser.h
- ./gen_def$(EXEEXT_FOR_BUILD) > postgres_token_types.h
-
-$(OBJECTS) $(libgda_postgres_la_OBJECTS): postgres_token_types.h keywords_hash.c
-
libgda_postgres_la_SOURCES = \
gda-postgres-blob-op.c \
gda-postgres-blob-op.h \
@@ -37,27 +15,21 @@ libgda_postgres_la_SOURCES = \
gda-postgres-ddl.h \
gda-postgres-handler-bin.c \
gda-postgres-handler-bin.h \
- gda-postgres-parser.c \
- gda-postgres-parser.h \
gda-postgres-provider.c \
gda-postgres-provider.h \
gda-postgres-pstmt.c \
gda-postgres-pstmt.h \
- gda-postgres-meta.c \
- gda-postgres-meta.h \
gda-postgres-recordset.c \
gda-postgres-recordset.h \
gda-postgres-util.c \
gda-postgres-util.h \
gda-postgres.h \
- libmain.c \
- parser.h \
- parser.c \
- postgres_token_types.h
+ libmain.c
libgda_postgres_la_LDFLAGS = -export-dynamic -module -avoid-version $(NO_UNDEFINED) $(LIBTOOL_PROV_EXPORT_OPTIONS)
libgda_postgres_la_LIBADD = \
$(top_builddir)/libgda/libgda-4.0.la \
+ $(top_builddir)/providers/reuseable/postgres/libgda-postgres.la \
$(LIBGDA_LIBS) $(POSTGRES_LIBS)
xmldir = $(datadir)/libgda-4.0
@@ -82,8 +54,5 @@ xml_DATA = $(xml_in_files:.xml.in=.xml)
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libgda-postgres-4.0.pc
-EXTRA_DIST = $(xml_in_files) libgda-postgres-4.0.pc.in parser.y gen_def.c $(keyword_files)
+EXTRA_DIST = $(xml_in_files) libgda-postgres-4.0.pc.in
DISTCLEANFILES = $(xml_DATA)
-
-CLEANFILES = parser.h parser.c parser.out postgres_token_types.h gen_def$(EXEEXT_FOR_BUILD) \
- mkkeywordhash$(EXEEXT_FOR_BUILD) keywords_hash.c
diff --git a/providers/postgres/gda-postgres-provider.c b/providers/postgres/gda-postgres-provider.c
index a58737b..73de91b 100644
--- a/providers/postgres/gda-postgres-provider.c
+++ b/providers/postgres/gda-postgres-provider.c
@@ -392,185 +392,6 @@ pq_notice_processor (PostgresConnectionData *cdata, const char *message)
gda_connection_add_event (cdata->cnc, error);
}
-static GType
-postgres_name_to_g_type (const gchar *name, const gchar *conv_func_name)
-{
- /* default built in data types */
- if (!strcmp (name, "bool"))
- return G_TYPE_BOOLEAN;
- else if (!strcmp (name, "int8"))
- return G_TYPE_INT64;
- else if (!strcmp (name, "int4") || !strcmp (name, "abstime"))
- return G_TYPE_INT;
- else if (!strcmp (name, "int2"))
- return GDA_TYPE_SHORT;
- else if (!strcmp (name, "float4"))
- return G_TYPE_FLOAT;
- else if (!strcmp (name, "float8"))
- return G_TYPE_DOUBLE;
- else if (!strcmp (name, "numeric"))
- return GDA_TYPE_NUMERIC;
- else if (!strncmp (name, "timestamp", 9))
- return GDA_TYPE_TIMESTAMP;
- else if (!strcmp (name, "date"))
- return G_TYPE_DATE;
- else if (!strncmp (name, "time", 4))
- return GDA_TYPE_TIME;
- else if (!strcmp (name, "point"))
- return GDA_TYPE_GEOMETRIC_POINT;
- else if (!strcmp (name, "oid"))
- return GDA_TYPE_BLOB;
- else if (!strcmp (name, "bytea"))
- return GDA_TYPE_BINARY;
-
- /* other data types, using the conversion function name as a hint */
- if (!conv_func_name)
- return G_TYPE_STRING;
-
- if (!strncmp (conv_func_name, "int2", 4))
- return GDA_TYPE_SHORT;
- if (!strncmp (conv_func_name, "int4", 4))
- return G_TYPE_INT;
- if (!strncmp (conv_func_name, "int8", 4))
- return G_TYPE_INT64;
- if (!strncmp (conv_func_name, "float4", 6))
- return G_TYPE_FLOAT;
- if (!strncmp (conv_func_name, "float8", 6))
- return G_TYPE_DOUBLE;
- if (!strncmp (conv_func_name, "timestamp", 9))
- return GDA_TYPE_TIMESTAMP;
- if (!strncmp (conv_func_name, "time", 4))
- return GDA_TYPE_TIME;
- if (!strncmp (conv_func_name, "date", 4))
- return G_TYPE_DATE;
- if (!strncmp (conv_func_name, "bool", 4))
- return G_TYPE_BOOLEAN;
- if (!strncmp (conv_func_name, "oid", 3))
- return GDA_TYPE_BLOB;
- if (!strncmp (conv_func_name, "bytea", 5))
- return GDA_TYPE_BINARY;
- return G_TYPE_STRING;
-}
-
-static int
-get_connection_type_list (PostgresConnectionData *cdata)
-{
- GHashTable *h_table;
- GdaPostgresTypeOid *td;
- PGresult *pg_res, *pg_res_avoid, *pg_res_anyoid = NULL;
- gint nrows, i;
- gchar *avoid_types = NULL;
- GString *string;
-
- if (cdata->version_float < 7.3) {
- gchar *query;
- avoid_types = "'SET', 'cid', 'oid', 'int2vector', 'oidvector', 'regproc', 'smgr', 'tid', 'unknown', 'xid'";
- /* main query to fetch infos about the data types */
- query = g_strdup_printf ("SELECT pg_type.oid, typname, usename, obj_description(pg_type.oid) "
- "FROM pg_type, pg_user "
- "WHERE typowner=usesysid AND typrelid = 0 AND typname !~ '^_' "
- "AND typname not in (%s) "
- "ORDER BY typname", avoid_types);
- pg_res = _gda_postgres_PQexec_wrap (cdata->cnc, cdata->pconn, query);
- g_free (query);
-
- /* query to fetch non returned data types */
- query = g_strdup_printf ("SELECT pg_type.oid FROM pg_type WHERE typname in (%s)", avoid_types);
- pg_res_avoid = _gda_postgres_PQexec_wrap (cdata->cnc, cdata->pconn, query);
- g_free (query);
- }
- else {
- gchar *query;
- avoid_types = "'any', 'anyarray', 'anyelement', 'cid', 'cstring', 'int2vector', 'internal', 'language_handler', 'oidvector', 'opaque', 'record', 'refcursor', 'regclass', 'regoper', 'regoperator', 'regproc', 'regprocedure', 'regtype', 'SET', 'smgr', 'tid', 'trigger', 'unknown', 'void', 'xid'";
-
- /* main query to fetch infos about the data types */
- query = g_strdup_printf (
- "SELECT t.oid, t.typname, u.usename, pg_catalog.obj_description(t.oid), t.typinput "
- "FROM pg_catalog.pg_type t LEFT JOIN pg_catalog.pg_user u ON (t.typowner=u.usesysid), pg_catalog.pg_namespace n "
- "WHERE n.oid = t.typnamespace "
- "AND pg_catalog.pg_type_is_visible(t.oid) "
- /*--AND (n.nspname = 'public' OR n.nspname = 'pg_catalog')*/
- "AND typname !~ '^_' "
- "AND (t.typrelid = 0 OR "
- "(SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) "
- "AND t.typname not in (%s) "
- "ORDER BY typname", avoid_types);
- pg_res = _gda_postgres_PQexec_wrap (cdata->cnc, cdata->pconn, query);
- g_free (query);
-
- /* query to fetch non returned data types */
- query = g_strdup_printf ("SELECT t.oid FROM pg_catalog.pg_type t WHERE t.typname in (%s)",
- avoid_types);
- pg_res_avoid = _gda_postgres_PQexec_wrap (cdata->cnc, cdata->pconn, query);
- g_free (query);
-
- /* query to fetch the oid of the 'any' data type */
- pg_res_anyoid = _gda_postgres_PQexec_wrap (cdata->cnc, cdata->pconn,
- "SELECT t.oid FROM pg_catalog.pg_type t WHERE t.typname = 'any'");
- }
-
- if (!pg_res || (PQresultStatus (pg_res) != PGRES_TUPLES_OK) ||
- !pg_res_avoid || (PQresultStatus (pg_res_avoid) != PGRES_TUPLES_OK) ||
- ((cdata->version_float >= 7.3) &&
- (!pg_res_anyoid || (PQresultStatus (pg_res_anyoid) != PGRES_TUPLES_OK)))) {
- if (pg_res)
- PQclear (pg_res);
- if (pg_res_avoid)
- PQclear (pg_res_avoid);
- if (pg_res_anyoid)
- PQclear (pg_res_anyoid);
- return -1;
- }
-
- /* Data types returned */
- nrows = PQntuples (pg_res);
- td = g_new (GdaPostgresTypeOid, nrows);
- h_table = g_hash_table_new (g_direct_hash, g_direct_equal);
- if (nrows == 0)
- g_warning ("PostgreSQL provider did not find any data type (expect some mis-behaviours) please report the error to bugzilla.gnome.org");
- for (i = 0; i < nrows; i++) {
- gchar *conv_func_name = NULL;
- if (PQnfields (pg_res) >= 5)
- conv_func_name = PQgetvalue (pg_res, i, 4);
- td[i].name = g_strdup (PQgetvalue (pg_res, i, 1));
- td[i].oid = atoi (PQgetvalue (pg_res, i, 0));
- td[i].type = postgres_name_to_g_type (td[i].name, conv_func_name);
- td[i].comments = g_strdup (PQgetvalue (pg_res, i, 3));
- td[i].owner = g_strdup (PQgetvalue (pg_res, i, 2));
- g_hash_table_insert (h_table, GUINT_TO_POINTER (td[i].oid), &(td[i].type));
- }
-
- PQclear (pg_res);
- cdata->ntypes = nrows;
- cdata->type_data = td;
- cdata->h_table = h_table;
-
- /* Make a string of data types internal to postgres and not returned, for future queries */
- string = NULL;
- nrows = PQntuples (pg_res_avoid);
- for (i = 0; i < nrows; i++) {
- if (!string)
- string = g_string_new (PQgetvalue (pg_res_avoid, i, 0));
- else
- g_string_append_printf (string, ", %s", PQgetvalue (pg_res_avoid, i, 0));
- }
- PQclear (pg_res_avoid);
- cdata->avoid_types = avoid_types;
- cdata->avoid_types_oids = string->str;
- g_string_free (string, FALSE);
-
- /* make a string of the oid of type 'any' */
- cdata->any_type_oid = g_strdup("");
- if (pg_res_anyoid) {
- if (PQntuples (pg_res_anyoid) == 1) {
- g_free(cdata->any_type_oid);
- cdata->any_type_oid = g_strdup (PQgetvalue (pg_res_anyoid, 0, 0));
- }
- PQclear (pg_res_anyoid);
- }
- return 0;
-}
-
/*
* Open connection request
*
@@ -597,7 +418,6 @@ gda_postgres_provider_open_connection (GdaServerProvider *provider, GdaConnectio
}
/* Check for connection parameters */
- /* TO_ADD: your own connection parameters */
const gchar *pq_host;
const gchar *pq_db;
const gchar *pg_searchpath;
@@ -707,9 +527,6 @@ gda_postgres_provider_open_connection (GdaServerProvider *provider, GdaConnectio
return FALSE;
}
cdata->version = g_strdup (PQgetvalue(pg_res, 0, 0));
- cdata->version_float = get_pg_version_float (cdata->version);
- cdata->short_version = g_strdup_printf ("%d%d", (int) cdata->version_float,
- (int) ((cdata->version_float - (int) cdata->version_float) * 10));
PQclear (pg_res);
/*
@@ -732,10 +549,22 @@ gda_postgres_provider_open_connection (GdaServerProvider *provider, GdaConnectio
}
PQclear (pg_res);
+ /* attach connection data */
+ gda_connection_internal_set_provider_data (cnc, cdata, (GDestroyNotify) gda_postgres_free_cnc_data);
+
+ /* handle LibPQ's notices */
+ PQsetNoticeProcessor (pconn, (PQnoticeProcessor) pq_notice_processor, cdata);
+
+ /* handle the reuseable part */
+ GdaProviderReuseableOperations *ops;
+ ops = _gda_postgres_reuseable_get_ops ();
+ cdata->reuseable = (GdaPostgresReuseable*) ops->re_new_data (NULL, NULL);
+ _gda_postgres_compute_types (cnc, cdata->reuseable);
+
/*
* Set the search_path
*/
- if ((cdata->version_float >= 7.3) && pg_searchpath) {
+ if ((cdata->reuseable->version_float >= 7.3) && pg_searchpath) {
const gchar *ptr;
gboolean path_valid = TRUE;
@@ -755,6 +584,7 @@ gda_postgres_provider_open_connection (GdaServerProvider *provider, GdaConnectio
gda_connection_add_event_string (cnc, _("Could not set search_path to %s"), pg_searchpath);
PQclear (pg_res);
gda_postgres_free_cnc_data (cdata);
+ gda_connection_internal_set_provider_data (cnc, NULL, NULL);
return FALSE;
}
PQclear (pg_res);
@@ -762,23 +592,11 @@ gda_postgres_provider_open_connection (GdaServerProvider *provider, GdaConnectio
else {
gda_connection_add_event_string (cnc, _("Search path %s is invalid"), pg_searchpath);
gda_postgres_free_cnc_data (cdata);
+ gda_connection_internal_set_provider_data (cnc, NULL, NULL);
return FALSE;
}
}
- /* attach connection data */
- gda_connection_internal_set_provider_data (cnc, cdata, (GDestroyNotify) gda_postgres_free_cnc_data);
-
-
- /* handle LibPQ's notices */
- PQsetNoticeProcessor (pconn, (PQnoticeProcessor) pq_notice_processor, cdata);
-
- if (get_connection_type_list (cdata) != 0) {
- _gda_postgres_make_error (cnc, pconn, NULL, NULL);
- gda_postgres_free_cnc_data (cdata);
- return FALSE;
- }
-
return TRUE;
}
@@ -1150,9 +968,9 @@ gda_postgres_provider_begin_transaction (GdaServerProvider *provider, GdaConnect
gchar *isolation_level = NULL;
GdaStatement *stmt = NULL;
- if (cdata->version_float >= 6.5){
+ if (cdata->reuseable->version_float >= 6.5){
if (gda_connection_get_options (cnc) & GDA_CONNECTION_OPTIONS_READ_ONLY) {
- if (cdata->version_float >= 7.4)
+ if (cdata->reuseable->version_float >= 7.4)
write_option = "READ ONLY";
else {
g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR,
@@ -1444,7 +1262,7 @@ gda_postgres_provider_supports_feature (GdaServerProvider *provider, GdaConnecti
cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
if (!cdata)
return FALSE;
- if (cdata->version_float >= 7.3)
+ if (cdata->reuseable->version_float >= 7.3)
return TRUE;
}
else
@@ -2578,7 +2396,8 @@ gda_postgresql_identifier_quote (GdaServerProvider *provider, GdaConnection *cnc
return NULL;
}
- kwfunc = _gda_postgres_get_reserved_keyword_func (cdata);
+ kwfunc = _gda_postgres_reuseable_get_reserved_keywords_func
+ (cdata ? (GdaProviderReuseable*) cdata->reuseable : NULL);
if (for_meta_store) {
gchar *tmp, *ptr;
@@ -2653,23 +2472,13 @@ gda_postgres_free_cnc_data (PostgresConnectionData *cdata)
if (cdata->pconn)
PQfinish (cdata->pconn);
- if (cdata->type_data) {
- gint i;
- for (i = 0; i < cdata->ntypes; i++) {
- g_free (cdata->type_data[i].name);
- g_free (cdata->type_data[i].comments);
- g_free (cdata->type_data[i].owner);
- }
- g_free (cdata->type_data);
- }
-
- if (cdata->h_table)
- g_hash_table_destroy (cdata->h_table);
-
g_free (cdata->version);
- g_free (cdata->short_version);
- g_free (cdata->avoid_types_oids);
- g_free (cdata->any_type_oid);
+
+ if (cdata->reuseable) {
+ GdaProviderReuseable *rdata = (GdaProviderReuseable*)cdata->reuseable;
+ rdata->operations->re_reset_data (rdata);
+ g_free (cdata->reuseable);
+ }
g_free (cdata);
}
diff --git a/providers/postgres/gda-postgres-recordset.c b/providers/postgres/gda-postgres-recordset.c
index 088ebb9..5843988 100644
--- a/providers/postgres/gda-postgres-recordset.c
+++ b/providers/postgres/gda-postgres-recordset.c
@@ -293,7 +293,7 @@ finish_prep_stmt_init (PostgresConnectionData *cdata, GdaPostgresPStmt *ps, PGre
postgres_type = PQftype (pg_res, i);
gtype = _GDA_PSTMT (ps)->types [i];
if (gtype == 0) {
- gtype = _gda_postgres_type_oid_to_gda (cdata, postgres_type);
+ gtype = _gda_postgres_type_oid_to_gda (cdata->cnc, cdata->reuseable, postgres_type);
_GDA_PSTMT (ps)->types [i] = gtype;
}
_GDA_PSTMT (ps)->types [i] = gtype;
diff --git a/providers/postgres/gda-postgres-util.c b/providers/postgres/gda-postgres-util.c
index 4190177..b558a2e 100644
--- a/providers/postgres/gda-postgres-util.c
+++ b/providers/postgres/gda-postgres-util.c
@@ -25,8 +25,6 @@
#include <glib/gi18n-lib.h>
#include "gda-postgres-util.h"
-#include <libgda/sqlite/keywords_hash.h>
-#include "keywords_hash.c" /* this one is dynamically generated */
static GdaConnectionEventCode
gda_postgres_sqlsate_to_gda_code (const gchar *sqlstate)
@@ -125,45 +123,3 @@ _gda_postgres_PQexec_wrap (GdaConnection *cnc, PGconn *pconn, const char *query)
return PQexec (pconn, query);
}
-
-GType
-_gda_postgres_type_oid_to_gda (PostgresConnectionData *cdata, Oid postgres_type)
-{
- GType *type;
- type = g_hash_table_lookup (cdata->h_table, GUINT_TO_POINTER (postgres_type));
- if (type)
- return *type;
- else
- return G_TYPE_STRING;
-}
-
-#ifdef GDA_DEBUG
-void
-_gda_postgres_test_keywords (void)
-{
- V82test_keywords();
- V83test_keywords();
- V84test_keywords();
-}
-#endif
-
-GdaSqlReservedKeywordsFunc
-_gda_postgres_get_reserved_keyword_func (PostgresConnectionData *cdata)
-{
- if (cdata && cdata->short_version) {
- switch (*cdata->short_version) {
- case '8':
- if (cdata->short_version[1] == '2')
- return V82is_keyword;
- if (cdata->short_version[1] == '3')
- return V83is_keyword;
- if (cdata->short_version[1] == '4')
- return V84is_keyword;
- return V84is_keyword;
- default:
- return V84is_keyword;
- break;
- }
- }
- return V84is_keyword;
-}
diff --git a/providers/postgres/gda-postgres-util.h b/providers/postgres/gda-postgres-util.h
index e961f12..1ecd1f9 100644
--- a/providers/postgres/gda-postgres-util.h
+++ b/providers/postgres/gda-postgres-util.h
@@ -1,5 +1,5 @@
/* GDA postgres provider
- * Copyright (C) 1998 - 2008 The GNOME Foundation.
+ * Copyright (C) 1998 - 2009 The GNOME Foundation.
*
* AUTHORS:
* Vivien Malerba <malerba gnome-db org>
@@ -32,12 +32,6 @@ GdaConnectionEvent *_gda_postgres_make_error (GdaConnection *cnc,
PGresult *_gda_postgres_PQexec_wrap (GdaConnection *cnc, PGconn *pconn, const char *query);
int _gda_postgres_get_connection_type_list (GdaConnection *cnc, PostgresConnectionData *cdata);
-GType _gda_postgres_type_oid_to_gda (PostgresConnectionData *cdata, Oid postgres_type);
-
-#ifdef GDA_DEBUG
-void _gda_postgres_test_keywords (void);
-#endif
-GdaSqlReservedKeywordsFunc _gda_postgres_get_reserved_keyword_func (PostgresConnectionData *cdata);
G_END_DECLS
diff --git a/providers/postgres/gda-postgres.h b/providers/postgres/gda-postgres.h
index dbaf7d6..fc70a11 100644
--- a/providers/postgres/gda-postgres.h
+++ b/providers/postgres/gda-postgres.h
@@ -33,6 +33,7 @@
#include <libgda/libgda.h>
#include <libpq-fe.h>
#include <libpq/libpq-fs.h>
+#include <gda-postgres-reuseable.h>
/*
* Postgres type identification
@@ -49,22 +50,14 @@ typedef struct {
* Provider's specific connection data
*/
typedef struct {
- GdaConnection *cnc;
- PGconn *pconn;
- gboolean pconn_is_busy;
- gint ntypes;
- GdaPostgresTypeOid *type_data;
- GHashTable *h_table;
+ GdaPostgresReuseable *reuseable;
+ GdaConnection *cnc;
+ PGconn *pconn;
+ gboolean pconn_is_busy;
/* Version of the backend to which we are connected */
- gchar *version;
- gfloat version_float;
- gchar *short_version;
-
- /* Internal data types not returned */
- gchar *avoid_types;
- gchar *avoid_types_oids;
- gchar *any_type_oid; /* oid for the 'any' data type, used to fetch aggregates and functions */
+ gchar *version;
+ gchar *short_version;
} PostgresConnectionData;
#endif
diff --git a/providers/reuseable/Makefile.am b/providers/reuseable/Makefile.am
new file mode 100644
index 0000000..6ce70fe
--- /dev/null
+++ b/providers/reuseable/Makefile.am
@@ -0,0 +1,19 @@
+noinst_LTLIBRARIES = libreuseable.la
+
+SUBDIRS = \
+ postgres
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libgda \
+ -I$(top_srcdir)/libgda/sqlite \
+ -I$(top_builddir) \
+ $(LIBGDA_CFLAGS)
+
+libreuseable_la_SOURCES = \
+ gda-provider-reuseable.h \
+ reuse-all.c \
+ reuse-all.h
+
+libreuseable_la_LIBADD = \
+ postgres/libgda-postgres.la
diff --git a/providers/reuseable/gda-provider-reuseable.h b/providers/reuseable/gda-provider-reuseable.h
new file mode 100644
index 0000000..e279a43
--- /dev/null
+++ b/providers/reuseable/gda-provider-reuseable.h
@@ -0,0 +1,69 @@
+/* GDA provider
+ * 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; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GDA_PROVIDER_REUSEABLE_H__
+#define __GDA_PROVIDER_REUSEABLE_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libgda/gda-decl.h>
+#include <libgda/gda-server-provider.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GdaProviderReuseable GdaProviderReuseable;
+
+typedef GdaProviderReuseable *(*GdaProviderReuseable_NewDataFunc) (const gchar *major, const gchar *minor);
+typedef void (*GdaProviderReuseable_ResetDataFunc) (GdaProviderReuseable *rdata);
+typedef GType (*GdaProviderReuseable_GetGTypeFunc) (GdaConnection *cnc, GdaProviderReuseable *rdata, const gchar *db_type);
+typedef GdaSqlReservedKeywordsFunc (*GdaProviderReuseable_GetReservedKeywordsFunc) (GdaProviderReuseable *rdata);
+typedef GdaSqlParser *(*GdaProviderReuseable_CreateParserFunc) (GdaProviderReuseable *rdata);
+
+/**
+ * GdaProviderReuseable: Reuseable provider part
+ *
+ * Defines the interface which a database provider has to implement to be reused
+ */
+typedef struct {
+ GdaProviderReuseable_NewDataFunc re_new_data;
+ GdaProviderReuseable_ResetDataFunc re_reset_data;
+
+ GdaProviderReuseable_GetGTypeFunc re_get_type; /* Returns GDA_TYPE_NULL if conversion failed */
+ GdaProviderReuseable_GetReservedKeywordsFunc re_get_reserved_keyword_func;
+
+ GdaProviderReuseable_CreateParserFunc re_create_parser;
+ GdaServerProviderMeta re_meta_funcs;
+} GdaProviderReuseableOperations;
+
+/*
+ * Declaration for provider's abstract operations
+ */
+struct _GdaProviderReuseable {
+ GdaProviderReuseableOperations *operations;
+ gchar *version_major;
+ gchar *version_minor;
+};
+
+
+G_END_DECLS
+
+#endif
+
diff --git a/providers/reuseable/postgres/Makefile.am b/providers/reuseable/postgres/Makefile.am
new file mode 100644
index 0000000..88e3132
--- /dev/null
+++ b/providers/reuseable/postgres/Makefile.am
@@ -0,0 +1,49 @@
+noinst_LTLIBRARIES = libgda-postgres.la
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libgda \
+ -I$(top_builddir) \
+ $(LIBGDA_CFLAGS) $(POSTGRES_CFLAGS)
+
+keyword_files=keywords_82.list keywords_83.list keywords_84.list
+pkeyword_files=$(addprefix $(top_srcdir)/providers/reuseable/postgres/,$(keyword_files))
+
+mkkeywordhash$(EXEEXT_FOR_BUILD): $(top_srcdir)/libgda/sqlite/mkkeywordhash.c
+ $(CC_FOR_BUILD) -g -o mkkeywordhash$(EXEEXT_FOR_BUILD) $(GDA_DEBUG_FLAGS) $<
+
+keywords_hash.c: mkkeywordhash$(EXEEXT_FOR_BUILD) $(pkeyword_files)
+ ./mkkeywordhash$(EXEEXT_FOR_BUILD) $(top_srcdir)/providers/reuseable/postgres/keywords_82.list V82 > keywords_hash.c
+ ./mkkeywordhash$(EXEEXT_FOR_BUILD) $(top_srcdir)/providers/reuseable/postgres/keywords_83.list V83 >> keywords_hash.c
+ ./mkkeywordhash$(EXEEXT_FOR_BUILD) $(top_srcdir)/providers/reuseable/postgres/keywords_84.list V84 >> keywords_hash.c
+
+# parser generation
+parser.c parser.h: parser.y $(top_builddir)/libgda/sql-parser/lemon$(EXEEXT_FOR_BUILD)
+ - $(top_builddir)/libgda/sql-parser/lemon$(EXEEXT_FOR_BUILD) -q -d $(srcdir)/parser.y $(top_srcdir)/libgda/sql-parser/lempar.c
+
+gen_def$(EXEEXT_FOR_BUILD): gen_def.c
+ $(CC_FOR_BUILD) -o gen_def$(EXEEXT_FOR_BUILD) -DIMPOSED_HEADER=\""$(top_builddir)/libgda/sql-parser/token_types.h"\" $(srcdir)/gen_def.c
+
+postgres_token_types.h: gen_def$(EXEEXT_FOR_BUILD) parser.h
+ ./gen_def$(EXEEXT_FOR_BUILD) > postgres_token_types.h
+
+$(OBJECTS) $(libgda_postgres_la_OBJECTS): postgres_token_types.h keywords_hash.c
+
+libgda_postgres_la_SOURCES = \
+ gda-postgres-reuseable.c \
+ gda-postgres-reuseable.h \
+ gda-postgres-parser.c \
+ gda-postgres-parser.h \
+ gda-postgres-meta.c \
+ gda-postgres-meta.h \
+ parser.h \
+ parser.c \
+ postgres_token_types.h
+
+libgda_postgres_la_LIBADD = \
+ $(top_builddir)/libgda/libgda-4.0.la \
+ $(LIBGDA_LIBS) $(POSTGRES_LIBS)
+
+EXTRA_DIST = parser.y gen_def.c $(keyword_files)
+CLEANFILES = parser.h parser.c parser.out postgres_token_types.h gen_def$(EXEEXT_FOR_BUILD) \
+ mkkeywordhash$(EXEEXT_FOR_BUILD) keywords_hash.c
diff --git a/providers/reuseable/postgres/gda-postgres-meta.c b/providers/reuseable/postgres/gda-postgres-meta.c
new file mode 100644
index 0000000..eee8196
--- /dev/null
+++ b/providers/reuseable/postgres/gda-postgres-meta.c
@@ -0,0 +1,2042 @@
+/* GDA postgres 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 <string.h>
+#include "gda-postgres-meta.h"
+#include <libgda/libgda.h>
+#include <libgda/sql-parser/gda-sql-parser.h>
+#include <glib/gi18n-lib.h>
+#include <libgda/gda-server-provider-extra.h>
+#include <libgda/gda-connection-private.h>
+#include <libgda/providers-support/gda-meta-column-types.h>
+
+#include "gda-postgres-reuseable.h"
+#include "gda-postgres-parser.h"
+
+/*
+ * predefined statements' IDs
+ */
+typedef enum {
+ I_STMT_CATALOG,
+ I_STMT_BTYPES,
+ I_STMT_SCHEMAS,
+ I_STMT_SCHEMAS_ALL,
+ I_STMT_SCHEMA_NAMED,
+ I_STMT_TABLES,
+ I_STMT_TABLES_ALL,
+ I_STMT_TABLE_NAMED,
+ I_STMT_VIEWS,
+ I_STMT_VIEWS_ALL,
+ I_STMT_VIEW_NAMED,
+ I_STMT_COLUMNS_OF_TABLE,
+ I_STMT_COLUMNS_ALL,
+ I_STMT_TABLES_CONSTRAINTS,
+ I_STMT_TABLES_CONSTRAINTS_ALL,
+ I_STMT_TABLES_CONSTRAINT_NAMED,
+ I_STMT_REF_CONSTRAINTS,
+ I_STMT_REF_CONSTRAINTS_ALL,
+ I_STMT_KEY_COLUMN_USAGE,
+ I_STMT_KEY_COLUMN_USAGE_ALL,
+ I_STMT_CHECK_COLUMN_USAGE,
+ I_STMT_CHECK_COLUMN_USAGE_ALL,
+ I_STMT_UDT,
+ I_STMT_UDT_ALL,
+ I_STMT_UDT_COLUMNS,
+ I_STMT_UDT_COLUMNS_ALL,
+ I_STMT_DOMAINS,
+ I_STMT_DOMAINS_ALL,
+ I_STMT_DOMAINS_CONSTRAINTS,
+ I_STMT_DOMAINS_CONSTRAINTS_ALL,
+ I_STMT_VIEWS_COLUMNS,
+ I_STMT_VIEWS_COLUMNS_ALL,
+ I_STMT_TRIGGERS,
+ I_STMT_TRIGGERS_ALL,
+ I_STMT_EL_TYPES_COL,
+ I_STMT_EL_TYPES_DOM,
+ I_STMT_EL_TYPES_UDT,
+ I_STMT_EL_TYPES_ROUT_PAR,
+ I_STMT_EL_TYPES_ROUT_COL,
+ I_STMT_EL_TYPES_ALL,
+ I_STMT_ROUTINES_ALL,
+ I_STMT_ROUTINES,
+ I_STMT_ROUTINES_ONE,
+ I_STMT_ROUTINE_PAR_ALL,
+ I_STMT_ROUTINE_PAR,
+ I_STMT_ROUTINE_COL_ALL,
+ I_STMT_ROUTINE_COL
+} InternalStatementItem;
+
+
+/*
+ * predefined statements' SQL
+ */
+static gchar *internal_sql[] = {
+ /* I_STMT_CATALOG */
+ "SELECT pg_catalog.current_database()",
+
+ /* I_STMT_BTYPES */
+ "SELECT t.typname, 'pg_catalog.' || t.typname, 'gchararray', pg_catalog.obj_description(t.oid), NULL, CASE WHEN t.typname ~ '^_' THEN TRUE WHEN typtype = 'p' THEN TRUE WHEN t.typname in ('any', 'anyarray', 'anyelement', 'cid', 'cstring', 'int2vector', 'internal', 'language_handler', 'oidvector', 'opaque', 'record', 'refcursor', 'regclass', 'regoper', 'regoperator', 'regproc', 'regprocedure', 'regtype', 'SET', 'smgr', 'tid', 'trigger', 'unknown', 'void', 'xid', 'oid', 'aclitem') THEN TRUE ELSE FALSE END, CAST (t.oid AS varchar) FROM pg_catalog.pg_type t, pg_catalog.pg_user u, pg_catalog.pg_namespace n WHERE t.typowner=u.usesysid AND n.oid = t.typnamespace AND pg_catalog.pg_type_is_visible(t.oid) AND (typtype='b' OR typtype='p') AND typelem = 0",
+
+ /* I_STMT_SCHEMAS */
+ "SELECT current_database() AS catalog_name, n.nspname AS schema_name, u.rolname AS schema_owner, CASE WHEN n.nspname ~ '^pg_' THEN TRUE WHEN n.nspname = 'information_schema' THEN TRUE ELSE FALSE END FROM pg_namespace n, pg_roles u WHERE n.nspowner = u.oid AND current_database() = ##cat::string",
+
+ /* I_STMT_SCHEMAS_ALL */
+ "SELECT current_database() AS catalog_name, n.nspname AS schema_name, u.rolname AS schema_owner, CASE WHEN n.nspname ~ '^pg_' THEN TRUE WHEN n.nspname = 'information_schema' THEN TRUE ELSE FALSE END FROM pg_namespace n, pg_roles u WHERE n.nspowner = u.oid",
+
+ /* I_STMT_SCHEMA_NAMED */
+ "SELECT current_database() AS catalog_name, n.nspname AS schema_name, u.rolname AS schema_owner, CASE WHEN n.nspname ~ '^pg_' THEN TRUE WHEN n.nspname = 'information_schema' THEN TRUE ELSE FALSE END FROM pg_namespace n, pg_roles u WHERE n.nspowner = u.oid AND current_database() = ##cat::string AND n.nspname = ##name::string",
+
+ /* I_STMT_TABLES */
+ "SELECT current_database() AS table_catalog, nc.nspname AS table_schema, c.relname AS table_name, CASE WHEN nc.oid = pg_my_temp_schema() THEN 'LOCAL TEMPORARY' WHEN c.relkind = 'r' THEN 'BASE TABLE' WHEN c.relkind = 'v' THEN 'VIEW' ELSE NULL END AS table_type, CASE WHEN c.relkind = 'r' THEN TRUE ELSE FALSE END, pg_catalog.obj_description(c.oid), CASE WHEN pg_catalog.pg_table_is_visible(c.oid) IS TRUE AND nc.nspname!='pg_catalog' THEN c.relname ELSE coalesce (nc.nspname || '.', '') || c.relname END, coalesce (nc.nspname || '.', '') || c.relname, o.rolname FROM pg_namespace nc, pg_class c, pg_roles o WHERE current_database() = ##cat::string AND nc.nspname = ##schema::string AND c.relnamespace = nc.oid AND (c.relkind = ANY (ARRAY['r', 'v'])) AND NOT pg_is_other_temp_schema(nc.oid) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'DELETE') O
R has_table_privilege(c.oid, 'REFERENCES') OR has_table_privilege(c.oid, 'TRIGGER')) AND o.oid=c.relowner",
+
+ /* I_STMT_TABLES_ALL */
+ "SELECT current_database() AS table_catalog, nc.nspname AS table_schema, c.relname AS table_name, CASE WHEN nc.oid = pg_my_temp_schema() THEN 'LOCAL TEMPORARY' WHEN c.relkind = 'r' THEN 'BASE TABLE' WHEN c.relkind = 'v' THEN 'VIEW' ELSE NULL END AS table_type, CASE WHEN c.relkind = 'r' THEN TRUE ELSE FALSE END, pg_catalog.obj_description(c.oid), CASE WHEN pg_catalog.pg_table_is_visible(c.oid) IS TRUE AND nc.nspname!='pg_catalog' THEN c.relname ELSE coalesce (nc.nspname || '.', '') || c.relname END, coalesce (nc.nspname || '.', '') || c.relname, o.rolname FROM pg_namespace nc, pg_class c, pg_roles o WHERE c.relnamespace = nc.oid AND (c.relkind = ANY (ARRAY['r', 'v'])) AND NOT pg_is_other_temp_schema(nc.oid) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'DELETE') OR has_table_privilege(c.oid, 'REFERENCES') OR has_table_privilege(c.oid,
'TRIGGER')) AND o.oid=c.relowner",
+
+ /* I_STMT_TABLE_NAMED */
+ "SELECT current_database() AS table_catalog, nc.nspname AS table_schema, c.relname AS table_name, CASE WHEN nc.oid = pg_my_temp_schema() THEN 'LOCAL TEMPORARY' WHEN c.relkind = 'r' THEN 'BASE TABLE' WHEN c.relkind = 'v' THEN 'VIEW' ELSE NULL END AS table_type, CASE WHEN c.relkind = 'r' THEN TRUE ELSE FALSE END, pg_catalog.obj_description(c.oid), CASE WHEN pg_catalog.pg_table_is_visible(c.oid) IS TRUE AND nc.nspname!='pg_catalog' THEN c.relname ELSE coalesce (nc.nspname || '.', '') || c.relname END, coalesce (nc.nspname || '.', '') || c.relname, o.rolname FROM pg_namespace nc, pg_class c, pg_roles o WHERE current_database() = ##cat::string AND nc.nspname = ##schema::string AND c.relnamespace = nc.oid AND (c.relkind = ANY (ARRAY['r', 'v'])) AND NOT pg_is_other_temp_schema(nc.oid) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'DELETE') O
R has_table_privilege(c.oid, 'REFERENCES') OR has_table_privilege(c.oid, 'TRIGGER')) AND o.oid=c.relowner AND c.relname = ##name::string",
+
+ /* I_STMT_VIEWS */
+ "SELECT current_database() AS table_catalog, nc.nspname AS table_schema, c.relname AS table_name, pg_catalog.pg_get_viewdef(c.oid, TRUE), NULL, CASE WHEN c.relkind = 'r' THEN TRUE ELSE FALSE END FROM pg_namespace nc, pg_class c WHERE current_database() = ##cat::string AND nc.nspname = ##schema::string AND c.relnamespace = nc.oid AND c.relkind = 'v' AND NOT pg_is_other_temp_schema(nc.oid) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'DELETE') OR has_table_privilege(c.oid, 'REFERENCES') OR has_table_privilege(c.oid, 'TRIGGER'))",
+
+ /* I_STMT_VIEWS_ALL */
+ "SELECT current_database() AS table_catalog, nc.nspname AS table_schema, c.relname AS table_name, pg_catalog.pg_get_viewdef(c.oid, TRUE), NULL, CASE WHEN c.relkind = 'r' THEN TRUE ELSE FALSE END FROM pg_namespace nc, pg_class c WHERE c.relnamespace = nc.oid AND c.relkind = 'v' AND NOT pg_is_other_temp_schema(nc.oid) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'DELETE') OR has_table_privilege(c.oid, 'REFERENCES') OR has_table_privilege(c.oid, 'TRIGGER'))",
+
+ /* I_STMT_VIEW_NAMED */
+ "SELECT current_database() AS table_catalog, nc.nspname AS table_schema, c.relname AS table_name, pg_catalog.pg_get_viewdef(c.oid, TRUE), NULL, CASE WHEN c.relkind = 'r' THEN TRUE ELSE FALSE END FROM pg_namespace nc, pg_class c WHERE current_database() = ##cat::string AND nc.nspname = ##schema::string AND c.relnamespace = nc.oid AND c.relkind = 'v' AND NOT pg_is_other_temp_schema(nc.oid) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'DELETE') OR has_table_privilege(c.oid, 'REFERENCES') OR has_table_privilege(c.oid, 'TRIGGER')) AND c.relname = ##name::string",
+
+ /* I_STMT_COLUMNS_OF_TABLE */
+ "SELECT current_database(), nc.nspname, c.relname, a.attname, a.attnum, pg_get_expr(ad.adbin, ad.adrelid), CASE WHEN a.attnotnull OR t.typtype = 'd' AND t.typnotnull THEN FALSE ELSE TRUE END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN NULL ELSE coalesce (nt.nspname || '.', '') || t.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'COL' || current_database() || '.' || nc.nspname || '.' || c.relname || '.' || a.attnum ELSE NULL END, 'gchararray', information_schema._pg_char_max_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_char_octet_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_numeric_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_numeric_scale(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), info
rmation_schema._pg_datetime_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), NULL, NULL, NULL, NULL, NULL, NULL, CASE WHEN pg_get_expr(ad.adbin, ad.adrelid) LIKE 'nextval(%' THEN '" GDA_EXTRA_AUTO_INCREMENT "' ELSE NULL END, CASE WHEN c.relkind = 'r' THEN TRUE ELSE FALSE END, pg_catalog.col_description(c.oid, a.attnum), CAST (t.oid AS varchar) FROM pg_attribute a LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum, pg_class c, pg_namespace nc, pg_type t JOIN pg_namespace nt ON t.typnamespace = nt.oid LEFT JOIN (pg_type bt JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid) ON t.typtype = 'd' AND t.typbasetype = bt.oid WHERE current_database() = ##cat::string AND nc.nspname = ##schema::string AND c.relname = ##name::string AND a.attrelid = c.oid AND a.atttypid = t.oid AND nc.oid = c.relnamespace AND NOT pg_is_other_temp_schema(nc.oid) AND a.attnum > 0 AND NOT a.attisdropped AND (c.relkind = ANY (ARRAY['
r', 'v'])) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'REFERENCES'))",
+
+ /* I_STMT_COLUMNS_ALL */
+ "SELECT current_database(), nc.nspname, c.relname, a.attname, a.attnum, pg_get_expr(ad.adbin, ad.adrelid), CASE WHEN a.attnotnull OR t.typtype = 'd' AND t.typnotnull THEN FALSE ELSE TRUE END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN NULL ELSE coalesce (nt.nspname || '.', '') || t.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'COL' || current_database() || '.' || nc.nspname || '.' || c.relname || '.' || a.attnum ELSE NULL END, 'gchararray', information_schema._pg_char_max_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_char_octet_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_numeric_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_numeric_scale(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), info
rmation_schema._pg_datetime_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), NULL, NULL, NULL, NULL, NULL, NULL, CASE WHEN pg_get_expr(ad.adbin, ad.adrelid) LIKE 'nextval(%' THEN '" GDA_EXTRA_AUTO_INCREMENT "' ELSE NULL END, CASE WHEN c.relkind = 'r' THEN TRUE ELSE FALSE END, pg_catalog.col_description(c.oid, a.attnum), CAST (t.oid AS varchar) FROM pg_attribute a LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum, pg_class c, pg_namespace nc, pg_type t JOIN pg_namespace nt ON t.typnamespace = nt.oid LEFT JOIN (pg_type bt JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid) ON t.typtype = 'd' AND t.typbasetype = bt.oid WHERE a.attrelid = c.oid AND a.atttypid = t.oid AND nc.oid = c.relnamespace AND NOT pg_is_other_temp_schema(nc.oid) AND a.attnum > 0 AND NOT a.attisdropped AND (c.relkind = ANY (ARRAY['r', 'v'])) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_pr
ivilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'REFERENCES'))",
+
+ /* I_STMT_TABLES_CONSTRAINTS */
+ "SELECT current_database() AS constraint_catalog, nc.nspname AS constraint_schema, c.conname AS constraint_name, current_database() AS table_catalog, nr.nspname AS table_schema, r.relname AS table_name, CASE c.contype WHEN 'c' THEN 'CHECK' WHEN 'f' THEN 'FOREIGN KEY' WHEN 'p' THEN 'PRIMARY KEY' WHEN 'u' THEN 'UNIQUE' ELSE NULL END AS constraint_type, CASE c.contype WHEN 'c' THEN c.consrc ELSE NULL END, CASE WHEN c.condeferrable THEN TRUE ELSE FALSE END AS is_deferrable, CASE WHEN c.condeferred THEN TRUE ELSE FALSE END AS initially_deferred FROM pg_namespace nc, pg_namespace nr, pg_constraint c, pg_class r WHERE nc.oid = c.connamespace AND nr.oid = r.relnamespace AND c.conrelid = r.oid AND r.relkind = 'r' AND NOT pg_is_other_temp_schema(nr.oid) AND (pg_has_role(r.relowner, 'USAGE') OR has_table_privilege(r.oid, 'INSERT') OR has_table_privilege(r.oid, 'UPDATE') OR has_table_privilege(r.oid, 'DELETE') OR has_table_privilege(r.oid, 'REFERENCES') OR has_table_privilege(r.oid, 'T
RIGGER')) AND current_database() = ##cat::string AND nr.nspname = ##schema::string AND r.relname = ##name::string "
+ "UNION SELECT current_database() AS constraint_catalog, nr.nspname AS constraint_schema, (((((nr.oid || '_') || r.oid) || '_') || a.attnum) || '_not_null') AS constraint_name, current_database() AS table_catalog, nr.nspname AS table_schema, r.relname AS table_name, 'CHECK' AS constraint_type, a.attname || ' IS NOT NULL', FALSE AS is_deferrable, FALSE AS initially_deferred FROM pg_namespace nr, pg_class r, pg_attribute a WHERE nr.oid = r.relnamespace AND r.oid = a.attrelid AND a.attnotnull AND a.attnum > 0 AND NOT a.attisdropped AND r.relkind = 'r' AND NOT pg_is_other_temp_schema(nr.oid) AND (pg_has_role(r.relowner, 'USAGE') OR has_table_privilege(r.oid, 'SELECT') OR has_table_privilege(r.oid, 'INSERT') OR has_table_privilege(r.oid, 'UPDATE') OR has_table_privilege(r.oid, 'DELETE') OR has_table_privilege(r.oid, 'REFERENCES') OR has_table_privilege(r.oid, 'TRIGGER')) AND current_database() = ##cat::string AND nr.nspname = ##schema::string AND r.relname = ##name::string",
+
+ /* I_STMT_TABLES_CONSTRAINTS_ALL */
+ "SELECT current_database() AS constraint_catalog, nc.nspname AS constraint_schema, c.conname AS constraint_name, current_database() AS table_catalog, nr.nspname AS table_schema, r.relname AS table_name, CASE c.contype WHEN 'c' THEN 'CHECK' WHEN 'f' THEN 'FOREIGN KEY' WHEN 'p' THEN 'PRIMARY KEY' WHEN 'u' THEN 'UNIQUE' ELSE NULL END AS constraint_type, CASE c.contype WHEN 'c' THEN c.consrc ELSE NULL END, CASE WHEN c.condeferrable THEN TRUE ELSE FALSE END AS is_deferrable, CASE WHEN c.condeferred THEN TRUE ELSE FALSE END AS initially_deferred FROM pg_namespace nc, pg_namespace nr, pg_constraint c, pg_class r WHERE nc.oid = c.connamespace AND nr.oid = r.relnamespace AND c.conrelid = r.oid AND r.relkind = 'r' AND NOT pg_is_other_temp_schema(nr.oid) AND (pg_has_role(r.relowner, 'USAGE') OR has_table_privilege(r.oid, 'INSERT') OR has_table_privilege(r.oid, 'UPDATE') OR has_table_privilege(r.oid, 'DELETE') OR has_table_privilege(r.oid, 'REFERENCES') OR has_table_privilege(r.oid, 'T
RIGGER')) "
+ "UNION SELECT current_database() AS constraint_catalog, nr.nspname AS constraint_schema, (((((nr.oid || '_') || r.oid) || '_') || a.attnum) || '_not_null') AS constraint_name, current_database() AS table_catalog, nr.nspname AS table_schema, r.relname AS table_name, 'CHECK' AS constraint_type, a.attname || ' IS NOT NULL', FALSE AS is_deferrable, FALSE AS initially_deferred FROM pg_namespace nr, pg_class r, pg_attribute a WHERE nr.oid = r.relnamespace AND r.oid = a.attrelid AND a.attnotnull AND a.attnum > 0 AND NOT a.attisdropped AND r.relkind = 'r' AND NOT pg_is_other_temp_schema(nr.oid) AND (pg_has_role(r.relowner, 'USAGE') OR has_table_privilege(r.oid, 'SELECT') OR has_table_privilege(r.oid, 'INSERT') OR has_table_privilege(r.oid, 'UPDATE') OR has_table_privilege(r.oid, 'DELETE') OR has_table_privilege(r.oid, 'REFERENCES') OR has_table_privilege(r.oid, 'TRIGGER'))",
+
+ /* I_STMT_TABLES_CONSTRAINT_NAMED */
+ "SELECT constraint_catalog, constraint_schema, constraint_name, table_catalog, table_schema, table_name, constraint_type, NULL, CASE WHEN is_deferrable = 'YES' THEN TRUE ELSE FALSE END, CASE WHEN initially_deferred = 'YES' THEN TRUE ELSE FALSE END FROM information_schema.table_constraints WHERE table_catalog = ##cat::string AND table_schema = ##schema::string AND table_name = ##name::string AND constraint_name = ##name2::string",
+
+ /* I_STMT_REF_CONSTRAINTS */
+ "SELECT current_database(), nt.nspname, t.relname, c.conname, current_database(), nref.nspname, ref.relname, pkc.conname, CASE c.confmatchtype WHEN 'f' THEN 'FULL' WHEN 'p' THEN 'PARTIAL' WHEN 'u' THEN 'NONE' ELSE NULL END AS match_option, CASE c.confupdtype WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' WHEN 'r' THEN 'RESTRICT' WHEN 'a' THEN 'NO ACTION' ELSE NULL END AS update_rule, CASE c.confdeltype WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' WHEN 'r' THEN 'RESTRICT' WHEN 'a' THEN 'NO ACTION' ELSE NULL END AS delete_rule FROM pg_constraint c INNER JOIN pg_class t ON (c.conrelid=t.oid) INNER JOIN pg_namespace nt ON (nt.oid=t.relnamespace) INNER JOIN pg_class ref ON (c.confrelid=ref.oid) INNER JOIN pg_namespace nref ON (nref.oid=ref.relnamespace) INNER JOIN pg_constraint pkc ON (c.confrelid = pkc.conrelid AND information_schema._pg_keysequal(c.confkey, pkc.conkey) AND pkc.contype='p') WHERE c.contype = 'f' AND
current_database() = ##cat::string AND nt.nspname = ##schema::string AND t.relname = ##name::string AND c.conname = ##name2::string",
+
+ /* I_STMT_REF_CONSTRAINTS_ALL */
+ "SELECT current_database(), nt.nspname, t.relname, c.conname, current_database(), nref.nspname, ref.relname, pkc.conname, CASE c.confmatchtype WHEN 'f' THEN 'FULL' WHEN 'p' THEN 'PARTIAL' WHEN 'u' THEN 'NONE' ELSE NULL END AS match_option, CASE c.confupdtype WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' WHEN 'r' THEN 'RESTRICT' WHEN 'a' THEN 'NO ACTION' ELSE NULL END AS update_rule, CASE c.confdeltype WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' WHEN 'r' THEN 'RESTRICT' WHEN 'a' THEN 'NO ACTION' ELSE NULL END AS delete_rule FROM pg_constraint c INNER JOIN pg_class t ON (c.conrelid=t.oid) INNER JOIN pg_namespace nt ON (nt.oid=t.relnamespace) INNER JOIN pg_class ref ON (c.confrelid=ref.oid) INNER JOIN pg_namespace nref ON (nref.oid=ref.relnamespace) INNER JOIN pg_constraint pkc ON (c.confrelid = pkc.conrelid AND information_schema._pg_keysequal(c.confkey, pkc.conkey) AND pkc.contype='p') WHERE c.contype = 'f'",
+
+ /* I_STMT_KEY_COLUMN_USAGE */
+ "SELECT table_catalog, table_schema, table_name, constraint_name, column_name, ordinal_position FROM information_schema.key_column_usage WHERE table_catalog = ##cat::string AND table_schema = ##schema::string AND table_name = ##name::string AND constraint_name = ##name2::string",
+
+ /* I_STMT_KEY_COLUMN_USAGE_ALL */
+ "SELECT table_catalog, table_schema, table_name, constraint_name, column_name, ordinal_position FROM information_schema.key_column_usage",
+
+ /* I_STMT_CHECK_COLUMN_USAGE */
+ "SELECT current_database() AS table_catalog, nr.nspname AS table_schema, r.relname AS table_name, c.conname AS constraint_name,a.attname FROM pg_namespace nc, pg_namespace nr, pg_constraint c, pg_class r, pg_attribute a, (SELECT sc.oid, information_schema._pg_expandarray (sc.conkey) as x FROM pg_constraint sc WHERE sc.contype = 'c') ss WHERE nc.oid = c.connamespace AND nr.oid = r.relnamespace AND c.conrelid = r.oid AND r.relkind = 'r' AND NOT pg_is_other_temp_schema(nr.oid) AND (pg_has_role(r.relowner, 'USAGE') OR has_table_privilege(r.oid, 'INSERT') OR has_table_privilege(r.oid, 'UPDATE') OR has_table_privilege(r.oid, 'DELETE') OR has_table_privilege(r.oid, 'REFERENCES') OR has_table_privilege(r.oid, 'TRIGGER')) AND c.contype = 'c' AND ss.oid = c.oid AND a.attrelid = r.oid AND a.attnum = (ss.x).x AND current_database() = ##cat::string AND nr.nspname = ##schema::string AND r.relname = ##name::string AND c.conname = ##name2::string "
+ "UNION SELECT current_database() AS table_catalog, nr.nspname AS table_schema, r.relname AS table_name, (((((nr.oid || '_') || r.oid) || '_') || a.attnum) || '_not_null') AS constraint_name, a.attname FROM pg_namespace nr, pg_class r, pg_attribute a WHERE nr.oid = r.relnamespace AND r.oid = a.attrelid AND a.attnotnull AND a.attnum > 0 AND NOT a.attisdropped AND r.relkind = 'r' AND NOT pg_is_other_temp_schema(nr.oid) AND (pg_has_role(r.relowner, 'USAGE') OR has_table_privilege(r.oid, 'SELECT') OR has_table_privilege(r.oid, 'INSERT') OR has_table_privilege(r.oid, 'UPDATE') OR has_table_privilege(r.oid, 'DELETE') OR has_table_privilege(r.oid, 'REFERENCES') OR has_table_privilege(r.oid, 'TRIGGER')) AND current_database() = ##cat::string AND nr.nspname = ##schema::string AND r.relname = ##name::string AND (((((nr.oid || '_') || r.oid) || '_') || a.attnum) || '_not_null') = ##name2::string",
+
+ /* I_STMT_CHECK_COLUMN_USAGE_ALL */
+ "SELECT current_database() AS table_catalog, nr.nspname AS table_schema, r.relname AS table_name, c.conname AS constraint_name,a.attname FROM pg_namespace nc, pg_namespace nr, pg_constraint c, pg_class r, pg_attribute a, (SELECT sc.oid, information_schema._pg_expandarray (sc.conkey) as x FROM pg_constraint sc WHERE sc.contype = 'c') ss WHERE nc.oid = c.connamespace AND nr.oid = r.relnamespace AND c.conrelid = r.oid AND r.relkind = 'r' AND NOT pg_is_other_temp_schema(nr.oid) AND (pg_has_role(r.relowner, 'USAGE') OR has_table_privilege(r.oid, 'INSERT') OR has_table_privilege(r.oid, 'UPDATE') OR has_table_privilege(r.oid, 'DELETE') OR has_table_privilege(r.oid, 'REFERENCES') OR has_table_privilege(r.oid, 'TRIGGER')) AND c.contype = 'c' AND ss.oid = c.oid AND a.attrelid = r.oid AND a.attnum = (ss.x).x "
+ "UNION SELECT current_database() AS table_catalog, nr.nspname AS table_schema, r.relname AS table_name, (((((nr.oid || '_') || r.oid) || '_') || a.attnum) || '_not_null') AS constraint_name, a.attname FROM pg_namespace nr, pg_class r, pg_attribute a WHERE nr.oid = r.relnamespace AND r.oid = a.attrelid AND a.attnotnull AND a.attnum > 0 AND NOT a.attisdropped AND r.relkind = 'r' AND NOT pg_is_other_temp_schema(nr.oid) AND (pg_has_role(r.relowner, 'USAGE') OR has_table_privilege(r.oid, 'SELECT') OR has_table_privilege(r.oid, 'INSERT') OR has_table_privilege(r.oid, 'UPDATE') OR has_table_privilege(r.oid, 'DELETE') OR has_table_privilege(r.oid, 'REFERENCES') OR has_table_privilege(r.oid, 'TRIGGER'))",
+
+ /* I_STMT_UDT */
+ "SELECT pg_catalog.current_database() as cat, n.nspname, t.typname, 'gchararray', pg_catalog.obj_description(t.oid), CASE WHEN pg_catalog.pg_type_is_visible(t.oid) IS TRUE THEN t.typname ELSE coalesce (n.nspname || '.', '') || t.typname END, coalesce (n.nspname || '.', '') || t.typname, CASE WHEN t.typname ~ '^_' THEN TRUE WHEN t.typname in ('any', 'anyarray', 'anyelement', 'cid', 'cstring', 'int2vector', 'internal', 'language_handler', 'oidvector', 'opaque', 'record', 'refcursor', 'regclass', 'regoper', 'regoperator', 'regproc', 'regprocedure', 'regtype', 'SET', 'smgr', 'tid', 'trigger', 'unknown', 'void', 'xid', 'oid', 'aclitem') THEN TRUE ELSE FALSE END, o.rolname FROM pg_catalog.pg_type t, pg_catalog.pg_user u, pg_catalog.pg_namespace n , pg_roles o WHERE t.typowner=u.usesysid AND n.oid = t.typnamespace AND pg_catalog.pg_type_is_visible(t.oid) AND (t.typrelid != 0 AND (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) AND o.oid=t.typowner AND
pg_catalog.current_database() = ##cat::string AND n.nspname = ##schema::string",
+
+ /* I_STMT_UDT_ALL */
+ "SELECT pg_catalog.current_database(), n.nspname, t.typname, 'gchararray', pg_catalog.obj_description(t.oid), CASE WHEN pg_catalog.pg_type_is_visible(t.oid) IS TRUE THEN t.typname ELSE coalesce (n.nspname || '.', '') || t.typname END, coalesce (n.nspname || '.', '') || t.typname, CASE WHEN t.typname ~ '^_' THEN TRUE WHEN t.typname in ('any', 'anyarray', 'anyelement', 'cid', 'cstring', 'int2vector', 'internal', 'language_handler', 'oidvector', 'opaque', 'record', 'refcursor', 'regclass', 'regoper', 'regoperator', 'regproc', 'regprocedure', 'regtype', 'SET', 'smgr', 'tid', 'trigger', 'unknown', 'void', 'xid', 'oid', 'aclitem') THEN TRUE ELSE FALSE END, o.rolname FROM pg_catalog.pg_type t, pg_catalog.pg_user u, pg_catalog.pg_namespace n , pg_roles o WHERE t.typowner=u.usesysid AND n.oid = t.typnamespace AND pg_catalog.pg_type_is_visible(t.oid) AND (t.typrelid != 0 AND (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) AND o.oid=t.typowner",
+
+ /* I_STMT_UDT_COLUMNS */
+ "select pg_catalog.current_database(), n.nspname, udt.typname, a.attname, a.attnum, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN NULL ELSE coalesce (nt.nspname || '.', '') || t.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'UDT' || current_database() || '.' || n.nspname || '.' || udt.typname || '.' || a.attnum ELSE NULL END, information_schema._pg_char_max_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_char_octet_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_numeric_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_numeric_scale(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_datetime_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*,
t.*)), NULL, NULL , NULL, NULL, NULL, NULL FROM pg_type udt INNER JOIN pg_namespace n ON (udt.typnamespace=n.oid) INNER JOIN pg_attribute a ON (a.attrelid=udt.typrelid) INNER JOIN pg_type t ON (a.atttypid=t.oid) INNER JOIN pg_namespace nt ON (t.typnamespace = nt.oid) WHERE udt.typrelid != 0 AND (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = udt.typrelid) AND pg_catalog.current_database() = ##cat::string AND n.nspname = ##schema::string AND udt.typname = ##name::string",
+
+ /* I_STMT_UDT_COLUMNS_ALL */
+ "select pg_catalog.current_database(), n.nspname, udt.typname, a.attname, a.attnum, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN NULL ELSE coalesce (nt.nspname || '.', '') || t.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'UDT' || current_database() || '.' || n.nspname || '.' || udt.typname || '.' || a.attnum ELSE NULL END, information_schema._pg_char_max_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_char_octet_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_numeric_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_numeric_scale(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_datetime_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*,
t.*)), NULL, NULL , NULL, NULL, NULL, NULL FROM pg_type udt INNER JOIN pg_namespace n ON (udt.typnamespace=n.oid) INNER JOIN pg_attribute a ON (a.attrelid=udt.typrelid) INNER JOIN pg_type t ON (a.atttypid=t.oid) INNER JOIN pg_namespace nt ON (t.typnamespace = nt.oid) WHERE udt.typrelid != 0 AND (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = udt.typrelid)",
+
+ /* I_STMT_DOMAINS */
+ "SELECT pg_catalog.current_database(), nt.nspname, t.typname, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN NULL ELSE coalesce (nbt.nspname || '.', '') || bt.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'DOM' || current_database() || '.' || nt.nspname || '.' || t.typname ELSE NULL END, 'gchararray', information_schema._pg_char_max_length(t.typbasetype, t.typtypmod), information_schema._pg_char_octet_length(t.typbasetype, t.typtypmod), NULL, NULL, NULL, NULL, NULL, NULL, information_schema._pg_numeric_precision(t.typbasetype, t.typtypmod), information_schema._pg_numeric_scale(t.typbasetype, t.typtypmod), t.typdefault, pg_catalog.obj_description(t.oid), CASE WHEN pg_catalog.pg_type_is_visible(t.oid) IS TRUE THEN t.typname ELSE coalesce (nt.nspname || '.', '') || t.typname END, coalesce (nt.nspname || '.', '') || t.typname, FALSE, o.rolname FROM pg_type t, pg_namespace nt, pg_type bt, pg_namespace nbt, pg_roles o WHERE t.typnamespace = nt.oid AND t.typbaset
ype = bt.oid AND bt.typnamespace = nbt.oid AND t.typtype = 'd' AND o.oid=t.typowner AND pg_catalog.current_database() = ##cat::string AND nt.nspname = ##schema::string",
+
+ /* I_STMT_DOMAINS_ALL */
+ "SELECT pg_catalog.current_database(), nt.nspname, t.typname, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN NULL ELSE coalesce (nbt.nspname || '.', '') || bt.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'DOM' || current_database() || '.' || nt.nspname || '.' || t.typname ELSE NULL END, 'gchararray', information_schema._pg_char_max_length(t.typbasetype, t.typtypmod), information_schema._pg_char_octet_length(t.typbasetype, t.typtypmod), NULL, NULL, NULL, NULL, NULL, NULL, information_schema._pg_numeric_precision(t.typbasetype, t.typtypmod), information_schema._pg_numeric_scale(t.typbasetype, t.typtypmod), t.typdefault, pg_catalog.obj_description(t.oid), CASE WHEN pg_catalog.pg_type_is_visible(t.oid) IS TRUE THEN t.typname ELSE coalesce (nt.nspname || '.', '') || t.typname END, coalesce (nt.nspname || '.', '') || t.typname, FALSE, o.rolname FROM pg_type t, pg_namespace nt, pg_type bt, pg_namespace nbt, pg_roles o WHERE t.typnamespace = nt.oid AND t.typbaset
ype = bt.oid AND bt.typnamespace = nbt.oid AND t.typtype = 'd' AND o.oid=t.typowner",
+
+ /* I_STMT_DOMAINS_CONSTRAINTS */
+ "SELECT constraint_catalog, constraint_schema, constraint_name, domain_catalog, domain_schema, domain_name, NULL, CASE WHEN is_deferrable = 'YES' THEN TRUE ELSE FALSE END, CASE WHEN initially_deferred = 'YES' THEN TRUE ELSE FALSE END FROM information_schema.domain_constraints WHERE domain_catalog = ##cat::string AND domain_schema = ##schema::string AND domain_name = ##name::string",
+
+ /* I_STMT_DOMAINS_CONSTRAINTS_ALL */
+ "SELECT constraint_catalog, constraint_schema, constraint_name, domain_catalog, domain_schema, domain_name, NULL, CASE WHEN is_deferrable = 'YES' THEN TRUE ELSE FALSE END, CASE WHEN initially_deferred = 'YES' THEN TRUE ELSE FALSE END FROM information_schema.domain_constraints",
+
+ /* I_STMT_VIEWS_COLUMNS */
+ "SELECT view_catalog, view_schema, view_name, table_catalog, table_schema, table_name, column_name FROM information_schema.view_column_usage WHERE view_catalog = ##cat::string AND view_schema = ##schema::string AND view_name = ##name::string",
+
+ /* I_STMT_VIEWS_COLUMNS_ALL */
+ "SELECT view_catalog, view_schema, view_name, table_catalog, table_schema, table_name, column_name FROM information_schema.view_column_usage",
+
+ /* I_STMT_TRIGGERS */
+ "SELECT current_database(), n.nspname, t.tgname, em.text, current_database(), n.nspname, c.relname, \"substring\"(pg_get_triggerdef(t.oid), \"position\"(\"substring\"(pg_get_triggerdef(t.oid), 48), 'EXECUTE PROCEDURE') + 47) AS action_statement, CASE WHEN (t.tgtype & 1) = 1 THEN 'ROW' ELSE 'STATEMENT' END AS action_orientation, CASE WHEN (t.tgtype & 2) = 2 THEN 'BEFORE' ELSE 'AFTER' END AS condition_timing, pg_catalog.obj_description(t.oid), t.tgname, t.tgname FROM pg_namespace n, pg_class c, pg_trigger t, (( SELECT 4, 'INSERT' UNION ALL SELECT 8, 'DELETE') UNION ALL SELECT 16, 'UPDATE') em(num, text) WHERE n.oid = c.relnamespace AND c.oid = t.tgrelid AND (t.tgtype & em.num) <> 0 AND NOT t.tgisconstraint AND NOT pg_is_other_temp_schema(n.oid) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'DELETE') OR has_table_privilege(c.oid, 'REFERENCES') OR has_table_privilege(c.oid, 'TR
IGGER')) AND current_database() = ##cat::string AND n.nspname = ##schema::string AND c.relname = ##name::string",
+
+ /* I_STMT_TRIGGERS_ALL */
+ "SELECT current_database(), n.nspname, t.tgname, em.text, current_database(), n.nspname, c.relname, \"substring\"(pg_get_triggerdef(t.oid), \"position\"(\"substring\"(pg_get_triggerdef(t.oid), 48), 'EXECUTE PROCEDURE') + 47) AS action_statement, CASE WHEN (t.tgtype & 1) = 1 THEN 'ROW' ELSE 'STATEMENT' END AS action_orientation, CASE WHEN (t.tgtype & 2) = 2 THEN 'BEFORE' ELSE 'AFTER' END AS condition_timing, pg_catalog.obj_description(t.oid), t.tgname, t.tgname FROM pg_namespace n, pg_class c, pg_trigger t, (( SELECT 4, 'INSERT' UNION ALL SELECT 8, 'DELETE') UNION ALL SELECT 16, 'UPDATE') em(num, text) WHERE n.oid = c.relnamespace AND c.oid = t.tgrelid AND (t.tgtype & em.num) <> 0 AND NOT t.tgisconstraint AND NOT pg_is_other_temp_schema(n.oid) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'DELETE') OR has_table_privilege(c.oid, 'REFERENCES') OR has_table_privilege(c.oid, 'TR
IGGER'))",
+
+ /* I_STMT_EL_TYPES_COL */
+ "SELECT 'COL' || current_database() || '.' || nc.nspname || '.' || c.relname || '.' || a.attnum, current_database(), nc.nspname, c.relname, 'TABLE_COL', coalesce (nbt.nspname || '.', '') || bt.typname, NULL, NULL, NULL FROM pg_attribute a, pg_class c, pg_namespace nc, pg_type t JOIN pg_namespace nt ON t.typnamespace = nt.oid LEFT JOIN (pg_type bt JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid) ON t.typelem = bt.oid WHERE a.attrelid = c.oid AND a.atttypid = t.oid AND nc.oid = c.relnamespace AND NOT pg_is_other_temp_schema(nc.oid) AND a.attnum > 0 AND NOT a.attisdropped AND (c.relkind = ANY (ARRAY['r', 'v'])) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'REFERENCES')) AND t.typelem <> 0 AND t.typlen = -1 AND 'COL' || current_database() || '.' || nc.nspname || '.' || c.relname || '.' || a.attnum = ##name::string",
+
+ /* I_STMT_EL_TYPES_DOM */
+ "SELECT 'DOM' || current_database() || '.' || nt.nspname || '.' || t.typname, current_database(), nt.nspname, t.typname, 'DOMAIN', coalesce (nbt.nspname || '.', '') || bt.typname, NULL, NULL, NULL FROM pg_type t, pg_namespace nt, pg_type bt, pg_namespace nbt WHERE t.typnamespace = nt.oid AND t.typelem = bt.oid AND bt.typnamespace = nbt.oid AND t.typtype = 'd' AND t.typlen = -1 AND 'DOM' || current_database() || '.' || nt.nspname || '.' || t.typname = ##name::string",
+
+ /* I_STMT_EL_TYPES_UDT */
+ "SELECT 'UDT' || current_database() || '.' || n.nspname || '.' || udt.typname || '.' || a.attnum, pg_catalog.current_database(), n.nspname, udt.typname, 'UDT_COL', coalesce (nbt.nspname || '.', '') || bt.typname, NULL, NULL, NULL FROM pg_type udt INNER JOIN pg_namespace n ON (udt.typnamespace=n.oid) INNER JOIN pg_attribute a ON (a.attrelid=udt.typrelid) INNER JOIN pg_type t ON (a.atttypid=t.oid) INNER JOIN pg_namespace nt ON (t.typnamespace = nt.oid), pg_type bt, pg_namespace nbt where udt.typrelid != 0 AND (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = udt.typrelid) AND t.typelem = bt.oid AND bt.typnamespace = nbt.oid AND t.typlen = -1 AND 'UDT' || current_database() || '.' || n.nspname || '.' || udt.typname || '.' || a.attnum = ##name::string",
+
+ /* I_STMT_EL_TYPES_ROUT_PAR */
+ "SELECT 'ROUP' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n, current_database(), ss.n_nspname, ((ss.proname || '_') || ss.p_oid), 'ROUTINE_PAR', coalesce (nbt.nspname || '.', '') || bt.typname, NULL, NULL, NULL FROM pg_type t, pg_type bt, pg_namespace nbt, ( SELECT n.nspname AS n_nspname, p.proname, p.oid AS p_oid, p.proargnames, p.proargmodes, information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x FROM pg_namespace n, pg_proc p WHERE n.oid = p.pronamespace AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE'))) ss WHERE t.oid = (ss.x).x AND bt.oid= t.typelem AND bt.typnamespace = nbt.oid AND t.typelem <> 0 AND t.typlen = -1 AND 'ROUP' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n = ##name::string",
+
+ /* I_STMT_EL_TYPES_ROUT_COL */
+ "SELECT 'ROUC' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n, current_database(), ss.n_nspname, ((ss.proname || '_') || ss.p_oid), 'ROUTINE_COL', CASE WHEN at.typelem <> 0 AND at.typlen = -1 THEN 'array_spec' ELSE coalesce (ant.nspname || '.', '') || at.typname END, CASE WHEN at.typelem <> 0 AND at.typlen = -1 THEN 'ARR' || at.typelem ELSE NULL END, NULL, NULL FROM pg_type t, pg_namespace nt, ( SELECT n.nspname AS n_nspname, p.proname, p.oid AS p_oid, p.proargnames, p.proargmodes, information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x FROM pg_namespace n, pg_proc p WHERE n.oid = p.pronamespace AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE'))) ss, pg_type at, pg_namespace ant WHERE t.oid = (ss.x).x AND t.typnamespace = nt.oid AND at.oid = t.typelem AND at.typnamespace = ant.oid AND (ss.proargmodes[(ss.x).n] = 'o' OR ss.proargmodes[(ss.x).n] = 'b'
) AND 'ROUC' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n = ##name::string",
+
+ /* I_STMT_EL_TYPES_ALL */
+ "SELECT 'UDT' || current_database() || '.' || n.nspname || '.' || udt.typname || '.' || a.attnum, pg_catalog.current_database(), n.nspname, udt.typname, 'UDT_COL', coalesce (nbt.nspname || '.', '') || bt.typname, NULL, NULL, NULL FROM pg_type udt INNER JOIN pg_namespace n ON (udt.typnamespace=n.oid) INNER JOIN pg_attribute a ON (a.attrelid=udt.typrelid) INNER JOIN pg_type t ON (a.atttypid=t.oid) INNER JOIN pg_namespace nt ON (t.typnamespace = nt.oid), pg_type bt, pg_namespace nbt where udt.typrelid != 0 AND (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = udt.typrelid) AND t.typelem = bt.oid AND bt.typnamespace = nbt.oid AND t.typlen = -1 "
+ "UNION SELECT 'DOM' || current_database() || '.' || nt.nspname || '.' || t.typname, current_database(), nt.nspname, t.typname, 'DOMAIN', coalesce (nbt.nspname || '.', '') || bt.typname, NULL, NULL, NULL FROM pg_type t, pg_namespace nt, pg_type bt, pg_namespace nbt WHERE t.typnamespace = nt.oid AND t.typelem = bt.oid AND bt.typnamespace = nbt.oid AND t.typtype = 'd' AND t.typlen = -1 "
+ "UNION SELECT 'COL' || current_database() || '.' || nc.nspname || '.' || c.relname || '.' || a.attnum, current_database(), nc.nspname, c.relname, 'TABLE_COL', coalesce (nbt.nspname || '.', '') || bt.typname, NULL, NULL, NULL FROM pg_attribute a, pg_class c, pg_namespace nc, pg_type t JOIN pg_namespace nt ON t.typnamespace = nt.oid LEFT JOIN (pg_type bt JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid) ON t.typelem = bt.oid WHERE a.attrelid = c.oid AND a.atttypid = t.oid AND nc.oid = c.relnamespace AND NOT pg_is_other_temp_schema(nc.oid) AND a.attnum > 0 AND NOT a.attisdropped AND (c.relkind = ANY (ARRAY['r', 'v'])) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'REFERENCES')) AND t.typelem <> 0 AND t.typlen = -1 "
+ "UNION SELECT 'ROUP' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n, current_database(), ss.n_nspname, ((ss.proname || '_') || ss.p_oid), 'ROUTINE_PAR', coalesce (nbt.nspname || '.', '') || bt.typname, NULL, NULL, NULL FROM pg_type t, pg_type bt, pg_namespace nbt, ( SELECT n.nspname AS n_nspname, p.proname, p.oid AS p_oid, p.proargnames, p.proargmodes, information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x FROM pg_namespace n, pg_proc p WHERE n.oid = p.pronamespace AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE'))) ss WHERE t.oid = (ss.x).x AND bt.oid= t.typelem AND bt.typnamespace = nbt.oid AND t.typelem <> 0 AND t.typlen = -1 "
+ "UNION SELECT 'ROUC' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n, current_database(), ss.n_nspname, ((ss.proname || '_') || ss.p_oid), 'ROUTINE_COL', CASE WHEN at.typelem <> 0 AND at.typlen = -1 THEN 'array_spec' ELSE coalesce (ant.nspname || '.', '') || at.typname END, CASE WHEN at.typelem <> 0 AND at.typlen = -1 THEN 'ARR' || at.typelem ELSE NULL END, NULL, NULL FROM pg_type t, pg_namespace nt, ( SELECT n.nspname AS n_nspname, p.proname, p.oid AS p_oid, p.proargnames, p.proargmodes, information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x FROM pg_namespace n, pg_proc p WHERE n.oid = p.pronamespace AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE'))) ss, pg_type at, pg_namespace ant WHERE t.oid = (ss.x).x AND t.typnamespace = nt.oid AND at.oid = t.typelem AND at.typnamespace = ant.oid AND (ss.proargmodes[(ss.x).n] = 'o' OR ss.proargmodes[(ss.x).n]
= 'b')",
+
+ /* I_STMT_ROUTINES_ALL */
+ "SELECT current_database(), n.nspname, ((p.proname || '_') || p.oid), current_database(), n.nspname, p.proname, CASE WHEN p.proisagg THEN 'AGGREGATE' ELSE 'FUNCTION' END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ROUC' || current_database() || '.' || n.nspname || '.' || p.proname || '.' || p.oid ELSE coalesce (nt.nspname || '.', '') || t.typname END AS rettype, p.proretset, p.pronargs, CASE WHEN l.lanname = 'sql' THEN 'SQL' ELSE 'EXTERNAL' END, CASE WHEN pg_has_role(p.proowner, 'USAGE') THEN p.prosrc ELSE NULL END, CASE WHEN l.lanname = 'c' THEN p.prosrc ELSE NULL END, upper(l.lanname) AS external_language, 'GENERAL' AS parameter_style, CASE WHEN p.provolatile = 'i' THEN TRUE ELSE FALSE END, 'MODIFIES' AS sql_data_access, CASE WHEN p.proisstrict THEN TRUE ELSE FALSE END, pg_catalog.obj_description(p.oid), CASE WHEN pg_catalog.pg_function_is_visible(p.oid) IS TRUE THEN p.proname ELSE coalesce (n.nspname || '.', '') || p.proname END, coalesce (n.nspname || '.', '') ||
p.proname, o.rolname FROM pg_namespace n, pg_proc p, pg_language l, pg_type t, pg_namespace nt, pg_roles o WHERE n.oid = p.pronamespace AND p.prolang = l.oid AND p.prorettype = t.oid AND t.typnamespace = nt.oid AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE')) AND o.oid=p.proowner",
+
+ /* I_STMT_ROUTINES */
+ "SELECT current_database(), n.nspname, ((p.proname || '_') || p.oid), current_database(), n.nspname, p.proname, CASE WHEN p.proisagg THEN 'AGGREGATE' ELSE 'FUNCTION' END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ROUC' || current_database() || '.' || n.nspname || '.' || p.proname || '.' || p.oid ELSE coalesce (nt.nspname || '.', '') || t.typname END AS rettype, p.proretset, p.pronargs, CASE WHEN l.lanname = 'sql' THEN 'SQL' ELSE 'EXTERNAL' END, CASE WHEN pg_has_role(p.proowner, 'USAGE') THEN p.prosrc ELSE NULL END, CASE WHEN l.lanname = 'c' THEN p.prosrc ELSE NULL END, upper(l.lanname) AS external_language, 'GENERAL' AS parameter_style, CASE WHEN p.provolatile = 'i' THEN TRUE ELSE FALSE END, 'MODIFIES' AS sql_data_access, CASE WHEN p.proisstrict THEN TRUE ELSE FALSE END, pg_catalog.obj_description(p.oid), CASE WHEN pg_catalog.pg_function_is_visible(p.oid) IS TRUE THEN p.proname ELSE coalesce (n.nspname || '.', '') || p.proname END, coalesce (n.nspname || '.', '') ||
p.proname, o.rolname FROM pg_namespace n, pg_proc p, pg_language l, pg_type t, pg_namespace nt, pg_roles o WHERE current_database() = ##cat::string AND n.nspname = ##schema::string AND n.oid = p.pronamespace AND p.prolang = l.oid AND p.prorettype = t.oid AND t.typnamespace = nt.oid AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE')) AND o.oid=p.proowner",
+
+ /* I_STMT_ROUTINES_ONE */
+ "SELECT current_database(), n.nspname, ((p.proname || '_') || p.oid), current_database(), n.nspname, p.proname, CASE WHEN p.proisagg THEN 'AGGREGATE' ELSE 'FUNCTION' END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ROUC' || current_database() || '.' || n.nspname || '.' || p.proname || '.' || p.oid ELSE coalesce (nt.nspname || '.', '') || t.typname END AS rettype, p.proretset, p.pronargs, CASE WHEN l.lanname = 'sql' THEN 'SQL' ELSE 'EXTERNAL' END, CASE WHEN pg_has_role(p.proowner, 'USAGE') THEN p.prosrc ELSE NULL END, CASE WHEN l.lanname = 'c' THEN p.prosrc ELSE NULL END, upper(l.lanname) AS external_language, 'GENERAL' AS parameter_style, CASE WHEN p.provolatile = 'i' THEN TRUE ELSE FALSE END, 'MODIFIES' AS sql_data_access, CASE WHEN p.proisstrict THEN TRUE ELSE FALSE END, pg_catalog.obj_description(p.oid), CASE WHEN pg_catalog.pg_function_is_visible(p.oid) IS TRUE THEN p.proname ELSE coalesce (n.nspname || '.', '') || p.proname END, coalesce (n.nspname || '.', '') ||
p.proname, o.rolname FROM pg_namespace n, pg_proc p, pg_language l, pg_type t, pg_namespace nt, pg_roles o WHERE current_database() = ##cat::string AND n.nspname = ##schema::string AND ((p.proname || '_') || p.oid) = ##name::string AND n.oid = p.pronamespace AND p.prolang = l.oid AND p.prorettype = t.oid AND t.typnamespace = nt.oid AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE')) AND o.oid=p.proowner",
+
+ /* I_STMT_ROUTINE_PAR_ALL */
+ "SELECT current_database(), ss.n_nspname, ((ss.proname || '_') || ss.p_oid), (ss.x).n, CASE WHEN ss.proargmodes IS NULL THEN 'IN' WHEN ss.proargmodes[(ss.x).n] = 'i' THEN 'IN' WHEN ss.proargmodes[(ss.x).n] = 'o' THEN 'OUT' WHEN ss.proargmodes[(ss.x).n] = 'b' THEN 'INOUT' ELSE NULL END, NULLIF(ss.proargnames[(ss.x).n], ''), CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'array_spec' ELSE coalesce (nt.nspname || '.', '') || t.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ROUP' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n ELSE NULL END FROM pg_type t, pg_namespace nt, ( SELECT n.nspname AS n_nspname, p.proname, p.oid AS p_oid, p.proargnames, p.proargmodes, information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x FROM pg_namespace n, pg_proc p WHERE n.oid = p.pronamespace AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE'))) ss WHERE t.o
id = (ss.x).x AND t.typnamespace = nt.oid",
+
+ /* I_STMT_ROUTINE_PAR */
+ "SELECT current_database(), ss.n_nspname, ((ss.proname || '_') || ss.p_oid), (ss.x).n, CASE WHEN ss.proargmodes IS NULL THEN 'IN' WHEN ss.proargmodes[(ss.x).n] = 'i' THEN 'IN' WHEN ss.proargmodes[(ss.x).n] = 'o' THEN 'OUT' WHEN ss.proargmodes[(ss.x).n] = 'b' THEN 'INOUT' ELSE NULL END, NULLIF(ss.proargnames[(ss.x).n], ''), CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'array_spec' ELSE coalesce (nt.nspname || '.', '') || t.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ROUP' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n ELSE NULL END FROM pg_type t, pg_namespace nt, ( SELECT n.nspname AS n_nspname, p.proname, p.oid AS p_oid, p.proargnames, p.proargmodes, information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x FROM pg_namespace n, pg_proc p WHERE n.oid = p.pronamespace AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE'))) ss WHERE t.o
id = (ss.x).x AND t.typnamespace = nt.oid AND current_database() = ##cat::string AND ss.n_nspname = ##schema::string AND ((ss.proname || '_') || ss.p_oid) = ##name::string",
+
+ /* I_STMT_ROUTINE_COL_ALL */
+ "SELECT current_database(), ss.n_nspname, ((ss.proname || '_') || ss.p_oid), NULLIF(ss.proargnames[(ss.x).n], ''), (ss.x).n, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'array_spec' ELSE coalesce (nt.nspname || '.', '') || t.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ROUC' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n ELSE NULL END FROM pg_type t, pg_namespace nt, ( SELECT n.nspname AS n_nspname, p.proname, p.oid AS p_oid, p.proargnames, p.proargmodes, information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x FROM pg_namespace n, pg_proc p WHERE n.oid = p.pronamespace AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE'))) ss WHERE t.oid = (ss.x).x AND t.typnamespace = nt.oid AND (ss.proargmodes[(ss.x).n] = 'o' OR ss.proargmodes[(ss.x).n] = 'b') ORDER BY 1, 2, 3, 4, 5",
+
+ /* I_STMT_ROUTINE_COL */
+ "SELECT current_database(), ss.n_nspname, ((ss.proname || '_') || ss.p_oid), NULLIF(ss.proargnames[(ss.x).n], ''), (ss.x).n, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'array_spec' ELSE coalesce (nt.nspname || '.', '') || t.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ROUC' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n ELSE NULL END FROM pg_type t, pg_namespace nt, ( SELECT n.nspname AS n_nspname, p.proname, p.oid AS p_oid, p.proargnames, p.proargmodes, information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x FROM pg_namespace n, pg_proc p WHERE n.oid = p.pronamespace AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE'))) ss WHERE t.oid = (ss.x).x AND t.typnamespace = nt.oid AND (ss.proargmodes[(ss.x).n] = 'o' OR ss.proargmodes[(ss.x).n] = 'b') AND current_database() = ##cat::string AND ss.n_nspname = ##schema::string AND ((ss.pro
name || '_') || ss.p_oid) = ##name::string ORDER BY 1, 2, 3, 4, 5"
+
+};
+
+/*
+ * global static values, and
+ * predefined statements' GdaStatement, all initialized in _gda_postgres_provider_meta_init()
+ */
+static GdaStatement **internal_stmt;
+static GdaSet *i_set;
+
+/*
+ * Meta initialization
+ */
+void
+_gda_postgres_provider_meta_init (GdaServerProvider *provider)
+{
+ static GStaticMutex init_mutex = G_STATIC_MUTEX_INIT;
+ InternalStatementItem i;
+ static GdaSqlParser *parser = NULL;
+
+ g_static_mutex_lock (&init_mutex);
+
+ if (provider)
+ parser = gda_server_provider_internal_get_parser (provider);
+ else
+ parser = GDA_SQL_PARSER (g_object_new (GDA_TYPE_POSTGRES_PARSER, NULL));
+ internal_stmt = g_new0 (GdaStatement *, sizeof (internal_sql) / sizeof (gchar*));
+ for (i = I_STMT_CATALOG; i < sizeof (internal_sql) / sizeof (gchar*); i++) {
+ internal_stmt[i] = gda_sql_parser_parse_string (parser, internal_sql[i], NULL, NULL);
+ if (!internal_stmt[i])
+ g_error ("Could not parse internal statement: %s\n", internal_sql[i]);
+ }
+ if (!provider)
+ g_object_unref (parser);
+
+ i_set = gda_set_new_inline (4, "cat", G_TYPE_STRING, "",
+ "name", G_TYPE_STRING, "",
+ "schema", G_TYPE_STRING, "",
+ "name2", G_TYPE_STRING, "");
+
+ g_static_mutex_unlock (&init_mutex);
+
+#ifdef GDA_DEBUG
+ _gda_postgres_test_keywords ();
+#endif
+}
+
+#define GDA_POSTGRES_GET_REUSEABLE_DATA(cdata) (* ((GdaPostgresReuseable**) (cdata)))
+
+gboolean
+_gda_postgres_meta__info (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_CATALOG],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_information_schema_catalog_name, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store,
+ _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify (store, context->table_name, model, NULL, error, NULL);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta__btypes (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model, *proxy;
+ gboolean retval = TRUE;
+ gint i, nrows;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ /* use a prepared statement for the "base" model */
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_BTYPES],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_builtin_data_types, error);
+ if (!model)
+ return FALSE;
+
+ /* use a proxy to customize @model */
+ proxy = (GdaDataModel*) gda_data_proxy_new (model);
+ g_object_set (G_OBJECT (proxy), "defer-sync", FALSE, "sample-size", 0, NULL);
+ nrows = gda_data_model_get_n_rows (model);
+ for (i = 0; i < nrows; i++) {
+ const GValue *value;
+ GType type;
+ value = gda_data_model_get_value_at (model, 6, i, error);
+ if (!value) {
+ retval = FALSE;
+ break;
+ }
+
+ guint oid = (guint) g_ascii_strtoull (g_value_get_string (value), NULL, 10);
+ type = _gda_postgres_type_oid_to_gda (cnc, rdata, oid);
+ if (type != G_TYPE_STRING) {
+ GValue *v;
+ g_value_set_string (v = gda_value_new (G_TYPE_STRING), g_type_name (type));
+ retval = gda_data_model_set_value_at (proxy, 2, i, v, error);
+ gda_value_free (v);
+ if (!retval)
+ break;
+ }
+ }
+
+ /* modify meta store with @proxy */
+ if (retval) {
+ gda_meta_store_set_reserved_keywords_func (store,
+ _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify (store, context->table_name, proxy, NULL, error, NULL);
+ }
+ g_object_unref (proxy);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta__udt (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_UDT_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_udt, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_udt (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *udt_catalog, const GValue *udt_schema)
+{
+ GdaDataModel *model;
+ gboolean retval = TRUE;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), udt_catalog, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), udt_schema, error))
+ return FALSE;
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_UDT],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_udt, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta__udt_cols (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_UDT_COLUMNS_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_udt_columns, error);
+
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_udt_cols (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *udt_catalog, const GValue *udt_schema, const GValue *udt_name)
+{
+ GdaDataModel *model;
+ gboolean retval = TRUE;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ if (!gda_holder_set_value (gda_set_get_holder (i_set, "cat"), udt_catalog, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), udt_schema, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), udt_name, error))
+ return FALSE;
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_UDT_COLUMNS_ALL],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_udt_columns, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta__enums (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+ if (rdata->version_float >= 8.3)
+ TO_IMPLEMENT;
+
+ return TRUE;
+}
+
+gboolean
+_gda_postgres_meta_enums (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *udt_catalog, const GValue *udt_schema, const GValue *udt_name)
+{
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+ if (rdata->version_float >= 8.3)
+ TO_IMPLEMENT;
+
+ return TRUE;
+}
+
+gboolean
+_gda_postgres_meta__domains (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_DOMAINS_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_domains, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_domains (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *domain_catalog, const GValue *domain_schema)
+{
+ GdaDataModel *model;
+ gboolean retval = TRUE;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), domain_catalog, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), domain_schema, error))
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_DOMAINS],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_domains, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta__constraints_dom (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_DOMAINS_CONSTRAINTS_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_domain_constraints, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_constraints_dom (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *domain_catalog, const GValue *domain_schema,
+ const GValue *domain_name)
+{
+ GdaDataModel *model;
+ gboolean retval = TRUE;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), domain_catalog, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), domain_schema, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), domain_name, error))
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_DOMAINS_CONSTRAINTS],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_domain_constraints, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta__el_types (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+
+ /* check correct postgres server version */
+ GdaPostgresReuseable *rdata;
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+ if (rdata->version_float < 8.2) {
+ /* nothing for this version of PostgreSQL */
+ return TRUE;
+ }
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_EL_TYPES_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_element_types, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_el_types (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *specific_name)
+{
+ const gchar *cstr;
+ GdaDataModel *model;
+ gboolean retval;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), specific_name, error))
+ return FALSE;
+ cstr = g_value_get_string (specific_name);
+ if (*cstr == 'C') {
+ /* check correct postgres server version */
+ if (rdata->version_float < 8.2) {
+ /* nothing for this version of PostgreSQL */
+ return TRUE;
+ }
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_EL_TYPES_COL],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_element_types, error);
+ }
+ else if (*cstr == 'D')
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_EL_TYPES_DOM],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_element_types, error);
+ else if (*cstr == 'U')
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_EL_TYPES_UDT],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_element_types, error);
+ else if (!strcmp (cstr, "ROUTINE_PAR"))
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_EL_TYPES_ROUT_PAR],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_element_types, error);
+ else if (!strcmp (cstr, "ROUTINE_COL"))
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_EL_TYPES_ROUT_COL],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_element_types, error);
+ else
+ TO_IMPLEMENT;
+
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta__collations (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ /* nothing to do */
+ return TRUE;
+}
+
+gboolean
+_gda_postgres_meta_collations (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *collation_catalog, const GValue *collation_schema,
+ const GValue *collation_name_n)
+{
+ /* nothing to do */
+ return TRUE;
+}
+
+gboolean
+_gda_postgres_meta__character_sets (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ /* nothing to do */
+ return TRUE;
+}
+
+gboolean
+_gda_postgres_meta_character_sets (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *chset_catalog, const GValue *chset_schema,
+ const GValue *chset_name_n)
+{
+ /* nothing to do */
+ return TRUE;
+}
+
+gboolean
+_gda_postgres_meta__schemata (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_SCHEMAS_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_schemata, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_schemata (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *catalog_name, const GValue *schema_name_n)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), catalog_name, error))
+ return FALSE;
+ if (!schema_name_n) {
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_SCHEMAS],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_schemata, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify (store, context->table_name, model, NULL, error, NULL);
+ }
+ else {
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), schema_name_n, error))
+ return FALSE;
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_SCHEMA_NAMED],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_schemata, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify (store, context->table_name, model, "schema_name = ##name::string", error,
+ "name", schema_name_n, NULL);
+ }
+ g_object_unref (model);
+
+ return retval;
+}
+
+/* get the float version of a Postgres version which looks like:
+ * PostgreSQL 7.2.2 on i686-pc-linux-gnu, compiled by GCC 2.96 => returns 7.22
+ * PostgreSQL 7.3 on i686-pc-linux-gnu, compiled by GCC 2.95.3 => returns 7.3
+ * WARNING: no serious test is made on the validity of the string
+ */
+static gfloat
+get_pg_version_float (const gchar *str)
+{
+ gfloat retval = 0.;
+ const gchar *ptr;
+ gfloat div = 1;
+
+ if (!str)
+ return retval;
+
+ /* go on the first digit of version number */
+ ptr = str;
+ while (*ptr != ' ')
+ ptr++;
+ ptr++;
+
+ /* elaborate the real version number */
+ while (*ptr != ' ') {
+ if (*ptr != '.') {
+ retval += (*ptr - '0')/div;
+ div *= 10;
+ }
+ ptr++;
+ }
+
+ return retval;
+}
+
+static gboolean
+compute_pg_version (GdaConnection *cnc, GdaPostgresReuseable *rdata, GError **error)
+{
+ GdaSqlBuilder *b;
+ GdaStatement *stmt;
+ GdaDataModel *model;
+
+ b = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT);
+ gda_sql_builder_add_function (b, 1, "version", 0);
+ gda_sql_builder_add_field (b, 1, 0);
+ stmt = gda_sql_builder_get_statement (b, NULL);
+ g_object_unref (b);
+ g_assert (stmt);
+
+ model = gda_connection_statement_execute_select (cnc, stmt, NULL, error);
+ g_object_unref (stmt);
+ if (!model)
+ return FALSE;
+
+ const GValue *cvalue;
+ cvalue = gda_data_model_get_value_at (model, 0, 0, NULL);
+ if (!cvalue) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+ GDA_SERVER_PROVIDER_INTERNAL_ERROR, "%s",
+ _("Can't import data from web server"));
+ g_object_unref (model);
+ return FALSE;
+ }
+
+ const gchar *str;
+ str = g_value_get_string (cvalue);
+ rdata->version_float = get_pg_version_float (str);
+ ((GdaProviderReuseable*)rdata)->version_major =
+ g_strdup_printf ("%d", (int) rdata->version_float);
+ ((GdaProviderReuseable*)rdata)->version_minor =
+ g_strdup_printf ("%d", (int) ((rdata->version_float - (int) rdata->version_float) * 10));
+
+ g_object_unref (model);
+
+ /*g_print ("VERSIONS: [%f] [%s] [%s]\n", rdata->version_float,
+ ((GdaProviderReuseable*)rdata)->version_major,
+ ((GdaProviderReuseable*)rdata)->version_minor);*/
+ return TRUE;
+}
+
+gboolean
+_gda_postgres_meta__tables_views (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *tables_model, *views_model;
+ gboolean retval = TRUE;
+
+ /* check correct postgres server version */
+ GdaPostgresReuseable *rdata;
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+ if ((rdata->version_float == 0) && ! compute_pg_version (cnc, rdata, error))
+ return FALSE;
+ if (rdata->version_float < 8.2) {
+ /* nothing for this version of PostgreSQL */
+ return TRUE;
+ }
+
+ tables_model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_TABLES_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_tables, error);
+ if (!tables_model)
+ return FALSE;
+ views_model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_VIEWS_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_views, error);
+ if (!views_model) {
+ g_object_unref (tables_model);
+ return FALSE;
+ }
+
+ GdaMetaContext c2;
+ c2 = *context; /* copy contents, just because we need to modify @context->table_name */
+ if (retval) {
+ c2.table_name = "_tables";
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, &c2, tables_model, error);
+ }
+ if (retval) {
+ c2.table_name = "_views";
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, &c2, views_model, error);
+ }
+ g_object_unref (tables_model);
+ g_object_unref (views_model);
+
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_tables_views (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema, const GValue *table_name_n)
+{
+ GdaDataModel *tables_model, *views_model;
+ gboolean retval = TRUE;
+
+ /* check correct postgres server version */
+ GdaPostgresReuseable *rdata;
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+ if (rdata->version_float < 8.2) {
+ /* nothing for this version of PostgreSQL */
+ return TRUE;
+ }
+
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), table_schema, error))
+ return FALSE;
+ if (!table_name_n) {
+ tables_model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_TABLES],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_tables, error);
+ if (!tables_model)
+ return FALSE;
+ views_model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_VIEWS],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_views, error);
+ if (!views_model) {
+ g_object_unref (tables_model);
+ return FALSE;
+ }
+ }
+ else {
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), table_name_n, error))
+ return FALSE;
+ tables_model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_TABLE_NAMED],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_tables, error);
+ if (!tables_model)
+ return FALSE;
+ views_model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_VIEW_NAMED],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_views, error);
+ if (!views_model) {
+ g_object_unref (tables_model);
+ return FALSE;
+ }
+ }
+
+ GdaMetaContext c2;
+ c2 = *context; /* copy contents, just because we need to modify @context->table_name */
+ if (retval) {
+ c2.table_name = "_tables";
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, &c2, tables_model, error);
+ }
+ if (retval) {
+ c2.table_name = "_views";
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, &c2, views_model, error);
+ }
+ g_object_unref (tables_model);
+ g_object_unref (views_model);
+
+ return retval;
+}
+
+gboolean _gda_postgres_meta__columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model, *proxy;
+ gboolean retval = TRUE;
+ gint i, nrows;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ /* use a prepared statement for the "base" model */
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_COLUMNS_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_columns, error);
+ if (!model)
+ return FALSE;
+
+ /* use a proxy to customize @model */
+ proxy = (GdaDataModel*) gda_data_proxy_new (model);
+ g_object_set (G_OBJECT (proxy), "defer-sync", FALSE, "sample-size", 0, NULL);
+ nrows = gda_data_model_get_n_rows (model);
+ for (i = 0; i < nrows; i++) {
+ const GValue *value;
+ GType type;
+
+ /* GType */
+ value = gda_data_model_get_value_at (model, 24, i, error);
+ if (!value) {
+ retval = FALSE;
+ break;
+ }
+
+ guint oid = (guint) g_ascii_strtoull (g_value_get_string (value), NULL, 10);
+ type = _gda_postgres_type_oid_to_gda (cnc, rdata, oid);
+ if (type != G_TYPE_STRING) {
+ GValue *v;
+ g_value_set_string (v = gda_value_new (G_TYPE_STRING), g_type_name (type));
+ retval = gda_data_model_set_value_at (proxy, 9, i, v, error);
+ gda_value_free (v);
+ if (!retval)
+ break;
+ }
+
+ /* column default: remove the datatype cast on strings:
+ * 'abd'::character varying => 'abd' */
+ value = gda_data_model_get_value_at (model, 5, i, error);
+ if (!value) {
+ retval = FALSE;
+ break;
+ }
+
+ if (G_VALUE_TYPE (value) == G_TYPE_STRING) {
+ const gchar *cstr;
+ cstr = g_value_get_string (value);
+ if (cstr && (*cstr == '\'')) {
+ gint len;
+ len = strlen (cstr);
+ if (cstr [len-1] != '\'') {
+ gchar *tmp = g_strdup (cstr);
+ gint k;
+ for (k = len - 1; k > 0; k--) {
+ if (tmp [k] == '\'') {
+ tmp [k+1] = 0;
+ break;
+ }
+ }
+ GValue *v;
+ g_value_take_string (v = gda_value_new (G_TYPE_STRING), tmp);
+ retval = gda_data_model_set_value_at (proxy, 5, i, v, error);
+ gda_value_free (v);
+ if (!retval)
+ break;
+ }
+ }
+ }
+ }
+
+ /* modify meta store with @proxy */
+ if (retval) {
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, proxy, error);
+ }
+ g_object_unref (proxy);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema, const GValue *table_name)
+{
+ GdaDataModel *model, *proxy;
+ gboolean retval = TRUE;
+ gint i, nrows;
+ GdaPostgresReuseable *rdata;
+
+ /* check correct postgres server version */
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+ if (rdata->version_float < 8.2) {
+ /* nothing for this version of PostgreSQL */
+ return TRUE;
+ }
+
+ /* use a prepared statement for the "base" model */
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), table_schema, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), table_name, error))
+ return FALSE;
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_COLUMNS_OF_TABLE],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_columns, error);
+ if (!model)
+ return FALSE;
+
+ /* use a proxy to customize @model */
+ proxy = (GdaDataModel*) gda_data_proxy_new (model);
+ g_object_set (G_OBJECT (proxy), "defer-sync", FALSE, "sample-size", 0, NULL);
+ nrows = gda_data_model_get_n_rows (model);
+ for (i = 0; i < nrows; i++) {
+ const GValue *value;
+ GType type;
+
+ /* GType */
+ value = gda_data_model_get_value_at (model, 24, i, error);
+ if (!value) {
+ retval = FALSE;
+ break;
+ }
+
+ guint oid = (guint) g_ascii_strtoull (g_value_get_string (value), NULL, 10);
+ type = _gda_postgres_type_oid_to_gda (cnc, rdata, oid);
+ if (type != G_TYPE_STRING) {
+ GValue *v;
+ g_value_set_string (v = gda_value_new (G_TYPE_STRING), g_type_name (type));
+ retval = gda_data_model_set_value_at (proxy, 9, i, v, error);
+ gda_value_free (v);
+ if (!retval)
+ break;
+ }
+
+ /* column default: remove the datatype cast on strings:
+ * 'abd'::character varying => 'abd' */
+ value = gda_data_model_get_value_at (model, 5, i, error);
+ if (!value) {
+ retval = FALSE;
+ break;
+ }
+
+ if (G_VALUE_TYPE (value) == G_TYPE_STRING) {
+ const gchar *cstr;
+ cstr = g_value_get_string (value);
+ if (cstr && (*cstr == '\'')) {
+ gint len;
+ len = strlen (cstr);
+ if (cstr [len-1] != '\'') {
+ gchar *tmp = g_strdup (cstr);
+ gint k;
+ for (k = len - 1; k > 0; k--) {
+ if (tmp [k] == '\'') {
+ tmp [k+1] = 0;
+ break;
+ }
+ }
+ GValue *v;
+ g_value_take_string (v = gda_value_new (G_TYPE_STRING), tmp);
+ retval = gda_data_model_set_value_at (proxy, 5, i, v, error);
+ gda_value_free (v);
+ if (!retval)
+ break;
+ }
+ }
+ }
+ }
+
+ /* modify meta store with @proxy */
+ if (retval) {
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify (store, context->table_name, proxy,
+ "table_schema = ##schema::string AND table_name = ##name::string", error,
+ "schema", table_schema, "name", table_name, NULL);
+ }
+ g_object_unref (proxy);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta__view_cols (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+
+ /* check correct postgres server version */
+ GdaPostgresReuseable *rdata;
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+ if (rdata->version_float < 8.2) {
+ /* nothing for this version of PostgreSQL */
+ return TRUE;
+ }
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_VIEWS_COLUMNS_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_view_column_usage, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_view_cols (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *view_catalog, const GValue *view_schema,
+ const GValue *view_name)
+{
+ GdaDataModel *model;
+ gboolean retval = TRUE;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), view_catalog, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), view_schema, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), view_name, error))
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_VIEWS_COLUMNS],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_view_column_usage, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta__constraints_tab (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_TABLES_CONSTRAINTS_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_table_constraints, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_constraints_tab (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema, const GValue *table_name,
+ const GValue *constraint_name_n)
+{
+ GdaDataModel *model;
+ gboolean retval = TRUE;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), table_schema, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), table_name, error))
+ return FALSE;
+
+ if (!constraint_name_n) {
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_TABLES_CONSTRAINTS],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_table_constraints, error);
+ if (!model)
+ return FALSE;
+ if (retval) {
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify (store, context->table_name, model,
+ "table_schema = ##schema::string AND table_name = ##name::string",
+ error,
+ "schema", table_schema, "name", table_name, NULL);
+ }
+
+ }
+ else {
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name2"), constraint_name_n, error))
+ return FALSE;
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_TABLES_CONSTRAINT_NAMED],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_table_constraints, error);
+ if (!model)
+ return FALSE;
+ if (retval) {
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify (store, context->table_name, model,
+ "table_schema = ##schema::string AND table_name = ##name::string AND constraint_name = ##name2::string", error,
+ "schema", table_schema, "name", table_name, "name2", constraint_name_n, NULL);
+ }
+ }
+
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta__constraints_ref (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_REF_CONSTRAINTS_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_referential_constraints, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_constraints_ref (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema, const GValue *table_name,
+ const GValue *constraint_name)
+{
+ GdaDataModel *model;
+ gboolean retval = TRUE;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), table_schema, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), table_name, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name2"), constraint_name, error))
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_REF_CONSTRAINTS],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_referential_constraints, error);
+ if (!model)
+ return FALSE;
+
+
+ /* modify meta store */
+ if (retval) {
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify (store, context->table_name, model,
+ "table_schema = ##schema::string AND table_name = ##name::string AND constraint_name = ##name2::string",
+ error,
+ "schema", table_schema, "name", table_name, "name2", constraint_name, NULL);
+ }
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta__key_columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_KEY_COLUMN_USAGE_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_key_column_usage, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_key_columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema, const GValue *table_name,
+ const GValue *constraint_name)
+{
+ GdaDataModel *model;
+ gboolean retval = TRUE;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), table_schema, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), table_name, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name2"), constraint_name, error))
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_KEY_COLUMN_USAGE],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_key_column_usage, error);
+ if (!model)
+ return FALSE;
+
+
+ /* modify meta store */
+ if (retval) {
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify (store, context->table_name, model,
+ "table_schema = ##schema::string AND table_name = ##name::string AND constraint_name = ##name2::string",
+ error,
+ "schema", table_schema, "name", table_name, "name2", constraint_name, NULL);
+ }
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta__check_columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_CHECK_COLUMN_USAGE_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_check_column_usage, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_check_columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema,
+ const GValue *table_name, const GValue *constraint_name)
+{
+ GdaDataModel *model;
+ gboolean retval = TRUE;
+ GdaPostgresReuseable *rdata;
+
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), table_schema, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), table_name, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name2"), constraint_name, error))
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_CHECK_COLUMN_USAGE],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_check_column_usage, error);
+ if (!model)
+ return FALSE;
+
+
+ /* modify meta store */
+ if (retval) {
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify (store, context->table_name, model,
+ "table_schema = ##schema::string AND table_name = ##name::string AND constraint_name = ##name2::string",
+ error,
+ "schema", table_schema, "name", table_name, "name2", constraint_name, NULL);
+ }
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta__triggers (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+
+ /* check correct postgres server version */
+ GdaPostgresReuseable *rdata;
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+ if (rdata->version_float < 8.2) {
+ /* nothing for this version of PostgreSQL */
+ return TRUE;
+ }
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_TRIGGERS_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_triggers, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_triggers (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema,
+ const GValue *table_name)
+{
+ GdaDataModel *model;
+ gboolean retval = TRUE;
+
+ /* check correct postgres server version */
+ GdaPostgresReuseable *rdata;
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+ if (rdata->version_float < 8.2) {
+ /* nothing for this version of PostgreSQL */
+ return TRUE;
+ }
+
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), table_schema, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), table_name, error))
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_TRIGGERS],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_triggers, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta__routines (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+
+ /* check correct postgres server version */
+ GdaPostgresReuseable *rdata;
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+ if (rdata->version_float < 8.2) {
+ /* nothing for this version of PostgreSQL */
+ return TRUE;
+ }
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_ROUTINES_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_routines, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_routines (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *routine_catalog, const GValue *routine_schema,
+ const GValue *routine_name_n)
+{
+ GdaDataModel *model;
+ gboolean retval = TRUE;
+
+ /* check correct postgres server version */
+ GdaPostgresReuseable *rdata;
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+ if (rdata->version_float < 8.2) {
+ /* nothing for this version of PostgreSQL */
+ return TRUE;
+ }
+
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), routine_catalog, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), routine_schema, error))
+ return FALSE;
+ if (routine_name_n) {
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), routine_name_n, error))
+ return FALSE;
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_ROUTINES_ONE],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_routines, error);
+ }
+ else
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_ROUTINES],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_routines, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta__routine_col (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model, *proxy;
+ gint ordinal_pos, i, nrows;
+ const GValue *spname = NULL;
+ gboolean retval;
+
+ /* check correct postgres server version */
+ GdaPostgresReuseable *rdata;
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+ if (rdata->version_float < 8.2) {
+ /* nothing for this version of PostgreSQL */
+ return TRUE;
+ }
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_ROUTINE_COL_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_routine_columns, error);
+ if (!model)
+ return FALSE;
+
+ /* use a proxy to customize @model */
+ proxy = (GdaDataModel*) gda_data_proxy_new (model);
+ g_object_set (G_OBJECT (proxy), "defer-sync", FALSE, "sample-size", 0, NULL);
+ nrows = gda_data_model_get_n_rows (model);
+ for (i = 0; i < nrows; i++) {
+ const GValue *cvalue;
+ GValue *v;
+
+ cvalue = gda_data_model_get_value_at (model, 2, i, error);
+ if (!cvalue) {
+ retval = FALSE;
+ break;
+ }
+ if (!spname || gda_value_compare (spname, cvalue))
+ /* reinit ordinal position */
+ ordinal_pos = 1;
+ spname = cvalue;
+
+ g_value_set_int ((v = gda_value_new (G_TYPE_INT)), ordinal_pos++);
+ retval = gda_data_model_set_value_at (proxy, 4, i, v, error);
+ gda_value_free (v);
+ if (!retval)
+ break;
+ }
+
+ if (retval) {
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, proxy, error);
+ }
+ g_object_unref (model);
+ g_object_unref (proxy);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_routine_col (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *rout_catalog, const GValue *rout_schema,
+ const GValue *rout_name)
+{
+ GdaDataModel *model, *proxy;
+ gint ordinal_pos, i, nrows;
+ const GValue *spname = NULL;
+ gboolean retval;
+
+ /* check correct postgres server version */
+ GdaPostgresReuseable *rdata;
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+ if (rdata->version_float < 8.2) {
+ /* nothing for this version of PostgreSQL */
+ return TRUE;
+ }
+
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), rout_catalog, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), rout_schema, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), rout_name, error))
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_ROUTINE_COL],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_routine_columns, error);
+ if (!model)
+ return FALSE;
+
+ /* use a proxy to customize @model */
+ proxy = (GdaDataModel*) gda_data_proxy_new (model);
+ g_object_set (G_OBJECT (proxy), "defer-sync", FALSE, "sample-size", 0, NULL);
+ nrows = gda_data_model_get_n_rows (model);
+ for (i = 0; i < nrows; i++) {
+ const GValue *cvalue;
+ GValue *v;
+
+ cvalue = gda_data_model_get_value_at (model, 2, i, error);
+ if (!cvalue) {
+ retval = FALSE;
+ break;
+ }
+
+ if (!spname || gda_value_compare (spname, cvalue))
+ /* reinit ordinal position */
+ ordinal_pos = 1;
+ spname = cvalue;
+
+ g_value_set_int ((v = gda_value_new (G_TYPE_INT)), ordinal_pos++);
+ retval = gda_data_model_set_value_at (proxy, 4, i, v, error);
+ gda_value_free (v);
+ if (!retval)
+ break;
+ }
+
+ if (retval) {
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, proxy, error);
+ }
+ g_object_unref (model);
+ g_object_unref (proxy);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta__routine_par (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+
+ /* check correct postgres server version */
+ GdaPostgresReuseable *rdata;
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+ if (rdata->version_float < 8.2) {
+ /* nothing for this version of PostgreSQL */
+ return TRUE;
+ }
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_ROUTINE_PAR_ALL],
+ NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_parameters, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_routine_par (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *rout_catalog, const GValue *rout_schema,
+ const GValue *rout_name)
+{
+ GdaDataModel *model;
+ gboolean retval = TRUE;
+
+ /* check correct postgres server version */
+ GdaPostgresReuseable *rdata;
+ rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+ if (!rdata)
+ return FALSE;
+ if (rdata->version_float < 8.2) {
+ /* nothing for this version of PostgreSQL */
+ return TRUE;
+ }
+
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), rout_catalog, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), rout_schema, error))
+ return FALSE;
+ if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), rout_name, error))
+ return FALSE;
+
+ model = gda_connection_statement_execute_select_full (cnc,
+ internal_stmt[I_STMT_ROUTINE_PAR],
+ i_set,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ _col_types_parameters, error);
+ if (!model)
+ return FALSE;
+
+ gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+ ((GdaProviderReuseable*) rdata));
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
diff --git a/providers/postgres/gda-postgres-meta.h b/providers/reuseable/postgres/gda-postgres-meta.h
similarity index 100%
rename from providers/postgres/gda-postgres-meta.h
rename to providers/reuseable/postgres/gda-postgres-meta.h
diff --git a/providers/postgres/gda-postgres-parser.c b/providers/reuseable/postgres/gda-postgres-parser.c
similarity index 100%
rename from providers/postgres/gda-postgres-parser.c
rename to providers/reuseable/postgres/gda-postgres-parser.c
diff --git a/providers/postgres/gda-postgres-parser.h b/providers/reuseable/postgres/gda-postgres-parser.h
similarity index 100%
rename from providers/postgres/gda-postgres-parser.h
rename to providers/reuseable/postgres/gda-postgres-parser.h
diff --git a/providers/reuseable/postgres/gda-postgres-reuseable.c b/providers/reuseable/postgres/gda-postgres-reuseable.c
new file mode 100644
index 0000000..75d8972
--- /dev/null
+++ b/providers/reuseable/postgres/gda-postgres-reuseable.c
@@ -0,0 +1,432 @@
+/* GDA postgres provider
+ * Copyright (C) 1998 - 2009 The GNOME Foundation.
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ * Rodrigo Moya <rodrigo gnome-db org>
+ * Gonzalo Paniagua Javier <gonzalo 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 <glib/gi18n-lib.h>
+#include "gda-postgres-reuseable.h"
+#include "gda-postgres-parser.h"
+
+#include <libgda/sqlite/keywords_hash.h>
+#include "keywords_hash.c" /* this one is dynamically generated */
+
+/*
+ * Reuseable interface entry point
+ */
+static GdaProviderReuseableOperations
+_gda_postgres_reuseable = {
+ _gda_postgres_reuseable_new_data,
+ _gda_postgres_reuseable_reset_data,
+ _gda_postgres_reuseable_get_g_type,
+ _gda_postgres_reuseable_get_reserved_keywords_func,
+ _gda_postgres_reuseable_create_parser,
+ {
+ ._info = _gda_postgres_meta__info,
+ ._btypes = _gda_postgres_meta__btypes,
+ ._udt = _gda_postgres_meta__udt,
+ .udt = _gda_postgres_meta_udt,
+ ._udt_cols = _gda_postgres_meta__udt_cols,
+ .udt_cols = _gda_postgres_meta_udt_cols,
+ ._enums = _gda_postgres_meta__enums,
+ .enums = _gda_postgres_meta_enums,
+ ._domains = _gda_postgres_meta__domains,
+ .domains = _gda_postgres_meta_domains,
+ ._constraints_dom = _gda_postgres_meta__constraints_dom,
+ .constraints_dom = _gda_postgres_meta_constraints_dom,
+ ._el_types = _gda_postgres_meta__el_types,
+ .el_types = _gda_postgres_meta_el_types,
+ ._collations = _gda_postgres_meta__collations,
+ .collations = _gda_postgres_meta_collations,
+ ._character_sets = _gda_postgres_meta__character_sets,
+ .character_sets = _gda_postgres_meta_character_sets,
+ ._schemata = _gda_postgres_meta__schemata,
+ .schemata = _gda_postgres_meta_schemata,
+ ._tables_views = _gda_postgres_meta__tables_views,
+ .tables_views = _gda_postgres_meta_tables_views,
+ ._columns = _gda_postgres_meta__columns,
+ .columns = _gda_postgres_meta_columns,
+ ._view_cols = _gda_postgres_meta__view_cols,
+ .view_cols = _gda_postgres_meta_view_cols,
+ ._constraints_tab = _gda_postgres_meta__constraints_tab,
+ .constraints_tab = _gda_postgres_meta_constraints_tab,
+ ._constraints_ref = _gda_postgres_meta__constraints_ref,
+ .constraints_ref = _gda_postgres_meta_constraints_ref,
+ ._key_columns = _gda_postgres_meta__key_columns,
+ .key_columns = _gda_postgres_meta_key_columns,
+ ._check_columns = _gda_postgres_meta__check_columns,
+ .check_columns = _gda_postgres_meta_check_columns,
+ ._triggers = _gda_postgres_meta__triggers,
+ .triggers = _gda_postgres_meta_triggers,
+ ._routines = _gda_postgres_meta__routines,
+ .routines = _gda_postgres_meta_routines,
+ ._routine_col = _gda_postgres_meta__routine_col,
+ .routine_col = _gda_postgres_meta_routine_col,
+ ._routine_par = _gda_postgres_meta__routine_par,
+ .routine_par = _gda_postgres_meta_routine_par
+ }
+};
+
+GdaProviderReuseableOperations *
+_gda_postgres_reuseable_get_ops (void)
+{
+ return &_gda_postgres_reuseable;
+}
+
+/*
+ * Postgres type identification
+ */
+typedef struct {
+ gchar *name;
+ unsigned int oid; /* <=> to Postgres's Oid type */
+ GType type;
+ gchar *comments;
+ gchar *owner;
+} GdaPostgresTypeOid;
+
+static void
+gda_postgres_type_oid_free (GdaPostgresTypeOid *typedata)
+{
+ g_free (typedata->name);
+ g_free (typedata->comments);
+ g_free (typedata->owner);
+ g_free (typedata);
+}
+
+#ifdef GDA_DEBUG
+void
+_gda_postgres_test_keywords (void)
+{
+ V82test_keywords();
+ V83test_keywords();
+ V84test_keywords();
+}
+#endif
+
+GdaProviderReuseable *
+_gda_postgres_reuseable_new_data (const gchar *major, const gchar *minor)
+{
+ GdaPostgresReuseable *reuseable;
+ reuseable = g_new0 (GdaPostgresReuseable, 1);
+ reuseable->types_oid_hash = NULL;
+ reuseable->types_dbtype_hash = NULL;
+ _gda_postgres_provider_meta_init (NULL);
+
+ ((GdaProviderReuseable*)reuseable)->operations = &_gda_postgres_reuseable;
+
+ return (GdaProviderReuseable*) reuseable;
+}
+
+void
+_gda_postgres_reuseable_reset_data (GdaProviderReuseable *rdata)
+{
+ GdaPostgresReuseable *reuseable;
+ reuseable = (GdaPostgresReuseable*) rdata;
+ if (reuseable->types_dbtype_hash)
+ g_hash_table_destroy (reuseable->types_dbtype_hash);
+ if (reuseable->types_oid_hash)
+ g_hash_table_destroy (reuseable->types_oid_hash);
+
+ /* don't free reuseable->avoid_types */
+ g_free (reuseable->avoid_types_oids);
+ g_free (reuseable->any_type_oid);
+ memset (reuseable, 0, sizeof (GdaPostgresReuseable));
+}
+
+static GdaDataModel *
+execute_select (GdaConnection *cnc, GdaPostgresReuseable *rdata, const gchar *sql)
+{
+ GdaSqlParser *parser;
+ GdaStatement *stmt;
+ GdaDataModel *model;
+ parser = _gda_postgres_reuseable_create_parser ((GdaProviderReuseable*) rdata);
+
+ stmt = gda_sql_parser_parse_string (parser, sql, NULL, NULL);
+ g_object_unref (parser);
+ g_assert (stmt);
+
+ model = gda_connection_statement_execute_select (cnc, stmt, NULL, NULL);
+ g_object_unref (stmt);
+
+ return model;
+}
+
+static GType
+postgres_name_to_g_type (const gchar *name, const gchar *conv_func_name)
+{
+ /* default built in data types */
+ if (!strcmp (name, "bool"))
+ return G_TYPE_BOOLEAN;
+ else if (!strcmp (name, "int8"))
+ return G_TYPE_INT64;
+ else if (!strcmp (name, "int4") || !strcmp (name, "abstime"))
+ return G_TYPE_INT;
+ else if (!strcmp (name, "int2"))
+ return GDA_TYPE_SHORT;
+ else if (!strcmp (name, "float4"))
+ return G_TYPE_FLOAT;
+ else if (!strcmp (name, "float8"))
+ return G_TYPE_DOUBLE;
+ else if (!strcmp (name, "numeric"))
+ return GDA_TYPE_NUMERIC;
+ else if (!strncmp (name, "timestamp", 9))
+ return GDA_TYPE_TIMESTAMP;
+ else if (!strcmp (name, "date"))
+ return G_TYPE_DATE;
+ else if (!strncmp (name, "time", 4))
+ return GDA_TYPE_TIME;
+ else if (!strcmp (name, "point"))
+ return GDA_TYPE_GEOMETRIC_POINT;
+ else if (!strcmp (name, "oid"))
+ return GDA_TYPE_BLOB;
+ else if (!strcmp (name, "bytea"))
+ return GDA_TYPE_BINARY;
+
+ /* other data types, using the conversion function name as a hint */
+ if (!conv_func_name)
+ return G_TYPE_STRING;
+
+ if (!strncmp (conv_func_name, "int2", 4))
+ return GDA_TYPE_SHORT;
+ if (!strncmp (conv_func_name, "int4", 4))
+ return G_TYPE_INT;
+ if (!strncmp (conv_func_name, "int8", 4))
+ return G_TYPE_INT64;
+ if (!strncmp (conv_func_name, "float4", 6))
+ return G_TYPE_FLOAT;
+ if (!strncmp (conv_func_name, "float8", 6))
+ return G_TYPE_DOUBLE;
+ if (!strncmp (conv_func_name, "timestamp", 9))
+ return GDA_TYPE_TIMESTAMP;
+ if (!strncmp (conv_func_name, "time", 4))
+ return GDA_TYPE_TIME;
+ if (!strncmp (conv_func_name, "date", 4))
+ return G_TYPE_DATE;
+ if (!strncmp (conv_func_name, "bool", 4))
+ return G_TYPE_BOOLEAN;
+ if (!strncmp (conv_func_name, "oid", 3))
+ return GDA_TYPE_BLOB;
+ if (!strncmp (conv_func_name, "bytea", 5))
+ return GDA_TYPE_BINARY;
+ return G_TYPE_STRING;
+}
+
+void
+_gda_postgres_compute_types (GdaConnection *cnc, GdaPostgresReuseable *rdata)
+{
+ if (rdata->types_oid_hash)
+ return;
+
+ rdata->types_oid_hash = g_hash_table_new_full (g_int_hash, g_int_equal,
+ NULL, (GDestroyNotify) gda_postgres_type_oid_free);
+ rdata->types_dbtype_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ GdaDataModel *model, *model_avoid, *model_anyoid = NULL;
+ gint ncols, nrows, i;
+ gchar *avoid_types = NULL;
+ GString *string;
+
+ if (rdata->version_float < 7.3) {
+ gchar *query;
+ avoid_types = "'SET', 'cid', 'oid', 'int2vector', 'oidvector', 'regproc', 'smgr', 'tid', 'unknown', 'xid'";
+ /* main query to fetch infos about the data types */
+ query = g_strdup_printf ("SELECT pg_type.oid, typname, usename, obj_description(pg_type.oid) "
+ "FROM pg_type, pg_user "
+ "WHERE typowner=usesysid AND typrelid = 0 AND typname !~ '^_' "
+ "AND typname not in (%s) "
+ "ORDER BY typname", avoid_types);
+ model = execute_select (cnc, rdata, query);
+ g_free (query);
+
+ /* query to fetch non returned data types */
+ query = g_strdup_printf ("SELECT pg_type.oid FROM pg_type WHERE typname in (%s)", avoid_types);
+ model_avoid = execute_select (cnc, rdata, query);
+ g_free (query);
+ }
+ else {
+ gchar *query;
+ avoid_types = "'any', 'anyarray', 'anyelement', 'cid', 'cstring', 'int2vector', 'internal', 'language_handler', 'oidvector', 'opaque', 'record', 'refcursor', 'regclass', 'regoper', 'regoperator', 'regproc', 'regprocedure', 'regtype', 'SET', 'smgr', 'tid', 'trigger', 'unknown', 'void', 'xid'";
+
+ /* main query to fetch infos about the data types */
+ query = g_strdup_printf (
+ "SELECT t.oid, t.typname, u.usename, pg_catalog.obj_description(t.oid), t.typinput "
+ "FROM pg_catalog.pg_type t LEFT JOIN pg_catalog.pg_user u ON (t.typowner=u.usesysid), pg_catalog.pg_namespace n "
+ "WHERE n.oid = t.typnamespace "
+ "AND pg_catalog.pg_type_is_visible(t.oid) "
+ /*--AND (n.nspname = 'public' OR n.nspname = 'pg_catalog')*/
+ "AND typname !~ '^_' "
+ "AND (t.typrelid = 0 OR "
+ "(SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) "
+ "AND t.typname not in (%s) "
+ "ORDER BY typname", avoid_types);
+ model = execute_select (cnc, rdata, query);
+ g_free (query);
+
+ /* query to fetch non returned data types */
+ query = g_strdup_printf ("SELECT t.oid FROM pg_catalog.pg_type t WHERE t.typname in (%s)",
+ avoid_types);
+ model_avoid = execute_select (cnc, rdata, query);
+ g_free (query);
+
+ /* query to fetch the oid of the 'any' data type */
+ model_anyoid = execute_select (cnc, rdata,
+ "SELECT t.oid FROM pg_catalog.pg_type t WHERE t.typname = 'any'");
+ }
+
+ if (!model || !model_avoid ||
+ ((rdata->version_float >= 7.3) && !model_anyoid)) {
+ if (model)
+ g_object_unref (model);
+ if (model_avoid)
+ g_object_unref (model_avoid);
+ if (model_anyoid)
+ g_object_unref (model_anyoid);
+ return;
+ }
+
+ /* Data types returned to the Gda client */
+ nrows = gda_data_model_get_n_rows (model);
+ ncols = gda_data_model_get_n_columns (model);
+ if (nrows == 0)
+ g_warning ("PostgreSQL provider did not find any data type (expect some mis-behaviours) please report the error to bugzilla.gnome.org");
+ for (i = 0; i < nrows; i++) {
+ const GValue *conv_func_name = NULL;
+ const GValue *values[4];
+ gint j;
+ gboolean allread = TRUE;
+ if (ncols >= 5)
+ conv_func_name = gda_data_model_get_value_at (model, 4, i, NULL);
+ for (j = 0; j < 4; j++) {
+ values[j] = gda_data_model_get_value_at (model, j, i, NULL);
+ if (!values [j]) {
+ allread = FALSE;
+ break;
+ }
+ }
+ if (allread && (G_VALUE_TYPE (values[1]) == G_TYPE_STRING)) {
+ GdaPostgresTypeOid *td;
+ td = g_new0 (GdaPostgresTypeOid, 1);
+ td->name = g_value_dup_string (values [1]);
+ td->oid = (guint) g_ascii_strtoull (g_value_get_string (values[0]), NULL, 10);
+ td->type = postgres_name_to_g_type (td->name,
+ conv_func_name ? g_value_get_string (conv_func_name) : NULL);
+ if (G_VALUE_TYPE (values[3]) == G_TYPE_STRING)
+ td->comments = g_value_dup_string (values [3]);
+ if (G_VALUE_TYPE (values[2]) == G_TYPE_STRING)
+ td->owner = g_value_dup_string (values [2]);
+
+ g_hash_table_insert (rdata->types_oid_hash, &(td->oid), td);
+ g_hash_table_insert (rdata->types_dbtype_hash, &(td->name), td);
+ }
+ }
+
+ /* Make a string of data types internal to postgres and not returned, for future queries */
+ string = NULL;
+ nrows = gda_data_model_get_n_rows (model_avoid);
+ for (i = 0; i < nrows; i++) {
+ const GValue *cvalue;
+
+ cvalue = gda_data_model_get_value_at (model_avoid, 0, i, NULL);
+ if (cvalue && (G_VALUE_TYPE (cvalue) == G_TYPE_STRING)) {
+ if (!string)
+ string = g_string_new (g_value_get_string (cvalue));
+ else {
+ g_string_append (string, ", ");
+ g_string_append (string, g_value_get_string (cvalue));
+ }
+ }
+ }
+ rdata->avoid_types = avoid_types;
+ if (string)
+ rdata->avoid_types_oids = g_string_free (string, FALSE);
+
+ g_object_unref (model);
+ g_object_unref (model_avoid);
+
+ /* make a string of the oid of type 'any' */
+ rdata->any_type_oid = NULL;
+ if (model_anyoid) {
+ if (gda_data_model_get_n_rows (model_anyoid) == 1) {
+ const GValue *cvalue;
+ cvalue = gda_data_model_get_value_at (model_anyoid, 0, 0, NULL);
+ if (cvalue && (G_VALUE_TYPE (cvalue) == G_TYPE_STRING))
+ rdata->any_type_oid = g_value_dup_string (cvalue);
+ }
+ g_object_unref (model_anyoid);
+ }
+}
+
+GType
+_gda_postgres_type_oid_to_gda (GdaConnection *cnc, GdaPostgresReuseable *rdata, unsigned int postgres_oid)
+{
+ GdaPostgresTypeOid *type;
+ guint id;
+ id = postgres_oid;
+
+ _gda_postgres_compute_types (cnc, rdata);
+ type = g_hash_table_lookup (rdata->types_oid_hash, &id);
+ if (type)
+ return type->type;
+ else
+ return G_TYPE_STRING;
+}
+
+GType
+_gda_postgres_reuseable_get_g_type (GdaConnection *cnc, GdaProviderReuseable *rdata, const gchar *db_type)
+{
+ GdaPostgresTypeOid *type;
+ g_return_val_if_fail (db_type, GDA_TYPE_NULL);
+
+ _gda_postgres_compute_types (cnc, (GdaPostgresReuseable*)rdata);
+ type = g_hash_table_lookup (((GdaPostgresReuseable*)rdata)->types_dbtype_hash, db_type);
+ if (type)
+ return type->type;
+ else
+ return GDA_TYPE_NULL;
+}
+
+
+GdaSqlReservedKeywordsFunc
+_gda_postgres_reuseable_get_reserved_keywords_func (GdaProviderReuseable *rdata)
+{
+ if (rdata && rdata->version_minor) {
+ switch (*rdata->version_minor) {
+ case '8':
+ if (rdata->version_minor[1] == '2')
+ return V82is_keyword;
+ if (rdata->version_minor[1] == '3')
+ return V83is_keyword;
+ if (rdata->version_minor[1] == '4')
+ return V84is_keyword;
+ return V84is_keyword;
+ default:
+ return V84is_keyword;
+ break;
+ }
+ }
+ return V84is_keyword;
+}
+
+GdaSqlParser *
+_gda_postgres_reuseable_create_parser (GdaProviderReuseable *rdata)
+{
+ return GDA_SQL_PARSER (g_object_new (GDA_TYPE_POSTGRES_PARSER, NULL));
+}
diff --git a/providers/reuseable/postgres/gda-postgres-reuseable.h b/providers/reuseable/postgres/gda-postgres-reuseable.h
new file mode 100644
index 0000000..7765e13
--- /dev/null
+++ b/providers/reuseable/postgres/gda-postgres-reuseable.h
@@ -0,0 +1,78 @@
+/* GDA provider
+ * 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; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GDA_POSTGRES_REUSEABLE_H__
+#define __GDA_POSTGRES_REUSEABLE_H__
+
+#include "../gda-provider-reuseable.h"
+#include "gda-postgres-meta.h"
+
+G_BEGIN_DECLS
+
+/*
+ * Specific structure for PostgreSQL
+ *
+ * Note: each GdaPostgresTypeOid is managed by the types_oid_hash hash table, and not by the types_dbtype_hash
+ * even though it's also referenced there.
+ */
+typedef struct {
+ GdaProviderReuseable parent;
+
+ gfloat version_float;
+ GHashTable *types_oid_hash; /* key = an unsigned int * (for OID) , value = a #GdaPostgresTypeOid */
+ GHashTable *types_dbtype_hash; /* key = a gchar *, value = a #GdaPostgresTypeOid */
+
+ /* Internal data types hidden from the exterior */
+ gchar *avoid_types; /* may be %NULL */
+ gchar *avoid_types_oids;
+ gchar *any_type_oid; /* oid for the 'any' data type, used to fetch aggregates and functions, may be %NULL */
+} GdaPostgresReuseable;
+
+/*
+ * Reuseable implementation
+ */
+GdaProviderReuseable *_gda_postgres_reuseable_new_data (const gchar *major, const gchar *minor);
+void _gda_postgres_reuseable_reset_data (GdaProviderReuseable *rdata);
+GType _gda_postgres_reuseable_get_g_type (GdaConnection *cnc, GdaProviderReuseable *rdata, const gchar *db_type);
+GdaSqlReservedKeywordsFunc _gda_postgres_reuseable_get_reserved_keywords_func (GdaProviderReuseable *rdata);
+GdaSqlParser *_gda_postgres_reuseable_create_parser (GdaProviderReuseable *rdata);
+
+/*
+ * entry point
+ */
+GdaProviderReuseableOperations *_gda_postgres_reuseable_get_ops (void);
+
+/*
+ * Specific API
+ */
+void _gda_postgres_compute_types (GdaConnection *cnc, GdaPostgresReuseable *rdata);
+
+GType _gda_postgres_type_oid_to_gda (GdaConnection *cnc, GdaPostgresReuseable *rdata,
+ unsigned int postgres_oid);
+
+#ifdef GDA_DEBUG
+void _gda_postgres_test_keywords (void);
+#endif
+
+G_END_DECLS
+
+#endif
+
diff --git a/providers/postgres/gen_def.c b/providers/reuseable/postgres/gen_def.c
similarity index 100%
rename from providers/postgres/gen_def.c
rename to providers/reuseable/postgres/gen_def.c
diff --git a/providers/postgres/keywords_82.list b/providers/reuseable/postgres/keywords_82.list
similarity index 100%
rename from providers/postgres/keywords_82.list
rename to providers/reuseable/postgres/keywords_82.list
diff --git a/providers/postgres/keywords_83.list b/providers/reuseable/postgres/keywords_83.list
similarity index 100%
rename from providers/postgres/keywords_83.list
rename to providers/reuseable/postgres/keywords_83.list
diff --git a/providers/postgres/keywords_84.list b/providers/reuseable/postgres/keywords_84.list
similarity index 100%
rename from providers/postgres/keywords_84.list
rename to providers/reuseable/postgres/keywords_84.list
diff --git a/providers/postgres/parser.y b/providers/reuseable/postgres/parser.y
similarity index 100%
rename from providers/postgres/parser.y
rename to providers/reuseable/postgres/parser.y
diff --git a/providers/reuseable/reuse-all.c b/providers/reuseable/reuse-all.c
new file mode 100644
index 0000000..332fe47
--- /dev/null
+++ b/providers/reuseable/reuse-all.c
@@ -0,0 +1,55 @@
+/* GDA provider
+ * 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 "reuse-all.h"
+
+#include "postgres/gda-postgres-reuseable.h"
+
+/**
+ * _gda_provider_reuseable_new
+ * @provider_name: a database provider name
+ * @version_major: the major version number, or %NULL
+ * @version_minor: the minor version number, or %NULL
+ *
+ * Entry point to get provider's reuseable features for a provider.
+ *
+ * Returns: a new GdaProviderReuseable pointer, or %NULL
+ */
+GdaProviderReuseable *
+_gda_provider_reuseable_new (const gchar *provider_name,
+ const gchar *version_major,
+ const gchar *version_minor)
+{
+ GdaProviderReuseable *reuseable = NULL;
+ GdaProviderReuseableOperations *ops = NULL;
+ g_return_val_if_fail (provider_name && *provider_name, NULL);
+ if (!strcmp (provider_name, "PostgreSQL"))
+ ops = _gda_postgres_reuseable_get_ops ();
+
+ if (ops) {
+ reuseable = ops->re_new_data (version_major, version_minor);
+ g_assert (reuseable->operations == ops);
+ }
+
+ return reuseable;
+}
diff --git a/providers/reuseable/reuse-all.h b/providers/reuseable/reuse-all.h
new file mode 100644
index 0000000..d3c79a3
--- /dev/null
+++ b/providers/reuseable/reuse-all.h
@@ -0,0 +1,37 @@
+/* GDA provider
+ * 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; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GDA_REUSEABLE_H__
+#define __GDA_REUSEABLE_H__
+
+#include "gda-provider-reuseable.h"
+
+G_BEGIN_DECLS
+
+GdaProviderReuseable *_gda_provider_reuseable_new (const gchar *provider_name,
+ const gchar *version_major,
+ const gchar *version_minor);
+
+
+G_END_DECLS
+
+#endif
+
diff --git a/providers/web/Makefile.am b/providers/web/Makefile.am
new file mode 100644
index 0000000..2dbb2fb
--- /dev/null
+++ b/providers/web/Makefile.am
@@ -0,0 +1,69 @@
+providerdir=$(libdir)/libgda-$(GDA_ABI_MAJOR_VERSION).$(GDA_ABI_MINOR_VERSION)/providers
+provider_LTLIBRARIES = libgda-web.la
+
+#Rem: WEB_CFLAGS and WEB_LIBS are the compile and link flags necessary to use the
+# C API. It is specific to the API and should be computed in the configure.in script.
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libgda \
+ -I$(top_builddir) \
+ $(LIBGDA_CFLAGS) $(LIBSOUP_CFLAGS)
+
+libgda_web_la_SOURCES = \
+ gda-web-blob-op.c \
+ gda-web-blob-op.h \
+ gda-web-ddl.c \
+ gda-web-ddl.h \
+ gda-web-provider.c \
+ gda-web-provider.h \
+ gda-web-pstmt.h \
+ gda-web-pstmt.c \
+ gda-web-meta.c \
+ gda-web-meta.h \
+ gda-web-recordset.c \
+ gda-web-recordset.h \
+ gda-web-util.h \
+ gda-web-util.c \
+ gda-web.h \
+ $(top_srcdir)/libgda/global.h \
+ $(top_srcdir)/libgda/md5.h \
+ $(top_srcdir)/libgda/md5c.c \
+ libmain.c
+
+libgda_web_la_LDFLAGS = -export-dynamic -module -avoid-version $(NO_UNDEFINED) $(LIBTOOL_PROV_EXPORT_OPTIONS)
+libgda_web_la_LIBADD = \
+ $(top_builddir)/providers/reuseable/libreuseable.la \
+ $(top_builddir)/libgda/libgda-4.0.la \
+ $(LIBGDA_LIBS) $(LIBSOUP_LIBS)
+
+xmldir = $(datadir)/libgda-4.0
+xml_in_files = \
+ web_specs_dsn.xml.in \
+ web_specs_auth.xml.in
+
+ INTLTOOL_XML_RULE@
+
+xml_DATA = $(xml_in_files:.xml.in=.xml)
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libgda-web-4.0.pc
+
+phpdir = $(datadir)/libgda-4.0/php
+php_DATA = \
+ php/README \
+ php/gda-clean.php \
+ php/gda-config.php \
+ php/gda-exception.php \
+ php/gda-front.php \
+ php/gda-meta.php \
+ php/gda-setup.php \
+ php/gda-tester.php \
+ php/gda-utils.php \
+ php/gda-worker.php
+
+
+EXTRA_DIST = $(xml_in_files) libgda-web-4.0.pc.in \
+ $(php_DATA)
+
+DISTCLEANFILES = $(xml_DATA)
diff --git a/providers/web/README b/providers/web/README
new file mode 100644
index 0000000..86d6437
--- /dev/null
+++ b/providers/web/README
@@ -0,0 +1,63 @@
+GDA Web provider
+================
+
+Unlike most other provider, this provider does not link to a database
+specific library but rather acts as a web client for a web server
+running some PHP scripts which acutally make the web server connect
+to a database.
+
+This provider enables a Libgda application to connect to a database
+which is accessible only behind a web server (as many hosting databases
+are) which can execute some PHP scripts and can connect to the
+database. It is necessary to install some specific PHP scripts on the
+web server which handle the connection and implement the protocol
+described hereafter. Also the PHP interpreter need to be compiled with
+the SimpleXML extension.
+
+It is recommended to use an SSL/TLS web server to avoid clear text
+messages being read by third parties, but even with non SSL/TLS
+connections, the protocol features a reasonnable security by:
+* including a message hash in each message to avoid man in the middle
+ message contents tampering
+* making the server generate a challenge which has to be encoded in the
+ next request by the client to authenticate the client
+* avoiding sending any database name, server name, user name or password
+ in any message
+* having to define, on the web server side, connections which can be opened
+ through the provider; each connection is composed of an arbitrary name,
+ an arbitrary password, and the real connection string the web server
+ will use to actually open the connection to the database.
+
+The exchanges between the provider (client) and the web server are based
+on XML as each message has the following structure:
+
+"
+[message hash]
+<?xml version="1.0"?>
+...
+"
+
+where the [message hash] is a hash for the remaining part of the
+message which is a valid XML message, compiled using the HMAC MD5
+algorithm (see http://en.wikipedia.org/wiki/HMAC) using (except for
+bootstrapping the authentication) a key which is defined as:
+"
+[connection name]/AND/[password]
+"
+where [connection name] and [password] have to represent a connection
+described on the web server side.
+
+For example opening a connection using the web provider from the gda-sql
+tool can be achieved by the shell command:
+
+[prompt]> gda-sql "web://none:MyPass1 HOST=example com;PATH=libgda-php;DB_NAME=cnc1;PORT=8081;SECRET=MySecret"
+
+for:
+* the example.com web server
+* on port 8081
+* with a server secret: MySecret
+* the /libgda-php script location
+* the defined connection cnc1
+* the password MyPass1 (note that the gda-sql command requires a username when a password is specified which is
+ the reason for the dummy "none" user name here to avoid any user name prompting, or use the -p command line option
+ otherwise)
diff --git a/providers/web/gda-web-blob-op.c b/providers/web/gda-web-blob-op.c
new file mode 100644
index 0000000..89f0002
--- /dev/null
+++ b/providers/web/gda-web-blob-op.c
@@ -0,0 +1,226 @@
+/* GDA Web provider
+ * Copyright (C) 2008 The GNOME Foundation
+ *
+ * AUTHORS:
+ * TO_ADD: your name and email
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public 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 <glib/gi18n-lib.h>
+#include <string.h>
+#include <libgda/libgda.h>
+#include "gda-web.h"
+#include "gda-web-blob-op.h"
+
+struct _GdaWebBlobOpPrivate {
+ GdaConnection *cnc;
+ /* TO_ADD: specific information describing a Blob in the C API */
+};
+
+static void gda_web_blob_op_class_init (GdaWebBlobOpClass *klass);
+static void gda_web_blob_op_init (GdaWebBlobOp *blob,
+ GdaWebBlobOpClass *klass);
+static void gda_web_blob_op_finalize (GObject *object);
+
+static glong gda_web_blob_op_get_length (GdaBlobOp *op);
+static glong gda_web_blob_op_read (GdaBlobOp *op, GdaBlob *blob, glong offset, glong size);
+static glong gda_web_blob_op_write (GdaBlobOp *op, GdaBlob *blob, glong offset);
+
+static GObjectClass *parent_class = NULL;
+
+/*
+ * Object init and finalize
+ */
+GType
+gda_web_blob_op_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+ static const GTypeInfo info = {
+ sizeof (GdaWebBlobOpClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gda_web_blob_op_class_init,
+ NULL,
+ NULL,
+ sizeof (GdaWebBlobOp),
+ 0,
+ (GInstanceInitFunc) gda_web_blob_op_init
+ };
+ g_static_mutex_lock (®istering);
+ if (type == 0)
+ type = g_type_register_static (GDA_TYPE_BLOB_OP, "GdaWebBlobOp", &info, 0);
+ g_static_mutex_unlock (®istering);
+ }
+ return type;
+}
+
+static void
+gda_web_blob_op_init (GdaWebBlobOp *op,
+ GdaWebBlobOpClass *klass)
+{
+ g_return_if_fail (GDA_IS_WEB_BLOB_OP (op));
+
+ op->priv = g_new0 (GdaWebBlobOpPrivate, 1);
+
+ /* initialize specific structure */
+ TO_IMPLEMENT;
+}
+
+static void
+gda_web_blob_op_class_init (GdaWebBlobOpClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GdaBlobOpClass *blob_class = GDA_BLOB_OP_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->finalize = gda_web_blob_op_finalize;
+ blob_class->get_length = gda_web_blob_op_get_length;
+ blob_class->read = gda_web_blob_op_read;
+ blob_class->write = gda_web_blob_op_write;
+}
+
+static void
+gda_web_blob_op_finalize (GObject * object)
+{
+ GdaWebBlobOp *bop = (GdaWebBlobOp *) object;
+
+ g_return_if_fail (GDA_IS_WEB_BLOB_OP (bop));
+
+ /* free specific information */
+ TO_IMPLEMENT;
+
+ g_free (bop->priv);
+ bop->priv = NULL;
+
+ parent_class->finalize (object);
+}
+
+GdaBlobOp *
+gda_web_blob_op_new (GdaConnection *cnc)
+{
+ GdaWebBlobOp *bop;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+
+ bop = g_object_new (GDA_TYPE_WEB_BLOB_OP, NULL);
+ bop->priv->cnc = cnc;
+
+ return GDA_BLOB_OP (bop);
+}
+
+/*
+ * Get length request
+ */
+static glong
+gda_web_blob_op_get_length (GdaBlobOp *op)
+{
+ GdaWebBlobOp *bop;
+
+ g_return_val_if_fail (GDA_IS_WEB_BLOB_OP (op), -1);
+ bop = GDA_WEB_BLOB_OP (op);
+ g_return_val_if_fail (bop->priv, -1);
+ g_return_val_if_fail (GDA_IS_CONNECTION (bop->priv->cnc), -1);
+
+ TO_IMPLEMENT;
+ return -1;
+}
+
+/*
+ * Blob read request
+ */
+static glong
+gda_web_blob_op_read (GdaBlobOp *op, GdaBlob *blob, glong offset, glong size)
+{
+ GdaWebBlobOp *bop;
+ GdaBinary *bin;
+
+ g_return_val_if_fail (GDA_IS_WEB_BLOB_OP (op), -1);
+ bop = GDA_WEB_BLOB_OP (op);
+ g_return_val_if_fail (bop->priv, -1);
+ g_return_val_if_fail (GDA_IS_CONNECTION (bop->priv->cnc), -1);
+ if (offset >= G_MAXINT)
+ return -1;
+ g_return_val_if_fail (blob, -1);
+
+ bin = (GdaBinary *) blob;
+ if (bin->data)
+ g_free (bin->data);
+ bin->data = g_new0 (guchar, size);
+ bin->binary_length = 0;
+
+ /* fetch blob data using C API into bin->data, and set bin->binary_length */
+ TO_IMPLEMENT;
+
+ return bin->binary_length;
+}
+
+/*
+ * Blob write request
+ */
+static glong
+gda_web_blob_op_write (GdaBlobOp *op, GdaBlob *blob, glong offset)
+{
+ GdaWebBlobOp *bop;
+ GdaBinary *bin;
+ glong nbwritten = -1;
+
+ g_return_val_if_fail (GDA_IS_WEB_BLOB_OP (op), -1);
+ bop = GDA_WEB_BLOB_OP (op);
+ g_return_val_if_fail (bop->priv, -1);
+ g_return_val_if_fail (GDA_IS_CONNECTION (bop->priv->cnc), -1);
+ g_return_val_if_fail (blob, -1);
+
+ if (blob->op && (blob->op != op)) {
+ /* use data through blob->op */
+ #define buf_size 16384
+ gint nread = 0;
+ GdaBlob *tmpblob = g_new0 (GdaBlob, 1);
+ gda_blob_set_op (tmpblob, blob->op);
+
+ nbwritten = 0;
+
+ for (nread = gda_blob_op_read (tmpblob->op, tmpblob, nbwritten, buf_size);
+ nread > 0;
+ nread = gda_blob_op_read (tmpblob->op, tmpblob, nbwritten, buf_size)) {
+ glong tmp_written;
+
+ tmp_written = -1; TO_IMPLEMENT;
+
+ if (tmp_written < 0) {
+ /* treat error */
+ gda_blob_free ((gpointer) tmpblob);
+ return -1;
+ }
+ nbwritten += tmp_written;
+ if (nread < buf_size)
+ /* nothing more to read */
+ break;
+ }
+ gda_blob_free ((gpointer) tmpblob);
+ }
+ else {
+ /* write blob using bin->data and bin->binary_length */
+ bin = (GdaBinary *) blob;
+ nbwritten = -1; TO_IMPLEMENT;
+ }
+
+ return nbwritten;
+}
diff --git a/providers/web/gda-web-blob-op.h b/providers/web/gda-web-blob-op.h
new file mode 100644
index 0000000..7cb4efd
--- /dev/null
+++ b/providers/web/gda-web-blob-op.h
@@ -0,0 +1,57 @@
+/* GDA Web provider
+ * Copyright (C) 2008 The GNOME Foundation
+ *
+ * AUTHORS:
+ * TO_ADD: your name and email
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public 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_WEB_BLOB_OP_H__
+#define __GDA_WEB_BLOB_OP_H__
+
+#include <libgda/gda-blob-op.h>
+
+G_BEGIN_DECLS
+
+#define GDA_TYPE_WEB_BLOB_OP (gda_web_blob_op_get_type())
+#define GDA_WEB_BLOB_OP(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_WEB_BLOB_OP, GdaWebBlobOp))
+#define GDA_WEB_BLOB_OP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_WEB_BLOB_OP, GdaWebBlobOpClass))
+#define GDA_IS_WEB_BLOB_OP(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_WEB_BLOB_OP))
+#define GDA_IS_WEB_BLOB_OP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDA_TYPE_WEB_BLOB_OP))
+
+typedef struct _GdaWebBlobOp GdaWebBlobOp;
+typedef struct _GdaWebBlobOpClass GdaWebBlobOpClass;
+typedef struct _GdaWebBlobOpPrivate GdaWebBlobOpPrivate;
+
+struct _GdaWebBlobOp {
+ GdaBlobOp parent;
+ GdaWebBlobOpPrivate *priv;
+};
+
+struct _GdaWebBlobOpClass {
+ GdaBlobOpClass parent_class;
+};
+
+GType gda_web_blob_op_get_type (void) G_GNUC_CONST;
+GdaBlobOp *gda_web_blob_op_new (GdaConnection *cnc);
+
+/* TO_ADD: more convenient API to create a GdaBlobOp with some specific information as argument */
+
+G_END_DECLS
+
+#endif
+
diff --git a/providers/web/gda-web-ddl.c b/providers/web/gda-web-ddl.c
new file mode 100644
index 0000000..4ee9138
--- /dev/null
+++ b/providers/web/gda-web-ddl.c
@@ -0,0 +1,145 @@
+/* GDA Web Provider
+ * Copyright (C) 2008 The GNOME Foundation
+ *
+ * AUTHORS:
+ * TO_ADD: your name and email
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public 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 <glib/gi18n-lib.h>
+#include <libgda/libgda.h>
+#include "gda-web-ddl.h"
+
+gchar *
+gda_web_render_CREATE_TABLE (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error)
+{
+ GString *string;
+ const GValue *value;
+ gboolean allok = TRUE;
+ gboolean hasfields = FALSE;
+ gint nrows;
+ gint i;
+ gboolean first;
+ GSList *pkfields = NULL; /* list of GValue* composing the pkey */
+ gint nbpkfields = 0;
+ gchar *tmp;
+
+ /* CREATE TABLE */
+ string = g_string_new ("CREATE TABLE ");
+
+ tmp = gda_server_operation_get_sql_identifier_at (op, cnc, provider, "/TABLE_DEF_P/TABLE_NAME");
+ g_string_append (string, tmp);
+ g_free (tmp);
+ g_string_append (string, " (");
+
+ /* FIELDS */
+ if (allok) {
+ GdaServerOperationNode *node;
+
+ node = gda_server_operation_get_node_info (op, "/FIELDS_A");
+ g_assert (node);
+
+ /* finding if there is a composed primary key */
+ nrows = gda_data_model_get_n_rows (node->model);
+ for (i = 0; i < nrows; i++) {
+ value = gda_server_operation_get_value_at (op, "/FIELDS_A/@COLUMN_PKEY/%d", i);
+ if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) && g_value_get_boolean (value)) {
+ tmp = gda_server_operation_get_sql_identifier_at (op, cnc, provider,
+ "/FIELDS_A/@COLUMN_NAME/%d", i);
+ pkfields = g_slist_append (pkfields, tmp);
+ nbpkfields++;
+ }
+ }
+
+ /* manually defined fields */
+ first = TRUE;
+ for (i = 0; i < nrows; i++) {
+ hasfields = TRUE;
+ if (first)
+ first = FALSE;
+ else
+ g_string_append (string, ", ");
+
+ tmp = gda_server_operation_get_sql_identifier_at (op, cnc, provider,
+ "/FIELDS_A/@COLUMN_NAME/%d", i);
+ g_string_append (string, tmp);
+ g_free (tmp);
+ g_string_append_c (string, ' ');
+
+ value = gda_server_operation_get_value_at (op, "/FIELDS_A/@COLUMN_TYPE/%d", i);
+ g_string_append (string, g_value_get_string (value));
+
+ value = gda_server_operation_get_value_at (op, "/FIELDS_A/@COLUMN_DEFAULT/%d", i);
+ if (value && G_VALUE_HOLDS (value, G_TYPE_STRING)) {
+ const gchar *str = g_value_get_string (value);
+ if (str && *str) {
+ g_string_append (string, " DEFAULT ");
+ g_string_append (string, str);
+ }
+ }
+
+ value = gda_server_operation_get_value_at (op, "/FIELDS_A/@COLUMN_NNUL/%d", i);
+ if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) && g_value_get_boolean (value))
+ g_string_append (string, " NOT NULL");
+
+ value = gda_server_operation_get_value_at (op, "/FIELDS_A/@COLUMN_UNIQUE/%d", i);
+ if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) && g_value_get_boolean (value))
+ g_string_append (string, " UNIQUE");
+
+ if (nbpkfields == 1) {
+ value = gda_server_operation_get_value_at (op, "/FIELDS_A/@COLUMN_PKEY/%d", i);
+ if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) && g_value_get_boolean (value))
+ g_string_append (string, " PRIMARY KEY");
+ }
+
+ value = gda_server_operation_get_value_at (op, "/FIELDS_A/@COLUMN_CHECK/%d", i);
+ if (value && G_VALUE_HOLDS (value, G_TYPE_STRING)) {
+ const gchar *str = g_value_get_string (value);
+ if (str && *str) {
+ g_string_append (string, " CHECK (");
+ g_string_append (string, str);
+ g_string_append_c (string, ')');
+ }
+ }
+ }
+ }
+
+ /* composed primary key */
+ if (nbpkfields > 1) {
+ GSList *list;
+
+ g_string_append (string, ", PRIMARY KEY (");
+ for (list = pkfields; list; list = list->next) {
+ if (list != pkfields)
+ g_string_append (string, ", ");
+ g_string_append (string, (gchar*) list->data);
+ }
+ g_string_append_c (string, ')');
+ }
+ g_slist_foreach (pkfields, (GFunc) g_free, NULL);
+ g_slist_free (pkfields);
+
+ g_string_append (string, ")");
+
+ if (!hasfields) {
+ allok = FALSE;
+ g_set_error (error, 0, 0, "%s", _("Table to create must have at least one row"));
+ }
+
+ return g_string_free (string, FALSE);
+}
diff --git a/providers/web/gda-web-ddl.h b/providers/web/gda-web-ddl.h
new file mode 100644
index 0000000..6d3bd28
--- /dev/null
+++ b/providers/web/gda-web-ddl.h
@@ -0,0 +1,34 @@
+/* GDA Web provider
+ * Copyright (C) 2008 The GNOME Foundation
+ *
+ * AUTHORS:
+ * TO_ADD: your name and email
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * 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; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GDA_WEB_DDL_H__
+#define __GDA_WEB_DDL_H__
+
+#include <libgda/gda-server-provider.h>
+
+G_BEGIN_DECLS
+
+gchar *gda_web_render_CREATE_TABLE (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error);
+G_END_DECLS
+
+#endif
+
diff --git a/providers/web/gda-web-meta.c b/providers/web/gda-web-meta.c
new file mode 100644
index 0000000..e4bc0a3
--- /dev/null
+++ b/providers/web/gda-web-meta.c
@@ -0,0 +1,1382 @@
+/* GDA provider
+ * 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 "gda-web.h"
+#include "gda-web-meta.h"
+#include "gda-web-provider.h"
+#include <libgda/gda-meta-store.h>
+#include <libgda/sql-parser/gda-sql-parser.h>
+#include <glib/gi18n-lib.h>
+#include <libgda/gda-server-provider-extra.h>
+#include <libgda/gda-connection-private.h>
+#include <libgda/gda-data-model-array.h>
+#include <libgda/gda-set.h>
+#include <libgda/gda-holder.h>
+
+#include "gda-web-util.h"
+
+/*
+ * Meta initialization
+ */
+void
+_gda_web_provider_meta_init (GdaServerProvider *provider)
+{
+}
+
+/*
+ * ... is a list of (arg name, arg value) as strings, terminated with NULL
+ */
+static GdaDataModel *
+run_meta_command_args (GdaConnection *cnc, WebConnectionData *cdata, const gchar *type, GError **error, ...)
+{
+ /* send message */
+ xmlDocPtr doc;
+ gchar status;
+ gchar *tmp, *token;
+ GString *string;
+ va_list ap;
+#define MSG__TEMPL "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" \
+ "<request>\n" \
+ " <token>%s</token>\n" \
+ " <cmd type=\"%s\">META%s</cmd>\n" \
+ "</request>"
+
+ string = g_string_new ("");
+ va_start (ap, error);
+ for (tmp = va_arg (ap, gchar*); tmp; tmp = va_arg (ap, gchar*)) {
+ gchar *argval;
+ xmlChar *xargval;
+ argval = va_arg (ap, gchar*);
+ xargval = xmlEncodeSpecialChars (NULL, BAD_CAST argval);
+ g_string_append_printf (string, "<arg name=\"%s\">%s</arg>", tmp, (gchar*) xargval);
+ xmlFree (xargval);
+ }
+ va_end (ap);
+
+ token = _gda_web_compute_token (cdata);
+ tmp = g_strdup_printf (MSG__TEMPL, token, type, string->str);
+ g_string_free (string, TRUE);
+ g_free (token);
+
+ doc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_META, tmp, cdata->key, &status);
+ g_free (tmp);
+ if (!doc)
+ return FALSE;
+ if (status != 'O') {
+ _gda_web_set_connection_error_from_xmldoc (cnc, doc, error);
+ xmlFreeDoc (doc);
+ return FALSE;
+ }
+
+ /* compute returned data model */
+ xmlNodePtr root, node;
+ GdaDataModel *model = NULL;
+ root = xmlDocGetRootElement (doc);
+ for (node = root->children; node; node = node->next) {
+ if (!strcmp ((gchar*) node->name, "gda_array")) {
+ model = gda_data_model_import_new_xml_node (node);
+ break;
+ }
+ }
+ xmlFreeDoc (doc);
+ /*gda_data_model_dump (model, NULL);*/
+
+ if (! model)
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+ GDA_SERVER_PROVIDER_INTERNAL_ERROR, "%s",
+ _("Can't import data from web server"));
+ return model;
+}
+
+static GdaDataModel *
+run_meta_command (GdaConnection *cnc, WebConnectionData *cdata, const gchar *type, GError **error)
+{
+ return run_meta_command_args (cnc, cdata, type, error, NULL);
+}
+
+gboolean
+_gda_web_meta__info (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._info)
+ return cdata->reuseable->operations->re_meta_funcs._info (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ model = run_meta_command (cnc, cdata, "info", error);
+ if (!model)
+ return FALSE;
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta__btypes (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._btypes)
+ return cdata->reuseable->operations->re_meta_funcs._btypes (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ model = run_meta_command (cnc, cdata, "btypes", error);
+ if (!model)
+ return FALSE;
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta__udt (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._udt)
+ return cdata->reuseable->operations->re_meta_funcs._udt (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta_udt (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *udt_catalog, const GValue *udt_schema)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.udt)
+ return cdata->reuseable->operations->re_meta_funcs.udt (NULL, cnc, store,
+ context, error, udt_catalog,
+ udt_schema);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+
+gboolean
+_gda_web_meta__udt_cols (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._udt_cols)
+ return cdata->reuseable->operations->re_meta_funcs._udt_cols (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta_udt_cols (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *udt_catalog, const GValue *udt_schema, const GValue *udt_name)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.udt_cols)
+ return cdata->reuseable->operations->re_meta_funcs.udt_cols (NULL, cnc, store,
+ context, error,
+ udt_catalog, udt_schema,
+ udt_name);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta__enums (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._enums)
+ return cdata->reuseable->operations->re_meta_funcs._enums (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta_enums (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *udt_catalog, const GValue *udt_schema, const GValue *udt_name)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.enums)
+ return cdata->reuseable->operations->re_meta_funcs.enums (NULL, cnc, store,
+ context, error,
+ udt_catalog, udt_schema,
+ udt_name);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+
+gboolean
+_gda_web_meta__domains (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._domains)
+ return cdata->reuseable->operations->re_meta_funcs._domains (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta_domains (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *domain_catalog, const GValue *domain_schema)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.domains)
+ return cdata->reuseable->operations->re_meta_funcs.domains (NULL, cnc, store,
+ context, error,
+ domain_catalog, domain_schema);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta__constraints_dom (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._constraints_dom)
+ return cdata->reuseable->operations->re_meta_funcs._constraints_dom (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta_constraints_dom (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *domain_catalog, const GValue *domain_schema,
+ const GValue *domain_name)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.constraints_dom)
+ return cdata->reuseable->operations->re_meta_funcs.constraints_dom (NULL, cnc, store,
+ context, error,
+ domain_catalog, domain_schema,
+ domain_name);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta__el_types (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._el_types)
+ return cdata->reuseable->operations->re_meta_funcs._el_types (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta_el_types (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *specific_name)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.el_types)
+ return cdata->reuseable->operations->re_meta_funcs.el_types (NULL, cnc, store,
+ context, error,
+ specific_name);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta__collations (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._collations)
+ return cdata->reuseable->operations->re_meta_funcs._collations (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta_collations (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *collation_catalog, const GValue *collation_schema,
+ const GValue *collation_name_n)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.collations)
+ return cdata->reuseable->operations->re_meta_funcs.collations (NULL, cnc, store,
+ context, error,
+ collation_catalog,
+ collation_schema,
+ collation_name_n);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta__character_sets (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._character_sets)
+ return cdata->reuseable->operations->re_meta_funcs._character_sets (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta_character_sets (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *chset_catalog, const GValue *chset_schema,
+ const GValue *chset_name_n)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.character_sets)
+ return cdata->reuseable->operations->re_meta_funcs.character_sets (NULL, cnc, store,
+ context, error,
+ chset_catalog, chset_schema,
+ chset_name_n);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta__schemata (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._schemata)
+ return cdata->reuseable->operations->re_meta_funcs._schemata (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ model = run_meta_command (cnc, cdata, "schemas", error);
+ if (!model)
+ return FALSE;
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta_schemata (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *catalog_name, const GValue *schema_name_n)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.schemata)
+ return cdata->reuseable->operations->re_meta_funcs.schemata (NULL, cnc, store,
+ context, error, catalog_name, schema_name_n);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ if (schema_name_n)
+ model = run_meta_command_args (cnc, cdata, "schemas", error,
+ "catalog_name", g_value_get_string (catalog_name),
+ "schema_name", g_value_get_string (schema_name_n), NULL);
+ else
+ model = run_meta_command_args (cnc, cdata, "schemas", error,
+ "catalog_name", g_value_get_string (catalog_name), NULL);
+ if (!model)
+ return FALSE;
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta__tables_views (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *tables_model, *views_model;
+ gboolean retval = TRUE;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._tables_views)
+ return cdata->reuseable->operations->re_meta_funcs._tables_views (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ tables_model = run_meta_command (cnc, cdata, "tables", error);
+ if (!tables_model)
+ return FALSE;
+ views_model = run_meta_command (cnc, cdata, "views", error);
+ if (!views_model) {
+ g_object_unref (tables_model);
+ return FALSE;
+ }
+
+ GdaMetaContext c2;
+ c2 = *context; /* copy contents, just because we need to modify @context->table_name */
+ if (retval) {
+ c2.table_name = "_tables";
+ retval = gda_meta_store_modify_with_context (store, &c2, tables_model, error);
+ }
+ if (retval) {
+ c2.table_name = "_views";
+ retval = gda_meta_store_modify_with_context (store, &c2, views_model, error);
+ }
+ g_object_unref (tables_model);
+ g_object_unref (views_model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta_tables_views (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema,
+ const GValue *table_name_n)
+{
+ GdaDataModel *tables_model, *views_model;
+ gboolean retval = TRUE;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.tables_views)
+ return cdata->reuseable->operations->re_meta_funcs.tables_views (NULL, cnc, store,
+ context, error,
+ table_catalog, table_schema,
+ table_name_n);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ if (table_name_n)
+ tables_model = run_meta_command_args (cnc, cdata, "tables", error,
+ "table_catalog", g_value_get_string (table_catalog),
+ "table_schema", g_value_get_string (table_schema),
+ "table_name", g_value_get_string (table_name_n), NULL);
+ else
+ tables_model = run_meta_command_args (cnc, cdata, "tables", error,
+ "table_catalog", g_value_get_string (table_catalog),
+ "table_schema", g_value_get_string (table_schema), NULL);
+ if (!tables_model)
+ return FALSE;
+
+ if (table_name_n)
+ tables_model = run_meta_command_args (cnc, cdata, "views", error,
+ "table_catalog", g_value_get_string (table_catalog),
+ "table_schema", g_value_get_string (table_schema),
+ "table_name", g_value_get_string (table_name_n), NULL);
+ else
+ tables_model = run_meta_command_args (cnc, cdata, "views", error,
+ "table_catalog", g_value_get_string (table_catalog),
+ "table_schema", g_value_get_string (table_schema), NULL);
+ if (!views_model) {
+ g_object_unref (tables_model);
+ return FALSE;
+ }
+
+ GdaMetaContext c2;
+ c2 = *context; /* copy contents, just because we need to modify @context->table_name */
+ if (retval) {
+ c2.table_name = "_tables";
+ retval = gda_meta_store_modify_with_context (store, &c2, tables_model, error);
+ }
+ if (retval) {
+ c2.table_name = "_views";
+ retval = gda_meta_store_modify_with_context (store, &c2, views_model, error);
+ }
+ g_object_unref (tables_model);
+ g_object_unref (views_model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta__columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._columns)
+ return cdata->reuseable->operations->re_meta_funcs._columns (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ model = run_meta_command (cnc, cdata, "columns", error);
+ if (!model)
+ return FALSE;
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta_columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema,
+ const GValue *table_name)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.columns)
+ return cdata->reuseable->operations->re_meta_funcs.columns (NULL, cnc, store,
+ context, error, table_catalog, table_schema,
+ table_name);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ model = run_meta_command_args (cnc, cdata, "columns", error,
+ "table_catalog", g_value_get_string (table_catalog),
+ "table_schema", g_value_get_string (table_schema),
+ "table_name", g_value_get_string (table_name), NULL);
+ if (!model)
+ return FALSE;
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta__view_cols (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._view_cols)
+ return cdata->reuseable->operations->re_meta_funcs._view_cols (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta_view_cols (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *view_catalog, const GValue *view_schema,
+ const GValue *view_name)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.view_cols)
+ return cdata->reuseable->operations->re_meta_funcs.view_cols (NULL, cnc, store,
+ context, error,
+ view_catalog, view_schema,
+ view_name);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta__constraints_tab (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._constraints_tab)
+ return cdata->reuseable->operations->re_meta_funcs._constraints_tab (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ model = run_meta_command (cnc, cdata, "constraints_tab", error);
+ if (!model)
+ return FALSE;
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta_constraints_tab (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema,
+ const GValue *table_name, const GValue *constraint_name_n)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.constraints_tab)
+ return cdata->reuseable->operations->re_meta_funcs.constraints_tab (NULL, cnc, store,
+ context, error,
+ table_catalog, table_schema,
+ table_name, constraint_name_n);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ if (constraint_name_n)
+ model = run_meta_command_args (cnc, cdata, "constraints_tab", error,
+ "table_catalog", g_value_get_string (table_catalog),
+ "table_schema", g_value_get_string (table_schema),
+ "table_name", g_value_get_string (table_name),
+ "constraint_name_", g_value_get_string (table_name), NULL);
+ else
+ model = run_meta_command_args (cnc, cdata, "constraints_tab", error,
+ "table_catalog", g_value_get_string (table_catalog),
+ "table_schema", g_value_get_string (table_schema),
+ "table_name", g_value_get_string (table_name), NULL);
+ if (!model)
+ return FALSE;
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta__constraints_ref (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._constraints_ref)
+ return cdata->reuseable->operations->re_meta_funcs._constraints_ref (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ model = run_meta_command (cnc, cdata, "constraints_ref", error);
+ if (!model)
+ return FALSE;
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta_constraints_ref (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema, const GValue *table_name,
+ const GValue *constraint_name)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.constraints_ref)
+ return cdata->reuseable->operations->re_meta_funcs.constraints_ref (NULL, cnc, store,
+ context, error,
+ table_catalog, table_schema,
+ table_name,
+ constraint_name);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ model = run_meta_command_args (cnc, cdata, "constraints_ref", error,
+ "table_catalog", g_value_get_string (table_catalog),
+ "table_schema", g_value_get_string (table_schema),
+ "table_name", g_value_get_string (table_name),
+ "constraint_name_", g_value_get_string (table_name), NULL);
+ if (!model)
+ return FALSE;
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta__key_columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._key_columns)
+ return cdata->reuseable->operations->re_meta_funcs._key_columns (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ model = run_meta_command (cnc, cdata, "key_columns", error);
+ if (!model)
+ return FALSE;
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta_key_columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema,
+ const GValue *table_name, const GValue *constraint_name)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.key_columns)
+ return cdata->reuseable->operations->re_meta_funcs.key_columns (NULL, cnc, store,
+ context, error,
+ table_catalog, table_schema,
+ table_name,
+ constraint_name);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ model = run_meta_command_args (cnc, cdata, "key_columns", error,
+ "table_catalog", g_value_get_string (table_catalog),
+ "table_schema", g_value_get_string (table_schema),
+ "table_name", g_value_get_string (table_name),
+ "constraint_name_", g_value_get_string (table_name), NULL);
+ if (!model)
+ return FALSE;
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta__check_columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._check_columns)
+ return cdata->reuseable->operations->re_meta_funcs._check_columns (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ model = run_meta_command (cnc, cdata, "check_columns", error);
+ if (!model)
+ return FALSE;
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta_check_columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema,
+ const GValue *table_name, const GValue *constraint_name)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.check_columns)
+ return cdata->reuseable->operations->re_meta_funcs.check_columns (NULL, cnc, store,
+ context, error,
+ table_catalog, table_schema,
+ table_name,
+ constraint_name);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ model = run_meta_command_args (cnc, cdata, "check_columns", error,
+ "table_catalog", g_value_get_string (table_catalog),
+ "table_schema", g_value_get_string (table_schema),
+ "table_name", g_value_get_string (table_name),
+ "constraint_name_", g_value_get_string (table_name), NULL);
+ if (!model)
+ return FALSE;
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta__triggers (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._triggers)
+ return cdata->reuseable->operations->re_meta_funcs._triggers (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ model = run_meta_command (cnc, cdata, "triggers", error);
+ if (!model)
+ return FALSE;
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta_triggers (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema,
+ const GValue *table_name)
+{
+ GdaDataModel *model;
+ gboolean retval;
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.triggers)
+ return cdata->reuseable->operations->re_meta_funcs.triggers (NULL, cnc, store,
+ context, error,
+ table_catalog, table_schema,
+ table_name);
+ else
+ return TRUE;
+ }
+
+ /* fallback to default method */
+ model = run_meta_command_args (cnc, cdata, "triggers", error,
+ "table_catalog", g_value_get_string (table_catalog),
+ "table_schema", g_value_get_string (table_schema),
+ "table_name", g_value_get_string (table_name), NULL);
+ if (!model)
+ return FALSE;
+ retval = gda_meta_store_modify_with_context (store, context, model, error);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_web_meta__routines (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._routines)
+ return cdata->reuseable->operations->re_meta_funcs._routines (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta_routines (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *routine_catalog, const GValue *routine_schema,
+ const GValue *routine_name_n)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.routines)
+ return cdata->reuseable->operations->re_meta_funcs.routines (NULL, cnc, store,
+ context, error,
+ routine_catalog, routine_schema,
+ routine_name_n);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta__routine_col (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._routine_col)
+ return cdata->reuseable->operations->re_meta_funcs._routine_col (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta_routine_col (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *rout_catalog, const GValue *rout_schema,
+ const GValue *rout_name)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.routine_col)
+ return cdata->reuseable->operations->re_meta_funcs.routine_col (NULL, cnc, store,
+ context, error,
+ rout_catalog, rout_schema,
+ rout_name);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta__routine_par (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs._routine_par)
+ return cdata->reuseable->operations->re_meta_funcs._routine_par (NULL, cnc, store,
+ context, error);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
+
+gboolean
+_gda_web_meta_routine_par (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *rout_catalog, const GValue *rout_schema,
+ const GValue *rout_name)
+{
+ WebConnectionData *cdata;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use reuseable methods if available */
+ if (cdata->reuseable) {
+ if (cdata->reuseable->operations->re_meta_funcs.routine_par)
+ return cdata->reuseable->operations->re_meta_funcs.routine_par (NULL, cnc, store,
+ context, error,
+ rout_catalog, rout_schema,
+ rout_name);
+ else
+ return TRUE;
+ }
+
+ /* no default method */
+ return TRUE;
+}
diff --git a/providers/web/gda-web-meta.h b/providers/web/gda-web-meta.h
new file mode 100644
index 0000000..9476955
--- /dev/null
+++ b/providers/web/gda-web-meta.h
@@ -0,0 +1,197 @@
+/* GDA provider
+ * 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; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GDA_WEB_META_H__
+#define __GDA_WEB_META_H__
+
+#include <libgda/gda-server-provider.h>
+
+G_BEGIN_DECLS
+
+void _gda_web_provider_meta_init (GdaServerProvider *provider);
+
+/* _information_schema_catalog_name */
+gboolean _gda_web_meta__info (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+
+/* _builtin_data_types */
+gboolean _gda_web_meta__btypes (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+
+/* _udt */
+gboolean _gda_web_meta__udt (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_udt (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *udt_catalog, const GValue *udt_schema);
+
+/* _udt_columns */
+gboolean _gda_web_meta__udt_cols (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_udt_cols (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *udt_catalog, const GValue *udt_schema, const GValue *udt_name);
+
+/* _enums */
+gboolean _gda_web_meta__enums (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_enums (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *udt_catalog, const GValue *udt_schema, const GValue *udt_name);
+
+/* _domains */
+gboolean _gda_web_meta__domains (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_domains (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *domain_catalog, const GValue *domain_schema);
+
+/* _domain_constraints */
+gboolean _gda_web_meta__constraints_dom (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_constraints_dom (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *domain_catalog, const GValue *domain_schema,
+ const GValue *domain_name);
+
+/* _element_types */
+gboolean _gda_web_meta__el_types (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_el_types (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *specific_name);
+
+/* _collations */
+gboolean _gda_web_meta__collations (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_collations (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *collation_catalog, const GValue *collation_schema,
+ const GValue *collation_name_n);
+
+/* _character_sets */
+gboolean _gda_web_meta__character_sets (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_character_sets (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *chset_catalog, const GValue *chset_schema,
+ const GValue *chset_name_n);
+
+/* _schemata */
+gboolean _gda_web_meta__schemata (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_schemata (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *catalog_name, const GValue *schema_name_n);
+
+/* _tables or _views */
+gboolean _gda_web_meta__tables_views (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_tables_views (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema,
+ const GValue *table_name_n);
+
+/* _columns */
+gboolean _gda_web_meta__columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema,
+ const GValue *table_name);
+
+/* _view_column_usage */
+gboolean _gda_web_meta__view_cols (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_view_cols (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *view_catalog, const GValue *view_schema,
+ const GValue *view_name);
+
+/* _table_constraints */
+gboolean _gda_web_meta__constraints_tab (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_constraints_tab (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema,
+ const GValue *table_name, const GValue *constraint_name_n);
+
+/* _referential_constraints */
+gboolean _gda_web_meta__constraints_ref (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_constraints_ref (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema, const GValue *table_name,
+ const GValue *constraint_name);
+
+/* _key_column_usage */
+gboolean _gda_web_meta__key_columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_key_columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema,
+ const GValue *table_name, const GValue *constraint_name);
+
+/* _check_column_usage */
+gboolean _gda_web_meta__check_columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_check_columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema,
+ const GValue *table_name, const GValue *constraint_name);
+
+/* _triggers */
+gboolean _gda_web_meta__triggers (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_triggers (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_catalog, const GValue *table_schema,
+ const GValue *table_name);
+
+/* _routines */
+gboolean _gda_web_meta__routines (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_routines (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *routine_catalog, const GValue *routine_schema,
+ const GValue *routine_name_n);
+
+/* _routine_columns */
+gboolean _gda_web_meta__routine_col (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_routine_col (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *rout_catalog, const GValue *rout_schema,
+ const GValue *rout_name);
+
+/* _parameters */
+gboolean _gda_web_meta__routine_par (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_routine_par (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *rout_catalog, const GValue *rout_schema,
+ const GValue *rout_name);
+
+
+G_END_DECLS
+
+#endif
+
diff --git a/providers/web/gda-web-provider.c b/providers/web/gda-web-provider.c
new file mode 100644
index 0000000..4841bda
--- /dev/null
+++ b/providers/web/gda-web-provider.c
@@ -0,0 +1,1686 @@
+/* GDA provider
+ * 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 <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/binreloc/gda-binreloc.h>
+#include <libgda/gda-statement-extra.h>
+#include <sql-parser/gda-sql-parser.h>
+#include "gda-web.h"
+#include "gda-web-provider.h"
+#include "gda-web-recordset.h"
+#include "gda-web-ddl.h"
+#include "gda-web-meta.h"
+#include "gda-web-util.h"
+
+/* Use the RSA reference implementation included in the RFC-1321, http://www.freesoft.org/CIE/RFC/1321/ */
+#include "global.h"
+#include "md5.h"
+
+#define _GDA_PSTMT(x) ((GdaPStmt*)(x))
+
+/*
+ * GObject methods
+ */
+static void gda_web_provider_class_init (GdaWebProviderClass *klass);
+static void gda_web_provider_init (GdaWebProvider *provider,
+ GdaWebProviderClass *klass);
+static GObjectClass *parent_class = NULL;
+
+/*
+ * GdaServerProvider's virtual methods
+ */
+/* connection management */
+static gboolean gda_web_provider_open_connection (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaQuarkList *params, GdaQuarkList *auth,
+ guint *task_id, GdaServerProviderAsyncCallback async_cb, gpointer cb_data);
+static gboolean gda_web_provider_close_connection (GdaServerProvider *provider, GdaConnection *cnc);
+static const gchar *gda_web_provider_get_server_version (GdaServerProvider *provider, GdaConnection *cnc);
+static const gchar *gda_web_provider_get_database (GdaServerProvider *provider, GdaConnection *cnc);
+
+/* DDL operations */
+static gboolean gda_web_provider_supports_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperationType type, GdaSet *options);
+static GdaServerOperation *gda_web_provider_create_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperationType type,
+ GdaSet *options, GError **error);
+static gchar *gda_web_provider_render_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error);
+
+static gboolean gda_web_provider_perform_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, guint *task_id,
+ GdaServerProviderAsyncCallback async_cb, gpointer cb_data,
+ GError **error);
+/* transactions */
+static gboolean gda_web_provider_begin_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GdaTransactionIsolation level, GError **error);
+static gboolean gda_web_provider_commit_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error);
+static gboolean gda_web_provider_rollback_transaction (GdaServerProvider *provider, GdaConnection * cnc,
+ const gchar *name, GError **error);
+static gboolean gda_web_provider_add_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error);
+static gboolean gda_web_provider_rollback_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error);
+static gboolean gda_web_provider_delete_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error);
+
+/* information retrieval */
+static const gchar *gda_web_provider_get_version (GdaServerProvider *provider);
+static gboolean gda_web_provider_supports_feature (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaConnectionFeature feature);
+
+static const gchar *gda_web_provider_get_name (GdaServerProvider *provider);
+
+static GdaDataHandler *gda_web_provider_get_data_handler (GdaServerProvider *provider, GdaConnection *cnc,
+ GType g_type, const gchar *dbms_type);
+
+static const gchar* gda_web_provider_get_default_dbms_type (GdaServerProvider *provider, GdaConnection *cnc,
+ GType type);
+/* statements */
+static GdaSqlParser *gda_web_provider_create_parser (GdaServerProvider *provider, GdaConnection *cnc);
+static gchar *gda_web_provider_statement_to_sql (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GdaSet *params,
+ GdaStatementSqlFlag flags,
+ GSList **params_used, GError **error);
+static gboolean gda_web_provider_statement_prepare (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GError **error);
+static GObject *gda_web_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_web_provider_xa_start (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error);
+
+static gboolean gda_web_provider_xa_end (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error);
+static gboolean gda_web_provider_xa_prepare (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error);
+
+static gboolean gda_web_provider_xa_commit (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error);
+static gboolean gda_web_provider_xa_rollback (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error);
+
+static GList *gda_web_provider_xa_recover (GdaServerProvider *provider, GdaConnection *cnc,
+ GError **error);
+
+
+/*
+ * GdaWebProvider class implementation
+ */
+static void
+gda_web_provider_class_init (GdaWebProviderClass *klass)
+{
+ GdaServerProviderClass *provider_class = GDA_SERVER_PROVIDER_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ provider_class->get_version = gda_web_provider_get_version;
+ provider_class->get_server_version = gda_web_provider_get_server_version;
+ provider_class->get_name = gda_web_provider_get_name;
+ provider_class->supports_feature = gda_web_provider_supports_feature;
+
+ provider_class->get_data_handler = gda_web_provider_get_data_handler;
+ provider_class->get_def_dbms_type = gda_web_provider_get_default_dbms_type;
+
+ provider_class->open_connection = gda_web_provider_open_connection;
+ provider_class->close_connection = gda_web_provider_close_connection;
+ provider_class->get_database = gda_web_provider_get_database;
+
+ provider_class->supports_operation = gda_web_provider_supports_operation;
+ provider_class->create_operation = gda_web_provider_create_operation;
+ provider_class->render_operation = gda_web_provider_render_operation;
+ provider_class->perform_operation = gda_web_provider_perform_operation;
+
+ provider_class->begin_transaction = gda_web_provider_begin_transaction;
+ provider_class->commit_transaction = gda_web_provider_commit_transaction;
+ provider_class->rollback_transaction = gda_web_provider_rollback_transaction;
+ provider_class->add_savepoint = gda_web_provider_add_savepoint;
+ provider_class->rollback_savepoint = gda_web_provider_rollback_savepoint;
+ provider_class->delete_savepoint = gda_web_provider_delete_savepoint;
+
+ provider_class->create_parser = gda_web_provider_create_parser;
+ provider_class->statement_to_sql = gda_web_provider_statement_to_sql;
+ provider_class->statement_prepare = gda_web_provider_statement_prepare;
+ provider_class->statement_execute = gda_web_provider_statement_execute;
+
+ provider_class->is_busy = NULL;
+ provider_class->cancel = NULL;
+ provider_class->create_connection = NULL;
+
+ memset (&(provider_class->meta_funcs), 0, sizeof (GdaServerProviderMeta));
+ provider_class->meta_funcs._info = _gda_web_meta__info;
+ provider_class->meta_funcs._btypes = _gda_web_meta__btypes;
+ provider_class->meta_funcs._udt = _gda_web_meta__udt;
+ provider_class->meta_funcs.udt = _gda_web_meta_udt;
+ provider_class->meta_funcs._udt_cols = _gda_web_meta__udt_cols;
+ provider_class->meta_funcs.udt_cols = _gda_web_meta_udt_cols;
+ provider_class->meta_funcs._enums = _gda_web_meta__enums;
+ provider_class->meta_funcs.enums = _gda_web_meta_enums;
+ provider_class->meta_funcs._domains = _gda_web_meta__domains;
+ provider_class->meta_funcs.domains = _gda_web_meta_domains;
+ provider_class->meta_funcs._constraints_dom = _gda_web_meta__constraints_dom;
+ provider_class->meta_funcs.constraints_dom = _gda_web_meta_constraints_dom;
+ provider_class->meta_funcs._el_types = _gda_web_meta__el_types;
+ provider_class->meta_funcs.el_types = _gda_web_meta_el_types;
+ provider_class->meta_funcs._collations = _gda_web_meta__collations;
+ provider_class->meta_funcs.collations = _gda_web_meta_collations;
+ provider_class->meta_funcs._character_sets = _gda_web_meta__character_sets;
+ provider_class->meta_funcs.character_sets = _gda_web_meta_character_sets;
+ provider_class->meta_funcs._schemata = _gda_web_meta__schemata;
+ provider_class->meta_funcs.schemata = _gda_web_meta_schemata;
+ provider_class->meta_funcs._tables_views = _gda_web_meta__tables_views;
+ provider_class->meta_funcs.tables_views = _gda_web_meta_tables_views;
+ provider_class->meta_funcs._columns = _gda_web_meta__columns;
+ provider_class->meta_funcs.columns = _gda_web_meta_columns;
+ provider_class->meta_funcs._view_cols = _gda_web_meta__view_cols;
+ provider_class->meta_funcs.view_cols = _gda_web_meta_view_cols;
+ provider_class->meta_funcs._constraints_tab = _gda_web_meta__constraints_tab;
+ provider_class->meta_funcs.constraints_tab = _gda_web_meta_constraints_tab;
+ provider_class->meta_funcs._constraints_ref = _gda_web_meta__constraints_ref;
+ provider_class->meta_funcs.constraints_ref = _gda_web_meta_constraints_ref;
+ provider_class->meta_funcs._key_columns = _gda_web_meta__key_columns;
+ provider_class->meta_funcs.key_columns = _gda_web_meta_key_columns;
+ provider_class->meta_funcs._check_columns = _gda_web_meta__check_columns;
+ provider_class->meta_funcs.check_columns = _gda_web_meta_check_columns;
+ provider_class->meta_funcs._triggers = _gda_web_meta__triggers;
+ provider_class->meta_funcs.triggers = _gda_web_meta_triggers;
+ provider_class->meta_funcs._routines = _gda_web_meta__routines;
+ provider_class->meta_funcs.routines = _gda_web_meta_routines;
+ provider_class->meta_funcs._routine_col = _gda_web_meta__routine_col;
+ provider_class->meta_funcs.routine_col = _gda_web_meta_routine_col;
+ provider_class->meta_funcs._routine_par = _gda_web_meta__routine_par;
+ provider_class->meta_funcs.routine_par = _gda_web_meta_routine_par;
+
+ /* 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_web_provider_xa_start;
+ provider_class->xa_funcs->xa_end = gda_web_provider_xa_end;
+ provider_class->xa_funcs->xa_prepare = gda_web_provider_xa_prepare;
+ provider_class->xa_funcs->xa_commit = gda_web_provider_xa_commit;
+ provider_class->xa_funcs->xa_rollback = gda_web_provider_xa_rollback;
+ provider_class->xa_funcs->xa_recover = gda_web_provider_xa_recover;
+
+ /* provider is thread safe */
+ provider_class->limiting_thread = NULL;
+}
+
+static void
+gda_web_provider_init (GdaWebProvider *web_prv, GdaWebProviderClass *klass)
+{
+}
+
+GType
+gda_web_provider_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+ static GTypeInfo info = {
+ sizeof (GdaWebProviderClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gda_web_provider_class_init,
+ NULL, NULL,
+ sizeof (GdaWebProvider),
+ 0,
+ (GInstanceInitFunc) gda_web_provider_init
+ };
+ g_static_mutex_lock (®istering);
+ if (type == 0)
+ type = g_type_register_static (GDA_TYPE_SERVER_PROVIDER, "GdaWebProvider", &info, 0);
+ g_static_mutex_unlock (®istering);
+ }
+
+ return type;
+}
+
+
+/*
+ * Get provider name request
+ */
+static const gchar *
+gda_web_provider_get_name (GdaServerProvider *provider)
+{
+ return WEB_PROVIDER_NAME;
+}
+
+/*
+ * Get provider's version, no need to change this
+ */
+static const gchar *
+gda_web_provider_get_version (GdaServerProvider *provider)
+{
+ return PACKAGE_VERSION;
+}
+
+static gboolean
+do_server_setup (GdaConnection *cnc, WebConnectionData *cdata)
+{
+ SoupMessage *msg;
+ guint status;
+ gchar *real_url;
+
+ real_url = g_strdup_printf ("%s/gda-setup.php", cdata->server_base_url);
+ msg = soup_message_new ("GET", real_url);
+ if (!msg) {
+ gda_connection_add_event_string (cnc, _("Invalid HOST/SCRIPT '%s'"), real_url);
+ g_free (real_url);
+ return FALSE;
+ }
+ g_free (real_url);
+
+ g_object_set (G_OBJECT (cdata->front_session), SOUP_SESSION_TIMEOUT, 5, NULL);
+ status = soup_session_send_message (cdata->front_session, msg);
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ gda_connection_add_event_string (cnc, msg->reason_phrase);
+ g_object_unref (msg);
+ return FALSE;
+ }
+
+ xmlDocPtr doc;
+ gchar out_status_chr;
+ doc = _gda_web_decode_response (cnc, cdata, msg->response_body, &out_status_chr, NULL);
+ g_object_unref (msg);
+ if (doc) {
+ if (out_status_chr != 'O') {
+ _gda_web_set_connection_error_from_xmldoc (cnc, doc, NULL);
+ xmlFreeDoc (doc);
+ return FALSE;
+ }
+ xmlFreeDoc (doc);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Open connection request
+ *
+ * In this function, the following _must_ be done:
+ * - check for the presence and validify of the parameters required to actually open a connection,
+ * using @params
+ * - open the real connection to the database using the parameters previously checked
+ * - create a WebConnectionData structure and associate it to @cnc
+ *
+ * Returns: TRUE if no error occurred, or FALSE otherwise (and an ERROR connection event must be added to @cnc)
+ */
+static gboolean
+gda_web_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_WEB_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;
+ }
+
+ /* Check for connection parameters */
+ const gchar *db_name, *host, *path, *port, *serversecret, *pass = NULL, *use_ssl;
+
+ if (auth)
+ pass = gda_quark_list_find (auth, "PASSWORD");
+ if (!pass) {
+ gda_connection_add_event_string (cnc, _("The connection string must contain the %s value"), "PASSWORD");
+ return FALSE;
+ }
+ host = gda_quark_list_find (params, "HOST");
+ if (!host) {
+ gda_connection_add_event_string (cnc,
+ _("The connection string must contain the %s value"), "HOST");
+ return FALSE;
+ }
+ serversecret = gda_quark_list_find (params, "SECRET");
+ if (!serversecret) {
+ gda_connection_add_event_string (cnc,
+ _("The connection string must contain the %s value"), "SECRET");
+ return FALSE;
+ }
+ path = gda_quark_list_find (params, "PATH");
+ port = gda_quark_list_find (params, "PORT");
+ db_name = gda_quark_list_find (params, "DB_NAME");
+ if (!db_name) {
+ gda_connection_add_event_string (cnc,
+ _("The connection string must contain the %s value"), "DB_NAME");
+ return FALSE;
+ }
+ use_ssl = gda_quark_list_find (params, "USE_SSL");
+ if (use_ssl && (*use_ssl != 'T') && (*use_ssl != 't'))
+ use_ssl = NULL;
+
+ /* open Libsoup session */
+ WebConnectionData *cdata;
+ GString *server_url;
+
+ cdata = g_new0 (WebConnectionData, 1);
+ cdata->mutex = gda_mutex_new ();
+ cdata->server_id = NULL;
+ cdata->forced_closing = FALSE;
+ cdata->worker_session = soup_session_sync_new ();
+ cdata->front_session = soup_session_sync_new_with_options ("max-conns-per-host", 1, NULL);
+ if (use_ssl) {
+ server_url = g_string_new ("https://");
+ g_print ("USING SSL\n");
+ }
+ else
+ server_url = g_string_new ("http://");
+ g_string_append (server_url, host);
+ if (port)
+ g_string_append_printf (server_url, ":%s", port);
+ if (path)
+ g_string_append_printf (server_url, "/%s", path);
+ cdata->front_url = g_strdup_printf ("%s/gda-front.php", server_url->str);
+ cdata->worker_url = g_strdup_printf ("%s/gda-worker.php", server_url->str);
+ cdata->server_base_url = g_string_free (server_url, FALSE);
+ if (serversecret)
+ cdata->key = g_strdup (serversecret);
+ gda_connection_internal_set_provider_data (cnc, cdata, (GDestroyNotify) _gda_web_free_cnc_data);
+
+ /*
+ * perform setup
+ */
+ if (! do_server_setup (cnc, cdata))
+ return FALSE;
+
+ /*
+ * send HELLO
+ */
+ xmlDocPtr doc;
+ gchar status;
+#define HELLO_MSG "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" \
+ "<request>\n" \
+ " <cmd>HELLO</cmd>\n" \
+ "</request>"
+ doc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_HELLO, HELLO_MSG, NULL, &status);
+ if (!doc) {
+ gda_connection_internal_set_provider_data (cnc, NULL, NULL);
+ _gda_web_do_server_cleanup (cnc, cdata);
+ return FALSE;
+ }
+ if (status != 'O') {
+ _gda_web_set_connection_error_from_xmldoc (cnc, doc, NULL);
+ xmlFreeDoc (doc);
+ gda_connection_internal_set_provider_data (cnc, NULL, NULL);
+ _gda_web_do_server_cleanup (cnc, cdata);
+ return FALSE;
+ }
+ xmlFreeDoc (doc);
+
+ /*
+ * send CONNECT
+ */
+ gchar *tmp, *token;
+#define CONNECT_MSG "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" \
+ "<request>\n" \
+ " <token>%s</token>\n" \
+ " <cmd>CONNECT</cmd>\n" \
+ "</request>"
+ if (cdata->key)
+ g_free (cdata->key);
+ cdata->key = g_strdup_printf ("%s/AND/%s", db_name, pass);
+
+ token = _gda_web_compute_token (cdata);
+ tmp = g_strdup_printf (CONNECT_MSG, token);
+ g_free (token);
+
+ cdata->server_secret = g_strdup (serversecret);
+ doc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_CONNECT, tmp, serversecret, &status);
+ g_free (tmp);
+ if (!doc) {
+ gda_connection_internal_set_provider_data (cnc, NULL, NULL);
+ _gda_web_do_server_cleanup (cnc, cdata);
+ return FALSE;
+ }
+ if (status != 'O') {
+ _gda_web_set_connection_error_from_xmldoc (cnc, doc, NULL);
+ xmlFreeDoc (doc);
+ gda_connection_internal_set_provider_data (cnc, NULL, NULL);
+ _gda_web_do_server_cleanup (cnc, cdata);
+ return FALSE;
+ }
+ xmlFreeDoc (doc);
+
+ /*
+ * change key: cdata->key = MD5(cdata->key)
+ */
+ MD5_CTX md5c;
+ unsigned char digest[16];
+ GString *md5str;
+ gint i;
+ MD5Init (&md5c);
+ MD5Update (&md5c, (unsigned char *) cdata->key, strlen (cdata->key));
+ MD5Final (digest, &md5c);
+ md5str = g_string_new ("");
+ for (i = 0; i < 16; i++)
+ g_string_append_printf (md5str, "%02x", digest[i]);
+ g_free (cdata->key);
+ cdata->key = g_string_free (md5str, FALSE);
+
+ return TRUE;
+}
+
+/*
+ * Close connection request
+ *
+ * In this function, the following _must_ be done:
+ * - Actually close the connection to the database using @cnc's associated WebConnectionData structure
+ * - Free the WebConnectionData structure and its contents
+ *
+ * Returns: TRUE if no error occurred, or FALSE otherwise (and an ERROR connection event must be added to @cnc)
+ */
+static gboolean
+gda_web_provider_close_connection (GdaServerProvider *provider, GdaConnection *cnc)
+{
+ WebConnectionData *cdata;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ gda_mutex_lock (cdata->mutex);
+ if (!cdata->forced_closing && cdata->worker_running) {
+ gda_mutex_unlock (cdata->mutex);
+ /* send BYE message */
+ xmlDocPtr doc;
+ gchar status;
+ gchar *tmp, *token;
+#define BYE_MSG "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" \
+ "<request>\n" \
+ " <token>%s</token>\n" \
+ " <cmd>BYE</cmd>\n" \
+ "</request>"
+ token = _gda_web_compute_token (cdata);
+ tmp = g_strdup_printf (BYE_MSG, token);
+ g_free (token);
+
+ doc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_BYE, tmp, cdata->key, &status);
+ g_free (tmp);
+ if (!doc)
+ return FALSE;
+ if (status != 'C') {
+ _gda_web_set_connection_error_from_xmldoc (cnc, doc, NULL);
+ xmlFreeDoc (doc);
+ return FALSE;
+ }
+ xmlFreeDoc (doc);
+ }
+ else
+ gda_mutex_unlock (cdata->mutex);
+
+ _gda_web_do_server_cleanup (cnc, cdata);
+
+ /* Free the WebConnectionData structure and its contents*/
+ _gda_web_free_cnc_data (cdata);
+ gda_connection_internal_set_provider_data (cnc, NULL, NULL);
+
+ return TRUE;
+}
+
+/*
+ * Server version request
+ *
+ * Returns the server version as a string, which should be stored in @cnc's associated WebConnectionData structure
+ */
+static const gchar *
+gda_web_provider_get_server_version (GdaServerProvider *provider, GdaConnection *cnc)
+{
+ WebConnectionData *cdata;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+ TO_IMPLEMENT;
+ return NULL;
+}
+
+/*
+ * Get database request
+ *
+ * Returns the database name as a string, which should be stored in @cnc's associated WebConnectionData structure
+ */
+static const gchar *
+gda_web_provider_get_database (GdaServerProvider *provider, GdaConnection *cnc)
+{
+ WebConnectionData *cdata;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return NULL;
+ TO_IMPLEMENT;
+ return NULL;
+}
+
+/*
+ * Support operation request
+ *
+ * Tells what the implemented server operations are. To add support for an operation, the following steps are required:
+ * - create a web_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_web_provider_render_operation() and gda_web_provider_perform_operation() methods
+ *
+ * In this example, the GDA_SERVER_OPERATION_CREATE_TABLE is implemented
+ */
+static gboolean
+gda_web_provider_supports_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperationType type, GdaSet *options)
+{
+ 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 (type) {
+ case GDA_SERVER_OPERATION_CREATE_DB:
+ case GDA_SERVER_OPERATION_DROP_DB:
+ return FALSE;
+
+ case GDA_SERVER_OPERATION_CREATE_TABLE:
+ return TRUE;
+ case GDA_SERVER_OPERATION_DROP_TABLE:
+ case GDA_SERVER_OPERATION_RENAME_TABLE:
+
+ case GDA_SERVER_OPERATION_ADD_COLUMN:
+
+ case GDA_SERVER_OPERATION_CREATE_INDEX:
+ case GDA_SERVER_OPERATION_DROP_INDEX:
+
+ case GDA_SERVER_OPERATION_CREATE_VIEW:
+ case GDA_SERVER_OPERATION_DROP_VIEW:
+ default:
+ return 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 dependent on @cnc
+ */
+static GdaServerOperation *
+gda_web_provider_create_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperationType type, GdaSet *options, GError **error)
+{
+ gchar *file;
+ GdaServerOperation *op;
+ gchar *str;
+ gchar *dir;
+
+ if (cnc) {
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+ }
+
+ file = g_utf8_strdown (gda_server_operation_op_type_to_string (type), -1);
+ str = g_strdup_printf ("web_specs_%s.xml", file);
+ g_free (file);
+
+ dir = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, NULL);
+ file = gda_server_provider_find_file (provider, dir, str);
+ g_free (dir);
+
+ if (! file) {
+ g_set_error (error, 0, 0, _("Missing spec. file '%s'"), str);
+ g_free (str);
+ return NULL;
+ }
+ g_free (str);
+
+ op = gda_server_operation_new (type, file);
+ g_free (file);
+
+ return op;
+}
+
+/*
+ * Render operation request
+ */
+static gchar *
+gda_web_provider_render_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error)
+{
+ gchar *sql = NULL;
+ gchar *file;
+ gchar *str;
+ gchar *dir;
+
+ if (cnc) {
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+ }
+
+ /* test @op's validity */
+ file = g_utf8_strdown (gda_server_operation_op_type_to_string (gda_server_operation_get_op_type (op)), -1);
+ str = g_strdup_printf ("web_specs_%s.xml", file);
+ g_free (file);
+
+ dir = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, NULL);
+ file = gda_server_provider_find_file (provider, dir, str);
+ g_free (dir);
+
+ if (! file) {
+ g_set_error (error, 0, 0, _("Missing spec. file '%s'"), str);
+ g_free (str);
+ return NULL;
+ }
+ g_free (str);
+ if (!gda_server_operation_is_valid (op, file, error)) {
+ g_free (file);
+ return NULL;
+ }
+ g_free (file);
+
+ /* actual rendering */
+ switch (gda_server_operation_get_op_type (op)) {
+ case GDA_SERVER_OPERATION_CREATE_DB:
+ case GDA_SERVER_OPERATION_DROP_DB:
+ sql = NULL;
+ break;
+ case GDA_SERVER_OPERATION_CREATE_TABLE:
+ sql = gda_web_render_CREATE_TABLE (provider, cnc, op, error);
+ break;
+ case GDA_SERVER_OPERATION_DROP_TABLE:
+ case GDA_SERVER_OPERATION_RENAME_TABLE:
+ case GDA_SERVER_OPERATION_ADD_COLUMN:
+ case GDA_SERVER_OPERATION_CREATE_INDEX:
+ case GDA_SERVER_OPERATION_DROP_INDEX:
+ case GDA_SERVER_OPERATION_CREATE_VIEW:
+ case GDA_SERVER_OPERATION_DROP_VIEW:
+ sql = NULL;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ return sql;
+}
+
+/*
+ * Perform operation request
+ */
+static gboolean
+gda_web_provider_perform_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, guint *task_id,
+ GdaServerProviderAsyncCallback async_cb, gpointer cb_data, GError **error)
+{
+ GdaServerOperationType optype;
+
+ /* 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_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+ }
+ optype = gda_server_operation_get_op_type (op);
+ switch (optype) {
+ case GDA_SERVER_OPERATION_CREATE_DB:
+ case GDA_SERVER_OPERATION_DROP_DB:
+ default:
+ /* use the SQL from the provider to perform the action */
+ return gda_server_provider_perform_operation_default (provider, cnc, op, error);
+ }
+}
+
+/*
+ * Begin transaction request
+ */
+static gboolean
+gda_web_provider_begin_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GdaTransactionIsolation level,
+ GError **error)
+{
+ WebConnectionData *cdata;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ TO_IMPLEMENT;
+
+ return FALSE;
+}
+
+/*
+ * Commit transaction request
+ */
+static gboolean
+gda_web_provider_commit_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error)
+{
+ WebConnectionData *cdata;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ TO_IMPLEMENT;
+
+ return FALSE;
+}
+
+/*
+ * Rollback transaction request
+ */
+static gboolean
+gda_web_provider_rollback_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error)
+{
+ WebConnectionData *cdata;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ TO_IMPLEMENT;
+
+ return FALSE;
+}
+
+/*
+ * Add savepoint request
+ */
+static gboolean
+gda_web_provider_add_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error)
+{
+ WebConnectionData *cdata;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ TO_IMPLEMENT;
+
+ return FALSE;
+}
+
+/*
+ * Rollback savepoint request
+ */
+static gboolean
+gda_web_provider_rollback_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error)
+{
+ WebConnectionData *cdata;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ TO_IMPLEMENT;
+
+ return FALSE;
+}
+
+/*
+ * Delete savepoint request
+ */
+static gboolean
+gda_web_provider_delete_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error)
+{
+ WebConnectionData *cdata;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ TO_IMPLEMENT;
+
+ return FALSE;
+}
+
+/*
+ * Feature support request
+ */
+static gboolean
+gda_web_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.
+ */
+static GdaDataHandler *
+gda_web_provider_get_data_handler (GdaServerProvider *provider, GdaConnection *cnc,
+ GType type, const gchar *dbms_type)
+{
+ GdaDataHandler *dh;
+ if (cnc) {
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+ }
+
+ if (type == G_TYPE_INVALID) {
+ TO_IMPLEMENT; /* use @dbms_type */
+ dh = NULL;
+ }
+ else if ((type == GDA_TYPE_BINARY) ||
+ (type == GDA_TYPE_BLOB)) {
+ TO_IMPLEMENT; /* define data handlers for these types */
+ dh = NULL;
+ }
+ else if ((type == GDA_TYPE_TIME) ||
+ (type == GDA_TYPE_TIMESTAMP) ||
+ (type == G_TYPE_DATE)) {
+ TO_IMPLEMENT; /* define data handlers for these types */
+ dh = NULL;
+ }
+ else
+ dh = gda_server_provider_get_data_handler_default (provider, cnc, type, dbms_type);
+
+ return dh;
+}
+
+/*
+ * Get default DBMS type request
+ *
+ * This method returns the "preferred" DBMS type for GType
+ */
+static const gchar*
+gda_web_provider_get_default_dbms_type (GdaServerProvider *provider, GdaConnection *cnc, GType type)
+{
+ if (cnc) {
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+ }
+
+ TO_IMPLEMENT;
+
+ if ((type == G_TYPE_INT64) ||
+ (type == G_TYPE_INT) ||
+ (type == GDA_TYPE_SHORT) ||
+ (type == GDA_TYPE_USHORT) ||
+ (type == G_TYPE_CHAR) ||
+ (type == G_TYPE_UCHAR) ||
+ (type == G_TYPE_ULONG) ||
+ (type == G_TYPE_UINT) ||
+ (type == G_TYPE_UINT64))
+ return "integer";
+
+ if ((type == GDA_TYPE_BINARY) ||
+ (type == GDA_TYPE_BLOB))
+ return "blob";
+
+ if (type == G_TYPE_BOOLEAN)
+ return "boolean";
+
+ if ((type == G_TYPE_DATE) ||
+ (type == GDA_TYPE_GEOMETRIC_POINT) ||
+ (type == G_TYPE_OBJECT) ||
+ (type == GDA_TYPE_LIST) ||
+ (type == G_TYPE_STRING) ||
+ (type == GDA_TYPE_TIME) ||
+ (type == GDA_TYPE_TIMESTAMP) ||
+ (type == G_TYPE_INVALID) ||
+ (type == G_TYPE_GTYPE))
+ return "string";
+
+ if ((type == G_TYPE_DOUBLE) ||
+ (type == GDA_TYPE_NUMERIC) ||
+ (type == G_TYPE_FLOAT))
+ return "real";
+
+ if (type == GDA_TYPE_TIME)
+ return "time";
+ if (type == GDA_TYPE_TIMESTAMP)
+ return "timestamp";
+ if (type == G_TYPE_DATE)
+ return "date";
+
+ return "text";
+}
+
+/*
+ * 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 *
+gda_web_provider_create_parser (GdaServerProvider *provider, GdaConnection *cnc)
+{
+ WebConnectionData *cdata = NULL;
+
+ if (cnc)
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return NULL;
+ if (cdata->reuseable && cdata->reuseable->operations->re_create_parser)
+ return cdata->reuseable->operations->re_create_parser (cdata->reuseable);
+ else
+ return 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
+ */
+static gchar *
+gda_web_provider_statement_to_sql (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GdaSet *params, GdaStatementSqlFlag flags,
+ GSList **params_used, GError **error)
+{
+ g_return_val_if_fail (GDA_IS_STATEMENT (stmt), NULL);
+ if (cnc) {
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
+ }
+
+ return gda_statement_to_sql_extended (stmt, cnc, params, flags, params_used, error);
+}
+
+static const gchar*
+gtype_to_webtype (GType type)
+{
+ if (type == G_TYPE_INT64)
+ return "integer";
+ if (type == G_TYPE_UINT64)
+ return "integer";
+ if (type == GDA_TYPE_BINARY)
+ return "text";
+ if (type == GDA_TYPE_BLOB)
+ return "blob";
+ if (type == G_TYPE_BOOLEAN)
+ return "boolean";
+ if (type == G_TYPE_DATE)
+ return "date";
+ if (type == G_TYPE_DOUBLE)
+ return "float";
+ if (type == GDA_TYPE_GEOMETRIC_POINT)
+ return "text";
+ if (type == G_TYPE_OBJECT)
+ return "text";
+ if (type == G_TYPE_INT)
+ return "integer";
+ if (type == GDA_TYPE_LIST)
+ return "text";
+ if (type == GDA_TYPE_NUMERIC)
+ return "decimal";
+ if (type == G_TYPE_FLOAT)
+ return "float";
+ if (type == GDA_TYPE_SHORT)
+ return "integer";
+ if (type == GDA_TYPE_USHORT)
+ return "integer";
+ if (type == G_TYPE_STRING)
+ return "text";
+ if (type == GDA_TYPE_TIME)
+ return "time";
+ if (type == GDA_TYPE_TIMESTAMP)
+ return "timestamp";
+ if (type == G_TYPE_CHAR)
+ return "integer";
+ if (type == G_TYPE_UCHAR)
+ return "integer";
+ if (type == G_TYPE_ULONG)
+ return "integer";
+ if (type == G_TYPE_GTYPE)
+ return "text";
+ if (type == G_TYPE_UINT)
+ return "integer";
+ if (type == G_TYPE_INVALID)
+ return "text";
+
+ return "text";
+}
+
+/*
+ * 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 #GdaWebPStmt object and declare it to @cnc.
+ */
+static gboolean
+gda_web_provider_statement_prepare (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GError **error)
+{
+ GdaWebPStmt *ps;
+ gboolean retval = FALSE;
+ WebConnectionData *cdata;
+
+ 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);
+
+
+ /* fetch prepares stmt if already done */
+ ps = (GdaWebPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
+ if (ps)
+ return TRUE;
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* render as SQL understood by the provider */
+ GdaSet *params = NULL;
+ gchar *sql;
+ GSList *used_params = NULL;
+ if (! gda_statement_get_parameters (stmt, ¶ms, error))
+ return FALSE;
+ sql = gda_web_provider_statement_to_sql (provider, cnc, stmt, params, GDA_STATEMENT_SQL_PARAMS_AS_UQMARK,
+ &used_params, error);
+ if (!sql)
+ goto out;
+
+ /* make a list of the parameter names used in the statement */
+ GSList *param_ids = NULL;
+ if (used_params) {
+ GSList *list;
+ for (list = used_params; list; list = list->next) {
+ const gchar *cid;
+ cid = gda_holder_get_id (GDA_HOLDER (list->data));
+ if (cid) {
+ param_ids = g_slist_append (param_ids, g_strdup (cid));
+ /*g_print ("PREPARATION: param ID: %s\n", cid);*/
+ }
+ else {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_PREPARE_STMT_ERROR,
+ "%s", _("Unnamed parameter is not allowed in prepared statements"));
+ g_slist_foreach (param_ids, (GFunc) g_free, NULL);
+ g_slist_free (param_ids);
+ goto out;
+ }
+ }
+ }
+
+ /* prepare XML command */
+ xmlDocPtr doc;
+ xmlNodePtr root, cmdnode, node;
+ gchar *token;
+ doc = xmlNewDoc (BAD_CAST "1.0");
+ root = xmlNewNode (NULL, BAD_CAST "request");
+ xmlDocSetRootElement (doc, root);
+ token = _gda_web_compute_token (cdata);
+ xmlNewChild (root, NULL, BAD_CAST "token", BAD_CAST token);
+ g_free (token);
+ cmdnode = xmlNewChild (root, NULL, BAD_CAST "cmd", BAD_CAST "PREPARE");
+ node = xmlNewTextChild (cmdnode, NULL, BAD_CAST "sql", BAD_CAST sql);
+ if ((gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT) ||
+ (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_COMPOUND))
+ xmlSetProp (node, BAD_CAST "type", BAD_CAST "SELECT");
+ else if (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_UNKNOWN) {
+ if (! g_ascii_strncasecmp (sql, "select", 6) ||
+ ! g_ascii_strncasecmp (sql, "pragma", 6) ||
+ ! g_ascii_strncasecmp (sql, "describe", 8))
+ xmlSetProp (node, BAD_CAST "type", BAD_CAST "SELECT");
+ }
+ if (param_ids) {
+ GSList *list;
+ xmlNodePtr argsnode;
+ argsnode = xmlNewChild (cmdnode, NULL, BAD_CAST "arguments", NULL);
+ for (list = used_params; list; list = list->next) {
+ node = xmlNewChild (argsnode, NULL, BAD_CAST "arg", NULL);
+ xmlSetProp (node, BAD_CAST "type",
+ BAD_CAST gtype_to_webtype (gda_holder_get_g_type (GDA_HOLDER (list->data))));
+ }
+ }
+
+ /* send command */
+ xmlChar *cmde;
+ xmlDocPtr replydoc;
+ int size;
+ gchar status;
+
+ xmlDocDumpMemory (doc, &cmde, &size);
+ xmlFreeDoc (doc);
+ replydoc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_PREPARE, (gchar*) cmde, cdata->key, &status);
+ xmlFree (cmde);
+
+ if (!replydoc) {
+ _gda_web_change_connection_to_closed (cnc, cdata);
+ goto out;
+ }
+ if (status != 'O') {
+ _gda_web_set_connection_error_from_xmldoc (cnc, replydoc, error);
+ xmlFreeDoc (replydoc);
+
+ if (status == 'C')
+ _gda_web_change_connection_to_closed (cnc, cdata);
+ goto out;
+ }
+
+ /* create a prepared statement object */
+ ps = NULL;
+ root = xmlDocGetRootElement (replydoc);
+ for (node = root->children; node; node = node->next) {
+ if (!strcmp ((gchar*) node->name, "preparehash")) {
+ xmlChar *contents;
+ contents = xmlNodeGetContent (node);
+ ps = gda_web_pstmt_new (cnc, (gchar*) contents);
+ xmlFree (contents);
+ break;
+ }
+ }
+ xmlFreeDoc (replydoc);
+
+ if (!ps)
+ goto out;
+
+ gda_pstmt_set_gda_statement (_GDA_PSTMT (ps), stmt);
+ _GDA_PSTMT (ps)->param_ids = param_ids;
+ _GDA_PSTMT (ps)->sql = sql;
+
+ gda_connection_add_prepared_statement (cnc, stmt, (GdaPStmt *) ps);
+ g_object_unref (ps);
+
+ retval = TRUE;
+
+ out:
+ if (used_params)
+ g_slist_free (used_params);
+ if (params)
+ g_object_unref (params);
+ return retval;
+}
+
+/*
+ * 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
+ */
+static GObject *
+gda_web_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)
+{
+ GdaWebPStmt *ps;
+ WebConnectionData *cdata;
+ gboolean allow_noparam;
+ gboolean empty_rs = FALSE; /* TRUE when @allow_noparam is TRUE and there is a problem with @params
+ => resulting data model will be empty (0 row) */
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
+ g_return_val_if_fail (GDA_IS_STATEMENT (stmt), NULL);
+
+ /* 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 statement execution"));
+ return NULL;
+ }
+
+ allow_noparam = (model_usage & GDA_STATEMENT_MODEL_ALLOW_NOPARAM) &&
+ (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT);
+
+ if (last_inserted_row)
+ *last_inserted_row = NULL;
+
+ /* Get private data */
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return NULL;
+
+
+ /* get/create new prepared statement */
+ ps = (GdaWebPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
+ if (!ps) {
+ if (!gda_web_provider_statement_prepare (provider, cnc, stmt, error)) {
+ /* this case can appear for example if some variables are used in places
+ * where the C API cannot allow them (for example if the variable is the table name
+ * in a SELECT statement). The action here is to get the actual SQL code for @stmt,
+ * and use that SQL instead of @stmt to create another GdaWebPStmt object.
+ *
+ * Don't call gda_connection_add_prepared_statement() with this new prepared statement
+ * as it will be destroyed once used.
+ */
+ return NULL;
+ }
+ else {
+ ps = (GdaWebPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
+ g_object_ref (ps);
+ }
+ }
+ else
+ g_object_ref (ps);
+ g_assert (ps);
+
+ /* prepare XML command */
+ xmlDocPtr doc;
+ xmlNodePtr root, cmdnode, node;
+ gchar *token;
+ doc = xmlNewDoc (BAD_CAST "1.0");
+ root = xmlNewNode (NULL, BAD_CAST "request");
+ xmlDocSetRootElement (doc, root);
+ token = _gda_web_compute_token (cdata);
+ xmlNewChild (root, NULL, BAD_CAST "token", BAD_CAST token);
+ g_free (token);
+ cmdnode = xmlNewChild (root, NULL, BAD_CAST "cmd", BAD_CAST "EXEC");
+ node = xmlNewTextChild (cmdnode, NULL, BAD_CAST "sql", BAD_CAST _GDA_PSTMT (ps)->sql);
+ if ((gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT) ||
+ (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_COMPOUND))
+ xmlSetProp (node, BAD_CAST "type", BAD_CAST "SELECT");
+ else if (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_UNKNOWN) {
+ if (! g_ascii_strncasecmp (_GDA_PSTMT (ps)->sql, "select", 6) ||
+ ! g_ascii_strncasecmp (_GDA_PSTMT (ps)->sql, "pragma", 6) ||
+ ! g_ascii_strncasecmp (_GDA_PSTMT (ps)->sql, "describe", 8))
+ xmlSetProp (node, BAD_CAST "type", BAD_CAST "SELECT");
+ }
+ xmlNewChild (cmdnode, NULL, BAD_CAST "preparehash", BAD_CAST (ps->pstmt_hash));
+
+ /* bind statement's parameters */
+ GSList *list;
+ GdaConnectionEvent *event = NULL;
+ int i;
+ xmlNodePtr argsnode;
+ if (_GDA_PSTMT (ps)->param_ids)
+ argsnode = xmlNewChild (cmdnode, NULL, BAD_CAST "arguments", NULL);
+
+ for (i = 1, list = _GDA_PSTMT (ps)->param_ids; list; list = list->next, i++) {
+ const gchar *pname = (gchar *) list->data;
+ GdaHolder *h;
+
+ /* find requested parameter */
+ if (!params) {
+ event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
+ gda_connection_event_set_description (event, _("Missing parameter(s) to execute query"));
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+ GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR,
+ "%s", _("Missing parameter(s) to execute query"));
+ break;
+ }
+
+ h = gda_set_get_holder (params, pname);
+ if (!h) {
+ gchar *tmp = gda_alphanum_to_text (g_strdup (pname + 1));
+ if (tmp) {
+ h = gda_set_get_holder (params, tmp);
+ g_free (tmp);
+ }
+ }
+ if (!h) {
+ if (! allow_noparam) {
+ gchar *str;
+ str = g_strdup_printf (_("Missing parameter '%s' to execute query"), pname);
+ event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
+ gda_connection_event_set_description (event, str);
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+ GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, "%s", str);
+ g_free (str);
+ break;
+ }
+ else {
+ /* bind param to NULL */
+ node = xmlNewChild (argsnode, NULL, BAD_CAST "arg", NULL);
+ xmlSetProp (node, BAD_CAST "type", BAD_CAST "NULL");
+ empty_rs = TRUE;
+ continue;
+ }
+
+ }
+ if (!gda_holder_is_valid (h)) {
+ if (! allow_noparam) {
+ gchar *str;
+ str = g_strdup_printf (_("Parameter '%s' is invalid"), pname);
+ event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
+ gda_connection_event_set_description (event, str);
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+ GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, "%s", str);
+ g_free (str);
+ break;
+ }
+ else {
+ /* bind param to NULL */
+ xmlSetProp (node, BAD_CAST "type", BAD_CAST "NULL");
+ empty_rs = TRUE;
+ continue;
+ }
+ }
+
+ /* actual binding using the C API, for parameter at position @i */
+ const GValue *value = gda_holder_get_value (h);
+ gchar *tmp;
+ tmp = gda_value_stringify (value);
+ node = xmlNewTextChild (argsnode, NULL, BAD_CAST "arg", BAD_CAST tmp);
+ g_free (tmp);
+ xmlSetProp (node, BAD_CAST "type",
+ BAD_CAST gtype_to_webtype (gda_holder_get_g_type (h)));
+ }
+
+ if (event) {
+ gda_connection_add_event (cnc, event);
+ g_object_unref (ps);
+ xmlFreeDoc (doc);
+ return NULL;
+ }
+
+ /* add a connection event for the execution */
+ event = gda_connection_event_new (GDA_CONNECTION_EVENT_COMMAND);
+ gda_connection_event_set_description (event, _GDA_PSTMT (ps)->sql);
+ gda_connection_add_event (cnc, event);
+
+ if (empty_rs) {
+ /* There are some missing parameters, so the SQL can't be executed but we still want
+ * to execute something to get the columns correctly. A possibility is to actually
+ * execute another SQL which is the code shown here.
+ *
+ * To adapt depending on the C API and its features */
+ GdaStatement *estmt;
+ gchar *esql;
+ estmt = gda_select_alter_select_for_empty (stmt, error);
+ if (!estmt) {
+ g_object_unref (ps);
+ return NULL;
+ }
+ esql = gda_statement_to_sql (estmt, NULL, error);
+ g_object_unref (estmt);
+ if (!esql) {
+ g_object_unref (ps);
+ return NULL;
+ }
+
+ /* Execute the 'esql' SQL code */
+ g_free (esql);
+
+ /* modify @doc */
+ TO_IMPLEMENT;
+ }
+
+ /* send command */
+ xmlChar *cmde;
+ xmlDocPtr replydoc;
+ int size;
+ gchar status;
+
+ xmlDocDumpMemory (doc, &cmde, &size);
+ xmlFreeDoc (doc);
+ replydoc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_EXEC, (gchar*) cmde, cdata->key, &status);
+ xmlFree (cmde);
+
+ if (!replydoc)
+ status = 'E';
+ if (status != 'O') {
+ if (replydoc) {
+ _gda_web_set_connection_error_from_xmldoc (cnc, replydoc, error);
+ xmlFreeDoc (replydoc);
+ if (status == 'C')
+ _gda_web_change_connection_to_closed (cnc, cdata);
+ }
+ else
+ _gda_web_change_connection_to_closed (cnc, cdata);
+ return NULL;
+ }
+
+ /* required: help @cnc keep some stats */
+ event = gda_connection_event_new (GDA_CONNECTION_EVENT_NOTICE);
+ gda_connection_event_set_description (event, "Command OK");
+ gda_connection_add_event (cnc, event);
+ gda_connection_internal_statement_executed (cnc, stmt, params, event);
+
+ root = xmlDocGetRootElement (replydoc);
+ GObject *retval = NULL;
+ for (node = root->children; node; node = node->next) {
+ if (!strcmp ((gchar*) node->name, "impacted_rows")) {
+ xmlChar *contents;
+ GdaSet *set = NULL;
+
+ contents = xmlNodeGetContent (node);
+ set = gda_set_new_inline (1, "IMPACTED_ROWS", G_TYPE_INT, atoi ((gchar*) contents));
+ xmlFree (contents);
+ retval = (GObject*) set;
+ }
+ else if (!strcmp ((gchar*) node->name, "gda_array")) {
+ GdaDataModel *data_model;
+ gda_mutex_lock (cdata->mutex);
+ data_model = gda_web_recordset_new (cnc, ps, params, model_usage,
+ col_types, cdata->session_id, node, error);
+ gda_mutex_unlock (cdata->mutex);
+ retval = (GObject*) data_model;
+
+ if (! gda_web_recordset_store (GDA_WEB_RECORDSET (data_model), node, error)) {
+ g_object_unref (G_OBJECT (data_model));
+ retval = NULL;
+ }
+ }
+ else if (!strcmp ((gchar*) node->name, "preparehash")) {
+ xmlChar *contents;
+ contents = xmlNodeGetContent (node);
+ g_free (ps->pstmt_hash);
+ ps->pstmt_hash = g_strdup ((gchar*) contents);
+ xmlFree (contents);
+ }
+ }
+
+ xmlFreeDoc (replydoc);
+ g_object_unref (ps);
+ return retval;
+}
+
+/*
+ * starts a distributed transaction: put the XA transaction in the ACTIVE state
+ */
+static gboolean
+gda_web_provider_xa_start (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error)
+{
+ WebConnectionData *cdata;
+
+ 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 = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ TO_IMPLEMENT;
+ return 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 gboolean
+gda_web_provider_xa_end (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error)
+{
+ WebConnectionData *cdata;
+
+ 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 = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ TO_IMPLEMENT;
+ return FALSE;
+}
+
+/*
+ * prepares the distributed transaction: put the XA transaction in the PREPARED state
+ */
+static gboolean
+gda_web_provider_xa_prepare (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error)
+{
+ WebConnectionData *cdata;
+
+ 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 = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ TO_IMPLEMENT;
+ return FALSE;
+}
+
+/*
+ * commits the distributed transaction: actually write the prepared data to the database and
+ * terminates the XA transaction
+ */
+static gboolean
+gda_web_provider_xa_commit (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error)
+{
+ WebConnectionData *cdata;
+
+ 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 = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ TO_IMPLEMENT;
+ return FALSE;
+}
+
+/*
+ * Rolls back an XA transaction, possible only if in the ACTIVE, IDLE or PREPARED state
+ */
+static gboolean
+gda_web_provider_xa_rollback (GdaServerProvider *provider, GdaConnection *cnc,
+ const GdaXaTransactionId *xid, GError **error)
+{
+ WebConnectionData *cdata;
+
+ 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 = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ TO_IMPLEMENT;
+ return 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 *
+gda_web_provider_xa_recover (GdaServerProvider *provider, GdaConnection *cnc,
+ GError **error)
+{
+ WebConnectionData *cdata;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
+
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return NULL;
+
+ TO_IMPLEMENT;
+ return NULL;
+}
diff --git a/providers/web/gda-web-provider.h b/providers/web/gda-web-provider.h
new file mode 100644
index 0000000..63953a1
--- /dev/null
+++ b/providers/web/gda-web-provider.h
@@ -0,0 +1,51 @@
+/* GDA provider
+ * 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_WEB_PROVIDER_H__
+#define __GDA_WEB_PROVIDER_H__
+
+#include <libgda/gda-server-provider.h>
+
+#define GDA_TYPE_WEB_PROVIDER (gda_web_provider_get_type())
+#define GDA_WEB_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_WEB_PROVIDER, GdaWebProvider))
+#define GDA_WEB_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_WEB_PROVIDER, GdaWebProviderClass))
+#define GDA_IS_WEB_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_WEB_PROVIDER))
+#define GDA_IS_WEB_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDA_TYPE_WEB_PROVIDER))
+
+typedef struct _GdaWebProvider GdaWebProvider;
+typedef struct _GdaWebProviderClass GdaWebProviderClass;
+
+struct _GdaWebProvider {
+ GdaServerProvider provider;
+};
+
+struct _GdaWebProviderClass {
+ GdaServerProviderClass parent_class;
+};
+
+G_BEGIN_DECLS
+
+GType gda_web_provider_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/providers/web/gda-web-pstmt.c b/providers/web/gda-web-pstmt.c
new file mode 100644
index 0000000..dfe412c
--- /dev/null
+++ b/providers/web/gda-web-pstmt.c
@@ -0,0 +1,143 @@
+/* GDA Web provider
+ * 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-web-pstmt.h"
+#include "gda-web-util.h"
+
+static void gda_web_pstmt_class_init (GdaWebPStmtClass *klass);
+static void gda_web_pstmt_init (GdaWebPStmt *pstmt, GdaWebPStmtClass *klass);
+static void gda_web_pstmt_finalize (GObject *object);
+
+static GObjectClass *parent_class = NULL;
+
+/**
+ * gda_web_pstmt_get_type
+ *
+ * Returns: the #GType of GdaWebPStmt.
+ */
+GType
+gda_web_pstmt_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+ static const GTypeInfo info = {
+ sizeof (GdaWebPStmtClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gda_web_pstmt_class_init,
+ NULL,
+ NULL,
+ sizeof (GdaWebPStmt),
+ 0,
+ (GInstanceInitFunc) gda_web_pstmt_init
+ };
+
+ g_static_mutex_lock (®istering);
+ if (type == 0)
+ type = g_type_register_static (GDA_TYPE_PSTMT, "GdaWebPStmt", &info, 0);
+ g_static_mutex_unlock (®istering);
+ }
+ return type;
+}
+
+static void
+gda_web_pstmt_class_init (GdaWebPStmtClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ parent_class = g_type_class_peek_parent (klass);
+
+ /* virtual functions */
+ object_class->finalize = gda_web_pstmt_finalize;
+}
+
+static void
+gda_web_pstmt_init (GdaWebPStmt *pstmt, GdaWebPStmtClass *klass)
+{
+ g_return_if_fail (GDA_IS_PSTMT (pstmt));
+
+ pstmt->pstmt_hash = NULL;
+}
+
+static void
+gda_web_pstmt_finalize (GObject *object)
+{
+ GdaWebPStmt *pstmt = (GdaWebPStmt *) object;
+
+ g_return_if_fail (GDA_IS_PSTMT (pstmt));
+
+ if (pstmt->pstmt_hash) {
+ WebConnectionData *cdata;
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (pstmt->cnc);
+ if (!cdata)
+ goto next;
+
+ /* send command to deallocate prepared statement */
+ xmlDocPtr doc;
+ xmlNodePtr root, cmdnode, node;
+ gchar *token;
+ doc = xmlNewDoc (BAD_CAST "1.0");
+ root = xmlNewNode (NULL, BAD_CAST "request");
+ xmlDocSetRootElement (doc, root);
+ token = _gda_web_compute_token (cdata);
+ xmlNewChild (root, NULL, BAD_CAST "token", BAD_CAST token);
+ g_free (token);
+ cmdnode = xmlNewChild (root, NULL, BAD_CAST "cmd", BAD_CAST "UNPREPARE");
+ node = xmlNewChild (cmdnode, NULL, BAD_CAST "preparehash", BAD_CAST pstmt->pstmt_hash);
+
+ xmlChar *cmde;
+ xmlDocPtr replydoc;
+ int size;
+ gchar status;
+
+ xmlDocDumpMemory (doc, &cmde, &size);
+ xmlFreeDoc (doc);
+ replydoc = _gda_web_send_message_to_frontend (pstmt->cnc, cdata, MESSAGE_UNPREPARE, (gchar*) cmde,
+ cdata->key, &status);
+ xmlFree (cmde);
+ if (replydoc)
+ xmlFreeDoc (replydoc);
+
+ next:
+ /* free memory */
+ g_free (pstmt->pstmt_hash);
+ }
+
+ /* chain to parent class */
+ parent_class->finalize (object);
+}
+
+GdaWebPStmt *
+gda_web_pstmt_new (GdaConnection *cnc, const gchar *pstmt_hash)
+{
+ GdaWebPStmt *pstmt;
+ g_return_val_if_fail (pstmt_hash && *pstmt_hash, NULL);
+
+ pstmt = (GdaWebPStmt *) g_object_new (GDA_TYPE_WEB_PSTMT, NULL);
+ pstmt->cnc = cnc;
+ pstmt->pstmt_hash = g_strdup (pstmt_hash);
+
+ return pstmt;
+}
diff --git a/providers/web/gda-web-pstmt.h b/providers/web/gda-web-pstmt.h
new file mode 100644
index 0000000..9844ea0
--- /dev/null
+++ b/providers/web/gda-web-pstmt.h
@@ -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_WEB_PSTMT_H__
+#define __GDA_WEB_PSTMT_H__
+
+#include <providers-support/gda-pstmt.h>
+#include "gda-web.h"
+#include <libgda/libgda.h>
+
+G_BEGIN_DECLS
+
+#define GDA_TYPE_WEB_PSTMT (gda_web_pstmt_get_type())
+#define GDA_WEB_PSTMT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_WEB_PSTMT, GdaWebPStmt))
+#define GDA_WEB_PSTMT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_WEB_PSTMT, GdaWebPStmtClass))
+#define GDA_IS_WEB_PSTMT(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, GDA_TYPE_WEB_PSTMT))
+#define GDA_IS_WEB_PSTMT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GDA_TYPE_WEB_PSTMT))
+
+typedef struct _GdaWebPStmt GdaWebPStmt;
+typedef struct _GdaWebPStmtClass GdaWebPStmtClass;
+
+struct _GdaWebPStmt {
+ GdaPStmt object;
+
+ GdaConnection *cnc;
+ gchar *pstmt_hash;
+};
+
+struct _GdaWebPStmtClass {
+ GdaPStmtClass parent_class;
+};
+
+GType gda_web_pstmt_get_type (void) G_GNUC_CONST;
+GdaWebPStmt *gda_web_pstmt_new (GdaConnection *cnc, const gchar *pstmt_hash);
+
+G_END_DECLS
+
+#endif
diff --git a/providers/web/gda-web-recordset.c b/providers/web/gda-web-recordset.c
new file mode 100644
index 0000000..910c119
--- /dev/null
+++ b/providers/web/gda-web-recordset.c
@@ -0,0 +1,631 @@
+/* GDA provider
+ * 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 <libgda/gda-util.h>
+#include <libgda/sql-parser/gda-sql-parser.h>
+#include <libgda/gda-connection-private.h>
+#include "gda-web.h"
+#include "gda-web-recordset.h"
+#include "gda-web-provider.h"
+
+#define _GDA_PSTMT(x) ((GdaPStmt*)(x))
+
+static void gda_web_recordset_class_init (GdaWebRecordsetClass *klass);
+static void gda_web_recordset_init (GdaWebRecordset *recset,
+ GdaWebRecordsetClass *klass);
+static void gda_web_recordset_dispose (GObject *object);
+
+/* virtual methods */
+static gint gda_web_recordset_fetch_nb_rows (GdaDataSelect *model);
+static gboolean gda_web_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
+static gboolean gda_web_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
+static gboolean gda_web_recordset_fetch_prev (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
+static gboolean gda_web_recordset_fetch_at (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
+
+
+struct _GdaWebRecordsetPrivate {
+ GdaConnection *cnc;
+
+ GdaConnection *rs_cnc; /* connection to store resultsets */
+ gboolean table_created;
+
+ GdaStatement *insert; /* adding new data to @rs_cnc */
+ GdaStatement *select; /* final SELECT */
+ GdaDataModel *real_model;
+};
+static GObjectClass *parent_class = NULL;
+
+/*
+ * Object init and finalize
+ */
+static void
+gda_web_recordset_init (GdaWebRecordset *recset,
+ GdaWebRecordsetClass *klass)
+{
+ g_return_if_fail (GDA_IS_WEB_RECORDSET (recset));
+ recset->priv = g_new0 (GdaWebRecordsetPrivate, 1);
+ recset->priv->cnc = NULL;
+ recset->priv->rs_cnc = NULL;
+ recset->priv->table_created = FALSE;
+ recset->priv->insert = NULL;
+ recset->priv->select = NULL;
+}
+
+static void
+gda_web_recordset_class_init (GdaWebRecordsetClass *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_web_recordset_dispose;
+ pmodel_class->fetch_nb_rows = gda_web_recordset_fetch_nb_rows;
+ pmodel_class->fetch_random = gda_web_recordset_fetch_random;
+
+ pmodel_class->fetch_next = gda_web_recordset_fetch_next;
+ pmodel_class->fetch_prev = gda_web_recordset_fetch_prev;
+ pmodel_class->fetch_at = gda_web_recordset_fetch_at;
+}
+
+static void
+gda_web_recordset_dispose (GObject *object)
+{
+ GdaWebRecordset *recset = (GdaWebRecordset *) object;
+
+ g_return_if_fail (GDA_IS_WEB_RECORDSET (recset));
+
+ if (recset->priv) {
+ if (recset->priv->cnc)
+ g_object_unref (recset->priv->cnc);
+ if (recset->priv->real_model)
+ g_object_unref (recset->priv->real_model);
+ if (recset->priv->rs_cnc)
+ g_object_unref (recset->priv->rs_cnc);
+
+ if (recset->priv->insert)
+ g_object_unref (recset->priv->insert);
+ if (recset->priv->select)
+ g_object_unref (recset->priv->select);
+
+ g_free (recset->priv);
+ recset->priv = NULL;
+ }
+
+ parent_class->dispose (object);
+}
+
+/*
+ * Public functions
+ */
+
+GType
+gda_web_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 (GdaWebRecordsetClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gda_web_recordset_class_init,
+ NULL,
+ NULL,
+ sizeof (GdaWebRecordset),
+ 0,
+ (GInstanceInitFunc) gda_web_recordset_init
+ };
+ g_static_mutex_lock (®istering);
+ if (type == 0)
+ type = g_type_register_static (GDA_TYPE_DATA_SELECT, "GdaWebRecordset", &info, 0);
+ g_static_mutex_unlock (®istering);
+ }
+
+ return type;
+}
+
+/*
+ * the @ps struct is modified and transferred to the new data model created in
+ * this function
+ */
+GdaDataModel *
+gda_web_recordset_new (GdaConnection *cnc, GdaWebPStmt *ps, GdaSet *exec_params,
+ GdaDataModelAccessFlags flags, GType *col_types,
+ const gchar *session_id, xmlNodePtr data_node, GError **error)
+{
+ GdaWebRecordset *model;
+ gint i;
+ GdaDataModelAccessFlags rflags;
+ static guint counter = 0;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (ps != NULL, NULL);
+
+ /* prepare internal connection which will be used to store
+ * the recordset's data
+ */
+ GdaConnection *rs_cnc;
+ gchar *fname, *tmp;
+
+ for (fname = (gchar*) session_id; *fname && (*fname != '='); fname++);
+ g_assert (*fname == '=');
+ fname++;
+ tmp = g_strdup_printf ("%s%u.db", fname, counter++);
+ rs_cnc = gda_connection_open_sqlite (NULL, tmp, TRUE);
+ if (!rs_cnc) {
+ fname = g_build_filename (g_get_tmp_dir(), tmp, NULL);
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+ GDA_SERVER_PROVIDER_INTERNAL_ERROR,
+ _("Can't create temporary file '%s'"), fname);
+ g_free (tmp);
+ g_free (fname);
+ return NULL;
+ }
+ g_free (tmp);
+
+ /* make sure @ps reports the correct number of columns using the API*/
+ if (_GDA_PSTMT (ps)->ncols < 0) {
+ xmlNodePtr child;
+ _GDA_PSTMT (ps)->ncols = 0;
+ for (child = data_node->children; child; child = child->next) {
+ if (!strcmp ((gchar*) child->name, "gda_array_field"))
+ _GDA_PSTMT (ps)->ncols ++;
+ }
+ }
+
+ /* completing @ps if not yet done */
+ if (!_GDA_PSTMT (ps)->types && (_GDA_PSTMT (ps)->ncols > 0)) {
+ /* create prepared statement's columns */
+ GSList *list;
+ for (i = 0; i < _GDA_PSTMT (ps)->ncols; i++)
+ _GDA_PSTMT (ps)->tmpl_columns = g_slist_prepend (_GDA_PSTMT (ps)->tmpl_columns,
+ gda_column_new ());
+ _GDA_PSTMT (ps)->tmpl_columns = g_slist_reverse (_GDA_PSTMT (ps)->tmpl_columns);
+
+ /* create prepared statement's types */
+ _GDA_PSTMT (ps)->types = g_new0 (GType, _GDA_PSTMT (ps)->ncols); /* all types are initialized to GDA_TYPE_NULL */
+ if (col_types) {
+ for (i = 0; ; i++) {
+ if (col_types [i] > 0) {
+ if (col_types [i] == G_TYPE_NONE)
+ break;
+ if (i >= _GDA_PSTMT (ps)->ncols)
+ g_warning (_("Column %d out of range (0-%d), ignoring its specified type"), i,
+ _GDA_PSTMT (ps)->ncols - 1);
+ else
+ _GDA_PSTMT (ps)->types [i] = col_types [i];
+ }
+ }
+ }
+
+ /* fill GdaColumn's data */
+ xmlNodePtr child;
+ WebConnectionData *cdata;
+ cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ for (child = data_node->children, i = 0, list = _GDA_PSTMT (ps)->tmpl_columns;
+ child && (i < GDA_PSTMT (ps)->ncols);
+ child = child->next, i++, list = list->next) {
+ GdaColumn *column;
+ xmlChar *prop;
+ gboolean typeset = FALSE;
+
+ while (strcmp ((gchar*) child->name, "gda_array_field"))
+ child = child->next;
+ column = GDA_COLUMN (list->data);
+
+ if (_GDA_PSTMT (ps)->types [i] == 0) {
+ if (cdata && cdata->reuseable && cdata->reuseable->operations->re_get_type) {
+ prop = xmlGetProp (child, BAD_CAST "dbtype");
+ if (prop) {
+ GType type;
+ type = cdata->reuseable->operations->re_get_type (cnc, cdata->reuseable,
+ (gchar*) prop);
+ if (type != GDA_TYPE_NULL) {
+ _GDA_PSTMT (ps)->types [i] = type;
+ gda_column_set_g_type (column, type);
+ typeset = TRUE;
+ }
+ xmlFree (prop);
+ }
+ }
+ if (!typeset) {
+ prop = xmlGetProp (child, BAD_CAST "gdatype");
+ if (prop) {
+ GType type;
+
+ type = gda_g_type_from_string ((gchar*) prop);
+ _GDA_PSTMT (ps)->types [i] = type;
+ gda_column_set_g_type (column, type);
+ xmlFree (prop);
+ }
+ else {
+ _GDA_PSTMT (ps)->types [i] = G_TYPE_STRING;
+ gda_column_set_g_type (column, G_TYPE_STRING);
+ }
+ }
+ }
+ else
+ gda_column_set_g_type (column, _GDA_PSTMT (ps)->types [i]);
+ prop = xmlGetProp (child, BAD_CAST "name");
+ if (prop && *prop) {
+ gda_column_set_name (column, (gchar*) prop);
+ gda_column_set_description (column, (gchar*) prop);
+ }
+ else {
+ gchar *tmp;
+ tmp = g_strdup_printf ("col%d", i);
+ gda_column_set_name (column, tmp);
+ gda_column_set_description (column, tmp);
+ g_free (tmp);
+ }
+ if (prop)
+ xmlFree (prop);
+ }
+ }
+
+ /* determine access mode: RANDOM or CURSOR FORWARD are the only supported */
+ if (flags & GDA_DATA_MODEL_ACCESS_RANDOM)
+ rflags = GDA_DATA_MODEL_ACCESS_RANDOM;
+ else
+ rflags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
+
+ /* create data model */
+ model = g_object_new (GDA_TYPE_WEB_RECORDSET,
+ "prepared-stmt", ps,
+ "model-usage", rflags,
+ "exec-params", exec_params, NULL);
+ model->priv->cnc = cnc;
+ model->priv->rs_cnc = rs_cnc;
+ g_object_ref (cnc);
+
+ return GDA_DATA_MODEL (model);
+}
+
+
+/*
+ * create tha table to store the actual data
+ */
+static gboolean
+create_table (GdaWebRecordset *rs, GError **error)
+{
+#define TABLE_NAME "data"
+ GString *string;
+ gint i, ncols;
+ gboolean retval = FALSE;
+
+ GdaSqlBuilder *sb, *ib;
+
+ ib = gda_sql_builder_new (GDA_SQL_STATEMENT_INSERT);
+ sb = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT);
+
+ gda_sql_builder_set_table (ib, TABLE_NAME);
+ gda_sql_builder_select_add_target (sb, 0, gda_sql_builder_add_id (sb, 0, TABLE_NAME), NULL);
+
+ string = g_string_new ("CREATE table " TABLE_NAME " (");
+ ncols = gda_data_model_get_n_columns ((GdaDataModel*) rs);
+ for (i = 0; i < ncols; i++) {
+ GdaColumn *column;
+ gchar *colname;
+
+ column = gda_data_model_describe_column ((GdaDataModel*) rs, i);
+ if (i > 0)
+ g_string_append (string, ", ");
+ colname = g_strdup_printf ("col%d", i);
+ g_string_append_printf (string, "%s %s", colname,
+ gda_g_type_to_string (gda_column_get_g_type (column)));
+
+ gda_sql_builder_add_field (ib, gda_sql_builder_add_id (ib, 0, colname),
+ gda_sql_builder_add_param (ib, 0, colname,
+ gda_column_get_g_type (column), TRUE));
+ gda_sql_builder_add_field (sb, gda_sql_builder_add_id (sb, 0, colname), 0);
+
+ g_free (colname);
+ }
+ g_string_append (string, ")");
+ /*g_print ("CREATE SQL: [%s]\n", string->str);*/
+
+ GdaStatement *stmt;
+ GdaSqlParser *parser;
+ const gchar *remain;
+ parser = gda_sql_parser_new ();
+ stmt = gda_sql_parser_parse_string (parser, string->str, &remain, NULL);
+ g_object_unref (parser);
+ if (!stmt || remain) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+ GDA_SERVER_PROVIDER_INTERNAL_ERROR, "%s",
+ _("Can't create temporary table to store data from web server"));
+ goto out;
+ }
+ if (gda_connection_statement_execute_non_select (rs->priv->rs_cnc, stmt, NULL, NULL, NULL)) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+ GDA_SERVER_PROVIDER_INTERNAL_ERROR, "%s",
+ _("Can't create temporary table to store data from web server"));
+ goto out;
+ }
+
+ rs->priv->insert = gda_sql_builder_get_statement (ib, error);
+ if (rs->priv->insert)
+ rs->priv->select = gda_sql_builder_get_statement (sb, NULL);
+ if (! rs->priv->insert || ! rs->priv->select) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+ GDA_SERVER_PROVIDER_INTERNAL_ERROR, "%s",
+ _("Can't create temporary table to store data from web server"));
+ goto out;
+ }
+ /*g_print ("INSERT: [%s]\n", gda_statement_to_sql (rs->priv->insert, NULL, NULL));
+ g_print ("SELECT: [%s]\n", gda_statement_to_sql (rs->priv->select, NULL, NULL));*/
+
+ retval = TRUE;
+ out:
+ if (!retval) {
+ if (rs->priv->insert)
+ g_object_unref (rs->priv->insert);
+ rs->priv->insert = NULL;
+ if (rs->priv->select)
+ g_object_unref (rs->priv->select);
+ rs->priv->select = NULL;
+ }
+ g_object_unref (ib);
+ g_object_unref (sb);
+ if (stmt)
+ g_object_unref (stmt);
+ g_string_free (string, TRUE);
+ return retval;
+}
+
+/**
+ * gda_web_recordset_store
+ *
+ * Store some more rows coming from the @data_node array
+ */
+gboolean
+gda_web_recordset_store (GdaWebRecordset *rs, xmlNodePtr data_node, GError **error)
+{
+ GdaSet *params, *iter;
+ GdaDataModel *data;
+ GSList *plist, *ilist;
+ gboolean retval = FALSE;
+ gint i, ncols;
+ xmlNodePtr node;
+
+ g_return_val_if_fail (GDA_IS_WEB_RECORDSET (rs), FALSE);
+ g_return_val_if_fail (data_node, FALSE);
+ g_return_val_if_fail (!strcmp ((gchar*) data_node->name, "gda_array"), FALSE);
+ if (! rs->priv->table_created && ! create_table (rs, error))
+ return FALSE;
+
+ /* modify the @data_node tree to set the correct data types */
+ ncols = gda_data_model_get_n_columns ((GdaDataModel*) rs);
+ for (node = data_node->children, i = 0;
+ (i < ncols) && node;
+ node = node->next) {
+ if (strcmp ((gchar*) node->name, "gda_array_field"))
+ continue;
+ GdaColumn *column;
+
+ column = gda_data_model_describe_column ((GdaDataModel*) rs, i);
+ i++;
+ xmlSetProp (node, BAD_CAST "gdatype", gda_g_type_to_string (gda_column_get_g_type (column)));
+ }
+
+ /* for each row in @data_mode, insert the row in @rs->priv->rs_cnc */
+ g_assert (rs->priv->insert);
+
+ data = gda_data_model_import_new_xml_node (data_node);
+ if (!data) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+ GDA_SERVER_PROVIDER_INTERNAL_ERROR, "%s",
+ _("Can't import data from web server"));
+ return FALSE;
+ }
+
+ g_assert (gda_statement_get_parameters (rs->priv->insert, ¶ms, NULL));
+ iter = GDA_SET (gda_data_model_create_iter (data));
+ for (plist = params->holders, ilist = iter->holders;
+ plist && ilist;
+ plist = plist->next, ilist = ilist->next) {
+ GdaHolder *ph, *ih;
+ ph = GDA_HOLDER (plist->data);
+ ih = GDA_HOLDER (ilist->data);
+ g_assert (gda_holder_set_bind (ph, ih, NULL));
+ }
+ g_assert (!plist && !ilist);
+
+ for (; gda_data_model_iter_move_next ((GdaDataModelIter*) iter); ) {
+ if (gda_connection_statement_execute_non_select (rs->priv->rs_cnc, rs->priv->insert,
+ params, NULL, NULL) == -1) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+ GDA_SERVER_PROVIDER_INTERNAL_ERROR, "%s",
+ _("Can't import data from web server"));
+ goto out;
+ }
+ }
+
+ retval = TRUE;
+
+ out:
+ g_object_unref (data);
+ g_object_unref (iter);
+ g_object_unref (params);
+
+ return retval;
+}
+
+static void
+create_real_model (GdaWebRecordset *rs)
+{
+ if (rs->priv->real_model)
+ return;
+ rs->priv->real_model = gda_connection_statement_execute_select (rs->priv->rs_cnc, rs->priv->select,
+ NULL, NULL);
+}
+
+/*
+ * Get the number of rows in @model, if possible
+ */
+static gint
+gda_web_recordset_fetch_nb_rows (GdaDataSelect *model)
+{
+ GdaWebRecordset *imodel;
+
+ imodel = GDA_WEB_RECORDSET (model);
+ if (model->advertized_nrows >= 0)
+ return model->advertized_nrows;
+
+ create_real_model (imodel);
+ if (imodel->priv->real_model)
+ model->advertized_nrows = gda_data_model_get_n_rows (imodel->priv->real_model);
+
+ return model->advertized_nrows;
+}
+
+/*
+ * Create a new filled #GdaRow object for the row at position @rownum, and put it into *prow.
+ *
+ * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
+ * - If *prow is NULL then a new #GdaRow object has to be created,
+ * - and otherwise *prow contains a #GdaRow object which has already been created
+ * (through a call to this very function), and in this case it should not be modified
+ * but the function may return FALSE if an error occurred.
+ *
+ * Memory management for that new GdaRow object is left to the implementation, which
+ * can use gda_data_select_take_row(). If new row objects are "given" to the GdaDataSelect implemantation
+ * using that method, then this method should detect when all the data model rows have been analyzed
+ * (when model->nb_stored_rows == model->advertized_nrows) and then possibly discard the API handle
+ * as it won't be used anymore to fetch rows.
+ */
+static gboolean
+gda_web_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
+{
+ GdaWebRecordset *imodel;
+
+ imodel = GDA_WEB_RECORDSET (model);
+
+ if (*prow)
+ return TRUE;
+
+ create_real_model (imodel);
+ if (imodel->priv->real_model)
+ return GDA_DATA_SELECT_CLASS (G_OBJECT_GET_CLASS (imodel->priv->real_model))->fetch_random
+ ((GdaDataSelect*) imodel->priv->real_model, prow, rownum, error);
+
+ return FALSE;
+}
+
+/*
+ * Create a new filled #GdaRow object for the next cursor row, and put it into *prow.
+ *
+ * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
+ * - If *prow is NULL then a new #GdaRow object has to be created,
+ * - and otherwise *prow contains a #GdaRow object which has already been created
+ * (through a call to this very function), and in this case it should not be modified
+ * but the function may return FALSE if an error occurred.
+ *
+ * Memory management for that new GdaRow object is left to the implementation, which
+ * can use gda_data_select_take_row().
+ */
+static gboolean
+gda_web_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
+{
+ GdaWebRecordset *imodel;
+
+ imodel = GDA_WEB_RECORDSET (model);
+
+ if (*prow)
+ return TRUE;
+
+ create_real_model (imodel);
+ if (imodel->priv->real_model)
+ return GDA_DATA_SELECT_CLASS (G_OBJECT_GET_CLASS (imodel->priv->real_model))->fetch_next
+ ((GdaDataSelect*) imodel->priv->real_model, prow, rownum, error);
+
+ return FALSE;
+}
+
+/*
+ * Create a new filled #GdaRow object for the previous cursor row, and put it into *prow.
+ *
+ * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
+ * - If *prow is NULL then a new #GdaRow object has to be created,
+ * - and otherwise *prow contains a #GdaRow object which has already been created
+ * (through a call to this very function), and in this case it should not be modified
+ * but the function may return FALSE if an error occurred.
+ *
+ * Memory management for that new GdaRow object is left to the implementation, which
+ * can use gda_data_select_take_row().
+ */
+static gboolean
+gda_web_recordset_fetch_prev (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
+{
+ GdaWebRecordset *imodel;
+
+ imodel = GDA_WEB_RECORDSET (model);
+
+ if (*prow)
+ return TRUE;
+
+ create_real_model (imodel);
+ if (imodel->priv->real_model)
+ return GDA_DATA_SELECT_CLASS (G_OBJECT_GET_CLASS (imodel->priv->real_model))->fetch_prev
+ ((GdaDataSelect*) imodel->priv->real_model, prow, rownum, error);
+
+ return FALSE;
+}
+
+/*
+ * Create a new filled #GdaRow object for the cursor row at position @rownum, and put it into *prow.
+ *
+ * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
+ * - If *prow is NULL then a new #GdaRow object has to be created,
+ * - and otherwise *prow contains a #GdaRow object which has already been created
+ * (through a call to this very function), and in this case it should not be modified
+ * but the function may return FALSE if an error occurred.
+ *
+ * Memory management for that new GdaRow object is left to the implementation, which
+ * can use gda_data_select_take_row().
+ */
+static gboolean
+gda_web_recordset_fetch_at (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
+{
+ GdaWebRecordset *imodel;
+
+ imodel = GDA_WEB_RECORDSET (model);
+
+ if (*prow)
+ return TRUE;
+
+ create_real_model (imodel);
+ if (imodel->priv->real_model)
+ return GDA_DATA_SELECT_CLASS (G_OBJECT_GET_CLASS (imodel->priv->real_model))->fetch_at
+ ((GdaDataSelect*) imodel->priv->real_model, prow, rownum, error);
+
+ return FALSE;
+}
+
diff --git a/providers/web/gda-web-recordset.h b/providers/web/gda-web-recordset.h
new file mode 100644
index 0000000..84dabd7
--- /dev/null
+++ b/providers/web/gda-web-recordset.h
@@ -0,0 +1,60 @@
+/* GDA provider
+ * 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_WEB_RECORDSET_H__
+#define __GDA_WEB_RECORDSET_H__
+
+#include <libgda/libgda.h>
+#include <providers-support/gda-data-select-priv.h>
+#include "gda-web-pstmt.h"
+
+G_BEGIN_DECLS
+
+#define GDA_TYPE_WEB_RECORDSET (gda_web_recordset_get_type())
+#define GDA_WEB_RECORDSET(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_WEB_RECORDSET, GdaWebRecordset))
+#define GDA_WEB_RECORDSET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_WEB_RECORDSET, GdaWebRecordsetClass))
+#define GDA_IS_WEB_RECORDSET(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_WEB_RECORDSET))
+#define GDA_IS_WEB_RECORDSET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDA_TYPE_WEB_RECORDSET))
+
+typedef struct _GdaWebRecordset GdaWebRecordset;
+typedef struct _GdaWebRecordsetClass GdaWebRecordsetClass;
+typedef struct _GdaWebRecordsetPrivate GdaWebRecordsetPrivate;
+
+struct _GdaWebRecordset {
+ GdaDataSelect model;
+ GdaWebRecordsetPrivate *priv;
+};
+
+struct _GdaWebRecordsetClass {
+ GdaDataSelectClass parent_class;
+};
+
+GType gda_web_recordset_get_type (void) G_GNUC_CONST;
+GdaDataModel *gda_web_recordset_new (GdaConnection *cnc, GdaWebPStmt *ps, GdaSet *exec_params,
+ GdaDataModelAccessFlags flags, GType *col_types,
+ const gchar *session_id, xmlNodePtr data_node, GError **error);
+
+gboolean gda_web_recordset_store (GdaWebRecordset *rs, xmlNodePtr data_node, GError **error);
+
+G_END_DECLS
+
+#endif
diff --git a/providers/web/gda-web-util.c b/providers/web/gda-web-util.c
new file mode 100644
index 0000000..761fd89
--- /dev/null
+++ b/providers/web/gda-web-util.c
@@ -0,0 +1,657 @@
+/* GDA provider
+ * 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.
+ */
+
+/*#define DEBUG_WEB_PROV*/
+
+#include <glib/gi18n-lib.h>
+#include "gda-web-util.h"
+#include <string.h>
+#include "../reuseable/reuse-all.h"
+
+/* Use the RSA reference implementation included in the RFC-1321, http://www.freesoft.org/CIE/RFC/1321/ */
+#include "global.h"
+#include "md5.h"
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#define PAD_LEN 64 /* PAD length */
+#define SIG_LEN 16 /* MD5 digest length */
+/*
+ * From RFC 2104
+ */
+static void
+hmac_md5 (uint8_t* text, /* pointer to data stream */
+ int text_len, /* length of data stream */
+ uint8_t* key, /* pointer to authentication key */
+ int key_len, /* length of authentication key */
+ uint8_t *hmac) /* returned hmac-md5 */
+{
+ MD5_CTX md5c;
+ uint8_t k_ipad[PAD_LEN]; /* inner padding - key XORd with ipad */
+ uint8_t k_opad[PAD_LEN]; /* outer padding - key XORd with opad */
+ uint8_t keysig[SIG_LEN];
+ int i;
+
+ /* if key is longer than PAD length, reset it to key=MD5(key) */
+ if (key_len > PAD_LEN) {
+ MD5_CTX md5key;
+
+ MD5Init (&md5key);
+ MD5Update (&md5key, key, key_len);
+ MD5Final (keysig, &md5key);
+
+ key = keysig;
+ key_len = SIG_LEN;
+ }
+
+ /*
+ * the HMAC_MD5 transform looks like:
+ *
+ * MD5(Key XOR opad, MD5(Key XOR ipad, text))
+ *
+ * where Key is an n byte key
+ * ipad is the byte 0x36 repeated 64 times
+
+ * opad is the byte 0x5c repeated 64 times
+ * and text is the data being protected
+ */
+
+ /* Zero pads and store key */
+ memset (k_ipad, 0, PAD_LEN);
+ memcpy (k_ipad, key, key_len);
+ memcpy (k_opad, k_ipad, PAD_LEN);
+
+ /* XOR key with ipad and opad values */
+ for (i=0; i<PAD_LEN; i++) {
+ k_ipad[i] ^= 0x36;
+ k_opad[i] ^= 0x5c;
+ }
+
+ /* perform inner MD5 */
+ MD5Init (&md5c); /* start inner hash */
+ MD5Update (&md5c, k_ipad, PAD_LEN); /* hash inner pad */
+ MD5Update (&md5c, text, text_len); /* hash text */
+ MD5Final (hmac, &md5c); /* store inner hash */
+
+ /* perform outer MD5 */
+ MD5Init (&md5c); /* start outer hash */
+ MD5Update (&md5c, k_opad, PAD_LEN); /* hash outer pad */
+ MD5Update (&md5c, hmac, SIG_LEN); /* hash inner hash */
+ MD5Final (hmac, &md5c); /* store results */
+}
+
+static gboolean
+check_hash (const gchar *key, const gchar *data, const gchar *expected_hash)
+{
+ uint8_t hmac[16];
+ GString *md5str;
+ gint i;
+ gboolean retval = TRUE;
+
+ hmac_md5 ((uint8_t *) data, strlen (data),
+ (uint8_t *) key, strlen (key), hmac);
+ md5str = g_string_new ("");
+ for (i = 0; i < 16; i++)
+ g_string_append_printf (md5str, "%02x", hmac[i]);
+
+ if (strcmp (md5str->str, expected_hash))
+ retval = FALSE;
+
+ g_string_free (md5str, TRUE);
+ return retval;
+}
+
+/*
+ * If there is a mismatch or an error, then gda_connection_add_event_string() is used
+ *
+ * - Modifies @sbuffer (to separate HASH from XML part)
+ * - if all OK, extracs the <challenge> value and replace cdata->next_challenge with it (or simply
+ * reset cdata->next_challenge to NULL)
+ *
+ * Returns: a new #xmlDocPtr, or %NULL on error
+ */
+static xmlDocPtr
+decode_buffer_response (GdaConnection *cnc, WebConnectionData *cdata, SoupBuffer *sbuffer,
+ gchar *out_status_chr, guint *out_counter_id)
+{
+ xmlDocPtr doc;
+ gchar *ptr, *response;
+
+ if (out_status_chr)
+ *out_status_chr = 0;
+ if (out_counter_id)
+ *out_counter_id = 0;
+
+ g_assert (sbuffer);
+ response = (gchar*) sbuffer->data;
+
+ for (ptr = response; *ptr && (*ptr != '\n'); ptr++);
+ if (*ptr != '\n') {
+ gda_connection_add_event_string (cnc, _("Could not parse server's reponse"));
+ return NULL;
+ }
+ *ptr = 0;
+ ptr++;
+
+ if ((cdata->key && !check_hash (cdata->key, ptr, response)) &&
+ (cdata->server_secret && !check_hash (cdata->server_secret, ptr, response))) {
+ gda_connection_add_event_string (cnc, _("Invalid response hash"));
+ return NULL;
+ }
+ doc = xmlParseMemory (ptr, strlen (ptr));
+
+ if (!doc) {
+ gda_connection_add_event_string (cnc, _("Could not parse server's reponse"));
+ return NULL;
+ }
+ else {
+ xmlNodePtr node, root;
+
+ root = xmlDocGetRootElement (doc);
+ for (node = root->children; node; node = node->next) {
+ if (!strcmp ((gchar*) node->name, "session")) {
+ xmlChar *contents;
+ contents = xmlNodeGetContent (node);
+ g_free (cdata->session_id);
+ cdata->session_id = g_strdup ((gchar*) contents);
+ xmlFree (contents);
+ }
+ else if (!strcmp ((gchar*) node->name, "challenge")) {
+ xmlChar *contents;
+ if (cdata->next_challenge) {
+ g_free (cdata->next_challenge);
+ cdata->next_challenge = NULL;
+ }
+ contents = xmlNodeGetContent (node);
+ cdata->next_challenge = g_strdup ((gchar*) contents);
+ xmlFree (contents);
+ }
+ else if (out_status_chr && !strcmp ((gchar*) node->name, "status")) {
+ xmlChar *contents;
+ contents = xmlNodeGetContent (node);
+ *out_status_chr = *contents;
+ xmlFree (contents);
+ }
+ else if (out_counter_id && !strcmp ((gchar*) node->name, "counter")) {
+ xmlChar *contents;
+ contents = xmlNodeGetContent (node);
+ *out_counter_id = atoi ((gchar*) contents);
+ xmlFree (contents);
+ }
+ else if (!cdata->server_id && !strcmp ((gchar*) node->name, "servertype")) {
+ xmlChar *contents;
+ contents = xmlNodeGetContent (node);
+ cdata->server_id = g_strdup ((gchar*) contents);
+ xmlFree (contents);
+
+ /* FIXME: get version from server */
+ cdata->reuseable = _gda_provider_reuseable_new (cdata->server_id, NULL, NULL);
+#ifdef DEBUG_WEB_PROV
+ g_print ("REUSEABLE [%s]: %p\n", cdata->server_id, cdata->reuseable);
+#endif
+ }
+ }
+ }
+
+ return doc;
+}
+
+typedef struct {
+ GdaConnection *cnc;
+ WebConnectionData *cdata;
+} ThreadData;
+
+/* executed in sub thread */
+static void
+worker_got_chunk_cb (SoupMessage *msg, SoupBuffer *chunk, ThreadData *thdata)
+{
+ xmlDocPtr doc;
+ gchar *data, *ptr;
+
+ data = g_strndup (chunk->data, chunk->length);
+ soup_message_body_set_accumulate (msg->response_body, FALSE);
+
+#ifdef DEBUG_WEB_PROV
+ if (*data)
+ g_print (">>>> WORKER\n%s\n<<<< WORKER\n", data);
+#endif
+ if (!thdata->cdata->session_id) {
+ ptr = strstr (data, "</reply>");
+ if (ptr) {
+ gchar status;
+ guint counter_id;
+ ptr += 8;
+ *ptr = 0;
+ doc = decode_buffer_response (thdata->cnc, thdata->cdata, chunk, &status, &counter_id);
+ if (!doc || (status != 'O')) {
+ /* this cannot happen at the moment */
+ g_assert_not_reached ();
+ if (doc)
+ xmlFreeDoc (doc);
+ }
+ else {
+ gda_mutex_lock (thdata->cdata->mutex);
+ g_assert (thdata->cdata->worker_counter == counter_id);
+ gda_mutex_unlock (thdata->cdata->mutex);
+ xmlFreeDoc (doc);
+ }
+ }
+ }
+ g_free (data);
+}
+
+/* executed in sub thread */
+static gpointer
+start_worker_in_sub_thread (ThreadData *thdata)
+{
+ SoupMessage *msg;
+ gulong sigid;
+ gboolean runagain = TRUE;
+
+ while (runagain) {
+ GString *real_url;
+ gda_mutex_lock (thdata->cdata->mutex);
+ real_url = g_string_new (thdata->cdata->worker_url);
+ if (thdata->cdata->session_id)
+ g_string_append_printf (real_url, "?%s", thdata->cdata->session_id);
+ thdata->cdata->worker_running = TRUE;
+ if (thdata->cdata->worker_counter == 0)
+ thdata->cdata->worker_counter = 1;
+ else
+ thdata->cdata->worker_counter ++;
+ gda_mutex_unlock (thdata->cdata->mutex);
+
+ msg = soup_message_new ("GET", real_url->str);
+ /*g_print ("=== WORKER Request URL: [%s]\n", real_url->str);*/
+ if (!msg) {
+ g_warning (_("Invalid HOST/SCRIPT '%s'"), real_url->str);
+ g_string_free (real_url, TRUE);
+ gda_mutex_lock (thdata->cdata->mutex);
+ thdata->cdata->worker_running = FALSE;
+ gda_mutex_unlock (thdata->cdata->mutex);
+ g_free (thdata);
+ return NULL;
+ }
+ g_string_free (real_url, TRUE);
+
+ sigid = g_signal_connect (msg, "got-chunk",
+ G_CALLBACK (worker_got_chunk_cb), thdata);
+ guint res;
+ res = soup_session_send_message (thdata->cdata->worker_session, msg);
+
+ gda_mutex_lock (thdata->cdata->mutex);
+ thdata->cdata->worker_running = FALSE;
+ runagain = thdata->cdata->worker_needed;
+ runagain = runagain && SOUP_STATUS_IS_SUCCESSFUL (res);
+ gda_mutex_unlock (thdata->cdata->mutex);
+
+ g_signal_handler_disconnect (msg, sigid);
+ g_object_unref (msg);
+ }
+
+ g_free (thdata);
+#ifdef DEBUG_WEB_PROV
+ g_print ("Worker closed!\n");
+#endif
+
+ /* end of sub thread */
+ return NULL;
+}
+
+static void
+start_worker (GdaConnection *cnc, WebConnectionData *cdata)
+{
+ ThreadData *thdata;
+
+ thdata = g_new0 (ThreadData, 1); /* freed by sub thread */
+ thdata->cnc = cnc;
+ thdata->cdata = cdata;
+
+ /* set cdata->worker_running to TRUE to avoid having to add a delay */
+ gda_mutex_lock (cdata->mutex);
+ cdata->worker_running = TRUE;
+ gda_mutex_unlock (cdata->mutex);
+
+ if (! g_thread_create ((GThreadFunc) start_worker_in_sub_thread,
+ thdata, FALSE, NULL)) {
+ g_free (thdata);
+ gda_connection_add_event_string (cnc, _("Can't start new thread"));
+ return;
+ }
+
+ gint nb_retries;
+ for (nb_retries = 0; nb_retries < 10; nb_retries++) {
+ gboolean wait_over;
+ gda_mutex_lock (cdata->mutex);
+ wait_over = !cdata->worker_running || cdata->session_id;
+ gda_mutex_unlock (cdata->mutex);
+ if (wait_over)
+ break;
+ else
+ g_usleep (200000);
+ }
+
+ gda_mutex_lock (cdata->mutex);
+ if (!cdata->session_id) {
+ /* there was an error */
+ cdata->worker_running = FALSE;
+ }
+ gda_mutex_unlock (cdata->mutex);
+}
+
+/*
+ * Adds a HASH to the message using @hash_key, or adds "NOHASH" if @hash_key is %NULL
+ *
+ * If there is an error, then gda_connection_add_event_string() is called
+ *
+ * @out_status_chr, if NOT NULL will contain the 1st char of the <status> node's contents
+ */
+xmlDocPtr
+_gda_web_send_message_to_frontend (GdaConnection *cnc, WebConnectionData *cdata,
+ WebMessageType msgtype, const gchar *message,
+ const gchar *hash_key, gchar *out_status_chr)
+{
+ SoupMessage *msg;
+ guint status;
+ gchar *h_message;
+ gchar *real_url;
+ static gint counter = 0;
+
+ if (out_status_chr)
+ *out_status_chr = 0;
+
+ /* handle the need to run the worker to get an initial sessionID */
+ gda_mutex_lock (cdata->mutex);
+ cdata->worker_needed = TRUE;
+ if (!cdata->worker_running && !cdata->session_id) {
+ gda_mutex_unlock (cdata->mutex);
+ start_worker (cnc, cdata);
+
+ gda_mutex_lock (cdata->mutex);
+ if (! cdata->worker_running) {
+ gda_connection_add_event_string (cnc, _("Could not run PHP script on the server"));
+ cdata->worker_needed = FALSE;
+ gda_mutex_unlock (cdata->mutex);
+ return NULL;
+ }
+ }
+
+ /* prepare new message */
+ g_assert (cdata->session_id);
+ real_url = g_strdup_printf ("%s?%s&c=%d", cdata->front_url, cdata->session_id, counter++);
+ gda_mutex_unlock (cdata->mutex);
+ msg = soup_message_new ("POST", real_url);
+ if (!msg) {
+ gda_connection_add_event_string (cnc, _("Invalid HOST/SCRIPT '%s'"), real_url);
+ g_free (real_url);
+ return NULL;
+ }
+ g_free (real_url);
+
+ /* check context */
+ gda_mutex_lock (cdata->mutex);
+ if (gda_connection_get_transaction_status (cnc) &&
+ (!cdata->worker_running ||
+ ((msgtype == MESSAGE_EXEC) && (cdata->last_exec_counter != cdata->worker_counter)))) {
+ /* update cdata->last_exec_counter so next statement can be run */
+ cdata->last_exec_counter = cdata->worker_counter;
+
+ gda_connection_add_event_string (cnc, _("The transaction has been automatically rolled back"));
+ g_object_unref (msg);
+
+ gda_connection_internal_reset_transaction_status (cnc);
+ gda_mutex_unlock (cdata->mutex);
+ return NULL;
+ }
+ if (! cdata->worker_running) {
+ gda_mutex_unlock (cdata->mutex);
+ start_worker (cnc, cdata);
+
+ gda_mutex_lock (cdata->mutex);
+ if (! cdata->worker_running) {
+ gda_connection_add_event_string (cnc, _("Could not run PHP script on the server"));
+ g_object_unref (msg);
+ gda_mutex_unlock (cdata->mutex);
+ return NULL;
+ }
+ }
+ gda_mutex_unlock (cdata->mutex);
+
+ /* finalize and send message */
+ if (hash_key) {
+ uint8_t hmac[16];
+ GString *md5str;
+ gint i;
+
+ hmac_md5 ((uint8_t *) message, strlen (message),
+ (uint8_t *) hash_key, strlen (hash_key), hmac);
+ md5str = g_string_new ("");
+ for (i = 0; i < 16; i++)
+ g_string_append_printf (md5str, "%02x", hmac[i]);
+ g_string_append_c (md5str, '\n');
+ g_string_append (md5str, message);
+ h_message = g_string_free (md5str, FALSE);
+ }
+ else
+ h_message = g_strdup_printf ("NOHASH\n%s", message);
+
+#ifdef DEBUG_WEB_PROV
+ g_print ("=== START of request ===\n%s\n=== END of request ===\n", h_message);
+#endif
+ soup_message_set_request (msg, "text/plain",
+ SOUP_MEMORY_COPY, h_message, strlen (h_message));
+ g_free (h_message);
+ g_object_set (G_OBJECT (cdata->front_session), SOUP_SESSION_TIMEOUT, 20, NULL);
+ status = soup_session_send_message (cdata->front_session, msg);
+
+ gda_mutex_lock (cdata->mutex);
+ cdata->worker_needed = FALSE;
+ gda_mutex_unlock (cdata->mutex);
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ gda_connection_add_event_string (cnc, msg->reason_phrase);
+ g_object_unref (msg);
+ return NULL;
+ }
+
+ xmlDocPtr doc;
+ guint counter_id;
+ doc = _gda_web_decode_response (cnc, cdata, msg->response_body, out_status_chr, &counter_id);
+ g_object_unref (msg);
+
+ gda_mutex_lock (cdata->mutex);
+ if (msgtype == MESSAGE_EXEC)
+ cdata->last_exec_counter = counter_id;
+ gda_mutex_unlock (cdata->mutex);
+
+ return doc;
+}
+
+
+/*
+ * If there is a mismatch or an error, then gda_connection_add_event_string() is used
+ *
+ * - if all OK, extracs the <challenge> value and replace cdata->next_challenge with it (or simply
+ * reset cdata->next_challenge to NULL)
+ *
+ * Returns: a new #xmlDocPtr, or %NULL on error
+ */
+xmlDocPtr
+_gda_web_decode_response (GdaConnection *cnc, WebConnectionData *cdata, SoupMessageBody *body,
+ gchar *out_status_chr, guint *out_counter_id)
+{
+ SoupBuffer *sbuffer;
+ xmlDocPtr doc;
+ sbuffer = soup_message_body_flatten (body);
+#ifdef DEBUG_WEB_PROV
+ g_print ("=== START of response ===\n%s\n=== END of response ===\n", (gchar*) sbuffer->data);
+#endif
+ doc = decode_buffer_response (cnc, cdata, sbuffer, out_status_chr, out_counter_id);
+ soup_buffer_free (sbuffer);
+ return doc;
+}
+
+/*
+ * Creates a new string
+ */
+gchar *
+_gda_web_compute_token (WebConnectionData *cdata)
+{
+ uint8_t hmac[16];
+ GString *md5str;
+ gint i;
+
+ g_return_val_if_fail (cdata->next_challenge && cdata->key, NULL);
+
+ hmac_md5 ((uint8_t *) cdata->next_challenge, strlen (cdata->next_challenge),
+ (uint8_t *) cdata->key, strlen (cdata->key), hmac);
+ md5str = g_string_new ("");
+ for (i = 0; i < 16; i++)
+ g_string_append_printf (md5str, "%02x", hmac[i]);
+
+ return g_string_free (md5str, FALSE);
+}
+
+/*
+ * Cleans any remaining data on the web server
+ */
+void
+_gda_web_do_server_cleanup (GdaConnection *cnc, WebConnectionData *cdata)
+{
+ SoupMessage *msg;
+ guint status;
+ gchar *real_url;
+ gint nb_retries;
+
+ /* wait for worker to finish */
+ gda_mutex_lock (cdata->mutex);
+ for (nb_retries = 0; (nb_retries < 10) && cdata->worker_running; nb_retries ++) {
+ gda_mutex_unlock (cdata->mutex);
+ g_usleep (50000);
+ gda_mutex_lock (cdata->mutex);
+ }
+ gda_mutex_unlock (cdata->mutex);
+
+ real_url = g_strdup_printf ("%s/gda-clean.php?%s", cdata->server_base_url, cdata->session_id);
+ msg = soup_message_new ("GET", real_url);
+ if (!msg) {
+ gda_connection_add_event_string (cnc, _("Invalid HOST/SCRIPT '%s'"), real_url);
+ g_free (real_url);
+ return;
+ }
+ g_free (real_url);
+
+ g_object_set (G_OBJECT (cdata->front_session), SOUP_SESSION_TIMEOUT, 5, NULL);
+ status = soup_session_send_message (cdata->front_session, msg);
+ g_object_unref (msg);
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status))
+ g_warning (_("Error cleaning data on the server for session %s"), cdata->session_id);
+#ifdef DEBUG_WEB_PROV
+ else
+ g_print ("CLEANUP DONE!\n");
+#endif
+}
+
+/**
+ * _gda_web_set_connection_error_from_xmldoc
+ *
+ * Handles errors reported by @doc, and ser @error if not %NULL
+ *
+ * Returns: a #GdaConnectionEvent, which must not be modified or freed
+ */
+GdaConnectionEvent *
+_gda_web_set_connection_error_from_xmldoc (GdaConnection *cnc, xmlDocPtr doc, GError **error)
+{
+ xmlNodePtr node, root;
+ GdaConnectionEvent *ev = NULL;
+
+ g_return_val_if_fail (doc, NULL);
+
+ root = xmlDocGetRootElement (doc);
+ for (node = root->children; node; node = node->next) {
+ if (!strcmp ((gchar*) node->name, "status")) {
+ xmlChar *prop;
+ prop = xmlGetProp (node, BAD_CAST "error");
+ if (prop) {
+ ev = gda_connection_add_event_string (cnc, (gchar*) prop);
+ xmlFree (prop);
+ }
+ else
+ ev = gda_connection_add_event_string (cnc, _("Non detailled error"));
+ break;
+ }
+ }
+
+ if (ev && error) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+ GDA_SERVER_PROVIDER_STATEMENT_EXEC_ERROR, "%s",
+ gda_connection_event_get_description (ev));
+ }
+
+ return ev;
+}
+
+/*
+ * Actually closes the connection from Libgda's point of view
+ */
+void
+_gda_web_change_connection_to_closed (GdaConnection *cnc, WebConnectionData *cdata)
+{
+ cdata->forced_closing = TRUE;
+ gda_connection_close_no_warning (cnc);
+}
+
+/*
+ * Free connection's specific data
+ */
+void
+_gda_web_free_cnc_data (WebConnectionData *cdata)
+{
+ if (!cdata)
+ return;
+
+ if (cdata->reuseable) {
+ g_assert (cdata->reuseable->operations);
+ if (cdata->reuseable->operations->re_reset_data)
+ cdata->reuseable->operations->re_reset_data (cdata->reuseable);
+ g_free (cdata->reuseable);
+ }
+ g_free (cdata->server_id);
+ g_free (cdata->server_base_url);
+ g_free (cdata->front_url);
+ g_free (cdata->worker_url);
+ if (cdata->mutex)
+ gda_mutex_free (cdata->mutex);
+ if (cdata->worker_session)
+ g_object_unref (cdata->worker_session);
+ if (cdata->front_session)
+ g_object_unref (cdata->front_session);
+ g_free (cdata->session_id);
+ g_free (cdata->server_secret);
+ g_free (cdata->key);
+ g_free (cdata->next_challenge);
+
+ g_free (cdata);
+}
diff --git a/providers/web/gda-web-util.h b/providers/web/gda-web-util.h
new file mode 100644
index 0000000..06df2d1
--- /dev/null
+++ b/providers/web/gda-web-util.h
@@ -0,0 +1,59 @@
+/* GDA provider
+ * 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; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GDA_WEB_UTIL_H__
+#define __GDA_WEB_UTIL_H__
+
+#include "gda-web.h"
+#include <libgda/libgda.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ MESSAGE_HELLO,
+ MESSAGE_CONNECT,
+ MESSAGE_BYE,
+ MESSAGE_PREPARE,
+ MESSAGE_UNPREPARE,
+ MESSAGE_EXEC,
+ MESSAGE_META
+} WebMessageType;
+
+gchar *_gda_web_compute_token (WebConnectionData *cdata);
+
+xmlDocPtr _gda_web_send_message_to_frontend (GdaConnection *cnc, WebConnectionData *cdata,
+ WebMessageType msgtype, const gchar *message,
+ const gchar *hash_key, gchar *out_status_chr);
+
+xmlDocPtr _gda_web_decode_response (GdaConnection *cnc, WebConnectionData *cdata, SoupMessageBody *body,
+ gchar *out_status_chr, guint *out_counter_id);
+
+GdaConnectionEvent *_gda_web_set_connection_error_from_xmldoc (GdaConnection *cnc, xmlDocPtr doc, GError **error);
+
+void _gda_web_do_server_cleanup (GdaConnection *cnc, WebConnectionData *cdata);
+void _gda_web_change_connection_to_closed (GdaConnection *cnc, WebConnectionData *cdata);
+void _gda_web_free_cnc_data (WebConnectionData *cdata);
+
+
+G_END_DECLS
+
+#endif
+
diff --git a/providers/web/gda-web.h b/providers/web/gda-web.h
new file mode 100644
index 0000000..5aa3140
--- /dev/null
+++ b/providers/web/gda-web.h
@@ -0,0 +1,67 @@
+/* GDA web provider
+ * 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_WEB_H__
+#define __GDA_WEB_H__
+
+/*
+ * Provider name
+ */
+#define WEB_PROVIDER_NAME "Web"
+
+#include <libsoup/soup.h>
+#include <libgda/gda-mutex.h>
+#include <libgda/gda-connection.h>
+#include "../reuseable/gda-provider-reuseable.h"
+
+/*
+ * Provider's specific connection data
+ */
+typedef struct {
+ GdaProviderReuseable *reuseable; /* pointer to GdaProviderReuseable, not inherited! */
+ GdaMutex *mutex; /* protected access */
+
+ gchar *server_id; /* PostgreSQL, MySQL, ... */
+
+ gboolean forced_closing;
+ gchar *server_base_url;
+ gchar *front_url;
+ gchar *worker_url;
+
+ gchar *server_secret;
+ gchar *key;
+ gchar *next_challenge;
+
+ gchar *session_id; /* as defined by the server */
+
+ /* worker attributes */
+ SoupSession *worker_session;
+ gboolean worker_needed;
+ gboolean worker_running;
+ guint worker_counter; /* incremented each time the worker is run */
+
+ /* front and others attributes */
+ SoupSession *front_session;
+ guint last_exec_counter; /* the worker counter which replied to the last EXEC command */
+} WebConnectionData;
+
+#endif
diff --git a/providers/web/libgda-web-4.0.pc.in b/providers/web/libgda-web-4.0.pc.in
new file mode 100644
index 0000000..ca0a707
--- /dev/null
+++ b/providers/web/libgda-web-4.0.pc.in
@@ -0,0 +1,9 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+libdir= libdir@
+includedir= includedir@
+
+Name: libgda-web- GDA_ABI_MAJOR_VERSION@ GDA_ABI_MINOR_VERSION@
+Description: GDA (GNOME Data Access) Web provider
+Requires: libgda- GDA_ABI_MAJOR_VERSION@ GDA_ABI_MINOR_VERSION@
+Version: @VERSION@
diff --git a/providers/web/libmain.c b/providers/web/libmain.c
new file mode 100644
index 0000000..f17bfde
--- /dev/null
+++ b/providers/web/libmain.c
@@ -0,0 +1,104 @@
+/* GDA Web Provider
+ * Copyright (C) 2008 The GNOME Foundation
+ *
+ * AUTHORS:
+ * TO_ADD: your name and email
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * 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; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+#include <libgda/gda-server-provider-extra.h>
+#include <libgda/binreloc/gda-binreloc.h>
+#include "gda-web.h"
+#include "gda-web-provider.h"
+
+static gchar *module_path = NULL;
+const gchar *plugin_get_name (void);
+const gchar *plugin_get_description (void);
+gchar *plugin_get_dsn_spec (void);
+GdaServerProvider *plugin_create_provider (void);
+
+/*
+ * Functions executed when calling g_module_open() and g_module_close()
+ */
+const gchar *
+g_module_check_init (GModule *module)
+{
+ /*g_module_make_resident (module);*/
+ return NULL;
+}
+
+void
+g_module_unload (GModule *module)
+{
+ g_free (module_path);
+ module_path = NULL;
+}
+
+/*
+ * Normal plugin functions
+ */
+void
+plugin_init (const gchar *real_path)
+{
+ if (real_path)
+ module_path = g_strdup (real_path);
+}
+
+const gchar *
+plugin_get_name (void)
+{
+ return WEB_PROVIDER_NAME;
+}
+
+const gchar *
+plugin_get_description (void)
+{
+ return _("Provider for web server proxies");
+}
+
+gchar *
+plugin_get_dsn_spec (void)
+{
+ gchar *ret, *dir;
+
+ dir = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, NULL);
+ ret = gda_server_provider_load_file_contents (module_path, dir, "web_specs_dsn.xml");
+ g_free (dir);
+ return ret;
+}
+
+gchar *
+plugin_get_auth_spec (void)
+{
+ gchar *ret, *dir;
+
+ dir = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, NULL);
+ ret = gda_server_provider_load_file_contents (module_path, dir, "web_specs_auth.xml");
+ g_free (dir);
+ return ret;
+}
+
+GdaServerProvider *
+plugin_create_provider (void)
+{
+ GdaServerProvider *prov;
+
+ prov = (GdaServerProvider*) g_object_new (GDA_TYPE_WEB_PROVIDER, NULL);
+ g_object_set_data ((GObject *) prov, "GDA_PROVIDER_DIR", module_path);
+ return prov;
+}
diff --git a/providers/web/php/README b/providers/web/php/README
new file mode 100644
index 0000000..8ff3940
--- /dev/null
+++ b/providers/web/php/README
@@ -0,0 +1,35 @@
+Installation instructions
+=========================
+
+The PHP scripts in this directory are used by the GDA Web provider and
+have to be installed on a web server. The location of the scripts will have
+to be passed as the PATH argument on the Libgda side when
+opening the connection. For example if the scripts are accessible
+as: http://example.com/path/to/gda-worker.php then the PATH argument
+will have to be "path/to".
+
+These scripts need the PHP engine's SimpleXML extension (which is
+usually included when compiling PHP) and the PEAR's MDB2 module
+(it's the module which actually opens the connection to the database)
+for which the current implementation is provided under the PEAR_MDB2/
+directory.
+
+Once the scripts are installed, some configuration needs to be done
+in the gda-config.php script.
+
+Note that for better security, this script should not be accessible
+from the outside (ie it should be put outside ot the web server's
+root directory, and the gda-front.php and gda-worker.php scripts should be
+modified to point to its real location).
+
+
+Note about gda-tester.php
+-------------------------
+The gda-tester.php script can be executed to diagnose any setup problem,
+it reports errors if the MDB2 PEAR of SimpleXML extensions are missing,
+and, for each configured connection, tests if it can be opened.
+
+Note that by default the gda-tester.php will _not_ display any
+configured connection information, this needs to be enabled in the
+script's source. Better yet is to remove that file once not necessary
+anymore as it could leak usefull information.
diff --git a/providers/web/php/gda-clean.php b/providers/web/php/gda-clean.php
new file mode 100644
index 0000000..ea8b1d2
--- /dev/null
+++ b/providers/web/php/gda-clean.php
@@ -0,0 +1,23 @@
+<?php
+/* Configure session cache */
+session_cache_limiter('nocache');
+session_start();
+
+include_once "gda-utils.php";
+include_once "gda-config.php";
+
+header('Content-type: text/plain; charset=UTF-8');
+
+$cmdfile = get_command_filename (session_id ());
+$replyfile = get_reply_filename (session_id ());
+
+ unlink ($cmdfile);
+ unlink ($replyfile);
+
+/* all cleaned */
+$reply = new SimpleXMLElement("<reply></reply>");
+$reply->addChild ("status", "OK");
+echo gda_add_hash ($init_shared, $reply->asXml());
+session_destroy ();
+
+?>
diff --git a/providers/web/php/gda-config.php b/providers/web/php/gda-config.php
new file mode 100644
index 0000000..8acd867
--- /dev/null
+++ b/providers/web/php/gda-config.php
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * initial shared secret: will have to be passed as the SECRET argument when opening
+ * the connection from Libgda
+ */
+$init_shared = "MySecret";
+
+/*
+ * declared connections: for each connection which can be opened by Libgda, the
+ * the connection's password and the real connection's DSN need to be added respectively
+ * to the $cnc and $dsn arrays, using the connection name as a key. The connection name
+ * and password have no significance outside of the Libgda's context and be arbitrary.
+ * However the real connection's DSN need to be valid for the PEAR's MDB2 module, as
+ * per http://pear.php.net/manual/en/package.database.mdb2.intro-dsn.php
+ *
+ */
+
+/* sample connection cnc1 */
+$cnc["cnc1"] = "MyPass1";
+//$dsn["cnc1"] = "pgsql://vivien unix(/var/run/postgresql)/sales";
+$dsn["cnc1"] = "pgsql://unix(/tmp)/sales";
+
+/* sample connection cnc2 */
+$cnc["cnc2"] = "MyPass2";
+$dsn["cnc2"] = "mysql://user unix(/path/to/socket)/sales";
+
+?>
diff --git a/providers/web/php/gda-exception.php b/providers/web/php/gda-exception.php
new file mode 100644
index 0000000..d59372b
--- /dev/null
+++ b/providers/web/php/gda-exception.php
@@ -0,0 +1,9 @@
+<?php
+class GdaException extends Exception {
+ var $cnc_closed;
+ // Constructor
+ public function __construct($msg, $cnc_closed) {
+ parent :: __construct($msg);
+ $this->cnc_closed = $cnc_closed;
+ }
+}
diff --git a/providers/web/php/gda-front.php b/providers/web/php/gda-front.php
new file mode 100644
index 0000000..1de3f76
--- /dev/null
+++ b/providers/web/php/gda-front.php
@@ -0,0 +1,68 @@
+<?php
+include_once "gda-utils.php";
+include_once "gda-config.php";
+
+/*
+ * REM: no session is started in this script because sessions would serialize the
+ * execution of the gda-front.php and gda-worker.php scripts. Only the session ID, transmitted
+ * in the URL is used here.
+ */
+
+header ('Content-type: text/plain; charset=UTF-8');
+
+$datafile = null;
+
+try {
+ global $datafile;
+ $text = $GLOBALS["HTTP_RAW_POST_DATA"];
+ if ($text == "")
+ throw new Exception ("Empty command");
+
+ /* Send command */
+ $cmdfile = get_command_filename ($_GET['PHPSESSID']);
+ if (! file_exists ($cmdfile))
+ throw new Exception ("Bad setup $cmdfile");
+
+ $datafile = tempnam (session_save_path (), "GDACommand");
+ //echo "DATAFILE [$datafile]\n"; flush (); ob_flush();
+ if (file_put_contents ($datafile, $text) == false)
+ throw new Exception ("Can't create command file ".$cmdfile);
+
+ $file = fopen ($cmdfile, "w");
+ if (fwrite ($file, $datafile) == false) // block until there is a reader
+ throw new Exception ("Can't send command");
+ fclose ($file);
+
+ /* wait for reply, and destroy the reading file */
+ $text = "";
+ $replyfile = get_reply_filename ($_GET['PHPSESSID']);
+ if (! file_exists ($replyfile))
+ throw new Exception ("Bad setup");
+ $file = fopen ($replyfile, "r");
+ $datafile = fread ($file, 8192);
+ fclose ($file);
+ //echo "DATAFILE [$datafile]\n"; flush (); ob_flush();
+
+ if (filesize ($datafile) == 0)
+ throw new Exception ("No reply");
+
+ $file = fopen ($datafile, 'rb');
+ fpassthru ($file);
+ @unlink ($datafile);
+
+ echo $text;
+}
+catch (Exception $e) {
+ $reply = new SimpleXMLElement("<reply></reply>");
+ $node = $reply->addChild ('status', "ERROR");
+ $node->addAttribute ("error", $e->getMessage());
+
+ global $init_shared;
+ echo gda_add_hash ($init_shared, $reply->asXml());
+
+ global $datafile;
+ if (isset ($datafile))
+ @unlink ($datafile);
+}
+
+?>
diff --git a/providers/web/php/gda-meta.php b/providers/web/php/gda-meta.php
new file mode 100644
index 0000000..ce227d1
--- /dev/null
+++ b/providers/web/php/gda-meta.php
@@ -0,0 +1,648 @@
+<?php
+
+/*
+ * _gda_web_meta__info
+ */
+function do_meta_info ($reply, &$mdb2)
+{
+ $catalog = get_catalog ($reply, &$mdb2);
+
+ $node = $reply->addChild ("gda_array", null);
+ declare_column ($node, 0, "name", "string", false);
+ $data = $node->addChild ("gda_array_data", null);
+ $xmlrow = $data->addChild ("gda_array_row", null);
+
+ add_value_child ($xmlrow, $catalog);
+}
+
+/*
+ * _gda_web_meta__btypes
+ */
+function do_meta_btypes ($reply, &$mdb2)
+{
+ $res = $mdb2->datatype->getValidTypes();
+ handle_pear_error ($res, $reply);
+
+ $node = $reply->addChild ("gda_array", null);
+
+ $col = 0;
+ declare_column ($node, $col++, "short_type_name", "string", false);
+ declare_column ($node, $col++, "full_type_name", "string", false);
+ declare_column ($node, $col++, "gtype", "string", false);
+ declare_column ($node, $col++, "comments", "string", true);
+ declare_column ($node, $col++, "synonyms", "string", true);
+ declare_column ($node, $col++, "internal", "boolean", false);
+
+ $data = $node->addChild ("gda_array_data", null);
+ foreach ($res as $i => $value) {
+ $xmlrow = $data->addChild ("gda_array_row", null);
+ add_value_child ($xmlrow, $i);
+ add_value_child ($xmlrow, $i);
+ $field = array ("type" => $i);
+ $mtype = $mdb2->datatype->mapNativeDatatype ($field);
+ if (PEAR::isError($mtype))
+ add_value_child ($xmlrow, "gchararray"); // FIXME, maybe use mapNativeDatatype()
+ else {
+ $done = false;
+ foreach ($mtype[0] as $j => $type) {
+ if ($type == "text") {
+ add_value_child ($xmlrow, $type);
+ $done = true;
+ break;
+ }
+ }
+ if (!$done)
+ add_value_child ($xmlrow, $mtype[0][0]);
+ }
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, false, true);
+ }
+}
+
+/*
+ * _gda_web_meta__schemata
+ */
+function do_meta_schemas ($reply, &$mdb2, $schema_name = null)
+{
+ if (determine_db_type ($mdb2) == "PostgreSQL") {
+ do_meta_schemas_pgsql ($reply, &$mdb2);
+ return;
+ }
+ $catalog = get_catalog ($reply, &$mdb2);
+
+ $res = $mdb2->manager->listDatabases();
+ handle_pear_error ($res, $reply);
+
+ $node = $reply->addChild ("gda_array", null);
+ declare_column ($node, 0, "catalog_name", "string", false);
+ declare_column ($node, 1, "schema_name", "string", false);
+ declare_column ($node, 2, "schema_owner", "string", false);
+ declare_column ($node, 3, "schema_internal", "boolean", false);
+ $data = $node->addChild ("gda_array_data", null);
+
+ foreach ($res as $i => $value) {
+ if ($schema_name && ($schema_name != $value))
+ continue;
+ $xmlrow = $data->addChild ("gda_array_row", null);
+
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $value);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, false, true);
+ }
+}
+
+function do_meta_schemas_pgsql ($reply, &$mdb2)
+{
+ $sql = "SELECT current_database()::information_schema.sql_identifier AS catalog_name, n.nspname::information_schema.sql_identifier AS schema_name, u.rolname::information_schema.sql_identifier AS schema_owner, CASE WHEN n.nspname::information_schema.sql_identifier ~ '^pg_' THEN TRUE WHEN n.nspname::information_schema.sql_identifier = 'information_schema' THEN TRUE ELSE FALSE END FROM pg_namespace n, pg_roles u WHERE n.nspowner = u.oid";
+ $res = &$mdb2->query ($sql);
+ handle_pear_error ($res, $reply);
+
+ $node = $reply->addChild ("gda_array", null);
+ declare_column ($node, 0, "catalog_name", "string", false);
+ declare_column ($node, 1, "schema_name", "string", false);
+ declare_column ($node, 2, "schema_owner", "string", false);
+ declare_column ($node, 3, "schema_internal", "boolean", false);
+ $data = $node->addChild ("gda_array_data", null);
+ while (($row = $res->fetchRow())) {
+ $xmlrow = $data->addChild ("gda_array_row", null);
+
+ add_value_child ($xmlrow, $row[0]);
+ add_value_child ($xmlrow, $row[1]);
+ add_value_child ($xmlrow, $row[2]);
+ add_value_child ($xmlrow, $row[3] == 't' ? "TRUE" : "FALSE");
+ }
+}
+
+/*
+ * _gda_web_meta__schemata
+ */
+function do_meta_tables ($reply, &$mdb2, $table_schema = null, $table_name = null)
+{
+ $catalog = get_catalog ($reply, &$mdb2);
+ $res = $mdb2->manager->listTables();
+ handle_pear_error ($res, $reply);
+
+ $node = $reply->addChild ("gda_array", null);
+ $col = 0;
+ declare_column ($node, $col++, "table_catalog", "string", false);
+ declare_column ($node, $col++, "table_schema", "string", false);
+ declare_column ($node, $col++, "table_name", "string", false);
+ declare_column ($node, $col++, "table_type", "string", false);
+ declare_column ($node, $col++, "is_insertable_into", "boolean", true);
+ declare_column ($node, $col++, "table_comments", "string", true);
+ declare_column ($node, $col++, "table_short_name", "string", false);
+ declare_column ($node, $col++, "table_full_name", "string", false);
+ declare_column ($node, $col++, "table_owner", "string", true);
+ $data = $node->addChild ("gda_array_data", null);
+
+ foreach ($res as $i => $value) {
+ if ($table_name && ($table_name != $value))
+ continue;
+ $xmlrow = $data->addChild ("gda_array_row", null);
+
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $value);
+ add_value_child ($xmlrow, "BASE TABLE");
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, $value);
+ add_value_child ($xmlrow, $catalog.".".$value);
+ add_value_child ($xmlrow, null);
+ }
+}
+
+function do_meta_views ($reply, &$mdb2, $table_schema = null, $table_name = null)
+{
+ $catalog = get_catalog ($reply, &$mdb2);
+
+ $res = $mdb2->manager->listViews();
+ handle_pear_error ($res, $reply);
+
+ $node = $reply->addChild ("gda_array", null);
+ $col = 0;
+ declare_column ($node, $col++, "table_catalog", "string", false);
+ declare_column ($node, $col++, "table_schema", "string", false);
+ declare_column ($node, $col++, "table_name", "string", false);
+ declare_column ($node, $col++, "view_definition", "string", true);
+ declare_column ($node, $col++, "check_option", "string", true);
+ declare_column ($node, $col++, "is_updatable", "boolean", true);
+ $data = $node->addChild ("gda_array_data", null);
+
+ foreach ($res as $i => $value) {
+ if ($table_name && ($table_name != $value))
+ continue;
+
+ $xmlrow = $data->addChild ("gda_array_row", null);
+
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $value);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ }
+}
+
+function do_meta_columns_named ($reply, &$mdb2, $data, $table_name)
+{
+ $catalog = get_catalog ($reply, &$mdb2);
+ $res = $mdb2->reverse->tableInfo($table_name);
+ if (PEAR::isError($res))
+ return;
+
+ foreach ($res as $i => $value) {
+ $xmlrow = $data->addChild ("gda_array_row", null);
+
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $table_name);
+ add_value_child ($xmlrow, $value['name']);
+ add_value_child ($xmlrow, $i+1);
+
+ if ($value['default'] != "")
+ add_value_child ($xmlrow, $value['default']);
+ else
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, true, true);
+
+ add_value_child ($xmlrow, $value['nativetype']);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, mdb2_type_to_gtype ($value['mdb2type']));
+ add_value_child ($xmlrow, $value['len']);
+
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+
+ if ($value['autoincrement'])
+ add_value_child ($xmlrow, "AUTO_INCREMENT");
+ else
+ add_value_child ($xmlrow, null);
+
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ }
+}
+
+function do_meta_columns ($reply, &$mdb2, $table_schema=null, $table_name=null)
+{
+ if (! $table_name) {
+ $res = $mdb2->manager->listTables();
+ handle_pear_error ($res, $reply);
+ }
+
+ $node = $reply->addChild ("gda_array", null);
+ $col = 0;
+ declare_column ($node, $col++, "table_catalog", "string", false);
+ declare_column ($node, $col++, "table_schema", "string", false);
+ declare_column ($node, $col++, "table_name", "string", false);
+ declare_column ($node, $col++, "column_name", "string", false);
+ declare_column ($node, $col++, "ordinal_position", "gint", false);
+ declare_column ($node, $col++, "column_default", "string", true);
+ declare_column ($node, $col++, "is_nullable", "boolean", false);
+ declare_column ($node, $col++, "data_type", "string", true);
+ declare_column ($node, $col++, "array_spec", "string", true);
+ declare_column ($node, $col++, "gtype", "string", false);
+ declare_column ($node, $col++, "character_maximum_length", "gint", true);
+ declare_column ($node, $col++, "character_octet_length", "gint", true);
+ declare_column ($node, $col++, "numeric_precision", "gint", true);
+ declare_column ($node, $col++, "numeric_scale", "gint", true);
+ declare_column ($node, $col++, "datetime_precision", "gint", true);
+ declare_column ($node, $col++, "character_set_catalog", "string", true);
+ declare_column ($node, $col++, "character_set_schema", "string", true);
+ declare_column ($node, $col++, "character_set_name", "string", true);
+ declare_column ($node, $col++, "collation_catalog", "string", true);
+ declare_column ($node, $col++, "collation_schema", "string", true);
+ declare_column ($node, $col++, "collation_name", "string", true);
+ declare_column ($node, $col++, "extra", "string", true);
+ declare_column ($node, $col++, "is_updatable", "boolean", true);
+ declare_column ($node, $col++, "column_comments", "string", true);
+
+ $data = $node->addChild ("gda_array_data", null);
+
+ if ($table_name)
+ do_meta_columns_named ($reply, $mdb2, $data, $table_name);
+ else {
+ foreach ($res as $i => $value)
+ do_meta_columns_named ($reply, $mdb2, $data, $value);
+ }
+}
+
+function do_meta_constraints_tab ($reply, &$mdb2, $table_schema=null, $table_name=null, $constraint_name=null)
+{
+ $catalog = get_catalog ($reply, &$mdb2);
+ $res = $mdb2->manager->listTables();
+ handle_pear_error ($res, $reply);
+
+ $node = $reply->addChild ("gda_array", null);
+ $col = 0;
+ declare_column ($node, $col++, "constraint_catalog", "string", true);
+ declare_column ($node, $col++, "constraint_schema", "string", true);
+ declare_column ($node, $col++, "constraint_name", "string", false);
+ declare_column ($node, $col++, "table_catalog", "string", false);
+ declare_column ($node, $col++, "table_schema", "string", false);
+ declare_column ($node, $col++, "table_name", "string", false);
+ declare_column ($node, $col++, "constraint_type", "string", false);
+ declare_column ($node, $col++, "check_clause", "string", true);
+ declare_column ($node, $col++, "is_deferrable", "boolean", true);
+ declare_column ($node, $col++, "initially_deferred", "boolean", true);
+
+ $data = $node->addChild ("gda_array_data", null);
+ foreach ($res as $i => $value) {
+ if ($table_name && ($table_name != $value))
+ continue;
+
+ $constraints = $mdb2->manager->listTableConstraints ($value);
+ if (PEAR::isError($constraints))
+ continue;
+
+ foreach ($constraints as $cindex => $cname) {
+ if ($constraint_name && ($constraint_name != $cname))
+ continue;
+
+ $cdef = $mdb2->reverse->getTableConstraintDefinition ($value, $cname);
+ if (PEAR::isError($cdef))
+ continue;
+
+ $xmlrow = $data->addChild ("gda_array_row", null);
+
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, $cname);
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $value);
+ if ($cdef ["primary"]) {
+ add_value_child ($xmlrow, "PRIMARY KEY");
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ }
+ else if ($cdef ["unique"]) {
+ add_value_child ($xmlrow, "UNIQUE");
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ }
+ else if ($cdef ["foreign"]) {
+ add_value_child ($xmlrow, "FOREIGN KEY");
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, $cdef ["deferrable"] ? true : false, true);
+ add_value_child ($xmlrow, $cdef ["initiallydeferred"] ? true : false, true);
+ }
+ else if ($cdef ["check"]) {
+ add_value_child ($xmlrow, "CHECK");
+ add_value_child ($xmlrow, null); /* FIXME: how to obtain that ? */
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ }
+ else {
+ add_value_child ($xmlrow, "UNKNOWN");
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, null);
+ }
+ }
+ }
+}
+
+
+function do_meta_constraints_ref ($reply, &$mdb2, $table_schema=null, $table_name=null, $constraint_name=null)
+{
+ $catalog = get_catalog ($reply, &$mdb2);
+ $res = $mdb2->manager->listTables();
+ handle_pear_error ($res, $reply);
+
+ $node = $reply->addChild ("gda_array", null);
+ $col = 0;
+ declare_column ($node, $col++, "table_catalog", "string", false);
+ declare_column ($node, $col++, "table_schema", "string", false);
+ declare_column ($node, $col++, "table_name", "string", false);
+ declare_column ($node, $col++, "constraint_name", "string", false);
+ declare_column ($node, $col++, "ref_table_catalog", "string", false);
+ declare_column ($node, $col++, "ref_table_schema", "string", false);
+ declare_column ($node, $col++, "ref_table_name", "string", false);
+ declare_column ($node, $col++, "ref_constraint_name", "string", false);
+ declare_column ($node, $col++, "match_option", "string", true);
+ declare_column ($node, $col++, "update_rule", "string", true);
+ declare_column ($node, $col++, "delete_rule", "string", true);
+
+
+ $data = $node->addChild ("gda_array_data", null);
+ foreach ($res as $i => $value) {
+ if ($table_name && ($table_name != $value))
+ continue;
+
+ $constraints = $mdb2->manager->listTableConstraints ($value);
+ if (PEAR::isError($constraints))
+ continue;
+
+ foreach ($constraints as $cindex => $cname) {
+ if ($constraint_name && ($constraint_name != $cname))
+ continue;
+
+ $cdef = $mdb2->reverse->getTableConstraintDefinition ($value, $cname);
+ if (PEAR::isError($cdef))
+ continue;
+
+ if (! $cdef ["foreign"])
+ continue;
+
+ $xmlrow = $data->addChild ("gda_array_row", null);
+
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $value);
+ add_value_child ($xmlrow, $cname);
+
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $cdef["references"]["table"]);
+ add_value_child ($xmlrow, _get_table_pk_constraint ($mdb2, $cdef["references"]));
+
+ if (isset ($cdef["match"]) && ($cdef["match"] != "UNSPECIFIED"))
+ add_value_child ($xmlrow, $cdef["match"]);
+ else
+ add_value_child ($xmlrow, null);
+ add_value_child ($xmlrow, $cdef["onupdate"]);
+ add_value_child ($xmlrow, $cdef["ondelete"]);
+ }
+ }
+}
+
+function _get_table_pk_constraint (&$mdb2, $references)
+{
+ $table_name = $references["table"];
+ $constraints = $mdb2->manager->listTableConstraints ($table_name);
+ if (PEAR::isError($constraints))
+ return "";
+ foreach ($constraints as $cindex => $cname) {
+ $cdef = $mdb2->reverse->getTableConstraintDefinition ($table_name, $cname);
+ if (PEAR::isError($constraints))
+ continue;
+ if (! $cdef ["primary"])
+ continue;
+
+ $rfields = $references["fields"];
+ $allmatch = true;
+ foreach ($cdef ["fields"] as $fname => $attrs) {
+ if (!isset ($rfields [$fname]) ||
+ ($rfields [$fname]["position"] != $attrs["position"])) {
+ $allmatch = false;
+ break;
+ }
+ }
+
+ if ($allmatch)
+ return $cname;
+ }
+ return "";
+}
+
+function do_meta_key_columns ($reply, &$mdb2, $table_schema=null, $table_name=null, $constraint_name=null)
+{
+ $catalog = get_catalog ($reply, &$mdb2);
+ $res = $mdb2->manager->listTables();
+ handle_pear_error ($res, $reply);
+
+ $node = $reply->addChild ("gda_array", null);
+ $col = 0;
+ declare_column ($node, $col++, "table_catalog", "string", false);
+ declare_column ($node, $col++, "table_schema", "string", false);
+ declare_column ($node, $col++, "table_name", "string", false);
+ declare_column ($node, $col++, "constraint_name", "string", false);
+ declare_column ($node, $col++, "column_name", "string", false);
+ declare_column ($node, $col++, "ordinal_position", "gint", false);
+
+ $data = $node->addChild ("gda_array_data", null);
+ foreach ($res as $i => $value) {
+ if ($table_name && ($table_name != $value))
+ continue;
+
+ $constraints = $mdb2->manager->listTableConstraints ($value);
+ if (PEAR::isError($constraints))
+ continue;
+
+ foreach ($constraints as $cindex => $cname) {
+ if ($constraint_name && ($constraint_name != $cname))
+ continue;
+
+ $cdef = $mdb2->reverse->getTableConstraintDefinition ($value, $cname);
+ if (PEAR::isError($cdef))
+ continue;
+
+ if (! $cdef ["primary"])
+ continue;
+
+ $fields = $cdef["fields"];
+ if (!$fields)
+ continue;
+
+ foreach ($fields as $fname => $attrs) {
+ $xmlrow = $data->addChild ("gda_array_row", null);
+
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $value);
+ add_value_child ($xmlrow, $cname);
+ add_value_child ($xmlrow, $fname);
+ add_value_child ($xmlrow, $attrs["position"]);
+ }
+ }
+ }
+}
+
+function do_meta_check_columns ($reply, &$mdb2, $table_schema=null, $table_name=null, $constraint_name=null)
+{
+ $catalog = get_catalog ($reply, &$mdb2);
+ $res = $mdb2->manager->listTables();
+ handle_pear_error ($res, $reply);
+
+ $node = $reply->addChild ("gda_array", null);
+ $col = 0;
+ declare_column ($node, $col++, "table_catalog", "string", false);
+ declare_column ($node, $col++, "table_schema", "string", false);
+ declare_column ($node, $col++, "table_name", "string", false);
+ declare_column ($node, $col++, "constraint_name", "string", false);
+ declare_column ($node, $col++, "column_name", "string", false);
+
+ $data = $node->addChild ("gda_array_data", null);
+ foreach ($res as $i => $value) {
+ if ($table_name && ($table_name != $value))
+ continue;
+
+ $constraints = $mdb2->manager->listTableConstraints ($value);
+ if (PEAR::isError($constraints))
+ continue;
+
+ foreach ($constraints as $cindex => $cname) {
+ if ($constraint_name && ($constraint_name != $cname))
+ continue;
+
+ $cdef = $mdb2->reverse->getTableConstraintDefinition ($value, $cname);
+ if (PEAR::isError($cdef))
+ continue;
+
+ if (! $cdef ["check"])
+ continue;
+
+ $fields = $cdef["fields"];
+ if (!$fields)
+ continue;
+
+ foreach ($fields as $fname => $attrs) {
+ $xmlrow = $data->addChild ("gda_array_row", null);
+
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $value);
+ add_value_child ($xmlrow, $cname);
+ add_value_child ($xmlrow, $fname);
+ }
+ }
+ }
+}
+
+function do_meta_triggers ($reply, &$mdb2, $table_schema=null, $table_name=null)
+{
+ $catalog = get_catalog ($reply, &$mdb2);
+ $res = $mdb2->manager->listTableTriggers ($table_name);
+ handle_pear_error ($res, $reply);
+
+ $node = $reply->addChild ("gda_array", null);
+ $col = 0;
+ declare_column ($node, $col++, "trigger_catalog", "string", false);
+ declare_column ($node, $col++, "trigger_schema", "string", false);
+ declare_column ($node, $col++, "trigger_name", "string", false);
+ declare_column ($node, $col++, "event_manipulation", "string", false);
+ declare_column ($node, $col++, "event_object_catalog", "string", false);
+ declare_column ($node, $col++, "event_object_schema", "string", false);
+ declare_column ($node, $col++, "event_object_table", "string", false);
+ declare_column ($node, $col++, "action_statement", "string", true);
+ declare_column ($node, $col++, "action_orientation", "string", false);
+ declare_column ($node, $col++, "condition_timing", "string", false);
+ declare_column ($node, $col++, "trigger_comments", "string", true);
+ declare_column ($node, $col++, "trigger_short_name", "string", false);
+ declare_column ($node, $col++, "trigger_full_name", "string", false);
+
+ $data = $node->addChild ("gda_array_data", null);
+ foreach ($res as $i => $value) {
+ $tdef = $mdb2->reverse->getTriggerDefinition($value);
+ if (PEAR::isError($constraints))
+ continue;
+
+ print_r ($tdef);
+
+ $xmlrow = $data->addChild ("gda_array_row", null);
+
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $value);
+ add_value_child ($xmlrow, $tdef ["trigger_event"]);
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $catalog);
+ add_value_child ($xmlrow, $tdef ["table_name"]);
+ add_value_child ($xmlrow, $tdef ["trigger_body"]);
+ add_value_child ($xmlrow, "ROW"); /* FIXME */
+ add_value_child ($xmlrow, $tdef ["trigger_type"]);
+ add_value_child ($xmlrow, $tdef ["trigger_comment"]);
+ add_value_child ($xmlrow, $value);
+ add_value_child ($xmlrow, $catalog.".".$value);
+ }
+}
+
+/*
+ * get the connection's catalog
+ */
+$catalog = null;
+function get_catalog ($reply, &$mdb2)
+{
+ global $catalog;
+ if (!$catalog) {
+ $res = MDB2::parseDSN ($_SESSION['dsn']);
+ handle_pear_error ($res, $reply);
+ $catalog = $res ['database'];
+ }
+ return $catalog;
+}
+
+/*
+ * Declare a column in the resulting XML tree
+ */
+function declare_column ($arraynode, $id, $name, $gdatype, $nullok)
+{
+ $field = $arraynode->addChild ("gda_array_field", null);
+
+ $field->addAttribute ("id", "FI".$id);
+ $field->addAttribute ("name", $name);
+ $field->addAttribute ("gdatype", $gdatype);
+ $field->addAttribute ("nullok", $nullok ? "TRUE" : "FALSE");
+}
+
+/*
+ * Add a <gda_value> node to @node
+ */
+function add_value_child ($node, $value, $isbool = false)
+{
+ $val = $node->addChild ("gda_value");
+ if (isset ($value)) {
+ if ($isbool)
+ $val[0] = ($value === true || $value == 't') ? "TRUE" : "FALSE";
+ else
+ $val[0] = str_replace ("&", "&", $value);
+ }
+ else
+ $val->addAttribute ("isnull", "t");
+ return $val;
+}
+?>
\ No newline at end of file
diff --git a/providers/web/php/gda-setup.php b/providers/web/php/gda-setup.php
new file mode 100644
index 0000000..eb98391
--- /dev/null
+++ b/providers/web/php/gda-setup.php
@@ -0,0 +1,57 @@
+<?php
+/* Configure session cache */
+session_cache_limiter('nocache');
+session_start();
+
+header('Content-type: text/plain; charset=UTF-8');
+
+include_once "gda-utils.php";
+include_once "gda-config.php";
+
+if (! try_include ("MDB2.php") && ! try_include ("PEAR".DIRECTORY_SEPARATOR."MDB2.php")) {
+ $reply = new SimpleXMLElement("<reply></reply>");
+ $node = $reply->addChild ("status", "ERROR");
+ $node->addAttribute ("error", "The PEAR MDB2 extension is required");
+ echo gda_add_hash ($init_shared, $reply->asXml());
+ session_destroy ();
+ exit (1);
+}
+
+if (! extension_loaded ("SimpleXML")) {
+ $reply = new SimpleXMLElement("<reply></reply>");
+ $node = $reply->addChild ("status", "ERROR");
+ $node->addAttribute ("error", "The SimpleXML extension is required");
+ echo gda_add_hash ($init_shared, $reply->asXml());
+ session_destroy ();
+ exit (1);
+}
+
+$cmdfile = get_command_filename (session_id ());
+$replyfile = get_reply_filename (session_id ());
+
+umask(0);
+$mode = 0600;
+posix_mkfifo ($cmdfile, $mode);
+posix_mkfifo ($replyfile, $mode);
+
+if (!file_exists ($cmdfile) ||
+ !file_exists ($replyfile)) {
+ $reply = new SimpleXMLElement("<reply></reply>");
+ $node = $reply->addChild ("status", "ERROR");
+ $node->addAttribute ("error", "Can't create named pipes");
+ echo gda_add_hash ($init_shared, $reply->asXml());
+
+ @unlink ($cmdfile);
+ @unlink ($replyfile);
+ session_destroy ();
+ exit (1);
+}
+
+/* all setup */
+$reply = new SimpleXMLElement("<reply></reply>");
+$reply->addChild ('session', htmlspecialchars(SID));
+$reply->addChild ("status", "OK");
+echo gda_add_hash ($init_shared, $reply->asXml());
+flush ();
+ob_flush ();
+?>
diff --git a/providers/web/php/gda-tester.php b/providers/web/php/gda-tester.php
new file mode 100644
index 0000000..07781ca
--- /dev/null
+++ b/providers/web/php/gda-tester.php
@@ -0,0 +1,50 @@
+<?php
+session_cache_limiter('nocache');
+header('Content-type: text/plain; charset=UTF-8');
+
+include_once "gda-utils.php";
+include_once "gda-exception.php";
+include_once "gda-config.php";
+
+$test_connections = false; // set to true to enable each connection testing
+
+echo "Gda connections tester\n----------------------\n\n";
+
+if (! try_include ("MDB2.php", true)) {
+ echo "ERROR: The PEAR MDB2 extension is required\n";
+ exit (1);
+}
+
+if (! extension_loaded ("SimpleXML")) {
+ echo "ERROR: The SimpleXML extension is required\n";
+ exit (1);
+}
+
+function handle_pear_error ($res, $reply)
+{
+ if (PEAR::isError($res)) {
+ $cause = "\tStandard Message [".$res->getMessage()."]\n".
+ "\tUser Information [".$res->getUserInfo()."]\n".
+ "\tDebug Information [".$res->getDebugInfo()."]";
+ throw new GdaException($cause, false);
+ }
+}
+
+echo "\n";
+if ($test_connections) {
+ foreach ($cnc as $dbname => $dbpass) {
+ echo "Connection ".$dbname;
+ try {
+ $mdb2 = MDB2::connect($dsn[$dbname]);
+ handle_pear_error ($mdb2, $reply);
+ echo " ==> OK\n";
+ }
+ catch (GdaException $e) {
+ echo " ==> FAILED:\n".$e->getMessage()."\n";
+ }
+ }
+}
+else {
+ echo "Connections are not tested, set \$test_connections to true to enable connection testing\n";
+}
+?>
diff --git a/providers/web/php/gda-utils.php b/providers/web/php/gda-utils.php
new file mode 100644
index 0000000..212b27d
--- /dev/null
+++ b/providers/web/php/gda-utils.php
@@ -0,0 +1,107 @@
+<?php
+
+function get_command_filename ($session_id)
+{
+ //return session_save_path ()."/command.gda";
+ return session_save_path ()."/GDA".$session_id."Cpipe";
+}
+
+function get_reply_filename ($session_id)
+{
+ //return session_save_path ()."/reply.gda";
+ return session_save_path ()."/GDA".$session_id."Rpipe";
+}
+
+function try_include ($file, $debug=false)
+{
+ $array = explode (":", get_include_path ());
+ foreach ($array as $index => $path) {
+ if (file_exists ($path.DIRECTORY_SEPARATOR.$file)) {
+ include_once ($path.DIRECTORY_SEPARATOR.$file);
+ if ($debug)
+ echo "Using: ".$path.DIRECTORY_SEPARATOR.$file."\n";
+ return true;
+ }
+ }
+ return false;
+}
+
+function gda_add_hash ($key, $text)
+{
+ if ($key == "")
+ return "NOHASH\n".$text;
+ else
+ return hmac ($key, $text)."\n".$text;
+}
+
+// Create an md5 HMAC
+function hmac ($key, $data)
+{
+ // RFC 2104 HMAC implementation for php.
+ // Creates an md5 HMAC.
+ // Eliminates the need to install mhash to compute a HMAC
+ // Hacked by Lance Rushing
+
+ $b = 64; // byte length for md5
+ if (strlen($key) > $b) {
+ $key = pack("H*",md5($key));
+ }
+ $key = str_pad($key, $b, chr(0x00));
+ $ipad = str_pad('', $b, chr(0x36));
+ $opad = str_pad('', $b, chr(0x5c));
+ $k_ipad = $key ^ $ipad ;
+ $k_opad = $key ^ $opad;
+
+ return md5($k_opad . pack("H*",md5($k_ipad . $data)));
+}
+
+// Generate a random character string
+function rand_str($length = 32, $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890')
+{
+ // Length of character list
+ $chars_length = (strlen($chars) - 1);
+
+ // Start our string
+ $string = $chars{rand(0, $chars_length)};
+
+ // Generate random string
+ for ($i = 1; $i < $length; $i = strlen($string))
+ {
+ // Grab a random character from our list
+ $r = $chars{rand(0, $chars_length)};
+
+ // Make sure the same two characters don't appear next to each other
+ if ($r != $string{$i - 1}) $string .= $r;
+ }
+
+ // Return the string
+ return $string;
+}
+
+function mdb2_type_to_gtype ($mdb2type)
+{
+ switch ($mdb2type) {
+ default:
+ case "text":
+ case "clob":
+ return "gchararray";
+ case "integer":
+ return "gint";
+ case "boolean":
+ return "boolean";
+ case "decimal":
+ return "GdaNumerical";
+ case "float":
+ return "gdouble";
+ case "date":
+ return "GDate";
+ case "time":
+ return "GdaTime";
+ case "timestamp":
+ return "GdaTimestamp";
+ case "blob":
+ return "GdaBinary";
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/providers/web/php/gda-worker.php b/providers/web/php/gda-worker.php
new file mode 100644
index 0000000..f4de601
--- /dev/null
+++ b/providers/web/php/gda-worker.php
@@ -0,0 +1,654 @@
+<?php
+/* Configure session cache */
+session_cache_limiter('nocache');
+session_start();
+
+header('Content-type: text/plain; charset=UTF-8');
+
+include_once "gda-utils.php";
+include_once "gda-exception.php";
+include_once "gda-meta.php";
+include_once "gda-config.php";
+
+$debug = false;
+
+if (! try_include ("MDB2.php")) {
+ $reply = new SimpleXMLElement("<reply></reply>");
+ $node = $reply->addChild ("status", "CLOSED");
+ $node->addAttribute ("error", "The PEAR MDB2 extension is required");
+ echo gda_add_hash ($init_shared, $reply->asXml());
+ session_destroy ();
+ exit (1);
+}
+
+if (! extension_loaded ("SimpleXML")) {
+ $reply = new SimpleXMLElement("<reply></reply>");
+ $node = $reply->addChild ("status", "CLOSED");
+ $node->addAttribute ("error", "The SimpleXML extension is required");
+ echo $reply->asXml();
+ exit (1);
+}
+
+register_shutdown_function ('shutdown');
+
+$cmdfile = get_command_filename (session_id ());
+$replyfile = get_reply_filename (session_id ());
+
+if (isset ($_SESSION ["cncclosed"]) && $_SESSION ["cncclosed"]) {
+ /* just wait a little for the worker to terminate and return:
+ * the worker script has been re-spawned but should not try to read
+ * any further command
+ */
+ usleep (50000);
+}
+else {
+ if (isset ($_SESSION ["counter"]))
+ $_SESSION ["counter"] ++;
+ else
+ $_SESSION ["counter"] = 1;
+ gda_worker_main ();
+}
+
+function shutdown ()
+{
+ session_write_close ();
+ flush ();
+ ob_flush();
+}
+
+function send_reply ($retval)
+{
+ global $cmdfile;
+ global $replyfile;
+ global $debug;
+
+ if ($debug) {
+ echo "{{{".$retval."}}}\n";
+ flush ();
+ ob_flush();
+ }
+
+ if (!file_exists ($replyfile))
+ throw new GdaException ("Bad setup", true);
+
+ $datafile = tempnam (session_save_path (), "GDAReply");
+ if (file_put_contents ($datafile, $retval) == false)
+ throw new GdaException ("Can't send reply ", true);
+
+ $file = fopen ($replyfile, "w");
+ if (fwrite ($file, $datafile) == false)
+ throw new Exception ("Can't send command");
+ fclose ($file);
+}
+
+/*
+ * Main function, leaves only when a database error occurs of when
+ * asked to.
+ */
+function gda_worker_main ()
+{
+ global $init_shared;
+ global $cmdfile;
+ global $replyfile;
+
+ $reply = new SimpleXMLElement("<reply></reply>");
+ $reply->addChild ('session', htmlspecialchars(SID));
+ $reply->addChild ('status', "OK");
+ $reply->addChild ("counter", $_SESSION ["counter"]);
+ echo gda_add_hash ($init_shared, $reply->asXml());
+ flush (); ob_flush();
+
+ /* set max execution time to 10s */
+ set_time_limit (10);
+
+ while (1) {
+ $doquit = false;
+ try {
+ if (!file_exists ($cmdfile))
+ throw new GdaException ("Bad setup", true);
+ //echo "Reading from fifo...\n"; flush (); ob_flush();
+ $file = fopen ($cmdfile, "r+");
+ stream_set_blocking ($file, false);
+ while (1) {
+ $readers = array ($file);
+ if (stream_select ($readers, $writers=null, $except=null, 5, 0) < 1) {
+ /* More waiting... */
+ echo chr(0); flush (); ob_flush(); // send something to the client to test connection
+ if (connection_status() != 0) {
+ fclose ($file);
+ exit (0);
+ break;
+ }
+ continue;
+ }
+ else {
+ /* data available to read */
+ break;
+ }
+ }
+ $datafile = fread ($file, 8192);
+ fclose ($file);
+
+ $text = file_get_contents ($datafile);
+ @unlink ($datafile);
+ if ($text == false)
+ throw new GdaException('Could not read command', false);
+
+ //echo "COMMAND [$datafile]\n"; flush (); ob_flush();
+
+ $array = split ("\n", $text, 2);
+ if (sizeof ($array) != 2)
+ throw new GdaException('Missing message hash', true);
+ if ($array[0][0] == "<")
+ throw new GdaException('Bad message hash', true);
+ if ($array[0] != "NOHASH" ||
+ isset ($_SESSION['key'])) {
+ $hash = hmac ($_SESSION['key'], $array[1]);
+ if ($array[0] != $hash)
+ throw new GdaException('Bad message hash', true);
+ }
+ if ($array[1][0] != "<")
+ throw new GdaException('Bad message format', true);
+
+ $xml = simplexml_load_string ($array[1]);
+ $retval = gda_handle_xml ($xml, $doquit);
+
+ /* send reply */
+ try {
+ send_reply ($retval);
+ }
+ catch (Exception $e) {
+ /* will be caught by the listening thread */
+ echo $retval;
+ }
+ }
+ catch (GdaException $e) {
+ $reply = new SimpleXMLElement("<reply></reply>");
+ if (! $e->cnc_closed) {
+ $chal = rand_str ();
+ $_SESSION['challenge'] = $chal;
+ $reply->addChild ('challenge', $chal);
+
+ if (isset ($debug) && $debug)
+ $reply->addChild ('session', htmlspecialchars(SID));
+
+ $node = $reply->addChild ('status', "ERROR");
+ }
+ else {
+ $_SESSION ["cncclosed"] = true;
+ $doquit = true;
+ $node = $reply->addChild ('status', "CLOSED");
+ }
+ $node->addAttribute ("error", $e->getMessage());
+
+ $reply->addChild ("counter", $_SESSION ["counter"]);
+
+ if (isset ($_SESSION['key']))
+ $retval = gda_add_hash ($_SESSION['key'], $reply->asXml());
+ else
+ $retval = gda_add_hash ("", $reply->asXml());
+
+ try {
+ send_reply ($retval);
+ }
+ catch (Exception $e) {
+ /* will be caught by the listening thread */
+ echo $retval;
+ }
+ }
+ //echo "REPLY [$retval]\n";
+ flush ();
+ ob_flush();
+ if ($doquit)
+ break;
+ }
+}
+
+function handle_pear_error ($res, $reply)
+{
+ if (PEAR::isError($res)) {
+ global $debug;
+ if (isset ($debug) && $debug) {
+ $cause = "Standard Message [".$res->getMessage()."] ".
+ "User Information [".$res->getUserInfo()."] ".
+ "Debug Information [".$res->getDebugInfo()."]";
+ $reply->addChild ("debug", $cause);
+ }
+ $tmp = $res->getMessage ();
+ $tmp = str_replace ("MDB2 Error: ", "", $tmp);
+ throw new GdaException ($tmp, false);
+ }
+}
+
+function determine_db_type ($mdb2)
+{
+ if (strstr (get_class ($mdb2), "pgsql"))
+ return "PostgreSQL";
+ else if (strstr (get_class ($mdb2), "mysql"))
+ return "MySQL";
+ else if (strstr (get_class ($mdb2), "sqlite"))
+ return "SQLite";
+ else if (strstr (get_class ($mdb2), "oci"))
+ return "Oracle";
+
+ return false;
+}
+
+/* MDB2 connection, global so it can persist multiple gda_handle_xml() calls */
+$mdb2 = null;
+$prepared_statements = array();
+
+function real_connect ($dsn, $reply)
+{
+ global $mdb2;
+ $options = array(
+ 'debug' => 2,
+ 'portability' => MDB2_PORTABILITY_DELETE_COUNT | MDB2_PORTABILITY_EMPTY_TO_NULL |
+ MDB2_PORTABILITY_ERRORS | MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES | MDB2_PORTABILITY_NUMROWS |
+ MDB2_PORTABILITY_RTRIM,
+ );
+ $mdb2 = &MDB2::connect ($dsn."?new_link=true", $options);
+ handle_pear_error ($mdb2, $reply);
+
+ /* load extra modules */
+ $mdb2->loadModule('Reverse', null, true);
+ $mdb2->loadModule('Manager', null, true);
+ $mdb2->loadModule('Datatype', null, true);
+
+ return $mdb2;
+}
+
+function gda_handle_xml ($xml, &$doquit)
+{
+ //var_dump($xml);
+ $reply = new SimpleXMLElement("<reply></reply>");
+ $apply_md5_to_key = false;
+ $doquit = false;
+
+ global $debug;
+ global $init_shared;
+ global $cnc;
+ global $dsn;
+ global $mdb2;
+ $token = "-";
+ $status = "";
+
+ if ($xml->getName() != "request")
+ throw new GdaException('Bad XML input', true);
+ foreach ($xml->children() as $child) {
+ if ($child->getName() == "token") {
+ $token = $child[0];
+ if ($_SESSION['key'] == $init_shared) {
+ /* change $_SESSION['key'] depending on the configured connections */
+ foreach ($cnc as $dbname => $dbpass) {
+ $computed_token = hmac ($dbname."/AND/".$dbpass, $_SESSION['challenge']);
+ if ($computed_token == $token) {
+ $_SESSION['key'] = $dbname."/AND/".$dbpass;
+ $_SESSION['dsn'] = $dsn[$dbname];
+ $cncfound = true;
+ break;
+ }
+ }
+ if (!isset ($cncfound))
+ throw new GdaException('Connection not found', true);
+ }
+ else {
+ $computed_token = hmac ($_SESSION['key'], $_SESSION['challenge']);
+ if ($computed_token != $token)
+ throw new GdaException('Authentication error', true);
+ }
+ }
+ else if ($child->getName() == "cmd") {
+ switch ($child[0]) {
+ case "HELLO":
+ /* just output a new challenge */
+ $_SESSION['key'] = $init_shared;
+ $reply->addChild ('session', htmlspecialchars(SID));
+ $status = "OK";
+ break;
+
+ case "CONNECT":
+ if (!isset ($_SESSION['key']))
+ throw new GdaException('Protocol error', true);
+ if ($token == "-")
+ throw new GdaException('Not authenticated', true);
+
+ /* actually open the connection */
+ $mdb2 = real_connect ($_SESSION['dsn'], $reply);
+
+ $apply_md5_to_key = true;
+ $dbtype = determine_db_type ($mdb2);
+ if ($dbtype)
+ $reply->addChild ('servertype', $dbtype);
+ $status = "OK";
+ break;
+
+ case "BYE":
+ if (!isset ($_SESSION['key']))
+ throw new GdaException('Protocol error', true);
+ if ($token == "-")
+ throw new GdaException('Not authenticated', true);
+
+ /* actually close the connection */
+ if (isset ($mdb2))
+ $mdb2->disconnect();
+
+ $status = "CLOSED";
+ $doquit = true;
+ $_SESSION ["cncclosed"] = true;
+ break;
+
+ case "PREPARE":
+ if (!isset ($_SESSION['key']))
+ throw new GdaException('Protocol error', true);
+ if ($token == "-")
+ throw new GdaException('Not authenticated', true);
+ $argsnode = null;
+ $type_is_result = false;
+ foreach ($child->children() as $sqlnode) {
+ if ($sqlnode->getName() == "sql") {
+ $sql = $sqlnode[0];
+ foreach($sqlnode->attributes() as $a => $b) {
+ if (($a == "type") && ($b == "SELECT")) {
+ $type_is_result = true;
+ break;
+ }
+ }
+ }
+ else if ($sqlnode->getName() == "arguments") {
+ $argsnode = $sqlnode;
+ }
+ }
+ if (! isset ($sql))
+ throw new GdaException('Bad XML input', true);
+
+ /* actually execute SQL in $sql */
+ if (!isset ($mdb2))
+ $mdb2 = real_connect ($_SESSION['dsn'], $reply);
+
+ do_prepare ($reply, $mdb2, $sql, $type_is_result, $argsnode);
+ $status = "OK";
+ break;
+ case "UNPREPARE":
+ if (!isset ($_SESSION['key']))
+ throw new GdaException('Protocol error', true);
+ if ($token == "-")
+ throw new GdaException('Not authenticated', true);
+ $pstmt_hash = null;
+ foreach ($child->children() as $sqlnode) {
+ if ($sqlnode->getName() == "preparehash") {
+ $pstmt_hash = $sqlnode[0];
+ break;
+ }
+ }
+ /* actually execute SQL in $sql */
+ if (isset ($mdb2) && isset ($pstmt_hash))
+ do_unprepare ($reply, $mdb2, $pstmt_hash);
+ $status = "OK";
+ break;
+ case "EXEC":
+ if (!isset ($_SESSION['key']))
+ throw new GdaException('Protocol error', true);
+ if ($token == "-")
+ throw new GdaException('Not authenticated', true);
+ $argsnode = null;
+ $pstmt_hash = null;
+ $type_is_result = false;
+
+ foreach ($child->children() as $sqlnode) {
+ if ($sqlnode->getName() == "sql") {
+ $sql = $sqlnode[0];
+ foreach ($sqlnode->attributes() as $a => $b) {
+ if (($a == "type") && ($b == "SELECT")) {
+ $type_is_result = true;
+ break;
+ }
+ }
+ }
+ else if ($sqlnode->getName() == "arguments")
+ $argsnode = $sqlnode;
+ else if ($sqlnode->getName() == "preparehash")
+ $pstmt_hash = $sqlnode[0];
+ }
+ if (! isset ($sql))
+ throw new GdaException('Bad XML input', true);
+
+ /* actually execute SQL in $sql */
+ if (!isset ($mdb2))
+ $mdb2 = real_connect ($_SESSION['dsn'], $reply);
+
+ do_exec ($reply, $mdb2, $pstmt_hash, $sql, $type_is_result, $argsnode);
+ $status = "OK";
+ break;
+ case 'META':
+ if (!isset ($_SESSION['key']))
+ throw new GdaException('Protocol error', true);
+ if ($token == "-")
+ throw new GdaException('Not authenticated', true);
+
+ $args = array ();
+ foreach ($child->children() as $sqlnode) {
+ if ($sqlnode->getName() == "arg") {
+ foreach ($sqlnode->attributes() as $a => $b) {
+ if ($a == "name") {
+ $args[$b] = $sqlnode[0];
+ break;
+ }
+ }
+ }
+ }
+ foreach ($child->attributes() as $a => $b) {
+ if ($a == "type") {
+ $type = $b;
+ break;
+ }
+ }
+ if (! isset ($type))
+ throw new GdaException('Bad XML input', true);
+
+ if (!isset ($mdb2))
+ $mdb2 = real_connect ($_SESSION['dsn'], $reply);
+
+ do_meta ($reply, $mdb2, $type, $args);
+ $status = "OK";
+ break;
+ default:
+ throw new GdaException('Unknown command '.$child[0], false);
+ }
+ }
+ else {
+ throw new GdaException('Bad XML input', true);
+ }
+ }
+
+ if ($status != "CLOSED") {
+ $chal = rand_str ();
+ $_SESSION['challenge'] = $chal;
+ $reply->addChild ('challenge', $chal);
+ }
+
+ if ($status != "")
+ $reply->addChild ('status', $status);
+ $reply->addChild ("counter", $_SESSION ["counter"]);
+ if (isset ($debug) && $debug && $status != "CLOSED") {
+ $reply->addChild ('session', htmlspecialchars(SID));
+ }
+
+ /* output reply */
+ $retval = gda_add_hash ($_SESSION['key'], $reply->asXml());
+ if ($apply_md5_to_key) {
+ /* modify $key so it's possible for the client to forget about password */
+ $_SESSION['key'] = md5 ($_SESSION['key']);
+ }
+ return $retval;
+}
+
+function do_prepare ($reply, &$mdb2, $sql, $type_is_result, $argsnode)
+{
+ /* prepare argument types */
+ if ($argsnode) {
+ $argtypes = array();
+ foreach ($argsnode->children() as $node) {
+ if ($node->getName() == "arg") {
+ foreach($node->attributes() as $a => $b) {
+ if ($a == "type") {
+ $argtypes[] = $b;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ $argtypes = null;
+ if ($type_is_result)
+ $res = $mdb2->prepare((string) $sql, $argtypes, MDB2_PREPARE_RESULT);
+ else
+ $res = $mdb2->prepare((string) $sql, $argtypes, MDB2_PREPARE_MANIP);
+ handle_pear_error ($res, $reply);
+
+ global $prepared_statements;
+ $pstmt_hash = md5 ((string) $sql);
+ $prepared_statements [$pstmt_hash] = $res;
+ $reply->addChild ('preparehash', $pstmt_hash);
+ return $res;
+}
+
+function do_unprepare ($reply, &$mdb2, $pstmt_hash)
+{
+ /* get prepared statement */
+ global $prepared_statements;
+ if (isset ($pstmt_hash))
+ $prep = $prepared_statements [(string) $pstmt_hash];
+ if (isset ($prep)) {
+ $prep->free();
+ unset ($prepared_statements [(string) $pstmt_hash]);
+ }
+}
+
+function do_exec ($reply, &$mdb2, $pstmt_hash, $sql, $type_is_result, $argsnode)
+{
+ /* get prepared statement */
+ global $prepared_statements;
+ if (isset ($pstmt_hash))
+ $prep = $prepared_statements [(string) $pstmt_hash];
+
+ if (!isset ($prep))
+ $prep = do_prepare ($reply, $mdb2, (string) $sql, $type_is_result, $argsnode);
+
+ /* handle arguments */
+ if ($argsnode) {
+ $args = array();
+ foreach ($argsnode->children() as $node) {
+ if ($node->getName() == "arg") {
+ $args[] = $node[0];
+ }
+ }
+ }
+ else
+ $args = null;
+
+ /* actual execution */
+ $res = $prep->execute ($args);
+ handle_pear_error ($res, $reply);
+
+ if ($type_is_result) {
+ $node = $reply->addChild ("gda_array", null);
+ $ncols = $res->numCols();
+
+ /* compute field type */
+ $tableinfo = $mdb2->tableInfo ($res, NULL);
+ if (! PEAR::isError ($tableinfo)) {
+ //var_dump($tableinfo);
+ $gtypes = array();
+ $dbtypes = array();
+ for ($i = 0; $i < $ncols; $i++) {
+ $gtypes [$i] = mdb2_type_to_gtype ($tableinfo[$i]['mdb2type']);
+ $dbtypes [$i] = $tableinfo[$i]['type'];
+ }
+ }
+
+ $colnames = $res->getColumnNames ();
+ if (PEAR::isError ($colnames))
+ unset ($colnames);
+ for ($i = 0; $i < $ncols; $i++) {
+ $field = $node->addChild ("gda_array_field", null);
+ $field->addAttribute ("id", "FI".$i);
+ if (isset ($colnames)) {
+ foreach ($colnames as $name => $pos) {
+ if ($pos == $i) {
+ $field->addAttribute ("name", $name);
+ break;
+ }
+ }
+ }
+ if (isset ($gtypes))
+ $field->addAttribute ("gdatype", $gtypes[$i]);
+ else
+ $field->addAttribute ("gdatype", "string");
+ if (isset ($dbtypes) && $dbtypes[$i])
+ $field->addAttribute ("dbtype", $dbtypes[$i]);
+ $field->addAttribute ("nullok", "TRUE");
+ }
+
+ $data = $node->addChild ("gda_array_data", null);
+ while (($row = $res->fetchRow())) {
+ // MDB2's default fetchmode is MDB2_FETCHMODE_ORDERED
+ $xmlrow = $data->addChild ("gda_array_row", null);
+ for ($i = 0; $i < $ncols; $i++) {
+ $val = $xmlrow->addChild ("gda_value");
+ $val[0] = str_replace ("&", "&", $row[$i]);
+ }
+ }
+ }
+ else {
+ $reply->addChild ("impacted_rows", (string) $res);
+ }
+}
+
+function do_meta ($reply, &$mdb2, $type, $args)
+{
+ switch ($type) {
+ case "info":
+ do_meta_info ($reply, &$mdb2);
+ break;
+ case "btypes":
+ do_meta_btypes ($reply, &$mdb2);
+ break;
+ case "schemas":
+ do_meta_schemas ($reply, &$mdb2, $args["schema_name"]);
+ break;
+ case "tables":
+ do_meta_tables ($reply, &$mdb2, $args["table_schema"], $args["table_name"]);
+ break;
+ case "views":
+ do_meta_views ($reply, &$mdb2, $args["table_schema"], $args["table_name"]);
+ break;
+ case "columns":
+ do_meta_columns ($reply, &$mdb2, $args["table_schema"], $args["table_name"]);
+ break;
+ case "constraints_tab":
+ do_meta_constraints_tab ($reply, &$mdb2,
+ $args["table_schema"], $args["table_name"], $args["constraint_name"]);
+ break;
+ case "constraints_ref":
+ do_meta_constraints_ref ($reply, &$mdb2,
+ $args["table_schema"], $args["table_name"], $args["constraint_name"]);
+ break;
+ case "key_columns":
+ do_meta_key_columns ($reply, &$mdb2,
+ $args["table_schema"], $args["table_name"], $args["constraint_name"]);
+ break;
+ case "check_columns":
+ do_meta_check_columns ($reply, &$mdb2,
+ $args["table_schema"], $args["table_name"], $args["constraint_name"]);
+ break;
+ case "triggers":
+ do_meta_triggers ($reply, &$mdb2, $args["table_schema"], $args["table_name"]);
+ break;
+ default:
+ throw new GdaException('Unknown META command '.$type, false);
+ }
+}
+
+?>
diff --git a/providers/web/protocol.dia b/providers/web/protocol.dia
new file mode 100644
index 0000000..c83a252
Binary files /dev/null and b/providers/web/protocol.dia differ
diff --git a/providers/web/web_specs_auth.xml.in b/providers/web/web_specs_auth.xml.in
new file mode 100644
index 0000000..cf7106f
--- /dev/null
+++ b/providers/web/web_specs_auth.xml.in
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<data-set-spec>
+ <parameters>
+ <parameter id="PASSWORD" _name="Password" _descr="Connection password as defined on the web server" gdatype="gchararray" nullok="FALSE"/>
+ </parameters>
+</data-set-spec>
diff --git a/providers/web/web_specs_dsn.xml.in b/providers/web/web_specs_dsn.xml.in
new file mode 100644
index 0000000..0578478
--- /dev/null
+++ b/providers/web/web_specs_dsn.xml.in
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<data-set-spec>
+ <parameters>
+ <parameter id="HOST" _name="Web server name" _descr="The name of the web server which proxies connections" gdatype="gchararray" nullok="FALSE"/>
+ <parameter id="PORT" _name="Port" _descr="Web server port (leave this field empty for default)" gdatype="gint"/>
+ <parameter id="PATH" _name="Path to PHP script" _descr="The path of the script to use if service is not at the web server's root" gdatype="gchararray"/>
+ <parameter id="SECRET" _name="Server secret" _descr="Server secret" gdatype="gchararray" nullok="FALSE"/>
+ <parameter id="DB_NAME" _name="Database name" _descr="The name of a database to use" gdatype="gchararray" nullok="FALSE"/>
+ <parameter id="USE_SSL" _name="Require SSL" _descr="Whether or not to use SSL to establish the connection" gdatype="gboolean"/>
+ </parameters>
+</data-set-spec>
diff --git a/tools/gda-sql.c b/tools/gda-sql.c
index 5c03a08..7b7b5e2 100644
--- a/tools/gda-sql.c
+++ b/tools/gda-sql.c
@@ -38,6 +38,7 @@
#ifndef G_OS_WIN32
#include <signal.h>
+typedef void (*sighandler_t)(int);
#include <pwd.h>
#else
#include <stdlib.h>
@@ -1243,6 +1244,7 @@ typedef struct {
static gpointer thread_start_update_meta_store (MetaUpdateData *data);
static void thread_ok_cb_update_meta_store (GdaThreader *threader, guint job, MetaUpdateData *data);
static void thread_cancelled_cb_update_meta_store (GdaThreader *threader, guint job, MetaUpdateData *data);
+static void conn_closed_cb (GdaConnection *cnc, ConnectionSetting *cs);
/*
* Open a connection
@@ -1453,6 +1455,11 @@ open_connection (SqlConsole *console, const gchar *cnc_name, const gchar *cnc_st
g_free (dict_file_name);
}
+ if (cs) {
+ g_signal_connect (cs->cnc, "conn-closed",
+ G_CALLBACK (conn_closed_cb), cs);
+ }
+
return cs;
}
@@ -1514,8 +1521,11 @@ static void
connection_settings_free (ConnectionSetting *cs)
{
g_free (cs->name);
- if (cs->cnc)
+ if (cs->cnc) {
+ g_signal_handlers_disconnect_by_func (cs->cnc,
+ G_CALLBACK (conn_closed_cb), cs);
g_object_unref (cs->cnc);
+ }
if (cs->parser)
g_object_unref (cs->parser);
if (cs->query_buffer)
@@ -1641,7 +1651,7 @@ output_string (const gchar *str)
gboolean append_nl = FALSE;
gint length;
static gint force_no_pager = -1;
-
+
if (!str)
return;
@@ -1666,13 +1676,15 @@ output_string (const gchar *str)
/* use pager */
FILE *pipe;
const char *pager;
-
+#ifndef G_OS_WIN32
+ sighandler_t phandler;
+#endif
pager = getenv ("PAGER");
if (!pager)
pager = "more";
pipe = popen (pager, "w");
#ifndef G_OS_WIN32
- signal (SIGPIPE, SIG_IGN);
+ phandler = signal (SIGPIPE, SIG_IGN);
#endif
if (append_nl)
g_fprintf (pipe, "%s\n", str);
@@ -1680,7 +1692,7 @@ output_string (const gchar *str)
g_fprintf (pipe, "%s", str);
pclose (pipe);
#ifndef G_OS_WIN32
- signal(SIGPIPE, SIG_DFL);
+ signal (SIGPIPE, phandler);
#endif
}
else {
@@ -3010,12 +3022,18 @@ extra_command_manage_cnc (SqlConsole *console, GdaConnection *cnc, const gchar *
}
}
+static void
+conn_closed_cb (GdaConnection *cnc, ConnectionSetting *cs)
+{
+ extra_command_close_cnc (NULL, cnc, NULL, NULL, NULL);
+}
+
static
GdaInternalCommandResult *
extra_command_close_cnc (SqlConsole *console, GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
{
ConnectionSetting *cs = NULL;
- if (args[0] && *args[0]) {
+ if (args && args[0] && *args[0]) {
cs = find_connection_from_name (args[0]);
if (!cs) {
g_set_error (error, 0, 0,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]