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

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

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

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

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

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

#include "mysql_priv.h"

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

35 36 37
// options from from mysqld.cc
extern my_bool opt_ndb_optimized_node_selection;
extern const char *opt_ndbcluster_connectstring;
unknown's avatar
unknown committed
38
extern ulong opt_ndb_cache_check_time;
39

unknown's avatar
unknown committed
40
// Default value for parallelism
41
static const int parallelism= 0;
unknown's avatar
unknown committed
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 = {
unknown's avatar
unknown 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
unknown's avatar
unknown committed
78

79 80
#define NDB_INVALID_SCHEMA_OBJECT 241

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

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

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

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

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

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

// Table lock handling
static HASH ndbcluster_open_tables;

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

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

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

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

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

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

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

144 145 146 147 148 149
/* 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}
};

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

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

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

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

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

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

205 206
  { 284, HA_ERR_TABLE_DEF_CHANGED, 0 },

207 208 209
  { 0, 1, 0 },

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


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


unknown's avatar
unknown committed
230 231

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

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

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

inline
273 274
int execute_no_commit_ie(ha_ndbcluster *h, NdbTransaction *trans,
			 bool force_release)
unknown's avatar
unknown committed
275
{
unknown's avatar
unknown committed
276
#ifdef NOT_USED
277
  int m_batch_execute= 0;
unknown's avatar
unknown committed
278
  if (m_batch_execute)
unknown's avatar
unknown committed
279
    return 0;
unknown's avatar
unknown committed
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);
unknown's avatar
unknown committed
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
unknown's avatar
unknown committed
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;
unknown's avatar
unknown committed
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;
};

unknown's avatar
unknown 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++)
unknown's avatar
unknown 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);
unknown's avatar
unknown 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;
}

unknown's avatar
unknown committed
447 448
/*
  Take care of the error that occured in NDB
449

unknown's avatar
unknown committed
450
  RETURN
451
    0   No error
unknown's avatar
unknown committed
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);
unknown's avatar
unknown committed
477
  table->s->version=0L;			/* Free when thread is ready */
478
  /* Invalidate indexes */
unknown's avatar
unknown committed
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;

unknown's avatar
unknown committed
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);
unknown's avatar
unknown committed
492
      break;
unknown's avatar
unknown committed
493
    case UNIQUE_ORDERED_INDEX:
494 495 496 497
      if (global)
        dict->invalidateIndex(index->getName(), m_tabname);
      else
        dict->removeCachedIndex(index->getName(), m_tabname);
unknown's avatar
unknown committed
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;
unknown's avatar
unknown committed
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)
unknown's avatar
unknown committed
513
{
514
  int res;
515
  NdbError err= trans->getNdbError();
unknown's avatar
unknown committed
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
    }
unknown's avatar
unknown committed
548
    break;
549
  }
unknown's avatar
unknown committed
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
unknown's avatar
unknown committed
568 569
    {
      /* We are batching inserts, offending key is not available */
570
      m_dupkey= (uint) -1;
unknown's avatar
unknown committed
571
    }
572
  }
573
  DBUG_RETURN(res);
unknown's avatar
unknown committed
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)
unknown's avatar
unknown committed
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
}


unknown's avatar
unknown committed
600
#ifndef DBUG_OFF
unknown's avatar
unknown committed
601 602 603 604
/*
  Check if type is supported by NDB.
*/

unknown's avatar
unknown committed
605
static bool ndb_supported_type(enum_field_types type)
unknown's avatar
unknown committed
606 607
{
  switch (type) {
unknown's avatar
unknown 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:
unknown's avatar
unknown 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:
unknown's avatar
unknown committed
625
  case MYSQL_TYPE_VARCHAR:
unknown's avatar
unknown 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:
unknown's avatar
unknown committed
634
    return TRUE;
unknown's avatar
unknown committed
635
  case MYSQL_TYPE_NULL:   
unknown's avatar
unknown committed
636
    break;
unknown's avatar
unknown committed
637
  }
unknown's avatar
unknown committed
638
  return FALSE;
unknown's avatar
unknown committed
639
}
unknown's avatar
unknown committed
640
#endif /* !DBUG_OFF */
unknown's avatar
unknown committed
641 642


unknown's avatar
unknown committed
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)
unknown's avatar
unknown committed
649 650 651
{
  DBUG_ENTER("set_hidden_key");
  DBUG_RETURN(ndb_op->equal(fieldnr, (char*)field_ptr,
652
                            NDB_HIDDEN_PRIMARY_KEY_LENGTH) != 0);
unknown's avatar
unknown committed
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);
  
unknown's avatar
unknown 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);
unknown's avatar
unknown committed
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)
unknown's avatar
unknown committed
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);
unknown's avatar
unknown committed
691

unknown's avatar
unknown committed
692
  DBUG_ASSERT(ndb_supported_type(field->type()));
unknown's avatar
unknown committed
693
  {
694
    // ndb currently does not support size 0
unknown's avatar
unknown committed
695
    uint32 empty_field;
696 697
    if (pack_len == 0)
    {
unknown's avatar
unknown committed
698 699 700
      pack_len= sizeof(empty_field);
      field_ptr= (byte *)&empty_field;
      if (field->is_null())
701
        empty_field= 0;
unknown's avatar
unknown committed
702
      else
703
        empty_field= 1;
704
    }
unknown's avatar
unknown 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
      }
unknown's avatar
unknown committed
738 739
    }
    // Blob type
740
    NdbBlob *ndb_blob= ndb_op->getBlobHandle(fieldnr);
unknown's avatar
unknown 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
      }
unknown's avatar
unknown committed
758

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

763
      if (set_blob_value)
764
        *set_blob_value= TRUE;
unknown's avatar
unknown committed
765 766 767 768
      // No callback needed to write value
      DBUG_RETURN(ndb_blob->setValue(blob_ptr, blob_len) != 0);
    }
    DBUG_RETURN(1);
unknown's avatar
unknown committed
769
  }
unknown's avatar
unknown 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
*/

unknown's avatar
unknown committed
785
NdbBlob::ActiveHook g_get_ndb_blobs_value;
unknown's avatar
unknown committed
786

unknown's avatar
unknown committed
787
int g_get_ndb_blobs_value(NdbBlob *ndb_blob, void *arg)
unknown's avatar
unknown committed
788
{
unknown's avatar
unknown committed
789
  DBUG_ENTER("g_get_ndb_blobs_value");
unknown's avatar
unknown 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));
unknown's avatar
unknown committed
794 795
}

796 797
int ha_ndbcluster::get_ndb_blobs_value(NdbBlob *last_ndb_blob,
				       my_ptrdiff_t ptrdiff)
unknown's avatar
unknown 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++)
unknown's avatar
unknown 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;
unknown's avatar
unknown committed
824 825
          uint32 len= 0xffffffff;  // Max uint32
          DBUG_PRINT("value", ("read blob ptr=%x len=%u",
unknown's avatar
unknown committed
826
                               (UintPtr)buf, (uint)blob_len));
unknown's avatar
unknown 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;
unknown's avatar
unknown committed
832
          field_blob->set_ptr(len, buf);
833
          field_blob->ptr-= ptrdiff;
unknown's avatar
unknown committed
834 835 836 837
        }
        offset+= blob_size;
      }
    }
838
    if (loop == 0 && offset > m_blobs_buffer_size)
unknown's avatar
unknown committed
839
    {
840 841
      my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
      m_blobs_buffer_size= 0;
unknown's avatar
unknown 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)
unknown's avatar
unknown committed
845
        DBUG_RETURN(-1);
846
      m_blobs_buffer_size= offset;
unknown's avatar
unknown committed
847
    }
unknown's avatar
unknown committed
848
  }
unknown's avatar
unknown committed
849
  DBUG_RETURN(0);
unknown's avatar
unknown committed
850 851 852 853 854
}


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

unknown's avatar
unknown committed
859
int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field,
unknown's avatar
unknown committed
860
                                 uint fieldnr, byte* buf)
unknown's avatar
unknown committed
861 862
{
  DBUG_ENTER("get_ndb_value");
unknown's avatar
unknown committed
863 864 865 866 867
  DBUG_PRINT("enter", ("fieldnr: %d flags: %o", fieldnr,
                       (int)(field != NULL ? field->flags : 0)));

  if (field != NULL)
  {
unknown's avatar
unknown committed
868 869
      DBUG_ASSERT(buf);
      DBUG_ASSERT(ndb_supported_type(field->type()));
unknown's avatar
unknown 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);
        }
unknown's avatar
unknown 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];
unknown's avatar
unknown committed
897
        void *arg= (void *)this;
unknown's avatar
unknown committed
898
        DBUG_RETURN(ndb_blob->setActiveHook(g_get_ndb_blobs_value, arg) != 0);
unknown's avatar
unknown 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);
unknown's avatar
unknown 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)
unknown's avatar
unknown committed
915
    return FALSE;
unknown's avatar
unknown committed
916
  if (all_fields)
unknown's avatar
unknown committed
917
    return TRUE;
unknown's avatar
unknown committed
918
  {
919
    uint no_fields= table->s->fields;
unknown's avatar
unknown committed
920
    int i;
921
    THD *thd= current_thd;
unknown's avatar
unknown 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)
      {
unknown's avatar
unknown committed
928
        return TRUE;
unknown's avatar
unknown committed
929 930 931
      }
    }
  }
unknown's avatar
unknown committed
932
  return FALSE;
unknown's avatar
unknown committed
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();
unknown's avatar
unknown committed
948 949
  const NDBTAB *tab;
  int error;
unknown's avatar
unknown committed
950
  bool invalidating_ndb_table= FALSE;
951

unknown's avatar
unknown committed
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);
    }
unknown's avatar
unknown committed
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
    {
unknown's avatar
unknown committed
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);

unknown's avatar
unknown committed
1010 1011
  if (error)
    DBUG_RETURN(error);
unknown's avatar
unknown committed
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
}
unknown's avatar
unknown committed
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);
unknown's avatar
unknown 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);
}
unknown's avatar
unknown committed
1053

1054 1055


1056
int ha_ndbcluster::build_index_list(Ndb *ndb, TABLE *tab, enum ILBP phase)
1057
{
1058
  uint i;
unknown's avatar
unknown committed
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";
unknown's avatar
unknown committed
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;
unknown's avatar
unknown committed
1070
  // Save information about all known indexes
1071
  for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
1072
  {
unknown's avatar
unknown committed
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
    }
unknown's avatar
unknown committed
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
        
unknown's avatar
unknown committed
1089
      case PRIMARY_KEY_INDEX:
1090 1091
        // Do nothing, already created
        break;
unknown's avatar
unknown committed
1092
      case PRIMARY_KEY_ORDERED_INDEX:
1093 1094
        error= create_ordered_index(index_name, key_info);
        break;
unknown's avatar
unknown committed
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;
unknown's avatar
unknown committed
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;
unknown's avatar
unknown committed
1109
      case ORDERED_INDEX:
1110 1111
        error= create_ordered_index(index_name, key_info);
        break;
unknown's avatar
unknown committed
1112
      default:
1113 1114
        DBUG_ASSERT(FALSE);
        break;
unknown's avatar
unknown committed
1115 1116 1117
      }
      if (error)
      {
1118 1119 1120
        DBUG_PRINT("error", ("Failed to create index %u", i));
        drop_table();
        break;
unknown's avatar
unknown committed
1121 1122 1123
      }
    }
    // Add handles to index objects
1124
    if (idx_type != PRIMARY_KEY_INDEX && idx_type != UNIQUE_INDEX)
1125
    {
1126
      DBUG_PRINT("info", ("Get handle to index %s", index_name));
unknown's avatar
unknown committed
1127
      const NDBINDEX *index= dict->getIndex(index_name, m_tabname);
1128
      if (!index) DBUG_RETURN(1);
unknown's avatar
unknown committed
1129
      m_index[i].index= (void *) index;
1130
    }
1131
    if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX)
1132
    {
1133 1134
      DBUG_PRINT("info", ("Get handle to unique_index %s", unique_index_name));
      const NDBINDEX *index= dict->getIndex(unique_index_name, m_tabname);
1135
      if (!index) DBUG_RETURN(1);
unknown's avatar
unknown committed
1136
      m_index[i].unique_index= (void *) index;
1137 1138
      error= fix_unique_index_attr_order(m_index[i], index, key_info);
    }
1139 1140 1141 1142 1143
    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;
1144
  }
unknown's avatar
unknown committed
1145 1146
  
  DBUG_RETURN(error);
1147 1148
}

1149

unknown's avatar
unknown committed
1150 1151 1152 1153
/*
  Decode the type of an index from information 
  provided in table object
*/
1154
NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_table(uint inx) const
unknown's avatar
unknown committed
1155
{
1156
  bool is_hash_index=  (table->key_info[inx].algorithm == HA_KEY_ALG_HASH);
1157
  if (inx == table->s->primary_key)
1158
    return is_hash_index ? PRIMARY_KEY_INDEX : PRIMARY_KEY_ORDERED_INDEX;
1159 1160 1161 1162

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

1165
bool ha_ndbcluster::check_index_fields_not_null(uint inx)
1166 1167 1168 1169
{
  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;
1170
  DBUG_ENTER("ha_ndbcluster::check_index_fields_not_null");
1171 1172 1173 1174 1175
  
  for (; key_part != end; key_part++) 
    {
      Field* field= key_part->field;
      if (field->maybe_null())
1176
        DBUG_RETURN(true);
1177 1178
    }
  
1179
  DBUG_RETURN(false);
1180
}
unknown's avatar
unknown committed
1181 1182 1183

void ha_ndbcluster::release_metadata()
{
1184
  uint i;
1185

unknown's avatar
unknown committed
1186 1187 1188 1189
  DBUG_ENTER("release_metadata");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));

  m_table= NULL;
unknown's avatar
unknown committed
1190
  m_table_info= NULL;
unknown's avatar
unknown committed
1191

1192
  // Release index list 
1193 1194
  for (i= 0; i < MAX_KEY; i++)
  {
1195 1196
    m_index[i].unique_index= NULL;      
    m_index[i].index= NULL;      
1197 1198 1199 1200 1201
    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;
    }
1202 1203
  }

unknown's avatar
unknown committed
1204 1205 1206
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
1207
int ha_ndbcluster::get_ndb_lock_type(enum thr_lock_type type)
1208
{
1209
  DBUG_ENTER("ha_ndbcluster::get_ndb_lock_type");
1210
  if (type >= TL_WRITE_ALLOW_WRITE)
1211 1212 1213 1214 1215 1216 1217 1218 1219 1220
  {
    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);
  }
unknown's avatar
unknown committed
1221
  else
1222 1223 1224 1225
  {
    DBUG_PRINT("info", ("Using committed read"));
    DBUG_RETURN(NdbOperation::LM_CommittedRead);
  }
1226 1227
}

unknown's avatar
unknown committed
1228 1229 1230 1231 1232 1233
static const ulong index_type_flags[]=
{
  /* UNDEFINED_INDEX */
  0,                         

  /* PRIMARY_KEY_INDEX */
1234
  HA_ONLY_WHOLE_INDEX, 
1235 1236

  /* PRIMARY_KEY_ORDERED_INDEX */
1237
  /* 
unknown's avatar
unknown committed
1238
     Enable HA_KEYREAD_ONLY when "sorted" indexes are supported, 
1239 1240 1241
     thus ORDERD BY clauses can be optimized by reading directly 
     through the index.
  */
unknown's avatar
unknown committed
1242
  // HA_KEYREAD_ONLY | 
unknown's avatar
unknown committed
1243
  HA_READ_NEXT |
1244
  HA_READ_PREV |
unknown's avatar
unknown committed
1245 1246
  HA_READ_RANGE |
  HA_READ_ORDER,
unknown's avatar
unknown committed
1247 1248

  /* UNIQUE_INDEX */
1249
  HA_ONLY_WHOLE_INDEX,
unknown's avatar
unknown committed
1250

1251
  /* UNIQUE_ORDERED_INDEX */
unknown's avatar
unknown committed
1252
  HA_READ_NEXT |
1253
  HA_READ_PREV |
unknown's avatar
unknown committed
1254 1255
  HA_READ_RANGE |
  HA_READ_ORDER,
1256

unknown's avatar
unknown committed
1257
  /* ORDERED_INDEX */
unknown's avatar
unknown committed
1258
  HA_READ_NEXT |
1259
  HA_READ_PREV |
unknown's avatar
unknown committed
1260 1261
  HA_READ_RANGE |
  HA_READ_ORDER
unknown's avatar
unknown committed
1262 1263 1264 1265 1266 1267 1268
};

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);
1269
  return m_index[idx_no].type;
unknown's avatar
unknown committed
1270 1271
}

1272 1273 1274 1275 1276 1277
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;
}

unknown's avatar
unknown committed
1278 1279 1280 1281 1282 1283 1284 1285

/*
  Get the flags for an index

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

1286 1287
inline ulong ha_ndbcluster::index_flags(uint idx_no, uint part,
                                        bool all_parts) const 
unknown's avatar
unknown committed
1288
{ 
1289
  DBUG_ENTER("ha_ndbcluster::index_flags");
1290
  DBUG_PRINT("info", ("idx_no: %d", idx_no));
unknown's avatar
unknown committed
1291
  DBUG_ASSERT(get_index_type_from_table(idx_no) < index_flags_size);
1292 1293
  DBUG_RETURN(index_type_flags[get_index_type_from_table(idx_no)] | 
              HA_KEY_SCAN_NOT_ROR);
unknown's avatar
unknown committed
1294 1295
}

unknown's avatar
unknown committed
1296 1297
static void shrink_varchar(Field* field, const byte* & ptr, char* buf)
{
1298
  if (field->type() == MYSQL_TYPE_VARCHAR && ptr != NULL) {
unknown's avatar
unknown committed
1299
    Field_varstring* f= (Field_varstring*)field;
unknown's avatar
unknown committed
1300
    if (f->length_bytes == 1) {
unknown's avatar
unknown committed
1301 1302 1303 1304 1305
      uint pack_len= field->pack_length();
      DBUG_ASSERT(1 <= pack_len && pack_len <= 256);
      if (ptr[1] == 0) {
        buf[0]= ptr[0];
      } else {
unknown's avatar
unknown committed
1306
        DBUG_ASSERT(FALSE);
unknown's avatar
unknown committed
1307 1308 1309 1310 1311 1312 1313
        buf[0]= 255;
      }
      memmove(buf + 1, ptr + 2, pack_len - 1);
      ptr= buf;
    }
  }
}
unknown's avatar
unknown committed
1314 1315 1316

int ha_ndbcluster::set_primary_key(NdbOperation *op, const byte *key)
{
1317
  KEY* key_info= table->key_info + table->s->primary_key;
unknown's avatar
unknown committed
1318 1319 1320 1321 1322 1323 1324
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
  DBUG_ENTER("set_primary_key");

  for (; key_part != end; key_part++) 
  {
    Field* field= key_part->field;
unknown's avatar
unknown committed
1325 1326 1327
    const byte* ptr= key;
    char buf[256];
    shrink_varchar(field, ptr, buf);
unknown's avatar
unknown committed
1328
    if (set_ndb_key(op, field, 
1329
                    key_part->fieldnr-1, ptr))
unknown's avatar
unknown committed
1330
      ERR_RETURN(op->getNdbError());
unknown's avatar
unknown committed
1331
    key += key_part->store_length;
unknown's avatar
unknown committed
1332 1333 1334 1335 1336
  }
  DBUG_RETURN(0);
}


1337
int ha_ndbcluster::set_primary_key_from_record(NdbOperation *op, const byte *record)
1338
{
1339
  KEY* key_info= table->key_info + table->s->primary_key;
1340 1341
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
1342
  DBUG_ENTER("set_primary_key_from_record");
1343 1344 1345 1346 1347

  for (; key_part != end; key_part++) 
  {
    Field* field= key_part->field;
    if (set_ndb_key(op, field, 
1348
		    key_part->fieldnr-1, record+key_part->offset))
1349 1350 1351 1352 1353
      ERR_RETURN(op->getNdbError());
  }
  DBUG_RETURN(0);
}

1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371
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);
}

1372 1373
int 
ha_ndbcluster::set_index_key(NdbOperation *op, 
1374 1375
                             const KEY *key_info, 
                             const byte * key_ptr)
1376
{
1377
  DBUG_ENTER("ha_ndbcluster::set_index_key");
1378 1379 1380 1381 1382 1383
  uint i;
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
  
  for (i= 0; key_part != end; key_part++, i++) 
  {
unknown's avatar
unknown committed
1384 1385 1386 1387
    Field* field= key_part->field;
    const byte* ptr= key_part->null_bit ? key_ptr + 1 : key_ptr;
    char buf[256];
    shrink_varchar(field, ptr, buf);
unknown's avatar
Merge  
unknown committed
1388
    if (set_ndb_key(op, field, m_index[active_index].unique_index_attrid_map[i], ptr))
1389 1390 1391 1392 1393
      ERR_RETURN(m_active_trans->getNdbError());
    key_ptr+= key_part->store_length;
  }
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1394

unknown's avatar
unknown committed
1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407
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) ||
1408 1409
        ((field->flags & PRI_KEY_FLAG)) || 
        m_retrieve_all_fields)
unknown's avatar
unknown committed
1410 1411
    {      
      if (get_ndb_value(op, field, i, buf))
1412
        ERR_RETURN(op->getNdbError());
unknown's avatar
unknown committed
1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435
    } 
    else 
    {
      m_value[i].ptr= NULL;
    }
  }
    
  if (table->s->primary_key == MAX_KEY) 
  {
    DBUG_PRINT("info", ("Getting hidden key"));
    // Scanning table with no primary key
    int hidden_no= table->s->fields;      
#ifndef DBUG_OFF
    const NDBTAB *tab= (const NDBTAB *) m_table;    
    if (!tab->getColumn(hidden_no))
      DBUG_RETURN(1);
#endif
    if (get_ndb_value(op, NULL, hidden_no, NULL))
      ERR_RETURN(op->getNdbError());
  }
  DBUG_RETURN(0);
} 

unknown's avatar
unknown committed
1436 1437 1438 1439
/*
  Read one record from NDB using primary key
*/

1440
int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf) 
unknown's avatar
unknown committed
1441
{
1442
  uint no_fields= table->s->fields;
unknown's avatar
unknown committed
1443 1444
  NdbConnection *trans= m_active_trans;
  NdbOperation *op;
1445

1446 1447 1448 1449
  int res;
  DBUG_ENTER("pk_read");
  DBUG_PRINT("enter", ("key_len: %u", key_len));
  DBUG_DUMP("key", (char*)key, key_len);
unknown's avatar
unknown committed
1450

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

1490 1491 1492 1493 1494 1495
/*
  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)
{
1496
  uint no_fields= table->s->fields, i;
1497
  NdbTransaction *trans= m_active_trans;
1498 1499 1500 1501
  NdbOperation *op;
  THD *thd= current_thd;
  DBUG_ENTER("complemented_pk_read");

1502
  if (m_retrieve_all_fields)
1503 1504 1505
    // We have allready retrieved all fields, nothing to complement
    DBUG_RETURN(0);

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

  /**
   * restore m_value
   */
  for (i= 0; i < no_fields; i++) 
  {
    Field *field= table->field[i];
    if (!((field->flags & PRI_KEY_FLAG) ||
1542
          (thd->query_id == field->query_id)))
1543 1544 1545 1546 1547
    {
      m_value[i].ptr= NULL;
    }
  }
  
1548 1549 1550
  DBUG_RETURN(0);
}

1551
/*
1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610
 * 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
1611 1612
*/

