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

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

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

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

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

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

#include "mysql_priv.h"

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

35 36 37
// 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
38
extern ulong opt_ndb_cache_check_time;
39

40
// Default value for parallelism
41
static const int parallelism= 0;
42

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

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

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

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

77
#define NDB_AUTO_INCREMENT_RETRIES 10
78

79 80
#define NDB_INVALID_SCHEMA_OBJECT 241

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

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

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

97
bool ndbcluster_inited= FALSE;
98

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

102 103 104 105 106 107 108 109 110 111 112 113 114
// Handler synchronization
pthread_mutex_t ndbcluster_mutex;

// Table lock handling
static HASH ndbcluster_open_tables;

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

static int packfrm(const void *data, uint len, const void **pack_data, uint *pack_len);
static int unpackfrm(const void **data, uint *len,
115
                     const void* pack_data);
116

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

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

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

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

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

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

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

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

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

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

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

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

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

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

205 206
  { 284, HA_ERR_TABLE_DEF_CHANGED, 0 },

207 208 209
  { 0, 1, 0 },

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


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


230 231

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

529 530
    invalidate_dictionary_cache(TRUE);

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


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

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

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

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


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

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


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

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


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


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

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

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

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

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

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


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

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

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

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


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

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

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


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

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

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

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

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

1020
static int fix_unique_index_attr_order(NDB_INDEX_DATA &data,
1021 1022
                                       const NDBINDEX *index,
                                       KEY *key_info)
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
{
  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++)
    {
1042
      const NDBCOL *c= index->getColumn(j);
msvensson@neptunus.(none)'s avatar
msvensson@neptunus.(none) committed
1043
      if (strcmp(field_name, c->getName()) == 0)
1044
      {
1045 1046
        data.unique_index_attrid_map[i]= j;
        break;
1047 1048 1049 1050 1051 1052
      }
    }
    DBUG_ASSERT(data.unique_index_attrid_map[i] != 255);
  }
  DBUG_RETURN(0);
}
1053

1054 1055


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

1159

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

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

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

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

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

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

1214 1215 1216
  DBUG_VOID_RETURN;
}

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

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

  /* PRIMARY_KEY_INDEX */
1244
  HA_ONLY_WHOLE_INDEX, 
1245 1246

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

  /* UNIQUE_INDEX */
1259
  HA_ONLY_WHOLE_INDEX,
1260

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

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

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

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

1288 1289 1290 1291 1292 1293 1294 1295

/*
  Get the flags for an index

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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 1620
 * 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
1621 1622
*/

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

jonas@perch.ndb.mysql.com's avatar
jonas@perch.ndb.mysql.com committed
1632 1633 1634
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
  
1635
  first= NULL;
1636
  if (check_pk && table->s->primary_key != MAX_KEY)
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 1667
  {
    /*
     * 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());
1668

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

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

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

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

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

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

1841 1842 1843 1844 1845 1846 1847
*/

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2269
    if ((res= set_primary_key_from_record(op, record)))
2270
      return res;  
2271 2272 2273
  }

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

2286 2287
  m_rows_changed++;

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

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

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


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

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

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

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

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

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

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

2515 2516
  m_rows_changed++;

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

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


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

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

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

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

2572 2573
    no_uncommitted_rows_update(-1);

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

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

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

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

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

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

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

#ifndef DBUG_OFF
2711
  const NDBTAB *tab= (const NDBTAB*) m_table;
2712

2713 2714
  if (!_db_on_)
    DBUG_VOID_RETURN;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
2715

2716
  char buf_type[MAX_FIELD_WIDTH], buf_val[MAX_FIELD_WIDTH];
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
2717
  String type(buf_type, sizeof(buf_type), &my_charset_bin);
2718
  String val(buf_val, sizeof(buf_val), &my_charset_bin);
2719
  for (uint f= 0; f < table->s->fields; f++)
2720
  {
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
2721
    /* Use DBUG_PRINT since DBUG_FILE cannot be filtered out */
2722
    char buf[2000];
2723
    Field *field;
2724
    void* ptr;
pekka@mysql.com's avatar
pekka@mysql.com committed
2725
    NdbValue value;
2726

2727
    buf[0]= 0;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
2728
    field= table->field[f];
pekka@mysql.com's avatar
pekka@mysql.com committed
2729
    if (!(value= m_value[f]).ptr)
2730
    {
2731
      strmov(buf, "not read");
2732
      goto print_value;
2733
    }
2734

2735
    ptr= field->ptr;
pekka@mysql.com's avatar
pekka@mysql.com committed
2736 2737

    if (! (field->flags & BLOB_FLAG))
2738
    {
pekka@mysql.com's avatar
pekka@mysql.com committed
2739 2740
      if (value.rec->isNULL())
      {
2741
        strmov(buf, "NULL");
2742
        goto print_value;
pekka@mysql.com's avatar
pekka@mysql.com committed
2743
      }
2744 2745 2746 2747 2748
      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
2749 2750 2751
    }
    else
    {
2752
      NdbBlob *ndb_blob= value.blob;
2753
      bool isNull= TRUE;
pekka@mysql.com's avatar
pekka@mysql.com committed
2754
      ndb_blob->getNull(isNull);
2755 2756
      if (isNull)
        strmov(buf, "NULL");
2757
    }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
2758

2759
print_value:
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
2760
    DBUG_PRINT("value", ("%u,%s: %s", f, field->field_name, buf));
2761 2762 2763 2764 2765 2766 2767 2768
  }
#endif
  DBUG_VOID_RETURN;
}


int ha_ndbcluster::index_init(uint index)
{
2769
  DBUG_ENTER("ha_ndbcluster::index_init");
2770
  DBUG_PRINT("enter", ("index: %u", index));
2771 2772 2773 2774 2775 2776
 /*
    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;
2777 2778 2779 2780 2781 2782
  DBUG_RETURN(handler::index_init(index));
}


int ha_ndbcluster::index_end()
{
2783
  DBUG_ENTER("ha_ndbcluster::index_end");
2784
  DBUG_RETURN(close_scan());
2785 2786
}

2787 2788 2789 2790 2791 2792 2793 2794
/**
 * 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;
2795
  const byte* end_ptr= key + key_len;
2796 2797 2798 2799 2800 2801
  curr_part= key_info->key_part;
  end_part= curr_part + key_info->key_parts;
  

  for (; curr_part != end_part && key < end_ptr; curr_part++)
  {
2802
    if (curr_part->null_bit && *key)
2803 2804 2805 2806 2807 2808
      return 1;

    key += curr_part->store_length;
  }
  return 0;
}
2809 2810

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


int ha_ndbcluster::index_read_idx(byte *buf, uint index_no, 
2879 2880
                              const byte *key, uint key_len, 
                              enum ha_rkey_function find_flag)
2881
{
2882
  statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status);
2883
  DBUG_ENTER("ha_ndbcluster::index_read_idx");
2884 2885 2886 2887 2888 2889 2890 2891
  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)
{
2892
  DBUG_ENTER("ha_ndbcluster::index_next");
2893
  statistic_increment(current_thd->status_var.ha_read_next_count,
2894
                      &LOCK_status);
2895
  DBUG_RETURN(next_result(buf));
2896 2897 2898 2899 2900
}


int ha_ndbcluster::index_prev(byte *buf)
{
2901
  DBUG_ENTER("ha_ndbcluster::index_prev");
2902
  statistic_increment(current_thd->status_var.ha_read_prev_count,
2903
                      &LOCK_status);
2904
  DBUG_RETURN(next_result(buf));
2905 2906 2907 2908 2909
}


int ha_ndbcluster::index_first(byte *buf)
{
2910
  DBUG_ENTER("ha_ndbcluster::index_first");
2911
  statistic_increment(current_thd->status_var.ha_read_first_count,
2912
                      &LOCK_status);
2913 2914 2915
  // Start the ordered index scan and fetch the first row

  // Only HA_READ_ORDER indexes get called by index_first
2916
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, FALSE, buf));
2917 2918 2919 2920 2921
}


int ha_ndbcluster::index_last(byte *buf)
{
2922
  DBUG_ENTER("ha_ndbcluster::index_last");
2923
  statistic_increment(current_thd->status_var.ha_read_last_count,&LOCK_status);
2924
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, TRUE, buf));
2925 2926
}

2927 2928 2929 2930 2931
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));
}
2932

2933 2934
inline
int ha_ndbcluster::read_range_first_to_buf(const key_range *start_key,
2935 2936 2937
                                           const key_range *end_key,
                                           bool eq_r, bool sorted,
                                           byte* buf)
2938
{
2939 2940
   ndb_index_type type= get_index_type(active_index);
KEY* key_info;
2941 2942
  int error= 1; 
  DBUG_ENTER("ha_ndbcluster::read_range_first_to_buf");
2943
  DBUG_PRINT("info", ("eq_r: %d, sorted: %d", eq_r, sorted));
2944

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

  // Start the ordered index scan and fetch the first row
2982
  error= ordered_index_scan(start_key, end_key, sorted, FALSE, buf);
2983 2984 2985
  DBUG_RETURN(error);
}

2986

joreland@mysql.com's avatar
joreland@mysql.com committed
2987
int ha_ndbcluster::read_range_first(const key_range *start_key,
2988 2989
                                    const key_range *end_key,
                                    bool eq_r, bool sorted)
joreland@mysql.com's avatar
joreland@mysql.com committed
2990 2991 2992 2993 2994
{
  byte* buf= table->record[0];
  DBUG_ENTER("ha_ndbcluster::read_range_first");
  
  DBUG_RETURN(read_range_first_to_buf(start_key,
2995 2996 2997 2998
                                      end_key,
                                      eq_r, 
                                      sorted,
                                      buf));
joreland@mysql.com's avatar
joreland@mysql.com committed
2999 3000
}

3001
int ha_ndbcluster::read_range_next()
3002 3003 3004 3005 3006 3007
{
  DBUG_ENTER("ha_ndbcluster::read_range_next");
  DBUG_RETURN(next_result(table->record[0]));
}


3008 3009
int ha_ndbcluster::rnd_init(bool scan)
{
3010
  NdbScanOperation *cursor= m_active_cursor;
3011 3012
  DBUG_ENTER("rnd_init");
  DBUG_PRINT("enter", ("scan: %d", scan));
3013
  // Check if scan is to be restarted
mskold@mysql.com's avatar
mskold@mysql.com committed
3014 3015 3016 3017
  if (cursor)
  {
    if (!scan)
      DBUG_RETURN(1);
3018
    if (cursor->restart(m_force_send) != 0)
3019 3020 3021 3022
    {
      DBUG_ASSERT(0);
      DBUG_RETURN(-1);
    }
mskold@mysql.com's avatar
mskold@mysql.com committed
3023
  }
3024
  index_init(table->s->primary_key);
3025 3026 3027
  DBUG_RETURN(0);
}

3028 3029
int ha_ndbcluster::close_scan()
{
3030
  NdbTransaction *trans= m_active_trans;
3031 3032
  DBUG_ENTER("close_scan");

3033 3034
  m_multi_cursor= 0;
  if (!m_active_cursor && !m_multi_cursor)
3035 3036
    DBUG_RETURN(1);

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

int ha_ndbcluster::rnd_end()
{
  DBUG_ENTER("rnd_end");
3081
  DBUG_RETURN(close_scan());
3082 3083 3084 3085 3086 3087
}


int ha_ndbcluster::rnd_next(byte *buf)
{
  DBUG_ENTER("rnd_next");
3088
  statistic_increment(current_thd->status_var.ha_read_rnd_next_count,
3089
                      &LOCK_status);
3090

3091
  if (!m_active_cursor)
3092 3093
    DBUG_RETURN(full_table_scan(buf));
  DBUG_RETURN(next_result(buf));
3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106
}


/*
  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");
3107
  statistic_increment(current_thd->status_var.ha_read_rnd_count,
3108
                      &LOCK_status);
3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128
  // 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");

3129
  if (table->s->primary_key != MAX_KEY) 
3130
  {
3131
    key_info= table->key_info + table->s->primary_key;
3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146
    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;
      }
3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166

      size_t len = key_part->length;
      const byte * ptr = record + key_part->offset;
      Field *field = key_part->field;
      if ((field->type() ==  MYSQL_TYPE_VARCHAR) &&
	  ((Field_varstring*)field)->length_bytes == 1)
      {
	/** 
	 * Keys always use 2 bytes length
	 */
	buff[0] = ptr[0];
	buff[1] = 0;
	memcpy(buff+2, ptr + 1, len);	
	len += 2;
      }
      else
      {
	memcpy(buff, ptr, len);
      }
      buff += len;
3167 3168 3169 3170 3171 3172
    }
  } 
  else 
  {
    // No primary key, get hidden key
    DBUG_PRINT("info", ("Getting hidden key"));
3173
#ifndef DBUG_OFF
3174
    int hidden_no= table->s->fields;
joreland@mysql.com's avatar
joreland@mysql.com committed
3175
    const NDBTAB *tab= (const NDBTAB *) m_table;  
3176 3177 3178 3179
    const NDBCOL *hidden_col= tab->getColumn(hidden_no);
    DBUG_ASSERT(hidden_col->getPrimaryKey() && 
                hidden_col->getAutoIncrement() &&
                ref_length == NDB_HIDDEN_PRIMARY_KEY_LENGTH);
3180
#endif
3181
    memcpy(ref, m_ref, ref_length);
3182 3183 3184 3185 3186 3187 3188
  }
  
  DBUG_DUMP("ref", (char*)ref, ref_length);
  DBUG_VOID_RETURN;
}


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

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

  DBUG_RETURN(result);
3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282
}


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"));
3283 3284
    DBUG_PRINT("info", ("Clearing condition stack"));
    cond_clear();
3285 3286
    m_delete_cannot_batch= FALSE;
    m_update_cannot_batch= FALSE;
3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 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
    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"));
