text: /* Copyright (C) MariaDB Corporation Ab
  
    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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
  
  /** @file ha_connect.h
  	Author Olivier Bertrand
  
      @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
  
  /****************************************************************************/
  /*  mycat.h contains the TOS, PTOS, ha_table_option_struct declarations.    */
  /****************************************************************************/
  #include "mycat.h"
  
  #if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
  bool MongoEnabled(void);
  #endif   // JAVA_SUPPORT || CMGO_SUPPORT
  
  /****************************************************************************/
  /*  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;
                oldopn= newopn= NULL;
                oldpix= newpix= NULL;}
  
    inline char *SetName(PGLOBAL g, PCSZ name) {return PlugDup(g, name);}
  
    bool         oldsep;              // Sepindex before create/alter
    bool         newsep;              // Sepindex after create/alter
    char        *oldopn;              // Optname before create/alter
    char        *newopn;              // Optname 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_field_option_struct FOS, *PFOS;
  typedef struct ha_index_option_struct XOS, *PXOS;
  
  extern handlerton *connect_hton;
  
  /**
    structure for CREATE TABLE options (table options)
  
    These can be specified in the CREATE TABLE:
    CREATE TABLE ( ... ) {...here...}
  
  	------ Was moved to mycat.h ------
  	*/
  
  /**
    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;
    ulonglong fldlen;
    uint opt;
    const char *dateformat;
    const char *fieldformat;
  	const char* jsonpath;
  	const char* xmlpath;
  	char *special;
  };
  
  /*
    index options can be declared similarly
    using the ha_index_option_struct structure.
  
    Their values can be specified in the CREATE TABLE per index:
    CREATE TABLE ( field ..., .., INDEX .... *here*, ... )
  */
  struct ha_index_option_struct
  {
    bool dynamic;
    bool mapped;
  };
  
  /** @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.
  */
  class CONNECT_SHARE : public Handler_share {
  public:
    mysql_mutex_t mutex;
    THR_LOCK lock;
    CONNECT_SHARE()
    {
      thr_lock_init(&lock);
    }
    ~CONNECT_SHARE()
    {
      thr_lock_delete(&lock);
      mysql_mutex_destroy(&mutex);
    }
  };
  
  typedef class ha_connect *PHC;
  
  /** @brief
    Class definition for the storage engine
  */
  class ha_connect final : public handler
  {
    THR_LOCK_DATA lock;      ///< MySQL lock
    CONNECT_SHARE *share;        ///< Shared lock info
    CONNECT_SHARE *get_share();
  
  protected:
    char *PlugSubAllocStr(PGLOBAL g, void *memp, const char *str, size_t length)
    {
      char *ptr= (char*)PlgDBSubAlloc(g, memp, length + 1);
  
      if (ptr) {
        memcpy(ptr, str, length);
        ptr[length]= '\0';
        } // endif ptr
  
      return ptr;
    } // end of PlugSubAllocStr
  
  public:
    ha_connect(handlerton *hton, TABLE_SHARE *table_arg);
    ~ha_connect();
  
    // CONNECT Implementation
  //static   bool connect_init(void);
  //static   bool connect_end(void);
    TABTYPE  GetRealType(PTOS pos= NULL);
    char    *GetRealString(PCSZ s);
  	PCSZ     GetStringOption(PCSZ opname, PCSZ sdef= NULL);
    PTOS     GetTableOptionStruct(TABLE_SHARE *s= NULL);
    bool     GetBooleanOption(PCSZ opname, bool bdef);
    bool     SetBooleanOption(PCSZ opname, bool b);
    int      GetIntegerOption(PCSZ opname);
    bool     GetIndexOption(KEY *kp, PCSZ opname);
    bool     CheckString(PCSZ str1, PCSZ str2);
    bool     SameString(TABLE *tab, PCSZ opn);
    bool     SetIntegerOption(PCSZ opname, int n);
    bool     SameInt(TABLE *tab, PCSZ opn);
    bool     SameBool(TABLE *tab, PCSZ opn);
    bool     FileExists(const char *fn, bool bf);
    bool     NoFieldOptionChange(TABLE *tab);
    PFOS     GetFieldOptionStruct(Field *fp);
    void    *GetColumnOption(PGLOBAL g, void *field, PCOLINFO pcf);
    PXOS     GetIndexOptionStruct(KEY *kp);
    PIXDEF   GetIndexInfo(TABLE_SHARE *s= NULL);
    bool     CheckVirtualIndex(TABLE_SHARE *s);
    PCSZ     GetDBName(PCSZ name);
    PCSZ     GetTableName(void);
    char    *GetPartName(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);
    bool     IsPartitioned(void);
    bool     IsUnique(uint n);
    PCSZ     GetDataPath(void) {return datapath;}
  
    bool     SetDataPath(PGLOBAL g, PCSZ path);
    PTDB     GetTDB(PGLOBAL g);
    int      OpenTable(PGLOBAL g, bool del= false);
    bool     CheckColumnList(PGLOBAL g);
    bool     IsOpened(void);
    int      CloseTable(PGLOBAL g);
    int      MakeRecord(char *buf);
    int      ScanRecord(PGLOBAL g, const uchar *buf);
    int      CheckRecord(PGLOBAL g, const uchar *oldbuf, const uchar *newbuf);
  	int      ReadIndexed(uchar *buf, OPVAL op, const key_range *kr= NULL);
  	bool     IsIndexed(Field *fp);
    bool     MakeKeyWhere(PGLOBAL g, PSTRG qry, OPVAL op, char q,
                                     const key_range *kr);
  //inline char *Strz(LEX_STRING &ls);
  	key_range start_key;
  
  
    /** @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);
  
    /** @brief
      The file extensions.
     */
  //const char **bas_ext() const;
  
   /**
      Check if a storage engine supports a particular alter table in-place
      @note Called without holding thr_lock.c lock.
   */
   virtual enum_alter_inplace_result
   check_if_supported_inplace_alter(TABLE *altered_table,
                                    Alter_inplace_info *ha_alter_info);
  
    /** @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;
  
    /** @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;
  
    /** @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);
  PCFIL CheckCond(PGLOBAL g, PCFIL filp, const Item *cond);
  const char *GetValStr(OPVAL vop, bool neg);
  PFIL  CondFilter(PGLOBAL g, Item *cond);
  //PFIL  CheckFilter(PGLOBAL g);
  
  /** admin commands - called from mysql_admin_table */
  virtual int check(THD* thd, HA_CHECK_OPT* check_opt);
  
   /**
     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();
  
   /**
     Type of table for caching query
     CONNECT should not use caching because its tables are external
     data prone to me modified out of MariaDB
   */
   virtual uint8 table_cache_type(void)
   {
  #if defined(MEMORY_TRACE)
     // Temporary until bug MDEV-4771 is fixed
     return HA_CACHE_TBL_NONTRANSACT;
  #else
     return HA_CACHE_TBL_NOCACHE;
  #endif
   }
  
   /** @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(const 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, const 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);
  
    /* Index condition pushdown implementation */
  //Item *idx_cond_push(uint keyno, Item* idx_cond);
  
    /** @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 start_stmt(THD *thd, thr_lock_type lock_type);
    int external_lock(THD *thd, int lock_type);                   ///< required
    int delete_all_rows(void);
    ha_rows records_in_range(uint inx, const key_range *start_key,
                             const key_range *end_key, page_range *pages);
    /**
      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);
  
    /**
     * Multi Range Read interface
     */
    int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
                              uint n_ranges, uint mode, HANDLER_BUFFER *buf);
    int multi_range_read_next(range_id_t *range_info);
    ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
                                        void *seq_init_param,
                                        uint n_ranges, uint *bufsz,
                                        uint *flags, Cost_estimate *cost);
    ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys,
                                  uint key_parts, uint *bufsz,
                                  uint *flags, Cost_estimate *cost);
    int multi_range_read_explain_info(uint mrr_mode, char *str, size_t size);
  
    int reset(void) {ds_mrr.dsmrr_close(); return 0;}
  
    /* Index condition pushdown implementation */
  //  Item *idx_cond_push(uint keyno, Item* idx_cond);
  private:
    DsMrr_impl ds_mrr;
  
  protected:
    bool check_privileges(THD *thd, PTOS options, const char *dbn, bool quick=false);
    MODE CheckMode(PGLOBAL g, THD *thd, MODE newmode, bool *chk, bool *cras);
  	int  check_stmt(PGLOBAL g, MODE newmode, bool cras);
  	char *GetDBfromName(const char *name);
  
    // 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
    PCSZ          datapath;             // Is the Path of DB data directory
    PTDB          tdbp;                 // To table class object
    PVAL          sdvalin1;             // Used to convert date values
    PVAL          sdvalin2;             // Used to convert date values
    PVAL          sdvalin3;             // Used to convert date values
    PVAL          sdvalin4;             // Used to convert date values
    PVAL          sdvalout;             // Used to convert date values
    bool          istable;              // True for table handler
    char          partname[65];         // The partition 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
    bool          alter;                // True when converting to other engine
    bool          mrr;                  // True when getting index positions
    bool          nox;                  // True when index should not be made
    bool          abort;                // True after error in UPDATE/DELETE
    int           indexing;             // Type of indexing for CONNECT
    int           locked;               // Table lock
    MY_BITMAP    *part_id;              // Columns used for partition func
    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
  
  #if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
  bool MongoEnabled(void);
  #endif   // JAVA_SUPPORT || CMGO_SUPPORT
  