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