/* $Id: connection.hg,v 1.35 2006/11/30 06:58:55 murrayc Exp $ */
// -*- C++ -*- // 

/* connection.h
 * 
 * Copyright 2003 libgdamm Development Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <libgdamm/transactionstatus.h>
#include <libgdamm/connectionevent.h>
#include <libgdamm/datamodel.h>
#include <libgdamm/statement.h>
#include <libgdamm/sqlbuilder.h>
#include <libgdamm/sqlparser.h>
#include <libgdamm/serveroperation.h>
#include <libgdamm/serverprovider.h> //For ServerProviderError, sometimes thrown by *execute*().
#include <libgdamm/value.h>

_DEFS(libgdamm,libgda)
_PINCLUDE(glibmm/private/object_p.h)

namespace Gnome
{

namespace Gda
{

class ServerProvider;
class MetaStore;
typedef GdaServerProviderInfo ServerProviderInfo;

/** Exception class for Gda connection errors.
 */
_WRAP_GERROR(ConnectionError, GdaConnectionError, GDA_CONNECTION_ERROR)

_WRAP_ENUM(ConnectionSchema, GdaConnectionSchema)
_WRAP_ENUM(ConnectionOptions, GdaConnectionOptions)
_WRAP_ENUM(ConnectionMetaType, GdaConnectionMetaType)

/** Manages a connection to a data source.
 * This class offers access to all operations involving an opened connection to a database. 
 * Connection objects are obtained via the Client class.
 * 
 * Once obtained, applications can use a Connection to execute commands, run transactions, and get information about all 
 * objects stored in the underlying database.
 *
 * @ingroup Connections
 */
class Connection : public Glib::Object
{
  _CLASS_GOBJECT(Connection, GdaConnection, GDA_CONNECTION, Glib::Object, GObject)
protected:

  _CTOR_DEFAULT
  _CUSTOM_DTOR()

public:
  _WRAP_CREATE()

  /* This throws an error which we don't want to do in a constructor */
  _WRAP_METHOD_DOCS_ONLY(gda_connection_open_from_string)
#ifdef GLIBMM_EXCEPTIONS_ENABLED
  static Glib::RefPtr<Connection> open_from_string(const Glib::ustring& provider_name,
                                                    const Glib::ustring& cnc_string,
                                                    const Glib::ustring& auth_string = Glib::ustring(),
                                                    ConnectionOptions options = CONNECTION_OPTIONS_NONE);
#else
  static Glib::RefPtr<Connection> open_from_string(const Glib::ustring& provider_name,
                                                    const Glib::ustring& cnc_string,
                                                    const Glib::ustring& auth_string,
                                                    ConnectionOptions options,
                                                    std::auto_ptr<Glib::Error>& error);
#endif

  _WRAP_METHOD(bool open(), gda_connection_open, errthrow)

  /** Closes the connection to the underlying data source, but first emits the "conn_to_close" signal.
   */
  void close();
  _IGNORE(gda_connection_close, gda_connection_close_no_warning)

  _IGNORE(gda_connection_internal_transaction_started, gda_connection_internal_savepoint_added,
          gda_connection_internal_treat_sql, gda_connection_internal_transaction_rolledback,
          gda_connection_internal_savepoint_removed, gda_connection_internal_transaction_committed,
          gda_connection_internal_savepoint_rolledback, gda_connection_execute_command,
          gda_connection_internal_sql_executed, gda_connection_internal_change_transaction_state,
          gda_connection_internal_statement_executed, gda_connection_internal_get_provider_data,
          gda_connection_internal_set_provider_data, gda_connection_add_prepared_statement,
          gda_connection_del_prepared_statement, gda_connection_get_prepared_statement)

  _WRAP_METHOD(bool is_opened() const, gda_connection_is_opened)

  _WRAP_METHOD(ConnectionOptions get_options() const, gda_connection_get_options)

