ha_ndbcluster.cc 213 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000-2003 MySQL AB
unknown's avatar
unknown committed
2 3 4 5 6 7 8 9 10 11 12 13 14

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
15
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
unknown's avatar
unknown committed
16 17 18 19 20 21 22
*/

/*
  This file defines the NDB Cluster handler: the interface between MySQL and
  NDB Cluster
*/

23
#ifdef USE_PRAGMA_IMPLEMENTATION
24
#pragma implementation				// gcc: Class implementation
unknown's avatar
unknown committed
25 26 27 28 29 30 31 32 33 34
#endif

#include "mysql_priv.h"

#ifdef HAVE_NDBCLUSTER_DB
#include <my_dir.h>
#include "ha_ndbcluster.h"
#include <ndbapi/NdbApi.hpp>
#include <ndbapi/NdbScanFilter.hpp>

35 36 37 38
// options from from mysqld.cc
extern my_bool opt_ndb_optimized_node_selection;
extern const char *opt_ndbcluster_connectstring;

unknown's avatar
unknown committed
39
// Default value for parallelism
40
static const int parallelism= 0;
unknown's avatar
unknown committed
41

42 43
// Default value for max number of transactions
// createable against NDB from this handler
44
static const int max_transactions= 2;
45

46 47
static const char *ha_ndb_ext=".ndb";

48 49 50 51
static int ndbcluster_close_connection(THD *thd);
static int ndbcluster_commit(THD *thd, bool all);
static int ndbcluster_rollback(THD *thd, bool all);

52
handlerton ndbcluster_hton = {
unknown's avatar
unknown committed
53
  "ndbcluster",
54 55 56 57
  SHOW_OPTION_YES,
  "Clustered, fault-tolerant, memory-based tables", 
  DB_TYPE_NDBCLUSTER,
  ndbcluster_init,
58 59 60 61 62 63 64 65 66
  0, /* slot */
  0, /* savepoint size */
  ndbcluster_close_connection,
  NULL, /* savepoint_set */
  NULL, /* savepoint_rollback */
  NULL, /* savepoint_release */
  ndbcluster_commit,
  ndbcluster_rollback,
  NULL, /* prepare */
67 68
  NULL, /* recover */
  NULL, /* commit_by_xid */
69
  NULL, /* rollback_by_xid */
70 71 72
  NULL, /* create_cursor_read_view */
  NULL, /* set_cursor_read_view */
  NULL, /* close_cursor_read_view */
73
  HTON_NO_FLAGS
74 75
};

unknown's avatar
unknown committed
76
#define NDB_HIDDEN_PRIMARY_KEY_LENGTH 8
77

78
#define NDB_FAILED_AUTO_INCREMENT ~(Uint64)0
79
#define NDB_AUTO_INCREMENT_RETRIES 10
unknown's avatar
unknown committed
80

81 82
#define NDB_INVALID_SCHEMA_OBJECT 241

unknown's avatar
unknown committed
83
#define ERR_PRINT(err) \
84
  DBUG_PRINT("error", ("%d  message: %s", err.code, err.message))
unknown's avatar
unknown committed
85

86 87
#define ERR_RETURN(err)                  \
{                                        \
unknown's avatar
unknown committed
88
  const NdbError& tmp= err;              \
89
  ERR_PRINT(tmp);                        \
unknown's avatar
unknown committed
90
  DBUG_RETURN(ndb_to_mysql_error(&tmp)); \
unknown's avatar
unknown committed
91 92 93 94
}

// Typedefs for long names
typedef NdbDictionary::Column NDBCOL;
unknown's avatar
unknown committed
95
typedef NdbDictionary::Table NDBTAB;
unknown's avatar
unknown committed
96 97 98
typedef NdbDictionary::Index  NDBINDEX;
typedef NdbDictionary::Dictionary  NDBDICT;

unknown's avatar
unknown committed
99
bool ndbcluster_inited= FALSE;
unknown's avatar
unknown committed
100

101
static Ndb* g_ndb= NULL;
102
static Ndb_cluster_connection* g_ndb_cluster_connection= NULL;
103

unknown's avatar
unknown committed
104 105 106 107 108 109 110 111 112 113 114 115 116
// Handler synchronization
pthread_mutex_t ndbcluster_mutex;

// Table lock handling
static HASH ndbcluster_open_tables;

static byte *ndbcluster_get_key(NDB_SHARE *share,uint *length,
                                my_bool not_used __attribute__((unused)));
static NDB_SHARE *get_share(const char *table_name);
static void free_share(NDB_SHARE *share);

static int packfrm(const void *data, uint len, const void **pack_data, uint *pack_len);
static int unpackfrm(const void **data, uint *len,
117
                     const void* pack_data);
unknown's avatar
unknown committed
118

unknown's avatar
unknown committed
119
static int ndb_get_table_statistics(Ndb*, const char *, 
120
                                    struct Ndb_statistics *);
121

unknown's avatar
Merge  
unknown committed
122 123 124 125
// Util thread variables
static pthread_t ndb_util_thread;
pthread_mutex_t LOCK_ndb_util_thread;
pthread_cond_t COND_ndb_util_thread;
126
pthread_handler_t ndb_util_thread_func(void *arg);
unknown's avatar
Merge  
unknown committed
127
ulong ndb_cache_check_time;
unknown's avatar
unknown committed
128

129 130 131 132
/*
  Dummy buffer to read zero pack_length fields
  which are mapped to 1 char
*/
unknown's avatar
unknown committed
133
static uint32 dummy_buf;
134

135 136 137 138 139 140 141 142 143 144 145
/*
  Stats that can be retrieved from ndb
*/

struct Ndb_statistics {
  Uint64 row_count;
  Uint64 commit_count;
  Uint64 row_size;
  Uint64 fragment_memory;
};

146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
/* Status variables shown with 'show status like 'Ndb%' */

static long ndb_cluster_node_id= 0;
static const char * ndb_connected_host= 0;
static long ndb_connected_port= 0;
static long ndb_number_of_replicas= 0;
static long ndb_number_of_storage_nodes= 0;

static int update_status_variables(Ndb_cluster_connection *c)
{
  ndb_cluster_node_id=         c->node_id();
  ndb_connected_port=          c->get_connected_port();
  ndb_connected_host=          c->get_connected_host();
  ndb_number_of_replicas=      0;
  ndb_number_of_storage_nodes= c->no_db_nodes();
  return 0;
}

struct show_var_st ndb_status_variables[]= {
  {"cluster_node_id",        (char*) &ndb_cluster_node_id,         SHOW_LONG},
  {"connected_host",         (char*) &ndb_connected_host,      SHOW_CHAR_PTR},
  {"connected_port",         (char*) &ndb_connected_port,          SHOW_LONG},
//  {"number_of_replicas",     (char*) &ndb_number_of_replicas,      SHOW_LONG},
  {"number_of_storage_nodes",(char*) &ndb_number_of_storage_nodes, SHOW_LONG},
  {NullS, NullS, SHOW_LONG}
};

unknown's avatar
unknown committed
173 174 175 176 177 178 179 180
/*
  Error handling functions
*/

struct err_code_mapping
{
  int ndb_err;
  int my_err;
181
  int show_warning;
unknown's avatar
unknown committed
182 183 184 185
};

static const err_code_mapping err_map[]= 
{
186 187
  { 626, HA_ERR_KEY_NOT_FOUND, 0 },
  { 630, HA_ERR_FOUND_DUPP_KEY, 0 },
unknown's avatar
unknown committed
188
  { 893, HA_ERR_FOUND_DUPP_KEY, 0 },
189 190 191
  { 721, HA_ERR_TABLE_EXIST, 1 },
  { 4244, HA_ERR_TABLE_EXIST, 1 },

192
  { 709, HA_ERR_NO_SUCH_TABLE, 0 },
193 194 195 196 197 198 199 200 201 202 203 204 205 206

  { 266, HA_ERR_LOCK_WAIT_TIMEOUT, 1 },
  { 274, HA_ERR_LOCK_WAIT_TIMEOUT, 1 },
  { 296, HA_ERR_LOCK_WAIT_TIMEOUT, 1 },
  { 297, HA_ERR_LOCK_WAIT_TIMEOUT, 1 },
  { 237, HA_ERR_LOCK_WAIT_TIMEOUT, 1 },

  { 623, HA_ERR_RECORD_FILE_FULL, 1 },
  { 624, HA_ERR_RECORD_FILE_FULL, 1 },
  { 625, HA_ERR_RECORD_FILE_FULL, 1 },
  { 826, HA_ERR_RECORD_FILE_FULL, 1 },
  { 827, HA_ERR_RECORD_FILE_FULL, 1 },
  { 832, HA_ERR_RECORD_FILE_FULL, 1 },

207 208
  { 284, HA_ERR_TABLE_DEF_CHANGED, 0 },

209 210 211
  { 0, 1, 0 },

  { -1, -1, 1 }
unknown's avatar
unknown committed
212 213 214 215 216 217
};


static int ndb_to_mysql_error(const NdbError *err)
{
  uint i;
218 219
  for (i=0; err_map[i].ndb_err != err->code && err_map[i].my_err != -1; i++);
  if (err_map[i].show_warning)
unknown's avatar
unknown committed
220
  {
221 222
    // Push the NDB error message as warning
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
223 224
                        ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
                        err->code, err->message, "NDB");
unknown's avatar
unknown committed
225
  }
226 227
  if (err_map[i].my_err == -1)
    return err->code;
unknown's avatar
unknown committed
228 229 230 231
  return err_map[i].my_err;
}


unknown's avatar
unknown committed
232 233

inline
234
int execute_no_commit(ha_ndbcluster *h, NdbTransaction *trans)
unknown's avatar
unknown committed
235
{
unknown's avatar
unknown committed
236
#ifdef NOT_USED
237
  int m_batch_execute= 0;
unknown's avatar
unknown committed
238
  if (m_batch_execute)
unknown's avatar
unknown committed
239
    return 0;
unknown's avatar
unknown committed
240
#endif
241
  return trans->execute(NdbTransaction::NoCommit,
242 243
                        NdbTransaction::AbortOnError,
                        h->m_force_send);
unknown's avatar
unknown committed
244 245 246
}

inline
247
int execute_commit(ha_ndbcluster *h, NdbTransaction *trans)
unknown's avatar
unknown committed
248
{
unknown's avatar
unknown committed
249
#ifdef NOT_USED
250
  int m_batch_execute= 0;
unknown's avatar
unknown committed
251
  if (m_batch_execute)
unknown's avatar
unknown committed
252
    return 0;
unknown's avatar
unknown committed
253
#endif
254
  return trans->execute(NdbTransaction::Commit,
255 256
                        NdbTransaction::AbortOnError,
                        h->m_force_send);
257 258 259
}

inline
260
int execute_commit(THD *thd, NdbTransaction *trans)
261 262
{
#ifdef NOT_USED
263
  int m_batch_execute= 0;
264 265 266
  if (m_batch_execute)
    return 0;
#endif
267
  return trans->execute(NdbTransaction::Commit,
268 269
                        NdbTransaction::AbortOnError,
                        thd->variables.ndb_force_send);
unknown's avatar
unknown committed
270 271 272
}

inline
273
int execute_no_commit_ie(ha_ndbcluster *h, NdbTransaction *trans)
unknown's avatar
unknown committed
274
{
unknown's avatar
unknown committed
275
#ifdef NOT_USED
276
  int m_batch_execute= 0;
unknown's avatar
unknown committed
277
  if (m_batch_execute)
unknown's avatar
unknown committed
278
    return 0;
unknown's avatar
unknown committed
279
#endif
280
  return trans->execute(NdbTransaction::NoCommit,
281 282
                        NdbTransaction::AO_IgnoreError,
                        h->m_force_send);
unknown's avatar
unknown committed
283 284
}

unknown's avatar
unknown committed
285 286 287
/*
  Place holder for ha_ndbcluster thread specific data
*/
288 289
Thd_ndb::Thd_ndb()
{
290
  ndb= new Ndb(g_ndb_cluster_connection, "");
291 292
  lock_count= 0;
  count= 0;
293 294
  all= NULL;
  stmt= NULL;
295
  error= 0;
296 297 298 299
}

Thd_ndb::~Thd_ndb()
{
300
  if (ndb)
301 302 303 304 305 306 307 308 309 310 311 312 313
  {
#ifndef DBUG_OFF
    Ndb::Free_list_usage tmp; tmp.m_name= 0;
    while (ndb->get_free_list_usage(&tmp))
    {
      uint leaked= (uint) tmp.m_created - tmp.m_free;
      if (leaked)
        fprintf(stderr, "NDB: Found %u %s%s that %s not been released\n",
                leaked, tmp.m_name,
                (leaked == 1)?"":"'s",
                (leaked == 1)?"has":"have");
    }
#endif
314
    delete ndb;
315
  }
316 317
  ndb= NULL;
  changed_tables.empty();
318 319
}

320 321 322 323 324 325 326 327
inline
Thd_ndb *
get_thd_ndb(THD *thd) { return (Thd_ndb *) thd->ha_data[ndbcluster_hton.slot]; }

inline
void
set_thd_ndb(THD *thd, Thd_ndb *thd_ndb) { thd->ha_data[ndbcluster_hton.slot]= thd_ndb; }

328 329 330
inline
Ndb *ha_ndbcluster::get_ndb()
{
331
  return get_thd_ndb(current_thd)->ndb;
332 333 334 335 336 337
}

/*
 * manage uncommitted insert/deletes during transactio to get records correct
 */

338
struct Ndb_local_table_statistics {
339
  int no_uncommitted_rows_count;
340
  ulong last_count;
341 342 343
  ha_rows records;
};

unknown's avatar
unknown committed
344 345 346
void ha_ndbcluster::set_rec_per_key()
{
  DBUG_ENTER("ha_ndbcluster::get_status_const");
347
  for (uint i=0 ; i < table->s->keys ; i++)
unknown's avatar
unknown committed
348 349 350 351 352 353
  {
    table->key_info[i].rec_per_key[table->key_info[i].key_parts-1]= 1;
  }
  DBUG_VOID_RETURN;
}

354 355
void ha_ndbcluster::records_update()
{
356 357
  if (m_ha_not_exact_count)
    return;
358
  DBUG_ENTER("ha_ndbcluster::records_update");
359 360
  struct Ndb_local_table_statistics *info= 
    (struct Ndb_local_table_statistics *)m_table_info;
361
  DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
362 363
                      ((const NDBTAB *)m_table)->getTableId(),
                      info->no_uncommitted_rows_count));
364
  //  if (info->records == ~(ha_rows)0)
365
  {
366
    Ndb *ndb= get_ndb();
367
    struct Ndb_statistics stat;
unknown's avatar
unknown committed
368
    if (ndb_get_table_statistics(ndb, m_tabname, &stat) == 0){
369 370 371
      mean_rec_length= stat.row_size;
      data_file_length= stat.fragment_memory;
      info->records= stat.row_count;
372 373
    }
  }
374 375
  {
    THD *thd= current_thd;
376
    if (get_thd_ndb(thd)->error)
377 378
      info->no_uncommitted_rows_count= 0;
  }
379 380 381 382
  records= info->records+ info->no_uncommitted_rows_count;
  DBUG_VOID_RETURN;
}

383 384
void ha_ndbcluster::no_uncommitted_rows_execute_failure()
{
385 386
  if (m_ha_not_exact_count)
    return;
387
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_execute_failure");
388
  get_thd_ndb(current_thd)->error= 1;
389 390 391
  DBUG_VOID_RETURN;
}

392 393
void ha_ndbcluster::no_uncommitted_rows_init(THD *thd)
{
394 395
  if (m_ha_not_exact_count)
    return;
396
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_init");
397 398
  struct Ndb_local_table_statistics *info= 
    (struct Ndb_local_table_statistics *)m_table_info;
399
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
400
  if (info->last_count != thd_ndb->count)
401
  {
402
    info->last_count= thd_ndb->count;
403 404 405
    info->no_uncommitted_rows_count= 0;
    info->records= ~(ha_rows)0;
    DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
406 407
                        ((const NDBTAB *)m_table)->getTableId(),
                        info->no_uncommitted_rows_count));
408 409 410 411 412 413
  }
  DBUG_VOID_RETURN;
}

void ha_ndbcluster::no_uncommitted_rows_update(int c)
{
414 415
  if (m_ha_not_exact_count)
    return;
416
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_update");
417 418
  struct Ndb_local_table_statistics *info=
    (struct Ndb_local_table_statistics *)m_table_info;
419 420
  info->no_uncommitted_rows_count+= c;
  DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
421 422
                      ((const NDBTAB *)m_table)->getTableId(),
                      info->no_uncommitted_rows_count));
423 424 425 426 427
  DBUG_VOID_RETURN;
}

void ha_ndbcluster::no_uncommitted_rows_reset(THD *thd)
{
428 429
  if (m_ha_not_exact_count)
    return;
430
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_reset");
431 432 433
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  thd_ndb->count++;
  thd_ndb->error= 0;
434 435 436
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
437 438
/*
  Take care of the error that occured in NDB
439

unknown's avatar
unknown committed
440
  RETURN
441
    0   No error
unknown's avatar
unknown committed
442 443 444
    #   The mapped error code
*/

445
void ha_ndbcluster::invalidate_dictionary_cache(bool global)
446 447
{
  NDBDICT *dict= get_ndb()->getDictionary();
448
  DBUG_ENTER("invalidate_dictionary_cache");
449
  DBUG_PRINT("info", ("invalidating %s", m_tabname));
450

451
  if (global)
452
  {
453 454 455 456
    const NDBTAB *tab= dict->getTable(m_tabname);
    if (!tab)
      DBUG_VOID_RETURN;
    if (tab->getObjectStatus() == NdbDictionary::Object::Invalid)
457 458 459 460 461 462 463 464
    {
      // Global cache has already been invalidated
      dict->removeCachedTable(m_tabname);
      global= FALSE;
    }
    else
      dict->invalidateTable(m_tabname);
  }
465 466
  else
    dict->removeCachedTable(m_tabname);
unknown's avatar
unknown committed
467
  table->s->version=0L;			/* Free when thread is ready */
468
  /* Invalidate indexes */
unknown's avatar
unknown committed
469
  for (uint i= 0; i < table->s->keys; i++)
470 471 472 473 474
  {
    NDBINDEX *index = (NDBINDEX *) m_index[i].index;
    NDBINDEX *unique_index = (NDBINDEX *) m_index[i].unique_index;
    NDB_INDEX_TYPE idx_type= m_index[i].type;

unknown's avatar
unknown committed
475 476 477
    switch (idx_type) {
    case PRIMARY_KEY_ORDERED_INDEX:
    case ORDERED_INDEX:
478 479 480 481
      if (global)
        dict->invalidateIndex(index->getName(), m_tabname);
      else
        dict->removeCachedIndex(index->getName(), m_tabname);
unknown's avatar
unknown committed
482
      break;
unknown's avatar
unknown committed
483
    case UNIQUE_ORDERED_INDEX:
484 485 486 487
      if (global)
        dict->invalidateIndex(index->getName(), m_tabname);
      else
        dict->removeCachedIndex(index->getName(), m_tabname);
unknown's avatar
unknown committed
488
    case UNIQUE_INDEX:
489 490 491 492
      if (global)
        dict->invalidateIndex(unique_index->getName(), m_tabname);
      else
        dict->removeCachedIndex(unique_index->getName(), m_tabname);
493
      break;
unknown's avatar
unknown committed
494 495
    case PRIMARY_KEY_INDEX:
    case UNDEFINED_INDEX:
496 497 498
      break;
    }
  }
499
  DBUG_VOID_RETURN;
500
}
501

502
int ha_ndbcluster::ndb_err(NdbTransaction *trans)
unknown's avatar
unknown committed
503
{
504
  int res;
505
  NdbError err= trans->getNdbError();
unknown's avatar
unknown committed
506 507 508 509 510
  DBUG_ENTER("ndb_err");
  
  ERR_PRINT(err);
  switch (err.classification) {
  case NdbError::SchemaError:
511
    invalidate_dictionary_cache(TRUE);
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527

    if (err.code==284)
    {
      /*
         Check if the table is _really_ gone or if the table has
         been alterend and thus changed table id
       */
      NDBDICT *dict= get_ndb()->getDictionary();
      DBUG_PRINT("info", ("Check if table %s is really gone", m_tabname));
      if (!(dict->getTable(m_tabname)))
      {
        err= dict->getNdbError();
        DBUG_PRINT("info", ("Table not found, error: %d", err.code));
        if (err.code != 709)
          DBUG_RETURN(1);
      }
528
      DBUG_PRINT("info", ("Table exists but must have changed"));
529
    }
unknown's avatar
unknown committed
530 531 532 533
    break;
  default:
    break;
  }
534 535
  res= ndb_to_mysql_error(&err);
  DBUG_PRINT("info", ("transformed ndbcluster error %d to mysql error %d", 
536
                      err.code, res));
537
  if (res == HA_ERR_FOUND_DUPP_KEY)
538 539
  {
    if (m_rows_to_insert == 1)
unknown's avatar
Merge  
unknown committed
540
      m_dupkey= table->s->primary_key;
541
    else
unknown's avatar
unknown committed
542 543
    {
      /* We are batching inserts, offending key is not available */
544
      m_dupkey= (uint) -1;
unknown's avatar
unknown committed
545
    }
546
  }
547
  DBUG_RETURN(res);
unknown's avatar
unknown committed
548 549 550
}


551
/*
552
  Override the default get_error_message in order to add the 
553 554 555
  error message of NDB 
 */

556
bool ha_ndbcluster::get_error_message(int error, 
557
                                      String *buf)
558
{
559
  DBUG_ENTER("ha_ndbcluster::get_error_message");
560
  DBUG_PRINT("enter", ("error: %d", error));
561

562
  Ndb *ndb= get_ndb();
563
  if (!ndb)
unknown's avatar
unknown committed
564
    DBUG_RETURN(FALSE);
565

566
  const NdbError err= ndb->getNdbError(error);
567 568 569 570
  bool temporary= err.status==NdbError::TemporaryError;
  buf->set(err.message, strlen(err.message), &my_charset_bin);
  DBUG_PRINT("exit", ("message: %s, temporary: %d", buf->ptr(), temporary));
  DBUG_RETURN(temporary);
571 572 573
}


unknown's avatar
unknown committed
574
#ifndef DBUG_OFF
unknown's avatar
unknown committed
575 576 577 578
/*
  Check if type is supported by NDB.
*/

unknown's avatar
unknown committed
579
static bool ndb_supported_type(enum_field_types type)
unknown's avatar
unknown committed
580 581
{
  switch (type) {
unknown's avatar
unknown committed
582 583 584 585 586 587 588
  case MYSQL_TYPE_TINY:        
  case MYSQL_TYPE_SHORT:
  case MYSQL_TYPE_LONG:
  case MYSQL_TYPE_INT24:       
  case MYSQL_TYPE_LONGLONG:
  case MYSQL_TYPE_FLOAT:
  case MYSQL_TYPE_DOUBLE:
589 590
  case MYSQL_TYPE_DECIMAL:    
  case MYSQL_TYPE_NEWDECIMAL:
unknown's avatar
unknown committed
591 592 593 594 595 596 597 598
  case MYSQL_TYPE_TIMESTAMP:
  case MYSQL_TYPE_DATETIME:    
  case MYSQL_TYPE_DATE:
  case MYSQL_TYPE_NEWDATE:
  case MYSQL_TYPE_TIME:        
  case MYSQL_TYPE_YEAR:        
  case MYSQL_TYPE_STRING:      
  case MYSQL_TYPE_VAR_STRING:
unknown's avatar
unknown committed
599
  case MYSQL_TYPE_VARCHAR:
unknown's avatar
unknown committed
600 601 602 603 604 605
  case MYSQL_TYPE_TINY_BLOB:
  case MYSQL_TYPE_BLOB:    
  case MYSQL_TYPE_MEDIUM_BLOB:   
  case MYSQL_TYPE_LONG_BLOB:  
  case MYSQL_TYPE_ENUM:
  case MYSQL_TYPE_SET:         
606
  case MYSQL_TYPE_BIT:
607
  case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
608
    return TRUE;
unknown's avatar
unknown committed
609
  case MYSQL_TYPE_NULL:   
unknown's avatar
unknown committed
610
    break;
unknown's avatar
unknown committed
611
  }
unknown's avatar
unknown committed
612
  return FALSE;
unknown's avatar
unknown committed
613
}
unknown's avatar
unknown committed
614
#endif /* !DBUG_OFF */
unknown's avatar
unknown committed
615 616


unknown's avatar
unknown committed
617 618 619 620 621
/*
  Instruct NDB to set the value of the hidden primary key
*/

bool ha_ndbcluster::set_hidden_key(NdbOperation *ndb_op,
622
                                   uint fieldnr, const byte *field_ptr)
unknown's avatar
unknown committed
623 624 625
{
  DBUG_ENTER("set_hidden_key");
  DBUG_RETURN(ndb_op->equal(fieldnr, (char*)field_ptr,
626
                            NDB_HIDDEN_PRIMARY_KEY_LENGTH) != 0);
unknown's avatar
unknown committed
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643
}


/*
  Instruct NDB to set the value of one primary key attribute
*/

int ha_ndbcluster::set_ndb_key(NdbOperation *ndb_op, Field *field,
                               uint fieldnr, const byte *field_ptr)
{
  uint32 pack_len= field->pack_length();
  DBUG_ENTER("set_ndb_key");
  DBUG_PRINT("enter", ("%d: %s, ndb_type: %u, len=%d", 
                       fieldnr, field->field_name, field->type(),
                       pack_len));
  DBUG_DUMP("key", (char*)field_ptr, pack_len);
  
unknown's avatar
unknown committed
644 645 646 647
  DBUG_ASSERT(ndb_supported_type(field->type()));
  DBUG_ASSERT(! (field->flags & BLOB_FLAG));
  // Common implementation for most field types
  DBUG_RETURN(ndb_op->equal(fieldnr, (char*) field_ptr, pack_len) != 0);
unknown's avatar
unknown committed
648 649 650 651 652 653 654 655
}


/*
 Instruct NDB to set the value of one attribute
*/

int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field, 
656
                                 uint fieldnr, bool *set_blob_value)
unknown's avatar
unknown committed
657 658 659 660 661 662 663 664
{
  const byte* field_ptr= field->ptr;
  uint32 pack_len=  field->pack_length();
  DBUG_ENTER("set_ndb_value");
  DBUG_PRINT("enter", ("%d: %s, type: %u, len=%d, is_null=%s", 
                       fieldnr, field->field_name, field->type(), 
                       pack_len, field->is_null()?"Y":"N"));
  DBUG_DUMP("value", (char*) field_ptr, pack_len);
unknown's avatar
unknown committed
665

unknown's avatar
unknown committed
666
  DBUG_ASSERT(ndb_supported_type(field->type()));
unknown's avatar
unknown committed
667
  {
668
    // ndb currently does not support size 0
unknown's avatar
unknown committed
669
    uint32 empty_field;
670 671
    if (pack_len == 0)
    {
unknown's avatar
unknown committed
672 673 674
      pack_len= sizeof(empty_field);
      field_ptr= (byte *)&empty_field;
      if (field->is_null())
675
        empty_field= 0;
unknown's avatar
unknown committed
676
      else
677
        empty_field= 1;
678
    }
unknown's avatar
unknown committed
679 680
    if (! (field->flags & BLOB_FLAG))
    {
681 682
      if (field->type() != MYSQL_TYPE_BIT)
      {
683 684 685 686 687 688 689
        if (field->is_null())
          // Set value to NULL
          DBUG_RETURN((ndb_op->setValue(fieldnr, 
                                        (char*)NULL, pack_len) != 0));
        // Common implementation for most field types
        DBUG_RETURN(ndb_op->setValue(fieldnr, 
                                     (char*)field_ptr, pack_len) != 0);
690 691 692
      }
      else // if (field->type() == MYSQL_TYPE_BIT)
      {
693
        longlong bits= field->val_int();
694
 
695 696
        // Round up bit field length to nearest word boundry
        pack_len= ((pack_len + 3) >> 2) << 2;
697 698 699 700 701
        DBUG_ASSERT(pack_len <= 8);
        if (field->is_null())
          // Set value to NULL
          DBUG_RETURN((ndb_op->setValue(fieldnr, (char*)NULL, pack_len) != 0));
        DBUG_PRINT("info", ("bit field"));
702
        DBUG_DUMP("value", (char*)&bits, pack_len);
703
#ifdef WORDS_BIGENDIAN
704 705 706 707 708
        if (pack_len < 5)
        {
          DBUG_RETURN(ndb_op->setValue(fieldnr, 
                                       ((char*)&bits)+4, pack_len) != 0);
        }
709
#endif
710
        DBUG_RETURN(ndb_op->setValue(fieldnr, (char*)&bits, pack_len) != 0);
711
      }
unknown's avatar
unknown committed
712 713
    }
    // Blob type
714
    NdbBlob *ndb_blob= ndb_op->getBlobHandle(fieldnr);
unknown's avatar
unknown committed
715 716 717 718 719 720 721 722 723 724 725 726
    if (ndb_blob != NULL)
    {
      if (field->is_null())
        DBUG_RETURN(ndb_blob->setNull() != 0);

      Field_blob *field_blob= (Field_blob*)field;

      // Get length and pointer to data
      uint32 blob_len= field_blob->get_length(field_ptr);
      char* blob_ptr= NULL;
      field_blob->get_ptr(&blob_ptr);

727 728 729
      // Looks like NULL ptr signals length 0 blob
      if (blob_ptr == NULL) {
        DBUG_ASSERT(blob_len == 0);
730
        blob_ptr= (char*)"";
731
      }
unknown's avatar
unknown committed
732

unknown's avatar
unknown committed
733 734
      DBUG_PRINT("value", ("set blob ptr=%p len=%u",
                           blob_ptr, blob_len));
unknown's avatar
unknown committed
735 736
      DBUG_DUMP("value", (char*)blob_ptr, min(blob_len, 26));

737
      if (set_blob_value)
738
        *set_blob_value= TRUE;
unknown's avatar
unknown committed
739 740 741 742
      // No callback needed to write value
      DBUG_RETURN(ndb_blob->setValue(blob_ptr, blob_len) != 0);
    }
    DBUG_RETURN(1);
unknown's avatar
unknown committed
743
  }
unknown's avatar
unknown committed
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
}


/*
  Callback to read all blob values.
  - not done in unpack_record because unpack_record is valid
    after execute(Commit) but reading blobs is not
  - may only generate read operations; they have to be executed
    somewhere before the data is available
  - due to single buffer for all blobs, we let the last blob
    process all blobs (last so that all are active)
  - null bit is still set in unpack_record
  - TODO allocate blob part aligned buffers
*/

unknown's avatar
unknown committed
759
NdbBlob::ActiveHook g_get_ndb_blobs_value;
unknown's avatar
unknown committed
760

unknown's avatar
unknown committed
761
int g_get_ndb_blobs_value(NdbBlob *ndb_blob, void *arg)
unknown's avatar
unknown committed
762
{
unknown's avatar
unknown committed
763
  DBUG_ENTER("g_get_ndb_blobs_value");
unknown's avatar
unknown committed
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
  if (ndb_blob->blobsNextBlob() != NULL)
    DBUG_RETURN(0);
  ha_ndbcluster *ha= (ha_ndbcluster *)arg;
  DBUG_RETURN(ha->get_ndb_blobs_value(ndb_blob));
}

int ha_ndbcluster::get_ndb_blobs_value(NdbBlob *last_ndb_blob)
{
  DBUG_ENTER("get_ndb_blobs_value");

  // Field has no field number so cannot use TABLE blob_field
  // Loop twice, first only counting total buffer size
  for (int loop= 0; loop <= 1; loop++)
  {
    uint32 offset= 0;
779
    for (uint i= 0; i < table->s->fields; i++)
unknown's avatar
unknown committed
780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
    {
      Field *field= table->field[i];
      NdbValue value= m_value[i];
      if (value.ptr != NULL && (field->flags & BLOB_FLAG))
      {
        Field_blob *field_blob= (Field_blob *)field;
        NdbBlob *ndb_blob= value.blob;
        Uint64 blob_len= 0;
        if (ndb_blob->getLength(blob_len) != 0)
          DBUG_RETURN(-1);
        // Align to Uint64
        uint32 blob_size= blob_len;
        if (blob_size % 8 != 0)
          blob_size+= 8 - blob_size % 8;
        if (loop == 1)
        {
796
          char *buf= m_blobs_buffer + offset;
unknown's avatar
unknown committed
797 798
          uint32 len= 0xffffffff;  // Max uint32
          DBUG_PRINT("value", ("read blob ptr=%x len=%u",
unknown's avatar
unknown committed
799
                               (UintPtr)buf, (uint)blob_len));
unknown's avatar
unknown committed
800 801 802 803 804 805 806 807
          if (ndb_blob->readData(buf, len) != 0)
            DBUG_RETURN(-1);
          DBUG_ASSERT(len == blob_len);
          field_blob->set_ptr(len, buf);
        }
        offset+= blob_size;
      }
    }
808
    if (loop == 0 && offset > m_blobs_buffer_size)
unknown's avatar
unknown committed
809
    {
810 811
      my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
      m_blobs_buffer_size= 0;
unknown's avatar
unknown committed
812
      DBUG_PRINT("value", ("allocate blobs buffer size %u", offset));
813 814
      m_blobs_buffer= my_malloc(offset, MYF(MY_WME));
      if (m_blobs_buffer == NULL)
unknown's avatar
unknown committed
815
        DBUG_RETURN(-1);
816
      m_blobs_buffer_size= offset;
unknown's avatar
unknown committed
817
    }
unknown's avatar
unknown committed
818
  }
unknown's avatar
unknown committed
819
  DBUG_RETURN(0);
unknown's avatar
unknown committed
820 821 822 823 824
}


/*
  Instruct NDB to fetch one field
unknown's avatar
unknown committed
825 826
  - data is read directly into buffer provided by field
    if field is NULL, data is read into memory provided by NDBAPI
unknown's avatar
unknown committed
827 828
*/

unknown's avatar
unknown committed
829
int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field,
unknown's avatar
unknown committed
830
                                 uint fieldnr, byte* buf)
unknown's avatar
unknown committed
831 832
{
  DBUG_ENTER("get_ndb_value");
unknown's avatar
unknown committed
833 834 835 836 837
  DBUG_PRINT("enter", ("fieldnr: %d flags: %o", fieldnr,
                       (int)(field != NULL ? field->flags : 0)));

  if (field != NULL)
  {
unknown's avatar
unknown committed
838 839
      DBUG_ASSERT(buf);
      DBUG_ASSERT(ndb_supported_type(field->type()));
unknown's avatar
unknown committed
840 841
      DBUG_ASSERT(field->ptr != NULL);
      if (! (field->flags & BLOB_FLAG))
842
      { 
843 844
        if (field->type() != MYSQL_TYPE_BIT)
        {
845 846 847 848 849 850 851 852
          byte *field_buf;
          if (field->pack_length() != 0)
            field_buf= buf + (field->ptr - table->record[0]);
          else
            field_buf= (byte *)&dummy_buf;
          m_value[fieldnr].rec= ndb_op->getValue(fieldnr, 
                                                 field_buf);
        }
853 854 855 856
        else // if (field->type() == MYSQL_TYPE_BIT)
        {
          m_value[fieldnr].rec= ndb_op->getValue(fieldnr);
        }
unknown's avatar
unknown committed
857 858 859 860 861 862 863 864 865 866
        DBUG_RETURN(m_value[fieldnr].rec == NULL);
      }

      // Blob type
      NdbBlob *ndb_blob= ndb_op->getBlobHandle(fieldnr);
      m_value[fieldnr].blob= ndb_blob;
      if (ndb_blob != NULL)
      {
        // Set callback
        void *arg= (void *)this;
unknown's avatar
unknown committed
867
        DBUG_RETURN(ndb_blob->setActiveHook(g_get_ndb_blobs_value, arg) != 0);
unknown's avatar
unknown committed
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882
      }
      DBUG_RETURN(1);
  }

  // Used for hidden key only
  m_value[fieldnr].rec= ndb_op->getValue(fieldnr, NULL);
  DBUG_RETURN(m_value[fieldnr].rec == NULL);
}


/*
  Check if any set or get of blob value in current query.
*/
bool ha_ndbcluster::uses_blob_value(bool all_fields)
{
883
  if (table->s->blob_fields == 0)
unknown's avatar
unknown committed
884
    return FALSE;
unknown's avatar
unknown committed
885
  if (all_fields)
unknown's avatar
unknown committed
886
    return TRUE;
unknown's avatar
unknown committed
887
  {
888
    uint no_fields= table->s->fields;
unknown's avatar
unknown committed
889
    int i;
890
    THD *thd= current_thd;
unknown's avatar
unknown committed
891 892 893 894 895 896
    // They always put blobs at the end..
    for (i= no_fields - 1; i >= 0; i--)
    {
      Field *field= table->field[i];
      if (thd->query_id == field->query_id)
      {
unknown's avatar
unknown committed
897
        return TRUE;
unknown's avatar
unknown committed
898 899 900
      }
    }
  }
unknown's avatar
unknown committed
901
  return FALSE;
unknown's avatar
unknown committed
902 903 904 905 906 907 908 909 910 911 912 913 914
}


/*
  Get metadata for this table from NDB 

  IMPLEMENTATION
    - check that frm-file on disk is equal to frm-file
      of table accessed in NDB
*/

