ha_ndbcluster.cc 239 KB
Newer Older
1
/* Copyright (C) 2000-2003 MySQL AB
2 3 4

  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
5
  the Free Software Foundation; version 2 of the License.
6 7 8 9 10 11 12 13

  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
14
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
15 16 17 18 19 20 21
*/

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

22
#ifdef USE_PRAGMA_IMPLEMENTATION
23
#pragma implementation				// gcc: Class implementation
24 25 26 27 28 29 30 31 32 33
#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>

34 35 36
// options from from mysqld.cc
extern my_bool opt_ndb_optimized_node_selection;
extern const char *opt_ndbcluster_connectstring;
jonas@perch.ndb.mysql.com's avatar
jonas@perch.ndb.mysql.com committed
37
extern ulong opt_ndb_cache_check_time;
38

39
// Default value for parallelism
40
static const int parallelism= 0;
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 = {
serg@serg.mylan's avatar
serg@serg.mylan 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_CAN_RECREATE
74 75
};

76
#define NDB_AUTO_INCREMENT_RETRIES 10
77

78 79
#define NDB_INVALID_SCHEMA_OBJECT 241

80
#define ERR_PRINT(err) \
81
  DBUG_PRINT("error", ("%d  message: %s", err.code, err.message))
82

83 84
#define ERR_RETURN(err)                  \
{                                        \
85
  const NdbError& tmp= err;              \
86
  ERR_PRINT(tmp);                        \
87
  DBUG_RETURN(ndb_to_mysql_error(&tmp)); \
88 89 90 91
}

// Typedefs for long names
typedef NdbDictionary::Column NDBCOL;
joreland@mysql.com's avatar
joreland@mysql.com committed
92
typedef NdbDictionary::Table NDBTAB;
93 94 95
typedef NdbDictionary::Index  NDBINDEX;
typedef NdbDictionary::Dictionary  NDBDICT;

96
bool ndbcluster_inited= FALSE;
97

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

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

116
static int ndb_get_table_statistics(ha_ndbcluster*, bool, Ndb*, const char *,
117
                                    struct Ndb_statistics *);
118

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

126 127 128 129
/*
  Dummy buffer to read zero pack_length fields
  which are mapped to 1 char
*/
130
static uint32 dummy_buf;
131

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

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

143 144 145 146 147 148
/* 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;
149
static long ndb_number_of_data_nodes= 0;
150 151 152 153 154 155 156

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;
157
  ndb_number_of_data_nodes= c->no_db_nodes();
158 159 160 161 162
  return 0;
}

struct show_var_st ndb_status_variables[]= {
  {"cluster_node_id",        (char*) &ndb_cluster_node_id,         SHOW_LONG},
163 164
  {"config_from_host",         (char*) &ndb_connected_host,      SHOW_CHAR_PTR},
  {"config_from_port",         (char*) &ndb_connected_port,          SHOW_LONG},
165
//  {"number_of_replicas",     (char*) &ndb_number_of_replicas,      SHOW_LONG},
166
  {"number_of_data_nodes",(char*) &ndb_number_of_data_nodes, SHOW_LONG},
167 168 169
  {NullS, NullS, SHOW_LONG}
};

170 171 172 173 174 175 176 177
/*
  Error handling functions
*/

struct err_code_mapping
{
  int ndb_err;
  int my_err;
178
  int show_warning;
179 180 181 182
};

static const err_code_mapping err_map[]= 
{
183 184
  { 626, HA_ERR_KEY_NOT_FOUND, 0 },
  { 630, HA_ERR_FOUND_DUPP_KEY, 0 },
185
  { 893, HA_ERR_FOUND_DUPP_KEY, 0 },
186 187 188
  { 721, HA_ERR_TABLE_EXIST, 1 },
  { 4244, HA_ERR_TABLE_EXIST, 1 },

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

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

204 205
  { 284, HA_ERR_TABLE_DEF_CHANGED, 0 },

206
  {4000, HA_ERR_OUT_OF_MEM, 1 },
207 208
  {4009, HA_ERR_NO_CONNECTION, 1 },

209 210 211
  { 0, 1, 0 },

  { -1, -1, 1 }
212 213 214 215 216 217
};


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


232 233

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

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

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

inline
275 276
int execute_no_commit_ie(ha_ndbcluster *h, NdbTransaction *trans,
			 bool force_release)
277
{
278
#ifdef NOT_USED
279
  int m_batch_execute= 0;
280
  if (m_batch_execute)
281
    return 0;
282
#endif
283
  h->release_completed_operations(trans, force_release);
284
  return trans->execute(NdbTransaction::NoCommit,
285 286
                        NdbTransaction::AO_IgnoreError,
                        h->m_force_send);
287 288
}

289 290 291
/*
  Place holder for ha_ndbcluster thread specific data
*/
292 293
Thd_ndb::Thd_ndb()
{
294
  ndb= new Ndb(g_ndb_cluster_connection, "");
295 296
  lock_count= 0;
  count= 0;
297 298
  all= NULL;
  stmt= NULL;
299
  error= 0;
300
  query_state&= NDB_QUERY_NORMAL;
301 302 303 304
}

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

326 327 328 329 330 331 332 333
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; }

334 335 336
inline
Ndb *ha_ndbcluster::get_ndb()
{
337
  return get_thd_ndb(current_thd)->ndb;
338 339 340 341 342 343
}

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

344
struct Ndb_local_table_statistics {
345
  int no_uncommitted_rows_count;
346
  ulong last_count;
347 348 349
  ha_rows records;
};

tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
350 351 352
void ha_ndbcluster::set_rec_per_key()
{
  DBUG_ENTER("ha_ndbcluster::get_status_const");
353
  for (uint i=0 ; i < table->s->keys ; i++)
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
354 355 356 357 358 359
  {
    table->key_info[i].rec_per_key[table->key_info[i].key_parts-1]= 1;
  }
  DBUG_VOID_RETURN;
}

360
int ha_ndbcluster::records_update()
361
{
362
  if (m_ha_not_exact_count)
363
    return 0;
364
  DBUG_ENTER("ha_ndbcluster::records_update");
365 366
  int result= 0;

367
  struct Ndb_local_table_statistics *local_info= 
368
    (struct Ndb_local_table_statistics *)m_table_info;
369
  DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
370
                      ((const NDBTAB *)m_table)->getTableId(),
371
                      local_info->no_uncommitted_rows_count));
372
  //  if (info->records == ~(ha_rows)0)
373
  {
374
    Ndb *ndb= get_ndb();
375
    struct Ndb_statistics stat;
376 377 378 379
    if (ndb->setDatabaseName(m_dbname))
    {
      return my_errno= HA_ERR_OUT_OF_MEM;
    }
stewart@willster.(none)'s avatar
stewart@willster.(none) committed
380 381 382
    result= ndb_get_table_statistics(this, true, ndb, m_tabname, &stat);
    if (result == 0)
    {
383 384
      mean_rec_length= stat.row_size;
      data_file_length= stat.fragment_memory;
385
      local_info->records= stat.row_count;
386 387
    }
  }
388 389
  {
    THD *thd= current_thd;
390
    if (get_thd_ndb(thd)->error)
391
      local_info->no_uncommitted_rows_count= 0;
392
  }
393
  if(result==0)
394
    records= local_info->records+ local_info->no_uncommitted_rows_count;
395
  DBUG_RETURN(result);
396 397
}

398 399
void ha_ndbcluster::no_uncommitted_rows_execute_failure()
{
400 401
  if (m_ha_not_exact_count)
    return;
402
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_execute_failure");
403
  get_thd_ndb(current_thd)->error= 1;
404 405 406
  DBUG_VOID_RETURN;
}

407 408
void ha_ndbcluster::no_uncommitted_rows_init(THD *thd)
{
409 410
  if (m_ha_not_exact_count)
    return;
411
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_init");
412
  struct Ndb_local_table_statistics *local_info= 
413
    (struct Ndb_local_table_statistics *)m_table_info;
414
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
415
  if (local_info->last_count != thd_ndb->count)
416
  {
417 418 419
    local_info->last_count= thd_ndb->count;
    local_info->no_uncommitted_rows_count= 0;
    local_info->records= ~(ha_rows)0;
420
    DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
421
                        ((const NDBTAB *)m_table)->getTableId(),
422
                        local_info->no_uncommitted_rows_count));
423 424 425 426 427 428
  }
  DBUG_VOID_RETURN;
}

void ha_ndbcluster::no_uncommitted_rows_update(int c)
{
429 430
  if (m_ha_not_exact_count)
    return;
431
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_update");
432
  struct Ndb_local_table_statistics *local_info=
433
    (struct Ndb_local_table_statistics *)m_table_info;
434
  local_info->no_uncommitted_rows_count+= c;
435
  DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
436
                      ((const NDBTAB *)m_table)->getTableId(),
437
                      local_info->no_uncommitted_rows_count));
438 439 440 441 442
  DBUG_VOID_RETURN;
}

void ha_ndbcluster::no_uncommitted_rows_reset(THD *thd)
{
443 444
  if (m_ha_not_exact_count)
    return;
445
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_reset");
446 447 448
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  thd_ndb->count++;
  thd_ndb->error= 0;
449 450 451
  DBUG_VOID_RETURN;
}

452 453
/*
  Take care of the error that occured in NDB
454

455
  RETURN
456
    0   No error
457 458 459
    #   The mapped error code
*/

460
void ha_ndbcluster::invalidate_dictionary_cache(bool global)
461 462
{
  NDBDICT *dict= get_ndb()->getDictionary();
463
  DBUG_ENTER("invalidate_dictionary_cache");
464
  DBUG_PRINT("info", ("invalidating %s", m_tabname));
465

466
  if (global)
467
  {
468 469 470 471
    const NDBTAB *tab= dict->getTable(m_tabname);
    if (!tab)
      DBUG_VOID_RETURN;
    if (tab->getObjectStatus() == NdbDictionary::Object::Invalid)
472 473 474 475 476 477 478 479
    {
      // Global cache has already been invalidated
      dict->removeCachedTable(m_tabname);
      global= FALSE;
    }
    else
      dict->invalidateTable(m_tabname);
  }
480 481
  else
    dict->removeCachedTable(m_tabname);
482
  table->s->version=0L;			/* Free when thread is ready */
483
  /* Invalidate indexes */
484
  for (uint i= 0; i < table->s->keys; i++)
485 486 487 488 489
  {
    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;

490 491 492
    switch (idx_type) {
    case PRIMARY_KEY_ORDERED_INDEX:
    case ORDERED_INDEX:
493 494 495 496
      if (global)
        dict->invalidateIndex(index->getName(), m_tabname);
      else
        dict->removeCachedIndex(index->getName(), m_tabname);
serg@serg.mylan's avatar
serg@serg.mylan committed
497
      break;
498
    case UNIQUE_ORDERED_INDEX:
499 500 501 502
      if (global)
        dict->invalidateIndex(index->getName(), m_tabname);
      else
        dict->removeCachedIndex(index->getName(), m_tabname);
503
    case UNIQUE_INDEX:
504 505 506 507
      if (global)
        dict->invalidateIndex(unique_index->getName(), m_tabname);
      else
        dict->removeCachedIndex(unique_index->getName(), m_tabname);
508
      break;
509 510
    case PRIMARY_KEY_INDEX:
    case UNDEFINED_INDEX:
511 512 513
      break;
    }
  }
514
  DBUG_VOID_RETURN;
515
}
516

517
int ha_ndbcluster::ndb_err(NdbTransaction *trans)
518
{
519
  int res;
520
  NdbError err= trans->getNdbError();
521 522 523 524 525
  DBUG_ENTER("ndb_err");
  
  ERR_PRINT(err);
  switch (err.classification) {
  case NdbError::SchemaError:
526
  {
527 528 529 530 531 532 533
    /* Close other open handlers not used by any thread */
    TABLE_LIST table_list;
    bzero((char*) &table_list,sizeof(table_list));
    table_list.db= m_dbname;
    table_list.alias= table_list.table_name= m_tabname;
    close_cached_tables(current_thd, 0, &table_list);

534 535
    invalidate_dictionary_cache(TRUE);

536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
    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);
      }
551
      DBUG_PRINT("info", ("Table exists but must have changed"));
552
    }
553
    break;
554
  }
555 556 557
  default:
    break;
  }
558 559
  res= ndb_to_mysql_error(&err);
  DBUG_PRINT("info", ("transformed ndbcluster error %d to mysql error %d", 
560
                      err.code, res));
561
  if (res == HA_ERR_FOUND_DUPP_KEY)
562 563
  {
    if (m_rows_to_insert == 1)
564 565 566 567 568 569
    {
      /*
	We can only distinguish between primary and non-primary
	violations here, so we need to return MAX_KEY for non-primary
	to signal that key is unknown
      */
570
      m_dupkey= err.code == 630 ? table->s->primary_key : MAX_KEY; 
571
    }
572
    else
monty@mishka.local's avatar
monty@mishka.local committed
573 574
    {
      /* We are batching inserts, offending key is not available */
575
      m_dupkey= (uint) -1;
monty@mishka.local's avatar
monty@mishka.local committed
576
    }
577
  }
578
  DBUG_RETURN(res);
579 580 581
}


582
/*
583
  Override the default get_error_message in order to add the 
584 585 586
  error message of NDB 
 */

587
bool ha_ndbcluster::get_error_message(int error, 
588
                                      String *buf)
589
{
590
  DBUG_ENTER("ha_ndbcluster::get_error_message");
591
  DBUG_PRINT("enter", ("error: %d", error));
592

593
  Ndb *ndb= get_ndb();
594
  if (!ndb)
595
    DBUG_RETURN(FALSE);
596

597
  const NdbError err= ndb->getNdbError(error);
598 599 600 601
  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);
602 603 604
}


tulin@dl145c.mysql.com's avatar
tulin@dl145c.mysql.com committed
605
#ifndef DBUG_OFF
pekka@mysql.com's avatar
pekka@mysql.com committed
606 607 608 609
/*
  Check if type is supported by NDB.
*/

tulin@dl145c.mysql.com's avatar
tulin@dl145c.mysql.com committed
610
static bool ndb_supported_type(enum_field_types type)
pekka@mysql.com's avatar
pekka@mysql.com committed
611 612
{
  switch (type) {
pekka@mysql.com's avatar
pekka@mysql.com committed
613 614 615 616 617 618 619
  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:
620 621
  case MYSQL_TYPE_DECIMAL:    
  case MYSQL_TYPE_NEWDECIMAL:
pekka@mysql.com's avatar
pekka@mysql.com committed
622 623 624 625 626 627 628 629
  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:
pekka@mysql.com's avatar
pekka@mysql.com committed
630
  case MYSQL_TYPE_VARCHAR:
pekka@mysql.com's avatar
pekka@mysql.com committed
631 632 633 634 635 636
  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:         
637
  case MYSQL_TYPE_BIT:
638
  case MYSQL_TYPE_GEOMETRY:
639
    return TRUE;
pekka@mysql.com's avatar
pekka@mysql.com committed
640
  case MYSQL_TYPE_NULL:   
pekka@mysql.com's avatar
pekka@mysql.com committed
641
    break;
pekka@mysql.com's avatar
pekka@mysql.com committed
642
  }
643
  return FALSE;
pekka@mysql.com's avatar
pekka@mysql.com committed
644
}
tulin@dl145c.mysql.com's avatar
tulin@dl145c.mysql.com committed
645
#endif /* !DBUG_OFF */
pekka@mysql.com's avatar
pekka@mysql.com committed
646 647


648 649 650 651 652
/*
  Instruct NDB to set the value of the hidden primary key
*/

bool ha_ndbcluster::set_hidden_key(NdbOperation *ndb_op,
653
                                   uint fieldnr, const byte *field_ptr)
654 655 656
{
  DBUG_ENTER("set_hidden_key");
  DBUG_RETURN(ndb_op->equal(fieldnr, (char*)field_ptr,
657
                            NDB_HIDDEN_PRIMARY_KEY_LENGTH) != 0);
658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
}


/*
  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);
  
tulin@dl145c.mysql.com's avatar
tulin@dl145c.mysql.com committed
675 676 677 678
  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);
679 680 681 682 683 684 685 686
}


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

int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field, 
687
                                 uint fieldnr, bool *set_blob_value)
688 689 690 691 692 693 694 695
{
  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);
pekka@mysql.com's avatar
pekka@mysql.com committed
696

tulin@dl145c.mysql.com's avatar
tulin@dl145c.mysql.com committed
697
  DBUG_ASSERT(ndb_supported_type(field->type()));
698
  {
699
    // ndb currently does not support size 0
700
    uint32 empty_field;
701 702
    if (pack_len == 0)
    {
703 704 705
      pack_len= sizeof(empty_field);
      field_ptr= (byte *)&empty_field;
      if (field->is_null())
706
        empty_field= 0;
707
      else
708
        empty_field= 1;
709
    }
pekka@mysql.com's avatar
pekka@mysql.com committed
710 711
    if (! (field->flags & BLOB_FLAG))
    {
712 713
      if (field->type() != MYSQL_TYPE_BIT)
      {
714 715 716 717 718 719 720
        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);
721 722 723
      }
      else // if (field->type() == MYSQL_TYPE_BIT)
      {
724
        longlong bits= field->val_int();
725
 
726 727
        // Round up bit field length to nearest word boundry
        pack_len= ((pack_len + 3) >> 2) << 2;
728 729 730 731 732
        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"));
733
        DBUG_DUMP("value", (char*)&bits, pack_len);
734
#ifdef WORDS_BIGENDIAN
735
        /* store lsw first */
joerg@trift2's avatar
joerg@trift2 committed
736 737
        bits = ((bits >> 32) & 0x00000000FFFFFFFFLL)
          |    ((bits << 32) & 0xFFFFFFFF00000000LL);
738
#endif
739
        DBUG_RETURN(ndb_op->setValue(fieldnr, (char*)&bits, pack_len) != 0);
740
      }
pekka@mysql.com's avatar
pekka@mysql.com committed
741 742
    }
    // Blob type
743
    NdbBlob *ndb_blob= ndb_op->getBlobHandle(fieldnr);
pekka@mysql.com's avatar
pekka@mysql.com committed
744 745 746 747 748 749 750 751 752 753 754 755
    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);

756 757 758
      // Looks like NULL ptr signals length 0 blob
      if (blob_ptr == NULL) {
        DBUG_ASSERT(blob_len == 0);
759
        blob_ptr= (char*)"";
760
      }
pekka@mysql.com's avatar
pekka@mysql.com committed
761

elliot@mysql.com's avatar
elliot@mysql.com committed
762 763
      DBUG_PRINT("value", ("set blob ptr=%p len=%u",
                           blob_ptr, blob_len));
pekka@mysql.com's avatar
pekka@mysql.com committed
764 765
      DBUG_DUMP("value", (char*)blob_ptr, min(blob_len, 26));

766
      if (set_blob_value)
767
        *set_blob_value= TRUE;
pekka@mysql.com's avatar
pekka@mysql.com committed
768 769 770 771
      // No callback needed to write value
      DBUG_RETURN(ndb_blob->setValue(blob_ptr, blob_len) != 0);
    }
    DBUG_RETURN(1);
772
  }
pekka@mysql.com's avatar
pekka@mysql.com committed
773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
}


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

788
NdbBlob::ActiveHook g_get_ndb_blobs_value;
pekka@mysql.com's avatar
pekka@mysql.com committed
789

790
int g_get_ndb_blobs_value(NdbBlob *ndb_blob, void *arg)
pekka@mysql.com's avatar
pekka@mysql.com committed
791
{
792
  DBUG_ENTER("g_get_ndb_blobs_value");
pekka@mysql.com's avatar
pekka@mysql.com committed
793 794 795
  if (ndb_blob->blobsNextBlob() != NULL)
    DBUG_RETURN(0);
  ha_ndbcluster *ha= (ha_ndbcluster *)arg;
796
  DBUG_RETURN(ha->get_ndb_blobs_value(ndb_blob, ha->m_blobs_offset));
pekka@mysql.com's avatar
pekka@mysql.com committed
797 798
}

799 800
int ha_ndbcluster::get_ndb_blobs_value(NdbBlob *last_ndb_blob,
				       my_ptrdiff_t ptrdiff)
pekka@mysql.com's avatar
pekka@mysql.com committed
801 802 803 804 805 806 807 808
{
  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;
809
    for (uint i= 0; i < table->s->fields; i++)
pekka@mysql.com's avatar
pekka@mysql.com committed
810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825
    {
      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)
        {
826
          char *buf= m_blobs_buffer + offset;
pekka@mysql.com's avatar
pekka@mysql.com committed
827
          uint32 len= 0xffffffff;  // Max uint32
828 829
          DBUG_PRINT("value", ("read blob ptr: 0x%lx  len: %u",
                               (long)buf, (uint)blob_len));
pekka@mysql.com's avatar
pekka@mysql.com committed
830 831 832
          if (ndb_blob->readData(buf, len) != 0)
            DBUG_RETURN(-1);
          DBUG_ASSERT(len == blob_len);
833 834
          // Ugly hack assumes only ptr needs to be changed
          field_blob->ptr+= ptrdiff;
pekka@mysql.com's avatar
pekka@mysql.com committed
835
          field_blob->set_ptr(len, buf);
836
          field_blob->ptr-= ptrdiff;
pekka@mysql.com's avatar
pekka@mysql.com committed
837 838 839 840
        }
        offset+= blob_size;
      }
    }
841
    if (loop == 0 && offset > m_blobs_buffer_size)
pekka@mysql.com's avatar
pekka@mysql.com committed
842
    {
843 844
      my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
      m_blobs_buffer_size= 0;
pekka@mysql.com's avatar
pekka@mysql.com committed
845
      DBUG_PRINT("value", ("allocate blobs buffer size %u", offset));
846 847
      m_blobs_buffer= my_malloc(offset, MYF(MY_WME));
      if (m_blobs_buffer == NULL)
848 849 850
      {
        sql_print_error("ha_ndbcluster::get_ndb_blobs_value: "
                        "my_malloc(%u) failed", offset);
pekka@mysql.com's avatar
pekka@mysql.com committed
851
        DBUG_RETURN(-1);
852
      }
853
      m_blobs_buffer_size= offset;
pekka@mysql.com's avatar
pekka@mysql.com committed
854
    }
855
  }
pekka@mysql.com's avatar
pekka@mysql.com committed
856
  DBUG_RETURN(0);
857 858 859 860 861
}


/*
  Instruct NDB to fetch one field
pekka@mysql.com's avatar
pekka@mysql.com committed
862 863
  - data is read directly into buffer provided by field
    if field is NULL, data is read into memory provided by NDBAPI
864 865
*/

pekka@mysql.com's avatar
pekka@mysql.com committed
866
int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field,
867
                                 uint fieldnr, byte* buf)
868 869
{
  DBUG_ENTER("get_ndb_value");
pekka@mysql.com's avatar
pekka@mysql.com committed
870 871 872 873 874
  DBUG_PRINT("enter", ("fieldnr: %d flags: %o", fieldnr,
                       (int)(field != NULL ? field->flags : 0)));

  if (field != NULL)
  {
tulin@dl145c.mysql.com's avatar
tulin@dl145c.mysql.com committed
875 876
      DBUG_ASSERT(buf);
      DBUG_ASSERT(ndb_supported_type(field->type()));
pekka@mysql.com's avatar
pekka@mysql.com committed
877 878
      DBUG_ASSERT(field->ptr != NULL);
      if (! (field->flags & BLOB_FLAG))
879
      { 
880 881
        if (field->type() != MYSQL_TYPE_BIT)
        {
882 883 884 885 886 887 888 889
          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);
        }
890 891 892 893
        else // if (field->type() == MYSQL_TYPE_BIT)
        {
          m_value[fieldnr].rec= ndb_op->getValue(fieldnr);
        }
pekka@mysql.com's avatar
pekka@mysql.com committed
894 895 896 897 898 899 900 901 902
        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
903
	m_blobs_offset= buf - (byte*) table->record[0];
pekka@mysql.com's avatar
pekka@mysql.com committed
904
        void *arg= (void *)this;
905
        DBUG_RETURN(ndb_blob->setActiveHook(g_get_ndb_blobs_value, arg) != 0);
pekka@mysql.com's avatar
pekka@mysql.com committed
906 907 908 909 910
      }
      DBUG_RETURN(1);
  }

  // Used for hidden key only
911
  m_value[fieldnr].rec= ndb_op->getValue(fieldnr, m_ref);
pekka@mysql.com's avatar
pekka@mysql.com committed
912 913 914 915 916 917 918 919 920
  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)
{
921
  if (table->s->blob_fields == 0)
922
    return FALSE;
pekka@mysql.com's avatar
pekka@mysql.com committed
923
  if (all_fields)
924
    return TRUE;
pekka@mysql.com's avatar
pekka@mysql.com committed
925
  {
926
    uint no_fields= table->s->fields;
pekka@mysql.com's avatar
pekka@mysql.com committed
927
    int i;
928
    THD *thd= current_thd;
pekka@mysql.com's avatar
pekka@mysql.com committed
929 930 931 932 933 934
    // 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)
      {
935
        return TRUE;
pekka@mysql.com's avatar
pekka@mysql.com committed
936 937 938
      }
    }
  }
939
  return FALSE;
940 941 942 943 944 945 946 947 948 949 950 951 952
}


/*
  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)
{
953 954
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
955 956
  const NDBTAB *tab;
  int error;
957
  bool invalidating_ndb_table= FALSE;
958

959 960 961
  DBUG_ENTER("get_metadata");
  DBUG_PRINT("enter", ("m_tabname: %s, path: %s", m_tabname, path));

962
  do {
963
    const void *data= NULL, *pack_data= NULL;
964 965 966 967
    uint length, pack_length;

    if (!(tab= dict->getTable(m_tabname)))
      ERR_RETURN(dict->getNdbError());
968
    // Check if thread has stale local cache
969 970 971 972 973 974 975
    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()));
    }
976 977 978 979 980
    /*
      Compare FrmData in NDB with frm file from disk.
    */
    error= 0;
    if (readfrm(path, &data, &length) ||
981
        packfrm(data, length, &pack_data, &pack_length))
982 983 984 985 986
    {
      my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
      my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR));
      DBUG_RETURN(1);
    }
987
    
988
    if ((pack_length != tab->getFrmLength()) || 
989
        (memcmp(pack_data, tab->getFrmData(), pack_length)))
990 991 992
    {
      if (!invalidating_ndb_table)
      {
993
        DBUG_PRINT("info", ("Invalidating table"));
994
        invalidate_dictionary_cache(TRUE);
995
        invalidating_ndb_table= TRUE;
996 997 998
      }
      else
      {
999 1000 1001 1002 1003 1004 1005 1006
        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;
1007 1008 1009 1010
      }
    }
    else
    {
1011
      invalidating_ndb_table= FALSE;
1012 1013 1014 1015 1016
    }
    my_free((char*)data, MYF(0));
    my_free((char*)pack_data, MYF(0));
  } while (invalidating_ndb_table);

1017 1018
  if (error)
    DBUG_RETURN(error);
1019
  
1020
  m_table_version= tab->getObjectVersion();
1021 1022 1023 1024
  m_table= (void *)tab; 
  m_table_info= NULL; // Set in external lock
  
  DBUG_RETURN(build_index_list(ndb, table, ILBP_OPEN));
1025
}
1026

1027
static int fix_unique_index_attr_order(NDB_INDEX_DATA &data,
1028 1029
                                       const NDBINDEX *index,
                                       KEY *key_info)
1030 1031 1032 1033 1034 1035 1036
{
  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));
1037 1038 1039 1040 1041 1042
  if (data.unique_index_attrid_map == 0)
  {
    sql_print_error("fix_unique_index_attr_order: my_malloc(%u) failure",
                    (unsigned int)sz);
    DBUG_RETURN(HA_ERR_OUT_OF_MEM);
  }
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054

  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++)
    {
1055
      const NDBCOL *c= index->getColumn(j);
msvensson@neptunus.(none)'s avatar
msvensson@neptunus.(none) committed
1056
      if (strcmp(field_name, c->getName()) == 0)
1057
      {
1058 1059
        data.unique_index_attrid_map[i]= j;
        break;
1060 1061 1062 1063 1064 1065
      }
    }
    DBUG_ASSERT(data.unique_index_attrid_map[i] != 255);
  }
  DBUG_RETURN(0);
}
1066

1067 1068


1069
int ha_ndbcluster::build_index_list(Ndb *ndb, TABLE *tab, enum ILBP phase)
1070
{
1071
  uint i;
1072
  int error= 0;
1073
  const char *index_name;
1074
  char unique_index_name[FN_LEN];
1075
  bool null_in_unique_index= false;
1076
  static const char* unique_suffix= "$unique";
1077
  KEY* key_info= tab->key_info;
1078
  const char **key_name= tab->s->keynames.type_names;
1079
  NDBDICT *dict= ndb->getDictionary();
1080
  DBUG_ENTER("ha_ndbcluster::build_index_list");
1081
  
1082
  m_has_unique_index= FALSE;
1083
  // Save information about all known indexes
1084
  for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
1085
  {
1086
    index_name= *key_name;
1087
    NDB_INDEX_TYPE idx_type= get_index_type_from_table(i);
1088
    m_index[i].type= idx_type;
1089
    if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX)
1090
    {
1091
      m_has_unique_index= TRUE;
1092 1093
      strxnmov(unique_index_name, FN_LEN, index_name, unique_suffix, NullS);
      DBUG_PRINT("info", ("Created unique index name \'%s\' for index %d",
1094
                          unique_index_name, i));
1095
    }
1096 1097 1098
    // Create secondary indexes if in create phase
    if (phase == ILBP_CREATE)
    {
1099 1100
      DBUG_PRINT("info", ("Creating index %u: %s", i, index_name));      
      switch (idx_type){
1101
        
1102
      case PRIMARY_KEY_INDEX:
1103 1104
        // Do nothing, already created
        break;
1105
      case PRIMARY_KEY_ORDERED_INDEX:
1106 1107
        error= create_ordered_index(index_name, key_info);
        break;
1108
      case UNIQUE_ORDERED_INDEX:
1109 1110 1111
        if (!(error= create_ordered_index(index_name, key_info)))
          error= create_unique_index(unique_index_name, key_info);
        break;
1112
      case UNIQUE_INDEX:
1113 1114 1115 1116 1117 1118 1119 1120
	if (check_index_fields_not_null(i))
	{
	  push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
			      ER_NULL_COLUMN_IN_INDEX,
			      "Ndb does not support unique index on NULL valued attributes, index access with NULL value will become full table scan");
	  null_in_unique_index= true;
	}
	error= create_unique_index(unique_index_name, key_info);
1121
        break;
1122
      case ORDERED_INDEX:
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132
	if (key_info->algorithm == HA_KEY_ALG_HASH)
	{
	  push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
			      ER_UNSUPPORTED_EXTENSION,
			      ER(ER_UNSUPPORTED_EXTENSION),
			      "Ndb does not support non-unique "
			      "hash based indexes");
	  error= HA_ERR_UNSUPPORTED;
	  break;
	}
1133 1134
        error= create_ordered_index(index_name, key_info);
        break;
1135
      default:
1136 1137
        DBUG_ASSERT(FALSE);
        break;
1138 1139 1140
      }
      if (error)
      {
1141 1142 1143
        DBUG_PRINT("error", ("Failed to create index %u", i));
        drop_table();
        break;
1144 1145 1146
      }
    }
    // Add handles to index objects