1613
int ha_ndbcluster::peek_indexed_rows(const byte *record)
1614
{
1615
  NdbTransaction *trans= m_active_trans;
1616
  NdbOperation *op;
1617 1618 1619 1620
  const NdbOperation *first, *last;
  uint i;
  int res;
  DBUG_ENTER("peek_indexed_rows");
unknown's avatar
unknown committed
1621

unknown's avatar
unknown committed
1622 1623 1624
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
  
1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657
  first= NULL;
  if (table->s->primary_key != MAX_KEY)
  {
    /*
     * 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());
unknown's avatar
unknown committed
1658

1659 1660 1661 1662 1663 1664 1665 1666
      if (!first)
        first= iop;
      if ((res= set_index_key_from_record(iop, record, i)))
        ERR_RETURN(trans->getNdbError());
    }
  }
  last= trans->getLastDefinedOperation();
  if (first)
1667
    res= execute_no_commit_ie(this,trans,false);
1668 1669 1670 1671 1672 1673 1674 1675
  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))
unknown's avatar
unknown committed
1676 1677 1678 1679
  {
    table->status= STATUS_NOT_FOUND;
    DBUG_RETURN(ndb_err(trans));
  } 
1680 1681 1682 1683
  else
  {
    DBUG_PRINT("info", ("m_dupkey %d", m_dupkey));
  }
1684 1685
  DBUG_RETURN(0);
}
1686

unknown's avatar
unknown committed
1687 1688 1689 1690 1691
/*
  Read one record from NDB using unique secondary index
*/

int ha_ndbcluster::unique_index_read(const byte *key,
1692
                                     uint key_len, byte *buf)
unknown's avatar
unknown committed
1693
{
1694
  int res;
1695
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
1696
  NdbIndexOperation *op;
1697
  DBUG_ENTER("ha_ndbcluster::unique_index_read");
unknown's avatar
unknown committed
1698 1699 1700
  DBUG_PRINT("enter", ("key_len: %u, index: %u", key_len, active_index));
  DBUG_DUMP("key", (char*)key, key_len);
  
1701 1702
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
1703
  if (!(op= trans->getNdbIndexOperation((NDBINDEX *) 
1704
                                        m_index[active_index].unique_index, 
unknown's avatar
unknown committed
1705
                                        (const NDBTAB *) m_table)) ||
1706
      op->readTuple(lm) != 0)
unknown's avatar
unknown committed
1707 1708 1709
    ERR_RETURN(trans->getNdbError());
  
  // Set secondary index key(s)
unknown's avatar
unknown committed
1710
  if ((res= set_index_key(op, table->key_info + active_index, key)))
1711 1712
    DBUG_RETURN(res);
  
unknown's avatar
unknown committed
1713
  if ((res= define_read_attrs(buf, op)))
1714
    DBUG_RETURN(res);
unknown's avatar
unknown committed
1715

1716
  if (execute_no_commit_ie(this,trans,false) != 0) 
unknown's avatar
unknown committed
1717 1718 1719 1720 1721 1722 1723 1724 1725 1726
  {
    table->status= STATUS_NOT_FOUND;
    DBUG_RETURN(ndb_err(trans));
  }
  // The value have now been fetched from NDB
  unpack_record(buf);
  table->status= 0;
  DBUG_RETURN(0);
}

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

1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829
  DBUG_RETURN(1);
}

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

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

1831 1832 1833 1834 1835 1836 1837
*/

inline int ha_ndbcluster::next_result(byte *buf)
{  
  int res;
  DBUG_ENTER("next_result");
    
1838 1839 1840
  if (!m_active_cursor)
    DBUG_RETURN(HA_ERR_END_OF_FILE);
  
unknown's avatar
unknown committed
1841
  if ((res= fetch_next(m_active_cursor)) == 0)
1842 1843 1844 1845 1846 1847 1848
  {
    DBUG_PRINT("info", ("One more record found"));    
    
    unpack_record(buf);
    table->status= 0;
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
1849
  else if (res == 1)
1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860
  {
    // No more records
    table->status= STATUS_NOT_FOUND;
    
    DBUG_PRINT("info", ("No more records"));
    DBUG_RETURN(HA_ERR_END_OF_FILE);
  }
  else
  {
    DBUG_RETURN(ndb_err(m_active_trans));
  }
unknown's avatar
unknown committed
1861 1862
}

1863
/*
1864
  Set bounds for ordered index scan.
1865 1866
*/

unknown's avatar
unknown committed
1867
int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
1868 1869
                              const key_range *keys[2],
                              uint range_no)
1870
{
1871 1872 1873 1874
  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;
1875
  uint i, j;
1876 1877

  DBUG_ENTER("set_bounds");
1878
  DBUG_PRINT("info", ("key_parts=%d", key_parts));
1879

1880
  for (j= 0; j <= 1; j++)
1881
  {
1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894
    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;
    }
1895 1896
  }
  tot_len= 0;
unknown's avatar
unknown committed
1897

1898 1899 1900 1901
  for (i= 0; i < key_parts; i++)
  {
    KEY_PART_INFO *key_part= &key_info->key_part[i];
    Field *field= key_part->field;
1902
#ifndef DBUG_OFF
1903
    uint part_len= key_part->length;
1904
#endif
1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918
    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++)
    {
1919
      struct part_st &p= part[j];
1920 1921 1922 1923 1924 1925 1926
      p.key= NULL;
      p.bound_type= -1;
      if (tot_len < key_tot_len[j])
      {
        p.part_last= (tot_len + part_store_len >= key_tot_len[j]);
        p.key= keys[j];
        p.part_ptr= &p.key->key[tot_len];
unknown's avatar
unknown committed
1927
        p.part_null= key_part->null_bit && *p.part_ptr;
1928
        p.bound_ptr= (const char *)
unknown's avatar
unknown committed
1929
          p.part_null ? 0 : key_part->null_bit ? p.part_ptr + 1 : p.part_ptr;
1930 1931 1932 1933 1934 1935 1936 1937

        if (j == 0)
        {
          switch (p.key->flag)
          {
            case HA_READ_KEY_EXACT:
              p.bound_type= NdbIndexScanOperation::BoundEQ;
              break;
1938
            // ascending
1939 1940 1941 1942 1943 1944 1945 1946 1947
            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;
1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960
            // 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;
1961 1962 1963 1964 1965 1966 1967
            default:
              break;
          }
        }
        if (j == 1) {
          switch (p.key->flag)
          {
1968
            // ascending
1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979
            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;
1980
            // descending strangely sets no end key
1981 1982
          }
        }
1983

1984 1985 1986
        if (p.bound_type == -1)
        {
          DBUG_PRINT("error", ("key %d unknown flag %d", j, p.key->flag));
unknown's avatar
unknown committed
1987
          DBUG_ASSERT(FALSE);
1988
          // Stop setting bounds but continue with what we have
1989
          op->end_of_bound(range_no);
1990 1991 1992 1993
          DBUG_RETURN(0);
        }
      }
    }
1994

1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011
    // 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;
    }
2012

2013 2014
    for (j= 0; j <= 1; j++)
    {
2015
      struct part_st &p= part[j];
2016 2017 2018 2019 2020 2021 2022 2023 2024
      // 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)
2025
        {
unknown's avatar
unknown committed
2026 2027 2028
          const char* ptr= p.bound_ptr;
          char buf[256];
          shrink_varchar(field, ptr, buf);
unknown's avatar
Merge  
unknown committed
2029
          if (op->setBound(i, p.bound_type, ptr))
2030
            ERR_RETURN(op->getNdbError());
2031
        }
2032 2033 2034 2035
      }
    }

    tot_len+= part_store_len;
2036
  }
2037
  op->end_of_bound(range_no);
2038 2039 2040
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
2041
/*
2042
  Start ordered index scan in NDB
unknown's avatar
unknown committed
2043 2044
*/

2045
int ha_ndbcluster::ordered_index_scan(const key_range *start_key,
2046 2047
                                      const key_range *end_key,
                                      bool sorted, bool descending, byte* buf)
unknown's avatar
unknown committed
2048
{  
2049
  int res;
unknown's avatar
unknown committed
2050
  bool restart;
2051
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
2052
  NdbIndexScanOperation *op;
2053

2054 2055 2056
  DBUG_ENTER("ha_ndbcluster::ordered_index_scan");
  DBUG_PRINT("enter", ("index: %u, sorted: %d, descending: %d",
             active_index, sorted, descending));  
unknown's avatar
unknown committed
2057
  DBUG_PRINT("enter", ("Starting new ordered scan on %s", m_tabname));
unknown's avatar
unknown committed
2058

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

  if (!restart && generate_scan_filter(m_cond_stack, op))
    DBUG_RETURN(ndb_err(trans));
2094
  
2095
  if (!restart && (res= define_read_attrs(buf, op)))
2096
  {
2097
    DBUG_RETURN(res);
unknown's avatar
unknown committed
2098
  }
2099

2100
  if (execute_no_commit(this,trans,false) != 0)
2101 2102 2103 2104
    DBUG_RETURN(ndb_err(trans));
  
  DBUG_RETURN(next_result(buf));
}
unknown's avatar
unknown committed
2105

2106 2107 2108 2109 2110 2111 2112 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
/*
  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));
}

unknown's avatar
unknown committed
2142
/*
2143
  Start full table scan in NDB
unknown's avatar
unknown committed
2144 2145 2146 2147
 */

int ha_ndbcluster::full_table_scan(byte *buf)
{
2148
  int res;
unknown's avatar
unknown committed
2149
  NdbScanOperation *op;
2150
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
2151 2152 2153 2154

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

2155 2156
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
2157
  bool need_pk = (lm == NdbOperation::LM_Read);
2158
  if (!(op=trans->getNdbScanOperation((const NDBTAB *) m_table)) ||
2159 2160 2161
      op->readTuples(lm, 
		     (need_pk)?NdbScanOperation::SF_KeyInfo:0, 
		     parallelism))
unknown's avatar
unknown committed
2162
    ERR_RETURN(trans->getNdbError());
2163
  m_active_cursor= op;
2164 2165
  if (generate_scan_filter(m_cond_stack, op))
    DBUG_RETURN(ndb_err(trans));
unknown's avatar
unknown committed
2166
  if ((res= define_read_attrs(buf, op)))
2167 2168
    DBUG_RETURN(res);

2169
  if (execute_no_commit(this,trans,false) != 0)
2170 2171 2172
    DBUG_RETURN(ndb_err(trans));
  DBUG_PRINT("exit", ("Scan started successfully"));
  DBUG_RETURN(next_result(buf));
2173 2174
}

unknown's avatar
unknown committed
2175 2176 2177 2178 2179
/*
  Insert one record into NDB
*/
int ha_ndbcluster::write_row(byte *record)
{
unknown's avatar
unknown committed
2180
  bool has_auto_increment;
unknown's avatar
unknown committed
2181
  uint i;
2182
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
2183 2184
  NdbOperation *op;
  int res;
unknown's avatar
unknown committed
2185 2186
  THD *thd= current_thd;

unknown's avatar
unknown committed
2187
  DBUG_ENTER("write_row");
2188

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

unknown's avatar
unknown committed
2229
  statistic_increment(thd->status_var.ha_write_count, &LOCK_status);
unknown's avatar
unknown committed
2230 2231
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
    table->timestamp_field->set_time();
unknown's avatar
unknown committed
2232

unknown's avatar
unknown committed
2233
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)))
unknown's avatar
unknown committed
2234 2235 2236 2237 2238 2239
    ERR_RETURN(trans->getNdbError());

  res= (m_use_write) ? op->writeTuple() :op->insertTuple(); 
  if (res != 0)
    ERR_RETURN(trans->getNdbError());  
 
2240
  if (table->s->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
2241 2242
  {
    // Table has hidden primary key
2243
    Ndb *ndb= get_ndb();
2244 2245
    int ret;
    Uint64 auto_value;
2246 2247
    uint retries= NDB_AUTO_INCREMENT_RETRIES;
    do {
2248 2249
      ret= ndb->getAutoIncrementValue((const NDBTAB *) m_table, auto_value, 1);
    } while (ret == -1 && 
2250 2251
             --retries &&
             ndb->getNdbError().status == NdbError::TemporaryError);
2252
    if (ret == -1)
2253
      ERR_RETURN(ndb->getNdbError());
2254
    if (set_hidden_key(op, table->s->fields, (const byte*)&auto_value))
unknown's avatar
unknown committed
2255 2256 2257 2258 2259
      ERR_RETURN(op->getNdbError());
  } 
  else 
  {
    int res;
2260

2261
    if ((res= set_primary_key_from_record(op, record)))
2262
      return res;  
unknown's avatar
unknown committed
2263 2264 2265
  }

  // Set non-key attribute(s)
unknown's avatar
unknown committed
2266
  bool set_blob_value= FALSE;
2267
  for (i= 0; i < table->s->fields; i++) 
unknown's avatar
unknown committed
2268 2269 2270
  {
    Field *field= table->field[i];
    if (!(field->flags & PRI_KEY_FLAG) &&
2271
        set_ndb_value(op, field, i, &set_blob_value))
2272
    {
2273
      m_skip_auto_increment= TRUE;
unknown's avatar
unknown committed
2274
      ERR_RETURN(op->getNdbError());
2275
    }
unknown's avatar
unknown committed
2276 2277
  }

2278 2279
  m_rows_changed++;

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

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

unknown's avatar
unknown committed
2339 2340 2341 2342 2343 2344 2345
  DBUG_RETURN(0);
}


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

int ha_ndbcluster::key_cmp(uint keynr, const byte * old_row,
2346
                           const byte * new_row)
unknown's avatar
unknown committed
2347 2348 2349 2350 2351 2352 2353 2354 2355
{
  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) !=
2356 2357
          (new_row[key_part->null_offset] & key_part->null_bit))
        return 1;
unknown's avatar
unknown committed
2358
    }
2359
    if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
unknown's avatar
unknown committed
2360 2361 2362
    {

      if (key_part->field->cmp_binary((char*) (old_row + key_part->offset),
2363 2364 2365
                                      (char*) (new_row + key_part->offset),
                                      (ulong) key_part->length))
        return 1;
unknown's avatar
unknown committed
2366 2367 2368 2369
    }
    else
    {
      if (memcmp(old_row+key_part->offset, new_row+key_part->offset,
2370 2371
                 key_part->length))
        return 1;
unknown's avatar
unknown committed
2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383
    }
  }
  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;
2384
  NdbTransaction *trans= m_active_trans;
2385
  NdbScanOperation* cursor= m_active_cursor;
unknown's avatar
unknown committed
2386 2387 2388 2389
  NdbOperation *op;
  uint i;
  DBUG_ENTER("update_row");
  
2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404
  /*
   * If IGNORE the ignore constraint violations on primary and unique keys
   */
  if (m_ignore_dup_key)
  {
    int peek_res= peek_indexed_rows(new_data);
    
    if (!peek_res) 
    {
      DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
    }
    if (peek_res != HA_ERR_KEY_NOT_FOUND)
      DBUG_RETURN(peek_res);
  }

unknown's avatar
unknown committed
2405
  statistic_increment(thd->status_var.ha_update_count, &LOCK_status);
unknown's avatar
unknown committed
2406
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
2407
  {
unknown's avatar
unknown committed
2408
    table->timestamp_field->set_time();
2409 2410 2411
    // Set query_id so that field is really updated
    table->timestamp_field->query_id= thd->query_id;
  }
unknown's avatar
unknown committed
2412

2413
  /* Check for update of primary key for special handling */  
2414 2415
  if ((table->s->primary_key != MAX_KEY) &&
      (key_cmp(table->s->primary_key, old_data, new_data)))
2416
  {
2417
    int read_res, insert_res, delete_res, undo_res;
2418

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

2462
  if (cursor)
unknown's avatar
unknown committed
2463
  {
2464 2465 2466 2467 2468 2469 2470 2471
    /*
      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"));
2472
    if (!(op= cursor->updateCurrentTuple()))
2473
      ERR_RETURN(trans->getNdbError());
2474
    m_lock_tuple= false;
2475
    m_ops_pending++;
unknown's avatar
unknown committed
2476
    if (uses_blob_value(FALSE))
2477
      m_blobs_pending= TRUE;
2478 2479 2480
  }
  else
  {  
unknown's avatar
unknown committed
2481
    if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) ||
2482
        op->updateTuple() != 0)
2483 2484
      ERR_RETURN(trans->getNdbError());  
    
2485
    if (table->s->primary_key == MAX_KEY) 
2486 2487 2488 2489 2490
    {
      // 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 
2491 2492
      // read into m_ref
      DBUG_DUMP("key", m_ref, NDB_HIDDEN_PRIMARY_KEY_LENGTH);
2493
      
unknown's avatar
unknown committed
2494
      if (set_hidden_key(op, table->s->fields, m_ref))
2495
        ERR_RETURN(op->getNdbError());
2496 2497 2498 2499
    } 
    else 
    {
      int res;
2500
      if ((res= set_primary_key_from_record(op, old_data)))
2501
        DBUG_RETURN(res);
2502
    }
unknown's avatar
unknown committed
2503 2504
  }

2505 2506
  m_rows_changed++;

unknown's avatar
unknown committed
2507
  // Set non-key attribute(s)
2508
  for (i= 0; i < table->s->fields; i++) 
unknown's avatar
unknown committed
2509 2510
  {
    Field *field= table->field[i];
2511
    if (((thd->query_id == field->query_id) || m_retrieve_all_fields) &&
unknown's avatar
unknown committed
2512
        (!(field->flags & PRI_KEY_FLAG)) &&
2513
        set_ndb_value(op, field, i))
unknown's avatar
unknown committed
2514 2515
      ERR_RETURN(op->getNdbError());
  }
2516

unknown's avatar
unknown committed
2517
  // Execute update operation
2518
  if (!cursor && execute_no_commit(this,trans,false) != 0) {
2519
    no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
2520
    DBUG_RETURN(ndb_err(trans));
2521
  }
unknown's avatar
unknown committed
2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532
  
  DBUG_RETURN(0);
}


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

int ha_ndbcluster::delete_row(const byte *record)
{
unknown's avatar
unknown committed
2533
  THD *thd= current_thd;
2534
  NdbTransaction *trans= m_active_trans;
2535
  NdbScanOperation* cursor= m_active_cursor;
unknown's avatar
unknown committed
2536 2537 2538
  NdbOperation *op;
  DBUG_ENTER("delete_row");

unknown's avatar
unknown committed
2539
  statistic_increment(thd->status_var.ha_delete_count,&LOCK_status);
unknown's avatar
unknown committed
2540
  m_rows_changed++;
unknown's avatar
unknown committed
2541

2542
  if (cursor)
unknown's avatar
unknown committed
2543
  {
2544
    /*
2545
      We are scanning records and want to delete the record
2546
      that was just found, call deleteTuple on the cursor 
2547
      to take over the lock to a new delete operation
2548 2549 2550 2551
      And thus setting the primary key of the record from 
      the active record in cursor
    */
    DBUG_PRINT("info", ("Calling deleteTuple on cursor"));
2552
    if (cursor->deleteCurrentTuple() != 0)
2553
      ERR_RETURN(trans->getNdbError());     
2554
    m_lock_tuple= false;
2555
    m_ops_pending++;
unknown's avatar
unknown committed
2556

2557 2558
    no_uncommitted_rows_update(-1);

2559 2560 2561
    if (!m_primary_key_update)
      // If deleting from cursor, NoCommit will be handled in next_result
      DBUG_RETURN(0);
2562 2563
  }
  else
unknown's avatar
unknown committed
2564
  {
2565
    
unknown's avatar
unknown committed
2566
    if (!(op=trans->getNdbOperation((const NDBTAB *) m_table)) || 
2567
        op->deleteTuple() != 0)
2568 2569
      ERR_RETURN(trans->getNdbError());
    
2570 2571
    no_uncommitted_rows_update(-1);
    
2572
    if (table->s->primary_key == MAX_KEY) 
2573 2574 2575 2576
    {
      // This table has no primary key, use "hidden" primary key
      DBUG_PRINT("info", ("Using hidden key"));
      
unknown's avatar
unknown committed
2577
      if (set_hidden_key(op, table->s->fields, m_ref))
2578
        ERR_RETURN(op->getNdbError());
2579 2580 2581 2582
    } 
    else 
    {
      int res;
2583 2584
      if ((res= set_primary_key_from_record(op, record)))
        return res;  
2585
    }
unknown's avatar
unknown committed
2586
  }
2587

unknown's avatar
unknown committed
2588
  // Execute delete operation
2589
  if (execute_no_commit(this,trans,false) != 0) {
2590
    no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
2591
    DBUG_RETURN(ndb_err(trans));
2592
  }
unknown's avatar
unknown committed
2593 2594
  DBUG_RETURN(0);
}
2595
  
unknown's avatar
unknown committed
2596 2597 2598 2599 2600
/*
  Unpack a record read from NDB 

  SYNOPSIS
    unpack_record()
2601
    buf                 Buffer to store read row
unknown's avatar
unknown committed
2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613

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

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

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

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

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

#ifndef DBUG_OFF
2696
  const NDBTAB *tab= (const NDBTAB*) m_table;
2697

unknown's avatar
unknown committed
2698 2699
  if (!_db_on_)
    DBUG_VOID_RETURN;
unknown's avatar
Merge  
unknown committed
2700

2701
  char buf_type[MAX_FIELD_WIDTH], buf_val[MAX_FIELD_WIDTH];
unknown's avatar
Merge  
unknown committed
2702
  String type(buf_type, sizeof(buf_type), &my_charset_bin);
2703
  String val(buf_val, sizeof(buf_val), &my_charset_bin);
2704
  for (uint f= 0; f < table->s->fields; f++)
unknown's avatar
unknown committed
2705
  {
unknown's avatar
Merge  
unknown committed
2706
    /* Use DBUG_PRINT since DBUG_FILE cannot be filtered out */
2707
    char buf[2000];
unknown's avatar
unknown committed
2708
    Field *field;
2709
    void* ptr;
unknown's avatar
unknown committed
2710
    NdbValue value;
unknown's avatar
unknown committed
2711

2712
    buf[0]= 0;
unknown's avatar
Merge  
unknown committed
2713
    field= table->field[f];
unknown's avatar
unknown committed
2714
    if (!(value= m_value[f]).ptr)
unknown's avatar
unknown committed
2715
    {
unknown's avatar
unknown committed
2716
      strmov(buf, "not read");
2717
      goto print_value;
unknown's avatar
unknown committed
2718
    }
2719

2720
    ptr= field->ptr;
unknown's avatar
unknown committed
2721 2722

    if (! (field->flags & BLOB_FLAG))
unknown's avatar
unknown committed
2723
    {
unknown's avatar
unknown committed
2724 2725
      if (value.rec->isNULL())
      {
unknown's avatar
unknown committed
2726
        strmov(buf, "NULL");
2727
        goto print_value;
unknown's avatar
unknown committed
2728
      }
2729 2730 2731 2732 2733
      type.length(0);
      val.length(0);
      field->sql_type(type);
      field->val_str(&val);
      my_snprintf(buf, sizeof(buf), "%s %s", type.c_ptr(), val.c_ptr());
unknown's avatar
unknown committed
2734 2735 2736
    }
    else
    {
2737
      NdbBlob *ndb_blob= value.blob;
unknown's avatar
unknown committed
2738
      bool isNull= TRUE;
unknown's avatar
unknown committed
2739
      ndb_blob->getNull(isNull);
unknown's avatar
unknown committed
2740 2741
      if (isNull)
        strmov(buf, "NULL");
unknown's avatar
unknown committed
2742
    }
unknown's avatar
Merge  
unknown committed
2743

2744
print_value:
unknown's avatar
Merge  
unknown committed
2745
    DBUG_PRINT("value", ("%u,%s: %s", f, field->field_name, buf));
unknown's avatar
unknown committed
2746 2747 2748 2749 2750 2751 2752 2753
  }
#endif
  DBUG_VOID_RETURN;
}


