MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
NdbInfo.cpp
1 /*
2  Copyright (c) 2009, 2010, 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 "NdbInfo.hpp"
19 
20 #include <ndbapi/ndb_cluster_connection.hpp>
21 
22 NdbInfo::NdbInfo(class Ndb_cluster_connection* connection,
23  const char* prefix, const char* dbname,
24  const char* table_prefix) :
25  m_connect_count(connection->get_connect_count()),
26  m_min_db_version(0),
27  m_connection(connection),
28  m_tables_table(NULL), m_columns_table(NULL),
29  m_prefix(prefix),
30  m_dbname(dbname),
31  m_table_prefix(table_prefix),
32  m_id_counter(0)
33 {
34 }
35 
36 bool NdbInfo::init(void)
37 {
38  if (pthread_mutex_init(&m_mutex, MY_MUTEX_INIT_FAST))
39  return false;
40  if (!load_hardcoded_tables())
41  return false;
42  return true;
43 }
44 
45 NdbInfo::~NdbInfo(void)
46 {
47  pthread_mutex_destroy(&m_mutex);
48 }
49 
50 BaseString NdbInfo::mysql_table_name(const char* table_name) const
51 {
52  DBUG_ENTER("mysql_table_name");
53  BaseString mysql_name;
54  mysql_name.assfmt("%s%s", m_prefix.c_str(), table_name);
55  DBUG_PRINT("exit", ("mysql_name: %s", mysql_name.c_str()));
56  DBUG_RETURN(mysql_name);
57 }
58 
59 bool NdbInfo::load_hardcoded_tables(void)
60 {
61  {
62  Table tabs("tables", 0);
63  if (!tabs.addColumn(Column("table_id", 0, Column::Number)) ||
64  !tabs.addColumn(Column("table_name", 1, Column::String)) ||
65  !tabs.addColumn(Column("comment", 2, Column::String)))
66  return false;
67 
68  BaseString hash_key = mysql_table_name(tabs.getName());
69  if (!m_tables.insert(hash_key.c_str(), tabs))
70  return false;
71  if (!m_tables.search(hash_key.c_str(), &m_tables_table))
72  return false;
73  }
74 
75  {
76  Table cols("columns", 1);
77  if (!cols.addColumn(Column("table_id", 0, Column::Number)) ||
78  !cols.addColumn(Column("column_id", 1, Column::Number)) ||
79  !cols.addColumn(Column("column_name", 2, Column::String)) ||
80  !cols.addColumn(Column("column_type", 3, Column::Number)) ||
81  !cols.addColumn(Column("comment", 4, Column::String)))
82  return false;
83 
84  BaseString hash_key = mysql_table_name(cols.getName());
85  if (!m_tables.insert(hash_key.c_str(), cols))
86  return false;
87  if (!m_tables.search(hash_key.c_str(), &m_columns_table))
88  return false;
89  }
90 
91  return true;
92 }
93 
94 bool NdbInfo::addColumn(Uint32 tableId, Column aCol)
95 {
96  Table * table = NULL;
97 
98  // Find the table with correct id
99  for (size_t i = 0; i < m_tables.entries(); i++)
100  {
101  table = m_tables.value(i);
102  if (table->m_table_id == tableId)
103  break;
104  }
105 
106  table->addColumn(aCol);
107 
108  return true;
109 }
110 
111 bool NdbInfo::load_ndbinfo_tables(void)
112 {
113  DBUG_ENTER("load_ndbinfo_tables");
114  assert(m_tables_table && m_columns_table);
115 
116  {
117  // Create tables by scanning the TABLES table
118  NdbInfoScanOperation* scanOp = NULL;
119  if (createScanOperation(m_tables_table, &scanOp) != 0)
120  DBUG_RETURN(false);
121 
122  if (scanOp->readTuples() != 0)
123  {
124  releaseScanOperation(scanOp);
125  DBUG_RETURN(false);
126  }
127 
128  const NdbInfoRecAttr *tableIdRes = scanOp->getValue("table_id");
129  const NdbInfoRecAttr *tableNameRes = scanOp->getValue("table_name");
130  if (!tableIdRes || !tableNameRes)
131  {
132  releaseScanOperation(scanOp);
133  DBUG_RETURN(false);
134  }
135 
136  if (scanOp->execute() != 0)
137  {
138  releaseScanOperation(scanOp);
139  DBUG_RETURN(false);
140  }
141 
142  int err;
143  while ((err = scanOp->nextResult()) == 1)
144  {
145  Uint32 tableId = tableIdRes->u_32_value();
146  const char * tableName = tableNameRes->c_str();
147  DBUG_PRINT("info", ("table: '%s', id: %u",
148  tableName, tableId));
149  switch (tableId) {
150  case 0:
151  assert(strcmp(tableName, "tables") == 0);
152  break;
153  case 1:
154  assert(strcmp(tableName, "columns") == 0);
155  break;
156 
157  default:
158  BaseString hash_key = mysql_table_name(tableName);
159  if (!m_tables.insert(hash_key.c_str(),
160  Table(tableName, tableId)))
161  {
162  DBUG_PRINT("error", ("Failed to insert Table('%s', %u)",
163  tableName, tableId));
164  releaseScanOperation(scanOp);
165  DBUG_RETURN(false);
166  }
167  }
168  }
169  releaseScanOperation(scanOp);
170 
171  if (err != 0)
172  DBUG_RETURN(false);
173  }
174 
175  {
176  // Fill tables with columns by scanning the COLUMNS table
177  NdbInfoScanOperation* scanOp = NULL;
178  if (createScanOperation(m_columns_table, &scanOp) != 0)
179  DBUG_RETURN(false);
180 
181  if (scanOp->readTuples() != 0)
182  {
183  releaseScanOperation(scanOp);
184  DBUG_RETURN(false);
185  }
186 
187  const NdbInfoRecAttr *tableIdRes = scanOp->getValue("table_id");
188  const NdbInfoRecAttr *columnIdRes = scanOp->getValue("column_id");
189  const NdbInfoRecAttr *columnNameRes = scanOp->getValue("column_name");
190  const NdbInfoRecAttr *columnTypeRes = scanOp->getValue("column_type");
191  if (!tableIdRes || !columnIdRes || !columnNameRes || !columnTypeRes)
192  {
193  releaseScanOperation(scanOp);
194  DBUG_RETURN(false);
195  }
196  if (scanOp->execute() != 0)
197  {
198  releaseScanOperation(scanOp);
199  DBUG_RETURN(false);
200  }
201 
202  int err;
203  while ((err = scanOp->nextResult()) == 1)
204  {
205  Uint32 tableId = tableIdRes->u_32_value();
206  Uint32 columnId = columnIdRes->u_32_value();
207  const char * columnName = columnNameRes->c_str();
208  Uint32 columnType = columnTypeRes->u_32_value();
209  DBUG_PRINT("info",
210  ("tableId: %u, columnId: %u, column: '%s', type: %d",
211  tableId, columnId, columnName, columnType));
212  switch (tableId) {
213  case 0:
214  case 1:
215  // Ignore columns for TABLES and COLUMNS tables since
216  // those are already known(hardcoded)
217  break;
218 
219  default:
220  {
221  Column::Type type;
222  switch(columnType)
223  {
224  case 1:
225  type = Column::String;
226  break;
227  case 2:
228  type = Column::Number;
229  break;
230  case 3:
231  type = Column::Number64;
232  break;
233  default:
234  {
235  DBUG_PRINT("error", ("Unknown columntype: %d", columnType));
236  releaseScanOperation(scanOp);
237  DBUG_RETURN(false);
238  }
239  }
240 
241  Column column(columnName, columnId, type);
242 
243  // Find the table with given id
244 
245  if (!addColumn(tableId, column))
246  {
247  DBUG_PRINT("error", ("Failed to add column for %d, %d, '%s', %d)",
248  tableId, columnId, columnName, columnType));
249  releaseScanOperation(scanOp);
250  DBUG_RETURN(false);
251  }
252  break;
253  }
254  }
255  }
256  releaseScanOperation(scanOp);
257 
258  if (err != 0)
259  DBUG_RETURN(false);
260  }
261  DBUG_RETURN(true);
262 }
263 
264 bool NdbInfo::load_tables()
265 {
266  if (!load_ndbinfo_tables())
267  {
268  // Remove any dynamic tables that might have been partially created
269  flush_tables();
270  return false;
271  }
272 
273  // After sucessfull load of the tables, set connect count
274  // and the min db version of cluster
275  m_connect_count = m_connection->get_connect_count();
276  m_min_db_version = m_connection->get_min_db_version();
277  return true;
278 }
279 
280 int NdbInfo::createScanOperation(const Table* table,
281  NdbInfoScanOperation** ret_scan_op,
282  Uint32 max_rows, Uint32 max_bytes)
283 {
284  NdbInfoScanOperation* scan_op = new NdbInfoScanOperation(*this, m_connection,
285  table, max_rows,
286  max_bytes);
287  if (!scan_op)
288  return ERR_OutOfMemory;
289  // Global id counter, not critical if you get same id on two instances
290  // since reference is also part of the unique identifier.
291  if (!scan_op->init(m_id_counter++))
292  {
293  delete scan_op;
294  return ERR_ClusterFailure;
295  }
296 
297  if (table->getTableId() < NUM_HARDCODED_TABLES)
298  {
299  // Each db node have the full list -> scan only one node
300  scan_op->m_max_nodes = 1;
301  }
302 
303  *ret_scan_op = scan_op;
304 
305  return 0;
306 }
307 
308 void NdbInfo::releaseScanOperation(NdbInfoScanOperation* scan_op) const
309 {
310  delete scan_op;
311 }
312 
313 void NdbInfo::flush_tables()
314 {
315  // Delete all but the hardcoded tables
316  while (m_tables.entries() > NUM_HARDCODED_TABLES)
317  {
318  for (size_t i = 0; i<m_tables.entries(); i++)
319  {
320  Table * tab = m_tables.value(i);
321  if (! (tab == m_tables_table || tab == m_columns_table))
322  {
323  m_tables.remove(i);
324  break;
325  }
326  }
327  }
328  assert(m_tables.entries() == NUM_HARDCODED_TABLES);
329 }
330 
331 bool
332 NdbInfo::check_tables()
333 {
334  if (unlikely(m_connection->get_connect_count() != m_connect_count ||
335  m_connection->get_min_db_version() != m_min_db_version))
336  {
337  // Connect count or min db version of cluster has changed
338  // -> flush the cached table definitions
339  flush_tables();
340  }
341  if (unlikely(m_tables.entries() <= NUM_HARDCODED_TABLES))
342  {
343  // Global table cache is not loaded yet or has been
344  // flushed, try to load it
345  if (!load_tables())
346  {
347  return false;
348  }
349  }
350  // Make sure that some dynamic tables have been loaded
351  assert(m_tables.entries() > NUM_HARDCODED_TABLES);
352  return true;
353 }
354 
355 
356 int
357 NdbInfo::openTable(const char* table_name,
358  const NdbInfo::Table** table_copy)
359 {
360  pthread_mutex_lock(&m_mutex);
361 
362  if (!check_tables()){
363  pthread_mutex_unlock(&m_mutex);
364  return ERR_ClusterFailure;
365  }
366 
367  Table* tab;
368  if (!m_tables.search(table_name, &tab))
369  {
370  // No such table existed
371  pthread_mutex_unlock(&m_mutex);
372  return ERR_NoSuchTable;
373  }
374 
375  // Return a _copy_ of the table
376  *table_copy = new Table(*tab);
377 
378  pthread_mutex_unlock(&m_mutex);
379  return 0;
380 }
381 
382 int
383 NdbInfo::openTable(Uint32 tableId,
384  const NdbInfo::Table** table_copy)
385 {
386  pthread_mutex_lock(&m_mutex);
387 
388  if (!check_tables()){
389  pthread_mutex_unlock(&m_mutex);
390  return ERR_ClusterFailure;
391  }
392 
393  // Find the table with correct id
394  const Table* table = NULL;
395  for (size_t i = 0; i < m_tables.entries(); i++)
396  {
397  const Table* tmp = m_tables.value(i);
398  if (tmp->m_table_id == tableId)
399  {
400  table = tmp;
401  break;
402  }
403  }
404  if (table == NULL)
405  {
406  // No such table existed
407  pthread_mutex_unlock(&m_mutex);
408  return ERR_NoSuchTable;
409  }
410 
411  // Return a _copy_ of the table
412  *table_copy = new Table(*table);
413 
414  pthread_mutex_unlock(&m_mutex);
415  return 0;
416 }
417 
418 void NdbInfo::closeTable(const Table* table) {
419  delete table;
420 }
421 
422 
423 // Column
424 
425 NdbInfo::Column::Column(const char* name, Uint32 col_id,
426  NdbInfo::Column::Type type) :
427  m_type(type),
428  m_column_id(col_id),
429  m_name(name)
430 {
431 }
432 
433 NdbInfo::Column::Column(const NdbInfo::Column & col)
434 {
435  m_column_id = col.m_column_id;
436  m_name.assign(col.m_name);
437  m_type = col.m_type;
438 }
439 
441 NdbInfo::Column::operator=(const NdbInfo::Column & col)
442 {
443  m_column_id = col.m_column_id;
444  m_name.assign(col.m_name);
445  m_type = col.m_type;
446  return *this;
447 }
448 
449 
450 // Table
451 
452 NdbInfo::Table::Table(const char *name, Uint32 id) :
453  m_name(name),
454  m_table_id(id)
455 {
456 };
457 
458 NdbInfo::Table::Table(const NdbInfo::Table& tab)
459 {
460  DBUG_ENTER("Table(const Table&");
461  m_table_id = tab.m_table_id;
462  m_name.assign(tab.m_name);
463  for (unsigned i = 0; i < tab.m_columns.size(); i++)
464  addColumn(*tab.m_columns[i]);
465  DBUG_VOID_RETURN;
466 }
467 
468 const NdbInfo::Table &
469 NdbInfo::Table::operator=(const NdbInfo::Table& tab)
470 {
471  DBUG_ENTER("Table::operator=");
472  m_table_id = tab.m_table_id;
473  m_name.assign(tab.m_name);
474  for (unsigned i = 0; i < tab.m_columns.size(); i++)
475  addColumn(*tab.m_columns[i]);
476  DBUG_RETURN(*this);
477 }
478 
479 NdbInfo::Table::~Table()
480 {
481  for (unsigned i = 0; i < m_columns.size(); i++)
482  delete m_columns[i];
483 };
484 
485 const char * NdbInfo::Table::getName() const
486 {
487  return m_name.c_str();
488 }
489 
490 Uint32 NdbInfo::Table::getTableId() const
491 {
492  return m_table_id;
493 }
494 
495 bool NdbInfo::Table::addColumn(const NdbInfo::Column aCol)
496 {
497  NdbInfo::Column* col = new NdbInfo::Column(aCol);
498  if (col == NULL)
499  {
500  errno = ENOMEM;
501  return false;
502  }
503 
504  if (m_columns.push_back(col))
505  {
506  delete col;
507  return false;
508  }
509  return true;
510 }
511 
512 unsigned NdbInfo::Table::columns(void) const {
513  return m_columns.size();
514 }
515 
516 const NdbInfo::Column*
517 NdbInfo::Table::getColumn(const unsigned attributeId) const
518 {
519  return (attributeId < m_columns.size()) ?
520  m_columns[attributeId]
521  : NULL;
522 }
523 
524 const NdbInfo::Column* NdbInfo::Table::getColumn(const char * name) const
525 {
526  DBUG_ENTER("Column::getColumn");
527  DBUG_PRINT("info", ("columns: %d", m_columns.size()));
528  const NdbInfo::Column* column = NULL;
529  for (uint i = 0; i < m_columns.size(); i++)
530  {
531  DBUG_PRINT("info", ("col: %d %s", i, m_columns[i]->m_name.c_str()));
532  if (strcmp(m_columns[i]->m_name.c_str(), name) == 0)
533  {
534  column = m_columns[i];
535  break;
536  }
537  }
538  DBUG_RETURN(column);
539 }
540 
541 
542 template class Vector<NdbInfo::Column*>;
543