  _WRAP_METHOD(Glib::ustring get_dsn() const, gda_connection_get_dsn)
  _WRAP_METHOD(Glib::ustring get_cnc_string() const, gda_connection_get_cnc_string)

  _WRAP_METHOD(void add_event(const Glib::RefPtr<ConnectionEvent>& event), gda_connection_add_event)
  _WRAP_METHOD(Glib::RefPtr<ConnectionEvent> add_event_string(const Glib::ustring& str), gda_connection_add_event_string)

  _WRAP_METHOD(void clear_events_list(), gda_connection_clear_events_list)

  _WRAP_METHOD_DOCS_ONLY(gda_connection_statement_execute)

#ifdef GLIBMM_EXCEPTIONS_ENABLED
  Glib::RefPtr<Glib::Object> statement_execute(const Glib::RefPtr<const Statement>& stmt, const Glib::RefPtr<const Set>& params, StatementModelUsage model_usage, Glib::RefPtr<const Set>& last_insert_row);
#else
  Glib::RefPtr<Glib::Object> statement_execute(const Glib::RefPtr<const Statement>& stmt, const Glib::RefPtr<const Set>& params, StatementModelUsage model_usage, Glib::RefPtr<const Set>& last_insert_row, std::auto_ptr<Glib::Error>& error);
#endif // GLIBMM_EXCEPTIONS_ENABLED

//TODO: Documentation:
#ifdef GLIBMM_EXCEPTIONS_ENABLED
  Glib::RefPtr<Glib::Object> statement_execute(const Glib::RefPtr<const Statement>& stmt, StatementModelUsage model_usage = STATEMENT_MODEL_RANDOM_ACCESS);
#else
  Glib::RefPtr<Glib::Object> statement_execute(const Glib::RefPtr<const Statement>& stmt, StatementModelUsage model_usage, std::auto_ptr<Glib::Error>& error);
#endif // GLIBMM_EXCEPTIONS_ENABLED


#ifdef GLIBMM_EXCEPTIONS_ENABLED
  Glib::RefPtr<Glib::Object> statement_execute(const Glib::ustring& sql, StatementModelUsage model_usage = STATEMENT_MODEL_RANDOM_ACCESS);
#else
  Glib::RefPtr<Glib::Object> statement_execute(const Glib::ustring& sql, StatementModelUsage model_usage, std::auto_ptr<Glib::Error>& error);
#endif // GLIBMM_EXCEPTIONS_ENABLED
  