1147
    if (idx_type != PRIMARY_KEY_INDEX && idx_type != UNIQUE_INDEX)
1148
    {
1149
      DBUG_PRINT("info", ("Get handle to index %s", index_name));
1150
      const NDBINDEX *index= dict->getIndex(index_name, m_tabname);
1151 1152
      if (!index)
        ERR_RETURN(dict->getNdbError());
mskold@mysql.com's avatar
mskold@mysql.com committed
1153
      m_index[i].index= (void *) index;
1154
    }
1155
    if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX)
1156
    {
1157 1158
      DBUG_PRINT("info", ("Get handle to unique_index %s", unique_index_name));
      const NDBINDEX *index= dict->getIndex(unique_index_name, m_tabname);
1159 1160
      if (!index)
        ERR_RETURN(dict->getNdbError());
mskold@mysql.com's avatar
mskold@mysql.com committed
1161
      m_index[i].unique_index= (void *) index;
1162 1163
      error= fix_unique_index_attr_order(m_index[i], index, key_info);
    }
1164 1165 1166 1167 1168
    if (idx_type == UNIQUE_INDEX && 
	phase != ILBP_CREATE &&
	check_index_fields_not_null(i))
      null_in_unique_index= true;
    m_index[i].null_in_unique_index= null_in_unique_index;
1169
  }
1170 1171
  
  DBUG_RETURN(error);
1172 1173
}

1174

1175 1176 1177 1178
/*
  Decode the type of an index from information 
  provided in table object
*/
1179
NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_table(uint inx) const
1180
{
1181
  bool is_hash_index=  (table->key_info[inx].algorithm == HA_KEY_ALG_HASH);
1182
  if (inx == table->s->primary_key)
1183
    return is_hash_index ? PRIMARY_KEY_INDEX : PRIMARY_KEY_ORDERED_INDEX;
1184 1185 1186 1187

  return ((table->key_info[inx].flags & HA_NOSAME) ? 
          (is_hash_index ? UNIQUE_INDEX : UNIQUE_ORDERED_INDEX) :
          ORDERED_INDEX);
1188
} 
1189

1190
bool ha_ndbcluster::check_index_fields_not_null(uint inx)
1191 1192 1193 1194
{
  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;
1195
  DBUG_ENTER("ha_ndbcluster::check_index_fields_not_null");
1196 1197 1198 1199 1200
  
  for (; key_part != end; key_part++) 
    {
      Field* field= key_part->field;
      if (field->maybe_null())
1201
        DBUG_RETURN(true);
1202 1203
    }
  
1204
  DBUG_RETURN(false);
1205
}
1206 1207 1208

void ha_ndbcluster::release_metadata()
{
1209
  uint i;
1210

1211 1212 1213 1214
  DBUG_ENTER("release_metadata");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));

  m_table= NULL;
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
1215
  m_table_info= NULL;
1216

1217
  // Release index list 
1218 1219
  for (i= 0; i < MAX_KEY; i++)
  {
1220 1221
    m_index[i].unique_index= NULL;      
    m_index[i].index= NULL;      
1222 1223 1224 1225 1226
    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;
    }
1227 1228
  }

1229 1230 1231
  DBUG_VOID_RETURN;
}

pekka@mysql.com's avatar
pekka@mysql.com committed
1232
int ha_ndbcluster::get_ndb_lock_type(enum thr_lock_type type)
1233
{
1234
  DBUG_ENTER("ha_ndbcluster::get_ndb_lock_type");
1235
  if (type >= TL_WRITE_ALLOW_WRITE)
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245
  {
    DBUG_PRINT("info", ("Using exclusive lock"));
    DBUG_RETURN(NdbOperation::LM_Exclusive);
  }
  else if (type ==  TL_READ_WITH_SHARED_LOCKS ||
	   uses_blob_value(m_retrieve_all_fields))
  {
    DBUG_PRINT("info", ("Using read lock"));
    DBUG_RETURN(NdbOperation::LM_Read);
  }
pekka@mysql.com's avatar
pekka@mysql.com committed
1246
  else
1247 1248 1249 1250
  {
    DBUG_PRINT("info", ("Using committed read"));
    DBUG_RETURN(NdbOperation::LM_CommittedRead);
  }
1251 1252
}

1253 1254 1255 1256 1257 1258
static const ulong index_type_flags[]=
{
  /* UNDEFINED_INDEX */
  0,                         

  /* PRIMARY_KEY_INDEX */
1259
  HA_ONLY_WHOLE_INDEX, 
1260 1261

  /* PRIMARY_KEY_ORDERED_INDEX */
1262
  /* 
mskold@mysql.com's avatar
mskold@mysql.com committed
1263
     Enable HA_KEYREAD_ONLY when "sorted" indexes are supported, 
1264 1265 1266
     thus ORDERD BY clauses can be optimized by reading directly 
     through the index.
  */
mskold@mysql.com's avatar
mskold@mysql.com committed
1267
  // HA_KEYREAD_ONLY | 
1268
  HA_READ_NEXT |
1269
  HA_READ_PREV |
1270 1271
  HA_READ_RANGE |
  HA_READ_ORDER,
1272 1273

  /* UNIQUE_INDEX */
1274
  HA_ONLY_WHOLE_INDEX,
1275

1276
  /* UNIQUE_ORDERED_INDEX */
1277
  HA_READ_NEXT |
1278
  HA_READ_PREV |
1279 1280
  HA_READ_RANGE |
  HA_READ_ORDER,
1281

1282
  /* ORDERED_INDEX */
1283
  HA_READ_NEXT |
1284
  HA_READ_PREV |
1285 1286
  HA_READ_RANGE |
  HA_READ_ORDER
1287 1288 1289 1290 1291 1292 1293
};

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);
1294
  return m_index[idx_no].type;
1295 1296
}

1297 1298 1299 1300 1301 1302
inline bool ha_ndbcluster::has_null_in_unique_index(uint idx_no) const
{
  DBUG_ASSERT(idx_no < MAX_KEY);
  return m_index[idx_no].null_in_unique_index;
}

1303 1304 1305 1306 1307 1308 1309 1310

/*
  Get the flags for an index

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

1311 1312
inline ulong ha_ndbcluster::index_flags(uint idx_no, uint part,
                                        bool all_parts) const 
1313
{ 
1314
  DBUG_ENTER("ha_ndbcluster::index_flags");
1315
  DBUG_PRINT("info", ("idx_no: %d", idx_no));
1316
  DBUG_ASSERT(get_index_type_from_table(idx_no) < index_flags_size);
1317 1318
  DBUG_RETURN(index_type_flags[get_index_type_from_table(idx_no)] | 
              HA_KEY_SCAN_NOT_ROR);
1319 1320
}

pekka@mysql.com's avatar
pekka@mysql.com committed
1321 1322
static void shrink_varchar(Field* field, const byte* & ptr, char* buf)
{
1323
  if (field->type() == MYSQL_TYPE_VARCHAR && ptr != NULL) {
pekka@mysql.com's avatar
pekka@mysql.com committed
1324
    Field_varstring* f= (Field_varstring*)field;
pekka@mysql.com's avatar
pekka@mysql.com committed
1325
    if (f->length_bytes == 1) {
pekka@mysql.com's avatar
pekka@mysql.com committed
1326 1327 1328 1329 1330
      uint pack_len= field->pack_length();
      DBUG_ASSERT(1 <= pack_len && pack_len <= 256);
      if (ptr[1] == 0) {
        buf[0]= ptr[0];
      } else {
1331
        DBUG_ASSERT(FALSE);
pekka@mysql.com's avatar
pekka@mysql.com committed
1332 1333 1334 1335 1336 1337 1338
        buf[0]= 255;
      }
      memmove(buf + 1, ptr + 2, pack_len - 1);
      ptr= buf;
    }
  }
}
1339 1340 1341

int ha_ndbcluster::set_primary_key(NdbOperation *op, const byte *key)
{
1342
  KEY* key_info= table->key_info + table->s->primary_key;
1343 1344 1345 1346 1347 1348 1349
  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;
pekka@mysql.com's avatar
pekka@mysql.com committed
1350 1351 1352
    const byte* ptr= key;
    char buf[256];
    shrink_varchar(field, ptr, buf);
1353
    if (set_ndb_key(op, field, 
1354
                    key_part->fieldnr-1, ptr))
1355
      ERR_RETURN(op->getNdbError());
pekka@mysql.com's avatar
pekka@mysql.com committed
1356
    key += key_part->store_length;
1357 1358 1359 1360 1361
  }
  DBUG_RETURN(0);
}


1362
int ha_ndbcluster::set_primary_key_from_record(NdbOperation *op, const byte *record)
1363
{
1364
  KEY* key_info= table->key_info + table->s->primary_key;
1365 1366
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
1367
  DBUG_ENTER("set_primary_key_from_record");
1368 1369 1370 1371 1372

  for (; key_part != end; key_part++) 
  {
    Field* field= key_part->field;
    if (set_ndb_key(op, field, 
1373
		    key_part->fieldnr-1, record+key_part->offset))
1374 1375 1376 1377 1378
      ERR_RETURN(op->getNdbError());
  }
  DBUG_RETURN(0);
}

1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396
int ha_ndbcluster::set_index_key_from_record(NdbOperation *op, const byte *record, uint keyno)
{
  KEY* key_info= table->key_info + keyno;
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
  uint i;
  DBUG_ENTER("set_index_key_from_record");
                                                                                
  for (i= 0; key_part != end; key_part++, i++)
  {
    Field* field= key_part->field;
    if (set_ndb_key(op, field, m_index[keyno].unique_index_attrid_map[i],
                    record+key_part->offset))
      ERR_RETURN(m_active_trans->getNdbError());
  }
  DBUG_RETURN(0);
}

1397 1398
int 
ha_ndbcluster::set_index_key(NdbOperation *op, 
1399 1400
                             const KEY *key_info, 
                             const byte * key_ptr)
1401
{
1402
  DBUG_ENTER("ha_ndbcluster::set_index_key");
1403 1404 1405 1406 1407 1408
  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++) 
  {
pekka@mysql.com's avatar
pekka@mysql.com committed
1409 1410 1411 1412
    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);
tomas@poseidon.ndb.mysql.com's avatar
Merge  
tomas@poseidon.ndb.mysql.com committed
1413
    if (set_ndb_key(op, field, m_index[active_index].unique_index_attrid_map[i], ptr))
1414 1415 1416 1417 1418
      ERR_RETURN(m_active_trans->getNdbError());
    key_ptr+= key_part->store_length;
  }
  DBUG_RETURN(0);
}
1419

1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432
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) ||
1433 1434
        ((field->flags & PRI_KEY_FLAG)) || 
        m_retrieve_all_fields)
1435 1436
    {      
      if (get_ndb_value(op, field, i, buf))
1437
        ERR_RETURN(op->getNdbError());
1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460
    } 
    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);
} 

1461 1462 1463 1464
/*
  Read one record from NDB using primary key
*/

1465
int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf) 
1466
{
1467
  uint no_fields= table->s->fields;
1468 1469
  NdbConnection *trans= m_active_trans;
  NdbOperation *op;
1470

1471 1472 1473 1474
  int res;
  DBUG_ENTER("pk_read");
  DBUG_PRINT("enter", ("key_len: %u", key_len));
  DBUG_DUMP("key", (char*)key, key_len);
1475

1476 1477
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
joreland@mysql.com's avatar
joreland@mysql.com committed
1478
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || 
1479
      op->readTuple(lm) != 0)
1480
    ERR_RETURN(trans->getNdbError());
1481
  
1482
  if (table->s->primary_key == MAX_KEY) 
1483 1484 1485 1486 1487
  {
    // 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))
1488
      ERR_RETURN(trans->getNdbError());
1489
    
1490
    // Read key at the same time, for future reference
1491
    if (get_ndb_value(op, NULL, no_fields, NULL))
1492
      ERR_RETURN(trans->getNdbError());
1493 1494 1495 1496 1497 1498 1499
  } 
  else 
  {
    if ((res= set_primary_key(op, key)))
      return res;
  }
  
1500
  if ((res= define_read_attrs(buf, op)))
1501
    DBUG_RETURN(res);
1502
  
1503
  if (execute_no_commit_ie(this,trans,false) != 0) 
1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514
  {
    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);
}

1515 1516 1517 1518 1519 1520
/*
  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)
{
1521
  uint no_fields= table->s->fields, i;
1522
  NdbTransaction *trans= m_active_trans;
1523 1524 1525 1526
  NdbOperation *op;
  THD *thd= current_thd;
  DBUG_ENTER("complemented_pk_read");

1527
  if (m_retrieve_all_fields)
1528 1529 1530
    // We have allready retrieved all fields, nothing to complement
    DBUG_RETURN(0);

1531 1532
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
joreland@mysql.com's avatar
joreland@mysql.com committed
1533
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || 
1534
      op->readTuple(lm) != 0)
1535
    ERR_RETURN(trans->getNdbError());
1536
  int res;
mskold@mysql.com's avatar
mskold@mysql.com committed
1537
  if ((res= set_primary_key_from_record(op, old_data)))
1538
    ERR_RETURN(trans->getNdbError());
1539 1540 1541 1542
  // Read all unreferenced non-key field(s)
  for (i= 0; i < no_fields; i++) 
  {
    Field *field= table->field[i];
1543
    if (!((field->flags & PRI_KEY_FLAG) ||
1544
          (thd->query_id == field->query_id)))
1545
    {
1546
      if (get_ndb_value(op, field, i, new_data))
1547
        ERR_RETURN(trans->getNdbError());
1548 1549
    }
  }
1550
  if (execute_no_commit(this,trans,false) != 0) 
1551 1552 1553 1554 1555 1556 1557 1558
  {
    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;     
1559 1560 1561 1562 1563 1564 1565 1566

  /**
   * restore m_value
   */
  for (i= 0; i < no_fields; i++) 
  {
    Field *field= table->field[i];
    if (!((field->flags & PRI_KEY_FLAG) ||
1567
          (thd->query_id == field->query_id)))
1568 1569 1570 1571 1572
    {
      m_value[i].ptr= NULL;
    }
  }
  
1573 1574 1575
  DBUG_RETURN(0);
}

1576
/*
1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635
 * Check that all operations between first and last all
 * have gotten the errcode
 * If checking for HA_ERR_KEY_NOT_FOUND then update m_dupkey
 * for all succeeding operations
 */
bool ha_ndbcluster::check_all_operations_for_error(NdbTransaction *trans,
                                                   const NdbOperation *first,
                                                   const NdbOperation *last,
                                                   uint errcode)
{
  const NdbOperation *op= first;
  DBUG_ENTER("ha_ndbcluster::check_all_operations_for_error");

  while(op)
  {
    NdbError err= op->getNdbError();
    if (err.status != NdbError::Success)
    {
      if (ndb_to_mysql_error(&err) != (int) errcode)
        DBUG_RETURN(false);
      if (op == last) break;
      op= trans->getNextCompletedOperation(op);
    }
    else
    {
      // We found a duplicate
      if (op->getType() == NdbOperation::UniqueIndexAccess)
      {
        if (errcode == HA_ERR_KEY_NOT_FOUND)
        {
          NdbIndexOperation *iop= (NdbIndexOperation *) op;
          const NDBINDEX *index= iop->getIndex();
          // Find the key_no of the index
          for(uint i= 0; i<table->s->keys; i++)
          {
            if (m_index[i].unique_index == index)
            {
              m_dupkey= i;
              break;
            }
          }
        }
      }
      else
      {
        // Must have been primary key access
        DBUG_ASSERT(op->getType() == NdbOperation::PrimaryKeyAccess);
        if (errcode == HA_ERR_KEY_NOT_FOUND)
          m_dupkey= table->s->primary_key;
      }
      DBUG_RETURN(false);      
    }
  }
  DBUG_RETURN(true);
}

/*
 * Peek to check if any rows already exist with conflicting
 * primary key or unique index values
1636 1637
*/

1638
int ha_ndbcluster::peek_indexed_rows(const byte *record, bool check_pk)
1639
{
1640
  NdbTransaction *trans= m_active_trans;
1641
  NdbOperation *op;
1642 1643 1644 1645
  const NdbOperation *first, *last;
  uint i;
  int res;
  DBUG_ENTER("peek_indexed_rows");
1646

jonas@perch.ndb.mysql.com's avatar
jonas@perch.ndb.mysql.com committed
1647 1648 1649
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
  
1650
  first= NULL;
1651
  if (check_pk && table->s->primary_key != MAX_KEY)
1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682
  {
    /*
     * Fetch any row with colliding primary key
     */
    if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) ||
        op->readTuple(lm) != 0)
      ERR_RETURN(trans->getNdbError());
    
    first= op;
    if ((res= set_primary_key_from_record(op, record)))
      ERR_RETURN(trans->getNdbError());
  }
  /*
   * Fetch any rows with colliding unique indexes
   */
  KEY* key_info;
  KEY_PART_INFO *key_part, *end;
  for (i= 0, key_info= table->key_info; i < table->s->keys; i++, key_info++)
  {
    if (i != table->s->primary_key &&
        key_info->flags & HA_NOSAME)
    {
      // A unique index is defined on table
      NdbIndexOperation *iop;
      NDBINDEX *unique_index = (NDBINDEX *) m_index[i].unique_index;
      key_part= key_info->key_part;
      end= key_part + key_info->key_parts;
      if (!(iop= trans->getNdbIndexOperation(unique_index,
                                             (const NDBTAB *) m_table)) ||
          iop->readTuple(lm) != 0)
        ERR_RETURN(trans->getNdbError());
1683

1684 1685 1686 1687 1688 1689 1690 1691
      if (!first)
        first= iop;
      if ((res= set_index_key_from_record(iop, record, i)))
        ERR_RETURN(trans->getNdbError());
    }
  }
  last= trans->getLastDefinedOperation();
  if (first)
1692
    res= execute_no_commit_ie(this,trans,false);
1693 1694 1695 1696 1697 1698 1699 1700
  else
  {
    // Table has no keys
    table->status= STATUS_NOT_FOUND;
    DBUG_RETURN(HA_ERR_KEY_NOT_FOUND);
  }
  if (check_all_operations_for_error(trans, first, last, 
                                     HA_ERR_KEY_NOT_FOUND))
1701 1702 1703 1704
  {
    table->status= STATUS_NOT_FOUND;
    DBUG_RETURN(ndb_err(trans));
  } 
1705 1706 1707 1708
  else
  {
    DBUG_PRINT("info", ("m_dupkey %d", m_dupkey));
  }
1709 1710
  DBUG_RETURN(0);
}
1711

1712 1713 1714 1715 1716
/*
  Read one record from NDB using unique secondary index
*/

int ha_ndbcluster::unique_index_read(const byte *key,
1717
                                     uint key_len, byte *buf)
1718
{
1719
  int res;
1720
  NdbTransaction *trans= m_active_trans;
1721
  NdbIndexOperation *op;
1722
  DBUG_ENTER("ha_ndbcluster::unique_index_read");
1723 1724 1725
  DBUG_PRINT("enter", ("key_len: %u, index: %u", key_len, active_index));
  DBUG_DUMP("key", (char*)key, key_len);
  
1726 1727
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
1728
  if (!(op= trans->getNdbIndexOperation((NDBINDEX *) 
1729
                                        m_index[active_index].unique_index, 
joreland@mysql.com's avatar
joreland@mysql.com committed
1730
                                        (const NDBTAB *) m_table)) ||
1731
      op->readTuple(lm) != 0)
1732 1733 1734
    ERR_RETURN(trans->getNdbError());
  
  // Set secondary index key(s)
1735
  if ((res= set_index_key(op, table->key_info + active_index, key)))
1736 1737
    DBUG_RETURN(res);
  
1738
  if ((res= define_read_attrs(buf, op)))
1739
    DBUG_RETURN(res);
1740

1741
  if (execute_no_commit_ie(this,trans,false) != 0) 
1742 1743 1744 1745 1746 1747 1748 1749 1750 1751
  {
    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);
}

1752
inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor)
1753 1754
{
  DBUG_ENTER("fetch_next");
1755
  int local_check;
1756
  NdbTransaction *trans= m_active_trans;
1757
  
1758 1759 1760 1761 1762 1763 1764 1765
    if (m_lock_tuple)
  {
    /*
      Lock level m_lock.type either TL_WRITE_ALLOW_WRITE
      (SELECT FOR UPDATE) or TL_READ_WITH_SHARED_LOCKS (SELECT
      LOCK WITH SHARE MODE) and row was not explictly unlocked 
      with unlock_row() call
    */
1766
      NdbConnection *con_trans= m_active_trans;
1767 1768 1769 1770 1771 1772
      NdbOperation *op;
      // Lock row
      DBUG_PRINT("info", ("Keeping lock on scanned row"));
      
      if (!(op= m_active_cursor->lockCurrentTuple()))
      {
1773
        /* purecov: begin inspected */
1774
	m_lock_tuple= false;
1775 1776
	ERR_RETURN(con_trans->getNdbError());
        /* purecov: end */    
1777 1778 1779 1780 1781 1782 1783
      }
      m_ops_pending++;
  }
  m_lock_tuple= false;

  bool contact_ndb= m_lock.type < TL_WRITE_ALLOW_WRITE &&
                    m_lock.type != TL_READ_WITH_SHARED_LOCKS;
1784 1785
  do {
    DBUG_PRINT("info", ("Call nextResult, contact_ndb: %d", contact_ndb));
pekka@mysql.com's avatar
pekka@mysql.com committed
1786 1787 1788
    /*
      We can only handle one tuple with blobs at a time.
    */
1789
    if (m_ops_pending && m_blobs_pending)
pekka@mysql.com's avatar
pekka@mysql.com committed
1790
    {
1791
      if (execute_no_commit(this,trans,false) != 0)
1792
        DBUG_RETURN(ndb_err(trans));
1793 1794
      m_ops_pending= 0;
      m_blobs_pending= FALSE;
pekka@mysql.com's avatar
pekka@mysql.com committed
1795
    }
1796
    
1797
    if ((local_check= cursor->nextResult(contact_ndb, m_force_send)) == 0)
1798
    {
1799 1800 1801 1802 1803 1804 1805
      /*
	Explicitly lock tuple if "select for update" or
	"select lock in share mode"
      */
      m_lock_tuple= (m_lock.type == TL_WRITE_ALLOW_WRITE
		     || 
		     m_lock.type == TL_READ_WITH_SHARED_LOCKS);
1806 1807
      DBUG_RETURN(0);
    } 
1808
    else if (local_check == 1 || local_check == 2)
1809 1810 1811
    {
      // 1: No more records
      // 2: No more cached records
1812
      
1813
      /*
1814 1815 1816
        Before fetching more rows and releasing lock(s),
        all pending update or delete operations should 
        be sent to NDB
1817
      */
1818
      DBUG_PRINT("info", ("ops_pending: %ld", (long) m_ops_pending));    
1819
      if (m_ops_pending)
1820
      {
1821 1822
        if (m_transaction_on)
        {
1823
          if (execute_no_commit(this,trans,false) != 0)
1824 1825 1826 1827 1828 1829
            DBUG_RETURN(-1);
        }
        else
        {
          if  (execute_commit(this,trans) != 0)
            DBUG_RETURN(-1);
1830
          if (trans->restart() != 0)
1831 1832 1833 1834 1835 1836
          {
            DBUG_ASSERT(0);
            DBUG_RETURN(-1);
          }
        }
        m_ops_pending= 0;
1837
      }
1838
      contact_ndb= (local_check == 2);
1839
    }
1840 1841 1842 1843
    else
    {
      DBUG_RETURN(-1);
    }
1844
  } while (local_check == 2);
1845

1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856
  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.
1857

1858 1859 1860 1861 1862 1863 1864
*/

inline int ha_ndbcluster::next_result(byte *buf)
{  
  int res;
  DBUG_ENTER("next_result");
    
1865 1866 1867
  if (!m_active_cursor)
    DBUG_RETURN(HA_ERR_END_OF_FILE);
  
1868
  if ((res= fetch_next(m_active_cursor)) == 0)
1869 1870 1871 1872 1873 1874 1875
  {
    DBUG_PRINT("info", ("One more record found"));    
    
    unpack_record(buf);
    table->status= 0;
    DBUG_RETURN(0);
  }
1876
  else if (res == 1)
1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887
  {
    // 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));
  }
1888 1889
}

1890
/*
1891
  Set bounds for ordered index scan.
1892 1893
*/

joreland@mysql.com's avatar
joreland@mysql.com committed
1894
int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
1895 1896
                              const key_range *keys[2],
                              uint range_no)
1897
{
1898 1899 1900 1901
  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;
1902
  uint i, j;
1903 1904

  DBUG_ENTER("set_bounds");
1905
  DBUG_PRINT("info", ("key_parts=%d", key_parts));
1906

1907
  for (j= 0; j <= 1; j++)
1908
  {
1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921
    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;
    }
1922 1923
  }
  tot_len= 0;
1924

1925 1926 1927 1928
  for (i= 0; i < key_parts; i++)
  {
    KEY_PART_INFO *key_part= &key_info->key_part[i];
    Field *field= key_part->field;
1929
#ifndef DBUG_OFF
1930
    uint part_len= key_part->length;
1931
#endif
1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945
    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++)
    {
1946
      struct part_st &p= part[j];
1947 1948 1949 1950 1951 1952 1953
      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];
joreland@mysql.com's avatar
joreland@mysql.com committed
1954
        p.part_null= key_part->null_bit && *p.part_ptr;
1955
        p.bound_ptr= (const char *)
joreland@mysql.com's avatar
joreland@mysql.com committed
1956
          p.part_null ? 0 : key_part->null_bit ? p.part_ptr + 1 : p.part_ptr;
1957 1958 1959 1960 1961 1962 1963 1964

        if (j == 0)
        {
          switch (p.key->flag)
          {
            case HA_READ_KEY_EXACT:
              p.bound_type= NdbIndexScanOperation::BoundEQ;
              break;
1965
            // ascending
1966 1967 1968 1969 1970 1971 1972 1973 1974
            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;
1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987
            // 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;
1988 1989 1990 1991 1992 1993 1994
            default:
              break;
          }
        }
        if (j == 1) {
          switch (p.key->flag)
          {
1995
            // ascending
1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006
            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;
2007
            // descending strangely sets no end key
2008 2009
          }
        }
2010

2011 2012 2013
        if (p.bound_type == -1)
        {
          DBUG_PRINT("error", ("key %d unknown flag %d", j, p.key->flag));
2014
          DBUG_ASSERT(FALSE);
2015
          // Stop setting bounds but continue with what we have
2016
          DBUG_RETURN(op->end_of_bound(range_no));
2017 2018 2019
        }
      }
    }
2020

2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037
    // 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;
    }
2038

2039 2040
    for (j= 0; j <= 1; j++)
    {
2041
      struct part_st &p= part[j];
2042 2043 2044 2045 2046 2047 2048 2049 2050
      // 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)
2051
        {
pekka@mysql.com's avatar
pekka@mysql.com committed
2052 2053 2054
          const char* ptr= p.bound_ptr;
          char buf[256];
          shrink_varchar(field, ptr, buf);
tomas@poseidon.ndb.mysql.com's avatar
Merge  
tomas@poseidon.ndb.mysql.com committed
2055
          if (op->setBound(i, p.bound_type, ptr))
2056
            ERR_RETURN(op->getNdbError());
2057
        }
2058 2059 2060 2061
      }
    }

    tot_len+= part_store_len;
2062
  }
2063
  DBUG_RETURN(op->end_of_bound(range_no));
2064 2065
}

2066
/*
2067
  Start ordered index scan in NDB
2068 2069
*/

2070
int ha_ndbcluster::ordered_index_scan(const key_range *start_key,
2071 2072
                                      const key_range *end_key,
                                      bool sorted, bool descending, byte* buf)
