ha_ndbcluster.cc 237 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 207
  {4009, HA_ERR_NO_CONNECTION, 1 },

208 209 210
  { 0, 1, 0 },

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


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


231 232

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

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

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

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

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

Thd_ndb::~Thd_ndb()
{
304
  if (ndb)
305 306
  {
#ifndef DBUG_OFF
307 308
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
309 310 311 312 313 314 315 316 317 318
    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
319
    delete ndb;
320
    ndb= NULL;
321
  }
322
  changed_tables.empty();
323 324
}

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

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

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

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

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

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

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

394 395
void ha_ndbcluster::no_uncommitted_rows_execute_failure()
{
396 397
  if (m_ha_not_exact_count)
    return;
398
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_execute_failure");
399
  get_thd_ndb(current_thd)->error= 1;
400 401 402
  DBUG_VOID_RETURN;
}

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

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

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

448 449
/*
  Take care of the error that occured in NDB
450

451
  RETURN
452
    0   No error
453 454 455
    #   The mapped error code
*/

456
void ha_ndbcluster::invalidate_dictionary_cache(bool global)
457 458
{
  NDBDICT *dict= get_ndb()->getDictionary();
459
  DBUG_ENTER("invalidate_dictionary_cache");
460
  DBUG_PRINT("info", ("invalidating %s", m_tabname));
461

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

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

513
int ha_ndbcluster::ndb_err(NdbTransaction *trans)
514
{
515
  int res;
516
  NdbError err= trans->getNdbError();
517 518 519 520 521
  DBUG_ENTER("ndb_err");
  
  ERR_PRINT(err);
  switch (err.classification) {
  case NdbError::SchemaError:
522
  {
523 524 525 526 527 528 529
    /* 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);

530 531
    invalidate_dictionary_cache(TRUE);

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


578
/*
579
  Override the default get_error_message in order to add the 
580 581 582
  error message of NDB 
 */

583
bool ha_ndbcluster::get_error_message(int error, 
584
                                      String *buf)
585
{
586
  DBUG_ENTER("ha_ndbcluster::get_error_message");
587
  DBUG_PRINT("enter", ("error: %d", error));
588

589
  Ndb *ndb= get_ndb();
590
  if (!ndb)
591
    DBUG_RETURN(FALSE);
592

593
  const NdbError err= ndb->getNdbError(error);
594 595 596 597
  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);
598 599 600
}


tulin@dl145c.mysql.com's avatar
tulin@dl145c.mysql.com committed
601
#ifndef DBUG_OFF
pekka@mysql.com's avatar
pekka@mysql.com committed
602 603 604 605
/*
  Check if type is supported by NDB.
*/

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


644 645 646 647 648
/*
  Instruct NDB to set the value of the hidden primary key
*/

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


/*
  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
671 672 673 674
  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);
675 676 677 678 679 680 681 682
}


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

int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field, 
683
                                 uint fieldnr, bool *set_blob_value)
684 685 686 687 688 689 690 691
{
  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
692

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

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

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

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


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

784
NdbBlob::ActiveHook g_get_ndb_blobs_value;
pekka@mysql.com's avatar
pekka@mysql.com committed
785

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

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


/*
  Instruct NDB to fetch one field
pekka@mysql.com's avatar
pekka@mysql.com committed
854 855
  - data is read directly into buffer provided by field
    if field is NULL, data is read into memory provided by NDBAPI
856 857
*/

pekka@mysql.com's avatar
pekka@mysql.com committed
858
int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field,
859
                                 uint fieldnr, byte* buf)
860 861
{
  DBUG_ENTER("get_ndb_value");
pekka@mysql.com's avatar
pekka@mysql.com committed
862 863 864 865 866
  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
867 868
      DBUG_ASSERT(buf);
      DBUG_ASSERT(ndb_supported_type(field->type()));
pekka@mysql.com's avatar
pekka@mysql.com committed
869 870
      DBUG_ASSERT(field->ptr != NULL);
      if (! (field->flags & BLOB_FLAG))
871
      { 
872 873
        if (field->type() != MYSQL_TYPE_BIT)
        {
874 875 876 877 878 879 880 881
          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);
        }
882 883 884 885
        else // if (field->type() == MYSQL_TYPE_BIT)
        {
          m_value[fieldnr].rec= ndb_op->getValue(fieldnr);
        }
pekka@mysql.com's avatar
pekka@mysql.com committed
886 887 888 889 890 891 892 893 894
        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
895
	m_blobs_offset= buf - (byte*) table->record[0];
pekka@mysql.com's avatar
pekka@mysql.com committed
896
        void *arg= (void *)this;
897
        DBUG_RETURN(ndb_blob->setActiveHook(g_get_ndb_blobs_value, arg) != 0);
pekka@mysql.com's avatar
pekka@mysql.com committed
898 899 900 901 902
      }
      DBUG_RETURN(1);
  }

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


/*
  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)
{
945 946
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
947 948
  const NDBTAB *tab;
  int error;
949
  bool invalidating_ndb_table= FALSE;
950

951 952 953
  DBUG_ENTER("get_metadata");
  DBUG_PRINT("enter", ("m_tabname: %s, path: %s", m_tabname, path));

954
  do {
955
    const void *data= NULL, *pack_data= NULL;
956 957 958 959
    uint length, pack_length;

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

1009 1010
  if (error)
    DBUG_RETURN(error);
1011
  
1012
  m_table_version= tab->getObjectVersion();
1013 1014 1015 1016
  m_table= (void *)tab; 
  m_table_info= NULL; // Set in external lock
  
  DBUG_RETURN(build_index_list(ndb, table, ILBP_OPEN));
1017
}
1018

1019
static int fix_unique_index_attr_order(NDB_INDEX_DATA &data,
1020 1021
                                       const NDBINDEX *index,
                                       KEY *key_info)
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
{
  DBUG_ENTER("fix_unique_index_attr_order");
  unsigned sz= index->getNoOfIndexColumns();

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

  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
  DBUG_ASSERT(key_info->key_parts == sz);
  for (unsigned i= 0; key_part != end; key_part++, i++) 
  {
    const char *field_name= key_part->field->field_name;
#ifndef DBUG_OFF
   data.unique_index_attrid_map[i]= 255;
#endif
    for (unsigned j= 0; j < sz; j++)
    {
1041
      const NDBCOL *c= index->getColumn(j);
msvensson@neptunus.(none)'s avatar
msvensson@neptunus.(none) committed
1042
      if (strcmp(field_name, c->getName()) == 0)
1043
      {
1044 1045
        data.unique_index_attrid_map[i]= j;
        break;
1046 1047 1048 1049 1050 1051
      }
    }
    DBUG_ASSERT(data.unique_index_attrid_map[i] != 255);
  }
  DBUG_RETURN(0);
}
1052

1053 1054


1055
int ha_ndbcluster::build_index_list(Ndb *ndb, TABLE *tab, enum ILBP phase)
1056
{
1057
  uint i;
1058
  int error= 0;
1059
  const char *index_name;
1060
  char unique_index_name[FN_LEN];
1061
  bool null_in_unique_index= false;
1062
  static const char* unique_suffix= "$unique";
1063
  KEY* key_info= tab->key_info;
1064
  const char **key_name= tab->s->keynames.type_names;
1065
  NDBDICT *dict= ndb->getDictionary();
1066
  DBUG_ENTER("ha_ndbcluster::build_index_list");
1067
  
1068
  m_has_unique_index= FALSE;
1069
  // Save information about all known indexes
1070
  for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
1071
  {
1072
    index_name= *key_name;
1073
    NDB_INDEX_TYPE idx_type= get_index_type_from_table(i);
1074
    m_index[i].type= idx_type;
1075
    if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX)
1076
    {
1077
      m_has_unique_index= TRUE;
1078 1079
      strxnmov(unique_index_name, FN_LEN, index_name, unique_suffix, NullS);
      DBUG_PRINT("info", ("Created unique index name \'%s\' for index %d",
1080
                          unique_index_name, i));
1081
    }
1082 1083 1084
    // Create secondary indexes if in create phase
    if (phase == ILBP_CREATE)
    {
1085 1086
      DBUG_PRINT("info", ("Creating index %u: %s", i, index_name));      
      switch (idx_type){
1087
        
1088
      case PRIMARY_KEY_INDEX:
1089 1090
        // Do nothing, already created
        break;
1091
      case PRIMARY_KEY_ORDERED_INDEX:
1092 1093
        error= create_ordered_index(index_name, key_info);
        break;
1094
      case UNIQUE_ORDERED_INDEX:
1095 1096 1097
        if (!(error= create_ordered_index(index_name, key_info)))
          error= create_unique_index(unique_index_name, key_info);
        break;
1098
      case UNIQUE_INDEX:
1099 1100 1101 1102 1103 1104 1105 1106
	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);
1107
        break;
1108
      case ORDERED_INDEX:
1109 1110 1111 1112 1113 1114 1115 1116 1117 1118
	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;
	}
1119 1120
        error= create_ordered_index(index_name, key_info);
        break;
1121
      default:
1122 1123
        DBUG_ASSERT(FALSE);
        break;
1124 1125 1126
      }
      if (error)
      {
1127 1128 1129
        DBUG_PRINT("error", ("Failed to create index %u", i));
        drop_table();
        break;
1130 1131 1132
      }
    }
    // Add handles to index objects
1133
    if (idx_type != PRIMARY_KEY_INDEX && idx_type != UNIQUE_INDEX)
1134
    {
1135
      DBUG_PRINT("info", ("Get handle to index %s", index_name));
1136
      const NDBINDEX *index= dict->getIndex(index_name, m_tabname);
1137
      if (!index) DBUG_RETURN(1);
mskold@mysql.com's avatar
mskold@mysql.com committed
1138
      m_index[i].index= (void *) index;
1139
    }
1140
    if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX)
1141
    {
1142 1143
      DBUG_PRINT("info", ("Get handle to unique_index %s", unique_index_name));
      const NDBINDEX *index= dict->getIndex(unique_index_name, m_tabname);
1144
      if (!index) DBUG_RETURN(1);
mskold@mysql.com's avatar
mskold@mysql.com committed
1145
      m_index[i].unique_index= (void *) index;
1146 1147
      error= fix_unique_index_attr_order(m_index[i], index, key_info);
    }
1148 1149 1150 1151 1152
    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;
1153
  }
1154 1155
  
  DBUG_RETURN(error);
1156 1157
}

1158

1159 1160 1161 1162
/*
  Decode the type of an index from information 
  provided in table object
*/
1163
NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_table(uint inx) const
1164
{
1165
  bool is_hash_index=  (table->key_info[inx].algorithm == HA_KEY_ALG_HASH);
1166
  if (inx == table->s->primary_key)
1167
    return is_hash_index ? PRIMARY_KEY_INDEX : PRIMARY_KEY_ORDERED_INDEX;
1168 1169 1170 1171

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

1174
bool ha_ndbcluster::check_index_fields_not_null(uint inx)
1175 1176 1177 1178
{
  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;
1179
  DBUG_ENTER("ha_ndbcluster::check_index_fields_not_null");
1180 1181 1182 1183 1184
  
  for (; key_part != end; key_part++) 
    {
      Field* field= key_part->field;
      if (field->maybe_null())
1185
        DBUG_RETURN(true);
1186 1187
    }
  
1188
  DBUG_RETURN(false);
1189
}
1190 1191 1192

void ha_ndbcluster::release_metadata()
{
1193
  uint i;
1194

1195 1196 1197 1198
  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
1199
  m_table_info= NULL;
1200

1201
  // Release index list 
1202 1203
  for (i= 0; i < MAX_KEY; i++)
  {
1204 1205
    m_index[i].unique_index= NULL;      
    m_index[i].index= NULL;      
1206 1207 1208 1209 1210
    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;
    }
1211 1212
  }

1213 1214 1215
  DBUG_VOID_RETURN;
}

pekka@mysql.com's avatar
pekka@mysql.com committed
1216
int ha_ndbcluster::get_ndb_lock_type(enum thr_lock_type type)
1217
{
1218
  DBUG_ENTER("ha_ndbcluster::get_ndb_lock_type");
1219
  if (type >= TL_WRITE_ALLOW_WRITE)
1220 1221 1222 1223 1224 1225 1226 1227 1228 1229
  {
    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
1230
  else
1231 1232 1233 1234
  {
    DBUG_PRINT("info", ("Using committed read"));
    DBUG_RETURN(NdbOperation::LM_CommittedRead);
  }
1235 1236
}

1237 1238 1239 1240 1241 1242
static const ulong index_type_flags[]=
{
  /* UNDEFINED_INDEX */
  0,                         

  /* PRIMARY_KEY_INDEX */
1243
  HA_ONLY_WHOLE_INDEX, 
1244 1245

  /* PRIMARY_KEY_ORDERED_INDEX */
1246
  /* 
mskold@mysql.com's avatar
mskold@mysql.com committed
1247
     Enable HA_KEYREAD_ONLY when "sorted" indexes are supported, 
1248 1249 1250
     thus ORDERD BY clauses can be optimized by reading directly 
     through the index.
  */
mskold@mysql.com's avatar
mskold@mysql.com committed
1251
  // HA_KEYREAD_ONLY | 
1252
  HA_READ_NEXT |
1253
  HA_READ_PREV |
1254 1255
  HA_READ_RANGE |
  HA_READ_ORDER,
1256 1257

  /* UNIQUE_INDEX */
1258
  HA_ONLY_WHOLE_INDEX,
1259

1260
  /* UNIQUE_ORDERED_INDEX */
1261
  HA_READ_NEXT |
1262
  HA_READ_PREV |
1263 1264
  HA_READ_RANGE |
  HA_READ_ORDER,
1265

1266
  /* ORDERED_INDEX */
1267
  HA_READ_NEXT |
1268
  HA_READ_PREV |
1269 1270
  HA_READ_RANGE |
  HA_READ_ORDER
1271 1272 1273 1274 1275 1276 1277
};

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);
1278
  return m_index[idx_no].type;
1279 1280
}

1281 1282 1283 1284 1285 1286
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;
}

1287 1288 1289 1290 1291 1292 1293 1294

/*
  Get the flags for an index

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

1295 1296
inline ulong ha_ndbcluster::index_flags(uint idx_no, uint part,
                                        bool all_parts) const 
1297
{ 
1298
  DBUG_ENTER("ha_ndbcluster::index_flags");
1299
  DBUG_PRINT("info", ("idx_no: %d", idx_no));
1300
  DBUG_ASSERT(get_index_type_from_table(idx_no) < index_flags_size);
1301 1302
  DBUG_RETURN(index_type_flags[get_index_type_from_table(idx_no)] | 
              HA_KEY_SCAN_NOT_ROR);
1303 1304
}

pekka@mysql.com's avatar
pekka@mysql.com committed
1305 1306
static void shrink_varchar(Field* field, const byte* & ptr, char* buf)
{
1307
  if (field->type() == MYSQL_TYPE_VARCHAR && ptr != NULL) {
pekka@mysql.com's avatar
pekka@mysql.com committed
1308
    Field_varstring* f= (Field_varstring*)field;
pekka@mysql.com's avatar
pekka@mysql.com committed
1309
    if (f->length_bytes == 1) {
pekka@mysql.com's avatar
pekka@mysql.com committed
1310 1311 1312 1313 1314
      uint pack_len= field->pack_length();
      DBUG_ASSERT(1 <= pack_len && pack_len <= 256);
      if (ptr[1] == 0) {
        buf[0]= ptr[0];
      } else {
1315
        DBUG_ASSERT(FALSE);
pekka@mysql.com's avatar
pekka@mysql.com committed
1316 1317 1318 1319 1320 1321 1322
        buf[0]= 255;
      }
      memmove(buf + 1, ptr + 2, pack_len - 1);
      ptr= buf;
    }
  }
}
1323 1324 1325

int ha_ndbcluster::set_primary_key(NdbOperation *op, const byte *key)
{
1326
  KEY* key_info= table->key_info + table->s->primary_key;
1327 1328 1329 1330 1331 1332 1333
  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
1334 1335 1336
    const byte* ptr= key;
    char buf[256];
    shrink_varchar(field, ptr, buf);
1337
    if (set_ndb_key(op, field, 
1338
                    key_part->fieldnr-1, ptr))
1339
      ERR_RETURN(op->getNdbError());
pekka@mysql.com's avatar
pekka@mysql.com committed
1340
    key += key_part->store_length;
1341 1342 1343 1344 1345
  }
  DBUG_RETURN(0);
}


1346
int ha_ndbcluster::set_primary_key_from_record(NdbOperation *op, const byte *record)
1347
{
1348
  KEY* key_info= table->key_info + table->s->primary_key;
1349 1350
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
1351
  DBUG_ENTER("set_primary_key_from_record");
1352 1353 1354 1355 1356

  for (; key_part != end; key_part++) 
  {
    Field* field= key_part->field;
    if (set_ndb_key(op, field, 
1357
		    key_part->fieldnr-1, record+key_part->offset))
1358 1359 1360 1361 1362
      ERR_RETURN(op->getNdbError());
  }
  DBUG_RETURN(0);
}

1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380
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);
}

1381 1382
int 
ha_ndbcluster::set_index_key(NdbOperation *op, 
1383 1384
                             const KEY *key_info, 
                             const byte * key_ptr)
1385
{
1386
  DBUG_ENTER("ha_ndbcluster::set_index_key");
1387 1388 1389 1390 1391 1392
  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
1393 1394 1395 1396
    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
1397
    if (set_ndb_key(op, field, m_index[active_index].unique_index_attrid_map[i], ptr))
1398 1399 1400 1401 1402
      ERR_RETURN(m_active_trans->getNdbError());
    key_ptr+= key_part->store_length;
  }
  DBUG_RETURN(0);
}
1403

1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416
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) ||
1417 1418
        ((field->flags & PRI_KEY_FLAG)) || 
        m_retrieve_all_fields)
1419 1420
    {      
      if (get_ndb_value(op, field, i, buf))
1421
        ERR_RETURN(op->getNdbError());
1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444
    } 
    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);
} 

1445 1446 1447 1448
/*
  Read one record from NDB using primary key
*/

1449
int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf) 
1450
{
1451
  uint no_fields= table->s->fields;
1452 1453
  NdbConnection *trans= m_active_trans;
  NdbOperation *op;
1454

1455 1456 1457 1458
  int res;
  DBUG_ENTER("pk_read");
  DBUG_PRINT("enter", ("key_len: %u", key_len));
  DBUG_DUMP("key", (char*)key, key_len);
1459

1460 1461
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
joreland@mysql.com's avatar
joreland@mysql.com committed
1462
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || 
1463
      op->readTuple(lm) != 0)
1464
    ERR_RETURN(trans->getNdbError());
1465
  
1466
  if (table->s->primary_key == MAX_KEY) 
1467 1468 1469 1470 1471
  {
    // 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))
1472
      ERR_RETURN(trans->getNdbError());
1473
    
1474
    // Read key at the same time, for future reference
1475
    if (get_ndb_value(op, NULL, no_fields, NULL))
1476
      ERR_RETURN(trans->getNdbError());
1477 1478 1479 1480 1481 1482 1483
  } 
  else 
  {
    if ((res= set_primary_key(op, key)))
      return res;
  }
  
1484
  if ((res= define_read_attrs(buf, op)))
1485
    DBUG_RETURN(res);
1486
  
1487
  if (execute_no_commit_ie(this,trans,false) != 0) 
1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498
  {
    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);
}

1499 1500 1501 1502 1503 1504
/*
  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)
{
1505
  uint no_fields= table->s->fields, i;
1506
  NdbTransaction *trans= m_active_trans;
1507 1508 1509 1510
  NdbOperation *op;
  THD *thd= current_thd;
  DBUG_ENTER("complemented_pk_read");

1511
  if (m_retrieve_all_fields)
1512 1513 1514
    // We have allready retrieved all fields, nothing to complement
    DBUG_RETURN(0);

1515 1516
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
joreland@mysql.com's avatar
joreland@mysql.com committed
1517
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || 
1518
      op->readTuple(lm) != 0)
1519
    ERR_RETURN(trans->getNdbError());
1520
  int res;
mskold@mysql.com's avatar
mskold@mysql.com committed
1521
  if ((res= set_primary_key_from_record(op, old_data)))
1522
    ERR_RETURN(trans->getNdbError());
1523 1524 1525 1526
  // Read all unreferenced non-key field(s)
  for (i= 0; i < no_fields; i++) 
  {
    Field *field= table->field[i];
1527
    if (!((field->flags & PRI_KEY_FLAG) ||
1528
          (thd->query_id == field->query_id)))
1529
    {
1530
      if (get_ndb_value(op, field, i, new_data))
1531
        ERR_RETURN(trans->getNdbError());
1532 1533
    }
  }
1534
  if (execute_no_commit(this,trans,false) != 0) 
1535 1536 1537 1538 1539 1540 1541 1542
  {
    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;     
1543 1544 1545 1546 1547 1548 1549 1550

  /**
   * restore m_value
   */
  for (i= 0; i < no_fields; i++) 
  {
    Field *field= table->field[i];
    if (!((field->flags & PRI_KEY_FLAG) ||
1551
          (thd->query_id == field->query_id)))
1552 1553 1554 1555 1556
    {
      m_value[i].ptr= NULL;
    }
  }
  
1557 1558 1559
  DBUG_RETURN(0);
}

1560
/*
1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 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
 * 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
1620 1621
*/

