/* Copyright (C) Olivier Bertrand 2004 - 2013

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; version 2 of the License.

  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 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/** @file ha_connect.h

    @brief
  The ha_connect engine is a prototype storage engine to access external data.

   @see
  /sql/handler.h and /storage/connect/ha_connect.cc
*/

#ifdef USE_PRAGMA_INTERFACE
#pragma interface     /* gcc class implementation */
#endif

/****************************************************************************/
/*  Structures used to pass info between CONNECT and ha_connect.            */
/****************************************************************************/
typedef struct _create_xinfo {
  char *Type;                               /* Retrieved from table comment */
  char *Filename;                           /* Set if not standard          */
  char *IndexFN;                            /* Set if not standard          */
  ulonglong Maxrows;                        /* Estimated max nb of rows     */
  ulong Lrecl;                              /* Set if not default           */
  ulong Elements;                           /* Number of lines in blocks    */
  bool  Fixed;                              /* False for DOS type           */
  void *Pcf;                                /* To list of columns           */
  void *Pxdf;                               /* To list of indexes           */
} CRXINFO, *PCXF;

typedef struct _xinfo {
  ulonglong data_file_length;               /* Length of data file          */
  ha_rows   records;                        /* Records in table             */
  ulong     mean_rec_length;                /* Physical record length       */
  char     *data_file_name;                 /* Physical file name           */
} XINFO, *PXF;

class XCHK : public BLOCK {
public:
  XCHK(void) {oldsep= newsep= false; oldpix= newpix= NULL;}
  bool         oldsep;              // Sepindex before create/alter
  bool         newsep;              // Sepindex after create/alter
  PIXDEF       oldpix;              // The indexes before create/alter
  PIXDEF       newpix;              // The indexes after create/alter
}; // end of class XCHK

typedef class XCHK *PCHK;
typedef class user_connect *PCONNECT;
typedef struct ha_table_option_struct TOS, *PTOS;
typedef struct ha_field_option_struct FOS, *PFOS; 

extern handlerton *connect_hton;

/**
  structure for CREATE TABLE options (table options)

  These can be specified in the CREATE TABLE:
  CREATE TABLE ( ... ) {...here...}
*/
struct ha_table_option_struct {
  const char *type;
  const char *filename;
  const char *optname;
  const char *tabname;
  const char *tablist;
  const char *dbname;
  const char *separator;
//const char *connect;
  const char *qchar;
  const char *module;
  const char *subtype;
  const char *catfunc;
  const char *srcdef;
  const char *colist;
  const char *oplist;
  const char *data_charset;
  ulonglong lrecl;
  ulonglong elements;
//ulonglong estimate;
  ulonglong multiple;
  ulonglong header;
  ulonglong quoted;
  ulonglong ending;
  ulonglong compressed;
  bool mapped;
  bool huge;
  bool split;
  bool readonly;
  bool sepindex;
  };

/**
  structure for CREATE TABLE options (field options)

  These can be specified in the CREATE TABLE per field:
  CREATE TABLE ( field ... {...here...}, ... )
*/
struct ha_field_option_struct
{
  ulonglong offset;
  ulonglong freq;      // Not used by this version
  ulonglong opt;       // Not used by this version
  ulonglong fldlen;
  const char *dateformat;
  const char *fieldformat;
  char *special;
};

/** @brief
  CONNECT_SHARE is a structure that will be shared among all open handlers.
  This example implements the minimum of what you will probably need.
*/
typedef struct st_connect_share {
  char *table_name;
  uint  table_name_length, use_count;
  mysql_mutex_t mutex;
  THR_LOCK lock;
} CONNECT_SHARE;

typedef class ha_connect *PHC;

/** @brief
  Class definition for the storage engine
*/
class ha_connect: public handler
{
  THR_LOCK_DATA lock;      ///< MySQL lock
  CONNECT_SHARE *share;        ///< Shared lock info

public:
  ha_connect(handlerton *hton, TABLE_SHARE *table_arg);
  ~ha_connect();

  // CONNECT Implementation
  static   bool connect_init(void);
  static   bool connect_end(void);
  char    *GetStringOption(char *opname, char *sdef= NULL);
  PTOS     GetTableOptionStruct(TABLE *table_arg);
  bool     GetBooleanOption(char *opname, bool bdef);
  bool     SetBooleanOption(char *opname, bool b);
  int      GetIntegerOption(char *opname);
  bool     SetIntegerOption(char *opname, int n);
  PFOS     GetFieldOptionStruct(Field *fp);
  void    *GetColumnOption(void *field, PCOLINFO pcf);
  PIXDEF   GetIndexInfo(void);
  const char *GetDBName(const char *name);
  const char *GetTableName(void);
  int      GetColNameLen(Field *fp);
  char    *GetColName(Field *fp);
  void     AddColName(char *cp, Field *fp);
  TABLE   *GetTable(void) {return table;}
  bool     IsSameIndex(PIXDEF xp1, PIXDEF xp2);