  //_WRAP_METHOD(Glib::RefPtr<DataModel> statement_execute_select(const Glib::RefPtr<const Statement>& stmt, const Glib::RefPtr<const Set>& params), gda_connection_statement_execute_select, errthrow)
  _IGNORE(gda_connection_statement_execute_select) //We instead create a version that can take an optional usage parameter too.

#m4 _CONVERSION(`const std::vector<GType>&', `GType*', `const_cast<GType*>(Glib::ArrayHandler<GType>::vector_to_array($3).data())')
  _WRAP_METHOD(Glib::RefPtr<DataModel> statement_execute_select(const Glib::RefPtr<const Statement>& stmt, const Glib::RefPtr<const Set>& params, StatementModelUsage model_usage, const std::vector<GType>& col_types), gda_connection_statement_execute_select_full, errthrow)
  _IGNORE(gda_connection_statement_execute_select_fullv)
  
//TODO: Documentation:
#ifdef GLIBMM_EXCEPTIONS_ENABLED
  Glib::RefPtr<DataModel> statement_execute_select(const Glib::RefPtr<const Statement>& stmt, StatementModelUsage model_usage = STATEMENT_MODEL_RANDOM_ACCESS);
  Glib::RefPtr<DataModel> statement_execute_select(const Glib::ustring& sql, StatementModelUsage model_usage = STATEMENT_MODEL_RANDOM_ACCESS);
#else
  Glib::RefPtr<DataModel> statement_execute_select(const Glib::RefPtr<const Statement>& stmt, StatementModelUsage model_usage, std::auto_ptr<Glib::Error>& error);
  Glib::RefPtr<DataModel> statement_execute_select(const Glib::ustring& sql, StatementModelUsage model_usage, std::auto_ptr<Glib::Error>& error);
#endif

  
//TODO: Documentation:
#ifdef GLIBMM_EXCEPTIONS_ENABLED
  Glib::RefPtr<DataModel> statement_execute_select(const Glib::RefPtr<const Statement>& stmt, const Glib::RefPtr<const Set>& params, StatementModelUsage model_usage = STATEMENT_MODEL_RANDOM_ACCESS);
  Glib::RefPtr<DataModel> statement_execute_select(const Glib::ustring& sql, const Glib::RefPtr<const Set>& params, StatementModelUsage model_usage = STATEMENT_MODEL_RANDOM_ACCESS);
#else
  Glib::RefPtr<DataModel> statement_execute_select(const Glib::RefPtr<const Statement>& stmt, const Glib::RefPtr<const Set>& params, StatementModelUsage model_usage, std::auto_ptr<Glib::Error>& error);
  Glib::RefPtr<DataModel> statement_execute_select(const Glib::ustring& sql, const Glib::RefPtr<const Set>& params, StatementModelUsage model_usage, std::auto_ptr<Glib::Error>& error);
#endif


//TODO: Documentation:
//Note that we add the prefix _builder() because overloading on different RefPtr<> types seems to be ambiguous.
#ifdef GLIBMM_EXCEPTIONS_ENABLED
  Glib::RefPtr<DataModel> statement_execute_select_builder(const Glib::RefPtr<const SqlBuilder>& stmt, StatementModelUsage model_usage = STATEMENT_MODEL_RANDOM_ACCESS);
#else
  Glib::RefPtr<DataModel> statement_execute_select_builder(const Glib::RefPtr<const SqlBuilder>& stmt, StatementModelUsage model_usage, std::auto_ptr<Glib::Error>& error);
#endif

//TODO: Documentation:
//Note that we add the prefix _builder() because overloading on different RefPtr<> types seems to be ambiguous.
#ifdef GLIBMM_EXCEPTIONS_ENABLED
  Glib::RefPtr<DataModel> statement_execute_select_builder(const Glib::RefPtr<const SqlBuilder>& stmt, const Glib::RefPtr<const Set>& params, StatementModelUsage model_usage = STATEMENT_MODEL_RANDOM_ACCESS);
#else
  Glib::RefPtr<DataModel> statement_execute_select_builder(const Glib::RefPtr<const SqlBuilder>& stmt, const Glib::RefPtr<const Set>& params, StatementModelUsage model_usage, std::auto_ptr<Glib::Error>& error);
#endif

  
  _WRAP_METHOD_DOCS_ONLY(gda_connection_statement_execute_non_select)
//TODO: Documentation:
//TODO: It's not nice that these two method overloads differ only by the constness of the set.
#ifdef GLIBMM_EXCEPTIONS_ENABLED
  int statement_execute_non_select(const Glib::RefPtr<const Statement>& stmt, const Glib::RefPtr<const Set>& params, Glib::RefPtr<const Set>& last_insert_row);
#else
  int statement_execute_non_select(const Glib::RefPtr<const Statement>& stmt, const Glib::RefPtr<const Set>& params, Glib::RefPtr<const Set>& last_insert_row, std::auto_ptr<Glib::Error>& error);
#endif //GLIBMM_EXCEPTIONS_ENABLED  

//TODO: Documentation:
#ifdef GLIBMM_EXCEPTIONS_ENABLED
  int statement_execute_non_select(const Glib::RefPtr<const Statement>& stmt, const Glib::RefPtr<const Set>& params);
#else
  int statement_execute_non_select(const Glib::RefPtr<const Statement>& stmt, const Glib::RefPtr<const Set>& params, std::auto_ptr<Glib::Error>& error);
#endif //GLIBMM_EXCEPTIONS_ENABLED

//TODO: Documentation:
#ifdef GLIBMM_EXCEPTIONS_ENABLED
  int statement_execute_non_select(const Glib::RefPtr<const Statement>& stmt);
#else
  int statement_execute_non_select(const Glib::RefPtr<const Statement>& stmt, std::auto_ptr<Glib::Error>& error);
#endif //GLIBMM_EXCEPTIONS_ENABLED