2073
{  
2074
  int res;
joreland@mysql.com's avatar
joreland@mysql.com committed
2075
  bool restart;
2076
  NdbTransaction *trans= m_active_trans;
joreland@mysql.com's avatar
joreland@mysql.com committed
2077
  NdbIndexScanOperation *op;
2078

2079 2080 2081
  DBUG_ENTER("ha_ndbcluster::ordered_index_scan");
  DBUG_PRINT("enter", ("index: %u, sorted: %d, descending: %d",
             active_index, sorted, descending));  
2082
  DBUG_PRINT("enter", ("Starting new ordered scan on %s", m_tabname));
pekka@mysql.com's avatar
pekka@mysql.com committed
2083

2084 2085
  // Check that sorted seems to be initialised
  DBUG_ASSERT(sorted == 0 || sorted == 1);
2086
  
2087
  if (m_active_cursor == 0)
joreland@mysql.com's avatar
joreland@mysql.com committed
2088
  {
2089
    restart= FALSE;
joreland@mysql.com's avatar
joreland@mysql.com committed
2090 2091
    NdbOperation::LockMode lm=
      (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
2092
    bool need_pk = (lm == NdbOperation::LM_Read);
joreland@mysql.com's avatar
joreland@mysql.com committed
2093
    if (!(op= trans->getNdbIndexScanOperation((NDBINDEX *)
2094 2095
                                              m_index[active_index].index, 
                                              (const NDBTAB *) m_table)) ||
2096
        op->readTuples(lm, 0, parallelism, sorted, descending, false, need_pk))
joreland@mysql.com's avatar
joreland@mysql.com committed
2097
      ERR_RETURN(trans->getNdbError());
2098
    m_active_cursor= op;
joreland@mysql.com's avatar
joreland@mysql.com committed
2099
  } else {
2100
    restart= TRUE;
2101
    op= (NdbIndexScanOperation*)m_active_cursor;
joreland@mysql.com's avatar
joreland@mysql.com committed
2102 2103 2104
    
    DBUG_ASSERT(op->getSorted() == sorted);
    DBUG_ASSERT(op->getLockMode() == 
2105
                (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type));
2106
    if (op->reset_bounds(m_force_send))
joreland@mysql.com's avatar
joreland@mysql.com committed
2107 2108
      DBUG_RETURN(ndb_err(m_active_trans));
  }
2109
  
2110
  {
2111
    const key_range *keys[2]= { start_key, end_key };
2112 2113 2114
    res= set_bounds(op, keys);
    if (res)
      DBUG_RETURN(res);
2115
  }
2116 2117 2118

  if (!restart && generate_scan_filter(m_cond_stack, op))
    DBUG_RETURN(ndb_err(trans));
2119
  
2120
  if (!restart && (res= define_read_attrs(buf, op)))
2121
  {
2122
    DBUG_RETURN(res);
joreland@mysql.com's avatar
joreland@mysql.com committed
2123
  }
2124

2125
  if (execute_no_commit(this,trans,false) != 0)
2126 2127 2128 2129
    DBUG_RETURN(ndb_err(trans));
  
  DBUG_RETURN(next_result(buf));
}
2130

2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166
/*
  Unique index scan in NDB (full table scan with scan filter)
 */

int ha_ndbcluster::unique_index_scan(const KEY* key_info, 
				     const byte *key, 
				     uint key_len,
				     byte *buf)
{
  int res;
  NdbScanOperation *op;
  NdbTransaction *trans= m_active_trans;

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

  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
  bool need_pk = (lm == NdbOperation::LM_Read);
  if (!(op=trans->getNdbScanOperation((const NDBTAB *) m_table)) ||
      op->readTuples(lm, 
		     (need_pk)?NdbScanOperation::SF_KeyInfo:0, 
		     parallelism))
    ERR_RETURN(trans->getNdbError());
  m_active_cursor= op;
  if (generate_scan_filter_from_key(op, key_info, key, key_len, buf))
    DBUG_RETURN(ndb_err(trans));
  if ((res= define_read_attrs(buf, op)))
    DBUG_RETURN(res);

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

2167
/*
2168
  Start full table scan in NDB
2169 2170 2171 2172
 */

int ha_ndbcluster::full_table_scan(byte *buf)
{
2173
  int res;
2174
  NdbScanOperation *op;
2175
  NdbTransaction *trans= m_active_trans;
2176 2177 2178 2179

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

2180 2181
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
2182
  bool need_pk = (lm == NdbOperation::LM_Read);
2183
  if (!(op=trans->getNdbScanOperation((const NDBTAB *) m_table)) ||
2184 2185 2186
      op->readTuples(lm, 
		     (need_pk)?NdbScanOperation::SF_KeyInfo:0, 
		     parallelism))
2187
    ERR_RETURN(trans->getNdbError());
2188
  m_active_cursor= op;
2189 2190
  if (generate_scan_filter(m_cond_stack, op))
    DBUG_RETURN(ndb_err(trans));
2191
  if ((res= define_read_attrs(buf, op)))
2192 2193
    DBUG_RETURN(res);

2194
  if (execute_no_commit(this,trans,false) != 0)
2195 2196 2197
    DBUG_RETURN(ndb_err(trans));
  DBUG_PRINT("exit", ("Scan started successfully"));
  DBUG_RETURN(next_result(buf));
2198 2199
}

2200 2201 2202 2203 2204
/*
  Insert one record into NDB
*/
int ha_ndbcluster::write_row(byte *record)
{
mskold@mysql.com's avatar
mskold@mysql.com committed
2205
  bool has_auto_increment;
2206
  uint i;
2207
  NdbTransaction *trans= m_active_trans;
2208 2209
  NdbOperation *op;
  int res;
2210
  THD *thd= table->in_use;
2211
  DBUG_ENTER("write_row");
2212

2213 2214
  has_auto_increment= (table->next_number_field && record == table->record[0]);
  if (table->s->primary_key != MAX_KEY)
2215
  {
2216 2217 2218 2219 2220
    /*
     * Increase any auto_incremented primary key
     */
    if (has_auto_increment) 
    {
2221
      int error;
2222 2223
      
      m_skip_auto_increment= FALSE;
2224 2225
      if ((error= update_auto_increment()))
        DBUG_RETURN(error);
2226 2227 2228 2229 2230 2231 2232 2233 2234
      /* Ensure that handler is always called for auto_increment values */
      thd->next_insert_id= 0;
      m_skip_auto_increment= !auto_increment_column_changed;
    }
  }
  
  /*
   * If IGNORE the ignore constraint violations on primary and unique keys
   */
2235
  if (!m_use_write && m_ignore_dup_key)
2236
  {
2237 2238 2239 2240 2241
    /*
      compare if expression with that in start_bulk_insert()
      start_bulk_insert will set parameters to ensure that each
      write_row is committed individually
    */
2242
    int peek_res= peek_indexed_rows(record, true);
2243 2244 2245 2246 2247 2248 2249
    
    if (!peek_res) 
    {
      DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
    }
    if (peek_res != HA_ERR_KEY_NOT_FOUND)
      DBUG_RETURN(peek_res);
2250
  }
2251

2252
  statistic_increment(thd->status_var.ha_write_count, &LOCK_status);
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
2253 2254
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
    table->timestamp_field->set_time();
2255

joreland@mysql.com's avatar
joreland@mysql.com committed
2256
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)))
2257 2258 2259 2260 2261 2262
    ERR_RETURN(trans->getNdbError());

  res= (m_use_write) ? op->writeTuple() :op->insertTuple(); 
  if (res != 0)
    ERR_RETURN(trans->getNdbError());  
 
2263
  if (table->s->primary_key == MAX_KEY) 
2264 2265
  {
    // Table has hidden primary key
2266
    Ndb *ndb= get_ndb();
2267 2268
    int ret;
    Uint64 auto_value;
2269 2270
    uint retries= NDB_AUTO_INCREMENT_RETRIES;
    do {
2271 2272
      ret= ndb->getAutoIncrementValue((const NDBTAB *) m_table, auto_value, 1);
    } while (ret == -1 && 
2273 2274
             --retries &&
             ndb->getNdbError().status == NdbError::TemporaryError);
2275
    if (ret == -1)
2276
      ERR_RETURN(ndb->getNdbError());
2277
    if (set_hidden_key(op, table->s->fields, (const byte*)&auto_value))
2278 2279 2280 2281
      ERR_RETURN(op->getNdbError());
  } 
  else 
  {
2282
    if ((res= set_primary_key_from_record(op, record)))
2283
      return res;  
2284 2285 2286
  }

  // Set non-key attribute(s)
2287
  bool set_blob_value= FALSE;
2288
  for (i= 0; i < table->s->fields; i++) 
2289 2290 2291
  {
    Field *field= table->field[i];
    if (!(field->flags & PRI_KEY_FLAG) &&
2292
        set_ndb_value(op, field, i, &set_blob_value))
2293
    {
2294
      m_skip_auto_increment= TRUE;
2295
      ERR_RETURN(op->getNdbError());
2296
    }
2297 2298
  }

2299 2300
  m_rows_changed++;

2301 2302 2303 2304 2305 2306 2307
  /*
    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!
  */
2308
  m_rows_inserted++;
2309
  no_uncommitted_rows_update(1);
2310
  m_bulk_insert_not_flushed= TRUE;
2311
  if ((m_rows_to_insert == (ha_rows) 1) || 
2312
      ((m_rows_inserted % m_bulk_insert_rows) == 0) ||
2313
      m_primary_key_update ||
2314
      set_blob_value)
2315 2316 2317
  {
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
2318 2319
                        "rows_inserted:%d, bulk_insert_rows: %d", 
                        (int)m_rows_inserted, (int)m_bulk_insert_rows));
2320

2321
    m_bulk_insert_not_flushed= FALSE;
2322
    if (m_transaction_on)
2323
    {
2324
      if (execute_no_commit(this,trans,false) != 0)
2325
      {
2326 2327 2328
        m_skip_auto_increment= TRUE;
        no_uncommitted_rows_execute_failure();
        DBUG_RETURN(ndb_err(trans));
2329
      }
2330 2331
    }
    else
2332
    {
2333
      if (execute_commit(this,trans) != 0)
2334
      {
2335 2336 2337
        m_skip_auto_increment= TRUE;
        no_uncommitted_rows_execute_failure();
        DBUG_RETURN(ndb_err(trans));
2338
      }
2339
      if (trans->restart() != 0)
2340
      {
2341 2342
        DBUG_ASSERT(0);
        DBUG_RETURN(-1);
2343
      }
2344
    }
2345
  }
2346
  if ((has_auto_increment) && (m_skip_auto_increment))
mskold@mysql.com's avatar
mskold@mysql.com committed
2347
  {
2348
    Ndb *ndb= get_ndb();
2349
    Uint64 next_val= (Uint64) table->next_number_field->val_int() + 1;
2350
#ifndef DBUG_OFF
2351
    char buff[22];
mskold@mysql.com's avatar
mskold@mysql.com committed
2352
    DBUG_PRINT("info", 
2353 2354
               ("Trying to set next auto increment value to %s",
                llstr(next_val, buff)));
2355
#endif
2356
    if (ndb->setAutoIncrementValue((const NDBTAB *) m_table, next_val, TRUE)
2357
        == -1)
2358
      ERR_RETURN(ndb->getNdbError());
2359
  }
2360
  m_skip_auto_increment= TRUE;
2361

2362 2363 2364 2365 2366 2367 2368
  DBUG_RETURN(0);
}


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

int ha_ndbcluster::key_cmp(uint keynr, const byte * old_row,
2369
                           const byte * new_row)
2370 2371 2372 2373 2374 2375 2376 2377 2378
{
  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) !=
2379 2380
          (new_row[key_part->null_offset] & key_part->null_bit))
        return 1;
2381
    }
2382
    if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
2383 2384 2385
    {

      if (key_part->field->cmp_binary((char*) (old_row + key_part->offset),
2386 2387 2388
                                      (char*) (new_row + key_part->offset),
                                      (ulong) key_part->length))
        return 1;
2389 2390 2391 2392
    }
    else
    {
      if (memcmp(old_row+key_part->offset, new_row+key_part->offset,
2393 2394
                 key_part->length))
        return 1;
2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406
    }
  }
  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;
2407
  NdbTransaction *trans= m_active_trans;
2408
  NdbScanOperation* cursor= m_active_cursor;
2409 2410
  NdbOperation *op;
  uint i;
2411 2412
  bool pk_update= (table->s->primary_key != MAX_KEY &&
		   key_cmp(table->s->primary_key, old_data, new_data));
2413 2414
  DBUG_ENTER("update_row");
  
2415
  /*
2416 2417
   * If IGNORE the ignore constraint violations on primary and unique keys,
   * but check that it is not part of INSERT ... ON DUPLICATE KEY UPDATE
2418
   */
2419
  if (m_ignore_dup_key && thd->lex->sql_command == SQLCOM_UPDATE)
2420
  {
2421
    int peek_res= peek_indexed_rows(new_data, pk_update);
2422 2423 2424 2425 2426 2427 2428 2429 2430
    
    if (!peek_res) 
    {
      DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
    }
    if (peek_res != HA_ERR_KEY_NOT_FOUND)
      DBUG_RETURN(peek_res);
  }

2431
  statistic_increment(thd->status_var.ha_update_count, &LOCK_status);
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
2432
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
2433
  {
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
2434
    table->timestamp_field->set_time();
2435 2436 2437
    // Set query_id so that field is really updated
    table->timestamp_field->query_id= thd->query_id;
  }
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
2438

2439
  /* Check for update of primary key for special handling */  
2440
  if (pk_update)
2441
  {
2442
    int read_res, insert_res, delete_res, undo_res;
2443

2444
    DBUG_PRINT("info", ("primary key update, doing pk read+delete+insert"));
2445
    // Get all old fields, since we optimize away fields not in query
2446
    read_res= complemented_pk_read(old_data, new_data);
2447 2448 2449 2450 2451
    if (read_res)
    {
      DBUG_PRINT("info", ("pk read failed"));
      DBUG_RETURN(read_res);
    }
2452
    // Delete old row
2453
    m_primary_key_update= TRUE;
2454
    delete_res= delete_row(old_data);
2455
    m_primary_key_update= FALSE;
2456 2457 2458
    if (delete_res)
    {
      DBUG_PRINT("info", ("delete failed"));
2459
      DBUG_RETURN(delete_res);
2460
    }     
2461 2462
    // Insert new row
    DBUG_PRINT("info", ("delete succeded"));
2463
    m_primary_key_update= TRUE;
2464
    insert_res= write_row(new_data);
2465
    m_primary_key_update= FALSE;
2466 2467 2468 2469 2470
    if (insert_res)
    {
      DBUG_PRINT("info", ("insert failed"));
      if (trans->commitStatus() == NdbConnection::Started)
      {
2471
        // Undo delete_row(old_data)
2472
        m_primary_key_update= TRUE;
2473 2474 2475 2476 2477 2478
        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");
2479 2480 2481 2482 2483
        m_primary_key_update= FALSE;
      }
      DBUG_RETURN(insert_res);
    }
    DBUG_PRINT("info", ("delete+insert succeeded"));
2484
    DBUG_RETURN(0);
2485
  }
2486

2487
  if (cursor)
2488
  {
2489 2490 2491 2492 2493 2494 2495 2496
    /*
      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"));
2497
    if (!(op= cursor->updateCurrentTuple()))
2498
      ERR_RETURN(trans->getNdbError());
2499
    m_lock_tuple= false;
2500
    m_ops_pending++;
2501
    if (uses_blob_value(FALSE))
2502
      m_blobs_pending= TRUE;
2503 2504 2505
  }
  else
  {  
joreland@mysql.com's avatar
joreland@mysql.com committed
2506
    if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) ||
2507
        op->updateTuple() != 0)
2508 2509
      ERR_RETURN(trans->getNdbError());  
    
2510
    if (table->s->primary_key == MAX_KEY) 
2511 2512 2513 2514 2515
    {
      // 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 
2516 2517
      // read into m_ref
      DBUG_DUMP("key", m_ref, NDB_HIDDEN_PRIMARY_KEY_LENGTH);
2518
      
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
2519
      if (set_hidden_key(op, table->s->fields, m_ref))
2520
        ERR_RETURN(op->getNdbError());
2521 2522 2523 2524
    } 
    else 
    {
      int res;
2525
      if ((res= set_primary_key_from_record(op, old_data)))
2526
        DBUG_RETURN(res);
2527
    }
2528 2529
  }

2530 2531
  m_rows_changed++;

2532
  // Set non-key attribute(s)
2533
  for (i= 0; i < table->s->fields; i++) 
2534 2535
  {
    Field *field= table->field[i];
2536
    if (((thd->query_id == field->query_id) || m_retrieve_all_fields) &&
2537
        (!(field->flags & PRI_KEY_FLAG)) &&
2538
        set_ndb_value(op, field, i))
2539 2540
      ERR_RETURN(op->getNdbError());
  }
2541

2542 2543 2544 2545 2546 2547 2548
  /*
    Execute update operation if we are not doing a scan for update
    and there exist UPDATE AFTER triggers
  */

  if ((!cursor || m_update_cannot_batch) && 
      execute_no_commit(this,trans,false) != 0) {
2549
    no_uncommitted_rows_execute_failure();
2550
    DBUG_RETURN(ndb_err(trans));
2551
  }
2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562
  
  DBUG_RETURN(0);
}


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

int ha_ndbcluster::delete_row(const byte *record)
{
2563
  THD *thd= current_thd;
2564
  NdbTransaction *trans= m_active_trans;
2565
  NdbScanOperation* cursor= m_active_cursor;
2566 2567 2568
  NdbOperation *op;
  DBUG_ENTER("delete_row");

2569
  statistic_increment(thd->status_var.ha_delete_count,&LOCK_status);
2570
  m_rows_changed++;
2571

2572
  if (cursor)
2573
  {
2574
    /*
2575
      We are scanning records and want to delete the record
2576
      that was just found, call deleteTuple on the cursor 
2577
      to take over the lock to a new delete operation
2578 2579 2580 2581
      And thus setting the primary key of the record from 
      the active record in cursor
    */
    DBUG_PRINT("info", ("Calling deleteTuple on cursor"));
2582
    if (cursor->deleteCurrentTuple() != 0)
2583
      ERR_RETURN(trans->getNdbError());     
2584
    m_lock_tuple= false;
2585
    m_ops_pending++;
2586

2587 2588
    no_uncommitted_rows_update(-1);

2589
    if (!(m_primary_key_update || m_delete_cannot_batch))
2590 2591
      // If deleting from cursor, NoCommit will be handled in next_result
      DBUG_RETURN(0);
2592 2593
  }
  else
2594
  {
2595
    
joreland@mysql.com's avatar
joreland@mysql.com committed
2596
    if (!(op=trans->getNdbOperation((const NDBTAB *) m_table)) || 
2597
        op->deleteTuple() != 0)
2598 2599
      ERR_RETURN(trans->getNdbError());
    
2600 2601
    no_uncommitted_rows_update(-1);
    
2602
    if (table->s->primary_key == MAX_KEY) 
2603 2604 2605 2606
    {
      // This table has no primary key, use "hidden" primary key
      DBUG_PRINT("info", ("Using hidden key"));
      
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
2607
      if (set_hidden_key(op, table->s->fields, m_ref))
2608
        ERR_RETURN(op->getNdbError());
2609 2610 2611 2612
    } 
    else 
    {
      int res;
2613 2614
      if ((res= set_primary_key_from_record(op, record)))
        return res;  
2615
    }
2616
  }
2617

2618
  // Execute delete operation
2619
  if (execute_no_commit(this,trans,false) != 0) {
2620
    no_uncommitted_rows_execute_failure();
2621
    DBUG_RETURN(ndb_err(trans));
2622
  }
2623 2624
  DBUG_RETURN(0);
}
2625
  
2626 2627 2628 2629 2630
/*
  Unpack a record read from NDB 

  SYNOPSIS
    unpack_record()
2631
    buf                 Buffer to store read row
2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643

  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;
pekka@mysql.com's avatar
pekka@mysql.com committed
2644
  NdbValue *value= m_value;
2645
  DBUG_ENTER("unpack_record");
2646

joreland@mysql.com's avatar
merge  
joreland@mysql.com committed
2647
  end= table->field + table->s->fields;
2648 2649
  
  // Set null flag(s)
2650
  bzero(buf, table->s->null_bytes);
joreland@mysql.com's avatar
merge  
joreland@mysql.com committed
2651
  for (field= table->field;
2652 2653 2654
       field < end;
       field++, value++)
  {
pekka@mysql.com's avatar
pekka@mysql.com committed
2655 2656 2657 2658 2659 2660
    if ((*value).ptr)
    {
      if (! ((*field)->flags & BLOB_FLAG))
      {
        if ((*value).rec->isNULL())
         (*field)->set_null(row_offset);
2661 2662 2663 2664 2665 2666
        else if ((*field)->type() == MYSQL_TYPE_BIT)
        {
          uint pack_len= (*field)->pack_length();
          if (pack_len < 5)
          {
            DBUG_PRINT("info", ("bit field H'%.8X", 
2667
                                (*value).rec->u_32_value()));
2668
            ((Field_bit *) *field)->store((longlong) 
2669 2670
                                          (*value).rec->u_32_value(),
                                          FALSE);
2671 2672 2673 2674 2675 2676
          }
          else
          {
            DBUG_PRINT("info", ("bit field H'%.8X%.8X",
                                *(Uint32 *)(*value).rec->aRef(),
                                *((Uint32 *)(*value).rec->aRef()+1)));
2677 2678 2679
#ifdef WORDS_BIGENDIAN
            /* lsw is stored first */
            Uint32 *buf= (Uint32 *)(*value).rec->aRef();
2680
            ((Field_bit *) *field)->store((((longlong)*buf)
joerg@trift2's avatar
joerg@trift2 committed
2681
                                           & 0x000000000FFFFFFFFLL)
2682 2683
                                          |
                                          ((((longlong)*(buf+1)) << 32)
joerg@trift2's avatar
joerg@trift2 committed
2684
                                           & 0xFFFFFFFF00000000LL),
2685 2686
                                          TRUE);
#else
2687
            ((Field_bit *) *field)->store((longlong)
2688
                                          (*value).rec->u_64_value(), TRUE);
2689
#endif
2690
          }
2691
        }
pekka@mysql.com's avatar
pekka@mysql.com committed
2692 2693 2694 2695
      }
      else
      {
        NdbBlob* ndb_blob= (*value).blob;
2696
        bool isNull= TRUE;
2697 2698 2699
#ifndef DBUG_OFF
        int ret= 
#endif
2700
          ndb_blob->getNull(isNull);
pekka@mysql.com's avatar
pekka@mysql.com committed
2701 2702
        DBUG_ASSERT(ret == 0);
        if (isNull)
2703
          (*field)->set_null(row_offset);
pekka@mysql.com's avatar
pekka@mysql.com committed
2704 2705
      }
    }
2706
  }
2707
  
2708 2709
#ifndef DBUG_OFF
  // Read and print all values that was fetched
2710
  if (table->s->primary_key == MAX_KEY)
2711 2712
  {
    // Table with hidden primary key
2713
    int hidden_no= table->s->fields;
2714
    char buff[22];
joreland@mysql.com's avatar
joreland@mysql.com committed
2715
    const NDBTAB *tab= (const NDBTAB *) m_table;
2716
    const NDBCOL *hidden_col= tab->getColumn(hidden_no);
2717
    const NdbRecAttr* rec= m_value[hidden_no].rec;
2718
    DBUG_ASSERT(rec);
2719
    DBUG_PRINT("hidden", ("%d: %s \"%s\"", hidden_no, 
2720
			  hidden_col->getName(),
2721
                          llstr(rec->u_64_value(), buff)));
2722
  }
2723
  print_results();
2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738
#endif
  DBUG_VOID_RETURN;
}

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

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

#ifndef DBUG_OFF
  if (!_db_on_)
    DBUG_VOID_RETURN;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
2739

2740
  char buf_type[MAX_FIELD_WIDTH], buf_val[MAX_FIELD_WIDTH];
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
2741
  String type(buf_type, sizeof(buf_type), &my_charset_bin);
2742
  String val(buf_val, sizeof(buf_val), &my_charset_bin);
2743
  for (uint f= 0; f < table->s->fields; f++)
2744
  {
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
2745
    /* Use DBUG_PRINT since DBUG_FILE cannot be filtered out */
2746
    char buf[2000];
2747
    Field *field;
2748
    void* ptr;
pekka@mysql.com's avatar
pekka@mysql.com committed
2749
    NdbValue value;
2750

2751
    buf[0]= 0;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
2752
    field= table->field[f];
pekka@mysql.com's avatar
pekka@mysql.com committed
2753
    if (!(value= m_value[f]).ptr)
2754
    {
2755
      strmov(buf, "not read");
2756
      goto print_value;
2757
    }
2758

2759
    ptr= field->ptr;
pekka@mysql.com's avatar
pekka@mysql.com committed
2760 2761

    if (! (field->flags & BLOB_FLAG))
2762
    {
pekka@mysql.com's avatar
pekka@mysql.com committed
2763 2764
      if (value.rec->isNULL())
      {
2765
        strmov(buf, "NULL");
2766
        goto print_value;
pekka@mysql.com's avatar
pekka@mysql.com committed
2767
      }
2768 2769 2770 2771 2772
      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());
pekka@mysql.com's avatar
pekka@mysql.com committed
2773 2774 2775
    }
    else
    {
2776
      NdbBlob *ndb_blob= value.blob;
2777
      bool isNull= TRUE;
pekka@mysql.com's avatar
pekka@mysql.com committed
2778
      ndb_blob->getNull(isNull);
2779 2780
      if (isNull)
        strmov(buf, "NULL");
2781
    }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
2782

2783
print_value:
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
2784
    DBUG_PRINT("value", ("%u,%s: %s", f, field->field_name, buf));
2785 2786 2787 2788 2789 2790 2791 2792
  }
#endif
  DBUG_VOID_RETURN;
}


int ha_ndbcluster::index_init(uint index)
{
2793
  DBUG_ENTER("ha_ndbcluster::index_init");
2794
  DBUG_PRINT("enter", ("index: %u", index));
2795 2796 2797 2798 2799 2800
 /*
    Locks are are explicitly released in scan
    unless m_lock.type == TL_READ_HIGH_PRIORITY
    and no sub-sequent call to unlock_row()
   */
  m_lock_tuple= false;
2801 2802 2803 2804 2805 2806
  DBUG_RETURN(handler::index_init(index));
}


int ha_ndbcluster::index_end()
{
2807
  DBUG_ENTER("ha_ndbcluster::index_end");
2808
  DBUG_RETURN(close_scan());
2809 2810
}

2811 2812 2813 2814 2815 2816 2817 2818
/**
 * 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;
2819
  const byte* end_ptr= key + key_len;
2820 2821 2822 2823 2824 2825
  curr_part= key_info->key_part;
  end_part= curr_part + key_info->key_parts;
  

  for (; curr_part != end_part && key < end_ptr; curr_part++)
  {
2826
    if (curr_part->null_bit && *key)
2827 2828 2829 2830 2831 2832
      return 1;

    key += curr_part->store_length;
  }
  return 0;
}
2833 2834

int ha_ndbcluster::index_read(byte *buf,
2835 2836
                              const byte *key, uint key_len, 
                              enum ha_rkey_function find_flag)
2837
{
2838
  DBUG_ENTER("ha_ndbcluster::index_read");
2839 2840 2841
  DBUG_PRINT("enter", ("active_index: %u, key_len: %u, find_flag: %d", 
                       active_index, key_len, find_flag));

joreland@mysql.com's avatar
joreland@mysql.com committed
2842
  int error;
2843 2844
  ndb_index_type type= get_index_type(active_index);
  const KEY* key_info= table->key_info+active_index;
joreland@mysql.com's avatar
joreland@mysql.com committed
2845 2846 2847 2848 2849
  switch (type){
  case PRIMARY_KEY_ORDERED_INDEX:
  case PRIMARY_KEY_INDEX:
    if (find_flag == HA_READ_KEY_EXACT && key_info->key_length == key_len)
    {
2850
      if (m_active_cursor && (error= close_scan()))
2851
        DBUG_RETURN(error);
joreland@mysql.com's avatar
joreland@mysql.com committed
2852 2853 2854 2855 2856 2857 2858 2859 2860
      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:
2861
    if (find_flag == HA_READ_KEY_EXACT && key_info->key_length == key_len &&
2862
        !check_null_in_key(key_info, key, key_len))
joreland@mysql.com's avatar
joreland@mysql.com committed
2863
    {
2864
      if (m_active_cursor && (error= close_scan()))
2865
        DBUG_RETURN(error);
joreland@mysql.com's avatar
joreland@mysql.com committed
2866 2867 2868 2869
      DBUG_RETURN(unique_index_read(key, key_len, buf));
    }
    else if (type == UNIQUE_INDEX)
    {
2870
      DBUG_RETURN(unique_index_scan(key_info, key, key_len, buf));
joreland@mysql.com's avatar
joreland@mysql.com committed
2871 2872 2873 2874 2875 2876
    }
    break;
  case ORDERED_INDEX:
    break;
  default:
  case UNDEFINED_INDEX:
2877
    DBUG_ASSERT(FALSE);
2878
    DBUG_RETURN(1);
joreland@mysql.com's avatar
joreland@mysql.com committed
2879 2880 2881
    break;
  }
  
2882
  key_range start_key;
2883 2884 2885
  start_key.key= key;
  start_key.length= key_len;
  start_key.flag= find_flag;
2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897
  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);  
joreland@mysql.com's avatar
joreland@mysql.com committed
2898
  DBUG_RETURN(error == HA_ERR_END_OF_FILE ? HA_ERR_KEY_NOT_FOUND : error);
2899 2900 2901 2902
}


int ha_ndbcluster::index_read_idx(byte *buf, uint index_no, 
2903 2904
                              const byte *key, uint key_len, 
                              enum ha_rkey_function find_flag)
2905
{
2906
  statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status);
2907
  DBUG_ENTER("ha_ndbcluster::index_read_idx");
2908 2909 2910 2911 2912 2913 2914 2915
  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)
{
2916
  DBUG_ENTER("ha_ndbcluster::index_next");
2917
  statistic_increment(current_thd->status_var.ha_read_next_count,
2918
                      &LOCK_status);
2919
  DBUG_RETURN(next_result(buf));
2920 2921 2922 2923 2924
}


int ha_ndbcluster::index_prev(byte *buf)
{
2925
  DBUG_ENTER("ha_ndbcluster::index_prev");
2926
  statistic_increment(current_thd->status_var.ha_read_prev_count,
2927
                      &LOCK_status);
2928
  DBUG_RETURN(next_result(buf));
2929 2930 2931 2932 2933
}


int ha_ndbcluster::index_first(byte *buf)
{
2934
  DBUG_ENTER("ha_ndbcluster::index_first");
2935
  statistic_increment(current_thd->status_var.ha_read_first_count,
2936
                      &LOCK_status);
2937 2938 2939
  // Start the ordered index scan and fetch the first row

  // Only HA_READ_ORDER indexes get called by index_first
2940
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, FALSE, buf));
2941 2942 2943 2944 2945
}


int ha_ndbcluster::index_last(byte *buf)
{
2946
  DBUG_ENTER("ha_ndbcluster::index_last");
2947
  statistic_increment(current_thd->status_var.ha_read_last_count,&LOCK_status);
2948
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, TRUE, buf));
2949 2950
}

2951 2952 2953 2954 2955
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));
}
2956

2957 2958
inline
int ha_ndbcluster::read_range_first_to_buf(const key_range *start_key,
2959 2960 2961
                                           const key_range *end_key,
                                           bool eq_r, bool sorted,
                                           byte* buf)
2962
{
2963 2964
   ndb_index_type type= get_index_type(active_index);
KEY* key_info;
2965 2966
  int error= 1; 
  DBUG_ENTER("ha_ndbcluster::read_range_first_to_buf");
2967
  DBUG_PRINT("info", ("eq_r: %d, sorted: %d", eq_r, sorted));
2968

2969
  switch (type){
2970
  case PRIMARY_KEY_ORDERED_INDEX:
2971
  case PRIMARY_KEY_INDEX:
2972 2973
    key_info= table->key_info + active_index;
    if (start_key && 
2974 2975
        start_key->length == key_info->key_length &&
        start_key->flag == HA_READ_KEY_EXACT)
2976
    {
2977
      if (m_active_cursor && (error= close_scan()))
2978
        DBUG_RETURN(error);
2979 2980 2981
      error= pk_read(start_key->key, start_key->length, buf);      
      DBUG_RETURN(error == HA_ERR_KEY_NOT_FOUND ? HA_ERR_END_OF_FILE : error);
    }
2982
    break;
2983
  case UNIQUE_ORDERED_INDEX:
2984
  case UNIQUE_INDEX:
2985
    key_info= table->key_info + active_index;
2986
    if (start_key && start_key->length == key_info->key_length &&
2987 2988
        start_key->flag == HA_READ_KEY_EXACT && 
        !check_null_in_key(key_info, start_key->key, start_key->length))
2989
    {
2990
      if (m_active_cursor && (error= close_scan()))
2991
        DBUG_RETURN(error);
2992 2993 2994
      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);
    }
2995
    else if (type == UNIQUE_INDEX)
2996 2997 2998 2999
      DBUG_RETURN(unique_index_scan(key_info, 
				    start_key->key, 
				    start_key->length, 
				    buf));
3000 3001 3002 3003
    break;
  default:
    break;
  }
3004 3005

  // Start the ordered index scan and fetch the first row
3006
  error= ordered_index_scan(start_key, end_key, sorted, FALSE, buf);
3007 3008 3009
  DBUG_RETURN(error);
}

3010

joreland@mysql.com's avatar
joreland@mysql.com committed
3011
int ha_ndbcluster::read_range_first(const key_range *start_key,
3012 3013
                                    const key_range *end_key,
                                    bool eq_r, bool sorted)
joreland@mysql.com's avatar
joreland@mysql.com committed
3014 3015 3016 3017 3018
{
  byte* buf= table->record[0];
  DBUG_ENTER("ha_ndbcluster::read_range_first");
  
  DBUG_RETURN(read_range_first_to_buf(start_key,
3019 3020 3021 3022
                                      end_key,
                                      eq_r, 
                                      sorted,
                                      buf));
joreland@mysql.com's avatar
joreland@mysql.com committed
3023 3024
}

3025
int ha_ndbcluster::read_range_next()
3026 3027 3028 3029 3030 3031
{
  DBUG_ENTER("ha_ndbcluster::read_range_next");
  DBUG_RETURN(next_result(table->record[0]));
}


3032 3033
int ha_ndbcluster::rnd_init(bool scan)
{
3034
  NdbScanOperation *cursor= m_active_cursor;
3035 3036
  DBUG_ENTER("rnd_init");
  DBUG_PRINT("enter", ("scan: %d", scan));
3037
  // Check if scan is to be restarted
mskold@mysql.com's avatar
mskold@mysql.com committed
3038 3039 3040 3041
  if (cursor)
  {
    if (!scan)
      DBUG_RETURN(1);
3042
    if (cursor->restart(m_force_send) != 0)
3043 3044 3045 3046
    {
      DBUG_ASSERT(0);
      DBUG_RETURN(-1);
    }
mskold@mysql.com's avatar
mskold@mysql.com committed
3047
  }
3048
  index_init(table->s->primary_key);
3049 3050 3051
  DBUG_RETURN(0);
}

3052 3053
int ha_ndbcluster::close_scan()
{
3054
  NdbTransaction *trans= m_active_trans;
3055 3056
  DBUG_ENTER("close_scan");

3057 3058
  m_multi_cursor= 0;
  if (!m_active_cursor && !m_multi_cursor)
3059 3060
    DBUG_RETURN(1);

3061
  NdbScanOperation *cursor= m_active_cursor ? m_active_cursor : m_multi_cursor;
3062
  
3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074
  if (m_lock_tuple)
  {
    /*
      Lock level m_lock.type either TL_WRITE_ALLOW_WRITE
      (SELECT FOR UPDATE) or TL_READ_WITH_SHARED_LOCKS (SELECT
      LOCK WITH SHARE MODE) and row was not explictly unlocked 
      with unlock_row() call
    */
      NdbOperation *op;
      // Lock row
      DBUG_PRINT("info", ("Keeping lock on scanned row"));
      