1622
int ha_ndbcluster::peek_indexed_rows(const byte *record, bool check_pk)
1623
{
1624
  NdbTransaction *trans= m_active_trans;
1625
  NdbOperation *op;
1626 1627 1628 1629
  const NdbOperation *first, *last;
  uint i;
  int res;
  DBUG_ENTER("peek_indexed_rows");
1630

jonas@perch.ndb.mysql.com's avatar
jonas@perch.ndb.mysql.com committed
1631 1632 1633
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
  
1634
  first= NULL;
1635
  if (check_pk && table->s->primary_key != MAX_KEY)
1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666
  {
    /*
     * 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());
1667

1668 1669 1670 1671 1672 1673 1674 1675
      if (!first)
        first= iop;
      if ((res= set_index_key_from_record(iop, record, i)))
        ERR_RETURN(trans->getNdbError());
    }
  }
  last= trans->getLastDefinedOperation();
  if (first)
1676
    res= execute_no_commit_ie(this,trans,false);
1677 1678 1679 1680 1681 1682 1683 1684
  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))
1685 1686 1687 1688
  {
    table->status= STATUS_NOT_FOUND;
    DBUG_RETURN(ndb_err(trans));
  } 
1689 1690 1691 1692
  else
  {
    DBUG_PRINT("info", ("m_dupkey %d", m_dupkey));
  }
1693 1694
  DBUG_RETURN(0);
}
1695

1696 1697 1698 1699 1700
/*
  Read one record from NDB using unique secondary index
*/

int ha_ndbcluster::unique_index_read(const byte *key,
1701
                                     uint key_len, byte *buf)
1702
{
1703
  int res;
1704
  NdbTransaction *trans= m_active_trans;
1705
  NdbIndexOperation *op;
1706
  DBUG_ENTER("ha_ndbcluster::unique_index_read");
1707 1708 1709
  DBUG_PRINT("enter", ("key_len: %u, index: %u", key_len, active_index));
  DBUG_DUMP("key", (char*)key, key_len);
  
1710 1711
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
1712
  if (!(op= trans->getNdbIndexOperation((NDBINDEX *) 
1713
                                        m_index[active_index].unique_index, 
joreland@mysql.com's avatar
joreland@mysql.com committed
1714
                                        (const NDBTAB *) m_table)) ||
1715
      op->readTuple(lm) != 0)
1716 1717 1718
    ERR_RETURN(trans->getNdbError());
  
  // Set secondary index key(s)
1719
  if ((res= set_index_key(op, table->key_info + active_index, key)))
1720 1721
    DBUG_RETURN(res);
  
1722
  if ((res= define_read_attrs(buf, op)))
1723
    DBUG_RETURN(res);
1724

1725
  if (execute_no_commit_ie(this,trans,false) != 0) 
1726 1727 1728 1729 1730 1731 1732 1733 1734 1735
  {
    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);
}

1736
inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor)
1737 1738
{
  DBUG_ENTER("fetch_next");
1739
  int local_check;
1740
  NdbTransaction *trans= m_active_trans;
1741
  
1742 1743 1744 1745 1746 1747 1748 1749
    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
    */
1750
      NdbConnection *con_trans= m_active_trans;
1751 1752 1753 1754 1755 1756
      NdbOperation *op;
      // Lock row
      DBUG_PRINT("info", ("Keeping lock on scanned row"));
      
      if (!(op= m_active_cursor->lockCurrentTuple()))
      {
1757
        /* purecov: begin inspected */
1758
	m_lock_tuple= false;
1759 1760
	ERR_RETURN(con_trans->getNdbError());
        /* purecov: end */    
1761 1762 1763 1764 1765 1766 1767
      }
      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;
1768 1769
  do {
    DBUG_PRINT("info", ("Call nextResult, contact_ndb: %d", contact_ndb));
pekka@mysql.com's avatar
pekka@mysql.com committed
1770 1771 1772
    /*
      We can only handle one tuple with blobs at a time.
    */
1773
    if (m_ops_pending && m_blobs_pending)
pekka@mysql.com's avatar
pekka@mysql.com committed
1774
    {
1775
      if (execute_no_commit(this,trans,false) != 0)
1776
        DBUG_RETURN(ndb_err(trans));
1777 1778
      m_ops_pending= 0;
      m_blobs_pending= FALSE;
pekka@mysql.com's avatar
pekka@mysql.com committed
1779
    }
1780
    
1781
    if ((local_check= cursor->nextResult(contact_ndb, m_force_send)) == 0)
1782
    {
1783 1784 1785 1786 1787 1788 1789
      /*
	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);
1790 1791
      DBUG_RETURN(0);
    } 
1792
    else if (local_check == 1 || local_check == 2)
1793 1794 1795
    {
      // 1: No more records
      // 2: No more cached records
1796
      
1797
      /*
1798 1799 1800
        Before fetching more rows and releasing lock(s),
        all pending update or delete operations should 
        be sent to NDB
1801
      */
1802
      DBUG_PRINT("info", ("ops_pending: %ld", (long) m_ops_pending));    
1803
      if (m_ops_pending)
1804
      {
1805 1806
        if (m_transaction_on)
        {
1807
          if (execute_no_commit(this,trans,false) != 0)
1808 1809 1810 1811 1812 1813
            DBUG_RETURN(-1);
        }
        else
        {
          if  (execute_commit(this,trans) != 0)
            DBUG_RETURN(-1);
1814
          if (trans->restart() != 0)
1815 1816 1817 1818 1819 1820
          {
            DBUG_ASSERT(0);
            DBUG_RETURN(-1);
          }
        }
        m_ops_pending= 0;
1821
      }
1822
      contact_ndb= (local_check == 2);
1823
    }
1824 1825 1826 1827
    else
    {
      DBUG_RETURN(-1);
    }
1828
  } while (local_check == 2);
1829

1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840
  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.
1841

1842 1843 1844 1845 1846 1847 1848
*/

inline int ha_ndbcluster::next_result(byte *buf)
{  
  int res;
  DBUG_ENTER("next_result");
    
1849 1850 1851
  if (!m_active_cursor)
    DBUG_RETURN(HA_ERR_END_OF_FILE);
  
1852
  if ((res= fetch_next(m_active_cursor)) == 0)
1853 1854 1855 1856 1857 1858 1859
  {
    DBUG_PRINT("info", ("One more record found"));    
    
    unpack_record(buf);
    table->status= 0;
    DBUG_RETURN(0);
  }
1860
  else if (res == 1)
1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871
  {
    // 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));
  }
1872 1873
}

1874
/*
1875
  Set bounds for ordered index scan.
1876 1877
*/

joreland@mysql.com's avatar
joreland@mysql.com committed
1878
int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
1879 1880
                              const key_range *keys[2],
                              uint range_no)
1881
{
1882 1883 1884 1885
  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;
1886
  uint i, j;
1887 1888

  DBUG_ENTER("set_bounds");
1889
  DBUG_PRINT("info", ("key_parts=%d", key_parts));
1890

1891
  for (j= 0; j <= 1; j++)
1892
  {
1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905
    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;
    }
1906 1907
  }
  tot_len= 0;
1908

1909 1910 1911 1912
  for (i= 0; i < key_parts; i++)
  {
    KEY_PART_INFO *key_part= &key_info->key_part[i];
    Field *field= key_part->field;
1913
#ifndef DBUG_OFF
1914
    uint part_len= key_part->length;
1915
#endif
1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929
    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++)
    {
1930
      struct part_st &p= part[j];
1931 1932 1933 1934 1935 1936 1937
      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
1938
        p.part_null= key_part->null_bit && *p.part_ptr;
1939
        p.bound_ptr= (const char *)
joreland@mysql.com's avatar
joreland@mysql.com committed
1940
          p.part_null ? 0 : key_part->null_bit ? p.part_ptr + 1 : p.part_ptr;
1941 1942 1943 1944 1945 1946 1947 1948

        if (j == 0)
        {
          switch (p.key->flag)
          {
            case HA_READ_KEY_EXACT:
              p.bound_type= NdbIndexScanOperation::BoundEQ;
              break;
1949
            // ascending
1950 1951 1952 1953 1954 1955 1956 1957 1958
            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;
1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971
            // 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;
1972 1973 1974 1975 1976 1977 1978
            default:
              break;
          }
        }
        if (j == 1) {
          switch (p.key->flag)
          {
1979
            // ascending
1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990
            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;
1991
            // descending strangely sets no end key
1992 1993
          }
        }
1994

1995 1996 1997
        if (p.bound_type == -1)
        {
          DBUG_PRINT("error", ("key %d unknown flag %d", j, p.key->flag));
1998
          DBUG_ASSERT(FALSE);
1999
          // Stop setting bounds but continue with what we have
2000
          DBUG_RETURN(op->end_of_bound(range_no));
2001 2002 2003
        }
      }
    }
2004

2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021
    // 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;
    }
2022

2023 2024
    for (j= 0; j <= 1; j++)
    {
2025
      struct part_st &p= part[j];
2026 2027 2028 2029 2030 2031 2032 2033 2034
      // 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)
2035
        {
pekka@mysql.com's avatar
pekka@mysql.com committed
2036 2037 2038
          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
2039
          if (op->setBound(i, p.bound_type, ptr))
2040
            ERR_RETURN(op->getNdbError());
2041
        }
2042 2043 2044 2045
      }
    }

    tot_len+= part_store_len;
2046
  }
2047
  DBUG_RETURN(op->end_of_bound(range_no));
2048 2049
}

2050
/*
2051
  Start ordered index scan in NDB
2052 2053
*/

2054
int ha_ndbcluster::ordered_index_scan(const key_range *start_key,
2055 2056
                                      const key_range *end_key,
                                      bool sorted, bool descending, byte* buf)
2057
{  
2058
  int res;
joreland@mysql.com's avatar
joreland@mysql.com committed
2059
  bool restart;
2060
  NdbTransaction *trans= m_active_trans;
joreland@mysql.com's avatar
joreland@mysql.com committed
2061
  NdbIndexScanOperation *op;
2062

2063 2064 2065
  DBUG_ENTER("ha_ndbcluster::ordered_index_scan");
  DBUG_PRINT("enter", ("index: %u, sorted: %d, descending: %d",
             active_index, sorted, descending));  
2066
  DBUG_PRINT("enter", ("Starting new ordered scan on %s", m_tabname));
pekka@mysql.com's avatar
pekka@mysql.com committed
2067

2068 2069
  // Check that sorted seems to be initialised
  DBUG_ASSERT(sorted == 0 || sorted == 1);
2070
  
2071
  if (m_active_cursor == 0)
joreland@mysql.com's avatar
joreland@mysql.com committed
2072
  {
2073
    restart= FALSE;
joreland@mysql.com's avatar
joreland@mysql.com committed
2074 2075
    NdbOperation::LockMode lm=
      (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
2076
    bool need_pk = (lm == NdbOperation::LM_Read);
joreland@mysql.com's avatar
joreland@mysql.com committed
2077
    if (!(op= trans->getNdbIndexScanOperation((NDBINDEX *)
2078 2079
                                              m_index[active_index].index, 
                                              (const NDBTAB *) m_table)) ||
2080
        op->readTuples(lm, 0, parallelism, sorted, descending, false, need_pk))
joreland@mysql.com's avatar
joreland@mysql.com committed
2081
      ERR_RETURN(trans->getNdbError());
2082
    m_active_cursor= op;
joreland@mysql.com's avatar
joreland@mysql.com committed
2083
  } else {
2084
    restart= TRUE;
2085
    op= (NdbIndexScanOperation*)m_active_cursor;
joreland@mysql.com's avatar
joreland@mysql.com committed
2086 2087 2088
    
    DBUG_ASSERT(op->getSorted() == sorted);
    DBUG_ASSERT(op->getLockMode() == 
2089
                (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type));
2090
    if (op->reset_bounds(m_force_send))
joreland@mysql.com's avatar
joreland@mysql.com committed
2091 2092
      DBUG_RETURN(ndb_err(m_active_trans));
  }
2093
  
2094
  {
2095
    const key_range *keys[2]= { start_key, end_key };
2096 2097 2098
    res= set_bounds(op, keys);
    if (res)
      DBUG_RETURN(res);
2099
  }
2100 2101 2102

  if (!restart && generate_scan_filter(m_cond_stack, op))
    DBUG_RETURN(ndb_err(trans));
2103
  
2104
  if (!restart && (res= define_read_attrs(buf, op)))
2105
  {
2106
    DBUG_RETURN(res);
joreland@mysql.com's avatar
joreland@mysql.com committed
2107
  }
2108

2109
  if (execute_no_commit(this,trans,false) != 0)
2110 2111 2112 2113
    DBUG_RETURN(ndb_err(trans));
  
  DBUG_RETURN(next_result(buf));
}
2114

2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150
/*
  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));
}

2151
/*
2152
  Start full table scan in NDB
2153 2154 2155 2156
 */

int ha_ndbcluster::full_table_scan(byte *buf)
{
2157
  int res;
2158
  NdbScanOperation *op;
2159
  NdbTransaction *trans= m_active_trans;
2160 2161 2162 2163

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

2164 2165
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
2166
  bool need_pk = (lm == NdbOperation::LM_Read);
2167
  if (!(op=trans->getNdbScanOperation((const NDBTAB *) m_table)) ||
2168 2169 2170
      op->readTuples(lm, 
		     (need_pk)?NdbScanOperation::SF_KeyInfo:0, 
		     parallelism))
2171
    ERR_RETURN(trans->getNdbError());
2172
  m_active_cursor= op;
2173 2174
  if (generate_scan_filter(m_cond_stack, op))
    DBUG_RETURN(ndb_err(trans));
2175
  if ((res= define_read_attrs(buf, op)))
2176 2177
    DBUG_RETURN(res);

2178
  if (execute_no_commit(this,trans,false) != 0)
2179 2180 2181
    DBUG_RETURN(ndb_err(trans));
  DBUG_PRINT("exit", ("Scan started successfully"));
  DBUG_RETURN(next_result(buf));
2182 2183
}

2184 2185 2186 2187 2188
/*
  Insert one record into NDB
*/
int ha_ndbcluster::write_row(byte *record)
{
mskold@mysql.com's avatar
mskold@mysql.com committed
2189
  bool has_auto_increment;
2190
  uint i;
2191
  NdbTransaction *trans= m_active_trans;
2192 2193
  NdbOperation *op;
  int res;
2194
  THD *thd= table->in_use;
2195
  DBUG_ENTER("write_row");
2196

2197 2198
  has_auto_increment= (table->next_number_field && record == table->record[0]);
  if (table->s->primary_key != MAX_KEY)
2199
  {
2200 2201 2202 2203 2204
    /*
     * Increase any auto_incremented primary key
     */
    if (has_auto_increment) 
    {
2205
      int error;
2206 2207
      
      m_skip_auto_increment= FALSE;
2208 2209
      if ((error= update_auto_increment()))
        DBUG_RETURN(error);
2210 2211 2212 2213 2214 2215 2216 2217 2218
      /* 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
   */
2219
  if (!m_use_write && m_ignore_dup_key)
2220
  {
2221 2222 2223 2224 2225
    /*
      compare if expression with that in start_bulk_insert()
      start_bulk_insert will set parameters to ensure that each
      write_row is committed individually
    */
2226
    int peek_res= peek_indexed_rows(record, true);
2227 2228 2229 2230 2231 2232 2233
    
    if (!peek_res) 
    {
      DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
    }
    if (peek_res != HA_ERR_KEY_NOT_FOUND)
      DBUG_RETURN(peek_res);
2234
  }
2235

2236
  statistic_increment(thd->status_var.ha_write_count, &LOCK_status);
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
2237 2238
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
    table->timestamp_field->set_time();
2239

joreland@mysql.com's avatar
joreland@mysql.com committed
2240
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)))
2241 2242 2243 2244 2245 2246
    ERR_RETURN(trans->getNdbError());

  res= (m_use_write) ? op->writeTuple() :op->insertTuple(); 
  if (res != 0)
    ERR_RETURN(trans->getNdbError());  
 
2247
  if (table->s->primary_key == MAX_KEY) 
2248 2249
  {
    // Table has hidden primary key
2250
    Ndb *ndb= get_ndb();
2251 2252
    int ret;
    Uint64 auto_value;
2253 2254
    uint retries= NDB_AUTO_INCREMENT_RETRIES;
    do {
2255 2256
      ret= ndb->getAutoIncrementValue((const NDBTAB *) m_table, auto_value, 1);
    } while (ret == -1 && 
2257 2258
             --retries &&
             ndb->getNdbError().status == NdbError::TemporaryError);
2259
    if (ret == -1)
2260
      ERR_RETURN(ndb->getNdbError());
2261
    if (set_hidden_key(op, table->s->fields, (const byte*)&auto_value))
2262 2263 2264 2265
      ERR_RETURN(op->getNdbError());
  } 
  else 
  {
2266
    if ((res= set_primary_key_from_record(op, record)))
2267
      return res;  
2268 2269 2270
  }

  // Set non-key attribute(s)
2271
  bool set_blob_value= FALSE;
2272
  for (i= 0; i < table->s->fields; i++) 
2273 2274 2275
  {
    Field *field= table->field[i];
    if (!(field->flags & PRI_KEY_FLAG) &&
2276
        set_ndb_value(op, field, i, &set_blob_value))
2277
    {
2278
      m_skip_auto_increment= TRUE;
2279
      ERR_RETURN(op->getNdbError());
2280
    }
2281 2282
  }

2283 2284
  m_rows_changed++;

2285 2286 2287 2288 2289 2290 2291
  /*
    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!
  */
2292
  m_rows_inserted++;
2293
  no_uncommitted_rows_update(1);
2294
  m_bulk_insert_not_flushed= TRUE;
2295
  if ((m_rows_to_insert == (ha_rows) 1) || 
2296
      ((m_rows_inserted % m_bulk_insert_rows) == 0) ||
2297
      m_primary_key_update ||
2298
      set_blob_value)
2299 2300 2301
  {
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
2302 2303
                        "rows_inserted:%d, bulk_insert_rows: %d", 
                        (int)m_rows_inserted, (int)m_bulk_insert_rows));
2304

2305
    m_bulk_insert_not_flushed= FALSE;
2306
    if (m_transaction_on)
2307
    {
2308
      if (execute_no_commit(this,trans,false) != 0)
2309
      {
2310 2311 2312
        m_skip_auto_increment= TRUE;
        no_uncommitted_rows_execute_failure();
        DBUG_RETURN(ndb_err(trans));
2313
      }
2314 2315
    }
    else
2316
    {
2317
      if (execute_commit(this,trans) != 0)
2318
      {
2319 2320 2321
        m_skip_auto_increment= TRUE;
        no_uncommitted_rows_execute_failure();
        DBUG_RETURN(ndb_err(trans));
2322
      }
2323
      if (trans->restart() != 0)
2324
      {
2325 2326
        DBUG_ASSERT(0);
        DBUG_RETURN(-1);
2327
      }
2328
    }
2329
  }
2330
  if ((has_auto_increment) && (m_skip_auto_increment))
mskold@mysql.com's avatar
mskold@mysql.com committed
2331
  {
2332
    Ndb *ndb= get_ndb();
2333
    Uint64 next_val= (Uint64) table->next_number_field->val_int() + 1;
2334
#ifndef DBUG_OFF
2335
    char buff[22];
mskold@mysql.com's avatar
mskold@mysql.com committed
2336
    DBUG_PRINT("info", 
2337 2338
               ("Trying to set next auto increment value to %s",
                llstr(next_val, buff)));
2339
#endif
2340
    if (ndb->setAutoIncrementValue((const NDBTAB *) m_table, next_val, TRUE)
2341
        == -1)
2342
      ERR_RETURN(ndb->getNdbError());
2343
  }
2344
  m_skip_auto_increment= TRUE;
2345

2346 2347 2348 2349 2350 2351 2352
  DBUG_RETURN(0);
}


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

int ha_ndbcluster::key_cmp(uint keynr, const byte * old_row,
2353
                           const byte * new_row)
2354 2355 2356 2357 2358 2359 2360 2361 2362
{
  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) !=
2363 2364
          (new_row[key_part->null_offset] & key_part->null_bit))
        return 1;
2365
    }
2366
    if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
2367 2368 2369
    {

      if (key_part->field->cmp_binary((char*) (old_row + key_part->offset),
2370 2371 2372
                                      (char*) (new_row + key_part->offset),
                                      (ulong) key_part->length))
        return 1;
2373 2374 2375 2376
    }
    else
    {
      if (memcmp(old_row+key_part->offset, new_row+key_part->offset,
2377 2378
                 key_part->length))
        return 1;
2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390
    }
  }
  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;
2391
  NdbTransaction *trans= m_active_trans;
2392
  NdbScanOperation* cursor= m_active_cursor;
2393 2394
  NdbOperation *op;
  uint i;
2395 2396
  bool pk_update= (table->s->primary_key != MAX_KEY &&
		   key_cmp(table->s->primary_key, old_data, new_data));
2397 2398
  DBUG_ENTER("update_row");
  
2399
  /*
2400 2401
   * If IGNORE the ignore constraint violations on primary and unique keys,
   * but check that it is not part of INSERT ... ON DUPLICATE KEY UPDATE
2402
   */
2403
  if (m_ignore_dup_key && thd->lex->sql_command == SQLCOM_UPDATE)
2404
  {
2405
    int peek_res= peek_indexed_rows(new_data, pk_update);
2406 2407 2408 2409 2410 2411 2412 2413 2414
    
    if (!peek_res) 
    {
      DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
    }
    if (peek_res != HA_ERR_KEY_NOT_FOUND)
      DBUG_RETURN(peek_res);
  }

2415
  statistic_increment(thd->status_var.ha_update_count, &LOCK_status);
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
2416
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
2417
  {
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
2418
    table->timestamp_field->set_time();
2419 2420 2421
    // 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
2422

2423
  /* Check for update of primary key for special handling */  
2424
  if (pk_update)
2425
  {
2426
    int read_res, insert_res, delete_res, undo_res;
2427

2428
    DBUG_PRINT("info", ("primary key update, doing pk read+delete+insert"));
2429
    // Get all old fields, since we optimize away fields not in query
2430
    read_res= complemented_pk_read(old_data, new_data);
2431 2432 2433 2434 2435
    if (read_res)
    {
      DBUG_PRINT("info", ("pk read failed"));
      DBUG_RETURN(read_res);
    }
2436
    // Delete old row
2437
    m_primary_key_update= TRUE;
2438
    delete_res= delete_row(old_data);
2439
    m_primary_key_update= FALSE;
2440 2441 2442
    if (delete_res)
    {
      DBUG_PRINT("info", ("delete failed"));
2443
      DBUG_RETURN(delete_res);
2444
    }     
2445 2446
    // Insert new row
    DBUG_PRINT("info", ("delete succeded"));
2447
    m_primary_key_update= TRUE;
2448
    insert_res= write_row(new_data);
2449
    m_primary_key_update= FALSE;
2450 2451 2452 2453 2454
    if (insert_res)
    {
      DBUG_PRINT("info", ("insert failed"));
      if (trans->commitStatus() == NdbConnection::Started)
      {
2455
        // Undo delete_row(old_data)
2456
        m_primary_key_update= TRUE;
2457 2458 2459 2460 2461 2462
        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");
2463 2464 2465 2466 2467
        m_primary_key_update= FALSE;
      }
      DBUG_RETURN(insert_res);
    }
    DBUG_PRINT("info", ("delete+insert succeeded"));
2468
    DBUG_RETURN(0);
2469
  }
2470

2471
  if (cursor)
2472
  {
2473 2474 2475 2476 2477 2478 2479 2480
    /*
      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"));
2481
    if (!(op= cursor->updateCurrentTuple()))
2482
      ERR_RETURN(trans->getNdbError());
2483
    m_lock_tuple= false;
2484
    m_ops_pending++;
2485
    if (uses_blob_value(FALSE))
2486
      m_blobs_pending= TRUE;
2487 2488 2489
  }
  else
  {  
joreland@mysql.com's avatar
joreland@mysql.com committed
2490
    if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) ||
2491
        op->updateTuple() != 0)
2492 2493
      ERR_RETURN(trans->getNdbError());  
    
2494
    if (table->s->primary_key == MAX_KEY) 
2495 2496 2497 2498 2499
    {
      // 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 
2500 2501
      // read into m_ref
      DBUG_DUMP("key", m_ref, NDB_HIDDEN_PRIMARY_KEY_LENGTH);
2502
      
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
2503
      if (set_hidden_key(op, table->s->fields, m_ref))
2504
        ERR_RETURN(op->getNdbError());
2505 2506 2507 2508
    } 
    else 
    {
      int res;
2509
      if ((res= set_primary_key_from_record(op, old_data)))
2510
        DBUG_RETURN(res);
2511
    }
2512 2513
  }

2514 2515
  m_rows_changed++;

2516
  // Set non-key attribute(s)
2517
  for (i= 0; i < table->s->fields; i++) 
2518 2519
  {
    Field *field= table->field[i];
2520
    if (((thd->query_id == field->query_id) || m_retrieve_all_fields) &&
2521
        (!(field->flags & PRI_KEY_FLAG)) &&
2522
        set_ndb_value(op, field, i))
2523 2524
      ERR_RETURN(op->getNdbError());
  }
2525

2526 2527 2528 2529 2530 2531 2532
  /*
    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) {
2533
    no_uncommitted_rows_execute_failure();
2534
    DBUG_RETURN(ndb_err(trans));
2535
  }
2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546
  
  DBUG_RETURN(0);
}


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

int ha_ndbcluster::delete_row(const byte *record)
{
2547
  THD *thd= current_thd;
2548
  NdbTransaction *trans= m_active_trans;
2549
  NdbScanOperation* cursor= m_active_cursor;
2550 2551 2552
  NdbOperation *op;
  DBUG_ENTER("delete_row");

2553
  statistic_increment(thd->status_var.ha_delete_count,&LOCK_status);
2554
  m_rows_changed++;
2555

2556
  if (cursor)
2557
  {
2558
    /*
2559
      We are scanning records and want to delete the record
2560
      that was just found, call deleteTuple on the cursor 
2561
      to take over the lock to a new delete operation
2562 2563 2564 2565
      And thus setting the primary key of the record from 
      the active record in cursor
    */
    DBUG_PRINT("info", ("Calling deleteTuple on cursor"));
2566
    if (cursor->deleteCurrentTuple() != 0)
2567
      ERR_RETURN(trans->getNdbError());     
2568
    m_lock_tuple= false;
2569
    m_ops_pending++;
2570

2571 2572
    no_uncommitted_rows_update(-1);

2573
    if (!(m_primary_key_update || m_delete_cannot_batch))
2574 2575
      // If deleting from cursor, NoCommit will be handled in next_result
      DBUG_RETURN(0);
2576 2577
  }
  else
2578
  {
2579
    
joreland@mysql.com's avatar
joreland@mysql.com committed
2580
    if (!(op=trans->getNdbOperation((const NDBTAB *) m_table)) || 
2581
        op->deleteTuple() != 0)
2582 2583
      ERR_RETURN(trans->getNdbError());
    
2584 2585
    no_uncommitted_rows_update(-1);
    
2586
    if (table->s->primary_key == MAX_KEY) 
2587 2588 2589 2590
    {
      // 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
2591
      if (set_hidden_key(op, table->s->fields, m_ref))
2592
        ERR_RETURN(op->getNdbError());
2593 2594 2595 2596
    } 
    else 
    {
      int res;
2597 2598
      if ((res= set_primary_key_from_record(op, record)))
        return res;  
2599
    }
2600
  }
2601

2602
  // Execute delete operation
2603
  if (execute_no_commit(this,trans,false) != 0) {
2604
    no_uncommitted_rows_execute_failure();
2605
    DBUG_RETURN(ndb_err(trans));
2606
  }
2607 2608
  DBUG_RETURN(0);
}
2609
  
