1 /* Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
2 
3   This program is free software; you can redistribute it and/or modify
4   it under the terms of the GNU General Public License as published by
5   the Free Software Foundation; version 2 of the License.
6 
7   This program is distributed in the hope that it will be useful,
8   but WITHOUT ANY WARRANTY; without even the implied warranty of
9   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10   GNU General Public License for more details.
11 
12   You should have received a copy of the GNU General Public License
13   along with this program; if not, write to the Free Software
14   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
15 
16 /*
17   Please read ha_exmple.cc before reading this file.
18   Please keep in mind that the federated storage engine implements all methods
19   that are required to be implemented. handler.h has a full list of methods
20   that you can implement.
21 */
22 
23 #ifdef USE_PRAGMA_INTERFACE
24 #pragma interface			/* gcc class implementation */
25 #endif
26 
27 #include <mysql.h>
28 
29 /*
30   handler::print_error has a case statement for error numbers.
31   This value is (10000) is far out of range and will envoke the
32   default: case.
33   (Current error range is 120-159 from include/my_base.h)
34 */
35 #define HA_FEDERATED_ERROR_WITH_REMOTE_SYSTEM 10000
36 
37 #define FEDERATED_QUERY_BUFFER_SIZE STRING_BUFFER_USUAL_SIZE * 5
38 #define FEDERATED_RECORDS_IN_RANGE 2
39 #define FEDERATED_MAX_KEY_LENGTH 3500 // Same as innodb
40 
41 /*
42   FEDERATED_SHARE is a structure that will be shared amoung all open handlers
43   The example implements the minimum of what you will probably need.
44 */
45 typedef struct st_federated_share {
46   MEM_ROOT mem_root;
47 
48   bool parsed;
49   /* this key is unique db/tablename */
50   const char *share_key;
51   /*
52     the primary select query to be used in rnd_init
53   */
54   char *select_query;
55   /*
56     remote host info, parse_url supplies
57   */
58   char *server_name;
59   char *connection_string;
60   char *scheme;
61   char *connect_string;
62   char *hostname;
63   char *username;
64   char *password;
65   char *database;
66   char *table_name;
67   char *table;
68   char *socket;
69   char *sport;
70   int share_key_length;
71   ushort port;
72 
73   size_t table_name_length, server_name_length, connect_string_length, use_count;
74   mysql_mutex_t mutex;
75   THR_LOCK lock;
76 } FEDERATED_SHARE;
77 
78 /*
79   Class definition for the storage engine
80 */
81 class ha_federated: public handler
82 {
83   THR_LOCK_DATA lock;      /* MySQL lock */
84   FEDERATED_SHARE *share;    /* Shared lock info */
85   MYSQL *mysql; /* MySQL connection */
86   MYSQL_RES *stored_result;
87   /**
88     Array of all stored results we get during a query execution.
89   */
90   DYNAMIC_ARRAY results;
91   bool position_called;
92   uint fetch_num; // stores the fetch num
93   MYSQL_ROW_OFFSET current_position;  // Current position used by ::position()
94   int remote_error_number;
95   char remote_error_buf[FEDERATED_QUERY_BUFFER_SIZE];
96   bool ignore_duplicates, replace_duplicates;
97   bool insert_dup_update;
98   DYNAMIC_STRING bulk_insert;
99 
100 private:
101   /*
102       return 0 on success
103       return errorcode otherwise
104   */
105   uint convert_row_to_internal_format(uchar *buf, MYSQL_ROW row,
106                                       MYSQL_RES *result);
107   bool create_where_from_key(String *to, KEY *key_info,
108                              const key_range *start_key,
109                              const key_range *end_key,
110                              bool records_in_range, bool eq_range);
111   int stash_remote_error();
112 
113   bool append_stmt_insert(String *query);
114 
115   int read_next(uchar *buf, MYSQL_RES *result);
116   int index_read_idx_with_result_set(uchar *buf, uint index,
117                                      const uchar *key,
118                                      uint key_len,
119                                      ha_rkey_function find_flag,
120                                      MYSQL_RES **result);
121   int real_query(const char *query, size_t length);
122   int real_connect();
123 public:
124   ha_federated(handlerton *hton, TABLE_SHARE *table_arg);
~ha_federated()125   ~ha_federated() {}
126   /* The name that will be used for display purposes */
table_type()127   const char *table_type() const { return "FEDERATED"; }
128   /*
129     Next pointer used in transaction
130   */
131   ha_federated *trx_next;
132   /*
133     The name of the index type that will be used for display
134     don't implement this method unless you really have indexes
135    */
136   // perhaps get index type
index_type(uint inx)137   const char *index_type(uint inx) { return "REMOTE"; }
138   const char **bas_ext() const;
139   /*
140     This is a list of flags that says what the storage engine
141     implements. The current table flags are documented in
142     handler.h
143   */
table_flags()144   ulonglong table_flags() const
145   {
146     /* fix server to be able to get remote server table flags */
147     return (HA_PRIMARY_KEY_IN_READ_INDEX | HA_FILE_BASED
148             | HA_REC_NOT_IN_SEQ | HA_AUTO_PART_KEY | HA_CAN_INDEX_BLOBS |
149             HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE |
150             HA_NO_PREFIX_CHAR_KEYS | HA_PRIMARY_KEY_REQUIRED_FOR_DELETE |
151             HA_NO_TRANSACTIONS /* until fixed by WL#2952 */ |
152             HA_PARTIAL_COLUMN_READ | HA_NULL_IN_KEY |
153             HA_CAN_REPAIR);
154   }
155   /*
156     This is a bitmap of flags that says how the storage engine
157     implements indexes. The current index flags are documented in
158     handler.h. If you do not implement indexes, just return zero
159     here.
160 
161     part is the key part to check. First key part is 0
162     If all_parts it's set, MySQL want to know the flags for the combined
163     index up to and including 'part'.
164   */
165     /* fix server to be able to get remote server index flags */
index_flags(uint inx,uint part,bool all_parts)166   ulong index_flags(uint inx, uint part, bool all_parts) const
167   {
168     return (HA_READ_NEXT | HA_READ_RANGE | HA_READ_AFTER_KEY);
169   }
max_supported_record_length()170   uint max_supported_record_length() const { return HA_MAX_REC_LENGTH; }
max_supported_keys()171   uint max_supported_keys()          const { return MAX_KEY; }
max_supported_key_parts()172   uint max_supported_key_parts()     const { return MAX_REF_PARTS; }
max_supported_key_length()173   uint max_supported_key_length()    const { return FEDERATED_MAX_KEY_LENGTH; }
max_supported_key_part_length()174   uint max_supported_key_part_length() const { return FEDERATED_MAX_KEY_LENGTH; }
175   /*
176     Called in test_quick_select to determine if indexes should be used.
177     Normally, we need to know number of blocks . For federated we need to
178     know number of blocks on remote side, and number of packets and blocks
179     on the network side (?)
180     Talk to Kostja about this - how to get the
181     number of rows * ...
182     disk scan time on other side (block size, size of the row) + network time ...
183     The reason for "records * 1000" is that such a large number forces
184     this to use indexes "
185   */
scan_time()186   double scan_time()
187   {
188     DBUG_PRINT("info", ("records %lu", (ulong) stats.records));
189     return (double)(stats.records*1000);
190   }
191   /*
192     The next method will never be called if you do not implement indexes.
193   */
read_time(uint index,uint ranges,ha_rows rows)194   double read_time(uint index, uint ranges, ha_rows rows)
195   {
196     /*
197       Per Brian, this number is bugus, but this method must be implemented,
198       and at a later date, he intends to document this issue for handler code
199     */
200     return (double) rows /  20.0+1;
201   }
202 
keys_to_use_for_scanning()203   const key_map *keys_to_use_for_scanning() { return &key_map_full; }
204   /*
205     Everything below are methods that we implment in ha_federated.cc.
206 
207     Most of these methods are not obligatory, skip them and
208     MySQL will treat them as not implemented
209   */
210   int open(const char *name, int mode, uint test_if_locked);    // required
211   int close(void);                                              // required
212 
213   void start_bulk_insert(ha_rows rows);
214   int end_bulk_insert();
215   int write_row(uchar *buf);
216   int update_row(const uchar *old_data, uchar *new_data);
217   int delete_row(const uchar *buf);
218   int index_init(uint keynr, bool sorted);
219   ha_rows estimate_rows_upper_bound();
220   int index_read_idx_map(uchar *buf, uint index, const uchar *key,
221                                 key_part_map keypart_map,
222                                 enum ha_rkey_function find_flag);
223   int index_read(uchar *buf, const uchar *key,
224                  uint key_len, enum ha_rkey_function find_flag);
225   int index_read_idx(uchar *buf, uint idx, const uchar *key,
226                      uint key_len, enum ha_rkey_function find_flag);
227   int index_next(uchar *buf);
228   int index_end();
229   int read_range_first(const key_range *start_key,
230                                const key_range *end_key,
231                                bool eq_range, bool sorted);
232   int read_range_next();
233   /*
234     unlike index_init(), rnd_init() can be called two times
235     without rnd_end() in between (it only makes sense if scan=1).
236     then the second call should prepare for the new table scan
237     (e.g if rnd_init allocates the cursor, second call should
238     position it to the start of the table, no need to deallocate
239     and allocate it again
240   */
241   int rnd_init(bool scan);                                      //required
242   int rnd_end();
243   int rnd_next(uchar *buf);                                      //required
244   int rnd_next_int(uchar *buf);
245   int rnd_pos(uchar *buf, uchar *pos);                            //required
246   void position(const uchar *record);                            //required
247   int info(uint);                                              //required
248   int extra(ha_extra_function operation);
249 
250   void update_auto_increment(void);
251   int repair(THD* thd, HA_CHECK_OPT* check_opt);
252   int optimize(THD* thd, HA_CHECK_OPT* check_opt);
253 
254   int delete_all_rows(void);
255   int truncate();
256   int create(const char *name, TABLE *form,
257              HA_CREATE_INFO *create_info);                      //required
258   ha_rows records_in_range(uint inx, key_range *start_key,
259                                    key_range *end_key);
table_cache_type()260   uint8 table_cache_type() { return HA_CACHE_TBL_NOCACHE; }
261 
262   THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
263                              enum thr_lock_type lock_type);     //required
264   bool get_error_message(int error, String *buf);
265 
266   MYSQL_RES *store_result(MYSQL *mysql);
267   void free_result();
268 
269   int external_lock(THD *thd, int lock_type);
270   int connection_commit();
271   int connection_rollback();
272   int connection_autocommit(bool state);
273   int execute_simple_query(const char *query, int len);
274   int reset(void);
275 };
276 
277