3356 3357
    DBUG_PRINT("info", ("Ignoring duplicate key"));
    m_ignore_dup_key= TRUE;
3358 3359 3360
    break;
  case HA_EXTRA_NO_IGNORE_DUP_KEY:
    DBUG_PRINT("info", ("HA_EXTRA_NO_IGNORE_DUP_KEY"));
3361
    m_ignore_dup_key= FALSE;
3362 3363
    break;
  case HA_EXTRA_RETRIEVE_ALL_COLS:    /* Retrieve all columns, not just those
3364 3365
                                         where field->query_id is the same as
                                         the current query id */
3366
    DBUG_PRINT("info", ("HA_EXTRA_RETRIEVE_ALL_COLS"));
3367
    m_retrieve_all_fields= TRUE;
3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379
    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"));
3380
    m_retrieve_primary_key= TRUE;
3381 3382 3383 3384 3385 3386
    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"));
3387 3388
  case HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
    DBUG_PRINT("info", ("HA_EXTRA_KEYREAD_PRESERVE_FIELDS"));
3389
    break;
3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402
  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;
3403 3404 3405 3406 3407 3408 3409 3410
  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;
3411 3412 3413 3414 3415
  }
  
  DBUG_RETURN(0);
}

3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428
/* 
   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
3429
  const NDBTAB *tab= (const NDBTAB *) m_table;    
3430 3431

  DBUG_ENTER("start_bulk_insert");
pekka@mysql.com's avatar
pekka@mysql.com committed
3432
  DBUG_PRINT("enter", ("rows: %d", (int)rows));
3433
  
3434
  m_rows_inserted= (ha_rows) 0;
3435
  if (!m_use_write && m_ignore_dup_key)
3436 3437 3438
  {
    /*
      compare if expression with that in write_row
3439
      we have a situation where peek_indexed_rows() will be called
3440 3441 3442 3443 3444 3445 3446 3447
      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;
  }
3448
  if (rows == (ha_rows) 0)
3449
  {
3450 3451
    /* We don't know how many will be inserted, guess */
    m_rows_to_insert= m_autoincrement_prefetch;
3452
  }
3453 3454
  else
    m_rows_to_insert= rows; 
3455 3456 3457 3458 3459 3460 3461 3462

  /* 
    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.   
  */
3463
  const int bytesperbatch= 8192;
3464
  bytes= 12 + tab->getRowSizeInBytes() + 4 * tab->getNoOfColumns();
3465
  batch= bytesperbatch/bytes;
3466 3467
  batch= batch == 0 ? 1 : batch;
  DBUG_PRINT("info", ("batch: %d, bytes: %d", batch, bytes));
3468
  m_bulk_insert_rows= batch;
3469 3470 3471 3472 3473 3474 3475 3476 3477

  DBUG_VOID_RETURN;
}

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

3480
  DBUG_ENTER("end_bulk_insert");
3481
  // Check if last inserts need to be flushed
3482
  if (m_bulk_insert_not_flushed)
3483
  {
3484
    NdbTransaction *trans= m_active_trans;
3485 3486 3487
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
                        "rows_inserted:%d, bulk_insert_rows: %d", 
3488
                        (int) m_rows_inserted, (int) m_bulk_insert_rows)); 
3489
    m_bulk_insert_not_flushed= FALSE;
3490 3491
    if (m_transaction_on)
    {
3492
      if (execute_no_commit(this, trans,false) != 0)
3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504
      {
        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);
      }
3505 3506 3507 3508 3509
      else
      {
        int res= trans->restart();
        DBUG_ASSERT(res == 0);
      }
3510
    }
3511 3512
  }

3513 3514
  m_rows_inserted= (ha_rows) 0;
  m_rows_to_insert= (ha_rows) 1;
3515
  DBUG_RETURN(error);
3516 3517
}

3518 3519 3520 3521

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
3522
  DBUG_PRINT("enter", ("cache_size: %lu", cache_size));
3523 3524 3525
  DBUG_RETURN(extra(operation));
}

3526 3527 3528 3529
static const char *ha_ndbcluster_exts[] = {
 ha_ndb_ext,
 NullS
};
3530

3531
const char** ha_ndbcluster::bas_ext() const
3532 3533 3534
{
  return ha_ndbcluster_exts;
}
3535 3536 3537 3538 3539 3540 3541 3542 3543

/*
  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()
{
3544 3545 3546
  DBUG_ENTER("ha_ndbcluster::scan_time()");
  double res= rows2double(records*1000);
  DBUG_PRINT("exit", ("table: %s value: %f", 
3547
                      m_tabname, res));
3548
  DBUG_RETURN(res);
3549 3550
}

3551 3552 3553 3554 3555 3556 3557
/*
  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
*/
3558 3559 3560 3561 3562 3563 3564 3565

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

3567 3568 3569
    /* If we are not doing a LOCK TABLE, then allow multiple
       writers */
    
3570 3571 3572
    /* Since NDB does not currently have table locks
       this is treated as a ordinary lock */

3573
    if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588
         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;
3589 3590

  DBUG_PRINT("exit", ("lock_type: %d", lock_type));
3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612
  
  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
3613
  for the statement, this will be stored in thd_ndb.stmt.
3614
  If not, we have to start a master transaction if there doesn't exist
3615
  one from before, this will be stored in thd_ndb.all
3616 3617 3618
 
  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  
3619
  If we are locking the table then:
3620
  - save the NdbDictionary::Table for easy access
3621 3622
  - save reference to table statistics
  - refresh list of the indexes for the table if needed (if altered)
3623 3624 3625 3626 3627
 */

int ha_ndbcluster::external_lock(THD *thd, int lock_type)
{
  int error=0;
3628
  NdbTransaction* trans= NULL;
3629 3630 3631 3632 3633 3634

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

3638
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
3639
  Ndb *ndb= thd_ndb->ndb;
3640

3641 3642
  DBUG_PRINT("enter", ("thd: %x, thd_ndb: %x, thd_ndb->lock_count: %d",
                       thd, thd_ndb, thd_ndb->lock_count));
3643

3644 3645
  if (lock_type != F_UNLCK)
  {
3646
    DBUG_PRINT("info", ("lock_type != F_UNLCK"));
3647 3648 3649 3650 3651 3652 3653 3654
    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)
3655 3656 3657
      m_transaction_on= FALSE;
    else
      m_transaction_on= thd->variables.ndb_use_transactions;
3658
    if (!thd_ndb->lock_count++)
3659 3660
    {
      PRINT_OPTION_FLAGS(thd);
3661
      if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) 
3662 3663
      {
        // Autocommit transaction
3664
        DBUG_ASSERT(!thd_ndb->stmt);
3665 3666
        DBUG_PRINT("trans",("Starting transaction stmt"));      

3667
        trans= ndb->startTransaction();
3668
        if (trans == NULL)
3669
          ERR_RETURN(ndb->getNdbError());
3670
        no_uncommitted_rows_reset(thd);
3671
        thd_ndb->stmt= trans;
3672
	thd_ndb->query_state&= NDB_QUERY_NORMAL;
3673
        trans_register_ha(thd, FALSE, &ndbcluster_hton);
3674 3675 3676
      } 
      else 
      { 
3677
        if (!thd_ndb->all)
3678
        {
3679 3680 3681 3682
          // Not autocommit transaction
          // A "master" transaction ha not been started yet
          DBUG_PRINT("trans",("starting transaction, all"));
          
3683
          trans= ndb->startTransaction();
3684
          if (trans == NULL)
3685
            ERR_RETURN(ndb->getNdbError());
3686
          no_uncommitted_rows_reset(thd);
3687
          thd_ndb->all= trans; 
3688
	  thd_ndb->query_state&= NDB_QUERY_NORMAL;
3689
          trans_register_ha(thd, TRUE, &ndbcluster_hton);
3690 3691 3692 3693 3694 3695 3696 3697

          /*
            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))
3698
          {
3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717
            //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. 
     */

3718 3719 3720
    // 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;
3721 3722
    m_autoincrement_prefetch= 
      (ha_rows) thd->variables.ndb_autoincrement_prefetch_sz;
3723

3724
    m_active_trans= thd_ndb->all ? thd_ndb->all : thd_ndb->stmt;
3725
    DBUG_ASSERT(m_active_trans);
3726
    // Start of transaction
3727
    m_rows_changed= 0;
3728
    m_retrieve_all_fields= FALSE;
3729
    m_retrieve_primary_key= FALSE;
3730
    m_ops_pending= 0;
3731
    {
3732
      NDBDICT *dict= ndb->getDictionary();
3733 3734 3735
      const NDBTAB *tab;
      void *tab_info;
      if (!(tab= dict->getTable(m_tabname, &tab_info)))
3736
        ERR_RETURN(dict->getNdbError());
3737 3738 3739
      DBUG_PRINT("info", ("Table schema version: %d", 
                          tab->getObjectVersion()));
      // Check if thread has stale local cache
3740 3741 3742 3743
      // New transaction must not use old tables... (trans != 0)
      // Running might...
      if ((trans && tab->getObjectStatus() != NdbDictionary::Object::Retrieved)
	  || tab->getObjectStatus() == NdbDictionary::Object::Invalid)
3744 3745
      {
        invalidate_dictionary_cache(FALSE);
3746
        if (!(tab= dict->getTable(m_tabname, &tab_info)))
3747 3748 3749 3750
          ERR_RETURN(dict->getNdbError());
        DBUG_PRINT("info", ("Table schema version: %d", 
                            tab->getObjectVersion()));
      }
3751
      if (m_table_version < tab->getObjectVersion())
3752 3753 3754 3755 3756 3757 3758
      {
        /*
          The table has been altered, caller has to retry
        */
        NdbError err= ndb->getNdbError(NDB_INVALID_SCHEMA_OBJECT);
        DBUG_RETURN(ndb_to_mysql_error(&err));
      }
3759 3760 3761 3762
      if (m_table != (void *)tab)
      {
        m_table= (void *)tab;
        m_table_version = tab->getObjectVersion();
3763
        if ((my_errno= build_index_list(ndb, table, ILBP_OPEN)))
3764
          DBUG_RETURN(my_errno);
3765 3766 3767

        const void *data, *pack_data;
        uint length, pack_length;
3768
        if (readfrm(table->s->path, &data, &length) ||
3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779
            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));
3780
      }
3781 3782
      m_table_info= tab_info;
    }
3783
    no_uncommitted_rows_init(thd);
3784 3785
  }
  else
3786
  {
3787
    DBUG_PRINT("info", ("lock_type == F_UNLCK"));
3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805

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

3806
    if (!--thd_ndb->lock_count)
3807 3808 3809 3810
    {
      DBUG_PRINT("trans", ("Last external_lock"));
      PRINT_OPTION_FLAGS(thd);

3811
      if (thd_ndb->stmt)
3812 3813 3814 3815 3816 3817 3818
      {
        /*
          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"));
3819
        ndb->closeTransaction(m_active_trans);
3820
        thd_ndb->stmt= NULL;
3821 3822
      }
    }
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
3823
    m_table_info= NULL;
3824

3825 3826 3827 3828 3829 3830 3831 3832 3833
    /*
      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;    

3834 3835
    if (m_active_cursor)
      DBUG_PRINT("warning", ("m_active_cursor != NULL"));
3836 3837
    m_active_cursor= NULL;

3838 3839 3840 3841
    if (m_multi_cursor)
      DBUG_PRINT("warning", ("m_multi_cursor != NULL"));
    m_multi_cursor= NULL;
    
3842
    if (m_blobs_pending)
3843
      DBUG_PRINT("warning", ("blobs_pending != 0"));
3844
    m_blobs_pending= 0;
3845
    
3846
    if (m_ops_pending)
3847
      DBUG_PRINT("warning", ("ops_pending != 0L"));
3848
    m_ops_pending= 0;
3849 3850 3851 3852
  }
  DBUG_RETURN(error);
}

3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868
/*
  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;
}

3869
/*
3870 3871 3872 3873 3874
  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
3875 3876
*/

serg@serg.mylan's avatar
serg@serg.mylan committed
3877
int ha_ndbcluster::start_stmt(THD *thd, thr_lock_type lock_type)
3878 3879 3880 3881 3882
{
  int error=0;
  DBUG_ENTER("start_stmt");
  PRINT_OPTION_FLAGS(thd);

3883
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
3884
  NdbTransaction *trans= (thd_ndb->stmt)?thd_ndb->stmt:thd_ndb->all;
3885
  if (!trans){
3886
    Ndb *ndb= thd_ndb->ndb;
3887
    DBUG_PRINT("trans",("Starting transaction stmt"));  
3888
    trans= ndb->startTransaction();
3889
    if (trans == NULL)
3890
      ERR_RETURN(ndb->getNdbError());
3891
    no_uncommitted_rows_reset(thd);
3892
    thd_ndb->stmt= trans;
3893
    thd_ndb->query_state&= NDB_QUERY_NORMAL;
3894
    trans_register_ha(thd, FALSE, &ndbcluster_hton);
3895
  }
3896
  m_active_trans= trans;
3897
  // Start of statement
3898
  m_retrieve_all_fields= FALSE;
3899
  m_retrieve_primary_key= FALSE;
3900
  m_ops_pending= 0;    
3901 3902 3903 3904 3905 3906
  
  DBUG_RETURN(error);
}


/*
3907
  Commit a transaction started in NDB
3908 3909
 */

3910
int ndbcluster_commit(THD *thd, bool all)
3911 3912
{
  int res= 0;
3913 3914 3915
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  Ndb *ndb= thd_ndb->ndb;
  NdbTransaction *trans= all ? thd_ndb->all : thd_ndb->stmt;
3916 3917 3918

  DBUG_ENTER("ndbcluster_commit");
  DBUG_PRINT("transaction",("%s",
3919
                            trans == thd_ndb->stmt ?
3920 3921 3922
                            "stmt" : "all"));
  DBUG_ASSERT(ndb && trans);

3923
  if (execute_commit(thd,trans) != 0)
3924 3925
  {
    const NdbError err= trans->getNdbError();
3926
    const NdbOperation *error_op= trans->getNdbErrorOperation();
3927
    ERR_PRINT(err);
3928
    res= ndb_to_mysql_error(&err);
3929
    if (res != -1)
3930
      ndbcluster_print_error(res, error_op);
3931
  }
3932
  ndb->closeTransaction(trans);
3933

3934
  if (all)
3935 3936 3937
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;
3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951

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

3952 3953 3954 3955 3956 3957 3958 3959
  DBUG_RETURN(res);
}


/*
  Rollback a transaction started in NDB
 */

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

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

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

3984
  if (all)
3985 3986 3987 3988
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;

3989 3990 3991
  /* Clear list of tables changed by transaction */
  thd_ndb->changed_tables.empty();

3992
  DBUG_RETURN(res);
3993 3994 3995 3996
}


/*
pekka@mysql.com's avatar
pekka@mysql.com committed
3997 3998 3999
  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
4000 4001 4002

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

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

/*
  Create a table in NDB Cluster
 */

mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4279 4280
static void ndb_set_fragmentation(NDBTAB &tab, TABLE *form, uint pk_length)
{
4281 4282 4283 4284 4285
  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
4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302
    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;
4303 4304 4305 4306 4307
    /*
     * 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
4308
#if MYSQL_VERSION_ID >= 50100
4309
    no_fragments= (big_max_rows*acc_row_size)/acc_fragment_size+1;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4310
#else
4311
    no_fragments= ((big_max_rows*acc_row_size)/acc_fragment_size+1
4312
                   +1/*correct rounding*/)/2;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4313 4314 4315 4316 4317 4318 4319 4320 4321
#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)
4322 4323
        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
4324 4325 4326 4327 4328 4329 4330
    }
    else if (no_fragments > no_nodes)
      ftype= NDBTAB::FragAllMedium;
    else
      ftype= NDBTAB::FragAllSmall;
    tab.setFragmentType(ftype);
  }
4331 4332
  tab.setMaxRows(max_rows);
  tab.setMinRows(min_rows);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4333 4334
}