int ha_ndbcluster::get_metadata(const char *path)
{
915 916
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
unknown's avatar
unknown committed
917 918
  const NDBTAB *tab;
  int error;
unknown's avatar
unknown committed
919
  bool invalidating_ndb_table= FALSE;
920

unknown's avatar
unknown committed
921 922 923
  DBUG_ENTER("get_metadata");
  DBUG_PRINT("enter", ("m_tabname: %s, path: %s", m_tabname, path));

924 925 926 927 928 929
  do {
    const void *data, *pack_data;
    uint length, pack_length;

    if (!(tab= dict->getTable(m_tabname)))
      ERR_RETURN(dict->getNdbError());
930
    // Check if thread has stale local cache
931 932 933 934 935 936 937
    if (tab->getObjectStatus() == NdbDictionary::Object::Invalid)
    {
      invalidate_dictionary_cache(FALSE);
      if (!(tab= dict->getTable(m_tabname)))
         ERR_RETURN(dict->getNdbError());
      DBUG_PRINT("info", ("Table schema version: %d", tab->getObjectVersion()));
    }
938 939 940 941 942
    /*
      Compare FrmData in NDB with frm file from disk.
    */
    error= 0;
    if (readfrm(path, &data, &length) ||
943
        packfrm(data, length, &pack_data, &pack_length))
944 945 946 947 948
    {
      my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
      my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR));
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
949
    
950
    if ((pack_length != tab->getFrmLength()) || 
951
        (memcmp(pack_data, tab->getFrmData(), pack_length)))
952 953 954
    {
      if (!invalidating_ndb_table)
      {
955
        DBUG_PRINT("info", ("Invalidating table"));
956
        invalidate_dictionary_cache(TRUE);
957
        invalidating_ndb_table= TRUE;
958 959 960
      }
      else
      {
961 962 963 964 965 966 967 968
        DBUG_PRINT("error", 
                   ("metadata, pack_length: %d getFrmLength: %d memcmp: %d", 
                    pack_length, tab->getFrmLength(),
                    memcmp(pack_data, tab->getFrmData(), pack_length)));      
        DBUG_DUMP("pack_data", (char*)pack_data, pack_length);
        DBUG_DUMP("frm", (char*)tab->getFrmData(), tab->getFrmLength());
        error= 3;
        invalidating_ndb_table= FALSE;
969 970 971 972
      }
    }
    else
    {
unknown's avatar
unknown committed
973
      invalidating_ndb_table= FALSE;
974 975 976 977 978
    }
    my_free((char*)data, MYF(0));
    my_free((char*)pack_data, MYF(0));
  } while (invalidating_ndb_table);

unknown's avatar
unknown committed
979 980
  if (error)
    DBUG_RETURN(error);
unknown's avatar
unknown committed
981
  
982
  m_table_version= tab->getObjectVersion();
983 984 985 986
  m_table= (void *)tab; 
  m_table_info= NULL; // Set in external lock
  
  DBUG_RETURN(build_index_list(ndb, table, ILBP_OPEN));
987
}
unknown's avatar
unknown committed
988

989
static int fix_unique_index_attr_order(NDB_INDEX_DATA &data,
990 991
                                       const NDBINDEX *index,
                                       KEY *key_info)
992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
{
  DBUG_ENTER("fix_unique_index_attr_order");
  unsigned sz= index->getNoOfIndexColumns();

  if (data.unique_index_attrid_map)
    my_free((char*)data.unique_index_attrid_map, MYF(0));
  data.unique_index_attrid_map= (unsigned char*)my_malloc(sz,MYF(MY_WME));

  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
  DBUG_ASSERT(key_info->key_parts == sz);
  for (unsigned i= 0; key_part != end; key_part++, i++) 
  {
    const char *field_name= key_part->field->field_name;
#ifndef DBUG_OFF
   data.unique_index_attrid_map[i]= 255;
#endif
    for (unsigned j= 0; j < sz; j++)
    {
1011
      const NDBCOL *c= index->getColumn(j);
unknown's avatar
unknown committed
1012
      if (strcmp(field_name, c->getName()) == 0)
1013
      {
1014 1015
        data.unique_index_attrid_map[i]= j;
        break;
1016 1017 1018 1019 1020 1021
      }
    }
    DBUG_ASSERT(data.unique_index_attrid_map[i] != 255);
  }
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1022

1023
int ha_ndbcluster::build_index_list(Ndb *ndb, TABLE *tab, enum ILBP phase)
1024
{
1025
  uint i;
unknown's avatar
unknown committed
1026
  int error= 0;
1027
  const char *index_name;
1028
  char unique_index_name[FN_LEN];
1029
  static const char* unique_suffix= "$unique";
unknown's avatar
unknown committed
1030
  KEY* key_info= tab->key_info;
1031
  const char **key_name= tab->s->keynames.type_names;
1032
  NDBDICT *dict= ndb->getDictionary();
1033
  DBUG_ENTER("ha_ndbcluster::build_index_list");
1034
  
unknown's avatar
unknown committed
1035
  // Save information about all known indexes
1036
  for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
1037
  {
unknown's avatar
unknown committed
1038
    index_name= *key_name;
1039
    NDB_INDEX_TYPE idx_type= get_index_type_from_table(i);
1040
    m_index[i].type= idx_type;
1041
    if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX)
1042
    {
1043 1044
      strxnmov(unique_index_name, FN_LEN, index_name, unique_suffix, NullS);
      DBUG_PRINT("info", ("Created unique index name \'%s\' for index %d",
1045
                          unique_index_name, i));
1046
    }
unknown's avatar
unknown committed
1047 1048 1049
    // Create secondary indexes if in create phase
    if (phase == ILBP_CREATE)
    {
1050 1051
      DBUG_PRINT("info", ("Creating index %u: %s", i, index_name));      
      switch (idx_type){
1052
        
unknown's avatar
unknown committed
1053
      case PRIMARY_KEY_INDEX:
1054 1055
        // Do nothing, already created
        break;
unknown's avatar
unknown committed
1056
      case PRIMARY_KEY_ORDERED_INDEX:
1057 1058
        error= create_ordered_index(index_name, key_info);
        break;
unknown's avatar
unknown committed
1059
      case UNIQUE_ORDERED_INDEX:
1060 1061 1062
        if (!(error= create_ordered_index(index_name, key_info)))
          error= create_unique_index(unique_index_name, key_info);
        break;
unknown's avatar
unknown committed
1063
      case UNIQUE_INDEX:
1064 1065 1066
        if (!(error= check_index_fields_not_null(i)))
          error= create_unique_index(unique_index_name, key_info);
        break;
unknown's avatar
unknown committed
1067
      case ORDERED_INDEX:
1068 1069
        error= create_ordered_index(index_name, key_info);
        break;
unknown's avatar
unknown committed
1070
      default:
1071 1072
        DBUG_ASSERT(FALSE);
        break;
unknown's avatar
unknown committed
1073 1074 1075
      }
      if (error)
      {
1076 1077 1078
        DBUG_PRINT("error", ("Failed to create index %u", i));
        drop_table();
        break;
unknown's avatar
unknown committed
1079 1080 1081
      }
    }
    // Add handles to index objects
1082
    if (idx_type != PRIMARY_KEY_INDEX && idx_type != UNIQUE_INDEX)
1083
    {
1084
      DBUG_PRINT("info", ("Get handle to index %s", index_name));
unknown's avatar
unknown committed
1085
      const NDBINDEX *index= dict->getIndex(index_name, m_tabname);
1086
      if (!index) DBUG_RETURN(1);
unknown's avatar
unknown committed
1087
      m_index[i].index= (void *) index;
1088
    }
1089
    if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX)
1090
    {
1091 1092
      DBUG_PRINT("info", ("Get handle to unique_index %s", unique_index_name));
      const NDBINDEX *index= dict->getIndex(unique_index_name, m_tabname);
1093
      if (!index) DBUG_RETURN(1);
unknown's avatar
unknown committed
1094
      m_index[i].unique_index= (void *) index;
1095 1096
      error= fix_unique_index_attr_order(m_index[i], index, key_info);
    }
1097
  }
unknown's avatar
unknown committed
1098 1099
  
  DBUG_RETURN(error);
1100 1101
}

1102

unknown's avatar
unknown committed
1103 1104 1105 1106
/*
  Decode the type of an index from information 
  provided in table object
*/
1107
NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_table(uint inx) const
unknown's avatar
unknown committed
1108
{
1109
  bool is_hash_index=  (table->key_info[inx].algorithm == HA_KEY_ALG_HASH);
1110
  if (inx == table->s->primary_key)
1111
    return is_hash_index ? PRIMARY_KEY_INDEX : PRIMARY_KEY_ORDERED_INDEX;
1112 1113 1114 1115

  return ((table->key_info[inx].flags & HA_NOSAME) ? 
          (is_hash_index ? UNIQUE_INDEX : UNIQUE_ORDERED_INDEX) :
          ORDERED_INDEX);
unknown's avatar
unknown committed
1116
} 
1117

1118 1119 1120 1121 1122
int ha_ndbcluster::check_index_fields_not_null(uint inx)
{
  KEY* key_info= table->key_info + inx;
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
1123
  DBUG_ENTER("ha_ndbcluster::check_index_fields_not_null");
1124 1125 1126 1127 1128 1129
  
  for (; key_part != end; key_part++) 
    {
      Field* field= key_part->field;
      if (field->maybe_null())
      {
1130 1131 1132
        my_printf_error(ER_NULL_COLUMN_IN_INDEX,ER(ER_NULL_COLUMN_IN_INDEX),
                        MYF(0),field->field_name);
        DBUG_RETURN(ER_NULL_COLUMN_IN_INDEX);
1133 1134 1135 1136 1137
      }
    }
  
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1138 1139 1140

void ha_ndbcluster::release_metadata()
{
1141
  uint i;
1142

unknown's avatar
unknown committed
1143 1144 1145 1146
  DBUG_ENTER("release_metadata");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));

  m_table= NULL;
unknown's avatar
unknown committed
1147
  m_table_info= NULL;
unknown's avatar
unknown committed
1148

1149
  // Release index list 
1150 1151
  for (i= 0; i < MAX_KEY; i++)
  {
1152 1153
    m_index[i].unique_index= NULL;      
    m_index[i].index= NULL;      
1154 1155 1156 1157 1158
    if (m_index[i].unique_index_attrid_map)
    {
      my_free((char *)m_index[i].unique_index_attrid_map, MYF(0));
      m_index[i].unique_index_attrid_map= NULL;
    }
1159 1160
  }

unknown's avatar
unknown committed
1161 1162 1163
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
1164
int ha_ndbcluster::get_ndb_lock_type(enum thr_lock_type type)
1165
{
1166
  if (type >= TL_WRITE_ALLOW_WRITE)
unknown's avatar
unknown committed
1167
    return NdbOperation::LM_Exclusive;
1168
  else if (uses_blob_value(m_retrieve_all_fields))
1169
    return NdbOperation::LM_Read;
unknown's avatar
unknown committed
1170
  else
unknown's avatar
unknown committed
1171
    return NdbOperation::LM_CommittedRead;
1172 1173
}

unknown's avatar
unknown committed
1174 1175 1176 1177 1178 1179
static const ulong index_type_flags[]=
{
  /* UNDEFINED_INDEX */
  0,                         

  /* PRIMARY_KEY_INDEX */
1180
  HA_ONLY_WHOLE_INDEX, 
1181 1182

  /* PRIMARY_KEY_ORDERED_INDEX */
1183
  /* 
unknown's avatar
unknown committed
1184
     Enable HA_KEYREAD_ONLY when "sorted" indexes are supported, 
1185 1186 1187
     thus ORDERD BY clauses can be optimized by reading directly 
     through the index.
  */
unknown's avatar
unknown committed
1188
  // HA_KEYREAD_ONLY | 
unknown's avatar
unknown committed
1189
  HA_READ_NEXT |
1190
  HA_READ_PREV |
unknown's avatar
unknown committed
1191 1192
  HA_READ_RANGE |
  HA_READ_ORDER,
unknown's avatar
unknown committed
1193 1194

  /* UNIQUE_INDEX */
1195
  HA_ONLY_WHOLE_INDEX,
unknown's avatar
unknown committed
1196

1197
  /* UNIQUE_ORDERED_INDEX */
unknown's avatar
unknown committed
1198
  HA_READ_NEXT |
1199
  HA_READ_PREV |
unknown's avatar
unknown committed
1200 1201
  HA_READ_RANGE |
  HA_READ_ORDER,
1202

unknown's avatar
unknown committed
1203
  /* ORDERED_INDEX */
unknown's avatar
unknown committed
1204
  HA_READ_NEXT |
1205
  HA_READ_PREV |
unknown's avatar
unknown committed
1206 1207
  HA_READ_RANGE |
  HA_READ_ORDER
unknown's avatar
unknown committed
1208 1209 1210 1211 1212 1213 1214
};

static const int index_flags_size= sizeof(index_type_flags)/sizeof(ulong);

inline NDB_INDEX_TYPE ha_ndbcluster::get_index_type(uint idx_no) const
{
  DBUG_ASSERT(idx_no < MAX_KEY);
1215
  return m_index[idx_no].type;
unknown's avatar
unknown committed
1216 1217 1218 1219 1220 1221 1222 1223 1224 1225
}


/*
  Get the flags for an index

  RETURN
    flags depending on the type of the index.
*/

1226 1227
inline ulong ha_ndbcluster::index_flags(uint idx_no, uint part,
                                        bool all_parts) const 
unknown's avatar
unknown committed
1228
{ 
1229
  DBUG_ENTER("ha_ndbcluster::index_flags");
1230
  DBUG_PRINT("info", ("idx_no: %d", idx_no));
unknown's avatar
unknown committed
1231
  DBUG_ASSERT(get_index_type_from_table(idx_no) < index_flags_size);
1232 1233
  DBUG_RETURN(index_type_flags[get_index_type_from_table(idx_no)] | 
              HA_KEY_SCAN_NOT_ROR);
unknown's avatar
unknown committed
1234 1235
}

unknown's avatar
unknown committed
1236 1237
static void shrink_varchar(Field* field, const byte* & ptr, char* buf)
{
1238
  if (field->type() == MYSQL_TYPE_VARCHAR && ptr != NULL) {
unknown's avatar
unknown committed
1239
    Field_varstring* f= (Field_varstring*)field;
unknown's avatar
unknown committed
1240
    if (f->length_bytes == 1) {
unknown's avatar
unknown committed
1241 1242 1243 1244 1245
      uint pack_len= field->pack_length();
      DBUG_ASSERT(1 <= pack_len && pack_len <= 256);
      if (ptr[1] == 0) {
        buf[0]= ptr[0];
      } else {
unknown's avatar
unknown committed
1246
        DBUG_ASSERT(FALSE);
unknown's avatar
unknown committed
1247 1248 1249 1250 1251 1252 1253
        buf[0]= 255;
      }
      memmove(buf + 1, ptr + 2, pack_len - 1);
      ptr= buf;
    }
  }
}
unknown's avatar
unknown committed
1254 1255 1256

int ha_ndbcluster::set_primary_key(NdbOperation *op, const byte *key)
{
1257
  KEY* key_info= table->key_info + table->s->primary_key;
unknown's avatar
unknown committed
1258 1259 1260 1261 1262 1263 1264
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
  DBUG_ENTER("set_primary_key");

  for (; key_part != end; key_part++) 
  {
    Field* field= key_part->field;
unknown's avatar
unknown committed
1265 1266 1267
    const byte* ptr= key;
    char buf[256];
    shrink_varchar(field, ptr, buf);
unknown's avatar
unknown committed
1268
    if (set_ndb_key(op, field, 
1269
                    key_part->fieldnr-1, ptr))
unknown's avatar
unknown committed
1270
      ERR_RETURN(op->getNdbError());
unknown's avatar
unknown committed
1271
    key += key_part->store_length;
unknown's avatar
unknown committed
1272 1273 1274 1275 1276
  }
  DBUG_RETURN(0);
}


1277
int ha_ndbcluster::set_primary_key_from_record(NdbOperation *op, const byte *record)
1278
{
1279
  KEY* key_info= table->key_info + table->s->primary_key;
1280 1281
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
1282
  DBUG_ENTER("set_primary_key_from_record");
1283 1284 1285 1286 1287

  for (; key_part != end; key_part++) 
  {
    Field* field= key_part->field;
    if (set_ndb_key(op, field, 
1288
		    key_part->fieldnr-1, record+key_part->offset))
1289 1290 1291 1292 1293
      ERR_RETURN(op->getNdbError());
  }
  DBUG_RETURN(0);
}

1294 1295
int 
ha_ndbcluster::set_index_key(NdbOperation *op, 
1296 1297
                             const KEY *key_info, 
                             const byte * key_ptr)
1298
{
1299
  DBUG_ENTER("ha_ndbcluster::set_index_key");
1300 1301 1302 1303 1304 1305
  uint i;
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
  
  for (i= 0; key_part != end; key_part++, i++) 
  {
unknown's avatar
unknown committed
1306 1307 1308 1309
    Field* field= key_part->field;
    const byte* ptr= key_part->null_bit ? key_ptr + 1 : key_ptr;
    char buf[256];
    shrink_varchar(field, ptr, buf);
unknown's avatar
Merge  
unknown committed
1310
    if (set_ndb_key(op, field, m_index[active_index].unique_index_attrid_map[i], ptr))
1311 1312 1313 1314 1315
      ERR_RETURN(m_active_trans->getNdbError());
    key_ptr+= key_part->store_length;
  }
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1316

unknown's avatar
unknown committed
1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329
inline 
int ha_ndbcluster::define_read_attrs(byte* buf, NdbOperation* op)
{
  uint i;
  THD *thd= current_thd;

  DBUG_ENTER("define_read_attrs");  

  // Define attributes to read
  for (i= 0; i < table->s->fields; i++) 
  {
    Field *field= table->field[i];
    if ((thd->query_id == field->query_id) ||
1330 1331
        ((field->flags & PRI_KEY_FLAG)) || 
        m_retrieve_all_fields)
unknown's avatar
unknown committed
1332 1333
    {      
      if (get_ndb_value(op, field, i, buf))
1334
        ERR_RETURN(op->getNdbError());
unknown's avatar
unknown committed
1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357
    } 
    else 
    {
      m_value[i].ptr= NULL;
    }
  }
    
  if (table->s->primary_key == MAX_KEY) 
  {
    DBUG_PRINT("info", ("Getting hidden key"));
    // Scanning table with no primary key
    int hidden_no= table->s->fields;      
#ifndef DBUG_OFF
    const NDBTAB *tab= (const NDBTAB *) m_table;    
    if (!tab->getColumn(hidden_no))
      DBUG_RETURN(1);
#endif
    if (get_ndb_value(op, NULL, hidden_no, NULL))
      ERR_RETURN(op->getNdbError());
  }
  DBUG_RETURN(0);
} 

unknown's avatar
unknown committed
1358 1359 1360 1361
/*
  Read one record from NDB using primary key
*/

1362
int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf) 
unknown's avatar
unknown committed
1363
{
1364
  uint no_fields= table->s->fields;
unknown's avatar
unknown committed
1365 1366
  NdbConnection *trans= m_active_trans;
  NdbOperation *op;
1367

unknown's avatar
unknown committed
1368 1369 1370 1371
  int res;
  DBUG_ENTER("pk_read");
  DBUG_PRINT("enter", ("key_len: %u", key_len));
  DBUG_DUMP("key", (char*)key, key_len);
unknown's avatar
unknown committed
1372

1373 1374
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
unknown's avatar
unknown committed
1375
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || 
1376
      op->readTuple(lm) != 0)
1377
    ERR_RETURN(trans->getNdbError());
unknown's avatar
unknown committed
1378
  
1379
  if (table->s->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
1380 1381 1382 1383 1384
  {
    // This table has no primary key, use "hidden" primary key
    DBUG_PRINT("info", ("Using hidden key"));
    DBUG_DUMP("key", (char*)key, 8);    
    if (set_hidden_key(op, no_fields, key))
1385
      ERR_RETURN(trans->getNdbError());
unknown's avatar
unknown committed
1386
    
unknown's avatar
unknown committed
1387
    // Read key at the same time, for future reference
unknown's avatar
unknown committed
1388
    if (get_ndb_value(op, NULL, no_fields, NULL))
1389
      ERR_RETURN(trans->getNdbError());
unknown's avatar
unknown committed
1390 1391 1392 1393 1394 1395 1396
  } 
  else 
  {
    if ((res= set_primary_key(op, key)))
      return res;
  }
  
unknown's avatar
unknown committed
1397
  if ((res= define_read_attrs(buf, op)))
unknown's avatar
unknown committed
1398
    DBUG_RETURN(res);
unknown's avatar
unknown committed
1399
  
unknown's avatar
unknown committed
1400
  if (execute_no_commit_ie(this,trans) != 0) 
unknown's avatar
unknown committed
1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411
  {
    table->status= STATUS_NOT_FOUND;
    DBUG_RETURN(ndb_err(trans));
  }

  // The value have now been fetched from NDB  
  unpack_record(buf);
  table->status= 0;     
  DBUG_RETURN(0);
}

1412 1413 1414 1415 1416 1417
/*
  Read one complementing record from NDB using primary key from old_data
*/

int ha_ndbcluster::complemented_pk_read(const byte *old_data, byte *new_data)
{
1418
  uint no_fields= table->s->fields, i;
1419
  NdbTransaction *trans= m_active_trans;
1420 1421 1422 1423
  NdbOperation *op;
  THD *thd= current_thd;
  DBUG_ENTER("complemented_pk_read");

1424
  if (m_retrieve_all_fields)
1425 1426 1427
    // We have allready retrieved all fields, nothing to complement
    DBUG_RETURN(0);

1428 1429
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
unknown's avatar
unknown committed
1430
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || 
1431
      op->readTuple(lm) != 0)
1432
    ERR_RETURN(trans->getNdbError());
1433
  int res;
unknown's avatar
unknown committed
1434
  if ((res= set_primary_key_from_record(op, old_data)))
1435
    ERR_RETURN(trans->getNdbError());
1436 1437 1438 1439
  // Read all unreferenced non-key field(s)
  for (i= 0; i < no_fields; i++) 
  {
    Field *field= table->field[i];
1440
    if (!((field->flags & PRI_KEY_FLAG) ||
1441
          (thd->query_id == field->query_id)))
1442
    {
unknown's avatar
unknown committed
1443
      if (get_ndb_value(op, field, i, new_data))
1444
        ERR_RETURN(trans->getNdbError());
1445 1446 1447
    }
  }
  
unknown's avatar
unknown committed
1448
  if (execute_no_commit(this,trans) != 0) 
1449 1450 1451 1452 1453 1454 1455 1456
  {
    table->status= STATUS_NOT_FOUND;
    DBUG_RETURN(ndb_err(trans));
  }

  // The value have now been fetched from NDB  
  unpack_record(new_data);
  table->status= 0;     
1457 1458 1459 1460 1461 1462 1463 1464

  /**
   * restore m_value
   */
  for (i= 0; i < no_fields; i++) 
  {
    Field *field= table->field[i];
    if (!((field->flags & PRI_KEY_FLAG) ||
1465
          (thd->query_id == field->query_id)))
1466 1467 1468 1469 1470
    {
      m_value[i].ptr= NULL;
    }
  }
  
1471 1472 1473
  DBUG_RETURN(0);
}

1474 1475 1476 1477
/*
  Peek to check if a particular row already exists
*/

1478
int ha_ndbcluster::peek_row(const byte *record)
1479
{
1480
  NdbTransaction *trans= m_active_trans;
1481 1482
  NdbOperation *op;
  DBUG_ENTER("peek_row");
unknown's avatar
unknown committed
1483

1484 1485 1486 1487 1488
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) ||
      op->readTuple(lm) != 0)
    ERR_RETURN(trans->getNdbError());
unknown's avatar
unknown committed
1489

1490
  int res;
1491
  if ((res= set_primary_key_from_record(op, record)))
1492
    ERR_RETURN(trans->getNdbError());
unknown's avatar
unknown committed
1493

1494
  if (execute_no_commit_ie(this,trans) != 0)
unknown's avatar
unknown committed
1495 1496 1497 1498
  {
    table->status= STATUS_NOT_FOUND;
    DBUG_RETURN(ndb_err(trans));
  } 
1499 1500
  DBUG_RETURN(0);
}
1501

unknown's avatar
unknown committed
1502 1503 1504 1505 1506
/*
  Read one record from NDB using unique secondary index
*/

int ha_ndbcluster::unique_index_read(const byte *key,
1507
                                     uint key_len, byte *buf)
unknown's avatar
unknown committed
1508
{
unknown's avatar
unknown committed
1509
  int res;
1510
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
1511
  NdbIndexOperation *op;
1512
  DBUG_ENTER("ha_ndbcluster::unique_index_read");
unknown's avatar
unknown committed
1513 1514 1515
  DBUG_PRINT("enter", ("key_len: %u, index: %u", key_len, active_index));
  DBUG_DUMP("key", (char*)key, key_len);
  
1516 1517
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
1518
  if (!(op= trans->getNdbIndexOperation((NDBINDEX *) 
1519
                                        m_index[active_index].unique_index, 
unknown's avatar
unknown committed
1520
                                        (const NDBTAB *) m_table)) ||
1521
      op->readTuple(lm) != 0)
unknown's avatar
unknown committed
1522 1523 1524
    ERR_RETURN(trans->getNdbError());
  
  // Set secondary index key(s)
unknown's avatar
unknown committed
1525
  if ((res= set_index_key(op, table->key_info + active_index, key)))
1526 1527
    DBUG_RETURN(res);
  
unknown's avatar
unknown committed
1528
  if ((res= define_read_attrs(buf, op)))
unknown's avatar
unknown committed
1529
    DBUG_RETURN(res);
unknown's avatar
unknown committed
1530

unknown's avatar
unknown committed
1531
  if (execute_no_commit_ie(this,trans) != 0) 
unknown's avatar
unknown committed
1532 1533 1534 1535 1536 1537 1538 1539 1540 1541
  {
    table->status= STATUS_NOT_FOUND;
    DBUG_RETURN(ndb_err(trans));
  }
  // The value have now been fetched from NDB
  unpack_record(buf);
  table->status= 0;
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
1542
inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor)
1543 1544
{
  DBUG_ENTER("fetch_next");
1545
  int check;
1546
  NdbTransaction *trans= m_active_trans;
1547
  
1548
  bool contact_ndb= m_lock.type < TL_WRITE_ALLOW_WRITE;
1549 1550
  do {
    DBUG_PRINT("info", ("Call nextResult, contact_ndb: %d", contact_ndb));
unknown's avatar
unknown committed
1551 1552 1553
    /*
      We can only handle one tuple with blobs at a time.
    */
1554
    if (m_ops_pending && m_blobs_pending)
unknown's avatar
unknown committed
1555
    {
unknown's avatar
unknown committed
1556
      if (execute_no_commit(this,trans) != 0)
1557
        DBUG_RETURN(ndb_err(trans));
1558 1559
      m_ops_pending= 0;
      m_blobs_pending= FALSE;
unknown's avatar
unknown committed
1560
    }
1561 1562
    
    if ((check= cursor->nextResult(contact_ndb, m_force_send)) == 0)
1563 1564 1565 1566 1567 1568 1569
    {
      DBUG_RETURN(0);
    } 
    else if (check == 1 || check == 2)
    {
      // 1: No more records
      // 2: No more cached records
1570
      
1571
      /*
1572 1573 1574
        Before fetching more rows and releasing lock(s),
        all pending update or delete operations should 
        be sent to NDB
1575
      */
1576 1577
      DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending));    
      if (m_ops_pending)
1578
      {
1579 1580 1581 1582 1583 1584 1585 1586 1587
        if (m_transaction_on)
        {
          if (execute_no_commit(this,trans) != 0)
            DBUG_RETURN(-1);
        }
        else
        {
          if  (execute_commit(this,trans) != 0)
            DBUG_RETURN(-1);
unknown's avatar
unknown committed
1588
          if (trans->restart() != 0)
1589 1590 1591 1592 1593 1594
          {
            DBUG_ASSERT(0);
            DBUG_RETURN(-1);
          }
        }
        m_ops_pending= 0;
1595
      }
1596 1597
      contact_ndb= (check == 2);
    }
unknown's avatar
unknown committed
1598 1599 1600 1601
    else
    {
      DBUG_RETURN(-1);
    }
1602
  } while (check == 2);
unknown's avatar
unknown committed
1603

1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614
  DBUG_RETURN(1);
}

/*
  Get the next record of a started scan. Try to fetch
  it locally from NdbApi cached records if possible, 
  otherwise ask NDB for more.

  NOTE
  If this is a update/delete make sure to not contact 
  NDB before any pending ops have been sent to NDB.
unknown's avatar
unknown committed
1615

1616 1617 1618 1619 1620 1621 1622
*/

inline int ha_ndbcluster::next_result(byte *buf)
{  
  int res;
  DBUG_ENTER("next_result");
    
1623 1624 1625
  if (!m_active_cursor)
    DBUG_RETURN(HA_ERR_END_OF_FILE);
  
unknown's avatar
unknown committed
1626
  if ((res= fetch_next(m_active_cursor)) == 0)
1627 1628 1629 1630 1631 1632 1633
  {
    DBUG_PRINT("info", ("One more record found"));    
    
    unpack_record(buf);
    table->status= 0;
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
1634
  else if (res == 1)
1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645
  {
    // No more records
    table->status= STATUS_NOT_FOUND;
    
    DBUG_PRINT("info", ("No more records"));
    DBUG_RETURN(HA_ERR_END_OF_FILE);
  }
  else
  {
    DBUG_RETURN(ndb_err(m_active_trans));
  }
unknown's avatar
unknown committed
1646 1647
}

1648
/*
1649
  Set bounds for ordered index scan.
1650 1651
*/

unknown's avatar
unknown committed
1652
int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
1653 1654
                              const key_range *keys[2],
                              uint range_no)
1655
{
1656 1657 1658 1659
  const KEY *const key_info= table->key_info + active_index;
  const uint key_parts= key_info->key_parts;
  uint key_tot_len[2];
  uint tot_len;
1660
  uint i, j;
1661 1662

  DBUG_ENTER("set_bounds");
1663
  DBUG_PRINT("info", ("key_parts=%d", key_parts));
1664

1665
  for (j= 0; j <= 1; j++)
1666
  {
1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679
    const key_range *key= keys[j];
    if (key != NULL)
    {
      // for key->flag see ha_rkey_function
      DBUG_PRINT("info", ("key %d length=%d flag=%d",
                          j, key->length, key->flag));
      key_tot_len[j]= key->length;
    }
    else
    {
      DBUG_PRINT("info", ("key %d not present", j));
      key_tot_len[j]= 0;
    }
1680 1681
  }
  tot_len= 0;
unknown's avatar
unknown committed
1682

1683 1684 1685 1686
  for (i= 0; i < key_parts; i++)
  {
    KEY_PART_INFO *key_part= &key_info->key_part[i];
    Field *field= key_part->field;
1687
#ifndef DBUG_OFF
1688
    uint part_len= key_part->length;
1689
#endif
1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703
    uint part_store_len= key_part->store_length;
    // Info about each key part
    struct part_st {
      bool part_last;
      const key_range *key;
      const byte *part_ptr;
      bool part_null;
      int bound_type;
      const char* bound_ptr;
    };
    struct part_st part[2];

    for (j= 0; j <= 1; j++)
    {
1704
      struct part_st &p= part[j];
1705 1706 1707 1708 1709 1710 1711
      p.key= NULL;
      p.bound_type= -1;
      if (tot_len < key_tot_len[j])
      {
        p.part_last= (tot_len + part_store_len >= key_tot_len[j]);
        p.key= keys[j];
        p.part_ptr= &p.key->key[tot_len];
unknown's avatar
unknown committed
1712
        p.part_null= key_part->null_bit && *p.part_ptr;
1713
        p.bound_ptr= (const char *)
unknown's avatar
unknown committed
1714
          p.part_null ? 0 : key_part->null_bit ? p.part_ptr + 1 : p.part_ptr;
1715 1716 1717 1718 1719 1720 1721 1722

        if (j == 0)
        {
          switch (p.key->flag)
          {
            case HA_READ_KEY_EXACT:
              p.bound_type= NdbIndexScanOperation::BoundEQ;
              break;
1723
            // ascending
1724 1725 1726 1727 1728 1729 1730 1731 1732
            case HA_READ_KEY_OR_NEXT:
              p.bound_type= NdbIndexScanOperation::BoundLE;
              break;
            case HA_READ_AFTER_KEY:
              if (! p.part_last)
                p.bound_type= NdbIndexScanOperation::BoundLE;
              else
                p.bound_type= NdbIndexScanOperation::BoundLT;
              break;
1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745
            // descending
            case HA_READ_PREFIX_LAST:           // weird
              p.bound_type= NdbIndexScanOperation::BoundEQ;
              break;
            case HA_READ_PREFIX_LAST_OR_PREV:   // weird
              p.bound_type= NdbIndexScanOperation::BoundGE;
              break;
            case HA_READ_BEFORE_KEY:
              if (! p.part_last)
                p.bound_type= NdbIndexScanOperation::BoundGE;
              else
                p.bound_type= NdbIndexScanOperation::BoundGT;
              break;
1746 1747 1748 1749 1750 1751 1752
            default:
              break;
          }
        }
        if (j == 1) {
          switch (p.key->flag)
          {
1753
            // ascending
1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764
            case HA_READ_BEFORE_KEY:
              if (! p.part_last)
                p.bound_type= NdbIndexScanOperation::BoundGE;
              else
                p.bound_type= NdbIndexScanOperation::BoundGT;
              break;
            case HA_READ_AFTER_KEY:     // weird
              p.bound_type= NdbIndexScanOperation::BoundGE;
              break;
            default:
              break;
1765
            // descending strangely sets no end key
1766 1767
          }
        }
1768

1769 1770 1771
        if (p.bound_type == -1)
        {
          DBUG_PRINT("error", ("key %d unknown flag %d", j, p.key->flag));
unknown's avatar
unknown committed
1772
          DBUG_ASSERT(FALSE);
1773
          // Stop setting bounds but continue with what we have
1774
          op->end_of_bound(range_no);
1775 1776 1777 1778
          DBUG_RETURN(0);
        }
      }
    }
1779

1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796
    // Seen with e.g. b = 1 and c > 1
    if (part[0].bound_type == NdbIndexScanOperation::BoundLE &&
        part[1].bound_type == NdbIndexScanOperation::BoundGE &&
        memcmp(part[0].part_ptr, part[1].part_ptr, part_store_len) == 0)
    {
      DBUG_PRINT("info", ("replace LE/GE pair by EQ"));
      part[0].bound_type= NdbIndexScanOperation::BoundEQ;
      part[1].bound_type= -1;
    }
    // Not seen but was in previous version
    if (part[0].bound_type == NdbIndexScanOperation::BoundEQ &&
        part[1].bound_type == NdbIndexScanOperation::BoundGE &&
        memcmp(part[0].part_ptr, part[1].part_ptr, part_store_len) == 0)
    {
      DBUG_PRINT("info", ("remove GE from EQ/GE pair"));
      part[1].bound_type= -1;
    }
1797

1798 1799
    for (j= 0; j <= 1; j++)
    {
1800
      struct part_st &p= part[j];
1801 1802 1803 1804 1805 1806 1807 1808 1809
      // Set bound if not done with this key
      if (p.key != NULL)
      {
        DBUG_PRINT("info", ("key %d:%d offset=%d length=%d last=%d bound=%d",
                            j, i, tot_len, part_len, p.part_last, p.bound_type));
        DBUG_DUMP("info", (const char*)p.part_ptr, part_store_len);

        // Set bound if not cancelled via type -1
        if (p.bound_type != -1)
1810
        {
unknown's avatar
unknown committed
1811 1812 1813
          const char* ptr= p.bound_ptr;
          char buf[256];
          shrink_varchar(field, ptr, buf);
unknown's avatar
Merge  
unknown committed
1814
          if (op->setBound(i, p.bound_type, ptr))
1815
            ERR_RETURN(op->getNdbError());
1816
        }
1817 1818 1819 1820
      }
    }

    tot_len+= part_store_len;
1821
  }
1822
  op->end_of_bound(range_no);
1823 1824 1825
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
1826
/*
1827
  Start ordered index scan in NDB
unknown's avatar
unknown committed
1828 1829
*/

1830
int ha_ndbcluster::ordered_index_scan(const key_range *start_key,
1831 1832
                                      const key_range *end_key,
                                      bool sorted, bool descending, byte* buf)
unknown's avatar
unknown committed
1833
{  
unknown's avatar
unknown committed
1834
  int res;
unknown's avatar
unknown committed
1835
  bool restart;
1836
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
1837
  NdbIndexScanOperation *op;
1838

1839 1840 1841
  DBUG_ENTER("ha_ndbcluster::ordered_index_scan");
  DBUG_PRINT("enter", ("index: %u, sorted: %d, descending: %d",
             active_index, sorted, descending));  
unknown's avatar
unknown committed
1842
  DBUG_PRINT("enter", ("Starting new ordered scan on %s", m_tabname));
unknown's avatar
unknown committed
1843

1844 1845
  // Check that sorted seems to be initialised
  DBUG_ASSERT(sorted == 0 || sorted == 1);
unknown's avatar
unknown committed
1846
  
1847
  if (m_active_cursor == 0)
unknown's avatar
unknown committed
1848
  {
unknown's avatar
unknown committed
1849
    restart= FALSE;
unknown's avatar
unknown committed
1850 1851 1852
    NdbOperation::LockMode lm=
      (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
    if (!(op= trans->getNdbIndexScanOperation((NDBINDEX *)
1853 1854 1855
                                              m_index[active_index].index, 
                                              (const NDBTAB *) m_table)) ||
        op->readTuples(lm, 0, parallelism, sorted, descending))
unknown's avatar
unknown committed
1856
      ERR_RETURN(trans->getNdbError());
1857
    m_active_cursor= op;
unknown's avatar
unknown committed
1858
  } else {
unknown's avatar
unknown committed
1859
    restart= TRUE;
1860
    op= (NdbIndexScanOperation*)m_active_cursor;
unknown's avatar
unknown committed
1861 1862 1863
    
    DBUG_ASSERT(op->getSorted() == sorted);
    DBUG_ASSERT(op->getLockMode() == 
1864
                (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type));
unknown's avatar
unknown committed
1865
    if (op->reset_bounds(m_force_send))
unknown's avatar
unknown committed
1866 1867
      DBUG_RETURN(ndb_err(m_active_trans));
  }
1868
  
1869
  {
1870
    const key_range *keys[2]= { start_key, end_key };
unknown's avatar
unknown committed
1871 1872 1873
    res= set_bounds(op, keys);
    if (res)
      DBUG_RETURN(res);
1874
  }
1875 1876 1877

  if (!restart && generate_scan_filter(m_cond_stack, op))
    DBUG_RETURN(ndb_err(trans));
1878
  
unknown's avatar
unknown committed
1879
  if (!restart && (res= define_read_attrs(buf, op)))
1880
  {
unknown's avatar
unknown committed
1881
    DBUG_RETURN(res);
unknown's avatar
unknown committed
1882
  }
unknown's avatar
unknown committed
1883 1884 1885 1886 1887 1888

  if (execute_no_commit(this,trans) != 0)
    DBUG_RETURN(ndb_err(trans));
  
  DBUG_RETURN(next_result(buf));
}
unknown's avatar
unknown committed
1889 1890

/*
1891
  Start full table scan in NDB
unknown's avatar
unknown committed
1892 1893 1894 1895
 */

int ha_ndbcluster::full_table_scan(byte *buf)
{
unknown's avatar
unknown committed
1896
  int res;
unknown's avatar
unknown committed
1897
  NdbScanOperation *op;
1898
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
1899 1900 1901 1902

  DBUG_ENTER("full_table_scan");  
  DBUG_PRINT("enter", ("Starting new scan on %s", m_tabname));

1903 1904 1905
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
  if (!(op=trans->getNdbScanOperation((const NDBTAB *) m_table)) ||
1906
      op->readTuples(lm, 0, parallelism))
unknown's avatar
unknown committed
1907
    ERR_RETURN(trans->getNdbError());
1908
  m_active_cursor= op;
1909 1910
  if (generate_scan_filter(m_cond_stack, op))
    DBUG_RETURN(ndb_err(trans));
unknown's avatar
unknown committed
1911
  if ((res= define_read_attrs(buf, op)))
unknown's avatar
unknown committed
1912 1913 1914 1915 1916 1917
    DBUG_RETURN(res);

  if (execute_no_commit(this,trans) != 0)
    DBUG_RETURN(ndb_err(trans));
  DBUG_PRINT("exit", ("Scan started successfully"));
  DBUG_RETURN(next_result(buf));
1918 1919
}

unknown's avatar
unknown committed
1920 1921 1922 1923 1924
/*
  Insert one record into NDB
*/
int ha_ndbcluster::write_row(byte *record)
{
unknown's avatar
unknown committed
1925
  bool has_auto_increment;
unknown's avatar
unknown committed
1926
  uint i;
1927
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
1928 1929
  NdbOperation *op;
  int res;
unknown's avatar
unknown committed
1930 1931
  THD *thd= current_thd;

unknown's avatar
unknown committed
1932
  DBUG_ENTER("write_row");
1933

1934
  if (m_ignore_dup_key && table->s->primary_key != MAX_KEY)
1935
  {
1936
    int peek_res= peek_row(record);
1937 1938 1939
    
    if (!peek_res) 
    {
1940
      m_dupkey= table->s->primary_key;
1941 1942 1943 1944
      DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
    }
    if (peek_res != HA_ERR_KEY_NOT_FOUND)
      DBUG_RETURN(peek_res);
1945
  }
1946

unknown's avatar
unknown committed
1947
  statistic_increment(thd->status_var.ha_write_count, &LOCK_status);
unknown's avatar
unknown committed
1948 1949
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
    table->timestamp_field->set_time();
1950
  has_auto_increment= (table->next_number_field && record == table->record[0]);
unknown's avatar
unknown committed
1951

unknown's avatar
unknown committed
1952
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)))
unknown's avatar
unknown committed
1953 1954 1955 1956 1957 1958
    ERR_RETURN(trans->getNdbError());

  res= (m_use_write) ? op->writeTuple() :op->insertTuple(); 
  if (res != 0)
    ERR_RETURN(trans->getNdbError());  
 