3075
      if (!(op= cursor->lockCurrentTuple()))
3076 3077 3078 3079 3080 3081
      {
	m_lock_tuple= false;
	ERR_RETURN(trans->getNdbError());
      }
      m_ops_pending++;      
  }
3082
  m_lock_tuple= false;
3083
  if (m_ops_pending)
3084 3085 3086 3087 3088
  {
    /*
      Take over any pending transactions to the 
      deleteing/updating transaction before closing the scan    
    */
3089
    DBUG_PRINT("info", ("ops_pending: %ld", (long) m_ops_pending));    
3090
    if (execute_no_commit(this,trans,false) != 0) {
3091
      no_uncommitted_rows_execute_failure();
3092
      DBUG_RETURN(ndb_err(trans));
3093
    }
3094
    m_ops_pending= 0;
3095 3096
  }
  
3097
  cursor->close(m_force_send, TRUE);
3098
  m_active_cursor= m_multi_cursor= NULL;
mskold@mysql.com's avatar
mskold@mysql.com committed
3099
  DBUG_RETURN(0);
3100
}
3101 3102 3103 3104

int ha_ndbcluster::rnd_end()
{
  DBUG_ENTER("rnd_end");
3105
  DBUG_RETURN(close_scan());
3106 3107 3108 3109 3110 3111
}


int ha_ndbcluster::rnd_next(byte *buf)
{
  DBUG_ENTER("rnd_next");
3112
  statistic_increment(current_thd->status_var.ha_read_rnd_next_count,
3113
                      &LOCK_status);
3114

3115
  if (!m_active_cursor)
3116 3117
    DBUG_RETURN(full_table_scan(buf));
  DBUG_RETURN(next_result(buf));
3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130
}


/*
  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");
3131
  statistic_increment(current_thd->status_var.ha_read_rnd_count,
3132
                      &LOCK_status);
3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152
  // 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");

3153
  if (table->s->primary_key != MAX_KEY) 
3154
  {
3155
    key_info= table->key_info + table->s->primary_key;
3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170
    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;
      }
3171 3172 3173 3174

      size_t len = key_part->length;
      const byte * ptr = record + key_part->offset;
      Field *field = key_part->field;
3175
      if (field->type() ==  MYSQL_TYPE_VARCHAR)
3176
      {
3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190
        if (((Field_varstring*)field)->length_bytes == 1)
        {
          /**
           * Keys always use 2 bytes length
           */
          buff[0] = ptr[0];
          buff[1] = 0;
          memcpy(buff+2, ptr + 1, len);
        }
        else
        {
          memcpy(buff, ptr, len + 2);
        }
        len += 2;
3191 3192 3193
      }
      else
      {
3194
        memcpy(buff, ptr, len);
3195 3196
      }
      buff += len;
3197 3198 3199 3200 3201 3202
    }
  } 
  else 
  {
    // No primary key, get hidden key
    DBUG_PRINT("info", ("Getting hidden key"));
3203
#ifndef DBUG_OFF
3204
    int hidden_no= table->s->fields;
joreland@mysql.com's avatar
joreland@mysql.com committed
3205
    const NDBTAB *tab= (const NDBTAB *) m_table;  
3206 3207 3208 3209
    const NDBCOL *hidden_col= tab->getColumn(hidden_no);
    DBUG_ASSERT(hidden_col->getPrimaryKey() && 
                hidden_col->getAutoIncrement() &&
                ref_length == NDB_HIDDEN_PRIMARY_KEY_LENGTH);
3210
#endif
3211
    memcpy(ref, m_ref, ref_length);
3212 3213 3214 3215 3216 3217 3218
  }
  
  DBUG_DUMP("ref", (char*)ref, ref_length);
  DBUG_VOID_RETURN;
}


3219
int ha_ndbcluster::info(uint flag)
3220
{
3221
  int result= 0;
3222 3223 3224 3225 3226 3227 3228 3229 3230 3231
  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)
3232
  {
3233
    DBUG_PRINT("info", ("HA_STATUS_VARIABLE"));
3234 3235
    if (m_table_info)
    {
3236
      if (m_ha_not_exact_count)
3237
        records= 100;
3238
      else
3239
	result= records_update();
3240 3241 3242
    }
    else
    {
3243
      if ((my_errno= check_ndb_connection()))
3244
        DBUG_RETURN(my_errno);
3245
      Ndb *ndb= get_ndb();
3246
      struct Ndb_statistics stat;
3247 3248 3249 3250
      if (ndb->setDatabaseName(m_dbname))
      {
        DBUG_RETURN(my_errno= HA_ERR_OUT_OF_MEM);
      }
3251
      if (current_thd->variables.ndb_use_exact_count &&
stewart@willster.(none)'s avatar
stewart@willster.(none) committed
3252 3253
          (result= ndb_get_table_statistics(this, true, ndb, m_tabname, &stat))
          == 0)
3254
      {
3255 3256 3257
        mean_rec_length= stat.row_size;
        data_file_length= stat.fragment_memory;
        records= stat.row_count;
3258 3259 3260
      }
      else
      {
3261 3262
        mean_rec_length= 0;
        records= 100;
3263
      }
3264
    }
3265
  }
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
3266 3267 3268 3269 3270
  if (flag & HA_STATUS_CONST)
  {
    DBUG_PRINT("info", ("HA_STATUS_CONST"));
    set_rec_per_key();
  }
3271
  if (flag & HA_STATUS_ERRKEY)
3272
  {
3273
    DBUG_PRINT("info", ("HA_STATUS_ERRKEY"));
3274
    errkey= m_dupkey;
3275
  }
3276
  if (flag & HA_STATUS_AUTO)
3277
  {
3278
    DBUG_PRINT("info", ("HA_STATUS_AUTO"));
3279
    if (m_table && table->found_next_number_field)
3280 3281 3282
    {
      Ndb *ndb= get_ndb();
      
3283
      Uint64 auto_increment_value64;
3284
      if (ndb->readAutoIncrementValue((const NDBTAB *) m_table,
3285
                                      auto_increment_value64) == -1)
3286 3287 3288 3289 3290 3291
      {
        const NdbError err= ndb->getNdbError();
        sql_print_error("Error %lu in readAutoIncrementValue(): %s",
                        (ulong) err.code, err.message);
        auto_increment_value= ~(Uint64)0;
      }
3292 3293
      else
        auto_increment_value= (ulonglong)auto_increment_value64;
3294 3295
    }
  }
3296 3297 3298 3299 3300

  if(result == -1)
    result= HA_ERR_NO_CONNECTION;

  DBUG_RETURN(result);
3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315
}


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"));
3316
    reset();
3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385
    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"));
3386 3387
    DBUG_PRINT("info", ("Ignoring duplicate key"));
    m_ignore_dup_key= TRUE;
3388 3389 3390
    break;
  case HA_EXTRA_NO_IGNORE_DUP_KEY:
    DBUG_PRINT("info", ("HA_EXTRA_NO_IGNORE_DUP_KEY"));
3391
    m_ignore_dup_key= FALSE;
3392 3393
    break;
  case HA_EXTRA_RETRIEVE_ALL_COLS:    /* Retrieve all columns, not just those
3394 3395
                                         where field->query_id is the same as
                                         the current query id */
3396
    DBUG_PRINT("info", ("HA_EXTRA_RETRIEVE_ALL_COLS"));
3397
    m_retrieve_all_fields= TRUE;
3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409
    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"));
3410
    m_retrieve_primary_key= TRUE;
3411 3412 3413 3414 3415 3416
    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"));
3417 3418
  case HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
    DBUG_PRINT("info", ("HA_EXTRA_KEYREAD_PRESERVE_FIELDS"));
3419
    break;
3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432
  case HA_EXTRA_WRITE_CAN_REPLACE:
    DBUG_PRINT("info", ("HA_EXTRA_WRITE_CAN_REPLACE"));
    if (!m_has_unique_index)
    {
      DBUG_PRINT("info", ("Turning ON use of write instead of insert"));
      m_use_write= TRUE;
    }
    break;
  case HA_EXTRA_WRITE_CANNOT_REPLACE:
    DBUG_PRINT("info", ("HA_EXTRA_WRITE_CANNOT_REPLACE"));
    DBUG_PRINT("info", ("Turning OFF use of write instead of insert"));
    m_use_write= FALSE;
    break;
3433 3434 3435 3436 3437 3438 3439 3440
  case HA_EXTRA_DELETE_CANNOT_BATCH:
    DBUG_PRINT("info", ("HA_EXTRA_DELETE_CANNOT_BATCH"));
    m_delete_cannot_batch= TRUE;
    break;
  case HA_EXTRA_UPDATE_CANNOT_BATCH:
    DBUG_PRINT("info", ("HA_EXTRA_UPDATE_CANNOT_BATCH"));
    m_update_cannot_batch= TRUE;
    break;
3441 3442
  default:
    break;
3443 3444 3445 3446 3447
  }
  
  DBUG_RETURN(0);
}

3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458

int ha_ndbcluster::reset()
{
  DBUG_ENTER("ha_ndbcluster::reset");
  cond_clear();

  /* reset flags set by extra calls */
  m_retrieve_all_fields= FALSE;
  m_retrieve_primary_key= FALSE;
  m_ignore_dup_key= FALSE;
  m_use_write= FALSE;
3459 3460
  m_delete_cannot_batch= FALSE;
  m_update_cannot_batch= FALSE;
3461 3462 3463 3464
  DBUG_RETURN(0);
}


3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477
/* 
   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;
joreland@mysql.com's avatar
joreland@mysql.com committed
3478
  const NDBTAB *tab= (const NDBTAB *) m_table;    
3479 3480

  DBUG_ENTER("start_bulk_insert");
pekka@mysql.com's avatar
pekka@mysql.com committed
3481
  DBUG_PRINT("enter", ("rows: %d", (int)rows));
3482
  
3483
  m_rows_inserted= (ha_rows) 0;
3484
  if (!m_use_write && m_ignore_dup_key)
3485 3486 3487
  {
    /*
      compare if expression with that in write_row
3488
      we have a situation where peek_indexed_rows() will be called
3489 3490 3491 3492 3493 3494 3495 3496
      so we cannot batch
    */
    DBUG_PRINT("info", ("Batching turned off as duplicate key is "
                        "ignored by using peek_row"));
    m_rows_to_insert= 1;
    m_bulk_insert_rows= 1;
    DBUG_VOID_RETURN;
  }
3497
  if (rows == (ha_rows) 0)
3498
  {
3499 3500
    /* We don't know how many will be inserted, guess */
    m_rows_to_insert= m_autoincrement_prefetch;
3501
  }
3502 3503
  else
    m_rows_to_insert= rows; 
3504 3505 3506 3507 3508 3509 3510 3511

  /* 
    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.   
  */
3512
  const int bytesperbatch= 8192;
3513
  bytes= 12 + tab->getRowSizeInBytes() + 4 * tab->getNoOfColumns();
3514
  batch= bytesperbatch/bytes;
3515 3516
  batch= batch == 0 ? 1 : batch;
  DBUG_PRINT("info", ("batch: %d, bytes: %d", batch, bytes));
3517
  m_bulk_insert_rows= batch;
3518 3519 3520 3521 3522 3523 3524 3525 3526

  DBUG_VOID_RETURN;
}

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

3529
  DBUG_ENTER("end_bulk_insert");
3530
  // Check if last inserts need to be flushed
3531
  if (m_bulk_insert_not_flushed)
3532
  {
3533
    NdbTransaction *trans= m_active_trans;
3534 3535 3536
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
                        "rows_inserted:%d, bulk_insert_rows: %d", 
3537
                        (int) m_rows_inserted, (int) m_bulk_insert_rows)); 
3538
    m_bulk_insert_not_flushed= FALSE;
3539 3540
    if (m_transaction_on)
    {
3541
      if (execute_no_commit(this, trans,false) != 0)
3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553
      {
        no_uncommitted_rows_execute_failure();
        my_errno= error= ndb_err(trans);
      }
    }
    else
    {
      if (execute_commit(this, trans) != 0)
      {
        no_uncommitted_rows_execute_failure();
        my_errno= error= ndb_err(trans);
      }
3554 3555
      else
      {
3556
        IF_DBUG(int res=) trans->restart();
3557 3558
        DBUG_ASSERT(res == 0);
      }
3559
    }
3560 3561
  }

3562 3563
  m_rows_inserted= (ha_rows) 0;
  m_rows_to_insert= (ha_rows) 1;
3564
  DBUG_RETURN(error);
3565 3566
}

3567 3568 3569 3570

int ha_ndbcluster::extra_opt(enum ha_extra_function operation, ulong cache_size)
{
  DBUG_ENTER("extra_opt");
pekka@mysql.com's avatar
pekka@mysql.com committed
3571
  DBUG_PRINT("enter", ("cache_size: %lu", cache_size));
3572 3573 3574
  DBUG_RETURN(extra(operation));
}

3575 3576 3577 3578
static const char *ha_ndbcluster_exts[] = {
 ha_ndb_ext,
 NullS
};
3579

3580
const char** ha_ndbcluster::bas_ext() const
3581 3582 3583
{
  return ha_ndbcluster_exts;
}
3584 3585 3586 3587 3588 3589 3590 3591 3592

/*
  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()
{
3593 3594 3595
  DBUG_ENTER("ha_ndbcluster::scan_time()");
  double res= rows2double(records*1000);
  DBUG_PRINT("exit", ("table: %s value: %f", 
3596
                      m_tabname, res));
3597
  DBUG_RETURN(res);
3598 3599
}

3600 3601 3602 3603 3604 3605 3606
/*
  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
*/
3607 3608 3609 3610 3611 3612 3613 3614

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) 
  {
3615

3616 3617 3618
    /* If we are not doing a LOCK TABLE, then allow multiple
       writers */
    
3619 3620 3621
    /* Since NDB does not currently have table locks
       this is treated as a ordinary lock */

3622
    if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637
         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;
3638 3639

  DBUG_PRINT("exit", ("lock_type: %d", lock_type));
3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661
  
  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
3662
  for the statement, this will be stored in thd_ndb.stmt.
3663
  If not, we have to start a master transaction if there doesn't exist
3664
  one from before, this will be stored in thd_ndb.all
3665 3666 3667
 
  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  
3668
  If we are locking the table then:
3669
  - save the NdbDictionary::Table for easy access
3670 3671
  - save reference to table statistics
  - refresh list of the indexes for the table if needed (if altered)
3672 3673 3674 3675 3676
 */

int ha_ndbcluster::external_lock(THD *thd, int lock_type)
{
  int error=0;
3677
  NdbTransaction* trans= NULL;
3678 3679 3680 3681 3682 3683

  DBUG_ENTER("external_lock");
  /*
    Check that this handler instance has a connection
    set up to the Ndb object of thd
   */
3684
  if (check_ndb_connection(thd))
3685
    DBUG_RETURN(1);
3686

3687
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
3688
  Ndb *ndb= thd_ndb->ndb;
3689

3690 3691
  DBUG_PRINT("enter", ("thd: 0x%lx  thd_ndb: 0x%lx  thd_ndb->lock_count: %d",
                       (long) thd, (long) thd_ndb, thd_ndb->lock_count));
3692

3693 3694
  if (lock_type != F_UNLCK)
  {
3695
    DBUG_PRINT("info", ("lock_type != F_UNLCK"));
3696 3697 3698 3699 3700 3701 3702 3703
    if (thd->lex->sql_command == SQLCOM_LOAD)
    {
      m_transaction_on= FALSE;
      /* Would be simpler if has_transactions() didn't always say "yes" */
      thd->options|= OPTION_STATUS_NO_TRANS_UPDATE;
      thd->no_trans_update= TRUE;
    }
    else if (!thd->transaction.on)
3704 3705 3706
      m_transaction_on= FALSE;
    else
      m_transaction_on= thd->variables.ndb_use_transactions;
3707
    if (!thd_ndb->lock_count++)
3708 3709
    {
      PRINT_OPTION_FLAGS(thd);
3710
      if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) 
3711 3712
      {
        // Autocommit transaction
3713
        DBUG_ASSERT(!thd_ndb->stmt);
3714 3715
        DBUG_PRINT("trans",("Starting transaction stmt"));      

3716
        trans= ndb->startTransaction();
3717
        if (trans == NULL)
3718
          ERR_RETURN(ndb->getNdbError());
3719
        no_uncommitted_rows_reset(thd);
3720
        thd_ndb->stmt= trans;
3721
	thd_ndb->query_state&= NDB_QUERY_NORMAL;
3722
        trans_register_ha(thd, FALSE, &ndbcluster_hton);
3723 3724 3725
      } 
      else 
      { 
3726
        if (!thd_ndb->all)
3727
        {
3728 3729 3730 3731
          // Not autocommit transaction
          // A "master" transaction ha not been started yet
          DBUG_PRINT("trans",("starting transaction, all"));
          
3732
          trans= ndb->startTransaction();
3733
          if (trans == NULL)
3734
            ERR_RETURN(ndb->getNdbError());
3735
          no_uncommitted_rows_reset(thd);
3736
          thd_ndb->all= trans; 
3737
	  thd_ndb->query_state&= NDB_QUERY_NORMAL;
3738
          trans_register_ha(thd, TRUE, &ndbcluster_hton);
3739 3740 3741 3742 3743 3744 3745 3746

          /*
            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))
3747
          {
3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766
            //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. 
     */

3767 3768 3769
    // 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;
3770 3771
    m_autoincrement_prefetch= 
      (ha_rows) thd->variables.ndb_autoincrement_prefetch_sz;
3772

3773
    m_active_trans= thd_ndb->all ? thd_ndb->all : thd_ndb->stmt;
3774
    DBUG_ASSERT(m_active_trans);
3775
    // Start of transaction
3776
    m_rows_changed= 0;
3777
    m_retrieve_all_fields= FALSE;
3778
    m_retrieve_primary_key= FALSE;
3779
    m_ops_pending= 0;
3780
    {
3781
      NDBDICT *dict= ndb->getDictionary();
3782 3783 3784
      const NDBTAB *tab;
      void *tab_info;
      if (!(tab= dict->getTable(m_tabname, &tab_info)))
3785
        ERR_RETURN(dict->getNdbError());
3786 3787 3788
      DBUG_PRINT("info", ("Table schema version: %d", 
                          tab->getObjectVersion()));
      // Check if thread has stale local cache
3789 3790 3791 3792
      // New transaction must not use old tables... (trans != 0)
      // Running might...
      if ((trans && tab->getObjectStatus() != NdbDictionary::Object::Retrieved)
	  || tab->getObjectStatus() == NdbDictionary::Object::Invalid)
3793 3794
      {
        invalidate_dictionary_cache(FALSE);
3795
        if (!(tab= dict->getTable(m_tabname, &tab_info)))
3796 3797 3798 3799
          ERR_RETURN(dict->getNdbError());
        DBUG_PRINT("info", ("Table schema version: %d", 
                            tab->getObjectVersion()));
      }
3800
      if (m_table_version < tab->getObjectVersion())
3801 3802 3803 3804 3805 3806 3807
      {
        /*
          The table has been altered, caller has to retry
        */
        NdbError err= ndb->getNdbError(NDB_INVALID_SCHEMA_OBJECT);
        DBUG_RETURN(ndb_to_mysql_error(&err));
      }
3808 3809 3810 3811
      if (m_table != (void *)tab)
      {
        m_table= (void *)tab;
        m_table_version = tab->getObjectVersion();
3812
        if ((my_errno= build_index_list(ndb, table, ILBP_OPEN)))
3813
          DBUG_RETURN(my_errno);
3814

3815
        const void *data= NULL, *pack_data= NULL;
3816
        uint length, pack_length;
3817
        if (readfrm(table->s->path, &data, &length) ||
3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828
            packfrm(data, length, &pack_data, &pack_length) ||
            pack_length != tab->getFrmLength() ||
            memcmp(pack_data, tab->getFrmData(), pack_length))
        {
          my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
          my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR));
          NdbError err= ndb->getNdbError(NDB_INVALID_SCHEMA_OBJECT);
          DBUG_RETURN(ndb_to_mysql_error(&err));
        }
        my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
        my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR));
3829
      }
3830 3831
      m_table_info= tab_info;
    }
3832
    no_uncommitted_rows_init(thd);
3833 3834
  }
  else