4335
int ha_ndbcluster::create(const char *name, 
4336 4337
                          TABLE *form, 
                          HA_CREATE_INFO *info)
4338 4339 4340
{
  NDBTAB tab;
  NDBCOL col;
joreland@mysql.com's avatar
joreland@mysql.com committed
4341
  uint pack_length, length, i, pk_length= 0;
4342 4343
  const void *data, *pack_data;
  char name2[FN_HEADLEN];
4344
  bool create_from_engine= (info->table_options & HA_OPTION_CREATE_FROM_ENGINE);
4345

pekka@mysql.com's avatar
pekka@mysql.com committed
4346
  DBUG_ENTER("ha_ndbcluster::create");
4347 4348 4349
  DBUG_PRINT("enter", ("name: %s", name));
  fn_format(name2, name, "", "",2);       // Remove the .frm extension
  set_dbname(name2);
4350 4351
  set_tabname(name2);    

4352 4353 4354 4355 4356 4357
  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);
  }
4358 4359 4360 4361 4362 4363 4364 4365 4366 4367
  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);
  }
4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383

  DBUG_PRINT("table", ("name: %s", m_tabname));  
  tab.setName(m_tabname);
  tab.setLogging(!(info->options & HA_LEX_CREATE_TMP_TABLE));    
   
  // Save frm data for this table
  if (readfrm(name, &data, &length))
    DBUG_RETURN(1);
  if (packfrm(data, length, &pack_data, &pack_length))
    DBUG_RETURN(2);
  
  DBUG_PRINT("info", ("setFrm data=%x, len=%d", pack_data, pack_length));
  tab.setFrm(pack_data, pack_length);      
  my_free((char*)data, MYF(0));
  my_free((char*)pack_data, MYF(0));
  
4384
  for (i= 0; i < form->s->fields; i++) 
4385 4386 4387 4388
  {
    Field *field= form->field[i];
    DBUG_PRINT("info", ("name: %s, type: %u, pack_length: %d", 
                        field->field_name, field->real_type(),
4389
                        field->pack_length()));
4390
    if ((my_errno= create_ndb_column(col, field, info)))
pekka@mysql.com's avatar
pekka@mysql.com committed
4391
      DBUG_RETURN(my_errno);
4392
    tab.addColumn(col);
4393
    if (col.getPrimaryKey())
joreland@mysql.com's avatar
joreland@mysql.com committed
4394
      pk_length += (field->pack_length() + 3) / 4;
4395 4396 4397
  }
  
  // No primary key, create shadow key as 64 bit, auto increment  
4398
  if (form->s->primary_key == MAX_KEY) 
4399 4400 4401 4402 4403
  {
    DBUG_PRINT("info", ("Generating shadow key"));
    col.setName("$PK");
    col.setType(NdbDictionary::Column::Bigunsigned);
    col.setLength(1);
4404
    col.setNullable(FALSE);
4405 4406 4407
    col.setPrimaryKey(TRUE);
    col.setAutoIncrement(TRUE);
    tab.addColumn(col);
joreland@mysql.com's avatar
joreland@mysql.com committed
4408 4409 4410 4411
    pk_length += 2;
  }
  
  // Make sure that blob tables don't have to big part size
4412
  for (i= 0; i < form->s->fields; i++) 
joreland@mysql.com's avatar
joreland@mysql.com committed
4413 4414 4415 4416 4417 4418 4419
  {
    /**
     * 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()) {
4420
    case MYSQL_TYPE_GEOMETRY:
joreland@mysql.com's avatar
joreland@mysql.com committed
4421 4422 4423 4424
    case MYSQL_TYPE_BLOB:    
    case MYSQL_TYPE_MEDIUM_BLOB:   
    case MYSQL_TYPE_LONG_BLOB: 
    {
4425 4426
      NdbDictionary::Column * col= tab.getColumn(i);
      int size= pk_length + (col->getPartSize()+3)/4 + 7;
4427
      if (size > NDB_MAX_TUPLE_SIZE_IN_WORDS && 
4428
         (pk_length+7) < NDB_MAX_TUPLE_SIZE_IN_WORDS)
joreland@mysql.com's avatar
joreland@mysql.com committed
4429
      {
4430 4431
        size= NDB_MAX_TUPLE_SIZE_IN_WORDS - pk_length - 7;
        col->setPartSize(4*size);
joreland@mysql.com's avatar
joreland@mysql.com committed
4432 4433 4434 4435 4436 4437 4438 4439 4440 4441
      }
      /**
       * 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;
    }
4442
  }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4443 4444 4445

  ndb_set_fragmentation(tab, form, pk_length);

4446
  if ((my_errno= check_ndb_connection()))
4447 4448 4449
    DBUG_RETURN(my_errno);
  
  // Create the table in NDB     
4450 4451
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
4452
  if (dict->createTable(tab) != 0) 
4453 4454 4455 4456 4457 4458 4459 4460
  {
    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));
4461

4462
  // Create secondary indexes
4463
  my_errno= build_index_list(ndb, form, ILBP_CREATE);
4464

4465 4466 4467
  if (!my_errno)
    my_errno= write_ndb_file();

4468 4469 4470 4471
  DBUG_RETURN(my_errno);
}


4472
int ha_ndbcluster::create_ordered_index(const char *name, 
4473
                                        KEY *key_info)
4474
{
4475
  DBUG_ENTER("ha_ndbcluster::create_ordered_index");
4476
  DBUG_RETURN(create_index(name, key_info, FALSE));
4477 4478 4479
}

int ha_ndbcluster::create_unique_index(const char *name, 
4480
                                       KEY *key_info)
4481 4482
{

4483
  DBUG_ENTER("ha_ndbcluster::create_unique_index");
4484
  DBUG_RETURN(create_index(name, key_info, TRUE));
4485 4486 4487
}


4488 4489 4490 4491 4492
/*
  Create an index in NDB Cluster
 */

int ha_ndbcluster::create_index(const char *name, 
4493 4494
                                KEY *key_info,
                                bool unique)
4495
{
4496 4497
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
4498 4499 4500
  KEY_PART_INFO *key_part= key_info->key_part;
  KEY_PART_INFO *end= key_part + key_info->key_parts;
  
4501
  DBUG_ENTER("ha_ndbcluster::create_index");
4502
  DBUG_PRINT("enter", ("name: %s ", name));
4503

4504
  NdbDictionary::Index ndb_index(name);
4505
  if (unique)
4506 4507 4508 4509 4510
    ndb_index.setType(NdbDictionary::Index::UniqueHashIndex);
  else 
  {
    ndb_index.setType(NdbDictionary::Index::OrderedIndex);
    // TODO Only temporary ordered indexes supported
4511
    ndb_index.setLogging(FALSE); 
4512 4513 4514 4515 4516 4517 4518
  }
  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
4519
    ndb_index.addColumnName(field->field_name);
4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535
  }
  
  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)
{
4536
  NDBDICT *dict;
4537
  char new_tabname[FN_HEADLEN];
4538
  char new_dbname[FN_HEADLEN];
4539 4540
  const NDBTAB *orig_tab;
  int result;
4541 4542
  bool recreate_indexes= FALSE;
  NDBDICT::List index_list;
4543 4544

  DBUG_ENTER("ha_ndbcluster::rename_table");
4545
  DBUG_PRINT("info", ("Renaming %s to %s", from, to));
4546
  set_dbname(from);
4547
  set_dbname(to, new_dbname);
4548 4549 4550
  set_tabname(from);
  set_tabname(to, new_tabname);

4551 4552 4553
  if (check_ndb_connection())
    DBUG_RETURN(my_errno= HA_ERR_NO_CONNECTION);

mskold@mysql.com's avatar
mskold@mysql.com committed
4554 4555
  Ndb *ndb= get_ndb();
  dict= ndb->getDictionary();
4556 4557
  if (!(orig_tab= dict->getTable(m_tabname)))
    ERR_RETURN(dict->getNdbError());
4558 4559 4560 4561 4562 4563 4564
  // 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());
  }
4565 4566 4567 4568 4569 4570
  if (my_strcasecmp(system_charset_info, new_dbname, m_dbname))
  {
    dict->listIndexes(index_list, m_tabname);
    recreate_indexes= TRUE;
  }

4571 4572 4573
  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
4574
  ndb->setDatabaseName(m_dbname);
4575
  if (!(result= alter_table_name(new_tabname)))
4576
  {
4577 4578
    // Rename .ndb file
    result= handler::rename_table(from, to);
4579
  }
4580

4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608
  // 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);
    }
  }

4609 4610 4611 4612 4613 4614 4615 4616
  DBUG_RETURN(result);
}


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

4617
int ha_ndbcluster::alter_table_name(const char *to)
4618
{
4619 4620
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
4621
  const NDBTAB *orig_tab= (const NDBTAB *) m_table;
4622 4623
  DBUG_ENTER("alter_table_name_table");

4624
  NdbDictionary::Table new_tab= *orig_tab;
4625 4626
  new_tab.setName(to);
  if (dict->alterTable(new_tab) != 0)
4627 4628 4629
    ERR_RETURN(dict->getNdbError());

  m_table= NULL;
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
4630
  m_table_info= NULL;
4631 4632 4633 4634 4635 4636
                                                                             
  DBUG_RETURN(0);
}


/*
4637 4638
  Delete table from NDB Cluster

4639 4640 4641 4642
 */

int ha_ndbcluster::delete_table(const char *name)
{
4643
  DBUG_ENTER("ha_ndbcluster::delete_table");
4644 4645 4646
  DBUG_PRINT("enter", ("name: %s", name));
  set_dbname(name);
  set_tabname(name);
4647

4648 4649
  if (check_ndb_connection())
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4650 4651

  /* Call ancestor function to delete .ndb file */
4652
  handler::delete_table(name);
4653 4654
  
  /* Drop the table from NDB */
4655 4656 4657 4658 4659
  DBUG_RETURN(drop_table());
}


/*
4660
  Drop table in NDB Cluster
4661 4662 4663 4664
 */

int ha_ndbcluster::drop_table()
{
4665
  THD *thd= current_thd;
4666 4667
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
4668

4669 4670 4671 4672
  DBUG_ENTER("drop_table");
  DBUG_PRINT("enter", ("Deleting %s", m_tabname));
  
  release_metadata();
4673
  while (dict->dropTable(m_tabname)) 
4674 4675
  {
    const NdbError err= dict->getNdbError();
4676 4677 4678 4679 4680 4681 4682 4683 4684
    switch (err.status)
    {
      case NdbError::TemporaryError:
        if (!thd->killed)
          continue; // retry indefinitly
        break;
      default:
        break;
    }
4685
    ERR_RETURN(dict->getNdbError());
4686 4687
  }

4688 4689 4690 4691
  DBUG_RETURN(0);
}


4692
ulonglong ha_ndbcluster::get_auto_increment()
4693
{  
4694 4695
  int cache_size;
  Uint64 auto_value;
4696 4697
  DBUG_ENTER("get_auto_increment");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));
4698
  Ndb *ndb= get_ndb();
4699
   
4700
  if (m_rows_inserted > m_rows_to_insert)
4701
  {
4702 4703
    /* We guessed too low */
    m_rows_to_insert+= m_autoincrement_prefetch;
4704
  }
serg@serg.mylan's avatar
serg@serg.mylan committed
4705
  cache_size= 
4706 4707 4708 4709
    (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));
4710
  int ret;
4711 4712
  uint retries= NDB_AUTO_INCREMENT_RETRIES;
  do {
4713 4714 4715 4716 4717
    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 && 
4718 4719
           --retries &&
           ndb->getNdbError().status == NdbError::TemporaryError);
4720
  if (ret == -1)
4721 4722 4723 4724 4725 4726
  {
    const NdbError err= ndb->getNdbError();
    sql_print_error("Error %lu in ::get_auto_increment(): %s",
                    (ulong) err.code, err.message);
    DBUG_RETURN(~(ulonglong) 0);
  }