1959
  if (table->s->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
1960 1961
  {
    // Table has hidden primary key
1962
    Ndb *ndb= get_ndb();
1963 1964 1965 1966 1967 1968 1969
    Uint64 auto_value= NDB_FAILED_AUTO_INCREMENT;
    uint retries= NDB_AUTO_INCREMENT_RETRIES;
    do {
      auto_value= ndb->getAutoIncrementValue((const NDBTAB *) m_table);
    } while (auto_value == NDB_FAILED_AUTO_INCREMENT && 
             --retries &&
             ndb->getNdbError().status == NdbError::TemporaryError);
1970 1971
    if (auto_value == NDB_FAILED_AUTO_INCREMENT)
      ERR_RETURN(ndb->getNdbError());
1972
    if (set_hidden_key(op, table->s->fields, (const byte*)&auto_value))
unknown's avatar
unknown committed
1973 1974 1975 1976 1977
      ERR_RETURN(op->getNdbError());
  } 
  else 
  {
    int res;
1978

1979 1980
    if (has_auto_increment) 
    {
unknown's avatar
unknown committed
1981 1982
      THD *thd= table->in_use;

1983
      m_skip_auto_increment= FALSE;
1984
      update_auto_increment();
unknown's avatar
unknown committed
1985 1986
      /* Ensure that handler is always called for auto_increment values */
      thd->next_insert_id= 0;
1987
      m_skip_auto_increment= !auto_increment_column_changed;
1988
    }
1989

1990
    if ((res= set_primary_key_from_record(op, record)))
1991
      return res;  
unknown's avatar
unknown committed
1992 1993 1994
  }

  // Set non-key attribute(s)
unknown's avatar
unknown committed
1995
  bool set_blob_value= FALSE;
1996
  for (i= 0; i < table->s->fields; i++) 
unknown's avatar
unknown committed
1997 1998 1999
  {
    Field *field= table->field[i];
    if (!(field->flags & PRI_KEY_FLAG) &&
2000
        set_ndb_value(op, field, i, &set_blob_value))
2001
    {
2002
      m_skip_auto_increment= TRUE;
unknown's avatar
unknown committed
2003
      ERR_RETURN(op->getNdbError());
2004
    }
unknown's avatar
unknown committed
2005 2006
  }

2007 2008
  m_rows_changed++;

unknown's avatar
unknown committed
2009 2010 2011 2012 2013 2014 2015
  /*
    Execute write operation
    NOTE When doing inserts with many values in 
    each INSERT statement it should not be necessary
    to NoCommit the transaction between each row.
    Find out how this is detected!
  */
2016
  m_rows_inserted++;
2017
  no_uncommitted_rows_update(1);
2018
  m_bulk_insert_not_flushed= TRUE;
2019
  if ((m_rows_to_insert == (ha_rows) 1) || 
2020
      ((m_rows_inserted % m_bulk_insert_rows) == 0) ||
2021
      m_primary_key_update ||
2022
      set_blob_value)
2023 2024 2025
  {
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
2026 2027
                        "rows_inserted:%d, bulk_insert_rows: %d", 
                        (int)m_rows_inserted, (int)m_bulk_insert_rows));
2028

2029
    m_bulk_insert_not_flushed= FALSE;
2030
    if (m_transaction_on)
2031
    {
unknown's avatar
unknown committed
2032
      if (execute_no_commit(this,trans) != 0)
2033
      {
2034 2035 2036
        m_skip_auto_increment= TRUE;
        no_uncommitted_rows_execute_failure();
        DBUG_RETURN(ndb_err(trans));
2037
      }
2038 2039
    }
    else
2040
    {
unknown's avatar
unknown committed
2041
      if (execute_commit(this,trans) != 0)
2042
      {
2043 2044 2045
        m_skip_auto_increment= TRUE;
        no_uncommitted_rows_execute_failure();
        DBUG_RETURN(ndb_err(trans));
2046
      }
unknown's avatar
unknown committed
2047
      if (trans->restart() != 0)
2048
      {
2049 2050
        DBUG_ASSERT(0);
        DBUG_RETURN(-1);
2051
      }
2052
    }
2053
  }
2054
  if ((has_auto_increment) && (m_skip_auto_increment))
unknown's avatar
unknown committed
2055
  {
2056
    Ndb *ndb= get_ndb();
2057
    Uint64 next_val= (Uint64) table->next_number_field->val_int() + 1;
unknown's avatar
unknown committed
2058
    DBUG_PRINT("info", 
2059
               ("Trying to set next auto increment value to %lu",
2060
                (ulong) next_val));
2061
    if (ndb->setAutoIncrementValue((const NDBTAB *) m_table, next_val, TRUE))
unknown's avatar
unknown committed
2062
      DBUG_PRINT("info", 
2063
                 ("Setting next auto increment value to %u", next_val));  
2064
  }
2065
  m_skip_auto_increment= TRUE;
2066

unknown's avatar
unknown committed
2067 2068 2069 2070 2071 2072 2073
  DBUG_RETURN(0);
}


/* Compare if a key in a row has changed */

int ha_ndbcluster::key_cmp(uint keynr, const byte * old_row,
2074
                           const byte * new_row)
unknown's avatar
unknown committed
2075 2076 2077 2078 2079 2080 2081 2082 2083
{
  KEY_PART_INFO *key_part=table->key_info[keynr].key_part;
  KEY_PART_INFO *end=key_part+table->key_info[keynr].key_parts;

  for (; key_part != end ; key_part++)
  {
    if (key_part->null_bit)
    {
      if ((old_row[key_part->null_offset] & key_part->null_bit) !=
2084 2085
          (new_row[key_part->null_offset] & key_part->null_bit))
        return 1;
unknown's avatar
unknown committed
2086
    }
2087
    if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
unknown's avatar
unknown committed
2088 2089 2090
    {

      if (key_part->field->cmp_binary((char*) (old_row + key_part->offset),
2091 2092 2093
                                      (char*) (new_row + key_part->offset),
                                      (ulong) key_part->length))
        return 1;
unknown's avatar
unknown committed
2094 2095 2096 2097
    }
    else
    {
      if (memcmp(old_row+key_part->offset, new_row+key_part->offset,
2098 2099
                 key_part->length))
        return 1;
unknown's avatar
unknown committed
2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111
    }
  }
  return 0;
}

/*
  Update one record in NDB using primary key
*/

int ha_ndbcluster::update_row(const byte *old_data, byte *new_data)
{
  THD *thd= current_thd;
2112
  NdbTransaction *trans= m_active_trans;
2113
  NdbScanOperation* cursor= m_active_cursor;
unknown's avatar
unknown committed
2114 2115 2116 2117
  NdbOperation *op;
  uint i;
  DBUG_ENTER("update_row");
  
unknown's avatar
unknown committed
2118
  statistic_increment(thd->status_var.ha_update_count, &LOCK_status);
unknown's avatar
unknown committed
2119
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
2120
  {
unknown's avatar
unknown committed
2121
    table->timestamp_field->set_time();
2122 2123 2124
    // Set query_id so that field is really updated
    table->timestamp_field->query_id= thd->query_id;
  }
unknown's avatar
unknown committed
2125

2126
  /* Check for update of primary key for special handling */  
2127 2128
  if ((table->s->primary_key != MAX_KEY) &&
      (key_cmp(table->s->primary_key, old_data, new_data)))
2129
  {
2130
    int read_res, insert_res, delete_res, undo_res;
2131

2132
    DBUG_PRINT("info", ("primary key update, doing pk read+delete+insert"));
2133
    // Get all old fields, since we optimize away fields not in query
2134
    read_res= complemented_pk_read(old_data, new_data);
2135 2136 2137 2138 2139
    if (read_res)
    {
      DBUG_PRINT("info", ("pk read failed"));
      DBUG_RETURN(read_res);
    }
2140
    // Delete old row
2141
    m_primary_key_update= TRUE;
2142
    delete_res= delete_row(old_data);
2143
    m_primary_key_update= FALSE;
2144 2145 2146
    if (delete_res)
    {
      DBUG_PRINT("info", ("delete failed"));
2147
      DBUG_RETURN(delete_res);
2148
    }     
2149 2150
    // Insert new row
    DBUG_PRINT("info", ("delete succeded"));
2151
    m_primary_key_update= TRUE;
2152
    insert_res= write_row(new_data);
2153
    m_primary_key_update= FALSE;
2154 2155 2156 2157 2158
    if (insert_res)
    {
      DBUG_PRINT("info", ("insert failed"));
      if (trans->commitStatus() == NdbConnection::Started)
      {
2159
        // Undo delete_row(old_data)
2160
        m_primary_key_update= TRUE;
2161 2162 2163 2164 2165 2166
        undo_res= write_row((byte *)old_data);
        if (undo_res)
          push_warning(current_thd, 
                       MYSQL_ERROR::WARN_LEVEL_WARN, 
                       undo_res, 
                       "NDB failed undoing delete at primary key update");
2167 2168 2169 2170 2171
        m_primary_key_update= FALSE;
      }
      DBUG_RETURN(insert_res);
    }
    DBUG_PRINT("info", ("delete+insert succeeded"));
2172
    DBUG_RETURN(0);
2173
  }
2174

2175
  if (cursor)
unknown's avatar
unknown committed
2176
  {
2177 2178 2179 2180 2181 2182 2183 2184
    /*
      We are scanning records and want to update the record
      that was just found, call updateTuple on the cursor 
      to take over the lock to a new update operation
      And thus setting the primary key of the record from 
      the active record in cursor
    */
    DBUG_PRINT("info", ("Calling updateTuple on cursor"));
2185
    if (!(op= cursor->updateCurrentTuple()))
2186
      ERR_RETURN(trans->getNdbError());
2187
    m_ops_pending++;
unknown's avatar
unknown committed
2188
    if (uses_blob_value(FALSE))
2189
      m_blobs_pending= TRUE;
2190 2191 2192
  }
  else
  {  
unknown's avatar
unknown committed
2193
    if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) ||
2194
        op->updateTuple() != 0)
2195 2196
      ERR_RETURN(trans->getNdbError());  
    
2197
    if (table->s->primary_key == MAX_KEY) 
2198 2199 2200 2201 2202 2203
    {
      // This table has no primary key, use "hidden" primary key
      DBUG_PRINT("info", ("Using hidden key"));
      
      // Require that the PK for this record has previously been 
      // read into m_value
2204
      uint no_fields= table->s->fields;
2205
      const NdbRecAttr* rec= m_value[no_fields].rec;
2206 2207 2208 2209
      DBUG_ASSERT(rec);
      DBUG_DUMP("key", (char*)rec->aRef(), NDB_HIDDEN_PRIMARY_KEY_LENGTH);
      
      if (set_hidden_key(op, no_fields, rec->aRef()))
2210
        ERR_RETURN(op->getNdbError());
2211 2212 2213 2214
    } 
    else 
    {
      int res;
2215
      if ((res= set_primary_key_from_record(op, old_data)))
2216
        DBUG_RETURN(res);
2217
    }
unknown's avatar
unknown committed
2218 2219
  }

2220 2221
  m_rows_changed++;

unknown's avatar
unknown committed
2222
  // Set non-key attribute(s)
2223
  for (i= 0; i < table->s->fields; i++) 
unknown's avatar
unknown committed
2224 2225
  {
    Field *field= table->field[i];
2226
    if (((thd->query_id == field->query_id) || m_retrieve_all_fields) &&
unknown's avatar
unknown committed
2227
        (!(field->flags & PRI_KEY_FLAG)) &&
2228
        set_ndb_value(op, field, i))
unknown's avatar
unknown committed
2229 2230
      ERR_RETURN(op->getNdbError());
  }
2231

unknown's avatar
unknown committed
2232
  // Execute update operation
unknown's avatar
unknown committed
2233
  if (!cursor && execute_no_commit(this,trans) != 0) {
2234
    no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
2235
    DBUG_RETURN(ndb_err(trans));
2236
  }
unknown's avatar
unknown committed
2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247
  
  DBUG_RETURN(0);
}


/*
  Delete one record from NDB, using primary key 
*/

int ha_ndbcluster::delete_row(const byte *record)
{
unknown's avatar
unknown committed
2248
  THD *thd= current_thd;
2249
  NdbTransaction *trans= m_active_trans;
2250
  NdbScanOperation* cursor= m_active_cursor;
unknown's avatar
unknown committed
2251 2252 2253
  NdbOperation *op;
  DBUG_ENTER("delete_row");

unknown's avatar
unknown committed
2254
  statistic_increment(thd->status_var.ha_delete_count,&LOCK_status);
unknown's avatar
unknown committed
2255
  m_rows_changed++;
unknown's avatar
unknown committed
2256

2257
  if (cursor)
unknown's avatar
unknown committed
2258
  {
2259
    /*
2260
      We are scanning records and want to delete the record
2261
      that was just found, call deleteTuple on the cursor 
2262
      to take over the lock to a new delete operation
2263 2264 2265 2266
      And thus setting the primary key of the record from 
      the active record in cursor
    */
    DBUG_PRINT("info", ("Calling deleteTuple on cursor"));
2267
    if (cursor->deleteCurrentTuple() != 0)
2268
      ERR_RETURN(trans->getNdbError());     
2269
    m_ops_pending++;
unknown's avatar
unknown committed
2270

2271 2272
    no_uncommitted_rows_update(-1);

2273 2274 2275
    if (!m_primary_key_update)
      // If deleting from cursor, NoCommit will be handled in next_result
      DBUG_RETURN(0);
2276 2277
  }
  else
unknown's avatar
unknown committed
2278
  {
2279
    
unknown's avatar
unknown committed
2280
    if (!(op=trans->getNdbOperation((const NDBTAB *) m_table)) || 
2281
        op->deleteTuple() != 0)
2282 2283
      ERR_RETURN(trans->getNdbError());
    
2284 2285
    no_uncommitted_rows_update(-1);
    
2286
    if (table->s->primary_key == MAX_KEY) 
2287 2288 2289
    {
      // This table has no primary key, use "hidden" primary key
      DBUG_PRINT("info", ("Using hidden key"));
2290
      uint no_fields= table->s->fields;
2291
      const NdbRecAttr* rec= m_value[no_fields].rec;
2292 2293 2294
      DBUG_ASSERT(rec != NULL);
      
      if (set_hidden_key(op, no_fields, rec->aRef()))
2295
        ERR_RETURN(op->getNdbError());
2296 2297 2298 2299
    } 
    else 
    {
      int res;
2300 2301
      if ((res= set_primary_key_from_record(op, record)))
        return res;  
2302
    }
unknown's avatar
unknown committed
2303
  }
2304

unknown's avatar
unknown committed
2305
  // Execute delete operation
unknown's avatar
unknown committed
2306
  if (execute_no_commit(this,trans) != 0) {
2307
    no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
2308
    DBUG_RETURN(ndb_err(trans));
2309
  }
unknown's avatar
unknown committed
2310 2311
  DBUG_RETURN(0);
}
2312
  
unknown's avatar
unknown committed
2313 2314 2315 2316 2317
/*
  Unpack a record read from NDB 

  SYNOPSIS
    unpack_record()
2318
    buf                 Buffer to store read row
unknown's avatar
unknown committed
2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330

  NOTE
    The data for each row is read directly into the
    destination buffer. This function is primarily 
    called in order to check if any fields should be 
    set to null.
*/

void ha_ndbcluster::unpack_record(byte* buf)
{
  uint row_offset= (uint) (buf - table->record[0]);
  Field **field, **end;
unknown's avatar
unknown committed
2331
  NdbValue *value= m_value;
unknown's avatar
unknown committed
2332
  DBUG_ENTER("unpack_record");
2333

unknown's avatar
merge  
unknown committed
2334
  end= table->field + table->s->fields;
unknown's avatar
unknown committed
2335 2336
  
  // Set null flag(s)
2337
  bzero(buf, table->s->null_bytes);
unknown's avatar
merge  
unknown committed
2338
  for (field= table->field;
unknown's avatar
unknown committed
2339 2340 2341
       field < end;
       field++, value++)
  {
unknown's avatar
unknown committed
2342 2343 2344 2345 2346 2347
    if ((*value).ptr)
    {
      if (! ((*field)->flags & BLOB_FLAG))
      {
        if ((*value).rec->isNULL())
         (*field)->set_null(row_offset);
2348 2349 2350 2351 2352 2353
        else if ((*field)->type() == MYSQL_TYPE_BIT)
        {
          uint pack_len= (*field)->pack_length();
          if (pack_len < 5)
          {
            DBUG_PRINT("info", ("bit field H'%.8X", 
2354
                                (*value).rec->u_32_value()));
2355
            ((Field_bit *) *field)->store((longlong) 
2356 2357
                                          (*value).rec->u_32_value(),
                                          FALSE);
2358 2359 2360 2361 2362 2363 2364
          }
          else
          {
            DBUG_PRINT("info", ("bit field H'%.8X%.8X",
                                *(Uint32 *)(*value).rec->aRef(),
                                *((Uint32 *)(*value).rec->aRef()+1)));
            ((Field_bit *) *field)->store((longlong)
2365 2366
                                          (*value).rec->u_64_value(), TRUE);
          }
2367
        }
unknown's avatar
unknown committed
2368 2369 2370 2371
      }
      else
      {
        NdbBlob* ndb_blob= (*value).blob;
unknown's avatar
unknown committed
2372
        bool isNull= TRUE;
2373 2374 2375
#ifndef DBUG_OFF
        int ret= 
#endif
2376
          ndb_blob->getNull(isNull);
unknown's avatar
unknown committed
2377 2378
        DBUG_ASSERT(ret == 0);
        if (isNull)
2379
          (*field)->set_null(row_offset);
unknown's avatar
unknown committed
2380 2381
      }
    }
unknown's avatar
unknown committed
2382
  }
2383
  
unknown's avatar
unknown committed
2384 2385
#ifndef DBUG_OFF
  // Read and print all values that was fetched
2386
  if (table->s->primary_key == MAX_KEY)
unknown's avatar
unknown committed
2387 2388
  {
    // Table with hidden primary key
2389
    int hidden_no= table->s->fields;
unknown's avatar
unknown committed
2390
    const NDBTAB *tab= (const NDBTAB *) m_table;
unknown's avatar
unknown committed
2391
    const NDBCOL *hidden_col= tab->getColumn(hidden_no);
2392
    const NdbRecAttr* rec= m_value[hidden_no].rec;
unknown's avatar
unknown committed
2393 2394 2395 2396
    DBUG_ASSERT(rec);
    DBUG_PRINT("hidden", ("%d: %s \"%llu\"", hidden_no, 
                          hidden_col->getName(), rec->u_64_value()));
  } 
2397
  //print_results();
unknown's avatar
unknown committed
2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410
#endif
  DBUG_VOID_RETURN;
}

/*
  Utility function to print/dump the fetched field
 */

void ha_ndbcluster::print_results()
{
  DBUG_ENTER("print_results");

#ifndef DBUG_OFF
2411
  const NDBTAB *tab= (const NDBTAB*) m_table;
2412

unknown's avatar
unknown committed
2413 2414
  if (!_db_on_)
    DBUG_VOID_RETURN;
unknown's avatar
Merge  
unknown committed
2415

2416
  char buf_type[MAX_FIELD_WIDTH], buf_val[MAX_FIELD_WIDTH];
unknown's avatar
Merge  
unknown committed
2417
  String type(buf_type, sizeof(buf_type), &my_charset_bin);
2418
  String val(buf_val, sizeof(buf_val), &my_charset_bin);
2419
  for (uint f= 0; f < table->s->fields; f++)
unknown's avatar
unknown committed
2420
  {
unknown's avatar
Merge  
unknown committed
2421
    /* Use DBUG_PRINT since DBUG_FILE cannot be filtered out */
2422
    char buf[2000];
unknown's avatar
unknown committed
2423
    Field *field;
2424
    void* ptr;
unknown's avatar
unknown committed
2425
    NdbValue value;
unknown's avatar
unknown committed
2426

2427
    buf[0]= 0;
unknown's avatar
Merge  
unknown committed
2428
    field= table->field[f];
unknown's avatar
unknown committed
2429
    if (!(value= m_value[f]).ptr)
unknown's avatar
unknown committed
2430
    {
unknown's avatar
unknown committed
2431
      strmov(buf, "not read");
2432
      goto print_value;
unknown's avatar
unknown committed
2433
    }
2434

2435
    ptr= field->ptr;
unknown's avatar
unknown committed
2436 2437

    if (! (field->flags & BLOB_FLAG))
unknown's avatar
unknown committed
2438
    {
unknown's avatar
unknown committed
2439 2440
      if (value.rec->isNULL())
      {
unknown's avatar
unknown committed
2441
        strmov(buf, "NULL");
2442
        goto print_value;
unknown's avatar
unknown committed
2443
      }
2444 2445 2446 2447 2448
      type.length(0);
      val.length(0);
      field->sql_type(type);
      field->val_str(&val);
      my_snprintf(buf, sizeof(buf), "%s %s", type.c_ptr(), val.c_ptr());
unknown's avatar
unknown committed
2449 2450 2451
    }
    else
    {
2452
      NdbBlob *ndb_blob= value.blob;
unknown's avatar
unknown committed
2453
      bool isNull= TRUE;
unknown's avatar
unknown committed
2454
      ndb_blob->getNull(isNull);
unknown's avatar
unknown committed
2455 2456
      if (isNull)
        strmov(buf, "NULL");
unknown's avatar
unknown committed
2457
    }
unknown's avatar
Merge  
unknown committed
2458

2459
print_value:
unknown's avatar
Merge  
unknown committed
2460
    DBUG_PRINT("value", ("%u,%s: %s", f, field->field_name, buf));
unknown's avatar
unknown committed
2461 2462 2463 2464 2465 2466 2467 2468
  }
#endif
  DBUG_VOID_RETURN;
}


int ha_ndbcluster::index_init(uint index)
{
2469
  DBUG_ENTER("ha_ndbcluster::index_init");
unknown's avatar
unknown committed
2470 2471 2472 2473 2474 2475 2476
  DBUG_PRINT("enter", ("index: %u", index));
  DBUG_RETURN(handler::index_init(index));
}


int ha_ndbcluster::index_end()
{
2477
  DBUG_ENTER("ha_ndbcluster::index_end");
2478
  DBUG_RETURN(close_scan());
unknown's avatar
unknown committed
2479 2480
}

2481 2482 2483 2484 2485 2486 2487 2488
/**
 * Check if key contains null
 */
static
int
check_null_in_key(const KEY* key_info, const byte *key, uint key_len)
{
  KEY_PART_INFO *curr_part, *end_part;
2489
  const byte* end_ptr= key + key_len;
2490 2491 2492 2493 2494 2495
  curr_part= key_info->key_part;
  end_part= curr_part + key_info->key_parts;
  

  for (; curr_part != end_part && key < end_ptr; curr_part++)
  {
unknown's avatar
unknown committed
2496
    if (curr_part->null_bit && *key)
2497 2498 2499 2500 2501 2502
      return 1;

    key += curr_part->store_length;
  }
  return 0;
}
unknown's avatar
unknown committed
2503 2504

int ha_ndbcluster::index_read(byte *buf,
2505 2506
                              const byte *key, uint key_len, 
                              enum ha_rkey_function find_flag)
unknown's avatar
unknown committed
2507
{
2508
  DBUG_ENTER("ha_ndbcluster::index_read");
unknown's avatar
unknown committed
2509 2510 2511
  DBUG_PRINT("enter", ("active_index: %u, key_len: %u, find_flag: %d", 
                       active_index, key_len, find_flag));

unknown's avatar
unknown committed
2512
  int error;
2513 2514
  ndb_index_type type= get_index_type(active_index);
  const KEY* key_info= table->key_info+active_index;
unknown's avatar
unknown committed
2515 2516 2517 2518 2519
  switch (type){
  case PRIMARY_KEY_ORDERED_INDEX:
  case PRIMARY_KEY_INDEX:
    if (find_flag == HA_READ_KEY_EXACT && key_info->key_length == key_len)
    {
unknown's avatar
unknown committed
2520
      if (m_active_cursor && (error= close_scan()))
2521
        DBUG_RETURN(error);
unknown's avatar
unknown committed
2522 2523 2524 2525 2526 2527 2528 2529 2530
      DBUG_RETURN(pk_read(key, key_len, buf));
    }
    else if (type == PRIMARY_KEY_INDEX)
    {
      DBUG_RETURN(1);
    }
    break;
  case UNIQUE_ORDERED_INDEX:
  case UNIQUE_INDEX:
2531
    if (find_flag == HA_READ_KEY_EXACT && key_info->key_length == key_len &&
2532
        !check_null_in_key(key_info, key, key_len))
unknown's avatar
unknown committed
2533
    {
unknown's avatar
unknown committed
2534
      if (m_active_cursor && (error= close_scan()))
2535
        DBUG_RETURN(error);
unknown's avatar
unknown committed
2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546
      DBUG_RETURN(unique_index_read(key, key_len, buf));
    }
    else if (type == UNIQUE_INDEX)
    {
      DBUG_RETURN(1);
    }
    break;
  case ORDERED_INDEX:
    break;
  default:
  case UNDEFINED_INDEX:
unknown's avatar
unknown committed
2547
    DBUG_ASSERT(FALSE);
2548
    DBUG_RETURN(1);
unknown's avatar
unknown committed
2549 2550 2551
    break;
  }
  
2552
  key_range start_key;
2553 2554 2555
  start_key.key= key;
  start_key.length= key_len;
  start_key.flag= find_flag;
2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567
  bool descending= FALSE;
  switch (find_flag) {
  case HA_READ_KEY_OR_PREV:
  case HA_READ_BEFORE_KEY:
  case HA_READ_PREFIX_LAST:
  case HA_READ_PREFIX_LAST_OR_PREV:
    descending= TRUE;
    break;
  default:
    break;
  }
  error= ordered_index_scan(&start_key, 0, TRUE, descending, buf);  
unknown's avatar
unknown committed
2568
  DBUG_RETURN(error == HA_ERR_END_OF_FILE ? HA_ERR_KEY_NOT_FOUND : error);
unknown's avatar
unknown committed
2569 2570 2571 2572
}


int ha_ndbcluster::index_read_idx(byte *buf, uint index_no, 
2573 2574
                              const byte *key, uint key_len, 
                              enum ha_rkey_function find_flag)
unknown's avatar
unknown committed
2575
{
unknown's avatar
unknown committed
2576
  statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status);
2577
  DBUG_ENTER("ha_ndbcluster::index_read_idx");
unknown's avatar
unknown committed
2578 2579 2580 2581 2582 2583 2584 2585
  DBUG_PRINT("enter", ("index_no: %u, key_len: %u", index_no, key_len));  
  index_init(index_no);  
  DBUG_RETURN(index_read(buf, key, key_len, find_flag));
}


int ha_ndbcluster::index_next(byte *buf)
{
2586
  DBUG_ENTER("ha_ndbcluster::index_next");
unknown's avatar
unknown committed
2587
  statistic_increment(current_thd->status_var.ha_read_next_count,
2588
                      &LOCK_status);
2589
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
2590 2591 2592 2593 2594
}


int ha_ndbcluster::index_prev(byte *buf)
{
2595
  DBUG_ENTER("ha_ndbcluster::index_prev");
unknown's avatar
unknown committed
2596
  statistic_increment(current_thd->status_var.ha_read_prev_count,
2597
                      &LOCK_status);
2598
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
2599 2600 2601 2602 2603
}


int ha_ndbcluster::index_first(byte *buf)
{
2604
  DBUG_ENTER("ha_ndbcluster::index_first");
unknown's avatar
unknown committed
2605
  statistic_increment(current_thd->status_var.ha_read_first_count,
2606
                      &LOCK_status);
unknown's avatar
unknown committed
2607 2608 2609
  // Start the ordered index scan and fetch the first row

  // Only HA_READ_ORDER indexes get called by index_first
2610
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, FALSE, buf));
unknown's avatar
unknown committed
2611 2612 2613 2614 2615
}


int ha_ndbcluster::index_last(byte *buf)
{
2616
  DBUG_ENTER("ha_ndbcluster::index_last");
2617
  statistic_increment(current_thd->status_var.ha_read_last_count,&LOCK_status);
2618
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, TRUE, buf));
unknown's avatar
unknown committed
2619 2620
}

2621 2622 2623 2624 2625
int ha_ndbcluster::index_read_last(byte * buf, const byte * key, uint key_len)
{
  DBUG_ENTER("ha_ndbcluster::index_read_last");
  DBUG_RETURN(index_read(buf, key, key_len, HA_READ_PREFIX_LAST));
}
unknown's avatar
unknown committed
2626

2627 2628
inline
int ha_ndbcluster::read_range_first_to_buf(const key_range *start_key,
2629 2630 2631
                                           const key_range *end_key,
                                           bool eq_r, bool sorted,
                                           byte* buf)
2632
{
2633
  KEY* key_info;
2634 2635
  int error= 1; 
  DBUG_ENTER("ha_ndbcluster::read_range_first_to_buf");
unknown's avatar
unknown committed
2636
  DBUG_PRINT("info", ("eq_r: %d, sorted: %d", eq_r, sorted));
2637

2638
  switch (get_index_type(active_index)){
2639
  case PRIMARY_KEY_ORDERED_INDEX:
2640
  case PRIMARY_KEY_INDEX:
2641 2642
    key_info= table->key_info + active_index;
    if (start_key && 
2643 2644
        start_key->length == key_info->key_length &&
        start_key->flag == HA_READ_KEY_EXACT)
2645
    {
unknown's avatar
unknown committed
2646
      if (m_active_cursor && (error= close_scan()))
2647
        DBUG_RETURN(error);
2648 2649 2650
      error= pk_read(start_key->key, start_key->length, buf);      
      DBUG_RETURN(error == HA_ERR_KEY_NOT_FOUND ? HA_ERR_END_OF_FILE : error);
    }
2651
    break;
2652
  case UNIQUE_ORDERED_INDEX:
2653
  case UNIQUE_INDEX:
2654
    key_info= table->key_info + active_index;
2655
    if (start_key && start_key->length == key_info->key_length &&
2656 2657
        start_key->flag == HA_READ_KEY_EXACT && 
        !check_null_in_key(key_info, start_key->key, start_key->length))
2658
    {
unknown's avatar
unknown committed
2659
      if (m_active_cursor && (error= close_scan()))
2660
        DBUG_RETURN(error);
2661 2662 2663
      error= unique_index_read(start_key->key, start_key->length, buf);
      DBUG_RETURN(error == HA_ERR_KEY_NOT_FOUND ? HA_ERR_END_OF_FILE : error);
    }
2664 2665 2666 2667
    break;
  default:
    break;
  }
2668 2669

  // Start the ordered index scan and fetch the first row
2670
  error= ordered_index_scan(start_key, end_key, sorted, FALSE, buf);
2671 2672 2673
  DBUG_RETURN(error);
}

2674

unknown's avatar
unknown committed
2675
int ha_ndbcluster::read_range_first(const key_range *start_key,
2676 2677
                                    const key_range *end_key,
                                    bool eq_r, bool sorted)
unknown's avatar
unknown committed
2678 2679 2680 2681 2682
{
  byte* buf= table->record[0];
  DBUG_ENTER("ha_ndbcluster::read_range_first");
  
  DBUG_RETURN(read_range_first_to_buf(start_key,
2683 2684 2685 2686
                                      end_key,
                                      eq_r, 
                                      sorted,
                                      buf));
unknown's avatar
unknown committed
2687 2688
}

2689
int ha_ndbcluster::read_range_next()
2690 2691 2692 2693 2694 2695
{
  DBUG_ENTER("ha_ndbcluster::read_range_next");
  DBUG_RETURN(next_result(table->record[0]));
}


unknown's avatar
unknown committed
2696 2697
int ha_ndbcluster::rnd_init(bool scan)
{
2698
  NdbScanOperation *cursor= m_active_cursor;
unknown's avatar
unknown committed
2699 2700
  DBUG_ENTER("rnd_init");
  DBUG_PRINT("enter", ("scan: %d", scan));
2701
  // Check if scan is to be restarted
unknown's avatar
unknown committed
2702 2703 2704 2705
  if (cursor)
  {
    if (!scan)
      DBUG_RETURN(1);
unknown's avatar
unknown committed
2706
    if (cursor->restart(m_force_send) != 0)
2707 2708 2709 2710
    {
      DBUG_ASSERT(0);
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
2711
  }
2712
  index_init(table->s->primary_key);
unknown's avatar
unknown committed
2713 2714 2715
  DBUG_RETURN(0);
}

2716 2717
int ha_ndbcluster::close_scan()
{
2718
  NdbTransaction *trans= m_active_trans;
2719 2720
  DBUG_ENTER("close_scan");

unknown's avatar
unknown committed
2721 2722
  m_multi_cursor= 0;
  if (!m_active_cursor && !m_multi_cursor)
2723 2724
    DBUG_RETURN(1);

unknown's avatar
unknown committed
2725
  NdbScanOperation *cursor= m_active_cursor ? m_active_cursor : m_multi_cursor;
unknown's avatar
unknown committed
2726
  
2727
  if (m_ops_pending)
unknown's avatar
unknown committed
2728 2729 2730 2731 2732
  {
    /*
      Take over any pending transactions to the 
      deleteing/updating transaction before closing the scan    
    */
2733
    DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending));    
unknown's avatar
unknown committed
2734
    if (execute_no_commit(this,trans) != 0) {
2735
      no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
2736
      DBUG_RETURN(ndb_err(trans));
2737
    }
2738
    m_ops_pending= 0;
unknown's avatar
unknown committed
2739 2740
  }
  
unknown's avatar
unknown committed
2741
  cursor->close(m_force_send, TRUE);
unknown's avatar
unknown committed
2742
  m_active_cursor= m_multi_cursor= NULL;
unknown's avatar
unknown committed
2743
  DBUG_RETURN(0);
2744
}
unknown's avatar
unknown committed
2745 2746 2747 2748

int ha_ndbcluster::rnd_end()
{
  DBUG_ENTER("rnd_end");
2749
  DBUG_RETURN(close_scan());
unknown's avatar
unknown committed
2750 2751 2752 2753 2754 2755
}


int ha_ndbcluster::rnd_next(byte *buf)
{
  DBUG_ENTER("rnd_next");
unknown's avatar
unknown committed
2756
  statistic_increment(current_thd->status_var.ha_read_rnd_next_count,
2757
                      &LOCK_status);
2758

unknown's avatar
unknown committed
2759
  if (!m_active_cursor)
2760 2761
    DBUG_RETURN(full_table_scan(buf));
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774
}


/*
  An "interesting" record has been found and it's pk 
  retrieved by calling position
  Now it's time to read the record from db once 
  again
*/