  _WRAP_METHOD(int statement_execute_non_select(const Glib::ustring& sql), gda_execute_non_select_command, errthrow)
  

//TODO: Documentation:
#ifdef GLIBMM_EXCEPTIONS_ENABLED
  int statement_execute_non_select_builder(const Glib::RefPtr<const SqlBuilder>& builder);
#else
  int statement_execute_non_select_builder(const Glib::RefPtr<const SqlBuilder>& builder, std::auto_ptr<Glib::Error>& error);
#endif //GLIBMM_EXCEPTIONS_ENABLED

//TODO: Documentation:
#ifdef GLIBMM_EXCEPTIONS_ENABLED
  int statement_execute_non_select_builder(const Glib::RefPtr<const SqlBuilder>& builder, const Glib::RefPtr<const Set>& params);
#else
  int statement_execute_non_select_builder(const Glib::RefPtr<const SqlBuilder>& builder, const Glib::RefPtr<const Set>& params, std::auto_ptr<Glib::Error>& error);
#endif //GLIBMM_EXCEPTIONS_ENABLED


  //Note that we do not add a non-const version of this because the documentation says that it should not be modified:
  _WRAP_METHOD(Glib::RefPtr<const TransactionStatus> get_transaction_status() const, gda_connection_get_transaction_status, errthrow, refreturn)

  _WRAP_METHOD(bool begin_transaction(const Glib::ustring& name, TransactionIsolation level), gda_connection_begin_transaction, errthrow)
  _WRAP_METHOD(bool commit_transaction(const Glib::ustring& name), gda_connection_commit_transaction, errthrow)
  _WRAP_METHOD(bool rollback_transaction(const Glib::ustring& name), gda_connection_rollback_transaction, errthrow)

  _WRAP_METHOD(bool add_savepoint(const Glib::ustring& name), gda_connection_add_savepoint, errthrow)
  _WRAP_METHOD(bool rollback_savepoint(const Glib::ustring& name), gda_connection_rollback_savepoint, errthrow)
  _WRAP_METHOD(bool delete_savepoint(const Glib::ustring& name), gda_connection_delete_savepoint, errthrow)


   //Use a special conversion here, because we should not own any part of the returned GList, 
   //according to the documentation for gda_connection_get_events(), 
   //and because we need to unconst the GList.:
   #m4 _CONVERSION(`const GList*',`std::vector< Glib::RefPtr<ConnectionEvent> >',`$2(Glib::ListHandler< Glib::RefPtr<ConnectionEvent> >::list_to_vector(const_cast<GList*>($3), Glib::OWNERSHIP_NONE))')
   #m4 _CONVERSION(`const GList*',`std::vector< Glib::RefPtr<const ConnectionEvent> >',`$2(Glib::ListHandler< Glib::RefPtr<const ConnectionEvent> >::list_to_vector(const_cast<GList*>($3), Glib::OWNERSHIP_NONE))')

  _WRAP_METHOD(std::vector< Glib::RefPtr<ConnectionEvent> > get_events(), gda_connection_get_events)
  _WRAP_METHOD(std::vector< Glib::RefPtr<const ConnectionEvent> > get_events() const, gda_connection_get_events)


  _WRAP_METHOD(Glib::ustring value_to_sql_string(const Value& from) const, gda_connection_value_to_sql_string)

  _WRAP_METHOD(bool supports_feature(ConnectionFeature feature) const, gda_connection_supports_feature)
      
  _WRAP_METHOD(Glib::ustring get_authentication() const, gda_connection_get_authentication)
  _WRAP_METHOD(Glib::RefPtr<SqlParser> create_parser(), gda_connection_create_parser)