  PTDB     GetTDB(PGLOBAL g);
  bool     OpenTable(PGLOBAL g, bool del= false);
  bool     IsOpened(void); 
  int      CloseTable(PGLOBAL g);
  int      MakeRecord(char *buf);
  int      ScanRecord(PGLOBAL g, uchar *buf);
  int      CheckRecord(PGLOBAL g, const uchar *oldbuf, uchar *newbuf);
  int      ReadIndexed(uchar *buf, OPVAL op, const uchar* key= NULL,
                                             uint key_len= 0);

  /** @brief
    The name that will be used for display purposes.
   */
  const char *table_type() const {return "CONNECT";}

  /** @brief
    The name of the index type that will be used for display.
    Don't implement this method unless you really have indexes.
   */
  const char *index_type(uint inx) { return "XPLUG"; }

  /** @brief
    The file extensions.
   */
  const char **bas_ext() const;

  /** @brief
    This is a list of flags that indicate what functionality the storage engine
    implements. The current table flags are documented in handler.h
  */
  ulonglong table_flags() const
  {
    return (HA_NO_TRANSACTIONS | HA_REC_NOT_IN_SEQ | HA_HAS_RECORDS |
            HA_NO_AUTO_INCREMENT | HA_NO_PREFIX_CHAR_KEYS |
            HA_NO_COPY_ON_ALTER | HA_CAN_VIRTUAL_COLUMNS |
            HA_NULL_IN_KEY | HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE);
  }

  /** @brief
    This is a bitmap of flags that indicates how the storage engine
    implements indexes. The current index flags are documented in
    handler.h. If you do not implement indexes, just return zero here.

      @details
    part is the key part to check. First key part is 0.
    If all_parts is set, MySQL wants to know the flags for the combined
    index, up to and including 'part'.
  */
  ulong index_flags(uint inx, uint part, bool all_parts) const
  {
    return HA_READ_NEXT | HA_READ_RANGE;
  }

  /** @brief
    unireg.cc will call max_supported_record_length(), max_supported_keys(),
    max_supported_key_parts(), uint max_supported_key_length()
    to make sure that the storage engine can handle the data it is about to
    send. Return *real* limits of your storage engine here; MySQL will do
    min(your_limits, MySQL_limits) automatically.
   */
  uint max_supported_record_length() const { return HA_MAX_REC_LENGTH; }

  /** @brief
    unireg.cc will call this to make sure that the storage engine can handle
    the data it is about to send. Return *real* limits of your storage engine
    here; MySQL will do min(your_limits, MySQL_limits) automatically.

      @details
    There is no need to implement ..._key_... methods if your engine doesn't
    support indexes.
   */
  uint max_supported_keys()          const { return 10; }

  /** @brief
    unireg.cc will call this to make sure that the storage engine can handle
    the data it is about to send. Return *real* limits of your storage engine
    here; MySQL will do min(your_limits, MySQL_limits) automatically.

      @details
    There is no need to implement ..._key_... methods if your engine doesn't
    support indexes.
   */
  uint max_supported_key_parts()     const { return 10; }

  /** @brief
    unireg.cc will call this to make sure that the storage engine can handle
    the data it is about to send. Return *real* limits of your storage engine
    here; MySQL will do min(your_limits, MySQL_limits) automatically.

      @details
    There is no need to implement ..._key_... methods if your engine doesn't
    support indexes.
   */
  uint max_supported_key_length()    const { return 255; }

  /** @brief
    Called in test_quick_select to determine if indexes should be used.
  */
  virtual double scan_time() { return (double) (stats.records+stats.deleted) / 20.0+10; }

  /** @brief
    This method will never be called if you do not implement indexes.
  */
  virtual double read_time(uint, uint, ha_rows rows)
    { return (double) rows /  20.0+1; }

  /*
    Everything below are methods that we implement in ha_connect.cc.

    Most of these methods are not obligatory, skip them and
    MySQL will treat them as not implemented
  */
  virtual bool get_error_message(int error, String *buf);

 /**
   Push condition down to the table handler.

   @param  cond   Condition to be pushed. The condition tree must not be
                  modified by the by the caller.

   @return
     The 'remainder' condition that caller must use to filter out records.
     NULL means the handler will not return rows that do not match the
     passed condition.

   @note
   The pushed conditions form a stack (from which one can remove the
   last pushed condition using cond_pop).
   The table handler filters out rows using (pushed_cond1 AND pushed_cond2 
   AND ... AND pushed_condN)
   or less restrictive condition, depending on handler's capabilities.

   handler->ha_reset() call empties the condition stack.
   Calls to rnd_init/rnd_end, index_init/index_end etc do not affect the
   condition stack.
 */ 
virtual const COND *cond_push(const COND *cond);
PFIL  CheckCond(PGLOBAL g, PFIL filp, AMT tty, Item *cond);
const char *GetValStr(OPVAL vop, bool neg);

