1/* Copyright (c) 2004, 2013, Oracle and/or its affiliates.
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*/
45typedef 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*/
81class 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, table_will_be_deleted;
92 MYSQL_ROW_OFFSET current_position; // Current position used by ::position()
93 int remote_error_number;
94 char remote_error_buf[FEDERATED_QUERY_BUFFER_SIZE];
95 bool ignore_duplicates, replace_duplicates;
96 bool insert_dup_update;
97 DYNAMIC_STRING bulk_insert;
98
99private:
100 /*
101 return 0 on success
102 return errorcode otherwise
103 */
104 uint convert_row_to_internal_format(uchar *buf, MYSQL_ROW row,
105 MYSQL_RES *result);
106 bool create_where_from_key(String *to, KEY *key_info,
107 const key_range *start_key,
108 const key_range *end_key,
109 bool records_in_range, bool eq_range);
110 int stash_remote_error();
111
112 bool append_stmt_insert(String *query);
113
114 int read_next(uchar *buf, MYSQL_RES *result);
115 int index_read_idx_with_result_set(uchar *buf, uint index,
116 const uchar *key,
117 uint key_len,
118 ha_rkey_function find_flag,
119 MYSQL_RES **result);
120 int real_query(const char *query, size_t length);
121 int real_connect();
122public:
123 ha_federated(handlerton *hton, TABLE_SHARE *table_arg);
124 ~ha_federated() {}
125 /*
126 Next pointer used in transaction
127 */
128 ha_federated *trx_next;
129 /*
130 The name of the index type that will be used for display
131 don't implement this method unless you really have indexes
132 */
133 // perhaps get index type
134 const char *index_type(uint inx) { return "REMOTE"; }
135 /*
136 This is a list of flags that says what the storage engine
137 implements. The current table flags are documented in
138 handler.h
139 */
140 ulonglong table_flags() const
141 {
142 /* fix server to be able to get remote server table flags */
143 return (HA_PRIMARY_KEY_IN_READ_INDEX | HA_FILE_BASED
144 | HA_REC_NOT_IN_SEQ | HA_AUTO_PART_KEY | HA_CAN_INDEX_BLOBS |
145 HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE |
146 HA_NO_PREFIX_CHAR_KEYS | HA_PRIMARY_KEY_REQUIRED_FOR_DELETE |
147 HA_NO_TRANSACTIONS /* until fixed by WL#2952 */ |
148 HA_PARTIAL_COLUMN_READ | HA_NULL_IN_KEY |
149 HA_CAN_REPAIR);
150 }
151 /*
152 This is a bitmap of flags that says how the storage engine
153 implements indexes. The current index flags are documented in
154 handler.h. If you do not implement indexes, just return zero
155 here.
156
157 part is the key part to check. First key part is 0
158 If all_parts it's set, MySQL want to know the flags for the combined
159 index up to and including 'part'.
160 */
161 /* fix server to be able to get remote server index flags */
162 ulong index_flags(uint inx, uint part, bool all_parts) const
163 {
164 return (HA_READ_NEXT | HA_READ_RANGE | HA_READ_AFTER_KEY);
165 }
166 uint max_supported_record_length() const { return HA_MAX_REC_LENGTH; }
167 uint max_supported_keys() const { return MAX_KEY; }
168 uint max_supported_key_parts() const { return MAX_REF_PARTS; }
169 uint max_supported_key_length() const { return FEDERATED_MAX_KEY_LENGTH; }
170 uint max_supported_key_part_length() const { return FEDERATED_MAX_KEY_LENGTH; }
171 /*
172 Called in test_quick_select to determine if indexes should be used.
173 Normally, we need to know number of blocks . For federated we need to
174 know number of blocks on remote side, and number of packets and blocks
175 on the network side (?)
176 Talk to Kostja about this - how to get the
177 number of rows * ...
178 disk scan time on other side (block size, size of the row) + network time ...
179 The reason for "records * 1000" is that such a large number forces
180 this to use indexes "
181 */
182 double scan_time()
183 {
184 DBUG_PRINT("info", ("records %lu", (ulong) stats.records));
185 return (double)(stats.records*1000);
186 }
187 /*
188 The next method will never be called if you do not implement indexes.
189 */
190 double read_time(uint index, uint ranges, ha_rows rows)
191 {
192 /*
193 Per Brian, this number is bugus, but this method must be implemented,
194 and at a later date, he intends to document this issue for handler code
195 */
196 return (double) rows / 20.0+1;
197 }
198
199 const key_map *keys_to_use_for_scanning() { return &key_map_full; }
200 /*
201 Everything below are methods that we implment in ha_federated.cc.
202
203 Most of these methods are not obligatory, skip them and
204 MySQL will treat them as not implemented
205 */
206 int open(const char *name, int mode, uint test_if_locked); // required
207 int close(void); // required
208
209 void start_bulk_insert(ha_rows rows, uint flags);
210 int end_bulk_insert();
211 int write_row(uchar *buf);
212 int update_row(const uchar *old_data, const uchar *new_data);
213 int delete_row(const uchar *buf);
214 int index_init(uint keynr, bool sorted);
215 ha_rows estimate_rows_upper_bound();
216 int index_read(uchar *buf, const uchar *key,
217 uint key_len, enum ha_rkey_function find_flag);
218 int index_read_idx(uchar *buf, uint idx, const uchar *key,
219 uint key_len, enum ha_rkey_function find_flag);
220 int index_next(uchar *buf);
221 int index_end();
222 int read_range_first(const key_range *start_key,
223 const key_range *end_key,
224 bool eq_range, bool sorted);
225 int read_range_next();
226 /*
227 unlike index_init(), rnd_init() can be called two times
228 without rnd_end() in between (it only makes sense if scan=1).
229 then the second call should prepare for the new table scan
230 (e.g if rnd_init allocates the cursor, second call should
231 position it to the start of the table, no need to deallocate
232 and allocate it again
233 */
234 int rnd_init(bool scan); //required
235 int rnd_end();
236 int rnd_next(uchar *buf); //required
237 int rnd_next_int(uchar *buf);
238 int rnd_pos(uchar *buf, uchar *pos); //required
239 void position(const uchar *record); //required
240 int info(uint); //required
241 int extra(ha_extra_function operation);
242
243 void update_auto_increment(void);
244 int repair(THD* thd, HA_CHECK_OPT* check_opt);
245 int optimize(THD* thd, HA_CHECK_OPT* check_opt);
246
247 int delete_all_rows(void);
248 int truncate();
249 int create(const char *name, TABLE *form,
250 HA_CREATE_INFO *create_info); //required
251 ha_rows records_in_range(uint inx, key_range *start_key,
252 key_range *end_key);
253 uint8 table_cache_type() { return HA_CACHE_TBL_NOCACHE; }
254
255 THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
256 enum thr_lock_type lock_type); //required
257 bool get_error_message(int error, String *buf);
258
259 MYSQL_RES *store_result(MYSQL *mysql);
260 void free_result();
261
262 int external_lock(THD *thd, int lock_type);
263 int connection_commit();
264 int connection_rollback();
265 int connection_autocommit(bool state);
266 int execute_simple_query(const char *query, int len);
267 int reset(void);
268};
269
270