3835
  {
3836
    DBUG_PRINT("info", ("lock_type == F_UNLCK"));
3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854

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

3855
    if (!--thd_ndb->lock_count)
3856 3857 3858 3859
    {
      DBUG_PRINT("trans", ("Last external_lock"));
      PRINT_OPTION_FLAGS(thd);

3860
      if (thd_ndb->stmt)
3861 3862 3863 3864 3865 3866 3867
      {
        /*
          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"));
3868
        ndb->closeTransaction(m_active_trans);
3869
        thd_ndb->stmt= NULL;
3870 3871
      }
    }
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
3872
    m_table_info= NULL;
3873

3874 3875 3876 3877 3878 3879 3880 3881 3882
    /*
      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;    

3883 3884
    if (m_active_cursor)
      DBUG_PRINT("warning", ("m_active_cursor != NULL"));
3885 3886
    m_active_cursor= NULL;

3887 3888 3889 3890
    if (m_multi_cursor)
      DBUG_PRINT("warning", ("m_multi_cursor != NULL"));
    m_multi_cursor= NULL;
    
3891
    if (m_blobs_pending)
3892
      DBUG_PRINT("warning", ("blobs_pending != 0"));
3893
    m_blobs_pending= 0;
3894
    
3895
    if (m_ops_pending)
3896
      DBUG_PRINT("warning", ("ops_pending != 0L"));
3897
    m_ops_pending= 0;
3898 3899 3900 3901
  }
  DBUG_RETURN(error);
}

3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917
/*
  Unlock the last row read in an open scan.
  Rows are unlocked by default in ndb, but
  for SELECT FOR UPDATE and SELECT LOCK WIT SHARE MODE
  locks are kept if unlock_row() is not called.
*/

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

  DBUG_PRINT("info", ("Unlocking row"));
  m_lock_tuple= false;
  DBUG_VOID_RETURN;
}

3918
/*
3919 3920 3921 3922 3923
  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
3924 3925
*/

serg@serg.mylan's avatar
serg@serg.mylan committed
3926
int ha_ndbcluster::start_stmt(THD *thd, thr_lock_type lock_type)
3927 3928 3929 3930 3931
{
  int error=0;
  DBUG_ENTER("start_stmt");
  PRINT_OPTION_FLAGS(thd);

3932
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
3933
  NdbTransaction *trans= (thd_ndb->stmt)?thd_ndb->stmt:thd_ndb->all;
3934
  if (!trans){
3935
    Ndb *ndb= thd_ndb->ndb;
3936
    DBUG_PRINT("trans",("Starting transaction stmt"));  
3937
    trans= ndb->startTransaction();
3938
    if (trans == NULL)
3939
      ERR_RETURN(ndb->getNdbError());
3940
    no_uncommitted_rows_reset(thd);
3941
    thd_ndb->stmt= trans;
3942
    thd_ndb->query_state&= NDB_QUERY_NORMAL;
3943
    trans_register_ha(thd, FALSE, &ndbcluster_hton);
3944 3945
  }
  m_active_trans= trans;
3946
  // Start of statement
3947
  m_retrieve_all_fields= FALSE;
3948
  m_retrieve_primary_key= FALSE;
3949
  m_ops_pending= 0;    
3950 3951 3952 3953 3954 3955
  
  DBUG_RETURN(error);
}


/*
3956
  Commit a transaction started in NDB
3957 3958
 */

3959
int ndbcluster_commit(THD *thd, bool all)
3960 3961
{
  int res= 0;
3962 3963 3964
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  Ndb *ndb= thd_ndb->ndb;
  NdbTransaction *trans= all ? thd_ndb->all : thd_ndb->stmt;
3965 3966 3967

  DBUG_ENTER("ndbcluster_commit");
  DBUG_PRINT("transaction",("%s",
3968
                            trans == thd_ndb->stmt ?
3969 3970 3971
                            "stmt" : "all"));
  DBUG_ASSERT(ndb && trans);

3972
  if (execute_commit(thd,trans) != 0)
3973 3974
  {
    const NdbError err= trans->getNdbError();
3975
    const NdbOperation *error_op= trans->getNdbErrorOperation();
3976
    ERR_PRINT(err);
3977
    res= ndb_to_mysql_error(&err);
3978
    if (res != -1)
3979
      ndbcluster_print_error(res, error_op);
3980
  }
3981
  ndb->closeTransaction(trans);
3982

3983
  if (all)
3984 3985 3986
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;
3987 3988 3989 3990 3991 3992 3993

  /* 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);
3994 3995
    DBUG_PRINT("info", ("Invalidate commit_count for %s, share->commit_count: %lu",
                        share->table_name, (ulong) share->commit_count));
3996 3997 3998 3999 4000 4001
    share->commit_count= 0;
    share->commit_count_lock++;
    pthread_mutex_unlock(&share->mutex);
  }
  thd_ndb->changed_tables.empty();

4002 4003 4004 4005 4006 4007 4008 4009
  DBUG_RETURN(res);
}


/*
  Rollback a transaction started in NDB
 */

4010
int ndbcluster_rollback(THD *thd, bool all)
4011 4012
{
  int res= 0;
4013 4014 4015
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  Ndb *ndb= thd_ndb->ndb;
  NdbTransaction *trans= all ? thd_ndb->all : thd_ndb->stmt;
4016 4017 4018

  DBUG_ENTER("ndbcluster_rollback");
  DBUG_PRINT("transaction",("%s",
4019
                            trans == thd_ndb->stmt ? 
4020 4021 4022
                            "stmt" : "all"));
  DBUG_ASSERT(ndb && trans);

4023
  if (trans->execute(NdbTransaction::Rollback) != 0)
4024 4025
  {
    const NdbError err= trans->getNdbError();
4026
    const NdbOperation *error_op= trans->getNdbErrorOperation();
4027 4028
    ERR_PRINT(err);     
    res= ndb_to_mysql_error(&err);
4029 4030
    if (res != -1) 
      ndbcluster_print_error(res, error_op);
4031 4032
  }
  ndb->closeTransaction(trans);
4033

4034
  if (all)
4035 4036 4037 4038
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;

4039 4040 4041
  /* Clear list of tables changed by transaction */
  thd_ndb->changed_tables.empty();

4042
  DBUG_RETURN(res);
4043 4044 4045 4046
}


/*
pekka@mysql.com's avatar
pekka@mysql.com committed
4047 4048 4049
  Define NDB column based on Field.
  Returns 0 or mysql error code.
  Not member of ha_ndbcluster because NDBCOL cannot be declared.
pekka@mysql.com's avatar
pekka@mysql.com committed
4050 4051 4052

  MySQL text types with character set "binary" are mapped to true
  NDB binary types without a character set.  This may change.
4053 4054
 */

pekka@mysql.com's avatar
pekka@mysql.com committed
4055 4056 4057
static int create_ndb_column(NDBCOL &col,
                             Field *field,
                             HA_CREATE_INFO *info)
4058
{
pekka@mysql.com's avatar
pekka@mysql.com committed
4059
  // Set name
4060 4061 4062 4063
  if (col.setName(field->field_name))
  {
    return (my_errno= errno);
  }
pekka@mysql.com's avatar
pekka@mysql.com committed
4064 4065
  // Get char set
  CHARSET_INFO *cs= field->charset();
pekka@mysql.com's avatar
pekka@mysql.com committed
4066 4067 4068 4069
  // Set type and sizes
  const enum enum_field_types mysql_type= field->real_type();
  switch (mysql_type) {
  // Numeric types
4070
  case MYSQL_TYPE_TINY:        
pekka@mysql.com's avatar
pekka@mysql.com committed
4071 4072 4073 4074 4075 4076
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Tinyunsigned);
    else
      col.setType(NDBCOL::Tinyint);
    col.setLength(1);
    break;
4077
  case MYSQL_TYPE_SHORT:
pekka@mysql.com's avatar
pekka@mysql.com committed
4078 4079 4080 4081 4082 4083
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Smallunsigned);
    else
      col.setType(NDBCOL::Smallint);
    col.setLength(1);
    break;
4084
  case MYSQL_TYPE_LONG:
pekka@mysql.com's avatar
pekka@mysql.com committed
4085 4086 4087 4088 4089 4090
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Unsigned);
    else
      col.setType(NDBCOL::Int);
    col.setLength(1);
    break;
4091
  case MYSQL_TYPE_INT24:       
pekka@mysql.com's avatar
pekka@mysql.com committed
4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103
    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);
4104 4105
    break;
  case MYSQL_TYPE_FLOAT:
pekka@mysql.com's avatar
pekka@mysql.com committed
4106 4107 4108
    col.setType(NDBCOL::Float);
    col.setLength(1);
    break;
4109
  case MYSQL_TYPE_DOUBLE:
pekka@mysql.com's avatar
pekka@mysql.com committed
4110 4111 4112
    col.setType(NDBCOL::Double);
    col.setLength(1);
    break;
4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132
  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;
4133 4134 4135
  case MYSQL_TYPE_NEWDECIMAL:    
    {
      Field_new_decimal *f= (Field_new_decimal*)field;
4136
      uint precision= f->precision;
4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150
      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;
pekka@mysql.com's avatar
pekka@mysql.com committed
4151 4152 4153 4154 4155
  // Date types
  case MYSQL_TYPE_DATETIME:    
    col.setType(NDBCOL::Datetime);
    col.setLength(1);
    break;
4156 4157 4158 4159
  case MYSQL_TYPE_DATE: // ?
    col.setType(NDBCOL::Char);
    col.setLength(field->pack_length());
    break;
pekka@mysql.com's avatar
pekka@mysql.com committed
4160
  case MYSQL_TYPE_NEWDATE:
4161 4162 4163
    col.setType(NDBCOL::Date);
    col.setLength(1);
    break;
pekka@mysql.com's avatar
pekka@mysql.com committed
4164
  case MYSQL_TYPE_TIME:        
4165 4166 4167
    col.setType(NDBCOL::Time);
    col.setLength(1);
    break;
4168 4169 4170 4171 4172 4173 4174
  case MYSQL_TYPE_YEAR:
    col.setType(NDBCOL::Year);
    col.setLength(1);
    break;
  case MYSQL_TYPE_TIMESTAMP:
    col.setType(NDBCOL::Timestamp);
    col.setLength(1);
pekka@mysql.com's avatar
pekka@mysql.com committed
4175 4176 4177
    break;
  // Char types
  case MYSQL_TYPE_STRING:      
4178
    if (field->pack_length() == 0)
4179 4180 4181 4182
    {
      col.setType(NDBCOL::Bit);
      col.setLength(1);
    }
pekka@mysql.com's avatar
pekka@mysql.com committed
4183
    else if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
4184
    {
pekka@mysql.com's avatar
pekka@mysql.com committed
4185
      col.setType(NDBCOL::Binary);
4186
      col.setLength(field->pack_length());
pekka@mysql.com's avatar
pekka@mysql.com committed
4187
    }
4188
    else
4189 4190 4191
    {
      col.setType(NDBCOL::Char);
      col.setCharset(cs);
4192
      col.setLength(field->pack_length());
4193
    }
pekka@mysql.com's avatar
pekka@mysql.com committed
4194
    break;
pekka@mysql.com's avatar
pekka@mysql.com committed
4195 4196 4197 4198 4199 4200
  case MYSQL_TYPE_VAR_STRING: // ?
  case MYSQL_TYPE_VARCHAR:
    {
      Field_varstring* f= (Field_varstring*)field;
      if (f->length_bytes == 1)
      {
pekka@mysql.com's avatar
pekka@mysql.com committed
4201
        if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
pekka@mysql.com's avatar
pekka@mysql.com committed
4202 4203 4204 4205 4206 4207 4208 4209
          col.setType(NDBCOL::Varbinary);
        else {
          col.setType(NDBCOL::Varchar);
          col.setCharset(cs);
        }
      }
      else if (f->length_bytes == 2)
      {
pekka@mysql.com's avatar
pekka@mysql.com committed
4210
        if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
pekka@mysql.com's avatar
pekka@mysql.com committed
4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221
          col.setType(NDBCOL::Longvarbinary);
        else {
          col.setType(NDBCOL::Longvarchar);
          col.setCharset(cs);
        }
      }
      else
      {
        return HA_ERR_UNSUPPORTED;
      }
      col.setLength(field->field_length);
pekka@mysql.com's avatar
pekka@mysql.com committed
4222
    }
pekka@mysql.com's avatar
pekka@mysql.com committed
4223 4224 4225 4226
    break;
  // Blob types (all come in as MYSQL_TYPE_BLOB)
  mysql_type_tiny_blob:
  case MYSQL_TYPE_TINY_BLOB:
pekka@mysql.com's avatar
pekka@mysql.com committed
4227
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
pekka@mysql.com's avatar
pekka@mysql.com committed
4228
      col.setType(NDBCOL::Blob);
pekka@mysql.com's avatar
pekka@mysql.com committed
4229
    else {
pekka@mysql.com's avatar
pekka@mysql.com committed
4230
      col.setType(NDBCOL::Text);
pekka@mysql.com's avatar
pekka@mysql.com committed
4231 4232
      col.setCharset(cs);
    }
pekka@mysql.com's avatar
pekka@mysql.com committed
4233 4234 4235 4236 4237
    col.setInlineSize(256);
    // No parts
    col.setPartSize(0);
    col.setStripeSize(0);
    break;
4238
  //mysql_type_blob:
4239
  case MYSQL_TYPE_GEOMETRY:
pekka@mysql.com's avatar
pekka@mysql.com committed
4240
  case MYSQL_TYPE_BLOB:    
pekka@mysql.com's avatar
pekka@mysql.com committed
4241
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
pekka@mysql.com's avatar
pekka@mysql.com committed
4242
      col.setType(NDBCOL::Blob);
pekka@mysql.com's avatar
pekka@mysql.com committed
4243
    else {
pekka@mysql.com's avatar
pekka@mysql.com committed
4244
      col.setType(NDBCOL::Text);
pekka@mysql.com's avatar
pekka@mysql.com committed
4245 4246
      col.setCharset(cs);
    }
pekka@mysql.com's avatar
pekka@mysql.com committed
4247
    {
4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268
      Field_blob *field_blob= (Field_blob *)field;
      /*
       * max_data_length is 2^8-1, 2^16-1, 2^24-1 for tiny, blob, medium.
       * Tinyblob gets no blob parts.  The other cases are just a crude
       * way to control part size and striping.
       *
       * In mysql blob(256) is promoted to blob(65535) so it does not
       * in fact fit "inline" in NDB.
       */
      if (field_blob->max_data_length() < (1 << 8))
        goto mysql_type_tiny_blob;
      else if (field_blob->max_data_length() < (1 << 16))
      {
        col.setInlineSize(256);
        col.setPartSize(2000);
        col.setStripeSize(16);
      }
      else if (field_blob->max_data_length() < (1 << 24))
        goto mysql_type_medium_blob;
      else
        goto mysql_type_long_blob;
pekka@mysql.com's avatar
pekka@mysql.com committed
4269 4270 4271 4272
    }
    break;
  mysql_type_medium_blob:
  case MYSQL_TYPE_MEDIUM_BLOB:   
pekka@mysql.com's avatar
pekka@mysql.com committed
4273
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
pekka@mysql.com's avatar
pekka@mysql.com committed
4274
      col.setType(NDBCOL::Blob);
pekka@mysql.com's avatar
pekka@mysql.com committed
4275
    else {
pekka@mysql.com's avatar
pekka@mysql.com committed
4276
      col.setType(NDBCOL::Text);
pekka@mysql.com's avatar
pekka@mysql.com committed
4277 4278
      col.setCharset(cs);
    }
pekka@mysql.com's avatar
pekka@mysql.com committed
4279 4280 4281 4282 4283 4284
    col.setInlineSize(256);
    col.setPartSize(4000);
    col.setStripeSize(8);
    break;
  mysql_type_long_blob:
  case MYSQL_TYPE_LONG_BLOB:  
pekka@mysql.com's avatar
pekka@mysql.com committed
4285
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
pekka@mysql.com's avatar
pekka@mysql.com committed
4286
      col.setType(NDBCOL::Blob);
pekka@mysql.com's avatar
pekka@mysql.com committed
4287
    else {
pekka@mysql.com's avatar
pekka@mysql.com committed
4288
      col.setType(NDBCOL::Text);
pekka@mysql.com's avatar
pekka@mysql.com committed
4289 4290
      col.setCharset(cs);
    }
pekka@mysql.com's avatar
pekka@mysql.com committed
4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303
    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;
4304 4305
  case MYSQL_TYPE_BIT:
  {
4306
    int no_of_bits= field->field_length;
4307 4308 4309 4310 4311 4312 4313
    col.setType(NDBCOL::Bit);
    if (!no_of_bits)
      col.setLength(1);
      else
        col.setLength(no_of_bits);
    break;
  }
pekka@mysql.com's avatar
pekka@mysql.com committed
4314 4315 4316 4317 4318
  case MYSQL_TYPE_NULL:        
    goto mysql_type_unsupported;
  mysql_type_unsupported:
  default:
    return HA_ERR_UNSUPPORTED;
4319
  }
pekka@mysql.com's avatar
pekka@mysql.com committed
4320 4321 4322 4323 4324 4325
  // Set nullable and pk
  col.setNullable(field->maybe_null());
  col.setPrimaryKey(field->flags & PRI_KEY_FLAG);
  // Set autoincrement
  if (field->flags & AUTO_INCREMENT_FLAG) 
  {
4326
#ifndef DBUG_OFF
4327
    char buff[22];
4328
#endif
pekka@mysql.com's avatar
pekka@mysql.com committed
4329 4330
    col.setAutoIncrement(TRUE);
    ulonglong value= info->auto_increment_value ?
4331
      info->auto_increment_value : (ulonglong) 1;
4332
    DBUG_PRINT("info", ("Autoincrement key, initial: %s", llstr(value, buff)));
pekka@mysql.com's avatar
pekka@mysql.com committed
4333
    col.setAutoIncrementInitialValue(value);
4334
  }
pekka@mysql.com's avatar
pekka@mysql.com committed
4335
  else
4336
    col.setAutoIncrement(FALSE);
pekka@mysql.com's avatar
pekka@mysql.com committed
4337
  return 0;
4338 4339 4340 4341 4342 4343
}

/*
  Create a table in NDB Cluster
 */

mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4344 4345
static void ndb_set_fragmentation(NDBTAB &tab, TABLE *form, uint pk_length)
{
4346 4347 4348 4349 4350
  ha_rows max_rows= form->s->max_rows;
  ha_rows min_rows= form->s->min_rows;
  if (max_rows < min_rows)
    max_rows= min_rows;
  if (max_rows == (ha_rows)0) /* default setting, don't set fragmentation */
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367
    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;
4368 4369 4370 4371 4372
    /*
     * if not --with-big-tables then max_rows is ulong
     * the warning in this case is misleading though
     */
    ulonglong big_max_rows = (ulonglong)max_rows;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4373
#if MYSQL_VERSION_ID >= 50100
4374
    no_fragments= (big_max_rows*acc_row_size)/acc_fragment_size+1;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4375
#else
4376
    no_fragments= ((big_max_rows*acc_row_size)/acc_fragment_size+1
4377
                   +1/*correct rounding*/)/2;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4378 4379 4380 4381 4382 4383 4384 4385 4386
#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)
4387 4388
        push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
                     "Ndb might have problems storing the max amount of rows specified");
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4389 4390 4391 4392 4393 4394 4395
    }
    else if (no_fragments > no_nodes)
      ftype= NDBTAB::FragAllMedium;
    else
      ftype= NDBTAB::FragAllSmall;
    tab.setFragmentType(ftype);
  }
4396 4397
  tab.setMaxRows(max_rows);
  tab.setMinRows(min_rows);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4398 4399
}

4400
int ha_ndbcluster::create(const char *name, 
4401
                          TABLE *form, 
4402
                          HA_CREATE_INFO *create_info)
4403 4404 4405
{
  NDBTAB tab;
  NDBCOL col;
joreland@mysql.com's avatar
joreland@mysql.com committed
4406
  uint pack_length, length, i, pk_length= 0;
4407
  const void *data= NULL, *pack_data= NULL;
4408
  char name2[FN_HEADLEN];
4409
  bool create_from_engine= (create_info->table_options & HA_OPTION_CREATE_FROM_ENGINE);
4410

pekka@mysql.com's avatar
pekka@mysql.com committed
4411
  DBUG_ENTER("ha_ndbcluster::create");
4412 4413 4414
  DBUG_PRINT("enter", ("name: %s", name));
  fn_format(name2, name, "", "",2);       // Remove the .frm extension
  set_dbname(name2);
4415 4416
  set_tabname(name2);    

4417 4418 4419 4420 4421 4422
  if (current_thd->lex->sql_command == SQLCOM_TRUNCATE)
  {
    DBUG_PRINT("info", ("Dropping and re-creating table for TRUNCATE"));
    if ((my_errno= delete_table(name)))
      DBUG_RETURN(my_errno);
  }
4423 4424 4425 4426 4427 4428 4429 4430 4431 4432
  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);
  }
4433 4434

  DBUG_PRINT("table", ("name: %s", m_tabname));  
4435 4436 4437 4438
  if (tab.setName(m_tabname))
  {
    DBUG_RETURN(my_errno= errno);
  }
4439
  tab.setLogging(!(create_info->options & HA_LEX_CREATE_TMP_TABLE));    
4440 4441 4442 4443 4444
   
  // Save frm data for this table
  if (readfrm(name, &data, &length))
    DBUG_RETURN(1);
  if (packfrm(data, length, &pack_data, &pack_length))
4445 4446
  {
    my_free((char*)data, MYF(0));
4447
    DBUG_RETURN(2);
4448 4449
  }

4450
  DBUG_PRINT("info", ("setFrm data: 0x%lx  len: %d", (long) pack_data, pack_length));
4451 4452 4453 4454
  tab.setFrm(pack_data, pack_length);      
  my_free((char*)data, MYF(0));
  my_free((char*)pack_data, MYF(0));
  
4455
  for (i= 0; i < form->s->fields; i++) 
4456 4457 4458 4459
  {
    Field *field= form->field[i];
    DBUG_PRINT("info", ("name: %s, type: %u, pack_length: %d", 
                        field->field_name, field->real_type(),
4460
                        field->pack_length()));
4461
    if ((my_errno= create_ndb_column(col, field, create_info)))
pekka@mysql.com's avatar
pekka@mysql.com committed
4462
      DBUG_RETURN(my_errno);
4463 4464 4465 4466
    if (tab.addColumn(col))
    {
      DBUG_RETURN(my_errno= errno);
    }
4467
    if (col.getPrimaryKey())
joreland@mysql.com's avatar
joreland@mysql.com committed
4468
      pk_length += (field->pack_length() + 3) / 4;
4469 4470 4471
  }
  
  // No primary key, create shadow key as 64 bit, auto increment  
4472
  if (form->s->primary_key == MAX_KEY) 
4473 4474
  {
    DBUG_PRINT("info", ("Generating shadow key"));
4475 4476 4477 4478
    if (col.setName("$PK"))
    {
      DBUG_RETURN(my_errno= errno);
    }
4479 4480
    col.setType(NdbDictionary::Column::Bigunsigned);
    col.setLength(1);
4481
    col.setNullable(FALSE);
4482 4483
    col.setPrimaryKey(TRUE);
    col.setAutoIncrement(TRUE);
4484 4485 4486 4487
    if (tab.addColumn(col))
    {
      DBUG_RETURN(my_errno= errno);
    }
joreland@mysql.com's avatar
joreland@mysql.com committed
4488 4489 4490 4491
    pk_length += 2;
  }
  
  // Make sure that blob tables don't have to big part size
4492
  for (i= 0; i < form->s->fields; i++) 
joreland@mysql.com's avatar
joreland@mysql.com committed
4493 4494 4495 4496 4497 4498 4499
  {
    /**
     * 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()) {
4500
    case MYSQL_TYPE_GEOMETRY:
joreland@mysql.com's avatar
joreland@mysql.com committed
4501 4502 4503 4504
    case MYSQL_TYPE_BLOB:    
    case MYSQL_TYPE_MEDIUM_BLOB:   
    case MYSQL_TYPE_LONG_BLOB: 
    {
4505 4506
      NdbDictionary::Column * column= tab.getColumn(i);
      int size= pk_length + (column->getPartSize()+3)/4 + 7;
4507
      if (size > NDB_MAX_TUPLE_SIZE_IN_WORDS && 
4508
         (pk_length+7) < NDB_MAX_TUPLE_SIZE_IN_WORDS)
joreland@mysql.com's avatar
joreland@mysql.com committed
4509
      {
4510
        size= NDB_MAX_TUPLE_SIZE_IN_WORDS - pk_length - 7;
4511
        column->setPartSize(4*size);
joreland@mysql.com's avatar
joreland@mysql.com committed
4512 4513 4514 4515 4516 4517 4518 4519 4520 4521
      }
      /**
       * 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;
    }
4522
  }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4523 4524 4525

  ndb_set_fragmentation(tab, form, pk_length);

4526
  if ((my_errno= check_ndb_connection()))
4527 4528 4529
    DBUG_RETURN(my_errno);
  
  // Create the table in NDB     
4530 4531
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
4532
  if (dict->createTable(tab) != 0) 
4533 4534 4535 4536 4537 4538 4539 4540
  {
    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));
4541

4542
  // Create secondary indexes
4543
  my_errno= build_index_list(ndb, form, ILBP_CREATE);
4544

4545 4546 4547
  if (!my_errno)
    my_errno= write_ndb_file();

4548 4549 4550 4551
  DBUG_RETURN(my_errno);
}


4552
int ha_ndbcluster::create_ordered_index(const char *name, 
4553
                                        KEY *key_info)
4554
{
4555
  DBUG_ENTER("ha_ndbcluster::create_ordered_index");
4556
  DBUG_RETURN(create_index(name, key_info, FALSE));
4557 4558 4559
}

int ha_ndbcluster::create_unique_index(const char *name, 
4560
                                       KEY *key_info)
4561 4562
{

4563
  DBUG_ENTER("ha_ndbcluster::create_unique_index");
4564
  DBUG_RETURN(create_index(name, key_info, TRUE));
4565 4566 4567
}


4568 4569 4570 4571 4572
/*
  Create an index in NDB Cluster
 */

int ha_ndbcluster::create_index(const char *name, 
4573 4574
                                KEY *key_info,
                                bool unique)
4575
{
4576 4577
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
4578 4579 4580
  KEY_PART_INFO *key_part= key_info->key_part;
  KEY_PART_INFO *end= key_part + key_info->key_parts;
  
4581
  DBUG_ENTER("ha_ndbcluster::create_index");
4582
  DBUG_PRINT("enter", ("name: %s ", name));
4583

4584
  NdbDictionary::Index ndb_index(name);
4585
  if (unique)
4586 4587 4588 4589 4590
    ndb_index.setType(NdbDictionary::Index::UniqueHashIndex);
  else 
  {
    ndb_index.setType(NdbDictionary::Index::OrderedIndex);
    // TODO Only temporary ordered indexes supported
4591
    ndb_index.setLogging(FALSE); 
4592
  }
4593 4594 4595 4596
  if (ndb_index.setTable(m_tabname))
  {
    DBUG_RETURN(my_errno= errno);
  }
4597 4598 4599 4600 4601

  for (; key_part != end; key_part++) 
  {
    Field *field= key_part->field;
    DBUG_PRINT("info", ("attr: %s", field->field_name));
4602 4603 4604 4605
    if (ndb_index.addColumnName(field->field_name))
    {
      DBUG_RETURN(my_errno= errno);
    }
4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621
  }
  
  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)
{
4622
  NDBDICT *dict;
4623
  char new_tabname[FN_HEADLEN];
4624
  char new_dbname[FN_HEADLEN];
4625 4626
  const NDBTAB *orig_tab;
  int result;
4627 4628
  bool recreate_indexes= FALSE;
  NDBDICT::List index_list;
4629 4630

  DBUG_ENTER("ha_ndbcluster::rename_table");
4631
  DBUG_PRINT("info", ("Renaming %s to %s", from, to));
4632
  set_dbname(from);
4633
  set_dbname(to, new_dbname);
4634 4635 4636
  set_tabname(from);
  set_tabname(to, new_tabname);

4637 4638 4639
  if (check_ndb_connection())
    DBUG_RETURN(my_errno= HA_ERR_NO_CONNECTION);

mskold@mysql.com's avatar
mskold@mysql.com committed
4640 4641
  Ndb *ndb= get_ndb();
  dict= ndb->getDictionary();
4642 4643
  if (!(orig_tab= dict->getTable(m_tabname)))
    ERR_RETURN(dict->getNdbError());
4644 4645 4646 4647 4648 4649 4650
  // 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());
  }
4651 4652 4653 4654 4655 4656
  if (my_strcasecmp(system_charset_info, new_dbname, m_dbname))
  {
    dict->listIndexes(index_list, m_tabname);
    recreate_indexes= TRUE;
  }

4657 4658 4659
  m_table= (void *)orig_tab;
  // Change current database to that of target table
  set_dbname(to);
4660 4661 4662 4663
  if (ndb->setDatabaseName(m_dbname))
  {
    ERR_RETURN(ndb->getNdbError());
  }
4664
  if (!(result= alter_table_name(new_tabname)))
4665
  {
4666 4667
    // Rename .ndb file
    result= handler::rename_table(from, to);
4668
  }
4669

4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681
  // If we are moving tables between databases, we need to recreate
  // indexes
  if (recreate_indexes)
  {
    const NDBTAB *new_tab;
    set_tabname(to);
    if (!(new_tab= dict->getTable(m_tabname)))
      ERR_RETURN(dict->getNdbError());

    for (unsigned i = 0; i < index_list.count; i++) {
        NDBDICT::List::Element& index_el = index_list.elements[i];
	set_dbname(from);
4682 4683 4684 4685
	if (ndb->setDatabaseName(m_dbname))
        {
          ERR_RETURN(ndb->getNdbError());
        }
4686 4687
	const NDBINDEX * index= dict->getIndex(index_el.name,  *new_tab);
	set_dbname(to);
4688 4689 4690 4691
	if (ndb->setDatabaseName(m_dbname))
        {
          ERR_RETURN(ndb->getNdbError());
        }
4692 4693 4694 4695 4696 4697 4698
	DBUG_PRINT("info", ("Creating index %s/%s", 
			    m_dbname, index->getName()));
	dict->createIndex(*index);
        DBUG_PRINT("info", ("Dropping index %s/%s", 
			    m_dbname, index->getName()));
	
	set_dbname(from);
4699 4700 4701 4702
        if (ndb->setDatabaseName(m_dbname))
        {
          ERR_RETURN(ndb->getNdbError());
        }
4703 4704 4705 4706
	dict->dropIndex(*index);
    }
  }

4707 4708 4709 4710 4711 4712 4713 4714
  DBUG_RETURN(result);
}


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

4715
int ha_ndbcluster::alter_table_name(const char *to)
4716
{
4717 4718
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
4719
  const NDBTAB *orig_tab= (const NDBTAB *) m_table;
4720 4721
  DBUG_ENTER("alter_table_name_table");

4722
  NdbDictionary::Table new_tab= *orig_tab;
4723 4724 4725 4726
  if (new_tab.setName(to))
  {
    DBUG_RETURN(my_errno= errno);
  }
4727
  if (dict->alterTable(new_tab) != 0)
4728 4729 4730
    ERR_RETURN(dict->getNdbError());

  m_table= NULL;
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
4731
  m_table_info= NULL;
4732 4733 4734 4735 4736 4737
                                                                             
  DBUG_RETURN(0);
}


/*
4738 4739
  Delete table from NDB Cluster

4740 4741 4742 4743
 */

int ha_ndbcluster::delete_table(const char *name)
{
4744
  DBUG_ENTER("ha_ndbcluster::delete_table");
4745 4746 4747
  DBUG_PRINT("enter", ("name: %s", name));
  set_dbname(name);
  set_tabname(name);
4748

4749 4750
  if (check_ndb_connection())
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4751 4752

  /* Call ancestor function to delete .ndb file */
4753
  handler::delete_table(name);
4754 4755
  
  /* Drop the table from NDB */
4756 4757 4758 4759 4760
  DBUG_RETURN(drop_table());
}


/*
4761
  Drop table in NDB Cluster
4762 4763 4764 4765
 */

int ha_ndbcluster::drop_table()
{
4766
  THD *thd= current_thd;
4767 4768
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
4769

4770 4771 4772 4773
  DBUG_ENTER("drop_table");
  DBUG_PRINT("enter", ("Deleting %s", m_tabname));
  
  release_metadata();
4774
  while (dict->dropTable(m_tabname)) 
4775 4776
  {
    const NdbError err= dict->getNdbError();
4777 4778 4779 4780 4781 4782 4783 4784 4785
    switch (err.status)
    {
      case NdbError::TemporaryError:
        if (!thd->killed)
          continue; // retry indefinitly
        break;
      default:
        break;
    }
4786
    ERR_RETURN(dict->getNdbError());
4787 4788
  }

4789 4790 4791 4792
  DBUG_RETURN(0);
}


4793
ulonglong ha_ndbcluster::get_auto_increment()
4794
{  
4795 4796
  int cache_size;
  Uint64 auto_value;
4797 4798
  DBUG_ENTER("get_auto_increment");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));
4799
  Ndb *ndb= get_ndb();
4800
   
4801
  if (m_rows_inserted > m_rows_to_insert)
4802
  {
4803 4804
    /* We guessed too low */
    m_rows_to_insert+= m_autoincrement_prefetch;
4805
  }
serg@serg.mylan's avatar
serg@serg.mylan committed
4806
  cache_size= 
4807 4808 4809 4810
    (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));
4811
  int ret;
4812 4813
  uint retries= NDB_AUTO_INCREMENT_RETRIES;
  do {
4814 4815 4816 4817 4818
    ret=
      m_skip_auto_increment ? 
      ndb->readAutoIncrementValue((const NDBTAB *) m_table, auto_value) :
      ndb->getAutoIncrementValue((const NDBTAB *) m_table, auto_value, cache_size);
  } while (ret == -1 && 
4819 4820
           --retries &&
           ndb->getNdbError().status == NdbError::TemporaryError);
4821
  if (ret == -1)
4822 4823 4824 4825 4826 4827
  {
    const NdbError err= ndb->getNdbError();
    sql_print_error("Error %lu in ::get_auto_increment(): %s",
                    (ulong) err.code, err.message);
    DBUG_RETURN(~(ulonglong) 0);
  }
4828
  DBUG_RETURN((longlong)auto_value);
4829 4830 4831 4832 4833 4834 4835 4836
}


/*
  Constructor for the NDB Cluster table handler 
 */

ha_ndbcluster::ha_ndbcluster(TABLE *table_arg):
4837
  handler(&ndbcluster_hton, table_arg),
4838 4839 4840
  m_active_trans(NULL),
  m_active_cursor(NULL),
  m_table(NULL),
4841
  m_table_version(-1),
4842
  m_table_info(NULL),
4843
  m_table_flags(HA_REC_NOT_IN_SEQ |
4844 4845 4846 4847
                HA_NULL_IN_KEY |
                HA_AUTO_PART_KEY |
                HA_NO_PREFIX_CHAR_KEYS |
                HA_NEED_READ_RANGE_BUFFER |
4848
                HA_CAN_GEOMETRY |
4849 4850
                HA_CAN_BIT_FIELD |
                HA_PARTIAL_COLUMN_READ),
4851
  m_share(0),
4852
  m_use_write(FALSE),
4853
  m_ignore_dup_key(FALSE),
4854
  m_has_unique_index(FALSE),
4855 4856
  m_primary_key_update(FALSE),
  m_retrieve_all_fields(FALSE),
4857
  m_retrieve_primary_key(FALSE),
4858 4859 4860
  m_rows_to_insert((ha_rows) 1),
  m_rows_inserted((ha_rows) 0),
  m_bulk_insert_rows((ha_rows) 1024),
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4861
  m_rows_changed((ha_rows) 0),
4862
  m_bulk_insert_not_flushed(FALSE),
4863 4864
  m_delete_cannot_batch(FALSE),
  m_update_cannot_batch(FALSE),
4865 4866 4867
  m_ops_pending(0),
  m_skip_auto_increment(TRUE),
  m_blobs_pending(0),
4868
  m_blobs_offset(0),
4869 4870
  m_blobs_buffer(0),
  m_blobs_buffer_size(0),
4871 4872 4873
  m_dupkey((uint) -1),
  m_ha_not_exact_count(FALSE),
  m_force_send(TRUE),
4874
  m_autoincrement_prefetch((ha_rows) 32),
4875
  m_transaction_on(TRUE),
mskold@mysql.com's avatar
mskold@mysql.com committed
4876 4877
  m_cond_stack(NULL),
  m_multi_cursor(NULL)
4878
{
4879
  int i;
4880
 
4881 4882 4883 4884 4885
  DBUG_ENTER("ha_ndbcluster");

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

4886
  records= ~(ha_rows)0; // uninitialized
4887 4888
  block_size= 1024;

4889 4890
  for (i= 0; i < MAX_KEY; i++)
  {
4891 4892 4893 4894
    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;
4895 4896
  }

4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908
  DBUG_VOID_RETURN;
}


/*
  Destructor for NDB Cluster table handler
 */

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

4909 4910
  if (m_share)
    free_share(m_share);
4911
  release_metadata();
4912 4913
  my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
  m_blobs_buffer= 0;
4914 4915

  // Check for open cursor/transaction
4916 4917
  if (m_active_cursor) {
  }
4918
  DBUG_ASSERT(m_active_cursor == NULL);
4919 4920
  if (m_active_trans) {
  }
4921 4922
  DBUG_ASSERT(m_active_trans == NULL);

4923 4924 4925 4926
  // Discard the condition stack
  DBUG_PRINT("info", ("Clearing condition stack"));
  cond_clear();

4927 4928 4929 4930
  DBUG_VOID_RETURN;
}


mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4931

