1 /*
2 Copyright (c) 2011, 2021, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program 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, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include "ndb_local_schema.h"
26
27 #ifndef MYSQL_SERVER
28 #define MYSQL_SERVER
29 #endif
30
31 #include "sql_class.h"
32 #include "sql_table.h"
33 #include "mdl.h"
34 #include "log.h"
35 #include "table_trigger_dispatcher.h"
36 #include "sql_trigger.h"
37 #include "auth_common.h" // check_readonly()
38
39 static const char *ndb_ext=".ndb";
40
41
mdl_try_lock(void) const42 bool Ndb_local_schema::Base::mdl_try_lock(void) const
43 {
44 MDL_request_list mdl_requests;
45 MDL_request global_request;
46 MDL_request schema_request;
47 MDL_request mdl_request;
48
49 MDL_REQUEST_INIT(&global_request,
50 MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
51 MDL_STATEMENT);
52 MDL_REQUEST_INIT(&schema_request,
53 MDL_key::SCHEMA, m_db, "", MDL_INTENTION_EXCLUSIVE,
54 MDL_TRANSACTION);
55 MDL_REQUEST_INIT(&mdl_request,
56 MDL_key::TABLE, m_db, m_name, MDL_EXCLUSIVE,
57 MDL_TRANSACTION);
58
59 mdl_requests.push_front(&mdl_request);
60 mdl_requests.push_front(&schema_request);
61 mdl_requests.push_front(&global_request);
62
63 if (m_thd->mdl_context.acquire_locks(&mdl_requests,
64 0 /* don't wait for lock */))
65 {
66 // Check that an error has been pushed to thd and then
67 // clear it since this is just a _try lock_
68 assert(m_thd->is_error());
69 m_thd->clear_error();
70
71 log_warning("Failed to acquire metadata lock");
72
73 return false;
74 }
75
76 /*
77 Now when we have protection against concurrent change of read_only
78 option we can safely re-check its value.
79 */
80 if (check_readonly(m_thd, true))
81 return false;
82
83 DBUG_PRINT("info", ("acquired metadata lock"));
84 return true;
85 }
86
87
mdl_unlock(void)88 void Ndb_local_schema::Base::mdl_unlock(void)
89 {
90 m_thd->mdl_context.release_transactional_locks();
91 }
92
93
log_warning(const char * fmt,...) const94 void Ndb_local_schema::Base::log_warning(const char* fmt, ...) const
95 {
96 char buf[1024];
97 va_list args;
98 va_start(args, fmt);
99 my_vsnprintf(buf, sizeof(buf), fmt, args);
100 va_end(args);
101
102 if (m_push_warnings)
103 {
104 // Append the error which caused the error to thd's warning list
105 push_warning_printf(m_thd, Sql_condition::SL_NOTE,
106 ER_GET_ERRMSG, "Ndb schema[%s.%s]: %s",
107 m_db, m_name, buf);
108 }
109 else
110 {
111 // Print the warning to log file
112 sql_print_warning("Ndb schema[%s.%s]: %s",
113 m_db, m_name, buf);
114 }
115 }
116
117
Base(THD * thd,const char * db,const char * name)118 Ndb_local_schema::Base::Base(THD* thd, const char* db, const char* name) :
119 m_thd(thd),
120 m_db(db), m_name(name)
121 {
122 /*
123 System(or daemon) threads report error to log file
124 all other threads use push_warning
125 */
126 m_push_warnings = (thd->get_command() != COM_DAEMON);
127
128 m_have_mdl_lock= mdl_try_lock();
129 }
130
131
~Base()132 Ndb_local_schema::Base::~Base()
133 {
134 // Release MDL locks
135 if (m_have_mdl_lock)
136 {
137 DBUG_PRINT("info", ("releasing mdl lock"));
138 mdl_unlock();
139 }
140 }
141
142
143 bool
file_exists(const char * ext) const144 Ndb_local_schema::Table::file_exists(const char* ext) const
145 {
146 char buf[FN_REFLEN + 1];
147 build_table_filename(buf, sizeof(buf)-1,
148 m_db, m_name, ext, 0);
149
150 if (my_access(buf, F_OK))
151 {
152 DBUG_PRINT("info", ("File '%s' does not exist", buf));
153 return false;
154 }
155
156 DBUG_PRINT("info", ("File '%s' exist", buf));
157 return true;
158 }
159
160
161 bool
remove_file(const char * ext) const162 Ndb_local_schema::Table::remove_file(const char* ext) const
163 {
164 char buf[FN_REFLEN + 1];
165 build_table_filename(buf, sizeof(buf)-1,
166 m_db, m_name, ext, 0);
167
168 int error = my_delete(buf, 0);
169 if (!error || errno == ENOENT)
170 return true;
171
172 log_warning("Failed to remove file '%s', errno: %d", buf, errno);
173 return false;
174 }
175
176
177 bool
rename_file(const char * new_db,const char * new_name,const char * ext) const178 Ndb_local_schema::Table::rename_file(const char* new_db, const char* new_name,
179 const char* ext) const
180 {
181 char from[FN_REFLEN + 1];
182 build_table_filename(from, sizeof(from)-1,
183 m_db, m_name, ext, 0);
184
185 char to[FN_REFLEN + 1];
186 build_table_filename(to, sizeof(to) - 1, new_db, new_name, ext, 0);
187
188 int error = my_rename(from, to, 0);
189 if (!error)
190 return true;
191
192 log_warning("Failed to rename file '%s' to '%s', errno: %d",
193 from, to, errno);
194 return false;
195 }
196
197
198 // Read the engine type from .frm and return true if it says NDB
199 bool
frm_engine_is_ndb(void) const200 Ndb_local_schema::Table::frm_engine_is_ndb(void) const
201 {
202 char buf[FN_REFLEN + 1];
203 build_table_filename(buf, sizeof(buf)-1,
204 m_db, m_name, reg_ext, 0);
205
206 legacy_db_type engine_type;
207 (void)dd_frm_type(m_thd, buf, &engine_type);
208 DBUG_PRINT("info", ("engine_type: %d", engine_type));
209
210 return (engine_type == DB_TYPE_NDBCLUSTER);
211 }
212
213
Table(THD * thd,const char * db,const char * name)214 Ndb_local_schema::Table::Table(THD* thd,
215 const char* db, const char* name) :
216 Ndb_local_schema::Base(thd, db, name),
217 m_ndb_file_exist(false),
218 m_has_triggers(false)
219 {
220 DBUG_ENTER("Ndb_local_table");
221 DBUG_PRINT("enter", ("name: '%s.%s'", db, name));
222
223 // Check if .frm file exist
224 m_frm_file_exist = file_exists(reg_ext);
225 if (!m_frm_file_exist)
226 {
227 // Check for stray .ndb file
228 assert(!file_exists(ndb_ext));
229 DBUG_VOID_RETURN;
230 }
231
232 // Check if .ndb file exist
233 m_ndb_file_exist = file_exists(ndb_ext);
234
235 // Check if there are trigger files
236 m_has_triggers = file_exists(TRG_EXT);
237
238 DBUG_VOID_RETURN;
239 }
240
241
242 bool
is_local_table(void) const243 Ndb_local_schema::Table::is_local_table(void) const
244 {
245 if (m_frm_file_exist && !m_ndb_file_exist)
246 {
247 // The .frm exist but no .ndb file , this is a "local" table
248
249 // Double check that the engine type in .frm doesn't say NDB
250 assert(!frm_engine_is_ndb());
251
252 return true;
253 }
254 return false;
255 }
256
257
258 void
remove_table(void) const259 Ndb_local_schema::Table::remove_table(void) const
260 {
261 (void)remove_file(reg_ext);
262 (void)remove_file(ndb_ext);
263
264 if (m_has_triggers)
265 {
266 // Copy to buffers since 'drop_all_triggers' want char*
267 char db_name_buf[FN_REFLEN + 1], table_name_buf[FN_REFLEN + 1];
268 my_stpcpy(db_name_buf, m_db);
269 my_stpcpy(table_name_buf, m_name);
270
271 if (drop_all_triggers(m_thd, db_name_buf, table_name_buf))
272 {
273 log_warning("Failed to drop all triggers");
274 }
275 }
276 }
277
278
279 void
rename_table(const char * new_db,const char * new_name) const280 Ndb_local_schema::Table::rename_table(const char* new_db,
281 const char* new_name) const
282 {
283 (void)rename_file(new_db, new_name, reg_ext);
284 (void)rename_file(new_db, new_name, ndb_ext);
285
286 if (m_has_triggers)
287 {
288 if (!have_mdl_lock())
289 {
290 // change_trigger_table_name() requires an EXLUSIVE mdl lock
291 // so if the mdl lock was not aquired, skip this part
292 log_warning("Can't rename triggers, no mdl lock");
293 }
294 else
295 {
296 if (change_trigger_table_name(m_thd,
297 m_db, m_name, m_name,
298 new_db, new_name))
299 {
300 log_warning("Failed to rename all triggers");
301 }
302 }
303 }
304 }
305