int ha_ndbcluster::rnd_pos(byte *buf, byte *pos)
{
  DBUG_ENTER("rnd_pos");
unknown's avatar
unknown committed
2775
  statistic_increment(current_thd->status_var.ha_read_rnd_count,
2776
                      &LOCK_status);
unknown's avatar
unknown committed
2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796
  // The primary key for the record is stored in pos
  // Perform a pk_read using primary key "index"
  DBUG_RETURN(pk_read(pos, ref_length, buf));  
}


/*
  Store the primary key of this record in ref 
  variable, so that the row can be retrieved again later
  using "reference" in rnd_pos
*/

void ha_ndbcluster::position(const byte *record)
{
  KEY *key_info;
  KEY_PART_INFO *key_part;
  KEY_PART_INFO *end;
  byte *buff;
  DBUG_ENTER("position");

2797
  if (table->s->primary_key != MAX_KEY) 
unknown's avatar
unknown committed
2798
  {
2799
    key_info= table->key_info + table->s->primary_key;
unknown's avatar
unknown committed
2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814
    key_part= key_info->key_part;
    end= key_part + key_info->key_parts;
    buff= ref;
    
    for (; key_part != end; key_part++) 
    {
      if (key_part->null_bit) {
        /* Store 0 if the key part is a NULL part */      
        if (record[key_part->null_offset]
            & key_part->null_bit) {
          *buff++= 1;
          continue;
        }      
        *buff++= 0;
      }
2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834

      size_t len = key_part->length;
      const byte * ptr = record + key_part->offset;
      Field *field = key_part->field;
      if ((field->type() ==  MYSQL_TYPE_VARCHAR) &&
	  ((Field_varstring*)field)->length_bytes == 1)
      {
	/** 
	 * Keys always use 2 bytes length
	 */
	buff[0] = ptr[0];
	buff[1] = 0;
	memcpy(buff+2, ptr + 1, len);	
	len += 2;
      }
      else
      {
	memcpy(buff, ptr, len);
      }
      buff += len;
unknown's avatar
unknown committed
2835 2836 2837 2838 2839 2840
    }
  } 
  else 
  {
    // No primary key, get hidden key
    DBUG_PRINT("info", ("Getting hidden key"));
2841
    int hidden_no= table->s->fields;
2842
    const NdbRecAttr* rec= m_value[hidden_no].rec;
2843 2844
    memcpy(ref, (const void*)rec->aRef(), ref_length);
#ifndef DBUG_OFF
unknown's avatar
unknown committed
2845
    const NDBTAB *tab= (const NDBTAB *) m_table;  
unknown's avatar
unknown committed
2846 2847 2848 2849 2850
    const NDBCOL *hidden_col= tab->getColumn(hidden_no);
    DBUG_ASSERT(hidden_col->getPrimaryKey() && 
                hidden_col->getAutoIncrement() &&
                rec != NULL && 
                ref_length == NDB_HIDDEN_PRIMARY_KEY_LENGTH);
2851
#endif
unknown's avatar
unknown committed
2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870
  }
  
  DBUG_DUMP("ref", (char*)ref, ref_length);
  DBUG_VOID_RETURN;
}


void ha_ndbcluster::info(uint flag)
{
  DBUG_ENTER("info");
  DBUG_PRINT("enter", ("flag: %d", flag));
  
  if (flag & HA_STATUS_POS)
    DBUG_PRINT("info", ("HA_STATUS_POS"));
  if (flag & HA_STATUS_NO_LOCK)
    DBUG_PRINT("info", ("HA_STATUS_NO_LOCK"));
  if (flag & HA_STATUS_TIME)
    DBUG_PRINT("info", ("HA_STATUS_TIME"));
  if (flag & HA_STATUS_VARIABLE)
2871
  {
unknown's avatar
unknown committed
2872
    DBUG_PRINT("info", ("HA_STATUS_VARIABLE"));
2873 2874
    if (m_table_info)
    {
2875
      if (m_ha_not_exact_count)
2876
        records= 100;
2877
      else
2878
        records_update();
2879 2880 2881
    }
    else
    {
2882 2883 2884
      if ((my_errno= check_ndb_connection()))
        DBUG_VOID_RETURN;
      Ndb *ndb= get_ndb();
2885 2886
      struct Ndb_statistics stat;
      if (current_thd->variables.ndb_use_exact_count &&
2887
          ndb_get_table_statistics(ndb, m_tabname, &stat) == 0)
2888
      {
2889 2890 2891
        mean_rec_length= stat.row_size;
        data_file_length= stat.fragment_memory;
        records= stat.row_count;
2892 2893 2894
      }
      else
      {
2895 2896
        mean_rec_length= 0;
        records= 100;
2897
      }
2898
    }
2899
  }
unknown's avatar
unknown committed
2900 2901 2902 2903 2904
  if (flag & HA_STATUS_CONST)
  {
    DBUG_PRINT("info", ("HA_STATUS_CONST"));
    set_rec_per_key();
  }
unknown's avatar
unknown committed
2905
  if (flag & HA_STATUS_ERRKEY)
2906
  {
unknown's avatar
unknown committed
2907
    DBUG_PRINT("info", ("HA_STATUS_ERRKEY"));
2908
    errkey= m_dupkey;
2909
  }
unknown's avatar
unknown committed
2910
  if (flag & HA_STATUS_AUTO)
2911
  {
unknown's avatar
unknown committed
2912
    DBUG_PRINT("info", ("HA_STATUS_AUTO"));
2913 2914 2915 2916 2917 2918 2919 2920
    if (m_table)
    {
      Ndb *ndb= get_ndb();
      
      auto_increment_value= 
        ndb->readAutoIncrementValue((const NDBTAB *) m_table);
    }
  }
unknown's avatar
unknown committed
2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936
  DBUG_VOID_RETURN;
}


int ha_ndbcluster::extra(enum ha_extra_function operation)
{
  DBUG_ENTER("extra");
  switch (operation) {
  case HA_EXTRA_NORMAL:              /* Optimize for space (def) */
    DBUG_PRINT("info", ("HA_EXTRA_NORMAL"));
    break;
  case HA_EXTRA_QUICK:                 /* Optimize for speed */
    DBUG_PRINT("info", ("HA_EXTRA_QUICK"));
    break;
  case HA_EXTRA_RESET:                 /* Reset database to after open */
    DBUG_PRINT("info", ("HA_EXTRA_RESET"));
unknown's avatar
unknown committed
2937 2938
    DBUG_PRINT("info", ("Clearing condition stack"));
    cond_clear();
unknown's avatar
unknown committed
2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007
    break;
  case HA_EXTRA_CACHE:                 /* Cash record in HA_rrnd() */
    DBUG_PRINT("info", ("HA_EXTRA_CACHE"));
    break;
  case HA_EXTRA_NO_CACHE:              /* End cacheing of records (def) */
    DBUG_PRINT("info", ("HA_EXTRA_NO_CACHE"));
    break;
  case HA_EXTRA_NO_READCHECK:          /* No readcheck on update */
    DBUG_PRINT("info", ("HA_EXTRA_NO_READCHECK"));
    break;
  case HA_EXTRA_READCHECK:             /* Use readcheck (def) */
    DBUG_PRINT("info", ("HA_EXTRA_READCHECK"));
    break;
  case HA_EXTRA_KEYREAD:               /* Read only key to database */
    DBUG_PRINT("info", ("HA_EXTRA_KEYREAD"));
    break;
  case HA_EXTRA_NO_KEYREAD:            /* Normal read of records (def) */
    DBUG_PRINT("info", ("HA_EXTRA_NO_KEYREAD"));
    break;
  case HA_EXTRA_NO_USER_CHANGE:        /* No user is allowed to write */
    DBUG_PRINT("info", ("HA_EXTRA_NO_USER_CHANGE"));
    break;
  case HA_EXTRA_KEY_CACHE:
    DBUG_PRINT("info", ("HA_EXTRA_KEY_CACHE"));
    break;
  case HA_EXTRA_NO_KEY_CACHE:
    DBUG_PRINT("info", ("HA_EXTRA_NO_KEY_CACHE"));
    break;
  case HA_EXTRA_WAIT_LOCK:            /* Wait until file is avalably (def) */
    DBUG_PRINT("info", ("HA_EXTRA_WAIT_LOCK"));
    break;
  case HA_EXTRA_NO_WAIT_LOCK:         /* If file is locked, return quickly */
    DBUG_PRINT("info", ("HA_EXTRA_NO_WAIT_LOCK"));
    break;
  case HA_EXTRA_WRITE_CACHE:           /* Use write cache in ha_write() */
    DBUG_PRINT("info", ("HA_EXTRA_WRITE_CACHE"));
    break;
  case HA_EXTRA_FLUSH_CACHE:           /* flush write_record_cache */
    DBUG_PRINT("info", ("HA_EXTRA_FLUSH_CACHE"));
    break;
  case HA_EXTRA_NO_KEYS:               /* Remove all update of keys */
    DBUG_PRINT("info", ("HA_EXTRA_NO_KEYS"));
    break;
  case HA_EXTRA_KEYREAD_CHANGE_POS:         /* Keyread, but change pos */
    DBUG_PRINT("info", ("HA_EXTRA_KEYREAD_CHANGE_POS")); /* xxxxchk -r must be used */
    break;                                  
  case HA_EXTRA_REMEMBER_POS:          /* Remember pos for next/prev */
    DBUG_PRINT("info", ("HA_EXTRA_REMEMBER_POS"));
    break;
  case HA_EXTRA_RESTORE_POS:
    DBUG_PRINT("info", ("HA_EXTRA_RESTORE_POS"));
    break;
  case HA_EXTRA_REINIT_CACHE:          /* init cache from current record */
    DBUG_PRINT("info", ("HA_EXTRA_REINIT_CACHE"));
    break;
  case HA_EXTRA_FORCE_REOPEN:          /* Datafile have changed on disk */
    DBUG_PRINT("info", ("HA_EXTRA_FORCE_REOPEN"));
    break;
  case HA_EXTRA_FLUSH:                 /* Flush tables to disk */
    DBUG_PRINT("info", ("HA_EXTRA_FLUSH"));
    break;
  case HA_EXTRA_NO_ROWS:               /* Don't write rows */
    DBUG_PRINT("info", ("HA_EXTRA_NO_ROWS"));
    break;
  case HA_EXTRA_RESET_STATE:           /* Reset positions */
    DBUG_PRINT("info", ("HA_EXTRA_RESET_STATE"));
    break;
  case HA_EXTRA_IGNORE_DUP_KEY:       /* Dup keys don't rollback everything*/
    DBUG_PRINT("info", ("HA_EXTRA_IGNORE_DUP_KEY"));
3008 3009 3010 3011 3012 3013
    if (current_thd->lex->sql_command == SQLCOM_REPLACE)
    {
      DBUG_PRINT("info", ("Turning ON use of write instead of insert"));
      m_use_write= TRUE;
    } else 
    {
3014 3015
      DBUG_PRINT("info", ("Ignoring duplicate key"));
      m_ignore_dup_key= TRUE;
3016
    }
unknown's avatar
unknown committed
3017 3018 3019 3020
    break;
  case HA_EXTRA_NO_IGNORE_DUP_KEY:
    DBUG_PRINT("info", ("HA_EXTRA_NO_IGNORE_DUP_KEY"));
    DBUG_PRINT("info", ("Turning OFF use of write instead of insert"));
unknown's avatar
unknown committed
3021
    m_use_write= FALSE;
3022
    m_ignore_dup_key= FALSE;
unknown's avatar
unknown committed
3023 3024
    break;
  case HA_EXTRA_RETRIEVE_ALL_COLS:    /* Retrieve all columns, not just those
3025 3026
                                         where field->query_id is the same as
                                         the current query id */
unknown's avatar
unknown committed
3027
    DBUG_PRINT("info", ("HA_EXTRA_RETRIEVE_ALL_COLS"));
3028
    m_retrieve_all_fields= TRUE;
unknown's avatar
unknown committed
3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040
    break;
  case HA_EXTRA_PREPARE_FOR_DELETE:
    DBUG_PRINT("info", ("HA_EXTRA_PREPARE_FOR_DELETE"));
    break;
  case HA_EXTRA_PREPARE_FOR_UPDATE:     /* Remove read cache if problems */
    DBUG_PRINT("info", ("HA_EXTRA_PREPARE_FOR_UPDATE"));
    break;
  case HA_EXTRA_PRELOAD_BUFFER_SIZE: 
    DBUG_PRINT("info", ("HA_EXTRA_PRELOAD_BUFFER_SIZE"));
    break;
  case HA_EXTRA_RETRIEVE_PRIMARY_KEY: 
    DBUG_PRINT("info", ("HA_EXTRA_RETRIEVE_PRIMARY_KEY"));
3041
    m_retrieve_primary_key= TRUE;
unknown's avatar
unknown committed
3042 3043 3044 3045 3046 3047
    break;
  case HA_EXTRA_CHANGE_KEY_TO_UNIQUE: 
    DBUG_PRINT("info", ("HA_EXTRA_CHANGE_KEY_TO_UNIQUE"));
    break;
  case HA_EXTRA_CHANGE_KEY_TO_DUP: 
    DBUG_PRINT("info", ("HA_EXTRA_CHANGE_KEY_TO_DUP"));
3048 3049
  case HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
    DBUG_PRINT("info", ("HA_EXTRA_KEYREAD_PRESERVE_FIELDS"));
unknown's avatar
unknown committed
3050 3051 3052 3053 3054 3055 3056
    break;

  }
  
  DBUG_RETURN(0);
}

3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069
/* 
   Start of an insert, remember number of rows to be inserted, it will
   be used in write_row and get_autoincrement to send an optimal number
   of rows in each roundtrip to the server

   SYNOPSIS
   rows     number of rows to insert, 0 if unknown

*/

void ha_ndbcluster::start_bulk_insert(ha_rows rows)
{
  int bytes, batch;
unknown's avatar
unknown committed
3070
  const NDBTAB *tab= (const NDBTAB *) m_table;    
3071 3072

  DBUG_ENTER("start_bulk_insert");
unknown's avatar
unknown committed
3073
  DBUG_PRINT("enter", ("rows: %d", (int)rows));
3074
  
3075 3076
  m_rows_inserted= (ha_rows) 0;
  if (rows == (ha_rows) 0)
unknown's avatar
unknown committed
3077
  {
3078 3079
    /* We don't know how many will be inserted, guess */
    m_rows_to_insert= m_autoincrement_prefetch;
unknown's avatar
unknown committed
3080
  }
3081 3082
  else
    m_rows_to_insert= rows; 
3083 3084 3085 3086 3087 3088 3089 3090

  /* 
    Calculate how many rows that should be inserted
    per roundtrip to NDB. This is done in order to minimize the 
    number of roundtrips as much as possible. However performance will 
    degrade if too many bytes are inserted, thus it's limited by this 
    calculation.   
  */
3091
  const int bytesperbatch= 8192;
3092
  bytes= 12 + tab->getRowSizeInBytes() + 4 * tab->getNoOfColumns();
3093
  batch= bytesperbatch/bytes;
3094 3095
  batch= batch == 0 ? 1 : batch;
  DBUG_PRINT("info", ("batch: %d, bytes: %d", batch, bytes));
3096
  m_bulk_insert_rows= batch;
3097 3098 3099 3100 3101 3102 3103 3104 3105

  DBUG_VOID_RETURN;
}

/*
  End of an insert
 */
int ha_ndbcluster::end_bulk_insert()
{
3106 3107
  int error= 0;

3108
  DBUG_ENTER("end_bulk_insert");
3109
  // Check if last inserts need to be flushed
3110
  if (m_bulk_insert_not_flushed)
3111
  {
3112
    NdbTransaction *trans= m_active_trans;
3113 3114 3115
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
                        "rows_inserted:%d, bulk_insert_rows: %d", 
3116
                        (int) m_rows_inserted, (int) m_bulk_insert_rows)); 
3117
    m_bulk_insert_not_flushed= FALSE;
unknown's avatar
unknown committed
3118
    if (execute_no_commit(this,trans) != 0) {
3119
      no_uncommitted_rows_execute_failure();
3120
      my_errno= error= ndb_err(trans);
3121
    }
3122 3123
  }

3124 3125
  m_rows_inserted= (ha_rows) 0;
  m_rows_to_insert= (ha_rows) 1;
3126
  DBUG_RETURN(error);
3127 3128
}

unknown's avatar
unknown committed
3129 3130 3131 3132

int ha_ndbcluster::extra_opt(enum ha_extra_function operation, ulong cache_size)
{
  DBUG_ENTER("extra_opt");
unknown's avatar
unknown committed
3133
  DBUG_PRINT("enter", ("cache_size: %lu", cache_size));
unknown's avatar
unknown committed
3134 3135 3136
  DBUG_RETURN(extra(operation));
}

unknown's avatar
unknown committed
3137 3138 3139 3140
static const char *ha_ndbcluster_exts[] = {
 ha_ndb_ext,
 NullS
};
unknown's avatar
unknown committed
3141

3142
const char** ha_ndbcluster::bas_ext() const
unknown's avatar
unknown committed
3143 3144 3145
{
  return ha_ndbcluster_exts;
}
unknown's avatar
unknown committed
3146 3147 3148 3149 3150 3151 3152 3153 3154

/*
  How many seeks it will take to read through the table
  This is to be comparable to the number returned by records_in_range so
  that we can decide if we should scan the table or use keys.
*/

double ha_ndbcluster::scan_time()
{
unknown's avatar
unknown committed
3155 3156 3157
  DBUG_ENTER("ha_ndbcluster::scan_time()");
  double res= rows2double(records*1000);
  DBUG_PRINT("exit", ("table: %s value: %f", 
3158
                      m_tabname, res));
unknown's avatar
unknown committed
3159
  DBUG_RETURN(res);
unknown's avatar
unknown committed
3160 3161
}

unknown's avatar
unknown committed
3162 3163 3164 3165 3166 3167 3168
/*
  Convert MySQL table locks into locks supported by Ndb Cluster.
  Note that MySQL Cluster does currently not support distributed
  table locks, so to be safe one should set cluster in Single
  User Mode, before relying on table locks when updating tables
  from several MySQL servers
*/
unknown's avatar
unknown committed
3169 3170 3171 3172 3173 3174 3175 3176

THR_LOCK_DATA **ha_ndbcluster::store_lock(THD *thd,
                                          THR_LOCK_DATA **to,
                                          enum thr_lock_type lock_type)
{
  DBUG_ENTER("store_lock");
  if (lock_type != TL_IGNORE && m_lock.type == TL_UNLOCK) 
  {
unknown's avatar
unknown committed
3177

unknown's avatar
unknown committed
3178 3179 3180
    /* If we are not doing a LOCK TABLE, then allow multiple
       writers */
    
3181 3182 3183
    /* Since NDB does not currently have table locks
       this is treated as a ordinary lock */

3184
    if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
unknown's avatar
unknown committed
3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199
         lock_type <= TL_WRITE) && !thd->in_lock_tables)      
      lock_type= TL_WRITE_ALLOW_WRITE;
    
    /* In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
       MySQL would use the lock TL_READ_NO_INSERT on t2, and that
       would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
       to t2. Convert the lock to a normal read lock to allow
       concurrent inserts to t2. */
    
    if (lock_type == TL_READ_NO_INSERT && !thd->in_lock_tables)
      lock_type= TL_READ;
    
    m_lock.type=lock_type;
  }
  *to++= &m_lock;
3200 3201

  DBUG_PRINT("exit", ("lock_type: %d", lock_type));
unknown's avatar
unknown committed
3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223
  
  DBUG_RETURN(to);
}

#ifndef DBUG_OFF
#define PRINT_OPTION_FLAGS(t) { \
      if (t->options & OPTION_NOT_AUTOCOMMIT) \
        DBUG_PRINT("thd->options", ("OPTION_NOT_AUTOCOMMIT")); \
      if (t->options & OPTION_BEGIN) \
        DBUG_PRINT("thd->options", ("OPTION_BEGIN")); \
      if (t->options & OPTION_TABLE_LOCK) \
        DBUG_PRINT("thd->options", ("OPTION_TABLE_LOCK")); \
}
#else
#define PRINT_OPTION_FLAGS(t)
#endif


/*
  As MySQL will execute an external lock for every new table it uses
  we can use this to start the transactions.
  If we are in auto_commit mode we just need to start a transaction
3224
  for the statement, this will be stored in thd_ndb.stmt.
unknown's avatar
unknown committed
3225
  If not, we have to start a master transaction if there doesn't exist
3226
  one from before, this will be stored in thd_ndb.all
unknown's avatar
unknown committed
3227 3228 3229
 
  When a table lock is held one transaction will be started which holds
  the table lock and for each statement a hupp transaction will be started  
3230
  If we are locking the table then:
3231
  - save the NdbDictionary::Table for easy access
3232 3233
  - save reference to table statistics
  - refresh list of the indexes for the table if needed (if altered)
unknown's avatar
unknown committed
3234 3235 3236 3237 3238
 */

int ha_ndbcluster::external_lock(THD *thd, int lock_type)
{
  int error=0;
3239
  NdbTransaction* trans= NULL;
unknown's avatar
unknown committed
3240 3241 3242 3243 3244 3245

  DBUG_ENTER("external_lock");
  /*
    Check that this handler instance has a connection
    set up to the Ndb object of thd
   */
3246
  if (check_ndb_connection(thd))
unknown's avatar
unknown committed
3247
    DBUG_RETURN(1);
3248

3249
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
3250
  Ndb *ndb= thd_ndb->ndb;
3251

3252 3253
  DBUG_PRINT("enter", ("thd: %x, thd_ndb: %x, thd_ndb->lock_count: %d",
                       thd, thd_ndb, thd_ndb->lock_count));
3254

unknown's avatar
unknown committed
3255 3256
  if (lock_type != F_UNLCK)
  {
3257
    DBUG_PRINT("info", ("lock_type != F_UNLCK"));
3258
    if (!thd_ndb->lock_count++)
unknown's avatar
unknown committed
3259 3260
    {
      PRINT_OPTION_FLAGS(thd);
3261
      if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) 
unknown's avatar
unknown committed
3262 3263
      {
        // Autocommit transaction
3264
        DBUG_ASSERT(!thd_ndb->stmt);
unknown's avatar
unknown committed
3265 3266
        DBUG_PRINT("trans",("Starting transaction stmt"));      

3267
        trans= ndb->startTransaction();
unknown's avatar
unknown committed
3268
        if (trans == NULL)
3269
          ERR_RETURN(ndb->getNdbError());
3270
        no_uncommitted_rows_reset(thd);
3271 3272
        thd_ndb->stmt= trans;
        trans_register_ha(thd, FALSE, &ndbcluster_hton);
unknown's avatar
unknown committed
3273 3274 3275
      } 
      else 
      { 
3276
        if (!thd_ndb->all)
3277
        {
unknown's avatar
unknown committed
3278 3279 3280 3281
          // Not autocommit transaction
          // A "master" transaction ha not been started yet
          DBUG_PRINT("trans",("starting transaction, all"));
          
3282
          trans= ndb->startTransaction();
unknown's avatar
unknown committed
3283
          if (trans == NULL)
3284
            ERR_RETURN(ndb->getNdbError());
3285
          no_uncommitted_rows_reset(thd);
3286 3287
          thd_ndb->all= trans; 
          trans_register_ha(thd, TRUE, &ndbcluster_hton);
unknown's avatar
unknown committed
3288 3289 3290 3291 3292 3293 3294 3295

          /*
            If this is the start of a LOCK TABLE, a table look 
            should be taken on the table in NDB
           
            Check if it should be read or write lock
           */
          if (thd->options & (OPTION_TABLE_LOCK))
3296
          {
unknown's avatar
unknown committed
3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315
            //lockThisTable();
            DBUG_PRINT("info", ("Locking the table..." ));
          }

        }
      }
    }
    /*
      This is the place to make sure this handler instance
      has a started transaction.
     
      The transaction is started by the first handler on which 
      MySQL Server calls external lock
     
      Other handlers in the same stmt or transaction should use 
      the same NDB transaction. This is done by setting up the m_active_trans
      pointer to point to the NDB transaction. 
     */

3316 3317 3318
    // store thread specific data first to set the right context
    m_force_send=          thd->variables.ndb_force_send;
    m_ha_not_exact_count= !thd->variables.ndb_use_exact_count;
3319 3320
    m_autoincrement_prefetch= 
      (ha_rows) thd->variables.ndb_autoincrement_prefetch_sz;
3321 3322 3323 3324 3325
    if (!thd->transaction.on)
      m_transaction_on= FALSE;
    else
      m_transaction_on= thd->variables.ndb_use_transactions;

3326
    m_active_trans= thd_ndb->all ? thd_ndb->all : thd_ndb->stmt;
unknown's avatar
unknown committed
3327
    DBUG_ASSERT(m_active_trans);
3328
    // Start of transaction
3329
    m_rows_changed= 0;
3330
    m_retrieve_all_fields= FALSE;
3331
    m_retrieve_primary_key= FALSE;
3332
    m_ops_pending= 0;
3333
    {
3334
      NDBDICT *dict= ndb->getDictionary();
3335 3336 3337
      const NDBTAB *tab;
      void *tab_info;
      if (!(tab= dict->getTable(m_tabname, &tab_info)))
3338
        ERR_RETURN(dict->getNdbError());
3339 3340 3341
      DBUG_PRINT("info", ("Table schema version: %d", 
                          tab->getObjectVersion()));
      // Check if thread has stale local cache
3342 3343 3344 3345
      // New transaction must not use old tables... (trans != 0)
      // Running might...
      if ((trans && tab->getObjectStatus() != NdbDictionary::Object::Retrieved)
	  || tab->getObjectStatus() == NdbDictionary::Object::Invalid)
3346 3347
      {
        invalidate_dictionary_cache(FALSE);
3348
        if (!(tab= dict->getTable(m_tabname, &tab_info)))
3349 3350 3351 3352
          ERR_RETURN(dict->getNdbError());
        DBUG_PRINT("info", ("Table schema version: %d", 
                            tab->getObjectVersion()));
      }
3353
      if (m_table_version < tab->getObjectVersion())
3354 3355 3356 3357 3358 3359 3360
      {
        /*
          The table has been altered, caller has to retry
        */
        NdbError err= ndb->getNdbError(NDB_INVALID_SCHEMA_OBJECT);
        DBUG_RETURN(ndb_to_mysql_error(&err));
      }
3361 3362 3363 3364 3365 3366
      if (m_table != (void *)tab)
      {
        m_table= (void *)tab;
        m_table_version = tab->getObjectVersion();
        if (!(my_errno= build_index_list(ndb, table, ILBP_OPEN)))
          DBUG_RETURN(my_errno);
3367
      }
3368 3369
      m_table_info= tab_info;
    }
3370
    no_uncommitted_rows_init(thd);
3371 3372
  }
  else
unknown's avatar
unknown committed
3373
  {
3374
    DBUG_PRINT("info", ("lock_type == F_UNLCK"));
3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392

    if (ndb_cache_check_time && m_rows_changed)
    {
      DBUG_PRINT("info", ("Rows has changed and util thread is running"));
      if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
      {
        DBUG_PRINT("info", ("Add share to list of tables to be invalidated"));
        /* NOTE push_back allocates memory using transactions mem_root! */
        thd_ndb->changed_tables.push_back(m_share, &thd->transaction.mem_root);
      }

      pthread_mutex_lock(&m_share->mutex);
      DBUG_PRINT("info", ("Invalidating commit_count"));
      m_share->commit_count= 0;
      m_share->commit_count_lock++;
      pthread_mutex_unlock(&m_share->mutex);
    }

3393
    if (!--thd_ndb->lock_count)
unknown's avatar
unknown committed
3394 3395 3396 3397
    {
      DBUG_PRINT("trans", ("Last external_lock"));
      PRINT_OPTION_FLAGS(thd);

3398
      if (thd_ndb->stmt)
unknown's avatar
unknown committed
3399 3400 3401 3402 3403 3404 3405
      {
        /*
          Unlock is done without a transaction commit / rollback.
          This happens if the thread didn't update any rows
          We must in this case close the transaction to release resources
        */
        DBUG_PRINT("trans",("ending non-updating transaction"));
3406
        ndb->closeTransaction(m_active_trans);
3407
        thd_ndb->stmt= NULL;
unknown's avatar
unknown committed
3408 3409
      }
    }
unknown's avatar
unknown committed
3410
    m_table_info= NULL;
3411

3412 3413 3414 3415 3416 3417 3418 3419 3420
    /*
      This is the place to make sure this handler instance
      no longer are connected to the active transaction.

      And since the handler is no longer part of the transaction 
      it can't have open cursors, ops or blobs pending.
    */
    m_active_trans= NULL;    

3421 3422
    if (m_active_cursor)
      DBUG_PRINT("warning", ("m_active_cursor != NULL"));
3423 3424
    m_active_cursor= NULL;

unknown's avatar
unknown committed
3425 3426 3427 3428
    if (m_multi_cursor)
      DBUG_PRINT("warning", ("m_multi_cursor != NULL"));
    m_multi_cursor= NULL;
    
3429
    if (m_blobs_pending)
3430
      DBUG_PRINT("warning", ("blobs_pending != 0"));
3431
    m_blobs_pending= 0;
3432
    
3433
    if (m_ops_pending)
3434
      DBUG_PRINT("warning", ("ops_pending != 0L"));
3435
    m_ops_pending= 0;
unknown's avatar
unknown committed
3436 3437 3438 3439 3440
  }
  DBUG_RETURN(error);
}

/*
3441 3442 3443 3444 3445
  Start a transaction for running a statement if one is not
  already running in a transaction. This will be the case in
  a BEGIN; COMMIT; block
  When using LOCK TABLE's external_lock will start a transaction
  since ndb does not currently does not support table locking
unknown's avatar
unknown committed
3446 3447
*/

unknown's avatar
unknown committed
3448
int ha_ndbcluster::start_stmt(THD *thd, thr_lock_type lock_type)
unknown's avatar
unknown committed
3449 3450 3451 3452 3453
{
  int error=0;
  DBUG_ENTER("start_stmt");
  PRINT_OPTION_FLAGS(thd);

3454
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
3455
  NdbTransaction *trans= (thd_ndb->stmt)?thd_ndb->stmt:thd_ndb->all;
unknown's avatar
unknown committed
3456
  if (!trans){
3457
    Ndb *ndb= thd_ndb->ndb;
unknown's avatar
unknown committed
3458
    DBUG_PRINT("trans",("Starting transaction stmt"));  
3459
    trans= ndb->startTransaction();
unknown's avatar
unknown committed
3460
    if (trans == NULL)
3461
      ERR_RETURN(ndb->getNdbError());
3462
    no_uncommitted_rows_reset(thd);
3463 3464
    thd_ndb->stmt= trans;
    trans_register_ha(thd, FALSE, &ndbcluster_hton);
unknown's avatar
unknown committed
3465 3466
  }
  m_active_trans= trans;
unknown's avatar
unknown committed
3467

3468
  // Start of statement
3469
  m_retrieve_all_fields= FALSE;
3470
  m_retrieve_primary_key= FALSE;
3471
  m_ops_pending= 0;    
unknown's avatar
unknown committed
3472 3473 3474 3475 3476 3477
  
  DBUG_RETURN(error);
}


/*
3478
  Commit a transaction started in NDB
unknown's avatar
unknown committed
3479 3480
 */

3481
int ndbcluster_commit(THD *thd, bool all)
unknown's avatar
unknown committed
3482 3483
{
  int res= 0;
3484 3485 3486
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  Ndb *ndb= thd_ndb->ndb;
  NdbTransaction *trans= all ? thd_ndb->all : thd_ndb->stmt;
unknown's avatar
unknown committed
3487 3488 3489

  DBUG_ENTER("ndbcluster_commit");
  DBUG_PRINT("transaction",("%s",
3490
                            trans == thd_ndb->stmt ?
unknown's avatar
unknown committed
3491 3492 3493
                            "stmt" : "all"));
  DBUG_ASSERT(ndb && trans);

3494
  if (execute_commit(thd,trans) != 0)
unknown's avatar
unknown committed
3495 3496
  {
    const NdbError err= trans->getNdbError();
3497
    const NdbOperation *error_op= trans->getNdbErrorOperation();
3498
    ERR_PRINT(err);
unknown's avatar
unknown committed
3499
    res= ndb_to_mysql_error(&err);
3500
    if (res != -1)
3501
      ndbcluster_print_error(res, error_op);
unknown's avatar
unknown committed
3502
  }
3503
  ndb->closeTransaction(trans);
3504

unknown's avatar
unknown committed
3505
  if (all)
3506 3507 3508
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;
3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522

  /* Clear commit_count for tables changed by transaction */
  NDB_SHARE* share;
  List_iterator_fast<NDB_SHARE> it(thd_ndb->changed_tables);
  while ((share= it++))
  {
    pthread_mutex_lock(&share->mutex);
    DBUG_PRINT("info", ("Invalidate commit_count for %s, share->commit_count: %d ", share->table_name, share->commit_count));
    share->commit_count= 0;
    share->commit_count_lock++;
    pthread_mutex_unlock(&share->mutex);
  }
  thd_ndb->changed_tables.empty();

unknown's avatar
unknown committed
3523 3524 3525 3526 3527 3528 3529 3530
  DBUG_RETURN(res);
}


/*
  Rollback a transaction started in NDB
 */

3531
int ndbcluster_rollback(THD *thd, bool all)
unknown's avatar
unknown committed
3532 3533
{
  int res= 0;
3534 3535 3536
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  Ndb *ndb= thd_ndb->ndb;
  NdbTransaction *trans= all ? thd_ndb->all : thd_ndb->stmt;
unknown's avatar
unknown committed
3537 3538 3539

  DBUG_ENTER("ndbcluster_rollback");
  DBUG_PRINT("transaction",("%s",
3540
                            trans == thd_ndb->stmt ? 
unknown's avatar
unknown committed
3541 3542 3543
                            "stmt" : "all"));
  DBUG_ASSERT(ndb && trans);

3544
  if (trans->execute(NdbTransaction::Rollback) != 0)
unknown's avatar
unknown committed
3545 3546
  {
    const NdbError err= trans->getNdbError();
3547
    const NdbOperation *error_op= trans->getNdbErrorOperation();
unknown's avatar
unknown committed
3548 3549
    ERR_PRINT(err);     
    res= ndb_to_mysql_error(&err);
3550 3551
    if (res != -1) 
      ndbcluster_print_error(res, error_op);
unknown's avatar
unknown committed
3552 3553
  }
  ndb->closeTransaction(trans);
3554

unknown's avatar
unknown committed
3555
  if (all)
3556 3557 3558 3559
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;

3560 3561 3562
  /* Clear list of tables changed by transaction */
  thd_ndb->changed_tables.empty();

3563
  DBUG_RETURN(res);
unknown's avatar
unknown committed
3564 3565 3566 3567
}


/*
unknown's avatar
unknown committed
3568 3569 3570
  Define NDB column based on Field.
  Returns 0 or mysql error code.
  Not member of ha_ndbcluster because NDBCOL cannot be declared.
unknown's avatar
unknown committed
3571 3572 3573

  MySQL text types with character set "binary" are mapped to true
  NDB binary types without a character set.  This may change.
unknown's avatar
unknown committed
3574 3575
 */

unknown's avatar
unknown committed
3576 3577 3578
static int create_ndb_column(NDBCOL &col,
                             Field *field,
                             HA_CREATE_INFO *info)
unknown's avatar
unknown committed
3579
{
unknown's avatar
unknown committed
3580
  // Set name
unknown's avatar
unknown committed
3581
  col.setName(field->field_name);
unknown's avatar
unknown committed
3582 3583
  // Get char set
  CHARSET_INFO *cs= field->charset();
unknown's avatar
unknown committed
3584 3585 3586 3587
  // Set type and sizes
  const enum enum_field_types mysql_type= field->real_type();
  switch (mysql_type) {
  // Numeric types
unknown's avatar
unknown committed
3588
  case MYSQL_TYPE_TINY:        
unknown's avatar
unknown committed
3589 3590 3591 3592 3593 3594
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Tinyunsigned);
    else
      col.setType(NDBCOL::Tinyint);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
3595
  case MYSQL_TYPE_SHORT:
unknown's avatar
unknown committed
3596 3597 3598 3599 3600 3601
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Smallunsigned);
    else
      col.setType(NDBCOL::Smallint);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
3602
  case MYSQL_TYPE_LONG:
unknown's avatar
unknown committed
3603 3604 3605 3606 3607 3608
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Unsigned);
    else
      col.setType(NDBCOL::Int);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
3609
  case MYSQL_TYPE_INT24:       
unknown's avatar
unknown committed
3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Mediumunsigned);
    else
      col.setType(NDBCOL::Mediumint);
    col.setLength(1);
    break;
  case MYSQL_TYPE_LONGLONG:
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Bigunsigned);
    else
      col.setType(NDBCOL::Bigint);
    col.setLength(1);
unknown's avatar
unknown committed
3622 3623
    break;
  case MYSQL_TYPE_FLOAT:
unknown's avatar
unknown committed
3624 3625 3626
    col.setType(NDBCOL::Float);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
3627
  case MYSQL_TYPE_DOUBLE:
unknown's avatar
unknown committed
3628 3629 3630
    col.setType(NDBCOL::Double);
    col.setLength(1);
    break;
3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650
  case MYSQL_TYPE_DECIMAL:    
    {
      Field_decimal *f= (Field_decimal*)field;
      uint precision= f->pack_length();
      uint scale= f->decimals();
      if (field->flags & UNSIGNED_FLAG)
      {
        col.setType(NDBCOL::Olddecimalunsigned);
        precision-= (scale > 0);
      }
      else
      {
        col.setType(NDBCOL::Olddecimal);
        precision-= 1 + (scale > 0);
      }
      col.setPrecision(precision);
      col.setScale(scale);
      col.setLength(1);
    }
    break;
3651 3652 3653
  case MYSQL_TYPE_NEWDECIMAL:    
    {
      Field_new_decimal *f= (Field_new_decimal*)field;
unknown's avatar
unknown committed
3654
      uint precision= f->precision;
3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668
      uint scale= f->decimals();
      if (field->flags & UNSIGNED_FLAG)
      {
        col.setType(NDBCOL::Decimalunsigned);
      }
      else
      {
        col.setType(NDBCOL::Decimal);
      }
      col.setPrecision(precision);
      col.setScale(scale);
      col.setLength(1);
    }
    break;
unknown's avatar
unknown committed
3669 3670 3671 3672 3673
  // Date types
  case MYSQL_TYPE_DATETIME:    
    col.setType(NDBCOL::Datetime);
    col.setLength(1);
    break;
3674 3675 3676 3677
  case MYSQL_TYPE_DATE: // ?
    col.setType(NDBCOL::Char);
    col.setLength(field->pack_length());
    break;
unknown's avatar
unknown committed
3678
  case MYSQL_TYPE_NEWDATE:
unknown's avatar
unknown committed
3679 3680 3681
    col.setType(NDBCOL::Date);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
3682
  case MYSQL_TYPE_TIME:        
unknown's avatar
unknown committed
3683 3684 3685
    col.setType(NDBCOL::Time);
    col.setLength(1);
    break;
3686 3687 3688 3689 3690 3691 3692
  case MYSQL_TYPE_YEAR:
    col.setType(NDBCOL::Year);
    col.setLength(1);
    break;
  case MYSQL_TYPE_TIMESTAMP:
    col.setType(NDBCOL::Timestamp);
    col.setLength(1);
unknown's avatar
unknown committed
3693 3694 3695
    break;
  // Char types
  case MYSQL_TYPE_STRING:      
3696
    if (field->pack_length() == 0)
3697 3698 3699 3700
    {
      col.setType(NDBCOL::Bit);
      col.setLength(1);
    }
unknown's avatar
unknown committed
3701
    else if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
3702
    {
unknown's avatar
unknown committed
3703
      col.setType(NDBCOL::Binary);
unknown's avatar
unknown committed
3704
      col.setLength(field->pack_length());
unknown's avatar
unknown committed
3705
    }
3706
    else
unknown's avatar
unknown committed
3707 3708 3709
    {
      col.setType(NDBCOL::Char);
      col.setCharset(cs);
3710
      col.setLength(field->pack_length());
unknown's avatar
unknown committed
3711
    }
unknown's avatar
unknown committed
3712
    break;
unknown's avatar
unknown committed
3713 3714 3715 3716 3717 3718
  case MYSQL_TYPE_VAR_STRING: // ?
  case MYSQL_TYPE_VARCHAR:
    {
      Field_varstring* f= (Field_varstring*)field;
      if (f->length_bytes == 1)
      {
unknown's avatar
unknown committed
3719
        if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
3720 3721 3722 3723 3724 3725 3726 3727
          col.setType(NDBCOL::Varbinary);
        else {
          col.setType(NDBCOL::Varchar);
          col.setCharset(cs);
        }
      }
      else if (f->length_bytes == 2)
      {
unknown's avatar
unknown committed
3728
        if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739
          col.setType(NDBCOL::Longvarbinary);
        else {
          col.setType(NDBCOL::Longvarchar);
          col.setCharset(cs);
        }
      }
      else
      {
        return HA_ERR_UNSUPPORTED;
      }
      col.setLength(field->field_length);
unknown's avatar
unknown committed
3740
    }
unknown's avatar
unknown committed
3741 3742 3743 3744
    break;
  // Blob types (all come in as MYSQL_TYPE_BLOB)
  mysql_type_tiny_blob:
  case MYSQL_TYPE_TINY_BLOB:
unknown's avatar
unknown committed
3745
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
3746
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
3747
    else {
unknown's avatar
unknown committed
3748
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
3749 3750
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
3751 3752 3753 3754 3755
    col.setInlineSize(256);
    // No parts
    col.setPartSize(0);
    col.setStripeSize(0);
    break;
3756
  //mysql_type_blob:
3757
  case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
3758
  case MYSQL_TYPE_BLOB:    
unknown's avatar
unknown committed
3759
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
3760
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
3761
    else {
unknown's avatar
unknown committed
3762
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
3763 3764
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780
    // Use "<=" even if "<" is the exact condition
    if (field->max_length() <= (1 << 8))
      goto mysql_type_tiny_blob;
    else if (field->max_length() <= (1 << 16))
    {
      col.setInlineSize(256);
      col.setPartSize(2000);
      col.setStripeSize(16);
    }
    else if (field->max_length() <= (1 << 24))
      goto mysql_type_medium_blob;
    else
      goto mysql_type_long_blob;
    break;
  mysql_type_medium_blob:
  case MYSQL_TYPE_MEDIUM_BLOB:   
unknown's avatar
unknown committed
3781
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
3782
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
3783
    else {
unknown's avatar
unknown committed
3784
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
3785 3786
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
3787 3788 3789 3790 3791 3792
    col.setInlineSize(256);
    col.setPartSize(4000);
    col.setStripeSize(8);
    break;
  mysql_type_long_blob:
  case MYSQL_TYPE_LONG_BLOB:  
unknown's avatar
unknown committed
3793
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
3794
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
3795
    else {
unknown's avatar
unknown committed
3796
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
3797 3798
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811
    col.setInlineSize(256);
    col.setPartSize(8000);
    col.setStripeSize(4);
    break;
  // Other types
  case MYSQL_TYPE_ENUM:
    col.setType(NDBCOL::Char);
    col.setLength(field->pack_length());
    break;
  case MYSQL_TYPE_SET:         
    col.setType(NDBCOL::Char);
    col.setLength(field->pack_length());
    break;
unknown's avatar
unknown committed
3812 3813
  case MYSQL_TYPE_BIT:
  {
3814 3815 3816 3817 3818 3819 3820 3821
    int no_of_bits= field->field_length*8 + ((Field_bit *) field)->bit_len;
    col.setType(NDBCOL::Bit);
    if (!no_of_bits)
      col.setLength(1);
      else
        col.setLength(no_of_bits);
    break;
  }
unknown's avatar
unknown committed
3822 3823 3824 3825 3826
  case MYSQL_TYPE_NULL:        
    goto mysql_type_unsupported;
  mysql_type_unsupported:
  default:
    return HA_ERR_UNSUPPORTED;
unknown's avatar
unknown committed
3827
  }
unknown's avatar
unknown committed
3828 3829 3830 3831 3832 3833 3834 3835
  // Set nullable and pk
  col.setNullable(field->maybe_null());
  col.setPrimaryKey(field->flags & PRI_KEY_FLAG);
  // Set autoincrement
  if (field->flags & AUTO_INCREMENT_FLAG) 
  {
    col.setAutoIncrement(TRUE);
    ulonglong value= info->auto_increment_value ?
3836
      info->auto_increment_value : (ulonglong) 1;
unknown's avatar
unknown committed
3837 3838
    DBUG_PRINT("info", ("Autoincrement key, initial: %llu", value));
    col.setAutoIncrementInitialValue(value);
unknown's avatar
unknown committed
3839
  }
unknown's avatar
unknown committed
3840
  else
unknown's avatar
unknown committed
3841
    col.setAutoIncrement(FALSE);
unknown's avatar
unknown committed
3842
  return 0;
unknown's avatar
unknown committed
3843 3844 3845 3846 3847 3848
}

/*
  Create a table in NDB Cluster
 */

unknown's avatar
Merge  
unknown committed
3849 3850
static void ndb_set_fragmentation(NDBTAB &tab, TABLE *form, uint pk_length)
{
unknown's avatar
Merge  
unknown committed
3851
  if (form->s->max_rows == (ha_rows) 0) /* default setting, don't set fragmentation */
unknown's avatar
Merge  
unknown committed
3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873
    return;
  /**
   * get the number of fragments right
   */
  uint no_fragments;
  {
#if MYSQL_VERSION_ID >= 50000
    uint acc_row_size= 25 + /*safety margin*/ 2;
#else
    uint acc_row_size= pk_length*4;
    /* add acc overhead */
    if (pk_length <= 8)  /* main page will set the limit */
      acc_row_size+= 25 + /*safety margin*/ 2;
    else                /* overflow page will set the limit */
      acc_row_size+= 4 + /*safety margin*/ 4;
#endif
    ulonglong acc_fragment_size= 512*1024*1024;
    ulonglong max_rows= form->s->max_rows;
#if MYSQL_VERSION_ID >= 50100
    no_fragments= (max_rows*acc_row_size)/acc_fragment_size+1;
#else
    no_fragments= ((max_rows*acc_row_size)/acc_fragment_size+1
3874
                   +1/*correct rounding*/)/2;
unknown's avatar
Merge  
unknown committed
3875 3876 3877 3878 3879 3880 3881 3882 3883
#endif
  }
  {
    uint no_nodes= g_ndb_cluster_connection->no_db_nodes();
    NDBTAB::FragmentType ftype;
    if (no_fragments > 2*no_nodes)
    {
      ftype= NDBTAB::FragAllLarge;
      if (no_fragments > 4*no_nodes)
3884 3885
        push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
                     "Ndb might have problems storing the max amount of rows specified");
unknown's avatar
Merge  
unknown committed
3886 3887 3888 3889 3890 3891 3892 3893 3894
    }
    else if (no_fragments > no_nodes)
      ftype= NDBTAB::FragAllMedium;
    else
      ftype= NDBTAB::FragAllSmall;
    tab.setFragmentType(ftype);
  }
}

unknown's avatar
unknown committed
3895
int ha_ndbcluster::create(const char *name, 
3896 3897
                          TABLE *form, 
                          HA_CREATE_INFO *info)
unknown's avatar
unknown committed
3898 3899 3900
{
  NDBTAB tab;
  NDBCOL col;
unknown's avatar
unknown committed
3901
  uint pack_length, length, i, pk_length= 0;
unknown's avatar
unknown committed
3902 3903
  const void *data, *pack_data;
  char name2[FN_HEADLEN];
3904
  bool create_from_engine= (info->table_options & HA_OPTION_CREATE_FROM_ENGINE);
3905

unknown's avatar
unknown committed
3906
  DBUG_ENTER("ha_ndbcluster::create");
unknown's avatar
unknown committed
3907 3908 3909
  DBUG_PRINT("enter", ("name: %s", name));
  fn_format(name2, name, "", "",2);       // Remove the .frm extension
  set_dbname(name2);
3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921
  set_tabname(name2);    

  if (create_from_engine)
  {
    /*
      Table alreay exists in NDB and frm file has been created by 
      caller.
      Do Ndb specific stuff, such as create a .ndb file
    */
    my_errno= write_ndb_file();
    DBUG_RETURN(my_errno);
  }
unknown's avatar
unknown committed
3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937

  DBUG_PRINT("table", ("name: %s", m_tabname));  
  tab.setName(m_tabname);
  tab.setLogging(!(info->options & HA_LEX_CREATE_TMP_TABLE));    
   
  // Save frm data for this table
  if (readfrm(name, &data, &length))
    DBUG_RETURN(1);
  if (packfrm(data, length, &pack_data, &pack_length))
    DBUG_RETURN(2);
  
  DBUG_PRINT("info", ("setFrm data=%x, len=%d", pack_data, pack_length));
  tab.setFrm(pack_data, pack_length);      
  my_free((char*)data, MYF(0));
  my_free((char*)pack_data, MYF(0));
  
3938
  for (i= 0; i < form->s->fields; i++) 
unknown's avatar
unknown committed
3939 3940 3941 3942
  {
    Field *field= form->field[i];
    DBUG_PRINT("info", ("name: %s, type: %u, pack_length: %d", 
                        field->field_name, field->real_type(),
3943
                        field->pack_length()));
3944
    if ((my_errno= create_ndb_column(col, field, info)))
unknown's avatar
unknown committed
3945
      DBUG_RETURN(my_errno);
unknown's avatar
unknown committed
3946
    tab.addColumn(col);
unknown's avatar
unknown committed
3947
    if (col.getPrimaryKey())
unknown's avatar
unknown committed
3948
      pk_length += (field->pack_length() + 3) / 4;
unknown's avatar
unknown committed
3949 3950 3951
  }
  
  // No primary key, create shadow key as 64 bit, auto increment  
3952
  if (form->s->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
3953 3954 3955 3956 3957
  {
    DBUG_PRINT("info", ("Generating shadow key"));
    col.setName("$PK");
    col.setType(NdbDictionary::Column::Bigunsigned);
    col.setLength(1);
unknown's avatar
unknown committed
3958
    col.setNullable(FALSE);
unknown's avatar
unknown committed
3959 3960 3961
    col.setPrimaryKey(TRUE);
    col.setAutoIncrement(TRUE);
    tab.addColumn(col);
unknown's avatar
unknown committed
3962 3963 3964 3965
    pk_length += 2;
  }
  
  // Make sure that blob tables don't have to big part size
3966
  for (i= 0; i < form->s->fields; i++) 
unknown's avatar
unknown committed
3967 3968 3969 3970 3971 3972 3973
  {
    /**
     * The extra +7 concists
     * 2 - words from pk in blob table
     * 5 - from extra words added by tup/dict??
     */
    switch (form->field[i]->real_type()) {
3974
    case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
3975 3976 3977 3978
    case MYSQL_TYPE_BLOB:    
    case MYSQL_TYPE_MEDIUM_BLOB:   
    case MYSQL_TYPE_LONG_BLOB: 
    {
3979 3980
      NdbDictionary::Column * col= tab.getColumn(i);
      int size= pk_length + (col->getPartSize()+3)/4 + 7;
unknown's avatar
unknown committed
3981
      if (size > NDB_MAX_TUPLE_SIZE_IN_WORDS && 
3982
         (pk_length+7) < NDB_MAX_TUPLE_SIZE_IN_WORDS)
unknown's avatar
unknown committed
3983
      {
3984 3985
        size= NDB_MAX_TUPLE_SIZE_IN_WORDS - pk_length - 7;
        col->setPartSize(4*size);
unknown's avatar
unknown committed
3986 3987 3988 3989 3990 3991 3992 3993 3994 3995
      }
      /**
       * If size > NDB_MAX and pk_length+7 >= NDB_MAX
       *   then the table can't be created anyway, so skip
       *   changing part size, and have error later
       */ 
    }
    default:
      break;
    }
unknown's avatar
unknown committed
3996
  }
unknown's avatar
Merge  
unknown committed
3997 3998 3999

  ndb_set_fragmentation(tab, form, pk_length);

4000
  if ((my_errno= check_ndb_connection()))
unknown's avatar
unknown committed
4001 4002 4003
    DBUG_RETURN(my_errno);
  
  // Create the table in NDB     
4004 4005
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
4006
  if (dict->createTable(tab) != 0) 
unknown's avatar
unknown committed
4007 4008 4009 4010 4011 4012 4013 4014
  {
    const NdbError err= dict->getNdbError();
    ERR_PRINT(err);
    my_errno= ndb_to_mysql_error(&err);
    DBUG_RETURN(my_errno);
  }
  DBUG_PRINT("info", ("Table %s/%s created successfully", 
                      m_dbname, m_tabname));
4015

unknown's avatar
unknown committed
4016
  // Create secondary indexes
4017
  my_errno= build_index_list(ndb, form, ILBP_CREATE);
4018

4019 4020 4021
  if (!my_errno)
    my_errno= write_ndb_file();

unknown's avatar
unknown committed
4022 4023 4024 4025
  DBUG_RETURN(my_errno);
}


4026
int ha_ndbcluster::create_ordered_index(const char *name, 
4027
                                        KEY *key_info)
4028
{
4029
  DBUG_ENTER("ha_ndbcluster::create_ordered_index");
unknown's avatar
unknown committed
4030
  DBUG_RETURN(create_index(name, key_info, FALSE));
4031 4032 4033
}

int ha_ndbcluster::create_unique_index(const char *name, 
4034
                                       KEY *key_info)
4035 4036
{

4037
  DBUG_ENTER("ha_ndbcluster::create_unique_index");
unknown's avatar
unknown committed
4038
  DBUG_RETURN(create_index(name, key_info, TRUE));
4039 4040 4041
}


unknown's avatar
unknown committed
4042 4043 4044 4045 4046
/*
  Create an index in NDB Cluster
 */

int ha_ndbcluster::create_index(const char *name, 
4047 4048
                                KEY *key_info,
                                bool unique)
4049
{
4050 4051
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
unknown's avatar
unknown committed
4052 4053 4054
  KEY_PART_INFO *key_part= key_info->key_part;
  KEY_PART_INFO *end= key_part + key_info->key_parts;
  
4055
  DBUG_ENTER("ha_ndbcluster::create_index");
unknown's avatar
unknown committed
4056
  DBUG_PRINT("enter", ("name: %s ", name));
4057

unknown's avatar
unknown committed
4058
  NdbDictionary::Index ndb_index(name);
4059
  if (unique)
unknown's avatar
unknown committed
4060 4061 4062 4063 4064
    ndb_index.setType(NdbDictionary::Index::UniqueHashIndex);
  else 
  {
    ndb_index.setType(NdbDictionary::Index::OrderedIndex);
    // TODO Only temporary ordered indexes supported
unknown's avatar
unknown committed
4065
    ndb_index.setLogging(FALSE); 
unknown's avatar
unknown committed
4066 4067 4068 4069 4070 4071 4072
  }
  ndb_index.setTable(m_tabname);

  for (; key_part != end; key_part++) 
  {
    Field *field= key_part->field;
    DBUG_PRINT("info", ("attr: %s", field->field_name));
unknown's avatar
unknown committed
4073
    ndb_index.addColumnName(field->field_name);
unknown's avatar
unknown committed
4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090
  }
  
  if (dict->createIndex(ndb_index))
    ERR_RETURN(dict->getNdbError());

  // Success
  DBUG_PRINT("info", ("Created index %s", name));
  DBUG_RETURN(0);  
}


/*
  Rename a table in NDB Cluster
*/

int ha_ndbcluster::rename_table(const char *from, const char *to)
{
4091
  NDBDICT *dict;
unknown's avatar
unknown committed
4092
  char new_tabname[FN_HEADLEN];
4093 4094
  const NDBTAB *orig_tab;
  int result;
unknown's avatar
unknown committed
4095 4096

  DBUG_ENTER("ha_ndbcluster::rename_table");
4097
  DBUG_PRINT("info", ("Renaming %s to %s", from, to));
unknown's avatar
unknown committed
4098 4099 4100 4101
  set_dbname(from);
  set_tabname(from);
  set_tabname(to, new_tabname);

4102 4103 4104
  if (check_ndb_connection())
    DBUG_RETURN(my_errno= HA_ERR_NO_CONNECTION);

unknown's avatar
unknown committed
4105 4106
  Ndb *ndb= get_ndb();
  dict= ndb->getDictionary();
4107 4108
  if (!(orig_tab= dict->getTable(m_tabname)))
    ERR_RETURN(dict->getNdbError());
4109 4110 4111 4112 4113 4114 4115
  // Check if thread has stale local cache
  if (orig_tab->getObjectStatus() == NdbDictionary::Object::Invalid)
  {
    dict->removeCachedTable(m_tabname);
    if (!(orig_tab= dict->getTable(m_tabname)))
      ERR_RETURN(dict->getNdbError());
  }
4116 4117 4118
  m_table= (void *)orig_tab;
  // Change current database to that of target table
  set_dbname(to);
unknown's avatar
unknown committed
4119
  ndb->setDatabaseName(m_dbname);
4120
  if (!(result= alter_table_name(new_tabname)))
4121
  {
4122 4123
    // Rename .ndb file
    result= handler::rename_table(from, to);
4124
  }
4125

unknown's avatar
unknown committed
4126 4127 4128 4129 4130 4131 4132 4133
  DBUG_RETURN(result);
}


/*
  Rename a table in NDB Cluster using alter table
 */

4134
int ha_ndbcluster::alter_table_name(const char *to)
unknown's avatar
unknown committed
4135
{
4136 4137
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
4138
  const NDBTAB *orig_tab= (const NDBTAB *) m_table;
unknown's avatar
unknown committed
4139 4140
  DBUG_ENTER("alter_table_name_table");

unknown's avatar
unknown committed
4141
  NdbDictionary::Table new_tab= *orig_tab;
4142 4143
  new_tab.setName(to);
  if (dict->alterTable(new_tab) != 0)
unknown's avatar
unknown committed
4144 4145 4146
    ERR_RETURN(dict->getNdbError());

  m_table= NULL;
unknown's avatar
unknown committed
4147
  m_table_info= NULL;
unknown's avatar
unknown committed
4148 4149 4150 4151 4152 4153
                                                                             
  DBUG_RETURN(0);
}


/*
4154 4155
  Delete table from NDB Cluster

unknown's avatar
unknown committed
4156 4157 4158 4159
 */

int ha_ndbcluster::delete_table(const char *name)
{
4160
  DBUG_ENTER("ha_ndbcluster::delete_table");
unknown's avatar
unknown committed
4161 4162 4163
  DBUG_PRINT("enter", ("name: %s", name));
  set_dbname(name);
  set_tabname(name);
4164

unknown's avatar
unknown committed
4165 4166
  if (check_ndb_connection())
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4167 4168

  /* Call ancestor function to delete .ndb file */
4169
  handler::delete_table(name);
4170 4171
  
  /* Drop the table from NDB */
unknown's avatar
unknown committed
4172 4173 4174 4175 4176
  DBUG_RETURN(drop_table());
}


/*
4177
  Drop table in NDB Cluster
unknown's avatar
unknown committed
4178 4179 4180 4181
 */

int ha_ndbcluster::drop_table()
{
4182 4183
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
4184

unknown's avatar
unknown committed
4185 4186
  DBUG_ENTER("drop_table");
  DBUG_PRINT("enter", ("Deleting %s", m_tabname));
4187

unknown's avatar
unknown committed
4188
  release_metadata();
4189 4190
  if (dict->dropTable(m_tabname))
    ERR_RETURN(dict->getNdbError());
unknown's avatar
unknown committed
4191 4192 4193 4194
  DBUG_RETURN(0);
}


4195
ulonglong ha_ndbcluster::get_auto_increment()
4196
{  
4197 4198
  int cache_size;
  Uint64 auto_value;
unknown's avatar
unknown committed
4199 4200
  DBUG_ENTER("get_auto_increment");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));
4201
  Ndb *ndb= get_ndb();
4202
   
4203
  if (m_rows_inserted > m_rows_to_insert)
unknown's avatar
unknown committed
4204
  {
4205 4206
    /* We guessed too low */
    m_rows_to_insert+= m_autoincrement_prefetch;
unknown's avatar
unknown committed
4207
  }
unknown's avatar
merged  
unknown committed
4208
  cache_size= 
unknown's avatar
unknown committed
4209 4210 4211 4212
    (int) ((m_rows_to_insert - m_rows_inserted < m_autoincrement_prefetch) ?
           m_rows_to_insert - m_rows_inserted :
           ((m_rows_to_insert > m_autoincrement_prefetch) ?
            m_rows_to_insert : m_autoincrement_prefetch));
unknown's avatar
unknown committed
4213
  auto_value= NDB_FAILED_AUTO_INCREMENT;
4214 4215 4216 4217 4218 4219 4220 4221 4222
  uint retries= NDB_AUTO_INCREMENT_RETRIES;
  do {
    auto_value=
      (m_skip_auto_increment) ? 
      ndb->readAutoIncrementValue((const NDBTAB *) m_table)
      : ndb->getAutoIncrementValue((const NDBTAB *) m_table, cache_size);
  } while (auto_value == NDB_FAILED_AUTO_INCREMENT && 
           --retries &&
           ndb->getNdbError().status == NdbError::TemporaryError);
4223
  if (auto_value == NDB_FAILED_AUTO_INCREMENT)
4224 4225 4226 4227 4228 4229
  {
    const NdbError err= ndb->getNdbError();
    sql_print_error("Error %lu in ::get_auto_increment(): %s",
                    (ulong) err.code, err.message);
    DBUG_RETURN(~(ulonglong) 0);
  }
unknown's avatar
unknown committed
4230
  DBUG_RETURN((longlong)auto_value);
unknown's avatar
unknown committed
4231 4232 4233 4234 4235 4236 4237 4238
}


/*
  Constructor for the NDB Cluster table handler 
 */

ha_ndbcluster::ha_ndbcluster(TABLE *table_arg):
4239
  handler(&ndbcluster_hton, table_arg),
unknown's avatar
unknown committed
4240 4241 4242
  m_active_trans(NULL),
  m_active_cursor(NULL),
  m_table(NULL),
4243
  m_table_version(-1),
4244
  m_table_info(NULL),
unknown's avatar
unknown committed
4245
  m_table_flags(HA_REC_NOT_IN_SEQ |
4246 4247 4248 4249
                HA_NULL_IN_KEY |
                HA_AUTO_PART_KEY |
                HA_NO_PREFIX_CHAR_KEYS |
                HA_NEED_READ_RANGE_BUFFER |
4250
                HA_CAN_GEOMETRY |
4251
                HA_CAN_BIT_FIELD),
4252
  m_share(0),
unknown's avatar
unknown committed
4253
  m_use_write(FALSE),
4254
  m_ignore_dup_key(FALSE),
4255 4256
  m_primary_key_update(FALSE),
  m_retrieve_all_fields(FALSE),
4257
  m_retrieve_primary_key(FALSE),
4258 4259 4260
  m_rows_to_insert((ha_rows) 1),
  m_rows_inserted((ha_rows) 0),
  m_bulk_insert_rows((ha_rows) 1024),
unknown's avatar
Merge  
unknown committed
4261
  m_rows_changed((ha_rows) 0),
4262 4263 4264 4265 4266 4267
  m_bulk_insert_not_flushed(FALSE),
  m_ops_pending(0),
  m_skip_auto_increment(TRUE),
  m_blobs_pending(0),
  m_blobs_buffer(0),
  m_blobs_buffer_size(0),
4268 4269 4270
  m_dupkey((uint) -1),
  m_ha_not_exact_count(FALSE),
  m_force_send(TRUE),
4271
  m_autoincrement_prefetch((ha_rows) 32),
unknown's avatar
unknown committed
4272
  m_transaction_on(TRUE),
unknown's avatar
unknown committed
4273 4274
  m_cond_stack(NULL),
  m_multi_cursor(NULL)
4275
{
4276
  int i;
4277
 
unknown's avatar
unknown committed
4278 4279 4280 4281 4282
  DBUG_ENTER("ha_ndbcluster");

  m_tabname[0]= '\0';
  m_dbname[0]= '\0';

4283
  records= ~(ha_rows)0; // uninitialized
unknown's avatar
unknown committed
4284 4285
  block_size= 1024;

4286 4287
  for (i= 0; i < MAX_KEY; i++)
  {
4288 4289 4290 4291
    m_index[i].type= UNDEFINED_INDEX;
    m_index[i].unique_index= NULL;
    m_index[i].index= NULL;
    m_index[i].unique_index_attrid_map= NULL;
4292 4293
  }

unknown's avatar
unknown committed
4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305
  DBUG_VOID_RETURN;
}


/*
  Destructor for NDB Cluster table handler
 */

ha_ndbcluster::~ha_ndbcluster() 
{
  DBUG_ENTER("~ha_ndbcluster");

4306 4307
  if (m_share)
    free_share(m_share);
unknown's avatar
unknown committed
4308
  release_metadata();
4309 4310
  my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
  m_blobs_buffer= 0;
unknown's avatar
unknown committed
4311 4312

  // Check for open cursor/transaction
4313 4314
  if (m_active_cursor) {
  }
unknown's avatar
unknown committed
4315
  DBUG_ASSERT(m_active_cursor == NULL);
4316 4317
  if (m_active_trans) {
  }
unknown's avatar
unknown committed
4318 4319
  DBUG_ASSERT(m_active_trans == NULL);

unknown's avatar
unknown committed
4320 4321 4322 4323
  // Discard the condition stack
  DBUG_PRINT("info", ("Clearing condition stack"));
  cond_clear();

unknown's avatar
unknown committed
4324 4325 4326 4327
  DBUG_VOID_RETURN;
}


unknown's avatar
Merge  
unknown committed
4328

unknown's avatar
unknown committed
4329 4330 4331 4332 4333 4334 4335 4336
/*
  Open a table for further use
  - fetch metadata for this table from NDB
  - check that table exists
*/

int ha_ndbcluster::open(const char *name, int mode, uint test_if_locked)
{
unknown's avatar
unknown committed
4337
  int res;
unknown's avatar
unknown committed
4338 4339 4340 4341 4342 4343 4344 4345
  KEY *key;
  DBUG_ENTER("open");
  DBUG_PRINT("enter", ("name: %s mode: %d test_if_locked: %d",
                       name, mode, test_if_locked));
  
  // Setup ref_length to make room for the whole 
  // primary key to be written in the ref variable
  
4346
  if (table->s->primary_key != MAX_KEY) 
unknown's avatar
unknown committed
4347
  {
4348
    key= table->key_info+table->s->primary_key;
unknown's avatar
unknown committed
4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359
    ref_length= key->key_length;
    DBUG_PRINT("info", (" ref_length: %d", ref_length));
  }
  // Init table lock structure 
  if (!(m_share=get_share(name)))
    DBUG_RETURN(1);
  thr_lock_data_init(&m_share->lock,&m_lock,(void*) 0);
  
  set_dbname(name);
  set_tabname(name);
  
4360 4361
  if (check_ndb_connection()) {
    free_share(m_share); m_share= 0;
unknown's avatar
unknown committed
4362
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4363
  }
4364
  
unknown's avatar
unknown committed
4365 4366 4367
  res= get_metadata(name);
  if (!res)
    info(HA_STATUS_VARIABLE | HA_STATUS_CONST);
unknown's avatar
unknown committed
4368

unknown's avatar
unknown committed
4369
  DBUG_RETURN(res);
unknown's avatar
unknown committed
4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380
}


/*
  Close the table
  - release resources setup by open()
 */

int ha_ndbcluster::close(void)
{
  DBUG_ENTER("close");  
4381
  free_share(m_share); m_share= 0;
unknown's avatar
unknown committed
4382 4383 4384 4385 4386
  release_metadata();
  DBUG_RETURN(0);
}


4387
Thd_ndb* ha_ndbcluster::seize_thd_ndb()
unknown's avatar
unknown committed
4388
{
4389 4390
  Thd_ndb *thd_ndb;
  DBUG_ENTER("seize_thd_ndb");
unknown's avatar
unknown committed
4391

4392
  thd_ndb= new Thd_ndb();
4393 4394 4395
  thd_ndb->ndb->getDictionary()->set_local_table_data_size(
    sizeof(Ndb_local_table_statistics)
    );
4396
  if (thd_ndb->ndb->init(max_transactions) != 0)
unknown's avatar
unknown committed
4397
  {
4398
    ERR_PRINT(thd_ndb->ndb->getNdbError());
unknown's avatar
unknown committed
4399 4400 4401 4402 4403 4404
    /*
      TODO 
      Alt.1 If init fails because to many allocated Ndb 
      wait on condition for a Ndb object to be released.
      Alt.2 Seize/release from pool, wait until next release 
    */
4405 4406
    delete thd_ndb;
    thd_ndb= NULL;
unknown's avatar
unknown committed
4407
  }
4408
  DBUG_RETURN(thd_ndb);
unknown's avatar
unknown committed
4409 4410 4411
}


4412
void ha_ndbcluster::release_thd_ndb(Thd_ndb* thd_ndb)
unknown's avatar
unknown committed
4413
{
4414 4415
  DBUG_ENTER("release_thd_ndb");
  delete thd_ndb;
unknown's avatar
unknown committed
4416 4417 4418 4419 4420
  DBUG_VOID_RETURN;
}


/*
unknown's avatar
unknown committed
4421
  If this thread already has a Thd_ndb object allocated
unknown's avatar
unknown committed
4422
  in current THD, reuse it. Otherwise
unknown's avatar
unknown committed
4423
  seize a Thd_ndb object, assign it to current THD and use it.
unknown's avatar
unknown committed
4424 4425 4426
 
*/

4427
Ndb* check_ndb_in_thd(THD* thd)
unknown's avatar
unknown committed
4428
{
4429
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
4430
  if (!thd_ndb)
unknown's avatar
unknown committed
4431
  {
unknown's avatar
unknown committed
4432
    if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
unknown's avatar
Merge  
unknown committed
4433
      return NULL;
4434
    set_thd_ndb(thd, thd_ndb);
unknown's avatar
unknown committed
4435
  }
unknown's avatar
Merge  
unknown committed
4436
  return thd_ndb->ndb;
4437 4438
}

unknown's avatar
unknown committed
4439

4440

4441
int ha_ndbcluster::check_ndb_connection(THD* thd)
unknown's avatar
unknown committed
4442
{
4443
  Ndb *ndb;
unknown's avatar
unknown committed
4444 4445
  DBUG_ENTER("check_ndb_connection");
  
4446
  if (!(ndb= check_ndb_in_thd(thd)))
4447
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4448
  ndb->setDatabaseName(m_dbname);
unknown's avatar
unknown committed
4449 4450 4451
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
4452

4453
int ndbcluster_close_connection(THD *thd)
unknown's avatar
unknown committed
4454
{
4455
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
unknown's avatar
unknown committed
4456
  DBUG_ENTER("ndbcluster_close_connection");
4457 4458
  if (thd_ndb)
  {
4459
    ha_ndbcluster::release_thd_ndb(thd_ndb);
4460
    set_thd_ndb(thd, NULL); // not strictly required but does not hurt either
4461
  }
4462
  DBUG_RETURN(0);
unknown's avatar
unknown committed
4463 4464 4465 4466 4467 4468 4469
}


/*
  Try to discover one table from NDB
 */

4470
int ndbcluster_discover(THD* thd, const char *db, const char *name,
4471
                        const void** frmblob, uint* frmlen)
unknown's avatar
unknown committed
4472 4473 4474 4475
{
  uint len;
  const void* data;
  const NDBTAB* tab;
4476
  Ndb* ndb;
unknown's avatar
unknown committed
4477
  DBUG_ENTER("ndbcluster_discover");
4478
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name)); 
unknown's avatar
unknown committed
4479

4480 4481 4482
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(HA_ERR_NO_CONNECTION);  
  ndb->setDatabaseName(db);
4483

4484
  NDBDICT* dict= ndb->getDictionary();
4485
  dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
4486 4487 4488 4489 4490
  dict->invalidateTable(name);
  if (!(tab= dict->getTable(name)))
  {    
    const NdbError err= dict->getNdbError();
    if (err.code == 709)
4491
      DBUG_RETURN(-1);
4492
    ERR_RETURN(err);
unknown's avatar
unknown committed
4493 4494 4495 4496 4497 4498
  }
  DBUG_PRINT("info", ("Found table %s", tab->getName()));
  
  len= tab->getFrmLength();  
  if (len == 0 || tab->getFrmData() == NULL)
  {
4499 4500
    DBUG_PRINT("error", ("No frm data found."));
    DBUG_RETURN(1);
unknown's avatar
unknown committed
4501 4502 4503
  }
  
  if (unpackfrm(&data, &len, tab->getFrmData()))
4504 4505 4506 4507
  {
    DBUG_PRINT("error", ("Could not unpack table"));
    DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
4508 4509 4510 4511 4512 4513 4514 4515

  *frmlen= len;
  *frmblob= data;
  
  DBUG_RETURN(0);
}

/*
4516
  Check if a table exists in NDB
4517

4518
 */
unknown's avatar
unknown committed
4519

4520
int ndbcluster_table_exists_in_engine(THD* thd, const char *db, const char *name)
4521 4522 4523
{
  const NDBTAB* tab;
  Ndb* ndb;
4524
  DBUG_ENTER("ndbcluster_table_exists_in_engine");
4525
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
4526 4527

  if (!(ndb= check_ndb_in_thd(thd)))
4528
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4529 4530 4531
  ndb->setDatabaseName(db);

  NDBDICT* dict= ndb->getDictionary();
4532
  dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
4533 4534
  dict->invalidateTable(name);
  if (!(tab= dict->getTable(name)))
4535
  {
4536 4537 4538 4539 4540
    const NdbError err= dict->getNdbError();
    if (err.code == 709)
      DBUG_RETURN(0);
    ERR_RETURN(err);
  }
4541

4542 4543 4544 4545
  DBUG_PRINT("info", ("Found table %s", tab->getName()));
  DBUG_RETURN(1);
}

unknown's avatar
unknown committed
4546 4547


unknown's avatar
unknown committed
4548
extern "C" byte* tables_get_key(const char *entry, uint *length,
4549
                                my_bool not_used __attribute__((unused)))
4550 4551 4552 4553 4554 4555
{
  *length= strlen(entry);
  return (byte*) entry;
}


4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569
/*
  Drop a database in NDB Cluster
 */

int ndbcluster_drop_database(const char *path)
{
  DBUG_ENTER("ndbcluster_drop_database");
  THD *thd= current_thd;
  char dbname[FN_HEADLEN];
  Ndb* ndb;
  NdbDictionary::Dictionary::List list;
  uint i;
  char *tabname;
  List<char> drop_list;
4570
  int ret= 0;
4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596
  ha_ndbcluster::set_dbname(path, (char *)&dbname);
  DBUG_PRINT("enter", ("db: %s", dbname));
  
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
  
  // List tables in NDB
  NDBDICT *dict= ndb->getDictionary();
  if (dict->listObjects(list, 
                        NdbDictionary::Object::UserTable) != 0)
    ERR_RETURN(dict->getNdbError());
  for (i= 0 ; i < list.count ; i++)
  {
    NdbDictionary::Dictionary::List::Element& t= list.elements[i];
    DBUG_PRINT("info", ("Found %s/%s in NDB", t.database, t.name));     
    
    // Add only tables that belongs to db
    if (my_strcasecmp(system_charset_info, t.database, dbname))
      continue;
    DBUG_PRINT("info", ("%s must be dropped", t.name));     
    drop_list.push_back(thd->strdup(t.name));
  }
  // Drop any tables belonging to database
  ndb->setDatabaseName(dbname);
  List_iterator_fast<char> it(drop_list);
  while ((tabname=it++))
4597
  {
4598
    if (dict->dropTable(tabname))
4599 4600 4601
    {
      const NdbError err= dict->getNdbError();
      if (err.code != 709)
4602 4603
      {
        ERR_PRINT(err);
4604
        ret= ndb_to_mysql_error(&err);
4605
      }
4606 4607 4608
    }
  }
  DBUG_RETURN(ret);      
4609 4610 4611
}


4612
int ndbcluster_find_files(THD *thd,const char *db,const char *path,
4613
                          const char *wild, bool dir, List<char> *files)
unknown's avatar
unknown committed
4614
{
4615 4616 4617
  DBUG_ENTER("ndbcluster_find_files");
  DBUG_PRINT("enter", ("db: %s", db));
  { // extra bracket to avoid gcc 2.95.3 warning
unknown's avatar
unknown committed
4618
  uint i;
4619
  Ndb* ndb;
4620
  char name[FN_REFLEN];
unknown's avatar
unknown committed
4621
  HASH ndb_tables, ok_tables;
unknown's avatar
unknown committed
4622
  NdbDictionary::Dictionary::List list;
4623 4624 4625 4626

  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(HA_ERR_NO_CONNECTION);

4627
  if (dir)
unknown's avatar
unknown committed
4628
    DBUG_RETURN(0); // Discover of databases not yet supported
4629

unknown's avatar
unknown committed
4630
  // List tables in NDB
4631
  NDBDICT *dict= ndb->getDictionary();
unknown's avatar
unknown committed
4632
  if (dict->listObjects(list, 
4633
                        NdbDictionary::Object::UserTable) != 0)
unknown's avatar
unknown committed
4634
    ERR_RETURN(dict->getNdbError());
4635

unknown's avatar
unknown committed
4636
  if (hash_init(&ndb_tables, system_charset_info,list.count,0,0,
4637
                (hash_get_key)tables_get_key,0,0))
unknown's avatar
unknown committed
4638 4639 4640 4641 4642 4643
  {
    DBUG_PRINT("error", ("Failed to init HASH ndb_tables"));
    DBUG_RETURN(-1);
  }

  if (hash_init(&ok_tables, system_charset_info,32,0,0,
4644
                (hash_get_key)tables_get_key,0,0))
unknown's avatar
unknown committed
4645 4646 4647 4648 4649 4650
  {
    DBUG_PRINT("error", ("Failed to init HASH ok_tables"));
    hash_free(&ndb_tables);
    DBUG_RETURN(-1);
  }  

unknown's avatar
unknown committed
4651 4652 4653
  for (i= 0 ; i < list.count ; i++)
  {
    NdbDictionary::Dictionary::List::Element& t= list.elements[i];
unknown's avatar
unknown committed
4654
    DBUG_PRINT("info", ("Found %s/%s in NDB", t.database, t.name));     
unknown's avatar
unknown committed
4655

4656 4657 4658
    // Add only tables that belongs to db
    if (my_strcasecmp(system_charset_info, t.database, db))
      continue;
unknown's avatar
unknown committed
4659

unknown's avatar
unknown committed
4660
    // Apply wildcard to list of tables in NDB
4661
    if (wild)
4662
    {
4663 4664
      if (lower_case_table_names)
      {
4665 4666
        if (wild_case_compare(files_charset_info, t.name, wild))
          continue;
4667 4668
      }
      else if (wild_compare(t.name,wild,0))
4669
        continue;
4670
    }
unknown's avatar
unknown committed
4671 4672
    DBUG_PRINT("info", ("Inserting %s into ndb_tables hash", t.name));     
    my_hash_insert(&ndb_tables, (byte*)thd->strdup(t.name));
unknown's avatar
unknown committed
4673 4674
  }

unknown's avatar
unknown committed
4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689
  char *file_name;
  List_iterator<char> it(*files);
  List<char> delete_list;
  while ((file_name=it++))
  {
    DBUG_PRINT("info", ("%s", file_name));     
    if (hash_search(&ndb_tables, file_name, strlen(file_name)))
    {
      DBUG_PRINT("info", ("%s existed in NDB _and_ on disk ", file_name));
      // File existed in NDB and as frm file, put in ok_tables list
      my_hash_insert(&ok_tables, (byte*)file_name);
      continue;
    }
    
    // File is not in NDB, check for .ndb file with this name
4690
    (void)strxnmov(name, FN_REFLEN, 
4691
                   mysql_data_home,"/",db,"/",file_name,ha_ndb_ext,NullS);
unknown's avatar
unknown committed
4692
    DBUG_PRINT("info", ("Check access for %s", name));
4693
    if (access(name, F_OK))
unknown's avatar
unknown committed
4694 4695 4696 4697
    {
      DBUG_PRINT("info", ("%s did not exist on disk", name));     
      // .ndb file did not exist on disk, another table type
      continue;
4698
    }
4699

unknown's avatar
unknown committed
4700 4701 4702
    DBUG_PRINT("info", ("%s existed on disk", name));     
    // The .ndb file exists on disk, but it's not in list of tables in ndb
    // Verify that handler agrees table is gone.
4703
    if (ndbcluster_table_exists_in_engine(thd, db, file_name) == 0)    
unknown's avatar
unknown committed
4704 4705 4706 4707 4708 4709 4710
    {
      DBUG_PRINT("info", ("NDB says %s does not exists", file_name));     
      it.remove();
      // Put in list of tables to remove from disk
      delete_list.push_back(thd->strdup(file_name));
    }
  }
4711

unknown's avatar
unknown committed
4712 4713 4714 4715
  // Check for new files to discover
  DBUG_PRINT("info", ("Checking for new files to discover"));       
  List<char> create_list;
  for (i= 0 ; i < ndb_tables.records ; i++)
4716
  {
unknown's avatar
unknown committed
4717 4718
    file_name= hash_element(&ndb_tables, i);
    if (!hash_search(&ok_tables, file_name, strlen(file_name)))
4719
    {
unknown's avatar
unknown committed
4720 4721 4722 4723 4724 4725
      DBUG_PRINT("info", ("%s must be discovered", file_name));       
      // File is in list of ndb tables and not in ok_tables
      // This table need to be created
      create_list.push_back(thd->strdup(file_name));
    }
  }
4726

unknown's avatar
unknown committed
4727 4728
  // Lock mutex before deleting and creating frm files
  pthread_mutex_lock(&LOCK_open);
4729

unknown's avatar
unknown committed
4730 4731 4732 4733 4734
  if (!global_read_lock)
  {
    // Delete old files
    List_iterator_fast<char> it3(delete_list);
    while ((file_name=it3++))
4735 4736
    {
      DBUG_PRINT("info", ("Remove table %s/%s", db, file_name));
unknown's avatar
unknown committed
4737 4738 4739 4740
      // Delete the table and all related files
      TABLE_LIST table_list;
      bzero((char*) &table_list,sizeof(table_list));
      table_list.db= (char*) db;
4741
      table_list.alias= table_list.table_name= (char*)file_name;
4742
      (void)mysql_rm_table_part2(thd, &table_list,
unknown's avatar
Merge  
unknown committed
4743 4744 4745 4746
                                                                 /* if_exists */ FALSE,
                                                                 /* drop_temporary */ FALSE,
                                                                 /* drop_view */ FALSE,
                                                                 /* dont_log_query*/ TRUE);
4747 4748
      /* Clear error message that is returned when table is deleted */
      thd->clear_error();
4749 4750 4751
    }
  }

unknown's avatar
unknown committed
4752 4753 4754 4755
  // Create new files
  List_iterator_fast<char> it2(create_list);
  while ((file_name=it2++))
  {  
4756
    DBUG_PRINT("info", ("Table %s need discovery", file_name));
4757
    if (ha_create_table_from_engine(thd, db, file_name) == 0)
4758
      files->push_back(thd->strdup(file_name)); 
unknown's avatar
unknown committed
4759 4760 4761 4762 4763
  }

  pthread_mutex_unlock(&LOCK_open);      
  
  hash_free(&ok_tables);
4764
  hash_free(&ndb_tables);
4765
  } // extra bracket to avoid gcc 2.95.3 warning
4766
  DBUG_RETURN(0);    
unknown's avatar
unknown committed
4767 4768 4769 4770 4771 4772 4773 4774
}


/*
  Initialise all gloal variables before creating 
  a NDB Cluster table handler
 */

4775 4776 4777 4778 4779 4780 4781
/* Call back after cluster connect */
static int connect_callback()
{
  update_status_variables(g_ndb_cluster_connection);
  return 0;
}

4782
bool ndbcluster_init()
unknown's avatar
unknown committed
4783
{
unknown's avatar
unknown committed
4784
  int res;
unknown's avatar
unknown committed
4785
  DBUG_ENTER("ndbcluster_init");
4786 4787 4788 4789

  if (have_ndbcluster != SHOW_OPTION_YES)
    goto ndbcluster_init_error;

4790
  // Set connectstring if specified
4791 4792
  if (opt_ndbcluster_connectstring != 0)
    DBUG_PRINT("connectstring", ("%s", opt_ndbcluster_connectstring));     
4793
  if ((g_ndb_cluster_connection=
4794
       new Ndb_cluster_connection(opt_ndbcluster_connectstring)) == 0)
4795
  {
4796
    DBUG_PRINT("error",("Ndb_cluster_connection(%s)",
4797
                        opt_ndbcluster_connectstring));
unknown's avatar
unknown committed
4798
    goto ndbcluster_init_error;
4799
  }
unknown's avatar
unknown committed
4800

4801 4802 4803
  g_ndb_cluster_connection->set_optimized_node_selection
    (opt_ndb_optimized_node_selection);

unknown's avatar
unknown committed
4804
  // Create a Ndb object to open the connection  to NDB
4805 4806 4807 4808 4809
  if ( (g_ndb= new Ndb(g_ndb_cluster_connection, "sys")) == 0 )
  {
    DBUG_PRINT("error", ("failed to create global ndb object"));
    goto ndbcluster_init_error;
  }
4810
  g_ndb->getDictionary()->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
unknown's avatar
unknown committed
4811 4812 4813
  if (g_ndb->init() != 0)
  {
    ERR_PRINT (g_ndb->getNdbError());
unknown's avatar
unknown committed
4814
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
4815
  }
unknown's avatar
unknown committed
4816

unknown's avatar
unknown committed
4817
  if ((res= g_ndb_cluster_connection->connect(0,0,0)) == 0)
unknown's avatar
unknown committed
4818
  {
4819
    connect_callback();
unknown's avatar
unknown committed
4820
    DBUG_PRINT("info",("NDBCLUSTER storage engine at %s on port %d",
4821 4822
                       g_ndb_cluster_connection->get_connected_host(),
                       g_ndb_cluster_connection->get_connected_port()));
4823
    g_ndb_cluster_connection->wait_until_ready(10,3);
unknown's avatar
unknown committed
4824
  } 
unknown's avatar
unknown committed
4825
  else if (res == 1)
unknown's avatar
unknown committed
4826
  {
4827
    if (g_ndb_cluster_connection->start_connect_thread(connect_callback)) 
4828
    {
unknown's avatar
unknown committed
4829
      DBUG_PRINT("error", ("g_ndb_cluster_connection->start_connect_thread()"));
unknown's avatar
unknown committed
4830 4831
      goto ndbcluster_init_error;
    }
4832
#ifndef DBUG_OFF
unknown's avatar
unknown committed
4833 4834
    {
      char buf[1024];
4835
      DBUG_PRINT("info",
4836 4837 4838 4839
                 ("NDBCLUSTER storage engine not started, "
                  "will connect using %s",
                  g_ndb_cluster_connection->
                  get_connectstring(buf,sizeof(buf))));
unknown's avatar
unknown committed
4840
    }
4841
#endif
unknown's avatar
unknown committed
4842
  }
unknown's avatar
unknown committed
4843
  else
unknown's avatar
unknown committed
4844 4845 4846
  {
    DBUG_ASSERT(res == -1);
    DBUG_PRINT("error", ("permanent error"));
unknown's avatar
unknown committed
4847
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
4848
  }
unknown's avatar
unknown committed
4849
  
unknown's avatar
unknown committed
4850 4851 4852
  (void) hash_init(&ndbcluster_open_tables,system_charset_info,32,0,0,
                   (hash_get_key) ndbcluster_get_key,0,0);
  pthread_mutex_init(&ndbcluster_mutex,MY_MUTEX_INIT_FAST);
unknown's avatar
Merge  
unknown committed
4853 4854
  pthread_mutex_init(&LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST);
  pthread_cond_init(&COND_ndb_util_thread, NULL);
4855

unknown's avatar
Merge  
unknown committed
4856 4857 4858 4859 4860 4861

  // Create utility thread
  pthread_t tmp;
  if (pthread_create(&tmp, &connection_attrib, ndb_util_thread_func, 0))
  {
    DBUG_PRINT("error", ("Could not create ndb utility thread"));
4862 4863 4864 4865
    hash_free(&ndbcluster_open_tables);
    pthread_mutex_destroy(&ndbcluster_mutex);
    pthread_mutex_destroy(&LOCK_ndb_util_thread);
    pthread_cond_destroy(&COND_ndb_util_thread);
unknown's avatar
Merge  
unknown committed
4866 4867 4868
    goto ndbcluster_init_error;
  }
  
unknown's avatar
unknown committed
4869
  ndbcluster_inited= 1;
4870
  DBUG_RETURN(FALSE);
unknown's avatar
Merge  
unknown committed
4871

4872
ndbcluster_init_error:
unknown's avatar
unknown committed
4873
  if (g_ndb)
4874 4875 4876 4877 4878
    delete g_ndb;
  g_ndb= NULL;
  if (g_ndb_cluster_connection)
    delete g_ndb_cluster_connection;
  g_ndb_cluster_connection= NULL;
4879 4880
  have_ndbcluster= SHOW_OPTION_DISABLED;	// If we couldn't use handler
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4881 4882 4883 4884 4885 4886
}


/*
  End use of the NDB Cluster table handler
  - free all global variables allocated by 
unknown's avatar
Merge  
unknown committed
4887
    ndbcluster_init()
unknown's avatar
unknown committed
4888 4889 4890 4891 4892
*/

bool ndbcluster_end()
{
  DBUG_ENTER("ndbcluster_end");
unknown's avatar
Merge  
unknown committed
4893

4894 4895 4896
  if (!ndbcluster_inited)
    DBUG_RETURN(0);

unknown's avatar
Merge  
unknown committed
4897 4898 4899 4900 4901 4902
  // Kill ndb utility thread
  (void) pthread_mutex_lock(&LOCK_ndb_util_thread);  
  DBUG_PRINT("exit",("killing ndb util thread: %lx", ndb_util_thread));
  (void) pthread_cond_signal(&COND_ndb_util_thread);
  (void) pthread_mutex_unlock(&LOCK_ndb_util_thread);

unknown's avatar
unknown committed
4903
  if (g_ndb)
4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916
  {
#ifndef DBUG_OFF
    Ndb::Free_list_usage tmp; tmp.m_name= 0;
    while (g_ndb->get_free_list_usage(&tmp))
    {
      uint leaked= (uint) tmp.m_created - tmp.m_free;
      if (leaked)
        fprintf(stderr, "NDB: Found %u %s%s that %s not been released\n",
                leaked, tmp.m_name,
                (leaked == 1)?"":"'s",
                (leaked == 1)?"has":"have");
    }
#endif
unknown's avatar
unknown committed
4917
    delete g_ndb;
4918
  }
unknown's avatar
unknown committed
4919
  g_ndb= NULL;
4920 4921 4922
  if (g_ndb_cluster_connection)
    delete g_ndb_cluster_connection;
  g_ndb_cluster_connection= NULL;
4923

unknown's avatar
unknown committed
4924 4925
  hash_free(&ndbcluster_open_tables);
  pthread_mutex_destroy(&ndbcluster_mutex);
unknown's avatar
Merge  
unknown committed
4926 4927
  pthread_mutex_destroy(&LOCK_ndb_util_thread);
  pthread_cond_destroy(&COND_ndb_util_thread);
unknown's avatar
unknown committed
4928 4929 4930 4931
  ndbcluster_inited= 0;
  DBUG_RETURN(0);
}

4932 4933 4934 4935 4936
/*
  Static error print function called from
  static handler method ndbcluster_commit
  and ndbcluster_rollback
*/
4937 4938

void ndbcluster_print_error(int error, const NdbOperation *error_op)
4939
{
4940 4941
  DBUG_ENTER("ndbcluster_print_error");
  TABLE tab;
4942
  const char *tab_name= (error_op) ? error_op->getTableName() : "";
4943
  tab.alias= (char *) tab_name;
4944
  ha_ndbcluster error_handler(&tab);
4945
  tab.file= &error_handler;
4946
  error_handler.print_error(error, MYF(0));
unknown's avatar
unknown committed
4947
  DBUG_VOID_RETURN;
4948
}
unknown's avatar
unknown committed
4949

4950 4951 4952
/**
 * Set a given location from full pathname to database name
 *
unknown's avatar
unknown committed
4953
 */
4954
void ha_ndbcluster::set_dbname(const char *path_name, char *dbname)
unknown's avatar
unknown committed
4955 4956 4957 4958
{
  char *end, *ptr;
  
  /* Scan name from the end */
4959 4960 4961 4962 4963 4964
  ptr= strend(path_name)-1;
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  ptr--;
  end= ptr;
unknown's avatar
unknown committed
4965 4966 4967 4968
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  uint name_len= end - ptr;
4969 4970
  memcpy(dbname, ptr + 1, name_len);
  dbname[name_len]= '\0';
unknown's avatar
unknown committed
4971 4972
#ifdef __WIN__
  /* Put to lower case */
4973 4974
  
  ptr= dbname;
unknown's avatar
unknown committed
4975 4976
  
  while (*ptr != '\0') {
4977
    *ptr= tolower(*ptr);
unknown's avatar
unknown committed
4978 4979 4980 4981 4982
    ptr++;
  }
#endif
}

4983 4984 4985 4986 4987 4988 4989 4990 4991
/*
  Set m_dbname from full pathname to table file
 */

void ha_ndbcluster::set_dbname(const char *path_name)
{
  set_dbname(path_name, m_dbname);
}

unknown's avatar
unknown committed
4992 4993 4994 4995 4996 4997 4998 4999 5000 5001
/**
 * Set a given location from full pathname to table file
 *
 */
void
ha_ndbcluster::set_tabname(const char *path_name, char * tabname)
{
  char *end, *ptr;
  
  /* Scan name from the end */
5002 5003
  end= strend(path_name)-1;
  ptr= end;
unknown's avatar
unknown committed
5004 5005 5006
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
5007
  uint name_len= end - ptr;
unknown's avatar
unknown committed
5008
  memcpy(tabname, ptr + 1, end - ptr);
5009
  tabname[name_len]= '\0';
unknown's avatar
unknown committed
5010 5011
#ifdef __WIN__
  /* Put to lower case */
5012
  ptr= tabname;
unknown's avatar
unknown committed
5013 5014 5015 5016 5017 5018 5019 5020 5021
  
  while (*ptr != '\0') {
    *ptr= tolower(*ptr);
    ptr++;
  }
#endif
}

/*
5022
  Set m_tabname from full pathname to table file 
unknown's avatar
unknown committed
5023 5024
 */

5025
void ha_ndbcluster::set_tabname(const char *path_name)
unknown's avatar
unknown committed
5026
{
5027
  set_tabname(path_name, m_tabname);
unknown's avatar
unknown committed
5028 5029 5030 5031
}


ha_rows 
unknown's avatar
unknown committed
5032 5033 5034 5035
ha_ndbcluster::records_in_range(uint inx, key_range *min_key,
                                key_range *max_key)
{
  KEY *key_info= table->key_info + inx;
unknown's avatar
unknown committed
5036
  uint key_length= key_info->key_length;
5037
  NDB_INDEX_TYPE idx_type= get_index_type(inx);  
unknown's avatar
unknown committed
5038 5039

  DBUG_ENTER("records_in_range");
5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053
  // Prevent partial read of hash indexes by returning HA_POS_ERROR
  if ((idx_type == UNIQUE_INDEX || idx_type == PRIMARY_KEY_INDEX) &&
      ((min_key && min_key->length < key_length) ||
       (max_key && max_key->length < key_length)))
    DBUG_RETURN(HA_POS_ERROR);
  
  // Read from hash index with full key
  // This is a "const" table which returns only one record!      
  if ((idx_type != ORDERED_INDEX) &&
      ((min_key && min_key->length == key_length) || 
       (max_key && max_key->length == key_length)))
    DBUG_RETURN(1);
  
  DBUG_RETURN(10); /* Good guess when you don't know anything */
unknown's avatar
unknown committed
5054 5055
}

5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092
ulong ha_ndbcluster::table_flags(void) const
{
  if (m_ha_not_exact_count)
    return m_table_flags | HA_NOT_EXACT_COUNT;
  else
    return m_table_flags;
}
const char * ha_ndbcluster::table_type() const 
{
  return("ndbcluster");
}
uint ha_ndbcluster::max_supported_record_length() const
{ 
  return NDB_MAX_TUPLE_SIZE;
}
uint ha_ndbcluster::max_supported_keys() const
{
  return MAX_KEY;
}
uint ha_ndbcluster::max_supported_key_parts() const 
{
  return NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY;
}
uint ha_ndbcluster::max_supported_key_length() const
{
  return NDB_MAX_KEY_SIZE;
}
bool ha_ndbcluster::low_byte_first() const
{ 
#ifdef WORDS_BIGENDIAN
  return FALSE;
#else
  return TRUE;
#endif
}
bool ha_ndbcluster::has_transactions()
{
unknown's avatar
unknown committed
5093
  return m_transaction_on;
5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107
}
const char* ha_ndbcluster::index_type(uint key_number)
{
  switch (get_index_type(key_number)) {
  case ORDERED_INDEX:
  case UNIQUE_ORDERED_INDEX:
  case PRIMARY_KEY_ORDERED_INDEX:
    return "BTREE";
  case UNIQUE_INDEX:
  case PRIMARY_KEY_INDEX:
  default:
    return "HASH";
  }
}
unknown's avatar
Merge  
unknown committed
5108

5109 5110
uint8 ha_ndbcluster::table_cache_type()
{
unknown's avatar
Merge  
unknown committed
5111 5112 5113 5114 5115 5116
  DBUG_ENTER("ha_ndbcluster::table_cache_type=HA_CACHE_TBL_ASKTRANSACT");
  DBUG_RETURN(HA_CACHE_TBL_ASKTRANSACT);
}


uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname,
5117
                         Uint64 *commit_count)
unknown's avatar
Merge  
unknown committed
5118 5119 5120
{
  DBUG_ENTER("ndb_get_commitcount");

5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138
  char name[FN_REFLEN];
  NDB_SHARE *share;
  (void)strxnmov(name, FN_REFLEN, "./",dbname,"/",tabname,NullS);
  DBUG_PRINT("enter", ("name: %s", name));
  pthread_mutex_lock(&ndbcluster_mutex);
  if (!(share=(NDB_SHARE*) hash_search(&ndbcluster_open_tables,
                                       (byte*) name,
                                       strlen(name))))
  {
    pthread_mutex_unlock(&ndbcluster_mutex);
    DBUG_PRINT("info", ("Table %s not found in ndbcluster_open_tables",
                        name));
    DBUG_RETURN(1);
  }
  share->use_count++;
  pthread_mutex_unlock(&ndbcluster_mutex);

  pthread_mutex_lock(&share->mutex);
unknown's avatar
Merge  
unknown committed
5139 5140
  if (ndb_cache_check_time > 0)
  {
5141
    if (share->commit_count != 0)
unknown's avatar
Merge  
unknown committed
5142
    {
5143 5144 5145 5146 5147 5148
      *commit_count= share->commit_count;
      DBUG_PRINT("info", ("Getting commit_count: %llu from share",
                          share->commit_count));
      pthread_mutex_unlock(&share->mutex);
      free_share(share);
      DBUG_RETURN(0);
unknown's avatar
Merge  
unknown committed
5149 5150
    }
  }
5151
  DBUG_PRINT("info", ("Get commit_count from NDB"));
unknown's avatar
Merge  
unknown committed
5152 5153 5154 5155
  Ndb *ndb;
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(1);
  ndb->setDatabaseName(dbname);
5156 5157
  uint lock= share->commit_count_lock;
  pthread_mutex_unlock(&share->mutex);
unknown's avatar
Merge  
unknown committed
5158 5159 5160

  struct Ndb_statistics stat;
  if (ndb_get_table_statistics(ndb, tabname, &stat))
5161 5162
  {
    free_share(share);
unknown's avatar
Merge  
unknown committed
5163
    DBUG_RETURN(1);
5164 5165 5166
  }

  pthread_mutex_lock(&share->mutex);
unknown's avatar
unknown committed
5167
  if (share->commit_count_lock == lock)
5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179
  {
    DBUG_PRINT("info", ("Setting commit_count to %llu", stat.commit_count));
    share->commit_count= stat.commit_count;
    *commit_count= stat.commit_count;
  }
  else
  {
    DBUG_PRINT("info", ("Discarding commit_count, comit_count_lock changed"));
    *commit_count= 0;
  }
  pthread_mutex_unlock(&share->mutex);
  free_share(share);
unknown's avatar
Merge  
unknown committed
5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215
  DBUG_RETURN(0);
}


/*
  Check if a cached query can be used.
  This is done by comparing the supplied engine_data to commit_count of
  the table.
  The commit_count is either retrieved from the share for the table, where
  it has been cached by the util thread. If the util thread is not started,
  NDB has to be contacetd to retrieve the commit_count, this will introduce
  a small delay while waiting for NDB to answer.


  SYNOPSIS
  ndbcluster_cache_retrieval_allowed
    thd            thread handle
    full_name      concatenation of database name,
                   the null character '\0', and the table
                   name
    full_name_len  length of the full name,
                   i.e. len(dbname) + len(tablename) + 1

    engine_data    parameter retrieved when query was first inserted into
                   the cache. If the value of engine_data is changed,
                   all queries for this table should be invalidated.

  RETURN VALUE
    TRUE  Yes, use the query from cache
    FALSE No, don't use the cached query, and if engine_data
          has changed, all queries for this table should be invalidated

*/

static my_bool
ndbcluster_cache_retrieval_allowed(THD *thd,
5216 5217
                                   char *full_name, uint full_name_len,
                                   ulonglong *engine_data)
unknown's avatar
Merge  
unknown committed
5218 5219 5220 5221 5222 5223 5224 5225
{
  DBUG_ENTER("ndbcluster_cache_retrieval_allowed");

  Uint64 commit_count;
  bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
  char *dbname= full_name;
  char *tabname= dbname+strlen(dbname)+1;

5226 5227
  DBUG_PRINT("enter", ("dbname: %s, tabname: %s, is_autocommit: %d",
                       dbname, tabname, is_autocommit));
unknown's avatar
Merge  
unknown committed
5228 5229

  if (!is_autocommit)
5230 5231
  {
    DBUG_PRINT("exit", ("No, don't use cache in transaction"));
unknown's avatar
Merge  
unknown committed
5232
    DBUG_RETURN(FALSE);
5233
  }
unknown's avatar
Merge  
unknown committed
5234 5235 5236

  if (ndb_get_commitcount(thd, dbname, tabname, &commit_count))
  {
5237 5238
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, could not retrieve commit_count"));
unknown's avatar
Merge  
unknown committed
5239 5240
    DBUG_RETURN(FALSE);
  }
5241
  DBUG_PRINT("info", ("*engine_data: %llu, commit_count: %llu",
5242
                      *engine_data, commit_count));
5243
  if (commit_count == 0)
unknown's avatar
Merge  
unknown committed
5244
  {
5245 5246
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, local commit has been performed"));
unknown's avatar
Merge  
unknown committed
5247 5248
    DBUG_RETURN(FALSE);
  }
5249 5250 5251 5252 5253 5254
  else if (*engine_data != commit_count)
  {
    *engine_data= commit_count; /* invalidate */
     DBUG_PRINT("exit", ("No, commit_count has changed"));
     DBUG_RETURN(FALSE);
   }
unknown's avatar
Merge  
unknown committed
5255

5256
  DBUG_PRINT("exit", ("OK to use cache, engine_data: %llu", *engine_data));
unknown's avatar
Merge  
unknown committed
5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284
  DBUG_RETURN(TRUE);
}


/**
   Register a table for use in the query cache. Fetch the commit_count
   for the table and return it in engine_data, this will later be used
   to check if the table has changed, before the cached query is reused.

   SYNOPSIS
   ha_ndbcluster::can_query_cache_table
    thd            thread handle
    full_name      concatenation of database name,
                   the null character '\0', and the table
                   name
    full_name_len  length of the full name,
                   i.e. len(dbname) + len(tablename) + 1
    qc_engine_callback  function to be called before using cache on this table
    engine_data    out, commit_count for this table

  RETURN VALUE
    TRUE  Yes, it's ok to cahce this query
    FALSE No, don't cach the query

*/

my_bool
ha_ndbcluster::register_query_cache_table(THD *thd,
5285 5286 5287
                                          char *full_name, uint full_name_len,
                                          qc_engine_callback *engine_callback,
                                          ulonglong *engine_data)
unknown's avatar
Merge  
unknown committed
5288 5289 5290 5291
{
  DBUG_ENTER("ha_ndbcluster::register_query_cache_table");

  bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
5292 5293 5294 5295

  DBUG_PRINT("enter",("dbname: %s, tabname: %s, is_autocommit: %d",
		      m_dbname, m_tabname, is_autocommit));

unknown's avatar
Merge  
unknown committed
5296
  if (!is_autocommit)
5297 5298
  {
    DBUG_PRINT("exit", ("Can't register table during transaction"))
unknown's avatar
Merge  
unknown committed
5299
    DBUG_RETURN(FALSE);
5300
  }
unknown's avatar
Merge  
unknown committed
5301 5302 5303 5304 5305

  Uint64 commit_count;
  if (ndb_get_commitcount(thd, m_dbname, m_tabname, &commit_count))
  {
    *engine_data= 0;
5306
    DBUG_PRINT("exit", ("Error, could not get commitcount"))
unknown's avatar
Merge  
unknown committed
5307 5308 5309 5310
    DBUG_RETURN(FALSE);
  }
  *engine_data= commit_count;
  *engine_callback= ndbcluster_cache_retrieval_allowed;
5311 5312
  DBUG_PRINT("exit", ("commit_count: %llu", commit_count));
  DBUG_RETURN(commit_count > 0);
5313
}
unknown's avatar
unknown committed
5314

unknown's avatar
Merge  
unknown committed
5315

unknown's avatar
unknown committed
5316
/*
unknown's avatar
Merge  
unknown committed
5317
  Handling the shared NDB_SHARE structure that is needed to
unknown's avatar
unknown committed
5318 5319 5320 5321 5322 5323 5324
  provide table locking.
  It's also used for sharing data with other NDB handlers
  in the same MySQL Server. There is currently not much
  data we want to or can share.
 */

static byte* ndbcluster_get_key(NDB_SHARE *share,uint *length,
5325
                                my_bool not_used __attribute__((unused)))
unknown's avatar
unknown committed
5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353
{
  *length=share->table_name_length;
  return (byte*) share->table_name;
}

static NDB_SHARE* get_share(const char *table_name)
{
  NDB_SHARE *share;
  pthread_mutex_lock(&ndbcluster_mutex);
  uint length=(uint) strlen(table_name);
  if (!(share=(NDB_SHARE*) hash_search(&ndbcluster_open_tables,
                                       (byte*) table_name,
                                       length)))
  {
    if ((share=(NDB_SHARE *) my_malloc(sizeof(*share)+length+1,
                                       MYF(MY_WME | MY_ZEROFILL))))
    {
      share->table_name_length=length;
      share->table_name=(char*) (share+1);
      strmov(share->table_name,table_name);
      if (my_hash_insert(&ndbcluster_open_tables, (byte*) share))
      {
        pthread_mutex_unlock(&ndbcluster_mutex);
        my_free((gptr) share,0);
        return 0;
      }
      thr_lock_init(&share->lock);
      pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
unknown's avatar
Merge  
unknown committed
5354
      share->commit_count= 0;
5355 5356 5357 5358 5359 5360 5361
      share->commit_count_lock= 0;
    }
    else
    {
      DBUG_PRINT("error", ("Failed to alloc share"));
      pthread_mutex_unlock(&ndbcluster_mutex);
      return 0;
unknown's avatar
unknown committed
5362 5363 5364
    }
  }
  share->use_count++;
5365 5366 5367 5368 5369

  DBUG_PRINT("share",
	     ("table_name: %s, length: %d, use_count: %d, commit_count: %d",
	      share->table_name, share->table_name_length, share->use_count,
	      share->commit_count));
unknown's avatar
unknown committed
5370 5371 5372 5373 5374 5375 5376 5377 5378 5379
  pthread_mutex_unlock(&ndbcluster_mutex);
  return share;
}


static void free_share(NDB_SHARE *share)
{
  pthread_mutex_lock(&ndbcluster_mutex);
  if (!--share->use_count)
  {
5380
     hash_delete(&ndbcluster_open_tables, (byte*) share);
unknown's avatar
unknown committed
5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408
    thr_lock_delete(&share->lock);
    pthread_mutex_destroy(&share->mutex);
    my_free((gptr) share, MYF(0));
  }
  pthread_mutex_unlock(&ndbcluster_mutex);
}



/*
  Internal representation of the frm blob
   
*/

struct frm_blob_struct 
{
  struct frm_blob_header 
  {
    uint ver;      // Version of header
    uint orglen;   // Original length of compressed data
    uint complen;  // Compressed length of data, 0=uncompressed
  } head;
  char data[1];  
};



static int packfrm(const void *data, uint len, 
5409
                   const void **pack_data, uint *pack_len)
unknown's avatar
unknown committed
5410 5411 5412 5413 5414 5415 5416 5417 5418
{
  int error;
  ulong org_len, comp_len;
  uint blob_len;
  frm_blob_struct* blob;
  DBUG_ENTER("packfrm");
  DBUG_PRINT("enter", ("data: %x, len: %d", data, len));
  
  error= 1;
5419
  org_len= len;
unknown's avatar
unknown committed
5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438
  if (my_compress((byte*)data, &org_len, &comp_len))
    goto err;
  
  DBUG_PRINT("info", ("org_len: %d, comp_len: %d", org_len, comp_len));
  DBUG_DUMP("compressed", (char*)data, org_len);
  
  error= 2;
  blob_len= sizeof(frm_blob_struct::frm_blob_header)+org_len;
  if (!(blob= (frm_blob_struct*) my_malloc(blob_len,MYF(MY_WME))))
    goto err;
  
  // Store compressed blob in machine independent format
  int4store((char*)(&blob->head.ver), 1);
  int4store((char*)(&blob->head.orglen), comp_len);
  int4store((char*)(&blob->head.complen), org_len);
  
  // Copy frm data into blob, already in machine independent format
  memcpy(blob->data, data, org_len);  
  
5439 5440 5441
  *pack_data= blob;
  *pack_len= blob_len;
  error= 0;
unknown's avatar
unknown committed
5442 5443 5444 5445 5446 5447 5448 5449 5450
  
  DBUG_PRINT("exit", ("pack_data: %x, pack_len: %d", *pack_data, *pack_len));
err:
  DBUG_RETURN(error);
  
}


static int unpackfrm(const void **unpack_data, uint *unpack_len,
5451
                    const void *pack_data)
unknown's avatar
unknown committed
5452
{
5453
   const frm_blob_struct *blob= (frm_blob_struct*)pack_data;
unknown's avatar
unknown committed
5454 5455 5456 5457 5458
   byte *data;
   ulong complen, orglen, ver;
   DBUG_ENTER("unpackfrm");
   DBUG_PRINT("enter", ("pack_data: %x", pack_data));

5459 5460 5461
   complen=     uint4korr((char*)&blob->head.complen);
   orglen=      uint4korr((char*)&blob->head.orglen);
   ver=         uint4korr((char*)&blob->head.ver);
unknown's avatar
unknown committed
5462 5463
 
   DBUG_PRINT("blob",("ver: %d complen: %d orglen: %d",
5464
                     ver,complen,orglen));
unknown's avatar
unknown committed
5465 5466 5467 5468
   DBUG_DUMP("blob->data", (char*) blob->data, complen);
 
   if (ver != 1)
     DBUG_RETURN(1);
5469
   if (!(data= my_malloc(max(orglen, complen), MYF(MY_WME))))
unknown's avatar
unknown committed
5470 5471 5472 5473 5474 5475 5476 5477 5478
     DBUG_RETURN(2);
   memcpy(data, blob->data, complen);
 
   if (my_uncompress(data, &complen, &orglen))
   {
     my_free((char*)data, MYF(0));
     DBUG_RETURN(3);
   }

5479 5480
   *unpack_data= data;
   *unpack_len= complen;
unknown's avatar
unknown committed
5481 5482 5483 5484 5485

   DBUG_PRINT("exit", ("frmdata: %x, len: %d", *unpack_data, *unpack_len));

   DBUG_RETURN(0);
}
unknown's avatar
unknown committed
5486 5487 5488

static 
int
5489
ndb_get_table_statistics(Ndb* ndb, const char * table,
5490
                         struct Ndb_statistics * ndbstat)
unknown's avatar
unknown committed
5491 5492 5493
{
  DBUG_ENTER("ndb_get_table_statistics");
  DBUG_PRINT("enter", ("table: %s", table));
5494
  NdbTransaction* pTrans= ndb->startTransaction();
unknown's avatar
unknown committed
5495 5496 5497 5498
  do 
  {
    if (pTrans == NULL)
      break;
unknown's avatar
unknown committed
5499
      
unknown's avatar
unknown committed
5500 5501 5502 5503
    NdbScanOperation* pOp= pTrans->getNdbScanOperation(table);
    if (pOp == NULL)
      break;
    
5504
    if (pOp->readTuples(NdbOperation::LM_CommittedRead))
unknown's avatar
unknown committed
5505 5506 5507 5508 5509 5510
      break;
    
    int check= pOp->interpret_exit_last_row();
    if (check == -1)
      break;
    
unknown's avatar
unknown committed
5511 5512
    Uint64 rows, commits, mem;
    Uint32 size;
unknown's avatar
unknown committed
5513 5514
    pOp->getValue(NdbDictionary::Column::ROW_COUNT, (char*)&rows);
    pOp->getValue(NdbDictionary::Column::COMMIT_COUNT, (char*)&commits);
5515 5516
    pOp->getValue(NdbDictionary::Column::ROW_SIZE, (char*)&size);
    pOp->getValue(NdbDictionary::Column::FRAGMENT_MEMORY, (char*)&mem);
unknown's avatar
unknown committed
5517
    
5518
    check= pTrans->execute(NdbTransaction::NoCommit,
5519 5520
                           NdbTransaction::AbortOnError,
                           TRUE);
unknown's avatar
unknown committed
5521 5522 5523
    if (check == -1)
      break;
    
5524
    Uint32 count= 0;
unknown's avatar
unknown committed
5525 5526
    Uint64 sum_rows= 0;
    Uint64 sum_commits= 0;
5527 5528
    Uint64 sum_row_size= 0;
    Uint64 sum_mem= 0;
unknown's avatar
unknown committed
5529
    while ((check= pOp->nextResult(TRUE, TRUE)) == 0)
unknown's avatar
unknown committed
5530 5531 5532
    {
      sum_rows+= rows;
      sum_commits+= commits;
5533
      if (sum_row_size < size)
5534
        sum_row_size= size;
5535
      sum_mem+= mem;
5536
      count++;
unknown's avatar
unknown committed
5537 5538 5539 5540 5541
    }
    
    if (check == -1)
      break;

5542
    pOp->close(TRUE);
unknown's avatar
unknown committed
5543

unknown's avatar
unknown committed
5544
    ndb->closeTransaction(pTrans);
5545 5546 5547 5548 5549 5550

    ndbstat->row_count= sum_rows;
    ndbstat->commit_count= sum_commits;
    ndbstat->row_size= sum_row_size;
    ndbstat->fragment_memory= sum_mem;

5551 5552 5553 5554 5555
    DBUG_PRINT("exit", ("records: %llu commits: %llu "
                        "row_size: %llu mem: %llu count: %u",
			sum_rows, sum_commits, sum_row_size,
                        sum_mem, count));

unknown's avatar
unknown committed
5556
    DBUG_RETURN(0);
unknown's avatar
unknown committed
5557
  } while (0);
unknown's avatar
unknown committed
5558

unknown's avatar
unknown committed
5559 5560
  if (pTrans)
    ndb->closeTransaction(pTrans);
unknown's avatar
unknown committed
5561 5562 5563 5564
  DBUG_PRINT("exit", ("failed"));
  DBUG_RETURN(-1);
}

5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579
/*
  Create a .ndb file to serve as a placeholder indicating 
  that the table with this name is a ndb table
*/

int ha_ndbcluster::write_ndb_file()
{
  File file;
  bool error=1;
  char path[FN_REFLEN];
  
  DBUG_ENTER("write_ndb_file");
  DBUG_PRINT("enter", ("db: %s, name: %s", m_dbname, m_tabname));

  (void)strxnmov(path, FN_REFLEN, 
5580
                 mysql_data_home,"/",m_dbname,"/",m_tabname,ha_ndb_ext,NullS);
5581 5582 5583 5584 5585 5586 5587 5588 5589 5590

  if ((file=my_create(path, CREATE_MODE,O_RDWR | O_TRUNC,MYF(MY_WME))) >= 0)
  {
    // It's an empty file
    error=0;
    my_close(file,MYF(0));
  }
  DBUG_RETURN(error);
}

unknown's avatar
unknown committed
5591
int
5592
ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
5593 5594 5595 5596
                                      KEY_MULTI_RANGE *ranges, 
                                      uint range_count,
                                      bool sorted, 
                                      HANDLER_BUFFER *buffer)
unknown's avatar
unknown committed
5597 5598
{
  DBUG_ENTER("ha_ndbcluster::read_multi_range_first");
unknown's avatar
unknown committed
5599
  
unknown's avatar
unknown committed
5600 5601
  int res;
  KEY* key_info= table->key_info + active_index;
5602
  NDB_INDEX_TYPE index_type= get_index_type(active_index);
unknown's avatar
merge  
unknown committed
5603
  ulong reclength= table->s->reclength;
unknown's avatar
unknown committed
5604 5605
  NdbOperation* op;

5606
  if (uses_blob_value(m_retrieve_all_fields))
unknown's avatar
unknown committed
5607 5608 5609 5610
  {
    /**
     * blobs can't be batched currently
     */
unknown's avatar
unknown committed
5611
    m_disable_multi_read= TRUE;
unknown's avatar
unknown committed
5612
    DBUG_RETURN(handler::read_multi_range_first(found_range_p, 
5613 5614 5615 5616
                                                ranges, 
                                                range_count,
                                                sorted, 
                                                buffer));
unknown's avatar
unknown committed
5617 5618
  }

unknown's avatar
unknown committed
5619
  m_disable_multi_read= FALSE;
5620 5621 5622 5623

  /**
   * Copy arguments into member variables
   */
5624 5625 5626
  m_multi_ranges= ranges;
  multi_range_curr= ranges;
  multi_range_end= ranges+range_count;
unknown's avatar
unknown committed
5627 5628 5629
  multi_range_sorted= sorted;
  multi_range_buffer= buffer;

5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640
  /**
   * read multi range will read ranges as follows (if not ordered)
   *
   * input    read order
   * ======   ==========
   * pk-op 1  pk-op 1
   * pk-op 2  pk-op 2
   * range 3  range (3,5) NOTE result rows will be intermixed
   * pk-op 4  pk-op 4
   * range 5
   * pk-op 6  pk-ok 6
5641 5642
   */   

unknown's avatar
unknown committed
5643
  /**
5644 5645
   * Variables for loop
   */
unknown's avatar
unknown committed
5646 5647
  byte *curr= (byte*)buffer->buffer;
  byte *end_of_buffer= (byte*)buffer->buffer_end;
unknown's avatar
unknown committed
5648 5649 5650 5651
  NdbOperation::LockMode lm= 
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
  const NDBTAB *tab= (const NDBTAB *) m_table;
  const NDBINDEX *unique_idx= (NDBINDEX *) m_index[active_index].unique_index;
5652
  const NDBINDEX *idx= (NDBINDEX *) m_index[active_index].index; 
5653 5654
  const NdbOperation* lastOp= m_active_trans->getLastDefinedOperation();
  NdbIndexScanOperation* scanOp= 0;
unknown's avatar
unknown committed
5655 5656
  for (; multi_range_curr<multi_range_end && curr+reclength <= end_of_buffer; 
       multi_range_curr++)
5657
  {
unknown's avatar
unknown committed
5658 5659 5660 5661 5662 5663
    switch (index_type){
    case PRIMARY_KEY_ORDERED_INDEX:
      if (!(multi_range_curr->start_key.length == key_info->key_length &&
            multi_range_curr->start_key.flag == HA_READ_KEY_EXACT))
      goto range;
      /* fall through */
5664
    case PRIMARY_KEY_INDEX:
unknown's avatar
unknown committed
5665
    {
5666
      multi_range_curr->range_flag |= UNIQUE_RANGE;
unknown's avatar
unknown committed
5667
      if ((op= m_active_trans->getNdbOperation(tab)) && 
5668 5669 5670
          !op->readTuple(lm) && 
          !set_primary_key(op, multi_range_curr->start_key.key) &&
          !define_read_attrs(curr, op) &&
unknown's avatar
unknown committed
5671
          (op->setAbortOption(AO_IgnoreError), TRUE))
5672
        curr += reclength;
unknown's avatar
unknown committed
5673
      else
5674
        ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
5675
      break;
unknown's avatar
unknown committed
5676 5677
    }
    break;
unknown's avatar
unknown committed
5678 5679 5680 5681 5682 5683 5684
    case UNIQUE_ORDERED_INDEX:
      if (!(multi_range_curr->start_key.length == key_info->key_length &&
            multi_range_curr->start_key.flag == HA_READ_KEY_EXACT &&
            !check_null_in_key(key_info, multi_range_curr->start_key.key,
                               multi_range_curr->start_key.length)))
      goto range;
      /* fall through */
5685
    case UNIQUE_INDEX:
unknown's avatar
unknown committed
5686
    {
5687
      multi_range_curr->range_flag |= UNIQUE_RANGE;
unknown's avatar
unknown committed
5688
      if ((op= m_active_trans->getNdbIndexOperation(unique_idx, tab)) && 
5689 5690 5691
          !op->readTuple(lm) && 
          !set_index_key(op, key_info, multi_range_curr->start_key.key) &&
          !define_read_attrs(curr, op) &&
unknown's avatar
unknown committed
5692
          (op->setAbortOption(AO_IgnoreError), TRUE))
5693
        curr += reclength;
unknown's avatar
unknown committed
5694
      else
5695
        ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
5696 5697
      break;
    }
unknown's avatar
unknown committed
5698 5699
    case ORDERED_INDEX:
    {
5700
  range:
5701
      multi_range_curr->range_flag &= ~(uint)UNIQUE_RANGE;
5702 5703
      if (scanOp == 0)
      {
5704 5705 5706 5707 5708 5709
        if (m_multi_cursor)
        {
          scanOp= m_multi_cursor;
          DBUG_ASSERT(scanOp->getSorted() == sorted);
          DBUG_ASSERT(scanOp->getLockMode() == 
                      (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type));
unknown's avatar
unknown committed
5710
          if (scanOp->reset_bounds(m_force_send))
5711 5712 5713 5714 5715
            DBUG_RETURN(ndb_err(m_active_trans));
          
          end_of_buffer -= reclength;
        }
        else if ((scanOp= m_active_trans->getNdbIndexScanOperation(idx, tab)) 
unknown's avatar
unknown committed
5716
                 &&!scanOp->readTuples(lm, 0, parallelism, sorted, FALSE, TRUE)
5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727
                 &&!generate_scan_filter(m_cond_stack, scanOp)
                 &&!define_read_attrs(end_of_buffer-reclength, scanOp))
        {
          m_multi_cursor= scanOp;
          m_multi_range_cursor_result_ptr= end_of_buffer-reclength;
        }
        else
        {
          ERR_RETURN(scanOp ? scanOp->getNdbError() : 
                     m_active_trans->getNdbError());
        }
5728
      }
5729

5730
      const key_range *keys[2]= { &multi_range_curr->start_key, 
5731
                                  &multi_range_curr->end_key };
5732
      if ((res= set_bounds(scanOp, keys, multi_range_curr-ranges)))
5733
        DBUG_RETURN(res);
5734
      break;
unknown's avatar
unknown committed
5735
    }
unknown's avatar
unknown committed
5736
    case UNDEFINED_INDEX:
unknown's avatar
unknown committed
5737 5738 5739 5740
      DBUG_ASSERT(FALSE);
      DBUG_RETURN(1);
      break;
    }
unknown's avatar
unknown committed
5741 5742
  }
  
5743
  if (multi_range_curr != multi_range_end)
unknown's avatar
unknown committed
5744
  {
5745 5746 5747 5748 5749 5750
    /**
     * Mark that we're using entire buffer (even if might not) as
     *   we haven't read all ranges for some reason
     * This as we don't want mysqld to reuse the buffer when we read
     *   the remaining ranges
     */
5751
    buffer->end_of_used_area= (byte*)buffer->buffer_end;
unknown's avatar
unknown committed
5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762
  }
  else
  {
    buffer->end_of_used_area= curr;
  }
  
  /**
   * Set first operation in multi range
   */
  m_current_multi_operation= 
    lastOp ? lastOp->next() : m_active_trans->getFirstDefinedOperation();
5763
  if (!(res= execute_no_commit_ie(this, m_active_trans)))
unknown's avatar
unknown committed
5764
  {
5765 5766
    m_multi_range_defined= multi_range_curr;
    multi_range_curr= ranges;
5767 5768
    m_multi_range_result_ptr= (byte*)buffer->buffer;
    DBUG_RETURN(read_multi_range_next(found_range_p));
unknown's avatar
unknown committed
5769 5770 5771 5772
  }
  ERR_RETURN(m_active_trans->getNdbError());
}

unknown's avatar
unknown committed
5773 5774 5775 5776 5777 5778
#if 0
#define DBUG_MULTI_RANGE(x) printf("read_multi_range_next: case %d\n", x);
#else
#define DBUG_MULTI_RANGE(x)
#endif

unknown's avatar
unknown committed
5779
int
5780
ha_ndbcluster::read_multi_range_next(KEY_MULTI_RANGE ** multi_range_found_p)
unknown's avatar
unknown committed
5781 5782
{
  DBUG_ENTER("ha_ndbcluster::read_multi_range_next");
5783
  if (m_disable_multi_read)
5784
  {
5785
    DBUG_RETURN(handler::read_multi_range_next(multi_range_found_p));
5786
  }
5787
  
5788
  int res;
5789
  int range_no;
unknown's avatar
merge  
unknown committed
5790
  ulong reclength= table->s->reclength;
5791
  const NdbOperation* op= m_current_multi_operation;
unknown's avatar
unknown committed
5792
  for (;multi_range_curr < m_multi_range_defined; multi_range_curr++)
unknown's avatar
unknown committed
5793
  {
5794
    if (multi_range_curr->range_flag & UNIQUE_RANGE)
5795
    {
5796
      if (op->getNdbError().code == 0)
5797
        goto found_next;
5798 5799 5800
      
      op= m_active_trans->getNextCompletedOperation(op);
      m_multi_range_result_ptr += reclength;
5801
      continue;
5802
    } 
5803
    else if (m_multi_cursor && !multi_range_sorted)
5804
    {
unknown's avatar
unknown committed
5805 5806
      DBUG_MULTI_RANGE(1);
      if ((res= fetch_next(m_multi_cursor)) == 0)
5807
      {
5808 5809 5810
        DBUG_MULTI_RANGE(2);
        range_no= m_multi_cursor->get_range_no();
        goto found;
5811 5812 5813
      } 
      else
      {
5814
        goto close_scan;
5815 5816
      }
    }
unknown's avatar
unknown committed
5817
    else if (m_multi_cursor && multi_range_sorted)
5818
    {
unknown's avatar
unknown committed
5819 5820
      if (m_active_cursor && (res= fetch_next(m_multi_cursor)))
      {
5821 5822
        DBUG_MULTI_RANGE(3);
        goto close_scan;
unknown's avatar
unknown committed
5823
      }
5824
      
5825
      range_no= m_multi_cursor->get_range_no();
5826
      uint current_range_no= multi_range_curr - m_multi_ranges;
unknown's avatar
unknown committed
5827
      if ((uint) range_no == current_range_no)
5828
      {
5829
        DBUG_MULTI_RANGE(4);
5830
        // return current row
5831
        goto found;
5832
      }
5833
      else if (range_no > (int)current_range_no)
5834
      {
5835 5836 5837 5838
        DBUG_MULTI_RANGE(5);
        // wait with current row
        m_active_cursor= 0;
        continue;
5839 5840 5841
      }
      else 
      {
5842 5843 5844
        DBUG_MULTI_RANGE(6);
        // First fetch from cursor
        DBUG_ASSERT(range_no == -1);
unknown's avatar
unknown committed
5845
        if ((res= m_multi_cursor->nextResult(true)))
5846 5847 5848 5849 5850
        {
          goto close_scan;
        }
        multi_range_curr--; // Will be increased in for-loop
        continue;
5851
      }
5852
    }
unknown's avatar
unknown committed
5853
    else /** m_multi_cursor == 0 */
5854
    {
unknown's avatar
unknown committed
5855
      DBUG_MULTI_RANGE(7);
5856 5857 5858 5859
      /**
       * Corresponds to range 5 in example in read_multi_range_first
       */
      (void)1;
5860
      continue;
5861
    }
5862
    
unknown's avatar
unknown committed
5863
    DBUG_ASSERT(FALSE); // Should only get here via goto's
5864 5865 5866
close_scan:
    if (res == 1)
    {
unknown's avatar
unknown committed
5867
      m_multi_cursor->close(FALSE, TRUE);
5868
      m_active_cursor= m_multi_cursor= 0;
unknown's avatar
unknown committed
5869
      DBUG_MULTI_RANGE(8);
5870 5871 5872 5873 5874 5875 5876
      continue;
    } 
    else 
    {
      DBUG_RETURN(ndb_err(m_active_trans));
    }
  }
5877
  
5878
  if (multi_range_curr == multi_range_end)
unknown's avatar
unknown committed
5879
    DBUG_RETURN(HA_ERR_END_OF_FILE);
5880
  
unknown's avatar
unknown committed
5881 5882 5883 5884
  /**
   * Read remaining ranges
   */
  DBUG_RETURN(read_multi_range_first(multi_range_found_p, 
5885 5886 5887 5888
                                     multi_range_curr,
                                     multi_range_end - multi_range_curr, 
                                     multi_range_sorted,
                                     multi_range_buffer));
5889 5890
  
found:
5891 5892 5893
  /**
   * Found a record belonging to a scan
   */
5894
  m_active_cursor= m_multi_cursor;
5895
  * multi_range_found_p= m_multi_ranges + range_no;
5896 5897
  memcpy(table->record[0], m_multi_range_cursor_result_ptr, reclength);
  setup_recattr(m_active_cursor->getFirstRecAttr());
5898 5899 5900
  unpack_record(table->record[0]);
  table->status= 0;     
  DBUG_RETURN(0);
5901
  
5902
found_next:
5903 5904 5905 5906
  /**
   * Found a record belonging to a pk/index op,
   *   copy result and move to next to prepare for next call
   */
5907
  * multi_range_found_p= multi_range_curr;
5908
  memcpy(table->record[0], m_multi_range_result_ptr, reclength);
5909
  setup_recattr(op->getFirstRecAttr());
5910
  unpack_record(table->record[0]);
5911 5912
  table->status= 0;
  
5913
  multi_range_curr++;
5914
  m_current_multi_operation= m_active_trans->getNextCompletedOperation(op);
5915 5916
  m_multi_range_result_ptr += reclength;
  DBUG_RETURN(0);
unknown's avatar
unknown committed
5917 5918
}

5919 5920 5921 5922 5923 5924 5925 5926
int
ha_ndbcluster::setup_recattr(const NdbRecAttr* curr)
{
  DBUG_ENTER("setup_recattr");

  Field **field, **end;
  NdbValue *value= m_value;
  
unknown's avatar
merge  
unknown committed
5927
  end= table->field + table->s->fields;
5928 5929 5930 5931 5932 5933
  
  for (field= table->field; field < end; field++, value++)
  {
    if ((* value).ptr)
    {
      DBUG_ASSERT(curr != 0);
5934 5935
      (* value).rec= curr;
      curr= curr->next();
5936 5937 5938
    }
  }
  
unknown's avatar
unknown committed
5939
  DBUG_RETURN(0);
5940 5941
}

unknown's avatar
Merge  
unknown committed
5942 5943
char*
ha_ndbcluster::update_table_comment(
5944 5945
                                /* out: table comment + additional */
        const char*     comment)/* in:  table comment defined by user */
unknown's avatar
Merge  
unknown committed
5946 5947
{
  uint length= strlen(comment);
unknown's avatar
unknown committed
5948
  if (length > 64000 - 3)
unknown's avatar
Merge  
unknown committed
5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974
  {
    return((char*)comment); /* string too long */
  }

  Ndb* ndb;
  if (!(ndb= get_ndb()))
  {
    return((char*)comment);
  }

  ndb->setDatabaseName(m_dbname);
  NDBDICT* dict= ndb->getDictionary();
  const NDBTAB* tab;
  if (!(tab= dict->getTable(m_tabname)))
  {
    return((char*)comment);
  }

  char *str;
  const char *fmt="%s%snumber_of_replicas: %d";
  const unsigned fmt_len_plus_extra= length + strlen(fmt);
  if ((str= my_malloc(fmt_len_plus_extra, MYF(0))) == NULL)
  {
    return (char*)comment;
  }

unknown's avatar
unknown committed
5975 5976 5977
  my_snprintf(str,fmt_len_plus_extra,fmt,comment,
              length > 0 ? " ":"",
              tab->getReplicaCount());
unknown's avatar
Merge  
unknown committed
5978 5979 5980 5981 5982
  return str;
}


// Utility thread main loop
5983
pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
unknown's avatar
Merge  
unknown committed
5984 5985
{
  THD *thd; /* needs to be first for thread_stack */
5986
  Ndb* ndb;
unknown's avatar
Merge  
unknown committed
5987 5988 5989 5990 5991 5992 5993 5994
  struct timespec abstime;

  my_thread_init();
  DBUG_ENTER("ndb_util_thread");
  DBUG_PRINT("enter", ("ndb_cache_check_time: %d", ndb_cache_check_time));

  thd= new THD; /* note that contructor of THD uses DBUG_ */
  THD_CHECK_SENTRY(thd);
5995
  ndb= new Ndb(g_ndb_cluster_connection, "");
unknown's avatar
Merge  
unknown committed
5996 5997 5998 5999 6000

  pthread_detach_this_thread();
  ndb_util_thread= pthread_self();

  thd->thread_stack= (char*)&thd; /* remember where our stack is */
6001
  if (thd->store_globals() && (ndb->init() != -1))
unknown's avatar
Merge  
unknown committed
6002 6003 6004
  {
    thd->cleanup();
    delete thd;
6005
    delete ndb;
unknown's avatar
Merge  
unknown committed
6006 6007 6008 6009
    DBUG_RETURN(NULL);
  }

  List<NDB_SHARE> util_open_tables;
6010
  set_timespec(abstime, 0);
unknown's avatar
Merge  
unknown committed
6011 6012 6013 6014
  for (;;)
  {

    pthread_mutex_lock(&LOCK_ndb_util_thread);
unknown's avatar
unknown committed
6015 6016 6017
    pthread_cond_timedwait(&COND_ndb_util_thread,
                           &LOCK_ndb_util_thread,
                           &abstime);
unknown's avatar
Merge  
unknown committed
6018 6019 6020 6021 6022 6023 6024 6025 6026 6027
    pthread_mutex_unlock(&LOCK_ndb_util_thread);

    DBUG_PRINT("ndb_util_thread", ("Started, ndb_cache_check_time: %d",
                                   ndb_cache_check_time));

    if (abort_loop)
      break; /* Shutting down server */

    if (ndb_cache_check_time == 0)
    {
6028 6029
      /* Wake up in 1 second to check if value has changed */
      set_timespec(abstime, 1);
unknown's avatar
Merge  
unknown committed
6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050
      continue;
    }

    /* Lock mutex and fill list with pointers to all open tables */
    NDB_SHARE *share;
    pthread_mutex_lock(&ndbcluster_mutex);
    for (uint i= 0; i < ndbcluster_open_tables.records; i++)
    {
      share= (NDB_SHARE *)hash_element(&ndbcluster_open_tables, i);
      share->use_count++; /* Make sure the table can't be closed */
      DBUG_PRINT("ndb_util_thread",
                 ("Found open table[%d]: %s, use_count: %d",
                  i, share->table_name, share->use_count));

      /* Store pointer to table */
      util_open_tables.push_back(share);
    }
    pthread_mutex_unlock(&ndbcluster_mutex);

    /* Iterate through the  open files list */
    List_iterator_fast<NDB_SHARE> it(util_open_tables);
6051
    while ((share= it++))
unknown's avatar
Merge  
unknown committed
6052 6053 6054 6055 6056 6057 6058 6059 6060 6061
    {
      /* Split tab- and dbname */
      char buf[FN_REFLEN];
      char *tabname, *db;
      uint length= dirname_length(share->table_name);
      tabname= share->table_name+length;
      memcpy(buf, share->table_name, length-1);
      buf[length-1]= 0;
      db= buf+dirname_length(buf);
      DBUG_PRINT("ndb_util_thread",
6062 6063
                 ("Fetching commit count for: %s",
                  share->table_name));
unknown's avatar
Merge  
unknown committed
6064 6065

      /* Contact NDB to get commit count for table */
6066 6067 6068 6069 6070 6071 6072 6073
      ndb->setDatabaseName(db);
      struct Ndb_statistics stat;

      uint lock;
      pthread_mutex_lock(&share->mutex);
      lock= share->commit_count_lock;
      pthread_mutex_unlock(&share->mutex);

unknown's avatar
unknown committed
6074
      if (ndb_get_table_statistics(ndb, tabname, &stat) == 0)
unknown's avatar
Merge  
unknown committed
6075 6076
      {
        DBUG_PRINT("ndb_util_thread",
6077 6078
                   ("Table: %s, commit_count: %llu, rows: %llu",
                    share->table_name, stat.commit_count, stat.row_count));
unknown's avatar
Merge  
unknown committed
6079 6080 6081 6082 6083 6084
      }
      else
      {
        DBUG_PRINT("ndb_util_thread",
                   ("Error: Could not get commit count for table %s",
                    share->table_name));
6085
        stat.commit_count= 0;
unknown's avatar
Merge  
unknown committed
6086
      }
6087 6088 6089 6090 6091 6092

      pthread_mutex_lock(&share->mutex);
      if (share->commit_count_lock == lock)
        share->commit_count= stat.commit_count;
      pthread_mutex_unlock(&share->mutex);

unknown's avatar
Merge  
unknown committed
6093 6094 6095 6096 6097 6098 6099
      /* Decrease the use count and possibly free share */
      free_share(share);
    }

    /* Clear the list of open tables */
    util_open_tables.empty();

6100 6101 6102 6103 6104 6105 6106 6107 6108
    /* Calculate new time to wake up */
    int secs= 0;
    int msecs= ndb_cache_check_time;

    struct timeval tick_time;
    gettimeofday(&tick_time, 0);
    abstime.tv_sec=  tick_time.tv_sec;
    abstime.tv_nsec= tick_time.tv_usec * 1000;

unknown's avatar
unknown committed
6109
    if (msecs >= 1000){
6110 6111 6112 6113 6114 6115 6116 6117 6118 6119
      secs=  msecs / 1000;
      msecs= msecs % 1000;
    }

    abstime.tv_sec+=  secs;
    abstime.tv_nsec+= msecs * 1000000;
    if (abstime.tv_nsec >= 1000000000) {
      abstime.tv_sec+=  1;
      abstime.tv_nsec-= 1000000000;
    }
unknown's avatar
Merge  
unknown committed
6120 6121 6122 6123
  }

  thd->cleanup();
  delete thd;
6124
  delete ndb;
unknown's avatar
Merge  
unknown committed
6125 6126 6127 6128 6129 6130
  DBUG_PRINT("exit", ("ndb_util_thread"));
  my_thread_end();
  pthread_exit(0);
  DBUG_RETURN(NULL);
}

unknown's avatar
unknown committed
6131 6132 6133
/*
  Condition pushdown
*/
6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150
/*
  Push a condition to ndbcluster storage engine for evaluation 
  during table   and index scans. The conditions will be stored on a stack
  for possibly storing several conditions. The stack can be popped
  by calling cond_pop, handler::extra(HA_EXTRA_RESET) (handler::reset())
  will clear the stack.
  The current implementation supports arbitrary AND/OR nested conditions
  with comparisons between columns and constants (including constant
  expressions and function calls) and the following comparison operators:
  =, !=, >, >=, <, <=, "is null", and "is not null".
  
  RETURN
    NULL The condition was supported and will be evaluated for each 
    row found during the scan
    cond The condition was not supported and all rows will be returned from
         the scan for evaluation (and thus not saved on stack)
*/
unknown's avatar
unknown committed
6151 6152 6153 6154 6155
const 
COND* 
ha_ndbcluster::cond_push(const COND *cond) 
{ 
  DBUG_ENTER("cond_push");
6156 6157 6158
  Ndb_cond_stack *ndb_cond = new Ndb_cond_stack();
  DBUG_EXECUTE("where",print_where((COND *)cond, m_tabname););
  if (m_cond_stack)
unknown's avatar
unknown committed
6159
    ndb_cond->next= m_cond_stack;
6160 6161 6162 6163 6164 6165
  else
    ndb_cond->next= NULL;
  m_cond_stack= ndb_cond;
  
  if (serialize_cond(cond, ndb_cond))
  {
unknown's avatar
unknown committed
6166
    DBUG_RETURN(NULL);
6167 6168 6169 6170
  }
  else
  {
    cond_pop();
unknown's avatar
unknown committed
6171
  }
unknown's avatar
unknown committed
6172 6173 6174
  DBUG_RETURN(cond); 
}

6175 6176 6177
/*
  Pop the top condition from the condition stack of the handler instance.
*/
unknown's avatar
unknown committed
6178 6179 6180 6181 6182 6183 6184 6185 6186
void 
ha_ndbcluster::cond_pop() 
{ 
  Ndb_cond_stack *ndb_cond_stack= m_cond_stack;  
  if (ndb_cond_stack)
  {
    m_cond_stack= ndb_cond_stack->next;
    delete ndb_cond_stack;
  }
unknown's avatar
unknown committed
6187
}
unknown's avatar
unknown committed
6188

6189 6190 6191
/*
  Clear the condition stack
*/
unknown's avatar
unknown committed
6192 6193 6194 6195 6196 6197 6198 6199 6200 6201
void
ha_ndbcluster::cond_clear()
{
  DBUG_ENTER("cond_clear");
  while (m_cond_stack)
    cond_pop();

  DBUG_VOID_RETURN;
}

