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 #ifndef MYSQL_SERVER
26 #define MYSQL_SERVER
27 #endif
28
29 #include "my_global.h"
30 #include "ndb_local_connection.h"
31 #include "sql_class.h"
32 #include "sql_prepare.h"
33 #include "log.h"
34
Ndb_local_connection(THD * thd_arg)35 Ndb_local_connection::Ndb_local_connection(THD* thd_arg):
36 m_thd(thd_arg)
37 {
38 assert(thd_arg);
39
40 /*
41 System(or daemon) threads report error to log file
42 all other threads use push_warning
43 */
44 m_push_warnings = (thd_arg->get_command() != COM_DAEMON);
45 }
46
47
48 static inline bool
should_ignore_error(const uint * ignore_error_list,uint error)49 should_ignore_error(const uint* ignore_error_list, uint error)
50 {
51 DBUG_ENTER("should_ignore_error");
52 DBUG_PRINT("enter", ("error: %u", error));
53 const uint* ignore_error = ignore_error_list;
54 while(*ignore_error)
55 {
56 DBUG_PRINT("info", ("ignore_error: %u", *ignore_error));
57 if (*ignore_error == error)
58 DBUG_RETURN(true);
59 ignore_error++;
60 }
61 DBUG_PRINT("info", ("Don't ignore error"));
62 DBUG_RETURN(false);
63 }
64
65
66 class Suppressor {
67 public:
~Suppressor()68 virtual ~Suppressor() {}
69 virtual bool should_ignore_error(Ed_connection& con) const = 0;
70 };
71
72
73 bool
execute_query(MYSQL_LEX_STRING sql_text,const uint * ignore_mysql_errors,const Suppressor * suppressor)74 Ndb_local_connection::execute_query(MYSQL_LEX_STRING sql_text,
75 const uint* ignore_mysql_errors,
76 const Suppressor* suppressor)
77 {
78 DBUG_ENTER("Ndb_local_connection::execute_query");
79 Ed_connection con(m_thd);
80 if (con.execute_direct(sql_text))
81 {
82 /* Error occured while executing the query */
83 const uint last_errno = con.get_last_errno();
84 assert(last_errno); // last_errno must have been set
85 const char* last_errmsg = con.get_last_error();
86
87 DBUG_PRINT("error", ("Query '%s' failed, error: '%d: %s'",
88 sql_text.str,
89 last_errno, last_errmsg));
90
91 // catch some SQL parse errors in debug
92 assert(last_errno != ER_PARSE_ERROR ||
93 last_errno != ER_EMPTY_QUERY);
94
95 /* Check if this is a MySQL level errors that should be ignored */
96 if (ignore_mysql_errors &&
97 should_ignore_error(ignore_mysql_errors, last_errno))
98 {
99 /* MySQL level error suppressed -> return success */
100 m_thd->clear_error();
101 DBUG_RETURN(false);
102 }
103
104 /*
105 Call the suppressor to check if it want to silence
106 this error
107 */
108 if (suppressor &&
109 suppressor->should_ignore_error(con))
110 {
111 /* Error suppressed -> return sucess */
112 m_thd->clear_error();
113 DBUG_RETURN(false);
114 }
115
116 if (m_push_warnings)
117 {
118 // Append the error which caused the error to thd's warning list
119 push_warning(m_thd, Sql_condition::SL_WARNING,
120 last_errno, last_errmsg);
121 }
122 else
123 {
124 // Print the error to log file
125 sql_print_error("NDB: Query '%s' failed, error: %d: %s",
126 sql_text.str,
127 last_errno, last_errmsg);
128 }
129
130 DBUG_RETURN(true);
131 }
132
133 // Query returned ok, thd should have no error
134 assert(!m_thd->is_error());
135
136 DBUG_RETURN(false); // Success
137 }
138
139
140 /*
141 Execute the query with even higher isolation than what execute_query
142 provides to avoid that for example THD's status variables are changed
143 */
144
145 bool
execute_query_iso(MYSQL_LEX_STRING sql_text,const uint * ignore_mysql_errors,const Suppressor * suppressor)146 Ndb_local_connection::execute_query_iso(MYSQL_LEX_STRING sql_text,
147 const uint* ignore_mysql_errors,
148 const Suppressor* suppressor)
149 {
150 /* Don't allow queries to affect THD's status variables */
151 struct system_status_var save_thd_status_var= m_thd->status_var;
152
153 /* Check modified_non_trans_table is false(check if actually needed) */
154 assert(!m_thd->get_transaction()->has_modified_non_trans_table(
155 Transaction_ctx::STMT));
156
157 #if 0
158 /*
159 Saves pseudo_thread_id and assign a "random" thread id from
160 the global "thread_id" variable without taking a lock
161 This looks like a copy and paste bug from some THD:: function
162 should probably be assigned thd->thread_id, if the pseudo_thread_id
163 need to be changed at all..
164 */
165 ulong save_thd_thread_id= m_thd->variables.pseudo_thread_id;
166 m_thd->variables.pseudo_thread_id = thread_id;
167 #endif
168
169 /* Turn off binlogging */
170 ulonglong save_thd_options= m_thd->variables.option_bits;
171 assert(sizeof(save_thd_options) == sizeof(m_thd->variables.option_bits));
172 m_thd->variables.option_bits&= ~OPTION_BIN_LOG;
173
174 bool result = execute_query(sql_text,
175 ignore_mysql_errors,
176 suppressor);
177
178 /* Restore THD settings */
179 m_thd->variables.option_bits= save_thd_options;
180 #if 0
181 m_thd->variables.pseudo_thread_id = save_thd_thread_id;
182 #endif
183 m_thd->status_var= save_thd_status_var;
184
185 return result;
186 }
187
188
189 bool
truncate_table(const char * db,size_t db_length,const char * table,size_t table_length,bool ignore_no_such_table)190 Ndb_local_connection::truncate_table(const char* db, size_t db_length,
191 const char* table, size_t table_length,
192 bool ignore_no_such_table)
193 {
194 DBUG_ENTER("Ndb_local_connection::truncate_table");
195 DBUG_PRINT("enter", ("db: '%s', table: '%s'", db, table));
196
197 // Create the SQL string
198 String sql_text((uint32)(db_length + table_length + 100));
199 sql_text.append(STRING_WITH_LEN("TRUNCATE TABLE "));
200 sql_text.append(db, (uint32)db_length);
201 sql_text.append(STRING_WITH_LEN("."));
202 sql_text.append(table, (uint32)table_length);
203
204 // Setup list of errors to ignore
205 uint ignore_mysql_errors[2] = {0, 0};
206 if (ignore_no_such_table)
207 ignore_mysql_errors[0] = ER_NO_SUCH_TABLE;
208
209 DBUG_RETURN(execute_query_iso(sql_text.lex_string(),
210 ignore_mysql_errors,
211 NULL));
212 }
213
214
215 bool
flush_table(const char * db,size_t db_length,const char * table,size_t table_length)216 Ndb_local_connection::flush_table(const char* db, size_t db_length,
217 const char* table, size_t table_length)
218 {
219 DBUG_ENTER("Ndb_local_connection::flush_table");
220 DBUG_PRINT("enter", ("db: '%s', table: '%s'", db, table));
221
222 // Create the SQL string
223 String sql_text((uint32)(db_length + table_length + 100));
224 sql_text.append(STRING_WITH_LEN("FLUSH TABLES "));
225 sql_text.append(db, (uint32)db_length);
226 sql_text.append(STRING_WITH_LEN("."));
227 sql_text.append(table, (uint32)table_length);
228
229 DBUG_RETURN(execute_query_iso(sql_text.lex_string(),
230 NULL,
231 NULL));
232 }
233
234
235 bool
delete_rows(const char * db,size_t db_length,const char * table,size_t table_length,bool ignore_no_such_table,...)236 Ndb_local_connection::delete_rows(const char* db, size_t db_length,
237 const char* table, size_t table_length,
238 bool ignore_no_such_table,
239 ...)
240 {
241 DBUG_ENTER("Ndb_local_connection::truncate_table");
242 DBUG_PRINT("enter", ("db: '%s', table: '%s'", db, table));
243
244 // Create the SQL string
245 String sql_text((uint32)(db_length + table_length + 100));
246 sql_text.append(STRING_WITH_LEN("DELETE FROM "));
247 sql_text.append(db, (uint32)db_length);
248 sql_text.append(STRING_WITH_LEN("."));
249 sql_text.append(table, (uint32)table_length);
250 sql_text.append(" WHERE ");
251
252 va_list args;
253 va_start(args, ignore_no_such_table);
254
255 // Append var args strings until ending NULL as WHERE clause
256 const char* arg;
257 bool empty_where = true;
258 while ((arg= va_arg(args, char *)))
259 {
260 sql_text.append(arg);
261 empty_where = false;
262 }
263
264 va_end(args);
265
266 if (empty_where)
267 sql_text.append("1=1");
268
269 // Setup list of errors to ignore
270 uint ignore_mysql_errors[2] = {0, 0};
271 if (ignore_no_such_table)
272 ignore_mysql_errors[0] = ER_NO_SUCH_TABLE;
273
274 DBUG_RETURN(execute_query_iso(sql_text.lex_string(),
275 ignore_mysql_errors,
276 NULL));
277 }
278
279
280 class Create_sys_table_suppressor : public Suppressor
281 {
282 public:
~Create_sys_table_suppressor()283 virtual ~Create_sys_table_suppressor() {}
should_ignore_error(Ed_connection & con) const284 virtual bool should_ignore_error(Ed_connection& con) const
285 {
286 const uint last_errno = con.get_last_errno();
287 const char* last_errmsg = con.get_last_error();
288 DBUG_ENTER("Create_sys_table_suppressor::should_ignore_error");
289 DBUG_PRINT("enter", ("last_errno: %d, last_errmsg: '%s'",
290 last_errno, last_errmsg));
291
292 if (last_errno == ER_CANT_CREATE_TABLE)
293 {
294 /*
295 The CREATE TABLE failed late and it was classifed as a
296 'Can't create table' error.
297 */
298
299 /*
300 Error message always end with " %d)" in all languages. Find last
301 space and convert number from there
302 */
303 const char* last_space = strrchr(last_errmsg, ' ');
304 DBUG_PRINT("info", ("last_space: '%s'", last_space));
305 if (!last_space)
306 {
307 // Could not find last space, parse error
308 assert(false);
309 DBUG_RETURN(false); // Don't suppress
310 }
311
312 int error;
313 if (sscanf(last_space, " %d)", &error) != 1)
314 {
315 // Not a number here, parse error
316 assert(false);
317 DBUG_RETURN(false); // Don't suppress
318 }
319 DBUG_PRINT("info", ("error: %d", error));
320
321 switch (error)
322 {
323 case HA_ERR_TABLE_EXIST:
324 {
325 /*
326 The most common error is that NDB returns error 721
327 which means 'No such table' and the error is automatically
328 mapped to MySQL error code ER_TABLE_EXISTS_ERROR
329
330 This is most likley caused by another MySQL Server trying
331 to create the same table inbetween the check if table
332 exists(on local disk and in storage engine) and the actual
333 create.
334 */
335 DBUG_RETURN(true); // Suppress
336 break;
337 }
338
339 case 701: // System busy with other schema operation
340 case 711: // System busy with node restart, no schema operations
341 case 702: // Request to non-master(should never pop up to api)
342 {
343 /* Different errors from NDB, that just need to be retried later */
344 DBUG_RETURN(true); // Suppress
345 break;
346 }
347
348 case 4009: // Cluster failure
349 case HA_ERR_NO_CONNECTION: // 4009 auto mapped to this error
350 {
351 /*
352 No connection to cluster, don't spam error log with
353 failed to create ndb_xx tables
354 */
355 DBUG_RETURN(true); // Suppress
356 break;
357 }
358 }
359 }
360 DBUG_PRINT("info", ("Don't ignore error"));
361 DBUG_RETURN(false); // Don't suppress
362 }
363 };
364
365
366 bool
create_sys_table(const char * db,size_t db_length,const char * table,size_t table_length,bool create_if_not_exists,const char * create_definitions,const char * create_options)367 Ndb_local_connection::create_sys_table(const char* db, size_t db_length,
368 const char* table, size_t table_length,
369 bool create_if_not_exists,
370 const char* create_definitions,
371 const char* create_options)
372 {
373 DBUG_ENTER("Ndb_local_connection::create_table");
374 DBUG_PRINT("enter", ("db: '%s', table: '%s'", db, table));
375
376 // Create the SQL string
377 String sql_text(512);
378 sql_text.append(STRING_WITH_LEN("CREATE TABLE "));
379
380 if (create_if_not_exists)
381 sql_text.append(STRING_WITH_LEN("IF NOT EXISTS "));
382 sql_text.append(db, (uint32)db_length);
383 sql_text.append(STRING_WITH_LEN("."));
384 sql_text.append(table, (uint32)table_length);
385
386 sql_text.append(STRING_WITH_LEN(" ( "));
387 sql_text.append(create_definitions);
388 sql_text.append(STRING_WITH_LEN(" ) "));
389 sql_text.append(create_options);
390
391 // List of errors to ignore
392 uint ignore_mysql_errors[2] = {ER_TABLE_EXISTS_ERROR, 0};
393
394 /*
395 This is the only place where an error is suppressed
396 based one the original NDB error, wich is extracted
397 by parsing the error string, use a special suppressor
398 */
399 Create_sys_table_suppressor suppressor;
400
401 DBUG_RETURN(execute_query_iso(sql_text.lex_string(),
402 ignore_mysql_errors,
403 &suppressor));
404 }
405
406
407 bool
raw_run_query(const char * query,size_t query_length,const int * suppress_errors)408 Ndb_local_connection::raw_run_query(const char* query, size_t query_length,
409 const int* suppress_errors)
410 {
411 DBUG_ENTER("Ndb_local_connection::raw_run_query");
412
413 LEX_STRING sql_text = { (char*)query, query_length };
414
415 DBUG_RETURN(execute_query_iso(sql_text,
416 (const uint*)suppress_errors,
417 NULL));
418 }
419
420