int ha_ndbcluster::index_init(uint index)
{
2754
  DBUG_ENTER("ha_ndbcluster::index_init");
unknown's avatar
unknown committed
2755
  DBUG_PRINT("enter", ("index: %u", index));
2756 2757 2758 2759 2760 2761
 /*
    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;
unknown's avatar
unknown committed
2762 2763 2764 2765 2766 2767
  DBUG_RETURN(handler::index_init(index));
}


int ha_ndbcluster::index_end()
{
2768
  DBUG_ENTER("ha_ndbcluster::index_end");
2769
  DBUG_RETURN(close_scan());
unknown's avatar
unknown committed
2770 2771
}

2772 2773 2774 2775 2776 2777 2778 2779
/**
 * 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;
2780
  const byte* end_ptr= key + key_len;
2781 2782 2783 2784 2785 2786
  curr_part= key_info->key_part;
  end_part= curr_part + key_info->key_parts;
  

  for (; curr_part != end_part && key < end_ptr; curr_part++)
  {
unknown's avatar
unknown committed
2787
    if (curr_part->null_bit && *key)
2788 2789 2790 2791 2792 2793
      return 1;

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

int ha_ndbcluster::index_read(byte *buf,
2796 2797
                              const byte *key, uint key_len, 
                              enum ha_rkey_function find_flag)
unknown's avatar
unknown committed
2798
{
2799
  DBUG_ENTER("ha_ndbcluster::index_read");
unknown's avatar
unknown committed
2800 2801 2802
  DBUG_PRINT("enter", ("active_index: %u, key_len: %u, find_flag: %d", 
                       active_index, key_len, find_flag));

unknown's avatar
unknown committed
2803
  int error;
2804 2805
  ndb_index_type type= get_index_type(active_index);
  const KEY* key_info= table->key_info+active_index;
unknown's avatar
unknown committed
2806 2807 2808 2809 2810
  switch (type){
  case PRIMARY_KEY_ORDERED_INDEX:
  case PRIMARY_KEY_INDEX:
    if (find_flag == HA_READ_KEY_EXACT && key_info->key_length == key_len)
    {
unknown's avatar
unknown committed
2811
      if (m_active_cursor && (error= close_scan()))
2812
        DBUG_RETURN(error);
unknown's avatar
unknown committed
2813 2814 2815 2816 2817 2818 2819 2820 2821
      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:
2822
    if (find_flag == HA_READ_KEY_EXACT && key_info->key_length == key_len &&
2823
        !check_null_in_key(key_info, key, key_len))
unknown's avatar
unknown committed
2824
    {
unknown's avatar
unknown committed
2825
      if (m_active_cursor && (error= close_scan()))
2826
        DBUG_RETURN(error);
unknown's avatar
unknown committed
2827 2828 2829 2830
      DBUG_RETURN(unique_index_read(key, key_len, buf));
    }
    else if (type == UNIQUE_INDEX)
    {
2831
      DBUG_RETURN(unique_index_scan(key_info, key, key_len, buf));
unknown's avatar
unknown committed
2832 2833 2834 2835 2836 2837
    }
    break;
  case ORDERED_INDEX:
    break;
  default:
  case UNDEFINED_INDEX:
unknown's avatar
unknown committed
2838
    DBUG_ASSERT(FALSE);
2839
    DBUG_RETURN(1);
unknown's avatar
unknown committed
2840 2841 2842
    break;
  }
  
2843
  key_range start_key;
2844 2845 2846
  start_key.key= key;
  start_key.length= key_len;
  start_key.flag= find_flag;
2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858
  bool descending= FALSE;
  switch (find_flag) {
  case HA_READ_KEY_OR_PREV:
  case HA_READ_BEFORE_KEY:
  case HA_READ_PREFIX_LAST:
  case HA_READ_PREFIX_LAST_OR_PREV:
    descending= TRUE;
    break;
  default:
    break;
  }
  error= ordered_index_scan(&start_key, 0, TRUE, descending, buf);  
unknown's avatar
unknown committed
2859
  DBUG_RETURN(error == HA_ERR_END_OF_FILE ? HA_ERR_KEY_NOT_FOUND : error);
unknown's avatar
unknown committed
2860 2861 2862 2863
}


int ha_ndbcluster::index_read_idx(byte *buf, uint index_no, 
2864 2865
                              const byte *key, uint key_len, 
                              enum ha_rkey_function find_flag)
unknown's avatar
unknown committed
2866
{
unknown's avatar
unknown committed
2867
  statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status);
2868
  DBUG_ENTER("ha_ndbcluster::index_read_idx");
unknown's avatar
unknown committed
2869 2870 2871 2872 2873 2874 2875 2876
  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)
{
2877
  DBUG_ENTER("ha_ndbcluster::index_next");
unknown's avatar
unknown committed
2878
  statistic_increment(current_thd->status_var.ha_read_next_count,
2879
                      &LOCK_status);
2880
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
2881 2882 2883 2884 2885
}


int ha_ndbcluster::index_prev(byte *buf)
{
2886
  DBUG_ENTER("ha_ndbcluster::index_prev");
unknown's avatar
unknown committed
2887
  statistic_increment(current_thd->status_var.ha_read_prev_count,
2888
                      &LOCK_status);
2889
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
2890 2891 2892 2893 2894
}


int ha_ndbcluster::index_first(byte *buf)
{
2895
  DBUG_ENTER("ha_ndbcluster::index_first");
unknown's avatar
unknown committed
2896
  statistic_increment(current_thd->status_var.ha_read_first_count,
2897
                      &LOCK_status);
unknown's avatar
unknown committed
2898 2899 2900
  // Start the ordered index scan and fetch the first row

  // Only HA_READ_ORDER indexes get called by index_first
2901
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, FALSE, buf));
unknown's avatar
unknown committed
2902 2903 2904 2905 2906
}


int ha_ndbcluster::index_last(byte *buf)
{
2907
  DBUG_ENTER("ha_ndbcluster::index_last");
2908
  statistic_increment(current_thd->status_var.ha_read_last_count,&LOCK_status);
2909
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, TRUE, buf));
unknown's avatar
unknown committed
2910 2911
}

2912 2913 2914 2915 2916
int ha_ndbcluster::index_read_last(byte * buf, const byte * key, uint key_len)
{
  DBUG_ENTER("ha_ndbcluster::index_read_last");
  DBUG_RETURN(index_read(buf, key, key_len, HA_READ_PREFIX_LAST));
}
unknown's avatar
unknown committed
2917

2918 2919
inline
int ha_ndbcluster::read_range_first_to_buf(const key_range *start_key,
2920 2921 2922
                                           const key_range *end_key,
                                           bool eq_r, bool sorted,
                                           byte* buf)
2923
{
2924 2925
   ndb_index_type type= get_index_type(active_index);
KEY* key_info;
2926 2927
  int error= 1; 
  DBUG_ENTER("ha_ndbcluster::read_range_first_to_buf");
unknown's avatar
unknown committed
2928
  DBUG_PRINT("info", ("eq_r: %d, sorted: %d", eq_r, sorted));
2929

2930
  switch (type){
2931
  case PRIMARY_KEY_ORDERED_INDEX:
2932
  case PRIMARY_KEY_INDEX:
2933 2934
    key_info= table->key_info + active_index;
    if (start_key && 
2935 2936
        start_key->length == key_info->key_length &&
        start_key->flag == HA_READ_KEY_EXACT)
2937
    {
unknown's avatar
unknown committed
2938
      if (m_active_cursor && (error= close_scan()))
2939
        DBUG_RETURN(error);
2940 2941 2942
      error= pk_read(start_key->key, start_key->length, buf);      
      DBUG_RETURN(error == HA_ERR_KEY_NOT_FOUND ? HA_ERR_END_OF_FILE : error);
    }
2943
    break;
2944
  case UNIQUE_ORDERED_INDEX:
2945
  case UNIQUE_INDEX:
2946
    key_info= table->key_info + active_index;
2947
    if (start_key && start_key->length == key_info->key_length &&
2948 2949
        start_key->flag == HA_READ_KEY_EXACT && 
        !check_null_in_key(key_info, start_key->key, start_key->length))
2950
    {
unknown's avatar
unknown committed
2951
      if (m_active_cursor && (error= close_scan()))
2952
        DBUG_RETURN(error);
2953 2954 2955
      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);
    }
2956 2957 2958 2959 2960 2961 2962 2963
    else if (type == UNIQUE_INDEX)
    {
      error= unique_index_scan(key_info, 
			       start_key->key, 
			       start_key->length, 
			       buf);
      DBUG_RETURN(error == HA_ERR_KEY_NOT_FOUND ? HA_ERR_END_OF_FILE : error);
    }
2964 2965 2966 2967
    break;
  default:
    break;
  }
2968 2969

  // Start the ordered index scan and fetch the first row
2970
  error= ordered_index_scan(start_key, end_key, sorted, FALSE, buf);
2971 2972 2973
  DBUG_RETURN(error);
}

2974

unknown's avatar
unknown committed
2975
int ha_ndbcluster::read_range_first(const key_range *start_key,
2976 2977
                                    const key_range *end_key,
                                    bool eq_r, bool sorted)
unknown's avatar
unknown committed
2978 2979 2980 2981 2982
{
  byte* buf= table->record[0];
  DBUG_ENTER("ha_ndbcluster::read_range_first");
  
  DBUG_RETURN(read_range_first_to_buf(start_key,
2983 2984 2985 2986
                                      end_key,
                                      eq_r, 
                                      sorted,
                                      buf));
unknown's avatar
unknown committed
2987 2988
}

2989
int ha_ndbcluster::read_range_next()
2990 2991 2992 2993 2994 2995
{
  DBUG_ENTER("ha_ndbcluster::read_range_next");
  DBUG_RETURN(next_result(table->record[0]));
}


unknown's avatar
unknown committed
2996 2997
int ha_ndbcluster::rnd_init(bool scan)
{
2998
  NdbScanOperation *cursor= m_active_cursor;
unknown's avatar
unknown committed
2999 3000
  DBUG_ENTER("rnd_init");
  DBUG_PRINT("enter", ("scan: %d", scan));
3001
  // Check if scan is to be restarted
unknown's avatar
unknown committed
3002 3003 3004 3005
  if (cursor)
  {
    if (!scan)
      DBUG_RETURN(1);
unknown's avatar
unknown committed
3006
    if (cursor->restart(m_force_send) != 0)
3007 3008 3009 3010
    {
      DBUG_ASSERT(0);
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
3011
  }
3012
  index_init(table->s->primary_key);
unknown's avatar
unknown committed
3013 3014 3015
  DBUG_RETURN(0);
}

3016 3017
int ha_ndbcluster::close_scan()
{
3018
  NdbTransaction *trans= m_active_trans;
3019 3020
  DBUG_ENTER("close_scan");

unknown's avatar
unknown committed
3021 3022
  m_multi_cursor= 0;
  if (!m_active_cursor && !m_multi_cursor)
3023 3024
    DBUG_RETURN(1);

unknown's avatar
unknown committed
3025
  NdbScanOperation *cursor= m_active_cursor ? m_active_cursor : m_multi_cursor;
unknown's avatar
unknown committed
3026
  
unknown's avatar
unknown committed
3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038
  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"));
      
3039
      if (!(op= cursor->lockCurrentTuple()))
unknown's avatar
unknown committed
3040 3041 3042 3043 3044 3045
      {
	m_lock_tuple= false;
	ERR_RETURN(trans->getNdbError());
      }
      m_ops_pending++;      
  }
3046
  m_lock_tuple= false;
3047
  if (m_ops_pending)
unknown's avatar
unknown committed
3048 3049 3050 3051 3052
  {
    /*
      Take over any pending transactions to the 
      deleteing/updating transaction before closing the scan    
    */
3053
    DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending));    
3054
    if (execute_no_commit(this,trans,false) != 0) {
3055
      no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
3056
      DBUG_RETURN(ndb_err(trans));
3057
    }
3058
    m_ops_pending= 0;
unknown's avatar
unknown committed
3059 3060
  }
  
unknown's avatar
unknown committed
3061
  cursor->close(m_force_send, TRUE);
unknown's avatar
unknown committed
3062
  m_active_cursor= m_multi_cursor= NULL;
unknown's avatar
unknown committed
3063
  DBUG_RETURN(0);
3064
}
unknown's avatar
unknown committed
3065 3066 3067 3068

int ha_ndbcluster::rnd_end()
{
  DBUG_ENTER("rnd_end");
3069
  DBUG_RETURN(close_scan());
unknown's avatar
unknown committed
3070 3071 3072 3073 3074 3075
}


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

unknown's avatar
unknown committed
3079
  if (!m_active_cursor)
3080 3081
    DBUG_RETURN(full_table_scan(buf));
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094
}


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

int ha_ndbcluster::rnd_pos(byte *buf, byte *pos)
{
  DBUG_ENTER("rnd_pos");
unknown's avatar
unknown committed
3095
  statistic_increment(current_thd->status_var.ha_read_rnd_count,
3096
                      &LOCK_status);
unknown's avatar
unknown committed
3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116
  // 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");

3117
  if (table->s->primary_key != MAX_KEY) 
unknown's avatar
unknown committed
3118
  {
3119
    key_info= table->key_info + table->s->primary_key;
unknown's avatar
unknown committed
3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134
    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;
      }
3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154

      size_t len = key_part->length;
      const byte * ptr = record + key_part->offset;
      Field *field = key_part->field;
      if ((field->type() ==  MYSQL_TYPE_VARCHAR) &&
	  ((Field_varstring*)field)->length_bytes == 1)
      {
	/** 
	 * Keys always use 2 bytes length
	 */
	buff[0] = ptr[0];
	buff[1] = 0;
	memcpy(buff+2, ptr + 1, len);	
	len += 2;
      }
      else
      {
	memcpy(buff, ptr, len);
      }
      buff += len;
unknown's avatar
unknown committed
3155 3156 3157 3158 3159 3160
    }
  } 
  else 
  {
    // No primary key, get hidden key
    DBUG_PRINT("info", ("Getting hidden key"));
3161
#ifndef DBUG_OFF
3162
    int hidden_no= table->s->fields;
unknown's avatar
unknown committed
3163
    const NDBTAB *tab= (const NDBTAB *) m_table;  
unknown's avatar
unknown committed
3164 3165 3166 3167
    const NDBCOL *hidden_col= tab->getColumn(hidden_no);
    DBUG_ASSERT(hidden_col->getPrimaryKey() && 
                hidden_col->getAutoIncrement() &&
                ref_length == NDB_HIDDEN_PRIMARY_KEY_LENGTH);
3168
#endif
3169
    memcpy(ref, m_ref, ref_length);
unknown's avatar
unknown committed
3170 3171 3172 3173 3174 3175 3176
  }
  
  DBUG_DUMP("ref", (char*)ref, ref_length);
  DBUG_VOID_RETURN;
}


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

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

  DBUG_RETURN(result);
unknown's avatar
unknown committed
3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270
}


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"));
3271 3272
    DBUG_PRINT("info", ("Clearing condition stack"));
    cond_clear();
unknown's avatar
unknown committed
3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 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
    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"));
3342 3343
    DBUG_PRINT("info", ("Ignoring duplicate key"));
    m_ignore_dup_key= TRUE;
unknown's avatar
unknown committed
3344 3345 3346
    break;
  case HA_EXTRA_NO_IGNORE_DUP_KEY:
    DBUG_PRINT("info", ("HA_EXTRA_NO_IGNORE_DUP_KEY"));
3347
    m_ignore_dup_key= FALSE;
unknown's avatar
unknown committed
3348 3349
    break;
  case HA_EXTRA_RETRIEVE_ALL_COLS:    /* Retrieve all columns, not just those
3350 3351
                                         where field->query_id is the same as
                                         the current query id */
unknown's avatar
unknown committed
3352
    DBUG_PRINT("info", ("HA_EXTRA_RETRIEVE_ALL_COLS"));
3353
    m_retrieve_all_fields= TRUE;
unknown's avatar
unknown committed
3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365
    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"));
3366
    m_retrieve_primary_key= TRUE;
unknown's avatar
unknown committed
3367 3368 3369 3370 3371 3372
    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"));
3373 3374
  case HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
    DBUG_PRINT("info", ("HA_EXTRA_KEYREAD_PRESERVE_FIELDS"));
unknown's avatar
unknown committed
3375
    break;
3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388
  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;
unknown's avatar
unknown committed
3389 3390 3391 3392 3393
  }
  
  DBUG_RETURN(0);
}

3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406
/* 
   Start of an insert, remember number of rows to be inserted, it will
   be used in write_row and get_autoincrement to send an optimal number
   of rows in each roundtrip to the server

   SYNOPSIS
   rows     number of rows to insert, 0 if unknown

*/

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

  DBUG_ENTER("start_bulk_insert");
unknown's avatar
unknown committed
3410
  DBUG_PRINT("enter", ("rows: %d", (int)rows));
3411
  
3412
  m_rows_inserted= (ha_rows) 0;
3413
  if (!m_use_write && m_ignore_dup_key)
3414 3415 3416
  {
    /*
      compare if expression with that in write_row
3417
      we have a situation where peek_indexed_rows() will be called
3418 3419 3420 3421 3422 3423 3424 3425
      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;
  }
3426
  if (rows == (ha_rows) 0)
unknown's avatar
unknown committed
3427
  {
3428 3429
    /* We don't know how many will be inserted, guess */
    m_rows_to_insert= m_autoincrement_prefetch;
unknown's avatar
unknown committed
3430
  }
3431 3432
  else
    m_rows_to_insert= rows; 
3433 3434 3435 3436 3437 3438 3439 3440

  /* 
    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.   
  */
3441
  const int bytesperbatch= 8192;
3442
  bytes= 12 + tab->getRowSizeInBytes() + 4 * tab->getNoOfColumns();
3443
  batch= bytesperbatch/bytes;
3444 3445
  batch= batch == 0 ? 1 : batch;
  DBUG_PRINT("info", ("batch: %d, bytes: %d", batch, bytes));
3446
  m_bulk_insert_rows= batch;
3447 3448 3449 3450 3451 3452 3453 3454 3455

  DBUG_VOID_RETURN;
}

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

3458
  DBUG_ENTER("end_bulk_insert");
3459
  // Check if last inserts need to be flushed
3460
  if (m_bulk_insert_not_flushed)
3461
  {
3462
    NdbTransaction *trans= m_active_trans;
3463 3464 3465
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
                        "rows_inserted:%d, bulk_insert_rows: %d", 
3466
                        (int) m_rows_inserted, (int) m_bulk_insert_rows)); 
3467
    m_bulk_insert_not_flushed= FALSE;
3468 3469
    if (m_transaction_on)
    {
3470
      if (execute_no_commit(this, trans,false) != 0)
3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482
      {
        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);
      }
3483 3484 3485 3486 3487
      else
      {
        int res= trans->restart();
        DBUG_ASSERT(res == 0);
      }
3488
    }
3489 3490
  }

3491 3492
  m_rows_inserted= (ha_rows) 0;
  m_rows_to_insert= (ha_rows) 1;
3493
  DBUG_RETURN(error);
3494 3495
}

unknown's avatar
unknown committed
3496 3497 3498 3499

int ha_ndbcluster::extra_opt(enum ha_extra_function operation, ulong cache_size)
{
  DBUG_ENTER("extra_opt");
unknown's avatar
unknown committed
3500
  DBUG_PRINT("enter", ("cache_size: %lu", cache_size));
unknown's avatar
unknown committed
3501 3502 3503
  DBUG_RETURN(extra(operation));
}

unknown's avatar
unknown committed
3504 3505 3506 3507
static const char *ha_ndbcluster_exts[] = {
 ha_ndb_ext,
 NullS
};
unknown's avatar
unknown committed
3508

3509
const char** ha_ndbcluster::bas_ext() const
unknown's avatar
unknown committed
3510 3511 3512
{
  return ha_ndbcluster_exts;
}
unknown's avatar
unknown committed
3513 3514 3515 3516 3517 3518 3519 3520 3521

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

double ha_ndbcluster::scan_time()
{
unknown's avatar
unknown committed
3522 3523 3524
  DBUG_ENTER("ha_ndbcluster::scan_time()");
  double res= rows2double(records*1000);
  DBUG_PRINT("exit", ("table: %s value: %f", 
3525
                      m_tabname, res));
unknown's avatar
unknown committed
3526
  DBUG_RETURN(res);
unknown's avatar
unknown committed
3527 3528
}

unknown's avatar
unknown committed
3529 3530 3531 3532 3533 3534 3535
/*
  Convert MySQL table locks into locks supported by Ndb Cluster.
  Note that MySQL Cluster does currently not support distributed
  table locks, so to be safe one should set cluster in Single
  User Mode, before relying on table locks when updating tables
  from several MySQL servers
*/
unknown's avatar
unknown committed
3536 3537 3538 3539 3540 3541 3542 3543

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

unknown's avatar
unknown committed
3545 3546 3547
    /* If we are not doing a LOCK TABLE, then allow multiple
       writers */
    
3548 3549 3550
    /* Since NDB does not currently have table locks
       this is treated as a ordinary lock */

3551
    if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
unknown's avatar
unknown committed
3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566
         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;
3567 3568

  DBUG_PRINT("exit", ("lock_type: %d", lock_type));
unknown's avatar
unknown committed
3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590
  
  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
3591
  for the statement, this will be stored in thd_ndb.stmt.
unknown's avatar
unknown committed
3592
  If not, we have to start a master transaction if there doesn't exist
3593
  one from before, this will be stored in thd_ndb.all
unknown's avatar
unknown committed
3594 3595 3596
 
  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  
3597
  If we are locking the table then:
3598
  - save the NdbDictionary::Table for easy access
3599 3600
  - save reference to table statistics
  - refresh list of the indexes for the table if needed (if altered)
unknown's avatar
unknown committed
3601 3602 3603 3604 3605
 */

