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