2610 2611 2612 2613 2614
/*
  Unpack a record read from NDB 

  SYNOPSIS
    unpack_record()
2615
    buf                 Buffer to store read row
2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627

  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
2628
  NdbValue *value= m_value;
2629
  DBUG_ENTER("unpack_record");
2630

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

2724
  char buf_type[MAX_FIELD_WIDTH], buf_val[MAX_FIELD_WIDTH];
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
2725
  String type(buf_type, sizeof(buf_type), &my_charset_bin);
2726
  String val(buf_val, sizeof(buf_val), &my_charset_bin);
2727
  for (uint f= 0; f < table->s->fields; f++)
2728
  {
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
2729
    /* Use DBUG_PRINT since DBUG_FILE cannot be filtered out */
2730
    char buf[2000];
2731
    Field *field;
2732
    void* ptr;
pekka@mysql.com's avatar
pekka@mysql.com committed
2733
    NdbValue value;
2734

2735
    buf[0]= 0;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
2736
    field= table->field[f];
pekka@mysql.com's avatar
pekka@mysql.com committed
2737
    if (!(value= m_value[f]).ptr)
2738
    {
2739
      strmov(buf, "not read");
2740
      goto print_value;
2741
    }
2742

2743
    ptr= field->ptr;
pekka@mysql.com's avatar
pekka@mysql.com committed
2744 2745

    if (! (field->flags & BLOB_FLAG))
2746
    {
pekka@mysql.com's avatar
pekka@mysql.com committed
2747 2748
      if (value.rec->isNULL())
      {
2749
        strmov(buf, "NULL");
2750
        goto print_value;
pekka@mysql.com's avatar
pekka@mysql.com committed
2751
      }
2752 2753 2754 2755 2756
      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
2757 2758 2759
    }
    else
    {
2760
      NdbBlob *ndb_blob= value.blob;
2761
      bool isNull= TRUE;
pekka@mysql.com's avatar
pekka@mysql.com committed
2762
      ndb_blob->getNull(isNull);
2763 2764
      if (isNull)
        strmov(buf, "NULL");
2765
    }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
2766

2767
print_value:
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
2768
    DBUG_PRINT("value", ("%u,%s: %s", f, field->field_name, buf));
2769 2770 2771 2772 2773 2774 2775 2776
  }
#endif
  DBUG_VOID_RETURN;
}


int ha_ndbcluster::index_init(uint index)
{
2777
  DBUG_ENTER("ha_ndbcluster::index_init");
2778
  DBUG_PRINT("enter", ("index: %u", index));
2779 2780 2781 2782 2783 2784
 /*
    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;
2785 2786 2787 2788 2789 2790
  DBUG_RETURN(handler::index_init(index));
}


int ha_ndbcluster::index_end()
{
2791
  DBUG_ENTER("ha_ndbcluster::index_end");
2792
  DBUG_RETURN(close_scan());
2793 2794
}

2795 2796 2797 2798 2799 2800 2801 2802
/**
 * 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;
2803
  const byte* end_ptr= key + key_len;
2804 2805 2806 2807 2808 2809
  curr_part= key_info->key_part;
  end_part= curr_part + key_info->key_parts;
  

  for (; curr_part != end_part && key < end_ptr; curr_part++)
  {
2810
    if (curr_part->null_bit && *key)
2811 2812 2813 2814 2815 2816
      return 1;

    key += curr_part->store_length;
  }
  return 0;
}
2817 2818

int ha_ndbcluster::index_read(byte *buf,
2819 2820
                              const byte *key, uint key_len, 
                              enum ha_rkey_function find_flag)
2821
{
2822
  DBUG_ENTER("ha_ndbcluster::index_read");
2823 2824 2825
  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
2826
  int error;
2827 2828
  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
2829 2830 2831 2832 2833
  switch (type){
  case PRIMARY_KEY_ORDERED_INDEX:
  case PRIMARY_KEY_INDEX:
    if (find_flag == HA_READ_KEY_EXACT && key_info->key_length == key_len)
    {
2834
      if (m_active_cursor && (error= close_scan()))
2835
        DBUG_RETURN(error);
joreland@mysql.com's avatar
joreland@mysql.com committed
2836 2837 2838 2839 2840 2841 2842 2843 2844
      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:
2845
    if (find_flag == HA_READ_KEY_EXACT && key_info->key_length == key_len &&
2846
        !check_null_in_key(key_info, key, key_len))
joreland@mysql.com's avatar
joreland@mysql.com committed
2847
    {
2848
      if (m_active_cursor && (error= close_scan()))
2849
        DBUG_RETURN(error);
joreland@mysql.com's avatar
joreland@mysql.com committed
2850 2851 2852 2853
      DBUG_RETURN(unique_index_read(key, key_len, buf));
    }
    else if (type == UNIQUE_INDEX)
    {
2854
      DBUG_RETURN(unique_index_scan(key_info, key, key_len, buf));
joreland@mysql.com's avatar
joreland@mysql.com committed
2855 2856 2857 2858 2859 2860
    }
    break;
  case ORDERED_INDEX:
    break;
  default:
  case UNDEFINED_INDEX:
2861
    DBUG_ASSERT(FALSE);
2862
    DBUG_RETURN(1);
joreland@mysql.com's avatar
joreland@mysql.com committed
2863 2864 2865
    break;
  }
  
2866
  key_range start_key;
2867 2868 2869
  start_key.key= key;
  start_key.length= key_len;
  start_key.flag= find_flag;
2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881
  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
2882
  DBUG_RETURN(error == HA_ERR_END_OF_FILE ? HA_ERR_KEY_NOT_FOUND : error);
2883 2884 2885 2886
}


int ha_ndbcluster::index_read_idx(byte *buf, uint index_no, 
2887 2888
                              const byte *key, uint key_len, 
                              enum ha_rkey_function find_flag)
2889
{
2890
  statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status);
2891
  DBUG_ENTER("ha_ndbcluster::index_read_idx");
2892 2893 2894 2895 2896 2897 2898 2899
  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)
{
2900
  DBUG_ENTER("ha_ndbcluster::index_next");
2901
  statistic_increment(current_thd->status_var.ha_read_next_count,
2902
                      &LOCK_status);
2903
  DBUG_RETURN(next_result(buf));
2904 2905 2906 2907 2908
}


int ha_ndbcluster::index_prev(byte *buf)
{
2909
  DBUG_ENTER("ha_ndbcluster::index_prev");
2910
  statistic_increment(current_thd->status_var.ha_read_prev_count,
2911
                      &LOCK_status);
2912
  DBUG_RETURN(next_result(buf));
2913 2914 2915 2916 2917
}


int ha_ndbcluster::index_first(byte *buf)
{
2918
  DBUG_ENTER("ha_ndbcluster::index_first");
2919
  statistic_increment(current_thd->status_var.ha_read_first_count,
2920
                      &LOCK_status);
2921 2922 2923
  // Start the ordered index scan and fetch the first row

  // Only HA_READ_ORDER indexes get called by index_first
2924
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, FALSE, buf));
2925 2926 2927 2928 2929
}


int ha_ndbcluster::index_last(byte *buf)
{
2930
  DBUG_ENTER("ha_ndbcluster::index_last");
2931
  statistic_increment(current_thd->status_var.ha_read_last_count,&LOCK_status);
2932
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, TRUE, buf));
2933 2934
}

2935 2936 2937 2938 2939
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));
}
2940

2941 2942
inline
int ha_ndbcluster::read_range_first_to_buf(const key_range *start_key,
2943 2944 2945
                                           const key_range *end_key,
                                           bool eq_r, bool sorted,
                                           byte* buf)
2946
{
2947 2948
   ndb_index_type type= get_index_type(active_index);
KEY* key_info;
2949 2950
  int error= 1; 
  DBUG_ENTER("ha_ndbcluster::read_range_first_to_buf");
2951
  DBUG_PRINT("info", ("eq_r: %d, sorted: %d", eq_r, sorted));
2952

2953
  switch (type){
2954
  case PRIMARY_KEY_ORDERED_INDEX:
2955
  case PRIMARY_KEY_INDEX:
2956 2957
    key_info= table->key_info + active_index;
    if (start_key && 
2958 2959
        start_key->length == key_info->key_length &&
        start_key->flag == HA_READ_KEY_EXACT)
2960
    {
2961
      if (m_active_cursor && (error= close_scan()))
2962
        DBUG_RETURN(error);
2963 2964 2965
      error= pk_read(start_key->key, start_key->length, buf);      
      DBUG_RETURN(error == HA_ERR_KEY_NOT_FOUND ? HA_ERR_END_OF_FILE : error);
    }
2966
    break;
2967
  case UNIQUE_ORDERED_INDEX:
2968
  case UNIQUE_INDEX:
2969
    key_info= table->key_info + active_index;
2970
    if (start_key && start_key->length == key_info->key_length &&
2971 2972
        start_key->flag == HA_READ_KEY_EXACT && 
        !check_null_in_key(key_info, start_key->key, start_key->length))
2973
    {
2974
      if (m_active_cursor && (error= close_scan()))
2975
        DBUG_RETURN(error);
2976 2977 2978
      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);
    }
2979
    else if (type == UNIQUE_INDEX)
2980 2981 2982 2983
      DBUG_RETURN(unique_index_scan(key_info, 
				    start_key->key, 
				    start_key->length, 
				    buf));
2984 2985 2986 2987
    break;
  default:
    break;
  }
2988 2989

  // Start the ordered index scan and fetch the first row
2990
  error= ordered_index_scan(start_key, end_key, sorted, FALSE, buf);
2991 2992 2993
  DBUG_RETURN(error);
}

2994

joreland@mysql.com's avatar
joreland@mysql.com committed
2995
int ha_ndbcluster::read_range_first(const key_range *start_key,
2996 2997
                                    const key_range *end_key,
                                    bool eq_r, bool sorted)
joreland@mysql.com's avatar
joreland@mysql.com committed
2998 2999 3000 3001 3002
{
  byte* buf= table->record[0];
  DBUG_ENTER("ha_ndbcluster::read_range_first");
  
  DBUG_RETURN(read_range_first_to_buf(start_key,
3003 3004 3005 3006
                                      end_key,
                                      eq_r, 
                                      sorted,
                                      buf));
joreland@mysql.com's avatar
joreland@mysql.com committed
3007 3008
}

3009
int ha_ndbcluster::read_range_next()
3010 3011 3012 3013 3014 3015
{
  DBUG_ENTER("ha_ndbcluster::read_range_next");
  DBUG_RETURN(next_result(table->record[0]));
}


3016 3017
int ha_ndbcluster::rnd_init(bool scan)
{
3018
  NdbScanOperation *cursor= m_active_cursor;
3019 3020
  DBUG_ENTER("rnd_init");
  DBUG_PRINT("enter", ("scan: %d", scan));
3021
  // Check if scan is to be restarted
mskold@mysql.com's avatar
mskold@mysql.com committed
3022 3023 3024 3025
  if (cursor)
  {
    if (!scan)
      DBUG_RETURN(1);
3026
    if (cursor->restart(m_force_send) != 0)
3027 3028 3029 3030
    {
      DBUG_ASSERT(0);
      DBUG_RETURN(-1);
    }
mskold@mysql.com's avatar
mskold@mysql.com committed
3031
  }
3032
  index_init(table->s->primary_key);
3033 3034 3035
  DBUG_RETURN(0);
}

3036 3037
int ha_ndbcluster::close_scan()
{
3038
  NdbTransaction *trans= m_active_trans;
3039 3040
  DBUG_ENTER("close_scan");

3041 3042
  m_multi_cursor= 0;
  if (!m_active_cursor && !m_multi_cursor)
3043 3044
    DBUG_RETURN(1);

3045
  NdbScanOperation *cursor= m_active_cursor ? m_active_cursor : m_multi_cursor;
3046
  
3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058
  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"));
      
3059
      if (!(op= cursor->lockCurrentTuple()))
3060 3061 3062 3063 3064 3065
      {
	m_lock_tuple= false;
	ERR_RETURN(trans->getNdbError());
      }
      m_ops_pending++;      
  }
3066
  m_lock_tuple= false;
3067
  if (m_ops_pending)
3068 3069 3070 3071 3072
  {
    /*
      Take over any pending transactions to the 
      deleteing/updating transaction before closing the scan    
    */
3073
    DBUG_PRINT("info", ("ops_pending: %ld", (long) m_ops_pending));    
3074
    if (execute_no_commit(this,trans,false) != 0) {
3075
      no_uncommitted_rows_execute_failure();
3076
      DBUG_RETURN(ndb_err(trans));
3077
    }
3078
    m_ops_pending= 0;
3079 3080
  }
  
3081
  cursor->close(m_force_send, TRUE);
3082
  m_active_cursor= m_multi_cursor= NULL;
mskold@mysql.com's avatar
mskold@mysql.com committed
3083
  DBUG_RETURN(0);
3084
}
3085 3086 3087 3088

int ha_ndbcluster::rnd_end()
{
  DBUG_ENTER("rnd_end");
3089
  DBUG_RETURN(close_scan());
3090 3091 3092 3093 3094 3095
}


int ha_ndbcluster::rnd_next(byte *buf)
{
  DBUG_ENTER("rnd_next");
3096
  statistic_increment(current_thd->status_var.ha_read_rnd_next_count,
3097
                      &LOCK_status);
3098

3099
  if (!m_active_cursor)
3100 3101
    DBUG_RETURN(full_table_scan(buf));
  DBUG_RETURN(next_result(buf));
3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114
}


/*
  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");
3115
  statistic_increment(current_thd->status_var.ha_read_rnd_count,
3116
                      &LOCK_status);
3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136
  // 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");

3137
  if (table->s->primary_key != MAX_KEY) 
3138
  {
3139
    key_info= table->key_info + table->s->primary_key;
3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154
    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;
      }
3155 3156 3157 3158

      size_t len = key_part->length;
      const byte * ptr = record + key_part->offset;
      Field *field = key_part->field;
3159
      if (field->type() ==  MYSQL_TYPE_VARCHAR)
3160
      {
3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174
        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;
3175 3176 3177
      }
      else
      {
3178
        memcpy(buff, ptr, len);
3179 3180
      }
      buff += len;
3181 3182 3183 3184 3185 3186
    }
  } 
  else 
  {
    // No primary key, get hidden key
    DBUG_PRINT("info", ("Getting hidden key"));
3187
#ifndef DBUG_OFF
3188
    int hidden_no= table->s->fields;
joreland@mysql.com's avatar
joreland@mysql.com committed
3189
    const NDBTAB *tab= (const NDBTAB *) m_table;  
3190 3191 3192 3193
    const NDBCOL *hidden_col= tab->getColumn(hidden_no);
    DBUG_ASSERT(hidden_col->getPrimaryKey() && 
                hidden_col->getAutoIncrement() &&
                ref_length == NDB_HIDDEN_PRIMARY_KEY_LENGTH);
3194
#endif
3195
    memcpy(ref, m_ref, ref_length);
3196 3197 3198 3199 3200 3201 3202
  }
  
  DBUG_DUMP("ref", (char*)ref, ref_length);
  DBUG_VOID_RETURN;
}


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

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

  DBUG_RETURN(result);
3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296
}


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"));
3297
    reset();
3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 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
    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"));
3367 3368
    DBUG_PRINT("info", ("Ignoring duplicate key"));
    m_ignore_dup_key= TRUE;
3369 3370 3371
    break;
  case HA_EXTRA_NO_IGNORE_DUP_KEY:
    DBUG_PRINT("info", ("HA_EXTRA_NO_IGNORE_DUP_KEY"));
3372
    m_ignore_dup_key= FALSE;
3373 3374
    break;
  case HA_EXTRA_RETRIEVE_ALL_COLS:    /* Retrieve all columns, not just those
3375 3376
                                         where field->query_id is the same as
                                         the current query id */
3377
    DBUG_PRINT("info", ("HA_EXTRA_RETRIEVE_ALL_COLS"));
3378
    m_retrieve_all_fields= TRUE;
3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390
    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"));
3391
    m_retrieve_primary_key= TRUE;
3392 3393 3394 3395 3396 3397
    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"));
3398 3399
  case HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
    DBUG_PRINT("info", ("HA_EXTRA_KEYREAD_PRESERVE_FIELDS"));
3400
    break;
3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413
  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;
3414 3415 3416 3417 3418 3419 3420 3421
  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;
3422 3423
  default:
    break;
3424 3425 3426 3427 3428
  }
  
  DBUG_RETURN(0);
}

3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439

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;
3440 3441
  m_delete_cannot_batch= FALSE;
  m_update_cannot_batch= FALSE;
3442 3443 3444 3445
  DBUG_RETURN(0);
}


3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458
/* 
   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
3459
  const NDBTAB *tab= (const NDBTAB *) m_table;    
3460 3461

  DBUG_ENTER("start_bulk_insert");
pekka@mysql.com's avatar
pekka@mysql.com committed
3462
  DBUG_PRINT("enter", ("rows: %d", (int)rows));
3463
  
3464
  m_rows_inserted= (ha_rows) 0;
3465
  if (!m_use_write && m_ignore_dup_key)
3466 3467 3468
  {
    /*
      compare if expression with that in write_row
3469
      we have a situation where peek_indexed_rows() will be called
3470 3471 3472 3473 3474 3475 3476 3477
      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;
  }
3478
  if (rows == (ha_rows) 0)
3479
  {
3480 3481
    /* We don't know how many will be inserted, guess */
    m_rows_to_insert= m_autoincrement_prefetch;
3482
  }
3483 3484
  else
    m_rows_to_insert= rows; 
3485 3486 3487 3488 3489 3490 3491 3492

  /* 
    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.   
  */
3493
  const int bytesperbatch= 8192;
3494
  bytes= 12 + tab->getRowSizeInBytes() + 4 * tab->getNoOfColumns();
3495
  batch= bytesperbatch/bytes;
3496 3497
  batch= batch == 0 ? 1 : batch;
  DBUG_PRINT("info", ("batch: %d, bytes: %d", batch, bytes));
3498
  m_bulk_insert_rows= batch;
3499 3500 3501 3502 3503 3504 3505 3506 3507

  DBUG_VOID_RETURN;
}

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

3510
  DBUG_ENTER("end_bulk_insert");
3511
  // Check if last inserts need to be flushed
3512
  if (m_bulk_insert_not_flushed)
3513
  {
3514
    NdbTransaction *trans= m_active_trans;
3515 3516 3517
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
                        "rows_inserted:%d, bulk_insert_rows: %d", 
3518
                        (int) m_rows_inserted, (int) m_bulk_insert_rows)); 
3519
    m_bulk_insert_not_flushed= FALSE;
3520 3521
    if (m_transaction_on)
    {
3522
      if (execute_no_commit(this, trans,false) != 0)
3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534
      {
        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);
      }
3535 3536
      else
      {
3537
        IF_DBUG(int res=) trans->restart();
3538 3539
        DBUG_ASSERT(res == 0);
      }
3540
    }
3541 3542
  }

3543 3544
  m_rows_inserted= (ha_rows) 0;
  m_rows_to_insert= (ha_rows) 1;
3545
  DBUG_RETURN(error);
3546 3547
}

3548 3549 3550 3551

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
3552
  DBUG_PRINT("enter", ("cache_size: %lu", cache_size));
3553 3554 3555
  DBUG_RETURN(extra(operation));
}

3556 3557 3558 3559
static const char *ha_ndbcluster_exts[] = {
 ha_ndb_ext,
 NullS
};
3560

3561
const char** ha_ndbcluster::bas_ext() const
3562 3563 3564
{
  return ha_ndbcluster_exts;
}
3565 3566 3567 3568 3569 3570 3571 3572 3573

/*
  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()
{
3574 3575 3576
  DBUG_ENTER("ha_ndbcluster::scan_time()");
  double res= rows2double(records*1000);
  DBUG_PRINT("exit", ("table: %s value: %f", 
3577
                      m_tabname, res));
3578
  DBUG_RETURN(res);
3579 3580
}

3581 3582 3583 3584 3585 3586 3587
/*
  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
*/
3588 3589 3590 3591 3592 3593 3594 3595

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

3597 3598 3599
    /* If we are not doing a LOCK TABLE, then allow multiple
       writers */
    
3600 3601 3602
    /* Since NDB does not currently have table locks
       this is treated as a ordinary lock */

3603
    if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618
         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;
3619 3620

  DBUG_PRINT("exit", ("lock_type: %d", lock_type));
3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642
  
  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
3643
  for the statement, this will be stored in thd_ndb.stmt.
3644
  If not, we have to start a master transaction if there doesn't exist
3645
  one from before, this will be stored in thd_ndb.all
3646 3647 3648
 
  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  
3649
  If we are locking the table then:
3650
  - save the NdbDictionary::Table for easy access
3651 3652
  - save reference to table statistics
  - refresh list of the indexes for the table if needed (if altered)
3653 3654 3655 3656 3657
 */

int ha_ndbcluster::external_lock(THD *thd, int lock_type)
{
  int error=0;
3658
  NdbTransaction* trans= NULL;
3659 3660 3661 3662 3663 3664

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

3668
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
3669
  Ndb *ndb= thd_ndb->ndb;
3670

3671 3672
  DBUG_PRINT("enter", ("thd: 0x%lx  thd_ndb: 0x%lx  thd_ndb->lock_count: %d",
                       (long) thd, (long) thd_ndb, thd_ndb->lock_count));
3673

3674 3675
  if (lock_type != F_UNLCK)
  {
3676
    DBUG_PRINT("info", ("lock_type != F_UNLCK"));
3677 3678 3679 3680 3681 3682 3683 3684
    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)
3685 3686 3687
      m_transaction_on= FALSE;
    else
      m_transaction_on= thd->variables.ndb_use_transactions;
3688
    if (!thd_ndb->lock_count++)
3689 3690
    {
      PRINT_OPTION_FLAGS(thd);
3691
      if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) 
3692 3693
      {
        // Autocommit transaction
3694
        DBUG_ASSERT(!thd_ndb->stmt);
3695 3696
        DBUG_PRINT("trans",("Starting transaction stmt"));      

3697
        trans= ndb->startTransaction();
3698
        if (trans == NULL)
3699
          ERR_RETURN(ndb->getNdbError());
3700
        no_uncommitted_rows_reset(thd);
3701
        thd_ndb->stmt= trans;
3702
	thd_ndb->query_state&= NDB_QUERY_NORMAL;
3703
        trans_register_ha(thd, FALSE, &ndbcluster_hton);
3704 3705 3706
      } 
      else 
      { 
3707
        if (!thd_ndb->all)
3708
        {
3709 3710 3711 3712
          // Not autocommit transaction
          // A "master" transaction ha not been started yet
          DBUG_PRINT("trans",("starting transaction, all"));
          
3713
          trans= ndb->startTransaction();
3714
          if (trans == NULL)
3715
            ERR_RETURN(ndb->getNdbError());
3716
          no_uncommitted_rows_reset(thd);
3717
          thd_ndb->all= trans; 
3718
	  thd_ndb->query_state&= NDB_QUERY_NORMAL;
3719
          trans_register_ha(thd, TRUE, &ndbcluster_hton);
3720 3721 3722 3723 3724 3725 3726 3727

          /*
            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))
3728
          {
3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747
            //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. 
     */

3748 3749 3750
    // 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;
3751 3752
    m_autoincrement_prefetch= 
      (ha_rows) thd->variables.ndb_autoincrement_prefetch_sz;
3753

3754
    m_active_trans= thd_ndb->all ? thd_ndb->all : thd_ndb->stmt;
3755
    DBUG_ASSERT(m_active_trans);
3756
    // Start of transaction
3757
    m_rows_changed= 0;
3758
    m_retrieve_all_fields= FALSE;
3759
    m_retrieve_primary_key= FALSE;
3760
    m_ops_pending= 0;
3761
    {
3762
      NDBDICT *dict= ndb->getDictionary();
3763 3764 3765
      const NDBTAB *tab;
      void *tab_info;
      if (!(tab= dict->getTable(m_tabname, &tab_info)))
3766
        ERR_RETURN(dict->getNdbError());
3767 3768 3769
      DBUG_PRINT("info", ("Table schema version: %d", 
                          tab->getObjectVersion()));
      // Check if thread has stale local cache
3770 3771 3772 3773
      // New transaction must not use old tables... (trans != 0)
      // Running might...
      if ((trans && tab->getObjectStatus() != NdbDictionary::Object::Retrieved)
	  || tab->getObjectStatus() == NdbDictionary::Object::Invalid)
3774 3775
      {
        invalidate_dictionary_cache(FALSE);
3776
        if (!(tab= dict->getTable(m_tabname, &tab_info)))
3777 3778 3779 3780
          ERR_RETURN(dict->getNdbError());
        DBUG_PRINT("info", ("Table schema version: %d", 
                            tab->getObjectVersion()));
      }
3781
      if (m_table_version < tab->getObjectVersion())
3782 3783 3784 3785 3786 3787 3788
      {
        /*
          The table has been altered, caller has to retry
        */
        NdbError err= ndb->getNdbError(NDB_INVALID_SCHEMA_OBJECT);
        DBUG_RETURN(ndb_to_mysql_error(&err));
      }
3789 3790 3791 3792
      if (m_table != (void *)tab)
      {
        m_table= (void *)tab;
        m_table_version = tab->getObjectVersion();
3793
        if ((my_errno= build_index_list(ndb, table, ILBP_OPEN)))
3794
          DBUG_RETURN(my_errno);
3795

3796
        const void *data= NULL, *pack_data= NULL;
3797
        uint length, pack_length;
3798
        if (readfrm(table->s->path, &data, &length) ||
3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809
            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));
3810
      }
3811 3812
      m_table_info= tab_info;
    }
3813
    no_uncommitted_rows_init(thd);
3814 3815
  }
  else
3816
  {
3817
    DBUG_PRINT("info", ("lock_type == F_UNLCK"));
3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835

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

3836
    if (!--thd_ndb->lock_count)
3837 3838 3839 3840
    {
      DBUG_PRINT("trans", ("Last external_lock"));
      PRINT_OPTION_FLAGS(thd);

3841
      if (thd_ndb->stmt)
3842 3843 3844 3845 3846 3847 3848
      {
        /*
          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"));
3849
        ndb->closeTransaction(m_active_trans);
3850
        thd_ndb->stmt= NULL;
3851 3852
      }
    }
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
3853
    m_table_info= NULL;
3854

3855 3856 3857 3858 3859 3860 3861 3862 3863
    /*
      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;    

3864 3865
    if (m_active_cursor)
      DBUG_PRINT("warning", ("m_active_cursor != NULL"));
3866 3867
    m_active_cursor= NULL;

3868 3869 3870 3871
    if (m_multi_cursor)
      DBUG_PRINT("warning", ("m_multi_cursor != NULL"));
    m_multi_cursor= NULL;
    
3872
    if (m_blobs_pending)
3873
      DBUG_PRINT("warning", ("blobs_pending != 0"));
3874
    m_blobs_pending= 0;
3875
    
3876
    if (m_ops_pending)
3877
      DBUG_PRINT("warning", ("ops_pending != 0L"));
3878
    m_ops_pending= 0;
3879 3880 3881 3882
  }
  DBUG_RETURN(error);
}

3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898
/*
  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;
}

3899
/*
3900 3901 3902 3903 3904
  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
3905 3906
*/

serg@serg.mylan's avatar
serg@serg.mylan committed
3907
int ha_ndbcluster::start_stmt(THD *thd, thr_lock_type lock_type)
3908 3909 3910 3911 3912
{
  int error=0;
  DBUG_ENTER("start_stmt");
  PRINT_OPTION_FLAGS(thd);

3913
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
3914
  NdbTransaction *trans= (thd_ndb->stmt)?thd_ndb->stmt:thd_ndb->all;
3915
  if (!trans){
3916
    Ndb *ndb= thd_ndb->ndb;
3917
    DBUG_PRINT("trans",("Starting transaction stmt"));  
3918
    trans= ndb->startTransaction();
3919
    if (trans == NULL)
3920
      ERR_RETURN(ndb->getNdbError());
3921
    no_uncommitted_rows_reset(thd);
3922
    thd_ndb->stmt= trans;
3923
    thd_ndb->query_state&= NDB_QUERY_NORMAL;
3924
    trans_register_ha(thd, FALSE, &ndbcluster_hton);
3925 3926
  }
  m_active_trans= trans;
3927
  // Start of statement
3928
  m_retrieve_all_fields= FALSE;
3929
  m_retrieve_primary_key= FALSE;
3930
  m_ops_pending= 0;    
3931 3932 3933 3934 3935 3936
  
  DBUG_RETURN(error);
}


/*
3937
  Commit a transaction started in NDB
3938 3939
 */

3940
int ndbcluster_commit(THD *thd, bool all)
3941 3942
{
  int res= 0;
3943 3944 3945
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  Ndb *ndb= thd_ndb->ndb;
  NdbTransaction *trans= all ? thd_ndb->all : thd_ndb->stmt;
3946 3947 3948

  DBUG_ENTER("ndbcluster_commit");
  DBUG_PRINT("transaction",("%s",
3949
                            trans == thd_ndb->stmt ?
3950 3951 3952
                            "stmt" : "all"));
  DBUG_ASSERT(ndb && trans);

3953
  if (execute_commit(thd,trans) != 0)
3954 3955
  {
    const NdbError err= trans->getNdbError();
3956
    const NdbOperation *error_op= trans->getNdbErrorOperation();
3957
    ERR_PRINT(err);
3958
    res= ndb_to_mysql_error(&err);
3959
    if (res != -1)
3960
      ndbcluster_print_error(res, error_op);
3961
  }
3962
  ndb->closeTransaction(trans);
3963

3964
  if (all)
3965 3966 3967
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;
3968 3969 3970 3971 3972 3973 3974

  /* 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);
3975 3976
    DBUG_PRINT("info", ("Invalidate commit_count for %s, share->commit_count: %lu",
                        share->table_name, (ulong) share->commit_count));
3977 3978 3979 3980 3981 3982
    share->commit_count= 0;
    share->commit_count_lock++;
    pthread_mutex_unlock(&share->mutex);
  }
  thd_ndb->changed_tables.empty();

3983 3984 3985 3986 3987 3988 3989 3990
  DBUG_RETURN(res);
}


/*
  Rollback a transaction started in NDB
 */

3991
int ndbcluster_rollback(THD *thd, bool all)
3992 3993
{
  int res= 0;
3994 3995 3996
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  Ndb *ndb= thd_ndb->ndb;
  NdbTransaction *trans= all ? thd_ndb->all : thd_ndb->stmt;
3997 3998 3999

  DBUG_ENTER("ndbcluster_rollback");
  DBUG_PRINT("transaction",("%s",
4000
                            trans == thd_ndb->stmt ? 
4001 4002 4003
                            "stmt" : "all"));
  DBUG_ASSERT(ndb && trans);

4004
  if (trans->execute(NdbTransaction::Rollback) != 0)
4005 4006
  {
    const NdbError err= trans->getNdbError();
4007
    const NdbOperation *error_op= trans->getNdbErrorOperation();
4008 4009
    ERR_PRINT(err);     
    res= ndb_to_mysql_error(&err);
4010 4011
    if (res != -1) 
      ndbcluster_print_error(res, error_op);
4012 4013
  }
  ndb->closeTransaction(trans);
4014

4015
  if (all)
4016 4017 4018 4019
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;

4020 4021 4022
  /* Clear list of tables changed by transaction */
  thd_ndb->changed_tables.empty();

4023
  DBUG_RETURN(res);
4024 4025 4026 4027
}


/*
pekka@mysql.com's avatar
pekka@mysql.com committed
4028 4029 4030
  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
4031 4032 4033

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

pekka@mysql.com's avatar
pekka@mysql.com committed
4036 4037 4038
static int create_ndb_column(NDBCOL &col,
                             Field *field,
                             HA_CREATE_INFO *info)
4039
{
pekka@mysql.com's avatar
pekka@mysql.com committed
4040
  // Set name
msvensson@neptunus.(none)'s avatar
msvensson@neptunus.(none) committed
4041
  col.setName(field->field_name);
pekka@mysql.com's avatar
pekka@mysql.com committed
4042 4043
  // Get char set
  CHARSET_INFO *cs= field->charset();
pekka@mysql.com's avatar
pekka@mysql.com committed
4044 4045 4046 4047
  // Set type and sizes
  const enum enum_field_types mysql_type= field->real_type();
  switch (mysql_type) {
  // Numeric types
4048
  case MYSQL_TYPE_TINY:        
pekka@mysql.com's avatar
pekka@mysql.com committed
4049 4050 4051 4052 4053 4054
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Tinyunsigned);
    else
      col.setType(NDBCOL::Tinyint);
    col.setLength(1);
    break;
4055
  case MYSQL_TYPE_SHORT:
pekka@mysql.com's avatar
pekka@mysql.com committed
4056 4057 4058 4059 4060 4061
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Smallunsigned);
    else
      col.setType(NDBCOL::Smallint);
    col.setLength(1);
    break;
4062
  case MYSQL_TYPE_LONG:
pekka@mysql.com's avatar
pekka@mysql.com committed
4063 4064 4065 4066 4067 4068
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Unsigned);
    else
      col.setType(NDBCOL::Int);
    col.setLength(1);
    break;
4069
  case MYSQL_TYPE_INT24:       
pekka@mysql.com's avatar
pekka@mysql.com committed
4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081
    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);
4082 4083
    break;
  case MYSQL_TYPE_FLOAT:
pekka@mysql.com's avatar
pekka@mysql.com committed
4084 4085 4086
    col.setType(NDBCOL::Float);
    col.setLength(1);
    break;
4087
  case MYSQL_TYPE_DOUBLE:
pekka@mysql.com's avatar
pekka@mysql.com committed
4088 4089 4090
    col.setType(NDBCOL::Double);
    col.setLength(1);
    break;
4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110
  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;
4111 4112 4113
  case MYSQL_TYPE_NEWDECIMAL:    
    {
      Field_new_decimal *f= (Field_new_decimal*)field;
4114
      uint precision= f->precision;
4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128
      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
4129 4130 4131 4132 4133
  // Date types
  case MYSQL_TYPE_DATETIME:    
    col.setType(NDBCOL::Datetime);
    col.setLength(1);
    break;
4134 4135 4136 4137
  case MYSQL_TYPE_DATE: // ?
    col.setType(NDBCOL::Char);
    col.setLength(field->pack_length());
    break;
pekka@mysql.com's avatar
pekka@mysql.com committed
4138
  case MYSQL_TYPE_NEWDATE:
4139 4140 4141
    col.setType(NDBCOL::Date);
    col.setLength(1);
    break;
pekka@mysql.com's avatar
pekka@mysql.com committed
4142
  case MYSQL_TYPE_TIME:        
4143 4144 4145
    col.setType(NDBCOL::Time);
    col.setLength(1);
    break;
4146 4147 4148 4149 4150 4151 4152
  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
4153 4154 4155
    break;
  // Char types
  case MYSQL_TYPE_STRING:      
4156
    if (field->pack_length() == 0)
4157 4158 4159 4160
    {
      col.setType(NDBCOL::Bit);
      col.setLength(1);
    }
pekka@mysql.com's avatar
pekka@mysql.com committed
4161
    else if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
4162
    {
pekka@mysql.com's avatar
pekka@mysql.com committed
4163
      col.setType(NDBCOL::Binary);
4164
      col.setLength(field->pack_length());
pekka@mysql.com's avatar
pekka@mysql.com committed
4165
    }
4166
    else
4167 4168 4169
    {
      col.setType(NDBCOL::Char);
      col.setCharset(cs);
4170
      col.setLength(field->pack_length());
4171
    }
pekka@mysql.com's avatar
pekka@mysql.com committed
4172
    break;
pekka@mysql.com's avatar
pekka@mysql.com committed
4173 4174 4175 4176 4177 4178
  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
4179
        if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
pekka@mysql.com's avatar
pekka@mysql.com committed
4180 4181 4182 4183 4184 4185 4186 4187
          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
4188
        if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
pekka@mysql.com's avatar
pekka@mysql.com committed
4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199
          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
4200
    }
pekka@mysql.com's avatar
pekka@mysql.com committed
4201 4202 4203 4204
    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
4205
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
pekka@mysql.com's avatar
pekka@mysql.com committed
4206
      col.setType(NDBCOL::Blob);
pekka@mysql.com's avatar
pekka@mysql.com committed
4207
    else {
pekka@mysql.com's avatar
pekka@mysql.com committed
4208
      col.setType(NDBCOL::Text);
pekka@mysql.com's avatar
pekka@mysql.com committed
4209 4210
      col.setCharset(cs);
    }
pekka@mysql.com's avatar
pekka@mysql.com committed
4211 4212 4213 4214 4215
    col.setInlineSize(256);
    // No parts
    col.setPartSize(0);
    col.setStripeSize(0);
    break;
4216
  //mysql_type_blob:
4217
  case MYSQL_TYPE_GEOMETRY:
pekka@mysql.com's avatar
pekka@mysql.com committed
4218
  case MYSQL_TYPE_BLOB:    
pekka@mysql.com's avatar
pekka@mysql.com committed
4219
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
pekka@mysql.com's avatar
pekka@mysql.com committed
4220
      col.setType(NDBCOL::Blob);
pekka@mysql.com's avatar
pekka@mysql.com committed
4221
    else {
pekka@mysql.com's avatar
pekka@mysql.com committed
4222
      col.setType(NDBCOL::Text);
pekka@mysql.com's avatar
pekka@mysql.com committed
4223 4224
      col.setCharset(cs);
    }
pekka@mysql.com's avatar
pekka@mysql.com committed
4225
    {
4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246
      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
4247 4248 4249 4250
    }
    break;
  mysql_type_medium_blob:
  case MYSQL_TYPE_MEDIUM_BLOB:   
pekka@mysql.com's avatar
pekka@mysql.com committed
4251
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
pekka@mysql.com's avatar
pekka@mysql.com committed
4252
      col.setType(NDBCOL::Blob);
pekka@mysql.com's avatar
pekka@mysql.com committed
4253
    else {
pekka@mysql.com's avatar
pekka@mysql.com committed
4254
      col.setType(NDBCOL::Text);
pekka@mysql.com's avatar
pekka@mysql.com committed
4255 4256
      col.setCharset(cs);
    }
pekka@mysql.com's avatar
pekka@mysql.com committed
4257 4258 4259 4260 4261 4262
    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
4263
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
pekka@mysql.com's avatar
pekka@mysql.com committed
4264
      col.setType(NDBCOL::Blob);
pekka@mysql.com's avatar
pekka@mysql.com committed
4265
    else {
pekka@mysql.com's avatar
pekka@mysql.com committed
4266
      col.setType(NDBCOL::Text);
pekka@mysql.com's avatar
pekka@mysql.com committed
4267 4268
      col.setCharset(cs);
    }
pekka@mysql.com's avatar
pekka@mysql.com committed
4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281
    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;
4282 4283
  case MYSQL_TYPE_BIT:
  {
4284
    int no_of_bits= field->field_length;
4285 4286 4287 4288 4289 4290 4291
    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
4292 4293 4294 4295 4296
  case MYSQL_TYPE_NULL:        
    goto mysql_type_unsupported;
  mysql_type_unsupported:
  default:
    return HA_ERR_UNSUPPORTED;
4297
  }
pekka@mysql.com's avatar
pekka@mysql.com committed
4298 4299 4300 4301 4302 4303
  // Set nullable and pk
  col.setNullable(field->maybe_null());
  col.setPrimaryKey(field->flags & PRI_KEY_FLAG);
  // Set autoincrement
  if (field->flags & AUTO_INCREMENT_FLAG) 
  {
4304
#ifndef DBUG_OFF
4305
    char buff[22];
4306
#endif
pekka@mysql.com's avatar
pekka@mysql.com committed
4307 4308
    col.setAutoIncrement(TRUE);
    ulonglong value= info->auto_increment_value ?
4309
      info->auto_increment_value : (ulonglong) 1;
4310
    DBUG_PRINT("info", ("Autoincrement key, initial: %s", llstr(value, buff)));
pekka@mysql.com's avatar
pekka@mysql.com committed
4311
    col.setAutoIncrementInitialValue(value);
4312
  }
pekka@mysql.com's avatar
pekka@mysql.com committed
4313
  else
4314
    col.setAutoIncrement(FALSE);
pekka@mysql.com's avatar
pekka@mysql.com committed
4315
  return 0;
4316 4317 4318 4319 4320 4321
}

/*
  Create a table in NDB Cluster
 */

mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4322 4323
static void ndb_set_fragmentation(NDBTAB &tab, TABLE *form, uint pk_length)
{
4324 4325 4326 4327 4328
  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
4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345
    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;
4346 4347 4348 4349 4350
    /*
     * 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
4351
#if MYSQL_VERSION_ID >= 50100
4352
    no_fragments= (big_max_rows*acc_row_size)/acc_fragment_size+1;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4353
#else
4354
    no_fragments= ((big_max_rows*acc_row_size)/acc_fragment_size+1
4355
                   +1/*correct rounding*/)/2;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4356 4357 4358 4359 4360 4361 4362 4363 4364
#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)
4365 4366
        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
4367 4368 4369 4370 4371 4372 4373
    }
    else if (no_fragments > no_nodes)
      ftype= NDBTAB::FragAllMedium;
    else
      ftype= NDBTAB::FragAllSmall;
    tab.setFragmentType(ftype);
  }
4374 4375
  tab.setMaxRows(max_rows);
  tab.setMinRows(min_rows);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4376 4377
}

4378
int ha_ndbcluster::create(const char *name, 
4379
                          TABLE *form, 
4380
                          HA_CREATE_INFO *create_info)
4381 4382 4383
{
  NDBTAB tab;
  NDBCOL col;
joreland@mysql.com's avatar
joreland@mysql.com committed
4384
  uint pack_length, length, i, pk_length= 0;
4385
  const void *data= NULL, *pack_data= NULL;
4386
  char name2[FN_HEADLEN];
4387
  bool create_from_engine= (create_info->table_options & HA_OPTION_CREATE_FROM_ENGINE);
4388

pekka@mysql.com's avatar
pekka@mysql.com committed
4389
  DBUG_ENTER("ha_ndbcluster::create");
4390 4391 4392
  DBUG_PRINT("enter", ("name: %s", name));
  fn_format(name2, name, "", "",2);       // Remove the .frm extension
  set_dbname(name2);
4393 4394
  set_tabname(name2);    

4395 4396 4397 4398 4399 4400
  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);
  }
4401 4402 4403 4404 4405 4406 4407 4408 4409 4410
  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);
  }
4411 4412 4413

  DBUG_PRINT("table", ("name: %s", m_tabname));  
  tab.setName(m_tabname);
4414
  tab.setLogging(!(create_info->options & HA_LEX_CREATE_TMP_TABLE));    
4415 4416 4417 4418 4419
   
  // Save frm data for this table
  if (readfrm(name, &data, &length))
    DBUG_RETURN(1);
  if (packfrm(data, length, &pack_data, &pack_length))
4420 4421
  {
    my_free((char*)data, MYF(0));
4422
    DBUG_RETURN(2);
4423 4424
  }

4425
  DBUG_PRINT("info", ("setFrm data: 0x%lx  len: %d", (long) pack_data, pack_length));
4426 4427 4428 4429
  tab.setFrm(pack_data, pack_length);      
  my_free((char*)data, MYF(0));
  my_free((char*)pack_data, MYF(0));
  
4430
  for (i= 0; i < form->s->fields; i++) 
4431 4432 4433 4434
  {
    Field *field= form->field[i];
    DBUG_PRINT("info", ("name: %s, type: %u, pack_length: %d", 
                        field->field_name, field->real_type(),
4435
                        field->pack_length()));
4436
    if ((my_errno= create_ndb_column(col, field, create_info)))
pekka@mysql.com's avatar
pekka@mysql.com committed
4437
      DBUG_RETURN(my_errno);
4438
    tab.addColumn(col);
4439
    if (col.getPrimaryKey())
joreland@mysql.com's avatar
joreland@mysql.com committed
4440
      pk_length += (field->pack_length() + 3) / 4;
4441 4442 4443
  }
  
  // No primary key, create shadow key as 64 bit, auto increment  
4444
  if (form->s->primary_key == MAX_KEY) 
4445 4446 4447 4448 4449
  {
    DBUG_PRINT("info", ("Generating shadow key"));
    col.setName("$PK");
    col.setType(NdbDictionary::Column::Bigunsigned);
    col.setLength(1);
4450
    col.setNullable(FALSE);
4451 4452 4453
    col.setPrimaryKey(TRUE);
    col.setAutoIncrement(TRUE);
    tab.addColumn(col);
joreland@mysql.com's avatar
joreland@mysql.com committed
4454 4455 4456 4457
    pk_length += 2;
  }
  
  // Make sure that blob tables don't have to big part size
4458
  for (i= 0; i < form->s->fields; i++) 
joreland@mysql.com's avatar
joreland@mysql.com committed
4459 4460 4461 4462 4463 4464 4465
  {
    /**
     * 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()) {
4466
    case MYSQL_TYPE_GEOMETRY:
joreland@mysql.com's avatar
joreland@mysql.com committed
4467 4468 4469 4470
    case MYSQL_TYPE_BLOB:    
    case MYSQL_TYPE_MEDIUM_BLOB:   
    case MYSQL_TYPE_LONG_BLOB: 
    {
4471 4472
      NdbDictionary::Column * column= tab.getColumn(i);
      int size= pk_length + (column->getPartSize()+3)/4 + 7;
4473
      if (size > NDB_MAX_TUPLE_SIZE_IN_WORDS && 
4474
         (pk_length+7) < NDB_MAX_TUPLE_SIZE_IN_WORDS)
joreland@mysql.com's avatar
joreland@mysql.com committed
4475
      {
4476
        size= NDB_MAX_TUPLE_SIZE_IN_WORDS - pk_length - 7;
4477
        column->setPartSize(4*size);
joreland@mysql.com's avatar
joreland@mysql.com committed
4478 4479 4480 4481 4482 4483 4484 4485 4486 4487
      }
      /**
       * 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;
    }
4488
  }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4489 4490 4491

  ndb_set_fragmentation(tab, form, pk_length);

4492
  if ((my_errno= check_ndb_connection()))
4493 4494 4495
    DBUG_RETURN(my_errno);
  
  // Create the table in NDB     
4496 4497
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
4498
  if (dict->createTable(tab) != 0) 
4499 4500 4501 4502 4503 4504 4505 4506
  {
    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));
4507

4508
  // Create secondary indexes
4509
  my_errno= build_index_list(ndb, form, ILBP_CREATE);
4510

4511 4512 4513
  if (!my_errno)
    my_errno= write_ndb_file();

4514 4515 4516 4517
  DBUG_RETURN(my_errno);
}


4518
int ha_ndbcluster::create_ordered_index(const char *name, 
4519
                                        KEY *key_info)
4520
{
4521
  DBUG_ENTER("ha_ndbcluster::create_ordered_index");
4522
  DBUG_RETURN(create_index(name, key_info, FALSE));
4523 4524 4525
}

int ha_ndbcluster::create_unique_index(const char *name, 
4526
                                       KEY *key_info)
4527 4528
{

4529
  DBUG_ENTER("ha_ndbcluster::create_unique_index");
4530
  DBUG_RETURN(create_index(name, key_info, TRUE));
4531 4532 4533
}


4534 4535 4536 4537 4538
/*
  Create an index in NDB Cluster
 */

int ha_ndbcluster::create_index(const char *name, 
4539 4540
                                KEY *key_info,
                                bool unique)
4541
{
4542 4543
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
4544 4545 4546
  KEY_PART_INFO *key_part= key_info->key_part;
  KEY_PART_INFO *end= key_part + key_info->key_parts;
  
4547
  DBUG_ENTER("ha_ndbcluster::create_index");
4548
  DBUG_PRINT("enter", ("name: %s ", name));
4549

4550
  NdbDictionary::Index ndb_index(name);
4551
  if (unique)
4552 4553 4554 4555 4556
    ndb_index.setType(NdbDictionary::Index::UniqueHashIndex);
  else 
  {
    ndb_index.setType(NdbDictionary::Index::OrderedIndex);
    // TODO Only temporary ordered indexes supported
4557
    ndb_index.setLogging(FALSE); 
4558 4559 4560 4561 4562 4563 4564
  }
  ndb_index.setTable(m_tabname);

  for (; key_part != end; key_part++) 
  {
    Field *field= key_part->field;
    DBUG_PRINT("info", ("attr: %s", field->field_name));
msvensson@neptunus.(none)'s avatar
msvensson@neptunus.(none) committed
4565
    ndb_index.addColumnName(field->field_name);
4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581
  }
  
  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)
{
4582
  NDBDICT *dict;
4583
  char new_tabname[FN_HEADLEN];
4584
  char new_dbname[FN_HEADLEN];
4585 4586
  const NDBTAB *orig_tab;
  int result;
4587 4588
  bool recreate_indexes= FALSE;
  NDBDICT::List index_list;
4589 4590

  DBUG_ENTER("ha_ndbcluster::rename_table");
4591
  DBUG_PRINT("info", ("Renaming %s to %s", from, to));
4592
  set_dbname(from);
4593
  set_dbname(to, new_dbname);
4594 4595 4596
  set_tabname(from);
  set_tabname(to, new_tabname);

4597 4598 4599
  if (check_ndb_connection())
    DBUG_RETURN(my_errno= HA_ERR_NO_CONNECTION);

mskold@mysql.com's avatar
mskold@mysql.com committed
4600 4601
  Ndb *ndb= get_ndb();
  dict= ndb->getDictionary();
4602 4603
  if (!(orig_tab= dict->getTable(m_tabname)))
    ERR_RETURN(dict->getNdbError());
4604 4605 4606 4607 4608 4609 4610
  // 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());
  }
4611 4612 4613 4614 4615 4616
  if (my_strcasecmp(system_charset_info, new_dbname, m_dbname))
  {
    dict->listIndexes(index_list, m_tabname);
    recreate_indexes= TRUE;
  }

4617 4618 4619
  m_table= (void *)orig_tab;
  // Change current database to that of target table
  set_dbname(to);
mskold@mysql.com's avatar
mskold@mysql.com committed
4620
  ndb->setDatabaseName(m_dbname);
4621
  if (!(result= alter_table_name(new_tabname)))
4622
  {
4623 4624
    // Rename .ndb file
    result= handler::rename_table(from, to);
4625
  }
4626

4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654
  // 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);
	ndb->setDatabaseName(m_dbname);
	const NDBINDEX * index= dict->getIndex(index_el.name,  *new_tab);
	set_dbname(to);
	ndb->setDatabaseName(m_dbname);
	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);
	ndb->setDatabaseName(m_dbname);
	dict->dropIndex(*index);
    }
  }

4655 4656 4657 4658 4659 4660 4661 4662
  DBUG_RETURN(result);
}


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

4663
int ha_ndbcluster::alter_table_name(const char *to)
4664
{
4665 4666
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
4667
  const NDBTAB *orig_tab= (const NDBTAB *) m_table;
4668 4669
  DBUG_ENTER("alter_table_name_table");

4670
  NdbDictionary::Table new_tab= *orig_tab;
4671 4672
  new_tab.setName(to);
  if (dict->alterTable(new_tab) != 0)
4673 4674 4675
    ERR_RETURN(dict->getNdbError());

  m_table= NULL;
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
4676
  m_table_info= NULL;
4677 4678 4679 4680 4681 4682
                                                                             
  DBUG_RETURN(0);
}


/*
4683 4684
  Delete table from NDB Cluster

4685 4686 4687 4688
 */

int ha_ndbcluster::delete_table(const char *name)
{
4689
  DBUG_ENTER("ha_ndbcluster::delete_table");
4690 4691 4692
  DBUG_PRINT("enter", ("name: %s", name));
  set_dbname(name);
  set_tabname(name);
4693

4694 4695
  if (check_ndb_connection())
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4696 4697

  /* Call ancestor function to delete .ndb file */
4698
  handler::delete_table(name);
4699 4700
  
  /* Drop the table from NDB */
4701 4702 4703 4704 4705
  DBUG_RETURN(drop_table());
}


/*
4706
  Drop table in NDB Cluster
4707 4708 4709 4710
 */

int ha_ndbcluster::drop_table()
{
4711
  THD *thd= current_thd;
4712 4713
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
4714

4715 4716 4717 4718
  DBUG_ENTER("drop_table");
  DBUG_PRINT("enter", ("Deleting %s", m_tabname));
  
  release_metadata();
4719
  while (dict->dropTable(m_tabname)) 
4720 4721
  {
    const NdbError err= dict->getNdbError();
4722 4723 4724 4725 4726 4727 4728 4729 4730
    switch (err.status)
    {
      case NdbError::TemporaryError:
        if (!thd->killed)
          continue; // retry indefinitly
        break;
      default:
        break;
    }
4731
    ERR_RETURN(dict->getNdbError());
4732 4733
  }

4734 4735 4736 4737
  DBUG_RETURN(0);
}


4738
ulonglong ha_ndbcluster::get_auto_increment()
4739
{  
4740 4741
  int cache_size;
  Uint64 auto_value;
4742 4743
  DBUG_ENTER("get_auto_increment");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));
4744
  Ndb *ndb= get_ndb();
4745
   
4746
  if (m_rows_inserted > m_rows_to_insert)
4747
  {
4748 4749
    /* We guessed too low */
    m_rows_to_insert+= m_autoincrement_prefetch;
4750
  }
serg@serg.mylan's avatar
serg@serg.mylan committed
4751
  cache_size= 
4752 4753 4754 4755
    (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));
4756
  int ret;
4757 4758
  uint retries= NDB_AUTO_INCREMENT_RETRIES;
  do {
4759 4760 4761 4762 4763
    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 && 
4764 4765
           --retries &&
           ndb->getNdbError().status == NdbError::TemporaryError);
4766
  if (ret == -1)
4767 4768 4769 4770 4771 4772
  {
    const NdbError err= ndb->getNdbError();
    sql_print_error("Error %lu in ::get_auto_increment(): %s",
                    (ulong) err.code, err.message);
    DBUG_RETURN(~(ulonglong) 0);
  }
4773
  DBUG_RETURN((longlong)auto_value);
4774 4775 4776 4777 4778 4779 4780 4781
}


/*
  Constructor for the NDB Cluster table handler 
 */

ha_ndbcluster::ha_ndbcluster(TABLE *table_arg):
4782
  handler(&ndbcluster_hton, table_arg),
4783 4784 4785
  m_active_trans(NULL),
  m_active_cursor(NULL),
  m_table(NULL),
4786
  m_table_version(-1),
4787
  m_table_info(NULL),
4788
  m_table_flags(HA_REC_NOT_IN_SEQ |
4789 4790 4791 4792
                HA_NULL_IN_KEY |
                HA_AUTO_PART_KEY |
                HA_NO_PREFIX_CHAR_KEYS |
                HA_NEED_READ_RANGE_BUFFER |
4793
                HA_CAN_GEOMETRY |
4794 4795
                HA_CAN_BIT_FIELD |
                HA_PARTIAL_COLUMN_READ),
4796
  m_share(0),
4797
  m_use_write(FALSE),
4798
  m_ignore_dup_key(FALSE),
4799
  m_has_unique_index(FALSE),
4800 4801
  m_primary_key_update(FALSE),
  m_retrieve_all_fields(FALSE),
4802
  m_retrieve_primary_key(FALSE),
4803 4804 4805
  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
4806
  m_rows_changed((ha_rows) 0),
4807
  m_bulk_insert_not_flushed(FALSE),
4808 4809
  m_delete_cannot_batch(FALSE),
  m_update_cannot_batch(FALSE),
4810 4811 4812
  m_ops_pending(0),
  m_skip_auto_increment(TRUE),
  m_blobs_pending(0),
4813
  m_blobs_offset(0),
4814 4815
  m_blobs_buffer(0),
  m_blobs_buffer_size(0),
4816 4817 4818
  m_dupkey((uint) -1),
  m_ha_not_exact_count(FALSE),
  m_force_send(TRUE),
4819
  m_autoincrement_prefetch((ha_rows) 32),
4820
  m_transaction_on(TRUE),
mskold@mysql.com's avatar
mskold@mysql.com committed
4821 4822
  m_cond_stack(NULL),
  m_multi_cursor(NULL)
4823
{
4824
  int i;
4825
 
4826 4827 4828 4829 4830
  DBUG_ENTER("ha_ndbcluster");

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

4831
  records= ~(ha_rows)0; // uninitialized
4832 4833
  block_size= 1024;

4834 4835
  for (i= 0; i < MAX_KEY; i++)
  {
4836 4837 4838 4839
    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;
4840 4841
  }

4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853
  DBUG_VOID_RETURN;
}


/*
  Destructor for NDB Cluster table handler
 */

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

4854 4855
  if (m_share)
    free_share(m_share);
4856
  release_metadata();
4857 4858
  my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
  m_blobs_buffer= 0;
4859 4860

  // Check for open cursor/transaction
4861 4862
  if (m_active_cursor) {
  }
4863
  DBUG_ASSERT(m_active_cursor == NULL);
4864 4865
  if (m_active_trans) {
  }
4866 4867
  DBUG_ASSERT(m_active_trans == NULL);

4868 4869 4870 4871
  // Discard the condition stack
  DBUG_PRINT("info", ("Clearing condition stack"));
  cond_clear();

4872 4873 4874 4875
  DBUG_VOID_RETURN;
}


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

4877 4878 4879 4880 4881 4882 4883 4884
/*
  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
4885
  int res;
4886 4887 4888 4889 4890 4891 4892 4893
  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
  
4894
  if (table->s->primary_key != MAX_KEY) 
4895
  {
4896
    key= table->key_info+table->s->primary_key;
4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907
    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);
  
4908 4909
  if (check_ndb_connection()) {
    free_share(m_share); m_share= 0;
4910
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4911
  }
4912
  
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
4913 4914
  res= get_metadata(name);
  if (!res)
4915 4916 4917
  {
    Ndb *ndb= get_ndb();
    ndb->setDatabaseName(m_dbname);
stewart@willster.(none)'s avatar
stewart@willster.(none) committed
4918 4919 4920
    struct Ndb_statistics stat;
    res= ndb_get_table_statistics(NULL, false, ndb, m_tabname, &stat);
    records= stat.row_count;
4921 4922 4923
    if(!res)
      res= info(HA_STATUS_CONST);
  }
4924

tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
4925
  DBUG_RETURN(res);
4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936
}


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

int ha_ndbcluster::close(void)
{
  DBUG_ENTER("close");  
4937
  free_share(m_share); m_share= 0;
4938 4939 4940 4941 4942
  release_metadata();
  DBUG_RETURN(0);
}


4943
Thd_ndb* ha_ndbcluster::seize_thd_ndb()
4944
{
4945 4946
  Thd_ndb *thd_ndb;
  DBUG_ENTER("seize_thd_ndb");
4947

4948
  thd_ndb= new Thd_ndb();
4949 4950 4951
  thd_ndb->ndb->getDictionary()->set_local_table_data_size(
    sizeof(Ndb_local_table_statistics)
    );
4952
  if (thd_ndb->ndb->init(max_transactions) != 0)
4953
  {
4954
    ERR_PRINT(thd_ndb->ndb->getNdbError());
4955 4956 4957 4958 4959 4960
    /*
      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 
    */
4961 4962
    delete thd_ndb;
    thd_ndb= NULL;
4963
  }
4964
  DBUG_RETURN(thd_ndb);
4965 4966 4967
}


4968
void ha_ndbcluster::release_thd_ndb(Thd_ndb* thd_ndb)
4969
{
4970 4971
  DBUG_ENTER("release_thd_ndb");
  delete thd_ndb;
4972 4973 4974 4975 4976
  DBUG_VOID_RETURN;
}


/*
magnus@neptunus.(none)'s avatar
magnus@neptunus.(none) committed
4977
  If this thread already has a Thd_ndb object allocated
4978
  in current THD, reuse it. Otherwise
magnus@neptunus.(none)'s avatar
magnus@neptunus.(none) committed
4979
  seize a Thd_ndb object, assign it to current THD and use it.
4980 4981 4982
 
*/

4983
Ndb* check_ndb_in_thd(THD* thd)
4984
{
4985
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
4986
  if (!thd_ndb)
4987
  {
magnus@neptunus.(none)'s avatar
magnus@neptunus.(none) committed
4988
    if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4989
      return NULL;
4990
    set_thd_ndb(thd, thd_ndb);
4991
  }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4992
  return thd_ndb->ndb;
4993 4994
}

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

4996

4997
int ha_ndbcluster::check_ndb_connection(THD* thd)
4998
{
4999
  Ndb *ndb;
5000 5001
  DBUG_ENTER("check_ndb_connection");
  
5002
  if (!(ndb= check_ndb_in_thd(thd)))
5003
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
5004
  ndb->setDatabaseName(m_dbname);
5005 5006 5007
  DBUG_RETURN(0);
}

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

5009
int ndbcluster_close_connection(THD *thd)
5010
{
5011
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
5012
  DBUG_ENTER("ndbcluster_close_connection");
5013 5014
  if (thd_ndb)
  {
5015
    ha_ndbcluster::release_thd_ndb(thd_ndb);
5016
    set_thd_ndb(thd, NULL); // not strictly required but does not hurt either
5017
  }
5018
  DBUG_RETURN(0);
5019 5020 5021 5022 5023 5024 5025
}


/*
  Try to discover one table from NDB
 */

5026
int ndbcluster_discover(THD* thd, const char *db, const char *name,
5027
                        const void** frmblob, uint* frmlen)
5028 5029 5030 5031
{
  uint len;
  const void* data;
  const NDBTAB* tab;
5032
  Ndb* ndb;
5033
  DBUG_ENTER("ndbcluster_discover");
5034
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name)); 
5035

5036 5037 5038
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(HA_ERR_NO_CONNECTION);  
  ndb->setDatabaseName(db);
5039

5040
  NDBDICT* dict= ndb->getDictionary();
5041
  dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
5042 5043 5044 5045 5046
  dict->invalidateTable(name);
  if (!(tab= dict->getTable(name)))
  {    
    const NdbError err= dict->getNdbError();
    if (err.code == 709)
5047
      DBUG_RETURN(-1);
5048
    ERR_RETURN(err);
5049 5050 5051 5052 5053 5054
  }
  DBUG_PRINT("info", ("Found table %s", tab->getName()));
  
  len= tab->getFrmLength();  
  if (len == 0 || tab->getFrmData() == NULL)
  {
5055 5056
    DBUG_PRINT("error", ("No frm data found."));
    DBUG_RETURN(1);
5057 5058 5059
  }
  
  if (unpackfrm(&data, &len, tab->getFrmData()))
5060 5061 5062 5063
  {
    DBUG_PRINT("error", ("Could not unpack table"));
    DBUG_RETURN(1);
  }
5064 5065 5066 5067 5068 5069 5070 5071

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

/*
5072
  Check if a table exists in NDB
5073

5074
 */
5075

5076
int ndbcluster_table_exists_in_engine(THD* thd, const char *db, const char *name)
5077 5078 5079
{
  const NDBTAB* tab;
  Ndb* ndb;
5080
  DBUG_ENTER("ndbcluster_table_exists_in_engine");
5081
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
5082 5083

  if (!(ndb= check_ndb_in_thd(thd)))
5084
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
5085 5086 5087
  ndb->setDatabaseName(db);

  NDBDICT* dict= ndb->getDictionary();
5088
  dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
5089 5090
  dict->invalidateTable(name);
  if (!(tab= dict->getTable(name)))
5091
  {
5092
    ERR_RETURN(dict->getNdbError());
5093
  }
5094

5095
  DBUG_PRINT("info", ("Found table %s", tab->getName()));
5096
  DBUG_RETURN(HA_ERR_TABLE_EXIST);
5097 5098
}

5099 5100


5101
extern "C" byte* tables_get_key(const char *entry, uint *length,
5102
                                my_bool not_used __attribute__((unused)))
5103 5104 5105 5106 5107 5108
{
  *length= strlen(entry);
  return (byte*) entry;
}


5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122
/*
  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;
5123
  int ret= 0;
5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149
  ha_ndbcluster::set_dbname(path, (char *)&dbname);
  DBUG_PRINT("enter", ("db: %s", dbname));
  
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
  
  // List tables in NDB
  NDBDICT *dict= ndb->getDictionary();
  if (dict->listObjects(list, 
                        NdbDictionary::Object::UserTable) != 0)
    ERR_RETURN(dict->getNdbError());
  for (i= 0 ; i < list.count ; i++)
  {
    NdbDictionary::Dictionary::List::Element& t= list.elements[i];
    DBUG_PRINT("info", ("Found %s/%s in NDB", t.database, t.name));     
    
    // Add only tables that belongs to db
    if (my_strcasecmp(system_charset_info, t.database, dbname))
      continue;
    DBUG_PRINT("info", ("%s must be dropped", t.name));     
    drop_list.push_back(thd->strdup(t.name));
  }
  // Drop any tables belonging to database
  ndb->setDatabaseName(dbname);
  List_iterator_fast<char> it(drop_list);
  while ((tabname=it++))
5150
  {
5151
    while (dict->dropTable(tabname))
5152 5153
    {
      const NdbError err= dict->getNdbError();
5154 5155 5156 5157 5158 5159 5160 5161 5162 5163
      switch (err.status)
      {
        case NdbError::TemporaryError:
          if (!thd->killed)
            continue; // retry indefinitly
          break;
        default:
          break;
      }
      if (err.code != 709) // 709: No such table existed
5164 5165
      {
        ERR_PRINT(err);
5166
        ret= ndb_to_mysql_error(&err);
5167
      }
5168
      break;
5169 5170 5171
    }
  }
  DBUG_RETURN(ret);      
5172 5173 5174
}


5175
int ndbcluster_find_files(THD *thd,const char *db,const char *path,
5176
                          const char *wild, bool dir, List<char> *files)
5177
{
5178 5179 5180
  DBUG_ENTER("ndbcluster_find_files");
  DBUG_PRINT("enter", ("db: %s", db));
  { // extra bracket to avoid gcc 2.95.3 warning
5181
  uint i;
5182
  Ndb* ndb;
5183
  char name[FN_REFLEN];
5184
  HASH ndb_tables, ok_tables;
5185
  NdbDictionary::Dictionary::List list;
5186 5187 5188 5189

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

5190
  if (dir)
5191
    DBUG_RETURN(0); // Discover of databases not yet supported
5192

5193
  // List tables in NDB
5194
  NDBDICT *dict= ndb->getDictionary();
5195
  if (dict->listObjects(list, 
5196
                        NdbDictionary::Object::UserTable) != 0)
5197
    ERR_RETURN(dict->getNdbError());
5198

5199
  if (hash_init(&ndb_tables, system_charset_info,list.count,0,0,
5200
                (hash_get_key)tables_get_key,0,0))
5201 5202 5203 5204 5205 5206
  {
    DBUG_PRINT("error", ("Failed to init HASH ndb_tables"));
    DBUG_RETURN(-1);
  }

  if (hash_init(&ok_tables, system_charset_info,32,0,0,
5207
                (hash_get_key)tables_get_key,0,0))
5208 5209 5210 5211 5212 5213
  {
    DBUG_PRINT("error", ("Failed to init HASH ok_tables"));
    hash_free(&ndb_tables);
    DBUG_RETURN(-1);
  }  

5214 5215 5216
  for (i= 0 ; i < list.count ; i++)
  {
    NdbDictionary::Dictionary::List::Element& t= list.elements[i];
5217
    DBUG_PRINT("info", ("Found %s/%s in NDB", t.database, t.name));     
5218

5219 5220 5221
    // Add only tables that belongs to db
    if (my_strcasecmp(system_charset_info, t.database, db))
      continue;
5222

5223
    // Apply wildcard to list of tables in NDB
5224
    if (wild)
5225
    {
5226 5227
      if (lower_case_table_names)
      {
5228 5229
        if (wild_case_compare(files_charset_info, t.name, wild))
          continue;
5230 5231
      }
      else if (wild_compare(t.name,wild,0))
5232
        continue;
5233
    }
5234 5235
    DBUG_PRINT("info", ("Inserting %s into ndb_tables hash", t.name));     
    my_hash_insert(&ndb_tables, (byte*)thd->strdup(t.name));
5236 5237
  }

5238 5239 5240 5241 5242
  char *file_name;
  List_iterator<char> it(*files);
  List<char> delete_list;
  while ((file_name=it++))
  {
5243
    bool file_on_disk= false;
5244 5245 5246 5247
    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));
5248
      file_on_disk= true;
5249 5250
    }
    
5251
    // Check for .ndb file with this name
5252
    (void)strxnmov(name, FN_REFLEN, 
5253
                   mysql_data_home,"/",db,"/",file_name,ha_ndb_ext,NullS);
5254
    DBUG_PRINT("info", ("Check access for %s", name));
5255
    if (access(name, F_OK))
5256 5257 5258
    {
      DBUG_PRINT("info", ("%s did not exist on disk", name));     
      // .ndb file did not exist on disk, another table type
5259
      if (file_on_disk)
5260 5261 5262 5263 5264
      {
	// Ignore this ndb table
	gptr record=  hash_search(&ndb_tables, file_name, strlen(file_name));
	DBUG_ASSERT(record);
	hash_delete(&ndb_tables, record);
5265 5266 5267 5268
	push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
			    ER_TABLE_EXISTS_ERROR,
			    "Local table %s.%s shadows ndb table",
			    db, file_name);
5269
      }
5270 5271 5272 5273
      continue;
    }
    if (file_on_disk) 
    {
5274
      // File existed in NDB and as frm file, put in ok_tables list
5275
      my_hash_insert(&ok_tables, (byte*)file_name);
5276
      continue;
5277
    }
5278 5279 5280
    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.
5281
    if (ndbcluster_table_exists_in_engine(thd, db, file_name) == HA_ERR_NO_SUCH_TABLE)
5282 5283 5284 5285 5286 5287 5288
    {
      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));
    }
  }
5289

5290 5291 5292 5293
  // 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++)
5294
  {
5295 5296
    file_name= hash_element(&ndb_tables, i);
    if (!hash_search(&ok_tables, file_name, strlen(file_name)))
5297
    {
5298 5299 5300 5301 5302 5303
      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));
    }
  }
5304

5305 5306
  // Lock mutex before deleting and creating frm files
  pthread_mutex_lock(&LOCK_open);
5307

5308 5309 5310 5311 5312
  if (!global_read_lock)
  {
    // Delete old files
    List_iterator_fast<char> it3(delete_list);
    while ((file_name=it3++))
5313 5314
    {
      DBUG_PRINT("info", ("Remove table %s/%s", db, file_name));
5315 5316 5317 5318
      // Delete the table and all related files
      TABLE_LIST table_list;
      bzero((char*) &table_list,sizeof(table_list));
      table_list.db= (char*) db;
5319
      table_list.alias= table_list.table_name= (char*)file_name;
5320
      (void)mysql_rm_table_part2(thd, &table_list,
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5321 5322 5323 5324
                                                                 /* if_exists */ FALSE,
                                                                 /* drop_temporary */ FALSE,
                                                                 /* drop_view */ FALSE,
                                                                 /* dont_log_query*/ TRUE);