int ha_ndbcluster::external_lock(THD *thd, int lock_type)
{
  int error=0;
3606
  NdbTransaction* trans= NULL;
unknown's avatar
unknown committed
3607 3608 3609 3610 3611 3612

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

3616
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
3617
  Ndb *ndb= thd_ndb->ndb;
3618

3619 3620
  DBUG_PRINT("enter", ("thd: %x, thd_ndb: %x, thd_ndb->lock_count: %d",
                       thd, thd_ndb, thd_ndb->lock_count));
3621

unknown's avatar
unknown committed
3622 3623
  if (lock_type != F_UNLCK)
  {
3624
    DBUG_PRINT("info", ("lock_type != F_UNLCK"));
3625 3626 3627 3628 3629 3630 3631 3632
    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)
3633 3634 3635
      m_transaction_on= FALSE;
    else
      m_transaction_on= thd->variables.ndb_use_transactions;
3636
    if (!thd_ndb->lock_count++)
unknown's avatar
unknown committed
3637 3638
    {
      PRINT_OPTION_FLAGS(thd);
3639
      if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) 
unknown's avatar
unknown committed
3640 3641
      {
        // Autocommit transaction
3642
        DBUG_ASSERT(!thd_ndb->stmt);
unknown's avatar
unknown committed
3643 3644
        DBUG_PRINT("trans",("Starting transaction stmt"));      

3645
        trans= ndb->startTransaction();
unknown's avatar
unknown committed
3646
        if (trans == NULL)
3647
          ERR_RETURN(ndb->getNdbError());
3648
        no_uncommitted_rows_reset(thd);
3649
        thd_ndb->stmt= trans;
3650
	thd_ndb->query_state&= NDB_QUERY_NORMAL;
3651
        trans_register_ha(thd, FALSE, &ndbcluster_hton);
unknown's avatar
unknown committed
3652 3653 3654
      } 
      else 
      { 
3655
        if (!thd_ndb->all)
3656
        {
unknown's avatar
unknown committed
3657 3658 3659 3660
          // Not autocommit transaction
          // A "master" transaction ha not been started yet
          DBUG_PRINT("trans",("starting transaction, all"));
          
3661
          trans= ndb->startTransaction();
unknown's avatar
unknown committed
3662
          if (trans == NULL)
3663
            ERR_RETURN(ndb->getNdbError());
3664
          no_uncommitted_rows_reset(thd);
3665
          thd_ndb->all= trans; 
3666
	  thd_ndb->query_state&= NDB_QUERY_NORMAL;
3667
          trans_register_ha(thd, TRUE, &ndbcluster_hton);
unknown's avatar
unknown committed
3668 3669 3670 3671 3672 3673 3674 3675

          /*
            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))
3676
          {
unknown's avatar
unknown committed
3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695
            //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. 
     */

3696 3697 3698
    // 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;
3699 3700
    m_autoincrement_prefetch= 
      (ha_rows) thd->variables.ndb_autoincrement_prefetch_sz;
3701

3702
    m_active_trans= thd_ndb->all ? thd_ndb->all : thd_ndb->stmt;
unknown's avatar
unknown committed
3703
    DBUG_ASSERT(m_active_trans);
3704
    // Start of transaction
3705
    m_rows_changed= 0;
3706
    m_retrieve_all_fields= FALSE;
3707
    m_retrieve_primary_key= FALSE;
3708
    m_ops_pending= 0;
3709
    {
3710
      NDBDICT *dict= ndb->getDictionary();
3711 3712 3713
      const NDBTAB *tab;
      void *tab_info;
      if (!(tab= dict->getTable(m_tabname, &tab_info)))
3714
        ERR_RETURN(dict->getNdbError());
3715 3716 3717
      DBUG_PRINT("info", ("Table schema version: %d", 
                          tab->getObjectVersion()));
      // Check if thread has stale local cache
3718 3719 3720 3721
      // New transaction must not use old tables... (trans != 0)
      // Running might...
      if ((trans && tab->getObjectStatus() != NdbDictionary::Object::Retrieved)
	  || tab->getObjectStatus() == NdbDictionary::Object::Invalid)
3722 3723
      {
        invalidate_dictionary_cache(FALSE);
3724
        if (!(tab= dict->getTable(m_tabname, &tab_info)))
3725 3726 3727 3728
          ERR_RETURN(dict->getNdbError());
        DBUG_PRINT("info", ("Table schema version: %d", 
                            tab->getObjectVersion()));
      }
3729
      if (m_table_version < tab->getObjectVersion())
3730 3731 3732 3733 3734 3735 3736
      {
        /*
          The table has been altered, caller has to retry
        */
        NdbError err= ndb->getNdbError(NDB_INVALID_SCHEMA_OBJECT);
        DBUG_RETURN(ndb_to_mysql_error(&err));
      }
3737 3738 3739 3740
      if (m_table != (void *)tab)
      {
        m_table= (void *)tab;
        m_table_version = tab->getObjectVersion();
3741
        if ((my_errno= build_index_list(ndb, table, ILBP_OPEN)))
3742
          DBUG_RETURN(my_errno);
3743 3744 3745

        const void *data, *pack_data;
        uint length, pack_length;
unknown's avatar
unknown committed
3746
        if (readfrm(table->s->path, &data, &length) ||
3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757
            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));
3758
      }
3759 3760
      m_table_info= tab_info;
    }
3761
    no_uncommitted_rows_init(thd);
3762 3763
  }
  else
unknown's avatar
unknown committed
3764
  {
3765
    DBUG_PRINT("info", ("lock_type == F_UNLCK"));
3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783

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

3784
    if (!--thd_ndb->lock_count)
unknown's avatar
unknown committed
3785 3786 3787 3788
    {
      DBUG_PRINT("trans", ("Last external_lock"));
      PRINT_OPTION_FLAGS(thd);

3789
      if (thd_ndb->stmt)
unknown's avatar
unknown committed
3790 3791 3792 3793 3794 3795 3796
      {
        /*
          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"));
3797
        ndb->closeTransaction(m_active_trans);
3798
        thd_ndb->stmt= NULL;
unknown's avatar
unknown committed
3799 3800
      }
    }
unknown's avatar
unknown committed
3801
    m_table_info= NULL;
3802

3803 3804 3805 3806 3807 3808 3809 3810 3811
    /*
      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;    

3812 3813
    if (m_active_cursor)
      DBUG_PRINT("warning", ("m_active_cursor != NULL"));
3814 3815
    m_active_cursor= NULL;

unknown's avatar
unknown committed
3816 3817 3818 3819
    if (m_multi_cursor)
      DBUG_PRINT("warning", ("m_multi_cursor != NULL"));
    m_multi_cursor= NULL;
    
3820
    if (m_blobs_pending)
3821
      DBUG_PRINT("warning", ("blobs_pending != 0"));
3822
    m_blobs_pending= 0;
3823
    
3824
    if (m_ops_pending)
3825
      DBUG_PRINT("warning", ("ops_pending != 0L"));
3826
    m_ops_pending= 0;
unknown's avatar
unknown committed
3827 3828 3829 3830
  }
  DBUG_RETURN(error);
}

3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846
/*
  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;
}

unknown's avatar
unknown committed
3847
/*
3848 3849 3850 3851 3852
  Start a transaction for running a statement if one is not
  already running in a transaction. This will be the case in
  a BEGIN; COMMIT; block
  When using LOCK TABLE's external_lock will start a transaction
  since ndb does not currently does not support table locking
unknown's avatar
unknown committed
3853 3854
*/

unknown's avatar
unknown committed
3855
int ha_ndbcluster::start_stmt(THD *thd, thr_lock_type lock_type)
unknown's avatar
unknown committed
3856 3857 3858 3859 3860
{
  int error=0;
  DBUG_ENTER("start_stmt");
  PRINT_OPTION_FLAGS(thd);

3861
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
3862
  NdbTransaction *trans= (thd_ndb->stmt)?thd_ndb->stmt:thd_ndb->all;
unknown's avatar
unknown committed
3863
  if (!trans){
3864
    Ndb *ndb= thd_ndb->ndb;
unknown's avatar
unknown committed
3865
    DBUG_PRINT("trans",("Starting transaction stmt"));  
3866
    trans= ndb->startTransaction();
unknown's avatar
unknown committed
3867
    if (trans == NULL)
3868
      ERR_RETURN(ndb->getNdbError());
3869
    no_uncommitted_rows_reset(thd);
3870 3871
    thd_ndb->stmt= trans;
    trans_register_ha(thd, FALSE, &ndbcluster_hton);
unknown's avatar
unknown committed
3872
  }
3873
  thd_ndb->query_state&= NDB_QUERY_NORMAL;
unknown's avatar
unknown committed
3874
  m_active_trans= trans;
3875

3876
  // Start of statement
3877
  m_retrieve_all_fields= FALSE;
3878
  m_retrieve_primary_key= FALSE;
3879
  m_ops_pending= 0;    
unknown's avatar
unknown committed
3880 3881 3882 3883 3884 3885
  
  DBUG_RETURN(error);
}


/*
3886
  Commit a transaction started in NDB
unknown's avatar
unknown committed
3887 3888
 */

3889
int ndbcluster_commit(THD *thd, bool all)
unknown's avatar
unknown committed
3890 3891
{
  int res= 0;
3892 3893 3894
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  Ndb *ndb= thd_ndb->ndb;
  NdbTransaction *trans= all ? thd_ndb->all : thd_ndb->stmt;
unknown's avatar
unknown committed
3895 3896 3897

  DBUG_ENTER("ndbcluster_commit");
  DBUG_PRINT("transaction",("%s",
3898
                            trans == thd_ndb->stmt ?
unknown's avatar
unknown committed
3899 3900 3901
                            "stmt" : "all"));
  DBUG_ASSERT(ndb && trans);

3902
  if (execute_commit(thd,trans) != 0)
unknown's avatar
unknown committed
3903 3904
  {
    const NdbError err= trans->getNdbError();
3905
    const NdbOperation *error_op= trans->getNdbErrorOperation();
3906
    ERR_PRINT(err);
unknown's avatar
unknown committed
3907
    res= ndb_to_mysql_error(&err);
3908
    if (res != -1)
3909
      ndbcluster_print_error(res, error_op);
unknown's avatar
unknown committed
3910
  }
3911
  ndb->closeTransaction(trans);
3912

unknown's avatar
unknown committed
3913
  if (all)
3914 3915 3916
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;
3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930

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

unknown's avatar
unknown committed
3931 3932 3933 3934 3935 3936 3937 3938
  DBUG_RETURN(res);
}


/*
  Rollback a transaction started in NDB
 */

3939
int ndbcluster_rollback(THD *thd, bool all)
unknown's avatar
unknown committed
3940 3941
{
  int res= 0;
3942 3943 3944
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  Ndb *ndb= thd_ndb->ndb;
  NdbTransaction *trans= all ? thd_ndb->all : thd_ndb->stmt;
unknown's avatar
unknown committed
3945 3946 3947

  DBUG_ENTER("ndbcluster_rollback");
  DBUG_PRINT("transaction",("%s",
3948
                            trans == thd_ndb->stmt ? 
unknown's avatar
unknown committed
3949 3950 3951
                            "stmt" : "all"));
  DBUG_ASSERT(ndb && trans);

3952
  if (trans->execute(NdbTransaction::Rollback) != 0)
unknown's avatar
unknown committed
3953 3954
  {
    const NdbError err= trans->getNdbError();
3955
    const NdbOperation *error_op= trans->getNdbErrorOperation();
unknown's avatar
unknown committed
3956 3957
    ERR_PRINT(err);     
    res= ndb_to_mysql_error(&err);
3958 3959
    if (res != -1) 
      ndbcluster_print_error(res, error_op);
unknown's avatar
unknown committed
3960 3961
  }
  ndb->closeTransaction(trans);
3962

unknown's avatar
unknown committed
3963
  if (all)
3964 3965 3966 3967
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;

3968 3969 3970
  /* Clear list of tables changed by transaction */
  thd_ndb->changed_tables.empty();

3971
  DBUG_RETURN(res);
unknown's avatar
unknown committed
3972 3973 3974 3975
}


/*
unknown's avatar
unknown committed
3976 3977 3978
  Define NDB column based on Field.
  Returns 0 or mysql error code.
  Not member of ha_ndbcluster because NDBCOL cannot be declared.
unknown's avatar
unknown committed
3979 3980 3981

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

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

/*
  Create a table in NDB Cluster
 */

unknown's avatar
Merge  
unknown committed
4258 4259
static void ndb_set_fragmentation(NDBTAB &tab, TABLE *form, uint pk_length)
{
4260 4261 4262 4263 4264
  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 */
unknown's avatar
Merge  
unknown committed
4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281
    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;
4282 4283 4284 4285 4286
    /*
     * 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;
unknown's avatar
Merge  
unknown committed
4287
#if MYSQL_VERSION_ID >= 50100
4288
    no_fragments= (big_max_rows*acc_row_size)/acc_fragment_size+1;
unknown's avatar
Merge  
unknown committed
4289
#else
4290
    no_fragments= ((big_max_rows*acc_row_size)/acc_fragment_size+1
4291
                   +1/*correct rounding*/)/2;
unknown's avatar
Merge  
unknown committed
4292 4293 4294 4295 4296 4297 4298 4299 4300
#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)
4301 4302
        push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
                     "Ndb might have problems storing the max amount of rows specified");
unknown's avatar
Merge  
unknown committed
4303 4304 4305 4306 4307 4308 4309
    }
    else if (no_fragments > no_nodes)
      ftype= NDBTAB::FragAllMedium;
    else
      ftype= NDBTAB::FragAllSmall;
    tab.setFragmentType(ftype);
  }
4310 4311
  tab.setMaxRows(max_rows);
  tab.setMinRows(min_rows);
unknown's avatar
Merge  
unknown committed
4312 4313
}

unknown's avatar
unknown committed
4314
int ha_ndbcluster::create(const char *name, 
4315 4316
                          TABLE *form, 
                          HA_CREATE_INFO *info)
unknown's avatar
unknown committed
4317 4318 4319
{
  NDBTAB tab;
  NDBCOL col;
unknown's avatar
unknown committed
4320
  uint pack_length, length, i, pk_length= 0;
unknown's avatar
unknown committed
4321 4322
  const void *data, *pack_data;
  char name2[FN_HEADLEN];
4323
  bool create_from_engine= (info->table_options & HA_OPTION_CREATE_FROM_ENGINE);
4324

unknown's avatar
unknown committed
4325
  DBUG_ENTER("ha_ndbcluster::create");
unknown's avatar
unknown committed
4326 4327 4328
  DBUG_PRINT("enter", ("name: %s", name));
  fn_format(name2, name, "", "",2);       // Remove the .frm extension
  set_dbname(name2);
4329 4330
  set_tabname(name2);    

4331 4332 4333 4334 4335 4336
  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);
  }
4337 4338 4339 4340 4341 4342 4343 4344 4345 4346
  if (create_from_engine)
  {
    /*
      Table alreay exists in NDB and frm file has been created by 
      caller.
      Do Ndb specific stuff, such as create a .ndb file
    */
    my_errno= write_ndb_file();
    DBUG_RETURN(my_errno);
  }
unknown's avatar
unknown committed
4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362

  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));
  
4363
  for (i= 0; i < form->s->fields; i++) 
unknown's avatar
unknown committed
4364 4365 4366 4367
  {
    Field *field= form->field[i];
    DBUG_PRINT("info", ("name: %s, type: %u, pack_length: %d", 
                        field->field_name, field->real_type(),
4368
                        field->pack_length()));
4369
    if ((my_errno= create_ndb_column(col, field, info)))
unknown's avatar
unknown committed
4370
      DBUG_RETURN(my_errno);
unknown's avatar
unknown committed
4371
    tab.addColumn(col);
unknown's avatar
unknown committed
4372
    if (col.getPrimaryKey())
unknown's avatar
unknown committed
4373
      pk_length += (field->pack_length() + 3) / 4;
unknown's avatar
unknown committed
4374 4375 4376
  }
  
  // No primary key, create shadow key as 64 bit, auto increment  
4377
  if (form->s->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
4378 4379 4380 4381 4382
  {
    DBUG_PRINT("info", ("Generating shadow key"));
    col.setName("$PK");
    col.setType(NdbDictionary::Column::Bigunsigned);
    col.setLength(1);
unknown's avatar
unknown committed
4383
    col.setNullable(FALSE);
unknown's avatar
unknown committed
4384 4385 4386
    col.setPrimaryKey(TRUE);
    col.setAutoIncrement(TRUE);
    tab.addColumn(col);
unknown's avatar
unknown committed
4387 4388 4389 4390
    pk_length += 2;
  }
  
  // Make sure that blob tables don't have to big part size
4391
  for (i= 0; i < form->s->fields; i++) 
unknown's avatar
unknown committed
4392 4393 4394 4395 4396 4397 4398
  {
    /**
     * 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()) {
4399
    case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
4400 4401 4402 4403
    case MYSQL_TYPE_BLOB:    
    case MYSQL_TYPE_MEDIUM_BLOB:   
    case MYSQL_TYPE_LONG_BLOB: 
    {
4404 4405
      NdbDictionary::Column * col= tab.getColumn(i);
      int size= pk_length + (col->getPartSize()+3)/4 + 7;
unknown's avatar
unknown committed
4406
      if (size > NDB_MAX_TUPLE_SIZE_IN_WORDS && 
4407
         (pk_length+7) < NDB_MAX_TUPLE_SIZE_IN_WORDS)
unknown's avatar
unknown committed
4408
      {
4409 4410
        size= NDB_MAX_TUPLE_SIZE_IN_WORDS - pk_length - 7;
        col->setPartSize(4*size);
unknown's avatar
unknown committed
4411 4412 4413 4414 4415 4416 4417 4418 4419 4420
      }
      /**
       * If size > NDB_MAX and pk_length+7 >= NDB_MAX
       *   then the table can't be created anyway, so skip
       *   changing part size, and have error later
       */ 
    }
    default:
      break;
    }
unknown's avatar
unknown committed
4421
  }
unknown's avatar
Merge  
unknown committed
4422 4423 4424

  ndb_set_fragmentation(tab, form, pk_length);

4425
  if ((my_errno= check_ndb_connection()))
unknown's avatar
unknown committed
4426 4427 4428
    DBUG_RETURN(my_errno);
  
  // Create the table in NDB     
4429 4430
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
4431
  if (dict->createTable(tab) != 0) 
unknown's avatar
unknown committed
4432 4433 4434 4435 4436 4437 4438 4439
  {
    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));
4440

unknown's avatar
unknown committed
4441
  // Create secondary indexes
4442
  my_errno= build_index_list(ndb, form, ILBP_CREATE);
4443

4444 4445 4446
  if (!my_errno)
    my_errno= write_ndb_file();

unknown's avatar
unknown committed
4447 4448 4449 4450
  DBUG_RETURN(my_errno);
}


4451
int ha_ndbcluster::create_ordered_index(const char *name, 
4452
                                        KEY *key_info)
4453
{
4454
  DBUG_ENTER("ha_ndbcluster::create_ordered_index");
unknown's avatar
unknown committed
4455
  DBUG_RETURN(create_index(name, key_info, FALSE));
4456 4457 4458
}

int ha_ndbcluster::create_unique_index(const char *name, 
4459
                                       KEY *key_info)
4460 4461
{

4462
  DBUG_ENTER("ha_ndbcluster::create_unique_index");
unknown's avatar
unknown committed
4463
  DBUG_RETURN(create_index(name, key_info, TRUE));
4464 4465 4466
}


unknown's avatar
unknown committed
4467 4468 4469 4470 4471
/*
  Create an index in NDB Cluster
 */

int ha_ndbcluster::create_index(const char *name, 
4472 4473
                                KEY *key_info,
                                bool unique)
4474
{
4475 4476
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
unknown's avatar
unknown committed
4477 4478 4479
  KEY_PART_INFO *key_part= key_info->key_part;
  KEY_PART_INFO *end= key_part + key_info->key_parts;
  
4480
  DBUG_ENTER("ha_ndbcluster::create_index");
unknown's avatar
unknown committed
4481
  DBUG_PRINT("enter", ("name: %s ", name));
4482

unknown's avatar
unknown committed
4483
  NdbDictionary::Index ndb_index(name);
4484
  if (unique)
unknown's avatar
unknown committed
4485 4486 4487 4488 4489
    ndb_index.setType(NdbDictionary::Index::UniqueHashIndex);
  else 
  {
    ndb_index.setType(NdbDictionary::Index::OrderedIndex);
    // TODO Only temporary ordered indexes supported
unknown's avatar
unknown committed
4490
    ndb_index.setLogging(FALSE); 
unknown's avatar
unknown committed
4491 4492 4493 4494 4495 4496 4497
  }
  ndb_index.setTable(m_tabname);

  for (; key_part != end; key_part++) 
  {
    Field *field= key_part->field;
    DBUG_PRINT("info", ("attr: %s", field->field_name));
unknown's avatar
unknown committed
4498
    ndb_index.addColumnName(field->field_name);
unknown's avatar
unknown committed
4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514
  }
  
  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)
{
4515
  NDBDICT *dict;
unknown's avatar
unknown committed
4516
  char new_tabname[FN_HEADLEN];
4517
  char new_dbname[FN_HEADLEN];
4518 4519
  const NDBTAB *orig_tab;
  int result;
4520 4521
  bool recreate_indexes= FALSE;
  NDBDICT::List index_list;
unknown's avatar
unknown committed
4522 4523

  DBUG_ENTER("ha_ndbcluster::rename_table");
4524
  DBUG_PRINT("info", ("Renaming %s to %s", from, to));
unknown's avatar
unknown committed
4525
  set_dbname(from);
4526
  set_dbname(to, new_dbname);
unknown's avatar
unknown committed
4527 4528 4529
  set_tabname(from);
  set_tabname(to, new_tabname);

4530 4531 4532
  if (check_ndb_connection())
    DBUG_RETURN(my_errno= HA_ERR_NO_CONNECTION);

unknown's avatar
unknown committed
4533 4534
  Ndb *ndb= get_ndb();
  dict= ndb->getDictionary();
4535 4536
  if (!(orig_tab= dict->getTable(m_tabname)))
    ERR_RETURN(dict->getNdbError());
4537 4538 4539 4540 4541 4542 4543
  // 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());
  }
4544 4545 4546 4547 4548 4549
  if (my_strcasecmp(system_charset_info, new_dbname, m_dbname))
  {
    dict->listIndexes(index_list, m_tabname);
    recreate_indexes= TRUE;
  }

4550 4551 4552
  m_table= (void *)orig_tab;
  // Change current database to that of target table
  set_dbname(to);
unknown's avatar
unknown committed
4553
  ndb->setDatabaseName(m_dbname);
4554
  if (!(result= alter_table_name(new_tabname)))
4555
  {
4556 4557
    // Rename .ndb file
    result= handler::rename_table(from, to);
4558
  }
4559

4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587
  // 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);
    }
  }

unknown's avatar
unknown committed
4588 4589 4590 4591 4592 4593 4594 4595
  DBUG_RETURN(result);
}


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

4596
int ha_ndbcluster::alter_table_name(const char *to)
unknown's avatar
unknown committed
4597
{
4598 4599
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
4600
  const NDBTAB *orig_tab= (const NDBTAB *) m_table;
unknown's avatar
unknown committed
4601 4602
  DBUG_ENTER("alter_table_name_table");

unknown's avatar
unknown committed
4603
  NdbDictionary::Table new_tab= *orig_tab;
4604 4605
  new_tab.setName(to);
  if (dict->alterTable(new_tab) != 0)
unknown's avatar
unknown committed
4606 4607 4608
    ERR_RETURN(dict->getNdbError());

  m_table= NULL;
unknown's avatar
unknown committed
4609
  m_table_info= NULL;
unknown's avatar
unknown committed
4610 4611 4612 4613 4614 4615
                                                                             
  DBUG_RETURN(0);
}


/*
4616 4617
  Delete table from NDB Cluster

unknown's avatar
unknown committed
4618 4619 4620 4621
 */

int ha_ndbcluster::delete_table(const char *name)
{
4622
  DBUG_ENTER("ha_ndbcluster::delete_table");
unknown's avatar
unknown committed
4623 4624 4625
  DBUG_PRINT("enter", ("name: %s", name));
  set_dbname(name);
  set_tabname(name);
4626

unknown's avatar
unknown committed
4627 4628
  if (check_ndb_connection())
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4629 4630

  /* Call ancestor function to delete .ndb file */
4631
  handler::delete_table(name);
4632 4633
  
  /* Drop the table from NDB */
unknown's avatar
unknown committed
4634 4635 4636 4637 4638
  DBUG_RETURN(drop_table());
}


/*
4639
  Drop table in NDB Cluster
unknown's avatar
unknown committed
4640 4641 4642 4643
 */

int ha_ndbcluster::drop_table()
{
4644
  THD *thd= current_thd;
4645 4646
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
4647

unknown's avatar
unknown committed
4648 4649 4650 4651
  DBUG_ENTER("drop_table");
  DBUG_PRINT("enter", ("Deleting %s", m_tabname));
  
  release_metadata();
4652
  while (dict->dropTable(m_tabname)) 
unknown's avatar
unknown committed
4653 4654
  {
    const NdbError err= dict->getNdbError();
4655 4656 4657 4658 4659 4660 4661 4662 4663
    switch (err.status)
    {
      case NdbError::TemporaryError:
        if (!thd->killed)
          continue; // retry indefinitly
        break;
      default:
        break;
    }
4664
    ERR_RETURN(dict->getNdbError());
4665 4666
  }

unknown's avatar
unknown committed
4667 4668 4669 4670
  DBUG_RETURN(0);
}


4671
ulonglong ha_ndbcluster::get_auto_increment()
4672
{  
4673 4674
  int cache_size;
  Uint64 auto_value;
unknown's avatar
unknown committed
4675 4676
  DBUG_ENTER("get_auto_increment");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));
4677
  Ndb *ndb= get_ndb();
4678
   
4679
  if (m_rows_inserted > m_rows_to_insert)
unknown's avatar
unknown committed
4680
  {
4681 4682
    /* We guessed too low */
    m_rows_to_insert+= m_autoincrement_prefetch;
unknown's avatar
unknown committed
4683
  }
unknown's avatar
unknown committed
4684
  cache_size= 
unknown's avatar
unknown committed
4685 4686 4687 4688
    (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));
4689
  int ret;
4690 4691
  uint retries= NDB_AUTO_INCREMENT_RETRIES;
  do {
4692 4693 4694 4695 4696
    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 && 
4697 4698
           --retries &&
           ndb->getNdbError().status == NdbError::TemporaryError);
4699
  if (ret == -1)
4700 4701 4702 4703 4704 4705
  {
    const NdbError err= ndb->getNdbError();
    sql_print_error("Error %lu in ::get_auto_increment(): %s",
                    (ulong) err.code, err.message);
    DBUG_RETURN(~(ulonglong) 0);
  }
unknown's avatar
unknown committed
4706
  DBUG_RETURN((longlong)auto_value);
unknown's avatar
unknown committed
4707 4708 4709 4710 4711 4712 4713 4714
}


/*
  Constructor for the NDB Cluster table handler 
 */

