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