ha_ndbcluster.cc 284 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
#endif

#include "mysql_priv.h"

#include <my_dir.h>
#include "ha_ndbcluster.h"
#include <ndbapi/NdbApi.hpp>
#include <ndbapi/NdbScanFilter.hpp>
unknown's avatar
unknown committed
33
#include <../util/Bitmask.hpp>
34
#include <ndbapi/NdbIndexStat.hpp>
unknown's avatar
unknown committed
35

unknown's avatar
unknown committed
36
#include "ha_ndbcluster_binlog.h"
37
#include "ha_ndbcluster_tables.h"
unknown's avatar
unknown committed
38

39 40 41 42 43
#ifdef ndb_dynamite
#undef assert
#define assert(x) do { if(x) break; ::printf("%s %d: assert failed: %s\n", __FILE__, __LINE__, #x); ::fflush(stdout); ::signal(SIGABRT,SIG_DFL); ::abort(); ::kill(::getpid(),6); ::kill(::getpid(),9); } while (0)
#endif

44 45 46 47
// options from from mysqld.cc
extern my_bool opt_ndb_optimized_node_selection;
extern const char *opt_ndbcluster_connectstring;

48 49 50 51 52 53
const char *ndb_distribution_names[]= {"KEYHASH", "LINHASH", NullS};
TYPELIB ndb_distribution_typelib= { array_elements(ndb_distribution_names)-1,
                                    "", ndb_distribution_names, NULL };
const char *opt_ndb_distribution= ndb_distribution_names[ND_KEYHASH];
enum ndb_distribution opt_ndb_distribution_id= ND_KEYHASH;

unknown's avatar
unknown committed
54
// Default value for parallelism
55
static const int parallelism= 0;
unknown's avatar
unknown committed
56

57 58
// Default value for max number of transactions
// createable against NDB from this handler
unknown's avatar
unknown committed
59
static const int max_transactions= 3; // should really be 2 but there is a transaction to much allocated when loch table is used
60

unknown's avatar
unknown committed
61 62
static uint ndbcluster_partition_flags();
static uint ndbcluster_alter_table_flags(uint flags);
unknown's avatar
unknown committed
63 64 65
static bool ndbcluster_init(void);
static int ndbcluster_end(ha_panic_function flag);
static bool ndbcluster_show_status(THD*,stat_print_fn *,enum ha_stat_type);
unknown's avatar
unknown committed
66
static int ndbcluster_alter_tablespace(THD* thd, st_alter_tablespace *info);
67
static int ndbcluster_fill_files_table(THD *thd, TABLE_LIST *tables, COND *cond);
68

69
handlerton ndbcluster_hton = {
unknown's avatar
unknown committed
70
  MYSQL_HANDLERTON_INTERFACE_VERSION,
unknown's avatar
unknown committed
71
  "ndbcluster",
72
  SHOW_OPTION_YES,
73
  "Clustered, fault-tolerant tables", 
74 75
  DB_TYPE_NDBCLUSTER,
  ndbcluster_init,
unknown's avatar
unknown committed
76
  ~(uint)0, /* slot */
77 78
};

unknown's avatar
unknown committed
79
static handler *ndbcluster_create_handler(TABLE_SHARE *table)
80 81 82 83
{
  return new ha_ndbcluster(table);
}

unknown's avatar
unknown committed
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
static uint ndbcluster_partition_flags()
{
  return (HA_CAN_PARTITION | HA_CAN_UPDATE_PARTITION_KEY |
          HA_CAN_PARTITION_UNIQUE | HA_USE_AUTO_PARTITION);
}

static uint ndbcluster_alter_table_flags(uint flags)
{
  if (flags & ALTER_DROP_PARTITION)
    return 0;
  else
    return (HA_ONLINE_ADD_INDEX | HA_ONLINE_DROP_INDEX |
            HA_ONLINE_ADD_UNIQUE_INDEX | HA_ONLINE_DROP_UNIQUE_INDEX |
            HA_PARTITION_FUNCTION_SUPPORTED);

}

101
#define NDB_FAILED_AUTO_INCREMENT ~(Uint64)0
102
#define NDB_AUTO_INCREMENT_RETRIES 10
unknown's avatar
unknown committed
103

104 105
#define NDB_INVALID_SCHEMA_OBJECT 241

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

109 110
#define ERR_RETURN(err)                  \
{                                        \
111
  const NdbError& tmp= err;              \
112
  ERR_PRINT(tmp);                        \
113
  DBUG_RETURN(ndb_to_mysql_error(&tmp)); \
unknown's avatar
unknown committed
114 115
}

116 117 118 119 120 121 122 123
#define ERR_BREAK(err, code)             \
{                                        \
  const NdbError& tmp= err;              \
  ERR_PRINT(tmp);                        \
  code= ndb_to_mysql_error(&tmp);        \
  break;                                 \
}

unknown's avatar
unknown committed
124
static int ndbcluster_inited= 0;
unknown's avatar
unknown committed
125
int ndbcluster_util_inited= 0;
unknown's avatar
unknown committed
126

127
static Ndb* g_ndb= NULL;
unknown's avatar
unknown committed
128 129
Ndb_cluster_connection* g_ndb_cluster_connection= NULL;
unsigned char g_node_id_map[max_ndb_nodes];
130

unknown's avatar
unknown committed
131 132 133 134
// Handler synchronization
pthread_mutex_t ndbcluster_mutex;

// Table lock handling
unknown's avatar
unknown committed
135
HASH ndbcluster_open_tables;
unknown's avatar
unknown committed
136 137 138

static byte *ndbcluster_get_key(NDB_SHARE *share,uint *length,
                                my_bool not_used __attribute__((unused)));
unknown's avatar
unknown committed
139 140 141
#ifdef HAVE_NDB_BINLOG
static int rename_share(NDB_SHARE *share, const char *new_key);
#endif
unknown's avatar
unknown committed
142
static void ndb_set_fragmentation(NDBTAB &tab, TABLE *table, uint pk_len);
unknown's avatar
unknown committed
143

unknown's avatar
unknown committed
144
static int ndb_get_table_statistics(Ndb*, const char *, 
145
                                    struct Ndb_statistics *);
146

unknown's avatar
unknown committed
147

unknown's avatar
Merge  
unknown committed
148
// Util thread variables
unknown's avatar
unknown committed
149
pthread_t ndb_util_thread;
unknown's avatar
Merge  
unknown committed
150 151
pthread_mutex_t LOCK_ndb_util_thread;
pthread_cond_t COND_ndb_util_thread;
152
pthread_handler_t ndb_util_thread_func(void *arg);
unknown's avatar
Merge  
unknown committed
153
ulong ndb_cache_check_time;
unknown's avatar
unknown committed
154

155 156 157 158
/*
  Dummy buffer to read zero pack_length fields
  which are mapped to 1 char
*/
unknown's avatar
unknown committed
159
static uint32 dummy_buf;
160

161 162 163 164 165 166 167 168 169 170 171
/*
  Stats that can be retrieved from ndb
*/

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

172 173 174 175 176 177
/* 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;
unknown's avatar
unknown committed
178
long ndb_number_of_storage_nodes= 0;
179 180 181 182 183 184 185 186 187 188 189

static int update_status_variables(Ndb_cluster_connection *c)
{
  ndb_cluster_node_id=         c->node_id();
  ndb_connected_port=          c->get_connected_port();
  ndb_connected_host=          c->get_connected_host();
  ndb_number_of_replicas=      0;
  ndb_number_of_storage_nodes= c->no_db_nodes();
  return 0;
}

unknown's avatar
unknown committed
190
SHOW_VAR ndb_status_variables[]= {
191 192 193 194 195 196 197 198
  {"cluster_node_id",        (char*) &ndb_cluster_node_id,         SHOW_LONG},
  {"connected_host",         (char*) &ndb_connected_host,      SHOW_CHAR_PTR},
  {"connected_port",         (char*) &ndb_connected_port,          SHOW_LONG},
//  {"number_of_replicas",     (char*) &ndb_number_of_replicas,      SHOW_LONG},
  {"number_of_storage_nodes",(char*) &ndb_number_of_storage_nodes, SHOW_LONG},
  {NullS, NullS, SHOW_LONG}
};

unknown's avatar
unknown committed
199 200 201 202
/*
  Error handling functions
*/

unknown's avatar
unknown committed
203
/* Note for merge: old mapping table, moved to storage/ndb/ndberror.c */
unknown's avatar
unknown committed
204

unknown's avatar
unknown committed
205
static int ndb_to_mysql_error(const NdbError *ndberr)
unknown's avatar
unknown committed
206
{
unknown's avatar
unknown committed
207 208
  /* read the mysql mapped error code */
  int error= ndberr->mysql_code;
209

unknown's avatar
unknown committed
210 211 212 213 214 215 216 217 218 219 220 221 222
  switch (error)
  {
    /* errors for which we do not add warnings, just return mapped error code
    */
  case HA_ERR_NO_SUCH_TABLE:
  case HA_ERR_KEY_NOT_FOUND:
  case HA_ERR_FOUND_DUPP_KEY:
    return error;

    /* Mapping missing, go with the ndb error code*/
  case -1:
    error= ndberr->code;
    break;
unknown's avatar
unknown committed
223

unknown's avatar
unknown committed
224 225 226 227
    /* Mapping exists, go with the mapped code */
  default:
    break;
  }
unknown's avatar
unknown committed
228

unknown's avatar
unknown committed
229 230 231 232 233 234
  /*
    Push the NDB error message as warning
    - Used to be able to use SHOW WARNINGS toget more info on what the error is
    - Used by replication to see if the error was temporary
  */
  if (ndberr->status == NdbError::TemporaryError)
235
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
unknown's avatar
unknown committed
236 237 238 239 240 241 242
			ER_GET_TEMPORARY_ERRMSG, ER(ER_GET_TEMPORARY_ERRMSG),
			ndberr->code, ndberr->message, "NDB");
  else
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
			ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
			ndberr->code, ndberr->message, "NDB");
  return error;
unknown's avatar
unknown committed
243 244
}

unknown's avatar
unknown committed
245 246 247 248 249 250 251 252 253 254 255 256
int execute_no_commit_ignore_no_key(ha_ndbcluster *h, NdbTransaction *trans)
{
  int res= trans->execute(NdbTransaction::NoCommit,
                          NdbTransaction::AO_IgnoreError,
                          h->m_force_send);
  if (res == 0)
    return 0;

  const NdbError &err= trans->getNdbError();
  if (err.classification != NdbError::ConstraintViolation &&
      err.classification != NdbError::NoDataFound)
    return res;
unknown's avatar
unknown committed
257

unknown's avatar
unknown committed
258 259
  return 0;
}
unknown's avatar
unknown committed
260 261

inline
262
int execute_no_commit(ha_ndbcluster *h, NdbTransaction *trans)
unknown's avatar
unknown committed
263
{
unknown's avatar
unknown committed
264
#ifdef NOT_USED
265
  int m_batch_execute= 0;
unknown's avatar
unknown committed
266
  if (m_batch_execute)
unknown's avatar
unknown committed
267
    return 0;
unknown's avatar
unknown committed
268
#endif
unknown's avatar
unknown committed
269 270 271 272 273
  return h->m_ignore_no_key ?
    execute_no_commit_ignore_no_key(h,trans) :
    trans->execute(NdbTransaction::NoCommit,
		   NdbTransaction::AbortOnError,
		   h->m_force_send);
unknown's avatar
unknown committed
274 275 276
}

inline
277
int execute_commit(ha_ndbcluster *h, NdbTransaction *trans)
unknown's avatar
unknown committed
278
{
unknown's avatar
unknown committed
279
#ifdef NOT_USED
280
  int m_batch_execute= 0;
unknown's avatar
unknown committed
281
  if (m_batch_execute)
unknown's avatar
unknown committed
282
    return 0;
unknown's avatar
unknown committed
283
#endif
284
  return trans->execute(NdbTransaction::Commit,
285 286
                        NdbTransaction::AbortOnError,
                        h->m_force_send);
287 288 289
}

inline
290
int execute_commit(THD *thd, NdbTransaction *trans)
291 292
{
#ifdef NOT_USED
293
  int m_batch_execute= 0;
294 295 296
  if (m_batch_execute)
    return 0;
#endif
297
  return trans->execute(NdbTransaction::Commit,
298 299
                        NdbTransaction::AbortOnError,
                        thd->variables.ndb_force_send);
unknown's avatar
unknown committed
300 301 302
}

inline
303
int execute_no_commit_ie(ha_ndbcluster *h, NdbTransaction *trans)
unknown's avatar
unknown committed
304
{
unknown's avatar
unknown committed
305
#ifdef NOT_USED
306
  int m_batch_execute= 0;
unknown's avatar
unknown committed
307
  if (m_batch_execute)
unknown's avatar
unknown committed
308
    return 0;
unknown's avatar
unknown committed
309
#endif
310
  return trans->execute(NdbTransaction::NoCommit,
311 312
                        NdbTransaction::AO_IgnoreError,
                        h->m_force_send);
unknown's avatar
unknown committed
313 314
}

315 316 317
/*
  Place holder for ha_ndbcluster thread specific data
*/
318 319
Thd_ndb::Thd_ndb()
{
320
  ndb= new Ndb(g_ndb_cluster_connection, "");
321 322
  lock_count= 0;
  count= 0;
323 324
  all= NULL;
  stmt= NULL;
325
  error= 0;
unknown's avatar
unknown committed
326
  options= 0;
327 328 329 330
}

Thd_ndb::~Thd_ndb()
{
331
  if (ndb)
332 333
  {
#ifndef DBUG_OFF
unknown's avatar
unknown committed
334 335
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
336 337 338 339 340 341 342 343 344 345
    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
346
    delete ndb;
unknown's avatar
unknown committed
347
    ndb= NULL;
348
  }
349
  changed_tables.empty();
350 351
}

352 353 354
inline
Ndb *ha_ndbcluster::get_ndb()
{
355
  return get_thd_ndb(current_thd)->ndb;
356 357 358 359 360 361
}

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

362
struct Ndb_local_table_statistics {
363
  int no_uncommitted_rows_count;
364
  ulong last_count;
365 366 367
  ha_rows records;
};

unknown's avatar
unknown committed
368 369 370
void ha_ndbcluster::set_rec_per_key()
{
  DBUG_ENTER("ha_ndbcluster::get_status_const");
unknown's avatar
unknown committed
371
  for (uint i=0 ; i < table_share->keys ; i++)
unknown's avatar
unknown committed
372 373 374 375 376 377
  {
    table->key_info[i].rec_per_key[table->key_info[i].key_parts-1]= 1;
  }
  DBUG_VOID_RETURN;
}

378 379
void ha_ndbcluster::records_update()
{
380 381
  if (m_ha_not_exact_count)
    return;
382
  DBUG_ENTER("ha_ndbcluster::records_update");
383 384
  struct Ndb_local_table_statistics *info= 
    (struct Ndb_local_table_statistics *)m_table_info;
385
  DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
386 387
                      ((const NDBTAB *)m_table)->getTableId(),
                      info->no_uncommitted_rows_count));
388
  //  if (info->records == ~(ha_rows)0)
389
  {
390
    Ndb *ndb= get_ndb();
391
    struct Ndb_statistics stat;
unknown's avatar
unknown committed
392
    if (ndb_get_table_statistics(ndb, m_tabname, &stat) == 0){
393 394 395
      mean_rec_length= stat.row_size;
      data_file_length= stat.fragment_memory;
      info->records= stat.row_count;
396 397
    }
  }
398 399
  {
    THD *thd= current_thd;
400
    if (get_thd_ndb(thd)->error)
401 402
      info->no_uncommitted_rows_count= 0;
  }
403 404 405 406
  records= info->records+ info->no_uncommitted_rows_count;
  DBUG_VOID_RETURN;
}

407 408
void ha_ndbcluster::no_uncommitted_rows_execute_failure()
{
409 410
  if (m_ha_not_exact_count)
    return;
411
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_execute_failure");
412
  get_thd_ndb(current_thd)->error= 1;
413 414 415
  DBUG_VOID_RETURN;
}

416 417
void ha_ndbcluster::no_uncommitted_rows_init(THD *thd)
{
418 419
  if (m_ha_not_exact_count)
    return;
420
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_init");
421 422
  struct Ndb_local_table_statistics *info= 
    (struct Ndb_local_table_statistics *)m_table_info;
423
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
424
  if (info->last_count != thd_ndb->count)
425
  {
426
    info->last_count= thd_ndb->count;
427 428 429
    info->no_uncommitted_rows_count= 0;
    info->records= ~(ha_rows)0;
    DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
430 431
                        ((const NDBTAB *)m_table)->getTableId(),
                        info->no_uncommitted_rows_count));
432 433 434 435 436 437
  }
  DBUG_VOID_RETURN;
}

void ha_ndbcluster::no_uncommitted_rows_update(int c)
{
438 439
  if (m_ha_not_exact_count)
    return;
440
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_update");
441 442
  struct Ndb_local_table_statistics *info=
    (struct Ndb_local_table_statistics *)m_table_info;
443 444
  info->no_uncommitted_rows_count+= c;
  DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
445 446
                      ((const NDBTAB *)m_table)->getTableId(),
                      info->no_uncommitted_rows_count));
447 448 449 450 451
  DBUG_VOID_RETURN;
}

void ha_ndbcluster::no_uncommitted_rows_reset(THD *thd)
{
452 453
  if (m_ha_not_exact_count)
    return;
454
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_reset");
455 456 457
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  thd_ndb->count++;
  thd_ndb->error= 0;
458 459 460
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
461 462
/*
  Take care of the error that occured in NDB
463

unknown's avatar
unknown committed
464
  RETURN
465
    0   No error
unknown's avatar
unknown committed
466 467 468
    #   The mapped error code
*/

unknown's avatar
unknown committed
469
int
unknown's avatar
unknown committed
470
ha_ndbcluster::invalidate_dictionary_cache(TABLE_SHARE *share, Ndb *ndb,
471 472
                                           const char *dbname, const char *tabname,
                                           bool global)
473
{
unknown's avatar
unknown committed
474
  NDBDICT *dict= ndb->getDictionary();
475
  DBUG_ENTER("invalidate_dictionary_cache");
unknown's avatar
unknown committed
476
  DBUG_PRINT("info", ("invalidating %s", tabname));
477

unknown's avatar
unknown committed
478 479
#ifdef HAVE_NDB_BINLOG
  char key[FN_REFLEN];
480
  build_table_filename(key, sizeof(key), dbname, tabname, "");
unknown's avatar
unknown committed
481 482 483 484 485 486 487 488 489 490 491
  DBUG_PRINT("info", ("Getting ndbcluster mutex"));
  pthread_mutex_lock(&ndbcluster_mutex);
  NDB_SHARE *ndb_share= (NDB_SHARE*)hash_search(&ndbcluster_open_tables,
                                                (byte*) key, strlen(key));
  pthread_mutex_unlock(&ndbcluster_mutex);
  DBUG_PRINT("info", ("Released ndbcluster mutex"));
  // Only binlog_thread is allowed to globally invalidate a table
  if (global && ndb_share && ndb_share->op && (current_thd != injector_thd))
    DBUG_RETURN(1);
#endif

492
  if (global)
493
  {
unknown's avatar
unknown committed
494
    const NDBTAB *tab= dict->getTable(tabname);
495
    if (!tab)
unknown's avatar
unknown committed
496
      DBUG_RETURN(1);
497
    if (tab->getObjectStatus() == NdbDictionary::Object::Invalid)
498 499
    {
      // Global cache has already been invalidated
unknown's avatar
unknown committed
500
      dict->removeCachedTable(tabname);
501 502 503
      global= FALSE;
    }
    else
unknown's avatar
unknown committed
504
      dict->invalidateTable(tabname);
505
  }
506
  else
unknown's avatar
unknown committed
507
    dict->removeCachedTable(tabname);
unknown's avatar
unknown committed
508
  share->version=0L;			/* Free when thread is ready */
unknown's avatar
unknown committed
509
  DBUG_RETURN(0);
unknown's avatar
unknown committed
510 511 512 513 514
}

void ha_ndbcluster::invalidate_dictionary_cache(bool global)
{
  NDBDICT *dict= get_ndb()->getDictionary();
515
  if (invalidate_dictionary_cache(table_share, get_ndb(), m_dbname, m_tabname, global))
unknown's avatar
unknown committed
516
    return;
517
  /* Invalidate indexes */
unknown's avatar
unknown committed
518
  for (uint i= 0; i < table_share->keys; i++)
519 520 521
  {
    NDBINDEX *index = (NDBINDEX *) m_index[i].index;
    NDBINDEX *unique_index = (NDBINDEX *) m_index[i].unique_index;
522
    if (!index && !unique_index) continue;
523 524
    NDB_INDEX_TYPE idx_type= m_index[i].type;

unknown's avatar
unknown committed
525 526 527
    switch (idx_type) {
    case PRIMARY_KEY_ORDERED_INDEX:
    case ORDERED_INDEX:
528 529 530 531
      if (global)
        dict->invalidateIndex(index->getName(), m_tabname);
      else
        dict->removeCachedIndex(index->getName(), m_tabname);
unknown's avatar
unknown committed
532
      break;
unknown's avatar
unknown committed
533
    case UNIQUE_ORDERED_INDEX:
534 535 536 537
      if (global)
        dict->invalidateIndex(index->getName(), m_tabname);
      else
        dict->removeCachedIndex(index->getName(), m_tabname);
unknown's avatar
unknown committed
538
    case UNIQUE_INDEX:
539 540 541 542
      if (global)
        dict->invalidateIndex(unique_index->getName(), m_tabname);
      else
        dict->removeCachedIndex(unique_index->getName(), m_tabname);
543
      break;
unknown's avatar
unknown committed
544 545
    case PRIMARY_KEY_INDEX:
    case UNDEFINED_INDEX:
546 547 548 549
      break;
    }
  }
}
550

551
int ha_ndbcluster::ndb_err(NdbTransaction *trans)
unknown's avatar
unknown committed
552
{
553
  int res;
554
  NdbError err= trans->getNdbError();
unknown's avatar
unknown committed
555 556 557 558 559
  DBUG_ENTER("ndb_err");
  
  ERR_PRINT(err);
  switch (err.classification) {
  case NdbError::SchemaError:
560
    invalidate_dictionary_cache(TRUE);
561 562 563 564 565 566 567 568 569 570 571 572 573

    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));
unknown's avatar
unknown committed
574
        if (err.code != 709 && err.code != 723)
575 576
          DBUG_RETURN(1);
      }
577
      DBUG_PRINT("info", ("Table exists but must have changed"));
578
    }
unknown's avatar
unknown committed
579 580 581 582
    break;
  default:
    break;
  }
583 584
  res= ndb_to_mysql_error(&err);
  DBUG_PRINT("info", ("transformed ndbcluster error %d to mysql error %d", 
585
                      err.code, res));
586
  if (res == HA_ERR_FOUND_DUPP_KEY)
587 588
  {
    if (m_rows_to_insert == 1)
unknown's avatar
unknown committed
589
      m_dupkey= table_share->primary_key;
590
    else
unknown's avatar
unknown committed
591 592
    {
      /* We are batching inserts, offending key is not available */
593
      m_dupkey= (uint) -1;
unknown's avatar
unknown committed
594
    }
595
  }
596
  DBUG_RETURN(res);
unknown's avatar
unknown committed
597 598 599
}


600
/*
601
  Override the default get_error_message in order to add the 
602 603 604
  error message of NDB 
 */

605
bool ha_ndbcluster::get_error_message(int error, 
606
                                      String *buf)
607
{
608
  DBUG_ENTER("ha_ndbcluster::get_error_message");
609
  DBUG_PRINT("enter", ("error: %d", error));
610

611
  Ndb *ndb= get_ndb();
612
  if (!ndb)
unknown's avatar
unknown committed
613
    DBUG_RETURN(FALSE);
614

615
  const NdbError err= ndb->getNdbError(error);
616 617 618 619
  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);
620 621 622
}


unknown's avatar
unknown committed
623
#ifndef DBUG_OFF
unknown's avatar
unknown committed
624 625 626 627
/*
  Check if type is supported by NDB.
*/

unknown's avatar
unknown committed
628
static bool ndb_supported_type(enum_field_types type)
unknown's avatar
unknown committed
629 630
{
  switch (type) {
unknown's avatar
unknown committed
631 632 633 634 635 636 637
  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:
638 639
  case MYSQL_TYPE_DECIMAL:    
  case MYSQL_TYPE_NEWDECIMAL:
unknown's avatar
unknown committed
640 641 642 643 644 645 646 647
  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
648
  case MYSQL_TYPE_VARCHAR:
unknown's avatar
unknown committed
649 650 651 652 653 654
  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:         
655
  case MYSQL_TYPE_BIT:
656
  case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
657
    return TRUE;
unknown's avatar
unknown committed
658
  case MYSQL_TYPE_NULL:   
unknown's avatar
unknown committed
659
    break;
unknown's avatar
unknown committed
660
  }
unknown's avatar
unknown committed
661
  return FALSE;
unknown's avatar
unknown committed
662
}
unknown's avatar
unknown committed
663
#endif /* !DBUG_OFF */
unknown's avatar
unknown committed
664 665


unknown's avatar
unknown committed
666 667 668 669 670
/*
  Instruct NDB to set the value of the hidden primary key
*/

bool ha_ndbcluster::set_hidden_key(NdbOperation *ndb_op,
671
                                   uint fieldnr, const byte *field_ptr)
unknown's avatar
unknown committed
672 673
{
  DBUG_ENTER("set_hidden_key");
unknown's avatar
unknown committed
674
  DBUG_RETURN(ndb_op->equal(fieldnr, (char*)field_ptr) != 0);
unknown's avatar
unknown committed
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
}


/*
  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
692 693 694 695
  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
696 697 698 699 700 701 702 703
}


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

int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field, 
unknown's avatar
unknown committed
704 705
                                 uint fieldnr, int row_offset,
                                 bool *set_blob_value)
unknown's avatar
unknown committed
706
{
unknown's avatar
unknown committed
707 708
  const byte* field_ptr= field->ptr + row_offset;
  uint32 pack_len= field->pack_length();
unknown's avatar
unknown committed
709
  DBUG_ENTER("set_ndb_value");
unknown's avatar
unknown committed
710
  DBUG_PRINT("enter", ("%d: %s  type: %u  len=%d  is_null=%s", 
unknown's avatar
unknown committed
711
                       fieldnr, field->field_name, field->type(), 
unknown's avatar
unknown committed
712
                       pack_len, field->is_null(row_offset) ? "Y" : "N"));
unknown's avatar
unknown committed
713
  DBUG_DUMP("value", (char*) field_ptr, pack_len);
unknown's avatar
unknown committed
714

unknown's avatar
unknown committed
715
  DBUG_ASSERT(ndb_supported_type(field->type()));
unknown's avatar
unknown committed
716
  {
717
    // ndb currently does not support size 0
unknown's avatar
unknown committed
718
    uint32 empty_field;
719 720
    if (pack_len == 0)
    {
unknown's avatar
unknown committed
721 722
      pack_len= sizeof(empty_field);
      field_ptr= (byte *)&empty_field;
unknown's avatar
unknown committed
723
      if (field->is_null(row_offset))
724
        empty_field= 0;
unknown's avatar
unknown committed
725
      else
726
        empty_field= 1;
727
    }
unknown's avatar
unknown committed
728 729
    if (! (field->flags & BLOB_FLAG))
    {
730 731
      if (field->type() != MYSQL_TYPE_BIT)
      {
unknown's avatar
unknown committed
732 733 734
        if (field->is_null(row_offset))
        {
          DBUG_PRINT("info", ("field is NULL"));
735
          // Set value to NULL
unknown's avatar
unknown committed
736
          DBUG_RETURN((ndb_op->setValue(fieldnr, (char*)NULL) != 0));
unknown's avatar
unknown committed
737
	}
738
        // Common implementation for most field types
unknown's avatar
unknown committed
739
        DBUG_RETURN(ndb_op->setValue(fieldnr, (char*)field_ptr) != 0);
740 741 742
      }
      else // if (field->type() == MYSQL_TYPE_BIT)
      {
743
        longlong bits= field->val_int();
744
 
745 746
        // Round up bit field length to nearest word boundry
        pack_len= ((pack_len + 3) >> 2) << 2;
747
        DBUG_ASSERT(pack_len <= 8);
unknown's avatar
unknown committed
748
        if (field->is_null(row_offset))
749
          // Set value to NULL
unknown's avatar
unknown committed
750
          DBUG_RETURN((ndb_op->setValue(fieldnr, (char*)NULL) != 0));
751
        DBUG_PRINT("info", ("bit field"));
752
        DBUG_DUMP("value", (char*)&bits, pack_len);
753
#ifdef WORDS_BIGENDIAN
754 755
        if (pack_len < 5)
        {
unknown's avatar
unknown committed
756
          DBUG_RETURN(ndb_op->setValue(fieldnr, ((char*)&bits)+4) != 0);
757
        }
758
#endif
unknown's avatar
unknown committed
759
        DBUG_RETURN(ndb_op->setValue(fieldnr, (char*)&bits) != 0);
760
      }
unknown's avatar
unknown committed
761 762
    }
    // Blob type
763
    NdbBlob *ndb_blob= ndb_op->getBlobHandle(fieldnr);
unknown's avatar
unknown committed
764 765
    if (ndb_blob != NULL)
    {
unknown's avatar
unknown committed
766
      if (field->is_null(row_offset))
unknown's avatar
unknown committed
767 768 769 770 771 772 773 774 775
        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);

776 777 778
      // Looks like NULL ptr signals length 0 blob
      if (blob_ptr == NULL) {
        DBUG_ASSERT(blob_len == 0);
779
        blob_ptr= (char*)"";
780
      }
unknown's avatar
unknown committed
781

unknown's avatar
unknown committed
782 783
      DBUG_PRINT("value", ("set blob ptr=%p len=%u",
                           blob_ptr, blob_len));
unknown's avatar
unknown committed
784 785
      DBUG_DUMP("value", (char*)blob_ptr, min(blob_len, 26));

786
      if (set_blob_value)
787
        *set_blob_value= TRUE;
unknown's avatar
unknown committed
788 789 790 791
      // No callback needed to write value
      DBUG_RETURN(ndb_blob->setValue(blob_ptr, blob_len) != 0);
    }
    DBUG_RETURN(1);
unknown's avatar
unknown committed
792
  }
unknown's avatar
unknown committed
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
}


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

unknown's avatar
unknown committed
810
int g_get_ndb_blobs_value(NdbBlob *ndb_blob, void *arg)
unknown's avatar
unknown committed
811
{
unknown's avatar
unknown committed
812
  DBUG_ENTER("g_get_ndb_blobs_value");
unknown's avatar
unknown committed
813 814 815
  if (ndb_blob->blobsNextBlob() != NULL)
    DBUG_RETURN(0);
  ha_ndbcluster *ha= (ha_ndbcluster *)arg;
816 817 818 819
  int ret= get_ndb_blobs_value(ha->table, ha->m_value,
                               ha->m_blobs_buffer, ha->m_blobs_buffer_size,
                               0);
  DBUG_RETURN(ret);
unknown's avatar
unknown committed
820 821
}

822 823 824 825 826 827 828 829
/*
  This routine is shared by injector.  There is no common blobs buffer
  so the buffer and length are passed by reference.  Injector also
  passes a record pointer diff.
 */
int get_ndb_blobs_value(TABLE* table, NdbValue* value_array,
                        byte*& buffer, uint& buffer_size,
                        my_ptrdiff_t ptrdiff)
unknown's avatar
unknown committed
830 831 832 833 834 835 836 837
{
  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;
838
    for (uint i= 0; i < table->s->fields; i++)
unknown's avatar
unknown committed
839 840
    {
      Field *field= table->field[i];
841
      NdbValue value= value_array[i];
842 843 844
      if (! (field->flags & BLOB_FLAG))
        continue;
      if (value.blob == NULL)
unknown's avatar
unknown committed
845
      {
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
        DBUG_PRINT("info",("[%u] skipped", i));
        continue;
      }
      Field_blob *field_blob= (Field_blob *)field;
      NdbBlob *ndb_blob= value.blob;
      int isNull;
      if (ndb_blob->getNull(isNull) != 0)
        ERR_RETURN(ndb_blob->getNdbError());
      if (isNull == 0) {
        Uint64 len64= 0;
        if (ndb_blob->getLength(len64) != 0)
          ERR_RETURN(ndb_blob->getNdbError());
        // Align to Uint64
        uint32 size= len64;
        if (size % 8 != 0)
          size+= 8 - size % 8;
        if (loop == 1)
unknown's avatar
unknown committed
863
        {
864 865 866 867 868 869 870 871 872 873 874
          char *buf= buffer + offset;
          uint32 len= 0xffffffff;  // Max uint32
          if (ndb_blob->readData(buf, len) != 0)
            ERR_RETURN(ndb_blob->getNdbError());
          DBUG_PRINT("info", ("[%u] offset=%u buf=%p len=%u [ptrdiff=%d]",
                              i, offset, buf, len, (int)ptrdiff));
          DBUG_ASSERT(len == len64);
          // Ugly hack assumes only ptr needs to be changed
          field_blob->ptr+= ptrdiff;
          field_blob->set_ptr(len, buf);
          field_blob->ptr-= ptrdiff;
unknown's avatar
unknown committed
875
        }
876 877 878 879 880 881 882 883 884 885 886
        offset+= size;
      }
      else if (loop == 1) // undefined or null
      {
        // have to set length even in this case
        char *buf= buffer + offset; // or maybe NULL
        uint32 len= 0;
        field_blob->ptr+= ptrdiff;
        field_blob->set_ptr(len, buf);
        field_blob->ptr-= ptrdiff;
        DBUG_PRINT("info", ("[%u] isNull=%d", i, isNull));
unknown's avatar
unknown committed
887 888
      }
    }
889
    if (loop == 0 && offset > buffer_size)
unknown's avatar
unknown committed
890
    {
891 892 893 894 895
      my_free(buffer, MYF(MY_ALLOW_ZERO_PTR));
      buffer_size= 0;
      DBUG_PRINT("info", ("allocate blobs buffer size %u", offset));
      buffer= my_malloc(offset, MYF(MY_WME));
      if (buffer == NULL)
unknown's avatar
unknown committed
896
        DBUG_RETURN(-1);
897
      buffer_size= offset;
unknown's avatar
unknown committed
898
    }
unknown's avatar
unknown committed
899
  }
unknown's avatar
unknown committed
900
  DBUG_RETURN(0);
unknown's avatar
unknown committed
901 902 903 904 905
}


/*
  Instruct NDB to fetch one field
unknown's avatar
unknown committed
906 907
  - 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
908 909
*/

unknown's avatar
unknown committed
910
int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field,
unknown's avatar
unknown committed
911
                                 uint fieldnr, byte* buf)
unknown's avatar
unknown committed
912 913
{
  DBUG_ENTER("get_ndb_value");
unknown's avatar
unknown committed
914 915 916 917 918
  DBUG_PRINT("enter", ("fieldnr: %d flags: %o", fieldnr,
                       (int)(field != NULL ? field->flags : 0)));

  if (field != NULL)
  {
unknown's avatar
unknown committed
919 920
      DBUG_ASSERT(buf);
      DBUG_ASSERT(ndb_supported_type(field->type()));
unknown's avatar
unknown committed
921 922
      DBUG_ASSERT(field->ptr != NULL);
      if (! (field->flags & BLOB_FLAG))
923
      { 
924 925
        if (field->type() != MYSQL_TYPE_BIT)
        {
926 927 928 929 930 931 932 933
          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);
        }
934 935 936 937
        else // if (field->type() == MYSQL_TYPE_BIT)
        {
          m_value[fieldnr].rec= ndb_op->getValue(fieldnr);
        }
unknown's avatar
unknown committed
938 939 940 941 942 943 944 945 946 947
        DBUG_RETURN(m_value[fieldnr].rec == NULL);
      }

      // Blob type
      NdbBlob *ndb_blob= ndb_op->getBlobHandle(fieldnr);
      m_value[fieldnr].blob= ndb_blob;
      if (ndb_blob != NULL)
      {
        // Set callback
        void *arg= (void *)this;
unknown's avatar
unknown committed
948
        DBUG_RETURN(ndb_blob->setActiveHook(g_get_ndb_blobs_value, arg) != 0);
unknown's avatar
unknown committed
949 950 951 952 953
      }
      DBUG_RETURN(1);
  }

  // Used for hidden key only
954
  m_value[fieldnr].rec= ndb_op->getValue(fieldnr, m_ref);
unknown's avatar
unknown committed
955 956 957 958 959 960 961
  DBUG_RETURN(m_value[fieldnr].rec == NULL);
}


/*
  Check if any set or get of blob value in current query.
*/
962
bool ha_ndbcluster::uses_blob_value()
unknown's avatar
unknown committed
963
{
unknown's avatar
unknown committed
964
  if (table_share->blob_fields == 0)
unknown's avatar
unknown committed
965
    return FALSE;
unknown's avatar
unknown committed
966
  {
unknown's avatar
unknown committed
967
    uint no_fields= table_share->fields;
unknown's avatar
unknown committed
968 969 970 971
    int i;
    // They always put blobs at the end..
    for (i= no_fields - 1; i >= 0; i--)
    {
972 973
      if ((m_write_op && ha_get_bit_in_write_set(i+1)) ||
          (!m_write_op && ha_get_bit_in_read_set(i+1)))
unknown's avatar
unknown committed
974
      {
unknown's avatar
unknown committed
975
        return TRUE;
unknown's avatar
unknown committed
976 977 978
      }
    }
  }
unknown's avatar
unknown committed
979
  return FALSE;
unknown's avatar
unknown committed
980 981 982 983 984 985 986 987 988
}


/*
  Get metadata for this table from NDB 

  IMPLEMENTATION
    - check that frm-file on disk is equal to frm-file
      of table accessed in NDB
unknown's avatar
unknown committed
989 990 991 992

  RETURN
    0    ok
    -2   Meta data has changed; Re-read data and try again
unknown's avatar
unknown committed
993 994
*/

995 996
int cmp_frm(const NDBTAB *ndbtab, const void *pack_data,
            uint pack_length)
unknown's avatar
unknown committed
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
{
  DBUG_ENTER("cmp_frm");
  /*
    Compare FrmData in NDB with frm file from disk.
  */
  if ((pack_length != ndbtab->getFrmLength()) || 
      (memcmp(pack_data, ndbtab->getFrmData(), pack_length)))
    DBUG_RETURN(1);
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
1008 1009
int ha_ndbcluster::get_metadata(const char *path)
{
1010 1011
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
unknown's avatar
unknown committed
1012 1013
  const NDBTAB *tab;
  int error;
unknown's avatar
unknown committed
1014
  bool invalidating_ndb_table= FALSE;
unknown's avatar
unknown committed
1015 1016 1017
  DBUG_ENTER("get_metadata");
  DBUG_PRINT("enter", ("m_tabname: %s, path: %s", m_tabname, path));

1018 1019 1020 1021 1022 1023
  do {
    const void *data, *pack_data;
    uint length, pack_length;

    if (!(tab= dict->getTable(m_tabname)))
      ERR_RETURN(dict->getNdbError());
1024
    // Check if thread has stale local cache
1025 1026 1027 1028 1029 1030 1031
    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()));
    }
1032 1033 1034 1035 1036
    /*
      Compare FrmData in NDB with frm file from disk.
    */
    error= 0;
    if (readfrm(path, &data, &length) ||
1037
        packfrm(data, length, &pack_data, &pack_length))
1038 1039 1040 1041 1042
    {
      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
1043
    
1044 1045
    if (get_ndb_share_state(m_share) != NSS_ALTERED 
        && cmp_frm(tab, pack_data, pack_length))
1046
    {
unknown's avatar
unknown committed
1047
      if (!invalidating_ndb_table)
1048
      {
unknown's avatar
unknown committed
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
        DBUG_PRINT("info", ("Invalidating table"));
        invalidate_dictionary_cache(TRUE);
        invalidating_ndb_table= TRUE;
      }
      else
      {
        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= HA_ERR_TABLE_DEF_CHANGED;
        invalidating_ndb_table= FALSE;
1063 1064 1065 1066
      }
    }
    else
    {
unknown's avatar
unknown committed
1067
      invalidating_ndb_table= FALSE;
1068 1069 1070 1071 1072
    }
    my_free((char*)data, MYF(0));
    my_free((char*)pack_data, MYF(0));
  } while (invalidating_ndb_table);

unknown's avatar
unknown committed
1073 1074
  if (error)
    DBUG_RETURN(error);
unknown's avatar
unknown committed
1075
  
1076
  m_table_version= tab->getObjectVersion();
1077 1078 1079
  m_table= (void *)tab; 
  m_table_info= NULL; // Set in external lock
  
1080
  DBUG_RETURN(open_indexes(ndb, table, FALSE));
1081
}
unknown's avatar
unknown committed
1082

1083
static int fix_unique_index_attr_order(NDB_INDEX_DATA &data,
1084 1085
                                       const NDBINDEX *index,
                                       KEY *key_info)
1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104
{
  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++)
    {
1105
      const NDBCOL *c= index->getColumn(j);
unknown's avatar
unknown committed
1106
      if (strcmp(field_name, c->getName()) == 0)
1107
      {
1108 1109
        data.unique_index_attrid_map[i]= j;
        break;
1110 1111 1112 1113 1114 1115
      }
    }
    DBUG_ASSERT(data.unique_index_attrid_map[i] != 255);
  }
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1116

unknown's avatar
unknown committed
1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146
int ha_ndbcluster::table_changed(const void *pack_frm_data, uint pack_frm_len)
{
  Ndb *ndb;
  NDBDICT *dict;
  const NDBTAB *orig_tab;
  NdbDictionary::Table new_tab;
  int result;
  DBUG_ENTER("ha_ndbcluster::table_changed");
  DBUG_PRINT("info", ("Modifying frm for table %s", m_tabname));
  if (check_ndb_connection())
    DBUG_RETURN(my_errno= HA_ERR_NO_CONNECTION);
                                                                             
  ndb= get_ndb();
  dict= ndb->getDictionary();
  if (!(orig_tab= dict->getTable(m_tabname)))
    ERR_RETURN(dict->getNdbError());
  // 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());
  }
  new_tab= *orig_tab;
  new_tab.setFrm(pack_frm_data, pack_frm_len);
  if (dict->alterTable(new_tab) != 0)
    ERR_RETURN(dict->getNdbError());
  DBUG_RETURN(0);
}

1147 1148 1149 1150 1151 1152
/*
  Create all the indexes for a table.
  If any index should fail to be created,
  the error is returned immediately
*/
int ha_ndbcluster::create_indexes(Ndb *ndb, TABLE *tab)
1153
{
1154
  uint i;
unknown's avatar
unknown committed
1155
  int error= 0;
1156
  const char *index_name;
unknown's avatar
unknown committed
1157
  KEY* key_info= tab->key_info;
1158
  const char **key_name= tab->s->keynames.type_names;
1159
  NDBDICT *dict= ndb->getDictionary();
1160
  DBUG_ENTER("ha_ndbcluster::create_indexes");
1161
  
1162
  for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
1163
  {
unknown's avatar
unknown committed
1164
    index_name= *key_name;
1165
    NDB_INDEX_TYPE idx_type= get_index_type_from_table(i);
1166 1167
    error= create_index(index_name, key_info, idx_type, i);
    if (error)
1168
    {
1169 1170
      DBUG_PRINT("error", ("Failed to create index %u", i));
      break;
1171
    }
1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217
  }

  DBUG_RETURN(error);
}

void ha_ndbcluster::clear_index(int i)
{
  m_index[i].type= UNDEFINED_INDEX;
  m_index[i].status= UNDEFINED;
  m_index[i].unique_index= NULL;
  m_index[i].index= NULL;
  m_index[i].unique_index_attrid_map= NULL;
  m_index[i].index_stat=NULL;
  m_index[i].index_stat_cache_entries=0;
  m_index[i].index_stat_update_freq=0;
  m_index[i].index_stat_query_count=0;
}

void ha_ndbcluster::clear_indexes()
{
  for (int i= 0; i < MAX_KEY; i++) clear_index(i);
}

/*
  Associate a direct reference to an index handle
  with an index (for faster access)
 */
int ha_ndbcluster::add_index_handle(THD *thd, NDBDICT *dict, KEY *key_info,
                                    const char *index_name, uint index_no)
{
  int error= 0;
  NDB_INDEX_TYPE idx_type= get_index_type_from_table(index_no);
  m_index[index_no].type= idx_type;
  DBUG_ENTER("ha_ndbcluster::get_index_handle");

  if (idx_type != PRIMARY_KEY_INDEX && idx_type != UNIQUE_INDEX)
  {
    DBUG_PRINT("info", ("Get handle to index %s", index_name));
    const NDBINDEX *index= dict->getIndex(index_name, m_tabname);
    if (!index) ERR_RETURN(dict->getNdbError());
    m_index[index_no].index= (void *) index;
    // ordered index - add stats
    NDB_INDEX_DATA& d=m_index[index_no];
    delete d.index_stat;
    d.index_stat=NULL;
    if (thd->variables.ndb_index_stat_enable)
unknown's avatar
unknown committed
1218
    {
1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252
      d.index_stat=new NdbIndexStat(index);
      d.index_stat_cache_entries=thd->variables.ndb_index_stat_cache_entries;
      d.index_stat_update_freq=thd->variables.ndb_index_stat_update_freq;
      d.index_stat_query_count=0;
      d.index_stat->alloc_cache(d.index_stat_cache_entries);
      DBUG_PRINT("info", ("index %s stat=on cache_entries=%u update_freq=%u",
                          index->getName(),
                          d.index_stat_cache_entries,
                          d.index_stat_update_freq));
    } else
    {
      DBUG_PRINT("info", ("index %s stat=off", index->getName()));
    }
  }
  if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX)
  {
    char unique_index_name[FN_LEN];
    static const char* unique_suffix= "$unique";
    strxnmov(unique_index_name, FN_LEN, index_name, unique_suffix, NullS);
    DBUG_PRINT("info", ("Get handle to unique_index %s", unique_index_name));
    const NDBINDEX *index= dict->getIndex(unique_index_name, m_tabname);
    if (!index) ERR_RETURN(dict->getNdbError());
    m_index[index_no].unique_index= (void *) index;
    error= fix_unique_index_attr_order(m_index[index_no], index, key_info);
  }
  if (!error)
    m_index[index_no].status= ACTIVE;
  
  DBUG_RETURN(error);
}

/*
  Associate index handles for each index of a table
*/
1253
int ha_ndbcluster::open_indexes(Ndb *ndb, TABLE *tab, bool ignore_error)
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266
{
  uint i;
  int error= 0;
  THD *thd=current_thd;
  NDBDICT *dict= ndb->getDictionary();
  const char *index_name;
  KEY* key_info= tab->key_info;
  const char **key_name= tab->s->keynames.type_names;
  DBUG_ENTER("ha_ndbcluster::open_indexes");
  
  for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
  {
    if ((error= add_index_handle(thd, dict, key_info, *key_name, i)))
1267 1268 1269 1270
      if (ignore_error)
        m_index[i].index= m_index[i].unique_index= NULL;
      else
        break;
1271 1272 1273 1274 1275 1276 1277 1278 1279
  }
  
  DBUG_RETURN(error);
}

/*
  Renumber indexes in index list by shifting out
  indexes that are to be dropped
 */
1280
void ha_ndbcluster::renumber_indexes(Ndb *ndb, TABLE *tab)
1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301
{
  uint i;
  const char *index_name;
  KEY* key_info= tab->key_info;
  const char **key_name= tab->s->keynames.type_names;
  NDBDICT *dict= ndb->getDictionary();
  DBUG_ENTER("ha_ndbcluster::renumber_indexes");
  
  for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
  {
    index_name= *key_name;
    NDB_INDEX_TYPE idx_type= get_index_type_from_table(i);
    m_index[i].type= idx_type;
    if (m_index[i].status == TO_BE_DROPPED) 
    {
      DBUG_PRINT("info", ("Shifting index %s(%i) out of the list", 
                          index_name, i));
      NDB_INDEX_DATA tmp;
      uint j= i + 1;
      // Shift index out of list
      while(j != MAX_KEY && m_index[j].status != UNDEFINED)
unknown's avatar
unknown committed
1302
      {
1303 1304 1305 1306
        tmp=  m_index[j - 1];
        m_index[j - 1]= m_index[j];
        m_index[j]= tmp;
        j++;
unknown's avatar
unknown committed
1307 1308
      }
    }
1309 1310
  }

1311
  DBUG_VOID_RETURN;
1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332
}

/*
  Drop all indexes that are marked for deletion
*/
int ha_ndbcluster::drop_indexes(Ndb *ndb, TABLE *tab)
{
  uint i;
  int error= 0;
  const char *index_name;
  KEY* key_info= tab->key_info;
  const char **key_name= tab->s->keynames.type_names;
  NDBDICT *dict= ndb->getDictionary();
  DBUG_ENTER("ha_ndbcluster::drop_indexes");
  
  for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
  {
    index_name= *key_name;
    NDB_INDEX_TYPE idx_type= get_index_type_from_table(i);
    m_index[i].type= idx_type;
    if (m_index[i].status == TO_BE_DROPPED)
1333
    {
1334 1335 1336 1337 1338 1339
      NdbDictionary::Index *index= 
        (NdbDictionary::Index *) m_index[i].index;
      NdbDictionary::Index *unique_index= 
        (NdbDictionary::Index *) m_index[i].unique_index;
      
      if (index)
1340
      {
1341 1342 1343 1344 1345 1346 1347 1348
        index_name= index->getName();
        DBUG_PRINT("info", ("Dropping index %u: %s", i, index_name));  
        // Drop ordered index from ndb
        error= drop_ndb_index(index_name);
      }
      if (!error)
        m_index[i].index= NULL;
      if (!error && unique_index)
1349
      {
1350 1351 1352 1353
        index_name= index->getName();
        DBUG_PRINT("info", ("Dropping index %u: %s", i, index_name));
        // Drop unique index from ndb
        error= drop_ndb_index(index_name);
1354
      }
1355 1356 1357 1358
      if (error)
        DBUG_RETURN(error);
      clear_index(i);
      continue;
1359
    }
1360
  }
unknown's avatar
unknown committed
1361 1362
  
  DBUG_RETURN(error);
1363 1364
}

unknown's avatar
unknown committed
1365 1366 1367 1368
/*
  Decode the type of an index from information 
  provided in table object
*/
1369
NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_table(uint inx) const
unknown's avatar
unknown committed
1370
{
1371 1372 1373 1374 1375 1376 1377
  return get_index_type_from_key(inx, table_share->key_info);
}

NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_key(uint inx,
                                                      KEY *key_info) const
{
  bool is_hash_index=  (key_info[inx].algorithm == 
unknown's avatar
unknown committed
1378 1379
                        HA_KEY_ALG_HASH);
  if (inx == table_share->primary_key)
1380
    return is_hash_index ? PRIMARY_KEY_INDEX : PRIMARY_KEY_ORDERED_INDEX;
1381 1382
  
  return ((key_info[inx].flags & HA_NOSAME) ? 
1383 1384
          (is_hash_index ? UNIQUE_INDEX : UNIQUE_ORDERED_INDEX) :
          ORDERED_INDEX);
unknown's avatar
unknown committed
1385
} 
1386

1387 1388 1389 1390 1391
int ha_ndbcluster::check_index_fields_not_null(uint inx)
{
  KEY* key_info= table->key_info + inx;
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
1392
  DBUG_ENTER("ha_ndbcluster::check_index_fields_not_null");
1393 1394 1395 1396 1397 1398
  
  for (; key_part != end; key_part++) 
    {
      Field* field= key_part->field;
      if (field->maybe_null())
      {
1399 1400 1401
        my_printf_error(ER_NULL_COLUMN_IN_INDEX,ER(ER_NULL_COLUMN_IN_INDEX),
                        MYF(0),field->field_name);
        DBUG_RETURN(ER_NULL_COLUMN_IN_INDEX);
1402 1403 1404 1405 1406
      }
    }
  
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1407 1408 1409

void ha_ndbcluster::release_metadata()
{
1410
  uint i;
1411

unknown's avatar
unknown committed
1412 1413 1414 1415
  DBUG_ENTER("release_metadata");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));

  m_table= NULL;
unknown's avatar
unknown committed
1416
  m_table_info= NULL;
unknown's avatar
unknown committed
1417

1418
  // Release index list 
1419 1420
  for (i= 0; i < MAX_KEY; i++)
  {
1421 1422
    m_index[i].unique_index= NULL;      
    m_index[i].index= NULL;      
1423 1424 1425 1426 1427
    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;
    }
1428 1429
    delete m_index[i].index_stat;
    m_index[i].index_stat=NULL;
1430 1431
  }

unknown's avatar
unknown committed
1432 1433 1434
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
1435
int ha_ndbcluster::get_ndb_lock_type(enum thr_lock_type type)
1436
{
1437
  if (type >= TL_WRITE_ALLOW_WRITE)
unknown's avatar
unknown committed
1438
    return NdbOperation::LM_Exclusive;
1439
  else if (uses_blob_value())
1440
    return NdbOperation::LM_Read;
unknown's avatar
unknown committed
1441
  else
unknown's avatar
unknown committed
1442
    return NdbOperation::LM_CommittedRead;
1443 1444
}

unknown's avatar
unknown committed
1445 1446 1447 1448 1449 1450
static const ulong index_type_flags[]=
{
  /* UNDEFINED_INDEX */
  0,                         

  /* PRIMARY_KEY_INDEX */
1451
  HA_ONLY_WHOLE_INDEX, 
1452 1453

  /* PRIMARY_KEY_ORDERED_INDEX */
1454
  /* 
unknown's avatar
unknown committed
1455
     Enable HA_KEYREAD_ONLY when "sorted" indexes are supported, 
1456 1457 1458
     thus ORDERD BY clauses can be optimized by reading directly 
     through the index.
  */
unknown's avatar
unknown committed
1459
  // HA_KEYREAD_ONLY | 
unknown's avatar
unknown committed
1460
  HA_READ_NEXT |
1461
  HA_READ_PREV |
unknown's avatar
unknown committed
1462 1463
  HA_READ_RANGE |
  HA_READ_ORDER,
unknown's avatar
unknown committed
1464 1465

  /* UNIQUE_INDEX */
1466
  HA_ONLY_WHOLE_INDEX,
unknown's avatar
unknown committed
1467

1468
  /* UNIQUE_ORDERED_INDEX */
unknown's avatar
unknown committed
1469
  HA_READ_NEXT |
1470
  HA_READ_PREV |
unknown's avatar
unknown committed
1471 1472
  HA_READ_RANGE |
  HA_READ_ORDER,
1473

unknown's avatar
unknown committed
1474
  /* ORDERED_INDEX */
unknown's avatar
unknown committed
1475
  HA_READ_NEXT |
1476
  HA_READ_PREV |
unknown's avatar
unknown committed
1477 1478
  HA_READ_RANGE |
  HA_READ_ORDER
unknown's avatar
unknown committed
1479 1480 1481 1482 1483 1484 1485
};

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);
1486
  return m_index[idx_no].type;
unknown's avatar
unknown committed
1487 1488 1489 1490 1491 1492 1493 1494 1495 1496
}


/*
  Get the flags for an index

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

1497 1498
inline ulong ha_ndbcluster::index_flags(uint idx_no, uint part,
                                        bool all_parts) const 
unknown's avatar
unknown committed
1499
{ 
1500
  DBUG_ENTER("ha_ndbcluster::index_flags");
unknown's avatar
unknown committed
1501
  DBUG_PRINT("enter", ("idx_no: %u", idx_no));
unknown's avatar
unknown committed
1502
  DBUG_ASSERT(get_index_type_from_table(idx_no) < index_flags_size);
1503 1504
  DBUG_RETURN(index_type_flags[get_index_type_from_table(idx_no)] | 
              HA_KEY_SCAN_NOT_ROR);
unknown's avatar
unknown committed
1505 1506
}

unknown's avatar
unknown committed
1507 1508
static void shrink_varchar(Field* field, const byte* & ptr, char* buf)
{
1509
  if (field->type() == MYSQL_TYPE_VARCHAR && ptr != NULL) {
unknown's avatar
unknown committed
1510
    Field_varstring* f= (Field_varstring*)field;
unknown's avatar
unknown committed
1511
    if (f->length_bytes == 1) {
unknown's avatar
unknown committed
1512 1513 1514 1515 1516
      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
1517
        DBUG_ASSERT(FALSE);
unknown's avatar
unknown committed
1518 1519 1520 1521 1522 1523 1524
        buf[0]= 255;
      }
      memmove(buf + 1, ptr + 2, pack_len - 1);
      ptr= buf;
    }
  }
}
unknown's avatar
unknown committed
1525 1526 1527

int ha_ndbcluster::set_primary_key(NdbOperation *op, const byte *key)
{
unknown's avatar
unknown committed
1528
  KEY* key_info= table->key_info + table_share->primary_key;
unknown's avatar
unknown committed
1529 1530 1531 1532 1533 1534 1535
  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
1536 1537 1538
    const byte* ptr= key;
    char buf[256];
    shrink_varchar(field, ptr, buf);
unknown's avatar
unknown committed
1539
    if (set_ndb_key(op, field, 
1540
                    key_part->fieldnr-1, ptr))
unknown's avatar
unknown committed
1541
      ERR_RETURN(op->getNdbError());
unknown's avatar
unknown committed
1542
    key += key_part->store_length;
unknown's avatar
unknown committed
1543 1544 1545 1546 1547
  }
  DBUG_RETURN(0);
}


1548
int ha_ndbcluster::set_primary_key_from_record(NdbOperation *op, const byte *record)
1549
{
unknown's avatar
unknown committed
1550
  KEY* key_info= table->key_info + table_share->primary_key;
1551 1552
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
1553
  DBUG_ENTER("set_primary_key_from_record");
1554 1555 1556 1557 1558

  for (; key_part != end; key_part++) 
  {
    Field* field= key_part->field;
    if (set_ndb_key(op, field, 
1559
		    key_part->fieldnr-1, record+key_part->offset))
unknown's avatar
unknown committed
1560 1561 1562 1563 1564
      ERR_RETURN(op->getNdbError());
  }
  DBUG_RETURN(0);
}

1565 1566
int 
ha_ndbcluster::set_index_key(NdbOperation *op, 
1567 1568
                             const KEY *key_info, 
                             const byte * key_ptr)
1569
{
1570
  DBUG_ENTER("ha_ndbcluster::set_index_key");
1571 1572 1573 1574 1575 1576
  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
1577 1578 1579 1580
    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
1581
    if (set_ndb_key(op, field, m_index[active_index].unique_index_attrid_map[i], ptr))
1582 1583 1584 1585 1586
      ERR_RETURN(m_active_trans->getNdbError());
    key_ptr+= key_part->store_length;
  }
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1587

unknown's avatar
unknown committed
1588 1589 1590 1591 1592 1593 1594
inline 
int ha_ndbcluster::define_read_attrs(byte* buf, NdbOperation* op)
{
  uint i;
  DBUG_ENTER("define_read_attrs");  

  // Define attributes to read
unknown's avatar
unknown committed
1595
  for (i= 0; i < table_share->fields; i++) 
unknown's avatar
unknown committed
1596 1597
  {
    Field *field= table->field[i];
1598 1599
    if (ha_get_bit_in_read_set(i+1) ||
        ((field->flags & PRI_KEY_FLAG)))
unknown's avatar
unknown committed
1600 1601
    {      
      if (get_ndb_value(op, field, i, buf))
1602
        ERR_RETURN(op->getNdbError());
unknown's avatar
unknown committed
1603 1604 1605 1606 1607 1608 1609
    } 
    else 
    {
      m_value[i].ptr= NULL;
    }
  }
    
unknown's avatar
unknown committed
1610
  if (table_share->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
1611 1612 1613
  {
    DBUG_PRINT("info", ("Getting hidden key"));
    // Scanning table with no primary key
unknown's avatar
unknown committed
1614
    int hidden_no= table_share->fields;      
unknown's avatar
unknown committed
1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625
#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
Merge  
unknown committed
1626

unknown's avatar
unknown committed
1627 1628 1629 1630
/*
  Read one record from NDB using primary key
*/

1631 1632
int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf,
                           uint32 part_id)
unknown's avatar
unknown committed
1633
{
unknown's avatar
unknown committed
1634
  uint no_fields= table_share->fields;
unknown's avatar
unknown committed
1635 1636
  NdbConnection *trans= m_active_trans;
  NdbOperation *op;
1637

1638 1639 1640 1641
  int res;
  DBUG_ENTER("pk_read");
  DBUG_PRINT("enter", ("key_len: %u", key_len));
  DBUG_DUMP("key", (char*)key, key_len);
1642
  m_write_op= FALSE;
unknown's avatar
unknown committed
1643

1644 1645
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
unknown's avatar
unknown committed
1646
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || 
1647
      op->readTuple(lm) != 0)
1648
    ERR_RETURN(trans->getNdbError());
1649
  
1650 1651
  if (m_use_partition_function)
    op->setPartitionId(part_id);
unknown's avatar
unknown committed
1652
  if (table_share->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
1653 1654 1655 1656 1657
  {
    // 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))
1658
      ERR_RETURN(trans->getNdbError());
1659
    
unknown's avatar
unknown committed
1660
    // Read key at the same time, for future reference
unknown's avatar
unknown committed
1661
    if (get_ndb_value(op, NULL, no_fields, NULL))
1662
      ERR_RETURN(trans->getNdbError());
unknown's avatar
unknown committed
1663 1664 1665 1666 1667 1668 1669
  } 
  else 
  {
    if ((res= set_primary_key(op, key)))
      return res;
  }
  
unknown's avatar
unknown committed
1670
  if ((res= define_read_attrs(buf, op)))
1671
    DBUG_RETURN(res);
unknown's avatar
unknown committed
1672
  
unknown's avatar
unknown committed
1673
  if (execute_no_commit_ie(this,trans) != 0) 
unknown's avatar
unknown committed
1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684
  {
    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);
}

1685 1686
/*
  Read one complementing record from NDB using primary key from old_data
1687
  or hidden key
1688 1689
*/

1690 1691
int ha_ndbcluster::complemented_read(const byte *old_data, byte *new_data,
                                     uint32 old_part_id)
1692
{
unknown's avatar
unknown committed
1693
  uint no_fields= table_share->fields, i;
1694
  NdbTransaction *trans= m_active_trans;
1695
  NdbOperation *op;
1696
  DBUG_ENTER("complemented_read");
1697
  m_write_op= FALSE;
1698

1699 1700
  if (ha_get_all_bit_in_read_set())
  {
1701 1702
    // We have allready retrieved all fields, nothing to complement
    DBUG_RETURN(0);
1703
  }
1704

1705 1706
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
unknown's avatar
unknown committed
1707
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || 
1708
      op->readTuple(lm) != 0)
1709
    ERR_RETURN(trans->getNdbError());
1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720
  if (table_share->primary_key != MAX_KEY) 
  {
    if (set_primary_key_from_record(op, old_data))
      ERR_RETURN(trans->getNdbError());
  } 
  else 
  {
    // This table has no primary key, use "hidden" primary key
    if (set_hidden_key(op, table->s->fields, m_ref))
      ERR_RETURN(op->getNdbError());
  }
1721 1722 1723 1724

  if (m_use_partition_function)
    op->setPartitionId(old_part_id);
  
1725 1726 1727 1728
  // Read all unreferenced non-key field(s)
  for (i= 0; i < no_fields; i++) 
  {
    Field *field= table->field[i];
1729
    if (!((field->flags & PRI_KEY_FLAG) ||
1730
          (ha_get_bit_in_read_set(i+1))))
1731
    {
unknown's avatar
unknown committed
1732
      if (get_ndb_value(op, field, i, new_data))
1733
        ERR_RETURN(trans->getNdbError());
1734 1735 1736
    }
  }
  
unknown's avatar
unknown committed
1737
  if (execute_no_commit(this,trans) != 0) 
1738 1739 1740 1741 1742 1743 1744 1745
  {
    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;     
1746 1747 1748 1749 1750 1751 1752 1753

  /**
   * restore m_value
   */
  for (i= 0; i < no_fields; i++) 
  {
    Field *field= table->field[i];
    if (!((field->flags & PRI_KEY_FLAG) ||
1754
          (ha_get_bit_in_read_set(i+1))))
1755 1756 1757 1758 1759
    {
      m_value[i].ptr= NULL;
    }
  }
  
1760 1761 1762
  DBUG_RETURN(0);
}

1763 1764 1765 1766
/*
  Peek to check if a particular row already exists
*/

1767
int ha_ndbcluster::peek_row(const byte *record)
1768
{
1769
  NdbTransaction *trans= m_active_trans;
1770 1771
  NdbOperation *op;
  DBUG_ENTER("peek_row");
unknown's avatar
unknown committed
1772

1773 1774 1775 1776 1777
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) ||
      op->readTuple(lm) != 0)
    ERR_RETURN(trans->getNdbError());
unknown's avatar
unknown committed
1778

1779
  int res;
1780
  if ((res= set_primary_key_from_record(op, record)))
1781
    ERR_RETURN(trans->getNdbError());
unknown's avatar
unknown committed
1782

1783 1784 1785 1786
  if (m_use_partition_function)
  {
    uint32 part_id;
    int error;
1787 1788 1789
    longlong func_value;
    if ((error= m_part_info->get_partition_id(m_part_info, &part_id,
                                              &func_value)))
1790 1791 1792 1793 1794 1795
    {
      DBUG_RETURN(error);
    }
    op->setPartitionId(part_id);
  }

1796
  if (execute_no_commit_ie(this,trans) != 0)
unknown's avatar
unknown committed
1797 1798 1799 1800
  {
    table->status= STATUS_NOT_FOUND;
    DBUG_RETURN(ndb_err(trans));
  } 
1801 1802
  DBUG_RETURN(0);
}
1803

unknown's avatar
unknown committed
1804 1805 1806 1807 1808
/*
  Read one record from NDB using unique secondary index
*/

int ha_ndbcluster::unique_index_read(const byte *key,
1809
                                     uint key_len, byte *buf)
unknown's avatar
unknown committed
1810
{
1811
  int res;
1812
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
1813
  NdbIndexOperation *op;
1814
  DBUG_ENTER("ha_ndbcluster::unique_index_read");
unknown's avatar
unknown committed
1815 1816 1817
  DBUG_PRINT("enter", ("key_len: %u, index: %u", key_len, active_index));
  DBUG_DUMP("key", (char*)key, key_len);
  
1818 1819
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
1820
  if (!(op= trans->getNdbIndexOperation((NDBINDEX *) 
1821
                                        m_index[active_index].unique_index, 
unknown's avatar
unknown committed
1822
                                        (const NDBTAB *) m_table)) ||
1823
      op->readTuple(lm) != 0)
unknown's avatar
unknown committed
1824 1825 1826
    ERR_RETURN(trans->getNdbError());
  
  // Set secondary index key(s)
unknown's avatar
unknown committed
1827
  if ((res= set_index_key(op, table->key_info + active_index, key)))
1828 1829
    DBUG_RETURN(res);
  
unknown's avatar
unknown committed
1830
  if ((res= define_read_attrs(buf, op)))
1831
    DBUG_RETURN(res);
unknown's avatar
unknown committed
1832

unknown's avatar
unknown committed
1833
  if (execute_no_commit_ie(this,trans) != 0) 
unknown's avatar
unknown committed
1834 1835 1836 1837 1838 1839 1840 1841 1842 1843
  {
    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
1844
inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor)
1845 1846
{
  DBUG_ENTER("fetch_next");
1847
  int check;
1848
  NdbTransaction *trans= m_active_trans;
1849
  
1850
  bool contact_ndb= m_lock.type < TL_WRITE_ALLOW_WRITE;
1851 1852
  do {
    DBUG_PRINT("info", ("Call nextResult, contact_ndb: %d", contact_ndb));
unknown's avatar
unknown committed
1853 1854 1855
    /*
      We can only handle one tuple with blobs at a time.
    */
1856
    if (m_ops_pending && m_blobs_pending)
unknown's avatar
unknown committed
1857
    {
unknown's avatar
unknown committed
1858
      if (execute_no_commit(this,trans) != 0)
1859
        DBUG_RETURN(ndb_err(trans));
1860 1861
      m_ops_pending= 0;
      m_blobs_pending= FALSE;
unknown's avatar
unknown committed
1862
    }
1863 1864
    
    if ((check= cursor->nextResult(contact_ndb, m_force_send)) == 0)
1865 1866 1867 1868 1869 1870 1871
    {
      DBUG_RETURN(0);
    } 
    else if (check == 1 || check == 2)
    {
      // 1: No more records
      // 2: No more cached records
1872
      
1873
      /*
1874 1875 1876
        Before fetching more rows and releasing lock(s),
        all pending update or delete operations should 
        be sent to NDB
1877
      */
1878 1879
      DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending));    
      if (m_ops_pending)
1880
      {
1881 1882 1883 1884 1885 1886 1887 1888 1889
        if (m_transaction_on)
        {
          if (execute_no_commit(this,trans) != 0)
            DBUG_RETURN(-1);
        }
        else
        {
          if  (execute_commit(this,trans) != 0)
            DBUG_RETURN(-1);
unknown's avatar
unknown committed
1890
          if (trans->restart() != 0)
1891 1892 1893 1894 1895 1896
          {
            DBUG_ASSERT(0);
            DBUG_RETURN(-1);
          }
        }
        m_ops_pending= 0;
1897
      }
1898 1899
      contact_ndb= (check == 2);
    }
unknown's avatar
unknown committed
1900 1901 1902 1903
    else
    {
      DBUG_RETURN(-1);
    }
1904
  } while (check == 2);
unknown's avatar
unknown committed
1905

1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916
  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
1917

1918 1919 1920 1921 1922 1923 1924
*/

inline int ha_ndbcluster::next_result(byte *buf)
{  
  int res;
  DBUG_ENTER("next_result");
    
1925 1926 1927
  if (!m_active_cursor)
    DBUG_RETURN(HA_ERR_END_OF_FILE);
  
unknown's avatar
unknown committed
1928
  if ((res= fetch_next(m_active_cursor)) == 0)
1929 1930 1931 1932 1933 1934 1935
  {
    DBUG_PRINT("info", ("One more record found"));    
    
    unpack_record(buf);
    table->status= 0;
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
1936
  else if (res == 1)
1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947
  {
    // 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
1948 1949
}

1950
/*
1951
  Set bounds for ordered index scan.
1952 1953
*/

unknown's avatar
unknown committed
1954
int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
1955 1956
                              uint inx,
                              bool rir,
1957 1958
                              const key_range *keys[2],
                              uint range_no)
1959
{
1960
  const KEY *const key_info= table->key_info + inx;
1961 1962 1963
  const uint key_parts= key_info->key_parts;
  uint key_tot_len[2];
  uint tot_len;
1964
  uint i, j;
1965 1966

  DBUG_ENTER("set_bounds");
1967
  DBUG_PRINT("info", ("key_parts=%d", key_parts));
1968

1969
  for (j= 0; j <= 1; j++)
1970
  {
1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983
    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;
    }
1984 1985
  }
  tot_len= 0;
unknown's avatar
unknown committed
1986

1987 1988 1989 1990
  for (i= 0; i < key_parts; i++)
  {
    KEY_PART_INFO *key_part= &key_info->key_part[i];
    Field *field= key_part->field;
1991
#ifndef DBUG_OFF
1992
    uint part_len= key_part->length;
1993
#endif
1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007
    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++)
    {
2008
      struct part_st &p= part[j];
2009 2010 2011 2012 2013 2014 2015
      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
2016
        p.part_null= key_part->null_bit && *p.part_ptr;
2017
        p.bound_ptr= (const char *)
unknown's avatar
unknown committed
2018
          p.part_null ? 0 : key_part->null_bit ? p.part_ptr + 1 : p.part_ptr;
2019 2020 2021 2022 2023 2024

        if (j == 0)
        {
          switch (p.key->flag)
          {
            case HA_READ_KEY_EXACT:
2025 2026 2027 2028
              if (! rir)
                p.bound_type= NdbIndexScanOperation::BoundEQ;
              else // differs for records_in_range
                p.bound_type= NdbIndexScanOperation::BoundLE;
2029
              break;
2030
            // ascending
2031 2032 2033 2034 2035 2036 2037 2038 2039
            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;
2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052
            // 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;
2053 2054 2055 2056 2057 2058 2059
            default:
              break;
          }
        }
        if (j == 1) {
          switch (p.key->flag)
          {
2060
            // ascending
2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071
            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;
2072
            // descending strangely sets no end key
2073 2074
          }
        }
2075

2076 2077 2078
        if (p.bound_type == -1)
        {
          DBUG_PRINT("error", ("key %d unknown flag %d", j, p.key->flag));
unknown's avatar
unknown committed
2079
          DBUG_ASSERT(FALSE);
2080
          // Stop setting bounds but continue with what we have
2081
          op->end_of_bound(range_no);
2082 2083 2084 2085
          DBUG_RETURN(0);
        }
      }
    }
2086

2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103
    // 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;
    }
2104

2105 2106
    for (j= 0; j <= 1; j++)
    {
2107
      struct part_st &p= part[j];
2108 2109 2110 2111 2112 2113 2114 2115 2116
      // 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)
2117
        {
unknown's avatar
unknown committed
2118 2119 2120
          const char* ptr= p.bound_ptr;
          char buf[256];
          shrink_varchar(field, ptr, buf);
unknown's avatar
Merge  
unknown committed
2121
          if (op->setBound(i, p.bound_type, ptr))
2122
            ERR_RETURN(op->getNdbError());
2123
        }
2124 2125 2126 2127
      }
    }

    tot_len+= part_store_len;
2128
  }
2129
  op->end_of_bound(range_no);
2130 2131 2132
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
2133
/*
2134
  Start ordered index scan in NDB
unknown's avatar
unknown committed
2135 2136
*/

2137
int ha_ndbcluster::ordered_index_scan(const key_range *start_key,
2138
                                      const key_range *end_key,
2139 2140
                                      bool sorted, bool descending,
                                      byte* buf, part_id_range *part_spec)
unknown's avatar
unknown committed
2141
{  
2142
  int res;
unknown's avatar
unknown committed
2143
  bool restart;
2144
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
2145
  NdbIndexScanOperation *op;
2146

2147 2148 2149
  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
2150
  DBUG_PRINT("enter", ("Starting new ordered scan on %s", m_tabname));
2151
  m_write_op= FALSE;
unknown's avatar
unknown committed
2152

2153 2154
  // Check that sorted seems to be initialised
  DBUG_ASSERT(sorted == 0 || sorted == 1);
unknown's avatar
unknown committed
2155
  
2156
  if (m_active_cursor == 0)
unknown's avatar
unknown committed
2157
  {
unknown's avatar
unknown committed
2158
    restart= FALSE;
unknown's avatar
unknown committed
2159 2160 2161
    NdbOperation::LockMode lm=
      (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
    if (!(op= trans->getNdbIndexScanOperation((NDBINDEX *)
2162 2163 2164
                                              m_index[active_index].index, 
                                              (const NDBTAB *) m_table)) ||
        op->readTuples(lm, 0, parallelism, sorted, descending))
unknown's avatar
unknown committed
2165
      ERR_RETURN(trans->getNdbError());
2166 2167 2168
    if (m_use_partition_function && part_spec != NULL &&
        part_spec->start_part == part_spec->end_part)
      op->setPartitionId(part_spec->start_part);
2169
    m_active_cursor= op;
unknown's avatar
unknown committed
2170
  } else {
unknown's avatar
unknown committed
2171
    restart= TRUE;
2172
    op= (NdbIndexScanOperation*)m_active_cursor;
unknown's avatar
unknown committed
2173
    
2174 2175 2176
    if (m_use_partition_function && part_spec != NULL &&
        part_spec->start_part == part_spec->end_part)
      op->setPartitionId(part_spec->start_part);
unknown's avatar
unknown committed
2177 2178
    DBUG_ASSERT(op->getSorted() == sorted);
    DBUG_ASSERT(op->getLockMode() == 
2179
                (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type));
unknown's avatar
unknown committed
2180
    if (op->reset_bounds(m_force_send))
unknown's avatar
unknown committed
2181 2182
      DBUG_RETURN(ndb_err(m_active_trans));
  }
2183
  
2184
  {
2185
    const key_range *keys[2]= { start_key, end_key };
2186
    res= set_bounds(op, active_index, false, keys);
2187 2188
    if (res)
      DBUG_RETURN(res);
2189
  }
2190 2191 2192

  if (!restart && generate_scan_filter(m_cond_stack, op))
    DBUG_RETURN(ndb_err(trans));
2193
  
2194
  if (!restart && (res= define_read_attrs(buf, op)))
2195
  {
2196
    DBUG_RETURN(res);
unknown's avatar
unknown committed
2197
  }
2198 2199 2200 2201 2202 2203

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

/*
2206
  Start full table scan in NDB
unknown's avatar
unknown committed
2207 2208 2209 2210
 */

int ha_ndbcluster::full_table_scan(byte *buf)
{
2211
  int res;
unknown's avatar
unknown committed
2212
  NdbScanOperation *op;
2213
  NdbTransaction *trans= m_active_trans;
2214
  part_id_range part_spec;
unknown's avatar
unknown committed
2215 2216 2217

  DBUG_ENTER("full_table_scan");  
  DBUG_PRINT("enter", ("Starting new scan on %s", m_tabname));
2218
  m_write_op= FALSE;
unknown's avatar
unknown committed
2219

2220 2221 2222
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
  if (!(op=trans->getNdbScanOperation((const NDBTAB *) m_table)) ||
2223
      op->readTuples(lm, 0, parallelism))
unknown's avatar
unknown committed
2224
    ERR_RETURN(trans->getNdbError());
2225
  m_active_cursor= op;
2226 2227 2228 2229

  if (m_use_partition_function)
  {
    part_spec.start_part= 0;
2230
    part_spec.end_part= m_part_info->get_tot_partitions() - 1;
2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254
    prune_partition_set(table, &part_spec);
    DBUG_PRINT("info", ("part_spec.start_part = %u, part_spec.end_part = %u",
                        part_spec.start_part, part_spec.end_part));
    /*
      If partition pruning has found no partition in set
      we can return HA_ERR_END_OF_FILE
      If partition pruning has found exactly one partition in set
      we can optimize scan to run towards that partition only.
    */
    if (part_spec.start_part > part_spec.end_part)
    {
      DBUG_RETURN(HA_ERR_END_OF_FILE);
    }
    else if (part_spec.start_part == part_spec.end_part)
    {
      /*
        Only one partition is required to scan, if sorted is required we
        don't need it any more since output from one ordered partitioned
        index is always sorted.
      */
      m_active_cursor->setPartitionId(part_spec.start_part);
    }
  }

2255 2256
  if (generate_scan_filter(m_cond_stack, op))
    DBUG_RETURN(ndb_err(trans));
unknown's avatar
unknown committed
2257
  if ((res= define_read_attrs(buf, op)))
2258 2259 2260 2261 2262 2263
    DBUG_RETURN(res);

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

unknown's avatar
unknown committed
2266 2267 2268 2269 2270
/*
  Insert one record into NDB
*/
int ha_ndbcluster::write_row(byte *record)
{
unknown's avatar
unknown committed
2271
  bool has_auto_increment;
unknown's avatar
unknown committed
2272
  uint i;
2273
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
2274 2275
  NdbOperation *op;
  int res;
unknown's avatar
unknown committed
2276
  THD *thd= current_thd;
2277 2278
  longlong func_value= 0;
  DBUG_ENTER("ha_ndbcluster::write_row");
2279

2280
  m_write_op= TRUE;
unknown's avatar
unknown committed
2281
  if (!m_use_write && m_ignore_dup_key && table_share->primary_key != MAX_KEY)
2282
  {
2283
    int peek_res= peek_row(record);
2284 2285 2286
    
    if (!peek_res) 
    {
unknown's avatar
unknown committed
2287
      m_dupkey= table_share->primary_key;
2288 2289 2290 2291
      DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
    }
    if (peek_res != HA_ERR_KEY_NOT_FOUND)
      DBUG_RETURN(peek_res);
2292
  }
2293

unknown's avatar
unknown committed
2294
  statistic_increment(thd->status_var.ha_write_count, &LOCK_status);
unknown's avatar
unknown committed
2295 2296
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
    table->timestamp_field->set_time();
2297
  has_auto_increment= (table->next_number_field && record == table->record[0]);
unknown's avatar
unknown committed
2298

unknown's avatar
unknown committed
2299
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)))
unknown's avatar
unknown committed
2300 2301 2302 2303 2304 2305
    ERR_RETURN(trans->getNdbError());

  res= (m_use_write) ? op->writeTuple() :op->insertTuple(); 
  if (res != 0)
    ERR_RETURN(trans->getNdbError());  
 
2306 2307 2308 2309
  if (m_use_partition_function)
  {
    uint32 part_id;
    int error;
2310 2311
    if ((error= m_part_info->get_partition_id(m_part_info, &part_id,
                                              &func_value)))
2312 2313 2314 2315 2316 2317
    {
      DBUG_RETURN(error);
    }
    op->setPartitionId(part_id);
  }

unknown's avatar
unknown committed
2318
  if (table_share->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
2319 2320
  {
    // Table has hidden primary key
2321
    Ndb *ndb= get_ndb();
2322 2323 2324 2325 2326 2327 2328
    Uint64 auto_value= NDB_FAILED_AUTO_INCREMENT;
    uint retries= NDB_AUTO_INCREMENT_RETRIES;
    do {
      auto_value= ndb->getAutoIncrementValue((const NDBTAB *) m_table);
    } while (auto_value == NDB_FAILED_AUTO_INCREMENT && 
             --retries &&
             ndb->getNdbError().status == NdbError::TemporaryError);
2329 2330
    if (auto_value == NDB_FAILED_AUTO_INCREMENT)
      ERR_RETURN(ndb->getNdbError());
unknown's avatar
unknown committed
2331
    if (set_hidden_key(op, table_share->fields, (const byte*)&auto_value))
unknown's avatar
unknown committed
2332 2333 2334 2335 2336
      ERR_RETURN(op->getNdbError());
  } 
  else 
  {
    int res;
2337

2338 2339
    if (has_auto_increment) 
    {
unknown's avatar
unknown committed
2340 2341
      THD *thd= table->in_use;

2342
      m_skip_auto_increment= FALSE;
2343
      update_auto_increment();
unknown's avatar
unknown committed
2344 2345
      /* Ensure that handler is always called for auto_increment values */
      thd->next_insert_id= 0;
2346
      m_skip_auto_increment= !auto_increment_column_changed;
2347
    }
2348

2349
    if ((res= set_primary_key_from_record(op, record)))
2350
      return res;  
unknown's avatar
unknown committed
2351 2352 2353
  }

  // Set non-key attribute(s)
unknown's avatar
unknown committed
2354
  bool set_blob_value= FALSE;
unknown's avatar
unknown committed
2355
  for (i= 0; i < table_share->fields; i++) 
unknown's avatar
unknown committed
2356 2357 2358
  {
    Field *field= table->field[i];
    if (!(field->flags & PRI_KEY_FLAG) &&
unknown's avatar
unknown committed
2359 2360
	(ha_get_bit_in_write_set(i + 1) || !m_use_write) &&
        set_ndb_value(op, field, i, record-table->record[0], &set_blob_value))
2361
    {
2362
      m_skip_auto_increment= TRUE;
unknown's avatar
unknown committed
2363
      ERR_RETURN(op->getNdbError());
2364
    }
unknown's avatar
unknown committed
2365 2366
  }

2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382
  if (m_use_partition_function)
  {
    /*
      We need to set the value of the partition function value in
      NDB since the NDB kernel doesn't have easy access to the function
      to calculate the value.
    */
    if (func_value >= INT_MAX32)
      func_value= INT_MAX32;
    uint32 part_func_value= (uint32)func_value;
    uint no_fields= table_share->fields;
    if (table_share->primary_key == MAX_KEY)
      no_fields++;
    op->setValue(no_fields, part_func_value);
  }

2383 2384
  m_rows_changed++;

unknown's avatar
unknown committed
2385 2386 2387 2388 2389 2390 2391
  /*
    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!
  */
2392
  m_rows_inserted++;
2393
  no_uncommitted_rows_update(1);
2394
  m_bulk_insert_not_flushed= TRUE;
2395
  if ((m_rows_to_insert == (ha_rows) 1) || 
2396
      ((m_rows_inserted % m_bulk_insert_rows) == 0) ||
2397
      m_primary_key_update ||
2398
      set_blob_value)
2399 2400 2401
  {
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
2402 2403
                        "rows_inserted:%d, bulk_insert_rows: %d", 
                        (int)m_rows_inserted, (int)m_bulk_insert_rows));
2404

2405
    m_bulk_insert_not_flushed= FALSE;
2406
    if (m_transaction_on)
2407
    {
unknown's avatar
unknown committed
2408
      if (execute_no_commit(this,trans) != 0)
2409
      {
2410 2411 2412
        m_skip_auto_increment= TRUE;
        no_uncommitted_rows_execute_failure();
        DBUG_RETURN(ndb_err(trans));
2413
      }
2414 2415
    }
    else
2416
    {
unknown's avatar
unknown committed
2417
      if (execute_commit(this,trans) != 0)
2418
      {
2419 2420 2421
        m_skip_auto_increment= TRUE;
        no_uncommitted_rows_execute_failure();
        DBUG_RETURN(ndb_err(trans));
2422
      }
unknown's avatar
unknown committed
2423
      if (trans->restart() != 0)
2424
      {
2425 2426
        DBUG_ASSERT(0);
        DBUG_RETURN(-1);
2427
      }
2428
    }
2429
  }
2430
  if ((has_auto_increment) && (m_skip_auto_increment))
unknown's avatar
unknown committed
2431
  {
2432
    Ndb *ndb= get_ndb();
2433
    Uint64 next_val= (Uint64) table->next_number_field->val_int() + 1;
unknown's avatar
unknown committed
2434
    DBUG_PRINT("info", 
2435
               ("Trying to set next auto increment value to %lu",
2436
                (ulong) next_val));
2437
    if (ndb->setAutoIncrementValue((const NDBTAB *) m_table, next_val, TRUE))
unknown's avatar
unknown committed
2438
      DBUG_PRINT("info", 
2439
                 ("Setting next auto increment value to %u", next_val));  
2440
  }
2441
  m_skip_auto_increment= TRUE;
2442

unknown's avatar
unknown committed
2443 2444 2445 2446 2447 2448 2449
  DBUG_RETURN(0);
}


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

int ha_ndbcluster::key_cmp(uint keynr, const byte * old_row,
2450
                           const byte * new_row)
unknown's avatar
unknown committed
2451 2452 2453 2454 2455 2456 2457 2458 2459
{
  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) !=
2460 2461
          (new_row[key_part->null_offset] & key_part->null_bit))
        return 1;
unknown's avatar
unknown committed
2462
    }
2463
    if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
unknown's avatar
unknown committed
2464 2465 2466
    {

      if (key_part->field->cmp_binary((char*) (old_row + key_part->offset),
2467 2468 2469
                                      (char*) (new_row + key_part->offset),
                                      (ulong) key_part->length))
        return 1;
unknown's avatar
unknown committed
2470 2471 2472 2473
    }
    else
    {
      if (memcmp(old_row+key_part->offset, new_row+key_part->offset,
2474 2475
                 key_part->length))
        return 1;
unknown's avatar
unknown committed
2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487
    }
  }
  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;
2488
  NdbTransaction *trans= m_active_trans;
2489
  NdbScanOperation* cursor= m_active_cursor;
unknown's avatar
unknown committed
2490 2491
  NdbOperation *op;
  uint i;
2492 2493
  uint32 old_part_id= 0, new_part_id= 0;
  int error;
2494
  longlong func_value;
unknown's avatar
unknown committed
2495
  DBUG_ENTER("update_row");
2496
  m_write_op= TRUE;
unknown's avatar
unknown committed
2497
  
unknown's avatar
unknown committed
2498
  statistic_increment(thd->status_var.ha_update_count, &LOCK_status);
unknown's avatar
unknown committed
2499
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
2500
  {
unknown's avatar
unknown committed
2501
    table->timestamp_field->set_time();
2502
    ha_set_bit_in_write_set(table->timestamp_field->fieldnr);
2503
  }
unknown's avatar
unknown committed
2504

2505 2506
  if (m_use_partition_function &&
      (error= get_parts_for_update(old_data, new_data, table->record[0],
2507 2508
                                   m_part_info, &old_part_id, &new_part_id,
                                   &func_value)))
2509 2510 2511 2512
  {
    DBUG_RETURN(error);
  }

2513 2514 2515 2516 2517 2518
  /*
   * Check for update of primary key or partition change
   * for special handling
   */  
  if (((table_share->primary_key != MAX_KEY) &&
       key_cmp(table_share->primary_key, old_data, new_data)) ||
2519
      (old_part_id != new_part_id))
2520
  {
2521
    int read_res, insert_res, delete_res, undo_res;
2522

2523 2524
    DBUG_PRINT("info", ("primary key update or partition change, "
                        "doing read+delete+insert"));
2525
    // Get all old fields, since we optimize away fields not in query
2526
    read_res= complemented_read(old_data, new_data, old_part_id);
2527 2528
    if (read_res)
    {
2529
      DBUG_PRINT("info", ("read failed"));
2530 2531
      DBUG_RETURN(read_res);
    }
2532
    // Delete old row
2533
    m_primary_key_update= TRUE;
2534
    delete_res= delete_row(old_data);
2535
    m_primary_key_update= FALSE;
2536 2537 2538
    if (delete_res)
    {
      DBUG_PRINT("info", ("delete failed"));
2539
      DBUG_RETURN(delete_res);
2540
    }     
2541 2542
    // Insert new row
    DBUG_PRINT("info", ("delete succeded"));
2543
    m_primary_key_update= TRUE;
2544
    insert_res= write_row(new_data);
2545
    m_primary_key_update= FALSE;
2546 2547 2548 2549 2550
    if (insert_res)
    {
      DBUG_PRINT("info", ("insert failed"));
      if (trans->commitStatus() == NdbConnection::Started)
      {
2551
        // Undo delete_row(old_data)
2552
        m_primary_key_update= TRUE;
2553 2554 2555 2556 2557 2558
        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");
2559 2560 2561 2562 2563
        m_primary_key_update= FALSE;
      }
      DBUG_RETURN(insert_res);
    }
    DBUG_PRINT("info", ("delete+insert succeeded"));
2564
    DBUG_RETURN(0);
2565
  }
2566

2567
  if (cursor)
unknown's avatar
unknown committed
2568
  {
2569 2570 2571 2572 2573 2574 2575 2576
    /*
      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"));
2577
    if (!(op= cursor->updateCurrentTuple()))
2578
      ERR_RETURN(trans->getNdbError());
2579
    m_ops_pending++;
2580
    if (uses_blob_value())
2581
      m_blobs_pending= TRUE;
2582 2583
    if (m_use_partition_function)
      cursor->setPartitionId(new_part_id);
2584 2585 2586
  }
  else
  {  
unknown's avatar
unknown committed
2587
    if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) ||
2588
        op->updateTuple() != 0)
2589 2590
      ERR_RETURN(trans->getNdbError());  
    
2591 2592
    if (m_use_partition_function)
      op->setPartitionId(new_part_id);
unknown's avatar
unknown committed
2593
    if (table_share->primary_key == MAX_KEY) 
2594 2595 2596 2597 2598
    {
      // 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 
2599 2600
      // read into m_ref
      DBUG_DUMP("key", m_ref, NDB_HIDDEN_PRIMARY_KEY_LENGTH);
2601
      
unknown's avatar
unknown committed
2602
      if (set_hidden_key(op, table->s->fields, m_ref))
2603
        ERR_RETURN(op->getNdbError());
2604 2605 2606 2607
    } 
    else 
    {
      int res;
2608
      if ((res= set_primary_key_from_record(op, old_data)))
2609
        DBUG_RETURN(res);
2610
    }
unknown's avatar
unknown committed
2611 2612
  }

2613 2614
  m_rows_changed++;

unknown's avatar
unknown committed
2615
  // Set non-key attribute(s)
unknown's avatar
unknown committed
2616
  for (i= 0; i < table_share->fields; i++) 
unknown's avatar
unknown committed
2617 2618
  {
    Field *field= table->field[i];
2619
    if (ha_get_bit_in_write_set(i+1) &&
unknown's avatar
unknown committed
2620
        (!(field->flags & PRI_KEY_FLAG)) &&
unknown's avatar
unknown committed
2621
        set_ndb_value(op, field, i, new_data - table->record[0]))
unknown's avatar
unknown committed
2622 2623
      ERR_RETURN(op->getNdbError());
  }
2624

2625 2626 2627 2628 2629 2630 2631 2632 2633 2634
  if (m_use_partition_function)
  {
    if (func_value >= INT_MAX32)
      func_value= INT_MAX32;
    uint32 part_func_value= (uint32)func_value;
    uint no_fields= table_share->fields;
    if (table_share->primary_key == MAX_KEY)
      no_fields++;
    op->setValue(no_fields, part_func_value);
  }
unknown's avatar
unknown committed
2635
  // Execute update operation
unknown's avatar
unknown committed
2636
  if (!cursor && execute_no_commit(this,trans) != 0) {
2637
    no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
2638
    DBUG_RETURN(ndb_err(trans));
2639
  }
unknown's avatar
unknown committed
2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650
  
  DBUG_RETURN(0);
}


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

int ha_ndbcluster::delete_row(const byte *record)
{
unknown's avatar
unknown committed
2651
  THD *thd= current_thd;
2652
  NdbTransaction *trans= m_active_trans;
2653
  NdbScanOperation* cursor= m_active_cursor;
unknown's avatar
unknown committed
2654
  NdbOperation *op;
2655 2656
  uint32 part_id;
  int error;
unknown's avatar
unknown committed
2657
  DBUG_ENTER("delete_row");
2658
  m_write_op= TRUE;
unknown's avatar
unknown committed
2659

unknown's avatar
unknown committed
2660
  statistic_increment(thd->status_var.ha_delete_count,&LOCK_status);
unknown's avatar
unknown committed
2661
  m_rows_changed++;
unknown's avatar
unknown committed
2662

2663 2664 2665 2666 2667 2668 2669
  if (m_use_partition_function &&
      (error= get_part_for_delete(record, table->record[0], m_part_info,
                                  &part_id)))
  {
    DBUG_RETURN(error);
  }

2670
  if (cursor)
unknown's avatar
unknown committed
2671
  {
2672
    /*
2673
      We are scanning records and want to delete the record
2674
      that was just found, call deleteTuple on the cursor 
2675
      to take over the lock to a new delete operation
2676 2677 2678 2679
      And thus setting the primary key of the record from 
      the active record in cursor
    */
    DBUG_PRINT("info", ("Calling deleteTuple on cursor"));
2680
    if (cursor->deleteCurrentTuple() != 0)
2681
      ERR_RETURN(trans->getNdbError());     
2682
    m_ops_pending++;
unknown's avatar
unknown committed
2683

2684 2685 2686
    if (m_use_partition_function)
      cursor->setPartitionId(part_id);

2687 2688
    no_uncommitted_rows_update(-1);

2689 2690 2691
    if (!m_primary_key_update)
      // If deleting from cursor, NoCommit will be handled in next_result
      DBUG_RETURN(0);
2692 2693
  }
  else
unknown's avatar
unknown committed
2694
  {
2695
    
unknown's avatar
unknown committed
2696
    if (!(op=trans->getNdbOperation((const NDBTAB *) m_table)) || 
2697
        op->deleteTuple() != 0)
2698 2699
      ERR_RETURN(trans->getNdbError());
    
2700 2701 2702
    if (m_use_partition_function)
      op->setPartitionId(part_id);

2703 2704
    no_uncommitted_rows_update(-1);
    
unknown's avatar
unknown committed
2705
    if (table_share->primary_key == MAX_KEY) 
2706 2707 2708 2709
    {
      // This table has no primary key, use "hidden" primary key
      DBUG_PRINT("info", ("Using hidden key"));
      
unknown's avatar
unknown committed
2710
      if (set_hidden_key(op, table->s->fields, m_ref))
2711
        ERR_RETURN(op->getNdbError());
2712 2713 2714 2715
    } 
    else 
    {
      int res;
2716 2717
      if ((res= set_primary_key_from_record(op, record)))
        return res;  
2718
    }
unknown's avatar
unknown committed
2719
  }
2720

unknown's avatar
unknown committed
2721
  // Execute delete operation
unknown's avatar
unknown committed
2722
  if (execute_no_commit(this,trans) != 0) {
2723
    no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
2724
    DBUG_RETURN(ndb_err(trans));
2725
  }
unknown's avatar
unknown committed
2726 2727
  DBUG_RETURN(0);
}
2728
  
unknown's avatar
unknown committed
2729 2730 2731 2732 2733
/*
  Unpack a record read from NDB 

  SYNOPSIS
    unpack_record()
2734
    buf                 Buffer to store read row
unknown's avatar
unknown committed
2735 2736 2737 2738 2739 2740 2741 2742

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

unknown's avatar
unknown committed
2743 2744
void ndb_unpack_record(TABLE *table, NdbValue *value,
                       MY_BITMAP *defined, byte *buf)
unknown's avatar
unknown committed
2745
{
unknown's avatar
unknown committed
2746
  Field **p_field= table->field, *field= *p_field;
unknown's avatar
unknown committed
2747
  uint row_offset= (uint) (buf - table->record[0]);
unknown's avatar
unknown committed
2748
  DBUG_ENTER("ndb_unpack_record");
2749

unknown's avatar
unknown committed
2750
  // Set null flag(s)
2751
  bzero(buf, table->s->null_bytes);
unknown's avatar
unknown committed
2752 2753
  for ( ; field;
       p_field++, value++, field= *p_field)
unknown's avatar
unknown committed
2754
  {
unknown's avatar
unknown committed
2755 2756
    if ((*value).ptr)
    {
unknown's avatar
unknown committed
2757
      if (!(field->flags & BLOB_FLAG))
unknown's avatar
unknown committed
2758
      {
unknown's avatar
unknown committed
2759 2760
        int is_null= (*value).rec->isNULL();
        if (is_null)
2761
        {
unknown's avatar
unknown committed
2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780
          if (is_null > 0)
          {
            DBUG_PRINT("info",("[%u] NULL",
                               (*value).rec->getColumn()->getColumnNo()));
            field->set_null(row_offset);
          }
          else
          {
            DBUG_PRINT("info",("[%u] UNDEFINED",
                               (*value).rec->getColumn()->getColumnNo()));
            bitmap_clear_bit(defined,
                             (*value).rec->getColumn()->getColumnNo());
          }
        }
        else if (field->type() == MYSQL_TYPE_BIT)
        {
          byte *save_field_ptr= field->ptr;
          field->ptr= save_field_ptr + row_offset;
          if (field->pack_length() < 5)
2781 2782
          {
            DBUG_PRINT("info", ("bit field H'%.8X", 
2783
                                (*value).rec->u_32_value()));
unknown's avatar
unknown committed
2784 2785
            ((Field_bit*) field)->store((longlong)
                                        (*value).rec->u_32_value(), FALSE);
2786 2787 2788 2789
          }
          else
          {
            DBUG_PRINT("info", ("bit field H'%.8X%.8X",
unknown's avatar
unknown committed
2790 2791 2792 2793
                                *(Uint32*) (*value).rec->aRef(),
                                *((Uint32*) (*value).rec->aRef()+1)));
            ((Field_bit*) field)->store((longlong)
                                         (*value).rec->u_64_value(),TRUE);
2794
          }
unknown's avatar
unknown committed
2795 2796 2797 2798 2799 2800 2801 2802 2803 2804
          field->ptr= save_field_ptr;
          DBUG_PRINT("info",("[%u] SET",
                             (*value).rec->getColumn()->getColumnNo()));
          DBUG_DUMP("info", (const char*) field->ptr, field->field_length);
        }
        else
        {
          DBUG_PRINT("info",("[%u] SET",
                             (*value).rec->getColumn()->getColumnNo()));
          DBUG_DUMP("info", (const char*) field->ptr, field->field_length);
2805
        }
unknown's avatar
unknown committed
2806 2807 2808
      }
      else
      {
unknown's avatar
unknown committed
2809
        NdbBlob *ndb_blob= (*value).blob;
unknown's avatar
unknown committed
2810
        uint col_no = ndb_blob->getColumn()->getColumnNo();
2811 2812
        int isNull;
        ndb_blob->getDefined(isNull);
unknown's avatar
unknown committed
2813
        if (isNull == 1)
2814
        {
unknown's avatar
unknown committed
2815
          DBUG_PRINT("info",("[%u] NULL", col_no));
unknown's avatar
unknown committed
2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832
          field->set_null(row_offset);
        }
        else if (isNull == -1)
        {
          DBUG_PRINT("info",("[%u] UNDEFINED", col_no));
          bitmap_clear_bit(defined, col_no);
        }
        else
        {
#ifndef DBUG_OFF
          // pointer vas set in get_ndb_blobs_value
          Field_blob *field_blob= (Field_blob*)field;
          char* ptr;
          field_blob->get_ptr(&ptr, row_offset);
          uint32 len= field_blob->get_length(row_offset);
          DBUG_PRINT("info",("[%u] SET ptr=%p len=%u", col_no, ptr, len));
#endif
2833
        }
unknown's avatar
unknown committed
2834 2835
      }
    }
unknown's avatar
unknown committed
2836
  }
unknown's avatar
unknown committed
2837 2838 2839 2840 2841 2842
  DBUG_VOID_RETURN;
}

void ha_ndbcluster::unpack_record(byte *buf)
{
  ndb_unpack_record(table, m_value, 0, buf);
unknown's avatar
unknown committed
2843 2844
#ifndef DBUG_OFF
  // Read and print all values that was fetched
unknown's avatar
unknown committed
2845
  if (table_share->primary_key == MAX_KEY)
unknown's avatar
unknown committed
2846 2847
  {
    // Table with hidden primary key
unknown's avatar
unknown committed
2848
    int hidden_no= table_share->fields;
unknown's avatar
unknown committed
2849
    const NDBTAB *tab= (const NDBTAB *) m_table;
unknown's avatar
unknown committed
2850
    const NDBCOL *hidden_col= tab->getColumn(hidden_no);
2851
    const NdbRecAttr* rec= m_value[hidden_no].rec;
unknown's avatar
unknown committed
2852
    DBUG_ASSERT(rec);
unknown's avatar
unknown committed
2853
    DBUG_PRINT("hidden", ("%d: %s \"%llu\"", hidden_no,
unknown's avatar
unknown committed
2854
                          hidden_col->getName(), rec->u_64_value()));
unknown's avatar
unknown committed
2855 2856
  }
  //DBUG_EXECUTE("value", print_results(););
unknown's avatar
unknown committed
2857 2858 2859 2860 2861
#endif
}

/*
  Utility function to print/dump the fetched field
unknown's avatar
unknown committed
2862 2863 2864
  to avoid unnecessary work, wrap in DBUG_EXECUTE as in:

    DBUG_EXECUTE("value", print_results(););
unknown's avatar
unknown committed
2865 2866 2867 2868 2869 2870 2871
 */

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

#ifndef DBUG_OFF
unknown's avatar
Merge  
unknown committed
2872

2873
  char buf_type[MAX_FIELD_WIDTH], buf_val[MAX_FIELD_WIDTH];
unknown's avatar
Merge  
unknown committed
2874
  String type(buf_type, sizeof(buf_type), &my_charset_bin);
2875
  String val(buf_val, sizeof(buf_val), &my_charset_bin);
unknown's avatar
unknown committed
2876
  for (uint f= 0; f < table_share->fields; f++)
unknown's avatar
unknown committed
2877
  {
unknown's avatar
Merge  
unknown committed
2878
    /* Use DBUG_PRINT since DBUG_FILE cannot be filtered out */
2879
    char buf[2000];
unknown's avatar
unknown committed
2880
    Field *field;
2881
    void* ptr;
unknown's avatar
unknown committed
2882
    NdbValue value;
unknown's avatar
unknown committed
2883

2884
    buf[0]= 0;
unknown's avatar
Merge  
unknown committed
2885
    field= table->field[f];
unknown's avatar
unknown committed
2886
    if (!(value= m_value[f]).ptr)
unknown's avatar
unknown committed
2887
    {
unknown's avatar
unknown committed
2888
      strmov(buf, "not read");
2889
      goto print_value;
unknown's avatar
unknown committed
2890
    }
2891

2892
    ptr= field->ptr;
unknown's avatar
unknown committed
2893 2894

    if (! (field->flags & BLOB_FLAG))
unknown's avatar
unknown committed
2895
    {
unknown's avatar
unknown committed
2896 2897
      if (value.rec->isNULL())
      {
unknown's avatar
unknown committed
2898
        strmov(buf, "NULL");
2899
        goto print_value;
unknown's avatar
unknown committed
2900
      }
2901 2902 2903 2904 2905
      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
2906 2907 2908
    }
    else
    {
2909
      NdbBlob *ndb_blob= value.blob;
unknown's avatar
unknown committed
2910
      bool isNull= TRUE;
unknown's avatar
unknown committed
2911
      ndb_blob->getNull(isNull);
unknown's avatar
unknown committed
2912 2913
      if (isNull)
        strmov(buf, "NULL");
unknown's avatar
unknown committed
2914
    }
unknown's avatar
Merge  
unknown committed
2915

2916
print_value:
unknown's avatar
Merge  
unknown committed
2917
    DBUG_PRINT("value", ("%u,%s: %s", f, field->field_name, buf));
unknown's avatar
unknown committed
2918 2919 2920 2921 2922 2923
  }
#endif
  DBUG_VOID_RETURN;
}


2924
int ha_ndbcluster::index_init(uint index, bool sorted)
unknown's avatar
unknown committed
2925
{
2926
  DBUG_ENTER("ha_ndbcluster::index_init");
2927 2928 2929 2930
  DBUG_PRINT("enter", ("index: %u  sorted: %d", index, sorted));
  active_index= index;
  m_sorted= sorted;
  DBUG_RETURN(0);
unknown's avatar
unknown committed
2931 2932 2933 2934 2935
}


int ha_ndbcluster::index_end()
{
2936
  DBUG_ENTER("ha_ndbcluster::index_end");
2937
  DBUG_RETURN(close_scan());
unknown's avatar
unknown committed
2938 2939
}

2940 2941 2942 2943 2944 2945 2946 2947
/**
 * 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;
2948
  const byte* end_ptr= key + key_len;
2949 2950 2951 2952 2953 2954
  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
2955
    if (curr_part->null_bit && *key)
2956 2957 2958 2959 2960 2961
      return 1;

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

int ha_ndbcluster::index_read(byte *buf,
2964 2965
                              const byte *key, uint key_len, 
                              enum ha_rkey_function find_flag)
unknown's avatar
unknown committed
2966
{
2967 2968
  key_range start_key;
  bool descending= FALSE;
2969
  DBUG_ENTER("ha_ndbcluster::index_read");
unknown's avatar
unknown committed
2970 2971 2972
  DBUG_PRINT("enter", ("active_index: %u, key_len: %u, find_flag: %d", 
                       active_index, key_len, find_flag));

2973 2974 2975
  start_key.key= key;
  start_key.length= key_len;
  start_key.flag= find_flag;
2976
  descending= FALSE;
2977 2978 2979 2980 2981 2982 2983 2984 2985 2986
  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;
  }
2987 2988
  DBUG_RETURN(read_range_first_to_buf(&start_key, 0, descending,
                                      m_sorted, buf));
unknown's avatar
unknown committed
2989 2990 2991 2992
}


int ha_ndbcluster::index_read_idx(byte *buf, uint index_no, 
2993 2994
                              const byte *key, uint key_len, 
                              enum ha_rkey_function find_flag)
unknown's avatar
unknown committed
2995
{
unknown's avatar
unknown committed
2996
  statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status);
2997
  DBUG_ENTER("ha_ndbcluster::index_read_idx");
unknown's avatar
unknown committed
2998
  DBUG_PRINT("enter", ("index_no: %u, key_len: %u", index_no, key_len));  
unknown's avatar
unknown committed
2999
  close_scan();
3000
  index_init(index_no, 0);  
unknown's avatar
unknown committed
3001 3002 3003 3004 3005 3006
  DBUG_RETURN(index_read(buf, key, key_len, find_flag));
}


int ha_ndbcluster::index_next(byte *buf)
{
3007
  DBUG_ENTER("ha_ndbcluster::index_next");
unknown's avatar
unknown committed
3008
  statistic_increment(current_thd->status_var.ha_read_next_count,
3009
                      &LOCK_status);
3010
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
3011 3012 3013 3014 3015
}


int ha_ndbcluster::index_prev(byte *buf)
{
3016
  DBUG_ENTER("ha_ndbcluster::index_prev");
unknown's avatar
unknown committed
3017
  statistic_increment(current_thd->status_var.ha_read_prev_count,
3018
                      &LOCK_status);
3019
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
3020 3021 3022 3023 3024
}


int ha_ndbcluster::index_first(byte *buf)
{
3025
  DBUG_ENTER("ha_ndbcluster::index_first");
unknown's avatar
unknown committed
3026
  statistic_increment(current_thd->status_var.ha_read_first_count,
3027
                      &LOCK_status);
unknown's avatar
unknown committed
3028 3029 3030
  // Start the ordered index scan and fetch the first row

  // Only HA_READ_ORDER indexes get called by index_first
3031
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, FALSE, buf, NULL));
unknown's avatar
unknown committed
3032 3033 3034 3035 3036
}


int ha_ndbcluster::index_last(byte *buf)
{
3037
  DBUG_ENTER("ha_ndbcluster::index_last");
3038
  statistic_increment(current_thd->status_var.ha_read_last_count,&LOCK_status);
3039
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, TRUE, buf, NULL));
unknown's avatar
unknown committed
3040 3041
}

3042 3043 3044 3045 3046
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
3047

3048
int ha_ndbcluster::read_range_first_to_buf(const key_range *start_key,
3049
                                           const key_range *end_key,
3050
                                           bool desc, bool sorted,
3051
                                           byte* buf)
3052
{
3053 3054 3055 3056
  part_id_range part_spec;
  ndb_index_type type= get_index_type(active_index);
  const KEY* key_info= table->key_info+active_index;
  int error; 
3057
  DBUG_ENTER("ha_ndbcluster::read_range_first_to_buf");
3058
  DBUG_PRINT("info", ("desc: %d, sorted: %d", desc, sorted));
3059

3060 3061 3062
  if (m_use_partition_function)
  {
    get_partition_set(table, buf, active_index, start_key, &part_spec);
3063 3064 3065 3066 3067 3068 3069 3070
    DBUG_PRINT("info", ("part_spec.start_part = %u, part_spec.end_part = %u",
                        part_spec.start_part, part_spec.end_part));
    /*
      If partition pruning has found no partition in set
      we can return HA_ERR_END_OF_FILE
      If partition pruning has found exactly one partition in set
      we can optimize scan to run towards that partition only.
    */
3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084
    if (part_spec.start_part > part_spec.end_part)
    {
      DBUG_RETURN(HA_ERR_END_OF_FILE);
    }
    else if (part_spec.start_part == part_spec.end_part)
    {
      /*
        Only one partition is required to scan, if sorted is required we
        don't need it any more since output from one ordered partitioned
        index is always sorted.
      */
      sorted= FALSE;
    }
  }
3085

3086 3087
  m_write_op= FALSE;
  switch (type){
3088
  case PRIMARY_KEY_ORDERED_INDEX:
3089
  case PRIMARY_KEY_INDEX:
3090
    if (start_key && 
3091 3092
        start_key->length == key_info->key_length &&
        start_key->flag == HA_READ_KEY_EXACT)
3093
    {
unknown's avatar
unknown committed
3094
      if (m_active_cursor && (error= close_scan()))
3095
        DBUG_RETURN(error);
3096 3097
      DBUG_RETURN(pk_read(start_key->key, start_key->length, buf,
                          part_spec.start_part));
3098
    }
3099
    break;
3100
  case UNIQUE_ORDERED_INDEX:
3101
  case UNIQUE_INDEX:
3102
    if (start_key && start_key->length == key_info->key_length &&
3103 3104
        start_key->flag == HA_READ_KEY_EXACT && 
        !check_null_in_key(key_info, start_key->key, start_key->length))
3105
    {
unknown's avatar
unknown committed
3106
      if (m_active_cursor && (error= close_scan()))
3107
        DBUG_RETURN(error);
3108
      DBUG_RETURN(unique_index_read(start_key->key, start_key->length, buf));
3109
    }
3110 3111 3112 3113
    break;
  default:
    break;
  }
3114
  // Start the ordered index scan and fetch the first row
3115 3116
  DBUG_RETURN(ordered_index_scan(start_key, end_key, sorted, desc, buf,
                                 &part_spec));
3117 3118
}

unknown's avatar
unknown committed
3119
int ha_ndbcluster::read_range_first(const key_range *start_key,
3120 3121
                                    const key_range *end_key,
                                    bool eq_r, bool sorted)
unknown's avatar
unknown committed
3122 3123 3124
{
  byte* buf= table->record[0];
  DBUG_ENTER("ha_ndbcluster::read_range_first");
3125 3126
  DBUG_RETURN(read_range_first_to_buf(start_key, end_key, FALSE,
                                      sorted, buf));
unknown's avatar
unknown committed
3127 3128
}

3129
int ha_ndbcluster::read_range_next()
3130 3131 3132 3133 3134 3135
{
  DBUG_ENTER("ha_ndbcluster::read_range_next");
  DBUG_RETURN(next_result(table->record[0]));
}


unknown's avatar
unknown committed
3136 3137
int ha_ndbcluster::rnd_init(bool scan)
{
3138
  NdbScanOperation *cursor= m_active_cursor;
unknown's avatar
unknown committed
3139 3140
  DBUG_ENTER("rnd_init");
  DBUG_PRINT("enter", ("scan: %d", scan));
3141
  // Check if scan is to be restarted
unknown's avatar
unknown committed
3142 3143 3144 3145
  if (cursor)
  {
    if (!scan)
      DBUG_RETURN(1);
unknown's avatar
unknown committed
3146
    if (cursor->restart(m_force_send) != 0)
3147 3148 3149 3150
    {
      DBUG_ASSERT(0);
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
3151
  }
unknown's avatar
unknown committed
3152
  index_init(table_share->primary_key, 0);
unknown's avatar
unknown committed
3153 3154 3155
  DBUG_RETURN(0);
}

3156 3157
int ha_ndbcluster::close_scan()
{
3158
  NdbTransaction *trans= m_active_trans;
3159 3160
  DBUG_ENTER("close_scan");

unknown's avatar
unknown committed
3161 3162
  m_multi_cursor= 0;
  if (!m_active_cursor && !m_multi_cursor)
3163 3164
    DBUG_RETURN(1);

unknown's avatar
unknown committed
3165
  NdbScanOperation *cursor= m_active_cursor ? m_active_cursor : m_multi_cursor;
unknown's avatar
unknown committed
3166
  
3167
  if (m_ops_pending)
unknown's avatar
unknown committed
3168 3169 3170 3171 3172
  {
    /*
      Take over any pending transactions to the 
      deleteing/updating transaction before closing the scan    
    */
3173
    DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending));    
unknown's avatar
unknown committed
3174
    if (execute_no_commit(this,trans) != 0) {
3175
      no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
3176
      DBUG_RETURN(ndb_err(trans));
3177
    }
3178
    m_ops_pending= 0;
unknown's avatar
unknown committed
3179 3180
  }
  
unknown's avatar
unknown committed
3181
  cursor->close(m_force_send, TRUE);
unknown's avatar
unknown committed
3182
  m_active_cursor= m_multi_cursor= NULL;
unknown's avatar
unknown committed
3183
  DBUG_RETURN(0);
3184
}
unknown's avatar
unknown committed
3185 3186 3187 3188

int ha_ndbcluster::rnd_end()
{
  DBUG_ENTER("rnd_end");
3189
  DBUG_RETURN(close_scan());
unknown's avatar
unknown committed
3190 3191 3192 3193 3194 3195
}


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

unknown's avatar
unknown committed
3199
  if (!m_active_cursor)
3200 3201
    DBUG_RETURN(full_table_scan(buf));
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214
}


/*
  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
3215
  statistic_increment(current_thd->status_var.ha_read_rnd_count,
3216
                      &LOCK_status);
unknown's avatar
unknown committed
3217 3218
  // The primary key for the record is stored in pos
  // Perform a pk_read using primary key "index"
3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232
  {
    part_id_range part_spec;
    if (m_use_partition_function)
    {
      key_range key_spec;
      KEY *key_info= table->key_info + active_index;
      key_spec.key= pos;
      key_spec.length= ref_length;
      key_spec.flag= HA_READ_KEY_EXACT;
      get_full_part_id_from_key(table, buf, key_info, &key_spec, &part_spec);
      DBUG_ASSERT(part_spec.start_part == part_spec.end_part);
    }
    DBUG_RETURN(pk_read(pos, ref_length, buf, part_spec.start_part));
  }
unknown's avatar
unknown committed
3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249
}


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

unknown's avatar
unknown committed
3250
  if (table_share->primary_key != MAX_KEY) 
unknown's avatar
unknown committed
3251
  {
unknown's avatar
unknown committed
3252
    key_info= table->key_info + table_share->primary_key;
unknown's avatar
unknown committed
3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267
    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;
      }
3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287

      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
3288 3289 3290 3291 3292 3293
    }
  } 
  else 
  {
    // No primary key, get hidden key
    DBUG_PRINT("info", ("Getting hidden key"));
3294
#ifndef DBUG_OFF
3295
    int hidden_no= table->s->fields;
unknown's avatar
unknown committed
3296
    const NDBTAB *tab= (const NDBTAB *) m_table;  
unknown's avatar
unknown committed
3297 3298 3299 3300
    const NDBCOL *hidden_col= tab->getColumn(hidden_no);
    DBUG_ASSERT(hidden_col->getPrimaryKey() && 
                hidden_col->getAutoIncrement() &&
                ref_length == NDB_HIDDEN_PRIMARY_KEY_LENGTH);
3301
#endif
3302
    memcpy(ref, m_ref, ref_length);
unknown's avatar
unknown committed
3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321
  }
  
  DBUG_DUMP("ref", (char*)ref, ref_length);
  DBUG_VOID_RETURN;
}


void ha_ndbcluster::info(uint flag)
{
  DBUG_ENTER("info");
  DBUG_PRINT("enter", ("flag: %d", flag));
  
  if (flag & HA_STATUS_POS)
    DBUG_PRINT("info", ("HA_STATUS_POS"));
  if (flag & HA_STATUS_NO_LOCK)
    DBUG_PRINT("info", ("HA_STATUS_NO_LOCK"));
  if (flag & HA_STATUS_TIME)
    DBUG_PRINT("info", ("HA_STATUS_TIME"));
  if (flag & HA_STATUS_VARIABLE)
3322
  {
unknown's avatar
unknown committed
3323
    DBUG_PRINT("info", ("HA_STATUS_VARIABLE"));
3324 3325
    if (m_table_info)
    {
3326
      if (m_ha_not_exact_count)
3327
        records= 100;
3328
      else
3329
        records_update();
3330 3331 3332
    }
    else
    {
3333 3334 3335
      if ((my_errno= check_ndb_connection()))
        DBUG_VOID_RETURN;
      Ndb *ndb= get_ndb();
3336 3337
      struct Ndb_statistics stat;
      if (current_thd->variables.ndb_use_exact_count &&
3338
          ndb_get_table_statistics(ndb, m_tabname, &stat) == 0)
3339
      {
3340 3341 3342
        mean_rec_length= stat.row_size;
        data_file_length= stat.fragment_memory;
        records= stat.row_count;
3343 3344 3345
      }
      else
      {
3346 3347
        mean_rec_length= 0;
        records= 100;
3348
      }
3349
    }
3350
  }
unknown's avatar
unknown committed
3351 3352 3353 3354 3355
  if (flag & HA_STATUS_CONST)
  {
    DBUG_PRINT("info", ("HA_STATUS_CONST"));
    set_rec_per_key();
  }
unknown's avatar
unknown committed
3356
  if (flag & HA_STATUS_ERRKEY)
3357
  {
unknown's avatar
unknown committed
3358
    DBUG_PRINT("info", ("HA_STATUS_ERRKEY"));
3359
    errkey= m_dupkey;
3360
  }
unknown's avatar
unknown committed
3361
  if (flag & HA_STATUS_AUTO)
3362
  {
unknown's avatar
unknown committed
3363
    DBUG_PRINT("info", ("HA_STATUS_AUTO"));
3364 3365 3366 3367 3368 3369 3370 3371
    if (m_table)
    {
      Ndb *ndb= get_ndb();
      
      auto_increment_value= 
        ndb->readAutoIncrementValue((const NDBTAB *) m_table);
    }
  }
unknown's avatar
unknown committed
3372 3373 3374
  DBUG_VOID_RETURN;
}

3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388

void ha_ndbcluster::get_dynamic_partition_info(PARTITION_INFO *stat_info,
                                               uint part_id)
{
  /* 
     This functions should be fixed. Suggested fix: to
     implement ndb function which retrives the statistics
     about ndb partitions.
  */
  bzero((char*) stat_info, sizeof(PARTITION_INFO));
  return;
}


unknown's avatar
unknown committed
3389 3390 3391 3392 3393 3394
int ha_ndbcluster::extra(enum ha_extra_function operation)
{
  DBUG_ENTER("extra");
  switch (operation) {
  case HA_EXTRA_RESET:                 /* Reset database to after open */
    DBUG_PRINT("info", ("HA_EXTRA_RESET"));
3395 3396
    DBUG_PRINT("info", ("Clearing condition stack"));
    cond_clear();
3397 3398 3399 3400 3401
    /*
     * Regular partition pruning will set the bitmap appropriately.
     * Some queries like ALTER TABLE doesn't use partition pruning and
     * thus the 'used_partitions' bitmap needs to be initialized
     */
unknown's avatar
unknown committed
3402
    if (m_part_info)
3403
      bitmap_set_all(&m_part_info->used_partitions);
unknown's avatar
unknown committed
3404 3405 3406
    break;
  case HA_EXTRA_IGNORE_DUP_KEY:       /* Dup keys don't rollback everything*/
    DBUG_PRINT("info", ("HA_EXTRA_IGNORE_DUP_KEY"));
3407 3408 3409 3410 3411 3412
    if (current_thd->lex->sql_command == SQLCOM_REPLACE)
    {
      DBUG_PRINT("info", ("Turning ON use of write instead of insert"));
      m_use_write= TRUE;
    } else 
    {
3413 3414
      DBUG_PRINT("info", ("Ignoring duplicate key"));
      m_ignore_dup_key= TRUE;
3415
    }
unknown's avatar
unknown committed
3416 3417 3418 3419
    break;
  case HA_EXTRA_NO_IGNORE_DUP_KEY:
    DBUG_PRINT("info", ("HA_EXTRA_NO_IGNORE_DUP_KEY"));
    DBUG_PRINT("info", ("Turning OFF use of write instead of insert"));
unknown's avatar
unknown committed
3420
    m_use_write= FALSE;
3421
    m_ignore_dup_key= FALSE;
unknown's avatar
unknown committed
3422
    break;
unknown's avatar
unknown committed
3423 3424 3425 3426 3427 3428 3429 3430 3431 3432
  case HA_EXTRA_IGNORE_NO_KEY:
    DBUG_PRINT("info", ("HA_EXTRA_IGNORE_NO_KEY"));
    DBUG_PRINT("info", ("Turning on AO_IgnoreError at Commit/NoCommit"));
    m_ignore_no_key= TRUE;
    break;
  case HA_EXTRA_NO_IGNORE_NO_KEY:
    DBUG_PRINT("info", ("HA_EXTRA_NO_IGNORE_NO_KEY"));
    DBUG_PRINT("info", ("Turning on AO_IgnoreError at Commit/NoCommit"));
    m_ignore_no_key= FALSE;
    break;
3433 3434
  default:
    break;
unknown's avatar
unknown committed
3435 3436 3437 3438 3439
  }
  
  DBUG_RETURN(0);
}

3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452
/* 
   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
3453
  const NDBTAB *tab= (const NDBTAB *) m_table;    
3454 3455

  DBUG_ENTER("start_bulk_insert");
unknown's avatar
unknown committed
3456
  DBUG_PRINT("enter", ("rows: %d", (int)rows));
3457
  
3458 3459
  m_rows_inserted= (ha_rows) 0;
  if (rows == (ha_rows) 0)
unknown's avatar
unknown committed
3460
  {
3461 3462
    /* We don't know how many will be inserted, guess */
    m_rows_to_insert= m_autoincrement_prefetch;
unknown's avatar
unknown committed
3463
  }
3464 3465
  else
    m_rows_to_insert= rows; 
3466 3467 3468 3469 3470 3471 3472 3473

  /* 
    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.   
  */
3474
  const int bytesperbatch= 8192;
3475
  bytes= 12 + tab->getRowSizeInBytes() + 4 * tab->getNoOfColumns();
3476
  batch= bytesperbatch/bytes;
3477 3478
  batch= batch == 0 ? 1 : batch;
  DBUG_PRINT("info", ("batch: %d, bytes: %d", batch, bytes));
3479
  m_bulk_insert_rows= batch;
3480 3481 3482 3483 3484 3485 3486 3487 3488

  DBUG_VOID_RETURN;
}

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

3491
  DBUG_ENTER("end_bulk_insert");
3492
  // Check if last inserts need to be flushed
3493
  if (m_bulk_insert_not_flushed)
3494
  {
3495
    NdbTransaction *trans= m_active_trans;
3496 3497 3498
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
                        "rows_inserted:%d, bulk_insert_rows: %d", 
3499
                        (int) m_rows_inserted, (int) m_bulk_insert_rows)); 
3500
    m_bulk_insert_not_flushed= FALSE;
3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520
    if (m_transaction_on)
    {
      if (execute_no_commit(this, trans) != 0)
      {
        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);
      }
      else
      {
        int res= trans->restart();
        DBUG_ASSERT(res == 0);
      }
3521
    }
3522 3523
  }

3524 3525
  m_rows_inserted= (ha_rows) 0;
  m_rows_to_insert= (ha_rows) 1;
3526
  DBUG_RETURN(error);
3527 3528
}

unknown's avatar
unknown committed
3529 3530 3531 3532

int ha_ndbcluster::extra_opt(enum ha_extra_function operation, ulong cache_size)
{
  DBUG_ENTER("extra_opt");
unknown's avatar
unknown committed
3533
  DBUG_PRINT("enter", ("cache_size: %lu", cache_size));
unknown's avatar
unknown committed
3534 3535 3536
  DBUG_RETURN(extra(operation));
}

unknown's avatar
unknown committed
3537 3538 3539 3540
static const char *ha_ndbcluster_exts[] = {
 ha_ndb_ext,
 NullS
};
unknown's avatar
unknown committed
3541

3542
const char** ha_ndbcluster::bas_ext() const
unknown's avatar
unknown committed
3543 3544 3545
{
  return ha_ndbcluster_exts;
}
unknown's avatar
unknown committed
3546 3547 3548 3549 3550 3551 3552 3553 3554

/*
  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
3555 3556 3557
  DBUG_ENTER("ha_ndbcluster::scan_time()");
  double res= rows2double(records*1000);
  DBUG_PRINT("exit", ("table: %s value: %f", 
3558
                      m_tabname, res));
unknown's avatar
unknown committed
3559
  DBUG_RETURN(res);
unknown's avatar
unknown committed
3560 3561
}

unknown's avatar
unknown committed
3562 3563 3564 3565 3566 3567 3568
/*
  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
3569 3570 3571 3572 3573 3574 3575 3576

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
3577

unknown's avatar
unknown committed
3578 3579 3580
    /* If we are not doing a LOCK TABLE, then allow multiple
       writers */
    
3581 3582 3583
    /* Since NDB does not currently have table locks
       this is treated as a ordinary lock */

3584
    if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
unknown's avatar
unknown committed
3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599
         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;
3600 3601

  DBUG_PRINT("exit", ("lock_type: %d", lock_type));
unknown's avatar
unknown committed
3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623
  
  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
3624
  for the statement, this will be stored in thd_ndb.stmt.
unknown's avatar
unknown committed
3625
  If not, we have to start a master transaction if there doesn't exist
3626
  one from before, this will be stored in thd_ndb.all
unknown's avatar
unknown committed
3627 3628 3629
 
  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  
3630
  If we are locking the table then:
3631
  - save the NdbDictionary::Table for easy access
3632 3633
  - save reference to table statistics
  - refresh list of the indexes for the table if needed (if altered)
unknown's avatar
unknown committed
3634 3635 3636 3637 3638
 */

int ha_ndbcluster::external_lock(THD *thd, int lock_type)
{
  int error=0;
3639
  NdbTransaction* trans= NULL;
unknown's avatar
unknown committed
3640 3641 3642 3643 3644 3645

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

3649
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
3650
  Ndb *ndb= thd_ndb->ndb;
3651

unknown's avatar
unknown committed
3652 3653 3654
  DBUG_PRINT("enter", ("this: %x  thd: %lx  thd_ndb: %lx  "
                       "thd_ndb->lock_count: %d",
                       this, thd, thd_ndb, thd_ndb->lock_count));
3655

unknown's avatar
unknown committed
3656 3657
  if (lock_type != F_UNLCK)
  {
3658
    DBUG_PRINT("info", ("lock_type != F_UNLCK"));
3659 3660 3661 3662
    if (!thd->transaction.on)
      m_transaction_on= FALSE;
    else
      m_transaction_on= thd->variables.ndb_use_transactions;
3663
    if (!thd_ndb->lock_count++)
unknown's avatar
unknown committed
3664 3665
    {
      PRINT_OPTION_FLAGS(thd);
3666
      if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) 
unknown's avatar
unknown committed
3667 3668
      {
        // Autocommit transaction
3669
        DBUG_ASSERT(!thd_ndb->stmt);
unknown's avatar
unknown committed
3670 3671
        DBUG_PRINT("trans",("Starting transaction stmt"));      

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

          /*
            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))
3701
          {
unknown's avatar
unknown committed
3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720
            //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. 
     */

3721 3722 3723
    // 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;
3724 3725
    m_autoincrement_prefetch= 
      (ha_rows) thd->variables.ndb_autoincrement_prefetch_sz;
3726

3727
    m_active_trans= thd_ndb->all ? thd_ndb->all : thd_ndb->stmt;
unknown's avatar
unknown committed
3728
    DBUG_ASSERT(m_active_trans);
3729
    // Start of transaction
3730 3731
    m_rows_changed= 0;
    m_ops_pending= 0;
3732
    {
3733
      NDBDICT *dict= ndb->getDictionary();
3734 3735 3736
      const NDBTAB *tab;
      void *tab_info;
      if (!(tab= dict->getTable(m_tabname, &tab_info)))
3737
        ERR_RETURN(dict->getNdbError());
3738 3739 3740
      DBUG_PRINT("info", ("Table schema version: %d", 
                          tab->getObjectVersion()));
      // Check if thread has stale local cache
3741 3742 3743 3744
      // New transaction must not use old tables... (trans != 0)
      // Running might...
      if ((trans && tab->getObjectStatus() != NdbDictionary::Object::Retrieved)
	  || tab->getObjectStatus() == NdbDictionary::Object::Invalid)
3745 3746
      {
        invalidate_dictionary_cache(FALSE);
3747
        if (!(tab= dict->getTable(m_tabname, &tab_info)))
3748 3749 3750 3751
          ERR_RETURN(dict->getNdbError());
        DBUG_PRINT("info", ("Table schema version: %d", 
                            tab->getObjectVersion()));
      }
3752
      if (m_table_version < tab->getObjectVersion())
3753 3754 3755 3756 3757 3758 3759
      {
        /*
          The table has been altered, caller has to retry
        */
        NdbError err= ndb->getNdbError(NDB_INVALID_SCHEMA_OBJECT);
        DBUG_RETURN(ndb_to_mysql_error(&err));
      }
3760 3761 3762 3763
      if (m_table != (void *)tab)
      {
        m_table= (void *)tab;
        m_table_version = tab->getObjectVersion();
3764
        if (!(my_errno= open_indexes(ndb, table, FALSE)))
3765
          DBUG_RETURN(my_errno);
3766
      }
3767 3768
      m_table_info= tab_info;
    }
3769
    no_uncommitted_rows_init(thd);
3770 3771
  }
  else
unknown's avatar
unknown committed
3772
  {
3773
    DBUG_PRINT("info", ("lock_type == F_UNLCK"));
3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791

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

3792
    if (!--thd_ndb->lock_count)
unknown's avatar
unknown committed
3793 3794 3795 3796
    {
      DBUG_PRINT("trans", ("Last external_lock"));
      PRINT_OPTION_FLAGS(thd);

3797
      if (thd_ndb->stmt)
unknown's avatar
unknown committed
3798 3799 3800 3801 3802 3803 3804
      {
        /*
          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"));
3805
        ndb->closeTransaction(m_active_trans);
3806
        thd_ndb->stmt= NULL;
unknown's avatar
unknown committed
3807 3808
      }
    }
unknown's avatar
unknown committed
3809
    m_table_info= NULL;
3810

3811 3812 3813 3814 3815 3816 3817 3818 3819
    /*
      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;    

3820 3821
    if (m_active_cursor)
      DBUG_PRINT("warning", ("m_active_cursor != NULL"));
3822 3823
    m_active_cursor= NULL;

unknown's avatar
unknown committed
3824 3825 3826 3827
    if (m_multi_cursor)
      DBUG_PRINT("warning", ("m_multi_cursor != NULL"));
    m_multi_cursor= NULL;
    
3828
    if (m_blobs_pending)
3829
      DBUG_PRINT("warning", ("blobs_pending != 0"));
3830
    m_blobs_pending= 0;
3831
    
3832
    if (m_ops_pending)
3833
      DBUG_PRINT("warning", ("ops_pending != 0L"));
3834
    m_ops_pending= 0;
unknown's avatar
unknown committed
3835 3836 3837 3838 3839
  }
  DBUG_RETURN(error);
}

/*
3840 3841 3842 3843 3844
  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
3845 3846
*/

unknown's avatar
unknown committed
3847
int ha_ndbcluster::start_stmt(THD *thd, thr_lock_type lock_type)
unknown's avatar
unknown committed
3848 3849 3850 3851 3852
{
  int error=0;
  DBUG_ENTER("start_stmt");
  PRINT_OPTION_FLAGS(thd);

3853
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
3854
  NdbTransaction *trans= (thd_ndb->stmt)?thd_ndb->stmt:thd_ndb->all;
unknown's avatar
unknown committed
3855
  if (!trans){
3856
    Ndb *ndb= thd_ndb->ndb;
unknown's avatar
unknown committed
3857
    DBUG_PRINT("trans",("Starting transaction stmt"));  
3858
    trans= ndb->startTransaction();
unknown's avatar
unknown committed
3859
    if (trans == NULL)
3860
      ERR_RETURN(ndb->getNdbError());
3861
    no_uncommitted_rows_reset(thd);
3862 3863
    thd_ndb->stmt= trans;
    trans_register_ha(thd, FALSE, &ndbcluster_hton);
unknown's avatar
unknown committed
3864 3865
  }
  m_active_trans= trans;
3866

3867
  // Start of statement
3868
  m_ops_pending= 0;    
unknown's avatar
unknown committed
3869 3870 3871 3872 3873 3874
  
  DBUG_RETURN(error);
}


/*
3875
  Commit a transaction started in NDB
unknown's avatar
unknown committed
3876 3877
 */

unknown's avatar
unknown committed
3878
static int ndbcluster_commit(THD *thd, bool all)
unknown's avatar
unknown committed
3879 3880
{
  int res= 0;
3881 3882 3883
  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
3884 3885 3886

  DBUG_ENTER("ndbcluster_commit");
  DBUG_PRINT("transaction",("%s",
3887
                            trans == thd_ndb->stmt ?
unknown's avatar
unknown committed
3888 3889 3890
                            "stmt" : "all"));
  DBUG_ASSERT(ndb && trans);

3891
  if (execute_commit(thd,trans) != 0)
unknown's avatar
unknown committed
3892 3893
  {
    const NdbError err= trans->getNdbError();
3894
    const NdbOperation *error_op= trans->getNdbErrorOperation();
3895
    ERR_PRINT(err);
unknown's avatar
unknown committed
3896
    res= ndb_to_mysql_error(&err);
3897
    if (res != -1)
3898
      ndbcluster_print_error(res, error_op);
unknown's avatar
unknown committed
3899
  }
3900
  ndb->closeTransaction(trans);
3901

unknown's avatar
unknown committed
3902
  if (all)
3903 3904 3905
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;
3906 3907 3908 3909 3910 3911 3912

  /* 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);
unknown's avatar
unknown committed
3913 3914
    DBUG_PRINT("info", ("Invalidate commit_count for %s, share->commit_count: %d ",
			share->key, share->commit_count));
3915 3916 3917 3918 3919 3920
    share->commit_count= 0;
    share->commit_count_lock++;
    pthread_mutex_unlock(&share->mutex);
  }
  thd_ndb->changed_tables.empty();

unknown's avatar
unknown committed
3921 3922 3923 3924 3925 3926 3927 3928
  DBUG_RETURN(res);
}


/*
  Rollback a transaction started in NDB
 */

unknown's avatar
unknown committed
3929
static int ndbcluster_rollback(THD *thd, bool all)
unknown's avatar
unknown committed
3930 3931
{
  int res= 0;
3932 3933 3934
  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
3935 3936 3937

  DBUG_ENTER("ndbcluster_rollback");
  DBUG_PRINT("transaction",("%s",
3938
                            trans == thd_ndb->stmt ? 
unknown's avatar
unknown committed
3939 3940 3941
                            "stmt" : "all"));
  DBUG_ASSERT(ndb && trans);

3942
  if (trans->execute(NdbTransaction::Rollback) != 0)
unknown's avatar
unknown committed
3943 3944
  {
    const NdbError err= trans->getNdbError();
3945
    const NdbOperation *error_op= trans->getNdbErrorOperation();
unknown's avatar
unknown committed
3946 3947
    ERR_PRINT(err);     
    res= ndb_to_mysql_error(&err);
3948 3949
    if (res != -1) 
      ndbcluster_print_error(res, error_op);
unknown's avatar
unknown committed
3950 3951
  }
  ndb->closeTransaction(trans);
3952

unknown's avatar
unknown committed
3953
  if (all)
3954 3955 3956 3957
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;

3958 3959 3960
  /* Clear list of tables changed by transaction */
  thd_ndb->changed_tables.empty();

3961
  DBUG_RETURN(res);
unknown's avatar
unknown committed
3962 3963 3964 3965
}


/*
unknown's avatar
unknown committed
3966 3967 3968
  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
3969 3970 3971

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

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

unknown's avatar
unknown committed
4243 4244 4245 4246
/*
  Create a table in NDB Cluster
*/

unknown's avatar
unknown committed
4247
int ha_ndbcluster::create(const char *name, 
4248 4249
                          TABLE *form, 
                          HA_CREATE_INFO *info)
unknown's avatar
unknown committed
4250 4251 4252
{
  NDBTAB tab;
  NDBCOL col;
unknown's avatar
unknown committed
4253
  uint pack_length, length, i, pk_length= 0;
unknown's avatar
unknown committed
4254
  const void *data, *pack_data;
4255
  bool create_from_engine= (info->table_options & HA_OPTION_CREATE_FROM_ENGINE);
4256

unknown's avatar
unknown committed
4257
  DBUG_ENTER("ha_ndbcluster::create");
unknown's avatar
unknown committed
4258
  DBUG_PRINT("enter", ("name: %s", name));
unknown's avatar
unknown committed
4259

4260 4261 4262
  DBUG_ASSERT(*fn_rext((char*)name) == 0);
  set_dbname(name);
  set_tabname(name);
4263

unknown's avatar
unknown committed
4264
  table= form;
4265 4266 4267
  if (create_from_engine)
  {
    /*
unknown's avatar
unknown committed
4268
      Table already exists in NDB and frm file has been created by 
4269 4270 4271
      caller.
      Do Ndb specific stuff, such as create a .ndb file
    */
unknown's avatar
unknown committed
4272
    if ((my_errno= write_ndb_file(name)))
unknown's avatar
unknown committed
4273
      DBUG_RETURN(my_errno);
unknown's avatar
unknown committed
4274
#ifdef HAVE_NDB_BINLOG
4275
    ndbcluster_create_binlog_setup(get_ndb(), name, strlen(name),
4276
                                   m_dbname, m_tabname, FALSE);
unknown's avatar
unknown committed
4277
#endif /* HAVE_NDB_BINLOG */
4278 4279
    DBUG_RETURN(my_errno);
  }
unknown's avatar
unknown committed
4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290

  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);
  
unknown's avatar
unknown committed
4291
  DBUG_PRINT("info", ("setFrm data=%lx  len=%d", pack_data, pack_length));
unknown's avatar
unknown committed
4292 4293 4294 4295
  tab.setFrm(pack_data, pack_length);      
  my_free((char*)data, MYF(0));
  my_free((char*)pack_data, MYF(0));
  
4296
  for (i= 0; i < form->s->fields; i++) 
unknown's avatar
unknown committed
4297 4298 4299 4300
  {
    Field *field= form->field[i];
    DBUG_PRINT("info", ("name: %s, type: %u, pack_length: %d", 
                        field->field_name, field->real_type(),
4301
                        field->pack_length()));
4302
    if ((my_errno= create_ndb_column(col, field, info)))
unknown's avatar
unknown committed
4303
      DBUG_RETURN(my_errno);
unknown's avatar
unknown committed
4304 4305
 
    if (info->store_on_disk || getenv("NDB_DEFAULT_DISK"))
unknown's avatar
unknown committed
4306 4307 4308 4309
      col.setStorageType(NdbDictionary::Column::StorageTypeDisk);
    else
      col.setStorageType(NdbDictionary::Column::StorageTypeMemory);

unknown's avatar
unknown committed
4310
    tab.addColumn(col);
unknown's avatar
unknown committed
4311
    if (col.getPrimaryKey())
unknown's avatar
unknown committed
4312
      pk_length += (field->pack_length() + 3) / 4;
unknown's avatar
unknown committed
4313
  }
unknown's avatar
unknown committed
4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329

  KEY* key_info;
  for (i= 0, key_info= form->key_info; i < form->s->keys; i++, key_info++)
  {
    KEY_PART_INFO *key_part= key_info->key_part;
    KEY_PART_INFO *end= key_part + key_info->key_parts;
    for (; key_part != end; key_part++)
      tab.getColumn(key_part->fieldnr-1)->setStorageType(
                             NdbDictionary::Column::StorageTypeMemory);
  }

  if (info->store_on_disk)
    if (info->tablespace)
      tab.setTablespace(info->tablespace);
    else
      tab.setTablespace("DEFAULT-TS");
unknown's avatar
unknown committed
4330
  // No primary key, create shadow key as 64 bit, auto increment  
4331
  if (form->s->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
4332 4333 4334 4335 4336
  {
    DBUG_PRINT("info", ("Generating shadow key"));
    col.setName("$PK");
    col.setType(NdbDictionary::Column::Bigunsigned);
    col.setLength(1);
unknown's avatar
unknown committed
4337
    col.setNullable(FALSE);
unknown's avatar
unknown committed
4338 4339 4340
    col.setPrimaryKey(TRUE);
    col.setAutoIncrement(TRUE);
    tab.addColumn(col);
unknown's avatar
unknown committed
4341 4342
    pk_length += 2;
  }
4343
 
unknown's avatar
unknown committed
4344
  // Make sure that blob tables don't have to big part size
4345
  for (i= 0; i < form->s->fields; i++) 
unknown's avatar
unknown committed
4346 4347 4348 4349 4350 4351 4352
  {
    /**
     * 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()) {
4353
    case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
4354 4355 4356 4357
    case MYSQL_TYPE_BLOB:    
    case MYSQL_TYPE_MEDIUM_BLOB:   
    case MYSQL_TYPE_LONG_BLOB: 
    {
4358 4359
      NdbDictionary::Column * col= tab.getColumn(i);
      int size= pk_length + (col->getPartSize()+3)/4 + 7;
unknown's avatar
unknown committed
4360
      if (size > NDB_MAX_TUPLE_SIZE_IN_WORDS && 
4361
         (pk_length+7) < NDB_MAX_TUPLE_SIZE_IN_WORDS)
unknown's avatar
unknown committed
4362
      {
4363 4364
        size= NDB_MAX_TUPLE_SIZE_IN_WORDS - pk_length - 7;
        col->setPartSize(4*size);
unknown's avatar
unknown committed
4365 4366 4367 4368 4369 4370 4371 4372 4373 4374
      }
      /**
       * 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
4375
  }
unknown's avatar
Merge  
unknown committed
4376

4377
  // Check partition info
unknown's avatar
unknown committed
4378
  partition_info *part_info= form->part_info;
unknown's avatar
unknown committed
4379
  if ((my_errno= set_up_partition_info(part_info, form, (void*)&tab)))
4380
  {
unknown's avatar
unknown committed
4381
    DBUG_RETURN(my_errno);
4382 4383
  }

4384
  if ((my_errno= check_ndb_connection()))
unknown's avatar
unknown committed
4385 4386 4387
    DBUG_RETURN(my_errno);
  
  // Create the table in NDB     
4388 4389
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
4390
  if (dict->createTable(tab) != 0) 
unknown's avatar
unknown committed
4391 4392 4393 4394 4395 4396 4397 4398
  {
    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));
4399

unknown's avatar
unknown committed
4400
  // Create secondary indexes
4401
  my_errno= create_indexes(ndb, form);
4402

4403
  if (!my_errno)
unknown's avatar
unknown committed
4404
    my_errno= write_ndb_file(name);
4405 4406 4407 4408 4409 4410 4411 4412
  else
  {
    /*
      Failed to create an index,
      drop the table (and all it's indexes)
    */
    drop_ndb_table();
  }
4413

unknown's avatar
unknown committed
4414 4415 4416 4417 4418 4419 4420 4421 4422
#ifdef HAVE_NDB_BINLOG
  if (!my_errno)
  {
    NDB_SHARE *share= 0;
    pthread_mutex_lock(&ndbcluster_mutex);
    /*
      First make sure we get a "fresh" share here, not an old trailing one...
    */
    {
4423
      uint length= (uint) strlen(name);
unknown's avatar
unknown committed
4424
      if ((share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
4425
                                           (byte*) name, length)))
unknown's avatar
unknown committed
4426 4427 4428 4429 4430
        handle_trailing_share(share);
    }
    /*
      get a new share
    */
4431 4432

    if (!(share= get_share(name, form, true, true)))
unknown's avatar
unknown committed
4433
    {
4434
      sql_print_error("NDB: allocating table share for %s failed", name);
unknown's avatar
unknown committed
4435 4436 4437 4438 4439 4440 4441 4442 4443
      /* my_errno is set */
    }
    pthread_mutex_unlock(&ndbcluster_mutex);

    while (!IS_TMP_PREFIX(m_tabname))
    {
      const NDBTAB *t= dict->getTable(m_tabname);
      String event_name(INJECTOR_EVENT_LEN);
      ndb_rep_event_name(&event_name,m_dbname,m_tabname);
4444 4445 4446 4447 4448 4449
      int do_event_op= ndb_binlog_running;

      if (!schema_share &&
          strcmp(share->db, NDB_REP_DB) == 0 &&
          strcmp(share->table_name, NDB_SCHEMA_TABLE) == 0)
        do_event_op= 1;
unknown's avatar
unknown committed
4450 4451 4452 4453 4454

      /*
        Always create an event for the table, as other mysql servers
        expect it to be there.
      */
unknown's avatar
unknown committed
4455 4456
      if (!ndbcluster_create_event(ndb, t, event_name.c_ptr(), share,
                                   share && do_event_op /* push warning */))
unknown's avatar
unknown committed
4457
      {
unknown's avatar
unknown committed
4458 4459 4460 4461 4462 4463 4464 4465 4466 4467
        if (ndb_extra_logging)
          sql_print_information("NDB Binlog: CREATE TABLE Event: %s",
                                event_name.c_ptr());
        if (share && do_event_op &&
            ndbcluster_create_event_ops(share, t, event_name.c_ptr()))
        {
          sql_print_error("NDB Binlog: FAILED CREATE TABLE event operations."
                          " Event: %s", name);
          /* a warning has been issued to the client */
        }
unknown's avatar
unknown committed
4468
      }
unknown's avatar
unknown committed
4469 4470 4471 4472
      /*
        warning has been issued if ndbcluster_create_event failed
        and (share && do_event_op)
      */
4473
      if (share && !do_event_op)
4474
        share->flags|= NSF_NO_BINLOG;
unknown's avatar
unknown committed
4475 4476 4477 4478 4479 4480 4481 4482 4483 4484
      ndbcluster_log_schema_op(current_thd, share,
                               current_thd->query, current_thd->query_length,
                               share->db, share->table_name,
                               0, 0,
                               SOT_CREATE_TABLE);
      break;
    }
  }
#endif /* HAVE_NDB_BINLOG */

unknown's avatar
unknown committed
4485 4486 4487
  DBUG_RETURN(my_errno);
}

unknown's avatar
unknown committed
4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504
int ha_ndbcluster::create_handler_files(const char *file) 
{ 
  const char *name;
  Ndb* ndb;
  const NDBTAB *tab;
  const void *data, *pack_data;
  uint length, pack_length;
  int error= 0;

  DBUG_ENTER("create_handler_files");

  if (!(ndb= get_ndb()))
    DBUG_RETURN(HA_ERR_NO_CONNECTION);

  NDBDICT *dict= ndb->getDictionary();
  if (!(tab= dict->getTable(m_tabname)))
    DBUG_RETURN(0); // Must be a create, ignore since frm is saved in create
4505
  DBUG_ASSERT(get_ndb_share_state(m_share) == NSS_ALTERED);
unknown's avatar
unknown committed
4506 4507 4508 4509 4510 4511 4512 4513
  name= table->s->normalized_path.str;
  DBUG_PRINT("enter", ("m_tabname: %s, path: %s", m_tabname, name));
  if (readfrm(name, &data, &length) ||
      packfrm(data, length, &pack_data, &pack_length))
  {
    DBUG_PRINT("info", ("Missing frm for %s", m_tabname));
    my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
    my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR));
4514
    error= 1;
unknown's avatar
unknown committed
4515
  }
4516 4517
  else
  {
unknown's avatar
unknown committed
4518 4519 4520
    DBUG_PRINT("info", ("Table %s has changed, altering frm in ndb", 
                        m_tabname));
    error= table_changed(pack_data, pack_length);
4521 4522
    my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
    my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR));
unknown's avatar
unknown committed
4523
  }
4524
  set_ndb_share_state(m_share, NSS_INITIAL);
4525
  free_share(&m_share); // Decrease ref_count
unknown's avatar
unknown committed
4526 4527 4528 4529

  DBUG_RETURN(error);
}

4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570
int ha_ndbcluster::create_index(const char *name, KEY *key_info, 
                                NDB_INDEX_TYPE idx_type, uint idx_no)
{
  int error= 0;
  char unique_name[FN_LEN];
  static const char* unique_suffix= "$unique";
  DBUG_ENTER("ha_ndbcluster::create_ordered_index");
  DBUG_PRINT("info", ("Creating index %u: %s", idx_no, name));  

  if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX)
  {
    strxnmov(unique_name, FN_LEN, name, unique_suffix, NullS);
    DBUG_PRINT("info", ("Created unique index name \'%s\' for index %d",
                        unique_name, idx_no));
  }
    
  switch (idx_type){
  case PRIMARY_KEY_INDEX:
    // Do nothing, already created
    break;
  case PRIMARY_KEY_ORDERED_INDEX:
    error= create_ordered_index(name, key_info);
    break;
  case UNIQUE_ORDERED_INDEX:
    if (!(error= create_ordered_index(name, key_info)))
      error= create_unique_index(unique_name, key_info);
    break;
  case UNIQUE_INDEX:
    if (!(error= check_index_fields_not_null(idx_no)))
      error= create_unique_index(unique_name, key_info);
    break;
  case ORDERED_INDEX:
    error= create_ordered_index(name, key_info);
    break;
  default:
    DBUG_ASSERT(FALSE);
    break;
  }
  
  DBUG_RETURN(error);
}
unknown's avatar
unknown committed
4571

4572
int ha_ndbcluster::create_ordered_index(const char *name, 
4573
                                        KEY *key_info)
4574
{
4575
  DBUG_ENTER("ha_ndbcluster::create_ordered_index");
4576
  DBUG_RETURN(create_ndb_index(name, key_info, FALSE));
4577 4578 4579
}

int ha_ndbcluster::create_unique_index(const char *name, 
4580
                                       KEY *key_info)
4581 4582
{

4583
  DBUG_ENTER("ha_ndbcluster::create_unique_index");
4584
  DBUG_RETURN(create_ndb_index(name, key_info, TRUE));
4585 4586 4587
}


unknown's avatar
unknown committed
4588 4589 4590 4591
/*
  Create an index in NDB Cluster
 */

4592 4593 4594
int ha_ndbcluster::create_ndb_index(const char *name, 
                                     KEY *key_info,
                                     bool unique)
4595
{
4596 4597
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
unknown's avatar
unknown committed
4598 4599 4600
  KEY_PART_INFO *key_part= key_info->key_part;
  KEY_PART_INFO *end= key_part + key_info->key_parts;
  
4601
  DBUG_ENTER("ha_ndbcluster::create_index");
unknown's avatar
unknown committed
4602
  DBUG_PRINT("enter", ("name: %s ", name));
4603

unknown's avatar
unknown committed
4604
  NdbDictionary::Index ndb_index(name);
4605
  if (unique)
unknown's avatar
unknown committed
4606 4607 4608 4609 4610
    ndb_index.setType(NdbDictionary::Index::UniqueHashIndex);
  else 
  {
    ndb_index.setType(NdbDictionary::Index::OrderedIndex);
    // TODO Only temporary ordered indexes supported
unknown's avatar
unknown committed
4611
    ndb_index.setLogging(FALSE); 
unknown's avatar
unknown committed
4612 4613 4614 4615 4616 4617 4618
  }
  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
4619
    ndb_index.addColumnName(field->field_name);
unknown's avatar
unknown committed
4620 4621 4622 4623 4624 4625 4626 4627 4628 4629
  }
  
  if (dict->createIndex(ndb_index))
    ERR_RETURN(dict->getNdbError());

  // Success
  DBUG_PRINT("info", ("Created index %s", name));
  DBUG_RETURN(0);  
}

4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641
/*
  Add an index on-line to a table
*/
int ha_ndbcluster::add_index(TABLE *table_arg, 
                             KEY *key_info, uint num_of_keys)
{
  DBUG_ENTER("ha_ndbcluster::add_index");
  DBUG_PRINT("info", ("ha_ndbcluster::add_index to table %s", 
                      table_arg->s->table_name));
  int error= 0;
  uint idx;

4642
  DBUG_ASSERT(m_share->state == NSS_INITIAL);
4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657
  for (idx= 0; idx < num_of_keys; idx++)
  {
    KEY *key= key_info + idx;
    KEY_PART_INFO *key_part= key->key_part;
    KEY_PART_INFO *end= key_part + key->key_parts;
    NDB_INDEX_TYPE idx_type= get_index_type_from_key(idx, key);
    DBUG_PRINT("info", ("Adding index: '%s'", key_info[idx].name));
    // Add fields to key_part struct
    for (; key_part != end; key_part++)
      key_part->field= table->field[key_part->fieldnr];
    // Check index type
    // Create index in ndb
    if((error= create_index(key_info[idx].name, key, idx_type, idx)))
      break;
  }
4658 4659 4660
  if (!error)
  {
    ndbcluster_get_share(m_share); // Increase ref_count
4661
    set_ndb_share_state(m_share, NSS_ALTERED);
4662
  }
4663 4664 4665 4666 4667 4668
  DBUG_RETURN(error);  
}

/*
  Drop an index in ndb
 */
4669 4670 4671 4672 4673 4674 4675 4676
int ha_ndbcluster::drop_ndb_index(const char *name)
{
  DBUG_ENTER("ha_ndbcluster::drop_index");
  DBUG_PRINT("enter", ("name: %s ", name));
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
  DBUG_RETURN(dict->dropIndex(name, m_tabname));
} 
unknown's avatar
unknown committed
4677

4678 4679 4680 4681 4682 4683 4684 4685
/*
  Mark one or several indexes for deletion. and
  renumber the remaining indexes
*/
int ha_ndbcluster::prepare_drop_index(TABLE *table_arg, 
                                      uint *key_num, uint num_of_keys)
{
  DBUG_ENTER("ha_ndbcluster::prepare_drop_index");
4686
  DBUG_ASSERT(m_share->state == NSS_INITIAL);
4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697
  // Mark indexes for deletion
  uint idx;
  for (idx= 0; idx < num_of_keys; idx++)
  {
    DBUG_PRINT("info", ("ha_ndbcluster::prepare_drop_index %u", *key_num));
    m_index[*key_num++].status= TO_BE_DROPPED;
  }
  // Renumber indexes
  THD *thd= current_thd;
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  Ndb *ndb= thd_ndb->ndb;
4698 4699
  renumber_indexes(ndb, table_arg);
  ndbcluster_get_share(m_share); // Increase ref_count
4700
  set_ndb_share_state(m_share, NSS_ALTERED);
4701
  DBUG_RETURN(0);
4702 4703 4704 4705 4706 4707 4708
}
 
/*
  Really drop all indexes marked for deletion
*/
int ha_ndbcluster::final_drop_index(TABLE *table_arg)
{
4709
  int error;
4710 4711 4712 4713 4714 4715
  DBUG_ENTER("ha_ndbcluster::final_drop_index");
  DBUG_PRINT("info", ("ha_ndbcluster::final_drop_index"));
  // Really drop indexes
  THD *thd= current_thd;
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  Ndb *ndb= thd_ndb->ndb;
4716 4717 4718 4719 4720 4721
  if((error= drop_indexes(ndb, table_arg)))
  {
    m_share->state= NSS_INITIAL;
    free_share(&m_share); // Decrease ref_count
  }
  DBUG_RETURN(error);
4722 4723
}

unknown's avatar
unknown committed
4724 4725 4726 4727 4728 4729
/*
  Rename a table in NDB Cluster
*/

int ha_ndbcluster::rename_table(const char *from, const char *to)
{
4730
  NDBDICT *dict;
unknown's avatar
unknown committed
4731
  char new_tabname[FN_HEADLEN];
4732 4733
  const NDBTAB *orig_tab;
  int result;
unknown's avatar
unknown committed
4734 4735

  DBUG_ENTER("ha_ndbcluster::rename_table");
4736
  DBUG_PRINT("info", ("Renaming %s to %s", from, to));
unknown's avatar
unknown committed
4737 4738 4739 4740
  set_dbname(from);
  set_tabname(from);
  set_tabname(to, new_tabname);

4741 4742 4743
  if (check_ndb_connection())
    DBUG_RETURN(my_errno= HA_ERR_NO_CONNECTION);

unknown's avatar
unknown committed
4744 4745
  Ndb *ndb= get_ndb();
  dict= ndb->getDictionary();
4746 4747
  if (!(orig_tab= dict->getTable(m_tabname)))
    ERR_RETURN(dict->getNdbError());
4748 4749 4750 4751 4752 4753 4754
  // 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());
  }
unknown's avatar
unknown committed
4755
#ifdef HAVE_NDB_BINLOG
4756 4757
  NDB_SHARE *share= get_share(from, 0, false);
  if (share)
unknown's avatar
unknown committed
4758 4759 4760 4761 4762
  {
    int r= rename_share(share, to);
    DBUG_ASSERT(r == 0);
  }
#endif
4763 4764 4765
  m_table= (void *)orig_tab;
  // Change current database to that of target table
  set_dbname(to);
unknown's avatar
unknown committed
4766
  ndb->setDatabaseName(m_dbname);
unknown's avatar
unknown committed
4767 4768 4769

  if ((result= alter_table_name(new_tabname)))
  {
unknown's avatar
unknown committed
4770 4771 4772 4773 4774 4775 4776 4777
#ifdef HAVE_NDB_BINLOG
    if (share)
    {
      int r= rename_share(share, from);
      DBUG_ASSERT(r == 0);
      free_share(&share);
    }
#endif
unknown's avatar
unknown committed
4778 4779 4780 4781 4782
    DBUG_RETURN(result);
  }
  
  // Rename .ndb file
  if ((result= handler::rename_table(from, to)))
4783
  {
unknown's avatar
unknown committed
4784
    // ToDo in 4.1 should rollback alter table...
unknown's avatar
unknown committed
4785 4786 4787 4788
#ifdef HAVE_NDB_BINLOG
    if (share)
      free_share(&share);
#endif
unknown's avatar
unknown committed
4789
    DBUG_RETURN(result);
4790
  }
4791

unknown's avatar
unknown committed
4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812
#ifdef HAVE_NDB_BINLOG
  int is_old_table_tmpfile= 1;
  if (share && share->op)
    dict->forceGCPWait();

  /* handle old table */
  if (!IS_TMP_PREFIX(m_tabname))
  {
    is_old_table_tmpfile= 0;
    String event_name(INJECTOR_EVENT_LEN);
    ndb_rep_event_name(&event_name, from + sizeof(share_prefix) - 1, 0);
    ndbcluster_handle_drop_table(ndb, event_name.c_ptr(), share);
  }

  if (!result && !IS_TMP_PREFIX(new_tabname))
  {
    /* always create an event for the table */
    String event_name(INJECTOR_EVENT_LEN);
    ndb_rep_event_name(&event_name, to + sizeof(share_prefix) - 1, 0);
    const NDBTAB *ndbtab= dict->getTable(new_tabname);

unknown's avatar
unknown committed
4813 4814
    if (!ndbcluster_create_event(ndb, ndbtab, event_name.c_ptr(), share,
                                 share && ndb_binlog_running /* push warning */))
unknown's avatar
unknown committed
4815 4816 4817 4818
    {
      if (ndb_extra_logging)
        sql_print_information("NDB Binlog: RENAME Event: %s",
                              event_name.c_ptr());
unknown's avatar
unknown committed
4819 4820
      if (share && ndb_binlog_running &&
          ndbcluster_create_event_ops(share, ndbtab, event_name.c_ptr()))
unknown's avatar
unknown committed
4821
      {
unknown's avatar
unknown committed
4822 4823 4824
        sql_print_error("NDB Binlog: FAILED create event operations "
                        "during RENAME. Event %s", event_name.c_ptr());
        /* a warning has been issued to the client */
unknown's avatar
unknown committed
4825 4826
      }
    }
unknown's avatar
unknown committed
4827 4828 4829 4830
    /*
      warning has been issued if ndbcluster_create_event failed
      and (share && ndb_binlog_running)
    */
4831
    if (!is_old_table_tmpfile)
unknown's avatar
unknown committed
4832 4833 4834 4835 4836 4837 4838 4839 4840 4841
      ndbcluster_log_schema_op(current_thd, share,
                               current_thd->query, current_thd->query_length,
                               m_dbname, new_tabname,
                               0, 0,
                               SOT_RENAME_TABLE);
  }
  if (share)
    free_share(&share);
#endif

unknown's avatar
unknown committed
4842 4843 4844 4845 4846 4847 4848 4849
  DBUG_RETURN(result);
}


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

4850
int ha_ndbcluster::alter_table_name(const char *to)
unknown's avatar
unknown committed
4851
{
4852 4853
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
4854
  const NDBTAB *orig_tab= (const NDBTAB *) m_table;
unknown's avatar
unknown committed
4855
  DBUG_ENTER("alter_table_name");
4856
  DBUG_PRINT("info", ("from: %s to: %s", orig_tab->getName(), to));
unknown's avatar
unknown committed
4857

unknown's avatar
unknown committed
4858
  NdbDictionary::Table new_tab= *orig_tab;
4859 4860
  new_tab.setName(to);
  if (dict->alterTable(new_tab) != 0)
unknown's avatar
unknown committed
4861 4862 4863
    ERR_RETURN(dict->getNdbError());

  m_table= NULL;
unknown's avatar
unknown committed
4864
  m_table_info= NULL;
unknown's avatar
unknown committed
4865 4866 4867 4868 4869 4870
                                                                             
  DBUG_RETURN(0);
}


/*
4871 4872
  Delete table from NDB Cluster

unknown's avatar
unknown committed
4873 4874
 */

unknown's avatar
unknown committed
4875 4876 4877 4878 4879 4880 4881 4882 4883 4884
/* static version which does not need a handler */

int
ha_ndbcluster::delete_table(ha_ndbcluster *h, Ndb *ndb,
                            const char *path,
                            const char *db,
                            const char *table_name)
{
  DBUG_ENTER("ha_ndbcluster::ndbcluster_delete_table");
  NDBDICT *dict= ndb->getDictionary();
unknown's avatar
unknown committed
4885 4886 4887
#ifdef HAVE_NDB_BINLOG
  NDB_SHARE *share= get_share(path, 0, false);
#endif
unknown's avatar
unknown committed
4888 4889 4890 4891 4892 4893

  /* Drop the table from NDB */
  
  int res;
  if (h)
  {
4894
    res= h->drop_ndb_table();
unknown's avatar
unknown committed
4895 4896 4897 4898 4899 4900 4901 4902 4903
  }
  else
  {
    ndb->setDatabaseName(db);
    res= dict->dropTable(table_name);
  }

  if (res)
  {
unknown's avatar
unknown committed
4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921
#ifdef HAVE_NDB_BINLOG
    /* the drop table failed for some reason, drop the share anyways */
    if (share)
    {
      pthread_mutex_lock(&ndbcluster_mutex);
      if (share->state != NSS_DROPPED)
      {
        /*
          The share kept by the server has not been freed, free it
        */
        share->state= NSS_DROPPED;
        free_share(&share, TRUE);
      }
      /* free the share taken above */
      free_share(&share, TRUE);
      pthread_mutex_unlock(&ndbcluster_mutex);
    }
#endif
unknown's avatar
unknown committed
4922 4923 4924
    DBUG_RETURN(res);
  }

unknown's avatar
unknown committed
4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972
#ifdef HAVE_NDB_BINLOG
  /* stop the logging of the dropped table, and cleanup */

  /*
    drop table is successful even if table does not exist in ndb
    and in case table was actually not dropped, there is no need
    to force a gcp, and setting the event_name to null will indicate
    that there is no event to be dropped
  */
  int table_dropped= dict->getNdbError().code != 709;

  if (!IS_TMP_PREFIX(table_name) && share)
  {
    ndbcluster_log_schema_op(current_thd, share,
                             current_thd->query, current_thd->query_length,
                             share->db, share->table_name,
                             0, 0,
                             SOT_DROP_TABLE);
  }
  else if (table_dropped && share && share->op) /* ndbcluster_log_schema_op
                                                   will do a force GCP */
    dict->forceGCPWait();

  if (!IS_TMP_PREFIX(table_name))
  {
    String event_name(INJECTOR_EVENT_LEN);
    ndb_rep_event_name(&event_name, path + sizeof(share_prefix) - 1, 0);
    ndbcluster_handle_drop_table(ndb,
                                 table_dropped ? event_name.c_ptr() : 0,
                                 share);
  }

  if (share)
  {
    pthread_mutex_lock(&ndbcluster_mutex);
    if (share->state != NSS_DROPPED)
    {
      /*
        The share kept by the server has not been freed, free it
      */
      share->state= NSS_DROPPED;
      free_share(&share, TRUE);
    }
    /* free the share taken above */
    free_share(&share, TRUE);
    pthread_mutex_unlock(&ndbcluster_mutex);
  }
#endif
unknown's avatar
unknown committed
4973 4974 4975
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
4976 4977
int ha_ndbcluster::delete_table(const char *name)
{
4978
  DBUG_ENTER("ha_ndbcluster::delete_table");
unknown's avatar
unknown committed
4979 4980 4981
  DBUG_PRINT("enter", ("name: %s", name));
  set_dbname(name);
  set_tabname(name);
4982

unknown's avatar
unknown committed
4983 4984
  if (check_ndb_connection())
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
4985 4986

  /* Call ancestor function to delete .ndb file */
4987
  handler::delete_table(name);
unknown's avatar
unknown committed
4988 4989

  DBUG_RETURN(delete_table(this, get_ndb(),name, m_dbname, m_tabname));
unknown's avatar
unknown committed
4990 4991 4992 4993
}


/*
4994
  Drop table in NDB Cluster
unknown's avatar
unknown committed
4995 4996
 */

4997
int ha_ndbcluster::drop_ndb_table()
unknown's avatar
unknown committed
4998
{
4999 5000
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
5001

unknown's avatar
unknown committed
5002
  DBUG_ENTER("intern_drop_table");
unknown's avatar
unknown committed
5003 5004
  DBUG_PRINT("enter", ("Deleting %s", m_tabname));
  release_metadata();
5005 5006
  if (dict->dropTable(m_tabname))
    ERR_RETURN(dict->getNdbError());
unknown's avatar
unknown committed
5007 5008 5009 5010
  DBUG_RETURN(0);
}


5011
ulonglong ha_ndbcluster::get_auto_increment()
5012
{  
5013 5014
  int cache_size;
  Uint64 auto_value;
unknown's avatar
unknown committed
5015 5016
  DBUG_ENTER("get_auto_increment");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));
5017
  Ndb *ndb= get_ndb();
5018
   
5019
  if (m_rows_inserted > m_rows_to_insert)
unknown's avatar
unknown committed
5020
  {
5021 5022
    /* We guessed too low */
    m_rows_to_insert+= m_autoincrement_prefetch;
unknown's avatar
unknown committed
5023
  }
unknown's avatar
unknown committed
5024
  cache_size= 
unknown's avatar
unknown committed
5025 5026 5027 5028
    (int) ((m_rows_to_insert - m_rows_inserted < m_autoincrement_prefetch) ?
           m_rows_to_insert - m_rows_inserted :
           ((m_rows_to_insert > m_autoincrement_prefetch) ?
            m_rows_to_insert : m_autoincrement_prefetch));
unknown's avatar
unknown committed
5029
  auto_value= NDB_FAILED_AUTO_INCREMENT;
5030 5031 5032 5033 5034 5035 5036 5037 5038
  uint retries= NDB_AUTO_INCREMENT_RETRIES;
  do {
    auto_value=
      (m_skip_auto_increment) ? 
      ndb->readAutoIncrementValue((const NDBTAB *) m_table)
      : ndb->getAutoIncrementValue((const NDBTAB *) m_table, cache_size);
  } while (auto_value == NDB_FAILED_AUTO_INCREMENT && 
           --retries &&
           ndb->getNdbError().status == NdbError::TemporaryError);
5039
  if (auto_value == NDB_FAILED_AUTO_INCREMENT)
5040 5041 5042 5043 5044 5045
  {
    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
5046
  DBUG_RETURN((longlong)auto_value);
unknown's avatar
unknown committed
5047 5048 5049 5050 5051 5052 5053
}


/*
  Constructor for the NDB Cluster table handler 
 */

unknown's avatar
unknown committed
5054 5055 5056 5057 5058 5059 5060
#define HA_NDBCLUSTER_TABLE_FLAGS \
                HA_REC_NOT_IN_SEQ | \
                HA_NULL_IN_KEY | \
                HA_AUTO_PART_KEY | \
                HA_NO_PREFIX_CHAR_KEYS | \
                HA_NEED_READ_RANGE_BUFFER | \
                HA_CAN_GEOMETRY | \
unknown's avatar
unknown committed
5061 5062
                HA_CAN_BIT_FIELD | \
                HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
unknown's avatar
unknown committed
5063

unknown's avatar
unknown committed
5064
ha_ndbcluster::ha_ndbcluster(TABLE_SHARE *table_arg):
5065
  handler(&ndbcluster_hton, table_arg),
unknown's avatar
unknown committed
5066 5067 5068
  m_active_trans(NULL),
  m_active_cursor(NULL),
  m_table(NULL),
5069
  m_table_version(-1),
5070
  m_table_info(NULL),
unknown's avatar
unknown committed
5071
  m_table_flags(HA_NDBCLUSTER_TABLE_FLAGS),
5072
  m_share(0),
5073 5074 5075
  m_part_info(NULL),
  m_use_partition_function(FALSE),
  m_sorted(FALSE),
unknown's avatar
unknown committed
5076
  m_use_write(FALSE),
5077
  m_ignore_dup_key(FALSE),
5078
  m_primary_key_update(FALSE),
unknown's avatar
unknown committed
5079
  m_ignore_no_key(FALSE),
5080 5081 5082
  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
5083
  m_rows_changed((ha_rows) 0),
5084 5085 5086 5087 5088 5089
  m_bulk_insert_not_flushed(FALSE),
  m_ops_pending(0),
  m_skip_auto_increment(TRUE),
  m_blobs_pending(0),
  m_blobs_buffer(0),
  m_blobs_buffer_size(0),
5090 5091 5092
  m_dupkey((uint) -1),
  m_ha_not_exact_count(FALSE),
  m_force_send(TRUE),
5093
  m_autoincrement_prefetch((ha_rows) 32),
unknown's avatar
unknown committed
5094
  m_transaction_on(TRUE),
unknown's avatar
unknown committed
5095 5096
  m_cond_stack(NULL),
  m_multi_cursor(NULL)
5097
{
5098
  int i;
5099
 
unknown's avatar
unknown committed
5100 5101 5102 5103 5104
  DBUG_ENTER("ha_ndbcluster");

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

5105
  records= ~(ha_rows)0; // uninitialized
unknown's avatar
unknown committed
5106 5107
  block_size= 1024;

5108
  clear_indexes();
5109

unknown's avatar
unknown committed
5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121
  DBUG_VOID_RETURN;
}


/*
  Destructor for NDB Cluster table handler
 */

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

5122
  if (m_share)
unknown's avatar
unknown committed
5123 5124 5125
  {
    free_share(&m_share);
  }
unknown's avatar
unknown committed
5126
  release_metadata();
5127 5128
  my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
  m_blobs_buffer= 0;
unknown's avatar
unknown committed
5129 5130

  // Check for open cursor/transaction
5131 5132
  if (m_active_cursor) {
  }
unknown's avatar
unknown committed
5133
  DBUG_ASSERT(m_active_cursor == NULL);
5134 5135
  if (m_active_trans) {
  }
unknown's avatar
unknown committed
5136 5137
  DBUG_ASSERT(m_active_trans == NULL);

5138 5139 5140 5141
  // Discard the condition stack
  DBUG_PRINT("info", ("Clearing condition stack"));
  cond_clear();

unknown's avatar
unknown committed
5142 5143 5144 5145
  DBUG_VOID_RETURN;
}


unknown's avatar
Merge  
unknown committed
5146

unknown's avatar
unknown committed
5147 5148 5149 5150
/*
  Open a table for further use
  - fetch metadata for this table from NDB
  - check that table exists
unknown's avatar
unknown committed
5151 5152 5153 5154

  RETURN
    0    ok
    < 0  Table has changed
unknown's avatar
unknown committed
5155 5156 5157 5158
*/

int ha_ndbcluster::open(const char *name, int mode, uint test_if_locked)
{
unknown's avatar
unknown committed
5159
  int res;
unknown's avatar
unknown committed
5160
  KEY *key;
unknown's avatar
unknown committed
5161 5162 5163
  DBUG_ENTER("ha_ndbcluster::open");
  DBUG_PRINT("enter", ("name: %s  mode: %d  test_if_locked: %d",
                       name, mode, test_if_locked));
unknown's avatar
unknown committed
5164
  
unknown's avatar
unknown committed
5165 5166 5167 5168
  /*
    Setup ref_length to make room for the whole 
    primary key to be written in the ref variable
  */
unknown's avatar
unknown committed
5169
  
unknown's avatar
unknown committed
5170
  if (table_share->primary_key != MAX_KEY) 
unknown's avatar
unknown committed
5171
  {
unknown's avatar
unknown committed
5172
    key= table->key_info+table_share->primary_key;
unknown's avatar
unknown committed
5173 5174 5175 5176
    ref_length= key->key_length;
    DBUG_PRINT("info", (" ref_length: %d", ref_length));
  }
  // Init table lock structure 
unknown's avatar
unknown committed
5177
  if (!(m_share=get_share(name, table)))
unknown's avatar
unknown committed
5178 5179 5180 5181 5182 5183
    DBUG_RETURN(1);
  thr_lock_data_init(&m_share->lock,&m_lock,(void*) 0);
  
  set_dbname(name);
  set_tabname(name);
  
5184
  if (check_ndb_connection()) {
unknown's avatar
unknown committed
5185 5186
    free_share(&m_share);
    m_share= 0;
unknown's avatar
unknown committed
5187
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
5188
  }
5189
  
unknown's avatar
unknown committed
5190 5191 5192
  res= get_metadata(name);
  if (!res)
    info(HA_STATUS_VARIABLE | HA_STATUS_CONST);
unknown's avatar
unknown committed
5193

unknown's avatar
unknown committed
5194
  DBUG_RETURN(res);
unknown's avatar
unknown committed
5195 5196
}

unknown's avatar
unknown committed
5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210
/*
  Set partition info

  SYNOPSIS
    set_part_info()
    part_info

  RETURN VALUE
    NONE

  DESCRIPTION
    Set up partition info when handler object created
*/

5211 5212 5213 5214 5215
void ha_ndbcluster::set_part_info(partition_info *part_info)
{
  m_part_info= part_info;
  if (!(m_part_info->part_type == HASH_PARTITION &&
        m_part_info->list_of_part_fields &&
5216
        !m_part_info->is_sub_partitioned()))
5217 5218
    m_use_partition_function= TRUE;
}
unknown's avatar
unknown committed
5219 5220 5221 5222 5223 5224 5225 5226 5227

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

int ha_ndbcluster::close(void)
{
  DBUG_ENTER("close");  
unknown's avatar
unknown committed
5228 5229
  free_share(&m_share);
  m_share= 0;
unknown's avatar
unknown committed
5230 5231 5232 5233 5234
  release_metadata();
  DBUG_RETURN(0);
}


5235
Thd_ndb* ha_ndbcluster::seize_thd_ndb()
unknown's avatar
unknown committed
5236
{
5237 5238
  Thd_ndb *thd_ndb;
  DBUG_ENTER("seize_thd_ndb");
unknown's avatar
unknown committed
5239

5240
  thd_ndb= new Thd_ndb();
5241 5242 5243
  thd_ndb->ndb->getDictionary()->set_local_table_data_size(
    sizeof(Ndb_local_table_statistics)
    );
5244
  if (thd_ndb->ndb->init(max_transactions) != 0)
unknown's avatar
unknown committed
5245
  {
5246
    ERR_PRINT(thd_ndb->ndb->getNdbError());
unknown's avatar
unknown committed
5247 5248 5249 5250 5251 5252
    /*
      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 
    */
5253 5254
    delete thd_ndb;
    thd_ndb= NULL;
unknown's avatar
unknown committed
5255
  }
5256
  DBUG_RETURN(thd_ndb);
unknown's avatar
unknown committed
5257 5258 5259
}


5260
void ha_ndbcluster::release_thd_ndb(Thd_ndb* thd_ndb)
unknown's avatar
unknown committed
5261
{
5262 5263
  DBUG_ENTER("release_thd_ndb");
  delete thd_ndb;
unknown's avatar
unknown committed
5264 5265 5266 5267 5268
  DBUG_VOID_RETURN;
}


/*
unknown's avatar
unknown committed
5269
  If this thread already has a Thd_ndb object allocated
unknown's avatar
unknown committed
5270
  in current THD, reuse it. Otherwise
unknown's avatar
unknown committed
5271
  seize a Thd_ndb object, assign it to current THD and use it.
unknown's avatar
unknown committed
5272 5273 5274
 
*/

5275
Ndb* check_ndb_in_thd(THD* thd)
unknown's avatar
unknown committed
5276
{
5277
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
5278
  if (!thd_ndb)
unknown's avatar
unknown committed
5279
  {
unknown's avatar
unknown committed
5280
    if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
unknown's avatar
Merge  
unknown committed
5281
      return NULL;
5282
    set_thd_ndb(thd, thd_ndb);
unknown's avatar
unknown committed
5283
  }
unknown's avatar
Merge  
unknown committed
5284
  return thd_ndb->ndb;
5285 5286
}

unknown's avatar
unknown committed
5287

5288

5289
int ha_ndbcluster::check_ndb_connection(THD* thd)
unknown's avatar
unknown committed
5290
{
5291
  Ndb *ndb;
unknown's avatar
unknown committed
5292 5293
  DBUG_ENTER("check_ndb_connection");
  
5294
  if (!(ndb= check_ndb_in_thd(thd)))
5295
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
5296
  ndb->setDatabaseName(m_dbname);
unknown's avatar
unknown committed
5297 5298 5299
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
5300

unknown's avatar
unknown committed
5301
static int ndbcluster_close_connection(THD *thd)
unknown's avatar
unknown committed
5302
{
5303
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
unknown's avatar
unknown committed
5304
  DBUG_ENTER("ndbcluster_close_connection");
5305 5306
  if (thd_ndb)
  {
5307
    ha_ndbcluster::release_thd_ndb(thd_ndb);
5308
    set_thd_ndb(thd, NULL); // not strictly required but does not hurt either
5309
  }
5310
  DBUG_RETURN(0);
unknown's avatar
unknown committed
5311 5312 5313 5314 5315 5316 5317
}


/*
  Try to discover one table from NDB
 */

5318
int ndbcluster_discover(THD* thd, const char *db, const char *name,
5319
                        const void** frmblob, uint* frmlen)
unknown's avatar
unknown committed
5320 5321 5322 5323
{
  uint len;
  const void* data;
  const NDBTAB* tab;
5324
  Ndb* ndb;
5325
  char key[FN_REFLEN];
unknown's avatar
unknown committed
5326
  DBUG_ENTER("ndbcluster_discover");
5327
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name)); 
unknown's avatar
unknown committed
5328

5329 5330 5331 5332
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(HA_ERR_NO_CONNECTION);  
  ndb->setDatabaseName(db);
  NDBDICT* dict= ndb->getDictionary();
5333
  dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
5334
  dict->invalidateTable(name);
5335
  build_table_filename(key, sizeof(key), db, name, "");
5336
  NDB_SHARE *share= get_share(key, 0, false);
5337
  if (share && get_ndb_share_state(share) == NSS_ALTERED)
unknown's avatar
unknown committed
5338
  {
5339 5340 5341 5342 5343 5344 5345 5346
    // Frm has been altered on disk, but not yet written to ndb
    if (readfrm(key, &data, &len))
    {
      DBUG_PRINT("error", ("Could not read frm"));
      if (share)
        free_share(&share);
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
5347
  }
5348
  else
5349
  {
5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376
    if (!(tab= dict->getTable(name)))
    {    
      const NdbError err= dict->getNdbError();
      if (share)
        free_share(&share);
      if (err.code == 709 || err.code == 723)
        DBUG_RETURN(-1);
      ERR_RETURN(err);
    }
    DBUG_PRINT("info", ("Found table %s", tab->getName()));
    
    len= tab->getFrmLength();  
    if (len == 0 || tab->getFrmData() == NULL)
    {
      DBUG_PRINT("error", ("No frm data found."));
      if (share)
        free_share(&share);
      DBUG_RETURN(1);
    }
    
    if (unpackfrm(&data, &len, tab->getFrmData()))
    {
      DBUG_PRINT("error", ("Could not unpack table"));
      if (share)
        free_share(&share);
      DBUG_RETURN(1);
    }
5377
  }
unknown's avatar
unknown committed
5378 5379 5380 5381

  *frmlen= len;
  *frmblob= data;
  
5382 5383 5384
  if (share)
    free_share(&share);

unknown's avatar
unknown committed
5385 5386 5387 5388
  DBUG_RETURN(0);
}

/*
5389
  Check if a table exists in NDB
5390

5391
 */
unknown's avatar
unknown committed
5392

5393
int ndbcluster_table_exists_in_engine(THD* thd, const char *db, const char *name)
5394 5395 5396
{
  const NDBTAB* tab;
  Ndb* ndb;
5397
  DBUG_ENTER("ndbcluster_table_exists_in_engine");
5398
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
5399 5400

  if (!(ndb= check_ndb_in_thd(thd)))
5401
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
5402 5403 5404
  ndb->setDatabaseName(db);

  NDBDICT* dict= ndb->getDictionary();
5405
  dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
5406 5407
  dict->invalidateTable(name);
  if (!(tab= dict->getTable(name)))
5408
  {
5409
    const NdbError err= dict->getNdbError();
unknown's avatar
unknown committed
5410
    if (err.code == 709 || err.code == 723)
5411 5412 5413
      DBUG_RETURN(0);
    ERR_RETURN(err);
  }
5414

5415 5416 5417 5418
  DBUG_PRINT("info", ("Found table %s", tab->getName()));
  DBUG_RETURN(1);
}

unknown's avatar
unknown committed
5419 5420


unknown's avatar
unknown committed
5421
extern "C" byte* tables_get_key(const char *entry, uint *length,
5422
                                my_bool not_used __attribute__((unused)))
5423 5424 5425 5426 5427 5428
{
  *length= strlen(entry);
  return (byte*) entry;
}


5429 5430
/*
  Drop a database in NDB Cluster
5431 5432
  NOTE add a dummy void function, since stupid handlerton is returning void instead of int...
*/
5433

5434
int ndbcluster_drop_database_impl(const char *path)
5435 5436 5437 5438 5439 5440 5441 5442 5443
{
  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;
5444
  int ret= 0;
5445 5446 5447 5448
  ha_ndbcluster::set_dbname(path, (char *)&dbname);
  DBUG_PRINT("enter", ("db: %s", dbname));
  
  if (!(ndb= check_ndb_in_thd(thd)))
5449
    DBUG_RETURN(-1);
5450 5451 5452 5453 5454
  
  // List tables in NDB
  NDBDICT *dict= ndb->getDictionary();
  if (dict->listObjects(list, 
                        NdbDictionary::Object::UserTable) != 0)
5455
    DBUG_RETURN(-1);
5456 5457
  for (i= 0 ; i < list.count ; i++)
  {
unknown's avatar
unknown committed
5458 5459
    NdbDictionary::Dictionary::List::Element& elmt= list.elements[i];
    DBUG_PRINT("info", ("Found %s/%s in NDB", elmt.database, elmt.name));     
5460 5461
    
    // Add only tables that belongs to db
unknown's avatar
unknown committed
5462
    if (my_strcasecmp(system_charset_info, elmt.database, dbname))
5463
      continue;
unknown's avatar
unknown committed
5464 5465
    DBUG_PRINT("info", ("%s must be dropped", elmt.name));     
    drop_list.push_back(thd->strdup(elmt.name));
5466 5467
  }
  // Drop any tables belonging to database
unknown's avatar
unknown committed
5468
  char full_path[FN_REFLEN];
5469 5470 5471
  char *tmp= full_path +
    build_table_filename(full_path, sizeof(full_path), dbname, "", "");

5472 5473 5474
  ndb->setDatabaseName(dbname);
  List_iterator_fast<char> it(drop_list);
  while ((tabname=it++))
5475
  {
5476
    tablename_to_filename(tabname, tmp, FN_REFLEN - (tmp - full_path)-1);
unknown's avatar
unknown committed
5477
    if (ha_ndbcluster::delete_table(0, ndb, full_path, dbname, tabname))
5478 5479
    {
      const NdbError err= dict->getNdbError();
unknown's avatar
unknown committed
5480
      if (err.code != 709 && err.code != 723)
5481 5482
      {
        ERR_PRINT(err);
5483
        ret= ndb_to_mysql_error(&err);
5484
      }
5485 5486 5487
    }
  }
  DBUG_RETURN(ret);      
5488 5489
}

unknown's avatar
unknown committed
5490
static void ndbcluster_drop_database(char *path)
5491 5492
{
  ndbcluster_drop_database_impl(path);
unknown's avatar
unknown committed
5493 5494 5495 5496 5497 5498 5499
#ifdef HAVE_NDB_BINLOG
  char db[FN_REFLEN];
  ha_ndbcluster::set_dbname(path, db);
  ndbcluster_log_schema_op(current_thd, 0,
                           current_thd->query, current_thd->query_length,
                           db, "", 0, 0, SOT_DROP_DB);
#endif
5500
}
unknown's avatar
unknown committed
5501 5502 5503
/*
  find all tables in ndb and discover those needed
*/
5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515
int ndb_create_table_from_engine(THD *thd, const char *db,
                                 const char *table_name)
{
  LEX *old_lex= thd->lex, newlex;
  thd->lex= &newlex;
  newlex.current_select= NULL;
  lex_start(thd, (const uchar*) "", 0);
  int res= ha_create_table_from_engine(thd, db, table_name);
  thd->lex= old_lex;
  return res;
}

unknown's avatar
unknown committed
5516
int ndbcluster_find_all_files(THD *thd)
unknown's avatar
unknown committed
5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536
{
  DBUG_ENTER("ndbcluster_find_all_files");
  Ndb* ndb;
  char key[FN_REFLEN];
  NdbDictionary::Dictionary::List list;

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

  NDBDICT *dict= ndb->getDictionary();

  int unhandled, retries= 5;
  do
  {
    if (dict->listObjects(list, NdbDictionary::Object::UserTable) != 0)
      ERR_RETURN(dict->getNdbError());
    unhandled= 0;
    for (uint i= 0 ; i < list.count ; i++)
    {
      NDBDICT::List::Element& elmt= list.elements[i];
5537
      int do_handle_table= 0;
unknown's avatar
unknown committed
5538
      if (IS_TMP_PREFIX(elmt.name) || IS_NDB_BLOB_PREFIX(elmt.name))
unknown's avatar
unknown committed
5539 5540 5541 5542
      {
        DBUG_PRINT("info", ("Skipping %s.%s in NDB", elmt.database, elmt.name));
        continue;
      }
unknown's avatar
unknown committed
5543
      DBUG_PRINT("info", ("Found %s.%s in NDB", elmt.database, elmt.name));
5544 5545 5546 5547
      if (elmt.state == NDBOBJ::StateOnline ||
          elmt.state == NDBOBJ::StateBackup)
        do_handle_table= 1;
      else if (!(elmt.state == NDBOBJ::StateBuilding))
unknown's avatar
unknown committed
5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558
      {
        sql_print_information("NDB: skipping setup table %s.%s, in state %d",
                              elmt.database, elmt.name, elmt.state);
        continue;
      }

      ndb->setDatabaseName(elmt.database);
      const NDBTAB *ndbtab;

      if (!(ndbtab= dict->getTable(elmt.name)))
      {
5559
        if (do_handle_table)
unknown's avatar
unknown committed
5560 5561 5562 5563
          sql_print_error("NDB: failed to setup table %s.%s, error: %d, %s",
                          elmt.database, elmt.name,
                          dict->getNdbError().code,
                          dict->getNdbError().message);
unknown's avatar
unknown committed
5564 5565 5566 5567 5568 5569 5570
        unhandled++;
        continue;
      }

      if (ndbtab->getFrmLength() == 0)
        continue;
    
5571
      /* check if database exists */
5572 5573
      char *end= key +
        build_table_filename(key, sizeof(key), elmt.database, "", "");
5574 5575 5576 5577 5578
      if (my_access(key, F_OK))
      {
        /* no such database defined, skip table */
        continue;
      }
5579 5580 5581
      /* finalize construction of path */
      end+= tablename_to_filename(elmt.name, end,
                                  sizeof(key)-(end-key));
unknown's avatar
unknown committed
5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593
      const void *data= 0, *pack_data= 0;
      uint length, pack_length;
      int discover= 0;
      if (readfrm(key, &data, &length) ||
          packfrm(data, length, &pack_data, &pack_length))
      {
        discover= 1;
        sql_print_information("NDB: missing frm for %s.%s, discovering...",
                              elmt.database, elmt.name);
      }
      else if (cmp_frm(ndbtab, pack_data, pack_length))
      {
unknown's avatar
unknown committed
5594
        NDB_SHARE *share= get_share(key, 0, false);
5595
        if (!share || get_ndb_share_state(share) != NSS_ALTERED)
unknown's avatar
unknown committed
5596 5597 5598 5599 5600
        {
          discover= 1;
          sql_print_information("NDB: mismatch in frm for %s.%s, discovering...",
                                elmt.database, elmt.name);
        }
unknown's avatar
unknown committed
5601 5602
        if (share)
          free_share(&share);
unknown's avatar
unknown committed
5603 5604 5605 5606
      }
      my_free((char*) data, MYF(MY_ALLOW_ZERO_PTR));
      my_free((char*) pack_data, MYF(MY_ALLOW_ZERO_PTR));

5607
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
5608 5609 5610
      if (discover)
      {
        /* ToDo 4.1 database needs to be created if missing */
5611
        if (ndb_create_table_from_engine(thd, elmt.database, elmt.name))
unknown's avatar
unknown committed
5612 5613 5614 5615
        {
          /* ToDo 4.1 handle error */
        }
      }
unknown's avatar
unknown committed
5616
#ifdef HAVE_NDB_BINLOG
5617
      else
unknown's avatar
unknown committed
5618 5619
      {
        /* set up replication for this table */
5620 5621 5622
        ndbcluster_create_binlog_setup(ndb, key, end-key,
                                       elmt.database, elmt.name,
                                       TRUE);
unknown's avatar
unknown committed
5623 5624
      }
#endif
5625
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
5626 5627 5628 5629 5630 5631
    }
  }
  while (unhandled && retries--);

  DBUG_RETURN(0);
}
5632

5633
int ndbcluster_find_files(THD *thd,const char *db,const char *path,
5634
                          const char *wild, bool dir, List<char> *files)
unknown's avatar
unknown committed
5635
{
5636 5637 5638
  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
5639
  uint i;
5640
  Ndb* ndb;
5641
  char name[FN_REFLEN];
unknown's avatar
unknown committed
5642
  HASH ndb_tables, ok_tables;
unknown's avatar
unknown committed
5643
  NDBDICT::List list;
5644 5645 5646 5647

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

5648
  if (dir)
unknown's avatar
unknown committed
5649
    DBUG_RETURN(0); // Discover of databases not yet supported
5650

unknown's avatar
unknown committed
5651
  // List tables in NDB
5652
  NDBDICT *dict= ndb->getDictionary();
unknown's avatar
unknown committed
5653
  if (dict->listObjects(list, 
5654
                        NdbDictionary::Object::UserTable) != 0)
unknown's avatar
unknown committed
5655
    ERR_RETURN(dict->getNdbError());
5656

unknown's avatar
unknown committed
5657
  if (hash_init(&ndb_tables, system_charset_info,list.count,0,0,
5658
                (hash_get_key)tables_get_key,0,0))
unknown's avatar
unknown committed
5659 5660 5661 5662 5663 5664
  {
    DBUG_PRINT("error", ("Failed to init HASH ndb_tables"));
    DBUG_RETURN(-1);
  }

  if (hash_init(&ok_tables, system_charset_info,32,0,0,
5665
                (hash_get_key)tables_get_key,0,0))
unknown's avatar
unknown committed
5666 5667 5668 5669 5670 5671
  {
    DBUG_PRINT("error", ("Failed to init HASH ok_tables"));
    hash_free(&ndb_tables);
    DBUG_RETURN(-1);
  }  

unknown's avatar
unknown committed
5672 5673
  for (i= 0 ; i < list.count ; i++)
  {
unknown's avatar
unknown committed
5674
    NDBDICT::List::Element& elmt= list.elements[i];
unknown's avatar
unknown committed
5675
    if (IS_TMP_PREFIX(elmt.name) || IS_NDB_BLOB_PREFIX(elmt.name))
unknown's avatar
unknown committed
5676 5677 5678 5679
    {
      DBUG_PRINT("info", ("Skipping %s.%s in NDB", elmt.database, elmt.name));
      continue;
    }
unknown's avatar
unknown committed
5680
    DBUG_PRINT("info", ("Found %s/%s in NDB", elmt.database, elmt.name));
unknown's avatar
unknown committed
5681

5682
    // Add only tables that belongs to db
unknown's avatar
unknown committed
5683
    if (my_strcasecmp(system_charset_info, elmt.database, db))
5684
      continue;
unknown's avatar
unknown committed
5685

unknown's avatar
unknown committed
5686
    // Apply wildcard to list of tables in NDB
5687
    if (wild)
5688
    {
5689 5690
      if (lower_case_table_names)
      {
unknown's avatar
unknown committed
5691
        if (wild_case_compare(files_charset_info, elmt.name, wild))
5692
          continue;
5693
      }
unknown's avatar
unknown committed
5694
      else if (wild_compare(elmt.name,wild,0))
5695
        continue;
5696
    }
unknown's avatar
unknown committed
5697 5698
    DBUG_PRINT("info", ("Inserting %s into ndb_tables hash", elmt.name));     
    my_hash_insert(&ndb_tables, (byte*)thd->strdup(elmt.name));
unknown's avatar
unknown committed
5699 5700
  }

unknown's avatar
unknown committed
5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715
  char *file_name;
  List_iterator<char> it(*files);
  List<char> delete_list;
  while ((file_name=it++))
  {
    DBUG_PRINT("info", ("%s", file_name));     
    if (hash_search(&ndb_tables, file_name, strlen(file_name)))
    {
      DBUG_PRINT("info", ("%s existed in NDB _and_ on disk ", file_name));
      // File existed in NDB and as frm file, put in ok_tables list
      my_hash_insert(&ok_tables, (byte*)file_name);
      continue;
    }
    
    // File is not in NDB, check for .ndb file with this name
5716
    build_table_filename(name, sizeof(name), db, file_name, ha_ndb_ext);
unknown's avatar
unknown committed
5717
    DBUG_PRINT("info", ("Check access for %s", name));
5718
    if (my_access(name, F_OK))
unknown's avatar
unknown committed
5719 5720 5721 5722
    {
      DBUG_PRINT("info", ("%s did not exist on disk", name));     
      // .ndb file did not exist on disk, another table type
      continue;
5723
    }
5724

unknown's avatar
unknown committed
5725 5726 5727
    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.
5728
    if (ndbcluster_table_exists_in_engine(thd, db, file_name) == 0)    
unknown's avatar
unknown committed
5729 5730 5731 5732 5733 5734 5735
    {
      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));
    }
  }
5736

unknown's avatar
unknown committed
5737 5738 5739
#ifdef HAVE_NDB_BINLOG
  /* setup logging to binlog for all discovered tables */
  {
5740 5741
    char *end, *end1= name +
      build_table_filename(name, sizeof(name), db, "", "");
unknown's avatar
unknown committed
5742 5743 5744
    for (i= 0; i < ok_tables.records; i++)
    {
      file_name= (char*)hash_element(&ok_tables, i);
5745 5746
      end= end1 +
        tablename_to_filename(file_name, end1, sizeof(name) - (end1 - name));
5747 5748 5749 5750
      pthread_mutex_lock(&LOCK_open);
      ndbcluster_create_binlog_setup(ndb, name, end-name,
                                     db, file_name, TRUE);
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
5751 5752 5753 5754
    }
  }
#endif

unknown's avatar
unknown committed
5755 5756 5757 5758
  // 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++)
5759
  {
unknown's avatar
unknown committed
5760 5761
    file_name= hash_element(&ndb_tables, i);
    if (!hash_search(&ok_tables, file_name, strlen(file_name)))
5762
    {
5763 5764
      build_table_filename(name, sizeof(name), db, file_name, reg_ext);
      if (my_access(name, F_OK))
unknown's avatar
unknown committed
5765 5766 5767 5768 5769 5770
      {
        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));
      }
unknown's avatar
unknown committed
5771 5772
    }
  }
5773

unknown's avatar
unknown committed
5774 5775
  // Lock mutex before deleting and creating frm files
  pthread_mutex_lock(&LOCK_open);
5776

unknown's avatar
unknown committed
5777 5778 5779 5780 5781
  if (!global_read_lock)
  {
    // Delete old files
    List_iterator_fast<char> it3(delete_list);
    while ((file_name=it3++))
5782 5783
    {
      DBUG_PRINT("info", ("Remove table %s/%s", db, file_name));
unknown's avatar
unknown committed
5784 5785 5786 5787
      // Delete the table and all related files
      TABLE_LIST table_list;
      bzero((char*) &table_list,sizeof(table_list));
      table_list.db= (char*) db;
5788
      table_list.alias= table_list.table_name= (char*)file_name;
5789
      (void)mysql_rm_table_part2(thd, &table_list,
unknown's avatar
Merge  
unknown committed
5790 5791 5792 5793
                                                                 /* if_exists */ FALSE,
                                                                 /* drop_temporary */ FALSE,
                                                                 /* drop_view */ FALSE,
                                                                 /* dont_log_query*/ TRUE);
5794 5795
      /* Clear error message that is returned when table is deleted */
      thd->clear_error();
5796 5797 5798
    }
  }

unknown's avatar
unknown committed
5799 5800 5801 5802
  // Create new files
  List_iterator_fast<char> it2(create_list);
  while ((file_name=it2++))
  {  
5803
    DBUG_PRINT("info", ("Table %s need discovery", file_name));
5804
    if (ndb_create_table_from_engine(thd, db, file_name) == 0)
5805
      files->push_back(thd->strdup(file_name)); 
unknown's avatar
unknown committed
5806 5807
  }

unknown's avatar
unknown committed
5808
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
5809 5810
  
  hash_free(&ok_tables);
5811
  hash_free(&ndb_tables);
5812
  } // extra bracket to avoid gcc 2.95.3 warning
5813
  DBUG_RETURN(0);    
unknown's avatar
unknown committed
5814 5815 5816 5817 5818 5819 5820 5821
}


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

5822 5823 5824 5825
/* Call back after cluster connect */
static int connect_callback()
{
  update_status_variables(g_ndb_cluster_connection);
unknown's avatar
unknown committed
5826 5827 5828 5829 5830 5831 5832

  uint node_id, i= 0;
  Ndb_cluster_connection_node_iter node_iter;
  memset((void *)g_node_id_map, 0xFFFF, sizeof(g_node_id_map));
  while ((node_id= g_ndb_cluster_connection->get_next_node(node_iter)))
    g_node_id_map[node_id]= i++;

unknown's avatar
unknown committed
5833
  pthread_cond_signal(&COND_ndb_util_thread);
5834 5835 5836
  return 0;
}

unknown's avatar
unknown committed
5837
static bool ndbcluster_init()
unknown's avatar
unknown committed
5838
{
unknown's avatar
unknown committed
5839
  int res;
unknown's avatar
unknown committed
5840
  DBUG_ENTER("ndbcluster_init");
5841 5842 5843 5844

  if (have_ndbcluster != SHOW_OPTION_YES)
    goto ndbcluster_init_error;

unknown's avatar
unknown committed
5845 5846 5847 5848 5849 5850 5851 5852 5853 5854
  {
    handlerton &h= ndbcluster_hton;
    h.close_connection= ndbcluster_close_connection;
    h.commit=           ndbcluster_commit;
    h.rollback=         ndbcluster_rollback;
    h.create=           ndbcluster_create_handler; /* Create a new handler */
    h.drop_database=    ndbcluster_drop_database;  /* Drop a database */
    h.panic=            ndbcluster_end;            /* Panic call */
    h.show_status=      ndbcluster_show_status;    /* Show status */
    h.alter_tablespace= ndbcluster_alter_tablespace;    /* Show status */
unknown's avatar
unknown committed
5855 5856
    h.partition_flags=  ndbcluster_partition_flags; /* Partition flags */
    h.alter_table_flags=ndbcluster_alter_table_flags; /* Alter table flags */
5857
    h.fill_files_table= ndbcluster_fill_files_table;
unknown's avatar
unknown committed
5858 5859 5860
#ifdef HAVE_NDB_BINLOG
    ndbcluster_binlog_init_handlerton();
#endif
5861
    h.flags=            HTON_TEMPORARY_NOT_SUPPORTED;
unknown's avatar
unknown committed
5862 5863
  }

5864
  // Set connectstring if specified
5865 5866
  if (opt_ndbcluster_connectstring != 0)
    DBUG_PRINT("connectstring", ("%s", opt_ndbcluster_connectstring));     
5867
  if ((g_ndb_cluster_connection=
5868
       new Ndb_cluster_connection(opt_ndbcluster_connectstring)) == 0)
5869
  {
5870
    DBUG_PRINT("error",("Ndb_cluster_connection(%s)",
5871
                        opt_ndbcluster_connectstring));
unknown's avatar
unknown committed
5872
    goto ndbcluster_init_error;
5873
  }
unknown's avatar
ndb:  
unknown committed
5874 5875 5876 5877 5878
  {
    char buf[128];
    my_snprintf(buf, sizeof(buf), "mysqld --server-id=%d", server_id);
    g_ndb_cluster_connection->set_name(buf);
  }
5879 5880 5881
  g_ndb_cluster_connection->set_optimized_node_selection
    (opt_ndb_optimized_node_selection);

unknown's avatar
unknown committed
5882
  // Create a Ndb object to open the connection  to NDB
5883 5884 5885 5886 5887
  if ( (g_ndb= new Ndb(g_ndb_cluster_connection, "sys")) == 0 )
  {
    DBUG_PRINT("error", ("failed to create global ndb object"));
    goto ndbcluster_init_error;
  }
5888
  g_ndb->getDictionary()->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
unknown's avatar
unknown committed
5889 5890 5891
  if (g_ndb->init() != 0)
  {
    ERR_PRINT (g_ndb->getNdbError());
unknown's avatar
unknown committed
5892
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
5893
  }
unknown's avatar
unknown committed
5894

unknown's avatar
unknown committed
5895
  if ((res= g_ndb_cluster_connection->connect(0,0,0)) == 0)
unknown's avatar
unknown committed
5896
  {
5897
    connect_callback();
unknown's avatar
unknown committed
5898
    DBUG_PRINT("info",("NDBCLUSTER storage engine at %s on port %d",
5899 5900
                       g_ndb_cluster_connection->get_connected_host(),
                       g_ndb_cluster_connection->get_connected_port()));
5901
    g_ndb_cluster_connection->wait_until_ready(10,3);
unknown's avatar
unknown committed
5902
  } 
unknown's avatar
unknown committed
5903
  else if (res == 1)
unknown's avatar
unknown committed
5904
  {
5905
    if (g_ndb_cluster_connection->start_connect_thread(connect_callback)) 
5906
    {
unknown's avatar
unknown committed
5907
      DBUG_PRINT("error", ("g_ndb_cluster_connection->start_connect_thread()"));
unknown's avatar
unknown committed
5908 5909
      goto ndbcluster_init_error;
    }
5910
#ifndef DBUG_OFF
unknown's avatar
unknown committed
5911 5912
    {
      char buf[1024];
5913
      DBUG_PRINT("info",
5914 5915 5916 5917
                 ("NDBCLUSTER storage engine not started, "
                  "will connect using %s",
                  g_ndb_cluster_connection->
                  get_connectstring(buf,sizeof(buf))));
unknown's avatar
unknown committed
5918
    }
5919
#endif
unknown's avatar
unknown committed
5920
  }
unknown's avatar
unknown committed
5921
  else
unknown's avatar
unknown committed
5922 5923 5924
  {
    DBUG_ASSERT(res == -1);
    DBUG_PRINT("error", ("permanent error"));
unknown's avatar
unknown committed
5925
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
5926
  }
unknown's avatar
unknown committed
5927
  
unknown's avatar
unknown committed
5928 5929 5930
  (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
unknown committed
5931 5932
#ifdef HAVE_NDB_BINLOG
  /* start the ndb injector thread */
5933 5934
  if (ndbcluster_binlog_start())
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
5935 5936
#endif /* HAVE_NDB_BINLOG */
  
unknown's avatar
Merge  
unknown committed
5937 5938
  pthread_mutex_init(&LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST);
  pthread_cond_init(&COND_ndb_util_thread, NULL);
5939

unknown's avatar
Merge  
unknown committed
5940 5941 5942 5943 5944 5945

  // 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"));
5946 5947 5948 5949
    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
5950 5951 5952
    goto ndbcluster_init_error;
  }
  
unknown's avatar
unknown committed
5953
  ndbcluster_inited= 1;
5954
  DBUG_RETURN(FALSE);
unknown's avatar
Merge  
unknown committed
5955

5956
ndbcluster_init_error:
unknown's avatar
unknown committed
5957
  if (g_ndb)
5958 5959 5960 5961 5962
    delete g_ndb;
  g_ndb= NULL;
  if (g_ndb_cluster_connection)
    delete g_ndb_cluster_connection;
  g_ndb_cluster_connection= NULL;
5963 5964
  have_ndbcluster= SHOW_OPTION_DISABLED;	// If we couldn't use handler
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
5965 5966
}

unknown's avatar
unknown committed
5967
static int ndbcluster_end(ha_panic_function type)
unknown's avatar
unknown committed
5968 5969
{
  DBUG_ENTER("ndbcluster_end");
unknown's avatar
Merge  
unknown committed
5970

5971 5972 5973
  if (!ndbcluster_inited)
    DBUG_RETURN(0);

unknown's avatar
unknown committed
5974
  if (g_ndb)
5975 5976
  {
#ifndef DBUG_OFF
unknown's avatar
unknown committed
5977 5978
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
5979 5980 5981 5982 5983 5984 5985 5986 5987 5988
    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
5989
    delete g_ndb;
unknown's avatar
unknown committed
5990
    g_ndb= NULL;
5991
  }
unknown's avatar
unknown committed
5992
  delete g_ndb_cluster_connection;
5993
  g_ndb_cluster_connection= NULL;
5994

unknown's avatar
unknown committed
5995 5996
  hash_free(&ndbcluster_open_tables);
  pthread_mutex_destroy(&ndbcluster_mutex);
unknown's avatar
Merge  
unknown committed
5997 5998
  pthread_mutex_destroy(&LOCK_ndb_util_thread);
  pthread_cond_destroy(&COND_ndb_util_thread);
unknown's avatar
unknown committed
5999 6000 6001 6002
  ndbcluster_inited= 0;
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
6003 6004 6005 6006 6007 6008
void ha_ndbcluster::print_error(int error, myf errflag)
{
  DBUG_ENTER("ha_ndbcluster::print_error");
  DBUG_PRINT("enter", ("error = %d", error));

  if (error == HA_ERR_NO_PARTITION_FOUND)
6009 6010
  {
    char buf[100];
unknown's avatar
unknown committed
6011
    my_error(ER_NO_PARTITION_FOR_GIVEN_VALUE, MYF(0),
6012 6013
             llstr(m_part_info->part_expr->val_int(), buf));
  }
unknown's avatar
unknown committed
6014 6015 6016 6017 6018 6019
  else
    handler::print_error(error, errflag);
  DBUG_VOID_RETURN;
}


6020 6021 6022 6023 6024
/*
  Static error print function called from
  static handler method ndbcluster_commit
  and ndbcluster_rollback
*/
6025 6026

void ndbcluster_print_error(int error, const NdbOperation *error_op)
6027
{
6028
  DBUG_ENTER("ndbcluster_print_error");
unknown's avatar
unknown committed
6029
  TABLE_SHARE share;
6030
  const char *tab_name= (error_op) ? error_op->getTableName() : "";
unknown's avatar
unknown committed
6031 6032 6033 6034 6035
  share.db.str= (char*) "";
  share.db.length= 0;
  share.table_name.str= (char *) tab_name;
  share.table_name.length= strlen(tab_name);
  ha_ndbcluster error_handler(&share);
6036
  error_handler.print_error(error, MYF(0));
unknown's avatar
unknown committed
6037
  DBUG_VOID_RETURN;
6038
}
unknown's avatar
unknown committed
6039

6040 6041 6042
/**
 * Set a given location from full pathname to database name
 *
unknown's avatar
unknown committed
6043
 */
6044
void ha_ndbcluster::set_dbname(const char *path_name, char *dbname)
unknown's avatar
unknown committed
6045
{
unknown's avatar
unknown committed
6046 6047 6048 6049
  char *end, *ptr, *tmp_name;
  char tmp_buff[FN_REFLEN];
 
  tmp_name= tmp_buff;
unknown's avatar
unknown committed
6050
  /* Scan name from the end */
6051 6052 6053 6054 6055 6056
  ptr= strend(path_name)-1;
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  ptr--;
  end= ptr;
unknown's avatar
unknown committed
6057 6058 6059 6060
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  uint name_len= end - ptr;
unknown's avatar
unknown committed
6061 6062
  memcpy(tmp_name, ptr + 1, name_len);
  tmp_name[name_len]= '\0';
unknown's avatar
unknown committed
6063 6064
#ifdef __WIN__
  /* Put to lower case */
6065
  
unknown's avatar
unknown committed
6066
  ptr= tmp_name;
unknown's avatar
unknown committed
6067 6068
  
  while (*ptr != '\0') {
6069
    *ptr= tolower(*ptr);
unknown's avatar
unknown committed
6070 6071 6072
    ptr++;
  }
#endif
unknown's avatar
unknown committed
6073
  filename_to_tablename(tmp_name, dbname, FN_REFLEN);
unknown's avatar
unknown committed
6074 6075
}

6076 6077 6078 6079 6080 6081 6082 6083 6084
/*
  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
6085 6086 6087 6088 6089 6090 6091
/**
 * Set a given location from full pathname to table file
 *
 */
void
ha_ndbcluster::set_tabname(const char *path_name, char * tabname)
{
unknown's avatar
unknown committed
6092 6093 6094 6095
  char *end, *ptr, *tmp_name;
  char tmp_buff[FN_REFLEN];

  tmp_name= tmp_buff;
unknown's avatar
unknown committed
6096
  /* Scan name from the end */
6097 6098
  end= strend(path_name)-1;
  ptr= end;
unknown's avatar
unknown committed
6099 6100 6101
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
6102
  uint name_len= end - ptr;
unknown's avatar
unknown committed
6103 6104
  memcpy(tmp_name, ptr + 1, end - ptr);
  tmp_name[name_len]= '\0';
unknown's avatar
unknown committed
6105 6106
#ifdef __WIN__
  /* Put to lower case */
unknown's avatar
unknown committed
6107
  ptr= tmp_name;
unknown's avatar
unknown committed
6108 6109 6110 6111 6112 6113
  
  while (*ptr != '\0') {
    *ptr= tolower(*ptr);
    ptr++;
  }
#endif
unknown's avatar
unknown committed
6114
  filename_to_tablename(tmp_name, tabname, FN_REFLEN);
unknown's avatar
unknown committed
6115 6116 6117
}

/*
6118
  Set m_tabname from full pathname to table file 
unknown's avatar
unknown committed
6119 6120
 */

6121
void ha_ndbcluster::set_tabname(const char *path_name)
unknown's avatar
unknown committed
6122
{
6123
  set_tabname(path_name, m_tabname);
unknown's avatar
unknown committed
6124 6125 6126 6127
}


ha_rows 
unknown's avatar
unknown committed
6128 6129 6130 6131
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
6132
  uint key_length= key_info->key_length;
6133
  NDB_INDEX_TYPE idx_type= get_index_type(inx);  
unknown's avatar
unknown committed
6134 6135

  DBUG_ENTER("records_in_range");
6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148
  // 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);
  
6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 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 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226
  if ((idx_type == PRIMARY_KEY_ORDERED_INDEX ||
       idx_type == UNIQUE_ORDERED_INDEX ||
       idx_type == ORDERED_INDEX) &&
    m_index[inx].index_stat != NULL)
  {
    NDB_INDEX_DATA& d=m_index[inx];
    NDBINDEX* index=(NDBINDEX*)d.index;
    Ndb* ndb=get_ndb();
    NdbTransaction* trans=NULL;
    NdbIndexScanOperation* op=NULL;
    int res=0;
    Uint64 rows;

    do
    {
      // We must provide approx table rows
      Uint64 table_rows=0;
      Ndb_local_table_statistics *info= 
        (Ndb_local_table_statistics *)m_table_info;
      if (info->records != ~(ha_rows)0 && info->records != 0)
      {
        table_rows = info->records;
        DBUG_PRINT("info", ("use info->records: %llu", table_rows));
      }
      else
      {
        Ndb_statistics stat;
        if ((res=ndb_get_table_statistics(ndb, m_tabname, &stat)) != 0)
          break;
        table_rows=stat.row_count;
        DBUG_PRINT("info", ("use db row_count: %llu", table_rows));
        if (table_rows == 0) {
          // Problem if autocommit=0
#ifdef ndb_get_table_statistics_uses_active_trans
          rows=0;
          break;
#endif
        }
      }

      // Define scan op for the range
      if ((trans=m_active_trans) == NULL)
      {
        DBUG_PRINT("info", ("no active trans"));
        if (! (trans=ndb->startTransaction()))
          ERR_BREAK(ndb->getNdbError(), res);
      }
      if (! (op=trans->getNdbIndexScanOperation(index, (NDBTAB*)m_table)))
        ERR_BREAK(trans->getNdbError(), res);
      if ((op->readTuples(NdbOperation::LM_CommittedRead)) == -1)
        ERR_BREAK(op->getNdbError(), res);
      const key_range *keys[2]={ min_key, max_key };
      if ((res=set_bounds(op, inx, true, keys)) != 0)
        break;

      // Decide if db should be contacted
      int flags=0;
      if (d.index_stat_query_count < d.index_stat_cache_entries ||
          (d.index_stat_update_freq != 0 &&
           d.index_stat_query_count % d.index_stat_update_freq == 0))
      {
        DBUG_PRINT("info", ("force stat from db"));
        flags|=NdbIndexStat::RR_UseDb;
      }
      if (d.index_stat->records_in_range(index, op, table_rows, &rows, flags) == -1)
        ERR_BREAK(d.index_stat->getNdbError(), res);
      d.index_stat_query_count++;
    } while (0);

    if (trans != m_active_trans && rows == 0)
      rows = 1;
    if (trans != m_active_trans && trans != NULL)
      ndb->closeTransaction(trans);
    if (res != 0)
      DBUG_RETURN(HA_POS_ERROR);
    DBUG_RETURN(rows);
  }

6227
  DBUG_RETURN(10); /* Good guess when you don't know anything */
unknown's avatar
unknown committed
6228 6229
}

6230 6231 6232 6233 6234 6235 6236 6237 6238
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 
{
6239
  return("NDBCLUSTER");
6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256
}
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
6257 6258 6259 6260
uint ha_ndbcluster::max_supported_key_part_length() const
{
  return NDB_MAX_KEY_SIZE;
}
6261 6262 6263 6264 6265 6266 6267 6268 6269 6270
bool ha_ndbcluster::low_byte_first() const
{ 
#ifdef WORDS_BIGENDIAN
  return FALSE;
#else
  return TRUE;
#endif
}
bool ha_ndbcluster::has_transactions()
{
6271
  return TRUE;
6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285
}
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
6286

6287 6288
uint8 ha_ndbcluster::table_cache_type()
{
unknown's avatar
Merge  
unknown committed
6289 6290 6291 6292 6293 6294
  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,
6295
                         Uint64 *commit_count)
unknown's avatar
Merge  
unknown committed
6296
{
6297 6298
  char name[FN_REFLEN];
  NDB_SHARE *share;
unknown's avatar
unknown committed
6299 6300
  DBUG_ENTER("ndb_get_commitcount");

6301
  build_table_filename(name, sizeof(name), dbname, tabname, "");
6302 6303 6304 6305 6306 6307 6308
  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);
unknown's avatar
unknown committed
6309
    DBUG_PRINT("info", ("Table %s not found in ndbcluster_open_tables", name));
6310 6311 6312 6313 6314 6315
    DBUG_RETURN(1);
  }
  share->use_count++;
  pthread_mutex_unlock(&ndbcluster_mutex);

  pthread_mutex_lock(&share->mutex);
unknown's avatar
Merge  
unknown committed
6316 6317
  if (ndb_cache_check_time > 0)
  {
6318
    if (share->commit_count != 0)
unknown's avatar
Merge  
unknown committed
6319
    {
6320 6321 6322 6323
      *commit_count= share->commit_count;
      DBUG_PRINT("info", ("Getting commit_count: %llu from share",
                          share->commit_count));
      pthread_mutex_unlock(&share->mutex);
unknown's avatar
unknown committed
6324
      free_share(&share);
6325
      DBUG_RETURN(0);
unknown's avatar
Merge  
unknown committed
6326 6327
    }
  }
6328
  DBUG_PRINT("info", ("Get commit_count from NDB"));
unknown's avatar
Merge  
unknown committed
6329 6330 6331 6332
  Ndb *ndb;
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(1);
  ndb->setDatabaseName(dbname);
6333 6334
  uint lock= share->commit_count_lock;
  pthread_mutex_unlock(&share->mutex);
unknown's avatar
Merge  
unknown committed
6335 6336 6337

  struct Ndb_statistics stat;
  if (ndb_get_table_statistics(ndb, tabname, &stat))
6338
  {
unknown's avatar
unknown committed
6339
    free_share(&share);
unknown's avatar
Merge  
unknown committed
6340
    DBUG_RETURN(1);
6341 6342 6343
  }

  pthread_mutex_lock(&share->mutex);
unknown's avatar
unknown committed
6344
  if (share->commit_count_lock == lock)
6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355
  {
    DBUG_PRINT("info", ("Setting commit_count to %llu", stat.commit_count));
    share->commit_count= stat.commit_count;
    *commit_count= stat.commit_count;
  }
  else
  {
    DBUG_PRINT("info", ("Discarding commit_count, comit_count_lock changed"));
    *commit_count= 0;
  }
  pthread_mutex_unlock(&share->mutex);
unknown's avatar
unknown committed
6356
  free_share(&share);
unknown's avatar
Merge  
unknown committed
6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392
  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,
6393 6394
                                   char *full_name, uint full_name_len,
                                   ulonglong *engine_data)
unknown's avatar
Merge  
unknown committed
6395 6396 6397 6398 6399 6400 6401 6402
{
  DBUG_ENTER("ndbcluster_cache_retrieval_allowed");

  Uint64 commit_count;
  bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
  char *dbname= full_name;
  char *tabname= dbname+strlen(dbname)+1;

6403 6404
  DBUG_PRINT("enter", ("dbname: %s, tabname: %s, is_autocommit: %d",
                       dbname, tabname, is_autocommit));
unknown's avatar
Merge  
unknown committed
6405 6406

  if (!is_autocommit)
6407 6408
  {
    DBUG_PRINT("exit", ("No, don't use cache in transaction"));
unknown's avatar
Merge  
unknown committed
6409
    DBUG_RETURN(FALSE);
6410
  }
unknown's avatar
Merge  
unknown committed
6411 6412 6413

  if (ndb_get_commitcount(thd, dbname, tabname, &commit_count))
  {
6414 6415
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, could not retrieve commit_count"));
unknown's avatar
Merge  
unknown committed
6416 6417
    DBUG_RETURN(FALSE);
  }
6418
  DBUG_PRINT("info", ("*engine_data: %llu, commit_count: %llu",
6419
                      *engine_data, commit_count));
6420
  if (commit_count == 0)
unknown's avatar
Merge  
unknown committed
6421
  {
6422 6423
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, local commit has been performed"));
unknown's avatar
Merge  
unknown committed
6424 6425
    DBUG_RETURN(FALSE);
  }
6426 6427 6428 6429 6430 6431
  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
6432

6433
  DBUG_PRINT("exit", ("OK to use cache, engine_data: %llu", *engine_data));
unknown's avatar
Merge  
unknown committed
6434 6435 6436 6437 6438 6439 6440 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461
  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,
6462 6463 6464
                                          char *full_name, uint full_name_len,
                                          qc_engine_callback *engine_callback,
                                          ulonglong *engine_data)
unknown's avatar
Merge  
unknown committed
6465 6466 6467 6468
{
  DBUG_ENTER("ha_ndbcluster::register_query_cache_table");

  bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
6469 6470 6471 6472

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

unknown's avatar
Merge  
unknown committed
6473
  if (!is_autocommit)
6474
  {
unknown's avatar
unknown committed
6475
    DBUG_PRINT("exit", ("Can't register table during transaction"));
unknown's avatar
Merge  
unknown committed
6476
    DBUG_RETURN(FALSE);
6477
  }
unknown's avatar
Merge  
unknown committed
6478 6479 6480 6481 6482

  Uint64 commit_count;
  if (ndb_get_commitcount(thd, m_dbname, m_tabname, &commit_count))
  {
    *engine_data= 0;
unknown's avatar
unknown committed
6483
    DBUG_PRINT("exit", ("Error, could not get commitcount"));
unknown's avatar
Merge  
unknown committed
6484 6485 6486 6487
    DBUG_RETURN(FALSE);
  }
  *engine_data= commit_count;
  *engine_callback= ndbcluster_cache_retrieval_allowed;
6488 6489
  DBUG_PRINT("exit", ("commit_count: %llu", commit_count));
  DBUG_RETURN(commit_count > 0);
6490
}
unknown's avatar
unknown committed
6491

unknown's avatar
Merge  
unknown committed
6492

unknown's avatar
unknown committed
6493
/*
unknown's avatar
Merge  
unknown committed
6494
  Handling the shared NDB_SHARE structure that is needed to
unknown's avatar
unknown committed
6495 6496 6497 6498 6499 6500
  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.
 */

unknown's avatar
unknown committed
6501
static byte *ndbcluster_get_key(NDB_SHARE *share,uint *length,
6502
                                my_bool not_used __attribute__((unused)))
unknown's avatar
unknown committed
6503
{
unknown's avatar
unknown committed
6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521
  *length= share->key_length;
  return (byte*) share->key;
}

#ifndef DBUG_OFF
static void dbug_print_open_tables()
{
  DBUG_ENTER("dbug_print_open_tables");
  for (uint i= 0; i < ndbcluster_open_tables.records; i++)
  {
    NDB_SHARE *share= (NDB_SHARE*) hash_element(&ndbcluster_open_tables, i);
    DBUG_PRINT("share",
               ("[%d] 0x%lx key: %s  key_length: %d",
                i, share, share->key, share->key_length));
    DBUG_PRINT("share",
               ("db.tablename: %s.%s  use_count: %d  commit_count: %d",
                share->db, share->table_name,
                share->use_count, share->commit_count));
unknown's avatar
unknown committed
6522 6523 6524 6525 6526 6527
#ifdef HAVE_NDB_BINLOG
    if (share->table)
      DBUG_PRINT("share",
                 ("table->s->db.table_name: %s.%s",
                  share->table->s->db.str, share->table->s->table_name.str));
#endif
unknown's avatar
unknown committed
6528 6529
  }
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
6530
}
unknown's avatar
unknown committed
6531 6532 6533
#else
#define dbug_print_open_tables()
#endif
unknown's avatar
unknown committed
6534

unknown's avatar
unknown committed
6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584
#ifdef HAVE_NDB_BINLOG
/*
  For some reason a share is still around, try to salvage the situation
  by closing all cached tables. If the share still exists, there is an
  error somewhere but only report this to the error log.  Keep this
  "trailing share" but rename it since there are still references to it
  to avoid segmentation faults.  There is a risk that the memory for
  this trailing share leaks.
  
  Must be called with previous pthread_mutex_lock(&ndbcluster_mutex)
*/
int handle_trailing_share(NDB_SHARE *share)
{
  static ulong trailing_share_id= 0;
  DBUG_ENTER("handle_trailing_share");

  ++share->use_count;
  pthread_mutex_unlock(&ndbcluster_mutex);

  close_cached_tables((THD*) 0, 0, (TABLE_LIST*) 0, TRUE);

  pthread_mutex_lock(&ndbcluster_mutex);
  if (!--share->use_count)
  {
    DBUG_PRINT("info", ("NDB_SHARE: close_cashed_tables %s freed share.",
               share->key)); 
    real_free_share(&share);
    DBUG_RETURN(0);
  }

  /*
    share still exists, if share has not been dropped by server
    release that share
  */
  if (share->state != NSS_DROPPED && !--share->use_count)
  {
    DBUG_PRINT("info", ("NDB_SHARE: %s already exists, "
                        "use_count=%d  state != NSS_DROPPED.",
                        share->key, share->use_count)); 
    real_free_share(&share);
    DBUG_RETURN(0);
  }
  DBUG_PRINT("error", ("NDB_SHARE: %s already exists  use_count=%d.",
                       share->key, share->use_count));

  sql_print_error("NDB_SHARE: %s already exists  use_count=%d."
                  " Moving away for safety, but possible memleak.",
                  share->key, share->use_count);
  dbug_print_open_tables();

6585 6586 6587 6588 6589
  /*
    Ndb share has not been released as it should
  */
  DBUG_ASSERT(FALSE);

unknown's avatar
unknown committed
6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619 6620 6621 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678
  /*
    This is probably an error.  We can however save the situation
    at the cost of a possible mem leak, by "renaming" the share
    - First remove from hash
  */
  hash_delete(&ndbcluster_open_tables, (byte*) share);

  /*
    now give it a new name, just a running number
    if space is not enough allocate some more
  */
  {
    const uint min_key_length= 10;
    if (share->key_length < min_key_length)
    {
      share->key= alloc_root(&share->mem_root, min_key_length + 1);
      share->key_length= min_key_length;
    }
    share->key_length=
      my_snprintf(share->key, min_key_length + 1, "#leak%d",
                  trailing_share_id++);
  }
  /* Keep it for possible the future trailing free */
  my_hash_insert(&ndbcluster_open_tables, (byte*) share);

  DBUG_RETURN(0);
}

/*
  Rename share is used during rename table.
*/
static int rename_share(NDB_SHARE *share, const char *new_key)
{
  NDB_SHARE *tmp;
  pthread_mutex_lock(&ndbcluster_mutex);
  uint new_length= (uint) strlen(new_key);
  DBUG_PRINT("rename_share", ("old_key: %s  old__length: %d",
                              share->key, share->key_length));
  if ((tmp= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
                                     (byte*) new_key, new_length)))
    handle_trailing_share(tmp);

  /* remove the share from hash */
  hash_delete(&ndbcluster_open_tables, (byte*) share);
  dbug_print_open_tables();

  /* save old stuff if insert should fail */
  uint old_length= share->key_length;
  char *old_key= share->key;

  /*
    now allocate and set the new key, db etc
    enough space for key, db, and table_name
  */
  share->key= alloc_root(&share->mem_root, 2 * (new_length + 1));
  strmov(share->key, new_key);
  share->key_length= new_length;

  if (my_hash_insert(&ndbcluster_open_tables, (byte*) share))
  {
    // ToDo free the allocated stuff above?
    DBUG_PRINT("error", ("rename_share: my_hash_insert %s failed",
                         share->key));
    share->key= old_key;
    share->key_length= old_length;
    if (my_hash_insert(&ndbcluster_open_tables, (byte*) share))
    {
      sql_print_error("rename_share: failed to recover %s", share->key);
      DBUG_PRINT("error", ("rename_share: my_hash_insert %s failed",
                           share->key));
    }
    dbug_print_open_tables();
    pthread_mutex_unlock(&ndbcluster_mutex);
    return -1;
  }
  dbug_print_open_tables();

  share->db= share->key + new_length + 1;
  ha_ndbcluster::set_dbname(new_key, share->db);
  share->table_name= share->db + strlen(share->db) + 1;
  ha_ndbcluster::set_tabname(new_key, share->table_name);

  DBUG_PRINT("rename_share",
             ("0x%lx key: %s  key_length: %d",
              share, share->key, share->key_length));
  DBUG_PRINT("rename_share",
             ("db.tablename: %s.%s  use_count: %d  commit_count: %d",
              share->db, share->table_name,
              share->use_count, share->commit_count));
6679
  if (share->table)
unknown's avatar
unknown committed
6680
  {
6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691
    DBUG_PRINT("rename_share",
               ("table->s->db.table_name: %s.%s",
                share->table->s->db.str, share->table->s->table_name.str));

    if (share->op == 0)
    {
      share->table->s->db.str= share->db;
      share->table->s->db.length= strlen(share->db);
      share->table->s->table_name.str= share->table_name;
      share->table->s->table_name.length= strlen(share->table_name);
    }
unknown's avatar
unknown committed
6692 6693 6694 6695 6696 6697 6698 6699 6700 6701
  }
  /* else rename will be handled when the ALTER event comes */
  share->old_names= old_key;
  // ToDo free old_names after ALTER EVENT

  pthread_mutex_unlock(&ndbcluster_mutex);
  return 0;
}
#endif

unknown's avatar
unknown committed
6702 6703 6704 6705
/*
  Increase refcount on existing share.
  Always returns share and cannot fail.
*/
unknown's avatar
unknown committed
6706
NDB_SHARE *ndbcluster_get_share(NDB_SHARE *share)
unknown's avatar
unknown committed
6707 6708
{
  pthread_mutex_lock(&ndbcluster_mutex);
unknown's avatar
unknown committed
6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723
  share->use_count++;

  dbug_print_open_tables();

  DBUG_PRINT("get_share",
             ("0x%lx key: %s  key_length: %d",
              share, share->key, share->key_length));
  DBUG_PRINT("get_share",
             ("db.tablename: %s.%s  use_count: %d  commit_count: %d",
              share->db, share->table_name,
              share->use_count, share->commit_count));
  pthread_mutex_unlock(&ndbcluster_mutex);
  return share;
}

unknown's avatar
unknown committed
6724

unknown's avatar
unknown committed
6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738
/*
  Get a share object for key

  Returns share for key, and increases the refcount on the share.

  create_if_not_exists == TRUE:
    creates share if it does not alreade exist
    returns 0 only due to out of memory, and then sets my_error

  create_if_not_exists == FALSE:
    returns 0 if share does not exist

  have_lock == TRUE, pthread_mutex_lock(&ndbcluster_mutex) already taken
*/
unknown's avatar
unknown committed
6739

unknown's avatar
unknown committed
6740 6741 6742
NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table,
                                bool create_if_not_exists,
                                bool have_lock)
unknown's avatar
unknown committed
6743
{
unknown's avatar
unknown committed
6744
  THD *thd= current_thd;
unknown's avatar
unknown committed
6745
  NDB_SHARE *share;
unknown's avatar
unknown committed
6746 6747 6748 6749
  uint length= (uint) strlen(key);
  DBUG_ENTER("ndbcluster_get_share");
  DBUG_PRINT("enter", ("key: '%s'", key));

unknown's avatar
unknown committed
6750 6751 6752 6753 6754
  if (!have_lock)
    pthread_mutex_lock(&ndbcluster_mutex);
  if (!(share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
                                        (byte*) key,
                                        length)))
unknown's avatar
unknown committed
6755
  {
unknown's avatar
unknown committed
6756 6757 6758 6759 6760
    if (!create_if_not_exists)
    {
      DBUG_PRINT("error", ("get_share: %s does not exist", key));
      if (!have_lock)
        pthread_mutex_unlock(&ndbcluster_mutex);
unknown's avatar
unknown committed
6761
      DBUG_RETURN(0);
unknown's avatar
unknown committed
6762 6763
    }
    if ((share= (NDB_SHARE*) my_malloc(sizeof(*share),
unknown's avatar
unknown committed
6764 6765
                                       MYF(MY_WME | MY_ZEROFILL))))
    {
unknown's avatar
unknown committed
6766 6767 6768 6769 6770
      MEM_ROOT **root_ptr=
        my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC);
      MEM_ROOT *old_root= *root_ptr;
      init_sql_alloc(&share->mem_root, 1024, 0);
      *root_ptr= &share->mem_root; // remember to reset before return
unknown's avatar
unknown committed
6771
      share->state= NSS_INITIAL;
unknown's avatar
unknown committed
6772 6773 6774 6775
      /* enough space for key, db, and table_name */
      share->key= alloc_root(*root_ptr, 2 * (length + 1));
      share->key_length= length;
      strmov(share->key, key);
unknown's avatar
unknown committed
6776 6777
      if (my_hash_insert(&ndbcluster_open_tables, (byte*) share))
      {
unknown's avatar
unknown committed
6778 6779 6780 6781 6782
        free_root(&share->mem_root, MYF(0));
        my_free((gptr) share, 0);
        *root_ptr= old_root;
        if (!have_lock)
          pthread_mutex_unlock(&ndbcluster_mutex);
unknown's avatar
unknown committed
6783
        DBUG_RETURN(0);
unknown's avatar
unknown committed
6784 6785
      }
      thr_lock_init(&share->lock);
unknown's avatar
unknown committed
6786
      pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST);
unknown's avatar
Merge  
unknown committed
6787
      share->commit_count= 0;
6788
      share->commit_count_lock= 0;
unknown's avatar
unknown committed
6789 6790 6791 6792
      share->db= share->key + length + 1;
      ha_ndbcluster::set_dbname(key, share->db);
      share->table_name= share->db + strlen(share->db) + 1;
      ha_ndbcluster::set_tabname(key, share->table_name);
unknown's avatar
unknown committed
6793 6794 6795
#ifdef HAVE_NDB_BINLOG
      ndbcluster_binlog_init_share(share, table);
#endif
unknown's avatar
unknown committed
6796
      *root_ptr= old_root;
6797 6798 6799
    }
    else
    {
unknown's avatar
unknown committed
6800 6801 6802 6803
      DBUG_PRINT("error", ("get_share: failed to alloc share"));
      if (!have_lock)
        pthread_mutex_unlock(&ndbcluster_mutex);
      my_error(ER_OUTOFMEMORY, MYF(0), sizeof(*share));
unknown's avatar
unknown committed
6804
      DBUG_RETURN(0);
unknown's avatar
unknown committed
6805 6806 6807
    }
  }
  share->use_count++;
6808

unknown's avatar
unknown committed
6809 6810
  dbug_print_open_tables();

unknown's avatar
unknown committed
6811
  DBUG_PRINT("info",
unknown's avatar
unknown committed
6812 6813
             ("0x%lx key: %s  key_length: %d  key: %s",
              share, share->key, share->key_length, key));
unknown's avatar
unknown committed
6814
  DBUG_PRINT("info",
unknown's avatar
unknown committed
6815 6816 6817 6818 6819
             ("db.tablename: %s.%s  use_count: %d  commit_count: %d",
              share->db, share->table_name,
              share->use_count, share->commit_count));
  if (!have_lock)
    pthread_mutex_unlock(&ndbcluster_mutex);
unknown's avatar
unknown committed
6820
  DBUG_RETURN(share);
unknown's avatar
unknown committed
6821 6822
}

unknown's avatar
unknown committed
6823

unknown's avatar
unknown committed
6824
void ndbcluster_real_free_share(NDB_SHARE **share)
unknown's avatar
unknown committed
6825
{
unknown's avatar
unknown committed
6826
  DBUG_ENTER("ndbcluster_real_free_share");
unknown's avatar
unknown committed
6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838 6839
  DBUG_PRINT("real_free_share",
             ("0x%lx key: %s  key_length: %d",
              (*share), (*share)->key, (*share)->key_length));
  DBUG_PRINT("real_free_share",
             ("db.tablename: %s.%s  use_count: %d  commit_count: %d",
              (*share)->db, (*share)->table_name,
              (*share)->use_count, (*share)->commit_count));

  hash_delete(&ndbcluster_open_tables, (byte*) *share);
  thr_lock_delete(&(*share)->lock);
  pthread_mutex_destroy(&(*share)->mutex);
  free_root(&(*share)->mem_root, MYF(0));

unknown's avatar
unknown committed
6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859
#ifdef HAVE_NDB_BINLOG
  if ((*share)->table)
  {
    closefrm((*share)->table, 0);
#if 0 // todo ?
    free_root(&(*share)->table->mem_root, MYF(0));
#endif

#ifndef DBUG_OFF
    bzero((gptr)(*share)->table_share, sizeof(*(*share)->table_share));
    bzero((gptr)(*share)->table, sizeof(*(*share)->table));
#endif
    my_free((gptr) (*share)->table_share, MYF(0));
    my_free((gptr) (*share)->table, MYF(0));
#ifndef DBUG_OFF
    (*share)->table_share= 0;
    (*share)->table= 0;
#endif
  }
#endif
unknown's avatar
unknown committed
6860 6861 6862 6863
  my_free((gptr) *share, MYF(0));
  *share= 0;

  dbug_print_open_tables();
unknown's avatar
unknown committed
6864
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
6865 6866 6867 6868 6869
}

/*
  decrease refcount of share
  calls real_free_share when refcount reaches 0
unknown's avatar
unknown committed
6870

unknown's avatar
unknown committed
6871 6872
  have_lock == TRUE, pthread_mutex_lock(&ndbcluster_mutex) already taken
*/
unknown's avatar
unknown committed
6873
void ndbcluster_free_share(NDB_SHARE **share, bool have_lock)
unknown's avatar
unknown committed
6874
{
unknown's avatar
unknown committed
6875 6876 6877 6878 6879
  if (!have_lock)
    pthread_mutex_lock(&ndbcluster_mutex);
  if ((*share)->util_lock == current_thd)
    (*share)->util_lock= 0;
  if (!--(*share)->use_count)
unknown's avatar
unknown committed
6880
  {
unknown's avatar
unknown committed
6881
    real_free_share(share);
unknown's avatar
unknown committed
6882
  }
unknown's avatar
unknown committed
6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895
  else
  {
    dbug_print_open_tables();
    DBUG_PRINT("free_share",
               ("0x%lx key: %s  key_length: %d",
                *share, (*share)->key, (*share)->key_length));
    DBUG_PRINT("free_share",
               ("db.tablename: %s.%s  use_count: %d  commit_count: %d",
                (*share)->db, (*share)->table_name,
                (*share)->use_count, (*share)->commit_count));
  }
  if (!have_lock)
    pthread_mutex_unlock(&ndbcluster_mutex);
unknown's avatar
unknown committed
6896 6897 6898
}


unknown's avatar
unknown committed
6899 6900
static 
int
6901
ndb_get_table_statistics(Ndb* ndb, const char * table,
6902
                         struct Ndb_statistics * ndbstat)
unknown's avatar
unknown committed
6903 6904 6905
{
  DBUG_ENTER("ndb_get_table_statistics");
  DBUG_PRINT("enter", ("table: %s", table));
6906
  NdbTransaction* pTrans= ndb->startTransaction();
unknown's avatar
unknown committed
6907 6908
  if (pTrans == NULL)
    ERR_RETURN(ndb->getNdbError());
unknown's avatar
unknown committed
6909 6910 6911 6912 6913 6914
  do 
  {
    NdbScanOperation* pOp= pTrans->getNdbScanOperation(table);
    if (pOp == NULL)
      break;
    
6915
    if (pOp->readTuples(NdbOperation::LM_CommittedRead))
unknown's avatar
unknown committed
6916 6917 6918 6919 6920 6921
      break;
    
    int check= pOp->interpret_exit_last_row();
    if (check == -1)
      break;
    
unknown's avatar
unknown committed
6922 6923
    Uint64 rows, commits, mem;
    Uint32 size;
unknown's avatar
unknown committed
6924 6925
    pOp->getValue(NdbDictionary::Column::ROW_COUNT, (char*)&rows);
    pOp->getValue(NdbDictionary::Column::COMMIT_COUNT, (char*)&commits);
6926 6927
    pOp->getValue(NdbDictionary::Column::ROW_SIZE, (char*)&size);
    pOp->getValue(NdbDictionary::Column::FRAGMENT_MEMORY, (char*)&mem);
unknown's avatar
unknown committed
6928
    
6929
    check= pTrans->execute(NdbTransaction::NoCommit,
6930 6931
                           NdbTransaction::AbortOnError,
                           TRUE);
unknown's avatar
unknown committed
6932 6933 6934
    if (check == -1)
      break;
    
6935
    Uint32 count= 0;
unknown's avatar
unknown committed
6936 6937
    Uint64 sum_rows= 0;
    Uint64 sum_commits= 0;
6938 6939
    Uint64 sum_row_size= 0;
    Uint64 sum_mem= 0;
unknown's avatar
unknown committed
6940
    while ((check= pOp->nextResult(TRUE, TRUE)) == 0)
unknown's avatar
unknown committed
6941 6942 6943
    {
      sum_rows+= rows;
      sum_commits+= commits;
6944
      if (sum_row_size < size)
6945
        sum_row_size= size;
6946
      sum_mem+= mem;
6947
      count++;
unknown's avatar
unknown committed
6948 6949 6950 6951 6952
    }
    
    if (check == -1)
      break;

6953
    pOp->close(TRUE);
unknown's avatar
unknown committed
6954

unknown's avatar
unknown committed
6955
    ndb->closeTransaction(pTrans);
6956 6957 6958 6959 6960 6961

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

6962 6963 6964 6965 6966
    DBUG_PRINT("exit", ("records: %llu commits: %llu "
                        "row_size: %llu mem: %llu count: %u",
			sum_rows, sum_commits, sum_row_size,
                        sum_mem, count));

unknown's avatar
unknown committed
6967
    DBUG_RETURN(0);
unknown's avatar
unknown committed
6968
  } while (0);
unknown's avatar
unknown committed
6969

unknown's avatar
unknown committed
6970 6971
  if (pTrans)
    ndb->closeTransaction(pTrans);
unknown's avatar
unknown committed
6972 6973 6974 6975
  DBUG_PRINT("exit", ("failed"));
  DBUG_RETURN(-1);
}

6976 6977 6978 6979 6980
/*
  Create a .ndb file to serve as a placeholder indicating 
  that the table with this name is a ndb table
*/

unknown's avatar
unknown committed
6981
int ha_ndbcluster::write_ndb_file(const char *name)
6982 6983 6984 6985 6986 6987
{
  File file;
  bool error=1;
  char path[FN_REFLEN];
  
  DBUG_ENTER("write_ndb_file");
unknown's avatar
unknown committed
6988
  DBUG_PRINT("enter", ("name: %s", name));
6989

unknown's avatar
unknown committed
6990
  (void)strxnmov(path, FN_REFLEN-1, 
unknown's avatar
unknown committed
6991
                 mysql_data_home,"/",name,ha_ndb_ext,NullS);
6992 6993 6994 6995 6996 6997 6998 6999 7000 7001

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

7002
int
7003
ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
7004 7005 7006 7007
                                      KEY_MULTI_RANGE *ranges, 
                                      uint range_count,
                                      bool sorted, 
                                      HANDLER_BUFFER *buffer)
7008 7009
{
  DBUG_ENTER("ha_ndbcluster::read_multi_range_first");
7010
  m_write_op= FALSE;
unknown's avatar
unknown committed
7011
  
7012 7013
  int res;
  KEY* key_info= table->key_info + active_index;
7014
  NDB_INDEX_TYPE index_type= get_index_type(active_index);
unknown's avatar
unknown committed
7015
  ulong reclength= table_share->reclength;
7016 7017
  NdbOperation* op;

7018
  if (uses_blob_value())
unknown's avatar
unknown committed
7019 7020 7021 7022
  {
    /**
     * blobs can't be batched currently
     */
unknown's avatar
unknown committed
7023
    m_disable_multi_read= TRUE;
unknown's avatar
unknown committed
7024
    DBUG_RETURN(handler::read_multi_range_first(found_range_p, 
7025 7026 7027 7028
                                                ranges, 
                                                range_count,
                                                sorted, 
                                                buffer));
unknown's avatar
unknown committed
7029 7030
  }

unknown's avatar
unknown committed
7031
  m_disable_multi_read= FALSE;
7032 7033 7034 7035

  /**
   * Copy arguments into member variables
   */
7036 7037 7038
  m_multi_ranges= ranges;
  multi_range_curr= ranges;
  multi_range_end= ranges+range_count;
7039 7040 7041
  multi_range_sorted= sorted;
  multi_range_buffer= buffer;

7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052
  /**
   * 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
7053 7054
   */   

unknown's avatar
unknown committed
7055
  /**
7056 7057
   * Variables for loop
   */
unknown's avatar
unknown committed
7058 7059
  byte *curr= (byte*)buffer->buffer;
  byte *end_of_buffer= (byte*)buffer->buffer_end;
7060 7061 7062 7063
  NdbOperation::LockMode lm= 
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
  const NDBTAB *tab= (const NDBTAB *) m_table;
  const NDBINDEX *unique_idx= (NDBINDEX *) m_index[active_index].unique_index;
7064
  const NDBINDEX *idx= (NDBINDEX *) m_index[active_index].index; 
7065 7066
  const NdbOperation* lastOp= m_active_trans->getLastDefinedOperation();
  NdbIndexScanOperation* scanOp= 0;
unknown's avatar
unknown committed
7067 7068
  for (; multi_range_curr<multi_range_end && curr+reclength <= end_of_buffer; 
       multi_range_curr++)
7069
  {
7070 7071 7072 7073 7074 7075
    part_id_range part_spec;
    if (m_use_partition_function)
    {
      get_partition_set(table, curr, active_index,
                        &multi_range_curr->start_key,
                        &part_spec);
7076 7077 7078 7079 7080 7081
      DBUG_PRINT("info", ("part_spec.start_part = %u, part_spec.end_part = %u",
                          part_spec.start_part, part_spec.end_part));
      /*
        If partition pruning has found no partition in set
        we can skip this scan
      */
7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093
      if (part_spec.start_part > part_spec.end_part)
      {
        /*
          We can skip this partition since the key won't fit into any
          partition
        */
        curr += reclength;
        multi_range_curr->range_flag |= SKIP_RANGE;
        continue;
      }
    }
    switch(index_type){
unknown's avatar
unknown committed
7094 7095
    case PRIMARY_KEY_ORDERED_INDEX:
      if (!(multi_range_curr->start_key.length == key_info->key_length &&
7096 7097 7098
          multi_range_curr->start_key.flag == HA_READ_KEY_EXACT))
        goto range;
      // else fall through
7099
    case PRIMARY_KEY_INDEX:
7100
    {
7101
      multi_range_curr->range_flag |= UNIQUE_RANGE;
7102
      if ((op= m_active_trans->getNdbOperation(tab)) && 
7103 7104 7105
          !op->readTuple(lm) && 
          !set_primary_key(op, multi_range_curr->start_key.key) &&
          !define_read_attrs(curr, op) &&
7106 7107 7108
          (op->setAbortOption(AO_IgnoreError), TRUE) &&
          (!m_use_partition_function ||
           (op->setPartitionId(part_spec.start_part), true)))
7109
        curr += reclength;
7110
      else
7111
        ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
7112
      break;
7113 7114
    }
    break;
unknown's avatar
unknown committed
7115 7116
    case UNIQUE_ORDERED_INDEX:
      if (!(multi_range_curr->start_key.length == key_info->key_length &&
7117 7118 7119 7120 7121
          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;
      // else fall through
7122
    case UNIQUE_INDEX:
7123
    {
7124
      multi_range_curr->range_flag |= UNIQUE_RANGE;
7125
      if ((op= m_active_trans->getNdbIndexOperation(unique_idx, tab)) && 
7126 7127 7128
          !op->readTuple(lm) && 
          !set_index_key(op, key_info, multi_range_curr->start_key.key) &&
          !define_read_attrs(curr, op) &&
unknown's avatar
unknown committed
7129
          (op->setAbortOption(AO_IgnoreError), TRUE))
7130
        curr += reclength;
7131
      else
7132
        ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
7133 7134
      break;
    }
7135
    case ORDERED_INDEX: {
7136
  range:
7137
      multi_range_curr->range_flag &= ~(uint)UNIQUE_RANGE;
7138 7139
      if (scanOp == 0)
      {
7140 7141 7142 7143 7144 7145
        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
7146
          if (scanOp->reset_bounds(m_force_send))
7147 7148 7149 7150 7151
            DBUG_RETURN(ndb_err(m_active_trans));
          
          end_of_buffer -= reclength;
        }
        else if ((scanOp= m_active_trans->getNdbIndexScanOperation(idx, tab)) 
unknown's avatar
unknown committed
7152
                 &&!scanOp->readTuples(lm, 0, parallelism, sorted, FALSE, TRUE)
7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163
                 &&!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());
        }
7164
      }
7165

7166
      const key_range *keys[2]= { &multi_range_curr->start_key, 
7167
                                  &multi_range_curr->end_key };
7168 7169
      if ((res= set_bounds(scanOp, active_index, false, keys,
                           multi_range_curr-ranges)))
7170
        DBUG_RETURN(res);
7171
      break;
7172
    }
unknown's avatar
unknown committed
7173
    case UNDEFINED_INDEX:
unknown's avatar
unknown committed
7174 7175 7176 7177
      DBUG_ASSERT(FALSE);
      DBUG_RETURN(1);
      break;
    }
7178 7179
  }
  
7180
  if (multi_range_curr != multi_range_end)
7181
  {
7182 7183 7184 7185 7186 7187
    /**
     * 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
     */
7188
    buffer->end_of_used_area= (byte*)buffer->buffer_end;
7189 7190 7191 7192 7193 7194 7195 7196 7197 7198 7199
  }
  else
  {
    buffer->end_of_used_area= curr;
  }
  
  /**
   * Set first operation in multi range
   */
  m_current_multi_operation= 
    lastOp ? lastOp->next() : m_active_trans->getFirstDefinedOperation();
7200
  if (!(res= execute_no_commit_ie(this, m_active_trans)))
7201
  {
7202 7203
    m_multi_range_defined= multi_range_curr;
    multi_range_curr= ranges;
7204 7205
    m_multi_range_result_ptr= (byte*)buffer->buffer;
    DBUG_RETURN(read_multi_range_next(found_range_p));
7206 7207 7208 7209
  }
  ERR_RETURN(m_active_trans->getNdbError());
}

unknown's avatar
unknown committed
7210
#if 0
7211
#define DBUG_MULTI_RANGE(x) DBUG_PRINT("info", ("read_multi_range_next: case %d\n", x));
unknown's avatar
unknown committed
7212 7213 7214 7215
#else
#define DBUG_MULTI_RANGE(x)
#endif

7216
int
7217
ha_ndbcluster::read_multi_range_next(KEY_MULTI_RANGE ** multi_range_found_p)
7218 7219
{
  DBUG_ENTER("ha_ndbcluster::read_multi_range_next");
7220
  if (m_disable_multi_read)
7221
  {
7222
    DBUG_MULTI_RANGE(11);
7223
    DBUG_RETURN(handler::read_multi_range_next(multi_range_found_p));
7224
  }
7225
  
7226
  int res;
7227
  int range_no;
unknown's avatar
unknown committed
7228
  ulong reclength= table_share->reclength;
7229
  const NdbOperation* op= m_current_multi_operation;
unknown's avatar
unknown committed
7230
  for (;multi_range_curr < m_multi_range_defined; multi_range_curr++)
7231
  {
7232 7233 7234
    DBUG_MULTI_RANGE(12);
    if (multi_range_curr->range_flag & SKIP_RANGE)
      continue;
7235
    if (multi_range_curr->range_flag & UNIQUE_RANGE)
7236
    {
7237
      if (op->getNdbError().code == 0)
7238 7239
      {
        DBUG_MULTI_RANGE(13);
7240
        goto found_next;
7241
      }
7242 7243 7244
      
      op= m_active_trans->getNextCompletedOperation(op);
      m_multi_range_result_ptr += reclength;
7245
      continue;
7246
    } 
7247
    else if (m_multi_cursor && !multi_range_sorted)
7248
    {
unknown's avatar
unknown committed
7249 7250
      DBUG_MULTI_RANGE(1);
      if ((res= fetch_next(m_multi_cursor)) == 0)
7251
      {
7252 7253 7254
        DBUG_MULTI_RANGE(2);
        range_no= m_multi_cursor->get_range_no();
        goto found;
7255 7256 7257
      } 
      else
      {
7258
        DBUG_MULTI_RANGE(14);
7259
        goto close_scan;
7260 7261
      }
    }
unknown's avatar
unknown committed
7262
    else if (m_multi_cursor && multi_range_sorted)
7263
    {
unknown's avatar
unknown committed
7264 7265
      if (m_active_cursor && (res= fetch_next(m_multi_cursor)))
      {
7266 7267
        DBUG_MULTI_RANGE(3);
        goto close_scan;
unknown's avatar
unknown committed
7268
      }
7269
      
7270
      range_no= m_multi_cursor->get_range_no();
7271
      uint current_range_no= multi_range_curr - m_multi_ranges;
unknown's avatar
unknown committed
7272
      if ((uint) range_no == current_range_no)
7273
      {
7274
        DBUG_MULTI_RANGE(4);
7275
        // return current row
7276
        goto found;
7277
      }
7278
      else if (range_no > (int)current_range_no)
7279
      {
7280 7281 7282 7283
        DBUG_MULTI_RANGE(5);
        // wait with current row
        m_active_cursor= 0;
        continue;
7284 7285 7286
      }
      else 
      {
7287 7288 7289
        DBUG_MULTI_RANGE(6);
        // First fetch from cursor
        DBUG_ASSERT(range_no == -1);
unknown's avatar
unknown committed
7290
        if ((res= m_multi_cursor->nextResult(true)))
7291
        {
7292
          DBUG_MULTI_RANGE(15);
7293 7294 7295 7296
          goto close_scan;
        }
        multi_range_curr--; // Will be increased in for-loop
        continue;
7297
      }
7298
    }
unknown's avatar
unknown committed
7299
    else /** m_multi_cursor == 0 */
7300
    {
unknown's avatar
unknown committed
7301
      DBUG_MULTI_RANGE(7);
7302 7303 7304 7305
      /**
       * Corresponds to range 5 in example in read_multi_range_first
       */
      (void)1;
7306
      continue;
7307
    }
7308
    
unknown's avatar
unknown committed
7309
    DBUG_ASSERT(FALSE); // Should only get here via goto's
7310 7311 7312
close_scan:
    if (res == 1)
    {
unknown's avatar
unknown committed
7313
      m_multi_cursor->close(FALSE, TRUE);
7314
      m_active_cursor= m_multi_cursor= 0;
unknown's avatar
unknown committed
7315
      DBUG_MULTI_RANGE(8);
7316 7317 7318 7319
      continue;
    } 
    else 
    {
7320
      DBUG_MULTI_RANGE(9);
7321 7322 7323
      DBUG_RETURN(ndb_err(m_active_trans));
    }
  }
7324
  
7325
  if (multi_range_curr == multi_range_end)
7326 7327
  {
    DBUG_MULTI_RANGE(16);
7328
    DBUG_RETURN(HA_ERR_END_OF_FILE);
7329
  }
7330
  
7331 7332 7333 7334
  /**
   * Read remaining ranges
   */
  DBUG_RETURN(read_multi_range_first(multi_range_found_p, 
7335 7336 7337 7338
                                     multi_range_curr,
                                     multi_range_end - multi_range_curr, 
                                     multi_range_sorted,
                                     multi_range_buffer));
7339 7340
  
found:
7341 7342 7343
  /**
   * Found a record belonging to a scan
   */
7344
  m_active_cursor= m_multi_cursor;
7345
  * multi_range_found_p= m_multi_ranges + range_no;
7346 7347
  memcpy(table->record[0], m_multi_range_cursor_result_ptr, reclength);
  setup_recattr(m_active_cursor->getFirstRecAttr());
7348 7349 7350
  unpack_record(table->record[0]);
  table->status= 0;     
  DBUG_RETURN(0);
7351
  
7352
found_next:
7353 7354 7355 7356
  /**
   * Found a record belonging to a pk/index op,
   *   copy result and move to next to prepare for next call
   */
7357
  * multi_range_found_p= multi_range_curr;
7358
  memcpy(table->record[0], m_multi_range_result_ptr, reclength);
7359
  setup_recattr(op->getFirstRecAttr());
7360
  unpack_record(table->record[0]);
7361 7362
  table->status= 0;
  
7363
  multi_range_curr++;
7364
  m_current_multi_operation= m_active_trans->getNextCompletedOperation(op);
7365 7366
  m_multi_range_result_ptr += reclength;
  DBUG_RETURN(0);
7367 7368
}

7369 7370 7371 7372 7373 7374 7375 7376
int
ha_ndbcluster::setup_recattr(const NdbRecAttr* curr)
{
  DBUG_ENTER("setup_recattr");

  Field **field, **end;
  NdbValue *value= m_value;
  
unknown's avatar
unknown committed
7377
  end= table->field + table_share->fields;
7378 7379 7380 7381 7382 7383
  
  for (field= table->field; field < end; field++, value++)
  {
    if ((* value).ptr)
    {
      DBUG_ASSERT(curr != 0);
7384 7385
      (* value).rec= curr;
      curr= curr->next();
7386 7387 7388
    }
  }
  
unknown's avatar
unknown committed
7389
  DBUG_RETURN(0);
7390 7391
}

unknown's avatar
Merge  
unknown committed
7392 7393
char*
ha_ndbcluster::update_table_comment(
7394 7395
                                /* out: table comment + additional */
        const char*     comment)/* in:  table comment defined by user */
unknown's avatar
Merge  
unknown committed
7396 7397
{
  uint length= strlen(comment);
unknown's avatar
unknown committed
7398
  if (length > 64000 - 3)
unknown's avatar
Merge  
unknown committed
7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421 7422 7423 7424
  {
    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
7425 7426 7427
  my_snprintf(str,fmt_len_plus_extra,fmt,comment,
              length > 0 ? " ":"",
              tab->getReplicaCount());
unknown's avatar
Merge  
unknown committed
7428 7429 7430 7431 7432
  return str;
}


// Utility thread main loop
7433
pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
unknown's avatar
Merge  
unknown committed
7434 7435
{
  THD *thd; /* needs to be first for thread_stack */
7436
  Ndb* ndb;
unknown's avatar
Merge  
unknown committed
7437
  struct timespec abstime;
unknown's avatar
unknown committed
7438
  List<NDB_SHARE> util_open_tables;
unknown's avatar
Merge  
unknown committed
7439 7440 7441 7442 7443 7444 7445

  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);
7446
  ndb= new Ndb(g_ndb_cluster_connection, "");
unknown's avatar
Merge  
unknown committed
7447 7448 7449 7450 7451

  pthread_detach_this_thread();
  ndb_util_thread= pthread_self();

  thd->thread_stack= (char*)&thd; /* remember where our stack is */
7452
  if (thd->store_globals() && (ndb->init() != -1))
unknown's avatar
Merge  
unknown committed
7453 7454 7455
  {
    thd->cleanup();
    delete thd;
7456
    delete ndb;
unknown's avatar
Merge  
unknown committed
7457 7458
    DBUG_RETURN(NULL);
  }
unknown's avatar
unknown committed
7459 7460 7461 7462 7463 7464 7465 7466 7467 7468 7469 7470 7471 7472 7473 7474 7475 7476 7477 7478 7479
  thd->init_for_queries();
  thd->version=refresh_version;
  thd->set_time();
  thd->main_security_ctx.host_or_ip= "";
  thd->client_capabilities = 0;
  my_net_init(&thd->net, 0);
  thd->main_security_ctx.master_access= ~0;
  thd->main_security_ctx.priv_user = 0;

  /*
    wait for mysql server to start
  */
  pthread_mutex_lock(&LOCK_server_started);
  while (!mysqld_server_started)
    pthread_cond_wait(&COND_server_started, &LOCK_server_started);
  pthread_mutex_unlock(&LOCK_server_started);

  /*
    Wait for cluster to start
  */
  pthread_mutex_lock(&LOCK_ndb_util_thread);
unknown's avatar
unknown committed
7480
  while (!ndb_cluster_node_id && (ndbcluster_hton.slot != ~(uint)0))
unknown's avatar
unknown committed
7481 7482 7483 7484 7485 7486 7487 7488 7489 7490 7491 7492 7493 7494
  {
    /* ndb not connected yet */
    set_timespec(abstime, 1);
    pthread_cond_timedwait(&COND_ndb_util_thread,
                           &LOCK_ndb_util_thread,
                           &abstime);
    if (abort_loop)
    {
      pthread_mutex_unlock(&LOCK_ndb_util_thread);
      goto ndb_util_thread_end;
    }
  }
  pthread_mutex_unlock(&LOCK_ndb_util_thread);

unknown's avatar
unknown committed
7495 7496 7497 7498 7499 7500 7501 7502 7503 7504 7505 7506 7507 7508 7509
  {
    Thd_ndb *thd_ndb;
    if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
    {
      sql_print_error("Could not allocate Thd_ndb object");
      goto ndb_util_thread_end;
    }
    set_thd_ndb(thd, thd_ndb);
    thd_ndb->options|= TNO_NO_LOG_SCHEMA_OP;
  }

#ifdef HAVE_NDB_BINLOG
  /* create tables needed by the replication */
  ndbcluster_setup_binlog_table_shares(thd);
#else
unknown's avatar
unknown committed
7510 7511 7512 7513
  /*
    Get all table definitions from the storage node
  */
  ndbcluster_find_all_files(thd);
unknown's avatar
unknown committed
7514
#endif
unknown's avatar
unknown committed
7515 7516

  ndbcluster_util_inited= 1;
unknown's avatar
Merge  
unknown committed
7517

unknown's avatar
unknown committed
7518
#ifdef HAVE_NDB_BINLOG
7519 7520
  /* Signal injector thread that all is setup */
  pthread_cond_signal(&injector_cond);
unknown's avatar
unknown committed
7521 7522
#endif

7523
  set_timespec(abstime, 0);
unknown's avatar
unknown committed
7524
  for (;!abort_loop;)
unknown's avatar
Merge  
unknown committed
7525 7526 7527
  {

    pthread_mutex_lock(&LOCK_ndb_util_thread);
unknown's avatar
unknown committed
7528 7529 7530
    pthread_cond_timedwait(&COND_ndb_util_thread,
                           &LOCK_ndb_util_thread,
                           &abstime);
unknown's avatar
Merge  
unknown committed
7531
    pthread_mutex_unlock(&LOCK_ndb_util_thread);
unknown's avatar
unknown committed
7532
#ifdef NDB_EXTRA_DEBUG_UTIL_THREAD
unknown's avatar
Merge  
unknown committed
7533 7534
    DBUG_PRINT("ndb_util_thread", ("Started, ndb_cache_check_time: %d",
                                   ndb_cache_check_time));
unknown's avatar
unknown committed
7535
#endif
unknown's avatar
Merge  
unknown committed
7536 7537 7538
    if (abort_loop)
      break; /* Shutting down server */

unknown's avatar
unknown committed
7539 7540 7541 7542 7543 7544 7545 7546 7547
#ifdef HAVE_NDB_BINLOG
    /*
      Check that the apply_status_share and schema_share has been created.
      If not try to create it
    */
    if (!apply_status_share || !schema_share)
      ndbcluster_setup_binlog_table_shares(thd);
#endif

unknown's avatar
Merge  
unknown committed
7548 7549
    if (ndb_cache_check_time == 0)
    {
7550 7551
      /* Wake up in 1 second to check if value has changed */
      set_timespec(abstime, 1);
unknown's avatar
Merge  
unknown committed
7552 7553 7554 7555 7556 7557 7558 7559 7560
      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);
unknown's avatar
unknown committed
7561 7562 7563 7564 7565 7566
#ifdef HAVE_NDB_BINLOG
      if ((share->use_count - (int) (share->op != 0) - (int) (share->op != 0))
          <= 0)
        continue; // injector thread is the only user, skip statistics
      share->util_lock= current_thd; // Mark that util thread has lock
#endif /* HAVE_NDB_BINLOG */
unknown's avatar
Merge  
unknown committed
7567 7568 7569 7570 7571 7572 7573 7574 7575 7576 7577 7578
      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);
7579
    while ((share= it++))
unknown's avatar
Merge  
unknown committed
7580
    {
unknown's avatar
unknown committed
7581 7582 7583 7584 7585 7586 7587 7588 7589 7590 7591
#ifdef HAVE_NDB_BINLOG
      if ((share->use_count - (int) (share->op != 0) - (int) (share->op != 0))
          <= 1)
      {
        /*
          Util thread and injector thread is the only user, skip statistics
	*/
        free_share(&share);
        continue;
      }
#endif /* HAVE_NDB_BINLOG */
unknown's avatar
Merge  
unknown committed
7592
      DBUG_PRINT("ndb_util_thread",
7593
                 ("Fetching commit count for: %s",
unknown's avatar
unknown committed
7594
                  share->key));
unknown's avatar
Merge  
unknown committed
7595 7596

      /* Contact NDB to get commit count for table */
unknown's avatar
unknown committed
7597
      ndb->setDatabaseName(share->db);
7598 7599 7600 7601 7602 7603 7604
      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
7605
      if (ndb_get_table_statistics(ndb, share->table_name, &stat) == 0)
unknown's avatar
Merge  
unknown committed
7606 7607
      {
        DBUG_PRINT("ndb_util_thread",
7608
                   ("Table: %s, commit_count: %llu, rows: %llu",
unknown's avatar
unknown committed
7609
                    share->key, stat.commit_count, stat.row_count));
unknown's avatar
Merge  
unknown committed
7610 7611 7612 7613 7614
      }
      else
      {
        DBUG_PRINT("ndb_util_thread",
                   ("Error: Could not get commit count for table %s",
unknown's avatar
unknown committed
7615
                    share->key));
7616
        stat.commit_count= 0;
unknown's avatar
Merge  
unknown committed
7617
      }
7618 7619 7620 7621 7622 7623

      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
7624
      /* Decrease the use count and possibly free share */
unknown's avatar
unknown committed
7625
      free_share(&share);
unknown's avatar
Merge  
unknown committed
7626 7627 7628 7629 7630
    }

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

7631 7632 7633 7634 7635 7636 7637 7638 7639
    /* 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
7640
    if (msecs >= 1000){
7641 7642 7643 7644 7645 7646 7647 7648 7649 7650
      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
7651
  }
unknown's avatar
unknown committed
7652
ndb_util_thread_end:
unknown's avatar
unknown committed
7653
  sql_print_information("Stopping Cluster Utility thread");
unknown's avatar
unknown committed
7654
  net_end(&thd->net);
unknown's avatar
Merge  
unknown committed
7655 7656
  thd->cleanup();
  delete thd;
7657
  delete ndb;
unknown's avatar
Merge  
unknown committed
7658 7659 7660 7661 7662 7663
  DBUG_PRINT("exit", ("ndb_util_thread"));
  my_thread_end();
  pthread_exit(0);
  DBUG_RETURN(NULL);
}

7664 7665 7666
/*
  Condition pushdown
*/
7667 7668 7669 7670 7671 7672 7673 7674 7675 7676 7677 7678 7679 7680 7681 7682 7683
/*
  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)
*/
7684 7685 7686 7687 7688
const 
COND* 
ha_ndbcluster::cond_push(const COND *cond) 
{ 
  DBUG_ENTER("cond_push");
7689 7690 7691
  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
7692
    ndb_cond->next= m_cond_stack;
7693 7694 7695 7696 7697 7698
  else
    ndb_cond->next= NULL;
  m_cond_stack= ndb_cond;
  
  if (serialize_cond(cond, ndb_cond))
  {
unknown's avatar
unknown committed
7699
    DBUG_RETURN(NULL);
7700 7701 7702 7703
  }
  else
  {
    cond_pop();
unknown's avatar
unknown committed
7704
  }
7705 7706 7707
  DBUG_RETURN(cond); 
}

7708 7709 7710
/*
  Pop the top condition from the condition stack of the handler instance.
*/
7711 7712 7713 7714 7715 7716 7717 7718 7719
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
7720
}
7721

7722 7723 7724
/*
  Clear the condition stack
*/
7725 7726 7727 7728 7729 7730 7731 7732 7733 7734
void
ha_ndbcluster::cond_clear()
{
  DBUG_ENTER("cond_clear");
  while (m_cond_stack)
    cond_pop();

  DBUG_VOID_RETURN;
}

7735 7736 7737 7738 7739 7740
/*
  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.
*/
7741 7742 7743 7744 7745
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
7746 7747 7748 7749 7750
  // 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
7751 7752 7753
    switch (item->type()) {
    case Item::FUNC_ITEM:
    {
unknown's avatar
unknown committed
7754 7755 7756 7757
      Item_func *func_item= (Item_func *) item;
      context->skip+= func_item->argument_count();
      break;
    }
unknown's avatar
unknown committed
7758 7759 7760 7761 7762
    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
7763 7764
      break;
    default:
7765
      context->supported= FALSE;
unknown's avatar
unknown committed
7766 7767
      break;
    }
7768
    
unknown's avatar
unknown committed
7769 7770 7771
    DBUG_VOID_RETURN;
  }
  
7772
  if (context->supported)
7773
  {
7774 7775 7776 7777 7778 7779
    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
7780
    {
unknown's avatar
unknown committed
7781 7782
      switch (func_item->functype()) {
      case Item_func::BETWEEN:
7783
        /*
7784 7785 7786 7787 7788 7789 7790
          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()
7791
        */
unknown's avatar
unknown committed
7792 7793
      case Item_func::IN_FUNC:
      {
7794 7795 7796 7797 7798 7799 7800 7801 7802 7803 7804 7805 7806 7807 7808 7809
        /*
          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)
7810
          {
7811 7812 7813
            Item_func *func_item= (Item_func *) item;
            if (func_item->functype() == Item_func::UNKNOWN_FUNC &&
                func_item->const_item())
7814
            {
7815 7816 7817
              // Skip any arguments since we will evaluate function instead
              DBUG_PRINT("info", ("Skip until end of arguments marker"));
              context->skip= func_item->argument_count();
7818 7819 7820
            }
            else
            {
7821 7822 7823 7824
              DBUG_PRINT("info", ("Found unsupported functional expression in BETWEEN|IN"));
              context->supported= FALSE;
              DBUG_VOID_RETURN;
              
7825 7826 7827
            }
          }
        }
7828 7829
        else
        {
7830 7831 7832
          // Non-supported BETWEEN|IN expression
          DBUG_PRINT("info", ("Found unexpected item of type %u in BETWEEN|IN",
                              item->type()));
7833
          context->supported= FALSE;
7834
          DBUG_VOID_RETURN;
7835
        }
7836 7837 7838 7839 7840 7841 7842 7843 7844 7845 7846 7847 7848 7849 7850 7851 7852 7853 7854 7855 7856 7857
        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
7858 7859 7860 7861 7862 7863 7864 7865 7866 7867 7868 7869
        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()
          */
7870 7871 7872 7873 7874 7875 7876 7877 7878 7879 7880 7881 7882 7883 7884 7885 7886 7887 7888
          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;
          }
7889 7890
          break;
        }
unknown's avatar
unknown committed
7891 7892
        case Item_func::IN_FUNC:
        {
7893 7894 7895 7896 7897 7898 7899 7900 7901 7902 7903 7904 7905
          /*
            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);
7906 7907
          break;
        }
7908 7909
        default:
          context->supported= FALSE;
7910
        }
7911 7912 7913 7914 7915 7916 7917 7918 7919 7920 7921 7922 7923 7924 7925 7926 7927 7928 7929 7930 7931 7932 7933 7934 7935 7936 7937 7938 7939 7940
        // 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
7941 7942 7943 7944
      {
        switch (item->type()) {
        case Item::FIELD_ITEM:
        {
7945 7946 7947 7948 7949 7950 7951 7952 7953 7954 7955 7956 7957 7958 7959 7960 7961 7962 7963
          Item_field *field_item= (Item_field *) item;
          Field *field= field_item->field;
          enum_field_types type= field->type();
          /*
            Check that the field is part of the table of the handler
            instance and that we expect a field with of this result type.
          */
          if (context->table == field->table)
          {       
            const NDBTAB *tab= (const NDBTAB *) context->ndb_table;
            DBUG_PRINT("info", ("FIELD_ITEM"));
            DBUG_PRINT("info", ("table %s", tab->getName()));
            DBUG_PRINT("info", ("column %s", field->field_name));
            DBUG_PRINT("info", ("result type %d", field->result_type()));
            
            // Check that we are expecting a field and with the correct
            // result type
            if (context->expecting(Item::FIELD_ITEM) &&
                (context->expecting_field_result(field->result_type()) ||
unknown's avatar
unknown committed
7964
                 // Date and year can be written as string or int
7965 7966 7967 7968
                 ((type == MYSQL_TYPE_TIME ||
                   type == MYSQL_TYPE_DATE || 
                   type == MYSQL_TYPE_YEAR ||
                   type == MYSQL_TYPE_DATETIME)
unknown's avatar
unknown committed
7969 7970 7971
                  ? (context->expecting_field_result(STRING_RESULT) ||
                     context->expecting_field_result(INT_RESULT))
                  : true)) &&
7972 7973 7974 7975 7976 7977 7978 7979 7980
                // Bit fields no yet supported in scan filter
                type != MYSQL_TYPE_BIT)
            {
              const NDBCOL *col= tab->getColumn(field->field_name);
              DBUG_ASSERT(col);
              curr_cond->ndb_item= new Ndb_item(field, col->getColumnNo());
              context->dont_expect(Item::FIELD_ITEM);
              context->expect_no_field_result();
              if (context->expect_mask)
7981
              {
7982 7983 7984 7985 7986 7987 7988 7989 7990 7991
                // 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
7992 7993
                  switch (field->result_type()) {
                  case STRING_RESULT:
7994 7995 7996 7997 7998
                    // 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
7999
                  case REAL_RESULT:
8000 8001
                    context->expect_only(Item::REAL_ITEM);
                    context->expect(Item::DECIMAL_ITEM);
8002
                    context->expect(Item::INT_ITEM);
8003
                    break;
unknown's avatar
unknown committed
8004
                  case INT_RESULT:
8005 8006 8007
                    context->expect_only(Item::INT_ITEM);
                    context->expect(Item::VARBIN_ITEM);
                    break;
unknown's avatar
unknown committed
8008
                  case DECIMAL_RESULT:
8009 8010
                    context->expect_only(Item::DECIMAL_ITEM);
                    context->expect(Item::REAL_ITEM);
8011
                    context->expect(Item::INT_ITEM);
8012 8013 8014 8015
                    break;
                  default:
                    break;
                  }    
8016 8017
              }
              else
8018 8019 8020 8021
              {
                // Expect another logical expression
                context->expect_only(Item::FUNC_ITEM);
                context->expect(Item::COND_ITEM);
8022 8023 8024 8025 8026 8027 8028
                // 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)
8029
                {
unknown's avatar
unknown committed
8030
                  DBUG_PRINT("info", ("Found non-matching collation %s",  
8031 8032
                                      item->collation.collation->name)); 
                  context->supported= FALSE;                
8033 8034
                }
              }
8035 8036
              break;
            }
8037 8038
            else
            {
unknown's avatar
unknown committed
8039 8040
              DBUG_PRINT("info", ("Was not expecting field of type %u(%u)",
                                  field->result_type(), type));
8041
              context->supported= FALSE;
8042
            }
8043
          }
8044
          else
8045
          {
unknown's avatar
unknown committed
8046 8047 8048
            DBUG_PRINT("info", ("Was not expecting field from table %s (%s)",
                                context->table->s->table_name.str, 
                                field->table->s->table_name.str));
8049
            context->supported= FALSE;
8050
          }
8051 8052
          break;
        }
unknown's avatar
unknown committed
8053 8054
        case Item::FUNC_ITEM:
        {
8055 8056 8057 8058 8059 8060
          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
8061
          {
8062 8063 8064
            // Did not expect function here
            context->supported= FALSE;
            break;
8065
          }
8066
          
unknown's avatar
unknown committed
8067 8068 8069
          switch (func_item->functype()) {
          case Item_func::EQ_FUNC:
          {
8070 8071 8072 8073 8074 8075 8076 8077 8078 8079 8080 8081 8082 8083
            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;
8084
          }
unknown's avatar
unknown committed
8085 8086
          case Item_func::NE_FUNC:
          {
8087 8088 8089 8090 8091 8092 8093 8094 8095 8096 8097 8098 8099 8100
            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;
8101
          }
unknown's avatar
unknown committed
8102 8103
          case Item_func::LT_FUNC:
          {
8104 8105 8106 8107 8108 8109 8110 8111 8112 8113 8114 8115 8116 8117 8118
            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
8119 8120
          case Item_func::LE_FUNC:
          {
8121 8122 8123 8124 8125 8126 8127 8128 8129 8130 8131 8132 8133 8134 8135
            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
8136 8137
          case Item_func::GE_FUNC:
          {
8138 8139 8140 8141 8142 8143 8144 8145 8146 8147 8148 8149 8150 8151 8152
            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
8153 8154
          case Item_func::GT_FUNC:
          {
8155 8156 8157 8158 8159 8160 8161 8162 8163 8164 8165 8166 8167 8168 8169
            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
8170 8171
          case Item_func::LIKE_FUNC:
          {
8172 8173 8174 8175 8176 8177 8178 8179 8180
            DBUG_PRINT("info", ("LIKE_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect(Item::FUNC_ITEM);
            break;
          }
unknown's avatar
unknown committed
8181 8182
          case Item_func::ISNULL_FUNC:
          {
8183 8184 8185 8186 8187 8188 8189 8190 8191 8192
            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
8193 8194
          case Item_func::ISNOTNULL_FUNC:
          {
8195 8196 8197 8198 8199 8200 8201 8202 8203 8204
            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
8205 8206
          case Item_func::NOT_FUNC:
          {
8207 8208 8209 8210
            DBUG_PRINT("info", ("NOT_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);     
            context->expect(Item::FUNC_ITEM);
8211
            context->expect(Item::COND_ITEM);
8212
            break;
8213
          }
unknown's avatar
unknown committed
8214 8215
          case Item_func::BETWEEN:
          {
8216
            DBUG_PRINT("info", ("BETWEEN, rewriting using AND"));
8217
            Item_func_between *between_func= (Item_func_between *) func_item;
8218 8219 8220 8221
            Ndb_rewrite_context *rewrite_context= 
              new Ndb_rewrite_context(func_item);
            rewrite_context->next= context->rewrite_stack;
            context->rewrite_stack= rewrite_context;
8222 8223 8224 8225 8226 8227 8228 8229 8230
            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;
            }
8231
            DBUG_PRINT("info", ("COND_AND_FUNC"));
8232 8233 8234
            curr_cond->ndb_item= 
              new Ndb_item(Item_func::COND_AND_FUNC, 
                           func_item->argument_count() - 1);
8235
            context->expect_only(Item::FIELD_ITEM);
8236 8237 8238 8239 8240
            context->expect(Item::INT_ITEM);
            context->expect(Item::STRING_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FUNC_ITEM);
            break;
8241
          }
unknown's avatar
unknown committed
8242 8243
          case Item_func::IN_FUNC:
          {
8244
            DBUG_PRINT("info", ("IN_FUNC, rewriting using OR"));
8245
            Item_func_in *in_func= (Item_func_in *) func_item;
8246 8247 8248 8249
            Ndb_rewrite_context *rewrite_context= 
              new Ndb_rewrite_context(func_item);
            rewrite_context->next= context->rewrite_stack;
            context->rewrite_stack= rewrite_context;
8250 8251 8252 8253 8254 8255 8256 8257 8258
            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;
            }
8259 8260 8261 8262 8263 8264 8265 8266 8267
            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;
8268
          }
unknown's avatar
unknown committed
8269 8270
          case Item_func::UNKNOWN_FUNC:
          {
8271 8272 8273 8274
            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
8275 8276 8277 8278
            {
              switch (func_item->result_type()) {
              case STRING_RESULT:
              {
8279 8280 8281 8282 8283 8284 8285 8286 8287 8288 8289 8290 8291 8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 8305 8306
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::STRING_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item); 
                if (context->expect_field_result_mask)
                {
                  // We have not seen the field argument yet
                  context->expect_only(Item::FIELD_ITEM);
                  context->expect_only_field_result(STRING_RESULT);
                  context->expect_collation(func_item->collation.collation);
                }
                else
                {
                  // Expect another logical expression
                  context->expect_only(Item::FUNC_ITEM);
                  context->expect(Item::COND_ITEM);
                  // Check that string result have correct collation
                  if (!context->expecting_collation(item->collation.collation))
                  {
                    DBUG_PRINT("info", ("Found non-matching collation %s",  
                                        item->collation.collation->name));
                    context->supported= FALSE;
                  }
                }
                // Skip any arguments since we will evaluate function instead
                DBUG_PRINT("info", ("Skip until end of arguments marker"));
                context->skip= func_item->argument_count();
                break;
              }
unknown's avatar
unknown committed
8307 8308
              case REAL_RESULT:
              {
8309 8310 8311 8312 8313 8314 8315 8316 8317 8318 8319 8320 8321 8322 8323 8324 8325 8326 8327 8328 8329
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::REAL_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
                if (context->expect_field_result_mask) 
                {
                  // We have not seen the field argument yet
                  context->expect_only(Item::FIELD_ITEM);
                  context->expect_only_field_result(REAL_RESULT);
                }
                else
                {
                  // Expect another logical expression
                  context->expect_only(Item::FUNC_ITEM);
                  context->expect(Item::COND_ITEM);
                }
                
                // Skip any arguments since we will evaluate function instead
                DBUG_PRINT("info", ("Skip until end of arguments marker"));
                context->skip= func_item->argument_count();
                break;
              }
unknown's avatar
unknown committed
8330 8331
              case INT_RESULT:
              {
8332 8333 8334 8335 8336 8337 8338 8339 8340 8341 8342 8343 8344 8345 8346 8347 8348 8349 8350 8351 8352
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::INT_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
                if (context->expect_field_result_mask) 
                {
                  // We have not seen the field argument yet
                  context->expect_only(Item::FIELD_ITEM);
                  context->expect_only_field_result(INT_RESULT);
                }
                else
                {
                  // Expect another logical expression
                  context->expect_only(Item::FUNC_ITEM);
                  context->expect(Item::COND_ITEM);
                }
                
                // Skip any arguments since we will evaluate function instead
                DBUG_PRINT("info", ("Skip until end of arguments marker"));
                context->skip= func_item->argument_count();
                break;
              }
unknown's avatar
unknown committed
8353 8354
              case DECIMAL_RESULT:
              {
8355 8356 8357 8358 8359 8360 8361 8362 8363 8364 8365 8366 8367 8368 8369 8370 8371 8372 8373 8374 8375 8376 8377
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::DECIMAL_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
                if (context->expect_field_result_mask) 
                {
                  // We have not seen the field argument yet
                  context->expect_only(Item::FIELD_ITEM);
                  context->expect_only_field_result(DECIMAL_RESULT);
                }
                else
                {
                  // Expect another logical expression
                  context->expect_only(Item::FUNC_ITEM);
                  context->expect(Item::COND_ITEM);
                }
                // Skip any arguments since we will evaluate function instead
                DBUG_PRINT("info", ("Skip until end of arguments marker"));
                context->skip= func_item->argument_count();
                break;
              }
              default:
                break;
              }
unknown's avatar
unknown committed
8378
            }
8379 8380 8381 8382 8383
            else
              // Function does not return constant expression
              context->supported= FALSE;
            break;
          }
unknown's avatar
unknown committed
8384 8385
          default:
          {
8386 8387 8388
            DBUG_PRINT("info", ("Found func_item of type %d", 
                                func_item->functype()));
            context->supported= FALSE;
8389
          }
8390 8391
          }
          break;
8392
        }
unknown's avatar
unknown committed
8393
        case Item::STRING_ITEM:
8394 8395 8396
          DBUG_PRINT("info", ("STRING_ITEM")); 
          if (context->expecting(Item::STRING_ITEM)) 
          {
8397
#ifndef DBUG_OFF
8398 8399 8400 8401 8402 8403
            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()));
8404
#endif
8405 8406 8407 8408 8409 8410 8411 8412 8413 8414 8415 8416 8417 8418 8419 8420 8421 8422 8423 8424 8425 8426 8427 8428 8429 8430 8431
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::STRING_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);      
            if (context->expect_field_result_mask)
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(STRING_RESULT);
              context->expect_collation(item->collation.collation);
            }
            else 
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
              // Check that we are comparing with a field with same collation
              if (!context->expecting_collation(item->collation.collation))
              {
                DBUG_PRINT("info", ("Found non-matching collation %s",  
                                    item->collation.collation->name));
                context->supported= FALSE;
              }
            }
          }
          else
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
8432
        case Item::INT_ITEM:
8433 8434
          DBUG_PRINT("info", ("INT_ITEM"));
          if (context->expecting(Item::INT_ITEM)) 
8435
          {
8436 8437 8438 8439 8440 8441 8442 8443 8444 8445
            Item_int *int_item= (Item_int *) item;      
            DBUG_PRINT("info", ("value %d", int_item->value));
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::INT_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
            if (context->expect_field_result_mask) 
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(INT_RESULT);
8446 8447
              context->expect_field_result(REAL_RESULT);
              context->expect_field_result(DECIMAL_RESULT);
8448 8449 8450 8451 8452 8453 8454
            }
            else
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
            }
8455 8456
          }
          else
8457 8458
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
8459
        case Item::REAL_ITEM:
8460 8461
          DBUG_PRINT("info", ("REAL_ITEM %s"));
          if (context->expecting(Item::REAL_ITEM)) 
8462
          {
8463 8464 8465 8466 8467 8468 8469 8470 8471 8472 8473 8474 8475 8476 8477 8478 8479
            Item_float *float_item= (Item_float *) item;      
            DBUG_PRINT("info", ("value %f", float_item->value));
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::REAL_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
            if (context->expect_field_result_mask) 
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(REAL_RESULT);
            }
            else
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
            }
8480
          }
8481 8482 8483
          else
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
8484
        case Item::VARBIN_ITEM:
8485 8486
          DBUG_PRINT("info", ("VARBIN_ITEM"));
          if (context->expecting(Item::VARBIN_ITEM)) 
8487
          {
8488 8489 8490 8491 8492 8493 8494 8495 8496 8497 8498 8499 8500 8501 8502
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::VARBIN_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);      
            if (context->expect_field_result_mask)
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(STRING_RESULT);
            }
            else
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
            }
8503 8504
          }
          else
8505 8506
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
8507
        case Item::DECIMAL_ITEM:
8508 8509
          DBUG_PRINT("info", ("DECIMAL_ITEM %s"));
          if (context->expecting(Item::DECIMAL_ITEM)) 
8510
          {
8511 8512 8513 8514 8515 8516 8517 8518 8519 8520 8521 8522 8523 8524 8525 8526 8527 8528
            Item_decimal *decimal_item= (Item_decimal *) item;      
            DBUG_PRINT("info", ("value %f", decimal_item->val_real()));
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::DECIMAL_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
            if (context->expect_field_result_mask) 
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(REAL_RESULT);
              context->expect_field_result(DECIMAL_RESULT);
            }
            else
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
            }
8529
          }
8530 8531 8532
          else
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
8533 8534
        case Item::COND_ITEM:
        {
8535 8536 8537
          Item_cond *cond_item= (Item_cond *) item;
          
          if (context->expecting(Item::COND_ITEM))
unknown's avatar
unknown committed
8538 8539 8540
          {
            switch (cond_item->functype()) {
            case Item_func::COND_AND_FUNC:
8541 8542 8543 8544
              DBUG_PRINT("info", ("COND_AND_FUNC"));
              curr_cond->ndb_item= new Ndb_item(cond_item->functype(),
                                                cond_item);      
              break;
unknown's avatar
unknown committed
8545
            case Item_func::COND_OR_FUNC:
8546 8547 8548 8549 8550 8551 8552 8553 8554
              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
8555
          }
8556
          else
unknown's avatar
unknown committed
8557 8558
          {
            /* Did not expect condition */
8559
            context->supported= FALSE;          
unknown's avatar
unknown committed
8560
          }
8561
          break;
8562
        }
unknown's avatar
unknown committed
8563 8564
        default:
        {
8565
          DBUG_PRINT("info", ("Found item of type %d", item->type()));
8566
          context->supported= FALSE;
8567 8568
        }
        }
unknown's avatar
unknown committed
8569
      }
8570 8571 8572 8573 8574 8575 8576 8577 8578 8579
      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();
8580
          curr_cond->prev= prev_cond;
8581 8582 8583 8584 8585
          prev_cond->next= curr_cond;
          curr_cond->ndb_item= new Ndb_item(NDB_END_COND);
          // Pop rewrite stack
          context->rewrite_stack= context->rewrite_stack->next;
        }
8586
      }
8587
    }
8588
  }
8589
 
8590 8591 8592 8593 8594 8595 8596 8597
  DBUG_VOID_RETURN;
}

bool
ha_ndbcluster::serialize_cond(const COND *cond, Ndb_cond_stack *ndb_cond)
{
  DBUG_ENTER("serialize_cond");
  Item *item= (Item *) cond;
8598
  Ndb_cond_traverse_context context(table, (void *)m_table, ndb_cond);
8599 8600 8601
  // Expect a logical expression
  context.expect(Item::FUNC_ITEM);
  context.expect(Item::COND_ITEM);
8602
  item->traverse_cond(&ndb_serialize_cond, (void *) &context, Item::PREFIX);
8603
  DBUG_PRINT("info", ("The pushed condition is %ssupported", (context.supported)?"":"not "));
8604

8605
  DBUG_RETURN(context.supported);
8606 8607
}

8608 8609
int
ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, 
8610 8611
                                           NdbScanFilter *filter,
                                           bool negated)
8612 8613
{
  DBUG_ENTER("build_scan_filter_predicate");  
unknown's avatar
unknown committed
8614 8615 8616
  switch (cond->ndb_item->type) {
  case NDB_FUNCTION:
  {
8617 8618 8619
    if (!cond->next)
      break;
    Ndb_item *a= cond->next->ndb_item;
8620
    Ndb_item *b, *field, *value= NULL;
8621 8622
    LINT_INIT(field);

unknown's avatar
unknown committed
8623 8624
    switch (cond->ndb_item->argument_count()) {
    case 1:
8625 8626 8627
      field= 
        (a->type == NDB_FIELD)? a : NULL;
      break;
unknown's avatar
unknown committed
8628
    case 2:
8629
      if (!cond->next->next)
8630
        break;
8631 8632
      b= cond->next->next->ndb_item;
      value= 
8633 8634 8635
        (a->type == NDB_VALUE)? a
        : (b->type == NDB_VALUE)? b
        : NULL;
8636
      field= 
8637 8638 8639
        (a->type == NDB_FIELD)? a
        : (b->type == NDB_FIELD)? b
        : NULL;
8640
      break;
8641
    default:
8642 8643
      field= NULL; //Keep compiler happy
      DBUG_ASSERT(0);
8644 8645
      break;
    }
unknown's avatar
unknown committed
8646 8647 8648
    switch ((negated) ? 
            Ndb_item::negate(cond->ndb_item->qualification.function_type)
            : cond->ndb_item->qualification.function_type) {
8649
    case NDB_EQ_FUNC:
8650
    {
8651
      if (!value || !field) break;
unknown's avatar
unknown committed
8652 8653
      // Save value in right format for the field type
      value->save_in_field(field);
8654
      DBUG_PRINT("info", ("Generating EQ filter"));
8655
      if (filter->cmp(NdbScanFilter::COND_EQ, 
8656 8657 8658 8659
                      field->get_field_no(),
                      field->get_val(),
                      field->pack_length()) == -1)
        DBUG_RETURN(1);
8660 8661
      cond= cond->next->next->next;
      DBUG_RETURN(0);
8662
    }
8663
    case NDB_NE_FUNC:
unknown's avatar
unknown committed
8664
    {
8665
      if (!value || !field) break;
unknown's avatar
unknown committed
8666 8667
      // Save value in right format for the field type
      value->save_in_field(field);
8668
      DBUG_PRINT("info", ("Generating NE filter"));
8669
      if (filter->cmp(NdbScanFilter::COND_NE, 
8670 8671 8672 8673
                      field->get_field_no(),
                      field->get_val(),
                      field->pack_length()) == -1)
        DBUG_RETURN(1);
8674 8675
      cond= cond->next->next->next;
      DBUG_RETURN(0);
8676
    }
8677
    case NDB_LT_FUNC:
unknown's avatar
unknown committed
8678
    {
8679
      if (!value || !field) break;
unknown's avatar
unknown committed
8680 8681
      // Save value in right format for the field type
      value->save_in_field(field);
8682
      if (a == field)
8683
      {
8684 8685 8686 8687 8688 8689
        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);
8690
      }
8691
      else
8692
      {
8693 8694 8695 8696 8697 8698
        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);
8699
      }
8700 8701
      cond= cond->next->next->next;
      DBUG_RETURN(0);
8702
    }
8703
    case NDB_LE_FUNC:
unknown's avatar
unknown committed
8704
    {
8705
      if (!value || !field) break;
unknown's avatar
unknown committed
8706 8707
      // Save value in right format for the field type
      value->save_in_field(field);
8708
      if (a == field)
8709
      {
8710 8711 8712 8713 8714 8715
        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);       
8716
      }
8717
      else
8718
      {
8719 8720 8721 8722 8723 8724
        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);
8725
      }
8726 8727
      cond= cond->next->next->next;
      DBUG_RETURN(0);
8728
    }
8729
    case NDB_GE_FUNC:
unknown's avatar
unknown committed
8730
    {
8731
      if (!value || !field) break;
unknown's avatar
unknown committed
8732 8733
      // Save value in right format for the field type
      value->save_in_field(field);
8734
      if (a == field)
8735
      {
8736 8737 8738 8739 8740 8741
        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);
8742
      }
8743
      else
8744
      {
8745 8746 8747 8748 8749 8750
        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);
8751
      }
8752 8753
      cond= cond->next->next->next;
      DBUG_RETURN(0);
8754
    }
8755
    case NDB_GT_FUNC:
unknown's avatar
unknown committed
8756
    {
8757
      if (!value || !field) break;
unknown's avatar
unknown committed
8758 8759
      // Save value in right format for the field type
      value->save_in_field(field);
8760
      if (a == field)
8761
      {
8762 8763 8764 8765 8766 8767
        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);
8768
      }
8769
      else
8770
      {
8771 8772 8773 8774 8775 8776
        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);
8777
      }
8778 8779
      cond= cond->next->next->next;
      DBUG_RETURN(0);
8780
    }
8781
    case NDB_LIKE_FUNC:
unknown's avatar
unknown committed
8782
    {
8783
      if (!value || !field) break;
8784 8785 8786
      if ((value->qualification.value_type != Item::STRING_ITEM) &&
          (value->qualification.value_type != Item::VARBIN_ITEM))
          break;
unknown's avatar
unknown committed
8787 8788 8789
      // Save value in right format for the field type
      value->save_in_field(field);
      DBUG_PRINT("info", ("Generating LIKE filter: like(%d,%s,%d)", 
8790 8791 8792 8793
                          field->get_field_no(), value->get_val(), 
                          value->pack_length()));
      if (filter->cmp(NdbScanFilter::COND_LIKE, 
                      field->get_field_no(),
8794 8795
                      value->get_val(),
                      value->pack_length()) == -1)
8796
        DBUG_RETURN(1);
8797 8798
      cond= cond->next->next->next;
      DBUG_RETURN(0);
8799
    }
8800
    case NDB_NOTLIKE_FUNC:
unknown's avatar
unknown committed
8801
    {
8802
      if (!value || !field) break;
8803 8804
      if ((value->qualification.value_type != Item::STRING_ITEM) &&
          (value->qualification.value_type != Item::VARBIN_ITEM))
8805
          break;
unknown's avatar
unknown committed
8806 8807 8808
      // Save value in right format for the field type
      value->save_in_field(field);
      DBUG_PRINT("info", ("Generating NOTLIKE filter: notlike(%d,%s,%d)", 
8809 8810 8811 8812
                          field->get_field_no(), value->get_val(), 
                          value->pack_length()));
      if (filter->cmp(NdbScanFilter::COND_NOT_LIKE, 
                      field->get_field_no(),
8813 8814
                      value->get_val(),
                      value->pack_length()) == -1)
8815
        DBUG_RETURN(1);
8816 8817
      cond= cond->next->next->next;
      DBUG_RETURN(0);
8818
    }
8819
    case NDB_ISNULL_FUNC:
8820 8821 8822 8823 8824
      if (!field)
        break;
      DBUG_PRINT("info", ("Generating ISNULL filter"));
      if (filter->isnull(field->get_field_no()) == -1)
        DBUG_RETURN(1);
8825 8826
      cond= cond->next->next;
      DBUG_RETURN(0);
8827
    case NDB_ISNOTNULL_FUNC:
unknown's avatar
unknown committed
8828
    {
8829 8830 8831 8832 8833
      if (!field)
        break;
      DBUG_PRINT("info", ("Generating ISNOTNULL filter"));
      if (filter->isnotnull(field->get_field_no()) == -1)
        DBUG_RETURN(1);         
8834 8835
      cond= cond->next->next;
      DBUG_RETURN(0);
8836 8837 8838 8839 8840 8841 8842 8843 8844 8845
    }
    default:
      break;
    }
    break;
  }
  default:
    break;
  }
  DBUG_PRINT("info", ("Found illegal condition"));
8846
  DBUG_RETURN(1);
8847 8848
}

8849
int
8850
ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter)
8851
{
8852
  uint level=0;
unknown's avatar
unknown committed
8853
  bool negated= FALSE;
8854
  DBUG_ENTER("build_scan_filter_group");
unknown's avatar
unknown committed
8855

8856 8857
  do
  {
unknown's avatar
unknown committed
8858 8859 8860 8861 8862 8863
    if (!cond)
      DBUG_RETURN(1);
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
    {
      switch (cond->ndb_item->qualification.function_type) {
8864
      case NDB_COND_AND_FUNC:
unknown's avatar
unknown committed
8865
      {
8866 8867 8868 8869 8870
        level++;
        DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NAND":"AND",
                            level));
        if ((negated) ? filter->begin(NdbScanFilter::NAND)
            : filter->begin(NdbScanFilter::AND) == -1)
8871
          DBUG_RETURN(1);
unknown's avatar
unknown committed
8872
        negated= FALSE;
8873 8874 8875
        cond= cond->next;
        break;
      }
8876
      case NDB_COND_OR_FUNC:
unknown's avatar
unknown committed
8877
      {
8878 8879 8880 8881 8882 8883
        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
8884
        negated= FALSE;
8885 8886 8887
        cond= cond->next;
        break;
      }
8888
      case NDB_NOT_FUNC:
unknown's avatar
unknown committed
8889
      {
8890
        DBUG_PRINT("info", ("Generating negated query"));
8891
        cond= cond->next;
unknown's avatar
unknown committed
8892
        negated= TRUE;
8893 8894 8895 8896
        break;
      }
      default:
        if (build_scan_filter_predicate(cond, filter, negated))
8897
          DBUG_RETURN(1);
unknown's avatar
unknown committed
8898
        negated= FALSE;
8899 8900 8901
        break;
      }
      break;
unknown's avatar
unknown committed
8902 8903
    }
    case NDB_END_COND:
8904 8905
      DBUG_PRINT("info", ("End of group %u", level));
      level--;
8906 8907
      if (cond) cond= cond->next;
      if (filter->end() == -1)
8908
        DBUG_RETURN(1);
8909 8910 8911
      if (!negated)
        break;
      // else fall through (NOT END is an illegal condition)
unknown's avatar
unknown committed
8912 8913
    default:
    {
8914
      DBUG_PRINT("info", ("Illegal scan filter"));
8915
    }
8916
    }
8917
  }  while (level > 0 || negated);
8918
  
8919
  DBUG_RETURN(0);
8920 8921
}

8922 8923
int
ha_ndbcluster::build_scan_filter(Ndb_cond * &cond, NdbScanFilter *filter)
8924 8925 8926 8927
{
  bool simple_cond= TRUE;
  DBUG_ENTER("build_scan_filter");  

unknown's avatar
unknown committed
8928 8929 8930
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
      switch (cond->ndb_item->qualification.function_type) {
8931 8932
      case NDB_COND_AND_FUNC:
      case NDB_COND_OR_FUNC:
8933 8934 8935 8936 8937 8938 8939 8940 8941
        simple_cond= FALSE;
        break;
      default:
        break;
      }
      break;
    default:
      break;
    }
8942 8943 8944 8945 8946 8947
  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);
8948

8949
  DBUG_RETURN(0);
8950 8951
}

8952
int
8953
ha_ndbcluster::generate_scan_filter(Ndb_cond_stack *ndb_cond_stack,
8954
                                    NdbScanOperation *op)
8955 8956 8957 8958
{
  DBUG_ENTER("generate_scan_filter");
  if (ndb_cond_stack)
  {
8959
    DBUG_PRINT("info", ("Generating scan filter"));
8960 8961 8962 8963 8964
    NdbScanFilter filter(op);
    bool multiple_cond= FALSE;
    // Wrap an AND group around multiple conditions
    if (ndb_cond_stack->next) {
      multiple_cond= TRUE;
8965
      if (filter.begin() == -1)
8966
        DBUG_RETURN(1); 
8967 8968
    }
    for (Ndb_cond_stack *stack= ndb_cond_stack; 
8969 8970
         (stack); 
         stack= stack->next)
8971
      {
8972
        Ndb_cond *cond= stack->ndb_cond;
8973

8974 8975 8976 8977 8978
        if (build_scan_filter(cond, &filter))
        {
          DBUG_PRINT("info", ("build_scan_filter failed"));
          DBUG_RETURN(1);
        }
8979
      }
8980 8981
    if (multiple_cond && filter.end() == -1)
      DBUG_RETURN(1);
8982 8983 8984 8985 8986 8987
  }
  else
  {  
    DBUG_PRINT("info", ("Empty stack"));
  }

8988
  DBUG_RETURN(0);
8989 8990
}

8991 8992 8993
/*
  get table space info for SHOW CREATE TABLE
*/
8994
char* ha_ndbcluster::get_tablespace_name(THD *thd)
8995
{
8996
  Ndb *ndb= check_ndb_in_thd(thd);
8997
  NDBDICT *ndbdict= ndb->getDictionary();
8998 8999
  NdbError ndberr;
  Uint32 id;
9000 9001 9002
  ndb->setDatabaseName(m_dbname);
  const NDBTAB *ndbtab= ndbdict->getTable(m_tabname);
  if (ndbtab == 0)
9003 9004 9005 9006 9007 9008
  {
    ndberr= ndbdict->getNdbError();
    goto err;
  }
  if (!ndbtab->getTablespace(&id))
  {
9009
    return 0;
9010 9011 9012 9013 9014 9015 9016 9017 9018 9019
  }
  {
    NdbDictionary::Tablespace ts= ndbdict->getTablespace(id);
    ndberr= ndbdict->getNdbError();
    if(ndberr.classification != ndberror_cl_none)
      goto err;
    return (my_strdup(ts.getName(), MYF(0)));
  }
err:
  if (ndberr.status == NdbError::TemporaryError)
9020
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
9021 9022 9023
			ER_GET_TEMPORARY_ERRMSG, ER(ER_GET_TEMPORARY_ERRMSG),
			ndberr.code, ndberr.message, "NDB");
  else
9024
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
9025 9026
			ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
			ndberr.code, ndberr.message, "NDB");
9027 9028 9029
  return 0;
}

unknown's avatar
unknown committed
9030 9031 9032
/*
  Implements the SHOW NDB STATUS command.
*/
9033 9034 9035
bool
ndbcluster_show_status(THD* thd, stat_print_fn *stat_print,
                       enum ha_stat_type stat_type)
9036
{
9037
  char buf[IO_SIZE];
unknown's avatar
unknown committed
9038
  uint buflen;
9039 9040 9041 9042
  DBUG_ENTER("ndbcluster_show_status");
  
  if (have_ndbcluster != SHOW_OPTION_YES) 
  {
9043 9044 9045 9046 9047
    DBUG_RETURN(FALSE);
  }
  if (stat_type != HA_ENGINE_STATUS)
  {
    DBUG_RETURN(FALSE);
9048
  }
unknown's avatar
unknown committed
9049 9050 9051 9052 9053 9054 9055 9056 9057 9058 9059 9060 9061 9062 9063 9064 9065

  update_status_variables(g_ndb_cluster_connection);
  buflen=
    my_snprintf(buf, sizeof(buf),
                "cluster_node_id=%u, "
                "connected_host=%s, "
                "connected_port=%u, "
                "number_of_storage_nodes=%u",
                ndb_cluster_node_id,
                ndb_connected_host,
                ndb_connected_port,
                ndb_number_of_storage_nodes);
  if (stat_print(thd, ndbcluster_hton.name, strlen(ndbcluster_hton.name),
                 "connection", strlen("connection"),
                 buf, buflen))
    DBUG_RETURN(TRUE);

unknown's avatar
unknown committed
9066
  if (get_thd_ndb(thd) && get_thd_ndb(thd)->ndb)
9067
  {
unknown's avatar
unknown committed
9068
    Ndb* ndb= (get_thd_ndb(thd))->ndb;
unknown's avatar
unknown committed
9069 9070
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
9071 9072
    while (ndb->get_free_list_usage(&tmp))
    {
unknown's avatar
unknown committed
9073
      buflen=
unknown's avatar
unknown committed
9074
        my_snprintf(buf, sizeof(buf),
9075 9076
                  "created=%u, free=%u, sizeof=%u",
                  tmp.m_created, tmp.m_free, tmp.m_sizeof);
unknown's avatar
unknown committed
9077 9078
      if (stat_print(thd, ndbcluster_hton.name, strlen(ndbcluster_hton.name),
                     tmp.m_name, strlen(tmp.m_name), buf, buflen))
9079
        DBUG_RETURN(TRUE);
9080 9081
    }
  }
unknown's avatar
unknown committed
9082 9083 9084 9085
#ifdef HAVE_NDB_BINLOG
  ndbcluster_show_status_binlog(thd, stat_print, stat_type);
#endif

9086 9087
  DBUG_RETURN(FALSE);
}
9088

unknown's avatar
unknown committed
9089

9090 9091 9092 9093 9094 9095 9096 9097 9098 9099 9100 9101 9102 9103 9104 9105 9106 9107 9108 9109 9110 9111 9112 9113 9114 9115 9116 9117 9118 9119 9120 9121 9122 9123 9124 9125 9126 9127 9128 9129 9130 9131 9132 9133 9134 9135
/*
  Create a table in NDB Cluster
 */
static uint get_no_fragments(ulonglong max_rows)
{
#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;
#if MYSQL_VERSION_ID >= 50100
  return (max_rows*acc_row_size)/acc_fragment_size+1;
#else
  return ((max_rows*acc_row_size)/acc_fragment_size+1
	  +1/*correct rounding*/)/2;
#endif
}


/*
  Routine to adjust default number of partitions to always be a multiple
  of number of nodes and never more than 4 times the number of nodes.

*/
static bool adjusted_frag_count(uint no_fragments, uint no_nodes,
                                uint &reported_frags)
{
  uint i= 0;
  reported_frags= no_nodes;
  while (reported_frags < no_fragments && ++i < 4 &&
         (reported_frags + no_nodes) < MAX_PARTITIONS) 
    reported_frags+= no_nodes;
  return (reported_frags < no_fragments);
}

int ha_ndbcluster::get_default_no_partitions(ulonglong max_rows)
{
  uint reported_frags;
  uint no_fragments= get_no_fragments(max_rows);
  uint no_nodes= g_ndb_cluster_connection->no_db_nodes();
unknown's avatar
unknown committed
9136 9137 9138 9139 9140 9141
  if (adjusted_frag_count(no_fragments, no_nodes, reported_frags))
  {
    push_warning(current_thd,
                 MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
    "Ndb might have problems storing the max amount of rows specified");
  }
9142 9143 9144 9145
  return (int)reported_frags;
}


unknown's avatar
unknown committed
9146 9147 9148 9149 9150 9151 9152 9153 9154 9155 9156 9157 9158 9159 9160 9161 9162 9163 9164 9165 9166 9167 9168 9169 9170 9171 9172 9173 9174 9175 9176 9177 9178 9179 9180 9181 9182 9183 9184 9185 9186 9187 9188 9189 9190 9191 9192 9193 9194 9195 9196 9197
/*
  Set-up auto-partitioning for NDB Cluster

  SYNOPSIS
    set_auto_partitions()
    part_info                  Partition info struct to set-up
 
  RETURN VALUE
    NONE

  DESCRIPTION
    Set-up auto partitioning scheme for tables that didn't define any
    partitioning. We'll use PARTITION BY KEY() in this case which
    translates into partition by primary key if a primary key exists
    and partition by hidden key otherwise.
*/

void ha_ndbcluster::set_auto_partitions(partition_info *part_info)
{
  DBUG_ENTER("ha_ndbcluster::set_auto_partitions");
  part_info->list_of_part_fields= TRUE;
  part_info->part_type= HASH_PARTITION;
  switch (opt_ndb_distribution_id)
  {
  case ND_KEYHASH:
    part_info->linear_hash_ind= FALSE;
    break;
  case ND_LINHASH:
    part_info->linear_hash_ind= TRUE;
    break;
  }
  DBUG_VOID_RETURN;
}


int ha_ndbcluster::set_range_data(void *tab_ref, partition_info *part_info)
{
  NDBTAB *tab= (NDBTAB*)tab_ref;
  int32 *range_data= (int32*)my_malloc(part_info->no_parts*sizeof(int32),
                                       MYF(0));
  uint i;
  int error= 0;
  DBUG_ENTER("set_range_data");

  if (!range_data)
  {
    mem_alloc_error(part_info->no_parts*sizeof(int32));
    DBUG_RETURN(1);
  }
  for (i= 0; i < part_info->no_parts; i++)
  {
    longlong range_val= part_info->range_int_array[i];
9198
    if (range_val < INT_MIN32 || range_val >= INT_MAX32)
unknown's avatar
unknown committed
9199
    {
9200 9201 9202 9203 9204 9205 9206 9207
      if ((i != part_info->no_parts - 1) ||
          (range_val != LONGLONG_MAX))
      {
        my_error(ER_LIMITED_PART_RANGE, MYF(0), "NDB");
        error= 1;
        goto error;
      }
      range_val= INT_MAX32;
unknown's avatar
unknown committed
9208 9209 9210 9211 9212 9213 9214 9215 9216 9217 9218 9219 9220 9221 9222 9223 9224 9225 9226 9227 9228 9229 9230 9231 9232 9233 9234 9235 9236 9237 9238 9239 9240 9241 9242 9243 9244 9245 9246 9247 9248 9249 9250
    }
    range_data[i]= (int32)range_val;
  }
  tab->setRangeListData(range_data, sizeof(int32)*part_info->no_parts);
error:
  my_free((char*)range_data, MYF(0));
  DBUG_RETURN(error);
}

int ha_ndbcluster::set_list_data(void *tab_ref, partition_info *part_info)
{
  NDBTAB *tab= (NDBTAB*)tab_ref;
  int32 *list_data= (int32*)my_malloc(part_info->no_list_values * 2
                                      * sizeof(int32), MYF(0));
  uint32 *part_id, i;
  int error= 0;
  DBUG_ENTER("set_list_data");

  if (!list_data)
  {
    mem_alloc_error(part_info->no_list_values*2*sizeof(int32));
    DBUG_RETURN(1);
  }
  for (i= 0; i < part_info->no_list_values; i++)
  {
    LIST_PART_ENTRY *list_entry= &part_info->list_array[i];
    longlong list_val= list_entry->list_value;
    if (list_val < INT_MIN32 || list_val > INT_MAX32)
    {
      my_error(ER_LIMITED_PART_RANGE, MYF(0), "NDB");
      error= 1;
      goto error;
    }
    list_data[2*i]= (int32)list_val;
    part_id= (uint32*)&list_data[2*i+1];
    *part_id= list_entry->partition_id;
  }
  tab->setRangeListData(list_data, 2*sizeof(int32)*part_info->no_list_values);
error:
  my_free((char*)list_data, MYF(0));
  DBUG_RETURN(error);
}

9251 9252 9253 9254 9255 9256 9257 9258 9259 9260 9261 9262 9263 9264 9265 9266 9267
/*
  User defined partitioning set-up. We need to check how many fragments the
  user wants defined and which node groups to put those into. Later we also
  want to attach those partitions to a tablespace.

  All the functionality of the partition function, partition limits and so
  forth are entirely handled by the MySQL Server. There is one exception to
  this rule for PARTITION BY KEY where NDB handles the hash function and
  this type can thus be handled transparently also by NDB API program.
  For RANGE, HASH and LIST and subpartitioning the NDB API programs must
  implement the function to map to a partition.
*/

uint ha_ndbcluster::set_up_partition_info(partition_info *part_info,
                                          TABLE *table,
                                          void *tab_par)
{
unknown's avatar
unknown committed
9268 9269 9270
  uint16 frag_data[MAX_PARTITIONS];
  char *ts_names[MAX_PARTITIONS];
  ulong ts_index= 0, fd_index= 0, i, j;
9271 9272 9273
  NDBTAB *tab= (NDBTAB*)tab_par;
  NDBTAB::FragmentType ftype= NDBTAB::UserDefined;
  partition_element *part_elem;
unknown's avatar
unknown committed
9274 9275 9276 9277 9278 9279
  bool first= TRUE;
  uint ts_id, ts_version, part_count= 0, tot_ts_name_len;
  List_iterator<partition_element> part_it(part_info->partitions);
  int error;
  char *name_ptr;
  DBUG_ENTER("ha_ndbcluster::set_up_partition_info");
9280 9281 9282 9283 9284 9285 9286 9287 9288 9289 9290 9291 9292 9293 9294 9295 9296 9297

  if (part_info->part_type == HASH_PARTITION &&
      part_info->list_of_part_fields == TRUE)
  {
    Field **fields= part_info->part_field_array;

    if (part_info->linear_hash_ind)
      ftype= NDBTAB::DistrKeyLin;
    else
      ftype= NDBTAB::DistrKeyHash;

    for (i= 0; i < part_info->part_field_list.elements; i++)
    {
      NDBCOL *col= tab->getColumn(fields[i]->fieldnr - 1);
      DBUG_PRINT("info",("setting dist key on %s", col->getName()));
      col->setPartitionKey(TRUE);
    }
  }
9298
  else 
9299
  {
9300 9301 9302 9303 9304 9305 9306 9307 9308 9309 9310 9311 9312 9313 9314 9315 9316
    /*
      Create a shadow field for those tables that have user defined
      partitioning. This field stores the value of the partition
      function such that NDB can handle reorganisations of the data
      even when the MySQL Server isn't available to assist with
      calculation of the partition function value.
    */
    NDBCOL col;
    DBUG_PRINT("info", ("Generating partition func value field"));
    col.setName("$PART_FUNC_VALUE");
    col.setType(NdbDictionary::Column::Int);
    col.setLength(1);
    col.setNullable(FALSE);
    col.setPrimaryKey(FALSE);
    col.setAutoIncrement(FALSE);
    tab->addColumn(col);
    if (part_info->part_type == RANGE_PARTITION)
9317
    {
9318 9319 9320 9321
      if ((error= set_range_data((void*)tab, part_info)))
      {
        DBUG_RETURN(error);
      }
9322
    }
9323
    else if (part_info->part_type == LIST_PARTITION)
9324
    {
9325 9326 9327 9328
      if ((error= set_list_data((void*)tab, part_info)))
      {
        DBUG_RETURN(error);
      }
9329 9330 9331
    }
  }
  tab->setFragmentType(ftype);
unknown's avatar
unknown committed
9332 9333 9334
  i= 0;
  tot_ts_name_len= 0;
  do
9335
  {
unknown's avatar
unknown committed
9336 9337
    uint ng;
    part_elem= part_it++;
9338
    if (!part_info->is_sub_partitioned())
9339
    {
unknown's avatar
unknown committed
9340 9341 9342 9343 9344
      ng= part_elem->nodegroup_id;
      if (first && ng == UNDEF_NODEGROUP)
        ng= 0;
      ts_names[fd_index]= part_elem->tablespace_name;
      frag_data[fd_index++]= ng;
9345
    }
unknown's avatar
unknown committed
9346 9347 9348 9349 9350 9351 9352 9353 9354 9355 9356 9357 9358 9359 9360 9361 9362
    else
    {
      List_iterator<partition_element> sub_it(part_elem->subpartitions);
      j= 0;
      do
      {
        part_elem= sub_it++;
        ng= part_elem->nodegroup_id;
        if (first && ng == UNDEF_NODEGROUP)
          ng= 0;
        ts_names[fd_index]= part_elem->tablespace_name;
        frag_data[fd_index++]= ng;
      } while (++j < part_info->no_subparts);
    }
    first= FALSE;
  } while (++i < part_info->no_parts);
  tab->setDefaultNoPartitionsFlag(part_info->use_default_no_partitions);
9363
  tab->setLinearFlag(part_info->linear_hash_ind);
unknown's avatar
unknown committed
9364 9365 9366 9367 9368
  tab->setMaxRows(table->s->max_rows);
  tab->setTablespaceNames(ts_names, fd_index*sizeof(char*));
  tab->setFragmentCount(fd_index);
  tab->setFragmentData(&frag_data, fd_index*2);
  DBUG_RETURN(0);
9369
}
9370

unknown's avatar
unknown committed
9371

unknown's avatar
unknown committed
9372 9373 9374
bool ha_ndbcluster::check_if_incompatible_data(HA_CREATE_INFO *info,
					       uint table_changes)
{
9375 9376 9377
  DBUG_ENTER("ha_ndbcluster::check_if_incompatible_data");
  uint i;
  const NDBTAB *tab= (const NDBTAB *) m_table;
unknown's avatar
unknown committed
9378

9379 9380 9381 9382 9383 9384 9385 9386 9387 9388 9389
  for (i= 0; i < table->s->fields; i++) 
  {
    Field *field= table->field[i];
    const NDBCOL *col= tab->getColumn(field->field_name);
    if (field->add_index &&
        col->getStorageType() == NdbDictionary::Column::StorageTypeDisk)
    {
      DBUG_PRINT("info", ("add/drop index not supported for disk stored column"));
      DBUG_RETURN(COMPATIBLE_DATA_NO);
    }
  }
unknown's avatar
unknown committed
9390
  if (table_changes != IS_EQUAL_YES)
9391
    DBUG_RETURN(COMPATIBLE_DATA_NO);
unknown's avatar
unknown committed
9392 9393 9394 9395
  
  /* Check that auto_increment value was not changed */
  if ((info->used_fields & HA_CREATE_USED_AUTO) &&
      info->auto_increment_value != 0)
9396
    DBUG_RETURN(COMPATIBLE_DATA_NO);
unknown's avatar
unknown committed
9397 9398 9399 9400
  
  /* Check that row format didn't change */
  if ((info->used_fields & HA_CREATE_USED_AUTO) &&
      get_row_type() != info->row_type)
9401
    DBUG_RETURN(COMPATIBLE_DATA_NO);
unknown's avatar
unknown committed
9402

9403
  DBUG_RETURN(COMPATIBLE_DATA_YES);
unknown's avatar
unknown committed
9404 9405
}

unknown's avatar
unknown committed
9406 9407 9408 9409 9410 9411 9412 9413 9414 9415 9416 9417 9418 9419 9420 9421 9422 9423 9424 9425 9426 9427 9428 9429 9430 9431 9432 9433 9434 9435 9436 9437 9438 9439 9440 9441 9442 9443 9444 9445
bool set_up_tablespace(st_alter_tablespace *info,
                       NdbDictionary::Tablespace *ndb_ts)
{
  ndb_ts->setName(info->tablespace_name);
  ndb_ts->setExtentSize(info->extent_size);
  ndb_ts->setDefaultLogfileGroup(info->logfile_group_name);
  return false;
}

bool set_up_datafile(st_alter_tablespace *info,
                     NdbDictionary::Datafile *ndb_df)
{
  if (info->max_size > 0)
  {
    my_error(ER_TABLESPACE_AUTO_EXTEND_ERROR, MYF(0));
    return true;
  }
  ndb_df->setPath(info->data_file_name);
  ndb_df->setSize(info->initial_size);
  ndb_df->setTablespace(info->tablespace_name);
  return false;
}

bool set_up_logfile_group(st_alter_tablespace *info,
                          NdbDictionary::LogfileGroup *ndb_lg)
{
  ndb_lg->setName(info->logfile_group_name);
  ndb_lg->setUndoBufferSize(info->undo_buffer_size);
  return false;
}

bool set_up_undofile(st_alter_tablespace *info,
                     NdbDictionary::Undofile *ndb_uf)
{
  ndb_uf->setPath(info->undo_file_name);
  ndb_uf->setSize(info->initial_size);
  ndb_uf->setLogfileGroup(info->logfile_group_name);
  return false;
}

unknown's avatar
unknown committed
9446
int ndbcluster_alter_tablespace(THD* thd, st_alter_tablespace *info)
unknown's avatar
unknown committed
9447 9448
{
  DBUG_ENTER("ha_ndbcluster::alter_tablespace");
unknown's avatar
unknown committed
9449

9450
  int is_tablespace= 0;
unknown's avatar
unknown committed
9451 9452
  Ndb *ndb= check_ndb_in_thd(thd);
  if (ndb == NULL)
unknown's avatar
unknown committed
9453
  {
unknown's avatar
unknown committed
9454
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
unknown's avatar
unknown committed
9455
  }
unknown's avatar
unknown committed
9456 9457 9458 9459 9460
  
  NDBDICT *dict = ndb->getDictionary();
  int error;
  const char * errmsg;

unknown's avatar
unknown committed
9461 9462 9463
  switch (info->ts_cmd_type){
  case (CREATE_TABLESPACE):
  {
9464
    error= ER_CREATE_FILEGROUP_FAILED;
unknown's avatar
unknown committed
9465
    
unknown's avatar
unknown committed
9466 9467 9468 9469 9470 9471 9472 9473 9474 9475
    NdbDictionary::Tablespace ndb_ts;
    NdbDictionary::Datafile ndb_df;
    if (set_up_tablespace(info, &ndb_ts))
    {
      DBUG_RETURN(1);
    }
    if (set_up_datafile(info, &ndb_df))
    {
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
9476 9477
    errmsg= "TABLESPACE";
    if (dict->createTablespace(ndb_ts))
unknown's avatar
unknown committed
9478 9479
    {
      DBUG_PRINT("error", ("createTablespace returned %d", error));
unknown's avatar
unknown committed
9480
      goto ndberror;
unknown's avatar
unknown committed
9481 9482
    }
    DBUG_PRINT("info", ("Successfully created Tablespace"));
unknown's avatar
unknown committed
9483 9484
    errmsg= "DATAFILE";
    if (dict->createDatafile(ndb_df))
unknown's avatar
unknown committed
9485 9486
    {
      DBUG_PRINT("error", ("createDatafile returned %d", error));
unknown's avatar
unknown committed
9487
      goto ndberror;
unknown's avatar
unknown committed
9488
    }
9489
    is_tablespace= 1;
unknown's avatar
unknown committed
9490 9491 9492 9493
    break;
  }
  case (ALTER_TABLESPACE):
  {
9494
    error= ER_ALTER_FILEGROUP_FAILED;
unknown's avatar
unknown committed
9495 9496 9497 9498 9499 9500 9501
    if (info->ts_alter_tablespace_type == ALTER_TABLESPACE_ADD_FILE)
    {
      NdbDictionary::Datafile ndb_df;
      if (set_up_datafile(info, &ndb_df))
      {
	DBUG_RETURN(1);
      }
unknown's avatar
unknown committed
9502 9503
      errmsg= " CREATE DATAFILE";
      if (dict->createDatafile(ndb_df))
unknown's avatar
unknown committed
9504
      {
unknown's avatar
unknown committed
9505
	goto ndberror;
unknown's avatar
unknown committed
9506 9507 9508 9509 9510 9511 9512 9513
      }
    }
    else if(info->ts_alter_tablespace_type == ALTER_TABLESPACE_DROP_FILE)
    {
      NdbDictionary::Datafile df = dict->getDatafile(0, 
						     info->data_file_name);
      if (strcmp(df.getPath(), info->data_file_name) == 0)
      {
unknown's avatar
unknown committed
9514 9515
	errmsg= " DROP DATAFILE";
	if (dict->dropDatafile(df))
unknown's avatar
unknown committed
9516
	{
unknown's avatar
unknown committed
9517
	  goto ndberror;
unknown's avatar
unknown committed
9518 9519 9520 9521 9522
	}
      }
      else
      {
	DBUG_PRINT("error", ("No such datafile"));
9523
	my_error(ER_ALTER_FILEGROUP_FAILED, MYF(0), " NO SUCH FILE");
unknown's avatar
unknown committed
9524 9525 9526 9527 9528 9529 9530 9531 9532
	DBUG_RETURN(1);
      }
    }
    else
    {
      DBUG_PRINT("error", ("Unsupported alter tablespace: %d", 
			   info->ts_alter_tablespace_type));
      DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
    }
9533
    is_tablespace= 1;
unknown's avatar
unknown committed
9534 9535 9536 9537
    break;
  }
  case (CREATE_LOGFILE_GROUP):
  {
9538
    error= ER_CREATE_FILEGROUP_FAILED;
unknown's avatar
unknown committed
9539 9540 9541 9542 9543 9544 9545 9546 9547 9548 9549 9550 9551
    NdbDictionary::LogfileGroup ndb_lg;
    NdbDictionary::Undofile ndb_uf;
    if (info->undo_file_name == NULL)
    {
      /*
	REDO files in LOGFILE GROUP not supported yet
      */
      DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
    }
    if (set_up_logfile_group(info, &ndb_lg))
    {
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
9552 9553
    errmsg= "LOGFILE GROUP";
    if (dict->createLogfileGroup(ndb_lg))
unknown's avatar
unknown committed
9554
    {
unknown's avatar
unknown committed
9555
      goto ndberror;
unknown's avatar
unknown committed
9556 9557 9558 9559 9560 9561
    }
    DBUG_PRINT("info", ("Successfully created Logfile Group"));
    if (set_up_undofile(info, &ndb_uf))
    {
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
9562 9563
    errmsg= "UNDOFILE";
    if (dict->createUndofile(ndb_uf))
unknown's avatar
unknown committed
9564
    {
unknown's avatar
unknown committed
9565
      goto ndberror;
unknown's avatar
unknown committed
9566 9567 9568 9569 9570
    }
    break;
  }
  case (ALTER_LOGFILE_GROUP):
  {
9571
    error= ER_ALTER_FILEGROUP_FAILED;
unknown's avatar
unknown committed
9572 9573 9574 9575 9576 9577 9578 9579 9580 9581 9582 9583
    if (info->undo_file_name == NULL)
    {
      /*
	REDO files in LOGFILE GROUP not supported yet
      */
      DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
    }
    NdbDictionary::Undofile ndb_uf;
    if (set_up_undofile(info, &ndb_uf))
    {
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
9584 9585
    errmsg= "CREATE UNDOFILE";
    if (dict->createUndofile(ndb_uf))
unknown's avatar
unknown committed
9586
    {
unknown's avatar
unknown committed
9587
      goto ndberror;
unknown's avatar
unknown committed
9588 9589 9590 9591 9592
    }
    break;
  }
  case (DROP_TABLESPACE):
  {
9593
    error= ER_DROP_FILEGROUP_FAILED;
unknown's avatar
unknown committed
9594 9595
    errmsg= "TABLESPACE";
    if (dict->dropTablespace(dict->getTablespace(info->tablespace_name)))
unknown's avatar
unknown committed
9596
    {
unknown's avatar
unknown committed
9597
      goto ndberror;
unknown's avatar
unknown committed
9598
    }
9599
    is_tablespace= 1;
unknown's avatar
unknown committed
9600 9601 9602 9603
    break;
  }
  case (DROP_LOGFILE_GROUP):
  {
9604
    error= ER_DROP_FILEGROUP_FAILED;
unknown's avatar
unknown committed
9605 9606
    errmsg= "LOGFILE GROUP";
    if (dict->dropLogfileGroup(dict->getLogfileGroup(info->logfile_group_name)))
unknown's avatar
unknown committed
9607
    {
unknown's avatar
unknown committed
9608
      goto ndberror;
unknown's avatar
unknown committed
9609 9610 9611 9612 9613 9614 9615 9616 9617 9618 9619 9620 9621 9622 9623 9624
    }
    break;
  }
  case (CHANGE_FILE_TABLESPACE):
  {
    DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
  }
  case (ALTER_ACCESS_MODE_TABLESPACE):
  {
    DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
  }
  default:
  {
    DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
  }
  }
9625
#ifdef HAVE_NDB_BINLOG
9626 9627 9628 9629 9630 9631 9632 9633 9634 9635 9636 9637
  if (is_tablespace)
    ndbcluster_log_schema_op(thd, 0,
                             thd->query, thd->query_length,
                             "", info->tablespace_name,
                             0, 0,
                             SOT_TABLESPACE);
  else
    ndbcluster_log_schema_op(thd, 0,
                             thd->query, thd->query_length,
                             "", info->logfile_group_name,
                             0, 0,
                             SOT_LOGFILE_GROUP);
9638
#endif
unknown's avatar
unknown committed
9639
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
9640 9641 9642 9643 9644 9645 9646 9647

ndberror:
  const NdbError err= dict->getNdbError();
  ERR_PRINT(err);
  ndb_to_mysql_error(&err);
  
  my_error(error, MYF(0), errmsg);
  DBUG_RETURN(1);
unknown's avatar
unknown committed
9648 9649
}

unknown's avatar
unknown committed
9650 9651 9652 9653 9654 9655 9656 9657 9658 9659 9660 9661 9662 9663 9664 9665 9666 9667 9668

bool ha_ndbcluster::get_no_parts(const char *name, uint *no_parts)
{
  Ndb *ndb;
  NDBDICT *dict;
  const NDBTAB *tab;
  int err;
  DBUG_ENTER("ha_ndbcluster::get_no_parts");

  set_dbname(name);
  set_tabname(name);
  do
  {
    if (check_ndb_connection())
    {
      err= HA_ERR_NO_CONNECTION;
      break;
    }
    ndb= get_ndb();
9669
    ndb->setDatabaseName(m_dbname);
unknown's avatar
unknown committed
9670 9671 9672 9673 9674 9675 9676 9677 9678 9679 9680 9681 9682 9683 9684 9685 9686 9687 9688
    dict= ndb->getDictionary();
    if (!(tab= dict->getTable(m_tabname)))
      ERR_BREAK(dict->getNdbError(), err);
    // Check if thread has stale local cache
    if (tab->getObjectStatus() == NdbDictionary::Object::Invalid)
    {
      invalidate_dictionary_cache(FALSE);
      if (!(tab= dict->getTable(m_tabname)))
         ERR_BREAK(dict->getNdbError(), err);
    }
    *no_parts= tab->getFragmentCount();
    DBUG_RETURN(FALSE);
  } while (1);

end:
  print_error(err, MYF(0));
  DBUG_RETURN(TRUE);
}

9689 9690 9691
static int ndbcluster_fill_files_table(THD *thd, TABLE_LIST *tables, COND *cond)
{
  TABLE* table= tables->table;
9692
  Ndb *ndb= check_ndb_in_thd(thd);
9693 9694
  NdbDictionary::Dictionary* dict= ndb->getDictionary();
  NdbDictionary::Dictionary::List dflist;
9695
  NdbError ndberr;
9696 9697 9698
  unsigned i;

  DBUG_ENTER("ndbcluster_fill_files_table");
9699

9700 9701
  dict->listObjects(dflist, NdbDictionary::Object::Datafile);
  ndberr= dict->getNdbError();
9702 9703
  if (ndberr.classification != NdbError::NoError)
    ERR_RETURN(ndberr);
9704

9705
  for (i= 0; i < dflist.count; i++)
9706 9707 9708 9709 9710 9711 9712
  {
    NdbDictionary::Dictionary::List::Element& elt = dflist.elements[i];
    Ndb_cluster_connection_node_iter iter;
    unsigned id;

    g_ndb_cluster_connection->init_get_next_node(iter);

9713
    while ((id= g_ndb_cluster_connection->get_next_node(iter)))
9714 9715
    {
      NdbDictionary::Datafile df= dict->getDatafile(id, elt.name);
9716
      ndberr= dict->getNdbError();
9717 9718 9719 9720 9721 9722
      if(ndberr.classification != NdbError::NoError)
      {
        if (ndberr.classification == NdbError::SchemaError)
          continue;
        ERR_RETURN(ndberr);
      }
9723 9724
      NdbDictionary::Tablespace ts= dict->getTablespace(df.getTablespace());
      ndberr= dict->getNdbError();
9725 9726 9727 9728 9729 9730
      if (ndberr.classification != NdbError::NoError)
      {
        if (ndberr.classification == NdbError::SchemaError)
          continue;
        ERR_RETURN(ndberr);
      }
9731 9732

      int c= 0;
9733
      table->field[c++]->set_null(); // FILE_ID
9734 9735
      table->field[c++]->store(elt.name, strlen(elt.name),
                               system_charset_info);
9736
      table->field[c++]->store("DATAFILE",8,system_charset_info);
9737
      table->field[c++]->store(df.getTablespace(), strlen(df.getTablespace()),
9738 9739 9740 9741 9742 9743 9744 9745 9746 9747 9748 9749 9750 9751 9752 9753 9754 9755 9756 9757 9758 9759 9760 9761 9762 9763 9764 9765 9766 9767 9768 9769 9770
                               system_charset_info);
      table->field[c++]->set_null(); // TABLE_CATALOG
      table->field[c++]->set_null(); // TABLE_SCHEMA
      table->field[c++]->set_null(); // TABLE_NAME

      // LOGFILE_GROUP_NAME
      table->field[c++]->store(ts.getDefaultLogfileGroup(),
                               strlen(ts.getDefaultLogfileGroup()),
                               system_charset_info);
      table->field[c++]->set_null(); // LOGFILE_GROUP_NUMBER
      table->field[c++]->store(ndbcluster_hton.name,
                               strlen(ndbcluster_hton.name),
                               system_charset_info); // ENGINE

      table->field[c++]->set_null(); // FULLTEXT_KEYS
      table->field[c++]->set_null(); // DELETED_ROWS
      table->field[c++]->set_null(); // UPDATE_COUNT
      table->field[c++]->store(df.getFree() / ts.getExtentSize()); // FREE_EXTENTS
      table->field[c++]->store(df.getSize() / ts.getExtentSize()); // TOTAL_EXTENTS
      table->field[c++]->store(ts.getExtentSize()); // EXTENT_SIZE

      table->field[c++]->store(df.getSize()); // INITIAL_SIZE
      table->field[c++]->store(df.getSize()); // MAXIMUM_SIZE
      table->field[c++]->set_null(); // AUTOEXTEND_SIZE

      table->field[c++]->set_null(); // CREATION_TIME
      table->field[c++]->set_null(); // LAST_UPDATE_TIME
      table->field[c++]->set_null(); // LAST_ACCESS_TIME
      table->field[c++]->set_null(); // RECOVER_TIME
      table->field[c++]->set_null(); // TRANSACTION_COUNTER

      table->field[c++]->store(df.getObjectVersion()); // VERSION

9771
      table->field[c++]->store("FIXED", 5, system_charset_info); // ROW_FORMAT
9772 9773 9774 9775 9776 9777 9778 9779 9780 9781 9782 9783

      table->field[c++]->set_null(); // TABLE_ROWS
      table->field[c++]->set_null(); // AVG_ROW_LENGTH
      table->field[c++]->set_null(); // DATA_LENGTH
      table->field[c++]->set_null(); // MAX_DATA_LENGTH
      table->field[c++]->set_null(); // INDEX_LENGTH
      table->field[c++]->set_null(); // DATA_FREE
      table->field[c++]->set_null(); // CREATE_TIME
      table->field[c++]->set_null(); // UPDATE_TIME
      table->field[c++]->set_null(); // CHECK_TIME
      table->field[c++]->set_null(); // CHECKSUM

9784
      table->field[c++]->store("NORMAL", 6, system_charset_info);
9785 9786

      char extra[30];
9787 9788
      int len= my_snprintf(extra, sizeof(extra), "CLUSTER_NODE=%u", id);
      table->field[c]->store(extra, len, system_charset_info);
9789 9790 9791 9792
      schema_table_store_record(thd, table);
    }
  }

9793 9794
  dict->listObjects(dflist, NdbDictionary::Object::Undofile);
  ndberr= dict->getNdbError();
9795 9796
  if (ndberr.classification != NdbError::NoError)
    ERR_RETURN(ndberr);
9797

9798
  for (i= 0; i < dflist.count; i++)
9799
  {
9800
    NdbDictionary::Dictionary::List::Element& elt= dflist.elements[i];
9801 9802 9803 9804 9805
    Ndb_cluster_connection_node_iter iter;
    unsigned id;

    g_ndb_cluster_connection->init_get_next_node(iter);

9806
    while ((id= g_ndb_cluster_connection->get_next_node(iter)))
9807 9808
    {
      NdbDictionary::Undofile uf= dict->getUndofile(id, elt.name);
9809
      ndberr= dict->getNdbError();
9810 9811 9812 9813 9814 9815
      if (ndberr.classification != NdbError::NoError)
      {
        if (ndberr.classification == NdbError::SchemaError)
          continue;
        ERR_RETURN(ndberr);
      }
9816 9817 9818
      NdbDictionary::LogfileGroup lfg=
        dict->getLogfileGroup(uf.getLogfileGroup());
      ndberr= dict->getNdbError();
9819 9820 9821 9822 9823 9824
      if (ndberr.classification != NdbError::NoError)
      {
        if (ndberr.classification == NdbError::SchemaError)
          continue;
        ERR_RETURN(ndberr);
      }
9825 9826

      int c= 0;
9827
      table->field[c++]->set_null(); // FILE_ID
9828 9829 9830
      table->field[c++]->store(elt.name, strlen(elt.name),
                               system_charset_info);
      table->field[c++]->store("UNDO LOG", 8, system_charset_info);
9831 9832 9833 9834 9835 9836 9837 9838 9839 9840 9841 9842 9843 9844 9845 9846 9847 9848 9849 9850 9851 9852 9853 9854 9855 9856 9857 9858 9859 9860 9861 9862 9863 9864 9865 9866 9867 9868 9869 9870 9871 9872 9873 9874 9875 9876
      table->field[c++]->set_null(); // TABLESPACE NAME
      table->field[c++]->set_null(); // TABLE_CATALOG
      table->field[c++]->set_null(); // TABLE_SCHEMA
      table->field[c++]->set_null(); // TABLE_NAME

      // LOGFILE_GROUP_NAME
      table->field[c++]->store(uf.getLogfileGroup(),
                               strlen(uf.getLogfileGroup()),
                               system_charset_info);
      table->field[c++]->store(uf.getLogfileGroupId()); // LOGFILE_GROUP_NUMBER
      table->field[c++]->store(ndbcluster_hton.name,
                               strlen(ndbcluster_hton.name),
                               system_charset_info); // ENGINE

      table->field[c++]->set_null(); // FULLTEXT_KEYS
      table->field[c++]->set_null(); // DELETED_ROWS
      table->field[c++]->set_null(); // UPDATE_COUNT
      table->field[c++]->store(lfg.getUndoFreeWords()); // FREE_EXTENTS
      table->field[c++]->store(lfg.getUndoBufferSize()); // TOTAL_EXTENTS
      table->field[c++]->store(4); // EXTENT_SIZE

      table->field[c++]->store(uf.getSize()); // INITIAL_SIZE
      table->field[c++]->store(uf.getSize()); // MAXIMUM_SIZE
      table->field[c++]->set_null(); // AUTOEXTEND_SIZE

      table->field[c++]->set_null(); // CREATION_TIME
      table->field[c++]->set_null(); // LAST_UPDATE_TIME
      table->field[c++]->set_null(); // LAST_ACCESS_TIME
      table->field[c++]->set_null(); // RECOVER_TIME
      table->field[c++]->set_null(); // TRANSACTION_COUNTER

      table->field[c++]->store(uf.getObjectVersion()); // VERSION

      table->field[c++]->set_null(); // ROW FORMAT

      table->field[c++]->set_null(); // TABLE_ROWS
      table->field[c++]->set_null(); // AVG_ROW_LENGTH
      table->field[c++]->set_null(); // DATA_LENGTH
      table->field[c++]->set_null(); // MAX_DATA_LENGTH
      table->field[c++]->set_null(); // INDEX_LENGTH
      table->field[c++]->set_null(); // DATA_FREE
      table->field[c++]->set_null(); // CREATE_TIME
      table->field[c++]->set_null(); // UPDATE_TIME
      table->field[c++]->set_null(); // CHECK_TIME
      table->field[c++]->set_null(); // CHECKSUM

9877
      table->field[c++]->store("NORMAL", 6, system_charset_info);
9878 9879

      char extra[30];
unknown's avatar
unknown committed
9880
      int len= my_snprintf(extra,sizeof(extra),"CLUSTER_NODE=%u",id);
9881
      table->field[c]->store(extra, len, system_charset_info);
9882 9883 9884
      schema_table_store_record(thd, table);
    }
  }
9885
  DBUG_RETURN(0);
9886
}