4727
  DBUG_RETURN((longlong)auto_value);
4728 4729 4730 4731 4732 4733 4734 4735
}


/*
  Constructor for the NDB Cluster table handler 
 */

ha_ndbcluster::ha_ndbcluster(TABLE *table_arg):
4736
  handler(&ndbcluster_hton, table_arg),
4737 4738 4739
  m_active_trans(NULL),
  m_active_cursor(NULL),
  m_table(NULL),
4740
  m_table_version(-1),
4741
  m_table_info(NULL),
4742
  m_table_flags(HA_REC_NOT_IN_SEQ |
4743 4744 4745 4746
                HA_NULL_IN_KEY |
                HA_AUTO_PART_KEY |
                HA_NO_PREFIX_CHAR_KEYS |
                HA_NEED_READ_RANGE_BUFFER |
4747
                HA_CAN_GEOMETRY |
4748 4749
                HA_CAN_BIT_FIELD |
                HA_PARTIAL_COLUMN_READ),
4750
  m_share(0),
4751
  m_use_write(FALSE),
4752
  m_ignore_dup_key(FALSE),
4753
  m_has_unique_index(FALSE),
4754 4755
  m_primary_key_update(FALSE),
  m_retrieve_all_fields(FALSE),
4756
  m_retrieve_primary_key(FALSE),
4757 4758 4759
  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
4760
  m_rows_changed((ha_rows) 0),
4761
  m_bulk_insert_not_flushed(FALSE),
4762 4763
  m_delete_cannot_batch(FALSE),
  m_update_cannot_batch(FALSE),
4764 4765 4766
  m_ops_pending(0),
  m_skip_auto_increment(TRUE),
  m_blobs_pending(0),
4767
  m_blobs_offset(0),
4768 4769
  m_blobs_buffer(0),
  m_blobs_buffer_size(0),
4770 4771 4772
  m_dupkey((uint) -1),
  m_ha_not_exact_count(FALSE),
  m_force_send(TRUE),
4773
  m_autoincrement_prefetch((ha_rows) 32),
4774
  m_transaction_on(TRUE),
mskold@mysql.com's avatar
mskold@mysql.com committed
4775 4776
  m_cond_stack(NULL),
  m_multi_cursor(NULL)
4777
{
4778
  int i;
4779
 
4780 4781 4782 4783 4784
  DBUG_ENTER("ha_ndbcluster");

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

4785
  records= ~(ha_rows)0; // uninitialized
4786 4787
  block_size= 1024;

4788 4789
  for (i= 0; i < MAX_KEY; i++)
  {
4790 4791 4792 4793
    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;
4794 4795
  }

4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807
  DBUG_VOID_RETURN;
}


/*
  Destructor for NDB Cluster table handler
 */

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

4808 4809
  if (m_share)
    free_share(m_share);
4810
  release_metadata();
4811 4812
  my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
  m_blobs_buffer= 0;
4813 4814

  // Check for open cursor/transaction
4815 4816
  if (m_active_cursor) {
  }
4817
  DBUG_ASSERT(m_active_cursor == NULL);
4818 4819
  if (m_active_trans) {
  }
4820 4821
  DBUG_ASSERT(m_active_trans == NULL);

4822 4823 4824 4825
  // Discard the condition stack
  DBUG_PRINT("info", ("Clearing condition stack"));
  cond_clear();

4826 4827 4828 4829
  DBUG_VOID_RETURN;
}


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

4831 4832 4833 4834 4835 4836 4837 4838
/*
  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
4839
  int res;
4840 4841 4842 4843 4844 4845 4846 4847
  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
  
4848
  if (table->s->primary_key != MAX_KEY) 
4849
  {
4850
    key= table->key_info+table->s->primary_key;
4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861
    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);
  
4862 4863
  if (check_ndb_connection()) {
    free_share(m_share); m_share= 0;
4864
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4865
  }
4866
  
tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
4867 4868
  res= get_metadata(name);
  if (!res)
4869 4870 4871
  {
    Ndb *ndb= get_ndb();
    ndb->setDatabaseName(m_dbname);
stewart@willster.(none)'s avatar
stewart@willster.(none) committed
4872 4873 4874
    struct Ndb_statistics stat;
    res= ndb_get_table_statistics(NULL, false, ndb, m_tabname, &stat);
    records= stat.row_count;
4875 4876 4877
    if(!res)
      res= info(HA_STATUS_CONST);
  }
4878

tomas@poseidon.ndb.mysql.com's avatar
tomas@poseidon.ndb.mysql.com committed
4879
  DBUG_RETURN(res);
4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890
}


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

int ha_ndbcluster::close(void)
{
  DBUG_ENTER("close");  
4891
  free_share(m_share); m_share= 0;
4892 4893 4894 4895 4896
  release_metadata();
  DBUG_RETURN(0);
}


4897
Thd_ndb* ha_ndbcluster::seize_thd_ndb()
4898
{
4899 4900
  Thd_ndb *thd_ndb;
  DBUG_ENTER("seize_thd_ndb");
4901

4902
  thd_ndb= new Thd_ndb();
4903 4904 4905
  thd_ndb->ndb->getDictionary()->set_local_table_data_size(
    sizeof(Ndb_local_table_statistics)
    );
4906
  if (thd_ndb->ndb->init(max_transactions) != 0)
4907
  {
4908
    ERR_PRINT(thd_ndb->ndb->getNdbError());
4909 4910 4911 4912 4913 4914
    /*
      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 
    */
4915 4916
    delete thd_ndb;
    thd_ndb= NULL;
4917
  }
4918
  DBUG_RETURN(thd_ndb);
4919 4920 4921
}


4922
void ha_ndbcluster::release_thd_ndb(Thd_ndb* thd_ndb)
4923
{
4924 4925
  DBUG_ENTER("release_thd_ndb");
  delete thd_ndb;
4926 4927 4928 4929 4930
  DBUG_VOID_RETURN;
}


/*
magnus@neptunus.(none)'s avatar
magnus@neptunus.(none) committed
4931
  If this thread already has a Thd_ndb object allocated
4932
  in current THD, reuse it. Otherwise
magnus@neptunus.(none)'s avatar
magnus@neptunus.(none) committed
4933
  seize a Thd_ndb object, assign it to current THD and use it.
4934 4935 4936
 
*/

4937
Ndb* check_ndb_in_thd(THD* thd)
4938
{
4939
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
4940
  if (!thd_ndb)
4941
  {
magnus@neptunus.(none)'s avatar
magnus@neptunus.(none) committed
4942
    if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4943
      return NULL;
4944
    set_thd_ndb(thd, thd_ndb);
4945
  }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
4946
  return thd_ndb->ndb;
4947 4948
}

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

4950

4951
int ha_ndbcluster::check_ndb_connection(THD* thd)
4952
{
4953
  Ndb *ndb;
4954 4955
  DBUG_ENTER("check_ndb_connection");
  
4956
  if (!(ndb= check_ndb_in_thd(thd)))
4957
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4958
  ndb->setDatabaseName(m_dbname);
4959 4960 4961
  DBUG_RETURN(0);
}

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

4963
int ndbcluster_close_connection(THD *thd)
4964
{
4965
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
4966
  DBUG_ENTER("ndbcluster_close_connection");
4967 4968
  if (thd_ndb)
  {
4969
    ha_ndbcluster::release_thd_ndb(thd_ndb);
4970
    set_thd_ndb(thd, NULL); // not strictly required but does not hurt either
4971
  }
4972
  DBUG_RETURN(0);
4973 4974 4975 4976 4977 4978 4979
}


/*
  Try to discover one table from NDB
 */

4980
int ndbcluster_discover(THD* thd, const char *db, const char *name,
4981
                        const void** frmblob, uint* frmlen)
4982 4983 4984 4985
{
  uint len;
  const void* data;
  const NDBTAB* tab;
4986
  Ndb* ndb;
4987
  DBUG_ENTER("ndbcluster_discover");
4988
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name)); 
4989

4990 4991 4992
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(HA_ERR_NO_CONNECTION);  
  ndb->setDatabaseName(db);
4993

4994
  NDBDICT* dict= ndb->getDictionary();
4995
  dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
4996 4997 4998 4999 5000
  dict->invalidateTable(name);
  if (!(tab= dict->getTable(name)))
  {    
    const NdbError err= dict->getNdbError();
    if (err.code == 709)
5001
      DBUG_RETURN(-1);
5002
    ERR_RETURN(err);
5003 5004 5005 5006 5007 5008
  }
  DBUG_PRINT("info", ("Found table %s", tab->getName()));
  
  len= tab->getFrmLength();  
  if (len == 0 || tab->getFrmData() == NULL)
  {
5009 5010
    DBUG_PRINT("error", ("No frm data found."));
    DBUG_RETURN(1);
5011 5012 5013
  }
  
  if (unpackfrm(&data, &len, tab->getFrmData()))
5014 5015 5016 5017
  {
    DBUG_PRINT("error", ("Could not unpack table"));
    DBUG_RETURN(1);
  }
5018 5019 5020 5021 5022 5023 5024 5025

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

/*
5026
  Check if a table exists in NDB
5027

5028
 */
5029

5030
int ndbcluster_table_exists_in_engine(THD* thd, const char *db, const char *name)
5031 5032 5033
{
  const NDBTAB* tab;
  Ndb* ndb;
5034
  DBUG_ENTER("ndbcluster_table_exists_in_engine");
5035
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
5036 5037

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

  NDBDICT* dict= ndb->getDictionary();
5042
  dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
5043 5044
  dict->invalidateTable(name);
  if (!(tab= dict->getTable(name)))
5045
  {
5046 5047 5048 5049 5050
    const NdbError err= dict->getNdbError();
    if (err.code == 709)
      DBUG_RETURN(0);
    ERR_RETURN(err);
  }
5051

5052 5053 5054 5055
  DBUG_PRINT("info", ("Found table %s", tab->getName()));
  DBUG_RETURN(1);
}

5056 5057


5058
extern "C" byte* tables_get_key(const char *entry, uint *length,
5059
                                my_bool not_used __attribute__((unused)))
5060 5061 5062 5063 5064 5065
{
  *length= strlen(entry);
  return (byte*) entry;
}


5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079
/*
  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;
5080
  int ret= 0;
5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106
  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++))
5107
  {
5108
    while (dict->dropTable(tabname))
5109 5110
    {
      const NdbError err= dict->getNdbError();
5111 5112 5113 5114 5115 5116 5117 5118 5119 5120
      switch (err.status)
      {
        case NdbError::TemporaryError:
          if (!thd->killed)
            continue; // retry indefinitly
          break;
        default:
          break;
      }
      if (err.code != 709) // 709: No such table existed
5121 5122
      {
        ERR_PRINT(err);
5123
        ret= ndb_to_mysql_error(&err);
5124
      }
5125
      break;
5126 5127 5128
    }
  }
  DBUG_RETURN(ret);      
5129 5130 5131
}


5132
int ndbcluster_find_files(THD *thd,const char *db,const char *path,
5133
                          const char *wild, bool dir, List<char> *files)
5134
{
5135 5136 5137
  DBUG_ENTER("ndbcluster_find_files");
  DBUG_PRINT("enter", ("db: %s", db));
  { // extra bracket to avoid gcc 2.95.3 warning
5138
  uint i;
5139
  Ndb* ndb;
5140
  char name[FN_REFLEN];
5141
  HASH ndb_tables, ok_tables;
5142
  NdbDictionary::Dictionary::List list;
5143 5144 5145 5146

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

5147
  if (dir)
5148
    DBUG_RETURN(0); // Discover of databases not yet supported
5149

5150
  // List tables in NDB
5151
  NDBDICT *dict= ndb->getDictionary();
5152
  if (dict->listObjects(list, 
5153
                        NdbDictionary::Object::UserTable) != 0)
5154
    ERR_RETURN(dict->getNdbError());
5155

5156
  if (hash_init(&ndb_tables, system_charset_info,list.count,0,0,
5157
                (hash_get_key)tables_get_key,0,0))
5158 5159 5160 5161 5162 5163
  {
    DBUG_PRINT("error", ("Failed to init HASH ndb_tables"));
    DBUG_RETURN(-1);
  }

  if (hash_init(&ok_tables, system_charset_info,32,0,0,
5164
                (hash_get_key)tables_get_key,0,0))
5165 5166 5167 5168 5169 5170
  {
    DBUG_PRINT("error", ("Failed to init HASH ok_tables"));
    hash_free(&ndb_tables);
    DBUG_RETURN(-1);
  }  

5171 5172 5173
  for (i= 0 ; i < list.count ; i++)
  {
    NdbDictionary::Dictionary::List::Element& t= list.elements[i];
5174
    DBUG_PRINT("info", ("Found %s/%s in NDB", t.database, t.name));     
5175

5176 5177 5178
    // Add only tables that belongs to db
    if (my_strcasecmp(system_charset_info, t.database, db))
      continue;
5179

5180
    // Apply wildcard to list of tables in NDB
5181
    if (wild)
5182
    {
5183 5184
      if (lower_case_table_names)
      {
5185 5186
        if (wild_case_compare(files_charset_info, t.name, wild))
          continue;
5187 5188
      }
      else if (wild_compare(t.name,wild,0))
5189
        continue;
5190
    }
5191 5192
    DBUG_PRINT("info", ("Inserting %s into ndb_tables hash", t.name));     
    my_hash_insert(&ndb_tables, (byte*)thd->strdup(t.name));
5193 5194
  }

5195 5196 5197 5198 5199
  char *file_name;
  List_iterator<char> it(*files);
  List<char> delete_list;
  while ((file_name=it++))
  {
5200
    bool file_on_disk= false;
5201 5202 5203 5204
    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));
5205
      file_on_disk= true;
5206 5207
    }
    
5208
    // Check for .ndb file with this name
5209
    (void)strxnmov(name, FN_REFLEN, 
5210
                   mysql_data_home,"/",db,"/",file_name,ha_ndb_ext,NullS);
5211
    DBUG_PRINT("info", ("Check access for %s", name));
5212
    if (access(name, F_OK))
5213 5214 5215
    {
      DBUG_PRINT("info", ("%s did not exist on disk", name));     
      // .ndb file did not exist on disk, another table type
5216
      if (file_on_disk)
5217 5218 5219 5220 5221
      {
	// Ignore this ndb table
	gptr record=  hash_search(&ndb_tables, file_name, strlen(file_name));
	DBUG_ASSERT(record);
	hash_delete(&ndb_tables, record);
5222 5223 5224 5225
	push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
			    ER_TABLE_EXISTS_ERROR,
			    "Local table %s.%s shadows ndb table",
			    db, file_name);
5226
      }
5227 5228 5229 5230
      continue;
    }
    if (file_on_disk) 
    {
5231
      // File existed in NDB and as frm file, put in ok_tables list
5232
      my_hash_insert(&ok_tables, (byte*)file_name);
5233
      continue;
5234
    }
5235 5236 5237
    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.
5238
    if (ndbcluster_table_exists_in_engine(thd, db, file_name) == 0)    
5239 5240 5241 5242 5243 5244 5245
    {
      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));
    }
  }
5246

5247 5248 5249 5250
  // 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++)
5251
  {
5252 5253
    file_name= hash_element(&ndb_tables, i);
    if (!hash_search(&ok_tables, file_name, strlen(file_name)))
5254
    {
5255 5256 5257 5258 5259 5260
      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));
    }
  }
5261

5262 5263
  // Lock mutex before deleting and creating frm files
  pthread_mutex_lock(&LOCK_open);
5264

5265 5266 5267 5268 5269
  if (!global_read_lock)
  {
    // Delete old files
    List_iterator_fast<char> it3(delete_list);
    while ((file_name=it3++))
5270 5271
    {
      DBUG_PRINT("info", ("Remove table %s/%s", db, file_name));
5272 5273 5274 5275
      // Delete the table and all related files
      TABLE_LIST table_list;
      bzero((char*) &table_list,sizeof(table_list));
      table_list.db= (char*) db;
5276
      table_list.alias= table_list.table_name= (char*)file_name;
5277
      (void)mysql_rm_table_part2(thd, &table_list,
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5278 5279 5280 5281
                                                                 /* if_exists */ FALSE,
                                                                 /* drop_temporary */ FALSE,
                                                                 /* drop_view */ FALSE,
                                                                 /* dont_log_query*/ TRUE);