4932 4933 4934 4935 4936 4937 4938 4939
/*
  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)
{
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
4940
  int res;
4941 4942 4943 4944 4945 4946 4947 4948
  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
  
4949
  if (table->s->primary_key != MAX_KEY) 
4950
  {
4951
    key= table->key_info+table->s->primary_key;
4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962
    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);
  
4963 4964
  if (check_ndb_connection()) {
    free_share(m_share); m_share= 0;
4965
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4966
  }
4967
  
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
4968 4969
  res= get_metadata(name);
  if (!res)
4970 4971
  {
    Ndb *ndb= get_ndb();
4972 4973 4974 4975
    if (ndb->setDatabaseName(m_dbname))
    {
      ERR_RETURN(ndb->getNdbError());
    }
stewart@willster.(none)'s avatar
stewart@willster.(none) committed
4976 4977 4978
    struct Ndb_statistics stat;
    res= ndb_get_table_statistics(NULL, false, ndb, m_tabname, &stat);
    records= stat.row_count;
4979 4980 4981
    if(!res)
      res= info(HA_STATUS_CONST);
  }
4982

tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
4983
  DBUG_RETURN(res);
4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994
}


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

int ha_ndbcluster::close(void)
{
  DBUG_ENTER("close");  
4995
  free_share(m_share); m_share= 0;
4996 4997 4998 4999 5000
  release_metadata();
  DBUG_RETURN(0);
}


5001
Thd_ndb* ha_ndbcluster::seize_thd_ndb()
5002
{
5003 5004
  Thd_ndb *thd_ndb;
  DBUG_ENTER("seize_thd_ndb");
5005

5006
  thd_ndb= new Thd_ndb();
5007 5008 5009 5010 5011
  if (thd_ndb == NULL)
  {
    my_errno= HA_ERR_OUT_OF_MEM;
    return NULL;
  }
5012 5013 5014
  thd_ndb->ndb->getDictionary()->set_local_table_data_size(
    sizeof(Ndb_local_table_statistics)
    );
5015
  if (thd_ndb->ndb->init(max_transactions) != 0)
5016
  {
5017
    ERR_PRINT(thd_ndb->ndb->getNdbError());
5018 5019 5020 5021 5022 5023
    /*
      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 
    */
5024 5025
    delete thd_ndb;
    thd_ndb= NULL;
5026
  }
5027
  DBUG_RETURN(thd_ndb);
5028 5029 5030
}


5031
void ha_ndbcluster::release_thd_ndb(Thd_ndb* thd_ndb)
5032
{
5033 5034
  DBUG_ENTER("release_thd_ndb");
  delete thd_ndb;
5035 5036 5037 5038 5039
  DBUG_VOID_RETURN;
}


/*
magnus@neptunus.(none)'s avatar
magnus@neptunus.(none) committed
5040
  If this thread already has a Thd_ndb object allocated
5041
  in current THD, reuse it. Otherwise
magnus@neptunus.(none)'s avatar
magnus@neptunus.(none) committed
5042
  seize a Thd_ndb object, assign it to current THD and use it.
5043 5044 5045
 
*/

5046
Ndb* check_ndb_in_thd(THD* thd)
5047
{
5048
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
5049
  if (!thd_ndb)
5050
  {
magnus@neptunus.(none)'s avatar
magnus@neptunus.(none) committed
5051
    if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5052
      return NULL;
5053
    set_thd_ndb(thd, thd_ndb);
5054
  }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5055
  return thd_ndb->ndb;
5056 5057
}

magnus@neptunus.(none)'s avatar
magnus@neptunus.(none) committed
5058

5059

5060
int ha_ndbcluster::check_ndb_connection(THD* thd)
5061
{
5062
  Ndb *ndb;
5063 5064
  DBUG_ENTER("check_ndb_connection");
  
5065
  if (!(ndb= check_ndb_in_thd(thd)))
5066
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
5067 5068 5069 5070
  if (ndb->setDatabaseName(m_dbname))
  {
    ERR_RETURN(ndb->getNdbError());
  }
5071 5072 5073
  DBUG_RETURN(0);
}

magnus@neptunus.(none)'s avatar
magnus@neptunus.(none) committed
5074

5075
int ndbcluster_close_connection(THD *thd)
5076
{
5077
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
5078
  DBUG_ENTER("ndbcluster_close_connection");
5079 5080
  if (thd_ndb)
  {
5081
    ha_ndbcluster::release_thd_ndb(thd_ndb);
5082
    set_thd_ndb(thd, NULL); // not strictly required but does not hurt either
5083
  }
5084
  DBUG_RETURN(0);
5085 5086 5087 5088 5089 5090 5091
}


/*
  Try to discover one table from NDB
 */

5092
int ndbcluster_discover(THD* thd, const char *db, const char *name,
5093
                        const void** frmblob, uint* frmlen)
5094 5095 5096 5097
{
  uint len;
  const void* data;
  const NDBTAB* tab;
5098
  Ndb* ndb;
5099
  DBUG_ENTER("ndbcluster_discover");
5100
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name)); 
5101

5102 5103
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(HA_ERR_NO_CONNECTION);  
5104 5105 5106 5107
  if (ndb->setDatabaseName(db))
  {
    ERR_RETURN(ndb->getNdbError());
  }
5108
  NDBDICT* dict= ndb->getDictionary();
5109
  dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
5110 5111 5112 5113 5114
  dict->invalidateTable(name);
  if (!(tab= dict->getTable(name)))
  {    
    const NdbError err= dict->getNdbError();
    if (err.code == 709)
5115
      DBUG_RETURN(-1);
5116
    ERR_RETURN(err);
5117 5118 5119 5120 5121 5122
  }
  DBUG_PRINT("info", ("Found table %s", tab->getName()));
  
  len= tab->getFrmLength();  
  if (len == 0 || tab->getFrmData() == NULL)
  {
5123 5124
    DBUG_PRINT("error", ("No frm data found."));
    DBUG_RETURN(1);
5125 5126 5127
  }
  
  if (unpackfrm(&data, &len, tab->getFrmData()))
5128 5129 5130 5131
  {
    DBUG_PRINT("error", ("Could not unpack table"));
    DBUG_RETURN(1);
  }
5132 5133 5134 5135 5136 5137 5138 5139

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

/*
5140
  Check if a table exists in NDB
5141

5142
 */
5143

5144
int ndbcluster_table_exists_in_engine(THD* thd, const char *db, const char *name)
5145 5146 5147
{
  const NDBTAB* tab;
  Ndb* ndb;
5148
  DBUG_ENTER("ndbcluster_table_exists_in_engine");
5149
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
5150 5151

  if (!(ndb= check_ndb_in_thd(thd)))
5152
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
5153 5154 5155 5156
  if (ndb->setDatabaseName(db))
  {
    ERR_RETURN(ndb->getNdbError());
  }
5157
  NDBDICT* dict= ndb->getDictionary();
5158
  dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
5159 5160
  dict->invalidateTable(name);
  if (!(tab= dict->getTable(name)))
5161
  {
5162
    ERR_RETURN(dict->getNdbError());
5163
  }
5164

5165
  DBUG_PRINT("info", ("Found table %s", tab->getName()));
5166
  DBUG_RETURN(HA_ERR_TABLE_EXIST);
5167 5168
}

5169 5170


5171
extern "C" byte* tables_get_key(const char *entry, uint *length,
5172
                                my_bool not_used __attribute__((unused)))
5173 5174 5175 5176 5177 5178
{
  *length= strlen(entry);
  return (byte*) entry;
}


5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192
/*
  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;
5193
  int ret= 0;
5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216
  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
5217 5218 5219 5220
  if (ndb->setDatabaseName(dbname))
  {
    ERR_RETURN(ndb->getNdbError());
  }
5221 5222
  List_iterator_fast<char> it(drop_list);
  while ((tabname=it++))
5223
  {
5224
    while (dict->dropTable(tabname))
5225 5226
    {
      const NdbError err= dict->getNdbError();
5227 5228 5229 5230 5231 5232 5233 5234 5235 5236
      switch (err.status)
      {
        case NdbError::TemporaryError:
          if (!thd->killed)
            continue; // retry indefinitly
          break;
        default:
          break;
      }
      if (err.code != 709) // 709: No such table existed
5237 5238
      {
        ERR_PRINT(err);
5239
        ret= ndb_to_mysql_error(&err);
5240
      }
5241
      break;
5242 5243 5244
    }
  }
  DBUG_RETURN(ret);      
5245 5246 5247
}


5248
int ndbcluster_find_files(THD *thd,const char *db,const char *path,
5249
                          const char *wild, bool dir, List<char> *files)
5250
{
5251 5252 5253
  DBUG_ENTER("ndbcluster_find_files");
  DBUG_PRINT("enter", ("db: %s", db));
  { // extra bracket to avoid gcc 2.95.3 warning
5254
  uint i;
5255
  Ndb* ndb;
5256
  char name[FN_REFLEN];
5257
  HASH ndb_tables, ok_tables;
5258
  NdbDictionary::Dictionary::List list;
5259 5260 5261 5262

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

5263
  if (dir)
5264
    DBUG_RETURN(0); // Discover of databases not yet supported
5265

5266
  // List tables in NDB
5267
  NDBDICT *dict= ndb->getDictionary();
5268
  if (dict->listObjects(list, 
5269
                        NdbDictionary::Object::UserTable) != 0)
5270
    ERR_RETURN(dict->getNdbError());
5271

5272
  if (hash_init(&ndb_tables, system_charset_info,list.count,0,0,
5273
                (hash_get_key)tables_get_key,0,0))
5274 5275 5276 5277 5278 5279
  {
    DBUG_PRINT("error", ("Failed to init HASH ndb_tables"));
    DBUG_RETURN(-1);
  }

  if (hash_init(&ok_tables, system_charset_info,32,0,0,
5280
                (hash_get_key)tables_get_key,0,0))
5281 5282 5283 5284 5285 5286
  {
    DBUG_PRINT("error", ("Failed to init HASH ok_tables"));
    hash_free(&ndb_tables);
    DBUG_RETURN(-1);
  }  

5287 5288 5289
  for (i= 0 ; i < list.count ; i++)
  {
    NdbDictionary::Dictionary::List::Element& t= list.elements[i];
5290
    DBUG_PRINT("info", ("Found %s/%s in NDB", t.database, t.name));     
5291

5292 5293 5294
    // Add only tables that belongs to db
    if (my_strcasecmp(system_charset_info, t.database, db))
      continue;
5295

5296
    // Apply wildcard to list of tables in NDB
5297
    if (wild)
5298
    {
5299 5300
      if (lower_case_table_names)
      {
5301 5302
        if (wild_case_compare(files_charset_info, t.name, wild))
          continue;
5303 5304
      }
      else if (wild_compare(t.name,wild,0))
5305
        continue;
5306
    }
5307 5308
    DBUG_PRINT("info", ("Inserting %s into ndb_tables hash", t.name));     
    my_hash_insert(&ndb_tables, (byte*)thd->strdup(t.name));
5309 5310
  }

5311 5312 5313 5314 5315
  char *file_name;
  List_iterator<char> it(*files);
  List<char> delete_list;
  while ((file_name=it++))
  {
5316
    bool file_on_disk= false;
5317 5318 5319 5320
    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));
5321
      file_on_disk= true;
5322 5323
    }
    
5324
    // Check for .ndb file with this name
5325
    (void)strxnmov(name, FN_REFLEN, 
5326
                   mysql_data_home,"/",db,"/",file_name,ha_ndb_ext,NullS);
5327
    DBUG_PRINT("info", ("Check access for %s", name));
5328
    if (access(name, F_OK))
5329 5330 5331
    {
      DBUG_PRINT("info", ("%s did not exist on disk", name));     
      // .ndb file did not exist on disk, another table type
5332
      if (file_on_disk)
5333 5334 5335 5336 5337
      {
	// Ignore this ndb table
	gptr record=  hash_search(&ndb_tables, file_name, strlen(file_name));
	DBUG_ASSERT(record);
	hash_delete(&ndb_tables, record);
5338 5339 5340 5341
	push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
			    ER_TABLE_EXISTS_ERROR,
			    "Local table %s.%s shadows ndb table",
			    db, file_name);
5342
      }
5343 5344 5345 5346
      continue;
    }
    if (file_on_disk) 
    {
5347
      // File existed in NDB and as frm file, put in ok_tables list
5348
      my_hash_insert(&ok_tables, (byte*)file_name);
5349
      continue;
5350
    }
5351 5352 5353
    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.
5354
    if (ndbcluster_table_exists_in_engine(thd, db, file_name) == HA_ERR_NO_SUCH_TABLE)
5355 5356 5357 5358 5359 5360 5361
    {
      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));
    }
  }
5362

5363 5364 5365 5366
  // 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++)
5367
  {
5368 5369
    file_name= hash_element(&ndb_tables, i);
    if (!hash_search(&ok_tables, file_name, strlen(file_name)))
5370
    {
5371 5372 5373 5374 5375 5376
      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));
    }
  }
5377

5378 5379
  // Lock mutex before deleting and creating frm files
  pthread_mutex_lock(&LOCK_open);
5380

5381 5382 5383 5384 5385
  if (!global_read_lock)
  {
    // Delete old files
    List_iterator_fast<char> it3(delete_list);
    while ((file_name=it3++))
5386 5387
    {
      DBUG_PRINT("info", ("Remove table %s/%s", db, file_name));
5388 5389 5390 5391
      // Delete the table and all related files
      TABLE_LIST table_list;
      bzero((char*) &table_list,sizeof(table_list));
      table_list.db= (char*) db;
5392
      table_list.alias= table_list.table_name= (char*)file_name;
5393
      (void)mysql_rm_table_part2(thd, &table_list,
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5394 5395 5396 5397
                                                                 /* if_exists */ FALSE,
                                                                 /* drop_temporary */ FALSE,
                                                                 /* drop_view */ FALSE,
                                                                 /* dont_log_query*/ TRUE);
5398 5399
      /* Clear error message that is returned when table is deleted */
      thd->clear_error();
5400 5401 5402
    }
  }

5403 5404 5405 5406
  // Create new files
  List_iterator_fast<char> it2(create_list);
  while ((file_name=it2++))
  {  
5407
    DBUG_PRINT("info", ("Table %s need discovery", file_name));
5408
    if (ha_create_table_from_engine(thd, db, file_name) == 0)
5409
      files->push_back(thd->strdup(file_name)); 
5410 5411 5412 5413 5414
  }

  pthread_mutex_unlock(&LOCK_open);      
  
  hash_free(&ok_tables);
5415
  hash_free(&ndb_tables);
5416
  } // extra bracket to avoid gcc 2.95.3 warning
5417
  DBUG_RETURN(0);    
5418 5419 5420 5421 5422 5423 5424 5425
}


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

5426 5427 5428 5429 5430 5431 5432
/* Call back after cluster connect */
static int connect_callback()
{
  update_status_variables(g_ndb_cluster_connection);
  return 0;
}

5433
bool ndbcluster_init()
5434
{
5435
  int res;
5436
  DBUG_ENTER("ndbcluster_init");
5437 5438 5439 5440

  if (have_ndbcluster != SHOW_OPTION_YES)
    goto ndbcluster_init_error;

5441
  // Set connectstring if specified
5442 5443
  if (opt_ndbcluster_connectstring != 0)
    DBUG_PRINT("connectstring", ("%s", opt_ndbcluster_connectstring));     
5444
  if ((g_ndb_cluster_connection=
5445
       new Ndb_cluster_connection(opt_ndbcluster_connectstring)) == 0)
5446
  {
5447
    DBUG_PRINT("error",("Ndb_cluster_connection(%s)",
5448
                        opt_ndbcluster_connectstring));
5449
    my_errno= HA_ERR_OUT_OF_MEM;
5450
    goto ndbcluster_init_error;
5451
  }
tomas@poseidon.ndb.mysql.com's avatar
ndb:  
tomas@poseidon.ndb.mysql.com committed
5452 5453
  {
    char buf[128];
5454
    my_snprintf(buf, sizeof(buf), "mysqld --server-id=%lu", server_id);
tomas@poseidon.ndb.mysql.com's avatar
ndb:  
tomas@poseidon.ndb.mysql.com committed
5455 5456
    g_ndb_cluster_connection->set_name(buf);
  }
5457 5458 5459
  g_ndb_cluster_connection->set_optimized_node_selection
    (opt_ndb_optimized_node_selection);

5460
  // Create a Ndb object to open the connection  to NDB
5461 5462 5463
  if ( (g_ndb= new Ndb(g_ndb_cluster_connection, "sys")) == 0 )
  {
    DBUG_PRINT("error", ("failed to create global ndb object"));
5464
    my_errno= HA_ERR_OUT_OF_MEM;
5465 5466
    goto ndbcluster_init_error;
  }
5467
  g_ndb->getDictionary()->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
5468 5469 5470
  if (g_ndb->init() != 0)
  {
    ERR_PRINT (g_ndb->getNdbError());
5471
    goto ndbcluster_init_error;
5472
  }
5473

5474
  if ((res= g_ndb_cluster_connection->connect(0,0,0)) == 0)
5475
  {
5476
    connect_callback();
5477
    DBUG_PRINT("info",("NDBCLUSTER storage engine at %s on port %d",
5478 5479
                       g_ndb_cluster_connection->get_connected_host(),
                       g_ndb_cluster_connection->get_connected_port()));
5480
    g_ndb_cluster_connection->wait_until_ready(10,3);
5481
  } 
5482
  else if (res == 1)
5483
  {
5484
    if (g_ndb_cluster_connection->start_connect_thread(connect_callback)) 
5485
    {
5486
      DBUG_PRINT("error", ("g_ndb_cluster_connection->start_connect_thread()"));
5487 5488
      goto ndbcluster_init_error;
    }
5489
#ifndef DBUG_OFF
5490 5491
    {
      char buf[1024];
5492
      DBUG_PRINT("info",
5493 5494 5495 5496
                 ("NDBCLUSTER storage engine not started, "
                  "will connect using %s",
                  g_ndb_cluster_connection->
                  get_connectstring(buf,sizeof(buf))));
5497
    }
5498
#endif
5499
  }
5500
  else
5501 5502 5503
  {
    DBUG_ASSERT(res == -1);
    DBUG_PRINT("error", ("permanent error"));
5504
    goto ndbcluster_init_error;
5505
  }
5506
  
5507 5508 5509
  (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);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5510 5511
  pthread_mutex_init(&LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST);
  pthread_cond_init(&COND_ndb_util_thread, NULL);
5512

mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5513

jonas@perch.ndb.mysql.com's avatar
jonas@perch.ndb.mysql.com committed
5514
  ndb_cache_check_time = opt_ndb_cache_check_time;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5515 5516 5517 5518 5519
  // 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"));
5520 5521 5522 5523
    hash_free(&ndbcluster_open_tables);
    pthread_mutex_destroy(&ndbcluster_mutex);
    pthread_mutex_destroy(&LOCK_ndb_util_thread);
    pthread_cond_destroy(&COND_ndb_util_thread);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5524 5525 5526
    goto ndbcluster_init_error;
  }
  
5527
  ndbcluster_inited= 1;
5528
  DBUG_RETURN(FALSE);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5529

5530
ndbcluster_init_error:
5531
  if (g_ndb)
5532 5533 5534 5535 5536
    delete g_ndb;
  g_ndb= NULL;
  if (g_ndb_cluster_connection)
    delete g_ndb_cluster_connection;
  g_ndb_cluster_connection= NULL;
5537 5538
  have_ndbcluster= SHOW_OPTION_DISABLED;	// If we couldn't use handler
  DBUG_RETURN(TRUE);
5539 5540 5541 5542 5543 5544
}


/*
  End use of the NDB Cluster table handler
  - free all global variables allocated by 
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5545
    ndbcluster_init()
5546 5547 5548 5549 5550
*/

bool ndbcluster_end()
{
  DBUG_ENTER("ndbcluster_end");
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5551

5552 5553 5554
  if (!ndbcluster_inited)
    DBUG_RETURN(0);

mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5555 5556 5557 5558 5559 5560
  // 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);

5561
  if (g_ndb)
5562 5563
  {
#ifndef DBUG_OFF
5564 5565
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
5566 5567 5568 5569 5570 5571 5572 5573 5574 5575
    while (g_ndb->get_free_list_usage(&tmp))
    {
      uint leaked= (uint) tmp.m_created - tmp.m_free;
      if (leaked)
        fprintf(stderr, "NDB: Found %u %s%s that %s not been released\n",
                leaked, tmp.m_name,
                (leaked == 1)?"":"'s",
                (leaked == 1)?"has":"have");
    }
#endif
5576
    delete g_ndb;
5577
    g_ndb= NULL;
5578
  }
5579
  delete g_ndb_cluster_connection;
5580
  g_ndb_cluster_connection= NULL;
5581

5582 5583
  hash_free(&ndbcluster_open_tables);
  pthread_mutex_destroy(&ndbcluster_mutex);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5584 5585
  pthread_mutex_destroy(&LOCK_ndb_util_thread);
  pthread_cond_destroy(&COND_ndb_util_thread);
5586 5587 5588 5589
  ndbcluster_inited= 0;
  DBUG_RETURN(0);
}

5590 5591 5592 5593 5594
/*
  Static error print function called from
  static handler method ndbcluster_commit
  and ndbcluster_rollback
*/
5595 5596

void ndbcluster_print_error(int error, const NdbOperation *error_op)
5597
{
5598 5599
  DBUG_ENTER("ndbcluster_print_error");
  TABLE tab;
5600
  const char *tab_name= (error_op) ? error_op->getTableName() : "";
5601
  tab.alias= (char *) tab_name;
5602
  ha_ndbcluster error_handler(&tab);
5603
  tab.file= &error_handler;
5604
  error_handler.print_error(error, MYF(0));
ndbdev@ndbmaster.mysql.com's avatar
ndbdev@ndbmaster.mysql.com committed
5605
  DBUG_VOID_RETURN;
5606
}
5607

5608 5609 5610
/**
 * Set a given location from full pathname to database name
 *
5611
 */
5612
void ha_ndbcluster::set_dbname(const char *path_name, char *dbname)
5613 5614 5615 5616
{
  char *end, *ptr;
  
  /* Scan name from the end */
5617 5618 5619 5620 5621 5622
  ptr= strend(path_name)-1;
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  ptr--;
  end= ptr;
5623 5624 5625 5626
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  uint name_len= end - ptr;
5627 5628
  memcpy(dbname, ptr + 1, name_len);
  dbname[name_len]= '\0';
5629 5630
#ifdef __WIN__
  /* Put to lower case */
5631 5632
  
  ptr= dbname;
5633 5634
  
  while (*ptr != '\0') {
5635
    *ptr= tolower(*ptr);
5636 5637 5638 5639 5640
    ptr++;
  }
#endif
}

5641 5642 5643 5644 5645 5646 5647 5648 5649
/*
  Set m_dbname from full pathname to table file
 */

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

5650 5651 5652 5653 5654 5655 5656 5657 5658 5659
/**
 * 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 */
5660 5661
  end= strend(path_name)-1;
  ptr= end;
5662 5663 5664
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
5665
  uint name_len= end - ptr;
5666
  memcpy(tabname, ptr + 1, end - ptr);
5667
  tabname[name_len]= '\0';
5668 5669
#ifdef __WIN__
  /* Put to lower case */
5670
  ptr= tabname;
5671 5672 5673 5674 5675 5676 5677 5678 5679
  
  while (*ptr != '\0') {
    *ptr= tolower(*ptr);
    ptr++;
  }
#endif
}

/*
5680
  Set m_tabname from full pathname to table file 
5681 5682
 */

5683
void ha_ndbcluster::set_tabname(const char *path_name)
5684
{
5685
  set_tabname(path_name, m_tabname);
5686 5687 5688 5689
}


ha_rows 
5690 5691 5692 5693
ha_ndbcluster::records_in_range(uint inx, key_range *min_key,
                                key_range *max_key)
{
  KEY *key_info= table->key_info + inx;
5694
  uint key_length= key_info->key_length;
5695
  NDB_INDEX_TYPE idx_type= get_index_type(inx);  
5696 5697

  DBUG_ENTER("records_in_range");
5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711
  // 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 */
5712 5713
}

5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740
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;
}
pekka@mysql.com's avatar
pekka@mysql.com committed
5741 5742 5743 5744
uint ha_ndbcluster::max_supported_key_part_length() const
{
  return NDB_MAX_KEY_SIZE;
}
5745 5746 5747 5748 5749 5750 5751 5752 5753 5754
bool ha_ndbcluster::low_byte_first() const
{ 
#ifdef WORDS_BIGENDIAN
  return FALSE;
#else
  return TRUE;
#endif
}
bool ha_ndbcluster::has_transactions()
{
5755
  return TRUE;
5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769
}
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";
  }
}
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5770

5771 5772
uint8 ha_ndbcluster::table_cache_type()
{
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5773 5774 5775 5776 5777 5778
  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,
5779
                         Uint64 *commit_count)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5780 5781 5782
{
  DBUG_ENTER("ndb_get_commitcount");

5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800
  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);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5801 5802
  if (ndb_cache_check_time > 0)
  {
5803
    if (share->commit_count != 0)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5804
    {
5805
      *commit_count= share->commit_count;
5806
#ifndef DBUG_OFF
5807
      char buff[22];
5808
#endif
5809 5810
      DBUG_PRINT("info", ("Getting commit_count: %s from share",
                          llstr(share->commit_count, buff)));
5811 5812 5813
      pthread_mutex_unlock(&share->mutex);
      free_share(share);
      DBUG_RETURN(0);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5814 5815
    }
  }
5816
  DBUG_PRINT("info", ("Get commit_count from NDB"));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5817 5818 5819
  Ndb *ndb;
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(1);
5820 5821 5822 5823
  if (ndb->setDatabaseName(dbname))
  {
    ERR_RETURN(ndb->getNdbError());
  }
5824 5825
  uint lock= share->commit_count_lock;
  pthread_mutex_unlock(&share->mutex);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5826 5827

  struct Ndb_statistics stat;
5828
  if (ndb_get_table_statistics(NULL, false, ndb, tabname, &stat))
5829 5830
  {
    free_share(share);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5831
    DBUG_RETURN(1);
5832 5833 5834
  }

  pthread_mutex_lock(&share->mutex);
5835
  if (share->commit_count_lock == lock)
5836
  {
5837
#ifndef DBUG_OFF
5838
    char buff[22];
5839
#endif
5840 5841
    DBUG_PRINT("info", ("Setting commit_count to %s",
                        llstr(stat.commit_count, buff)));
5842 5843 5844 5845 5846 5847 5848 5849 5850 5851
    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);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887
  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,
5888 5889
                                   char *full_name, uint full_name_len,
                                   ulonglong *engine_data)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5890 5891 5892 5893 5894
{
  Uint64 commit_count;
  bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
  char *dbname= full_name;
  char *tabname= dbname+strlen(dbname)+1;
5895
#ifndef DBUG_OFF
5896
  char buff[22], buff2[22];
5897
#endif
5898
  DBUG_ENTER("ndbcluster_cache_retrieval_allowed");
5899 5900
  DBUG_PRINT("enter", ("dbname: %s, tabname: %s, is_autocommit: %d",
                       dbname, tabname, is_autocommit));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5901 5902

  if (!is_autocommit)
5903 5904
  {
    DBUG_PRINT("exit", ("No, don't use cache in transaction"));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5905
    DBUG_RETURN(FALSE);
5906
  }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5907 5908 5909

  if (ndb_get_commitcount(thd, dbname, tabname, &commit_count))
  {
5910 5911
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, could not retrieve commit_count"));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5912 5913
    DBUG_RETURN(FALSE);
  }
5914 5915
  DBUG_PRINT("info", ("*engine_data: %s, commit_count: %s",
                      llstr(*engine_data, buff), llstr(commit_count, buff2)));
5916
  if (commit_count == 0)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5917
  {
5918 5919
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, local commit has been performed"));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5920 5921
    DBUG_RETURN(FALSE);
  }
5922 5923 5924 5925 5926 5927
  else if (*engine_data != commit_count)
  {
    *engine_data= commit_count; /* invalidate */
     DBUG_PRINT("exit", ("No, commit_count has changed"));
     DBUG_RETURN(FALSE);
   }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5928

5929 5930
  DBUG_PRINT("exit", ("OK to use cache, engine_data: %s",
                      llstr(*engine_data, buff)));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958
  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,
5959 5960 5961
                                          char *full_name, uint full_name_len,
                                          qc_engine_callback *engine_callback,
                                          ulonglong *engine_data)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5962
{
5963
  Uint64 commit_count;
5964
#ifndef DBUG_OFF
5965
  char buff[22];
5966
#endif
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5967
  bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
5968
  DBUG_ENTER("ha_ndbcluster::register_query_cache_table");
5969 5970 5971
  DBUG_PRINT("enter",("dbname: %s, tabname: %s, is_autocommit: %d",
		      m_dbname, m_tabname, is_autocommit));

mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5972
  if (!is_autocommit)
5973 5974
  {
    DBUG_PRINT("exit", ("Can't register table during transaction"))
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5975
    DBUG_RETURN(FALSE);
5976
  }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5977 5978 5979 5980

  if (ndb_get_commitcount(thd, m_dbname, m_tabname, &commit_count))
  {
    *engine_data= 0;
5981
    DBUG_PRINT("exit", ("Error, could not get commitcount"))
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5982 5983 5984 5985
    DBUG_RETURN(FALSE);
  }
  *engine_data= commit_count;
  *engine_callback= ndbcluster_cache_retrieval_allowed;
5986
  DBUG_PRINT("exit", ("commit_count: %s", llstr(commit_count, buff)));
5987
  DBUG_RETURN(commit_count > 0);
5988
}
5989

mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5990

