1 /*
2    Copyright (c) 2003, 2016, 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 <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 #ifdef 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
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   DBUG_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 #ifdef 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
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 #ifndef 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
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