  _WRAP_METHOD(Glib::ustring get_provider_name() const, gda_connection_get_provider_name)


#ifdef GLIBMM_EXCEPTIONS_ENABLED
  /** Update the full meta store information.
   * 
   * Note that this may take some time for big databases
   * @return <tt>true</tt> if no error occurred.
   */
  bool update_meta_store();
#else
  bool update_meta_store(std::auto_ptr<Glib::Error>& err);
#endif

#ifdef GLIBMM_EXCEPTIONS_ENABLED
  /** Update the meta store information @a id
   * 
   * This can speed up the update of the meta store if you only need a 
   * specific information
   * @param id An id for the information to update (see http://library.gnome.org/devel/libgda/unstable/information_schema.html)
   * @return <tt>true</tt> if no error occurred.
   */
  bool update_meta_store(const Glib::ustring& id);
#else
  bool update_meta_store(const Glib::ustring& id, std::auto_ptr<Glib::Error>& err);
#endif
  _IGNORE(gda_connection_update_meta_store)

#ifdef GLIBMM_EXCEPTIONS_ENABLED
  /** Update the meta store information for the table named @a table_name
   * 
   * This can speed up the update of the meta store if you only need the information
   * for a specific table
   * @param table_name Name of the table where the information is needed
   * @param schema_name Name of the schema @table_name is in, or "" to update
   * all tables with the given name. As with update_meta_store_table_names(),
   * specifying a non-empty schema_name speeds up the operation, just not as
   * significantly as with update_meta_store_table_names() because the
   * operation itself is already pretty quick.
   * @return <tt>true</tt> if no error occurred.
   */
  bool update_meta_store_table(const Glib::ustring& table_name, const Glib::ustring& schema_name = Glib::ustring());
#else
  bool update_meta_store_table(const Glib::ustring& table_name, const Glib::ustring& schema_name, std::auto_ptr<Glib::Error>& err);
#endif //GLIBMM_EXCEPTIONS_ENABLED

#ifdef GLIBMM_EXCEPTIONS_ENABLED
  /** Update the meta store information for the table names.
   * 
   * This can speed up the update of the meta store if you only need the list of table names.
   *
   * @param schema_name Name of the schema whose tables to update, or "" to
   * update all tables. For example, for postgresql this function takes a
   * considerable amount of time when doing this. If you don't need
   * information about internal postgresql tables, then you can speed up the
   * process by only updating the tables for the "public" schema.
   * Unfortunately, schema names are not common between different database
   * systems.
   * @return <tt>true</tt> if no error occurred.
   */
  bool update_meta_store_table_names(const Glib::ustring& schema_name = Glib::ustring());
#else
  bool update_meta_store_table_names(const Glib::ustring& schema_name, std::auto_ptr<Glib::Error>& err);
#endif //GLIBMM_EXCEPTIONS_ENABLED

#ifdef GLIBMM_EXCEPTIONS_ENABLED
  /** Update the meta store information for the data types.
   * 
   * This can speed up the update of the meta store if you only need the data types.
   *
   * @return <tt>true</tt> if no error occurred.
   */
  bool update_meta_store_data_types();
#else
  bool update_meta_store_data_types(std::auto_ptr<Glib::Error>& err);
#endif //GLIBMM_EXCEPTIONS_ENABLED
  
  _WRAP_METHOD(bool statement_prepare(const Glib::RefPtr<const Statement>& stmt), gda_connection_statement_prepare, errthrow)


  #m4 _CONVERSION(`const std::vector< Glib::RefPtr<Holder> >&',`GList*',`Glib::ListHandler< Glib::RefPtr<Holder> >::vector_to_list($3).data()')
  _WRAP_METHOD(Glib::RefPtr<DataModel> get_meta_store_data(ConnectionMetaType meta_type, const std::vector< Glib::RefPtr<Holder> >& filters), gda_connection_get_meta_store_data_v, errthrow)
  _IGNORE(gda_connection_get_meta_store_data)

