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