5282 5283
      /* Clear error message that is returned when table is deleted */
      thd->clear_error();
5284 5285 5286
    }
  }

5287 5288 5289 5290
  // Create new files
  List_iterator_fast<char> it2(create_list);
  while ((file_name=it2++))
  {  
5291
    DBUG_PRINT("info", ("Table %s need discovery", file_name));
5292
    if (ha_create_table_from_engine(thd, db, file_name) == 0)
5293
      files->push_back(thd->strdup(file_name)); 
5294 5295 5296 5297 5298
  }

  pthread_mutex_unlock(&LOCK_open);      
  
  hash_free(&ok_tables);
5299
  hash_free(&ndb_tables);
5300
  } // extra bracket to avoid gcc 2.95.3 warning
5301
  DBUG_RETURN(0);    
5302 5303 5304 5305 5306 5307 5308 5309
}


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

5310 5311 5312 5313 5314 5315 5316
/* Call back after cluster connect */
static int connect_callback()
{
  update_status_variables(g_ndb_cluster_connection);
  return 0;
}

5317
bool ndbcluster_init()
5318
{
5319
  int res;
5320
  DBUG_ENTER("ndbcluster_init");
5321 5322 5323 5324

  if (have_ndbcluster != SHOW_OPTION_YES)
    goto ndbcluster_init_error;

5325
  // Set connectstring if specified
5326 5327
  if (opt_ndbcluster_connectstring != 0)
    DBUG_PRINT("connectstring", ("%s", opt_ndbcluster_connectstring));     
5328
  if ((g_ndb_cluster_connection=
5329
       new Ndb_cluster_connection(opt_ndbcluster_connectstring)) == 0)
5330
  {
5331
    DBUG_PRINT("error",("Ndb_cluster_connection(%s)",
5332
                        opt_ndbcluster_connectstring));
5333
    goto ndbcluster_init_error;
5334
  }
tomas@poseidon.ndb.mysql.com's avatar
ndb:  
tomas@poseidon.ndb.mysql.com committed
5335 5336 5337 5338 5339
  {
    char buf[128];
    my_snprintf(buf, sizeof(buf), "mysqld --server-id=%d", server_id);
    g_ndb_cluster_connection->set_name(buf);
  }
5340 5341 5342
  g_ndb_cluster_connection->set_optimized_node_selection
    (opt_ndb_optimized_node_selection);

5343
  // Create a Ndb object to open the connection  to NDB
5344 5345 5346 5347 5348
  if ( (g_ndb= new Ndb(g_ndb_cluster_connection, "sys")) == 0 )
  {
    DBUG_PRINT("error", ("failed to create global ndb object"));
    goto ndbcluster_init_error;
  }
5349
  g_ndb->getDictionary()->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
5350 5351 5352
  if (g_ndb->init() != 0)
  {
    ERR_PRINT (g_ndb->getNdbError());
5353
    goto ndbcluster_init_error;
5354
  }
5355

5356
  if ((res= g_ndb_cluster_connection->connect(0,0,0)) == 0)
5357
  {
5358
    connect_callback();
5359
    DBUG_PRINT("info",("NDBCLUSTER storage engine at %s on port %d",
5360 5361
                       g_ndb_cluster_connection->get_connected_host(),
                       g_ndb_cluster_connection->get_connected_port()));
5362
    g_ndb_cluster_connection->wait_until_ready(10,3);
5363
  } 
5364
  else if (res == 1)
5365
  {
5366
    if (g_ndb_cluster_connection->start_connect_thread(connect_callback)) 
5367
    {
5368
      DBUG_PRINT("error", ("g_ndb_cluster_connection->start_connect_thread()"));
5369 5370
      goto ndbcluster_init_error;
    }
5371
#ifndef DBUG_OFF
5372 5373
    {
      char buf[1024];
5374
      DBUG_PRINT("info",
5375 5376 5377 5378
                 ("NDBCLUSTER storage engine not started, "
                  "will connect using %s",
                  g_ndb_cluster_connection->
                  get_connectstring(buf,sizeof(buf))));
5379
    }
5380
#endif
5381
  }
5382
  else
5383 5384 5385
  {
    DBUG_ASSERT(res == -1);
    DBUG_PRINT("error", ("permanent error"));
5386
    goto ndbcluster_init_error;
5387
  }
5388
  
5389 5390 5391
  (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
5392 5393
  pthread_mutex_init(&LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST);
  pthread_cond_init(&COND_ndb_util_thread, NULL);
5394

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

jonas@perch.ndb.mysql.com's avatar
jonas@perch.ndb.mysql.com committed
5396
  ndb_cache_check_time = opt_ndb_cache_check_time;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5397 5398 5399 5400 5401
  // 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"));
5402 5403 5404 5405
    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
5406 5407 5408
    goto ndbcluster_init_error;
  }
  
5409
  ndbcluster_inited= 1;
5410
  DBUG_RETURN(FALSE);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5411

5412
ndbcluster_init_error:
5413
  if (g_ndb)
5414 5415 5416 5417 5418
    delete g_ndb;
  g_ndb= NULL;
  if (g_ndb_cluster_connection)
    delete g_ndb_cluster_connection;
  g_ndb_cluster_connection= NULL;
5419 5420
  have_ndbcluster= SHOW_OPTION_DISABLED;	// If we couldn't use handler
  DBUG_RETURN(TRUE);
5421 5422 5423 5424 5425 5426
}


/*
  End use of the NDB Cluster table handler
  - free all global variables allocated by 
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5427
    ndbcluster_init()
5428 5429 5430 5431 5432
*/

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

5434 5435 5436
  if (!ndbcluster_inited)
    DBUG_RETURN(0);

mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5437 5438 5439 5440 5441 5442
  // 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);

5443
  if (g_ndb)
5444 5445
  {
#ifndef DBUG_OFF
5446 5447
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
5448 5449 5450 5451 5452 5453 5454 5455 5456 5457
    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
5458
    delete g_ndb;
5459
    g_ndb= NULL;
5460
  }
5461
  delete g_ndb_cluster_connection;
5462
  g_ndb_cluster_connection= NULL;
5463

5464 5465
  hash_free(&ndbcluster_open_tables);
  pthread_mutex_destroy(&ndbcluster_mutex);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5466 5467
  pthread_mutex_destroy(&LOCK_ndb_util_thread);
  pthread_cond_destroy(&COND_ndb_util_thread);
5468 5469 5470 5471
  ndbcluster_inited= 0;
  DBUG_RETURN(0);
}

5472 5473 5474 5475 5476
/*
  Static error print function called from
  static handler method ndbcluster_commit
  and ndbcluster_rollback
*/
5477 5478

void ndbcluster_print_error(int error, const NdbOperation *error_op)
5479
{
5480 5481
  DBUG_ENTER("ndbcluster_print_error");
  TABLE tab;
5482
  const char *tab_name= (error_op) ? error_op->getTableName() : "";
5483
  tab.alias= (char *) tab_name;
5484
  ha_ndbcluster error_handler(&tab);
5485
  tab.file= &error_handler;
5486
  error_handler.print_error(error, MYF(0));
ndbdev@ndbmaster.mysql.com's avatar
ndbdev@ndbmaster.mysql.com committed
5487
  DBUG_VOID_RETURN;
5488
}
5489

5490 5491 5492
/**
 * Set a given location from full pathname to database name
 *
5493
 */
5494
void ha_ndbcluster::set_dbname(const char *path_name, char *dbname)
5495 5496 5497 5498
{
  char *end, *ptr;
  
  /* Scan name from the end */
5499 5500 5501 5502 5503 5504
  ptr= strend(path_name)-1;
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  ptr--;
  end= ptr;
5505 5506 5507 5508
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  uint name_len= end - ptr;
5509 5510
  memcpy(dbname, ptr + 1, name_len);
  dbname[name_len]= '\0';
5511 5512
#ifdef __WIN__
  /* Put to lower case */
5513 5514
  
  ptr= dbname;
5515 5516
  
  while (*ptr != '\0') {
5517
    *ptr= tolower(*ptr);
5518 5519 5520 5521 5522
    ptr++;
  }
#endif
}

5523 5524 5525 5526 5527 5528 5529 5530 5531
/*
  Set m_dbname from full pathname to table file
 */

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

5532 5533 5534 5535 5536 5537 5538 5539 5540 5541
/**
 * 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 */
5542 5543
  end= strend(path_name)-1;
  ptr= end;
5544 5545 5546
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
5547
  uint name_len= end - ptr;
5548
  memcpy(tabname, ptr + 1, end - ptr);
5549
  tabname[name_len]= '\0';
5550 5551
#ifdef __WIN__
  /* Put to lower case */
5552
  ptr= tabname;
5553 5554 5555 5556 5557 5558 5559 5560 5561
  
  while (*ptr != '\0') {
    *ptr= tolower(*ptr);
    ptr++;
  }
#endif
}

/*
5562
  Set m_tabname from full pathname to table file 
5563 5564
 */

5565
void ha_ndbcluster::set_tabname(const char *path_name)
5566
{
5567
  set_tabname(path_name, m_tabname);
5568 5569 5570 5571
}


ha_rows 
5572 5573 5574 5575
ha_ndbcluster::records_in_range(uint inx, key_range *min_key,
                                key_range *max_key)
{
  KEY *key_info= table->key_info + inx;
5576
  uint key_length= key_info->key_length;
5577
  NDB_INDEX_TYPE idx_type= get_index_type(inx);  
5578 5579

  DBUG_ENTER("records_in_range");
5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593
  // 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 */
5594 5595
}

5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622
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
5623 5624 5625 5626
uint ha_ndbcluster::max_supported_key_part_length() const
{
  return NDB_MAX_KEY_SIZE;
}
5627 5628 5629 5630 5631 5632 5633 5634 5635 5636
bool ha_ndbcluster::low_byte_first() const
{ 
#ifdef WORDS_BIGENDIAN
  return FALSE;
#else
  return TRUE;
#endif
}
bool ha_ndbcluster::has_transactions()
{
5637
  return TRUE;
5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651
}
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
5652