  #ifdef GLIBMM_EXCEPTIONS_ENABLED
  Glib::RefPtr<DataModel> get_meta_store_data(ConnectionMetaType meta_type);
  #else
  Glib::RefPtr<DataModel> get_meta_store_data(ConnectionMetaType meta_type, std::auto_ptr<Glib::Error>& error);
  #endif //GLIBMM_EXCEPTIONS_ENABLED

  _WRAP_METHOD(Glib::RefPtr<MetaStore> get_meta_store(), gda_connection_get_meta_store, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const MetaStore> get_meta_store() const, gda_connection_get_meta_store, constversion)

  #m4 _CONVERSION(`GSList*',`std::vector< Glib::RefPtr<Glib::Object> >',`$2(Glib::SListHandler< Glib::RefPtr<Glib::Object > >::slist_to_vector($3, Glib::OWNERSHIP_SHALLOW))')
  _WRAP_METHOD(std::vector< Glib::RefPtr<Glib::Object> > batch_execute(const Glib::RefPtr<Batch>& batch, const Glib::RefPtr<const Set>& params, StatementModelUsage model_usage), gda_connection_batch_execute, errthrow)

  _WRAP_METHOD(Glib::RefPtr<ServerOperation> create_operation(ServerOperationType type, const Glib::RefPtr<const Set>& options), gda_connection_create_operation, errthrow)
  _WRAP_METHOD(bool perform_operation(const Glib::RefPtr<ServerOperation>& op), gda_connection_perform_operation, errthrow)

  _WRAP_METHOD(Glib::RefPtr<ServerProvider> get_provider(), gda_connection_get_provider, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const ServerProvider> get_provider() const, gda_connection_get_provider, refreturn)
   
  _WRAP_METHOD(Glib::ustring quote_sql_identifier(const Glib::ustring& id) const, gda_connection_quote_sql_identifier)
   
  _WRAP_METHOD_DOCS_ONLY(gda_connection_statement_to_sql)

  //TODO: Add an overload without the params_used.
  #ifdef GLIBMM_EXCEPTIONS_ENABLED
  Glib::ustring statement_to_sql(const Glib::RefPtr<const Statement>& stmt, const Glib::RefPtr<const Set>& params, StatementSqlFlag flags, std::vector< Glib::RefPtr<Holder> >& params_used) const;
  #else
  Glib::ustring statement_to_sql(const Glib::RefPtr<const Statement>& stmt, const Glib::RefPtr<const Set>& params, StatementSqlFlag flags, std::vector< Glib::RefPtr<Holder> >& params_used, std::auto_ptr<Glib::Error>& error) const;
  #endif //GLIBMM_EXCEPTIONS_ENABLED
  
  _IGNORE(gda_connection_internal_reset_transaction_status)
    
  #m4 _CONVERSION(`GdaConnectionEvent*',`const Glib::RefPtr<ConnectionEvent>&',Glib::wrap($3, true))
  _WRAP_SIGNAL(void error(const Glib::RefPtr<ConnectionEvent>& error), "error")

  _WRAP_SIGNAL(void conn_opened(), "conn_opened")
  _WRAP_SIGNAL(void conn_to_close(), "conn_to_close")
  _WRAP_SIGNAL(void conn_closed(), "conn_closed")
  _WRAP_SIGNAL(void dsn_changed(), "dsn_changed")
  _WRAP_SIGNAL(void transaction_status_changed(), "transaction_status_changed")

  _WRAP_PROPERTY("auth-string", Glib::ustring)
  _WRAP_PROPERTY("cnc-string", Glib::ustring)
  _WRAP_PROPERTY("dsn", Glib::ustring)
  _WRAP_PROPERTY("meta-store", Glib::RefPtr<MetaStore>)
  _WRAP_PROPERTY("options", ConnectionOptions)
};

} // namespace Gda
} // namespace Gnome

