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 /*======
6 This file is part of TokuDB
7 
8 
9 Copyright (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 
35 namespace tokudb {
36 
37 const char tokens_delimiter = ' ';
38 const char tokens_escape_delimiter_char = '\\';
39 
MDL_and_TDC(THD * thd,const char * db,const char * table,const dir_cmd_callbacks & cb)40 static 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 
parse_db_and_table(const char * dname,std::string & db_name,std::string & table_name)74 static 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 
attach(THD * thd,const std::string & dname,const std::string & iname,const dir_cmd_callbacks & cb)110 static 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());
139 cleanup:
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 
detach(THD * thd,const std::string & dname,const dir_cmd_callbacks & cb)151 static 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());
178 cleanup:
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 
move(THD * thd,const std::string & old_dname,const std::string & new_dname,const dir_cmd_callbacks & cb)190 static 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());
219 cleanup:
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 
tokenize(const char * cmd_str,std::vector<std::string> & tokens)231 static 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 
process_dir_cmd(THD * thd,const char * cmd_str,const dir_cmd_callbacks & cb)265 void 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