/*
 * Copyright (c) 2008, 2024, Oracle and/or its affiliates.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2.0, as
 * published by the Free Software Foundation.
 *
 * This program is designed to work with certain software (including
 * but not limited to OpenSSL) that is licensed under separate terms, as
 * designated in a particular file or component or in included license
 * documentation. The authors of MySQL hereby grant you an additional
 * permission to link the program and your derivative works with the
 * separately licensed software that they have either included with
 * the program or referenced in the documentation.
 *
 * Without limiting anything contained in the foregoing, this file,
 * which is part of Connector/C++, is also subject to the
 * Universal FOSS Exception, version 1.0, a copy of which can be found at
 * https://oss.oracle.com/licenses/universal-foss-exception.
 *
 * This program 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 General Public License, version 2.0, for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */



#ifndef _MYSQL_CONNECTION_H_
#define _MYSQL_CONNECTION_H_

#include "cppconn/connection.h"
#include <memory>

#if(_WIN32 && CONCPP_BUILD_SHARED)
extern std::string driver_dll_path;
#endif
extern std::string default_plugin_dir;

namespace sql
{
namespace mysql
{

class CPPCONN_PUBLIC_FUNC MySQL_Savepoint : public sql::Savepoint
{
  sql::SQLString name;

public:
  MySQL_Savepoint(const sql::SQLString &savepoint);
  virtual ~MySQL_Savepoint() {}

  int getSavepointId();

  sql::SQLString getSavepointName();

private:
  /* Prevent use of these */
  MySQL_Savepoint(const MySQL_Savepoint &);
  void operator=(MySQL_Savepoint &);
};


class MySQL_DebugLogger;
struct MySQL_ConnectionData; /* PIMPL */
class MySQL_Statement;
class MySQL_Prepared_Statement;

namespace NativeAPI
{
class NativeConnectionWrapper;
struct st_mysql_client_plugin;
extern std::map <::sql::SQLString, st_mysql_client_plugin*> plugins_cache;
}

class CPPCONN_PUBLIC_FUNC MySQL_Connection : public sql::Connection
{
  friend MySQL_Statement;
  friend MySQL_Prepared_Statement;

  MySQL_Statement * createServiceStmt();

public:

  using Proxy = ::sql::mysql::NativeAPI::NativeConnectionWrapper;

  MySQL_Connection(Driver * _driver, Proxy& _proxy,
          const sql::SQLString& hostName,
          const sql::SQLString& userName,
          const sql::SQLString& password);

  MySQL_Connection(Driver * _driver, Proxy & _proxy,
          std::map< sql::SQLString, sql::ConnectPropertyVal > & options);

  virtual ~MySQL_Connection();

  void clearWarnings();

  void close();

  void commit();

  sql::Statement * createStatement();

  sql::SQLString escapeString(const sql::SQLString &);

  bool getAutoCommit();

  sql::SQLString getCatalog();

  Driver *getDriver();

  sql::SQLString getSchema();

  sql::SQLString getClientInfo();

  void getClientOption(const sql::SQLString & optionName, void * optionValue);

  sql::SQLString getClientOption(const sql::SQLString & optionName);

  sql::DatabaseMetaData * getMetaData();

  enum_transaction_isolation getTransactionIsolation();

  const SQLWarning * getWarnings();

  bool isClosed();

  bool isReadOnly();

  bool isValid();

  bool reconnect();

  sql::SQLString nativeSQL(const sql::SQLString& sql);

  sql::PreparedStatement * prepareStatement(const sql::SQLString& sql);

  sql::PreparedStatement * prepareStatement(const sql::SQLString& sql, int autoGeneratedKeys);

  sql::PreparedStatement * prepareStatement(const sql::SQLString& sql, int columnIndexes[]);

  sql::PreparedStatement * prepareStatement(const sql::SQLString& sql, int resultSetType, int resultSetConcurrency);

  sql::PreparedStatement * prepareStatement(const sql::SQLString& sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability);

  sql::PreparedStatement * prepareStatement(const sql::SQLString& sql, sql::SQLString columnNames[]);

  void releaseSavepoint(Savepoint * savepoint) ;

  void rollback();

  void rollback(Savepoint * savepoint);

  void setAutoCommit(bool autoCommit);

  void setCatalog(const sql::SQLString& catalog);

  void setSchema(const sql::SQLString& catalog);

  sql::Connection * setClientOption(const sql::SQLString & optionName, const void * optionValue);

  sql::Connection * setClientOption(const sql::SQLString & optionName, const sql::SQLString & optionValue);

  void setHoldability(int holdability);

  void setReadOnly(bool readOnly);

  sql::Savepoint * setSavepoint();

  sql::Savepoint * setSavepoint(const sql::SQLString& name);

  void setTransactionIsolation(enum_transaction_isolation level);

  virtual sql::SQLString getSessionVariable(const sql::SQLString & varname);

  virtual void setSessionVariable(const sql::SQLString & varname, const sql::SQLString & value);

  virtual void setSessionVariable(const sql::SQLString & varname, unsigned int value);

  virtual sql::SQLString getLastStatementInfo();

  sql::SQLString getCurrentUser();

private:
  /* We do not really think this class has to be subclassed*/
  void checkClosed();
  void init(std::map< sql::SQLString, sql::ConnectPropertyVal > & properties);

  Driver * driver;

#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable: 4251)
#endif
  std::shared_ptr<Proxy> proxy;
#ifdef _WIN32
#pragma warning(pop)
#endif

  /* statement handle to execute queries initiated by driver. Perhaps it is
     a good idea to move it to a separate helper class */
#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable: 4251)
#endif
  std::unique_ptr< ::sql::mysql::MySQL_Statement > service;

  std::unique_ptr< ::sql::mysql::MySQL_ConnectionData > intern; /* pimpl */
#ifdef _WIN32
#pragma warning(pop)
#endif

  /* We need to store the user name for telemetry */
  SQLString currentUser;

  /* Prevent use of these */
  MySQL_Connection(const MySQL_Connection &);
  void operator=(MySQL_Connection &);

  struct PluginGuard;
};

class MySQL_Driver;

/*
  A PluginGuard instance is used to lock plugin options so that no other
  connections can set them while the guard exists.

  It also manages the callback setting of the WebAuthN authentication
  plugin -- see register_webauthn_callback() method.
*/

struct MySQL_Connection::PluginGuard
{
  std::weak_ptr<Proxy> prx;

  PluginGuard(MySQL_Connection *c);
  ~PluginGuard();

  /*
    Depending on whether user has set the WebAuthN callback
    for the driver or not, this method arranges for either
    the driver's callback or the default callback be called
    in case WebAuthN authentication is used while making
    a connection.
  */
  void register_webauthn_callback(MySQL_Driver &drv);

  private:

  /*
    State of the WebAuthN plugin:

    NONE    -- plugin is not loaded;
    DEFAULT -- plugin is loaded and uses default callback;
    CALLER  -- plugin is loaded and its callback is set to
               the `callback_caller()` function.
  */
  static
  enum class state { NONE, DEFAULT, CALLER } webauthn_plugin_state;

  // The driver whose callback is called by `callback_caller()`
  static sql::mysql::MySQL_Driver * callback_drv;

  /*
    Callback function to be registered with WebAuthN authentication plugin.
    It calls the callback function of `callback_drv` if that is present.
  */
  static void callback_caller(const char* msg);
};

} /* namespace mysql */
} /* namespace sql */

#endif // _MYSQL_CONNECTION_H_

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */
