ha_ndbcluster.cc 212 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

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

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

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

unknown's avatar
unknown committed
97
bool ndbcluster_inited= FALSE;
unknown's avatar
unknown committed
98

99
static Ndb* g_ndb= NULL;
100
static Ndb_cluster_connection* g_ndb_cluster_connection= NULL;
101

unknown's avatar
unknown committed
102 103 104 105 106 107 108 109 110 111 112 113 114
// 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,
115
                     const void* pack_data);
unknown's avatar
unknown committed
116

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

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

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

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

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

144 145 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
/* 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
171 172 173 174 175 176 177 178
/*
  Error handling functions
*/

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

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

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

  { 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 },

205 206
  { 284, HA_ERR_TABLE_DEF_CHANGED, 0 },

207 208 209
  { 0, 1, 0 },

  { -1, -1, 1 }
unknown's avatar
unknown committed
210 211 212 213 214 215
};


static int ndb_to_mysql_error(const NdbError *err)
{
  uint i;
216 217
  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
218
  {
219 220
    // Push the NDB error message as warning
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
221 222
                        ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
                        err->code, err->message, "NDB");
unknown's avatar
unknown committed
223
  }
224 225
  if (err_map[i].my_err == -1)
    return err->code;
unknown's avatar
unknown committed
226 227 228 229
  return err_map[i].my_err;
}


unknown's avatar
unknown committed
230 231

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

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

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

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

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

Thd_ndb::~Thd_ndb()
{
298 299
  if (ndb)
    delete ndb;
300 301
  ndb= NULL;
  changed_tables.empty();
302 303
}

304 305 306 307 308 309 310 311
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; }

312 313 314
inline
Ndb *ha_ndbcluster::get_ndb()
{
315
  return get_thd_ndb(current_thd)->ndb;
316 317 318 319 320 321
}

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

322
struct Ndb_local_table_statistics {
323
  int no_uncommitted_rows_count;
324
  ulong last_count;
325 326 327
  ha_rows records;
};

unknown's avatar
unknown committed
328 329 330
void ha_ndbcluster::set_rec_per_key()
{
  DBUG_ENTER("ha_ndbcluster::get_status_const");
331
  for (uint i=0 ; i < table->s->keys ; i++)
unknown's avatar
unknown committed
332 333 334 335 336 337
  {
    table->key_info[i].rec_per_key[table->key_info[i].key_parts-1]= 1;
  }
  DBUG_VOID_RETURN;
}

338 339
void ha_ndbcluster::records_update()
{
340 341
  if (m_ha_not_exact_count)
    return;
342
  DBUG_ENTER("ha_ndbcluster::records_update");
343 344
  struct Ndb_local_table_statistics *info= 
    (struct Ndb_local_table_statistics *)m_table_info;
345
  DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
346 347
                      ((const NDBTAB *)m_table)->getTableId(),
                      info->no_uncommitted_rows_count));
348
  //  if (info->records == ~(ha_rows)0)
349
  {
350
    Ndb *ndb= get_ndb();
351
    struct Ndb_statistics stat;
unknown's avatar
unknown committed
352
    if (ndb_get_table_statistics(ndb, m_tabname, &stat) == 0){
353 354 355
      mean_rec_length= stat.row_size;
      data_file_length= stat.fragment_memory;
      info->records= stat.row_count;
356 357
    }
  }
358 359
  {
    THD *thd= current_thd;
360
    if (get_thd_ndb(thd)->error)
361 362
      info->no_uncommitted_rows_count= 0;
  }
363 364 365 366
  records= info->records+ info->no_uncommitted_rows_count;
  DBUG_VOID_RETURN;
}

367 368
void ha_ndbcluster::no_uncommitted_rows_execute_failure()
{
369 370
  if (m_ha_not_exact_count)
    return;
371
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_execute_failure");
372
  get_thd_ndb(current_thd)->error= 1;
373 374 375
  DBUG_VOID_RETURN;
}

376 377
void ha_ndbcluster::no_uncommitted_rows_init(THD *thd)
{
378 379
  if (m_ha_not_exact_count)
    return;
380
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_init");
381 382
  struct Ndb_local_table_statistics *info= 
    (struct Ndb_local_table_statistics *)m_table_info;
383
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
384
  if (info->last_count != thd_ndb->count)
385
  {
386
    info->last_count= thd_ndb->count;
387 388 389
    info->no_uncommitted_rows_count= 0;
    info->records= ~(ha_rows)0;
    DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
390 391
                        ((const NDBTAB *)m_table)->getTableId(),
                        info->no_uncommitted_rows_count));
392 393 394 395 396 397
  }
  DBUG_VOID_RETURN;
}

void ha_ndbcluster::no_uncommitted_rows_update(int c)
{
398 399
  if (m_ha_not_exact_count)
    return;
400
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_update");
401 402
  struct Ndb_local_table_statistics *info=
    (struct Ndb_local_table_statistics *)m_table_info;
403 404
  info->no_uncommitted_rows_count+= c;
  DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
405 406
                      ((const NDBTAB *)m_table)->getTableId(),
                      info->no_uncommitted_rows_count));
407 408 409 410 411
  DBUG_VOID_RETURN;
}

void ha_ndbcluster::no_uncommitted_rows_reset(THD *thd)
{
412 413
  if (m_ha_not_exact_count)
    return;
414
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_reset");
415 416 417
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  thd_ndb->count++;
  thd_ndb->error= 0;
418 419 420
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
421 422
/*
  Take care of the error that occured in NDB
423

unknown's avatar
unknown committed
424
  RETURN
425
    0   No error
unknown's avatar
unknown committed
426 427 428
    #   The mapped error code
*/

429
void ha_ndbcluster::invalidate_dictionary_cache(bool global)
430 431
{
  NDBDICT *dict= get_ndb()->getDictionary();
432
  DBUG_ENTER("invalidate_dictionary_cache");
433
  DBUG_PRINT("info", ("invalidating %s", m_tabname));
434

435
  if (global)
436
  {
437 438 439 440
    const NDBTAB *tab= dict->getTable(m_tabname);
    if (!tab)
      DBUG_VOID_RETURN;
    if (tab->getObjectStatus() == NdbDictionary::Object::Invalid)
441 442 443 444 445 446 447 448
    {
      // Global cache has already been invalidated
      dict->removeCachedTable(m_tabname);
      global= FALSE;
    }
    else
      dict->invalidateTable(m_tabname);
  }
449 450
  else
    dict->removeCachedTable(m_tabname);
unknown's avatar
unknown committed
451
  table->s->version=0L;			/* Free when thread is ready */
452
  /* Invalidate indexes */
unknown's avatar
unknown committed
453
  for (uint i= 0; i < table->s->keys; i++)
454 455 456 457 458
  {
    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
459 460 461
    switch (idx_type) {
    case PRIMARY_KEY_ORDERED_INDEX:
    case ORDERED_INDEX:
462 463 464 465
      if (global)
        dict->invalidateIndex(index->getName(), m_tabname);
      else
        dict->removeCachedIndex(index->getName(), m_tabname);
unknown's avatar
unknown committed
466
      break;
unknown's avatar
unknown committed
467
    case UNIQUE_ORDERED_INDEX:
468 469 470 471
      if (global)
        dict->invalidateIndex(index->getName(), m_tabname);
      else
        dict->removeCachedIndex(index->getName(), m_tabname);
unknown's avatar
unknown committed
472
    case UNIQUE_INDEX:
473 474 475 476
      if (global)
        dict->invalidateIndex(unique_index->getName(), m_tabname);
      else
        dict->removeCachedIndex(unique_index->getName(), m_tabname);
477
      break;
unknown's avatar
unknown committed
478 479
    case PRIMARY_KEY_INDEX:
    case UNDEFINED_INDEX:
480 481 482
      break;
    }
  }
483
  DBUG_VOID_RETURN;
484
}
485

486
int ha_ndbcluster::ndb_err(NdbTransaction *trans)
unknown's avatar
unknown committed
487
{
488
  int res;
489
  NdbError err= trans->getNdbError();
unknown's avatar
unknown committed
490 491 492 493 494
  DBUG_ENTER("ndb_err");
  
  ERR_PRINT(err);
  switch (err.classification) {
  case NdbError::SchemaError:
495
    invalidate_dictionary_cache(TRUE);
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511

    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);
      }
512
      DBUG_PRINT("info", ("Table exists but must have changed"));
513
    }
unknown's avatar
unknown committed
514 515 516 517
    break;
  default:
    break;
  }
518 519
  res= ndb_to_mysql_error(&err);
  DBUG_PRINT("info", ("transformed ndbcluster error %d to mysql error %d", 
520
                      err.code, res));
521
  if (res == HA_ERR_FOUND_DUPP_KEY)
522 523
  {
    if (m_rows_to_insert == 1)
unknown's avatar
Merge  
unknown committed
524
      m_dupkey= table->s->primary_key;
525
    else
unknown's avatar
unknown committed
526 527
    {
      /* We are batching inserts, offending key is not available */
528
      m_dupkey= (uint) -1;
unknown's avatar
unknown committed
529
    }
530
  }
531
  DBUG_RETURN(res);
unknown's avatar
unknown committed
532 533 534
}


535
/*
536
  Override the default get_error_message in order to add the 
537 538 539
  error message of NDB 
 */

540
bool ha_ndbcluster::get_error_message(int error, 
541
                                      String *buf)
542
{
543
  DBUG_ENTER("ha_ndbcluster::get_error_message");
544
  DBUG_PRINT("enter", ("error: %d", error));
545

546
  Ndb *ndb= get_ndb();
547
  if (!ndb)
unknown's avatar
unknown committed
548
    DBUG_RETURN(FALSE);
549

550
  const NdbError err= ndb->getNdbError(error);
551 552 553 554
  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);
555 556 557
}


unknown's avatar
unknown committed
558
#ifndef DBUG_OFF
unknown's avatar
unknown committed
559 560 561 562
/*
  Check if type is supported by NDB.
*/

unknown's avatar
unknown committed
563
static bool ndb_supported_type(enum_field_types type)
unknown's avatar
unknown committed
564 565
{
  switch (type) {
unknown's avatar
unknown committed
566 567 568 569 570 571 572
  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:
573 574
  case MYSQL_TYPE_DECIMAL:    
  case MYSQL_TYPE_NEWDECIMAL:
unknown's avatar
unknown committed
575 576 577 578 579 580 581 582
  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
583
  case MYSQL_TYPE_VARCHAR:
unknown's avatar
unknown committed
584 585 586 587 588 589
  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:         
590
  case MYSQL_TYPE_BIT:
591
  case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
592
    return TRUE;
unknown's avatar
unknown committed
593
  case MYSQL_TYPE_NULL:   
unknown's avatar
unknown committed
594
    break;
unknown's avatar
unknown committed
595
  }
unknown's avatar
unknown committed
596
  return FALSE;
unknown's avatar
unknown committed
597
}
unknown's avatar
unknown committed
598
#endif /* !DBUG_OFF */
unknown's avatar
unknown committed
599 600


unknown's avatar
unknown committed
601 602 603 604 605
/*
  Instruct NDB to set the value of the hidden primary key
*/

bool ha_ndbcluster::set_hidden_key(NdbOperation *ndb_op,
606
                                   uint fieldnr, const byte *field_ptr)
unknown's avatar
unknown committed
607 608 609
{
  DBUG_ENTER("set_hidden_key");
  DBUG_RETURN(ndb_op->equal(fieldnr, (char*)field_ptr,
610
                            NDB_HIDDEN_PRIMARY_KEY_LENGTH) != 0);
unknown's avatar
unknown committed
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
}


/*
  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
628 629 630 631
  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
632 633 634 635 636 637 638 639
}


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

int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field, 
640
                                 uint fieldnr, bool *set_blob_value)
unknown's avatar
unknown committed
641 642 643 644 645 646 647 648
{
  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
649

unknown's avatar
unknown committed
650
  DBUG_ASSERT(ndb_supported_type(field->type()));
unknown's avatar
unknown committed
651
  {
652
    // ndb currently does not support size 0
unknown's avatar
unknown committed
653
    uint32 empty_field;
654 655
    if (pack_len == 0)
    {
unknown's avatar
unknown committed
656 657 658
      pack_len= sizeof(empty_field);
      field_ptr= (byte *)&empty_field;
      if (field->is_null())
659
        empty_field= 0;
unknown's avatar
unknown committed
660
      else
661
        empty_field= 1;
662
    }
unknown's avatar
unknown committed
663 664
    if (! (field->flags & BLOB_FLAG))
    {
665 666
      if (field->type() != MYSQL_TYPE_BIT)
      {
667 668 669 670 671 672 673
        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);
674 675 676
      }
      else // if (field->type() == MYSQL_TYPE_BIT)
      {
677
        longlong bits= field->val_int();
678
 
679 680
        // Round up bit field length to nearest word boundry
        pack_len= ((pack_len + 3) >> 2) << 2;
681 682 683 684 685
        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"));
686
        DBUG_DUMP("value", (char*)&bits, pack_len);
687
#ifdef WORDS_BIGENDIAN
688 689 690 691 692
        if (pack_len < 5)
        {
          DBUG_RETURN(ndb_op->setValue(fieldnr, 
                                       ((char*)&bits)+4, pack_len) != 0);
        }
693
#endif
694
        DBUG_RETURN(ndb_op->setValue(fieldnr, (char*)&bits, pack_len) != 0);
695
      }
unknown's avatar
unknown committed
696 697
    }
    // Blob type
698
    NdbBlob *ndb_blob= ndb_op->getBlobHandle(fieldnr);
unknown's avatar
unknown committed
699 700 701 702 703 704 705 706 707 708 709 710
    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);

711 712 713
      // Looks like NULL ptr signals length 0 blob
      if (blob_ptr == NULL) {
        DBUG_ASSERT(blob_len == 0);
714
        blob_ptr= (char*)"";
715
      }
unknown's avatar
unknown committed
716

unknown's avatar
unknown committed
717 718
      DBUG_PRINT("value", ("set blob ptr=%p len=%u",
                           blob_ptr, blob_len));
unknown's avatar
unknown committed
719 720
      DBUG_DUMP("value", (char*)blob_ptr, min(blob_len, 26));

721
      if (set_blob_value)
722
        *set_blob_value= TRUE;
unknown's avatar
unknown committed
723 724 725 726
      // No callback needed to write value
      DBUG_RETURN(ndb_blob->setValue(blob_ptr, blob_len) != 0);
    }
    DBUG_RETURN(1);
unknown's avatar
unknown committed
727
  }
unknown's avatar
unknown committed
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742
}


/*
  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
743
NdbBlob::ActiveHook g_get_ndb_blobs_value;
unknown's avatar
unknown committed
744

unknown's avatar
unknown committed
745
int g_get_ndb_blobs_value(NdbBlob *ndb_blob, void *arg)
unknown's avatar
unknown committed
746
{
unknown's avatar
unknown committed
747
  DBUG_ENTER("g_get_ndb_blobs_value");
unknown's avatar
unknown committed
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
  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;
763
    for (uint i= 0; i < table->s->fields; i++)
unknown's avatar
unknown committed
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
    {
      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)
        {
780
          char *buf= m_blobs_buffer + offset;
unknown's avatar
unknown committed
781 782
          uint32 len= 0xffffffff;  // Max uint32
          DBUG_PRINT("value", ("read blob ptr=%x len=%u",
unknown's avatar
unknown committed
783
                               (UintPtr)buf, (uint)blob_len));
unknown's avatar
unknown committed
784 785 786 787 788 789 790 791
          if (ndb_blob->readData(buf, len) != 0)
            DBUG_RETURN(-1);
          DBUG_ASSERT(len == blob_len);
          field_blob->set_ptr(len, buf);
        }
        offset+= blob_size;
      }
    }
792
    if (loop == 0 && offset > m_blobs_buffer_size)
unknown's avatar
unknown committed
793
    {
794 795
      my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
      m_blobs_buffer_size= 0;
unknown's avatar
unknown committed
796
      DBUG_PRINT("value", ("allocate blobs buffer size %u", offset));
797 798
      m_blobs_buffer= my_malloc(offset, MYF(MY_WME));
      if (m_blobs_buffer == NULL)
unknown's avatar
unknown committed
799
        DBUG_RETURN(-1);
800
      m_blobs_buffer_size= offset;
unknown's avatar
unknown committed
801
    }
unknown's avatar
unknown committed
802
  }
unknown's avatar
unknown committed
803
  DBUG_RETURN(0);
unknown's avatar
unknown committed
804 805 806 807 808
}


/*
  Instruct NDB to fetch one field
unknown's avatar
unknown committed
809 810
  - 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
811 812
*/

unknown's avatar
unknown committed
813
int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field,
unknown's avatar
unknown committed
814
                                 uint fieldnr, byte* buf)
unknown's avatar
unknown committed
815 816
{
  DBUG_ENTER("get_ndb_value");
unknown's avatar
unknown committed
817 818 819 820 821
  DBUG_PRINT("enter", ("fieldnr: %d flags: %o", fieldnr,
                       (int)(field != NULL ? field->flags : 0)));

  if (field != NULL)
  {
unknown's avatar
unknown committed
822 823
      DBUG_ASSERT(buf);
      DBUG_ASSERT(ndb_supported_type(field->type()));
unknown's avatar
unknown committed
824 825
      DBUG_ASSERT(field->ptr != NULL);
      if (! (field->flags & BLOB_FLAG))
826
      { 
827 828
        if (field->type() != MYSQL_TYPE_BIT)
        {
829 830 831 832 833 834 835 836
          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);
        }
837 838 839 840
        else // if (field->type() == MYSQL_TYPE_BIT)
        {
          m_value[fieldnr].rec= ndb_op->getValue(fieldnr);
        }
unknown's avatar
unknown committed
841 842 843 844 845 846 847 848 849 850
        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
851
        DBUG_RETURN(ndb_blob->setActiveHook(g_get_ndb_blobs_value, arg) != 0);
unknown's avatar
unknown committed
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866
      }
      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)
{
867
  if (table->s->blob_fields == 0)
unknown's avatar
unknown committed
868
    return FALSE;
unknown's avatar
unknown committed
869
  if (all_fields)
unknown's avatar
unknown committed
870
    return TRUE;
unknown's avatar
unknown committed
871
  {
872
    uint no_fields= table->s->fields;
unknown's avatar
unknown committed
873
    int i;
874
    THD *thd= current_thd;
unknown's avatar
unknown committed
875 876 877 878 879 880
    // 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
881
        return TRUE;
unknown's avatar
unknown committed
882 883 884
      }
    }
  }
unknown's avatar
unknown committed
885
  return FALSE;
unknown's avatar
unknown committed
886 887 888 889 890 891 892 893 894 895 896 897 898
}


/*
  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)
{
899 900
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
unknown's avatar
unknown committed
901 902
  const NDBTAB *tab;
  int error;
unknown's avatar
unknown committed
903
  bool invalidating_ndb_table= FALSE;
904

unknown's avatar
unknown committed
905 906 907
  DBUG_ENTER("get_metadata");
  DBUG_PRINT("enter", ("m_tabname: %s, path: %s", m_tabname, path));

908 909 910 911 912 913
  do {
    const void *data, *pack_data;
    uint length, pack_length;

    if (!(tab= dict->getTable(m_tabname)))
      ERR_RETURN(dict->getNdbError());
914
    // Check if thread has stale local cache
915 916 917 918 919 920 921
    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()));
    }
922 923 924 925 926
    /*
      Compare FrmData in NDB with frm file from disk.
    */
    error= 0;
    if (readfrm(path, &data, &length) ||
927
        packfrm(data, length, &pack_data, &pack_length))
928 929 930 931 932
    {
      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
933
    
934
    if ((pack_length != tab->getFrmLength()) || 
935
        (memcmp(pack_data, tab->getFrmData(), pack_length)))
936 937 938
    {
      if (!invalidating_ndb_table)
      {
939
        DBUG_PRINT("info", ("Invalidating table"));
940
        invalidate_dictionary_cache(TRUE);
941
        invalidating_ndb_table= TRUE;
942 943 944
      }
      else
      {
945 946 947 948 949 950 951 952
        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;
953 954 955 956
      }
    }
    else
    {
unknown's avatar
unknown committed
957
      invalidating_ndb_table= FALSE;
958 959 960 961 962
    }
    my_free((char*)data, MYF(0));
    my_free((char*)pack_data, MYF(0));
  } while (invalidating_ndb_table);

unknown's avatar
unknown committed
963 964
  if (error)
    DBUG_RETURN(error);
unknown's avatar
unknown committed
965
  
966
  m_table_version= tab->getObjectVersion();
967 968 969 970
  m_table= (void *)tab; 
  m_table_info= NULL; // Set in external lock
  
  DBUG_RETURN(build_index_list(ndb, table, ILBP_OPEN));
971
}
unknown's avatar
unknown committed
972

973
static int fix_unique_index_attr_order(NDB_INDEX_DATA &data,
974 975
                                       const NDBINDEX *index,
                                       KEY *key_info)
976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994
{
  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++)
    {
995
      const NDBCOL *c= index->getColumn(j);
unknown's avatar
unknown committed
996
      if (strcmp(field_name, c->getName()) == 0)
997
      {
998 999
        data.unique_index_attrid_map[i]= j;
        break;
1000 1001 1002 1003 1004 1005
      }
    }
    DBUG_ASSERT(data.unique_index_attrid_map[i] != 255);
  }
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1006

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

1086

unknown's avatar
unknown committed
1087 1088 1089 1090
/*
  Decode the type of an index from information 
  provided in table object
*/
1091
NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_table(uint inx) const
unknown's avatar
unknown committed
1092
{
1093
  bool is_hash_index=  (table->key_info[inx].algorithm == HA_KEY_ALG_HASH);
1094
  if (inx == table->s->primary_key)
1095
    return is_hash_index ? PRIMARY_KEY_INDEX : PRIMARY_KEY_ORDERED_INDEX;
1096 1097 1098 1099

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

1102 1103 1104 1105 1106
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;
1107
  DBUG_ENTER("ha_ndbcluster::check_index_fields_not_null");
1108 1109 1110 1111 1112 1113
  
  for (; key_part != end; key_part++) 
    {
      Field* field= key_part->field;
      if (field->maybe_null())
      {
1114 1115 1116
        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);
1117 1118 1119 1120 1121
      }
    }
  
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1122 1123 1124

void ha_ndbcluster::release_metadata()
{
1125
  uint i;
1126

unknown's avatar
unknown committed
1127 1128 1129 1130
  DBUG_ENTER("release_metadata");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));

  m_table= NULL;
unknown's avatar
unknown committed
1131
  m_table_info= NULL;
unknown's avatar
unknown committed
1132

1133
  // Release index list 
1134 1135
  for (i= 0; i < MAX_KEY; i++)
  {
1136 1137
    m_index[i].unique_index= NULL;      
    m_index[i].index= NULL;      
1138 1139 1140 1141 1142
    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;
    }
1143 1144
  }

unknown's avatar
unknown committed
1145 1146 1147
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
1148
int ha_ndbcluster::get_ndb_lock_type(enum thr_lock_type type)
1149
{
1150
  if (type >= TL_WRITE_ALLOW_WRITE)
unknown's avatar
unknown committed
1151
    return NdbOperation::LM_Exclusive;
1152
  else if (uses_blob_value(m_retrieve_all_fields))
1153
    return NdbOperation::LM_Read;
unknown's avatar
unknown committed
1154
  else
unknown's avatar
unknown committed
1155
    return NdbOperation::LM_CommittedRead;
1156 1157
}

unknown's avatar
unknown committed
1158 1159 1160 1161 1162 1163
static const ulong index_type_flags[]=
{
  /* UNDEFINED_INDEX */
  0,                         

  /* PRIMARY_KEY_INDEX */
1164
  HA_ONLY_WHOLE_INDEX, 
1165 1166

  /* PRIMARY_KEY_ORDERED_INDEX */
1167
  /* 
unknown's avatar
unknown committed
1168
     Enable HA_KEYREAD_ONLY when "sorted" indexes are supported, 
1169 1170 1171
     thus ORDERD BY clauses can be optimized by reading directly 
     through the index.
  */
unknown's avatar
unknown committed
1172
  // HA_KEYREAD_ONLY | 
unknown's avatar
unknown committed
1173
  HA_READ_NEXT |
1174
  HA_READ_PREV |
unknown's avatar
unknown committed
1175 1176
  HA_READ_RANGE |
  HA_READ_ORDER,
unknown's avatar
unknown committed
1177 1178

  /* UNIQUE_INDEX */
1179
  HA_ONLY_WHOLE_INDEX,
unknown's avatar
unknown committed
1180

1181
  /* UNIQUE_ORDERED_INDEX */
unknown's avatar
unknown committed
1182
  HA_READ_NEXT |
1183
  HA_READ_PREV |
unknown's avatar
unknown committed
1184 1185
  HA_READ_RANGE |
  HA_READ_ORDER,
1186

unknown's avatar
unknown committed
1187
  /* ORDERED_INDEX */
unknown's avatar
unknown committed
1188
  HA_READ_NEXT |
1189
  HA_READ_PREV |
unknown's avatar
unknown committed
1190 1191
  HA_READ_RANGE |
  HA_READ_ORDER
unknown's avatar
unknown committed
1192 1193 1194 1195 1196 1197 1198
};

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);
1199
  return m_index[idx_no].type;
unknown's avatar
unknown committed
1200 1201 1202 1203 1204 1205 1206 1207 1208 1209
}


/*
  Get the flags for an index

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

1210 1211
inline ulong ha_ndbcluster::index_flags(uint idx_no, uint part,
                                        bool all_parts) const 
unknown's avatar
unknown committed
1212
{ 
1213
  DBUG_ENTER("ha_ndbcluster::index_flags");
1214
  DBUG_PRINT("info", ("idx_no: %d", idx_no));
unknown's avatar
unknown committed
1215
  DBUG_ASSERT(get_index_type_from_table(idx_no) < index_flags_size);
1216 1217
  DBUG_RETURN(index_type_flags[get_index_type_from_table(idx_no)] | 
              HA_KEY_SCAN_NOT_ROR);
unknown's avatar
unknown committed
1218 1219
}

unknown's avatar
unknown committed
1220 1221
static void shrink_varchar(Field* field, const byte* & ptr, char* buf)
{
1222
  if (field->type() == MYSQL_TYPE_VARCHAR && ptr != NULL) {
unknown's avatar
unknown committed
1223
    Field_varstring* f= (Field_varstring*)field;
unknown's avatar
unknown committed
1224
    if (f->length_bytes == 1) {
unknown's avatar
unknown committed
1225 1226 1227 1228 1229
      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
1230
        DBUG_ASSERT(FALSE);
unknown's avatar
unknown committed
1231 1232 1233 1234 1235 1236 1237
        buf[0]= 255;
      }
      memmove(buf + 1, ptr + 2, pack_len - 1);
      ptr= buf;
    }
  }
}
unknown's avatar
unknown committed
1238 1239 1240

int ha_ndbcluster::set_primary_key(NdbOperation *op, const byte *key)
{
1241
  KEY* key_info= table->key_info + table->s->primary_key;
unknown's avatar
unknown committed
1242 1243 1244 1245 1246 1247 1248
  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
1249 1250 1251
    const byte* ptr= key;
    char buf[256];
    shrink_varchar(field, ptr, buf);
unknown's avatar
unknown committed
1252
    if (set_ndb_key(op, field, 
1253
                    key_part->fieldnr-1, ptr))
unknown's avatar
unknown committed
1254
      ERR_RETURN(op->getNdbError());
unknown's avatar
unknown committed
1255
    key += key_part->store_length;
unknown's avatar
unknown committed
1256 1257 1258 1259 1260
  }
  DBUG_RETURN(0);
}


1261
int ha_ndbcluster::set_primary_key_from_record(NdbOperation *op, const byte *record)
1262
{
1263
  KEY* key_info= table->key_info + table->s->primary_key;
1264 1265
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
1266
  DBUG_ENTER("set_primary_key_from_record");
1267 1268 1269 1270 1271

  for (; key_part != end; key_part++) 
  {
    Field* field= key_part->field;
    if (set_ndb_key(op, field, 
1272
		    key_part->fieldnr-1, record+key_part->offset))
1273 1274 1275 1276 1277
      ERR_RETURN(op->getNdbError());
  }
  DBUG_RETURN(0);
}

1278 1279
int 
ha_ndbcluster::set_index_key(NdbOperation *op, 
1280 1281
                             const KEY *key_info, 
                             const byte * key_ptr)
1282
{
1283
  DBUG_ENTER("ha_ndbcluster::set_index_key");
1284 1285 1286 1287 1288 1289
  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
1290 1291 1292 1293
    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
1294
    if (set_ndb_key(op, field, m_index[active_index].unique_index_attrid_map[i], ptr))
1295 1296 1297 1298 1299
      ERR_RETURN(m_active_trans->getNdbError());
    key_ptr+= key_part->store_length;
  }
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1300

unknown's avatar
unknown committed
1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313
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) ||
1314 1315
        ((field->flags & PRI_KEY_FLAG)) || 
        m_retrieve_all_fields)
unknown's avatar
unknown committed
1316 1317
    {      
      if (get_ndb_value(op, field, i, buf))
1318
        ERR_RETURN(op->getNdbError());
unknown's avatar
unknown committed
1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341
    } 
    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
1342 1343 1344 1345
/*
  Read one record from NDB using primary key
*/

1346
int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf) 
unknown's avatar
unknown committed
1347
{
1348
  uint no_fields= table->s->fields;
unknown's avatar
unknown committed
1349 1350
  NdbConnection *trans= m_active_trans;
  NdbOperation *op;
1351

1352 1353 1354 1355
  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
1356

1357 1358
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
unknown's avatar
unknown committed
1359
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || 
1360
      op->readTuple(lm) != 0)
1361
    ERR_RETURN(trans->getNdbError());
1362
  
1363
  if (table->s->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
1364 1365 1366 1367 1368
  {
    // 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))
1369
      ERR_RETURN(trans->getNdbError());
1370
    
unknown's avatar
unknown committed
1371
    // Read key at the same time, for future reference
unknown's avatar
unknown committed
1372
    if (get_ndb_value(op, NULL, no_fields, NULL))
1373
      ERR_RETURN(trans->getNdbError());
unknown's avatar
unknown committed
1374 1375 1376 1377 1378 1379 1380
  } 
  else 
  {
    if ((res= set_primary_key(op, key)))
      return res;
  }
  
unknown's avatar
unknown committed
1381
  if ((res= define_read_attrs(buf, op)))
1382
    DBUG_RETURN(res);
unknown's avatar
unknown committed
1383
  
unknown's avatar
unknown committed
1384
  if (execute_no_commit_ie(this,trans) != 0) 
unknown's avatar
unknown committed
1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395
  {
    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);
}

1396 1397 1398 1399 1400 1401
/*
  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)
{
1402
  uint no_fields= table->s->fields, i;
1403
  NdbTransaction *trans= m_active_trans;
1404 1405 1406 1407
  NdbOperation *op;
  THD *thd= current_thd;
  DBUG_ENTER("complemented_pk_read");

1408
  if (m_retrieve_all_fields)
1409 1410 1411
    // We have allready retrieved all fields, nothing to complement
    DBUG_RETURN(0);

1412 1413
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
unknown's avatar
unknown committed
1414
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || 
1415
      op->readTuple(lm) != 0)
1416
    ERR_RETURN(trans->getNdbError());
1417
  int res;
unknown's avatar
unknown committed
1418
  if ((res= set_primary_key_from_record(op, old_data)))
1419
    ERR_RETURN(trans->getNdbError());
1420 1421 1422 1423
  // Read all unreferenced non-key field(s)
  for (i= 0; i < no_fields; i++) 
  {
    Field *field= table->field[i];
1424
    if (!((field->flags & PRI_KEY_FLAG) ||
1425
          (thd->query_id == field->query_id)))
1426
    {
unknown's avatar
unknown committed
1427
      if (get_ndb_value(op, field, i, new_data))
1428
        ERR_RETURN(trans->getNdbError());
1429 1430 1431
    }
  }
  
unknown's avatar
unknown committed
1432
  if (execute_no_commit(this,trans) != 0) 
1433 1434 1435 1436 1437 1438 1439 1440
  {
    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;     
1441 1442 1443 1444 1445 1446 1447 1448

  /**
   * restore m_value
   */
  for (i= 0; i < no_fields; i++) 
  {
    Field *field= table->field[i];
    if (!((field->flags & PRI_KEY_FLAG) ||
1449
          (thd->query_id == field->query_id)))
1450 1451 1452 1453 1454
    {
      m_value[i].ptr= NULL;
    }
  }
  
1455 1456 1457
  DBUG_RETURN(0);
}

1458 1459 1460 1461
/*
  Peek to check if a particular row already exists
*/

1462
int ha_ndbcluster::peek_row(const byte *record)
1463
{
1464
  NdbTransaction *trans= m_active_trans;
1465 1466
  NdbOperation *op;
  DBUG_ENTER("peek_row");
unknown's avatar
unknown committed
1467

1468 1469 1470 1471 1472
  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
1473

1474
  int res;
1475
  if ((res= set_primary_key_from_record(op, record)))
1476
    ERR_RETURN(trans->getNdbError());
unknown's avatar
unknown committed
1477

1478
  if (execute_no_commit_ie(this,trans) != 0)
unknown's avatar
unknown committed
1479 1480 1481 1482
  {
    table->status= STATUS_NOT_FOUND;
    DBUG_RETURN(ndb_err(trans));
  } 
1483 1484
  DBUG_RETURN(0);
}
1485

unknown's avatar
unknown committed
1486 1487 1488 1489 1490
/*
  Read one record from NDB using unique secondary index
*/

int ha_ndbcluster::unique_index_read(const byte *key,
1491
                                     uint key_len, byte *buf)
unknown's avatar
unknown committed
1492
{
1493
  int res;
1494
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
1495
  NdbIndexOperation *op;
1496
  DBUG_ENTER("ha_ndbcluster::unique_index_read");
unknown's avatar
unknown committed
1497 1498 1499
  DBUG_PRINT("enter", ("key_len: %u, index: %u", key_len, active_index));
  DBUG_DUMP("key", (char*)key, key_len);
  
1500 1501
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
1502
  if (!(op= trans->getNdbIndexOperation((NDBINDEX *) 
1503
                                        m_index[active_index].unique_index, 
unknown's avatar
unknown committed
1504
                                        (const NDBTAB *) m_table)) ||
1505
      op->readTuple(lm) != 0)
unknown's avatar
unknown committed
1506 1507 1508
    ERR_RETURN(trans->getNdbError());
  
  // Set secondary index key(s)
unknown's avatar
unknown committed
1509
  if ((res= set_index_key(op, table->key_info + active_index, key)))
1510 1511
    DBUG_RETURN(res);
  
unknown's avatar
unknown committed
1512
  if ((res= define_read_attrs(buf, op)))
1513
    DBUG_RETURN(res);
unknown's avatar
unknown committed
1514

unknown's avatar
unknown committed
1515
  if (execute_no_commit_ie(this,trans) != 0) 
unknown's avatar
unknown committed
1516 1517 1518 1519 1520 1521 1522 1523 1524 1525
  {
    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
1526
inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor)
1527 1528
{
  DBUG_ENTER("fetch_next");
1529
  int check;
1530
  NdbTransaction *trans= m_active_trans;
1531
  
1532
  bool contact_ndb= m_lock.type < TL_WRITE_ALLOW_WRITE;
1533 1534
  do {
    DBUG_PRINT("info", ("Call nextResult, contact_ndb: %d", contact_ndb));
unknown's avatar
unknown committed
1535 1536 1537
    /*
      We can only handle one tuple with blobs at a time.
    */
1538
    if (m_ops_pending && m_blobs_pending)
unknown's avatar
unknown committed
1539
    {
unknown's avatar
unknown committed
1540
      if (execute_no_commit(this,trans) != 0)
1541
        DBUG_RETURN(ndb_err(trans));
1542 1543
      m_ops_pending= 0;
      m_blobs_pending= FALSE;
unknown's avatar
unknown committed
1544
    }
1545 1546
    
    if ((check= cursor->nextResult(contact_ndb, m_force_send)) == 0)
1547 1548 1549 1550 1551 1552 1553
    {
      DBUG_RETURN(0);
    } 
    else if (check == 1 || check == 2)
    {
      // 1: No more records
      // 2: No more cached records
1554
      
1555
      /*
1556 1557 1558
        Before fetching more rows and releasing lock(s),
        all pending update or delete operations should 
        be sent to NDB
1559
      */
1560 1561
      DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending));    
      if (m_ops_pending)
1562
      {
1563 1564 1565 1566 1567 1568 1569 1570 1571
        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
1572
          if (trans->restart() != 0)
1573 1574 1575 1576 1577 1578
          {
            DBUG_ASSERT(0);
            DBUG_RETURN(-1);
          }
        }
        m_ops_pending= 0;
1579
      }
1580 1581
      contact_ndb= (check == 2);
    }
unknown's avatar
unknown committed
1582 1583 1584 1585
    else
    {
      DBUG_RETURN(-1);
    }
1586
  } while (check == 2);
unknown's avatar
unknown committed
1587

1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598
  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
1599

1600 1601 1602 1603 1604 1605 1606
*/

inline int ha_ndbcluster::next_result(byte *buf)
{  
  int res;
  DBUG_ENTER("next_result");
    
1607 1608 1609
  if (!m_active_cursor)
    DBUG_RETURN(HA_ERR_END_OF_FILE);
  
unknown's avatar
unknown committed
1610
  if ((res= fetch_next(m_active_cursor)) == 0)
1611 1612 1613 1614 1615 1616 1617
  {
    DBUG_PRINT("info", ("One more record found"));    
    
    unpack_record(buf);
    table->status= 0;
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
1618
  else if (res == 1)
1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629
  {
    // 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
1630 1631
}

1632
/*
1633
  Set bounds for ordered index scan.
1634 1635
*/

unknown's avatar
unknown committed
1636
int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
1637 1638
                              const key_range *keys[2],
                              uint range_no)
1639
{
1640 1641 1642 1643
  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;
1644
  uint i, j;
1645 1646

  DBUG_ENTER("set_bounds");
1647
  DBUG_PRINT("info", ("key_parts=%d", key_parts));
1648

1649
  for (j= 0; j <= 1; j++)
1650
  {
1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663
    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;
    }
1664 1665
  }
  tot_len= 0;
unknown's avatar
unknown committed
1666

1667 1668 1669 1670
  for (i= 0; i < key_parts; i++)
  {
    KEY_PART_INFO *key_part= &key_info->key_part[i];
    Field *field= key_part->field;
1671
#ifndef DBUG_OFF
1672
    uint part_len= key_part->length;
1673
#endif
1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687
    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++)
    {
1688
      struct part_st &p= part[j];
1689 1690 1691 1692 1693 1694 1695
      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
1696
        p.part_null= key_part->null_bit && *p.part_ptr;
1697
        p.bound_ptr= (const char *)
unknown's avatar
unknown committed
1698
          p.part_null ? 0 : key_part->null_bit ? p.part_ptr + 1 : p.part_ptr;
1699 1700 1701 1702 1703 1704 1705 1706

        if (j == 0)
        {
          switch (p.key->flag)
          {
            case HA_READ_KEY_EXACT:
              p.bound_type= NdbIndexScanOperation::BoundEQ;
              break;
1707
            // ascending
1708 1709 1710 1711 1712 1713 1714 1715 1716
            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;
1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729
            // 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;
1730 1731 1732 1733 1734 1735 1736
            default:
              break;
          }
        }
        if (j == 1) {
          switch (p.key->flag)
          {
1737
            // ascending
1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748
            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;
1749
            // descending strangely sets no end key
1750 1751
          }
        }
1752

1753 1754 1755
        if (p.bound_type == -1)
        {
          DBUG_PRINT("error", ("key %d unknown flag %d", j, p.key->flag));
unknown's avatar
unknown committed
1756
          DBUG_ASSERT(FALSE);
1757
          // Stop setting bounds but continue with what we have
1758
          op->end_of_bound(range_no);
1759 1760 1761 1762
          DBUG_RETURN(0);
        }
      }
    }
1763

1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780
    // 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;
    }
1781

1782 1783
    for (j= 0; j <= 1; j++)
    {
1784
      struct part_st &p= part[j];
1785 1786 1787 1788 1789 1790 1791 1792 1793
      // 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)
1794
        {
unknown's avatar
unknown committed
1795 1796 1797
          const char* ptr= p.bound_ptr;
          char buf[256];
          shrink_varchar(field, ptr, buf);
unknown's avatar
Merge  
unknown committed
1798
          if (op->setBound(i, p.bound_type, ptr))
1799
            ERR_RETURN(op->getNdbError());
1800
        }
1801 1802 1803 1804
      }
    }

    tot_len+= part_store_len;
1805
  }
1806
  op->end_of_bound(range_no);
1807 1808 1809
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
1810
/*
1811
  Start ordered index scan in NDB
unknown's avatar
unknown committed
1812 1813
*/

1814
int ha_ndbcluster::ordered_index_scan(const key_range *start_key,
1815 1816
                                      const key_range *end_key,
                                      bool sorted, bool descending, byte* buf)
unknown's avatar
unknown committed
1817
{  
1818
  int res;
unknown's avatar
unknown committed
1819
  bool restart;
1820
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
1821
  NdbIndexScanOperation *op;
1822

1823 1824 1825
  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
1826
  DBUG_PRINT("enter", ("Starting new ordered scan on %s", m_tabname));
unknown's avatar
unknown committed
1827

1828 1829
  // Check that sorted seems to be initialised
  DBUG_ASSERT(sorted == 0 || sorted == 1);
unknown's avatar
unknown committed
1830
  
1831
  if (m_active_cursor == 0)
unknown's avatar
unknown committed
1832
  {
unknown's avatar
unknown committed
1833
    restart= FALSE;
unknown's avatar
unknown committed
1834 1835 1836
    NdbOperation::LockMode lm=
      (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
    if (!(op= trans->getNdbIndexScanOperation((NDBINDEX *)
1837 1838 1839
                                              m_index[active_index].index, 
                                              (const NDBTAB *) m_table)) ||
        op->readTuples(lm, 0, parallelism, sorted, descending))
unknown's avatar
unknown committed
1840
      ERR_RETURN(trans->getNdbError());
1841
    m_active_cursor= op;
unknown's avatar
unknown committed
1842
  } else {
unknown's avatar
unknown committed
1843
    restart= TRUE;
1844
    op= (NdbIndexScanOperation*)m_active_cursor;
unknown's avatar
unknown committed
1845 1846 1847
    
    DBUG_ASSERT(op->getSorted() == sorted);
    DBUG_ASSERT(op->getLockMode() == 
1848
                (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type));
unknown's avatar
unknown committed
1849
    if (op->reset_bounds(m_force_send))
unknown's avatar
unknown committed
1850 1851
      DBUG_RETURN(ndb_err(m_active_trans));
  }
1852
  
1853
  {
1854
    const key_range *keys[2]= { start_key, end_key };
1855 1856 1857
    res= set_bounds(op, keys);
    if (res)
      DBUG_RETURN(res);
1858
  }
1859 1860 1861

  if (!restart && generate_scan_filter(m_cond_stack, op))
    DBUG_RETURN(ndb_err(trans));
1862
  
1863
  if (!restart && (res= define_read_attrs(buf, op)))
1864
  {
1865
    DBUG_RETURN(res);
unknown's avatar
unknown committed
1866
  }
1867 1868 1869 1870 1871 1872

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

/*
1875
  Start full table scan in NDB
unknown's avatar
unknown committed
1876 1877 1878 1879
 */

int ha_ndbcluster::full_table_scan(byte *buf)
{
1880
  int res;
unknown's avatar
unknown committed
1881
  NdbScanOperation *op;
1882
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
1883 1884 1885 1886

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

1887 1888 1889
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
  if (!(op=trans->getNdbScanOperation((const NDBTAB *) m_table)) ||
1890
      op->readTuples(lm, 0, parallelism))
unknown's avatar
unknown committed
1891
    ERR_RETURN(trans->getNdbError());
1892
  m_active_cursor= op;
1893 1894
  if (generate_scan_filter(m_cond_stack, op))
    DBUG_RETURN(ndb_err(trans));
unknown's avatar
unknown committed
1895
  if ((res= define_read_attrs(buf, op)))
1896 1897 1898 1899 1900 1901
    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));
1902 1903
}

unknown's avatar
unknown committed
1904 1905 1906 1907 1908
/*
  Insert one record into NDB
*/
int ha_ndbcluster::write_row(byte *record)
{
unknown's avatar
unknown committed
1909
  bool has_auto_increment;
unknown's avatar
unknown committed
1910
  uint i;
1911
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
1912 1913
  NdbOperation *op;
  int res;
unknown's avatar
unknown committed
1914 1915
  THD *thd= current_thd;

unknown's avatar
unknown committed
1916
  DBUG_ENTER("write_row");
1917

1918
  if (m_ignore_dup_key && table->s->primary_key != MAX_KEY)
1919
  {
1920
    int peek_res= peek_row(record);
1921 1922 1923
    
    if (!peek_res) 
    {
1924
      m_dupkey= table->s->primary_key;
1925 1926 1927 1928
      DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
    }
    if (peek_res != HA_ERR_KEY_NOT_FOUND)
      DBUG_RETURN(peek_res);
1929
  }
1930

unknown's avatar
unknown committed
1931
  statistic_increment(thd->status_var.ha_write_count, &LOCK_status);
unknown's avatar
unknown committed
1932 1933
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
    table->timestamp_field->set_time();
1934
  has_auto_increment= (table->next_number_field && record == table->record[0]);
unknown's avatar
unknown committed
1935

unknown's avatar
unknown committed
1936
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)))
unknown's avatar
unknown committed
1937 1938 1939 1940 1941 1942
    ERR_RETURN(trans->getNdbError());

  res= (m_use_write) ? op->writeTuple() :op->insertTuple(); 
  if (res != 0)
    ERR_RETURN(trans->getNdbError());  
 
1943
  if (table->s->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
1944 1945
  {
    // Table has hidden primary key
1946
    Ndb *ndb= get_ndb();
1947 1948 1949 1950 1951 1952 1953
    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);
1954 1955
    if (auto_value == NDB_FAILED_AUTO_INCREMENT)
      ERR_RETURN(ndb->getNdbError());
1956
    if (set_hidden_key(op, table->s->fields, (const byte*)&auto_value))
unknown's avatar
unknown committed
1957 1958 1959 1960 1961
      ERR_RETURN(op->getNdbError());
  } 
  else 
  {
    int res;
1962

1963 1964
    if (has_auto_increment) 
    {
unknown's avatar
unknown committed
1965 1966
      THD *thd= table->in_use;

1967
      m_skip_auto_increment= FALSE;
1968
      update_auto_increment();
unknown's avatar
unknown committed
1969 1970
      /* Ensure that handler is always called for auto_increment values */
      thd->next_insert_id= 0;
1971
      m_skip_auto_increment= !auto_increment_column_changed;
1972
    }
1973

1974
    if ((res= set_primary_key_from_record(op, record)))
1975
      return res;  
unknown's avatar
unknown committed
1976 1977 1978
  }

  // Set non-key attribute(s)
unknown's avatar
unknown committed
1979
  bool set_blob_value= FALSE;
1980
  for (i= 0; i < table->s->fields; i++) 
unknown's avatar
unknown committed
1981 1982 1983
  {
    Field *field= table->field[i];
    if (!(field->flags & PRI_KEY_FLAG) &&
1984
        set_ndb_value(op, field, i, &set_blob_value))
1985
    {
1986
      m_skip_auto_increment= TRUE;
unknown's avatar
unknown committed
1987
      ERR_RETURN(op->getNdbError());
1988
    }
unknown's avatar
unknown committed
1989 1990
  }

1991 1992
  m_rows_changed++;

unknown's avatar
unknown committed
1993 1994 1995 1996 1997 1998 1999
  /*
    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!
  */
2000
  m_rows_inserted++;
2001
  no_uncommitted_rows_update(1);
2002
  m_bulk_insert_not_flushed= TRUE;
2003
  if ((m_rows_to_insert == (ha_rows) 1) || 
2004
      ((m_rows_inserted % m_bulk_insert_rows) == 0) ||
2005
      m_primary_key_update ||
2006
      set_blob_value)
2007 2008 2009
  {
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
2010 2011
                        "rows_inserted:%d, bulk_insert_rows: %d", 
                        (int)m_rows_inserted, (int)m_bulk_insert_rows));
2012

2013
    m_bulk_insert_not_flushed= FALSE;
2014
    if (m_transaction_on)
2015
    {
unknown's avatar
unknown committed
2016
      if (execute_no_commit(this,trans) != 0)
2017
      {
2018 2019 2020
        m_skip_auto_increment= TRUE;
        no_uncommitted_rows_execute_failure();
        DBUG_RETURN(ndb_err(trans));
2021
      }
2022 2023
    }
    else
2024
    {
unknown's avatar
unknown committed
2025
      if (execute_commit(this,trans) != 0)
2026
      {
2027 2028 2029
        m_skip_auto_increment= TRUE;
        no_uncommitted_rows_execute_failure();
        DBUG_RETURN(ndb_err(trans));
2030
      }
unknown's avatar
unknown committed
2031
      if (trans->restart() != 0)
2032
      {
2033 2034
        DBUG_ASSERT(0);
        DBUG_RETURN(-1);
2035
      }
2036
    }
2037
  }
2038
  if ((has_auto_increment) && (m_skip_auto_increment))
unknown's avatar
unknown committed
2039
  {
2040
    Ndb *ndb= get_ndb();
2041
    Uint64 next_val= (Uint64) table->next_number_field->val_int() + 1;
unknown's avatar
unknown committed
2042
    DBUG_PRINT("info", 
2043
               ("Trying to set next auto increment value to %lu",
2044
                (ulong) next_val));
2045
    if (ndb->setAutoIncrementValue((const NDBTAB *) m_table, next_val, TRUE))
unknown's avatar
unknown committed
2046
      DBUG_PRINT("info", 
2047
                 ("Setting next auto increment value to %u", next_val));  
2048
  }
2049
  m_skip_auto_increment= TRUE;
2050

unknown's avatar
unknown committed
2051 2052 2053 2054 2055 2056 2057
  DBUG_RETURN(0);
}


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

int ha_ndbcluster::key_cmp(uint keynr, const byte * old_row,
2058
                           const byte * new_row)
unknown's avatar
unknown committed
2059 2060 2061 2062 2063 2064 2065 2066 2067
{
  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) !=
2068 2069
          (new_row[key_part->null_offset] & key_part->null_bit))
        return 1;
unknown's avatar
unknown committed
2070
    }
2071
    if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
unknown's avatar
unknown committed
2072 2073 2074
    {

      if (key_part->field->cmp_binary((char*) (old_row + key_part->offset),
2075 2076 2077
                                      (char*) (new_row + key_part->offset),
                                      (ulong) key_part->length))
        return 1;
unknown's avatar
unknown committed
2078 2079 2080 2081
    }
    else
    {
      if (memcmp(old_row+key_part->offset, new_row+key_part->offset,
2082 2083
                 key_part->length))
        return 1;
unknown's avatar
unknown committed
2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095
    }
  }
  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;
2096
  NdbTransaction *trans= m_active_trans;
2097
  NdbScanOperation* cursor= m_active_cursor;
unknown's avatar
unknown committed
2098 2099 2100 2101
  NdbOperation *op;
  uint i;
  DBUG_ENTER("update_row");
  
unknown's avatar
unknown committed
2102
  statistic_increment(thd->status_var.ha_update_count, &LOCK_status);
unknown's avatar
unknown committed
2103
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
2104
  {
unknown's avatar
unknown committed
2105
    table->timestamp_field->set_time();
2106 2107 2108
    // Set query_id so that field is really updated
    table->timestamp_field->query_id= thd->query_id;
  }
unknown's avatar
unknown committed
2109

2110
  /* Check for update of primary key for special handling */  
2111 2112
  if ((table->s->primary_key != MAX_KEY) &&
      (key_cmp(table->s->primary_key, old_data, new_data)))
2113
  {
2114
    int read_res, insert_res, delete_res, undo_res;
2115

2116
    DBUG_PRINT("info", ("primary key update, doing pk read+delete+insert"));
2117
    // Get all old fields, since we optimize away fields not in query
2118
    read_res= complemented_pk_read(old_data, new_data);
2119 2120 2121 2122 2123
    if (read_res)
    {
      DBUG_PRINT("info", ("pk read failed"));
      DBUG_RETURN(read_res);
    }
2124
    // Delete old row
2125
    m_primary_key_update= TRUE;
2126
    delete_res= delete_row(old_data);
2127
    m_primary_key_update= FALSE;
2128 2129 2130
    if (delete_res)
    {
      DBUG_PRINT("info", ("delete failed"));
2131
      DBUG_RETURN(delete_res);
2132
    }     
2133 2134
    // Insert new row
    DBUG_PRINT("info", ("delete succeded"));
2135
    m_primary_key_update= TRUE;
2136
    insert_res= write_row(new_data);
2137
    m_primary_key_update= FALSE;
2138 2139 2140 2141 2142
    if (insert_res)
    {
      DBUG_PRINT("info", ("insert failed"));
      if (trans->commitStatus() == NdbConnection::Started)
      {
2143
        // Undo delete_row(old_data)
2144
        m_primary_key_update= TRUE;
2145 2146 2147 2148 2149 2150
        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");
2151 2152 2153 2154 2155
        m_primary_key_update= FALSE;
      }
      DBUG_RETURN(insert_res);
    }
    DBUG_PRINT("info", ("delete+insert succeeded"));
2156
    DBUG_RETURN(0);
2157
  }
2158

2159
  if (cursor)
unknown's avatar
unknown committed
2160
  {
2161 2162 2163 2164 2165 2166 2167 2168
    /*
      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"));
2169
    if (!(op= cursor->updateCurrentTuple()))
2170
      ERR_RETURN(trans->getNdbError());
2171
    m_ops_pending++;
unknown's avatar
unknown committed
2172
    if (uses_blob_value(FALSE))
2173
      m_blobs_pending= TRUE;
2174 2175 2176
  }
  else
  {  
unknown's avatar
unknown committed
2177
    if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) ||
2178
        op->updateTuple() != 0)
2179 2180
      ERR_RETURN(trans->getNdbError());  
    
2181
    if (table->s->primary_key == MAX_KEY) 
2182 2183 2184 2185 2186 2187
    {
      // 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
2188
      uint no_fields= table->s->fields;
2189
      const NdbRecAttr* rec= m_value[no_fields].rec;
2190 2191 2192 2193
      DBUG_ASSERT(rec);
      DBUG_DUMP("key", (char*)rec->aRef(), NDB_HIDDEN_PRIMARY_KEY_LENGTH);
      
      if (set_hidden_key(op, no_fields, rec->aRef()))
2194
        ERR_RETURN(op->getNdbError());
2195 2196 2197 2198
    } 
    else 
    {
      int res;
2199
      if ((res= set_primary_key_from_record(op, old_data)))
2200
        DBUG_RETURN(res);
2201
    }
unknown's avatar
unknown committed
2202 2203
  }

2204 2205
  m_rows_changed++;

unknown's avatar
unknown committed
2206
  // Set non-key attribute(s)
2207
  for (i= 0; i < table->s->fields; i++) 
unknown's avatar
unknown committed
2208 2209
  {
    Field *field= table->field[i];
2210
    if (((thd->query_id == field->query_id) || m_retrieve_all_fields) &&
unknown's avatar
unknown committed
2211
        (!(field->flags & PRI_KEY_FLAG)) &&
2212
        set_ndb_value(op, field, i))
unknown's avatar
unknown committed
2213 2214
      ERR_RETURN(op->getNdbError());
  }
2215

unknown's avatar
unknown committed
2216
  // Execute update operation
unknown's avatar
unknown committed
2217
  if (!cursor && execute_no_commit(this,trans) != 0) {
2218
    no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
2219
    DBUG_RETURN(ndb_err(trans));
2220
  }
unknown's avatar
unknown committed
2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231
  
  DBUG_RETURN(0);
}


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

int ha_ndbcluster::delete_row(const byte *record)
{
unknown's avatar
unknown committed
2232
  THD *thd= current_thd;
2233
  NdbTransaction *trans= m_active_trans;
2234
  NdbScanOperation* cursor= m_active_cursor;
unknown's avatar
unknown committed
2235 2236 2237
  NdbOperation *op;
  DBUG_ENTER("delete_row");

unknown's avatar
unknown committed
2238
  statistic_increment(thd->status_var.ha_delete_count,&LOCK_status);
unknown's avatar
unknown committed
2239
  m_rows_changed++;
unknown's avatar
unknown committed
2240

2241
  if (cursor)
unknown's avatar
unknown committed
2242
  {
2243
    /*
2244
      We are scanning records and want to delete the record
2245
      that was just found, call deleteTuple on the cursor 
2246
      to take over the lock to a new delete operation
2247 2248 2249 2250
      And thus setting the primary key of the record from 
      the active record in cursor
    */
    DBUG_PRINT("info", ("Calling deleteTuple on cursor"));
2251
    if (cursor->deleteCurrentTuple() != 0)
2252
      ERR_RETURN(trans->getNdbError());     
2253
    m_ops_pending++;
unknown's avatar
unknown committed
2254

2255 2256
    no_uncommitted_rows_update(-1);

2257 2258 2259
    if (!m_primary_key_update)
      // If deleting from cursor, NoCommit will be handled in next_result
      DBUG_RETURN(0);
2260 2261
  }
  else
unknown's avatar
unknown committed
2262
  {
2263
    
unknown's avatar
unknown committed
2264
    if (!(op=trans->getNdbOperation((const NDBTAB *) m_table)) || 
2265
        op->deleteTuple() != 0)
2266 2267
      ERR_RETURN(trans->getNdbError());
    
2268 2269
    no_uncommitted_rows_update(-1);
    
2270
    if (table->s->primary_key == MAX_KEY) 
2271 2272 2273
    {
      // This table has no primary key, use "hidden" primary key
      DBUG_PRINT("info", ("Using hidden key"));
2274
      uint no_fields= table->s->fields;
2275
      const NdbRecAttr* rec= m_value[no_fields].rec;
2276 2277 2278
      DBUG_ASSERT(rec != NULL);
      
      if (set_hidden_key(op, no_fields, rec->aRef()))
2279
        ERR_RETURN(op->getNdbError());
2280 2281 2282 2283
    } 
    else 
    {
      int res;
2284 2285
      if ((res= set_primary_key_from_record(op, record)))
        return res;  
2286
    }
unknown's avatar
unknown committed
2287
  }
2288

unknown's avatar
unknown committed
2289
  // Execute delete operation
unknown's avatar
unknown committed
2290
  if (execute_no_commit(this,trans) != 0) {
2291
    no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
2292
    DBUG_RETURN(ndb_err(trans));
2293
  }
unknown's avatar
unknown committed
2294 2295
  DBUG_RETURN(0);
}
2296
  
unknown's avatar
unknown committed
2297 2298 2299 2300 2301
/*
  Unpack a record read from NDB 

  SYNOPSIS
    unpack_record()
2302
    buf                 Buffer to store read row
unknown's avatar
unknown committed
2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314

  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
2315
  NdbValue *value= m_value;
unknown's avatar
unknown committed
2316
  DBUG_ENTER("unpack_record");
2317

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

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

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

#ifndef DBUG_OFF
2395
  const NDBTAB *tab= (const NDBTAB*) m_table;
2396

unknown's avatar
unknown committed
2397 2398
  if (!_db_on_)
    DBUG_VOID_RETURN;
unknown's avatar
Merge  
unknown committed
2399

2400
  char buf_type[MAX_FIELD_WIDTH], buf_val[MAX_FIELD_WIDTH];
unknown's avatar
Merge  
unknown committed
2401
  String type(buf_type, sizeof(buf_type), &my_charset_bin);
2402
  String val(buf_val, sizeof(buf_val), &my_charset_bin);
2403
  for (uint f= 0; f < table->s->fields; f++)
unknown's avatar
unknown committed
2404
  {
unknown's avatar
Merge  
unknown committed
2405
    /* Use DBUG_PRINT since DBUG_FILE cannot be filtered out */
2406
    char buf[2000];
unknown's avatar
unknown committed
2407
    Field *field;
2408
    void* ptr;
unknown's avatar
unknown committed
2409
    NdbValue value;
unknown's avatar
unknown committed
2410

2411
    buf[0]= 0;
unknown's avatar
Merge  
unknown committed
2412
    field= table->field[f];
unknown's avatar
unknown committed
2413
    if (!(value= m_value[f]).ptr)
unknown's avatar
unknown committed
2414
    {
unknown's avatar
unknown committed
2415
      strmov(buf, "not read");
2416
      goto print_value;
unknown's avatar
unknown committed
2417
    }
2418

2419
    ptr= field->ptr;
unknown's avatar
unknown committed
2420 2421

    if (! (field->flags & BLOB_FLAG))
unknown's avatar
unknown committed
2422
    {
unknown's avatar
unknown committed
2423 2424
      if (value.rec->isNULL())
      {
unknown's avatar
unknown committed
2425
        strmov(buf, "NULL");
2426
        goto print_value;
unknown's avatar
unknown committed
2427
      }
2428 2429 2430 2431 2432
      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
2433 2434 2435
    }
    else
    {
2436
      NdbBlob *ndb_blob= value.blob;
unknown's avatar
unknown committed
2437
      bool isNull= TRUE;
unknown's avatar
unknown committed
2438
      ndb_blob->getNull(isNull);
unknown's avatar
unknown committed
2439 2440
      if (isNull)
        strmov(buf, "NULL");
unknown's avatar
unknown committed
2441
    }
unknown's avatar
Merge  
unknown committed
2442

2443
print_value:
unknown's avatar
Merge  
unknown committed
2444
    DBUG_PRINT("value", ("%u,%s: %s", f, field->field_name, buf));
unknown's avatar
unknown committed
2445 2446 2447 2448 2449 2450 2451 2452
  }
#endif
  DBUG_VOID_RETURN;
}


int ha_ndbcluster::index_init(uint index)
{
2453
  DBUG_ENTER("ha_ndbcluster::index_init");
unknown's avatar
unknown committed
2454 2455 2456 2457 2458 2459 2460
  DBUG_PRINT("enter", ("index: %u", index));
  DBUG_RETURN(handler::index_init(index));
}


int ha_ndbcluster::index_end()
{
2461
  DBUG_ENTER("ha_ndbcluster::index_end");
2462
  DBUG_RETURN(close_scan());
unknown's avatar
unknown committed
2463 2464
}

2465 2466 2467 2468 2469 2470 2471 2472
/**
 * 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;
2473
  const byte* end_ptr= key + key_len;
2474 2475 2476 2477 2478 2479
  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
2480
    if (curr_part->null_bit && *key)
2481 2482 2483 2484 2485 2486
      return 1;

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

int ha_ndbcluster::index_read(byte *buf,
2489 2490
                              const byte *key, uint key_len, 
                              enum ha_rkey_function find_flag)
unknown's avatar
unknown committed
2491
{
2492
  DBUG_ENTER("ha_ndbcluster::index_read");
unknown's avatar
unknown committed
2493 2494 2495
  DBUG_PRINT("enter", ("active_index: %u, key_len: %u, find_flag: %d", 
                       active_index, key_len, find_flag));

unknown's avatar
unknown committed
2496
  int error;
2497 2498
  ndb_index_type type= get_index_type(active_index);
  const KEY* key_info= table->key_info+active_index;
unknown's avatar
unknown committed
2499 2500 2501 2502 2503
  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
2504
      if (m_active_cursor && (error= close_scan()))
2505
        DBUG_RETURN(error);
unknown's avatar
unknown committed
2506 2507 2508 2509 2510 2511 2512 2513 2514
      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:
2515
    if (find_flag == HA_READ_KEY_EXACT && key_info->key_length == key_len &&
2516
        !check_null_in_key(key_info, key, key_len))
unknown's avatar
unknown committed
2517
    {
unknown's avatar
unknown committed
2518
      if (m_active_cursor && (error= close_scan()))
2519
        DBUG_RETURN(error);
unknown's avatar
unknown committed
2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530
      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
2531
    DBUG_ASSERT(FALSE);
2532
    DBUG_RETURN(1);
unknown's avatar
unknown committed
2533 2534 2535
    break;
  }
  
2536
  key_range start_key;
2537 2538 2539
  start_key.key= key;
  start_key.length= key_len;
  start_key.flag= find_flag;
2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551
  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
2552
  DBUG_RETURN(error == HA_ERR_END_OF_FILE ? HA_ERR_KEY_NOT_FOUND : error);
unknown's avatar
unknown committed
2553 2554 2555 2556
}


int ha_ndbcluster::index_read_idx(byte *buf, uint index_no, 
2557 2558
                              const byte *key, uint key_len, 
                              enum ha_rkey_function find_flag)
unknown's avatar
unknown committed
2559
{
unknown's avatar
unknown committed
2560
  statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status);
2561
  DBUG_ENTER("ha_ndbcluster::index_read_idx");
unknown's avatar
unknown committed
2562 2563 2564 2565 2566 2567 2568 2569
  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)
{
2570
  DBUG_ENTER("ha_ndbcluster::index_next");
unknown's avatar
unknown committed
2571
  statistic_increment(current_thd->status_var.ha_read_next_count,
2572
                      &LOCK_status);
2573
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
2574 2575 2576 2577 2578
}


int ha_ndbcluster::index_prev(byte *buf)
{
2579
  DBUG_ENTER("ha_ndbcluster::index_prev");
unknown's avatar
unknown committed
2580
  statistic_increment(current_thd->status_var.ha_read_prev_count,
2581
                      &LOCK_status);
2582
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
2583 2584 2585 2586 2587
}


int ha_ndbcluster::index_first(byte *buf)
{
2588
  DBUG_ENTER("ha_ndbcluster::index_first");
unknown's avatar
unknown committed
2589
  statistic_increment(current_thd->status_var.ha_read_first_count,
2590
                      &LOCK_status);
unknown's avatar
unknown committed
2591 2592 2593
  // Start the ordered index scan and fetch the first row

  // Only HA_READ_ORDER indexes get called by index_first
2594
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, FALSE, buf));
unknown's avatar
unknown committed
2595 2596 2597 2598 2599
}


int ha_ndbcluster::index_last(byte *buf)
{
2600
  DBUG_ENTER("ha_ndbcluster::index_last");
2601
  statistic_increment(current_thd->status_var.ha_read_last_count,&LOCK_status);
2602
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, TRUE, buf));
unknown's avatar
unknown committed
2603 2604
}

2605 2606 2607 2608 2609
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
2610

2611 2612
inline
int ha_ndbcluster::read_range_first_to_buf(const key_range *start_key,
2613 2614 2615
                                           const key_range *end_key,
                                           bool eq_r, bool sorted,
                                           byte* buf)
2616
{
2617
  KEY* key_info;
2618 2619
  int error= 1; 
  DBUG_ENTER("ha_ndbcluster::read_range_first_to_buf");
unknown's avatar
unknown committed
2620
  DBUG_PRINT("info", ("eq_r: %d, sorted: %d", eq_r, sorted));
2621

2622
  switch (get_index_type(active_index)){
2623
  case PRIMARY_KEY_ORDERED_INDEX:
2624
  case PRIMARY_KEY_INDEX:
2625 2626
    key_info= table->key_info + active_index;
    if (start_key && 
2627 2628
        start_key->length == key_info->key_length &&
        start_key->flag == HA_READ_KEY_EXACT)
2629
    {
unknown's avatar
unknown committed
2630
      if (m_active_cursor && (error= close_scan()))
2631
        DBUG_RETURN(error);
2632 2633 2634
      error= pk_read(start_key->key, start_key->length, buf);      
      DBUG_RETURN(error == HA_ERR_KEY_NOT_FOUND ? HA_ERR_END_OF_FILE : error);
    }
2635
    break;
2636
  case UNIQUE_ORDERED_INDEX:
2637
  case UNIQUE_INDEX:
2638
    key_info= table->key_info + active_index;
2639
    if (start_key && start_key->length == key_info->key_length &&
2640 2641
        start_key->flag == HA_READ_KEY_EXACT && 
        !check_null_in_key(key_info, start_key->key, start_key->length))
2642
    {
unknown's avatar
unknown committed
2643
      if (m_active_cursor && (error= close_scan()))
2644
        DBUG_RETURN(error);
2645 2646 2647
      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);
    }
2648 2649 2650 2651
    break;
  default:
    break;
  }
2652 2653

  // Start the ordered index scan and fetch the first row
2654
  error= ordered_index_scan(start_key, end_key, sorted, FALSE, buf);
2655 2656 2657
  DBUG_RETURN(error);
}

2658

unknown's avatar
unknown committed
2659
int ha_ndbcluster::read_range_first(const key_range *start_key,
2660 2661
                                    const key_range *end_key,
                                    bool eq_r, bool sorted)
unknown's avatar
unknown committed
2662 2663 2664 2665 2666
{
  byte* buf= table->record[0];
  DBUG_ENTER("ha_ndbcluster::read_range_first");
  
  DBUG_RETURN(read_range_first_to_buf(start_key,
2667 2668 2669 2670
                                      end_key,
                                      eq_r, 
                                      sorted,
                                      buf));
unknown's avatar
unknown committed
2671 2672
}

2673
int ha_ndbcluster::read_range_next()
2674 2675 2676 2677 2678 2679
{
  DBUG_ENTER("ha_ndbcluster::read_range_next");
  DBUG_RETURN(next_result(table->record[0]));
}


unknown's avatar
unknown committed
2680 2681
int ha_ndbcluster::rnd_init(bool scan)
{
2682
  NdbScanOperation *cursor= m_active_cursor;
unknown's avatar
unknown committed
2683 2684
  DBUG_ENTER("rnd_init");
  DBUG_PRINT("enter", ("scan: %d", scan));
2685
  // Check if scan is to be restarted
unknown's avatar
unknown committed
2686 2687 2688 2689
  if (cursor)
  {
    if (!scan)
      DBUG_RETURN(1);
unknown's avatar
unknown committed
2690
    if (cursor->restart(m_force_send) != 0)
2691 2692 2693 2694
    {
      DBUG_ASSERT(0);
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
2695
  }
2696
  index_init(table->s->primary_key);
unknown's avatar
unknown committed
2697 2698 2699
  DBUG_RETURN(0);
}

2700 2701
int ha_ndbcluster::close_scan()
{
2702
  NdbTransaction *trans= m_active_trans;
2703 2704
  DBUG_ENTER("close_scan");

unknown's avatar
unknown committed
2705 2706
  m_multi_cursor= 0;
  if (!m_active_cursor && !m_multi_cursor)
2707 2708
    DBUG_RETURN(1);

unknown's avatar
unknown committed
2709
  NdbScanOperation *cursor= m_active_cursor ? m_active_cursor : m_multi_cursor;
unknown's avatar
unknown committed
2710
  
2711
  if (m_ops_pending)
unknown's avatar
unknown committed
2712 2713 2714 2715 2716
  {
    /*
      Take over any pending transactions to the 
      deleteing/updating transaction before closing the scan    
    */
2717
    DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending));    
unknown's avatar
unknown committed
2718
    if (execute_no_commit(this,trans) != 0) {
2719
      no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
2720
      DBUG_RETURN(ndb_err(trans));
2721
    }
2722
    m_ops_pending= 0;
unknown's avatar
unknown committed
2723 2724
  }
  
unknown's avatar
unknown committed
2725
  cursor->close(m_force_send, TRUE);
unknown's avatar
unknown committed
2726
  m_active_cursor= m_multi_cursor= NULL;
unknown's avatar
unknown committed
2727
  DBUG_RETURN(0);
2728
}
unknown's avatar
unknown committed
2729 2730 2731 2732

int ha_ndbcluster::rnd_end()
{
  DBUG_ENTER("rnd_end");
2733
  DBUG_RETURN(close_scan());
unknown's avatar
unknown committed
2734 2735 2736 2737 2738 2739
}


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

unknown's avatar
unknown committed
2743
  if (!m_active_cursor)
2744 2745
    DBUG_RETURN(full_table_scan(buf));
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758
}


/*
  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
2759
  statistic_increment(current_thd->status_var.ha_read_rnd_count,
2760
                      &LOCK_status);
unknown's avatar
unknown committed
2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780
  // 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");

2781
  if (table->s->primary_key != MAX_KEY) 
unknown's avatar
unknown committed
2782
  {
2783
    key_info= table->key_info + table->s->primary_key;
unknown's avatar
unknown committed
2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806
    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;
      }
      memcpy(buff, record + key_part->offset, key_part->length);
      buff += key_part->length;
    }
  } 
  else 
  {
    // No primary key, get hidden key
    DBUG_PRINT("info", ("Getting hidden key"));
2807
    int hidden_no= table->s->fields;
2808
    const NdbRecAttr* rec= m_value[hidden_no].rec;
2809 2810
    memcpy(ref, (const void*)rec->aRef(), ref_length);
#ifndef DBUG_OFF
unknown's avatar
unknown committed
2811
    const NDBTAB *tab= (const NDBTAB *) m_table;  
unknown's avatar
unknown committed
2812 2813 2814 2815 2816
    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);
2817
#endif
unknown's avatar
unknown committed
2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836
  }
  
  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)
2837
  {
unknown's avatar
unknown committed
2838
    DBUG_PRINT("info", ("HA_STATUS_VARIABLE"));
2839 2840
    if (m_table_info)
    {
2841
      if (m_ha_not_exact_count)
2842
        records= 100;
2843
      else
2844
        records_update();
2845 2846 2847
    }
    else
    {
2848 2849 2850
      if ((my_errno= check_ndb_connection()))
        DBUG_VOID_RETURN;
      Ndb *ndb= get_ndb();
2851 2852
      struct Ndb_statistics stat;
      if (current_thd->variables.ndb_use_exact_count &&
2853
          ndb_get_table_statistics(ndb, m_tabname, &stat) == 0)
2854
      {
2855 2856 2857
        mean_rec_length= stat.row_size;
        data_file_length= stat.fragment_memory;
        records= stat.row_count;
2858 2859 2860
      }
      else
      {
2861 2862
        mean_rec_length= 0;
        records= 100;
2863
      }
2864
    }
2865
  }
unknown's avatar
unknown committed
2866 2867 2868 2869 2870
  if (flag & HA_STATUS_CONST)
  {
    DBUG_PRINT("info", ("HA_STATUS_CONST"));
    set_rec_per_key();
  }
unknown's avatar
unknown committed
2871
  if (flag & HA_STATUS_ERRKEY)
2872
  {
unknown's avatar
unknown committed
2873
    DBUG_PRINT("info", ("HA_STATUS_ERRKEY"));
2874
    errkey= m_dupkey;
2875
  }
unknown's avatar
unknown committed
2876
  if (flag & HA_STATUS_AUTO)
2877
  {
unknown's avatar
unknown committed
2878
    DBUG_PRINT("info", ("HA_STATUS_AUTO"));
2879 2880 2881 2882 2883 2884 2885 2886
    if (m_table)
    {
      Ndb *ndb= get_ndb();
      
      auto_increment_value= 
        ndb->readAutoIncrementValue((const NDBTAB *) m_table);
    }
  }
unknown's avatar
unknown committed
2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902
  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"));
2903 2904
    DBUG_PRINT("info", ("Clearing condition stack"));
    cond_clear();
unknown's avatar
unknown committed
2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 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
    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"));
2974 2975 2976 2977 2978 2979
    if (current_thd->lex->sql_command == SQLCOM_REPLACE)
    {
      DBUG_PRINT("info", ("Turning ON use of write instead of insert"));
      m_use_write= TRUE;
    } else 
    {
2980 2981
      DBUG_PRINT("info", ("Ignoring duplicate key"));
      m_ignore_dup_key= TRUE;
2982
    }
unknown's avatar
unknown committed
2983 2984 2985 2986
    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
2987
    m_use_write= FALSE;
2988
    m_ignore_dup_key= FALSE;
unknown's avatar
unknown committed
2989 2990
    break;
  case HA_EXTRA_RETRIEVE_ALL_COLS:    /* Retrieve all columns, not just those
2991 2992
                                         where field->query_id is the same as
                                         the current query id */
unknown's avatar
unknown committed
2993
    DBUG_PRINT("info", ("HA_EXTRA_RETRIEVE_ALL_COLS"));
2994
    m_retrieve_all_fields= TRUE;
unknown's avatar
unknown committed
2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006
    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"));
3007
    m_retrieve_primary_key= TRUE;
unknown's avatar
unknown committed
3008 3009 3010 3011 3012 3013
    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"));
3014 3015
  case HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
    DBUG_PRINT("info", ("HA_EXTRA_KEYREAD_PRESERVE_FIELDS"));
unknown's avatar
unknown committed
3016 3017 3018 3019 3020 3021 3022
    break;

  }
  
  DBUG_RETURN(0);
}

3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035
/* 
   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
3036
  const NDBTAB *tab= (const NDBTAB *) m_table;    
3037 3038

  DBUG_ENTER("start_bulk_insert");
unknown's avatar
unknown committed
3039
  DBUG_PRINT("enter", ("rows: %d", (int)rows));
3040
  
3041 3042
  m_rows_inserted= (ha_rows) 0;
  if (rows == (ha_rows) 0)
unknown's avatar
unknown committed
3043
  {
3044 3045
    /* We don't know how many will be inserted, guess */
    m_rows_to_insert= m_autoincrement_prefetch;
unknown's avatar
unknown committed
3046
  }
3047 3048
  else
    m_rows_to_insert= rows; 
3049 3050 3051 3052 3053 3054 3055 3056

  /* 
    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.   
  */
3057
  const int bytesperbatch= 8192;
3058
  bytes= 12 + tab->getRowSizeInBytes() + 4 * tab->getNoOfColumns();
3059
  batch= bytesperbatch/bytes;
3060 3061
  batch= batch == 0 ? 1 : batch;
  DBUG_PRINT("info", ("batch: %d, bytes: %d", batch, bytes));
3062
  m_bulk_insert_rows= batch;
3063 3064 3065 3066 3067 3068 3069 3070 3071

  DBUG_VOID_RETURN;
}

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

3074
  DBUG_ENTER("end_bulk_insert");
3075
  // Check if last inserts need to be flushed
3076
  if (m_bulk_insert_not_flushed)
3077
  {
3078
    NdbTransaction *trans= m_active_trans;
3079 3080 3081
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
                        "rows_inserted:%d, bulk_insert_rows: %d", 
3082
                        (int) m_rows_inserted, (int) m_bulk_insert_rows)); 
3083
    m_bulk_insert_not_flushed= FALSE;
unknown's avatar
unknown committed
3084
    if (execute_no_commit(this,trans) != 0) {
3085
      no_uncommitted_rows_execute_failure();
3086
      my_errno= error= ndb_err(trans);
3087
    }
3088 3089
  }

3090 3091
  m_rows_inserted= (ha_rows) 0;
  m_rows_to_insert= (ha_rows) 1;
3092
  DBUG_RETURN(error);
3093 3094
}

unknown's avatar
unknown committed
3095 3096 3097 3098

int ha_ndbcluster::extra_opt(enum ha_extra_function operation, ulong cache_size)
{
  DBUG_ENTER("extra_opt");
unknown's avatar
unknown committed
3099
  DBUG_PRINT("enter", ("cache_size: %lu", cache_size));
unknown's avatar
unknown committed
3100 3101 3102
  DBUG_RETURN(extra(operation));
}

unknown's avatar
unknown committed
3103 3104 3105 3106
static const char *ha_ndbcluster_exts[] = {
 ha_ndb_ext,
 NullS
};
unknown's avatar
unknown committed
3107

3108
const char** ha_ndbcluster::bas_ext() const
unknown's avatar
unknown committed
3109 3110 3111
{
  return ha_ndbcluster_exts;
}
unknown's avatar
unknown committed
3112 3113 3114 3115 3116 3117 3118 3119 3120

/*
  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
3121 3122 3123
  DBUG_ENTER("ha_ndbcluster::scan_time()");
  double res= rows2double(records*1000);
  DBUG_PRINT("exit", ("table: %s value: %f", 
3124
                      m_tabname, res));
unknown's avatar
unknown committed
3125
  DBUG_RETURN(res);
unknown's avatar
unknown committed
3126 3127
}

unknown's avatar
unknown committed
3128 3129 3130 3131 3132 3133 3134
/*
  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
3135 3136 3137 3138 3139 3140 3141 3142

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
3143

unknown's avatar
unknown committed
3144 3145 3146
    /* If we are not doing a LOCK TABLE, then allow multiple
       writers */
    
3147 3148 3149
    /* Since NDB does not currently have table locks
       this is treated as a ordinary lock */

3150
    if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
unknown's avatar
unknown committed
3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165
         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;
3166 3167

  DBUG_PRINT("exit", ("lock_type: %d", lock_type));
unknown's avatar
unknown committed
3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189
  
  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
3190
  for the statement, this will be stored in thd_ndb.stmt.
unknown's avatar
unknown committed
3191
  If not, we have to start a master transaction if there doesn't exist
3192
  one from before, this will be stored in thd_ndb.all
unknown's avatar
unknown committed
3193 3194 3195
 
  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  
3196
  If we are locking the table then:
3197
  - save the NdbDictionary::Table for easy access
3198 3199
  - save reference to table statistics
  - refresh list of the indexes for the table if needed (if altered)
unknown's avatar
unknown committed
3200 3201 3202 3203 3204
 */

int ha_ndbcluster::external_lock(THD *thd, int lock_type)
{
  int error=0;
3205
  NdbTransaction* trans= NULL;
unknown's avatar
unknown committed
3206 3207 3208 3209 3210 3211

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

3215
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
3216
  Ndb *ndb= thd_ndb->ndb;
3217

3218 3219
  DBUG_PRINT("enter", ("thd: %x, thd_ndb: %x, thd_ndb->lock_count: %d",
                       thd, thd_ndb, thd_ndb->lock_count));
3220

unknown's avatar
unknown committed
3221 3222
  if (lock_type != F_UNLCK)
  {
3223
    DBUG_PRINT("info", ("lock_type != F_UNLCK"));
3224
    if (!thd_ndb->lock_count++)
unknown's avatar
unknown committed
3225 3226
    {
      PRINT_OPTION_FLAGS(thd);
3227
      if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) 
unknown's avatar
unknown committed
3228 3229
      {
        // Autocommit transaction
3230
        DBUG_ASSERT(!thd_ndb->stmt);
unknown's avatar
unknown committed
3231 3232
        DBUG_PRINT("trans",("Starting transaction stmt"));      

3233
        trans= ndb->startTransaction();
unknown's avatar
unknown committed
3234
        if (trans == NULL)
3235
          ERR_RETURN(ndb->getNdbError());
3236
        no_uncommitted_rows_reset(thd);
3237 3238
        thd_ndb->stmt= trans;
        trans_register_ha(thd, FALSE, &ndbcluster_hton);
unknown's avatar
unknown committed
3239 3240 3241
      } 
      else 
      { 
3242
        if (!thd_ndb->all)
3243
        {
unknown's avatar
unknown committed
3244 3245 3246 3247
          // Not autocommit transaction
          // A "master" transaction ha not been started yet
          DBUG_PRINT("trans",("starting transaction, all"));
          
3248
          trans= ndb->startTransaction();
unknown's avatar
unknown committed
3249
          if (trans == NULL)
3250
            ERR_RETURN(ndb->getNdbError());
3251
          no_uncommitted_rows_reset(thd);
3252 3253
          thd_ndb->all= trans; 
          trans_register_ha(thd, TRUE, &ndbcluster_hton);
unknown's avatar
unknown committed
3254 3255 3256 3257 3258 3259 3260 3261

          /*
            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))
3262
          {
unknown's avatar
unknown committed
3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281
            //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. 
     */

3282 3283 3284
    // 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;
3285 3286
    m_autoincrement_prefetch= 
      (ha_rows) thd->variables.ndb_autoincrement_prefetch_sz;
3287 3288 3289 3290 3291
    if (!thd->transaction.on)
      m_transaction_on= FALSE;
    else
      m_transaction_on= thd->variables.ndb_use_transactions;

3292
    m_active_trans= thd_ndb->all ? thd_ndb->all : thd_ndb->stmt;
unknown's avatar
unknown committed
3293
    DBUG_ASSERT(m_active_trans);
3294
    // Start of transaction
3295
    m_rows_changed= 0;
3296
    m_retrieve_all_fields= FALSE;
3297
    m_retrieve_primary_key= FALSE;
3298
    m_ops_pending= 0;
3299
    {
3300
      NDBDICT *dict= ndb->getDictionary();
3301 3302 3303
      const NDBTAB *tab;
      void *tab_info;
      if (!(tab= dict->getTable(m_tabname, &tab_info)))
3304
        ERR_RETURN(dict->getNdbError());
3305 3306 3307
      DBUG_PRINT("info", ("Table schema version: %d", 
                          tab->getObjectVersion()));
      // Check if thread has stale local cache
3308 3309 3310 3311
      // New transaction must not use old tables... (trans != 0)
      // Running might...
      if ((trans && tab->getObjectStatus() != NdbDictionary::Object::Retrieved)
	  || tab->getObjectStatus() == NdbDictionary::Object::Invalid)
3312 3313
      {
        invalidate_dictionary_cache(FALSE);
3314
        if (!(tab= dict->getTable(m_tabname, &tab_info)))
3315 3316 3317 3318 3319
          ERR_RETURN(dict->getNdbError());
        DBUG_PRINT("info", ("Table schema version: %d", 
                            tab->getObjectVersion()));
      }
      if (m_table != (void *)tab || m_table_version < tab->getObjectVersion())
3320 3321 3322 3323 3324 3325
      {
        /*
          The table has been altered, refresh the index list
        */
        build_index_list(ndb, table, ILBP_OPEN);  
        m_table= (void *)tab;
3326
        m_table_version = tab->getObjectVersion();
3327
      }
3328 3329
      m_table_info= tab_info;
    }
3330
    no_uncommitted_rows_init(thd);
3331 3332
  }
  else
unknown's avatar
unknown committed
3333
  {
3334
    DBUG_PRINT("info", ("lock_type == F_UNLCK"));
3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352

    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);
    }

3353
    if (!--thd_ndb->lock_count)
unknown's avatar
unknown committed
3354 3355 3356 3357
    {
      DBUG_PRINT("trans", ("Last external_lock"));
      PRINT_OPTION_FLAGS(thd);

3358
      if (thd_ndb->stmt)
unknown's avatar
unknown committed
3359 3360 3361 3362 3363 3364 3365
      {
        /*
          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"));
3366
        ndb->closeTransaction(m_active_trans);
3367
        thd_ndb->stmt= NULL;
unknown's avatar
unknown committed
3368 3369
      }
    }
unknown's avatar
unknown committed
3370
    m_table_info= NULL;
3371

3372 3373 3374 3375 3376 3377 3378 3379 3380
    /*
      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;    

3381 3382
    if (m_active_cursor)
      DBUG_PRINT("warning", ("m_active_cursor != NULL"));
3383 3384
    m_active_cursor= NULL;

unknown's avatar
unknown committed
3385 3386 3387 3388
    if (m_multi_cursor)
      DBUG_PRINT("warning", ("m_multi_cursor != NULL"));
    m_multi_cursor= NULL;
    
3389
    if (m_blobs_pending)
3390
      DBUG_PRINT("warning", ("blobs_pending != 0"));
3391
    m_blobs_pending= 0;
3392
    
3393
    if (m_ops_pending)
3394
      DBUG_PRINT("warning", ("ops_pending != 0L"));
3395
    m_ops_pending= 0;
unknown's avatar
unknown committed
3396 3397 3398 3399 3400
  }
  DBUG_RETURN(error);
}

/*
3401 3402 3403 3404 3405
  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
3406 3407
*/

unknown's avatar
unknown committed
3408
int ha_ndbcluster::start_stmt(THD *thd, thr_lock_type lock_type)
unknown's avatar
unknown committed
3409 3410 3411 3412 3413
{
  int error=0;
  DBUG_ENTER("start_stmt");
  PRINT_OPTION_FLAGS(thd);

3414
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
3415
  NdbTransaction *trans= (thd_ndb->stmt)?thd_ndb->stmt:thd_ndb->all;
unknown's avatar
unknown committed
3416
  if (!trans){
3417
    Ndb *ndb= thd_ndb->ndb;
unknown's avatar
unknown committed
3418
    DBUG_PRINT("trans",("Starting transaction stmt"));  
3419
    trans= ndb->startTransaction();
unknown's avatar
unknown committed
3420
    if (trans == NULL)
3421
      ERR_RETURN(ndb->getNdbError());
3422
    no_uncommitted_rows_reset(thd);
3423 3424
    thd_ndb->stmt= trans;
    trans_register_ha(thd, FALSE, &ndbcluster_hton);
unknown's avatar
unknown committed
3425 3426
  }
  m_active_trans= trans;
3427

3428
  // Start of statement
3429
  m_retrieve_all_fields= FALSE;
3430
  m_retrieve_primary_key= FALSE;
3431
  m_ops_pending= 0;    
unknown's avatar
unknown committed
3432 3433 3434 3435 3436 3437
  
  DBUG_RETURN(error);
}


/*
3438
  Commit a transaction started in NDB
unknown's avatar
unknown committed
3439 3440
 */

3441
int ndbcluster_commit(THD *thd, bool all)
unknown's avatar
unknown committed
3442 3443
{
  int res= 0;
3444 3445 3446
  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
3447 3448 3449

  DBUG_ENTER("ndbcluster_commit");
  DBUG_PRINT("transaction",("%s",
3450
                            trans == thd_ndb->stmt ?
unknown's avatar
unknown committed
3451 3452 3453
                            "stmt" : "all"));
  DBUG_ASSERT(ndb && trans);

3454
  if (execute_commit(thd,trans) != 0)
unknown's avatar
unknown committed
3455 3456
  {
    const NdbError err= trans->getNdbError();
3457
    const NdbOperation *error_op= trans->getNdbErrorOperation();
3458
    ERR_PRINT(err);
unknown's avatar
unknown committed
3459
    res= ndb_to_mysql_error(&err);
3460
    if (res != -1)
3461
      ndbcluster_print_error(res, error_op);
unknown's avatar
unknown committed
3462
  }
3463
  ndb->closeTransaction(trans);
3464

unknown's avatar
unknown committed
3465
  if (all)
3466 3467 3468
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;
3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482

  /* 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
3483 3484 3485 3486 3487 3488 3489 3490
  DBUG_RETURN(res);
}


/*
  Rollback a transaction started in NDB
 */

3491
int ndbcluster_rollback(THD *thd, bool all)
unknown's avatar
unknown committed
3492 3493
{
  int res= 0;
3494 3495 3496
  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
3497 3498 3499

  DBUG_ENTER("ndbcluster_rollback");
  DBUG_PRINT("transaction",("%s",
3500
                            trans == thd_ndb->stmt ? 
unknown's avatar
unknown committed
3501 3502 3503
                            "stmt" : "all"));
  DBUG_ASSERT(ndb && trans);

3504
  if (trans->execute(NdbTransaction::Rollback) != 0)
unknown's avatar
unknown committed
3505 3506
  {
    const NdbError err= trans->getNdbError();
3507
    const NdbOperation *error_op= trans->getNdbErrorOperation();
unknown's avatar
unknown committed
3508 3509
    ERR_PRINT(err);     
    res= ndb_to_mysql_error(&err);
3510 3511
    if (res != -1) 
      ndbcluster_print_error(res, error_op);
unknown's avatar
unknown committed
3512 3513
  }
  ndb->closeTransaction(trans);
3514

unknown's avatar
unknown committed
3515
  if (all)
3516 3517 3518 3519
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;

3520 3521 3522
  /* Clear list of tables changed by transaction */
  thd_ndb->changed_tables.empty();

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


/*
unknown's avatar
unknown committed
3528 3529 3530
  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
3531 3532 3533

  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
3534 3535
 */

unknown's avatar
unknown committed
3536 3537 3538
static int create_ndb_column(NDBCOL &col,
                             Field *field,
                             HA_CREATE_INFO *info)
unknown's avatar
unknown committed
3539
{
unknown's avatar
unknown committed
3540
  // Set name
unknown's avatar
unknown committed
3541
  col.setName(field->field_name);
unknown's avatar
unknown committed
3542 3543
  // Get char set
  CHARSET_INFO *cs= field->charset();
unknown's avatar
unknown committed
3544 3545 3546 3547
  // Set type and sizes
  const enum enum_field_types mysql_type= field->real_type();
  switch (mysql_type) {
  // Numeric types
unknown's avatar
unknown committed
3548
  case MYSQL_TYPE_TINY:        
unknown's avatar
unknown committed
3549 3550 3551 3552 3553 3554
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Tinyunsigned);
    else
      col.setType(NDBCOL::Tinyint);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
3555
  case MYSQL_TYPE_SHORT:
unknown's avatar
unknown committed
3556 3557 3558 3559 3560 3561
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Smallunsigned);
    else
      col.setType(NDBCOL::Smallint);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
3562
  case MYSQL_TYPE_LONG:
unknown's avatar
unknown committed
3563 3564 3565 3566 3567 3568
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Unsigned);
    else
      col.setType(NDBCOL::Int);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
3569
  case MYSQL_TYPE_INT24:       
unknown's avatar
unknown committed
3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581
    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
3582 3583
    break;
  case MYSQL_TYPE_FLOAT:
unknown's avatar
unknown committed
3584 3585 3586
    col.setType(NDBCOL::Float);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
3587
  case MYSQL_TYPE_DOUBLE:
unknown's avatar
unknown committed
3588 3589 3590
    col.setType(NDBCOL::Double);
    col.setLength(1);
    break;
3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610
  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;
3611 3612 3613
  case MYSQL_TYPE_NEWDECIMAL:    
    {
      Field_new_decimal *f= (Field_new_decimal*)field;
unknown's avatar
unknown committed
3614
      uint precision= f->precision;
3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628
      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
3629 3630 3631 3632 3633
  // Date types
  case MYSQL_TYPE_DATETIME:    
    col.setType(NDBCOL::Datetime);
    col.setLength(1);
    break;
3634 3635 3636 3637
  case MYSQL_TYPE_DATE: // ?
    col.setType(NDBCOL::Char);
    col.setLength(field->pack_length());
    break;
unknown's avatar
unknown committed
3638
  case MYSQL_TYPE_NEWDATE:
unknown's avatar
unknown committed
3639 3640 3641
    col.setType(NDBCOL::Date);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
3642
  case MYSQL_TYPE_TIME:        
unknown's avatar
unknown committed
3643 3644 3645
    col.setType(NDBCOL::Time);
    col.setLength(1);
    break;
3646 3647 3648 3649 3650 3651 3652
  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
3653 3654 3655
    break;
  // Char types
  case MYSQL_TYPE_STRING:      
3656
    if (field->pack_length() == 0)
3657 3658 3659 3660
    {
      col.setType(NDBCOL::Bit);
      col.setLength(1);
    }
unknown's avatar
unknown committed
3661
    else if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
3662
    {
unknown's avatar
unknown committed
3663
      col.setType(NDBCOL::Binary);
unknown's avatar
unknown committed
3664
      col.setLength(field->pack_length());
unknown's avatar
unknown committed
3665
    }
3666
    else
unknown's avatar
unknown committed
3667 3668 3669
    {
      col.setType(NDBCOL::Char);
      col.setCharset(cs);
3670
      col.setLength(field->pack_length());
unknown's avatar
unknown committed
3671
    }
unknown's avatar
unknown committed
3672
    break;
unknown's avatar
unknown committed
3673 3674 3675 3676 3677 3678
  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
3679
        if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
3680 3681 3682 3683 3684 3685 3686 3687
          col.setType(NDBCOL::Varbinary);
        else {
          col.setType(NDBCOL::Varchar);
          col.setCharset(cs);
        }
      }
      else if (f->length_bytes == 2)
      {
unknown's avatar
unknown committed
3688
        if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699
          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
3700
    }
unknown's avatar
unknown committed
3701 3702 3703 3704
    break;
  // Blob types (all come in as MYSQL_TYPE_BLOB)
  mysql_type_tiny_blob:
  case MYSQL_TYPE_TINY_BLOB:
unknown's avatar
unknown committed
3705
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
3706
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
3707
    else {
unknown's avatar
unknown committed
3708
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
3709 3710
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
3711 3712 3713 3714 3715
    col.setInlineSize(256);
    // No parts
    col.setPartSize(0);
    col.setStripeSize(0);
    break;
3716
  //mysql_type_blob:
3717
  case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
3718
  case MYSQL_TYPE_BLOB:    
unknown's avatar
unknown committed
3719
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
3720
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
3721
    else {
unknown's avatar
unknown committed
3722
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
3723 3724
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740
    // 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
3741
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
3742
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
3743
    else {
unknown's avatar
unknown committed
3744
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
3745 3746
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
3747 3748 3749 3750 3751 3752
    col.setInlineSize(256);
    col.setPartSize(4000);
    col.setStripeSize(8);
    break;
  mysql_type_long_blob:
  case MYSQL_TYPE_LONG_BLOB:  
unknown's avatar
unknown committed
3753
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
3754
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
3755
    else {
unknown's avatar
unknown committed
3756
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
3757 3758
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771
    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
3772 3773
  case MYSQL_TYPE_BIT:
  {
3774 3775 3776 3777 3778 3779 3780 3781
    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
3782 3783 3784 3785 3786
  case MYSQL_TYPE_NULL:        
    goto mysql_type_unsupported;
  mysql_type_unsupported:
  default:
    return HA_ERR_UNSUPPORTED;
unknown's avatar
unknown committed
3787
  }
unknown's avatar
unknown committed
3788 3789 3790 3791 3792 3793 3794 3795
  // 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 ?
3796
      info->auto_increment_value : (ulonglong) 1;
unknown's avatar
unknown committed
3797 3798
    DBUG_PRINT("info", ("Autoincrement key, initial: %llu", value));
    col.setAutoIncrementInitialValue(value);
unknown's avatar
unknown committed
3799
  }
unknown's avatar
unknown committed
3800
  else
unknown's avatar
unknown committed
3801
    col.setAutoIncrement(FALSE);
unknown's avatar
unknown committed
3802
  return 0;
unknown's avatar
unknown committed
3803 3804 3805 3806 3807 3808
}

/*
  Create a table in NDB Cluster
 */

unknown's avatar
Merge  
unknown committed
3809 3810
static void ndb_set_fragmentation(NDBTAB &tab, TABLE *form, uint pk_length)
{
unknown's avatar
Merge  
unknown committed
3811
  if (form->s->max_rows == (ha_rows) 0) /* default setting, don't set fragmentation */
unknown's avatar
Merge  
unknown committed
3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833
    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
3834
                   +1/*correct rounding*/)/2;
unknown's avatar
Merge  
unknown committed
3835 3836 3837 3838 3839 3840 3841 3842 3843
#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)
3844 3845
        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
3846 3847 3848 3849 3850 3851 3852 3853 3854
    }
    else if (no_fragments > no_nodes)
      ftype= NDBTAB::FragAllMedium;
    else
      ftype= NDBTAB::FragAllSmall;
    tab.setFragmentType(ftype);
  }
}

unknown's avatar
unknown committed
3855
int ha_ndbcluster::create(const char *name, 
3856 3857
                          TABLE *form, 
                          HA_CREATE_INFO *info)
unknown's avatar
unknown committed
3858 3859 3860
{
  NDBTAB tab;
  NDBCOL col;
unknown's avatar
unknown committed
3861
  uint pack_length, length, i, pk_length= 0;
unknown's avatar
unknown committed
3862 3863
  const void *data, *pack_data;
  char name2[FN_HEADLEN];
3864
  bool create_from_engine= (info->table_options & HA_CREATE_FROM_ENGINE);
unknown's avatar
unknown committed
3865
   
unknown's avatar
unknown committed
3866
  DBUG_ENTER("ha_ndbcluster::create");
unknown's avatar
unknown committed
3867 3868 3869
  DBUG_PRINT("enter", ("name: %s", name));
  fn_format(name2, name, "", "",2);       // Remove the .frm extension
  set_dbname(name2);
3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881
  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
3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897

  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));
  
3898
  for (i= 0; i < form->s->fields; i++) 
unknown's avatar
unknown committed
3899 3900 3901 3902
  {
    Field *field= form->field[i];
    DBUG_PRINT("info", ("name: %s, type: %u, pack_length: %d", 
                        field->field_name, field->real_type(),
3903
                        field->pack_length()));
3904
    if ((my_errno= create_ndb_column(col, field, info)))
unknown's avatar
unknown committed
3905
      DBUG_RETURN(my_errno);
unknown's avatar
unknown committed
3906
    tab.addColumn(col);
unknown's avatar
unknown committed
3907
    if (col.getPrimaryKey())
unknown's avatar
unknown committed
3908
      pk_length += (field->pack_length() + 3) / 4;
unknown's avatar
unknown committed
3909 3910 3911
  }
  
  // No primary key, create shadow key as 64 bit, auto increment  
3912
  if (form->s->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
3913 3914 3915 3916 3917
  {
    DBUG_PRINT("info", ("Generating shadow key"));
    col.setName("$PK");
    col.setType(NdbDictionary::Column::Bigunsigned);
    col.setLength(1);
unknown's avatar
unknown committed
3918
    col.setNullable(FALSE);
unknown's avatar
unknown committed
3919 3920 3921
    col.setPrimaryKey(TRUE);
    col.setAutoIncrement(TRUE);
    tab.addColumn(col);
unknown's avatar
unknown committed
3922 3923 3924 3925
    pk_length += 2;
  }
  
  // Make sure that blob tables don't have to big part size
3926
  for (i= 0; i < form->s->fields; i++) 
unknown's avatar
unknown committed
3927 3928 3929 3930 3931 3932 3933
  {
    /**
     * 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()) {
3934
    case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
3935 3936 3937 3938
    case MYSQL_TYPE_BLOB:    
    case MYSQL_TYPE_MEDIUM_BLOB:   
    case MYSQL_TYPE_LONG_BLOB: 
    {
3939 3940
      NdbDictionary::Column * col= tab.getColumn(i);
      int size= pk_length + (col->getPartSize()+3)/4 + 7;
unknown's avatar
unknown committed
3941
      if (size > NDB_MAX_TUPLE_SIZE_IN_WORDS && 
3942
         (pk_length+7) < NDB_MAX_TUPLE_SIZE_IN_WORDS)
unknown's avatar
unknown committed
3943
      {
3944 3945
        size= NDB_MAX_TUPLE_SIZE_IN_WORDS - pk_length - 7;
        col->setPartSize(4*size);
unknown's avatar
unknown committed
3946 3947 3948 3949 3950 3951 3952 3953 3954 3955
      }
      /**
       * 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
3956
  }
unknown's avatar
Merge  
unknown committed
3957 3958 3959

  ndb_set_fragmentation(tab, form, pk_length);

3960
  if ((my_errno= check_ndb_connection()))
unknown's avatar
unknown committed
3961 3962 3963
    DBUG_RETURN(my_errno);
  
  // Create the table in NDB     
3964 3965
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
3966
  if (dict->createTable(tab) != 0) 
unknown's avatar
unknown committed
3967 3968 3969 3970 3971 3972 3973 3974
  {
    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));
3975

unknown's avatar
unknown committed
3976
  // Create secondary indexes
3977
  my_errno= build_index_list(ndb, form, ILBP_CREATE);
3978

3979 3980 3981
  if (!my_errno)
    my_errno= write_ndb_file();

unknown's avatar
unknown committed
3982 3983 3984 3985
  DBUG_RETURN(my_errno);
}


3986
int ha_ndbcluster::create_ordered_index(const char *name, 
3987
                                        KEY *key_info)
3988
{
3989
  DBUG_ENTER("ha_ndbcluster::create_ordered_index");
unknown's avatar
unknown committed
3990
  DBUG_RETURN(create_index(name, key_info, FALSE));
3991 3992 3993
}

int ha_ndbcluster::create_unique_index(const char *name, 
3994
                                       KEY *key_info)
3995 3996
{

3997
  DBUG_ENTER("ha_ndbcluster::create_unique_index");
unknown's avatar
unknown committed
3998
  DBUG_RETURN(create_index(name, key_info, TRUE));
3999 4000 4001
}


unknown's avatar
unknown committed
4002 4003 4004 4005 4006
/*
  Create an index in NDB Cluster
 */

int ha_ndbcluster::create_index(const char *name, 
4007 4008
                                KEY *key_info,
                                bool unique)
4009
{
4010 4011
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
unknown's avatar
unknown committed
4012 4013 4014
  KEY_PART_INFO *key_part= key_info->key_part;
  KEY_PART_INFO *end= key_part + key_info->key_parts;
  
4015
  DBUG_ENTER("ha_ndbcluster::create_index");
unknown's avatar
unknown committed
4016
  DBUG_PRINT("enter", ("name: %s ", name));
4017

unknown's avatar
unknown committed
4018
  NdbDictionary::Index ndb_index(name);
4019
  if (unique)
unknown's avatar
unknown committed
4020 4021 4022 4023 4024
    ndb_index.setType(NdbDictionary::Index::UniqueHashIndex);
  else 
  {
    ndb_index.setType(NdbDictionary::Index::OrderedIndex);
    // TODO Only temporary ordered indexes supported
unknown's avatar
unknown committed
4025
    ndb_index.setLogging(FALSE); 
unknown's avatar
unknown committed
4026 4027 4028 4029 4030 4031 4032
  }
  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
4033
    ndb_index.addColumnName(field->field_name);
unknown's avatar
unknown committed
4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050
  }
  
  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)
{
4051
  NDBDICT *dict;
unknown's avatar
unknown committed
4052
  char new_tabname[FN_HEADLEN];
4053 4054
  const NDBTAB *orig_tab;
  int result;
unknown's avatar
unknown committed
4055 4056

  DBUG_ENTER("ha_ndbcluster::rename_table");
4057
  DBUG_PRINT("info", ("Renaming %s to %s", from, to));
unknown's avatar
unknown committed
4058 4059 4060 4061
  set_dbname(from);
  set_tabname(from);
  set_tabname(to, new_tabname);

4062 4063 4064
  if (check_ndb_connection())
    DBUG_RETURN(my_errno= HA_ERR_NO_CONNECTION);

unknown's avatar
unknown committed
4065 4066
  Ndb *ndb= get_ndb();
  dict= ndb->getDictionary();
4067 4068
  if (!(orig_tab= dict->getTable(m_tabname)))
    ERR_RETURN(dict->getNdbError());
4069 4070 4071 4072 4073 4074 4075
  // 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());
  }
4076 4077 4078
  m_table= (void *)orig_tab;
  // Change current database to that of target table
  set_dbname(to);
unknown's avatar
unknown committed
4079
  ndb->setDatabaseName(m_dbname);
4080
  if (!(result= alter_table_name(new_tabname)))
4081
  {
4082 4083
    // Rename .ndb file
    result= handler::rename_table(from, to);
4084
  }
4085

unknown's avatar
unknown committed
4086 4087 4088 4089 4090 4091 4092 4093
  DBUG_RETURN(result);
}


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

4094
int ha_ndbcluster::alter_table_name(const char *to)
unknown's avatar
unknown committed
4095
{
4096 4097
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
4098
  const NDBTAB *orig_tab= (const NDBTAB *) m_table;
unknown's avatar
unknown committed
4099 4100
  DBUG_ENTER("alter_table_name_table");

unknown's avatar
unknown committed
4101
  NdbDictionary::Table new_tab= *orig_tab;
4102 4103
  new_tab.setName(to);
  if (dict->alterTable(new_tab) != 0)
unknown's avatar
unknown committed
4104 4105 4106
    ERR_RETURN(dict->getNdbError());

  m_table= NULL;
unknown's avatar
unknown committed
4107
  m_table_info= NULL;
unknown's avatar
unknown committed
4108 4109 4110 4111 4112 4113
                                                                             
  DBUG_RETURN(0);
}


/*
4114 4115
  Delete table from NDB Cluster

unknown's avatar
unknown committed
4116 4117 4118 4119
 */

int ha_ndbcluster::delete_table(const char *name)
{
4120
  DBUG_ENTER("ha_ndbcluster::delete_table");
unknown's avatar
unknown committed
4121 4122 4123
  DBUG_PRINT("enter", ("name: %s", name));
  set_dbname(name);
  set_tabname(name);
4124

unknown's avatar
unknown committed
4125 4126
  if (check_ndb_connection())
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4127 4128

  /* Call ancestor function to delete .ndb file */
4129
  handler::delete_table(name);
4130 4131
  
  /* Drop the table from NDB */
unknown's avatar
unknown committed
4132 4133 4134 4135 4136
  DBUG_RETURN(drop_table());
}


/*
4137
  Drop table in NDB Cluster
unknown's avatar
unknown committed
4138 4139 4140 4141
 */

int ha_ndbcluster::drop_table()
{
4142 4143
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
4144

unknown's avatar
unknown committed
4145 4146
  DBUG_ENTER("drop_table");
  DBUG_PRINT("enter", ("Deleting %s", m_tabname));
4147

unknown's avatar
unknown committed
4148
  release_metadata();
4149 4150
  if (dict->dropTable(m_tabname))
    ERR_RETURN(dict->getNdbError());
unknown's avatar
unknown committed
4151 4152 4153 4154
  DBUG_RETURN(0);
}


4155
ulonglong ha_ndbcluster::get_auto_increment()
4156
{  
4157 4158
  int cache_size;
  Uint64 auto_value;
unknown's avatar
unknown committed
4159 4160
  DBUG_ENTER("get_auto_increment");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));
4161
  Ndb *ndb= get_ndb();
4162
   
4163
  if (m_rows_inserted > m_rows_to_insert)
unknown's avatar
unknown committed
4164
  {
4165 4166
    /* We guessed too low */
    m_rows_to_insert+= m_autoincrement_prefetch;
unknown's avatar
unknown committed
4167
  }
unknown's avatar
unknown committed
4168
  cache_size= 
unknown's avatar
unknown committed
4169 4170 4171 4172
    (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
4173
  auto_value= NDB_FAILED_AUTO_INCREMENT;
4174 4175 4176 4177 4178 4179 4180 4181 4182
  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);
4183
  if (auto_value == NDB_FAILED_AUTO_INCREMENT)
4184 4185 4186 4187 4188 4189
  {
    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
4190
  DBUG_RETURN((longlong)auto_value);
unknown's avatar
unknown committed
4191 4192 4193 4194 4195 4196 4197 4198
}


/*
  Constructor for the NDB Cluster table handler 
 */

ha_ndbcluster::ha_ndbcluster(TABLE *table_arg):
4199
  handler(&ndbcluster_hton, table_arg),
unknown's avatar
unknown committed
4200 4201 4202
  m_active_trans(NULL),
  m_active_cursor(NULL),
  m_table(NULL),
4203
  m_table_version(-1),
4204
  m_table_info(NULL),
unknown's avatar
unknown committed
4205
  m_table_flags(HA_REC_NOT_IN_SEQ |
4206 4207 4208 4209
                HA_NULL_IN_KEY |
                HA_AUTO_PART_KEY |
                HA_NO_PREFIX_CHAR_KEYS |
                HA_NEED_READ_RANGE_BUFFER |
4210
                HA_CAN_GEOMETRY |
4211
                HA_CAN_BIT_FIELD),
4212
  m_share(0),
unknown's avatar
unknown committed
4213
  m_use_write(FALSE),
4214
  m_ignore_dup_key(FALSE),
4215 4216
  m_primary_key_update(FALSE),
  m_retrieve_all_fields(FALSE),
4217
  m_retrieve_primary_key(FALSE),
4218 4219 4220
  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
4221
  m_rows_changed((ha_rows) 0),
4222 4223 4224 4225 4226 4227
  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),
4228 4229 4230
  m_dupkey((uint) -1),
  m_ha_not_exact_count(FALSE),
  m_force_send(TRUE),
4231
  m_autoincrement_prefetch((ha_rows) 32),
unknown's avatar
unknown committed
4232
  m_transaction_on(TRUE),
unknown's avatar
unknown committed
4233 4234
  m_cond_stack(NULL),
  m_multi_cursor(NULL)
4235
{
4236
  int i;
4237
 
unknown's avatar
unknown committed
4238 4239 4240 4241 4242
  DBUG_ENTER("ha_ndbcluster");

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

4243
  records= ~(ha_rows)0; // uninitialized
unknown's avatar
unknown committed
4244 4245
  block_size= 1024;

4246 4247
  for (i= 0; i < MAX_KEY; i++)
  {
4248 4249 4250 4251
    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;
4252 4253
  }

unknown's avatar
unknown committed
4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265
  DBUG_VOID_RETURN;
}


/*
  Destructor for NDB Cluster table handler
 */

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

4266 4267
  if (m_share)
    free_share(m_share);
unknown's avatar
unknown committed
4268
  release_metadata();
4269 4270
  my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
  m_blobs_buffer= 0;
unknown's avatar
unknown committed
4271 4272

  // Check for open cursor/transaction
4273 4274
  if (m_active_cursor) {
  }
unknown's avatar
unknown committed
4275
  DBUG_ASSERT(m_active_cursor == NULL);
4276 4277
  if (m_active_trans) {
  }
unknown's avatar
unknown committed
4278 4279
  DBUG_ASSERT(m_active_trans == NULL);

4280 4281 4282 4283
  // Discard the condition stack
  DBUG_PRINT("info", ("Clearing condition stack"));
  cond_clear();

unknown's avatar
unknown committed
4284 4285 4286 4287
  DBUG_VOID_RETURN;
}


unknown's avatar
Merge  
unknown committed
4288

unknown's avatar
unknown committed
4289 4290 4291 4292 4293 4294 4295 4296
/*
  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
4297
  int res;
unknown's avatar
unknown committed
4298 4299 4300 4301 4302 4303 4304 4305
  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
  
4306
  if (table->s->primary_key != MAX_KEY) 
unknown's avatar
unknown committed
4307
  {
4308
    key= table->key_info+table->s->primary_key;
unknown's avatar
unknown committed
4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319
    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);
  
4320 4321
  if (check_ndb_connection()) {
    free_share(m_share); m_share= 0;
unknown's avatar
unknown committed
4322
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4323
  }
4324
  
unknown's avatar
unknown committed
4325 4326 4327
  res= get_metadata(name);
  if (!res)
    info(HA_STATUS_VARIABLE | HA_STATUS_CONST);
unknown's avatar
unknown committed
4328

unknown's avatar
unknown committed
4329
  DBUG_RETURN(res);
unknown's avatar
unknown committed
4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340
}


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

int ha_ndbcluster::close(void)
{
  DBUG_ENTER("close");  
4341
  free_share(m_share); m_share= 0;
unknown's avatar
unknown committed
4342 4343 4344 4345 4346
  release_metadata();
  DBUG_RETURN(0);
}


4347
Thd_ndb* ha_ndbcluster::seize_thd_ndb()
unknown's avatar
unknown committed
4348
{
4349 4350
  Thd_ndb *thd_ndb;
  DBUG_ENTER("seize_thd_ndb");
unknown's avatar
unknown committed
4351

4352
  thd_ndb= new Thd_ndb();
4353 4354 4355
  thd_ndb->ndb->getDictionary()->set_local_table_data_size(
    sizeof(Ndb_local_table_statistics)
    );
4356
  if (thd_ndb->ndb->init(max_transactions) != 0)
unknown's avatar
unknown committed
4357
  {
4358
    ERR_PRINT(thd_ndb->ndb->getNdbError());
unknown's avatar
unknown committed
4359 4360 4361 4362 4363 4364
    /*
      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 
    */
4365 4366
    delete thd_ndb;
    thd_ndb= NULL;
unknown's avatar
unknown committed
4367
  }
4368
  DBUG_RETURN(thd_ndb);
unknown's avatar
unknown committed
4369 4370 4371
}


4372
void ha_ndbcluster::release_thd_ndb(Thd_ndb* thd_ndb)
unknown's avatar
unknown committed
4373
{
4374 4375
  DBUG_ENTER("release_thd_ndb");
  delete thd_ndb;
unknown's avatar
unknown committed
4376 4377 4378 4379 4380
  DBUG_VOID_RETURN;
}


/*
unknown's avatar
unknown committed
4381
  If this thread already has a Thd_ndb object allocated
unknown's avatar
unknown committed
4382
  in current THD, reuse it. Otherwise
unknown's avatar
unknown committed
4383
  seize a Thd_ndb object, assign it to current THD and use it.
unknown's avatar
unknown committed
4384 4385 4386
 
*/

4387
Ndb* check_ndb_in_thd(THD* thd)
unknown's avatar
unknown committed
4388
{
4389
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
4390
  if (!thd_ndb)
unknown's avatar
unknown committed
4391
  {
unknown's avatar
unknown committed
4392
    if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
unknown's avatar
Merge  
unknown committed
4393
      return NULL;
4394
    set_thd_ndb(thd, thd_ndb);
unknown's avatar
unknown committed
4395
  }
unknown's avatar
Merge  
unknown committed
4396
  return thd_ndb->ndb;
4397 4398
}

unknown's avatar
unknown committed
4399

4400

4401
int ha_ndbcluster::check_ndb_connection(THD* thd)
unknown's avatar
unknown committed
4402
{
4403
  Ndb *ndb;
unknown's avatar
unknown committed
4404 4405
  DBUG_ENTER("check_ndb_connection");
  
4406
  if (!(ndb= check_ndb_in_thd(thd)))
4407
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4408
  ndb->setDatabaseName(m_dbname);
unknown's avatar
unknown committed
4409 4410 4411
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
4412

4413
int ndbcluster_close_connection(THD *thd)
unknown's avatar
unknown committed
4414
{
4415
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
unknown's avatar
unknown committed
4416
  DBUG_ENTER("ndbcluster_close_connection");
4417 4418
  if (thd_ndb)
  {
4419
    ha_ndbcluster::release_thd_ndb(thd_ndb);
4420
    set_thd_ndb(thd, NULL); // not strictly required but does not hurt either
4421
  }
4422
  DBUG_RETURN(0);
unknown's avatar
unknown committed
4423 4424 4425 4426 4427 4428 4429
}


/*
  Try to discover one table from NDB
 */

4430
int ndbcluster_discover(THD* thd, const char *db, const char *name,
4431
                        const void** frmblob, uint* frmlen)
unknown's avatar
unknown committed
4432 4433 4434 4435
{
  uint len;
  const void* data;
  const NDBTAB* tab;
4436
  Ndb* ndb;
unknown's avatar
unknown committed
4437
  DBUG_ENTER("ndbcluster_discover");
4438
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name)); 
unknown's avatar
unknown committed
4439

4440 4441 4442
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(HA_ERR_NO_CONNECTION);  
  ndb->setDatabaseName(db);
4443

4444
  NDBDICT* dict= ndb->getDictionary();
4445
  dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
4446 4447 4448 4449 4450
  dict->invalidateTable(name);
  if (!(tab= dict->getTable(name)))
  {    
    const NdbError err= dict->getNdbError();
    if (err.code == 709)
4451
      DBUG_RETURN(-1);
4452
    ERR_RETURN(err);
unknown's avatar
unknown committed
4453 4454 4455 4456 4457 4458
  }
  DBUG_PRINT("info", ("Found table %s", tab->getName()));
  
  len= tab->getFrmLength();  
  if (len == 0 || tab->getFrmData() == NULL)
  {
4459 4460
    DBUG_PRINT("error", ("No frm data found."));
    DBUG_RETURN(1);
unknown's avatar
unknown committed
4461 4462 4463
  }
  
  if (unpackfrm(&data, &len, tab->getFrmData()))
4464 4465 4466 4467
  {
    DBUG_PRINT("error", ("Could not unpack table"));
    DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
4468 4469 4470 4471 4472 4473 4474 4475

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

/*
4476
  Check if a table exists in NDB
4477

4478
 */
unknown's avatar
unknown committed
4479

4480
int ndbcluster_table_exists_in_engine(THD* thd, const char *db, const char *name)
4481 4482 4483
{
  const NDBTAB* tab;
  Ndb* ndb;
4484
  DBUG_ENTER("ndbcluster_table_exists_in_engine");
4485
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
4486 4487

  if (!(ndb= check_ndb_in_thd(thd)))
4488
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4489 4490 4491
  ndb->setDatabaseName(db);

  NDBDICT* dict= ndb->getDictionary();
4492
  dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
4493 4494
  dict->invalidateTable(name);
  if (!(tab= dict->getTable(name)))
4495
  {
4496 4497 4498 4499 4500
    const NdbError err= dict->getNdbError();
    if (err.code == 709)
      DBUG_RETURN(0);
    ERR_RETURN(err);
  }
4501

4502 4503 4504 4505
  DBUG_PRINT("info", ("Found table %s", tab->getName()));
  DBUG_RETURN(1);
}

unknown's avatar
unknown committed
4506 4507


unknown's avatar
unknown committed
4508
extern "C" byte* tables_get_key(const char *entry, uint *length,
4509
                                my_bool not_used __attribute__((unused)))
4510 4511 4512 4513 4514 4515
{
  *length= strlen(entry);
  return (byte*) entry;
}


4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529
/*
  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;
4530
  int ret= 0;
4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556
  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++))
4557
  {
4558
    if (dict->dropTable(tabname))
4559 4560 4561
    {
      const NdbError err= dict->getNdbError();
      if (err.code != 709)
4562 4563
      {
        ERR_PRINT(err);
4564
        ret= ndb_to_mysql_error(&err);
4565
      }
4566 4567 4568
    }
  }
  DBUG_RETURN(ret);      
4569 4570 4571
}


4572
int ndbcluster_find_files(THD *thd,const char *db,const char *path,
4573
                          const char *wild, bool dir, List<char> *files)
unknown's avatar
unknown committed
4574
{
4575 4576 4577
  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
4578
  uint i;
4579
  Ndb* ndb;
4580
  char name[FN_REFLEN];
unknown's avatar
unknown committed
4581
  HASH ndb_tables, ok_tables;
unknown's avatar
unknown committed
4582
  NdbDictionary::Dictionary::List list;
4583 4584 4585 4586

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

4587
  if (dir)
unknown's avatar
unknown committed
4588
    DBUG_RETURN(0); // Discover of databases not yet supported
4589

unknown's avatar
unknown committed
4590
  // List tables in NDB
4591
  NDBDICT *dict= ndb->getDictionary();
unknown's avatar
unknown committed
4592
  if (dict->listObjects(list, 
4593
                        NdbDictionary::Object::UserTable) != 0)
unknown's avatar
unknown committed
4594
    ERR_RETURN(dict->getNdbError());
4595

unknown's avatar
unknown committed
4596
  if (hash_init(&ndb_tables, system_charset_info,list.count,0,0,
4597
                (hash_get_key)tables_get_key,0,0))
unknown's avatar
unknown committed
4598 4599 4600 4601 4602 4603
  {
    DBUG_PRINT("error", ("Failed to init HASH ndb_tables"));
    DBUG_RETURN(-1);
  }

  if (hash_init(&ok_tables, system_charset_info,32,0,0,
4604
                (hash_get_key)tables_get_key,0,0))
unknown's avatar
unknown committed
4605 4606 4607 4608 4609 4610
  {
    DBUG_PRINT("error", ("Failed to init HASH ok_tables"));
    hash_free(&ndb_tables);
    DBUG_RETURN(-1);
  }  

unknown's avatar
unknown committed
4611 4612 4613
  for (i= 0 ; i < list.count ; i++)
  {
    NdbDictionary::Dictionary::List::Element& t= list.elements[i];
unknown's avatar
unknown committed
4614
    DBUG_PRINT("info", ("Found %s/%s in NDB", t.database, t.name));     
unknown's avatar
unknown committed
4615

4616 4617 4618
    // Add only tables that belongs to db
    if (my_strcasecmp(system_charset_info, t.database, db))
      continue;
unknown's avatar
unknown committed
4619

unknown's avatar
unknown committed
4620
    // Apply wildcard to list of tables in NDB
4621
    if (wild)
4622
    {
4623 4624
      if (lower_case_table_names)
      {
4625 4626
        if (wild_case_compare(files_charset_info, t.name, wild))
          continue;
4627 4628
      }
      else if (wild_compare(t.name,wild,0))
4629
        continue;
4630
    }
unknown's avatar
unknown committed
4631 4632
    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
4633 4634
  }

unknown's avatar
unknown committed
4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649
  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
4650
    (void)strxnmov(name, FN_REFLEN, 
4651
                   mysql_data_home,"/",db,"/",file_name,ha_ndb_ext,NullS);
unknown's avatar
unknown committed
4652
    DBUG_PRINT("info", ("Check access for %s", name));
4653
    if (access(name, F_OK))
unknown's avatar
unknown committed
4654 4655 4656 4657
    {
      DBUG_PRINT("info", ("%s did not exist on disk", name));     
      // .ndb file did not exist on disk, another table type
      continue;
4658
    }
4659

unknown's avatar
unknown committed
4660 4661 4662
    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.
4663
    if (ndbcluster_table_exists_in_engine(thd, db, file_name) == 0)    
unknown's avatar
unknown committed
4664 4665 4666 4667 4668 4669 4670
    {
      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));
    }
  }
4671

unknown's avatar
unknown committed
4672 4673 4674 4675
  // 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++)
4676
  {
unknown's avatar
unknown committed
4677 4678
    file_name= hash_element(&ndb_tables, i);
    if (!hash_search(&ok_tables, file_name, strlen(file_name)))
4679
    {
unknown's avatar
unknown committed
4680 4681 4682 4683 4684 4685
      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));
    }
  }
4686

unknown's avatar
unknown committed
4687 4688
  // Lock mutex before deleting and creating frm files
  pthread_mutex_lock(&LOCK_open);
4689

unknown's avatar
unknown committed
4690 4691 4692 4693 4694
  if (!global_read_lock)
  {
    // Delete old files
    List_iterator_fast<char> it3(delete_list);
    while ((file_name=it3++))
4695 4696
    {
      DBUG_PRINT("info", ("Remove table %s/%s", db, file_name));
unknown's avatar
unknown committed
4697 4698 4699 4700
      // Delete the table and all related files
      TABLE_LIST table_list;
      bzero((char*) &table_list,sizeof(table_list));
      table_list.db= (char*) db;
4701
      table_list.alias= table_list.table_name= (char*)file_name;
4702
      (void)mysql_rm_table_part2(thd, &table_list,
unknown's avatar
Merge  
unknown committed
4703 4704 4705 4706
                                                                 /* if_exists */ FALSE,
                                                                 /* drop_temporary */ FALSE,
                                                                 /* drop_view */ FALSE,
                                                                 /* dont_log_query*/ TRUE);
4707 4708
      /* Clear error message that is returned when table is deleted */
      thd->clear_error();
4709 4710 4711
    }
  }

unknown's avatar
unknown committed
4712 4713 4714 4715
  // Create new files
  List_iterator_fast<char> it2(create_list);
  while ((file_name=it2++))
  {  
4716
    DBUG_PRINT("info", ("Table %s need discovery", file_name));
4717
    if (ha_create_table_from_engine(thd, db, file_name) == 0)
4718
      files->push_back(thd->strdup(file_name)); 
unknown's avatar
unknown committed
4719 4720 4721 4722 4723
  }

  pthread_mutex_unlock(&LOCK_open);      
  
  hash_free(&ok_tables);
4724
  hash_free(&ndb_tables);
4725
  } // extra bracket to avoid gcc 2.95.3 warning
4726
  DBUG_RETURN(0);    
unknown's avatar
unknown committed
4727 4728 4729 4730 4731 4732 4733 4734
}


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

4735 4736 4737 4738 4739 4740 4741
/* Call back after cluster connect */
static int connect_callback()
{
  update_status_variables(g_ndb_cluster_connection);
  return 0;
}

4742
bool ndbcluster_init()
unknown's avatar
unknown committed
4743
{
unknown's avatar
unknown committed
4744
  int res;
unknown's avatar
unknown committed
4745
  DBUG_ENTER("ndbcluster_init");
4746 4747 4748 4749

  if (have_ndbcluster != SHOW_OPTION_YES)
    goto ndbcluster_init_error;

4750
  // Set connectstring if specified
4751 4752
  if (opt_ndbcluster_connectstring != 0)
    DBUG_PRINT("connectstring", ("%s", opt_ndbcluster_connectstring));     
4753
  if ((g_ndb_cluster_connection=
4754
       new Ndb_cluster_connection(opt_ndbcluster_connectstring)) == 0)
4755
  {
4756
    DBUG_PRINT("error",("Ndb_cluster_connection(%s)",
4757
                        opt_ndbcluster_connectstring));
unknown's avatar
unknown committed
4758
    goto ndbcluster_init_error;
4759
  }
unknown's avatar
unknown committed
4760

4761 4762 4763
  g_ndb_cluster_connection->set_optimized_node_selection
    (opt_ndb_optimized_node_selection);

unknown's avatar
unknown committed
4764
  // Create a Ndb object to open the connection  to NDB
4765 4766 4767 4768 4769
  if ( (g_ndb= new Ndb(g_ndb_cluster_connection, "sys")) == 0 )
  {
    DBUG_PRINT("error", ("failed to create global ndb object"));
    goto ndbcluster_init_error;
  }
4770
  g_ndb->getDictionary()->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
unknown's avatar
unknown committed
4771 4772 4773
  if (g_ndb->init() != 0)
  {
    ERR_PRINT (g_ndb->getNdbError());
unknown's avatar
unknown committed
4774
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
4775
  }
unknown's avatar
unknown committed
4776

unknown's avatar
unknown committed
4777
  if ((res= g_ndb_cluster_connection->connect(0,0,0)) == 0)
unknown's avatar
unknown committed
4778
  {
4779
    connect_callback();
unknown's avatar
unknown committed
4780
    DBUG_PRINT("info",("NDBCLUSTER storage engine at %s on port %d",
4781 4782
                       g_ndb_cluster_connection->get_connected_host(),
                       g_ndb_cluster_connection->get_connected_port()));
4783
    g_ndb_cluster_connection->wait_until_ready(10,3);
unknown's avatar
unknown committed
4784
  } 
unknown's avatar
unknown committed
4785
  else if (res == 1)
unknown's avatar
unknown committed
4786
  {
4787
    if (g_ndb_cluster_connection->start_connect_thread(connect_callback)) 
4788
    {
unknown's avatar
unknown committed
4789
      DBUG_PRINT("error", ("g_ndb_cluster_connection->start_connect_thread()"));
unknown's avatar
unknown committed
4790 4791
      goto ndbcluster_init_error;
    }
4792
#ifndef DBUG_OFF
unknown's avatar
unknown committed
4793 4794
    {
      char buf[1024];
4795
      DBUG_PRINT("info",
4796 4797 4798 4799
                 ("NDBCLUSTER storage engine not started, "
                  "will connect using %s",
                  g_ndb_cluster_connection->
                  get_connectstring(buf,sizeof(buf))));
unknown's avatar
unknown committed
4800
    }
4801
#endif
unknown's avatar
unknown committed
4802
  }
unknown's avatar
unknown committed
4803
  else
unknown's avatar
unknown committed
4804 4805 4806
  {
    DBUG_ASSERT(res == -1);
    DBUG_PRINT("error", ("permanent error"));
unknown's avatar
unknown committed
4807
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
4808
  }
unknown's avatar
unknown committed
4809
  
unknown's avatar
unknown committed
4810 4811 4812
  (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
4813 4814
  pthread_mutex_init(&LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST);
  pthread_cond_init(&COND_ndb_util_thread, NULL);
4815

unknown's avatar
Merge  
unknown committed
4816 4817 4818 4819 4820 4821

  // 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"));
4822 4823 4824 4825
    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
4826 4827 4828
    goto ndbcluster_init_error;
  }
  
unknown's avatar
unknown committed
4829
  ndbcluster_inited= 1;
4830
  DBUG_RETURN(FALSE);
unknown's avatar
Merge  
unknown committed
4831

4832
ndbcluster_init_error:
unknown's avatar
unknown committed
4833
  if (g_ndb)
4834 4835 4836 4837 4838
    delete g_ndb;
  g_ndb= NULL;
  if (g_ndb_cluster_connection)
    delete g_ndb_cluster_connection;
  g_ndb_cluster_connection= NULL;
4839 4840
  have_ndbcluster= SHOW_OPTION_DISABLED;	// If we couldn't use handler
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4841 4842 4843 4844 4845 4846
}


/*
  End use of the NDB Cluster table handler
  - free all global variables allocated by 
unknown's avatar
Merge  
unknown committed
4847
    ndbcluster_init()
unknown's avatar
unknown committed
4848 4849 4850 4851 4852
*/

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

4854 4855 4856
  if (!ndbcluster_inited)
    DBUG_RETURN(0);

unknown's avatar
Merge  
unknown committed
4857 4858 4859 4860 4861 4862
  // 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
4863
  if (g_ndb)
unknown's avatar
unknown committed
4864
    delete g_ndb;
unknown's avatar
unknown committed
4865
  g_ndb= NULL;
4866 4867 4868
  if (g_ndb_cluster_connection)
    delete g_ndb_cluster_connection;
  g_ndb_cluster_connection= NULL;
4869

unknown's avatar
unknown committed
4870 4871
  hash_free(&ndbcluster_open_tables);
  pthread_mutex_destroy(&ndbcluster_mutex);
unknown's avatar
Merge  
unknown committed
4872 4873
  pthread_mutex_destroy(&LOCK_ndb_util_thread);
  pthread_cond_destroy(&COND_ndb_util_thread);
unknown's avatar
unknown committed
4874 4875 4876 4877
  ndbcluster_inited= 0;
  DBUG_RETURN(0);
}

4878 4879 4880 4881 4882
/*
  Static error print function called from
  static handler method ndbcluster_commit
  and ndbcluster_rollback
*/
4883 4884

void ndbcluster_print_error(int error, const NdbOperation *error_op)
4885
{
4886 4887
  DBUG_ENTER("ndbcluster_print_error");
  TABLE tab;
4888
  const char *tab_name= (error_op) ? error_op->getTableName() : "";
4889
  tab.alias= (char *) tab_name;
4890
  ha_ndbcluster error_handler(&tab);
4891
  tab.file= &error_handler;
4892
  error_handler.print_error(error, MYF(0));
unknown's avatar
unknown committed
4893
  DBUG_VOID_RETURN;
4894
}
unknown's avatar
unknown committed
4895

4896 4897 4898
/**
 * Set a given location from full pathname to database name
 *
unknown's avatar
unknown committed
4899
 */
4900
void ha_ndbcluster::set_dbname(const char *path_name, char *dbname)
unknown's avatar
unknown committed
4901 4902 4903 4904
{
  char *end, *ptr;
  
  /* Scan name from the end */
4905 4906 4907 4908 4909 4910
  ptr= strend(path_name)-1;
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  ptr--;
  end= ptr;
unknown's avatar
unknown committed
4911 4912 4913 4914
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  uint name_len= end - ptr;
4915 4916
  memcpy(dbname, ptr + 1, name_len);
  dbname[name_len]= '\0';
unknown's avatar
unknown committed
4917 4918
#ifdef __WIN__
  /* Put to lower case */
4919 4920
  
  ptr= dbname;
unknown's avatar
unknown committed
4921 4922
  
  while (*ptr != '\0') {
4923
    *ptr= tolower(*ptr);
unknown's avatar
unknown committed
4924 4925 4926 4927 4928
    ptr++;
  }
#endif
}

4929 4930 4931 4932 4933 4934 4935 4936 4937
/*
  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
4938 4939 4940 4941 4942 4943 4944 4945 4946 4947
/**
 * 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 */
4948 4949
  end= strend(path_name)-1;
  ptr= end;
unknown's avatar
unknown committed
4950 4951 4952
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
4953
  uint name_len= end - ptr;
unknown's avatar
unknown committed
4954
  memcpy(tabname, ptr + 1, end - ptr);
4955
  tabname[name_len]= '\0';
unknown's avatar
unknown committed
4956 4957
#ifdef __WIN__
  /* Put to lower case */
4958
  ptr= tabname;
unknown's avatar
unknown committed
4959 4960 4961 4962 4963 4964 4965 4966 4967
  
  while (*ptr != '\0') {
    *ptr= tolower(*ptr);
    ptr++;
  }
#endif
}

/*
4968
  Set m_tabname from full pathname to table file 
unknown's avatar
unknown committed
4969 4970
 */

4971
void ha_ndbcluster::set_tabname(const char *path_name)
unknown's avatar
unknown committed
4972
{
4973
  set_tabname(path_name, m_tabname);
unknown's avatar
unknown committed
4974 4975 4976 4977
}


ha_rows 
unknown's avatar
unknown committed
4978 4979 4980 4981
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
4982
  uint key_length= key_info->key_length;
4983
  NDB_INDEX_TYPE idx_type= get_index_type(inx);  
unknown's avatar
unknown committed
4984 4985

  DBUG_ENTER("records_in_range");
4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999
  // 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
5000 5001
}

5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038
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
5039
  return m_transaction_on;
5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053
}
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
5054

5055 5056
uint8 ha_ndbcluster::table_cache_type()
{
unknown's avatar
Merge  
unknown committed
5057 5058 5059 5060 5061 5062
  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,
5063
                         Uint64 *commit_count)
unknown's avatar
Merge  
unknown committed
5064 5065 5066
{
  DBUG_ENTER("ndb_get_commitcount");

5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084
  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
5085 5086
  if (ndb_cache_check_time > 0)
  {
5087
    if (share->commit_count != 0)
unknown's avatar
Merge  
unknown committed
5088
    {
5089 5090 5091 5092 5093 5094
      *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
5095 5096
    }
  }
5097
  DBUG_PRINT("info", ("Get commit_count from NDB"));
unknown's avatar
Merge  
unknown committed
5098 5099 5100 5101
  Ndb *ndb;
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(1);
  ndb->setDatabaseName(dbname);
5102 5103
  uint lock= share->commit_count_lock;
  pthread_mutex_unlock(&share->mutex);
unknown's avatar
Merge  
unknown committed
5104 5105 5106

  struct Ndb_statistics stat;
  if (ndb_get_table_statistics(ndb, tabname, &stat))
5107 5108
  {
    free_share(share);
unknown's avatar
Merge  
unknown committed
5109
    DBUG_RETURN(1);
5110 5111 5112
  }

  pthread_mutex_lock(&share->mutex);
unknown's avatar
unknown committed
5113
  if (share->commit_count_lock == lock)
5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125
  {
    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
5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161
  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,
5162 5163
                                   char *full_name, uint full_name_len,
                                   ulonglong *engine_data)
unknown's avatar
Merge  
unknown committed
5164 5165 5166 5167 5168 5169 5170 5171
{
  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;

5172 5173
  DBUG_PRINT("enter", ("dbname: %s, tabname: %s, is_autocommit: %d",
                       dbname, tabname, is_autocommit));
unknown's avatar
Merge  
unknown committed
5174 5175

  if (!is_autocommit)
5176 5177
  {
    DBUG_PRINT("exit", ("No, don't use cache in transaction"));
unknown's avatar
Merge  
unknown committed
5178
    DBUG_RETURN(FALSE);
5179
  }
unknown's avatar
Merge  
unknown committed
5180 5181 5182

  if (ndb_get_commitcount(thd, dbname, tabname, &commit_count))
  {
5183 5184
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, could not retrieve commit_count"));
unknown's avatar
Merge  
unknown committed
5185 5186
    DBUG_RETURN(FALSE);
  }
5187
  DBUG_PRINT("info", ("*engine_data: %llu, commit_count: %llu",
5188
                      *engine_data, commit_count));
5189
  if (commit_count == 0)
unknown's avatar
Merge  
unknown committed
5190
  {
5191 5192
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, local commit has been performed"));
unknown's avatar
Merge  
unknown committed
5193 5194
    DBUG_RETURN(FALSE);
  }
5195 5196 5197 5198 5199 5200
  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
5201

5202
  DBUG_PRINT("exit", ("OK to use cache, engine_data: %llu", *engine_data));
unknown's avatar
Merge  
unknown committed
5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230
  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,
5231 5232 5233
                                          char *full_name, uint full_name_len,
                                          qc_engine_callback *engine_callback,
                                          ulonglong *engine_data)
unknown's avatar
Merge  
unknown committed
5234 5235 5236 5237
{
  DBUG_ENTER("ha_ndbcluster::register_query_cache_table");

  bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
5238 5239 5240 5241

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

unknown's avatar
Merge  
unknown committed
5242
  if (!is_autocommit)
5243 5244
  {
    DBUG_PRINT("exit", ("Can't register table during transaction"))
unknown's avatar
Merge  
unknown committed
5245
    DBUG_RETURN(FALSE);
5246
  }
unknown's avatar
Merge  
unknown committed
5247 5248 5249 5250 5251

  Uint64 commit_count;
  if (ndb_get_commitcount(thd, m_dbname, m_tabname, &commit_count))
  {
    *engine_data= 0;
5252
    DBUG_PRINT("exit", ("Error, could not get commitcount"))
unknown's avatar
Merge  
unknown committed
5253 5254 5255 5256
    DBUG_RETURN(FALSE);
  }
  *engine_data= commit_count;
  *engine_callback= ndbcluster_cache_retrieval_allowed;
5257 5258
  DBUG_PRINT("exit", ("commit_count: %llu", commit_count));
  DBUG_RETURN(commit_count > 0);
5259
}
unknown's avatar
unknown committed
5260

unknown's avatar
Merge  
unknown committed
5261

unknown's avatar
unknown committed
5262
/*
unknown's avatar
Merge  
unknown committed
5263
  Handling the shared NDB_SHARE structure that is needed to
unknown's avatar
unknown committed
5264 5265 5266 5267 5268 5269 5270
  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,
5271
                                my_bool not_used __attribute__((unused)))
unknown's avatar
unknown committed
5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299
{
  *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
5300
      share->commit_count= 0;
5301 5302 5303 5304 5305 5306 5307
      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
5308 5309 5310
    }
  }
  share->use_count++;
5311 5312 5313 5314 5315

  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
5316 5317 5318 5319 5320 5321 5322 5323 5324 5325
  pthread_mutex_unlock(&ndbcluster_mutex);
  return share;
}


static void free_share(NDB_SHARE *share)
{
  pthread_mutex_lock(&ndbcluster_mutex);
  if (!--share->use_count)
  {
5326
     hash_delete(&ndbcluster_open_tables, (byte*) share);
unknown's avatar
unknown committed
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 5354
    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, 
5355
                   const void **pack_data, uint *pack_len)
unknown's avatar
unknown committed
5356 5357 5358 5359 5360 5361 5362 5363 5364
{
  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;
5365
  org_len= len;
unknown's avatar
unknown committed
5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384
  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);  
  
5385 5386 5387
  *pack_data= blob;
  *pack_len= blob_len;
  error= 0;
unknown's avatar
unknown committed
5388 5389 5390 5391 5392 5393 5394 5395 5396
  
  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,
5397
                    const void *pack_data)
unknown's avatar
unknown committed
5398
{
5399
   const frm_blob_struct *blob= (frm_blob_struct*)pack_data;
unknown's avatar
unknown committed
5400 5401 5402 5403 5404
   byte *data;
   ulong complen, orglen, ver;
   DBUG_ENTER("unpackfrm");
   DBUG_PRINT("enter", ("pack_data: %x", pack_data));

5405 5406 5407
   complen=     uint4korr((char*)&blob->head.complen);
   orglen=      uint4korr((char*)&blob->head.orglen);
   ver=         uint4korr((char*)&blob->head.ver);
unknown's avatar
unknown committed
5408 5409
 
   DBUG_PRINT("blob",("ver: %d complen: %d orglen: %d",
5410
                     ver,complen,orglen));
unknown's avatar
unknown committed
5411 5412 5413 5414
   DBUG_DUMP("blob->data", (char*) blob->data, complen);
 
   if (ver != 1)
     DBUG_RETURN(1);
5415
   if (!(data= my_malloc(max(orglen, complen), MYF(MY_WME))))
unknown's avatar
unknown committed
5416 5417 5418 5419 5420 5421 5422 5423 5424
     DBUG_RETURN(2);
   memcpy(data, blob->data, complen);
 
   if (my_uncompress(data, &complen, &orglen))
   {
     my_free((char*)data, MYF(0));
     DBUG_RETURN(3);
   }

5425 5426
   *unpack_data= data;
   *unpack_len= complen;
unknown's avatar
unknown committed
5427 5428 5429 5430 5431

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

   DBUG_RETURN(0);
}
unknown's avatar
unknown committed
5432 5433 5434

static 
int
5435
ndb_get_table_statistics(Ndb* ndb, const char * table,
5436
                         struct Ndb_statistics * ndbstat)
unknown's avatar
unknown committed
5437 5438 5439
{
  DBUG_ENTER("ndb_get_table_statistics");
  DBUG_PRINT("enter", ("table: %s", table));
5440
  NdbTransaction* pTrans= ndb->startTransaction();
unknown's avatar
unknown committed
5441 5442 5443 5444
  do 
  {
    if (pTrans == NULL)
      break;
unknown's avatar
unknown committed
5445
      
unknown's avatar
unknown committed
5446 5447 5448 5449
    NdbScanOperation* pOp= pTrans->getNdbScanOperation(table);
    if (pOp == NULL)
      break;
    
5450
    if (pOp->readTuples(NdbOperation::LM_CommittedRead))
unknown's avatar
unknown committed
5451 5452 5453 5454 5455 5456
      break;
    
    int check= pOp->interpret_exit_last_row();
    if (check == -1)
      break;
    
unknown's avatar
unknown committed
5457 5458
    Uint64 rows, commits, mem;
    Uint32 size;
unknown's avatar
unknown committed
5459 5460
    pOp->getValue(NdbDictionary::Column::ROW_COUNT, (char*)&rows);
    pOp->getValue(NdbDictionary::Column::COMMIT_COUNT, (char*)&commits);
5461 5462
    pOp->getValue(NdbDictionary::Column::ROW_SIZE, (char*)&size);
    pOp->getValue(NdbDictionary::Column::FRAGMENT_MEMORY, (char*)&mem);
unknown's avatar
unknown committed
5463
    
5464
    check= pTrans->execute(NdbTransaction::NoCommit,
5465 5466
                           NdbTransaction::AbortOnError,
                           TRUE);
unknown's avatar
unknown committed
5467 5468 5469
    if (check == -1)
      break;
    
5470
    Uint32 count= 0;
unknown's avatar
unknown committed
5471 5472
    Uint64 sum_rows= 0;
    Uint64 sum_commits= 0;
5473 5474
    Uint64 sum_row_size= 0;
    Uint64 sum_mem= 0;
unknown's avatar
unknown committed
5475
    while ((check= pOp->nextResult(TRUE, TRUE)) == 0)
unknown's avatar
unknown committed
5476 5477 5478
    {
      sum_rows+= rows;
      sum_commits+= commits;
5479
      if (sum_row_size < size)
5480
        sum_row_size= size;
5481
      sum_mem+= mem;
5482
      count++;
unknown's avatar
unknown committed
5483 5484 5485 5486 5487
    }
    
    if (check == -1)
      break;

5488
    pOp->close(TRUE);
unknown's avatar
unknown committed
5489

unknown's avatar
unknown committed
5490
    ndb->closeTransaction(pTrans);
5491 5492 5493 5494 5495 5496

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

5497 5498 5499 5500 5501
    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
5502
    DBUG_RETURN(0);
unknown's avatar
unknown committed
5503
  } while (0);
unknown's avatar
unknown committed
5504

unknown's avatar
unknown committed
5505 5506
  if (pTrans)
    ndb->closeTransaction(pTrans);
unknown's avatar
unknown committed
5507 5508 5509 5510
  DBUG_PRINT("exit", ("failed"));
  DBUG_RETURN(-1);
}

5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525
/*
  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, 
5526
                 mysql_data_home,"/",m_dbname,"/",m_tabname,ha_ndb_ext,NullS);
5527 5528 5529 5530 5531 5532 5533 5534 5535 5536

  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);
}

5537
int
5538
ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
5539 5540 5541 5542
                                      KEY_MULTI_RANGE *ranges, 
                                      uint range_count,
                                      bool sorted, 
                                      HANDLER_BUFFER *buffer)
5543 5544
{
  DBUG_ENTER("ha_ndbcluster::read_multi_range_first");
unknown's avatar
unknown committed
5545
  
5546 5547
  int res;
  KEY* key_info= table->key_info + active_index;
5548
  NDB_INDEX_TYPE index_type= get_index_type(active_index);
unknown's avatar
merge  
unknown committed
5549
  ulong reclength= table->s->reclength;
5550 5551
  NdbOperation* op;

5552
  if (uses_blob_value(m_retrieve_all_fields))
unknown's avatar
unknown committed
5553 5554 5555 5556
  {
    /**
     * blobs can't be batched currently
     */
unknown's avatar
unknown committed
5557
    m_disable_multi_read= TRUE;
unknown's avatar
unknown committed
5558
    DBUG_RETURN(handler::read_multi_range_first(found_range_p, 
5559 5560 5561 5562
                                                ranges, 
                                                range_count,
                                                sorted, 
                                                buffer));
unknown's avatar
unknown committed
5563 5564
  }

unknown's avatar
unknown committed
5565
  m_disable_multi_read= FALSE;
5566 5567 5568 5569

  /**
   * Copy arguments into member variables
   */
5570 5571 5572
  m_multi_ranges= ranges;
  multi_range_curr= ranges;
  multi_range_end= ranges+range_count;
5573 5574 5575
  multi_range_sorted= sorted;
  multi_range_buffer= buffer;

5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586
  /**
   * 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
5587 5588
   */   

unknown's avatar
unknown committed
5589
  /**
5590 5591
   * Variables for loop
   */
unknown's avatar
unknown committed
5592 5593
  byte *curr= (byte*)buffer->buffer;
  byte *end_of_buffer= (byte*)buffer->buffer_end;
5594 5595 5596 5597
  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;
5598
  const NDBINDEX *idx= (NDBINDEX *) m_index[active_index].index; 
5599 5600
  const NdbOperation* lastOp= m_active_trans->getLastDefinedOperation();
  NdbIndexScanOperation* scanOp= 0;
unknown's avatar
unknown committed
5601 5602
  for (; multi_range_curr<multi_range_end && curr+reclength <= end_of_buffer; 
       multi_range_curr++)
5603
  {
unknown's avatar
unknown committed
5604 5605 5606 5607 5608 5609
    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 */
5610
    case PRIMARY_KEY_INDEX:
5611
    {
5612
      multi_range_curr->range_flag |= UNIQUE_RANGE;
5613
      if ((op= m_active_trans->getNdbOperation(tab)) && 
5614 5615 5616
          !op->readTuple(lm) && 
          !set_primary_key(op, multi_range_curr->start_key.key) &&
          !define_read_attrs(curr, op) &&
unknown's avatar
unknown committed
5617
          (op->setAbortOption(AO_IgnoreError), TRUE))
5618
        curr += reclength;
5619
      else
5620
        ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
5621
      break;
5622 5623
    }
    break;
unknown's avatar
unknown committed
5624 5625 5626 5627 5628 5629 5630
    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 */
5631
    case UNIQUE_INDEX:
5632
    {
5633
      multi_range_curr->range_flag |= UNIQUE_RANGE;
5634
      if ((op= m_active_trans->getNdbIndexOperation(unique_idx, tab)) && 
5635 5636 5637
          !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
5638
          (op->setAbortOption(AO_IgnoreError), TRUE))
5639
        curr += reclength;
5640
      else
5641
        ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
5642 5643
      break;
    }
unknown's avatar
unknown committed
5644 5645
    case ORDERED_INDEX:
    {
5646
  range:
5647
      multi_range_curr->range_flag &= ~(uint)UNIQUE_RANGE;
5648 5649
      if (scanOp == 0)
      {
5650 5651 5652 5653 5654 5655
        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
5656
          if (scanOp->reset_bounds(m_force_send))
5657 5658 5659 5660 5661
            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
5662
                 &&!scanOp->readTuples(lm, 0, parallelism, sorted, FALSE, TRUE)
5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673
                 &&!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());
        }
5674
      }
5675

5676
      const key_range *keys[2]= { &multi_range_curr->start_key, 
5677
                                  &multi_range_curr->end_key };
5678
      if ((res= set_bounds(scanOp, keys, multi_range_curr-ranges)))
5679
        DBUG_RETURN(res);
5680
      break;
5681
    }
unknown's avatar
unknown committed
5682
    case UNDEFINED_INDEX:
unknown's avatar
unknown committed
5683 5684 5685 5686
      DBUG_ASSERT(FALSE);
      DBUG_RETURN(1);
      break;
    }
5687 5688
  }
  
5689
  if (multi_range_curr != multi_range_end)
5690
  {
5691 5692 5693 5694 5695 5696
    /**
     * 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
     */
5697
    buffer->end_of_used_area= (byte*)buffer->buffer_end;
5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708
  }
  else
  {
    buffer->end_of_used_area= curr;
  }
  
  /**
   * Set first operation in multi range
   */
  m_current_multi_operation= 
    lastOp ? lastOp->next() : m_active_trans->getFirstDefinedOperation();
5709
  if (!(res= execute_no_commit_ie(this, m_active_trans)))
5710
  {
5711 5712
    m_multi_range_defined= multi_range_curr;
    multi_range_curr= ranges;
5713 5714
    m_multi_range_result_ptr= (byte*)buffer->buffer;
    DBUG_RETURN(read_multi_range_next(found_range_p));
5715 5716 5717 5718
  }
  ERR_RETURN(m_active_trans->getNdbError());
}

unknown's avatar
unknown committed
5719 5720 5721 5722 5723 5724
#if 0
#define DBUG_MULTI_RANGE(x) printf("read_multi_range_next: case %d\n", x);
#else
#define DBUG_MULTI_RANGE(x)
#endif

5725
int
5726
ha_ndbcluster::read_multi_range_next(KEY_MULTI_RANGE ** multi_range_found_p)
5727 5728
{
  DBUG_ENTER("ha_ndbcluster::read_multi_range_next");
5729
  if (m_disable_multi_read)
5730
  {
5731
    DBUG_RETURN(handler::read_multi_range_next(multi_range_found_p));
5732
  }
5733
  
5734
  int res;
5735
  int range_no;
unknown's avatar
merge  
unknown committed
5736
  ulong reclength= table->s->reclength;
5737
  const NdbOperation* op= m_current_multi_operation;
unknown's avatar
unknown committed
5738
  for (;multi_range_curr < m_multi_range_defined; multi_range_curr++)
5739
  {
5740
    if (multi_range_curr->range_flag & UNIQUE_RANGE)
5741
    {
5742
      if (op->getNdbError().code == 0)
5743
        goto found_next;
5744 5745 5746
      
      op= m_active_trans->getNextCompletedOperation(op);
      m_multi_range_result_ptr += reclength;
5747
      continue;
5748
    } 
5749
    else if (m_multi_cursor && !multi_range_sorted)
5750
    {
unknown's avatar
unknown committed
5751 5752
      DBUG_MULTI_RANGE(1);
      if ((res= fetch_next(m_multi_cursor)) == 0)
5753
      {
5754 5755 5756
        DBUG_MULTI_RANGE(2);
        range_no= m_multi_cursor->get_range_no();
        goto found;
5757 5758 5759
      } 
      else
      {
5760
        goto close_scan;
5761 5762
      }
    }
unknown's avatar
unknown committed
5763
    else if (m_multi_cursor && multi_range_sorted)
5764
    {
unknown's avatar
unknown committed
5765 5766
      if (m_active_cursor && (res= fetch_next(m_multi_cursor)))
      {
5767 5768
        DBUG_MULTI_RANGE(3);
        goto close_scan;
unknown's avatar
unknown committed
5769
      }
5770
      
5771
      range_no= m_multi_cursor->get_range_no();
5772
      uint current_range_no= multi_range_curr - m_multi_ranges;
unknown's avatar
unknown committed
5773
      if ((uint) range_no == current_range_no)
5774
      {
5775
        DBUG_MULTI_RANGE(4);
5776
        // return current row
5777
        goto found;
5778
      }
5779
      else if (range_no > (int)current_range_no)
5780
      {
5781 5782 5783 5784
        DBUG_MULTI_RANGE(5);
        // wait with current row
        m_active_cursor= 0;
        continue;
5785 5786 5787
      }
      else 
      {
5788 5789 5790
        DBUG_MULTI_RANGE(6);
        // First fetch from cursor
        DBUG_ASSERT(range_no == -1);
unknown's avatar
unknown committed
5791
        if ((res= m_multi_cursor->nextResult(true)))
5792 5793 5794 5795 5796
        {
          goto close_scan;
        }
        multi_range_curr--; // Will be increased in for-loop
        continue;
5797
      }
5798
    }
unknown's avatar
unknown committed
5799
    else /** m_multi_cursor == 0 */
5800
    {
unknown's avatar
unknown committed
5801
      DBUG_MULTI_RANGE(7);
5802 5803 5804 5805
      /**
       * Corresponds to range 5 in example in read_multi_range_first
       */
      (void)1;
5806
      continue;
5807
    }
5808
    
unknown's avatar
unknown committed
5809
    DBUG_ASSERT(FALSE); // Should only get here via goto's
5810 5811 5812
close_scan:
    if (res == 1)
    {
unknown's avatar
unknown committed
5813
      m_multi_cursor->close(FALSE, TRUE);
5814
      m_active_cursor= m_multi_cursor= 0;
unknown's avatar
unknown committed
5815
      DBUG_MULTI_RANGE(8);
5816 5817 5818 5819 5820 5821 5822
      continue;
    } 
    else 
    {
      DBUG_RETURN(ndb_err(m_active_trans));
    }
  }
5823
  
5824
  if (multi_range_curr == multi_range_end)
5825
    DBUG_RETURN(HA_ERR_END_OF_FILE);
5826
  
5827 5828 5829 5830
  /**
   * Read remaining ranges
   */
  DBUG_RETURN(read_multi_range_first(multi_range_found_p, 
5831 5832 5833 5834
                                     multi_range_curr,
                                     multi_range_end - multi_range_curr, 
                                     multi_range_sorted,
                                     multi_range_buffer));
5835 5836
  
found:
5837 5838 5839
  /**
   * Found a record belonging to a scan
   */
5840
  m_active_cursor= m_multi_cursor;
5841
  * multi_range_found_p= m_multi_ranges + range_no;
5842 5843
  memcpy(table->record[0], m_multi_range_cursor_result_ptr, reclength);
  setup_recattr(m_active_cursor->getFirstRecAttr());
5844 5845 5846
  unpack_record(table->record[0]);
  table->status= 0;     
  DBUG_RETURN(0);
5847
  
5848
found_next:
5849 5850 5851 5852
  /**
   * Found a record belonging to a pk/index op,
   *   copy result and move to next to prepare for next call
   */
5853
  * multi_range_found_p= multi_range_curr;
5854
  memcpy(table->record[0], m_multi_range_result_ptr, reclength);
5855
  setup_recattr(op->getFirstRecAttr());
5856
  unpack_record(table->record[0]);
5857 5858
  table->status= 0;
  
5859
  multi_range_curr++;
5860
  m_current_multi_operation= m_active_trans->getNextCompletedOperation(op);
5861 5862
  m_multi_range_result_ptr += reclength;
  DBUG_RETURN(0);
5863 5864
}

5865 5866 5867 5868 5869 5870 5871 5872
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
5873
  end= table->field + table->s->fields;
5874 5875 5876 5877 5878 5879
  
  for (field= table->field; field < end; field++, value++)
  {
    if ((* value).ptr)
    {
      DBUG_ASSERT(curr != 0);
5880 5881
      (* value).rec= curr;
      curr= curr->next();
5882 5883 5884
    }
  }
  
unknown's avatar
unknown committed
5885
  DBUG_RETURN(0);
5886 5887
}

unknown's avatar
Merge  
unknown committed
5888 5889
char*
ha_ndbcluster::update_table_comment(
5890 5891
                                /* out: table comment + additional */
        const char*     comment)/* in:  table comment defined by user */
unknown's avatar
Merge  
unknown committed
5892 5893
{
  uint length= strlen(comment);
unknown's avatar
unknown committed
5894
  if (length > 64000 - 3)
unknown's avatar
Merge  
unknown committed
5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920
  {
    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
5921 5922 5923
  my_snprintf(str,fmt_len_plus_extra,fmt,comment,
              length > 0 ? " ":"",
              tab->getReplicaCount());
unknown's avatar
Merge  
unknown committed
5924 5925 5926 5927 5928
  return str;
}


// Utility thread main loop
5929
pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
unknown's avatar
Merge  
unknown committed
5930 5931
{
  THD *thd; /* needs to be first for thread_stack */
5932
  Ndb* ndb;
unknown's avatar
Merge  
unknown committed
5933 5934 5935 5936 5937 5938 5939 5940
  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);
5941
  ndb= new Ndb(g_ndb_cluster_connection, "");
unknown's avatar
Merge  
unknown committed
5942 5943 5944 5945 5946

  pthread_detach_this_thread();
  ndb_util_thread= pthread_self();

  thd->thread_stack= (char*)&thd; /* remember where our stack is */
5947
  if (thd->store_globals() && (ndb->init() != -1))
unknown's avatar
Merge  
unknown committed
5948 5949 5950
  {
    thd->cleanup();
    delete thd;
5951
    delete ndb;
unknown's avatar
Merge  
unknown committed
5952 5953 5954 5955
    DBUG_RETURN(NULL);
  }

  List<NDB_SHARE> util_open_tables;
5956
  set_timespec(abstime, 0);
unknown's avatar
Merge  
unknown committed
5957 5958 5959 5960
  for (;;)
  {

    pthread_mutex_lock(&LOCK_ndb_util_thread);
unknown's avatar
unknown committed
5961 5962 5963
    pthread_cond_timedwait(&COND_ndb_util_thread,
                           &LOCK_ndb_util_thread,
                           &abstime);
unknown's avatar
Merge  
unknown committed
5964 5965 5966 5967 5968 5969 5970 5971 5972 5973
    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)
    {
5974 5975
      /* Wake up in 1 second to check if value has changed */
      set_timespec(abstime, 1);
unknown's avatar
Merge  
unknown committed
5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996
      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);
5997
    while ((share= it++))
unknown's avatar
Merge  
unknown committed
5998 5999 6000 6001 6002 6003 6004 6005 6006 6007
    {
      /* 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",
6008 6009
                 ("Fetching commit count for: %s",
                  share->table_name));
unknown's avatar
Merge  
unknown committed
6010 6011

      /* Contact NDB to get commit count for table */
6012 6013 6014 6015 6016 6017 6018 6019
      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
6020
      if (ndb_get_table_statistics(ndb, tabname, &stat) == 0)
unknown's avatar
Merge  
unknown committed
6021 6022
      {
        DBUG_PRINT("ndb_util_thread",
6023 6024
                   ("Table: %s, commit_count: %llu, rows: %llu",
                    share->table_name, stat.commit_count, stat.row_count));
unknown's avatar
Merge  
unknown committed
6025 6026 6027 6028 6029 6030
      }
      else
      {
        DBUG_PRINT("ndb_util_thread",
                   ("Error: Could not get commit count for table %s",
                    share->table_name));
6031
        stat.commit_count= 0;
unknown's avatar
Merge  
unknown committed
6032
      }
6033 6034 6035 6036 6037 6038

      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
6039 6040 6041 6042 6043 6044 6045
      /* Decrease the use count and possibly free share */
      free_share(share);
    }

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

6046 6047 6048 6049 6050 6051 6052 6053 6054
    /* 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
6055
    if (msecs >= 1000){
6056 6057 6058 6059 6060 6061 6062 6063 6064 6065
      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
6066 6067 6068 6069
  }

  thd->cleanup();
  delete thd;
6070
  delete ndb;
unknown's avatar
Merge  
unknown committed
6071 6072 6073 6074 6075 6076
  DBUG_PRINT("exit", ("ndb_util_thread"));
  my_thread_end();
  pthread_exit(0);
  DBUG_RETURN(NULL);
}

6077 6078 6079
/*
  Condition pushdown
*/
6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096
/*
  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)
*/
6097 6098 6099 6100 6101
const 
COND* 
ha_ndbcluster::cond_push(const COND *cond) 
{ 
  DBUG_ENTER("cond_push");
6102 6103 6104
  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
6105
    ndb_cond->next= m_cond_stack;
6106 6107 6108 6109 6110 6111
  else
    ndb_cond->next= NULL;
  m_cond_stack= ndb_cond;
  
  if (serialize_cond(cond, ndb_cond))
  {
unknown's avatar
unknown committed
6112
    DBUG_RETURN(NULL);
6113 6114 6115 6116
  }
  else
  {
    cond_pop();
unknown's avatar
unknown committed
6117
  }
6118 6119 6120
  DBUG_RETURN(cond); 
}

6121 6122 6123
/*
  Pop the top condition from the condition stack of the handler instance.
*/
6124 6125 6126 6127 6128 6129 6130 6131 6132
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
6133
}
6134

6135 6136 6137
/*
  Clear the condition stack
*/
6138 6139 6140 6141 6142 6143 6144 6145 6146 6147
void
ha_ndbcluster::cond_clear()
{
  DBUG_ENTER("cond_clear");
  while (m_cond_stack)
    cond_pop();

  DBUG_VOID_RETURN;
}

6148 6149 6150 6151 6152 6153
/*
  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.
*/
6154 6155 6156 6157 6158
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
6159 6160 6161 6162 6163
  // 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
6164 6165 6166
    switch (item->type()) {
    case Item::FUNC_ITEM:
    {
unknown's avatar
unknown committed
6167 6168 6169 6170
      Item_func *func_item= (Item_func *) item;
      context->skip+= func_item->argument_count();
      break;
    }
unknown's avatar
unknown committed
6171 6172 6173 6174 6175
    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
6176 6177
      break;
    default:
6178
      context->supported= FALSE;
unknown's avatar
unknown committed
6179 6180
      break;
    }
6181
    
unknown's avatar
unknown committed
6182 6183 6184
    DBUG_VOID_RETURN;
  }
  
6185
  if (context->supported)
6186
  {
6187 6188 6189 6190 6191 6192
    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
6193
    {
unknown's avatar
unknown committed
6194 6195
      switch (func_item->functype()) {
      case Item_func::BETWEEN:
6196
        /*
6197 6198 6199 6200 6201 6202 6203
          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()
6204
        */
unknown's avatar
unknown committed
6205 6206
      case Item_func::IN_FUNC:
      {
6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222
        /*
          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)
6223
          {
6224 6225 6226
            Item_func *func_item= (Item_func *) item;
            if (func_item->functype() == Item_func::UNKNOWN_FUNC &&
                func_item->const_item())
6227
            {
6228 6229 6230
              // Skip any arguments since we will evaluate function instead
              DBUG_PRINT("info", ("Skip until end of arguments marker"));
              context->skip= func_item->argument_count();
6231 6232 6233
            }
            else
            {
6234 6235 6236 6237
              DBUG_PRINT("info", ("Found unsupported functional expression in BETWEEN|IN"));
              context->supported= FALSE;
              DBUG_VOID_RETURN;
              
6238 6239 6240
            }
          }
        }
6241 6242
        else
        {
6243 6244 6245
          // Non-supported BETWEEN|IN expression
          DBUG_PRINT("info", ("Found unexpected item of type %u in BETWEEN|IN",
                              item->type()));
6246
          context->supported= FALSE;
6247
          DBUG_VOID_RETURN;
6248
        }
6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270
        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
6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282
        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()
          */
6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301
          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;
          }
6302 6303
          break;
        }
unknown's avatar
unknown committed
6304 6305
        case Item_func::IN_FUNC:
        {
6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318
          /*
            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);
6319 6320
          break;
        }
6321 6322
        default:
          context->supported= FALSE;
6323
        }
6324 6325 6326 6327 6328 6329 6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353
        // 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
6354 6355 6356 6357
      {
        switch (item->type()) {
        case Item::FIELD_ITEM:
        {
6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376
          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
6377
                 // Date and year can be written as string or int
6378 6379 6380 6381
                 ((type == MYSQL_TYPE_TIME ||
                   type == MYSQL_TYPE_DATE || 
                   type == MYSQL_TYPE_YEAR ||
                   type == MYSQL_TYPE_DATETIME)
unknown's avatar
unknown committed
6382 6383 6384
                  ? (context->expecting_field_result(STRING_RESULT) ||
                     context->expecting_field_result(INT_RESULT))
                  : true)) &&
6385 6386 6387 6388 6389 6390 6391 6392 6393
                // 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)
6394
              {
6395 6396 6397 6398 6399 6400 6401 6402 6403 6404
                // 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
6405 6406
                  switch (field->result_type()) {
                  case STRING_RESULT:
6407 6408 6409 6410 6411
                    // 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
6412
                  case REAL_RESULT:
6413 6414
                    context->expect_only(Item::REAL_ITEM);
                    context->expect(Item::DECIMAL_ITEM);
6415
                    context->expect(Item::INT_ITEM);
6416
                    break;
unknown's avatar
unknown committed
6417
                  case INT_RESULT:
6418 6419 6420
                    context->expect_only(Item::INT_ITEM);
                    context->expect(Item::VARBIN_ITEM);
                    break;
unknown's avatar
unknown committed
6421
                  case DECIMAL_RESULT:
6422 6423
                    context->expect_only(Item::DECIMAL_ITEM);
                    context->expect(Item::REAL_ITEM);
6424
                    context->expect(Item::INT_ITEM);
6425 6426 6427 6428
                    break;
                  default:
                    break;
                  }    
6429 6430
              }
              else
6431 6432 6433 6434
              {
                // Expect another logical expression
                context->expect_only(Item::FUNC_ITEM);
                context->expect(Item::COND_ITEM);
6435 6436 6437 6438 6439 6440 6441
                // 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)
6442
                {
unknown's avatar
unknown committed
6443
                  DBUG_PRINT("info", ("Found non-matching collation %s",  
6444 6445
                                      item->collation.collation->name)); 
                  context->supported= FALSE;                
6446 6447
                }
              }
6448 6449
              break;
            }
6450 6451
            else
            {
unknown's avatar
unknown committed
6452 6453
              DBUG_PRINT("info", ("Was not expecting field of type %u(%u)",
                                  field->result_type(), type));
6454
              context->supported= FALSE;
6455
            }
6456
          }
6457
          else
6458 6459 6460 6461
          {
            DBUG_PRINT("info", ("Was not expecting field from table %s(%s)",
                                context->table->s->table_name, 
                                field->table->s->table_name));
6462
            context->supported= FALSE;
6463
          }
6464 6465
          break;
        }
unknown's avatar
unknown committed
6466 6467
        case Item::FUNC_ITEM:
        {
6468 6469 6470 6471 6472 6473
          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
6474
          {
6475 6476 6477
            // Did not expect function here
            context->supported= FALSE;
            break;
6478
          }
6479
          
unknown's avatar
unknown committed
6480 6481 6482
          switch (func_item->functype()) {
          case Item_func::EQ_FUNC:
          {
6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496
            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;
6497
          }
unknown's avatar
unknown committed
6498 6499
          case Item_func::NE_FUNC:
          {
6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513
            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;
6514
          }
unknown's avatar
unknown committed
6515 6516
          case Item_func::LT_FUNC:
          {
6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531
            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
6532 6533
          case Item_func::LE_FUNC:
          {
6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548
            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
6549 6550
          case Item_func::GE_FUNC:
          {
6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565
            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
6566 6567
          case Item_func::GT_FUNC:
          {
6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582
            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
6583 6584
          case Item_func::LIKE_FUNC:
          {
6585 6586 6587 6588 6589 6590 6591 6592 6593
            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
6594 6595
          case Item_func::ISNULL_FUNC:
          {
6596 6597 6598 6599 6600 6601 6602 6603 6604 6605
            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
6606 6607
          case Item_func::ISNOTNULL_FUNC:
          {
6608 6609 6610 6611 6612 6613 6614 6615 6616 6617
            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
6618 6619
          case Item_func::NOT_FUNC:
          {
6620 6621 6622 6623
            DBUG_PRINT("info", ("NOT_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);     
            context->expect(Item::FUNC_ITEM);
6624
            context->expect(Item::COND_ITEM);
6625
            break;
6626
          }
unknown's avatar
unknown committed
6627 6628
          case Item_func::BETWEEN:
          {
6629
            DBUG_PRINT("info", ("BETWEEN, rewriting using AND"));
6630
            Item_func_between *between_func= (Item_func_between *) func_item;
6631 6632 6633 6634
            Ndb_rewrite_context *rewrite_context= 
              new Ndb_rewrite_context(func_item);
            rewrite_context->next= context->rewrite_stack;
            context->rewrite_stack= rewrite_context;
6635 6636 6637 6638 6639 6640 6641 6642 6643
            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;
            }
6644
            DBUG_PRINT("info", ("COND_AND_FUNC"));
6645 6646 6647
            curr_cond->ndb_item= 
              new Ndb_item(Item_func::COND_AND_FUNC, 
                           func_item->argument_count() - 1);
6648
            context->expect_only(Item::FIELD_ITEM);
6649 6650 6651 6652 6653
            context->expect(Item::INT_ITEM);
            context->expect(Item::STRING_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FUNC_ITEM);
            break;
6654
          }
unknown's avatar
unknown committed
6655 6656
          case Item_func::IN_FUNC:
          {
6657
            DBUG_PRINT("info", ("IN_FUNC, rewriting using OR"));
6658
            Item_func_in *in_func= (Item_func_in *) func_item;
6659 6660 6661 6662
            Ndb_rewrite_context *rewrite_context= 
              new Ndb_rewrite_context(func_item);
            rewrite_context->next= context->rewrite_stack;
            context->rewrite_stack= rewrite_context;
6663 6664 6665 6666 6667 6668 6669 6670 6671
            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;
            }
6672 6673 6674 6675 6676 6677 6678 6679 6680
            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;
6681
          }
unknown's avatar
unknown committed
6682 6683
          case Item_func::UNKNOWN_FUNC:
          {
6684 6685 6686 6687
            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
6688 6689 6690 6691
            {
              switch (func_item->result_type()) {
              case STRING_RESULT:
              {
6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719
                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
6720 6721
              case REAL_RESULT:
              {
6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742
                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
6743 6744
              case INT_RESULT:
              {
6745 6746 6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765
                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
6766 6767
              case DECIMAL_RESULT:
              {
6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790
                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
6791
            }
6792 6793 6794 6795 6796
            else
              // Function does not return constant expression
              context->supported= FALSE;
            break;
          }
unknown's avatar
unknown committed
6797 6798
          default:
          {
6799 6800 6801
            DBUG_PRINT("info", ("Found func_item of type %d", 
                                func_item->functype()));
            context->supported= FALSE;
6802
          }
6803 6804
          }
          break;
6805
        }
unknown's avatar
unknown committed
6806
        case Item::STRING_ITEM:
6807 6808 6809
          DBUG_PRINT("info", ("STRING_ITEM")); 
          if (context->expecting(Item::STRING_ITEM)) 
          {
6810
#ifndef DBUG_OFF
6811 6812 6813 6814 6815 6816
            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()));
6817
#endif
6818 6819 6820 6821 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::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
6845
        case Item::INT_ITEM:
6846 6847
          DBUG_PRINT("info", ("INT_ITEM"));
          if (context->expecting(Item::INT_ITEM)) 
6848
          {
6849 6850 6851 6852 6853 6854 6855 6856 6857 6858
            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);
6859 6860
              context->expect_field_result(REAL_RESULT);
              context->expect_field_result(DECIMAL_RESULT);
6861 6862 6863 6864 6865 6866 6867
            }
            else
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
            }
6868 6869
          }
          else
6870 6871
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
6872
        case Item::REAL_ITEM:
6873 6874
          DBUG_PRINT("info", ("REAL_ITEM %s"));
          if (context->expecting(Item::REAL_ITEM)) 
6875
          {
6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892
            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);
            }
6893
          }
6894 6895 6896
          else
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
6897
        case Item::VARBIN_ITEM:
6898 6899
          DBUG_PRINT("info", ("VARBIN_ITEM"));
          if (context->expecting(Item::VARBIN_ITEM)) 
6900
          {
6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915
            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);
            }
6916 6917
          }
          else
6918 6919
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
6920
        case Item::DECIMAL_ITEM:
6921 6922
          DBUG_PRINT("info", ("DECIMAL_ITEM %s"));
          if (context->expecting(Item::DECIMAL_ITEM)) 
6923
          {
6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941
            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);
            }
6942
          }
6943 6944 6945
          else
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
6946 6947
        case Item::COND_ITEM:
        {
6948 6949 6950
          Item_cond *cond_item= (Item_cond *) item;
          
          if (context->expecting(Item::COND_ITEM))
unknown's avatar
unknown committed
6951 6952 6953
          {
            switch (cond_item->functype()) {
            case Item_func::COND_AND_FUNC:
6954 6955 6956 6957
              DBUG_PRINT("info", ("COND_AND_FUNC"));
              curr_cond->ndb_item= new Ndb_item(cond_item->functype(),
                                                cond_item);      
              break;
unknown's avatar
unknown committed
6958
            case Item_func::COND_OR_FUNC:
6959 6960 6961 6962 6963 6964 6965 6966 6967
              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
6968
          }
6969
          else
unknown's avatar
unknown committed
6970 6971
          {
            /* Did not expect condition */
6972
            context->supported= FALSE;          
unknown's avatar
unknown committed
6973
          }
6974
          break;
6975
        }
unknown's avatar
unknown committed
6976 6977
        default:
        {
6978
          DBUG_PRINT("info", ("Found item of type %d", item->type()));
6979
          context->supported= FALSE;
6980 6981
        }
        }
unknown's avatar
unknown committed
6982
      }
6983 6984 6985 6986 6987 6988 6989 6990 6991 6992
      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();
6993
          curr_cond->prev= prev_cond;
6994 6995 6996 6997 6998
          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;
        }
6999
      }
7000
    }
7001
  }
7002
 
7003 7004 7005 7006 7007 7008 7009 7010
  DBUG_VOID_RETURN;
}

bool
ha_ndbcluster::serialize_cond(const COND *cond, Ndb_cond_stack *ndb_cond)
{
  DBUG_ENTER("serialize_cond");
  Item *item= (Item *) cond;
7011
  Ndb_cond_traverse_context context(table, (void *)m_table, ndb_cond);
7012 7013 7014
  // Expect a logical expression
  context.expect(Item::FUNC_ITEM);
  context.expect(Item::COND_ITEM);
7015
  item->traverse_cond(&ndb_serialize_cond, (void *) &context, Item::PREFIX);
7016
  DBUG_PRINT("info", ("The pushed condition is %ssupported", (context.supported)?"":"not "));
7017

7018
  DBUG_RETURN(context.supported);
7019 7020
}

7021 7022
int
ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, 
7023 7024
                                           NdbScanFilter *filter,
                                           bool negated)
7025 7026
{
  DBUG_ENTER("build_scan_filter_predicate");  
unknown's avatar
unknown committed
7027 7028 7029
  switch (cond->ndb_item->type) {
  case NDB_FUNCTION:
  {
7030 7031 7032
    if (!cond->next)
      break;
    Ndb_item *a= cond->next->ndb_item;
7033
    Ndb_item *b, *field, *value= NULL;
7034 7035
    LINT_INIT(field);

unknown's avatar
unknown committed
7036 7037
    switch (cond->ndb_item->argument_count()) {
    case 1:
7038 7039 7040
      field= 
        (a->type == NDB_FIELD)? a : NULL;
      break;
unknown's avatar
unknown committed
7041
    case 2:
7042
      if (!cond->next->next)
7043
        break;
7044 7045
      b= cond->next->next->ndb_item;
      value= 
7046 7047 7048
        (a->type == NDB_VALUE)? a
        : (b->type == NDB_VALUE)? b
        : NULL;
7049
      field= 
7050 7051 7052
        (a->type == NDB_FIELD)? a
        : (b->type == NDB_FIELD)? b
        : NULL;
7053
      break;
7054
    default:
7055 7056
      break;
    }
unknown's avatar
unknown committed
7057 7058 7059
    switch ((negated) ? 
            Ndb_item::negate(cond->ndb_item->qualification.function_type)
            : cond->ndb_item->qualification.function_type) {
7060
    case NDB_EQ_FUNC:
7061
    {
7062
      if (!value || !field) break;
unknown's avatar
unknown committed
7063 7064
      // Save value in right format for the field type
      value->save_in_field(field);
7065
      DBUG_PRINT("info", ("Generating EQ filter"));
7066
      if (filter->cmp(NdbScanFilter::COND_EQ, 
7067 7068 7069 7070
                      field->get_field_no(),
                      field->get_val(),
                      field->pack_length()) == -1)
        DBUG_RETURN(1);
7071 7072
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7073
    }
7074
    case NDB_NE_FUNC:
unknown's avatar
unknown committed
7075
    {
7076
      if (!value || !field) break;
unknown's avatar
unknown committed
7077 7078
      // Save value in right format for the field type
      value->save_in_field(field);
7079
      DBUG_PRINT("info", ("Generating NE filter"));
7080
      if (filter->cmp(NdbScanFilter::COND_NE, 
7081 7082 7083 7084
                      field->get_field_no(),
                      field->get_val(),
                      field->pack_length()) == -1)
        DBUG_RETURN(1);
7085 7086
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7087
    }
7088
    case NDB_LT_FUNC:
unknown's avatar
unknown committed
7089
    {
7090
      if (!value || !field) break;
unknown's avatar
unknown committed
7091 7092
      // Save value in right format for the field type
      value->save_in_field(field);
7093
      if (a == field)
7094
      {
7095 7096 7097 7098 7099 7100
        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);
7101
      }
7102
      else
7103
      {
7104 7105 7106 7107 7108 7109
        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);
7110
      }
7111 7112
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7113
    }
7114
    case NDB_LE_FUNC:
unknown's avatar
unknown committed
7115
    {
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
      if (a == field)
7120
      {
7121 7122 7123 7124 7125 7126
        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);       
7127
      }
7128
      else
7129
      {
7130 7131 7132 7133 7134 7135
        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);
7136
      }
7137 7138
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7139
    }
7140
    case NDB_GE_FUNC:
unknown's avatar
unknown committed
7141
    {
7142
      if (!value || !field) break;
unknown's avatar
unknown committed
7143 7144
      // Save value in right format for the field type
      value->save_in_field(field);
7145
      if (a == field)
7146
      {
7147 7148 7149 7150 7151 7152
        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);
7153
      }
7154
      else
7155
      {
7156 7157 7158 7159 7160 7161
        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);
7162
      }
7163 7164
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7165
    }
7166
    case NDB_GT_FUNC:
unknown's avatar
unknown committed
7167
    {
7168
      if (!value || !field) break;
unknown's avatar
unknown committed
7169 7170
      // Save value in right format for the field type
      value->save_in_field(field);
7171
      if (a == field)
7172
      {
7173 7174 7175 7176 7177 7178
        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);
7179
      }
7180
      else
7181
      {
7182 7183 7184 7185 7186 7187
        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);
7188
      }
7189 7190
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7191
    }
7192
    case NDB_LIKE_FUNC:
unknown's avatar
unknown committed
7193
    {
7194
      if (!value || !field) break;
7195 7196 7197
      if ((value->qualification.value_type != Item::STRING_ITEM) &&
          (value->qualification.value_type != Item::VARBIN_ITEM))
          break;
unknown's avatar
unknown committed
7198 7199 7200
      // Save value in right format for the field type
      value->save_in_field(field);
      DBUG_PRINT("info", ("Generating LIKE filter: like(%d,%s,%d)", 
7201 7202 7203 7204
                          field->get_field_no(), value->get_val(), 
                          value->pack_length()));
      if (filter->cmp(NdbScanFilter::COND_LIKE, 
                      field->get_field_no(),
7205 7206
                      value->get_val(),
                      value->pack_length()) == -1)
7207
        DBUG_RETURN(1);
7208 7209
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7210
    }
7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230
    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:
7231 7232 7233 7234 7235
      if (!field)
        break;
      DBUG_PRINT("info", ("Generating ISNULL filter"));
      if (filter->isnull(field->get_field_no()) == -1)
        DBUG_RETURN(1);
7236 7237
      cond= cond->next->next;
      DBUG_RETURN(0);
7238
    case NDB_ISNOTNULL_FUNC:
unknown's avatar
unknown committed
7239
    {
7240 7241 7242 7243 7244
      if (!field)
        break;
      DBUG_PRINT("info", ("Generating ISNOTNULL filter"));
      if (filter->isnotnull(field->get_field_no()) == -1)
        DBUG_RETURN(1);         
7245 7246
      cond= cond->next->next;
      DBUG_RETURN(0);
7247 7248 7249 7250 7251 7252 7253 7254 7255 7256
    }
    default:
      break;
    }
    break;
  }
  default:
    break;
  }
  DBUG_PRINT("info", ("Found illegal condition"));
7257
  DBUG_RETURN(1);
7258 7259
}

7260
int
7261
ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter)
7262
{
7263
  uint level=0;
unknown's avatar
unknown committed
7264
  bool negated= FALSE;
7265
  DBUG_ENTER("build_scan_filter_group");
unknown's avatar
unknown committed
7266

7267 7268
  do
  {
unknown's avatar
unknown committed
7269 7270 7271 7272 7273 7274
    if (!cond)
      DBUG_RETURN(1);
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
    {
      switch (cond->ndb_item->qualification.function_type) {
7275
      case NDB_COND_AND_FUNC:
unknown's avatar
unknown committed
7276
      {
7277 7278 7279 7280 7281
        level++;
        DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NAND":"AND",
                            level));
        if ((negated) ? filter->begin(NdbScanFilter::NAND)
            : filter->begin(NdbScanFilter::AND) == -1)
7282
          DBUG_RETURN(1);
unknown's avatar
unknown committed
7283
        negated= FALSE;
7284 7285 7286
        cond= cond->next;
        break;
      }
7287
      case NDB_COND_OR_FUNC:
unknown's avatar
unknown committed
7288
      {
7289 7290 7291 7292 7293 7294
        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
7295
        negated= FALSE;
7296 7297 7298
        cond= cond->next;
        break;
      }
7299
      case NDB_NOT_FUNC:
unknown's avatar
unknown committed
7300
      {
7301
        DBUG_PRINT("info", ("Generating negated query"));
7302
        cond= cond->next;
unknown's avatar
unknown committed
7303
        negated= TRUE;
7304 7305 7306 7307
        break;
      }
      default:
        if (build_scan_filter_predicate(cond, filter, negated))
7308
          DBUG_RETURN(1);
unknown's avatar
unknown committed
7309
        negated= FALSE;
7310 7311 7312
        break;
      }
      break;
unknown's avatar
unknown committed
7313 7314
    }
    case NDB_END_COND:
7315 7316
      DBUG_PRINT("info", ("End of group %u", level));
      level--;
7317 7318
      if (cond) cond= cond->next;
      if (filter->end() == -1)
7319
        DBUG_RETURN(1);
7320 7321 7322
      if (!negated)
        break;
      // else fall through (NOT END is an illegal condition)
unknown's avatar
unknown committed
7323 7324
    default:
    {
7325
      DBUG_PRINT("info", ("Illegal scan filter"));
7326
    }
7327
    }
7328
  }  while (level > 0 || negated);
7329
  
7330
  DBUG_RETURN(0);
7331 7332
}

7333 7334
int
ha_ndbcluster::build_scan_filter(Ndb_cond * &cond, NdbScanFilter *filter)
7335 7336 7337 7338
{
  bool simple_cond= TRUE;
  DBUG_ENTER("build_scan_filter");  

unknown's avatar
unknown committed
7339 7340 7341
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
      switch (cond->ndb_item->qualification.function_type) {
7342 7343
      case NDB_COND_AND_FUNC:
      case NDB_COND_OR_FUNC:
7344 7345 7346 7347 7348 7349 7350 7351 7352
        simple_cond= FALSE;
        break;
      default:
        break;
      }
      break;
    default:
      break;
    }
7353 7354 7355 7356 7357 7358
  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);
7359

7360
  DBUG_RETURN(0);
7361 7362
}

7363
int
7364
ha_ndbcluster::generate_scan_filter(Ndb_cond_stack *ndb_cond_stack,
7365
                                    NdbScanOperation *op)
7366 7367 7368 7369
{
  DBUG_ENTER("generate_scan_filter");
  if (ndb_cond_stack)
  {
7370
    DBUG_PRINT("info", ("Generating scan filter"));
7371 7372 7373 7374 7375
    NdbScanFilter filter(op);
    bool multiple_cond= FALSE;
    // Wrap an AND group around multiple conditions
    if (ndb_cond_stack->next) {
      multiple_cond= TRUE;
7376
      if (filter.begin() == -1)
7377
        DBUG_RETURN(1); 
7378 7379
    }
    for (Ndb_cond_stack *stack= ndb_cond_stack; 
7380 7381
         (stack); 
         stack= stack->next)
7382
      {
7383
        Ndb_cond *cond= stack->ndb_cond;
7384

7385 7386 7387 7388 7389
        if (build_scan_filter(cond, &filter))
        {
          DBUG_PRINT("info", ("build_scan_filter failed"));
          DBUG_RETURN(1);
        }
7390
      }
7391 7392
    if (multiple_cond && filter.end() == -1)
      DBUG_RETURN(1);
7393 7394 7395 7396 7397 7398
  }
  else
  {  
    DBUG_PRINT("info", ("Empty stack"));
  }

7399
  DBUG_RETURN(0);
7400 7401
}

7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421 7422
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));

7423
  if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
7424 7425
    DBUG_RETURN(TRUE);
  
unknown's avatar
unknown committed
7426
  if (get_thd_ndb(thd) && get_thd_ndb(thd)->ndb)
7427
  {
unknown's avatar
unknown committed
7428
    Ndb* ndb= (get_thd_ndb(thd))->ndb;
7429 7430 7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446
    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
7447
#endif /* HAVE_NDBCLUSTER_DB */