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