MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Ndb.cpp
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 as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17 
18 
19 
20 
21 /*****************************************************************************
22 Name: Ndb.cpp
23 ******************************************************************************/
24 
25 #include <ndb_global.h>
26 
27 #include "API.hpp"
28 #include <md5_hash.hpp>
29 #include <NdbSleep.h>
30 #include <NdbOut.hpp>
31 #include <ndb_limits.h>
32 #include <NdbEnv.h>
33 #include <BaseString.hpp>
34 #include <NdbSqlUtil.hpp>
35 
36 /****************************************************************************
37 void connect();
38 
39 Connect to any node which has no connection at the moment.
40 ****************************************************************************/
41 NdbTransaction* Ndb::doConnect(Uint32 tConNode, Uint32 instance)
42 {
43  Uint32 tNode;
44  Uint32 tAnyAlive = 0;
45  int TretCode= 0;
46 
47  DBUG_ENTER("Ndb::doConnect");
48 
49  if (tConNode != 0) {
50  TretCode = NDB_connect(tConNode, instance);
51  if ((TretCode == 1) || (TretCode == 2)) {
52 //****************************************************************************
53 // We have connections now to the desired node. Return
54 //****************************************************************************
55  DBUG_RETURN(getConnectedNdbTransaction(tConNode, instance));
56  } else if (TretCode < 0) {
57  DBUG_RETURN(NULL);
58  } else if (TretCode != 0) {
59  tAnyAlive = 1;
60  }//if
61  }//if
62 //****************************************************************************
63 // We will connect to any node. Make sure that we have connections to all
64 // nodes.
65 //****************************************************************************
66  Uint32 anyInstance = 0;
67  if (theImpl->m_optimized_node_selection)
68  {
70  theImpl->m_node_iter;
71  theImpl->m_ndb_cluster_connection.init_get_next_node(node_iter);
72  while ((tNode= theImpl->m_ndb_cluster_connection.get_next_node(node_iter)))
73  {
74  TretCode= NDB_connect(tNode, anyInstance);
75  if ((TretCode == 1) ||
76  (TretCode == 2))
77  {
78 //****************************************************************************
79 // We have connections now to the desired node. Return
80 //****************************************************************************
81  DBUG_RETURN(getConnectedNdbTransaction(tNode, anyInstance));
82  } else if (TretCode < 0) {
83  DBUG_RETURN(NULL);
84  } else if (TretCode != 0) {
85  tAnyAlive= 1;
86  }//if
87  DBUG_PRINT("info",("tried node %d, TretCode %d, error code %d, %s",
88  tNode, TretCode, getNdbError().code,
89  getNdbError().message));
90  }
91  }
92  else // just do a regular round robin
93  {
94  Uint32 tNoOfDbNodes= theImpl->theNoOfDBnodes;
95  Uint32 &theCurrentConnectIndex= theImpl->theCurrentConnectIndex;
96  UintR Tcount = 0;
97  do {
98  theCurrentConnectIndex++;
99  if (theCurrentConnectIndex >= tNoOfDbNodes)
100  theCurrentConnectIndex = 0;
101 
102  Tcount++;
103  tNode= theImpl->theDBnodes[theCurrentConnectIndex];
104  TretCode= NDB_connect(tNode, anyInstance);
105  if ((TretCode == 1) ||
106  (TretCode == 2))
107  {
108 //****************************************************************************
109 // We have connections now to the desired node. Return
110 //****************************************************************************
111  DBUG_RETURN(getConnectedNdbTransaction(tNode, anyInstance));
112  } else if (TretCode < 0) {
113  DBUG_RETURN(NULL);
114  } else if (TretCode != 0) {
115  tAnyAlive= 1;
116  }//if
117  DBUG_PRINT("info",("tried node %d TretCode %d", tNode, TretCode));
118  } while (Tcount < tNoOfDbNodes);
119  }
120 //****************************************************************************
121 // We were unable to find a free connection. If no node alive we will report
122 // error code for cluster failure otherwise connection failure.
123 //****************************************************************************
124  if (tAnyAlive == 1) {
125 #ifdef VM_TRACE
126  ndbout << "TretCode = " << TretCode << endl;
127 #endif
128  theError.code = 4006;
129  } else {
130  theError.code = 4009;
131  }//if
132  DBUG_RETURN(NULL);
133 }
134 
135 int
136 Ndb::NDB_connect(Uint32 tNode, Uint32 instance)
137 {
138 //****************************************************************************
139 // We will perform seize of a transaction record in DBTC in the specified node.
140 //***************************************************************************
141 
142  int tReturnCode;
143 
144  DBUG_ENTER("Ndb::NDB_connect");
145 
146  {
147  if (theImpl->get_node_stopping(tNode))
148  {
149  DBUG_RETURN(0);
150  }
151  }
152 
153  NdbTransaction * tConArray = theConnectionArray[tNode];
154  if (instance != 0 && tConArray != 0)
155  {
156  NdbTransaction* prev = 0;
157  NdbTransaction* curr = tConArray;
158  while (curr)
159  {
160  if (refToInstance(curr->m_tcRef) == instance)
161  {
162  if (prev != 0)
163  {
164  prev->theNext = curr->theNext;
165  curr->theNext = tConArray;
166  theConnectionArray[tNode] = curr;
167  }
168  else
169  {
170  assert(curr == tConArray);
171  }
172  DBUG_RETURN(2);
173  }
174  prev = curr;
175  curr = curr->theNext;
176  }
177  }
178  else if (tConArray != NULL)
179  {
180  DBUG_RETURN(2);
181  }
182 
183  NdbTransaction * tNdbCon = getNdbCon(); // Get free connection object.
184  if (tNdbCon == NULL) {
185  DBUG_RETURN(4);
186  }//if
187  NdbApiSignal* tSignal = getSignal(); // Get signal object
188  if (tSignal == NULL) {
189  releaseNdbCon(tNdbCon);
190  DBUG_RETURN(4);
191  }//if
192  if (tSignal->setSignal(GSN_TCSEIZEREQ, DBTC) == -1) {
193  releaseNdbCon(tNdbCon);
194  releaseSignal(tSignal);
195  DBUG_RETURN(4);
196  }//if
197  tSignal->setData(tNdbCon->ptr2int(), 1);
198 //************************************************
199 // Set connection pointer as NdbTransaction object
200 //************************************************
201  tSignal->setData(theMyRef, 2); // Set my block reference
202  tSignal->setData(instance, 3); // Set requested instance
203  tNdbCon->Status(NdbTransaction::Connecting); // Set status to connecting
204  tNdbCon->theDBnode = tNode;
205  Uint32 nodeSequence;
206  tReturnCode= sendRecSignal(tNode, WAIT_TC_SEIZE, tSignal,
207  0, &nodeSequence);
208  releaseSignal(tSignal);
209  if ((tReturnCode == 0) && (tNdbCon->Status() == NdbTransaction::Connected)) {
210  //************************************************
211  // Send and receive was successful
212  //************************************************
213  NdbTransaction* tPrevFirst = theConnectionArray[tNode];
214  tNdbCon->setConnectedNodeId(tNode, nodeSequence);
215 
216  tNdbCon->setMyBlockReference(theMyRef);
217  theConnectionArray[tNode] = tNdbCon;
218  tNdbCon->theNext = tPrevFirst;
219  DBUG_RETURN(1);
220  } else {
221 //****************************************************************************
222 // Unsuccessful connect is indicated by 3.
223 //****************************************************************************
224  DBUG_PRINT("info",
225  ("unsuccessful connect tReturnCode %d, tNdbCon->Status() %d",
226  tReturnCode, tNdbCon->Status()));
227  releaseNdbCon(tNdbCon);
228  if (theError.code == 299 || // single user mode
229  theError.code == 281 ) // cluster shutdown in progress
230  {
231  // no need to retry with other node
232  DBUG_RETURN(-1);
233  }
234 
240  switch(tReturnCode){
241  case -2:
242  case -3:
243  DBUG_RETURN(0);
244  }
245 
246  DBUG_RETURN(3);
247  }//if
248 }//Ndb::NDB_connect()
249 
251 Ndb::getConnectedNdbTransaction(Uint32 nodeId, Uint32 instance){
252  NdbTransaction* next = theConnectionArray[nodeId];
253  if (instance != 0)
254  {
255  NdbTransaction * prev = 0;
256  while (next)
257  {
258  if (refToInstance(next->m_tcRef) == instance)
259  {
260  if (prev != 0)
261  {
262  assert(false); // Should have been moved in NDB_connect
263  prev->theNext = next->theNext;
264  goto found_middle;
265  }
266  else
267  {
268  assert(next == theConnectionArray[nodeId]);
269  goto found_first;
270  }
271  }
272  prev = next;
273  next = next->theNext;
274  }
275  assert(false); // !!
276  return 0;
277  }
278 found_first:
279  theConnectionArray[nodeId] = next->theNext;
280 found_middle:
281  next->theNext = NULL;
282 
283  return next;
284 }//Ndb::getConnectedNdbTransaction()
285 
286 /*****************************************************************************
287 disconnect();
288 
289 Remark: Disconnect all connections to the database.
290 *****************************************************************************/
291 void
292 Ndb::doDisconnect()
293 {
294  DBUG_ENTER("Ndb::doDisconnect");
295  NdbTransaction* tNdbCon;
296  CHECK_STATUS_MACRO_VOID;
297 
298  Uint32 tNoOfDbNodes = theImpl->theNoOfDBnodes;
299  Uint8 *theDBnodes= theImpl->theDBnodes;
300  DBUG_PRINT("info", ("theNoOfDBnodes=%d", tNoOfDbNodes));
301  UintR i;
302  for (i = 0; i < tNoOfDbNodes; i++) {
303  Uint32 tNode = theDBnodes[i];
304  tNdbCon = theConnectionArray[tNode];
305  while (tNdbCon != NULL) {
306  NdbTransaction* tmpNdbCon = tNdbCon;
307  tNdbCon = tNdbCon->theNext;
308  releaseConnectToNdb(tmpNdbCon);
309  }//while
310  }//for
311  tNdbCon = theTransactionList;
312  while (tNdbCon != NULL) {
313  NdbTransaction* tmpNdbCon = tNdbCon;
314  tNdbCon = tNdbCon->theNext;
315  releaseConnectToNdb(tmpNdbCon);
316  }//while
317  DBUG_VOID_RETURN;
318 }//Ndb::disconnect()
319 
320 /*****************************************************************************
321 int waitUntilReady(int timeout);
322 
323 Return Value: Returns 0 if the Ndb is ready within timeout seconds.
324  Returns -1 otherwise.
325 Remark: Waits until a node has status != 0
326 *****************************************************************************/
327 int
329 {
330  DBUG_ENTER("Ndb::waitUntilReady");
331  int secondsCounter = 0;
332  int milliCounter = 0;
333 
334  if (theInitState != Initialised) {
335  // Ndb::init is not called
336  theError.code = 4256;
337  DBUG_RETURN(-1);
338  }
339 
340  while (theNode == 0) {
341  if (secondsCounter >= timeout)
342  {
343  theError.code = 4269;
344  DBUG_RETURN(-1);
345  }
346  NdbSleep_MilliSleep(100);
347  milliCounter += 100;
348  if (milliCounter >= 1000) {
349  secondsCounter++;
350  milliCounter = 0;
351  }//if
352  }
353 
354  if (theImpl->m_ndb_cluster_connection.wait_until_ready
355  (timeout-secondsCounter,30) < 0)
356  {
357  theError.code = 4009;
358  DBUG_RETURN(-1);
359  }
360 
361  DBUG_RETURN(0);
362 }
363 
364 /*****************************************************************************
365 NdbTransaction* computeHash()
366 
367 Return Value: Returns 0 for success, NDBAPI error code otherwise
368 Remark: Computes the distribution hash value for a row with the
369  supplied distribtion key values.
370  Only relevant for natively partitioned tables.
371 *****************************************************************************/
372 int
373 Ndb::computeHash(Uint32 *retval,
375  const struct Key_part_ptr * keyData,
376  void* buf, Uint32 bufLen)
377 {
378  Uint32 j = 0;
379  Uint32 sumlen = 0; // Needed len
380  const NdbTableImpl* impl = &NdbTableImpl::getImpl(*table);
381  const NdbColumnImpl* const * cols = impl->m_columns.getBase();
382  Uint32 len;
383  char* pos;
384 
385  Uint32 colcnt = impl->m_columns.size();
386  Uint32 parts = impl->m_noOfDistributionKeys;
387 
388  if (unlikely(impl->m_fragmentType == NdbDictionary::Object::UserDefined))
389  {
390  /* Calculating native hash on keys in user defined
391  * partitioned table is probably part of a bug
392  */
393  goto euserdeftable;
394  }
395 
396  if (parts == 0)
397  {
398  parts = impl->m_noOfKeys;
399  }
400 
401  for (Uint32 i = 0; i<parts; i++)
402  {
403  if (unlikely(keyData[i].ptr == 0))
404  goto enullptr;
405  }
406 
407  if (unlikely(keyData[parts].ptr != 0))
408  goto emissingnullptr;
409 
410  const NdbColumnImpl* partcols[NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY];
411  for (Uint32 i = 0; i<colcnt && j < parts; i++)
412  {
413  if (cols[i]->m_distributionKey)
414  {
415  // wl3717_todo
416  // char allowed now as dist key so this case should be tested
417  partcols[j++] = cols[i];
418  }
419  }
420  DBUG_ASSERT(j == parts);
421 
422  for (Uint32 i = 0; i<parts; i++)
423  {
424  Uint32 lb, len;
425  if (unlikely(!NdbSqlUtil::get_var_length(partcols[i]->m_type,
426  keyData[i].ptr,
427  keyData[i].len,
428  lb, len)))
429  goto emalformedkey;
430 
431  if (unlikely(keyData[i].len < (lb + len)))
432  goto elentosmall;
433 
434  Uint32 maxlen = (partcols[i]->m_attrSize * partcols[i]->m_arraySize);
435 
436  if (unlikely(lb == 0 && keyData[i].len != maxlen))
437  goto emalformedkey;
438 
439  if (partcols[i]->m_cs)
440  {
441  Uint32 xmul = partcols[i]->m_cs->strxfrm_multiply;
442  xmul = xmul ? xmul : 1;
443  len = xmul * (maxlen - lb);
444  }
445 
446  len = (lb + len + 3) & ~(Uint32)3;
447  sumlen += len;
448 
449  }
450 
451  if (buf)
452  {
453  /* Get 64-bit aligned ptr required for hashing */
454  assert(bufLen != 0);
455  UintPtr org = UintPtr(buf);
456  UintPtr use = (org + 7) & ~(UintPtr)7;
457 
458  buf = (void*)use;
459  bufLen -= Uint32(use - org);
460 
461  if (unlikely(sumlen > bufLen))
462  goto ebuftosmall;
463  }
464  else
465  {
466  buf = malloc(sumlen);
467  if (unlikely(buf == 0))
468  goto enomem;
469  bufLen = 0;
470  assert((UintPtr(buf) & 7) == 0);
471  }
472 
473  pos = (char*)buf;
474  for (Uint32 i = 0; i<parts; i++)
475  {
476  Uint32 lb, len;
477  NdbSqlUtil::get_var_length(partcols[i]->m_type,
478  keyData[i].ptr, keyData[i].len, lb, len);
479  CHARSET_INFO* cs;
480  if ((cs = partcols[i]->m_cs))
481  {
482  Uint32 xmul = cs->strxfrm_multiply;
483  if (xmul == 0)
484  xmul = 1;
485  /*
486  * Varchar end-spaces are ignored in comparisons. To get same hash
487  * we blank-pad to maximum length via strnxfrm.
488  */
489  Uint32 maxlen = (partcols[i]->m_attrSize * partcols[i]->m_arraySize);
490  Uint32 dstLen = xmul * (maxlen - lb);
492  (unsigned char*)pos,
493  dstLen,
494  ((unsigned char*)keyData[i].ptr)+lb,
495  len);
496 
497  if (unlikely(n == -1))
498  goto emalformedstring;
499 
500  while ((n & 3) != 0)
501  {
502  pos[n++] = 0;
503  }
504  pos += n;
505  }
506  else
507  {
508  len += lb;
509  memcpy(pos, keyData[i].ptr, len);
510  while (len & 3)
511  {
512  * (pos + len++) = 0;
513  }
514  pos += len;
515  }
516  }
517  len = Uint32(UintPtr(pos) - UintPtr(buf));
518  assert((len & 3) == 0);
519 
520  Uint32 values[4];
521  md5_hash(values, (const Uint64*)buf, len >> 2);
522 
523  if (retval)
524  {
525  * retval = values[1];
526  }
527 
528  if (bufLen == 0)
529  free(buf);
530 
531  return 0;
532 
533 euserdeftable:
534  return 4544;
535 
536 enullptr:
537  return 4316;
538 
539 emissingnullptr:
540  return 4276;
541 
542 elentosmall:
543  return 4277;
544 
545 ebuftosmall:
546  return 4278;
547 
548 emalformedstring:
549  if (bufLen == 0)
550  free(buf);
551 
552  return 4279;
553 
554 emalformedkey:
555  return 4280;
556 
557 enomem:
558  return 4000;
559 }
560 
561 int
562 Ndb::computeHash(Uint32 *retval,
563  const NdbRecord *keyRec,
564  const char *keyData,
565  void* buf, Uint32 bufLen)
566 {
567  Uint32 len;
568  char* pos = NULL;
569 
570  Uint32 parts = keyRec->distkey_index_length;
571 
572  if (unlikely(keyRec->flags & NdbRecord::RecHasUserDefinedPartitioning))
573  {
574  /* Calculating native hash on keys in user defined
575  * partitioned table is probably part of a bug
576  */
577  goto euserdeftable;
578  }
579 
580  if (buf)
581  {
582  /* Get 64-bit aligned address as required for hashing */
583  assert(bufLen != 0);
584  UintPtr org = UintPtr(buf);
585  UintPtr use = (org + 7) & ~(UintPtr)7;
586 
587  buf = (void*)use;
588  bufLen -= Uint32(use - org);
589  }
590  else
591  {
592  /* We malloc buf here. Don't have a handy 'Max distr key size'
593  * variable, so let's use the key length, which must include
594  * the Distr key.
595  */
596  buf= malloc(keyRec->m_keyLenInWords << 2);
597  if (unlikely(buf == 0))
598  goto enomem;
599  bufLen= 0; /* So we remember to deallocate */
600  assert((UintPtr(buf) & 7) == 0);
601  }
602 
603  pos= (char*) buf;
604 
605  for (Uint32 i = 0; i < parts; i++)
606  {
607  const struct NdbRecord::Attr &keyAttr =
608  keyRec->columns[keyRec->distkey_indexes[i]];
609 
610  Uint32 len;
611  Uint32 maxlen = keyAttr.maxSize;
612  unsigned char *src= (unsigned char*)keyData + keyAttr.offset;
613 
614  if (keyAttr.flags & NdbRecord::IsVar1ByteLen)
615  {
616  if (keyAttr.flags & NdbRecord::IsMysqldShrinkVarchar)
617  {
618  len = uint2korr(src);
619  src += 2;
620  }
621  else
622  {
623  len = *src;
624  src += 1;
625  }
626  maxlen -= 1;
627  }
628  else if (keyAttr.flags & NdbRecord::IsVar2ByteLen)
629  {
630  len = uint2korr(src);
631  src += 2;
632  maxlen -= 2;
633  }
634  else
635  {
636  len = maxlen;
637  }
638 
639  const CHARSET_INFO* cs = keyAttr.charset_info;
640  if (cs)
641  {
642  Uint32 xmul = cs->strxfrm_multiply;
643  if (xmul == 0)
644  xmul = 1;
645  /*
646  * Varchar end-spaces are ignored in comparisons. To get same hash
647  * we blank-pad to maximum length via strnxfrm.
648  */
649  Uint32 dstLen = xmul * maxlen;
651  (unsigned char*)pos,
652  dstLen, src,
653  len);
654  if (unlikely(n == -1))
655  goto emalformedstring;
656  len = n;
657  }
658  else
659  {
660  if (keyAttr.flags & NdbRecord::IsVar1ByteLen)
661  {
662  *pos= (unsigned char)len;
663  memcpy(pos+1, src, len);
664  len += 1;
665  }
666  else if (keyAttr.flags & NdbRecord::IsVar2ByteLen)
667  {
668  len += 2;
669  memcpy(pos, src-2, len);
670  }
671  else
672  memcpy(pos, src, len);
673  }
674  while (len & 3)
675  {
676  * (pos + len++) = 0;
677  }
678  pos += len;
679  }
680  len = Uint32(UintPtr(pos) - UintPtr(buf));
681  assert((len & 3) == 0);
682 
683  Uint32 values[4];
684  md5_hash(values, (const Uint64*)buf, len >> 2);
685 
686  if (retval)
687  {
688  * retval = values[1];
689  }
690 
691  if (bufLen == 0)
692  free(buf);
693 
694  return 0;
695 
696 euserdeftable:
697  return 4544;
698 
699 enomem:
700  return 4000;
701 
702 emalformedstring:
703  if (bufLen == 0)
704  free(buf);
705 
706  return 4279;
707 }
708 
710 Ndb::startTransaction(const NdbRecord *keyRec, const char *keyData,
711  void* xfrmbuf, Uint32 xfrmbuflen)
712 {
713  int ret;
714  Uint32 hash;
715  if ((ret = computeHash(&hash, keyRec, keyData, xfrmbuf, xfrmbuflen)) == 0)
716  {
717  return startTransaction(keyRec->table, keyRec->table->getPartitionId(hash));
718  }
719  theError.code = ret;
720  return 0;
721 }
722 
725  const struct Key_part_ptr * keyData,
726  void* buf, Uint32 bufLen)
727 {
728  int ret;
729  Uint32 hash;
730  if ((ret = computeHash(&hash, table, keyData, buf, bufLen)) == 0)
731  {
732  return startTransaction(table, table->getPartitionId(hash));
733  }
734 
735  theError.code = ret;
736  return 0;
737 }
738 
741 {
742  DBUG_ENTER("Ndb::startTransaction");
743  DBUG_PRINT("enter",
744  ("table: %s partitionId: %u", table->getName(), partitionId));
745  if (theInitState == Initialised)
746  {
747  theError.code = 0;
748  checkFailedNode();
749 
750  Uint32 nodeId;
751  const Uint16 *nodes;
752  Uint32 cnt = NdbTableImpl::getImpl(* table).get_nodes(partitionId,
753  &nodes);
754  if(cnt)
755  nodeId= nodes[0];
756  else
757  nodeId= 0;
758 
759  theImpl->incClientStat(TransStartCount, 1);
760 
761  NdbTransaction *trans= startTransactionLocal(0, nodeId, 0);
762  DBUG_PRINT("exit",("start trans: 0x%lx transid: 0x%lx",
763  (long) trans,
764  (long) (trans ? trans->getTransactionId() : 0)));
765  DBUG_RETURN(trans);
766  }
767  DBUG_RETURN(NULL);
768 }
769 
772  const char * keyData, Uint32 keyLen)
773 {
774  DBUG_ENTER("Ndb::startTransaction");
775 
776  if (theInitState == Initialised) {
777  theError.code = 0;
778  checkFailedNode();
784  Uint32 nodeId = 0;
785 
789  if(unlikely(table != 0 && keyData != 0))
790  {
791  NdbTableImpl* impl = &NdbTableImpl::getImpl(*table);
792  Uint32 hashValue;
793  {
794  Uint32 buf[4];
795  const Uint32 MaxKeySizeInLongWords= (NDB_MAX_KEY_SIZE + 7) / 8;
796  Uint64 tmp[ MaxKeySizeInLongWords ];
797 
798  if (keyLen >= sizeof(tmp))
799  {
800  theError.code = 4207;
801  DBUG_RETURN(NULL);
802  }
803  if((UintPtr(keyData) & 7) == 0 && (keyLen & 3) == 0)
804  {
805  md5_hash(buf, (const Uint64*)keyData, keyLen >> 2);
806  }
807  else
808  {
809  tmp[keyLen/8] = 0; // Zero out any 64-bit padding
810  memcpy(tmp, keyData, keyLen);
811  md5_hash(buf, tmp, (keyLen+3) >> 2);
812  }
813  hashValue= buf[1];
814  }
815 
816  const Uint16 *nodes;
817  Uint32 cnt= impl->get_nodes(table->getPartitionId(hashValue), &nodes);
818  if(cnt)
819  {
820  nodeId= nodes[0];
821  }
822  }
823 
824  /* TODO : Should call method above rather than duplicate call to
825  * startTransactionLocal
826  */
827  theImpl->incClientStat( TransStartCount, 1 );
828 
829  {
830  NdbTransaction *trans= startTransactionLocal(0, nodeId, 0);
831  DBUG_PRINT("exit",("start trans: 0x%lx transid: 0x%lx",
832  (long) trans,
833  (long) (trans ? trans->getTransactionId() : 0)));
834  DBUG_RETURN(trans);
835  }
836  } else {
837  DBUG_RETURN(NULL);
838  }//if
839 }//Ndb::startTransaction()
840 
841 /*****************************************************************************
842 NdbTransaction* hupp(NdbTransaction* pBuddyTrans);
843 
844 Return Value: Returns a pointer to a connection object.
845  Connected to the same node as pBuddyTrans
846  and also using the same transction id
847 Remark: Start transaction. Synchronous.
848 *****************************************************************************/
850 Ndb::hupp(NdbTransaction* pBuddyTrans)
851 {
852  DBUG_ENTER("Ndb::hupp");
853 
854  DBUG_PRINT("enter", ("trans: 0x%lx", (long) pBuddyTrans));
855 
856  Uint32 aPriority = 0;
857  if (pBuddyTrans == NULL){
858  DBUG_RETURN(startTransaction());
859  }
860 
861  if (theInitState == Initialised) {
862  theError.code = 0;
863  checkFailedNode();
864 
865  Uint32 nodeId = pBuddyTrans->getConnectedNodeId();
866  NdbTransaction* pCon =
867  startTransactionLocal(aPriority, nodeId,
868  refToInstance(pBuddyTrans->m_tcRef));
869  if(pCon == NULL)
870  DBUG_RETURN(NULL);
871 
872  if (pCon->getConnectedNodeId() != nodeId){
873  // We could not get a connection to the desired node
874  // release the connection and return NULL
875  closeTransaction(pCon);
876  theImpl->decClientStat( TransStartCount, 1 ); /* Correct stats */
877  theError.code = 4006;
878  DBUG_RETURN(NULL);
879  }
880  pCon->setTransactionId(pBuddyTrans->getTransactionId());
881  pCon->setBuddyConPtr((Uint32)pBuddyTrans->getTC_ConnectPtr());
882  DBUG_PRINT("exit", ("hupp trans: 0x%lx transid: 0x%lx",
883  (long) pCon,
884  (long) (pCon ? pCon->getTransactionId() : 0)));
885  DBUG_RETURN(pCon);
886  } else {
887  DBUG_RETURN(NULL);
888  }//if
889 }//Ndb::hupp()
890 
892 Ndb::startTransactionLocal(Uint32 aPriority, Uint32 nodeId, Uint32 instance)
893 {
894 #ifdef VM_TRACE
895  char buf[255];
896  const char* val = NdbEnv_GetEnv("NDB_TRANSACTION_NODE_ID", buf, 255);
897  if(val != 0){
898  nodeId = atoi(val);
899  }
900 #endif
901 
902  DBUG_ENTER("Ndb::startTransactionLocal");
903  DBUG_PRINT("enter", ("nodeid: %d", nodeId));
904 
905  if(unlikely(theRemainingStartTransactions == 0))
906  {
907  theError.code = 4006;
908  DBUG_RETURN(0);
909  }
910 
911  NdbTransaction* tConnection;
912  Uint64 tFirstTransId = theFirstTransId;
913  tConnection = doConnect(nodeId, instance);
914  if (tConnection == NULL) {
915  DBUG_RETURN(NULL);
916  }//if
917 
918  theRemainingStartTransactions--;
919  NdbTransaction* tConNext = theTransactionList;
920  if (tConnection->init())
921  {
922  theError.code = tConnection->theError.code;
923  DBUG_RETURN(NULL);
924  }
925  theTransactionList = tConnection; // into a transaction list.
926  tConnection->next(tConNext); // Add the active connection object
927  tConnection->setTransactionId(tFirstTransId);
928  tConnection->thePriority = aPriority;
929  if ((tFirstTransId & 0xFFFFFFFF) == 0xFFFFFFFF) {
930  //---------------------------------------------------
931 // Transaction id rolling round. We will start from
932 // consecutive identity 0 again.
933 //---------------------------------------------------
934  theFirstTransId = ((tFirstTransId >> 32) << 32);
935  } else {
936  theFirstTransId = tFirstTransId + 1;
937  }//if
938 #ifdef VM_TRACE
939  if (tConnection->theListState != NdbTransaction::NotInList) {
940  printState("startTransactionLocal %lx", (long)tConnection);
941  abort();
942  }
943 #endif
944  DBUG_RETURN(tConnection);
945 }//Ndb::startTransactionLocal()
946 
947 /*****************************************************************************
948 void closeTransaction(NdbTransaction* aConnection);
949 
950 Parameters: aConnection: the connection used in the transaction.
951 Remark: Close transaction by releasing the connection and all operations.
952 *****************************************************************************/
953 void
955 {
956  DBUG_ENTER("Ndb::closeTransaction");
957  NdbTransaction* tCon;
958  NdbTransaction* tPreviousCon;
959 
960  if (aConnection == NULL) {
961 //-----------------------------------------------------
962 // closeTransaction called on NULL pointer, destructive
963 // application behaviour.
964 //-----------------------------------------------------
965 #ifdef VM_TRACE
966  printf("NULL into closeTransaction\n");
967 #endif
968  DBUG_VOID_RETURN;
969  }//if
970  CHECK_STATUS_MACRO_VOID;
971 
972  tCon = theTransactionList;
973  theRemainingStartTransactions++;
974 
975  DBUG_PRINT("info",("close trans: 0x%lx transid: 0x%lx",
976  (long) aConnection,
977  (long) aConnection->getTransactionId()));
978  DBUG_PRINT("info",("magic number: 0x%x TCConPtr: 0x%x theMyRef: 0x%x 0x%x",
979  aConnection->theMagicNumber, aConnection->theTCConPtr,
980  aConnection->theMyRef, getReference()));
981 
982  if (aConnection == tCon) { // Remove the active connection object
983  theTransactionList = tCon->next(); // from the transaction list.
984  } else {
985  while (aConnection != tCon) {
986  if (tCon == NULL) {
987 //-----------------------------------------------------
988 // closeTransaction called on non-existing transaction
989 //-----------------------------------------------------
990 
991  if(aConnection->theError.code == 4008){
997 #ifdef VM_TRACE
998  printf("Scan timeout:ed NdbTransaction-> "
999  "not returning it-> memory leak\n");
1000 #endif
1001  DBUG_VOID_RETURN;
1002  }
1003 
1004 #ifdef VM_TRACE
1005  printf("Non-existing transaction into closeTransaction\n");
1006  abort();
1007 #endif
1008  DBUG_VOID_RETURN;
1009  }//if
1010  tPreviousCon = tCon;
1011  tCon = tCon->next();
1012  }//while
1013  tPreviousCon->next(tCon->next());
1014  }//if
1015 
1016  aConnection->release();
1017 
1018  theImpl->incClientStat(TransCloseCount, 1);
1019 
1020  if(aConnection->theError.code == 4008){
1026 #ifdef VM_TRACE
1027  printf("Con timeout:ed NdbTransaction-> not returning it-> memory leak\n");
1028 #endif
1029  DBUG_VOID_RETURN;
1030  }
1031 
1035  Uint32 nodeId = aConnection->getConnectedNodeId();
1036  Uint32 seq = theImpl->getNodeSequence(nodeId);
1037  if (aConnection->theNodeSequence != seq)
1038  {
1039  aConnection->theReleaseOnClose = true;
1040  }
1041 
1042  if (aConnection->theReleaseOnClose == false)
1043  {
1047  aConnection->theNext = theConnectionArray[nodeId];
1048  theConnectionArray[nodeId] = aConnection;
1049  DBUG_VOID_RETURN;
1050  } else {
1051  aConnection->theReleaseOnClose = false;
1052  releaseNdbCon(aConnection);
1053  }//if
1054  DBUG_VOID_RETURN;
1055 }//Ndb::closeTransaction()
1056 
1057 /****************************************************************************
1058 int getBlockNumber(void);
1059 
1060 Remark:
1061 ****************************************************************************/
1062 int
1063 Ndb::getBlockNumber()
1064 {
1065  return theNdbBlockNumber;
1066 }
1067 
1070  return theDictionary;
1071 }
1072 
1073 /****************************************************************************
1074 int getNodeId();
1075 
1076 Remark:
1077 ****************************************************************************/
1078 int
1080 {
1081  return theNode;
1082 }
1083 
1084 /****************************************************************************
1085 Uint64 getAutoIncrementValue( const char* aTableName,
1086  Uint64 & autoValue,
1087  Uint32 cacheSize,
1088  Uint64 step,
1089  Uint64 start);
1090 
1091 Parameters: aTableName (IN) : The table name.
1092  autoValue (OUT) : Returns new autoincrement value
1093  cacheSize (IN) : Prefetch this many values
1094  step (IN) : Specifies the step between the
1095  autoincrement values.
1096  start (IN) : Start value for first value
1097 Returns: 0 if succesful, -1 if error encountered
1098 Remark: Returns a new autoincrement value to the application.
1099  The autoincrement values can be increased by steps
1100  (default 1) and a number of values can be prefetched
1101  by specifying cacheSize (default 10).
1102 ****************************************************************************/
1103 int
1104 Ndb::getAutoIncrementValue(const char* aTableName,
1105  Uint64 & autoValue, Uint32 cacheSize,
1106  Uint64 step, Uint64 start)
1107 {
1108  DBUG_ENTER("Ndb::getAutoIncrementValue");
1109  ASSERT_NOT_MYSQLD;
1110  BaseString internal_tabname(internalize_table_name(aTableName));
1111 
1112  Ndb_local_table_info *info=
1113  theDictionary->get_local_table_info(internal_tabname);
1114  if (info == 0) {
1115  theError.code = theDictionary->getNdbError().code;
1116  DBUG_RETURN(-1);
1117  }
1118  const NdbTableImpl* table = info->m_table_impl;
1119  TupleIdRange & range = info->m_tuple_id_range;
1120  if (getTupleIdFromNdb(table, range, autoValue, cacheSize, step, start) == -1)
1121  DBUG_RETURN(-1);
1122  DBUG_PRINT("info", ("value %lu", (ulong) autoValue));
1123  DBUG_RETURN(0);
1124 }
1125 
1126 int
1127 Ndb::getAutoIncrementValue(const NdbDictionary::Table * aTable,
1128  Uint64 & autoValue, Uint32 cacheSize,
1129  Uint64 step, Uint64 start)
1130 {
1131  DBUG_ENTER("Ndb::getAutoIncrementValue");
1132  ASSERT_NOT_MYSQLD;
1133  assert(aTable != 0);
1134  const NdbTableImpl* table = & NdbTableImpl::getImpl(*aTable);
1135  const BaseString& internal_tabname = table->m_internalName;
1136 
1137  Ndb_local_table_info *info=
1138  theDictionary->get_local_table_info(internal_tabname);
1139  if (info == 0) {
1140  theError.code = theDictionary->getNdbError().code;
1141  DBUG_RETURN(-1);
1142  }
1143  TupleIdRange & range = info->m_tuple_id_range;
1144  if (getTupleIdFromNdb(table, range, autoValue, cacheSize, step, start) == -1)
1145  DBUG_RETURN(-1);
1146  DBUG_PRINT("info", ("value %lu", (ulong)autoValue));
1147  DBUG_RETURN(0);
1148 }
1149 
1150 int
1151 Ndb::getAutoIncrementValue(const NdbDictionary::Table * aTable,
1152  TupleIdRange & range, Uint64 & autoValue,
1153  Uint32 cacheSize, Uint64 step, Uint64 start)
1154 {
1155  DBUG_ENTER("Ndb::getAutoIncrementValue");
1156  assert(aTable != 0);
1157  const NdbTableImpl* table = & NdbTableImpl::getImpl(*aTable);
1158 
1159  if (getTupleIdFromNdb(table, range, autoValue, cacheSize, step, start) == -1)
1160  DBUG_RETURN(-1);
1161  DBUG_PRINT("info", ("value %lu", (ulong)autoValue));
1162  DBUG_RETURN(0);
1163 }
1164 
1165 int
1166 Ndb::getTupleIdFromNdb(const NdbTableImpl* table,
1167  TupleIdRange & range, Uint64 & tupleId,
1168  Uint32 cacheSize, Uint64 step, Uint64 start)
1169 {
1170 /*
1171  Returns a new TupleId to the application.
1172  The TupleId comes from SYSTAB_0 where SYSKEY_0 = TableId.
1173  It is initialized to (TableId << 48) + 1 in NdbcntrMain.cpp.
1174  In most cases step= start= 1, in which case we get:
1175  1,2,3,4,5,...
1176  If step=10 and start=5 and first number is 1, we get:
1177  5,15,25,35,...
1178 */
1179  DBUG_ENTER("Ndb::getTupleIdFromNdb");
1180  /*
1181  Check if the next value can be taken from the pre-fetched
1182  sequence.
1183  */
1184  if (range.m_first_tuple_id != range.m_last_tuple_id &&
1185  range.m_first_tuple_id + step <= range.m_last_tuple_id)
1186  {
1187  assert(range.m_first_tuple_id < range.m_last_tuple_id);
1188  range.m_first_tuple_id += step;
1189  tupleId = range.m_first_tuple_id;
1190  DBUG_PRINT("info", ("Next cached value %lu", (ulong) tupleId));
1191  }
1192  else
1193  {
1194  /*
1195  If start value is greater than step it is ignored
1196  */
1197  Uint64 offset = (start > step) ? 1 : start;
1198 
1199  /*
1200  Pre-fetch a number of values depending on cacheSize
1201  */
1202  if (cacheSize == 0)
1203  cacheSize = 1;
1204 
1205  DBUG_PRINT("info", ("reading %u values from database", (uint)cacheSize));
1206  /*
1207  * reserve next cacheSize entries in db. adds cacheSize to NEXTID
1208  * and returns first tupleId in the new range. If tupleId's are
1209  * incremented in steps then multiply the cacheSize with step size.
1210  */
1211  Uint64 opValue = cacheSize * step;
1212 
1213  if (opTupleIdOnNdb(table, range, opValue, 0) == -1)
1214  DBUG_RETURN(-1);
1215  DBUG_PRINT("info", ("Next value fetched from database %lu", (ulong) opValue));
1216  DBUG_PRINT("info", ("Increasing %lu by offset %lu, increment is %lu", (ulong) (ulong) opValue, (ulong) offset, (ulong) step));
1217  Uint64 current, next;
1218  Uint64 div = ((Uint64) (opValue + step - offset)) / step;
1219  next = div * step + offset;
1220  current = (next < step) ? next : next - step;
1221  tupleId = (opValue <= current) ? current : next;
1222  DBUG_PRINT("info", ("Returning %lu", (ulong) tupleId));
1223  range.m_first_tuple_id = tupleId;
1224  }
1225  DBUG_RETURN(0);
1226 }
1227 
1228 /****************************************************************************
1229 int readAutoIncrementValue( const char* aTableName,
1230  Uint64 & autoValue);
1231 
1232 Parameters: aTableName (IN) : The table name.
1233  autoValue (OUT) : The current autoincrement value
1234 Returns: 0 if succesful, -1 if error encountered
1235 Remark: Returns the current autoincrement value to the application.
1236 ****************************************************************************/
1237 int
1238 Ndb::readAutoIncrementValue(const char* aTableName,
1239  Uint64 & autoValue)
1240 {
1241  DBUG_ENTER("Ndb::readAutoIncrementValue");
1242  ASSERT_NOT_MYSQLD;
1243  BaseString internal_tabname(internalize_table_name(aTableName));
1244 
1245  Ndb_local_table_info *info=
1246  theDictionary->get_local_table_info(internal_tabname);
1247  if (info == 0) {
1248  theError.code = theDictionary->getNdbError().code;
1249  DBUG_RETURN(-1);
1250  }
1251  const NdbTableImpl* table = info->m_table_impl;
1252  TupleIdRange & range = info->m_tuple_id_range;
1253  if (readTupleIdFromNdb(table, range, autoValue) == -1)
1254  DBUG_RETURN(-1);
1255  DBUG_PRINT("info", ("value %lu", (ulong)autoValue));
1256  DBUG_RETURN(0);
1257 }
1258 
1259 int
1260 Ndb::readAutoIncrementValue(const NdbDictionary::Table * aTable,
1261  Uint64 & autoValue)
1262 {
1263  DBUG_ENTER("Ndb::readAutoIncrementValue");
1264  ASSERT_NOT_MYSQLD;
1265  assert(aTable != 0);
1266  const NdbTableImpl* table = & NdbTableImpl::getImpl(*aTable);
1267  const BaseString& internal_tabname = table->m_internalName;
1268 
1269  Ndb_local_table_info *info=
1270  theDictionary->get_local_table_info(internal_tabname);
1271  if (info == 0) {
1272  theError.code = theDictionary->getNdbError().code;
1273  DBUG_RETURN(-1);
1274  }
1275  TupleIdRange & range = info->m_tuple_id_range;
1276  if (readTupleIdFromNdb(table, range, autoValue) == -1)
1277  DBUG_RETURN(-1);
1278  DBUG_PRINT("info", ("value %lu", (ulong)autoValue));
1279  DBUG_RETURN(0);
1280 }
1281 
1282 int
1283 Ndb::readAutoIncrementValue(const NdbDictionary::Table * aTable,
1284  TupleIdRange & range, Uint64 & autoValue)
1285 {
1286  DBUG_ENTER("Ndb::readAutoIncrementValue");
1287  assert(aTable != 0);
1288  const NdbTableImpl* table = & NdbTableImpl::getImpl(*aTable);
1289 
1290  if (readTupleIdFromNdb(table, range, autoValue) == -1)
1291  DBUG_RETURN(-1);
1292  DBUG_PRINT("info", ("value %lu", (ulong)autoValue));
1293  DBUG_RETURN(0);
1294 }
1295 
1296 int
1297 Ndb::readTupleIdFromNdb(const NdbTableImpl* table,
1298  TupleIdRange & range, Uint64 & tupleId)
1299 {
1300  DBUG_ENTER("Ndb::readTupleIdFromNdb");
1301  if (range.m_first_tuple_id != range.m_last_tuple_id)
1302  {
1303  assert(range.m_first_tuple_id < range.m_last_tuple_id);
1304  tupleId = range.m_first_tuple_id + 1;
1305  }
1306  else
1307  {
1308  /*
1309  * peek at NEXTID. does not reserve it so the value is valid
1310  * only if no other transactions are allowed.
1311  */
1312  Uint64 opValue = 0;
1313  if (opTupleIdOnNdb(table, range, opValue, 3) == -1)
1314  DBUG_RETURN(-1);
1315  tupleId = opValue;
1316  }
1317  DBUG_RETURN(0);
1318 }
1319 
1320 /****************************************************************************
1321 int setAutoIncrementValue( const char* aTableName,
1322  Uint64 autoValue,
1323  bool modify);
1324 
1325 Parameters: aTableName (IN) : The table name.
1326  autoValue (IN) : The new autoincrement value
1327  modify (IN) : Modify existing value (not initialization)
1328 Returns: 0 if succesful, -1 if error encountered
1329 Remark: Sets a new autoincrement value for the application.
1330 ****************************************************************************/
1331 int
1332 Ndb::setAutoIncrementValue(const char* aTableName,
1333  Uint64 autoValue, bool modify)
1334 {
1335  DBUG_ENTER("Ndb::setAutoIncrementValue");
1336  ASSERT_NOT_MYSQLD;
1337  BaseString internal_tabname(internalize_table_name(aTableName));
1338 
1339  Ndb_local_table_info *info=
1340  theDictionary->get_local_table_info(internal_tabname);
1341  if (info == 0) {
1342  theError.code = theDictionary->getNdbError().code;
1343  DBUG_RETURN(-1);
1344  }
1345  const NdbTableImpl* table = info->m_table_impl;
1346  TupleIdRange & range = info->m_tuple_id_range;
1347  if (setTupleIdInNdb(table, range, autoValue, modify) == -1)
1348  DBUG_RETURN(-1);
1349  DBUG_RETURN(0);
1350 }
1351 
1352 int
1353 Ndb::setAutoIncrementValue(const NdbDictionary::Table * aTable,
1354  Uint64 autoValue, bool modify)
1355 {
1356  DBUG_ENTER("Ndb::setAutoIncrementValue");
1357  ASSERT_NOT_MYSQLD;
1358  assert(aTable != 0);
1359  const NdbTableImpl* table = & NdbTableImpl::getImpl(*aTable);
1360  const BaseString& internal_tabname = table->m_internalName;
1361 
1362  Ndb_local_table_info *info=
1363  theDictionary->get_local_table_info(internal_tabname);
1364  if (info == 0) {
1365  theError.code = theDictionary->getNdbError().code;
1366  DBUG_RETURN(-1);
1367  }
1368  TupleIdRange & range = info->m_tuple_id_range;
1369  if (setTupleIdInNdb(table, range, autoValue, modify) == -1)
1370  DBUG_RETURN(-1);
1371  DBUG_RETURN(0);
1372 }
1373 
1374 int
1375 Ndb::setAutoIncrementValue(const NdbDictionary::Table * aTable,
1376  TupleIdRange & range, Uint64 autoValue,
1377  bool modify)
1378 {
1379  DBUG_ENTER("Ndb::setAutoIncrementValue");
1380  assert(aTable != 0);
1381  const NdbTableImpl* table = & NdbTableImpl::getImpl(*aTable);
1382 
1383  if (setTupleIdInNdb(table, range, autoValue, modify) == -1)
1384  DBUG_RETURN(-1);
1385  DBUG_RETURN(0);
1386 }
1387 
1388 int
1389 Ndb::setTupleIdInNdb(const NdbTableImpl* table,
1390  TupleIdRange & range, Uint64 tupleId, bool modify)
1391 {
1392  DBUG_ENTER("Ndb::setTupleIdInNdb");
1393  if (modify)
1394  {
1395  if (checkTupleIdInNdb(range, tupleId))
1396  {
1397  if (range.m_first_tuple_id != range.m_last_tuple_id)
1398  {
1399  assert(range.m_first_tuple_id < range.m_last_tuple_id);
1400  if (tupleId <= range.m_first_tuple_id + 1)
1401  DBUG_RETURN(0);
1402  if (tupleId <= range.m_last_tuple_id)
1403  {
1404  range.m_first_tuple_id = tupleId - 1;
1405  DBUG_PRINT("info",
1406  ("Setting next auto increment cached value to %lu",
1407  (ulong)tupleId));
1408  DBUG_RETURN(0);
1409  }
1410  }
1411  /*
1412  * if tupleId <= NEXTID, do nothing. otherwise update NEXTID to
1413  * tupleId and set cached range to first = last = tupleId - 1.
1414  */
1415  if (opTupleIdOnNdb(table, range, tupleId, 2) == -1)
1416  DBUG_RETURN(-1);
1417  }
1418  }
1419  else
1420  {
1421  /*
1422  * update NEXTID to given value. reset cached range.
1423  */
1424  if (opTupleIdOnNdb(table, range, tupleId, 1) == -1)
1425  DBUG_RETURN(-1);
1426  }
1427  DBUG_RETURN(0);
1428 }
1429 
1430 int Ndb::initAutoIncrement()
1431 {
1432  if (m_sys_tab_0)
1433  return 0;
1434 
1435  BaseString currentDb(getDatabaseName());
1436  BaseString currentSchema(getDatabaseSchemaName());
1437 
1438  setDatabaseName("sys");
1439  setDatabaseSchemaName("def");
1440 
1441  m_sys_tab_0 = theDictionary->getTableGlobal("SYSTAB_0");
1442 
1443  // Restore current name space
1444  setDatabaseName(currentDb.c_str());
1445  setDatabaseSchemaName(currentSchema.c_str());
1446 
1447  if (m_sys_tab_0 == NULL) {
1448  assert(theDictionary->m_error.code != 0);
1449  theError.code = theDictionary->m_error.code;
1450  return -1;
1451  }
1452 
1453  return 0;
1454 }
1455 
1456 bool
1457 Ndb::checkUpdateAutoIncrementValue(TupleIdRange & range, Uint64 autoValue)
1458 {
1459  return(checkTupleIdInNdb(range, autoValue) != 0);
1460 }
1461 
1462 int
1463 Ndb::checkTupleIdInNdb(TupleIdRange & range, Uint64 tupleId)
1464 {
1465  DBUG_ENTER("Ndb::checkTupleIdIndNdb");
1466  if ((range.m_first_tuple_id != ~(Uint64)0) &&
1467  (range.m_first_tuple_id > tupleId))
1468  {
1469  /*
1470  * If we have ever cached a value in this object and this cached
1471  * value is larger than the value we're trying to set then we
1472  * need not check with the real value in the SYSTAB_0 table.
1473  */
1474  DBUG_RETURN(0);
1475  }
1476  if (range.m_highest_seen > tupleId)
1477  {
1478  /*
1479  * Although we've never cached any higher value we have read
1480  * a higher value and again it isn't necessary to change the
1481  * auto increment value.
1482  */
1483  DBUG_RETURN(0);
1484  }
1485  DBUG_RETURN(1);
1486 }
1487 
1488 
1489 int
1490 Ndb::opTupleIdOnNdb(const NdbTableImpl* table,
1491  TupleIdRange & range, Uint64 & opValue, Uint32 op)
1492 {
1493  DBUG_ENTER("Ndb::opTupleIdOnNdb");
1494  Uint32 aTableId = table->m_id;
1495  DBUG_PRINT("enter", ("table: %u value: %lu op: %u",
1496  aTableId, (ulong) opValue, op));
1497 
1498  NdbTransaction* tConnection = NULL;
1499  NdbOperation* tOperation = NULL;
1500  Uint64 tValue;
1501  NdbRecAttr* tRecAttrResult;
1502 
1503  CHECK_STATUS_MACRO;
1504 
1505  if (initAutoIncrement() == -1)
1506  goto error_handler;
1507 
1508  // Start transaction with table id as hint
1509  tConnection = this->startTransaction(m_sys_tab_0,
1510  (const char *) &aTableId,
1511  sizeof(Uint32));
1512 
1513  if (tConnection == NULL)
1514  goto error_handler;
1515 
1516  tOperation = tConnection->getNdbOperation(m_sys_tab_0);
1517  if (tOperation == NULL)
1518  goto error_handler;
1519 
1520  switch (op)
1521  {
1522  case 0:
1523  tOperation->interpretedUpdateTuple();
1524  tOperation->equal("SYSKEY_0", aTableId);
1525  tOperation->incValue("NEXTID", opValue);
1526  tRecAttrResult = tOperation->getValue("NEXTID");
1527 
1528  if (tConnection->execute( NdbTransaction::Commit ) == -1 )
1529  goto error_handler;
1530 
1531  tValue = tRecAttrResult->u_64_value();
1532 
1533  range.m_first_tuple_id = tValue - opValue;
1534  range.m_last_tuple_id = tValue - 1;
1535  opValue = range.m_first_tuple_id; // out
1536  break;
1537  case 1:
1538  // create on first use
1539  tOperation->writeTuple();
1540  tOperation->equal("SYSKEY_0", aTableId );
1541  tOperation->setValue("NEXTID", opValue);
1542 
1543  if (tConnection->execute( NdbTransaction::Commit ) == -1 )
1544  goto error_handler;
1545 
1546  range.reset();
1547  break;
1548  case 2:
1549  tOperation->interpretedUpdateTuple();
1550  tOperation->equal("SYSKEY_0", aTableId );
1551  tOperation->load_const_u64(1, opValue);
1552  tOperation->read_attr("NEXTID", 2);
1553  // compare NEXTID >= opValue
1554  tOperation->branch_le(2, 1, 0);
1555  tOperation->write_attr("NEXTID", 1);
1556  tOperation->interpret_exit_ok();
1557  tOperation->def_label(0);
1558  tOperation->interpret_exit_ok();
1559  tRecAttrResult = tOperation->getValue("NEXTID");
1560  if (tConnection->execute( NdbTransaction::Commit ) == -1)
1561  {
1562  goto error_handler;
1563  }
1564  else
1565  {
1566  range.m_highest_seen = tRecAttrResult->u_64_value();
1567  DBUG_PRINT("info",
1568  ("Setting next auto increment value (db) to %lu",
1569  (ulong) opValue));
1570  range.m_first_tuple_id = range.m_last_tuple_id = opValue - 1;
1571  }
1572  break;
1573  case 3:
1574  tOperation->readTuple();
1575  tOperation->equal("SYSKEY_0", aTableId );
1576  tRecAttrResult = tOperation->getValue("NEXTID");
1577  if (tConnection->execute( NdbTransaction::Commit ) == -1 )
1578  goto error_handler;
1579  range.m_highest_seen = opValue = tRecAttrResult->u_64_value(); // out
1580  break;
1581  default:
1582  goto error_handler;
1583  }
1584 
1585  this->closeTransaction(tConnection);
1586 
1587  DBUG_RETURN(0);
1588 
1589 error_handler:
1590  DBUG_PRINT("error", ("ndb=%d con=%d op=%d",
1591  theError.code,
1592  tConnection != NULL ? tConnection->theError.code : -1,
1593  tOperation != NULL ? tOperation->theError.code : -1));
1594 
1595  if (theError.code == 0 && tConnection != NULL)
1596  theError.code = tConnection->theError.code;
1597  if (theError.code == 0 && tOperation != NULL)
1598  theError.code = tOperation->theError.code;
1599  DBUG_ASSERT(theError.code != 0);
1600 
1601  NdbError savedError;
1602  savedError = theError;
1603 
1604  if (tConnection != NULL)
1605  this->closeTransaction(tConnection);
1606 
1607  theError = savedError;
1608 
1609  DBUG_RETURN(-1);
1610 }
1611 
1612 Uint32
1613 convertEndian(Uint32 Data)
1614 {
1615 #ifdef WORDS_BIGENDIAN
1616  Uint32 t1, t2, t3, t4;
1617  t4 = (Data >> 24) & 255;
1618  t3 = (Data >> 16) & 255;
1619  t4 = t4 + (t3 << 8);
1620  t2 = (Data >> 8) & 255;
1621  t4 = t4 + (t2 << 16);
1622  t1 = Data & 255;
1623  t4 = t4 + (t1 << 24);
1624  return t4;
1625 #else
1626  return Data;
1627 #endif
1628 }
1629 
1630 // <internal>
1633 {
1634  return theImpl->m_ndb_cluster_connection;
1635 }
1636 
1637 const char * Ndb::getCatalogName() const
1638 {
1639  return theImpl->m_dbname.c_str();
1640 }
1641 
1642 int Ndb::setCatalogName(const char * a_catalog_name)
1643 {
1644  // TODO can table_name_separator be escaped?
1645  if (a_catalog_name && ! strchr(a_catalog_name, table_name_separator)) {
1646  if (!theImpl->m_dbname.assign(a_catalog_name) ||
1647  theImpl->update_prefix())
1648  {
1649  theError.code = 4000;
1650  return -1;
1651  }
1652  }
1653  return 0;
1654 }
1655 
1656 const char * Ndb::getSchemaName() const
1657 {
1658  return theImpl->m_schemaname.c_str();
1659 }
1660 
1661 int Ndb::setSchemaName(const char * a_schema_name)
1662 {
1663  // TODO can table_name_separator be escaped?
1664  if (a_schema_name && ! strchr(a_schema_name, table_name_separator)) {
1665  if (!theImpl->m_schemaname.assign(a_schema_name) ||
1666  theImpl->update_prefix())
1667  {
1668  theError.code = 4000;
1669  return -1;
1670  }
1671  }
1672  return 0;
1673 }
1674 // </internal>
1675 
1676 const char * Ndb::getDatabaseName() const
1677 {
1678  return getCatalogName();
1679 }
1680 
1681 int Ndb::setDatabaseName(const char * a_catalog_name)
1682 {
1683  return setCatalogName(a_catalog_name);
1684 }
1685 
1686 const char * Ndb::getDatabaseSchemaName() const
1687 {
1688  return getSchemaName();
1689 }
1690 
1691 int Ndb::setDatabaseSchemaName(const char * a_schema_name)
1692 {
1693  return setSchemaName(a_schema_name);
1694 }
1695 
1697 {
1698  const char* s0 = t->m_impl.m_internalName.c_str();
1699  const char* s1 = strchr(s0, table_name_separator);
1700  if (s1 && s1 != s0) {
1701  const char* s2 = strchr(s1 + 1, table_name_separator);
1702  if (s2 && s2 != s1 + 1) {
1703  char buf[NAME_LEN + 1];
1704  if (s1 - s0 <= NAME_LEN && s2 - (s1 + 1) <= NAME_LEN) {
1705  sprintf(buf, "%.*s", (int) (s1 - s0), s0);
1706  setDatabaseName(buf);
1707  sprintf(buf, "%.*s", (int) (s2 - (s1 + 1)), s1 + 1);
1708  setDatabaseSchemaName(buf);
1709 #ifdef VM_TRACE
1710  // verify that m_prefix looks like abc/def/
1711  const char* s0 = theImpl->m_prefix.c_str();
1712  const char* s1 = s0 ? strchr(s0, table_name_separator) : 0;
1713  const char* s2 = s1 ? strchr(s1 + 1, table_name_separator) : 0;
1714  if (!(s1 && s1 != s0 && s2 && s2 != s1 + 1 && *(s2 + 1) == 0))
1715  {
1716  ndbout_c("t->m_impl.m_internalName.c_str(): %s", t->m_impl.m_internalName.c_str());
1717  ndbout_c("s0: %s", s0);
1718  ndbout_c("s1: %s", s1);
1719  ndbout_c("s2: %s", s2);
1720  assert(s1 && s1 != s0 && s2 && s2 != s1 + 1 && *(s2 + 1) == 0);
1721  }
1722 #endif
1723  return 0;
1724  }
1725  }
1726  }
1727  return -1;
1728 }
1729 
1730 bool Ndb::usingFullyQualifiedNames()
1731 {
1732  return fullyQualifiedNames;
1733 }
1734 
1735 const char *
1736 Ndb::externalizeTableName(const char * internalTableName, bool fullyQualifiedNames)
1737 {
1738  if (fullyQualifiedNames) {
1739  register const char *ptr = internalTableName;
1740 
1741  // Skip database name
1742  while (*ptr && *ptr++ != table_name_separator);
1743  // Skip schema name
1744  while (*ptr && *ptr++ != table_name_separator);
1745  return ptr;
1746  }
1747  else
1748  return internalTableName;
1749 }
1750 
1751 const char *
1752 Ndb::externalizeTableName(const char * internalTableName)
1753 {
1754  return externalizeTableName(internalTableName, usingFullyQualifiedNames());
1755 }
1756 
1757 const char *
1758 Ndb::externalizeIndexName(const char * internalIndexName, bool fullyQualifiedNames)
1759 {
1760  if (fullyQualifiedNames) {
1761  register const char *ptr = internalIndexName;
1762 
1763  // Scan name from the end
1764  while (*ptr++); ptr--; // strend
1765  while (ptr >= internalIndexName && *ptr != table_name_separator)
1766  ptr--;
1767 
1768  return ptr + 1;
1769  }
1770  else
1771  return internalIndexName;
1772 }
1773 
1774 const char *
1775 Ndb::externalizeIndexName(const char * internalIndexName)
1776 {
1777  return externalizeIndexName(internalIndexName, usingFullyQualifiedNames());
1778 }
1779 
1780 
1781 const BaseString
1782 Ndb::internalize_table_name(const char *external_name) const
1783 {
1784  BaseString ret;
1785  DBUG_ENTER("internalize_table_name");
1786  DBUG_PRINT("enter", ("external_name: %s", external_name));
1787 
1788  if (fullyQualifiedNames)
1789  {
1790  /* Internal table name format <db>/<schema>/<table>
1791  <db>/<schema>/ is already available in m_prefix
1792  so just concat the two strings
1793  */
1794 #ifdef VM_TRACE
1795  // verify that m_prefix looks like abc/def/
1796  const char* s0 = theImpl->m_prefix.c_str();
1797  const char* s1 = s0 ? strchr(s0, table_name_separator) : 0;
1798  const char* s2 = s1 ? strchr(s1 + 1, table_name_separator) : 0;
1799  if (!(s1 && s1 != s0 && s2 && s2 != s1 + 1 && *(s2 + 1) == 0))
1800  {
1801  ndbout_c("s0: %s", s0);
1802  ndbout_c("s1: %s", s1);
1803  ndbout_c("s2: %s", s2);
1804  assert(s1 && s1 != s0 && s2 && s2 != s1 + 1 && *(s2 + 1) == 0);
1805  }
1806 #endif
1807  ret.assfmt("%s%s",
1808  theImpl->m_prefix.c_str(),
1809  external_name);
1810  }
1811  else
1812  ret.assign(external_name);
1813 
1814  DBUG_PRINT("exit", ("internal_name: %s", ret.c_str()));
1815  DBUG_RETURN(ret);
1816 }
1817 
1818 const BaseString
1819 Ndb::old_internalize_index_name(const NdbTableImpl * table,
1820  const char * external_name) const
1821 {
1822  BaseString ret;
1823  DBUG_ENTER("old_internalize_index_name");
1824  DBUG_PRINT("enter", ("external_name: %s, table_id: %d",
1825  external_name, table ? table->m_id : ~0));
1826  if (!table)
1827  {
1828  DBUG_PRINT("error", ("!table"));
1829  DBUG_RETURN(ret);
1830  }
1831 
1832  if (fullyQualifiedNames)
1833  {
1834  /* Internal index name format <db>/<schema>/<tabid>/<table> */
1835  ret.assfmt("%s%d%c%s",
1836  theImpl->m_prefix.c_str(),
1837  table->m_id,
1838  table_name_separator,
1839  external_name);
1840  }
1841  else
1842  ret.assign(external_name);
1843 
1844  DBUG_PRINT("exit", ("internal_name: %s", ret.c_str()));
1845  DBUG_RETURN(ret);
1846 }
1847 
1848 const BaseString
1849 Ndb::internalize_index_name(const NdbTableImpl * table,
1850  const char * external_name) const
1851 {
1852  BaseString ret;
1853  DBUG_ENTER("internalize_index_name");
1854  DBUG_PRINT("enter", ("external_name: %s, table_id: %d",
1855  external_name, table ? table->m_id : ~0));
1856  if (!table)
1857  {
1858  DBUG_PRINT("error", ("!table"));
1859  DBUG_RETURN(ret);
1860  }
1861 
1862  if (fullyQualifiedNames)
1863  {
1864  /* Internal index name format sys/def/<tabid>/<table> */
1865  ret.assfmt("%s%d%c%s",
1866  theImpl->m_systemPrefix.c_str(),
1867  table->m_id,
1868  table_name_separator,
1869  external_name);
1870  }
1871  else
1872  ret.assign(external_name);
1873 
1874  DBUG_PRINT("exit", ("internal_name: %s", ret.c_str()));
1875  DBUG_RETURN(ret);
1876 }
1877 
1878 
1879 const BaseString
1880 Ndb::getDatabaseFromInternalName(const char * internalName)
1881 {
1882  char * databaseName = new char[strlen(internalName) + 1];
1883  if (databaseName == NULL)
1884  {
1885  errno = ENOMEM;
1886  return BaseString(NULL);
1887  }
1888  strcpy(databaseName, internalName);
1889  register char *ptr = databaseName;
1890 
1891  /* Scan name for the first table_name_separator */
1892  while (*ptr && *ptr != table_name_separator)
1893  ptr++;
1894  *ptr = '\0';
1895  BaseString ret = BaseString(databaseName);
1896  delete [] databaseName;
1897  return ret;
1898 }
1899 
1900 const BaseString
1901 Ndb::getSchemaFromInternalName(const char * internalName)
1902 {
1903  char * schemaName = new char[strlen(internalName)];
1904  if (schemaName == NULL)
1905  {
1906  errno = ENOMEM;
1907  return BaseString(NULL);
1908  }
1909  register const char *ptr1 = internalName;
1910 
1911  /* Scan name for the second table_name_separator */
1912  while (*ptr1 && *ptr1 != table_name_separator)
1913  ptr1++;
1914  strcpy(schemaName, ptr1 + 1);
1915  register char *ptr = schemaName;
1916  while (*ptr && *ptr != table_name_separator)
1917  ptr++;
1918  *ptr = '\0';
1919  BaseString ret = BaseString(schemaName);
1920  delete [] schemaName;
1921  return ret;
1922 }
1923 
1924 // ToDo set event buffer size
1926 {
1927  DBUG_ENTER("Ndb::createEventOperation");
1928  NdbEventOperation* tOp= theEventBuffer->createEventOperation(eventName,
1929  theError);
1930  if (tOp)
1931  {
1932  // keep track of all event operations
1934  NdbEventBuffer::getEventOperationImpl(tOp);
1935  op->m_next= theImpl->m_ev_op;
1936  op->m_prev= 0;
1937  theImpl->m_ev_op= op;
1938  if (op->m_next)
1939  op->m_next->m_prev= op;
1940  }
1941 
1942  DBUG_RETURN(tOp);
1943 }
1944 
1946 {
1947  DBUG_ENTER("Ndb::dropEventOperation");
1948  DBUG_PRINT("info", ("name: %s", tOp->getEvent()->getTable()->getName()));
1949  // remove it from list
1950 
1951  theEventBuffer->dropEventOperation(tOp);
1952  DBUG_RETURN(0);
1953 }
1954 
1955 NdbEventOperation *Ndb::getEventOperation(NdbEventOperation* tOp)
1956 {
1958  if (tOp)
1959  op= NdbEventBuffer::getEventOperationImpl(tOp)->m_next;
1960  else
1961  op= theImpl->m_ev_op;
1962  if (op)
1963  return op->m_facade;
1964  return 0;
1965 }
1966 
1967 int
1968 Ndb::pollEvents(int aMillisecondNumber, Uint64 *latestGCI)
1969 {
1970  return theEventBuffer->pollEvents(aMillisecondNumber, latestGCI);
1971 }
1972 
1973 int
1974 Ndb::flushIncompleteEvents(Uint64 gci)
1975 {
1976  theEventBuffer->lock();
1977  int ret = theEventBuffer->flushIncompleteEvents(gci);
1978  theEventBuffer->unlock();
1979  return ret;
1980 }
1981 
1983 {
1984  return theEventBuffer->nextEvent();
1985 }
1986 
1987 bool
1988 Ndb::isConsistent(Uint64& gci)
1989 {
1990  return theEventBuffer->isConsistent(gci);
1991 }
1992 
1993 bool
1995 {
1996  return theEventBuffer->isConsistentGCI(gci);
1997 }
1998 
1999 const NdbEventOperation*
2000 Ndb::getGCIEventOperations(Uint32* iter, Uint32* event_types)
2001 {
2002  NdbEventOperationImpl* op =
2003  theEventBuffer->getGCIEventOperations(iter, event_types);
2004  if (op != NULL)
2005  return op->m_facade;
2006  return NULL;
2007 }
2008 
2009 Uint64 Ndb::getLatestGCI()
2010 {
2011  return theEventBuffer->getLatestGCI();
2012 }
2013 
2014 void Ndb::setReportThreshEventGCISlip(unsigned thresh)
2015 {
2016  if (theEventBuffer->m_gci_slip_thresh != thresh)
2017  {
2018  theEventBuffer->m_gci_slip_thresh= thresh;
2019  }
2020 }
2021 
2022 void Ndb::setReportThreshEventFreeMem(unsigned thresh)
2023 {
2024  if (theEventBuffer->m_free_thresh != thresh)
2025  {
2026  theEventBuffer->m_free_thresh= thresh;
2027  theEventBuffer->m_min_free_thresh= thresh;
2028  theEventBuffer->m_max_free_thresh= 100;
2029  }
2030 }
2031 
2032 Uint64 Ndb::allocate_transaction_id()
2033 {
2034  Uint64 ret= theFirstTransId;
2035 
2036  if ((theFirstTransId & 0xFFFFFFFF) == 0xFFFFFFFF) {
2037  theFirstTransId = (theFirstTransId >> 32) << 32;
2038  } else {
2039  theFirstTransId++;
2040  }
2041 
2042  return ret;
2043 }
2044 
2045 #ifdef VM_TRACE
2046 #include <NdbMutex.h>
2047 extern NdbMutex *ndb_print_state_mutex;
2048 
2049 static bool
2050 checkdups(NdbTransaction** list, unsigned no)
2051 {
2052  for (unsigned i = 0; i < no; i++)
2053  for (unsigned j = i + 1; j < no; j++)
2054  if (list[i] == list[j])
2055  return true;
2056  return false;
2057 }
2058 void
2059 Ndb::printState(const char* fmt, ...)
2060 {
2061  char buf[200];
2062  va_list ap;
2063  va_start(ap, fmt);
2064  vsprintf(buf, fmt, ap);
2065  va_end(ap);
2066  NdbMutex_Lock(ndb_print_state_mutex);
2067  bool dups = false;
2068  unsigned i;
2069  ndbout << buf << " ndb=" << hex << (void*)this << endl;
2070  for (unsigned n = 0; n < MAX_NDB_NODES; n++) {
2071  NdbTransaction* con = theConnectionArray[n];
2072  if (con != 0) {
2073  ndbout << "conn " << n << ":" << endl;
2074  while (con != 0) {
2075  con->printState();
2076  con = con->theNext;
2077  }
2078  }
2079  }
2080  ndbout << "prepared: " << theNoOfPreparedTransactions<< endl;
2081  if (checkdups(thePreparedTransactionsArray, theNoOfPreparedTransactions)) {
2082  ndbout << "!! DUPS !!" << endl;
2083  dups = true;
2084  }
2085  for (i = 0; i < theNoOfPreparedTransactions; i++)
2086  thePreparedTransactionsArray[i]->printState();
2087  ndbout << "sent: " << theNoOfSentTransactions<< endl;
2088  if (checkdups(theSentTransactionsArray, theNoOfSentTransactions)) {
2089  ndbout << "!! DUPS !!" << endl;
2090  dups = true;
2091  }
2092  for (i = 0; i < theNoOfSentTransactions; i++)
2093  theSentTransactionsArray[i]->printState();
2094  ndbout << "completed: " << theNoOfCompletedTransactions<< endl;
2095  if (checkdups(theCompletedTransactionsArray, theNoOfCompletedTransactions)) {
2096  ndbout << "!! DUPS !!" << endl;
2097  dups = true;
2098  }
2099  for (i = 0; i < theNoOfCompletedTransactions; i++)
2100  theCompletedTransactionsArray[i]->printState();
2101  NdbMutex_Unlock(ndb_print_state_mutex);
2102 }
2103 
2104 #endif
2105 
2106 const char*
2107 Ndb::getNdbErrorDetail(const NdbError& err, char* buff, Uint32 buffLen) const
2108 {
2109  DBUG_ENTER("Ndb::getNdbErrorDetail");
2110  /* If err has non-null details member, prepare a string containing
2111  * those details
2112  */
2113  if (!buff)
2114  DBUG_RETURN(NULL);
2115 
2116  if (err.details != NULL)
2117  {
2118  DBUG_PRINT("info", ("err.code is %u", err.code));
2119  switch (err.code) {
2120  case 893: /* Unique constraint violation */
2121  {
2122  /* err.details contains the violated Index's object id
2123  * We'll map it to a name, then map the name to a
2124  * base table, schema and database, and put that in
2125  * string form into the caller's buffer
2126  */
2127  UintPtr uip = (UintPtr) err.details;
2128  Uint32 indexObjectId = (Uint32) (uip - (UintPtr(0)));
2129  Uint32 primTableObjectId = ~ (Uint32) 0;
2130  BaseString indexName;
2131  char splitChars[2] = {table_name_separator, 0};
2132  BaseString splitString(&splitChars[0]);
2133 
2134  {
2135  DBUG_PRINT("info", ("Index object id is %u", indexObjectId));
2137  int rc = theDictionary->listObjects(allIndices,
2139  false); // FullyQualified names
2140  if (rc)
2141  {
2142  DBUG_PRINT("info", ("listObjects call 1 failed with rc %u", rc));
2143  DBUG_RETURN(NULL);
2144  }
2145 
2146  DBUG_PRINT("info", ("Retrieved details for %u indices", allIndices.count));
2147 
2148  for (unsigned i = 0; i < allIndices.count; i++)
2149  {
2150  if (allIndices.elements[i].id == indexObjectId)
2151  {
2152  /* Found the index in question
2153  * Expect fully qualified name to be in the form :
2154  * <db>/<schema>/<primTabId>/<IndexName>
2155  */
2156  Vector<BaseString> idxNameComponents;
2157  BaseString idxName(allIndices.elements[i].name);
2158 
2159  Uint32 components = idxName.split(idxNameComponents,
2160  splitString);
2161 
2162  assert(components == 4);
2163 
2164  primTableObjectId = atoi(idxNameComponents[2].c_str());
2165  indexName = idxNameComponents[3];
2166 
2167  DBUG_PRINT("info", ("Found index name : %s, primary table id : %u",
2168  indexName.c_str(), primTableObjectId));
2169 
2170  break;
2171  }
2172  }
2173  }
2174 
2175  if (primTableObjectId != (~(Uint32) 0))
2176  {
2178  int rc = theDictionary->listObjects(allTables,
2180  false); // FullyQualified names
2181 
2182  if (rc)
2183  {
2184  DBUG_PRINT("info", ("listObjects call 2 failed with rc %u", rc));
2185  DBUG_RETURN(NULL);
2186  }
2187 
2188  DBUG_PRINT("info", ("Retrieved details for %u tables", allTables.count));
2189 
2190  for (Uint32 t = 0; t < allTables.count; t++)
2191  {
2192 
2193  if (allTables.elements[t].id == primTableObjectId)
2194  {
2195  /* Found table, name should be in format :
2196  * <db>/<schema>/<tablename>
2197  */
2198  Vector<BaseString> tabNameComponents;
2199  BaseString tabName(allTables.elements[t].name);
2200 
2201  Uint32 components = tabName.split(tabNameComponents,
2202  splitString);
2203  assert (components == 3);
2204 
2205  /* Now we generate a string of the format
2206  * <dbname>/<schemaname>/<tabname>/<idxname>
2207  * which should be usable by end users
2208  */
2209  BaseString result;
2210  result.assfmt("%s/%s/%s/%s",
2211  tabNameComponents[0].c_str(),
2212  tabNameComponents[1].c_str(),
2213  tabNameComponents[2].c_str(),
2214  indexName.c_str());
2215 
2216  DBUG_PRINT("info", ("Found full index details : %s",
2217  result.c_str()));
2218 
2219  memcpy(buff, result.c_str(),
2220  MIN(buffLen,
2221  (result.length() + 1)));
2222  buff[buffLen] = 0;
2223 
2224  DBUG_RETURN(buff);
2225  }
2226  }
2227 
2228  /* Primary table not found!
2229  * Strange - perhaps it's been dropped?
2230  */
2231  DBUG_PRINT("info", ("Table id %u not found", primTableObjectId));
2232  DBUG_RETURN(NULL);
2233  }
2234  else
2235  {
2236  /* Index not found from id - strange.
2237  * Perhaps it has been dropped?
2238  */
2239  DBUG_PRINT("info", ("Index id %u not found", indexObjectId));
2240  DBUG_RETURN(NULL);
2241  }
2242  }
2243  default:
2244  {
2245  /* Unhandled details type */
2246  }
2247  }
2248  }
2249 
2250  DBUG_PRINT("info", ("No details string for this error"));
2251  DBUG_RETURN(NULL);
2252 }
2253 
2254 void
2255 Ndb::setCustomData(void* _customDataPtr)
2256 {
2257  theImpl->customDataPtr = _customDataPtr;
2258 }
2259 
2260 void*
2261 Ndb::getCustomData() const
2262 {
2263  return theImpl->customDataPtr;
2264 }
2265 
2266 Uint32
2267 Ndb::getMinDbNodeVersion() const
2268 {
2269  return theCachedMinDbNodeVersion;
2270 }
2271 
2272 const char* ClientStatNames [] =
2273 { "WaitExecCompleteCount",
2274  "WaitScanResultCount",
2275  "WaitMetaRequestCount",
2276  "WaitNanosCount",
2277  "BytesSentCount",
2278  "BytesRecvdCount",
2279  "TransStartCount",
2280  "TransCommitCount",
2281  "TransAbortCount",
2282  "TransCloseCount",
2283  "PkOpCount",
2284  "UkOpCount",
2285  "TableScanCount",
2286  "RangeScanCount",
2287  "PrunedScanCount",
2288  "ScanBatchCount",
2289  "ReadRowCount",
2290  "TransLocalReadRowCount",
2291  "DataEventsRecvdCount",
2292  "NonDataEventsRecvdCount",
2293  "EventBytesRecvdCount"
2294 };
2295 
2296 Uint64
2297 Ndb::getClientStat(Uint32 id) const
2298 {
2299  if (likely(id < NumClientStatistics))
2300  return theImpl->clientStats[id];
2301 
2302  return 0;
2303 }
2304 
2305 const char*
2306 Ndb::getClientStatName(Uint32 id) const
2307 {
2308  if (likely(id < NumClientStatistics))
2309  return ClientStatNames[id];
2310 
2311  return NULL;
2312 }