5653 5654
uint8 ha_ndbcluster::table_cache_type()
{
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5655 5656 5657 5658 5659 5660
  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,
5661
                         Uint64 *commit_count)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5662 5663 5664
{
  DBUG_ENTER("ndb_get_commitcount");

5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682
  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
5683 5684
  if (ndb_cache_check_time > 0)
  {
5685
    if (share->commit_count != 0)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5686
    {
5687
      *commit_count= share->commit_count;
5688 5689 5690
      char buff[22];
      DBUG_PRINT("info", ("Getting commit_count: %s from share",
                          llstr(share->commit_count, buff)));
5691 5692 5693
      pthread_mutex_unlock(&share->mutex);
      free_share(share);
      DBUG_RETURN(0);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5694 5695
    }
  }
5696
  DBUG_PRINT("info", ("Get commit_count from NDB"));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5697 5698 5699 5700
  Ndb *ndb;
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(1);
  ndb->setDatabaseName(dbname);
5701 5702
  uint lock= share->commit_count_lock;
  pthread_mutex_unlock(&share->mutex);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5703 5704

  struct Ndb_statistics stat;
5705
  if (ndb_get_table_statistics(NULL, false, ndb, tabname, &stat))
5706 5707
  {
    free_share(share);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5708
    DBUG_RETURN(1);
5709 5710 5711
  }

  pthread_mutex_lock(&share->mutex);
5712
  if (share->commit_count_lock == lock)
5713
  {
5714 5715 5716
    char buff[22];
    DBUG_PRINT("info", ("Setting commit_count to %s",
                        llstr(stat.commit_count, buff)));
5717 5718 5719 5720 5721 5722 5723 5724 5725 5726
    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
5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762
  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,
5763 5764
                                   char *full_name, uint full_name_len,
                                   ulonglong *engine_data)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5765 5766 5767 5768 5769
{
  Uint64 commit_count;
  bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
  char *dbname= full_name;
  char *tabname= dbname+strlen(dbname)+1;
5770 5771
  char buff[22], buff2[22];
  DBUG_ENTER("ndbcluster_cache_retrieval_allowed");
5772 5773
  DBUG_PRINT("enter", ("dbname: %s, tabname: %s, is_autocommit: %d",
                       dbname, tabname, is_autocommit));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5774 5775

  if (!is_autocommit)
5776 5777
  {
    DBUG_PRINT("exit", ("No, don't use cache in transaction"));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5778
    DBUG_RETURN(FALSE);
5779
  }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5780 5781 5782

  if (ndb_get_commitcount(thd, dbname, tabname, &commit_count))
  {
5783 5784
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, could not retrieve commit_count"));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5785 5786
    DBUG_RETURN(FALSE);
  }
5787 5788
  DBUG_PRINT("info", ("*engine_data: %s, commit_count: %s",
                      llstr(*engine_data, buff), llstr(commit_count, buff2)));
5789
  if (commit_count == 0)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5790
  {
5791 5792
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, local commit has been performed"));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5793 5794
    DBUG_RETURN(FALSE);
  }
5795 5796 5797 5798 5799 5800
  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
5801

5802 5803
  DBUG_PRINT("exit", ("OK to use cache, engine_data: %s",
                      llstr(*engine_data, buff)));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831
  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,
5832 5833 5834
                                          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
5835
{
5836 5837
  Uint64 commit_count;
  char buff[22];
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5838
  bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
5839
  DBUG_ENTER("ha_ndbcluster::register_query_cache_table");
5840 5841 5842
  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
5843
  if (!is_autocommit)
5844 5845
  {
    DBUG_PRINT("exit", ("Can't register table during transaction"))
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5846
    DBUG_RETURN(FALSE);
5847
  }
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5848 5849 5850 5851

  if (ndb_get_commitcount(thd, m_dbname, m_tabname, &commit_count))
  {
    *engine_data= 0;
5852
    DBUG_PRINT("exit", ("Error, could not get commitcount"))
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5853 5854 5855 5856
    DBUG_RETURN(FALSE);
  }
  *engine_data= commit_count;
  *engine_callback= ndbcluster_cache_retrieval_allowed;
5857
  DBUG_PRINT("exit", ("commit_count: %s", llstr(commit_count, buff)));
5858
  DBUG_RETURN(commit_count > 0);
5859
}
5860

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

5862
/*
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
5863
  Handling the shared NDB_SHARE structure that is needed to
5864 5865 5866 5867 5868 5869 5870
  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,
5871
                                my_bool not_used __attribute__((unused)))
5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899
{
  *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
5900
      share->commit_count= 0;
5901 5902 5903 5904 5905 5906 5907
      share->commit_count_lock= 0;
    }
    else
    {
      DBUG_PRINT("error", ("Failed to alloc share"));
      pthread_mutex_unlock(&ndbcluster_mutex);
      return 0;
5908 5909 5910
    }
  }
  share->use_count++;
5911 5912 5913 5914 5915

  DBUG_PRINT("share",
	     ("table_name: %s, length: %d, use_count: %d, commit_count: %d",
	      share->table_name, share->table_name_length, share->use_count,
	      share->commit_count));
5916 5917 5918 5919 5920 5921 5922 5923 5924 5925
  pthread_mutex_unlock(&ndbcluster_mutex);
  return share;
}


static void free_share(NDB_SHARE *share)
{
  pthread_mutex_lock(&ndbcluster_mutex);
  if (!--share->use_count)
  {
5926
     hash_delete(&ndbcluster_open_tables, (byte*) share);
5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954
    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, 
5955
                   const void **pack_data, uint *pack_len)
5956 5957 5958 5959 5960 5961 5962 5963 5964
{
  int error;
  ulong org_len, comp_len;
  uint blob_len;
  frm_blob_struct* blob;
  DBUG_ENTER("packfrm");
  DBUG_PRINT("enter", ("data: %x, len: %d", data, len));
  
  error= 1;
5965
  org_len= len;
5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984
  if (my_compress((byte*)data, &org_len, &comp_len))
    goto err;
  
  DBUG_PRINT("info", ("org_len: %d, comp_len: %d", org_len, comp_len));
  DBUG_DUMP("compressed", (char*)data, org_len);
  
  error= 2;
  blob_len= sizeof(frm_blob_struct::frm_blob_header)+org_len;
  if (!(blob= (frm_blob_struct*) my_malloc(blob_len,MYF(MY_WME))))
    goto err;
  
  // Store compressed blob in machine independent format
  int4store((char*)(&blob->head.ver), 1);
  int4store((char*)(&blob->head.orglen), comp_len);
  int4store((char*)(&blob->head.complen), org_len);
  
  // Copy frm data into blob, already in machine independent format
  memcpy(blob->data, data, org_len);  
  
5985 5986 5987
  *pack_data= blob;
  *pack_len= blob_len;
  error= 0;
5988 5989 5990 5991 5992 5993 5994 5995 5996
  
  DBUG_PRINT("exit", ("pack_data: %x, pack_len: %d", *pack_data, *pack_len));
err:
  DBUG_RETURN(error);
  
}


static int unpackfrm(const void **unpack_data, uint *unpack_len,
5997
                    const void *pack_data)
5998
{
5999
   const frm_blob_struct *blob= (frm_blob_struct*)pack_data;
6000 6001 6002 6003 6004
   byte *data;
   ulong complen, orglen, ver;
   DBUG_ENTER("unpackfrm");
   DBUG_PRINT("enter", ("pack_data: %x", pack_data));

6005 6006 6007
   complen=     uint4korr((char*)&blob->head.complen);
   orglen=      uint4korr((char*)&blob->head.orglen);
   ver=         uint4korr((char*)&blob->head.ver);
6008 6009
 
   DBUG_PRINT("blob",("ver: %d complen: %d orglen: %d",
6010
                     ver,complen,orglen));
6011 6012 6013 6014
   DBUG_DUMP("blob->data", (char*) blob->data, complen);
 
   if (ver != 1)
     DBUG_RETURN(1);
6015
   if (!(data= my_malloc(max(orglen, complen), MYF(MY_WME))))
6016 6017 6018 6019 6020 6021 6022 6023 6024
     DBUG_RETURN(2);
   memcpy(data, blob->data, complen);
 
   if (my_uncompress(data, &complen, &orglen))
   {
     my_free((char*)data, MYF(0));
     DBUG_RETURN(3);
   }

6025 6026
   *unpack_data= data;
   *unpack_len= complen;
6027 6028 6029 6030 6031

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

   DBUG_RETURN(0);
}
6032 6033 6034

static 
int
6035
ndb_get_table_statistics(ha_ndbcluster* file, bool report_error, Ndb* ndb,
stewart@willster.(none)'s avatar
stewart@willster.(none) committed
6036
                         const char* table,
6037
                         struct Ndb_statistics * ndbstat)
6038
{
6039
  NdbTransaction* pTrans;
6040
  NdbError error;
6041
  int retries= 10;
6042
  int reterr= 0;
6043
  int retry_sleep= 30 * 1000; /* 30 milliseconds */
6044 6045 6046
  char buff[22], buff2[22], buff3[22], buff4[22];
  DBUG_ENTER("ndb_get_table_statistics");
  DBUG_PRINT("enter", ("table: %s", table));
6047 6048

  do
6049
  {
6050 6051
    Uint64 rows, commits, mem;
    Uint32 size;
6052
    Uint32 count= 0;
6053 6054
    Uint64 sum_rows= 0;
    Uint64 sum_commits= 0;
6055 6056
    Uint64 sum_row_size= 0;
    Uint64 sum_mem= 0;
6057 6058 6059 6060 6061
    NdbScanOperation*pOp;
    NdbResultSet *rs;
    int check;

    if ((pTrans= ndb->startTransaction()) == NULL)
6062
    {
6063 6064 6065
      error= ndb->getNdbError();
      goto retry;
    }
6066
      
6067 6068 6069 6070
    if ((pOp= pTrans->getNdbScanOperation(table)) == NULL)
    {
      error= pTrans->getNdbError();
      goto retry;
6071
    }
6072
    
6073
    if (pOp->readTuples(NdbOperation::LM_CommittedRead))
6074 6075 6076 6077
    {
      error= pOp->getNdbError();
      goto retry;
    }
6078
    
6079 6080 6081 6082 6083
    if (pOp->interpret_exit_last_row() == -1)
    {
      error= pOp->getNdbError();
      goto retry;
    }
6084 6085 6086
    
    pOp->getValue(NdbDictionary::Column::ROW_COUNT, (char*)&rows);
    pOp->getValue(NdbDictionary::Column::COMMIT_COUNT, (char*)&commits);
6087 6088
    pOp->getValue(NdbDictionary::Column::ROW_SIZE, (char*)&size);
    pOp->getValue(NdbDictionary::Column::FRAGMENT_MEMORY, (char*)&mem);
6089
    
6090 6091 6092
    if (pTrans->execute(NdbTransaction::NoCommit,
                        NdbTransaction::AbortOnError,
                        TRUE) == -1)
6093
    {
6094 6095
      error= pTrans->getNdbError();
      goto retry;
6096
    }
6097
    
monty@mishka.local's avatar
monty@mishka.local committed
6098
    while ((check= pOp->nextResult(TRUE, TRUE)) == 0)
6099 6100 6101
    {
      sum_rows+= rows;
      sum_commits+= commits;
6102
      if (sum_row_size < size)
6103
        sum_row_size= size;
6104
      sum_mem+= mem;
6105
      count++;
6106 6107 6108
    }
    
    if (check == -1)
6109 6110 6111 6112
    {
      error= pOp->getNdbError();
      goto retry;
    }
6113

6114
    pOp->close(TRUE);
6115

6116
    ndb->closeTransaction(pTrans);
6117 6118 6119 6120 6121 6122

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

6123 6124 6125 6126 6127 6128 6129
    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));
6130

6131
    DBUG_RETURN(0);
6132
retry:
6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145
    if(report_error)
    {
      if (file)
      {
        reterr= file->ndb_err(pTrans);
      }
      else
      {
        const NdbError& tmp= error;
        ERR_PRINT(tmp);
        reterr= ndb_to_mysql_error(&tmp);
      }
    }
6146 6147 6148
    else
      reterr= error.code;

6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159
    if (pTrans)
    {
      ndb->closeTransaction(pTrans);
      pTrans= NULL;
    }
    if (error.status == NdbError::TemporaryError && retries--)
    {
      my_sleep(retry_sleep);
      continue;
    }
    break;
6160
  } while(1);
6161 6162 6163
  DBUG_PRINT("exit", ("failed, reterr: %u, NdbError %u(%s)", reterr,
                      error.code, error.message));
  DBUG_RETURN(reterr);
6164 6165
}

6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180
/*
  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, 
6181
                 mysql_data_home,"/",m_dbname,"/",m_tabname,ha_ndb_ext,NullS);
6182 6183 6184 6185 6186 6187 6188 6189 6190 6191

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

6192
void 
6193 6194
ha_ndbcluster::release_completed_operations(NdbTransaction *trans,
					    bool force_release)
6195 6196 6197 6198 6199 6200 6201 6202
{
  if (trans->hasBlobOperation())
  {
    /* We are reading/writing BLOB fields, 
       releasing operation records is unsafe
    */
    return;
  }
6203 6204 6205 6206 6207 6208 6209 6210 6211 6212
  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;
    }
  }
6213
  trans->releaseCompletedOperations();
6214 6215
}

6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239
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);
}

6240
int
6241
ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
6242 6243 6244 6245
                                      KEY_MULTI_RANGE *ranges, 
                                      uint range_count,
                                      bool sorted, 
                                      HANDLER_BUFFER *buffer)