5991
/*
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5992
  Handling the shared NDB_SHARE structure that is needed to
5993 5994 5995 5996 5997 5998 5999
  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,
6000
                                my_bool not_used __attribute__((unused)))
6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028
{
  *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);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6029
      share->commit_count= 0;
6030 6031 6032 6033 6034 6035
      share->commit_count_lock= 0;
    }
    else
    {
      DBUG_PRINT("error", ("Failed to alloc share"));
      pthread_mutex_unlock(&ndbcluster_mutex);
6036 6037
      sql_print_error("get_share: my_malloc(%u) failed",
                      (unsigned int)(sizeof(*share)+length+1));
6038
      return 0;
6039 6040 6041
    }
  }
  share->use_count++;
6042 6043

  DBUG_PRINT("share",
6044
	     ("table_name: %s  length: %d  use_count: %d  commit_count: %lu",
6045
	      share->table_name, share->table_name_length, share->use_count,
6046
	      (ulong) share->commit_count));
6047 6048 6049 6050 6051 6052 6053 6054 6055 6056
  pthread_mutex_unlock(&ndbcluster_mutex);
  return share;
}


static void free_share(NDB_SHARE *share)
{
  pthread_mutex_lock(&ndbcluster_mutex);
  if (!--share->use_count)
  {
6057
     hash_delete(&ndbcluster_open_tables, (byte*) share);
6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085
    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, 
6086
                   const void **pack_data, uint *pack_len)
6087 6088 6089 6090 6091 6092
{
  int error;
  ulong org_len, comp_len;
  uint blob_len;
  frm_blob_struct* blob;
  DBUG_ENTER("packfrm");
6093
  DBUG_PRINT("enter", ("data: 0x%lx, len: %d", (long) data, len));
6094 6095
  
  error= 1;
6096
  org_len= len;
6097
  if (my_compress((byte*)data, &org_len, &comp_len))
6098 6099 6100
  {
    sql_print_error("packfrm: my_compress(org_len: %u)",
                    (unsigned int)org_len);
6101
    goto err;
6102 6103
  }

6104
  DBUG_PRINT("info", ("org_len: %lu  comp_len: %lu", org_len, comp_len));
6105 6106 6107 6108 6109
  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))))
6110 6111
  {
    sql_print_error("packfrm: my_malloc(%u)", blob_len);
6112
    goto err;
6113
  }
6114 6115 6116 6117 6118 6119 6120 6121
  // 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);  
  
6122 6123 6124
  *pack_data= blob;
  *pack_len= blob_len;
  error= 0;
6125
  
6126
  DBUG_PRINT("exit", ("pack_data: 0x%lx, pack_len: %d", (long) *pack_data, *pack_len));
6127 6128 6129 6130 6131 6132 6133
err:
  DBUG_RETURN(error);
  
}


static int unpackfrm(const void **unpack_data, uint *unpack_len,
6134
                    const void *pack_data)
6135
{
6136
   const frm_blob_struct *blob= (frm_blob_struct*)pack_data;
6137 6138 6139
   byte *data;
   ulong complen, orglen, ver;
   DBUG_ENTER("unpackfrm");
6140
   DBUG_PRINT("enter", ("pack_data: 0x%lx", (long) pack_data));
6141

6142 6143 6144
   complen=     uint4korr((char*)&blob->head.complen);
   orglen=      uint4korr((char*)&blob->head.orglen);
   ver=         uint4korr((char*)&blob->head.ver);
6145
 
6146
   DBUG_PRINT("blob",("ver: %lu  complen: %lu  orglen: %lu",
6147
                     ver,complen,orglen));
6148 6149 6150
   DBUG_DUMP("blob->data", (char*) blob->data, complen);
 
   if (ver != 1)
6151 6152
   {
     sql_print_error("unpackfrm: ver != 1");
6153
     DBUG_RETURN(1);
6154
   }
6155
   if (!(data= my_malloc(max(orglen, complen), MYF(MY_WME))))
6156 6157 6158 6159 6160
   {
     sql_print_error("unpackfrm: my_malloc(%u)",
                     (unsigned int)max(orglen, complen));
     DBUG_RETURN(HA_ERR_OUT_OF_MEM);
   }
6161 6162 6163 6164 6165
   memcpy(data, blob->data, complen);
 
   if (my_uncompress(data, &complen, &orglen))
   {
     my_free((char*)data, MYF(0));
6166 6167
     sql_print_error("unpackfrm: my_uncompress(complen: %u, orglen: %u)",
                     (unsigned int)complen, (unsigned int)orglen);
6168 6169 6170
     DBUG_RETURN(3);
   }

6171 6172
   *unpack_data= data;
   *unpack_len= complen;
6173

6174
   DBUG_PRINT("exit", ("frmdata: 0x%lx, len: %d", (long) *unpack_data, *unpack_len));
6175 6176 6177

   DBUG_RETURN(0);
}
6178 6179 6180

static 
int
6181
ndb_get_table_statistics(ha_ndbcluster* file, bool report_error, Ndb* ndb,
stewart@willster.(none)'s avatar
stewart@willster.(none) committed
6182
                         const char* table,
6183
                         struct Ndb_statistics * ndbstat)
6184
{
6185
  NdbTransaction* pTrans;
6186
  NdbError error;
6187
  int retries= 10;
6188
  int reterr= 0;
6189
  int retry_sleep= 30 * 1000; /* 30 milliseconds */
6190
#ifndef DBUG_OFF
6191
  char buff[22], buff2[22], buff3[22], buff4[22];
6192
#endif
6193 6194
  DBUG_ENTER("ndb_get_table_statistics");
  DBUG_PRINT("enter", ("table: %s", table));
6195 6196

  do
6197
  {
6198 6199
    Uint64 rows, commits, mem;
    Uint32 size;
6200
    Uint32 count= 0;
6201 6202
    Uint64 sum_rows= 0;
    Uint64 sum_commits= 0;
6203 6204
    Uint64 sum_row_size= 0;
    Uint64 sum_mem= 0;
6205 6206 6207 6208
    NdbScanOperation*pOp;
    int check;

    if ((pTrans= ndb->startTransaction()) == NULL)
6209
    {
6210 6211 6212
      error= ndb->getNdbError();
      goto retry;
    }
6213
      
6214 6215 6216 6217
    if ((pOp= pTrans->getNdbScanOperation(table)) == NULL)
    {
      error= pTrans->getNdbError();
      goto retry;
6218
    }
6219
    
6220
    if (pOp->readTuples(NdbOperation::LM_CommittedRead))
6221 6222 6223 6224
    {
      error= pOp->getNdbError();
      goto retry;
    }
6225
    
6226 6227 6228 6229 6230
    if (pOp->interpret_exit_last_row() == -1)
    {
      error= pOp->getNdbError();
      goto retry;
    }
6231 6232 6233
    
    pOp->getValue(NdbDictionary::Column::ROW_COUNT, (char*)&rows);
    pOp->getValue(NdbDictionary::Column::COMMIT_COUNT, (char*)&commits);
6234 6235
    pOp->getValue(NdbDictionary::Column::ROW_SIZE, (char*)&size);
    pOp->getValue(NdbDictionary::Column::FRAGMENT_MEMORY, (char*)&mem);
6236
    
6237 6238 6239
    if (pTrans->execute(NdbTransaction::NoCommit,
                        NdbTransaction::AbortOnError,
                        TRUE) == -1)
6240
    {
6241 6242
      error= pTrans->getNdbError();
      goto retry;
6243
    }
6244
    
monty@mishka.local's avatar
monty@mishka.local committed
6245
    while ((check= pOp->nextResult(TRUE, TRUE)) == 0)
6246 6247 6248
    {
      sum_rows+= rows;
      sum_commits+= commits;
6249
      if (sum_row_size < size)
6250
        sum_row_size= size;
6251
      sum_mem+= mem;
6252
      count++;
6253 6254 6255
    }
    
    if (check == -1)
6256 6257 6258 6259
    {
      error= pOp->getNdbError();
      goto retry;
    }
6260

6261
    pOp->close(TRUE);
6262

6263
    ndb->closeTransaction(pTrans);
6264 6265 6266 6267 6268 6269

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

6270 6271 6272 6273 6274 6275 6276
    DBUG_PRINT("exit", ("records: %s  commits: %s "
                        "row_size: %s  mem: %s count: %u",
			llstr(sum_rows, buff),
                        llstr(sum_commits, buff2),
                        llstr(sum_row_size, buff3),
                        llstr(sum_mem, buff4),
                        count));
6277

6278
    DBUG_RETURN(0);
6279
retry:
6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294
    if(report_error)
    {
      if (file && pTrans)
      {
        reterr= file->ndb_err(pTrans);
      }
      else
      {
        const NdbError& tmp= error;
        ERR_PRINT(tmp);
        reterr= ndb_to_mysql_error(&tmp);
      }
    }
    else
      reterr= error.code;
6295

6296
    if (pTrans)
6297 6298 6299 6300 6301 6302 6303 6304 6305 6306
    {
      ndb->closeTransaction(pTrans);
      pTrans= NULL;
    }
    if (error.status == NdbError::TemporaryError && retries--)
    {
      my_sleep(retry_sleep);
      continue;
    }
    break;
6307
  } while(1);
6308 6309 6310
  DBUG_PRINT("exit", ("failed, reterr: %u, NdbError %u(%s)", reterr,
                      error.code, error.message));
  DBUG_RETURN(reterr);
6311 6312
}

6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327
/*
  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, 
6328
                 mysql_data_home,"/",m_dbname,"/",m_tabname,ha_ndb_ext,NullS);
6329 6330 6331 6332 6333 6334 6335 6336 6337 6338

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

6339
void 
6340 6341
ha_ndbcluster::release_completed_operations(NdbTransaction *trans,
					    bool force_release)
6342 6343 6344 6345 6346 6347 6348 6349
{
  if (trans->hasBlobOperation())
  {
    /* We are reading/writing BLOB fields, 
       releasing operation records is unsafe
    */
    return;
  }
6350 6351 6352 6353 6354 6355 6356 6357 6358 6359
  if (!force_release)
  {
    if (get_thd_ndb(current_thd)->query_state & NDB_QUERY_MULTI_READ_RANGE)
    {
      /* We are batching reads and have not consumed all fetched
	 rows yet, releasing operation records is unsafe 
      */
      return;
    }
  }
6360
  trans->releaseCompletedOperations();
6361 6362
}

6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386
bool 
ha_ndbcluster::null_value_index_search(KEY_MULTI_RANGE *ranges,
				       KEY_MULTI_RANGE *end_range,
				       HANDLER_BUFFER *buffer)
{
  DBUG_ENTER("null_value_index_search");
  KEY* key_info= table->key_info + active_index;
  KEY_MULTI_RANGE *range= ranges;
  ulong reclength= table->s->reclength;
  byte *curr= (byte*)buffer->buffer;
  byte *end_of_buffer= (byte*)buffer->buffer_end;
  
  for (; range<end_range && curr+reclength <= end_of_buffer; 
       range++)
  {
    const byte *key= range->start_key.key;
    uint key_len= range->start_key.length;
    if (check_null_in_key(key_info, key, key_len))
      DBUG_RETURN(true);
    curr += reclength;
  }
  DBUG_RETURN(false);
}

6387
int
6388
ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
6389 6390 6391 6392
                                      KEY_MULTI_RANGE *ranges, 
                                      uint range_count,
                                      bool sorted, 
                                      HANDLER_BUFFER *buffer)
6393 6394 6395
{
  int res;
  KEY* key_info= table->key_info + active_index;
6396
  NDB_INDEX_TYPE cur_index_type= get_index_type(active_index);
joreland@mysql.com's avatar
merge  
joreland@mysql.com committed
6397
  ulong reclength= table->s->reclength;
6398
  NdbOperation* op;
6399
  Thd_ndb *thd_ndb= get_thd_ndb(current_thd);
6400
  DBUG_ENTER("ha_ndbcluster::read_multi_range_first");
6401

6402 6403 6404 6405
  /**
   * blobs and unique hash index with NULL can't be batched currently
   */
  if (uses_blob_value(m_retrieve_all_fields) ||
6406
      (cur_index_type == UNIQUE_INDEX &&
6407 6408
       has_null_in_unique_index(active_index) &&
       null_value_index_search(ranges, ranges+range_count, buffer)))
6409
  {
6410
    m_disable_multi_read= TRUE;
6411
    DBUG_RETURN(handler::read_multi_range_first(found_range_p, 
6412 6413 6414 6415
                                                ranges, 
                                                range_count,
                                                sorted, 
                                                buffer));
6416
  }
6417
  thd_ndb->query_state|= NDB_QUERY_MULTI_READ_RANGE;
6418
  m_disable_multi_read= FALSE;
6419 6420 6421 6422

  /**
   * Copy arguments into member variables
   */
6423 6424 6425
  m_multi_ranges= ranges;
  multi_range_curr= ranges;
  multi_range_end= ranges+range_count;
6426 6427 6428
  multi_range_sorted= sorted;
  multi_range_buffer= buffer;

6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439
  /**
   * 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
6440 6441
   */   

mskold@mysql.com's avatar
mskold@mysql.com committed
6442
  /**
6443 6444
   * Variables for loop
   */
6445 6446
  byte *curr= (byte*)buffer->buffer;
  byte *end_of_buffer= (byte*)buffer->buffer_end;
6447 6448
  NdbOperation::LockMode lm= 
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
6449
  bool need_pk = (lm == NdbOperation::LM_Read);
6450 6451
  const NDBTAB *tab= (const NDBTAB *) m_table;
  const NDBINDEX *unique_idx= (NDBINDEX *) m_index[active_index].unique_index;
6452
  const NDBINDEX *idx= (NDBINDEX *) m_index[active_index].index; 
6453 6454
  const NdbOperation* lastOp= m_active_trans->getLastDefinedOperation();
  NdbIndexScanOperation* scanOp= 0;
6455 6456
  for (; multi_range_curr<multi_range_end && curr+reclength <= end_of_buffer; 
       multi_range_curr++)
6457
  {
6458
    switch (cur_index_type) {
6459 6460 6461 6462 6463
    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 */
6464
    case PRIMARY_KEY_INDEX:
6465
      multi_range_curr->range_flag |= UNIQUE_RANGE;
6466
      if ((op= m_active_trans->getNdbOperation(tab)) && 
6467 6468 6469
          !op->readTuple(lm) && 
          !set_primary_key(op, multi_range_curr->start_key.key) &&
          !define_read_attrs(curr, op) &&
6470
          (op->setAbortOption(AO_IgnoreError), TRUE))
6471
        curr += reclength;
6472
      else
6473
        ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
6474
      break;
6475 6476 6477 6478 6479 6480 6481
    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 */
6482
    case UNIQUE_INDEX:
6483
      multi_range_curr->range_flag |= UNIQUE_RANGE;
6484
      if ((op= m_active_trans->getNdbIndexOperation(unique_idx, tab)) && 
6485 6486 6487 6488 6489
	  !op->readTuple(lm) && 
	  !set_index_key(op, key_info, multi_range_curr->start_key.key) &&
	  !define_read_attrs(curr, op) &&
	  (op->setAbortOption(AO_IgnoreError), TRUE))
	curr += reclength;
6490
      else
6491
	ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
6492
      break;
6493 6494
    case ORDERED_INDEX:
    {
6495
  range:
6496
      multi_range_curr->range_flag &= ~(uint)UNIQUE_RANGE;
6497 6498
      if (scanOp == 0)
      {
6499 6500 6501 6502 6503 6504
        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));
6505
          if (scanOp->reset_bounds(m_force_send))
6506 6507 6508 6509 6510
            DBUG_RETURN(ndb_err(m_active_trans));
          
          end_of_buffer -= reclength;
        }
        else if ((scanOp= m_active_trans->getNdbIndexScanOperation(idx, tab)) 
6511
                 &&!scanOp->readTuples(lm, 0, parallelism, sorted, 
6512
				       FALSE, TRUE, need_pk, TRUE)
6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523
                 &&!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());
        }
6524
      }
6525

6526
      const key_range *keys[2]= { &multi_range_curr->start_key, 
6527
                                  &multi_range_curr->end_key };
6528
      if ((res= set_bounds(scanOp, keys, multi_range_curr-ranges)))
6529
        DBUG_RETURN(res);
6530
      break;
6531
    }
6532
    case UNDEFINED_INDEX:
mskold@mysql.com's avatar
mskold@mysql.com committed
6533 6534 6535 6536
      DBUG_ASSERT(FALSE);
      DBUG_RETURN(1);
      break;
    }
6537 6538
  }
  
6539
  if (multi_range_curr != multi_range_end)
6540
  {
6541 6542 6543 6544 6545 6546
    /**
     * 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
     */
6547
    buffer->end_of_used_area= (byte*)buffer->buffer_end;
6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558
  }
  else
  {
    buffer->end_of_used_area= curr;
  }
  
  /**
   * Set first operation in multi range
   */
  m_current_multi_operation= 
    lastOp ? lastOp->next() : m_active_trans->getFirstDefinedOperation();
6559
  if (!(res= execute_no_commit_ie(this, m_active_trans, true)))
6560
  {
6561 6562
    m_multi_range_defined= multi_range_curr;
    multi_range_curr= ranges;
6563 6564
    m_multi_range_result_ptr= (byte*)buffer->buffer;
    DBUG_RETURN(read_multi_range_next(found_range_p));
6565 6566 6567 6568
  }
  ERR_RETURN(m_active_trans->getNdbError());
}

6569 6570 6571 6572 6573 6574
#if 0
#define DBUG_MULTI_RANGE(x) printf("read_multi_range_next: case %d\n", x);
#else
#define DBUG_MULTI_RANGE(x)
#endif

6575
int
6576
ha_ndbcluster::read_multi_range_next(KEY_MULTI_RANGE ** multi_range_found_p)
6577 6578
{
  DBUG_ENTER("ha_ndbcluster::read_multi_range_next");
6579
  if (m_disable_multi_read)
6580
  {
6581
    DBUG_RETURN(handler::read_multi_range_next(multi_range_found_p));
6582
  }
6583
  
6584
  int res;
6585
  int range_no;
joreland@mysql.com's avatar
merge  
joreland@mysql.com committed
6586
  ulong reclength= table->s->reclength;
6587
  const NdbOperation* op= m_current_multi_operation;
6588
  for (;multi_range_curr < m_multi_range_defined; multi_range_curr++)
6589
  {
6590
    if (multi_range_curr->range_flag & UNIQUE_RANGE)
6591
    {
6592
      if (op->getNdbError().code == 0)
6593
        goto found_next;
6594 6595 6596
      
      op= m_active_trans->getNextCompletedOperation(op);
      m_multi_range_result_ptr += reclength;
6597
      continue;
6598
    } 
6599
    else if (m_multi_cursor && !multi_range_sorted)
6600
    {
6601 6602
      DBUG_MULTI_RANGE(1);
      if ((res= fetch_next(m_multi_cursor)) == 0)
6603
      {
6604 6605 6606
        DBUG_MULTI_RANGE(2);
        range_no= m_multi_cursor->get_range_no();
        goto found;
6607 6608 6609
      } 
      else
      {
6610
        goto close_scan;
6611 6612
      }
    }
6613
    else if (m_multi_cursor && multi_range_sorted)
6614
    {
6615 6616
      if (m_active_cursor && (res= fetch_next(m_multi_cursor)))
      {
6617 6618
        DBUG_MULTI_RANGE(3);
        goto close_scan;
6619
      }
6620
      
6621
      range_no= m_multi_cursor->get_range_no();
6622
      uint current_range_no= multi_range_curr - m_multi_ranges;
mskold@mysql.com's avatar
mskold@mysql.com committed
6623
      if ((uint) range_no == current_range_no)
6624
      {
6625
        DBUG_MULTI_RANGE(4);
6626
        // return current row
6627
        goto found;
6628
      }
6629
      else if (range_no > (int)current_range_no)
6630
      {
6631 6632 6633 6634
        DBUG_MULTI_RANGE(5);
        // wait with current row
        m_active_cursor= 0;
        continue;
6635 6636 6637
      }
      else 
      {
6638 6639 6640
        DBUG_MULTI_RANGE(6);
        // First fetch from cursor
        DBUG_ASSERT(range_no == -1);
6641
        if ((res= m_multi_cursor->nextResult(true)))
6642 6643 6644 6645 6646
        {
          goto close_scan;
        }
        multi_range_curr--; // Will be increased in for-loop
        continue;
6647
      }
6648
    }
6649
    else /** m_multi_cursor == 0 */
6650
    {
6651
      DBUG_MULTI_RANGE(7);
6652 6653 6654 6655
      /**
       * Corresponds to range 5 in example in read_multi_range_first
       */
      (void)1;
6656
      continue;
6657
    }
6658
    
6659
    DBUG_ASSERT(FALSE); // Should only get here via goto's
6660 6661 6662
close_scan:
    if (res == 1)
    {
6663
      m_multi_cursor->close(FALSE, TRUE);
6664
      m_active_cursor= m_multi_cursor= 0;
6665
      DBUG_MULTI_RANGE(8);
6666 6667 6668 6669 6670 6671 6672
      continue;
    } 
    else 
    {
      DBUG_RETURN(ndb_err(m_active_trans));
    }
  }
6673
  
6674
  if (multi_range_curr == multi_range_end)
6675 6676 6677
  {
    Thd_ndb *thd_ndb= get_thd_ndb(current_thd);
    thd_ndb->query_state&= NDB_QUERY_NORMAL;
6678
    DBUG_RETURN(HA_ERR_END_OF_FILE);
6679
  }
6680
  
6681 6682 6683 6684
  /**
   * Read remaining ranges
   */
  DBUG_RETURN(read_multi_range_first(multi_range_found_p, 
6685 6686 6687 6688
                                     multi_range_curr,
                                     multi_range_end - multi_range_curr, 
                                     multi_range_sorted,
                                     multi_range_buffer));
6689 6690
  
found:
6691 6692 6693
  /**
   * Found a record belonging to a scan
   */
6694
  m_active_cursor= m_multi_cursor;
6695
  * multi_range_found_p= m_multi_ranges + range_no;
6696 6697
  memcpy(table->record[0], m_multi_range_cursor_result_ptr, reclength);
  setup_recattr(m_active_cursor->getFirstRecAttr());
6698 6699 6700
  unpack_record(table->record[0]);
  table->status= 0;     
  DBUG_RETURN(0);
6701
  
6702
found_next:
6703 6704 6705 6706
  /**
   * Found a record belonging to a pk/index op,
   *   copy result and move to next to prepare for next call
   */
6707
  * multi_range_found_p= multi_range_curr;
6708
  memcpy(table->record[0], m_multi_range_result_ptr, reclength);
6709
  setup_recattr(op->getFirstRecAttr());
6710
  unpack_record(table->record[0]);
6711 6712
  table->status= 0;
  
6713
  multi_range_curr++;
6714
  m_current_multi_operation= m_active_trans->getNextCompletedOperation(op);
6715 6716
  m_multi_range_result_ptr += reclength;
  DBUG_RETURN(0);
6717 6718
}

6719 6720 6721 6722 6723 6724 6725 6726
int
ha_ndbcluster::setup_recattr(const NdbRecAttr* curr)
{
  DBUG_ENTER("setup_recattr");

  Field **field, **end;
  NdbValue *value= m_value;
  
joreland@mysql.com's avatar
merge  
joreland@mysql.com committed
6727
  end= table->field + table->s->fields;
6728 6729 6730 6731 6732 6733
  
  for (field= table->field; field < end; field++, value++)
  {
    if ((* value).ptr)
    {
      DBUG_ASSERT(curr != 0);
6734 6735 6736
      NdbValue* val= m_value + curr->getColumn()->getColumnNo();
      DBUG_ASSERT(val->ptr);
      val->rec= curr;
6737
      curr= curr->next();
6738 6739 6740
    }
  }
  
6741
  DBUG_RETURN(0);
6742 6743
}

mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6744 6745
char*
ha_ndbcluster::update_table_comment(
6746 6747
                                /* out: table comment + additional */
        const char*     comment)/* in:  table comment defined by user */
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6748 6749
{
  uint length= strlen(comment);
6750
  if (length > 64000 - 3)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6751 6752 6753 6754 6755 6756 6757 6758 6759 6760
  {
    return((char*)comment); /* string too long */
  }

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

6761 6762 6763 6764
  if (ndb->setDatabaseName(m_dbname))
  {
    return((char*)comment);
  }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776
  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)
  {
6777 6778
    sql_print_error("ha_ndbcluster::update_table_comment: "
                    "my_malloc(%u) failed", (unsigned int)fmt_len_plus_extra);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6779 6780 6781
    return (char*)comment;
  }

6782 6783 6784
  my_snprintf(str,fmt_len_plus_extra,fmt,comment,
              length > 0 ? " ":"",
              tab->getReplicaCount());
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6785 6786 6787 6788 6789
  return str;
}


// Utility thread main loop
6790
pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6791 6792
{
  THD *thd; /* needs to be first for thread_stack */
6793
  Ndb* ndb;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6794 6795 6796 6797
  struct timespec abstime;

  my_thread_init();
  DBUG_ENTER("ndb_util_thread");
6798
  DBUG_PRINT("enter", ("ndb_cache_check_time: %lu", ndb_cache_check_time));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6799 6800

  thd= new THD; /* note that contructor of THD uses DBUG_ */
6801 6802 6803 6804 6805
  if (thd == NULL)
  {
    my_errno= HA_ERR_OUT_OF_MEM;
    DBUG_RETURN(NULL);
  }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6806
  THD_CHECK_SENTRY(thd);
6807
  ndb= new Ndb(g_ndb_cluster_connection, "");
6808 6809 6810 6811 6812 6813
  if (ndb == NULL)
  {
    thd->cleanup();
    delete thd;
    DBUG_RETURN(NULL);
  }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6814 6815 6816 6817
  pthread_detach_this_thread();
  ndb_util_thread= pthread_self();

  thd->thread_stack= (char*)&thd; /* remember where our stack is */
6818
  if (thd->store_globals() || (ndb->init() != 0))
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6819 6820 6821
  {
    thd->cleanup();
    delete thd;
6822
    delete ndb;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6823 6824 6825
    DBUG_RETURN(NULL);
  }

6826 6827
  uint share_list_size= 0;
  NDB_SHARE **share_list= NULL;
6828
  set_timespec(abstime, 0);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6829 6830 6831
  for (;;)
  {

6832 6833 6834
    if (abort_loop)
      break; /* Shutting down server */

mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6835
    pthread_mutex_lock(&LOCK_ndb_util_thread);
monty@mysql.com's avatar
monty@mysql.com committed
6836 6837 6838
    pthread_cond_timedwait(&COND_ndb_util_thread,
                           &LOCK_ndb_util_thread,
                           &abstime);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6839 6840
    pthread_mutex_unlock(&LOCK_ndb_util_thread);

6841
    DBUG_PRINT("ndb_util_thread", ("Started, ndb_cache_check_time: %lu",
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6842 6843 6844 6845 6846 6847 6848
                                   ndb_cache_check_time));

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

    if (ndb_cache_check_time == 0)
    {
6849 6850
      /* Wake up in 1 second to check if value has changed */
      set_timespec(abstime, 1);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6851 6852 6853 6854 6855 6856
      continue;
    }

    /* Lock mutex and fill list with pointers to all open tables */
    NDB_SHARE *share;
    pthread_mutex_lock(&ndbcluster_mutex);
6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872
    uint i, record_count= ndbcluster_open_tables.records;
    if (share_list_size < record_count)
    {
      NDB_SHARE ** new_share_list= new NDB_SHARE * [record_count];
      if (!new_share_list)
      {
        sql_print_warning("ndb util thread: malloc failure, "
                          "query cache not maintained properly");
        pthread_mutex_unlock(&ndbcluster_mutex);
        goto next;                               // At least do not crash
      }
      delete [] share_list;
      share_list_size= record_count;
      share_list= new_share_list;
    }
    for (i= 0; i < record_count; i++)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6873 6874 6875 6876 6877 6878 6879 6880
    {
      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 */
6881
      share_list[i]= share;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6882 6883 6884
    }
    pthread_mutex_unlock(&ndbcluster_mutex);

6885 6886
    /* Iterate through the open files list */
    for (i= 0; i < record_count; i++)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6887
    {
6888
      share= share_list[i];
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6889 6890 6891 6892 6893 6894 6895 6896 6897
      /* 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",
6898 6899
                 ("Fetching commit count for: %s",
                  share->table_name));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6900 6901

      /* Contact NDB to get commit count for table */
6902 6903 6904 6905 6906
      struct Ndb_statistics stat;
      uint lock;
      pthread_mutex_lock(&share->mutex);
      lock= share->commit_count_lock;
      pthread_mutex_unlock(&share->mutex);
6907 6908 6909 6910
      if (ndb->setDatabaseName(db))
      {
        goto loop_next;
      }
stewart@willster.(none)'s avatar
stewart@willster.(none) committed
6911
      if (ndb_get_table_statistics(NULL, false, ndb, tabname, &stat) == 0)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6912
      {
6913
#ifndef DBUG_OFF
6914
        char buff[22], buff2[22];
6915
#endif
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6916
        DBUG_PRINT("ndb_util_thread",
6917 6918 6919
                   ("Table: %s  commit_count: %s  rows: %s",
                    share->table_name,
                    llstr(stat.commit_count, buff),
monty@mysql.com's avatar
monty@mysql.com committed
6920
                    llstr(stat.row_count, buff2)));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6921 6922 6923 6924 6925 6926
      }
      else
      {
        DBUG_PRINT("ndb_util_thread",
                   ("Error: Could not get commit count for table %s",
                    share->table_name));
6927
        stat.commit_count= 0;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6928
      }
6929
  loop_next:
6930 6931 6932 6933 6934
      pthread_mutex_lock(&share->mutex);
      if (share->commit_count_lock == lock)
        share->commit_count= stat.commit_count;
      pthread_mutex_unlock(&share->mutex);

mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6935 6936 6937
      /* Decrease the use count and possibly free share */
      free_share(share);
    }
6938
next:
6939 6940 6941 6942 6943 6944 6945 6946 6947
    /* 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;

6948
    if (msecs >= 1000){
6949 6950 6951 6952 6953 6954 6955 6956 6957 6958
      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;
    }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6959 6960
  }

6961 6962
  if (share_list)
    delete [] share_list;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6963 6964
  thd->cleanup();
  delete thd;
6965
  delete ndb;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6966 6967 6968 6969 6970 6971
  DBUG_PRINT("exit", ("ndb_util_thread"));
  my_thread_end();
  pthread_exit(0);
  DBUG_RETURN(NULL);
}

6972 6973 6974
/*
  Condition pushdown
*/
6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991
/*
  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)
*/
6992 6993 6994 6995 6996
const 
COND* 
ha_ndbcluster::cond_push(const COND *cond) 
{ 
  DBUG_ENTER("cond_push");
6997
  Ndb_cond_stack *ndb_cond = new Ndb_cond_stack();
6998 6999 7000 7001 7002
  if (ndb_cond == NULL)
  {
    my_errno= HA_ERR_OUT_OF_MEM;
    DBUG_RETURN(NULL);
  }
7003 7004
  DBUG_EXECUTE("where",print_where((COND *)cond, m_tabname););
  if (m_cond_stack)
mskold@mysql.com's avatar
mskold@mysql.com committed
7005
    ndb_cond->next= m_cond_stack;
7006 7007 7008 7009 7010 7011
  else
    ndb_cond->next= NULL;
  m_cond_stack= ndb_cond;
  
  if (serialize_cond(cond, ndb_cond))
  {
mskold@mysql.com's avatar
mskold@mysql.com committed
7012
    DBUG_RETURN(NULL);
7013 7014 7015 7016
  }
  else
  {
    cond_pop();
mskold@mysql.com's avatar
mskold@mysql.com committed
7017
  }
7018 7019 7020
  DBUG_RETURN(cond); 
}

7021 7022 7023
/*
  Pop the top condition from the condition stack of the handler instance.
*/
7024 7025 7026 7027 7028 7029 7030 7031 7032
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;
  }
mskold@mysql.com's avatar
mskold@mysql.com committed
7033
}
7034

7035 7036 7037
/*
  Clear the condition stack
*/
7038 7039 7040 7041 7042 7043 7044 7045 7046 7047
void
ha_ndbcluster::cond_clear()
{
  DBUG_ENTER("cond_clear");
  while (m_cond_stack)
    cond_pop();

  DBUG_VOID_RETURN;
}

