libgda r3058 - in branches/V4-branch: . doc/C doc/C/tmpl libgda libgda-report/engine libgda/providers-support libgda/sqlite providers providers/mdb providers/postgres providers/skel-implementation/capi tools
- From: vivien svn gnome org
- To: svn-commits-list gnome org
- Subject: libgda r3058 - in branches/V4-branch: . doc/C doc/C/tmpl libgda libgda-report/engine libgda/providers-support libgda/sqlite providers providers/mdb providers/postgres providers/skel-implementation/capi tools
- Date: Wed, 20 Feb 2008 20:36:06 +0000 (GMT)
Author: vivien
Date: Wed Feb 20 20:36:04 2008
New Revision: 3058
URL: http://svn.gnome.org/viewvc/libgda?rev=3058&view=rev
Log:
2008-02-20 Vivien Malerba <malerba gnome-db org>
* tools/gda-sql.c: if the GDA_NO_PAGER environment variable is set, then don't use a pager at all
* libgda/providers-support/gda-pstmt.[ch]: prepared statement can now hold a (weak)reference to the
GdaStatement from which it has been prepared
* libgda/providers-support/gda-pmodel.[ch]: changed the signature of the virtual functions to allow
implementations to return errors
* libgda/gda-client.[ch]:
* libgda/gda-client-private.h:
* libgda/gda-connection.c: removed some symbols from the API/ABI
* libgda/gda-client.[ch]: removed gda_client_close_all_connections(), gda_client_begin_transaction(),
gda_client_commit_transaction(), gda_client_rollback_transaction() and gda_client_find_connection()
because their implementation was obvious and it could lead to assume some extra services where
provided which was not true
* libgda/gda-client.[ch]: use explicit error codes
* libgda/gda-connection.[ch]: removed gda_connection_new() because creating connection objects
should only be done using gda_client_open_connection...()
* doc/C: doc. improvements
* libgda/gda-config.c:
* libgda/gda-client.c: provider names are now case insensitive
* providers/Makefile.am:
* providers/postgres/: rewrote the PostgreSQL provider
* providers/skel-implementation/capi/: improved the skeleton example
* providers/prepare_provider_sources.sh: new script to change all files and objects names
to reflect the real provider name when starting a provider by making a copy of a
skeleton implementation
* other files: compilation warnings removed
Added:
branches/V4-branch/libgda/gda-client-private.h
branches/V4-branch/providers/postgres/gda-postgres-meta.c
branches/V4-branch/providers/postgres/gda-postgres-meta.h
branches/V4-branch/providers/postgres/gda-postgres-pstmt.c
branches/V4-branch/providers/postgres/gda-postgres-pstmt.h
branches/V4-branch/providers/postgres/gda-postgres-util.c
branches/V4-branch/providers/postgres/gda-postgres-util.h
branches/V4-branch/providers/prepare_provider_sources.sh (contents, props changed)
Removed:
branches/V4-branch/providers/postgres/utils.c
Modified:
branches/V4-branch/ChangeLog
branches/V4-branch/doc/C/libgda-4.0-sections.txt
branches/V4-branch/doc/C/prov-writing.xml
branches/V4-branch/doc/C/tmpl/gda-client.sgml
branches/V4-branch/doc/C/tmpl/gda-connection.sgml
branches/V4-branch/doc/C/tmpl/gda-data-model.sgml
branches/V4-branch/doc/C/tmpl/gda-pmodel.sgml
branches/V4-branch/doc/C/tmpl/gda-pstmt.sgml
branches/V4-branch/doc/C/tmpl/gda-server-provider.sgml
branches/V4-branch/libgda-report/engine/gda-report-engine.c
branches/V4-branch/libgda/gda-client.c
branches/V4-branch/libgda/gda-client.h
branches/V4-branch/libgda/gda-config.c
branches/V4-branch/libgda/gda-connection.c
branches/V4-branch/libgda/gda-connection.h
branches/V4-branch/libgda/gda-data-proxy.c
branches/V4-branch/libgda/gda-server-provider.c
branches/V4-branch/libgda/gda-server-provider.h
branches/V4-branch/libgda/providers-support/gda-pmodel.c
branches/V4-branch/libgda/providers-support/gda-pmodel.h
branches/V4-branch/libgda/providers-support/gda-pstmt.c
branches/V4-branch/libgda/providers-support/gda-pstmt.h
branches/V4-branch/libgda/sqlite/gda-sqlite-provider.c
branches/V4-branch/libgda/sqlite/gda-sqlite-recordset.c
branches/V4-branch/providers/Makefile.am
branches/V4-branch/providers/mdb/gda-mdb-provider.c
branches/V4-branch/providers/postgres/Makefile.am
branches/V4-branch/providers/postgres/gda-postgres-blob-op.c
branches/V4-branch/providers/postgres/gda-postgres-ddl.c
branches/V4-branch/providers/postgres/gda-postgres-ddl.h
branches/V4-branch/providers/postgres/gda-postgres-handler-bin.c
branches/V4-branch/providers/postgres/gda-postgres-handler-bin.h
branches/V4-branch/providers/postgres/gda-postgres-parser.c
branches/V4-branch/providers/postgres/gda-postgres-parser.h
branches/V4-branch/providers/postgres/gda-postgres-provider.c
branches/V4-branch/providers/postgres/gda-postgres-provider.h
branches/V4-branch/providers/postgres/gda-postgres-recordset.c
branches/V4-branch/providers/postgres/gda-postgres-recordset.h
branches/V4-branch/providers/postgres/gda-postgres.h
branches/V4-branch/providers/postgres/gen_def.c
branches/V4-branch/providers/postgres/libmain.c
branches/V4-branch/providers/skel-implementation/capi/gda-capi-ddl.h
branches/V4-branch/providers/skel-implementation/capi/gda-capi-parser.h
branches/V4-branch/providers/skel-implementation/capi/gda-capi-provider.c
branches/V4-branch/providers/skel-implementation/capi/gda-capi-pstmt.h
branches/V4-branch/providers/skel-implementation/capi/gda-capi-recordset.c
branches/V4-branch/providers/skel-implementation/capi/gda-capi-recordset.h
branches/V4-branch/providers/skel-implementation/capi/libgda-capi-4.0.pc.in
branches/V4-branch/tools/gda-sql.c
Modified: branches/V4-branch/doc/C/libgda-4.0-sections.txt
==============================================================================
--- branches/V4-branch/doc/C/libgda-4.0-sections.txt (original)
+++ branches/V4-branch/doc/C/libgda-4.0-sections.txt Wed Feb 20 20:36:04 2008
@@ -2,21 +2,13 @@
<FILE>gda-client</FILE>
<TITLE>GdaClient</TITLE>
GdaClient
+GdaClientError
GdaClientEvent
-GdaClientSpecsType
gda_client_new
gda_client_open_connection
gda_client_open_connection_from_string
gda_client_get_connections
-gda_client_find_connection
-gda_client_close_all_connections
-gda_client_notify_event
-gda_client_notify_error_event
-gda_client_notify_connection_opened_event
-gda_client_notify_connection_closed_event
-gda_client_begin_transaction
-gda_client_commit_transaction
-gda_client_rollback_transaction
+<SUBSECTION>
gda_client_prepare_create_database
gda_client_perform_create_database
gda_client_prepare_drop_database
@@ -170,7 +162,6 @@
<TITLE>GdaConnection</TITLE>
GdaConnection
GdaConnectionOptions
-gda_connection_new
gda_connection_open
gda_connection_close
gda_connection_close_no_warning
@@ -1432,6 +1423,8 @@
GdaPModelClass
gda_pmodel_take_row
gda_pmodel_get_stored_row
+gda_pmodel_set_modification_query
+gda_pmodel_compute_modification_queries
<SUBSECTION Standard>
GDA_IS_PMODEL
GDA_PMODEL
@@ -1445,6 +1438,7 @@
<TITLE>GdaPStmt</TITLE>
<INCLUDE>providers-support/gda-pstmt.h</INCLUDE>
GdaPStmt
+gda_pstmt_set_gda_statement
gda_pstmt_copy_contents
<SUBSECTION Standard>
GDA_IS_PSTMT
Modified: branches/V4-branch/doc/C/prov-writing.xml
==============================================================================
--- branches/V4-branch/doc/C/prov-writing.xml (original)
+++ branches/V4-branch/doc/C/prov-writing.xml Wed Feb 20 20:36:04 2008
@@ -313,6 +313,33 @@
This section explains how to write virtual methods for the <link linkend="GdaPModel">GdaPModel</link> subclasses
implementations, which should be done when implementing a database provider.
</para>
+ <para>
+ That data model implementation simplifies the implementation of provider's recordsets by having them implement only a
+ few virtual methods, and offering some services.
+ </para>
+ <para>
+ The data model represents each row in a separate <link linkend="GdaPRow">GdaPRow</link> object, and virtual
+ methods to retreive rows have to return new (and correctly initialized) objects of that class. The referencing
+ of these new objects is left up to the implementation which:
+ <itemizedlist>
+ <listitem>
+ <para>can rely on the <link linkend="GdaPRow">GdaPRow</link> implementation to keep them, using
+ the <link linkend="gda-pmodel-take-row">gda_pmodel_take_row ()</link> method for each row object created which
+ is the easiest, the fastest as once created, the rows are available for further usage,
+ but will consume more memory with each row read from the model.</para>
+ </listitem>
+ <listitem>
+ <para>can keep the references to these objects to the subclass implementation which will consume less memory but
+ will make it necessary to create a new row object each time a row is read which is slower</para>
+ </listitem>
+ <listitem>
+ <para>can do a mix of the two solutions above: monitor the memory usage for the data model to
+ enable to "cache" some rows and discard some when memory is needed</para>
+ </listitem>
+ </itemizedlist>
+ Note that the methods mentionned in the 1st and 3rd items should be reserved for random data access mode,
+ and that cursor based access mode should implement the method mentionned in th 2nd item.
+ </para>
<sect2>
<title>fetch_nb_rows()</title>
<para>
@@ -328,28 +355,45 @@
<sect2>
<title>fetch_random()</title>
<para>
- To write.
+ This method is called when the user calls <link linkend="gda-data-model-get-value-at">gda_data_model_get_value_at ()</link>,
+ and in available only when the data access mode for the data model is random (that is not cursor based). When data
+ access is cursor based, this method will not be called.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>store_all()</title>
+ <para>
+ This method is called when the user sets the "store-all-rows" to TRUE. It has the effect of forcing the creation
+ of a <link linkend="GdaPRow">GdaPRow</link> object for each row of the data model (thus increasing the memory consumption
+ but reducing further access times). It is available only when the data access mode for the data model is random
+ (that is not cursor based). When data access is cursor based, this method will not be called.
</para>
</sect2>
<sect2>
<title>fetch_next()</title>
<para>
- To write.
+ This method is called when data access is cursor based and a data model iterator is moved one position forward. The
+ <parameter>rownum</parameter> is an indication of what the row number will be once the next row has been fetched (it can
+ safely be discarded if that information is not necessary).
</para>
</sect2>
<sect2>
<title>fetch_prev()</title>
<para>
- To write.
+ This method is called when data access is cursor based and a data model iterator is moved one position backward. The
+ <parameter>rownum</parameter> is an indication of what the row number will be once the previous row has been fetched (it can
+ safely be discarded if that information is not necessary).
</para>
</sect2>
<sect2>
<title>fetch_at()</title>
<para>
- To write.
+ This method can be implemented when data access is cursor based and there is a shorter way of getting to a
+ specific row than having to call the fetch_next() or fetch_prev() methods several times.
</para>
</sect2>
</chapter>
Modified: branches/V4-branch/doc/C/tmpl/gda-client.sgml
==============================================================================
--- branches/V4-branch/doc/C/tmpl/gda-client.sgml (original)
+++ branches/V4-branch/doc/C/tmpl/gda-client.sgml Wed Feb 20 20:36:04 2008
@@ -9,7 +9,7 @@
This class is the main entry point for libgda client applications. It provides
the way by which client applications open connections. Thus, before using any other
database-oriented function in libgda, applications must create a #GdaClient object
- (via #gda_client_new), and, once created, open the connections from it.
+ (via gda_client_new()), and, once created, open the connections from it.
</para>
<para>
#GdaClient also provides a way to treat several connections as if they were only
@@ -18,10 +18,11 @@
obtain the list of all tables in all opened connections.
</para>
<para>
-Database creation and destruction is done through a #GdaClient object uding the
-gda_client_create_database() and gda_client_drop_database() methods. Note however that
-depending on the provider, an opened connection may be required in order to create
-or destroy a database.
+ Database creation and destruction is done through a #GdaClient object uding the
+ gda_client_prepare_create_database(), gda_client_perform_create_database(),
+ gda_client_prepare_drop_database() and gda_client_perform_drop_database() methods. Note however that
+ depending on the provider, an opened connection may be required in order to create
+ or destroy a database.
</para>
<!-- ##### SECTION See_Also ##### -->
@@ -48,6 +49,14 @@
@arg2:
@arg3:
+<!-- ##### ENUM GdaClientError ##### -->
+<para>
+
+</para>
+
+ GDA_CLIENT_DSN_NOT_FOUND_ERROR:
+ GDA_CLIENT_PROVIDER_NOT_FOUND_ERROR:
+
<!-- ##### ENUM GdaClientEvent ##### -->
<para>
@@ -105,98 +114,6 @@
@Returns:
-<!-- ##### FUNCTION gda_client_find_connection ##### -->
-<para>
-
-</para>
-
- client:
- dsn:
- auth_string:
- Returns:
-
-
-<!-- ##### FUNCTION gda_client_close_all_connections ##### -->
-<para>
-
-</para>
-
- client:
-
-
-<!-- ##### FUNCTION gda_client_notify_event ##### -->
-<para>
-
-</para>
-
- client:
- cnc:
- event:
- params:
-
-
-<!-- ##### FUNCTION gda_client_notify_error_event ##### -->
-<para>
-
-</para>
-
- client:
- cnc:
- error:
-
-
-<!-- ##### FUNCTION gda_client_notify_connection_opened_event ##### -->
-<para>
-
-</para>
-
- client:
- cnc:
-
-
-<!-- ##### FUNCTION gda_client_notify_connection_closed_event ##### -->
-<para>
-
-</para>
-
- client:
- cnc:
-
-
-<!-- ##### FUNCTION gda_client_begin_transaction ##### -->
-<para>
-
-</para>
-
- client:
- name:
- level:
- error:
- Returns:
-
-
-<!-- ##### FUNCTION gda_client_commit_transaction ##### -->
-<para>
-
-</para>
-
- client:
- name:
- error:
- Returns:
-
-
-<!-- ##### FUNCTION gda_client_rollback_transaction ##### -->
-<para>
-
-</para>
-
- client:
- name:
- error:
- Returns:
-
-
<!-- ##### FUNCTION gda_client_prepare_create_database ##### -->
<para>
Modified: branches/V4-branch/doc/C/tmpl/gda-connection.sgml
==============================================================================
--- branches/V4-branch/doc/C/tmpl/gda-connection.sgml (original)
+++ branches/V4-branch/doc/C/tmpl/gda-connection.sgml Wed Feb 20 20:36:04 2008
@@ -7,11 +7,11 @@
<!-- ##### SECTION Long_Description ##### -->
<para>
The #GdaConnection class offers access to all operations involving an
- opened connection to a database. #GdaConnection objects are obtained
- via the #GdaClient class.
+ opened connection to a database. #GdaConnection objects are not instiated directly
+ but by #GdaClient objects using gda_client_open_connection() and gda_client_open_connection_from_string()
</para>
<para>
- Once obtained, applications can use #GdaConnection to execute commands,
+ Applications can then use #GdaConnection objects to execute commands,
run transactions, and get information about all objects stored in the
underlying database.
</para>
@@ -117,19 +117,6 @@
@GDA_CONNECTION_OPTIONS_READ_ONLY:
@GDA_CONNECTION_OPTIONS_DONT_SHARE:
-<!-- ##### FUNCTION gda_connection_new ##### -->
-<para>
-
-</para>
-
- client:
- provider:
- dsn:
- auth_string:
- options:
- Returns:
-
-
<!-- ##### FUNCTION gda_connection_open ##### -->
<para>
Modified: branches/V4-branch/doc/C/tmpl/gda-data-model.sgml
==============================================================================
--- branches/V4-branch/doc/C/tmpl/gda-data-model.sgml (original)
+++ branches/V4-branch/doc/C/tmpl/gda-data-model.sgml Wed Feb 20 20:36:04 2008
@@ -17,10 +17,14 @@
</para>
<para>
Again, depending on the real implementation, data retreiving can be done either accessing direct random
-values located by their row and column, or using a #GdaDataModelIter cursor, or both. Use the
-gda_data_model_get_access_flags() method to know how the data model can be accessed. Note that a #GdaDatamodel
-which can be accessed in a random way can also be accessed using cursors (and several cusrors may be used at the same
-time), whereas data model which can only be accessed using cursors can only have one cursor for iterating.
+values located by their row and column, or using a cursor, or both. Use the gda_data_model_get_access_flags()
+method to know how the data model can be accessed.
+<itemizedlist>
+ <listitem><para>Random access to a data model's contents is done using gda_data_model_get_value_at(), or using
+ one or more #GdaDataModelIter object(s);</para></listitem>
+ <listitem><para>Cursor access to a data model's contents is done using a #GdaDataModelIter object (only one can be created),
+ it is <emphasis>not possible</emphasis> to use gda_data_model_get_value_at() in this mode.</para></listitem>
+</itemizedlist>
</para>
<para>
Random access data models are easier to use since picking a value is very simple using the gda_data_model_get_value_at(),
Modified: branches/V4-branch/doc/C/tmpl/gda-pmodel.sgml
==============================================================================
--- branches/V4-branch/doc/C/tmpl/gda-pmodel.sgml (original)
+++ branches/V4-branch/doc/C/tmpl/gda-pmodel.sgml Wed Feb 20 20:36:04 2008
@@ -39,6 +39,7 @@
@object: base object
@priv: private data
@prep_stmt: SELECT prepared statement for which the execution gave this object
+ nb_stored_rows:
@advertized_nrows: initially set to -1, set to a value >= 0 when the number of rows in the data model is known
<!-- ##### STRUCT GdaPModelClass ##### -->
@@ -49,6 +50,7 @@
@parent_class: parent object class
@fetch_nb_rows: virtual method which must be implemented when access method is GDA_DATA_MODEL_ACCESS_RANDOM
@fetch_random: virtual method which must be implemented when access method is GDA_DATA_MODEL_ACCESS_RANDOM
+ store_all:
@fetch_next: virtual method which must be implemented when access method is GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD
@fetch_prev: virtual method which must be implemented when access method is GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD
@fetch_at: virtual method which can be implemented when access method is GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD or GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD
@@ -73,3 +75,26 @@
@Returns:
+<!-- ##### FUNCTION gda_pmodel_set_modification_query ##### -->
+<para>
+
+</para>
+
+ model:
+ mod_stmt:
+ error:
+ Returns:
+
+
+<!-- ##### FUNCTION gda_pmodel_compute_modification_queries ##### -->
+<para>
+
+</para>
+
+ model:
+ target:
+ use_all_fields_if_no_pk:
+ error:
+ Returns:
+
+
Modified: branches/V4-branch/doc/C/tmpl/gda-pstmt.sgml
==============================================================================
--- branches/V4-branch/doc/C/tmpl/gda-pstmt.sgml (original)
+++ branches/V4-branch/doc/C/tmpl/gda-pstmt.sgml Wed Feb 20 20:36:04 2008
@@ -23,12 +23,22 @@
</para>
@object: base object
+ stmt:
@sql: actual SQL code used for this prepared statement, its memory is freed by the object itself
@param_ids: list of parameters' IDs (as gchar *), the memory is freed by object itself
@ncols: number of columns in the returned data model (if the prepared statement is a SELECT statement)
@types: array of ncols types (if the prepared statement is a SELECT statement)
@tmpl_columns: list of <link linkend="GdaColumn">GdaColumn</link> objects which data models created from this prepared statement can copy
+<!-- ##### FUNCTION gda_pstmt_set_gda_statement ##### -->
+<para>
+
+</para>
+
+ pstmt:
+ stmt:
+
+
<!-- ##### FUNCTION gda_pstmt_copy_contents ##### -->
<para>
Modified: branches/V4-branch/doc/C/tmpl/gda-server-provider.sgml
==============================================================================
--- branches/V4-branch/doc/C/tmpl/gda-server-provider.sgml (original)
+++ branches/V4-branch/doc/C/tmpl/gda-server-provider.sgml Wed Feb 20 20:36:04 2008
@@ -59,9 +59,16 @@
@statement_to_sql:
@statement_prepare:
@statement_execute:
+ is_busy:
@cancel:
@create_connection:
@meta_funcs:
+ _gda_reserved1:
+ _gda_reserved2:
+ _gda_reserved3:
+ _gda_reserved4:
+ _gda_reserved5:
+ _gda_reserved6:
<!-- ##### ENUM GdaServerProviderError ##### -->
<para>
@@ -75,6 +82,7 @@
@GDA_SERVER_PROVIDER_OPERATION_ERROR:
@GDA_SERVER_PROVIDER_INTERNAL_ERROR:
@GDA_SERVER_PROVIDER_BUSY_ERROR:
+ GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR:
<!-- ##### STRUCT GdaServerProviderMeta ##### -->
<para>
Modified: branches/V4-branch/libgda-report/engine/gda-report-engine.c
==============================================================================
--- branches/V4-branch/libgda-report/engine/gda-report-engine.c (original)
+++ branches/V4-branch/libgda-report/engine/gda-report-engine.c Wed Feb 20 20:36:04 2008
@@ -597,7 +597,7 @@
/* find which connection to use */
prop = xmlGetProp (child, BAD_CAST "cnc_name");
- cnc = run_context_find_connection (engine, context, (gchar *) prop);
+ cnc = run_context_find_connection (engine, context, prop);
if (!cnc) {
if (prop) {
g_set_error (error, 0, 0,
Added: branches/V4-branch/libgda/gda-client-private.h
==============================================================================
--- (empty file)
+++ branches/V4-branch/libgda/gda-client-private.h Wed Feb 20 20:36:04 2008
@@ -0,0 +1,37 @@
+/* GDA library
+ * 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.
+ */
+
+#ifndef __GDA_CLIENT_PRIVATE_H__
+#define __GDA_CLIENT_PRIVATE_H__
+
+#include <libgda/gda-client.h>
+
+G_BEGIN_DECLS
+
+void _gda_client_notify_event (GdaClient *client, GdaConnection *cnc, GdaClientEvent event, GdaSet *params);
+void _gda_client_notify_error_event (GdaClient *client, GdaConnection *cnc, GdaConnectionEvent *error);
+void _gda_client_notify_connection_opened_event (GdaClient *client, GdaConnection *cnc);
+void _gda_client_notify_connection_closed_event (GdaClient *client, GdaConnection *cnc);
+
+G_END_DECLS
+
+#endif
Modified: branches/V4-branch/libgda/gda-client.c
==============================================================================
--- branches/V4-branch/libgda/gda-client.c (original)
+++ branches/V4-branch/libgda/gda-client.c Wed Feb 20 20:36:04 2008
@@ -24,6 +24,7 @@
#include <gmodule.h>
#include <libgda/gda-client.h>
+#include <libgda/gda-client-private.h>
#include <libgda/gda-config.h>
#include <glib/gi18n-lib.h>
#include <libgda/gda-log.h>
@@ -44,6 +45,7 @@
static void gda_client_finalize (GObject *object);
static void cnc_error_cb (GdaConnection *cnc, GdaConnectionEvent *error, GdaClient *client);
+static GdaConnection *gda_client_find_connection (GdaClient *client, const gchar *dsn, const gchar *auth_string);
enum {
EVENT_NOTIFICATION,
@@ -63,7 +65,7 @@
g_return_if_fail (GDA_IS_CLIENT (client));
/* notify error */
- gda_client_notify_error_event (client, cnc, error);
+ _gda_client_notify_error_event (client, cnc, error);
}
/*
@@ -186,8 +188,10 @@
*
* The @auth_string must contain the authentification information for the server
* to accept the connection. It is a string containing semi-colon seperated named value, usually
- * like "USERNAME=...;PASSWORD=..." where the ... are replaced by actual values. However some database
- * providers may require different information.
+ * like "USERNAME=...;PASSWORD=..." where the ... are replaced by actual values.
+ * The actual named parameters required depend on the provider being used, and that list is available
+ * as the <parameter>auth_params</parameter> member of the #GdaProviderInfo struncture for each installed
+ * provider (use gda_config_get_provider_info() to get it).
*
* If a new #GdaConnection is created, then the caller will hold a reference on it, and if a #GdaConnection
* already existing is used, then the reference count of that object will be increased by one.
@@ -195,11 +199,8 @@
* Returns: the opened connection if successful, %NULL if there was an error.
*/
GdaConnection *
-gda_client_open_connection (GdaClient *client,
- const gchar *dsn,
- const gchar *auth_string,
- GdaConnectionOptions options,
- GError **error)
+gda_client_open_connection (GdaClient *client, const gchar *dsn, const gchar *auth_string,
+ GdaConnectionOptions options, GError **error)
{
GdaConnection *cnc = NULL;
GdaDataSourceInfo *dsn_info;
@@ -210,7 +211,7 @@
dsn_info = gda_config_get_dsn (dsn);
if (!dsn_info) {
gda_log_error (_("Data source %s not found in configuration"), dsn);
- g_set_error (error, GDA_CLIENT_ERROR, 0,
+ g_set_error (error, GDA_CLIENT_ERROR, GDA_CLIENT_DSN_NOT_FOUND_ERROR,
_("Data source %s not found in configuration"), dsn);
return NULL;
}
@@ -244,7 +245,7 @@
}
else {
g_warning (_("Datasource configuration error: no provider specified"));
- g_set_error (error, GDA_CLIENT_ERROR, 0,
+ g_set_error (error, GDA_CLIENT_ERROR, GDA_CLIENT_PROVIDER_NOT_FOUND_ERROR,
_("Datasource configuration error: no provider specified"));
}
@@ -284,8 +285,10 @@
*
* The @auth_string must contain the authentification information for the server
* to accept the connection. It is a string containing semi-colon seperated named value, usually
- * like "USERNAME=...;PASSWORD=..." where the ... are replaced by actual values. However some database
- * providers may require different information.
+ * like "USERNAME=...;PASSWORD=..." where the ... are replaced by actual values.
+ * The actual named parameters required depend on the provider being used, and that list is available
+ * as the <parameter>auth_params</parameter> member of the #GdaProviderInfo struncture for each installed
+ * provider (use gda_config_get_provider_info() to get it).
*
* Additionnally, it is possible to have the connection string
* respect the "<provider_name>://<real cnc string>" format, in which case the provider name
@@ -296,11 +299,8 @@
* an error.
*/
GdaConnection *
-gda_client_open_connection_from_string (GdaClient *client,
- const gchar *provider_id,
- const gchar *cnc_string,
- const gchar *auth_string,
- GdaConnectionOptions options,
+gda_client_open_connection_from_string (GdaClient *client, const gchar *provider_id, const gchar *cnc_string,
+ const gchar *auth_string, GdaConnectionOptions options,
GError **error)
{
GdaConnection *cnc = NULL;
@@ -322,7 +322,8 @@
}
if (!provider_id) {
- g_set_error (error, GDA_CLIENT_ERROR, 0, _("No provider specified"));
+ g_set_error (error, GDA_CLIENT_ERROR, GDA_CLIENT_PROVIDER_NOT_FOUND_ERROR,
+ _("No provider specified"));
g_free (dup);
return NULL;
}
@@ -335,7 +336,7 @@
tmp_prov = gda_connection_get_provider_name (cnc);
tmp_cnc_string = gda_connection_get_cnc_string (cnc);
- if (strcmp (provider_id, tmp_prov) == 0
+ if (g_ascii_strcasecmp (provider_id, tmp_prov) == 0
&& (! strcmp (cnc_string, tmp_cnc_string))) {
g_free (dup);
return cnc;
@@ -359,7 +360,7 @@
}
else {
g_warning (_("Datasource configuration error: no provider specified"));
- g_set_error (error, GDA_CLIENT_ERROR, 0,
+ g_set_error (error, GDA_CLIENT_ERROR, GDA_CLIENT_PROVIDER_NOT_FOUND_ERROR,
_("Datasource configuration error: no provider specified"));
}
@@ -385,7 +386,7 @@
return (const GList *) client->priv->connections;
}
-/**
+/*
* gda_client_find_connection
* @client: a #GdaClient object.
* @dsn: data source name.
@@ -402,7 +403,7 @@
* Returns: a pointer to the found connection, or %NULL if it could not
* be found.
*/
-GdaConnection *
+static GdaConnection *
gda_client_find_connection (GdaClient *client, const gchar *dsn, const gchar *auth_string)
{
GList *l;
@@ -434,23 +435,7 @@
}
/**
- * gda_client_close_all_connections
- * @client: a #GdaClient object.
- *
- * Closes all connections opened by the given #GdaClient object.
- */
-void
-gda_client_close_all_connections (GdaClient *client)
-{
- g_return_if_fail (GDA_IS_CLIENT (client));
- g_return_if_fail (client->priv);
-
- if (client->priv->connections)
- g_list_foreach (client->priv->connections, (GFunc) gda_connection_close, NULL);
-}
-
-/**
- * gda_client_notify_event
+ * _gda_client_notify_event
* @client: a #GdaClient object.
* @cnc: a #GdaConnection object where the event has occurred.
* @event: event ID.
@@ -461,10 +446,7 @@
* operation, to changes made to a table in an underlying database.
*/
void
-gda_client_notify_event (GdaClient *client,
- GdaConnection *cnc,
- GdaClientEvent event,
- GdaSet *params)
+_gda_client_notify_event (GdaClient *client, GdaConnection *cnc, GdaClientEvent event, GdaSet *params)
{
if (!client)
return;
@@ -475,7 +457,7 @@
}
/**
- * gda_client_notify_error_event
+ * _gda_client_notify_error_event
* @client: a #GdaClient object.
* @cnc: a #GdaConnection object.
* @error: the error to be notified.
@@ -483,7 +465,7 @@
* Notifies the given #GdaClient of the #GDA_CLIENT_EVENT_ERROR event.
*/
void
-gda_client_notify_error_event (GdaClient *client, GdaConnection *cnc, GdaConnectionEvent *error)
+_gda_client_notify_error_event (GdaClient *client, GdaConnection *cnc, GdaConnectionEvent *error)
{
GdaSet *params;
GdaHolder *param;
@@ -504,12 +486,12 @@
params = gda_set_new (NULL);
gda_set_add_holder (params, param);
g_object_unref (param);
- gda_client_notify_event (client, cnc, GDA_CLIENT_EVENT_ERROR, params);
+ _gda_client_notify_event (client, cnc, GDA_CLIENT_EVENT_ERROR, params);
g_object_unref (params);
}
/**
- * gda_client_notify_connection_opened_event
+ * _gda_client_notify_connection_opened_event
* @client: a #GdaClient object.
* @cnc: a #GdaConnection object.
*
@@ -517,7 +499,7 @@
* event.
*/
void
-gda_client_notify_connection_opened_event (GdaClient *client, GdaConnection *cnc)
+_gda_client_notify_connection_opened_event (GdaClient *client, GdaConnection *cnc)
{
if (!client)
return;
@@ -525,11 +507,11 @@
g_return_if_fail (GDA_IS_CLIENT (client));
g_return_if_fail (GDA_IS_CONNECTION (cnc));
- gda_client_notify_event (client, cnc, GDA_CLIENT_EVENT_CONNECTION_OPENED, NULL);
+ _gda_client_notify_event (client, cnc, GDA_CLIENT_EVENT_CONNECTION_OPENED, NULL);
}
/**
- * gda_client_notify_connection_closed_event
+ * _gda_client_notify_connection_closed_event
* @client: a #GdaClient object.
* @cnc: a #GdaConnection object.
*
@@ -537,7 +519,7 @@
* event.
*/
void
-gda_client_notify_connection_closed_event (GdaClient *client, GdaConnection *cnc)
+_gda_client_notify_connection_closed_event (GdaClient *client, GdaConnection *cnc)
{
if (!client)
return;
@@ -545,113 +527,7 @@
g_return_if_fail (GDA_IS_CLIENT (client));
g_return_if_fail (GDA_IS_CONNECTION (cnc));
- gda_client_notify_event (client, cnc, GDA_CLIENT_EVENT_CONNECTION_CLOSED, NULL);
-}
-
-/**
- * gda_client_begin_transaction
- * @client: a #GdaClient object.
- * @name: the name of the transation to start
- * @level:
- * @error: a place to store errors, or %NULL
- *
- * Starts a transaction on all connections being managed by the given
- * #GdaClient. It is important to note that this operates on all
- * connections opened within a #GdaClient, which could not be what
- * you're looking for.
- *
- * To execute a transaction on a unique connection, use
- * #gda_connection_begin_transaction, #gda_connection_commit_transaction
- * and #gda_connection_rollback_transaction.
- *
- * Returns: %TRUE if all transactions could be started successfully,
- * or %FALSE if one of them fails.
- */
-gboolean
-gda_client_begin_transaction (GdaClient *client, const gchar *name, GdaTransactionIsolation level,
- GError **error)
-{
- GList *l;
-
- g_return_val_if_fail (GDA_IS_CLIENT (client), FALSE);
-
- for (l = client->priv->connections; l != NULL; l = l->next) {
- if (!gda_connection_begin_transaction (GDA_CONNECTION (l->data), name, level, error)) {
- gda_client_rollback_transaction (client, name, NULL);
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-/**
- * gda_client_commit_transaction
- * @client: a #GdaClient object.
- * @name: the name of the transation to commit
- * @error: a place to store errors, or %NULL
- *
- * Commits a running transaction on all connections being managed by the given
- * #GdaClient. It is important to note that this operates on all
- * connections opened within a #GdaClient, which could not be what
- * you're looking for.
- *
- * To execute a transaction on a unique connection, use
- * #gda_connection_begin_transaction, #gda_connection_commit_transaction
- * and #gda_connection_rollback_transaction.
- *
- * Returns: %TRUE if all transactions could be committed successfully,
- * or %FALSE if one of them fails.
- */
-gboolean
-gda_client_commit_transaction (GdaClient *client, const gchar *name, GError **error)
-{
- GList *l;
-
- g_return_val_if_fail (GDA_IS_CLIENT (client), FALSE);
-
- for (l = client->priv->connections; l != NULL; l = l->next) {
- if (!gda_connection_commit_transaction (GDA_CONNECTION (l->data), name, error)) {
- gda_client_rollback_transaction (client, name, NULL);
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-/**
- * gda_client_rollback_transaction
- * @client: a #GdaClient object.
- * @name: the name of the transation to rollback
- * @error: a place to store errors, or %NULL
- *
- * Cancels a running transaction on all connections being managed by the given
- * #GdaClient. It is important to note that this operates on all
- * connections opened within a #GdaClient, which could not be what
- * you're looking for.
- *
- * To execute a transaction on a unique connection, use
- * #gda_connection_begin_transaction, #gda_connection_commit_transaction
- * and #gda_connection_rollback_transaction.
- *
- * Returns: %TRUE if all transactions could be cancelled successfully,
- * or %FALSE if one of them fails.
- */
-gboolean
-gda_client_rollback_transaction (GdaClient *client, const gchar *name, GError **error)
-{
- GList *l;
- gint failures = 0;
-
- g_return_val_if_fail (GDA_IS_CLIENT (client), FALSE);
-
- for (l = client->priv->connections; l != NULL; l = l->next) {
- if (!gda_connection_rollback_transaction (GDA_CONNECTION (l->data), name, error))
- failures++;
- }
-
- return failures == 0 ? TRUE : FALSE;
+ _gda_client_notify_event (client, cnc, GDA_CLIENT_EVENT_CONNECTION_CLOSED, NULL);
}
/**
@@ -765,7 +641,7 @@
if (provider)
return gda_server_provider_perform_operation (provider, NULL, op, error);
else {
- g_set_error (error, GDA_CLIENT_ERROR, 0,
+ g_set_error (error, GDA_CLIENT_ERROR, GDA_CLIENT_PROVIDER_NOT_FOUND_ERROR,
_("Could not find operation's associated provider, "
"did you use gda_client_prepare_create_database() ?"));
return FALSE;
@@ -795,7 +671,7 @@
if (provider)
return gda_server_provider_perform_operation (provider, NULL, op, error);
else {
- g_set_error (error, GDA_CLIENT_ERROR, GDA_CLIENT_GENERAL_ERROR,
+ g_set_error (error, GDA_CLIENT_ERROR, GDA_CLIENT_PROVIDER_NOT_FOUND_ERROR,
_("Could not find operation's associated provider, "
"did you use gda_client_prepare_drop_database() ?"));
return FALSE;
Modified: branches/V4-branch/libgda/gda-client.h
==============================================================================
--- branches/V4-branch/libgda/gda-client.h (original)
+++ branches/V4-branch/libgda/gda-client.h Wed Feb 20 20:36:04 2008
@@ -60,10 +60,7 @@
GObjectClass parent_class;
/* signals */
- void (* event_notification) (GdaClient *client,
- GdaConnection *cnc,
- GdaClientEvent event,
- GdaSet *params);
+ void (* event_notification) (GdaClient *client, GdaConnection *cnc, GdaClientEvent event, GdaSet *params);
};
/* error reporting */
@@ -72,53 +69,29 @@
typedef enum
{
- GDA_CLIENT_GENERAL_ERROR
+ GDA_CLIENT_DSN_NOT_FOUND_ERROR,
+ GDA_CLIENT_PROVIDER_NOT_FOUND_ERROR
} GdaClientError;
-GType gda_client_get_type (void) G_GNUC_CONST;
-GdaClient *gda_client_new (void);
+GType gda_client_get_type (void) G_GNUC_CONST;
+GdaClient *gda_client_new (void);
-GdaConnection *gda_client_open_connection (GdaClient *client,
- const gchar *dsn,
- const gchar *auth_string,
- GdaConnectionOptions options,
- GError **error);
-GdaConnection *gda_client_open_connection_from_string (GdaClient *client,
- const gchar *provider_id,
- const gchar *cnc_string,
- const gchar *auth_string,
- GdaConnectionOptions options,
- GError **error);
-const GList *gda_client_get_connections (GdaClient *client);
-GdaConnection *gda_client_find_connection (GdaClient *client,
- const gchar *dsn,
- const gchar *auth_string);
-void gda_client_close_all_connections (GdaClient *client);
-
-void gda_client_notify_event (GdaClient *client, GdaConnection *cnc,
- GdaClientEvent event, GdaSet *params);
-void gda_client_notify_error_event (GdaClient *client, GdaConnection *cnc, GdaConnectionEvent *error);
-void gda_client_notify_connection_opened_event (GdaClient *client, GdaConnection *cnc);
-void gda_client_notify_connection_closed_event (GdaClient *client, GdaConnection *cnc);
+GdaConnection *gda_client_open_connection (GdaClient *client, const gchar *dsn, const gchar *auth_string,
+ GdaConnectionOptions options, GError **error);
+GdaConnection *gda_client_open_connection_from_string (GdaClient *client, const gchar *provider_id,
+ const gchar *cnc_string, const gchar *auth_string,
+ GdaConnectionOptions options, GError **error);
+const GList *gda_client_get_connections (GdaClient *client);
/*
* Database creation and destruction functions
*/
-GdaServerOperation *gda_client_prepare_create_database (GdaClient *client, const gchar *db_name,
- const gchar *provider);
-gboolean gda_client_perform_create_database (GdaClient *client, GdaServerOperation *op, GError **error);
-GdaServerOperation *gda_client_prepare_drop_database (GdaClient *client, const gchar *db_name,
- const gchar *provider);
-gboolean gda_client_perform_drop_database (GdaClient *client, GdaServerOperation *op, GError **error);
+GdaServerOperation *gda_client_prepare_create_database (GdaClient *client, const gchar *db_name, const gchar *provider);
+gboolean gda_client_perform_create_database (GdaClient *client, GdaServerOperation *op, GError **error);
+GdaServerOperation *gda_client_prepare_drop_database (GdaClient *client, const gchar *db_name, const gchar *provider);
+gboolean gda_client_perform_drop_database (GdaClient *client, GdaServerOperation *op, GError **error);
-/*
- * Connection stack functions
- */
-gboolean gda_client_begin_transaction (GdaClient *client, const gchar *name, GdaTransactionIsolation level,
- GError **error);
-gboolean gda_client_commit_transaction (GdaClient *client, const gchar *name, GError **error);
-gboolean gda_client_rollback_transaction (GdaClient *client, const gchar *name, GError **error);
Modified: branches/V4-branch/libgda/gda-config.c
==============================================================================
--- branches/V4-branch/libgda/gda-config.c (original)
+++ branches/V4-branch/libgda/gda-config.c Wed Feb 20 20:36:04 2008
@@ -916,7 +916,7 @@
load_all_providers ();
for (list = unique_instance->priv->prov_list; list; list = list->next)
- if (!strcmp (((GdaProviderInfo*) list->data)->id, provider_name)) {
+ if (!g_ascii_strcasecmp (((GdaProviderInfo*) list->data)->id, provider_name)) {
GDA_CONFIG_UNLOCK ();
return (GdaProviderInfo*) list->data;
}
Modified: branches/V4-branch/libgda/gda-connection.c
==============================================================================
--- branches/V4-branch/libgda/gda-connection.c (original)
+++ branches/V4-branch/libgda/gda-connection.c Wed Feb 20 20:36:04 2008
@@ -26,6 +26,7 @@
#undef GDA_DISABLE_DEPRECATED
#include <stdio.h>
#include <libgda/gda-client.h>
+#include <libgda/gda-client-private.h>
#include <libgda/gda-config.h>
#include <libgda/gda-connection.h>
#include <libgda/gda-connection-private.h>
@@ -408,40 +409,6 @@
}
}
-
-/**
- * gda_connection_new
- * @client: a #GdaClient object, or %NULL
- * @provider: a #GdaServerProvider object.
- * @dsn: GDA data source to connect to.
- * @auth_string: authentification string.
- * @options: options for the connection.
- *
- * This function creates a new #GdaConnection object. It is not
- * intended to be used directly by applications (use
- * #gda_client_open_connection instead).
- *
- * The connection is not opened at this stage; use
- * gda_connection_open() to open the connection.
- *
- * Returns: a newly allocated #GdaConnection object.
- */
-GdaConnection *
-gda_connection_new (GdaClient *client, GdaServerProvider *provider,
- const gchar *dsn, const gchar *auth_string,
- GdaConnectionOptions options)
-{
- GdaConnection *cnc;
-
- g_return_val_if_fail (!client || GDA_IS_CLIENT (client), NULL);
- g_return_val_if_fail (GDA_IS_SERVER_PROVIDER (provider), NULL);
-
- cnc = g_object_new (GDA_TYPE_CONNECTION, "client", client, "provider_obj", provider,
- "dsn", dsn, "auth_string", auth_string,
- "options", options, NULL);
- return cnc;
-}
-
/**
* gda_connection_open
* @cnc: a #GdaConnection object
@@ -505,24 +472,16 @@
else {
if (dsn_info && dsn_info->auth_string)
real_auth_string = g_strdup (dsn_info->auth_string);
- else {
- TO_IMPLEMENT; /* look into @params */
- /*
- const gchar *s;
- s = gda_quark_list_find (params, "USER");
- if (s) {
- real_username = g_strdup (s);
- gda_quark_list_remove (params, "USER");
- }
- */
- }
+ else
+ /* look for authentification parameters in cnc string */
+ real_auth_string = g_strdup (cnc->priv->cnc_string);
}
/* try to open the connection */
- auth = gda_quark_list_new_from_string (cnc->priv->cnc_string);
+ auth = gda_quark_list_new_from_string (real_auth_string);
if (gda_server_provider_open_connection (cnc->priv->provider_obj, cnc, params, auth)) {
cnc->priv->is_open = TRUE;
- gda_client_notify_connection_opened_event (cnc->priv->client, cnc);
+ _gda_client_notify_connection_opened_event (cnc->priv->client, cnc);
}
else {
const GList *events;
@@ -539,8 +498,8 @@
if (error && !(*error))
g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_OPEN_ERROR,
gda_connection_event_get_description (event));
- gda_client_notify_error_event (cnc->priv->client, cnc,
- GDA_CONNECTION_EVENT (l->data));
+ _gda_client_notify_error_event (cnc->priv->client, cnc,
+ GDA_CONNECTION_EVENT (l->data));
}
}
}
@@ -610,7 +569,7 @@
return;
gda_server_provider_close_connection (cnc->priv->provider_obj, cnc);
- gda_client_notify_connection_closed_event (cnc->priv->client, cnc);
+ _gda_client_notify_connection_closed_event (cnc->priv->client, cnc);
cnc->priv->is_open = FALSE;
if (cnc->priv->provider_data) {
@@ -1135,7 +1094,9 @@
* return object will either be:
* <itemizedlist>
* <listitem><para>a #GdaDataModel if @stmt is a SELECT statement (a GDA_SQL_STATEMENT_SELECT, see #GdaSqlStatementType)
- * containing the results of the SELECT</para></listitem>
+ * containing the results of the SELECT. The resulting data model is by default read only, but
+ * modifications can be made possible using gda_pmodel_set_modification_query() and/or
+ * gda_pmodel_compute_modification_queries().</para></listitem>
* <listitem><para>a #GdaSet for any other SQL statement which correctly executed. In this case
* (if the provider supports it), then the #GdaSet may contain value holders named:
* <itemizedlist>
@@ -1180,9 +1141,9 @@
* This function returns the number of rows affected by the execution of @stmt, or -1
* if an error occurred, or -2 if the connection's provider does not return the number of rows affected.
*
- * This function is just a convenience function around the gda_connection_execute_statement()
+ * This function is just a convenience function around the gda_connection_statement_execute()
* function.
- * See the documentation of the gda_connection_execute_statement() for information
+ * See the documentation of the gda_connection_statement_execute() for information
* about the @params list of parameters.
*
* If @last_insert_row is not %NULL and @stmt is an INSERT statement, then it will contain (if the
@@ -1252,10 +1213,10 @@
* This function returns a #GdaDataModel resulting from the SELECT statement, or %NULL
* if an error occurred.
*
- * This function is just a convenience function around the gda_connection_execute_command()
+ * This function is just a convenience function around the gda_connection_statement_execute()
* function.
*
- * See the documentation of the gda_connection_execute_statement() for information
+ * See the documentation of the gda_connection_statement_execute() for information
* about the @params list of parameters.
*
* Returns: a #GdaDataModel containing the data returned by the
@@ -1300,10 +1261,10 @@
* This function returns a #GdaDataModel resulting from the SELECT statement, or %NULL
* if an error occurred.
*
- * This function is just a convenience function around the gda_connection_execute_command()
+ * This function is just a convenience function around the gda_connection_statement_execute()
* function.
*
- * See the documentation of the gda_connection_execute_statement() for information
+ * See the documentation of the gda_connection_statement_execute() for information
* about the @params list of parameters.
*
* Returns: a #GdaDataModel containing the data returned by the
@@ -1357,10 +1318,10 @@
* This function returns a #GdaDataModel resulting from the SELECT statement, or %NULL
* if an error occurred.
*
- * This function is just a convenience function around the gda_connection_execute_command()
+ * This function is just a convenience function around the gda_connection_statement_execute()
* function.
*
- * See the documentation of the gda_connection_execute_statement() for information
+ * See the documentation of the gda_connection_statement_execute() for information
* about the @params list of parameters.
*
* Returns: a #GdaDataModel containing the data returned by the
@@ -1421,7 +1382,7 @@
retval = gda_server_provider_begin_transaction (cnc->priv->provider_obj, cnc, name, level, error);
if (retval)
- gda_client_notify_event (cnc->priv->client, cnc, GDA_CLIENT_EVENT_TRANSACTION_STARTED, NULL);
+ _gda_client_notify_event (cnc->priv->client, cnc, GDA_CLIENT_EVENT_TRANSACTION_STARTED, NULL);
return retval;
}
@@ -1449,7 +1410,7 @@
retval = gda_server_provider_commit_transaction (cnc->priv->provider_obj, cnc, name, error);
if (retval)
- gda_client_notify_event (cnc->priv->client, cnc, GDA_CLIENT_EVENT_TRANSACTION_COMMITTED, NULL);
+ _gda_client_notify_event (cnc->priv->client, cnc, GDA_CLIENT_EVENT_TRANSACTION_COMMITTED, NULL);
return retval;
}
@@ -1478,7 +1439,7 @@
retval = gda_server_provider_rollback_transaction (cnc->priv->provider_obj, cnc, name, error);
if (retval)
- gda_client_notify_event (cnc->priv->client, cnc, GDA_CLIENT_EVENT_TRANSACTION_CANCELLED, NULL);
+ _gda_client_notify_event (cnc->priv->client, cnc, GDA_CLIENT_EVENT_TRANSACTION_CANCELLED, NULL);
return retval;
}
Modified: branches/V4-branch/libgda/gda-connection.h
==============================================================================
--- branches/V4-branch/libgda/gda-connection.h (original)
+++ branches/V4-branch/libgda/gda-connection.h Wed Feb 20 20:36:04 2008
@@ -128,9 +128,6 @@
GType gda_connection_get_type (void) G_GNUC_CONST;
-GdaConnection *gda_connection_new (GdaClient *client, GdaServerProvider *provider,
- const gchar *dsn, const gchar *auth_string,
- GdaConnectionOptions options);
gboolean gda_connection_open (GdaConnection *cnc, GError **error);
void gda_connection_close (GdaConnection *cnc);
void gda_connection_close_no_warning (GdaConnection *cnc);
Modified: branches/V4-branch/libgda/gda-data-proxy.c
==============================================================================
--- branches/V4-branch/libgda/gda-data-proxy.c (original)
+++ branches/V4-branch/libgda/gda-data-proxy.c Wed Feb 20 20:36:04 2008
@@ -3178,7 +3178,6 @@
GError **error)
{
GdaDataProxy *proxy;
- GdaValueAttribute att = 0;
g_return_val_if_fail (GDA_IS_DATA_PROXY (model), FALSE);
proxy = GDA_DATA_PROXY (model);
@@ -3188,13 +3187,6 @@
/* ensure that there is no sync to be done */
ensure_chunk_sync (proxy);
- att = gda_data_proxy_get_value_attributes (proxy, proxy_row, col);
- if (att & GDA_VALUE_ATTR_NO_MODIF) {
- g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_READ_ONLY_VALUE,
- _("Value is read-only"));
- return FALSE;
- }
-
if ((proxy_row == 0) && proxy->priv->add_null_entry) {
g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_READ_ONLY_ROW,
_("The first row is an empty row artificially prepended and cannot be altered"));
Modified: branches/V4-branch/libgda/gda-server-provider.c
==============================================================================
--- branches/V4-branch/libgda/gda-server-provider.c (original)
+++ branches/V4-branch/libgda/gda-server-provider.c Wed Feb 20 20:36:04 2008
@@ -306,8 +306,10 @@
g_object_set (G_OBJECT (cnc), "auth_string", auth_string, "options", options, NULL);
}
}
- else
- cnc = gda_connection_new (client, provider, dsn, auth_string, options);
+ else
+ cnc = g_object_new (GDA_TYPE_CONNECTION, "client", client, "provider_obj", provider,
+ "dsn", dsn, "auth_string", auth_string,
+ "options", options, NULL);
return cnc;
}
Modified: branches/V4-branch/libgda/gda-server-provider.h
==============================================================================
--- branches/V4-branch/libgda/gda-server-provider.h (original)
+++ branches/V4-branch/libgda/gda-server-provider.h Wed Feb 20 20:36:04 2008
@@ -54,7 +54,8 @@
GDA_SERVER_PROVIDER_STATEMENT_EXEC_ERROR,
GDA_SERVER_PROVIDER_OPERATION_ERROR,
GDA_SERVER_PROVIDER_INTERNAL_ERROR,
- GDA_SERVER_PROVIDER_BUSY_ERROR
+ GDA_SERVER_PROVIDER_BUSY_ERROR,
+ GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR
} GdaServerProviderError;
struct _GdaServerProvider {
@@ -150,6 +151,14 @@
gboolean (* cancel) (GdaServerProvider *provider, GdaConnection *cnc, guint task_id);
GdaConnection *(* create_connection) (GdaServerProvider *provider);
GdaServerProviderMeta meta_funcs;
+
+ /* Padding for future expansion */
+ void (*_gda_reserved1) (void);
+ void (*_gda_reserved2) (void);
+ void (*_gda_reserved3) (void);
+ void (*_gda_reserved4) (void);
+ void (*_gda_reserved5) (void);
+ void (*_gda_reserved6) (void);
};
GType gda_server_provider_get_type (void) G_GNUC_CONST;
Modified: branches/V4-branch/libgda/providers-support/gda-pmodel.c
==============================================================================
--- branches/V4-branch/libgda/providers-support/gda-pmodel.c (original)
+++ branches/V4-branch/libgda/providers-support/gda-pmodel.c Wed Feb 20 20:36:04 2008
@@ -55,7 +55,11 @@
PROP_0,
PROP_PREP_STMT,
PROP_MOD_STMT,
- PROP_FLAGS
+ PROP_FLAGS,
+ PROP_ALL_STORED,
+ PROP_INS_QUERY,
+ PROP_UPD_QUERY,
+ PROP_DEL_QUERY
};
static void gda_pmodel_class_init (GdaPModelClass *klass);
@@ -144,6 +148,27 @@
GDA_DATA_MODEL_ACCESS_RANDOM, G_MAXUINT,
GDA_DATA_MODEL_ACCESS_RANDOM,
G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class, PROP_ALL_STORED,
+ g_param_spec_boolean ("store-all-rows", "Store all the rows",
+ "Tells if model has analysed all the rows", FALSE,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+ g_object_class_install_property (object_class, PROP_INS_QUERY,
+ g_param_spec_object ("insert_query", "INSERT query",
+ "INSERT Query to be executed to add data",
+ GDA_TYPE_STATEMENT,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property (object_class, PROP_UPD_QUERY,
+ g_param_spec_object ("update_query", "UPDATE query",
+ "UPDATE Query to be executed to update data",
+ GDA_TYPE_STATEMENT,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property (object_class, PROP_DEL_QUERY,
+ g_param_spec_object ("delete_query", "DELETE query",
+ "DELETE Query to be executed to remove data",
+ GDA_TYPE_STATEMENT,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
/* virtual functions */
object_class->dispose = gda_pmodel_dispose;
@@ -308,6 +333,19 @@
model->priv->usage_flags = flags;
break;
}
+ case PROP_ALL_STORED:
+ if ((model->advertized_nrows < 0) && CLASS (model)->fetch_nb_rows)
+ CLASS (model)->fetch_nb_rows (model);
+
+ if (model->nb_stored_rows != model->advertized_nrows) {
+ if (CLASS (model)->store_all)
+ CLASS (model)->store_all (model, NULL);
+ }
+ break;
+ case PROP_INS_QUERY:
+ case PROP_DEL_QUERY:
+ case PROP_UPD_QUERY:
+ TO_IMPLEMENT;
default:
break;
}
@@ -332,6 +370,20 @@
case PROP_FLAGS:
g_value_set_uint (value, model->priv->usage_flags);
break;
+ case PROP_ALL_STORED:
+ if (!model->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
+ g_warning ("Cannot set the 'store-all-rows' property when acces mode is cursor based");
+ else {
+ if ((model->advertized_nrows < 0) && CLASS (model)->fetch_nb_rows)
+ CLASS (model)->fetch_nb_rows (model);
+ g_value_set_boolean (value, model->nb_stored_rows == model->advertized_nrows);
+ }
+ break;
+ case PROP_INS_QUERY:
+ case PROP_DEL_QUERY:
+ case PROP_UPD_QUERY:
+ TO_IMPLEMENT;
+
default:
break;
}
@@ -344,7 +396,8 @@
* @row: a #GdaPRow row
* @rownum: "external" advertized row number
*
- * Stores @row into @model, externally advertized at row number @rownum
+ * Stores @row into @model, externally advertized at row number @rownum. The reference to
+ * @row is stolen.
*/
void
gda_pmodel_take_row (GdaPModel *model, GdaPRow *row, gint rownum)
@@ -357,7 +410,7 @@
g_hash_table_insert (model->priv->index, GINT_TO_POINTER (rownum + 1), GINT_TO_POINTER (model->priv->rows->len + 1));
g_array_append_val (model->priv->rows, row);
- g_object_ref (row);
+ model->nb_stored_rows = model->priv->rows->len;
}
/**
@@ -383,6 +436,51 @@
return g_array_index (model->priv->rows, GdaPRow *, irow - 1);
}
+/**
+ * gda_pmodel_set_modification_query
+ * @model: a #GdaPModel data model
+ * @mod_stmt: a #GdaStatement (INSERT, UPDATE or DELETE)
+ * @error: a place to store errors, or %NULL
+ *
+ * Forces @model to allow data modification using @mod_stmt as the statement executed when the corresponding
+ * modification is requested
+ *
+ * Returns: TRUE if no error occurred.
+ */
+gboolean
+gda_pmodel_set_modification_query (GdaPModel *model, GdaStatement *mod_stmt, GError **error)
+{
+ g_return_val_if_fail (GDA_IS_PMODEL (model), FALSE);
+ g_return_val_if_fail (model->priv, FALSE);
+ g_return_val_if_fail (GDA_IS_STATEMENT (mod_stmt), FALSE);
+
+ TO_IMPLEMENT;
+
+ return FALSE;
+}
+
+/**
+ * gda_pmodel_compute_modification_queries
+ * @model: a #GdaPModel data model
+ * @target: the name of the target to modify (a table name or alias)
+ * @use_all_fields_if_no_pk: set to TRUE if all fields must be used in the WHERE condition when no primary key exists
+ * @error: a place to store errors, or %NULL
+ *
+ * Makes @model try to compute INSERT, UPDATE and DELETE statements to be used when modifying @model's contents
+ *
+ * Returns: TRUE if no error occurred.
+ */
+gboolean
+gda_pmodel_compute_modification_queries (GdaPModel *model, const gchar *target, gboolean use_all_fields_if_no_pk, GError **error)
+{
+ g_return_val_if_fail (GDA_IS_PMODEL (model), FALSE);
+ g_return_val_if_fail (model->priv, FALSE);
+
+ TO_IMPLEMENT;
+
+ return FALSE;
+}
+
/*
* GdaDataModel interface implementation
*/
@@ -449,27 +547,34 @@
gda_pmodel_get_value_at (GdaDataModel *model, gint col, gint row)
{
GdaPRow *prow;
- gint irow;
+ gint irow, nrows;
GdaPModel *imodel;
g_return_val_if_fail (GDA_IS_PMODEL (model), NULL);
imodel = (GdaPModel *) model;
- g_return_val_if_fail (imodel->priv, 0);
+ g_return_val_if_fail (imodel->priv, NULL);
/* available only if GDA_DATA_MODEL_ACCESS_RANDOM */
if (! (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM))
return NULL;
+ /* check row number validity */
+ nrows = imodel->advertized_nrows < 0 ? gda_pmodel_get_n_rows (model) : imodel->advertized_nrows;
+ if ((row < 0) || ((nrows >= 0) && (row >= nrows)))
+ return NULL;
+
irow = GPOINTER_TO_INT (g_hash_table_lookup (imodel->priv->index, GINT_TO_POINTER (row + 1)));
if (irow <= 0) {
- if (CLASS (model)->fetch_random)
- prow = CLASS (model)->fetch_random (imodel, row, NULL);
+ prow = NULL;
+ if (CLASS (model)->fetch_random)
+ CLASS (model)->fetch_random (imodel, &prow, row, NULL);
}
else
prow = g_array_index (imodel->priv->rows, GdaPRow *, irow - 1);
if (prow)
return gda_prow_get_value (prow, col);
+
return NULL;
}
@@ -520,10 +625,12 @@
GdaPModel *imodel;
GdaPRow *prow = NULL;
gint target_iter_row;
+ gint irow;
- g_return_val_if_fail (GDA_IS_PMODEL (model), 0);
+ g_return_val_if_fail (GDA_IS_PMODEL (model), FALSE);
imodel = (GdaPModel *) model;
- g_return_val_if_fail (imodel->priv, 0);
+ g_return_val_if_fail (imodel->priv, FALSE);
+ g_return_val_if_fail (CLASS (model)->fetch_next, FALSE);
if (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
return gda_data_model_move_iter_next_default (model, iter);
@@ -538,10 +645,11 @@
else
target_iter_row = imodel->priv->iter_row + 1;
- if (CLASS (model)->fetch_next)
- prow = CLASS (model)->fetch_next (imodel, target_iter_row, NULL);
- else
- g_error ("INTERNAL error: fetch_next() virtual method not implemented, aborting");
+ irow = GPOINTER_TO_INT (g_hash_table_lookup (imodel->priv->index, GINT_TO_POINTER (target_iter_row + 1)));
+ if (irow > 0)
+ prow = g_array_index (imodel->priv->rows, GdaPRow *, irow - 1);
+ if (!CLASS (model)->fetch_next (imodel, &prow, target_iter_row, NULL))
+ TO_IMPLEMENT;
if (prow) {
imodel->priv->iter_row = target_iter_row;
@@ -562,10 +670,12 @@
GdaPModel *imodel;
GdaPRow *prow = NULL;
gint target_iter_row;
+ gint irow;
- g_return_val_if_fail (GDA_IS_PMODEL (model), 0);
+ g_return_val_if_fail (GDA_IS_PMODEL (model), FALSE);
imodel = (GdaPModel *) model;
- g_return_val_if_fail (imodel->priv, 0);
+ g_return_val_if_fail (imodel->priv, FALSE);
+ g_return_val_if_fail (CLASS (model)->fetch_prev, FALSE);
if (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
return gda_data_model_move_iter_prev_default (model, iter);
@@ -583,18 +693,17 @@
else
target_iter_row = imodel->priv->iter_row - 1;
- if (CLASS (model)->fetch_prev)
- prow = CLASS (model)->fetch_prev (imodel, target_iter_row, NULL);
- else
- g_error ("INTERNAL error: fetch_prev() virtual method not implemented, aborting");
+ irow = GPOINTER_TO_INT (g_hash_table_lookup (imodel->priv->index, GINT_TO_POINTER (target_iter_row + 1)));
+ if (irow > 0)
+ prow = g_array_index (imodel->priv->rows, GdaPRow *, irow - 1);
+ if (!CLASS (model)->fetch_prev (imodel, &prow, target_iter_row, NULL))
+ TO_IMPLEMENT;
if (prow) {
imodel->priv->iter_row = target_iter_row;
update_iter (imodel, prow);
return TRUE;
}
- else
- goto prev_error;
prev_error:
g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
@@ -607,10 +716,11 @@
{
GdaPModel *imodel;
GdaPRow *prow = NULL;
+ gint irow;
- g_return_val_if_fail (GDA_IS_PMODEL (model), 0);
+ g_return_val_if_fail (GDA_IS_PMODEL (model), FALSE);
imodel = (GdaPModel *) model;
- g_return_val_if_fail (imodel->priv, 0);
+ g_return_val_if_fail (imodel->priv, FALSE);
if (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
return gda_data_model_move_iter_at_row_default (model, iter, row);
@@ -618,8 +728,13 @@
g_return_val_if_fail (iter, FALSE);
g_return_val_if_fail (imodel->priv->iter == iter, FALSE);
+ irow = GPOINTER_TO_INT (g_hash_table_lookup (imodel->priv->index, GINT_TO_POINTER (row + 1)));
+ if (irow > 0)
+ prow = g_array_index (imodel->priv->rows, GdaPRow *, irow - 1);
+
if (CLASS (model)->fetch_at) {
- prow = CLASS (model)->fetch_at (imodel, row, NULL);
+ if (!CLASS (model)->fetch_at (imodel, &prow, row, NULL))
+ TO_IMPLEMENT;
if (prow) {
imodel->priv->iter_row = row;
update_iter (imodel, prow);
@@ -632,9 +747,16 @@
}
}
else {
- /* implementation of fetch_at() is optional */
- TO_IMPLEMENT; /* iter back or forward the right number of times */
- return FALSE;
+ if (prow) {
+ imodel->priv->iter_row = row;
+ update_iter (imodel, prow);
+ return TRUE;
+ }
+ else {
+ /* implementation of fetch_at() is optional */
+ TO_IMPLEMENT; /* iter back or forward the right number of times */
+ return FALSE;
+ }
}
}
Modified: branches/V4-branch/libgda/providers-support/gda-pmodel.h
==============================================================================
--- branches/V4-branch/libgda/providers-support/gda-pmodel.h (original)
+++ branches/V4-branch/libgda/providers-support/gda-pmodel.h Wed Feb 20 20:36:04 2008
@@ -42,7 +42,9 @@
struct _GdaPModel {
GObject object;
GdaPModelPrivate *priv;
+ /* read only information */
GdaPStmt *prep_stmt; /* use the "prepared-stmt" property to set this */
+ gint nb_stored_rows; /* number of GdaPRow objects currently stored */
gint advertized_nrows; /* set when the number of rows becomes known */
};
@@ -63,17 +65,22 @@
/* GDA_DATA_MODEL_ACCESS_RANDOM */
gint (*fetch_nb_rows) (GdaPModel *model);
- GdaPRow *(*fetch_random) (GdaPModel *model, gint rownum, GError **error);
+ gboolean (*fetch_random) (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error);
+ gboolean (*store_all) (GdaPModel *model, GError **error);
/* GDA_STATEMENT_MODEL_CURSOR_* */
- GdaPRow *(*fetch_next) (GdaPModel *model, gint rownum, GError **error);
- GdaPRow *(*fetch_prev) (GdaPModel *model, gint rownum, GError **error);
- GdaPRow *(*fetch_at) (GdaPModel *model, gint rownum, GError **error);
+ gboolean (*fetch_next) (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error);
+ gboolean (*fetch_prev) (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error);
+ gboolean (*fetch_at) (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error);
};
-GType gda_pmodel_get_type (void) G_GNUC_CONST;
-void gda_pmodel_take_row (GdaPModel *model, GdaPRow *row, gint rownum);
-GdaPRow *gda_pmodel_get_stored_row (GdaPModel *model, gint rownum);
+GType gda_pmodel_get_type (void) G_GNUC_CONST;
+void gda_pmodel_take_row (GdaPModel *model, GdaPRow *row, gint rownum);
+GdaPRow *gda_pmodel_get_stored_row (GdaPModel *model, gint rownum);
+
+gboolean gda_pmodel_set_modification_query (GdaPModel *model, GdaStatement *mod_stmt, GError **error);
+gboolean gda_pmodel_compute_modification_queries (GdaPModel *model, const gchar *target,
+ gboolean use_all_fields_if_no_pk, GError **error);
G_END_DECLS
Modified: branches/V4-branch/libgda/providers-support/gda-pstmt.c
==============================================================================
--- branches/V4-branch/libgda/providers-support/gda-pstmt.c (original)
+++ branches/V4-branch/libgda/providers-support/gda-pstmt.c Wed Feb 20 20:36:04 2008
@@ -27,7 +27,8 @@
static void gda_pstmt_class_init (GdaPStmtClass *klass);
static void gda_pstmt_init (GdaPStmt *pstmt, GdaPStmtClass *klass);
-static void gda_pstmt_finalize (GObject *object);
+static void gda_pstmt_dispose (GObject *object);
+static void gda_pstmt_finalize (GObject *object);
static GObjectClass *parent_class = NULL;
@@ -66,6 +67,7 @@
parent_class = g_type_class_peek_parent (klass);
/* virtual functions */
+ object_class->dispose = gda_pstmt_dispose;
object_class->finalize = gda_pstmt_finalize;
}
@@ -81,11 +83,23 @@
}
static void
-gda_pstmt_finalize (GObject *object)
+gda_pstmt_dispose (GObject *object)
{
GdaPStmt *pstmt = (GdaPStmt *) object;
- g_return_if_fail (GDA_IS_PSTMT (pstmt));
+ if (pstmt->stmt) {
+ g_object_remove_weak_pointer ((GObject*) pstmt->stmt, (gpointer*) &(pstmt->stmt));
+ pstmt->stmt = NULL;
+ }
+
+ /* chain to parent class */
+ parent_class->dispose (object);
+}
+
+static void
+gda_pstmt_finalize (GObject *object)
+{
+ GdaPStmt *pstmt = (GdaPStmt *) object;
/* free memory */
if (pstmt->sql) {
@@ -110,7 +124,33 @@
parent_class->finalize (object);
}
-/*
+/**
+ * gda_pstmt_set_gda_statement
+ * @pstmt: a #GdaPStmt object
+ * @stmt: a #GdaStatement object
+ *
+ * Informs @pstmt that it corresponds to the preparation of the @stmt statement
+ */
+void
+gda_pstmt_set_gda_statement (GdaPStmt *pstmt, GdaStatement *stmt)
+{
+ g_return_if_fail (GDA_IS_PSTMT (pstmt));
+ g_return_if_fail (!stmt || GDA_IS_STATEMENT (stmt));
+
+ if (pstmt->stmt == stmt)
+ return;
+ if (pstmt->stmt)
+ g_object_unref (pstmt->stmt);
+ pstmt->stmt = stmt;
+ if (stmt)
+ g_object_add_weak_pointer ((GObject*) stmt, (gpointer*) &(pstmt->stmt));
+}
+
+/**
+ * gda_pstmt_copy_contents
+ * @src: a #GdaPStmt object
+ * @dest: a #GdaPStmt object
+ *
* Copies @src's data to @dest
*/
void
Modified: branches/V4-branch/libgda/providers-support/gda-pstmt.h
==============================================================================
--- branches/V4-branch/libgda/providers-support/gda-pstmt.h (original)
+++ branches/V4-branch/libgda/providers-support/gda-pstmt.h Wed Feb 20 20:36:04 2008
@@ -24,6 +24,7 @@
#define __GDA_PSTMT_H__
#include <glib-object.h>
+#include <libgda/gda-statement.h>
G_BEGIN_DECLS
@@ -39,6 +40,7 @@
struct _GdaPStmt {
GObject object;
+ GdaStatement *stmt; /* GdaPStmt object holds a reference on this stmt object, may be NULL */
gchar *sql; /* actual SQL code used for this prepared statement, mem freed by GdaPStmt */
GSList *param_ids; /* list of parameters' IDs (as gchar *), mem freed by GdaPStmt */
@@ -53,8 +55,9 @@
GObjectClass parent_class;
};
-GType gda_pstmt_get_type (void) G_GNUC_CONST;
-void gda_pstmt_copy_contents (GdaPStmt *src, GdaPStmt *dest);
+GType gda_pstmt_get_type (void) G_GNUC_CONST;
+void gda_pstmt_set_gda_statement (GdaPStmt *pstmt, GdaStatement *stmt);
+void gda_pstmt_copy_contents (GdaPStmt *src, GdaPStmt *dest);
G_END_DECLS
Modified: branches/V4-branch/libgda/sqlite/gda-sqlite-provider.c
==============================================================================
--- branches/V4-branch/libgda/sqlite/gda-sqlite-provider.c (original)
+++ branches/V4-branch/libgda/sqlite/gda-sqlite-provider.c Wed Feb 20 20:36:04 2008
@@ -1481,6 +1481,7 @@
/* create a prepared statement */
ps = gda_sqlite_pstmt_new (sqlite_stmt);
+ gda_pstmt_set_gda_statement (_GDA_PSTMT (ps), stmt);
_GDA_PSTMT (ps)->param_ids = param_ids;
_GDA_PSTMT (ps)->sql = sql;
return ps;
Modified: branches/V4-branch/libgda/sqlite/gda-sqlite-recordset.c
==============================================================================
--- branches/V4-branch/libgda/sqlite/gda-sqlite-recordset.c (original)
+++ branches/V4-branch/libgda/sqlite/gda-sqlite-recordset.c Wed Feb 20 20:36:04 2008
@@ -40,9 +40,9 @@
/* virtual methods */
static gint gda_sqlite_recordset_fetch_nb_rows (GdaPModel *model);
-static GdaPRow *gda_sqlite_recordset_fetch_random (GdaPModel *model, gint rownum, GError **error);
+static gboolean gda_sqlite_recordset_fetch_random (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error);
-static GdaPRow *gda_sqlite_recordset_fetch_next (GdaPModel *model, gint rownum, GError **error);
+static gboolean gda_sqlite_recordset_fetch_next (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error);
static GdaPRow *fetch_next_sqlite_row (GdaSqliteRecordset *model, gboolean do_store, GError **error);
@@ -457,16 +457,23 @@
return model->advertized_nrows;
}
-static GdaPRow *
-gda_sqlite_recordset_fetch_random (GdaPModel *model, gint rownum, GError **error)
+/*
+ * Create a new filled #GdaPRow object for the row at position @rownum.
+ *
+ * Each new #GdaPRow created is "given" to the #GdaPModel implementation using gda_pmodel_take_row ().
+ */
+static gboolean
+gda_sqlite_recordset_fetch_random (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error)
{
GdaSqliteRecordset *imodel;
- GdaPRow *prow = NULL;
+
+ if (*prow)
+ return TRUE;
imodel = GDA_SQLITE_RECORDSET (model);
for (; imodel->priv->next_row_num <= rownum; ) {
- prow = fetch_next_sqlite_row (imodel, TRUE, error);
- if (!prow) {
+ *prow = fetch_next_sqlite_row (imodel, TRUE, error);
+ if (!*prow) {
/*if (GDA_PMODEL (model)->advertized_nrows >= 0), it's not an error */
if ((GDA_PMODEL (model)->advertized_nrows >= 0) &&
(imodel->priv->next_row_num < rownum)) {
@@ -474,27 +481,29 @@
GDA_DATA_MODEL_ROW_OUT_OF_RANGE_ERROR,
_("Row %d not found"), rownum);
}
- break;
+ return FALSE;
}
}
- return prow;
+ return TRUE;
}
-static GdaPRow *
-gda_sqlite_recordset_fetch_next (GdaPModel *model, gint rownum, GError **error)
+/*
+ * Create a new filled #GdaPRow object for the next cursor row
+ *
+ * Each new #GdaPRow created is referenced only by imodel->priv->tmp_row (the #GdaPModel implementation
+ * never keeps a reference to it). Before a new #GdaPRow gets created, the previous one, if set, is discarded.
+ */
+static gboolean
+gda_sqlite_recordset_fetch_next (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error)
{
- GdaPRow *prow;
GdaSqliteRecordset *imodel = (GdaSqliteRecordset*) model;
- prow = gda_pmodel_get_stored_row (model, rownum);
- if (prow)
- return prow;
if (imodel->priv->tmp_row)
g_object_unref (imodel->priv->tmp_row);
- prow = fetch_next_sqlite_row (imodel, FALSE, error);
- imodel->priv->tmp_row = prow;
+ *prow = fetch_next_sqlite_row (imodel, FALSE, error);
+ imodel->priv->tmp_row = *prow;
- return prow;
+ return TRUE;
}
Modified: branches/V4-branch/providers/Makefile.am
==============================================================================
--- branches/V4-branch/providers/Makefile.am (original)
+++ branches/V4-branch/providers/Makefile.am Wed Feb 20 20:36:04 2008
@@ -53,7 +53,8 @@
SUBDIRS = \
sqlite \
$(GDA_BDB_SERVER) \
- $(GDA_MDB_SERVER)
+ $(GDA_MDB_SERVER) \
+ $(GDA_POSTGRES_SERVER)
# $(GDA_FREETDS_SERVER) \
# $(GDA_IBMDB2_SERVER) \
# $(GDA_FIREBIRD_SERVER) \
@@ -61,7 +62,6 @@
# $(GDA_MSQL_SERVER) \
# $(GDA_ODBC_SERVER) \
# $(GDA_ORACLE_SERVER) \
-# $(GDA_POSTGRES_SERVER) \
# $(GDA_SYBASE_SERVER) \
# $(GDA_XBASE_SERVER) \
# $(GDA_LDAP_SERVER)
Modified: branches/V4-branch/providers/mdb/gda-mdb-provider.c
==============================================================================
--- branches/V4-branch/providers/mdb/gda-mdb-provider.c (original)
+++ branches/V4-branch/providers/mdb/gda-mdb-provider.c Wed Feb 20 20:36:04 2008
@@ -473,7 +473,7 @@
GdaBinary bin;
bin.binary_length = mdb_ole_read (spec->cdata->mdb, mdb_col, bound_values[c], MDB_BIND_SIZE);
- bin.data = bound_values[c];
+ bin.data = (guchar*) bound_values[c];
gda_value_set_binary ((tmpval = gda_value_new (coltypes [c])), &bin);
#ifdef DUMP_BINARY
Modified: branches/V4-branch/providers/postgres/Makefile.am
==============================================================================
--- branches/V4-branch/providers/postgres/Makefile.am (original)
+++ branches/V4-branch/providers/postgres/Makefile.am Wed Feb 20 20:36:04 2008
@@ -5,9 +5,9 @@
-I$(top_srcdir) \
-I$(top_srcdir)/libgda \
-I$(top_builddir) \
- $(LIBGDA_CFLAGS) \
- $(POSTGRES_CFLAGS)
+ $(LIBGDA_CFLAGS) $(POSTGRES_CFLAGS)
+# 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) parser.y
@@ -22,8 +22,6 @@
libgda_postgres_la_SOURCES = \
gda-postgres-blob-op.c \
gda-postgres-blob-op.h \
- gda-postgres-cursor-recordset.c \
- gda-postgres-cursor-recordset.h \
gda-postgres-ddl.c \
gda-postgres-ddl.h \
gda-postgres-handler-bin.c \
@@ -32,34 +30,39 @@
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 \
- utils.c
+ parser.c \
+ postgres_token_types.h
+
libgda_postgres_la_LDFLAGS = -export-dynamic -module -avoid-version $(NO_UNDEFINED)
libgda_postgres_la_LIBADD = \
$(top_builddir)/libgda/libgda-4.0.la \
- $(LIBGDA_LIBS) \
- $(POSTGRES_LIBS)
+ $(LIBGDA_LIBS) $(POSTGRES_LIBS)
xmldir = $(datadir)/libgda-4.0
xml_in_files = \
postgres_specs_create_db.xml.in \
- postgres_specs_drop_db.xml.in \
- postgres_specs_dsn.xml.in \
- postgres_specs_create_table.xml.in \
- postgres_specs_drop_table.xml.in \
- postgres_specs_create_index.xml.in \
- postgres_specs_drop_index.xml.in \
- postgres_specs_rename_table.xml.in \
- postgres_specs_add_column.xml.in \
- postgres_specs_drop_column.xml.in \
- postgres_specs_create_view.xml.in \
- postgres_specs_drop_view.xml.in
+ postgres_specs_drop_db.xml.in \
+ postgres_specs_dsn.xml.in \
+ postgres_specs_create_table.xml.in \
+ postgres_specs_drop_table.xml.in \
+ postgres_specs_create_index.xml.in \
+ postgres_specs_drop_index.xml.in \
+ postgres_specs_rename_table.xml.in \
+ postgres_specs_add_column.xml.in \
+ postgres_specs_drop_column.xml.in \
+ postgres_specs_create_view.xml.in \
+ postgres_specs_drop_view.xml.in
@INTLTOOL_XML_RULE@
@@ -71,5 +74,4 @@
EXTRA_DIST = $(xml_in_files) libgda-postgres-4.0.pc.in parser.y
DISTCLEANFILES = $(xml_DATA)
-
CLEANFILES = parser.h parser.c parser.out postgres_token_types.h gen_def$(EXEEXT_FOR_BUILD)
Modified: branches/V4-branch/providers/postgres/gda-postgres-blob-op.c
==============================================================================
--- branches/V4-branch/providers/postgres/gda-postgres-blob-op.c (original)
+++ branches/V4-branch/providers/postgres/gda-postgres-blob-op.c Wed Feb 20 20:36:04 2008
@@ -1,5 +1,5 @@
/* GDA Postgres blob
- * Copyright (C) 2005 - 2007 The GNOME Foundation
+ * Copyright (C) 2005 - 2008 The GNOME Foundation
*
* AUTHORS:
* Vivien Malerba <malerba gnome-db org>
@@ -24,13 +24,7 @@
#include <string.h>
#include "gda-postgres.h"
#include "gda-postgres-blob-op.h"
-#include <libpq/libpq-fs.h>
-
-#ifdef PARENT_TYPE
-#undef PARENT_TYPE
-#endif
-
-#define PARENT_TYPE GDA_TYPE_BLOB_OP
+#include "gda-postgres-util.h"
struct _GdaPostgresBlobOpPrivate {
GdaConnection *cnc;
@@ -69,7 +63,7 @@
0,
(GInstanceInitFunc) gda_postgres_blob_op_init
};
- type = g_type_register_static (PARENT_TYPE, "GdaPostgresBlobOp", &info, 0);
+ type = g_type_register_static (GDA_TYPE_BLOB_OP, "GdaPostgresBlobOp", &info, 0);
}
return type;
}
@@ -102,33 +96,40 @@
static PGconn *
get_pconn (GdaConnection *cnc)
{
- GdaPostgresConnectionData *priv_data;
+ PostgresConnectionData *cdata;
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (!priv_data) {
- gda_connection_add_event_string (cnc, _("Invalid PostgreSQL handle"));
+ cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
return NULL;
- }
- return priv_data->pconn;
+ return cdata->pconn;
}
static gboolean
blob_op_open (GdaPostgresBlobOp *pgop)
{
+ gboolean use_svp = FALSE;
+
if (pgop->priv->blobid == InvalidOid)
return FALSE;
if (pgop->priv->fd >= 0)
return TRUE;
- /* add a savepoint to prevent a blob open failure from rendering the transaction unuseable */
- gda_connection_add_savepoint (pgop->priv->cnc, "__gda_blob_read_svp", NULL);
+ if (gda_connection_get_transaction_status (pgop->priv->cnc))
+ /* add a savepoint to prevent a blob open failure from rendering the transaction unuseable */
+ use_svp = TRUE;
+
+ if (use_svp)
+ gda_connection_add_savepoint (pgop->priv->cnc, "__gda_blob_read_svp", NULL);
+
pgop->priv->fd = lo_open (get_pconn (pgop->priv->cnc), pgop->priv->blobid, INV_READ | INV_WRITE);
if (pgop->priv->fd < 0) {
- gda_connection_rollback_savepoint (pgop->priv->cnc, "__gda_blob_read_svp", NULL);
+ if (use_svp)
+ gda_connection_rollback_savepoint (pgop->priv->cnc, "__gda_blob_read_svp", NULL);
return FALSE;
}
- gda_connection_delete_savepoint (pgop->priv->cnc, "__gda_blob_read_svp", NULL);
+ if (use_svp)
+ gda_connection_delete_savepoint (pgop->priv->cnc, "__gda_blob_read_svp", NULL);
return TRUE;
}
@@ -193,7 +194,7 @@
PGconn *pconn = get_pconn (pgop->priv->cnc);
pgop->priv->blobid = lo_creat (pconn, INV_READ | INV_WRITE);
if (pgop->priv->blobid == InvalidOid) {
- gda_postgres_make_error (pgop->priv->cnc, pconn, NULL);
+ _gda_postgres_make_error (pgop->priv->cnc, pconn, NULL, NULL);
return FALSE;
}
}
@@ -283,14 +284,14 @@
pconn = get_pconn (pgop->priv->cnc);
if (lo_lseek (pconn, pgop->priv->fd, offset, SEEK_SET) < 0) {
- gda_postgres_make_error (pgop->priv->cnc, pconn, NULL);
+ _gda_postgres_make_error (pgop->priv->cnc, pconn, NULL, NULL);
return -1;
}
bin = (GdaBinary *) blob;
if (bin->data)
g_free (bin->data);
- bin->data = g_new0 (gchar, size);
+ bin->data = g_new0 (guchar, size);
bin->binary_length = lo_read (pconn, pgop->priv->fd, (char *) (bin->data), size);
return bin->binary_length;
}
@@ -314,14 +315,14 @@
pconn = get_pconn (pgop->priv->cnc);
if (lo_lseek (pconn, pgop->priv->fd, offset, SEEK_SET) < 0) {
- gda_postgres_make_error (pgop->priv->cnc, pconn, NULL);
+ _gda_postgres_make_error (pgop->priv->cnc, pconn, NULL, NULL);
return -1;
}
bin = (GdaBinary *) blob;
nbwritten = lo_write (pconn, pgop->priv->fd, (char*) bin->data, bin->binary_length);
if (nbwritten == -1) {
- gda_postgres_make_error (pgop->priv->cnc, pconn, NULL);
+ _gda_postgres_make_error (pgop->priv->cnc, pconn, NULL, NULL);
return -1;
}
Modified: branches/V4-branch/providers/postgres/gda-postgres-ddl.c
==============================================================================
--- branches/V4-branch/providers/postgres/gda-postgres-ddl.c (original)
+++ branches/V4-branch/providers/postgres/gda-postgres-ddl.c Wed Feb 20 20:36:04 2008
@@ -1,9 +1,8 @@
-/* GNOME DB Postgres Provider
- * Copyright (C) 2006 - 2008 The GNOME Foundation
+/* GDA Postgres Provider
+ * Copyright (C) 2008 The GNOME Foundation
*
* AUTHORS:
- * Vivien Malerba <malerba gnome-db org>
- * Bas Driessen <bas driessen xobas com>
+ * TO_ADD: your name and email
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
@@ -21,85 +20,13 @@
* Boston, MA 02111-1307, USA.
*/
-#include "gda-postgres-ddl.h"
#include <glib/gi18n-lib.h>
-#include <libgda/gda-data-handler.h>
-
-gchar *
-gda_postgres_render_CREATE_DB (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error)
-{
- GString *string;
- const GValue *value;
- gchar *sql = NULL;
-
- string = g_string_new ("CREATE DATABASE ");
-
- value = gda_server_operation_get_value_at (op, "/DB_DEF_P/DB_NAME");
- g_assert (value && G_VALUE_HOLDS (value, G_TYPE_STRING));
- g_string_append_printf (string, "\"%s\"", g_value_get_string (value));
-
- value = gda_server_operation_get_value_at (op, "/DB_DEF_P/OWNER");
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value)) {
- g_string_append (string, " OWNER ");
- g_string_append (string, g_value_get_string (value));
- }
-
- value = gda_server_operation_get_value_at (op, "/DB_DEF_P/TEMPLATE");
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value)) {
- g_string_append (string, " TEMPLATE ");
- g_string_append (string, g_value_get_string (value));
- }
-
- value = gda_server_operation_get_value_at (op, "/DB_DEF_P/DB_CSET");
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value)) {
- GdaDataHandler *dh;
- gchar *str;
-
- dh = gda_server_provider_get_data_handler_gtype (provider, cnc, G_TYPE_STRING);
- str = gda_data_handler_get_sql_from_value (dh, value);
-
- g_string_append (string, " ENCODING ");
- g_string_append (string, str);
- g_free (str);
- }
-
- value = gda_server_operation_get_value_at (op, "/DB_DEF_P/TABLESPACE");
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value)) {
- g_string_append (string, " TABLESPACE ");
- g_string_append (string, g_value_get_string (value));
- }
-
- sql = string->str;
- g_string_free (string, FALSE);
-
- return sql;
-}
-
-gchar *
-gda_postgres_render_DROP_DB (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error)
-{
- GString *string;
- const GValue *value;
- gchar *sql = NULL;
-
- string = g_string_new ("DROP DATABASE ");
-
- value = gda_server_operation_get_value_at (op, "/DB_DESC_P/DB_NAME");
- g_assert (value && G_VALUE_HOLDS (value, G_TYPE_STRING));
- g_string_append_printf (string, "\"%s\"", g_value_get_string (value));
-
- sql = string->str;
- g_string_free (string, FALSE);
-
- return sql;
-}
-
+#include <libgda/libgda.h>
+#include "gda-postgres-ddl.h"
gchar *
gda_postgres_render_CREATE_TABLE (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error)
+ GdaServerOperation *op, GError **error)
{
GString *string;
const GValue *value;
@@ -112,11 +39,8 @@
gint nbpkfields = 0;
gchar *sql = NULL;
- string = g_string_new ("CREATE ");
- value = gda_server_operation_get_value_at (op, "/TABLE_DEF_P/TABLE_TEMP");
- if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) && g_value_get_boolean (value))
- g_string_append (string, "TEMP ");
- g_string_append (string, "TABLE ");
+ /* CREATE TABLE */
+ string = g_string_new ("CREATE TABLE ");
value = gda_server_operation_get_value_at (op, "/TABLE_DEF_P/TABLE_NAME");
g_assert (value && G_VALUE_HOLDS (value, G_TYPE_STRING));
@@ -150,25 +74,12 @@
g_string_append (string, ", ");
value = gda_server_operation_get_value_at (op, "/FIELDS_A/@COLUMN_NAME/%d", i);
- g_string_append_c (string, '\"');
g_string_append (string, g_value_get_string (value));
- g_string_append_c (string, '\"');
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_SIZE/%d", i);
- if (value && G_VALUE_HOLDS (value, G_TYPE_UINT)) {
- g_string_append_printf (string, "(%d", g_value_get_uint (value));
-
- value = gda_server_operation_get_value_at (op, "/FIELDS_A/@COLUMN_SCALE/%d", i);
- if (value && G_VALUE_HOLDS (value, G_TYPE_UINT))
- g_string_append_printf (string, ",%d)", g_value_get_uint (value));
- else
- g_string_append (string, ")");
- }
-
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);
@@ -181,7 +92,7 @@
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");
@@ -202,33 +113,6 @@
}
}
}
-
- /* LIKE inheritance */
- nrows = gda_server_operation_get_sequence_size (op, "/TABLE_PARENTS_S");
- for (i = 0; i < nrows; i++) {
- value = gda_server_operation_get_value_at (op, "/TABLE_PARENTS_S/%d/TABLE_PARENT_COPY", i);
- if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) && !g_value_get_boolean (value)) {
- value = gda_server_operation_get_value_at (op, "/TABLE_PARENTS_S/%d/TABLE_PARENT_TABLE", i);
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING)) {
- const gchar *str = g_value_get_string (value);
- if (str && *str) {
- hasfields = TRUE;
- if (first)
- first = FALSE;
- else
- g_string_append (string, ", ");
-
- g_string_append (string, "LIKE ");
- g_string_append (string, str);
- value = gda_server_operation_get_value_at (op,
- "/TABLE_PARENTS_S/%d/TABLE_PARENT_COPY_DEFAULTS", i);
- if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) &&
- g_value_get_boolean (value))
- g_string_append (string, " INCLUDING DEFAULTS");
- }
- }
- }
- }
}
/* composed primary key */
@@ -239,501 +123,20 @@
while (list) {
if (list != pkfields)
g_string_append (string, ", ");
- g_string_append_c (string, '\"');
g_string_append (string, g_value_get_string ((GValue*) list->data));
- g_string_append_c (string, '\"');
list = list->next;
}
g_string_append_c (string, ')');
}
- /* foreign keys */
- if (allok) {
- GdaServerOperationNode *node;
-
- first = TRUE;
- node = gda_server_operation_get_node_info (op, "/FKEY_S");
- if (node) {
- nrows = gda_server_operation_get_sequence_size (op, "/FKEY_S");
- for (i = 0; i < nrows; i++) {
- gint nbfields, j;
-
- g_string_append (string, ", FOREIGN KEY (");
- node = gda_server_operation_get_node_info (op, "/FKEY_S/%d/FKEY_FIELDS_A", i);
- if (!node || ((nbfields = gda_data_model_get_n_rows (node->model)) == 0)) {
- allok = FALSE;
- g_set_error (error, 0, 0, _("No field specified in foreign key constraint"));
- }
- else {
- for (j = 0; j < nbfields; j++) {
- if (j != 0)
- g_string_append (string, ", ");
- value = gda_server_operation_get_value_at (op,
- "/FKEY_S/%d/FKEY_FIELDS_A/@FK_FIELD/%d", i, j);
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value))
- {
- g_string_append_c (string, '\"');
- g_string_append (string, g_value_get_string (value));
- g_string_append_c (string, '\"');
- }
- else {
- allok = FALSE;
- g_set_error (error, 0, 0,
- _("Empty field specified in foreign key constraint"));
- }
- }
- }
- g_string_append (string, ") REFERENCES ");
- value = gda_server_operation_get_value_at (op, "/FKEY_S/%d/FKEY_REF_TABLE", i);
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value))
- g_string_append (string, g_value_get_string (value));
- else {
- allok = FALSE;
- g_set_error (error, 0, 0, _("No referenced table specified in foreign key constraint"));
- }
-
- g_string_append (string, " (");
- for (j = 0; j < nbfields; j++) {
- if (j != 0)
- g_string_append (string, ", ");
- value = gda_server_operation_get_value_at (op,
- "/FKEY_S/%d/FKEY_FIELDS_A/@FK_REF_PK_FIELD/%d", i, j);
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value))
- {
- g_string_append_c (string, '\"');
- g_string_append (string, g_value_get_string (value));
- g_string_append_c (string, '\"');
- }
- else {
- allok = FALSE;
- g_set_error (error, 0, 0,
- _("Empty referenced field specified in foreign key constraint"));
- }
- }
- g_string_append_c (string, ')');
- value = gda_server_operation_get_value_at (op, "/FKEY_S/%d/FKEY_MATCH_TYPE", i);
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value))
- g_string_append_printf (string, " %s", g_value_get_string (value));
- value = gda_server_operation_get_value_at (op, "/FKEY_S/%d/FKEY_ONUPDATE", i);
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value))
- g_string_append_printf (string, " ON UPDATE %s", g_value_get_string (value));
- value = gda_server_operation_get_value_at (op, "/FKEY_S/%d/FKEY_ONDELETE", i);
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value))
- g_string_append_printf (string, " ON DELETE %s", g_value_get_string (value));
- value = gda_server_operation_get_value_at (op, "/FKEY_S/%d/FKEY_DEFERRABLE", i);
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value))
- g_string_append_printf (string, " %s", g_value_get_string (value));
- }
- }
- }
-
g_string_append (string, ")");
- /* INHERITS */
- first = TRUE;
- nrows = gda_server_operation_get_sequence_size (op, "/TABLE_PARENTS_S");
- for (i = 0; i < nrows; i++) {
- value = gda_server_operation_get_value_at (op, "/TABLE_PARENTS_S/%d/TABLE_PARENT_COPY", i);
- if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) && g_value_get_boolean (value)) {
- value = gda_server_operation_get_value_at (op, "/TABLE_PARENTS_S/%d/TABLE_PARENT_TABLE", i);
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING)) {
- const gchar *str = g_value_get_string (value);
- if (str && *str) {
- hasfields = TRUE;
- if (first) {
- g_string_append (string, " INHERITS ");
- first = FALSE;
- }
- else
- g_string_append (string, ", ");
- g_string_append (string, str);
- }
- }
- }
- }
-
if (!hasfields) {
allok = FALSE;
g_set_error (error, 0, 0, _("Table to create must have at least one row"));
}
-
- if (allok) {
- value = gda_server_operation_get_value_at (op, "/TABLE_DEF_P/TABLE_WITH_OIDS");
- if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) && g_value_get_boolean (value))
- g_string_append (string, " WITH OIDS");
- }
-
g_slist_free (pkfields);
-
- sql = string->str;
- g_string_free (string, FALSE);
-
- return sql;
-}
-
-gchar *
-gda_postgres_render_DROP_TABLE (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error)
-{
- GString *string;
- const GValue *value;
- gchar *sql = NULL;
-
- string = g_string_new ("DROP TABLE ");
-
- value = gda_server_operation_get_value_at (op, "/TABLE_DESC_P/TABLE_NAME");
- g_assert (value && G_VALUE_HOLDS (value, G_TYPE_STRING));
- g_string_append (string, g_value_get_string (value));
-
- value = gda_server_operation_get_value_at (op, "/TABLE_DESC_P/REFERENCED_ACTION");
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING)) {
- g_string_append_c (string, ' ');
- g_string_append (string, g_value_get_string (value));
- }
-
- sql = string->str;
- g_string_free (string, FALSE);
-
- return sql;
-}
-
-gchar *
-gda_postgres_render_RENAME_TABLE (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error)
-{
- GString *string;
- const GValue *value;
- gchar *sql = NULL;
-
- string = g_string_new ("ALTER TABLE ");
-
- value = gda_server_operation_get_value_at (op, "/TABLE_DESC_P/TABLE_NAME");
- g_assert (value && G_VALUE_HOLDS (value, G_TYPE_STRING));
- g_string_append (string, g_value_get_string (value));
-
- value = gda_server_operation_get_value_at (op, "/TABLE_DESC_P/TABLE_NEW_NAME");
- g_assert (value && G_VALUE_HOLDS (value, G_TYPE_STRING));
- g_string_append (string, " RENAME TO ");
- g_string_append (string, g_value_get_string (value));
-
- sql = string->str;
- g_string_free (string, FALSE);
-
- return sql;
-}
-
-
-gchar *
-gda_postgres_render_ADD_COLUMN (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error)
-{
- GString *string;
- const GValue *value;
- gchar *sql = NULL;
-
- string = g_string_new ("ALTER TABLE ");
-
- value = gda_server_operation_get_value_at (op, "/COLUMN_DEF_P/TABLE_ONLY");
- if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) && g_value_get_boolean (value))
- g_string_append (string, "ONLY ");
-
- value = gda_server_operation_get_value_at (op, "/COLUMN_DEF_P/TABLE_NAME");
- g_assert (value && G_VALUE_HOLDS (value, G_TYPE_STRING));
- g_string_append (string, g_value_get_string (value));
-
- g_string_append (string, " ADD COLUMN ");
-
- value = gda_server_operation_get_value_at (op, "/COLUMN_DEF_P/COLUMN_NAME");
- g_assert (value && G_VALUE_HOLDS (value, G_TYPE_STRING));
- g_string_append (string, g_value_get_string (value));
-
- value = gda_server_operation_get_value_at (op, "/COLUMN_DEF_P/COLUMN_TYPE");
- g_assert (value && G_VALUE_HOLDS (value, G_TYPE_STRING));
- g_string_append_c (string, ' ');
- g_string_append (string, g_value_get_string (value));
-
- value = gda_server_operation_get_value_at (op, "/COLUMN_DEF_P/COLUMN_SIZE");
- if (value && G_VALUE_HOLDS (value, G_TYPE_UINT)) {
- g_string_append_printf (string, "(%d", g_value_get_uint (value));
-
- value = gda_server_operation_get_value_at (op, "/COLUMN_DEF_P/COLUMN_SCALE");
- if (value && G_VALUE_HOLDS (value, G_TYPE_UINT))
- g_string_append_printf (string, ",%d)", g_value_get_uint (value));
- else
- g_string_append (string, ")");
- }
-
- value = gda_server_operation_get_value_at (op, "/COLUMN_DEF_P/COLUMN_DEFAULT");
- 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, "/COLUMN_DEF_P/COLUMN_NNUL");
- 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, "/COLUMN_DEF_P/COLUMN_UNIQUE");
- if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) && g_value_get_boolean (value))
- g_string_append (string, " UNIQUE");
-
- value = gda_server_operation_get_value_at (op, "/COLUMN_DEF_P/COLUMN_PKEY");
- 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, "/COLUMN_DEF_P/COLUMN_CHECK");
- 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, ')');
- }
- }
-
- sql = string->str;
- g_string_free (string, FALSE);
-
- return sql;
-}
-
-gchar *
-gda_postgres_render_DROP_COLUMN (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error)
-{
- GString *string;
- const GValue *value;
- gchar *sql = NULL;
-
- string = g_string_new ("ALTER TABLE ");
-
- value = gda_server_operation_get_value_at (op, "/COLUMN_DESC_P/TABLE_NAME");
- g_assert (value && G_VALUE_HOLDS (value, G_TYPE_STRING));
- g_string_append (string, g_value_get_string (value));
-
- value = gda_server_operation_get_value_at (op, "/COLUMN_DESC_P/COLUMN_NAME");
- g_assert (value && G_VALUE_HOLDS (value, G_TYPE_STRING));
- g_string_append (string, " DROP COLUMN ");
- g_string_append (string, g_value_get_string (value));
-
- value = gda_server_operation_get_value_at (op, "/COLUMN_DESC_P/REFERENCED_ACTION");
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING)) {
- const gchar *str = g_value_get_string (value);
- if (str && *str) {
- g_string_append_c (string, ' ');
- g_string_append (string, str);
- }
- }
-
- sql = string->str;
- g_string_free (string, FALSE);
-
- return sql;
-}
-
-
-gchar *
-gda_postgres_render_CREATE_INDEX (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error)
-{
- GString *string;
- const GValue *value;
- gchar *sql = NULL;
- GdaServerOperationNode *node;
- gint nrows, i;
-
- string = g_string_new ("CREATE ");
-
- value = gda_server_operation_get_value_at (op, "/INDEX_DEF_P/INDEX_TYPE");
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) &&
- g_value_get_string (value) && *g_value_get_string (value)) {
- g_string_append (string, g_value_get_string (value));
- g_string_append_c (string, ' ');
- }
-
- g_string_append (string, "INDEX ");
-
- value = gda_server_operation_get_value_at (op, "/INDEX_DEF_P/INDEX_NAME");
- g_assert (value && G_VALUE_HOLDS (value, G_TYPE_STRING));
- g_string_append (string, g_value_get_string (value));
-
- g_string_append (string, " ON ");
-
- value = gda_server_operation_get_value_at (op, "/INDEX_DEF_P/INDEX_ON_TABLE");
- g_assert (value && G_VALUE_HOLDS (value, G_TYPE_STRING));
- g_string_append (string, g_value_get_string (value));
-
- value = gda_server_operation_get_value_at (op, "/INDEX_DEF_P/INDEX_METHOD");
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value)) {
- g_string_append (string, " USING ");
- g_string_append (string, g_value_get_string (value));
- }
-
- /* fields or expressions the index is on */
- g_string_append (string, " (");
- node = gda_server_operation_get_node_info (op, "/INDEX_FIELDS_S");
- g_assert (node);
- nrows = gda_server_operation_get_sequence_size (op, "/INDEX_FIELDS_S");
- for (i = 0; i < nrows; i++) {
- value = gda_server_operation_get_value_at (op, "/INDEX_FIELDS_S/%d/INDEX_FIELD", i);
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value)) {
- if (i != 0)
- g_string_append (string, ", ");
- g_string_append_c (string, '\"');
- g_string_append (string, g_value_get_string (value));
- g_string_append_c (string, '\"');
- }
- }
-
- g_string_append (string, ")");
-
- /* options */
- value = gda_server_operation_get_value_at (op, "/INDEX_DEF_P/INDEX_TABLESPACE");
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value)) {
- g_string_append (string, " TABLESPACE ");
- g_string_append (string, g_value_get_string (value));
- }
-
- value = gda_server_operation_get_value_at (op, "/INDEX_DEF_P/INDEX_PREDICATE");
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value)) {
- g_string_append (string, " WHERE ");
- g_string_append (string, g_value_get_string (value));
- }
-
- sql = string->str;
- g_string_free (string, FALSE);
-
- return sql;
-}
-
-gchar *
-gda_postgres_render_DROP_INDEX (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error)
-{
- GString *string;
- const GValue *value;
- gchar *sql = NULL;
-
- string = g_string_new ("DROP INDEX ");
-
- value = gda_server_operation_get_value_at (op, "/INDEX_DESC_P/INDEX_NAME");
- g_assert (value && G_VALUE_HOLDS (value, G_TYPE_STRING));
- g_string_append (string, g_value_get_string (value));
-
- value = gda_server_operation_get_value_at (op, "/INDEX_DESC_P/REFERENCED_ACTION");
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING)) {
- g_string_append_c (string, ' ');
- g_string_append (string, g_value_get_string (value));
- }
-
- sql = string->str;
- g_string_free (string, FALSE);
-
- return sql;
-}
-
-gchar *
-gda_postgres_render_CREATE_VIEW (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error)
-{
- GString *string;
- const GValue *value;
- gboolean allok = TRUE;
- gchar *sql = NULL;
- GdaServerOperationNode *node;
-
- string = g_string_new ("CREATE ");
-
- value = gda_server_operation_get_value_at (op, "/VIEW_DEF_P/VIEW_OR_REPLACE");
- if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) && g_value_get_boolean (value))
- g_string_append (string, "OR REPLACE ");
-
- value = gda_server_operation_get_value_at (op, "/VIEW_DEF_P/VIEW_TEMP");
- if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) && g_value_get_boolean (value))
- g_string_append (string, "TEMP ");
-
- g_string_append (string, "VIEW ");
-
- value = gda_server_operation_get_value_at (op, "/VIEW_DEF_P/VIEW_NAME");
- g_assert (value && G_VALUE_HOLDS (value, G_TYPE_STRING));
- g_string_append (string, g_value_get_string (value));
-
- node = gda_server_operation_get_node_info (op, "/FIELDS_A");
- if (node) {
- gint nrows;
- gint i;
- /* finding if there is a composed primary key */
- nrows = gda_data_model_get_n_rows (node->model);
- for (i = 0; (i < nrows) && allok; i++) {
- if (i == 0)
- g_string_append (string, " (");
- value = gda_server_operation_get_value_at (op, "/FIELDS_A/@COLUMN_NAME/%d", i);
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value)) {
- if (i != 0)
- g_string_append (string, ", ");
-
- g_string_append_c (string, '`');
- g_string_append (string, g_value_get_string (value));
- g_string_append_c (string, '`');
- g_string_append_c (string, ' ');
- }
- else {
- g_set_error (error, 0, 0, _("Incorrect specified column name"));
- allok = FALSE;
- }
- }
- if (i > 0)
- g_string_append (string, ")");
- }
-
- if (allok) {
- value = gda_server_operation_get_value_at (op, "/VIEW_DEF_P/VIEW_DEF");
- g_assert (value && G_VALUE_HOLDS (value, G_TYPE_STRING));
- g_string_append (string, " AS ");
- g_string_append (string, g_value_get_string (value));
- }
-
- if (allok) {
- sql = string->str;
- g_string_free (string, FALSE);
- }
- else {
- sql = NULL;
- g_string_free (string, TRUE);
- }
-
- return sql;
-}
-
-gchar *
-gda_postgres_render_DROP_VIEW (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error)
-{
- GString *string;
- const GValue *value;
- gchar *sql = NULL;
-
- string = g_string_new ("DROP VIEW");
-
- value = gda_server_operation_get_value_at (op, "/VIEW_DESC_P/VIEW_IFEXISTS");
- if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) && g_value_get_boolean (value))
- g_string_append (string, " IF EXISTS");
-
- value = gda_server_operation_get_value_at (op, "/VIEW_DESC_P/VIEW_NAME");
- g_assert (value && G_VALUE_HOLDS (value, G_TYPE_STRING));
- g_string_append_c (string, ' ');
- g_string_append (string, g_value_get_string (value));
-
- value = gda_server_operation_get_value_at (op, "/TABLE_DESC_P/REFERENCED_ACTION");
- if (value && G_VALUE_HOLDS (value, G_TYPE_STRING)) {
- g_string_append_c (string, ' ');
- g_string_append (string, g_value_get_string (value));
- }
-
sql = string->str;
g_string_free (string, FALSE);
Modified: branches/V4-branch/providers/postgres/gda-postgres-ddl.h
==============================================================================
--- branches/V4-branch/providers/postgres/gda-postgres-ddl.h (original)
+++ branches/V4-branch/providers/postgres/gda-postgres-ddl.h Wed Feb 20 20:36:04 2008
@@ -1,8 +1,10 @@
-/* GDA Postgres Provider
- * Copyright (C) 2006 - 2008 The GNOME Foundation
+/* GDA postgres provider
+ * Copyright (C) 1998 - 2008 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
@@ -26,29 +28,28 @@
G_BEGIN_DECLS
-gchar *gda_postgres_render_CREATE_DB (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error);
-gchar *gda_postgres_render_DROP_DB (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error);
-gchar *gda_postgres_render_CREATE_TABLE (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error);
-gchar *gda_postgres_render_RENAME_TABLE (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error);
-gchar *gda_postgres_render_DROP_TABLE (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error);
-gchar *gda_postgres_render_ADD_COLUMN (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error);
-gchar *gda_postgres_render_DROP_COLUMN (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error);
-gchar *gda_postgres_render_CREATE_INDEX (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error);
-gchar *gda_postgres_render_DROP_INDEX (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error);
-gchar *gda_postgres_render_CREATE_VIEW (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error);
-gchar *gda_postgres_render_DROP_VIEW (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error);
-
+gchar *gda_postgres_render_CREATE_DB (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error);
+gchar *gda_postgres_render_DROP_DB (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error);
+gchar *gda_postgres_render_CREATE_TABLE (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error);
+gchar *gda_postgres_render_RENAME_TABLE (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error);
+gchar *gda_postgres_render_DROP_TABLE (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error);
+gchar *gda_postgres_render_ADD_COLUMN (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error);
+gchar *gda_postgres_render_DROP_COLUMN (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error);
+gchar *gda_postgres_render_CREATE_INDEX (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error);
+gchar *gda_postgres_render_DROP_INDEX (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error);
+gchar *gda_postgres_render_CREATE_VIEW (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error);
+gchar *gda_postgres_render_DROP_VIEW (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error);
G_END_DECLS
#endif
Modified: branches/V4-branch/providers/postgres/gda-postgres-handler-bin.c
==============================================================================
--- branches/V4-branch/providers/postgres/gda-postgres-handler-bin.c (original)
+++ branches/V4-branch/providers/postgres/gda-postgres-handler-bin.c Wed Feb 20 20:36:04 2008
@@ -1,6 +1,8 @@
-/* gda-postgres-handler-bin.c
+/* GDA postgres provider
+ * Copyright (C) 2007 - 2008 The GNOME Foundation.
*
- * Copyright (C) 2007 Vivien Malerba
+ * 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
@@ -18,7 +20,7 @@
*/
#include "gda-postgres-handler-bin.h"
-#include "gda-postgres-provider.h"
+#include "gda-postgres.h"
#include <string.h>
#include <glib/gi18n-lib.h>
#include <libgda/gda-server-provider.h>
@@ -77,7 +79,7 @@
NULL
};
- type = g_type_register_static (GDA_TYPE_OBJECT, "GdaPostgresHandlerBin", &info, 0);
+ type = g_type_register_static (G_TYPE_OBJECT, "GdaPostgresHandlerBin", &info, 0);
g_type_add_interface_static (type, GDA_TYPE_DATA_HANDLER, &data_entry_info);
}
return type;
@@ -119,8 +121,8 @@
hdl->priv->valid_g_types[0] = GDA_TYPE_BINARY;
hdl->priv->valid_g_types[1] = GDA_TYPE_BLOB;
- gda_object_set_name (GDA_OBJECT (hdl), _("PostgresqlBin"));
- gda_object_set_description (GDA_OBJECT (hdl), _("PostgreSQL binary representation"));
+ g_object_set_data (G_OBJECT (hdl), "name", _("PostgresqlBin"));
+ g_object_set_data (G_OBJECT (hdl), "descr", _("PostgreSQL binary representation"));
}
static void
@@ -134,8 +136,6 @@
hdl = GDA_POSTGRES_HANDLER_BIN (object);
if (hdl->priv) {
- gda_object_destroy_check (GDA_OBJECT (object));
-
g_free (hdl->priv->valid_g_types);
hdl->priv->valid_g_types = NULL;
@@ -180,7 +180,7 @@
{
gchar *retval;
GdaPostgresHandlerBin *hdl;
- GdaPostgresConnectionData *priv_data = NULL;
+ PostgresConnectionData *cdata = NULL;
g_return_val_if_fail (iface && GDA_IS_POSTGRES_HANDLER_BIN (iface), NULL);
@@ -190,11 +190,9 @@
if (hdl->priv->cnc) {
g_return_val_if_fail (GDA_IS_CONNECTION (hdl->priv->cnc), NULL);
- priv_data = g_object_get_data (G_OBJECT (hdl->priv->cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (!priv_data) {
- gda_connection_add_event_string (hdl->priv->cnc, _("Invalid PostgreSQL handle"));
- return NULL;
- }
+ cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (hdl->priv->cnc);
+ if (!cdata)
+ return FALSE;
}
if (value) {
@@ -203,12 +201,13 @@
if (data) {
gchar *tmp;
size_t retlength;
- if (0 && priv_data) {
+ if (0 && cdata) {
/* FIXME: use this call but it's only defined for Postgres >= 8.1 */
- /*tmp = PQescapeByteaConn (priv_data->pconn, data, length, &retlength);*/
+ /*tmp = PQescapeByteaConn (cdata->pconn, data, length, &retlength);*/
}
else
- tmp = PQescapeBytea (data->data, data->binary_length, &retlength);
+ tmp = (gchar *)PQescapeBytea (data->data,
+ data->binary_length, &retlength);
if (tmp) {
retval = g_strdup_printf ("'%s'", tmp);
@@ -272,7 +271,7 @@
size_t retlength;
str[i-1] = 0;
- unstr = PQunescapeBytea (str+1, &retlength);
+ unstr = PQunescapeBytea ((guchar*) (str+1), &retlength);
if (unstr) {
value = gda_value_new_binary (unstr, retlength);
PQfreemem (unstr);
@@ -365,5 +364,5 @@
hdl = GDA_POSTGRES_HANDLER_BIN (iface);
g_return_val_if_fail (hdl->priv, NULL);
- return gda_object_get_description (GDA_OBJECT (hdl));
+ return g_object_get_data (G_OBJECT (hdl), "descr");
}
Modified: branches/V4-branch/providers/postgres/gda-postgres-handler-bin.h
==============================================================================
--- branches/V4-branch/providers/postgres/gda-postgres-handler-bin.h (original)
+++ branches/V4-branch/providers/postgres/gda-postgres-handler-bin.h Wed Feb 20 20:36:04 2008
@@ -1,6 +1,8 @@
-/* gda-postgres-handler-bin.h
+/* GDA postgres provider
+ * Copyright (C) 2007 - 2008 The GNOME Foundation.
*
- * Copyright (C) 2007 Vivien Malerba
+ * 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
@@ -20,15 +22,14 @@
#ifndef __GDA_POSTGRES_HANDLER_BIN__
#define __GDA_POSTGRES_HANDLER_BIN__
-#include <libgda/gda-object.h>
#include <libgda/gda-data-handler.h>
G_BEGIN_DECLS
#define GDA_TYPE_POSTGRES_HANDLER_BIN (gda_postgres_handler_bin_get_type())
-#define GDA_POSTGRES_HANDLER_BIN(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, gda_postgres_handler_bin_get_type(), GdaPostgresHandlerBin)
-#define GDA_POSTGRES_HANDLER_BIN_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, gda_postgres_handler_bin_get_type (), GdaPostgresHandlerBinClass)
-#define GDA_IS_POSTGRES_HANDLER_BIN(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, gda_postgres_handler_bin_get_type ())
+#define GDA_POSTGRES_HANDLER_BIN(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_POSTGRES_HANDLER_BIN, GdaPostgresHandlerBin)
+#define GDA_POSTGRES_HANDLER_BIN_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_POSTGRES_HANDLER_BIN, GdaPostgresHandlerBinClass)
+#define GDA_IS_POSTGRES_HANDLER_BIN(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_POSTGRES_HANDLER_BIN)
typedef struct _GdaPostgresHandlerBin GdaPostgresHandlerBin;
@@ -39,7 +40,7 @@
/* struct for the object's data */
struct _GdaPostgresHandlerBin
{
- GdaObject object;
+ GObject object;
GdaPostgresHandlerBinPriv *priv;
};
@@ -47,7 +48,7 @@
/* struct for the object's class */
struct _GdaPostgresHandlerBinClass
{
- GdaObjectClass parent_class;
+ GObjectClass parent_class;
};
Added: branches/V4-branch/providers/postgres/gda-postgres-meta.c
==============================================================================
--- (empty file)
+++ branches/V4-branch/providers/postgres/gda-postgres-meta.c Wed Feb 20 20:36:04 2008
@@ -0,0 +1,267 @@
+/* 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.h"
+#include "gda-postgres-meta.h"
+#include "gda-postgres-provider.h"
+#include "gda-postgres-util.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>
+
+static gboolean append_a_row (GdaDataModel *to_model, GError **error, gint nb, ...);
+
+/*
+ * predefined statements' IDs
+ */
+typedef enum {
+ I_STMT_CATALOG,
+ I_STMT_BTYPES,
+ I_STMT_SCHEMATA
+} InternalStatementItem;
+
+
+/*
+ * predefined statements' SQL
+ */
+static gchar *internal_sql[] = {
+ "SELECT pg_catalog.current_database()",
+ "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 int8) 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')",
+ "SELECT catalog_name, schema_name, schema_owner, CASE WHEN schema_name ~'^pg_' THEN TRUE WHEN schema_name ='information_schema' THEN TRUE ELSE FALSE END FROM information_schema.schemata"
+};
+
+/*
+ * predefined statements' GdaStatement
+ */
+static GdaStatement **internal_stmt;
+static GdaSet *internal_params;
+
+/*
+ * global static values
+ */
+static GdaSqlParser *internal_parser = NULL;
+/* TO_ADD: other static values */
+
+
+/*
+ * Meta initialization
+ */
+void
+_gda_postgres_provider_meta_init (GdaServerProvider *provider)
+{
+ InternalStatementItem i;
+
+ internal_parser = gda_server_provider_internal_get_parser (provider);
+ internal_params = gda_set_new (NULL);
+
+ internal_stmt = g_new0 (GdaStatement *, sizeof (internal_sql) / sizeof (gchar*));
+ for (i = I_STMT_CATALOG; i < sizeof (internal_sql) / sizeof (gchar*); i++) {
+ GdaSet *set;
+ internal_stmt[i] = gda_sql_parser_parse_string (internal_parser, internal_sql[i], NULL, NULL);
+ if (!internal_stmt[i])
+ g_error ("Could not parse internal statement: %s\n", internal_sql[i]);
+ g_assert (gda_statement_get_parameters (internal_stmt[i], &set, NULL));
+ if (set) {
+ gda_set_merge_with_set (internal_params, set);
+ g_object_unref (set);
+ }
+ }
+
+ /* initialize static values here */
+}
+
+gboolean
+_gda_postgres_meta_info (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval;
+
+ model = gda_connection_statement_execute_select (cnc, internal_stmt[I_STMT_CATALOG], NULL, error);
+ if (!model)
+ return FALSE;
+
+ 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;
+ PostgresConnectionData *cdata;
+
+ cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+
+ /* use a prepared statement for the "base" model */
+ model = gda_connection_statement_execute_select (cnc, internal_stmt[I_STMT_BTYPES], NULL, error);
+ if (!model)
+ return FALSE;
+
+ /* use a proxy to customize @model */
+ proxy = (GdaDataModel*) gda_data_proxy_new (model);
+ gda_data_proxy_set_sample_size ((GdaDataProxy*) proxy, 0);
+ 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);
+
+ type = _gda_postgres_type_oid_to_gda (cdata, g_value_get_int64 (value));
+ 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)
+ 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_schemata (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *schema_name)
+{
+ GdaDataModel *model;
+ gboolean retval;
+
+ model = gda_connection_statement_execute_select (cnc, internal_stmt[I_STMT_SCHEMATA], NULL, error);
+ if (!model)
+ return FALSE;
+
+ retval = gda_meta_store_modify (store, context->table_name, model, NULL, error, NULL);
+ g_object_unref (model);
+
+ return retval;
+}
+
+
+
+gboolean
+_gda_postgres_meta_tables_views (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval = TRUE;
+
+ model = gda_meta_store_create_modify_data_model (store, context->table_name);
+ g_assert (model);
+
+ /* fill in @model */
+ TO_IMPLEMENT;
+ if (retval)
+ retval = gda_meta_store_modify (store, context->table_name, model, NULL, error, NULL);
+ g_object_unref (model);
+
+ return retval;
+}
+
+
+
+gboolean
+_gda_postgres_meta_tables_views_s (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_schema, const GValue *table_name)
+{
+ GdaDataModel *model;
+ gboolean retval = TRUE;
+
+ model = gda_meta_store_create_modify_data_model (store, context->table_name);
+ g_assert (model);
+
+ /* fill in @model */
+ TO_IMPLEMENT;
+ if (retval)
+ retval = gda_meta_store_modify (store, context->table_name, model, NULL, error, NULL);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+ GdaDataModel *model;
+ gboolean retval = TRUE;
+
+ model = gda_meta_store_create_modify_data_model (store, context->table_name);
+ g_assert (model);
+
+ /* fill in @model */
+ TO_IMPLEMENT;
+ if (retval)
+ retval = gda_meta_store_modify (store, context->table_name, model, NULL, error, NULL);
+ g_object_unref (model);
+
+ return retval;
+}
+
+gboolean
+_gda_postgres_meta_columns_t (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_schema, const GValue *table_name)
+{
+ return _gda_postgres_meta_columns_c (prov, cnc, store, context, error, table_schema, table_name, NULL);
+}
+
+gboolean
+_gda_postgres_meta_columns_c (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_schema, const GValue *table_name, const GValue *column_name)
+{
+ GdaDataModel *model;
+ gboolean retval = TRUE;
+
+ model = gda_meta_store_create_modify_data_model (store, context->table_name);
+ g_assert (model);
+
+ /* fill in @model */
+ TO_IMPLEMENT;
+ if (retval)
+ retval = gda_meta_store_modify (store, context->table_name, model, NULL, error, NULL);
+ g_object_unref (model);
+
+ return retval;
+}
Added: branches/V4-branch/providers/postgres/gda-postgres-meta.h
==============================================================================
--- (empty file)
+++ branches/V4-branch/providers/postgres/gda-postgres-meta.h Wed Feb 20 20:36:04 2008
@@ -0,0 +1,56 @@
+/* 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; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GDA_POSTGRES_META_H__
+#define __GDA_POSTGRES_META_H__
+
+#include <libgda/gda-server-provider.h>
+
+G_BEGIN_DECLS
+
+void _gda_postgres_provider_meta_init (GdaServerProvider *provider);
+gboolean _gda_postgres_meta_info (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_postgres_meta_btypes (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_postgres_meta_schemata (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *schema_name);
+gboolean _gda_postgres_meta_tables_views (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_postgres_meta_tables_views_s(GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_schema, const GValue *table_name);
+gboolean _gda_postgres_meta_columns (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_postgres_meta_columns_t (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_schema, const GValue *table_name);
+gboolean _gda_postgres_meta_columns_c (GdaServerProvider *prov, GdaConnection *cnc,
+ GdaMetaStore *store, GdaMetaContext *context, GError **error,
+ const GValue *table_schema, const GValue *table_name, const GValue *column_name);
+
+/* TO_ADD: more functions as defined in GdaServerProviderMeta */
+
+G_END_DECLS
+
+#endif
+
Modified: branches/V4-branch/providers/postgres/gda-postgres-parser.c
==============================================================================
--- branches/V4-branch/providers/postgres/gda-postgres-parser.c (original)
+++ branches/V4-branch/providers/postgres/gda-postgres-parser.c Wed Feb 20 20:36:04 2008
@@ -1,6 +1,9 @@
-/* gda-postgres-parser.c
+/* GDA Postgres provider
*
- * Copyright (C) 2007 Vivien Malerba
+ * 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
Modified: branches/V4-branch/providers/postgres/gda-postgres-parser.h
==============================================================================
--- branches/V4-branch/providers/postgres/gda-postgres-parser.h (original)
+++ branches/V4-branch/providers/postgres/gda-postgres-parser.h Wed Feb 20 20:36:04 2008
@@ -1,6 +1,9 @@
-/* gda-postgres-parser.h
+/* GDA Postgres provider
*
- * Copyright (C) 2007 Vivien Malerba
+ * 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
@@ -26,10 +29,11 @@
G_BEGIN_DECLS
-#define GDA_TYPE_POSTGRES_PARSER (gda_postgres_parser_get_type())
-#define GDA_POSTGRES_PARSER(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, gda_postgres_parser_get_type(), GdaPostgresParser)
-#define GDA_POSTGRES_PARSER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, gda_postgres_parser_get_type (), GdaPostgresParserClass)
-#define GDA_IS_POSTGRES_PARSER(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, gda_postgres_parser_get_type ())
+#define GDA_TYPE_POSTGRES_PARSER (gda_postgres_parser_get_type())
+#define GDA_POSTGRES_PARSER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_POSTGRES_PARSER, GdaPostgresParser))
+#define GDA_POSTGRES_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_POSTGRES_PARSER, GdaPostgresParserClass))
+#define GDA_IS_POSTGRES_PARSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_POSTGRES_PARSER))
+#define GDA_IS_POSTGRES_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDA_TYPE_POSTGRES_PARSER))
typedef struct _GdaPostgresParser GdaPostgresParser;
typedef struct _GdaPostgresParserClass GdaPostgresParserClass;
@@ -38,17 +42,17 @@
/* struct for the object's data */
struct _GdaPostgresParser
{
- GdaSqlParser object;
+ GdaSqlParser object;
GdaPostgresParserPrivate *priv;
};
/* struct for the object's class */
struct _GdaPostgresParserClass
{
- GdaSqlParserClass parent_class;
+ GdaSqlParserClass parent_class;
};
-GType gda_postgres_parser_get_type (void) G_GNUC_CONST;
+GType gda_postgres_parser_get_type (void) G_GNUC_CONST;
G_END_DECLS
Modified: branches/V4-branch/providers/postgres/gda-postgres-provider.c
==============================================================================
--- branches/V4-branch/providers/postgres/gda-postgres-provider.c (original)
+++ branches/V4-branch/providers/postgres/gda-postgres-provider.c Wed Feb 20 20:36:04 2008
@@ -1,179 +1,148 @@
-/* GNOME DB Postgres Provider
- * Copyright (C) 1998 - 2008 The GNOME Foundation
+/* GDA postgres provider
+ * Copyright (C) 1998 - 2008 The GNOME Foundation.
*
* AUTHORS:
* Vivien Malerba <malerba gnome-db org>
* Rodrigo Moya <rodrigo gnome-db org>
* Gonzalo Paniagua Javier <gonzalo gnome-db org>
- * Bas Driessen <bas driessen xobas com>
*
- * 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 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,
+ * 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.
+ * 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 <sys/time.h>
-#include <libgda/gda-parameter-list.h>
-#include <libgda/gda-data-model-array.h>
+#include <glib/gi18n-lib.h>
+#include <glib/gstdio.h>
+#include <libgda/libgda.h>
#include <libgda/gda-data-model-private.h>
#include <libgda/gda-server-provider-extra.h>
-#include <libgda/gda-column-index.h>
-#include <libgda/gda-server-operation.h>
-#include <libgda/gda-query.h>
-#include <libgda/gda-util.h>
-#include <libgda/gda-renderer.h>
+#include <libgda/binreloc/gda-binreloc.h>
+#include <libgda/gda-statement-extra.h>
+#include <sql-parser/gda-sql-parser.h>
#include "gda-postgres.h"
-#include "gda-postgres-parser.h"
#include "gda-postgres-provider.h"
+#include "gda-postgres-handler-bin.h"
+#include "gda-postgres-recordset.h"
#include "gda-postgres-ddl.h"
+#include "gda-postgres-meta.h"
+#include "gda-postgres-util.h"
#include "gda-postgres-blob-op.h"
-#include "gda-postgres-handler-bin.h"
-#include <libpq/libpq-fs.h>
-
-#include <libgda/handlers/gda-handler-numerical.h>
-#include <libgda/handlers/gda-handler-boolean.h>
-#include <libgda/handlers/gda-handler-time.h>
-#include <libgda/handlers/gda-handler-string.h>
-#include <libgda/handlers/gda-handler-type.h>
-#include <libgda/handlers/gda-handler-bin.h>
+#include "gda-postgres-parser.h"
-#include <libgda/sql-delimiter/gda-sql-delimiter.h>
-#include <libgda/gda-connection-private.h>
-#include <libgda/binreloc/gda-binreloc.h>
+#define _GDA_PSTMT(x) ((GdaPStmt*)(x))
+/*
+ * GObject methods
+ */
static void gda_postgres_provider_class_init (GdaPostgresProviderClass *klass);
static void gda_postgres_provider_init (GdaPostgresProvider *provider,
GdaPostgresProviderClass *klass);
-static void gda_postgres_provider_finalize (GObject *object);
+static GObjectClass *parent_class = NULL;
+
+/*
+ * GdaServerProvider's virtual methods
+ */
+/* connection management */
+static gboolean gda_postgres_provider_open_connection (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaQuarkList *params, GdaQuarkList *auth,
+ guint *task_id, GdaServerProviderAsyncCallback async_cb, gpointer cb_data);
+static gboolean gda_postgres_provider_close_connection (GdaServerProvider *provider, GdaConnection *cnc);
+static const gchar *gda_postgres_provider_get_server_version (GdaServerProvider *provider, GdaConnection *cnc);
+static const gchar *gda_postgres_provider_get_database (GdaServerProvider *provider, GdaConnection *cnc);
+
+/* DDL operations */
+static gboolean gda_postgres_provider_supports_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperationType type, GdaSet *options);
+static GdaServerOperation *gda_postgres_provider_create_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperationType type,
+ GdaSet *options, GError **error);
+static gchar *gda_postgres_provider_render_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, GError **error);
+
+static gboolean gda_postgres_provider_perform_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperation *op, guint *task_id,
+ GdaServerProviderAsyncCallback async_cb, gpointer cb_data,
+ GError **error);
+/* transactions */
+static gboolean gda_postgres_provider_begin_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GdaTransactionIsolation level, GError **error);
+static gboolean gda_postgres_provider_commit_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error);
+static gboolean gda_postgres_provider_rollback_transaction (GdaServerProvider *provider, GdaConnection * cnc,
+ const gchar *name, GError **error);
+static gboolean gda_postgres_provider_add_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error);
+static gboolean gda_postgres_provider_rollback_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error);
+static gboolean gda_postgres_provider_delete_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error);
+
+/* information retreival */
+static const gchar *gda_postgres_provider_get_version (GdaServerProvider *provider);
+static gboolean gda_postgres_provider_supports_feature (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaConnectionFeature feature);
+
+static const gchar *gda_postgres_provider_get_name (GdaServerProvider *provider);
+
+static GdaDataHandler *gda_postgres_provider_get_data_handler (GdaServerProvider *provider, GdaConnection *cnc,
+ GType g_type, const gchar *dbms_type);
+
+static const gchar* gda_postgres_provider_get_default_dbms_type (GdaServerProvider *provider, GdaConnection *cnc,
+ GType type);
+/* statements */
+static GdaSqlParser *gda_postgres_provider_create_parser (GdaServerProvider *provider, GdaConnection *cnc);
+static gchar *gda_postgres_provider_statement_to_sql (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GdaSet *params,
+ GdaStatementSqlFlag flags,
+ GSList **params_used, GError **error);
+static gboolean gda_postgres_provider_statement_prepare (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GError **error);
+static GObject *gda_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GdaSet *params,
+ GdaStatementModelUsage model_usage,
+ GType *col_types, GdaSet **last_inserted_row,
+ guint *task_id, GdaServerProviderAsyncCallback async_cb,
+ gpointer cb_data, GError **error);
-static const gchar *gda_postgres_provider_get_version (GdaServerProvider *provider);
-static gboolean gda_postgres_provider_open_connection (GdaServerProvider *provider,
- GdaConnection *cnc,
- GdaQuarkList *params,
- const gchar *username,
- const gchar *password);
-
-static gboolean gda_postgres_provider_close_connection (GdaServerProvider *provider,
- GdaConnection *cnc);
-
-static const gchar *gda_postgres_provider_get_server_version (GdaServerProvider *provider,
- GdaConnection *cnc);
-
-static const gchar *gda_postgres_provider_get_database (GdaServerProvider *provider,
- GdaConnection *cnc);
-
-static gboolean gda_postgres_provider_supports_operation (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperationType type, GdaParameterList *options);
-static GdaServerOperation *gda_postgres_provider_create_operation (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperationType type,
- GdaParameterList *options, GError **error);
-static gchar *gda_postgres_provider_render_operation (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error);
-static gboolean gda_postgres_provider_perform_operation (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error);
-
-static GList *gda_postgres_provider_execute_command (GdaServerProvider *provider,
- GdaConnection *cnc,
- GdaCommand *cmd,
- GdaParameterList *params);
-static GdaObject *gda_postgres_provider_execute_query (GdaServerProvider *provider,
- GdaConnection *cnc,
- GdaQuery *query,
- GdaParameterList *params);
-
-static gchar *gda_postgres_provider_get_last_insert_id (GdaServerProvider *provider,
- GdaConnection *cnc,
- GdaDataModel *recset);
-
-static gboolean gda_postgres_provider_begin_transaction (GdaServerProvider *provider,
- GdaConnection *cnc,
- const gchar *name, GdaTransactionIsolation level,
- GError **error);
-
-static gboolean gda_postgres_provider_commit_transaction (GdaServerProvider *provider,
- GdaConnection *cnc,
- const gchar *name, GError **error);
-
-static gboolean gda_postgres_provider_rollback_transaction (GdaServerProvider *provider,
- GdaConnection *cnc,
- const gchar *name, GError **error);
-
-static gboolean gda_postgres_provider_add_savepoint (GdaServerProvider *provider,
- GdaConnection *cnc,
- const gchar *name,
- GError **error);
-
-static gboolean gda_postgres_provider_rollback_savepoint (GdaServerProvider *provider,
- GdaConnection *cnc,
- const gchar *name,
- GError **error);
-
-static gboolean gda_postgres_provider_delete_savepoint (GdaServerProvider *provider,
- GdaConnection *cnc,
- const gchar *name,
- GError **error);
-
-static gboolean gda_postgres_provider_single_command (const GdaPostgresProvider *provider,
- GdaConnection *cnc,
- const gchar *command);
-
-static gboolean gda_postgres_provider_supports (GdaServerProvider *provider,
- GdaConnection *cnc,
- GdaConnectionFeature feature);
-
-static GdaServerProviderInfo *gda_postgres_provider_get_info (GdaServerProvider *provider,
- GdaConnection *cnc);
-
-static GdaDataModel *gda_postgres_provider_get_schema (GdaServerProvider *provider,
- GdaConnection *cnc,
- GdaConnectionSchema schema,
- GdaParameterList *params);
-
-static GdaDataHandler *gda_postgres_provider_get_data_handler (GdaServerProvider *provider,
- GdaConnection *cnc,
- GType g_type,
- const gchar *dbms_type);
-static const gchar* gda_postgres_provider_get_default_dbms_type (GdaServerProvider *provider,
- GdaConnection *cnc,
- GType type);
-static gchar *gda_postgres_provider_escape_string (GdaServerProvider *provider,
- GdaConnection *cnc, const gchar *str);
-static gchar *gda_postgres_provider_unescape_string (GdaServerProvider *provider,
- GdaConnection *cnc, const gchar *str);
-
-static GdaSqlParser *gda_postgres_provider_create_parser (GdaServerProvider *provider, GdaConnection *cnc);
-
-typedef struct {
- gint ncolumns;
- gint *columns;
- gboolean primary;
- gboolean unique;
-} GdaPostgresIdxData;
+/*
+ * private connection data destroy
+ */
+static void gda_postgres_free_cnc_data (PostgresConnectionData *cdata);
-typedef enum {
- IDX_PRIMARY,
- IDX_UNIQUE
-} IdxType;
-
-typedef struct {
- gchar *colname; /* used for PG < 7.3 */
- guint colnum; /* used for PG >= 7.3 */
- gchar *reference;
-} GdaPostgresRefData;
-static GObjectClass *parent_class = NULL;
+/*
+ * Prepared internal statements
+ * TO_ADD: any prepared statement to be used internally by the provider should be
+ * declared here, as constants and as SQL statements
+ */
+GdaStatement **internal_stmt;
+
+typedef enum {
+ I_STMT_BEGIN,
+ I_STMT_COMMIT,
+ I_STMT_ROLLBACK,
+} InternalStatementItem;
+
+gchar *internal_sql[] = {
+ "BEGIN",
+ "COMMIT",
+ "ROLLBACK"
+};
/*
* GdaPostgresProvider class implementation
@@ -181,67 +150,67 @@
static void
gda_postgres_provider_class_init (GdaPostgresProviderClass *klass)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
GdaServerProviderClass *provider_class = GDA_SERVER_PROVIDER_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
- object_class->finalize = gda_postgres_provider_finalize;
-
provider_class->get_version = gda_postgres_provider_get_version;
provider_class->get_server_version = gda_postgres_provider_get_server_version;
- provider_class->get_info = gda_postgres_provider_get_info;
- provider_class->supports_feature = gda_postgres_provider_supports;
- provider_class->get_schema = gda_postgres_provider_get_schema;
+ provider_class->get_name = gda_postgres_provider_get_name;
+ provider_class->supports_feature = gda_postgres_provider_supports_feature;
provider_class->get_data_handler = gda_postgres_provider_get_data_handler;
- provider_class->string_to_value = NULL;
provider_class->get_def_dbms_type = gda_postgres_provider_get_default_dbms_type;
- provider_class->escape_string = gda_postgres_provider_escape_string;
- provider_class->unescape_string = gda_postgres_provider_unescape_string;
provider_class->create_connection = NULL;
provider_class->open_connection = gda_postgres_provider_open_connection;
provider_class->close_connection = gda_postgres_provider_close_connection;
provider_class->get_database = gda_postgres_provider_get_database;
- provider_class->change_database = NULL;
provider_class->supports_operation = gda_postgres_provider_supports_operation;
- provider_class->create_operation = gda_postgres_provider_create_operation;
- provider_class->render_operation = gda_postgres_provider_render_operation;
- provider_class->perform_operation = gda_postgres_provider_perform_operation;
-
- provider_class->execute_command = gda_postgres_provider_execute_command;
- provider_class->execute_query = gda_postgres_provider_execute_query;
- provider_class->get_last_insert_id = gda_postgres_provider_get_last_insert_id;
+ provider_class->create_operation = gda_postgres_provider_create_operation;
+ provider_class->render_operation = gda_postgres_provider_render_operation;
+ provider_class->perform_operation = gda_postgres_provider_perform_operation;
provider_class->begin_transaction = gda_postgres_provider_begin_transaction;
provider_class->commit_transaction = gda_postgres_provider_commit_transaction;
provider_class->rollback_transaction = gda_postgres_provider_rollback_transaction;
provider_class->add_savepoint = gda_postgres_provider_add_savepoint;
- provider_class->rollback_savepoint = gda_postgres_provider_rollback_savepoint;
- provider_class->delete_savepoint = gda_postgres_provider_delete_savepoint;
+ provider_class->rollback_savepoint = gda_postgres_provider_rollback_savepoint;
+ provider_class->delete_savepoint = gda_postgres_provider_delete_savepoint;
provider_class->create_parser = gda_postgres_provider_create_parser;
- provider_class->statement_to_sql = NULL;
- provider_class->statement_prepare = NULL;
- provider_class->statement_execute = NULL;
+ provider_class->statement_to_sql = gda_postgres_provider_statement_to_sql;
+ provider_class->statement_prepare = gda_postgres_provider_statement_prepare;
+ provider_class->statement_execute = gda_postgres_provider_statement_execute;
+
+ memset (&(provider_class->meta_funcs), 0, sizeof (GdaServerProviderMeta));
+ provider_class->meta_funcs.info = _gda_postgres_meta_info;
+ provider_class->meta_funcs.btypes = _gda_postgres_meta_btypes;
+ provider_class->meta_funcs.schemata = _gda_postgres_meta_schemata;
+ provider_class->meta_funcs.tables_views = _gda_postgres_meta_tables_views;
+ provider_class->meta_funcs.tables_views_s = _gda_postgres_meta_tables_views_s;
+ provider_class->meta_funcs.columns = _gda_postgres_meta_columns;
+ provider_class->meta_funcs.columns_t = _gda_postgres_meta_columns_t;
+ provider_class->meta_funcs.columns_c = _gda_postgres_meta_columns_c;
}
static void
-gda_postgres_provider_init (GdaPostgresProvider *pg_prv, GdaPostgresProviderClass *klass)
+gda_postgres_provider_init (GdaPostgresProvider *postgres_prv, GdaPostgresProviderClass *klass)
{
-}
+ InternalStatementItem i;
+ GdaSqlParser *parser;
-static void
-gda_postgres_provider_finalize (GObject *object)
-{
- GdaPostgresProvider *pg_prv = (GdaPostgresProvider *) object;
+ parser = gda_server_provider_internal_get_parser ((GdaServerProvider*) postgres_prv);
+ internal_stmt = g_new0 (GdaStatement *, sizeof (internal_sql) / sizeof (gchar*));
+ for (i = I_STMT_BEGIN; 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]);
+ }
- g_return_if_fail (GDA_IS_POSTGRES_PROVIDER (pg_prv));
-
- /* chain to parent class */
- parent_class->finalize(object);
+ /* meta data init */
+ _gda_postgres_provider_meta_init ((GdaServerProvider*) postgres_prv);
}
GType
@@ -260,21 +229,80 @@
0,
(GInstanceInitFunc) gda_postgres_provider_init
};
- type = g_type_register_static (PARENT_TYPE,
- "GdaPostgresProvider",
+ type = g_type_register_static (GDA_TYPE_SERVER_PROVIDER, "GdaPostgresProvider",
&info, 0);
}
return type;
}
-GdaServerProvider *
-gda_postgres_provider_new (void)
+
+/*
+ * Get provider name request
+ */
+static const gchar *
+gda_postgres_provider_get_name (GdaServerProvider *provider)
+{
+ return POSTGRES_PROVIDER_NAME;
+}
+
+/*
+ * Get provider's version, no need to change this
+ */
+static const gchar *
+gda_postgres_provider_get_version (GdaServerProvider *provider)
+{
+ return PACKAGE_VERSION;
+}
+
+/* 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 void
+pq_notice_processor (PostgresConnectionData *cdata, const char *message)
{
- GdaPostgresProvider *provider;
+ GdaConnectionEvent *error;
+
+ if (!message)
+ return;
+
+ error = gda_connection_event_new (GDA_CONNECTION_EVENT_NOTICE);
+ gda_connection_event_set_description (error, message);
+ gda_connection_event_set_code (error, -1);
+ gda_connection_event_set_source (error, gda_connection_get_provider_name (cdata->cnc));
+ gda_connection_event_set_sqlstate (error, "-1");
- provider = g_object_new (gda_postgres_provider_get_type (), NULL);
- return GDA_SERVER_PROVIDER (provider);
+ gda_connection_add_event (cdata->cnc, error);
}
static GType
@@ -338,7 +366,7 @@
}
static int
-get_connection_type_list (GdaPostgresConnectionData *priv_td)
+get_connection_type_list (PostgresConnectionData *cdata)
{
GHashTable *h_table;
GdaPostgresTypeOid *td;
@@ -347,7 +375,7 @@
gchar *avoid_types = NULL;
GString *string;
- if (priv_td->version_float < 7.3) {
+ 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 */
@@ -356,12 +384,12 @@
"WHERE typowner=usesysid AND typrelid = 0 AND typname !~ '^_' "
"AND typname not in (%s) "
"ORDER BY typname", avoid_types);
- pg_res = gda_postgres_PQexec_wrap (priv_td->cnc, priv_td->pconn, query);
+ 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 (priv_td->cnc, priv_td->pconn, query);
+ pg_res_avoid = _gda_postgres_PQexec_wrap (cdata->cnc, cdata->pconn, query);
g_free (query);
}
else {
@@ -381,23 +409,23 @@
"(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 (priv_td->cnc, priv_td->pconn, query);
+ 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 (priv_td->cnc, priv_td->pconn, query);
+ 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 (priv_td->cnc, priv_td->pconn,
- "SELECT t.oid FROM pg_catalog.pg_type t WHERE t.typname = 'any'");
+ 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) ||
- ((priv_td->version_float >= 7.3) &&
+ ((cdata->version_float >= 7.3) &&
(!pg_res_anyoid || (PQresultStatus (pg_res_anyoid) != PGRES_TUPLES_OK)))) {
if (pg_res)
PQclear (pg_res);
@@ -425,9 +453,9 @@
}
PQclear (pg_res);
- priv_td->ntypes = nrows;
- priv_td->type_data = td;
- priv_td->h_table = h_table;
+ 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;
@@ -439,600 +467,478 @@
g_string_append_printf (string, ", %s", PQgetvalue (pg_res_avoid, i, 0));
}
PQclear (pg_res_avoid);
- priv_td->avoid_types = avoid_types;
- priv_td->avoid_types_oids = string->str;
+ 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' */
- priv_td->any_type_oid = "";
+ cdata->any_type_oid = "";
if (pg_res_anyoid) {
if (PQntuples (pg_res_anyoid) == 1)
- priv_td->any_type_oid = g_strdup (PQgetvalue (pg_res_anyoid, 0, 0));
+ cdata->any_type_oid = g_strdup (PQgetvalue (pg_res_anyoid, 0, 0));
PQclear (pg_res_anyoid);
}
return 0;
}
-/* get_version handler for the GdaPostgresProvider class */
-static const gchar *
-gda_postgres_provider_get_version (GdaServerProvider *provider)
-{
- GdaPostgresProvider *pg_prv = (GdaPostgresProvider *) provider;
-
- g_return_val_if_fail (GDA_IS_POSTGRES_PROVIDER (pg_prv), NULL);
- return PACKAGE_VERSION;
-}
-
-/* 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
+/*
+ * 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 PostgresConnectionData structure and associate it to @cnc
+ *
+ * Returns: TRUE if no error occurred, or FALSE otherwise (and an ERROR gonnection event must be added to @cnc)
*/
-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 void
-pq_notice_processor (GdaPostgresConnectionData *data, const char *message)
+static gboolean
+gda_postgres_provider_open_connection (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaQuarkList *params, GdaQuarkList *auth,
+ guint *task_id, GdaServerProviderAsyncCallback async_cb, gpointer cb_data)
{
- GdaConnectionEvent *error;
-
- if (!message)
- return;
-
- error = gda_connection_event_new (GDA_CONNECTION_EVENT_NOTICE);
- gda_connection_event_set_description (error, message);
- gda_connection_event_set_code (error, -1);
- gda_connection_event_set_source (error, gda_connection_get_provider (data->cnc));
- gda_connection_event_set_sqlstate (error, "-1");
+ g_return_val_if_fail (GDA_IS_POSTGRES_PROVIDER (provider), FALSE);
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
- gda_connection_add_event (data->cnc, error);
-}
+ /* 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;
+ }
-/* open_connection handler for the GdaPostgresProvider class */
-static gboolean
-gda_postgres_provider_open_connection (GdaServerProvider *provider,
- GdaConnection *cnc,
- GdaQuarkList *params,
- const gchar *username,
- const gchar *password)
-{
+ /* Check for connection parameters */
+ /* TO_ADD: your own connection parameters */
const gchar *pq_host;
const gchar *pq_db;
- const gchar *pg_searchpath;
- const gchar *pq_port;
- const gchar *pq_options;
- const gchar *pq_tty;
- const gchar *pq_user;
- const gchar *pq_pwd;
- const gchar *pq_hostaddr;
- const gchar *pq_requiressl;
- gchar *conn_string;
- GdaPostgresConnectionData *priv_data;
- PGconn *pconn;
- PGresult *pg_res;
- gchar *version;
- gfloat version_float;
-
- GdaPostgresProvider *pg_prv = (GdaPostgresProvider *) provider;
-
- g_return_val_if_fail (GDA_IS_POSTGRES_PROVIDER (pg_prv), FALSE);
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
-
- /* parse connection string */
+ const gchar *pg_searchpath;
+ const gchar *pq_port;
+ const gchar *pq_options;
+ const gchar *pq_tty;
+ const gchar *pq_user = NULL;
+ const gchar *pq_pwd = NULL;
+ const gchar *pq_hostaddr;
+ const gchar *pq_requiressl;
+ gchar *conn_string;
pq_host = gda_quark_list_find (params, "HOST");
- pq_hostaddr = gda_quark_list_find (params, "HOSTADDR");
- pq_db = gda_quark_list_find (params, "DB_NAME");
- if (!pq_db) {
- const gchar *str;
-
- str = gda_quark_list_find (params, "DATABASE");
- if (!str) {
- gda_connection_add_event_string (cnc,
- _("The connection string must contain a DB_NAME value"));
- return FALSE;
- }
- else {
- g_warning (_("The connection string format has changed: replace DATABASE with "
- "DB_NAME and the same contents"));
- pq_db = str;
- }
- }
- pg_searchpath = gda_quark_list_find (params, "SEARCHPATH");
- pq_port = gda_quark_list_find (params, "PORT");
- pq_options = gda_quark_list_find (params, "OPTIONS");
- pq_tty = gda_quark_list_find (params, "TTY");
- if (!username || *username == '\0')
- pq_user = gda_quark_list_find (params, "USER");
- else
- pq_user = username;
+ pq_hostaddr = gda_quark_list_find (params, "HOSTADDR");
+ pq_db = gda_quark_list_find (params, "DB_NAME");
+ if (!pq_db) {
+ const gchar *str;
+
+ str = gda_quark_list_find (params, "DATABASE");
+ if (!str) {
+ gda_connection_add_event_string (cnc,
+ _("The connection string must contain a DB_NAME value"));
+ return FALSE;
+ }
+ else {
+ g_warning (_("The connection string format has changed: replace DATABASE with "
+ "DB_NAME and the same contents"));
+ pq_db = str;
+ }
+ }
+ pg_searchpath = gda_quark_list_find (params, "SEARCHPATH");
+ pq_port = gda_quark_list_find (params, "PORT");
+ pq_options = gda_quark_list_find (params, "OPTIONS");
+ pq_tty = gda_quark_list_find (params, "TTY");
+ pq_user = gda_quark_list_find (auth, "USERNAME");
+ if (!pq_user)
+ pq_user = gda_quark_list_find (params, "USERNAME");
- if (!password || *password == '\0')
+ pq_pwd = gda_quark_list_find (auth, "PASSWORD");
+ if (!pq_pwd)
pq_pwd = gda_quark_list_find (params, "PASSWORD");
- else
- pq_pwd = password;
- pq_requiressl = gda_quark_list_find (params, "USE_SSL");
+ pq_requiressl = gda_quark_list_find (params, "USE_SSL");
/* TODO: Escape single quotes and backslashes in the user name and password: */
- conn_string = g_strconcat ("",
- /* host: */
- pq_host ? "host='" : "",
- pq_host ? pq_host : "",
- pq_host ? "'" : "",
- /* hostaddr: */
- pq_hostaddr ? " hostaddr=" : "",
- pq_hostaddr ? pq_hostaddr : "",
- /* db: */
- pq_db ? " dbname='" : "",
- pq_db ? pq_db : "",
- pq_db ? "'" : "",
- /* port: */
- pq_port ? " port=" : "",
- pq_port ? pq_port : "",
- /* options: */
- pq_options ? " options='" : "",
- pq_options ? pq_options : "",
- pq_options ? "'" : "",
- /* tty: */
- pq_tty ? " tty=" : "",
- pq_tty ? pq_tty : "",
- /* user: */
- (pq_user && *pq_user) ? " user='" : "",
- (pq_user && *pq_user)? pq_user : "",
- (pq_user && *pq_user)? "'" : "",
- /* password: */
- (pq_pwd && *pq_pwd) ? " password='" : "",
- (pq_pwd && *pq_pwd) ? pq_pwd : "",
- (pq_pwd && *pq_pwd) ? "'" : "",
- pq_requiressl ? " requiressl=" : "",
- pq_requiressl ? pq_requiressl : "",
- NULL);
-
- /* actually establish the connection */
+ conn_string = g_strconcat ("",
+ /* host: */
+ pq_host ? "host='" : "",
+ pq_host ? pq_host : "",
+ pq_host ? "'" : "",
+ /* hostaddr: */
+ pq_hostaddr ? " hostaddr=" : "",
+ pq_hostaddr ? pq_hostaddr : "",
+ /* db: */
+ pq_db ? " dbname='" : "",
+ pq_db ? pq_db : "",
+ pq_db ? "'" : "",
+ /* port: */
+ pq_port ? " port=" : "",
+ pq_port ? pq_port : "",
+ /* options: */
+ pq_options ? " options='" : "",
+ pq_options ? pq_options : "",
+ pq_options ? "'" : "",
+ /* tty: */
+ pq_tty ? " tty=" : "",
+ pq_tty ? pq_tty : "",
+ /* user: */
+ (pq_user && *pq_user) ? " user='" : "",
+ (pq_user && *pq_user)? pq_user : "",
+ (pq_user && *pq_user)? "'" : "",
+ /* password: */
+ (pq_pwd && *pq_pwd) ? " password='" : "",
+ (pq_pwd && *pq_pwd) ? pq_pwd : "",
+ (pq_pwd && *pq_pwd) ? "'" : "",
+ pq_requiressl ? " requiressl=" : "",
+ pq_requiressl ? pq_requiressl : "",
+ NULL);
+
+ /* open the real connection to the database */
+ PGconn *pconn;
pconn = PQconnectdb (conn_string);
- g_free(conn_string);
+ g_free(conn_string);
+
+ if (PQstatus (pconn) != CONNECTION_OK) {
+ _gda_postgres_make_error (cnc, pconn, NULL, NULL);
+ PQfinish (pconn);
+ return FALSE;
+ }
+
+ /* Create a new instance of the provider specific data associated to a connection (PostgresConnectionData),
+ * and set its contents */
+ PostgresConnectionData *cdata;
+ cdata = g_new0 (PostgresConnectionData, 1);
+ cdata->cnc = cnc;
+ cdata->pconn = pconn;
- if (PQstatus (pconn) != CONNECTION_OK) {
- gda_postgres_make_error (cnc, pconn, NULL);
- PQfinish (pconn);
+ /*
+ * Get the version as a float
+ */
+ PGresult *pg_res;
+ pg_res = _gda_postgres_PQexec_wrap (cnc, pconn, "SELECT version ()");
+ if (!pg_res) {
+ gda_postgres_free_cnc_data (cdata);
return FALSE;
}
+ cdata->version = g_strdup (PQgetvalue(pg_res, 0, 0));
+ cdata->version_float = get_pg_version_float (cdata->version);
+ PQclear (pg_res);
/*
- * Sets the DATE format for all the current session to YYYY-MM-DD
- */
- pg_res = gda_postgres_PQexec_wrap (cnc, pconn, "SET DATESTYLE TO 'ISO'");
- PQclear (pg_res);
+ * Sets the DATE format for all the current session to YYYY-MM-DD
+ */
+ pg_res = _gda_postgres_PQexec_wrap (cnc, pconn, "SET DATESTYLE TO 'ISO'");
+ if (!pg_res) {
+ gda_postgres_free_cnc_data (cdata);
+ return FALSE;
+ }
+ PQclear (pg_res);
- /*
- * Unicode is the default character set now
- */
- pg_res = gda_postgres_PQexec_wrap (cnc, pconn, "SET CLIENT_ENCODING TO 'UNICODE'");
- PQclear (pg_res);
+ /*
+ * Unicode is the default character set now
+ */
+ pg_res = _gda_postgres_PQexec_wrap (cnc, pconn, "SET CLIENT_ENCODING TO 'UNICODE'");
+ if (!pg_res) {
+ gda_postgres_free_cnc_data (cdata);
+ return FALSE;
+ }
+ PQclear (pg_res);
/*
- * Get the version as a float
- */
- pg_res = gda_postgres_PQexec_wrap (cnc, pconn, "SELECT version ()");
- version = g_strdup (PQgetvalue(pg_res, 0, 0));
- version_float = get_pg_version_float (PQgetvalue(pg_res, 0, 0));
- PQclear (pg_res);
+ * Set the search_path
+ */
+ if ((cdata->version_float >= 7.3) && pg_searchpath) {
+ const gchar *ptr;
+ gboolean path_valid = TRUE;
+
+ ptr = pg_searchpath;
+ while (*ptr) {
+ if (*ptr == ';')
+ path_valid = FALSE;
+ ptr++;
+ }
- /*
- * Set the search_path
- */
- if ((version_float >= 7.3) && pg_searchpath) {
- const gchar *ptr;
- gboolean path_valid = TRUE;
-
- ptr = pg_searchpath;
- while (*ptr) {
- if (*ptr == ';')
- path_valid = FALSE;
- ptr++;
- }
-
- if (path_valid) {
- gchar *query = g_strdup_printf ("SET search_path TO %s", pg_searchpath);
- pg_res = gda_postgres_PQexec_wrap (cnc, pconn, query);
- g_free (query);
+ if (path_valid) {
+ gchar *query = g_strdup_printf ("SET search_path TO %s", pg_searchpath);
+ pg_res = _gda_postgres_PQexec_wrap (cnc, pconn, query);
+ g_free (query);
+
+ if (!pg_res || (PQresultStatus (pg_res) != PGRES_COMMAND_OK)) {
+ gda_connection_add_event_string (cnc, _("Could not set search_path to %s"), pg_searchpath);
+ PQclear (pg_res);
+ gda_postgres_free_cnc_data (cdata);
+ return FALSE;
+ }
+ PQclear (pg_res);
+ }
+ else {
+ gda_connection_add_event_string (cnc, _("Search path %s is invalid)"), pg_searchpath);
+ gda_postgres_free_cnc_data (cdata);
+ return FALSE;
+ }
+ }
+
+ /* attach connection data */
+ gda_connection_internal_set_provider_data (cnc, cdata, (GDestroyNotify) gda_postgres_free_cnc_data);
- if (!pg_res || (PQresultStatus (pg_res) != PGRES_COMMAND_OK)) {
- g_warning ("Could not set search_path to %s\n", pg_searchpath);
- PQclear (pg_res);
- return FALSE;
- }
- PQclear (pg_res);
- }
- else {
- g_warning ("Search path %s is invalid\n", pg_searchpath);
- return FALSE;
- }
- }
- /*
- * Associated data
- */
- priv_data = g_new0 (GdaPostgresConnectionData, 1);
- priv_data->cnc = cnc;
- priv_data->pconn = pconn;
- priv_data->version = version;
- priv_data->version_float = version_float;
- if (get_connection_type_list (priv_data) != 0) {
- gda_postgres_make_error (cnc, pconn, NULL);
- PQfinish (pconn);
- g_free (priv_data);
- return FALSE;
- }
+ /* handle LibPQ's notices */
+ PQsetNoticeProcessor (pconn, (PQnoticeProcessor) pq_notice_processor, cdata);
- g_object_set_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE, priv_data);
+ if (get_connection_type_list (cdata) != 0) {
+ _gda_postgres_make_error (cnc, pconn, NULL, NULL);
+ gda_postgres_free_cnc_data (cdata);
+ return FALSE;
+ }
- /* handle LibPQ's notices */
- PQsetNoticeProcessor (pconn, (PQnoticeProcessor) pq_notice_processor, priv_data);
-
return TRUE;
}
-/* close_connection handler for the GdaPostgresProvider class */
+/*
+ * Close connection request
+ *
+ * In this function, the following _must_ be done:
+ * - Actually close the connection to the database using @cnc's associated PostgresConnectionData structure
+ * - Free the PostgresConnectionData structure and its contents
+ *
+ * Returns: TRUE if no error occurred, or FALSE otherwise (and an ERROR gonnection event must be added to @cnc)
+ */
static gboolean
gda_postgres_provider_close_connection (GdaServerProvider *provider, GdaConnection *cnc)
{
- GdaPostgresConnectionData *priv_data;
- gint i;
-
- GdaPostgresProvider *pg_prv = (GdaPostgresProvider *) provider;
+ PostgresConnectionData *cdata;
- g_return_val_if_fail (GDA_IS_POSTGRES_PROVIDER (pg_prv), FALSE);
g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (!priv_data)
+ /* Close the connection using the C API */
+ cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
return FALSE;
- PQfinish (priv_data->pconn);
-
- for (i = 0; i < priv_data->ntypes; i++) {
- g_free (priv_data->type_data[i].name);
- g_free (priv_data->type_data[i].comments);
- g_free (priv_data->type_data[i].owner);
- }
-
- g_hash_table_destroy (priv_data->h_table);
- g_free (priv_data->type_data);
- g_free (priv_data->version);
- g_free (priv_data->avoid_types_oids);
- g_free (priv_data);
+ /* Free the PostgresConnectionData structure and its contents
+ * (will also actually close the connection to the server )*/
+ gda_postgres_free_cnc_data (cdata);
+ gda_connection_internal_set_provider_data (cnc, NULL, NULL);
- g_object_set_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE, NULL);
return TRUE;
}
-/* get_server_version handler for the GdaPostgresProvider class */
+/*
+ * Server version request
+ *
+ * Returns the server version as a string, which should be stored in @cnc's associated PostgresConnectionData structure
+ */
static const gchar *
gda_postgres_provider_get_server_version (GdaServerProvider *provider, GdaConnection *cnc)
{
- GdaPostgresProvider *pg_prv = (GdaPostgresProvider *) provider;
- GdaPostgresConnectionData *priv_data;
-
- g_return_val_if_fail (GDA_IS_POSTGRES_PROVIDER (pg_prv), NULL);
- if (cnc)
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
- else
- return NULL;
-
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (!priv_data) {
- gda_connection_add_event_string (cnc, _("Invalid PostgreSQL handle"));
- return NULL;
- }
-
- return priv_data->version;
-}
-
-static GdaObject *
-compute_retval_from_pg_res (GdaConnection *cnc, PGconn *pconn, const gchar *sql, PGresult *pg_res, const gchar *cursor_name)
-{
- GdaConnectionEvent *error = NULL;
- GdaObject *retval = NULL;
-
- if (pg_res == NULL)
- error = gda_postgres_make_error (cnc, pconn, NULL);
- else {
- GdaPostgresConnectionData *priv_data;
- gint status;
-
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- status = PQresultStatus (pg_res);
-
- if (status == PGRES_EMPTY_QUERY ||
- status == PGRES_TUPLES_OK ||
- status == PGRES_COMMAND_OK) {
- if ((status == PGRES_COMMAND_OK) && !cursor_name) {
- gchar *str;
- GdaConnectionEvent *event;
-
- event = gda_connection_event_new (GDA_CONNECTION_EVENT_NOTICE);
- str = g_strdup (PQcmdStatus (pg_res));
- gda_connection_event_set_description (event, str);
- g_free (str);
- gda_connection_add_event (cnc, event);
- retval = (GdaObject *) gda_parameter_list_new_inline (NULL,
- "IMPACTED_ROWS", G_TYPE_INT,
- atoi (PQcmdTuples (pg_res)),
- NULL);
-
- if ((PQoidValue (pg_res) != InvalidOid))
- priv_data->last_insert_id = PQoidValue (pg_res);
- else
- priv_data->last_insert_id = 0;
-
- PQclear (pg_res);
- }
- else if ((status == PGRES_TUPLES_OK) ||
- ((status == PGRES_COMMAND_OK) && cursor_name)) {
- GdaDataModel *recset;
-
- if (cursor_name) {
- recset = gda_postgres_cursor_recordset_new (cnc, cursor_name,
- priv_data->chunk_size);
- PQclear (pg_res);
- }
- else
- recset = gda_postgres_recordset_new (cnc, pg_res);
-
- if (GDA_IS_DATA_MODEL (recset)) {
- g_object_set (G_OBJECT (recset),
- "command_text", sql,
- "command_type", GDA_COMMAND_TYPE_SQL, NULL);
-
- retval = (GdaObject *) recset;
- }
- }
- else {
- PQclear (pg_res);
- retval = (GdaObject *) gda_data_model_array_new (0);
- }
- }
- else {
- error = gda_postgres_make_error (cnc, pconn, pg_res);
- PQclear (pg_res);
- }
- }
-
- gda_connection_internal_treat_sql (cnc, sql, error);
- return retval;
-}
-
-static GList *
-process_sql_commands (GList *reclist, GdaConnection *cnc,
- const gchar *sql, GdaCommandOptions options)
-{
- GdaPostgresConnectionData *priv_data;
- PGconn *pconn;
- gchar **arr;
-
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (!priv_data) {
- gda_connection_add_event_string (cnc, _("Invalid PostgreSQL handle"));
- return NULL;
- }
+ PostgresConnectionData *cdata;
- pconn = priv_data->pconn;
- /* parse SQL string, which can contain several commands, separated by ';' */
- arr = gda_delimiter_split_sql (sql);
- if (arr) {
- gint n = 0;
- gboolean allok = TRUE;
-
- while (arr[n] && allok) {
- PGresult *pg_res;
- GdaObject *obj;
- gchar *cursor_name = NULL;
-
- if (priv_data->use_cursor && !strncasecmp (arr[n], "SELECT", 6)) {
- gchar *str;
- struct timeval stm;
- static guint index = 0;
-
- gettimeofday (&stm, NULL);
- cursor_name = g_strdup_printf ("gda_%d_%d_%d", stm.tv_sec, stm.tv_usec, index++);
- str = g_strdup_printf ("DECLARE %s SCROLL CURSOR WITH HOLD FOR %s",
- cursor_name, arr[n]);
- pg_res = gda_postgres_PQexec_wrap (cnc, pconn, str);
- g_free (str);
- }
- else
- pg_res = gda_postgres_PQexec_wrap (cnc, pconn, arr[n]);
- obj = compute_retval_from_pg_res (cnc, pconn, arr[n], pg_res, cursor_name);
- g_free (cursor_name);
- reclist = g_list_append (reclist, obj);
- if (!obj && !(options & GDA_COMMAND_OPTION_IGNORE_ERRORS))
- allok = FALSE;
- n++;
- }
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, NULL);
- g_strfreev (arr);
- }
+ cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
- return reclist;
+ return cdata->version;
}
-/* get_database handler for the GdaPostgresProvider class */
+/*
+ * Get database request
+ *
+ * Returns the server version as a string, which should be stored in @cnc's associated PostgresConnectionData structure
+ */
static const gchar *
-gda_postgres_provider_get_database (GdaServerProvider *provider,
- GdaConnection *cnc)
+gda_postgres_provider_get_database (GdaServerProvider *provider, GdaConnection *cnc)
{
- GdaPostgresConnectionData *priv_data;
- GdaPostgresProvider *pg_prv = (GdaPostgresProvider *) provider;
+ PostgresConnectionData *cdata;
- g_return_val_if_fail (GDA_IS_POSTGRES_PROVIDER (pg_prv), NULL);
g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, NULL);
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (!priv_data) {
- gda_connection_add_event_string (cnc, _("Invalid PostgreSQL handle"));
+ cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
return NULL;
- }
- return (const char *) PQdb ((const PGconn *) priv_data->pconn);
+ return (const char *) PQdb ((const PGconn *) cdata->pconn);
}
+/*
+ * Support operation request
+ *
+ * Tells what the implemented server operations are. To add support for an operation, the following steps are required:
+ * - create a postgres_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_postgres_provider_render_operation() and gda_postgres_provider_perform_operation() methods
+ *
+ * In this example, the GDA_SERVER_OPERATION_CREATE_TABLE is implemented
+ */
static gboolean
gda_postgres_provider_supports_operation (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperationType type, GdaParameterList *options)
+ GdaServerOperationType type, GdaSet *options)
{
- switch (type) {
- case GDA_SERVER_OPERATION_CREATE_DB:
- case GDA_SERVER_OPERATION_DROP_DB:
-
- case GDA_SERVER_OPERATION_CREATE_TABLE:
- case GDA_SERVER_OPERATION_DROP_TABLE:
- case GDA_SERVER_OPERATION_RENAME_TABLE:
-
- case GDA_SERVER_OPERATION_ADD_COLUMN:
- case GDA_SERVER_OPERATION_DROP_COLUMN:
+ if (cnc) {
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
+ }
- case GDA_SERVER_OPERATION_CREATE_INDEX:
- case GDA_SERVER_OPERATION_DROP_INDEX:
+ switch (type) {
+ case GDA_SERVER_OPERATION_CREATE_DB:
+ case GDA_SERVER_OPERATION_DROP_DB:
- case GDA_SERVER_OPERATION_CREATE_VIEW:
- case GDA_SERVER_OPERATION_DROP_VIEW:
- return TRUE;
- default:
- return FALSE;
- }
+ case GDA_SERVER_OPERATION_CREATE_TABLE:
+ case GDA_SERVER_OPERATION_DROP_TABLE:
+ case GDA_SERVER_OPERATION_RENAME_TABLE:
+
+ case GDA_SERVER_OPERATION_ADD_COLUMN:
+ case GDA_SERVER_OPERATION_DROP_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:
+ return TRUE;
+ 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 dependant on @cnc
+ */
static GdaServerOperation *
-gda_postgres_provider_create_operation (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperationType type,
- GdaParameterList *options, GError **error)
+gda_postgres_provider_create_operation (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaServerOperationType type, GdaSet *options, GError **error)
{
- gchar *file;
- GdaServerOperation *op;
- gchar *str;
+ gchar *file;
+ GdaServerOperation *op;
+ gchar *str;
gchar *dir;
-
- file = g_utf8_strdown (gda_server_operation_op_type_to_string (type), -1);
- str = g_strdup_printf ("postgres_specs_%s.xml", file);
- g_free (file);
+
+ if (cnc) {
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
+ }
+
+ file = g_utf8_strdown (gda_server_operation_op_type_to_string (type), -1);
+ str = g_strdup_printf ("postgres_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);
+ file = gda_server_provider_find_file (provider, dir, str);
g_free (dir);
- g_free (str);
+ g_free (str);
- if (! file) {
- g_set_error (error, 0, 0, _("Missing spec. file '%s'"), file);
- return NULL;
- }
+ if (! file) {
+ g_set_error (error, 0, 0, _("Missing spec. file '%s'"), file);
+ return NULL;
+ }
- op = gda_server_operation_new (type, file);
- g_free (file);
+ op = gda_server_operation_new (type, file);
+ g_free (file);
- return op;
+ return op;
}
+/*
+ * Render operation request
+ */
static gchar *
-gda_postgres_provider_render_operation (GdaServerProvider *provider, GdaConnection *cnc,
+gda_postgres_provider_render_operation (GdaServerProvider *provider, GdaConnection *cnc,
GdaServerOperation *op, GError **error)
{
- gchar *sql = NULL;
- gchar *file;
- gchar *str;
+ gchar *sql = NULL;
+ gchar *file;
+ gchar *str;
gchar *dir;
- file = g_utf8_strdown (gda_server_operation_op_type_to_string (gda_server_operation_get_op_type (op)), -1);
- str = g_strdup_printf ("postgres_specs_%s.xml", file);
- g_free (file);
+ if (cnc) {
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider_obj (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 ("postgres_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);
+ file = gda_server_provider_find_file (provider, dir, str);
g_free (dir);
- g_free (str);
-
- if (! file) {
- g_set_error (error, 0, 0, _("Missing spec. file '%s'"), file);
- return NULL;
- }
- if (!gda_server_operation_is_valid (op, file, error)) {
- g_free (file);
- return NULL;
- }
- g_free (file);
+ g_free (str);
- switch (gda_server_operation_get_op_type (op)) {
- case GDA_SERVER_OPERATION_CREATE_DB:
- sql = gda_postgres_render_CREATE_DB (provider, cnc, op, error);
- break;
- case GDA_SERVER_OPERATION_DROP_DB:
- sql = gda_postgres_render_DROP_DB (provider, cnc, op, error);
- break;
- case GDA_SERVER_OPERATION_CREATE_TABLE:
- sql = gda_postgres_render_CREATE_TABLE (provider, cnc, op, error);
- break;
- case GDA_SERVER_OPERATION_DROP_TABLE:
- sql = gda_postgres_render_DROP_TABLE (provider, cnc, op, error);
- break;
- case GDA_SERVER_OPERATION_RENAME_TABLE:
- sql = gda_postgres_render_RENAME_TABLE (provider, cnc, op, error);
- break;
- case GDA_SERVER_OPERATION_ADD_COLUMN:
- sql = gda_postgres_render_ADD_COLUMN (provider, cnc, op, error);
- break;
- case GDA_SERVER_OPERATION_DROP_COLUMN:
- sql = gda_postgres_render_DROP_COLUMN (provider, cnc, op, error);
- break;
- case GDA_SERVER_OPERATION_CREATE_INDEX:
- sql = gda_postgres_render_CREATE_INDEX (provider, cnc, op, error);
- break;
- case GDA_SERVER_OPERATION_DROP_INDEX:
- sql = gda_postgres_render_DROP_INDEX (provider, cnc, op, error);
- break;
- case GDA_SERVER_OPERATION_CREATE_VIEW:
- sql = gda_postgres_render_CREATE_VIEW (provider, cnc, op, error);
- break;
- case GDA_SERVER_OPERATION_DROP_VIEW:
- sql = gda_postgres_render_DROP_VIEW (provider, cnc, op, error);
- break;
- default:
- g_assert_not_reached ();
- }
- return sql;
+ if (! file) {
+ g_set_error (error, 0, 0, _("Missing spec. file '%s'"), file);
+ return NULL;
+ }
+ 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:
+ sql = gda_postgres_render_CREATE_DB (provider, cnc, op, error);
+ break;
+ case GDA_SERVER_OPERATION_DROP_DB:
+ sql = gda_postgres_render_DROP_DB (provider, cnc, op, error);
+ break;
+ case GDA_SERVER_OPERATION_CREATE_TABLE:
+ sql = gda_postgres_render_CREATE_TABLE (provider, cnc, op, error);
+ break;
+ case GDA_SERVER_OPERATION_DROP_TABLE:
+ sql = gda_postgres_render_DROP_TABLE (provider, cnc, op, error);
+ break;
+ case GDA_SERVER_OPERATION_RENAME_TABLE:
+ sql = gda_postgres_render_RENAME_TABLE (provider, cnc, op, error);
+ break;
+ case GDA_SERVER_OPERATION_ADD_COLUMN:
+ sql = gda_postgres_render_ADD_COLUMN (provider, cnc, op, error);
+ break;
+ case GDA_SERVER_OPERATION_DROP_COLUMN:
+ sql = gda_postgres_render_DROP_COLUMN (provider, cnc, op, error);
+ break;
+ case GDA_SERVER_OPERATION_CREATE_INDEX:
+ sql = gda_postgres_render_CREATE_INDEX (provider, cnc, op, error);
+ break;
+ case GDA_SERVER_OPERATION_DROP_INDEX:
+ sql = gda_postgres_render_DROP_INDEX (provider, cnc, op, error);
+ break;
+ case GDA_SERVER_OPERATION_CREATE_VIEW:
+ sql = gda_postgres_render_CREATE_VIEW (provider, cnc, op, error);
+ break;
+ case GDA_SERVER_OPERATION_DROP_VIEW:
+ sql = gda_postgres_render_DROP_VIEW (provider, cnc, op, error);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ return sql;
}
+/*
+ * Perform operation request
+ */
static gboolean
-gda_postgres_provider_perform_operation (GdaServerProvider *provider, GdaConnection *cnc,
- GdaServerOperation *op, GError **error)
-{
- GdaServerOperationType optype;
+gda_postgres_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,
+ _("Provider does not support asynchronous server operation"));
+ return FALSE;
+ }
- optype = gda_server_operation_get_op_type (op);
+ if (cnc) {
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
+ }
+ optype = gda_server_operation_get_op_type (op);
if (!cnc &&
((optype == GDA_SERVER_OPERATION_CREATE_DB) ||
(optype == GDA_SERVER_OPERATION_DROP_DB))) {
@@ -1040,7 +946,7 @@
PGconn *pconn;
PGresult *pg_res;
GString *string;
-
+
const gchar *pq_host = NULL;
const gchar *pq_db = NULL;
gint pq_port = -1;
@@ -1107,7 +1013,7 @@
sql = gda_server_provider_render_operation (provider, cnc, op, error);
if (!sql)
return FALSE;
- pg_res = gda_postgres_PQexec_wrap (cnc, pconn, sql);
+ pg_res = _gda_postgres_PQexec_wrap (cnc, pconn, sql);
g_free (sql);
if (!pg_res || PQresultStatus (pg_res) != PGRES_COMMAND_OK) {
g_set_error (error, 0, 0, PQresultErrorMessage (pg_res));
@@ -1121,2226 +1027,929 @@
}
else {
/* use the SQL from the provider to perform the action */
- gchar *sql;
- GdaCommand *cmd;
-
- sql = gda_server_provider_render_operation (provider, cnc, op, error);
- if (!sql)
- return FALSE;
-
- cmd = gda_command_new (sql, GDA_COMMAND_TYPE_SQL, GDA_COMMAND_OPTION_STOP_ON_ERRORS);
- g_free (sql);
- if (gda_connection_execute_non_select_command (cnc, cmd, NULL, error) != -1) {
- gda_command_free (cmd);
- return TRUE;
- }
- else {
- gda_command_free (cmd);
- return FALSE;
- }
+ return gda_server_provider_perform_operation_default (provider, cnc, op, error);
}
}
-/* execute_command handler for the GdaPostgresProvider class */
-static GList *
-gda_postgres_provider_execute_command (GdaServerProvider *provider,
- GdaConnection *cnc,
- GdaCommand *cmd,
- GdaParameterList *params)
+/*
+ * Begin transaction request
+ */
+static gboolean
+gda_postgres_provider_begin_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GdaTransactionIsolation level,
+ GError **error)
{
- GList *reclist = NULL;
- gchar *str;
- GdaPostgresProvider *pg_prv = (GdaPostgresProvider *) provider;
- GdaCommandOptions options;
- gboolean prev_use_cursor;
- gint prev_chunk_size;
- GdaPostgresConnectionData *priv_data;
+ PostgresConnectionData *cdata;
- g_return_val_if_fail (GDA_IS_POSTGRES_PROVIDER (pg_prv), NULL);
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
- g_return_val_if_fail (cmd != NULL, NULL);
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (!priv_data) {
- gda_connection_add_event_string (cnc, _("Invalid PostgreSQL handle"));
- return NULL;
- }
+ cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
- /* save previous settings */
- prev_use_cursor = priv_data->use_cursor;
- prev_chunk_size = priv_data->chunk_size;
-
- if (params) {
- GdaParameter *param;
- param = gda_parameter_list_find_param (params, "ITER_MODEL_ONLY");
- if (param) {
- const GValue *value;
-
- value = gda_parameter_get_value (param);
- if (value) {
- if (G_VALUE_TYPE (value) != G_TYPE_BOOLEAN)
- g_warning (_("Parameter ITER_MODEL_ONLY should be a boolean, not a '%s'"),
- g_type_name (G_VALUE_TYPE (value)));
- else
- priv_data->use_cursor = g_value_get_boolean (value);
- }
+ /* transaction's parameters */
+ gchar *write_option = NULL;
+ gchar *isolation_level = NULL;
+ GdaStatement *stmt = NULL;
+
+ if (cdata->version_float >= 6.5){
+ if (gda_connection_get_options (cnc) & GDA_CONNECTION_OPTIONS_READ_ONLY) {
+ if (cdata->version_float >= 7.4)
+ write_option = "READ ONLY";
+ else {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR,
+ _("Transactions are not supported in read-only mode"));
+ gda_connection_add_event_string (cnc, _("Transactions are not supported in read-only mode"));
+ return FALSE;
+ }
+ }
+ switch (level) {
+ case GDA_TRANSACTION_ISOLATION_READ_COMMITTED :
+ isolation_level = g_strconcat ("SET TRANSACTION ISOLATION LEVEL READ COMMITTED ", write_option, NULL);
+ break;
+ case GDA_TRANSACTION_ISOLATION_READ_UNCOMMITTED:
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR,
+ _("Transactions are not supported in read uncommitted isolation level"));
+ gda_connection_add_event_string (cnc,
+ _("Transactions are not supported in read uncommitted isolation level"));
+ return FALSE;
+ case GDA_TRANSACTION_ISOLATION_REPEATABLE_READ:
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR,
+ _("Transactions are not supported in repeatable read isolation level"));
+ gda_connection_add_event_string (cnc,
+ _("Transactions are not supported in repeatable read isolation level"));
+ return FALSE;
+ case GDA_TRANSACTION_ISOLATION_SERIALIZABLE :
+ isolation_level = g_strconcat ("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE ", write_option, NULL);
+ break;
+ default:
+ isolation_level = NULL;
+ }
+ }
- param = gda_parameter_list_find_param (params, "ITER_CHUNCK_SIZE");
- if (param) {
- value = gda_parameter_get_value (param);
- if (value) {
- if (G_VALUE_TYPE (value) != G_TYPE_INT)
- g_warning (_("Parameter ITER_CHUNCK_SIZE should be a gint, not a '%s'"),
- g_type_name (G_VALUE_TYPE (value)));
- else
- priv_data->chunk_size = g_value_get_int (value);
- }
- }
+ if (isolation_level) {
+ GdaSqlParser *parser;
+ parser = gda_server_provider_internal_get_parser (provider);
+ stmt = gda_sql_parser_parse_string (parser, isolation_level, NULL, NULL);
+ g_free (isolation_level);
+ if (!stmt) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_INTERNAL_ERROR,
+ _("Internal error"));
+ return FALSE;
}
}
- options = gda_command_get_options (cmd);
- switch (gda_command_get_command_type (cmd)) {
- case GDA_COMMAND_TYPE_SQL :
- reclist = process_sql_commands (reclist, cnc, gda_command_get_text (cmd),
- options);
- break;
- case GDA_COMMAND_TYPE_TABLE :
- str = g_strdup_printf ("SELECT * FROM %s", gda_command_get_text (cmd));
- reclist = process_sql_commands (reclist, cnc, str, options);
- g_free (str);
- break;
- default:
- break;
- }
+ /* BEGIN statement */
+ if (gda_connection_statement_execute_non_select (cnc, internal_stmt [I_STMT_BEGIN], NULL, NULL, error) == -1)
+ return FALSE;
- /* restore previous settings */
- priv_data->use_cursor = prev_use_cursor;
- priv_data->chunk_size = prev_chunk_size;
+ if (stmt) {
+ if (gda_connection_statement_execute_non_select (cnc, stmt, NULL, NULL, error) == -1) {
+ g_object_unref (stmt);
+ gda_postgres_provider_rollback_transaction (provider, cnc, name, NULL);
+ return FALSE;
+ }
+ g_object_unref (stmt);
+ }
- return reclist;
+ return TRUE;
}
-static PGresult *
-fetch_existing_blobs (GdaConnection *cnc, PGconn *pconn, GdaQuery *query, GdaParameterList *params)
+/*
+ * Commit transaction request
+ */
+static gboolean
+gda_postgres_provider_commit_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error)
{
- PGresult *pg_update_blobs = NULL;
-
- if (gda_query_is_update_query (query) || gda_query_is_delete_query (query)) {
- GSList *list;
- GdaParameterList *plist = NULL;
- GdaQuery *select = NULL;
+ PostgresConnectionData *cdata;
- if ((gda_query_is_update_query (query) &&
- !gda_server_provider_blob_list_for_update (cnc, query, &select, NULL)) ||
- (gda_query_is_delete_query (query) &&
- !gda_server_provider_blob_list_for_delete (cnc, query, &select, NULL))) {
- if (select)
- g_object_unref (select);
- gda_connection_add_event_string (cnc, _("Could not create a SELECT query to "
- "fetch existing BLOB values"));
- return NULL;
- }
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
- /* execute select query to get BLOB ids */
- if (select) {
- GError *error = NULL;
- gchar *sql;
-
- plist = gda_query_get_parameter_list (select);
- if (plist) {
- for (list = plist->parameters; list; list = list->next) {
- GdaParameter *param = GDA_PARAMETER (list->data);
- GdaParameter *inc_param;
-
- inc_param = gda_parameter_list_find_param (params,
- gda_object_get_name (GDA_OBJECT (param)));
- if (!inc_param) {
- /* missing parameter */
- GdaConnectionEvent *event = NULL;
- gchar *str;
- str = g_strdup_printf (_("Missing parameter for '%s'"),
- gda_object_get_name (GDA_OBJECT (param)));
- event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
- gda_connection_event_set_description (event, str);
- gda_connection_add_event (cnc, event);
- g_free (str);
- return NULL;
- }
- else
- gda_parameter_set_value (param, gda_parameter_get_value (inc_param));
- }
- }
+ cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
- sql = gda_renderer_render_as_sql (GDA_RENDERER (select), plist, NULL, 0, &error);
- if (plist)
- g_object_unref (plist);
- g_object_unref (select);
- if (!sql || !*sql) {
- gchar *msg = g_strdup_printf (_("Could not render SQL for SELECT "
- "query to fetch existing BLOB values: %s"),
- error && error->message ?
- error->message : _("No detail"));
- gda_connection_add_event_string (cnc, msg);
- g_error_free (error);
- g_free (msg);
- return NULL;
- }
- pg_update_blobs = gda_postgres_PQexec_wrap (cnc, pconn, sql);
- g_free (sql);
- if (!pg_update_blobs || (PQresultStatus (pg_update_blobs) != PGRES_TUPLES_OK)) {
- gda_postgres_make_error (cnc, pconn, pg_update_blobs);
- return NULL;
- }
- }
- }
+ /* COMMIT statement */
+ if (gda_connection_statement_execute_non_select (cnc, internal_stmt [I_STMT_COMMIT], NULL, NULL, error) == -1)
+ return FALSE;
- return pg_update_blobs;
+ return TRUE;
}
-static GdaObject *
-split_and_execute_update_query (GdaServerProvider *provider, GdaConnection *cnc, PGresult *pg_existing_blobs,
- GdaQuery *query, GdaParameterList *params)
+/*
+ * Rollback transaction request
+ */
+static gboolean
+gda_postgres_provider_rollback_transaction (GdaServerProvider *provider,
+ GdaConnection *cnc,
+ const gchar *name, GError **error)
{
- GdaQuery *nquery;
- GError *error = NULL;
- GdaParameterList *ret = NULL;
-
- if (!gda_server_provider_split_update_query (cnc, query, &nquery, &error)) {
- GdaConnectionEvent *event;
- event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
- gda_connection_event_set_description (event,
- error && error->message ? error->message : _("No detail"));
- if (error)
- g_error_free (error);
- }
- else {
- /* add params to @params to allow the execution of the nquery query with
- * parameters from pg_existing_blobs */
- GdaParameterList *iter_params;
- gint affected_rows = 0;
- GSList *list;
- gboolean allok = TRUE;
-
- iter_params = gda_query_get_parameter_list (nquery);
- g_return_val_if_fail (iter_params, NULL);
- for (list = params ? params->parameters : NULL; list; list = list->next) {
- GdaParameter *inc_param;
- inc_param = gda_parameter_list_find_param (iter_params,
- gda_object_get_name (GDA_OBJECT (list->data)));
- g_assert (inc_param);
- gda_parameter_set_value (inc_param,
- gda_parameter_get_value (GDA_PARAMETER (list->data)));
- }
-
- /* run the new update query for each row in pg_existing_blobs */
- gint row, col, nrows, ncols, npkfields, i;
- nrows = PQntuples (pg_existing_blobs);
- ncols = PQnfields (pg_existing_blobs);
- npkfields = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (nquery), "_gda_nb_key_fields"));
- for (row = 0; (row < nrows) && allok; row++) {
- /* set some specific parameters for this run */
- for (i = 0, col = ncols - npkfields; col < ncols; col++, i++) {
- GdaParameter *param;
- gchar *str;
-
- str = g_strdup_printf ("_prov_EXTRA%d", i);
- param = gda_parameter_list_find_param (iter_params, str);
- g_free (str);
- g_assert (param);
-
- gboolean isnull;
- gchar *txtvalue = PQgetvalue (pg_existing_blobs, row, col);
- GValue *value = g_new0 (GValue, 1);
- isnull = *txtvalue != '\0' ? FALSE : PQgetisnull (pg_existing_blobs, row, col);
- gda_postgres_set_value (cnc, value,
- gda_parameter_get_g_type (param),
- txtvalue, isnull, PQgetlength (pg_existing_blobs, row, col));
- gda_parameter_set_value (param, value);
- gda_value_free (value);
- }
- ret = (GdaParameterList*) gda_postgres_provider_execute_query (provider, cnc, nquery, iter_params);
- if (ret) {
- GdaParameter *param;
- g_assert (GDA_IS_PARAMETER_LIST (ret));
- param = gda_parameter_list_find_param (ret, "IMPACTED_ROWS");
- if (param)
- affected_rows += g_value_get_int (gda_parameter_get_value (param));
- g_object_unref (ret);
- }
- else
- allok = FALSE;
- }
- if (allok)
- ret = gda_parameter_list_new_inline (gda_object_get_dict (GDA_OBJECT (nquery)),
- "IMPACTED_ROWS", G_TYPE_INT, affected_rows, NULL);
- else
- ret = NULL;
- g_object_unref (nquery);
- }
+ PostgresConnectionData *cdata;
- return (GdaObject *) ret;
-}
-
-static GdaObject *
-gda_postgres_provider_execute_query (GdaServerProvider *provider,
- GdaConnection *cnc,
- GdaQuery *query,
- GdaParameterList *params)
-{
- GSList *list;
- GdaObject *retval = NULL;
- gchar *pseudo_sql;
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
- PGresult *pg_existing_blobs = NULL;
- GdaPostgresProvider *pg_prv = (GdaPostgresProvider *) provider;
- GdaPostgresConnectionData *priv_data;
- PGconn *pconn;
- PGresult *pg_res;
- gchar *prep_stm_name;
- gint nb_params = 0;
- gboolean allok = TRUE;
- GSList *used_params = NULL;
+ cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
- char **param_values = NULL;
- int *param_lengths = NULL;
- int *param_formats = NULL;
+ /* ROLLBACK statement */
+ if (gda_connection_statement_execute_non_select (cnc, internal_stmt [I_STMT_ROLLBACK], NULL, NULL, error) == -1)
+ return FALSE;
- g_return_val_if_fail (GDA_IS_POSTGRES_PROVIDER (pg_prv), NULL);
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
- g_return_val_if_fail (GDA_IS_QUERY (query), NULL);
+ return TRUE;
+}
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (!priv_data) {
- gda_connection_add_event_string (cnc, _("Invalid PostgreSQL handle"));
- return NULL;
- }
- pconn = priv_data->pconn;
-
-#ifdef GDA_DEBUG_NO
- pseudo_sql = gda_renderer_render_as_sql (GDA_RENDERER (query), params, NULL, GDA_RENDERER_PARAMS_AS_DETAILED , NULL);
- g_print ("Execute_query: %s\n", pseudo_sql);
- g_free (pseudo_sql);
-#endif
-
- /* if the query is a SELECT, we need to start a transaction if the query returns BLOB fields */
- if (gda_query_is_select_query (query) &&
- gda_server_provider_select_query_has_blobs (cnc, query, NULL))
- if (!gda_postgres_check_transaction_started (cnc))
- return NULL;
+/*
+ * Add savepoint request
+ */
+static gboolean
+gda_postgres_provider_add_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error)
+{
+ PostgresConnectionData *cdata;
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
- /* if query is an update query, then create a SELECT query to be able to fetch blob references, if
- * there are any blob parameters */
- pg_existing_blobs = fetch_existing_blobs (cnc, pconn, query, params);
- if (pg_existing_blobs && (PQntuples (pg_existing_blobs) > 1) && gda_query_is_update_query (query)) {
- /* As the UPDATE query concerns several rows, create several UPDATE queries
- * with one row each, and fail if it can't be done */
- retval = split_and_execute_update_query (provider, cnc, pg_existing_blobs, query, params);
- PQclear (pg_existing_blobs);
- return retval;
- }
-
- /* use or create a prepared statement */
- prep_stm_name = NULL; /* FIXME: store a ref to that prepared statement,
- which must be destroyed if @query changes */
- if (!prep_stm_name) {
- GError *error = NULL;
- GdaConnectionEvent *event = NULL;
-
- pseudo_sql = gda_renderer_render_as_sql (GDA_RENDERER (query), params, &used_params,
- GDA_RENDERER_PARAMS_AS_DOLLAR, &error);
- if (!pseudo_sql) {
- event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
- gda_connection_event_set_description (event, error && error->message ?
- error->message : _("No detail"));
- gda_connection_add_event (cnc, event);
- g_error_free (error);
- }
+ cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
- prep_stm_name = g_strdup_printf ("gda_query_prep_stm");
- pg_res = PQprepare (pconn, prep_stm_name, pseudo_sql, 0, NULL);
- if (!pg_res || (PQresultStatus (pg_res) != PGRES_COMMAND_OK))
- event = gda_postgres_make_error (cnc, pconn, pg_res);
- PQclear (pg_res);
+ /* Statement */
+ GdaStatement *stmt;
+ GdaSqlParser *parser;
+ gchar *str;
+ const gchar *remain;
- if (event)
- return NULL;
- }
+ parser = gda_server_provider_internal_get_parser (provider);
+ if (name)
+ str = g_strdup_printf ("SAVEPOINT %s", name);
+ else
+ str = (gchar *) name;
+ stmt = gda_sql_parser_parse_string (parser, str, &remain, NULL);
+ if (name)
+ g_free (str);
- /* bind parameters to the prepared statement */
- if (used_params) {
- gint index, blob_index;
- nb_params = g_slist_length (used_params);
- param_values = g_new0 (char *, nb_params + 1);
- param_lengths = g_new0 (int, nb_params + 1);
- param_formats = g_new0 (int, nb_params + 1);
-
- for (index = 0, blob_index = 0, list = used_params; list && allok; list = list->next, index++) {
- GdaParameter *param = GDA_PARAMETER (list->data);
- const GValue *value;
-
- value = gda_parameter_get_value (param);
- if (!value && gda_value_is_null (value))
- param_values [index] = NULL;
- else {
- GType type = G_VALUE_TYPE (value);
- if (type == GDA_TYPE_BLOB) {
- /* specific BLOB treatment */
- GdaBlob *blob = (GdaBlob*) gda_value_get_blob ((GValue *) value);
- GdaPostgresBlobOp *op;
-
- /* Postgres requires that a transaction be started for LOB operations */
- allok = gda_postgres_check_transaction_started (cnc);
- if (!allok)
- continue;
-
- /* create GdaBlobOp */
- op = (GdaPostgresBlobOp *) gda_postgres_blob_op_new (cnc);
-
- /* always create a new blob as there is no way to truncate an existing blob */
- if (gda_postgres_blob_op_declare_blob (op) &&
- gda_blob_op_write ((GdaBlobOp*) op, blob, 0))
- param_values [index] = gda_postgres_blob_op_get_id (op);
- else
- param_values [index] = NULL;
- g_object_unref (op);
- blob_index++;
- }
- else if (type == GDA_TYPE_BINARY) {
- /* directly bin binary data */
- GdaBinary *bin = (GdaBinary *) gda_value_get_binary ((GValue *) value);
- param_values [index] = bin->data;
- param_lengths [index] = bin->binary_length;
- param_formats [index] = 1; /* binary format */
- }
- else if ((type == G_TYPE_DATE) || (type == GDA_TYPE_TIMESTAMP) ||
- (type == GDA_TYPE_TIME)) {
- GdaHandlerTime *timdh;
-
- timdh = GDA_HANDLER_TIME (gda_server_provider_get_data_handler_gtype (provider, cnc, type));
- g_assert (timdh);
- param_values [index] = gda_handler_time_get_no_locale_str_from_value (timdh,
- value);
- }
- else {
- GdaDataHandler *dh;
-
- dh = gda_server_provider_get_data_handler_gtype (provider, cnc, type);
- if (dh)
- param_values [index] = gda_data_handler_get_str_from_value (dh, value);
- else
- param_values [index] = NULL;
- }
- }
- }
- g_slist_free (used_params);
+ if (!stmt) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_INTERNAL_ERROR,
+ _("Internal error"));
+ return FALSE;
}
- if (!allok) {
- g_free (prep_stm_name);
- gda_postgres_provider_single_command (pg_prv, cnc, "DEALLOCATE gda_query_prep_stm");
- g_strfreev (param_values);
- g_free (param_lengths);
- g_free (param_formats);
- if (pg_existing_blobs)
- PQclear (pg_existing_blobs);
- return NULL;
+ if (remain) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_PREPARE_STMT_ERROR,
+ _("Wrong savepoint name '%s'"), name);
+ g_object_unref (stmt);
+ return FALSE;
}
- GdaConnectionEvent *event;
- event = gda_connection_event_new (GDA_CONNECTION_EVENT_COMMAND);
- gda_connection_event_set_description (event, pseudo_sql);
- gda_connection_add_event (cnc, event);
-
- pg_res = PQexecPrepared (pconn, prep_stm_name, nb_params, (const char * const *) param_values,
- param_lengths, param_formats, 0);
- g_free (prep_stm_name);
- gda_postgres_provider_single_command (pg_prv, cnc, "DEALLOCATE gda_query_prep_stm");
- g_strfreev (param_values);
- g_free (param_lengths);
- g_free (param_formats);
- retval = compute_retval_from_pg_res (cnc, pconn, pseudo_sql, pg_res, NULL);
-
- if (retval) {
- if (pg_existing_blobs) {
- /* delete the blobs from the existing blobs */
- gint row, nrows, col, ncols;
- nrows = PQntuples (pg_existing_blobs);
- ncols = PQnfields (pg_existing_blobs);
- for (row = 0; row < nrows; row++) {
- for (col = 0; col < ncols; col++) {
- if (PQftype (pg_existing_blobs, col) == 26 /*OIDOID*/) {
- gchar *blobid = PQgetvalue (pg_existing_blobs, row, col);
- if (atoi (blobid) != InvalidOid) {
- /* add a savepoint to prevent a blob unlink
- failure from rendering the transaction unuseable */
- gda_connection_add_savepoint (cnc,
- "__gda_blob_read_svp",
- NULL);
- if (lo_unlink (pconn, atoi (blobid)) != 1)
- gda_connection_rollback_savepoint (cnc,
- "__gda_blob_read_svp", NULL);
- else
- gda_connection_delete_savepoint (cnc,
- "__gda_blob_read_svp", NULL);
- }
- }
- else
- ncols = col;
- }
- }
- PQclear (pg_existing_blobs);
- }
+ if (gda_connection_statement_execute_non_select (cnc, stmt, NULL, NULL, error) == -1) {
+ g_object_unref (stmt);
+ return FALSE;
}
- return retval;
+ g_object_unref (stmt);
+ return TRUE;
}
-static gchar *
-gda_postgres_provider_get_last_insert_id (GdaServerProvider *provider,
- GdaConnection *cnc,
- GdaDataModel *recset)
-{
- Oid oid;
- PGresult *pgres;
- GdaPostgresConnectionData *priv_data;
- GdaPostgresProvider *pg_prv = (GdaPostgresProvider *) provider;
+/*
+ * Rollback savepoint request
+ */
+static gboolean
+gda_postgres_provider_rollback_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error)
+{
+ PostgresConnectionData *cdata;
- g_return_val_if_fail (GDA_IS_POSTGRES_PROVIDER (pg_prv), NULL);
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (!priv_data) {
- gda_connection_add_event_string (cnc, _("Invalid PostgreSQL handle"));
- return NULL;
- }
+ cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
- if (recset) {
- g_return_val_if_fail (GDA_IS_POSTGRES_RECORDSET (recset), NULL);
- /* get the PQresult from the recordset */
- pgres = gda_postgres_recordset_get_pgresult (GDA_POSTGRES_RECORDSET (recset));
- if (pgres) {
- oid = PQoidValue (pgres);
- if (oid != InvalidOid)
- return g_strdup_printf ("%d", oid);
- }
- return NULL;
- }
+ /* Statement */
+ GdaStatement *stmt;
+ GdaSqlParser *parser;
+ gchar *str;
+ const gchar *remain;
- /* get the last inserted OID kept */
- if (priv_data->last_insert_id)
- return g_strdup_printf ("%d", priv_data->last_insert_id);
+ parser = gda_server_provider_internal_get_parser (provider);
+ if (name)
+ str = g_strdup_printf ("ROLLBACK TO SAVEPOINT %s", name);
else
- return NULL;
-}
-
-static gboolean
-gda_postgres_provider_begin_transaction (GdaServerProvider *provider,
- GdaConnection *cnc,
- const gchar *name, GdaTransactionIsolation level,
- GError **error)
-{
- gboolean result;
- GdaPostgresProvider *pg_prv = (GdaPostgresProvider *) provider;
- GdaPostgresConnectionData *priv_data;
-
- gchar * write_option=NULL;
- gchar * isolation_level=NULL;
-
- g_return_val_if_fail (GDA_IS_POSTGRES_PROVIDER (pg_prv), FALSE);
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ str = (gchar *) name;
+ stmt = gda_sql_parser_parse_string (parser, str, &remain, NULL);
+ if (name)
+ g_free (str);
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (priv_data->version_float >= 6.5){
- if (gda_connection_get_options (cnc) & GDA_CONNECTION_OPTIONS_READ_ONLY) {
- if (priv_data->version_float >= 7.4){
- write_option = "READ ONLY";
- }
- else {
- gda_connection_add_event_string (cnc, _("Transactions are not supported in read-only mode"));
- return FALSE;
- }
- }
- switch (level) {
- case GDA_TRANSACTION_ISOLATION_READ_COMMITTED :
- isolation_level = g_strconcat ("SET TRANSACTION ISOLATION LEVEL READ COMMITTED ", write_option, NULL);
- break;
- case GDA_TRANSACTION_ISOLATION_READ_UNCOMMITTED :
- gda_connection_add_event_string (cnc, _("Transactions are not supported in read uncommitted isolation level"));
- return FALSE;
- case GDA_TRANSACTION_ISOLATION_REPEATABLE_READ :
- gda_connection_add_event_string (cnc, _("Transactions are not supported in repeatable read isolation level"));
- return FALSE;
- case GDA_TRANSACTION_ISOLATION_SERIALIZABLE :
- isolation_level = g_strconcat ("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE ", write_option, NULL);
- break;
- default:
- isolation_level = NULL;
- }
+ if (!stmt) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_INTERNAL_ERROR,
+ _("Internal error"));
+ return FALSE;
}
- result = gda_postgres_provider_single_command (pg_prv, cnc, "BEGIN");
- if (result && isolation_level != NULL) {
- result = gda_postgres_provider_single_command (pg_prv, cnc, isolation_level) ;
- }
- g_free(isolation_level);
-
- return result;
-}
-
-static gboolean
-gda_postgres_provider_commit_transaction (GdaServerProvider *provider,
- GdaConnection *cnc,
- const gchar *name, GError **error)
-{
- GdaPostgresProvider *pg_prv = (GdaPostgresProvider *) provider;
+ if (remain) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_PREPARE_STMT_ERROR,
+ _("Wrong savepoint name '%s'"), name);
+ g_object_unref (stmt);
+ return FALSE;
+ }
- g_return_val_if_fail (GDA_IS_POSTGRES_PROVIDER (pg_prv), FALSE);
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ if (gda_connection_statement_execute_non_select (cnc, stmt, NULL, NULL, error) == -1) {
+ g_object_unref (stmt);
+ return FALSE;
+ }
- return gda_postgres_provider_single_command (pg_prv, cnc, "COMMIT");
+ g_object_unref (stmt);
+ return TRUE;
}
-static gboolean
-gda_postgres_provider_rollback_transaction (GdaServerProvider *provider,
- GdaConnection *cnc,
- const gchar *name, GError **error)
+/*
+ * Delete savepoint request
+ */
+static gboolean
+gda_postgres_provider_delete_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error)
{
- GdaPostgresProvider *pg_prv = (GdaPostgresProvider *) provider;
+ PostgresConnectionData *cdata;
- g_return_val_if_fail (GDA_IS_POSTGRES_PROVIDER (pg_prv), FALSE);
g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
- return gda_postgres_provider_single_command (pg_prv, cnc, "ROLLBACK");
-}
+ cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
-static gboolean
-gda_postgres_provider_add_savepoint (GdaServerProvider *provider,
- GdaConnection *cnc,
- const gchar *name,
- GError **error)
-{
- GdaPostgresProvider *pg_prv = (GdaPostgresProvider *) provider;
+ /* Statement */
+ GdaStatement *stmt;
+ GdaSqlParser *parser;
gchar *str;
- gboolean retval;
-
- g_return_val_if_fail (GDA_IS_POSTGRES_PROVIDER (pg_prv), FALSE);
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ const gchar *remain;
- str = g_strdup_printf ("SAVEPOINT %s", name);
- retval = gda_postgres_provider_single_command (pg_prv, cnc, str);
- g_free (str);
- return retval;
-}
+ parser = gda_server_provider_internal_get_parser (provider);
+ if (name)
+ str = g_strdup_printf ("DELETE SAVEPOINT %s", name);
+ else
+ str = (gchar *) name;
+ stmt = gda_sql_parser_parse_string (parser, str, &remain, NULL);
+ if (name)
+ g_free (str);
-static gboolean
-gda_postgres_provider_rollback_savepoint (GdaServerProvider *provider,
- GdaConnection *cnc,
- const gchar *name,
- GError **error)
-{
- GdaPostgresProvider *pg_prv = (GdaPostgresProvider *) provider;
- gchar *str;
- gboolean retval;
+ if (!stmt) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_INTERNAL_ERROR,
+ _("Internal error"));
+ return FALSE;
+ }
- g_return_val_if_fail (GDA_IS_POSTGRES_PROVIDER (pg_prv), FALSE);
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ if (remain) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_PREPARE_STMT_ERROR,
+ _("Wrong savepoint name '%s'"), name);
+ g_object_unref (stmt);
+ return FALSE;
+ }
- str = g_strdup_printf ("ROLLBACK TO SAVEPOINT %s", name);
- retval = gda_postgres_provider_single_command (pg_prv, cnc, str);
- g_free (str);
- return retval;
+ if (gda_connection_statement_execute_non_select (cnc, stmt, NULL, NULL, error) == -1) {
+ g_object_unref (stmt);
+ return FALSE;
+ }
+ g_object_unref (stmt);
+ return TRUE;
}
+/*
+ * Feature support request
+ */
static gboolean
-gda_postgres_provider_delete_savepoint (GdaServerProvider *provider,
- GdaConnection *cnc,
- const gchar *name,
- GError **error)
+gda_postgres_provider_supports_feature (GdaServerProvider *provider, GdaConnection *cnc, GdaConnectionFeature feature)
{
- GdaPostgresProvider *pg_prv = (GdaPostgresProvider *) provider;
- gchar *str;
- gboolean retval;
+ if (cnc) {
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
+ }
- g_return_val_if_fail (GDA_IS_POSTGRES_PROVIDER (pg_prv), FALSE);
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ switch (feature) {
+ case GDA_CONNECTION_FEATURE_AGGREGATES:
+ case GDA_CONNECTION_FEATURE_INDEXES:
+ case GDA_CONNECTION_FEATURE_INHERITANCE:
+ case GDA_CONNECTION_FEATURE_PROCEDURES:
+ case GDA_CONNECTION_FEATURE_SEQUENCES:
+ case GDA_CONNECTION_FEATURE_SQL:
+ case GDA_CONNECTION_FEATURE_TRANSACTIONS:
+ case GDA_CONNECTION_FEATURE_SAVEPOINTS:
+ case GDA_CONNECTION_FEATURE_SAVEPOINTS_REMOVE:
+ case GDA_CONNECTION_FEATURE_TRIGGERS:
+ case GDA_CONNECTION_FEATURE_USERS:
+ case GDA_CONNECTION_FEATURE_VIEWS:
+ case GDA_CONNECTION_FEATURE_BLOBS:
+ return TRUE;
+ case GDA_CONNECTION_FEATURE_NAMESPACES:
+ if (cnc) {
+ PostgresConnectionData *cdata;
- str = g_strdup_printf ("RELEASE SAVEPOINT %s", name);
- retval = gda_postgres_provider_single_command (pg_prv, cnc, str);
- g_free (str);
- return retval;
+ cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
+ if (cdata->version_float >= 7.3)
+ return TRUE;
+ }
+ else
+ return TRUE;
+ default:
+ break;
+ }
+
+ return FALSE;
}
-static gboolean
-gda_postgres_provider_single_command (const GdaPostgresProvider *provider,
- GdaConnection *cnc,
- const gchar *command)
+/*
+ * 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_postgres_provider_get_data_handler (GdaServerProvider *provider, GdaConnection *cnc,
+ GType type, const gchar *dbms_type)
{
- GdaPostgresConnectionData *priv_data;
- PGconn *pconn;
- PGresult *pg_res;
- gboolean result;
- GdaConnectionEvent *error = NULL;
-
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (!priv_data) {
- gda_connection_add_event_string (cnc, _("Invalid PostgreSQL handle"));
- return FALSE;
+ GdaDataHandler *dh;
+ if (cnc) {
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
}
- pconn = priv_data->pconn;
- result = FALSE;
- pg_res = gda_postgres_PQexec_wrap (cnc, pconn, command);
- if (pg_res != NULL){
- result = PQresultStatus (pg_res) == PGRES_COMMAND_OK;
- if (result == FALSE)
- error = gda_postgres_make_error (cnc, pconn, pg_res);
-
- PQclear (pg_res);
+ if (type == G_TYPE_INVALID) {
+ TO_IMPLEMENT; /* use @dbms_type */
+ dh = NULL;
+ }
+ else if ((type == GDA_TYPE_BINARY) ||
+ (type == GDA_TYPE_BLOB)) {
+ dh = gda_server_provider_handler_find (provider, cnc, type, NULL);
+ if (!dh) {
+ dh = gda_postgres_handler_bin_new (cnc);
+ if (dh) {
+ gda_server_provider_handler_declare (provider, dh, cnc, GDA_TYPE_BINARY, NULL);
+ gda_server_provider_handler_declare (provider, dh, cnc, GDA_TYPE_BLOB, NULL);
+ g_object_unref (dh);
+ }
+ }
+ }
+ else if ((type == GDA_TYPE_TIME) ||
+ (type == GDA_TYPE_TIMESTAMP) ||
+ (type == G_TYPE_DATE)) {
+ dh = gda_server_provider_handler_find (provider, NULL, type, NULL);
+ if (!dh) {
+ dh = gda_handler_time_new ();
+ gda_handler_time_set_sql_spec ((GdaHandlerTime *) dh, G_DATE_YEAR,
+ G_DATE_MONTH, G_DATE_DAY, '-', FALSE);
+ gda_server_provider_handler_declare (provider, dh, NULL, G_TYPE_DATE, NULL);
+ gda_server_provider_handler_declare (provider, dh, NULL, GDA_TYPE_TIME, NULL);
+ gda_server_provider_handler_declare (provider, dh, NULL, GDA_TYPE_TIMESTAMP, NULL);
+ g_object_unref (dh);
+ }
}
else
- error = gda_postgres_make_error (cnc, pconn, NULL);
+ dh = gda_server_provider_get_data_handler_default (provider, cnc, type, dbms_type);
- gda_connection_internal_treat_sql (cnc, command, error);
-
- return result;
+ return dh;
}
-static gboolean gda_postgres_provider_supports (GdaServerProvider *provider,
- GdaConnection *cnc,
- GdaConnectionFeature feature)
+/*
+ * Get default DBMS type request
+ *
+ * This method returns the "preferred" DBMS type for GType
+ */
+static const gchar*
+gda_postgres_provider_get_default_dbms_type (GdaServerProvider *provider, GdaConnection *cnc, GType type)
{
- GdaPostgresProvider *pgprv = (GdaPostgresProvider *) provider;
- GdaPostgresConnectionData *priv_data;
+ if (cnc) {
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
+ }
- g_return_val_if_fail (GDA_IS_POSTGRES_PROVIDER (pgprv), FALSE);
- if (cnc)
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ if (type == G_TYPE_INT64)
+ return "int8";
+ if (type == G_TYPE_UINT64)
+ return "int8";
+ if (type == GDA_TYPE_BINARY)
+ return "bytea";
+ if (type == GDA_TYPE_BLOB)
+ return "oid";
+ if (type == G_TYPE_BOOLEAN)
+ return "bool";
+ if (type == G_TYPE_DATE)
+ return "date";
+ if (type == G_TYPE_DOUBLE)
+ return "float8";
+ if (type == GDA_TYPE_GEOMETRIC_POINT)
+ return "point";
+ if (type == G_TYPE_OBJECT)
+ return "text";
+ if (type == G_TYPE_INT)
+ return "int4";
+ if (type == GDA_TYPE_LIST)
+ return "text";
+ if (type == GDA_TYPE_NUMERIC)
+ return "numeric";
+ if (type == G_TYPE_FLOAT)
+ return "float4";
+ if (type == GDA_TYPE_SHORT)
+ return "int2";
+ if (type == GDA_TYPE_USHORT)
+ return "int2";
+ if (type == G_TYPE_STRING)
+ return "varchar";
+ if (type == GDA_TYPE_TIME)
+ return "time";
+ if (type == GDA_TYPE_TIMESTAMP)
+ return "timestamp";
+ if (type == G_TYPE_CHAR)
+ return "smallint";
+ if (type == G_TYPE_UCHAR)
+ return "smallint";
+ if (type == G_TYPE_ULONG)
+ return "int8";
+ if (type == G_TYPE_UINT)
+ return "int4";
+ if (type == G_TYPE_INVALID)
+ return "text";
- switch (feature) {
- case GDA_CONNECTION_FEATURE_AGGREGATES:
- case GDA_CONNECTION_FEATURE_INDEXES:
- case GDA_CONNECTION_FEATURE_INHERITANCE:
- case GDA_CONNECTION_FEATURE_PROCEDURES:
- case GDA_CONNECTION_FEATURE_SEQUENCES:
- case GDA_CONNECTION_FEATURE_SQL:
- case GDA_CONNECTION_FEATURE_TRANSACTIONS:
- case GDA_CONNECTION_FEATURE_SAVEPOINTS:
- case GDA_CONNECTION_FEATURE_SAVEPOINTS_REMOVE:
- case GDA_CONNECTION_FEATURE_TRIGGERS:
- case GDA_CONNECTION_FEATURE_USERS:
- case GDA_CONNECTION_FEATURE_VIEWS:
- case GDA_CONNECTION_FEATURE_BLOBS:
- return TRUE;
- case GDA_CONNECTION_FEATURE_NAMESPACES:
- if (cnc) {
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (priv_data->version_float >= 7.3)
- return TRUE;
- }
- else
- return FALSE;
- default:
- break;
- }
-
- return FALSE;
+ return "text";
}
-static GdaServerProviderInfo *
-gda_postgres_provider_get_info (GdaServerProvider *provider, GdaConnection *cnc)
+/*
+ * 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_postgres_provider_create_parser (GdaServerProvider *provider, GdaConnection *cnc)
{
- static GdaServerProviderInfo info = {
- "PostgreSQL",
- TRUE,
- TRUE,
- TRUE,
- TRUE,
- TRUE,
- TRUE
- };
-
- return &info;
+ return (GdaSqlParser*) g_object_new (GDA_TYPE_POSTGRES_PARSER, "tokenizer-flavour",
+ GDA_SQL_PARSER_FLAVOUR_POSTGRESQL, NULL);
}
-static const GdaPostgresTypeOid *
-find_data_type_from_oid (GdaPostgresConnectionData *priv_data, const gchar *oid)
+/*
+ * 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_postgres_provider_statement_to_sql (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GdaSet *params, GdaStatementSqlFlag flags,
+ GSList **params_used, GError **error)
{
- GdaPostgresTypeOid *retval = NULL;
- gint i = 0;
-
- while ((i < priv_data->ntypes) && !retval) {
- if (priv_data->type_data[i].oid == atoi (oid))
- retval = &(priv_data->type_data[i]);
- i++;
+ 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_obj (cnc) == provider, NULL);
}
-
- return retval;
+
+ return gda_statement_to_sql_extended (stmt, cnc, params, flags, params_used, error);
}
-static GList *
-gda_postgres_fill_procs_data (GdaDataModelArray *recset,
- GdaPostgresConnectionData *priv_data)
+/*
+ * 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 #GdaPostgresPStmt object and declare it to @cnc.
+ */
+static gboolean
+gda_postgres_provider_statement_prepare (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GError **error)
{
- gchar *query;
- PGresult *pg_res;
- gint row_count, i;
- GList *list = NULL;
+ GdaPostgresPStmt *ps;
+ PostgresConnectionData *cdata;
- if (priv_data->version_float < 7.3)
- query = g_strdup_printf (
- "SELECT pg_proc.oid, proname, usename, obj_description (pg_proc.oid), typname, "
- "pronargs, proargtypes, prosrc "
- "FROM pg_proc, pg_user, pg_type "
- "WHERE pg_type.oid=prorettype AND usesysid=proowner AND "
- "pg_type.oid in (SELECT ty.oid FROM pg_type ty WHERE ty.typrelid = 0 AND "
- "ty.typname !~ '^_' AND ty.oid not in (%s)) "
- "AND proretset = 'f' AND ((pronargs != 0 AND oidvectortypes (proargtypes)!= '') "
- "OR pronargs=0) "
- "ORDER BY proname, pronargs",
- priv_data->avoid_types_oids);
- else
- query = g_strdup_printf (
- "SELECT p.oid, p.proname, u.usename, pg_catalog.obj_description (p.oid), "
- "t.typname, p.pronargs, p.proargtypes, p.prosrc "
- "FROM pg_catalog.pg_proc p, pg_catalog.pg_user u, pg_catalog.pg_type t, "
- "pg_catalog.pg_namespace n "
- "WHERE t.oid=p.prorettype AND u.usesysid=p.proowner AND n.oid = p.pronamespace "
- "AND p.prorettype <> 'pg_catalog.cstring'::pg_catalog.regtype "
- "AND p.proargtypes[0] <> 'pg_catalog.cstring'::pg_catalog.regtype "
- "AND t.oid in (SELECT ty.oid FROM pg_catalog.pg_type ty WHERE ty.typrelid = 0 AND "
- "ty.typname !~ '^_' AND (ty.oid not in (%s) OR ty.oid = '%s')) "
- "AND p.proretset = 'f' "
- "AND NOT p.proisagg "
- "AND pg_catalog.pg_function_is_visible(p.oid) "
- "ORDER BY proname, pronargs",
- priv_data->avoid_types_oids, priv_data->any_type_oid);
-
- pg_res = gda_postgres_PQexec_wrap (priv_data->cnc, priv_data->pconn, query);
- g_free (query);
- if (pg_res == NULL)
- return NULL;
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
+ g_return_val_if_fail (GDA_IS_STATEMENT (stmt), FALSE);
- row_count = PQntuples (pg_res);
- for (i = 0; i < row_count; i++) {
- GValue *value;
- gchar *thevalue, *procname, *instr, *ptr;
- GString *gstr;
- gint nbargs, argcounter;
- GList *rowlist = NULL;
- gboolean insert = TRUE;
-
- /* Proc name */
- procname = thevalue = PQgetvalue(pg_res, i, 1);
- g_value_set_string (value = gda_value_new (G_TYPE_STRING), thevalue);
- rowlist = g_list_append (rowlist, value);
-
- /* Proc_Id */
- thevalue = PQgetvalue (pg_res, i, 0);
- g_value_set_string (value = gda_value_new (G_TYPE_STRING), thevalue);
- rowlist = g_list_append (rowlist, value);
-
- /* Owner */
- thevalue = PQgetvalue(pg_res, i, 2);
- g_value_set_string (value = gda_value_new (G_TYPE_STRING), thevalue);
- rowlist = g_list_append (rowlist, value);
-
- /* Comments */
- thevalue = PQgetvalue(pg_res, i, 3);
- g_value_set_string (value = gda_value_new (G_TYPE_STRING), thevalue);
- rowlist = g_list_append (rowlist, value);
-
- /* Out type */
- thevalue = PQgetvalue(pg_res, i, 4);
- g_value_set_string (value = gda_value_new (G_TYPE_STRING), thevalue);
- rowlist = g_list_append (rowlist, value);
-
- /* Number of args */
- thevalue = PQgetvalue(pg_res, i, 5);
- nbargs = atoi (thevalue);
- g_value_set_int (value = gda_value_new (G_TYPE_INT), nbargs);
- rowlist = g_list_append (rowlist, value);
-
- /* In types */
- argcounter = 0;
- gstr = NULL;
- if (PQgetvalue(pg_res, i, 6)) {
- instr = g_strdup (PQgetvalue(pg_res, i, 6));
- ptr = strtok (instr, " ");
- while (ptr && *ptr && insert) {
- const GdaPostgresTypeOid *typeoid = NULL;
+ /* fetch prepares stmt if already done */
+ ps = gda_connection_get_prepared_statement (cnc, stmt);
+ if (ps)
+ return TRUE;
- if (!strcmp (ptr, priv_data->any_type_oid)) {
- thevalue = "-";
- }
- else {
- typeoid = find_data_type_from_oid (priv_data, ptr);
- if (typeoid) {
- thevalue = typeoid->name;
- }
- else {
- insert = FALSE;
- }
- }
+ /* get private connection data */
+ cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return FALSE;
- if (insert) {
- if (!gstr)
- gstr = g_string_new (thevalue);
- else
- g_string_append_printf (gstr, ",%s", thevalue);
- }
-
- ptr = strtok (NULL, " ");
- argcounter ++;
- }
- g_free (instr);
- }
- else
- insert = FALSE;
+ /* render as SQL understood by PostgreSQL */
+ GdaSet *params = NULL;
+ gchar *sql;
+ GSList *used_params = NULL;
+ if (! gda_statement_get_parameters (stmt, ¶ms, error))
+ return FALSE;
+ sql = gda_postgres_provider_statement_to_sql (provider, cnc, stmt, params, GDA_STATEMENT_SQL_PARAMS_AS_DOLLAR,
+ &used_params, error);
+ if (!sql)
+ goto out_err;
- if (gstr) {
- g_value_take_string (value = gda_value_new (G_TYPE_STRING), gstr->str);
- g_string_free (gstr, FALSE);
- }
- else
- g_value_set_string (value = gda_value_new (G_TYPE_STRING), NULL);
- rowlist = g_list_append (rowlist, value);
-
- if (argcounter != nbargs)
- insert = FALSE;
-
-
-
- /* Definition */
- thevalue = PQgetvalue(pg_res, i, 7);
- if (!strcmp (thevalue, procname))
- g_value_set_string (value = gda_value_new (G_TYPE_STRING), NULL);
- else
- g_value_set_string (value = gda_value_new (G_TYPE_STRING), thevalue);
- rowlist = g_list_append (rowlist, value);
+ /* actually prepare statement */
+ PGresult *pg_res;
+ gchar *prep_stm_name;
+ GdaConnectionEvent *event = NULL;
- if (insert)
- list = g_list_append (list, rowlist);
- else {
- g_list_foreach (rowlist, (GFunc) gda_value_free, NULL);
- g_list_free (rowlist);
- }
-
- rowlist = NULL;
+ prep_stm_name = g_strdup_printf ("PS%p", stmt);
+ pg_res = PQprepare (cdata->pconn, prep_stm_name, sql, 0, NULL);
+ if (!pg_res || (PQresultStatus (pg_res) != PGRES_COMMAND_OK)) {
+ event = _gda_postgres_make_error (cnc, cdata->pconn, pg_res, error);
+ g_free (prep_stm_name);
+ if (pg_res)
+ PQclear (pg_res);
+ goto out_err;
}
-
PQclear (pg_res);
- return list;
-}
-
-/* defined later */
-static void add_g_list_row (gpointer data, gpointer user_data);
-
-static GdaDataModel *
-get_postgres_procedures (GdaConnection *cnc, GdaParameterList *params)
-{
- GList *list;
- GdaPostgresConnectionData *priv_data;
- GdaDataModelArray *recset;
-
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
-
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
+ /* 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 ("PostgreSQL's PREPARATION: param ID: %s\n", cid);
+ }
+ else {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_PREPARE_STMT_ERROR,
+ _("Unnammed parameter is not allowed in prepared statements"));
+ g_slist_foreach (param_ids, (GFunc) g_free, NULL);
+ g_slist_free (param_ids);
+ goto out_err;
+ }
+ }
+ }
- recset = GDA_DATA_MODEL_ARRAY (gda_data_model_array_new
- (gda_server_provider_get_schema_nb_columns (GDA_CONNECTION_SCHEMA_PROCEDURES)));
- gda_server_provider_init_schema_model (GDA_DATA_MODEL (recset), GDA_CONNECTION_SCHEMA_PROCEDURES);
- list = gda_postgres_fill_procs_data (recset, priv_data);
- g_list_foreach (list, add_g_list_row, recset);
- g_list_free (list);
-
- return GDA_DATA_MODEL (recset);
-}
-
-static GdaDataModel *
-get_postgres_tables (GdaConnection *cnc, GdaParameterList *params)
-{
- GList *reclist;
- GdaPostgresConnectionData *priv_data;
- GdaDataModel *recset;
- GdaParameter *par = NULL;
- const gchar *namespace = NULL;
- const gchar *tablename = NULL;
+ /* create a prepared statement */
+ ps = gda_postgres_pstmt_new (cnc, cdata->pconn, prep_stm_name);
+ gda_pstmt_set_gda_statement (_GDA_PSTMT (ps), stmt);
+ _GDA_PSTMT (ps)->param_ids = param_ids;
+ _GDA_PSTMT (ps)->sql = sql;
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
-
- if (params) {
- par = gda_parameter_list_find_param (params, "namespace");
- if (par)
- namespace = g_value_get_string ((GValue *) gda_parameter_get_value (par));
-
- par = gda_parameter_list_find_param (params, "name");
- if (par)
- tablename = g_value_get_string ((GValue *) gda_parameter_get_value (par));
- }
-
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (priv_data->version_float < 7.3) {
- reclist = process_sql_commands (NULL, cnc,
- "SELECT relname, usename, obj_description(pg_class.oid), NULL "
- "FROM pg_class, pg_user "
- "WHERE usesysid=relowner AND relkind = 'r' AND relname !~ '^pg_' "
- "ORDER BY relname",
- GDA_COMMAND_OPTION_STOP_ON_ERRORS);
- }
- else {
- if (namespace) {
- gchar *part = NULL;
- gchar *query;
-
- if (tablename)
- part = g_strdup_printf ("AND c.relname = '%s' ", tablename);
-
- query = g_strdup_printf ("SELECT c.relname, u.usename, pg_catalog.obj_description(c.oid), NULL "
- "FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n "
- "WHERE u.usesysid=c.relowner AND c.relkind = 'r' "
- "AND c.relnamespace=n.oid "
- "%s"
- "AND n.nspname ='%s' "
- "AND n.nspname NOT IN ('pg_catalog', 'pg_toast') "
- "ORDER BY relname", part ? part : "", namespace);
- if (part) g_free (part);
- reclist = process_sql_commands (NULL, cnc,
- query,
- GDA_COMMAND_OPTION_STOP_ON_ERRORS);
- g_free (query);
- }
- else {
- gchar *part = NULL;
- gchar *query;
-
- if (tablename)
- part = g_strdup_printf ("AND c.relname = '%s' ", tablename);
-
- query = g_strdup_printf ("SELECT c.relname, u.usename, pg_catalog.obj_description(c.oid), NULL "
- "FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n "
- "WHERE u.usesysid=c.relowner AND c.relkind = 'r' "
- "AND c.relnamespace=n.oid "
- "%s"
- "AND pg_catalog.pg_table_is_visible (c.oid) "
- "AND n.nspname NOT IN ('pg_catalog', 'pg_toast') "
- "ORDER BY relname",
- part ? part : "");
- if (part) g_free (part);
- reclist = process_sql_commands (NULL, cnc,
- query,
- GDA_COMMAND_OPTION_STOP_ON_ERRORS);
- g_free (query);
- }
- }
-
- if (!reclist)
- return NULL;
-
- recset = GDA_DATA_MODEL (reclist->data);
- g_list_free (reclist);
-
- /* Set it here instead of the SQL query to allow i18n */
- gda_server_provider_init_schema_model (recset, GDA_CONNECTION_SCHEMA_TABLES);
-
- return recset;
-}
-
-static GdaDataModel *
-get_postgres_types (GdaConnection *cnc, GdaParameterList *params)
-{
- GdaDataModelArray *recset;
- GdaPostgresConnectionData *priv_data;
- gint i;
- static GHashTable *synonyms = NULL;
-
- if (!synonyms) {
- synonyms = g_hash_table_new (g_str_hash, g_str_equal);
-
- g_hash_table_insert (synonyms, "int4", "int,integer");
- g_hash_table_insert (synonyms, "int8", "bigint");
- g_hash_table_insert (synonyms, "serial8", "bigserial");
- g_hash_table_insert (synonyms, "varbit", "bit varying");
- g_hash_table_insert (synonyms, "bool", "boolean");
- g_hash_table_insert (synonyms, "varchar", "character varying");
- g_hash_table_insert (synonyms, "char", "character");
- g_hash_table_insert (synonyms, "float8", "double precision");
- g_hash_table_insert (synonyms, "numeric", "decimal");
- g_hash_table_insert (synonyms, "float4", "real");
- g_hash_table_insert (synonyms, "int2", "smallint");
- g_hash_table_insert (synonyms, "serial4", "serial");
- }
-
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
-
- /* create the recordset */
- recset = GDA_DATA_MODEL_ARRAY (gda_data_model_array_new
- (gda_server_provider_get_schema_nb_columns (GDA_CONNECTION_SCHEMA_TYPES)));
- gda_server_provider_init_schema_model (GDA_DATA_MODEL (recset), GDA_CONNECTION_SCHEMA_TYPES);
-
- /* fill the recordset */
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
-
- for (i = 0; i < priv_data->ntypes; i++) {
- GList *value_list = NULL;
- gchar *syn;
- GValue *tmpval;
-
- g_value_set_string (tmpval = gda_value_new (G_TYPE_STRING), priv_data->type_data[i].name);
- value_list = g_list_append (value_list, tmpval);
-
- g_value_set_string (tmpval = gda_value_new (G_TYPE_STRING), priv_data->type_data[i].owner);
- value_list = g_list_append (value_list, tmpval);
-
- g_value_set_string (tmpval = gda_value_new (G_TYPE_STRING), priv_data->type_data[i].comments);
- value_list = g_list_append (value_list, tmpval);
-
- g_value_set_ulong (tmpval = gda_value_new (G_TYPE_ULONG), priv_data->type_data[i].type);
- value_list = g_list_append (value_list, tmpval);
-
- syn = g_hash_table_lookup (synonyms, priv_data->type_data[i].name);
- g_value_set_string (tmpval = gda_value_new (G_TYPE_STRING), syn);
- value_list = g_list_append (value_list, tmpval);
-
- gda_data_model_append_values (GDA_DATA_MODEL (recset), value_list, NULL);
-
- g_list_foreach (value_list, (GFunc) gda_value_free, NULL);
- g_list_free (value_list);
- }
+ gda_connection_add_prepared_statement (cnc, stmt, ps);
+ return TRUE;
- return GDA_DATA_MODEL (recset);
+ out_err:
+ if (used_params)
+ g_slist_free (used_params);
+ if (params)
+ g_object_unref (params);
+ g_free (sql);
+ return FALSE;
}
-static GdaDataModel *
-get_postgres_views (GdaConnection *cnc, GdaParameterList *params)
+static gboolean
+check_transaction_started (GdaConnection *cnc)
{
- GList *reclist;
- GdaPostgresConnectionData *priv_data;
- GdaDataModel *recset;
-
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
-
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (priv_data->version_float < 7.3)
- reclist = process_sql_commands (
- NULL, cnc,
- "SELECT relname, usename, obj_description(pg_class.oid), NULL "
- "FROM pg_class, pg_user "
- "WHERE usesysid=relowner AND relkind = 'v' AND relname !~ '^pg_' ORDER BY relname",
- GDA_COMMAND_OPTION_STOP_ON_ERRORS);
- else
- reclist = process_sql_commands (
- NULL, cnc,
- "SELECT c.relname, u.usename, pg_catalog.obj_description(c.oid), "
- "pg_catalog.pg_get_viewdef (c.oid) "
- "FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n "
- "WHERE u.usesysid=c.relowner AND c.relkind = 'v' "
- "AND c.relnamespace=n.oid AND pg_catalog.pg_table_is_visible (c.oid) "
- "AND n.nspname NOT IN ('pg_catalog', 'pg_toast') "
- "ORDER BY relname",
- GDA_COMMAND_OPTION_STOP_ON_ERRORS);
-
- if (!reclist)
- return NULL;
-
- recset = GDA_DATA_MODEL (reclist->data);
- g_list_free (reclist);
-
- /* Set it here instead of the SQL query to allow i18n */
- gda_server_provider_init_schema_model (recset, GDA_CONNECTION_SCHEMA_VIEWS);
-
- return recset;
+ GdaTransactionStatus *trans;
+ trans = gda_connection_get_transaction_status (cnc);
+ if (!trans && !gda_connection_begin_transaction (cnc, NULL,
+ GDA_TRANSACTION_ISOLATION_UNKNOWN, NULL))
+ return FALSE;
+ else
+ return TRUE;
}
-static GdaDataModel *
-get_postgres_indexes (GdaConnection *cnc, GdaParameterList *params)
+/*
+ * Creates a "simple" prepared statement which has no variable, but does not call
+ * gda_connection_add_prepared_statement()
+ */
+static GdaPostgresPStmt *
+prepare_stmt_simple (PostgresConnectionData *cdata, const gchar *sql, GError **error)
{
- GList *reclist;
- GdaPostgresConnectionData *priv_data;
- GdaDataModel *recset;
-
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
-
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (priv_data->version_float < 7.3)
- reclist = process_sql_commands (NULL, cnc,
- "SELECT relname FROM pg_class WHERE relkind = 'i' AND "
- "relname !~ '^pg_' ORDER BY relname",
- GDA_COMMAND_OPTION_STOP_ON_ERRORS);
- else
- reclist = process_sql_commands (NULL, cnc,
- "SELECT c.relname "
- "FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "
- "WHERE relkind = 'i' "
- "AND n.oid = c.relnamespace "
- "AND pg_catalog.pg_table_is_visible(c.oid) "
- "AND n.nspname NOT IN ('pg_catalog', 'pg_toast') "
- "ORDER BY relname",
- GDA_COMMAND_OPTION_STOP_ON_ERRORS);
+ static guint counter = 0; /* each prepared statement MUST have a unique name, ensured with this counter */
+ PGresult *pg_res;
+ gchar *prep_stm_name;
+ GdaConnectionEvent *event = NULL;
- if (!reclist)
+ prep_stm_name = g_strdup_printf ("PS%d", counter++);
+ pg_res = PQprepare (cdata->pconn, prep_stm_name, sql, 0, NULL);
+ if (!pg_res || (PQresultStatus (pg_res) != PGRES_COMMAND_OK)) {
+ event = _gda_postgres_make_error (cdata->cnc, cdata->pconn, pg_res, error);
+ g_free (prep_stm_name);
+ if (pg_res)
+ PQclear (pg_res);
return NULL;
-
- recset = GDA_DATA_MODEL (reclist->data);
- g_list_free (reclist);
-
- /* Set it here instead of the SQL query to allow i18n */
- gda_server_provider_init_schema_model (recset, GDA_CONNECTION_SCHEMA_INDEXES);
-
- return recset;
-}
-
-static GdaDataModel *
-get_postgres_aggregates (GdaConnection *cnc, GdaParameterList *params)
-{
- GList *reclist;
- GdaPostgresConnectionData *priv_data;
- GdaDataModel *recset;
- GdaParameter *par = NULL;
- const gchar *namespace = NULL;
-
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
-
- if (params)
- par = gda_parameter_list_find_param (params, "namespace");
-
- if (par)
- namespace = g_value_get_string ((GValue *) gda_parameter_get_value (par));
-
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (priv_data->version_float < 7.3) {
- reclist = process_sql_commands (
- NULL, cnc,
- "(SELECT a.aggname, a.oid::varchar, usename, obj_description (a.oid), t2.typname, t1.typname, NULL "
- "FROM pg_aggregate a, pg_type t1, pg_type t2, pg_user u "
- "WHERE a.aggbasetype = t1.oid AND a.aggfinaltype = t2.oid AND u.usesysid=aggowner) UNION "
- "(SELECT a.aggname, a.oid, usename, obj_description (a.oid), t2.typname , '-', NULL "
- "FROM pg_aggregate a, pg_type t2, pg_user u "
- "WHERE a.aggfinaltype = t2.oid AND u.usesysid=aggowner AND a.aggbasetype = 0) ORDER BY 2",
- GDA_COMMAND_OPTION_STOP_ON_ERRORS);
}
else {
- if (namespace) {
- gchar *query;
-
- query = g_strdup_printf ("%s", namespace);
- reclist = process_sql_commands (NULL, cnc, query, GDA_COMMAND_OPTION_STOP_ON_ERRORS);
- g_free (query);
- }
- else {
- gchar *query;
-
- query = g_strdup_printf (
- "SELECT p.proname, p.oid::varchar, u.usename, pg_catalog.obj_description(p.oid),"
- "typo.typname,"
- "CASE p.proargtypes[0] "
- "WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype "
- "THEN CAST('-' AS pg_catalog.text) "
- "ELSE typi.typname "
- "END,"
- "NULL "
- "FROM pg_catalog.pg_proc p, pg_catalog.pg_user u, pg_catalog.pg_namespace n, "
- "pg_catalog.pg_type typi, pg_catalog.pg_type typo "
- "WHERE u.usesysid=p.proowner "
- "AND n.oid = p.pronamespace "
- "AND p.prorettype = typo.oid "
- "AND (typo.oid NOT IN (%s) OR typo.oid='%s') "
- "AND p.proargtypes[0] = typi.oid "
- "AND (typi.oid NOT IN (%s) OR typi.oid='%s') "
- "AND p.proisagg "
- "AND pg_catalog.pg_function_is_visible(p.oid) "
- "ORDER BY 2", priv_data->avoid_types_oids, priv_data->any_type_oid,
- priv_data->avoid_types_oids, priv_data->any_type_oid);
- reclist = process_sql_commands (NULL, cnc, query, GDA_COMMAND_OPTION_STOP_ON_ERRORS);
- g_free (query);
- }
+ GdaPostgresPStmt *ps;
+ PQclear (pg_res);
+ ps = gda_postgres_pstmt_new (cdata->cnc, cdata->pconn, prep_stm_name);
+ _GDA_PSTMT (ps)->param_ids = NULL;
+ _GDA_PSTMT (ps)->sql = g_strdup (sql);
+ return ps;
}
-
- if (!reclist)
- return NULL;
-
- recset = GDA_DATA_MODEL (reclist->data);
- g_list_free (reclist);
-
- /* Set it here instead of the SQL query to allow i18n */
- gda_server_provider_init_schema_model (recset, GDA_CONNECTION_SCHEMA_AGGREGATES);
-
- return recset;
}
-static GdaDataModel *
-get_postgres_triggers (GdaConnection *cnc, GdaParameterList *params)
+/*
+ * 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_postgres_provider_statement_execute (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GdaSet *params,
+ GdaStatementModelUsage model_usage,
+ GType *col_types, GdaSet **last_inserted_row,
+ guint *task_id,
+ GdaServerProviderAsyncCallback async_cb, gpointer cb_data, GError **error)
{
- GList *reclist;
- GdaDataModel *recset;
- GdaPostgresConnectionData *priv_data;
+ GdaPostgresPStmt *ps;
+ PostgresConnectionData *cdata;
g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, NULL);
+ g_return_val_if_fail (GDA_IS_STATEMENT (stmt), NULL);
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (priv_data->version_float < 7.3)
- reclist = process_sql_commands (NULL, cnc,
- "SELECT tgname FROM pg_trigger "
- "WHERE tgisconstraint = false "
- "ORDER BY tgname ",
- GDA_COMMAND_OPTION_STOP_ON_ERRORS);
- else
- /* REM: the triggers don't seem to belong to a particular schema... */
- reclist = process_sql_commands (NULL, cnc,
- "SELECT t.tgname "
- "FROM pg_catalog.pg_trigger t "
- "WHERE t.tgisconstraint = false "
- "ORDER BY t.tgname",
- GDA_COMMAND_OPTION_STOP_ON_ERRORS);
- if (!reclist)
- return NULL;
-
- recset = GDA_DATA_MODEL (reclist->data);
- g_list_free (reclist);
-
- /* Set it here instead of the SQL query to allow i18n */
- gda_server_provider_init_schema_model (recset, GDA_CONNECTION_SCHEMA_TRIGGERS);
+ /* 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,
+ _("Provider does not support asynchronous statement execution"));
+ return NULL;
+ }
- return recset;
-}
+ cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return NULL;
-static GList *
-gda_postgres_get_idx_data (GdaPostgresConnectionData *priv_data, const gchar *tblname)
-{
- gint nidx, i;
- GList *idx_list = NULL;
- GdaPostgresIdxData *idx_data;
- gchar *query;
- PGresult *pg_idx;
+ /*
+ * execute prepared statement using C API: CURSOR based
+ */
+ if (!(model_usage & GDA_STATEMENT_MODEL_RANDOM_ACCESS) &&
+ (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT)) {
+ static guint counter = 0; /* each cursor MUST have a unique name, ensured with this counter */
+ gchar *cursor_name;
+ gchar *cursor_sql;
+ PGresult *pg_res;
+ gchar *sql;
+ GdaDataModel *recset;
+ GdaConnectionEvent *event;
- if (priv_data->version_float < 7.3)
- query = g_strdup_printf ("SELECT i.indkey, i.indisprimary, i.indisunique "
- "FROM pg_class c, pg_class c2, pg_index i "
- "WHERE c.relname = '%s' AND c.oid = i.indrelid "
- "AND i.indexrelid = c2.oid", tblname);
- else
- query = g_strdup_printf ("SELECT i.indkey, i.indisprimary, i.indisunique "
- /*",pg_get_indexdef (i.indexrelid, 1, false) ": to get index definition */
- "FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i "
- "WHERE c.relname = '%s' "
- "AND c.oid = i.indrelid "
- "AND i.indexrelid = c2.oid "
- "AND pg_catalog.pg_table_is_visible(c.oid) AND i.indkey [0] <> 0", tblname);
-
- pg_idx = gda_postgres_PQexec_wrap (priv_data->cnc, priv_data->pconn, query);
- g_free (query);
- if (pg_idx == NULL)
- return NULL;
+ /* convert to SQL to remove any reference to a variable */
+ sql = gda_postgres_provider_statement_to_sql (provider, cnc, stmt, params, 0, NULL, error);
+ if (!sql)
+ return NULL;
- nidx = PQntuples (pg_idx);
+ /* create a prepared statement object for @sql */
+ ps = prepare_stmt_simple (cdata, sql, error);
+ if (!ps) {
+ g_free (sql);
+ return NULL;
+ }
- idx_data = g_new (GdaPostgresIdxData, nidx);
- for (i = 0; i < nidx; i++){
- gchar **arr, **ptr;
- gint ncolumns;
- gchar *value;
- gint k;
-
- value = PQgetvalue (pg_idx, i, 0);
- if (value && *value)
- arr = g_strsplit (value, " ", 0);
- else {
- idx_data[i].ncolumns = 0;
- continue;
+ /* declare a CURSOR and add a connection event for the execution*/
+ cursor_name = g_strdup_printf ("CURSOR%d", counter++);
+ cursor_sql = g_strdup_printf ("DECLARE %s SCROLL CURSOR WITH HOLD FOR %s", cursor_name, sql);
+ g_free (sql);
+ pg_res = _gda_postgres_PQexec_wrap (cnc, cdata->pconn, cursor_sql);
+ event = gda_connection_event_new (GDA_CONNECTION_EVENT_COMMAND);
+ gda_connection_event_set_description (event, cursor_sql);
+ gda_connection_add_event (cnc, event);
+ g_free (cursor_sql);
+ if (!pg_res || (PQresultStatus (pg_res) != PGRES_COMMAND_OK)) {
+ _gda_postgres_make_error (cnc, cdata->pconn, pg_res, error);
+ if (pg_res)
+ PQclear (pg_res);
+ g_object_unref (ps);
+ return NULL;
}
+ PQclear (pg_res);
- value = PQgetvalue (pg_idx, i, 1);
- idx_data[i].primary = (*value == 't') ? TRUE : FALSE;
- value = PQgetvalue (pg_idx, i, 2);
- idx_data[i].unique = (*value == 't') ? TRUE : FALSE;
-
- ptr = arr;
- ncolumns = 0;
- while (*ptr++)
- ncolumns++;
-
- idx_data[i].ncolumns = ncolumns;
- idx_data[i].columns = g_new (gint, ncolumns);
- for (k = 0; k < ncolumns; k++)
- idx_data[i].columns[k] = atoi (arr[k]) - 1;
-
- idx_list = g_list_append (idx_list, &idx_data[i]);
- g_strfreev (arr);
+ /* create data model in CURSOR mode */
+ recset = gda_postgres_recordset_new_cursor (cnc, ps, cursor_name, col_types);
+ gda_connection_internal_statement_executed (cnc, stmt, NULL); /* required: help @cnc keep some stats */
+ return (GObject*) recset;
}
- PQclear (pg_idx);
-
- return idx_list;
-}
-
-static gboolean
-gda_postgres_index_type (gint colnum, GList *idx_list, IdxType type)
-{
- GList *list;
- GdaPostgresIdxData *idx_data;
- gint i;
-
- if (idx_list == NULL || g_list_length (idx_list) == 0)
- return FALSE;
+ /* get/create new prepared statement */
+ ps = gda_connection_get_prepared_statement (cnc, stmt);
+ if (!ps) {
+ if (!gda_postgres_provider_statement_prepare (provider, cnc, stmt, NULL)) {
+ /* this case can appear for example if some variables are used in places
+ * 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 GdaPostgresPStmt object.
+ */
+ gchar *sql;
- for (list = idx_list; list; list = list->next) {
- idx_data = (GdaPostgresIdxData *) list->data;
- for (i = 0; i < idx_data->ncolumns; i++) {
- if (idx_data->columns[i] == colnum){
- return (type == IDX_PRIMARY) ?
- idx_data->primary : idx_data->unique;
- }
+ sql = gda_postgres_provider_statement_to_sql (provider, cnc, stmt, params, 0, NULL, error);
+ if (!sql)
+ return NULL;
+ ps = prepare_stmt_simple (cdata, sql, error);
+ g_free (sql);
+ if (!ps)
+ return NULL;
}
+ else
+ ps = gda_connection_get_prepared_statement (cnc, stmt);
}
+ g_assert (ps);
- return FALSE;
-}
-
-/* Converts a single dimension array in the form of '{AA,BB,CC}' to a list of
- * strings (here ->"AA"->"BB"->"CC) which must be freed by the caller
- */
-static GSList *
-gda_postgres_itemize_simple_array (const gchar *array)
-{
- GSList *list = NULL;
- gchar *str, *ptr, *tok;
-
- g_return_val_if_fail (array, NULL);
-
- str = g_strdup (array);
- ptr = str;
-
- /* stripping { and } */
- if (*str == '{')
- ptr++;
- if (str [strlen (str)-1] == '}')
- str [strlen (str)-1] = 0;
-
- /* splitting */
- ptr = strtok_r (ptr, ",", &tok);
- while (ptr && *ptr) {
- list = g_slist_append (list, g_strdup (ptr));
- ptr = strtok_r (NULL, ",", &tok);
- }
-
- g_free (str);
-
- return list;
-}
-
-static void
-gda_postgres_itemize_simple_array_free (GSList *list)
-{
- GSList *list2;
-
- list2 = list;
- while (list2) {
- g_free (list2->data);
- list2 = g_slist_next (list2);
- }
- g_slist_free (list);
-}
-
-static GList *
-gda_postgres_get_ref_data (GdaPostgresConnectionData *priv_data, const gchar *tblname)
-{
- gint nref, i;
- GList *ref_list = NULL;
- GdaPostgresRefData *ref_data;
- gchar *query;
- PGresult *pg_ref;
-
- if (priv_data->version_float < 7.3)
- query = g_strdup_printf ("SELECT tr.tgargs "
- "FROM pg_class c, pg_trigger tr "
- "WHERE c.relname = '%s' AND c.oid = tr.tgrelid AND "
- "tr.tgisconstraint = true AND tr.tgnargs = 6", tblname);
- else
- query = g_strdup_printf ("SELECT o.conkey, o.confkey, fc.relname "
- "FROM pg_catalog.pg_class c "
- "INNER JOIN pg_catalog.pg_constraint o ON (o.conrelid = c.oid) "
- "LEFT JOIN pg_catalog.pg_class fc ON (fc.oid=o.confrelid) "
- "WHERE c.relname = '%s' AND contype = 'f' "
- "AND pg_catalog.pg_table_is_visible (c.oid) "
- "AND pg_catalog.pg_table_is_visible (fc.oid)", tblname);
-
- pg_ref = gda_postgres_PQexec_wrap (priv_data->cnc, priv_data->pconn, query);
- g_free (query);
- if (pg_ref == NULL)
- return NULL;
-
- nref = PQntuples (pg_ref);
+ /* bind statement's parameters */
+ GSList *list;
+ GdaConnectionEvent *event = NULL;
+ int i;
+ char **param_values = NULL;
+ int *param_lengths = NULL;
+ int *param_formats = NULL;
+ gint nb_params;
+
+ nb_params = g_slist_length (_GDA_PSTMT (ps)->param_ids);
+ param_values = g_new0 (char *, nb_params + 1);
+ param_lengths = g_new0 (int, nb_params + 1);
+ param_formats = g_new0 (int, nb_params + 1);
+
+ for (i = 0, 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,
+ _("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) {
+ 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, str);
+ g_free (str);
+ break;
+ }
- for (i = 0; i < nref; i++){
- if (priv_data->version_float < 7.3) {
- gint lentblname;
- gchar **arr;
- gchar *value;
+ /* actual binding using the C API, for parameter at position @i */
+ const GValue *value = gda_holder_get_value (h);
+ if (!value || gda_value_is_null (value))
+ param_values [i] = NULL;
+ else if (G_VALUE_TYPE (value) == GDA_TYPE_BLOB) {
+ /* specific BLOB treatment */
+ GdaBlob *blob = (GdaBlob*) gda_value_get_blob ((GValue *) value);
+ GdaPostgresBlobOp *op;
- lentblname = strlen (tblname);
- ref_data = g_new0 (GdaPostgresRefData, 1);
- value = PQgetvalue (pg_ref, i, 0);
- arr = g_strsplit (value, "\\000", 0);
- if (!strncmp (tblname, arr[1], lentblname)) {
- ref_data->colname = g_strdup (arr[4]);
- ref_data->reference = g_strdup_printf ("%s.%s", arr[2], arr[5]);
- ref_list = g_list_append (ref_list, ref_data);
+ /* Postgres requires that a transaction be started for LOB operations */
+ if (!check_transaction_started (cnc)) {
+ event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
+ gda_connection_event_set_description (event, _("Cannot start transaction"));
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR,
+ _("Cannot start transaction"));
+ break;
}
- g_strfreev (arr);
- }
- else {
- gchar *value;
- GSList *itemf, *listf;
- GSList *itemp, *listp;
-
- value = PQgetvalue (pg_ref, i, 0);
- listf = gda_postgres_itemize_simple_array (value);
- itemf = listf;
-
- value = PQgetvalue (pg_ref, i, 1);
- listp = gda_postgres_itemize_simple_array (value);
- itemp = listp;
-
- g_assert (g_slist_length (listf) == g_slist_length (listp));
- while (itemp && itemf) {
- PGresult *pg_res;
- query = g_strdup_printf ("SELECT a.attname FROM pg_catalog.pg_class c "
- "LEFT JOIN pg_catalog.pg_attribute a ON (a.attrelid = c.oid) "
- "WHERE c.relname = '%s' AND pg_catalog.pg_table_is_visible (c.oid) "
- "AND a.attnum = %d AND NOT a.attisdropped",
- PQgetvalue (pg_ref, i, 2), atoi (itemp->data));
- pg_res = gda_postgres_PQexec_wrap (priv_data->cnc, priv_data->pconn, query);
- /*g_print ("Query: %s\n", query);*/
- g_free (query);
- if (pg_res == NULL)
- return NULL;
-
- g_assert (PQntuples (pg_res) == 1);
-
- ref_data = g_new0 (GdaPostgresRefData, 1);
- ref_data->colname = NULL;
- ref_data->colnum = atoi (itemf->data);
- ref_data->reference = g_strdup_printf ("%s.%s",
- PQgetvalue (pg_ref, i, 2), PQgetvalue (pg_res, 0, 0));
- PQclear (pg_res);
- ref_list = g_list_append (ref_list, ref_data);
- itemp = g_slist_next (itemp);
- itemf = g_slist_next (itemf);
- }
- gda_postgres_itemize_simple_array_free (listf);
- gda_postgres_itemize_simple_array_free (listp);
- }
- }
- PQclear (pg_ref);
-
- return ref_list;
-}
-
-static void
-free_idx_data (gpointer data, gpointer user_data)
-{
- GdaPostgresIdxData *idx_data = (GdaPostgresIdxData *) data;
-
- g_free (idx_data->columns);
-}
-
-static void
-free_ref_data (gpointer data, gpointer user_data)
-{
- GdaPostgresRefData *ref_data = (GdaPostgresRefData *) data;
-
- if (ref_data->colname)
- g_free (ref_data->colname);
- g_free (ref_data->reference);
- g_free (ref_data);
-}
-
-/* FIXME: a and b are of the same type! */
-static gint
-ref_custom_compare (gconstpointer a, gconstpointer b)
-{
- GdaPostgresRefData *ref_data = (GdaPostgresRefData *) a;
- gchar *colname = (gchar *) b;
-
- if (!strcmp (ref_data->colname, colname))
- return 0;
-
- return 1;
-}
-
-static GList *
-gda_postgres_fill_md_data (const gchar *tblname, GdaDataModelArray *recset,
- GdaPostgresConnectionData *priv_data)
-{
- gchar *query;
- PGresult *pg_res;
- gint row_count, i;
- GList *list = NULL;
- GList *idx_list;
- GList *ref_list;
-
- if (priv_data->version_float < 7.3)
- query = g_strdup_printf (
- "(SELECT a.attname, b.typname, a.atttypmod, b.typlen, a.attnotnull, d.adsrc, "
- "a.attnum FROM pg_class c, pg_attribute a, pg_type b, pg_attrdef d "
- "WHERE c.relname = '%s' AND a.attnum > 0 AND "
- "a.attrelid = c.oid and b.oid = a.atttypid AND "
- "a.atthasdef = 't' and d.adrelid=c.oid and d.adnum=a.attnum) "
- "UNION (SELECT a.attname, b.typname, a.atttypmod, b.typlen, a.attnotnull, NULL, "
- "a.attnum FROM pg_class c, pg_attribute a, pg_type b "
- "WHERE c.relname = '%s' AND a.attnum > 0 AND "
- "a.attrelid = c.oid and b.oid = a.atttypid AND a.atthasdef = 'f') ORDER BY 7",
- tblname, tblname);
- else
- query = g_strdup_printf (
- "SELECT a.attname, t.typname, a.atttypmod, t.typlen, a.attnotnull, pg_get_expr (d.adbin, c.oid), a.attnum "
- "FROM pg_catalog.pg_class c "
- "LEFT JOIN pg_catalog.pg_attribute a ON (a.attrelid = c.oid) "
- "FULL JOIN pg_catalog.pg_attrdef d ON (a.attnum = d.adnum AND d.adrelid=c.oid) "
- "LEFT JOIN pg_catalog.pg_type t ON (t.oid = a.atttypid) "
- "WHERE c.relname = '%s' AND pg_catalog.pg_table_is_visible (c.oid) AND a.attnum > 0 "
- "AND NOT a.attisdropped ORDER BY 7", tblname);
-
- pg_res = gda_postgres_PQexec_wrap (priv_data->cnc, priv_data->pconn, query);
- g_free (query);
- if (pg_res == NULL)
- return NULL;
-
- idx_list = gda_postgres_get_idx_data (priv_data, tblname);
- ref_list = gda_postgres_get_ref_data (priv_data, tblname);
-
- row_count = PQntuples (pg_res);
- for (i = 0; i < row_count; i++) {
- GValue *value;
- gchar *thevalue, *colname, *ref = NULL;
- GType type;
- gint32 integer;
- GList *rowlist = NULL;
- GList *rlist;
-
- /* Field name */
- colname = thevalue = PQgetvalue (pg_res, i, 0);
- g_value_set_string (value = gda_value_new (G_TYPE_STRING), thevalue);
- rowlist = g_list_append (rowlist, value);
-
- /* Data type */
- thevalue = PQgetvalue(pg_res, i, 1);
- type = gda_postgres_type_name_to_gda (priv_data->h_table,
- thevalue);
- g_value_set_string (value = gda_value_new (G_TYPE_STRING), thevalue);
- rowlist = g_list_append (rowlist, value);
-
- /* Defined size */
- thevalue = PQgetvalue(pg_res, i, 3);
- integer = atoi (thevalue);
- if (integer == -1 && type == G_TYPE_STRING) {
- thevalue = PQgetvalue(pg_res, i, 2);
- integer = atoi (thevalue) - 4; /* don't know where the -4 comes from! */
- }
- if (integer == -1 && type == GDA_TYPE_NUMERIC) {
- thevalue = PQgetvalue(pg_res, i, 2);
- integer = atoi(thevalue) / 65536;
- }
+ /* create GdaBlobOp */
+ op = (GdaPostgresBlobOp *) gda_postgres_blob_op_new (cnc);
- g_value_set_int (value = gda_value_new (G_TYPE_INT), (integer != -1) ? integer : 0);
- rowlist = g_list_append (rowlist, value);
-
- /* Scale */
- integer = 0;
- if (type == GDA_TYPE_NUMERIC) {
- thevalue = PQgetvalue(pg_res, i, 2);
- integer = (atoi(thevalue) % 65536) - 4;
- }
- g_value_set_int (value = gda_value_new (G_TYPE_INT), integer);
- rowlist = g_list_append (rowlist, value);
-
- /* Not null? */
- thevalue = PQgetvalue(pg_res, i, 4);
- g_value_set_boolean (value = gda_value_new (G_TYPE_BOOLEAN), (*thevalue == 't' ? TRUE : FALSE));
- rowlist = g_list_append (rowlist, value);
-
- /* Primary key? */
- g_value_set_boolean (value = gda_value_new (G_TYPE_BOOLEAN),
- gda_postgres_index_type (i, idx_list, IDX_PRIMARY));
- rowlist = g_list_append (rowlist, value);
-
- /* Unique index? */
- g_value_set_boolean (value = gda_value_new (G_TYPE_BOOLEAN),
- gda_postgres_index_type (i, idx_list, IDX_UNIQUE));
- rowlist = g_list_append (rowlist, value);
-
- /* References */
- if (priv_data->version_float < 7.3) {
- rlist = g_list_find_custom (ref_list, colname, ref_custom_compare);
- if (rlist)
- ref = ((GdaPostgresRefData *) rlist->data)->reference;
+ /* always create a new blob as there is no way to truncate an existing blob */
+ if (gda_postgres_blob_op_declare_blob (op) &&
+ gda_blob_op_write ((GdaBlobOp*) op, blob, 0))
+ param_values [i] = gda_postgres_blob_op_get_id (op);
else
- ref = "";
+ param_values [i] = NULL;
+ g_object_unref (op);
}
- else {
- GList *plist = ref_list;
- ref = NULL;
+ else if (G_VALUE_TYPE (value) == GDA_TYPE_BINARY) {
+ /* directly bin binary data */
+ GdaBinary *bin = (GdaBinary *) gda_value_get_binary ((GValue *) value);
+ param_values [i] = (gchar*) bin->data;
+ param_lengths [i] = bin->binary_length;
+ param_formats [i] = 1; /* binary format */
+ }
+ else if ((G_VALUE_TYPE (value) == G_TYPE_DATE) ||
+ (G_VALUE_TYPE (value) == GDA_TYPE_TIMESTAMP) ||
+ (G_VALUE_TYPE (value) == GDA_TYPE_TIME)) {
+ GdaHandlerTime *timdh;
- while (plist && !ref) {
- if (((GdaPostgresRefData *) plist->data)->colnum == atoi (PQgetvalue(pg_res, i, 6)))
- ref = ((GdaPostgresRefData *) plist->data)->reference;
- plist = g_list_next (plist);
- }
- if (!ref)
- ref = "";
+ timdh = GDA_HANDLER_TIME (gda_server_provider_get_data_handler_gtype (provider, cnc,
+ G_VALUE_TYPE (value)));
+ g_assert (timdh);
+ param_values [i] = gda_handler_time_get_no_locale_str_from_value (timdh, value);
}
- g_value_set_string (value = gda_value_new (G_TYPE_STRING), ref);
- rowlist = g_list_append (rowlist, value);
+ else {
+ GdaDataHandler *dh;
- /* Default value */
- if (!PQgetisnull (pg_res, i, 5)) {
- thevalue = PQgetvalue (pg_res, i, 5);
- g_value_set_string (value = gda_value_new (G_TYPE_STRING), thevalue);
+ dh = gda_server_provider_get_data_handler_gtype (provider, cnc, G_VALUE_TYPE (value));
+ if (dh)
+ param_values [i] = gda_data_handler_get_str_from_value (dh, value);
+ else
+ param_values [i] = NULL;
}
- else
- value = gda_value_new_null ();
- rowlist = g_list_append (rowlist, value);
-
- /* Extra attributes */
- if (strstr (PQgetvalue (pg_res, i, 5), "nextval"))
- g_value_set_string (value = gda_value_new (G_TYPE_STRING), "AUTO_INCREMENT");
- else
- g_value_set_string (value = gda_value_new (G_TYPE_STRING), "");
- rowlist = g_list_append (rowlist, value);
-
- list = g_list_append (list, rowlist);
- rowlist = NULL;
}
-
- PQclear (pg_res);
-
- if (idx_list && idx_list->data) {
- g_list_foreach (idx_list, free_idx_data, NULL);
- g_free (idx_list->data);
- }
-
- if (ref_list && ref_list->data)
- g_list_foreach (ref_list, free_ref_data, NULL);
-
- g_list_free (ref_list);
- g_list_free (idx_list);
-
- return list;
-}
-
-static void
-add_g_list_row (gpointer data, gpointer user_data)
-{
- GList *rowlist = data;
- GdaDataModelArray *recset = user_data;
-
- gda_data_model_append_values (GDA_DATA_MODEL (recset), rowlist, NULL);
- g_list_foreach (rowlist, (GFunc) gda_value_free, NULL);
- g_list_free (rowlist);
-}
-
-static GdaDataModel *
-get_postgres_fields_metadata (GdaConnection *cnc, GdaParameterList *params)
-{
- GList *list;
- GdaPostgresConnectionData *priv_data;
- GdaDataModelArray *recset;
- GdaParameter *par;
- const gchar *tblname;
-
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
- g_return_val_if_fail (params != NULL, NULL);
-
- par = gda_parameter_list_find_param (params, "name");
- g_return_val_if_fail (par != NULL, NULL);
-
- tblname = g_value_get_string ((GValue *) gda_parameter_get_value (par));
- g_return_val_if_fail (tblname != NULL, NULL);
-
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
-
- recset = GDA_DATA_MODEL_ARRAY (gda_data_model_array_new
- (gda_server_provider_get_schema_nb_columns (GDA_CONNECTION_SCHEMA_FIELDS)));
- gda_server_provider_init_schema_model (GDA_DATA_MODEL (recset), GDA_CONNECTION_SCHEMA_FIELDS);
- list = gda_postgres_fill_md_data (tblname, recset, priv_data);
- g_list_foreach (list, add_g_list_row, recset);
- g_list_free (list);
-
- return GDA_DATA_MODEL (recset);
-}
-
-static GdaDataModel *
-get_postgres_databases (GdaConnection *cnc, GdaParameterList *params)
-{
- GList *reclist;
- GdaPostgresConnectionData *priv_data;
- GdaDataModel *recset;
-
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
-
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
-
- if (priv_data->version_float < 7.3)
- reclist = process_sql_commands (NULL, cnc,
- "SELECT datname "
- "FROM pg_database "
- "ORDER BY 1",
- GDA_COMMAND_OPTION_STOP_ON_ERRORS);
- else
- reclist = process_sql_commands (NULL, cnc,
- "SELECT datname "
- "FROM pg_catalog.pg_database "
- "ORDER BY 1",
- GDA_COMMAND_OPTION_STOP_ON_ERRORS);
-
- if (!reclist)
- return NULL;
-
- recset = GDA_DATA_MODEL (reclist->data);
- g_list_free (reclist);
-
- /* Set it here instead of the SQL query to allow i18n */
- gda_server_provider_init_schema_model (recset, GDA_CONNECTION_SCHEMA_DATABASES);
-
- return recset;
-}
-
-static GdaDataModel *
-get_postgres_users (GdaConnection *cnc, GdaParameterList *params)
-{
- GList *reclist;
- GdaDataModel *recset;
- GdaPostgresConnectionData *priv_data;
-
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
-
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
-
- if (priv_data->version_float < 7.3)
- reclist = process_sql_commands (NULL, cnc,
- "SELECT usename "
- "FROM pg_user "
- "ORDER BY usename",
- GDA_COMMAND_OPTION_STOP_ON_ERRORS);
- else
- reclist = process_sql_commands (NULL, cnc,
- "SELECT u.usename "
- "FROM pg_catalog.pg_user u"
- "ORDER BY u.usename",
- GDA_COMMAND_OPTION_STOP_ON_ERRORS);
-
- if (!reclist)
- return NULL;
-
- recset = GDA_DATA_MODEL (reclist->data);
- g_list_free (reclist);
-
- /* Set it here instead of the SQL query to allow i18n */
- gda_server_provider_init_schema_model (recset, GDA_CONNECTION_SCHEMA_USERS);
-
- return recset;
-}
-
-static GdaDataModel *
-get_postgres_sequences (GdaConnection *cnc, GdaParameterList *params)
-{
- GList *reclist;
- GdaDataModel *recset;
- GdaPostgresConnectionData *priv_data;
-
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
-
-
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
-
- if (priv_data->version_float < 7.3)
- reclist = process_sql_commands (
- NULL, cnc,
- "SELECT relname, usename, obj_description(pg_class.oid), NULL "
- "FROM pg_class, pg_user "
- "WHERE usesysid=relowner AND relkind = 'S' AND relname !~ '^pg_' ORDER BY relname",
- GDA_COMMAND_OPTION_STOP_ON_ERRORS);
- else
- reclist = process_sql_commands (
- NULL, cnc,
- "SELECT c.relname, u.usename, obj_description (c.oid), NULL "
- "FROM pg_catalog.pg_class c, pg_catalog.pg_user u "
- "WHERE u.usesysid = c.relowner "
- "AND c.relkind = 'S' "
- "AND pg_catalog.pg_table_is_visible(c.oid) "
- "ORDER BY relname",
- GDA_COMMAND_OPTION_STOP_ON_ERRORS);
-
- if (!reclist)
- return NULL;
-
- recset = GDA_DATA_MODEL (reclist->data);
- g_list_free (reclist);
-
- /* Set it here instead of the SQL query to allow i18n */
- gda_server_provider_init_schema_model (recset, GDA_CONNECTION_SCHEMA_SEQUENCES);
-
- return recset;
-}
-
-static GdaDataModel *
-get_postgres_parent_tables (GdaConnection *cnc, GdaParameterList *params)
-{
- GList *reclist;
- GdaDataModel *recset;
- gchar *query;
- const gchar *tblname;
- GdaParameter *par;
- GdaPostgresConnectionData *priv_data;
-
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
- g_return_val_if_fail (params != NULL, NULL);
-
- par = gda_parameter_list_find_param (params, "name");
- g_return_val_if_fail (par != NULL, NULL);
-
- tblname = g_value_get_string ((GValue *) gda_parameter_get_value (par));
- g_return_val_if_fail (tblname != NULL, NULL);
-
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
-
- if (priv_data->version_float < 7.3)
- query = g_strdup_printf ("SELECT a.relname, b.inhseqno "
- "FROM pg_inherits b, pg_class a, pg_class c "
- "WHERE a.oid=b.inhparent AND b.inhrelid = c.oid "
- "AND c.relname = '%s' ORDER BY b.inhseqno",
- tblname);
- else
- query = g_strdup_printf ("SELECT p.relname, h.inhseqno "
- "FROM pg_catalog.pg_inherits h, pg_catalog.pg_class p, pg_catalog.pg_class c "
- "WHERE pg_catalog.pg_table_is_visible(c.oid) "
- "AND pg_catalog.pg_table_is_visible(p.oid) "
- "AND p.oid = h.inhparent "
- "AND h.inhrelid = c.oid "
- "AND c.relname = '%s' "
- "ORDER BY h.inhseqno",
- tblname);
-
- reclist = process_sql_commands (NULL, cnc, query,
- GDA_COMMAND_OPTION_STOP_ON_ERRORS);
- g_free (query);
- if (!reclist)
- return NULL;
-
- recset = GDA_DATA_MODEL (reclist->data);
- g_list_free (reclist);
-
- /* Set it here instead of the SQL query to allow i18n */
- gda_server_provider_init_schema_model (recset, GDA_CONNECTION_SCHEMA_PARENT_TABLES);
-
- return recset;
-}
-
-static GdaDataModel *
-get_postgres_constraints (GdaConnection *cnc, GdaParameterList *params)
-{
- GdaPostgresConnectionData *priv_data;
- GdaDataModelArray *recset;
- GdaParameter *par;
- const gchar *tblname;
-
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
- g_return_val_if_fail (params != NULL, NULL);
-
- par = gda_parameter_list_find_param (params, "name");
- g_return_val_if_fail (par != NULL, NULL);
-
- tblname = g_value_get_string ((GValue *) gda_parameter_get_value (par));
- g_return_val_if_fail (tblname != NULL, NULL);
-
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
-
- recset = GDA_DATA_MODEL_ARRAY (gda_data_model_array_new
- (gda_server_provider_get_schema_nb_columns (GDA_CONNECTION_SCHEMA_CONSTRAINTS)));
- gda_server_provider_init_schema_model (GDA_DATA_MODEL (recset), GDA_CONNECTION_SCHEMA_CONSTRAINTS);
-
- TO_IMPLEMENT;
-
- return GDA_DATA_MODEL (recset);
-}
-
-
-/* get_schema handler for the GdaPostgresProvider class */
-static GdaDataModel *
-gda_postgres_provider_get_schema (GdaServerProvider *provider,
- GdaConnection *cnc,
- GdaConnectionSchema schema,
- GdaParameterList *params)
-{
- g_return_val_if_fail (GDA_IS_SERVER_PROVIDER (provider), NULL);
- if (cnc)
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
- else
+ if (event) {
+ gda_connection_add_event (cnc, event);
+ g_strfreev (param_values);
+ g_free (param_lengths);
+ g_free (param_formats);
return NULL;
-
- switch (schema) {
- case GDA_CONNECTION_SCHEMA_AGGREGATES :
- return get_postgres_aggregates (cnc, params);
- case GDA_CONNECTION_SCHEMA_DATABASES :
- return get_postgres_databases (cnc, params);
- case GDA_CONNECTION_SCHEMA_INDEXES :
- return get_postgres_indexes (cnc, params);
- case GDA_CONNECTION_SCHEMA_FIELDS :
- return get_postgres_fields_metadata (cnc, params);
- case GDA_CONNECTION_SCHEMA_PROCEDURES :
- return get_postgres_procedures (cnc, params);
- case GDA_CONNECTION_SCHEMA_SEQUENCES :
- return get_postgres_sequences (cnc, params);
- case GDA_CONNECTION_SCHEMA_TABLES :
- return get_postgres_tables (cnc, params);
- case GDA_CONNECTION_SCHEMA_TRIGGERS :
- return get_postgres_triggers (cnc, params);
- case GDA_CONNECTION_SCHEMA_TYPES :
- return get_postgres_types (cnc, params);
- case GDA_CONNECTION_SCHEMA_USERS :
- return get_postgres_users (cnc, params);
- case GDA_CONNECTION_SCHEMA_VIEWS :
- return get_postgres_views (cnc, params);
- case GDA_CONNECTION_SCHEMA_PARENT_TABLES :
- return get_postgres_parent_tables (cnc, params);
- case GDA_CONNECTION_SCHEMA_CONSTRAINTS :
- return get_postgres_constraints (cnc, params);
- default:
- break;
}
-
- return NULL;
-}
-
-static GdaDataHandler *
-gda_postgres_provider_get_data_handler (GdaServerProvider *provider,
- GdaConnection *cnc,
- GType type,
- const gchar *dbms_type)
-{
- GdaDataHandler *dh = NULL;
- g_return_val_if_fail (GDA_IS_SERVER_PROVIDER (provider), 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 ((type == G_TYPE_INT64) ||
- (type == G_TYPE_UINT64) ||
- (type == G_TYPE_DOUBLE) ||
- (type == G_TYPE_INT) ||
- (type == GDA_TYPE_NUMERIC) ||
- (type == G_TYPE_FLOAT) ||
- (type == GDA_TYPE_SHORT) ||
- (type == GDA_TYPE_USHORT) ||
- (type == G_TYPE_CHAR) ||
- (type == G_TYPE_UCHAR) ||
- (type == G_TYPE_UINT)) {
- dh = gda_server_provider_handler_find (provider, NULL, type, NULL);
- if (!dh) {
- dh = gda_handler_numerical_new ();
- gda_server_provider_handler_declare (provider, dh, NULL, G_TYPE_INT64, NULL);
- gda_server_provider_handler_declare (provider, dh, NULL, G_TYPE_UINT64, NULL);
- gda_server_provider_handler_declare (provider, dh, NULL, G_TYPE_DOUBLE, NULL);
- gda_server_provider_handler_declare (provider, dh, NULL, G_TYPE_INT, NULL);
- gda_server_provider_handler_declare (provider, dh, NULL, GDA_TYPE_NUMERIC, NULL);
- gda_server_provider_handler_declare (provider, dh, NULL, G_TYPE_FLOAT, NULL);
- gda_server_provider_handler_declare (provider, dh, NULL, GDA_TYPE_SHORT, NULL);
- gda_server_provider_handler_declare (provider, dh, NULL, GDA_TYPE_USHORT, NULL);
- gda_server_provider_handler_declare (provider, dh, NULL, G_TYPE_CHAR, NULL);
- gda_server_provider_handler_declare (provider, dh, NULL, G_TYPE_UCHAR, NULL);
- gda_server_provider_handler_declare (provider, dh, NULL, G_TYPE_UINT, NULL);
- g_object_unref (dh);
- }
- }
- else if ((type == GDA_TYPE_BINARY) ||
- (type == GDA_TYPE_BLOB)){
- dh = gda_server_provider_handler_find (provider, cnc, type, NULL);
- if (!dh) {
- dh = gda_postgres_handler_bin_new (cnc);
- if (dh) {
- gda_server_provider_handler_declare (provider, dh, cnc, GDA_TYPE_BINARY, NULL);
- gda_server_provider_handler_declare (provider, dh, cnc, GDA_TYPE_BLOB, NULL);
- g_object_unref (dh);
- }
- }
- }
- else if (type == G_TYPE_BOOLEAN) {
- dh = gda_server_provider_handler_find (provider, NULL, type, NULL);
- if (!dh) {
- dh = gda_handler_boolean_new ();
- gda_server_provider_handler_declare (provider, dh, NULL, G_TYPE_BOOLEAN, NULL);
- g_object_unref (dh);
- }
- }
- else if ((type == G_TYPE_DATE) ||
- (type == GDA_TYPE_TIME) ||
- (type == GDA_TYPE_TIMESTAMP)) {
- dh = gda_server_provider_handler_find (provider, NULL, type, NULL);
- if (!dh) {
- dh = gda_handler_time_new ();
- gda_handler_time_set_sql_spec ((GdaHandlerTime *) dh, G_DATE_YEAR,
- G_DATE_MONTH, G_DATE_DAY, '-', FALSE);
- gda_server_provider_handler_declare (provider, dh, NULL, G_TYPE_DATE, NULL);
- gda_server_provider_handler_declare (provider, dh, NULL, GDA_TYPE_TIME, NULL);
- gda_server_provider_handler_declare (provider, dh, NULL, GDA_TYPE_TIMESTAMP, NULL);
- g_object_unref (dh);
- }
- }
- else if (type == G_TYPE_STRING) {
- dh = gda_server_provider_handler_find (provider, NULL, type, NULL);
- if (!dh) {
- dh = gda_handler_string_new_with_provider (provider, cnc);
- gda_server_provider_handler_declare (provider, dh, NULL, G_TYPE_STRING, NULL);
- g_object_unref (dh);
- }
- }
- else if (type == G_TYPE_ULONG) {
- dh = gda_server_provider_handler_find (provider, NULL, type, NULL);
- if (!dh) {
- dh = gda_handler_type_new ();
- gda_server_provider_handler_declare (provider, dh, NULL, G_TYPE_ULONG, NULL);
- g_object_unref (dh);
- }
- }
+ /* execute prepared statement using C API: random access based */
+ PGresult *pg_res;
+ GObject *retval = NULL;
+ pg_res = PQexecPrepared (cdata->pconn, ps->prep_name, nb_params, (const char * const *) param_values,
+ param_lengths, param_formats, 0);
+ if (!pg_res)
+ event = _gda_postgres_make_error (cnc, cdata->pconn, NULL, error);
else {
- if (dbms_type) {
- GdaPostgresConnectionData *priv_data = NULL;
-
- if (cnc) {
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- }
- if (priv_data) {
- gint i;
- GdaPostgresTypeOid *td = NULL;
-
- for (i = 0; i < priv_data->ntypes; i++) {
- if (!strcmp (priv_data->type_data[i].name, dbms_type))
- td = &(priv_data->type_data[i]);
- }
-
- if (td) {
- dh = gda_postgres_provider_get_data_handler (provider, cnc, td->type, NULL);
- gda_server_provider_handler_declare (provider, dh, cnc,
- G_TYPE_INVALID, dbms_type);
+ int status;
+ status = PQresultStatus (pg_res);
+ if (status == PGRES_EMPTY_QUERY ||
+ status == PGRES_TUPLES_OK ||
+ status == PGRES_COMMAND_OK) {
+ if (status == PGRES_COMMAND_OK) {
+ gchar *str;
+ GdaConnectionEvent *event;
+
+ event = gda_connection_event_new (GDA_CONNECTION_EVENT_NOTICE);
+ str = g_strdup (PQcmdStatus (pg_res));
+ gda_connection_event_set_description (event, str);
+ g_free (str);
+ gda_connection_add_event (cnc, event);
+ retval = (GObject *) gda_set_new_inline (1, "IMPACTED_ROWS", G_TYPE_INT,
+ atoi (PQcmdTuples (pg_res)));
+
+ if ((PQoidValue (pg_res) != InvalidOid) && last_inserted_row) {
+ TO_IMPLEMENT;
+ /* use PQoidValue (pg_res); */
}
- }
- else {
- dh = gda_postgres_provider_get_data_handler (provider, cnc,
- postgres_name_to_g_type (dbms_type, NULL),
- NULL);
- gda_server_provider_handler_declare (provider, dh, cnc,
- G_TYPE_INVALID, dbms_type);
- }
- }
- }
-
- return dh;
-}
-
-static const gchar*
-gda_postgres_provider_get_default_dbms_type (GdaServerProvider *provider,
- GdaConnection *cnc,
- GType type)
-{
- g_return_val_if_fail (GDA_IS_SERVER_PROVIDER (provider), NULL);
-
- if (type == G_TYPE_INT64)
- return "int8";
- if (type == G_TYPE_UINT64)
- return "int8";
- if (type == GDA_TYPE_BINARY)
- return "bytea";
- if (type == GDA_TYPE_BLOB)
- return "oid";
- if (type == G_TYPE_BOOLEAN)
- return "bool";
- if (type == G_TYPE_DATE)
- return "date";
- if (type == G_TYPE_DOUBLE)
- return "float8";
- if (type == GDA_TYPE_GEOMETRIC_POINT)
- return "point";
- if (type == G_TYPE_OBJECT)
- return "text";
- if (type == G_TYPE_INT)
- return "int4";
- if (type == GDA_TYPE_LIST)
- return "text";
- if (type == GDA_TYPE_NUMERIC)
- return "numeric";
- if (type == G_TYPE_FLOAT)
- return "float4";
- if (type == GDA_TYPE_SHORT)
- return "int2";
- if (type == GDA_TYPE_USHORT)
- return "int2";
- if (type == G_TYPE_STRING)
- return "varchar";
- if (type == GDA_TYPE_TIME)
- return "time";
- if (type == GDA_TYPE_TIMESTAMP)
- return "timestamp";
- if (type == G_TYPE_CHAR)
- return "smallint";
- if (type == G_TYPE_UCHAR)
- return "smallint";
- if (type == G_TYPE_ULONG)
- return "int8";
- if (type == G_TYPE_UINT)
- return "int4";
- if (type == G_TYPE_INVALID)
- return "text";
-
- return "text";
-}
-
-static gchar *
-gda_postgres_provider_escape_string (GdaServerProvider *provider, GdaConnection *cnc, const gchar *str)
-{
- GdaPostgresConnectionData *priv_data = NULL;
- gchar *dest;
- gint length;
-
- g_return_val_if_fail (GDA_IS_SERVER_PROVIDER (provider), NULL);
- if (cnc) {
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
-
- priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
- if (!priv_data) {
- gda_connection_add_event_string (cnc, _("Invalid PostgreSQL handle"));
- return NULL;
- }
- }
-
- length = strlen (str);
- dest = g_new (gchar, 2*length + 1);
- if (0 && priv_data) {
- /* FIXME: use this call but it's only defined for Postgres >= 8.1 */
- /*PQescapeStringConn (priv_data->pconnpconn, dest, str, length, NULL);*/
+ PQclear (pg_res);
+ }
+ else if (status == PGRES_TUPLES_OK)
+ retval = (GObject*) gda_postgres_recordset_new_random (cnc, ps, pg_res, col_types);
+ else {
+ PQclear (pg_res);
+ retval = (GObject *) gda_data_model_array_new (0);
+ }
+ }
+ else
+ event = _gda_postgres_make_error (cnc, cdata->pconn, NULL, error);
}
- else
- PQescapeString (dest, str, length);
- return dest;
+ gda_connection_internal_statement_executed (cnc, stmt, NULL); /* required: help @cnc keep some stats */
+ return retval;
}
-static gchar *
-gda_postgres_provider_unescape_string (GdaServerProvider *provider, GdaConnection *cnc, const gchar *str)
+/*
+ * Free connection's specific data
+ */
+static void
+gda_postgres_free_cnc_data (PostgresConnectionData *cdata)
{
- g_return_val_if_fail (GDA_IS_SERVER_PROVIDER (provider), NULL);
-
- return gda_default_unescape_string (str);
-}
+ if (!cdata)
+ return;
-static GdaSqlParser *
-gda_postgres_provider_create_parser (GdaServerProvider *provider, GdaConnection *cnc)
-{
- g_return_val_if_fail (GDA_IS_SERVER_PROVIDER (provider), NULL);
- return (GdaSqlParser*) g_object_new (GDA_TYPE_POSTGRES_PARSER, "tokenizer-flavour",
- GDA_SQL_PARSER_FLAVOUR_POSTGRESQL, NULL);
+ if (cdata->pconn)
+ PQfinish (cdata->pconn);
+ TO_IMPLEMENT;
+ g_free (cdata);
}
Modified: branches/V4-branch/providers/postgres/gda-postgres-provider.h
==============================================================================
--- branches/V4-branch/providers/postgres/gda-postgres-provider.h (original)
+++ branches/V4-branch/providers/postgres/gda-postgres-provider.h Wed Feb 20 20:36:04 2008
@@ -1,31 +1,31 @@
-/* GNOME DB Postgres Provider
- * Copyright (C) 1998 - 2007 The GNOME Foundation
+/* GDA postgres provider
+ * Copyright (C) 1998 - 2008 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 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,
+ * 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.
+ * 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_POSTGRES_PROVIDER_H__
#define __GDA_POSTGRES_PROVIDER_H__
#include <libgda/gda-server-provider.h>
-#include <libpq-fe.h>
#define GDA_TYPE_POSTGRES_PROVIDER (gda_postgres_provider_get_type())
#define GDA_POSTGRES_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_POSTGRES_PROVIDER, GdaPostgresProvider))
@@ -33,72 +33,21 @@
#define GDA_IS_POSTGRES_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_POSTGRES_PROVIDER))
#define GDA_IS_POSTGRES_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDA_TYPE_POSTGRES_PROVIDER))
-#define PARENT_TYPE GDA_TYPE_SERVER_PROVIDER
-#define OBJECT_DATA_POSTGRES_HANDLE "GDA_Postgres_PostgresHandle"
-
-
typedef struct _GdaPostgresProvider GdaPostgresProvider;
typedef struct _GdaPostgresProviderClass GdaPostgresProviderClass;
struct _GdaPostgresProvider {
- GdaServerProvider provider;
+ GdaServerProvider provider;
};
struct _GdaPostgresProviderClass {
GdaServerProviderClass parent_class;
};
-/* Connection data. */
-typedef struct {
- gchar *name;
- Oid oid;
- GType type;
- gchar *comments;
- gchar *owner;
-} GdaPostgresTypeOid;
-
-typedef struct {
- GdaConnection *cnc;
- PGconn *pconn;
- gint ntypes;
- GdaPostgresTypeOid *type_data;
- GHashTable *h_table;
-
- /* Version of the backend to which we are connected */
- gchar *version;
- gfloat version_float;
-
- /* 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 */
-
- /* OID of the last inserted tuple */
- Oid last_insert_id;
-
- /* Cursor usage */
- gboolean use_cursor;
- gint chunk_size;
-} GdaPostgresConnectionData;
-
-/* NOTE ABOUT THE POSTGRES VERSIONS:
- *
- * From Postgres versions <= 7.2.x to version 7.3:
- * => introduction of schemas (namespaces) and the pg_namespace table
- * => introduction of the "qualified name" construction for the database objects
- * => introduction of dropped columns means that the column numbering may have gaps
- * to be teste with the "attisdropped" attribute
- * => tables names can start with "pg_" (so don't exclude them anymore)
- * => aggregates now haves entries in the pg_proc table (pg_aggregate is now only
- * an extension of pg_proc)
- */
-
G_BEGIN_DECLS
-GType gda_postgres_provider_get_type (void) G_GNUC_CONST;
-GdaServerProvider *gda_postgres_provider_new (void);
+GType gda_postgres_provider_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif
-
Added: branches/V4-branch/providers/postgres/gda-postgres-pstmt.c
==============================================================================
--- (empty file)
+++ branches/V4-branch/providers/postgres/gda-postgres-pstmt.c Wed Feb 20 20:36:04 2008
@@ -0,0 +1,111 @@
+/* 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 <glib/gi18n-lib.h>
+#include "gda-postgres-pstmt.h"
+#include "gda-postgres-util.h"
+
+static void gda_postgres_pstmt_class_init (GdaPostgresPStmtClass *klass);
+static void gda_postgres_pstmt_init (GdaPostgresPStmt *pstmt, GdaPostgresPStmtClass *klass);
+static void gda_postgres_pstmt_finalize (GObject *object);
+
+static GObjectClass *parent_class = NULL;
+
+/**
+ * gda_postgres_pstmt_get_type
+ *
+ * Returns: the #GType of GdaPostgresPStmt.
+ */
+GType
+gda_postgres_pstmt_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (GdaPostgresPStmtClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gda_postgres_pstmt_class_init,
+ NULL,
+ NULL,
+ sizeof (GdaPostgresPStmt),
+ 0,
+ (GInstanceInitFunc) gda_postgres_pstmt_init
+ };
+
+ type = g_type_register_static (GDA_TYPE_PSTMT, "GdaPostgresPStmt", &info, 0);
+ }
+ return type;
+}
+
+static void
+gda_postgres_pstmt_class_init (GdaPostgresPStmtClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ parent_class = g_type_class_peek_parent (klass);
+
+ /* virtual functions */
+ object_class->finalize = gda_postgres_pstmt_finalize;
+}
+
+static void
+gda_postgres_pstmt_init (GdaPostgresPStmt *pstmt, GdaPostgresPStmtClass *klass)
+{
+ g_return_if_fail (GDA_IS_PSTMT (pstmt));
+
+ pstmt->prep_name = NULL;
+}
+
+static void
+gda_postgres_pstmt_finalize (GObject *object)
+{
+ GdaPostgresPStmt *pstmt = (GdaPostgresPStmt *) object;
+
+ g_return_if_fail (GDA_IS_PSTMT (pstmt));
+
+ /* deallocate statement */
+ gchar *sql;
+ sql = g_strdup_printf ("DEALLOCATE %s", pstmt->prep_name);
+ _gda_postgres_PQexec_wrap (pstmt->cnc, pstmt->pconn, sql);
+ g_free (sql);
+
+ /* free memory */
+ g_free (pstmt->prep_name);
+
+ /* chain to parent class */
+ parent_class->finalize (object);
+}
+
+GdaPostgresPStmt *
+gda_postgres_pstmt_new (GdaConnection *cnc, PGconn *pconn, const gchar *prep_name)
+{
+ GdaPostgresPStmt *pstmt;
+
+ pstmt = (GdaPostgresPStmt *) g_object_new (GDA_TYPE_POSTGRES_PSTMT, NULL);
+ pstmt->prep_name = g_strdup (prep_name);
+ pstmt->cnc = cnc;
+ pstmt->pconn = pconn;
+
+ return pstmt;
+}
Added: branches/V4-branch/providers/postgres/gda-postgres-pstmt.h
==============================================================================
--- (empty file)
+++ branches/V4-branch/providers/postgres/gda-postgres-pstmt.h Wed Feb 20 20:36:04 2008
@@ -0,0 +1,61 @@
+/* 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.
+ */
+
+#ifndef __GDA_POSTGRES_PSTMT_H__
+#define __GDA_POSTGRES_PSTMT_H__
+
+#include <providers-support/gda-pstmt.h>
+#include "gda-postgres.h"
+
+G_BEGIN_DECLS
+
+#define GDA_TYPE_POSTGRES_PSTMT (gda_postgres_pstmt_get_type())
+#define GDA_POSTGRES_PSTMT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_POSTGRES_PSTMT, GdaPostgresPStmt))
+#define GDA_POSTGRES_PSTMT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_POSTGRES_PSTMT, GdaPostgresPStmtClass))
+#define GDA_IS_POSTGRES_PSTMT(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, GDA_TYPE_POSTGRES_PSTMT))
+#define GDA_IS_POSTGRES_PSTMT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GDA_TYPE_POSTGRES_PSTMT))
+
+typedef struct _GdaPostgresPStmt GdaPostgresPStmt;
+typedef struct _GdaPostgresPStmtClass GdaPostgresPStmtClass;
+
+struct _GdaPostgresPStmt {
+ GdaPStmt object;
+
+ /* PostgreSQL identifies a prepared statement by its name, which we'll keep here,
+ * along with a pointer to a GdaConnection and a PGconn because when the prepared
+ * statement is destroyed, we need to call "DEALLOCATE..."
+ */
+ GdaConnection *cnc;
+ PGconn *pconn;
+ gchar *prep_name;
+};
+
+struct _GdaPostgresPStmtClass {
+ GdaPStmtClass parent_class;
+};
+
+GType gda_postgres_pstmt_get_type (void) G_GNUC_CONST;
+GdaPostgresPStmt *gda_postgres_pstmt_new (GdaConnection *cnc, PGconn *pconn, const gchar *prep_name);
+
+G_END_DECLS
+
+#endif
Modified: branches/V4-branch/providers/postgres/gda-postgres-recordset.c
==============================================================================
--- branches/V4-branch/providers/postgres/gda-postgres-recordset.c (original)
+++ branches/V4-branch/providers/postgres/gda-postgres-recordset.c Wed Feb 20 20:36:04 2008
@@ -1,12 +1,8 @@
/* GDA Postgres provider
- * Copyright (C) 1998 - 2007 The GNOME Foundation
+ * Copyright (C) 2008 The GNOME Foundation.
*
* AUTHORS:
- * Michael Lausch <michael lausch at>
- * Rodrigo Moya <rodrigo gnome-db org>
- * Vivien Malerba <malerba gnome-db org>
- * Gonzalo Paniagua Javier <gonzalo gnome-db org>
- * Bas Driessen <bas driessen xobas com>
+ * TO_ADD: your name and email
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
@@ -24,631 +20,1016 @@
* Boston, MA 02111-1307, USA.
*/
-#include <glib/gi18n-lib.h>
-#include <libgda/gda-data-model.h>
-#include <libgda/gda-data-model-private.h>
+#include <stdarg.h>
#include <string.h>
+#include <glib/gi18n-lib.h>
+#include <libgda/gda-util.h>
+#include <libgda/gda-connection-private.h>
#include "gda-postgres.h"
#include "gda-postgres-recordset.h"
-
-#ifdef PARENT_TYPE
-#undef PARENT_TYPE
+#include "gda-postgres-provider.h"
+#include "gda-postgres-blob-op.h"
+#include "gda-postgres-util.h"
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
#endif
-#define PARENT_TYPE GDA_TYPE_DATA_MODEL_HASH
-
-struct _GdaPostgresRecordsetPrivate {
- GdaConnection *cnc;
- PGresult *pg_res;
-
- GType *column_types;
- gint ncolumns;
- gint nrows;
-
- gchar *table_name;
-};
+#define _GDA_PSTMT(x) ((GdaPStmt*)(x))
static void gda_postgres_recordset_class_init (GdaPostgresRecordsetClass *klass);
static void gda_postgres_recordset_init (GdaPostgresRecordset *recset,
GdaPostgresRecordsetClass *klass);
-static void gda_postgres_recordset_finalize (GObject *object);
+static void gda_postgres_recordset_dispose (GObject *object);
+
+static void gda_postgres_recordset_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gda_postgres_recordset_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+/* virtual methods */
+static gint gda_postgres_recordset_fetch_nb_rows (GdaPModel *model);
+static gboolean gda_postgres_recordset_fetch_random (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error);
+static gboolean gda_postgres_recordset_store_all (GdaPModel *model, GError **error);
+static gboolean gda_postgres_recordset_fetch_next (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error);
+static gboolean gda_postgres_recordset_fetch_prev (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error);
+static gboolean gda_postgres_recordset_fetch_at (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error);
+
+/* static helper functions */
+static void make_point (GdaGeometricPoint *point, const gchar *value);
+static void make_time (GdaTime *timegda, const gchar *value);
+static void make_timestamp (GdaTimestamp *timestamp, const gchar *value);
+static void set_value (GdaConnection *cnc, GValue *value, GType type, const gchar *thevalue, gboolean isNull, gint length);
+
+static GdaPRow *new_row_from_pg_res (GdaPostgresRecordset *imodel, gint pg_res_rownum);
+static gboolean row_is_in_current_pg_res (GdaPostgresRecordset *model, gint row);
+static gboolean fetch_next_chunk (GdaPostgresRecordset *model, gboolean *fetch_error, GError **error);
+static gboolean fetch_prev_chunk (GdaPostgresRecordset *model, gboolean *fetch_error, GError **error);
+static gboolean fetch_row_number_chunk (GdaPostgresRecordset *model, int row_index, gboolean *fetch_error, GError **error);
-static const GValue *gda_postgres_recordset_get_value_at (GdaDataModelRow *model, gint col, gint row);
-static gint gda_postgres_recordset_get_n_rows (GdaDataModelRow *model);
-static GdaRow *gda_postgres_recordset_get_row (GdaDataModelRow *model, gint rownum, GError **error);
-static gboolean gda_postgres_recordset_append_row (GdaDataModelRow *model, GdaRow *row, GError **error);
-static gboolean gda_postgres_recordset_remove_row (GdaDataModelRow *model, GdaRow *row, GError **error);
-static gboolean gda_postgres_recordset_update_row (GdaDataModelRow *model, GdaRow *row, GError **error);
+struct _GdaPostgresRecordsetPrivate {
+ GdaConnection *cnc;
+
+ /* random access attributes */
+ PGresult *pg_res;
+
+ /* cursor access attributes */
+ GdaPRow *tmp_row; /* used to store a reference to the last #GdaPRow returned */
+ gchar *cursor_name;
+ PGconn *pconn;
+ gint chunk_size; /* Number of rows to fetch at a time when iterating forward or backwards. */
+ gint chunks_read; /* Effectively equal to the number of times that we have iterated forwards or backwards. */
+
+ /* Pg cursor's information */
+ gint pg_pos; /* from G_MININT to G_MAXINT */
+ gint pg_res_size; /* The number of rows in the current chunk - usually equal to chunk_size when iterating forward or backward. */
+ gint pg_res_inf; /* The row number of the first row in the current chunk. Don't use if (@pg_res_size <= 0). */
+};
static GObjectClass *parent_class = NULL;
-/*
- * Private functions
- */
+/* properties */
+enum
+{
+ PROP_0,
+ PROP_CHUNCK_SIZE,
+ PROP_CHUNCKS_READ
+};
/*
* Object init and finalize
*/
static void
-gda_postgres_recordset_init (GdaPostgresRecordset *recset,
- GdaPostgresRecordsetClass *klass)
+gda_postgres_recordset_init (GdaPostgresRecordset *recset, GdaPostgresRecordsetClass *klass)
{
g_return_if_fail (GDA_IS_POSTGRES_RECORDSET (recset));
-
recset->priv = g_new0 (GdaPostgresRecordsetPrivate, 1);
+ recset->priv->cnc = NULL;
+
+ recset->priv->pg_res = NULL;
+ recset->priv->pg_pos = G_MININT;
+ recset->priv->pg_res_size = 0;
+
+ recset->priv->chunk_size = 10;
+ recset->priv->chunks_read = 0;
}
static void
gda_postgres_recordset_class_init (GdaPostgresRecordsetClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
- GdaDataModelRowClass *model_class = GDA_DATA_MODEL_ROW_CLASS (klass);
+ GdaPModelClass *pmodel_class = GDA_PMODEL_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
- object_class->finalize = gda_postgres_recordset_finalize;
- model_class->get_n_rows = gda_postgres_recordset_get_n_rows;
- model_class->get_value_at = gda_postgres_recordset_get_value_at;
- model_class->get_row = gda_postgres_recordset_get_row;
- model_class->append_row = gda_postgres_recordset_append_row;
- model_class->remove_row = gda_postgres_recordset_remove_row;
- model_class->update_row = gda_postgres_recordset_update_row;
+ object_class->dispose = gda_postgres_recordset_dispose;
+ pmodel_class->fetch_nb_rows = gda_postgres_recordset_fetch_nb_rows;
+ pmodel_class->fetch_random = gda_postgres_recordset_fetch_random;
+ pmodel_class->store_all = gda_postgres_recordset_store_all;
+
+ pmodel_class->fetch_next = gda_postgres_recordset_fetch_next;
+ pmodel_class->fetch_prev = gda_postgres_recordset_fetch_prev;
+ pmodel_class->fetch_at = gda_postgres_recordset_fetch_at;
+
+ /* properties */
+ object_class->set_property = gda_postgres_recordset_set_property;
+ object_class->get_property = gda_postgres_recordset_get_property;
+ g_object_class_install_property (object_class, PROP_CHUNCK_SIZE,
+ g_param_spec_int ("chunk_size", _("Number of rows fetched at a time"), NULL,
+ 1, G_MAXINT - 1, 10,
+ G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE));
+ g_object_class_install_property (object_class, PROP_CHUNCKS_READ,
+ g_param_spec_int ("chunks_read",
+ _("Number of rows chunks read since the object creation"), NULL,
+ 0, G_MAXINT - 1, 0,
+ G_PARAM_READABLE));
}
static void
-gda_postgres_recordset_finalize (GObject * object)
+gda_postgres_recordset_dispose (GObject *object)
{
GdaPostgresRecordset *recset = (GdaPostgresRecordset *) object;
g_return_if_fail (GDA_IS_POSTGRES_RECORDSET (recset));
- if (recset->priv->pg_res != NULL) {
- PQclear (recset->priv->pg_res);
- recset->priv->pg_res = NULL;
+ if (recset->priv) {
+ if (recset->priv->tmp_row)
+ g_object_unref (recset->priv->tmp_row);
+
+ if (recset->priv->cnc)
+ g_object_unref (recset->priv->cnc);
+
+ if (recset->priv->pg_res)
+ PQclear (recset->priv->pg_res);
+
+ if (recset->priv->cursor_name) {
+ gchar *str;
+ PGresult *pg_res;
+ str = g_strdup_printf ("CLOSE %s", recset->priv->cursor_name);
+ pg_res = PQexec (recset->priv->pconn, str);
+ g_free (str);
+ PQclear (pg_res);
+ g_free (recset->priv->cursor_name);
+ }
+
+ g_free (recset->priv);
+ recset->priv = NULL;
}
- g_free (recset->priv->column_types);
- recset->priv->column_types = NULL;
- g_free (recset->priv);
- recset->priv = NULL;
+ parent_class->dispose (object);
+}
- parent_class->finalize (object);
+static void
+gda_postgres_recordset_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdaPostgresRecordset *model = (GdaPostgresRecordset *) object;
+ if (model->priv) {
+ switch (param_id) {
+ case PROP_CHUNCK_SIZE:
+ model->priv->chunk_size = g_value_get_int (value);
+ break;
+ default:
+ break;
+ }
+ }
}
-static GdaRow *
-get_row (GdaDataModel *model, GdaPostgresRecordsetPrivate *priv, gint rownum, GError **error)
+static void
+gda_postgres_recordset_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- gchar *thevalue;
- GType ftype;
- gboolean isNull;
- GValue *value;
- GdaRow *row;
- gint i;
- gchar *id;
- gint length;
-
- row = gda_row_new (model, priv->ncolumns);
- for (i = 0; i < priv->ncolumns; i++) {
- thevalue = PQgetvalue (priv->pg_res, rownum, i);
- length = PQgetlength (priv->pg_res, rownum, i);
- ftype = priv->column_types [i];
- isNull = thevalue && *thevalue != '\0' ? FALSE : PQgetisnull (priv->pg_res, rownum, i);
- value = (GValue *) gda_row_get_value (row, i);
- gda_postgres_set_value (priv->cnc, value, ftype, thevalue, isNull, length);
- }
-
- gda_row_set_number (row, rownum);
- id = g_strdup_printf ("%d", rownum);
- /* Use oid or figure out primary keys ? could use libsql to add oid to every query. */
- gda_row_set_id (row, id); /* FIXME: by now, the rowid is just the row number */
- g_free (id);
- return row;
+ GdaPostgresRecordset *model = (GdaPostgresRecordset *) object;
+ if (model->priv) {
+ switch (param_id) {
+ case PROP_CHUNCK_SIZE:
+ g_value_set_int (value, model->priv->chunk_size);
+ break;
+ case PROP_CHUNCKS_READ:
+ g_value_set_int (value, model->priv->chunks_read);
+ break;
+ default:
+ break;
+ }
+ }
}
/*
- * Overrides
+ * Public functions
*/
-static GdaRow *
-gda_postgres_recordset_get_row (GdaDataModelRow *model, gint row, GError **error)
+GType
+gda_postgres_recordset_get_type (void)
{
- GdaPostgresRecordset *recset = (GdaPostgresRecordset *) model;
- GdaPostgresRecordsetPrivate *priv_data;
- GdaRow *row_list;
-
- g_return_val_if_fail (GDA_IS_POSTGRES_RECORDSET (recset), NULL);
- g_return_val_if_fail (recset->priv != NULL, 0);
-
- row_list = (GdaRow *) GDA_DATA_MODEL_ROW_CLASS (parent_class)->get_row (model, row,
- error);
- if (row_list != NULL)
- return row_list;
-
- priv_data = recset->priv;
- if (!priv_data->pg_res) {
- gda_connection_add_event_string (priv_data->cnc,
- _("Invalid PostgreSQL handle"));
- return NULL;
- }
-
- if (row == priv_data->nrows)
- return NULL; /* For the last row don't add an error. */
+ static GType type = 0;
- if (row < 0 || row > priv_data->nrows) {
- gda_connection_add_event_string (priv_data->cnc,
- _("Row number out of range"));
- return NULL;
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (GdaPostgresRecordsetClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gda_postgres_recordset_class_init,
+ NULL,
+ NULL,
+ sizeof (GdaPostgresRecordset),
+ 0,
+ (GInstanceInitFunc) gda_postgres_recordset_init
+ };
+ type = g_type_register_static (GDA_TYPE_PMODEL, "GdaPostgresRecordset", &info, 0);
}
- row_list = get_row (GDA_DATA_MODEL (model), priv_data, row, error);
- gda_data_model_hash_insert_row (GDA_DATA_MODEL_HASH (model),
- row, row_list);
-
- return row_list;
+ return type;
}
-static gboolean
-gda_postgres_recordset_append_row (GdaDataModelRow *model, GdaRow *row, GError **error)
+static void
+finish_prep_stmt_init (PostgresConnectionData *cdata, GdaPostgresPStmt *ps, PGresult *pg_res, GType *col_types)
{
- GdaPostgresRecordset *recset = (GdaPostgresRecordset *) model;
- GdaPostgresRecordsetPrivate *priv_data;
- GString *sql, *sql_value;
- gchar *cur_val;
- gint i;
- PGresult *pg_res;
- GdaPostgresConnectionData *cnc_priv_data;
- PGconn *pg_conn;
-
- g_return_val_if_fail (GDA_IS_POSTGRES_RECORDSET (recset), FALSE);
- g_return_val_if_fail (row != NULL, FALSE);
- g_return_val_if_fail (gda_data_model_is_updatable (GDA_DATA_MODEL (model)), FALSE);
- g_return_val_if_fail (recset->priv != NULL, FALSE);
- priv_data = recset->priv;
- pg_res = priv_data->pg_res;
- cnc_priv_data = g_object_get_data (G_OBJECT (priv_data->cnc),
- OBJECT_DATA_POSTGRES_HANDLE);
- pg_conn = cnc_priv_data->pconn;
-
- /* checks if the table name has been guessed */
- if (priv_data->table_name == NULL) {
- gda_connection_add_event_string (priv_data->cnc,
- _("Table name could not be guessed."));
- return FALSE;
+ /* make sure @ps reports the correct number of columns */
+ if (_GDA_PSTMT (ps)->ncols < 0) {
+ if (pg_res)
+ _GDA_PSTMT (ps)->ncols = PQnfields (pg_res);
+ else
+ _GDA_PSTMT (ps)->ncols = 0;
}
- /* checks for correct number of columns */
- if (priv_data->ncolumns != gda_row_get_length (row)) {
- gda_connection_add_event_string (priv_data->cnc,
- _("Attempt to insert a row with an invalid number of columns"));
- return FALSE;
+ /* completing @ps if not yet done */
+ if (!_GDA_PSTMT (ps)->types && (_GDA_PSTMT (ps)->ncols > 0)) {
+ /* create prepared statement's columns */
+ GSList *list;
+ gint i;
+ 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 is 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 */
+ for (i=0, list = _GDA_PSTMT (ps)->tmpl_columns;
+ i < GDA_PSTMT (ps)->ncols;
+ i++, list = list->next) {
+ GdaColumn *column;
+ Oid postgres_type;
+ GType gtype;
+ column = GDA_COLUMN (list->data);
+ postgres_type = PQftype (pg_res, i);
+ gtype = _gda_postgres_type_oid_to_gda (cdata, postgres_type);
+ _GDA_PSTMT (ps)->types [i] = gtype;
+ gda_column_set_g_type (column, gtype);
+ gda_column_set_name (column, PQfname (pg_res, i));
+ gda_column_set_title (column, PQfname (pg_res, i));
+ gda_column_set_scale (column, (gtype == G_TYPE_DOUBLE) ? DBL_DIG :
+ (gtype == G_TYPE_FLOAT) ? FLT_DIG : 0);
+ gda_column_set_defined_size (column, PQfsize (pg_res, i));
+ gda_column_set_references (column, "");
+
+ /*
+ FIXME: Use @cnc's associated GdaMetaStore to get the following information:
+ gda_column_set_references (column, "");
+ gda_column_set_table (column, ...);
+ gda_column_set_primary_key (column, ...);
+ gda_column_set_unique_key (column, ...);
+ gda_column_set_allow_null (column, ...);
+ gda_column_set_auto_increment (column, ...);
+ */
+ }
}
+}
- /* Prepare the SQL command */
- sql = g_string_new ("INSERT INTO ");
- g_string_append_printf (sql, "%s (", priv_data->table_name);
+GdaDataModel *
+gda_postgres_recordset_new_random (GdaConnection *cnc, GdaPostgresPStmt *ps, PGresult *pg_res, GType *col_types)
+{
+ GdaPostgresRecordset *model;
+ PostgresConnectionData *cdata;
- sql_value = g_string_new ("VALUES (");
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (ps, NULL);
- for (i = 0; i < priv_data->ncolumns; i++) {
+ cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return NULL;
- if (i != 0)
- {
- sql = g_string_append (sql, ", ");
- sql_value = g_string_append (sql_value, ", ");
- }
+ /* finish prepared statement's init */
+ finish_prep_stmt_init (cdata, ps, pg_res, col_types);
- sql = g_string_append (sql, "\"");
- sql = g_string_append (sql, PQfname (priv_data->pg_res, i));
- sql = g_string_append (sql, "\"");
+ /* create data model */
+ model = g_object_new (GDA_TYPE_POSTGRES_RECORDSET, "prepared-stmt", ps,
+ "model-usage", GDA_DATA_MODEL_ACCESS_RANDOM, NULL);
+ model->priv->cnc = cnc;
+ g_object_ref (cnc);
+ model->priv->pg_res = pg_res;
+ ((GdaPModel*) model)->advertized_nrows = PQntuples (model->priv->pg_res);
- cur_val = gda_value_stringify (gda_row_get_value ((GdaRow *) row, i));
- sql_value = g_string_append (sql_value, "'");
- sql_value = g_string_append (sql_value, cur_val);
- sql_value = g_string_append (sql_value, "'");
- g_free (cur_val);
- }
+ return GDA_DATA_MODEL (model);
+}
- /* concatenate SQL statement */
- sql = g_string_append (sql, ") ");
- sql = g_string_append (sql, sql_value->str);
- sql = g_string_append (sql, ")");
+/*
+ * Takes ownership of @cursor_name
+ */
+GdaDataModel *
+gda_postgres_recordset_new_cursor (GdaConnection *cnc, GdaPostgresPStmt *ps, gchar *cursor_name, GType *col_types)
+{
+ GdaPostgresRecordset *model;
+ PostgresConnectionData *cdata;
- /* execute the SQL query */
- pg_res = gda_postgres_PQexec_wrap (priv_data->cnc, pg_conn, sql->str);
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (ps, NULL);
- g_string_free (sql, TRUE);
- g_string_free (sql_value, TRUE);
+ cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+ if (!cdata)
+ return NULL;
- if (pg_res != NULL) {
- /* update ok! */
- if (PQresultStatus (pg_res) != PGRES_COMMAND_OK) {
- gda_postgres_make_error (priv_data->cnc, pg_conn, pg_res);
+ /* Fetch the 1st row to finish initialization of @ps */
+ gchar *str;
+ int status;
+ PGresult *pg_res;
+
+ str = g_strdup_printf ("FETCH FORWARD 1 FROM %s;", cursor_name);
+ pg_res = PQexec (cdata->pconn, str);
+ g_free (str);
+ status = PQresultStatus (pg_res);
+ if (!pg_res || (PQresultStatus (pg_res) != PGRES_TUPLES_OK)) {
+ _gda_postgres_make_error (cdata->cnc, cdata->pconn, pg_res, NULL);
+ if (pg_res) {
PQclear (pg_res);
- return FALSE;
+ pg_res = NULL;
}
- PQclear (pg_res);
}
- else
- gda_postgres_make_error (priv_data->cnc, pg_conn, NULL);
+ else {
+ PGresult *tmp_res;
+ str = g_strdup_printf ("MOVE BACKWARD 1 FROM %s;", cursor_name);
+ tmp_res = PQexec (cdata->pconn, str);
+ g_free (str);
+ if (tmp_res)
+ PQclear (tmp_res);
+ }
+
+ /* finish prepared statement's init */
+ finish_prep_stmt_init (cdata, ps, pg_res, col_types);
+ if (pg_res)
+ PQclear (pg_res);
+
+ /* create model */
+ model = g_object_new (GDA_TYPE_POSTGRES_RECORDSET, "prepared-stmt", ps, "model-usage",
+ GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD | GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD, NULL);
+ model->priv->cnc = cnc;
+ g_object_ref (cnc);
+ model->priv->pconn = cdata->pconn;
+ model->priv->cursor_name = cursor_name;
+ gboolean fetch_error;
+ fetch_next_chunk (model, &fetch_error, NULL);
+
+ return GDA_DATA_MODEL (model);
+}
+
+/*
+ * Get the number of rows in @model, if possible
+ */
+static gint
+gda_postgres_recordset_fetch_nb_rows (GdaPModel *model)
+{
+ GdaPostgresRecordset *imodel;
+
+ imodel = GDA_POSTGRES_RECORDSET (model);
+ if (model->advertized_nrows >= 0)
+ return model->advertized_nrows;
+
+ /* use C API to determine number of rows,if possible */
+ if (!imodel->priv->cursor_name)
+ model->advertized_nrows = PQntuples (imodel->priv->pg_res);
+
+ return model->advertized_nrows;
+}
+
+/*
+ * Create a new filled #GdaPRow object for the row at position @rownum.
+ *
+ * Each new #GdaPRow created is "given" to the #GdaPModel implementation using gda_pmodel_take_row ().
+ */
+static gboolean
+gda_postgres_recordset_fetch_random (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error)
+{
+ GdaPostgresRecordset *imodel = (GdaPostgresRecordset *) model;
- /* append row in hash table */
- if (! GDA_DATA_MODEL_ROW_CLASS (parent_class)->append_row (model, row, error)) {
- gda_postgres_make_error (priv_data->cnc, pg_conn, pg_res);
+ if (*prow)
+ return TRUE;
+
+ if (!imodel->priv->pg_res) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_INTERNAL_ERROR,
+ _("Internal error"));
return FALSE;
}
+ *prow = new_row_from_pg_res (imodel, rownum);
+ gda_pmodel_take_row (model, *prow, rownum);
+
+ if (model->nb_stored_rows == model->advertized_nrows) {
+ /* all the rows have been converted from PGresult to GdaPRow objects => we can
+ * discard the PGresult */
+ PQclear (imodel->priv->pg_res);
+ imodel->priv->pg_res = NULL;
+ }
+
return TRUE;
}
+/*
+ * Create and "give" filled #GdaPRow object for all the rows in the model
+ */
static gboolean
-gda_postgres_recordset_remove_row (GdaDataModelRow *model, GdaRow *row, GError **error)
+gda_postgres_recordset_store_all (GdaPModel *model, GError **error)
{
- GdaPostgresRecordset *recset = (GdaPostgresRecordset *) model;
- GdaPostgresRecordsetPrivate *priv_data;
- gint colnum, uk;
- PGresult *pg_res, *pg_rm_res;
- gchar *query, *query_where, *tmp;
- GdaPostgresConnectionData *cnc_priv_data;
- PGconn *pg_conn;
- gboolean status = FALSE;
-
- g_return_val_if_fail (GDA_IS_POSTGRES_RECORDSET (recset), FALSE);
- g_return_val_if_fail (recset->priv != NULL, FALSE);
- g_return_val_if_fail (row != NULL, FALSE);
-
- priv_data = recset->priv;
- pg_res = priv_data->pg_res;
- cnc_priv_data = g_object_get_data (G_OBJECT (priv_data->cnc),
- OBJECT_DATA_POSTGRES_HANDLE);
- pg_conn = cnc_priv_data->pconn;
-
- /* checks if the given row belongs to the given model */
- if (gda_row_get_model ((GdaRow *) row) != GDA_DATA_MODEL (model)) {
- gda_connection_add_event_string (priv_data->cnc,
- _("Given row doesn't belong to the model."));
+ GdaPostgresRecordset *imodel = (GdaPostgresRecordset*) model;
+ gint i;
+
+ if (!imodel->priv->pg_res) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_INTERNAL_ERROR,
+ _("Internal error"));
return FALSE;
}
- /* checks if the table name has been guessed */
- if (priv_data->table_name == NULL) {
- gda_connection_add_event_string (priv_data->cnc,
- _("Table name could not be guessed."));
- return FALSE;
+ for (i = 0; i < model->advertized_nrows; i++) {
+ GdaPRow *prow;
+ if (! gda_postgres_recordset_fetch_random (model, &prow, i, error))
+ return FALSE;
}
+ return TRUE;
+}
- query_where = g_strdup ("WHERE TRUE ");
+/*
+ * Create a new filled #GdaPRow object for the next cursor row
+ *
+ * Each new #GdaPRow created is referenced only by imodel->priv->tmp_row (the #GdaPModel implementation
+ * never keeps a reference to it). Before a new #GdaPRow gets created, the previous one, if set, is discarded.
+ */
+static gboolean
+gda_postgres_recordset_fetch_next (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error)
+{
+ GdaPostgresRecordset *imodel = (GdaPostgresRecordset*) model;
+
+ if (*prow)
+ return TRUE;
- for (colnum = uk = 0;
- colnum != gda_data_model_get_n_columns (GDA_DATA_MODEL(model));
- colnum++)
- {
- GdaColumn *attrs = gda_data_model_describe_column (GDA_DATA_MODEL(model), colnum);
- const gchar *column_name = PQfname (pg_res, colnum);
- gchar *curval = gda_value_stringify (gda_row_get_value ((GdaRow *) row, colnum));
-
- /* unique column: we will use it as an index */
- if (gda_column_get_primary_key (attrs) ||
- gda_column_get_unique_key (attrs))
- {
- /* fills the 'where' part of the update command */
- tmp = g_strdup_printf ("AND \"%s\" = '%s' ",
- column_name,
- curval);
- query_where = g_strconcat (query_where, tmp, NULL);
- g_free (tmp);
- uk++;
- }
-
- g_free (curval);
- }
-
- if (uk == 0) {
- gda_connection_add_event_string (priv_data->cnc,
- _("Model doesn't have at least one unique key."));
+ if (imodel->priv->tmp_row) {
+ g_object_unref (imodel->priv->tmp_row);
+ imodel->priv->tmp_row = NULL;
+ }
+
+ if (row_is_in_current_pg_res (imodel, rownum)) {
+ *prow = new_row_from_pg_res (imodel, rownum - imodel->priv->pg_res_inf);
+ imodel->priv->tmp_row = *prow;
+ return TRUE;
}
else {
- /* build the delete command */
- query = g_strdup_printf ("DELETE FROM %s %s",
- priv_data->table_name,
- query_where);
-
- /* remove the row */
- pg_rm_res = gda_postgres_PQexec_wrap (priv_data->cnc, pg_conn, query);
- g_free (query);
-
- if (pg_rm_res != NULL) {
- /* removal ok! */
- if (PQresultStatus (pg_rm_res) == PGRES_COMMAND_OK)
- status = TRUE;
- else
- gda_postgres_make_error (priv_data->cnc, pg_conn, pg_rm_res);
- PQclear (pg_rm_res);
+ gboolean fetch_error = FALSE;
+ if (fetch_next_chunk (imodel, &fetch_error, error)) {
+ *prow = new_row_from_pg_res (imodel, rownum - imodel->priv->pg_res_inf);
+ imodel->priv->tmp_row = *prow;
+ return TRUE;
}
else
- gda_postgres_make_error (priv_data->cnc, pg_conn, NULL);
+ return !fetch_error;
}
-
- g_free (query_where);
-
- /* remove entry from data model */
- if (status == TRUE)
- status = GDA_DATA_MODEL_ROW_CLASS (parent_class)->remove_row (model, row, error);
-
- return status;
}
+/*
+ * Create a new filled #GdaPRow object for the previous cursor row
+ *
+ * Each new #GdaPRow created is referenced only by imodel->priv->tmp_row (the #GdaPModel implementation
+ * never keeps a reference to it). Before a new #GdaPRow gets created, the previous one, if set, is discarded.
+ */
static gboolean
-gda_postgres_recordset_update_row (GdaDataModelRow *model, GdaRow *row, GError **error)
+gda_postgres_recordset_fetch_prev (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error)
{
- GdaPostgresRecordset *recset = (GdaPostgresRecordset *) model;
- GdaPostgresRecordsetPrivate *priv_data;
- gint colnum, rownum, uk, nuk;
- PGresult *pg_res, *pg_upd_res;
- gchar *query, *query_where, *query_set, *tmp;
- gchar *oldval, *newval;
- const gchar *column_name;
- GdaPostgresConnectionData *cnc_priv_data;
- PGconn *pg_conn;
- gboolean status = FALSE;
-
- g_return_val_if_fail (GDA_IS_POSTGRES_RECORDSET (recset), FALSE);
- g_return_val_if_fail (recset->priv != NULL, FALSE);
- g_return_val_if_fail (row != NULL, FALSE);
- priv_data = recset->priv;
- pg_res = priv_data->pg_res;
- cnc_priv_data = g_object_get_data (G_OBJECT (priv_data->cnc),
- OBJECT_DATA_POSTGRES_HANDLE);
- pg_conn = cnc_priv_data->pconn;
-
- /* checks if the given row belongs to the given model */
- if (gda_row_get_model ((GdaRow *) row) != GDA_DATA_MODEL (model)) {
- gda_connection_add_event_string (priv_data->cnc,
- _("Given row doesn't belong to the model."));
- return FALSE;
- }
+ GdaPostgresRecordset *imodel = (GdaPostgresRecordset*) model;
- /* checks if the table name has been guessed */
- if (priv_data->table_name == NULL) {
- gda_connection_add_event_string (priv_data->cnc,
- _("Table name could not be guessed."));
- return FALSE;
- }
-
- rownum = gda_row_get_number ((GdaRow *) row);
+ if (*prow)
+ return TRUE;
- query_where = g_strdup ("WHERE TRUE ");
- query_set = g_strdup ("SET ");
+ if (imodel->priv->tmp_row) {
+ g_object_unref (imodel->priv->tmp_row);
+ imodel->priv->tmp_row = NULL;
+ }
- for (colnum = uk = nuk = 0;
- colnum != gda_data_model_get_n_columns (GDA_DATA_MODEL (model));
- colnum++)
- {
- GdaColumn *attrs = gda_data_model_describe_column (GDA_DATA_MODEL (model), colnum);
- column_name = PQfname (pg_res, colnum);
- newval = gda_value_stringify (gda_row_get_value ((GdaRow *) row, colnum));
-
- /* for data from mysql result we can retrieve original values to avoid
- unique columns to be updated */
- if (rownum < priv_data->nrows)
- oldval = PQgetvalue (pg_res, gda_row_get_number ((GdaRow *) row), colnum);
+ if (row_is_in_current_pg_res (imodel, rownum)) {
+ *prow = new_row_from_pg_res (imodel, rownum - imodel->priv->pg_res_inf);
+ imodel->priv->tmp_row = *prow;
+ return TRUE;
+ }
+ else {
+ gboolean fetch_error = FALSE;
+ if (fetch_prev_chunk (imodel, &fetch_error, error)) {
+ *prow = new_row_from_pg_res (imodel, rownum - imodel->priv->pg_res_inf);
+ imodel->priv->tmp_row = *prow;
+ return TRUE;
+ }
else
- oldval = newval;
+ return !fetch_error;
+ }
+}
- /* unique column: we won't update it, but we will use it as
- an index */
- if (gda_column_get_primary_key (attrs) ||
- gda_column_get_unique_key (attrs))
- {
- /* checks if it hasn't be modified anyway */
- if (oldval == NULL ||
- newval == NULL ||
- strcmp (oldval, newval) != 0)
- continue;
-
- /* fills the 'where' part of the update command */
- tmp = g_strdup_printf ("AND \"%s\" = '%s' ",
- column_name,
- newval);
- query_where = g_strconcat (query_where, tmp, NULL);
- g_free (tmp);
- uk++;
- }
- /* non-unique column: update it */
- else {
- /* fills the 'set' part of the update command */
- tmp = g_strdup_printf ("\"%s\" = '%s', ",
- column_name,
- newval);
- query_set = g_strconcat (query_set, tmp, NULL);
- g_free (tmp);
- nuk++;
- }
+/*
+ * Create a new filled #GdaPRow object for the cursor row at position @rownum
+ *
+ * Each new #GdaPRow created is referenced only by imodel->priv->tmp_row (the #GdaPModel implementation
+ * never keeps a reference to it). Before a new #GdaPRow gets created, the previous one, if set, is discarded.
+ */
+static gboolean
+gda_postgres_recordset_fetch_at (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error)
+{
+ GdaPostgresRecordset *imodel = (GdaPostgresRecordset*) model;
- g_free (newval);
- }
+ if (*prow)
+ return TRUE;
- if (uk == 0) {
- gda_connection_add_event_string (priv_data->cnc,
- _("Model doesn't have at least one non-modified unique key."));
+ if (imodel->priv->tmp_row) {
+ g_object_unref (imodel->priv->tmp_row);
+ imodel->priv->tmp_row = NULL;
}
- else if (nuk == 0) {
- gda_connection_add_event_string (priv_data->cnc,
- _("Model doesn't have any non-unique values to update."));
+
+ if (row_is_in_current_pg_res (imodel, rownum)) {
+ *prow = new_row_from_pg_res (imodel, rownum - imodel->priv->pg_res_inf);
+ imodel->priv->tmp_row = *prow;
+ return TRUE;
}
else {
- /* remove the last ',' in the SET part */
- tmp = strrchr (query_set, ',');
- if (tmp != NULL)
- *tmp = ' ';
-
- /* build the update command */
- query = g_strdup_printf ("UPDATE %s %s %s",
- priv_data->table_name,
- query_set,
- query_where);
-
- /* update the row */
- pg_upd_res = gda_postgres_PQexec_wrap (priv_data->cnc, pg_conn, query);
- g_free (query);
-
- if (pg_upd_res != NULL) {
- /* update ok! */
- if (PQresultStatus (pg_upd_res) == PGRES_COMMAND_OK)
- status = TRUE;
- else
- gda_postgres_make_error (priv_data->cnc, pg_conn, pg_upd_res);
- PQclear (pg_upd_res);
+ gboolean fetch_error = FALSE;
+ if (fetch_row_number_chunk (imodel, rownum, &fetch_error, error)) {
+ *prow = new_row_from_pg_res (imodel, rownum - imodel->priv->pg_res_inf);
+ imodel->priv->tmp_row = *prow;
+ return TRUE;
}
else
- gda_postgres_make_error (priv_data->cnc, pg_conn, NULL);
+ return !fetch_error;
}
-
- g_free (query_set);
- g_free (query_where);
+}
+
+
- /* emit update signals */
- gda_data_model_row_updated (GDA_DATA_MODEL (model), gda_row_get_number ((GdaRow *) row));
- return status;
+
+
+/*
+ * Static helper functions
+ */
+
+/* Makes a point from a string like "(3.2,5.6)" */
+static void
+make_point (GdaGeometricPoint *point, const gchar *value)
+{
+ value++;
+ point->x = atof (value);
+ value = strchr (value, ',');
+ value++;
+ point->y = atof (value);
}
-static const GValue *
-gda_postgres_recordset_get_value_at (GdaDataModelRow *model, gint col, gint row)
+/* Makes a GdaTime from a string like "12:30:15+01" */
+static void
+make_time (GdaTime *timegda, const gchar *value)
{
- GdaPostgresRecordset *recset = (GdaPostgresRecordset *) model;
- GdaPostgresRecordsetPrivate *priv_data;
- PGresult *pg_res;
- GdaRow *row_list;
- const GValue *value;
+ timegda->hour = atoi (value);
+ value += 3;
+ timegda->minute = atoi (value);
+ value += 3;
+ timegda->second = atoi (value);
+ value += 2;
+ if (*value)
+ timegda->timezone = atoi (value);
+ else
+ timegda->timezone = GDA_TIMEZONE_INVALID;
+}
- g_return_val_if_fail (GDA_IS_POSTGRES_RECORDSET (recset), NULL);
- g_return_val_if_fail (recset->priv != NULL, 0);
-
- value = GDA_DATA_MODEL_ROW_CLASS (parent_class)->get_value_at (model, col, row);
- if (value != NULL)
- return value;
-
- priv_data = recset->priv;
- pg_res = priv_data->pg_res;
- if (!pg_res) {
- gda_connection_add_event_string (priv_data->cnc,
- _("Invalid PostgreSQL handle"));
- return NULL;
- }
+/* Makes a GdaTimestamp from a string like "2003-12-13 13:12:01.12+01" */
+static void
+make_timestamp (GdaTimestamp *timestamp, const gchar *value)
+{
+ timestamp->year = atoi (value);
+ value += 5;
+ timestamp->month = atoi (value);
+ value += 3;
+ timestamp->day = atoi (value);
+ value += 3;
+ timestamp->hour = atoi (value);
+ value += 3;
+ timestamp->minute = atoi (value);
+ value += 3;
+ timestamp->second = atoi (value);
+ value += 2;
+ if (*value != '.') {
+ timestamp->fraction = 0;
+ } else {
+ gint ndigits = 0;
+ gint64 fraction;
+
+ value++;
+ fraction = atol (value);
+ while (*value && *value != '+') {
+ value++;
+ ndigits++;
+ }
- if (row == priv_data->nrows)
- return NULL; /* For the last row don't add an error. */
+ while (ndigits < 3) {
+ fraction *= 10;
+ ndigits++;
+ }
- if (row < 0 || row > priv_data->nrows) {
- gda_connection_add_event_string (priv_data->cnc,
- _("Row number out of range"));
- return NULL;
+ while (fraction > 0 && ndigits > 3) {
+ fraction /= 10;
+ ndigits--;
+ }
+
+ timestamp->fraction = fraction;
}
- if (col >= priv_data->ncolumns) {
- gda_connection_add_event_string (priv_data->cnc,
- _("Column number out of range"));
- return NULL;
+ if (*value != '+') {
+ timestamp->timezone = 0;
+ } else {
+ value++;
+ timestamp->timezone = atol (value) * 60 * 60;
}
-
- row_list = get_row (GDA_DATA_MODEL (model), priv_data, row, NULL);
- gda_data_model_hash_insert_row (GDA_DATA_MODEL_HASH (model),
- row, row_list);
- return gda_row_get_value (row_list, col);
}
-static gint
-gda_postgres_recordset_get_n_rows (GdaDataModelRow *model)
+static void
+set_value (GdaConnection *cnc, GValue *value, GType type,
+ const gchar *thevalue, gboolean isNull, gint length)
{
- gint parent_row_num;
- GdaPostgresRecordset *recset = (GdaPostgresRecordset *) model;
-
- g_return_val_if_fail (GDA_IS_POSTGRES_RECORDSET (model), 0);
- g_return_val_if_fail (recset->priv != NULL, 0);
-
- parent_row_num = GDA_DATA_MODEL_ROW_CLASS (parent_class)->get_n_rows (model);
+ if (isNull) {
+ gda_value_set_null (value);
+ return;
+ }
+
+ gda_value_reset_with_type (value, type);
+
+ if (type == G_TYPE_BOOLEAN)
+ g_value_set_boolean (value, (*thevalue == 't') ? TRUE : FALSE);
+ else if (type == G_TYPE_STRING)
+ g_value_set_string (value, thevalue);
+ else if (type == G_TYPE_INT64)
+ g_value_set_int64 (value, atoll (thevalue));
+ else if (type == G_TYPE_ULONG)
+ g_value_set_ulong (value, atoll (thevalue));
+ else if (type == G_TYPE_LONG)
+ g_value_set_ulong (value, atoll (thevalue));
+ else if (type == G_TYPE_INT)
+ g_value_set_int (value, atol (thevalue));
+ else if (type == GDA_TYPE_SHORT)
+ gda_value_set_short (value, atoi (thevalue));
+ else if (type == G_TYPE_FLOAT) {
+ setlocale (LC_NUMERIC, "C");
+ g_value_set_float (value, atof (thevalue));
+ setlocale (LC_NUMERIC, "");
+ }
+ else if (type == G_TYPE_DOUBLE) {
+ setlocale (LC_NUMERIC, "C");
+ g_value_set_double (value, atof (thevalue));
+ setlocale (LC_NUMERIC, "");
+ }
+ else if (type == GDA_TYPE_NUMERIC) {
+ GdaNumeric numeric;
+ numeric.number = g_strdup (thevalue);
+ numeric.precision = 0; /* FIXME */
+ numeric.width = 0; /* FIXME */
+ gda_value_set_numeric (value, &numeric);
+ g_free (numeric.number);
+ }
+ else if (type == G_TYPE_DATE) {
+ GDate *gdate;
+ gdate = g_date_new ();
+ g_date_set_parse (gdate, thevalue);
+ if (!g_date_valid (gdate)) {
+ g_warning (_("Could not parse date '%s', assuming 01/01/0001"), thevalue);
+ g_date_clear (gdate, 1);
+ g_date_set_dmy (gdate, 1, 1, 1);
+ }
+ g_value_take_boxed (value, gdate);
+ }
+ else if (type == GDA_TYPE_GEOMETRIC_POINT) {
+ GdaGeometricPoint point;
+ make_point (&point, thevalue);
+ gda_value_set_geometric_point (value, &point);
+ }
+ else if (type == GDA_TYPE_TIMESTAMP) {
+ GdaTimestamp timestamp;
+ make_timestamp (×tamp, thevalue);
+ gda_value_set_timestamp (value, ×tamp);
+ }
+ else if (type == GDA_TYPE_TIME) {
+ GdaTime timegda;
+ make_time (&timegda, thevalue);
+ gda_value_set_time (value, &timegda);
+ }
+ else if (type == GDA_TYPE_BINARY) {
+ /*
+ * Requires PQunescapeBytea in libpq (present since 7.3.x)
+ */
+ guchar *unescaped;
+ size_t pqlength = 0;
+
+ unescaped = PQunescapeBytea ((guchar*)thevalue, &pqlength);
+ if (unescaped != NULL) {
+ GdaBinary bin;
+ bin.data = unescaped;
+ bin.binary_length = pqlength;
+ gda_value_set_binary (value, &bin);
+ PQfreemem (unescaped);
+ }
+ }
+ else if (type == GDA_TYPE_BLOB) {
+ GdaBlob *blob;
+ GdaBlobOp *op;
+ blob = g_new0 (GdaBlob, 1);
+ op = gda_postgres_blob_op_new_with_id (cnc, thevalue);
+ gda_blob_set_op (blob, op);
+ g_object_unref (op);
- /* if not initialized return number of PQ Tuples */
- if (parent_row_num == 0)
- return recset->priv->nrows;
- else
- return parent_row_num;
+ gda_value_take_blob (value, blob);
+ }
+ else if (type == G_TYPE_STRING)
+ g_value_set_string (value, thevalue);
+ else {
+ g_warning ("Type %s not translated for value '%s' => set as string", g_type_name (type), thevalue);
+ gda_value_reset_with_type (value, G_TYPE_STRING);
+ g_value_set_string (value, thevalue);
+ }
}
-/*
- * Public functions
- */
+static gboolean
+row_is_in_current_pg_res (GdaPostgresRecordset *model, gint row)
+{
+ if ((model->priv->pg_res) && (model->priv->pg_res_size > 0) &&
+ (row >= model->priv->pg_res_inf) && (row < model->priv->pg_res_inf + model->priv->pg_res_size))
+ return TRUE;
+ else
+ return FALSE;
+}
-GType
-gda_postgres_recordset_get_type (void)
+static GdaPRow *
+new_row_from_pg_res (GdaPostgresRecordset *imodel, gint pg_res_rownum)
{
- static GType type = 0;
+ GdaPRow *prow;
+ gchar *thevalue;
+ gboolean isNull;
+ gint col;
- if (G_UNLIKELY (type == 0)) {
- static const GTypeInfo info = {
- sizeof (GdaPostgresRecordsetClass),
- (GBaseInitFunc) NULL,
- (GBaseFinalizeFunc) NULL,
- (GClassInitFunc) gda_postgres_recordset_class_init,
- NULL,
- NULL,
- sizeof (GdaPostgresRecordset),
- 0,
- (GInstanceInitFunc) gda_postgres_recordset_init
- };
- type = g_type_register_static (PARENT_TYPE, "GdaPostgresRecordset", &info, 0);
+ prow = gda_prow_new (((GdaPModel*) imodel)->prep_stmt->ncols);
+ for (col = 0; col < ((GdaPModel*) imodel)->prep_stmt->ncols; col++) {
+ thevalue = PQgetvalue (imodel->priv->pg_res, pg_res_rownum, col);
+ isNull = thevalue && *thevalue != '\0' ? FALSE : PQgetisnull (imodel->priv->pg_res, pg_res_rownum, col);
+ set_value (imodel->priv->cnc,
+ gda_prow_get_value (prow, col),
+ ((GdaPModel*) imodel)->prep_stmt->types [col],
+ thevalue, isNull,
+ PQgetlength (imodel->priv->pg_res, pg_res_rownum, col));
}
- return type;
+ return prow;
}
-GdaDataModel *
-gda_postgres_recordset_new (GdaConnection *cnc, PGresult *pg_res)
+static gboolean
+fetch_next_chunk (GdaPostgresRecordset *model, gboolean *fetch_error, GError **error)
{
- GdaPostgresRecordset *model;
- GdaPostgresConnectionData *cnc_priv_data;
- gchar *cmd_tuples;
- gchar *endptr [1];
- gint i;
+ if (model->priv->pg_res) {
+ PQclear (model->priv->pg_res);
+ model->priv->pg_res = NULL;
+ }
+ *fetch_error = FALSE;
- g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
- g_return_val_if_fail (pg_res != NULL, NULL);
+ if (model->priv->pg_pos == G_MAXINT)
+ return FALSE;
- cnc_priv_data = g_object_get_data (G_OBJECT (cnc), OBJECT_DATA_POSTGRES_HANDLE);
+ gchar *str;
+ gboolean retval = TRUE;
+ int status;
+
+ str = g_strdup_printf ("FETCH FORWARD %d FROM %s;",
+ model->priv->chunk_size, model->priv->cursor_name);
+#ifdef GDA_PG_DEBUG
+ g_print ("QUERY: %s\n", str);
+#endif
+ model->priv->pg_res = PQexec (model->priv->pconn, str);
+ g_free (str);
+ status = PQresultStatus (model->priv->pg_res);
+ model->priv->chunks_read ++;
+ if (status != PGRES_TUPLES_OK) {
+ _gda_postgres_make_error (model->priv->cnc, model->priv->pconn, model->priv->pg_res, error);
+ PQclear (model->priv->pg_res);
+ model->priv->pg_res = NULL;
+ model->priv->pg_res_size = 0;
+ retval = FALSE;
+ *fetch_error = TRUE;
+ }
+ else {
+#ifdef GDA_PG_DEBUG
+ dump_pg_res (model->priv->pg_res);
+#endif
- model = g_object_new (GDA_TYPE_POSTGRES_RECORDSET, NULL);
- model->priv->pg_res = pg_res;
- model->priv->cnc = cnc;
- model->priv->ncolumns = PQnfields (pg_res);
- cmd_tuples = PQcmdTuples (pg_res);
- if (cmd_tuples == NULL || *cmd_tuples == '\0')
- model->priv->nrows = PQntuples (pg_res);
+ //PQntuples() returns the number of rows in the result:
+ const gint nbtuples = PQntuples (model->priv->pg_res);
+ model->priv->pg_res_size = nbtuples;
+
+ if (nbtuples > 0) {
+ /* model->priv->pg_res_inf */
+ if (model->priv->pg_pos == G_MININT)
+ model->priv->pg_res_inf = 0;
+ else
+ model->priv->pg_res_inf = model->priv->pg_pos + 1;
+
+ /* GDA_PMODEL (model)->advertized_nrows and model->priv->pg_pos */
+ if (nbtuples < model->priv->chunk_size) {
+ if (model->priv->pg_pos == G_MININT)
+ GDA_PMODEL (model)->advertized_nrows = nbtuples;
+ else
+ GDA_PMODEL (model)->advertized_nrows = model->priv->pg_pos + nbtuples + 1;
+
+ model->priv->pg_pos = G_MAXINT;
+ }
+ else {
+ if (model->priv->pg_pos == G_MININT)
+ model->priv->pg_pos = nbtuples - 1;
+ else
+ model->priv->pg_pos += nbtuples;
+ }
+ }
+ else {
+ if (model->priv->pg_pos == G_MININT)
+ GDA_PMODEL (model)->advertized_nrows = 0;
+ else
+ GDA_PMODEL (model)->advertized_nrows = model->priv->pg_pos + 1; /* total number of rows */
+ model->priv->pg_pos = G_MAXINT;
+ retval = FALSE;
+ }
+ }
+
+#ifdef GDA_PG_DEBUG
+ g_print ("--> SIZE = %d (inf = %d) nrows = %d, pg_pos = %d\n", model->priv->pg_res_size, model->priv->pg_res_inf,
+ GDA_PMODEL (model)->advertized_nrows, model->priv->pg_pos);
+#endif
+
+ return retval;
+}
+
+static gboolean
+fetch_prev_chunk (GdaPostgresRecordset *model, gboolean *fetch_error, GError **error)
+{
+ if (model->priv->pg_res) {
+ PQclear (model->priv->pg_res);
+ model->priv->pg_res = NULL;
+ }
+ *fetch_error = FALSE;
+
+ if (model->priv->pg_pos == G_MININT)
+ return FALSE;
+ else if (model->priv->pg_pos == G_MAXINT)
+ g_assert (GDA_PMODEL (model)->advertized_nrows >= 0); /* total number of rows MUST be known at this point */
+
+ gchar *str;
+ gboolean retval = TRUE;
+ int status;
+ gint noffset;
+
+ if (model->priv->pg_pos == G_MAXINT)
+ noffset = model->priv->chunk_size + 1;
+ else
+ noffset = model->priv->pg_res_size + model->priv->chunk_size;
+ str = g_strdup_printf ("MOVE BACKWARD %d FROM %s; FETCH FORWARD %d FROM %s;",
+ noffset, model->priv->cursor_name,
+ model->priv->chunk_size, model->priv->cursor_name);
+#ifdef GDA_PG_DEBUG
+ g_print ("QUERY: %s\n", str);
+#endif
+ model->priv->pg_res = PQexec (model->priv->pconn, str);
+ g_free (str);
+ status = PQresultStatus (model->priv->pg_res);
+ model->priv->chunks_read ++;
+ if (status != PGRES_TUPLES_OK) {
+ _gda_postgres_make_error (model->priv->cnc, model->priv->pconn, model->priv->pg_res, error);
+ PQclear (model->priv->pg_res);
+ model->priv->pg_res = NULL;
+ model->priv->pg_res_size = 0;
+ retval = FALSE;
+ *fetch_error = TRUE;
+ }
else {
- model->priv->nrows = strtol (cmd_tuples, endptr, 10);
- if (**endptr != '\0')
- g_warning (_("Tuples:\"%s\""), cmd_tuples);
-
+#ifdef GDA_PG_DEBUG
+ dump_pg_res (model->priv->pg_res);
+#endif
+
+ //PQntuples() returns the number of rows in the result:
+ const gint nbtuples = PQntuples (model->priv->pg_res);
+ model->priv->pg_res_size = nbtuples;
+
+ if (nbtuples > 0) {
+ /* model->priv->pg_res_inf */
+ if (model->priv->pg_pos == G_MAXINT)
+ model->priv->pg_res_inf = GDA_PMODEL (model)->advertized_nrows - nbtuples;
+ else
+ model->priv->pg_res_inf =
+ MAX (model->priv->pg_res_inf - (noffset - model->priv->chunk_size), 0);
+
+ /* model->priv->pg_pos */
+ if (nbtuples < model->priv->chunk_size) {
+ model->priv->pg_pos = G_MAXINT;
+ }
+ else {
+ if (model->priv->pg_pos == G_MAXINT)
+ model->priv->pg_pos = GDA_PMODEL (model)->advertized_nrows - 1;
+ else
+ model->priv->pg_pos = MAX (model->priv->pg_pos - noffset, -1) + nbtuples;
+ }
+ }
+ else {
+ model->priv->pg_pos = G_MAXINT;
+ retval = FALSE;
+ }
}
- model->priv->column_types = gda_postgres_get_column_types (pg_res,
- cnc_priv_data->type_data,
- cnc_priv_data->ntypes);
- gda_data_model_hash_set_n_columns (GDA_DATA_MODEL_HASH (model),
- model->priv->ncolumns);
- model->priv->table_name = gda_postgres_guess_table_name (cnc, pg_res);
-
- /* set GdaColumn attributes */
- for (i = 0; i < model->priv->ncolumns; i++)
- gda_postgres_recordset_describe_column (GDA_DATA_MODEL (model), cnc, pg_res,
- cnc_priv_data->type_data,
- cnc_priv_data->ntypes,
- model->priv->table_name, i);
- return GDA_DATA_MODEL (model);
+#ifdef GDA_PG_DEBUG
+ g_print ("<-- SIZE = %d (inf = %d) nrows = %d, pg_pos = %d\n", model->priv->pg_res_size, model->priv->pg_res_inf,
+ GDA_PMODEL (model)->advertized_nrows, model->priv->pg_pos);
+#endif
+
+ return retval;
}
-PGresult *
-gda_postgres_recordset_get_pgresult (GdaPostgresRecordset *recset)
+static gboolean
+fetch_row_number_chunk (GdaPostgresRecordset *model, int row_index, gboolean *fetch_error, GError **error)
{
- g_return_val_if_fail (GDA_IS_POSTGRES_RECORDSET (recset), NULL);
+ if (model->priv->pg_res) {
+ PQclear (model->priv->pg_res);
+ model->priv->pg_res = NULL;
+ }
+ *fetch_error = FALSE;
+
+ gchar *str;
+ gboolean retval = TRUE;
+ int status;
+
+ /* Postgres's FETCH ABSOLUTE seems to use a 1-based index: */
+ str = g_strdup_printf ("FETCH ABSOLUTE %d FROM %s;",
+ row_index + 1, model->priv->cursor_name);
+#ifdef GDA_PG_DEBUG
+ g_print ("QUERY: %s\n", str);
+#endif
+ model->priv->pg_res = PQexec (model->priv->pconn, str);
+ g_free (str);
+ status = PQresultStatus (model->priv->pg_res);
+ model->priv->chunks_read ++; /* Not really correct, because we are only fetching 1 row, not a whole chunk of rows. */
+ if (status != PGRES_TUPLES_OK) {
+ _gda_postgres_make_error (model->priv->cnc, model->priv->pconn, model->priv->pg_res, error);
+ PQclear (model->priv->pg_res);
+ model->priv->pg_res = NULL;
+ model->priv->pg_res_size = 0;
+ retval = FALSE;
+ *fetch_error = TRUE;
+ }
+ else {
+#ifdef GDA_PG_DEBUG
+ dump_pg_res (model->priv->pg_res);
+#endif
+
+ //PQntuples() returns the number of rows in the result:
+ const gint nbtuples = PQntuples (model->priv->pg_res);
+ model->priv->pg_res_size = nbtuples;
+
+ if (nbtuples > 0) {
+ /* Remember the row number for the start of this chunk:
+ * (actually a chunk of just 1 record in this case.) */
+ model->priv->pg_res_inf = row_index;
+
+ /* don't change model->priv->nrows because we can't know if we have reached the end */
+ model->priv->pg_pos = row_index;
+ }
+ else {
+ model->priv->pg_pos = G_MAXINT;
+ retval = FALSE;
+ }
+ }
+
+#ifdef GDA_PG_DEBUG
+ g_print ("--> SIZE = %d (inf = %d) nrows = %d, pg_pos = %d\n", model->priv->pg_res_size, model->priv->pg_res_inf,
+ model->priv->nrows, model->priv->pg_pos);
+#endif
- return recset->priv->pg_res;
+ return retval;
}
Modified: branches/V4-branch/providers/postgres/gda-postgres-recordset.h
==============================================================================
--- branches/V4-branch/providers/postgres/gda-postgres-recordset.h (original)
+++ branches/V4-branch/providers/postgres/gda-postgres-recordset.h Wed Feb 20 20:36:04 2008
@@ -1,11 +1,8 @@
-/* GDA DB Postgres provider
- * Copyright (C) 1998 - 2002 The GNOME Foundation
+/* GDA postgres provider
+ * Copyright (C) 1998 - 2008 The GNOME Foundation.
*
* AUTHORS:
- * Michael Lausch <michael lausch at>
- * Rodrigo Moya <rodrigo gnome-db org>
- * Vivien Malerba <malerba gnome-db org>
- * Gonzalo Paniagua Javier <gonzalo gnome-db org>
+ * Vivien Malerba <malerba gnome-db org>
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
@@ -26,9 +23,9 @@
#ifndef __GDA_POSTGRES_RECORDSET_H__
#define __GDA_POSTGRES_RECORDSET_H__
-#include <libgda/gda-data-model-hash.h>
-#include <libgda/gda-value.h>
-#include <libpq-fe.h>
+#include <libgda/libgda.h>
+#include <providers-support/gda-pmodel.h>
+#include "gda-postgres-pstmt.h"
G_BEGIN_DECLS
@@ -43,20 +40,19 @@
typedef struct _GdaPostgresRecordsetPrivate GdaPostgresRecordsetPrivate;
struct _GdaPostgresRecordset {
- GdaDataModelHash model;
+ GdaPModel model;
GdaPostgresRecordsetPrivate *priv;
};
struct _GdaPostgresRecordsetClass {
- GdaDataModelHashClass parent_class;
+ GdaPModelClass parent_class;
};
-GType gda_postgres_recordset_get_type (void) G_GNUC_CONST;
-GdaDataModel *gda_postgres_recordset_new (GdaConnection *cnc, PGresult *pgres);
+GType gda_postgres_recordset_get_type (void) G_GNUC_CONST;
+GdaDataModel *gda_postgres_recordset_new_random (GdaConnection *cnc, GdaPostgresPStmt *ps, PGresult *pg_res, GType *col_types);
+GdaDataModel *gda_postgres_recordset_new_cursor (GdaConnection *cnc, GdaPostgresPStmt *ps, gchar *cursor_name, GType *col_types);
-PGresult *gda_postgres_recordset_get_pgresult (GdaPostgresRecordset *recset);
G_END_DECLS
#endif
-
Added: branches/V4-branch/providers/postgres/gda-postgres-util.c
==============================================================================
--- (empty file)
+++ branches/V4-branch/providers/postgres/gda-postgres-util.c Wed Feb 20 20:36:04 2008
@@ -0,0 +1,135 @@
+/* GDA postgres provider
+ * Copyright (C) 1998 - 2008 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-util.h"
+
+static GdaConnectionEventCode
+gda_postgres_sqlsate_to_gda_code (const gchar *sqlstate)
+{
+ guint64 gda_code = g_ascii_strtoull (sqlstate, NULL, 0);
+
+ switch (gda_code) {
+ case 42501:
+ return GDA_CONNECTION_EVENT_CODE_INSUFFICIENT_PRIVILEGES;
+ case 23505:
+ return GDA_CONNECTION_EVENT_CODE_UNIQUE_VIOLATION;
+ case 23502:
+ return GDA_CONNECTION_EVENT_CODE_NOT_NULL_VIOLATION;
+ default:
+ return GDA_CONNECTION_EVENT_CODE_UNKNOWN;
+ }
+}
+
+/*
+ * Create a new #GdaConnectionEvent object and "adds" it to @cnc
+ *
+ * Returns: a new GdaConnectionEvent which must not be unrefed()
+ */
+GdaConnectionEvent *
+_gda_postgres_make_error (GdaConnection *cnc, PGconn *pconn, PGresult *pg_res, GError **error)
+{
+ GdaConnectionEvent *error_ev;
+ GdaConnectionEventCode gda_code = GDA_CONNECTION_EVENT_CODE_UNKNOWN;
+ GdaTransactionStatus *trans;
+
+ error_ev = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
+ if (pconn != NULL) {
+ gchar *message;
+
+ if (pg_res != NULL) {
+ gchar *sqlstate;
+
+ message = g_strdup (PQresultErrorMessage (pg_res));
+ sqlstate = PQresultErrorField (pg_res, PG_DIAG_SQLSTATE);
+ gda_connection_event_set_sqlstate (error_ev, sqlstate);
+ gda_code = gda_postgres_sqlsate_to_gda_code (sqlstate);
+ }
+ else {
+ message = g_strdup (PQerrorMessage (pconn));
+ gda_code = GDA_CONNECTION_EVENT_CODE_UNKNOWN;
+ }
+
+
+ gchar *ptr = message;
+ if (g_str_has_prefix (message, "ERROR:"))
+ ptr += 6;
+ g_strstrip (ptr);
+
+ gda_connection_event_set_description (error_ev, ptr);
+ gda_connection_event_set_gda_code (error_ev, gda_code);
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_STATEMENT_EXEC_ERROR, ptr);
+ g_free (message);
+ }
+ else {
+ gda_connection_event_set_description (error_ev, _("No detail"));
+ gda_connection_event_set_gda_code (error_ev, gda_code);
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_STATEMENT_EXEC_ERROR,
+ _("No detail"));
+ }
+
+ gda_connection_event_set_code (error_ev, -1);
+ gda_connection_event_set_source (error_ev, "gda-postgres");
+ gda_connection_add_event (cnc, error_ev);
+
+ /* change the transaction status if there is a problem */
+ trans = gda_connection_get_transaction_status (cnc);
+ if (trans) {
+ if ((PQtransactionStatus (pconn) == PQTRANS_INERROR) &&
+ (trans->state != GDA_TRANSACTION_STATUS_STATE_FAILED))
+ gda_connection_internal_change_transaction_state (cnc,
+ GDA_TRANSACTION_STATUS_STATE_FAILED);
+ }
+ return error_ev;
+}
+
+/* to be used only while initializing a connection */
+PGresult *
+_gda_postgres_PQexec_wrap (GdaConnection *cnc, PGconn *pconn, const char *query)
+{
+ GdaConnectionEvent *event;
+
+ if (cnc) {
+ event = gda_connection_event_new (GDA_CONNECTION_EVENT_COMMAND);
+ gda_connection_event_set_description (event, query);
+ gda_connection_add_event (cnc, event);
+ }
+
+ return PQexec (pconn, query);
+}
+
+GType
+_gda_postgres_type_oid_to_gda (PostgresConnectionData *cdata, Oid postgres_type)
+{
+ gint i;
+
+ for (i = 0; i < cdata->ntypes; i++)
+ if (cdata->type_data[i].oid == postgres_type)
+ break;
+
+ if (cdata->type_data[i].oid != postgres_type)
+ return G_TYPE_STRING;
+
+ return cdata->type_data[i].type;
+}
Added: branches/V4-branch/providers/postgres/gda-postgres-util.h
==============================================================================
--- (empty file)
+++ branches/V4-branch/providers/postgres/gda-postgres-util.h Wed Feb 20 20:36:04 2008
@@ -0,0 +1,40 @@
+/* GDA postgres provider
+ * Copyright (C) 1998 - 2008 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; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GDA_POSTGRES_UTIL_H__
+#define __GDA_POSTGRES_UTIL_H__
+
+#include "gda-postgres.h"
+
+G_BEGIN_DECLS
+
+GdaConnectionEvent *_gda_postgres_make_error (GdaConnection *cnc, PGconn *pconn, PGresult *pg_res, GError **error);
+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);
+
+G_END_DECLS
+
+#endif
+
Modified: branches/V4-branch/providers/postgres/gda-postgres.h
==============================================================================
--- branches/V4-branch/providers/postgres/gda-postgres.h (original)
+++ branches/V4-branch/providers/postgres/gda-postgres.h Wed Feb 20 20:36:04 2008
@@ -1,75 +1,68 @@
-/* GDA Postgres Provider
- * Copyright (C) 1998 - 2007 The GNOME Foundation
+/* GDA postgres provider
+ * Copyright (C) 1998 - 2008 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 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,
+ * 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.
+ * 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_POSTGRES_H__
#define __GDA_POSTGRES_H__
-#include <glib/gmacros.h>
-#include <glib/gi18n-lib.h>
-#include <libgda/gda-server-provider.h>
-#include "gda-postgres-provider.h"
-#include "gda-postgres-recordset.h"
-#include "gda-postgres-cursor-recordset.h"
-
-#define GDA_POSTGRES_PROVIDER_ID "GDA PostgreSQL provider"
-
-G_BEGIN_DECLS
-
/*
- * Utility functions
+ * Provider name
*/
+#define POSTGRES_PROVIDER_NAME "PostgreSQL"
-GdaConnectionEvent *gda_postgres_make_error (GdaConnection *cnc, PGconn *pconn, PGresult *pg_res);
-void gda_postgres_set_value (GdaConnection *cnc,
- GValue *value,
- GType type,
- const gchar *thevalue,
- gboolean isNull,
- gint length);
-
-
-GType gda_postgres_type_oid_to_gda (GdaPostgresTypeOid *type_data,
- gint ntypes,
- Oid postgres_type);
-
-GType gda_postgres_type_name_to_gda (GHashTable *h_table,
- const gchar *name);
-
-const gchar *gda_data_type_to_string (GType type);
-gchar *gda_postgres_value_to_sql_string (GValue *value);
-gboolean gda_postgres_check_transaction_started (GdaConnection *cnc);
+#include <libgda/libgda.h>
+#include <libpq-fe.h>
+#include <libpq/libpq-fs.h>
-PGresult *gda_postgres_PQexec_wrap (GdaConnection *cnc, PGconn *conn, const char *query);
+/*
+ * Postgres type identification
+ */
+typedef struct {
+ gchar *name;
+ Oid oid;
+ GType type;
+ gchar *comments;
+ gchar *owner;
+} GdaPostgresTypeOid;
/*
- * For recordset implementations
+ * Provider's specific connection data
*/
-void gda_postgres_recordset_describe_column (GdaDataModel *model, GdaConnection *cnc, PGresult *pg_res,
- GdaPostgresTypeOid *type_data, gint ntypes, const gchar *table_name,
- gint col);
-gchar *gda_postgres_guess_table_name (GdaConnection *cnc, PGresult *pg_res);
-GType *gda_postgres_get_column_types (PGresult *pg_res, GdaPostgresTypeOid *type_data, gint ntypes);
-G_END_DECLS
+typedef struct {
+ GdaConnection *cnc;
+ PGconn *pconn;
+ gint ntypes;
+ GdaPostgresTypeOid *type_data;
+ GHashTable *h_table;
+
+ /* Version of the backend to which we are connected */
+ gchar *version;
+ gfloat version_float;
+
+ /* 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 */
+} PostgresConnectionData;
#endif
-
Modified: branches/V4-branch/providers/postgres/gen_def.c
==============================================================================
--- branches/V4-branch/providers/postgres/gen_def.c (original)
+++ branches/V4-branch/providers/postgres/gen_def.c Wed Feb 20 20:36:04 2008
@@ -1,5 +1,4 @@
/*
- *
* Copyright (C) 2007 Vivien Malerba
*
* This Library is free software; you can redistribute it and/or
Modified: branches/V4-branch/providers/postgres/libmain.c
==============================================================================
--- branches/V4-branch/providers/postgres/libmain.c (original)
+++ branches/V4-branch/providers/postgres/libmain.c Wed Feb 20 20:36:04 2008
@@ -1,10 +1,8 @@
/* GDA Postgres Provider
- * Copyright (C) 1998 - 2007 The GNOME Foundation
+ * Copyright (C) 2008 The GNOME Foundation
*
* AUTHORS:
- * Vivien Malerba <malerba gnome-db org>
- * Rodrigo Moya <rodrigo gnome-db org>
- * Gonzalo Paniagua Javier <gonzalo gnome-db org>
+ * 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
@@ -22,10 +20,10 @@
*/
#include <glib/gi18n-lib.h>
-#include <libgda/gda-config.h>
-#include "gda-postgres-provider.h"
#include <libgda/gda-server-provider-extra.h>
#include <libgda/binreloc/gda-binreloc.h>
+#include "gda-postgres.h"
+#include "gda-postgres-provider.h"
static gchar *module_path = NULL;
const gchar *plugin_get_name (void);
@@ -36,14 +34,14 @@
void
plugin_init (const gchar *real_path)
{
- if (real_path)
- module_path = g_strdup (real_path);
+ if (real_path)
+ module_path = g_strdup (real_path);
}
const gchar *
plugin_get_name (void)
{
- return "PostgreSQL";
+ return POSTGRES_PROVIDER_NAME;
}
const gchar *
@@ -68,7 +66,7 @@
{
GdaServerProvider *prov;
- prov = gda_postgres_provider_new ();
- g_object_set_data ((GObject *) prov, "GDA_PROVIDER_DIR", module_path);
- return prov;
+ prov = (GdaServerProvider*) g_object_new (GDA_TYPE_POSTGRES_PROVIDER, NULL);
+ g_object_set_data ((GObject *) prov, "GDA_PROVIDER_DIR", module_path);
+ return prov;
}
Added: branches/V4-branch/providers/prepare_provider_sources.sh
==============================================================================
--- (empty file)
+++ branches/V4-branch/providers/prepare_provider_sources.sh Wed Feb 20 20:36:04 2008
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+#
+# This script prepares the sources to write a new database provider (it renames the files and
+# the objects within the files) using a new name given as the only argument of the command
+# line.
+#
+# Usage:
+# 1 - Copy the ./skel-implementation/capi or ./skel-implementation/models to another directory (for example MySQL)
+# 2 - go into the new directory (MySQL/ in this case)
+# 3 - execute this script with the name of the provider to create (mysql in this case)
+# ../prepare_provider_sources.sh mysql
+#
+
+if [ $# != 1 ]
+then
+ echo "Usage: `basename $0` <provider name>"
+ exit 1
+fi
+provname=$1
+
+#
+# compute what to rename from ("capi", "models")
+#
+for file in $( ls | grep "gda-[a-z]*.h");
+do
+ base=$(echo $file | cut -b 5- | sed -e 's/\.h$//')
+done
+
+#
+# renaming files
+#
+for file in *
+do
+ newname=$(echo $file | sed -e "s/$base/$provname/")
+ if [ $file != $newname ]; then
+ mv $file $newname
+ fi
+done
+
+#
+# changing contents
+#
+upname=$(echo $provname | cut -b 1 | tr a-z A-Z)$(echo $provname | sed -e "s/^.//")
+allupname=$(echo $provname | tr a-z A-Z)
+bupname=$(echo $base | cut -b 1 | tr a-z A-Z)$(echo $base | sed -e "s/^.//")
+ballupname=$(echo $base | tr a-z A-Z)
+
+
+for file in *
+do
+ mv $file $file.1
+ cat $file.1 | sed -e "s/$base/$provname/g" -e "s/$bupname/$upname/g" -e "s/$ballupname/$allupname/g"> $file
+ rm -f $file.1
+done
Modified: branches/V4-branch/providers/skel-implementation/capi/gda-capi-ddl.h
==============================================================================
--- branches/V4-branch/providers/skel-implementation/capi/gda-capi-ddl.h (original)
+++ branches/V4-branch/providers/skel-implementation/capi/gda-capi-ddl.h Wed Feb 20 20:36:04 2008
@@ -1,5 +1,5 @@
/* GDA Capi provider
- * Copyright (C) 2006 - 2008 The GNOME Foundation
+ * Copyright (C) 2008 The GNOME Foundation
*
* AUTHORS:
* TO_ADD: your name and email
Modified: branches/V4-branch/providers/skel-implementation/capi/gda-capi-parser.h
==============================================================================
--- branches/V4-branch/providers/skel-implementation/capi/gda-capi-parser.h (original)
+++ branches/V4-branch/providers/skel-implementation/capi/gda-capi-parser.h Wed Feb 20 20:36:04 2008
@@ -29,10 +29,11 @@
G_BEGIN_DECLS
-#define GDA_TYPE_CAPI_PARSER (gda_capi_parser_get_type())
-#define GDA_CAPI_PARSER(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, gda_capi_parser_get_type(), GdaCapiParser)
-#define GDA_CAPI_PARSER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, gda_capi_parser_get_type (), GdaCapiParserClass)
-#define GDA_IS_CAPI_PARSER(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, gda_capi_parser_get_type ())
+#define GDA_TYPE_CAPI_PARSER (gda_capi_parser_get_type())
+#define GDA_CAPI_PARSER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_CAPI_PARSER, GdaCapiParser))
+#define GDA_CAPI_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_CAPI_PARSER, GdaCapiParserClass))
+#define GDA_IS_CAPI_PARSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_CAPI_PARSER))
+#define GDA_IS_CAPI_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDA_TYPE_CAPI_PARSER))
typedef struct _GdaCapiParser GdaCapiParser;
typedef struct _GdaCapiParserClass GdaCapiParserClass;
Modified: branches/V4-branch/providers/skel-implementation/capi/gda-capi-provider.c
==============================================================================
--- branches/V4-branch/providers/skel-implementation/capi/gda-capi-provider.c (original)
+++ branches/V4-branch/providers/skel-implementation/capi/gda-capi-provider.c Wed Feb 20 20:36:04 2008
@@ -151,7 +151,6 @@
provider_class->get_data_handler = gda_capi_provider_get_data_handler;
provider_class->get_def_dbms_type = gda_capi_provider_get_default_dbms_type;
- provider_class->create_connection = NULL;
provider_class->open_connection = gda_capi_provider_open_connection;
provider_class->close_connection = gda_capi_provider_close_connection;
provider_class->get_database = gda_capi_provider_get_database;
@@ -173,6 +172,10 @@
provider_class->statement_prepare = gda_capi_provider_statement_prepare;
provider_class->statement_execute = gda_capi_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_capi_meta_info;
provider_class->meta_funcs.btypes = _gda_capi_meta_btypes;
@@ -569,9 +572,8 @@
* Commit transaction request
*/
static gboolean
-gda_capi_provider_commit_transaction (GdaServerProvider *provider,
- GdaConnection *cnc,
- const gchar *name, GError **error)
+gda_capi_provider_commit_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error)
{
CapiConnectionData *cdata;
@@ -591,9 +593,8 @@
* Rollback transaction request
*/
static gboolean
-gda_capi_provider_rollback_transaction (GdaServerProvider *provider,
- GdaConnection *cnc,
- const gchar *name, GError **error)
+gda_capi_provider_rollback_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+ const gchar *name, GError **error)
{
CapiConnectionData *cdata;
@@ -678,6 +679,11 @@
static gboolean
gda_capi_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_obj (cnc) == provider, FALSE);
+ }
+
switch (feature) {
case GDA_CONNECTION_FEATURE_SQL :
return TRUE;
@@ -955,7 +961,7 @@
break;
}
- /* actual binding using the C API */
+ /* actual binding using the C API, for parameter at position @i */
const GValue *value = gda_holder_get_value (h);
TO_IMPLEMENT;
}
Modified: branches/V4-branch/providers/skel-implementation/capi/gda-capi-pstmt.h
==============================================================================
--- branches/V4-branch/providers/skel-implementation/capi/gda-capi-pstmt.h (original)
+++ branches/V4-branch/providers/skel-implementation/capi/gda-capi-pstmt.h Wed Feb 20 20:36:04 2008
@@ -29,10 +29,10 @@
G_BEGIN_DECLS
#define GDA_TYPE_CAPI_PSTMT (gda_capi_pstmt_get_type())
-#define GDA_CAPI_PSTMT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_PSTMT, GdaCapiPStmt))
-#define GDA_CAPI_PSTMT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_PSTMT, GdaCapiPStmtClass))
-#define GDA_IS_CAPI_PSTMT(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, GDA_TYPE_PSTMT))
-#define GDA_IS_CAPI_PSTMT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GDA_TYPE_PSTMT))
+#define GDA_CAPI_PSTMT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_CAPI_PSTMT, GdaCapiPStmt))
+#define GDA_CAPI_PSTMT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_CAPI_PSTMT, GdaCapiPStmtClass))
+#define GDA_IS_CAPI_PSTMT(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, GDA_TYPE_CAPI_PSTMT))
+#define GDA_IS_CAPI_PSTMT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GDA_TYPE_CAPI_PSTMT))
typedef struct _GdaCapiPStmt GdaCapiPStmt;
typedef struct _GdaCapiPStmtClass GdaCapiPStmtClass;
Modified: branches/V4-branch/providers/skel-implementation/capi/gda-capi-recordset.c
==============================================================================
--- branches/V4-branch/providers/skel-implementation/capi/gda-capi-recordset.c (original)
+++ branches/V4-branch/providers/skel-implementation/capi/gda-capi-recordset.c Wed Feb 20 20:36:04 2008
@@ -38,10 +38,10 @@
/* virtual methods */
static gint gda_capi_recordset_fetch_nb_rows (GdaPModel *model);
-static GdaPRow *gda_capi_recordset_fetch_random (GdaPModel *model, gint rownum, GError **error);
-static GdaPRow *gda_capi_recordset_fetch_next (GdaPModel *model, gint rownum, GError **error);
-static GdaPRow *gda_capi_recordset_fetch_prev (GdaPModel *model, gint rownum, GError **error);
-static GdaPRow *gda_capi_recordset_fetch_at (GdaPModel *model, gint rownum, GError **error);
+static gboolean gda_capi_recordset_fetch_random (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error);
+static gboolean gda_capi_recordset_fetch_next (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error);
+static gboolean gda_capi_recordset_fetch_prev (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error);
+static gboolean gda_capi_recordset_fetch_at (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error);
struct _GdaCapiRecordsetPrivate {
@@ -148,6 +148,11 @@
if (!cdata)
return NULL;
+ /* make sure @ps reports the correct number of columns using the API*/
+ if (_GDA_PSTMT (ps)->ncols < 0)
+ /*_GDA_PSTMT (ps)->ncols = ...;*/
+ TO_IMPLEMENT;
+
/* completing @ps if not yet done */
if (!_GDA_PSTMT (ps)->types && (_GDA_PSTMT (ps)->ncols > 0)) {
/* create prepared statement's columns */
@@ -223,84 +228,114 @@
}
/*
- * Create a new filled #GdaPRow object for the row at position @rownum.
+ * Create a new filled #GdaPRow 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 #GdaPRow object has to be created,
+ * - and otherwise *prow contains a #GdaPRow 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 GdaPRow object is left to the implementation, which
- * can use gda_pmodel_take_row().
+ * can use gda_pmodel_take_row(). If new row objects are "given" to the GdaPModel implemantation
+ * using that method, then this method should detect when all the data model rows have been analysed
+ * (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 GdaPRow *
-gda_capi_recordset_fetch_random (GdaPModel *model, gint rownum, GError **error)
+static gboolean
+gda_capi_recordset_fetch_random (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error)
{
GdaCapiRecordset *imodel;
- GdaPRow *prow = NULL;
imodel = GDA_CAPI_RECORDSET (model);
TO_IMPLEMENT;
- return prow;
+ return TRUE;
+}
+
+/*
+ * Create and "give" filled #GdaPRow object for all the rows in the model
+ */
+static gboolean
+gda_capi_recordset_store_all (GdaPModel *model, GError **error)
+{
+ GdaCapiRecordset *imodel;
+
+ imodel = GDA_CAPI_RECORDSET (model);
+
+ /* default implementation */
+ for (i = 0; i < model->advertized_nrows; i++) {
+ GdaPRow *prow;
+ if (! gda_capi_recordset_fetch_random (model, &prow, i, error))
+ return FALSE;
+ }
+ return TRUE;
}
/*
- * Create a new filled #GdaPRow object for the next cursor row
+ * Create a new filled #GdaPRow 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 #GdaPRow object has to be created,
+ * - and otherwise *prow contains a #GdaPRow 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 GdaPRow object is left to the implementation, which
* can use gda_pmodel_take_row().
*/
-static GdaPRow *
-gda_capi_recordset_fetch_next (GdaPModel *model, gint rownum, GError **error)
+static gboolean
+gda_capi_recordset_fetch_next (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error)
{
- GdaPRow *prow;
GdaCapiRecordset *imodel = (GdaCapiRecordset*) model;
- prow = gda_pmodel_get_stored_row (model, rownum);
- if (prow)
- return prow;
-
TO_IMPLEMENT;
- return prow;
+ return TRUE;
}
/*
- * Create a new filled #GdaPRow object for the previous cursor row
+ * Create a new filled #GdaPRow 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 #GdaPRow object has to be created,
+ * - and otherwise *prow contains a #GdaPRow 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 GdaPRow object is left to the implementation, which
* can use gda_pmodel_take_row().
*/
-static GdaPRow *
-gda_capi_recordset_fetch_prev (GdaPModel *model, gint rownum, GError **error)
+static gboolean
+gda_capi_recordset_fetch_prev (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error)
{
- GdaPRow *prow;
GdaCapiRecordset *imodel = (GdaCapiRecordset*) model;
- prow = gda_pmodel_get_stored_row (model, rownum);
- if (prow)
- return prow;
-
TO_IMPLEMENT;
- return prow;
+ return TRUE;
}
/*
- * Create a new filled #GdaPRow object for the cursor row at position @rownum
+ * Create a new filled #GdaPRow 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 #GdaPRow object has to be created,
+ * - and otherwise *prow contains a #GdaPRow 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 GdaPRow object is left to the implementation, which
* can use gda_pmodel_take_row().
*/
-static GdaPRow *
-gda_capi_recordset_fetch_at (GdaPModel *model, gint rownum, GError **error)
+static gboolean
+gda_capi_recordset_fetch_at (GdaPModel *model, GdaPRow **prow, gint rownum, GError **error)
{
- GdaPRow *prow;
GdaCapiRecordset *imodel = (GdaCapiRecordset*) model;
-
- prow = gda_pmodel_get_stored_row (model, rownum);
- if (prow)
- return prow;
TO_IMPLEMENT;
- return prow;
+ return TRUE;
}
Modified: branches/V4-branch/providers/skel-implementation/capi/gda-capi-recordset.h
==============================================================================
--- branches/V4-branch/providers/skel-implementation/capi/gda-capi-recordset.h (original)
+++ branches/V4-branch/providers/skel-implementation/capi/gda-capi-recordset.h Wed Feb 20 20:36:04 2008
@@ -1,5 +1,5 @@
/* GDA Capi provider
- * Copyright (C) 1998 - 2006 The GNOME Foundation.
+ * Copyright (C) 2008 The GNOME Foundation.
*
* AUTHORS:
* TO_ADD: your name and email
Modified: branches/V4-branch/providers/skel-implementation/capi/libgda-capi-4.0.pc.in
==============================================================================
--- branches/V4-branch/providers/skel-implementation/capi/libgda-capi-4.0.pc.in (original)
+++ branches/V4-branch/providers/skel-implementation/capi/libgda-capi-4.0.pc.in Wed Feb 20 20:36:04 2008
@@ -4,6 +4,6 @@
includedir= includedir@
Name: libgda-postgres- GDA_ABI_MAJOR_VERSION@ GDA_ABI_MINOR_VERSION@
-Description: GDA (GNOME Data Access) CApi provider
+Description: GDA (GNOME Data Access) Capi provider
Requires: libgda- GDA_ABI_MAJOR_VERSION@ GDA_ABI_MINOR_VERSION@
Version: @VERSION@
Modified: branches/V4-branch/tools/gda-sql.c
==============================================================================
--- branches/V4-branch/tools/gda-sql.c (original)
+++ branches/V4-branch/tools/gda-sql.c Wed Feb 20 20:36:04 2008
@@ -1028,10 +1028,19 @@
FILE *to_stream;
gboolean append_nl = FALSE;
gint length;
+ static gint force_no_pager = -1;
if (!str)
return;
+ if (force_no_pager < 0) {
+ /* still unset... */
+ if (getenv ("GDA_NO_PAGER"))
+ force_no_pager = 1;
+ else
+ force_no_pager = 0;
+ }
+
length = strlen (str);
if (str[length] != '\n')
append_nl = TRUE;
@@ -1041,7 +1050,7 @@
else
to_stream = stdout;
- if (isatty (fileno (to_stream))) {
+ if (!force_no_pager && isatty (fileno (to_stream))) {
/* use pager */
FILE *pipe;
const char *pager;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]