ha_ndbcluster::ha_ndbcluster(TABLE *table_arg):
4715
  handler(&ndbcluster_hton, table_arg),
unknown's avatar
unknown committed
4716 4717 4718
  m_active_trans(NULL),
  m_active_cursor(NULL),
  m_table(NULL),
4719
  m_table_version(-1),
4720
  m_table_info(NULL),
unknown's avatar
unknown committed
4721
  m_table_flags(HA_REC_NOT_IN_SEQ |
4722 4723 4724 4725
                HA_NULL_IN_KEY |
                HA_AUTO_PART_KEY |
                HA_NO_PREFIX_CHAR_KEYS |
                HA_NEED_READ_RANGE_BUFFER |
4726
                HA_CAN_GEOMETRY |
4727 4728
                HA_CAN_BIT_FIELD |
                HA_PARTIAL_COLUMN_READ),
4729
  m_share(0),
unknown's avatar
unknown committed
4730
  m_use_write(FALSE),
4731
  m_ignore_dup_key(FALSE),
4732
  m_has_unique_index(FALSE),
4733 4734
  m_primary_key_update(FALSE),
  m_retrieve_all_fields(FALSE),
4735
  m_retrieve_primary_key(FALSE),
4736 4737 4738
  m_rows_to_insert((ha_rows) 1),
  m_rows_inserted((ha_rows) 0),
  m_bulk_insert_rows((ha_rows) 1024),
unknown's avatar
Merge  
unknown committed
4739
  m_rows_changed((ha_rows) 0),
4740 4741 4742 4743
  m_bulk_insert_not_flushed(FALSE),
  m_ops_pending(0),
  m_skip_auto_increment(TRUE),
  m_blobs_pending(0),
4744
  m_blobs_offset(0),
4745 4746
  m_blobs_buffer(0),
  m_blobs_buffer_size(0),
4747 4748 4749
  m_dupkey((uint) -1),
  m_ha_not_exact_count(FALSE),
  m_force_send(TRUE),
4750
  m_autoincrement_prefetch((ha_rows) 32),
unknown's avatar
unknown committed
4751
  m_transaction_on(TRUE),
unknown's avatar
unknown committed
4752 4753
  m_cond_stack(NULL),
  m_multi_cursor(NULL)
4754
{
4755
  int i;
4756
 
unknown's avatar
unknown committed
4757 4758 4759 4760 4761
  DBUG_ENTER("ha_ndbcluster");

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

4762
  records= ~(ha_rows)0; // uninitialized
unknown's avatar
unknown committed
4763 4764
  block_size= 1024;

4765 4766
  for (i= 0; i < MAX_KEY; i++)
  {
4767 4768 4769 4770
    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;
4771 4772
  }

unknown's avatar
unknown committed
4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784
  DBUG_VOID_RETURN;
}


/*
  Destructor for NDB Cluster table handler
 */

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

4785 4786
  if (m_share)
    free_share(m_share);
unknown's avatar
unknown committed
4787
  release_metadata();
4788 4789
  my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
  m_blobs_buffer= 0;
unknown's avatar
unknown committed
4790 4791

  // Check for open cursor/transaction
4792 4793
  if (m_active_cursor) {
  }
unknown's avatar
unknown committed
4794
  DBUG_ASSERT(m_active_cursor == NULL);
4795 4796
  if (m_active_trans) {
  }
unknown's avatar
unknown committed
4797 4798
  DBUG_ASSERT(m_active_trans == NULL);

4799 4800 4801 4802
  // Discard the condition stack
  DBUG_PRINT("info", ("Clearing condition stack"));
  cond_clear();

unknown's avatar
unknown committed
4803 4804 4805 4806
  DBUG_VOID_RETURN;
}


unknown's avatar
Merge  
unknown committed
4807

unknown's avatar
unknown committed
4808 4809 4810 4811 4812 4813 4814 4815
/*
  Open a table for further use
  - fetch metadata for this table from NDB
  - check that table exists
*/

int ha_ndbcluster::open(const char *name, int mode, uint test_if_locked)
{
unknown's avatar
unknown committed
4816
  int res;
unknown's avatar
unknown committed
4817 4818 4819 4820 4821 4822 4823 4824
  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
  
4825
  if (table->s->primary_key != MAX_KEY) 
unknown's avatar
unknown committed
4826
  {
4827
    key= table->key_info+table->s->primary_key;
unknown's avatar
unknown committed
4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838
    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);
  
4839 4840
  if (check_ndb_connection()) {
    free_share(m_share); m_share= 0;
unknown's avatar
unknown committed
4841
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4842
  }
4843
  
unknown's avatar
unknown committed
4844 4845
  res= get_metadata(name);
  if (!res)
4846 4847 4848
  {
    Ndb *ndb= get_ndb();
    ndb->setDatabaseName(m_dbname);
unknown's avatar
unknown committed
4849 4850 4851
    struct Ndb_statistics stat;
    res= ndb_get_table_statistics(NULL, false, ndb, m_tabname, &stat);
    records= stat.row_count;
4852 4853 4854
    if(!res)
      res= info(HA_STATUS_CONST);
  }
unknown's avatar
unknown committed
4855

unknown's avatar
unknown committed
4856
  DBUG_RETURN(res);
unknown's avatar
unknown committed
4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867
}


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

int ha_ndbcluster::close(void)
{
  DBUG_ENTER("close");  
4868
  free_share(m_share); m_share= 0;
unknown's avatar
unknown committed
4869 4870 4871 4872 4873
  release_metadata();
  DBUG_RETURN(0);
}


4874
Thd_ndb* ha_ndbcluster::seize_thd_ndb()
unknown's avatar
unknown committed
4875
{
4876 4877
  Thd_ndb *thd_ndb;
  DBUG_ENTER("seize_thd_ndb");
unknown's avatar
unknown committed
4878

4879
  thd_ndb= new Thd_ndb();
4880 4881 4882
  thd_ndb->ndb->getDictionary()->set_local_table_data_size(
    sizeof(Ndb_local_table_statistics)
    );
4883
  if (thd_ndb->ndb->init(max_transactions) != 0)
unknown's avatar
unknown committed
4884
  {
4885
    ERR_PRINT(thd_ndb->ndb->getNdbError());
unknown's avatar
unknown committed
4886 4887 4888 4889 4890 4891
    /*
      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 
    */
4892 4893
    delete thd_ndb;
    thd_ndb= NULL;
unknown's avatar
unknown committed
4894
  }
4895
  DBUG_RETURN(thd_ndb);
unknown's avatar
unknown committed
4896 4897 4898
}


4899
void ha_ndbcluster::release_thd_ndb(Thd_ndb* thd_ndb)
unknown's avatar
unknown committed
4900
{
4901 4902
  DBUG_ENTER("release_thd_ndb");
  delete thd_ndb;
unknown's avatar
unknown committed
4903 4904 4905 4906 4907
  DBUG_VOID_RETURN;
}


/*
unknown's avatar
unknown committed
4908
  If this thread already has a Thd_ndb object allocated
unknown's avatar
unknown committed
4909
  in current THD, reuse it. Otherwise
unknown's avatar
unknown committed
4910
  seize a Thd_ndb object, assign it to current THD and use it.
unknown's avatar
unknown committed
4911 4912 4913
 
*/

4914
Ndb* check_ndb_in_thd(THD* thd)
unknown's avatar
unknown committed
4915
{
4916
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
4917
  if (!thd_ndb)
unknown's avatar
unknown committed
4918
  {
unknown's avatar
unknown committed
4919
    if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
unknown's avatar
Merge  
unknown committed
4920
      return NULL;
4921
    set_thd_ndb(thd, thd_ndb);
unknown's avatar
unknown committed
4922
  }
unknown's avatar
Merge  
unknown committed
4923
  return thd_ndb->ndb;
4924 4925
}

unknown's avatar
unknown committed
4926

4927

4928
int ha_ndbcluster::check_ndb_connection(THD* thd)
unknown's avatar
unknown committed
4929
{
4930
  Ndb *ndb;
unknown's avatar
unknown committed
4931 4932
  DBUG_ENTER("check_ndb_connection");
  
4933
  if (!(ndb= check_ndb_in_thd(thd)))
4934
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4935
  ndb->setDatabaseName(m_dbname);
unknown's avatar
unknown committed
4936 4937 4938
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
4939

4940
int ndbcluster_close_connection(THD *thd)
unknown's avatar
unknown committed
4941
{
4942
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
unknown's avatar
unknown committed
4943
  DBUG_ENTER("ndbcluster_close_connection");
4944 4945
  if (thd_ndb)
  {
4946
    ha_ndbcluster::release_thd_ndb(thd_ndb);
4947
    set_thd_ndb(thd, NULL); // not strictly required but does not hurt either
4948
  }
4949
  DBUG_RETURN(0);
unknown's avatar
unknown committed
4950 4951 4952 4953 4954 4955 4956
}


/*
  Try to discover one table from NDB
 */

4957
int ndbcluster_discover(THD* thd, const char *db, const char *name,
4958
                        const void** frmblob, uint* frmlen)
unknown's avatar
unknown committed
4959 4960 4961 4962
{
  uint len;
  const void* data;
  const NDBTAB* tab;
4963
  Ndb* ndb;
unknown's avatar
unknown committed
4964
  DBUG_ENTER("ndbcluster_discover");
4965
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name)); 
unknown's avatar
unknown committed
4966

4967 4968 4969
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(HA_ERR_NO_CONNECTION);  
  ndb->setDatabaseName(db);
4970

4971
  NDBDICT* dict= ndb->getDictionary();
4972
  dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
4973 4974 4975 4976 4977
  dict->invalidateTable(name);
  if (!(tab= dict->getTable(name)))
  {    
    const NdbError err= dict->getNdbError();
    if (err.code == 709)
4978
      DBUG_RETURN(-1);
4979
    ERR_RETURN(err);
unknown's avatar
unknown committed
4980 4981 4982 4983 4984 4985
  }
  DBUG_PRINT("info", ("Found table %s", tab->getName()));
  
  len= tab->getFrmLength();  
  if (len == 0 || tab->getFrmData() == NULL)
  {
4986 4987
    DBUG_PRINT("error", ("No frm data found."));
    DBUG_RETURN(1);
unknown's avatar
unknown committed
4988 4989 4990
  }
  
  if (unpackfrm(&data, &len, tab->getFrmData()))
4991 4992 4993 4994
  {
    DBUG_PRINT("error", ("Could not unpack table"));
    DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
4995 4996 4997 4998 4999 5000 5001 5002

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

/*
5003
  Check if a table exists in NDB
5004

5005
 */
unknown's avatar
unknown committed
5006

5007
int ndbcluster_table_exists_in_engine(THD* thd, const char *db, const char *name)
5008 5009 5010
{
  const NDBTAB* tab;
  Ndb* ndb;
5011
  DBUG_ENTER("ndbcluster_table_exists_in_engine");
5012
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
5013 5014

  if (!(ndb= check_ndb_in_thd(thd)))
5015
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
5016 5017 5018
  ndb->setDatabaseName(db);

  NDBDICT* dict= ndb->getDictionary();
5019
  dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
5020 5021
  dict->invalidateTable(name);
  if (!(tab= dict->getTable(name)))
5022
  {
5023 5024 5025 5026 5027
    const NdbError err= dict->getNdbError();
    if (err.code == 709)
      DBUG_RETURN(0);
    ERR_RETURN(err);
  }
5028

5029 5030 5031 5032
  DBUG_PRINT("info", ("Found table %s", tab->getName()));
  DBUG_RETURN(1);
}

unknown's avatar
unknown committed
5033 5034


unknown's avatar
unknown committed
5035
extern "C" byte* tables_get_key(const char *entry, uint *length,
5036
                                my_bool not_used __attribute__((unused)))
5037 5038 5039 5040 5041 5042
{
  *length= strlen(entry);
  return (byte*) entry;
}


5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056
/*
  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;
5057
  int ret= 0;
5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083
  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++))
5084
  {
5085
    while (dict->dropTable(tabname))
5086 5087
    {
      const NdbError err= dict->getNdbError();
5088 5089 5090 5091 5092 5093 5094 5095 5096 5097
      switch (err.status)
      {
        case NdbError::TemporaryError:
          if (!thd->killed)
            continue; // retry indefinitly
          break;
        default:
          break;
      }
      if (err.code != 709) // 709: No such table existed
5098 5099
      {
        ERR_PRINT(err);
5100
        ret= ndb_to_mysql_error(&err);
5101
      }
5102
      break;
5103 5104 5105
    }
  }
  DBUG_RETURN(ret);      
5106 5107 5108
}


5109
int ndbcluster_find_files(THD *thd,const char *db,const char *path,
5110
                          const char *wild, bool dir, List<char> *files)
unknown's avatar
unknown committed
5111
{
5112 5113 5114
  DBUG_ENTER("ndbcluster_find_files");
  DBUG_PRINT("enter", ("db: %s", db));
  { // extra bracket to avoid gcc 2.95.3 warning
unknown's avatar
unknown committed
5115
  uint i;
5116
  Ndb* ndb;
5117
  char name[FN_REFLEN];
unknown's avatar
unknown committed
5118
  HASH ndb_tables, ok_tables;
unknown's avatar
unknown committed
5119
  NdbDictionary::Dictionary::List list;
5120 5121 5122 5123

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

5124
  if (dir)
unknown's avatar
unknown committed
5125
    DBUG_RETURN(0); // Discover of databases not yet supported
5126

unknown's avatar
unknown committed
5127
  // List tables in NDB
5128
  NDBDICT *dict= ndb->getDictionary();
unknown's avatar
unknown committed
5129
  if (dict->listObjects(list, 
5130
                        NdbDictionary::Object::UserTable) != 0)
unknown's avatar
unknown committed
5131
    ERR_RETURN(dict->getNdbError());
5132

unknown's avatar
unknown committed
5133
  if (hash_init(&ndb_tables, system_charset_info,list.count,0,0,
5134
                (hash_get_key)tables_get_key,0,0))
unknown's avatar
unknown committed
5135 5136 5137 5138 5139 5140
  {
    DBUG_PRINT("error", ("Failed to init HASH ndb_tables"));
    DBUG_RETURN(-1);
  }

  if (hash_init(&ok_tables, system_charset_info,32,0,0,
5141
                (hash_get_key)tables_get_key,0,0))
unknown's avatar
unknown committed
5142 5143 5144 5145 5146 5147
  {
    DBUG_PRINT("error", ("Failed to init HASH ok_tables"));
    hash_free(&ndb_tables);
    DBUG_RETURN(-1);
  }  

unknown's avatar
unknown committed
5148 5149 5150
  for (i= 0 ; i < list.count ; i++)
  {
    NdbDictionary::Dictionary::List::Element& t= list.elements[i];
unknown's avatar
unknown committed
5151
    DBUG_PRINT("info", ("Found %s/%s in NDB", t.database, t.name));     
unknown's avatar
unknown committed
5152

5153 5154 5155
    // Add only tables that belongs to db
    if (my_strcasecmp(system_charset_info, t.database, db))
      continue;
unknown's avatar
unknown committed
5156

unknown's avatar
unknown committed
5157
    // Apply wildcard to list of tables in NDB
5158
    if (wild)
5159
    {
5160 5161
      if (lower_case_table_names)
      {
5162 5163
        if (wild_case_compare(files_charset_info, t.name, wild))
          continue;
5164 5165
      }
      else if (wild_compare(t.name,wild,0))
5166
        continue;
5167
    }
unknown's avatar
unknown committed
5168 5169
    DBUG_PRINT("info", ("Inserting %s into ndb_tables hash", t.name));     
    my_hash_insert(&ndb_tables, (byte*)thd->strdup(t.name));
unknown's avatar
unknown committed
5170 5171
  }

unknown's avatar
unknown committed
5172 5173 5174 5175 5176
  char *file_name;
  List_iterator<char> it(*files);
  List<char> delete_list;
  while ((file_name=it++))
  {
5177
    bool file_on_disk= false;
unknown's avatar
unknown committed
5178 5179 5180 5181
    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));
5182
      file_on_disk= true;
unknown's avatar
unknown committed
5183 5184
    }
    
5185
    // Check for .ndb file with this name
5186
    (void)strxnmov(name, FN_REFLEN, 
5187
                   mysql_data_home,"/",db,"/",file_name,ha_ndb_ext,NullS);
unknown's avatar
unknown committed
5188
    DBUG_PRINT("info", ("Check access for %s", name));
5189
    if (access(name, F_OK))
unknown's avatar
unknown committed
5190 5191 5192
    {
      DBUG_PRINT("info", ("%s did not exist on disk", name));     
      // .ndb file did not exist on disk, another table type
5193
      if (file_on_disk)
5194 5195 5196 5197 5198
      {
	// Ignore this ndb table
	gptr record=  hash_search(&ndb_tables, file_name, strlen(file_name));
	DBUG_ASSERT(record);
	hash_delete(&ndb_tables, record);
5199 5200 5201 5202
	push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
			    ER_TABLE_EXISTS_ERROR,
			    "Local table %s.%s shadows ndb table",
			    db, file_name);
5203
      }
5204 5205 5206 5207
      continue;
    }
    if (file_on_disk) 
    {
5208
      // File existed in NDB and as frm file, put in ok_tables list
5209
      my_hash_insert(&ok_tables, (byte*)file_name);
unknown's avatar
unknown committed
5210
      continue;
5211
    }
unknown's avatar
unknown committed
5212 5213 5214
    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.
5215
    if (ndbcluster_table_exists_in_engine(thd, db, file_name) == 0)    
unknown's avatar
unknown committed
5216 5217 5218 5219 5220 5221 5222
    {
      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));
    }
  }
5223

unknown's avatar
unknown committed
5224 5225 5226 5227
  // 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++)
5228
  {
unknown's avatar
unknown committed
5229 5230
    file_name= hash_element(&ndb_tables, i);
    if (!hash_search(&ok_tables, file_name, strlen(file_name)))
5231
    {
unknown's avatar
unknown committed
5232 5233 5234 5235 5236 5237
      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));
    }
  }
5238

unknown's avatar
unknown committed
5239 5240
  // Lock mutex before deleting and creating frm files
  pthread_mutex_lock(&LOCK_open);
5241

unknown's avatar
unknown committed
5242 5243 5244 5245 5246
  if (!global_read_lock)
  {
    // Delete old files
    List_iterator_fast<char> it3(delete_list);
    while ((file_name=it3++))
5247 5248
    {
      DBUG_PRINT("info", ("Remove table %s/%s", db, file_name));
unknown's avatar
unknown committed
5249 5250 5251 5252
      // Delete the table and all related files
      TABLE_LIST table_list;
      bzero((char*) &table_list,sizeof(table_list));
      table_list.db= (char*) db;
5253
      table_list.alias= table_list.table_name= (char*)file_name;
5254
      (void)mysql_rm_table_part2(thd, &table_list,
unknown's avatar
Merge  
unknown committed
5255 5256 5257 5258
                                                                 /* if_exists */ FALSE,
                                                                 /* drop_temporary */ FALSE,
                                                                 /* drop_view */ FALSE,
                                                                 /* dont_log_query*/ TRUE);
5259 5260
      /* Clear error message that is returned when table is deleted */
      thd->clear_error();
5261 5262 5263
    }
  }

unknown's avatar
unknown committed
5264 5265 5266 5267
  // Create new files
  List_iterator_fast<char> it2(create_list);
  while ((file_name=it2++))
  {  
5268
    DBUG_PRINT("info", ("Table %s need discovery", file_name));
5269
    if (ha_create_table_from_engine(thd, db, file_name) == 0)
5270
      files->push_back(thd->strdup(file_name)); 
unknown's avatar
unknown committed
5271 5272 5273 5274 5275
  }

  pthread_mutex_unlock(&LOCK_open);      
  
  hash_free(&ok_tables);
5276
  hash_free(&ndb_tables);
5277
  } // extra bracket to avoid gcc 2.95.3 warning
5278
  DBUG_RETURN(0);    
unknown's avatar
unknown committed
5279 5280 5281 5282 5283 5284 5285 5286
}


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

5287 5288 5289 5290 5291 5292 5293
/* Call back after cluster connect */
static int connect_callback()
{
  update_status_variables(g_ndb_cluster_connection);
  return 0;
}

5294
bool ndbcluster_init()
unknown's avatar
unknown committed
5295
{
unknown's avatar
unknown committed
5296
  int res;
unknown's avatar
unknown committed
5297
  DBUG_ENTER("ndbcluster_init");
5298 5299 5300 5301

  if (have_ndbcluster != SHOW_OPTION_YES)
    goto ndbcluster_init_error;

5302
  // Set connectstring if specified
5303 5304
  if (opt_ndbcluster_connectstring != 0)
    DBUG_PRINT("connectstring", ("%s", opt_ndbcluster_connectstring));     
5305
  if ((g_ndb_cluster_connection=
5306
       new Ndb_cluster_connection(opt_ndbcluster_connectstring)) == 0)
5307
  {
5308
    DBUG_PRINT("error",("Ndb_cluster_connection(%s)",
5309
                        opt_ndbcluster_connectstring));
unknown's avatar
unknown committed
5310
    goto ndbcluster_init_error;
5311
  }
unknown's avatar
ndb:  
unknown committed
5312 5313 5314 5315 5316
  {
    char buf[128];
    my_snprintf(buf, sizeof(buf), "mysqld --server-id=%d", server_id);
    g_ndb_cluster_connection->set_name(buf);
  }
5317 5318 5319
  g_ndb_cluster_connection->set_optimized_node_selection
    (opt_ndb_optimized_node_selection);

unknown's avatar
unknown committed
5320
  // Create a Ndb object to open the connection  to NDB
5321 5322 5323 5324 5325
  if ( (g_ndb= new Ndb(g_ndb_cluster_connection, "sys")) == 0 )
  {
    DBUG_PRINT("error", ("failed to create global ndb object"));
    goto ndbcluster_init_error;
  }
5326
  g_ndb->getDictionary()->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
unknown's avatar
unknown committed
5327 5328 5329
  if (g_ndb->init() != 0)
  {
    ERR_PRINT (g_ndb->getNdbError());
unknown's avatar
unknown committed
5330
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
5331
  }
unknown's avatar
unknown committed
5332

unknown's avatar
unknown committed
5333
  if ((res= g_ndb_cluster_connection->connect(0,0,0)) == 0)
unknown's avatar
unknown committed
5334
  {
5335
    connect_callback();
unknown's avatar
unknown committed
5336
    DBUG_PRINT("info",("NDBCLUSTER storage engine at %s on port %d",
5337 5338
                       g_ndb_cluster_connection->get_connected_host(),
                       g_ndb_cluster_connection->get_connected_port()));
5339
    g_ndb_cluster_connection->wait_until_ready(10,3);
unknown's avatar
unknown committed
5340
  } 
unknown's avatar
unknown committed
5341
  else if (res == 1)
unknown's avatar
unknown committed
5342
  {
5343
    if (g_ndb_cluster_connection->start_connect_thread(connect_callback)) 
5344
    {
unknown's avatar
unknown committed
5345
      DBUG_PRINT("error", ("g_ndb_cluster_connection->start_connect_thread()"));
unknown's avatar
unknown committed
5346 5347
      goto ndbcluster_init_error;
    }
5348
#ifndef DBUG_OFF
unknown's avatar
unknown committed
5349 5350
    {
      char buf[1024];
5351
      DBUG_PRINT("info",
5352 5353 5354 5355
                 ("NDBCLUSTER storage engine not started, "
                  "will connect using %s",
                  g_ndb_cluster_connection->
                  get_connectstring(buf,sizeof(buf))));
unknown's avatar
unknown committed
5356
    }
5357
#endif
unknown's avatar
unknown committed
5358
  }
unknown's avatar
unknown committed
5359
  else
unknown's avatar
unknown committed
5360 5361 5362
  {
    DBUG_ASSERT(res == -1);
    DBUG_PRINT("error", ("permanent error"));
unknown's avatar
unknown committed
5363
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
5364
  }
unknown's avatar
unknown committed
5365
  
unknown's avatar
unknown committed
5366 5367 5368
  (void) hash_init(&ndbcluster_open_tables,system_charset_info,32,0,0,
                   (hash_get_key) ndbcluster_get_key,0,0);
  pthread_mutex_init(&ndbcluster_mutex,MY_MUTEX_INIT_FAST);
unknown's avatar
Merge  
unknown committed
5369 5370
  pthread_mutex_init(&LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST);
  pthread_cond_init(&COND_ndb_util_thread, NULL);
5371

unknown's avatar
Merge  
unknown committed
5372

unknown's avatar
unknown committed
5373
  ndb_cache_check_time = opt_ndb_cache_check_time;
unknown's avatar
Merge  
unknown committed
5374 5375 5376 5377 5378
  // 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"));
5379 5380 5381 5382
    hash_free(&ndbcluster_open_tables);
    pthread_mutex_destroy(&ndbcluster_mutex);
    pthread_mutex_destroy(&LOCK_ndb_util_thread);
    pthread_cond_destroy(&COND_ndb_util_thread);
unknown's avatar
Merge  
unknown committed
5383 5384 5385
    goto ndbcluster_init_error;
  }
  
unknown's avatar
unknown committed
5386
  ndbcluster_inited= 1;
5387
  DBUG_RETURN(FALSE);
unknown's avatar
Merge  
unknown committed
5388

5389
ndbcluster_init_error:
unknown's avatar
unknown committed
5390
  if (g_ndb)
5391 5392 5393 5394 5395
    delete g_ndb;
  g_ndb= NULL;
  if (g_ndb_cluster_connection)
    delete g_ndb_cluster_connection;
  g_ndb_cluster_connection= NULL;
5396 5397
  have_ndbcluster= SHOW_OPTION_DISABLED;	// If we couldn't use handler
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
5398 5399 5400 5401 5402 5403
}


/*
  End use of the NDB Cluster table handler
  - free all global variables allocated by 
unknown's avatar
Merge  
unknown committed
5404
    ndbcluster_init()
unknown's avatar
unknown committed
5405 5406 5407 5408 5409
*/

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

5411 5412 5413
  if (!ndbcluster_inited)
    DBUG_RETURN(0);

unknown's avatar
Merge  
unknown committed
5414 5415 5416 5417 5418 5419
  // Kill ndb utility thread
  (void) pthread_mutex_lock(&LOCK_ndb_util_thread);  
  DBUG_PRINT("exit",("killing ndb util thread: %lx", ndb_util_thread));
  (void) pthread_cond_signal(&COND_ndb_util_thread);
  (void) pthread_mutex_unlock(&LOCK_ndb_util_thread);

unknown's avatar
unknown committed
5420
  if (g_ndb)
5421 5422
  {
#ifndef DBUG_OFF
unknown's avatar
unknown committed
5423 5424
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
5425 5426 5427 5428 5429 5430 5431 5432 5433 5434
    while (g_ndb->get_free_list_usage(&tmp))
    {
      uint leaked= (uint) tmp.m_created - tmp.m_free;
      if (leaked)
        fprintf(stderr, "NDB: Found %u %s%s that %s not been released\n",
                leaked, tmp.m_name,
                (leaked == 1)?"":"'s",
                (leaked == 1)?"has":"have");
    }
#endif
unknown's avatar
unknown committed
5435
    delete g_ndb;
unknown's avatar
unknown committed
5436
    g_ndb= NULL;
5437
  }
unknown's avatar
unknown committed
5438
  delete g_ndb_cluster_connection;
5439
  g_ndb_cluster_connection= NULL;
5440

unknown's avatar
unknown committed
5441 5442
  hash_free(&ndbcluster_open_tables);
  pthread_mutex_destroy(&ndbcluster_mutex);
unknown's avatar
Merge  
unknown committed
5443 5444
  pthread_mutex_destroy(&LOCK_ndb_util_thread);
  pthread_cond_destroy(&COND_ndb_util_thread);
unknown's avatar
unknown committed
5445 5446 5447 5448
  ndbcluster_inited= 0;
  DBUG_RETURN(0);
}

5449 5450 5451 5452 5453
/*
  Static error print function called from
  static handler method ndbcluster_commit
  and ndbcluster_rollback
*/
5454 5455

void ndbcluster_print_error(int error, const NdbOperation *error_op)
5456
{
5457 5458
  DBUG_ENTER("ndbcluster_print_error");
  TABLE tab;
5459
  const char *tab_name= (error_op) ? error_op->getTableName() : "";
5460
  tab.alias= (char *) tab_name;
5461
  ha_ndbcluster error_handler(&tab);
5462
  tab.file= &error_handler;
5463
  error_handler.print_error(error, MYF(0));
unknown's avatar
unknown committed
5464
  DBUG_VOID_RETURN;
5465
}
unknown's avatar
unknown committed
5466

5467 5468 5469
/**
 * Set a given location from full pathname to database name
 *
unknown's avatar
unknown committed
5470
 */
5471
void ha_ndbcluster::set_dbname(const char *path_name, char *dbname)
unknown's avatar
unknown committed
5472 5473 5474 5475
{
  char *end, *ptr;
  
  /* Scan name from the end */
5476 5477 5478 5479 5480 5481
  ptr= strend(path_name)-1;
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  ptr--;
  end= ptr;
unknown's avatar
unknown committed
5482 5483 5484 5485
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  uint name_len= end - ptr;
5486 5487
  memcpy(dbname, ptr + 1, name_len);
  dbname[name_len]= '\0';
unknown's avatar
unknown committed
5488 5489
#ifdef __WIN__
  /* Put to lower case */
5490 5491
  
  ptr= dbname;
unknown's avatar
unknown committed
5492 5493
  
  while (*ptr != '\0') {
5494
    *ptr= tolower(*ptr);
unknown's avatar
unknown committed
5495 5496 5497 5498 5499
    ptr++;
  }
#endif
}

5500 5501 5502 5503 5504 5505 5506 5507 5508
/*
  Set m_dbname from full pathname to table file
 */

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

unknown's avatar
unknown committed
5509 5510 5511 5512 5513 5514 5515 5516 5517 5518
/**
 * 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 */
5519 5520
  end= strend(path_name)-1;
  ptr= end;
unknown's avatar
unknown committed
5521 5522 5523
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
5524
  uint name_len= end - ptr;
unknown's avatar
unknown committed
5525
  memcpy(tabname, ptr + 1, end - ptr);
5526
  tabname[name_len]= '\0';
unknown's avatar
unknown committed
5527 5528
#ifdef __WIN__
  /* Put to lower case */
5529
  ptr= tabname;
unknown's avatar
unknown committed
5530 5531 5532 5533 5534 5535 5536 5537 5538
  
  while (*ptr != '\0') {
    *ptr= tolower(*ptr);
    ptr++;
  }
#endif
}

/*
5539
  Set m_tabname from full pathname to table file 
unknown's avatar
unknown committed
5540 5541
 */

5542
void ha_ndbcluster::set_tabname(const char *path_name)
unknown's avatar
unknown committed
5543
{
5544
  set_tabname(path_name, m_tabname);
unknown's avatar
unknown committed
5545 5546 5547 5548
}


ha_rows 
unknown's avatar
unknown committed
5549 5550 5551 5552
ha_ndbcluster::records_in_range(uint inx, key_range *min_key,
                                key_range *max_key)
{
  KEY *key_info= table->key_info + inx;
unknown's avatar
unknown committed
5553
  uint key_length= key_info->key_length;
5554
  NDB_INDEX_TYPE idx_type= get_index_type(inx);  
unknown's avatar
unknown committed
5555 5556

  DBUG_ENTER("records_in_range");
5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570
  // Prevent partial read of hash indexes by returning HA_POS_ERROR
  if ((idx_type == UNIQUE_INDEX || idx_type == PRIMARY_KEY_INDEX) &&
      ((min_key && min_key->length < key_length) ||
       (max_key && max_key->length < key_length)))
    DBUG_RETURN(HA_POS_ERROR);
  
  // Read from hash index with full key
  // This is a "const" table which returns only one record!      
  if ((idx_type != ORDERED_INDEX) &&
      ((min_key && min_key->length == key_length) || 
       (max_key && max_key->length == key_length)))
    DBUG_RETURN(1);
  
  DBUG_RETURN(10); /* Good guess when you don't know anything */
unknown's avatar
unknown committed
5571 5572
}

5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599
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;
}
unknown's avatar
unknown committed
5600 5601 5602 5603
uint ha_ndbcluster::max_supported_key_part_length() const
{
  return NDB_MAX_KEY_SIZE;
}
5604 5605 5606 5607 5608 5609 5610 5611 5612 5613
bool ha_ndbcluster::low_byte_first() const
{ 
#ifdef WORDS_BIGENDIAN
  return FALSE;
#else
  return TRUE;
#endif
}
bool ha_ndbcluster::has_transactions()
{
5614
  return TRUE;
5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628
}
const char* ha_ndbcluster::index_type(uint key_number)
{
  switch (get_index_type(key_number)) {
  case ORDERED_INDEX:
  case UNIQUE_ORDERED_INDEX:
  case PRIMARY_KEY_ORDERED_INDEX:
    return "BTREE";
  case UNIQUE_INDEX:
  case PRIMARY_KEY_INDEX:
  default:
    return "HASH";
  }
}
unknown's avatar
Merge  
unknown committed
5629

5630 5631
uint8 ha_ndbcluster::table_cache_type()
{
unknown's avatar
Merge  
unknown committed
5632 5633 5634 5635 5636 5637
  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,
5638
                         Uint64 *commit_count)
unknown's avatar
Merge  
unknown committed
5639 5640 5641
{
  DBUG_ENTER("ndb_get_commitcount");

5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659
  char name[FN_REFLEN];
  NDB_SHARE *share;
  (void)strxnmov(name, FN_REFLEN, "./",dbname,"/",tabname,NullS);
  DBUG_PRINT("enter", ("name: %s", name));
  pthread_mutex_lock(&ndbcluster_mutex);
  if (!(share=(NDB_SHARE*) hash_search(&ndbcluster_open_tables,
                                       (byte*) name,
                                       strlen(name))))
  {
    pthread_mutex_unlock(&ndbcluster_mutex);
    DBUG_PRINT("info", ("Table %s not found in ndbcluster_open_tables",
                        name));
    DBUG_RETURN(1);
  }
  share->use_count++;
  pthread_mutex_unlock(&ndbcluster_mutex);

  pthread_mutex_lock(&share->mutex);
unknown's avatar
Merge  
unknown committed
5660 5661
  if (ndb_cache_check_time > 0)
  {
5662
    if (share->commit_count != 0)
unknown's avatar
Merge  
unknown committed
5663
    {
5664
      *commit_count= share->commit_count;
unknown's avatar
unknown committed
5665 5666 5667
      char buff[22];
      DBUG_PRINT("info", ("Getting commit_count: %s from share",
                          llstr(share->commit_count, buff)));
5668 5669 5670
      pthread_mutex_unlock(&share->mutex);
      free_share(share);
      DBUG_RETURN(0);
unknown's avatar
Merge  
unknown committed
5671 5672
    }
  }
5673
  DBUG_PRINT("info", ("Get commit_count from NDB"));
unknown's avatar
Merge  
unknown committed
5674 5675 5676 5677
  Ndb *ndb;
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(1);
  ndb->setDatabaseName(dbname);
5678 5679
  uint lock= share->commit_count_lock;
  pthread_mutex_unlock(&share->mutex);
unknown's avatar
Merge  
unknown committed
5680 5681

  struct Ndb_statistics stat;
unknown's avatar
unknown committed
5682
  if (ndb_get_table_statistics(NULL, false, ndb, tabname, &stat))
5683 5684
  {
    free_share(share);
unknown's avatar
Merge  
unknown committed
5685
    DBUG_RETURN(1);
5686 5687 5688
  }

  pthread_mutex_lock(&share->mutex);
unknown's avatar
unknown committed
5689
  if (share->commit_count_lock == lock)
5690
  {
unknown's avatar
unknown committed
5691 5692 5693
    char buff[22];
    DBUG_PRINT("info", ("Setting commit_count to %s",
                        llstr(stat.commit_count, buff)));
5694 5695 5696 5697 5698 5699 5700 5701 5702 5703
    share->commit_count= stat.commit_count;
    *commit_count= stat.commit_count;
  }
  else
  {
    DBUG_PRINT("info", ("Discarding commit_count, comit_count_lock changed"));
    *commit_count= 0;
  }
  pthread_mutex_unlock(&share->mutex);
  free_share(share);
unknown's avatar
Merge  
unknown committed
5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739
  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,
5740 5741
                                   char *full_name, uint full_name_len,
                                   ulonglong *engine_data)
unknown's avatar
Merge  
unknown committed
5742 5743 5744 5745 5746
{
  Uint64 commit_count;
  bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
  char *dbname= full_name;
  char *tabname= dbname+strlen(dbname)+1;
unknown's avatar
unknown committed
5747 5748
  char buff[22], buff2[22];
  DBUG_ENTER("ndbcluster_cache_retrieval_allowed");
5749 5750
  DBUG_PRINT("enter", ("dbname: %s, tabname: %s, is_autocommit: %d",
                       dbname, tabname, is_autocommit));
unknown's avatar
Merge  
unknown committed
5751 5752

  if (!is_autocommit)
5753 5754
  {
    DBUG_PRINT("exit", ("No, don't use cache in transaction"));
unknown's avatar
Merge  
unknown committed
5755
    DBUG_RETURN(FALSE);
5756
  }
unknown's avatar
Merge  
unknown committed
5757 5758 5759

  if (ndb_get_commitcount(thd, dbname, tabname, &commit_count))
  {
5760 5761
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, could not retrieve commit_count"));
unknown's avatar
Merge  
unknown committed
5762 5763
    DBUG_RETURN(FALSE);
  }
unknown's avatar
unknown committed
5764 5765
  DBUG_PRINT("info", ("*engine_data: %s, commit_count: %s",
                      llstr(*engine_data, buff), llstr(commit_count, buff2)));
5766
  if (commit_count == 0)
unknown's avatar
Merge  
unknown committed
5767
  {
5768 5769
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, local commit has been performed"));
unknown's avatar
Merge  
unknown committed
5770 5771
    DBUG_RETURN(FALSE);
  }
5772 5773 5774 5775 5776 5777
  else if (*engine_data != commit_count)
  {
    *engine_data= commit_count; /* invalidate */
     DBUG_PRINT("exit", ("No, commit_count has changed"));
     DBUG_RETURN(FALSE);
   }
unknown's avatar
Merge  
unknown committed
5778

unknown's avatar
unknown committed
5779 5780
  DBUG_PRINT("exit", ("OK to use cache, engine_data: %s",
                      llstr(*engine_data, buff)));
unknown's avatar
Merge  
unknown committed
5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808
  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,
5809 5810 5811
                                          char *full_name, uint full_name_len,
                                          qc_engine_callback *engine_callback,
                                          ulonglong *engine_data)
unknown's avatar
Merge  
unknown committed
5812
{
unknown's avatar
unknown committed
5813 5814
  Uint64 commit_count;
  char buff[22];
unknown's avatar
Merge  
unknown committed
5815
  bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
unknown's avatar
unknown committed
5816
  DBUG_ENTER("ha_ndbcluster::register_query_cache_table");
5817 5818 5819
  DBUG_PRINT("enter",("dbname: %s, tabname: %s, is_autocommit: %d",
		      m_dbname, m_tabname, is_autocommit));

unknown's avatar
Merge  
unknown committed
5820
  if (!is_autocommit)
5821 5822
  {
    DBUG_PRINT("exit", ("Can't register table during transaction"))
unknown's avatar
Merge  
unknown committed
5823
    DBUG_RETURN(FALSE);
5824
  }
unknown's avatar
Merge  
unknown committed
5825 5826 5827 5828

  if (ndb_get_commitcount(thd, m_dbname, m_tabname, &commit_count))
  {
    *engine_data= 0;
5829
    DBUG_PRINT("exit", ("Error, could not get commitcount"))
unknown's avatar
Merge  
unknown committed
5830 5831 5832 5833
    DBUG_RETURN(FALSE);
  }
  *engine_data= commit_count;
  *engine_callback= ndbcluster_cache_retrieval_allowed;
unknown's avatar
unknown committed
5834
  DBUG_PRINT("exit", ("commit_count: %s", llstr(commit_count, buff)));
5835
  DBUG_RETURN(commit_count > 0);
5836
}
unknown's avatar
unknown committed
5837

unknown's avatar
Merge  
unknown committed
5838

unknown's avatar
unknown committed
5839
/*
unknown's avatar
Merge  
unknown committed
5840
  Handling the shared NDB_SHARE structure that is needed to
unknown's avatar
unknown committed
5841 5842 5843 5844 5845 5846 5847
  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,
5848
                                my_bool not_used __attribute__((unused)))
unknown's avatar
unknown committed
5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876
{
  *length=share->table_name_length;
  return (byte*) share->table_name;
}

static NDB_SHARE* get_share(const char *table_name)
{
  NDB_SHARE *share;
  pthread_mutex_lock(&ndbcluster_mutex);
  uint length=(uint) strlen(table_name);
  if (!(share=(NDB_SHARE*) hash_search(&ndbcluster_open_tables,
                                       (byte*) table_name,
                                       length)))
  {
    if ((share=(NDB_SHARE *) my_malloc(sizeof(*share)+length+1,
                                       MYF(MY_WME | MY_ZEROFILL))))
    {
      share->table_name_length=length;
      share->table_name=(char*) (share+1);
      strmov(share->table_name,table_name);
      if (my_hash_insert(&ndbcluster_open_tables, (byte*) share))
      {
        pthread_mutex_unlock(&ndbcluster_mutex);
        my_free((gptr) share,0);
        return 0;
      }
      thr_lock_init(&share->lock);
      pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
unknown's avatar
Merge  
unknown committed
5877
      share->commit_count= 0;
5878 5879 5880 5881 5882 5883 5884
      share->commit_count_lock= 0;
    }
    else
    {
      DBUG_PRINT("error", ("Failed to alloc share"));
      pthread_mutex_unlock(&ndbcluster_mutex);
      return 0;
unknown's avatar
unknown committed
5885 5886 5887
    }
  }
  share->use_count++;
5888 5889 5890 5891 5892

  DBUG_PRINT("share",
	     ("table_name: %s, length: %d, use_count: %d, commit_count: %d",
	      share->table_name, share->table_name_length, share->use_count,
	      share->commit_count));
unknown's avatar
unknown committed
5893 5894 5895 5896 5897 5898 5899 5900 5901 5902
  pthread_mutex_unlock(&ndbcluster_mutex);
  return share;
}


static void free_share(NDB_SHARE *share)
{
  pthread_mutex_lock(&ndbcluster_mutex);
  if (!--share->use_count)
  {
5903
     hash_delete(&ndbcluster_open_tables, (byte*) share);
unknown's avatar
unknown committed
5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931
    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, 
5932
                   const void **pack_data, uint *pack_len)
unknown's avatar
unknown committed
5933 5934 5935 5936 5937 5938 5939 5940 5941
{
  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;
5942
  org_len= len;
unknown's avatar
unknown committed
5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961
  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);  
  
5962 5963 5964
  *pack_data= blob;
  *pack_len= blob_len;
  error= 0;
unknown's avatar
unknown committed
5965 5966 5967 5968 5969 5970 5971 5972 5973
  
  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,
5974
                    const void *pack_data)
unknown's avatar
unknown committed
5975
{
5976
   const frm_blob_struct *blob= (frm_blob_struct*)pack_data;
unknown's avatar
unknown committed
5977 5978 5979 5980 5981
   byte *data;
   ulong complen, orglen, ver;
   DBUG_ENTER("unpackfrm");
   DBUG_PRINT("enter", ("pack_data: %x", pack_data));

5982 5983 5984
   complen=     uint4korr((char*)&blob->head.complen);
   orglen=      uint4korr((char*)&blob->head.orglen);
   ver=         uint4korr((char*)&blob->head.ver);
unknown's avatar
unknown committed
5985 5986
 
   DBUG_PRINT("blob",("ver: %d complen: %d orglen: %d",
5987
                     ver,complen,orglen));
unknown's avatar
unknown committed
5988 5989 5990 5991
   DBUG_DUMP("blob->data", (char*) blob->data, complen);
 
   if (ver != 1)
     DBUG_RETURN(1);
5992
   if (!(data= my_malloc(max(orglen, complen), MYF(MY_WME))))
unknown's avatar
unknown committed
5993 5994 5995 5996 5997 5998 5999 6000 6001
     DBUG_RETURN(2);
   memcpy(data, blob->data, complen);
 
   if (my_uncompress(data, &complen, &orglen))
   {
     my_free((char*)data, MYF(0));
     DBUG_RETURN(3);
   }

6002 6003
   *unpack_data= data;
   *unpack_len= complen;
unknown's avatar
unknown committed
6004 6005 6006 6007 6008

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

   DBUG_RETURN(0);
}
unknown's avatar
unknown committed
6009 6010 6011

static 
int
6012
ndb_get_table_statistics(ha_ndbcluster* file, bool report_error, Ndb* ndb,
unknown's avatar
unknown committed
6013
                         const char* table,
6014
                         struct Ndb_statistics * ndbstat)
unknown's avatar
unknown committed
6015
{
6016
  NdbTransaction* pTrans;
6017
  NdbError error;
6018
  int retries= 10;
6019
  int reterr= 0;
6020
  int retry_sleep= 30 * 1000; /* 30 milliseconds */
unknown's avatar
unknown committed
6021 6022 6023
  char buff[22], buff2[22], buff3[22], buff4[22];
  DBUG_ENTER("ndb_get_table_statistics");
  DBUG_PRINT("enter", ("table: %s", table));
6024 6025

  do
unknown's avatar
unknown committed
6026
  {
6027 6028
    Uint64 rows, commits, mem;
    Uint32 size;
unknown's avatar
unknown committed
6029
    Uint32 count= 0;
6030 6031
    Uint64 sum_rows= 0;
    Uint64 sum_commits= 0;
unknown's avatar
unknown committed
6032 6033
    Uint64 sum_row_size= 0;
    Uint64 sum_mem= 0;
6034 6035 6036 6037 6038
    NdbScanOperation*pOp;
    NdbResultSet *rs;
    int check;

    if ((pTrans= ndb->startTransaction()) == NULL)
6039
    {
6040 6041 6042
      error= ndb->getNdbError();
      goto retry;
    }
unknown's avatar
unknown committed
6043
      
6044 6045 6046 6047
    if ((pOp= pTrans->getNdbScanOperation(table)) == NULL)
    {
      error= pTrans->getNdbError();
      goto retry;
6048
    }
unknown's avatar
unknown committed
6049
    
6050
    if (pOp->readTuples(NdbOperation::LM_CommittedRead))
6051 6052 6053 6054
    {
      error= pOp->getNdbError();
      goto retry;
    }
unknown's avatar
unknown committed
6055
    
6056 6057 6058 6059 6060
    if (pOp->interpret_exit_last_row() == -1)
    {
      error= pOp->getNdbError();
      goto retry;
    }
unknown's avatar
unknown committed
6061 6062 6063
    
    pOp->getValue(NdbDictionary::Column::ROW_COUNT, (char*)&rows);
    pOp->getValue(NdbDictionary::Column::COMMIT_COUNT, (char*)&commits);
6064 6065
    pOp->getValue(NdbDictionary::Column::ROW_SIZE, (char*)&size);
    pOp->getValue(NdbDictionary::Column::FRAGMENT_MEMORY, (char*)&mem);
unknown's avatar
unknown committed
6066
    
6067 6068 6069
    if (pTrans->execute(NdbTransaction::NoCommit,
                        NdbTransaction::AbortOnError,
                        TRUE) == -1)
6070
    {
6071 6072
      error= pTrans->getNdbError();
      goto retry;
6073
    }
unknown's avatar
unknown committed
6074
    
unknown's avatar
unknown committed
6075
    while ((check= pOp->nextResult(TRUE, TRUE)) == 0)
unknown's avatar
unknown committed
6076 6077 6078
    {
      sum_rows+= rows;
      sum_commits+= commits;
6079
      if (sum_row_size < size)
6080
        sum_row_size= size;
6081
      sum_mem+= mem;
6082
      count++;
unknown's avatar
unknown committed
6083 6084 6085
    }
    
    if (check == -1)
6086 6087 6088 6089
    {
      error= pOp->getNdbError();
      goto retry;
    }
unknown's avatar
unknown committed
6090

6091
    pOp->close(TRUE);
unknown's avatar
unknown committed
6092

unknown's avatar
unknown committed
6093
    ndb->closeTransaction(pTrans);
6094 6095 6096 6097 6098 6099

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

unknown's avatar
unknown committed
6100 6101 6102 6103 6104 6105 6106
    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));
6107

unknown's avatar
unknown committed
6108
    DBUG_RETURN(0);
6109
retry:
6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122
    if(report_error)
    {
      if (file)
      {
        reterr= file->ndb_err(pTrans);
      }
      else
      {
        const NdbError& tmp= error;
        ERR_PRINT(tmp);
        reterr= ndb_to_mysql_error(&tmp);
      }
    }
unknown's avatar
unknown committed
6123 6124 6125
    else
      reterr= error.code;

6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136
    if (pTrans)
    {
      ndb->closeTransaction(pTrans);
      pTrans= NULL;
    }
    if (error.status == NdbError::TemporaryError && retries--)
    {
      my_sleep(retry_sleep);
      continue;
    }
    break;
6137
  } while(1);
6138 6139 6140
  DBUG_PRINT("exit", ("failed, reterr: %u, NdbError %u(%s)", reterr,
                      error.code, error.message));
  DBUG_RETURN(reterr);
unknown's avatar
unknown committed
6141 6142
}

6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157
/*
  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, 
6158
                 mysql_data_home,"/",m_dbname,"/",m_tabname,ha_ndb_ext,NullS);
6159 6160 6161 6162 6163 6164 6165 6166 6167 6168

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

unknown's avatar
unknown committed
6169
void 
6170 6171
ha_ndbcluster::release_completed_operations(NdbTransaction *trans,
					    bool force_release)
unknown's avatar
unknown committed
6172 6173 6174 6175 6176 6177 6178 6179
{
  if (trans->hasBlobOperation())
  {
    /* We are reading/writing BLOB fields, 
       releasing operation records is unsafe
    */
    return;
  }
6180 6181 6182 6183 6184 6185 6186 6187 6188 6189
  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;
    }
  }
unknown's avatar
unknown committed
6190
  trans->releaseCompletedOperations();
6191 6192
}

6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216
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);
}

6217
int
6218
ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
6219 6220 6221 6222
                                      KEY_MULTI_RANGE *ranges, 
                                      uint range_count,
                                      bool sorted, 
                                      HANDLER_BUFFER *buffer)
6223 6224
{
  DBUG_ENTER("ha_ndbcluster::read_multi_range_first");
unknown's avatar
unknown committed
6225
  
6226 6227
  int res;
  KEY* key_info= table->key_info + active_index;
6228
  NDB_INDEX_TYPE index_type= get_index_type(active_index);
unknown's avatar
merge  
unknown committed
6229
  ulong reclength= table->s->reclength;
6230
  NdbOperation* op;
6231
  Thd_ndb *thd_ndb= get_thd_ndb(current_thd);
6232

6233 6234 6235 6236 6237 6238 6239
  /**
   * 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)))
unknown's avatar
unknown committed
6240
  {
unknown's avatar
unknown committed
6241
    m_disable_multi_read= TRUE;
unknown's avatar
unknown committed
6242
    DBUG_RETURN(handler::read_multi_range_first(found_range_p, 
6243 6244 6245 6246
                                                ranges, 
                                                range_count,
                                                sorted, 
                                                buffer));
unknown's avatar
unknown committed
6247
  }
6248
  thd_ndb->query_state|= NDB_QUERY_MULTI_READ_RANGE;
unknown's avatar
unknown committed
6249
  m_disable_multi_read= FALSE;
6250 6251 6252 6253

  /**
   * Copy arguments into member variables
   */
6254 6255 6256
  m_multi_ranges= ranges;
  multi_range_curr= ranges;
  multi_range_end= ranges+range_count;
6257 6258 6259
  multi_range_sorted= sorted;
  multi_range_buffer= buffer;

6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270
  /**
   * 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
6271 6272
   */   

unknown's avatar
unknown committed
6273
  /**
6274 6275
   * Variables for loop
   */
unknown's avatar
unknown committed
6276 6277
  byte *curr= (byte*)buffer->buffer;
  byte *end_of_buffer= (byte*)buffer->buffer_end;
6278 6279
  NdbOperation::LockMode lm= 
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
6280
  bool need_pk = (lm == NdbOperation::LM_Read);
6281 6282
  const NDBTAB *tab= (const NDBTAB *) m_table;
  const NDBINDEX *unique_idx= (NDBINDEX *) m_index[active_index].unique_index;
6283
  const NDBINDEX *idx= (NDBINDEX *) m_index[active_index].index; 
6284 6285
  const NdbOperation* lastOp= m_active_trans->getLastDefinedOperation();
  NdbIndexScanOperation* scanOp= 0;
unknown's avatar
unknown committed
6286 6287
  for (; multi_range_curr<multi_range_end && curr+reclength <= end_of_buffer; 
       multi_range_curr++)
6288
  {
unknown's avatar
unknown committed
6289 6290 6291 6292 6293 6294
    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 */
6295
    case PRIMARY_KEY_INDEX:
6296
      multi_range_curr->range_flag |= UNIQUE_RANGE;
6297
      if ((op= m_active_trans->getNdbOperation(tab)) && 
6298 6299 6300
          !op->readTuple(lm) && 
          !set_primary_key(op, multi_range_curr->start_key.key) &&
          !define_read_attrs(curr, op) &&
unknown's avatar
unknown committed
6301
          (op->setAbortOption(AO_IgnoreError), TRUE))
6302
        curr += reclength;
6303
      else
6304
        ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
6305
      break;
unknown's avatar
unknown committed
6306 6307 6308 6309 6310 6311 6312
    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 */
6313
    case UNIQUE_INDEX:
6314
      multi_range_curr->range_flag |= UNIQUE_RANGE;
6315
      if ((op= m_active_trans->getNdbIndexOperation(unique_idx, tab)) && 
6316 6317 6318 6319 6320
	  !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;
6321
      else
6322
	ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
6323
      break;
unknown's avatar
unknown committed
6324 6325
    case ORDERED_INDEX:
    {
6326
  range:
6327
      multi_range_curr->range_flag &= ~(uint)UNIQUE_RANGE;
6328 6329
      if (scanOp == 0)
      {
6330 6331 6332 6333 6334 6335
        if (m_multi_cursor)
        {
          scanOp= m_multi_cursor;
          DBUG_ASSERT(scanOp->getSorted() == sorted);
          DBUG_ASSERT(scanOp->getLockMode() == 
                      (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type));
unknown's avatar
unknown committed
6336
          if (scanOp->reset_bounds(m_force_send))
6337 6338 6339 6340 6341
            DBUG_RETURN(ndb_err(m_active_trans));
          
          end_of_buffer -= reclength;
        }
        else if ((scanOp= m_active_trans->getNdbIndexScanOperation(idx, tab)) 
6342 6343
                 &&!scanOp->readTuples(lm, 0, parallelism, sorted, 
				       FALSE, TRUE, need_pk)
6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354
                 &&!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());
        }
6355
      }
6356

6357
      const key_range *keys[2]= { &multi_range_curr->start_key, 
6358
                                  &multi_range_curr->end_key };
6359
      if ((res= set_bounds(scanOp, keys, multi_range_curr-ranges)))
6360
        DBUG_RETURN(res);
6361
      break;
6362
    }
unknown's avatar
unknown committed
6363
    case UNDEFINED_INDEX:
unknown's avatar
unknown committed
6364 6365 6366 6367
      DBUG_ASSERT(FALSE);
      DBUG_RETURN(1);
      break;
    }
6368 6369
  }
  
6370
  if (multi_range_curr != multi_range_end)
6371
  {
6372 6373 6374 6375 6376 6377
    /**
     * 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
     */
6378
    buffer->end_of_used_area= (byte*)buffer->buffer_end;
6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389
  }
  else
  {
    buffer->end_of_used_area= curr;
  }
  
  /**
   * Set first operation in multi range
   */
  m_current_multi_operation= 
    lastOp ? lastOp->next() : m_active_trans->getFirstDefinedOperation();
6390
  if (!(res= execute_no_commit_ie(this, m_active_trans, true)))
6391
  {
6392 6393
    m_multi_range_defined= multi_range_curr;
    multi_range_curr= ranges;
6394 6395
    m_multi_range_result_ptr= (byte*)buffer->buffer;
    DBUG_RETURN(read_multi_range_next(found_range_p));
6396 6397 6398 6399
  }
  ERR_RETURN(m_active_trans->getNdbError());
}

unknown's avatar
unknown committed
6400 6401 6402 6403 6404 6405
#if 0
#define DBUG_MULTI_RANGE(x) printf("read_multi_range_next: case %d\n", x);
#else
#define DBUG_MULTI_RANGE(x)
#endif

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

6546 6547 6548 6549 6550 6551 6552 6553
int
ha_ndbcluster::setup_recattr(const NdbRecAttr* curr)
{
  DBUG_ENTER("setup_recattr");

  Field **field, **end;
  NdbValue *value= m_value;
  
unknown's avatar
merge  
unknown committed
6554
  end= table->field + table->s->fields;
6555 6556 6557 6558 6559 6560
  
  for (field= table->field; field < end; field++, value++)
  {
    if ((* value).ptr)
    {
      DBUG_ASSERT(curr != 0);
unknown's avatar
unknown committed
6561 6562 6563
      NdbValue* val= m_value + curr->getColumn()->getColumnNo();
      DBUG_ASSERT(val->ptr);
      val->rec= curr;
6564
      curr= curr->next();
6565 6566 6567
    }
  }
  
unknown's avatar
unknown committed
6568
  DBUG_RETURN(0);
6569 6570
}

unknown's avatar
Merge  
unknown committed
6571 6572
char*
ha_ndbcluster::update_table_comment(
6573 6574
                                /* out: table comment + additional */
        const char*     comment)/* in:  table comment defined by user */
unknown's avatar
Merge  
unknown committed
6575 6576
{
  uint length= strlen(comment);
unknown's avatar
unknown committed
6577
  if (length > 64000 - 3)
unknown's avatar
Merge  
unknown committed
6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 6602 6603
  {
    return((char*)comment); /* string too long */
  }

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

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

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

unknown's avatar
unknown committed
6604 6605 6606
  my_snprintf(str,fmt_len_plus_extra,fmt,comment,
              length > 0 ? " ":"",
              tab->getReplicaCount());
unknown's avatar
Merge  
unknown committed
6607 6608 6609 6610 6611
  return str;
}


// Utility thread main loop
6612
pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
unknown's avatar
Merge  
unknown committed
6613 6614
{
  THD *thd; /* needs to be first for thread_stack */
6615
  Ndb* ndb;
unknown's avatar
Merge  
unknown committed
6616 6617 6618 6619 6620 6621 6622 6623
  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);
6624
  ndb= new Ndb(g_ndb_cluster_connection, "");
unknown's avatar
Merge  
unknown committed
6625 6626 6627 6628 6629

  pthread_detach_this_thread();
  ndb_util_thread= pthread_self();

  thd->thread_stack= (char*)&thd; /* remember where our stack is */
unknown's avatar
unknown committed
6630
  if (thd->store_globals() || (ndb->init() != 0))
unknown's avatar
Merge  
unknown committed
6631 6632 6633
  {
    thd->cleanup();
    delete thd;
6634
    delete ndb;
unknown's avatar
Merge  
unknown committed
6635 6636 6637 6638
    DBUG_RETURN(NULL);
  }

  List<NDB_SHARE> util_open_tables;
6639
  set_timespec(abstime, 0);
unknown's avatar
Merge  
unknown committed
6640 6641 6642 6643
  for (;;)
  {

    pthread_mutex_lock(&LOCK_ndb_util_thread);
unknown's avatar
unknown committed
6644 6645 6646
    pthread_cond_timedwait(&COND_ndb_util_thread,
                           &LOCK_ndb_util_thread,
                           &abstime);
unknown's avatar
Merge  
unknown committed
6647 6648 6649 6650 6651 6652 6653 6654 6655 6656
    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)
    {
6657 6658
      /* Wake up in 1 second to check if value has changed */
      set_timespec(abstime, 1);
unknown's avatar
Merge  
unknown committed
6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679
      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);
6680
    while ((share= it++))
unknown's avatar
Merge  
unknown committed
6681 6682 6683 6684 6685 6686 6687 6688 6689 6690
    {
      /* 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",
6691 6692
                 ("Fetching commit count for: %s",
                  share->table_name));
unknown's avatar
Merge  
unknown committed
6693 6694

      /* Contact NDB to get commit count for table */
6695 6696 6697 6698 6699 6700 6701 6702
      ndb->setDatabaseName(db);
      struct Ndb_statistics stat;

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

unknown's avatar
unknown committed
6703
      if (ndb_get_table_statistics(NULL, false, ndb, tabname, &stat) == 0)
unknown's avatar
Merge  
unknown committed
6704
      {
unknown's avatar
unknown committed
6705
        char buff[22], buff2[22];
unknown's avatar
Merge  
unknown committed
6706
        DBUG_PRINT("ndb_util_thread",
unknown's avatar
unknown committed
6707 6708 6709
                   ("Table: %s  commit_count: %s  rows: %s",
                    share->table_name,
                    llstr(stat.commit_count, buff),
unknown's avatar
unknown committed
6710
                    llstr(stat.row_count, buff2)));
unknown's avatar
Merge  
unknown committed
6711 6712 6713 6714 6715 6716
      }
      else
      {
        DBUG_PRINT("ndb_util_thread",
                   ("Error: Could not get commit count for table %s",
                    share->table_name));
6717
        stat.commit_count= 0;
unknown's avatar
Merge  
unknown committed
6718
      }
6719 6720 6721 6722 6723 6724

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

unknown's avatar
Merge  
unknown committed
6725 6726 6727 6728 6729 6730 6731
      /* Decrease the use count and possibly free share */
      free_share(share);
    }

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

6732 6733 6734 6735 6736 6737 6738 6739 6740
    /* Calculate new time to wake up */
    int secs= 0;
    int msecs= ndb_cache_check_time;

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

unknown's avatar
unknown committed
6741
    if (msecs >= 1000){
6742 6743 6744 6745 6746 6747 6748 6749 6750 6751
      secs=  msecs / 1000;
      msecs= msecs % 1000;
    }

    abstime.tv_sec+=  secs;
    abstime.tv_nsec+= msecs * 1000000;
    if (abstime.tv_nsec >= 1000000000) {
      abstime.tv_sec+=  1;
      abstime.tv_nsec-= 1000000000;
    }
unknown's avatar
Merge  
unknown committed
6752 6753 6754 6755
  }

  thd->cleanup();
  delete thd;
6756
  delete ndb;
unknown's avatar
Merge  
unknown committed
6757 6758 6759 6760 6761 6762
  DBUG_PRINT("exit", ("ndb_util_thread"));
  my_thread_end();
  pthread_exit(0);
  DBUG_RETURN(NULL);
}

6763 6764 6765
/*
  Condition pushdown
*/
6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782
/*
  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)
*/
6783 6784 6785 6786 6787
const 
COND* 
ha_ndbcluster::cond_push(const COND *cond) 
{ 
  DBUG_ENTER("cond_push");
6788 6789 6790
  Ndb_cond_stack *ndb_cond = new Ndb_cond_stack();
  DBUG_EXECUTE("where",print_where((COND *)cond, m_tabname););
  if (m_cond_stack)
unknown's avatar
unknown committed
6791
    ndb_cond->next= m_cond_stack;
6792 6793 6794 6795 6796 6797
  else
    ndb_cond->next= NULL;
  m_cond_stack= ndb_cond;
  
  if (serialize_cond(cond, ndb_cond))
  {
unknown's avatar
unknown committed
6798
    DBUG_RETURN(NULL);
6799 6800 6801 6802
  }
  else
  {
    cond_pop();
unknown's avatar
unknown committed
6803
  }
6804 6805 6806
  DBUG_RETURN(cond); 
}

6807 6808 6809
/*
  Pop the top condition from the condition stack of the handler instance.
*/
6810 6811 6812 6813 6814 6815 6816 6817 6818
void 
ha_ndbcluster::cond_pop() 
{ 
  Ndb_cond_stack *ndb_cond_stack= m_cond_stack;  
  if (ndb_cond_stack)
  {
    m_cond_stack= ndb_cond_stack->next;
    delete ndb_cond_stack;
  }
unknown's avatar
unknown committed
6819
}
6820

6821 6822 6823
/*
  Clear the condition stack
*/
6824 6825 6826 6827 6828 6829 6830 6831 6832 6833
void
ha_ndbcluster::cond_clear()
{
  DBUG_ENTER("cond_clear");
  while (m_cond_stack)
    cond_pop();

  DBUG_VOID_RETURN;
}

6834 6835 6836 6837 6838 6839
/*
  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.
*/
6840 6841 6842 6843 6844
void ndb_serialize_cond(const Item *item, void *arg)
{
  Ndb_cond_traverse_context *context= (Ndb_cond_traverse_context *) arg;
  DBUG_ENTER("ndb_serialize_cond");  

unknown's avatar
unknown committed
6845 6846 6847 6848 6849
  // Check if we are skipping arguments to a function to be evaluated
  if (context->skip)
  {
    DBUG_PRINT("info", ("Skiping argument %d", context->skip));
    context->skip--;
unknown's avatar
unknown committed
6850 6851 6852
    switch (item->type()) {
    case Item::FUNC_ITEM:
    {
unknown's avatar
unknown committed
6853 6854 6855 6856
      Item_func *func_item= (Item_func *) item;
      context->skip+= func_item->argument_count();
      break;
    }
unknown's avatar
unknown committed
6857 6858 6859 6860 6861
    case Item::INT_ITEM:
    case Item::REAL_ITEM:
    case Item::STRING_ITEM:
    case Item::VARBIN_ITEM:
    case Item::DECIMAL_ITEM:
unknown's avatar
unknown committed
6862 6863
      break;
    default:
6864
      context->supported= FALSE;
unknown's avatar
unknown committed
6865 6866
      break;
    }
6867
    
unknown's avatar
unknown committed
6868 6869 6870
    DBUG_VOID_RETURN;
  }
  
6871
  if (context->supported)
6872
  {
6873 6874 6875 6876 6877 6878
    Ndb_rewrite_context *rewrite_context= context->rewrite_stack;
    const Item_func *func_item;
    // Check if we are rewriting some unsupported function call
    if (rewrite_context &&
        (func_item= rewrite_context->func_item) &&
        rewrite_context->count++ == 0)
unknown's avatar
unknown committed
6879
    {
unknown's avatar
unknown committed
6880 6881
      switch (func_item->functype()) {
      case Item_func::BETWEEN:
6882
        /*
6883 6884 6885 6886 6887 6888 6889
          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()
6890
        */
unknown's avatar
unknown committed
6891 6892
      case Item_func::IN_FUNC:
      {
6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908
        /*
          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)
6909
          {
6910 6911 6912
            Item_func *func_item= (Item_func *) item;
            if (func_item->functype() == Item_func::UNKNOWN_FUNC &&
                func_item->const_item())
6913
            {
6914 6915 6916
              // Skip any arguments since we will evaluate function instead
              DBUG_PRINT("info", ("Skip until end of arguments marker"));
              context->skip= func_item->argument_count();
6917 6918 6919
            }
            else
            {
6920 6921 6922 6923
              DBUG_PRINT("info", ("Found unsupported functional expression in BETWEEN|IN"));
              context->supported= FALSE;
              DBUG_VOID_RETURN;
              
6924 6925 6926
            }
          }
        }
6927 6928
        else
        {
6929 6930 6931
          // Non-supported BETWEEN|IN expression
          DBUG_PRINT("info", ("Found unexpected item of type %u in BETWEEN|IN",
                              item->type()));
6932
          context->supported= FALSE;
6933
          DBUG_VOID_RETURN;
6934
        }
6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956
        break;
      }
      default:
        context->supported= FALSE;
        break;
      }
      DBUG_VOID_RETURN;
    }
    else
    {
      Ndb_cond_stack *ndb_stack= context->stack_ptr;
      Ndb_cond *prev_cond= context->cond_ptr;
      Ndb_cond *curr_cond= context->cond_ptr= new Ndb_cond();
      if (!ndb_stack->ndb_cond)
        ndb_stack->ndb_cond= curr_cond;
      curr_cond->prev= prev_cond;
      if (prev_cond) prev_cond->next= curr_cond;
    // Check if we are rewriting some unsupported function call
      if (context->rewrite_stack)
      {
        Ndb_rewrite_context *rewrite_context= context->rewrite_stack;
        const Item_func *func_item= rewrite_context->func_item;
unknown's avatar
unknown committed
6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968
        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()
          */
6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987
          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;
          }
6988 6989
          break;
        }
unknown's avatar
unknown committed
6990 6991
        case Item_func::IN_FUNC:
        {
6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004
          /*
            Rewrite <field>|<const> IN(<const1>|<field1>, <const2>|<field2>,..)
            to <field>|<const> = <const1>|<field1> OR 
            <field> = <const2>|<field2> ...
            or actually in prefix format
            BEGIN(OR) EQ(<field>|<const>, <const1><field1>), 
            EQ(<field>|<const>, <const2>|<field2>), ... END()
            Each part of the disjunction is added for each call
            to ndb_serialize_cond and end of rewrite statement 
            is wrapped in end of ndb_serialize_cond
          */
          DBUG_PRINT("info", ("EQ_FUNC"));      
          curr_cond->ndb_item= new Ndb_item(Item_func::EQ_FUNC, 2);
7005 7006
          break;
        }
7007 7008
        default:
          context->supported= FALSE;
7009
        }
7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039
        // Handle left hand <field>|<const>
        context->rewrite_stack= NULL; // Disable rewrite mode
        context->expect_only(Item::FIELD_ITEM);
        context->expect_field_result(STRING_RESULT);
        context->expect_field_result(REAL_RESULT);
        context->expect_field_result(INT_RESULT);
        context->expect_field_result(DECIMAL_RESULT);
        context->expect(Item::INT_ITEM);
        context->expect(Item::STRING_ITEM);
        context->expect(Item::VARBIN_ITEM);
        context->expect(Item::FUNC_ITEM);
        ndb_serialize_cond(rewrite_context->left_hand_item, arg);
        context->skip= 0; // Any FUNC_ITEM expression has already been parsed
        context->rewrite_stack= rewrite_context; // Enable rewrite mode
        if (!context->supported)
          DBUG_VOID_RETURN;

        prev_cond= context->cond_ptr;
        curr_cond= context->cond_ptr= new Ndb_cond();
        prev_cond->next= curr_cond;
      }
      
      // Check for end of AND/OR expression
      if (!item)
      {
        // End marker for condition group
        DBUG_PRINT("info", ("End of condition group"));
        curr_cond->ndb_item= new Ndb_item(NDB_END_COND);
      }
      else
unknown's avatar
unknown committed
7040 7041 7042 7043
      {
        switch (item->type()) {
        case Item::FIELD_ITEM:
        {
7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056
          Item_field *field_item= (Item_field *) item;
          Field *field= field_item->field;
          enum_field_types type= field->type();
          /*
            Check that the field is part of the table of the handler
            instance and that we expect a field with of this result type.
          */
          if (context->table == field->table)
          {       
            const NDBTAB *tab= (const NDBTAB *) context->ndb_table;
            DBUG_PRINT("info", ("FIELD_ITEM"));
            DBUG_PRINT("info", ("table %s", tab->getName()));
            DBUG_PRINT("info", ("column %s", field->field_name));
7057
            DBUG_PRINT("info", ("type %d", field->type()));
7058 7059 7060 7061 7062
            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) &&
7063
                context->expecting_field_type(field->type()) &&
7064
                (context->expecting_field_result(field->result_type()) ||
unknown's avatar
unknown committed
7065
                 // Date and year can be written as string or int
7066 7067 7068 7069
                 ((type == MYSQL_TYPE_TIME ||
                   type == MYSQL_TYPE_DATE || 
                   type == MYSQL_TYPE_YEAR ||
                   type == MYSQL_TYPE_DATETIME)
unknown's avatar
unknown committed
7070 7071 7072
                  ? (context->expecting_field_result(STRING_RESULT) ||
                     context->expecting_field_result(INT_RESULT))
                  : true)) &&
7073
                // Bit fields no yet supported in scan filter
7074 7075 7076
                type != MYSQL_TYPE_BIT &&
                // No BLOB support in scan filter
                type != MYSQL_TYPE_TINY_BLOB &&
7077 7078
                type != MYSQL_TYPE_MEDIUM_BLOB &&
                type != MYSQL_TYPE_LONG_BLOB &&
7079
                type != MYSQL_TYPE_BLOB)
7080 7081 7082 7083 7084 7085
            {
              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();
7086
              if (! context->expecting_nothing())
7087
              {
7088 7089 7090 7091 7092 7093 7094 7095 7096 7097
                // We have not seen second argument yet
                if (type == MYSQL_TYPE_TIME ||
                    type == MYSQL_TYPE_DATE || 
                    type == MYSQL_TYPE_YEAR ||
                    type == MYSQL_TYPE_DATETIME)
                {
                  context->expect_only(Item::STRING_ITEM);
                  context->expect(Item::INT_ITEM);
                }
                else
unknown's avatar
unknown committed
7098 7099
                  switch (field->result_type()) {
                  case STRING_RESULT:
7100 7101 7102 7103 7104
                    // Expect char string or binary string
                    context->expect_only(Item::STRING_ITEM);
                    context->expect(Item::VARBIN_ITEM);
                    context->expect_collation(field_item->collation.collation);
                    break;
unknown's avatar
unknown committed
7105
                  case REAL_RESULT:
7106 7107
                    context->expect_only(Item::REAL_ITEM);
                    context->expect(Item::DECIMAL_ITEM);
7108
                    context->expect(Item::INT_ITEM);
7109
                    break;
unknown's avatar
unknown committed
7110
                  case INT_RESULT:
7111 7112 7113
                    context->expect_only(Item::INT_ITEM);
                    context->expect(Item::VARBIN_ITEM);
                    break;
unknown's avatar
unknown committed
7114
                  case DECIMAL_RESULT:
7115 7116
                    context->expect_only(Item::DECIMAL_ITEM);
                    context->expect(Item::REAL_ITEM);
7117
                    context->expect(Item::INT_ITEM);
7118 7119 7120 7121
                    break;
                  default:
                    break;
                  }    
7122 7123
              }
              else
7124 7125 7126 7127
              {
                // Expect another logical expression
                context->expect_only(Item::FUNC_ITEM);
                context->expect(Item::COND_ITEM);
7128 7129 7130 7131 7132 7133 7134
                // 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)
7135
                {
unknown's avatar
unknown committed
7136
                  DBUG_PRINT("info", ("Found non-matching collation %s",  
7137 7138
                                      item->collation.collation->name)); 
                  context->supported= FALSE;                
7139 7140
                }
              }
7141 7142
              break;
            }
7143 7144
            else
            {
unknown's avatar
unknown committed
7145 7146
              DBUG_PRINT("info", ("Was not expecting field of type %u(%u)",
                                  field->result_type(), type));
7147
              context->supported= FALSE;
7148
            }
7149
          }
7150
          else
7151 7152 7153 7154
          {
            DBUG_PRINT("info", ("Was not expecting field from table %s(%s)",
                                context->table->s->table_name, 
                                field->table->s->table_name));
7155
            context->supported= FALSE;
7156
          }
7157 7158
          break;
        }
unknown's avatar
unknown committed
7159 7160
        case Item::FUNC_ITEM:
        {
7161 7162 7163 7164 7165 7166
          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
7167
          {
7168 7169 7170
            // Did not expect function here
            context->supported= FALSE;
            break;
7171
          }
7172
          
unknown's avatar
unknown committed
7173 7174 7175
          switch (func_item->functype()) {
          case Item_func::EQ_FUNC:
          {
7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188 7189
            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;
7190
          }
unknown's avatar
unknown committed
7191 7192
          case Item_func::NE_FUNC:
          {
7193 7194 7195 7196 7197 7198 7199 7200 7201 7202 7203 7204 7205 7206
            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;
7207
          }
unknown's avatar
unknown committed
7208 7209
          case Item_func::LT_FUNC:
          {
7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 7224
            DBUG_PRINT("info", ("LT_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::REAL_ITEM);
            context->expect(Item::DECIMAL_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
7225 7226
          case Item_func::LE_FUNC:
          {
7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241
            DBUG_PRINT("info", ("LE_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::REAL_ITEM);
            context->expect(Item::DECIMAL_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
7242 7243
          case Item_func::GE_FUNC:
          {
7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258
            DBUG_PRINT("info", ("GE_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::REAL_ITEM);
            context->expect(Item::DECIMAL_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
7259 7260
          case Item_func::GT_FUNC:
          {
7261 7262 7263 7264 7265 7266 7267 7268 7269 7270 7271 7272 7273 7274 7275
            DBUG_PRINT("info", ("GT_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::REAL_ITEM);
            context->expect(Item::DECIMAL_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
7276 7277
          case Item_func::LIKE_FUNC:
          {
7278 7279 7280 7281 7282
            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);
7283 7284 7285
            context->expect_only_field_type(MYSQL_TYPE_STRING);
            context->expect_field_type(MYSQL_TYPE_VAR_STRING);
            context->expect_field_type(MYSQL_TYPE_VARCHAR);
7286 7287 7288 7289
            context->expect_field_result(STRING_RESULT);
            context->expect(Item::FUNC_ITEM);
            break;
          }
unknown's avatar
unknown committed
7290 7291
          case Item_func::ISNULL_FUNC:
          {
7292 7293 7294 7295 7296 7297 7298 7299 7300 7301
            DBUG_PRINT("info", ("ISNULL_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
7302 7303
          case Item_func::ISNOTNULL_FUNC:
          {
7304 7305 7306 7307 7308 7309 7310 7311 7312 7313
            DBUG_PRINT("info", ("ISNOTNULL_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);     
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
7314 7315
          case Item_func::NOT_FUNC:
          {
7316 7317 7318 7319
            DBUG_PRINT("info", ("NOT_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);     
            context->expect(Item::FUNC_ITEM);
7320
            context->expect(Item::COND_ITEM);
7321
            break;
7322
          }
unknown's avatar
unknown committed
7323 7324
          case Item_func::BETWEEN:
          {
7325
            DBUG_PRINT("info", ("BETWEEN, rewriting using AND"));
7326
            Item_func_between *between_func= (Item_func_between *) func_item;
7327 7328 7329 7330
            Ndb_rewrite_context *rewrite_context= 
              new Ndb_rewrite_context(func_item);
            rewrite_context->next= context->rewrite_stack;
            context->rewrite_stack= rewrite_context;
7331 7332 7333 7334 7335 7336 7337 7338 7339
            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;
            }
7340
            DBUG_PRINT("info", ("COND_AND_FUNC"));
7341 7342 7343
            curr_cond->ndb_item= 
              new Ndb_item(Item_func::COND_AND_FUNC, 
                           func_item->argument_count() - 1);
7344
            context->expect_only(Item::FIELD_ITEM);
7345 7346 7347 7348 7349
            context->expect(Item::INT_ITEM);
            context->expect(Item::STRING_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FUNC_ITEM);
            break;
7350
          }
unknown's avatar
unknown committed
7351 7352
          case Item_func::IN_FUNC:
          {
7353
            DBUG_PRINT("info", ("IN_FUNC, rewriting using OR"));
7354
            Item_func_in *in_func= (Item_func_in *) func_item;
7355 7356 7357 7358
            Ndb_rewrite_context *rewrite_context= 
              new Ndb_rewrite_context(func_item);
            rewrite_context->next= context->rewrite_stack;
            context->rewrite_stack= rewrite_context;
7359 7360 7361 7362 7363 7364 7365 7366 7367
            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;
            }
7368 7369 7370 7371 7372 7373 7374 7375 7376
            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;
7377
          }
unknown's avatar
unknown committed
7378 7379
          case Item_func::UNKNOWN_FUNC:
          {
7380 7381 7382 7383
            DBUG_PRINT("info", ("UNKNOWN_FUNC %s", 
                                func_item->const_item()?"const":""));  
            DBUG_PRINT("info", ("result type %d", func_item->result_type()));
            if (func_item->const_item())
unknown's avatar
unknown committed
7384 7385 7386 7387
            {
              switch (func_item->result_type()) {
              case STRING_RESULT:
              {
7388 7389 7390
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::STRING_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item); 
7391
                if (! context->expecting_no_field_result())
7392 7393 7394 7395 7396 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 7412 7413 7414 7415
                {
                  // We have not seen the field argument yet
                  context->expect_only(Item::FIELD_ITEM);
                  context->expect_only_field_result(STRING_RESULT);
                  context->expect_collation(func_item->collation.collation);
                }
                else
                {
                  // Expect another logical expression
                  context->expect_only(Item::FUNC_ITEM);
                  context->expect(Item::COND_ITEM);
                  // Check that string result have correct collation
                  if (!context->expecting_collation(item->collation.collation))
                  {
                    DBUG_PRINT("info", ("Found non-matching collation %s",  
                                        item->collation.collation->name));
                    context->supported= FALSE;
                  }
                }
                // Skip any arguments since we will evaluate function instead
                DBUG_PRINT("info", ("Skip until end of arguments marker"));
                context->skip= func_item->argument_count();
                break;
              }
unknown's avatar
unknown committed
7416 7417
              case REAL_RESULT:
              {
7418 7419 7420
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::REAL_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
7421
                if (! context->expecting_no_field_result()) 
7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438
                {
                  // We have not seen the field argument yet
                  context->expect_only(Item::FIELD_ITEM);
                  context->expect_only_field_result(REAL_RESULT);
                }
                else
                {
                  // Expect another logical expression
                  context->expect_only(Item::FUNC_ITEM);
                  context->expect(Item::COND_ITEM);
                }
                
                // Skip any arguments since we will evaluate function instead
                DBUG_PRINT("info", ("Skip until end of arguments marker"));
                context->skip= func_item->argument_count();
                break;
              }
unknown's avatar
unknown committed
7439 7440
              case INT_RESULT:
              {
7441 7442 7443
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::INT_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
7444
                if (! context->expecting_no_field_result()) 
7445 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456 7457 7458 7459 7460 7461
                {
                  // We have not seen the field argument yet
                  context->expect_only(Item::FIELD_ITEM);
                  context->expect_only_field_result(INT_RESULT);
                }
                else
                {
                  // Expect another logical expression
                  context->expect_only(Item::FUNC_ITEM);
                  context->expect(Item::COND_ITEM);
                }
                
                // Skip any arguments since we will evaluate function instead
                DBUG_PRINT("info", ("Skip until end of arguments marker"));
                context->skip= func_item->argument_count();
                break;
              }
unknown's avatar
unknown committed
7462 7463
              case DECIMAL_RESULT:
              {
7464 7465 7466
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::DECIMAL_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
7467
                if (! context->expecting_no_field_result()) 
7468 7469 7470 7471 7472 7473 7474 7475 7476 7477 7478 7479 7480 7481 7482 7483 7484 7485 7486
                {
                  // We have not seen the field argument yet
                  context->expect_only(Item::FIELD_ITEM);
                  context->expect_only_field_result(DECIMAL_RESULT);
                }
                else
                {
                  // Expect another logical expression
                  context->expect_only(Item::FUNC_ITEM);
                  context->expect(Item::COND_ITEM);
                }
                // Skip any arguments since we will evaluate function instead
                DBUG_PRINT("info", ("Skip until end of arguments marker"));
                context->skip= func_item->argument_count();
                break;
              }
              default:
                break;
              }
unknown's avatar
unknown committed
7487
            }
7488 7489 7490 7491 7492
            else
              // Function does not return constant expression
              context->supported= FALSE;
            break;
          }
unknown's avatar
unknown committed
7493 7494
          default:
          {
7495 7496 7497
            DBUG_PRINT("info", ("Found func_item of type %d", 
                                func_item->functype()));
            context->supported= FALSE;
7498
          }
7499 7500
          }
          break;
7501
        }
unknown's avatar
unknown committed
7502
        case Item::STRING_ITEM:
7503 7504 7505
          DBUG_PRINT("info", ("STRING_ITEM")); 
          if (context->expecting(Item::STRING_ITEM)) 
          {
7506
#ifndef DBUG_OFF
7507 7508 7509 7510 7511 7512
            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()));
7513
#endif
7514 7515 7516
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::STRING_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);      
7517
            if (! context->expecting_no_field_result())
7518 7519 7520 7521 7522 7523 7524 7525 7526 7527 7528 7529 7530 7531 7532 7533 7534 7535 7536 7537 7538 7539 7540
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(STRING_RESULT);
              context->expect_collation(item->collation.collation);
            }
            else 
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
              // Check that we are comparing with a field with same collation
              if (!context->expecting_collation(item->collation.collation))
              {
                DBUG_PRINT("info", ("Found non-matching collation %s",  
                                    item->collation.collation->name));
                context->supported= FALSE;
              }
            }
          }
          else
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
7541
        case Item::INT_ITEM:
7542 7543
          DBUG_PRINT("info", ("INT_ITEM"));
          if (context->expecting(Item::INT_ITEM)) 
7544
          {
7545 7546 7547 7548 7549
            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);
7550
            if (! context->expecting_no_field_result()) 
7551 7552 7553 7554
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(INT_RESULT);
7555 7556
              context->expect_field_result(REAL_RESULT);
              context->expect_field_result(DECIMAL_RESULT);
7557 7558 7559 7560 7561 7562 7563
            }
            else
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
            }
7564 7565
          }
          else
7566 7567
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
7568
        case Item::REAL_ITEM:
7569 7570
          DBUG_PRINT("info", ("REAL_ITEM %s"));
          if (context->expecting(Item::REAL_ITEM)) 
7571
          {
7572 7573 7574 7575 7576
            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);
7577
            if (! context->expecting_no_field_result()) 
7578 7579 7580 7581 7582 7583 7584 7585 7586 7587 7588
            {
              // 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);
            }
7589
          }
7590 7591 7592
          else
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
7593
        case Item::VARBIN_ITEM:
7594 7595
          DBUG_PRINT("info", ("VARBIN_ITEM"));
          if (context->expecting(Item::VARBIN_ITEM)) 
7596
          {
7597 7598 7599
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::VARBIN_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);      
7600
            if (! context->expecting_no_field_result())
7601 7602 7603 7604 7605 7606 7607 7608 7609 7610 7611
            {
              // 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);
            }
7612 7613
          }
          else
7614 7615
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
7616
        case Item::DECIMAL_ITEM:
7617 7618
          DBUG_PRINT("info", ("DECIMAL_ITEM %s"));
          if (context->expecting(Item::DECIMAL_ITEM)) 
7619
          {
7620 7621 7622 7623 7624
            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);
7625
            if (! context->expecting_no_field_result()) 
7626 7627 7628 7629 7630 7631 7632 7633 7634 7635 7636 7637
            {
              // 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);
            }
7638
          }
7639 7640 7641
          else
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
7642 7643
        case Item::COND_ITEM:
        {
7644 7645 7646
          Item_cond *cond_item= (Item_cond *) item;
          
          if (context->expecting(Item::COND_ITEM))
unknown's avatar
unknown committed
7647 7648 7649
          {
            switch (cond_item->functype()) {
            case Item_func::COND_AND_FUNC:
7650 7651 7652 7653
              DBUG_PRINT("info", ("COND_AND_FUNC"));
              curr_cond->ndb_item= new Ndb_item(cond_item->functype(),
                                                cond_item);      
              break;
unknown's avatar
unknown committed
7654
            case Item_func::COND_OR_FUNC:
7655 7656 7657 7658 7659 7660 7661 7662 7663
              DBUG_PRINT("info", ("COND_OR_FUNC"));
              curr_cond->ndb_item= new Ndb_item(cond_item->functype(),
                                                cond_item);      
              break;
            default:
              DBUG_PRINT("info", ("COND_ITEM %d", cond_item->functype()));
              context->supported= FALSE;
              break;
            }
unknown's avatar
unknown committed
7664
          }
7665
          else
unknown's avatar
unknown committed
7666 7667
          {
            /* Did not expect condition */
7668
            context->supported= FALSE;          
unknown's avatar
unknown committed
7669
          }
7670
          break;
7671
        }
unknown's avatar
unknown committed
7672 7673
        default:
        {
7674
          DBUG_PRINT("info", ("Found item of type %d", item->type()));
7675
          context->supported= FALSE;
7676 7677
        }
        }
unknown's avatar
unknown committed
7678
      }
7679 7680 7681 7682 7683 7684 7685 7686 7687 7688
      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();
7689
          curr_cond->prev= prev_cond;
7690 7691 7692
          prev_cond->next= curr_cond;
          curr_cond->ndb_item= new Ndb_item(NDB_END_COND);
          // Pop rewrite stack
unknown's avatar
unknown committed
7693 7694 7695
          context->rewrite_stack=  rewrite_context->next;
          rewrite_context->next= NULL;
          delete(rewrite_context);
7696
        }
7697
      }
7698
    }
7699
  }
7700
 
7701 7702 7703 7704 7705 7706 7707 7708
  DBUG_VOID_RETURN;
}

bool
ha_ndbcluster::serialize_cond(const COND *cond, Ndb_cond_stack *ndb_cond)
{
  DBUG_ENTER("serialize_cond");
  Item *item= (Item *) cond;
7709
  Ndb_cond_traverse_context context(table, (void *)m_table, ndb_cond);
7710 7711 7712
  // Expect a logical expression
  context.expect(Item::FUNC_ITEM);
  context.expect(Item::COND_ITEM);
7713
  item->traverse_cond(&ndb_serialize_cond, (void *) &context, Item::PREFIX);
7714
  DBUG_PRINT("info", ("The pushed condition is %ssupported", (context.supported)?"":"not "));
7715

7716
  DBUG_RETURN(context.supported);
7717 7718
}

7719 7720
int
ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, 
7721 7722
                                           NdbScanFilter *filter,
                                           bool negated)
7723 7724
{
  DBUG_ENTER("build_scan_filter_predicate");  
unknown's avatar
unknown committed
7725 7726 7727
  switch (cond->ndb_item->type) {
  case NDB_FUNCTION:
  {
7728 7729 7730
    if (!cond->next)
      break;
    Ndb_item *a= cond->next->ndb_item;
7731
    Ndb_item *b, *field, *value= NULL;
7732 7733
    LINT_INIT(field);

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

7958
int
7959
ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter)
7960
{
7961
  uint level=0;
unknown's avatar
unknown committed
7962
  bool negated= FALSE;
7963
  DBUG_ENTER("build_scan_filter_group");
unknown's avatar
unknown committed
7964

7965 7966
  do
  {
unknown's avatar
unknown committed
7967 7968 7969 7970 7971 7972
    if (!cond)
      DBUG_RETURN(1);
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
    {
      switch (cond->ndb_item->qualification.function_type) {
7973
      case NDB_COND_AND_FUNC:
unknown's avatar
unknown committed
7974
      {
7975 7976 7977 7978 7979
        level++;
        DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NAND":"AND",
                            level));
        if ((negated) ? filter->begin(NdbScanFilter::NAND)
            : filter->begin(NdbScanFilter::AND) == -1)
7980
          DBUG_RETURN(1);
unknown's avatar
unknown committed
7981
        negated= FALSE;
7982 7983 7984
        cond= cond->next;
        break;
      }
7985
      case NDB_COND_OR_FUNC:
unknown's avatar
unknown committed
7986
      {
7987 7988 7989 7990 7991 7992
        level++;
        DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NOR":"OR",
                            level));
        if ((negated) ? filter->begin(NdbScanFilter::NOR)
            : filter->begin(NdbScanFilter::OR) == -1)
          DBUG_RETURN(1);
unknown's avatar
unknown committed
7993
        negated= FALSE;
7994 7995 7996
        cond= cond->next;
        break;
      }
7997
      case NDB_NOT_FUNC:
unknown's avatar
unknown committed
7998
      {
7999
        DBUG_PRINT("info", ("Generating negated query"));
8000
        cond= cond->next;
unknown's avatar
unknown committed
8001
        negated= TRUE;
8002 8003 8004 8005
        break;
      }
      default:
        if (build_scan_filter_predicate(cond, filter, negated))
8006
          DBUG_RETURN(1);
unknown's avatar
unknown committed
8007
        negated= FALSE;
8008 8009 8010
        break;
      }
      break;
unknown's avatar
unknown committed
8011 8012
    }
    case NDB_END_COND:
8013 8014
      DBUG_PRINT("info", ("End of group %u", level));
      level--;
8015 8016
      if (cond) cond= cond->next;
      if (filter->end() == -1)
8017
        DBUG_RETURN(1);
8018 8019 8020
      if (!negated)
        break;
      // else fall through (NOT END is an illegal condition)
unknown's avatar
unknown committed
8021 8022
    default:
    {
8023
      DBUG_PRINT("info", ("Illegal scan filter"));
8024
    }
8025
    }
8026
  }  while (level > 0 || negated);
8027
  
8028
  DBUG_RETURN(0);
8029 8030
}

8031 8032
int
ha_ndbcluster::build_scan_filter(Ndb_cond * &cond, NdbScanFilter *filter)
8033 8034 8035 8036
{
  bool simple_cond= TRUE;
  DBUG_ENTER("build_scan_filter");  

unknown's avatar
unknown committed
8037 8038 8039
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
      switch (cond->ndb_item->qualification.function_type) {
8040 8041
      case NDB_COND_AND_FUNC:
      case NDB_COND_OR_FUNC:
8042 8043 8044 8045 8046 8047 8048 8049 8050
        simple_cond= FALSE;
        break;
      default:
        break;
      }
      break;
    default:
      break;
    }
8051 8052 8053 8054 8055 8056
  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);
8057

8058
  DBUG_RETURN(0);
8059 8060
}

8061
int
8062
ha_ndbcluster::generate_scan_filter(Ndb_cond_stack *ndb_cond_stack,
8063
                                    NdbScanOperation *op)
8064 8065
{
  DBUG_ENTER("generate_scan_filter");
8066

8067 8068 8069
  if (ndb_cond_stack)
  {
    NdbScanFilter filter(op);
8070 8071
    
    DBUG_RETURN(generate_scan_filter_from_cond(ndb_cond_stack, filter));
8072 8073 8074 8075 8076 8077
  }
  else
  {  
    DBUG_PRINT("info", ("Empty stack"));
  }

8078
  DBUG_RETURN(0);
8079 8080
}

8081 8082 8083 8084 8085 8086 8087 8088 8089 8090 8091 8092 8093 8094 8095 8096 8097 8098 8099 8100 8101 8102 8103 8104 8105 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
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);
}

8163 8164 8165 8166 8167 8168 8169 8170 8171 8172
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,
unknown's avatar
unknown committed
8173 8174
	       "Cannot call SHOW NDBCLUSTER STATUS because skip-ndbcluster is "
               "defined",
8175 8176 8177 8178 8179 8180 8181 8182 8183 8184
	       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));

unknown's avatar
unknown committed
8185 8186
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
8187 8188
    DBUG_RETURN(TRUE);
  
unknown's avatar
unknown committed
8189
  if (get_thd_ndb(thd) && get_thd_ndb(thd)->ndb)
8190
  {
unknown's avatar
unknown committed
8191
    Ndb* ndb= (get_thd_ndb(thd))->ndb;
unknown's avatar
unknown committed
8192 8193
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
8194 8195 8196 8197 8198 8199 8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210
    while (ndb->get_free_list_usage(&tmp))
    {
      protocol->prepare_for_resend();
      
      protocol->store(tmp.m_name, &my_charset_bin);
      protocol->store((uint)tmp.m_created);
      protocol->store((uint)tmp.m_free);
      protocol->store((uint)tmp.m_sizeof);
      if (protocol->write())
	DBUG_RETURN(TRUE);
    }
  }
  send_eof(thd);
  
  DBUG_RETURN(FALSE);
}

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