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