1 /*
2 Copyright (c) 2003, 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 #include <ndb_global.h>
26 #include "DictCache.hpp"
27 #include "NdbDictionaryImpl.hpp"
28 #include <NdbTick.h>
29 #include <NdbCondition.h>
30 #include <NdbSleep.h>
31
32 static NdbTableImpl * f_invalid_table = 0;
33 static NdbTableImpl * f_altered_table = 0;
34
35 // If we are linked with libstdc++ then thread safe
36 // initialization of the shared table objects can be simplified
37 #if HAVE_CXXABI_H
38 #include <my_pthread.h>
39
40 static my_pthread_once_t once_control = MY_PTHREAD_ONCE_INIT;
41
init_static_variables(void)42 void init_static_variables( void )
43 {
44 static NdbTableImpl _invalid_table;
45 static NdbTableImpl _altered_table;
46 f_invalid_table = &_invalid_table;
47 f_altered_table = &_altered_table;
48 }
49 #else
50 extern NdbMutex *g_ndb_connection_mutex;
51 static int ndb_dict_cache_count = 0;
52 #endif // HAVE_CXXABI_H
53
54 Ndb_local_table_info *
create(NdbTableImpl * table_impl,Uint32 sz)55 Ndb_local_table_info::create(NdbTableImpl *table_impl, Uint32 sz)
56 {
57 assert(! is_ndb_blob_table(table_impl));
58 Uint32 tot_size= sizeof(Ndb_local_table_info) - sizeof(Uint64)
59 + ((sz+7) & ~7); // round to Uint64
60 void *data= malloc(tot_size);
61 if (data == 0)
62 return 0;
63 memset(data, 0, tot_size);
64 new (data) Ndb_local_table_info(table_impl);
65 return (Ndb_local_table_info *) data;
66 }
67
destroy(Ndb_local_table_info * info)68 void Ndb_local_table_info::destroy(Ndb_local_table_info *info)
69 {
70 free((void *)info);
71 }
72
Ndb_local_table_info(NdbTableImpl * table_impl)73 Ndb_local_table_info::Ndb_local_table_info(NdbTableImpl *table_impl)
74 {
75 assert(! is_ndb_blob_table(table_impl));
76 m_table_impl= table_impl;
77 m_tuple_id_range.reset();
78 }
79
~Ndb_local_table_info()80 Ndb_local_table_info::~Ndb_local_table_info()
81 {
82 }
83
LocalDictCache()84 LocalDictCache::LocalDictCache(){
85 m_tableHash.createHashTable();
86 }
87
~LocalDictCache()88 LocalDictCache::~LocalDictCache(){
89 m_tableHash.releaseHashTable();
90 }
91
92 Ndb_local_table_info *
get(const char * name)93 LocalDictCache::get(const char * name){
94 ASSERT_NOT_MYSQLD;
95 assert(! is_ndb_blob_table(name));
96 const Uint32 len = (Uint32)strlen(name);
97 return m_tableHash.getData(name, len);
98 }
99
100 void
put(const char * name,Ndb_local_table_info * tab_info)101 LocalDictCache::put(const char * name, Ndb_local_table_info * tab_info){
102 ASSERT_NOT_MYSQLD;
103 assert(! is_ndb_blob_table(name));
104 const Uint32 id = tab_info->m_table_impl->m_id;
105 m_tableHash.insertKey(name, (Uint32)strlen(name), id, tab_info);
106 }
107
108 void
drop(const char * name)109 LocalDictCache::drop(const char * name){
110 ASSERT_NOT_MYSQLD;
111 assert(! is_ndb_blob_table(name));
112 Ndb_local_table_info *info= m_tableHash.deleteKey(name, (Uint32)strlen(name));
113 assert(info != 0);
114 Ndb_local_table_info::destroy(info);
115 }
116
117 /*****************************************************************
118 * Global cache
119 */
GlobalDictCache()120 GlobalDictCache::GlobalDictCache(){
121 DBUG_ENTER("GlobalDictCache::GlobalDictCache");
122 // Initialize static variables
123 #if HAVE_CXXABI_H
124 my_pthread_once(&once_control, init_static_variables);
125 #else
126 NdbMutex_Lock(g_ndb_connection_mutex);
127 if (f_invalid_table == NULL)
128 f_invalid_table = new NdbTableImpl();
129 if (f_altered_table == NULL)
130 f_altered_table = new NdbTableImpl();
131 ndb_dict_cache_count++;
132 NdbMutex_Unlock(g_ndb_connection_mutex);
133 #endif // HAVE_CXXABI_H
134 m_tableHash.createHashTable();
135 m_waitForTableCondition = NdbCondition_Create();
136 DBUG_VOID_RETURN;
137 }
138
~GlobalDictCache()139 GlobalDictCache::~GlobalDictCache(){
140 DBUG_ENTER("GlobalDictCache::~GlobalDictCache");
141 #if !HAVE_CXXABI_H
142 NdbMutex_Lock(g_ndb_connection_mutex);
143 if (--ndb_dict_cache_count == 0)
144 {
145 if (f_invalid_table)
146 {
147 delete f_invalid_table;
148 f_invalid_table = 0;
149 }
150 if (f_altered_table)
151 {
152 delete f_altered_table;
153 f_altered_table = 0;
154 }
155 }
156 NdbMutex_Unlock(g_ndb_connection_mutex);
157 #endif // !HAVE_CXXABI_H
158 NdbElement_t<Vector<TableVersion> > * curr = m_tableHash.getNext(0);
159 while(curr != 0){
160 Vector<TableVersion> * vers = curr->theData;
161 const unsigned sz = vers->size();
162 for(unsigned i = 0; i<sz ; i++){
163 TableVersion tv= (*vers)[i];
164 DBUG_PRINT(" ", ("vers[%d]: ver: %d, refCount: %d, status: %d",
165 i, tv.m_version, tv.m_refCount, tv.m_status));
166 if(tv.m_impl != 0)
167 {
168 DBUG_PRINT(" ", ("m_impl: internalname: %s",
169 tv.m_impl->m_internalName.c_str()));
170 delete (* vers)[i].m_impl;
171 }
172 }
173 delete curr->theData;
174 curr->theData= NULL;
175 curr = m_tableHash.getNext(curr);
176 }
177 m_tableHash.releaseHashTable();
178 NdbCondition_Destroy(m_waitForTableCondition);
179 DBUG_VOID_RETURN;
180 }
181
printCache()182 void GlobalDictCache::printCache()
183 {
184 DBUG_ENTER("GlobalDictCache::printCache");
185 NdbElement_t<Vector<TableVersion> > * curr = m_tableHash.getNext(0);
186 while(curr != 0){
187 DBUG_PRINT("curr", ("len: %d, hash: %d, lk: %d, str: %s",
188 curr->len, curr->hash, curr->localkey1,
189 (char*) curr->str));
190 if (curr->theData){
191 Vector<TableVersion> * vers = curr->theData;
192 const unsigned sz = vers->size();
193 for(unsigned i = 0; i<sz ; i++){
194 TableVersion tv= (*vers)[i];
195 DBUG_PRINT(" ", ("impl: %p vers[%d]: ver: %d, refCount: %d, status: %d",
196 tv.m_impl, i, tv.m_version, tv.m_refCount, tv.m_status));
197 if(tv.m_impl != 0)
198 {
199 DBUG_PRINT(" ", ("m_impl: internalname: %s",
200 tv.m_impl->m_internalName.c_str()));
201 }
202 }
203 }
204 else
205 {
206 DBUG_PRINT(" ", ("NULL"));
207 }
208 curr = m_tableHash.getNext(curr);
209 }
210 DBUG_VOID_RETURN;
211 }
212
213 NdbTableImpl *
get(const char * name,int * error)214 GlobalDictCache::get(const char * name, int *error)
215 {
216 DBUG_ENTER("GlobalDictCache::get");
217 DBUG_PRINT("enter", ("name: %s", name));
218 assert(! is_ndb_blob_table(name));
219
220 const Uint32 len = (Uint32)strlen(name);
221 Vector<TableVersion> * versions = 0;
222 versions = m_tableHash.getData(name, len);
223 if(versions == 0){
224 versions = new Vector<TableVersion>(2);
225 if (versions == NULL)
226 {
227 *error = -1;
228 DBUG_RETURN(0);
229 }
230 m_tableHash.insertKey(name, len, 0, versions);
231 }
232
233 int waitTime = 100;
234
235 bool retreive = false;
236 while(versions->size() > 0 && !retreive){
237 TableVersion * ver = & versions->back();
238 switch(ver->m_status){
239 case OK:
240 if (ver->m_impl->m_status == NdbDictionary::Object::Invalid)
241 {
242 ver->m_status = DROPPED;
243 retreive = true; // Break loop
244 if (ver->m_refCount == 0)
245 {
246 delete ver->m_impl;
247 versions->erase(versions->size() - 1);
248 }
249 break;
250 }
251 ver->m_refCount++;
252 DBUG_PRINT("info", ("Table OK tab: %p version=%x.%x refCount=%u",
253 ver->m_impl,
254 ver->m_impl->m_version & 0xFFFFFF,
255 ver->m_impl->m_version >> 24,
256 ver->m_refCount));
257 DBUG_RETURN(ver->m_impl);
258 case DROPPED:
259 retreive = true; // Break loop
260 break;
261 case RETREIVING:
262 DBUG_PRINT("info", ("Wait for retrieving thread"));
263 NdbCondition_WaitTimeout(m_waitForTableCondition, m_mutex, waitTime);
264 continue;
265 }
266 }
267
268 /**
269 * Create new...
270 */
271 TableVersion tmp;
272 tmp.m_version = 0;
273 tmp.m_impl = 0;
274 tmp.m_status = RETREIVING;
275 tmp.m_refCount = 1; // The one retreiving it
276 if (versions->push_back(tmp))
277 {
278 *error = -1;
279 DBUG_RETURN(0);
280 }
281 DBUG_PRINT("info", ("No table found"));
282 DBUG_RETURN(0);
283 }
284
285 NdbTableImpl *
put(const char * name,NdbTableImpl * tab)286 GlobalDictCache::put(const char * name, NdbTableImpl * tab)
287 {
288 DBUG_ENTER("GlobalDictCache::put");
289 DBUG_PRINT("enter", ("tab: %p name: %s, internal_name: %s version: %x.%x",
290 tab, name,
291 tab ? tab->m_internalName.c_str() : "tab NULL",
292 tab ? tab->m_version & 0xFFFFFF : 0,
293 tab ? tab->m_version >> 24 : 0));
294 assert(! is_ndb_blob_table(name));
295
296 const Uint32 len = (Uint32)strlen(name);
297 Vector<TableVersion> * vers = m_tableHash.getData(name, len);
298 if(vers == 0){
299 // Should always tried to retreive it first
300 // and thus there should be a record
301 abort();
302 }
303
304 const Uint32 sz = vers->size();
305 if(sz == 0){
306 // Should always tried to retreive it first
307 // and thus there should be a record
308 abort();
309 }
310
311 TableVersion & ver = vers->back();
312 if(ver.m_status != RETREIVING ||
313 !(ver.m_impl == 0 ||
314 ver.m_impl == f_invalid_table || ver.m_impl == f_altered_table) ||
315 ver.m_version != 0 ||
316 ver.m_refCount == 0){
317 abort();
318 }
319
320 if(tab == 0)
321 {
322 DBUG_PRINT("info", ("No table found in db"));
323 vers->erase(sz - 1);
324 }
325 else if (ver.m_impl == 0) {
326 DBUG_PRINT("info", ("Table OK"));
327 ver.m_impl = tab;
328 ver.m_version = tab->m_version;
329 ver.m_status = OK;
330 }
331 else if (ver.m_impl == f_invalid_table)
332 {
333 DBUG_PRINT("info", ("Table DROPPED invalid"));
334 ver.m_impl = tab;
335 ver.m_version = tab->m_version;
336 ver.m_status = DROPPED;
337 ver.m_impl->m_status = NdbDictionary::Object::Invalid;
338 }
339 else if(ver.m_impl == f_altered_table)
340 {
341 DBUG_PRINT("info", ("Table DROPPED altered"));
342 ver.m_impl = tab;
343 ver.m_version = tab->m_version;
344 ver.m_status = DROPPED;
345 ver.m_impl->m_status = NdbDictionary::Object::Altered;
346 }
347 else
348 {
349 abort();
350 }
351 NdbCondition_Broadcast(m_waitForTableCondition);
352 DBUG_RETURN(tab);
353 }
354
355 unsigned
get_size()356 GlobalDictCache::get_size()
357 {
358 NdbElement_t<Vector<TableVersion> > * curr = m_tableHash.getNext(0);
359 int sz = 0;
360 while(curr != 0){
361 sz += curr->theData->size();
362 curr = m_tableHash.getNext(curr);
363 }
364 if (sz)
365 {
366 printCache();
367 }
368 return sz;
369 }
370
371 void
invalidate_all()372 GlobalDictCache::invalidate_all()
373 {
374 DBUG_ENTER("GlobalDictCache::invalidate_all");
375 NdbElement_t<Vector<TableVersion> > * curr = m_tableHash.getNext(0);
376 while(curr != 0){
377 Vector<TableVersion> * vers = curr->theData;
378 if (vers->size())
379 {
380 TableVersion * ver = & vers->back();
381 if (ver->m_status != RETREIVING)
382 {
383 ver->m_impl->m_status = NdbDictionary::Object::Invalid;
384 ver->m_status = DROPPED;
385 if (ver->m_refCount == 0)
386 {
387 delete ver->m_impl;
388 vers->erase(vers->size() - 1);
389 }
390 }
391 }
392 curr = m_tableHash.getNext(curr);
393 }
394 DBUG_VOID_RETURN;
395 }
396
397 void
invalidateDb(const char * name,size_t len)398 GlobalDictCache::invalidateDb(const char * name, size_t len)
399 {
400 DBUG_ENTER("GlobalDictCache::invalidateDb");
401 NdbElement_t<Vector<TableVersion> > * curr = m_tableHash.getNext(0);
402 while(curr != 0)
403 {
404 Vector<TableVersion> * vers = curr->theData;
405 if (vers->size())
406 {
407 TableVersion * ver = & vers->back();
408 if (ver->m_status != RETREIVING)
409 {
410 if (ver->m_impl->matchDb(name, len))
411 {
412 ver->m_impl->m_status = NdbDictionary::Object::Invalid;
413 ver->m_status = DROPPED;
414 if (ver->m_refCount == 0)
415 {
416 delete ver->m_impl;
417 vers->erase(vers->size() - 1);
418 }
419 }
420 }
421 }
422 curr = m_tableHash.getNext(curr);
423 }
424 DBUG_VOID_RETURN;
425 }
426
427 void
release(const NdbTableImpl * tab,int invalidate)428 GlobalDictCache::release(const NdbTableImpl * tab, int invalidate)
429 {
430 DBUG_ENTER("GlobalDictCache::release");
431 DBUG_PRINT("enter", ("tab: %p internal_name: %s",
432 tab, tab->m_internalName.c_str()));
433 assert(! is_ndb_blob_table(tab));
434
435 unsigned i;
436 const Uint32 len = (Uint32)strlen(tab->m_internalName.c_str());
437 Vector<TableVersion> * vers =
438 m_tableHash.getData(tab->m_internalName.c_str(), len);
439 if(vers == 0){
440 // Should always tried to retreive it first
441 // and thus there should be a record
442 abort();
443 }
444
445 const Uint32 sz = vers->size();
446 if(sz == 0){
447 // Should always tried to retreive it first
448 // and thus there should be a record
449 abort();
450 }
451
452 for(i = 0; i < sz; i++){
453 TableVersion & ver = (* vers)[i];
454 if(ver.m_impl == tab){
455 if(ver.m_refCount == 0 || ver.m_status == RETREIVING ||
456 ver.m_version != tab->m_version){
457 DBUG_PRINT("info", ("Releasing with refCount=%d status=%d impl=%p",
458 ver.m_refCount, ver.m_status, ver.m_impl));
459 break;
460 }
461
462 ver.m_refCount--;
463 if (ver.m_impl->m_status == NdbDictionary::Object::Invalid || invalidate)
464 {
465 ver.m_impl->m_status = NdbDictionary::Object::Invalid;
466 ver.m_status = DROPPED;
467 }
468 if (ver.m_refCount == 0 && ver.m_status == DROPPED)
469 {
470 DBUG_PRINT("info", ("refCount is zero, deleting m_impl"));
471 delete ver.m_impl;
472 vers->erase(i);
473 }
474 DBUG_VOID_RETURN;
475 }
476 }
477
478 for(i = 0; i<sz; i++){
479 TableVersion & ver = (* vers)[i];
480 ndbout_c("%d: version: %d refCount: %d status: %d impl: %p",
481 i, ver.m_version, ver.m_refCount,
482 ver.m_status, ver.m_impl);
483 }
484
485 abort();
486 }
487
488 void
alter_table_rep(const char * name,Uint32 tableId,Uint32 tableVersion,bool altered)489 GlobalDictCache::alter_table_rep(const char * name,
490 Uint32 tableId,
491 Uint32 tableVersion,
492 bool altered)
493 {
494 DBUG_ENTER("GlobalDictCache::alter_table_rep");
495 const Uint32 len = (Uint32)strlen(name);
496 Vector<TableVersion> * vers =
497 m_tableHash.getData(name, len);
498
499 if(vers == 0)
500 {
501 DBUG_VOID_RETURN;
502 }
503
504 assert(! is_ndb_blob_table(name));
505 const Uint32 sz = vers->size();
506 if(sz == 0)
507 {
508 DBUG_VOID_RETURN;
509 }
510
511 for(Uint32 i = 0; i < sz; i++)
512 {
513 TableVersion & ver = (* vers)[i];
514 if(ver.m_version == tableVersion && ver.m_impl &&
515 (Uint32) ver.m_impl->m_id == tableId)
516 {
517 ver.m_status = DROPPED;
518 ver.m_impl->m_status = altered ?
519 NdbDictionary::Object::Altered : NdbDictionary::Object::Invalid;
520 if (ver.m_refCount == 0)
521 {
522 delete ver.m_impl;
523 vers->erase(i);
524 }
525 DBUG_VOID_RETURN;
526 }
527
528 if(i == sz - 1 && ver.m_status == RETREIVING)
529 {
530 ver.m_impl = altered ? f_altered_table : f_invalid_table;
531 DBUG_VOID_RETURN;
532 }
533 }
534 DBUG_VOID_RETURN;
535 }
536
537 int
chg_ref_count(const NdbTableImpl * impl,int value)538 GlobalDictCache::chg_ref_count(const NdbTableImpl * impl, int value)
539 {
540 DBUG_ENTER("GlobalDictCache::chg_ref_count");
541 const char * name = impl->m_internalName.c_str();
542 assert(! is_ndb_blob_table(name));
543
544 const Uint32 len = (Uint32)strlen(name);
545 Vector<TableVersion> * vers =
546 m_tableHash.getData(name, len);
547
548 if(vers == 0)
549 {
550 DBUG_RETURN(-1);
551 }
552
553 const Uint32 sz = vers->size();
554 if(sz == 0)
555 {
556 DBUG_RETURN(-1);
557 }
558
559 for(Uint32 i = 0; i < sz; i++)
560 {
561 TableVersion & ver = (* vers)[i];
562 if(ver.m_impl == impl)
563 {
564 if (value == +1)
565 {
566 DBUG_PRINT("info", ("%s id=%u ver=0x%x: inc old ref count %u",
567 name, impl->m_id, impl->m_version, ver.m_refCount));
568 ver.m_refCount++;
569 }
570 else if (value == -1)
571 {
572 DBUG_PRINT("info", ("%s id=%u ver=0x%x: dec old ref count %u",
573 name, impl->m_id, impl->m_version, ver.m_refCount));
574 if (ver.m_refCount == 0)
575 abort();
576 ver.m_refCount--;
577 if (ver.m_refCount == 0)
578 {
579 delete ver.m_impl;
580 vers->erase(i);
581 }
582 }
583 else
584 abort();
585 DBUG_RETURN(0);
586 }
587 }
588 DBUG_RETURN(0);
589 }
590
591 template class Vector<GlobalDictCache::TableVersion>;
592