1/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
3/* -*- mode: C; c-basic-offset: 4 -*- */
4#ident "$Id$"
5/*======
6This file is part of TokuDB
7
8
9Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
10
11 TokuDBis is free software: you can redistribute it and/or modify
12 it under the terms of the GNU General Public License, version 2,
13 as published by the Free Software Foundation.
14
15 TokuDB is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with TokuDB. If not, see <http://www.gnu.org/licenses/>.
22
23======= */
24
25#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
26
27#include "hatoku_hton.h"
28#include "tokudb_dir_cmd.h"
29#include "my_dbug.h"
30#include "sql_base.h"
31
32#include <vector>
33#include <string>
34
35namespace tokudb {
36
37const char tokens_delimiter = ' ';
38const char tokens_escape_delimiter_char = '\\';
39
40static int MDL_and_TDC(THD *thd,
41 const char *db,
42 const char *table,
43 const dir_cmd_callbacks &cb) {
44 int error;
45 LEX_CSTRING db_arg;
46 LEX_CSTRING table_arg;
47
48 db_arg.str = const_cast<char *>(db);
49 db_arg.length = strlen(db);;
50 table_arg.str = const_cast<char *>(table);
51 table_arg.length = strlen(table);
52 Table_ident table_ident(thd, &db_arg, &table_arg, true);;
53 thd->lex->select_lex.add_table_to_list(
54 thd, &table_ident, NULL, 1, TL_UNLOCK, MDL_EXCLUSIVE, 0, 0, 0);
55 /* The lock will be released at the end of mysq_execute_command() */
56 error = lock_table_names(thd,
57 thd->lex->select_lex.table_list.first,
58 NULL,
59 thd->variables.lock_wait_timeout,
60 0);
61 if (error) {
62 if (cb.set_error)
63 cb.set_error(thd,
64 error,
65 "Can't lock table '%s.%s'",
66 db,
67 table);
68 return error;
69 }
70 tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db, table, false);
71 return error;
72}
73
74static bool parse_db_and_table(const char *dname,
75 std::string /*out*/ &db_name,
76 std::string /*out*/ &table_name) {
77 const char *begin;
78 const char *end;
79 const char *db_name_begin;
80 const char *db_name_end;
81
82 begin = strchr(dname, '/');
83 if (!begin)
84 return false;
85 ++begin;
86 end = strchr(begin, '/');
87 if (!end)
88 return false;
89
90 db_name_begin = begin;
91 db_name_end = end;
92
93 begin = end + 1;
94
95 end = strchr(begin, '-');
96 if (!end)
97 return false;
98
99 if (strncmp(end, "-main", strlen("-main")) &&
100 strncmp(end, "-status", strlen("-status")) &&
101 strncmp(end, "-key", strlen("-key")))
102 return false;
103
104 db_name.assign(db_name_begin, db_name_end);
105 table_name.assign(begin, end);
106
107 return true;
108}
109
110static int attach(THD *thd,
111 const std::string &dname,
112 const std::string &iname,
113 const dir_cmd_callbacks &cb) {
114 int error;
115 DB_TXN* txn = NULL;
116 DB_TXN *parent_txn = NULL;
117 tokudb_trx_data *trx = NULL;
118
119 std::string db_name;
120 std::string table_name;
121
122 if (parse_db_and_table(dname.c_str(), db_name, table_name)) {
123 error = MDL_and_TDC(thd, db_name.c_str(), table_name.c_str(), cb);
124 if (error)
125 goto cleanup;
126 }
127
128 trx = (tokudb_trx_data *) thd_get_ha_data(thd, tokudb_hton);
129 if (trx && trx->sub_sp_level)
130 parent_txn = trx->sub_sp_level;
131 error = txn_begin(db_env, parent_txn, &txn, 0, thd);
132 if (error)
133 goto cleanup;
134
135 error = db_env->dirtool_attach(db_env,
136 txn,
137 dname.c_str(),
138 iname.c_str());
139cleanup:
140 if (txn) {
141 if (error) {
142 abort_txn(txn);
143 }
144 else {
145 commit_txn(txn, 0);
146 }
147 }
148 return error;
149}
150
151static int detach(THD *thd,
152 const std::string &dname,
153 const dir_cmd_callbacks &cb) {
154 int error;
155 DB_TXN* txn = NULL;
156 DB_TXN *parent_txn = NULL;
157 tokudb_trx_data *trx = NULL;
158
159 std::string db_name;
160 std::string table_name;
161
162 if (parse_db_and_table(dname.c_str(), db_name, table_name)) {
163 error = MDL_and_TDC(thd, db_name.c_str(), table_name.c_str(), cb);
164 if (error)
165 goto cleanup;
166 }
167
168 trx = (tokudb_trx_data *) thd_get_ha_data(thd, tokudb_hton);
169 if (trx && trx->sub_sp_level)
170 parent_txn = trx->sub_sp_level;
171 error = txn_begin(db_env, parent_txn, &txn, 0, thd);
172 if (error)
173 goto cleanup;
174
175 error = db_env->dirtool_detach(db_env,
176 txn,
177 dname.c_str());
178cleanup:
179 if (txn) {
180 if (error) {
181 abort_txn(txn);
182 }
183 else {
184 commit_txn(txn, 0);
185 }
186 }
187 return error;
188}
189
190static int move(THD *thd,
191 const std::string &old_dname,
192 const std::string &new_dname,
193 const dir_cmd_callbacks &cb) {
194 int error;
195 DB_TXN* txn = NULL;
196 DB_TXN *parent_txn = NULL;
197 tokudb_trx_data *trx = NULL;
198
199 std::string db_name;
200 std::string table_name;
201
202 if (parse_db_and_table(old_dname.c_str(), db_name, table_name)) {
203 error = MDL_and_TDC(thd, db_name.c_str(), table_name.c_str(), cb);
204 if (error)
205 goto cleanup;
206 }
207
208 trx = (tokudb_trx_data *) thd_get_ha_data(thd, tokudb_hton);
209 if (trx && trx->sub_sp_level)
210 parent_txn = trx->sub_sp_level;
211 error = txn_begin(db_env, parent_txn, &txn, 0, thd);
212 if (error)
213 goto cleanup;
214
215 error = db_env->dirtool_move(db_env,
216 txn,
217 old_dname.c_str(),
218 new_dname.c_str());
219cleanup:
220 if (txn) {
221 if (error) {
222 abort_txn(txn);
223 }
224 else {
225 commit_txn(txn, 0);
226 }
227 }
228 return error;
229}
230
231static void tokenize(const char *cmd_str,
232 std::vector<std::string> /*out*/ &tokens) {
233 DBUG_ASSERT(cmd_str);
234
235 bool was_escape = false;
236 const char *token_begin = cmd_str;
237 const char *token_end = token_begin;
238
239 while (*token_end) {
240 if (*token_end == tokens_escape_delimiter_char) {
241 was_escape = true;
242 }
243 else if (*token_end == tokens_delimiter) {
244 if (was_escape)
245 was_escape = false;
246 else {
247 if (token_begin == token_end)
248 ++token_begin;
249 else {
250 tokens.push_back(std::string(token_begin, token_end));
251 token_begin = token_end + 1;
252 }
253 }
254 }
255 else {
256 was_escape = false;
257 }
258 ++token_end;
259 }
260
261 if (token_begin != token_end)
262 tokens.push_back(std::string(token_begin, token_end));
263}
264
265void process_dir_cmd(THD *thd,
266 const char *cmd_str,
267 const dir_cmd_callbacks &cb) {
268
269 DBUG_ASSERT(thd);
270 DBUG_ASSERT(cmd_str);
271
272 std::vector<std::string> tokens;
273 tokenize(cmd_str, tokens);
274
275 if (tokens.empty())
276 return;
277
278 const std::string &cmd = tokens[0];
279
280 if (!cmd.compare("attach")) {
281 if (tokens.size() != 3) {
282 if (cb.set_error)
283 cb.set_error(thd,
284 EINVAL,
285 "attach command requires two arguments");
286 }
287 else {
288 int r = attach(thd, tokens[1], tokens[2], cb);
289 if (r && cb.set_error)
290 cb.set_error(thd, r, "Attach command error");
291 }
292 }
293 else if (!cmd.compare("detach")) {
294 if (tokens.size() != 2) {
295 if (cb.set_error)
296 cb.set_error(thd,
297 EINVAL,
298 "detach command requires one argument");
299 }
300 else {
301 int r = detach(thd, tokens[1], cb);
302 if (r && cb.set_error)
303 cb.set_error(thd, r, "detach command error");
304 }
305 }
306 else if (!cmd.compare("move")) {
307 if (tokens.size() != 3) {
308 if (cb.set_error)
309 cb.set_error(thd,
310 EINVAL,
311 "move command requires two arguments");
312 }
313 else {
314 int r = move(thd, tokens[1], tokens[2], cb);
315 if (r && cb.set_error)
316 cb.set_error(thd, r, "move command error");
317 }
318 }
319 else {
320 if (cb.set_error)
321 cb.set_error(thd,
322 ENOENT,
323 "Unknown command '%s'",
324 cmd.c_str());
325 }
326
327 return;
328};
329
330
331} // namespace tokudb
332