 /**
   Number of rows in table. It will only be called if
   (table_flags() & (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT)) != 0
 */
 virtual ha_rows records();

 /** @brief
    We implement this in ha_connect.cc; it's a required method.
  */
  int open(const char *name, int mode, uint test_if_locked);    // required

  /** @brief
    We implement this in ha_connect.cc; it's a required method.
  */
  int close(void);                                              // required

  /** @brief
    We implement this in ha_connect.cc. It's not an obligatory method;
    skip it and and MySQL will treat it as not implemented.
  */
  int write_row(uchar *buf);

  /** @brief
    We implement this in ha_connect.cc. It's not an obligatory method;
    skip it and and MySQL will treat it as not implemented.
  */
  int update_row(const uchar *old_data, uchar *new_data);

  /** @brief
    We implement this in ha_connect.cc. It's not an obligatory method;
    skip it and and MySQL will treat it as not implemented.
  */
  int delete_row(const uchar *buf);

  // Added to the connect handler
  int index_init(uint idx, bool sorted);
  int index_end();
  int index_read(uchar * buf, const uchar * key, uint key_len,
                              enum ha_rkey_function find_flag);
  int index_next_same(uchar *buf, const uchar *key, uint keylen);

  /** @brief
    We implement this in ha_connect.cc. It's not an obligatory method;
    skip it and and MySQL will treat it as not implemented.
  */
//int index_read_map(uchar *buf, const uchar *key,
//                   key_part_map keypart_map, enum ha_rkey_function find_flag);

  /** @brief
    We implement this in ha_connect.cc. It's not an obligatory method;
    skip it and and MySQL will treat it as not implemented.
  */
  int index_next(uchar *buf);

  /** @brief
    We implement this in ha_connect.cc. It's not an obligatory method;
    skip it and and MySQL will treat it as not implemented.
  */
//int index_prev(uchar *buf);

  /** @brief
    We implement this in ha_connect.cc. It's not an obligatory method;
    skip it and and MySQL will treat it as not implemented.
  */
  int index_first(uchar *buf);

  /** @brief
    We implement this in ha_connect.cc. It's not an obligatory method;
    skip it and and MySQL will treat it as not implemented.
  */
//int index_last(uchar *buf);

  /** @brief
    Unlike index_init(), rnd_init() can be called two consecutive times
    without rnd_end() in between (it only makes sense if scan=1). In this
    case, the second call should prepare for the new table scan (e.g if
    rnd_init() allocates the cursor, the second call should position the
    cursor to the start of the table; no need to deallocate and allocate
    it again. This is a required method.
  */
  int rnd_init(bool scan);                                      //required
  int rnd_end();
  int rnd_next(uchar *buf);                                     ///< required
  int rnd_pos(uchar *buf, uchar *pos);                          ///< required
  void position(const uchar *record);                           ///< required
  int info(uint);                                               ///< required
  int extra(enum ha_extra_function operation);
  int external_lock(THD *thd, int lock_type);                   ///< required
  int delete_all_rows(void);
  ha_rows records_in_range(uint inx, key_range *min_key,
                           key_range *max_key);
  /**
    These methods can be overridden, but their default implementation
    provide useful functionality.
  */
  int rename_table(const char *from, const char *to);
  /**
    Delete a table in the engine. Called for base as well as temporary
    tables.
  */
  int delete_table(const char *name);
  /**
    Called by delete_table and rename_table
  */
  int delete_or_rename_table(const char *from, const char *to);
  int create(const char *name, TABLE *form,
             HA_CREATE_INFO *create_info);                      ///< required
  bool check_if_incompatible_data(HA_CREATE_INFO *info,
                                  uint table_changes);

  THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
                             enum thr_lock_type lock_type);     ///< required
  int optimize(THD* thd, HA_CHECK_OPT* check_opt);

protected:
  bool check_privileges(THD *thd, PTOS options);

  // Members
  static ulong  num;                  // Tracable handler number
  PCONNECT      xp;                   // To user_connect associated class
  ulong         hnum;                 // The number of this handler
  query_id_t    valid_query_id;       // The one when tdbp was allocated
  query_id_t    creat_query_id;       // The one when handler was allocated
  PTDB          tdbp;                 // To table class object
  PVAL          sdvalin;              // Used to convert date values
  PVAL          sdvalout;             // Used to convert date values
  bool          istable;              // True for table handler
//char          tname[64];            // The table name
  MODE          xmod;                 // Table mode
  XINFO         xinfo;                // The table info structure
  bool          valid_info;           // True if xinfo is valid
  bool          stop;                 // Used when creating index
  int           indexing;             // Type of indexing for CONNECT
  THR_LOCK_DATA lock_data;

public:
  TABLE_SHARE  *tshp;                 // Used by called tables 
  char   *data_file_name;
  char   *index_file_name;
  uint    int_table_flags;            // Inherited from MyISAM
  bool    enable_activate_all_index;  // Inherited from MyISAM
};  // end of ha_connect class definition