7048 7049 7050 7051 7052 7053
/*
  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.
*/
7054 7055 7056 7057 7058
void ndb_serialize_cond(const Item *item, void *arg)
{
  Ndb_cond_traverse_context *context= (Ndb_cond_traverse_context *) arg;
  DBUG_ENTER("ndb_serialize_cond");  

mskold@mysql.com's avatar
mskold@mysql.com committed
7059 7060 7061 7062 7063
  // 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--;
7064 7065 7066
    switch (item->type()) {
    case Item::FUNC_ITEM:
    {
mskold@mysql.com's avatar
mskold@mysql.com committed
7067 7068 7069 7070
      Item_func *func_item= (Item_func *) item;
      context->skip+= func_item->argument_count();
      break;
    }
7071 7072 7073 7074 7075
    case Item::INT_ITEM:
    case Item::REAL_ITEM:
    case Item::STRING_ITEM:
    case Item::VARBIN_ITEM:
    case Item::DECIMAL_ITEM:
mskold@mysql.com's avatar
mskold@mysql.com committed
7076 7077
      break;
    default:
7078
      context->supported= FALSE;
mskold@mysql.com's avatar
mskold@mysql.com committed
7079 7080
      break;
    }
7081
    
mskold@mysql.com's avatar
mskold@mysql.com committed
7082 7083 7084
    DBUG_VOID_RETURN;
  }
  
7085
  if (context->supported)
7086
  {
7087 7088
    Ndb_rewrite_context *rewrite_context2= context->rewrite_stack;
    const Item_func *rewrite_func_item;
7089
    // Check if we are rewriting some unsupported function call
7090 7091 7092
    if (rewrite_context2 &&
        (rewrite_func_item= rewrite_context2->func_item) &&
        rewrite_context2->count++ == 0)
mskold@mysql.com's avatar
mskold@mysql.com committed
7093
    {
7094
      switch (rewrite_func_item->functype()) {
7095
      case Item_func::BETWEEN:
7096
        /*
7097 7098 7099 7100 7101 7102 7103
          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()
7104
        */
7105 7106
      case Item_func::IN_FUNC:
      {
7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120
        /*
          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
7121
          rewrite_context2->left_hand_item= item;
7122
          if (item->type() == Item::FUNC_ITEM)
7123
          {
7124 7125 7126
            Item_func *func_item= (Item_func *) item;
            if (func_item->functype() == Item_func::UNKNOWN_FUNC &&
                func_item->const_item())
7127
            {
7128 7129 7130
              // Skip any arguments since we will evaluate function instead
              DBUG_PRINT("info", ("Skip until end of arguments marker"));
              context->skip= func_item->argument_count();
7131 7132 7133
            }
            else
            {
7134 7135 7136 7137
              DBUG_PRINT("info", ("Found unsupported functional expression in BETWEEN|IN"));
              context->supported= FALSE;
              DBUG_VOID_RETURN;
              
7138 7139 7140
            }
          }
        }
7141 7142
        else
        {
7143 7144 7145
          // Non-supported BETWEEN|IN expression
          DBUG_PRINT("info", ("Found unexpected item of type %u in BETWEEN|IN",
                              item->type()));
7146
          context->supported= FALSE;
7147
          DBUG_VOID_RETURN;
7148
        }
7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165 7166 7167 7168 7169 7170
        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;
7171 7172 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182
        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()
          */
7183 7184 7185 7186 7187 7188 7189 7190 7191 7192 7193 7194 7195 7196 7197 7198 7199 7200 7201
          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;
          }
7202 7203
          break;
        }
7204 7205
        case Item_func::IN_FUNC:
        {
7206 7207 7208 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218
          /*
            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);
7219 7220
          break;
        }
7221 7222
        default:
          context->supported= FALSE;
7223
        }
7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253
        // 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
7254 7255 7256 7257
      {
        switch (item->type()) {
        case Item::FIELD_ITEM:
        {
7258 7259 7260 7261 7262 7263 7264
          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.
          */
7265
          if (context->table->s == field->table->s)
7266 7267 7268 7269 7270
          {       
            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));
7271
            DBUG_PRINT("info", ("type %d", field->type()));
7272 7273 7274 7275 7276
            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) &&
7277
                context->expecting_field_type(field->type()) &&
7278
                (context->expecting_field_result(field->result_type()) ||
mskold@mysql.com's avatar
mskold@mysql.com committed
7279
                 // Date and year can be written as string or int
7280 7281 7282 7283
                 ((type == MYSQL_TYPE_TIME ||
                   type == MYSQL_TYPE_DATE || 
                   type == MYSQL_TYPE_YEAR ||
                   type == MYSQL_TYPE_DATETIME)
mskold@mysql.com's avatar
mskold@mysql.com committed
7284 7285 7286
                  ? (context->expecting_field_result(STRING_RESULT) ||
                     context->expecting_field_result(INT_RESULT))
                  : true)) &&
7287
                // Bit fields no yet supported in scan filter
7288 7289 7290
                type != MYSQL_TYPE_BIT &&
                // No BLOB support in scan filter
                type != MYSQL_TYPE_TINY_BLOB &&
7291 7292
                type != MYSQL_TYPE_MEDIUM_BLOB &&
                type != MYSQL_TYPE_LONG_BLOB &&
7293
                type != MYSQL_TYPE_BLOB)
7294 7295 7296 7297 7298 7299
            {
              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();
7300
              if (! context->expecting_nothing())
7301
              {
7302 7303 7304 7305 7306 7307 7308 7309 7310 7311
                // 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
7312 7313
                  switch (field->result_type()) {
                  case STRING_RESULT:
7314 7315 7316 7317 7318
                    // 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;
7319
                  case REAL_RESULT:
7320 7321
                    context->expect_only(Item::REAL_ITEM);
                    context->expect(Item::DECIMAL_ITEM);
7322
                    context->expect(Item::INT_ITEM);
7323
                    break;
7324
                  case INT_RESULT:
7325 7326 7327
                    context->expect_only(Item::INT_ITEM);
                    context->expect(Item::VARBIN_ITEM);
                    break;
7328
                  case DECIMAL_RESULT:
7329 7330
                    context->expect_only(Item::DECIMAL_ITEM);
                    context->expect(Item::REAL_ITEM);
7331
                    context->expect(Item::INT_ITEM);
7332 7333 7334 7335
                    break;
                  default:
                    break;
                  }    
7336 7337
              }
              else
7338 7339 7340 7341
              {
                // Expect another logical expression
                context->expect_only(Item::FUNC_ITEM);
                context->expect(Item::COND_ITEM);
7342 7343 7344 7345 7346 7347 7348
                // 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)
7349
                {
mskold@mysql.com's avatar
mskold@mysql.com committed
7350
                  DBUG_PRINT("info", ("Found non-matching collation %s",  
7351 7352
                                      item->collation.collation->name)); 
                  context->supported= FALSE;                
7353 7354
                }
              }
7355 7356
              break;
            }
7357 7358
            else
            {
mskold@mysql.com's avatar
mskold@mysql.com committed
7359 7360
              DBUG_PRINT("info", ("Was not expecting field of type %u(%u)",
                                  field->result_type(), type));
7361
              context->supported= FALSE;
7362
            }
7363
          }
7364
          else
7365 7366 7367 7368
          {
            DBUG_PRINT("info", ("Was not expecting field from table %s(%s)",
                                context->table->s->table_name, 
                                field->table->s->table_name));
7369
            context->supported= FALSE;
7370
          }
7371 7372
          break;
        }
7373 7374
        case Item::FUNC_ITEM:
        {
7375 7376 7377 7378 7379 7380
          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
7381
          {
7382 7383 7384
            // Did not expect function here
            context->supported= FALSE;
            break;
7385
          }
7386
          
7387 7388 7389
          switch (func_item->functype()) {
          case Item_func::EQ_FUNC:
          {
7390 7391 7392 7393 7394 7395 7396 7397 7398 7399 7400 7401 7402 7403
            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;
7404
          }
7405 7406
          case Item_func::NE_FUNC:
          {
7407 7408 7409 7410 7411 7412 7413 7414 7415 7416 7417 7418 7419 7420
            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;
7421
          }
7422 7423
          case Item_func::LT_FUNC:
          {
7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438
            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;
          }
7439 7440
          case Item_func::LE_FUNC:
          {
7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455
            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;
          }
7456 7457
          case Item_func::GE_FUNC:
          {
7458 7459 7460 7461 7462 7463 7464 7465 7466 7467 7468 7469 7470 7471 7472
            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;
          }
7473 7474
          case Item_func::GT_FUNC:
          {
7475 7476 7477 7478 7479 7480 7481 7482 7483 7484 7485 7486 7487 7488 7489
            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;
          }
7490 7491
          case Item_func::LIKE_FUNC:
          {
7492 7493 7494 7495 7496
            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);
7497 7498 7499
            context->expect_only_field_type(MYSQL_TYPE_STRING);
            context->expect_field_type(MYSQL_TYPE_VAR_STRING);
            context->expect_field_type(MYSQL_TYPE_VARCHAR);
7500 7501 7502 7503
            context->expect_field_result(STRING_RESULT);
            context->expect(Item::FUNC_ITEM);
            break;
          }
7504 7505
          case Item_func::ISNULL_FUNC:
          {
7506 7507 7508 7509 7510 7511 7512 7513 7514 7515
            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;
          }
7516 7517
          case Item_func::ISNOTNULL_FUNC:
          {
7518 7519 7520 7521 7522 7523 7524 7525 7526 7527
            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;
          }
7528 7529
          case Item_func::NOT_FUNC:
          {
7530 7531 7532 7533
            DBUG_PRINT("info", ("NOT_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);     
            context->expect(Item::FUNC_ITEM);
7534
            context->expect(Item::COND_ITEM);
7535
            break;
7536
          }
7537 7538
          case Item_func::BETWEEN:
          {
7539
            DBUG_PRINT("info", ("BETWEEN, rewriting using AND"));
7540
            Item_func_between *between_func= (Item_func_between *) func_item;
7541 7542 7543 7544
            Ndb_rewrite_context *rewrite_context= 
              new Ndb_rewrite_context(func_item);
            rewrite_context->next= context->rewrite_stack;
            context->rewrite_stack= rewrite_context;
7545 7546 7547 7548 7549 7550 7551 7552 7553
            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;
            }
7554
            DBUG_PRINT("info", ("COND_AND_FUNC"));
7555 7556 7557
            curr_cond->ndb_item= 
              new Ndb_item(Item_func::COND_AND_FUNC, 
                           func_item->argument_count() - 1);
7558
            context->expect_only(Item::FIELD_ITEM);
7559 7560 7561 7562 7563
            context->expect(Item::INT_ITEM);
            context->expect(Item::STRING_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FUNC_ITEM);
            break;
7564
          }
7565 7566
          case Item_func::IN_FUNC:
          {
7567
            DBUG_PRINT("info", ("IN_FUNC, rewriting using OR"));
7568
            Item_func_in *in_func= (Item_func_in *) func_item;
7569 7570 7571 7572
            Ndb_rewrite_context *rewrite_context= 
              new Ndb_rewrite_context(func_item);
            rewrite_context->next= context->rewrite_stack;
            context->rewrite_stack= rewrite_context;
7573 7574 7575 7576 7577 7578 7579 7580 7581
            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;
            }
7582 7583 7584 7585 7586 7587 7588 7589 7590
            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;
7591
          }
7592 7593
          case Item_func::UNKNOWN_FUNC:
          {
7594 7595 7596 7597
            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())
7598 7599 7600 7601
            {
              switch (func_item->result_type()) {
              case STRING_RESULT:
              {
7602 7603 7604
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::STRING_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item); 
7605
                if (! context->expecting_no_field_result())
7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629
                {
                  // 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;
              }
7630 7631
              case REAL_RESULT:
              {
7632 7633 7634
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::REAL_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
7635
                if (! context->expecting_no_field_result()) 
7636 7637 7638 7639 7640 7641 7642 7643 7644 7645 7646 7647 7648 7649 7650 7651 7652
                {
                  // 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;
              }
7653 7654
              case INT_RESULT:
              {
7655 7656 7657
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::INT_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
7658
                if (! context->expecting_no_field_result()) 
7659 7660 7661 7662 7663 7664 7665 7666 7667 7668 7669 7670 7671 7672 7673 7674 7675
                {
                  // 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;
              }
7676 7677
              case DECIMAL_RESULT:
              {
7678 7679 7680
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::DECIMAL_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
7681
                if (! context->expecting_no_field_result()) 
7682 7683 7684 7685 7686 7687 7688 7689 7690 7691 7692 7693 7694 7695 7696 7697 7698 7699 7700
                {
                  // 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;
              }
7701
            }
7702 7703 7704 7705 7706
            else
              // Function does not return constant expression
              context->supported= FALSE;
            break;
          }
7707 7708
          default:
          {
7709 7710 7711
            DBUG_PRINT("info", ("Found func_item of type %d", 
                                func_item->functype()));
            context->supported= FALSE;
7712
          }
7713 7714
          }
          break;
7715
        }
7716
        case Item::STRING_ITEM:
7717 7718 7719
          DBUG_PRINT("info", ("STRING_ITEM")); 
          if (context->expecting(Item::STRING_ITEM)) 
          {
7720
#ifndef DBUG_OFF
7721 7722 7723 7724 7725 7726
            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()));
7727
#endif
7728 7729 7730
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::STRING_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);      
7731
            if (! context->expecting_no_field_result())
7732 7733 7734 7735 7736 7737 7738 7739 7740 7741 7742 7743 7744 7745 7746 7747 7748 7749 7750 7751 7752 7753 7754
            {
              // 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;
7755
        case Item::INT_ITEM:
7756 7757
          DBUG_PRINT("info", ("INT_ITEM"));
          if (context->expecting(Item::INT_ITEM)) 
7758
          {
7759 7760
            DBUG_PRINT("info", ("value %ld",
                                (long) ((Item_int*) item)->value));
7761 7762 7763
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::INT_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
7764
            if (! context->expecting_no_field_result()) 
7765 7766 7767 7768
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(INT_RESULT);
7769 7770
              context->expect_field_result(REAL_RESULT);
              context->expect_field_result(DECIMAL_RESULT);
7771 7772 7773 7774 7775 7776 7777
            }
            else
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
            }
7778 7779
          }
          else
7780 7781
            context->supported= FALSE;
          break;
7782
        case Item::REAL_ITEM:
7783
          DBUG_PRINT("info", ("REAL_ITEM"));
7784
          if (context->expecting(Item::REAL_ITEM)) 
7785
          {
7786
            DBUG_PRINT("info", ("value %f", ((Item_float *) item)->value));
7787 7788 7789
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::REAL_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
7790
            if (! context->expecting_no_field_result()) 
7791 7792 7793 7794 7795 7796 7797 7798 7799 7800 7801
            {
              // 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);
            }
7802
          }
7803 7804 7805
          else
            context->supported= FALSE;
          break;
7806
        case Item::VARBIN_ITEM:
7807 7808
          DBUG_PRINT("info", ("VARBIN_ITEM"));
          if (context->expecting(Item::VARBIN_ITEM)) 
7809
          {
7810 7811 7812
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::VARBIN_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);      
7813
            if (! context->expecting_no_field_result())
7814 7815 7816 7817 7818 7819 7820 7821 7822 7823 7824
            {
              // 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);
            }
7825 7826
          }
          else
7827 7828
            context->supported= FALSE;
          break;
7829
        case Item::DECIMAL_ITEM:
7830
          DBUG_PRINT("info", ("DECIMAL_ITEM"));
7831
          if (context->expecting(Item::DECIMAL_ITEM)) 
7832
          {
7833 7834
            DBUG_PRINT("info", ("value %f",
                                ((Item_decimal*) item)->val_real()));
7835 7836 7837
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::DECIMAL_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
7838
            if (! context->expecting_no_field_result()) 
7839 7840 7841 7842 7843 7844 7845 7846 7847 7848 7849 7850
            {
              // 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);
            }
7851
          }
7852 7853 7854
          else
            context->supported= FALSE;
          break;
7855 7856
        case Item::COND_ITEM:
        {
7857 7858 7859
          Item_cond *cond_item= (Item_cond *) item;
          
          if (context->expecting(Item::COND_ITEM))
7860 7861 7862
          {
            switch (cond_item->functype()) {
            case Item_func::COND_AND_FUNC:
7863 7864 7865 7866
              DBUG_PRINT("info", ("COND_AND_FUNC"));
              curr_cond->ndb_item= new Ndb_item(cond_item->functype(),
                                                cond_item);      
              break;
7867
            case Item_func::COND_OR_FUNC:
7868 7869 7870 7871 7872 7873 7874 7875 7876
              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;
            }
7877
          }
7878
          else
7879 7880
          {
            /* Did not expect condition */
7881
            context->supported= FALSE;          
7882
          }
7883
          break;
7884
        }
7885 7886
        default:
        {
7887
          DBUG_PRINT("info", ("Found item of type %d", item->type()));
7888
          context->supported= FALSE;
7889 7890
        }
        }
7891
      }
7892 7893 7894 7895 7896 7897 7898 7899 7900 7901
      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();
7902
          curr_cond->prev= prev_cond;
7903 7904 7905
          prev_cond->next= curr_cond;
          curr_cond->ndb_item= new Ndb_item(NDB_END_COND);
          // Pop rewrite stack
7906 7907 7908
          context->rewrite_stack=  rewrite_context->next;
          rewrite_context->next= NULL;
          delete(rewrite_context);
7909
        }
7910
      }
7911
    }
7912
  }
7913
 
7914 7915 7916 7917 7918 7919 7920 7921
  DBUG_VOID_RETURN;
}

bool
ha_ndbcluster::serialize_cond(const COND *cond, Ndb_cond_stack *ndb_cond)
{
  DBUG_ENTER("serialize_cond");
  Item *item= (Item *) cond;
7922
  Ndb_cond_traverse_context context(table, (void *)m_table, ndb_cond);
7923 7924 7925
  // Expect a logical expression
  context.expect(Item::FUNC_ITEM);
  context.expect(Item::COND_ITEM);
7926
  item->traverse_cond(&ndb_serialize_cond, (void *) &context, Item::PREFIX);
7927
  DBUG_PRINT("info", ("The pushed condition is %ssupported", (context.supported)?"":"not "));
7928

7929
  DBUG_RETURN(context.supported);
7930 7931
}

7932 7933
int
ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, 
7934 7935
                                           NdbScanFilter *filter,
                                           bool negated)
7936 7937
{
  DBUG_ENTER("build_scan_filter_predicate");  
7938 7939 7940
  switch (cond->ndb_item->type) {
  case NDB_FUNCTION:
  {
7941 7942 7943
    if (!cond->next)
      break;
    Ndb_item *a= cond->next->ndb_item;
7944
    Ndb_item *b, *field, *value= NULL;
7945 7946
    LINT_INIT(field);

7947 7948
    switch (cond->ndb_item->argument_count()) {
    case 1:
7949 7950 7951
      field= 
        (a->type == NDB_FIELD)? a : NULL;
      break;
7952
    case 2:
7953
      if (!cond->next->next)
7954
        break;
7955 7956
      b= cond->next->next->ndb_item;
      value= 
7957 7958 7959
        (a->type == NDB_VALUE)? a
        : (b->type == NDB_VALUE)? b
        : NULL;
7960
      field= 
7961 7962 7963
        (a->type == NDB_FIELD)? a
        : (b->type == NDB_FIELD)? b
        : NULL;
7964
      break;
7965
    default:
7966 7967
      break;
    }
7968 7969 7970
    switch ((negated) ? 
            Ndb_item::negate(cond->ndb_item->qualification.function_type)
            : cond->ndb_item->qualification.function_type) {
7971
    case NDB_EQ_FUNC:
7972
    {
7973
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
7974 7975
      // Save value in right format for the field type
      value->save_in_field(field);
7976
      DBUG_PRINT("info", ("Generating EQ filter"));
7977
      if (filter->cmp(NdbScanFilter::COND_EQ, 
7978 7979 7980 7981
                      field->get_field_no(),
                      field->get_val(),
                      field->pack_length()) == -1)
        DBUG_RETURN(1);
7982 7983
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7984
    }
7985
    case NDB_NE_FUNC:
7986
    {
7987
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
7988 7989
      // Save value in right format for the field type
      value->save_in_field(field);
7990
      DBUG_PRINT("info", ("Generating NE filter"));
7991
      if (filter->cmp(NdbScanFilter::COND_NE, 
7992 7993 7994 7995
                      field->get_field_no(),
                      field->get_val(),
                      field->pack_length()) == -1)
        DBUG_RETURN(1);
7996 7997
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7998
    }
7999
    case NDB_LT_FUNC:
8000
    {
8001
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
8002 8003
      // Save value in right format for the field type
      value->save_in_field(field);
8004
      if (a == field)
8005
      {
8006 8007 8008 8009 8010 8011
        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);
8012
      }
8013
      else
8014
      {
8015 8016 8017 8018 8019 8020
        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);
8021
      }
8022 8023
      cond= cond->next->next->next;
      DBUG_RETURN(0);
8024
    }
8025
    case NDB_LE_FUNC:
8026
    {
8027
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
8028 8029
      // Save value in right format for the field type
      value->save_in_field(field);
8030
      if (a == field)
8031
      {
8032 8033 8034 8035 8036 8037
        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);       
8038
      }
8039
      else
8040
      {
8041 8042 8043 8044 8045 8046
        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);
8047
      }
8048 8049
      cond= cond->next->next->next;
      DBUG_RETURN(0);
8050
    }
8051
    case NDB_GE_FUNC:
8052
    {
8053
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
8054 8055
      // Save value in right format for the field type
      value->save_in_field(field);
8056
      if (a == field)
8057
      {
8058 8059 8060 8061 8062 8063
        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);
8064
      }
8065
      else
8066
      {
8067 8068 8069 8070 8071 8072
        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);
8073
      }
8074 8075
      cond= cond->next->next->next;
      DBUG_RETURN(0);
8076
    }
8077
    case NDB_GT_FUNC:
8078
    {
8079
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
8080 8081
      // Save value in right format for the field type
      value->save_in_field(field);
8082
      if (a == field)
8083
      {
8084 8085 8086 8087 8088 8089
        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);
8090
      }
8091
      else
8092
      {
8093 8094 8095 8096 8097 8098
        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);
8099
      }
8100 8101
      cond= cond->next->next->next;
      DBUG_RETURN(0);
8102
    }
8103
    case NDB_LIKE_FUNC:
8104
    {
8105
      if (!value || !field) break;
8106 8107 8108
      if ((value->qualification.value_type != Item::STRING_ITEM) &&
          (value->qualification.value_type != Item::VARBIN_ITEM))
          break;
mskold@mysql.com's avatar
mskold@mysql.com committed
8109 8110 8111
      // Save value in right format for the field type
      value->save_in_field(field);
      DBUG_PRINT("info", ("Generating LIKE filter: like(%d,%s,%d)", 
8112 8113 8114 8115
                          field->get_field_no(), value->get_val(), 
                          value->pack_length()));
      if (filter->cmp(NdbScanFilter::COND_LIKE, 
                      field->get_field_no(),
8116 8117
                      value->get_val(),
                      value->pack_length()) == -1)
8118
        DBUG_RETURN(1);
8119 8120
      cond= cond->next->next->next;
      DBUG_RETURN(0);
8121
    }
8122 8123 8124 8125 8126 8127 8128 8129 8130 8131 8132 8133 8134 8135 8136 8137 8138 8139 8140 8141
    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:
8142 8143 8144 8145 8146
      if (!field)
        break;
      DBUG_PRINT("info", ("Generating ISNULL filter"));
      if (filter->isnull(field->get_field_no()) == -1)
        DBUG_RETURN(1);
8147 8148
      cond= cond->next->next;
      DBUG_RETURN(0);
8149
    case NDB_ISNOTNULL_FUNC:
8150
    {
8151 8152 8153 8154 8155
      if (!field)
        break;
      DBUG_PRINT("info", ("Generating ISNOTNULL filter"));
      if (filter->isnotnull(field->get_field_no()) == -1)
        DBUG_RETURN(1);         
8156 8157
      cond= cond->next->next;
      DBUG_RETURN(0);
8158 8159 8160 8161 8162 8163 8164 8165 8166 8167
    }
    default:
      break;
    }
    break;
  }
  default:
    break;
  }
  DBUG_PRINT("info", ("Found illegal condition"));
8168
  DBUG_RETURN(1);
8169 8170
}

8171

8172
int
8173
ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter)
8174
{
8175
  uint level=0;
8176
  bool negated= FALSE;
8177
  DBUG_ENTER("build_scan_filter_group");
8178

8179 8180
  do
  {
8181 8182 8183 8184 8185 8186
    if (!cond)
      DBUG_RETURN(1);
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
    {
      switch (cond->ndb_item->qualification.function_type) {
8187
      case NDB_COND_AND_FUNC:
8188
      {
8189 8190 8191 8192 8193
        level++;
        DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NAND":"AND",
                            level));
        if ((negated) ? filter->begin(NdbScanFilter::NAND)
            : filter->begin(NdbScanFilter::AND) == -1)
8194
          DBUG_RETURN(1);
8195
        negated= FALSE;
8196 8197 8198
        cond= cond->next;
        break;
      }
8199
      case NDB_COND_OR_FUNC:
8200
      {
8201 8202 8203 8204 8205 8206
        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);
8207
        negated= FALSE;
8208 8209 8210
        cond= cond->next;
        break;
      }
8211
      case NDB_NOT_FUNC:
8212
      {
8213
        DBUG_PRINT("info", ("Generating negated query"));
8214
        cond= cond->next;
8215
        negated= TRUE;
8216 8217 8218 8219
        break;
      }
      default:
        if (build_scan_filter_predicate(cond, filter, negated))
8220
          DBUG_RETURN(1);
8221
        negated= FALSE;
8222 8223 8224
        break;
      }
      break;
8225 8226
    }
    case NDB_END_COND:
8227 8228
      DBUG_PRINT("info", ("End of group %u", level));
      level--;
8229 8230
      if (cond) cond= cond->next;
      if (filter->end() == -1)
8231
        DBUG_RETURN(1);
8232 8233 8234
      if (!negated)
        break;
      // else fall through (NOT END is an illegal condition)
8235 8236
    default:
    {
8237
      DBUG_PRINT("info", ("Illegal scan filter"));
8238
    }
8239
    }
8240
  }  while (level > 0 || negated);
8241
  
8242
  DBUG_RETURN(0);
8243 8244
}

8245

8246 8247
int
ha_ndbcluster::build_scan_filter(Ndb_cond * &cond, NdbScanFilter *filter)
8248 8249 8250 8251
{
  bool simple_cond= TRUE;
  DBUG_ENTER("build_scan_filter");  

8252 8253 8254
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
      switch (cond->ndb_item->qualification.function_type) {
8255 8256
      case NDB_COND_AND_FUNC:
      case NDB_COND_OR_FUNC:
8257 8258 8259 8260 8261 8262 8263 8264 8265
        simple_cond= FALSE;
        break;
      default:
        break;
      }
      break;
    default:
      break;
    }
8266 8267 8268 8269 8270 8271
  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);
8272

8273
  DBUG_RETURN(0);
8274 8275
}

8276
int
8277
ha_ndbcluster::generate_scan_filter(Ndb_cond_stack *ndb_cond_stack,
8278
                                    NdbScanOperation *op)
8279 8280
{
  DBUG_ENTER("generate_scan_filter");
8281

8282 8283 8284
  if (ndb_cond_stack)
  {
    NdbScanFilter filter(op);
8285 8286
    
    DBUG_RETURN(generate_scan_filter_from_cond(ndb_cond_stack, filter));
8287 8288 8289 8290 8291 8292
  }
  else
  {  
    DBUG_PRINT("info", ("Empty stack"));
  }

8293
  DBUG_RETURN(0);
8294 8295
}

8296

8297 8298 8299 8300 8301
int
ha_ndbcluster::generate_scan_filter_from_cond(Ndb_cond_stack *ndb_cond_stack,
					      NdbScanFilter& filter)
{
  bool multiple_cond= FALSE;
8302 8303
  DBUG_ENTER("generate_scan_filter_from_cond");

8304 8305 8306 8307 8308 8309 8310 8311 8312 8313 8314 8315 8316 8317 8318 8319 8320 8321 8322 8323 8324 8325 8326 8327 8328
  // Wrap an AND group around multiple conditions
  if (ndb_cond_stack->next) 
  {
    multiple_cond= TRUE;
    if (filter.begin() == -1)
      DBUG_RETURN(1); 
  }
  for (Ndb_cond_stack *stack= ndb_cond_stack; 
       (stack); 
       stack= stack->next)
  {
    Ndb_cond *cond= stack->ndb_cond;
    
    if (build_scan_filter(cond, &filter))
    {
      DBUG_PRINT("info", ("build_scan_filter failed"));
      DBUG_RETURN(1);
    }
  }
  if (multiple_cond && filter.end() == -1)
    DBUG_RETURN(1);

  DBUG_RETURN(0);
}

8329

8330 8331 8332 8333 8334 8335 8336 8337 8338 8339 8340
int ha_ndbcluster::generate_scan_filter_from_key(NdbScanOperation *op,
						 const KEY* key_info, 
						 const byte *key, 
						 uint key_len,
						 byte *buf)
{
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
  NdbScanFilter filter(op);
  int res;
  DBUG_ENTER("generate_scan_filter_from_key");
8341

8342 8343 8344 8345 8346 8347 8348 8349 8350 8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363 8364 8365 8366 8367 8368 8369 8370 8371 8372 8373 8374 8375 8376 8377
  filter.begin(NdbScanFilter::AND);
  for (; key_part != end; key_part++) 
  {
    Field* field= key_part->field;
    uint32 pack_len= field->pack_length();
    const byte* ptr= key;
    DBUG_PRINT("info", ("Filtering value for %s", field->field_name));
    DBUG_DUMP("key", (char*)ptr, pack_len);
    if (key_part->null_bit)
    {
      DBUG_PRINT("info", ("Generating ISNULL filter"));
      if (filter.isnull(key_part->fieldnr-1) == -1)
	DBUG_RETURN(1);
    }
    else
    {
      DBUG_PRINT("info", ("Generating EQ filter"));
      if (filter.cmp(NdbScanFilter::COND_EQ, 
		     key_part->fieldnr-1,
		     ptr,
		     pack_len) == -1)
	DBUG_RETURN(1);
    }
    key += key_part->store_length;
  }      
  // Add any pushed condition
  if (m_cond_stack &&
      (res= generate_scan_filter_from_cond(m_cond_stack, filter)))
    DBUG_RETURN(res);
    
  if (filter.end() == -1)
    DBUG_RETURN(1);

  DBUG_RETURN(0);
}

8378 8379 8380 8381 8382 8383 8384 8385 8386
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,
8387 8388
	       "Cannot call SHOW NDBCLUSTER STATUS because skip-ndbcluster is "
               "defined",
8389 8390 8391 8392 8393 8394 8395 8396 8397 8398
	       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));

8399 8400
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
8401 8402
    DBUG_RETURN(TRUE);
  
8403
  if (get_thd_ndb(thd) && get_thd_ndb(thd)->ndb)
8404
  {
8405
    Ndb* ndb= (get_thd_ndb(thd))->ndb;
8406 8407
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
8408 8409 8410 8411 8412 8413 8414 8415 8416 8417 8418 8419 8420 8421 8422 8423 8424
    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);
}

8425
#endif /* HAVE_NDBCLUSTER_DB */