5325 5326
      /* Clear error message that is returned when table is deleted */
      thd->clear_error();
5327 5328 5329
    }
  }

5330 5331 5332 5333
  // Create new files
  List_iterator_fast<char> it2(create_list);
  while ((file_name=it2++))
  {  
5334
    DBUG_PRINT("info", ("Table %s need discovery", file_name));
5335
    if (ha_create_table_from_engine(thd, db, file_name) == 0)
5336
      files->push_back(thd->strdup(file_name)); 
5337 5338 5339 5340 5341
  }

  pthread_mutex_unlock(&LOCK_open);      
  
  hash_free(&ok_tables);
5342
  hash_free(&ndb_tables);
5343
  } // extra bracket to avoid gcc 2.95.3 warning
5344
  DBUG_RETURN(0);    
5345 5346 5347 5348 5349 5350 5351 5352
}


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

5353 5354 5355 5356 5357 5358 5359
/* Call back after cluster connect */
static int connect_callback()
{
  update_status_variables(g_ndb_cluster_connection);
  return 0;
}

5360
bool ndbcluster_init()
5361
{
5362
  int res;
5363
  DBUG_ENTER("ndbcluster_init");
5364 5365 5366 5367

  if (have_ndbcluster != SHOW_OPTION_YES)
    goto ndbcluster_init_error;

5368
  // Set connectstring if specified
5369 5370
  if (opt_ndbcluster_connectstring != 0)
    DBUG_PRINT("connectstring", ("%s", opt_ndbcluster_connectstring));     
5371
  if ((g_ndb_cluster_connection=
5372
       new Ndb_cluster_connection(opt_ndbcluster_connectstring)) == 0)
5373
  {
5374
    DBUG_PRINT("error",("Ndb_cluster_connection(%s)",
5375
                        opt_ndbcluster_connectstring));
5376
    goto ndbcluster_init_error;
5377
  }
tomas@poseidon.ndb.mysql.com's avatar
ndb:  
tomas@poseidon.ndb.mysql.com committed
5378 5379
  {
    char buf[128];
5380
    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
5381 5382
    g_ndb_cluster_connection->set_name(buf);
  }
5383 5384 5385
  g_ndb_cluster_connection->set_optimized_node_selection
    (opt_ndb_optimized_node_selection);

5386
  // Create a Ndb object to open the connection  to NDB
5387 5388 5389 5390 5391
  if ( (g_ndb= new Ndb(g_ndb_cluster_connection, "sys")) == 0 )
  {
    DBUG_PRINT("error", ("failed to create global ndb object"));
    goto ndbcluster_init_error;
  }
5392
  g_ndb->getDictionary()->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
5393 5394 5395
  if (g_ndb->init() != 0)
  {
    ERR_PRINT (g_ndb->getNdbError());
5396
    goto ndbcluster_init_error;
5397
  }
5398

5399
  if ((res= g_ndb_cluster_connection->connect(0,0,0)) == 0)
5400
  {
5401
    connect_callback();
5402
    DBUG_PRINT("info",("NDBCLUSTER storage engine at %s on port %d",
5403 5404
                       g_ndb_cluster_connection->get_connected_host(),
                       g_ndb_cluster_connection->get_connected_port()));
5405
    g_ndb_cluster_connection->wait_until_ready(10,3);
5406
  } 
5407
  else if (res == 1)
5408
  {
5409
    if (g_ndb_cluster_connection->start_connect_thread(connect_callback)) 
5410
    {
5411
      DBUG_PRINT("error", ("g_ndb_cluster_connection->start_connect_thread()"));
5412 5413
      goto ndbcluster_init_error;
    }
5414
#ifndef DBUG_OFF
5415 5416
    {
      char buf[1024];
5417
      DBUG_PRINT("info",
5418 5419 5420 5421
                 ("NDBCLUSTER storage engine not started, "
                  "will connect using %s",
                  g_ndb_cluster_connection->
                  get_connectstring(buf,sizeof(buf))));
5422
    }
5423
#endif
5424
  }
5425
  else
5426 5427 5428
  {
    DBUG_ASSERT(res == -1);
    DBUG_PRINT("error", ("permanent error"));
5429
    goto ndbcluster_init_error;
5430
  }
5431
  
5432 5433 5434
  (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
5435 5436
  pthread_mutex_init(&LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST);
  pthread_cond_init(&COND_ndb_util_thread, NULL);
5437

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

jonas@perch.ndb.mysql.com's avatar
jonas@perch.ndb.mysql.com committed
5439
  ndb_cache_check_time = opt_ndb_cache_check_time;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5440 5441 5442 5443 5444
  // 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"));
5445 5446 5447 5448
    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
5449 5450 5451
    goto ndbcluster_init_error;
  }
  
5452
  ndbcluster_inited= 1;
5453
  DBUG_RETURN(FALSE);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5454

5455
ndbcluster_init_error:
5456
  if (g_ndb)
5457 5458 5459 5460 5461
    delete g_ndb;
  g_ndb= NULL;
  if (g_ndb_cluster_connection)
    delete g_ndb_cluster_connection;
  g_ndb_cluster_connection= NULL;
5462 5463
  have_ndbcluster= SHOW_OPTION_DISABLED;	// If we couldn't use handler
  DBUG_RETURN(TRUE);
5464 5465 5466 5467 5468 5469
}


/*
  End use of the NDB Cluster table handler
  - free all global variables allocated by 
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5470
    ndbcluster_init()
5471 5472 5473 5474 5475
*/

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

5477 5478 5479
  if (!ndbcluster_inited)
    DBUG_RETURN(0);

mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5480 5481 5482 5483 5484 5485
  // 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);

5486
  if (g_ndb)
5487 5488
  {
#ifndef DBUG_OFF
5489 5490
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
5491 5492 5493 5494 5495 5496 5497 5498 5499 5500
    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
5501
    delete g_ndb;
5502
    g_ndb= NULL;
5503
  }
5504
  delete g_ndb_cluster_connection;
5505
  g_ndb_cluster_connection= NULL;
5506

5507 5508
  hash_free(&ndbcluster_open_tables);
  pthread_mutex_destroy(&ndbcluster_mutex);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5509 5510
  pthread_mutex_destroy(&LOCK_ndb_util_thread);
  pthread_cond_destroy(&COND_ndb_util_thread);
5511 5512 5513 5514
  ndbcluster_inited= 0;
  DBUG_RETURN(0);
}

5515 5516 5517 5518 5519
/*
  Static error print function called from
  static handler method ndbcluster_commit
  and ndbcluster_rollback
*/
5520 5521

void ndbcluster_print_error(int error, const NdbOperation *error_op)
5522
{
5523 5524
  DBUG_ENTER("ndbcluster_print_error");
  TABLE tab;
5525
  const char *tab_name= (error_op) ? error_op->getTableName() : "";
5526
  tab.alias= (char *) tab_name;
5527
  ha_ndbcluster error_handler(&tab);
5528
  tab.file= &error_handler;
5529
  error_handler.print_error(error, MYF(0));
ndbdev@ndbmaster.mysql.com's avatar
ndbdev@ndbmaster.mysql.com committed
5530
  DBUG_VOID_RETURN;
5531
}
5532

5533 5534 5535
/**
 * Set a given location from full pathname to database name
 *
5536
 */
5537
void ha_ndbcluster::set_dbname(const char *path_name, char *dbname)
5538 5539 5540 5541
{
  char *end, *ptr;
  
  /* Scan name from the end */
5542 5543 5544 5545 5546 5547
  ptr= strend(path_name)-1;
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  ptr--;
  end= ptr;
5548 5549 5550 5551
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  uint name_len= end - ptr;
5552 5553
  memcpy(dbname, ptr + 1, name_len);
  dbname[name_len]= '\0';
5554 5555
#ifdef __WIN__
  /* Put to lower case */
5556 5557
  
  ptr= dbname;
5558 5559
  
  while (*ptr != '\0') {
5560
    *ptr= tolower(*ptr);
5561 5562 5563 5564 5565
    ptr++;
  }
#endif
}

5566 5567 5568 5569 5570 5571 5572 5573 5574
/*
  Set m_dbname from full pathname to table file
 */

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

5575 5576 5577 5578 5579 5580 5581 5582 5583 5584
/**
 * 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 */
5585 5586
  end= strend(path_name)-1;
  ptr= end;
5587 5588 5589
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
5590
  uint name_len= end - ptr;
5591
  memcpy(tabname, ptr + 1, end - ptr);
5592
  tabname[name_len]= '\0';
5593 5594
#ifdef __WIN__
  /* Put to lower case */
5595
  ptr= tabname;
5596 5597 5598 5599 5600 5601 5602 5603 5604
  
  while (*ptr != '\0') {
    *ptr= tolower(*ptr);
    ptr++;
  }
#endif
}

/*
5605
  Set m_tabname from full pathname to table file 
5606 5607
 */

5608
void ha_ndbcluster::set_tabname(const char *path_name)
5609
{
5610
  set_tabname(path_name, m_tabname);
5611 5612 5613 5614
}


ha_rows 
5615 5616 5617 5618
ha_ndbcluster::records_in_range(uint inx, key_range *min_key,
                                key_range *max_key)
{
  KEY *key_info= table->key_info + inx;
5619
  uint key_length= key_info->key_length;
5620
  NDB_INDEX_TYPE idx_type= get_index_type(inx);  
5621 5622

  DBUG_ENTER("records_in_range");
5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636
  // 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 */
5637 5638
}

5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665
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
5666 5667 5668 5669
uint ha_ndbcluster::max_supported_key_part_length() const
{
  return NDB_MAX_KEY_SIZE;
}
5670 5671 5672 5673 5674 5675 5676 5677 5678 5679
bool ha_ndbcluster::low_byte_first() const
{ 
#ifdef WORDS_BIGENDIAN
  return FALSE;
#else
  return TRUE;
#endif
}
bool ha_ndbcluster::has_transactions()
{
5680
  return TRUE;
5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694
}
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
5695

5696 5697
uint8 ha_ndbcluster::table_cache_type()
{
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5698 5699 5700 5701 5702 5703
  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,
5704
                         Uint64 *commit_count)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5705 5706 5707
{
  DBUG_ENTER("ndb_get_commitcount");

5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725
  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
5726 5727
  if (ndb_cache_check_time > 0)
  {
5728
    if (share->commit_count != 0)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5729
    {
5730
      *commit_count= share->commit_count;
5731
#ifndef DBUG_OFF
5732
      char buff[22];
5733
#endif
5734 5735
      DBUG_PRINT("info", ("Getting commit_count: %s from share",
                          llstr(share->commit_count, buff)));
5736 5737 5738
      pthread_mutex_unlock(&share->mutex);
      free_share(share);
      DBUG_RETURN(0);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5739 5740
    }
  }
5741
  DBUG_PRINT("info", ("Get commit_count from NDB"));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5742 5743 5744 5745
  Ndb *ndb;
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(1);
  ndb->setDatabaseName(dbname);
5746 5747
  uint lock= share->commit_count_lock;
  pthread_mutex_unlock(&share->mutex);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5748 5749

  struct Ndb_statistics stat;
5750
  if (ndb_get_table_statistics(NULL, false, ndb, tabname, &stat))
5751 5752
  {
    free_share(share);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5753
    DBUG_RETURN(1);
5754 5755 5756
  }

  pthread_mutex_lock(&share->mutex);
5757
  if (share->commit_count_lock == lock)
5758
  {
5759
#ifndef DBUG_OFF
5760
    char buff[22];
5761
#endif
5762 5763
    DBUG_PRINT("info", ("Setting commit_count to %s",
                        llstr(stat.commit_count, buff)));
5764 5765 5766 5767 5768 5769 5770 5771 5772 5773
    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
5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809
  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,
5810 5811
                                   char *full_name, uint full_name_len,
                                   ulonglong *engine_data)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5812 5813 5814 5815 5816
{
  Uint64 commit_count;
  bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
  char *dbname= full_name;
  char *tabname= dbname+strlen(dbname)+1;
5817
#ifndef DBUG_OFF
5818
  char buff[22], buff2[22];
5819
#endif
5820
  DBUG_ENTER("ndbcluster_cache_retrieval_allowed");
5821 5822
  DBUG_PRINT("enter", ("dbname: %s, tabname: %s, is_autocommit: %d",
                       dbname, tabname, is_autocommit));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5823 5824

  if (!is_autocommit)
5825 5826
  {
    DBUG_PRINT("exit", ("No, don't use cache in transaction"));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5827
    DBUG_RETURN(FALSE);
5828
  }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5829 5830 5831

  if (ndb_get_commitcount(thd, dbname, tabname, &commit_count))
  {
5832 5833
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, could not retrieve commit_count"));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5834 5835
    DBUG_RETURN(FALSE);
  }
5836 5837
  DBUG_PRINT("info", ("*engine_data: %s, commit_count: %s",
                      llstr(*engine_data, buff), llstr(commit_count, buff2)));
5838
  if (commit_count == 0)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5839
  {
5840 5841
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, local commit has been performed"));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5842 5843
    DBUG_RETURN(FALSE);
  }
5844 5845 5846 5847 5848 5849
  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
5850

5851 5852
  DBUG_PRINT("exit", ("OK to use cache, engine_data: %s",
                      llstr(*engine_data, buff)));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
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
  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,
5881 5882 5883
                                          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
5884
{
5885
  Uint64 commit_count;
5886
#ifndef DBUG_OFF
5887
  char buff[22];
5888
#endif
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5889
  bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
5890
  DBUG_ENTER("ha_ndbcluster::register_query_cache_table");
5891 5892 5893
  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
5894
  if (!is_autocommit)
5895 5896
  {
    DBUG_PRINT("exit", ("Can't register table during transaction"))
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5897
    DBUG_RETURN(FALSE);
5898
  }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5899 5900 5901 5902

  if (ndb_get_commitcount(thd, m_dbname, m_tabname, &commit_count))
  {
    *engine_data= 0;
5903
    DBUG_PRINT("exit", ("Error, could not get commitcount"))
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5904 5905 5906 5907
    DBUG_RETURN(FALSE);
  }
  *engine_data= commit_count;
  *engine_callback= ndbcluster_cache_retrieval_allowed;
5908
  DBUG_PRINT("exit", ("commit_count: %s", llstr(commit_count, buff)));
5909
  DBUG_RETURN(commit_count > 0);
5910
}
5911

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

5913
/*
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5914
  Handling the shared NDB_SHARE structure that is needed to
5915 5916 5917 5918 5919 5920 5921
  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,
5922
                                my_bool not_used __attribute__((unused)))
5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950
{
  *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
5951
      share->commit_count= 0;
5952 5953 5954 5955 5956 5957 5958
      share->commit_count_lock= 0;
    }
    else
    {
      DBUG_PRINT("error", ("Failed to alloc share"));
      pthread_mutex_unlock(&ndbcluster_mutex);
      return 0;
5959 5960 5961
    }
  }
  share->use_count++;
5962 5963

  DBUG_PRINT("share",
5964
	     ("table_name: %s  length: %d  use_count: %d  commit_count: %lu",
5965
	      share->table_name, share->table_name_length, share->use_count,
5966
	      (ulong) share->commit_count));
5967 5968 5969 5970 5971 5972 5973 5974 5975 5976
  pthread_mutex_unlock(&ndbcluster_mutex);
  return share;
}


static void free_share(NDB_SHARE *share)
{
  pthread_mutex_lock(&ndbcluster_mutex);
  if (!--share->use_count)
  {
5977
     hash_delete(&ndbcluster_open_tables, (byte*) share);
5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005
    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, 
6006
                   const void **pack_data, uint *pack_len)
6007 6008 6009 6010 6011 6012
{
  int error;
  ulong org_len, comp_len;
  uint blob_len;
  frm_blob_struct* blob;
  DBUG_ENTER("packfrm");
6013
  DBUG_PRINT("enter", ("data: 0x%lx, len: %d", (long) data, len));
6014 6015
  
  error= 1;
6016
  org_len= len;
6017 6018 6019
  if (my_compress((byte*)data, &org_len, &comp_len))
    goto err;
  
6020
  DBUG_PRINT("info", ("org_len: %lu  comp_len: %lu", org_len, comp_len));
6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035
  DBUG_DUMP("compressed", (char*)data, org_len);
  
  error= 2;
  blob_len= sizeof(frm_blob_struct::frm_blob_header)+org_len;
  if (!(blob= (frm_blob_struct*) my_malloc(blob_len,MYF(MY_WME))))
    goto err;
  
  // Store compressed blob in machine independent format
  int4store((char*)(&blob->head.ver), 1);
  int4store((char*)(&blob->head.orglen), comp_len);
  int4store((char*)(&blob->head.complen), org_len);
  
  // Copy frm data into blob, already in machine independent format
  memcpy(blob->data, data, org_len);  
  
6036 6037 6038
  *pack_data= blob;
  *pack_len= blob_len;
  error= 0;
6039
  
6040
  DBUG_PRINT("exit", ("pack_data: 0x%lx, pack_len: %d", (long) *pack_data, *pack_len));
6041 6042 6043 6044 6045 6046 6047
err:
  DBUG_RETURN(error);
  
}


static int unpackfrm(const void **unpack_data, uint *unpack_len,
6048
                    const void *pack_data)
6049
{
6050
   const frm_blob_struct *blob= (frm_blob_struct*)pack_data;
6051 6052 6053
   byte *data;
   ulong complen, orglen, ver;
   DBUG_ENTER("unpackfrm");
6054
   DBUG_PRINT("enter", ("pack_data: 0x%lx", (long) pack_data));
6055

6056 6057 6058
   complen=     uint4korr((char*)&blob->head.complen);
   orglen=      uint4korr((char*)&blob->head.orglen);
   ver=         uint4korr((char*)&blob->head.ver);
6059
 
6060
   DBUG_PRINT("blob",("ver: %lu  complen: %lu  orglen: %lu",
6061
                     ver,complen,orglen));
6062 6063 6064 6065
   DBUG_DUMP("blob->data", (char*) blob->data, complen);
 
   if (ver != 1)
     DBUG_RETURN(1);
6066
   if (!(data= my_malloc(max(orglen, complen), MYF(MY_WME))))
6067 6068 6069 6070 6071 6072 6073 6074 6075
     DBUG_RETURN(2);
   memcpy(data, blob->data, complen);
 
   if (my_uncompress(data, &complen, &orglen))
   {
     my_free((char*)data, MYF(0));
     DBUG_RETURN(3);
   }

6076 6077
   *unpack_data= data;
   *unpack_len= complen;
6078

6079
   DBUG_PRINT("exit", ("frmdata: 0x%lx, len: %d", (long) *unpack_data, *unpack_len));
6080 6081 6082

   DBUG_RETURN(0);
}
6083 6084 6085

static 
int
6086
ndb_get_table_statistics(ha_ndbcluster* file, bool report_error, Ndb* ndb,
stewart@willster.(none)'s avatar
stewart@willster.(none) committed
6087
                         const char* table,
6088
                         struct Ndb_statistics * ndbstat)
6089
{
6090
  NdbTransaction* pTrans;
6091
  NdbError error;
6092
  int retries= 10;
6093
  int reterr= 0;
6094
  int retry_sleep= 30 * 1000; /* 30 milliseconds */
6095
#ifndef DBUG_OFF
6096
  char buff[22], buff2[22], buff3[22], buff4[22];
6097
#endif
6098 6099
  DBUG_ENTER("ndb_get_table_statistics");
  DBUG_PRINT("enter", ("table: %s", table));
6100 6101

  do
6102
  {
6103 6104
    Uint64 rows, commits, mem;
    Uint32 size;
6105
    Uint32 count= 0;
6106 6107
    Uint64 sum_rows= 0;
    Uint64 sum_commits= 0;
6108 6109
    Uint64 sum_row_size= 0;
    Uint64 sum_mem= 0;
6110 6111 6112 6113
    NdbScanOperation*pOp;
    int check;

    if ((pTrans= ndb->startTransaction()) == NULL)
6114
    {
6115 6116 6117
      error= ndb->getNdbError();
      goto retry;
    }
6118
      
6119 6120 6121 6122
    if ((pOp= pTrans->getNdbScanOperation(table)) == NULL)
    {
      error= pTrans->getNdbError();
      goto retry;
6123
    }
6124
    
6125
    if (pOp->readTuples(NdbOperation::LM_CommittedRead))
6126 6127 6128 6129
    {
      error= pOp->getNdbError();
      goto retry;
    }
6130
    
6131 6132 6133 6134 6135
    if (pOp->interpret_exit_last_row() == -1)
    {
      error= pOp->getNdbError();
      goto retry;
    }
6136 6137 6138
    
    pOp->getValue(NdbDictionary::Column::ROW_COUNT, (char*)&rows);
    pOp->getValue(NdbDictionary::Column::COMMIT_COUNT, (char*)&commits);
6139 6140
    pOp->getValue(NdbDictionary::Column::ROW_SIZE, (char*)&size);
    pOp->getValue(NdbDictionary::Column::FRAGMENT_MEMORY, (char*)&mem);
6141
    
6142 6143 6144
    if (pTrans->execute(NdbTransaction::NoCommit,
                        NdbTransaction::AbortOnError,
                        TRUE) == -1)
6145
    {
6146 6147
      error= pTrans->getNdbError();
      goto retry;
6148
    }
6149
    
monty@mishka.local's avatar
monty@mishka.local committed
6150
    while ((check= pOp->nextResult(TRUE, TRUE)) == 0)
6151 6152 6153
    {
      sum_rows+= rows;
      sum_commits+= commits;
6154
      if (sum_row_size < size)
6155
        sum_row_size= size;
6156
      sum_mem+= mem;
6157
      count++;
6158 6159 6160
    }
    
    if (check == -1)
6161 6162 6163 6164
    {
      error= pOp->getNdbError();
      goto retry;
    }
6165

6166
    pOp->close(TRUE);
6167

6168
    ndb->closeTransaction(pTrans);
6169 6170 6171 6172 6173 6174

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

6175 6176 6177 6178 6179 6180 6181
    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));
6182

6183
    DBUG_RETURN(0);
6184
retry:
6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199
    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;
6200

6201
    if (pTrans)
6202 6203 6204 6205 6206 6207 6208 6209 6210 6211
    {
      ndb->closeTransaction(pTrans);
      pTrans= NULL;
    }
    if (error.status == NdbError::TemporaryError && retries--)
    {
      my_sleep(retry_sleep);
      continue;
    }
    break;
6212
  } while(1);
6213 6214 6215
  DBUG_PRINT("exit", ("failed, reterr: %u, NdbError %u(%s)", reterr,
                      error.code, error.message));
  DBUG_RETURN(reterr);
6216 6217
}

6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232
/*
  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, 
6233
                 mysql_data_home,"/",m_dbname,"/",m_tabname,ha_ndb_ext,NullS);
6234 6235 6236 6237 6238 6239 6240 6241 6242 6243

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

6244
void 
6245 6246
ha_ndbcluster::release_completed_operations(NdbTransaction *trans,
					    bool force_release)
6247 6248 6249 6250 6251 6252 6253 6254
{
  if (trans->hasBlobOperation())
  {
    /* We are reading/writing BLOB fields, 
       releasing operation records is unsafe
    */
    return;
  }
6255 6256 6257 6258 6259 6260 6261 6262 6263 6264
  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;
    }
  }
6265
  trans->releaseCompletedOperations();
6266 6267
}

6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291
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);
}

6292
int
6293
ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
6294 6295 6296 6297
                                      KEY_MULTI_RANGE *ranges, 
                                      uint range_count,
                                      bool sorted, 
                                      HANDLER_BUFFER *buffer)
6298 6299 6300
{
  int res;
  KEY* key_info= table->key_info + active_index;
6301
  NDB_INDEX_TYPE cur_index_type= get_index_type(active_index);
joreland@mysql.com's avatar
merge  
joreland@mysql.com committed
6302
  ulong reclength= table->s->reclength;
6303
  NdbOperation* op;
6304
  Thd_ndb *thd_ndb= get_thd_ndb(current_thd);
6305
  DBUG_ENTER("ha_ndbcluster::read_multi_range_first");
6306

6307 6308 6309 6310
  /**
   * blobs and unique hash index with NULL can't be batched currently
   */
  if (uses_blob_value(m_retrieve_all_fields) ||
6311
      (cur_index_type == UNIQUE_INDEX &&
6312 6313
       has_null_in_unique_index(active_index) &&
       null_value_index_search(ranges, ranges+range_count, buffer)))
6314
  {
6315
    m_disable_multi_read= TRUE;
6316
    DBUG_RETURN(handler::read_multi_range_first(found_range_p, 
6317 6318 6319 6320
                                                ranges, 
                                                range_count,
                                                sorted, 
                                                buffer));
6321
  }
6322
  thd_ndb->query_state|= NDB_QUERY_MULTI_READ_RANGE;
6323
  m_disable_multi_read= FALSE;
6324 6325 6326 6327

  /**
   * Copy arguments into member variables
   */
6328 6329 6330
  m_multi_ranges= ranges;
  multi_range_curr= ranges;
  multi_range_end= ranges+range_count;
6331 6332 6333
  multi_range_sorted= sorted;
  multi_range_buffer= buffer;

6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344
  /**
   * 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
6345 6346
   */   

mskold@mysql.com's avatar
mskold@mysql.com committed
6347
  /**
6348 6349
   * Variables for loop
   */
6350 6351
  byte *curr= (byte*)buffer->buffer;
  byte *end_of_buffer= (byte*)buffer->buffer_end;
6352 6353
  NdbOperation::LockMode lm= 
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
6354
  bool need_pk = (lm == NdbOperation::LM_Read);
6355 6356
  const NDBTAB *tab= (const NDBTAB *) m_table;
  const NDBINDEX *unique_idx= (NDBINDEX *) m_index[active_index].unique_index;
6357
  const NDBINDEX *idx= (NDBINDEX *) m_index[active_index].index; 
6358 6359
  const NdbOperation* lastOp= m_active_trans->getLastDefinedOperation();
  NdbIndexScanOperation* scanOp= 0;
6360 6361
  for (; multi_range_curr<multi_range_end && curr+reclength <= end_of_buffer; 
       multi_range_curr++)
6362
  {
6363
    switch (cur_index_type) {
6364 6365 6366 6367 6368
    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 */
6369
    case PRIMARY_KEY_INDEX:
6370
      multi_range_curr->range_flag |= UNIQUE_RANGE;
6371
      if ((op= m_active_trans->getNdbOperation(tab)) && 
6372 6373 6374
          !op->readTuple(lm) && 
          !set_primary_key(op, multi_range_curr->start_key.key) &&
          !define_read_attrs(curr, op) &&
6375
          (op->setAbortOption(AO_IgnoreError), TRUE))
6376
        curr += reclength;
6377
      else
6378
        ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
6379
      break;
6380 6381 6382 6383 6384 6385 6386
    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 */
6387
    case UNIQUE_INDEX:
6388
      multi_range_curr->range_flag |= UNIQUE_RANGE;
6389
      if ((op= m_active_trans->getNdbIndexOperation(unique_idx, tab)) && 
6390 6391 6392 6393 6394
	  !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;
6395
      else
6396
	ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
6397
      break;
6398 6399
    case ORDERED_INDEX:
    {
6400
  range:
6401
      multi_range_curr->range_flag &= ~(uint)UNIQUE_RANGE;
6402 6403
      if (scanOp == 0)
      {
6404 6405 6406 6407 6408 6409
        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));
6410
          if (scanOp->reset_bounds(m_force_send))
6411 6412 6413 6414 6415
            DBUG_RETURN(ndb_err(m_active_trans));
          
          end_of_buffer -= reclength;
        }
        else if ((scanOp= m_active_trans->getNdbIndexScanOperation(idx, tab)) 
6416
                 &&!scanOp->readTuples(lm, 0, parallelism, sorted, 
6417
				       FALSE, TRUE, need_pk, TRUE)
6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428
                 &&!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());
        }
6429
      }
6430

6431
      const key_range *keys[2]= { &multi_range_curr->start_key, 
6432
                                  &multi_range_curr->end_key };
6433
      if ((res= set_bounds(scanOp, keys, multi_range_curr-ranges)))
6434
        DBUG_RETURN(res);
6435
      break;
6436
    }
6437
    case UNDEFINED_INDEX:
mskold@mysql.com's avatar
mskold@mysql.com committed
6438 6439 6440 6441
      DBUG_ASSERT(FALSE);
      DBUG_RETURN(1);
      break;
    }
6442 6443
  }
  
6444
  if (multi_range_curr != multi_range_end)
6445
  {
6446 6447 6448 6449 6450 6451
    /**
     * 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
     */
6452
    buffer->end_of_used_area= (byte*)buffer->buffer_end;
6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463
  }
  else
  {
    buffer->end_of_used_area= curr;
  }
  
  /**
   * Set first operation in multi range
   */
  m_current_multi_operation= 
    lastOp ? lastOp->next() : m_active_trans->getFirstDefinedOperation();
6464
  if (!(res= execute_no_commit_ie(this, m_active_trans, true)))
6465
  {
6466 6467
    m_multi_range_defined= multi_range_curr;
    multi_range_curr= ranges;
6468 6469
    m_multi_range_result_ptr= (byte*)buffer->buffer;
    DBUG_RETURN(read_multi_range_next(found_range_p));
6470 6471 6472 6473
  }
  ERR_RETURN(m_active_trans->getNdbError());
}

6474 6475 6476 6477 6478 6479
#if 0
#define DBUG_MULTI_RANGE(x) printf("read_multi_range_next: case %d\n", x);
#else
#define DBUG_MULTI_RANGE(x)
#endif

6480
int
6481
ha_ndbcluster::read_multi_range_next(KEY_MULTI_RANGE ** multi_range_found_p)
6482 6483
{
  DBUG_ENTER("ha_ndbcluster::read_multi_range_next");
6484
  if (m_disable_multi_read)
6485
  {
6486
    DBUG_RETURN(handler::read_multi_range_next(multi_range_found_p));
6487
  }
6488
  
6489
  int res;
6490
  int range_no;
joreland@mysql.com's avatar
merge  
joreland@mysql.com committed
6491
  ulong reclength= table->s->reclength;
6492
  const NdbOperation* op= m_current_multi_operation;
6493
  for (;multi_range_curr < m_multi_range_defined; multi_range_curr++)
6494
  {
6495
    if (multi_range_curr->range_flag & UNIQUE_RANGE)
6496
    {
6497
      if (op->getNdbError().code == 0)
6498
        goto found_next;
6499 6500 6501
      
      op= m_active_trans->getNextCompletedOperation(op);
      m_multi_range_result_ptr += reclength;
6502
      continue;
6503
    } 
6504
    else if (m_multi_cursor && !multi_range_sorted)
6505
    {
6506 6507
      DBUG_MULTI_RANGE(1);
      if ((res= fetch_next(m_multi_cursor)) == 0)
6508
      {
6509 6510 6511
        DBUG_MULTI_RANGE(2);
        range_no= m_multi_cursor->get_range_no();
        goto found;
6512 6513 6514
      } 
      else
      {
6515
        goto close_scan;
6516 6517
      }
    }
6518
    else if (m_multi_cursor && multi_range_sorted)
6519
    {
6520 6521
      if (m_active_cursor && (res= fetch_next(m_multi_cursor)))
      {
6522 6523
        DBUG_MULTI_RANGE(3);
        goto close_scan;
6524
      }
6525
      
6526
      range_no= m_multi_cursor->get_range_no();
6527
      uint current_range_no= multi_range_curr - m_multi_ranges;
mskold@mysql.com's avatar
mskold@mysql.com committed
6528
      if ((uint) range_no == current_range_no)
6529
      {
6530
        DBUG_MULTI_RANGE(4);
6531
        // return current row
6532
        goto found;
6533
      }
6534
      else if (range_no > (int)current_range_no)
6535
      {
6536 6537 6538 6539
        DBUG_MULTI_RANGE(5);
        // wait with current row
        m_active_cursor= 0;
        continue;
6540 6541 6542
      }
      else 
      {
6543 6544 6545
        DBUG_MULTI_RANGE(6);
        // First fetch from cursor
        DBUG_ASSERT(range_no == -1);
6546
        if ((res= m_multi_cursor->nextResult(true)))
6547 6548 6549 6550 6551
        {
          goto close_scan;
        }
        multi_range_curr--; // Will be increased in for-loop
        continue;
6552
      }
6553
    }
6554
    else /** m_multi_cursor == 0 */
6555
    {
6556
      DBUG_MULTI_RANGE(7);
6557 6558 6559 6560
      /**
       * Corresponds to range 5 in example in read_multi_range_first
       */
      (void)1;
6561
      continue;
6562
    }
6563
    
6564
    DBUG_ASSERT(FALSE); // Should only get here via goto's
6565 6566 6567
close_scan:
    if (res == 1)
    {
6568
      m_multi_cursor->close(FALSE, TRUE);
6569
      m_active_cursor= m_multi_cursor= 0;
6570
      DBUG_MULTI_RANGE(8);
6571 6572 6573 6574 6575 6576 6577
      continue;
    } 
    else 
    {
      DBUG_RETURN(ndb_err(m_active_trans));
    }
  }
6578
  
6579
  if (multi_range_curr == multi_range_end)
6580 6581 6582
  {
    Thd_ndb *thd_ndb= get_thd_ndb(current_thd);
    thd_ndb->query_state&= NDB_QUERY_NORMAL;
6583
    DBUG_RETURN(HA_ERR_END_OF_FILE);
6584
  }
6585
  
6586 6587 6588 6589
  /**
   * Read remaining ranges
   */
  DBUG_RETURN(read_multi_range_first(multi_range_found_p, 
6590 6591 6592 6593
                                     multi_range_curr,
                                     multi_range_end - multi_range_curr, 
                                     multi_range_sorted,
                                     multi_range_buffer));
6594 6595
  
found:
6596 6597 6598
  /**
   * Found a record belonging to a scan
   */
6599
  m_active_cursor= m_multi_cursor;
6600
  * multi_range_found_p= m_multi_ranges + range_no;
6601 6602
  memcpy(table->record[0], m_multi_range_cursor_result_ptr, reclength);
  setup_recattr(m_active_cursor->getFirstRecAttr());
6603 6604 6605
  unpack_record(table->record[0]);
  table->status= 0;     
  DBUG_RETURN(0);
6606
  
6607
found_next:
6608 6609 6610 6611
  /**
   * Found a record belonging to a pk/index op,
   *   copy result and move to next to prepare for next call
   */
6612
  * multi_range_found_p= multi_range_curr;
6613
  memcpy(table->record[0], m_multi_range_result_ptr, reclength);
6614
  setup_recattr(op->getFirstRecAttr());
6615
  unpack_record(table->record[0]);
6616 6617
  table->status= 0;
  
6618
  multi_range_curr++;
6619
  m_current_multi_operation= m_active_trans->getNextCompletedOperation(op);
6620 6621
  m_multi_range_result_ptr += reclength;
  DBUG_RETURN(0);
6622 6623
}

6624 6625 6626 6627 6628 6629 6630 6631
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
6632
  end= table->field + table->s->fields;
6633 6634 6635 6636 6637 6638
  
  for (field= table->field; field < end; field++, value++)
  {
    if ((* value).ptr)
    {
      DBUG_ASSERT(curr != 0);
6639 6640 6641
      NdbValue* val= m_value + curr->getColumn()->getColumnNo();
      DBUG_ASSERT(val->ptr);
      val->rec= curr;
6642
      curr= curr->next();
6643 6644 6645
    }
  }
  
6646
  DBUG_RETURN(0);
6647 6648
}

mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6649 6650
char*
ha_ndbcluster::update_table_comment(
6651 6652
                                /* out: table comment + additional */
        const char*     comment)/* in:  table comment defined by user */
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6653 6654
{
  uint length= strlen(comment);
6655
  if (length > 64000 - 3)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681
  {
    return((char*)comment); /* string too long */
  }

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

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

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

6682 6683 6684
  my_snprintf(str,fmt_len_plus_extra,fmt,comment,
              length > 0 ? " ":"",
              tab->getReplicaCount());
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6685 6686 6687 6688 6689
  return str;
}


// Utility thread main loop
6690
pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6691 6692
{
  THD *thd; /* needs to be first for thread_stack */
6693
  Ndb* ndb;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6694 6695 6696 6697
  struct timespec abstime;

  my_thread_init();
  DBUG_ENTER("ndb_util_thread");
6698
  DBUG_PRINT("enter", ("ndb_cache_check_time: %lu", ndb_cache_check_time));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6699 6700 6701

  thd= new THD; /* note that contructor of THD uses DBUG_ */
  THD_CHECK_SENTRY(thd);
6702
  ndb= new Ndb(g_ndb_cluster_connection, "");
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6703 6704 6705 6706 6707

  pthread_detach_this_thread();
  ndb_util_thread= pthread_self();

  thd->thread_stack= (char*)&thd; /* remember where our stack is */
6708
  if (thd->store_globals() || (ndb->init() != 0))
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6709 6710 6711
  {
    thd->cleanup();
    delete thd;
6712
    delete ndb;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6713 6714 6715
    DBUG_RETURN(NULL);
  }

6716 6717
  uint share_list_size= 0;
  NDB_SHARE **share_list= NULL;
6718
  set_timespec(abstime, 0);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6719 6720 6721
  for (;;)
  {

6722 6723 6724
    if (abort_loop)
      break; /* Shutting down server */

mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6725
    pthread_mutex_lock(&LOCK_ndb_util_thread);
monty@mysql.com's avatar
monty@mysql.com committed
6726 6727 6728
    pthread_cond_timedwait(&COND_ndb_util_thread,
                           &LOCK_ndb_util_thread,
                           &abstime);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6729 6730
    pthread_mutex_unlock(&LOCK_ndb_util_thread);

6731
    DBUG_PRINT("ndb_util_thread", ("Started, ndb_cache_check_time: %lu",
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6732 6733 6734 6735 6736 6737 6738
                                   ndb_cache_check_time));

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

    if (ndb_cache_check_time == 0)
    {
6739 6740
      /* 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
6741 6742 6743 6744 6745 6746
      continue;
    }

    /* Lock mutex and fill list with pointers to all open tables */
    NDB_SHARE *share;
    pthread_mutex_lock(&ndbcluster_mutex);
6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762
    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
6763 6764 6765 6766 6767 6768 6769 6770
    {
      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 */
6771
      share_list[i]= share;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6772 6773 6774
    }
    pthread_mutex_unlock(&ndbcluster_mutex);

6775 6776
    /* Iterate through the open files list */
    for (i= 0; i < record_count; i++)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6777
    {
6778
      share= share_list[i];
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6779 6780 6781 6782 6783 6784 6785 6786 6787
      /* 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",
6788 6789
                 ("Fetching commit count for: %s",
                  share->table_name));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6790 6791

      /* Contact NDB to get commit count for table */
6792 6793 6794 6795 6796 6797 6798 6799
      ndb->setDatabaseName(db);
      struct Ndb_statistics stat;

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

stewart@willster.(none)'s avatar
stewart@willster.(none) committed
6800
      if (ndb_get_table_statistics(NULL, false, ndb, tabname, &stat) == 0)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6801
      {
6802
#ifndef DBUG_OFF
6803
        char buff[22], buff2[22];
6804
#endif
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6805
        DBUG_PRINT("ndb_util_thread",
6806 6807 6808
                   ("Table: %s  commit_count: %s  rows: %s",
                    share->table_name,
                    llstr(stat.commit_count, buff),
monty@mysql.com's avatar
monty@mysql.com committed
6809
                    llstr(stat.row_count, buff2)));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6810 6811 6812 6813 6814 6815
      }
      else
      {
        DBUG_PRINT("ndb_util_thread",
                   ("Error: Could not get commit count for table %s",
                    share->table_name));
6816
        stat.commit_count= 0;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6817
      }
6818 6819 6820 6821 6822 6823

      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
6824 6825 6826
      /* Decrease the use count and possibly free share */
      free_share(share);
    }
6827
next:
6828 6829 6830 6831 6832 6833 6834 6835 6836
    /* 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;

6837
    if (msecs >= 1000){
6838 6839 6840 6841 6842 6843 6844 6845 6846 6847
      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
6848 6849
  }

6850 6851
  if (share_list)
    delete [] share_list;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6852 6853
  thd->cleanup();
  delete thd;
6854
  delete ndb;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6855 6856 6857 6858 6859 6860
  DBUG_PRINT("exit", ("ndb_util_thread"));
  my_thread_end();
  pthread_exit(0);
  DBUG_RETURN(NULL);
}

6861 6862 6863
/*
  Condition pushdown
*/
6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880
/*
  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)
*/
6881 6882 6883 6884 6885
const 
COND* 
ha_ndbcluster::cond_push(const COND *cond) 
{ 
  DBUG_ENTER("cond_push");
6886 6887 6888
  Ndb_cond_stack *ndb_cond = new Ndb_cond_stack();
  DBUG_EXECUTE("where",print_where((COND *)cond, m_tabname););
  if (m_cond_stack)
mskold@mysql.com's avatar
mskold@mysql.com committed
6889
    ndb_cond->next= m_cond_stack;
6890 6891 6892 6893 6894 6895
  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
6896
    DBUG_RETURN(NULL);
6897 6898 6899 6900
  }
  else
  {
    cond_pop();
mskold@mysql.com's avatar
mskold@mysql.com committed
6901
  }
6902 6903 6904
  DBUG_RETURN(cond); 
}

6905 6906 6907
/*
  Pop the top condition from the condition stack of the handler instance.
*/
6908 6909 6910 6911 6912 6913 6914 6915 6916
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
6917
}
6918

6919 6920 6921
/*
  Clear the condition stack
*/
6922 6923 6924 6925 6926 6927 6928 6929 6930 6931
void
ha_ndbcluster::cond_clear()
{
  DBUG_ENTER("cond_clear");
  while (m_cond_stack)
    cond_pop();

  DBUG_VOID_RETURN;
}

6932 6933 6934 6935 6936 6937
/*
  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.
*/
6938 6939 6940 6941 6942
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
6943 6944 6945 6946 6947
  // 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--;
6948 6949 6950
    switch (item->type()) {
    case Item::FUNC_ITEM:
    {
mskold@mysql.com's avatar
mskold@mysql.com committed
6951 6952 6953 6954
      Item_func *func_item= (Item_func *) item;
      context->skip+= func_item->argument_count();
      break;
    }
6955 6956 6957 6958 6959
    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
6960 6961
      break;
    default:
6962
      context->supported= FALSE;
mskold@mysql.com's avatar
mskold@mysql.com committed
6963 6964
      break;
    }
6965
    
mskold@mysql.com's avatar
mskold@mysql.com committed
6966 6967 6968
    DBUG_VOID_RETURN;
  }
  
6969
  if (context->supported)
6970
  {
6971 6972
    Ndb_rewrite_context *rewrite_context2= context->rewrite_stack;
    const Item_func *rewrite_func_item;
6973
    // Check if we are rewriting some unsupported function call
6974 6975 6976
    if (rewrite_context2 &&
        (rewrite_func_item= rewrite_context2->func_item) &&
        rewrite_context2->count++ == 0)
mskold@mysql.com's avatar
mskold@mysql.com committed
6977
    {
6978
      switch (rewrite_func_item->functype()) {
6979
      case Item_func::BETWEEN:
6980
        /*
6981 6982 6983 6984 6985 6986 6987
          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()
6988
        */
6989 6990
      case Item_func::IN_FUNC:
      {
6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004
        /*
          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
7005
          rewrite_context2->left_hand_item= item;
7006
          if (item->type() == Item::FUNC_ITEM)
7007
          {
7008 7009 7010
            Item_func *func_item= (Item_func *) item;
            if (func_item->functype() == Item_func::UNKNOWN_FUNC &&
                func_item->const_item())
7011
            {
7012 7013 7014
              // Skip any arguments since we will evaluate function instead
              DBUG_PRINT("info", ("Skip until end of arguments marker"));
              context->skip= func_item->argument_count();
7015 7016 7017
            }
            else
            {
7018 7019 7020 7021
              DBUG_PRINT("info", ("Found unsupported functional expression in BETWEEN|IN"));
              context->supported= FALSE;
              DBUG_VOID_RETURN;
              
7022 7023 7024
            }
          }
        }
7025 7026
        else
        {
7027 7028 7029
          // Non-supported BETWEEN|IN expression
          DBUG_PRINT("info", ("Found unexpected item of type %u in BETWEEN|IN",
                              item->type()));
7030
          context->supported= FALSE;
7031
          DBUG_VOID_RETURN;
7032
        }
7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054
        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;
7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066
        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()
          */
7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085
          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;
          }
7086 7087
          break;
        }
7088 7089
        case Item_func::IN_FUNC:
        {
7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102
          /*
            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);
7103 7104
          break;
        }
7105 7106
        default:
          context->supported= FALSE;
7107
        }
7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137
        // 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
7138 7139 7140 7141
      {
        switch (item->type()) {
        case Item::FIELD_ITEM:
        {
7142 7143 7144 7145 7146 7147 7148
          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.
          */
7149
          if (context->table->s == field->table->s)
7150 7151 7152 7153 7154
          {       
            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));
7155
            DBUG_PRINT("info", ("type %d", field->type()));
7156 7157 7158 7159 7160
            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) &&
7161
                context->expecting_field_type(field->type()) &&
7162
                (context->expecting_field_result(field->result_type()) ||
mskold@mysql.com's avatar
mskold@mysql.com committed
7163
                 // Date and year can be written as string or int
7164 7165 7166 7167
                 ((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
7168 7169 7170
                  ? (context->expecting_field_result(STRING_RESULT) ||
                     context->expecting_field_result(INT_RESULT))
                  : true)) &&
7171
                // Bit fields no yet supported in scan filter
7172 7173 7174
                type != MYSQL_TYPE_BIT &&
                // No BLOB support in scan filter
                type != MYSQL_TYPE_TINY_BLOB &&
7175 7176
                type != MYSQL_TYPE_MEDIUM_BLOB &&
                type != MYSQL_TYPE_LONG_BLOB &&
7177
                type != MYSQL_TYPE_BLOB)
7178 7179 7180 7181 7182 7183
            {
              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();
7184
              if (! context->expecting_nothing())
7185
              {
7186 7187 7188 7189 7190 7191 7192 7193 7194 7195
                // 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
7196 7197
                  switch (field->result_type()) {
                  case STRING_RESULT:
7198 7199 7200 7201 7202
                    // 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;
7203
                  case REAL_RESULT:
7204 7205
                    context->expect_only(Item::REAL_ITEM);
                    context->expect(Item::DECIMAL_ITEM);
7206
                    context->expect(Item::INT_ITEM);
7207
                    break;
7208
                  case INT_RESULT:
7209 7210 7211
                    context->expect_only(Item::INT_ITEM);
                    context->expect(Item::VARBIN_ITEM);
                    break;
7212
                  case DECIMAL_RESULT:
7213 7214
                    context->expect_only(Item::DECIMAL_ITEM);
                    context->expect(Item::REAL_ITEM);
7215
                    context->expect(Item::INT_ITEM);
7216 7217 7218 7219
                    break;
                  default:
                    break;
                  }    
7220 7221
              }
              else
7222 7223 7224 7225
              {
                // Expect another logical expression
                context->expect_only(Item::FUNC_ITEM);
                context->expect(Item::COND_ITEM);
7226 7227 7228 7229 7230 7231 7232
                // 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)
7233
                {
mskold@mysql.com's avatar
mskold@mysql.com committed
7234
                  DBUG_PRINT("info", ("Found non-matching collation %s",  
7235 7236
                                      item->collation.collation->name)); 
                  context->supported= FALSE;                
7237 7238
                }
              }
7239 7240
              break;
            }
7241 7242
            else
            {
mskold@mysql.com's avatar
mskold@mysql.com committed
7243 7244
              DBUG_PRINT("info", ("Was not expecting field of type %u(%u)",
                                  field->result_type(), type));
7245
              context->supported= FALSE;
7246
            }
7247
          }