6202 6203 6204 6205 6206 6207
/*
  Serialize the item tree into a linked list represented by Ndb_cond
  for fast generation of NbdScanFilter. Adds information such as
  position of fields that is not directly available in the Item tree.
  Also checks if condition is supported.
*/
unknown's avatar
unknown committed
6208 6209 6210 6211 6212
void ndb_serialize_cond(const Item *item, void *arg)
{
  Ndb_cond_traverse_context *context= (Ndb_cond_traverse_context *) arg;
  DBUG_ENTER("ndb_serialize_cond");  

unknown's avatar
unknown committed
6213 6214 6215 6216 6217
  // Check if we are skipping arguments to a function to be evaluated
  if (context->skip)
  {
    DBUG_PRINT("info", ("Skiping argument %d", context->skip));
    context->skip--;
unknown's avatar
unknown committed
6218 6219 6220
    switch (item->type()) {
    case Item::FUNC_ITEM:
    {
unknown's avatar
unknown committed
6221 6222 6223 6224
      Item_func *func_item= (Item_func *) item;
      context->skip+= func_item->argument_count();
      break;
    }
unknown's avatar
unknown committed
6225 6226 6227 6228 6229
    case Item::INT_ITEM:
    case Item::REAL_ITEM:
    case Item::STRING_ITEM:
    case Item::VARBIN_ITEM:
    case Item::DECIMAL_ITEM:
unknown's avatar
unknown committed
6230 6231
      break;
    default:
6232
      context->supported= FALSE;
unknown's avatar
unknown committed
6233 6234
      break;
    }
6235
    
unknown's avatar
unknown committed
6236 6237 6238
    DBUG_VOID_RETURN;
  }
  
6239
  if (context->supported)
unknown's avatar
unknown committed
6240
  {
6241 6242 6243 6244 6245 6246
    Ndb_rewrite_context *rewrite_context= context->rewrite_stack;
    const Item_func *func_item;
    // Check if we are rewriting some unsupported function call
    if (rewrite_context &&
        (func_item= rewrite_context->func_item) &&
        rewrite_context->count++ == 0)
unknown's avatar
unknown committed
6247
    {
unknown's avatar
unknown committed
6248 6249
      switch (func_item->functype()) {
      case Item_func::BETWEEN:
6250
        /*
6251 6252 6253 6254 6255 6256 6257
          Rewrite 
          <field>|<const> BETWEEN <const1>|<field1> AND <const2>|<field2>
          to <field>|<const> > <const1>|<field1> AND 
          <field>|<const> < <const2>|<field2>
          or actually in prefix format
          BEGIN(AND) GT(<field>|<const>, <const1>|<field1>), 
          LT(<field>|<const>, <const2>|<field2>), END()
6258
        */
unknown's avatar
unknown committed
6259 6260
      case Item_func::IN_FUNC:
      {
6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276
        /*
          Rewrite <field>|<const> IN(<const1>|<field1>, <const2>|<field2>,..)
          to <field>|<const> = <const1>|<field1> OR 
          <field> = <const2>|<field2> ...
          or actually in prefix format
          BEGIN(OR) EQ(<field>|<const>, <const1><field1>), 
          EQ(<field>|<const>, <const2>|<field2>), ... END()
          Each part of the disjunction is added for each call
          to ndb_serialize_cond and end of rewrite statement 
          is wrapped in end of ndb_serialize_cond
        */
        if (context->expecting(item->type()))
        {
          // This is the <field>|<const> item, save it in the rewrite context
          rewrite_context->left_hand_item= item;
          if (item->type() == Item::FUNC_ITEM)
6277
          {
6278 6279 6280
            Item_func *func_item= (Item_func *) item;
            if (func_item->functype() == Item_func::UNKNOWN_FUNC &&
                func_item->const_item())
6281
            {
6282 6283 6284
              // Skip any arguments since we will evaluate function instead
              DBUG_PRINT("info", ("Skip until end of arguments marker"));
              context->skip= func_item->argument_count();
6285 6286 6287
            }
            else
            {
6288 6289 6290 6291
              DBUG_PRINT("info", ("Found unsupported functional expression in BETWEEN|IN"));
              context->supported= FALSE;
              DBUG_VOID_RETURN;
              
6292 6293 6294
            }
          }
        }
6295 6296
        else
        {
6297 6298 6299
          // Non-supported BETWEEN|IN expression
          DBUG_PRINT("info", ("Found unexpected item of type %u in BETWEEN|IN",
                              item->type()));
6300
          context->supported= FALSE;
6301
          DBUG_VOID_RETURN;
6302
        }
6303 6304 6305 6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324
        break;
      }
      default:
        context->supported= FALSE;
        break;
      }
      DBUG_VOID_RETURN;
    }
    else
    {
      Ndb_cond_stack *ndb_stack= context->stack_ptr;
      Ndb_cond *prev_cond= context->cond_ptr;
      Ndb_cond *curr_cond= context->cond_ptr= new Ndb_cond();
      if (!ndb_stack->ndb_cond)
        ndb_stack->ndb_cond= curr_cond;
      curr_cond->prev= prev_cond;
      if (prev_cond) prev_cond->next= curr_cond;
    // Check if we are rewriting some unsupported function call
      if (context->rewrite_stack)
      {
        Ndb_rewrite_context *rewrite_context= context->rewrite_stack;
        const Item_func *func_item= rewrite_context->func_item;
unknown's avatar
unknown committed
6325 6326 6327 6328 6329 6330 6331 6332 6333 6334 6335 6336
        switch (func_item->functype()) {
        case Item_func::BETWEEN:
        {
          /*
            Rewrite 
            <field>|<const> BETWEEN <const1>|<field1> AND <const2>|<field2>
            to <field>|<const> > <const1>|<field1> AND 
            <field>|<const> < <const2>|<field2>
            or actually in prefix format
            BEGIN(AND) GT(<field>|<const>, <const1>|<field1>), 
            LT(<field>|<const>, <const2>|<field2>), END()
          */
6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355
          if (rewrite_context->count == 2)
          {
            // Lower limit of BETWEEN
            DBUG_PRINT("info", ("GE_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(Item_func::GE_FUNC, 2);
          }
          else if (rewrite_context->count == 3)
          {
            // Upper limit of BETWEEN
            DBUG_PRINT("info", ("LE_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(Item_func::LE_FUNC, 2);
          }
          else
          {
            // Illegal BETWEEN expression
            DBUG_PRINT("info", ("Illegal BETWEEN expression"));
            context->supported= FALSE;
            DBUG_VOID_RETURN;
          }
6356 6357
          break;
        }
unknown's avatar
unknown committed
6358 6359
        case Item_func::IN_FUNC:
        {
6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372
          /*
            Rewrite <field>|<const> IN(<const1>|<field1>, <const2>|<field2>,..)
            to <field>|<const> = <const1>|<field1> OR 
            <field> = <const2>|<field2> ...
            or actually in prefix format
            BEGIN(OR) EQ(<field>|<const>, <const1><field1>), 
            EQ(<field>|<const>, <const2>|<field2>), ... END()
            Each part of the disjunction is added for each call
            to ndb_serialize_cond and end of rewrite statement 
            is wrapped in end of ndb_serialize_cond
          */
          DBUG_PRINT("info", ("EQ_FUNC"));      
          curr_cond->ndb_item= new Ndb_item(Item_func::EQ_FUNC, 2);
6373 6374
          break;
        }
6375 6376
        default:
          context->supported= FALSE;
6377
        }
6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407
        // Handle left hand <field>|<const>
        context->rewrite_stack= NULL; // Disable rewrite mode
        context->expect_only(Item::FIELD_ITEM);
        context->expect_field_result(STRING_RESULT);
        context->expect_field_result(REAL_RESULT);
        context->expect_field_result(INT_RESULT);
        context->expect_field_result(DECIMAL_RESULT);
        context->expect(Item::INT_ITEM);
        context->expect(Item::STRING_ITEM);
        context->expect(Item::VARBIN_ITEM);
        context->expect(Item::FUNC_ITEM);
        ndb_serialize_cond(rewrite_context->left_hand_item, arg);
        context->skip= 0; // Any FUNC_ITEM expression has already been parsed
        context->rewrite_stack= rewrite_context; // Enable rewrite mode
        if (!context->supported)
          DBUG_VOID_RETURN;

        prev_cond= context->cond_ptr;
        curr_cond= context->cond_ptr= new Ndb_cond();
        prev_cond->next= curr_cond;
      }
      
      // Check for end of AND/OR expression
      if (!item)
      {
        // End marker for condition group
        DBUG_PRINT("info", ("End of condition group"));
        curr_cond->ndb_item= new Ndb_item(NDB_END_COND);
      }
      else
unknown's avatar
unknown committed
6408 6409 6410 6411
      {
        switch (item->type()) {
        case Item::FIELD_ITEM:
        {
6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 6430
          Item_field *field_item= (Item_field *) item;
          Field *field= field_item->field;
          enum_field_types type= field->type();
          /*
            Check that the field is part of the table of the handler
            instance and that we expect a field with of this result type.
          */
          if (context->table == field->table)
          {       
            const NDBTAB *tab= (const NDBTAB *) context->ndb_table;
            DBUG_PRINT("info", ("FIELD_ITEM"));
            DBUG_PRINT("info", ("table %s", tab->getName()));
            DBUG_PRINT("info", ("column %s", field->field_name));
            DBUG_PRINT("info", ("result type %d", field->result_type()));
            
            // Check that we are expecting a field and with the correct
            // result type
            if (context->expecting(Item::FIELD_ITEM) &&
                (context->expecting_field_result(field->result_type()) ||
unknown's avatar
unknown committed
6431
                 // Date and year can be written as string or int
6432 6433 6434 6435
                 ((type == MYSQL_TYPE_TIME ||
                   type == MYSQL_TYPE_DATE || 
                   type == MYSQL_TYPE_YEAR ||
                   type == MYSQL_TYPE_DATETIME)
unknown's avatar
unknown committed
6436 6437 6438
                  ? (context->expecting_field_result(STRING_RESULT) ||
                     context->expecting_field_result(INT_RESULT))
                  : true)) &&
6439 6440 6441 6442 6443 6444 6445 6446 6447
                // Bit fields no yet supported in scan filter
                type != MYSQL_TYPE_BIT)
            {
              const NDBCOL *col= tab->getColumn(field->field_name);
              DBUG_ASSERT(col);
              curr_cond->ndb_item= new Ndb_item(field, col->getColumnNo());
              context->dont_expect(Item::FIELD_ITEM);
              context->expect_no_field_result();
              if (context->expect_mask)
6448
              {
6449 6450 6451 6452 6453 6454 6455 6456 6457 6458
                // We have not seen second argument yet
                if (type == MYSQL_TYPE_TIME ||
                    type == MYSQL_TYPE_DATE || 
                    type == MYSQL_TYPE_YEAR ||
                    type == MYSQL_TYPE_DATETIME)
                {
                  context->expect_only(Item::STRING_ITEM);
                  context->expect(Item::INT_ITEM);
                }
                else
unknown's avatar
unknown committed
6459 6460
                  switch (field->result_type()) {
                  case STRING_RESULT:
6461 6462 6463 6464 6465
                    // Expect char string or binary string
                    context->expect_only(Item::STRING_ITEM);
                    context->expect(Item::VARBIN_ITEM);
                    context->expect_collation(field_item->collation.collation);
                    break;
unknown's avatar
unknown committed
6466
                  case REAL_RESULT:
6467 6468
                    context->expect_only(Item::REAL_ITEM);
                    context->expect(Item::DECIMAL_ITEM);
6469
                    context->expect(Item::INT_ITEM);
6470
                    break;
unknown's avatar
unknown committed
6471
                  case INT_RESULT:
6472 6473 6474
                    context->expect_only(Item::INT_ITEM);
                    context->expect(Item::VARBIN_ITEM);
                    break;
unknown's avatar
unknown committed
6475
                  case DECIMAL_RESULT:
6476 6477
                    context->expect_only(Item::DECIMAL_ITEM);
                    context->expect(Item::REAL_ITEM);
6478
                    context->expect(Item::INT_ITEM);
6479 6480 6481 6482
                    break;
                  default:
                    break;
                  }    
6483 6484
              }
              else
6485 6486 6487 6488
              {
                // Expect another logical expression
                context->expect_only(Item::FUNC_ITEM);
                context->expect(Item::COND_ITEM);
6489 6490 6491 6492 6493 6494 6495
                // Check that field and string constant collations are the same
                if ((field->result_type() == STRING_RESULT) &&
                    !context->expecting_collation(item->collation.collation)
                    && type != MYSQL_TYPE_TIME
                    && type != MYSQL_TYPE_DATE
                    && type != MYSQL_TYPE_YEAR
                    && type != MYSQL_TYPE_DATETIME)
6496
                {
unknown's avatar
unknown committed
6497
                  DBUG_PRINT("info", ("Found non-matching collation %s",  
6498 6499
                                      item->collation.collation->name)); 
                  context->supported= FALSE;                
6500 6501
                }
              }
6502 6503
              break;
            }
6504 6505
            else
            {
unknown's avatar
unknown committed
6506 6507
              DBUG_PRINT("info", ("Was not expecting field of type %u(%u)",
                                  field->result_type(), type));
6508
              context->supported= FALSE;
6509
            }
6510
          }
6511
          else
6512 6513 6514 6515
          {
            DBUG_PRINT("info", ("Was not expecting field from table %s(%s)",
                                context->table->s->table_name, 
                                field->table->s->table_name));
6516
            context->supported= FALSE;
6517
          }
6518 6519
          break;
        }
unknown's avatar
unknown committed
6520 6521
        case Item::FUNC_ITEM:
        {
6522 6523 6524 6525 6526 6527
          Item_func *func_item= (Item_func *) item;
          // Check that we expect a function or functional expression here
          if (context->expecting(Item::FUNC_ITEM) || 
              func_item->functype() == Item_func::UNKNOWN_FUNC)
            context->expect_nothing();
          else
6528
          {
6529 6530 6531
            // Did not expect function here
            context->supported= FALSE;
            break;
6532
          }
6533
          
unknown's avatar
unknown committed
6534 6535 6536
          switch (func_item->functype()) {
          case Item_func::EQ_FUNC:
          {
6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550
            DBUG_PRINT("info", ("EQ_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(), 
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::REAL_ITEM);
            context->expect(Item::DECIMAL_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
6551
          }
unknown's avatar
unknown committed
6552 6553
          case Item_func::NE_FUNC:
          {
6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567
            DBUG_PRINT("info", ("NE_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::REAL_ITEM);
            context->expect(Item::DECIMAL_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
6568
          }
unknown's avatar
unknown committed
6569 6570
          case Item_func::LT_FUNC:
          {
6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585
            DBUG_PRINT("info", ("LT_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::REAL_ITEM);
            context->expect(Item::DECIMAL_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
6586 6587
          case Item_func::LE_FUNC:
          {
6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 6602
            DBUG_PRINT("info", ("LE_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::REAL_ITEM);
            context->expect(Item::DECIMAL_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
6603 6604
          case Item_func::GE_FUNC:
          {
6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619
            DBUG_PRINT("info", ("GE_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::REAL_ITEM);
            context->expect(Item::DECIMAL_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
6620 6621
          case Item_func::GT_FUNC:
          {
6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636
            DBUG_PRINT("info", ("GT_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::REAL_ITEM);
            context->expect(Item::DECIMAL_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
6637 6638
          case Item_func::LIKE_FUNC:
          {
6639 6640 6641 6642 6643 6644 6645 6646 6647
            DBUG_PRINT("info", ("LIKE_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect(Item::FUNC_ITEM);
            break;
          }
unknown's avatar
unknown committed
6648 6649
          case Item_func::ISNULL_FUNC:
          {
6650 6651 6652 6653 6654 6655 6656 6657 6658 6659
            DBUG_PRINT("info", ("ISNULL_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
6660 6661
          case Item_func::ISNOTNULL_FUNC:
          {
6662 6663 6664 6665 6666 6667 6668 6669 6670 6671
            DBUG_PRINT("info", ("ISNOTNULL_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);     
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
6672 6673
          case Item_func::NOT_FUNC:
          {
6674 6675 6676 6677
            DBUG_PRINT("info", ("NOT_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);     
            context->expect(Item::FUNC_ITEM);
6678
            context->expect(Item::COND_ITEM);
6679
            break;
6680
          }
unknown's avatar
unknown committed
6681 6682
          case Item_func::BETWEEN:
          {
6683
            DBUG_PRINT("info", ("BETWEEN, rewriting using AND"));
6684
            Item_func_between *between_func= (Item_func_between *) func_item;
6685 6686 6687 6688
            Ndb_rewrite_context *rewrite_context= 
              new Ndb_rewrite_context(func_item);
            rewrite_context->next= context->rewrite_stack;
            context->rewrite_stack= rewrite_context;
6689 6690 6691 6692 6693 6694 6695 6696 6697
            if (between_func->negated)
            {
              DBUG_PRINT("info", ("NOT_FUNC"));
              curr_cond->ndb_item= new Ndb_item(Item_func::NOT_FUNC, 1);
              prev_cond= curr_cond;
              curr_cond= context->cond_ptr= new Ndb_cond();
              curr_cond->prev= prev_cond;
              prev_cond->next= curr_cond;
            }
6698
            DBUG_PRINT("info", ("COND_AND_FUNC"));
6699 6700 6701
            curr_cond->ndb_item= 
              new Ndb_item(Item_func::COND_AND_FUNC, 
                           func_item->argument_count() - 1);
6702
            context->expect_only(Item::FIELD_ITEM);
6703 6704 6705 6706 6707
            context->expect(Item::INT_ITEM);
            context->expect(Item::STRING_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FUNC_ITEM);
            break;
6708
          }
unknown's avatar
unknown committed
6709 6710
          case Item_func::IN_FUNC:
          {
6711
            DBUG_PRINT("info", ("IN_FUNC, rewriting using OR"));
6712
            Item_func_in *in_func= (Item_func_in *) func_item;
6713 6714 6715 6716
            Ndb_rewrite_context *rewrite_context= 
              new Ndb_rewrite_context(func_item);
            rewrite_context->next= context->rewrite_stack;
            context->rewrite_stack= rewrite_context;
6717 6718 6719 6720 6721 6722 6723 6724 6725
            if (in_func->negated)
            {
              DBUG_PRINT("info", ("NOT_FUNC"));
              curr_cond->ndb_item= new Ndb_item(Item_func::NOT_FUNC, 1);
              prev_cond= curr_cond;
              curr_cond= context->cond_ptr= new Ndb_cond();
              curr_cond->prev= prev_cond;
              prev_cond->next= curr_cond;
            }
6726 6727 6728 6729 6730 6731 6732 6733 6734
            DBUG_PRINT("info", ("COND_OR_FUNC"));
            curr_cond->ndb_item= new Ndb_item(Item_func::COND_OR_FUNC, 
                                              func_item->argument_count() - 1);
            context->expect_only(Item::FIELD_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::STRING_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FUNC_ITEM);
            break;
6735
          }
unknown's avatar
unknown committed
6736 6737
          case Item_func::UNKNOWN_FUNC:
          {
6738 6739 6740 6741
            DBUG_PRINT("info", ("UNKNOWN_FUNC %s", 
                                func_item->const_item()?"const":""));  
            DBUG_PRINT("info", ("result type %d", func_item->result_type()));
            if (func_item->const_item())
unknown's avatar
unknown committed
6742 6743 6744 6745
            {
              switch (func_item->result_type()) {
              case STRING_RESULT:
              {
6746 6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770 6771 6772 6773
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::STRING_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item); 
                if (context->expect_field_result_mask)
                {
                  // We have not seen the field argument yet
                  context->expect_only(Item::FIELD_ITEM);
                  context->expect_only_field_result(STRING_RESULT);
                  context->expect_collation(func_item->collation.collation);
                }
                else
                {
                  // Expect another logical expression
                  context->expect_only(Item::FUNC_ITEM);
                  context->expect(Item::COND_ITEM);
                  // Check that string result have correct collation
                  if (!context->expecting_collation(item->collation.collation))
                  {
                    DBUG_PRINT("info", ("Found non-matching collation %s",  
                                        item->collation.collation->name));
                    context->supported= FALSE;
                  }
                }
                // Skip any arguments since we will evaluate function instead
                DBUG_PRINT("info", ("Skip until end of arguments marker"));
                context->skip= func_item->argument_count();
                break;
              }
unknown's avatar
unknown committed
6774 6775
              case REAL_RESULT:
              {
6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::REAL_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
                if (context->expect_field_result_mask) 
                {
                  // We have not seen the field argument yet
                  context->expect_only(Item::FIELD_ITEM);
                  context->expect_only_field_result(REAL_RESULT);
                }
                else
                {
                  // Expect another logical expression
                  context->expect_only(Item::FUNC_ITEM);
                  context->expect(Item::COND_ITEM);
                }
                
                // Skip any arguments since we will evaluate function instead
                DBUG_PRINT("info", ("Skip until end of arguments marker"));
                context->skip= func_item->argument_count();
                break;
              }
unknown's avatar
unknown committed
6797 6798
              case INT_RESULT:
              {
6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 6818 6819
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::INT_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
                if (context->expect_field_result_mask) 
                {
                  // We have not seen the field argument yet
                  context->expect_only(Item::FIELD_ITEM);
                  context->expect_only_field_result(INT_RESULT);
                }
                else
                {
                  // Expect another logical expression
                  context->expect_only(Item::FUNC_ITEM);
                  context->expect(Item::COND_ITEM);
                }
                
                // Skip any arguments since we will evaluate function instead
                DBUG_PRINT("info", ("Skip until end of arguments marker"));
                context->skip= func_item->argument_count();
                break;
              }
unknown's avatar
unknown committed
6820 6821
              case DECIMAL_RESULT:
              {
6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::DECIMAL_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
                if (context->expect_field_result_mask) 
                {
                  // We have not seen the field argument yet
                  context->expect_only(Item::FIELD_ITEM);
                  context->expect_only_field_result(DECIMAL_RESULT);
                }
                else
                {
                  // Expect another logical expression
                  context->expect_only(Item::FUNC_ITEM);
                  context->expect(Item::COND_ITEM);
                }
                // Skip any arguments since we will evaluate function instead
                DBUG_PRINT("info", ("Skip until end of arguments marker"));
                context->skip= func_item->argument_count();
                break;
              }
              default:
                break;
              }
unknown's avatar
unknown committed
6845
            }
6846 6847 6848 6849 6850
            else
              // Function does not return constant expression
              context->supported= FALSE;
            break;
          }
unknown's avatar
unknown committed
6851 6852
          default:
          {
6853 6854 6855
            DBUG_PRINT("info", ("Found func_item of type %d", 
                                func_item->functype()));
            context->supported= FALSE;
6856
          }
6857 6858
          }
          break;
6859
        }
unknown's avatar
unknown committed
6860
        case Item::STRING_ITEM:
6861 6862 6863
          DBUG_PRINT("info", ("STRING_ITEM")); 
          if (context->expecting(Item::STRING_ITEM)) 
          {
6864
#ifndef DBUG_OFF
6865 6866 6867 6868 6869 6870
            char buff[256];
            String str(buff,(uint32) sizeof(buff), system_charset_info);
            str.length(0);
            Item_string *string_item= (Item_string *) item;
            DBUG_PRINT("info", ("value \"%s\"", 
                                string_item->val_str(&str)->ptr()));
6871
#endif
6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::STRING_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);      
            if (context->expect_field_result_mask)
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(STRING_RESULT);
              context->expect_collation(item->collation.collation);
            }
            else 
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
              // Check that we are comparing with a field with same collation
              if (!context->expecting_collation(item->collation.collation))
              {
                DBUG_PRINT("info", ("Found non-matching collation %s",  
                                    item->collation.collation->name));
                context->supported= FALSE;
              }
            }
          }
          else
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
6899
        case Item::INT_ITEM:
6900 6901
          DBUG_PRINT("info", ("INT_ITEM"));
          if (context->expecting(Item::INT_ITEM)) 
6902
          {
6903 6904 6905 6906 6907 6908 6909 6910 6911 6912
            Item_int *int_item= (Item_int *) item;      
            DBUG_PRINT("info", ("value %d", int_item->value));
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::INT_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
            if (context->expect_field_result_mask) 
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(INT_RESULT);
6913 6914
              context->expect_field_result(REAL_RESULT);
              context->expect_field_result(DECIMAL_RESULT);
6915 6916 6917 6918 6919 6920 6921
            }
            else
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
            }
6922 6923
          }
          else
6924 6925
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
6926
        case Item::REAL_ITEM:
6927 6928
          DBUG_PRINT("info", ("REAL_ITEM %s"));
          if (context->expecting(Item::REAL_ITEM)) 
6929
          {
6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946
            Item_float *float_item= (Item_float *) item;      
            DBUG_PRINT("info", ("value %f", float_item->value));
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::REAL_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
            if (context->expect_field_result_mask) 
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(REAL_RESULT);
            }
            else
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
            }
6947
          }
6948 6949 6950
          else
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
6951
        case Item::VARBIN_ITEM:
6952 6953
          DBUG_PRINT("info", ("VARBIN_ITEM"));
          if (context->expecting(Item::VARBIN_ITEM)) 
6954
          {
6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::VARBIN_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);      
            if (context->expect_field_result_mask)
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(STRING_RESULT);
            }
            else
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
            }
6970 6971
          }
          else
6972 6973
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
6974
        case Item::DECIMAL_ITEM:
6975 6976
          DBUG_PRINT("info", ("DECIMAL_ITEM %s"));
          if (context->expecting(Item::DECIMAL_ITEM)) 
6977
          {
6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995
            Item_decimal *decimal_item= (Item_decimal *) item;      
            DBUG_PRINT("info", ("value %f", decimal_item->val_real()));
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::DECIMAL_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
            if (context->expect_field_result_mask) 
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(REAL_RESULT);
              context->expect_field_result(DECIMAL_RESULT);
            }
            else
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
            }
6996
          }
6997 6998 6999
          else
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
7000 7001
        case Item::COND_ITEM:
        {
7002 7003 7004
          Item_cond *cond_item= (Item_cond *) item;
          
          if (context->expecting(Item::COND_ITEM))
unknown's avatar
unknown committed
7005 7006 7007
          {
            switch (cond_item->functype()) {
            case Item_func::COND_AND_FUNC:
7008 7009 7010 7011
              DBUG_PRINT("info", ("COND_AND_FUNC"));
              curr_cond->ndb_item= new Ndb_item(cond_item->functype(),
                                                cond_item);      
              break;
unknown's avatar
unknown committed
7012
            case Item_func::COND_OR_FUNC:
7013 7014 7015 7016 7017 7018 7019 7020 7021
              DBUG_PRINT("info", ("COND_OR_FUNC"));
              curr_cond->ndb_item= new Ndb_item(cond_item->functype(),
                                                cond_item);      
              break;
            default:
              DBUG_PRINT("info", ("COND_ITEM %d", cond_item->functype()));
              context->supported= FALSE;
              break;
            }
unknown's avatar
unknown committed
7022
          }
7023
          else
unknown's avatar
unknown committed
7024 7025
          {
            /* Did not expect condition */
7026
            context->supported= FALSE;          
unknown's avatar
unknown committed
7027
          }
7028
          break;
7029
        }
unknown's avatar
unknown committed
7030 7031
        default:
        {
7032
          DBUG_PRINT("info", ("Found item of type %d", item->type()));
7033
          context->supported= FALSE;
7034 7035
        }
        }
unknown's avatar
unknown committed
7036
      }
7037 7038 7039 7040 7041 7042 7043 7044 7045 7046
      if (context->supported && context->rewrite_stack)
      {
        Ndb_rewrite_context *rewrite_context= context->rewrite_stack;
        if (rewrite_context->count == 
            rewrite_context->func_item->argument_count())
        {
          // Rewrite is done, wrap an END() at the en
          DBUG_PRINT("info", ("End of condition group"));
          prev_cond= curr_cond;
          curr_cond= context->cond_ptr= new Ndb_cond();
7047
          curr_cond->prev= prev_cond;
7048 7049 7050 7051 7052
          prev_cond->next= curr_cond;
          curr_cond->ndb_item= new Ndb_item(NDB_END_COND);
          // Pop rewrite stack
          context->rewrite_stack= context->rewrite_stack->next;
        }
unknown's avatar
unknown committed
7053
      }
7054
    }
unknown's avatar
unknown committed
7055
  }
7056
 
unknown's avatar
unknown committed
7057 7058 7059 7060 7061 7062 7063 7064
  DBUG_VOID_RETURN;
}

bool
ha_ndbcluster::serialize_cond(const COND *cond, Ndb_cond_stack *ndb_cond)
{
  DBUG_ENTER("serialize_cond");
  Item *item= (Item *) cond;
7065
  Ndb_cond_traverse_context context(table, (void *)m_table, ndb_cond);
7066 7067 7068
  // Expect a logical expression
  context.expect(Item::FUNC_ITEM);
  context.expect(Item::COND_ITEM);
unknown's avatar
unknown committed
7069
  item->traverse_cond(&ndb_serialize_cond, (void *) &context, Item::PREFIX);
7070
  DBUG_PRINT("info", ("The pushed condition is %ssupported", (context.supported)?"":"not "));
unknown's avatar
unknown committed
7071

7072
  DBUG_RETURN(context.supported);
unknown's avatar
unknown committed
7073 7074
}

7075 7076
int
ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, 
7077 7078
                                           NdbScanFilter *filter,
                                           bool negated)
unknown's avatar
unknown committed
7079 7080
{
  DBUG_ENTER("build_scan_filter_predicate");  
unknown's avatar
unknown committed
7081 7082 7083
  switch (cond->ndb_item->type) {
  case NDB_FUNCTION:
  {
unknown's avatar
unknown committed
7084 7085 7086
    if (!cond->next)
      break;
    Ndb_item *a= cond->next->ndb_item;
7087
    Ndb_item *b, *field, *value= NULL;
7088 7089
    LINT_INIT(field);

unknown's avatar
unknown committed
7090 7091
    switch (cond->ndb_item->argument_count()) {
    case 1:
7092 7093 7094
      field= 
        (a->type == NDB_FIELD)? a : NULL;
      break;
unknown's avatar
unknown committed
7095
    case 2:
unknown's avatar
unknown committed
7096
      if (!cond->next->next)
7097
        break;
7098 7099
      b= cond->next->next->ndb_item;
      value= 
7100 7101 7102
        (a->type == NDB_VALUE)? a
        : (b->type == NDB_VALUE)? b
        : NULL;
7103
      field= 
7104 7105 7106
        (a->type == NDB_FIELD)? a
        : (b->type == NDB_FIELD)? b
        : NULL;
7107
      break;
unknown's avatar
unknown committed
7108
    default:
7109 7110
      break;
    }
unknown's avatar
unknown committed
7111 7112 7113
    switch ((negated) ? 
            Ndb_item::negate(cond->ndb_item->qualification.function_type)
            : cond->ndb_item->qualification.function_type) {
7114
    case NDB_EQ_FUNC:
7115
    {
unknown's avatar
unknown committed
7116
      if (!value || !field) break;
unknown's avatar
unknown committed
7117 7118
      // Save value in right format for the field type
      value->save_in_field(field);
7119
      DBUG_PRINT("info", ("Generating EQ filter"));
7120
      if (filter->cmp(NdbScanFilter::COND_EQ, 
7121 7122 7123 7124
                      field->get_field_no(),
                      field->get_val(),
                      field->pack_length()) == -1)
        DBUG_RETURN(1);
7125 7126
      cond= cond->next->next->next;
      DBUG_RETURN(0);
unknown's avatar
unknown committed
7127
    }
7128
    case NDB_NE_FUNC:
unknown's avatar
unknown committed
7129
    {
unknown's avatar
unknown committed
7130
      if (!value || !field) break;
unknown's avatar
unknown committed
7131 7132
      // Save value in right format for the field type
      value->save_in_field(field);
7133
      DBUG_PRINT("info", ("Generating NE filter"));
7134
      if (filter->cmp(NdbScanFilter::COND_NE, 
7135 7136 7137 7138
                      field->get_field_no(),
                      field->get_val(),
                      field->pack_length()) == -1)
        DBUG_RETURN(1);
7139 7140
      cond= cond->next->next->next;
      DBUG_RETURN(0);
unknown's avatar
unknown committed
7141
    }
7142
    case NDB_LT_FUNC:
unknown's avatar
unknown committed
7143
    {
unknown's avatar
unknown committed
7144
      if (!value || !field) break;
unknown's avatar
unknown committed
7145 7146
      // Save value in right format for the field type
      value->save_in_field(field);
unknown's avatar
unknown committed
7147
      if (a == field)
7148
      {
7149 7150 7151 7152 7153 7154
        DBUG_PRINT("info", ("Generating LT filter")); 
        if (filter->cmp(NdbScanFilter::COND_LT, 
                        field->get_field_no(),
                        field->get_val(),
                        field->pack_length()) == -1)
          DBUG_RETURN(1);
7155
      }
unknown's avatar
unknown committed
7156
      else
7157
      {
7158 7159 7160 7161 7162 7163
        DBUG_PRINT("info", ("Generating GT filter")); 
        if (filter->cmp(NdbScanFilter::COND_GT, 
                        field->get_field_no(),
                        field->get_val(),
                        field->pack_length()) == -1)
          DBUG_RETURN(1);
7164
      }
7165 7166
      cond= cond->next->next->next;
      DBUG_RETURN(0);
unknown's avatar
unknown committed
7167
    }
7168
    case NDB_LE_FUNC:
unknown's avatar
unknown committed
7169
    {
unknown's avatar
unknown committed
7170
      if (!value || !field) break;
unknown's avatar
unknown committed
7171 7172
      // Save value in right format for the field type
      value->save_in_field(field);
unknown's avatar
unknown committed
7173
      if (a == field)
7174
      {
7175 7176 7177 7178 7179 7180
        DBUG_PRINT("info", ("Generating LE filter")); 
        if (filter->cmp(NdbScanFilter::COND_LE, 
                        field->get_field_no(),
                        field->get_val(),
                        field->pack_length()) == -1)
          DBUG_RETURN(1);       
7181
      }
unknown's avatar
unknown committed
7182
      else
7183
      {
7184 7185 7186 7187 7188 7189
        DBUG_PRINT("info", ("Generating GE filter")); 
        if (filter->cmp(NdbScanFilter::COND_GE, 
                        field->get_field_no(),
                        field->get_val(),
                        field->pack_length()) == -1)
          DBUG_RETURN(1);
7190
      }
7191 7192
      cond= cond->next->next->next;
      DBUG_RETURN(0);
unknown's avatar
unknown committed
7193
    }
7194
    case NDB_GE_FUNC:
unknown's avatar
unknown committed
7195
    {
unknown's avatar
unknown committed
7196
      if (!value || !field) break;
unknown's avatar
unknown committed
7197 7198
      // Save value in right format for the field type
      value->save_in_field(field);
unknown's avatar
unknown committed
7199
      if (a == field)
7200
      {
7201 7202 7203 7204 7205 7206
        DBUG_PRINT("info", ("Generating GE filter")); 
        if (filter->cmp(NdbScanFilter::COND_GE, 
                        field->get_field_no(),
                        field->get_val(),
                        field->pack_length()) == -1)
          DBUG_RETURN(1);
7207
      }
unknown's avatar
unknown committed
7208
      else
7209
      {
7210 7211 7212 7213 7214 7215
        DBUG_PRINT("info", ("Generating LE filter")); 
        if (filter->cmp(NdbScanFilter::COND_LE, 
                        field->get_field_no(),
                        field->get_val(),
                        field->pack_length()) == -1)
          DBUG_RETURN(1);
7216
      }
7217 7218
      cond= cond->next->next->next;
      DBUG_RETURN(0);
unknown's avatar
unknown committed
7219
    }
7220
    case NDB_GT_FUNC:
unknown's avatar
unknown committed
7221
    {
unknown's avatar
unknown committed
7222
      if (!value || !field) break;
unknown's avatar
unknown committed
7223 7224
      // Save value in right format for the field type
      value->save_in_field(field);
unknown's avatar
unknown committed
7225
      if (a == field)
7226
      {
7227 7228 7229 7230 7231 7232
        DBUG_PRINT("info", ("Generating GT filter"));
        if (filter->cmp(NdbScanFilter::COND_GT, 
                        field->get_field_no(),
                        field->get_val(),
                        field->pack_length()) == -1)
          DBUG_RETURN(1);
7233
      }
unknown's avatar
unknown committed
7234
      else
7235
      {
7236 7237 7238 7239 7240 7241
        DBUG_PRINT("info", ("Generating LT filter"));
        if (filter->cmp(NdbScanFilter::COND_LT, 
                        field->get_field_no(),
                        field->get_val(),
                        field->pack_length()) == -1)
          DBUG_RETURN(1);
7242
      }
7243 7244
      cond= cond->next->next->next;
      DBUG_RETURN(0);
unknown's avatar
unknown committed
7245
    }
7246
    case NDB_LIKE_FUNC:
unknown's avatar
unknown committed
7247
    {
unknown's avatar
unknown committed
7248
      if (!value || !field) break;
7249 7250 7251
      if ((value->qualification.value_type != Item::STRING_ITEM) &&
          (value->qualification.value_type != Item::VARBIN_ITEM))
          break;
unknown's avatar
unknown committed
7252 7253 7254
      // Save value in right format for the field type
      value->save_in_field(field);
      DBUG_PRINT("info", ("Generating LIKE filter: like(%d,%s,%d)", 
7255 7256 7257 7258
                          field->get_field_no(), value->get_val(), 
                          value->pack_length()));
      if (filter->cmp(NdbScanFilter::COND_LIKE, 
                      field->get_field_no(),
7259 7260
                      value->get_val(),
                      value->pack_length()) == -1)
7261
        DBUG_RETURN(1);
7262 7263
      cond= cond->next->next->next;
      DBUG_RETURN(0);
unknown's avatar
unknown committed
7264
    }
7265 7266 7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279 7280 7281 7282 7283 7284
    case NDB_NOTLIKE_FUNC:
    {
      if (!value || !field) break;
      if ((value->qualification.value_type != Item::STRING_ITEM) &&
          (value->qualification.value_type != Item::VARBIN_ITEM))
          break;
      // Save value in right format for the field type
      value->save_in_field(field);
      DBUG_PRINT("info", ("Generating NOTLIKE filter: notlike(%d,%s,%d)", 
                          field->get_field_no(), value->get_val(), 
                          value->pack_length()));
      if (filter->cmp(NdbScanFilter::COND_NOT_LIKE, 
                      field->get_field_no(),
                      value->get_val(),
                      value->pack_length()) == -1)
        DBUG_RETURN(1);
      cond= cond->next->next->next;
      DBUG_RETURN(0);
    }
    case NDB_ISNULL_FUNC:
7285 7286 7287 7288 7289
      if (!field)
        break;
      DBUG_PRINT("info", ("Generating ISNULL filter"));
      if (filter->isnull(field->get_field_no()) == -1)
        DBUG_RETURN(1);
7290 7291
      cond= cond->next->next;
      DBUG_RETURN(0);
7292
    case NDB_ISNOTNULL_FUNC:
unknown's avatar
unknown committed
7293
    {
7294 7295 7296 7297 7298
      if (!field)
        break;
      DBUG_PRINT("info", ("Generating ISNOTNULL filter"));
      if (filter->isnotnull(field->get_field_no()) == -1)
        DBUG_RETURN(1);         
7299 7300
      cond= cond->next->next;
      DBUG_RETURN(0);
unknown's avatar
unknown committed
7301 7302 7303 7304 7305 7306 7307 7308 7309 7310
    }
    default:
      break;
    }
    break;
  }
  default:
    break;
  }
  DBUG_PRINT("info", ("Found illegal condition"));
7311
  DBUG_RETURN(1);
unknown's avatar
unknown committed
7312 7313
}

7314
int
7315
ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter)
unknown's avatar
unknown committed
7316
{
7317
  uint level=0;
unknown's avatar
unknown committed
7318
  bool negated= FALSE;
unknown's avatar
unknown committed
7319
  DBUG_ENTER("build_scan_filter_group");
unknown's avatar
unknown committed
7320

7321 7322
  do
  {
unknown's avatar
unknown committed
7323 7324 7325 7326 7327 7328
    if (!cond)
      DBUG_RETURN(1);
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
    {
      switch (cond->ndb_item->qualification.function_type) {
7329
      case NDB_COND_AND_FUNC:
unknown's avatar
unknown committed
7330
      {
7331 7332 7333 7334 7335
        level++;
        DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NAND":"AND",
                            level));
        if ((negated) ? filter->begin(NdbScanFilter::NAND)
            : filter->begin(NdbScanFilter::AND) == -1)
7336
          DBUG_RETURN(1);
unknown's avatar
unknown committed
7337
        negated= FALSE;
7338 7339 7340
        cond= cond->next;
        break;
      }
7341
      case NDB_COND_OR_FUNC:
unknown's avatar
unknown committed
7342
      {
7343 7344 7345 7346 7347 7348
        level++;
        DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NOR":"OR",
                            level));
        if ((negated) ? filter->begin(NdbScanFilter::NOR)
            : filter->begin(NdbScanFilter::OR) == -1)
          DBUG_RETURN(1);
unknown's avatar
unknown committed
7349
        negated= FALSE;
7350 7351 7352
        cond= cond->next;
        break;
      }
7353
      case NDB_NOT_FUNC:
unknown's avatar
unknown committed
7354
      {
7355
        DBUG_PRINT("info", ("Generating negated query"));
7356
        cond= cond->next;
unknown's avatar
unknown committed
7357
        negated= TRUE;
7358 7359 7360 7361
        break;
      }
      default:
        if (build_scan_filter_predicate(cond, filter, negated))
7362
          DBUG_RETURN(1);
unknown's avatar
unknown committed
7363
        negated= FALSE;
7364 7365 7366
        break;
      }
      break;
unknown's avatar
unknown committed
7367 7368
    }
    case NDB_END_COND:
7369 7370
      DBUG_PRINT("info", ("End of group %u", level));
      level--;
7371 7372
      if (cond) cond= cond->next;
      if (filter->end() == -1)
7373
        DBUG_RETURN(1);
7374 7375 7376
      if (!negated)
        break;
      // else fall through (NOT END is an illegal condition)
unknown's avatar
unknown committed
7377 7378
    default:
    {
7379
      DBUG_PRINT("info", ("Illegal scan filter"));
7380
    }
unknown's avatar
unknown committed
7381
    }
7382
  }  while (level > 0 || negated);
unknown's avatar
unknown committed
7383
  
7384
  DBUG_RETURN(0);
unknown's avatar
unknown committed
7385 7386
}

7387 7388
int
ha_ndbcluster::build_scan_filter(Ndb_cond * &cond, NdbScanFilter *filter)
unknown's avatar
unknown committed
7389 7390 7391 7392
{
  bool simple_cond= TRUE;
  DBUG_ENTER("build_scan_filter");  

unknown's avatar
unknown committed
7393 7394 7395
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
      switch (cond->ndb_item->qualification.function_type) {
7396 7397
      case NDB_COND_AND_FUNC:
      case NDB_COND_OR_FUNC:
7398 7399 7400 7401 7402 7403 7404 7405 7406
        simple_cond= FALSE;
        break;
      default:
        break;
      }
      break;
    default:
      break;
    }
7407 7408 7409 7410 7411 7412
  if (simple_cond && filter->begin() == -1)
    DBUG_RETURN(1);
  if (build_scan_filter_group(cond, filter))
    DBUG_RETURN(1);
  if (simple_cond && filter->end() == -1)
    DBUG_RETURN(1);
unknown's avatar
unknown committed
7413

7414
  DBUG_RETURN(0);
unknown's avatar
unknown committed
7415 7416
}

7417
int
unknown's avatar
unknown committed
7418
ha_ndbcluster::generate_scan_filter(Ndb_cond_stack *ndb_cond_stack,
7419
                                    NdbScanOperation *op)
unknown's avatar
unknown committed
7420 7421 7422 7423
{
  DBUG_ENTER("generate_scan_filter");
  if (ndb_cond_stack)
  {
7424
    DBUG_PRINT("info", ("Generating scan filter"));
unknown's avatar
unknown committed
7425 7426 7427 7428 7429
    NdbScanFilter filter(op);
    bool multiple_cond= FALSE;
    // Wrap an AND group around multiple conditions
    if (ndb_cond_stack->next) {
      multiple_cond= TRUE;
7430
      if (filter.begin() == -1)
7431
        DBUG_RETURN(1); 
unknown's avatar
unknown committed
7432 7433
    }
    for (Ndb_cond_stack *stack= ndb_cond_stack; 
7434 7435
         (stack); 
         stack= stack->next)
unknown's avatar
unknown committed
7436
      {
7437
        Ndb_cond *cond= stack->ndb_cond;
7438

7439 7440 7441 7442 7443
        if (build_scan_filter(cond, &filter))
        {
          DBUG_PRINT("info", ("build_scan_filter failed"));
          DBUG_RETURN(1);
        }
unknown's avatar
unknown committed
7444
      }
7445 7446
    if (multiple_cond && filter.end() == -1)
      DBUG_RETURN(1);
unknown's avatar
unknown committed
7447 7448 7449 7450 7451 7452
  }
  else
  {  
    DBUG_PRINT("info", ("Empty stack"));
  }

7453
  DBUG_RETURN(0);
unknown's avatar
unknown committed
7454 7455
}

unknown's avatar
unknown committed
7456 7457 7458 7459 7460 7461 7462 7463 7464 7465 7466 7467 7468 7469 7470 7471 7472 7473 7474 7475 7476
int
ndbcluster_show_status(THD* thd)
{
  Protocol *protocol= thd->protocol;
  
  DBUG_ENTER("ndbcluster_show_status");
  
  if (have_ndbcluster != SHOW_OPTION_YES) 
  {
    my_message(ER_NOT_SUPPORTED_YET,
	       "Cannot call SHOW NDBCLUSTER STATUS because skip-ndbcluster is defined",
	       MYF(0));
    DBUG_RETURN(TRUE);
  }
  
  List<Item> field_list;
  field_list.push_back(new Item_empty_string("free_list", 255));
  field_list.push_back(new Item_return_int("created", 10,MYSQL_TYPE_LONG));
  field_list.push_back(new Item_return_int("free", 10,MYSQL_TYPE_LONG));
  field_list.push_back(new Item_return_int("sizeof", 10,MYSQL_TYPE_LONG));

7477
  if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
unknown's avatar
unknown committed
7478 7479
    DBUG_RETURN(TRUE);
  
unknown's avatar
unknown committed
7480
  if (get_thd_ndb(thd) && get_thd_ndb(thd)->ndb)
unknown's avatar
unknown committed
7481
  {
unknown's avatar
unknown committed
7482
    Ndb* ndb= (get_thd_ndb(thd))->ndb;
unknown's avatar
unknown committed
7483 7484 7485 7486 7487 7488 7489 7490 7491 7492 7493 7494 7495 7496 7497 7498 7499 7500
    Ndb::Free_list_usage tmp; tmp.m_name= 0;
    while (ndb->get_free_list_usage(&tmp))
    {
      protocol->prepare_for_resend();
      
      protocol->store(tmp.m_name, &my_charset_bin);
      protocol->store((uint)tmp.m_created);
      protocol->store((uint)tmp.m_free);
      protocol->store((uint)tmp.m_sizeof);
      if (protocol->write())
	DBUG_RETURN(TRUE);
    }
  }
  send_eof(thd);
  
  DBUG_RETURN(FALSE);
}

unknown's avatar
unknown committed
7501
#endif /* HAVE_NDBCLUSTER_DB */