6246 6247
{
  DBUG_ENTER("ha_ndbcluster::read_multi_range_first");
6248
  
6249 6250
  int res;
  KEY* key_info= table->key_info + active_index;
6251
  NDB_INDEX_TYPE index_type= get_index_type(active_index);
joreland@mysql.com's avatar
merge  
joreland@mysql.com committed
6252
  ulong reclength= table->s->reclength;
6253
  NdbOperation* op;
6254
  Thd_ndb *thd_ndb= get_thd_ndb(current_thd);
6255

6256 6257 6258 6259 6260 6261 6262
  /**
   * blobs and unique hash index with NULL can't be batched currently
   */
  if (uses_blob_value(m_retrieve_all_fields) ||
      (index_type ==  UNIQUE_INDEX &&
       has_null_in_unique_index(active_index) &&
       null_value_index_search(ranges, ranges+range_count, buffer)))
6263
  {
6264
    m_disable_multi_read= TRUE;
6265
    DBUG_RETURN(handler::read_multi_range_first(found_range_p, 
6266 6267 6268 6269
                                                ranges, 
                                                range_count,
                                                sorted, 
                                                buffer));
6270
  }
6271
  thd_ndb->query_state|= NDB_QUERY_MULTI_READ_RANGE;
6272
  m_disable_multi_read= FALSE;
6273 6274 6275 6276

  /**
   * Copy arguments into member variables
   */
6277 6278 6279
  m_multi_ranges= ranges;
  multi_range_curr= ranges;
  multi_range_end= ranges+range_count;
6280 6281 6282
  multi_range_sorted= sorted;
  multi_range_buffer= buffer;

6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293
  /**
   * 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
6294 6295
   */   

mskold@mysql.com's avatar
mskold@mysql.com committed
6296
  /**
6297 6298
   * Variables for loop
   */
6299 6300
  byte *curr= (byte*)buffer->buffer;
  byte *end_of_buffer= (byte*)buffer->buffer_end;
6301 6302
  NdbOperation::LockMode lm= 
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
6303
  bool need_pk = (lm == NdbOperation::LM_Read);
6304 6305
  const NDBTAB *tab= (const NDBTAB *) m_table;
  const NDBINDEX *unique_idx= (NDBINDEX *) m_index[active_index].unique_index;
6306
  const NDBINDEX *idx= (NDBINDEX *) m_index[active_index].index; 
6307 6308
  const NdbOperation* lastOp= m_active_trans->getLastDefinedOperation();
  NdbIndexScanOperation* scanOp= 0;
6309 6310
  for (; multi_range_curr<multi_range_end && curr+reclength <= end_of_buffer; 
       multi_range_curr++)
6311
  {
6312 6313 6314 6315 6316 6317
    switch (index_type){
    case PRIMARY_KEY_ORDERED_INDEX:
      if (!(multi_range_curr->start_key.length == key_info->key_length &&
            multi_range_curr->start_key.flag == HA_READ_KEY_EXACT))
      goto range;
      /* fall through */
6318
    case PRIMARY_KEY_INDEX:
6319
      multi_range_curr->range_flag |= UNIQUE_RANGE;
6320
      if ((op= m_active_trans->getNdbOperation(tab)) && 
6321 6322 6323
          !op->readTuple(lm) && 
          !set_primary_key(op, multi_range_curr->start_key.key) &&
          !define_read_attrs(curr, op) &&
6324
          (op->setAbortOption(AO_IgnoreError), TRUE))
6325
        curr += reclength;
6326
      else
6327
        ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
6328
      break;
6329 6330 6331 6332 6333 6334 6335
    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 */
6336
    case UNIQUE_INDEX:
6337
      multi_range_curr->range_flag |= UNIQUE_RANGE;
6338
      if ((op= m_active_trans->getNdbIndexOperation(unique_idx, tab)) && 
6339 6340 6341 6342 6343
	  !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;
6344
      else
6345
	ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
6346
      break;
6347 6348
    case ORDERED_INDEX:
    {
6349
  range:
6350
      multi_range_curr->range_flag &= ~(uint)UNIQUE_RANGE;
6351 6352
      if (scanOp == 0)
      {
6353 6354 6355 6356 6357 6358
        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));
6359
          if (scanOp->reset_bounds(m_force_send))
6360 6361 6362 6363 6364
            DBUG_RETURN(ndb_err(m_active_trans));
          
          end_of_buffer -= reclength;
        }
        else if ((scanOp= m_active_trans->getNdbIndexScanOperation(idx, tab)) 
6365
                 &&!scanOp->readTuples(lm, 0, parallelism, sorted, 
6366
				       FALSE, TRUE, need_pk, TRUE)
6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377
                 &&!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());
        }
6378
      }
6379

6380
      const key_range *keys[2]= { &multi_range_curr->start_key, 
6381
                                  &multi_range_curr->end_key };
6382
      if ((res= set_bounds(scanOp, keys, multi_range_curr-ranges)))
6383
        DBUG_RETURN(res);
6384
      break;
6385
    }
6386
    case UNDEFINED_INDEX:
mskold@mysql.com's avatar
mskold@mysql.com committed
6387 6388 6389 6390
      DBUG_ASSERT(FALSE);
      DBUG_RETURN(1);
      break;
    }
6391 6392
  }
  
6393
  if (multi_range_curr != multi_range_end)
6394
  {
6395 6396 6397 6398 6399 6400
    /**
     * 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
     */
6401
    buffer->end_of_used_area= (byte*)buffer->buffer_end;
6402 6403 6404 6405 6406 6407 6408 6409 6410 6411 6412
  }
  else
  {
    buffer->end_of_used_area= curr;
  }
  
  /**
   * Set first operation in multi range
   */
  m_current_multi_operation= 
    lastOp ? lastOp->next() : m_active_trans->getFirstDefinedOperation();
6413
  if (!(res= execute_no_commit_ie(this, m_active_trans, true)))
6414
  {
6415 6416
    m_multi_range_defined= multi_range_curr;
    multi_range_curr= ranges;
6417 6418
    m_multi_range_result_ptr= (byte*)buffer->buffer;
    DBUG_RETURN(read_multi_range_next(found_range_p));
6419 6420 6421 6422
  }
  ERR_RETURN(m_active_trans->getNdbError());
}

6423 6424 6425 6426 6427 6428
#if 0
#define DBUG_MULTI_RANGE(x) printf("read_multi_range_next: case %d\n", x);
#else
#define DBUG_MULTI_RANGE(x)
#endif

6429
int
6430
ha_ndbcluster::read_multi_range_next(KEY_MULTI_RANGE ** multi_range_found_p)
6431 6432
{
  DBUG_ENTER("ha_ndbcluster::read_multi_range_next");
6433
  if (m_disable_multi_read)
6434
  {
6435
    DBUG_RETURN(handler::read_multi_range_next(multi_range_found_p));
6436
  }
6437
  
6438
  int res;
6439
  int range_no;
joreland@mysql.com's avatar
merge  
joreland@mysql.com committed
6440
  ulong reclength= table->s->reclength;
6441
  const NdbOperation* op= m_current_multi_operation;
6442
  for (;multi_range_curr < m_multi_range_defined; multi_range_curr++)
6443
  {
6444
    if (multi_range_curr->range_flag & UNIQUE_RANGE)
6445
    {
6446
      if (op->getNdbError().code == 0)
6447
        goto found_next;
6448 6449 6450
      
      op= m_active_trans->getNextCompletedOperation(op);
      m_multi_range_result_ptr += reclength;
6451
      continue;
6452
    } 
6453
    else if (m_multi_cursor && !multi_range_sorted)
6454
    {
6455 6456
      DBUG_MULTI_RANGE(1);
      if ((res= fetch_next(m_multi_cursor)) == 0)
6457
      {
6458 6459 6460
        DBUG_MULTI_RANGE(2);
        range_no= m_multi_cursor->get_range_no();
        goto found;
6461 6462 6463
      } 
      else
      {
6464
        goto close_scan;
6465 6466
      }
    }
6467
    else if (m_multi_cursor && multi_range_sorted)
6468
    {
6469 6470
      if (m_active_cursor && (res= fetch_next(m_multi_cursor)))
      {
6471 6472
        DBUG_MULTI_RANGE(3);
        goto close_scan;
6473
      }
6474
      
6475
      range_no= m_multi_cursor->get_range_no();
6476
      uint current_range_no= multi_range_curr - m_multi_ranges;
mskold@mysql.com's avatar
mskold@mysql.com committed
6477
      if ((uint) range_no == current_range_no)
6478
      {
6479
        DBUG_MULTI_RANGE(4);
6480
        // return current row
6481
        goto found;
6482
      }
6483
      else if (range_no > (int)current_range_no)
6484
      {
6485 6486 6487 6488
        DBUG_MULTI_RANGE(5);
        // wait with current row
        m_active_cursor= 0;
        continue;
6489 6490 6491
      }
      else 
      {
6492 6493 6494
        DBUG_MULTI_RANGE(6);
        // First fetch from cursor
        DBUG_ASSERT(range_no == -1);
6495
        if ((res= m_multi_cursor->nextResult(true)))
6496 6497 6498 6499 6500
        {
          goto close_scan;
        }
        multi_range_curr--; // Will be increased in for-loop
        continue;
6501
      }
6502
    }
6503
    else /** m_multi_cursor == 0 */
6504
    {
6505
      DBUG_MULTI_RANGE(7);
6506 6507 6508 6509
      /**
       * Corresponds to range 5 in example in read_multi_range_first
       */
      (void)1;
6510
      continue;
6511
    }
6512
    
6513
    DBUG_ASSERT(FALSE); // Should only get here via goto's
6514 6515 6516
close_scan:
    if (res == 1)
    {
6517
      m_multi_cursor->close(FALSE, TRUE);
6518
      m_active_cursor= m_multi_cursor= 0;
6519
      DBUG_MULTI_RANGE(8);
6520 6521 6522 6523 6524 6525 6526
      continue;
    } 
    else 
    {
      DBUG_RETURN(ndb_err(m_active_trans));
    }
  }
6527
  
6528
  if (multi_range_curr == multi_range_end)
6529 6530 6531
  {
    Thd_ndb *thd_ndb= get_thd_ndb(current_thd);
    thd_ndb->query_state&= NDB_QUERY_NORMAL;
6532
    DBUG_RETURN(HA_ERR_END_OF_FILE);
6533
  }
6534
  
6535 6536 6537 6538
  /**
   * Read remaining ranges
   */
  DBUG_RETURN(read_multi_range_first(multi_range_found_p, 
6539 6540 6541 6542
                                     multi_range_curr,
                                     multi_range_end - multi_range_curr, 
                                     multi_range_sorted,
                                     multi_range_buffer));
6543 6544
  
found:
6545 6546 6547
  /**
   * Found a record belonging to a scan
   */
6548
  m_active_cursor= m_multi_cursor;
6549
  * multi_range_found_p= m_multi_ranges + range_no;
6550 6551
  memcpy(table->record[0], m_multi_range_cursor_result_ptr, reclength);
  setup_recattr(m_active_cursor->getFirstRecAttr());
6552 6553 6554
  unpack_record(table->record[0]);
  table->status= 0;     
  DBUG_RETURN(0);
6555
  
6556
found_next:
6557 6558 6559 6560
  /**
   * Found a record belonging to a pk/index op,
   *   copy result and move to next to prepare for next call
   */
6561
  * multi_range_found_p= multi_range_curr;
6562
  memcpy(table->record[0], m_multi_range_result_ptr, reclength);
6563
  setup_recattr(op->getFirstRecAttr());
6564
  unpack_record(table->record[0]);
6565 6566
  table->status= 0;
  
6567
  multi_range_curr++;
6568
  m_current_multi_operation= m_active_trans->getNextCompletedOperation(op);
6569 6570
  m_multi_range_result_ptr += reclength;
  DBUG_RETURN(0);
6571 6572
}

6573 6574 6575 6576 6577 6578 6579 6580
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
6581
  end= table->field + table->s->fields;
6582 6583 6584 6585 6586 6587
  
  for (field= table->field; field < end; field++, value++)
  {
    if ((* value).ptr)
    {
      DBUG_ASSERT(curr != 0);
6588 6589 6590
      NdbValue* val= m_value + curr->getColumn()->getColumnNo();
      DBUG_ASSERT(val->ptr);
      val->rec= curr;
6591
      curr= curr->next();
6592 6593 6594
    }
  }
  
6595
  DBUG_RETURN(0);
6596 6597
}

mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6598 6599
char*
ha_ndbcluster::update_table_comment(
6600 6601
                                /* out: table comment + additional */
        const char*     comment)/* in:  table comment defined by user */
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6602 6603
{
  uint length= strlen(comment);
6604
  if (length > 64000 - 3)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619 6620 6621 6622 6623 6624 6625 6626 6627 6628 6629 6630
  {
    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;
  }

6631 6632 6633
  my_snprintf(str,fmt_len_plus_extra,fmt,comment,
              length > 0 ? " ":"",
              tab->getReplicaCount());
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6634 6635 6636 6637 6638
  return str;
}


