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

#include "mysql_priv.h"

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

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

unknown's avatar
unknown committed
40 41
#include <mysql/plugin.h>

42 43 44 45 46
#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

47 48 49
// options from from mysqld.cc
extern my_bool opt_ndb_optimized_node_selection;
extern const char *opt_ndbcluster_connectstring;
unknown's avatar
unknown committed
50
extern ulong opt_ndb_cache_check_time;
51

unknown's avatar
unknown committed
52 53 54 55 56 57 58 59 60 61
// ndb interface initialization/cleanup
#ifdef  __cplusplus
extern "C" {
#endif
extern void ndb_init_internal();
extern void ndb_end_internal();
#ifdef  __cplusplus
}
#endif

62 63 64 65 66 67
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
68
// Default value for parallelism
69
static const int parallelism= 0;
unknown's avatar
unknown committed
70

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

unknown's avatar
unknown committed
75 76
static uint ndbcluster_partition_flags();
static uint ndbcluster_alter_table_flags(uint flags);
77
static int ndbcluster_init(void *);
78 79 80 81 82 83 84 85 86 87 88
static int ndbcluster_end(handlerton *hton, ha_panic_function flag);
static bool ndbcluster_show_status(handlerton *hton, THD*,
                                   stat_print_fn *,
                                   enum ha_stat_type);
static int ndbcluster_alter_tablespace(handlerton *hton,
                                       THD* thd, 
                                       st_alter_tablespace *info);
static int ndbcluster_fill_files_table(handlerton *hton,
                                       THD *thd, 
                                       TABLE_LIST *tables, 
                                       COND *cond);
89

90
handlerton *ndbcluster_hton;
91

92 93
static handler *ndbcluster_create_handler(handlerton *hton,
                                          TABLE_SHARE *table,
94
                                          MEM_ROOT *mem_root)
95
{
96
  return new (mem_root) ha_ndbcluster(hton, table);
97 98
}

unknown's avatar
unknown committed
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
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);

}

116
#define NDB_AUTO_INCREMENT_RETRIES 10
unknown's avatar
unknown committed
117 118

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

121 122
#define ERR_RETURN(err)                  \
{                                        \
123
  const NdbError& tmp= err;              \
124
  ERR_PRINT(tmp);                        \
125
  DBUG_RETURN(ndb_to_mysql_error(&tmp)); \
unknown's avatar
unknown committed
126 127
}

128 129 130 131 132 133 134 135
#define ERR_BREAK(err, code)             \
{                                        \
  const NdbError& tmp= err;              \
  ERR_PRINT(tmp);                        \
  code= ndb_to_mysql_error(&tmp);        \
  break;                                 \
}

unknown's avatar
unknown committed
136
static int ndbcluster_inited= 0;
unknown's avatar
unknown committed
137
int ndbcluster_util_inited= 0;
unknown's avatar
unknown committed
138

139
static Ndb* g_ndb= NULL;
unknown's avatar
unknown committed
140
Ndb_cluster_connection* g_ndb_cluster_connection= NULL;
unknown's avatar
unknown committed
141
uchar g_node_id_map[max_ndb_nodes];
142

unknown's avatar
unknown committed
143 144 145 146
// Handler synchronization
pthread_mutex_t ndbcluster_mutex;

// Table lock handling
unknown's avatar
unknown committed
147
HASH ndbcluster_open_tables;
unknown's avatar
unknown committed
148 149 150

static byte *ndbcluster_get_key(NDB_SHARE *share,uint *length,
                                my_bool not_used __attribute__((unused)));
unknown's avatar
unknown committed
151 152 153
#ifdef HAVE_NDB_BINLOG
static int rename_share(NDB_SHARE *share, const char *new_key);
#endif
unknown's avatar
unknown committed
154
static void ndb_set_fragmentation(NDBTAB &tab, TABLE *table, uint pk_len);
unknown's avatar
unknown committed
155

156
static int ndb_get_table_statistics(Ndb*, const NDBTAB *, 
157
                                    struct Ndb_statistics *);
158

unknown's avatar
unknown committed
159

unknown's avatar
Merge  
unknown committed
160
// Util thread variables
unknown's avatar
unknown committed
161
pthread_t ndb_util_thread;
unknown's avatar
Merge  
unknown committed
162 163
pthread_mutex_t LOCK_ndb_util_thread;
pthread_cond_t COND_ndb_util_thread;
164
pthread_handler_t ndb_util_thread_func(void *arg);
unknown's avatar
Merge  
unknown committed
165
ulong ndb_cache_check_time;
unknown's avatar
unknown committed
166

167 168 169 170
/*
  Dummy buffer to read zero pack_length fields
  which are mapped to 1 char
*/
unknown's avatar
unknown committed
171
static uint32 dummy_buf;
172

173 174 175 176 177 178 179 180 181 182 183
/*
  Stats that can be retrieved from ndb
*/

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

184 185 186 187 188 189
/* 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;
190 191
long ndb_number_of_data_nodes= 0;
long ndb_number_of_ready_data_nodes= 0;
192
long ndb_connect_count= 0;
193 194 195 196 197 198 199

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;
200
  ndb_number_of_ready_data_nodes= c->get_no_ready();
unknown's avatar
unknown committed
201
  ndb_number_of_data_nodes=     c->no_db_nodes();
202
  ndb_connect_count= c->get_connect_count();
203 204 205
  return 0;
}

unknown's avatar
unknown committed
206
SHOW_VAR ndb_status_variables[]= {
207
  {"cluster_node_id",        (char*) &ndb_cluster_node_id,         SHOW_LONG},
208 209
  {"config_from_host",         (char*) &ndb_connected_host,      SHOW_CHAR_PTR},
  {"config_from_port",         (char*) &ndb_connected_port,          SHOW_LONG},
210
//  {"number_of_replicas",     (char*) &ndb_number_of_replicas,      SHOW_LONG},
211
  {"number_of_data_nodes",(char*) &ndb_number_of_data_nodes, SHOW_LONG},
212 213 214
  {NullS, NullS, SHOW_LONG}
};

unknown's avatar
unknown committed
215 216 217 218
/*
  Error handling functions
*/

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

unknown's avatar
unknown committed
221
static int ndb_to_mysql_error(const NdbError *ndberr)
unknown's avatar
unknown committed
222
{
unknown's avatar
unknown committed
223 224
  /* read the mysql mapped error code */
  int error= ndberr->mysql_code;
225

unknown's avatar
unknown committed
226 227 228 229 230 231 232 233 234 235 236 237 238
  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
239

unknown's avatar
unknown committed
240 241 242 243
    /* Mapping exists, go with the mapped code */
  default:
    break;
  }
unknown's avatar
unknown committed
244

unknown's avatar
unknown committed
245 246 247 248 249 250
  /*
    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)
251
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
unknown's avatar
unknown committed
252 253 254 255 256 257 258
			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
259 260
}

unknown's avatar
unknown committed
261 262 263 264 265 266 267 268 269 270 271 272
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
273

unknown's avatar
unknown committed
274 275
  return 0;
}
unknown's avatar
unknown committed
276 277

inline
278
int execute_no_commit(ha_ndbcluster *h, NdbTransaction *trans,
279
		      bool force_release)
unknown's avatar
unknown committed
280
{
unknown's avatar
unknown committed
281
#ifdef NOT_USED
282
  int m_batch_execute= 0;
unknown's avatar
unknown committed
283
  if (m_batch_execute)
unknown's avatar
unknown committed
284
    return 0;
unknown's avatar
unknown committed
285
#endif
286
  h->release_completed_operations(trans, force_release);
unknown's avatar
unknown committed
287 288 289 290 291
  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
292 293 294
}

inline
295
int execute_commit(ha_ndbcluster *h, NdbTransaction *trans)
unknown's avatar
unknown committed
296
{
unknown's avatar
unknown committed
297
#ifdef NOT_USED
298
  int m_batch_execute= 0;
unknown's avatar
unknown committed
299
  if (m_batch_execute)
unknown's avatar
unknown committed
300
    return 0;
unknown's avatar
unknown committed
301
#endif
302
  return trans->execute(NdbTransaction::Commit,
303 304
                        NdbTransaction::AbortOnError,
                        h->m_force_send);
305 306 307
}

inline
308
int execute_commit(THD *thd, NdbTransaction *trans)
309 310
{
#ifdef NOT_USED
311
  int m_batch_execute= 0;
312 313 314
  if (m_batch_execute)
    return 0;
#endif
315
  return trans->execute(NdbTransaction::Commit,
316 317
                        NdbTransaction::AbortOnError,
                        thd->variables.ndb_force_send);
unknown's avatar
unknown committed
318 319 320
}

inline
321
int execute_no_commit_ie(ha_ndbcluster *h, NdbTransaction *trans,
322
			 bool force_release)
unknown's avatar
unknown committed
323
{
unknown's avatar
unknown committed
324
#ifdef NOT_USED
325
  int m_batch_execute= 0;
unknown's avatar
unknown committed
326
  if (m_batch_execute)
unknown's avatar
unknown committed
327
    return 0;
unknown's avatar
unknown committed
328
#endif
329
  h->release_completed_operations(trans, force_release);
330
  return trans->execute(NdbTransaction::NoCommit,
331 332
                        NdbTransaction::AO_IgnoreError,
                        h->m_force_send);
unknown's avatar
unknown committed
333 334
}

335 336 337
/*
  Place holder for ha_ndbcluster thread specific data
*/
338 339 340 341 342
static
byte *thd_ndb_share_get_key(THD_NDB_SHARE *thd_ndb_share, uint *length,
                            my_bool not_used __attribute__((unused)))
{
  *length= sizeof(thd_ndb_share->key);
343
  return (byte*) &thd_ndb_share->key;
344 345
}

346 347
Thd_ndb::Thd_ndb()
{
348
  ndb= new Ndb(g_ndb_cluster_connection, "");
349 350
  lock_count= 0;
  count= 0;
351 352
  all= NULL;
  stmt= NULL;
353
  error= 0;
354
  query_state&= NDB_QUERY_NORMAL;
unknown's avatar
unknown committed
355
  options= 0;
356
  (void) hash_init(&open_tables, &my_charset_bin, 5, 0, 0,
357
                   (hash_get_key)thd_ndb_share_get_key, 0, 0);
358 359 360 361
}

Thd_ndb::~Thd_ndb()
{
362
  if (ndb)
363 364
  {
#ifndef DBUG_OFF
unknown's avatar
unknown committed
365 366
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
367 368 369 370 371 372 373 374 375 376
    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
377
    delete ndb;
unknown's avatar
unknown committed
378
    ndb= NULL;
379
  }
380
  changed_tables.empty();
381 382 383 384 385 386
  hash_free(&open_tables);
}

void
Thd_ndb::init_open_tables()
{
387 388
  count= 0;
  error= 0;
389 390 391 392 393 394 395
  my_hash_reset(&open_tables);
}

THD_NDB_SHARE *
Thd_ndb::get_open_table(THD *thd, const void *key)
{
  DBUG_ENTER("Thd_ndb::get_open_table");
unknown's avatar
unknown committed
396
  HASH_SEARCH_STATE state;
397
  THD_NDB_SHARE *thd_ndb_share=
398
    (THD_NDB_SHARE*)hash_first(&open_tables, (byte *)&key, sizeof(key), &state);
unknown's avatar
unknown committed
399
  while (thd_ndb_share && thd_ndb_share->key != key)
400
    thd_ndb_share= (THD_NDB_SHARE*)hash_next(&open_tables, (byte *)&key, sizeof(key), &state);
401 402 403 404 405
  if (thd_ndb_share == 0)
  {
    thd_ndb_share= (THD_NDB_SHARE *) alloc_root(&thd->transaction.mem_root,
                                                sizeof(THD_NDB_SHARE));
    thd_ndb_share->key= key;
406 407
    thd_ndb_share->stat.last_count= count;
    thd_ndb_share->stat.no_uncommitted_rows_count= 0;
unknown's avatar
unknown committed
408
    thd_ndb_share->stat.records= ~(ha_rows)0;
409 410
    my_hash_insert(&open_tables, (byte *)thd_ndb_share);
  }
411 412 413 414
  else if (thd_ndb_share->stat.last_count != count)
  {
    thd_ndb_share->stat.last_count= count;
    thd_ndb_share->stat.no_uncommitted_rows_count= 0;
unknown's avatar
unknown committed
415
    thd_ndb_share->stat.records= ~(ha_rows)0;
416
  }
unknown's avatar
unknown committed
417
  DBUG_PRINT("exit", ("thd_ndb_share: 0x%x  key: 0x%x", thd_ndb_share, key));
418
  DBUG_RETURN(thd_ndb_share);
419 420
}

421 422 423
inline
Ndb *ha_ndbcluster::get_ndb()
{
424
  return get_thd_ndb(current_thd)->ndb;
425 426 427 428 429 430
}

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

unknown's avatar
unknown committed
431 432 433
void ha_ndbcluster::set_rec_per_key()
{
  DBUG_ENTER("ha_ndbcluster::get_status_const");
unknown's avatar
unknown committed
434
  for (uint i=0 ; i < table_share->keys ; i++)
unknown's avatar
unknown committed
435 436 437 438 439 440
  {
    table->key_info[i].rec_per_key[table->key_info[i].key_parts-1]= 1;
  }
  DBUG_VOID_RETURN;
}

441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
ha_rows ha_ndbcluster::records()
{
  ha_rows retval;
  DBUG_ENTER("ha_ndbcluster::records");
  struct Ndb_local_table_statistics *info= m_table_info;
  DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
                      ((const NDBTAB *)m_table)->getTableId(),
                      info->no_uncommitted_rows_count));

  Ndb *ndb= get_ndb();
  ndb->setDatabaseName(m_dbname);
  struct Ndb_statistics stat;
  if (ndb_get_table_statistics(ndb, m_table, &stat) == 0)
  {
    retval= stat.row_count;
  }
457 458 459 460 461 462 463
  else
  {
    /**
     * Be consistent with BUG#19914 until we fix it properly
     */
    DBUG_RETURN(-1);
  }
464 465 466 467 468 469 470 471

  THD *thd= current_thd;
  if (get_thd_ndb(thd)->error)
    info->no_uncommitted_rows_count= 0;

  DBUG_RETURN(retval + info->no_uncommitted_rows_count);
}

472
int ha_ndbcluster::records_update()
473
{
474
  if (m_ha_not_exact_count)
475
    return 0;
476
  DBUG_ENTER("ha_ndbcluster::records_update");
477 478
  int result= 0;

479
  struct Ndb_local_table_statistics *info= m_table_info;
480
  DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
481 482
                      ((const NDBTAB *)m_table)->getTableId(),
                      info->no_uncommitted_rows_count));
483
  //  if (info->records == ~(ha_rows)0)
484
  {
485
    Ndb *ndb= get_ndb();
486
    struct Ndb_statistics stat;
487
    ndb->setDatabaseName(m_dbname);
488
    if ((result= ndb_get_table_statistics(ndb, m_table, &stat)) == 0)
489 490 491
    {
      stats.mean_rec_length= stat.row_size;
      stats.data_file_length= stat.fragment_memory;
492
      info->records= stat.row_count;
493 494
    }
  }
495 496
  {
    THD *thd= current_thd;
497
    if (get_thd_ndb(thd)->error)
498 499
      info->no_uncommitted_rows_count= 0;
  }
500
  stats.records= info->records+ info->no_uncommitted_rows_count;
501
  DBUG_RETURN(result);
502 503
}

504 505
void ha_ndbcluster::no_uncommitted_rows_execute_failure()
{
506 507
  if (m_ha_not_exact_count)
    return;
508
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_execute_failure");
509
  get_thd_ndb(current_thd)->error= 1;
510 511 512
  DBUG_VOID_RETURN;
}

513 514
void ha_ndbcluster::no_uncommitted_rows_update(int c)
{
515 516
  if (m_ha_not_exact_count)
    return;
517
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_update");
518
  struct Ndb_local_table_statistics *info= m_table_info;
519 520
  info->no_uncommitted_rows_count+= c;
  DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
521 522
                      ((const NDBTAB *)m_table)->getTableId(),
                      info->no_uncommitted_rows_count));
523 524 525 526 527
  DBUG_VOID_RETURN;
}

void ha_ndbcluster::no_uncommitted_rows_reset(THD *thd)
{
528 529
  if (m_ha_not_exact_count)
    return;
530
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_reset");
531 532 533
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  thd_ndb->count++;
  thd_ndb->error= 0;
534 535 536
  DBUG_VOID_RETURN;
}

537
int ha_ndbcluster::ndb_err(NdbTransaction *trans)
unknown's avatar
unknown committed
538
{
539
  int res;
540
  NdbError err= trans->getNdbError();
unknown's avatar
unknown committed
541 542 543 544 545
  DBUG_ENTER("ndb_err");
  
  ERR_PRINT(err);
  switch (err.classification) {
  case NdbError::SchemaError:
546
  {
547 548
    // TODO perhaps we need to do more here, invalidate also in the cache
    m_table->setStatusInvalid();
549 550 551 552 553 554
    /* Close other open handlers not used by any thread */
    TABLE_LIST table_list;
    bzero((char*) &table_list,sizeof(table_list));
    table_list.db= m_dbname;
    table_list.alias= table_list.table_name= m_tabname;
    close_cached_tables(current_thd, 0, &table_list);
unknown's avatar
unknown committed
555
    break;
556
  }
unknown's avatar
unknown committed
557 558 559
  default:
    break;
  }
560 561
  res= ndb_to_mysql_error(&err);
  DBUG_PRINT("info", ("transformed ndbcluster error %d to mysql error %d", 
562
                      err.code, res));
563
  if (res == HA_ERR_FOUND_DUPP_KEY)
564 565
  {
    if (m_rows_to_insert == 1)
566 567 568 569 570 571
    {
      /*
	We can only distinguish between primary and non-primary
	violations here, so we need to return MAX_KEY for non-primary
	to signal that key is unknown
      */
572
      m_dupkey= err.code == 630 ? table_share->primary_key : MAX_KEY; 
573
    }
574
    else
unknown's avatar
unknown committed
575 576
    {
      /* We are batching inserts, offending key is not available */
577
      m_dupkey= (uint) -1;
unknown's avatar
unknown committed
578
    }
579
  }
580
  DBUG_RETURN(res);
unknown's avatar
unknown committed
581 582 583
}


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

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

595
  Ndb *ndb= get_ndb();
596
  if (!ndb)
unknown's avatar
unknown committed
597
    DBUG_RETURN(FALSE);
598

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


unknown's avatar
unknown committed
607
#ifndef DBUG_OFF
unknown's avatar
unknown committed
608 609 610 611
/*
  Check if type is supported by NDB.
*/

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


unknown's avatar
unknown committed
650 651 652 653 654
/*
  Instruct NDB to set the value of the hidden primary key
*/

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


/*
  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
676 677 678 679
  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
680 681 682 683 684 685 686 687
}


/*
 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
688 689
                                 uint fieldnr, int row_offset,
                                 bool *set_blob_value)
unknown's avatar
unknown committed
690
{
unknown's avatar
unknown committed
691 692
  const byte* field_ptr= field->ptr + row_offset;
  uint32 pack_len= field->pack_length();
unknown's avatar
unknown committed
693
  DBUG_ENTER("set_ndb_value");
unknown's avatar
unknown committed
694
  DBUG_PRINT("enter", ("%d: %s  type: %u  len=%d  is_null=%s", 
unknown's avatar
unknown committed
695
                       fieldnr, field->field_name, field->type(), 
unknown's avatar
unknown committed
696
                       pack_len, field->is_null(row_offset) ? "Y" : "N"));
unknown's avatar
unknown committed
697
  DBUG_DUMP("value", (char*) field_ptr, pack_len);
unknown's avatar
unknown committed
698

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

760 761 762
      // Looks like NULL ptr signals length 0 blob
      if (blob_ptr == NULL) {
        DBUG_ASSERT(blob_len == 0);
763
        blob_ptr= (char*)"";
764
      }
unknown's avatar
unknown committed
765

unknown's avatar
unknown committed
766 767
      DBUG_PRINT("value", ("set blob ptr=%p len=%u",
                           blob_ptr, blob_len));
unknown's avatar
unknown committed
768 769
      DBUG_DUMP("value", (char*)blob_ptr, min(blob_len, 26));

770
      if (set_blob_value)
771
        *set_blob_value= TRUE;
unknown's avatar
unknown committed
772 773 774 775
      // No callback needed to write value
      DBUG_RETURN(ndb_blob->setValue(blob_ptr, blob_len) != 0);
    }
    DBUG_RETURN(1);
unknown's avatar
unknown committed
776
  }
unknown's avatar
unknown committed
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
}


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

unknown's avatar
unknown committed
794
int g_get_ndb_blobs_value(NdbBlob *ndb_blob, void *arg)
unknown's avatar
unknown committed
795
{
unknown's avatar
unknown committed
796
  DBUG_ENTER("g_get_ndb_blobs_value");
unknown's avatar
unknown committed
797 798 799
  if (ndb_blob->blobsNextBlob() != NULL)
    DBUG_RETURN(0);
  ha_ndbcluster *ha= (ha_ndbcluster *)arg;
800 801
  int ret= get_ndb_blobs_value(ha->table, ha->m_value,
                               ha->m_blobs_buffer, ha->m_blobs_buffer_size,
802
                               ha->m_blobs_offset);
803
  DBUG_RETURN(ret);
unknown's avatar
unknown committed
804 805
}

806 807 808 809 810 811 812 813
/*
  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
814 815 816 817 818 819 820 821
{
  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;
822
    for (uint i= 0; i < table->s->fields; i++)
unknown's avatar
unknown committed
823 824
    {
      Field *field= table->field[i];
825
      NdbValue value= value_array[i];
826 827 828
      if (! (field->flags & BLOB_FLAG))
        continue;
      if (value.blob == NULL)
unknown's avatar
unknown committed
829
      {
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846
        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
847
        {
848 849 850 851 852 853 854 855 856 857 858
          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
859
        }
860 861 862 863 864 865 866 867 868 869 870
        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
871 872
      }
    }
873
    if (loop == 0 && offset > buffer_size)
unknown's avatar
unknown committed
874
    {
875 876 877 878 879
      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
880
        DBUG_RETURN(-1);
881
      buffer_size= offset;
unknown's avatar
unknown committed
882
    }
unknown's avatar
unknown committed
883
  }
unknown's avatar
unknown committed
884
  DBUG_RETURN(0);
unknown's avatar
unknown committed
885 886 887 888 889
}


/*
  Instruct NDB to fetch one field
unknown's avatar
unknown committed
890 891
  - 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
892 893
*/

unknown's avatar
unknown committed
894
int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field,
unknown's avatar
unknown committed
895
                                 uint fieldnr, byte* buf)
unknown's avatar
unknown committed
896 897
{
  DBUG_ENTER("get_ndb_value");
unknown's avatar
unknown committed
898 899 900 901 902
  DBUG_PRINT("enter", ("fieldnr: %d flags: %o", fieldnr,
                       (int)(field != NULL ? field->flags : 0)));

  if (field != NULL)
  {
unknown's avatar
unknown committed
903 904
      DBUG_ASSERT(buf);
      DBUG_ASSERT(ndb_supported_type(field->type()));
unknown's avatar
unknown committed
905 906
      DBUG_ASSERT(field->ptr != NULL);
      if (! (field->flags & BLOB_FLAG))
907
      { 
908 909
        if (field->type() != MYSQL_TYPE_BIT)
        {
910 911 912 913 914 915 916 917
          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);
        }
918 919 920 921
        else // if (field->type() == MYSQL_TYPE_BIT)
        {
          m_value[fieldnr].rec= ndb_op->getValue(fieldnr);
        }
unknown's avatar
unknown committed
922 923 924 925 926 927 928 929 930
        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
931
	m_blobs_offset= buf - (byte*) table->record[0];
unknown's avatar
unknown committed
932
        void *arg= (void *)this;
unknown's avatar
unknown committed
933
        DBUG_RETURN(ndb_blob->setActiveHook(g_get_ndb_blobs_value, arg) != 0);
unknown's avatar
unknown committed
934 935 936 937 938
      }
      DBUG_RETURN(1);
  }

  // Used for hidden key only
939
  m_value[fieldnr].rec= ndb_op->getValue(fieldnr, m_ref);
unknown's avatar
unknown committed
940 941 942
  DBUG_RETURN(m_value[fieldnr].rec == NULL);
}

943 944 945 946 947 948
/*
  Instruct NDB to fetch the partition id (fragment id)
*/
int ha_ndbcluster::get_ndb_partition_id(NdbOperation *ndb_op)
{
  DBUG_ENTER("get_ndb_partition_id");
949 950
  DBUG_RETURN(ndb_op->getValue(NdbDictionary::Column::FRAGMENT, 
                               (char *)&m_part_id) == NULL);
951
}
unknown's avatar
unknown committed
952 953 954 955

/*
  Check if any set or get of blob value in current query.
*/
956

957
bool ha_ndbcluster::uses_blob_value()
unknown's avatar
unknown committed
958
{
959 960 961
  uint blob_fields;
  MY_BITMAP *bitmap;
  uint *blob_index, *blob_index_end;
unknown's avatar
unknown committed
962
  if (table_share->blob_fields == 0)
unknown's avatar
unknown committed
963
    return FALSE;
964 965 966 967 968

  bitmap= m_write_op ? table->write_set : table->read_set;
  blob_index=     table_share->blob_field;
  blob_index_end= blob_index + table_share->blob_fields;
  do
unknown's avatar
unknown committed
969
  {
970 971 972 973
    if (bitmap_is_set(table->write_set,
                      table->field[*blob_index]->field_index))
      return TRUE;
  } while (++blob_index != blob_index_end);
unknown's avatar
unknown committed
974
  return FALSE;
unknown's avatar
unknown committed
975 976 977 978 979 980 981 982 983
}


/*
  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
984 985 986 987

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

990 991
int cmp_frm(const NDBTAB *ndbtab, const void *pack_data,
            uint pack_length)
unknown's avatar
unknown committed
992 993 994 995 996 997 998 999 1000 1001 1002
{
  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
1003 1004
int ha_ndbcluster::get_metadata(const char *path)
{
1005 1006
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
unknown's avatar
unknown committed
1007 1008 1009 1010 1011
  const NDBTAB *tab;
  int error;
  DBUG_ENTER("get_metadata");
  DBUG_PRINT("enter", ("m_tabname: %s, path: %s", m_tabname, path));

1012 1013
  DBUG_ASSERT(m_table == NULL);
  DBUG_ASSERT(m_table_info == NULL);
1014

1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
  const void *data, *pack_data;
  uint length, pack_length;

  /*
    Compare FrmData in NDB with frm file from disk.
  */
  error= 0;
  if (readfrm(path, &data, &length) ||
      packfrm(data, length, &pack_data, &pack_length))
  {
    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
1029
    
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
  Ndb_table_guard ndbtab_g(dict, m_tabname);
  if (!(tab= ndbtab_g.get_table()))
    ERR_RETURN(dict->getNdbError());

  if (get_ndb_share_state(m_share) != NSS_ALTERED 
      && cmp_frm(tab, pack_data, pack_length))
  {
    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;
  }
  my_free((char*)data, MYF(0));
  my_free((char*)pack_data, MYF(0));
1047

unknown's avatar
unknown committed
1048
  if (error)
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061
    goto err;

  DBUG_PRINT("info", ("fetched table %s", tab->getName()));
  m_table= tab;
  if ((error= open_indexes(ndb, table, FALSE)) == 0)
  {
    ndbtab_g.release();
    DBUG_RETURN(0);
  }
err:
  ndbtab_g.invalidate();
  m_table= NULL;
  DBUG_RETURN(error);
1062
}
unknown's avatar
unknown committed
1063

1064
static int fix_unique_index_attr_order(NDB_INDEX_DATA &data,
1065 1066
                                       const NDBINDEX *index,
                                       KEY *key_info)
1067 1068 1069 1070 1071 1072
{
  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));
unknown's avatar
unknown committed
1073
  data.unique_index_attrid_map= (uchar*)my_malloc(sz,MYF(MY_WME));
1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085

  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++)
    {
1086
      const NDBCOL *c= index->getColumn(j);
unknown's avatar
unknown committed
1087
      if (strcmp(field_name, c->getName()) == 0)
1088
      {
1089 1090
        data.unique_index_attrid_map[i]= j;
        break;
1091 1092 1093 1094 1095 1096
      }
    }
    DBUG_ASSERT(data.unique_index_attrid_map[i] != 255);
  }
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1097

1098 1099 1100 1101 1102 1103
/*
  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)
1104
{
1105
  uint i;
unknown's avatar
unknown committed
1106
  int error= 0;
1107
  const char *index_name;
unknown's avatar
unknown committed
1108
  KEY* key_info= tab->key_info;
1109
  const char **key_name= tab->s->keynames.type_names;
1110
  NDBDICT *dict= ndb->getDictionary();
1111
  DBUG_ENTER("ha_ndbcluster::create_indexes");
1112
  
1113
  for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
1114
  {
unknown's avatar
unknown committed
1115
    index_name= *key_name;
1116
    NDB_INDEX_TYPE idx_type= get_index_type_from_table(i);
1117 1118
    error= create_index(index_name, key_info, idx_type, i);
    if (error)
1119
    {
1120 1121
      DBUG_PRINT("error", ("Failed to create index %u", i));
      break;
1122
    }
1123 1124 1125 1126 1127
  }

  DBUG_RETURN(error);
}

unknown's avatar
ndb:  
unknown committed
1128
static void ndb_init_index(NDB_INDEX_DATA &data)
1129
{
unknown's avatar
ndb:  
unknown committed
1130 1131 1132 1133 1134 1135 1136 1137 1138
  data.type= UNDEFINED_INDEX;
  data.status= UNDEFINED;
  data.unique_index= NULL;
  data.index= NULL;
  data.unique_index_attrid_map= NULL;
  data.index_stat=NULL;
  data.index_stat_cache_entries=0;
  data.index_stat_update_freq=0;
  data.index_stat_query_count=0;
1139 1140
}

unknown's avatar
ndb:  
unknown committed
1141
static void ndb_clear_index(NDB_INDEX_DATA &data)
1142
{
unknown's avatar
ndb:  
unknown committed
1143 1144 1145 1146 1147 1148 1149 1150 1151
  if (data.unique_index_attrid_map)
  {
    my_free((char*)data.unique_index_attrid_map, MYF(0));
  }
  if (data.index_stat)
  {
    delete data.index_stat;
  }
  ndb_init_index(data);
1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163
}

/*
  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;
1164 1165
  DBUG_ENTER("ha_ndbcluster::add_index_handle");
  DBUG_PRINT("enter", ("table %s", m_tabname));
1166 1167 1168 1169

  if (idx_type != PRIMARY_KEY_INDEX && idx_type != UNIQUE_INDEX)
  {
    DBUG_PRINT("info", ("Get handle to index %s", index_name));
1170 1171 1172
    const NDBINDEX *index;
    do
    {
1173
      index= dict->getIndexGlobal(index_name, *m_table);
1174 1175 1176 1177 1178 1179 1180 1181
      if (!index)
        ERR_RETURN(dict->getNdbError());
      DBUG_PRINT("info", ("index: 0x%x  id: %d  version: %d.%d  status: %d",
                          index,
                          index->getObjectId(),
                          index->getObjectVersion() & 0xFFFFFF,
                          index->getObjectVersion() >> 24,
                          index->getObjectStatus()));
1182 1183
      DBUG_ASSERT(index->getObjectStatus() ==
                  NdbDictionary::Object::Retrieved);
1184 1185
      break;
    } while (1);
1186
    m_index[index_no].index= index;
1187 1188 1189 1190 1191
    // 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
1192
    {
1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210
      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";
1211
    m_has_unique_index= TRUE;
1212 1213
    strxnmov(unique_index_name, FN_LEN, index_name, unique_suffix, NullS);
    DBUG_PRINT("info", ("Get handle to unique_index %s", unique_index_name));
1214 1215 1216
    const NDBINDEX *index;
    do
    {
1217
      index= dict->getIndexGlobal(unique_index_name, *m_table);
1218 1219 1220 1221 1222 1223 1224 1225
      if (!index)
        ERR_RETURN(dict->getNdbError());
      DBUG_PRINT("info", ("index: 0x%x  id: %d  version: %d.%d  status: %d",
                          index,
                          index->getObjectId(),
                          index->getObjectVersion() & 0xFFFFFF,
                          index->getObjectVersion() >> 24,
                          index->getObjectStatus()));
1226 1227
      DBUG_ASSERT(index->getObjectStatus() ==
                  NdbDictionary::Object::Retrieved);
1228 1229
      break;
    } while (1);
1230
    m_index[index_no].unique_index= index;
1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241
    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
*/
1242
int ha_ndbcluster::open_indexes(Ndb *ndb, TABLE *tab, bool ignore_error)
1243 1244 1245 1246 1247 1248 1249 1250 1251
{
  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");
1252
  m_has_unique_index= FALSE;
1253 1254 1255
  for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
  {
    if ((error= add_index_handle(thd, dict, key_info, *key_name, i)))
1256 1257 1258 1259
      if (ignore_error)
        m_index[i].index= m_index[i].unique_index= NULL;
      else
        break;
1260
  }
1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281

  if (error && !ignore_error)
  {
    while (i > 0)
    {
      i--;
      if (m_index[i].index)
      {
         dict->removeIndexGlobal(*m_index[i].index, 1);
         m_index[i].index= NULL;
      }
      if (m_index[i].unique_index)
      {
         dict->removeIndexGlobal(*m_index[i].unique_index, 1);
         m_index[i].unique_index= NULL;
      }
    }
  }

  DBUG_ASSERT(error == 0 || error == 4243);

1282 1283 1284 1285 1286 1287 1288
  DBUG_RETURN(error);
}

/*
  Renumber indexes in index list by shifting out
  indexes that are to be dropped
 */
1289
void ha_ndbcluster::renumber_indexes(Ndb *ndb, TABLE *tab)
1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310
{
  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
1311
      {
1312 1313 1314 1315
        tmp=  m_index[j - 1];
        m_index[j - 1]= m_index[j];
        m_index[j]= tmp;
        j++;
unknown's avatar
unknown committed
1316 1317
      }
    }
1318 1319
  }

1320
  DBUG_VOID_RETURN;
1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334
}

/*
  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;
  NDBDICT *dict= ndb->getDictionary();
  DBUG_ENTER("ha_ndbcluster::drop_indexes");
  
unknown's avatar
unknown committed
1335
  for (i= 0; i < tab->s->keys; i++, key_info++)
1336 1337 1338 1339
  {
    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)
1340
    {
1341 1342
      const NdbDictionary::Index *index= m_index[i].index;
      const NdbDictionary::Index *unique_index= m_index[i].unique_index;
1343 1344
      
      if (index)
1345
      {
1346 1347 1348
        index_name= index->getName();
        DBUG_PRINT("info", ("Dropping index %u: %s", i, index_name));  
        // Drop ordered index from ndb
1349 1350 1351 1352 1353 1354
        error= dict->dropIndexGlobal(*index);
        if (!error)
        {
          dict->removeIndexGlobal(*index, 1);
          m_index[i].index= NULL;
        }
1355 1356
      }
      if (!error && unique_index)
1357
      {
unknown's avatar
unknown committed
1358 1359
        index_name= unique_index->getName();
        DBUG_PRINT("info", ("Dropping unique index %u: %s", i, index_name));
1360
        // Drop unique index from ndb
1361 1362 1363 1364 1365 1366
        error= dict->dropIndexGlobal(*unique_index);
        if (!error)
        {
          dict->removeIndexGlobal(*unique_index, 1);
          m_index[i].unique_index= NULL;
        }
1367
      }
1368 1369
      if (error)
        DBUG_RETURN(error);
unknown's avatar
ndb:  
unknown committed
1370
      ndb_clear_index(m_index[i]);
1371
      continue;
1372
    }
1373
  }
unknown's avatar
unknown committed
1374 1375
  
  DBUG_RETURN(error);
1376 1377
}

unknown's avatar
unknown committed
1378 1379 1380 1381
/*
  Decode the type of an index from information 
  provided in table object
*/
1382
NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_table(uint inx) const
unknown's avatar
unknown committed
1383
{
1384 1385
  return get_index_type_from_key(inx, table_share->key_info,
                                 inx == table_share->primary_key);
1386 1387 1388
}

NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_key(uint inx,
1389 1390
                                                      KEY *key_info,
                                                      bool primary) const
1391 1392
{
  bool is_hash_index=  (key_info[inx].algorithm == 
unknown's avatar
unknown committed
1393
                        HA_KEY_ALG_HASH);
1394
  if (primary)
1395
    return is_hash_index ? PRIMARY_KEY_INDEX : PRIMARY_KEY_ORDERED_INDEX;
1396 1397
  
  return ((key_info[inx].flags & HA_NOSAME) ? 
1398 1399
          (is_hash_index ? UNIQUE_INDEX : UNIQUE_ORDERED_INDEX) :
          ORDERED_INDEX);
unknown's avatar
unknown committed
1400
} 
1401

1402 1403 1404 1405 1406
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;
1407
  DBUG_ENTER("ha_ndbcluster::check_index_fields_not_null");
1408 1409 1410 1411 1412 1413
  
  for (; key_part != end; key_part++) 
    {
      Field* field= key_part->field;
      if (field->maybe_null())
      {
1414 1415 1416
        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);
1417 1418 1419 1420 1421
      }
    }
  
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1422

1423
void ha_ndbcluster::release_metadata(THD *thd, Ndb *ndb)
unknown's avatar
unknown committed
1424
{
1425
  uint i;
1426

unknown's avatar
unknown committed
1427 1428 1429
  DBUG_ENTER("release_metadata");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));

1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443
  NDBDICT *dict= ndb->getDictionary();
  int invalidate_indexes= 0;
  if (thd && thd->lex && thd->lex->sql_command == SQLCOM_FLUSH)
  {
    invalidate_indexes = 1;
  }
  if (m_table != NULL)
  {
    if (m_table->getObjectStatus() == NdbDictionary::Object::Invalid)
      invalidate_indexes= 1;
    dict->removeTableGlobal(*m_table, invalidate_indexes);
  }
  // TODO investigate
  DBUG_ASSERT(m_table_info == NULL);
unknown's avatar
unknown committed
1444
  m_table_info= NULL;
unknown's avatar
unknown committed
1445

1446
  // Release index list 
1447 1448
  for (i= 0; i < MAX_KEY; i++)
  {
1449 1450 1451 1452 1453 1454 1455 1456 1457 1458
    if (m_index[i].unique_index)
    {
      DBUG_ASSERT(m_table != NULL);
      dict->removeIndexGlobal(*m_index[i].unique_index, invalidate_indexes);
    }
    if (m_index[i].index)
    {
      DBUG_ASSERT(m_table != NULL);
      dict->removeIndexGlobal(*m_index[i].index, invalidate_indexes);
    }
unknown's avatar
ndb:  
unknown committed
1459
    ndb_clear_index(m_index[i]);
1460 1461
  }

1462
  m_table= NULL;
unknown's avatar
unknown committed
1463 1464 1465
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
1466
int ha_ndbcluster::get_ndb_lock_type(enum thr_lock_type type)
1467
{
1468
  if (type >= TL_WRITE_ALLOW_WRITE)
unknown's avatar
unknown committed
1469
    return NdbOperation::LM_Exclusive;
unknown's avatar
unknown committed
1470 1471
  if (type ==  TL_READ_WITH_SHARED_LOCKS ||
      uses_blob_value())
1472
    return NdbOperation::LM_Read;
1473
  return NdbOperation::LM_CommittedRead;
1474 1475
}

unknown's avatar
unknown committed
1476 1477 1478 1479 1480 1481
static const ulong index_type_flags[]=
{
  /* UNDEFINED_INDEX */
  0,                         

  /* PRIMARY_KEY_INDEX */
1482
  HA_ONLY_WHOLE_INDEX, 
1483 1484

  /* PRIMARY_KEY_ORDERED_INDEX */
1485
  /* 
unknown's avatar
unknown committed
1486
     Enable HA_KEYREAD_ONLY when "sorted" indexes are supported, 
1487 1488 1489
     thus ORDERD BY clauses can be optimized by reading directly 
     through the index.
  */
unknown's avatar
unknown committed
1490
  // HA_KEYREAD_ONLY | 
unknown's avatar
unknown committed
1491
  HA_READ_NEXT |
1492
  HA_READ_PREV |
unknown's avatar
unknown committed
1493 1494
  HA_READ_RANGE |
  HA_READ_ORDER,
unknown's avatar
unknown committed
1495 1496

  /* UNIQUE_INDEX */
1497
  HA_ONLY_WHOLE_INDEX,
unknown's avatar
unknown committed
1498

1499
  /* UNIQUE_ORDERED_INDEX */
unknown's avatar
unknown committed
1500
  HA_READ_NEXT |
1501
  HA_READ_PREV |
unknown's avatar
unknown committed
1502 1503
  HA_READ_RANGE |
  HA_READ_ORDER,
1504

unknown's avatar
unknown committed
1505
  /* ORDERED_INDEX */
unknown's avatar
unknown committed
1506
  HA_READ_NEXT |
1507
  HA_READ_PREV |
unknown's avatar
unknown committed
1508 1509
  HA_READ_RANGE |
  HA_READ_ORDER
unknown's avatar
unknown committed
1510 1511 1512 1513 1514 1515 1516
};

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);
1517
  return m_index[idx_no].type;
unknown's avatar
unknown committed
1518 1519 1520 1521 1522 1523 1524 1525 1526 1527
}


/*
  Get the flags for an index

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

1528 1529
inline ulong ha_ndbcluster::index_flags(uint idx_no, uint part,
                                        bool all_parts) const 
unknown's avatar
unknown committed
1530
{ 
1531
  DBUG_ENTER("ha_ndbcluster::index_flags");
unknown's avatar
unknown committed
1532
  DBUG_PRINT("enter", ("idx_no: %u", idx_no));
unknown's avatar
unknown committed
1533
  DBUG_ASSERT(get_index_type_from_table(idx_no) < index_flags_size);
1534 1535
  DBUG_RETURN(index_type_flags[get_index_type_from_table(idx_no)] | 
              HA_KEY_SCAN_NOT_ROR);
unknown's avatar
unknown committed
1536 1537
}

unknown's avatar
unknown committed
1538 1539
static void shrink_varchar(Field* field, const byte* & ptr, char* buf)
{
1540
  if (field->type() == MYSQL_TYPE_VARCHAR && ptr != NULL) {
unknown's avatar
unknown committed
1541
    Field_varstring* f= (Field_varstring*)field;
unknown's avatar
unknown committed
1542
    if (f->length_bytes == 1) {
unknown's avatar
unknown committed
1543 1544 1545 1546 1547
      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
1548
        DBUG_ASSERT(FALSE);
unknown's avatar
unknown committed
1549 1550 1551 1552 1553 1554 1555
        buf[0]= 255;
      }
      memmove(buf + 1, ptr + 2, pack_len - 1);
      ptr= buf;
    }
  }
}
unknown's avatar
unknown committed
1556 1557 1558

int ha_ndbcluster::set_primary_key(NdbOperation *op, const byte *key)
{
unknown's avatar
unknown committed
1559
  KEY* key_info= table->key_info + table_share->primary_key;
unknown's avatar
unknown committed
1560 1561 1562 1563 1564 1565 1566
  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
1567 1568 1569
    const byte* ptr= key;
    char buf[256];
    shrink_varchar(field, ptr, buf);
unknown's avatar
unknown committed
1570
    if (set_ndb_key(op, field, 
1571
                    key_part->fieldnr-1, ptr))
unknown's avatar
unknown committed
1572
      ERR_RETURN(op->getNdbError());
unknown's avatar
unknown committed
1573
    key += key_part->store_length;
unknown's avatar
unknown committed
1574 1575 1576 1577 1578
  }
  DBUG_RETURN(0);
}


1579
int ha_ndbcluster::set_primary_key_from_record(NdbOperation *op, const byte *record)
1580
{
unknown's avatar
unknown committed
1581
  KEY* key_info= table->key_info + table_share->primary_key;
1582 1583
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
1584
  DBUG_ENTER("set_primary_key_from_record");
1585 1586 1587 1588 1589

  for (; key_part != end; key_part++) 
  {
    Field* field= key_part->field;
    if (set_ndb_key(op, field, 
1590
		    key_part->fieldnr-1, record+key_part->offset))
unknown's avatar
unknown committed
1591 1592 1593 1594 1595
      ERR_RETURN(op->getNdbError());
  }
  DBUG_RETURN(0);
}

1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614
int ha_ndbcluster::set_index_key_from_record(NdbOperation *op, 
                                             const byte *record, uint keyno)
{
  KEY* key_info= table->key_info + keyno;
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
  uint i;
  DBUG_ENTER("set_index_key_from_record");
                                                                                
  for (i= 0; key_part != end; key_part++, i++)
  {
    Field* field= key_part->field;
    if (set_ndb_key(op, field, m_index[keyno].unique_index_attrid_map[i],
                    record+key_part->offset))
      ERR_RETURN(m_active_trans->getNdbError());
  }
  DBUG_RETURN(0);
}

1615 1616
int 
ha_ndbcluster::set_index_key(NdbOperation *op, 
1617 1618
                             const KEY *key_info, 
                             const byte * key_ptr)
1619
{
1620
  DBUG_ENTER("ha_ndbcluster::set_index_key");
1621 1622 1623 1624 1625 1626
  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
1627 1628 1629 1630
    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
1631
    if (set_ndb_key(op, field, m_index[active_index].unique_index_attrid_map[i], ptr))
1632 1633 1634 1635 1636
      ERR_RETURN(m_active_trans->getNdbError());
    key_ptr+= key_part->store_length;
  }
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1637

unknown's avatar
unknown committed
1638 1639 1640 1641 1642 1643 1644
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
1645
  for (i= 0; i < table_share->fields; i++) 
unknown's avatar
unknown committed
1646 1647
  {
    Field *field= table->field[i];
1648
    if (bitmap_is_set(table->read_set, i) ||
1649
        ((field->flags & PRI_KEY_FLAG)))
unknown's avatar
unknown committed
1650 1651
    {      
      if (get_ndb_value(op, field, i, buf))
1652
        ERR_RETURN(op->getNdbError());
unknown's avatar
unknown committed
1653
    } 
1654
    else
unknown's avatar
unknown committed
1655 1656 1657 1658 1659
    {
      m_value[i].ptr= NULL;
    }
  }
    
unknown's avatar
unknown committed
1660
  if (table_share->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
1661 1662 1663
  {
    DBUG_PRINT("info", ("Getting hidden key"));
    // Scanning table with no primary key
unknown's avatar
unknown committed
1664
    int hidden_no= table_share->fields;      
unknown's avatar
unknown committed
1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675
#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
1676

unknown's avatar
unknown committed
1677 1678 1679 1680
/*
  Read one record from NDB using primary key
*/

1681 1682
int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf,
                           uint32 part_id)
unknown's avatar
unknown committed
1683
{
unknown's avatar
unknown committed
1684
  uint no_fields= table_share->fields;
unknown's avatar
unknown committed
1685 1686
  NdbConnection *trans= m_active_trans;
  NdbOperation *op;
1687

1688 1689 1690 1691
  int res;
  DBUG_ENTER("pk_read");
  DBUG_PRINT("enter", ("key_len: %u", key_len));
  DBUG_DUMP("key", (char*)key, key_len);
1692
  m_write_op= FALSE;
unknown's avatar
unknown committed
1693

1694 1695
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
unknown's avatar
unknown committed
1696
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || 
1697
      op->readTuple(lm) != 0)
1698
    ERR_RETURN(trans->getNdbError());
1699
  
unknown's avatar
unknown committed
1700
  if (table_share->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
1701 1702 1703 1704 1705
  {
    // 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))
1706
      ERR_RETURN(trans->getNdbError());
1707
    
unknown's avatar
unknown committed
1708
    // Read key at the same time, for future reference
unknown's avatar
unknown committed
1709
    if (get_ndb_value(op, NULL, no_fields, NULL))
1710
      ERR_RETURN(trans->getNdbError());
unknown's avatar
unknown committed
1711 1712 1713 1714 1715 1716 1717
  } 
  else 
  {
    if ((res= set_primary_key(op, key)))
      return res;
  }
  
unknown's avatar
unknown committed
1718
  if ((res= define_read_attrs(buf, op)))
1719
    DBUG_RETURN(res);
1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731

  if (m_use_partition_function)
  {
    op->setPartitionId(part_id);
    // If table has user defined partitioning
    // and no indexes, we need to read the partition id
    // to support ORDER BY queries
    if (table_share->primary_key == MAX_KEY &&
        get_ndb_partition_id(op))
      ERR_RETURN(trans->getNdbError());
  }

1732
  if (execute_no_commit_ie(this,trans,false) != 0) 
unknown's avatar
unknown committed
1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743
  {
    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);
}

1744 1745
/*
  Read one complementing record from NDB using primary key from old_data
1746
  or hidden key
1747 1748
*/

1749 1750
int ha_ndbcluster::complemented_read(const byte *old_data, byte *new_data,
                                     uint32 old_part_id)
1751
{
unknown's avatar
unknown committed
1752
  uint no_fields= table_share->fields, i;
1753
  NdbTransaction *trans= m_active_trans;
1754
  NdbOperation *op;
1755
  DBUG_ENTER("complemented_read");
1756
  m_write_op= FALSE;
1757

1758
  if (bitmap_is_set_all(table->read_set))
1759
  {
1760 1761
    // We have allready retrieved all fields, nothing to complement
    DBUG_RETURN(0);
1762
  }
1763

1764 1765
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
unknown's avatar
unknown committed
1766
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || 
1767
      op->readTuple(lm) != 0)
1768
    ERR_RETURN(trans->getNdbError());
1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779
  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());
  }
1780 1781 1782 1783

  if (m_use_partition_function)
    op->setPartitionId(old_part_id);
  
1784 1785 1786 1787
  // Read all unreferenced non-key field(s)
  for (i= 0; i < no_fields; i++) 
  {
    Field *field= table->field[i];
1788
    if (!((field->flags & PRI_KEY_FLAG) ||
1789 1790
          bitmap_is_set(table->read_set, i)) &&
        !bitmap_is_set(table->write_set, i))
1791
    {
unknown's avatar
unknown committed
1792
      if (get_ndb_value(op, field, i, new_data))
1793
        ERR_RETURN(trans->getNdbError());
1794 1795 1796
    }
  }
  
1797
  if (execute_no_commit(this,trans,false) != 0) 
1798 1799 1800 1801 1802 1803 1804 1805
  {
    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;     
1806 1807 1808 1809 1810 1811 1812 1813

  /**
   * restore m_value
   */
  for (i= 0; i < no_fields; i++) 
  {
    Field *field= table->field[i];
    if (!((field->flags & PRI_KEY_FLAG) ||
1814
          bitmap_is_set(table->read_set, i)))
1815 1816 1817 1818 1819
    {
      m_value[i].ptr= NULL;
    }
  }
  
1820 1821 1822
  DBUG_RETURN(0);
}

1823
/*
1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883
 * Check that all operations between first and last all
 * have gotten the errcode
 * If checking for HA_ERR_KEY_NOT_FOUND then update m_dupkey
 * for all succeeding operations
 */
bool ha_ndbcluster::check_all_operations_for_error(NdbTransaction *trans,
                                                   const NdbOperation *first,
                                                   const NdbOperation *last,
                                                   uint errcode)
{
  const NdbOperation *op= first;
  DBUG_ENTER("ha_ndbcluster::check_all_operations_for_error");

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


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

1886
int ha_ndbcluster::peek_indexed_rows(const byte *record)
1887
{
1888
  NdbTransaction *trans= m_active_trans;
1889
  NdbOperation *op;
1890 1891
  const NdbOperation *first, *last;
  uint i;
1892
  int res;
1893
  DBUG_ENTER("peek_indexed_rows");
unknown's avatar
unknown committed
1894

unknown's avatar
unknown committed
1895
  NdbOperation::LockMode lm=
1896
      (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
1897 1898
  first= NULL;
  if (table->s->primary_key != MAX_KEY)
1899
  {
1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911
    /*
     * Fetch any row with colliding primary key
     */
    if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) ||
        op->readTuple(lm) != 0)
      ERR_RETURN(trans->getNdbError());
    
    first= op;
    if ((res= set_primary_key_from_record(op, record)))
      ERR_RETURN(trans->getNdbError());

    if (m_use_partition_function)
1912
    {
1913 1914 1915
      uint32 part_id;
      int error;
      longlong func_value;
1916 1917 1918 1919
      my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
      error= m_part_info->get_partition_id(m_part_info, &part_id, &func_value);
      dbug_tmp_restore_column_map(table->read_set, old_map);
      if (error)
1920 1921
      {
        m_part_info->err_value= func_value;
1922
        DBUG_RETURN(error);
1923
      }
1924
      op->setPartitionId(part_id);
1925 1926
    }
  }
1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938
  /*
   * Fetch any rows with colliding unique indexes
   */
  KEY* key_info;
  KEY_PART_INFO *key_part, *end;
  for (i= 0, key_info= table->key_info; i < table->s->keys; i++, key_info++)
  {
    if (i != table->s->primary_key &&
        key_info->flags & HA_NOSAME)
    {
      // A unique index is defined on table
      NdbIndexOperation *iop;
1939
      const NDBINDEX *unique_index = m_index[i].unique_index;
1940 1941
      key_part= key_info->key_part;
      end= key_part + key_info->key_parts;
1942
      if (!(iop= trans->getNdbIndexOperation(unique_index, m_table)) ||
1943 1944
          iop->readTuple(lm) != 0)
        ERR_RETURN(trans->getNdbError());
1945

1946 1947 1948 1949 1950 1951 1952 1953
      if (!first)
        first= iop;
      if ((res= set_index_key_from_record(iop, record, i)))
        ERR_RETURN(trans->getNdbError());
    }
  }
  last= trans->getLastDefinedOperation();
  if (first)
1954
    res= execute_no_commit_ie(this,trans,false);
1955 1956 1957 1958 1959 1960 1961 1962
  else
  {
    // Table has no keys
    table->status= STATUS_NOT_FOUND;
    DBUG_RETURN(HA_ERR_KEY_NOT_FOUND);
  }
  if (check_all_operations_for_error(trans, first, last, 
                                     HA_ERR_KEY_NOT_FOUND))
unknown's avatar
unknown committed
1963 1964 1965 1966
  {
    table->status= STATUS_NOT_FOUND;
    DBUG_RETURN(ndb_err(trans));
  } 
1967 1968 1969 1970
  else
  {
    DBUG_PRINT("info", ("m_dupkey %d", m_dupkey));
  }
1971 1972
  DBUG_RETURN(0);
}
1973

1974

unknown's avatar
unknown committed
1975 1976 1977 1978 1979
/*
  Read one record from NDB using unique secondary index
*/

int ha_ndbcluster::unique_index_read(const byte *key,
1980
                                     uint key_len, byte *buf)
unknown's avatar
unknown committed
1981
{
1982
  int res;
1983
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
1984
  NdbIndexOperation *op;
1985
  DBUG_ENTER("ha_ndbcluster::unique_index_read");
unknown's avatar
unknown committed
1986 1987 1988
  DBUG_PRINT("enter", ("key_len: %u, index: %u", key_len, active_index));
  DBUG_DUMP("key", (char*)key, key_len);
  
1989 1990
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
1991 1992
  if (!(op= trans->getNdbIndexOperation(m_index[active_index].unique_index, 
                                        m_table)) ||
1993
      op->readTuple(lm) != 0)
unknown's avatar
unknown committed
1994 1995 1996
    ERR_RETURN(trans->getNdbError());
  
  // Set secondary index key(s)
unknown's avatar
unknown committed
1997
  if ((res= set_index_key(op, table->key_info + active_index, key)))
1998 1999
    DBUG_RETURN(res);
  
unknown's avatar
unknown committed
2000
  if ((res= define_read_attrs(buf, op)))
2001
    DBUG_RETURN(res);
unknown's avatar
unknown committed
2002

2003
  if (execute_no_commit_ie(this,trans,false) != 0) 
unknown's avatar
unknown committed
2004 2005 2006 2007 2008 2009 2010 2011 2012 2013
  {
    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
2014
inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor)
2015 2016
{
  DBUG_ENTER("fetch_next");
2017
  int check;
2018
  NdbTransaction *trans= m_active_trans;
2019
  
unknown's avatar
unknown committed
2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043
  if (m_lock_tuple)
  {
    /*
      Lock level m_lock.type either TL_WRITE_ALLOW_WRITE
      (SELECT FOR UPDATE) or TL_READ_WITH_SHARED_LOCKS (SELECT
      LOCK WITH SHARE MODE) and row was not explictly unlocked 
      with unlock_row() call
    */
      NdbConnection *trans= m_active_trans;
      NdbOperation *op;
      // Lock row
      DBUG_PRINT("info", ("Keeping lock on scanned row"));
      
      if (!(op= m_active_cursor->lockCurrentTuple()))
      {
	m_lock_tuple= false;
	ERR_RETURN(trans->getNdbError());
      }
      m_ops_pending++;
  }
  m_lock_tuple= false;
  
  bool contact_ndb= m_lock.type < TL_WRITE_ALLOW_WRITE &&
                    m_lock.type != TL_READ_WITH_SHARED_LOCKS;;
2044 2045
  do {
    DBUG_PRINT("info", ("Call nextResult, contact_ndb: %d", contact_ndb));
unknown's avatar
unknown committed
2046 2047 2048
    /*
      We can only handle one tuple with blobs at a time.
    */
2049
    if (m_ops_pending && m_blobs_pending)
unknown's avatar
unknown committed
2050
    {
2051
      if (execute_no_commit(this,trans,false) != 0)
2052
        DBUG_RETURN(ndb_err(trans));
2053 2054
      m_ops_pending= 0;
      m_blobs_pending= FALSE;
unknown's avatar
unknown committed
2055
    }
2056 2057
    
    if ((check= cursor->nextResult(contact_ndb, m_force_send)) == 0)
2058
    {
unknown's avatar
unknown committed
2059 2060 2061 2062 2063 2064 2065
      /*
	Explicitly lock tuple if "select for update" or
	"select lock in share mode"
      */
      m_lock_tuple= (m_lock.type == TL_WRITE_ALLOW_WRITE
		     || 
		     m_lock.type == TL_READ_WITH_SHARED_LOCKS);
2066 2067 2068 2069 2070 2071
      DBUG_RETURN(0);
    } 
    else if (check == 1 || check == 2)
    {
      // 1: No more records
      // 2: No more cached records
2072
      
2073
      /*
2074 2075 2076
        Before fetching more rows and releasing lock(s),
        all pending update or delete operations should 
        be sent to NDB
2077
      */
2078 2079
      DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending));    
      if (m_ops_pending)
2080
      {
2081 2082
        if (m_transaction_on)
        {
2083
          if (execute_no_commit(this,trans,false) != 0)
2084 2085 2086 2087 2088 2089
            DBUG_RETURN(-1);
        }
        else
        {
          if  (execute_commit(this,trans) != 0)
            DBUG_RETURN(-1);
unknown's avatar
unknown committed
2090
          if (trans->restart() != 0)
2091 2092 2093 2094 2095 2096
          {
            DBUG_ASSERT(0);
            DBUG_RETURN(-1);
          }
        }
        m_ops_pending= 0;
2097
      }
2098 2099
      contact_ndb= (check == 2);
    }
unknown's avatar
unknown committed
2100 2101 2102 2103
    else
    {
      DBUG_RETURN(-1);
    }
2104
  } while (check == 2);
unknown's avatar
unknown committed
2105

2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116
  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
2117

2118 2119 2120 2121 2122 2123 2124
*/

inline int ha_ndbcluster::next_result(byte *buf)
{  
  int res;
  DBUG_ENTER("next_result");
    
2125 2126 2127
  if (!m_active_cursor)
    DBUG_RETURN(HA_ERR_END_OF_FILE);
  
unknown's avatar
unknown committed
2128
  if ((res= fetch_next(m_active_cursor)) == 0)
2129 2130 2131 2132 2133 2134 2135
  {
    DBUG_PRINT("info", ("One more record found"));    
    
    unpack_record(buf);
    table->status= 0;
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
2136
  else if (res == 1)
2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147
  {
    // 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
2148 2149
}

2150
/*
2151
  Set bounds for ordered index scan.
2152 2153
*/

unknown's avatar
unknown committed
2154
int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
2155 2156
                              uint inx,
                              bool rir,
2157 2158
                              const key_range *keys[2],
                              uint range_no)
2159
{
2160
  const KEY *const key_info= table->key_info + inx;
2161 2162 2163
  const uint key_parts= key_info->key_parts;
  uint key_tot_len[2];
  uint tot_len;
2164
  uint i, j;
2165 2166

  DBUG_ENTER("set_bounds");
2167
  DBUG_PRINT("info", ("key_parts=%d", key_parts));
2168

2169
  for (j= 0; j <= 1; j++)
2170
  {
2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183
    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;
    }
2184 2185
  }
  tot_len= 0;
unknown's avatar
unknown committed
2186

2187 2188 2189 2190
  for (i= 0; i < key_parts; i++)
  {
    KEY_PART_INFO *key_part= &key_info->key_part[i];
    Field *field= key_part->field;
2191
#ifndef DBUG_OFF
2192
    uint part_len= key_part->length;
2193
#endif
2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207
    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++)
    {
2208
      struct part_st &p= part[j];
2209 2210 2211 2212 2213 2214 2215
      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
2216
        p.part_null= key_part->null_bit && *p.part_ptr;
2217
        p.bound_ptr= (const char *)
unknown's avatar
unknown committed
2218
          p.part_null ? 0 : key_part->null_bit ? p.part_ptr + 1 : p.part_ptr;
2219 2220 2221 2222 2223 2224

        if (j == 0)
        {
          switch (p.key->flag)
          {
            case HA_READ_KEY_EXACT:
2225 2226 2227 2228
              if (! rir)
                p.bound_type= NdbIndexScanOperation::BoundEQ;
              else // differs for records_in_range
                p.bound_type= NdbIndexScanOperation::BoundLE;
2229
              break;
2230
            // ascending
2231 2232 2233 2234 2235 2236 2237 2238 2239
            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;
2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252
            // 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;
2253 2254 2255 2256 2257 2258 2259
            default:
              break;
          }
        }
        if (j == 1) {
          switch (p.key->flag)
          {
2260
            // ascending
2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271
            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;
2272
            // descending strangely sets no end key
2273 2274
          }
        }
2275

2276 2277 2278
        if (p.bound_type == -1)
        {
          DBUG_PRINT("error", ("key %d unknown flag %d", j, p.key->flag));
unknown's avatar
unknown committed
2279
          DBUG_ASSERT(FALSE);
2280
          // Stop setting bounds but continue with what we have
2281
          op->end_of_bound(range_no);
2282 2283 2284 2285
          DBUG_RETURN(0);
        }
      }
    }
2286

2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303
    // 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;
    }
2304

2305 2306
    for (j= 0; j <= 1; j++)
    {
2307
      struct part_st &p= part[j];
2308 2309 2310 2311 2312 2313 2314 2315 2316
      // 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)
2317
        {
unknown's avatar
unknown committed
2318 2319 2320
          const char* ptr= p.bound_ptr;
          char buf[256];
          shrink_varchar(field, ptr, buf);
unknown's avatar
Merge  
unknown committed
2321
          if (op->setBound(i, p.bound_type, ptr))
2322
            ERR_RETURN(op->getNdbError());
2323
        }
2324 2325 2326 2327
      }
    }

    tot_len+= part_store_len;
2328
  }
2329
  op->end_of_bound(range_no);
2330 2331 2332
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
2333
/*
2334
  Start ordered index scan in NDB
unknown's avatar
unknown committed
2335 2336
*/

2337
int ha_ndbcluster::ordered_index_scan(const key_range *start_key,
2338
                                      const key_range *end_key,
2339 2340
                                      bool sorted, bool descending,
                                      byte* buf, part_id_range *part_spec)
unknown's avatar
unknown committed
2341
{  
2342
  int res;
unknown's avatar
unknown committed
2343
  bool restart;
2344
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
2345
  NdbIndexScanOperation *op;
2346

2347 2348 2349
  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
2350
  DBUG_PRINT("enter", ("Starting new ordered scan on %s", m_tabname));
2351
  m_write_op= FALSE;
unknown's avatar
unknown committed
2352

2353 2354
  // Check that sorted seems to be initialised
  DBUG_ASSERT(sorted == 0 || sorted == 1);
unknown's avatar
unknown committed
2355
  
2356
  if (m_active_cursor == 0)
unknown's avatar
unknown committed
2357
  {
unknown's avatar
unknown committed
2358
    restart= FALSE;
unknown's avatar
unknown committed
2359 2360
    NdbOperation::LockMode lm=
      (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
unknown's avatar
unknown committed
2361
   bool need_pk = (lm == NdbOperation::LM_Read);
2362 2363
    if (!(op= trans->getNdbIndexScanOperation(m_index[active_index].index, 
                                              m_table)) ||
unknown's avatar
unknown committed
2364
        op->readTuples(lm, 0, parallelism, sorted, descending, false, need_pk))
unknown's avatar
unknown committed
2365
      ERR_RETURN(trans->getNdbError());
2366 2367 2368
    if (m_use_partition_function && part_spec != NULL &&
        part_spec->start_part == part_spec->end_part)
      op->setPartitionId(part_spec->start_part);
2369
    m_active_cursor= op;
unknown's avatar
unknown committed
2370
  } else {
unknown's avatar
unknown committed
2371
    restart= TRUE;
2372
    op= (NdbIndexScanOperation*)m_active_cursor;
unknown's avatar
unknown committed
2373
    
2374 2375 2376
    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
2377 2378
    DBUG_ASSERT(op->getSorted() == sorted);
    DBUG_ASSERT(op->getLockMode() == 
2379
                (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type));
unknown's avatar
unknown committed
2380
    if (op->reset_bounds(m_force_send))
unknown's avatar
unknown committed
2381 2382
      DBUG_RETURN(ndb_err(m_active_trans));
  }
2383
  
2384
  {
2385
    const key_range *keys[2]= { start_key, end_key };
2386
    res= set_bounds(op, active_index, false, keys);
2387 2388
    if (res)
      DBUG_RETURN(res);
2389
  }
2390

2391
  if (!restart)
2392
  {
2393 2394 2395
    if (generate_scan_filter(m_cond_stack, op))
      DBUG_RETURN(ndb_err(trans));

2396
    if ((res= define_read_attrs(buf, op)))
2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407
    {
      DBUG_RETURN(res);
    }
    
    // If table has user defined partitioning
    // and no primary key, we need to read the partition id
    // to support ORDER BY queries
    if (m_use_partition_function &&
        (table_share->primary_key == MAX_KEY) && 
        (get_ndb_partition_id(op)))
      ERR_RETURN(trans->getNdbError());
unknown's avatar
unknown committed
2408
  }
2409

2410
  if (execute_no_commit(this,trans,false) != 0)
2411 2412 2413 2414
    DBUG_RETURN(ndb_err(trans));
  
  DBUG_RETURN(next_result(buf));
}
unknown's avatar
unknown committed
2415

unknown's avatar
unknown committed
2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439
static
int
guess_scan_flags(NdbOperation::LockMode lm, 
		 const NDBTAB* tab, const MY_BITMAP* readset)
{
  int flags= 0;
  flags|= (lm == NdbOperation::LM_Read) ? NdbScanOperation::SF_KeyInfo : 0;
  if (tab->checkColumns(0, 0) & 2)
  {
    int ret = tab->checkColumns(readset->bitmap, no_bytes_in_map(readset));
    
    if (ret & 2)
    { // If disk columns...use disk scan
      flags |= NdbScanOperation::SF_DiskScan;
    }
    else if ((ret & 4) == 0 && (lm == NdbOperation::LM_Exclusive))
    {
      // If no mem column is set and exclusive...guess disk scan
      flags |= NdbScanOperation::SF_DiskScan;
    }
  }
  return flags;
}

unknown's avatar
unknown committed
2440
/*
2441
  Start full table scan in NDB
unknown's avatar
unknown committed
2442 2443 2444 2445
 */

int ha_ndbcluster::full_table_scan(byte *buf)
{
2446
  int res;
unknown's avatar
unknown committed
2447
  NdbScanOperation *op;
2448
  NdbTransaction *trans= m_active_trans;
2449
  part_id_range part_spec;
unknown's avatar
unknown committed
2450 2451 2452

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

2455 2456
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
unknown's avatar
unknown committed
2457
  int flags= guess_scan_flags(lm, m_table, table->read_set);
2458
  if (!(op=trans->getNdbScanOperation(m_table)) ||
unknown's avatar
unknown committed
2459
      op->readTuples(lm, flags, parallelism))
unknown's avatar
unknown committed
2460
    ERR_RETURN(trans->getNdbError());
2461
  m_active_cursor= op;
2462 2463 2464 2465

  if (m_use_partition_function)
  {
    part_spec.start_part= 0;
2466
    part_spec.end_part= m_part_info->get_tot_partitions() - 1;
2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488
    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);
    }
2489 2490 2491 2492 2493 2494
    // If table has user defined partitioning
    // and no primary key, we need to read the partition id
    // to support ORDER BY queries
    if ((table_share->primary_key == MAX_KEY) && 
        (get_ndb_partition_id(op)))
      ERR_RETURN(trans->getNdbError());
2495 2496
  }

2497 2498
  if (generate_scan_filter(m_cond_stack, op))
    DBUG_RETURN(ndb_err(trans));
unknown's avatar
unknown committed
2499
  if ((res= define_read_attrs(buf, op)))
2500 2501
    DBUG_RETURN(res);

2502
  if (execute_no_commit(this,trans,false) != 0)
2503 2504 2505
    DBUG_RETURN(ndb_err(trans));
  DBUG_PRINT("exit", ("Scan started successfully"));
  DBUG_RETURN(next_result(buf));
2506 2507
}

unknown's avatar
unknown committed
2508 2509 2510 2511 2512
/*
  Insert one record into NDB
*/
int ha_ndbcluster::write_row(byte *record)
{
unknown's avatar
unknown committed
2513
  bool has_auto_increment;
unknown's avatar
unknown committed
2514
  uint i;
2515
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
2516 2517
  NdbOperation *op;
  int res;
unknown's avatar
unknown committed
2518
  THD *thd= current_thd;
2519 2520
  longlong func_value= 0;
  DBUG_ENTER("ha_ndbcluster::write_row");
2521

2522
  m_write_op= TRUE;
2523 2524 2525 2526 2527 2528 2529 2530 2531
  has_auto_increment= (table->next_number_field && record == table->record[0]);
  if (table_share->primary_key != MAX_KEY)
  {
    /*
     * Increase any auto_incremented primary key
     */
    if (has_auto_increment) 
    {
      THD *thd= table->in_use;
unknown's avatar
unknown committed
2532
      int error;
2533 2534

      m_skip_auto_increment= FALSE;
unknown's avatar
unknown committed
2535 2536
      if ((error= update_auto_increment()))
        DBUG_RETURN(error);
2537
      m_skip_auto_increment= (insert_id_for_cur_row == 0);
2538 2539 2540 2541 2542 2543 2544
    }
  }

  /*
   * If IGNORE the ignore constraint violations on primary and unique keys
   */
  if (!m_use_write && m_ignore_dup_key)
2545
  {
2546 2547 2548 2549 2550
    /*
      compare if expression with that in start_bulk_insert()
      start_bulk_insert will set parameters to ensure that each
      write_row is committed individually
    */
2551
    int peek_res= peek_indexed_rows(record);
2552 2553 2554 2555 2556 2557 2558
    
    if (!peek_res) 
    {
      DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
    }
    if (peek_res != HA_ERR_KEY_NOT_FOUND)
      DBUG_RETURN(peek_res);
2559
  }
2560

unknown's avatar
unknown committed
2561
  statistic_increment(thd->status_var.ha_write_count, &LOCK_status);
unknown's avatar
unknown committed
2562 2563
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
    table->timestamp_field->set_time();
unknown's avatar
unknown committed
2564

2565
  if (!(op= trans->getNdbOperation(m_table)))
unknown's avatar
unknown committed
2566 2567 2568 2569 2570 2571
    ERR_RETURN(trans->getNdbError());

  res= (m_use_write) ? op->writeTuple() :op->insertTuple(); 
  if (res != 0)
    ERR_RETURN(trans->getNdbError());  
 
2572 2573 2574 2575
  if (m_use_partition_function)
  {
    uint32 part_id;
    int error;
2576 2577 2578 2579
    my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
    error= m_part_info->get_partition_id(m_part_info, &part_id, &func_value);
    dbug_tmp_restore_column_map(table->read_set, old_map);
    if (error)
2580 2581
    {
      m_part_info->err_value= func_value;
2582
      DBUG_RETURN(error);
2583
    }
2584 2585 2586
    op->setPartitionId(part_id);
  }

unknown's avatar
unknown committed
2587
  if (table_share->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
2588 2589
  {
    // Table has hidden primary key
2590
    Ndb *ndb= get_ndb();
2591 2592
    int ret;
    Uint64 auto_value;
2593 2594
    uint retries= NDB_AUTO_INCREMENT_RETRIES;
    do {
2595 2596
      Ndb_tuple_id_range_guard g(m_share);
      ret= ndb->getAutoIncrementValue(m_table, g.range, auto_value, 1);
2597
    } while (ret == -1 && 
2598 2599
             --retries &&
             ndb->getNdbError().status == NdbError::TemporaryError);
2600
    if (ret == -1)
2601
      ERR_RETURN(ndb->getNdbError());
unknown's avatar
unknown committed
2602
    if (set_hidden_key(op, table_share->fields, (const byte*)&auto_value))
unknown's avatar
unknown committed
2603 2604 2605 2606
      ERR_RETURN(op->getNdbError());
  } 
  else 
  {
2607 2608 2609
    int error;
    if ((error= set_primary_key_from_record(op, record)))
      DBUG_RETURN(error);
unknown's avatar
unknown committed
2610 2611 2612
  }

  // Set non-key attribute(s)
unknown's avatar
unknown committed
2613
  bool set_blob_value= FALSE;
2614
  my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
unknown's avatar
unknown committed
2615
  for (i= 0; i < table_share->fields; i++) 
unknown's avatar
unknown committed
2616 2617 2618
  {
    Field *field= table->field[i];
    if (!(field->flags & PRI_KEY_FLAG) &&
2619
	(bitmap_is_set(table->write_set, i) || !m_use_write) &&
unknown's avatar
unknown committed
2620
        set_ndb_value(op, field, i, record-table->record[0], &set_blob_value))
2621
    {
2622
      m_skip_auto_increment= TRUE;
2623
      dbug_tmp_restore_column_map(table->read_set, old_map);
unknown's avatar
unknown committed
2624
      ERR_RETURN(op->getNdbError());
2625
    }
unknown's avatar
unknown committed
2626
  }
2627
  dbug_tmp_restore_column_map(table->read_set, old_map);
unknown's avatar
unknown committed
2628

2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644
  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);
  }

2645 2646
  m_rows_changed++;

unknown's avatar
unknown committed
2647 2648 2649 2650 2651 2652 2653
  /*
    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!
  */
2654
  m_rows_inserted++;
2655
  no_uncommitted_rows_update(1);
2656
  m_bulk_insert_not_flushed= TRUE;
2657
  if ((m_rows_to_insert == (ha_rows) 1) || 
2658
      ((m_rows_inserted % m_bulk_insert_rows) == 0) ||
2659
      m_primary_key_update ||
2660
      set_blob_value)
2661 2662 2663
  {
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
2664 2665
                        "rows_inserted:%d, bulk_insert_rows: %d", 
                        (int)m_rows_inserted, (int)m_bulk_insert_rows));
2666

2667
    m_bulk_insert_not_flushed= FALSE;
2668
    if (m_transaction_on)
2669
    {
2670
      if (execute_no_commit(this,trans,false) != 0)
2671
      {
2672 2673 2674
        m_skip_auto_increment= TRUE;
        no_uncommitted_rows_execute_failure();
        DBUG_RETURN(ndb_err(trans));
2675
      }
2676 2677
    }
    else
2678
    {
unknown's avatar
unknown committed
2679
      if (execute_commit(this,trans) != 0)
2680
      {
2681 2682 2683
        m_skip_auto_increment= TRUE;
        no_uncommitted_rows_execute_failure();
        DBUG_RETURN(ndb_err(trans));
2684
      }
unknown's avatar
unknown committed
2685
      if (trans->restart() != 0)
2686
      {
2687 2688
        DBUG_ASSERT(0);
        DBUG_RETURN(-1);
2689
      }
2690
    }
2691
  }
2692
  if ((has_auto_increment) && (m_skip_auto_increment))
unknown's avatar
unknown committed
2693
  {
2694
    Ndb *ndb= get_ndb();
2695
    Uint64 next_val= (Uint64) table->next_number_field->val_int() + 1;
unknown's avatar
unknown committed
2696
    char buff[22];
unknown's avatar
unknown committed
2697
    DBUG_PRINT("info", 
unknown's avatar
unknown committed
2698 2699
               ("Trying to set next auto increment value to %s",
                llstr(next_val, buff)));
2700 2701
    Ndb_tuple_id_range_guard g(m_share);
    if (ndb->setAutoIncrementValue(m_table, g.range, next_val, TRUE)
2702
        == -1)
2703
      ERR_RETURN(ndb->getNdbError());
2704
  }
2705
  m_skip_auto_increment= TRUE;
2706

2707
  DBUG_PRINT("exit",("ok"));
unknown's avatar
unknown committed
2708 2709 2710 2711 2712 2713 2714
  DBUG_RETURN(0);
}


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

int ha_ndbcluster::key_cmp(uint keynr, const byte * old_row,
2715
                           const byte * new_row)
unknown's avatar
unknown committed
2716 2717 2718 2719 2720 2721 2722 2723 2724
{
  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) !=
2725 2726
          (new_row[key_part->null_offset] & key_part->null_bit))
        return 1;
unknown's avatar
unknown committed
2727
    }
2728
    if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
unknown's avatar
unknown committed
2729 2730 2731
    {

      if (key_part->field->cmp_binary((char*) (old_row + key_part->offset),
2732 2733 2734
                                      (char*) (new_row + key_part->offset),
                                      (ulong) key_part->length))
        return 1;
unknown's avatar
unknown committed
2735 2736 2737 2738
    }
    else
    {
      if (memcmp(old_row+key_part->offset, new_row+key_part->offset,
2739 2740
                 key_part->length))
        return 1;
unknown's avatar
unknown committed
2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752
    }
  }
  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;
2753
  NdbTransaction *trans= m_active_trans;
2754
  NdbScanOperation* cursor= m_active_cursor;
unknown's avatar
unknown committed
2755 2756
  NdbOperation *op;
  uint i;
2757 2758
  uint32 old_part_id= 0, new_part_id= 0;
  int error;
2759
  longlong func_value;
unknown's avatar
unknown committed
2760
  DBUG_ENTER("update_row");
2761
  m_write_op= TRUE;
unknown's avatar
unknown committed
2762
  
unknown's avatar
unknown committed
2763
  statistic_increment(thd->status_var.ha_update_count, &LOCK_status);
unknown's avatar
unknown committed
2764
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
2765
  {
unknown's avatar
unknown committed
2766
    table->timestamp_field->set_time();
2767
    bitmap_set_bit(table->write_set, table->timestamp_field->field_index);
2768
  }
unknown's avatar
unknown committed
2769

2770 2771
  if (m_use_partition_function &&
      (error= get_parts_for_update(old_data, new_data, table->record[0],
2772 2773
                                   m_part_info, &old_part_id, &new_part_id,
                                   &func_value)))
2774
  {
2775
    m_part_info->err_value= func_value;
2776 2777 2778
    DBUG_RETURN(error);
  }

2779 2780 2781 2782 2783 2784
  /*
   * 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)) ||
2785
      (old_part_id != new_part_id))
2786
  {
2787
    int read_res, insert_res, delete_res, undo_res;
2788

2789 2790
    DBUG_PRINT("info", ("primary key update or partition change, "
                        "doing read+delete+insert"));
2791
    // Get all old fields, since we optimize away fields not in query
2792
    read_res= complemented_read(old_data, new_data, old_part_id);
2793 2794
    if (read_res)
    {
2795
      DBUG_PRINT("info", ("read failed"));
2796 2797
      DBUG_RETURN(read_res);
    }
2798
    // Delete old row
2799
    m_primary_key_update= TRUE;
2800
    delete_res= delete_row(old_data);
2801
    m_primary_key_update= FALSE;
2802 2803 2804
    if (delete_res)
    {
      DBUG_PRINT("info", ("delete failed"));
2805
      DBUG_RETURN(delete_res);
2806
    }     
2807 2808
    // Insert new row
    DBUG_PRINT("info", ("delete succeded"));
2809
    m_primary_key_update= TRUE;
2810
    insert_res= write_row(new_data);
2811
    m_primary_key_update= FALSE;
2812 2813 2814 2815 2816
    if (insert_res)
    {
      DBUG_PRINT("info", ("insert failed"));
      if (trans->commitStatus() == NdbConnection::Started)
      {
2817
        // Undo delete_row(old_data)
2818
        m_primary_key_update= TRUE;
2819 2820 2821 2822 2823 2824
        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");
2825 2826 2827 2828 2829
        m_primary_key_update= FALSE;
      }
      DBUG_RETURN(insert_res);
    }
    DBUG_PRINT("info", ("delete+insert succeeded"));
2830
    DBUG_RETURN(0);
2831
  }
2832

2833
  if (cursor)
unknown's avatar
unknown committed
2834
  {
2835 2836 2837 2838 2839 2840 2841 2842
    /*
      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"));
2843
    if (!(op= cursor->updateCurrentTuple()))
2844
      ERR_RETURN(trans->getNdbError());
unknown's avatar
unknown committed
2845
    m_lock_tuple= false;
2846
    m_ops_pending++;
2847
    if (uses_blob_value())
2848
      m_blobs_pending= TRUE;
2849 2850
    if (m_use_partition_function)
      cursor->setPartitionId(new_part_id);
2851 2852 2853
  }
  else
  {  
2854
    if (!(op= trans->getNdbOperation(m_table)) ||
2855
        op->updateTuple() != 0)
2856 2857
      ERR_RETURN(trans->getNdbError());  
    
2858 2859
    if (m_use_partition_function)
      op->setPartitionId(new_part_id);
unknown's avatar
unknown committed
2860
    if (table_share->primary_key == MAX_KEY) 
2861 2862 2863 2864 2865
    {
      // 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 
2866 2867
      // read into m_ref
      DBUG_DUMP("key", m_ref, NDB_HIDDEN_PRIMARY_KEY_LENGTH);
2868
      
unknown's avatar
unknown committed
2869
      if (set_hidden_key(op, table->s->fields, m_ref))
2870
        ERR_RETURN(op->getNdbError());
2871 2872 2873 2874
    } 
    else 
    {
      int res;
2875
      if ((res= set_primary_key_from_record(op, old_data)))
2876
        DBUG_RETURN(res);
2877
    }
unknown's avatar
unknown committed
2878 2879
  }

2880 2881
  m_rows_changed++;

unknown's avatar
unknown committed
2882
  // Set non-key attribute(s)
2883
  my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
unknown's avatar
unknown committed
2884
  for (i= 0; i < table_share->fields; i++) 
unknown's avatar
unknown committed
2885 2886
  {
    Field *field= table->field[i];
2887
    if (bitmap_is_set(table->write_set, i) &&
unknown's avatar
unknown committed
2888
        (!(field->flags & PRI_KEY_FLAG)) &&
unknown's avatar
unknown committed
2889
        set_ndb_value(op, field, i, new_data - table->record[0]))
2890 2891
    {
      dbug_tmp_restore_column_map(table->read_set, old_map);
unknown's avatar
unknown committed
2892
      ERR_RETURN(op->getNdbError());
2893
    }
unknown's avatar
unknown committed
2894
  }
2895
  dbug_tmp_restore_column_map(table->read_set, old_map);
2896

2897 2898 2899 2900 2901 2902 2903 2904 2905 2906
  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
2907
  // Execute update operation
2908
  if (!cursor && execute_no_commit(this,trans,false) != 0) {
2909
    no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
2910
    DBUG_RETURN(ndb_err(trans));
2911
  }
unknown's avatar
unknown committed
2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922
  
  DBUG_RETURN(0);
}


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

int ha_ndbcluster::delete_row(const byte *record)
{
unknown's avatar
unknown committed
2923
  THD *thd= current_thd;
2924
  NdbTransaction *trans= m_active_trans;
2925
  NdbScanOperation* cursor= m_active_cursor;
unknown's avatar
unknown committed
2926
  NdbOperation *op;
2927 2928
  uint32 part_id;
  int error;
unknown's avatar
unknown committed
2929
  DBUG_ENTER("delete_row");
2930
  m_write_op= TRUE;
unknown's avatar
unknown committed
2931

unknown's avatar
unknown committed
2932
  statistic_increment(thd->status_var.ha_delete_count,&LOCK_status);
unknown's avatar
unknown committed
2933
  m_rows_changed++;
unknown's avatar
unknown committed
2934

2935 2936 2937 2938 2939 2940 2941
  if (m_use_partition_function &&
      (error= get_part_for_delete(record, table->record[0], m_part_info,
                                  &part_id)))
  {
    DBUG_RETURN(error);
  }

2942
  if (cursor)
unknown's avatar
unknown committed
2943
  {
2944
    /*
2945
      We are scanning records and want to delete the record
2946
      that was just found, call deleteTuple on the cursor 
2947
      to take over the lock to a new delete operation
2948 2949 2950 2951
      And thus setting the primary key of the record from 
      the active record in cursor
    */
    DBUG_PRINT("info", ("Calling deleteTuple on cursor"));
2952
    if (cursor->deleteCurrentTuple() != 0)
2953
      ERR_RETURN(trans->getNdbError());     
unknown's avatar
unknown committed
2954
    m_lock_tuple= false;
2955
    m_ops_pending++;
unknown's avatar
unknown committed
2956

2957 2958 2959
    if (m_use_partition_function)
      cursor->setPartitionId(part_id);

2960 2961
    no_uncommitted_rows_update(-1);

2962 2963 2964
    if (!m_primary_key_update)
      // If deleting from cursor, NoCommit will be handled in next_result
      DBUG_RETURN(0);
2965 2966
  }
  else
unknown's avatar
unknown committed
2967
  {
2968
    
2969
    if (!(op=trans->getNdbOperation(m_table)) || 
2970
        op->deleteTuple() != 0)
2971 2972
      ERR_RETURN(trans->getNdbError());
    
2973 2974 2975
    if (m_use_partition_function)
      op->setPartitionId(part_id);

2976 2977
    no_uncommitted_rows_update(-1);
    
unknown's avatar
unknown committed
2978
    if (table_share->primary_key == MAX_KEY) 
2979 2980 2981 2982
    {
      // This table has no primary key, use "hidden" primary key
      DBUG_PRINT("info", ("Using hidden key"));
      
unknown's avatar
unknown committed
2983
      if (set_hidden_key(op, table->s->fields, m_ref))
2984
        ERR_RETURN(op->getNdbError());
2985 2986 2987
    } 
    else 
    {
2988 2989
      if ((error= set_primary_key_from_record(op, record)))
        DBUG_RETURN(error);
2990
    }
unknown's avatar
unknown committed
2991
  }
2992

unknown's avatar
unknown committed
2993
  // Execute delete operation
2994
  if (execute_no_commit(this,trans,false) != 0) {
2995
    no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
2996
    DBUG_RETURN(ndb_err(trans));
2997
  }
unknown's avatar
unknown committed
2998 2999
  DBUG_RETURN(0);
}
3000
  
unknown's avatar
unknown committed
3001 3002 3003 3004 3005
/*
  Unpack a record read from NDB 

  SYNOPSIS
    unpack_record()
3006
    buf                 Buffer to store read row
unknown's avatar
unknown committed
3007 3008 3009 3010 3011 3012 3013 3014

  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
3015 3016
void ndb_unpack_record(TABLE *table, NdbValue *value,
                       MY_BITMAP *defined, byte *buf)
unknown's avatar
unknown committed
3017
{
unknown's avatar
unknown committed
3018
  Field **p_field= table->field, *field= *p_field;
3019
  my_ptrdiff_t row_offset= (my_ptrdiff_t) (buf - table->record[0]);
3020
  my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
unknown's avatar
unknown committed
3021
  DBUG_ENTER("ndb_unpack_record");
3022

unknown's avatar
unknown committed
3023
  // Set null flag(s)
3024
  bzero(buf, table->s->null_bytes);
unknown's avatar
unknown committed
3025 3026
  for ( ; field;
       p_field++, value++, field= *p_field)
unknown's avatar
unknown committed
3027
  {
unknown's avatar
unknown committed
3028 3029
    if ((*value).ptr)
    {
unknown's avatar
unknown committed
3030
      if (!(field->flags & BLOB_FLAG))
unknown's avatar
unknown committed
3031
      {
unknown's avatar
unknown committed
3032 3033
        int is_null= (*value).rec->isNULL();
        if (is_null)
3034
        {
unknown's avatar
unknown committed
3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050
          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)
        {
3051 3052 3053 3054 3055 3056 3057 3058
          Field_bit *field_bit= static_cast<Field_bit*>(field);

          /*
            Move internal field pointer to point to 'buf'.  Calling
            the correct member function directly since we know the
            type of the object.
           */
          field_bit->Field_bit::move_field_offset(row_offset);
unknown's avatar
unknown committed
3059
          if (field->pack_length() < 5)
3060 3061
          {
            DBUG_PRINT("info", ("bit field H'%.8X", 
3062
                                (*value).rec->u_32_value()));
3063 3064
            field_bit->Field_bit::store((longlong) (*value).rec->u_32_value(),
                                        FALSE);
3065 3066 3067 3068
          }
          else
          {
            DBUG_PRINT("info", ("bit field H'%.8X%.8X",
unknown's avatar
unknown committed
3069 3070
                                *(Uint32*) (*value).rec->aRef(),
                                *((Uint32*) (*value).rec->aRef()+1)));
3071 3072
            field_bit->Field_bit::store((longlong) (*value).rec->u_64_value(), 
                                        TRUE);
3073
          }
3074 3075 3076 3077 3078
          /*
            Move back internal field pointer to point to original
            value (usually record[0]).
           */
          field_bit->Field_bit::move_field_offset(-row_offset);
unknown's avatar
unknown committed
3079 3080
          DBUG_PRINT("info",("[%u] SET",
                             (*value).rec->getColumn()->getColumnNo()));
3081
          DBUG_DUMP("info", (const char*) field->ptr, field->pack_length());
unknown's avatar
unknown committed
3082 3083 3084 3085 3086
        }
        else
        {
          DBUG_PRINT("info",("[%u] SET",
                             (*value).rec->getColumn()->getColumnNo()));
3087
          DBUG_DUMP("info", (const char*) field->ptr, field->pack_length());
3088
        }
unknown's avatar
unknown committed
3089 3090 3091
      }
      else
      {
unknown's avatar
unknown committed
3092
        NdbBlob *ndb_blob= (*value).blob;
unknown's avatar
unknown committed
3093
        uint col_no = ndb_blob->getColumn()->getColumnNo();
3094 3095
        int isNull;
        ndb_blob->getDefined(isNull);
unknown's avatar
unknown committed
3096
        if (isNull == 1)
3097
        {
unknown's avatar
unknown committed
3098
          DBUG_PRINT("info",("[%u] NULL", col_no));
unknown's avatar
unknown committed
3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115
          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
3116
        }
unknown's avatar
unknown committed
3117 3118
      }
    }
unknown's avatar
unknown committed
3119
  }
3120
  dbug_tmp_restore_column_map(table->write_set, old_map);
unknown's avatar
unknown committed
3121 3122 3123 3124 3125 3126
  DBUG_VOID_RETURN;
}

void ha_ndbcluster::unpack_record(byte *buf)
{
  ndb_unpack_record(table, m_value, 0, buf);
unknown's avatar
unknown committed
3127 3128
#ifndef DBUG_OFF
  // Read and print all values that was fetched
unknown's avatar
unknown committed
3129
  if (table_share->primary_key == MAX_KEY)
unknown's avatar
unknown committed
3130 3131
  {
    // Table with hidden primary key
unknown's avatar
unknown committed
3132
    int hidden_no= table_share->fields;
3133
    const NDBTAB *tab= m_table;
3134
    char buff[22];
unknown's avatar
unknown committed
3135
    const NDBCOL *hidden_col= tab->getColumn(hidden_no);
3136
    const NdbRecAttr* rec= m_value[hidden_no].rec;
unknown's avatar
unknown committed
3137
    DBUG_ASSERT(rec);
3138
    DBUG_PRINT("hidden", ("%d: %s \"%s\"", hidden_no,
unknown's avatar
unknown committed
3139
			  hidden_col->getName(),
3140
                          llstr(rec->u_64_value(), buff)));
unknown's avatar
unknown committed
3141 3142
  }
  //DBUG_EXECUTE("value", print_results(););
unknown's avatar
unknown committed
3143 3144 3145 3146 3147
#endif
}

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

    DBUG_EXECUTE("value", print_results(););
unknown's avatar
unknown committed
3151 3152 3153 3154 3155 3156 3157
 */

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

#ifndef DBUG_OFF
unknown's avatar
Merge  
unknown committed
3158

3159
  char buf_type[MAX_FIELD_WIDTH], buf_val[MAX_FIELD_WIDTH];
unknown's avatar
Merge  
unknown committed
3160
  String type(buf_type, sizeof(buf_type), &my_charset_bin);
3161
  String val(buf_val, sizeof(buf_val), &my_charset_bin);
unknown's avatar
unknown committed
3162
  for (uint f= 0; f < table_share->fields; f++)
unknown's avatar
unknown committed
3163
  {
unknown's avatar
Merge  
unknown committed
3164
    /* Use DBUG_PRINT since DBUG_FILE cannot be filtered out */
3165
    char buf[2000];
unknown's avatar
unknown committed
3166
    Field *field;
3167
    void* ptr;
unknown's avatar
unknown committed
3168
    NdbValue value;
unknown's avatar
unknown committed
3169

3170
    buf[0]= 0;
unknown's avatar
Merge  
unknown committed
3171
    field= table->field[f];
unknown's avatar
unknown committed
3172
    if (!(value= m_value[f]).ptr)
unknown's avatar
unknown committed
3173
    {
unknown's avatar
unknown committed
3174
      strmov(buf, "not read");
3175
      goto print_value;
unknown's avatar
unknown committed
3176
    }
3177

3178
    ptr= field->ptr;
unknown's avatar
unknown committed
3179 3180

    if (! (field->flags & BLOB_FLAG))
unknown's avatar
unknown committed
3181
    {
unknown's avatar
unknown committed
3182 3183
      if (value.rec->isNULL())
      {
unknown's avatar
unknown committed
3184
        strmov(buf, "NULL");
3185
        goto print_value;
unknown's avatar
unknown committed
3186
      }
3187 3188 3189 3190 3191
      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
3192 3193 3194
    }
    else
    {
3195
      NdbBlob *ndb_blob= value.blob;
unknown's avatar
unknown committed
3196
      bool isNull= TRUE;
unknown's avatar
unknown committed
3197
      ndb_blob->getNull(isNull);
unknown's avatar
unknown committed
3198 3199
      if (isNull)
        strmov(buf, "NULL");
unknown's avatar
unknown committed
3200
    }
unknown's avatar
Merge  
unknown committed
3201

3202
print_value:
unknown's avatar
Merge  
unknown committed
3203
    DBUG_PRINT("value", ("%u,%s: %s", f, field->field_name, buf));
unknown's avatar
unknown committed
3204 3205 3206 3207 3208 3209
  }
#endif
  DBUG_VOID_RETURN;
}


3210
int ha_ndbcluster::index_init(uint index, bool sorted)
unknown's avatar
unknown committed
3211
{
3212
  DBUG_ENTER("ha_ndbcluster::index_init");
3213 3214 3215
  DBUG_PRINT("enter", ("index: %u  sorted: %d", index, sorted));
  active_index= index;
  m_sorted= sorted;
unknown's avatar
unknown committed
3216 3217 3218 3219 3220 3221 3222
  /*
    Locks are are explicitly released in scan
    unless m_lock.type == TL_READ_HIGH_PRIORITY
    and no sub-sequent call to unlock_row()
  */
  m_lock_tuple= false;
    m_lock_tuple= false;
3223
  DBUG_RETURN(0);
unknown's avatar
unknown committed
3224 3225 3226 3227 3228
}


int ha_ndbcluster::index_end()
{
3229
  DBUG_ENTER("ha_ndbcluster::index_end");
3230
  DBUG_RETURN(close_scan());
unknown's avatar
unknown committed
3231 3232
}

3233 3234 3235 3236 3237 3238 3239 3240
/**
 * 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;
3241
  const byte* end_ptr= key + key_len;
3242 3243 3244 3245 3246 3247
  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
3248
    if (curr_part->null_bit && *key)
3249 3250 3251 3252 3253 3254
      return 1;

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

int ha_ndbcluster::index_read(byte *buf,
3257 3258
                              const byte *key, uint key_len, 
                              enum ha_rkey_function find_flag)
unknown's avatar
unknown committed
3259
{
3260 3261
  key_range start_key;
  bool descending= FALSE;
3262
  DBUG_ENTER("ha_ndbcluster::index_read");
unknown's avatar
unknown committed
3263 3264 3265
  DBUG_PRINT("enter", ("active_index: %u, key_len: %u, find_flag: %d", 
                       active_index, key_len, find_flag));

3266 3267 3268
  start_key.key= key;
  start_key.length= key_len;
  start_key.flag= find_flag;
3269
  descending= FALSE;
3270 3271 3272 3273 3274 3275 3276 3277 3278 3279
  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;
  }
3280 3281
  DBUG_RETURN(read_range_first_to_buf(&start_key, 0, descending,
                                      m_sorted, buf));
unknown's avatar
unknown committed
3282 3283 3284 3285
}


int ha_ndbcluster::index_read_idx(byte *buf, uint index_no, 
3286 3287
                              const byte *key, uint key_len, 
                              enum ha_rkey_function find_flag)
unknown's avatar
unknown committed
3288
{
unknown's avatar
unknown committed
3289
  statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status);
3290
  DBUG_ENTER("ha_ndbcluster::index_read_idx");
unknown's avatar
unknown committed
3291
  DBUG_PRINT("enter", ("index_no: %u, key_len: %u", index_no, key_len));  
unknown's avatar
unknown committed
3292
  close_scan();
3293
  index_init(index_no, 0);  
unknown's avatar
unknown committed
3294 3295 3296 3297 3298 3299
  DBUG_RETURN(index_read(buf, key, key_len, find_flag));
}


int ha_ndbcluster::index_next(byte *buf)
{
3300
  DBUG_ENTER("ha_ndbcluster::index_next");
unknown's avatar
unknown committed
3301
  statistic_increment(current_thd->status_var.ha_read_next_count,
3302
                      &LOCK_status);
3303
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
3304 3305 3306 3307 3308
}


int ha_ndbcluster::index_prev(byte *buf)
{
3309
  DBUG_ENTER("ha_ndbcluster::index_prev");
unknown's avatar
unknown committed
3310
  statistic_increment(current_thd->status_var.ha_read_prev_count,
3311
                      &LOCK_status);
3312
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
3313 3314 3315 3316 3317
}


int ha_ndbcluster::index_first(byte *buf)
{
3318
  DBUG_ENTER("ha_ndbcluster::index_first");
unknown's avatar
unknown committed
3319
  statistic_increment(current_thd->status_var.ha_read_first_count,
3320
                      &LOCK_status);
unknown's avatar
unknown committed
3321 3322 3323
  // Start the ordered index scan and fetch the first row

  // Only HA_READ_ORDER indexes get called by index_first
3324
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, FALSE, buf, NULL));
unknown's avatar
unknown committed
3325 3326 3327 3328 3329
}


int ha_ndbcluster::index_last(byte *buf)
{
3330
  DBUG_ENTER("ha_ndbcluster::index_last");
3331
  statistic_increment(current_thd->status_var.ha_read_last_count,&LOCK_status);
3332
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, TRUE, buf, NULL));
unknown's avatar
unknown committed
3333 3334
}

3335 3336 3337 3338 3339
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
3340

3341
int ha_ndbcluster::read_range_first_to_buf(const key_range *start_key,
3342
                                           const key_range *end_key,
3343
                                           bool desc, bool sorted,
3344
                                           byte* buf)
3345
{
3346 3347 3348 3349
  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; 
3350
  DBUG_ENTER("ha_ndbcluster::read_range_first_to_buf");
3351
  DBUG_PRINT("info", ("desc: %d, sorted: %d", desc, sorted));
3352

3353 3354 3355
  if (m_use_partition_function)
  {
    get_partition_set(table, buf, active_index, start_key, &part_spec);
3356 3357 3358 3359 3360 3361 3362 3363
    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.
    */
3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377
    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;
    }
  }
3378

3379 3380
  m_write_op= FALSE;
  switch (type){
3381
  case PRIMARY_KEY_ORDERED_INDEX:
3382
  case PRIMARY_KEY_INDEX:
3383
    if (start_key && 
3384 3385
        start_key->length == key_info->key_length &&
        start_key->flag == HA_READ_KEY_EXACT)
3386
    {
unknown's avatar
unknown committed
3387
      if (m_active_cursor && (error= close_scan()))
3388
        DBUG_RETURN(error);
3389 3390
      DBUG_RETURN(pk_read(start_key->key, start_key->length, buf,
                          part_spec.start_part));
3391
    }
3392
    break;
3393
  case UNIQUE_ORDERED_INDEX:
3394
  case UNIQUE_INDEX:
3395
    if (start_key && start_key->length == key_info->key_length &&
3396 3397
        start_key->flag == HA_READ_KEY_EXACT && 
        !check_null_in_key(key_info, start_key->key, start_key->length))
3398
    {
unknown's avatar
unknown committed
3399
      if (m_active_cursor && (error= close_scan()))
3400
        DBUG_RETURN(error);
3401
      DBUG_RETURN(unique_index_read(start_key->key, start_key->length, buf));
3402
    }
3403 3404 3405 3406
    break;
  default:
    break;
  }
3407
  // Start the ordered index scan and fetch the first row
3408 3409
  DBUG_RETURN(ordered_index_scan(start_key, end_key, sorted, desc, buf,
                                 &part_spec));
3410 3411
}

unknown's avatar
unknown committed
3412
int ha_ndbcluster::read_range_first(const key_range *start_key,
3413 3414
                                    const key_range *end_key,
                                    bool eq_r, bool sorted)
unknown's avatar
unknown committed
3415 3416 3417
{
  byte* buf= table->record[0];
  DBUG_ENTER("ha_ndbcluster::read_range_first");
3418 3419
  DBUG_RETURN(read_range_first_to_buf(start_key, end_key, FALSE,
                                      sorted, buf));
unknown's avatar
unknown committed
3420 3421
}

3422
int ha_ndbcluster::read_range_next()
3423 3424 3425 3426 3427 3428
{
  DBUG_ENTER("ha_ndbcluster::read_range_next");
  DBUG_RETURN(next_result(table->record[0]));
}


unknown's avatar
unknown committed
3429 3430
int ha_ndbcluster::rnd_init(bool scan)
{
3431
  NdbScanOperation *cursor= m_active_cursor;
unknown's avatar
unknown committed
3432 3433
  DBUG_ENTER("rnd_init");
  DBUG_PRINT("enter", ("scan: %d", scan));
3434
  // Check if scan is to be restarted
unknown's avatar
unknown committed
3435 3436 3437 3438
  if (cursor)
  {
    if (!scan)
      DBUG_RETURN(1);
unknown's avatar
unknown committed
3439
    if (cursor->restart(m_force_send) != 0)
3440 3441 3442 3443
    {
      DBUG_ASSERT(0);
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
3444
  }
unknown's avatar
unknown committed
3445
  index_init(table_share->primary_key, 0);
unknown's avatar
unknown committed
3446 3447 3448
  DBUG_RETURN(0);
}

3449 3450
int ha_ndbcluster::close_scan()
{
3451
  NdbTransaction *trans= m_active_trans;
3452 3453
  DBUG_ENTER("close_scan");

unknown's avatar
unknown committed
3454 3455
  m_multi_cursor= 0;
  if (!m_active_cursor && !m_multi_cursor)
3456 3457
    DBUG_RETURN(1);

unknown's avatar
unknown committed
3458
  NdbScanOperation *cursor= m_active_cursor ? m_active_cursor : m_multi_cursor;
unknown's avatar
unknown committed
3459
  
3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471
  if (m_lock_tuple)
  {
    /*
      Lock level m_lock.type either TL_WRITE_ALLOW_WRITE
      (SELECT FOR UPDATE) or TL_READ_WITH_SHARED_LOCKS (SELECT
      LOCK WITH SHARE MODE) and row was not explictly unlocked 
      with unlock_row() call
    */
      NdbOperation *op;
      // Lock row
      DBUG_PRINT("info", ("Keeping lock on scanned row"));
      
3472
      if (!(op= cursor->lockCurrentTuple()))
3473 3474 3475 3476 3477 3478
      {
	m_lock_tuple= false;
	ERR_RETURN(trans->getNdbError());
      }
      m_ops_pending++;      
  }
3479
  m_lock_tuple= false;
3480
  if (m_ops_pending)
unknown's avatar
unknown committed
3481 3482 3483 3484 3485
  {
    /*
      Take over any pending transactions to the 
      deleteing/updating transaction before closing the scan    
    */
3486
    DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending));    
3487
    if (execute_no_commit(this,trans,false) != 0) {
3488
      no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
3489
      DBUG_RETURN(ndb_err(trans));
3490
    }
3491
    m_ops_pending= 0;
unknown's avatar
unknown committed
3492 3493
  }
  
unknown's avatar
unknown committed
3494
  cursor->close(m_force_send, TRUE);
unknown's avatar
unknown committed
3495
  m_active_cursor= m_multi_cursor= NULL;
unknown's avatar
unknown committed
3496
  DBUG_RETURN(0);
3497
}
unknown's avatar
unknown committed
3498 3499 3500 3501

int ha_ndbcluster::rnd_end()
{
  DBUG_ENTER("rnd_end");
3502
  DBUG_RETURN(close_scan());
unknown's avatar
unknown committed
3503 3504 3505 3506 3507 3508
}


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

unknown's avatar
unknown committed
3512
  if (!m_active_cursor)
3513 3514
    DBUG_RETURN(full_table_scan(buf));
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527
}


/*
  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
3528
  statistic_increment(current_thd->status_var.ha_read_rnd_count,
3529
                      &LOCK_status);
unknown's avatar
unknown committed
3530 3531
  // The primary key for the record is stored in pos
  // Perform a pk_read using primary key "index"
3532 3533
  {
    part_id_range part_spec;
3534
    uint key_length= ref_length;
3535 3536
    if (m_use_partition_function)
    {
3537 3538 3539 3540 3541 3542
      if (table_share->primary_key == MAX_KEY)
      {
        /*
          The partition id has been fetched from ndb
          and has been stored directly after the hidden key
        */
unknown's avatar
unknown committed
3543
        DBUG_DUMP("key+part", (char *)pos, key_length);
3544
        key_length= ref_length - sizeof(m_part_id);
3545
        part_spec.start_part= part_spec.end_part= *(uint32 *)(pos + key_length);
3546 3547 3548 3549
      }
      else
      {
        key_range key_spec;
3550
        KEY *key_info= table->key_info + table_share->primary_key;
3551 3552 3553 3554 3555 3556 3557 3558
        key_spec.key= pos;
        key_spec.length= key_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_PRINT("info", ("partition id %u", part_spec.start_part));
3559
    }
unknown's avatar
unknown committed
3560
    DBUG_DUMP("key", (char *)pos, key_length);
3561
    DBUG_RETURN(pk_read(pos, key_length, buf, part_spec.start_part));
3562
  }
unknown's avatar
unknown committed
3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577
}


/*
  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;
3578 3579
  uint key_length;

unknown's avatar
unknown committed
3580 3581
  DBUG_ENTER("position");

unknown's avatar
unknown committed
3582
  if (table_share->primary_key != MAX_KEY) 
unknown's avatar
unknown committed
3583
  {
3584
    key_length= ref_length;
unknown's avatar
unknown committed
3585
    key_info= table->key_info + table_share->primary_key;
unknown's avatar
unknown committed
3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600
    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;
      }
3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620

      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
3621 3622 3623 3624 3625 3626
    }
  } 
  else 
  {
    // No primary key, get hidden key
    DBUG_PRINT("info", ("Getting hidden key"));
3627 3628 3629
    // If table has user defined partition save the partition id as well
    if(m_use_partition_function)
    {
unknown's avatar
unknown committed
3630
      DBUG_PRINT("info", ("Saving partition id %u", m_part_id));
3631 3632 3633
      key_length= ref_length - sizeof(m_part_id);
      memcpy(ref+key_length, (void *)&m_part_id, sizeof(m_part_id));
    }
3634 3635
    else
      key_length= ref_length;
3636
#ifndef DBUG_OFF
3637
    int hidden_no= table->s->fields;
3638
    const NDBTAB *tab= m_table;  
unknown's avatar
unknown committed
3639 3640 3641
    const NDBCOL *hidden_col= tab->getColumn(hidden_no);
    DBUG_ASSERT(hidden_col->getPrimaryKey() && 
                hidden_col->getAutoIncrement() &&
3642
                key_length == NDB_HIDDEN_PRIMARY_KEY_LENGTH);
3643
#endif
3644
    memcpy(ref, m_ref, key_length);
unknown's avatar
unknown committed
3645
  }
unknown's avatar
unknown committed
3646 3647 3648 3649
#ifndef DBUG_OFF
  if (table_share->primary_key == MAX_KEY && m_use_partition_function) 
    DBUG_DUMP("key+part", (char*)ref, key_length+sizeof(m_part_id));
#endif
3650
  DBUG_DUMP("ref", (char*)ref, key_length);
unknown's avatar
unknown committed
3651 3652 3653 3654
  DBUG_VOID_RETURN;
}


3655
int ha_ndbcluster::info(uint flag)
unknown's avatar
unknown committed
3656
{
3657
  int result= 0;
unknown's avatar
unknown committed
3658 3659 3660 3661 3662 3663 3664 3665 3666 3667
  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)
3668
  {
unknown's avatar
unknown committed
3669
    DBUG_PRINT("info", ("HA_STATUS_VARIABLE"));
3670 3671
    if (m_table_info)
    {
3672
      if (m_ha_not_exact_count)
3673
        stats.records= 100;
3674
      else
3675
	result= records_update();
3676 3677 3678
    }
    else
    {
3679
      if ((my_errno= check_ndb_connection()))
3680
        DBUG_RETURN(my_errno);
3681
      Ndb *ndb= get_ndb();
3682
      ndb->setDatabaseName(m_dbname);
3683
      struct Ndb_statistics stat;
3684
      ndb->setDatabaseName(m_dbname);
3685
      if (current_thd->variables.ndb_use_exact_count &&
3686
          (result= ndb_get_table_statistics(ndb, m_table, &stat)) == 0)
3687
      {
3688 3689 3690
        stats.mean_rec_length= stat.row_size;
        stats.data_file_length= stat.fragment_memory;
        stats.records= stat.row_count;
3691 3692 3693
      }
      else
      {
3694 3695
        stats.mean_rec_length= 0;
        stats.records= 100;
3696
      }
3697
    }
3698
  }
unknown's avatar
unknown committed
3699 3700 3701 3702 3703
  if (flag & HA_STATUS_CONST)
  {
    DBUG_PRINT("info", ("HA_STATUS_CONST"));
    set_rec_per_key();
  }
unknown's avatar
unknown committed
3704
  if (flag & HA_STATUS_ERRKEY)
3705
  {
unknown's avatar
unknown committed
3706
    DBUG_PRINT("info", ("HA_STATUS_ERRKEY"));
3707
    errkey= m_dupkey;
3708
  }
unknown's avatar
unknown committed
3709
  if (flag & HA_STATUS_AUTO)
3710
  {
unknown's avatar
unknown committed
3711
    DBUG_PRINT("info", ("HA_STATUS_AUTO"));
3712 3713 3714
    if (m_table)
    {
      Ndb *ndb= get_ndb();
3715
      Ndb_tuple_id_range_guard g(m_share);
3716
      
3717
      Uint64 auto_increment_value64;
3718
      if (ndb->readAutoIncrementValue(m_table, g.range,
3719
                                      auto_increment_value64) == -1)
3720 3721 3722 3723
      {
        const NdbError err= ndb->getNdbError();
        sql_print_error("Error %lu in readAutoIncrementValue(): %s",
                        (ulong) err.code, err.message);
3724
        stats.auto_increment_value= ~(ulonglong)0;
3725
      }
3726
      else
3727
        stats.auto_increment_value= (ulonglong)auto_increment_value64;
3728 3729
    }
  }
3730 3731 3732 3733 3734

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

  DBUG_RETURN(result);
unknown's avatar
unknown committed
3735 3736
}

3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750

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
3751 3752 3753 3754 3755 3756
int ha_ndbcluster::extra(enum ha_extra_function operation)
{
  DBUG_ENTER("extra");
  switch (operation) {
  case HA_EXTRA_IGNORE_DUP_KEY:       /* Dup keys don't rollback everything*/
    DBUG_PRINT("info", ("HA_EXTRA_IGNORE_DUP_KEY"));
3757 3758
    DBUG_PRINT("info", ("Ignoring duplicate key"));
    m_ignore_dup_key= TRUE;
unknown's avatar
unknown committed
3759 3760 3761
    break;
  case HA_EXTRA_NO_IGNORE_DUP_KEY:
    DBUG_PRINT("info", ("HA_EXTRA_NO_IGNORE_DUP_KEY"));
3762
    m_ignore_dup_key= FALSE;
unknown's avatar
unknown committed
3763
    break;
unknown's avatar
unknown committed
3764 3765 3766 3767 3768 3769 3770 3771 3772 3773
  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;
3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786
  case HA_EXTRA_WRITE_CAN_REPLACE:
    DBUG_PRINT("info", ("HA_EXTRA_WRITE_CAN_REPLACE"));
    if (!m_has_unique_index)
    {
      DBUG_PRINT("info", ("Turning ON use of write instead of insert"));
      m_use_write= TRUE;
    }
    break;
  case HA_EXTRA_WRITE_CANNOT_REPLACE:
    DBUG_PRINT("info", ("HA_EXTRA_WRITE_CANNOT_REPLACE"));
    DBUG_PRINT("info", ("Turning OFF use of write instead of insert"));
    m_use_write= FALSE;
    break;
3787 3788
  default:
    break;
unknown's avatar
unknown committed
3789 3790 3791 3792 3793
  }
  
  DBUG_RETURN(0);
}

3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809

int ha_ndbcluster::reset()
{
  DBUG_ENTER("ha_ndbcluster::reset");
  cond_clear();
  /*
    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
  */
  if (m_part_info)
    bitmap_set_all(&m_part_info->used_partitions);
  DBUG_RETURN(0);
}


3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822
/* 
   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;
3823
  const NDBTAB *tab= m_table;    
3824 3825

  DBUG_ENTER("start_bulk_insert");
unknown's avatar
unknown committed
3826
  DBUG_PRINT("enter", ("rows: %d", (int)rows));
3827
  
3828
  m_rows_inserted= (ha_rows) 0;
3829
  if (!m_use_write && m_ignore_dup_key)
3830 3831 3832
  {
    /*
      compare if expression with that in write_row
3833
      we have a situation where peek_indexed_rows() will be called
3834 3835 3836 3837 3838 3839 3840 3841
      so we cannot batch
    */
    DBUG_PRINT("info", ("Batching turned off as duplicate key is "
                        "ignored by using peek_row"));
    m_rows_to_insert= 1;
    m_bulk_insert_rows= 1;
    DBUG_VOID_RETURN;
  }
3842
  if (rows == (ha_rows) 0)
unknown's avatar
unknown committed
3843
  {
3844 3845
    /* We don't know how many will be inserted, guess */
    m_rows_to_insert= m_autoincrement_prefetch;
unknown's avatar
unknown committed
3846
  }
3847 3848
  else
    m_rows_to_insert= rows; 
3849 3850 3851 3852 3853 3854 3855 3856

  /* 
    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.   
  */
3857
  const int bytesperbatch= 8192;
3858
  bytes= 12 + tab->getRowSizeInBytes() + 4 * tab->getNoOfColumns();
3859
  batch= bytesperbatch/bytes;
3860 3861
  batch= batch == 0 ? 1 : batch;
  DBUG_PRINT("info", ("batch: %d, bytes: %d", batch, bytes));
3862
  m_bulk_insert_rows= batch;
3863 3864 3865 3866 3867 3868 3869 3870 3871

  DBUG_VOID_RETURN;
}

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

3874
  DBUG_ENTER("end_bulk_insert");
3875
  // Check if last inserts need to be flushed
3876
  if (m_bulk_insert_not_flushed)
3877
  {
3878
    NdbTransaction *trans= m_active_trans;
3879 3880 3881
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
                        "rows_inserted:%d, bulk_insert_rows: %d", 
3882
                        (int) m_rows_inserted, (int) m_bulk_insert_rows)); 
3883
    m_bulk_insert_not_flushed= FALSE;
3884 3885
    if (m_transaction_on)
    {
3886
      if (execute_no_commit(this, trans,false) != 0)
3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903
      {
        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);
      }
3904
    }
3905 3906
  }

3907 3908
  m_rows_inserted= (ha_rows) 0;
  m_rows_to_insert= (ha_rows) 1;
3909
  DBUG_RETURN(error);
3910 3911
}

unknown's avatar
unknown committed
3912 3913 3914 3915

int ha_ndbcluster::extra_opt(enum ha_extra_function operation, ulong cache_size)
{
  DBUG_ENTER("extra_opt");
unknown's avatar
unknown committed
3916
  DBUG_PRINT("enter", ("cache_size: %lu", cache_size));
unknown's avatar
unknown committed
3917 3918 3919
  DBUG_RETURN(extra(operation));
}

unknown's avatar
unknown committed
3920 3921 3922 3923
static const char *ha_ndbcluster_exts[] = {
 ha_ndb_ext,
 NullS
};
unknown's avatar
unknown committed
3924

3925
const char** ha_ndbcluster::bas_ext() const
unknown's avatar
unknown committed
3926 3927 3928
{
  return ha_ndbcluster_exts;
}
unknown's avatar
unknown committed
3929 3930 3931 3932 3933 3934 3935 3936 3937

/*
  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
3938
  DBUG_ENTER("ha_ndbcluster::scan_time()");
3939
  double res= rows2double(stats.records*1000);
unknown's avatar
unknown committed
3940
  DBUG_PRINT("exit", ("table: %s value: %f", 
3941
                      m_tabname, res));
unknown's avatar
unknown committed
3942
  DBUG_RETURN(res);
unknown's avatar
unknown committed
3943 3944
}

unknown's avatar
unknown committed
3945 3946 3947 3948 3949 3950 3951
/*
  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
3952 3953 3954 3955 3956 3957 3958 3959

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
3960

unknown's avatar
unknown committed
3961 3962 3963
    /* If we are not doing a LOCK TABLE, then allow multiple
       writers */
    
3964 3965 3966
    /* Since NDB does not currently have table locks
       this is treated as a ordinary lock */

3967
    if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
unknown's avatar
unknown committed
3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982
         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;
3983 3984

  DBUG_PRINT("exit", ("lock_type: %d", lock_type));
unknown's avatar
unknown committed
3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006
  
  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
4007
  for the statement, this will be stored in thd_ndb.stmt.
unknown's avatar
unknown committed
4008
  If not, we have to start a master transaction if there doesn't exist
4009
  one from before, this will be stored in thd_ndb.all
unknown's avatar
unknown committed
4010 4011 4012
 
  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  
4013
  If we are locking the table then:
4014
  - save the NdbDictionary::Table for easy access
4015 4016
  - save reference to table statistics
  - refresh list of the indexes for the table if needed (if altered)
unknown's avatar
unknown committed
4017 4018 4019 4020 4021
 */

int ha_ndbcluster::external_lock(THD *thd, int lock_type)
{
  int error=0;
4022
  NdbTransaction* trans= NULL;
unknown's avatar
unknown committed
4023
  DBUG_ENTER("external_lock");
4024

unknown's avatar
unknown committed
4025 4026 4027 4028
  /*
    Check that this handler instance has a connection
    set up to the Ndb object of thd
   */
4029
  if (check_ndb_connection(thd))
unknown's avatar
unknown committed
4030
    DBUG_RETURN(1);
4031

4032
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
4033
  Ndb *ndb= thd_ndb->ndb;
4034

4035
  DBUG_PRINT("enter", ("this: 0x%lx  thd: 0x%lx  thd_ndb: %lx  "
unknown's avatar
unknown committed
4036
                       "thd_ndb->lock_count: %d",
4037 4038
                       (long) this, (long) thd, (long) thd_ndb,
                       thd_ndb->lock_count));
4039

unknown's avatar
unknown committed
4040 4041
  if (lock_type != F_UNLCK)
  {
4042
    DBUG_PRINT("info", ("lock_type != F_UNLCK"));
4043 4044 4045 4046 4047 4048 4049 4050
    if (thd->lex->sql_command == SQLCOM_LOAD)
    {
      m_transaction_on= FALSE;
      /* Would be simpler if has_transactions() didn't always say "yes" */
      thd->options|= OPTION_STATUS_NO_TRANS_UPDATE;
      thd->no_trans_update= TRUE;
    }
    else if (!thd->transaction.on)
4051 4052 4053
      m_transaction_on= FALSE;
    else
      m_transaction_on= thd->variables.ndb_use_transactions;
4054
    if (!thd_ndb->lock_count++)
unknown's avatar
unknown committed
4055 4056
    {
      PRINT_OPTION_FLAGS(thd);
4057
      if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) 
unknown's avatar
unknown committed
4058 4059
      {
        // Autocommit transaction
4060
        DBUG_ASSERT(!thd_ndb->stmt);
unknown's avatar
unknown committed
4061 4062
        DBUG_PRINT("trans",("Starting transaction stmt"));      

4063
        trans= ndb->startTransaction();
unknown's avatar
unknown committed
4064
        if (trans == NULL)
4065
          ERR_RETURN(ndb->getNdbError());
4066
        thd_ndb->init_open_tables();
4067
        thd_ndb->stmt= trans;
4068
	thd_ndb->query_state&= NDB_QUERY_NORMAL;
4069
        trans_register_ha(thd, FALSE, ndbcluster_hton);
unknown's avatar
unknown committed
4070 4071 4072
      } 
      else 
      { 
4073
        if (!thd_ndb->all)
4074
        {
unknown's avatar
unknown committed
4075 4076 4077 4078
          // Not autocommit transaction
          // A "master" transaction ha not been started yet
          DBUG_PRINT("trans",("starting transaction, all"));
          
4079
          trans= ndb->startTransaction();
unknown's avatar
unknown committed
4080
          if (trans == NULL)
4081
            ERR_RETURN(ndb->getNdbError());
4082
          thd_ndb->init_open_tables();
4083
          thd_ndb->all= trans; 
4084
	  thd_ndb->query_state&= NDB_QUERY_NORMAL;
4085
          trans_register_ha(thd, TRUE, ndbcluster_hton);
unknown's avatar
unknown committed
4086 4087 4088 4089 4090 4091 4092 4093

          /*
            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))
4094
          {
unknown's avatar
unknown committed
4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113
            //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. 
     */

4114 4115 4116
    // 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;
4117 4118
    m_autoincrement_prefetch= 
      (ha_rows) thd->variables.ndb_autoincrement_prefetch_sz;
4119

4120
    m_active_trans= thd_ndb->all ? thd_ndb->all : thd_ndb->stmt;
unknown's avatar
unknown committed
4121
    DBUG_ASSERT(m_active_trans);
4122
    // Start of transaction
4123 4124
    m_rows_changed= 0;
    m_ops_pending= 0;
4125 4126

    // TODO remove double pointers...
4127 4128
    m_thd_ndb_share= thd_ndb->get_open_table(thd, m_table);
    m_table_info= &m_thd_ndb_share->stat;
4129 4130
  }
  else
unknown's avatar
unknown committed
4131
  {
4132
    DBUG_PRINT("info", ("lock_type == F_UNLCK"));
4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150

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

4151
    if (!--thd_ndb->lock_count)
unknown's avatar
unknown committed
4152 4153 4154 4155
    {
      DBUG_PRINT("trans", ("Last external_lock"));
      PRINT_OPTION_FLAGS(thd);

4156
      if (thd_ndb->stmt)
unknown's avatar
unknown committed
4157 4158 4159 4160 4161 4162 4163
      {
        /*
          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"));
4164
        ndb->closeTransaction(m_active_trans);
4165
        thd_ndb->stmt= NULL;
unknown's avatar
unknown committed
4166 4167
      }
    }
unknown's avatar
unknown committed
4168
    m_table_info= NULL;
4169

4170 4171 4172 4173 4174 4175 4176 4177 4178
    /*
      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;    

4179 4180
    if (m_active_cursor)
      DBUG_PRINT("warning", ("m_active_cursor != NULL"));
4181 4182
    m_active_cursor= NULL;

unknown's avatar
unknown committed
4183 4184 4185 4186
    if (m_multi_cursor)
      DBUG_PRINT("warning", ("m_multi_cursor != NULL"));
    m_multi_cursor= NULL;
    
4187
    if (m_blobs_pending)
4188
      DBUG_PRINT("warning", ("blobs_pending != 0"));
4189
    m_blobs_pending= 0;
4190
    
4191
    if (m_ops_pending)
4192
      DBUG_PRINT("warning", ("ops_pending != 0L"));
4193
    m_ops_pending= 0;
unknown's avatar
unknown committed
4194
  }
4195
  thd->set_current_stmt_binlog_row_based_if_mixed();
unknown's avatar
unknown committed
4196 4197 4198
  DBUG_RETURN(error);
}

unknown's avatar
unknown committed
4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214
/*
  Unlock the last row read in an open scan.
  Rows are unlocked by default in ndb, but
  for SELECT FOR UPDATE and SELECT LOCK WIT SHARE MODE
  locks are kept if unlock_row() is not called.
*/

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

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

unknown's avatar
unknown committed
4215
/*
4216 4217 4218 4219 4220
  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
4221 4222
*/

unknown's avatar
unknown committed
4223
int ha_ndbcluster::start_stmt(THD *thd, thr_lock_type lock_type)
unknown's avatar
unknown committed
4224 4225 4226 4227 4228
{
  int error=0;
  DBUG_ENTER("start_stmt");
  PRINT_OPTION_FLAGS(thd);

4229
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
4230
  NdbTransaction *trans= (thd_ndb->stmt)?thd_ndb->stmt:thd_ndb->all;
unknown's avatar
unknown committed
4231
  if (!trans){
4232
    Ndb *ndb= thd_ndb->ndb;
unknown's avatar
unknown committed
4233
    DBUG_PRINT("trans",("Starting transaction stmt"));  
4234
    trans= ndb->startTransaction();
unknown's avatar
unknown committed
4235
    if (trans == NULL)
4236
      ERR_RETURN(ndb->getNdbError());
4237
    no_uncommitted_rows_reset(thd);
4238
    thd_ndb->stmt= trans;
4239
    trans_register_ha(thd, FALSE, ndbcluster_hton);
unknown's avatar
unknown committed
4240
  }
4241
  thd_ndb->query_state&= NDB_QUERY_NORMAL;
unknown's avatar
unknown committed
4242
  m_active_trans= trans;
4243

4244
  // Start of statement
4245
  m_ops_pending= 0;    
4246 4247
  thd->set_current_stmt_binlog_row_based_if_mixed();

unknown's avatar
unknown committed
4248 4249 4250 4251 4252
  DBUG_RETURN(error);
}


/*
4253
  Commit a transaction started in NDB
unknown's avatar
unknown committed
4254 4255
 */

4256
static int ndbcluster_commit(handlerton *hton, THD *thd, bool all)
unknown's avatar
unknown committed
4257 4258
{
  int res= 0;
4259 4260 4261
  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
4262 4263 4264

  DBUG_ENTER("ndbcluster_commit");
  DBUG_PRINT("transaction",("%s",
4265
                            trans == thd_ndb->stmt ?
unknown's avatar
unknown committed
4266 4267 4268
                            "stmt" : "all"));
  DBUG_ASSERT(ndb && trans);

4269
  if (execute_commit(thd,trans) != 0)
unknown's avatar
unknown committed
4270 4271
  {
    const NdbError err= trans->getNdbError();
4272
    const NdbOperation *error_op= trans->getNdbErrorOperation();
4273
    ERR_PRINT(err);
unknown's avatar
unknown committed
4274
    res= ndb_to_mysql_error(&err);
4275
    if (res != -1)
4276
      ndbcluster_print_error(res, error_op);
unknown's avatar
unknown committed
4277
  }
4278
  ndb->closeTransaction(trans);
4279

unknown's avatar
unknown committed
4280
  if (all)
4281 4282 4283
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;
4284 4285 4286 4287 4288 4289 4290

  /* 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
4291 4292
    DBUG_PRINT("info", ("Invalidate commit_count for %s, share->commit_count: %d ",
			share->key, share->commit_count));
4293 4294 4295 4296 4297 4298
    share->commit_count= 0;
    share->commit_count_lock++;
    pthread_mutex_unlock(&share->mutex);
  }
  thd_ndb->changed_tables.empty();

unknown's avatar
unknown committed
4299 4300 4301 4302 4303 4304 4305 4306
  DBUG_RETURN(res);
}


/*
  Rollback a transaction started in NDB
 */

4307
static int ndbcluster_rollback(handlerton *hton, THD *thd, bool all)
unknown's avatar
unknown committed
4308 4309
{
  int res= 0;
4310 4311 4312
  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
4313 4314 4315

  DBUG_ENTER("ndbcluster_rollback");
  DBUG_PRINT("transaction",("%s",
4316
                            trans == thd_ndb->stmt ? 
unknown's avatar
unknown committed
4317 4318 4319
                            "stmt" : "all"));
  DBUG_ASSERT(ndb && trans);

4320
  if (trans->execute(NdbTransaction::Rollback) != 0)
unknown's avatar
unknown committed
4321 4322
  {
    const NdbError err= trans->getNdbError();
4323
    const NdbOperation *error_op= trans->getNdbErrorOperation();
unknown's avatar
unknown committed
4324 4325
    ERR_PRINT(err);     
    res= ndb_to_mysql_error(&err);
4326 4327
    if (res != -1) 
      ndbcluster_print_error(res, error_op);
unknown's avatar
unknown committed
4328 4329
  }
  ndb->closeTransaction(trans);
4330

unknown's avatar
unknown committed
4331
  if (all)
4332 4333 4334 4335
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;

4336 4337 4338
  /* Clear list of tables changed by transaction */
  thd_ndb->changed_tables.empty();

4339
  DBUG_RETURN(res);
unknown's avatar
unknown committed
4340 4341 4342 4343
}


/*
unknown's avatar
unknown committed
4344 4345 4346
  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
4347 4348 4349

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

unknown's avatar
unknown committed
4352 4353 4354
static int create_ndb_column(NDBCOL &col,
                             Field *field,
                             HA_CREATE_INFO *info)
unknown's avatar
unknown committed
4355
{
unknown's avatar
unknown committed
4356
  // Set name
unknown's avatar
unknown committed
4357
  col.setName(field->field_name);
unknown's avatar
unknown committed
4358 4359
  // Get char set
  CHARSET_INFO *cs= field->charset();
unknown's avatar
unknown committed
4360 4361 4362 4363
  // Set type and sizes
  const enum enum_field_types mysql_type= field->real_type();
  switch (mysql_type) {
  // Numeric types
unknown's avatar
unknown committed
4364
  case MYSQL_TYPE_TINY:        
unknown's avatar
unknown committed
4365 4366 4367 4368 4369 4370
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Tinyunsigned);
    else
      col.setType(NDBCOL::Tinyint);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
4371
  case MYSQL_TYPE_SHORT:
unknown's avatar
unknown committed
4372 4373 4374 4375 4376 4377
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Smallunsigned);
    else
      col.setType(NDBCOL::Smallint);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
4378
  case MYSQL_TYPE_LONG:
unknown's avatar
unknown committed
4379 4380 4381 4382 4383 4384
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Unsigned);
    else
      col.setType(NDBCOL::Int);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
4385
  case MYSQL_TYPE_INT24:       
unknown's avatar
unknown committed
4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397
    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
4398 4399
    break;
  case MYSQL_TYPE_FLOAT:
unknown's avatar
unknown committed
4400 4401 4402
    col.setType(NDBCOL::Float);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
4403
  case MYSQL_TYPE_DOUBLE:
unknown's avatar
unknown committed
4404 4405 4406
    col.setType(NDBCOL::Double);
    col.setLength(1);
    break;
4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426
  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;
4427 4428 4429
  case MYSQL_TYPE_NEWDECIMAL:    
    {
      Field_new_decimal *f= (Field_new_decimal*)field;
unknown's avatar
unknown committed
4430
      uint precision= f->precision;
4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444
      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
4445 4446 4447 4448 4449
  // Date types
  case MYSQL_TYPE_DATETIME:    
    col.setType(NDBCOL::Datetime);
    col.setLength(1);
    break;
4450 4451 4452 4453
  case MYSQL_TYPE_DATE: // ?
    col.setType(NDBCOL::Char);
    col.setLength(field->pack_length());
    break;
unknown's avatar
unknown committed
4454
  case MYSQL_TYPE_NEWDATE:
unknown's avatar
unknown committed
4455 4456 4457
    col.setType(NDBCOL::Date);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
4458
  case MYSQL_TYPE_TIME:        
unknown's avatar
unknown committed
4459 4460 4461
    col.setType(NDBCOL::Time);
    col.setLength(1);
    break;
4462 4463 4464 4465 4466 4467 4468
  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
4469 4470 4471
    break;
  // Char types
  case MYSQL_TYPE_STRING:      
4472
    if (field->pack_length() == 0)
4473 4474 4475 4476
    {
      col.setType(NDBCOL::Bit);
      col.setLength(1);
    }
unknown's avatar
unknown committed
4477
    else if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4478
    {
unknown's avatar
unknown committed
4479
      col.setType(NDBCOL::Binary);
unknown's avatar
unknown committed
4480
      col.setLength(field->pack_length());
unknown's avatar
unknown committed
4481
    }
4482
    else
unknown's avatar
unknown committed
4483 4484 4485
    {
      col.setType(NDBCOL::Char);
      col.setCharset(cs);
4486
      col.setLength(field->pack_length());
unknown's avatar
unknown committed
4487
    }
unknown's avatar
unknown committed
4488
    break;
unknown's avatar
unknown committed
4489 4490 4491 4492 4493 4494
  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
4495
        if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4496 4497 4498 4499 4500 4501 4502 4503
          col.setType(NDBCOL::Varbinary);
        else {
          col.setType(NDBCOL::Varchar);
          col.setCharset(cs);
        }
      }
      else if (f->length_bytes == 2)
      {
unknown's avatar
unknown committed
4504
        if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515
          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
4516
    }
unknown's avatar
unknown committed
4517 4518 4519 4520
    break;
  // Blob types (all come in as MYSQL_TYPE_BLOB)
  mysql_type_tiny_blob:
  case MYSQL_TYPE_TINY_BLOB:
unknown's avatar
unknown committed
4521
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4522
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
4523
    else {
unknown's avatar
unknown committed
4524
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
4525 4526
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
4527 4528 4529 4530 4531
    col.setInlineSize(256);
    // No parts
    col.setPartSize(0);
    col.setStripeSize(0);
    break;
4532
  //mysql_type_blob:
4533
  case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
4534
  case MYSQL_TYPE_BLOB:    
unknown's avatar
unknown committed
4535
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4536
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
4537
    else {
unknown's avatar
unknown committed
4538
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
4539 4540
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556
    // 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
4557
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4558
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
4559
    else {
unknown's avatar
unknown committed
4560
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
4561 4562
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
4563 4564 4565 4566 4567 4568
    col.setInlineSize(256);
    col.setPartSize(4000);
    col.setStripeSize(8);
    break;
  mysql_type_long_blob:
  case MYSQL_TYPE_LONG_BLOB:  
unknown's avatar
unknown committed
4569
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4570
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
4571
    else {
unknown's avatar
unknown committed
4572
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
4573 4574
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587
    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
4588 4589
  case MYSQL_TYPE_BIT:
  {
unknown's avatar
unknown committed
4590
    int no_of_bits= field->field_length;
4591 4592 4593 4594 4595 4596 4597
    col.setType(NDBCOL::Bit);
    if (!no_of_bits)
      col.setLength(1);
      else
        col.setLength(no_of_bits);
    break;
  }
unknown's avatar
unknown committed
4598 4599 4600 4601 4602
  case MYSQL_TYPE_NULL:        
    goto mysql_type_unsupported;
  mysql_type_unsupported:
  default:
    return HA_ERR_UNSUPPORTED;
unknown's avatar
unknown committed
4603
  }
unknown's avatar
unknown committed
4604 4605 4606 4607 4608 4609
  // Set nullable and pk
  col.setNullable(field->maybe_null());
  col.setPrimaryKey(field->flags & PRI_KEY_FLAG);
  // Set autoincrement
  if (field->flags & AUTO_INCREMENT_FLAG) 
  {
unknown's avatar
unknown committed
4610
    char buff[22];
unknown's avatar
unknown committed
4611 4612
    col.setAutoIncrement(TRUE);
    ulonglong value= info->auto_increment_value ?
4613
      info->auto_increment_value : (ulonglong) 1;
unknown's avatar
unknown committed
4614
    DBUG_PRINT("info", ("Autoincrement key, initial: %s", llstr(value, buff)));
unknown's avatar
unknown committed
4615
    col.setAutoIncrementInitialValue(value);
unknown's avatar
unknown committed
4616
  }
unknown's avatar
unknown committed
4617
  else
unknown's avatar
unknown committed
4618
    col.setAutoIncrement(FALSE);
unknown's avatar
unknown committed
4619
  return 0;
unknown's avatar
unknown committed
4620 4621
}

unknown's avatar
unknown committed
4622 4623 4624 4625
/*
  Create a table in NDB Cluster
*/

unknown's avatar
unknown committed
4626
int ha_ndbcluster::create(const char *name, 
4627 4628
                          TABLE *form, 
                          HA_CREATE_INFO *info)
unknown's avatar
unknown committed
4629
{
4630
  THD *thd= current_thd;
unknown's avatar
unknown committed
4631 4632
  NDBTAB tab;
  NDBCOL col;
unknown's avatar
unknown committed
4633
  uint pack_length, length, i, pk_length= 0;
unknown's avatar
unknown committed
4634
  const void *data, *pack_data;
4635
  bool create_from_engine= (info->table_options & HA_OPTION_CREATE_FROM_ENGINE);
4636
  bool is_truncate= (thd->lex->sql_command == SQLCOM_TRUNCATE);
4637

unknown's avatar
unknown committed
4638
  DBUG_ENTER("ha_ndbcluster::create");
unknown's avatar
unknown committed
4639
  DBUG_PRINT("enter", ("name: %s", name));
unknown's avatar
unknown committed
4640

4641 4642 4643
  DBUG_ASSERT(*fn_rext((char*)name) == 0);
  set_dbname(name);
  set_tabname(name);
4644

unknown's avatar
unknown committed
4645 4646 4647 4648 4649 4650
  if (is_truncate)
  {
    DBUG_PRINT("info", ("Dropping and re-creating table for TRUNCATE"));
    if ((my_errno= delete_table(name)))
      DBUG_RETURN(my_errno);
  }
unknown's avatar
unknown committed
4651
  table= form;
4652 4653 4654
  if (create_from_engine)
  {
    /*
unknown's avatar
unknown committed
4655
      Table already exists in NDB and frm file has been created by 
4656 4657 4658
      caller.
      Do Ndb specific stuff, such as create a .ndb file
    */
unknown's avatar
unknown committed
4659
    if ((my_errno= write_ndb_file(name)))
unknown's avatar
unknown committed
4660
      DBUG_RETURN(my_errno);
unknown's avatar
unknown committed
4661
#ifdef HAVE_NDB_BINLOG
4662
    ndbcluster_create_binlog_setup(get_ndb(), name, strlen(name),
4663
                                   m_dbname, m_tabname, FALSE);
unknown's avatar
unknown committed
4664
#endif /* HAVE_NDB_BINLOG */
4665 4666
    DBUG_RETURN(my_errno);
  }
unknown's avatar
unknown committed
4667

4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682
#ifdef HAVE_NDB_BINLOG
  /*
    Don't allow table creation unless
    schema distribution table is setup
    ( unless it is a creation of the schema dist table itself )
  */
  if (!schema_share &&
      !(strcmp(m_dbname, NDB_REP_DB) == 0 &&
        strcmp(m_tabname, NDB_SCHEMA_TABLE) == 0))
  {
    DBUG_PRINT("info", ("Schema distribution table not setup"));
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
  }
#endif /* HAVE_NDB_BINLOG */

unknown's avatar
unknown committed
4683 4684 4685 4686 4687 4688 4689 4690
  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))
4691 4692
  {
    my_free((char*)data, MYF(0));
unknown's avatar
unknown committed
4693
    DBUG_RETURN(2);
4694 4695
  }

unknown's avatar
unknown committed
4696
  DBUG_PRINT("info", ("setFrm data=%lx  len=%d", pack_data, pack_length));
unknown's avatar
unknown committed
4697 4698 4699 4700
  tab.setFrm(pack_data, pack_length);      
  my_free((char*)data, MYF(0));
  my_free((char*)pack_data, MYF(0));
  
4701
  for (i= 0; i < form->s->fields; i++) 
unknown's avatar
unknown committed
4702 4703 4704 4705
  {
    Field *field= form->field[i];
    DBUG_PRINT("info", ("name: %s, type: %u, pack_length: %d", 
                        field->field_name, field->real_type(),
4706
                        field->pack_length()));
4707
    if ((my_errno= create_ndb_column(col, field, info)))
unknown's avatar
unknown committed
4708
      DBUG_RETURN(my_errno);
unknown's avatar
unknown committed
4709 4710
 
    if (info->store_on_disk || getenv("NDB_DEFAULT_DISK"))
unknown's avatar
unknown committed
4711 4712 4713 4714
      col.setStorageType(NdbDictionary::Column::StorageTypeDisk);
    else
      col.setStorageType(NdbDictionary::Column::StorageTypeMemory);

unknown's avatar
unknown committed
4715
    tab.addColumn(col);
unknown's avatar
unknown committed
4716
    if (col.getPrimaryKey())
unknown's avatar
unknown committed
4717
      pk_length += (field->pack_length() + 3) / 4;
unknown's avatar
unknown committed
4718
  }
unknown's avatar
unknown committed
4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734

  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
4735
  // No primary key, create shadow key as 64 bit, auto increment  
4736
  if (form->s->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
4737 4738 4739 4740 4741
  {
    DBUG_PRINT("info", ("Generating shadow key"));
    col.setName("$PK");
    col.setType(NdbDictionary::Column::Bigunsigned);
    col.setLength(1);
unknown's avatar
unknown committed
4742
    col.setNullable(FALSE);
unknown's avatar
unknown committed
4743 4744 4745
    col.setPrimaryKey(TRUE);
    col.setAutoIncrement(TRUE);
    tab.addColumn(col);
unknown's avatar
unknown committed
4746 4747
    pk_length += 2;
  }
4748
 
unknown's avatar
unknown committed
4749
  // Make sure that blob tables don't have to big part size
4750
  for (i= 0; i < form->s->fields; i++) 
unknown's avatar
unknown committed
4751 4752 4753 4754 4755 4756 4757
  {
    /**
     * 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()) {
4758
    case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
4759 4760 4761 4762
    case MYSQL_TYPE_BLOB:    
    case MYSQL_TYPE_MEDIUM_BLOB:   
    case MYSQL_TYPE_LONG_BLOB: 
    {
4763 4764
      NdbDictionary::Column * col= tab.getColumn(i);
      int size= pk_length + (col->getPartSize()+3)/4 + 7;
unknown's avatar
unknown committed
4765
      if (size > NDB_MAX_TUPLE_SIZE_IN_WORDS && 
4766
         (pk_length+7) < NDB_MAX_TUPLE_SIZE_IN_WORDS)
unknown's avatar
unknown committed
4767
      {
4768 4769
        size= NDB_MAX_TUPLE_SIZE_IN_WORDS - pk_length - 7;
        col->setPartSize(4*size);
unknown's avatar
unknown committed
4770 4771 4772 4773 4774 4775 4776 4777 4778 4779
      }
      /**
       * 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
4780
  }
unknown's avatar
Merge  
unknown committed
4781

4782
  // Check partition info
unknown's avatar
unknown committed
4783
  partition_info *part_info= form->part_info;
unknown's avatar
unknown committed
4784
  if ((my_errno= set_up_partition_info(part_info, form, (void*)&tab)))
4785
  {
unknown's avatar
unknown committed
4786
    DBUG_RETURN(my_errno);
4787 4788
  }

4789
  if ((my_errno= check_ndb_connection()))
unknown's avatar
unknown committed
4790 4791 4792
    DBUG_RETURN(my_errno);
  
  // Create the table in NDB     
4793 4794
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
4795
  if (dict->createTable(tab) != 0) 
unknown's avatar
unknown committed
4796 4797 4798 4799 4800 4801
  {
    const NdbError err= dict->getNdbError();
    ERR_PRINT(err);
    my_errno= ndb_to_mysql_error(&err);
    DBUG_RETURN(my_errno);
  }
4802 4803 4804 4805 4806 4807

  Ndb_table_guard ndbtab_g(dict, m_tabname);
  // temporary set m_table during create
  // reset at return
  m_table= ndbtab_g.get_table();
  // TODO check also that we have the same frm...
4808 4809 4810 4811 4812 4813 4814 4815 4816
  if (!m_table)
  {
    /* purecov: begin deadcode */
    const NdbError err= dict->getNdbError();
    ERR_PRINT(err);
    my_errno= ndb_to_mysql_error(&err);
    DBUG_RETURN(my_errno);
    /* purecov: end */
  }
4817

unknown's avatar
unknown committed
4818 4819
  DBUG_PRINT("info", ("Table %s/%s created successfully", 
                      m_dbname, m_tabname));
4820

unknown's avatar
unknown committed
4821
  // Create secondary indexes
4822
  my_errno= create_indexes(ndb, form);
4823

4824
  if (!my_errno)
unknown's avatar
unknown committed
4825
    my_errno= write_ndb_file(name);
4826 4827 4828 4829 4830 4831
  else
  {
    /*
      Failed to create an index,
      drop the table (and all it's indexes)
    */
4832
    while (dict->dropTableGlobal(*m_table))
4833
    {
4834 4835 4836 4837 4838 4839 4840 4841 4842
      switch (dict->getNdbError().status)
      {
        case NdbError::TemporaryError:
          if (!thd->killed) 
            continue; // retry indefinitly
          break;
        default:
          break;
      }
unknown's avatar
unknown committed
4843
      break;
4844
    }
4845 4846
    m_table = 0;
    DBUG_RETURN(my_errno);
4847
  }
4848

unknown's avatar
unknown committed
4849 4850 4851 4852 4853 4854 4855 4856 4857
#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...
    */
    {
4858
      uint length= (uint) strlen(name);
unknown's avatar
unknown committed
4859
      if ((share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
4860
                                           (byte*) name, length)))
unknown's avatar
unknown committed
4861 4862 4863 4864 4865
        handle_trailing_share(share);
    }
    /*
      get a new share
    */
4866 4867

    if (!(share= get_share(name, form, true, true)))
unknown's avatar
unknown committed
4868
    {
4869
      sql_print_error("NDB: allocating table share for %s failed", name);
unknown's avatar
unknown committed
4870 4871 4872 4873 4874 4875 4876 4877
      /* my_errno is set */
    }
    pthread_mutex_unlock(&ndbcluster_mutex);

    while (!IS_TMP_PREFIX(m_tabname))
    {
      String event_name(INJECTOR_EVENT_LEN);
      ndb_rep_event_name(&event_name,m_dbname,m_tabname);
4878 4879 4880 4881 4882 4883
      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
4884 4885 4886 4887 4888

      /*
        Always create an event for the table, as other mysql servers
        expect it to be there.
      */
4889
      if (!ndbcluster_create_event(ndb, m_table, event_name.c_ptr(), share,
4890
                                   share && do_event_op ? 2 : 1/* push warning */))
unknown's avatar
unknown committed
4891
      {
unknown's avatar
unknown committed
4892 4893 4894 4895
        if (ndb_extra_logging)
          sql_print_information("NDB Binlog: CREATE TABLE Event: %s",
                                event_name.c_ptr());
        if (share && do_event_op &&
4896
            ndbcluster_create_event_ops(share, m_table, event_name.c_ptr()))
unknown's avatar
unknown committed
4897 4898 4899 4900 4901
        {
          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
4902
      }
unknown's avatar
unknown committed
4903 4904 4905 4906
      /*
        warning has been issued if ndbcluster_create_event failed
        and (share && do_event_op)
      */
4907
      if (share && !do_event_op)
4908
        share->flags|= NSF_NO_BINLOG;
4909 4910
      ndbcluster_log_schema_op(thd, share,
                               thd->query, thd->query_length,
unknown's avatar
unknown committed
4911
                               share->db, share->table_name,
4912 4913
                               m_table->getObjectId(),
                               m_table->getObjectVersion(),
unknown's avatar
unknown committed
4914 4915 4916
                               (is_truncate) ?
			       SOT_TRUNCATE_TABLE : SOT_CREATE_TABLE, 
			       0, 0, 1);
unknown's avatar
unknown committed
4917 4918 4919 4920 4921
      break;
    }
  }
#endif /* HAVE_NDB_BINLOG */

4922
  m_table= 0;
unknown's avatar
unknown committed
4923 4924 4925
  DBUG_RETURN(my_errno);
}

4926 4927
int ha_ndbcluster::create_handler_files(const char *file,
                                        const char *old_name,
4928 4929
                                        int action_flag,
                                        HA_CREATE_INFO *info) 
unknown's avatar
unknown committed
4930
{ 
4931
  char path[FN_REFLEN];
unknown's avatar
unknown committed
4932 4933 4934 4935 4936 4937 4938 4939 4940
  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");

4941
  if (action_flag != CHF_INDEX_FLAG)
4942 4943 4944
  {
    DBUG_RETURN(FALSE);
  }
4945
  DBUG_PRINT("enter", ("file: %s", file));
unknown's avatar
unknown committed
4946 4947 4948 4949
  if (!(ndb= get_ndb()))
    DBUG_RETURN(HA_ERR_NO_CONNECTION);

  NDBDICT *dict= ndb->getDictionary();
4950
  if (!info->frm_only)
unknown's avatar
unknown committed
4951
    DBUG_RETURN(0); // Must be a create, ignore since frm is saved in create
4952 4953 4954 4955

  // TODO handle this
  DBUG_ASSERT(m_table != 0);

4956 4957
  set_dbname(file);
  set_tabname(file);
4958
  Ndb_table_guard ndbtab_g(dict, m_tabname);
4959
  DBUG_PRINT("info", ("m_dbname: %s, m_tabname: %s", m_dbname, m_tabname));
4960
  if (!(tab= ndbtab_g.get_table()))
4961 4962
    DBUG_RETURN(0); // Unkown table, must be temporary table

4963
  DBUG_ASSERT(get_ndb_share_state(m_share) == NSS_ALTERED);
4964
  if (readfrm(file, &data, &length) ||
unknown's avatar
unknown committed
4965 4966 4967 4968 4969
      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));
4970
    error= 1;
unknown's avatar
unknown committed
4971
  }
4972 4973
  else
  {
unknown's avatar
unknown committed
4974 4975
    DBUG_PRINT("info", ("Table %s has changed, altering frm in ndb", 
                        m_tabname));
4976 4977 4978 4979 4980 4981
    NdbDictionary::Table new_tab= *tab;
    new_tab.setFrm(pack_data, pack_length);
    if (dict->alterTableGlobal(*tab, new_tab))
    {
      error= ndb_to_mysql_error(&dict->getNdbError());
    }
4982 4983
    my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
    my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR));
unknown's avatar
unknown committed
4984
  }
4985
  
4986
  set_ndb_share_state(m_share, NSS_INITIAL);
4987
  free_share(&m_share); // Decrease ref_count
unknown's avatar
unknown committed
4988 4989 4990 4991

  DBUG_RETURN(error);
}

4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032
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
5033

5034
int ha_ndbcluster::create_ordered_index(const char *name, 
5035
                                        KEY *key_info)
5036
{
5037
  DBUG_ENTER("ha_ndbcluster::create_ordered_index");
5038
  DBUG_RETURN(create_ndb_index(name, key_info, FALSE));
5039 5040 5041
}

int ha_ndbcluster::create_unique_index(const char *name, 
5042
                                       KEY *key_info)
5043 5044
{

5045
  DBUG_ENTER("ha_ndbcluster::create_unique_index");
5046
  DBUG_RETURN(create_ndb_index(name, key_info, TRUE));
5047 5048 5049
}


unknown's avatar
unknown committed
5050 5051 5052 5053
/*
  Create an index in NDB Cluster
 */

5054 5055 5056
int ha_ndbcluster::create_ndb_index(const char *name, 
                                     KEY *key_info,
                                     bool unique)
5057
{
5058 5059
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
unknown's avatar
unknown committed
5060 5061 5062
  KEY_PART_INFO *key_part= key_info->key_part;
  KEY_PART_INFO *end= key_part + key_info->key_parts;
  
5063
  DBUG_ENTER("ha_ndbcluster::create_index");
unknown's avatar
unknown committed
5064
  DBUG_PRINT("enter", ("name: %s ", name));
5065

unknown's avatar
unknown committed
5066
  NdbDictionary::Index ndb_index(name);
5067
  if (unique)
unknown's avatar
unknown committed
5068 5069 5070 5071 5072
    ndb_index.setType(NdbDictionary::Index::UniqueHashIndex);
  else 
  {
    ndb_index.setType(NdbDictionary::Index::OrderedIndex);
    // TODO Only temporary ordered indexes supported
unknown's avatar
unknown committed
5073
    ndb_index.setLogging(FALSE); 
unknown's avatar
unknown committed
5074 5075 5076 5077 5078 5079 5080
  }
  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
5081
    ndb_index.addColumnName(field->field_name);
unknown's avatar
unknown committed
5082 5083
  }
  
5084
  if (dict->createIndex(ndb_index, *m_table))
unknown's avatar
unknown committed
5085 5086 5087 5088 5089 5090 5091
    ERR_RETURN(dict->getNdbError());

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

5092 5093 5094 5095 5096 5097 5098 5099 5100
/*
 Prepare for an on-line alter table
*/ 
void ha_ndbcluster::prepare_for_alter()
{
  ndbcluster_get_share(m_share); // Increase ref_count
  set_ndb_share_state(m_share, NSS_ALTERED);
}

5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112
/*
  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;

5113
  DBUG_ASSERT(m_share->state == NSS_ALTERED);
5114 5115 5116 5117 5118
  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;
5119
    NDB_INDEX_TYPE idx_type= get_index_type_from_key(idx, key, false);
5120 5121 5122 5123 5124 5125 5126 5127 5128
    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;
  }
5129
  if (error)
5130
  {
5131 5132
    set_ndb_share_state(m_share, NSS_INITIAL);
    free_share(&m_share); // Decrease ref_count
5133
  }
5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144
  DBUG_RETURN(error);  
}

/*
  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");
5145
  DBUG_ASSERT(m_share->state == NSS_ALTERED);
5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156
  // 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;
5157 5158
  renumber_indexes(ndb, table_arg);
  DBUG_RETURN(0);
5159 5160 5161 5162 5163 5164 5165
}
 
/*
  Really drop all indexes marked for deletion
*/
int ha_ndbcluster::final_drop_index(TABLE *table_arg)
{
5166
  int error;
5167 5168 5169 5170 5171 5172
  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;
5173 5174 5175 5176 5177 5178
  if((error= drop_indexes(ndb, table_arg)))
  {
    m_share->state= NSS_INITIAL;
    free_share(&m_share); // Decrease ref_count
  }
  DBUG_RETURN(error);
5179 5180
}

unknown's avatar
unknown committed
5181 5182 5183 5184 5185 5186
/*
  Rename a table in NDB Cluster
*/

int ha_ndbcluster::rename_table(const char *from, const char *to)
{
5187
  NDBDICT *dict;
5188
  char old_dbname[FN_HEADLEN];
5189
  char new_dbname[FN_HEADLEN];
unknown's avatar
unknown committed
5190
  char new_tabname[FN_HEADLEN];
5191 5192
  const NDBTAB *orig_tab;
  int result;
5193 5194
  bool recreate_indexes= FALSE;
  NDBDICT::List index_list;
unknown's avatar
unknown committed
5195 5196

  DBUG_ENTER("ha_ndbcluster::rename_table");
5197
  DBUG_PRINT("info", ("Renaming %s to %s", from, to));
5198
  set_dbname(from, old_dbname);
5199
  set_dbname(to, new_dbname);
unknown's avatar
unknown committed
5200 5201 5202
  set_tabname(from);
  set_tabname(to, new_tabname);

5203 5204 5205
  if (check_ndb_connection())
    DBUG_RETURN(my_errno= HA_ERR_NO_CONNECTION);

unknown's avatar
unknown committed
5206
  Ndb *ndb= get_ndb();
5207
  ndb->setDatabaseName(old_dbname);
unknown's avatar
unknown committed
5208
  dict= ndb->getDictionary();
5209 5210
  Ndb_table_guard ndbtab_g(dict, m_tabname);
  if (!(orig_tab= ndbtab_g.get_table()))
5211
    ERR_RETURN(dict->getNdbError());
5212

unknown's avatar
unknown committed
5213
#ifdef HAVE_NDB_BINLOG
5214 5215 5216
  int ndb_table_id= orig_tab->getObjectId();
  int ndb_table_version= orig_tab->getObjectVersion();

5217 5218
  NDB_SHARE *share= get_share(from, 0, false);
  if (share)
unknown's avatar
unknown committed
5219 5220 5221 5222 5223
  {
    int r= rename_share(share, to);
    DBUG_ASSERT(r == 0);
  }
#endif
5224 5225 5226 5227 5228
  if (my_strcasecmp(system_charset_info, new_dbname, old_dbname))
  {
    dict->listIndexes(index_list, *orig_tab);    
    recreate_indexes= TRUE;
  }
5229 5230
  // Change current database to that of target table
  set_dbname(to);
unknown's avatar
unknown committed
5231
  ndb->setDatabaseName(m_dbname);
unknown's avatar
unknown committed
5232

5233 5234 5235
  NdbDictionary::Table new_tab= *orig_tab;
  new_tab.setName(new_tabname);
  if (dict->alterTableGlobal(*orig_tab, new_tab) != 0)
unknown's avatar
unknown committed
5236
  {
5237
    NdbError ndb_error= dict->getNdbError();
unknown's avatar
unknown committed
5238 5239 5240 5241 5242 5243 5244 5245
#ifdef HAVE_NDB_BINLOG
    if (share)
    {
      int r= rename_share(share, from);
      DBUG_ASSERT(r == 0);
      free_share(&share);
    }
#endif
5246
    ERR_RETURN(ndb_error);
unknown's avatar
unknown committed
5247 5248 5249 5250
  }
  
  // Rename .ndb file
  if ((result= handler::rename_table(from, to)))
5251
  {
unknown's avatar
unknown committed
5252
    // ToDo in 4.1 should rollback alter table...
unknown's avatar
unknown committed
5253 5254 5255 5256
#ifdef HAVE_NDB_BINLOG
    if (share)
      free_share(&share);
#endif
unknown's avatar
unknown committed
5257
    DBUG_RETURN(result);
5258
  }
5259

unknown's avatar
unknown committed
5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270
#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);
5271 5272
    ndbcluster_handle_drop_table(ndb, event_name.c_ptr(), share,
                                 "rename table");
unknown's avatar
unknown committed
5273 5274 5275 5276 5277 5278 5279
  }

  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);
5280 5281
    Ndb_table_guard ndbtab_g2(dict, new_tabname);
    const NDBTAB *ndbtab= ndbtab_g2.get_table();
unknown's avatar
unknown committed
5282

unknown's avatar
unknown committed
5283
    if (!ndbcluster_create_event(ndb, ndbtab, event_name.c_ptr(), share,
5284
                                 share && ndb_binlog_running ? 2 : 1/* push warning */))
unknown's avatar
unknown committed
5285 5286 5287 5288
    {
      if (ndb_extra_logging)
        sql_print_information("NDB Binlog: RENAME Event: %s",
                              event_name.c_ptr());
unknown's avatar
unknown committed
5289 5290
      if (share && ndb_binlog_running &&
          ndbcluster_create_event_ops(share, ndbtab, event_name.c_ptr()))
unknown's avatar
unknown committed
5291
      {
unknown's avatar
unknown committed
5292 5293 5294
        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
5295 5296
      }
    }
unknown's avatar
unknown committed
5297 5298 5299 5300
    /*
      warning has been issued if ndbcluster_create_event failed
      and (share && ndb_binlog_running)
    */
5301
    if (!is_old_table_tmpfile)
unknown's avatar
unknown committed
5302 5303
      ndbcluster_log_schema_op(current_thd, share,
                               current_thd->query, current_thd->query_length,
5304 5305
                               old_dbname, m_tabname,
                               ndb_table_id, ndb_table_version,
5306
                               SOT_RENAME_TABLE,
5307
                               m_dbname, new_tabname, 1);
unknown's avatar
unknown committed
5308
  }
5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333

  // If we are moving tables between databases, we need to recreate
  // indexes
  if (recreate_indexes)
  {
    for (unsigned i = 0; i < index_list.count; i++) 
    {
        NDBDICT::List::Element& index_el = index_list.elements[i];
	// Recreate any indexes not stored in the system database
	if (my_strcasecmp(system_charset_info, 
			  index_el.database, NDB_SYSTEM_DATABASE))
	{
	  set_dbname(from);
	  ndb->setDatabaseName(m_dbname);
	  const NDBINDEX * index= dict->getIndexGlobal(index_el.name,  new_tab);
	  DBUG_PRINT("info", ("Creating index %s/%s",
			      index_el.database, index->getName()));
	  dict->createIndex(*index, new_tab);
	  DBUG_PRINT("info", ("Dropping index %s/%s",
			      index_el.database, index->getName()));
	  set_dbname(from);
	  ndb->setDatabaseName(m_dbname);
	  dict->dropIndexGlobal(*index);
	}
    }
unknown's avatar
unknown committed
5334 5335 5336 5337 5338
  }
  if (share)
    free_share(&share);
#endif

unknown's avatar
unknown committed
5339 5340 5341 5342 5343
  DBUG_RETURN(result);
}


/*
5344 5345
  Delete table from NDB Cluster

unknown's avatar
unknown committed
5346 5347
 */

unknown's avatar
unknown committed
5348 5349 5350 5351 5352 5353 5354 5355
/* 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)
{
5356
  THD *thd= current_thd;
unknown's avatar
unknown committed
5357 5358
  DBUG_ENTER("ha_ndbcluster::ndbcluster_delete_table");
  NDBDICT *dict= ndb->getDictionary();
5359 5360
  int ndb_table_id= 0;
  int ndb_table_version= 0;
5361
#ifdef HAVE_NDB_BINLOG
5362 5363 5364 5365 5366 5367 5368 5369 5370
  /*
    Don't allow drop table unless
    schema distribution table is setup
  */
  if (!schema_share)
  {
    DBUG_PRINT("info", ("Schema distribution table not setup"));
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
  }
unknown's avatar
unknown committed
5371 5372
  NDB_SHARE *share= get_share(path, 0, false);
#endif
unknown's avatar
unknown committed
5373 5374 5375

  /* Drop the table from NDB */
  
5376
  int res= 0;
5377
  if (h && h->m_table)
unknown's avatar
unknown committed
5378
  {
5379 5380
retry_temporary_error1:
    if (dict->dropTableGlobal(*h->m_table) == 0)
5381 5382 5383 5384
    {
      ndb_table_id= h->m_table->getObjectId();
      ndb_table_version= h->m_table->getObjectVersion();
    }
5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397
    else
    {
      switch (dict->getNdbError().status)
      {
        case NdbError::TemporaryError:
          if (!thd->killed) 
            goto retry_temporary_error1; // retry indefinitly
          break;
        default:
          break;
      }
      res= ndb_to_mysql_error(&dict->getNdbError());
    }
5398
    h->release_metadata(thd, ndb);
unknown's avatar
unknown committed
5399 5400 5401 5402
  }
  else
  {
    ndb->setDatabaseName(db);
5403 5404 5405 5406 5407
    while (1)
    {
      Ndb_table_guard ndbtab_g(dict, table_name);
      if (ndbtab_g.get_table())
      {
5408
    retry_temporary_error2:
5409 5410 5411 5412 5413
        if (dict->dropTableGlobal(*ndbtab_g.get_table()) == 0)
        {
          ndb_table_id= ndbtab_g.get_table()->getObjectId();
          ndb_table_version= ndbtab_g.get_table()->getObjectVersion();
        }
5414
        else
5415
        {
5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429
          switch (dict->getNdbError().status)
          {
            case NdbError::TemporaryError:
              if (!thd->killed) 
                goto retry_temporary_error2; // retry indefinitly
              break;
            default:
              if (dict->getNdbError().code == NDB_INVALID_SCHEMA_OBJECT)
              {
                ndbtab_g.invalidate();
                continue;
              }
              break;
          }
5430 5431 5432 5433 5434 5435
        }
      }
      else
        res= ndb_to_mysql_error(&dict->getNdbError());
      break;
    }
unknown's avatar
unknown committed
5436 5437 5438 5439
  }

  if (res)
  {
unknown's avatar
unknown committed
5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457
#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
5458 5459 5460
    DBUG_RETURN(res);
  }

unknown's avatar
unknown committed
5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471
#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;

unknown's avatar
unknown committed
5472 5473
  if (!IS_TMP_PREFIX(table_name) && share &&
      current_thd->lex->sql_command != SQLCOM_TRUNCATE)
unknown's avatar
unknown committed
5474
  {
5475 5476
    ndbcluster_log_schema_op(thd, share,
                             thd->query, thd->query_length,
unknown's avatar
unknown committed
5477
                             share->db, share->table_name,
5478
                             ndb_table_id, ndb_table_version,
5479
                             SOT_DROP_TABLE, 0, 0, 1);
unknown's avatar
unknown committed
5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490
  }
  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,
5491
                                 share, "delete table");
unknown's avatar
unknown committed
5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 5509
  }

  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
5510 5511 5512
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
5513 5514
int ha_ndbcluster::delete_table(const char *name)
{
5515
  DBUG_ENTER("ha_ndbcluster::delete_table");
unknown's avatar
unknown committed
5516 5517 5518
  DBUG_PRINT("enter", ("name: %s", name));
  set_dbname(name);
  set_tabname(name);
5519

5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531
#ifdef HAVE_NDB_BINLOG
  /*
    Don't allow drop table unless
    schema distribution table is setup
  */
  if (!schema_share)
  {
    DBUG_PRINT("info", ("Schema distribution table not setup"));
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
  }
#endif

unknown's avatar
unknown committed
5532 5533
  if (check_ndb_connection())
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
5534 5535

  /* Call ancestor function to delete .ndb file */
5536
  handler::delete_table(name);
unknown's avatar
unknown committed
5537 5538

  DBUG_RETURN(delete_table(this, get_ndb(),name, m_dbname, m_tabname));
unknown's avatar
unknown committed
5539 5540 5541
}


5542 5543 5544 5545
void ha_ndbcluster::get_auto_increment(ulonglong offset, ulonglong increment,
                                       ulonglong nb_desired_values,
                                       ulonglong *first_value,
                                       ulonglong *nb_reserved_values)
5546
{  
5547 5548
  int cache_size;
  Uint64 auto_value;
unknown's avatar
unknown committed
5549 5550
  DBUG_ENTER("get_auto_increment");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));
5551
  Ndb *ndb= get_ndb();
5552
   
5553
  if (m_rows_inserted > m_rows_to_insert)
unknown's avatar
unknown committed
5554
  {
5555 5556
    /* We guessed too low */
    m_rows_to_insert+= m_autoincrement_prefetch;
unknown's avatar
unknown committed
5557
  }
unknown's avatar
unknown committed
5558
  cache_size= 
unknown's avatar
unknown committed
5559 5560 5561 5562
    (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));
5563
  int ret;
5564 5565
  uint retries= NDB_AUTO_INCREMENT_RETRIES;
  do {
5566
    Ndb_tuple_id_range_guard g(m_share);
5567 5568
    ret=
      m_skip_auto_increment ? 
5569 5570
      ndb->readAutoIncrementValue(m_table, g.range, auto_value) :
      ndb->getAutoIncrementValue(m_table, g.range, auto_value, cache_size);
5571
  } while (ret == -1 && 
5572 5573
           --retries &&
           ndb->getNdbError().status == NdbError::TemporaryError);
5574
  if (ret == -1)
5575 5576 5577 5578
  {
    const NdbError err= ndb->getNdbError();
    sql_print_error("Error %lu in ::get_auto_increment(): %s",
                    (ulong) err.code, err.message);
5579 5580
    *first_value= ~(ulonglong) 0;
    DBUG_VOID_RETURN;
5581
  }
5582 5583 5584 5585
  *first_value= (longlong)auto_value;
  /* From the point of view of MySQL, NDB reserves one row at a time */
  *nb_reserved_values= 1;
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
5586 5587 5588 5589 5590 5591 5592
}


/*
  Constructor for the NDB Cluster table handler 
 */

unknown's avatar
unknown committed
5593 5594 5595 5596 5597 5598 5599
#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
5600
                HA_CAN_BIT_FIELD | \
5601 5602
                HA_PRIMARY_KEY_REQUIRED_FOR_POSITION | \
                HA_PRIMARY_KEY_REQUIRED_FOR_DELETE | \
5603
                HA_PARTIAL_COLUMN_READ | \
5604 5605
                HA_HAS_OWN_BINLOGGING | \
                HA_HAS_RECORDS
unknown's avatar
unknown committed
5606

5607 5608
ha_ndbcluster::ha_ndbcluster(handlerton *hton, TABLE_SHARE *table_arg):
  handler(hton, table_arg),
unknown's avatar
unknown committed
5609 5610 5611
  m_active_trans(NULL),
  m_active_cursor(NULL),
  m_table(NULL),
5612
  m_table_info(NULL),
unknown's avatar
unknown committed
5613
  m_table_flags(HA_NDBCLUSTER_TABLE_FLAGS),
5614
  m_share(0),
5615 5616 5617
  m_part_info(NULL),
  m_use_partition_function(FALSE),
  m_sorted(FALSE),
unknown's avatar
unknown committed
5618
  m_use_write(FALSE),
5619
  m_ignore_dup_key(FALSE),
5620
  m_has_unique_index(FALSE),
5621
  m_primary_key_update(FALSE),
unknown's avatar
unknown committed
5622
  m_ignore_no_key(FALSE),
5623 5624 5625
  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
5626
  m_rows_changed((ha_rows) 0),
5627 5628 5629 5630
  m_bulk_insert_not_flushed(FALSE),
  m_ops_pending(0),
  m_skip_auto_increment(TRUE),
  m_blobs_pending(0),
5631
  m_blobs_offset(0),
5632 5633
  m_blobs_buffer(0),
  m_blobs_buffer_size(0),
5634 5635 5636
  m_dupkey((uint) -1),
  m_ha_not_exact_count(FALSE),
  m_force_send(TRUE),
5637
  m_autoincrement_prefetch((ha_rows) 32),
unknown's avatar
unknown committed
5638
  m_transaction_on(TRUE),
unknown's avatar
unknown committed
5639 5640
  m_cond_stack(NULL),
  m_multi_cursor(NULL)
5641
{
5642
  int i;
5643
 
unknown's avatar
unknown committed
5644 5645 5646 5647 5648
  DBUG_ENTER("ha_ndbcluster");

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

5649 5650
  stats.records= ~(ha_rows)0; // uninitialized
  stats.block_size= 1024;
unknown's avatar
unknown committed
5651

unknown's avatar
ndb:  
unknown committed
5652 5653
  for (i= 0; i < MAX_KEY; i++)
    ndb_init_index(m_index[i]);
5654

unknown's avatar
unknown committed
5655 5656 5657 5658
  DBUG_VOID_RETURN;
}


5659 5660 5661 5662 5663 5664 5665 5666 5667 5668
int ha_ndbcluster::ha_initialise()
{
  DBUG_ENTER("ha_ndbcluster::ha_initialise");
  if (check_ndb_in_thd(current_thd))
  {
    DBUG_RETURN(FALSE);
  }
  DBUG_RETURN(TRUE);
}

unknown's avatar
unknown committed
5669 5670 5671 5672 5673 5674
/*
  Destructor for NDB Cluster table handler
 */

ha_ndbcluster::~ha_ndbcluster() 
{
5675 5676
  THD *thd= current_thd;
  Ndb *ndb= thd ? check_ndb_in_thd(thd) : g_ndb;
unknown's avatar
unknown committed
5677 5678
  DBUG_ENTER("~ha_ndbcluster");

5679
  if (m_share)
unknown's avatar
unknown committed
5680 5681 5682
  {
    free_share(&m_share);
  }
5683
  release_metadata(thd, ndb);
5684 5685
  my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
  m_blobs_buffer= 0;
unknown's avatar
unknown committed
5686 5687

  // Check for open cursor/transaction
5688 5689
  if (m_active_cursor) {
  }
unknown's avatar
unknown committed
5690
  DBUG_ASSERT(m_active_cursor == NULL);
5691 5692
  if (m_active_trans) {
  }
unknown's avatar
unknown committed
5693 5694
  DBUG_ASSERT(m_active_trans == NULL);

5695 5696 5697 5698
  // Discard the condition stack
  DBUG_PRINT("info", ("Clearing condition stack"));
  cond_clear();

unknown's avatar
unknown committed
5699 5700 5701 5702
  DBUG_VOID_RETURN;
}


unknown's avatar
Merge  
unknown committed
5703

unknown's avatar
unknown committed
5704 5705 5706 5707
/*
  Open a table for further use
  - fetch metadata for this table from NDB
  - check that table exists
unknown's avatar
unknown committed
5708 5709 5710 5711

  RETURN
    0    ok
    < 0  Table has changed
unknown's avatar
unknown committed
5712 5713 5714 5715
*/

int ha_ndbcluster::open(const char *name, int mode, uint test_if_locked)
{
unknown's avatar
unknown committed
5716
  int res;
unknown's avatar
unknown committed
5717
  KEY *key;
unknown's avatar
unknown committed
5718 5719 5720
  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
5721
  
unknown's avatar
unknown committed
5722 5723 5724 5725
  /*
    Setup ref_length to make room for the whole 
    primary key to be written in the ref variable
  */
unknown's avatar
unknown committed
5726
  
unknown's avatar
unknown committed
5727
  if (table_share->primary_key != MAX_KEY) 
unknown's avatar
unknown committed
5728
  {
unknown's avatar
unknown committed
5729
    key= table->key_info+table_share->primary_key;
unknown's avatar
unknown committed
5730 5731
    ref_length= key->key_length;
  }
5732 5733 5734 5735 5736 5737 5738 5739 5740 5741
  else // (table_share->primary_key == MAX_KEY) 
  {
    if (m_use_partition_function)
    {
      ref_length+= sizeof(m_part_id);
    }
  }

  DBUG_PRINT("info", ("ref_length: %d", ref_length));

unknown's avatar
unknown committed
5742
  // Init table lock structure 
unknown's avatar
unknown committed
5743
  if (!(m_share=get_share(name, table)))
unknown's avatar
unknown committed
5744 5745 5746 5747 5748 5749
    DBUG_RETURN(1);
  thr_lock_data_init(&m_share->lock,&m_lock,(void*) 0);
  
  set_dbname(name);
  set_tabname(name);
  
5750
  if (check_ndb_connection()) {
unknown's avatar
unknown committed
5751 5752
    free_share(&m_share);
    m_share= 0;
unknown's avatar
unknown committed
5753
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
5754
  }
5755
  
unknown's avatar
unknown committed
5756 5757 5758
  res= get_metadata(name);
  if (!res)
    info(HA_STATUS_VARIABLE | HA_STATUS_CONST);
unknown's avatar
unknown committed
5759

5760 5761 5762 5763 5764
#ifdef HAVE_NDB_BINLOG
  if (!ndb_binlog_tables_inited && ndb_binlog_running)
    table->db_stat|= HA_READ_ONLY;
#endif

unknown's avatar
unknown committed
5765
  DBUG_RETURN(res);
unknown's avatar
unknown committed
5766 5767
}

unknown's avatar
unknown committed
5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781
/*
  Set partition info

  SYNOPSIS
    set_part_info()
    part_info

  RETURN VALUE
    NONE

  DESCRIPTION
    Set up partition info when handler object created
*/

5782 5783 5784 5785 5786
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 &&
5787
        !m_part_info->is_sub_partitioned()))
5788 5789
    m_use_partition_function= TRUE;
}
unknown's avatar
unknown committed
5790 5791 5792 5793 5794 5795 5796 5797

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

int ha_ndbcluster::close(void)
{
5798 5799 5800
  DBUG_ENTER("close");
  THD *thd= current_thd;
  Ndb *ndb= thd ? check_ndb_in_thd(thd) : g_ndb;
unknown's avatar
unknown committed
5801 5802
  free_share(&m_share);
  m_share= 0;
5803
  release_metadata(thd, ndb);
unknown's avatar
unknown committed
5804 5805 5806 5807
  DBUG_RETURN(0);
}


5808
Thd_ndb* ha_ndbcluster::seize_thd_ndb()
unknown's avatar
unknown committed
5809
{
5810 5811
  Thd_ndb *thd_ndb;
  DBUG_ENTER("seize_thd_ndb");
unknown's avatar
unknown committed
5812

5813 5814
  thd_ndb= new Thd_ndb();
  if (thd_ndb->ndb->init(max_transactions) != 0)
unknown's avatar
unknown committed
5815
  {
5816
    ERR_PRINT(thd_ndb->ndb->getNdbError());
unknown's avatar
unknown committed
5817 5818 5819 5820 5821 5822
    /*
      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 
    */
5823 5824
    delete thd_ndb;
    thd_ndb= NULL;
unknown's avatar
unknown committed
5825
  }
5826
  DBUG_RETURN(thd_ndb);
unknown's avatar
unknown committed
5827 5828 5829
}


5830
void ha_ndbcluster::release_thd_ndb(Thd_ndb* thd_ndb)
unknown's avatar
unknown committed
5831
{
5832 5833
  DBUG_ENTER("release_thd_ndb");
  delete thd_ndb;
unknown's avatar
unknown committed
5834 5835 5836 5837 5838
  DBUG_VOID_RETURN;
}


/*
unknown's avatar
unknown committed
5839
  If this thread already has a Thd_ndb object allocated
unknown's avatar
unknown committed
5840
  in current THD, reuse it. Otherwise
unknown's avatar
unknown committed
5841
  seize a Thd_ndb object, assign it to current THD and use it.
unknown's avatar
unknown committed
5842 5843 5844
 
*/

5845
Ndb* check_ndb_in_thd(THD* thd)
unknown's avatar
unknown committed
5846
{
5847
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
5848
  if (!thd_ndb)
unknown's avatar
unknown committed
5849
  {
unknown's avatar
unknown committed
5850
    if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
unknown's avatar
Merge  
unknown committed
5851
      return NULL;
5852
    set_thd_ndb(thd, thd_ndb);
unknown's avatar
unknown committed
5853
  }
unknown's avatar
Merge  
unknown committed
5854
  return thd_ndb->ndb;
5855 5856
}

unknown's avatar
unknown committed
5857

5858

5859
int ha_ndbcluster::check_ndb_connection(THD* thd)
unknown's avatar
unknown committed
5860
{
5861
  Ndb *ndb;
unknown's avatar
unknown committed
5862 5863
  DBUG_ENTER("check_ndb_connection");
  
5864
  if (!(ndb= check_ndb_in_thd(thd)))
5865
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
5866
  ndb->setDatabaseName(m_dbname);
unknown's avatar
unknown committed
5867 5868 5869
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
5870

5871
static int ndbcluster_close_connection(handlerton *hton, THD *thd)
unknown's avatar
unknown committed
5872
{
5873
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
unknown's avatar
unknown committed
5874
  DBUG_ENTER("ndbcluster_close_connection");
5875 5876
  if (thd_ndb)
  {
5877
    ha_ndbcluster::release_thd_ndb(thd_ndb);
5878
    set_thd_ndb(thd, NULL); // not strictly required but does not hurt either
5879
  }
5880
  DBUG_RETURN(0);
unknown's avatar
unknown committed
5881 5882 5883 5884 5885 5886 5887
}


/*
  Try to discover one table from NDB
 */

5888 5889 5890 5891
int ndbcluster_discover(handlerton *hton, THD* thd, const char *db, 
                        const char *name,
                        const void** frmblob, 
                        uint* frmlen)
unknown's avatar
unknown committed
5892
{
5893 5894
  int error= 0;
  NdbError ndb_error;
unknown's avatar
unknown committed
5895 5896
  uint len;
  const void* data;
5897
  Ndb* ndb;
5898
  char key[FN_REFLEN];
unknown's avatar
unknown committed
5899
  DBUG_ENTER("ndbcluster_discover");
5900
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name)); 
unknown's avatar
unknown committed
5901

5902 5903 5904 5905
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(HA_ERR_NO_CONNECTION);  
  ndb->setDatabaseName(db);
  NDBDICT* dict= ndb->getDictionary();
5906
  build_table_filename(key, sizeof(key), db, name, "", 0);
5907
  NDB_SHARE *share= get_share(key, 0, false);
5908
  if (share && get_ndb_share_state(share) == NSS_ALTERED)
unknown's avatar
unknown committed
5909
  {
5910 5911 5912 5913
    // Frm has been altered on disk, but not yet written to ndb
    if (readfrm(key, &data, &len))
    {
      DBUG_PRINT("error", ("Could not read frm"));
5914 5915
      error= 1;
      goto err;
5916
    }
unknown's avatar
unknown committed
5917
  }
5918
  else
5919
  {
5920 5921 5922 5923
    Ndb_table_guard ndbtab_g(dict, name);
    const NDBTAB *tab= ndbtab_g.get_table();
    if (!tab)
    {
5924 5925
      const NdbError err= dict->getNdbError();
      if (err.code == 709 || err.code == 723)
5926 5927 5928 5929
        error= -1;
      else
        ndb_error= err;
      goto err;
5930 5931 5932 5933 5934 5935 5936
    }
    DBUG_PRINT("info", ("Found table %s", tab->getName()));
    
    len= tab->getFrmLength();  
    if (len == 0 || tab->getFrmData() == NULL)
    {
      DBUG_PRINT("error", ("No frm data found."));
5937 5938
      error= 1;
      goto err;
5939 5940 5941 5942 5943
    }
    
    if (unpackfrm(&data, &len, tab->getFrmData()))
    {
      DBUG_PRINT("error", ("Could not unpack table"));
5944 5945
      error= 1;
      goto err;
5946
    }
5947
  }
unknown's avatar
unknown committed
5948 5949 5950 5951

  *frmlen= len;
  *frmblob= data;
  
5952 5953 5954
  if (share)
    free_share(&share);

unknown's avatar
unknown committed
5955
  DBUG_RETURN(0);
5956 5957 5958 5959 5960 5961 5962 5963
err:
  if (share)
    free_share(&share);
  if (ndb_error.code)
  {
    ERR_RETURN(ndb_error);
  }
  DBUG_RETURN(error);
unknown's avatar
unknown committed
5964 5965 5966
}

/*
5967
  Check if a table exists in NDB
5968

5969
 */
unknown's avatar
unknown committed
5970

5971 5972
int ndbcluster_table_exists_in_engine(handlerton *hton, THD* thd, 
                                      const char *db,
5973
                                      const char *name)
5974 5975
{
  Ndb* ndb;
5976
  DBUG_ENTER("ndbcluster_table_exists_in_engine");
5977
  DBUG_PRINT("enter", ("db: %s  name: %s", db, name));
5978 5979

  if (!(ndb= check_ndb_in_thd(thd)))
5980
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
5981 5982

  NDBDICT* dict= ndb->getDictionary();
5983 5984 5985
  NdbDictionary::Dictionary::List list;
  if (dict->listObjects(list, NdbDictionary::Object::UserTable) != 0)
    ERR_RETURN(dict->getNdbError());
5986
  for (uint i= 0 ; i < list.count ; i++)
5987
  {
5988 5989 5990 5991 5992 5993 5994
    NdbDictionary::Dictionary::List::Element& elmt= list.elements[i];
    if (my_strcasecmp(system_charset_info, elmt.database, db))
      continue;
    if (my_strcasecmp(system_charset_info, elmt.name, name))
      continue;
    DBUG_PRINT("info", ("Found table"));
    DBUG_RETURN(1);
5995
  }
5996
  DBUG_RETURN(0);
5997 5998
}

unknown's avatar
unknown committed
5999 6000


unknown's avatar
unknown committed
6001
extern "C" byte* tables_get_key(const char *entry, uint *length,
6002
                                my_bool not_used __attribute__((unused)))
6003 6004 6005 6006 6007 6008
{
  *length= strlen(entry);
  return (byte*) entry;
}


6009 6010
/*
  Drop a database in NDB Cluster
6011 6012
  NOTE add a dummy void function, since stupid handlerton is returning void instead of int...
*/
6013

6014
int ndbcluster_drop_database_impl(const char *path)
6015 6016 6017 6018 6019 6020 6021 6022 6023
{
  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;
6024
  int ret= 0;
6025 6026 6027 6028
  ha_ndbcluster::set_dbname(path, (char *)&dbname);
  DBUG_PRINT("enter", ("db: %s", dbname));
  
  if (!(ndb= check_ndb_in_thd(thd)))
6029
    DBUG_RETURN(-1);
6030 6031 6032 6033 6034
  
  // List tables in NDB
  NDBDICT *dict= ndb->getDictionary();
  if (dict->listObjects(list, 
                        NdbDictionary::Object::UserTable) != 0)
6035
    DBUG_RETURN(-1);
6036 6037
  for (i= 0 ; i < list.count ; i++)
  {
unknown's avatar
unknown committed
6038 6039
    NdbDictionary::Dictionary::List::Element& elmt= list.elements[i];
    DBUG_PRINT("info", ("Found %s/%s in NDB", elmt.database, elmt.name));     
6040 6041
    
    // Add only tables that belongs to db
unknown's avatar
unknown committed
6042
    if (my_strcasecmp(system_charset_info, elmt.database, dbname))
6043
      continue;
unknown's avatar
unknown committed
6044 6045
    DBUG_PRINT("info", ("%s must be dropped", elmt.name));     
    drop_list.push_back(thd->strdup(elmt.name));
6046 6047
  }
  // Drop any tables belonging to database
unknown's avatar
unknown committed
6048
  char full_path[FN_REFLEN];
6049
  char *tmp= full_path +
6050
    build_table_filename(full_path, sizeof(full_path), dbname, "", "", 0);
6051

6052 6053 6054
  ndb->setDatabaseName(dbname);
  List_iterator_fast<char> it(drop_list);
  while ((tabname=it++))
6055
  {
6056
    tablename_to_filename(tabname, tmp, FN_REFLEN - (tmp - full_path)-1);
unknown's avatar
unknown committed
6057
    VOID(pthread_mutex_lock(&LOCK_open));
unknown's avatar
unknown committed
6058
    if (ha_ndbcluster::delete_table(0, ndb, full_path, dbname, tabname))
6059 6060
    {
      const NdbError err= dict->getNdbError();
unknown's avatar
unknown committed
6061
      if (err.code != 709 && err.code != 723)
6062 6063
      {
        ERR_PRINT(err);
6064
        ret= ndb_to_mysql_error(&err);
6065
      }
6066
    }
unknown's avatar
unknown committed
6067
    VOID(pthread_mutex_unlock(&LOCK_open));
6068 6069
  }
  DBUG_RETURN(ret);      
6070 6071
}

6072
static void ndbcluster_drop_database(handlerton *hton, char *path)
6073
{
6074
  THD *thd= current_thd;
6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087
  DBUG_ENTER("ndbcluster_drop_database");
#ifdef HAVE_NDB_BINLOG
  /*
    Don't allow drop database unless
    schema distribution table is setup
  */
  if (!schema_share)
  {
    DBUG_PRINT("info", ("Schema distribution table not setup"));
    DBUG_VOID_RETURN;
    //DBUG_RETURN(HA_ERR_NO_CONNECTION);
  }
#endif
6088
  ndbcluster_drop_database_impl(path);
unknown's avatar
unknown committed
6089 6090 6091
#ifdef HAVE_NDB_BINLOG
  char db[FN_REFLEN];
  ha_ndbcluster::set_dbname(path, db);
6092 6093
  ndbcluster_log_schema_op(thd, 0,
                           thd->query, thd->query_length,
6094
                           db, "", 0, 0, SOT_DROP_DB, 0, 0, 0);
unknown's avatar
unknown committed
6095
#endif
6096
  DBUG_VOID_RETURN;
6097
}
unknown's avatar
unknown committed
6098 6099 6100
/*
  find all tables in ndb and discover those needed
*/
6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112
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
6113
int ndbcluster_find_all_files(THD *thd)
unknown's avatar
unknown committed
6114 6115 6116 6117 6118 6119 6120 6121 6122 6123
{
  DBUG_ENTER("ndbcluster_find_all_files");
  Ndb* ndb;
  char key[FN_REFLEN];

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

  NDBDICT *dict= ndb->getDictionary();

6124
  int unhandled, retries= 5, skipped;
6125 6126
  LINT_INIT(unhandled);
  LINT_INIT(skipped);
unknown's avatar
unknown committed
6127 6128
  do
  {
unknown's avatar
ndb -  
unknown committed
6129
    NdbDictionary::Dictionary::List list;
unknown's avatar
unknown committed
6130 6131 6132
    if (dict->listObjects(list, NdbDictionary::Object::UserTable) != 0)
      ERR_RETURN(dict->getNdbError());
    unhandled= 0;
6133 6134
    skipped= 0;
    retries--;
unknown's avatar
unknown committed
6135 6136 6137
    for (uint i= 0 ; i < list.count ; i++)
    {
      NDBDICT::List::Element& elmt= list.elements[i];
unknown's avatar
unknown committed
6138
      if (IS_TMP_PREFIX(elmt.name) || IS_NDB_BLOB_PREFIX(elmt.name))
unknown's avatar
unknown committed
6139 6140 6141 6142
      {
        DBUG_PRINT("info", ("Skipping %s.%s in NDB", elmt.database, elmt.name));
        continue;
      }
unknown's avatar
unknown committed
6143
      DBUG_PRINT("info", ("Found %s.%s in NDB", elmt.database, elmt.name));
6144 6145 6146
      if (elmt.state != NDBOBJ::StateOnline &&
          elmt.state != NDBOBJ::StateBackup &&
          elmt.state != NDBOBJ::StateBuilding)
unknown's avatar
unknown committed
6147 6148 6149
      {
        sql_print_information("NDB: skipping setup table %s.%s, in state %d",
                              elmt.database, elmt.name, elmt.state);
6150
        skipped++;
unknown's avatar
unknown committed
6151 6152 6153 6154
        continue;
      }

      ndb->setDatabaseName(elmt.database);
6155 6156 6157
      Ndb_table_guard ndbtab_g(dict, elmt.name);
      const NDBTAB *ndbtab= ndbtab_g.get_table();
      if (!ndbtab)
unknown's avatar
unknown committed
6158
      {
6159
        if (retries == 0)
unknown's avatar
unknown committed
6160 6161 6162 6163
          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
6164 6165 6166 6167 6168 6169 6170
        unhandled++;
        continue;
      }

      if (ndbtab->getFrmLength() == 0)
        continue;
    
6171
      /* check if database exists */
6172
      char *end= key +
6173
        build_table_filename(key, sizeof(key), elmt.database, "", "", 0);
6174 6175 6176 6177 6178
      if (my_access(key, F_OK))
      {
        /* no such database defined, skip table */
        continue;
      }
6179 6180 6181
      /* finalize construction of path */
      end+= tablename_to_filename(elmt.name, end,
                                  sizeof(key)-(end-key));
unknown's avatar
unknown committed
6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193
      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
6194
        NDB_SHARE *share= get_share(key, 0, false);
6195
        if (!share || get_ndb_share_state(share) != NSS_ALTERED)
unknown's avatar
unknown committed
6196 6197 6198 6199 6200
        {
          discover= 1;
          sql_print_information("NDB: mismatch in frm for %s.%s, discovering...",
                                elmt.database, elmt.name);
        }
unknown's avatar
unknown committed
6201 6202
        if (share)
          free_share(&share);
unknown's avatar
unknown committed
6203 6204 6205 6206
      }
      my_free((char*) data, MYF(MY_ALLOW_ZERO_PTR));
      my_free((char*) pack_data, MYF(MY_ALLOW_ZERO_PTR));

6207
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
6208 6209 6210
      if (discover)
      {
        /* ToDo 4.1 database needs to be created if missing */
6211
        if (ndb_create_table_from_engine(thd, elmt.database, elmt.name))
unknown's avatar
unknown committed
6212 6213 6214 6215
        {
          /* ToDo 4.1 handle error */
        }
      }
unknown's avatar
unknown committed
6216
#ifdef HAVE_NDB_BINLOG
6217
      else
unknown's avatar
unknown committed
6218 6219
      {
        /* set up replication for this table */
6220 6221 6222
        ndbcluster_create_binlog_setup(ndb, key, end-key,
                                       elmt.database, elmt.name,
                                       TRUE);
unknown's avatar
unknown committed
6223 6224
      }
#endif
6225
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
6226 6227
    }
  }
6228
  while (unhandled && retries);
unknown's avatar
unknown committed
6229

6230
  DBUG_RETURN(-(skipped + unhandled));
unknown's avatar
unknown committed
6231
}
6232

6233 6234 6235
int ndbcluster_find_files(handlerton *hton, THD *thd,
                          const char *db,
                          const char *path,
6236
                          const char *wild, bool dir, List<char> *files)
unknown's avatar
unknown committed
6237
{
6238 6239 6240
  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
6241
  uint i;
6242
  Ndb* ndb;
6243
  char name[FN_REFLEN];
unknown's avatar
unknown committed
6244
  HASH ndb_tables, ok_tables;
unknown's avatar
unknown committed
6245
  NDBDICT::List list;
6246 6247 6248 6249

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

6250
  if (dir)
unknown's avatar
unknown committed
6251
    DBUG_RETURN(0); // Discover of databases not yet supported
6252

unknown's avatar
unknown committed
6253
  // List tables in NDB
6254
  NDBDICT *dict= ndb->getDictionary();
unknown's avatar
unknown committed
6255
  if (dict->listObjects(list, 
6256
                        NdbDictionary::Object::UserTable) != 0)
unknown's avatar
unknown committed
6257
    ERR_RETURN(dict->getNdbError());
6258

unknown's avatar
unknown committed
6259
  if (hash_init(&ndb_tables, system_charset_info,list.count,0,0,
6260
                (hash_get_key)tables_get_key,0,0))
unknown's avatar
unknown committed
6261 6262 6263 6264 6265 6266
  {
    DBUG_PRINT("error", ("Failed to init HASH ndb_tables"));
    DBUG_RETURN(-1);
  }

  if (hash_init(&ok_tables, system_charset_info,32,0,0,
6267
                (hash_get_key)tables_get_key,0,0))
unknown's avatar
unknown committed
6268 6269 6270 6271 6272 6273
  {
    DBUG_PRINT("error", ("Failed to init HASH ok_tables"));
    hash_free(&ndb_tables);
    DBUG_RETURN(-1);
  }  

unknown's avatar
unknown committed
6274 6275
  for (i= 0 ; i < list.count ; i++)
  {
unknown's avatar
unknown committed
6276
    NDBDICT::List::Element& elmt= list.elements[i];
unknown's avatar
unknown committed
6277
    if (IS_TMP_PREFIX(elmt.name) || IS_NDB_BLOB_PREFIX(elmt.name))
unknown's avatar
unknown committed
6278 6279 6280 6281
    {
      DBUG_PRINT("info", ("Skipping %s.%s in NDB", elmt.database, elmt.name));
      continue;
    }
unknown's avatar
unknown committed
6282
    DBUG_PRINT("info", ("Found %s/%s in NDB", elmt.database, elmt.name));
unknown's avatar
unknown committed
6283

6284
    // Add only tables that belongs to db
unknown's avatar
unknown committed
6285
    if (my_strcasecmp(system_charset_info, elmt.database, db))
6286
      continue;
unknown's avatar
unknown committed
6287

unknown's avatar
unknown committed
6288
    // Apply wildcard to list of tables in NDB
6289
    if (wild)
6290
    {
6291 6292
      if (lower_case_table_names)
      {
unknown's avatar
unknown committed
6293
        if (wild_case_compare(files_charset_info, elmt.name, wild))
6294
          continue;
6295
      }
unknown's avatar
unknown committed
6296
      else if (wild_compare(elmt.name,wild,0))
6297
        continue;
6298
    }
unknown's avatar
unknown committed
6299 6300
    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
6301 6302
  }

unknown's avatar
unknown committed
6303 6304 6305 6306 6307
  char *file_name;
  List_iterator<char> it(*files);
  List<char> delete_list;
  while ((file_name=it++))
  {
6308
    bool file_on_disk= false;
unknown's avatar
unknown committed
6309 6310 6311 6312
    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));
6313
      file_on_disk= true;
unknown's avatar
unknown committed
6314 6315
    }
    
6316
    // Check for .ndb file with this name
6317
    build_table_filename(name, sizeof(name), db, file_name, ha_ndb_ext, 0);
unknown's avatar
unknown committed
6318
    DBUG_PRINT("info", ("Check access for %s", name));
6319
    if (my_access(name, F_OK))
unknown's avatar
unknown committed
6320 6321 6322
    {
      DBUG_PRINT("info", ("%s did not exist on disk", name));     
      // .ndb file did not exist on disk, another table type
6323
      if (file_on_disk)
6324 6325 6326 6327 6328
      {
	// Ignore this ndb table
	gptr record=  hash_search(&ndb_tables, file_name, strlen(file_name));
	DBUG_ASSERT(record);
	hash_delete(&ndb_tables, record);
6329 6330 6331 6332
	push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
			    ER_TABLE_EXISTS_ERROR,
			    "Local table %s.%s shadows ndb table",
			    db, file_name);
6333
      }
6334 6335 6336 6337
      continue;
    }
    if (file_on_disk) 
    {
6338
      // File existed in NDB and as frm file, put in ok_tables list
6339
      my_hash_insert(&ok_tables, (byte*)file_name);
unknown's avatar
unknown committed
6340
      continue;
6341
    }
unknown's avatar
unknown committed
6342 6343 6344
    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.
6345
    if (ndbcluster_table_exists_in_engine(hton, thd, db, file_name) == 0)    
unknown's avatar
unknown committed
6346 6347 6348 6349 6350 6351 6352
    {
      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));
    }
  }
6353

unknown's avatar
unknown committed
6354 6355 6356
#ifdef HAVE_NDB_BINLOG
  /* setup logging to binlog for all discovered tables */
  {
6357
    char *end, *end1= name +
6358
      build_table_filename(name, sizeof(name), db, "", "", 0);
unknown's avatar
unknown committed
6359 6360 6361
    for (i= 0; i < ok_tables.records; i++)
    {
      file_name= (char*)hash_element(&ok_tables, i);
6362 6363
      end= end1 +
        tablename_to_filename(file_name, end1, sizeof(name) - (end1 - name));
6364 6365 6366 6367
      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
6368 6369 6370 6371
    }
  }
#endif

unknown's avatar
unknown committed
6372 6373 6374 6375
  // 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++)
6376
  {
unknown's avatar
unknown committed
6377 6378
    file_name= hash_element(&ndb_tables, i);
    if (!hash_search(&ok_tables, file_name, strlen(file_name)))
6379
    {
6380
      build_table_filename(name, sizeof(name), db, file_name, reg_ext, 0);
6381
      if (my_access(name, F_OK))
unknown's avatar
unknown committed
6382 6383 6384 6385 6386 6387
      {
        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
6388 6389
    }
  }
6390

unknown's avatar
unknown committed
6391 6392
  // Lock mutex before deleting and creating frm files
  pthread_mutex_lock(&LOCK_open);
6393

unknown's avatar
unknown committed
6394 6395 6396 6397 6398
  if (!global_read_lock)
  {
    // Delete old files
    List_iterator_fast<char> it3(delete_list);
    while ((file_name=it3++))
6399 6400
    {
      DBUG_PRINT("info", ("Remove table %s/%s", db, file_name));
unknown's avatar
unknown committed
6401 6402 6403 6404
      // Delete the table and all related files
      TABLE_LIST table_list;
      bzero((char*) &table_list,sizeof(table_list));
      table_list.db= (char*) db;
6405
      table_list.alias= table_list.table_name= (char*)file_name;
6406
      (void)mysql_rm_table_part2(thd, &table_list,
unknown's avatar
Merge  
unknown committed
6407 6408 6409 6410
                                                                 /* if_exists */ FALSE,
                                                                 /* drop_temporary */ FALSE,
                                                                 /* drop_view */ FALSE,
                                                                 /* dont_log_query*/ TRUE);
6411 6412
      /* Clear error message that is returned when table is deleted */
      thd->clear_error();
6413 6414 6415
    }
  }

unknown's avatar
unknown committed
6416 6417 6418 6419
  // Create new files
  List_iterator_fast<char> it2(create_list);
  while ((file_name=it2++))
  {  
6420
    DBUG_PRINT("info", ("Table %s need discovery", file_name));
6421
    if (ndb_create_table_from_engine(thd, db, file_name) == 0)
6422
      files->push_back(thd->strdup(file_name)); 
unknown's avatar
unknown committed
6423 6424
  }

unknown's avatar
unknown committed
6425
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
6426 6427
  
  hash_free(&ok_tables);
6428
  hash_free(&ndb_tables);
6429
  } // extra bracket to avoid gcc 2.95.3 warning
6430
  DBUG_RETURN(0);    
unknown's avatar
unknown committed
6431 6432 6433 6434 6435 6436 6437 6438
}


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

6439 6440 6441 6442
/* Call back after cluster connect */
static int connect_callback()
{
  update_status_variables(g_ndb_cluster_connection);
unknown's avatar
unknown committed
6443 6444 6445 6446 6447 6448 6449

  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
6450
  pthread_cond_signal(&COND_ndb_util_thread);
6451 6452 6453
  return 0;
}

6454
extern int ndb_dictionary_is_mysqld;
unknown's avatar
unknown committed
6455

6456
static int ndbcluster_init(void *p)
unknown's avatar
unknown committed
6457
{
unknown's avatar
unknown committed
6458
  int res;
unknown's avatar
unknown committed
6459
  DBUG_ENTER("ndbcluster_init");
6460

6461
  ndb_dictionary_is_mysqld= 1;
6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478
  ndbcluster_hton= (handlerton *)p;

  {
    handlerton *h= ndbcluster_hton;
    h->state=            have_ndbcluster;
    h->db_type=          DB_TYPE_NDBCLUSTER;
    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 */
    h->partition_flags=  ndbcluster_partition_flags; /* Partition flags */
    h->alter_table_flags=ndbcluster_alter_table_flags; /* Alter table flags */
    h->fill_files_table= ndbcluster_fill_files_table;
unknown's avatar
unknown committed
6479 6480 6481
#ifdef HAVE_NDB_BINLOG
    ndbcluster_binlog_init_handlerton();
#endif
6482 6483 6484 6485
    h->flags=            HTON_CAN_RECREATE | HTON_TEMPORARY_NOT_SUPPORTED;
    h->discover=         ndbcluster_discover;
    h->find_files= ndbcluster_find_files;
    h->table_exists_in_engine= ndbcluster_table_exists_in_engine;
unknown's avatar
unknown committed
6486 6487
  }

unknown's avatar
unknown committed
6488 6489 6490
  if (have_ndbcluster != SHOW_OPTION_YES)
    DBUG_RETURN(0); // nothing else to do

unknown's avatar
unknown committed
6491 6492 6493
  // Initialize ndb interface
  ndb_init_internal();

6494
  // Set connectstring if specified
6495 6496
  if (opt_ndbcluster_connectstring != 0)
    DBUG_PRINT("connectstring", ("%s", opt_ndbcluster_connectstring));     
6497
  if ((g_ndb_cluster_connection=
6498
       new Ndb_cluster_connection(opt_ndbcluster_connectstring)) == 0)
6499
  {
6500
    DBUG_PRINT("error",("Ndb_cluster_connection(%s)",
6501
                        opt_ndbcluster_connectstring));
unknown's avatar
unknown committed
6502
    goto ndbcluster_init_error;
6503
  }
unknown's avatar
ndb:  
unknown committed
6504 6505 6506 6507 6508
  {
    char buf[128];
    my_snprintf(buf, sizeof(buf), "mysqld --server-id=%d", server_id);
    g_ndb_cluster_connection->set_name(buf);
  }
6509 6510 6511
  g_ndb_cluster_connection->set_optimized_node_selection
    (opt_ndb_optimized_node_selection);

unknown's avatar
unknown committed
6512
  // Create a Ndb object to open the connection  to NDB
6513 6514 6515 6516 6517
  if ( (g_ndb= new Ndb(g_ndb_cluster_connection, "sys")) == 0 )
  {
    DBUG_PRINT("error", ("failed to create global ndb object"));
    goto ndbcluster_init_error;
  }
unknown's avatar
unknown committed
6518 6519 6520
  if (g_ndb->init() != 0)
  {
    ERR_PRINT (g_ndb->getNdbError());
unknown's avatar
unknown committed
6521
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
6522
  }
unknown's avatar
unknown committed
6523

unknown's avatar
unknown committed
6524
  if ((res= g_ndb_cluster_connection->connect(0,0,0)) == 0)
unknown's avatar
unknown committed
6525
  {
6526
    connect_callback();
unknown's avatar
unknown committed
6527
    DBUG_PRINT("info",("NDBCLUSTER storage engine at %s on port %d",
6528 6529
                       g_ndb_cluster_connection->get_connected_host(),
                       g_ndb_cluster_connection->get_connected_port()));
6530
    g_ndb_cluster_connection->wait_until_ready(10,3);
unknown's avatar
unknown committed
6531
  } 
unknown's avatar
unknown committed
6532
  else if (res == 1)
unknown's avatar
unknown committed
6533
  {
6534
    if (g_ndb_cluster_connection->start_connect_thread(connect_callback)) 
6535
    {
unknown's avatar
unknown committed
6536
      DBUG_PRINT("error", ("g_ndb_cluster_connection->start_connect_thread()"));
unknown's avatar
unknown committed
6537 6538
      goto ndbcluster_init_error;
    }
6539
#ifndef DBUG_OFF
unknown's avatar
unknown committed
6540 6541
    {
      char buf[1024];
6542
      DBUG_PRINT("info",
6543 6544 6545 6546
                 ("NDBCLUSTER storage engine not started, "
                  "will connect using %s",
                  g_ndb_cluster_connection->
                  get_connectstring(buf,sizeof(buf))));
unknown's avatar
unknown committed
6547
    }
6548
#endif
unknown's avatar
unknown committed
6549
  }
unknown's avatar
unknown committed
6550
  else
unknown's avatar
unknown committed
6551 6552 6553
  {
    DBUG_ASSERT(res == -1);
    DBUG_PRINT("error", ("permanent error"));
unknown's avatar
unknown committed
6554
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
6555
  }
unknown's avatar
unknown committed
6556
  
unknown's avatar
unknown committed
6557 6558 6559
  (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
6560 6561
#ifdef HAVE_NDB_BINLOG
  /* start the ndb injector thread */
6562 6563
  if (ndbcluster_binlog_start())
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
6564
#endif /* HAVE_NDB_BINLOG */
unknown's avatar
unknown committed
6565

unknown's avatar
Merge  
unknown committed
6566 6567
  pthread_mutex_init(&LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST);
  pthread_cond_init(&COND_ndb_util_thread, NULL);
6568

unknown's avatar
Merge  
unknown committed
6569

unknown's avatar
unknown committed
6570
  ndb_cache_check_time = opt_ndb_cache_check_time;
unknown's avatar
Merge  
unknown committed
6571 6572 6573 6574 6575
  // 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"));
6576 6577 6578 6579
    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
6580 6581
    goto ndbcluster_init_error;
  }
unknown's avatar
unknown committed
6582

unknown's avatar
unknown committed
6583
  ndbcluster_inited= 1;
6584
  DBUG_RETURN(FALSE);
unknown's avatar
Merge  
unknown committed
6585

6586
ndbcluster_init_error:
unknown's avatar
unknown committed
6587
  if (g_ndb)
6588 6589 6590 6591 6592
    delete g_ndb;
  g_ndb= NULL;
  if (g_ndb_cluster_connection)
    delete g_ndb_cluster_connection;
  g_ndb_cluster_connection= NULL;
6593
  have_ndbcluster= SHOW_OPTION_DISABLED;	// If we couldn't use handler
6594 6595
  ndbcluster_hton->state= SHOW_OPTION_DISABLED;               // If we couldn't use handler

6596
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
6597 6598
}

6599
static int ndbcluster_end(handlerton *hton, ha_panic_function type)
unknown's avatar
unknown committed
6600 6601
{
  DBUG_ENTER("ndbcluster_end");
unknown's avatar
Merge  
unknown committed
6602

6603 6604 6605
  if (!ndbcluster_inited)
    DBUG_RETURN(0);

6606 6607 6608
#ifdef HAVE_NDB_BINLOG
  {
    pthread_mutex_lock(&ndbcluster_mutex);
6609
    while (ndbcluster_open_tables.records)
6610 6611
    {
      NDB_SHARE *share=
6612
        (NDB_SHARE*) hash_element(&ndbcluster_open_tables, 0);
6613 6614 6615 6616 6617 6618 6619 6620 6621 6622 6623
#ifndef DBUG_OFF
      fprintf(stderr, "NDB: table share %s with use_count %d not freed\n",
              share->key, share->use_count);
#endif
      real_free_share(&share);
    }
    pthread_mutex_unlock(&ndbcluster_mutex);
  }
#endif
  hash_free(&ndbcluster_open_tables);

unknown's avatar
unknown committed
6624
  if (g_ndb)
6625 6626
  {
#ifndef DBUG_OFF
unknown's avatar
unknown committed
6627 6628
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
6629 6630 6631 6632 6633 6634 6635 6636 6637 6638
    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
6639
    delete g_ndb;
unknown's avatar
unknown committed
6640
    g_ndb= NULL;
6641
  }
unknown's avatar
unknown committed
6642
  delete g_ndb_cluster_connection;
6643
  g_ndb_cluster_connection= NULL;
6644

unknown's avatar
unknown committed
6645 6646 6647
  // cleanup ndb interface
  ndb_end_internal();

unknown's avatar
unknown committed
6648
  pthread_mutex_destroy(&ndbcluster_mutex);
unknown's avatar
Merge  
unknown committed
6649 6650
  pthread_mutex_destroy(&LOCK_ndb_util_thread);
  pthread_cond_destroy(&COND_ndb_util_thread);
unknown's avatar
unknown committed
6651 6652 6653 6654
  ndbcluster_inited= 0;
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
6655 6656 6657 6658 6659 6660
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)
unknown's avatar
unknown committed
6661
    m_part_info->print_no_partition_found(table);
unknown's avatar
unknown committed
6662 6663 6664 6665 6666 6667
  else
    handler::print_error(error, errflag);
  DBUG_VOID_RETURN;
}


6668 6669 6670 6671 6672
/*
  Static error print function called from
  static handler method ndbcluster_commit
  and ndbcluster_rollback
*/
6673 6674

void ndbcluster_print_error(int error, const NdbOperation *error_op)
6675
{
6676
  DBUG_ENTER("ndbcluster_print_error");
unknown's avatar
unknown committed
6677
  TABLE_SHARE share;
6678
  const char *tab_name= (error_op) ? error_op->getTableName() : "";
unknown's avatar
unknown committed
6679 6680 6681 6682
  share.db.str= (char*) "";
  share.db.length= 0;
  share.table_name.str= (char *) tab_name;
  share.table_name.length= strlen(tab_name);
6683
  ha_ndbcluster error_handler(ndbcluster_hton, &share);
6684
  error_handler.print_error(error, MYF(0));
unknown's avatar
unknown committed
6685
  DBUG_VOID_RETURN;
6686
}
unknown's avatar
unknown committed
6687

6688 6689 6690
/**
 * Set a given location from full pathname to database name
 *
unknown's avatar
unknown committed
6691
 */
6692
void ha_ndbcluster::set_dbname(const char *path_name, char *dbname)
unknown's avatar
unknown committed
6693
{
unknown's avatar
unknown committed
6694 6695 6696 6697
  char *end, *ptr, *tmp_name;
  char tmp_buff[FN_REFLEN];
 
  tmp_name= tmp_buff;
unknown's avatar
unknown committed
6698
  /* Scan name from the end */
6699 6700 6701 6702 6703 6704
  ptr= strend(path_name)-1;
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  ptr--;
  end= ptr;
unknown's avatar
unknown committed
6705 6706 6707 6708
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  uint name_len= end - ptr;
unknown's avatar
unknown committed
6709 6710
  memcpy(tmp_name, ptr + 1, name_len);
  tmp_name[name_len]= '\0';
unknown's avatar
unknown committed
6711 6712
#ifdef __WIN__
  /* Put to lower case */
6713
  
unknown's avatar
unknown committed
6714
  ptr= tmp_name;
unknown's avatar
unknown committed
6715 6716
  
  while (*ptr != '\0') {
6717
    *ptr= tolower(*ptr);
unknown's avatar
unknown committed
6718 6719 6720
    ptr++;
  }
#endif
unknown's avatar
unknown committed
6721
  filename_to_tablename(tmp_name, dbname, FN_REFLEN);
unknown's avatar
unknown committed
6722 6723
}

6724 6725 6726 6727 6728 6729 6730 6731 6732
/*
  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
6733 6734 6735 6736 6737 6738 6739
/**
 * 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
6740 6741 6742 6743
  char *end, *ptr, *tmp_name;
  char tmp_buff[FN_REFLEN];

  tmp_name= tmp_buff;
unknown's avatar
unknown committed
6744
  /* Scan name from the end */
6745 6746
  end= strend(path_name)-1;
  ptr= end;
unknown's avatar
unknown committed
6747 6748 6749
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
6750
  uint name_len= end - ptr;
unknown's avatar
unknown committed
6751 6752
  memcpy(tmp_name, ptr + 1, end - ptr);
  tmp_name[name_len]= '\0';
unknown's avatar
unknown committed
6753 6754
#ifdef __WIN__
  /* Put to lower case */
unknown's avatar
unknown committed
6755
  ptr= tmp_name;
unknown's avatar
unknown committed
6756 6757 6758 6759 6760 6761
  
  while (*ptr != '\0') {
    *ptr= tolower(*ptr);
    ptr++;
  }
#endif
unknown's avatar
unknown committed
6762
  filename_to_tablename(tmp_name, tabname, FN_REFLEN);
unknown's avatar
unknown committed
6763 6764 6765
}

/*
6766
  Set m_tabname from full pathname to table file 
unknown's avatar
unknown committed
6767 6768
 */

6769
void ha_ndbcluster::set_tabname(const char *path_name)
unknown's avatar
unknown committed
6770
{
6771
  set_tabname(path_name, m_tabname);
unknown's avatar
unknown committed
6772 6773 6774 6775
}


ha_rows 
unknown's avatar
unknown committed
6776 6777 6778 6779
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
6780
  uint key_length= key_info->key_length;
6781
  NDB_INDEX_TYPE idx_type= get_index_type(inx);  
unknown's avatar
unknown committed
6782 6783

  DBUG_ENTER("records_in_range");
6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796
  // 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);
  
6797 6798 6799 6800 6801 6802
  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];
6803
    const NDBINDEX* index= d.index;
6804 6805 6806 6807 6808 6809 6810 6811 6812 6813
    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;
6814
      Ndb_local_table_statistics *info= m_table_info;
6815 6816 6817 6818 6819 6820 6821 6822
      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;
6823
        if ((res=ndb_get_table_statistics(ndb, m_table, &stat)) != 0)
6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836
          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
6837 6838
      if ((trans=m_active_trans) == NULL || 
	  trans->commitStatus() != NdbTransaction::Started)
6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874
      {
        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);
  }

6875
  DBUG_RETURN(10); /* Good guess when you don't know anything */
unknown's avatar
unknown committed
6876 6877
}

6878
ulonglong ha_ndbcluster::table_flags(void) const
6879 6880
{
  if (m_ha_not_exact_count)
6881 6882
    return m_table_flags & ~HA_STATS_RECORDS_IS_EXACT;
  return m_table_flags;
6883 6884 6885
}
const char * ha_ndbcluster::table_type() const 
{
6886
  return("NDBCLUSTER");
6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903
}
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
6904 6905 6906 6907
uint ha_ndbcluster::max_supported_key_part_length() const
{
  return NDB_MAX_KEY_SIZE;
}
6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928
bool ha_ndbcluster::low_byte_first() const
{ 
#ifdef WORDS_BIGENDIAN
  return FALSE;
#else
  return TRUE;
#endif
}
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
6929

6930 6931
uint8 ha_ndbcluster::table_cache_type()
{
unknown's avatar
Merge  
unknown committed
6932 6933 6934 6935 6936 6937
  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,
6938
                         Uint64 *commit_count)
unknown's avatar
Merge  
unknown committed
6939
{
6940 6941
  char name[FN_REFLEN];
  NDB_SHARE *share;
unknown's avatar
unknown committed
6942 6943
  DBUG_ENTER("ndb_get_commitcount");

6944
  build_table_filename(name, sizeof(name), dbname, tabname, "", 0);
6945 6946 6947 6948 6949 6950 6951
  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
6952
    DBUG_PRINT("info", ("Table %s not found in ndbcluster_open_tables", name));
6953 6954 6955 6956 6957 6958
    DBUG_RETURN(1);
  }
  share->use_count++;
  pthread_mutex_unlock(&ndbcluster_mutex);

  pthread_mutex_lock(&share->mutex);
unknown's avatar
Merge  
unknown committed
6959 6960
  if (ndb_cache_check_time > 0)
  {
6961
    if (share->commit_count != 0)
unknown's avatar
Merge  
unknown committed
6962
    {
6963
      *commit_count= share->commit_count;
unknown's avatar
unknown committed
6964 6965 6966
      char buff[22];
      DBUG_PRINT("info", ("Getting commit_count: %s from share",
                          llstr(share->commit_count, buff)));
6967
      pthread_mutex_unlock(&share->mutex);
unknown's avatar
unknown committed
6968
      free_share(&share);
6969
      DBUG_RETURN(0);
unknown's avatar
Merge  
unknown committed
6970 6971
    }
  }
6972
  DBUG_PRINT("info", ("Get commit_count from NDB"));
unknown's avatar
Merge  
unknown committed
6973 6974 6975 6976
  Ndb *ndb;
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(1);
  ndb->setDatabaseName(dbname);
6977 6978
  uint lock= share->commit_count_lock;
  pthread_mutex_unlock(&share->mutex);
unknown's avatar
Merge  
unknown committed
6979 6980

  struct Ndb_statistics stat;
6981
  {
6982 6983 6984 6985 6986 6987 6988
    Ndb_table_guard ndbtab_g(ndb->getDictionary(), tabname);
    if (ndbtab_g.get_table() == 0
        || ndb_get_table_statistics(ndb, ndbtab_g.get_table(), &stat))
    {
      free_share(&share);
      DBUG_RETURN(1);
    }
6989 6990 6991
  }

  pthread_mutex_lock(&share->mutex);
unknown's avatar
unknown committed
6992
  if (share->commit_count_lock == lock)
6993
  {
unknown's avatar
unknown committed
6994 6995 6996
    char buff[22];
    DBUG_PRINT("info", ("Setting commit_count to %s",
                        llstr(stat.commit_count, buff)));
6997 6998 6999 7000 7001 7002 7003 7004 7005
    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
7006
  free_share(&share);
unknown's avatar
Merge  
unknown committed
7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042
  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,
7043 7044
                                   char *full_name, uint full_name_len,
                                   ulonglong *engine_data)
unknown's avatar
Merge  
unknown committed
7045 7046 7047 7048 7049
{
  Uint64 commit_count;
  bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
  char *dbname= full_name;
  char *tabname= dbname+strlen(dbname)+1;
unknown's avatar
unknown committed
7050 7051
  char buff[22], buff2[22];
  DBUG_ENTER("ndbcluster_cache_retrieval_allowed");
7052 7053
  DBUG_PRINT("enter", ("dbname: %s, tabname: %s, is_autocommit: %d",
                       dbname, tabname, is_autocommit));
unknown's avatar
Merge  
unknown committed
7054 7055

  if (!is_autocommit)
7056 7057
  {
    DBUG_PRINT("exit", ("No, don't use cache in transaction"));
unknown's avatar
Merge  
unknown committed
7058
    DBUG_RETURN(FALSE);
7059
  }
unknown's avatar
Merge  
unknown committed
7060 7061 7062

  if (ndb_get_commitcount(thd, dbname, tabname, &commit_count))
  {
7063 7064
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, could not retrieve commit_count"));
unknown's avatar
Merge  
unknown committed
7065 7066
    DBUG_RETURN(FALSE);
  }
unknown's avatar
unknown committed
7067 7068
  DBUG_PRINT("info", ("*engine_data: %s, commit_count: %s",
                      llstr(*engine_data, buff), llstr(commit_count, buff2)));
7069
  if (commit_count == 0)
unknown's avatar
Merge  
unknown committed
7070
  {
7071 7072
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, local commit has been performed"));
unknown's avatar
Merge  
unknown committed
7073 7074
    DBUG_RETURN(FALSE);
  }
7075 7076 7077 7078 7079 7080
  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
7081

unknown's avatar
unknown committed
7082 7083
  DBUG_PRINT("exit", ("OK to use cache, engine_data: %s",
                      llstr(*engine_data, buff)));
unknown's avatar
Merge  
unknown committed
7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111
  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,
7112 7113 7114
                                          char *full_name, uint full_name_len,
                                          qc_engine_callback *engine_callback,
                                          ulonglong *engine_data)
unknown's avatar
Merge  
unknown committed
7115
{
unknown's avatar
unknown committed
7116 7117
  Uint64 commit_count;
  char buff[22];
unknown's avatar
Merge  
unknown committed
7118
  bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
unknown's avatar
unknown committed
7119
  DBUG_ENTER("ha_ndbcluster::register_query_cache_table");
7120 7121 7122
  DBUG_PRINT("enter",("dbname: %s, tabname: %s, is_autocommit: %d",
		      m_dbname, m_tabname, is_autocommit));

unknown's avatar
Merge  
unknown committed
7123
  if (!is_autocommit)
7124
  {
unknown's avatar
unknown committed
7125
    DBUG_PRINT("exit", ("Can't register table during transaction"));
unknown's avatar
Merge  
unknown committed
7126
    DBUG_RETURN(FALSE);
7127
  }
unknown's avatar
Merge  
unknown committed
7128 7129 7130 7131

  if (ndb_get_commitcount(thd, m_dbname, m_tabname, &commit_count))
  {
    *engine_data= 0;
unknown's avatar
unknown committed
7132
    DBUG_PRINT("exit", ("Error, could not get commitcount"));
unknown's avatar
Merge  
unknown committed
7133 7134 7135 7136
    DBUG_RETURN(FALSE);
  }
  *engine_data= commit_count;
  *engine_callback= ndbcluster_cache_retrieval_allowed;
unknown's avatar
unknown committed
7137
  DBUG_PRINT("exit", ("commit_count: %s", llstr(commit_count, buff)));
7138
  DBUG_RETURN(commit_count > 0);
7139
}
unknown's avatar
unknown committed
7140

unknown's avatar
Merge  
unknown committed
7141

unknown's avatar
unknown committed
7142
/*
unknown's avatar
Merge  
unknown committed
7143
  Handling the shared NDB_SHARE structure that is needed to
unknown's avatar
unknown committed
7144 7145 7146 7147 7148 7149
  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
7150
static byte *ndbcluster_get_key(NDB_SHARE *share,uint *length,
7151
                                my_bool not_used __attribute__((unused)))
unknown's avatar
unknown committed
7152
{
unknown's avatar
unknown committed
7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165 7166 7167 7168 7169 7170
  *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
7171 7172 7173 7174 7175 7176
#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
7177 7178
  }
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
7179
}
unknown's avatar
unknown committed
7180 7181 7182
#else
#define dbug_print_open_tables()
#endif
unknown's avatar
unknown committed
7183

unknown's avatar
unknown committed
7184 7185 7186 7187 7188 7189 7190 7191 7192 7193 7194 7195 7196
#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)
{
7197
  THD *thd= current_thd;
unknown's avatar
unknown committed
7198 7199 7200 7201 7202 7203
  static ulong trailing_share_id= 0;
  DBUG_ENTER("handle_trailing_share");

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

7204 7205 7206 7207
  TABLE_LIST table_list;
  bzero((char*) &table_list,sizeof(table_list));
  table_list.db= share->db;
  table_list.alias= table_list.table_name= share->table_name;
7208
  close_cached_tables(thd, 0, &table_list, TRUE);
unknown's avatar
unknown committed
7209 7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237 7238

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

7239 7240 7241 7242 7243
  /*
    Ndb share has not been released as it should
  */
  DBUG_ASSERT(FALSE);

unknown's avatar
unknown committed
7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258 7259 7260 7261 7262 7263 7264 7265 7266 7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279 7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 7310 7311 7312 7313 7314 7315 7316 7317 7318 7319 7320 7321 7322 7323 7324 7325 7326 7327 7328 7329 7330 7331 7332
  /*
    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));
7333
  if (share->table)
unknown's avatar
unknown committed
7334
  {
7335 7336 7337 7338 7339 7340 7341 7342 7343 7344 7345
    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
7346 7347 7348 7349 7350 7351 7352 7353 7354 7355
  }
  /* 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
7356 7357 7358 7359
/*
  Increase refcount on existing share.
  Always returns share and cannot fail.
*/
unknown's avatar
unknown committed
7360
NDB_SHARE *ndbcluster_get_share(NDB_SHARE *share)
unknown's avatar
unknown committed
7361 7362
{
  pthread_mutex_lock(&ndbcluster_mutex);
unknown's avatar
unknown committed
7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 7375 7376 7377
  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
7378

unknown's avatar
unknown committed
7379 7380 7381 7382 7383 7384 7385 7386 7387 7388 7389 7390 7391 7392
/*
  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
7393

unknown's avatar
unknown committed
7394 7395 7396
NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table,
                                bool create_if_not_exists,
                                bool have_lock)
unknown's avatar
unknown committed
7397
{
unknown's avatar
unknown committed
7398
  THD *thd= current_thd;
unknown's avatar
unknown committed
7399
  NDB_SHARE *share;
unknown's avatar
unknown committed
7400 7401 7402 7403
  uint length= (uint) strlen(key);
  DBUG_ENTER("ndbcluster_get_share");
  DBUG_PRINT("enter", ("key: '%s'", key));

unknown's avatar
unknown committed
7404 7405 7406 7407 7408
  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
7409
  {
unknown's avatar
unknown committed
7410 7411 7412 7413 7414
    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
7415
      DBUG_RETURN(0);
unknown's avatar
unknown committed
7416 7417
    }
    if ((share= (NDB_SHARE*) my_malloc(sizeof(*share),
unknown's avatar
unknown committed
7418 7419
                                       MYF(MY_WME | MY_ZEROFILL))))
    {
unknown's avatar
unknown committed
7420 7421 7422 7423 7424
      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
7425
      share->state= NSS_INITIAL;
unknown's avatar
unknown committed
7426 7427 7428 7429
      /* 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
7430 7431
      if (my_hash_insert(&ndbcluster_open_tables, (byte*) share))
      {
unknown's avatar
unknown committed
7432 7433 7434 7435 7436
        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
7437
        DBUG_RETURN(0);
unknown's avatar
unknown committed
7438 7439
      }
      thr_lock_init(&share->lock);
unknown's avatar
unknown committed
7440
      pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST);
unknown's avatar
Merge  
unknown committed
7441
      share->commit_count= 0;
7442
      share->commit_count_lock= 0;
unknown's avatar
unknown committed
7443 7444 7445 7446
      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
7447 7448 7449
#ifdef HAVE_NDB_BINLOG
      ndbcluster_binlog_init_share(share, table);
#endif
unknown's avatar
unknown committed
7450
      *root_ptr= old_root;
7451 7452 7453
    }
    else
    {
unknown's avatar
unknown committed
7454 7455 7456 7457
      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
7458
      DBUG_RETURN(0);
unknown's avatar
unknown committed
7459 7460 7461
    }
  }
  share->use_count++;
7462

unknown's avatar
unknown committed
7463 7464
  dbug_print_open_tables();

unknown's avatar
unknown committed
7465
  DBUG_PRINT("info",
unknown's avatar
unknown committed
7466 7467
             ("0x%lx key: %s  key_length: %d  key: %s",
              share, share->key, share->key_length, key));
unknown's avatar
unknown committed
7468
  DBUG_PRINT("info",
unknown's avatar
unknown committed
7469 7470 7471 7472 7473
             ("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
7474
  DBUG_RETURN(share);
unknown's avatar
unknown committed
7475 7476
}

unknown's avatar
unknown committed
7477

unknown's avatar
unknown committed
7478
void ndbcluster_real_free_share(NDB_SHARE **share)
unknown's avatar
unknown committed
7479
{
unknown's avatar
unknown committed
7480
  DBUG_ENTER("ndbcluster_real_free_share");
unknown's avatar
unknown committed
7481 7482 7483 7484 7485 7486 7487 7488 7489 7490 7491 7492
  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);

unknown's avatar
unknown committed
7493 7494 7495
#ifdef HAVE_NDB_BINLOG
  if ((*share)->table)
  {
7496
    // (*share)->table->mem_root is freed by closefrm
unknown's avatar
unknown committed
7497
    closefrm((*share)->table, 0);
7498 7499
    // (*share)->table_share->mem_root is freed by free_table_share
    free_table_share((*share)->table_share);
unknown's avatar
unknown committed
7500 7501 7502 7503 7504 7505 7506 7507
#ifndef DBUG_OFF
    bzero((gptr)(*share)->table_share, sizeof(*(*share)->table_share));
    bzero((gptr)(*share)->table, sizeof(*(*share)->table));
    (*share)->table_share= 0;
    (*share)->table= 0;
#endif
  }
#endif
unknown's avatar
unknown committed
7508
  free_root(&(*share)->mem_root, MYF(0));
unknown's avatar
unknown committed
7509 7510 7511 7512
  my_free((gptr) *share, MYF(0));
  *share= 0;

  dbug_print_open_tables();
unknown's avatar
unknown committed
7513
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
7514 7515 7516 7517 7518
}

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

unknown's avatar
unknown committed
7520 7521
  have_lock == TRUE, pthread_mutex_lock(&ndbcluster_mutex) already taken
*/
unknown's avatar
unknown committed
7522
void ndbcluster_free_share(NDB_SHARE **share, bool have_lock)
unknown's avatar
unknown committed
7523
{
unknown's avatar
unknown committed
7524 7525 7526 7527 7528
  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
7529
  {
unknown's avatar
unknown committed
7530
    real_free_share(share);
unknown's avatar
unknown committed
7531
  }
unknown's avatar
unknown committed
7532 7533 7534 7535 7536 7537 7538 7539 7540 7541 7542 7543 7544
  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
7545 7546 7547
}


unknown's avatar
unknown committed
7548 7549
static 
int
7550
ndb_get_table_statistics(Ndb* ndb, const NDBTAB *ndbtab,
7551
                         struct Ndb_statistics * ndbstat)
unknown's avatar
unknown committed
7552
{
7553
  NdbTransaction* pTrans;
7554
  NdbError error;
7555 7556
  int retries= 10;
  int retry_sleep= 30 * 1000; /* 30 milliseconds */
unknown's avatar
unknown committed
7557 7558
  char buff[22], buff2[22], buff3[22], buff4[22];
  DBUG_ENTER("ndb_get_table_statistics");
unknown's avatar
unknown committed
7559
  DBUG_PRINT("enter", ("table: %s", ndbtab->getName()));
7560

7561 7562
  DBUG_ASSERT(ndbtab != 0);

7563
  do
unknown's avatar
unknown committed
7564
  {
7565
    Uint64 rows, commits, fixed_mem, var_mem;
7566
    Uint32 size;
unknown's avatar
unknown committed
7567
    Uint32 count= 0;
7568 7569
    Uint64 sum_rows= 0;
    Uint64 sum_commits= 0;
unknown's avatar
unknown committed
7570 7571
    Uint64 sum_row_size= 0;
    Uint64 sum_mem= 0;
7572 7573 7574 7575 7576
    NdbScanOperation*pOp;
    NdbResultSet *rs;
    int check;

    if ((pTrans= ndb->startTransaction()) == NULL)
7577
    {
7578 7579 7580
      error= ndb->getNdbError();
      goto retry;
    }
unknown's avatar
unknown committed
7581
      
unknown's avatar
unknown committed
7582
    if ((pOp= pTrans->getNdbScanOperation(ndbtab)) == NULL)
7583 7584 7585
    {
      error= pTrans->getNdbError();
      goto retry;
7586
    }
unknown's avatar
unknown committed
7587
    
7588
    if (pOp->readTuples(NdbOperation::LM_CommittedRead))
7589 7590 7591 7592
    {
      error= pOp->getNdbError();
      goto retry;
    }
unknown's avatar
unknown committed
7593
    
7594 7595 7596 7597 7598
    if (pOp->interpret_exit_last_row() == -1)
    {
      error= pOp->getNdbError();
      goto retry;
    }
unknown's avatar
unknown committed
7599 7600 7601
    
    pOp->getValue(NdbDictionary::Column::ROW_COUNT, (char*)&rows);
    pOp->getValue(NdbDictionary::Column::COMMIT_COUNT, (char*)&commits);
7602
    pOp->getValue(NdbDictionary::Column::ROW_SIZE, (char*)&size);
7603 7604 7605 7606
    pOp->getValue(NdbDictionary::Column::FRAGMENT_FIXED_MEMORY, 
		  (char*)&fixed_mem);
    pOp->getValue(NdbDictionary::Column::FRAGMENT_VARSIZED_MEMORY, 
		  (char*)&var_mem);
unknown's avatar
unknown committed
7607
    
7608 7609 7610
    if (pTrans->execute(NdbTransaction::NoCommit,
                        NdbTransaction::AbortOnError,
                        TRUE) == -1)
7611
    {
7612 7613
      error= pTrans->getNdbError();
      goto retry;
7614
    }
unknown's avatar
unknown committed
7615
    
unknown's avatar
unknown committed
7616
    while ((check= pOp->nextResult(TRUE, TRUE)) == 0)
unknown's avatar
unknown committed
7617 7618 7619
    {
      sum_rows+= rows;
      sum_commits+= commits;
7620
      if (sum_row_size < size)
7621
        sum_row_size= size;
7622
      sum_mem+= fixed_mem + var_mem;
7623
      count++;
unknown's avatar
unknown committed
7624 7625 7626
    }
    
    if (check == -1)
7627 7628 7629 7630
    {
      error= pOp->getNdbError();
      goto retry;
    }
unknown's avatar
unknown committed
7631

7632
    pOp->close(TRUE);
unknown's avatar
unknown committed
7633

unknown's avatar
unknown committed
7634
    ndb->closeTransaction(pTrans);
7635 7636 7637 7638 7639 7640

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

unknown's avatar
unknown committed
7641 7642 7643 7644 7645 7646 7647
    DBUG_PRINT("exit", ("records: %s  commits: %s "
                        "row_size: %s  mem: %s count: %u",
			llstr(sum_rows, buff),
                        llstr(sum_commits, buff2),
                        llstr(sum_row_size, buff3),
                        llstr(sum_mem, buff4),
                        count));
7648

unknown's avatar
unknown committed
7649
    DBUG_RETURN(0);
7650 7651 7652 7653 7654 7655 7656 7657 7658 7659 7660 7661
retry:
    if (pTrans)
    {
      ndb->closeTransaction(pTrans);
      pTrans= NULL;
    }
    if (error.status == NdbError::TemporaryError && retries--)
    {
      my_sleep(retry_sleep);
      continue;
    }
    break;
7662
  } while(1);
7663 7664
  DBUG_PRINT("exit", ("failed, error %u(%s)", error.code, error.message));
  ERR_RETURN(error);
unknown's avatar
unknown committed
7665 7666
}

7667 7668 7669 7670 7671
/*
  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
7672
int ha_ndbcluster::write_ndb_file(const char *name)
7673 7674 7675 7676 7677 7678
{
  File file;
  bool error=1;
  char path[FN_REFLEN];
  
  DBUG_ENTER("write_ndb_file");
unknown's avatar
unknown committed
7679
  DBUG_PRINT("enter", ("name: %s", name));
7680

unknown's avatar
unknown committed
7681
  (void)strxnmov(path, FN_REFLEN-1, 
unknown's avatar
unknown committed
7682
                 mysql_data_home,"/",name,ha_ndb_ext,NullS);
7683 7684 7685 7686 7687 7688 7689 7690 7691 7692

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

unknown's avatar
unknown committed
7693
void 
7694 7695
ha_ndbcluster::release_completed_operations(NdbTransaction *trans,
					    bool force_release)
unknown's avatar
unknown committed
7696 7697 7698 7699 7700 7701 7702 7703
{
  if (trans->hasBlobOperation())
  {
    /* We are reading/writing BLOB fields, 
       releasing operation records is unsafe
    */
    return;
  }
7704 7705 7706 7707 7708 7709 7710 7711 7712 7713
  if (!force_release)
  {
    if (get_thd_ndb(current_thd)->query_state & NDB_QUERY_MULTI_READ_RANGE)
    {
      /* We are batching reads and have not consumed all fetched
	 rows yet, releasing operation records is unsafe 
      */
      return;
    }
  }
unknown's avatar
unknown committed
7714
  trans->releaseCompletedOperations();
7715 7716
}

7717
int
7718
ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
7719 7720 7721 7722
                                      KEY_MULTI_RANGE *ranges, 
                                      uint range_count,
                                      bool sorted, 
                                      HANDLER_BUFFER *buffer)
7723 7724
{
  DBUG_ENTER("ha_ndbcluster::read_multi_range_first");
7725
  m_write_op= FALSE;
unknown's avatar
unknown committed
7726
  
7727 7728
  int res;
  KEY* key_info= table->key_info + active_index;
7729
  NDB_INDEX_TYPE index_type= get_index_type(active_index);
unknown's avatar
unknown committed
7730
  ulong reclength= table_share->reclength;
7731
  NdbOperation* op;
7732
  Thd_ndb *thd_ndb= get_thd_ndb(current_thd);
7733

7734
  if (uses_blob_value())
unknown's avatar
unknown committed
7735 7736 7737 7738
  {
    /**
     * blobs can't be batched currently
     */
unknown's avatar
unknown committed
7739
    m_disable_multi_read= TRUE;
unknown's avatar
unknown committed
7740
    DBUG_RETURN(handler::read_multi_range_first(found_range_p, 
7741 7742 7743 7744
                                                ranges, 
                                                range_count,
                                                sorted, 
                                                buffer));
unknown's avatar
unknown committed
7745
  }
7746
  thd_ndb->query_state|= NDB_QUERY_MULTI_READ_RANGE;
unknown's avatar
unknown committed
7747
  m_disable_multi_read= FALSE;
7748 7749 7750 7751

  /**
   * Copy arguments into member variables
   */
7752 7753 7754
  m_multi_ranges= ranges;
  multi_range_curr= ranges;
  multi_range_end= ranges+range_count;
7755 7756 7757
  multi_range_sorted= sorted;
  multi_range_buffer= buffer;

7758 7759 7760 7761 7762 7763 7764 7765 7766 7767 7768
  /**
   * 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
7769 7770
   */   

unknown's avatar
unknown committed
7771
  /**
7772 7773
   * Variables for loop
   */
unknown's avatar
unknown committed
7774 7775
  byte *curr= (byte*)buffer->buffer;
  byte *end_of_buffer= (byte*)buffer->buffer_end;
7776 7777
  NdbOperation::LockMode lm= 
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
unknown's avatar
unknown committed
7778
  bool need_pk = (lm == NdbOperation::LM_Read);
7779 7780 7781
  const NDBTAB *tab= m_table;
  const NDBINDEX *unique_idx= m_index[active_index].unique_index;
  const NDBINDEX *idx= m_index[active_index].index; 
7782 7783
  const NdbOperation* lastOp= m_active_trans->getLastDefinedOperation();
  NdbIndexScanOperation* scanOp= 0;
unknown's avatar
unknown committed
7784 7785
  for (; multi_range_curr<multi_range_end && curr+reclength <= end_of_buffer; 
       multi_range_curr++)
7786
  {
7787 7788 7789 7790 7791 7792
    part_id_range part_spec;
    if (m_use_partition_function)
    {
      get_partition_set(table, curr, active_index,
                        &multi_range_curr->start_key,
                        &part_spec);
7793 7794 7795 7796 7797 7798
      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
      */
7799 7800 7801 7802 7803 7804 7805 7806 7807 7808 7809 7810
      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
7811 7812
    case PRIMARY_KEY_ORDERED_INDEX:
      if (!(multi_range_curr->start_key.length == key_info->key_length &&
7813 7814 7815
          multi_range_curr->start_key.flag == HA_READ_KEY_EXACT))
        goto range;
      // else fall through
7816
    case PRIMARY_KEY_INDEX:
7817
    {
7818
      multi_range_curr->range_flag |= UNIQUE_RANGE;
7819
      if ((op= m_active_trans->getNdbOperation(tab)) && 
7820 7821 7822
          !op->readTuple(lm) && 
          !set_primary_key(op, multi_range_curr->start_key.key) &&
          !define_read_attrs(curr, op) &&
7823 7824 7825
          (op->setAbortOption(AO_IgnoreError), TRUE) &&
          (!m_use_partition_function ||
           (op->setPartitionId(part_spec.start_part), true)))
7826
        curr += reclength;
7827
      else
7828
        ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
7829
      break;
7830 7831
    }
    break;
unknown's avatar
unknown committed
7832 7833
    case UNIQUE_ORDERED_INDEX:
      if (!(multi_range_curr->start_key.length == key_info->key_length &&
7834 7835 7836 7837 7838
          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
7839
    case UNIQUE_INDEX:
7840
    {
7841
      multi_range_curr->range_flag |= UNIQUE_RANGE;
7842
      if ((op= m_active_trans->getNdbIndexOperation(unique_idx, tab)) && 
7843 7844 7845
          !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
7846
          (op->setAbortOption(AO_IgnoreError), TRUE))
7847
        curr += reclength;
7848
      else
7849
        ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
7850 7851
      break;
    }
7852
    case ORDERED_INDEX: {
7853
  range:
7854
      multi_range_curr->range_flag &= ~(uint)UNIQUE_RANGE;
7855 7856
      if (scanOp == 0)
      {
7857 7858 7859 7860 7861 7862
        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
7863
          if (scanOp->reset_bounds(m_force_send))
7864 7865 7866 7867 7868
            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
7869 7870
                 &&!scanOp->readTuples(lm, 0, parallelism, sorted, 
				       FALSE, TRUE, need_pk)
7871 7872 7873 7874 7875 7876 7877 7878 7879 7880 7881
                 &&!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());
        }
7882
      }
7883

7884
      const key_range *keys[2]= { &multi_range_curr->start_key, 
7885
                                  &multi_range_curr->end_key };
7886 7887
      if ((res= set_bounds(scanOp, active_index, false, keys,
                           multi_range_curr-ranges)))
7888
        DBUG_RETURN(res);
7889
      break;
7890
    }
unknown's avatar
unknown committed
7891
    case UNDEFINED_INDEX:
unknown's avatar
unknown committed
7892 7893 7894 7895
      DBUG_ASSERT(FALSE);
      DBUG_RETURN(1);
      break;
    }
7896 7897
  }
  
7898
  if (multi_range_curr != multi_range_end)
7899
  {
7900 7901 7902 7903 7904 7905
    /**
     * 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
     */
7906
    buffer->end_of_used_area= (byte*)buffer->buffer_end;
7907 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917
  }
  else
  {
    buffer->end_of_used_area= curr;
  }
  
  /**
   * Set first operation in multi range
   */
  m_current_multi_operation= 
    lastOp ? lastOp->next() : m_active_trans->getFirstDefinedOperation();
7918
  if (!(res= execute_no_commit_ie(this, m_active_trans,true)))
7919
  {
7920 7921
    m_multi_range_defined= multi_range_curr;
    multi_range_curr= ranges;
7922 7923
    m_multi_range_result_ptr= (byte*)buffer->buffer;
    DBUG_RETURN(read_multi_range_next(found_range_p));
7924 7925 7926 7927
  }
  ERR_RETURN(m_active_trans->getNdbError());
}

unknown's avatar
unknown committed
7928
#if 0
7929
#define DBUG_MULTI_RANGE(x) DBUG_PRINT("info", ("read_multi_range_next: case %d\n", x));
unknown's avatar
unknown committed
7930 7931 7932 7933
#else
#define DBUG_MULTI_RANGE(x)
#endif

7934
int
7935
ha_ndbcluster::read_multi_range_next(KEY_MULTI_RANGE ** multi_range_found_p)
7936 7937
{
  DBUG_ENTER("ha_ndbcluster::read_multi_range_next");
7938
  if (m_disable_multi_read)
7939
  {
7940
    DBUG_MULTI_RANGE(11);
7941
    DBUG_RETURN(handler::read_multi_range_next(multi_range_found_p));
7942
  }
7943
  
7944
  int res;
7945
  int range_no;
unknown's avatar
unknown committed
7946
  ulong reclength= table_share->reclength;
7947
  const NdbOperation* op= m_current_multi_operation;
unknown's avatar
unknown committed
7948
  for (;multi_range_curr < m_multi_range_defined; multi_range_curr++)
7949
  {
7950 7951 7952
    DBUG_MULTI_RANGE(12);
    if (multi_range_curr->range_flag & SKIP_RANGE)
      continue;
7953
    if (multi_range_curr->range_flag & UNIQUE_RANGE)
7954
    {
7955
      if (op->getNdbError().code == 0)
7956 7957
      {
        DBUG_MULTI_RANGE(13);
7958
        goto found_next;
7959
      }
7960 7961 7962
      
      op= m_active_trans->getNextCompletedOperation(op);
      m_multi_range_result_ptr += reclength;
7963
      continue;
7964
    } 
7965
    else if (m_multi_cursor && !multi_range_sorted)
7966
    {
unknown's avatar
unknown committed
7967 7968
      DBUG_MULTI_RANGE(1);
      if ((res= fetch_next(m_multi_cursor)) == 0)
7969
      {
7970 7971 7972
        DBUG_MULTI_RANGE(2);
        range_no= m_multi_cursor->get_range_no();
        goto found;
7973 7974 7975
      } 
      else
      {
7976
        DBUG_MULTI_RANGE(14);
7977
        goto close_scan;
7978 7979
      }
    }
unknown's avatar
unknown committed
7980
    else if (m_multi_cursor && multi_range_sorted)
7981
    {
unknown's avatar
unknown committed
7982 7983
      if (m_active_cursor && (res= fetch_next(m_multi_cursor)))
      {
7984 7985
        DBUG_MULTI_RANGE(3);
        goto close_scan;
unknown's avatar
unknown committed
7986
      }
7987
      
7988
      range_no= m_multi_cursor->get_range_no();
7989
      uint current_range_no= multi_range_curr - m_multi_ranges;
unknown's avatar
unknown committed
7990
      if ((uint) range_no == current_range_no)
7991
      {
7992
        DBUG_MULTI_RANGE(4);
7993
        // return current row
7994
        goto found;
7995
      }
7996
      else if (range_no > (int)current_range_no)
7997
      {
7998 7999 8000 8001
        DBUG_MULTI_RANGE(5);
        // wait with current row
        m_active_cursor= 0;
        continue;
8002 8003 8004
      }
      else 
      {
8005 8006 8007
        DBUG_MULTI_RANGE(6);
        // First fetch from cursor
        DBUG_ASSERT(range_no == -1);
unknown's avatar
unknown committed
8008
        if ((res= m_multi_cursor->nextResult(true)))
8009
        {
8010
          DBUG_MULTI_RANGE(15);
8011 8012 8013 8014
          goto close_scan;
        }
        multi_range_curr--; // Will be increased in for-loop
        continue;
8015
      }
8016
    }
unknown's avatar
unknown committed
8017
    else /** m_multi_cursor == 0 */
8018
    {
unknown's avatar
unknown committed
8019
      DBUG_MULTI_RANGE(7);
8020 8021 8022 8023
      /**
       * Corresponds to range 5 in example in read_multi_range_first
       */
      (void)1;
8024
      continue;
8025
    }
8026
    
unknown's avatar
unknown committed
8027
    DBUG_ASSERT(FALSE); // Should only get here via goto's
8028 8029 8030
close_scan:
    if (res == 1)
    {
unknown's avatar
unknown committed
8031
      m_multi_cursor->close(FALSE, TRUE);
8032
      m_active_cursor= m_multi_cursor= 0;
unknown's avatar
unknown committed
8033
      DBUG_MULTI_RANGE(8);
8034 8035 8036 8037
      continue;
    } 
    else 
    {
8038
      DBUG_MULTI_RANGE(9);
8039 8040 8041
      DBUG_RETURN(ndb_err(m_active_trans));
    }
  }
8042
  
8043
  if (multi_range_curr == multi_range_end)
8044 8045
  {
    DBUG_MULTI_RANGE(16);
8046
    DBUG_RETURN(HA_ERR_END_OF_FILE);
8047
  }
8048
  
8049 8050 8051 8052
  /**
   * Read remaining ranges
   */
  DBUG_RETURN(read_multi_range_first(multi_range_found_p, 
8053 8054 8055 8056
                                     multi_range_curr,
                                     multi_range_end - multi_range_curr, 
                                     multi_range_sorted,
                                     multi_range_buffer));
8057 8058
  
found:
8059 8060 8061
  /**
   * Found a record belonging to a scan
   */
8062
  m_active_cursor= m_multi_cursor;
8063
  * multi_range_found_p= m_multi_ranges + range_no;
8064 8065
  memcpy(table->record[0], m_multi_range_cursor_result_ptr, reclength);
  setup_recattr(m_active_cursor->getFirstRecAttr());
8066 8067 8068
  unpack_record(table->record[0]);
  table->status= 0;     
  DBUG_RETURN(0);
8069
  
8070
found_next:
8071 8072 8073 8074
  /**
   * Found a record belonging to a pk/index op,
   *   copy result and move to next to prepare for next call
   */
8075
  * multi_range_found_p= multi_range_curr;
8076
  memcpy(table->record[0], m_multi_range_result_ptr, reclength);
8077
  setup_recattr(op->getFirstRecAttr());
8078
  unpack_record(table->record[0]);
8079 8080
  table->status= 0;
  
8081
  multi_range_curr++;
8082
  m_current_multi_operation= m_active_trans->getNextCompletedOperation(op);
8083 8084
  m_multi_range_result_ptr += reclength;
  DBUG_RETURN(0);
8085 8086
}

8087 8088 8089 8090 8091 8092 8093 8094
int
ha_ndbcluster::setup_recattr(const NdbRecAttr* curr)
{
  DBUG_ENTER("setup_recattr");

  Field **field, **end;
  NdbValue *value= m_value;
  
unknown's avatar
unknown committed
8095
  end= table->field + table_share->fields;
8096 8097 8098 8099 8100 8101
  
  for (field= table->field; field < end; field++, value++)
  {
    if ((* value).ptr)
    {
      DBUG_ASSERT(curr != 0);
unknown's avatar
unknown committed
8102 8103 8104
      NdbValue* val= m_value + curr->getColumn()->getColumnNo();
      DBUG_ASSERT(val->ptr);
      val->rec= curr;
8105
      curr= curr->next();
8106 8107 8108
    }
  }
  
unknown's avatar
unknown committed
8109
  DBUG_RETURN(0);
8110 8111
}

unknown's avatar
Merge  
unknown committed
8112 8113
char*
ha_ndbcluster::update_table_comment(
8114 8115
                                /* out: table comment + additional */
        const char*     comment)/* in:  table comment defined by user */
unknown's avatar
Merge  
unknown committed
8116 8117
{
  uint length= strlen(comment);
unknown's avatar
unknown committed
8118
  if (length > 64000 - 3)
unknown's avatar
Merge  
unknown committed
8119 8120 8121 8122 8123 8124 8125 8126 8127 8128 8129 8130
  {
    return((char*)comment); /* string too long */
  }

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

  ndb->setDatabaseName(m_dbname);
  NDBDICT* dict= ndb->getDictionary();
8131 8132
  const NDBTAB* tab= m_table;
  DBUG_ASSERT(tab != NULL);
unknown's avatar
Merge  
unknown committed
8133 8134 8135 8136 8137 8138 8139 8140 8141

  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
8142 8143 8144
  my_snprintf(str,fmt_len_plus_extra,fmt,comment,
              length > 0 ? " ":"",
              tab->getReplicaCount());
unknown's avatar
Merge  
unknown committed
8145 8146 8147 8148 8149
  return str;
}


// Utility thread main loop
8150
pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
unknown's avatar
Merge  
unknown committed
8151 8152
{
  THD *thd; /* needs to be first for thread_stack */
8153
  Ndb* ndb;
unknown's avatar
Merge  
unknown committed
8154
  struct timespec abstime;
unknown's avatar
unknown committed
8155
  List<NDB_SHARE> util_open_tables;
unknown's avatar
Merge  
unknown committed
8156 8157 8158 8159 8160 8161 8162

  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);
8163
  ndb= new Ndb(g_ndb_cluster_connection, "");
unknown's avatar
Merge  
unknown committed
8164 8165 8166 8167 8168

  pthread_detach_this_thread();
  ndb_util_thread= pthread_self();

  thd->thread_stack= (char*)&thd; /* remember where our stack is */
unknown's avatar
unknown committed
8169
  if (thd->store_globals() || (ndb->init() != 0))
unknown's avatar
Merge  
unknown committed
8170 8171 8172
  {
    thd->cleanup();
    delete thd;
8173
    delete ndb;
unknown's avatar
Merge  
unknown committed
8174 8175
    DBUG_RETURN(NULL);
  }
unknown's avatar
unknown committed
8176 8177 8178 8179 8180 8181 8182 8183
  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;
8184
  thd->current_stmt_binlog_row_based= TRUE;     // If in mixed mode
unknown's avatar
unknown committed
8185 8186 8187 8188 8189 8190 8191 8192 8193

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

8194 8195
  ndbcluster_util_inited= 1;

unknown's avatar
unknown committed
8196 8197 8198 8199
  /*
    Wait for cluster to start
  */
  pthread_mutex_lock(&LOCK_ndb_util_thread);
8200
  while (!ndb_cluster_node_id && (ndbcluster_hton->slot != ~(uint)0))
unknown's avatar
unknown committed
8201 8202 8203 8204 8205 8206 8207 8208 8209 8210 8211 8212 8213 8214
  {
    /* 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
8215 8216 8217 8218 8219 8220 8221 8222 8223 8224 8225 8226
  {
    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
8227 8228
  if (ndb_extra_logging && ndb_binlog_running)
    sql_print_information("NDB Binlog: Ndb tables initially read only.");
unknown's avatar
unknown committed
8229 8230 8231
  /* create tables needed by the replication */
  ndbcluster_setup_binlog_table_shares(thd);
#else
unknown's avatar
unknown committed
8232 8233 8234 8235
  /*
    Get all table definitions from the storage node
  */
  ndbcluster_find_all_files(thd);
unknown's avatar
unknown committed
8236
#endif
unknown's avatar
unknown committed
8237

8238
  set_timespec(abstime, 0);
unknown's avatar
unknown committed
8239
  for (;!abort_loop;)
unknown's avatar
Merge  
unknown committed
8240 8241
  {
    pthread_mutex_lock(&LOCK_ndb_util_thread);
unknown's avatar
unknown committed
8242 8243 8244
    pthread_cond_timedwait(&COND_ndb_util_thread,
                           &LOCK_ndb_util_thread,
                           &abstime);
unknown's avatar
Merge  
unknown committed
8245
    pthread_mutex_unlock(&LOCK_ndb_util_thread);
unknown's avatar
unknown committed
8246
#ifdef NDB_EXTRA_DEBUG_UTIL_THREAD
unknown's avatar
Merge  
unknown committed
8247 8248
    DBUG_PRINT("ndb_util_thread", ("Started, ndb_cache_check_time: %d",
                                   ndb_cache_check_time));
unknown's avatar
unknown committed
8249
#endif
unknown's avatar
Merge  
unknown committed
8250 8251 8252
    if (abort_loop)
      break; /* Shutting down server */

unknown's avatar
unknown committed
8253 8254 8255 8256 8257
#ifdef HAVE_NDB_BINLOG
    /*
      Check that the apply_status_share and schema_share has been created.
      If not try to create it
    */
8258
    if (!ndb_binlog_tables_inited)
unknown's avatar
unknown committed
8259 8260 8261
      ndbcluster_setup_binlog_table_shares(thd);
#endif

unknown's avatar
Merge  
unknown committed
8262 8263
    if (ndb_cache_check_time == 0)
    {
8264 8265
      /* Wake up in 1 second to check if value has changed */
      set_timespec(abstime, 1);
unknown's avatar
Merge  
unknown committed
8266 8267 8268 8269 8270 8271 8272 8273 8274
      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
8275 8276 8277 8278 8279 8280
#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
8281 8282 8283 8284 8285 8286 8287 8288 8289 8290 8291 8292
      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);
8293
    while ((share= it++))
unknown's avatar
Merge  
unknown committed
8294
    {
unknown's avatar
unknown committed
8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 8305
#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
8306
      DBUG_PRINT("ndb_util_thread",
8307
                 ("Fetching commit count for: %s",
unknown's avatar
unknown committed
8308
                  share->key));
unknown's avatar
Merge  
unknown committed
8309 8310

      /* Contact NDB to get commit count for table */
unknown's avatar
unknown committed
8311
      ndb->setDatabaseName(share->db);
8312 8313 8314 8315 8316 8317 8318
      struct Ndb_statistics stat;

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

unknown's avatar
Merge  
unknown committed
8319
      {
8320 8321 8322 8323
        Ndb_table_guard ndbtab_g(ndb->getDictionary(), share->table_name);
        if (ndbtab_g.get_table() &&
            ndb_get_table_statistics(ndb, ndbtab_g.get_table(), &stat) == 0)
        {
8324
          char buff[22], buff2[22];
8325 8326
          DBUG_PRINT("ndb_util_thread",
                     ("Table: %s, commit_count: %llu, rows: %llu",
8327 8328
                      share->key,
                      llstr(stat.commit_count, buff),
unknown's avatar
unknown committed
8329
                      llstr(stat.row_count, buff2)));
8330 8331 8332 8333 8334 8335 8336 8337
        }
        else
        {
          DBUG_PRINT("ndb_util_thread",
                     ("Error: Could not get commit count for table %s",
                      share->key));
          stat.commit_count= 0;
        }
unknown's avatar
Merge  
unknown committed
8338
      }
8339 8340 8341 8342 8343 8344

      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
8345
      /* Decrease the use count and possibly free share */
unknown's avatar
unknown committed
8346
      free_share(&share);
unknown's avatar
Merge  
unknown committed
8347 8348 8349 8350 8351
    }

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

8352 8353 8354 8355 8356 8357 8358 8359 8360
    /* 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
8361
    if (msecs >= 1000){
8362 8363 8364 8365 8366 8367 8368 8369 8370 8371
      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
8372
  }
unknown's avatar
unknown committed
8373
ndb_util_thread_end:
unknown's avatar
unknown committed
8374
  sql_print_information("Stopping Cluster Utility thread");
unknown's avatar
unknown committed
8375
  net_end(&thd->net);
unknown's avatar
Merge  
unknown committed
8376 8377
  thd->cleanup();
  delete thd;
8378
  delete ndb;
unknown's avatar
Merge  
unknown committed
8379 8380 8381 8382 8383 8384
  DBUG_PRINT("exit", ("ndb_util_thread"));
  my_thread_end();
  pthread_exit(0);
  DBUG_RETURN(NULL);
}

8385 8386 8387
/*
  Condition pushdown
*/
8388 8389 8390 8391 8392 8393 8394 8395 8396 8397 8398 8399 8400 8401 8402 8403 8404
/*
  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)
*/
8405 8406 8407 8408 8409
const 
COND* 
ha_ndbcluster::cond_push(const COND *cond) 
{ 
  DBUG_ENTER("cond_push");
8410 8411 8412
  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
8413
    ndb_cond->next= m_cond_stack;
8414 8415 8416 8417 8418 8419
  else
    ndb_cond->next= NULL;
  m_cond_stack= ndb_cond;
  
  if (serialize_cond(cond, ndb_cond))
  {
unknown's avatar
unknown committed
8420
    DBUG_RETURN(NULL);
8421 8422 8423 8424
  }
  else
  {
    cond_pop();
unknown's avatar
unknown committed
8425
  }
8426 8427 8428
  DBUG_RETURN(cond); 
}

8429 8430 8431
/*
  Pop the top condition from the condition stack of the handler instance.
*/
8432 8433 8434 8435 8436 8437 8438 8439 8440
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
8441
}
8442

8443 8444 8445
/*
  Clear the condition stack
*/
8446 8447 8448 8449 8450 8451 8452 8453 8454 8455
void
ha_ndbcluster::cond_clear()
{
  DBUG_ENTER("cond_clear");
  while (m_cond_stack)
    cond_pop();

  DBUG_VOID_RETURN;
}

8456 8457 8458 8459 8460 8461
/*
  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.
*/
8462 8463 8464 8465 8466
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
8467 8468 8469 8470 8471
  // 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
8472 8473 8474
    switch (item->type()) {
    case Item::FUNC_ITEM:
    {
unknown's avatar
unknown committed
8475 8476 8477 8478
      Item_func *func_item= (Item_func *) item;
      context->skip+= func_item->argument_count();
      break;
    }
unknown's avatar
unknown committed
8479 8480 8481 8482 8483
    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
8484 8485
      break;
    default:
8486
      context->supported= FALSE;
unknown's avatar
unknown committed
8487 8488
      break;
    }
8489
    
unknown's avatar
unknown committed
8490 8491 8492
    DBUG_VOID_RETURN;
  }
  
8493
  if (context->supported)
8494
  {
8495 8496 8497 8498 8499 8500
    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
8501
    {
unknown's avatar
unknown committed
8502 8503
      switch (func_item->functype()) {
      case Item_func::BETWEEN:
8504
        /*
8505 8506 8507 8508 8509 8510 8511
          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()
8512
        */
unknown's avatar
unknown committed
8513 8514
      case Item_func::IN_FUNC:
      {
8515 8516 8517 8518 8519 8520 8521 8522 8523 8524 8525 8526 8527 8528 8529 8530
        /*
          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)
8531
          {
8532 8533 8534
            Item_func *func_item= (Item_func *) item;
            if (func_item->functype() == Item_func::UNKNOWN_FUNC &&
                func_item->const_item())
8535
            {
8536 8537 8538
              // Skip any arguments since we will evaluate function instead
              DBUG_PRINT("info", ("Skip until end of arguments marker"));
              context->skip= func_item->argument_count();
8539 8540 8541
            }
            else
            {
8542 8543 8544 8545
              DBUG_PRINT("info", ("Found unsupported functional expression in BETWEEN|IN"));
              context->supported= FALSE;
              DBUG_VOID_RETURN;
              
8546 8547 8548
            }
          }
        }
8549 8550
        else
        {
8551 8552 8553
          // Non-supported BETWEEN|IN expression
          DBUG_PRINT("info", ("Found unexpected item of type %u in BETWEEN|IN",
                              item->type()));
8554
          context->supported= FALSE;
8555
          DBUG_VOID_RETURN;
8556
        }
8557 8558 8559 8560 8561 8562 8563 8564 8565 8566 8567 8568 8569 8570 8571 8572 8573 8574 8575 8576 8577 8578
        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
8579 8580 8581 8582 8583 8584 8585 8586 8587 8588 8589 8590
        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()
          */
8591 8592 8593 8594 8595 8596 8597 8598 8599 8600 8601 8602 8603 8604 8605 8606 8607 8608 8609
          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;
          }
8610 8611
          break;
        }
unknown's avatar
unknown committed
8612 8613
        case Item_func::IN_FUNC:
        {
8614 8615 8616 8617 8618 8619 8620 8621 8622 8623 8624 8625 8626
          /*
            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);
8627 8628
          break;
        }
8629 8630
        default:
          context->supported= FALSE;
8631
        }
8632 8633 8634 8635 8636 8637 8638 8639 8640 8641 8642 8643 8644 8645 8646 8647 8648 8649 8650 8651 8652 8653 8654 8655 8656 8657 8658 8659 8660 8661
        // 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
8662 8663 8664 8665
      {
        switch (item->type()) {
        case Item::FIELD_ITEM:
        {
8666 8667 8668 8669 8670 8671 8672 8673 8674 8675 8676 8677 8678
          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));
8679
            DBUG_PRINT("info", ("type %d", field->type()));
8680 8681 8682 8683 8684
            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) &&
8685
                context->expecting_field_type(field->type()) &&
8686
                (context->expecting_field_result(field->result_type()) ||
unknown's avatar
unknown committed
8687
                 // Date and year can be written as string or int
8688 8689 8690 8691
                 ((type == MYSQL_TYPE_TIME ||
                   type == MYSQL_TYPE_DATE || 
                   type == MYSQL_TYPE_YEAR ||
                   type == MYSQL_TYPE_DATETIME)
unknown's avatar
unknown committed
8692 8693 8694
                  ? (context->expecting_field_result(STRING_RESULT) ||
                     context->expecting_field_result(INT_RESULT))
                  : true)) &&
8695
                // Bit fields no yet supported in scan filter
8696 8697 8698
                type != MYSQL_TYPE_BIT &&
                // No BLOB support in scan filter
                type != MYSQL_TYPE_TINY_BLOB &&
8699 8700
                type != MYSQL_TYPE_MEDIUM_BLOB &&
                type != MYSQL_TYPE_LONG_BLOB &&
8701
                type != MYSQL_TYPE_BLOB)
8702 8703 8704 8705 8706 8707
            {
              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();
8708
              if (! context->expecting_nothing())
8709
              {
8710 8711 8712 8713 8714 8715 8716 8717 8718 8719
                // 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
8720 8721
                  switch (field->result_type()) {
                  case STRING_RESULT:
8722 8723 8724 8725 8726
                    // 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
8727
                  case REAL_RESULT:
8728 8729
                    context->expect_only(Item::REAL_ITEM);
                    context->expect(Item::DECIMAL_ITEM);
8730
                    context->expect(Item::INT_ITEM);
8731
                    break;
unknown's avatar
unknown committed
8732
                  case INT_RESULT:
8733 8734 8735
                    context->expect_only(Item::INT_ITEM);
                    context->expect(Item::VARBIN_ITEM);
                    break;
unknown's avatar
unknown committed
8736
                  case DECIMAL_RESULT:
8737 8738
                    context->expect_only(Item::DECIMAL_ITEM);
                    context->expect(Item::REAL_ITEM);
8739
                    context->expect(Item::INT_ITEM);
8740 8741 8742 8743
                    break;
                  default:
                    break;
                  }    
8744 8745
              }
              else
8746 8747 8748 8749
              {
                // Expect another logical expression
                context->expect_only(Item::FUNC_ITEM);
                context->expect(Item::COND_ITEM);
8750 8751 8752 8753 8754 8755 8756
                // 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)
8757
                {
unknown's avatar
unknown committed
8758
                  DBUG_PRINT("info", ("Found non-matching collation %s",  
8759 8760
                                      item->collation.collation->name)); 
                  context->supported= FALSE;                
8761 8762
                }
              }
8763 8764
              break;
            }
8765 8766
            else
            {
unknown's avatar
unknown committed
8767 8768
              DBUG_PRINT("info", ("Was not expecting field of type %u(%u)",
                                  field->result_type(), type));
8769
              context->supported= FALSE;
8770
            }
8771
          }
8772
          else
8773
          {
unknown's avatar
unknown committed
8774 8775 8776
            DBUG_PRINT("info", ("Was not expecting field from table %s (%s)",
                                context->table->s->table_name.str, 
                                field->table->s->table_name.str));
8777
            context->supported= FALSE;
8778
          }
8779 8780
          break;
        }
unknown's avatar
unknown committed
8781 8782
        case Item::FUNC_ITEM:
        {
8783 8784 8785 8786 8787 8788
          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
8789
          {
8790 8791 8792
            // Did not expect function here
            context->supported= FALSE;
            break;
8793
          }
8794
          
unknown's avatar
unknown committed
8795 8796 8797
          switch (func_item->functype()) {
          case Item_func::EQ_FUNC:
          {
8798 8799 8800 8801 8802 8803 8804 8805 8806 8807 8808 8809 8810 8811
            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;
8812
          }
unknown's avatar
unknown committed
8813 8814
          case Item_func::NE_FUNC:
          {
8815 8816 8817 8818 8819 8820 8821 8822 8823 8824 8825 8826 8827 8828
            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;
8829
          }
unknown's avatar
unknown committed
8830 8831
          case Item_func::LT_FUNC:
          {
8832 8833 8834 8835 8836 8837 8838 8839 8840 8841 8842 8843 8844 8845 8846
            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
8847 8848
          case Item_func::LE_FUNC:
          {
8849 8850 8851 8852 8853 8854 8855 8856 8857 8858 8859 8860 8861 8862 8863
            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
8864 8865
          case Item_func::GE_FUNC:
          {
8866 8867 8868 8869 8870 8871 8872 8873 8874 8875 8876 8877 8878 8879 8880
            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
8881 8882
          case Item_func::GT_FUNC:
          {
8883 8884 8885 8886 8887 8888 8889 8890 8891 8892 8893 8894 8895 8896 8897
            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
8898 8899
          case Item_func::LIKE_FUNC:
          {
8900 8901 8902 8903 8904
            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);
8905 8906 8907
            context->expect_only_field_type(MYSQL_TYPE_STRING);
            context->expect_field_type(MYSQL_TYPE_VAR_STRING);
            context->expect_field_type(MYSQL_TYPE_VARCHAR);
8908 8909 8910 8911
            context->expect_field_result(STRING_RESULT);
            context->expect(Item::FUNC_ITEM);
            break;
          }
unknown's avatar
unknown committed
8912 8913
          case Item_func::ISNULL_FUNC:
          {
8914 8915 8916 8917 8918 8919 8920 8921 8922 8923
            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
8924 8925
          case Item_func::ISNOTNULL_FUNC:
          {
8926 8927 8928 8929 8930 8931 8932 8933 8934 8935
            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
8936 8937
          case Item_func::NOT_FUNC:
          {
8938 8939 8940 8941
            DBUG_PRINT("info", ("NOT_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);     
            context->expect(Item::FUNC_ITEM);
8942
            context->expect(Item::COND_ITEM);
8943
            break;
8944
          }
unknown's avatar
unknown committed
8945 8946
          case Item_func::BETWEEN:
          {
8947
            DBUG_PRINT("info", ("BETWEEN, rewriting using AND"));
8948
            Item_func_between *between_func= (Item_func_between *) func_item;
8949 8950 8951 8952
            Ndb_rewrite_context *rewrite_context= 
              new Ndb_rewrite_context(func_item);
            rewrite_context->next= context->rewrite_stack;
            context->rewrite_stack= rewrite_context;
8953 8954 8955 8956 8957 8958 8959 8960 8961
            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;
            }
8962
            DBUG_PRINT("info", ("COND_AND_FUNC"));
8963 8964 8965
            curr_cond->ndb_item= 
              new Ndb_item(Item_func::COND_AND_FUNC, 
                           func_item->argument_count() - 1);
8966
            context->expect_only(Item::FIELD_ITEM);
8967 8968 8969 8970 8971
            context->expect(Item::INT_ITEM);
            context->expect(Item::STRING_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FUNC_ITEM);
            break;
8972
          }
unknown's avatar
unknown committed
8973 8974
          case Item_func::IN_FUNC:
          {
8975
            DBUG_PRINT("info", ("IN_FUNC, rewriting using OR"));
8976
            Item_func_in *in_func= (Item_func_in *) func_item;
8977 8978 8979 8980
            Ndb_rewrite_context *rewrite_context= 
              new Ndb_rewrite_context(func_item);
            rewrite_context->next= context->rewrite_stack;
            context->rewrite_stack= rewrite_context;
8981 8982 8983 8984 8985 8986 8987 8988 8989
            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;
            }
8990 8991 8992 8993 8994 8995 8996 8997 8998
            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;
8999
          }
unknown's avatar
unknown committed
9000 9001
          case Item_func::UNKNOWN_FUNC:
          {
9002 9003 9004 9005
            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
9006 9007 9008 9009
            {
              switch (func_item->result_type()) {
              case STRING_RESULT:
              {
9010 9011 9012
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::STRING_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item); 
9013
                if (! context->expecting_no_field_result())
9014 9015 9016 9017 9018 9019 9020 9021 9022 9023 9024 9025 9026 9027 9028 9029 9030 9031 9032 9033 9034 9035 9036 9037
                {
                  // 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
9038 9039
              case REAL_RESULT:
              {
9040 9041 9042
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::REAL_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
9043
                if (! context->expecting_no_field_result()) 
9044 9045 9046 9047 9048 9049 9050 9051 9052 9053 9054 9055 9056 9057 9058 9059 9060
                {
                  // 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
9061 9062
              case INT_RESULT:
              {
9063 9064 9065
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::INT_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
9066
                if (! context->expecting_no_field_result()) 
9067 9068 9069 9070 9071 9072 9073 9074 9075 9076 9077 9078 9079 9080 9081 9082 9083
                {
                  // 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
9084 9085
              case DECIMAL_RESULT:
              {
9086 9087 9088
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::DECIMAL_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
9089
                if (! context->expecting_no_field_result()) 
9090 9091 9092 9093 9094 9095 9096 9097 9098 9099 9100 9101 9102 9103 9104 9105 9106 9107 9108
                {
                  // 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
9109
            }
9110 9111 9112 9113 9114
            else
              // Function does not return constant expression
              context->supported= FALSE;
            break;
          }
unknown's avatar
unknown committed
9115 9116
          default:
          {
9117 9118 9119
            DBUG_PRINT("info", ("Found func_item of type %d", 
                                func_item->functype()));
            context->supported= FALSE;
9120
          }
9121 9122
          }
          break;
9123
        }
unknown's avatar
unknown committed
9124
        case Item::STRING_ITEM:
9125 9126 9127
          DBUG_PRINT("info", ("STRING_ITEM")); 
          if (context->expecting(Item::STRING_ITEM)) 
          {
9128
#ifndef DBUG_OFF
9129 9130 9131 9132 9133 9134
            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()));
9135
#endif
9136 9137 9138
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::STRING_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);      
9139
            if (! context->expecting_no_field_result())
9140 9141 9142 9143 9144 9145 9146 9147 9148 9149 9150 9151 9152 9153 9154 9155 9156 9157 9158 9159 9160 9161 9162
            {
              // 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
9163
        case Item::INT_ITEM:
9164 9165
          DBUG_PRINT("info", ("INT_ITEM"));
          if (context->expecting(Item::INT_ITEM)) 
9166
          {
9167 9168 9169 9170 9171
            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);
9172
            if (! context->expecting_no_field_result()) 
9173 9174 9175 9176
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(INT_RESULT);
9177 9178
              context->expect_field_result(REAL_RESULT);
              context->expect_field_result(DECIMAL_RESULT);
9179 9180 9181 9182 9183 9184 9185
            }
            else
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
            }
9186 9187
          }
          else
9188 9189
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
9190
        case Item::REAL_ITEM:
9191 9192
          DBUG_PRINT("info", ("REAL_ITEM %s"));
          if (context->expecting(Item::REAL_ITEM)) 
9193
          {
9194 9195 9196 9197 9198
            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);
9199
            if (! context->expecting_no_field_result()) 
9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210
            {
              // 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);
            }
9211
          }
9212 9213 9214
          else
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
9215
        case Item::VARBIN_ITEM:
9216 9217
          DBUG_PRINT("info", ("VARBIN_ITEM"));
          if (context->expecting(Item::VARBIN_ITEM)) 
9218
          {
9219 9220 9221
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::VARBIN_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);      
9222
            if (! context->expecting_no_field_result())
9223 9224 9225 9226 9227 9228 9229 9230 9231 9232 9233
            {
              // 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);
            }
9234 9235
          }
          else
9236 9237
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
9238
        case Item::DECIMAL_ITEM:
9239 9240
          DBUG_PRINT("info", ("DECIMAL_ITEM %s"));
          if (context->expecting(Item::DECIMAL_ITEM)) 
9241
          {
9242 9243 9244 9245 9246
            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);
9247
            if (! context->expecting_no_field_result()) 
9248 9249 9250 9251 9252 9253 9254 9255 9256 9257 9258 9259
            {
              // 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);
            }
9260
          }
9261 9262 9263
          else
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
9264 9265
        case Item::COND_ITEM:
        {
9266 9267 9268
          Item_cond *cond_item= (Item_cond *) item;
          
          if (context->expecting(Item::COND_ITEM))
unknown's avatar
unknown committed
9269 9270 9271
          {
            switch (cond_item->functype()) {
            case Item_func::COND_AND_FUNC:
9272 9273 9274 9275
              DBUG_PRINT("info", ("COND_AND_FUNC"));
              curr_cond->ndb_item= new Ndb_item(cond_item->functype(),
                                                cond_item);      
              break;
unknown's avatar
unknown committed
9276
            case Item_func::COND_OR_FUNC:
9277 9278 9279 9280 9281 9282 9283 9284 9285
              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
9286
          }
9287
          else
unknown's avatar
unknown committed
9288 9289
          {
            /* Did not expect condition */
9290
            context->supported= FALSE;          
unknown's avatar
unknown committed
9291
          }
9292
          break;
9293
        }
unknown's avatar
unknown committed
9294 9295
        default:
        {
9296
          DBUG_PRINT("info", ("Found item of type %d", item->type()));
9297
          context->supported= FALSE;
9298 9299
        }
        }
unknown's avatar
unknown committed
9300
      }
9301 9302 9303 9304 9305 9306 9307 9308 9309 9310
      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();
9311
          curr_cond->prev= prev_cond;
9312 9313 9314
          prev_cond->next= curr_cond;
          curr_cond->ndb_item= new Ndb_item(NDB_END_COND);
          // Pop rewrite stack
unknown's avatar
unknown committed
9315 9316 9317
          context->rewrite_stack=  rewrite_context->next;
          rewrite_context->next= NULL;
          delete(rewrite_context);
9318
        }
9319
      }
9320
    }
9321
  }
9322
 
9323 9324 9325 9326 9327 9328 9329 9330
  DBUG_VOID_RETURN;
}

bool
ha_ndbcluster::serialize_cond(const COND *cond, Ndb_cond_stack *ndb_cond)
{
  DBUG_ENTER("serialize_cond");
  Item *item= (Item *) cond;
9331
  Ndb_cond_traverse_context context(table, (void *)m_table, ndb_cond);
9332 9333 9334
  // Expect a logical expression
  context.expect(Item::FUNC_ITEM);
  context.expect(Item::COND_ITEM);
9335
  item->traverse_cond(&ndb_serialize_cond, (void *) &context, Item::PREFIX);
9336
  DBUG_PRINT("info", ("The pushed condition is %ssupported", (context.supported)?"":"not "));
9337

9338
  DBUG_RETURN(context.supported);
9339 9340
}

9341 9342
int
ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, 
9343 9344
                                           NdbScanFilter *filter,
                                           bool negated)
9345 9346
{
  DBUG_ENTER("build_scan_filter_predicate");  
unknown's avatar
unknown committed
9347 9348 9349
  switch (cond->ndb_item->type) {
  case NDB_FUNCTION:
  {
9350 9351 9352
    if (!cond->next)
      break;
    Ndb_item *a= cond->next->ndb_item;
9353
    Ndb_item *b, *field, *value= NULL;
9354 9355
    LINT_INIT(field);

unknown's avatar
unknown committed
9356 9357
    switch (cond->ndb_item->argument_count()) {
    case 1:
9358 9359 9360
      field= 
        (a->type == NDB_FIELD)? a : NULL;
      break;
unknown's avatar
unknown committed
9361
    case 2:
9362
      if (!cond->next->next)
9363
        break;
9364 9365
      b= cond->next->next->ndb_item;
      value= 
9366 9367 9368
        (a->type == NDB_VALUE)? a
        : (b->type == NDB_VALUE)? b
        : NULL;
9369
      field= 
9370 9371 9372
        (a->type == NDB_FIELD)? a
        : (b->type == NDB_FIELD)? b
        : NULL;
9373
      break;
9374
    default:
9375 9376
      field= NULL; //Keep compiler happy
      DBUG_ASSERT(0);
9377 9378
      break;
    }
unknown's avatar
unknown committed
9379 9380 9381
    switch ((negated) ? 
            Ndb_item::negate(cond->ndb_item->qualification.function_type)
            : cond->ndb_item->qualification.function_type) {
9382
    case NDB_EQ_FUNC:
9383
    {
9384
      if (!value || !field) break;
unknown's avatar
unknown committed
9385 9386
      // Save value in right format for the field type
      value->save_in_field(field);
9387
      DBUG_PRINT("info", ("Generating EQ filter"));
9388
      if (filter->cmp(NdbScanFilter::COND_EQ, 
9389 9390 9391 9392
                      field->get_field_no(),
                      field->get_val(),
                      field->pack_length()) == -1)
        DBUG_RETURN(1);
9393 9394
      cond= cond->next->next->next;
      DBUG_RETURN(0);
9395
    }
9396
    case NDB_NE_FUNC:
unknown's avatar
unknown committed
9397
    {
9398
      if (!value || !field) break;
unknown's avatar
unknown committed
9399 9400
      // Save value in right format for the field type
      value->save_in_field(field);
9401
      DBUG_PRINT("info", ("Generating NE filter"));
9402
      if (filter->cmp(NdbScanFilter::COND_NE, 
9403 9404 9405 9406
                      field->get_field_no(),
                      field->get_val(),
                      field->pack_length()) == -1)
        DBUG_RETURN(1);
9407 9408
      cond= cond->next->next->next;
      DBUG_RETURN(0);
9409
    }
9410
    case NDB_LT_FUNC:
unknown's avatar
unknown committed
9411
    {
9412
      if (!value || !field) break;
unknown's avatar
unknown committed
9413 9414
      // Save value in right format for the field type
      value->save_in_field(field);
9415
      if (a == field)
9416
      {
9417 9418 9419 9420 9421 9422
        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);
9423
      }
9424
      else
9425
      {
9426 9427 9428 9429 9430 9431
        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);
9432
      }
9433 9434
      cond= cond->next->next->next;
      DBUG_RETURN(0);
9435
    }
9436
    case NDB_LE_FUNC:
unknown's avatar
unknown committed
9437
    {
9438
      if (!value || !field) break;
unknown's avatar
unknown committed
9439 9440
      // Save value in right format for the field type
      value->save_in_field(field);
9441
      if (a == field)
9442
      {
9443 9444 9445 9446 9447 9448
        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);       
9449
      }
9450
      else
9451
      {
9452 9453 9454 9455 9456 9457
        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);
9458
      }
9459 9460
      cond= cond->next->next->next;
      DBUG_RETURN(0);
9461
    }
9462
    case NDB_GE_FUNC:
unknown's avatar
unknown committed
9463
    {
9464
      if (!value || !field) break;
unknown's avatar
unknown committed
9465 9466
      // Save value in right format for the field type
      value->save_in_field(field);
9467
      if (a == field)
9468
      {
9469 9470 9471 9472 9473 9474
        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);
9475
      }
9476
      else
9477
      {
9478 9479 9480 9481 9482 9483
        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);
9484
      }
9485 9486
      cond= cond->next->next->next;
      DBUG_RETURN(0);
9487
    }
9488
    case NDB_GT_FUNC:
unknown's avatar
unknown committed
9489
    {
9490
      if (!value || !field) break;
unknown's avatar
unknown committed
9491 9492
      // Save value in right format for the field type
      value->save_in_field(field);
9493
      if (a == field)
9494
      {
9495 9496 9497 9498 9499 9500
        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);
9501
      }
9502
      else
9503
      {
9504 9505 9506 9507 9508 9509
        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);
9510
      }
9511 9512
      cond= cond->next->next->next;
      DBUG_RETURN(0);
9513
    }
9514
    case NDB_LIKE_FUNC:
unknown's avatar
unknown committed
9515
    {
9516
      if (!value || !field) break;
9517 9518 9519
      if ((value->qualification.value_type != Item::STRING_ITEM) &&
          (value->qualification.value_type != Item::VARBIN_ITEM))
          break;
unknown's avatar
unknown committed
9520 9521 9522
      // Save value in right format for the field type
      value->save_in_field(field);
      DBUG_PRINT("info", ("Generating LIKE filter: like(%d,%s,%d)", 
9523 9524 9525 9526
                          field->get_field_no(), value->get_val(), 
                          value->pack_length()));
      if (filter->cmp(NdbScanFilter::COND_LIKE, 
                      field->get_field_no(),
9527 9528
                      value->get_val(),
                      value->pack_length()) == -1)
9529
        DBUG_RETURN(1);
9530 9531
      cond= cond->next->next->next;
      DBUG_RETURN(0);
9532
    }
9533
    case NDB_NOTLIKE_FUNC:
unknown's avatar
unknown committed
9534
    {
9535
      if (!value || !field) break;
9536 9537
      if ((value->qualification.value_type != Item::STRING_ITEM) &&
          (value->qualification.value_type != Item::VARBIN_ITEM))
9538
          break;
unknown's avatar
unknown committed
9539 9540 9541
      // Save value in right format for the field type
      value->save_in_field(field);
      DBUG_PRINT("info", ("Generating NOTLIKE filter: notlike(%d,%s,%d)", 
9542 9543 9544 9545
                          field->get_field_no(), value->get_val(), 
                          value->pack_length()));
      if (filter->cmp(NdbScanFilter::COND_NOT_LIKE, 
                      field->get_field_no(),
9546 9547
                      value->get_val(),
                      value->pack_length()) == -1)
9548
        DBUG_RETURN(1);
9549 9550
      cond= cond->next->next->next;
      DBUG_RETURN(0);
9551
    }
9552
    case NDB_ISNULL_FUNC:
9553 9554 9555 9556 9557
      if (!field)
        break;
      DBUG_PRINT("info", ("Generating ISNULL filter"));
      if (filter->isnull(field->get_field_no()) == -1)
        DBUG_RETURN(1);
9558 9559
      cond= cond->next->next;
      DBUG_RETURN(0);
9560
    case NDB_ISNOTNULL_FUNC:
unknown's avatar
unknown committed
9561
    {
9562 9563 9564 9565 9566
      if (!field)
        break;
      DBUG_PRINT("info", ("Generating ISNOTNULL filter"));
      if (filter->isnotnull(field->get_field_no()) == -1)
        DBUG_RETURN(1);         
9567 9568
      cond= cond->next->next;
      DBUG_RETURN(0);
9569 9570 9571 9572 9573 9574 9575 9576 9577 9578
    }
    default:
      break;
    }
    break;
  }
  default:
    break;
  }
  DBUG_PRINT("info", ("Found illegal condition"));
9579
  DBUG_RETURN(1);
9580 9581
}

9582
int
9583
ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter)
9584
{
9585
  uint level=0;
unknown's avatar
unknown committed
9586
  bool negated= FALSE;
9587
  DBUG_ENTER("build_scan_filter_group");
unknown's avatar
unknown committed
9588

9589 9590
  do
  {
unknown's avatar
unknown committed
9591 9592 9593 9594 9595 9596
    if (!cond)
      DBUG_RETURN(1);
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
    {
      switch (cond->ndb_item->qualification.function_type) {
9597
      case NDB_COND_AND_FUNC:
unknown's avatar
unknown committed
9598
      {
9599 9600 9601 9602 9603
        level++;
        DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NAND":"AND",
                            level));
        if ((negated) ? filter->begin(NdbScanFilter::NAND)
            : filter->begin(NdbScanFilter::AND) == -1)
9604
          DBUG_RETURN(1);
unknown's avatar
unknown committed
9605
        negated= FALSE;
9606 9607 9608
        cond= cond->next;
        break;
      }
9609
      case NDB_COND_OR_FUNC:
unknown's avatar
unknown committed
9610
      {
9611 9612 9613 9614 9615 9616
        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
9617
        negated= FALSE;
9618 9619 9620
        cond= cond->next;
        break;
      }
9621
      case NDB_NOT_FUNC:
unknown's avatar
unknown committed
9622
      {
9623
        DBUG_PRINT("info", ("Generating negated query"));
9624
        cond= cond->next;
unknown's avatar
unknown committed
9625
        negated= TRUE;
9626 9627 9628 9629
        break;
      }
      default:
        if (build_scan_filter_predicate(cond, filter, negated))
9630
          DBUG_RETURN(1);
unknown's avatar
unknown committed
9631
        negated= FALSE;
9632 9633 9634
        break;
      }
      break;
unknown's avatar
unknown committed
9635 9636
    }
    case NDB_END_COND:
9637 9638
      DBUG_PRINT("info", ("End of group %u", level));
      level--;
9639 9640
      if (cond) cond= cond->next;
      if (filter->end() == -1)
9641
        DBUG_RETURN(1);
9642 9643 9644
      if (!negated)
        break;
      // else fall through (NOT END is an illegal condition)
unknown's avatar
unknown committed
9645 9646
    default:
    {
9647
      DBUG_PRINT("info", ("Illegal scan filter"));
9648
    }
9649
    }
9650
  }  while (level > 0 || negated);
9651
  
9652
  DBUG_RETURN(0);
9653 9654
}

9655 9656
int
ha_ndbcluster::build_scan_filter(Ndb_cond * &cond, NdbScanFilter *filter)
9657 9658 9659 9660
{
  bool simple_cond= TRUE;
  DBUG_ENTER("build_scan_filter");  

unknown's avatar
unknown committed
9661 9662 9663
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
      switch (cond->ndb_item->qualification.function_type) {
9664 9665
      case NDB_COND_AND_FUNC:
      case NDB_COND_OR_FUNC:
9666 9667 9668 9669 9670 9671 9672 9673 9674
        simple_cond= FALSE;
        break;
      default:
        break;
      }
      break;
    default:
      break;
    }
9675 9676 9677 9678 9679 9680
  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);
9681

9682
  DBUG_RETURN(0);
9683 9684
}

9685
int
9686
ha_ndbcluster::generate_scan_filter(Ndb_cond_stack *ndb_cond_stack,
9687
                                    NdbScanOperation *op)
9688 9689 9690 9691
{
  DBUG_ENTER("generate_scan_filter");
  if (ndb_cond_stack)
  {
9692
    DBUG_PRINT("info", ("Generating scan filter"));
9693 9694 9695 9696 9697
    NdbScanFilter filter(op);
    bool multiple_cond= FALSE;
    // Wrap an AND group around multiple conditions
    if (ndb_cond_stack->next) {
      multiple_cond= TRUE;
9698
      if (filter.begin() == -1)
9699
        DBUG_RETURN(1); 
9700 9701
    }
    for (Ndb_cond_stack *stack= ndb_cond_stack; 
9702 9703
         (stack); 
         stack= stack->next)
9704
      {
9705
        Ndb_cond *cond= stack->ndb_cond;
9706

9707 9708 9709 9710 9711
        if (build_scan_filter(cond, &filter))
        {
          DBUG_PRINT("info", ("build_scan_filter failed"));
          DBUG_RETURN(1);
        }
9712
      }
9713 9714
    if (multiple_cond && filter.end() == -1)
      DBUG_RETURN(1);
9715 9716 9717 9718 9719 9720
  }
  else
  {  
    DBUG_PRINT("info", ("Empty stack"));
  }

9721
  DBUG_RETURN(0);
9722 9723
}

9724 9725 9726
/*
  get table space info for SHOW CREATE TABLE
*/
9727
char* ha_ndbcluster::get_tablespace_name(THD *thd)
9728
{
9729
  Ndb *ndb= check_ndb_in_thd(thd);
9730
  NDBDICT *ndbdict= ndb->getDictionary();
9731 9732
  NdbError ndberr;
  Uint32 id;
9733
  ndb->setDatabaseName(m_dbname);
9734 9735
  const NDBTAB *ndbtab= m_table;
  DBUG_ASSERT(ndbtab != NULL);
9736 9737
  if (!ndbtab->getTablespace(&id))
  {
9738
    return 0;
9739 9740 9741 9742
  }
  {
    NdbDictionary::Tablespace ts= ndbdict->getTablespace(id);
    ndberr= ndbdict->getNdbError();
9743
    if(ndberr.classification != NdbError::NoError)
9744 9745 9746 9747 9748
      goto err;
    return (my_strdup(ts.getName(), MYF(0)));
  }
err:
  if (ndberr.status == NdbError::TemporaryError)
9749
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
9750 9751 9752
			ER_GET_TEMPORARY_ERRMSG, ER(ER_GET_TEMPORARY_ERRMSG),
			ndberr.code, ndberr.message, "NDB");
  else
9753
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
9754 9755
			ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
			ndberr.code, ndberr.message, "NDB");
9756 9757 9758
  return 0;
}

unknown's avatar
unknown committed
9759 9760 9761
/*
  Implements the SHOW NDB STATUS command.
*/
9762
bool
9763
ndbcluster_show_status(handlerton *hton, THD* thd, stat_print_fn *stat_print,
9764
                       enum ha_stat_type stat_type)
9765
{
9766
  char buf[IO_SIZE];
unknown's avatar
unknown committed
9767
  uint buflen;
9768 9769 9770 9771
  DBUG_ENTER("ndbcluster_show_status");
  
  if (have_ndbcluster != SHOW_OPTION_YES) 
  {
9772 9773 9774 9775 9776
    DBUG_RETURN(FALSE);
  }
  if (stat_type != HA_ENGINE_STATUS)
  {
    DBUG_RETURN(FALSE);
9777
  }
unknown's avatar
unknown committed
9778 9779 9780 9781 9782 9783 9784

  update_status_variables(g_ndb_cluster_connection);
  buflen=
    my_snprintf(buf, sizeof(buf),
                "cluster_node_id=%u, "
                "connected_host=%s, "
                "connected_port=%u, "
unknown's avatar
unknown committed
9785 9786
                "number_of_data_nodes=%u, "
                "number_of_ready_data_nodes=%u, "
9787
                "connect_count=%u",
unknown's avatar
unknown committed
9788 9789 9790
                ndb_cluster_node_id,
                ndb_connected_host,
                ndb_connected_port,
unknown's avatar
unknown committed
9791 9792
                ndb_number_of_data_nodes,
                ndb_number_of_ready_data_nodes,
9793
                ndb_connect_count);
unknown's avatar
unknown committed
9794 9795
  if (stat_print(thd, ndbcluster_hton_name, ndbcluster_hton_name_length,
                 STRING_WITH_LEN("connection"), buf, buflen))
unknown's avatar
unknown committed
9796 9797
    DBUG_RETURN(TRUE);

unknown's avatar
unknown committed
9798
  if (get_thd_ndb(thd) && get_thd_ndb(thd)->ndb)
9799
  {
unknown's avatar
unknown committed
9800
    Ndb* ndb= (get_thd_ndb(thd))->ndb;
unknown's avatar
unknown committed
9801 9802
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
9803 9804
    while (ndb->get_free_list_usage(&tmp))
    {
unknown's avatar
unknown committed
9805
      buflen=
unknown's avatar
unknown committed
9806
        my_snprintf(buf, sizeof(buf),
9807 9808
                  "created=%u, free=%u, sizeof=%u",
                  tmp.m_created, tmp.m_free, tmp.m_sizeof);
unknown's avatar
unknown committed
9809
      if (stat_print(thd, ndbcluster_hton_name, ndbcluster_hton_name_length,
unknown's avatar
unknown committed
9810
                     tmp.m_name, strlen(tmp.m_name), buf, buflen))
9811
        DBUG_RETURN(TRUE);
9812 9813
    }
  }
unknown's avatar
unknown committed
9814 9815 9816 9817
#ifdef HAVE_NDB_BINLOG
  ndbcluster_show_status_binlog(thd, stat_print, stat_type);
#endif

9818 9819
  DBUG_RETURN(FALSE);
}
9820

unknown's avatar
unknown committed
9821

9822 9823 9824
/*
  Create a table in NDB Cluster
 */
9825
static uint get_no_fragments(ulonglong max_rows)
9826 9827 9828 9829 9830 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
{
#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);
}

9863
int ha_ndbcluster::get_default_no_partitions(HA_CREATE_INFO *info)
9864
{
9865 9866 9867 9868 9869 9870 9871 9872 9873 9874 9875
  ha_rows max_rows, min_rows;
  if (info)
  {
    max_rows= info->max_rows;
    min_rows= info->min_rows;
  }
  else
  {
    max_rows= table_share->max_rows;
    min_rows= table_share->min_rows;
  }
9876
  uint reported_frags;
9877 9878
  uint no_fragments=
    get_no_fragments(max_rows >= min_rows ? max_rows : min_rows);
9879
  uint no_nodes= g_ndb_cluster_connection->no_db_nodes();
unknown's avatar
unknown committed
9880 9881 9882 9883 9884 9885
  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");
  }
9886 9887 9888 9889
  return (int)reported_frags;
}


unknown's avatar
unknown committed
9890 9891 9892 9893 9894 9895 9896 9897 9898 9899 9900 9901 9902 9903 9904 9905 9906 9907 9908 9909 9910 9911 9912 9913 9914 9915 9916 9917 9918 9919 9920 9921 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931
/*
  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;
unknown's avatar
unknown committed
9932
  bool unsigned_flag= part_info->part_expr->unsigned_flag;
unknown's avatar
unknown committed
9933 9934 9935 9936 9937 9938 9939 9940 9941 9942
  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];
unknown's avatar
unknown committed
9943 9944
    if (unsigned_flag)
      range_val-= 0x8000000000000000ULL;
9945
    if (range_val < INT_MIN32 || range_val >= INT_MAX32)
unknown's avatar
unknown committed
9946
    {
9947 9948 9949 9950 9951 9952 9953 9954
      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
9955 9956 9957 9958 9959 9960 9961 9962 9963 9964 9965 9966 9967 9968 9969 9970
    }
    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;
unknown's avatar
unknown committed
9971
  bool unsigned_flag= part_info->part_expr->unsigned_flag;
unknown's avatar
unknown committed
9972 9973 9974 9975 9976 9977 9978 9979 9980 9981 9982
  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;
unknown's avatar
unknown committed
9983 9984
    if (unsigned_flag)
      list_val-= 0x8000000000000000ULL;
unknown's avatar
unknown committed
9985 9986 9987 9988 9989 9990 9991 9992 9993 9994 9995 9996 9997 9998 9999 10000
    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);
}

10001 10002 10003 10004 10005 10006 10007 10008 10009 10010 10011 10012 10013 10014 10015 10016 10017
/*
  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
10018 10019 10020
  uint16 frag_data[MAX_PARTITIONS];
  char *ts_names[MAX_PARTITIONS];
  ulong ts_index= 0, fd_index= 0, i, j;
10021 10022 10023
  NDBTAB *tab= (NDBTAB*)tab_par;
  NDBTAB::FragmentType ftype= NDBTAB::UserDefined;
  partition_element *part_elem;
unknown's avatar
unknown committed
10024 10025 10026 10027 10028 10029
  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");
10030 10031 10032 10033 10034 10035 10036 10037 10038 10039 10040 10041 10042

  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++)
    {
10043
      NDBCOL *col= tab->getColumn(fields[i]->field_index);
10044 10045 10046 10047
      DBUG_PRINT("info",("setting dist key on %s", col->getName()));
      col->setPartitionKey(TRUE);
    }
  }
10048
  else 
10049
  {
10050 10051 10052 10053 10054 10055 10056 10057
    if (!current_thd->variables.new_mode)
    {
      push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
                          ER_ILLEGAL_HA_CREATE_OPTION,
                          ER(ER_ILLEGAL_HA_CREATE_OPTION),
                          ndbcluster_hton_name,
                          "LIST, RANGE and HASH partition disabled by default,"
                          " use --new option to enable");
10058
      DBUG_RETURN(HA_ERR_UNSUPPORTED);
10059 10060
    }
   /*
10061 10062 10063 10064 10065 10066 10067 10068 10069 10070 10071 10072 10073 10074 10075 10076
      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)
10077
    {
10078 10079 10080 10081
      if ((error= set_range_data((void*)tab, part_info)))
      {
        DBUG_RETURN(error);
      }
10082
    }
10083
    else if (part_info->part_type == LIST_PARTITION)
10084
    {
10085 10086 10087 10088
      if ((error= set_list_data((void*)tab, part_info)))
      {
        DBUG_RETURN(error);
      }
10089 10090 10091
    }
  }
  tab->setFragmentType(ftype);
unknown's avatar
unknown committed
10092 10093 10094
  i= 0;
  tot_ts_name_len= 0;
  do
10095
  {
unknown's avatar
unknown committed
10096 10097
    uint ng;
    part_elem= part_it++;
10098
    if (!part_info->is_sub_partitioned())
10099
    {
unknown's avatar
unknown committed
10100 10101 10102 10103 10104
      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;
10105
    }
unknown's avatar
unknown committed
10106 10107 10108 10109 10110 10111 10112 10113 10114 10115 10116 10117 10118 10119 10120 10121 10122
    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);
10123
  tab->setLinearFlag(part_info->linear_hash_ind);
10124
  {
10125 10126
    ha_rows max_rows= table_share->max_rows;
    ha_rows min_rows= table_share->min_rows;
10127 10128 10129 10130 10131
    if (max_rows < min_rows)
      max_rows= min_rows;
    if (max_rows != (ha_rows)0) /* default setting, don't set fragmentation */
    {
      tab->setMaxRows(max_rows);
10132
      tab->setMinRows(min_rows);
10133 10134
    }
  }
unknown's avatar
unknown committed
10135 10136 10137 10138
  tab->setTablespaceNames(ts_names, fd_index*sizeof(char*));
  tab->setFragmentCount(fd_index);
  tab->setFragmentData(&frag_data, fd_index*2);
  DBUG_RETURN(0);
10139
}
10140

unknown's avatar
unknown committed
10141

unknown's avatar
unknown committed
10142 10143 10144
bool ha_ndbcluster::check_if_incompatible_data(HA_CREATE_INFO *info,
					       uint table_changes)
{
10145 10146 10147
  DBUG_ENTER("ha_ndbcluster::check_if_incompatible_data");
  uint i;
  const NDBTAB *tab= (const NDBTAB *) m_table;
unknown's avatar
unknown committed
10148

10149 10150 10151 10152 10153 10154
  if (current_thd->variables.ndb_use_copying_alter_table)
  {
    DBUG_PRINT("info", ("On-line alter table disabled"));
    DBUG_RETURN(COMPATIBLE_DATA_NO);
  }

10155 10156 10157
  for (i= 0; i < table->s->fields; i++) 
  {
    Field *field= table->field[i];
10158 10159
    const NDBCOL *col= tab->getColumn(i);
    if (field->flags & FIELD_IS_RENAMED)
10160 10161 10162 10163
    {
      DBUG_PRINT("info", ("Field has been renamed, copy table"));
      DBUG_RETURN(COMPATIBLE_DATA_NO);
    }
10164
    if ((field->flags & FIELD_IN_ADD_INDEX) &&
10165 10166 10167 10168 10169 10170
        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
10171
  if (table_changes != IS_EQUAL_YES)
10172
    DBUG_RETURN(COMPATIBLE_DATA_NO);
unknown's avatar
unknown committed
10173 10174 10175 10176
  
  /* Check that auto_increment value was not changed */
  if ((info->used_fields & HA_CREATE_USED_AUTO) &&
      info->auto_increment_value != 0)
10177
    DBUG_RETURN(COMPATIBLE_DATA_NO);
unknown's avatar
unknown committed
10178 10179 10180 10181
  
  /* Check that row format didn't change */
  if ((info->used_fields & HA_CREATE_USED_AUTO) &&
      get_row_type() != info->row_type)
10182
    DBUG_RETURN(COMPATIBLE_DATA_NO);
unknown's avatar
unknown committed
10183

10184
  DBUG_RETURN(COMPATIBLE_DATA_YES);
unknown's avatar
unknown committed
10185 10186
}

unknown's avatar
unknown committed
10187 10188 10189 10190 10191 10192 10193 10194 10195 10196 10197 10198 10199 10200 10201 10202 10203 10204 10205 10206 10207 10208 10209 10210 10211 10212 10213 10214 10215 10216 10217 10218 10219 10220 10221 10222 10223 10224 10225 10226
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;
}

10227
int ndbcluster_alter_tablespace(handlerton *hton, THD* thd, st_alter_tablespace *info)
unknown's avatar
unknown committed
10228 10229
{
  DBUG_ENTER("ha_ndbcluster::alter_tablespace");
unknown's avatar
unknown committed
10230

10231
  int is_tablespace= 0;
unknown's avatar
unknown committed
10232 10233
  Ndb *ndb= check_ndb_in_thd(thd);
  if (ndb == NULL)
unknown's avatar
unknown committed
10234
  {
unknown's avatar
unknown committed
10235
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
unknown's avatar
unknown committed
10236
  }
unknown's avatar
unknown committed
10237 10238

  NdbError err;
unknown's avatar
unknown committed
10239
  NDBDICT *dict= ndb->getDictionary();
unknown's avatar
unknown committed
10240 10241
  int error;
  const char * errmsg;
10242
  LINT_INIT(errmsg);
unknown's avatar
unknown committed
10243

unknown's avatar
unknown committed
10244 10245 10246
  switch (info->ts_cmd_type){
  case (CREATE_TABLESPACE):
  {
10247
    error= ER_CREATE_FILEGROUP_FAILED;
unknown's avatar
unknown committed
10248
    
unknown's avatar
unknown committed
10249 10250
    NdbDictionary::Tablespace ndb_ts;
    NdbDictionary::Datafile ndb_df;
unknown's avatar
unknown committed
10251
    NdbDictionary::ObjectId objid;
unknown's avatar
unknown committed
10252 10253 10254 10255 10256 10257 10258 10259
    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
10260
    errmsg= "TABLESPACE";
unknown's avatar
unknown committed
10261
    if (dict->createTablespace(ndb_ts, &objid))
unknown's avatar
unknown committed
10262 10263
    {
      DBUG_PRINT("error", ("createTablespace returned %d", error));
unknown's avatar
unknown committed
10264
      goto ndberror;
unknown's avatar
unknown committed
10265 10266
    }
    DBUG_PRINT("info", ("Successfully created Tablespace"));
unknown's avatar
unknown committed
10267 10268
    errmsg= "DATAFILE";
    if (dict->createDatafile(ndb_df))
unknown's avatar
unknown committed
10269
    {
unknown's avatar
unknown committed
10270 10271 10272 10273 10274 10275 10276 10277 10278
      err= dict->getNdbError();
      NdbDictionary::Tablespace tmp= dict->getTablespace(ndb_ts.getName());
      if (dict->getNdbError().code == 0 &&
	  tmp.getObjectId() == objid.getObjectId() &&
	  tmp.getObjectVersion() == objid.getObjectVersion())
      {
	dict->dropTablespace(tmp);
      }
      
unknown's avatar
unknown committed
10279
      DBUG_PRINT("error", ("createDatafile returned %d", error));
unknown's avatar
unknown committed
10280
      goto ndberror2;
unknown's avatar
unknown committed
10281
    }
10282
    is_tablespace= 1;
unknown's avatar
unknown committed
10283 10284 10285 10286
    break;
  }
  case (ALTER_TABLESPACE):
  {
10287
    error= ER_ALTER_FILEGROUP_FAILED;
unknown's avatar
unknown committed
10288 10289 10290 10291 10292 10293 10294
    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
10295 10296
      errmsg= " CREATE DATAFILE";
      if (dict->createDatafile(ndb_df))
unknown's avatar
unknown committed
10297
      {
unknown's avatar
unknown committed
10298
	goto ndberror;
unknown's avatar
unknown committed
10299 10300 10301 10302
      }
    }
    else if(info->ts_alter_tablespace_type == ALTER_TABLESPACE_DROP_FILE)
    {
unknown's avatar
unknown committed
10303 10304 10305 10306 10307 10308
      NdbDictionary::Tablespace ts= dict->getTablespace(info->tablespace_name);
      NdbDictionary::Datafile df= dict->getDatafile(0, info->data_file_name);
      NdbDictionary::ObjectId objid;
      df.getTablespaceId(&objid);
      if (ts.getObjectId() == objid.getObjectId() && 
	  strcmp(df.getPath(), info->data_file_name) == 0)
unknown's avatar
unknown committed
10309
      {
unknown's avatar
unknown committed
10310 10311
	errmsg= " DROP DATAFILE";
	if (dict->dropDatafile(df))
unknown's avatar
unknown committed
10312
	{
unknown's avatar
unknown committed
10313
	  goto ndberror;
unknown's avatar
unknown committed
10314 10315 10316 10317 10318
	}
      }
      else
      {
	DBUG_PRINT("error", ("No such datafile"));
10319
	my_error(ER_ALTER_FILEGROUP_FAILED, MYF(0), " NO SUCH FILE");
unknown's avatar
unknown committed
10320 10321 10322 10323 10324 10325 10326 10327 10328
	DBUG_RETURN(1);
      }
    }
    else
    {
      DBUG_PRINT("error", ("Unsupported alter tablespace: %d", 
			   info->ts_alter_tablespace_type));
      DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
    }
10329
    is_tablespace= 1;
unknown's avatar
unknown committed
10330 10331 10332 10333
    break;
  }
  case (CREATE_LOGFILE_GROUP):
  {
10334
    error= ER_CREATE_FILEGROUP_FAILED;
unknown's avatar
unknown committed
10335 10336
    NdbDictionary::LogfileGroup ndb_lg;
    NdbDictionary::Undofile ndb_uf;
unknown's avatar
unknown committed
10337
    NdbDictionary::ObjectId objid;
unknown's avatar
unknown committed
10338 10339 10340 10341 10342 10343 10344 10345 10346 10347 10348
    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
10349
    errmsg= "LOGFILE GROUP";
unknown's avatar
unknown committed
10350
    if (dict->createLogfileGroup(ndb_lg, &objid))
unknown's avatar
unknown committed
10351
    {
unknown's avatar
unknown committed
10352
      goto ndberror;
unknown's avatar
unknown committed
10353 10354 10355 10356 10357 10358
    }
    DBUG_PRINT("info", ("Successfully created Logfile Group"));
    if (set_up_undofile(info, &ndb_uf))
    {
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
10359 10360
    errmsg= "UNDOFILE";
    if (dict->createUndofile(ndb_uf))
unknown's avatar
unknown committed
10361
    {
unknown's avatar
unknown committed
10362 10363 10364 10365 10366 10367 10368 10369 10370
      err= dict->getNdbError();
      NdbDictionary::LogfileGroup tmp= dict->getLogfileGroup(ndb_lg.getName());
      if (dict->getNdbError().code == 0 &&
	  tmp.getObjectId() == objid.getObjectId() &&
	  tmp.getObjectVersion() == objid.getObjectVersion())
      {
	dict->dropLogfileGroup(tmp);
      }
      goto ndberror2;
unknown's avatar
unknown committed
10371 10372 10373 10374 10375
    }
    break;
  }
  case (ALTER_LOGFILE_GROUP):
  {
10376
    error= ER_ALTER_FILEGROUP_FAILED;
unknown's avatar
unknown committed
10377 10378 10379 10380 10381 10382 10383 10384 10385 10386 10387 10388
    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
10389 10390
    errmsg= "CREATE UNDOFILE";
    if (dict->createUndofile(ndb_uf))
unknown's avatar
unknown committed
10391
    {
unknown's avatar
unknown committed
10392
      goto ndberror;
unknown's avatar
unknown committed
10393 10394 10395 10396 10397
    }
    break;
  }
  case (DROP_TABLESPACE):
  {
10398
    error= ER_DROP_FILEGROUP_FAILED;
unknown's avatar
unknown committed
10399 10400
    errmsg= "TABLESPACE";
    if (dict->dropTablespace(dict->getTablespace(info->tablespace_name)))
unknown's avatar
unknown committed
10401
    {
unknown's avatar
unknown committed
10402
      goto ndberror;
unknown's avatar
unknown committed
10403
    }
10404
    is_tablespace= 1;
unknown's avatar
unknown committed
10405 10406 10407 10408
    break;
  }
  case (DROP_LOGFILE_GROUP):
  {
10409
    error= ER_DROP_FILEGROUP_FAILED;
unknown's avatar
unknown committed
10410 10411
    errmsg= "LOGFILE GROUP";
    if (dict->dropLogfileGroup(dict->getLogfileGroup(info->logfile_group_name)))
unknown's avatar
unknown committed
10412
    {
unknown's avatar
unknown committed
10413
      goto ndberror;
unknown's avatar
unknown committed
10414 10415 10416 10417 10418 10419 10420 10421 10422 10423 10424 10425 10426 10427 10428 10429
    }
    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);
  }
  }
10430
#ifdef HAVE_NDB_BINLOG
10431 10432 10433 10434 10435
  if (is_tablespace)
    ndbcluster_log_schema_op(thd, 0,
                             thd->query, thd->query_length,
                             "", info->tablespace_name,
                             0, 0,
10436
                             SOT_TABLESPACE, 0, 0, 0);
10437 10438 10439 10440 10441
  else
    ndbcluster_log_schema_op(thd, 0,
                             thd->query, thd->query_length,
                             "", info->logfile_group_name,
                             0, 0,
10442
                             SOT_LOGFILE_GROUP, 0, 0, 0);
10443
#endif
unknown's avatar
unknown committed
10444
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
10445 10446

ndberror:
unknown's avatar
unknown committed
10447 10448
  err= dict->getNdbError();
ndberror2:
unknown's avatar
unknown committed
10449 10450 10451 10452 10453
  ERR_PRINT(err);
  ndb_to_mysql_error(&err);
  
  my_error(error, MYF(0), errmsg);
  DBUG_RETURN(1);
unknown's avatar
unknown committed
10454 10455
}

unknown's avatar
unknown committed
10456 10457 10458 10459 10460 10461 10462 10463

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");
10464
  LINT_INIT(err);
unknown's avatar
unknown committed
10465 10466 10467

  set_dbname(name);
  set_tabname(name);
10468
  for (;;)
unknown's avatar
unknown committed
10469 10470 10471 10472 10473 10474 10475
  {
    if (check_ndb_connection())
    {
      err= HA_ERR_NO_CONNECTION;
      break;
    }
    ndb= get_ndb();
10476
    ndb->setDatabaseName(m_dbname);
10477 10478
    Ndb_table_guard ndbtab_g(dict= ndb->getDictionary(), m_tabname);
    if (!ndbtab_g.get_table())
unknown's avatar
unknown committed
10479
      ERR_BREAK(dict->getNdbError(), err);
10480
    *no_parts= ndbtab_g.get_table()->getFragmentCount();
unknown's avatar
unknown committed
10481
    DBUG_RETURN(FALSE);
10482
  }
unknown's avatar
unknown committed
10483 10484 10485 10486 10487

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

10488 10489 10490
static int ndbcluster_fill_files_table(handlerton *hton, 
                                       THD *thd, 
                                       TABLE_LIST *tables,
10491
                                       COND *cond)
10492 10493
{
  TABLE* table= tables->table;
10494
  Ndb *ndb= check_ndb_in_thd(thd);
10495 10496
  NdbDictionary::Dictionary* dict= ndb->getDictionary();
  NdbDictionary::Dictionary::List dflist;
10497
  NdbError ndberr;
10498
  uint i;
10499
  DBUG_ENTER("ndbcluster_fill_files_table");
10500

10501 10502
  dict->listObjects(dflist, NdbDictionary::Object::Datafile);
  ndberr= dict->getNdbError();
10503 10504
  if (ndberr.classification != NdbError::NoError)
    ERR_RETURN(ndberr);
10505

10506
  for (i= 0; i < dflist.count; i++)
10507 10508 10509
  {
    NdbDictionary::Dictionary::List::Element& elt = dflist.elements[i];
    Ndb_cluster_connection_node_iter iter;
10510 10511
    uint id;
    
10512 10513
    g_ndb_cluster_connection->init_get_next_node(iter);

10514
    while ((id= g_ndb_cluster_connection->get_next_node(iter)))
10515
    {
10516
      init_fill_schema_files_row(table);
10517
      NdbDictionary::Datafile df= dict->getDatafile(id, elt.name);
10518
      ndberr= dict->getNdbError();
10519 10520 10521 10522 10523 10524
      if(ndberr.classification != NdbError::NoError)
      {
        if (ndberr.classification == NdbError::SchemaError)
          continue;
        ERR_RETURN(ndberr);
      }
10525 10526
      NdbDictionary::Tablespace ts= dict->getTablespace(df.getTablespace());
      ndberr= dict->getNdbError();
10527 10528 10529 10530 10531 10532
      if (ndberr.classification != NdbError::NoError)
      {
        if (ndberr.classification == NdbError::SchemaError)
          continue;
        ERR_RETURN(ndberr);
      }
10533

10534 10535 10536 10537 10538 10539 10540 10541 10542 10543 10544 10545 10546 10547 10548 10549 10550 10551 10552 10553 10554 10555 10556 10557 10558 10559 10560 10561 10562 10563 10564 10565 10566 10567 10568 10569 10570
      table->field[IS_FILES_FILE_NAME]->set_notnull();
      table->field[IS_FILES_FILE_NAME]->store(elt.name, strlen(elt.name),
                                              system_charset_info);
      table->field[IS_FILES_FILE_TYPE]->set_notnull();
      table->field[IS_FILES_FILE_TYPE]->store("DATAFILE",8,
                                              system_charset_info);
      table->field[IS_FILES_TABLESPACE_NAME]->set_notnull();
      table->field[IS_FILES_TABLESPACE_NAME]->store(df.getTablespace(),
                                                    strlen(df.getTablespace()),
                                                    system_charset_info);
      table->field[IS_FILES_LOGFILE_GROUP_NAME]->set_notnull();
      table->field[IS_FILES_LOGFILE_GROUP_NAME]->
        store(ts.getDefaultLogfileGroup(),
              strlen(ts.getDefaultLogfileGroup()),
              system_charset_info);
      table->field[IS_FILES_ENGINE]->set_notnull();
      table->field[IS_FILES_ENGINE]->store(ndbcluster_hton_name,
                                           ndbcluster_hton_name_length,
                                           system_charset_info);

      table->field[IS_FILES_FREE_EXTENTS]->set_notnull();
      table->field[IS_FILES_FREE_EXTENTS]->store(df.getFree()
                                                 / ts.getExtentSize());
      table->field[IS_FILES_TOTAL_EXTENTS]->set_notnull();
      table->field[IS_FILES_TOTAL_EXTENTS]->store(df.getSize()
                                                  / ts.getExtentSize());
      table->field[IS_FILES_EXTENT_SIZE]->set_notnull();
      table->field[IS_FILES_EXTENT_SIZE]->store(ts.getExtentSize());
      table->field[IS_FILES_INITIAL_SIZE]->set_notnull();
      table->field[IS_FILES_INITIAL_SIZE]->store(df.getSize());
      table->field[IS_FILES_MAXIMUM_SIZE]->set_notnull();
      table->field[IS_FILES_MAXIMUM_SIZE]->store(df.getSize());
      table->field[IS_FILES_VERSION]->set_notnull();
      table->field[IS_FILES_VERSION]->store(df.getObjectVersion());

      table->field[IS_FILES_ROW_FORMAT]->set_notnull();
      table->field[IS_FILES_ROW_FORMAT]->store("FIXED", 5, system_charset_info);
10571 10572

      char extra[30];
10573
      int len= my_snprintf(extra, sizeof(extra), "CLUSTER_NODE=%u", id);
10574 10575
      table->field[IS_FILES_EXTRA]->set_notnull();
      table->field[IS_FILES_EXTRA]->store(extra, len, system_charset_info);
10576 10577 10578 10579
      schema_table_store_record(thd, table);
    }
  }

unknown's avatar
ndb -  
unknown committed
10580 10581
  NdbDictionary::Dictionary::List uflist;
  dict->listObjects(uflist, NdbDictionary::Object::Undofile);
10582
  ndberr= dict->getNdbError();
10583 10584
  if (ndberr.classification != NdbError::NoError)
    ERR_RETURN(ndberr);
10585

unknown's avatar
ndb -  
unknown committed
10586
  for (i= 0; i < uflist.count; i++)
10587
  {
unknown's avatar
ndb -  
unknown committed
10588
    NdbDictionary::Dictionary::List::Element& elt= uflist.elements[i];
10589 10590 10591 10592 10593
    Ndb_cluster_connection_node_iter iter;
    unsigned id;

    g_ndb_cluster_connection->init_get_next_node(iter);

10594
    while ((id= g_ndb_cluster_connection->get_next_node(iter)))
10595 10596
    {
      NdbDictionary::Undofile uf= dict->getUndofile(id, elt.name);
10597
      ndberr= dict->getNdbError();
10598 10599 10600 10601 10602 10603
      if (ndberr.classification != NdbError::NoError)
      {
        if (ndberr.classification == NdbError::SchemaError)
          continue;
        ERR_RETURN(ndberr);
      }
10604 10605 10606
      NdbDictionary::LogfileGroup lfg=
        dict->getLogfileGroup(uf.getLogfileGroup());
      ndberr= dict->getNdbError();
10607 10608 10609 10610 10611 10612
      if (ndberr.classification != NdbError::NoError)
      {
        if (ndberr.classification == NdbError::SchemaError)
          continue;
        ERR_RETURN(ndberr);
      }
10613

10614 10615 10616 10617 10618 10619 10620
      init_fill_schema_files_row(table);
      table->field[IS_FILES_FILE_NAME]->set_notnull();
      table->field[IS_FILES_FILE_NAME]->store(elt.name, strlen(elt.name),
                                              system_charset_info);
      table->field[IS_FILES_FILE_TYPE]->set_notnull();
      table->field[IS_FILES_FILE_TYPE]->store("UNDO LOG", 8,
                                              system_charset_info);
unknown's avatar
unknown committed
10621 10622
      NdbDictionary::ObjectId objid;
      uf.getLogfileGroupId(&objid);
10623 10624 10625 10626 10627 10628 10629 10630 10631 10632 10633 10634 10635 10636 10637 10638 10639 10640 10641 10642 10643 10644 10645
      table->field[IS_FILES_LOGFILE_GROUP_NAME]->set_notnull();
      table->field[IS_FILES_LOGFILE_GROUP_NAME]->store(uf.getLogfileGroup(),
                                                  strlen(uf.getLogfileGroup()),
                                                       system_charset_info);
      table->field[IS_FILES_LOGFILE_GROUP_NUMBER]->set_notnull();
      table->field[IS_FILES_LOGFILE_GROUP_NUMBER]->store(objid.getObjectId());
      table->field[IS_FILES_ENGINE]->set_notnull();
      table->field[IS_FILES_ENGINE]->store(ndbcluster_hton_name,
                                           ndbcluster_hton_name_length,
                                           system_charset_info);

      table->field[IS_FILES_TOTAL_EXTENTS]->set_notnull();
      table->field[IS_FILES_TOTAL_EXTENTS]->store(uf.getSize()/4);
      table->field[IS_FILES_EXTENT_SIZE]->set_notnull();
      table->field[IS_FILES_EXTENT_SIZE]->store(4);

      table->field[IS_FILES_INITIAL_SIZE]->set_notnull();
      table->field[IS_FILES_INITIAL_SIZE]->store(uf.getSize());
      table->field[IS_FILES_MAXIMUM_SIZE]->set_notnull();
      table->field[IS_FILES_MAXIMUM_SIZE]->store(uf.getSize());

      table->field[IS_FILES_VERSION]->set_notnull();
      table->field[IS_FILES_VERSION]->store(uf.getObjectVersion());
10646

10647 10648
      char extra[100];
      int len= my_snprintf(extra,sizeof(extra),"CLUSTER_NODE=%u;UNDO_BUFFER_SIZE=%lu",id,lfg.getUndoBufferSize());
10649 10650
      table->field[IS_FILES_EXTRA]->set_notnull();
      table->field[IS_FILES_EXTRA]->store(extra, len, system_charset_info);
10651 10652 10653
      schema_table_store_record(thd, table);
    }
  }
10654 10655 10656 10657 10658 10659 10660 10661 10662 10663 10664 10665 10666 10667 10668 10669 10670 10671 10672 10673 10674 10675

  // now for LFGs
  NdbDictionary::Dictionary::List lfglist;
  dict->listObjects(lfglist, NdbDictionary::Object::LogfileGroup);
  ndberr= dict->getNdbError();
  if (ndberr.classification != NdbError::NoError)
    ERR_RETURN(ndberr);

  for (i= 0; i < lfglist.count; i++)
  {
    NdbDictionary::Dictionary::List::Element& elt= lfglist.elements[i];
    unsigned id;

    NdbDictionary::LogfileGroup lfg= dict->getLogfileGroup(elt.name);
    ndberr= dict->getNdbError();
    if (ndberr.classification != NdbError::NoError)
    {
      if (ndberr.classification == NdbError::SchemaError)
        continue;
      ERR_RETURN(ndberr);
    }

10676 10677 10678 10679 10680 10681 10682 10683 10684 10685 10686 10687 10688 10689 10690 10691 10692 10693 10694 10695 10696 10697 10698
    init_fill_schema_files_row(table);
    table->field[IS_FILES_FILE_TYPE]->set_notnull();
    table->field[IS_FILES_FILE_TYPE]->store("UNDO LOG", 8,
                                            system_charset_info);

    table->field[IS_FILES_LOGFILE_GROUP_NAME]->set_notnull();
    table->field[IS_FILES_LOGFILE_GROUP_NAME]->store(elt.name,
                                                     strlen(elt.name),
                                                     system_charset_info);
    table->field[IS_FILES_LOGFILE_GROUP_NUMBER]->set_notnull();
    table->field[IS_FILES_LOGFILE_GROUP_NUMBER]->store(lfg.getObjectId());
    table->field[IS_FILES_ENGINE]->set_notnull();
    table->field[IS_FILES_ENGINE]->store(ndbcluster_hton_name,
                                         ndbcluster_hton_name_length,
                                         system_charset_info);

    table->field[IS_FILES_FREE_EXTENTS]->set_notnull();
    table->field[IS_FILES_FREE_EXTENTS]->store(lfg.getUndoFreeWords());
    table->field[IS_FILES_EXTENT_SIZE]->set_notnull();
    table->field[IS_FILES_EXTENT_SIZE]->store(4);

    table->field[IS_FILES_VERSION]->set_notnull();
    table->field[IS_FILES_VERSION]->store(lfg.getObjectVersion());
10699 10700

    char extra[100];
10701 10702 10703 10704 10705
    int len= my_snprintf(extra,sizeof(extra),
                         "UNDO_BUFFER_SIZE=%lu",
                         lfg.getUndoBufferSize());
    table->field[IS_FILES_EXTRA]->set_notnull();
    table->field[IS_FILES_EXTRA]->store(extra, len, system_charset_info);
10706 10707
    schema_table_store_record(thd, table);
  }
10708
  DBUG_RETURN(0);
10709
}
unknown's avatar
unknown committed
10710

unknown's avatar
unknown committed
10711 10712 10713 10714 10715
SHOW_VAR ndb_status_variables_export[]= {
  {"Ndb",                      (char*) &ndb_status_variables,   SHOW_ARRAY},
  {NullS, NullS, SHOW_LONG}
};

unknown's avatar
unknown committed
10716
struct st_mysql_storage_engine ndbcluster_storage_engine=
10717
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
unknown's avatar
unknown committed
10718 10719 10720 10721

mysql_declare_plugin(ndbcluster)
{
  MYSQL_STORAGE_ENGINE_PLUGIN,
unknown's avatar
unknown committed
10722
  &ndbcluster_storage_engine,
10723
  ndbcluster_hton_name,
unknown's avatar
unknown committed
10724
  "MySQL AB",
unknown's avatar
unknown committed
10725
  "Clustered, fault-tolerant tables",
10726
  PLUGIN_LICENSE_GPL,
unknown's avatar
unknown committed
10727
  ndbcluster_init, /* Plugin Init */
unknown's avatar
unknown committed
10728 10729
  NULL, /* Plugin Deinit */
  0x0100 /* 1.0 */,
10730 10731 10732
  ndb_status_variables_export,/* status variables                */
  NULL,                       /* system variables                */
  NULL                        /* config options                  */
unknown's avatar
unknown committed
10733 10734 10735 10736
}
mysql_declare_plugin_end;

#endif