7248
          else
7249 7250 7251 7252
          {
            DBUG_PRINT("info", ("Was not expecting field from table %s(%s)",
                                context->table->s->table_name, 
                                field->table->s->table_name));
7253
            context->supported= FALSE;
7254
          }
7255 7256
          break;
        }
7257 7258
        case Item::FUNC_ITEM:
        {
7259 7260 7261 7262 7263 7264
          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
7265
          {
7266 7267 7268
            // Did not expect function here
            context->supported= FALSE;
            break;
7269
          }
7270
          
7271 7272 7273
          switch (func_item->functype()) {
          case Item_func::EQ_FUNC:
          {
7274 7275 7276 7277 7278 7279 7280 7281 7282 7283 7284 7285 7286 7287
            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;
7288
          }
7289 7290
          case Item_func::NE_FUNC:
          {
7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 7304
            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;
7305
          }
7306 7307
          case Item_func::LT_FUNC:
          {
7308 7309 7310 7311 7312 7313 7314 7315 7316 7317 7318 7319 7320 7321 7322
            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;
          }
7323 7324
          case Item_func::LE_FUNC:
          {
7325 7326 7327 7328 7329 7330 7331 7332 7333 7334 7335 7336 7337 7338 7339
            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;
          }
7340 7341
          case Item_func::GE_FUNC:
          {
7342 7343 7344 7345 7346 7347 7348 7349 7350 7351 7352 7353 7354 7355 7356
            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;
          }
7357 7358
          case Item_func::GT_FUNC:
          {
7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373
            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;
          }
7374 7375
          case Item_func::LIKE_FUNC:
          {
7376 7377 7378 7379 7380
            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);
7381 7382 7383
            context->expect_only_field_type(MYSQL_TYPE_STRING);
            context->expect_field_type(MYSQL_TYPE_VAR_STRING);
            context->expect_field_type(MYSQL_TYPE_VARCHAR);
7384 7385 7386 7387
            context->expect_field_result(STRING_RESULT);
            context->expect(Item::FUNC_ITEM);
            break;
          }
7388 7389
          case Item_func::ISNULL_FUNC:
          {
7390 7391 7392 7393 7394 7395 7396 7397 7398 7399
            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;
          }
7400 7401
          case Item_func::ISNOTNULL_FUNC:
          {
7402 7403 7404 7405 7406 7407 7408 7409 7410 7411
            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;
          }
7412 7413
          case Item_func::NOT_FUNC:
          {
7414 7415 7416 7417
            DBUG_PRINT("info", ("NOT_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);     
            context->expect(Item::FUNC_ITEM);
7418
            context->expect(Item::COND_ITEM);
7419
            break;
7420
          }
7421 7422
          case Item_func::BETWEEN:
          {
7423
            DBUG_PRINT("info", ("BETWEEN, rewriting using AND"));
7424
            Item_func_between *between_func= (Item_func_between *) func_item;
7425 7426 7427 7428
            Ndb_rewrite_context *rewrite_context= 
              new Ndb_rewrite_context(func_item);
            rewrite_context->next= context->rewrite_stack;
            context->rewrite_stack= rewrite_context;
7429 7430 7431 7432 7433 7434 7435 7436 7437
            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;
            }
7438
            DBUG_PRINT("info", ("COND_AND_FUNC"));
7439 7440 7441
            curr_cond->ndb_item= 
              new Ndb_item(Item_func::COND_AND_FUNC, 
                           func_item->argument_count() - 1);
7442
            context->expect_only(Item::FIELD_ITEM);
7443 7444 7445 7446 7447
            context->expect(Item::INT_ITEM);
            context->expect(Item::STRING_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FUNC_ITEM);
            break;
7448
          }
7449 7450
          case Item_func::IN_FUNC:
          {
7451
            DBUG_PRINT("info", ("IN_FUNC, rewriting using OR"));
7452
            Item_func_in *in_func= (Item_func_in *) func_item;
7453 7454 7455 7456
            Ndb_rewrite_context *rewrite_context= 
              new Ndb_rewrite_context(func_item);
            rewrite_context->next= context->rewrite_stack;
            context->rewrite_stack= rewrite_context;
7457 7458 7459 7460 7461 7462 7463 7464 7465
            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;
            }
7466 7467 7468 7469 7470 7471 7472 7473 7474
            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;
7475
          }
7476 7477
          case Item_func::UNKNOWN_FUNC:
          {
7478 7479 7480 7481
            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())
7482 7483 7484 7485
            {
              switch (func_item->result_type()) {
              case STRING_RESULT:
              {
7486 7487 7488
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::STRING_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item); 
7489
                if (! context->expecting_no_field_result())
7490 7491 7492 7493 7494 7495 7496 7497 7498 7499 7500 7501 7502 7503 7504 7505 7506 7507 7508 7509 7510 7511 7512 7513
                {
                  // 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;
              }
7514 7515
              case REAL_RESULT:
              {
7516 7517 7518
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::REAL_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
7519
                if (! context->expecting_no_field_result()) 
7520 7521 7522 7523 7524 7525 7526 7527 7528 7529 7530 7531 7532 7533 7534 7535 7536
                {
                  // 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;
              }
7537 7538
              case INT_RESULT:
              {
7539 7540 7541
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::INT_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
7542
                if (! context->expecting_no_field_result()) 
7543 7544 7545 7546 7547 7548 7549 7550 7551 7552 7553 7554 7555 7556 7557 7558 7559
                {
                  // 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;
              }
7560 7561
              case DECIMAL_RESULT:
              {
7562 7563 7564
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::DECIMAL_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
7565
                if (! context->expecting_no_field_result()) 
7566 7567 7568 7569 7570 7571 7572 7573 7574 7575 7576 7577 7578 7579 7580 7581 7582 7583 7584
                {
                  // 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;
              }
7585
            }
7586 7587 7588 7589 7590
            else
              // Function does not return constant expression
              context->supported= FALSE;
            break;
          }
7591 7592
          default:
          {
7593 7594 7595
            DBUG_PRINT("info", ("Found func_item of type %d", 
                                func_item->functype()));
            context->supported= FALSE;
7596
          }
7597 7598
          }
          break;
7599
        }
7600
        case Item::STRING_ITEM:
7601 7602 7603
          DBUG_PRINT("info", ("STRING_ITEM")); 
          if (context->expecting(Item::STRING_ITEM)) 
          {
7604
#ifndef DBUG_OFF
7605 7606 7607 7608 7609 7610
            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()));
7611
#endif
7612 7613 7614
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::STRING_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);      
7615
            if (! context->expecting_no_field_result())
7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 7630 7631 7632 7633 7634 7635 7636 7637 7638
            {
              // 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;
7639
        case Item::INT_ITEM:
7640 7641
          DBUG_PRINT("info", ("INT_ITEM"));
          if (context->expecting(Item::INT_ITEM)) 
7642
          {
7643 7644
            DBUG_PRINT("info", ("value %ld",
                                (long) ((Item_int*) item)->value));
7645 7646 7647
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::INT_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
7648
            if (! context->expecting_no_field_result()) 
7649 7650 7651 7652
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(INT_RESULT);
7653 7654
              context->expect_field_result(REAL_RESULT);
              context->expect_field_result(DECIMAL_RESULT);
7655 7656 7657 7658 7659 7660 7661
            }
            else
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
            }
7662 7663
          }
          else
7664 7665
            context->supported= FALSE;
          break;
7666
        case Item::REAL_ITEM:
7667
          DBUG_PRINT("info", ("REAL_ITEM"));
7668
          if (context->expecting(Item::REAL_ITEM)) 
7669
          {
7670
            DBUG_PRINT("info", ("value %f", ((Item_float *) item)->value));
7671 7672 7673
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::REAL_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
7674
            if (! context->expecting_no_field_result()) 
7675 7676 7677 7678 7679 7680 7681 7682 7683 7684 7685
            {
              // 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);
            }
7686
          }
7687 7688 7689
          else
            context->supported= FALSE;
          break;
7690
        case Item::VARBIN_ITEM:
7691 7692
          DBUG_PRINT("info", ("VARBIN_ITEM"));
          if (context->expecting(Item::VARBIN_ITEM)) 
7693
          {
7694 7695 7696
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::VARBIN_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);      
7697
            if (! context->expecting_no_field_result())
7698 7699 7700 7701 7702 7703 7704 7705 7706 7707 7708
            {
              // 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);
            }
7709 7710
          }
          else
7711 7712
            context->supported= FALSE;
          break;
7713
        case Item::DECIMAL_ITEM:
7714
          DBUG_PRINT("info", ("DECIMAL_ITEM"));
7715
          if (context->expecting(Item::DECIMAL_ITEM)) 
7716
          {
7717 7718
            DBUG_PRINT("info", ("value %f",
                                ((Item_decimal*) item)->val_real()));
7719 7720 7721
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::DECIMAL_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
7722
            if (! context->expecting_no_field_result()) 
7723 7724 7725 7726 7727 7728 7729 7730 7731 7732 7733 7734
            {
              // 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);
            }
7735
          }
7736 7737 7738
          else
            context->supported= FALSE;
          break;
7739 7740
        case Item::COND_ITEM:
        {
7741 7742 7743
          Item_cond *cond_item= (Item_cond *) item;
          
          if (context->expecting(Item::COND_ITEM))
7744 7745 7746
          {
            switch (cond_item->functype()) {
            case Item_func::COND_AND_FUNC:
7747 7748 7749 7750
              DBUG_PRINT("info", ("COND_AND_FUNC"));
              curr_cond->ndb_item= new Ndb_item(cond_item->functype(),
                                                cond_item);      
              break;
7751
            case Item_func::COND_OR_FUNC:
7752 7753 7754 7755 7756 7757 7758 7759 7760
              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;
            }
7761
          }
7762
          else
7763 7764
          {
            /* Did not expect condition */
7765
            context->supported= FALSE;          
7766
          }
7767
          break;
7768
        }
7769 7770
        default:
        {
7771
          DBUG_PRINT("info", ("Found item of type %d", item->type()));
7772
          context->supported= FALSE;
7773 7774
        }
        }
7775
      }
7776 7777 7778 7779 7780 7781 7782 7783 7784 7785
      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();
7786
          curr_cond->prev= prev_cond;
7787 7788 7789
          prev_cond->next= curr_cond;
          curr_cond->ndb_item= new Ndb_item(NDB_END_COND);
          // Pop rewrite stack
7790 7791 7792
          context->rewrite_stack=  rewrite_context->next;
          rewrite_context->next= NULL;
          delete(rewrite_context);
7793
        }
7794
      }
7795
    }
7796
  }
7797
 
7798 7799 7800 7801 7802 7803 7804 7805
  DBUG_VOID_RETURN;
}

bool
ha_ndbcluster::serialize_cond(const COND *cond, Ndb_cond_stack *ndb_cond)
{
  DBUG_ENTER("serialize_cond");
  Item *item= (Item *) cond;
7806
  Ndb_cond_traverse_context context(table, (void *)m_table, ndb_cond);
7807 7808 7809
  // Expect a logical expression
  context.expect(Item::FUNC_ITEM);
  context.expect(Item::COND_ITEM);
7810
  item->traverse_cond(&ndb_serialize_cond, (void *) &context, Item::PREFIX);
7811
  DBUG_PRINT("info", ("The pushed condition is %ssupported", (context.supported)?"":"not "));
7812

7813
  DBUG_RETURN(context.supported);
7814 7815
}

7816 7817
int
ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, 
7818 7819
                                           NdbScanFilter *filter,
                                           bool negated)
7820 7821
{
  DBUG_ENTER("build_scan_filter_predicate");  
7822 7823 7824
  switch (cond->ndb_item->type) {
  case NDB_FUNCTION:
  {
7825 7826 7827
    if (!cond->next)
      break;
    Ndb_item *a= cond->next->ndb_item;
7828
    Ndb_item *b, *field, *value= NULL;
7829 7830
    LINT_INIT(field);

7831 7832
    switch (cond->ndb_item->argument_count()) {
    case 1:
7833 7834 7835
      field= 
        (a->type == NDB_FIELD)? a : NULL;
      break;
7836
    case 2:
7837
      if (!cond->next->next)
7838
        break;
7839 7840
      b= cond->next->next->ndb_item;
      value= 
7841 7842 7843
        (a->type == NDB_VALUE)? a
        : (b->type == NDB_VALUE)? b
        : NULL;
7844
      field= 
7845 7846 7847
        (a->type == NDB_FIELD)? a
        : (b->type == NDB_FIELD)? b
        : NULL;
7848
      break;
7849
    default:
7850 7851
      break;
    }
7852 7853 7854
    switch ((negated) ? 
            Ndb_item::negate(cond->ndb_item->qualification.function_type)
            : cond->ndb_item->qualification.function_type) {
7855
    case NDB_EQ_FUNC:
7856
    {
7857
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
7858 7859
      // Save value in right format for the field type
      value->save_in_field(field);
7860
      DBUG_PRINT("info", ("Generating EQ filter"));
7861
      if (filter->cmp(NdbScanFilter::COND_EQ, 
7862 7863 7864 7865
                      field->get_field_no(),
                      field->get_val(),
                      field->pack_length()) == -1)
        DBUG_RETURN(1);
7866 7867
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7868
    }
7869
    case NDB_NE_FUNC:
7870
    {
7871
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
7872 7873
      // Save value in right format for the field type
      value->save_in_field(field);
7874
      DBUG_PRINT("info", ("Generating NE filter"));
7875
      if (filter->cmp(NdbScanFilter::COND_NE, 
7876 7877 7878 7879
                      field->get_field_no(),
                      field->get_val(),
                      field->pack_length()) == -1)
        DBUG_RETURN(1);
7880 7881
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7882
    }
7883
    case NDB_LT_FUNC:
7884
    {
7885
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
7886 7887
      // Save value in right format for the field type
      value->save_in_field(field);
7888
      if (a == field)
7889
      {
7890 7891 7892 7893 7894 7895
        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);
7896
      }
7897
      else
7898
      {
7899 7900 7901 7902 7903 7904
        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);
7905
      }
7906 7907
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7908
    }
7909
    case NDB_LE_FUNC:
7910
    {
7911
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
7912 7913
      // Save value in right format for the field type
      value->save_in_field(field);
7914
      if (a == field)
7915
      {
7916 7917 7918 7919 7920 7921
        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);       
7922
      }
7923
      else
7924
      {
7925 7926 7927 7928 7929 7930
        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);
7931
      }
7932 7933
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7934
    }
7935
    case NDB_GE_FUNC:
7936
    {
7937
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
7938 7939
      // Save value in right format for the field type
      value->save_in_field(field);
7940
      if (a == field)
7941
      {
7942 7943 7944 7945 7946 7947
        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);
7948
      }
7949
      else
7950
      {
7951 7952 7953 7954 7955 7956
        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);
7957
      }
7958 7959
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7960
    }
7961
    case NDB_GT_FUNC:
7962
    {
7963
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
7964 7965
      // Save value in right format for the field type
      value->save_in_field(field);
7966
      if (a == field)
7967
      {
7968 7969 7970 7971 7972 7973
        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);
7974
      }
7975
      else
7976
      {
7977 7978 7979 7980 7981 7982
        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);
7983
      }
7984 7985
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7986
    }
7987
    case NDB_LIKE_FUNC:
7988
    {
7989
      if (!value || !field) break;
7990 7991 7992
      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
7993 7994 7995
      // Save value in right format for the field type
      value->save_in_field(field);
      DBUG_PRINT("info", ("Generating LIKE filter: like(%d,%s,%d)", 
7996 7997 7998 7999
                          field->get_field_no(), value->get_val(), 
                          value->pack_length()));
      if (filter->cmp(NdbScanFilter::COND_LIKE, 
                      field->get_field_no(),
8000 8001
                      value->get_val(),
                      value->pack_length()) == -1)
8002
        DBUG_RETURN(1);
8003 8004
      cond= cond->next->next->next;
      DBUG_RETURN(0);
8005
    }
8006 8007 8008 8009 8010 8011 8012 8013 8014 8015 8016 8017 8018 8019 8020 8021 8022 8023 8024 8025
    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:
8026 8027 8028 8029 8030
      if (!field)
        break;
      DBUG_PRINT("info", ("Generating ISNULL filter"));
      if (filter->isnull(field->get_field_no()) == -1)
        DBUG_RETURN(1);
8031 8032
      cond= cond->next->next;
      DBUG_RETURN(0);
8033
    case NDB_ISNOTNULL_FUNC:
8034
    {
8035 8036 8037 8038 8039
      if (!field)
        break;
      DBUG_PRINT("info", ("Generating ISNOTNULL filter"));
      if (filter->isnotnull(field->get_field_no()) == -1)
        DBUG_RETURN(1);         
8040 8041
      cond= cond->next->next;
      DBUG_RETURN(0);
8042 8043 8044 8045 8046 8047 8048 8049 8050 8051
    }
    default:
      break;
    }
    break;
  }
  default:
    break;
  }
  DBUG_PRINT("info", ("Found illegal condition"));
8052
  DBUG_RETURN(1);
8053 8054
}

8055

8056
int
8057
ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter)
8058
{
8059
  uint level=0;
8060
  bool negated= FALSE;
8061
  DBUG_ENTER("build_scan_filter_group");
8062

8063 8064
  do
  {
8065 8066 8067 8068 8069 8070
    if (!cond)
      DBUG_RETURN(1);
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
    {
      switch (cond->ndb_item->qualification.function_type) {
8071
      case NDB_COND_AND_FUNC:
8072
      {
8073 8074 8075 8076 8077
        level++;
        DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NAND":"AND",
                            level));
        if ((negated) ? filter->begin(NdbScanFilter::NAND)
            : filter->begin(NdbScanFilter::AND) == -1)
8078
          DBUG_RETURN(1);
8079
        negated= FALSE;
8080 8081 8082
        cond= cond->next;
        break;
      }
8083
      case NDB_COND_OR_FUNC:
8084
      {
8085 8086 8087 8088 8089 8090
        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);
8091
        negated= FALSE;
8092 8093 8094
        cond= cond->next;
        break;
      }
8095
      case NDB_NOT_FUNC:
8096
      {
8097
        DBUG_PRINT("info", ("Generating negated query"));
8098
        cond= cond->next;
8099
        negated= TRUE;
8100 8101 8102 8103
        break;
      }
      default:
        if (build_scan_filter_predicate(cond, filter, negated))
8104
          DBUG_RETURN(1);
8105
        negated= FALSE;
8106 8107 8108
        break;
      }
      break;
8109 8110
    }
    case NDB_END_COND:
8111 8112
      DBUG_PRINT("info", ("End of group %u", level));
      level--;
8113 8114
      if (cond) cond= cond->next;
      if (filter->end() == -1)
8115
        DBUG_RETURN(1);
8116 8117 8118
      if (!negated)
        break;
      // else fall through (NOT END is an illegal condition)
8119 8120
    default:
    {
8121
      DBUG_PRINT("info", ("Illegal scan filter"));
8122
    }
8123
    }
8124
  }  while (level > 0 || negated);
8125
  
8126
  DBUG_RETURN(0);
8127 8128
}

8129

8130 8131
int
ha_ndbcluster::build_scan_filter(Ndb_cond * &cond, NdbScanFilter *filter)
8132 8133 8134 8135
{
  bool simple_cond= TRUE;
  DBUG_ENTER("build_scan_filter");  

8136 8137 8138
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
      switch (cond->ndb_item->qualification.function_type) {
8139 8140
      case NDB_COND_AND_FUNC:
      case NDB_COND_OR_FUNC:
8141 8142 8143 8144 8145 8146 8147 8148 8149
        simple_cond= FALSE;
        break;
      default:
        break;
      }
      break;
    default:
      break;
    }
8150 8151 8152 8153 8154 8155
  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);
8156

8157
  DBUG_RETURN(0);
8158 8159
}

8160
int
8161
ha_ndbcluster::generate_scan_filter(Ndb_cond_stack *ndb_cond_stack,
8162
                                    NdbScanOperation *op)
8163 8164
{
  DBUG_ENTER("generate_scan_filter");
8165

8166 8167 8168
  if (ndb_cond_stack)
  {
    NdbScanFilter filter(op);
8169 8170
    
    DBUG_RETURN(generate_scan_filter_from_cond(ndb_cond_stack, filter));
8171 8172 8173 8174 8175 8176
  }
  else
  {  
    DBUG_PRINT("info", ("Empty stack"));
  }

8177
  DBUG_RETURN(0);
8178 8179
}

8180

8181 8182 8183 8184 8185
int
ha_ndbcluster::generate_scan_filter_from_cond(Ndb_cond_stack *ndb_cond_stack,
					      NdbScanFilter& filter)
{
  bool multiple_cond= FALSE;
8186 8187
  DBUG_ENTER("generate_scan_filter_from_cond");

8188 8189 8190 8191 8192 8193 8194 8195 8196 8197 8198 8199 8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210 8211 8212
  // 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);
}

8213

8214 8215 8216 8217 8218 8219 8220 8221 8222 8223 8224
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");
8225

8226 8227 8228 8229 8230 8231 8232 8233 8234 8235 8236 8237 8238 8239 8240 8241 8242 8243 8244 8245 8246 8247 8248 8249 8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261
  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);
}

8262 8263 8264 8265 8266 8267 8268 8269 8270
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,
8271 8272
	       "Cannot call SHOW NDBCLUSTER STATUS because skip-ndbcluster is "
               "defined",
8273 8274 8275 8276 8277 8278 8279 8280 8281 8282
	       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));

8283 8284
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
8285 8286
    DBUG_RETURN(TRUE);
  
8287
  if (get_thd_ndb(thd) && get_thd_ndb(thd)->ndb)
8288
  {
8289
    Ndb* ndb= (get_thd_ndb(thd))->ndb;
8290 8291
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 8305 8306 8307 8308
    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);
}

8309
#endif /* HAVE_NDBCLUSTER_DB */