// Utility thread main loop
6639
pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6640 6641
{
  THD *thd; /* needs to be first for thread_stack */
6642
  Ndb* ndb;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6643 6644 6645 6646 6647 6648 6649 6650
  struct timespec abstime;

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

  thd= new THD; /* note that contructor of THD uses DBUG_ */
  THD_CHECK_SENTRY(thd);
6651
  ndb= new Ndb(g_ndb_cluster_connection, "");
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6652 6653 6654 6655 6656

  pthread_detach_this_thread();
  ndb_util_thread= pthread_self();

  thd->thread_stack= (char*)&thd; /* remember where our stack is */
6657
  if (thd->store_globals() || (ndb->init() != 0))
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6658 6659 6660
  {
    thd->cleanup();
    delete thd;
6661
    delete ndb;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6662 6663 6664 6665
    DBUG_RETURN(NULL);
  }

  List<NDB_SHARE> util_open_tables;
6666
  set_timespec(abstime, 0);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6667 6668 6669 6670
  for (;;)
  {

    pthread_mutex_lock(&LOCK_ndb_util_thread);
monty@mysql.com's avatar
monty@mysql.com committed
6671 6672 6673
    pthread_cond_timedwait(&COND_ndb_util_thread,
                           &LOCK_ndb_util_thread,
                           &abstime);
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6674 6675 6676 6677 6678 6679 6680 6681 6682 6683
    pthread_mutex_unlock(&LOCK_ndb_util_thread);

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

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

    if (ndb_cache_check_time == 0)
    {
6684 6685
      /* 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
6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706
      continue;
    }

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

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

    /* Iterate through the  open files list */
    List_iterator_fast<NDB_SHARE> it(util_open_tables);
6707
    while ((share= it++))
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6708 6709 6710 6711 6712 6713 6714 6715 6716 6717
    {
      /* 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",
6718 6719
                 ("Fetching commit count for: %s",
                  share->table_name));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6720 6721

      /* Contact NDB to get commit count for table */
6722 6723 6724 6725 6726 6727 6728 6729
      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
6730
      if (ndb_get_table_statistics(NULL, false, ndb, tabname, &stat) == 0)
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6731
      {
6732
        char buff[22], buff2[22];
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6733
        DBUG_PRINT("ndb_util_thread",
6734 6735 6736
                   ("Table: %s  commit_count: %s  rows: %s",
                    share->table_name,
                    llstr(stat.commit_count, buff),
monty@mysql.com's avatar
monty@mysql.com committed
6737
                    llstr(stat.row_count, buff2)));
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6738 6739 6740 6741 6742 6743
      }
      else
      {
        DBUG_PRINT("ndb_util_thread",
                   ("Error: Could not get commit count for table %s",
                    share->table_name));
6744
        stat.commit_count= 0;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6745
      }
6746 6747 6748 6749 6750 6751

      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
6752 6753 6754 6755 6756 6757 6758
      /* Decrease the use count and possibly free share */
      free_share(share);
    }

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

6759 6760 6761 6762 6763 6764 6765 6766 6767
    /* 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;

6768
    if (msecs >= 1000){
6769 6770 6771 6772 6773 6774 6775 6776 6777 6778
      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
6779 6780 6781 6782
  }

  thd->cleanup();
  delete thd;
6783
  delete ndb;
mskold@mysql.com's avatar
Merge  
mskold@mysql.com committed
6784 6785 6786 6787 6788 6789
  DBUG_PRINT("exit", ("ndb_util_thread"));
  my_thread_end();
  pthread_exit(0);
  DBUG_RETURN(NULL);
}

6790 6791 6792
/*
  Condition pushdown
*/
6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809
/*
  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)
*/
6810 6811 6812 6813 6814
const 
COND* 
ha_ndbcluster::cond_push(const COND *cond) 
{ 
  DBUG_ENTER("cond_push");
6815 6816 6817
  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
6818
    ndb_cond->next= m_cond_stack;
6819 6820 6821 6822 6823 6824
  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
6825
    DBUG_RETURN(NULL);
6826 6827 6828 6829
  }
  else
  {
    cond_pop();
mskold@mysql.com's avatar
mskold@mysql.com committed
6830
  }
6831 6832 6833
  DBUG_RETURN(cond); 
}

6834 6835 6836
/*
  Pop the top condition from the condition stack of the handler instance.
*/
6837 6838 6839 6840 6841 6842 6843 6844 6845
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
6846
}
6847

6848 6849 6850
/*
  Clear the condition stack
*/
6851 6852 6853 6854 6855 6856 6857 6858 6859 6860
void
ha_ndbcluster::cond_clear()
{
  DBUG_ENTER("cond_clear");
  while (m_cond_stack)
    cond_pop();

  DBUG_VOID_RETURN;
}

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

bool
ha_ndbcluster::serialize_cond(const COND *cond, Ndb_cond_stack *ndb_cond)
{
  DBUG_ENTER("serialize_cond");
  Item *item= (Item *) cond;
7736
  Ndb_cond_traverse_context context(table, (void *)m_table, ndb_cond);
7737 7738 7739
  // Expect a logical expression
  context.expect(Item::FUNC_ITEM);
  context.expect(Item::COND_ITEM);
7740
  item->traverse_cond(&ndb_serialize_cond, (void *) &context, Item::PREFIX);
7741
  DBUG_PRINT("info", ("The pushed condition is %ssupported", (context.supported)?"":"not "));
7742

7743
  DBUG_RETURN(context.supported);
7744 7745
}

7746 7747
int
ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, 
7748 7749
                                           NdbScanFilter *filter,
                                           bool negated)
7750 7751
{
  DBUG_ENTER("build_scan_filter_predicate");  
7752 7753 7754
  switch (cond->ndb_item->type) {
  case NDB_FUNCTION:
  {
7755 7756 7757
    if (!cond->next)
      break;
    Ndb_item *a= cond->next->ndb_item;
7758
    Ndb_item *b, *field, *value= NULL;
7759 7760
    LINT_INIT(field);

7761 7762
    switch (cond->ndb_item->argument_count()) {
    case 1:
7763 7764 7765
      field= 
        (a->type == NDB_FIELD)? a : NULL;
      break;
7766
    case 2:
7767
      if (!cond->next->next)
7768
        break;
7769 7770
      b= cond->next->next->ndb_item;
      value= 
7771 7772 7773
        (a->type == NDB_VALUE)? a
        : (b->type == NDB_VALUE)? b
        : NULL;
7774
      field= 
7775 7776 7777
        (a->type == NDB_FIELD)? a
        : (b->type == NDB_FIELD)? b
        : NULL;
7778
      break;
7779
    default:
7780 7781
      break;
    }
7782 7783 7784
    switch ((negated) ? 
            Ndb_item::negate(cond->ndb_item->qualification.function_type)
            : cond->ndb_item->qualification.function_type) {
7785
    case NDB_EQ_FUNC:
7786
    {
7787
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
7788 7789
      // Save value in right format for the field type
      value->save_in_field(field);
7790
      DBUG_PRINT("info", ("Generating EQ filter"));
7791
      if (filter->cmp(NdbScanFilter::COND_EQ, 
7792 7793 7794 7795
                      field->get_field_no(),
                      field->get_val(),
                      field->pack_length()) == -1)
        DBUG_RETURN(1);
7796 7797
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7798
    }
7799
    case NDB_NE_FUNC:
7800
    {
7801
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
7802 7803
      // Save value in right format for the field type
      value->save_in_field(field);
7804
      DBUG_PRINT("info", ("Generating NE filter"));
7805
      if (filter->cmp(NdbScanFilter::COND_NE, 
7806 7807 7808 7809
                      field->get_field_no(),
                      field->get_val(),
                      field->pack_length()) == -1)
        DBUG_RETURN(1);
7810 7811
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7812
    }
7813
    case NDB_LT_FUNC:
7814
    {
7815
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
7816 7817
      // Save value in right format for the field type
      value->save_in_field(field);
7818
      if (a == field)
7819
      {
7820 7821 7822 7823 7824 7825
        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);
7826
      }
7827
      else
7828
      {
7829 7830 7831 7832 7833 7834
        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);
7835
      }
7836 7837
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7838
    }
7839
    case NDB_LE_FUNC:
7840
    {
7841
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
7842 7843
      // Save value in right format for the field type
      value->save_in_field(field);
7844
      if (a == field)
7845
      {
7846 7847 7848 7849 7850 7851
        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);       
7852
      }
7853
      else
7854
      {
7855 7856 7857 7858 7859 7860
        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);
7861
      }
7862 7863
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7864
    }
7865
    case NDB_GE_FUNC:
7866
    {
7867
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
7868 7869
      // Save value in right format for the field type
      value->save_in_field(field);
7870
      if (a == field)
7871
      {
7872 7873 7874 7875 7876 7877
        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);
7878
      }
7879
      else
7880
      {
7881 7882 7883 7884 7885 7886
        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);
7887
      }
7888 7889
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7890
    }
7891
    case NDB_GT_FUNC:
7892
    {
7893
      if (!value || !field) break;
mskold@mysql.com's avatar
mskold@mysql.com committed
7894 7895
      // Save value in right format for the field type
      value->save_in_field(field);
7896
      if (a == field)
7897
      {
7898 7899 7900 7901 7902 7903
        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);
7904
      }
7905
      else
7906
      {
7907 7908 7909 7910 7911 7912
        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);
7913
      }
7914 7915
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7916
    }
7917
    case NDB_LIKE_FUNC:
7918
    {
7919
      if (!value || !field) break;
7920 7921 7922
      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
7923 7924 7925
      // Save value in right format for the field type
      value->save_in_field(field);
      DBUG_PRINT("info", ("Generating LIKE filter: like(%d,%s,%d)", 
7926 7927 7928 7929
                          field->get_field_no(), value->get_val(), 
                          value->pack_length()));
      if (filter->cmp(NdbScanFilter::COND_LIKE, 
                      field->get_field_no(),
7930 7931
                      value->get_val(),
                      value->pack_length()) == -1)
7932
        DBUG_RETURN(1);
7933 7934
      cond= cond->next->next->next;
      DBUG_RETURN(0);
7935
    }
7936 7937 7938 7939 7940 7941 7942 7943 7944 7945 7946 7947 7948 7949 7950 7951 7952 7953 7954 7955
    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:
7956 7957 7958 7959 7960
      if (!field)
        break;
      DBUG_PRINT("info", ("Generating ISNULL filter"));
      if (filter->isnull(field->get_field_no()) == -1)
        DBUG_RETURN(1);
7961 7962
      cond= cond->next->next;
      DBUG_RETURN(0);
7963
    case NDB_ISNOTNULL_FUNC:
7964
    {
7965 7966 7967 7968 7969
      if (!field)
        break;
      DBUG_PRINT("info", ("Generating ISNOTNULL filter"));
      if (filter->isnotnull(field->get_field_no()) == -1)
        DBUG_RETURN(1);         
7970 7971
      cond= cond->next->next;
      DBUG_RETURN(0);
7972 7973 7974 7975 7976 7977 7978 7979 7980 7981
    }
    default:
      break;
    }
    break;
  }
  default:
    break;
  }
  DBUG_PRINT("info", ("Found illegal condition"));
7982
  DBUG_RETURN(1);
7983 7984
}

7985
int
7986
ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter)
7987
{
7988
  uint level=0;
7989
  bool negated= FALSE;
7990
  DBUG_ENTER("build_scan_filter_group");
7991

7992 7993
  do
  {
7994 7995 7996 7997 7998 7999
    if (!cond)
      DBUG_RETURN(1);
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
    {
      switch (cond->ndb_item->qualification.function_type) {
8000
      case NDB_COND_AND_FUNC:
8001
      {
8002 8003 8004 8005 8006
        level++;
        DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NAND":"AND",
                            level));
        if ((negated) ? filter->begin(NdbScanFilter::NAND)
            : filter->begin(NdbScanFilter::AND) == -1)
8007
          DBUG_RETURN(1);
8008
        negated= FALSE;
8009 8010 8011
        cond= cond->next;
        break;
      }
8012
      case NDB_COND_OR_FUNC:
8013
      {
8014 8015 8016 8017 8018 8019
        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);
8020
        negated= FALSE;
8021 8022 8023
        cond= cond->next;
        break;
      }
8024
      case NDB_NOT_FUNC:
8025
      {
8026
        DBUG_PRINT("info", ("Generating negated query"));
8027
        cond= cond->next;
8028
        negated= TRUE;
8029 8030 8031 8032
        break;
      }
      default:
        if (build_scan_filter_predicate(cond, filter, negated))
8033
          DBUG_RETURN(1);
8034
        negated= FALSE;
8035 8036 8037
        break;
      }
      break;
8038 8039
    }
    case NDB_END_COND:
8040 8041
      DBUG_PRINT("info", ("End of group %u", level));
      level--;
8042 8043
      if (cond) cond= cond->next;
      if (filter->end() == -1)
8044
        DBUG_RETURN(1);
8045 8046 8047
      if (!negated)
        break;
      // else fall through (NOT END is an illegal condition)
8048 8049
    default:
    {
8050
      DBUG_PRINT("info", ("Illegal scan filter"));
8051
    }
8052
    }
8053
  }  while (level > 0 || negated);
8054
  
8055
  DBUG_RETURN(0);
8056 8057
}

8058 8059
int
ha_ndbcluster::build_scan_filter(Ndb_cond * &cond, NdbScanFilter *filter)
8060 8061 8062 8063
{
  bool simple_cond= TRUE;
  DBUG_ENTER("build_scan_filter");  

8064 8065 8066
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
      switch (cond->ndb_item->qualification.function_type) {
8067 8068
      case NDB_COND_AND_FUNC:
      case NDB_COND_OR_FUNC:
8069 8070 8071 8072 8073 8074 8075 8076 8077
        simple_cond= FALSE;
        break;
      default:
        break;
      }
      break;
    default:
      break;
    }
8078 8079 8080 8081 8082 8083
  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);
8084

8085
  DBUG_RETURN(0);
8086 8087
}

8088
int
8089
ha_ndbcluster::generate_scan_filter(Ndb_cond_stack *ndb_cond_stack,
8090
                                    NdbScanOperation *op)
8091 8092
{
  DBUG_ENTER("generate_scan_filter");
8093

8094 8095 8096
  if (ndb_cond_stack)
  {
    NdbScanFilter filter(op);
8097 8098
    
    DBUG_RETURN(generate_scan_filter_from_cond(ndb_cond_stack, filter));
8099 8100 8101 8102 8103 8104
  }
  else
  {  
    DBUG_PRINT("info", ("Empty stack"));
  }

8105
  DBUG_RETURN(0);
8106 8107
}

8108 8109 8110 8111 8112 8113 8114 8115 8116 8117 8118 8119 8120 8121 8122 8123 8124 8125 8126 8127 8128 8129 8130 8131 8132 8133 8134 8135 8136 8137 8138 8139 8140 8141 8142 8143 8144 8145 8146 8147 8148 8149 8150 8151 8152 8153 8154 8155 8156 8157 8158 8159 8160 8161 8162 8163 8164 8165 8166 8167 8168 8169 8170 8171 8172 8173 8174 8175 8176 8177 8178 8179 8180 8181 8182 8183 8184 8185 8186 8187 8188 8189
int
ha_ndbcluster::generate_scan_filter_from_cond(Ndb_cond_stack *ndb_cond_stack,
					      NdbScanFilter& filter)
{
  DBUG_ENTER("generate_scan_filter_from_cond");
  bool multiple_cond= FALSE;
  
  DBUG_PRINT("info", ("Generating scan filter"));
  // 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);
}

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");
  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;
    char buf[256];
    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);
}

8190 8191 8192 8193 8194 8195 8196 8197 8198 8199
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,
8200 8201
	       "Cannot call SHOW NDBCLUSTER STATUS because skip-ndbcluster is "
               "defined",
8202 8203 8204 8205 8206 8207 8208 8209 8210 8211
	       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));

8212 8213
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
8214 8215
    DBUG_RETURN(TRUE);
  
8216
  if (get_thd_ndb(thd) && get_thd_ndb(thd)->ndb)
8217
  {
8218
    Ndb* ndb= (get_thd_ndb(thd))->ndb;
8219 8220
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
8221 8222 8223 8224 8225 8226 8227 8228 8229 8230 8231 8232 8233 8234 8235 8236 8237
    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);
}

8238
#endif /* HAVE_NDBCLUSTER_DB */