MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ha_ndb_index_stat.cc
1 /*
2  Copyright (c) 2011, 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 #include "ha_ndbcluster_glue.h"
19 
20 #ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
21 
22 #include "ha_ndbcluster.h"
23 #include "ha_ndb_index_stat.h"
24 #include <mysql/plugin.h>
25 #include <ctype.h>
26 
27 // copied from ha_ndbcluster_binlog.h
28 
29 extern handlerton *ndbcluster_hton;
30 
31 inline
32 void
33 set_thd_ndb(THD *thd, Thd_ndb *thd_ndb)
34 { thd_set_ha_data(thd, ndbcluster_hton, thd_ndb); }
35 
36 // Typedefs for long names
39 
40 struct Ndb_index_stat {
41  enum {
42  LT_Undef= 0,
43  LT_New= 1, /* new entry added by a table handler */
44  LT_Update = 2, /* force kernel update from analyze table */
45  LT_Read= 3, /* read or reread stats into new query cache */
46  LT_Idle= 4, /* stats exist */
47  LT_Check= 5, /* check for new stats */
48  LT_Delete= 6, /* delete the entry */
49  LT_Error= 7, /* error, on hold for a while */
50  LT_Count= 8
51  };
52  NdbIndexStat* is;
53  int index_id;
54  int index_version;
55 #ifndef DBUG_OFF
56  char id[32];
57 #endif
58  time_t access_time; /* by any table handler */
59  time_t load_time; /* when stats were created by kernel */
60  time_t read_time; /* when stats were read by us (>= load_time) */
61  uint sample_version; /* goes with read_time */
62  time_t check_time; /* when checked for updated stats (>= read_time) */
63  bool cache_clean; /* old caches have been deleted */
64  uint force_update; /* one-time force update from analyze table */
65  bool no_stats; /* have detected that no stats exist */
66  NdbIndexStat::Error error;
67  time_t error_time;
68  int error_count;
69  struct Ndb_index_stat *share_next; /* per-share list */
70  int lt;
71  int lt_old; /* for info only */
72  struct Ndb_index_stat *list_next;
73  struct Ndb_index_stat *list_prev;
74  struct NDB_SHARE *share;
75  Ndb_index_stat();
76 };
77 
78 struct Ndb_index_stat_list {
79  const char *name;
80  int lt;
81  struct Ndb_index_stat *head;
82  struct Ndb_index_stat *tail;
83  uint count;
84  Ndb_index_stat_list(int the_lt, const char* the_name);
85 };
86 
87 extern Ndb_index_stat_list ndb_index_stat_list[];
88 
89 time_t ndb_index_stat_time_now= 0;
90 
91 time_t
92 ndb_index_stat_time()
93 {
94  time_t now= time(0);
95 
96  if (unlikely(ndb_index_stat_time_now == 0))
97  ndb_index_stat_time_now= now;
98 
99  if (unlikely(now < ndb_index_stat_time_now))
100  {
101  DBUG_PRINT("index_stat", ("time moved backwards %d seconds",
102  int(ndb_index_stat_time_now - now)));
103  now= ndb_index_stat_time_now;
104  }
105 
106  ndb_index_stat_time_now= now;
107  return now;
108 }
109 
110 bool ndb_index_stat_allow_flag= false;
111 
112 bool
113 ndb_index_stat_allow(int flag= -1)
114 {
115  if (flag != -1) {
116  pthread_mutex_lock(&ndb_index_stat_list_mutex);
117  ndb_index_stat_allow_flag= (bool)flag;
118  pthread_mutex_unlock(&ndb_index_stat_list_mutex);
119  }
120  return ndb_index_stat_allow_flag;
121 }
122 
123 /* Options */
124 
125 /* Options in string format buffer size */
126 static const uint ndb_index_stat_option_sz= 512;
127 void ndb_index_stat_opt2str(const struct Ndb_index_stat_opt&, char*);
128 
129 struct Ndb_index_stat_opt {
130  enum Unit {
131  Ubool = 1,
132  Usize = 2,
133  Utime = 3,
134  Umsec = 4
135  };
136  enum Flag {
137  Freadonly = (1 << 0)
138  };
139  struct Val {
140  const char* name;
141  uint val;
142  uint minval;
143  uint maxval;
144  Unit unit;
145  uint flag;
146  };
147  enum Idx {
148  Iloop_checkon = 0,
149  Iloop_idle = 1,
150  Iloop_busy = 2,
151  Iupdate_batch = 3,
152  Iread_batch = 4,
153  Iidle_batch = 5,
154  Icheck_batch = 6,
155  Icheck_delay = 7,
156  Idelete_batch = 8,
157  Iclean_delay = 9,
158  Ierror_batch = 10,
159  Ierror_delay = 11,
160  Ievict_batch = 12,
161  Ievict_delay = 13,
162  Icache_limit = 14,
163  Icache_lowpct = 15,
164  Imax = 16
165  };
166  Val val[Imax];
167  /* Options in string format (SYSVAR ndb_index_stat_option) */
168  char *option;
169  Ndb_index_stat_opt(char* buf);
170  uint get(Idx i) const {
171  assert(i < Imax);
172  return val[i].val;
173  }
174 };
175 
176 Ndb_index_stat_opt::Ndb_index_stat_opt(char* buf) :
177  option(buf)
178 {
179 #define ival(aname, aval, aminval, amaxval, aunit, aflag) \
180  val[I##aname].name = #aname; \
181  val[I##aname].val = aval; \
182  val[I##aname].minval = aminval; \
183  val[I##aname].maxval = amaxval; \
184  val[I##aname].unit = aunit; \
185  val[I##aname].flag = aflag
186  ival(loop_checkon, 1000, 0, ~0, Umsec, 0);
187  ival(loop_idle, 1000, 0, ~0, Umsec, 0);
188  ival(loop_busy, 100, 0, ~0, Umsec, 0);
189  ival(update_batch, 1, 1, ~0, Usize, 0);
190  ival(read_batch, 4, 1, ~0, Usize, 0);
191  ival(idle_batch, 32, 1, ~0, Usize, 0);
192  ival(check_batch, 32, 1, ~0, Usize, 0);
193  ival(check_delay, 60, 0, ~0, Utime, 0);
194  ival(clean_delay, 0, 0, ~0, Utime, 0);
195  ival(delete_batch, 8, 1, ~0, Usize, 0);
196  ival(error_batch, 4, 1, ~0, Usize, 0);
197  ival(error_delay, 60, 0, ~0, Utime, 0);
198  ival(evict_batch, 8, 1, ~0, Usize, 0);
199  ival(evict_delay, 60, 0, ~0, Utime, 0);
200  ival(cache_limit, 32*1024*1024, 1024*1024, ~0, Usize, 0);
201  ival(cache_lowpct, 90, 0, 100, Usize, 0);
202 #undef ival
203 
204  ndb_index_stat_opt2str(*this, option);
205 }
206 
207 /* Hard limits */
208 static const uint ndb_index_stat_max_evict_batch = 32;
209 
210 char ndb_index_stat_option_buf[ndb_index_stat_option_sz];
211 Ndb_index_stat_opt ndb_index_stat_opt(ndb_index_stat_option_buf);
212 
213 /* Copy option struct to string buffer */
214 void
215 ndb_index_stat_opt2str(const Ndb_index_stat_opt& opt, char* str)
216 {
217  DBUG_ENTER("ndb_index_stat_opt2str");
218 
219  char buf[ndb_index_stat_option_sz];
220  char *const end= &buf[sizeof(buf)];
221  char* ptr= buf;
222  *ptr= 0;
223 
224  const uint imax= Ndb_index_stat_opt::Imax;
225  for (uint i= 0; i < imax; i++)
226  {
227  const Ndb_index_stat_opt::Val& v= opt.val[i];
228  ptr+= strlen(ptr);
229  const char* sep= (ptr == buf ? "" : ",");
230  const uint sz= ptr < end ? end - ptr : 0;
231 
232  switch (v.unit) {
233  case Ndb_index_stat_opt::Ubool:
234  {
235  DBUG_ASSERT(v.val == 0 || v.val == 1);
236  if (v.val == 0)
237  my_snprintf(ptr, sz, "%s%s=OFF", sep, v.name);
238  else
239  my_snprintf(ptr, sz, "%s%s=ON", sep, v.name);
240  }
241  break;
242 
243  case Ndb_index_stat_opt::Usize:
244  {
245  uint m;
246  if (v.val == 0)
247  my_snprintf(ptr, sz, "%s%s=0", sep, v.name);
248  else if (v.val % (m= 1024*1024*1024) == 0)
249  my_snprintf(ptr, sz, "%s%s=%uG", sep, v.name, v.val / m);
250  else if (v.val % (m= 1024*1024) == 0)
251  my_snprintf(ptr, sz, "%s%s=%uM", sep, v.name, v.val / m);
252  else if (v.val % (m= 1024) == 0)
253  my_snprintf(ptr, sz, "%s%s=%uK", sep, v.name, v.val / m);
254  else
255  my_snprintf(ptr, sz, "%s%s=%u", sep, v.name, v.val);
256  }
257  break;
258 
259  case Ndb_index_stat_opt::Utime:
260  {
261  uint m;
262  if (v.val == 0)
263  my_snprintf(ptr, sz, "%s%s=0", sep, v.name);
264  else if (v.val % (m= 60*60*24) == 0)
265  my_snprintf(ptr, sz, "%s%s=%ud", sep, v.name, v.val / m);
266  else if (v.val % (m= 60*60) == 0)
267  my_snprintf(ptr, sz, "%s%s=%uh", sep, v.name, v.val / m);
268  else if (v.val % (m= 60) == 0)
269  my_snprintf(ptr, sz, "%s%s=%um", sep, v.name, v.val / m);
270  else
271  my_snprintf(ptr, sz, "%s%s=%us", sep, v.name, v.val);
272  }
273  break;
274 
275  case Ndb_index_stat_opt::Umsec:
276  {
277  if (v.val == 0)
278  my_snprintf(ptr, sz, "%s%s=0", sep, v.name);
279  else
280  my_snprintf(ptr, sz, "%s%s=%ums", sep, v.name, v.val);
281  }
282  break;
283 
284  default:
285  DBUG_ASSERT(false);
286  break;
287  }
288  }
289 
290  memset(str, 0, ndb_index_stat_option_sz);
291  strcpy(str, buf);
292  DBUG_PRINT("index_stat", ("str: \"%s\"", str));
293  DBUG_VOID_RETURN;
294 }
295 
296 int
297 ndb_index_stat_option_parse(char* p, Ndb_index_stat_opt& opt)
298 {
299  DBUG_ENTER("ndb_index_stat_option_parse");
300 
301  char *r= strchr(p, '=');
302  if (r == 0)
303  DBUG_RETURN(-1);
304  *r++= 0;
305 
306  while (isspace(*r))
307  *r++= 0;
308  if (*r == 0)
309  DBUG_RETURN(-1);
310 
311  const uint imax= Ndb_index_stat_opt::Imax;
312  for (uint i= 0; i < imax; i++)
313  {
314  Ndb_index_stat_opt::Val& v= opt.val[i];
315  if (strcmp(p, v.name) != 0)
316  continue;
317 
318  char *s;
319  for (s= r; *s != 0; s++)
320  *s= tolower(*s);
321  ulonglong val= strtoull(r, &s, 10);
322 
323  switch (v.unit) {
324  case Ndb_index_stat_opt::Ubool:
325  {
326  if ((s > r && *s == 0 && val == 0) ||
327  strcmp(r, "off") == 0 ||
328  strcmp(r, "false") == 0)
329  val= 0;
330  else if ((s > r && *s == 0 && val == 1) ||
331  strcmp(r, "on") == 0 ||
332  strcmp(r, "true") == 0)
333  val= 1;
334  else
335  DBUG_RETURN(-1);
336  v.val= (uint)val;
337  }
338  break;
339 
340  case Ndb_index_stat_opt::Usize:
341  {
342  if (s == r)
343  DBUG_RETURN(-1);
344  if (strcmp(s, "") == 0)
345  ;
346  else if (strcmp(s, "k") == 0)
347  val*= 1024;
348  else if (strcmp(s, "m") == 0)
349  val*= 1024*1024;
350  else if (strcmp(s, "g") == 0)
351  val*= 1024*1024*1024;
352  else
353  DBUG_RETURN(-1);
354  if (val < v.minval || val > v.maxval)
355  DBUG_RETURN(-1);
356  v.val= (uint)val;
357  }
358  break;
359 
360  case Ndb_index_stat_opt::Utime:
361  {
362  if (s == r)
363  DBUG_RETURN(-1);
364  if (strcmp(s, "") == 0)
365  ;
366  else if (strcmp(s, "s") == 0)
367  ;
368  else if (strcmp(s, "m") == 0)
369  val*= 60;
370  else if (strcmp(s, "h") == 0)
371  val*= 60*60;
372  else if (strcmp(s, "d") == 0)
373  val*= 24*60*60;
374  else
375  DBUG_RETURN(-1);
376  if (val < v.minval || val > v.maxval)
377  DBUG_RETURN(-1);
378  v.val= (uint)val;
379  }
380  break;
381 
382  case Ndb_index_stat_opt::Umsec:
383  {
384  if (s == r)
385  DBUG_RETURN(-1);
386  if (strcmp(s, "") == 0)
387  ;
388  else if (strcmp(s, "ms") == 0)
389  ;
390  else
391  DBUG_RETURN(-1);
392  if (val < v.minval || val > v.maxval)
393  DBUG_RETURN(-1);
394  v.val= (uint)val;
395  }
396  break;
397 
398  default:
399  DBUG_ASSERT(false);
400  break;
401  }
402  }
403  DBUG_RETURN(0);
404 }
405 
406 /* Copy option string to option struct */
407 int
408 ndb_index_stat_str2opt(const char *str, Ndb_index_stat_opt& opt)
409 {
410  DBUG_ENTER("ndb_index_stat_str2opt");
411  DBUG_PRINT("index_stat", ("str: \"%s\"", str));
412 
413  char buf[ndb_index_stat_option_sz];
414 
415  assert(str != 0);
416  if (strlen(str) >= sizeof(buf))
417  DBUG_RETURN(-1);
418  strcpy(buf, str);
419 
420  char *p= buf;
421  while (1)
422  {
423  while (isspace(*p))
424  p++;
425  if (*p == 0)
426  break;
427 
428  char *q= strchr(p, ',');
429  if (q == p)
430  DBUG_RETURN(-1);
431  if (q != 0)
432  *q= 0;
433 
434  DBUG_PRINT("index_stat", ("parse: %s", p));
435  if (ndb_index_stat_option_parse(p, opt) == -1)
436  DBUG_RETURN(-1);
437 
438  if (q == 0)
439  break;
440  p= q + 1;
441  }
442 
443  ndb_index_stat_opt2str(opt, opt.option);
444  DBUG_RETURN(0);
445 }
446 
447 /* Thanks to ha_innodb.cc */
448 
449 /* Need storage between check and update (assume locked) */
450 char ndb_index_stat_option_tmp[ndb_index_stat_option_sz];
451 
452 int
453 ndb_index_stat_option_check(MYSQL_THD,
454  struct st_mysql_sys_var *var,
455  void *save,
456  struct st_mysql_value *value)
457 {
458  DBUG_ENTER("ndb_index_stat_option_check");
459  char buf[ndb_index_stat_option_sz];
460  int len= sizeof(buf);
461  const char *str= value->val_str(value, buf, &len);
462  if (str != 0)
463  {
464  /* Seems to be nothing in buf */
465  DBUG_PRINT("index_stat", ("str: %s len: %d", str, len));
466  char buf2[ndb_index_stat_option_sz];
467  Ndb_index_stat_opt opt(buf2);
468  if (ndb_index_stat_str2opt(str, opt) == 0)
469  {
470  /* Passed to update */
471  strcpy(ndb_index_stat_option_tmp, str);
472  *(const char**)save= ndb_index_stat_option_tmp;
473  DBUG_RETURN(0);
474  }
475  }
476  DBUG_RETURN(1);
477 }
478 
479 void
480 ndb_index_stat_option_update(MYSQL_THD,
481  struct st_mysql_sys_var *var,
482  void *var_ptr,
483  const void *save)
484 {
485  DBUG_ENTER("ndb_index_stat_option_update");
486  const char *str= *(const char**)save;
487  DBUG_PRINT("index_stat", ("str: %s", str));
488  Ndb_index_stat_opt& opt= ndb_index_stat_opt;
489  int ret= ndb_index_stat_str2opt(str, opt);
490  assert(ret == 0); NDB_IGNORE_VALUE(ret);
491  *(const char**)var_ptr= ndb_index_stat_opt.option;
492  DBUG_VOID_RETURN;
493 }
494 
495 /* Global stuff */
496 
497 struct Ndb_index_stat_glob {
498  uint list_count[Ndb_index_stat::LT_Count]; /* Temporary use */
499  uint total_count;
500  uint force_update;
501  uint wait_update;
502  uint no_stats;
503  uint cache_query_bytes; /* In use */
504  uint cache_clean_bytes; /* Obsolete versions not yet removed */
505  Ndb_index_stat_glob() :
506  total_count(0),
507  force_update(0),
508  wait_update(0),
509  no_stats(0),
510  cache_query_bytes(0),
511  cache_clean_bytes(0)
512  {
513  }
514  void set_list_count()
515  {
516  total_count= 0;
517  int lt;
518  for (lt= 0; lt < Ndb_index_stat::LT_Count; lt++)
519  {
520  const Ndb_index_stat_list &list= ndb_index_stat_list[lt];
521  list_count[lt]= list.count;
522  total_count++;
523  }
524  }
525  void set_status_variables()
526  {
527  g_ndb_status_index_stat_cache_query= cache_query_bytes;
528  g_ndb_status_index_stat_cache_clean= cache_clean_bytes;
529  }
530 };
531 
532 Ndb_index_stat_glob ndb_index_stat_glob;
533 
534 /* Shared index entries */
535 
536 Ndb_index_stat::Ndb_index_stat()
537 {
538  is= 0;
539  index_id= 0;
540  index_version= 0;
541 #ifndef DBUG_OFF
542  memset(id, 0, sizeof(id));
543 #endif
544  access_time= 0;
545  load_time= 0;
546  read_time= 0;
547  sample_version= 0;
548  check_time= 0;
549  cache_clean= false;
550  force_update= 0;
551  no_stats= false;
552  error_time= 0;
553  error_count= 0;
554  share_next= 0;
555  lt= 0;
556  lt_old= 0;
557  list_next= 0;
558  list_prev= 0;
559  share= 0;
560 }
561 
562 void
563 ndb_index_stat_error(Ndb_index_stat *st, const char* place, int line)
564 {
565  time_t now= ndb_index_stat_time();
566  NdbIndexStat::Error error= st->is->getNdbError();
567  if (error.code == 0)
568  {
569  // XXX why this if
570  NdbIndexStat::Error error2;
571  error= error2;
572  error.code= NdbIndexStat::InternalError;
574  }
575  st->error= error;
576  st->error_time= now;
577  st->error_count++;
578 
579  DBUG_PRINT("index_stat", ("%s line %d: error %d line %d extra %d",
580  place, line, error.code, error.line, error.extra));
581 }
582 
583 void
584 ndb_index_stat_clear_error(Ndb_index_stat *st)
585 {
586  st->error.code= 0;
587  st->error.status= NdbError::Success;
588 }
589 
590 /* Lists across shares */
591 
592 Ndb_index_stat_list::Ndb_index_stat_list(int the_lt, const char* the_name)
593 {
594  lt= the_lt;
595  name= the_name;
596  head= 0;
597  tail= 0;
598  count= 0;
599 }
600 
601 Ndb_index_stat_list ndb_index_stat_list[Ndb_index_stat::LT_Count] = {
602  Ndb_index_stat_list(0, 0),
603  Ndb_index_stat_list(Ndb_index_stat::LT_New, "New"),
604  Ndb_index_stat_list(Ndb_index_stat::LT_Update, "Update"),
605  Ndb_index_stat_list(Ndb_index_stat::LT_Read, "Read"),
606  Ndb_index_stat_list(Ndb_index_stat::LT_Idle, "Idle"),
607  Ndb_index_stat_list(Ndb_index_stat::LT_Check, "Check"),
608  Ndb_index_stat_list(Ndb_index_stat::LT_Delete, "Delete"),
609  Ndb_index_stat_list(Ndb_index_stat::LT_Error, "Error")
610 };
611 
612 void
613 ndb_index_stat_list_add(Ndb_index_stat* st, int lt)
614 {
615  assert(st != 0 && st->lt == 0);
616  assert(st->list_next == 0 && st->list_prev == 0);
617  assert(1 <= lt && lt < Ndb_index_stat::LT_Count);
618  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
619 
620  DBUG_PRINT("index_stat", ("st %s -> %s", st->id, list.name));
621 
622  if (list.count == 0)
623  {
624  assert(list.head == 0 && list.tail == 0);
625  list.head= st;
626  list.tail= st;
627  }
628  else
629  {
630  assert(list.tail != 0 && list.tail->list_next == 0);
631  st->list_prev= list.tail;
632  list.tail->list_next= st;
633  list.tail= st;
634  }
635  list.count++;
636 
637  st->lt= lt;
638 }
639 
640 void
641 ndb_index_stat_list_remove(Ndb_index_stat* st)
642 {
643  assert(st != 0);
644  int lt= st->lt;
645  assert(1 <= lt && lt < Ndb_index_stat::LT_Count);
646  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
647 
648  DBUG_PRINT("index_stat", ("st %s <- %s", st->id, list.name));
649 
650  Ndb_index_stat* next= st->list_next;
651  Ndb_index_stat* prev= st->list_prev;
652 
653  if (list.head == st)
654  list.head= next;
655  if (list.tail == st)
656  list.tail= prev;
657  assert(list.count != 0);
658  list.count--;
659 
660  if (next != 0)
661  next->list_prev= prev;
662  if (prev != 0)
663  prev->list_next= next;
664 
665  st->lt= 0;
666  st->lt_old= 0;
667  st->list_next= 0;
668  st->list_prev= 0;
669 }
670 
671 void
672 ndb_index_stat_list_move(Ndb_index_stat *st, int lt)
673 {
674  assert(st != 0);
675  ndb_index_stat_list_remove(st);
676  ndb_index_stat_list_add(st, lt);
677 }
678 
679 /* Stats entry changes (must hold stat_mutex) */
680 
681 void
682 ndb_index_stat_force_update(Ndb_index_stat *st, bool onoff)
683 {
684  Ndb_index_stat_glob &glob= ndb_index_stat_glob;
685  if (onoff)
686  {
687  /* One more request */
688  glob.force_update++;
689  st->force_update++;
690  }
691  else
692  {
693  /* All done */
694  assert(glob.force_update >= st->force_update);
695  glob.force_update-= st->force_update;
696  st->force_update= 0;
697  }
698 }
699 
700 void
701 ndb_index_stat_no_stats(Ndb_index_stat *st, bool flag)
702 {
703  Ndb_index_stat_glob &glob= ndb_index_stat_glob;
704  if (st->no_stats != flag)
705  {
706  if (flag)
707  {
708  glob.no_stats++;
709  st->no_stats= true;
710  }
711  else
712  {
713  assert(glob.no_stats >= 1);
714  glob.no_stats-= 1;
715  st->no_stats= false;
716  }
717  }
718 }
719 
720 /* Find or add entry under the share */
721 
722 Ndb_index_stat*
723 ndb_index_stat_alloc(const NDBINDEX *index,
724  const NDBTAB *table,
725  int &err_out)
726 {
727  err_out= 0;
728  Ndb_index_stat *st= new Ndb_index_stat;
729  NdbIndexStat *is= new NdbIndexStat;
730  if (st != 0 && is != 0)
731  {
732  st->is= is;
733  st->index_id= index->getObjectId();
734  st->index_version= index->getObjectVersion();
735 #ifndef DBUG_OFF
736  my_snprintf(st->id, sizeof(st->id), "%d.%d", st->index_id, st->index_version);
737 #endif
738  if (is->set_index(*index, *table) == 0)
739  return st;
740  ndb_index_stat_error(st, "set_index", __LINE__);
741  err_out= st->error.code;
742  }
743  else
744  {
745  err_out= NdbIndexStat::NoMemError;
746  }
747  if (is != 0)
748  delete is;
749  if (st != 0)
750  delete st;
751  return 0;
752 }
753 
754 /* Subroutine, have lock */
755 Ndb_index_stat*
756 ndb_index_stat_find_share(NDB_SHARE *share,
757  const NDBINDEX *index,
758  Ndb_index_stat *&st_last)
759 {
760  struct Ndb_index_stat *st= share->index_stat_list;
761  st_last= 0;
762  while (st != 0)
763  {
764  assert(st->share == share);
765  assert(st->is != 0);
766  NdbIndexStat::Head head;
767  st->is->get_head(head);
768  if (head.m_indexId == (uint)index->getObjectId() &&
769  head.m_indexVersion == (uint)index->getObjectVersion())
770  break;
771  st_last= st;
772  st= st->share_next;
773  }
774  return st;
775 }
776 
777 /* Subroutine, have lock */
778 void
779 ndb_index_stat_add_share(NDB_SHARE *share,
780  Ndb_index_stat *st,
781  Ndb_index_stat *st_last)
782 {
783  st->share= share;
784  if (st_last == 0)
785  share->index_stat_list= st;
786  else
787  st_last->share_next= st;
788 }
789 
790 Ndb_index_stat*
791 ndb_index_stat_get_share(NDB_SHARE *share,
792  const NDBINDEX *index,
793  const NDBTAB *table,
794  int &err_out,
795  bool allow_add,
796  bool force_update)
797 {
798  pthread_mutex_lock(&share->mutex);
799  pthread_mutex_lock(&ndb_index_stat_list_mutex);
800  pthread_mutex_lock(&ndb_index_stat_stat_mutex);
801  time_t now= ndb_index_stat_time();
802  err_out= 0;
803 
804  struct Ndb_index_stat *st= 0;
805  struct Ndb_index_stat *st_last= 0;
806  do
807  {
808  if (unlikely(!ndb_index_stat_allow()))
809  {
810  err_out= Ndb_index_stat_error_NOT_ALLOW;
811  break;
812  }
813  st= ndb_index_stat_find_share(share, index, st_last);
814  if (st == 0)
815  {
816  if (!allow_add)
817  {
818  err_out= Ndb_index_stat_error_NOT_FOUND;
819  break;
820  }
821  st= ndb_index_stat_alloc(index, table, err_out);
822  if (st == 0)
823  {
824  assert(err_out != 0);
825  break;
826  }
827  ndb_index_stat_add_share(share, st, st_last);
828  ndb_index_stat_list_add(st, Ndb_index_stat::LT_New);
829  }
830  if (force_update)
831  ndb_index_stat_force_update(st, true);
832  st->access_time= now;
833  }
834  while (0);
835 
836  pthread_mutex_unlock(&ndb_index_stat_stat_mutex);
837  pthread_mutex_unlock(&ndb_index_stat_list_mutex);
838  pthread_mutex_unlock(&share->mutex);
839  return st;
840 }
841 
842 void
843 ndb_index_stat_free(Ndb_index_stat *st)
844 {
845  pthread_mutex_lock(&ndb_index_stat_list_mutex);
846  NDB_SHARE *share= st->share;
847  assert(share != 0);
848 
849  Ndb_index_stat *st_head= 0;
850  Ndb_index_stat *st_tail= 0;
851  Ndb_index_stat *st_loop= share->index_stat_list;
852  bool found= false;
853  while (st_loop != 0) {
854  if (st == st_loop) {
855  st->share= 0;
856  assert(st->lt != 0);
857  assert(st->lt != Ndb_index_stat::LT_Delete);
858  ndb_index_stat_list_move(st, Ndb_index_stat::LT_Delete);
859  st_loop= st_loop->share_next;
860  assert(!found);
861  found++;
862  } else {
863  if (st_head == 0)
864  st_head= st_loop;
865  else
866  st_tail->share_next= st_loop;
867  st_tail= st_loop;
868  st_loop= st_loop->share_next;
869  st_tail->share_next= 0;
870  }
871  }
872  assert(found);
873  share->index_stat_list= st_head;
874  pthread_mutex_unlock(&ndb_index_stat_list_mutex);
875 }
876 
877 void
878 ndb_index_stat_free(NDB_SHARE *share)
879 {
880  pthread_mutex_lock(&ndb_index_stat_list_mutex);
881  Ndb_index_stat *st;
882  while ((st= share->index_stat_list) != 0)
883  {
884  share->index_stat_list= st->share_next;
885  st->share= 0;
886  assert(st->lt != 0);
887  assert(st->lt != Ndb_index_stat::LT_Delete);
888  ndb_index_stat_list_move(st, Ndb_index_stat::LT_Delete);
889  }
890  pthread_mutex_unlock(&ndb_index_stat_list_mutex);
891 }
892 
893 /* Find entry across shares */
894 /* wl4124_todo mutex overkill, hash table, can we find table share */
895 Ndb_index_stat*
896 ndb_index_stat_find_entry(int index_id, int index_version, int table_id)
897 {
898  DBUG_ENTER("ndb_index_stat_find_entry");
899  pthread_mutex_lock(&ndbcluster_mutex);
900  pthread_mutex_lock(&ndb_index_stat_list_mutex);
901  DBUG_PRINT("index_stat", ("find index:%d version:%d table:%d",
902  index_id, index_version, table_id));
903 
904  int lt;
905  for (lt=1; lt < Ndb_index_stat::LT_Count; lt++)
906  {
907  Ndb_index_stat *st=ndb_index_stat_list[lt].head;
908  while (st != 0)
909  {
910  if (st->index_id == index_id &&
911  st->index_version == index_version)
912  {
913  pthread_mutex_unlock(&ndb_index_stat_list_mutex);
914  pthread_mutex_unlock(&ndbcluster_mutex);
915  DBUG_RETURN(st);
916  }
917  st= st->list_next;
918  }
919  }
920 
921  pthread_mutex_unlock(&ndb_index_stat_list_mutex);
922  pthread_mutex_unlock(&ndbcluster_mutex);
923  DBUG_RETURN(0);
924 }
925 
926 /* Statistics thread sub-routines */
927 
928 void
929 ndb_index_stat_cache_move(Ndb_index_stat *st)
930 {
931  Ndb_index_stat_glob &glob= ndb_index_stat_glob;
932  NdbIndexStat::CacheInfo infoBuild;
933  NdbIndexStat::CacheInfo infoQuery;
934 
935  st->is->get_cache_info(infoBuild, NdbIndexStat::CacheBuild);
936  st->is->get_cache_info(infoQuery, NdbIndexStat::CacheQuery);
937  const uint new_query_bytes= infoBuild.m_totalBytes;
938  const uint old_query_bytes= infoQuery.m_totalBytes;
939  DBUG_PRINT("index_stat", ("st %s cache move: query:%u clean:%u",
940  st->id, new_query_bytes, old_query_bytes));
941  st->is->move_cache();
942  assert(glob.cache_query_bytes >= old_query_bytes);
943  glob.cache_query_bytes-= old_query_bytes;
944  glob.cache_query_bytes+= new_query_bytes;
945  glob.cache_clean_bytes+= old_query_bytes;
946  glob.set_status_variables();
947 }
948 
949 void
950 ndb_index_stat_cache_clean(Ndb_index_stat *st)
951 {
952  Ndb_index_stat_glob &glob= ndb_index_stat_glob;
953  NdbIndexStat::CacheInfo infoClean;
954 
955  st->is->get_cache_info(infoClean, NdbIndexStat::CacheClean);
956  const uint old_clean_bytes= infoClean.m_totalBytes;
957  DBUG_PRINT("index_stat", ("st %s cache clean: clean:%u",
958  st->id, old_clean_bytes));
959  st->is->clean_cache();
960  assert(glob.cache_clean_bytes >= old_clean_bytes);
961  glob.cache_clean_bytes-= old_clean_bytes;
962  glob.set_status_variables();
963 }
964 
965 /* Misc in/out parameters for process steps */
966 struct Ndb_index_stat_proc {
967  NdbIndexStat* is_util; // For metadata and polling
968  Ndb *ndb;
969  time_t now;
970  int lt;
971  bool busy;
972  bool end;
973  Ndb_index_stat_proc() :
974  is_util(0),
975  ndb(0),
976  now(0),
977  lt(0),
978  busy(false),
979  end(false)
980  {}
981 };
982 
983 void
984 ndb_index_stat_proc_new(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
985 {
986  if (st->error.code != 0)
987  pr.lt= Ndb_index_stat::LT_Error;
988  else if (st->force_update)
989  pr.lt= Ndb_index_stat::LT_Update;
990  else
991  pr.lt= Ndb_index_stat::LT_Read;
992 }
993 
994 void
995 ndb_index_stat_proc_new(Ndb_index_stat_proc &pr)
996 {
997  pthread_mutex_lock(&ndb_index_stat_list_mutex);
998  const int lt= Ndb_index_stat::LT_New;
999  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1000 
1001  Ndb_index_stat *st_loop= list.head;
1002  while (st_loop != 0)
1003  {
1004  Ndb_index_stat *st= st_loop;
1005  st_loop= st_loop->list_next;
1006  DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
1007  ndb_index_stat_proc_new(pr, st);
1008  ndb_index_stat_list_move(st, pr.lt);
1009  }
1010  pthread_mutex_unlock(&ndb_index_stat_list_mutex);
1011 }
1012 
1013 void
1014 ndb_index_stat_proc_update(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
1015 {
1016  if (st->is->update_stat(pr.ndb) == -1)
1017  {
1018  ndb_index_stat_error(st, "update_stat", __LINE__);
1019  pr.lt= Ndb_index_stat::LT_Error;
1020  return;
1021  }
1022  pr.lt= Ndb_index_stat::LT_Read;
1023 }
1024 
1025 void
1026 ndb_index_stat_proc_update(Ndb_index_stat_proc &pr)
1027 {
1028  const int lt= Ndb_index_stat::LT_Update;
1029  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1030  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1031  const uint batch= opt.get(Ndb_index_stat_opt::Iupdate_batch);
1032 
1033  Ndb_index_stat *st_loop= list.head;
1034  uint cnt= 0;
1035  while (st_loop != 0 && cnt < batch)
1036  {
1037  Ndb_index_stat *st= st_loop;
1038  st_loop= st_loop->list_next;
1039  DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
1040  ndb_index_stat_proc_update(pr, st);
1041  ndb_index_stat_list_move(st, pr.lt);
1042  cnt++;
1043  }
1044  if (cnt == batch)
1045  pr.busy= true;
1046 }
1047 
1048 void
1049 ndb_index_stat_proc_read(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
1050 {
1051  NdbIndexStat::Head head;
1052  if (st->is->read_stat(pr.ndb) == -1)
1053  {
1054  pthread_mutex_lock(&ndb_index_stat_stat_mutex);
1055  ndb_index_stat_error(st, "read_stat", __LINE__);
1056  const uint force_update= st->force_update;
1057  ndb_index_stat_force_update(st, false);
1058 
1059  /* no stats is not unexpected error, unless analyze was done */
1060  if (st->is->getNdbError().code == NdbIndexStat::NoIndexStats &&
1061  force_update == 0)
1062  {
1063  ndb_index_stat_no_stats(st, true);
1064  pr.lt= Ndb_index_stat::LT_Idle;
1065  }
1066  else
1067  {
1068  pr.lt= Ndb_index_stat::LT_Error;
1069  }
1070 
1071  pthread_cond_broadcast(&ndb_index_stat_stat_cond);
1072  pthread_mutex_unlock(&ndb_index_stat_stat_mutex);
1073  return;
1074  }
1075 
1076  pthread_mutex_lock(&ndb_index_stat_stat_mutex);
1077  pr.now= ndb_index_stat_time();
1078  st->is->get_head(head);
1079  st->load_time= head.m_loadTime;
1080  st->read_time= pr.now;
1081  st->sample_version= head.m_sampleVersion;
1082 
1083  ndb_index_stat_force_update(st, false);
1084  ndb_index_stat_no_stats(st, false);
1085 
1086  ndb_index_stat_cache_move(st);
1087  st->cache_clean= false;
1088  pr.lt= Ndb_index_stat::LT_Idle;
1089  pthread_cond_broadcast(&ndb_index_stat_stat_cond);
1090  pthread_mutex_unlock(&ndb_index_stat_stat_mutex);
1091 }
1092 
1093 void
1094 ndb_index_stat_proc_read(Ndb_index_stat_proc &pr)
1095 {
1096  const int lt= Ndb_index_stat::LT_Read;
1097  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1098  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1099  const uint batch= opt.get(Ndb_index_stat_opt::Iread_batch);
1100 
1101  Ndb_index_stat *st_loop= list.head;
1102  uint cnt= 0;
1103  while (st_loop != 0 && cnt < batch)
1104  {
1105  Ndb_index_stat *st= st_loop;
1106  st_loop= st_loop->list_next;
1107  DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
1108  ndb_index_stat_proc_read(pr, st);
1109  ndb_index_stat_list_move(st, pr.lt);
1110  cnt++;
1111  }
1112  if (cnt == batch)
1113  pr.busy= true;
1114 }
1115 
1116 // wl4124_todo detect force_update faster
1117 void
1118 ndb_index_stat_proc_idle(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
1119 {
1120  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1121  const int clean_delay= opt.get(Ndb_index_stat_opt::Iclean_delay);
1122  const int check_delay= opt.get(Ndb_index_stat_opt::Icheck_delay);
1123  const time_t clean_wait=
1124  st->cache_clean ? 0 : st->read_time + clean_delay - pr.now;
1125  const time_t check_wait=
1126  st->check_time == 0 ? 0 : st->check_time + check_delay - pr.now;
1127 
1128  DBUG_PRINT("index_stat", ("st %s check wait:%lds force update:%u"
1129  " clean wait:%lds cache clean:%d",
1130  st->id, (long)check_wait, st->force_update,
1131  (long)clean_wait, st->cache_clean));
1132 
1133  if (!st->cache_clean && clean_wait <= 0)
1134  {
1135  ndb_index_stat_cache_clean(st);
1136  st->cache_clean= true;
1137  }
1138  if (st->force_update)
1139  {
1140  pr.lt= Ndb_index_stat::LT_Update;
1141  return;
1142  }
1143  if (check_wait <= 0)
1144  {
1145  pr.lt= Ndb_index_stat::LT_Check;
1146  return;
1147  }
1148  pr.lt= Ndb_index_stat::LT_Idle;
1149 }
1150 
1151 void
1152 ndb_index_stat_proc_idle(Ndb_index_stat_proc &pr)
1153 {
1154  const int lt= Ndb_index_stat::LT_Idle;
1155  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1156  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1157  const uint batch= opt.get(Ndb_index_stat_opt::Iidle_batch);
1158  pr.now= ndb_index_stat_time();
1159 
1160  Ndb_index_stat *st_loop= list.head;
1161  uint cnt= 0;
1162  while (st_loop != 0 && cnt < batch)
1163  {
1164  Ndb_index_stat *st= st_loop;
1165  st_loop= st_loop->list_next;
1166  DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
1167  ndb_index_stat_proc_idle(pr, st);
1168  // rotates list if entry remains LT_Idle
1169  ndb_index_stat_list_move(st, pr.lt);
1170  cnt++;
1171  }
1172  if (cnt == batch)
1173  pr.busy= true;
1174 }
1175 
1176 void
1177 ndb_index_stat_proc_check(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
1178 {
1179  pr.now= ndb_index_stat_time();
1180  st->check_time= pr.now;
1181  NdbIndexStat::Head head;
1182  if (st->is->read_head(pr.ndb) == -1)
1183  {
1184  ndb_index_stat_error(st, "read_head", __LINE__);
1185  /* no stats is not unexpected error */
1186  if (st->is->getNdbError().code == NdbIndexStat::NoIndexStats)
1187  {
1188  ndb_index_stat_no_stats(st, true);
1189  pr.lt= Ndb_index_stat::LT_Idle;
1190  }
1191  else
1192  {
1193  pr.lt= Ndb_index_stat::LT_Error;
1194  }
1195  return;
1196  }
1197  st->is->get_head(head);
1198  const uint version_old= st->sample_version;
1199  const uint version_new= head.m_sampleVersion;
1200  if (version_old != version_new)
1201  {
1202  DBUG_PRINT("index_stat", ("st %s sample version old:%u new:%u",
1203  st->id, version_old, version_new));
1204  pr.lt= Ndb_index_stat::LT_Read;
1205  return;
1206  }
1207  pr.lt= Ndb_index_stat::LT_Idle;
1208 }
1209 
1210 void
1211 ndb_index_stat_proc_check(Ndb_index_stat_proc &pr)
1212 {
1213  const int lt= Ndb_index_stat::LT_Check;
1214  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1215  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1216  const uint batch= opt.get(Ndb_index_stat_opt::Icheck_batch);
1217 
1218  Ndb_index_stat *st_loop= list.head;
1219  uint cnt= 0;
1220  while (st_loop != 0 && cnt < batch)
1221  {
1222  Ndb_index_stat *st= st_loop;
1223  st_loop= st_loop->list_next;
1224  DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
1225  ndb_index_stat_proc_check(pr, st);
1226  ndb_index_stat_list_move(st, pr.lt);
1227  cnt++;
1228  }
1229  if (cnt == batch)
1230  pr.busy= true;
1231 }
1232 
1233 void
1234 ndb_index_stat_proc_evict(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
1235 {
1236  NdbIndexStat::Head head;
1237  NdbIndexStat::CacheInfo infoBuild;
1238  NdbIndexStat::CacheInfo infoQuery;
1239  NdbIndexStat::CacheInfo infoClean;
1240  st->is->get_head(head);
1241  st->is->get_cache_info(infoBuild, NdbIndexStat::CacheBuild);
1242  st->is->get_cache_info(infoQuery, NdbIndexStat::CacheQuery);
1243  st->is->get_cache_info(infoClean, NdbIndexStat::CacheClean);
1244 
1245  DBUG_PRINT("index_stat",
1246  ("evict table: %u index: %u version: %u"
1247  " sample version: %u"
1248  " cache bytes build:%u query:%u clean:%u",
1249  head.m_tableId, head.m_indexId, head.m_indexVersion,
1250  head.m_sampleVersion,
1251  infoBuild.m_totalBytes, infoQuery.m_totalBytes, infoClean.m_totalBytes));
1252 
1253  /* Twice to move all caches to clean */
1254  ndb_index_stat_cache_move(st);
1255  ndb_index_stat_cache_move(st);
1256  ndb_index_stat_cache_clean(st);
1257 }
1258 
1259 bool
1260 ndb_index_stat_proc_evict()
1261 {
1262  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1263  Ndb_index_stat_glob &glob= ndb_index_stat_glob;
1264  uint curr_size= glob.cache_query_bytes + glob.cache_clean_bytes;
1265  const uint cache_lowpct= opt.get(Ndb_index_stat_opt::Icache_lowpct);
1266  const uint cache_limit= opt.get(Ndb_index_stat_opt::Icache_limit);
1267  if (100 * curr_size <= cache_lowpct * cache_limit)
1268  return false;
1269  return true;
1270 }
1271 
1272 void
1273 ndb_index_stat_proc_evict(Ndb_index_stat_proc &pr, int lt)
1274 {
1275  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1276  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1277  const uint batch= opt.get(Ndb_index_stat_opt::Ievict_batch);
1278  const int evict_delay= opt.get(Ndb_index_stat_opt::Ievict_delay);
1279  pr.now= ndb_index_stat_time();
1280 
1281  if (!ndb_index_stat_proc_evict())
1282  return;
1283 
1284  /* Create a LRU batch */
1285  Ndb_index_stat* st_lru_arr[ndb_index_stat_max_evict_batch + 1];
1286  uint st_lru_cnt= 0;
1287  Ndb_index_stat *st_loop= list.head;
1288  while (st_loop != 0 && st_lru_cnt < batch)
1289  {
1290  Ndb_index_stat *st= st_loop;
1291  st_loop= st_loop->list_next;
1292  if (st->read_time + evict_delay <= pr.now)
1293  {
1294  /* Insertion sort into the batch from the end */
1295  if (st_lru_cnt == 0)
1296  st_lru_arr[st_lru_cnt++]= st;
1297  else
1298  {
1299  uint i= st_lru_cnt;
1300  while (i != 0)
1301  {
1302  if (st_lru_arr[i-1]->access_time < st->access_time)
1303  break;
1304  i--;
1305  }
1306  if (i < st_lru_cnt)
1307  {
1308  uint j= st_lru_cnt; /* There is place for one more at end */
1309  while (j > i)
1310  {
1311  st_lru_arr[j]= st_lru_arr[j-1];
1312  j--;
1313  }
1314  st_lru_arr[i]= st;
1315  if (st_lru_cnt < batch)
1316  st_lru_cnt++;
1317  }
1318  }
1319  }
1320  }
1321 
1322  /* Process the LRU batch */
1323  uint cnt= 0;
1324  while (cnt < st_lru_cnt)
1325  {
1326  if (!ndb_index_stat_proc_evict())
1327  break;
1328 
1329  Ndb_index_stat *st= st_lru_arr[cnt];
1330  DBUG_PRINT("index_stat", ("st %s proc evict %s", st->id, list.name));
1331  ndb_index_stat_proc_evict(pr, st);
1332  ndb_index_stat_free(st);
1333  cnt++;
1334  }
1335  if (cnt == batch)
1336  pr.busy= true;
1337 }
1338 
1339 void
1340 ndb_index_stat_proc_evict(Ndb_index_stat_proc &pr)
1341 {
1342  ndb_index_stat_proc_evict(pr, Ndb_index_stat::LT_Error);
1343  ndb_index_stat_proc_evict(pr, Ndb_index_stat::LT_Idle);
1344 }
1345 
1346 void
1347 ndb_index_stat_proc_delete(Ndb_index_stat_proc &pr)
1348 {
1349  const int lt= Ndb_index_stat::LT_Delete;
1350  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1351  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1352  const uint delete_batch= opt.get(Ndb_index_stat_opt::Idelete_batch);
1353  const uint batch= !pr.end ? delete_batch : 0xFFFFFFFF;
1354 
1355  Ndb_index_stat *st_loop= list.head;
1356  uint cnt= 0;
1357  while (st_loop != 0 && cnt < batch)
1358  {
1359  Ndb_index_stat *st= st_loop;
1360  st_loop= st_loop->list_next;
1361  DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
1362  ndb_index_stat_proc_evict(pr, st);
1363  ndb_index_stat_list_remove(st);
1364  delete st->is;
1365  delete st;
1366  cnt++;
1367  }
1368  if (cnt == batch)
1369  pr.busy= true;
1370 }
1371 
1372 void
1373 ndb_index_stat_proc_error(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
1374 {
1375  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1376  const int error_delay= opt.get(Ndb_index_stat_opt::Ierror_delay);
1377  const time_t error_wait= st->error_time + error_delay - pr.now;
1378 
1379  if (error_wait <= 0 ||
1380  /* Analyze issued after previous error */
1381  st->force_update)
1382  {
1383  DBUG_PRINT("index_stat", ("st %s error wait:%ds error count:%u"
1384  " force update:%u",
1385  st->id, (int)error_wait, st->error_count,
1386  st->force_update));
1387  ndb_index_stat_clear_error(st);
1388  if (st->force_update)
1389  pr.lt= Ndb_index_stat::LT_Update;
1390  else
1391  pr.lt= Ndb_index_stat::LT_Read;
1392  return;
1393  }
1394  pr.lt= Ndb_index_stat::LT_Error;
1395 }
1396 
1397 void
1398 ndb_index_stat_proc_error(Ndb_index_stat_proc &pr)
1399 {
1400  const int lt= Ndb_index_stat::LT_Error;
1401  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1402  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1403  const uint batch= opt.get(Ndb_index_stat_opt::Ierror_batch);
1404  pr.now= ndb_index_stat_time();
1405 
1406  Ndb_index_stat *st_loop= list.head;
1407  uint cnt= 0;
1408  while (st_loop != 0 && cnt < batch)
1409  {
1410  Ndb_index_stat *st= st_loop;
1411  st_loop= st_loop->list_next;
1412  DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
1413  ndb_index_stat_proc_error(pr, st);
1414  ndb_index_stat_list_move(st, pr.lt);
1415  cnt++;
1416  }
1417  if (cnt == batch)
1418  pr.busy= true;
1419 }
1420 
1421 void
1422 ndb_index_stat_proc_event(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
1423 {
1424  /*
1425  Put on Check list if idle.
1426  We get event also for our own analyze but this should not matter.
1427  */
1428  pr.lt= st->lt;
1429  if (st->lt == Ndb_index_stat::LT_Idle ||
1430  st->lt == Ndb_index_stat::LT_Error)
1431  pr.lt= Ndb_index_stat::LT_Check;
1432 }
1433 
1434 void
1435 ndb_index_stat_proc_event(Ndb_index_stat_proc &pr)
1436 {
1437  NdbIndexStat *is= pr.is_util;
1438  Ndb *ndb= pr.ndb;
1439  int ret;
1440  ret= is->poll_listener(ndb, 0);
1441  DBUG_PRINT("index_stat", ("poll_listener ret: %d", ret));
1442  if (ret == -1)
1443  {
1444  // wl4124_todo report error
1445  DBUG_ASSERT(false);
1446  return;
1447  }
1448  if (ret == 0)
1449  return;
1450 
1451  while (1)
1452  {
1453  ret= is->next_listener(ndb);
1454  DBUG_PRINT("index_stat", ("next_listener ret: %d", ret));
1455  if (ret == -1)
1456  {
1457  // wl4124_todo report error
1458  DBUG_ASSERT(false);
1459  return;
1460  }
1461  if (ret == 0)
1462  break;
1463 
1464  NdbIndexStat::Head head;
1465  is->get_head(head);
1466  DBUG_PRINT("index_stat", ("next_listener eventType: %d indexId: %u",
1467  head.m_eventType, head.m_indexId));
1468 
1469  Ndb_index_stat *st= ndb_index_stat_find_entry(head.m_indexId,
1470  head.m_indexVersion,
1471  head.m_tableId);
1472  /*
1473  Another process can update stats for an index which is not found
1474  in this mysqld. Ignore it.
1475  */
1476  if (st != 0)
1477  {
1478  DBUG_PRINT("index_stat", ("st %s proc %s", st->id, "Event"));
1479  ndb_index_stat_proc_event(pr, st);
1480  if (pr.lt != st->lt)
1481  ndb_index_stat_list_move(st, pr.lt);
1482  }
1483  else
1484  {
1485  DBUG_PRINT("index_stat", ("entry not found in this mysqld"));
1486  }
1487  }
1488 }
1489 
1490 #ifndef DBUG_OFF
1491 void
1492 ndb_index_stat_report(const Ndb_index_stat_glob& old_glob)
1493 {
1494  Ndb_index_stat_glob new_glob= ndb_index_stat_glob;
1495  new_glob.set_list_count();
1496 
1497  /* List counts */
1498  {
1499  const uint (&old_count)[Ndb_index_stat::LT_Count]= old_glob.list_count;
1500  const uint (&new_count)[Ndb_index_stat::LT_Count]= new_glob.list_count;
1501  bool any= false;
1502  int lt;
1503  for (lt=1; lt < Ndb_index_stat::LT_Count; lt++)
1504  {
1505  const Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1506  const char* name= list.name;
1507  if (old_count[lt] != new_count[lt])
1508  {
1509  DBUG_PRINT("index_stat", ("%s: %u -> %u",
1510  name, old_count[lt], new_count[lt]));
1511  any= true;
1512  }
1513  }
1514  if (any)
1515  {
1516  const uint bufsz= 20 * Ndb_index_stat::LT_Count;
1517  char buf[bufsz];
1518  char *ptr= buf;
1519  for (lt= 1; lt < Ndb_index_stat::LT_Count; lt++)
1520  {
1521  const Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1522  const char* name= list.name;
1523  sprintf(ptr, " %s:%u", name, new_count[lt]);
1524  ptr+= strlen(ptr);
1525  }
1526  DBUG_PRINT("index_stat", ("list:%s", buf));
1527  }
1528  }
1529 
1530  /* Cache summary */
1531  {
1532  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1533  uint query_size= new_glob.cache_query_bytes;
1534  uint clean_size= new_glob.cache_clean_bytes;
1535  uint total_size= query_size + clean_size;
1536  const uint limit= opt.get(Ndb_index_stat_opt::Icache_limit);
1537  double pct= 100.0;
1538  if (limit != 0)
1539  pct= 100.0 * (double)total_size / (double)limit;
1540  DBUG_PRINT("index_stat", ("cache query:%u clean:%u (%.2f pct)",
1541  query_size, clean_size, pct));
1542  }
1543 
1544  /* Updates waited for and forced updates */
1545  {
1546  uint wait_update= new_glob.wait_update;
1547  uint force_update= new_glob.force_update;
1548  uint no_stats= new_glob.no_stats;
1549  DBUG_PRINT("index_stat", ("wait update:%u force update:%u no stats:%u",
1550  wait_update, force_update, no_stats));
1551  }
1552 }
1553 #endif
1554 
1555 void
1556 ndb_index_stat_proc(Ndb_index_stat_proc &pr)
1557 {
1558 #ifndef DBUG_OFF
1559  Ndb_index_stat_glob old_glob= ndb_index_stat_glob;
1560  old_glob.set_list_count();
1561 #endif
1562 
1563  DBUG_ENTER("ndb_index_stat_proc");
1564 
1565  ndb_index_stat_proc_new(pr);
1566  ndb_index_stat_proc_update(pr);
1567  ndb_index_stat_proc_read(pr);
1568  ndb_index_stat_proc_idle(pr);
1569  ndb_index_stat_proc_check(pr);
1570  ndb_index_stat_proc_evict(pr);
1571  ndb_index_stat_proc_delete(pr);
1572  ndb_index_stat_proc_error(pr);
1573  ndb_index_stat_proc_event(pr);
1574 
1575 #ifndef DBUG_OFF
1576  ndb_index_stat_report(old_glob);
1577 #endif
1578  DBUG_VOID_RETURN;
1579 }
1580 
1581 void
1582 ndb_index_stat_end()
1583 {
1584  DBUG_ENTER("ndb_index_stat_end");
1585  Ndb_index_stat_proc pr;
1586  pr.end= true;
1587 
1588  /*
1589  * Shares have been freed so any index stat entries left should be
1590  * in LT_Delete. The first two steps here should be unnecessary.
1591  */
1592 
1593  ndb_index_stat_allow(0);
1594 
1595  int lt;
1596  for (lt= 1; lt < Ndb_index_stat::LT_Count; lt++)
1597  {
1598  if (lt == (int)Ndb_index_stat::LT_Delete)
1599  continue;
1600  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1601  Ndb_index_stat *st_loop= list.head;
1602  while (st_loop != 0)
1603  {
1604  Ndb_index_stat *st= st_loop;
1605  st_loop= st_loop->list_next;
1606  DBUG_PRINT("index_stat", ("st %s end %s", st->id, list.name));
1607  pr.lt= Ndb_index_stat::LT_Delete;
1608  ndb_index_stat_list_move(st, pr.lt);
1609  }
1610  }
1611 
1612  /* Real free */
1613  ndb_index_stat_proc_delete(pr);
1614  DBUG_VOID_RETURN;
1615 }
1616 
1617 /* Index stats thread */
1618 
1619 int
1620 ndb_index_stat_check_or_create_systables(Ndb_index_stat_proc &pr)
1621 {
1622  DBUG_ENTER("ndb_index_stat_check_or_create_systables");
1623 
1624  NdbIndexStat *is= pr.is_util;
1625  Ndb *ndb= pr.ndb;
1626 
1627  if (is->check_systables(ndb) == 0)
1628  {
1629  DBUG_PRINT("index_stat", ("using existing index stats tables"));
1630  DBUG_RETURN(0);
1631  }
1632 
1633  if (is->create_systables(ndb) == 0)
1634  {
1635  DBUG_PRINT("index_stat", ("created index stats tables"));
1636  DBUG_RETURN(0);
1637  }
1638 
1639  if (is->getNdbError().code == 721 ||
1640  is->getNdbError().code == 4244)
1641  {
1642  // race between mysqlds, maybe
1643  DBUG_PRINT("index_stat", ("create index stats tables failed: error %d line %d",
1644  is->getNdbError().code, is->getNdbError().line));
1645  DBUG_RETURN(-1);
1646  }
1647 
1648  sql_print_warning("create index stats tables failed: error %d line %d",
1649  is->getNdbError().code, is->getNdbError().line);
1650  DBUG_RETURN(-1);
1651 }
1652 
1653 int
1654 ndb_index_stat_check_or_create_sysevents(Ndb_index_stat_proc &pr)
1655 {
1656  DBUG_ENTER("ndb_index_stat_check_or_create_sysevents");
1657 
1658  NdbIndexStat *is= pr.is_util;
1659  Ndb *ndb= pr.ndb;
1660 
1661  if (is->check_sysevents(ndb) == 0)
1662  {
1663  DBUG_PRINT("index_stat", ("using existing index stats events"));
1664  DBUG_RETURN(0);
1665  }
1666 
1667  if (is->create_sysevents(ndb) == 0)
1668  {
1669  DBUG_PRINT("index_stat", ("created index stats events"));
1670  DBUG_RETURN(0);
1671  }
1672 
1673  if (is->getNdbError().code == 746)
1674  {
1675  // race between mysqlds, maybe
1676  DBUG_PRINT("index_stat", ("create index stats events failed: error %d line %d",
1677  is->getNdbError().code, is->getNdbError().line));
1678  DBUG_RETURN(-1);
1679  }
1680 
1681  sql_print_warning("create index stats events failed: error %d line %d",
1682  is->getNdbError().code, is->getNdbError().line);
1683  DBUG_RETURN(-1);
1684 }
1685 
1686 int
1687 ndb_index_stat_start_listener(Ndb_index_stat_proc &pr)
1688 {
1689  DBUG_ENTER("ndb_index_stat_start_listener");
1690 
1691  NdbIndexStat *is= pr.is_util;
1692  Ndb *ndb= pr.ndb;
1693 
1694  if (is->create_listener(ndb) == -1)
1695  {
1696  sql_print_warning("create index stats listener failed: error %d line %d",
1697  is->getNdbError().code, is->getNdbError().line);
1698  DBUG_RETURN(-1);
1699  }
1700 
1701  if (is->execute_listener(ndb) == -1)
1702  {
1703  sql_print_warning("execute index stats listener failed: error %d line %d",
1704  is->getNdbError().code, is->getNdbError().line);
1705  DBUG_RETURN(-1);
1706  }
1707 
1708  DBUG_RETURN(0);
1709 }
1710 
1711 int
1712 ndb_index_stat_stop_listener(Ndb_index_stat_proc &pr)
1713 {
1714  DBUG_ENTER("ndb_index_stat_stop_listener");
1715 
1716  NdbIndexStat *is= pr.is_util;
1717  Ndb *ndb= pr.ndb;
1718 
1719  if (is->drop_listener(ndb) == -1)
1720  {
1721  sql_print_warning("drop index stats listener failed: error %d line %d",
1722  is->getNdbError().code, is->getNdbError().line);
1723  DBUG_RETURN(-1);
1724  }
1725 
1726  DBUG_RETURN(0);
1727 }
1728 
1729 pthread_handler_t
1730 ndb_index_stat_thread_func(void *arg __attribute__((unused)))
1731 {
1732  THD *thd; /* needs to be first for thread_stack */
1733  struct timespec abstime;
1734  Thd_ndb *thd_ndb= NULL;
1735 
1736  my_thread_init();
1737  DBUG_ENTER("ndb_index_stat_thread_func");
1738 
1739  Ndb_index_stat_proc pr;
1740 
1741  bool have_listener;
1742  have_listener= false;
1743 
1744  // wl4124_todo remove useless stuff copied from utility thread
1745 
1746  pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
1747 
1748  thd= new THD; /* note that contructor of THD uses DBUG_ */
1749  if (thd == NULL)
1750  {
1751  my_errno= HA_ERR_OUT_OF_MEM;
1752  DBUG_RETURN(NULL);
1753  }
1754  THD_CHECK_SENTRY(thd);
1755  pthread_detach_this_thread();
1756  ndb_index_stat_thread= pthread_self();
1757 
1758  thd->thread_stack= (char*)&thd; /* remember where our stack is */
1759  if (thd->store_globals())
1760  goto ndb_index_stat_thread_fail;
1761  lex_start(thd);
1762  thd->init_for_queries();
1763 #ifndef NDB_THD_HAS_NO_VERSION
1764  thd->version=refresh_version;
1765 #endif
1766  thd->client_capabilities = 0;
1767  thd->security_ctx->skip_grants();
1768  my_net_init(&thd->net, 0);
1769 
1770  CHARSET_INFO *charset_connection;
1771  charset_connection= get_charset_by_csname("utf8",
1772  MY_CS_PRIMARY, MYF(MY_WME));
1773  thd->variables.character_set_client= charset_connection;
1774  thd->variables.character_set_results= charset_connection;
1775  thd->variables.collation_connection= charset_connection;
1776  thd->update_charset();
1777 
1778  /* Signal successful initialization */
1779  ndb_index_stat_thread_running= 1;
1780  pthread_cond_signal(&COND_ndb_index_stat_ready);
1781  pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
1782 
1783  /*
1784  wait for mysql server to start
1785  */
1786  mysql_mutex_lock(&LOCK_server_started);
1787  while (!mysqld_server_started)
1788  {
1789  set_timespec(abstime, 1);
1790  mysql_cond_timedwait(&COND_server_started, &LOCK_server_started,
1791  &abstime);
1792  if (ndbcluster_terminating)
1793  {
1794  mysql_mutex_unlock(&LOCK_server_started);
1795  pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
1796  goto ndb_index_stat_thread_end;
1797  }
1798  }
1799  mysql_mutex_unlock(&LOCK_server_started);
1800 
1801  /*
1802  Wait for cluster to start
1803  */
1804  pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
1805  while (!g_ndb_status.cluster_node_id && (ndbcluster_hton->slot != ~(uint)0))
1806  {
1807  /* ndb not connected yet */
1808  pthread_cond_wait(&COND_ndb_index_stat_thread, &LOCK_ndb_index_stat_thread);
1809  if (ndbcluster_terminating)
1810  goto ndb_index_stat_thread_end;
1811  }
1812  pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
1813 
1814  /* Get instance used for sys objects check and create */
1815  if (!(pr.is_util= new NdbIndexStat))
1816  {
1817  sql_print_error("Could not allocate NdbIndexStat is_util object");
1818  pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
1819  goto ndb_index_stat_thread_end;
1820  }
1821 
1822  /* Get thd_ndb for this thread */
1823  if (!(thd_ndb= Thd_ndb::seize(thd)))
1824  {
1825  sql_print_error("Could not allocate Thd_ndb object");
1826  pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
1827  goto ndb_index_stat_thread_end;
1828  }
1829  set_thd_ndb(thd, thd_ndb);
1830  thd_ndb->options|= TNO_NO_LOG_SCHEMA_OP;
1831  if (thd_ndb->ndb->setDatabaseName(NDB_INDEX_STAT_DB) == -1)
1832  {
1833  sql_print_error("Could not change index stats thd_ndb database to %s",
1835  pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
1836  goto ndb_index_stat_thread_end;
1837  }
1838  pr.ndb= thd_ndb->ndb;
1839 
1840  ndb_index_stat_allow(1);
1841  bool enable_ok;
1842  enable_ok= false;
1843 
1844  set_timespec(abstime, 0);
1845  for (;;)
1846  {
1847  pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
1848  if (!ndbcluster_terminating) {
1849  int ret= pthread_cond_timedwait(&COND_ndb_index_stat_thread,
1850  &LOCK_ndb_index_stat_thread,
1851  &abstime);
1852  const char* reason= ret == ETIMEDOUT ? "timed out" : "wake up";
1853  (void)reason; // USED
1854  DBUG_PRINT("index_stat", ("loop: %s", reason));
1855  }
1856  if (ndbcluster_terminating) /* Shutting down server */
1857  goto ndb_index_stat_thread_end;
1858  pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
1859 
1860  /* const bool enable_ok_new= THDVAR(NULL, index_stat_enable); */
1861  const bool enable_ok_new= ndb_index_stat_get_enable(NULL);
1862 
1863  do
1864  {
1865  if (enable_ok != enable_ok_new)
1866  {
1867  DBUG_PRINT("index_stat", ("global enable: %d -> %d",
1868  enable_ok, enable_ok_new));
1869 
1870  if (enable_ok_new)
1871  {
1872  // at enable check or create stats tables and events
1873  if (ndb_index_stat_check_or_create_systables(pr) == -1 ||
1874  ndb_index_stat_check_or_create_sysevents(pr) == -1 ||
1875  ndb_index_stat_start_listener(pr) == -1)
1876  {
1877  // try again in next loop
1878  break;
1879  }
1880  have_listener= true;
1881  }
1882  else
1883  {
1884  // not a normal use-case
1885  if (have_listener)
1886  {
1887  if (ndb_index_stat_stop_listener(pr) == 0)
1888  have_listener= false;
1889  }
1890  }
1891  enable_ok= enable_ok_new;
1892  }
1893 
1894  if (!enable_ok)
1895  break;
1896 
1897  pr.busy= false;
1898  ndb_index_stat_proc(pr);
1899  } while (0);
1900 
1901  /* Calculate new time to wake up */
1902 
1903  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1904  uint msecs= 0;
1905  if (!enable_ok)
1906  msecs= opt.get(Ndb_index_stat_opt::Iloop_checkon);
1907  else if (!pr.busy)
1908  msecs= opt.get(Ndb_index_stat_opt::Iloop_idle);
1909  else
1910  msecs= opt.get(Ndb_index_stat_opt::Iloop_busy);
1911  DBUG_PRINT("index_stat", ("sleep %dms", msecs));
1912 
1913  set_timespec_nsec(abstime, msecs * 1000000ULL);
1914  }
1915 
1916 ndb_index_stat_thread_end:
1917  net_end(&thd->net);
1918 
1919 ndb_index_stat_thread_fail:
1920  if (have_listener)
1921  {
1922  if (ndb_index_stat_stop_listener(pr) == 0)
1923  have_listener= false;
1924  }
1925  if (pr.is_util)
1926  {
1927  delete pr.is_util;
1928  pr.is_util= 0;
1929  }
1930  if (thd_ndb)
1931  {
1932  Thd_ndb::release(thd_ndb);
1933  set_thd_ndb(thd, NULL);
1934  }
1935  thd->cleanup();
1936  delete thd;
1937 
1938  /* signal termination */
1939  ndb_index_stat_thread_running= 0;
1940  pthread_cond_signal(&COND_ndb_index_stat_ready);
1941  pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
1942  DBUG_PRINT("exit", ("ndb_index_stat_thread"));
1943 
1944  DBUG_LEAVE;
1945  my_thread_end();
1946  pthread_exit(0);
1947  return NULL;
1948 }
1949 
1950 /* Optimizer queries */
1951 
1952 static ulonglong
1953 ndb_index_stat_round(double x)
1954 {
1955  char buf[100];
1956  if (x < 0.0)
1957  x= 0.0;
1958  // my_snprintf has no float and windows has no snprintf
1959  sprintf(buf, "%.0f", x);
1960  /* mysql provides strtoull */
1961  ulonglong n= strtoull(buf, 0, 10);
1962  return n;
1963 }
1964 
1965 int
1966 ndb_index_stat_wait(Ndb_index_stat *st,
1967  uint sample_version,
1968  bool from_analyze)
1969 {
1970  DBUG_ENTER("ndb_index_stat_wait");
1971 
1972  pthread_mutex_lock(&ndb_index_stat_stat_mutex);
1973  int err= 0;
1974  uint count= 0;
1975  struct timespec abstime;
1976  while (true)
1977  {
1978  int ret= 0;
1979  if (count == 0)
1980  {
1981  if (st->lt == Ndb_index_stat::LT_Error && !from_analyze)
1982  {
1983  err= Ndb_index_stat_error_HAS_ERROR;
1984  break;
1985  }
1986  ndb_index_stat_clear_error(st);
1987  }
1988  if (st->no_stats && !from_analyze)
1989  {
1990  /* Have detected no stats now or before */
1991  err= NdbIndexStat::NoIndexStats;
1992  break;
1993  }
1994  if (st->error.code != 0)
1995  {
1996  /* A new error has occured */
1997  err= st->error.code;
1998  break;
1999  }
2000  if (st->sample_version > sample_version)
2001  break;
2002  DBUG_PRINT("index_stat", ("st %s wait count:%u",
2003  st->id, ++count));
2004  pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
2005  pthread_cond_signal(&COND_ndb_index_stat_thread);
2006  pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
2007  set_timespec(abstime, 1);
2008  ret= pthread_cond_timedwait(&ndb_index_stat_stat_cond,
2009  &ndb_index_stat_stat_mutex,
2010  &abstime);
2011  if (ret != 0 && ret != ETIMEDOUT)
2012  {
2013  err= ret;
2014  break;
2015  }
2016  }
2017  pthread_mutex_unlock(&ndb_index_stat_stat_mutex);
2018  if (err != 0)
2019  {
2020  DBUG_PRINT("index_stat", ("st %s wait error: %d",
2021  st->id, err));
2022  DBUG_RETURN(err);
2023  }
2024  DBUG_PRINT("index_stat", ("st %s wait ok: sample_version %u -> %u",
2025  st->id, sample_version, st->sample_version));
2026  DBUG_RETURN(0);
2027 }
2028 
2029 int
2030 ha_ndbcluster::ndb_index_stat_query(uint inx,
2031  const key_range *min_key,
2032  const key_range *max_key,
2033  NdbIndexStat::Stat& stat,
2034  int from)
2035 {
2036  DBUG_ENTER("ha_ndbcluster::ndb_index_stat_query");
2037 
2038  const KEY *key_info= table->key_info + inx;
2039  const NDB_INDEX_DATA &data= m_index[inx];
2040  const NDBINDEX *index= data.index;
2041  DBUG_PRINT("index_stat", ("index: %u name: %s", inx, index->getName()));
2042 
2043  int err= 0;
2044 
2045  /* Create an IndexBound struct for the keys */
2047  compute_index_bounds(ib, key_info, min_key, max_key, from);
2048  ib.range_no= 0;
2049 
2050  Ndb_index_stat *st=
2051  ndb_index_stat_get_share(m_share, index, m_table, err, true, false);
2052  if (st == 0)
2053  DBUG_RETURN(err);
2054 
2055  /* Pass old version 0 so existing stats terminates wait at once */
2056  err= ndb_index_stat_wait(st, 0, false);
2057  if (err != 0)
2058  DBUG_RETURN(err);
2059 
2060  if (st->read_time == 0)
2061  {
2062  DBUG_PRINT("index_stat", ("no index stats"));
2063  pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
2064  pthread_cond_signal(&COND_ndb_index_stat_thread);
2065  pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
2066  DBUG_RETURN(NdbIndexStat::NoIndexStats);
2067  }
2068 
2069  uint8 bound_lo_buffer[NdbIndexStat::BoundBufferBytes];
2070  uint8 bound_hi_buffer[NdbIndexStat::BoundBufferBytes];
2071  NdbIndexStat::Bound bound_lo(st->is, bound_lo_buffer);
2072  NdbIndexStat::Bound bound_hi(st->is, bound_hi_buffer);
2073  NdbIndexStat::Range range(bound_lo, bound_hi);
2074 
2075  const NdbRecord* key_record= data.ndb_record_key;
2076  if (st->is->convert_range(range, key_record, &ib) == -1)
2077  {
2078  ndb_index_stat_error(st, "convert_range", __LINE__);
2079  DBUG_RETURN(st->error.code);
2080  }
2081  if (st->is->query_stat(range, stat) == -1)
2082  {
2083  /* Invalid cache - should remove the entry */
2084  ndb_index_stat_error(st, "query_stat", __LINE__);
2085  DBUG_RETURN(st->error.code);
2086  }
2087 
2088  DBUG_RETURN(0);
2089 }
2090 
2091 int
2092 ha_ndbcluster::ndb_index_stat_get_rir(uint inx,
2093  key_range *min_key,
2094  key_range *max_key,
2095  ha_rows *rows_out)
2096 {
2097  DBUG_ENTER("ha_ndbcluster::ndb_index_stat_get_rir");
2098  uint8 stat_buffer[NdbIndexStat::StatBufferBytes];
2099  NdbIndexStat::Stat stat(stat_buffer);
2100  int err= ndb_index_stat_query(inx, min_key, max_key, stat, 1);
2101  if (err == 0)
2102  {
2103  double rir= -1.0;
2104  NdbIndexStat::get_rir(stat, &rir);
2105  ha_rows rows= ndb_index_stat_round(rir);
2106  /* Estimate only so cannot return exact zero */
2107  if (rows == 0)
2108  rows= 1;
2109  *rows_out= rows;
2110 #ifndef DBUG_OFF
2111  char rule[NdbIndexStat::RuleBufferBytes];
2112  NdbIndexStat::get_rule(stat, rule);
2113 #endif
2114  DBUG_PRINT("index_stat", ("rir: %u rule: %s", (uint)rows, rule));
2115  DBUG_RETURN(0);
2116  }
2117  DBUG_RETURN(err);
2118 }
2119 
2120 int
2121 ha_ndbcluster::ndb_index_stat_set_rpk(uint inx)
2122 {
2123  DBUG_ENTER("ha_ndbcluster::ndb_index_stat_set_rpk");
2124 
2125  KEY *key_info= table->key_info + inx;
2126  int err= 0;
2127 
2128  uint8 stat_buffer[NdbIndexStat::StatBufferBytes];
2129  NdbIndexStat::Stat stat(stat_buffer);
2130  const key_range *min_key= 0;
2131  const key_range *max_key= 0;
2132  err= ndb_index_stat_query(inx, min_key, max_key, stat, 2);
2133  if (err == 0)
2134  {
2135  uint k;
2136  for (k= 0; k < key_info->user_defined_key_parts; k++)
2137  {
2138  double rpk= -1.0;
2139  NdbIndexStat::get_rpk(stat, k, &rpk);
2140  ulonglong recs= ndb_index_stat_round(rpk);
2141  key_info->rec_per_key[k]= (ulong)recs;
2142 #ifndef DBUG_OFF
2143  char rule[NdbIndexStat::RuleBufferBytes];
2144  NdbIndexStat::get_rule(stat, rule);
2145 #endif
2146  DBUG_PRINT("index_stat", ("rpk[%u]: %u rule: %s", k, (uint)recs, rule));
2147  }
2148  DBUG_RETURN(0);
2149  }
2150  DBUG_RETURN(err);
2151 }
2152 
2153 int
2154 ha_ndbcluster::ndb_index_stat_analyze(Ndb *ndb,
2155  uint *inx_list,
2156  uint inx_count)
2157 {
2158  DBUG_ENTER("ha_ndbcluster::ndb_index_stat_analyze");
2159 
2160  struct {
2161  uint sample_version;
2162  uint error_count;
2163  } old[MAX_INDEXES];
2164 
2165  int err= 0;
2166  uint i;
2167 
2168  /* Force stats update on each index */
2169  for (i= 0; i < inx_count; i++)
2170  {
2171  uint inx= inx_list[i];
2172  const NDB_INDEX_DATA &data= m_index[inx];
2173  const NDBINDEX *index= data.index;
2174  DBUG_PRINT("index_stat", ("force update: %s", index->getName()));
2175 
2176  Ndb_index_stat *st=
2177  ndb_index_stat_get_share(m_share, index, m_table, err, true, true);
2178  if (st == 0)
2179  DBUG_RETURN(err);
2180 
2181  old[i].sample_version= st->sample_version;
2182  old[i].error_count= st->error_count;
2183  }
2184 
2185  /* Wait for each update (or error) */
2186  for (i = 0; i < inx_count; i++)
2187  {
2188  uint inx= inx_list[i];
2189  const NDB_INDEX_DATA &data= m_index[inx];
2190  const NDBINDEX *index= data.index;
2191  DBUG_PRINT("index_stat", ("wait for update: %s", index->getName()));
2192 
2193  Ndb_index_stat *st=
2194  ndb_index_stat_get_share(m_share, index, m_table, err, false, false);
2195  if (st == 0)
2196  DBUG_RETURN(err);
2197 
2198  err= ndb_index_stat_wait(st, old[i].sample_version, true);
2199  if (err != 0)
2200  DBUG_RETURN(err);
2201  }
2202 
2203  DBUG_RETURN(0);
2204 }
2205 
2206 #endif