ha_ndbcluster.cc 297 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000-2003 MySQL AB
unknown's avatar
unknown committed
2 3 4

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
unknown's avatar
unknown committed
5
  the Free Software Foundation; version 2 of the License.
unknown's avatar
unknown committed
6 7 8 9 10 11 12 13

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

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

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

22
#ifdef USE_PRAGMA_IMPLEMENTATION
23
#pragma implementation				// gcc: Class implementation
unknown's avatar
unknown committed
24 25 26
#endif

#include "mysql_priv.h"
27
#include "rpl_mi.h"
unknown's avatar
unknown committed
28 29

#include <my_dir.h>
unknown's avatar
unknown committed
30
#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
unknown's avatar
unknown committed
31 32
#include "ha_ndbcluster.h"
#include <ndbapi/NdbApi.hpp>
unknown's avatar
unknown committed
33
#include "ha_ndbcluster_cond.h"
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;              \
unknown's avatar
unknown committed
124
  set_ndb_err(current_thd, tmp);         \
125
  DBUG_RETURN(ndb_to_mysql_error(&tmp)); \
unknown's avatar
unknown committed
126 127
}

128 129 130
#define ERR_BREAK(err, code)             \
{                                        \
  const NdbError& tmp= err;              \
unknown's avatar
unknown committed
131
  set_ndb_err(current_thd, tmp);         \
132 133 134 135
  code= ndb_to_mysql_error(&tmp);        \
  break;                                 \
}

unknown's avatar
unknown committed
136
static int ndbcluster_inited= 0;
137
int ndbcluster_terminating= 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
static uchar *ndbcluster_get_key(NDB_SHARE *share, size_t *length,
unknown's avatar
unknown committed
150
                                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
154
static int ndb_get_table_statistics(ha_ndbcluster*, bool, Ndb*, const NDBTAB *, 
155
                                    struct Ndb_statistics *);
156

unknown's avatar
unknown committed
157

unknown's avatar
Merge  
unknown committed
158
// Util thread variables
unknown's avatar
unknown committed
159
pthread_t ndb_util_thread;
160
int ndb_util_thread_running= 0;
unknown's avatar
Merge  
unknown committed
161 162
pthread_mutex_t LOCK_ndb_util_thread;
pthread_cond_t COND_ndb_util_thread;
unknown's avatar
unknown committed
163
pthread_cond_t COND_ndb_util_ready;
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 239 240 241
  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:
    return error;

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

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

unknown's avatar
unknown committed
259 260
int execute_no_commit_ignore_no_key(ha_ndbcluster *h, NdbTransaction *trans)
{
261 262 263
  if (trans->execute(NdbTransaction::NoCommit,
                     NdbOperation::AO_IgnoreError,
                     h->m_force_send) == -1)
264
    return -1;
unknown's avatar
unknown committed
265 266

  const NdbError &err= trans->getNdbError();
267 268
  if (err.classification != NdbError::NoError &&
      err.classification != NdbError::ConstraintViolation &&
unknown's avatar
unknown committed
269
      err.classification != NdbError::NoDataFound)
270
    return -1;
unknown's avatar
unknown committed
271

unknown's avatar
unknown committed
272 273
  return 0;
}
unknown's avatar
unknown committed
274 275

inline
276
int execute_no_commit(ha_ndbcluster *h, NdbTransaction *trans,
277
		      bool force_release)
unknown's avatar
unknown committed
278
{
279
  h->release_completed_operations(trans, force_release);
unknown's avatar
unknown committed
280 281 282
  return h->m_ignore_no_key ?
    execute_no_commit_ignore_no_key(h,trans) :
    trans->execute(NdbTransaction::NoCommit,
283
		   NdbOperation::AbortOnError,
unknown's avatar
unknown committed
284
		   h->m_force_send);
unknown's avatar
unknown committed
285 286 287
}

inline
288
int execute_commit(ha_ndbcluster *h, NdbTransaction *trans)
unknown's avatar
unknown committed
289
{
290
  return trans->execute(NdbTransaction::Commit,
291
                        NdbOperation::AbortOnError,
292
                        h->m_force_send);
293 294 295
}

inline
296
int execute_commit(THD *thd, NdbTransaction *trans)
297
{
298
  return trans->execute(NdbTransaction::Commit,
299
                        NdbOperation::AbortOnError,
300
                        thd->variables.ndb_force_send);
unknown's avatar
unknown committed
301 302 303
}

inline
304
int execute_no_commit_ie(ha_ndbcluster *h, NdbTransaction *trans,
305
			 bool force_release)
unknown's avatar
unknown committed
306
{
307
  h->release_completed_operations(trans, force_release);
308
  return trans->execute(NdbTransaction::NoCommit,
309
                        NdbOperation::AO_IgnoreError,
310
                        h->m_force_send);
unknown's avatar
unknown committed
311 312
}

313 314 315
/*
  Place holder for ha_ndbcluster thread specific data
*/
316
static
317
uchar *thd_ndb_share_get_key(THD_NDB_SHARE *thd_ndb_share, size_t *length,
318 319 320
                            my_bool not_used __attribute__((unused)))
{
  *length= sizeof(thd_ndb_share->key);
321
  return (uchar*) &thd_ndb_share->key;
322 323
}

324 325
Thd_ndb::Thd_ndb()
{
326
  ndb= new Ndb(g_ndb_cluster_connection, "");
327
  lock_count= 0;
328
  start_stmt_count= 0;
329
  count= 0;
330
  trans= NULL;
331
  m_error= FALSE;
332
  m_error_code= 0;
333
  query_state&= NDB_QUERY_NORMAL;
unknown's avatar
unknown committed
334
  options= 0;
335
  (void) hash_init(&open_tables, &my_charset_bin, 5, 0, 0,
336
                   (hash_get_key)thd_ndb_share_get_key, 0, 0);
337 338 339 340
}

Thd_ndb::~Thd_ndb()
{
341
  if (ndb)
342 343
  {
#ifndef DBUG_OFF
unknown's avatar
unknown committed
344 345
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
346 347 348 349 350 351 352 353 354 355
    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
356
    delete ndb;
unknown's avatar
unknown committed
357
    ndb= NULL;
358
  }
359
  changed_tables.empty();
360 361 362 363 364 365
  hash_free(&open_tables);
}

void
Thd_ndb::init_open_tables()
{
366
  count= 0;
367
  m_error= FALSE;
368
  m_error_code= 0;
369 370 371 372 373 374 375
  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
376
  HASH_SEARCH_STATE state;
377
  THD_NDB_SHARE *thd_ndb_share=
378
    (THD_NDB_SHARE*)hash_first(&open_tables, (uchar *)&key, sizeof(key), &state);
unknown's avatar
unknown committed
379
  while (thd_ndb_share && thd_ndb_share->key != key)
380
    thd_ndb_share= (THD_NDB_SHARE*)hash_next(&open_tables, (uchar *)&key, sizeof(key), &state);
381 382 383 384
  if (thd_ndb_share == 0)
  {
    thd_ndb_share= (THD_NDB_SHARE *) alloc_root(&thd->transaction.mem_root,
                                                sizeof(THD_NDB_SHARE));
385 386 387 388 389
    if (!thd_ndb_share)
    {
      mem_alloc_error(sizeof(THD_NDB_SHARE));
      DBUG_RETURN(NULL);
    }
390
    thd_ndb_share->key= key;
391 392
    thd_ndb_share->stat.last_count= count;
    thd_ndb_share->stat.no_uncommitted_rows_count= 0;
unknown's avatar
unknown committed
393
    thd_ndb_share->stat.records= ~(ha_rows)0;
394
    my_hash_insert(&open_tables, (uchar *)thd_ndb_share);
395
  }
396 397 398 399
  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
400
    thd_ndb_share->stat.records= ~(ha_rows)0;
401
  }
unknown's avatar
unknown committed
402 403
  DBUG_PRINT("exit", ("thd_ndb_share: 0x%lx  key: 0x%lx",
                      (long) thd_ndb_share, (long) key));
404
  DBUG_RETURN(thd_ndb_share);
405 406
}

407 408 409
inline
Ndb *ha_ndbcluster::get_ndb()
{
410
  return get_thd_ndb(current_thd)->ndb;
411 412 413 414 415 416
}

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

unknown's avatar
unknown committed
417 418 419
void ha_ndbcluster::set_rec_per_key()
{
  DBUG_ENTER("ha_ndbcluster::get_status_const");
unknown's avatar
unknown committed
420
  for (uint i=0 ; i < table_share->keys ; i++)
unknown's avatar
unknown committed
421 422 423 424 425 426
  {
    table->key_info[i].rec_per_key[table->key_info[i].key_parts-1]= 1;
  }
  DBUG_VOID_RETURN;
}

427 428 429 430
ha_rows ha_ndbcluster::records()
{
  ha_rows retval;
  DBUG_ENTER("ha_ndbcluster::records");
unknown's avatar
unknown committed
431
  struct Ndb_local_table_statistics *local_info= m_table_info;
432 433
  DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
                      ((const NDBTAB *)m_table)->getTableId(),
unknown's avatar
unknown committed
434
                      local_info->no_uncommitted_rows_count));
435 436 437 438

  Ndb *ndb= get_ndb();
  ndb->setDatabaseName(m_dbname);
  struct Ndb_statistics stat;
unknown's avatar
unknown committed
439
  if (ndb_get_table_statistics(this, TRUE, ndb, m_table, &stat) == 0)
440 441 442
  {
    retval= stat.row_count;
  }
443 444
  else
  {
445
    DBUG_RETURN(HA_POS_ERROR);
446
  }
447 448

  THD *thd= current_thd;
449
  if (get_thd_ndb(thd)->m_error)
unknown's avatar
unknown committed
450
    local_info->no_uncommitted_rows_count= 0;
451

unknown's avatar
unknown committed
452
  DBUG_RETURN(retval + local_info->no_uncommitted_rows_count);
453 454
}

455
int ha_ndbcluster::records_update()
456
{
457
  if (m_ha_not_exact_count)
458
    return 0;
459
  DBUG_ENTER("ha_ndbcluster::records_update");
460 461
  int result= 0;

unknown's avatar
unknown committed
462
  struct Ndb_local_table_statistics *local_info= m_table_info;
463
  DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
464
                      ((const NDBTAB *)m_table)->getTableId(),
465
                      local_info->no_uncommitted_rows_count));
466
  {
467
    Ndb *ndb= get_ndb();
468
    struct Ndb_statistics stat;
469 470 471 472
    if (ndb->setDatabaseName(m_dbname))
    {
      return my_errno= HA_ERR_OUT_OF_MEM;
    }
unknown's avatar
unknown committed
473
    result= ndb_get_table_statistics(this, TRUE, ndb, m_table, &stat);
unknown's avatar
unknown committed
474
    if (result == 0)
475 476 477
    {
      stats.mean_rec_length= stat.row_size;
      stats.data_file_length= stat.fragment_memory;
478
      local_info->records= stat.row_count;
479 480
    }
  }
481 482
  {
    THD *thd= current_thd;
483
    if (get_thd_ndb(thd)->m_error)
484
      local_info->no_uncommitted_rows_count= 0;
485
  }
unknown's avatar
unknown committed
486 487
  if (result == 0)
    stats.records= local_info->records+ local_info->no_uncommitted_rows_count;
488
  DBUG_RETURN(result);
489 490
}

491 492
void ha_ndbcluster::no_uncommitted_rows_execute_failure()
{
493 494
  if (m_ha_not_exact_count)
    return;
495
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_execute_failure");
496
  get_thd_ndb(current_thd)->m_error= TRUE;
497
  get_thd_ndb(current_thd)->m_error_code= 0;
498 499 500
  DBUG_VOID_RETURN;
}

501 502
void ha_ndbcluster::no_uncommitted_rows_update(int c)
{
503 504
  if (m_ha_not_exact_count)
    return;
505
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_update");
unknown's avatar
unknown committed
506
  struct Ndb_local_table_statistics *local_info= m_table_info;
507
  local_info->no_uncommitted_rows_count+= c;
508
  DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
509
                      ((const NDBTAB *)m_table)->getTableId(),
510
                      local_info->no_uncommitted_rows_count));
511 512 513 514 515
  DBUG_VOID_RETURN;
}

void ha_ndbcluster::no_uncommitted_rows_reset(THD *thd)
{
516 517
  if (m_ha_not_exact_count)
    return;
518
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_reset");
519 520
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  thd_ndb->count++;
521
  thd_ndb->m_error= FALSE;
522 523 524
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
/*
  Sets the latest ndb error code on the thd_ndb object such that it
  can be retrieved later to know which ndb error caused the handler
  error.
*/
static void set_ndb_err(THD *thd, const NdbError &err)
{
  DBUG_ENTER("set_ndb_err");
  ERR_PRINT(err);

  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  if (thd_ndb == NULL)
    DBUG_VOID_RETURN;
#ifdef NOT_YET
  /*
    Check if error code is overwritten, in this case the original
    failure cause will be lost.  E.g. if 4350 error is given. So
    push a warning so that it can be detected which is the root
    error cause.
  */
  if (thd_ndb->m_query_id == thd->query_id &&
      thd_ndb->m_error_code != 0 &&
      thd_ndb->m_error_code != err.code)
  {
    char buf[FN_REFLEN];
    ndb_error_string(thd_ndb->m_error_code, buf, sizeof(buf));
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
			ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
			thd_ndb->m_error_code, buf, "NDB");
  }
#endif
  thd_ndb->m_query_id= thd->query_id;
  thd_ndb->m_error_code= err.code;
  DBUG_VOID_RETURN;
}

561
int ha_ndbcluster::ndb_err(NdbTransaction *trans)
unknown's avatar
unknown committed
562
{
unknown's avatar
unknown committed
563
  THD *thd= current_thd;
564
  int res;
565
  NdbError err= trans->getNdbError();
unknown's avatar
unknown committed
566 567
  DBUG_ENTER("ndb_err");
  
unknown's avatar
unknown committed
568 569
  set_ndb_err(thd, err);

unknown's avatar
unknown committed
570 571
  switch (err.classification) {
  case NdbError::SchemaError:
572
  {
573 574
    // TODO perhaps we need to do more here, invalidate also in the cache
    m_table->setStatusInvalid();
575 576 577 578 579
    /* 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;
unknown's avatar
unknown committed
580
    close_cached_tables(thd, 0, &table_list);
unknown's avatar
unknown committed
581
    break;
582
  }
unknown's avatar
unknown committed
583 584 585
  default:
    break;
  }
586 587
  res= ndb_to_mysql_error(&err);
  DBUG_PRINT("info", ("transformed ndbcluster error %d to mysql error %d", 
588
                      err.code, res));
589
  if (res == HA_ERR_FOUND_DUPP_KEY)
590 591
  {
    if (m_rows_to_insert == 1)
592 593 594 595 596 597
    {
      /*
	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
      */
598
      m_dupkey= err.code == 630 ? table_share->primary_key : MAX_KEY; 
599
    }
600
    else
unknown's avatar
unknown committed
601 602
    {
      /* We are batching inserts, offending key is not available */
603
      m_dupkey= (uint) -1;
unknown's avatar
unknown committed
604
    }
605
  }
606
  DBUG_RETURN(res);
unknown's avatar
unknown committed
607 608 609
}


610
/*
611
  Override the default get_error_message in order to add the 
612 613 614
  error message of NDB 
 */

615
bool ha_ndbcluster::get_error_message(int error, 
616
                                      String *buf)
617
{
618
  DBUG_ENTER("ha_ndbcluster::get_error_message");
619
  DBUG_PRINT("enter", ("error: %d", error));
620

621
  Ndb *ndb= get_ndb();
622
  if (!ndb)
unknown's avatar
unknown committed
623
    DBUG_RETURN(FALSE);
624

625
  const NdbError err= ndb->getNdbError(error);
626 627 628 629
  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);
630 631 632
}


unknown's avatar
unknown committed
633
#ifndef DBUG_OFF
unknown's avatar
unknown committed
634 635 636 637
/*
  Check if type is supported by NDB.
*/

unknown's avatar
unknown committed
638
static bool ndb_supported_type(enum_field_types type)
unknown's avatar
unknown committed
639 640
{
  switch (type) {
unknown's avatar
unknown committed
641 642 643 644 645 646 647
  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:
648 649
  case MYSQL_TYPE_DECIMAL:    
  case MYSQL_TYPE_NEWDECIMAL:
unknown's avatar
unknown committed
650 651 652 653 654 655 656 657
  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
658
  case MYSQL_TYPE_VARCHAR:
unknown's avatar
unknown committed
659 660 661 662 663 664
  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:         
665
  case MYSQL_TYPE_BIT:
666
  case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
667
    return TRUE;
unknown's avatar
unknown committed
668
  case MYSQL_TYPE_NULL:   
unknown's avatar
unknown committed
669
    break;
unknown's avatar
unknown committed
670
  }
unknown's avatar
unknown committed
671
  return FALSE;
unknown's avatar
unknown committed
672
}
unknown's avatar
unknown committed
673
#endif /* !DBUG_OFF */
unknown's avatar
unknown committed
674 675


676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
/*
  Check if MySQL field type forces var part in ndb storage
*/
static bool field_type_forces_var_part(enum_field_types type)
{
  switch (type) {
  case MYSQL_TYPE_VAR_STRING:
  case MYSQL_TYPE_VARCHAR:
    return TRUE;
  case MYSQL_TYPE_TINY_BLOB:
  case MYSQL_TYPE_BLOB:
  case MYSQL_TYPE_MEDIUM_BLOB:
  case MYSQL_TYPE_LONG_BLOB:
  case MYSQL_TYPE_GEOMETRY:
    return FALSE;
  default:
    return FALSE;
  }
}

unknown's avatar
unknown committed
696 697 698 699 700
/*
  Instruct NDB to set the value of the hidden primary key
*/

bool ha_ndbcluster::set_hidden_key(NdbOperation *ndb_op,
701
                                   uint fieldnr, const uchar *field_ptr)
unknown's avatar
unknown committed
702 703
{
  DBUG_ENTER("set_hidden_key");
unknown's avatar
unknown committed
704
  DBUG_RETURN(ndb_op->equal(fieldnr, (char*)field_ptr) != 0);
unknown's avatar
unknown committed
705 706 707 708 709 710 711 712
}


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

int ha_ndbcluster::set_ndb_key(NdbOperation *ndb_op, Field *field,
713
                               uint fieldnr, const uchar *field_ptr)
unknown's avatar
unknown committed
714 715 716 717 718 719
{
  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));
720
  DBUG_DUMP("key", field_ptr, pack_len);
unknown's avatar
unknown committed
721
  
unknown's avatar
unknown committed
722 723 724 725
  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
726 727 728 729 730 731 732 733
}


/*
 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
734 735
                                 uint fieldnr, int row_offset,
                                 bool *set_blob_value)
unknown's avatar
unknown committed
736
{
737
  const uchar* field_ptr= field->ptr + row_offset;
unknown's avatar
unknown committed
738
  uint32 pack_len= field->pack_length();
unknown's avatar
unknown committed
739
  DBUG_ENTER("set_ndb_value");
unknown's avatar
unknown committed
740
  DBUG_PRINT("enter", ("%d: %s  type: %u  len=%d  is_null=%s", 
unknown's avatar
unknown committed
741
                       fieldnr, field->field_name, field->type(), 
unknown's avatar
unknown committed
742
                       pack_len, field->is_null(row_offset) ? "Y" : "N"));
743
  DBUG_DUMP("value", field_ptr, pack_len);
unknown's avatar
unknown committed
744

unknown's avatar
unknown committed
745
  DBUG_ASSERT(ndb_supported_type(field->type()));
unknown's avatar
unknown committed
746
  {
747
    // ndb currently does not support size 0
unknown's avatar
unknown committed
748
    uint32 empty_field;
749 750
    if (pack_len == 0)
    {
unknown's avatar
unknown committed
751
      pack_len= sizeof(empty_field);
752
      field_ptr= (uchar *)&empty_field;
unknown's avatar
unknown committed
753
      if (field->is_null(row_offset))
754
        empty_field= 0;
unknown's avatar
unknown committed
755
      else
756
        empty_field= 1;
757
    }
unknown's avatar
unknown committed
758 759
    if (! (field->flags & BLOB_FLAG))
    {
760 761
      if (field->type() != MYSQL_TYPE_BIT)
      {
unknown's avatar
unknown committed
762 763 764
        if (field->is_null(row_offset))
        {
          DBUG_PRINT("info", ("field is NULL"));
765
          // Set value to NULL
unknown's avatar
unknown committed
766
          DBUG_RETURN((ndb_op->setValue(fieldnr, (char*)NULL) != 0));
unknown's avatar
unknown committed
767
	}
768
        // Common implementation for most field types
unknown's avatar
unknown committed
769
        DBUG_RETURN(ndb_op->setValue(fieldnr, (char*)field_ptr) != 0);
770 771 772
      }
      else // if (field->type() == MYSQL_TYPE_BIT)
      {
773
        longlong bits= field->val_int();
774
 
775 776
        // Round up bit field length to nearest word boundry
        pack_len= ((pack_len + 3) >> 2) << 2;
777
        DBUG_ASSERT(pack_len <= 8);
unknown's avatar
unknown committed
778
        if (field->is_null(row_offset))
779
          // Set value to NULL
unknown's avatar
unknown committed
780
          DBUG_RETURN((ndb_op->setValue(fieldnr, (char*)NULL) != 0));
781
        DBUG_PRINT("info", ("bit field"));
782
        DBUG_DUMP("value", (uchar*)&bits, pack_len);
783
#ifdef WORDS_BIGENDIAN
784
        /* store lsw first */
unknown's avatar
unknown committed
785 786
        bits = ((bits >> 32) & 0x00000000FFFFFFFFLL)
          |    ((bits << 32) & 0xFFFFFFFF00000000LL);
787
#endif
unknown's avatar
unknown committed
788
        DBUG_RETURN(ndb_op->setValue(fieldnr, (char*)&bits) != 0);
789
      }
unknown's avatar
unknown committed
790 791
    }
    // Blob type
792
    NdbBlob *ndb_blob= ndb_op->getBlobHandle(fieldnr);
unknown's avatar
unknown committed
793 794
    if (ndb_blob != NULL)
    {
unknown's avatar
unknown committed
795
      if (field->is_null(row_offset))
unknown's avatar
unknown committed
796 797 798 799 800 801
        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);
802
      uchar* blob_ptr= NULL;
unknown's avatar
unknown committed
803 804
      field_blob->get_ptr(&blob_ptr);

805 806 807
      // Looks like NULL ptr signals length 0 blob
      if (blob_ptr == NULL) {
        DBUG_ASSERT(blob_len == 0);
808
        blob_ptr= (uchar*)"";
809
      }
unknown's avatar
unknown committed
810

unknown's avatar
unknown committed
811 812
      DBUG_PRINT("value", ("set blob ptr: 0x%lx  len: %u",
                           (long) blob_ptr, blob_len));
813
      DBUG_DUMP("value", blob_ptr, min(blob_len, 26));
unknown's avatar
unknown committed
814

815
      if (set_blob_value)
816
        *set_blob_value= TRUE;
unknown's avatar
unknown committed
817 818 819 820
      // No callback needed to write value
      DBUG_RETURN(ndb_blob->setValue(blob_ptr, blob_len) != 0);
    }
    DBUG_RETURN(1);
unknown's avatar
unknown committed
821
  }
unknown's avatar
unknown committed
822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
}


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

unknown's avatar
unknown committed
839
int g_get_ndb_blobs_value(NdbBlob *ndb_blob, void *arg)
unknown's avatar
unknown committed
840
{
unknown's avatar
unknown committed
841
  DBUG_ENTER("g_get_ndb_blobs_value");
unknown's avatar
unknown committed
842 843 844
  if (ndb_blob->blobsNextBlob() != NULL)
    DBUG_RETURN(0);
  ha_ndbcluster *ha= (ha_ndbcluster *)arg;
845 846
  int ret= get_ndb_blobs_value(ha->table, ha->m_value,
                               ha->m_blobs_buffer, ha->m_blobs_buffer_size,
847
                               ha->m_blobs_offset);
848
  DBUG_RETURN(ret);
unknown's avatar
unknown committed
849 850
}

851 852 853 854 855 856
/*
  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,
857
                        uchar*& buffer, uint& buffer_size,
858
                        my_ptrdiff_t ptrdiff)
unknown's avatar
unknown committed
859 860 861 862 863 864 865 866
{
  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;
867
    for (uint i= 0; i < table->s->fields; i++)
unknown's avatar
unknown committed
868 869
    {
      Field *field= table->field[i];
870
      NdbValue value= value_array[i];
871 872 873
      if (! (field->flags & BLOB_FLAG))
        continue;
      if (value.blob == NULL)
unknown's avatar
unknown committed
874
      {
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891
        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
892
        {
893
          uchar *buf= buffer + offset;
894 895 896
          uint32 len= 0xffffffff;  // Max uint32
          if (ndb_blob->readData(buf, len) != 0)
            ERR_RETURN(ndb_blob->getNdbError());
unknown's avatar
unknown committed
897 898
          DBUG_PRINT("info", ("[%u] offset: %u  buf: 0x%lx  len=%u  [ptrdiff=%d]",
                              i, offset, (long) buf, len, (int)ptrdiff));
899 900
          DBUG_ASSERT(len == len64);
          // Ugly hack assumes only ptr needs to be changed
901
          field_blob->set_ptr_offset(ptrdiff, len, buf);
unknown's avatar
unknown committed
902
        }
903 904 905 906 907
        offset+= size;
      }
      else if (loop == 1) // undefined or null
      {
        // have to set length even in this case
908
        uchar *buf= buffer + offset; // or maybe NULL
909
        uint32 len= 0;
910
	field_blob->set_ptr_offset(ptrdiff, len, buf);
911
        DBUG_PRINT("info", ("[%u] isNull=%d", i, isNull));
unknown's avatar
unknown committed
912 913
      }
    }
914
    if (loop == 0 && offset > buffer_size)
unknown's avatar
unknown committed
915
    {
916 917 918
      my_free(buffer, MYF(MY_ALLOW_ZERO_PTR));
      buffer_size= 0;
      DBUG_PRINT("info", ("allocate blobs buffer size %u", offset));
919
      buffer= (uchar*) my_malloc(offset, MYF(MY_WME));
920
      if (buffer == NULL)
921 922 923
      {
        sql_print_error("ha_ndbcluster::get_ndb_blobs_value: "
                        "my_malloc(%u) failed", offset);
unknown's avatar
unknown committed
924
        DBUG_RETURN(-1);
unknown's avatar
unknown committed
925
      }
926
      buffer_size= offset;
unknown's avatar
unknown committed
927
    }
unknown's avatar
unknown committed
928
  }
unknown's avatar
unknown committed
929
  DBUG_RETURN(0);
unknown's avatar
unknown committed
930 931 932 933 934
}


/*
  Instruct NDB to fetch one field
unknown's avatar
unknown committed
935 936
  - 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
937 938
*/

unknown's avatar
unknown committed
939
int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field,
940
                                 uint fieldnr, uchar* buf)
unknown's avatar
unknown committed
941 942
{
  DBUG_ENTER("get_ndb_value");
unknown's avatar
unknown committed
943 944 945 946 947
  DBUG_PRINT("enter", ("fieldnr: %d flags: %o", fieldnr,
                       (int)(field != NULL ? field->flags : 0)));

  if (field != NULL)
  {
unknown's avatar
unknown committed
948 949
      DBUG_ASSERT(buf);
      DBUG_ASSERT(ndb_supported_type(field->type()));
unknown's avatar
unknown committed
950 951
      DBUG_ASSERT(field->ptr != NULL);
      if (! (field->flags & BLOB_FLAG))
952
      { 
953 954
        if (field->type() != MYSQL_TYPE_BIT)
        {
955
          uchar *field_buf;
956 957 958
          if (field->pack_length() != 0)
            field_buf= buf + (field->ptr - table->record[0]);
          else
959
            field_buf= (uchar *)&dummy_buf;
960
          m_value[fieldnr].rec= ndb_op->getValue(fieldnr, 
961
                                                 (char*) field_buf);
962
        }
963 964 965 966
        else // if (field->type() == MYSQL_TYPE_BIT)
        {
          m_value[fieldnr].rec= ndb_op->getValue(fieldnr);
        }
unknown's avatar
unknown committed
967 968 969 970 971 972 973 974 975
        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
976
	m_blobs_offset= buf - (uchar*) table->record[0];
unknown's avatar
unknown committed
977
        void *arg= (void *)this;
unknown's avatar
unknown committed
978
        DBUG_RETURN(ndb_blob->setActiveHook(g_get_ndb_blobs_value, arg) != 0);
unknown's avatar
unknown committed
979 980 981 982 983
      }
      DBUG_RETURN(1);
  }

  // Used for hidden key only
984
  m_value[fieldnr].rec= ndb_op->getValue(fieldnr, (char*) m_ref);
unknown's avatar
unknown committed
985 986 987
  DBUG_RETURN(m_value[fieldnr].rec == NULL);
}

988 989 990 991 992 993
/*
  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");
994 995
  DBUG_RETURN(ndb_op->getValue(NdbDictionary::Column::FRAGMENT, 
                               (char *)&m_part_id) == NULL);
996
}
unknown's avatar
unknown committed
997 998 999 1000

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

1002
bool ha_ndbcluster::uses_blob_value()
unknown's avatar
unknown committed
1003
{
1004 1005
  MY_BITMAP *bitmap;
  uint *blob_index, *blob_index_end;
unknown's avatar
unknown committed
1006
  if (table_share->blob_fields == 0)
unknown's avatar
unknown committed
1007
    return FALSE;
1008 1009 1010 1011 1012

  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
1013
  {
1014
    if (bitmap_is_set(bitmap, table->field[*blob_index]->field_index))
1015 1016
      return TRUE;
  } while (++blob_index != blob_index_end);
unknown's avatar
unknown committed
1017
  return FALSE;
unknown's avatar
unknown committed
1018 1019 1020 1021 1022 1023 1024 1025 1026
}


/*
  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
1027 1028 1029 1030

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

1033 1034
int cmp_frm(const NDBTAB *ndbtab, const void *pack_data,
            uint pack_length)
unknown's avatar
unknown committed
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045
{
  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
1046 1047
int ha_ndbcluster::get_metadata(const char *path)
{
1048 1049
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
unknown's avatar
unknown committed
1050 1051 1052 1053 1054
  const NDBTAB *tab;
  int error;
  DBUG_ENTER("get_metadata");
  DBUG_PRINT("enter", ("m_tabname: %s, path: %s", m_tabname, path));

1055 1056
  DBUG_ASSERT(m_table == NULL);
  DBUG_ASSERT(m_table_info == NULL);
1057

1058 1059
  uchar *data= NULL, *pack_data= NULL;
  size_t length, pack_length;
1060 1061 1062 1063 1064 1065 1066 1067

  /*
    Compare FrmData in NDB with frm file from disk.
  */
  error= 0;
  if (readfrm(path, &data, &length) ||
      packfrm(data, length, &pack_data, &pack_length))
  {
1068 1069
    my_free(data, MYF(MY_ALLOW_ZERO_PTR));
    my_free(pack_data, MYF(MY_ALLOW_ZERO_PTR));
1070 1071
    DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
1072
    
1073 1074 1075 1076 1077 1078 1079 1080
  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", 
1081 1082
               ("metadata, pack_length: %lu  getFrmLength: %d  memcmp: %d",
                (ulong) pack_length, tab->getFrmLength(),
1083
                memcmp(pack_data, tab->getFrmData(), pack_length)));
1084 1085
    DBUG_DUMP("pack_data", (uchar*) pack_data, pack_length);
    DBUG_DUMP("frm", (uchar*) tab->getFrmData(), tab->getFrmLength());
1086 1087 1088 1089
    error= HA_ERR_TABLE_DEF_CHANGED;
  }
  my_free((char*)data, MYF(0));
  my_free((char*)pack_data, MYF(0));
1090

unknown's avatar
unknown committed
1091
  if (error)
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104
    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);
1105
}
unknown's avatar
unknown committed
1106

1107
static int fix_unique_index_attr_order(NDB_INDEX_DATA &data,
1108 1109
                                       const NDBINDEX *index,
                                       KEY *key_info)
1110 1111 1112 1113 1114 1115
{
  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
1116
  data.unique_index_attrid_map= (uchar*)my_malloc(sz,MYF(MY_WME));
1117 1118 1119 1120 1121 1122
  if (data.unique_index_attrid_map == 0)
  {
    sql_print_error("fix_unique_index_attr_order: my_malloc(%u) failure",
                    (unsigned int)sz);
    DBUG_RETURN(HA_ERR_OUT_OF_MEM);
  }
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134

  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++)
    {
1135
      const NDBCOL *c= index->getColumn(j);
unknown's avatar
unknown committed
1136
      if (strcmp(field_name, c->getName()) == 0)
1137
      {
1138 1139
        data.unique_index_attrid_map[i]= j;
        break;
1140 1141 1142 1143 1144 1145
      }
    }
    DBUG_ASSERT(data.unique_index_attrid_map[i] != 255);
  }
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1146

1147 1148 1149 1150 1151 1152
/*
  Create all the indexes for a table.
  If any index should fail to be created,
  the error is returned immediately
*/
int ha_ndbcluster::create_indexes(Ndb *ndb, TABLE *tab)
1153
{
1154
  uint i;
unknown's avatar
unknown committed
1155
  int error= 0;
1156
  const char *index_name;
unknown's avatar
unknown committed
1157
  KEY* key_info= tab->key_info;
1158
  const char **key_name= tab->s->keynames.type_names;
1159
  DBUG_ENTER("ha_ndbcluster::create_indexes");
1160

1161
  for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
1162
  {
unknown's avatar
unknown committed
1163
    index_name= *key_name;
1164
    NDB_INDEX_TYPE idx_type= get_index_type_from_table(i);
1165 1166
    error= create_index(index_name, key_info, idx_type, i);
    if (error)
1167
    {
1168 1169
      DBUG_PRINT("error", ("Failed to create index %u", i));
      break;
1170
    }
1171 1172 1173 1174 1175
  }

  DBUG_RETURN(error);
}

unknown's avatar
ndb:  
unknown committed
1176
static void ndb_init_index(NDB_INDEX_DATA &data)
1177
{
unknown's avatar
ndb:  
unknown committed
1178 1179 1180 1181 1182 1183 1184 1185 1186
  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;
1187 1188
}

unknown's avatar
ndb:  
unknown committed
1189
static void ndb_clear_index(NDB_INDEX_DATA &data)
1190
{
unknown's avatar
ndb:  
unknown committed
1191 1192 1193 1194 1195 1196 1197 1198 1199
  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);
1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211
}

/*
  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;
1212 1213
  DBUG_ENTER("ha_ndbcluster::add_index_handle");
  DBUG_PRINT("enter", ("table %s", m_tabname));
1214 1215 1216 1217

  if (idx_type != PRIMARY_KEY_INDEX && idx_type != UNIQUE_INDEX)
  {
    DBUG_PRINT("info", ("Get handle to index %s", index_name));
1218 1219 1220
    const NDBINDEX *index;
    do
    {
1221
      index= dict->getIndexGlobal(index_name, *m_table);
1222 1223
      if (!index)
        ERR_RETURN(dict->getNdbError());
unknown's avatar
unknown committed
1224 1225
      DBUG_PRINT("info", ("index: 0x%lx  id: %d  version: %d.%d  status: %d",
                          (long) index,
1226 1227 1228 1229
                          index->getObjectId(),
                          index->getObjectVersion() & 0xFFFFFF,
                          index->getObjectVersion() >> 24,
                          index->getObjectStatus()));
1230 1231
      DBUG_ASSERT(index->getObjectStatus() ==
                  NdbDictionary::Object::Retrieved);
1232 1233
      break;
    } while (1);
1234
    m_index[index_no].index= index;
1235 1236 1237 1238 1239
    // 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
1240
    {
1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258
      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";
1259
    m_has_unique_index= TRUE;
1260 1261
    strxnmov(unique_index_name, FN_LEN, index_name, unique_suffix, NullS);
    DBUG_PRINT("info", ("Get handle to unique_index %s", unique_index_name));
1262 1263 1264
    const NDBINDEX *index;
    do
    {
1265
      index= dict->getIndexGlobal(unique_index_name, *m_table);
1266 1267
      if (!index)
        ERR_RETURN(dict->getNdbError());
unknown's avatar
unknown committed
1268 1269
      DBUG_PRINT("info", ("index: 0x%lx  id: %d  version: %d.%d  status: %d",
                          (long) index,
1270 1271 1272 1273
                          index->getObjectId(),
                          index->getObjectVersion() & 0xFFFFFF,
                          index->getObjectVersion() >> 24,
                          index->getObjectStatus()));
1274 1275
      DBUG_ASSERT(index->getObjectStatus() ==
                  NdbDictionary::Object::Retrieved);
1276 1277
      break;
    } while (1);
1278
    m_index[index_no].unique_index= index;
1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289
    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
*/
1290
int ha_ndbcluster::open_indexes(Ndb *ndb, TABLE *tab, bool ignore_error)
1291 1292 1293 1294 1295 1296 1297 1298
{
  uint i;
  int error= 0;
  THD *thd=current_thd;
  NDBDICT *dict= ndb->getDictionary();
  KEY* key_info= tab->key_info;
  const char **key_name= tab->s->keynames.type_names;
  DBUG_ENTER("ha_ndbcluster::open_indexes");
1299
  m_has_unique_index= FALSE;
1300 1301 1302
  for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
  {
    if ((error= add_index_handle(thd, dict, key_info, *key_name, i)))
1303 1304 1305 1306
      if (ignore_error)
        m_index[i].index= m_index[i].unique_index= NULL;
      else
        break;
unknown's avatar
unknown committed
1307
    m_index[i].null_in_unique_index= FALSE;
1308
    if (check_index_fields_not_null(key_info))
unknown's avatar
unknown committed
1309
      m_index[i].null_in_unique_index= TRUE;
1310
  }
1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331

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

1332 1333 1334 1335 1336 1337 1338
  DBUG_RETURN(error);
}

/*
  Renumber indexes in index list by shifting out
  indexes that are to be dropped
 */
1339
void ha_ndbcluster::renumber_indexes(Ndb *ndb, TABLE *tab)
1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359
{
  uint i;
  const char *index_name;
  KEY* key_info= tab->key_info;
  const char **key_name= tab->s->keynames.type_names;
  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
1360
      {
1361 1362 1363 1364
        tmp=  m_index[j - 1];
        m_index[j - 1]= m_index[j];
        m_index[j]= tmp;
        j++;
unknown's avatar
unknown committed
1365 1366
      }
    }
1367 1368
  }

1369
  DBUG_VOID_RETURN;
1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383
}

/*
  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
1384
  for (i= 0; i < tab->s->keys; i++, key_info++)
1385 1386 1387 1388
  {
    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)
1389
    {
1390 1391
      const NdbDictionary::Index *index= m_index[i].index;
      const NdbDictionary::Index *unique_index= m_index[i].unique_index;
1392 1393
      
      if (index)
1394
      {
1395 1396 1397
        index_name= index->getName();
        DBUG_PRINT("info", ("Dropping index %u: %s", i, index_name));  
        // Drop ordered index from ndb
1398 1399 1400 1401 1402 1403
        error= dict->dropIndexGlobal(*index);
        if (!error)
        {
          dict->removeIndexGlobal(*index, 1);
          m_index[i].index= NULL;
        }
1404 1405
      }
      if (!error && unique_index)
1406
      {
unknown's avatar
unknown committed
1407 1408
        index_name= unique_index->getName();
        DBUG_PRINT("info", ("Dropping unique index %u: %s", i, index_name));
1409
        // Drop unique index from ndb
1410 1411 1412 1413 1414 1415
        error= dict->dropIndexGlobal(*unique_index);
        if (!error)
        {
          dict->removeIndexGlobal(*unique_index, 1);
          m_index[i].unique_index= NULL;
        }
1416
      }
1417 1418
      if (error)
        DBUG_RETURN(error);
unknown's avatar
ndb:  
unknown committed
1419
      ndb_clear_index(m_index[i]);
1420
      continue;
1421
    }
1422
  }
unknown's avatar
unknown committed
1423 1424
  
  DBUG_RETURN(error);
1425 1426
}

unknown's avatar
unknown committed
1427 1428 1429 1430
/*
  Decode the type of an index from information 
  provided in table object
*/
1431
NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_table(uint inx) const
unknown's avatar
unknown committed
1432
{
1433 1434
  return get_index_type_from_key(inx, table_share->key_info,
                                 inx == table_share->primary_key);
1435 1436 1437
}

NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_key(uint inx,
1438 1439
                                                      KEY *key_info,
                                                      bool primary) const
1440 1441
{
  bool is_hash_index=  (key_info[inx].algorithm == 
unknown's avatar
unknown committed
1442
                        HA_KEY_ALG_HASH);
1443
  if (primary)
1444
    return is_hash_index ? PRIMARY_KEY_INDEX : PRIMARY_KEY_ORDERED_INDEX;
1445 1446
  
  return ((key_info[inx].flags & HA_NOSAME) ? 
1447 1448
          (is_hash_index ? UNIQUE_INDEX : UNIQUE_ORDERED_INDEX) :
          ORDERED_INDEX);
unknown's avatar
unknown committed
1449
} 
1450

1451
bool ha_ndbcluster::check_index_fields_not_null(KEY* key_info)
1452 1453 1454
{
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
1455
  DBUG_ENTER("ha_ndbcluster::check_index_fields_not_null");
1456 1457 1458 1459 1460
  
  for (; key_part != end; key_part++) 
    {
      Field* field= key_part->field;
      if (field->maybe_null())
unknown's avatar
unknown committed
1461
	DBUG_RETURN(TRUE);
1462 1463
    }
  
unknown's avatar
unknown committed
1464
  DBUG_RETURN(FALSE);
1465
}
unknown's avatar
unknown committed
1466

1467
void ha_ndbcluster::release_metadata(THD *thd, Ndb *ndb)
unknown's avatar
unknown committed
1468
{
1469
  uint i;
1470

unknown's avatar
unknown committed
1471 1472 1473
  DBUG_ENTER("release_metadata");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));

1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487
  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
1488
  m_table_info= NULL;
unknown's avatar
unknown committed
1489

1490
  // Release index list 
1491 1492
  for (i= 0; i < MAX_KEY; i++)
  {
1493 1494 1495 1496 1497 1498 1499 1500 1501 1502
    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
1503
    ndb_clear_index(m_index[i]);
1504 1505
  }

1506
  m_table= NULL;
unknown's avatar
unknown committed
1507 1508 1509
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
1510
int ha_ndbcluster::get_ndb_lock_type(enum thr_lock_type type)
1511
{
1512
  if (type >= TL_WRITE_ALLOW_WRITE)
unknown's avatar
unknown committed
1513
    return NdbOperation::LM_Exclusive;
unknown's avatar
unknown committed
1514 1515
  if (type ==  TL_READ_WITH_SHARED_LOCKS ||
      uses_blob_value())
1516
    return NdbOperation::LM_Read;
1517
  return NdbOperation::LM_CommittedRead;
1518 1519
}

unknown's avatar
unknown committed
1520 1521 1522 1523 1524 1525
static const ulong index_type_flags[]=
{
  /* UNDEFINED_INDEX */
  0,                         

  /* PRIMARY_KEY_INDEX */
1526
  HA_ONLY_WHOLE_INDEX, 
1527 1528

  /* PRIMARY_KEY_ORDERED_INDEX */
1529
  /* 
unknown's avatar
unknown committed
1530
     Enable HA_KEYREAD_ONLY when "sorted" indexes are supported, 
1531 1532 1533
     thus ORDERD BY clauses can be optimized by reading directly 
     through the index.
  */
unknown's avatar
unknown committed
1534
  // HA_KEYREAD_ONLY | 
unknown's avatar
unknown committed
1535
  HA_READ_NEXT |
1536
  HA_READ_PREV |
unknown's avatar
unknown committed
1537 1538
  HA_READ_RANGE |
  HA_READ_ORDER,
unknown's avatar
unknown committed
1539 1540

  /* UNIQUE_INDEX */
1541
  HA_ONLY_WHOLE_INDEX,
unknown's avatar
unknown committed
1542

1543
  /* UNIQUE_ORDERED_INDEX */
unknown's avatar
unknown committed
1544
  HA_READ_NEXT |
1545
  HA_READ_PREV |
unknown's avatar
unknown committed
1546 1547
  HA_READ_RANGE |
  HA_READ_ORDER,
1548

unknown's avatar
unknown committed
1549
  /* ORDERED_INDEX */
unknown's avatar
unknown committed
1550
  HA_READ_NEXT |
1551
  HA_READ_PREV |
unknown's avatar
unknown committed
1552 1553
  HA_READ_RANGE |
  HA_READ_ORDER
unknown's avatar
unknown committed
1554 1555 1556 1557 1558 1559 1560
};

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);
1561
  return m_index[idx_no].type;
unknown's avatar
unknown committed
1562 1563
}

1564 1565 1566 1567 1568 1569
inline bool ha_ndbcluster::has_null_in_unique_index(uint idx_no) const
{
  DBUG_ASSERT(idx_no < MAX_KEY);
  return m_index[idx_no].null_in_unique_index;
}

unknown's avatar
unknown committed
1570 1571 1572 1573 1574 1575 1576 1577

/*
  Get the flags for an index

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

1578 1579
inline ulong ha_ndbcluster::index_flags(uint idx_no, uint part,
                                        bool all_parts) const 
unknown's avatar
unknown committed
1580
{ 
1581
  DBUG_ENTER("ha_ndbcluster::index_flags");
unknown's avatar
unknown committed
1582
  DBUG_PRINT("enter", ("idx_no: %u", idx_no));
unknown's avatar
unknown committed
1583
  DBUG_ASSERT(get_index_type_from_table(idx_no) < index_flags_size);
1584 1585
  DBUG_RETURN(index_type_flags[get_index_type_from_table(idx_no)] | 
              HA_KEY_SCAN_NOT_ROR);
unknown's avatar
unknown committed
1586 1587
}

1588
static void shrink_varchar(Field* field, const uchar* & ptr, uchar* buf)
unknown's avatar
unknown committed
1589
{
1590
  if (field->type() == MYSQL_TYPE_VARCHAR && ptr != NULL) {
unknown's avatar
unknown committed
1591
    Field_varstring* f= (Field_varstring*)field;
unknown's avatar
unknown committed
1592
    if (f->length_bytes == 1) {
unknown's avatar
unknown committed
1593 1594 1595 1596 1597
      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
1598
        DBUG_ASSERT(FALSE);
unknown's avatar
unknown committed
1599 1600 1601 1602 1603 1604 1605
        buf[0]= 255;
      }
      memmove(buf + 1, ptr + 2, pack_len - 1);
      ptr= buf;
    }
  }
}
unknown's avatar
unknown committed
1606

1607
int ha_ndbcluster::set_primary_key(NdbOperation *op, const uchar *key)
unknown's avatar
unknown committed
1608
{
unknown's avatar
unknown committed
1609
  KEY* key_info= table->key_info + table_share->primary_key;
unknown's avatar
unknown committed
1610 1611 1612 1613 1614 1615 1616
  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;
1617 1618
    const uchar* ptr= key;
    uchar buf[256];
unknown's avatar
unknown committed
1619
    shrink_varchar(field, ptr, buf);
unknown's avatar
unknown committed
1620
    if (set_ndb_key(op, field, 
1621
                    key_part->fieldnr-1, ptr))
unknown's avatar
unknown committed
1622
      ERR_RETURN(op->getNdbError());
unknown's avatar
unknown committed
1623
    key += key_part->store_length;
unknown's avatar
unknown committed
1624 1625 1626 1627 1628
  }
  DBUG_RETURN(0);
}


1629
int ha_ndbcluster::set_primary_key_from_record(NdbOperation *op, const uchar *record)
1630
{
unknown's avatar
unknown committed
1631
  KEY* key_info= table->key_info + table_share->primary_key;
1632 1633
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
1634
  DBUG_ENTER("set_primary_key_from_record");
1635 1636 1637 1638 1639

  for (; key_part != end; key_part++) 
  {
    Field* field= key_part->field;
    if (set_ndb_key(op, field, 
1640
		    key_part->fieldnr-1, record+key_part->offset))
unknown's avatar
unknown committed
1641 1642 1643 1644 1645
      ERR_RETURN(op->getNdbError());
  }
  DBUG_RETURN(0);
}

1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656
bool ha_ndbcluster::check_index_fields_in_write_set(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("check_index_fields_in_write_set");

  for (i= 0; key_part != end; key_part++, i++)
  {
    Field* field= key_part->field;
1657
    if (!bitmap_is_set(table->write_set, field->field_index))
1658 1659 1660 1661 1662 1663 1664 1665
    {
      DBUG_RETURN(false);
    }
  }

  DBUG_RETURN(true);
}

1666
int ha_ndbcluster::set_index_key_from_record(NdbOperation *op, 
1667
                                             const uchar *record, uint keyno)
1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684
{
  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);
}

1685 1686
int 
ha_ndbcluster::set_index_key(NdbOperation *op, 
1687
                             const KEY *key_info, 
1688
                             const uchar * key_ptr)
1689
{
1690
  DBUG_ENTER("ha_ndbcluster::set_index_key");
1691 1692 1693 1694 1695 1696
  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
1697
    Field* field= key_part->field;
1698 1699
    const uchar* ptr= key_part->null_bit ? key_ptr + 1 : key_ptr;
    uchar buf[256];
unknown's avatar
unknown committed
1700
    shrink_varchar(field, ptr, buf);
unknown's avatar
Merge  
unknown committed
1701
    if (set_ndb_key(op, field, m_index[active_index].unique_index_attrid_map[i], ptr))
1702 1703 1704 1705 1706
      ERR_RETURN(m_active_trans->getNdbError());
    key_ptr+= key_part->store_length;
  }
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1707

unknown's avatar
unknown committed
1708
inline 
1709
int ha_ndbcluster::define_read_attrs(uchar* buf, NdbOperation* op)
unknown's avatar
unknown committed
1710 1711 1712 1713 1714
{
  uint i;
  DBUG_ENTER("define_read_attrs");  

  // Define attributes to read
unknown's avatar
unknown committed
1715
  for (i= 0; i < table_share->fields; i++) 
unknown's avatar
unknown committed
1716 1717
  {
    Field *field= table->field[i];
1718
    if (bitmap_is_set(table->read_set, i) ||
1719
        ((field->flags & PRI_KEY_FLAG)))
unknown's avatar
unknown committed
1720 1721
    {      
      if (get_ndb_value(op, field, i, buf))
1722
        ERR_RETURN(op->getNdbError());
unknown's avatar
unknown committed
1723
    } 
1724
    else
unknown's avatar
unknown committed
1725 1726 1727 1728 1729
    {
      m_value[i].ptr= NULL;
    }
  }
    
unknown's avatar
unknown committed
1730
  if (table_share->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
1731 1732 1733
  {
    DBUG_PRINT("info", ("Getting hidden key"));
    // Scanning table with no primary key
unknown's avatar
unknown committed
1734
    int hidden_no= table_share->fields;      
unknown's avatar
unknown committed
1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745
#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
1746

unknown's avatar
unknown committed
1747 1748 1749 1750
/*
  Read one record from NDB using primary key
*/

1751
int ha_ndbcluster::pk_read(const uchar *key, uint key_len, uchar *buf,
1752
                           uint32 part_id)
unknown's avatar
unknown committed
1753
{
unknown's avatar
unknown committed
1754
  uint no_fields= table_share->fields;
unknown's avatar
unknown committed
1755 1756
  NdbConnection *trans= m_active_trans;
  NdbOperation *op;
1757

1758 1759 1760
  int res;
  DBUG_ENTER("pk_read");
  DBUG_PRINT("enter", ("key_len: %u", key_len));
1761
  DBUG_DUMP("key", key, key_len);
1762
  m_write_op= FALSE;
unknown's avatar
unknown committed
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
  
unknown's avatar
unknown committed
1770
  if (table_share->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
1771 1772 1773
  {
    // This table has no primary key, use "hidden" primary key
    DBUG_PRINT("info", ("Using hidden key"));
1774
    DBUG_DUMP("key", key, 8);    
unknown's avatar
unknown committed
1775
    if (set_hidden_key(op, no_fields, key))
1776
      ERR_RETURN(trans->getNdbError());
1777
    
unknown's avatar
unknown committed
1778
    // Read key at the same time, for future reference
unknown's avatar
unknown committed
1779
    if (get_ndb_value(op, NULL, no_fields, NULL))
1780
      ERR_RETURN(trans->getNdbError());
unknown's avatar
unknown committed
1781 1782 1783 1784 1785 1786 1787
  } 
  else 
  {
    if ((res= set_primary_key(op, key)))
      return res;
  }
  
unknown's avatar
unknown committed
1788
  if ((res= define_read_attrs(buf, op)))
1789
    DBUG_RETURN(res);
1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801

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

1802
  if ((res = execute_no_commit_ie(this,trans,FALSE)) != 0 ||
1803
      op->getNdbError().code) 
unknown's avatar
unknown committed
1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814
  {
    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);
}

1815 1816
/*
  Read one complementing record from NDB using primary key from old_data
1817
  or hidden key
1818 1819
*/

1820
int ha_ndbcluster::complemented_read(const uchar *old_data, uchar *new_data,
1821
                                     uint32 old_part_id)
1822
{
unknown's avatar
unknown committed
1823
  uint no_fields= table_share->fields, i;
1824
  NdbTransaction *trans= m_active_trans;
1825
  NdbOperation *op;
1826
  DBUG_ENTER("complemented_read");
1827
  m_write_op= FALSE;
1828

1829
  if (bitmap_is_set_all(table->read_set))
1830
  {
1831 1832
    // We have allready retrieved all fields, nothing to complement
    DBUG_RETURN(0);
1833
  }
1834

1835 1836
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
unknown's avatar
unknown committed
1837
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || 
1838
      op->readTuple(lm) != 0)
1839
    ERR_RETURN(trans->getNdbError());
1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850
  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());
  }
1851 1852 1853 1854

  if (m_use_partition_function)
    op->setPartitionId(old_part_id);
  
1855 1856 1857 1858
  // Read all unreferenced non-key field(s)
  for (i= 0; i < no_fields; i++) 
  {
    Field *field= table->field[i];
1859
    if (!((field->flags & PRI_KEY_FLAG) ||
1860 1861
          bitmap_is_set(table->read_set, i)) &&
        !bitmap_is_set(table->write_set, i))
1862
    {
unknown's avatar
unknown committed
1863
      if (get_ndb_value(op, field, i, new_data))
1864
        ERR_RETURN(trans->getNdbError());
1865 1866 1867
    }
  }
  
unknown's avatar
unknown committed
1868
  if (execute_no_commit(this,trans,FALSE) != 0) 
1869 1870 1871 1872 1873 1874 1875 1876
  {
    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;     
1877 1878 1879 1880 1881 1882 1883 1884

  /**
   * restore m_value
   */
  for (i= 0; i < no_fields; i++) 
  {
    Field *field= table->field[i];
    if (!((field->flags & PRI_KEY_FLAG) ||
1885
          bitmap_is_set(table->read_set, i)))
1886 1887 1888 1889 1890
    {
      m_value[i].ptr= NULL;
    }
  }
  
1891 1892 1893
  DBUG_RETURN(0);
}

1894
/*
1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913
 * 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)
unknown's avatar
unknown committed
1914
        DBUG_RETURN(FALSE);
1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944
      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;
      }
unknown's avatar
unknown committed
1945
      DBUG_RETURN(FALSE);      
1946 1947
    }
  }
unknown's avatar
unknown committed
1948
  DBUG_RETURN(TRUE);
1949 1950 1951
}


unknown's avatar
unknown committed
1952 1953 1954 1955 1956
/**
 * Check if record contains any null valued columns that are part of a key
 */
static
int
unknown's avatar
unknown committed
1957
check_null_in_record(const KEY* key_info, const uchar *record)
unknown's avatar
unknown committed
1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978
{
  KEY_PART_INFO *curr_part, *end_part;
  curr_part= key_info->key_part;
  end_part= curr_part + key_info->key_parts;

  while (curr_part != end_part)
  {
    if (curr_part->null_bit &&
        (record[curr_part->null_offset] & curr_part->null_bit))
      return 1;
    curr_part++;
  }
  return 0;
  /*
    We could instead pre-compute a bitmask in table_share with one bit for
    every null-bit in the key, and so check this just by OR'ing the bitmask
    with the null bitmap in the record.
    But not sure it's worth it.
  */
}

1979 1980 1981
/*
 * Peek to check if any rows already exist with conflicting
 * primary key or unique index values
1982 1983
*/

1984
int ha_ndbcluster::peek_indexed_rows(const uchar *record, 
1985
                                     NDB_WRITE_OP write_op)
1986
{
1987
  NdbTransaction *trans= m_active_trans;
1988
  NdbOperation *op;
1989 1990
  const NdbOperation *first, *last;
  uint i;
1991
  int res;
1992
  DBUG_ENTER("peek_indexed_rows");
unknown's avatar
unknown committed
1993

unknown's avatar
unknown committed
1994
  NdbOperation::LockMode lm=
1995
      (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
1996
  first= NULL;
1997
  if (write_op != NDB_UPDATE && table->s->primary_key != MAX_KEY)
1998
  {
1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010
    /*
     * 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)
2011
    {
2012 2013 2014
      uint32 part_id;
      int error;
      longlong func_value;
2015 2016 2017 2018
      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)
2019 2020
      {
        m_part_info->err_value= func_value;
2021
        DBUG_RETURN(error);
2022
      }
2023
      op->setPartitionId(part_id);
2024 2025
    }
  }
2026 2027 2028 2029 2030 2031 2032 2033 2034 2035
  /*
   * 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)
    {
unknown's avatar
unknown committed
2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046
      /*
        A unique index is defined on table.
        We cannot look up a NULL field value in a unique index. But since
        keys with NULLs are not indexed, such rows cannot conflict anyway, so
        we just skip the index in this case.
      */
      if (check_null_in_record(key_info, record))
      {
        DBUG_PRINT("info", ("skipping check for key with NULL"));
        continue;
      } 
2047 2048 2049 2050 2051
      if (write_op != NDB_INSERT && !check_index_fields_in_write_set(i))
      {
        DBUG_PRINT("info", ("skipping check for key %u not in write_set", i));
        continue;
      }
2052
      NdbIndexOperation *iop;
2053
      const NDBINDEX *unique_index = m_index[i].unique_index;
2054 2055
      key_part= key_info->key_part;
      end= key_part + key_info->key_parts;
2056
      if (!(iop= trans->getNdbIndexOperation(unique_index, m_table)) ||
2057 2058
          iop->readTuple(lm) != 0)
        ERR_RETURN(trans->getNdbError());
2059

2060 2061 2062 2063 2064 2065 2066 2067
      if (!first)
        first= iop;
      if ((res= set_index_key_from_record(iop, record, i)))
        ERR_RETURN(trans->getNdbError());
    }
  }
  last= trans->getLastDefinedOperation();
  if (first)
unknown's avatar
unknown committed
2068
    res= execute_no_commit_ie(this,trans,FALSE);
2069 2070 2071 2072 2073 2074 2075 2076
  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
2077 2078 2079 2080
  {
    table->status= STATUS_NOT_FOUND;
    DBUG_RETURN(ndb_err(trans));
  } 
2081 2082 2083 2084
  else
  {
    DBUG_PRINT("info", ("m_dupkey %d", m_dupkey));
  }
2085 2086
  DBUG_RETURN(0);
}
2087

2088

unknown's avatar
unknown committed
2089 2090 2091 2092
/*
  Read one record from NDB using unique secondary index
*/

2093 2094
int ha_ndbcluster::unique_index_read(const uchar *key,
                                     uint key_len, uchar *buf)
unknown's avatar
unknown committed
2095
{
2096
  int res;
2097
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
2098
  NdbIndexOperation *op;
2099
  DBUG_ENTER("ha_ndbcluster::unique_index_read");
unknown's avatar
unknown committed
2100
  DBUG_PRINT("enter", ("key_len: %u, index: %u", key_len, active_index));
2101
  DBUG_DUMP("key", key, key_len);
unknown's avatar
unknown committed
2102
  
2103 2104
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
2105 2106
  if (!(op= trans->getNdbIndexOperation(m_index[active_index].unique_index, 
                                        m_table)) ||
2107
      op->readTuple(lm) != 0)
unknown's avatar
unknown committed
2108 2109 2110
    ERR_RETURN(trans->getNdbError());
  
  // Set secondary index key(s)
unknown's avatar
unknown committed
2111
  if ((res= set_index_key(op, table->key_info + active_index, key)))
2112 2113
    DBUG_RETURN(res);
  
unknown's avatar
unknown committed
2114
  if ((res= define_read_attrs(buf, op)))
2115
    DBUG_RETURN(res);
unknown's avatar
unknown committed
2116

2117
  if (execute_no_commit_ie(this,trans,FALSE) != 0 ||
2118
      op->getNdbError().code) 
unknown's avatar
unknown committed
2119
  {
unknown's avatar
unknown committed
2120 2121 2122 2123 2124 2125 2126
    int err= ndb_err(trans);
    if(err==HA_ERR_KEY_NOT_FOUND)
      table->status= STATUS_NOT_FOUND;
    else
      table->status= STATUS_GARBAGE;

    DBUG_RETURN(err);
unknown's avatar
unknown committed
2127
  }
unknown's avatar
unknown committed
2128

unknown's avatar
unknown committed
2129 2130 2131 2132 2133 2134
  // The value have now been fetched from NDB
  unpack_record(buf);
  table->status= 0;
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
2135
inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor)
2136 2137
{
  DBUG_ENTER("fetch_next");
2138
  int local_check;
2139
  NdbTransaction *trans= m_active_trans;
2140
  
unknown's avatar
unknown committed
2141 2142 2143 2144 2145 2146 2147 2148
  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
    */
2149
      NdbConnection *con_trans= m_active_trans;
unknown's avatar
unknown committed
2150 2151 2152 2153 2154 2155
      NdbOperation *op;
      // Lock row
      DBUG_PRINT("info", ("Keeping lock on scanned row"));
      
      if (!(op= m_active_cursor->lockCurrentTuple()))
      {
2156
        /* purecov: begin inspected */
unknown's avatar
unknown committed
2157
	m_lock_tuple= FALSE;
2158 2159
	ERR_RETURN(con_trans->getNdbError());
        /* purecov: end */    
unknown's avatar
unknown committed
2160 2161 2162
      }
      m_ops_pending++;
  }
unknown's avatar
unknown committed
2163
  m_lock_tuple= FALSE;
unknown's avatar
unknown committed
2164 2165 2166
  
  bool contact_ndb= m_lock.type < TL_WRITE_ALLOW_WRITE &&
                    m_lock.type != TL_READ_WITH_SHARED_LOCKS;;
2167 2168
  do {
    DBUG_PRINT("info", ("Call nextResult, contact_ndb: %d", contact_ndb));
unknown's avatar
unknown committed
2169 2170 2171
    /*
      We can only handle one tuple with blobs at a time.
    */
2172
    if (m_ops_pending && m_blobs_pending)
unknown's avatar
unknown committed
2173
    {
unknown's avatar
unknown committed
2174
      if (execute_no_commit(this,trans,FALSE) != 0)
2175
        DBUG_RETURN(ndb_err(trans));
2176 2177
      m_ops_pending= 0;
      m_blobs_pending= FALSE;
unknown's avatar
unknown committed
2178
    }
2179
    
2180
    if ((local_check= cursor->nextResult(contact_ndb, m_force_send)) == 0)
2181
    {
unknown's avatar
unknown committed
2182 2183 2184 2185 2186 2187 2188
      /*
	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);
2189 2190
      DBUG_RETURN(0);
    } 
2191
    else if (local_check == 1 || local_check == 2)
2192 2193 2194
    {
      // 1: No more records
      // 2: No more cached records
2195
      
2196
      /*
2197 2198 2199
        Before fetching more rows and releasing lock(s),
        all pending update or delete operations should 
        be sent to NDB
2200
      */
unknown's avatar
unknown committed
2201
      DBUG_PRINT("info", ("ops_pending: %ld", (long) m_ops_pending));    
2202
      if (m_ops_pending)
2203
      {
2204 2205
        if (m_transaction_on)
        {
unknown's avatar
unknown committed
2206
          if (execute_no_commit(this,trans,FALSE) != 0)
2207 2208 2209 2210 2211 2212
            DBUG_RETURN(-1);
        }
        else
        {
          if  (execute_commit(this,trans) != 0)
            DBUG_RETURN(-1);
unknown's avatar
unknown committed
2213
          if (trans->restart() != 0)
2214 2215 2216 2217 2218 2219
          {
            DBUG_ASSERT(0);
            DBUG_RETURN(-1);
          }
        }
        m_ops_pending= 0;
2220
      }
2221
      contact_ndb= (local_check == 2);
2222
    }
unknown's avatar
unknown committed
2223 2224 2225 2226
    else
    {
      DBUG_RETURN(-1);
    }
2227
  } while (local_check == 2);
unknown's avatar
unknown committed
2228

2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239
  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
2240

2241 2242
*/

2243
inline int ha_ndbcluster::next_result(uchar *buf)
2244 2245 2246 2247
{  
  int res;
  DBUG_ENTER("next_result");
    
2248 2249 2250
  if (!m_active_cursor)
    DBUG_RETURN(HA_ERR_END_OF_FILE);
  
unknown's avatar
unknown committed
2251
  if ((res= fetch_next(m_active_cursor)) == 0)
2252 2253 2254 2255 2256 2257 2258
  {
    DBUG_PRINT("info", ("One more record found"));    
    
    unpack_record(buf);
    table->status= 0;
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
2259
  else if (res == 1)
2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270
  {
    // 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
2271 2272
}

2273
/*
2274
  Set bounds for ordered index scan.
2275 2276
*/

unknown's avatar
unknown committed
2277
int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
2278 2279
                              uint inx,
                              bool rir,
2280 2281
                              const key_range *keys[2],
                              uint range_no)
2282
{
2283
  const KEY *const key_info= table->key_info + inx;
2284 2285 2286
  const uint key_parts= key_info->key_parts;
  uint key_tot_len[2];
  uint tot_len;
2287
  uint i, j;
2288 2289

  DBUG_ENTER("set_bounds");
2290
  DBUG_PRINT("info", ("key_parts=%d", key_parts));
2291

2292
  for (j= 0; j <= 1; j++)
2293
  {
2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306
    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;
    }
2307 2308
  }
  tot_len= 0;
unknown's avatar
unknown committed
2309

2310 2311 2312 2313
  for (i= 0; i < key_parts; i++)
  {
    KEY_PART_INFO *key_part= &key_info->key_part[i];
    Field *field= key_part->field;
2314
#ifndef DBUG_OFF
2315
    uint part_len= key_part->length;
2316
#endif
2317 2318 2319 2320 2321
    uint part_store_len= key_part->store_length;
    // Info about each key part
    struct part_st {
      bool part_last;
      const key_range *key;
2322
      const uchar *part_ptr;
2323 2324
      bool part_null;
      int bound_type;
2325
      const uchar* bound_ptr;
2326 2327 2328 2329 2330
    };
    struct part_st part[2];

    for (j= 0; j <= 1; j++)
    {
2331
      struct part_st &p= part[j];
2332 2333 2334 2335 2336 2337 2338
      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
2339
        p.part_null= key_part->null_bit && *p.part_ptr;
2340
        p.bound_ptr= (const char *)
unknown's avatar
unknown committed
2341
          p.part_null ? 0 : key_part->null_bit ? p.part_ptr + 1 : p.part_ptr;
2342 2343 2344 2345 2346 2347

        if (j == 0)
        {
          switch (p.key->flag)
          {
            case HA_READ_KEY_EXACT:
2348 2349 2350 2351
              if (! rir)
                p.bound_type= NdbIndexScanOperation::BoundEQ;
              else // differs for records_in_range
                p.bound_type= NdbIndexScanOperation::BoundLE;
2352
              break;
2353
            // ascending
2354 2355 2356 2357 2358 2359 2360 2361 2362
            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;
2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375
            // 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;
2376 2377 2378 2379 2380 2381 2382
            default:
              break;
          }
        }
        if (j == 1) {
          switch (p.key->flag)
          {
2383
            // ascending
2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394
            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;
2395
            // descending strangely sets no end key
2396 2397
          }
        }
2398

2399 2400 2401
        if (p.bound_type == -1)
        {
          DBUG_PRINT("error", ("key %d unknown flag %d", j, p.key->flag));
unknown's avatar
unknown committed
2402
          DBUG_ASSERT(FALSE);
2403
          // Stop setting bounds but continue with what we have
2404
          DBUG_RETURN(op->end_of_bound(range_no));
2405 2406 2407
        }
      }
    }
2408

2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425
    // 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;
    }
2426

2427 2428
    for (j= 0; j <= 1; j++)
    {
2429
      struct part_st &p= part[j];
2430 2431 2432
      // Set bound if not done with this key
      if (p.key != NULL)
      {
unknown's avatar
unknown committed
2433
        DBUG_PRINT("info", ("key %d:%d  offset: %d  length: %d  last: %d  bound: %d",
2434
                            j, i, tot_len, part_len, p.part_last, p.bound_type));
2435
        DBUG_DUMP("info", p.part_ptr, part_store_len);
2436 2437 2438

        // Set bound if not cancelled via type -1
        if (p.bound_type != -1)
2439
        {
2440 2441
          const uchar* ptr= p.bound_ptr;
          uchar buf[256];
unknown's avatar
unknown committed
2442
          shrink_varchar(field, ptr, buf);
unknown's avatar
Merge  
unknown committed
2443
          if (op->setBound(i, p.bound_type, ptr))
2444
            ERR_RETURN(op->getNdbError());
2445
        }
2446 2447 2448 2449
      }
    }

    tot_len+= part_store_len;
2450
  }
2451
  DBUG_RETURN(op->end_of_bound(range_no));
2452 2453
}

unknown's avatar
unknown committed
2454
/*
2455
  Start ordered index scan in NDB
unknown's avatar
unknown committed
2456 2457
*/

2458
int ha_ndbcluster::ordered_index_scan(const key_range *start_key,
2459
                                      const key_range *end_key,
2460
                                      bool sorted, bool descending,
2461
                                      uchar* buf, part_id_range *part_spec)
unknown's avatar
unknown committed
2462
{  
2463
  int res;
unknown's avatar
unknown committed
2464
  bool restart;
2465
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
2466
  NdbIndexScanOperation *op;
2467

2468 2469 2470
  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
2471
  DBUG_PRINT("enter", ("Starting new ordered scan on %s", m_tabname));
2472
  m_write_op= FALSE;
unknown's avatar
unknown committed
2473

2474 2475
  // Check that sorted seems to be initialised
  DBUG_ASSERT(sorted == 0 || sorted == 1);
unknown's avatar
unknown committed
2476
  
2477
  if (m_active_cursor == 0)
unknown's avatar
unknown committed
2478
  {
unknown's avatar
unknown committed
2479
    restart= FALSE;
unknown's avatar
unknown committed
2480 2481
    NdbOperation::LockMode lm=
      (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
unknown's avatar
unknown committed
2482
   bool need_pk = (lm == NdbOperation::LM_Read);
2483 2484
    if (!(op= trans->getNdbIndexScanOperation(m_index[active_index].index, 
                                              m_table)) ||
unknown's avatar
unknown committed
2485
        op->readTuples(lm, 0, parallelism, sorted, descending, FALSE, need_pk))
unknown's avatar
unknown committed
2486
      ERR_RETURN(trans->getNdbError());
2487 2488 2489
    if (m_use_partition_function && part_spec != NULL &&
        part_spec->start_part == part_spec->end_part)
      op->setPartitionId(part_spec->start_part);
2490
    m_active_cursor= op;
unknown's avatar
unknown committed
2491
  } else {
unknown's avatar
unknown committed
2492
    restart= TRUE;
2493
    op= (NdbIndexScanOperation*)m_active_cursor;
unknown's avatar
unknown committed
2494
    
2495 2496 2497
    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
2498 2499
    DBUG_ASSERT(op->getSorted() == sorted);
    DBUG_ASSERT(op->getLockMode() == 
2500
                (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type));
unknown's avatar
unknown committed
2501
    if (op->reset_bounds(m_force_send))
unknown's avatar
unknown committed
2502 2503
      DBUG_RETURN(ndb_err(m_active_trans));
  }
2504
  
2505
  {
2506
    const key_range *keys[2]= { start_key, end_key };
unknown's avatar
unknown committed
2507
    res= set_bounds(op, active_index, FALSE, keys);
2508 2509
    if (res)
      DBUG_RETURN(res);
2510
  }
2511

2512
  if (!restart)
2513
  {
unknown's avatar
unknown committed
2514
    if (m_cond && m_cond->generate_scan_filter(op))
2515 2516
      DBUG_RETURN(ndb_err(trans));

2517
    if ((res= define_read_attrs(buf, op)))
2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528
    {
      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
2529
  }
2530

unknown's avatar
unknown committed
2531
  if (execute_no_commit(this,trans,FALSE) != 0)
2532 2533 2534 2535
    DBUG_RETURN(ndb_err(trans));
  
  DBUG_RETURN(next_result(buf));
}
unknown's avatar
unknown committed
2536

unknown's avatar
unknown committed
2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560
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;
}

2561 2562 2563 2564 2565 2566

/*
  Unique index scan in NDB (full table scan with scan filter)
 */

int ha_ndbcluster::unique_index_scan(const KEY* key_info, 
2567
				     const uchar *key, 
2568
				     uint key_len,
2569
				     uchar *buf)
2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619
{
  int res;
  NdbScanOperation *op;
  NdbTransaction *trans= m_active_trans;
  part_id_range part_spec;

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

  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
  int flags= guess_scan_flags(lm, m_table, table->read_set);
  if (!(op=trans->getNdbScanOperation((const NDBTAB *) m_table)) ||
      op->readTuples(lm, flags, parallelism))
    ERR_RETURN(trans->getNdbError());
  m_active_cursor= op;

  if (m_use_partition_function)
  {
    part_spec.start_part= 0;
    part_spec.end_part= m_part_info->get_tot_partitions() - 1;
    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);
    }
    // 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());
  }
unknown's avatar
unknown committed
2620 2621 2622 2623 2624 2625 2626 2627
  if (!m_cond)
    m_cond= new ha_ndbcluster_cond;
  if (!m_cond)
  {
    my_errno= HA_ERR_OUT_OF_MEM;
    DBUG_RETURN(my_errno);
  }       
  if (m_cond->generate_scan_filter_from_key(op, key_info, key, key_len, buf))
2628 2629 2630 2631
    DBUG_RETURN(ndb_err(trans));
  if ((res= define_read_attrs(buf, op)))
    DBUG_RETURN(res);

unknown's avatar
unknown committed
2632
  if (execute_no_commit(this,trans,FALSE) != 0)
2633 2634 2635 2636 2637 2638
    DBUG_RETURN(ndb_err(trans));
  DBUG_PRINT("exit", ("Scan started successfully"));
  DBUG_RETURN(next_result(buf));
}


unknown's avatar
unknown committed
2639
/*
2640
  Start full table scan in NDB
unknown's avatar
unknown committed
2641 2642
 */

2643
int ha_ndbcluster::full_table_scan(uchar *buf)
unknown's avatar
unknown committed
2644
{
2645
  int res;
unknown's avatar
unknown committed
2646
  NdbScanOperation *op;
2647
  NdbTransaction *trans= m_active_trans;
2648
  part_id_range part_spec;
unknown's avatar
unknown committed
2649 2650 2651

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

2654 2655
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
unknown's avatar
unknown committed
2656
  int flags= guess_scan_flags(lm, m_table, table->read_set);
2657
  if (!(op=trans->getNdbScanOperation(m_table)) ||
unknown's avatar
unknown committed
2658
      op->readTuples(lm, flags, parallelism))
unknown's avatar
unknown committed
2659
    ERR_RETURN(trans->getNdbError());
2660
  m_active_cursor= op;
2661 2662 2663 2664

  if (m_use_partition_function)
  {
    part_spec.start_part= 0;
2665
    part_spec.end_part= m_part_info->get_tot_partitions() - 1;
2666
    prune_partition_set(table, &part_spec);
unknown's avatar
unknown committed
2667
    DBUG_PRINT("info", ("part_spec.start_part: %u  part_spec.end_part: %u",
2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687
                        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);
    }
2688 2689 2690 2691 2692 2693
    // 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());
2694 2695
  }

unknown's avatar
unknown committed
2696
  if (m_cond && m_cond->generate_scan_filter(op))
2697
    DBUG_RETURN(ndb_err(trans));
unknown's avatar
unknown committed
2698
  if ((res= define_read_attrs(buf, op)))
2699 2700
    DBUG_RETURN(res);

unknown's avatar
unknown committed
2701
  if (execute_no_commit(this,trans,FALSE) != 0)
2702 2703 2704
    DBUG_RETURN(ndb_err(trans));
  DBUG_PRINT("exit", ("Scan started successfully"));
  DBUG_RETURN(next_result(buf));
2705 2706
}

unknown's avatar
unknown committed
2707 2708 2709
/*
  Insert one record into NDB
*/
2710
int ha_ndbcluster::write_row(uchar *record)
unknown's avatar
unknown committed
2711
{
unknown's avatar
unknown committed
2712
  bool has_auto_increment;
unknown's avatar
unknown committed
2713
  uint i;
2714
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
2715 2716
  NdbOperation *op;
  int res;
2717
  THD *thd= table->in_use;
2718 2719
  longlong func_value= 0;
  DBUG_ENTER("ha_ndbcluster::write_row");
2720

2721
  m_write_op= TRUE;
2722 2723 2724 2725 2726 2727 2728 2729
  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) 
    {
unknown's avatar
unknown committed
2730
      int error;
2731 2732

      m_skip_auto_increment= FALSE;
unknown's avatar
unknown committed
2733 2734
      if ((error= update_auto_increment()))
        DBUG_RETURN(error);
2735
      m_skip_auto_increment= (insert_id_for_cur_row == 0);
2736 2737 2738 2739 2740 2741 2742
    }
  }

  /*
   * If IGNORE the ignore constraint violations on primary and unique keys
   */
  if (!m_use_write && m_ignore_dup_key)
2743
  {
2744 2745 2746 2747 2748
    /*
      compare if expression with that in start_bulk_insert()
      start_bulk_insert will set parameters to ensure that each
      write_row is committed individually
    */
2749
    int peek_res= peek_indexed_rows(record, NDB_INSERT);
2750 2751 2752 2753 2754 2755 2756
    
    if (!peek_res) 
    {
      DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
    }
    if (peek_res != HA_ERR_KEY_NOT_FOUND)
      DBUG_RETURN(peek_res);
2757
  }
2758

unknown's avatar
unknown committed
2759
  ha_statistic_increment(&SSV::ha_write_count);
unknown's avatar
unknown committed
2760 2761
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
    table->timestamp_field->set_time();
unknown's avatar
unknown committed
2762

2763
  if (!(op= trans->getNdbOperation(m_table)))
unknown's avatar
unknown committed
2764 2765 2766 2767 2768 2769
    ERR_RETURN(trans->getNdbError());

  res= (m_use_write) ? op->writeTuple() :op->insertTuple(); 
  if (res != 0)
    ERR_RETURN(trans->getNdbError());  
 
2770 2771 2772 2773
  if (m_use_partition_function)
  {
    uint32 part_id;
    int error;
2774 2775 2776 2777
    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)
2778 2779
    {
      m_part_info->err_value= func_value;
2780
      DBUG_RETURN(error);
2781
    }
2782 2783 2784
    op->setPartitionId(part_id);
  }

unknown's avatar
unknown committed
2785
  if (table_share->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
2786 2787
  {
    // Table has hidden primary key
2788
    Ndb *ndb= get_ndb();
2789
    Uint64 auto_value;
2790
    uint retries= NDB_AUTO_INCREMENT_RETRIES;
2791 2792 2793
    int retry_sleep= 30; /* 30 milliseconds, transaction */
    for (;;)
    {
2794
      Ndb_tuple_id_range_guard g(m_share);
2795
      if (ndb->getAutoIncrementValue(m_table, g.range, auto_value, 1) == -1)
2796
      {
2797
	if (--retries &&
2798
	    ndb->getNdbError().status == NdbError::TemporaryError)
2799 2800 2801 2802 2803
	{
	  my_sleep(retry_sleep);
	  continue;
	}
	ERR_RETURN(ndb->getNdbError());
2804 2805 2806
      }
      break;
    }
2807
    if (set_hidden_key(op, table_share->fields, (const uchar*)&auto_value))
unknown's avatar
unknown committed
2808 2809 2810 2811
      ERR_RETURN(op->getNdbError());
  } 
  else 
  {
2812 2813 2814
    int error;
    if ((error= set_primary_key_from_record(op, record)))
      DBUG_RETURN(error);
unknown's avatar
unknown committed
2815 2816 2817
  }

  // Set non-key attribute(s)
unknown's avatar
unknown committed
2818
  bool set_blob_value= FALSE;
2819
  my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
unknown's avatar
unknown committed
2820
  for (i= 0; i < table_share->fields; i++) 
unknown's avatar
unknown committed
2821 2822 2823
  {
    Field *field= table->field[i];
    if (!(field->flags & PRI_KEY_FLAG) &&
2824
	(bitmap_is_set(table->write_set, i) || !m_use_write) &&
unknown's avatar
unknown committed
2825
        set_ndb_value(op, field, i, record-table->record[0], &set_blob_value))
2826
    {
2827
      m_skip_auto_increment= TRUE;
2828
      dbug_tmp_restore_column_map(table->read_set, old_map);
unknown's avatar
unknown committed
2829
      ERR_RETURN(op->getNdbError());
2830
    }
unknown's avatar
unknown committed
2831
  }
2832
  dbug_tmp_restore_column_map(table->read_set, old_map);
unknown's avatar
unknown committed
2833

2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849
  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);
  }

2850 2851
  if (unlikely(m_slow_path))
  {
2852 2853 2854 2855 2856 2857
    /*
      ignore TNTO_NO_LOGGING for slave thd.  It is used to indicate
      log-slave-updates option.  This is instead handled in the
      injector thread, by looking explicitly at the
      opt_log_slave_updates flag.
    */
2858
    Thd_ndb *thd_ndb= get_thd_ndb(thd);
2859
    if (thd->slave_thread)
2860
      op->setAnyValue(thd->server_id);
2861
    else if (thd_ndb->trans_options & TNTO_NO_LOGGING)
2862
      op->setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING);
2863
  }
2864 2865
  m_rows_changed++;

unknown's avatar
unknown committed
2866 2867 2868 2869 2870 2871 2872
  /*
    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!
  */
2873
  m_rows_inserted++;
2874
  no_uncommitted_rows_update(1);
2875
  m_bulk_insert_not_flushed= TRUE;
2876
  if ((m_rows_to_insert == (ha_rows) 1) || 
2877
      ((m_rows_inserted % m_bulk_insert_rows) == 0) ||
2878
      m_primary_key_update ||
2879
      set_blob_value)
2880 2881 2882
  {
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
unknown's avatar
unknown committed
2883
                        "rows_inserted: %d  bulk_insert_rows: %d", 
2884
                        (int)m_rows_inserted, (int)m_bulk_insert_rows));
2885

2886
    m_bulk_insert_not_flushed= FALSE;
2887
    if (m_transaction_on)
2888
    {
unknown's avatar
unknown committed
2889
      if (execute_no_commit(this,trans,FALSE) != 0)
2890
      {
2891 2892 2893
        m_skip_auto_increment= TRUE;
        no_uncommitted_rows_execute_failure();
        DBUG_RETURN(ndb_err(trans));
2894
      }
2895 2896
    }
    else
2897
    {
unknown's avatar
unknown committed
2898
      if (execute_commit(this,trans) != 0)
2899
      {
2900 2901 2902
        m_skip_auto_increment= TRUE;
        no_uncommitted_rows_execute_failure();
        DBUG_RETURN(ndb_err(trans));
2903
      }
unknown's avatar
unknown committed
2904
      if (trans->restart() != 0)
2905
      {
2906 2907
        DBUG_ASSERT(0);
        DBUG_RETURN(-1);
2908
      }
2909
    }
2910
  }
2911
  if ((has_auto_increment) && (m_skip_auto_increment))
unknown's avatar
unknown committed
2912
  {
2913
    Ndb *ndb= get_ndb();
2914
    Uint64 next_val= (Uint64) table->next_number_field->val_int() + 1;
unknown's avatar
unknown committed
2915
#ifndef DBUG_OFF
unknown's avatar
unknown committed
2916
    char buff[22];
unknown's avatar
unknown committed
2917
    DBUG_PRINT("info", 
unknown's avatar
unknown committed
2918 2919
               ("Trying to set next auto increment value to %s",
                llstr(next_val, buff)));
unknown's avatar
unknown committed
2920
#endif
2921 2922
    Ndb_tuple_id_range_guard g(m_share);
    if (ndb->setAutoIncrementValue(m_table, g.range, next_val, TRUE)
2923
        == -1)
2924
      ERR_RETURN(ndb->getNdbError());
2925
  }
2926
  m_skip_auto_increment= TRUE;
2927

2928
  DBUG_PRINT("exit",("ok"));
unknown's avatar
unknown committed
2929 2930 2931 2932 2933 2934
  DBUG_RETURN(0);
}


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

2935 2936
int ha_ndbcluster::key_cmp(uint keynr, const uchar * old_row,
                           const uchar * new_row)
unknown's avatar
unknown committed
2937 2938 2939 2940 2941 2942 2943 2944 2945
{
  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) !=
2946 2947
          (new_row[key_part->null_offset] & key_part->null_bit))
        return 1;
unknown's avatar
unknown committed
2948
    }
2949
    if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
unknown's avatar
unknown committed
2950 2951
    {

2952 2953
      if (key_part->field->cmp_binary((old_row + key_part->offset),
                                      (new_row + key_part->offset),
2954 2955
                                      (ulong) key_part->length))
        return 1;
unknown's avatar
unknown committed
2956 2957 2958 2959
    }
    else
    {
      if (memcmp(old_row+key_part->offset, new_row+key_part->offset,
2960 2961
                 key_part->length))
        return 1;
unknown's avatar
unknown committed
2962 2963 2964 2965 2966 2967 2968 2969 2970
    }
  }
  return 0;
}

/*
  Update one record in NDB using primary key
*/

2971
int ha_ndbcluster::update_row(const uchar *old_data, uchar *new_data)
unknown's avatar
unknown committed
2972
{
unknown's avatar
unknown committed
2973
  THD *thd= table->in_use;
2974
  NdbTransaction *trans= m_active_trans;
2975
  NdbScanOperation* cursor= m_active_cursor;
unknown's avatar
unknown committed
2976 2977
  NdbOperation *op;
  uint i;
2978 2979
  uint32 old_part_id= 0, new_part_id= 0;
  int error;
2980
  longlong func_value;
2981 2982
  bool pk_update= (table_share->primary_key != MAX_KEY &&
		   key_cmp(table_share->primary_key, old_data, new_data));
unknown's avatar
unknown committed
2983
  DBUG_ENTER("update_row");
2984
  m_write_op= TRUE;
unknown's avatar
unknown committed
2985
  
2986
  /*
2987 2988
   * If IGNORE the ignore constraint violations on primary and unique keys,
   * but check that it is not part of INSERT ... ON DUPLICATE KEY UPDATE
2989
   */
2990 2991
  if (m_ignore_dup_key && (thd->lex->sql_command == SQLCOM_UPDATE ||
                           thd->lex->sql_command == SQLCOM_UPDATE_MULTI))
2992
  {
2993 2994
    NDB_WRITE_OP write_op= (pk_update) ? NDB_PK_UPDATE : NDB_UPDATE;
    int peek_res= peek_indexed_rows(new_data, write_op);
2995 2996 2997 2998 2999 3000 3001 3002 3003
    
    if (!peek_res) 
    {
      DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
    }
    if (peek_res != HA_ERR_KEY_NOT_FOUND)
      DBUG_RETURN(peek_res);
  }

unknown's avatar
unknown committed
3004
  ha_statistic_increment(&SSV::ha_update_count);
unknown's avatar
unknown committed
3005
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
3006
  {
unknown's avatar
unknown committed
3007
    table->timestamp_field->set_time();
3008
    bitmap_set_bit(table->write_set, table->timestamp_field->field_index);
3009
  }
unknown's avatar
unknown committed
3010

3011 3012
  if (m_use_partition_function &&
      (error= get_parts_for_update(old_data, new_data, table->record[0],
3013 3014
                                   m_part_info, &old_part_id, &new_part_id,
                                   &func_value)))
3015
  {
3016
    m_part_info->err_value= func_value;
3017 3018 3019
    DBUG_RETURN(error);
  }

3020 3021 3022 3023
  /*
   * Check for update of primary key or partition change
   * for special handling
   */  
3024
  if (pk_update || old_part_id != new_part_id)
3025
  {
3026
    int read_res, insert_res, delete_res, undo_res;
3027

3028 3029
    DBUG_PRINT("info", ("primary key update or partition change, "
                        "doing read+delete+insert"));
3030
    // Get all old fields, since we optimize away fields not in query
3031
    read_res= complemented_read(old_data, new_data, old_part_id);
3032 3033
    if (read_res)
    {
3034
      DBUG_PRINT("info", ("read failed"));
3035 3036
      DBUG_RETURN(read_res);
    }
3037
    // Delete old row
3038
    m_primary_key_update= TRUE;
3039
    delete_res= delete_row(old_data);
3040
    m_primary_key_update= FALSE;
3041 3042 3043
    if (delete_res)
    {
      DBUG_PRINT("info", ("delete failed"));
3044
      DBUG_RETURN(delete_res);
3045
    }     
3046 3047
    // Insert new row
    DBUG_PRINT("info", ("delete succeded"));
3048
    m_primary_key_update= TRUE;
3049
    insert_res= write_row(new_data);
3050
    m_primary_key_update= FALSE;
3051 3052 3053 3054 3055
    if (insert_res)
    {
      DBUG_PRINT("info", ("insert failed"));
      if (trans->commitStatus() == NdbConnection::Started)
      {
3056
        // Undo delete_row(old_data)
3057
        m_primary_key_update= TRUE;
3058
        undo_res= write_row((uchar *)old_data);
3059 3060 3061 3062 3063
        if (undo_res)
          push_warning(current_thd, 
                       MYSQL_ERROR::WARN_LEVEL_WARN, 
                       undo_res, 
                       "NDB failed undoing delete at primary key update");
3064 3065 3066 3067 3068
        m_primary_key_update= FALSE;
      }
      DBUG_RETURN(insert_res);
    }
    DBUG_PRINT("info", ("delete+insert succeeded"));
3069
    DBUG_RETURN(0);
3070
  }
3071

3072
  if (cursor)
unknown's avatar
unknown committed
3073
  {
3074 3075 3076 3077 3078 3079 3080 3081
    /*
      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"));
3082
    if (!(op= cursor->updateCurrentTuple()))
3083
      ERR_RETURN(trans->getNdbError());
unknown's avatar
unknown committed
3084
    m_lock_tuple= FALSE;
3085
    m_ops_pending++;
3086
    if (uses_blob_value())
3087
      m_blobs_pending= TRUE;
3088 3089
    if (m_use_partition_function)
      cursor->setPartitionId(new_part_id);
3090 3091 3092
  }
  else
  {  
3093
    if (!(op= trans->getNdbOperation(m_table)) ||
3094
        op->updateTuple() != 0)
3095 3096
      ERR_RETURN(trans->getNdbError());  
    
3097 3098
    if (m_use_partition_function)
      op->setPartitionId(new_part_id);
unknown's avatar
unknown committed
3099
    if (table_share->primary_key == MAX_KEY) 
3100 3101 3102 3103 3104
    {
      // 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 
3105 3106
      // read into m_ref
      DBUG_DUMP("key", m_ref, NDB_HIDDEN_PRIMARY_KEY_LENGTH);
3107
      
unknown's avatar
unknown committed
3108
      if (set_hidden_key(op, table->s->fields, m_ref))
3109
        ERR_RETURN(op->getNdbError());
3110 3111 3112 3113
    } 
    else 
    {
      int res;
3114
      if ((res= set_primary_key_from_record(op, old_data)))
3115
        DBUG_RETURN(res);
3116
    }
unknown's avatar
unknown committed
3117 3118
  }

3119 3120
  m_rows_changed++;

unknown's avatar
unknown committed
3121
  // Set non-key attribute(s)
3122
  my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
unknown's avatar
unknown committed
3123
  for (i= 0; i < table_share->fields; i++) 
unknown's avatar
unknown committed
3124 3125
  {
    Field *field= table->field[i];
3126
    if (bitmap_is_set(table->write_set, i) &&
unknown's avatar
unknown committed
3127
        (!(field->flags & PRI_KEY_FLAG)) &&
unknown's avatar
unknown committed
3128
        set_ndb_value(op, field, i, new_data - table->record[0]))
3129 3130
    {
      dbug_tmp_restore_column_map(table->read_set, old_map);
unknown's avatar
unknown committed
3131
      ERR_RETURN(op->getNdbError());
3132
    }
unknown's avatar
unknown committed
3133
  }
3134
  dbug_tmp_restore_column_map(table->read_set, old_map);
3135

3136 3137 3138 3139 3140 3141 3142 3143 3144 3145
  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);
  }
3146

3147 3148
  if (unlikely(m_slow_path))
  {
3149 3150 3151 3152 3153 3154
    /*
      ignore TNTO_NO_LOGGING for slave thd.  It is used to indicate
      log-slave-updates option.  This is instead handled in the
      injector thread, by looking explicitly at the
      opt_log_slave_updates flag.
    */
3155
    Thd_ndb *thd_ndb= get_thd_ndb(thd);
3156
    if (thd->slave_thread)
3157
      op->setAnyValue(thd->server_id);
3158
    else if (thd_ndb->trans_options & TNTO_NO_LOGGING)
3159
      op->setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING);
3160
  }
3161 3162 3163 3164 3165 3166 3167
  /*
    Execute update operation if we are not doing a scan for update
    and there exist UPDATE AFTER triggers
  */

  if ((!cursor || m_update_cannot_batch) && 
      execute_no_commit(this,trans,false) != 0) {
3168
    no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
3169
    DBUG_RETURN(ndb_err(trans));
3170
  }
unknown's avatar
unknown committed
3171 3172 3173 3174 3175 3176 3177 3178 3179
  
  DBUG_RETURN(0);
}


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

3180
int ha_ndbcluster::delete_row(const uchar *record)
unknown's avatar
unknown committed
3181
{
unknown's avatar
unknown committed
3182
  THD *thd= table->in_use;
3183
  NdbTransaction *trans= m_active_trans;
3184
  NdbScanOperation* cursor= m_active_cursor;
unknown's avatar
unknown committed
3185
  NdbOperation *op;
3186 3187
  uint32 part_id;
  int error;
unknown's avatar
unknown committed
3188
  DBUG_ENTER("delete_row");
3189
  m_write_op= TRUE;
unknown's avatar
unknown committed
3190

unknown's avatar
unknown committed
3191
  ha_statistic_increment(&SSV::ha_delete_count);
unknown's avatar
unknown committed
3192
  m_rows_changed++;
unknown's avatar
unknown committed
3193

3194 3195 3196 3197 3198 3199 3200
  if (m_use_partition_function &&
      (error= get_part_for_delete(record, table->record[0], m_part_info,
                                  &part_id)))
  {
    DBUG_RETURN(error);
  }

3201
  if (cursor)
unknown's avatar
unknown committed
3202
  {
3203
    /*
3204
      We are scanning records and want to delete the record
3205
      that was just found, call deleteTuple on the cursor 
3206
      to take over the lock to a new delete operation
3207 3208 3209 3210
      And thus setting the primary key of the record from 
      the active record in cursor
    */
    DBUG_PRINT("info", ("Calling deleteTuple on cursor"));
3211
    if (cursor->deleteCurrentTuple() != 0)
3212
      ERR_RETURN(trans->getNdbError());     
unknown's avatar
unknown committed
3213
    m_lock_tuple= FALSE;
3214
    m_ops_pending++;
unknown's avatar
unknown committed
3215

3216 3217 3218
    if (m_use_partition_function)
      cursor->setPartitionId(part_id);

3219 3220
    no_uncommitted_rows_update(-1);

3221 3222
    if (unlikely(m_slow_path))
    {
3223 3224 3225 3226 3227 3228
      /*
        ignore TNTO_NO_LOGGING for slave thd.  It is used to indicate
        log-slave-updates option.  This is instead handled in the
        injector thread, by looking explicitly at the
        opt_log_slave_updates flag.
      */
3229
      Thd_ndb *thd_ndb= get_thd_ndb(thd);
3230
      if (thd->slave_thread)
3231 3232
        ((NdbOperation *)trans->getLastDefinedOperation())->
          setAnyValue(thd->server_id);
3233
      else if (thd_ndb->trans_options & TNTO_NO_LOGGING)
3234 3235
        ((NdbOperation *)trans->getLastDefinedOperation())->
          setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING);
3236
    }
3237
    if (!(m_primary_key_update || m_delete_cannot_batch))
3238 3239
      // If deleting from cursor, NoCommit will be handled in next_result
      DBUG_RETURN(0);
3240 3241
  }
  else
unknown's avatar
unknown committed
3242
  {
3243
    
3244
    if (!(op=trans->getNdbOperation(m_table)) || 
3245
        op->deleteTuple() != 0)
3246 3247
      ERR_RETURN(trans->getNdbError());
    
3248 3249 3250
    if (m_use_partition_function)
      op->setPartitionId(part_id);

3251 3252
    no_uncommitted_rows_update(-1);
    
unknown's avatar
unknown committed
3253
    if (table_share->primary_key == MAX_KEY) 
3254 3255 3256 3257
    {
      // This table has no primary key, use "hidden" primary key
      DBUG_PRINT("info", ("Using hidden key"));
      
unknown's avatar
unknown committed
3258
      if (set_hidden_key(op, table->s->fields, m_ref))
3259
        ERR_RETURN(op->getNdbError());
3260 3261 3262
    } 
    else 
    {
3263 3264
      if ((error= set_primary_key_from_record(op, record)))
        DBUG_RETURN(error);
3265
    }
3266

3267 3268
    if (unlikely(m_slow_path))
    {
3269 3270 3271 3272 3273 3274
      /*
        ignore TNTO_NO_LOGGING for slave thd.  It is used to indicate
        log-slave-updates option.  This is instead handled in the
        injector thread, by looking explicitly at the
        opt_log_slave_updates flag.
      */
3275
      Thd_ndb *thd_ndb= get_thd_ndb(thd);
3276
      if (thd->slave_thread)
3277
        op->setAnyValue(thd->server_id);
3278
      else if (thd_ndb->trans_options & TNTO_NO_LOGGING)
3279
        op->setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING);
3280
    }
unknown's avatar
unknown committed
3281
  }
3282

unknown's avatar
unknown committed
3283
  // Execute delete operation
unknown's avatar
unknown committed
3284
  if (execute_no_commit(this,trans,FALSE) != 0) {
3285
    no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
3286
    DBUG_RETURN(ndb_err(trans));
3287
  }
unknown's avatar
unknown committed
3288 3289
  DBUG_RETURN(0);
}
3290
  
unknown's avatar
unknown committed
3291 3292 3293 3294 3295
/*
  Unpack a record read from NDB 

  SYNOPSIS
    unpack_record()
3296
    buf                 Buffer to store read row
unknown's avatar
unknown committed
3297 3298 3299 3300 3301 3302 3303 3304

  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
3305
void ndb_unpack_record(TABLE *table, NdbValue *value,
3306
                       MY_BITMAP *defined, uchar *buf)
unknown's avatar
unknown committed
3307
{
unknown's avatar
unknown committed
3308
  Field **p_field= table->field, *field= *p_field;
3309
  my_ptrdiff_t row_offset= (my_ptrdiff_t) (buf - table->record[0]);
3310
  my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
unknown's avatar
unknown committed
3311
  DBUG_ENTER("ndb_unpack_record");
3312

unknown's avatar
unknown committed
3313 3314 3315 3316 3317 3318
  /*
    Set the filler bits of the null byte, since they are
    not touched in the code below.
    
    The filler bits are the MSBs in the last null byte
  */ 
3319 3320 3321
  if (table->s->null_bytes > 0)
       buf[table->s->null_bytes - 1]|= 256U - (1U <<
					       table->s->last_null_bit_pos);
unknown's avatar
unknown committed
3322 3323 3324
  /*
    Set null flag(s)
  */
unknown's avatar
unknown committed
3325 3326
  for ( ; field;
       p_field++, value++, field= *p_field)
unknown's avatar
unknown committed
3327
  {
3328
    field->set_notnull(row_offset);       
unknown's avatar
unknown committed
3329 3330
    if ((*value).ptr)
    {
unknown's avatar
unknown committed
3331
      if (!(field->flags & BLOB_FLAG))
unknown's avatar
unknown committed
3332
      {
unknown's avatar
unknown committed
3333 3334
        int is_null= (*value).rec->isNULL();
        if (is_null)
3335
        {
unknown's avatar
unknown committed
3336 3337
          if (is_null > 0)
          {
3338
	    DBUG_PRINT("info",("[%u] NULL",
unknown's avatar
unknown committed
3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351
                               (*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)
        {
3352 3353 3354 3355 3356 3357 3358 3359
          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
3360
          if (field->pack_length() < 5)
3361 3362
          {
            DBUG_PRINT("info", ("bit field H'%.8X", 
3363
                                (*value).rec->u_32_value()));
3364 3365
            field_bit->Field_bit::store((longlong) (*value).rec->u_32_value(),
                                        FALSE);
3366 3367 3368 3369 3370 3371
          }
          else
          {
            DBUG_PRINT("info", ("bit field H'%.8X%.8X",
                                *(Uint32 *)(*value).rec->aRef(),
                                *((Uint32 *)(*value).rec->aRef()+1)));
3372 3373 3374
#ifdef WORDS_BIGENDIAN
            /* lsw is stored first */
            Uint32 *buf= (Uint32 *)(*value).rec->aRef();
unknown's avatar
unknown committed
3375
            field_bit->Field_bit::store((((longlong)*buf)
3376
                                         & 0x000000000FFFFFFFFLL)
unknown's avatar
unknown committed
3377 3378
                                        |
                                        ((((longlong)*(buf+1)) << 32)
3379
                                         & 0xFFFFFFFF00000000LL),
unknown's avatar
unknown committed
3380
                                        TRUE);
3381
#else
unknown's avatar
unknown committed
3382 3383
            field_bit->Field_bit::store((longlong)
                                        (*value).rec->u_64_value(), TRUE);
3384
#endif
3385
          }
3386 3387 3388 3389 3390
          /*
            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
3391 3392
          DBUG_PRINT("info",("[%u] SET",
                             (*value).rec->getColumn()->getColumnNo()));
3393
          DBUG_DUMP("info", field->ptr, field->pack_length());
unknown's avatar
unknown committed
3394 3395 3396 3397 3398
        }
        else
        {
          DBUG_PRINT("info",("[%u] SET",
                             (*value).rec->getColumn()->getColumnNo()));
3399
          DBUG_DUMP("info", field->ptr, field->pack_length());
3400
        }
unknown's avatar
unknown committed
3401 3402 3403
      }
      else
      {
unknown's avatar
unknown committed
3404
        NdbBlob *ndb_blob= (*value).blob;
unknown's avatar
unknown committed
3405
        uint col_no = ndb_blob->getColumn()->getColumnNo();
3406 3407
        int isNull;
        ndb_blob->getDefined(isNull);
unknown's avatar
unknown committed
3408
        if (isNull == 1)
3409
        {
unknown's avatar
unknown committed
3410
          DBUG_PRINT("info",("[%u] NULL", col_no));
unknown's avatar
unknown committed
3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422
          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;
3423
          uchar *ptr;
unknown's avatar
unknown committed
3424 3425
          field_blob->get_ptr(&ptr, row_offset);
          uint32 len= field_blob->get_length(row_offset);
unknown's avatar
unknown committed
3426 3427
          DBUG_PRINT("info",("[%u] SET ptr: 0x%lx  len: %u",
                             col_no, (long) ptr, len));
unknown's avatar
unknown committed
3428
#endif
3429
        }
unknown's avatar
unknown committed
3430 3431
      }
    }
unknown's avatar
unknown committed
3432
  }
3433
  dbug_tmp_restore_column_map(table->write_set, old_map);
unknown's avatar
unknown committed
3434 3435 3436
  DBUG_VOID_RETURN;
}

3437
void ha_ndbcluster::unpack_record(uchar *buf)
unknown's avatar
unknown committed
3438 3439
{
  ndb_unpack_record(table, m_value, 0, buf);
unknown's avatar
unknown committed
3440 3441
#ifndef DBUG_OFF
  // Read and print all values that was fetched
unknown's avatar
unknown committed
3442
  if (table_share->primary_key == MAX_KEY)
unknown's avatar
unknown committed
3443 3444
  {
    // Table with hidden primary key
unknown's avatar
unknown committed
3445
    int hidden_no= table_share->fields;
3446
    const NDBTAB *tab= m_table;
3447
    char buff[22];
unknown's avatar
unknown committed
3448
    const NDBCOL *hidden_col= tab->getColumn(hidden_no);
3449
    const NdbRecAttr* rec= m_value[hidden_no].rec;
unknown's avatar
unknown committed
3450
    DBUG_ASSERT(rec);
3451
    DBUG_PRINT("hidden", ("%d: %s \"%s\"", hidden_no,
unknown's avatar
unknown committed
3452
			  hidden_col->getName(),
3453
                          llstr(rec->u_64_value(), buff)));
unknown's avatar
unknown committed
3454 3455
  }
  //DBUG_EXECUTE("value", print_results(););
unknown's avatar
unknown committed
3456 3457 3458 3459 3460
#endif
}

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

    DBUG_EXECUTE("value", print_results(););
unknown's avatar
unknown committed
3464 3465 3466 3467 3468 3469 3470
 */

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

#ifndef DBUG_OFF
unknown's avatar
Merge  
unknown committed
3471

3472
  char buf_type[MAX_FIELD_WIDTH], buf_val[MAX_FIELD_WIDTH];
unknown's avatar
Merge  
unknown committed
3473
  String type(buf_type, sizeof(buf_type), &my_charset_bin);
3474
  String val(buf_val, sizeof(buf_val), &my_charset_bin);
unknown's avatar
unknown committed
3475
  for (uint f= 0; f < table_share->fields; f++)
unknown's avatar
unknown committed
3476
  {
unknown's avatar
Merge  
unknown committed
3477
    /* Use DBUG_PRINT since DBUG_FILE cannot be filtered out */
3478
    char buf[2000];
unknown's avatar
unknown committed
3479
    Field *field;
3480
    void* ptr;
unknown's avatar
unknown committed
3481
    NdbValue value;
unknown's avatar
unknown committed
3482

3483
    buf[0]= 0;
unknown's avatar
Merge  
unknown committed
3484
    field= table->field[f];
unknown's avatar
unknown committed
3485
    if (!(value= m_value[f]).ptr)
unknown's avatar
unknown committed
3486
    {
unknown's avatar
unknown committed
3487
      strmov(buf, "not read");
3488
      goto print_value;
unknown's avatar
unknown committed
3489
    }
3490

3491
    ptr= field->ptr;
unknown's avatar
unknown committed
3492 3493

    if (! (field->flags & BLOB_FLAG))
unknown's avatar
unknown committed
3494
    {
unknown's avatar
unknown committed
3495 3496
      if (value.rec->isNULL())
      {
unknown's avatar
unknown committed
3497
        strmov(buf, "NULL");
3498
        goto print_value;
unknown's avatar
unknown committed
3499
      }
3500 3501 3502 3503 3504
      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
3505 3506 3507
    }
    else
    {
3508
      NdbBlob *ndb_blob= value.blob;
unknown's avatar
unknown committed
3509
      bool isNull= TRUE;
unknown's avatar
unknown committed
3510
      ndb_blob->getNull(isNull);
unknown's avatar
unknown committed
3511 3512
      if (isNull)
        strmov(buf, "NULL");
unknown's avatar
unknown committed
3513
    }
unknown's avatar
Merge  
unknown committed
3514

3515
print_value:
unknown's avatar
Merge  
unknown committed
3516
    DBUG_PRINT("value", ("%u,%s: %s", f, field->field_name, buf));
unknown's avatar
unknown committed
3517 3518 3519 3520 3521 3522
  }
#endif
  DBUG_VOID_RETURN;
}


3523
int ha_ndbcluster::index_init(uint index, bool sorted)
unknown's avatar
unknown committed
3524
{
3525
  DBUG_ENTER("ha_ndbcluster::index_init");
3526 3527 3528
  DBUG_PRINT("enter", ("index: %u  sorted: %d", index, sorted));
  active_index= index;
  m_sorted= sorted;
unknown's avatar
unknown committed
3529 3530 3531 3532 3533
  /*
    Locks are are explicitly released in scan
    unless m_lock.type == TL_READ_HIGH_PRIORITY
    and no sub-sequent call to unlock_row()
  */
unknown's avatar
unknown committed
3534
  m_lock_tuple= FALSE;
3535
  DBUG_RETURN(0);
unknown's avatar
unknown committed
3536 3537 3538 3539 3540
}


int ha_ndbcluster::index_end()
{
3541
  DBUG_ENTER("ha_ndbcluster::index_end");
3542
  DBUG_RETURN(close_scan());
unknown's avatar
unknown committed
3543 3544
}

3545 3546 3547 3548 3549
/**
 * Check if key contains null
 */
static
int
3550
check_null_in_key(const KEY* key_info, const uchar *key, uint key_len)
3551 3552
{
  KEY_PART_INFO *curr_part, *end_part;
3553
  const uchar* end_ptr= key + key_len;
3554 3555 3556 3557 3558
  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
3559
    if (curr_part->null_bit && *key)
3560 3561 3562 3563 3564 3565
      return 1;

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

3567 3568
int ha_ndbcluster::index_read(uchar *buf,
                              const uchar *key, uint key_len, 
3569
                              enum ha_rkey_function find_flag)
unknown's avatar
unknown committed
3570
{
3571 3572
  key_range start_key;
  bool descending= FALSE;
3573
  DBUG_ENTER("ha_ndbcluster::index_read");
unknown's avatar
unknown committed
3574 3575 3576
  DBUG_PRINT("enter", ("active_index: %u, key_len: %u, find_flag: %d", 
                       active_index, key_len, find_flag));

3577 3578 3579
  start_key.key= key;
  start_key.length= key_len;
  start_key.flag= find_flag;
3580
  descending= FALSE;
3581 3582 3583 3584 3585 3586 3587 3588 3589 3590
  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;
  }
3591 3592
  DBUG_RETURN(read_range_first_to_buf(&start_key, 0, descending,
                                      m_sorted, buf));
unknown's avatar
unknown committed
3593 3594 3595
}


3596
int ha_ndbcluster::index_next(uchar *buf)
unknown's avatar
unknown committed
3597
{
3598
  DBUG_ENTER("ha_ndbcluster::index_next");
unknown's avatar
unknown committed
3599
  ha_statistic_increment(&SSV::ha_read_next_count);
3600
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
3601 3602 3603
}


3604
int ha_ndbcluster::index_prev(uchar *buf)
unknown's avatar
unknown committed
3605
{
3606
  DBUG_ENTER("ha_ndbcluster::index_prev");
unknown's avatar
unknown committed
3607
  ha_statistic_increment(&SSV::ha_read_prev_count);
3608
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
3609 3610 3611
}


3612
int ha_ndbcluster::index_first(uchar *buf)
unknown's avatar
unknown committed
3613
{
3614
  DBUG_ENTER("ha_ndbcluster::index_first");
unknown's avatar
unknown committed
3615
  ha_statistic_increment(&SSV::ha_read_first_count);
unknown's avatar
unknown committed
3616 3617 3618
  // Start the ordered index scan and fetch the first row

  // Only HA_READ_ORDER indexes get called by index_first
3619
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, FALSE, buf, NULL));
unknown's avatar
unknown committed
3620 3621 3622
}


3623
int ha_ndbcluster::index_last(uchar *buf)
unknown's avatar
unknown committed
3624
{
3625
  DBUG_ENTER("ha_ndbcluster::index_last");
unknown's avatar
unknown committed
3626
  ha_statistic_increment(&SSV::ha_read_last_count);
3627
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, TRUE, buf, NULL));
unknown's avatar
unknown committed
3628 3629
}

3630
int ha_ndbcluster::index_read_last(uchar * buf, const uchar * key, uint key_len)
3631 3632 3633 3634
{
  DBUG_ENTER("ha_ndbcluster::index_read_last");
  DBUG_RETURN(index_read(buf, key, key_len, HA_READ_PREFIX_LAST));
}
unknown's avatar
unknown committed
3635

3636
int ha_ndbcluster::read_range_first_to_buf(const key_range *start_key,
3637
                                           const key_range *end_key,
3638
                                           bool desc, bool sorted,
3639
                                           uchar* buf)
3640
{
3641 3642 3643 3644
  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; 
3645
  DBUG_ENTER("ha_ndbcluster::read_range_first_to_buf");
3646
  DBUG_PRINT("info", ("desc: %d, sorted: %d", desc, sorted));
3647

3648 3649 3650
  if (m_use_partition_function)
  {
    get_partition_set(table, buf, active_index, start_key, &part_spec);
unknown's avatar
unknown committed
3651
    DBUG_PRINT("info", ("part_spec.start_part: %u  part_spec.end_part: %u",
3652 3653 3654 3655 3656 3657 3658
                        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.
    */
3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672
    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;
    }
  }
3673

3674 3675
  m_write_op= FALSE;
  switch (type){
3676
  case PRIMARY_KEY_ORDERED_INDEX:
3677
  case PRIMARY_KEY_INDEX:
3678
    if (start_key && 
3679 3680
        start_key->length == key_info->key_length &&
        start_key->flag == HA_READ_KEY_EXACT)
3681
    {
unknown's avatar
unknown committed
3682
      if (m_active_cursor && (error= close_scan()))
3683
        DBUG_RETURN(error);
3684 3685 3686
      error= pk_read(start_key->key, start_key->length, buf,
		     part_spec.start_part);
      DBUG_RETURN(error == HA_ERR_KEY_NOT_FOUND ? HA_ERR_END_OF_FILE : error);
3687
    }
3688
    break;
3689
  case UNIQUE_ORDERED_INDEX:
3690
  case UNIQUE_INDEX:
3691
    if (start_key && start_key->length == key_info->key_length &&
3692 3693
        start_key->flag == HA_READ_KEY_EXACT && 
        !check_null_in_key(key_info, start_key->key, start_key->length))
3694
    {
unknown's avatar
unknown committed
3695
      if (m_active_cursor && (error= close_scan()))
3696
        DBUG_RETURN(error);
3697 3698 3699

      error= unique_index_read(start_key->key, start_key->length, buf);
      DBUG_RETURN(error == HA_ERR_KEY_NOT_FOUND ? HA_ERR_END_OF_FILE : error);
3700
    }
3701 3702 3703 3704 3705
    else if (type == UNIQUE_INDEX)
      DBUG_RETURN(unique_index_scan(key_info, 
				    start_key->key, 
				    start_key->length, 
				    buf));
3706 3707 3708 3709
    break;
  default:
    break;
  }
3710
  // Start the ordered index scan and fetch the first row
3711 3712
  DBUG_RETURN(ordered_index_scan(start_key, end_key, sorted, desc, buf,
                                 &part_spec));
3713 3714
}

unknown's avatar
unknown committed
3715
int ha_ndbcluster::read_range_first(const key_range *start_key,
3716 3717
                                    const key_range *end_key,
                                    bool eq_r, bool sorted)
unknown's avatar
unknown committed
3718
{
3719
  uchar* buf= table->record[0];
unknown's avatar
unknown committed
3720
  DBUG_ENTER("ha_ndbcluster::read_range_first");
3721 3722
  DBUG_RETURN(read_range_first_to_buf(start_key, end_key, FALSE,
                                      sorted, buf));
unknown's avatar
unknown committed
3723 3724
}

3725
int ha_ndbcluster::read_range_next()
3726 3727 3728 3729 3730 3731
{
  DBUG_ENTER("ha_ndbcluster::read_range_next");
  DBUG_RETURN(next_result(table->record[0]));
}


unknown's avatar
unknown committed
3732 3733
int ha_ndbcluster::rnd_init(bool scan)
{
3734
  NdbScanOperation *cursor= m_active_cursor;
unknown's avatar
unknown committed
3735 3736
  DBUG_ENTER("rnd_init");
  DBUG_PRINT("enter", ("scan: %d", scan));
3737
  // Check if scan is to be restarted
unknown's avatar
unknown committed
3738 3739 3740 3741
  if (cursor)
  {
    if (!scan)
      DBUG_RETURN(1);
unknown's avatar
unknown committed
3742
    if (cursor->restart(m_force_send) != 0)
3743 3744 3745 3746
    {
      DBUG_ASSERT(0);
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
3747
  }
unknown's avatar
unknown committed
3748
  index_init(table_share->primary_key, 0);
unknown's avatar
unknown committed
3749 3750 3751
  DBUG_RETURN(0);
}

3752 3753
int ha_ndbcluster::close_scan()
{
3754
  NdbTransaction *trans= m_active_trans;
3755 3756
  DBUG_ENTER("close_scan");

unknown's avatar
unknown committed
3757 3758
  m_multi_cursor= 0;
  if (!m_active_cursor && !m_multi_cursor)
3759
    DBUG_RETURN(0);
3760

unknown's avatar
unknown committed
3761
  NdbScanOperation *cursor= m_active_cursor ? m_active_cursor : m_multi_cursor;
3762

3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774
  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"));
      
3775
      if (!(op= cursor->lockCurrentTuple()))
3776
      {
unknown's avatar
unknown committed
3777
	m_lock_tuple= FALSE;
3778 3779 3780 3781
	ERR_RETURN(trans->getNdbError());
      }
      m_ops_pending++;      
  }
unknown's avatar
unknown committed
3782
  m_lock_tuple= FALSE;
3783
  if (m_ops_pending)
unknown's avatar
unknown committed
3784 3785 3786 3787 3788
  {
    /*
      Take over any pending transactions to the 
      deleteing/updating transaction before closing the scan    
    */
unknown's avatar
unknown committed
3789
    DBUG_PRINT("info", ("ops_pending: %ld", (long) m_ops_pending));    
unknown's avatar
unknown committed
3790
    if (execute_no_commit(this,trans,FALSE) != 0) {
3791
      no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
3792
      DBUG_RETURN(ndb_err(trans));
3793
    }
3794
    m_ops_pending= 0;
unknown's avatar
unknown committed
3795 3796
  }
  
unknown's avatar
unknown committed
3797
  cursor->close(m_force_send, TRUE);
unknown's avatar
unknown committed
3798
  m_active_cursor= m_multi_cursor= NULL;
unknown's avatar
unknown committed
3799
  DBUG_RETURN(0);
3800
}
unknown's avatar
unknown committed
3801 3802 3803 3804

int ha_ndbcluster::rnd_end()
{
  DBUG_ENTER("rnd_end");
3805
  DBUG_RETURN(close_scan());
unknown's avatar
unknown committed
3806 3807 3808
}


3809
int ha_ndbcluster::rnd_next(uchar *buf)
unknown's avatar
unknown committed
3810 3811
{
  DBUG_ENTER("rnd_next");
unknown's avatar
unknown committed
3812
  ha_statistic_increment(&SSV::ha_read_rnd_next_count);
3813

unknown's avatar
unknown committed
3814
  if (!m_active_cursor)
3815 3816
    DBUG_RETURN(full_table_scan(buf));
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
3817 3818 3819 3820 3821 3822 3823 3824 3825 3826
}


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

3827
int ha_ndbcluster::rnd_pos(uchar *buf, uchar *pos)
unknown's avatar
unknown committed
3828 3829
{
  DBUG_ENTER("rnd_pos");
unknown's avatar
unknown committed
3830
  ha_statistic_increment(&SSV::ha_read_rnd_count);
unknown's avatar
unknown committed
3831 3832
  // The primary key for the record is stored in pos
  // Perform a pk_read using primary key "index"
3833 3834
  {
    part_id_range part_spec;
3835
    uint key_length= ref_length;
3836 3837
    if (m_use_partition_function)
    {
3838 3839 3840 3841 3842 3843
      if (table_share->primary_key == MAX_KEY)
      {
        /*
          The partition id has been fetched from ndb
          and has been stored directly after the hidden key
        */
3844
        DBUG_DUMP("key+part", pos, key_length);
3845
        key_length= ref_length - sizeof(m_part_id);
3846
        part_spec.start_part= part_spec.end_part= *(uint32 *)(pos + key_length);
3847 3848 3849 3850
      }
      else
      {
        key_range key_spec;
3851
        KEY *key_info= table->key_info + table_share->primary_key;
3852 3853 3854 3855 3856 3857 3858 3859
        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));
3860
    }
3861
    DBUG_DUMP("key", pos, key_length);
3862
    DBUG_RETURN(pk_read(pos, key_length, buf, part_spec.start_part));
3863
  }
unknown's avatar
unknown committed
3864 3865 3866 3867 3868 3869 3870 3871 3872
}


/*
  Store the primary key of this record in ref 
  variable, so that the row can be retrieved again later
  using "reference" in rnd_pos
*/

3873
void ha_ndbcluster::position(const uchar *record)
unknown's avatar
unknown committed
3874 3875 3876 3877
{
  KEY *key_info;
  KEY_PART_INFO *key_part;
  KEY_PART_INFO *end;
3878
  uchar *buff;
3879 3880
  uint key_length;

unknown's avatar
unknown committed
3881 3882
  DBUG_ENTER("position");

unknown's avatar
unknown committed
3883
  if (table_share->primary_key != MAX_KEY) 
unknown's avatar
unknown committed
3884
  {
3885
    key_length= ref_length;
unknown's avatar
unknown committed
3886
    key_info= table->key_info + table_share->primary_key;
unknown's avatar
unknown committed
3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901
    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;
      }
3902 3903

      size_t len = key_part->length;
3904
      const uchar * ptr = record + key_part->offset;
3905
      Field *field = key_part->field;
3906
      if (field->type() ==  MYSQL_TYPE_VARCHAR)
3907
      {
3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921
        if (((Field_varstring*)field)->length_bytes == 1)
        {
          /**
           * Keys always use 2 bytes length
           */
          buff[0] = ptr[0];
          buff[1] = 0;
          memcpy(buff+2, ptr + 1, len);
        }
        else
        {
          memcpy(buff, ptr, len + 2);
        }
        len += 2;
3922 3923 3924
      }
      else
      {
3925
        memcpy(buff, ptr, len);
3926 3927
      }
      buff += len;
unknown's avatar
unknown committed
3928 3929 3930 3931 3932 3933
    }
  } 
  else 
  {
    // No primary key, get hidden key
    DBUG_PRINT("info", ("Getting hidden key"));
3934 3935 3936
    // If table has user defined partition save the partition id as well
    if(m_use_partition_function)
    {
unknown's avatar
unknown committed
3937
      DBUG_PRINT("info", ("Saving partition id %u", m_part_id));
3938 3939 3940
      key_length= ref_length - sizeof(m_part_id);
      memcpy(ref+key_length, (void *)&m_part_id, sizeof(m_part_id));
    }
3941 3942
    else
      key_length= ref_length;
3943
#ifndef DBUG_OFF
3944
    int hidden_no= table->s->fields;
3945
    const NDBTAB *tab= m_table;  
unknown's avatar
unknown committed
3946 3947 3948
    const NDBCOL *hidden_col= tab->getColumn(hidden_no);
    DBUG_ASSERT(hidden_col->getPrimaryKey() && 
                hidden_col->getAutoIncrement() &&
3949
                key_length == NDB_HIDDEN_PRIMARY_KEY_LENGTH);
3950
#endif
3951
    memcpy(ref, m_ref, key_length);
unknown's avatar
unknown committed
3952
  }
unknown's avatar
unknown committed
3953 3954
#ifndef DBUG_OFF
  if (table_share->primary_key == MAX_KEY && m_use_partition_function) 
3955
    DBUG_DUMP("key+part", ref, key_length+sizeof(m_part_id));
unknown's avatar
unknown committed
3956
#endif
3957
  DBUG_DUMP("ref", ref, key_length);
unknown's avatar
unknown committed
3958 3959 3960 3961
  DBUG_VOID_RETURN;
}


3962
int ha_ndbcluster::info(uint flag)
unknown's avatar
unknown committed
3963
{
3964
  int result= 0;
unknown's avatar
unknown committed
3965 3966 3967 3968 3969 3970 3971 3972 3973 3974
  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)
3975
  {
unknown's avatar
unknown committed
3976
    DBUG_PRINT("info", ("HA_STATUS_VARIABLE"));
3977 3978
    if (m_table_info)
    {
3979
      if (m_ha_not_exact_count)
3980
        stats.records= 100;
3981
      else
3982
	result= records_update();
3983 3984 3985
    }
    else
    {
3986
      if ((my_errno= check_ndb_connection()))
3987
        DBUG_RETURN(my_errno);
3988
      Ndb *ndb= get_ndb();
3989
      ndb->setDatabaseName(m_dbname);
3990
      struct Ndb_statistics stat;
3991 3992 3993 3994
      if (ndb->setDatabaseName(m_dbname))
      {
        DBUG_RETURN(my_errno= HA_ERR_OUT_OF_MEM);
      }
3995
      if (current_thd->variables.ndb_use_exact_count &&
unknown's avatar
unknown committed
3996
          (result= ndb_get_table_statistics(this, TRUE, ndb, m_table, &stat))
unknown's avatar
unknown committed
3997
          == 0)
3998
      {
3999 4000 4001
        stats.mean_rec_length= stat.row_size;
        stats.data_file_length= stat.fragment_memory;
        stats.records= stat.row_count;
4002 4003 4004
      }
      else
      {
4005 4006
        stats.mean_rec_length= 0;
        stats.records= 100;
4007
      }
4008
    }
4009
  }
unknown's avatar
unknown committed
4010 4011 4012 4013 4014
  if (flag & HA_STATUS_CONST)
  {
    DBUG_PRINT("info", ("HA_STATUS_CONST"));
    set_rec_per_key();
  }
unknown's avatar
unknown committed
4015
  if (flag & HA_STATUS_ERRKEY)
4016
  {
unknown's avatar
unknown committed
4017
    DBUG_PRINT("info", ("HA_STATUS_ERRKEY"));
4018
    errkey= m_dupkey;
4019
  }
unknown's avatar
unknown committed
4020
  if (flag & HA_STATUS_AUTO)
4021
  {
unknown's avatar
unknown committed
4022
    DBUG_PRINT("info", ("HA_STATUS_AUTO"));
4023
    if (m_table && table->found_next_number_field)
4024
    {
4025 4026
      if ((my_errno= check_ndb_connection()))
        DBUG_RETURN(my_errno);
4027
      Ndb *ndb= get_ndb();
4028
      Ndb_tuple_id_range_guard g(m_share);
4029
      
4030
      Uint64 auto_increment_value64;
4031
      if (ndb->readAutoIncrementValue(m_table, g.range,
4032
                                      auto_increment_value64) == -1)
4033 4034 4035 4036
      {
        const NdbError err= ndb->getNdbError();
        sql_print_error("Error %lu in readAutoIncrementValue(): %s",
                        (ulong) err.code, err.message);
4037
        stats.auto_increment_value= ~(ulonglong)0;
4038
      }
4039
      else
4040
        stats.auto_increment_value= (ulonglong)auto_increment_value64;
4041 4042
    }
  }
4043 4044 4045 4046 4047

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

  DBUG_RETURN(result);
unknown's avatar
unknown committed
4048 4049
}

4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063

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
4064 4065 4066 4067 4068 4069
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"));
4070 4071
    DBUG_PRINT("info", ("Ignoring duplicate key"));
    m_ignore_dup_key= TRUE;
unknown's avatar
unknown committed
4072 4073 4074
    break;
  case HA_EXTRA_NO_IGNORE_DUP_KEY:
    DBUG_PRINT("info", ("HA_EXTRA_NO_IGNORE_DUP_KEY"));
4075
    m_ignore_dup_key= FALSE;
unknown's avatar
unknown committed
4076
    break;
unknown's avatar
unknown committed
4077 4078 4079 4080 4081 4082 4083 4084 4085 4086
  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;
4087 4088
  case HA_EXTRA_WRITE_CAN_REPLACE:
    DBUG_PRINT("info", ("HA_EXTRA_WRITE_CAN_REPLACE"));
4089 4090
    if (!m_has_unique_index ||
        current_thd->slave_thread) /* always set if slave, quick fix for bug 27378 */
4091 4092 4093 4094 4095 4096 4097 4098 4099 4100
    {
      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;
4101 4102 4103 4104 4105 4106 4107
  case HA_EXTRA_DELETE_CANNOT_BATCH:
    DBUG_PRINT("info", ("HA_EXTRA_DELETE_CANNOT_BATCH"));
    m_delete_cannot_batch= TRUE;
    break;
  case HA_EXTRA_UPDATE_CANNOT_BATCH:
    DBUG_PRINT("info", ("HA_EXTRA_UPDATE_CANNOT_BATCH"));
    m_update_cannot_batch= TRUE;
4108
    break;
unknown's avatar
unknown committed
4109 4110
  default:
    break;
unknown's avatar
unknown committed
4111 4112 4113 4114 4115
  }
  
  DBUG_RETURN(0);
}

4116 4117 4118 4119

int ha_ndbcluster::reset()
{
  DBUG_ENTER("ha_ndbcluster::reset");
unknown's avatar
unknown committed
4120 4121 4122 4123 4124
  if (m_cond)
  {
    m_cond->cond_clear();
  }

4125 4126 4127 4128 4129 4130 4131
  /*
    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);
4132 4133 4134 4135

  /* reset flags set by extra calls */
  m_ignore_dup_key= FALSE;
  m_use_write= FALSE;
4136
  m_ignore_no_key= FALSE;
4137 4138
  m_delete_cannot_batch= FALSE;
  m_update_cannot_batch= FALSE;
4139

4140 4141 4142 4143
  DBUG_RETURN(0);
}


4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156
/* 
   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;
4157
  const NDBTAB *tab= m_table;    
4158 4159

  DBUG_ENTER("start_bulk_insert");
unknown's avatar
unknown committed
4160
  DBUG_PRINT("enter", ("rows: %d", (int)rows));
4161
  
4162
  m_rows_inserted= (ha_rows) 0;
4163
  if (!m_use_write && m_ignore_dup_key)
4164 4165 4166
  {
    /*
      compare if expression with that in write_row
4167
      we have a situation where peek_indexed_rows() will be called
4168 4169 4170 4171 4172 4173 4174 4175
      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;
  }
4176
  if (rows == (ha_rows) 0)
unknown's avatar
unknown committed
4177
  {
4178 4179
    /* We don't know how many will be inserted, guess */
    m_rows_to_insert= m_autoincrement_prefetch;
unknown's avatar
unknown committed
4180
  }
4181 4182
  else
    m_rows_to_insert= rows; 
4183 4184 4185 4186 4187 4188 4189 4190

  /* 
    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.   
  */
4191
  const int bytesperbatch= 8192;
4192
  bytes= 12 + tab->getRowSizeInBytes() + 4 * tab->getNoOfColumns();
4193
  batch= bytesperbatch/bytes;
4194 4195
  batch= batch == 0 ? 1 : batch;
  DBUG_PRINT("info", ("batch: %d, bytes: %d", batch, bytes));
4196
  m_bulk_insert_rows= batch;
4197 4198 4199 4200 4201 4202 4203 4204 4205

  DBUG_VOID_RETURN;
}

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

4208
  DBUG_ENTER("end_bulk_insert");
4209
  // Check if last inserts need to be flushed
4210
  if (m_bulk_insert_not_flushed)
4211
  {
4212
    NdbTransaction *trans= m_active_trans;
4213 4214
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
unknown's avatar
unknown committed
4215
                        "rows_inserted: %d  bulk_insert_rows: %d", 
4216
                        (int) m_rows_inserted, (int) m_bulk_insert_rows)); 
4217
    m_bulk_insert_not_flushed= FALSE;
4218 4219
    if (m_transaction_on)
    {
unknown's avatar
unknown committed
4220
      if (execute_no_commit(this, trans,FALSE) != 0)
4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234
      {
        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
      {
unknown's avatar
unknown committed
4235
        IF_DBUG(int res=) trans->restart();
4236 4237
        DBUG_ASSERT(res == 0);
      }
4238
    }
4239 4240
  }

4241 4242
  m_rows_inserted= (ha_rows) 0;
  m_rows_to_insert= (ha_rows) 1;
4243
  DBUG_RETURN(error);
4244 4245
}

unknown's avatar
unknown committed
4246 4247 4248 4249

int ha_ndbcluster::extra_opt(enum ha_extra_function operation, ulong cache_size)
{
  DBUG_ENTER("extra_opt");
unknown's avatar
unknown committed
4250
  DBUG_PRINT("enter", ("cache_size: %lu", cache_size));
unknown's avatar
unknown committed
4251 4252 4253
  DBUG_RETURN(extra(operation));
}

unknown's avatar
unknown committed
4254 4255 4256 4257
static const char *ha_ndbcluster_exts[] = {
 ha_ndb_ext,
 NullS
};
unknown's avatar
unknown committed
4258

4259
const char** ha_ndbcluster::bas_ext() const
unknown's avatar
unknown committed
4260 4261 4262
{
  return ha_ndbcluster_exts;
}
unknown's avatar
unknown committed
4263 4264 4265 4266 4267 4268 4269 4270 4271

/*
  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
4272
  DBUG_ENTER("ha_ndbcluster::scan_time()");
4273
  double res= rows2double(stats.records*1000);
unknown's avatar
unknown committed
4274
  DBUG_PRINT("exit", ("table: %s value: %f", 
4275
                      m_tabname, res));
unknown's avatar
unknown committed
4276
  DBUG_RETURN(res);
unknown's avatar
unknown committed
4277 4278
}

unknown's avatar
unknown committed
4279 4280 4281 4282 4283 4284 4285
/*
  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
4286 4287 4288 4289 4290 4291 4292 4293

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
4294

unknown's avatar
unknown committed
4295 4296 4297
    /* If we are not doing a LOCK TABLE, then allow multiple
       writers */
    
4298 4299 4300
    /* Since NDB does not currently have table locks
       this is treated as a ordinary lock */

4301
    if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
unknown's avatar
unknown committed
4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316
         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;
4317 4318

  DBUG_PRINT("exit", ("lock_type: %d", lock_type));
unknown's avatar
unknown committed
4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340
  
  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
4341
  for the statement, this will be stored in thd_ndb.stmt.
unknown's avatar
unknown committed
4342
  If not, we have to start a master transaction if there doesn't exist
4343
  one from before, this will be stored in thd_ndb.all
unknown's avatar
unknown committed
4344 4345 4346
 
  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  
4347
  If we are locking the table then:
4348
  - save the NdbDictionary::Table for easy access
4349 4350
  - save reference to table statistics
  - refresh list of the indexes for the table if needed (if altered)
unknown's avatar
unknown committed
4351 4352
 */

4353
#ifdef HAVE_NDB_BINLOG
4354
extern Master_info *active_mi;
4355 4356 4357 4358 4359 4360
static int ndbcluster_update_apply_status(THD *thd, int do_update)
{
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  Ndb *ndb= thd_ndb->ndb;
  NDBDICT *dict= ndb->getDictionary();
  const NDBTAB *ndbtab;
4361
  NdbTransaction *trans= thd_ndb->trans;
4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377
  ndb->setDatabaseName(NDB_REP_DB);
  Ndb_table_guard ndbtab_g(dict, NDB_APPLY_TABLE);
  if (!(ndbtab= ndbtab_g.get_table()))
  {
    return -1;
  }
  NdbOperation *op= 0;
  int r= 0;
  r|= (op= trans->getNdbOperation(ndbtab)) == 0;
  DBUG_ASSERT(r == 0);
  if (do_update)
    r|= op->updateTuple();
  else
    r|= op->writeTuple();
  DBUG_ASSERT(r == 0);
  // server_id
4378
  r|= op->equal(0u, (Uint32)thd->server_id);
4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404
  DBUG_ASSERT(r == 0);
  if (!do_update)
  {
    // epoch
    r|= op->setValue(1u, (Uint64)0);
    DBUG_ASSERT(r == 0);
  }
  // log_name
  char tmp_buf[FN_REFLEN];
  ndb_pack_varchar(ndbtab->getColumn(2u), tmp_buf,
                   active_mi->rli.group_master_log_name,
                   strlen(active_mi->rli.group_master_log_name));
  r|= op->setValue(2u, tmp_buf);
  DBUG_ASSERT(r == 0);
  // start_pos
  r|= op->setValue(3u, (Uint64)active_mi->rli.group_master_log_pos);
  DBUG_ASSERT(r == 0);
  // end_pos
  r|= op->setValue(4u, (Uint64)active_mi->rli.group_master_log_pos + 
                   ((Uint64)active_mi->rli.future_event_relay_log_pos -
                    (Uint64)active_mi->rli.group_relay_log_pos));
  DBUG_ASSERT(r == 0);
  return 0;
}
#endif /* HAVE_NDB_BINLOG */

4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505
void ha_ndbcluster::transaction_checks(THD *thd)
{
  if (thd->lex->sql_command == SQLCOM_LOAD)
  {
    m_transaction_on= FALSE;
    /* Would be simpler if has_transactions() didn't always say "yes" */
    thd->transaction.all.modified_non_trans_table=
      thd->transaction.stmt.modified_non_trans_table= TRUE;
  }
  else if (!thd->transaction.on)
    m_transaction_on= FALSE;
  else
    m_transaction_on= thd->variables.ndb_use_transactions;
}

int ha_ndbcluster::start_statement(THD *thd,
                                   Thd_ndb *thd_ndb,
                                   Ndb *ndb)
{
  DBUG_ENTER("ha_ndbcluster::start_statement");
  PRINT_OPTION_FLAGS(thd);
  
  trans_register_ha(thd, FALSE, ndbcluster_hton);
  if (!thd_ndb->trans)
  {
    if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
      trans_register_ha(thd, TRUE, ndbcluster_hton);
    DBUG_PRINT("trans",("Starting transaction"));      
    thd_ndb->trans= ndb->startTransaction();
    if (thd_ndb->trans == NULL)
      ERR_RETURN(ndb->getNdbError());
    thd_ndb->init_open_tables();
    thd_ndb->query_state&= NDB_QUERY_NORMAL;
    thd_ndb->trans_options= 0;
    thd_ndb->m_slow_path= FALSE;
    if (!(thd->options & OPTION_BIN_LOG) ||
        thd->variables.binlog_format == BINLOG_FORMAT_STMT)
    {
      thd_ndb->trans_options|= TNTO_NO_LOGGING;
      thd_ndb->m_slow_path= TRUE;
    }
    else if (thd->slave_thread)
      thd_ndb->m_slow_path= TRUE;
  }
  /*
    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))
  {
    //lockThisTable();
    DBUG_PRINT("info", ("Locking the table..." ));
  }
  DBUG_RETURN(0);
}

int ha_ndbcluster::init_handler_for_statement(THD *thd, Thd_ndb *thd_ndb)
{
  /*
    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. 
   */

  DBUG_ENTER("ha_ndbcluster::init_handler_for_statement");
  // 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;
  m_autoincrement_prefetch= 
    (ha_rows) thd->variables.ndb_autoincrement_prefetch_sz;

  m_active_trans= thd_ndb->trans;
  DBUG_ASSERT(m_active_trans);
  // Start of transaction
  m_rows_changed= 0;
  m_ops_pending= 0;
  m_slow_path= thd_ndb->m_slow_path;
#ifdef HAVE_NDB_BINLOG
  if (unlikely(m_slow_path))
  {
    if (m_share == ndb_apply_status_share && thd->slave_thread)
        thd_ndb->trans_options|= TNTO_INJECTED_APPLY_STATUS;
  }
#endif
  // TODO remove double pointers...
  if (!(m_thd_ndb_share= thd_ndb->get_open_table(thd, m_table)))
  {
    DBUG_RETURN(1);
  }
  m_table_info= &m_thd_ndb_share->stat;
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
4506 4507 4508 4509
int ha_ndbcluster::external_lock(THD *thd, int lock_type)
{
  int error=0;
  DBUG_ENTER("external_lock");
4510

unknown's avatar
unknown committed
4511 4512 4513 4514
  /*
    Check that this handler instance has a connection
    set up to the Ndb object of thd
   */
4515
  if (check_ndb_connection(thd))
unknown's avatar
unknown committed
4516
    DBUG_RETURN(1);
4517

4518
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
4519
  Ndb *ndb= thd_ndb->ndb;
4520

4521
  DBUG_PRINT("enter", ("this: 0x%lx  thd: 0x%lx  thd_ndb: %lx  "
unknown's avatar
unknown committed
4522
                       "thd_ndb->lock_count: %d",
4523 4524
                       (long) this, (long) thd, (long) thd_ndb,
                       thd_ndb->lock_count));
4525

unknown's avatar
unknown committed
4526 4527
  if (lock_type != F_UNLCK)
  {
4528
    DBUG_PRINT("info", ("lock_type != F_UNLCK"));
4529
    transaction_checks(thd);
4530
    if (!thd_ndb->lock_count++)
unknown's avatar
unknown committed
4531
    {
4532 4533
      if ((error= start_statement(thd, thd_ndb, ndb)))
        goto error;
4534
    }
4535 4536 4537
    if ((error= init_handler_for_statement(thd, thd_ndb)))
      goto error;
    DBUG_RETURN(0);
4538 4539
  }
  else
unknown's avatar
unknown committed
4540
  {
4541
    DBUG_PRINT("info", ("lock_type == F_UNLCK"));
4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559

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

4560
    if (!--thd_ndb->lock_count)
unknown's avatar
unknown committed
4561 4562 4563 4564
    {
      DBUG_PRINT("trans", ("Last external_lock"));
      PRINT_OPTION_FLAGS(thd);

4565
      if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
unknown's avatar
unknown committed
4566
      {
4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577
        if (thd_ndb->trans)
        {
          /*
            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"));
          ndb->closeTransaction(thd_ndb->trans);
          thd_ndb->trans= NULL;
        }
unknown's avatar
unknown committed
4578 4579
      }
    }
unknown's avatar
unknown committed
4580
    m_table_info= NULL;
4581

4582 4583 4584 4585 4586 4587 4588 4589 4590
    /*
      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;    

4591 4592
    if (m_active_cursor)
      DBUG_PRINT("warning", ("m_active_cursor != NULL"));
4593 4594
    m_active_cursor= NULL;

unknown's avatar
unknown committed
4595 4596 4597 4598
    if (m_multi_cursor)
      DBUG_PRINT("warning", ("m_multi_cursor != NULL"));
    m_multi_cursor= NULL;
    
4599
    if (m_blobs_pending)
4600
      DBUG_PRINT("warning", ("blobs_pending != 0"));
4601
    m_blobs_pending= 0;
4602
    
4603
    if (m_ops_pending)
4604
      DBUG_PRINT("warning", ("ops_pending != 0L"));
4605
    m_ops_pending= 0;
4606
    DBUG_RETURN(0);
unknown's avatar
unknown committed
4607
  }
4608 4609
error:
  thd_ndb->lock_count--;
unknown's avatar
unknown committed
4610 4611 4612
  DBUG_RETURN(error);
}

unknown's avatar
unknown committed
4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624
/*
  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"));
unknown's avatar
unknown committed
4625
  m_lock_tuple= FALSE;
unknown's avatar
unknown committed
4626 4627 4628
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
4629
/*
4630 4631 4632 4633 4634
  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
4635 4636
*/

unknown's avatar
unknown committed
4637
int ha_ndbcluster::start_stmt(THD *thd, thr_lock_type lock_type)
unknown's avatar
unknown committed
4638 4639 4640 4641
{
  int error=0;
  DBUG_ENTER("start_stmt");

4642
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
4643 4644 4645
  transaction_checks(thd);
  if (!thd_ndb->start_stmt_count++)
  {
4646
    Ndb *ndb= thd_ndb->ndb;
4647 4648
    if ((error= start_statement(thd, thd_ndb, ndb)))
      goto error;
unknown's avatar
unknown committed
4649
  }
4650 4651 4652 4653 4654
  if ((error= init_handler_for_statement(thd, thd_ndb)))
    goto error;
  DBUG_RETURN(0);
error:
  thd_ndb->start_stmt_count--;
unknown's avatar
unknown committed
4655 4656 4657 4658 4659
  DBUG_RETURN(error);
}


/*
4660
  Commit a transaction started in NDB
unknown's avatar
unknown committed
4661 4662
 */

4663
static int ndbcluster_commit(handlerton *hton, THD *thd, bool all)
unknown's avatar
unknown committed
4664 4665
{
  int res= 0;
4666 4667
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  Ndb *ndb= thd_ndb->ndb;
4668
  NdbTransaction *trans= thd_ndb->trans;
unknown's avatar
unknown committed
4669 4670

  DBUG_ENTER("ndbcluster_commit");
4671
  DBUG_ASSERT(ndb);
unknown's avatar
unknown committed
4672 4673
  PRINT_OPTION_FLAGS(thd);
  DBUG_PRINT("enter", ("Commit %s", (all ? "all" : "stmt")));
4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688
  thd_ndb->start_stmt_count= 0;
  if (trans == NULL || (!all &&
      thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
  {
    /*
      An odditity in the handler interface is that commit on handlerton
      is called to indicate end of statement only in cases where 
      autocommit isn't used and the all flag isn't set.
   
      We also leave quickly when a transaction haven't even been started,
      in this case we are safe that no clean up is needed. In this case
      the MySQL Server could handle the query without contacting the
      NDB kernel.
    */
    DBUG_PRINT("info", ("Commit before start or end-of-statement only"));
4689
    DBUG_RETURN(0);
4690
  }
unknown's avatar
unknown committed
4691

4692
#ifdef HAVE_NDB_BINLOG
4693 4694 4695 4696 4697 4698
  if (unlikely(thd_ndb->m_slow_path))
  {
    if (thd->slave_thread)
      ndbcluster_update_apply_status
        (thd, thd_ndb->trans_options & TNTO_INJECTED_APPLY_STATUS);
  }
4699 4700
#endif /* HAVE_NDB_BINLOG */

4701
  if (execute_commit(thd,trans) != 0)
unknown's avatar
unknown committed
4702 4703
  {
    const NdbError err= trans->getNdbError();
4704
    const NdbOperation *error_op= trans->getNdbErrorOperation();
unknown's avatar
unknown committed
4705
    set_ndb_err(thd, err);
unknown's avatar
unknown committed
4706
    res= ndb_to_mysql_error(&err);
4707
    if (res != -1)
4708
      ndbcluster_print_error(res, error_op);
unknown's avatar
unknown committed
4709
  }
4710
  ndb->closeTransaction(trans);
4711
  thd_ndb->trans= NULL;
4712 4713 4714 4715 4716 4717 4718

  /* 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
4719 4720
    DBUG_PRINT("info", ("Invalidate commit_count for %s, share->commit_count: %lu",
                        share->table_name, (ulong) share->commit_count));
4721 4722 4723 4724 4725 4726
    share->commit_count= 0;
    share->commit_count_lock++;
    pthread_mutex_unlock(&share->mutex);
  }
  thd_ndb->changed_tables.empty();

unknown's avatar
unknown committed
4727 4728 4729 4730 4731 4732 4733 4734
  DBUG_RETURN(res);
}


/*
  Rollback a transaction started in NDB
 */

4735
static int ndbcluster_rollback(handlerton *hton, THD *thd, bool all)
unknown's avatar
unknown committed
4736 4737
{
  int res= 0;
4738 4739
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  Ndb *ndb= thd_ndb->ndb;
4740
  NdbTransaction *trans= thd_ndb->trans;
unknown's avatar
unknown committed
4741 4742

  DBUG_ENTER("ndbcluster_rollback");
4743 4744
  DBUG_ASSERT(ndb);
  thd_ndb->start_stmt_count= 0;
unknown's avatar
unknown committed
4745 4746
  if (trans == NULL || (!all &&
      thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
4747
  {
unknown's avatar
unknown committed
4748 4749
    /* Ignore end-of-statement until real rollback or commit is called */
    DBUG_PRINT("info", ("Rollback before start or end-of-statement only"));
4750 4751
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
4752

4753
  if (trans->execute(NdbTransaction::Rollback) != 0)
unknown's avatar
unknown committed
4754 4755
  {
    const NdbError err= trans->getNdbError();
4756
    const NdbOperation *error_op= trans->getNdbErrorOperation();
unknown's avatar
unknown committed
4757
    set_ndb_err(thd, err);
unknown's avatar
unknown committed
4758
    res= ndb_to_mysql_error(&err);
4759 4760
    if (res != -1) 
      ndbcluster_print_error(res, error_op);
unknown's avatar
unknown committed
4761 4762
  }
  ndb->closeTransaction(trans);
4763
  thd_ndb->trans= NULL;
4764

4765 4766 4767
  /* Clear list of tables changed by transaction */
  thd_ndb->changed_tables.empty();

4768
  DBUG_RETURN(res);
unknown's avatar
unknown committed
4769 4770 4771 4772
}


/*
unknown's avatar
unknown committed
4773 4774 4775
  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
4776 4777 4778

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

unknown's avatar
unknown committed
4781 4782 4783
static int create_ndb_column(NDBCOL &col,
                             Field *field,
                             HA_CREATE_INFO *info)
unknown's avatar
unknown committed
4784
{
unknown's avatar
unknown committed
4785
  // Set name
4786 4787 4788 4789
  if (col.setName(field->field_name))
  {
    return (my_errno= errno);
  }
unknown's avatar
unknown committed
4790 4791
  // Get char set
  CHARSET_INFO *cs= field->charset();
unknown's avatar
unknown committed
4792 4793 4794 4795
  // Set type and sizes
  const enum enum_field_types mysql_type= field->real_type();
  switch (mysql_type) {
  // Numeric types
unknown's avatar
unknown committed
4796
  case MYSQL_TYPE_TINY:        
unknown's avatar
unknown committed
4797 4798 4799 4800 4801 4802
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Tinyunsigned);
    else
      col.setType(NDBCOL::Tinyint);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
4803
  case MYSQL_TYPE_SHORT:
unknown's avatar
unknown committed
4804 4805 4806 4807 4808 4809
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Smallunsigned);
    else
      col.setType(NDBCOL::Smallint);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
4810
  case MYSQL_TYPE_LONG:
unknown's avatar
unknown committed
4811 4812 4813 4814 4815 4816
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Unsigned);
    else
      col.setType(NDBCOL::Int);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
4817
  case MYSQL_TYPE_INT24:       
unknown's avatar
unknown committed
4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829
    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
4830 4831
    break;
  case MYSQL_TYPE_FLOAT:
unknown's avatar
unknown committed
4832 4833 4834
    col.setType(NDBCOL::Float);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
4835
  case MYSQL_TYPE_DOUBLE:
unknown's avatar
unknown committed
4836 4837 4838
    col.setType(NDBCOL::Double);
    col.setLength(1);
    break;
4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858
  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;
4859 4860 4861
  case MYSQL_TYPE_NEWDECIMAL:    
    {
      Field_new_decimal *f= (Field_new_decimal*)field;
unknown's avatar
unknown committed
4862
      uint precision= f->precision;
4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876
      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
4877 4878 4879 4880 4881
  // Date types
  case MYSQL_TYPE_DATETIME:    
    col.setType(NDBCOL::Datetime);
    col.setLength(1);
    break;
4882 4883 4884 4885
  case MYSQL_TYPE_DATE: // ?
    col.setType(NDBCOL::Char);
    col.setLength(field->pack_length());
    break;
unknown's avatar
unknown committed
4886
  case MYSQL_TYPE_NEWDATE:
unknown's avatar
unknown committed
4887 4888 4889
    col.setType(NDBCOL::Date);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
4890
  case MYSQL_TYPE_TIME:        
unknown's avatar
unknown committed
4891 4892 4893
    col.setType(NDBCOL::Time);
    col.setLength(1);
    break;
4894 4895 4896 4897 4898 4899 4900
  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
4901 4902 4903
    break;
  // Char types
  case MYSQL_TYPE_STRING:      
4904
    if (field->pack_length() == 0)
4905 4906 4907 4908
    {
      col.setType(NDBCOL::Bit);
      col.setLength(1);
    }
unknown's avatar
unknown committed
4909
    else if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4910
    {
unknown's avatar
unknown committed
4911
      col.setType(NDBCOL::Binary);
unknown's avatar
unknown committed
4912
      col.setLength(field->pack_length());
unknown's avatar
unknown committed
4913
    }
4914
    else
unknown's avatar
unknown committed
4915 4916 4917
    {
      col.setType(NDBCOL::Char);
      col.setCharset(cs);
4918
      col.setLength(field->pack_length());
unknown's avatar
unknown committed
4919
    }
unknown's avatar
unknown committed
4920
    break;
unknown's avatar
unknown committed
4921 4922 4923 4924 4925 4926
  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
4927
        if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4928 4929 4930 4931 4932 4933 4934 4935
          col.setType(NDBCOL::Varbinary);
        else {
          col.setType(NDBCOL::Varchar);
          col.setCharset(cs);
        }
      }
      else if (f->length_bytes == 2)
      {
unknown's avatar
unknown committed
4936
        if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947
          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
4948
    }
unknown's avatar
unknown committed
4949 4950 4951 4952
    break;
  // Blob types (all come in as MYSQL_TYPE_BLOB)
  mysql_type_tiny_blob:
  case MYSQL_TYPE_TINY_BLOB:
unknown's avatar
unknown committed
4953
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4954
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
4955
    else {
unknown's avatar
unknown committed
4956
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
4957 4958
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
4959 4960 4961 4962 4963
    col.setInlineSize(256);
    // No parts
    col.setPartSize(0);
    col.setStripeSize(0);
    break;
4964
  //mysql_type_blob:
4965
  case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
4966
  case MYSQL_TYPE_BLOB:    
unknown's avatar
unknown committed
4967
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4968
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
4969
    else {
unknown's avatar
unknown committed
4970
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
4971 4972
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
4973
    {
4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994
      Field_blob *field_blob= (Field_blob *)field;
      /*
       * max_data_length is 2^8-1, 2^16-1, 2^24-1 for tiny, blob, medium.
       * Tinyblob gets no blob parts.  The other cases are just a crude
       * way to control part size and striping.
       *
       * In mysql blob(256) is promoted to blob(65535) so it does not
       * in fact fit "inline" in NDB.
       */
      if (field_blob->max_data_length() < (1 << 8))
        goto mysql_type_tiny_blob;
      else if (field_blob->max_data_length() < (1 << 16))
      {
        col.setInlineSize(256);
        col.setPartSize(2000);
        col.setStripeSize(16);
      }
      else if (field_blob->max_data_length() < (1 << 24))
        goto mysql_type_medium_blob;
      else
        goto mysql_type_long_blob;
unknown's avatar
unknown committed
4995 4996 4997 4998
    }
    break;
  mysql_type_medium_blob:
  case MYSQL_TYPE_MEDIUM_BLOB:   
unknown's avatar
unknown committed
4999
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
5000
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
5001
    else {
unknown's avatar
unknown committed
5002
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
5003 5004
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
5005 5006 5007 5008 5009 5010
    col.setInlineSize(256);
    col.setPartSize(4000);
    col.setStripeSize(8);
    break;
  mysql_type_long_blob:
  case MYSQL_TYPE_LONG_BLOB:  
unknown's avatar
unknown committed
5011
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
5012
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
5013
    else {
unknown's avatar
unknown committed
5014
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
5015 5016
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029
    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
5030 5031
  case MYSQL_TYPE_BIT:
  {
unknown's avatar
unknown committed
5032
    int no_of_bits= field->field_length;
5033 5034 5035 5036 5037 5038 5039
    col.setType(NDBCOL::Bit);
    if (!no_of_bits)
      col.setLength(1);
      else
        col.setLength(no_of_bits);
    break;
  }
unknown's avatar
unknown committed
5040 5041 5042 5043 5044
  case MYSQL_TYPE_NULL:        
    goto mysql_type_unsupported;
  mysql_type_unsupported:
  default:
    return HA_ERR_UNSUPPORTED;
unknown's avatar
unknown committed
5045
  }
unknown's avatar
unknown committed
5046 5047 5048 5049 5050 5051
  // 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
5052
#ifndef DBUG_OFF
unknown's avatar
unknown committed
5053
    char buff[22];
unknown's avatar
unknown committed
5054
#endif
unknown's avatar
unknown committed
5055 5056
    col.setAutoIncrement(TRUE);
    ulonglong value= info->auto_increment_value ?
5057
      info->auto_increment_value : (ulonglong) 1;
unknown's avatar
unknown committed
5058
    DBUG_PRINT("info", ("Autoincrement key, initial: %s", llstr(value, buff)));
unknown's avatar
unknown committed
5059
    col.setAutoIncrementInitialValue(value);
unknown's avatar
unknown committed
5060
  }
unknown's avatar
unknown committed
5061
  else
unknown's avatar
unknown committed
5062
    col.setAutoIncrement(FALSE);
unknown's avatar
unknown committed
5063
  return 0;
unknown's avatar
unknown committed
5064 5065
}

unknown's avatar
unknown committed
5066 5067 5068 5069
/*
  Create a table in NDB Cluster
*/

unknown's avatar
unknown committed
5070
int ha_ndbcluster::create(const char *name, 
5071
                          TABLE *form, 
5072
                          HA_CREATE_INFO *create_info)
unknown's avatar
unknown committed
5073
{
5074
  THD *thd= current_thd;
unknown's avatar
unknown committed
5075 5076
  NDBTAB tab;
  NDBCOL col;
5077 5078 5079
  size_t pack_length, length;
  uint i, pk_length= 0;
  uchar *data= NULL, *pack_data= NULL;
5080
  bool create_from_engine= (create_info->table_options & HA_OPTION_CREATE_FROM_ENGINE);
5081
  bool is_truncate= (thd->lex->sql_command == SQLCOM_TRUNCATE);
5082
  char tablespace[FN_LEN];
5083
  NdbDictionary::Table::SingleUserMode single_user_mode= NdbDictionary::Table::SingleUserModeLocked;
5084

unknown's avatar
unknown committed
5085
  DBUG_ENTER("ha_ndbcluster::create");
unknown's avatar
unknown committed
5086
  DBUG_PRINT("enter", ("name: %s", name));
unknown's avatar
unknown committed
5087

5088 5089 5090
  DBUG_ASSERT(*fn_rext((char*)name) == 0);
  set_dbname(name);
  set_tabname(name);
5091

5092 5093 5094 5095 5096 5097
  if ((my_errno= check_ndb_connection()))
    DBUG_RETURN(my_errno);
  
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();

unknown's avatar
unknown committed
5098 5099
  if (is_truncate)
  {
5100 5101 5102 5103 5104
    {
      Ndb_table_guard ndbtab_g(dict, m_tabname);
      if (!(m_table= ndbtab_g.get_table()))
	ERR_RETURN(dict->getNdbError());
      if ((get_tablespace_name(thd, tablespace, FN_LEN)))
unknown's avatar
unknown committed
5105
	create_info->tablespace= tablespace;    
5106 5107
      m_table= NULL;
    }
unknown's avatar
unknown committed
5108 5109 5110 5111
    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
5112
  table= form;
5113 5114 5115
  if (create_from_engine)
  {
    /*
unknown's avatar
unknown committed
5116
      Table already exists in NDB and frm file has been created by 
5117 5118 5119
      caller.
      Do Ndb specific stuff, such as create a .ndb file
    */
unknown's avatar
unknown committed
5120
    if ((my_errno= write_ndb_file(name)))
unknown's avatar
unknown committed
5121
      DBUG_RETURN(my_errno);
unknown's avatar
unknown committed
5122
#ifdef HAVE_NDB_BINLOG
5123
    ndbcluster_create_binlog_setup(get_ndb(), name, strlen(name),
5124
                                   m_dbname, m_tabname, FALSE);
unknown's avatar
unknown committed
5125
#endif /* HAVE_NDB_BINLOG */
5126 5127
    DBUG_RETURN(my_errno);
  }
unknown's avatar
unknown committed
5128

5129 5130 5131 5132 5133 5134
#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 )
  */
5135
  if (!ndb_schema_share)
5136
  {
5137 5138 5139 5140 5141 5142 5143
    if (!(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);
    }
    single_user_mode = NdbDictionary::Table::SingleUserModeReadWrite;
5144 5145 5146
  }
#endif /* HAVE_NDB_BINLOG */

unknown's avatar
unknown committed
5147
  DBUG_PRINT("table", ("name: %s", m_tabname));  
5148
  if (tab.setName(m_tabname))
5149 5150 5151
  {
    DBUG_RETURN(my_errno= errno);
  }
5152
  tab.setLogging(!(create_info->options & HA_LEX_CREATE_TMP_TABLE));    
5153 5154
  tab.setSingleUserMode(single_user_mode);

unknown's avatar
unknown committed
5155 5156 5157 5158
  // Save frm data for this table
  if (readfrm(name, &data, &length))
    DBUG_RETURN(1);
  if (packfrm(data, length, &pack_data, &pack_length))
5159 5160
  {
    my_free((char*)data, MYF(0));
unknown's avatar
unknown committed
5161
    DBUG_RETURN(2);
5162
  }
5163 5164 5165
  DBUG_PRINT("info",
             ("setFrm data: 0x%lx  len: %lu", (long) pack_data,
              (ulong) pack_length));
unknown's avatar
unknown committed
5166 5167 5168 5169
  tab.setFrm(pack_data, pack_length);      
  my_free((char*)data, MYF(0));
  my_free((char*)pack_data, MYF(0));
  
5170 5171 5172
  /*
    Check for disk options
  */
5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188
  if (create_info->storage_media == HA_SM_DISK)
  { 
    if (create_info->tablespace)
      tab.setTablespaceName(create_info->tablespace);
    else
      tab.setTablespaceName("DEFAULT-TS");
  }
  else if (create_info->tablespace)
  {
    if (create_info->storage_media == HA_SM_MEMORY)
    {
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
			  ER_ILLEGAL_HA_CREATE_OPTION,
			  ER(ER_ILLEGAL_HA_CREATE_OPTION),
			  ndbcluster_hton_name,
			  "TABLESPACE currently only supported for "
5189
			  "STORAGE DISK");
5190 5191 5192 5193 5194 5195
      DBUG_RETURN(HA_ERR_UNSUPPORTED);
    }
    tab.setTablespaceName(create_info->tablespace);
    create_info->storage_media = HA_SM_DISK;  //if use tablespace, that also means store on disk
  }

5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217
  /*
    Handle table row type

    Default is to let table rows have var part reference so that online 
    add column can be performed in the future.  Explicitly setting row 
    type to fixed will omit var part reference, which will save data 
    memory in ndb, but at the cost of not being able to online add 
    column to this table
  */
  switch (create_info->row_type) {
  case ROW_TYPE_FIXED:
    tab.setForceVarPart(FALSE);
    break;
  case ROW_TYPE_DYNAMIC:
    /* fall through, treat as default */
  default:
    /* fall through, treat as default */
  case ROW_TYPE_DEFAULT:
    tab.setForceVarPart(TRUE);
    break;
  }

5218 5219 5220
  /*
    Setup columns
  */
5221
  for (i= 0; i < form->s->fields; i++) 
unknown's avatar
unknown committed
5222 5223
  {
    Field *field= form->field[i];
unknown's avatar
unknown committed
5224
    DBUG_PRINT("info", ("name: %s  type: %u  pack_length: %d", 
unknown's avatar
unknown committed
5225
                        field->field_name, field->real_type(),
5226
                        field->pack_length()));
5227
    if ((my_errno= create_ndb_column(col, field, create_info)))
unknown's avatar
unknown committed
5228
      DBUG_RETURN(my_errno);
unknown's avatar
unknown committed
5229
 
5230
    if (create_info->storage_media == HA_SM_DISK)
unknown's avatar
unknown committed
5231 5232 5233 5234
      col.setStorageType(NdbDictionary::Column::StorageTypeDisk);
    else
      col.setStorageType(NdbDictionary::Column::StorageTypeMemory);

5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255
    switch (create_info->row_type) {
    case ROW_TYPE_FIXED:
      if (field_type_forces_var_part(field->type()))
      {
        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
                            ER_ILLEGAL_HA_CREATE_OPTION,
                            ER(ER_ILLEGAL_HA_CREATE_OPTION),
                            ndbcluster_hton_name,
                            "Row format FIXED incompatible with "
                            "variable sized attribute");
        DBUG_RETURN(HA_ERR_UNSUPPORTED);
      }
      break;
    case ROW_TYPE_DYNAMIC:
      /*
        Future: make columns dynamic in this case
      */
      break;
    default:
      break;
    }
5256 5257 5258 5259
    if (tab.addColumn(col))
    {
      DBUG_RETURN(my_errno= errno);
    }
unknown's avatar
unknown committed
5260
    if (col.getPrimaryKey())
unknown's avatar
unknown committed
5261
      pk_length += (field->pack_length() + 3) / 4;
unknown's avatar
unknown committed
5262
  }
unknown's avatar
unknown committed
5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273

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

unknown's avatar
unknown committed
5274
  // No primary key, create shadow key as 64 bit, auto increment  
5275
  if (form->s->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
5276 5277
  {
    DBUG_PRINT("info", ("Generating shadow key"));
5278 5279 5280 5281
    if (col.setName("$PK"))
    {
      DBUG_RETURN(my_errno= errno);
    }
unknown's avatar
unknown committed
5282 5283
    col.setType(NdbDictionary::Column::Bigunsigned);
    col.setLength(1);
unknown's avatar
unknown committed
5284
    col.setNullable(FALSE);
unknown's avatar
unknown committed
5285 5286
    col.setPrimaryKey(TRUE);
    col.setAutoIncrement(TRUE);
5287 5288 5289 5290
    if (tab.addColumn(col))
    {
      DBUG_RETURN(my_errno= errno);
    }
unknown's avatar
unknown committed
5291 5292
    pk_length += 2;
  }
5293
 
unknown's avatar
unknown committed
5294
  // Make sure that blob tables don't have to big part size
5295
  for (i= 0; i < form->s->fields; i++) 
unknown's avatar
unknown committed
5296 5297 5298 5299 5300 5301 5302
  {
    /**
     * 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()) {
5303
    case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
5304 5305 5306 5307
    case MYSQL_TYPE_BLOB:    
    case MYSQL_TYPE_MEDIUM_BLOB:   
    case MYSQL_TYPE_LONG_BLOB: 
    {
5308 5309
      NdbDictionary::Column * column= tab.getColumn(i);
      int size= pk_length + (column->getPartSize()+3)/4 + 7;
unknown's avatar
unknown committed
5310
      if (size > NDB_MAX_TUPLE_SIZE_IN_WORDS && 
5311
         (pk_length+7) < NDB_MAX_TUPLE_SIZE_IN_WORDS)
unknown's avatar
unknown committed
5312
      {
5313
        size= NDB_MAX_TUPLE_SIZE_IN_WORDS - pk_length - 7;
5314
        column->setPartSize(4*size);
unknown's avatar
unknown committed
5315 5316 5317 5318 5319 5320 5321 5322 5323 5324
      }
      /**
       * 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
5325
  }
unknown's avatar
Merge  
unknown committed
5326

5327
  // Check partition info
unknown's avatar
unknown committed
5328
  partition_info *part_info= form->part_info;
unknown's avatar
unknown committed
5329
  if ((my_errno= set_up_partition_info(part_info, form, (void*)&tab)))
5330
  {
unknown's avatar
unknown committed
5331
    DBUG_RETURN(my_errno);
5332 5333
  }

unknown's avatar
unknown committed
5334
  // Create the table in NDB     
5335
  if (dict->createTable(tab) != 0) 
unknown's avatar
unknown committed
5336 5337
  {
    const NdbError err= dict->getNdbError();
unknown's avatar
unknown committed
5338
    set_ndb_err(thd, err);
unknown's avatar
unknown committed
5339 5340 5341
    my_errno= ndb_to_mysql_error(&err);
    DBUG_RETURN(my_errno);
  }
5342 5343 5344 5345 5346 5347

  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...
5348 5349 5350 5351
  if (!m_table)
  {
    /* purecov: begin deadcode */
    const NdbError err= dict->getNdbError();
unknown's avatar
unknown committed
5352
    set_ndb_err(thd, err);
5353 5354 5355 5356
    my_errno= ndb_to_mysql_error(&err);
    DBUG_RETURN(my_errno);
    /* purecov: end */
  }
5357

unknown's avatar
unknown committed
5358 5359
  DBUG_PRINT("info", ("Table %s/%s created successfully", 
                      m_dbname, m_tabname));
5360

unknown's avatar
unknown committed
5361
  // Create secondary indexes
5362
  my_errno= create_indexes(ndb, form);
5363

5364
  if (!my_errno)
unknown's avatar
unknown committed
5365
    my_errno= write_ndb_file(name);
5366 5367 5368 5369 5370 5371
  else
  {
    /*
      Failed to create an index,
      drop the table (and all it's indexes)
    */
5372
    while (dict->dropTableGlobal(*m_table))
5373
    {
5374 5375 5376 5377 5378 5379 5380 5381 5382
      switch (dict->getNdbError().status)
      {
        case NdbError::TemporaryError:
          if (!thd->killed) 
            continue; // retry indefinitly
          break;
        default:
          break;
      }
unknown's avatar
unknown committed
5383
      break;
5384
    }
5385 5386
    m_table = 0;
    DBUG_RETURN(my_errno);
5387
  }
5388

unknown's avatar
unknown committed
5389 5390 5391 5392 5393 5394 5395 5396 5397
#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...
    */
    {
5398
      uint length= (uint) strlen(name);
unknown's avatar
unknown committed
5399
      if ((share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
5400
                                           (uchar*) name, length)))
unknown's avatar
unknown committed
5401 5402 5403 5404 5405
        handle_trailing_share(share);
    }
    /*
      get a new share
    */
5406

5407
    /* ndb_share reference create */
unknown's avatar
unknown committed
5408
    if (!(share= get_share(name, form, TRUE, TRUE)))
unknown's avatar
unknown committed
5409
    {
5410
      sql_print_error("NDB: allocating table share for %s failed", name);
unknown's avatar
unknown committed
5411 5412
      /* my_errno is set */
    }
5413 5414 5415 5416 5417
    else
    {
      DBUG_PRINT("NDB_SHARE", ("%s binlog create  use_count: %u",
                               share->key, share->use_count));
    }
unknown's avatar
unknown committed
5418 5419 5420 5421 5422 5423
    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);
5424 5425
      int do_event_op= ndb_binlog_running;

5426
      if (!ndb_schema_share &&
5427 5428 5429
          strcmp(share->db, NDB_REP_DB) == 0 &&
          strcmp(share->table_name, NDB_SCHEMA_TABLE) == 0)
        do_event_op= 1;
unknown's avatar
unknown committed
5430 5431 5432 5433 5434

      /*
        Always create an event for the table, as other mysql servers
        expect it to be there.
      */
5435
      if (!ndbcluster_create_event(ndb, m_table, event_name.c_ptr(), share,
5436
                                   share && do_event_op ? 2 : 1/* push warning */))
unknown's avatar
unknown committed
5437
      {
unknown's avatar
unknown committed
5438 5439 5440
        if (ndb_extra_logging)
          sql_print_information("NDB Binlog: CREATE TABLE Event: %s",
                                event_name.c_ptr());
5441
        if (share && 
5442
            ndbcluster_create_event_ops(share, m_table, event_name.c_ptr()))
unknown's avatar
unknown committed
5443 5444 5445 5446 5447
        {
          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
5448
      }
unknown's avatar
unknown committed
5449 5450 5451 5452
      /*
        warning has been issued if ndbcluster_create_event failed
        and (share && do_event_op)
      */
5453
      if (share && !do_event_op)
5454
        share->flags|= NSF_NO_BINLOG;
5455 5456
      ndbcluster_log_schema_op(thd, share,
                               thd->query, thd->query_length,
unknown's avatar
unknown committed
5457
                               share->db, share->table_name,
5458 5459
                               m_table->getObjectId(),
                               m_table->getObjectVersion(),
unknown's avatar
unknown committed
5460 5461 5462
                               (is_truncate) ?
			       SOT_TRUNCATE_TABLE : SOT_CREATE_TABLE, 
			       0, 0, 1);
unknown's avatar
unknown committed
5463 5464 5465 5466 5467
      break;
    }
  }
#endif /* HAVE_NDB_BINLOG */

5468
  m_table= 0;
unknown's avatar
unknown committed
5469 5470 5471
  DBUG_RETURN(my_errno);
}

5472 5473
int ha_ndbcluster::create_handler_files(const char *file,
                                        const char *old_name,
5474
                                        int action_flag,
unknown's avatar
unknown committed
5475
                                        HA_CREATE_INFO *create_info)
unknown's avatar
unknown committed
5476 5477 5478
{ 
  Ndb* ndb;
  const NDBTAB *tab;
5479 5480
  uchar *data= NULL, *pack_data= NULL;
  size_t length, pack_length;
unknown's avatar
unknown committed
5481 5482 5483 5484
  int error= 0;

  DBUG_ENTER("create_handler_files");

5485
  if (action_flag != CHF_INDEX_FLAG)
5486 5487 5488
  {
    DBUG_RETURN(FALSE);
  }
5489
  DBUG_PRINT("enter", ("file: %s", file));
unknown's avatar
unknown committed
5490 5491 5492 5493
  if (!(ndb= get_ndb()))
    DBUG_RETURN(HA_ERR_NO_CONNECTION);

  NDBDICT *dict= ndb->getDictionary();
unknown's avatar
unknown committed
5494
  if (!create_info->frm_only)
unknown's avatar
unknown committed
5495
    DBUG_RETURN(0); // Must be a create, ignore since frm is saved in create
5496 5497 5498 5499

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

5500 5501
  set_dbname(file);
  set_tabname(file);
5502
  Ndb_table_guard ndbtab_g(dict, m_tabname);
5503
  DBUG_PRINT("info", ("m_dbname: %s, m_tabname: %s", m_dbname, m_tabname));
5504
  if (!(tab= ndbtab_g.get_table()))
5505 5506
    DBUG_RETURN(0); // Unkown table, must be temporary table

5507
  DBUG_ASSERT(get_ndb_share_state(m_share) == NSS_ALTERED);
5508
  if (readfrm(file, &data, &length) ||
unknown's avatar
unknown committed
5509 5510 5511 5512 5513
      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));
5514
    error= 1;
unknown's avatar
unknown committed
5515
  }
5516 5517
  else
  {
unknown's avatar
unknown committed
5518 5519
    DBUG_PRINT("info", ("Table %s has changed, altering frm in ndb", 
                        m_tabname));
5520 5521 5522 5523
    NdbDictionary::Table new_tab= *tab;
    new_tab.setFrm(pack_data, pack_length);
    if (dict->alterTableGlobal(*tab, new_tab))
    {
unknown's avatar
unknown committed
5524
      set_ndb_err(current_thd, dict->getNdbError());
5525 5526
      error= ndb_to_mysql_error(&dict->getNdbError());
    }
5527 5528
    my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
    my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR));
unknown's avatar
unknown committed
5529
  }
5530
  
5531
  set_ndb_share_state(m_share, NSS_INITIAL);
5532 5533 5534
  /* ndb_share reference schema(?) free */
  DBUG_PRINT("NDB_SHARE", ("%s binlog schema(?) free  use_count: %u",
                           m_share->key, m_share->use_count));
5535
  free_share(&m_share); // Decrease ref_count
unknown's avatar
unknown committed
5536 5537 5538 5539

  DBUG_RETURN(error);
}

5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567
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:
5568 5569 5570 5571 5572 5573 5574
    if (check_index_fields_not_null(key_info))
    {
      push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
			  ER_NULL_COLUMN_IN_INDEX,
			  "Ndb does not support unique index on NULL valued attributes, index access with NULL value will become full table scan");
    }
    error= create_unique_index(unique_name, key_info);
5575 5576
    break;
  case ORDERED_INDEX:
5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587
    if (key_info->algorithm == HA_KEY_ALG_HASH)
    {
      push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
			  ER_ILLEGAL_HA_CREATE_OPTION,
			  ER(ER_ILLEGAL_HA_CREATE_OPTION),
			  ndbcluster_hton_name,
			  "Ndb does not support non-unique "
			  "hash based indexes");
      error= HA_ERR_UNSUPPORTED;
      break;
    }
5588 5589 5590 5591 5592 5593 5594 5595 5596
    error= create_ordered_index(name, key_info);
    break;
  default:
    DBUG_ASSERT(FALSE);
    break;
  }
  
  DBUG_RETURN(error);
}
unknown's avatar
unknown committed
5597

5598
int ha_ndbcluster::create_ordered_index(const char *name, 
5599
                                        KEY *key_info)
5600
{
5601
  DBUG_ENTER("ha_ndbcluster::create_ordered_index");
5602
  DBUG_RETURN(create_ndb_index(name, key_info, FALSE));
5603 5604 5605
}

int ha_ndbcluster::create_unique_index(const char *name, 
5606
                                       KEY *key_info)
5607 5608
{

5609
  DBUG_ENTER("ha_ndbcluster::create_unique_index");
5610
  DBUG_RETURN(create_ndb_index(name, key_info, TRUE));
5611 5612 5613
}


unknown's avatar
unknown committed
5614 5615 5616 5617
/*
  Create an index in NDB Cluster
 */

5618 5619 5620
int ha_ndbcluster::create_ndb_index(const char *name, 
                                     KEY *key_info,
                                     bool unique)
5621
{
5622 5623
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
unknown's avatar
unknown committed
5624 5625 5626
  KEY_PART_INFO *key_part= key_info->key_part;
  KEY_PART_INFO *end= key_part + key_info->key_parts;
  
5627
  DBUG_ENTER("ha_ndbcluster::create_index");
unknown's avatar
unknown committed
5628
  DBUG_PRINT("enter", ("name: %s ", name));
5629

unknown's avatar
unknown committed
5630
  NdbDictionary::Index ndb_index(name);
5631
  if (unique)
unknown's avatar
unknown committed
5632 5633 5634 5635 5636
    ndb_index.setType(NdbDictionary::Index::UniqueHashIndex);
  else 
  {
    ndb_index.setType(NdbDictionary::Index::OrderedIndex);
    // TODO Only temporary ordered indexes supported
unknown's avatar
unknown committed
5637
    ndb_index.setLogging(FALSE); 
unknown's avatar
unknown committed
5638
  }
5639 5640 5641 5642
  if (ndb_index.setTable(m_tabname))
  {
    DBUG_RETURN(my_errno= errno);
  }
unknown's avatar
unknown committed
5643 5644 5645 5646 5647

  for (; key_part != end; key_part++) 
  {
    Field *field= key_part->field;
    DBUG_PRINT("info", ("attr: %s", field->field_name));
5648 5649 5650 5651
    if (ndb_index.addColumnName(field->field_name))
    {
      DBUG_RETURN(my_errno= errno);
    }
unknown's avatar
unknown committed
5652 5653
  }
  
5654
  if (dict->createIndex(ndb_index, *m_table))
unknown's avatar
unknown committed
5655 5656 5657 5658 5659 5660 5661
    ERR_RETURN(dict->getNdbError());

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

5662 5663 5664 5665 5666
/*
 Prepare for an on-line alter table
*/ 
void ha_ndbcluster::prepare_for_alter()
{
5667
  /* ndb_share reference schema */
5668
  ndbcluster_get_share(m_share); // Increase ref_count
5669 5670
  DBUG_PRINT("NDB_SHARE", ("%s binlog schema  use_count: %u",
                           m_share->key, m_share->use_count));
5671 5672 5673
  set_ndb_share_state(m_share, NSS_ALTERED);
}

5674 5675 5676 5677 5678 5679 5680 5681
/*
  Add an index on-line to a table
*/
int ha_ndbcluster::add_index(TABLE *table_arg, 
                             KEY *key_info, uint num_of_keys)
{
  int error= 0;
  uint idx;
unknown's avatar
unknown committed
5682 5683
  DBUG_ENTER("ha_ndbcluster::add_index");
  DBUG_PRINT("enter", ("table %s", table_arg->s->table_name.str));
5684
  DBUG_ASSERT(m_share->state == NSS_ALTERED);
unknown's avatar
unknown committed
5685

5686 5687 5688 5689 5690
  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;
5691
    NDB_INDEX_TYPE idx_type= get_index_type_from_key(idx, key_info, false);
5692 5693 5694 5695 5696 5697 5698 5699 5700
    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;
  }
5701
  if (error)
5702
  {
5703
    set_ndb_share_state(m_share, NSS_INITIAL);
5704 5705 5706
    /* ndb_share reference schema free */
    DBUG_PRINT("NDB_SHARE", ("%s binlog schema free  use_count: %u",
                             m_share->key, m_share->use_count));
5707
    free_share(&m_share); // Decrease ref_count
5708
  }
5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719
  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");
5720
  DBUG_ASSERT(m_share->state == NSS_ALTERED);
5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731
  // 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;
5732 5733
  renumber_indexes(ndb, table_arg);
  DBUG_RETURN(0);
5734 5735 5736 5737 5738 5739 5740
}
 
/*
  Really drop all indexes marked for deletion
*/
int ha_ndbcluster::final_drop_index(TABLE *table_arg)
{
5741
  int error;
5742 5743 5744 5745 5746 5747
  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;
5748 5749 5750
  if((error= drop_indexes(ndb, table_arg)))
  {
    m_share->state= NSS_INITIAL;
5751 5752 5753
    /* ndb_share reference schema free */
    DBUG_PRINT("NDB_SHARE", ("%s binlog schema free  use_count: %u",
                             m_share->key, m_share->use_count));
5754 5755 5756
    free_share(&m_share); // Decrease ref_count
  }
  DBUG_RETURN(error);
5757 5758
}

unknown's avatar
unknown committed
5759 5760 5761 5762 5763 5764
/*
  Rename a table in NDB Cluster
*/

int ha_ndbcluster::rename_table(const char *from, const char *to)
{
5765
  NDBDICT *dict;
5766
  char old_dbname[FN_HEADLEN];
5767
  char new_dbname[FN_HEADLEN];
unknown's avatar
unknown committed
5768
  char new_tabname[FN_HEADLEN];
5769 5770
  const NDBTAB *orig_tab;
  int result;
5771 5772
  bool recreate_indexes= FALSE;
  NDBDICT::List index_list;
unknown's avatar
unknown committed
5773 5774

  DBUG_ENTER("ha_ndbcluster::rename_table");
5775
  DBUG_PRINT("info", ("Renaming %s to %s", from, to));
5776
  set_dbname(from, old_dbname);
5777
  set_dbname(to, new_dbname);
unknown's avatar
unknown committed
5778 5779 5780
  set_tabname(from);
  set_tabname(to, new_tabname);

5781 5782 5783
  if (check_ndb_connection())
    DBUG_RETURN(my_errno= HA_ERR_NO_CONNECTION);

unknown's avatar
unknown committed
5784
  Ndb *ndb= get_ndb();
5785
  ndb->setDatabaseName(old_dbname);
unknown's avatar
unknown committed
5786
  dict= ndb->getDictionary();
5787 5788
  Ndb_table_guard ndbtab_g(dict, m_tabname);
  if (!(orig_tab= ndbtab_g.get_table()))
5789
    ERR_RETURN(dict->getNdbError());
5790

unknown's avatar
unknown committed
5791
#ifdef HAVE_NDB_BINLOG
5792 5793 5794
  int ndb_table_id= orig_tab->getObjectId();
  int ndb_table_version= orig_tab->getObjectVersion();

5795
  /* ndb_share reference temporary */
unknown's avatar
unknown committed
5796
  NDB_SHARE *share= get_share(from, 0, FALSE);
5797
  if (share)
unknown's avatar
unknown committed
5798
  {
5799 5800
    DBUG_PRINT("NDB_SHARE", ("%s temporary  use_count: %u",
                             share->key, share->use_count));
unknown's avatar
unknown committed
5801
    IF_DBUG(int r=) rename_share(share, to);
unknown's avatar
unknown committed
5802 5803 5804
    DBUG_ASSERT(r == 0);
  }
#endif
5805 5806 5807 5808 5809
  if (my_strcasecmp(system_charset_info, new_dbname, old_dbname))
  {
    dict->listIndexes(index_list, *orig_tab);    
    recreate_indexes= TRUE;
  }
5810 5811
  // Change current database to that of target table
  set_dbname(to);
5812 5813 5814 5815
  if (ndb->setDatabaseName(m_dbname))
  {
    ERR_RETURN(ndb->getNdbError());
  }
unknown's avatar
unknown committed
5816

5817 5818 5819
  NdbDictionary::Table new_tab= *orig_tab;
  new_tab.setName(new_tabname);
  if (dict->alterTableGlobal(*orig_tab, new_tab) != 0)
unknown's avatar
unknown committed
5820
  {
5821
    NdbError ndb_error= dict->getNdbError();
unknown's avatar
unknown committed
5822 5823 5824
#ifdef HAVE_NDB_BINLOG
    if (share)
    {
unknown's avatar
unknown committed
5825 5826
      IF_DBUG(int ret=) rename_share(share, from);
      DBUG_ASSERT(ret == 0);
5827 5828 5829
      /* ndb_share reference temporary free */
      DBUG_PRINT("NDB_SHARE", ("%s temporary free  use_count: %u",
                               share->key, share->use_count));
unknown's avatar
unknown committed
5830 5831 5832
      free_share(&share);
    }
#endif
5833
    ERR_RETURN(ndb_error);
unknown's avatar
unknown committed
5834 5835 5836 5837
  }
  
  // Rename .ndb file
  if ((result= handler::rename_table(from, to)))
5838
  {
unknown's avatar
unknown committed
5839
    // ToDo in 4.1 should rollback alter table...
unknown's avatar
unknown committed
5840 5841
#ifdef HAVE_NDB_BINLOG
    if (share)
5842 5843 5844 5845
    {
      /* ndb_share reference temporary free */
      DBUG_PRINT("NDB_SHARE", ("%s temporary  use_count: %u",
                               share->key, share->use_count));
unknown's avatar
unknown committed
5846
      free_share(&share);
5847
    }
unknown's avatar
unknown committed
5848
#endif
unknown's avatar
unknown committed
5849
    DBUG_RETURN(result);
5850
  }
5851

unknown's avatar
unknown committed
5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862
#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);
5863 5864
    ndbcluster_handle_drop_table(ndb, event_name.c_ptr(), share,
                                 "rename table");
unknown's avatar
unknown committed
5865 5866 5867 5868 5869 5870 5871
  }

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

unknown's avatar
unknown committed
5875
    if (!ndbcluster_create_event(ndb, ndbtab, event_name.c_ptr(), share,
5876
                                 share && ndb_binlog_running ? 2 : 1/* push warning */))
unknown's avatar
unknown committed
5877 5878 5879 5880
    {
      if (ndb_extra_logging)
        sql_print_information("NDB Binlog: RENAME Event: %s",
                              event_name.c_ptr());
5881
      if (share &&
unknown's avatar
unknown committed
5882
          ndbcluster_create_event_ops(share, ndbtab, event_name.c_ptr()))
unknown's avatar
unknown committed
5883
      {
unknown's avatar
unknown committed
5884 5885 5886
        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
5887 5888
      }
    }
unknown's avatar
unknown committed
5889 5890 5891 5892
    /*
      warning has been issued if ndbcluster_create_event failed
      and (share && ndb_binlog_running)
    */
5893
    if (!is_old_table_tmpfile)
unknown's avatar
unknown committed
5894 5895
      ndbcluster_log_schema_op(current_thd, share,
                               current_thd->query, current_thd->query_length,
5896 5897
                               old_dbname, m_tabname,
                               ndb_table_id, ndb_table_version,
5898
                               SOT_RENAME_TABLE,
5899
                               m_dbname, new_tabname, 1);
unknown's avatar
unknown committed
5900
  }
5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925

  // 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
5926 5927
  }
  if (share)
5928 5929 5930 5931
  {
    /* ndb_share reference temporary free */
    DBUG_PRINT("NDB_SHARE", ("%s temporary free  use_count: %u",
                             share->key, share->use_count));
unknown's avatar
unknown committed
5932
    free_share(&share);
5933
  }
unknown's avatar
unknown committed
5934 5935
#endif

unknown's avatar
unknown committed
5936 5937 5938 5939 5940
  DBUG_RETURN(result);
}


/*
5941 5942
  Delete table from NDB Cluster

unknown's avatar
unknown committed
5943 5944
 */

unknown's avatar
unknown committed
5945 5946 5947 5948 5949 5950 5951 5952
/* 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)
{
5953
  THD *thd= current_thd;
unknown's avatar
unknown committed
5954 5955
  DBUG_ENTER("ha_ndbcluster::ndbcluster_delete_table");
  NDBDICT *dict= ndb->getDictionary();
5956 5957
  int ndb_table_id= 0;
  int ndb_table_version= 0;
5958
#ifdef HAVE_NDB_BINLOG
5959 5960 5961 5962
  /*
    Don't allow drop table unless
    schema distribution table is setup
  */
5963
  if (!ndb_schema_share)
5964 5965 5966 5967
  {
    DBUG_PRINT("info", ("Schema distribution table not setup"));
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
  }
5968
  /* ndb_share reference temporary */
unknown's avatar
unknown committed
5969
  NDB_SHARE *share= get_share(path, 0, FALSE);
5970 5971 5972 5973 5974
  if (share)
  {
    DBUG_PRINT("NDB_SHARE", ("%s temporary  use_count: %u",
                             share->key, share->use_count));
  }
unknown's avatar
unknown committed
5975
#endif
unknown's avatar
unknown committed
5976 5977 5978

  /* Drop the table from NDB */
  
5979
  int res= 0;
5980
  if (h && h->m_table)
unknown's avatar
unknown committed
5981
  {
5982 5983
retry_temporary_error1:
    if (dict->dropTableGlobal(*h->m_table) == 0)
5984 5985 5986
    {
      ndb_table_id= h->m_table->getObjectId();
      ndb_table_version= h->m_table->getObjectVersion();
5987
      DBUG_PRINT("info", ("success 1"));
5988
    }
5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999
    else
    {
      switch (dict->getNdbError().status)
      {
        case NdbError::TemporaryError:
          if (!thd->killed) 
            goto retry_temporary_error1; // retry indefinitly
          break;
        default:
          break;
      }
unknown's avatar
unknown committed
6000
      set_ndb_err(thd, dict->getNdbError());
6001
      res= ndb_to_mysql_error(&dict->getNdbError());
6002
      DBUG_PRINT("info", ("error(1) %u", res));
6003
    }
6004
    h->release_metadata(thd, ndb);
unknown's avatar
unknown committed
6005 6006 6007 6008
  }
  else
  {
    ndb->setDatabaseName(db);
6009 6010 6011 6012 6013
    while (1)
    {
      Ndb_table_guard ndbtab_g(dict, table_name);
      if (ndbtab_g.get_table())
      {
6014
    retry_temporary_error2:
6015 6016 6017 6018
        if (dict->dropTableGlobal(*ndbtab_g.get_table()) == 0)
        {
          ndb_table_id= ndbtab_g.get_table()->getObjectId();
          ndb_table_version= ndbtab_g.get_table()->getObjectVersion();
6019 6020
          DBUG_PRINT("info", ("success 2"));
          break;
6021
        }
6022
        else
6023
        {
6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037
          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;
          }
6038 6039
        }
      }
unknown's avatar
unknown committed
6040
      set_ndb_err(thd, dict->getNdbError());
6041 6042
      res= ndb_to_mysql_error(&dict->getNdbError());
      DBUG_PRINT("info", ("error(2) %u", res));
6043 6044
      break;
    }
unknown's avatar
unknown committed
6045 6046 6047 6048
  }

  if (res)
  {
unknown's avatar
unknown committed
6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059
#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;
6060 6061 6062
        /* ndb_share reference create free */
        DBUG_PRINT("NDB_SHARE", ("%s create free  use_count: %u",
                                 share->key, share->use_count));
unknown's avatar
unknown committed
6063 6064
        free_share(&share, TRUE);
      }
6065 6066 6067
      /* ndb_share reference temporary free */
      DBUG_PRINT("NDB_SHARE", ("%s temporary free  use_count: %u",
                               share->key, share->use_count));
unknown's avatar
unknown committed
6068 6069 6070 6071
      free_share(&share, TRUE);
      pthread_mutex_unlock(&ndbcluster_mutex);
    }
#endif
unknown's avatar
unknown committed
6072 6073 6074
    DBUG_RETURN(res);
  }

unknown's avatar
unknown committed
6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085
#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
6086 6087
  if (!IS_TMP_PREFIX(table_name) && share &&
      current_thd->lex->sql_command != SQLCOM_TRUNCATE)
unknown's avatar
unknown committed
6088
  {
6089 6090
    ndbcluster_log_schema_op(thd, share,
                             thd->query, thd->query_length,
unknown's avatar
unknown committed
6091
                             share->db, share->table_name,
6092
                             ndb_table_id, ndb_table_version,
6093
                             SOT_DROP_TABLE, 0, 0, 1);
unknown's avatar
unknown committed
6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104
  }
  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,
6105
                                 share, "delete table");
unknown's avatar
unknown committed
6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116
  }

  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;
6117 6118 6119
      /* ndb_share reference create free */
      DBUG_PRINT("NDB_SHARE", ("%s create free  use_count: %u",
                               share->key, share->use_count));
unknown's avatar
unknown committed
6120 6121
      free_share(&share, TRUE);
    }
6122 6123 6124
    /* ndb_share reference temporary free */
    DBUG_PRINT("NDB_SHARE", ("%s temporary free  use_count: %u",
                             share->key, share->use_count));
unknown's avatar
unknown committed
6125 6126 6127 6128
    free_share(&share, TRUE);
    pthread_mutex_unlock(&ndbcluster_mutex);
  }
#endif
unknown's avatar
unknown committed
6129 6130 6131
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
6132 6133
int ha_ndbcluster::delete_table(const char *name)
{
6134
  DBUG_ENTER("ha_ndbcluster::delete_table");
unknown's avatar
unknown committed
6135 6136 6137
  DBUG_PRINT("enter", ("name: %s", name));
  set_dbname(name);
  set_tabname(name);
6138

6139 6140 6141 6142 6143
#ifdef HAVE_NDB_BINLOG
  /*
    Don't allow drop table unless
    schema distribution table is setup
  */
6144
  if (!ndb_schema_share)
6145 6146 6147 6148 6149 6150
  {
    DBUG_PRINT("info", ("Schema distribution table not setup"));
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
  }
#endif

unknown's avatar
unknown committed
6151 6152
  if (check_ndb_connection())
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
6153 6154

  /* Call ancestor function to delete .ndb file */
6155
  handler::delete_table(name);
unknown's avatar
unknown committed
6156 6157

  DBUG_RETURN(delete_table(this, get_ndb(),name, m_dbname, m_tabname));
unknown's avatar
unknown committed
6158 6159 6160
}


6161 6162 6163 6164
void ha_ndbcluster::get_auto_increment(ulonglong offset, ulonglong increment,
                                       ulonglong nb_desired_values,
                                       ulonglong *first_value,
                                       ulonglong *nb_reserved_values)
6165
{
6166 6167
  int cache_size;
  Uint64 auto_value;
unknown's avatar
unknown committed
6168 6169
  DBUG_ENTER("get_auto_increment");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));
6170
  Ndb *ndb= get_ndb();
6171
   
6172
  if (m_rows_inserted > m_rows_to_insert)
unknown's avatar
unknown committed
6173
  {
6174 6175
    /* We guessed too low */
    m_rows_to_insert+= m_autoincrement_prefetch;
unknown's avatar
unknown committed
6176
  }
unknown's avatar
unknown committed
6177
  cache_size= 
unknown's avatar
unknown committed
6178 6179 6180 6181
    (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));
6182
  uint retries= NDB_AUTO_INCREMENT_RETRIES;
6183 6184
  int retry_sleep= 30; /* 30 milliseconds, transaction */
  for (;;)
6185
  {
6186
    Ndb_tuple_id_range_guard g(m_share);
6187
    if (m_skip_auto_increment &&
6188
        ndb->readAutoIncrementValue(m_table, g.range, auto_value) ||
6189
        ndb->getAutoIncrementValue(m_table, g.range, auto_value, cache_size, increment, offset))
6190 6191
    {
      if (--retries &&
6192
          ndb->getNdbError().status == NdbError::TemporaryError)
6193 6194 6195 6196 6197 6198 6199
      {
        my_sleep(retry_sleep);
        continue;
      }
      const NdbError err= ndb->getNdbError();
      sql_print_error("Error %lu in ::get_auto_increment(): %s",
                      (ulong) err.code, err.message);
unknown's avatar
unknown committed
6200 6201
      *first_value= ~(ulonglong) 0;
      DBUG_VOID_RETURN;
6202 6203
    }
    break;
6204
  }
6205 6206 6207 6208
  *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
6209 6210 6211 6212 6213 6214 6215
}


/*
  Constructor for the NDB Cluster table handler 
 */

unknown's avatar
unknown committed
6216 6217 6218 6219 6220
/*
  Normal flags for binlogging is that ndb has HA_HAS_OWN_BINLOGGING
  and preferes HA_BINLOG_ROW_CAPABLE
  Other flags are set under certain circumstaces in table_flags()
*/
unknown's avatar
unknown committed
6221 6222 6223 6224 6225 6226 6227
#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
6228
                HA_CAN_BIT_FIELD | \
6229 6230
                HA_PRIMARY_KEY_REQUIRED_FOR_POSITION | \
                HA_PRIMARY_KEY_REQUIRED_FOR_DELETE | \
6231
                HA_PARTIAL_COLUMN_READ | \
6232
                HA_HAS_OWN_BINLOGGING | \
6233
                HA_BINLOG_ROW_CAPABLE | \
6234
                HA_HAS_RECORDS
unknown's avatar
unknown committed
6235

6236 6237
ha_ndbcluster::ha_ndbcluster(handlerton *hton, TABLE_SHARE *table_arg):
  handler(hton, table_arg),
unknown's avatar
unknown committed
6238 6239 6240
  m_active_trans(NULL),
  m_active_cursor(NULL),
  m_table(NULL),
6241
  m_table_info(NULL),
unknown's avatar
unknown committed
6242
  m_table_flags(HA_NDBCLUSTER_TABLE_FLAGS),
6243
  m_share(0),
6244 6245 6246
  m_part_info(NULL),
  m_use_partition_function(FALSE),
  m_sorted(FALSE),
unknown's avatar
unknown committed
6247
  m_use_write(FALSE),
6248
  m_ignore_dup_key(FALSE),
6249
  m_has_unique_index(FALSE),
6250
  m_primary_key_update(FALSE),
unknown's avatar
unknown committed
6251
  m_ignore_no_key(FALSE),
6252 6253 6254
  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
6255
  m_rows_changed((ha_rows) 0),
6256
  m_bulk_insert_not_flushed(FALSE),
6257 6258
  m_delete_cannot_batch(FALSE),
  m_update_cannot_batch(FALSE),
6259 6260 6261
  m_ops_pending(0),
  m_skip_auto_increment(TRUE),
  m_blobs_pending(0),
6262
  m_blobs_offset(0),
6263 6264
  m_blobs_buffer(0),
  m_blobs_buffer_size(0),
6265 6266 6267
  m_dupkey((uint) -1),
  m_ha_not_exact_count(FALSE),
  m_force_send(TRUE),
6268
  m_autoincrement_prefetch((ha_rows) 32),
unknown's avatar
unknown committed
6269
  m_transaction_on(TRUE),
unknown's avatar
unknown committed
6270
  m_cond(NULL),
unknown's avatar
unknown committed
6271
  m_multi_cursor(NULL)
6272
{
6273
  int i;
6274
 
unknown's avatar
unknown committed
6275 6276 6277 6278 6279
  DBUG_ENTER("ha_ndbcluster");

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

6280 6281
  stats.records= ~(ha_rows)0; // uninitialized
  stats.block_size= 1024;
unknown's avatar
unknown committed
6282

unknown's avatar
ndb:  
unknown committed
6283 6284
  for (i= 0; i < MAX_KEY; i++)
    ndb_init_index(m_index[i]);
6285

unknown's avatar
unknown committed
6286 6287 6288 6289
  DBUG_VOID_RETURN;
}


6290 6291 6292 6293 6294 6295 6296 6297 6298 6299
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
6300 6301 6302 6303 6304 6305
/*
  Destructor for NDB Cluster table handler
 */

ha_ndbcluster::~ha_ndbcluster() 
{
6306 6307
  THD *thd= current_thd;
  Ndb *ndb= thd ? check_ndb_in_thd(thd) : g_ndb;
unknown's avatar
unknown committed
6308 6309
  DBUG_ENTER("~ha_ndbcluster");

6310
  if (m_share)
unknown's avatar
unknown committed
6311
  {
6312 6313 6314
    /* ndb_share reference handler free */
    DBUG_PRINT("NDB_SHARE", ("%s handler free  use_count: %u",
                             m_share->key, m_share->use_count));
unknown's avatar
unknown committed
6315 6316
    free_share(&m_share);
  }
6317
  release_metadata(thd, ndb);
6318 6319
  my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
  m_blobs_buffer= 0;
unknown's avatar
unknown committed
6320 6321

  // Check for open cursor/transaction
6322 6323
  if (m_active_cursor) {
  }
unknown's avatar
unknown committed
6324
  DBUG_ASSERT(m_active_cursor == NULL);
6325 6326
  if (m_active_trans) {
  }
unknown's avatar
unknown committed
6327 6328
  DBUG_ASSERT(m_active_trans == NULL);

unknown's avatar
unknown committed
6329 6330 6331 6332 6333 6334 6335
  // Discard any generated condition
  DBUG_PRINT("info", ("Deleting generated condition"));
  if (m_cond)
  {
    delete m_cond;
    m_cond= NULL;
  }
6336

unknown's avatar
unknown committed
6337 6338 6339 6340
  DBUG_VOID_RETURN;
}


unknown's avatar
Merge  
unknown committed
6341

unknown's avatar
unknown committed
6342 6343 6344 6345
/*
  Open a table for further use
  - fetch metadata for this table from NDB
  - check that table exists
unknown's avatar
unknown committed
6346 6347 6348 6349

  RETURN
    0    ok
    < 0  Table has changed
unknown's avatar
unknown committed
6350 6351 6352 6353
*/

int ha_ndbcluster::open(const char *name, int mode, uint test_if_locked)
{
unknown's avatar
unknown committed
6354
  int res;
unknown's avatar
unknown committed
6355
  KEY *key;
unknown's avatar
unknown committed
6356 6357 6358
  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
6359
  
unknown's avatar
unknown committed
6360 6361 6362 6363
  /*
    Setup ref_length to make room for the whole 
    primary key to be written in the ref variable
  */
unknown's avatar
unknown committed
6364
  
unknown's avatar
unknown committed
6365
  if (table_share->primary_key != MAX_KEY) 
unknown's avatar
unknown committed
6366
  {
unknown's avatar
unknown committed
6367
    key= table->key_info+table_share->primary_key;
unknown's avatar
unknown committed
6368 6369
    ref_length= key->key_length;
  }
6370 6371 6372 6373 6374 6375 6376 6377 6378 6379
  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
6380
  // Init table lock structure 
6381
  /* ndb_share reference handler */
unknown's avatar
unknown committed
6382
  if (!(m_share=get_share(name, table)))
unknown's avatar
unknown committed
6383
    DBUG_RETURN(1);
6384 6385
  DBUG_PRINT("NDB_SHARE", ("%s handler  use_count: %u",
                           m_share->key, m_share->use_count));
unknown's avatar
unknown committed
6386 6387 6388 6389 6390
  thr_lock_data_init(&m_share->lock,&m_lock,(void*) 0);
  
  set_dbname(name);
  set_tabname(name);
  
6391
  if ((res= check_ndb_connection()) ||
6392
      (res= get_metadata(name)))
6393 6394 6395 6396
  {
    /* ndb_share reference handler free */
    DBUG_PRINT("NDB_SHARE", ("%s handler free  use_count: %u",
                             m_share->key, m_share->use_count));
unknown's avatar
unknown committed
6397 6398
    free_share(&m_share);
    m_share= 0;
6399
    DBUG_RETURN(res);
6400
  }
6401
  while (1)
6402 6403
  {
    Ndb *ndb= get_ndb();
6404 6405
    if (ndb->setDatabaseName(m_dbname))
    {
unknown's avatar
unknown committed
6406
      set_ndb_err(current_thd, ndb->getNdbError());
6407 6408
      res= ndb_to_mysql_error(&ndb->getNdbError());
      break;
6409
    }
unknown's avatar
unknown committed
6410
    struct Ndb_statistics stat;
unknown's avatar
unknown committed
6411
    res= ndb_get_table_statistics(NULL, FALSE, ndb, m_table, &stat);
6412 6413 6414
    stats.mean_rec_length= stat.row_size;
    stats.data_file_length= stat.fragment_memory;
    stats.records= stat.row_count;
6415 6416
    if(!res)
      res= info(HA_STATUS_CONST);
6417
    break;
6418
  }
6419 6420
  if (res)
  {
6421
    free_share(&m_share);
6422
    m_share= 0;
6423
    release_metadata(current_thd, get_ndb());
6424
    DBUG_RETURN(res);
6425
  }
6426 6427 6428 6429
#ifdef HAVE_NDB_BINLOG
  if (!ndb_binlog_tables_inited && ndb_binlog_running)
    table->db_stat|= HA_READ_ONLY;
#endif
6430
  DBUG_RETURN(0);
unknown's avatar
unknown committed
6431 6432
}

unknown's avatar
unknown committed
6433 6434 6435 6436 6437 6438 6439 6440 6441 6442 6443 6444 6445 6446
/*
  Set partition info

  SYNOPSIS
    set_part_info()
    part_info

  RETURN VALUE
    NONE

  DESCRIPTION
    Set up partition info when handler object created
*/

6447 6448 6449 6450 6451
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 &&
6452
        !m_part_info->is_sub_partitioned()))
6453 6454
    m_use_partition_function= TRUE;
}
unknown's avatar
unknown committed
6455 6456 6457 6458 6459 6460 6461 6462

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

int ha_ndbcluster::close(void)
{
6463
  DBUG_ENTER("close");
unknown's avatar
unknown committed
6464
  THD *thd= table->in_use;
6465
  Ndb *ndb= thd ? check_ndb_in_thd(thd) : g_ndb;
6466 6467 6468
  /* ndb_share reference handler free */
  DBUG_PRINT("NDB_SHARE", ("%s handler free  use_count: %u",
                           m_share->key, m_share->use_count));
unknown's avatar
unknown committed
6469 6470
  free_share(&m_share);
  m_share= 0;
6471
  release_metadata(thd, ndb);
unknown's avatar
unknown committed
6472 6473 6474 6475
  DBUG_RETURN(0);
}


6476
Thd_ndb* ha_ndbcluster::seize_thd_ndb()
unknown's avatar
unknown committed
6477
{
6478 6479
  Thd_ndb *thd_ndb;
  DBUG_ENTER("seize_thd_ndb");
unknown's avatar
unknown committed
6480

6481
  thd_ndb= new Thd_ndb();
6482 6483 6484 6485 6486
  if (thd_ndb == NULL)
  {
    my_errno= HA_ERR_OUT_OF_MEM;
    return NULL;
  }
6487
  if (thd_ndb->ndb->init(max_transactions) != 0)
unknown's avatar
unknown committed
6488
  {
6489
    ERR_PRINT(thd_ndb->ndb->getNdbError());
unknown's avatar
unknown committed
6490 6491 6492 6493 6494 6495
    /*
      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 
    */
6496 6497
    delete thd_ndb;
    thd_ndb= NULL;
unknown's avatar
unknown committed
6498
  }
6499
  DBUG_RETURN(thd_ndb);
unknown's avatar
unknown committed
6500 6501 6502
}


6503
void ha_ndbcluster::release_thd_ndb(Thd_ndb* thd_ndb)
unknown's avatar
unknown committed
6504
{
6505 6506
  DBUG_ENTER("release_thd_ndb");
  delete thd_ndb;
unknown's avatar
unknown committed
6507 6508 6509 6510 6511
  DBUG_VOID_RETURN;
}


/*
unknown's avatar
unknown committed
6512
  If this thread already has a Thd_ndb object allocated
unknown's avatar
unknown committed
6513
  in current THD, reuse it. Otherwise
unknown's avatar
unknown committed
6514
  seize a Thd_ndb object, assign it to current THD and use it.
unknown's avatar
unknown committed
6515 6516 6517
 
*/

6518
Ndb* check_ndb_in_thd(THD* thd)
unknown's avatar
unknown committed
6519
{
6520
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
6521
  if (!thd_ndb)
unknown's avatar
unknown committed
6522
  {
unknown's avatar
unknown committed
6523
    if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
unknown's avatar
Merge  
unknown committed
6524
      return NULL;
6525
    set_thd_ndb(thd, thd_ndb);
unknown's avatar
unknown committed
6526
  }
unknown's avatar
Merge  
unknown committed
6527
  return thd_ndb->ndb;
6528 6529
}

unknown's avatar
unknown committed
6530

6531

6532
int ha_ndbcluster::check_ndb_connection(THD* thd)
unknown's avatar
unknown committed
6533
{
6534
  Ndb *ndb;
unknown's avatar
unknown committed
6535 6536
  DBUG_ENTER("check_ndb_connection");
  
6537
  if (!(ndb= check_ndb_in_thd(thd)))
6538
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
6539 6540 6541 6542
  if (ndb->setDatabaseName(m_dbname))
  {
    ERR_RETURN(ndb->getNdbError());
  }
unknown's avatar
unknown committed
6543 6544 6545
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
6546

6547
static int ndbcluster_close_connection(handlerton *hton, THD *thd)
unknown's avatar
unknown committed
6548
{
6549
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
unknown's avatar
unknown committed
6550
  DBUG_ENTER("ndbcluster_close_connection");
6551 6552
  if (thd_ndb)
  {
6553
    ha_ndbcluster::release_thd_ndb(thd_ndb);
6554
    set_thd_ndb(thd, NULL); // not strictly required but does not hurt either
6555
  }
6556
  DBUG_RETURN(0);
unknown's avatar
unknown committed
6557 6558 6559 6560 6561 6562 6563
}


/*
  Try to discover one table from NDB
 */

6564 6565
int ndbcluster_discover(handlerton *hton, THD* thd, const char *db, 
                        const char *name,
6566 6567
                        uchar **frmblob, 
                        size_t *frmlen)
unknown's avatar
unknown committed
6568
{
6569 6570
  int error= 0;
  NdbError ndb_error;
6571 6572
  size_t len;
  uchar* data= NULL;
6573
  Ndb* ndb;
6574
  char key[FN_REFLEN];
unknown's avatar
unknown committed
6575
  DBUG_ENTER("ndbcluster_discover");
6576
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name)); 
unknown's avatar
unknown committed
6577

6578 6579
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(HA_ERR_NO_CONNECTION);  
6580 6581 6582 6583
  if (ndb->setDatabaseName(db))
  {
    ERR_RETURN(ndb->getNdbError());
  }
6584
  NDBDICT* dict= ndb->getDictionary();
6585
  build_table_filename(key, sizeof(key), db, name, "", 0);
6586
  /* ndb_share reference temporary */
unknown's avatar
unknown committed
6587
  NDB_SHARE *share= get_share(key, 0, FALSE);
6588 6589 6590 6591 6592
  if (share)
  {
    DBUG_PRINT("NDB_SHARE", ("%s temporary  use_count: %u",
                             share->key, share->use_count));
  }
6593
  if (share && get_ndb_share_state(share) == NSS_ALTERED)
unknown's avatar
unknown committed
6594
  {
6595 6596 6597 6598
    // Frm has been altered on disk, but not yet written to ndb
    if (readfrm(key, &data, &len))
    {
      DBUG_PRINT("error", ("Could not read frm"));
6599 6600
      error= 1;
      goto err;
6601
    }
unknown's avatar
unknown committed
6602
  }
6603
  else
6604
  {
6605 6606 6607 6608
    Ndb_table_guard ndbtab_g(dict, name);
    const NDBTAB *tab= ndbtab_g.get_table();
    if (!tab)
    {
6609 6610
      const NdbError err= dict->getNdbError();
      if (err.code == 709 || err.code == 723)
6611
      {
6612
        error= -1;
6613 6614
        DBUG_PRINT("info", ("ndb_error.code: %u", ndb_error.code));
      }
6615
      else
6616 6617
      {
        error= -1;
6618
        ndb_error= err;
6619 6620
        DBUG_PRINT("info", ("ndb_error.code: %u", ndb_error.code));
      }
6621
      goto err;
6622 6623 6624 6625 6626 6627 6628
    }
    DBUG_PRINT("info", ("Found table %s", tab->getName()));
    
    len= tab->getFrmLength();  
    if (len == 0 || tab->getFrmData() == NULL)
    {
      DBUG_PRINT("error", ("No frm data found."));
6629 6630
      error= 1;
      goto err;
6631 6632
    }
    
6633
    if (unpackfrm(&data, &len, (uchar*) tab->getFrmData()))
6634 6635
    {
      DBUG_PRINT("error", ("Could not unpack table"));
6636 6637
      error= 1;
      goto err;
6638
    }
6639
  }
unknown's avatar
unknown committed
6640 6641 6642 6643

  *frmlen= len;
  *frmblob= data;
  
6644
  if (share)
6645 6646 6647 6648
  {
    /* ndb_share reference temporary free */
    DBUG_PRINT("NDB_SHARE", ("%s temporary free  use_count: %u",
                             share->key, share->use_count));
6649
    free_share(&share);
6650
  }
6651

unknown's avatar
unknown committed
6652
  DBUG_RETURN(0);
6653
err:
6654
  my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
6655
  if (share)
6656 6657 6658 6659
  {
    /* ndb_share reference temporary free */
    DBUG_PRINT("NDB_SHARE", ("%s temporary free  use_count: %u",
                             share->key, share->use_count));
6660
    free_share(&share);
6661
  }
6662 6663 6664 6665 6666
  if (ndb_error.code)
  {
    ERR_RETURN(ndb_error);
  }
  DBUG_RETURN(error);
unknown's avatar
unknown committed
6667 6668 6669
}

/*
6670
  Check if a table exists in NDB
6671

6672
 */
unknown's avatar
unknown committed
6673

6674 6675
int ndbcluster_table_exists_in_engine(handlerton *hton, THD* thd, 
                                      const char *db,
6676
                                      const char *name)
6677 6678
{
  Ndb* ndb;
6679
  DBUG_ENTER("ndbcluster_table_exists_in_engine");
6680
  DBUG_PRINT("enter", ("db: %s  name: %s", db, name));
6681 6682

  if (!(ndb= check_ndb_in_thd(thd)))
6683
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
6684
  NDBDICT* dict= ndb->getDictionary();
6685 6686 6687
  NdbDictionary::Dictionary::List list;
  if (dict->listObjects(list, NdbDictionary::Object::UserTable) != 0)
    ERR_RETURN(dict->getNdbError());
6688
  for (uint i= 0 ; i < list.count ; i++)
6689
  {
6690 6691 6692 6693 6694 6695
    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"));
6696
    DBUG_RETURN(HA_ERR_TABLE_EXIST);
6697
  }
6698
  DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
6699 6700
}

unknown's avatar
unknown committed
6701 6702


6703
extern "C" uchar* tables_get_key(const char *entry, size_t *length,
6704
                                my_bool not_used __attribute__((unused)))
6705 6706
{
  *length= strlen(entry);
6707
  return (uchar*) entry;
6708 6709 6710
}


6711 6712
/*
  Drop a database in NDB Cluster
6713 6714
  NOTE add a dummy void function, since stupid handlerton is returning void instead of int...
*/
6715

6716
int ndbcluster_drop_database_impl(const char *path)
6717 6718 6719 6720 6721 6722 6723 6724 6725
{
  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;
6726
  int ret= 0;
6727 6728 6729 6730
  ha_ndbcluster::set_dbname(path, (char *)&dbname);
  DBUG_PRINT("enter", ("db: %s", dbname));
  
  if (!(ndb= check_ndb_in_thd(thd)))
6731
    DBUG_RETURN(-1);
6732 6733 6734 6735 6736
  
  // List tables in NDB
  NDBDICT *dict= ndb->getDictionary();
  if (dict->listObjects(list, 
                        NdbDictionary::Object::UserTable) != 0)
6737
    DBUG_RETURN(-1);
6738 6739
  for (i= 0 ; i < list.count ; i++)
  {
unknown's avatar
unknown committed
6740 6741
    NdbDictionary::Dictionary::List::Element& elmt= list.elements[i];
    DBUG_PRINT("info", ("Found %s/%s in NDB", elmt.database, elmt.name));     
6742 6743
    
    // Add only tables that belongs to db
unknown's avatar
unknown committed
6744
    if (my_strcasecmp(system_charset_info, elmt.database, dbname))
6745
      continue;
unknown's avatar
unknown committed
6746 6747
    DBUG_PRINT("info", ("%s must be dropped", elmt.name));     
    drop_list.push_back(thd->strdup(elmt.name));
6748 6749
  }
  // Drop any tables belonging to database
unknown's avatar
unknown committed
6750
  char full_path[FN_REFLEN];
6751
  char *tmp= full_path +
6752
    build_table_filename(full_path, sizeof(full_path), dbname, "", "", 0);
6753 6754 6755 6756
  if (ndb->setDatabaseName(dbname))
  {
    ERR_RETURN(ndb->getNdbError());
  }
6757 6758
  List_iterator_fast<char> it(drop_list);
  while ((tabname=it++))
6759
  {
6760
    tablename_to_filename(tabname, tmp, FN_REFLEN - (tmp - full_path)-1);
unknown's avatar
unknown committed
6761
    VOID(pthread_mutex_lock(&LOCK_open));
unknown's avatar
unknown committed
6762
    if (ha_ndbcluster::delete_table(0, ndb, full_path, dbname, tabname))
6763 6764
    {
      const NdbError err= dict->getNdbError();
unknown's avatar
unknown committed
6765
      if (err.code != 709 && err.code != 723)
6766
      {
unknown's avatar
unknown committed
6767
        set_ndb_err(thd, err);
6768
        ret= ndb_to_mysql_error(&err);
6769
      }
6770
    }
unknown's avatar
unknown committed
6771
    VOID(pthread_mutex_unlock(&LOCK_open));
6772 6773
  }
  DBUG_RETURN(ret);      
6774 6775
}

6776
static void ndbcluster_drop_database(handlerton *hton, char *path)
6777
{
6778 6779 6780 6781 6782 6783
  DBUG_ENTER("ndbcluster_drop_database");
#ifdef HAVE_NDB_BINLOG
  /*
    Don't allow drop database unless
    schema distribution table is setup
  */
6784
  if (!ndb_schema_share)
6785 6786 6787 6788 6789 6790
  {
    DBUG_PRINT("info", ("Schema distribution table not setup"));
    DBUG_VOID_RETURN;
    //DBUG_RETURN(HA_ERR_NO_CONNECTION);
  }
#endif
6791
  ndbcluster_drop_database_impl(path);
unknown's avatar
unknown committed
6792 6793
#ifdef HAVE_NDB_BINLOG
  char db[FN_REFLEN];
unknown's avatar
unknown committed
6794
  THD *thd= current_thd;
unknown's avatar
unknown committed
6795
  ha_ndbcluster::set_dbname(path, db);
6796 6797
  ndbcluster_log_schema_op(thd, 0,
                           thd->query, thd->query_length,
6798
                           db, "", 0, 0, SOT_DROP_DB, 0, 0, 0);
unknown's avatar
unknown committed
6799
#endif
6800
  DBUG_VOID_RETURN;
6801
}
unknown's avatar
unknown committed
6802

6803 6804 6805 6806 6807 6808
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;
6809
  lex_start(thd);
6810 6811 6812 6813 6814
  int res= ha_create_table_from_engine(thd, db, table_name);
  thd->lex= old_lex;
  return res;
}

unknown's avatar
unknown committed
6815 6816 6817
/*
  find all tables in ndb and discover those needed
*/
unknown's avatar
unknown committed
6818
int ndbcluster_find_all_files(THD *thd)
unknown's avatar
unknown committed
6819 6820 6821
{
  Ndb* ndb;
  char key[FN_REFLEN];
unknown's avatar
unknown committed
6822 6823 6824
  NDBDICT *dict;
  int unhandled, retries= 5, skipped;
  DBUG_ENTER("ndbcluster_find_all_files");
unknown's avatar
unknown committed
6825 6826 6827 6828

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

unknown's avatar
unknown committed
6829
  dict= ndb->getDictionary();
unknown's avatar
unknown committed
6830

6831 6832
  LINT_INIT(unhandled);
  LINT_INIT(skipped);
unknown's avatar
unknown committed
6833 6834
  do
  {
unknown's avatar
ndb -  
unknown committed
6835
    NdbDictionary::Dictionary::List list;
unknown's avatar
unknown committed
6836 6837 6838
    if (dict->listObjects(list, NdbDictionary::Object::UserTable) != 0)
      ERR_RETURN(dict->getNdbError());
    unhandled= 0;
6839 6840
    skipped= 0;
    retries--;
unknown's avatar
unknown committed
6841 6842 6843
    for (uint i= 0 ; i < list.count ; i++)
    {
      NDBDICT::List::Element& elmt= list.elements[i];
unknown's avatar
unknown committed
6844
      if (IS_TMP_PREFIX(elmt.name) || IS_NDB_BLOB_PREFIX(elmt.name))
unknown's avatar
unknown committed
6845 6846 6847 6848
      {
        DBUG_PRINT("info", ("Skipping %s.%s in NDB", elmt.database, elmt.name));
        continue;
      }
unknown's avatar
unknown committed
6849
      DBUG_PRINT("info", ("Found %s.%s in NDB", elmt.database, elmt.name));
6850 6851 6852
      if (elmt.state != NDBOBJ::StateOnline &&
          elmt.state != NDBOBJ::StateBackup &&
          elmt.state != NDBOBJ::StateBuilding)
unknown's avatar
unknown committed
6853 6854 6855
      {
        sql_print_information("NDB: skipping setup table %s.%s, in state %d",
                              elmt.database, elmt.name, elmt.state);
6856
        skipped++;
unknown's avatar
unknown committed
6857 6858 6859 6860
        continue;
      }

      ndb->setDatabaseName(elmt.database);
6861 6862 6863
      Ndb_table_guard ndbtab_g(dict, elmt.name);
      const NDBTAB *ndbtab= ndbtab_g.get_table();
      if (!ndbtab)
unknown's avatar
unknown committed
6864
      {
6865
        if (retries == 0)
unknown's avatar
unknown committed
6866 6867 6868 6869
          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
6870 6871 6872 6873 6874 6875 6876
        unhandled++;
        continue;
      }

      if (ndbtab->getFrmLength() == 0)
        continue;
    
6877
      /* check if database exists */
6878
      char *end= key +
6879
        build_table_filename(key, sizeof(key), elmt.database, "", "", 0);
6880 6881 6882 6883 6884
      if (my_access(key, F_OK))
      {
        /* no such database defined, skip table */
        continue;
      }
6885 6886 6887
      /* finalize construction of path */
      end+= tablename_to_filename(elmt.name, end,
                                  sizeof(key)-(end-key));
6888 6889
      uchar *data= 0, *pack_data= 0;
      size_t length, pack_length;
unknown's avatar
unknown committed
6890 6891 6892 6893 6894 6895 6896 6897 6898 6899
      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))
      {
6900
        /* ndb_share reference temporary */
unknown's avatar
unknown committed
6901
        NDB_SHARE *share= get_share(key, 0, FALSE);
6902 6903 6904 6905 6906
        if (share)
        {
          DBUG_PRINT("NDB_SHARE", ("%s temporary  use_count: %u",
                                   share->key, share->use_count));
        }
6907
        if (!share || get_ndb_share_state(share) != NSS_ALTERED)
unknown's avatar
unknown committed
6908 6909 6910 6911 6912
        {
          discover= 1;
          sql_print_information("NDB: mismatch in frm for %s.%s, discovering...",
                                elmt.database, elmt.name);
        }
unknown's avatar
unknown committed
6913
        if (share)
6914 6915 6916 6917
        {
          /* ndb_share reference temporary free */
          DBUG_PRINT("NDB_SHARE", ("%s temporary free  use_count: %u",
                                   share->key, share->use_count));
unknown's avatar
unknown committed
6918
          free_share(&share);
6919
        }
unknown's avatar
unknown committed
6920 6921 6922 6923
      }
      my_free((char*) data, MYF(MY_ALLOW_ZERO_PTR));
      my_free((char*) pack_data, MYF(MY_ALLOW_ZERO_PTR));

6924
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
6925 6926 6927
      if (discover)
      {
        /* ToDo 4.1 database needs to be created if missing */
6928
        if (ndb_create_table_from_engine(thd, elmt.database, elmt.name))
unknown's avatar
unknown committed
6929 6930 6931 6932
        {
          /* ToDo 4.1 handle error */
        }
      }
unknown's avatar
unknown committed
6933
#ifdef HAVE_NDB_BINLOG
6934
      else
unknown's avatar
unknown committed
6935 6936
      {
        /* set up replication for this table */
6937 6938 6939
        ndbcluster_create_binlog_setup(ndb, key, end-key,
                                       elmt.database, elmt.name,
                                       TRUE);
unknown's avatar
unknown committed
6940 6941
      }
#endif
6942
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
6943 6944
    }
  }
6945
  while (unhandled && retries);
unknown's avatar
unknown committed
6946

6947
  DBUG_RETURN(-(skipped + unhandled));
unknown's avatar
unknown committed
6948
}
6949

6950 6951 6952
int ndbcluster_find_files(handlerton *hton, THD *thd,
                          const char *db,
                          const char *path,
6953
                          const char *wild, bool dir, List<LEX_STRING> *files)
unknown's avatar
unknown committed
6954
{
6955 6956 6957
  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
6958
  uint i;
6959
  Ndb* ndb;
6960
  char name[FN_REFLEN];
unknown's avatar
unknown committed
6961
  HASH ndb_tables, ok_tables;
unknown's avatar
unknown committed
6962
  NDBDICT::List list;
6963 6964 6965 6966

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

6967
  if (dir)
unknown's avatar
unknown committed
6968
    DBUG_RETURN(0); // Discover of databases not yet supported
6969

unknown's avatar
unknown committed
6970
  // List tables in NDB
6971
  NDBDICT *dict= ndb->getDictionary();
unknown's avatar
unknown committed
6972
  if (dict->listObjects(list, 
6973
                        NdbDictionary::Object::UserTable) != 0)
unknown's avatar
unknown committed
6974
    ERR_RETURN(dict->getNdbError());
6975

unknown's avatar
unknown committed
6976
  if (hash_init(&ndb_tables, system_charset_info,list.count,0,0,
6977
                (hash_get_key)tables_get_key,0,0))
unknown's avatar
unknown committed
6978 6979 6980 6981 6982 6983
  {
    DBUG_PRINT("error", ("Failed to init HASH ndb_tables"));
    DBUG_RETURN(-1);
  }

  if (hash_init(&ok_tables, system_charset_info,32,0,0,
6984
                (hash_get_key)tables_get_key,0,0))
unknown's avatar
unknown committed
6985 6986 6987 6988 6989 6990
  {
    DBUG_PRINT("error", ("Failed to init HASH ok_tables"));
    hash_free(&ndb_tables);
    DBUG_RETURN(-1);
  }  

unknown's avatar
unknown committed
6991 6992
  for (i= 0 ; i < list.count ; i++)
  {
unknown's avatar
unknown committed
6993
    NDBDICT::List::Element& elmt= list.elements[i];
unknown's avatar
unknown committed
6994
    if (IS_TMP_PREFIX(elmt.name) || IS_NDB_BLOB_PREFIX(elmt.name))
unknown's avatar
unknown committed
6995 6996 6997 6998
    {
      DBUG_PRINT("info", ("Skipping %s.%s in NDB", elmt.database, elmt.name));
      continue;
    }
unknown's avatar
unknown committed
6999
    DBUG_PRINT("info", ("Found %s/%s in NDB", elmt.database, elmt.name));
unknown's avatar
unknown committed
7000

7001
    // Add only tables that belongs to db
unknown's avatar
unknown committed
7002
    if (my_strcasecmp(system_charset_info, elmt.database, db))
7003
      continue;
unknown's avatar
unknown committed
7004

unknown's avatar
unknown committed
7005
    // Apply wildcard to list of tables in NDB
7006
    if (wild)
7007
    {
7008 7009
      if (lower_case_table_names)
      {
unknown's avatar
unknown committed
7010
        if (wild_case_compare(files_charset_info, elmt.name, wild))
7011
          continue;
7012
      }
unknown's avatar
unknown committed
7013
      else if (wild_compare(elmt.name,wild,0))
7014
        continue;
7015
    }
unknown's avatar
unknown committed
7016
    DBUG_PRINT("info", ("Inserting %s into ndb_tables hash", elmt.name));     
7017
    my_hash_insert(&ndb_tables, (uchar*)thd->strdup(elmt.name));
unknown's avatar
unknown committed
7018 7019
  }

7020 7021
  LEX_STRING *file_name;
  List_iterator<LEX_STRING> it(*files);
unknown's avatar
unknown committed
7022
  List<char> delete_list;
7023
  char *file_name_str;
unknown's avatar
unknown committed
7024 7025
  while ((file_name=it++))
  {
unknown's avatar
unknown committed
7026
    bool file_on_disk= FALSE;
7027
    DBUG_PRINT("info", ("%s", file_name->str));
7028
    if (hash_search(&ndb_tables, (uchar*) file_name->str, file_name->length))
unknown's avatar
unknown committed
7029
    {
7030
      build_table_filename(name, sizeof(name), db, file_name->str, reg_ext, 0);
7031 7032 7033 7034
      if (my_access(name, F_OK))
      {
        pthread_mutex_lock(&LOCK_open);
        DBUG_PRINT("info", ("Table %s listed and need discovery",
7035 7036
                            file_name->str));
        if (ndb_create_table_from_engine(thd, db, file_name->str))
7037 7038 7039 7040 7041
        {
          pthread_mutex_unlock(&LOCK_open);
          push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                              ER_TABLE_EXISTS_ERROR,
                              "Discover of table %s.%s failed",
7042
                              db, file_name->str);
7043 7044 7045 7046
          continue;
        }
        pthread_mutex_unlock(&LOCK_open);
      }
7047
      DBUG_PRINT("info", ("%s existed in NDB _and_ on disk ", file_name->str));
unknown's avatar
unknown committed
7048
      file_on_disk= TRUE;
unknown's avatar
unknown committed
7049 7050
    }
    
7051
    // Check for .ndb file with this name
7052
    build_table_filename(name, sizeof(name), db, file_name->str, ha_ndb_ext, 0);
unknown's avatar
unknown committed
7053
    DBUG_PRINT("info", ("Check access for %s", name));
7054
    if (my_access(name, F_OK))
unknown's avatar
unknown committed
7055 7056 7057
    {
      DBUG_PRINT("info", ("%s did not exist on disk", name));     
      // .ndb file did not exist on disk, another table type
7058
      if (file_on_disk)
7059
      {
7060 7061 7062
	// Ignore this ndb table 
 	uchar *record= hash_search(&ndb_tables, (uchar*) file_name->str,
                                   file_name->length);
7063 7064
	DBUG_ASSERT(record);
	hash_delete(&ndb_tables, record);
7065 7066 7067
	push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
			    ER_TABLE_EXISTS_ERROR,
			    "Local table %s.%s shadows ndb table",
7068
			    db, file_name->str);
7069
      }
7070 7071 7072 7073
      continue;
    }
    if (file_on_disk) 
    {
7074
      // File existed in NDB and as frm file, put in ok_tables list
7075
      my_hash_insert(&ok_tables, (uchar*) file_name->str);
unknown's avatar
unknown committed
7076
      continue;
7077
    }
unknown's avatar
unknown committed
7078 7079 7080
    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.
7081 7082
    if (ndbcluster_table_exists_in_engine(hton, thd, db, file_name->str) ==
        HA_ERR_NO_SUCH_TABLE)
unknown's avatar
unknown committed
7083
    {
7084
      DBUG_PRINT("info", ("NDB says %s does not exists", file_name->str));
unknown's avatar
unknown committed
7085 7086
      it.remove();
      // Put in list of tables to remove from disk
7087
      delete_list.push_back(thd->strdup(file_name->str));
unknown's avatar
unknown committed
7088 7089
    }
  }
7090

unknown's avatar
unknown committed
7091 7092 7093
#ifdef HAVE_NDB_BINLOG
  /* setup logging to binlog for all discovered tables */
  {
7094
    char *end, *end1= name +
7095
      build_table_filename(name, sizeof(name), db, "", "", 0);
unknown's avatar
unknown committed
7096 7097
    for (i= 0; i < ok_tables.records; i++)
    {
7098
      file_name_str= (char*)hash_element(&ok_tables, i);
7099
      end= end1 +
7100
        tablename_to_filename(file_name_str, end1, sizeof(name) - (end1 - name));
7101 7102
      pthread_mutex_lock(&LOCK_open);
      ndbcluster_create_binlog_setup(ndb, name, end-name,
7103
                                     db, file_name_str, TRUE);
7104
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
7105 7106 7107 7108
    }
  }
#endif

unknown's avatar
unknown committed
7109 7110 7111 7112
  // 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++)
7113
  {
7114 7115
    file_name_str= (char*) hash_element(&ndb_tables, i);
    if (!hash_search(&ok_tables, (uchar*) file_name_str, strlen(file_name_str)))
7116
    {
7117
      build_table_filename(name, sizeof(name), db, file_name_str, reg_ext, 0);
7118
      if (my_access(name, F_OK))
unknown's avatar
unknown committed
7119
      {
7120
        DBUG_PRINT("info", ("%s must be discovered", file_name_str));
unknown's avatar
unknown committed
7121 7122
        // File is in list of ndb tables and not in ok_tables
        // This table need to be created
7123
        create_list.push_back(thd->strdup(file_name_str));
unknown's avatar
unknown committed
7124
      }
unknown's avatar
unknown committed
7125 7126
    }
  }
7127

unknown's avatar
unknown committed
7128 7129 7130 7131
  if (!global_read_lock)
  {
    // Delete old files
    List_iterator_fast<char> it3(delete_list);
7132
    while ((file_name_str= it3++))
7133
    {
7134
      DBUG_PRINT("info", ("Remove table %s/%s", db, file_name_str));
unknown's avatar
unknown committed
7135 7136 7137 7138
      // Delete the table and all related files
      TABLE_LIST table_list;
      bzero((char*) &table_list,sizeof(table_list));
      table_list.db= (char*) db;
7139
      table_list.alias= table_list.table_name= (char*)file_name_str;
7140
      (void)mysql_rm_table_part2(thd, &table_list,
7141 7142 7143
                                 FALSE,   /* if_exists */
                                 FALSE,   /* drop_temporary */ 
                                 FALSE,   /* drop_view */
7144
                                 TRUE     /* dont_log_query*/);
7145

7146 7147
      /* Clear error message that is returned when table is deleted */
      thd->clear_error();
7148 7149 7150
    }
  }

7151
  pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
7152 7153
  // Create new files
  List_iterator_fast<char> it2(create_list);
7154
  while ((file_name_str=it2++))
unknown's avatar
unknown committed
7155
  {  
7156 7157 7158 7159 7160 7161 7162 7163
    DBUG_PRINT("info", ("Table %s need discovery", file_name_str));
    if (ndb_create_table_from_engine(thd, db, file_name_str) == 0)
    {
      LEX_STRING *tmp_file_name= 0;
      tmp_file_name= thd->make_lex_string(tmp_file_name, file_name_str,
                                          strlen(file_name_str), TRUE);
      files->push_back(tmp_file_name); 
    }
unknown's avatar
unknown committed
7164 7165
  }

unknown's avatar
unknown committed
7166
  pthread_mutex_unlock(&LOCK_open);
7167

unknown's avatar
unknown committed
7168
  hash_free(&ok_tables);
7169
  hash_free(&ndb_tables);
7170 7171 7172 7173 7174 7175 7176

  // Delete schema file from files
  if (!strcmp(db, NDB_REP_DB))
  {
    uint count = 0;
    while (count++ < files->elements)
    {
7177 7178
      file_name = (LEX_STRING *)files->pop();
      if (!strcmp(file_name->str, NDB_SCHEMA_TABLE))
7179 7180 7181 7182 7183 7184 7185 7186
      {
        DBUG_PRINT("info", ("skip %s.%s table, it should be hidden to user",
                   NDB_REP_DB, NDB_SCHEMA_TABLE));
        continue;
      }
      files->push_back(file_name); 
    }
  }
7187
  } // extra bracket to avoid gcc 2.95.3 warning
7188
  DBUG_RETURN(0);    
unknown's avatar
unknown committed
7189 7190 7191 7192 7193 7194 7195 7196
}


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

7197 7198 7199
/* Call back after cluster connect */
static int connect_callback()
{
unknown's avatar
unknown committed
7200
  pthread_mutex_lock(&LOCK_ndb_util_thread);
7201
  update_status_variables(g_ndb_cluster_connection);
unknown's avatar
unknown committed
7202 7203 7204 7205 7206 7207 7208

  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
7209
  pthread_cond_signal(&COND_ndb_util_thread);
unknown's avatar
unknown committed
7210
  pthread_mutex_unlock(&LOCK_ndb_util_thread);
7211 7212 7213
  return 0;
}

7214
extern int ndb_dictionary_is_mysqld;
unknown's avatar
unknown committed
7215
extern pthread_mutex_t LOCK_plugin;
unknown's avatar
unknown committed
7216

7217
static int ndbcluster_init(void *p)
unknown's avatar
unknown committed
7218
{
unknown's avatar
unknown committed
7219
  int res;
unknown's avatar
unknown committed
7220
  DBUG_ENTER("ndbcluster_init");
7221

unknown's avatar
unknown committed
7222 7223 7224
  if (ndbcluster_inited)
    DBUG_RETURN(FALSE);

unknown's avatar
unknown committed
7225 7226 7227 7228 7229 7230 7231
  /*
    Below we create new THD's. They'll need LOCK_plugin, but it's taken now by
    plugin initialization code. Release it to avoid deadlocks.  It's safe, as
    there're no threads that may concurrently access plugin control structures.
  */
  pthread_mutex_unlock(&LOCK_plugin);

unknown's avatar
unknown committed
7232 7233 7234 7235 7236 7237
  pthread_mutex_init(&ndbcluster_mutex,MY_MUTEX_INIT_FAST);
  pthread_mutex_init(&LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST);
  pthread_cond_init(&COND_ndb_util_thread, NULL);
  pthread_cond_init(&COND_ndb_util_ready, NULL);
  ndb_util_thread_running= -1;
  ndbcluster_terminating= 0;
7238
  ndb_dictionary_is_mysqld= 1;
7239 7240 7241 7242
  ndbcluster_hton= (handlerton *)p;

  {
    handlerton *h= ndbcluster_hton;
unknown's avatar
unknown committed
7243
    h->state=            SHOW_OPTION_YES;
7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255
    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
7256 7257 7258
#ifdef HAVE_NDB_BINLOG
    ndbcluster_binlog_init_handlerton();
#endif
7259 7260 7261 7262
    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
7263 7264
  }

unknown's avatar
unknown committed
7265 7266 7267
  // Initialize ndb interface
  ndb_init_internal();

7268
  // Set connectstring if specified
7269 7270
  if (opt_ndbcluster_connectstring != 0)
    DBUG_PRINT("connectstring", ("%s", opt_ndbcluster_connectstring));     
7271
  if ((g_ndb_cluster_connection=
7272
       new Ndb_cluster_connection(opt_ndbcluster_connectstring)) == 0)
7273
  {
7274
    DBUG_PRINT("error",("Ndb_cluster_connection(%s)",
7275
                        opt_ndbcluster_connectstring));
7276
    my_errno= HA_ERR_OUT_OF_MEM;
unknown's avatar
unknown committed
7277
    goto ndbcluster_init_error;
7278
  }
unknown's avatar
ndb:  
unknown committed
7279 7280
  {
    char buf[128];
7281
    my_snprintf(buf, sizeof(buf), "mysqld --server-id=%lu", server_id);
unknown's avatar
ndb:  
unknown committed
7282 7283
    g_ndb_cluster_connection->set_name(buf);
  }
7284 7285 7286
  g_ndb_cluster_connection->set_optimized_node_selection
    (opt_ndb_optimized_node_selection);

unknown's avatar
unknown committed
7287
  // Create a Ndb object to open the connection  to NDB
7288 7289 7290
  if ( (g_ndb= new Ndb(g_ndb_cluster_connection, "sys")) == 0 )
  {
    DBUG_PRINT("error", ("failed to create global ndb object"));
7291
    my_errno= HA_ERR_OUT_OF_MEM;
7292 7293
    goto ndbcluster_init_error;
  }
unknown's avatar
unknown committed
7294 7295 7296
  if (g_ndb->init() != 0)
  {
    ERR_PRINT (g_ndb->getNdbError());
unknown's avatar
unknown committed
7297
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
7298
  }
unknown's avatar
unknown committed
7299

unknown's avatar
unknown committed
7300
  if ((res= g_ndb_cluster_connection->connect(0,0,0)) == 0)
unknown's avatar
unknown committed
7301
  {
7302
    connect_callback();
unknown's avatar
unknown committed
7303
    DBUG_PRINT("info",("NDBCLUSTER storage engine at %s on port %d",
7304 7305
                       g_ndb_cluster_connection->get_connected_host(),
                       g_ndb_cluster_connection->get_connected_port()));
7306
    g_ndb_cluster_connection->wait_until_ready(10,3);
unknown's avatar
unknown committed
7307
  } 
unknown's avatar
unknown committed
7308
  else if (res == 1)
unknown's avatar
unknown committed
7309
  {
7310
    if (g_ndb_cluster_connection->start_connect_thread(connect_callback)) 
7311
    {
unknown's avatar
unknown committed
7312
      DBUG_PRINT("error", ("g_ndb_cluster_connection->start_connect_thread()"));
unknown's avatar
unknown committed
7313 7314
      goto ndbcluster_init_error;
    }
7315
#ifndef DBUG_OFF
unknown's avatar
unknown committed
7316 7317
    {
      char buf[1024];
7318
      DBUG_PRINT("info",
7319 7320 7321 7322
                 ("NDBCLUSTER storage engine not started, "
                  "will connect using %s",
                  g_ndb_cluster_connection->
                  get_connectstring(buf,sizeof(buf))));
unknown's avatar
unknown committed
7323
    }
7324
#endif
unknown's avatar
unknown committed
7325
  }
unknown's avatar
unknown committed
7326
  else
unknown's avatar
unknown committed
7327 7328 7329
  {
    DBUG_ASSERT(res == -1);
    DBUG_PRINT("error", ("permanent error"));
unknown's avatar
unknown committed
7330
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
7331
  }
unknown's avatar
unknown committed
7332
  
unknown's avatar
unknown committed
7333 7334
  (void) hash_init(&ndbcluster_open_tables,system_charset_info,32,0,0,
                   (hash_get_key) ndbcluster_get_key,0,0);
unknown's avatar
unknown committed
7335 7336
#ifdef HAVE_NDB_BINLOG
  /* start the ndb injector thread */
7337 7338
  if (ndbcluster_binlog_start())
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
7339
#endif /* HAVE_NDB_BINLOG */
unknown's avatar
unknown committed
7340

unknown's avatar
unknown committed
7341
  ndb_cache_check_time = opt_ndb_cache_check_time;
unknown's avatar
Merge  
unknown committed
7342 7343 7344 7345 7346
  // 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"));
7347 7348 7349 7350
    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
unknown committed
7351
    pthread_cond_destroy(&COND_ndb_util_ready);
unknown's avatar
Merge  
unknown committed
7352 7353
    goto ndbcluster_init_error;
  }
unknown's avatar
unknown committed
7354

7355 7356
  /* Wait for the util thread to start */
  pthread_mutex_lock(&LOCK_ndb_util_thread);
unknown's avatar
unknown committed
7357 7358
  while (ndb_util_thread_running < 0)
    pthread_cond_wait(&COND_ndb_util_ready, &LOCK_ndb_util_thread);
7359
  pthread_mutex_unlock(&LOCK_ndb_util_thread);
unknown's avatar
unknown committed
7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370
  
  if (!ndb_util_thread_running)
  {
    DBUG_PRINT("error", ("ndb utility thread exited prematurely"));
    hash_free(&ndbcluster_open_tables);
    pthread_mutex_destroy(&ndbcluster_mutex);
    pthread_mutex_destroy(&LOCK_ndb_util_thread);
    pthread_cond_destroy(&COND_ndb_util_thread);
    pthread_cond_destroy(&COND_ndb_util_ready);
    goto ndbcluster_init_error;
  }
7371

unknown's avatar
unknown committed
7372 7373
  pthread_mutex_lock(&LOCK_plugin);

unknown's avatar
unknown committed
7374
  ndbcluster_inited= 1;
7375
  DBUG_RETURN(FALSE);
unknown's avatar
Merge  
unknown committed
7376

7377
ndbcluster_init_error:
unknown's avatar
unknown committed
7378
  if (g_ndb)
7379 7380 7381 7382 7383
    delete g_ndb;
  g_ndb= NULL;
  if (g_ndb_cluster_connection)
    delete g_ndb_cluster_connection;
  g_ndb_cluster_connection= NULL;
7384 7385
  ndbcluster_hton->state= SHOW_OPTION_DISABLED;               // If we couldn't use handler

unknown's avatar
unknown committed
7386 7387
  pthread_mutex_lock(&LOCK_plugin);

7388
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7389 7390
}

7391
static int ndbcluster_end(handlerton *hton, ha_panic_function type)
unknown's avatar
unknown committed
7392 7393
{
  DBUG_ENTER("ndbcluster_end");
unknown's avatar
Merge  
unknown committed
7394

7395 7396
  if (!ndbcluster_inited)
    DBUG_RETURN(0);
7397 7398 7399
  ndbcluster_inited= 0;

  /* wait for util thread to finish */
unknown's avatar
unknown committed
7400
  sql_print_information("Stopping Cluster Utility thread");
7401
  pthread_mutex_lock(&LOCK_ndb_util_thread);
unknown's avatar
unknown committed
7402 7403 7404 7405
  ndbcluster_terminating= 1;
  pthread_cond_signal(&COND_ndb_util_thread);
  while (ndb_util_thread_running > 0)
    pthread_cond_wait(&COND_ndb_util_ready, &LOCK_ndb_util_thread);
7406 7407
  pthread_mutex_unlock(&LOCK_ndb_util_thread);

7408

7409 7410 7411
#ifdef HAVE_NDB_BINLOG
  {
    pthread_mutex_lock(&ndbcluster_mutex);
7412
    while (ndbcluster_open_tables.records)
7413 7414
    {
      NDB_SHARE *share=
7415
        (NDB_SHARE*) hash_element(&ndbcluster_open_tables, 0);
7416 7417 7418 7419
#ifndef DBUG_OFF
      fprintf(stderr, "NDB: table share %s with use_count %d not freed\n",
              share->key, share->use_count);
#endif
7420
      ndbcluster_real_free_share(&share);
7421 7422 7423 7424 7425 7426
    }
    pthread_mutex_unlock(&ndbcluster_mutex);
  }
#endif
  hash_free(&ndbcluster_open_tables);

unknown's avatar
unknown committed
7427
  if (g_ndb)
7428 7429
  {
#ifndef DBUG_OFF
unknown's avatar
unknown committed
7430 7431
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
7432 7433 7434 7435 7436 7437 7438 7439 7440 7441
    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
7442
    delete g_ndb;
unknown's avatar
unknown committed
7443
    g_ndb= NULL;
7444
  }
unknown's avatar
unknown committed
7445
  delete g_ndb_cluster_connection;
7446
  g_ndb_cluster_connection= NULL;
7447

unknown's avatar
unknown committed
7448 7449 7450
  // cleanup ndb interface
  ndb_end_internal();

unknown's avatar
unknown committed
7451
  pthread_mutex_destroy(&ndbcluster_mutex);
unknown's avatar
Merge  
unknown committed
7452 7453
  pthread_mutex_destroy(&LOCK_ndb_util_thread);
  pthread_cond_destroy(&COND_ndb_util_thread);
unknown's avatar
unknown committed
7454
  pthread_cond_destroy(&COND_ndb_util_ready);
unknown's avatar
unknown committed
7455 7456 7457
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
7458 7459 7460
void ha_ndbcluster::print_error(int error, myf errflag)
{
  DBUG_ENTER("ha_ndbcluster::print_error");
unknown's avatar
unknown committed
7461
  DBUG_PRINT("enter", ("error: %d", error));
unknown's avatar
unknown committed
7462 7463

  if (error == HA_ERR_NO_PARTITION_FOUND)
unknown's avatar
unknown committed
7464
    m_part_info->print_no_partition_found(table);
unknown's avatar
unknown committed
7465 7466 7467 7468 7469 7470
  else
    handler::print_error(error, errflag);
  DBUG_VOID_RETURN;
}


7471 7472 7473 7474 7475
/*
  Static error print function called from
  static handler method ndbcluster_commit
  and ndbcluster_rollback
*/
7476 7477

void ndbcluster_print_error(int error, const NdbOperation *error_op)
7478
{
7479
  DBUG_ENTER("ndbcluster_print_error");
unknown's avatar
unknown committed
7480
  TABLE_SHARE share;
7481
  const char *tab_name= (error_op) ? error_op->getTableName() : "";
unknown's avatar
unknown committed
7482 7483 7484 7485
  share.db.str= (char*) "";
  share.db.length= 0;
  share.table_name.str= (char *) tab_name;
  share.table_name.length= strlen(tab_name);
7486
  ha_ndbcluster error_handler(ndbcluster_hton, &share);
7487
  error_handler.print_error(error, MYF(0));
unknown's avatar
unknown committed
7488
  DBUG_VOID_RETURN;
7489
}
unknown's avatar
unknown committed
7490

7491 7492 7493
/**
 * Set a given location from full pathname to database name
 *
unknown's avatar
unknown committed
7494
 */
7495
void ha_ndbcluster::set_dbname(const char *path_name, char *dbname)
unknown's avatar
unknown committed
7496
{
unknown's avatar
unknown committed
7497 7498 7499 7500
  char *end, *ptr, *tmp_name;
  char tmp_buff[FN_REFLEN];
 
  tmp_name= tmp_buff;
unknown's avatar
unknown committed
7501
  /* Scan name from the end */
7502 7503 7504 7505 7506 7507
  ptr= strend(path_name)-1;
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  ptr--;
  end= ptr;
unknown's avatar
unknown committed
7508 7509 7510 7511
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  uint name_len= end - ptr;
unknown's avatar
unknown committed
7512 7513
  memcpy(tmp_name, ptr + 1, name_len);
  tmp_name[name_len]= '\0';
unknown's avatar
unknown committed
7514 7515
#ifdef __WIN__
  /* Put to lower case */
7516
  
unknown's avatar
unknown committed
7517
  ptr= tmp_name;
unknown's avatar
unknown committed
7518 7519
  
  while (*ptr != '\0') {
7520
    *ptr= tolower(*ptr);
unknown's avatar
unknown committed
7521 7522 7523
    ptr++;
  }
#endif
unknown's avatar
unknown committed
7524
  filename_to_tablename(tmp_name, dbname, FN_REFLEN);
unknown's avatar
unknown committed
7525 7526
}

7527 7528 7529 7530 7531 7532 7533 7534 7535
/*
  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
7536 7537 7538 7539 7540 7541 7542
/**
 * 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
7543 7544 7545 7546
  char *end, *ptr, *tmp_name;
  char tmp_buff[FN_REFLEN];

  tmp_name= tmp_buff;
unknown's avatar
unknown committed
7547
  /* Scan name from the end */
7548 7549
  end= strend(path_name)-1;
  ptr= end;
unknown's avatar
unknown committed
7550 7551 7552
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
7553
  uint name_len= end - ptr;
unknown's avatar
unknown committed
7554 7555
  memcpy(tmp_name, ptr + 1, end - ptr);
  tmp_name[name_len]= '\0';
unknown's avatar
unknown committed
7556 7557
#ifdef __WIN__
  /* Put to lower case */
unknown's avatar
unknown committed
7558
  ptr= tmp_name;
unknown's avatar
unknown committed
7559 7560 7561 7562 7563 7564
  
  while (*ptr != '\0') {
    *ptr= tolower(*ptr);
    ptr++;
  }
#endif
unknown's avatar
unknown committed
7565
  filename_to_tablename(tmp_name, tabname, FN_REFLEN);
unknown's avatar
unknown committed
7566 7567 7568
}

/*
7569
  Set m_tabname from full pathname to table file 
unknown's avatar
unknown committed
7570 7571
 */

7572
void ha_ndbcluster::set_tabname(const char *path_name)
unknown's avatar
unknown committed
7573
{
7574
  set_tabname(path_name, m_tabname);
unknown's avatar
unknown committed
7575 7576 7577 7578
}


ha_rows 
unknown's avatar
unknown committed
7579 7580 7581 7582
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
7583
  uint key_length= key_info->key_length;
7584
  NDB_INDEX_TYPE idx_type= get_index_type(inx);  
unknown's avatar
unknown committed
7585 7586

  DBUG_ENTER("records_in_range");
7587 7588 7589 7590 7591 7592 7593 7594 7595 7596 7597 7598 7599
  // 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);
  
7600 7601 7602 7603 7604 7605
  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];
7606
    const NDBINDEX* index= d.index;
7607 7608 7609 7610 7611 7612 7613 7614 7615 7616
    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;
unknown's avatar
unknown committed
7617 7618
      Ndb_local_table_statistics *ndb_info= m_table_info;
      if (ndb_info->records != ~(ha_rows)0 && ndb_info->records != 0)
7619
      {
unknown's avatar
unknown committed
7620 7621
        table_rows = ndb_info->records;
        DBUG_PRINT("info", ("use info->records: %lu", (ulong) table_rows));
7622 7623 7624 7625
      }
      else
      {
        Ndb_statistics stat;
unknown's avatar
unknown committed
7626
        if ((res=ndb_get_table_statistics(this, TRUE, ndb, m_table, &stat)))
7627 7628
          break;
        table_rows=stat.row_count;
unknown's avatar
unknown committed
7629
        DBUG_PRINT("info", ("use db row_count: %lu", (ulong) table_rows));
7630 7631 7632 7633 7634 7635 7636 7637 7638 7639
        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
7640 7641
      if ((trans=m_active_trans) == NULL || 
	  trans->commitStatus() != NdbTransaction::Started)
7642 7643 7644 7645 7646 7647 7648 7649 7650 7651
      {
        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 };
unknown's avatar
unknown committed
7652
      if ((res=set_bounds(op, inx, TRUE, keys)) != 0)
7653 7654 7655 7656 7657 7658 7659 7660 7661 7662 7663 7664 7665 7666 7667 7668 7669 7670 7671 7672 7673 7674 7675 7676 7677
        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);
  }

7678
  DBUG_RETURN(10); /* Good guess when you don't know anything */
unknown's avatar
unknown committed
7679 7680
}

7681
ulonglong ha_ndbcluster::table_flags(void) const
7682
{
unknown's avatar
unknown committed
7683 7684
  THD *thd= current_thd;
  ulonglong f= m_table_flags;
7685
  if (m_ha_not_exact_count)
unknown's avatar
unknown committed
7686 7687 7688 7689 7690 7691 7692 7693
    f= f & ~HA_STATS_RECORDS_IS_EXACT;
  /*
    To allow for logging of ndb tables during stmt based logging;
    flag cabablity, but also turn off flag for OWN_BINLOGGING
  */
  if (thd->variables.binlog_format == BINLOG_FORMAT_STMT)
    f= (f | HA_BINLOG_STMT_CAPABLE) & ~HA_HAS_OWN_BINLOGGING;
  return f;
7694 7695 7696
}
const char * ha_ndbcluster::table_type() const 
{
7697
  return("NDBCLUSTER");
7698 7699 7700 7701 7702 7703 7704 7705 7706 7707 7708 7709 7710 7711 7712 7713 7714
}
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
7715 7716 7717 7718
uint ha_ndbcluster::max_supported_key_part_length() const
{
  return NDB_MAX_KEY_SIZE;
}
7719 7720 7721 7722 7723 7724 7725 7726 7727 7728 7729 7730 7731 7732 7733 7734 7735 7736 7737 7738 7739
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
7740

7741 7742
uint8 ha_ndbcluster::table_cache_type()
{
unknown's avatar
Merge  
unknown committed
7743 7744 7745 7746 7747 7748
  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,
7749
                         Uint64 *commit_count)
unknown's avatar
Merge  
unknown committed
7750
{
7751 7752
  char name[FN_REFLEN];
  NDB_SHARE *share;
unknown's avatar
unknown committed
7753 7754
  DBUG_ENTER("ndb_get_commitcount");

7755
  build_table_filename(name, sizeof(name), dbname, tabname, "", 0);
7756 7757 7758
  DBUG_PRINT("enter", ("name: %s", name));
  pthread_mutex_lock(&ndbcluster_mutex);
  if (!(share=(NDB_SHARE*) hash_search(&ndbcluster_open_tables,
7759
                                       (uchar*) name,
7760 7761 7762
                                       strlen(name))))
  {
    pthread_mutex_unlock(&ndbcluster_mutex);
unknown's avatar
unknown committed
7763
    DBUG_PRINT("info", ("Table %s not found in ndbcluster_open_tables", name));
7764 7765
    DBUG_RETURN(1);
  }
7766
  /* ndb_share reference temporary, free below */
7767
  share->use_count++;
7768 7769
  DBUG_PRINT("NDB_SHARE", ("%s temporary  use_count: %u",
                           share->key, share->use_count));
7770 7771 7772
  pthread_mutex_unlock(&ndbcluster_mutex);

  pthread_mutex_lock(&share->mutex);
unknown's avatar
Merge  
unknown committed
7773 7774
  if (ndb_cache_check_time > 0)
  {
7775
    if (share->commit_count != 0)
unknown's avatar
Merge  
unknown committed
7776
    {
7777
      *commit_count= share->commit_count;
unknown's avatar
unknown committed
7778
#ifndef DBUG_OFF
unknown's avatar
unknown committed
7779
      char buff[22];
unknown's avatar
unknown committed
7780
#endif
unknown's avatar
unknown committed
7781 7782
      DBUG_PRINT("info", ("Getting commit_count: %s from share",
                          llstr(share->commit_count, buff)));
7783
      pthread_mutex_unlock(&share->mutex);
7784 7785 7786
      /* ndb_share reference temporary free */
      DBUG_PRINT("NDB_SHARE", ("%s temporary free  use_count: %u",
                               share->key, share->use_count));
unknown's avatar
unknown committed
7787
      free_share(&share);
7788
      DBUG_RETURN(0);
unknown's avatar
Merge  
unknown committed
7789 7790
    }
  }
7791
  DBUG_PRINT("info", ("Get commit_count from NDB"));
unknown's avatar
Merge  
unknown committed
7792 7793 7794
  Ndb *ndb;
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(1);
7795 7796 7797 7798
  if (ndb->setDatabaseName(dbname))
  {
    ERR_RETURN(ndb->getNdbError());
  }
7799 7800
  uint lock= share->commit_count_lock;
  pthread_mutex_unlock(&share->mutex);
unknown's avatar
Merge  
unknown committed
7801 7802

  struct Ndb_statistics stat;
7803
  {
7804 7805
    Ndb_table_guard ndbtab_g(ndb->getDictionary(), tabname);
    if (ndbtab_g.get_table() == 0
unknown's avatar
unknown committed
7806
        || ndb_get_table_statistics(NULL, FALSE, ndb, ndbtab_g.get_table(), &stat))
7807
    {
7808 7809 7810
      /* ndb_share reference temporary free */
      DBUG_PRINT("NDB_SHARE", ("%s temporary free  use_count: %u",
                               share->key, share->use_count));
7811 7812 7813
      free_share(&share);
      DBUG_RETURN(1);
    }
7814 7815 7816
  }

  pthread_mutex_lock(&share->mutex);
unknown's avatar
unknown committed
7817
  if (share->commit_count_lock == lock)
7818
  {
unknown's avatar
unknown committed
7819
#ifndef DBUG_OFF
unknown's avatar
unknown committed
7820
    char buff[22];
unknown's avatar
unknown committed
7821
#endif
unknown's avatar
unknown committed
7822 7823
    DBUG_PRINT("info", ("Setting commit_count to %s",
                        llstr(stat.commit_count, buff)));
7824 7825 7826 7827 7828 7829 7830 7831 7832
    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);
7833 7834 7835
  /* ndb_share reference temporary free */
  DBUG_PRINT("NDB_SHARE", ("%s temporary free  use_count: %u",
                           share->key, share->use_count));
unknown's avatar
unknown committed
7836
  free_share(&share);
unknown's avatar
Merge  
unknown committed
7837 7838 7839 7840 7841 7842 7843 7844 7845 7846 7847 7848 7849 7850 7851 7852 7853 7854 7855 7856 7857 7858 7859 7860 7861 7862 7863 7864 7865 7866 7867 7868 7869 7870 7871 7872
  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,
7873 7874
                                   char *full_name, uint full_name_len,
                                   ulonglong *engine_data)
unknown's avatar
Merge  
unknown committed
7875 7876 7877 7878 7879
{
  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
7880
#ifndef DBUG_OFF
unknown's avatar
unknown committed
7881
  char buff[22], buff2[22];
unknown's avatar
unknown committed
7882
#endif
unknown's avatar
unknown committed
7883
  DBUG_ENTER("ndbcluster_cache_retrieval_allowed");
7884 7885
  DBUG_PRINT("enter", ("dbname: %s, tabname: %s, is_autocommit: %d",
                       dbname, tabname, is_autocommit));
unknown's avatar
Merge  
unknown committed
7886 7887

  if (!is_autocommit)
7888 7889
  {
    DBUG_PRINT("exit", ("No, don't use cache in transaction"));
unknown's avatar
Merge  
unknown committed
7890
    DBUG_RETURN(FALSE);
7891
  }
unknown's avatar
Merge  
unknown committed
7892 7893 7894

  if (ndb_get_commitcount(thd, dbname, tabname, &commit_count))
  {
7895 7896
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, could not retrieve commit_count"));
unknown's avatar
Merge  
unknown committed
7897 7898
    DBUG_RETURN(FALSE);
  }
unknown's avatar
unknown committed
7899 7900
  DBUG_PRINT("info", ("*engine_data: %s, commit_count: %s",
                      llstr(*engine_data, buff), llstr(commit_count, buff2)));
7901
  if (commit_count == 0)
unknown's avatar
Merge  
unknown committed
7902
  {
7903 7904
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, local commit has been performed"));
unknown's avatar
Merge  
unknown committed
7905 7906
    DBUG_RETURN(FALSE);
  }
7907 7908 7909 7910 7911 7912
  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
7913

unknown's avatar
unknown committed
7914 7915
  DBUG_PRINT("exit", ("OK to use cache, engine_data: %s",
                      llstr(*engine_data, buff)));
unknown's avatar
Merge  
unknown committed
7916 7917 7918 7919 7920 7921 7922 7923 7924 7925 7926 7927 7928 7929 7930 7931 7932 7933 7934 7935 7936 7937 7938 7939 7940 7941 7942 7943
  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,
7944 7945 7946
                                          char *full_name, uint full_name_len,
                                          qc_engine_callback *engine_callback,
                                          ulonglong *engine_data)
unknown's avatar
Merge  
unknown committed
7947
{
unknown's avatar
unknown committed
7948
  Uint64 commit_count;
unknown's avatar
unknown committed
7949
#ifndef DBUG_OFF
unknown's avatar
unknown committed
7950
  char buff[22];
unknown's avatar
unknown committed
7951
#endif
unknown's avatar
Merge  
unknown committed
7952
  bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
unknown's avatar
unknown committed
7953
  DBUG_ENTER("ha_ndbcluster::register_query_cache_table");
7954 7955 7956
  DBUG_PRINT("enter",("dbname: %s, tabname: %s, is_autocommit: %d",
		      m_dbname, m_tabname, is_autocommit));

unknown's avatar
Merge  
unknown committed
7957
  if (!is_autocommit)
7958
  {
unknown's avatar
unknown committed
7959
    DBUG_PRINT("exit", ("Can't register table during transaction"));
unknown's avatar
Merge  
unknown committed
7960
    DBUG_RETURN(FALSE);
7961
  }
unknown's avatar
Merge  
unknown committed
7962 7963 7964 7965

  if (ndb_get_commitcount(thd, m_dbname, m_tabname, &commit_count))
  {
    *engine_data= 0;
unknown's avatar
unknown committed
7966
    DBUG_PRINT("exit", ("Error, could not get commitcount"));
unknown's avatar
Merge  
unknown committed
7967 7968 7969 7970
    DBUG_RETURN(FALSE);
  }
  *engine_data= commit_count;
  *engine_callback= ndbcluster_cache_retrieval_allowed;
unknown's avatar
unknown committed
7971
  DBUG_PRINT("exit", ("commit_count: %s", llstr(commit_count, buff)));
7972
  DBUG_RETURN(commit_count > 0);
7973
}
unknown's avatar
unknown committed
7974

unknown's avatar
Merge  
unknown committed
7975

unknown's avatar
unknown committed
7976
/*
unknown's avatar
Merge  
unknown committed
7977
  Handling the shared NDB_SHARE structure that is needed to
unknown's avatar
unknown committed
7978 7979 7980 7981 7982 7983
  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.
 */

7984
static uchar *ndbcluster_get_key(NDB_SHARE *share, size_t *length,
7985
                                my_bool not_used __attribute__((unused)))
unknown's avatar
unknown committed
7986
{
unknown's avatar
unknown committed
7987
  *length= share->key_length;
7988
  return (uchar*) share->key;
unknown's avatar
unknown committed
7989 7990
}

unknown's avatar
unknown committed
7991

unknown's avatar
unknown committed
7992
#ifndef DBUG_OFF
unknown's avatar
unknown committed
7993 7994

static void print_share(const char* where, NDB_SHARE* share)
unknown's avatar
unknown committed
7995
{
unknown's avatar
unknown committed
7996
  fprintf(DBUG_FILE,
unknown's avatar
unknown committed
7997
          "%s %s.%s: use_count: %u, commit_count: %lu\n",
unknown's avatar
unknown committed
7998
          where, share->db, share->table_name, share->use_count,
unknown's avatar
unknown committed
7999
          (ulong) share->commit_count);
unknown's avatar
unknown committed
8000 8001 8002 8003
  fprintf(DBUG_FILE,
          "  - key: %s, key_length: %d\n",
          share->key, share->key_length);

unknown's avatar
unknown committed
8004
#ifdef HAVE_NDB_BINLOG
unknown's avatar
unknown committed
8005 8006 8007 8008 8009
  if (share->table)
    fprintf(DBUG_FILE,
            "  - share->table: %p %s.%s\n",
            share->table, share->table->s->db.str,
            share->table->s->table_name.str);
unknown's avatar
unknown committed
8010
#endif
unknown's avatar
unknown committed
8011
}
unknown's avatar
unknown committed
8012 8013 8014 8015 8016 8017 8018 8019 8020 8021 8022 8023 8024


static void print_ndbcluster_open_tables()
{
  DBUG_LOCK_FILE;
  fprintf(DBUG_FILE, ">ndbcluster_open_tables\n");
  for (uint i= 0; i < ndbcluster_open_tables.records; i++)
    print_share("",
                (NDB_SHARE*)hash_element(&ndbcluster_open_tables, i));
  fprintf(DBUG_FILE, "<ndbcluster_open_tables\n");
  DBUG_UNLOCK_FILE;
}

unknown's avatar
unknown committed
8025 8026
#endif

unknown's avatar
unknown committed
8027 8028 8029 8030 8031 8032 8033 8034 8035 8036 8037

#define dbug_print_open_tables()                \
  DBUG_EXECUTE("info",                          \
               print_ndbcluster_open_tables(););

#define dbug_print_share(t, s)                  \
  DBUG_LOCK_FILE;                               \
  DBUG_EXECUTE("info",                          \
               print_share((t), (s)););         \
  DBUG_UNLOCK_FILE;

unknown's avatar
unknown committed
8038

unknown's avatar
unknown committed
8039 8040 8041 8042 8043 8044 8045 8046 8047 8048 8049 8050 8051
#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)
{
8052
  THD *thd= current_thd;
unknown's avatar
unknown committed
8053 8054 8055
  static ulong trailing_share_id= 0;
  DBUG_ENTER("handle_trailing_share");

8056
  /* ndb_share reference temporary, free below */
unknown's avatar
unknown committed
8057
  ++share->use_count;
8058 8059
  DBUG_PRINT("NDB_SHARE", ("%s temporary  use_count: %u",
                           share->key, share->use_count));
unknown's avatar
unknown committed
8060 8061
  pthread_mutex_unlock(&ndbcluster_mutex);

8062 8063 8064 8065
  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;
8066
  safe_mutex_assert_owner(&LOCK_open);
8067
  close_cached_tables(thd, 0, &table_list, TRUE);
unknown's avatar
unknown committed
8068 8069

  pthread_mutex_lock(&ndbcluster_mutex);
8070 8071 8072
  /* ndb_share reference temporary free */
  DBUG_PRINT("NDB_SHARE", ("%s temporary free  use_count: %u",
                           share->key, share->use_count));
unknown's avatar
unknown committed
8073 8074
  if (!--share->use_count)
  {
8075
    if (ndb_extra_logging)
8076 8077
      sql_print_information("NDB_SHARE: trailing share "
                            "%s(connect_count: %u) "
8078 8079 8080 8081 8082 8083
                            "released by close_cached_tables at "
                            "connect_count: %u",
                            share->key,
                            share->connect_count,
                            g_ndb_cluster_connection->get_connect_count());
    ndbcluster_real_free_share(&share);
unknown's avatar
unknown committed
8084 8085 8086 8087 8088 8089 8090
    DBUG_RETURN(0);
  }

  /*
    share still exists, if share has not been dropped by server
    release that share
  */
8091
  if (share->state != NSS_DROPPED)
unknown's avatar
unknown committed
8092
  {
8093 8094 8095 8096 8097 8098 8099 8100 8101 8102 8103 8104 8105 8106 8107 8108 8109 8110 8111
    share->state= NSS_DROPPED;
    /* ndb_share reference create free */
    DBUG_PRINT("NDB_SHARE", ("%s create free  use_count: %u",
                             share->key, share->use_count));
    --share->use_count;

    if (share->use_count == 0)
    {
      if (ndb_extra_logging)
        sql_print_information("NDB_SHARE: trailing share "
                              "%s(connect_count: %u) "
                              "released after NSS_DROPPED check "
                              "at connect_count: %u",
                              share->key,
                              share->connect_count,
                              g_ndb_cluster_connection->get_connect_count());
      ndbcluster_real_free_share(&share);
      DBUG_RETURN(0);
    }
unknown's avatar
unknown committed
8112 8113
  }

unknown's avatar
unknown committed
8114 8115 8116
  sql_print_warning("NDB_SHARE: %s already exists  use_count=%d."
                    " Moving away for safety, but possible memleak.",
                    share->key, share->use_count);
unknown's avatar
unknown committed
8117 8118
  dbug_print_open_tables();

8119 8120 8121
  /*
    Ndb share has not been released as it should
  */
unknown's avatar
unknown committed
8122
#ifdef NOT_YET
8123
  DBUG_ASSERT(FALSE);
unknown's avatar
unknown committed
8124
#endif
8125

unknown's avatar
unknown committed
8126 8127 8128 8129 8130
  /*
    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
  */
8131
  hash_delete(&ndbcluster_open_tables, (uchar*) share);
unknown's avatar
unknown committed
8132 8133 8134 8135 8136 8137 8138 8139 8140

  /*
    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)
    {
8141
      share->key= (char*) alloc_root(&share->mem_root, min_key_length + 1);
unknown's avatar
unknown committed
8142 8143 8144
      share->key_length= min_key_length;
    }
    share->key_length=
8145
      my_snprintf(share->key, min_key_length + 1, "#leak%lu",
unknown's avatar
unknown committed
8146 8147 8148
                  trailing_share_id++);
  }
  /* Keep it for possible the future trailing free */
8149
  my_hash_insert(&ndbcluster_open_tables, (uchar*) share);
unknown's avatar
unknown committed
8150 8151 8152 8153 8154 8155 8156 8157 8158 8159 8160 8161 8162 8163 8164

  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,
8165
                                     (uchar*) new_key, new_length)))
unknown's avatar
unknown committed
8166 8167 8168
    handle_trailing_share(tmp);

  /* remove the share from hash */
8169
  hash_delete(&ndbcluster_open_tables, (uchar*) share);
unknown's avatar
unknown committed
8170 8171 8172 8173 8174 8175 8176 8177 8178 8179
  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
  */
8180
  share->key= (char*) alloc_root(&share->mem_root, 2 * (new_length + 1));
unknown's avatar
unknown committed
8181 8182 8183
  strmov(share->key, new_key);
  share->key_length= new_length;

8184
  if (my_hash_insert(&ndbcluster_open_tables, (uchar*) share))
unknown's avatar
unknown committed
8185 8186 8187 8188 8189 8190
  {
    // 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;
8191
    if (my_hash_insert(&ndbcluster_open_tables, (uchar*) share))
unknown's avatar
unknown committed
8192 8193 8194 8195 8196 8197 8198 8199 8200 8201 8202 8203 8204 8205 8206 8207
    {
      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);

unknown's avatar
unknown committed
8208
  dbug_print_share("rename_share:", share);
8209
  if (share->table)
unknown's avatar
unknown committed
8210
  {
8211 8212 8213 8214 8215 8216 8217
    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
8218 8219 8220 8221 8222 8223 8224 8225 8226 8227
  }
  /* 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
8228 8229 8230 8231
/*
  Increase refcount on existing share.
  Always returns share and cannot fail.
*/
unknown's avatar
unknown committed
8232
NDB_SHARE *ndbcluster_get_share(NDB_SHARE *share)
unknown's avatar
unknown committed
8233 8234
{
  pthread_mutex_lock(&ndbcluster_mutex);
unknown's avatar
unknown committed
8235 8236 8237
  share->use_count++;

  dbug_print_open_tables();
unknown's avatar
unknown committed
8238
  dbug_print_share("ndbcluster_get_share:", share);
unknown's avatar
unknown committed
8239 8240 8241 8242
  pthread_mutex_unlock(&ndbcluster_mutex);
  return share;
}

unknown's avatar
unknown committed
8243

unknown's avatar
unknown committed
8244 8245 8246 8247 8248 8249 8250 8251 8252 8253 8254 8255 8256 8257
/*
  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
8258

unknown's avatar
unknown committed
8259 8260 8261
NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table,
                                bool create_if_not_exists,
                                bool have_lock)
unknown's avatar
unknown committed
8262 8263
{
  NDB_SHARE *share;
unknown's avatar
unknown committed
8264 8265 8266 8267
  uint length= (uint) strlen(key);
  DBUG_ENTER("ndbcluster_get_share");
  DBUG_PRINT("enter", ("key: '%s'", key));

unknown's avatar
unknown committed
8268 8269 8270
  if (!have_lock)
    pthread_mutex_lock(&ndbcluster_mutex);
  if (!(share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
8271
                                        (uchar*) key,
unknown's avatar
unknown committed
8272
                                        length)))
unknown's avatar
unknown committed
8273
  {
unknown's avatar
unknown committed
8274 8275 8276 8277 8278
    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
8279
      DBUG_RETURN(0);
unknown's avatar
unknown committed
8280 8281
    }
    if ((share= (NDB_SHARE*) my_malloc(sizeof(*share),
unknown's avatar
unknown committed
8282 8283
                                       MYF(MY_WME | MY_ZEROFILL))))
    {
unknown's avatar
unknown committed
8284 8285 8286 8287 8288
      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
8289
      share->state= NSS_INITIAL;
unknown's avatar
unknown committed
8290
      /* enough space for key, db, and table_name */
8291
      share->key= (char*) alloc_root(*root_ptr, 2 * (length + 1));
unknown's avatar
unknown committed
8292 8293
      share->key_length= length;
      strmov(share->key, key);
8294
      if (my_hash_insert(&ndbcluster_open_tables, (uchar*) share))
unknown's avatar
unknown committed
8295
      {
unknown's avatar
unknown committed
8296
        free_root(&share->mem_root, MYF(0));
8297
        my_free((uchar*) share, 0);
unknown's avatar
unknown committed
8298 8299 8300
        *root_ptr= old_root;
        if (!have_lock)
          pthread_mutex_unlock(&ndbcluster_mutex);
unknown's avatar
unknown committed
8301
        DBUG_RETURN(0);
unknown's avatar
unknown committed
8302 8303
      }
      thr_lock_init(&share->lock);
unknown's avatar
unknown committed
8304
      pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST);
unknown's avatar
Merge  
unknown committed
8305
      share->commit_count= 0;
8306
      share->commit_count_lock= 0;
unknown's avatar
unknown committed
8307 8308 8309 8310
      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
8311
#ifdef HAVE_NDB_BINLOG
8312 8313 8314 8315 8316 8317 8318 8319 8320
      if (ndbcluster_binlog_init_share(share, table))
      {
        DBUG_PRINT("error", ("get_share: %s could not init share", key));
        ndbcluster_real_free_share(&share);
        *root_ptr= old_root;
        if (!have_lock)
          pthread_mutex_unlock(&ndbcluster_mutex);
        DBUG_RETURN(0);
      }
unknown's avatar
unknown committed
8321
#endif
unknown's avatar
unknown committed
8322
      *root_ptr= old_root;
8323 8324 8325
    }
    else
    {
unknown's avatar
unknown committed
8326 8327 8328 8329
      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
8330
      DBUG_RETURN(0);
unknown's avatar
unknown committed
8331 8332 8333
    }
  }
  share->use_count++;
8334

unknown's avatar
unknown committed
8335
  dbug_print_open_tables();
unknown's avatar
unknown committed
8336
  dbug_print_share("ndbcluster_get_share:", share);
unknown's avatar
unknown committed
8337 8338
  if (!have_lock)
    pthread_mutex_unlock(&ndbcluster_mutex);
unknown's avatar
unknown committed
8339
  DBUG_RETURN(share);
unknown's avatar
unknown committed
8340 8341
}

unknown's avatar
unknown committed
8342

unknown's avatar
unknown committed
8343
void ndbcluster_real_free_share(NDB_SHARE **share)
unknown's avatar
unknown committed
8344
{
unknown's avatar
unknown committed
8345
  DBUG_ENTER("ndbcluster_real_free_share");
unknown's avatar
unknown committed
8346
  dbug_print_share("ndbcluster_real_free_share:", *share);
unknown's avatar
unknown committed
8347

8348
  hash_delete(&ndbcluster_open_tables, (uchar*) *share);
unknown's avatar
unknown committed
8349 8350 8351
  thr_lock_delete(&(*share)->lock);
  pthread_mutex_destroy(&(*share)->mutex);

unknown's avatar
unknown committed
8352 8353 8354
#ifdef HAVE_NDB_BINLOG
  if ((*share)->table)
  {
8355
    // (*share)->table->mem_root is freed by closefrm
unknown's avatar
unknown committed
8356
    closefrm((*share)->table, 0);
8357 8358
    // (*share)->table_share->mem_root is freed by free_table_share
    free_table_share((*share)->table_share);
unknown's avatar
unknown committed
8359
#ifndef DBUG_OFF
8360 8361
    bzero((uchar*)(*share)->table_share, sizeof(*(*share)->table_share));
    bzero((uchar*)(*share)->table, sizeof(*(*share)->table));
unknown's avatar
unknown committed
8362 8363 8364 8365 8366
    (*share)->table_share= 0;
    (*share)->table= 0;
#endif
  }
#endif
unknown's avatar
unknown committed
8367
  free_root(&(*share)->mem_root, MYF(0));
8368
  my_free((uchar*) *share, MYF(0));
unknown's avatar
unknown committed
8369 8370 8371
  *share= 0;

  dbug_print_open_tables();
unknown's avatar
unknown committed
8372
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
8373 8374
}

unknown's avatar
unknown committed
8375

unknown's avatar
unknown committed
8376
void ndbcluster_free_share(NDB_SHARE **share, bool have_lock)
unknown's avatar
unknown committed
8377
{
unknown's avatar
unknown committed
8378 8379 8380 8381 8382
  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
8383
  {
8384
    ndbcluster_real_free_share(share);
unknown's avatar
unknown committed
8385
  }
unknown's avatar
unknown committed
8386 8387 8388
  else
  {
    dbug_print_open_tables();
unknown's avatar
unknown committed
8389
    dbug_print_share("ndbcluster_free_share:", *share);
unknown's avatar
unknown committed
8390 8391 8392
  }
  if (!have_lock)
    pthread_mutex_unlock(&ndbcluster_mutex);
unknown's avatar
unknown committed
8393 8394 8395
}


unknown's avatar
unknown committed
8396 8397
static 
int
8398
ndb_get_table_statistics(ha_ndbcluster* file, bool report_error, Ndb* ndb, const NDBTAB *ndbtab,
8399
                         struct Ndb_statistics * ndbstat)
unknown's avatar
unknown committed
8400
{
8401
  NdbTransaction* pTrans;
8402
  NdbError error;
8403
  int retries= 10;
8404
  int reterr= 0;
8405
  int retry_sleep= 30; /* 30 milliseconds, transaction */
unknown's avatar
unknown committed
8406
#ifndef DBUG_OFF
unknown's avatar
unknown committed
8407
  char buff[22], buff2[22], buff3[22], buff4[22];
unknown's avatar
unknown committed
8408
#endif
unknown's avatar
unknown committed
8409
  DBUG_ENTER("ndb_get_table_statistics");
unknown's avatar
unknown committed
8410
  DBUG_PRINT("enter", ("table: %s", ndbtab->getName()));
8411

8412 8413
  DBUG_ASSERT(ndbtab != 0);

8414
  do
unknown's avatar
unknown committed
8415
  {
8416
    Uint64 rows, commits, fixed_mem, var_mem;
8417
    Uint32 size;
unknown's avatar
unknown committed
8418
    Uint32 count= 0;
8419 8420
    Uint64 sum_rows= 0;
    Uint64 sum_commits= 0;
unknown's avatar
unknown committed
8421 8422
    Uint64 sum_row_size= 0;
    Uint64 sum_mem= 0;
8423 8424 8425 8426
    NdbScanOperation*pOp;
    int check;

    if ((pTrans= ndb->startTransaction()) == NULL)
8427
    {
8428 8429 8430
      error= ndb->getNdbError();
      goto retry;
    }
unknown's avatar
unknown committed
8431
      
unknown's avatar
unknown committed
8432
    if ((pOp= pTrans->getNdbScanOperation(ndbtab)) == NULL)
8433 8434 8435
    {
      error= pTrans->getNdbError();
      goto retry;
8436
    }
unknown's avatar
unknown committed
8437
    
8438
    if (pOp->readTuples(NdbOperation::LM_CommittedRead))
8439 8440 8441 8442
    {
      error= pOp->getNdbError();
      goto retry;
    }
unknown's avatar
unknown committed
8443
    
8444 8445 8446 8447 8448
    if (pOp->interpret_exit_last_row() == -1)
    {
      error= pOp->getNdbError();
      goto retry;
    }
unknown's avatar
unknown committed
8449 8450 8451
    
    pOp->getValue(NdbDictionary::Column::ROW_COUNT, (char*)&rows);
    pOp->getValue(NdbDictionary::Column::COMMIT_COUNT, (char*)&commits);
8452
    pOp->getValue(NdbDictionary::Column::ROW_SIZE, (char*)&size);
8453 8454 8455 8456
    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
8457
    
8458
    if (pTrans->execute(NdbTransaction::NoCommit,
8459
                        NdbOperation::AbortOnError,
8460
                        TRUE) == -1)
8461
    {
8462 8463
      error= pTrans->getNdbError();
      goto retry;
8464
    }
unknown's avatar
unknown committed
8465
    
unknown's avatar
unknown committed
8466
    while ((check= pOp->nextResult(TRUE, TRUE)) == 0)
unknown's avatar
unknown committed
8467 8468 8469
    {
      sum_rows+= rows;
      sum_commits+= commits;
8470
      if (sum_row_size < size)
8471
        sum_row_size= size;
8472
      sum_mem+= fixed_mem + var_mem;
8473
      count++;
unknown's avatar
unknown committed
8474 8475 8476
    }
    
    if (check == -1)
8477 8478 8479 8480
    {
      error= pOp->getNdbError();
      goto retry;
    }
unknown's avatar
unknown committed
8481

8482
    pOp->close(TRUE);
unknown's avatar
unknown committed
8483

unknown's avatar
unknown committed
8484
    ndb->closeTransaction(pTrans);
8485 8486 8487 8488 8489 8490

    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
8491 8492 8493 8494 8495 8496 8497
    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));
8498

unknown's avatar
unknown committed
8499
    DBUG_RETURN(0);
8500
retry:
8501 8502
    if(report_error)
    {
8503
      if (file && pTrans)
8504 8505 8506 8507 8508 8509 8510 8511 8512 8513
      {
        reterr= file->ndb_err(pTrans);
      }
      else
      {
        const NdbError& tmp= error;
        ERR_PRINT(tmp);
        reterr= ndb_to_mysql_error(&tmp);
      }
    }
unknown's avatar
unknown committed
8514 8515 8516
    else
      reterr= error.code;

8517 8518 8519 8520 8521 8522 8523 8524 8525 8526
    if (pTrans)
    {
      ndb->closeTransaction(pTrans);
      pTrans= NULL;
    }
    if (error.status == NdbError::TemporaryError && retries--)
    {
      my_sleep(retry_sleep);
      continue;
    }
unknown's avatar
unknown committed
8527
    set_ndb_err(current_thd, error);
8528
    break;
8529
  } while(1);
8530 8531 8532
  DBUG_PRINT("exit", ("failed, reterr: %u, NdbError %u(%s)", reterr,
                      error.code, error.message));
  DBUG_RETURN(reterr);
unknown's avatar
unknown committed
8533 8534
}

8535 8536 8537 8538 8539
/*
  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
8540
int ha_ndbcluster::write_ndb_file(const char *name)
8541 8542 8543 8544 8545 8546
{
  File file;
  bool error=1;
  char path[FN_REFLEN];
  
  DBUG_ENTER("write_ndb_file");
unknown's avatar
unknown committed
8547
  DBUG_PRINT("enter", ("name: %s", name));
8548

unknown's avatar
unknown committed
8549
  (void)strxnmov(path, FN_REFLEN-1, 
unknown's avatar
unknown committed
8550
                 mysql_data_home,"/",name,ha_ndb_ext,NullS);
8551 8552 8553 8554 8555 8556 8557 8558 8559 8560

  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
8561
void 
8562 8563
ha_ndbcluster::release_completed_operations(NdbTransaction *trans,
					    bool force_release)
unknown's avatar
unknown committed
8564 8565 8566 8567 8568 8569 8570 8571
{
  if (trans->hasBlobOperation())
  {
    /* We are reading/writing BLOB fields, 
       releasing operation records is unsafe
    */
    return;
  }
8572 8573 8574 8575 8576 8577 8578 8579 8580 8581
  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
8582
  trans->releaseCompletedOperations();
8583 8584
}

8585 8586 8587 8588 8589 8590 8591 8592 8593
bool 
ha_ndbcluster::null_value_index_search(KEY_MULTI_RANGE *ranges,
				       KEY_MULTI_RANGE *end_range,
				       HANDLER_BUFFER *buffer)
{
  DBUG_ENTER("null_value_index_search");
  KEY* key_info= table->key_info + active_index;
  KEY_MULTI_RANGE *range= ranges;
  ulong reclength= table->s->reclength;
8594 8595
  uchar *curr= (uchar*)buffer->buffer;
  uchar *end_of_buffer= (uchar*)buffer->buffer_end;
8596 8597 8598 8599
  
  for (; range<end_range && curr+reclength <= end_of_buffer; 
       range++)
  {
8600
    const uchar *key= range->start_key.key;
8601 8602
    uint key_len= range->start_key.length;
    if (check_null_in_key(key_info, key, key_len))
unknown's avatar
unknown committed
8603
      DBUG_RETURN(TRUE);
8604 8605
    curr += reclength;
  }
unknown's avatar
unknown committed
8606
  DBUG_RETURN(FALSE);
8607 8608
}

8609
int
8610
ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
8611 8612 8613 8614
                                      KEY_MULTI_RANGE *ranges, 
                                      uint range_count,
                                      bool sorted, 
                                      HANDLER_BUFFER *buffer)
8615
{
8616
  m_write_op= FALSE;
8617 8618
  int res;
  KEY* key_info= table->key_info + active_index;
8619
  NDB_INDEX_TYPE cur_index_type= get_index_type(active_index);
unknown's avatar
unknown committed
8620
  ulong reclength= table_share->reclength;
8621
  NdbOperation* op;
8622
  Thd_ndb *thd_ndb= get_thd_ndb(current_thd);
unknown's avatar
unknown committed
8623
  DBUG_ENTER("ha_ndbcluster::read_multi_range_first");
8624

8625 8626 8627 8628
  /**
   * blobs and unique hash index with NULL can't be batched currently
   */
  if (uses_blob_value() ||
unknown's avatar
unknown committed
8629
      (cur_index_type ==  UNIQUE_INDEX &&
8630
       has_null_in_unique_index(active_index) &&
8631 8632
       null_value_index_search(ranges, ranges+range_count, buffer))
      || m_delete_cannot_batch || m_update_cannot_batch)
unknown's avatar
unknown committed
8633
  {
unknown's avatar
unknown committed
8634
    m_disable_multi_read= TRUE;
unknown's avatar
unknown committed
8635
    DBUG_RETURN(handler::read_multi_range_first(found_range_p, 
8636 8637 8638 8639
                                                ranges, 
                                                range_count,
                                                sorted, 
                                                buffer));
unknown's avatar
unknown committed
8640
  }
8641
  thd_ndb->query_state|= NDB_QUERY_MULTI_READ_RANGE;
unknown's avatar
unknown committed
8642
  m_disable_multi_read= FALSE;
8643 8644 8645 8646

  /**
   * Copy arguments into member variables
   */
8647 8648 8649
  m_multi_ranges= ranges;
  multi_range_curr= ranges;
  multi_range_end= ranges+range_count;
8650 8651 8652
  multi_range_sorted= sorted;
  multi_range_buffer= buffer;

8653 8654 8655 8656 8657 8658 8659 8660 8661 8662 8663
  /**
   * 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
8664 8665
   */   

unknown's avatar
unknown committed
8666
  /**
8667 8668
   * Variables for loop
   */
8669 8670
  uchar *curr= (uchar*)buffer->buffer;
  uchar *end_of_buffer= (uchar*)buffer->buffer_end;
8671 8672
  NdbOperation::LockMode lm= 
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
unknown's avatar
unknown committed
8673
  bool need_pk = (lm == NdbOperation::LM_Read);
8674 8675 8676
  const NDBTAB *tab= m_table;
  const NDBINDEX *unique_idx= m_index[active_index].unique_index;
  const NDBINDEX *idx= m_index[active_index].index; 
8677 8678
  const NdbOperation* lastOp= m_active_trans->getLastDefinedOperation();
  NdbIndexScanOperation* scanOp= 0;
unknown's avatar
unknown committed
8679 8680
  for (; multi_range_curr<multi_range_end && curr+reclength <= end_of_buffer; 
       multi_range_curr++)
8681
  {
8682 8683 8684 8685 8686 8687
    part_id_range part_spec;
    if (m_use_partition_function)
    {
      get_partition_set(table, curr, active_index,
                        &multi_range_curr->start_key,
                        &part_spec);
unknown's avatar
unknown committed
8688
      DBUG_PRINT("info", ("part_spec.start_part: %u  part_spec.end_part: %u",
8689 8690 8691 8692 8693
                          part_spec.start_part, part_spec.end_part));
      /*
        If partition pruning has found no partition in set
        we can skip this scan
      */
8694 8695 8696 8697 8698 8699 8700 8701 8702 8703 8704
      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;
      }
    }
8705
    switch (cur_index_type) {
unknown's avatar
unknown committed
8706 8707
    case PRIMARY_KEY_ORDERED_INDEX:
      if (!(multi_range_curr->start_key.length == key_info->key_length &&
8708 8709 8710
          multi_range_curr->start_key.flag == HA_READ_KEY_EXACT))
        goto range;
      // else fall through
8711
    case PRIMARY_KEY_INDEX:
8712
    {
8713
      multi_range_curr->range_flag |= UNIQUE_RANGE;
8714
      if ((op= m_active_trans->getNdbOperation(tab)) && 
8715 8716 8717
          !op->readTuple(lm) && 
          !set_primary_key(op, multi_range_curr->start_key.key) &&
          !define_read_attrs(curr, op) &&
8718
          (!m_use_partition_function ||
unknown's avatar
unknown committed
8719
           (op->setPartitionId(part_spec.start_part), TRUE)))
8720
        curr += reclength;
8721
      else
8722
        ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
8723
      break;
8724 8725
    }
    break;
unknown's avatar
unknown committed
8726 8727
    case UNIQUE_ORDERED_INDEX:
      if (!(multi_range_curr->start_key.length == key_info->key_length &&
8728 8729 8730 8731 8732
          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
8733
    case UNIQUE_INDEX:
8734
    {
8735
      multi_range_curr->range_flag |= UNIQUE_RANGE;
8736
      if ((op= m_active_trans->getNdbIndexOperation(unique_idx, tab)) && 
8737 8738
          !op->readTuple(lm) && 
          !set_index_key(op, key_info, multi_range_curr->start_key.key) &&
8739
          !define_read_attrs(curr, op))
8740
        curr += reclength;
8741
      else
8742
        ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
8743 8744
      break;
    }
8745
    case ORDERED_INDEX: {
8746
  range:
8747
      multi_range_curr->range_flag &= ~(uint)UNIQUE_RANGE;
8748 8749
      if (scanOp == 0)
      {
8750 8751 8752 8753 8754 8755
        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
8756
          if (scanOp->reset_bounds(m_force_send))
8757 8758 8759 8760 8761
            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
8762
                 &&!scanOp->readTuples(lm, 0, parallelism, sorted, 
8763
				       FALSE, TRUE, need_pk, TRUE)
unknown's avatar
unknown committed
8764
                 &&!(m_cond && m_cond->generate_scan_filter(scanOp))
8765 8766 8767 8768 8769 8770 8771 8772 8773 8774
                 &&!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());
        }
8775
      }
8776

8777
      const key_range *keys[2]= { &multi_range_curr->start_key, 
8778
                                  &multi_range_curr->end_key };
unknown's avatar
unknown committed
8779
      if ((res= set_bounds(scanOp, active_index, FALSE, keys,
8780
                           multi_range_curr-ranges)))
8781
        DBUG_RETURN(res);
8782
      break;
8783
    }
unknown's avatar
unknown committed
8784
    case UNDEFINED_INDEX:
unknown's avatar
unknown committed
8785 8786 8787 8788
      DBUG_ASSERT(FALSE);
      DBUG_RETURN(1);
      break;
    }
8789 8790
  }
  
8791
  if (multi_range_curr != multi_range_end)
8792
  {
8793 8794 8795 8796 8797 8798
    /**
     * 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
     */
8799
    buffer->end_of_used_area= (uchar*)buffer->buffer_end;
8800 8801 8802 8803 8804 8805 8806 8807 8808 8809 8810
  }
  else
  {
    buffer->end_of_used_area= curr;
  }
  
  /**
   * Set first operation in multi range
   */
  m_current_multi_operation= 
    lastOp ? lastOp->next() : m_active_trans->getFirstDefinedOperation();
8811
  if (!(res= execute_no_commit_ie(this, m_active_trans,true)))
8812
  {
8813 8814
    m_multi_range_defined= multi_range_curr;
    multi_range_curr= ranges;
8815
    m_multi_range_result_ptr= (uchar*)buffer->buffer;
8816
    DBUG_RETURN(read_multi_range_next(found_range_p));
8817 8818 8819 8820
  }
  ERR_RETURN(m_active_trans->getNdbError());
}

unknown's avatar
unknown committed
8821
#if 0
8822
#define DBUG_MULTI_RANGE(x) DBUG_PRINT("info", ("read_multi_range_next: case %d\n", x));
unknown's avatar
unknown committed
8823 8824 8825 8826
#else
#define DBUG_MULTI_RANGE(x)
#endif

8827
int
8828
ha_ndbcluster::read_multi_range_next(KEY_MULTI_RANGE ** multi_range_found_p)
8829 8830
{
  DBUG_ENTER("ha_ndbcluster::read_multi_range_next");
8831
  if (m_disable_multi_read)
8832
  {
8833
    DBUG_MULTI_RANGE(11);
8834
    DBUG_RETURN(handler::read_multi_range_next(multi_range_found_p));
8835
  }
8836
  
8837
  int res;
8838
  int range_no;
unknown's avatar
unknown committed
8839
  ulong reclength= table_share->reclength;
8840
  const NdbOperation* op= m_current_multi_operation;
unknown's avatar
unknown committed
8841
  for (;multi_range_curr < m_multi_range_defined; multi_range_curr++)
8842
  {
8843 8844 8845
    DBUG_MULTI_RANGE(12);
    if (multi_range_curr->range_flag & SKIP_RANGE)
      continue;
8846
    if (multi_range_curr->range_flag & UNIQUE_RANGE)
8847
    {
8848
      if (op->getNdbError().code == 0)
8849 8850
      {
        DBUG_MULTI_RANGE(13);
8851
        goto found_next;
8852
      }
8853 8854 8855
      
      op= m_active_trans->getNextCompletedOperation(op);
      m_multi_range_result_ptr += reclength;
8856
      continue;
8857
    } 
8858
    else if (m_multi_cursor && !multi_range_sorted)
8859
    {
unknown's avatar
unknown committed
8860 8861
      DBUG_MULTI_RANGE(1);
      if ((res= fetch_next(m_multi_cursor)) == 0)
8862
      {
8863 8864 8865
        DBUG_MULTI_RANGE(2);
        range_no= m_multi_cursor->get_range_no();
        goto found;
8866 8867 8868
      } 
      else
      {
8869
        DBUG_MULTI_RANGE(14);
8870
        goto close_scan;
8871 8872
      }
    }
unknown's avatar
unknown committed
8873
    else if (m_multi_cursor && multi_range_sorted)
8874
    {
unknown's avatar
unknown committed
8875 8876
      if (m_active_cursor && (res= fetch_next(m_multi_cursor)))
      {
8877 8878
        DBUG_MULTI_RANGE(3);
        goto close_scan;
unknown's avatar
unknown committed
8879
      }
8880
      
8881
      range_no= m_multi_cursor->get_range_no();
8882
      uint current_range_no= multi_range_curr - m_multi_ranges;
unknown's avatar
unknown committed
8883
      if ((uint) range_no == current_range_no)
8884
      {
8885
        DBUG_MULTI_RANGE(4);
8886
        // return current row
8887
        goto found;
8888
      }
8889
      else if (range_no > (int)current_range_no)
8890
      {
8891 8892 8893 8894
        DBUG_MULTI_RANGE(5);
        // wait with current row
        m_active_cursor= 0;
        continue;
8895 8896 8897
      }
      else 
      {
8898 8899 8900
        DBUG_MULTI_RANGE(6);
        // First fetch from cursor
        DBUG_ASSERT(range_no == -1);
unknown's avatar
unknown committed
8901
        if ((res= m_multi_cursor->nextResult(TRUE)))
8902
        {
8903
          DBUG_MULTI_RANGE(15);
8904 8905 8906 8907
          goto close_scan;
        }
        multi_range_curr--; // Will be increased in for-loop
        continue;
8908
      }
8909
    }
unknown's avatar
unknown committed
8910
    else /** m_multi_cursor == 0 */
8911
    {
unknown's avatar
unknown committed
8912
      DBUG_MULTI_RANGE(7);
8913 8914 8915 8916
      /**
       * Corresponds to range 5 in example in read_multi_range_first
       */
      (void)1;
8917
      continue;
8918
    }
8919
    
unknown's avatar
unknown committed
8920
    DBUG_ASSERT(FALSE); // Should only get here via goto's
8921 8922 8923
close_scan:
    if (res == 1)
    {
unknown's avatar
unknown committed
8924
      m_multi_cursor->close(FALSE, TRUE);
8925
      m_active_cursor= m_multi_cursor= 0;
unknown's avatar
unknown committed
8926
      DBUG_MULTI_RANGE(8);
8927 8928 8929 8930
      continue;
    } 
    else 
    {
8931
      DBUG_MULTI_RANGE(9);
8932 8933 8934
      DBUG_RETURN(ndb_err(m_active_trans));
    }
  }
8935
  
8936
  if (multi_range_curr == multi_range_end)
8937 8938
  {
    DBUG_MULTI_RANGE(16);
8939 8940
    Thd_ndb *thd_ndb= get_thd_ndb(current_thd);
    thd_ndb->query_state&= NDB_QUERY_NORMAL;
8941
    DBUG_RETURN(HA_ERR_END_OF_FILE);
8942
  }
8943
  
8944 8945 8946 8947
  /**
   * Read remaining ranges
   */
  DBUG_RETURN(read_multi_range_first(multi_range_found_p, 
8948 8949 8950 8951
                                     multi_range_curr,
                                     multi_range_end - multi_range_curr, 
                                     multi_range_sorted,
                                     multi_range_buffer));
8952 8953
  
found:
8954 8955 8956
  /**
   * Found a record belonging to a scan
   */
8957
  m_active_cursor= m_multi_cursor;
8958
  * multi_range_found_p= m_multi_ranges + range_no;
8959 8960
  memcpy(table->record[0], m_multi_range_cursor_result_ptr, reclength);
  setup_recattr(m_active_cursor->getFirstRecAttr());
8961 8962 8963
  unpack_record(table->record[0]);
  table->status= 0;     
  DBUG_RETURN(0);
8964
  
8965
found_next:
8966 8967 8968 8969
  /**
   * Found a record belonging to a pk/index op,
   *   copy result and move to next to prepare for next call
   */
8970
  * multi_range_found_p= multi_range_curr;
8971
  memcpy(table->record[0], m_multi_range_result_ptr, reclength);
8972
  setup_recattr(op->getFirstRecAttr());
8973
  unpack_record(table->record[0]);
8974 8975
  table->status= 0;
  
8976
  multi_range_curr++;
8977
  m_current_multi_operation= m_active_trans->getNextCompletedOperation(op);
8978 8979
  m_multi_range_result_ptr += reclength;
  DBUG_RETURN(0);
8980 8981
}

8982 8983 8984 8985 8986 8987 8988 8989
int
ha_ndbcluster::setup_recattr(const NdbRecAttr* curr)
{
  DBUG_ENTER("setup_recattr");

  Field **field, **end;
  NdbValue *value= m_value;
  
unknown's avatar
unknown committed
8990
  end= table->field + table_share->fields;
8991 8992 8993 8994 8995 8996
  
  for (field= table->field; field < end; field++, value++)
  {
    if ((* value).ptr)
    {
      DBUG_ASSERT(curr != 0);
unknown's avatar
unknown committed
8997 8998 8999
      NdbValue* val= m_value + curr->getColumn()->getColumnNo();
      DBUG_ASSERT(val->ptr);
      val->rec= curr;
9000
      curr= curr->next();
9001 9002 9003
    }
  }
  
unknown's avatar
unknown committed
9004
  DBUG_RETURN(0);
9005 9006
}

unknown's avatar
Merge  
unknown committed
9007 9008
char*
ha_ndbcluster::update_table_comment(
9009 9010
                                /* out: table comment + additional */
        const char*     comment)/* in:  table comment defined by user */
unknown's avatar
Merge  
unknown committed
9011 9012
{
  uint length= strlen(comment);
unknown's avatar
unknown committed
9013
  if (length > 64000 - 3)
unknown's avatar
Merge  
unknown committed
9014 9015 9016 9017 9018 9019 9020 9021 9022 9023
  {
    return((char*)comment); /* string too long */
  }

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

9024 9025 9026 9027
  if (ndb->setDatabaseName(m_dbname))
  {
    return((char*)comment);
  }
9028 9029
  const NDBTAB* tab= m_table;
  DBUG_ASSERT(tab != NULL);
unknown's avatar
Merge  
unknown committed
9030 9031 9032 9033

  char *str;
  const char *fmt="%s%snumber_of_replicas: %d";
  const unsigned fmt_len_plus_extra= length + strlen(fmt);
9034
  if ((str= (char*) my_malloc(fmt_len_plus_extra, MYF(0))) == NULL)
unknown's avatar
Merge  
unknown committed
9035
  {
9036 9037
    sql_print_error("ha_ndbcluster::update_table_comment: "
                    "my_malloc(%u) failed", (unsigned int)fmt_len_plus_extra);
unknown's avatar
Merge  
unknown committed
9038 9039 9040
    return (char*)comment;
  }

unknown's avatar
unknown committed
9041 9042 9043
  my_snprintf(str,fmt_len_plus_extra,fmt,comment,
              length > 0 ? " ":"",
              tab->getReplicaCount());
unknown's avatar
Merge  
unknown committed
9044 9045 9046 9047 9048
  return str;
}


// Utility thread main loop
9049
pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
unknown's avatar
Merge  
unknown committed
9050 9051 9052
{
  THD *thd; /* needs to be first for thread_stack */
  struct timespec abstime;
9053
  Thd_ndb *thd_ndb;
unknown's avatar
unknown committed
9054 9055
  uint share_list_size= 0;
  NDB_SHARE **share_list= NULL;
unknown's avatar
Merge  
unknown committed
9056 9057 9058

  my_thread_init();
  DBUG_ENTER("ndb_util_thread");
unknown's avatar
unknown committed
9059
  DBUG_PRINT("enter", ("ndb_cache_check_time: %lu", ndb_cache_check_time));
unknown's avatar
unknown committed
9060 9061
 
   pthread_mutex_lock(&LOCK_ndb_util_thread);
unknown's avatar
Merge  
unknown committed
9062 9063

  thd= new THD; /* note that contructor of THD uses DBUG_ */
9064 9065 9066 9067 9068
  if (thd == NULL)
  {
    my_errno= HA_ERR_OUT_OF_MEM;
    DBUG_RETURN(NULL);
  }
unknown's avatar
Merge  
unknown committed
9069 9070 9071 9072 9073
  THD_CHECK_SENTRY(thd);
  pthread_detach_this_thread();
  ndb_util_thread= pthread_self();

  thd->thread_stack= (char*)&thd; /* remember where our stack is */
9074
  if (thd->store_globals())
unknown's avatar
unknown committed
9075
    goto ndb_util_thread_fail;
9076
  lex_start(thd);
unknown's avatar
unknown committed
9077 9078 9079 9080 9081 9082 9083 9084
  thd->init_for_queries();
  thd->version=refresh_version;
  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;

9085 9086 9087 9088 9089 9090 9091 9092
  CHARSET_INFO *charset_connection;
  charset_connection= get_charset_by_csname("utf8",
                                            MY_CS_PRIMARY, MYF(MY_WME));
  thd->variables.character_set_client= charset_connection;
  thd->variables.character_set_results= charset_connection;
  thd->variables.collation_connection= charset_connection;
  thd->update_charset();

unknown's avatar
unknown committed
9093
  /* Signal successful initialization */
9094
  ndb_util_thread_running= 1;
unknown's avatar
unknown committed
9095 9096
  pthread_cond_signal(&COND_ndb_util_ready);
  pthread_mutex_unlock(&LOCK_ndb_util_thread);
9097

unknown's avatar
unknown committed
9098 9099 9100 9101 9102
  /*
    wait for mysql server to start
  */
  pthread_mutex_lock(&LOCK_server_started);
  while (!mysqld_server_started)
unknown's avatar
unknown committed
9103 9104 9105 9106 9107 9108 9109 9110 9111 9112 9113
  {
    set_timespec(abstime, 1);
    pthread_cond_timedwait(&COND_server_started, &LOCK_server_started,
	                       &abstime);
    if (ndbcluster_terminating)
    {
      pthread_mutex_unlock(&LOCK_server_started);
      pthread_mutex_lock(&LOCK_ndb_util_thread);
      goto ndb_util_thread_end;
    }
  }
unknown's avatar
unknown committed
9114 9115 9116 9117 9118 9119
  pthread_mutex_unlock(&LOCK_server_started);

  /*
    Wait for cluster to start
  */
  pthread_mutex_lock(&LOCK_ndb_util_thread);
9120
  while (!ndb_cluster_node_id && (ndbcluster_hton->slot != ~(uint)0))
unknown's avatar
unknown committed
9121 9122
  {
    /* ndb not connected yet */
unknown's avatar
unknown committed
9123 9124
    pthread_cond_wait(&COND_ndb_util_thread, &LOCK_ndb_util_thread);
    if (ndbcluster_terminating)
unknown's avatar
unknown committed
9125 9126 9127 9128
      goto ndb_util_thread_end;
  }
  pthread_mutex_unlock(&LOCK_ndb_util_thread);

9129 9130
  /* Get thd_ndb for this thread */
  if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
unknown's avatar
unknown committed
9131
  {
9132
    sql_print_error("Could not allocate Thd_ndb object");
unknown's avatar
unknown committed
9133
    pthread_mutex_lock(&LOCK_ndb_util_thread);
9134
    goto ndb_util_thread_end;
unknown's avatar
unknown committed
9135
  }
9136 9137
  set_thd_ndb(thd, thd_ndb);
  thd_ndb->options|= TNO_NO_LOG_SCHEMA_OP;
unknown's avatar
unknown committed
9138 9139

#ifdef HAVE_NDB_BINLOG
9140 9141
  if (ndb_extra_logging && ndb_binlog_running)
    sql_print_information("NDB Binlog: Ndb tables initially read only.");
unknown's avatar
unknown committed
9142 9143 9144
  /* create tables needed by the replication */
  ndbcluster_setup_binlog_table_shares(thd);
#else
unknown's avatar
unknown committed
9145 9146 9147 9148
  /*
    Get all table definitions from the storage node
  */
  ndbcluster_find_all_files(thd);
unknown's avatar
unknown committed
9149
#endif
unknown's avatar
unknown committed
9150

9151
  set_timespec(abstime, 0);
unknown's avatar
unknown committed
9152
  for (;;)
unknown's avatar
Merge  
unknown committed
9153 9154
  {
    pthread_mutex_lock(&LOCK_ndb_util_thread);
unknown's avatar
unknown committed
9155 9156 9157 9158 9159 9160
    if (!ndbcluster_terminating)
      pthread_cond_timedwait(&COND_ndb_util_thread,
                             &LOCK_ndb_util_thread,
                             &abstime);
    if (ndbcluster_terminating) /* Shutting down server */
      goto ndb_util_thread_end;
unknown's avatar
Merge  
unknown committed
9161
    pthread_mutex_unlock(&LOCK_ndb_util_thread);
unknown's avatar
unknown committed
9162
#ifdef NDB_EXTRA_DEBUG_UTIL_THREAD
unknown's avatar
unknown committed
9163
    DBUG_PRINT("ndb_util_thread", ("Started, ndb_cache_check_time: %lu",
unknown's avatar
Merge  
unknown committed
9164
                                   ndb_cache_check_time));
unknown's avatar
unknown committed
9165
#endif
unknown's avatar
Merge  
unknown committed
9166

unknown's avatar
unknown committed
9167 9168
#ifdef HAVE_NDB_BINLOG
    /*
9169 9170
      Check that the ndb_apply_status_share and ndb_schema_share 
      have been created.
unknown's avatar
unknown committed
9171 9172
      If not try to create it
    */
9173
    if (!ndb_binlog_tables_inited)
unknown's avatar
unknown committed
9174 9175 9176
      ndbcluster_setup_binlog_table_shares(thd);
#endif

unknown's avatar
Merge  
unknown committed
9177 9178
    if (ndb_cache_check_time == 0)
    {
9179 9180
      /* Wake up in 1 second to check if value has changed */
      set_timespec(abstime, 1);
unknown's avatar
Merge  
unknown committed
9181 9182 9183 9184 9185 9186
      continue;
    }

    /* Lock mutex and fill list with pointers to all open tables */
    NDB_SHARE *share;
    pthread_mutex_lock(&ndbcluster_mutex);
unknown's avatar
unknown committed
9187
    uint i, open_count, record_count= ndbcluster_open_tables.records;
9188 9189 9190 9191 9192 9193 9194 9195 9196 9197 9198 9199 9200 9201
    if (share_list_size < record_count)
    {
      NDB_SHARE ** new_share_list= new NDB_SHARE * [record_count];
      if (!new_share_list)
      {
        sql_print_warning("ndb util thread: malloc failure, "
                          "query cache not maintained properly");
        pthread_mutex_unlock(&ndbcluster_mutex);
        goto next;                               // At least do not crash
      }
      delete [] share_list;
      share_list_size= record_count;
      share_list= new_share_list;
    }
unknown's avatar
unknown committed
9202
    for (i= 0, open_count= 0; i < record_count; i++)
unknown's avatar
Merge  
unknown committed
9203 9204
    {
      share= (NDB_SHARE *)hash_element(&ndbcluster_open_tables, i);
unknown's avatar
unknown committed
9205 9206 9207 9208 9209 9210
#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 */
9211
      /* ndb_share reference temporary, free below */
unknown's avatar
Merge  
unknown committed
9212
      share->use_count++; /* Make sure the table can't be closed */
9213 9214
      DBUG_PRINT("NDB_SHARE", ("%s temporary  use_count: %u",
                               share->key, share->use_count));
unknown's avatar
Merge  
unknown committed
9215 9216 9217 9218 9219
      DBUG_PRINT("ndb_util_thread",
                 ("Found open table[%d]: %s, use_count: %d",
                  i, share->table_name, share->use_count));

      /* Store pointer to table */
unknown's avatar
unknown committed
9220
      share_list[open_count++]= share;
unknown's avatar
Merge  
unknown committed
9221 9222 9223
    }
    pthread_mutex_unlock(&ndbcluster_mutex);

9224
    /* Iterate through the open files list */
unknown's avatar
unknown committed
9225
    for (i= 0; i < open_count; i++)
unknown's avatar
Merge  
unknown committed
9226
    {
9227
      share= share_list[i];
unknown's avatar
unknown committed
9228 9229 9230 9231 9232 9233 9234
#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
	*/
9235 9236 9237
        /* ndb_share reference temporary free */
        DBUG_PRINT("NDB_SHARE", ("%s temporary free  use_count: %u",
                                 share->key, share->use_count));
unknown's avatar
unknown committed
9238 9239 9240 9241
        free_share(&share);
        continue;
      }
#endif /* HAVE_NDB_BINLOG */
unknown's avatar
Merge  
unknown committed
9242
      DBUG_PRINT("ndb_util_thread",
9243
                 ("Fetching commit count for: %s", share->key));
unknown's avatar
Merge  
unknown committed
9244

9245 9246 9247 9248 9249
      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
9250
      {
9251 9252
        /* Contact NDB to get commit count for table */
        Ndb* ndb= thd_ndb->ndb;
unknown's avatar
unknown committed
9253 9254 9255 9256
        if (ndb->setDatabaseName(share->db))
        {
          goto loop_next;
        }
9257 9258
        Ndb_table_guard ndbtab_g(ndb->getDictionary(), share->table_name);
        if (ndbtab_g.get_table() &&
unknown's avatar
unknown committed
9259
            ndb_get_table_statistics(NULL, FALSE, ndb,
9260
                                     ndbtab_g.get_table(), &stat) == 0)
9261
        {
unknown's avatar
unknown committed
9262
#ifndef DBUG_OFF
9263
          char buff[22], buff2[22];
unknown's avatar
unknown committed
9264
#endif
unknown's avatar
unknown committed
9265 9266
          DBUG_PRINT("info",
                     ("Table: %s  commit_count: %s  rows: %s",
9267 9268
                      share->key,
                      llstr(stat.commit_count, buff),
unknown's avatar
unknown committed
9269
                      llstr(stat.row_count, buff2)));
9270 9271 9272 9273 9274 9275 9276 9277
        }
        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
9278
      }
9279
  loop_next:
9280 9281 9282 9283 9284
      pthread_mutex_lock(&share->mutex);
      if (share->commit_count_lock == lock)
        share->commit_count= stat.commit_count;
      pthread_mutex_unlock(&share->mutex);

9285 9286 9287
      /* ndb_share reference temporary free */
      DBUG_PRINT("NDB_SHARE", ("%s temporary free  use_count: %u",
                               share->key, share->use_count));
unknown's avatar
unknown committed
9288
      free_share(&share);
unknown's avatar
Merge  
unknown committed
9289
    }
9290
next:
9291 9292 9293 9294 9295 9296 9297 9298 9299
    /* 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
9300
    if (msecs >= 1000){
9301 9302 9303 9304 9305 9306 9307 9308 9309 9310
      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
9311
  }
unknown's avatar
unknown committed
9312 9313 9314

  pthread_mutex_lock(&LOCK_ndb_util_thread);

unknown's avatar
unknown committed
9315 9316
ndb_util_thread_end:
  net_end(&thd->net);
unknown's avatar
unknown committed
9317
ndb_util_thread_fail:
9318 9319
  if (share_list)
    delete [] share_list;
unknown's avatar
Merge  
unknown committed
9320 9321
  thd->cleanup();
  delete thd;
unknown's avatar
unknown committed
9322 9323
  
  /* signal termination */
9324
  ndb_util_thread_running= 0;
unknown's avatar
unknown committed
9325
  pthread_cond_signal(&COND_ndb_util_ready);
9326
  pthread_mutex_unlock(&LOCK_ndb_util_thread);
unknown's avatar
Merge  
unknown committed
9327 9328 9329 9330 9331 9332
  DBUG_PRINT("exit", ("ndb_util_thread"));
  my_thread_end();
  pthread_exit(0);
  DBUG_RETURN(NULL);
}

9333 9334 9335
/*
  Condition pushdown
*/
9336 9337 9338 9339 9340 9341 9342 9343 9344 9345 9346 9347 9348 9349 9350 9351 9352
/*
  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)
*/
9353 9354 9355 9356 9357
const 
COND* 
ha_ndbcluster::cond_push(const COND *cond) 
{ 
  DBUG_ENTER("cond_push");
unknown's avatar
unknown committed
9358 9359 9360
  if (!m_cond) 
    m_cond= new ha_ndbcluster_cond;
  if (!m_cond)
9361 9362 9363 9364
  {
    my_errno= HA_ERR_OUT_OF_MEM;
    DBUG_RETURN(NULL);
  }
9365
  DBUG_EXECUTE("where",print_where((COND *)cond, m_tabname););
unknown's avatar
unknown committed
9366
  DBUG_RETURN(m_cond->cond_push(cond, table, (NDBTAB *)m_table));
9367 9368
}

9369 9370 9371
/*
  Pop the top condition from the condition stack of the handler instance.
*/
9372 9373 9374
void 
ha_ndbcluster::cond_pop() 
{ 
unknown's avatar
unknown committed
9375 9376
  if (m_cond)
    m_cond->cond_pop();
9377 9378 9379
}


9380 9381 9382
/*
  get table space info for SHOW CREATE TABLE
*/
9383
char* ha_ndbcluster::get_tablespace_name(THD *thd, char* name, uint name_len)
9384
{
9385
  Ndb *ndb= check_ndb_in_thd(thd);
9386
  NDBDICT *ndbdict= ndb->getDictionary();
9387 9388
  NdbError ndberr;
  Uint32 id;
9389
  ndb->setDatabaseName(m_dbname);
9390 9391
  const NDBTAB *ndbtab= m_table;
  DBUG_ASSERT(ndbtab != NULL);
9392 9393
  if (!ndbtab->getTablespace(&id))
  {
9394
    return 0;
9395 9396 9397 9398
  }
  {
    NdbDictionary::Tablespace ts= ndbdict->getTablespace(id);
    ndberr= ndbdict->getNdbError();
9399
    if(ndberr.classification != NdbError::NoError)
9400
      goto err;
9401
    DBUG_PRINT("info", ("Found tablespace '%s'", ts.getName()));
9402 9403
    if (name)
    {
9404
      strxnmov(name, name_len, ts.getName(), NullS);
9405 9406 9407 9408
      return name;
    }
    else
      return (my_strdup(ts.getName(), MYF(0)));
9409 9410 9411
  }
err:
  if (ndberr.status == NdbError::TemporaryError)
9412
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
9413 9414 9415
			ER_GET_TEMPORARY_ERRMSG, ER(ER_GET_TEMPORARY_ERRMSG),
			ndberr.code, ndberr.message, "NDB");
  else
9416
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
9417 9418
			ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
			ndberr.code, ndberr.message, "NDB");
9419 9420 9421
  return 0;
}

unknown's avatar
unknown committed
9422 9423 9424
/*
  Implements the SHOW NDB STATUS command.
*/
9425
bool
9426
ndbcluster_show_status(handlerton *hton, THD* thd, stat_print_fn *stat_print,
9427
                       enum ha_stat_type stat_type)
9428
{
9429
  char buf[IO_SIZE];
unknown's avatar
unknown committed
9430
  uint buflen;
9431 9432
  DBUG_ENTER("ndbcluster_show_status");
  
9433 9434 9435
  if (stat_type != HA_ENGINE_STATUS)
  {
    DBUG_RETURN(FALSE);
9436
  }
unknown's avatar
unknown committed
9437 9438 9439 9440

  update_status_variables(g_ndb_cluster_connection);
  buflen=
    my_snprintf(buf, sizeof(buf),
9441
                "cluster_node_id=%ld, "
unknown's avatar
unknown committed
9442
                "connected_host=%s, "
9443 9444 9445 9446
                "connected_port=%ld, "
                "number_of_data_nodes=%ld, "
                "number_of_ready_data_nodes=%ld, "
                "connect_count=%ld",
unknown's avatar
unknown committed
9447 9448 9449
                ndb_cluster_node_id,
                ndb_connected_host,
                ndb_connected_port,
unknown's avatar
unknown committed
9450 9451
                ndb_number_of_data_nodes,
                ndb_number_of_ready_data_nodes,
9452
                ndb_connect_count);
unknown's avatar
unknown committed
9453 9454
  if (stat_print(thd, ndbcluster_hton_name, ndbcluster_hton_name_length,
                 STRING_WITH_LEN("connection"), buf, buflen))
unknown's avatar
unknown committed
9455 9456
    DBUG_RETURN(TRUE);

unknown's avatar
unknown committed
9457
  if (get_thd_ndb(thd) && get_thd_ndb(thd)->ndb)
9458
  {
unknown's avatar
unknown committed
9459
    Ndb* ndb= (get_thd_ndb(thd))->ndb;
unknown's avatar
unknown committed
9460 9461
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
9462 9463
    while (ndb->get_free_list_usage(&tmp))
    {
unknown's avatar
unknown committed
9464
      buflen=
unknown's avatar
unknown committed
9465
        my_snprintf(buf, sizeof(buf),
9466 9467
                  "created=%u, free=%u, sizeof=%u",
                  tmp.m_created, tmp.m_free, tmp.m_sizeof);
unknown's avatar
unknown committed
9468
      if (stat_print(thd, ndbcluster_hton_name, ndbcluster_hton_name_length,
unknown's avatar
unknown committed
9469
                     tmp.m_name, strlen(tmp.m_name), buf, buflen))
9470
        DBUG_RETURN(TRUE);
9471 9472
    }
  }
unknown's avatar
unknown committed
9473 9474 9475 9476
#ifdef HAVE_NDB_BINLOG
  ndbcluster_show_status_binlog(thd, stat_print, stat_type);
#endif

9477 9478
  DBUG_RETURN(FALSE);
}
9479

unknown's avatar
unknown committed
9480

9481 9482 9483
/*
  Create a table in NDB Cluster
 */
9484
static uint get_no_fragments(ulonglong max_rows)
9485 9486 9487 9488 9489 9490 9491 9492 9493 9494 9495 9496 9497 9498 9499 9500 9501 9502 9503 9504 9505 9506 9507 9508 9509 9510 9511 9512 9513 9514 9515 9516 9517 9518 9519 9520 9521
{
#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);
}

unknown's avatar
unknown committed
9522
int ha_ndbcluster::get_default_no_partitions(HA_CREATE_INFO *create_info)
9523
{
9524
  ha_rows max_rows, min_rows;
unknown's avatar
unknown committed
9525
  if (create_info)
9526
  {
unknown's avatar
unknown committed
9527 9528
    max_rows= create_info->max_rows;
    min_rows= create_info->min_rows;
9529 9530 9531 9532 9533 9534
  }
  else
  {
    max_rows= table_share->max_rows;
    min_rows= table_share->min_rows;
  }
9535
  uint reported_frags;
9536 9537
  uint no_fragments=
    get_no_fragments(max_rows >= min_rows ? max_rows : min_rows);
9538
  uint no_nodes= g_ndb_cluster_connection->no_db_nodes();
unknown's avatar
unknown committed
9539 9540 9541 9542 9543 9544
  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");
  }
9545 9546 9547 9548
  return (int)reported_frags;
}


unknown's avatar
unknown committed
9549 9550 9551 9552 9553 9554 9555 9556 9557 9558 9559 9560 9561 9562 9563 9564 9565 9566 9567 9568 9569 9570 9571 9572 9573 9574 9575 9576 9577 9578 9579 9580 9581 9582 9583 9584 9585 9586 9587 9588 9589 9590
/*
  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
9591
  bool unsigned_flag= part_info->part_expr->unsigned_flag;
unknown's avatar
unknown committed
9592 9593 9594 9595 9596 9597 9598 9599 9600 9601
  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
9602 9603
    if (unsigned_flag)
      range_val-= 0x8000000000000000ULL;
9604
    if (range_val < INT_MIN32 || range_val >= INT_MAX32)
unknown's avatar
unknown committed
9605
    {
9606 9607 9608 9609 9610 9611 9612 9613
      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
9614 9615 9616 9617 9618 9619 9620 9621 9622 9623 9624 9625 9626 9627 9628 9629
    }
    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
9630
  bool unsigned_flag= part_info->part_expr->unsigned_flag;
unknown's avatar
unknown committed
9631 9632 9633 9634 9635 9636 9637 9638 9639 9640 9641
  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
9642 9643
    if (unsigned_flag)
      list_val-= 0x8000000000000000ULL;
unknown's avatar
unknown committed
9644 9645 9646 9647 9648 9649 9650 9651 9652 9653 9654 9655 9656 9657 9658 9659
    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);
}

9660 9661 9662 9663 9664 9665 9666 9667 9668 9669 9670 9671 9672 9673 9674 9675 9676
/*
  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
9677 9678
  uint16 frag_data[MAX_PARTITIONS];
  char *ts_names[MAX_PARTITIONS];
unknown's avatar
unknown committed
9679
  ulong fd_index= 0, i, j;
9680 9681 9682
  NDBTAB *tab= (NDBTAB*)tab_par;
  NDBTAB::FragmentType ftype= NDBTAB::UserDefined;
  partition_element *part_elem;
unknown's avatar
unknown committed
9683
  bool first= TRUE;
unknown's avatar
unknown committed
9684
  uint tot_ts_name_len;
unknown's avatar
unknown committed
9685 9686 9687
  List_iterator<partition_element> part_it(part_info->partitions);
  int error;
  DBUG_ENTER("ha_ndbcluster::set_up_partition_info");
9688 9689 9690 9691 9692 9693 9694 9695 9696 9697 9698 9699 9700

  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++)
    {
9701
      NDBCOL *col= tab->getColumn(fields[i]->field_index);
9702 9703 9704 9705
      DBUG_PRINT("info",("setting dist key on %s", col->getName()));
      col->setPartitionKey(TRUE);
    }
  }
9706
  else 
9707
  {
9708 9709 9710 9711 9712 9713 9714 9715
    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");
9716
      DBUG_RETURN(HA_ERR_UNSUPPORTED);
9717 9718
    }
   /*
9719 9720 9721 9722 9723 9724 9725 9726 9727 9728 9729 9730 9731 9732 9733 9734
      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)
9735
    {
9736 9737 9738 9739
      if ((error= set_range_data((void*)tab, part_info)))
      {
        DBUG_RETURN(error);
      }
9740
    }
9741
    else if (part_info->part_type == LIST_PARTITION)
9742
    {
9743 9744 9745 9746
      if ((error= set_list_data((void*)tab, part_info)))
      {
        DBUG_RETURN(error);
      }
9747 9748 9749
    }
  }
  tab->setFragmentType(ftype);
unknown's avatar
unknown committed
9750 9751 9752
  i= 0;
  tot_ts_name_len= 0;
  do
9753
  {
unknown's avatar
unknown committed
9754 9755
    uint ng;
    part_elem= part_it++;
9756
    if (!part_info->is_sub_partitioned())
9757
    {
unknown's avatar
unknown committed
9758 9759 9760 9761 9762
      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;
9763
    }
unknown's avatar
unknown committed
9764 9765 9766 9767 9768 9769 9770 9771 9772 9773 9774 9775 9776 9777 9778 9779 9780
    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);
9781
  tab->setLinearFlag(part_info->linear_hash_ind);
9782
  {
9783 9784
    ha_rows max_rows= table_share->max_rows;
    ha_rows min_rows= table_share->min_rows;
9785 9786 9787 9788 9789
    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);
9790
      tab->setMinRows(min_rows);
9791 9792
    }
  }
unknown's avatar
unknown committed
9793 9794 9795 9796
  tab->setTablespaceNames(ts_names, fd_index*sizeof(char*));
  tab->setFragmentCount(fd_index);
  tab->setFragmentData(&frag_data, fd_index*2);
  DBUG_RETURN(0);
9797
}
9798

unknown's avatar
unknown committed
9799

unknown's avatar
unknown committed
9800
bool ha_ndbcluster::check_if_incompatible_data(HA_CREATE_INFO *create_info,
unknown's avatar
unknown committed
9801 9802
					       uint table_changes)
{
9803 9804 9805
  DBUG_ENTER("ha_ndbcluster::check_if_incompatible_data");
  uint i;
  const NDBTAB *tab= (const NDBTAB *) m_table;
unknown's avatar
unknown committed
9806

9807 9808 9809 9810 9811 9812
  if (current_thd->variables.ndb_use_copying_alter_table)
  {
    DBUG_PRINT("info", ("On-line alter table disabled"));
    DBUG_RETURN(COMPATIBLE_DATA_NO);
  }

unknown's avatar
unknown committed
9813 9814
  int pk= 0;
  int ai= 0;
9815 9816 9817 9818 9819 9820

  if (create_info->tablespace)
    create_info->storage_media = HA_SM_DISK;
  else
    create_info->storage_media = HA_SM_MEMORY;

9821 9822 9823
  for (i= 0; i < table->s->fields; i++) 
  {
    Field *field= table->field[i];
9824
    const NDBCOL *col= tab->getColumn(i);
9825 9826 9827 9828 9829 9830 9831
    if (col->getStorageType() == NDB_STORAGETYPE_MEMORY && create_info->storage_media != HA_SM_MEMORY ||
        col->getStorageType() == NDB_STORAGETYPE_DISK && create_info->storage_media != HA_SM_DISK)
    {
      DBUG_PRINT("info", ("Column storage media is changed"));
      DBUG_RETURN(COMPATIBLE_DATA_NO);
    }
    
9832
    if (field->flags & FIELD_IS_RENAMED)
9833 9834 9835 9836
    {
      DBUG_PRINT("info", ("Field has been renamed, copy table"));
      DBUG_RETURN(COMPATIBLE_DATA_NO);
    }
9837
    if ((field->flags & FIELD_IN_ADD_INDEX) &&
9838 9839 9840 9841 9842
        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
9843 9844 9845 9846 9847
    
    if (field->flags & PRI_KEY_FLAG)
      pk=1;
    if (field->flags & FIELD_IN_ADD_INDEX)
      ai=1;
9848
  }
9849 9850 9851 9852 9853 9854 9855 9856 9857 9858 9859 9860 9861 9862 9863 9864 9865 9866 9867 9868 9869 9870 9871 9872 9873 9874 9875 9876 9877 9878

  char tablespace_name[FN_LEN]; 
  if (get_tablespace_name(current_thd, tablespace_name, FN_LEN))
  {
    if (create_info->tablespace) 
    {
      if (strcmp(create_info->tablespace, tablespace_name))
      {
        DBUG_PRINT("info", ("storage media is changed, old tablespace=%s, new tablespace=%s",
          tablespace_name, create_info->tablespace));
        DBUG_RETURN(COMPATIBLE_DATA_NO);
      }
    }
    else
    {
      DBUG_PRINT("info", ("storage media is changed, old is DISK and tablespace=%s, new is MEM",
        tablespace_name));
      DBUG_RETURN(COMPATIBLE_DATA_NO);
    }
  }
  else
  {
    if (create_info->storage_media != HA_SM_MEMORY)
    {
      DBUG_PRINT("info", ("storage media is changed, old is MEM, new is DISK and tablespace=%s",
        create_info->tablespace));
      DBUG_RETURN(COMPATIBLE_DATA_NO);
    }
  }

unknown's avatar
unknown committed
9879
  if (table_changes != IS_EQUAL_YES)
9880
    DBUG_RETURN(COMPATIBLE_DATA_NO);
unknown's avatar
unknown committed
9881
  
unknown's avatar
unknown committed
9882 9883 9884 9885 9886 9887 9888 9889 9890 9891 9892 9893 9894 9895 9896 9897 9898 9899
  /**
   * Changing from/to primary key
   *
   * This is _not_ correct, but check_if_incompatible_data-interface
   *   doesnt give more info, so I guess that we can't do any
   *   online add index if not using primary key
   *
   *   This as mysql will handle a unique not null index as primary 
   *     even wo/ user specifiying it... :-(
   *   
   */
  if ((table_share->primary_key == MAX_KEY && pk) ||
      (table_share->primary_key != MAX_KEY && !pk) ||
      (table_share->primary_key == MAX_KEY && !pk && ai))
  {
    DBUG_RETURN(COMPATIBLE_DATA_NO);
  }
  
unknown's avatar
unknown committed
9900
  /* Check that auto_increment value was not changed */
unknown's avatar
unknown committed
9901 9902
  if ((create_info->used_fields & HA_CREATE_USED_AUTO) &&
      create_info->auto_increment_value != 0)
9903
    DBUG_RETURN(COMPATIBLE_DATA_NO);
unknown's avatar
unknown committed
9904 9905
  
  /* Check that row format didn't change */
unknown's avatar
unknown committed
9906
  if ((create_info->used_fields & HA_CREATE_USED_AUTO) &&
unknown's avatar
unknown committed
9907
      get_row_type() != create_info->row_type)
9908
    DBUG_RETURN(COMPATIBLE_DATA_NO);
unknown's avatar
unknown committed
9909

9910
  DBUG_RETURN(COMPATIBLE_DATA_YES);
unknown's avatar
unknown committed
9911 9912
}

unknown's avatar
unknown committed
9913
bool set_up_tablespace(st_alter_tablespace *alter_info,
unknown's avatar
unknown committed
9914 9915
                       NdbDictionary::Tablespace *ndb_ts)
{
unknown's avatar
unknown committed
9916 9917 9918 9919
  ndb_ts->setName(alter_info->tablespace_name);
  ndb_ts->setExtentSize(alter_info->extent_size);
  ndb_ts->setDefaultLogfileGroup(alter_info->logfile_group_name);
  return FALSE;
unknown's avatar
unknown committed
9920 9921
}

unknown's avatar
unknown committed
9922
bool set_up_datafile(st_alter_tablespace *alter_info,
unknown's avatar
unknown committed
9923 9924
                     NdbDictionary::Datafile *ndb_df)
{
unknown's avatar
unknown committed
9925
  if (alter_info->max_size > 0)
unknown's avatar
unknown committed
9926 9927
  {
    my_error(ER_TABLESPACE_AUTO_EXTEND_ERROR, MYF(0));
unknown's avatar
unknown committed
9928
    return TRUE;
unknown's avatar
unknown committed
9929
  }
unknown's avatar
unknown committed
9930 9931 9932 9933
  ndb_df->setPath(alter_info->data_file_name);
  ndb_df->setSize(alter_info->initial_size);
  ndb_df->setTablespace(alter_info->tablespace_name);
  return FALSE;
unknown's avatar
unknown committed
9934 9935
}

unknown's avatar
unknown committed
9936
bool set_up_logfile_group(st_alter_tablespace *alter_info,
unknown's avatar
unknown committed
9937 9938
                          NdbDictionary::LogfileGroup *ndb_lg)
{
unknown's avatar
unknown committed
9939 9940 9941
  ndb_lg->setName(alter_info->logfile_group_name);
  ndb_lg->setUndoBufferSize(alter_info->undo_buffer_size);
  return FALSE;
unknown's avatar
unknown committed
9942 9943
}

unknown's avatar
unknown committed
9944
bool set_up_undofile(st_alter_tablespace *alter_info,
unknown's avatar
unknown committed
9945 9946
                     NdbDictionary::Undofile *ndb_uf)
{
unknown's avatar
unknown committed
9947 9948 9949 9950
  ndb_uf->setPath(alter_info->undo_file_name);
  ndb_uf->setSize(alter_info->initial_size);
  ndb_uf->setLogfileGroup(alter_info->logfile_group_name);
  return FALSE;
unknown's avatar
unknown committed
9951 9952
}

unknown's avatar
unknown committed
9953 9954
int ndbcluster_alter_tablespace(handlerton *hton,
                                THD* thd, st_alter_tablespace *alter_info)
unknown's avatar
unknown committed
9955
{
unknown's avatar
unknown committed
9956 9957 9958 9959 9960 9961
  int is_tablespace= 0;
  NdbError err;
  NDBDICT *dict;
  int error;
  const char *errmsg;
  Ndb *ndb;
unknown's avatar
unknown committed
9962
  DBUG_ENTER("ha_ndbcluster::alter_tablespace");
unknown's avatar
unknown committed
9963
  LINT_INIT(errmsg);
unknown's avatar
unknown committed
9964

unknown's avatar
unknown committed
9965
  ndb= check_ndb_in_thd(thd);
unknown's avatar
unknown committed
9966
  if (ndb == NULL)
unknown's avatar
unknown committed
9967
  {
unknown's avatar
unknown committed
9968
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
unknown's avatar
unknown committed
9969
  }
unknown's avatar
unknown committed
9970
  dict= ndb->getDictionary();
unknown's avatar
unknown committed
9971

unknown's avatar
unknown committed
9972
  switch (alter_info->ts_cmd_type){
unknown's avatar
unknown committed
9973 9974
  case (CREATE_TABLESPACE):
  {
9975
    error= ER_CREATE_FILEGROUP_FAILED;
unknown's avatar
unknown committed
9976
    
unknown's avatar
unknown committed
9977 9978
    NdbDictionary::Tablespace ndb_ts;
    NdbDictionary::Datafile ndb_df;
unknown's avatar
unknown committed
9979
    NdbDictionary::ObjectId objid;
unknown's avatar
unknown committed
9980
    if (set_up_tablespace(alter_info, &ndb_ts))
unknown's avatar
unknown committed
9981 9982 9983
    {
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
9984
    if (set_up_datafile(alter_info, &ndb_df))
unknown's avatar
unknown committed
9985 9986 9987
    {
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
9988
    errmsg= "TABLESPACE";
unknown's avatar
unknown committed
9989
    if (dict->createTablespace(ndb_ts, &objid))
unknown's avatar
unknown committed
9990 9991
    {
      DBUG_PRINT("error", ("createTablespace returned %d", error));
unknown's avatar
unknown committed
9992
      goto ndberror;
unknown's avatar
unknown committed
9993
    }
unknown's avatar
unknown committed
9994
    DBUG_PRINT("alter_info", ("Successfully created Tablespace"));
unknown's avatar
unknown committed
9995 9996
    errmsg= "DATAFILE";
    if (dict->createDatafile(ndb_df))
unknown's avatar
unknown committed
9997
    {
unknown's avatar
unknown committed
9998 9999 10000 10001 10002 10003 10004 10005 10006
      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
10007
      DBUG_PRINT("error", ("createDatafile returned %d", error));
unknown's avatar
unknown committed
10008
      goto ndberror2;
unknown's avatar
unknown committed
10009
    }
10010
    is_tablespace= 1;
unknown's avatar
unknown committed
10011 10012 10013 10014
    break;
  }
  case (ALTER_TABLESPACE):
  {
10015
    error= ER_ALTER_FILEGROUP_FAILED;
unknown's avatar
unknown committed
10016
    if (alter_info->ts_alter_tablespace_type == ALTER_TABLESPACE_ADD_FILE)
unknown's avatar
unknown committed
10017 10018
    {
      NdbDictionary::Datafile ndb_df;
unknown's avatar
unknown committed
10019
      if (set_up_datafile(alter_info, &ndb_df))
unknown's avatar
unknown committed
10020 10021 10022
      {
	DBUG_RETURN(1);
      }
unknown's avatar
unknown committed
10023 10024
      errmsg= " CREATE DATAFILE";
      if (dict->createDatafile(ndb_df))
unknown's avatar
unknown committed
10025
      {
unknown's avatar
unknown committed
10026
	goto ndberror;
unknown's avatar
unknown committed
10027 10028
      }
    }
unknown's avatar
unknown committed
10029
    else if(alter_info->ts_alter_tablespace_type == ALTER_TABLESPACE_DROP_FILE)
unknown's avatar
unknown committed
10030
    {
unknown's avatar
unknown committed
10031 10032
      NdbDictionary::Tablespace ts= dict->getTablespace(alter_info->tablespace_name);
      NdbDictionary::Datafile df= dict->getDatafile(0, alter_info->data_file_name);
unknown's avatar
unknown committed
10033 10034 10035
      NdbDictionary::ObjectId objid;
      df.getTablespaceId(&objid);
      if (ts.getObjectId() == objid.getObjectId() && 
unknown's avatar
unknown committed
10036
	  strcmp(df.getPath(), alter_info->data_file_name) == 0)
unknown's avatar
unknown committed
10037
      {
unknown's avatar
unknown committed
10038 10039
	errmsg= " DROP DATAFILE";
	if (dict->dropDatafile(df))
unknown's avatar
unknown committed
10040
	{
unknown's avatar
unknown committed
10041
	  goto ndberror;
unknown's avatar
unknown committed
10042 10043 10044 10045 10046
	}
      }
      else
      {
	DBUG_PRINT("error", ("No such datafile"));
10047
	my_error(ER_ALTER_FILEGROUP_FAILED, MYF(0), " NO SUCH FILE");
unknown's avatar
unknown committed
10048 10049 10050 10051 10052 10053
	DBUG_RETURN(1);
      }
    }
    else
    {
      DBUG_PRINT("error", ("Unsupported alter tablespace: %d", 
unknown's avatar
unknown committed
10054
			   alter_info->ts_alter_tablespace_type));
unknown's avatar
unknown committed
10055 10056
      DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
    }
10057
    is_tablespace= 1;
unknown's avatar
unknown committed
10058 10059 10060 10061
    break;
  }
  case (CREATE_LOGFILE_GROUP):
  {
10062
    error= ER_CREATE_FILEGROUP_FAILED;
unknown's avatar
unknown committed
10063 10064
    NdbDictionary::LogfileGroup ndb_lg;
    NdbDictionary::Undofile ndb_uf;
unknown's avatar
unknown committed
10065
    NdbDictionary::ObjectId objid;
unknown's avatar
unknown committed
10066
    if (alter_info->undo_file_name == NULL)
unknown's avatar
unknown committed
10067 10068 10069 10070 10071 10072
    {
      /*
	REDO files in LOGFILE GROUP not supported yet
      */
      DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
    }
unknown's avatar
unknown committed
10073
    if (set_up_logfile_group(alter_info, &ndb_lg))
unknown's avatar
unknown committed
10074 10075 10076
    {
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
10077
    errmsg= "LOGFILE GROUP";
unknown's avatar
unknown committed
10078
    if (dict->createLogfileGroup(ndb_lg, &objid))
unknown's avatar
unknown committed
10079
    {
unknown's avatar
unknown committed
10080
      goto ndberror;
unknown's avatar
unknown committed
10081
    }
unknown's avatar
unknown committed
10082 10083
    DBUG_PRINT("alter_info", ("Successfully created Logfile Group"));
    if (set_up_undofile(alter_info, &ndb_uf))
unknown's avatar
unknown committed
10084 10085 10086
    {
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
10087 10088
    errmsg= "UNDOFILE";
    if (dict->createUndofile(ndb_uf))
unknown's avatar
unknown committed
10089
    {
unknown's avatar
unknown committed
10090 10091 10092 10093 10094 10095 10096 10097 10098
      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
10099 10100 10101 10102 10103
    }
    break;
  }
  case (ALTER_LOGFILE_GROUP):
  {
10104
    error= ER_ALTER_FILEGROUP_FAILED;
unknown's avatar
unknown committed
10105
    if (alter_info->undo_file_name == NULL)
unknown's avatar
unknown committed
10106 10107 10108 10109 10110 10111 10112
    {
      /*
	REDO files in LOGFILE GROUP not supported yet
      */
      DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
    }
    NdbDictionary::Undofile ndb_uf;
unknown's avatar
unknown committed
10113
    if (set_up_undofile(alter_info, &ndb_uf))
unknown's avatar
unknown committed
10114 10115 10116
    {
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
10117 10118
    errmsg= "CREATE UNDOFILE";
    if (dict->createUndofile(ndb_uf))
unknown's avatar
unknown committed
10119
    {
unknown's avatar
unknown committed
10120
      goto ndberror;
unknown's avatar
unknown committed
10121 10122 10123 10124 10125
    }
    break;
  }
  case (DROP_TABLESPACE):
  {
10126
    error= ER_DROP_FILEGROUP_FAILED;
unknown's avatar
unknown committed
10127
    errmsg= "TABLESPACE";
unknown's avatar
unknown committed
10128
    if (dict->dropTablespace(dict->getTablespace(alter_info->tablespace_name)))
unknown's avatar
unknown committed
10129
    {
unknown's avatar
unknown committed
10130
      goto ndberror;
unknown's avatar
unknown committed
10131
    }
10132
    is_tablespace= 1;
unknown's avatar
unknown committed
10133 10134 10135 10136
    break;
  }
  case (DROP_LOGFILE_GROUP):
  {
10137
    error= ER_DROP_FILEGROUP_FAILED;
unknown's avatar
unknown committed
10138
    errmsg= "LOGFILE GROUP";
unknown's avatar
unknown committed
10139
    if (dict->dropLogfileGroup(dict->getLogfileGroup(alter_info->logfile_group_name)))
unknown's avatar
unknown committed
10140
    {
unknown's avatar
unknown committed
10141
      goto ndberror;
unknown's avatar
unknown committed
10142 10143 10144 10145 10146 10147 10148 10149 10150 10151 10152 10153 10154 10155 10156 10157
    }
    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);
  }
  }
10158
#ifdef HAVE_NDB_BINLOG
10159 10160 10161
  if (is_tablespace)
    ndbcluster_log_schema_op(thd, 0,
                             thd->query, thd->query_length,
unknown's avatar
unknown committed
10162
                             "", alter_info->tablespace_name,
10163
                             0, 0,
10164
                             SOT_TABLESPACE, 0, 0, 0);
10165 10166 10167
  else
    ndbcluster_log_schema_op(thd, 0,
                             thd->query, thd->query_length,
unknown's avatar
unknown committed
10168
                             "", alter_info->logfile_group_name,
10169
                             0, 0,
10170
                             SOT_LOGFILE_GROUP, 0, 0, 0);
10171
#endif
unknown's avatar
unknown committed
10172
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
10173 10174

ndberror:
unknown's avatar
unknown committed
10175 10176
  err= dict->getNdbError();
ndberror2:
unknown's avatar
unknown committed
10177
  set_ndb_err(thd, err);
unknown's avatar
unknown committed
10178 10179 10180 10181
  ndb_to_mysql_error(&err);
  
  my_error(error, MYF(0), errmsg);
  DBUG_RETURN(1);
unknown's avatar
unknown committed
10182 10183
}

unknown's avatar
unknown committed
10184 10185 10186 10187 10188 10189 10190

bool ha_ndbcluster::get_no_parts(const char *name, uint *no_parts)
{
  Ndb *ndb;
  NDBDICT *dict;
  int err;
  DBUG_ENTER("ha_ndbcluster::get_no_parts");
10191
  LINT_INIT(err);
unknown's avatar
unknown committed
10192 10193 10194

  set_dbname(name);
  set_tabname(name);
10195
  for (;;)
unknown's avatar
unknown committed
10196 10197 10198 10199 10200 10201 10202
  {
    if (check_ndb_connection())
    {
      err= HA_ERR_NO_CONNECTION;
      break;
    }
    ndb= get_ndb();
10203
    ndb->setDatabaseName(m_dbname);
10204 10205
    Ndb_table_guard ndbtab_g(dict= ndb->getDictionary(), m_tabname);
    if (!ndbtab_g.get_table())
unknown's avatar
unknown committed
10206
      ERR_BREAK(dict->getNdbError(), err);
10207
    *no_parts= ndbtab_g.get_table()->getFragmentCount();
unknown's avatar
unknown committed
10208
    DBUG_RETURN(FALSE);
10209
  }
unknown's avatar
unknown committed
10210 10211 10212 10213 10214

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

10215 10216 10217
static int ndbcluster_fill_files_table(handlerton *hton, 
                                       THD *thd, 
                                       TABLE_LIST *tables,
10218
                                       COND *cond)
10219 10220
{
  TABLE* table= tables->table;
10221
  Ndb *ndb= check_ndb_in_thd(thd);
10222 10223
  NdbDictionary::Dictionary* dict= ndb->getDictionary();
  NdbDictionary::Dictionary::List dflist;
10224
  NdbError ndberr;
10225
  uint i;
10226
  DBUG_ENTER("ndbcluster_fill_files_table");
10227

10228 10229
  dict->listObjects(dflist, NdbDictionary::Object::Datafile);
  ndberr= dict->getNdbError();
10230 10231
  if (ndberr.classification != NdbError::NoError)
    ERR_RETURN(ndberr);
10232

10233
  for (i= 0; i < dflist.count; i++)
10234 10235 10236
  {
    NdbDictionary::Dictionary::List::Element& elt = dflist.elements[i];
    Ndb_cluster_connection_node_iter iter;
10237 10238
    uint id;
    
10239 10240
    g_ndb_cluster_connection->init_get_next_node(iter);

10241
    while ((id= g_ndb_cluster_connection->get_next_node(iter)))
10242
    {
10243
      init_fill_schema_files_row(table);
10244
      NdbDictionary::Datafile df= dict->getDatafile(id, elt.name);
10245
      ndberr= dict->getNdbError();
10246 10247 10248 10249
      if(ndberr.classification != NdbError::NoError)
      {
        if (ndberr.classification == NdbError::SchemaError)
          continue;
10250 10251 10252 10253

        if (ndberr.classification == NdbError::UnknownResultError)
          continue;

10254 10255
        ERR_RETURN(ndberr);
      }
10256 10257
      NdbDictionary::Tablespace ts= dict->getTablespace(df.getTablespace());
      ndberr= dict->getNdbError();
10258 10259 10260 10261 10262 10263
      if (ndberr.classification != NdbError::NoError)
      {
        if (ndberr.classification == NdbError::SchemaError)
          continue;
        ERR_RETURN(ndberr);
      }
10264

10265 10266 10267 10268 10269 10270 10271 10272 10273 10274 10275 10276 10277 10278 10279 10280 10281 10282 10283 10284 10285 10286 10287 10288 10289 10290 10291 10292 10293 10294 10295 10296 10297 10298 10299 10300 10301
      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);
10302 10303

      char extra[30];
10304
      int len= my_snprintf(extra, sizeof(extra), "CLUSTER_NODE=%u", id);
10305 10306
      table->field[IS_FILES_EXTRA]->set_notnull();
      table->field[IS_FILES_EXTRA]->store(extra, len, system_charset_info);
10307 10308 10309 10310
      schema_table_store_record(thd, table);
    }
  }

unknown's avatar
ndb -  
unknown committed
10311 10312
  NdbDictionary::Dictionary::List uflist;
  dict->listObjects(uflist, NdbDictionary::Object::Undofile);
10313
  ndberr= dict->getNdbError();
10314 10315
  if (ndberr.classification != NdbError::NoError)
    ERR_RETURN(ndberr);
10316

unknown's avatar
ndb -  
unknown committed
10317
  for (i= 0; i < uflist.count; i++)
10318
  {
unknown's avatar
ndb -  
unknown committed
10319
    NdbDictionary::Dictionary::List::Element& elt= uflist.elements[i];
10320 10321 10322 10323 10324
    Ndb_cluster_connection_node_iter iter;
    unsigned id;

    g_ndb_cluster_connection->init_get_next_node(iter);

10325
    while ((id= g_ndb_cluster_connection->get_next_node(iter)))
10326 10327
    {
      NdbDictionary::Undofile uf= dict->getUndofile(id, elt.name);
10328
      ndberr= dict->getNdbError();
10329 10330 10331 10332
      if (ndberr.classification != NdbError::NoError)
      {
        if (ndberr.classification == NdbError::SchemaError)
          continue;
unknown's avatar
unknown committed
10333 10334
        if (ndberr.classification == NdbError::UnknownResultError)
          continue;
10335 10336
        ERR_RETURN(ndberr);
      }
10337 10338 10339
      NdbDictionary::LogfileGroup lfg=
        dict->getLogfileGroup(uf.getLogfileGroup());
      ndberr= dict->getNdbError();
10340 10341 10342 10343 10344 10345
      if (ndberr.classification != NdbError::NoError)
      {
        if (ndberr.classification == NdbError::SchemaError)
          continue;
        ERR_RETURN(ndberr);
      }
10346

10347 10348 10349 10350 10351 10352 10353
      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
10354 10355
      NdbDictionary::ObjectId objid;
      uf.getLogfileGroupId(&objid);
10356 10357 10358 10359 10360 10361 10362 10363 10364 10365 10366 10367 10368 10369 10370 10371 10372 10373 10374 10375 10376 10377 10378
      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());
10379

10380
      char extra[100];
10381 10382
      int len= my_snprintf(extra,sizeof(extra),"CLUSTER_NODE=%u;UNDO_BUFFER_SIZE=%lu",
                           id, (ulong) lfg.getUndoBufferSize());
10383 10384
      table->field[IS_FILES_EXTRA]->set_notnull();
      table->field[IS_FILES_EXTRA]->store(extra, len, system_charset_info);
10385 10386 10387
      schema_table_store_record(thd, table);
    }
  }
10388 10389 10390 10391 10392 10393 10394 10395 10396 10397 10398 10399 10400 10401 10402 10403 10404 10405 10406 10407 10408

  // 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];

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

10409 10410 10411 10412 10413 10414 10415 10416 10417 10418 10419 10420 10421 10422 10423 10424 10425 10426 10427 10428 10429 10430 10431
    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());
10432 10433

    char extra[100];
10434 10435
    int len= my_snprintf(extra,sizeof(extra),
                         "UNDO_BUFFER_SIZE=%lu",
10436
                         (ulong) lfg.getUndoBufferSize());
10437 10438
    table->field[IS_FILES_EXTRA]->set_notnull();
    table->field[IS_FILES_EXTRA]->store(extra, len, system_charset_info);
10439 10440
    schema_table_store_record(thd, table);
  }
10441
  DBUG_RETURN(0);
10442
}
unknown's avatar
unknown committed
10443

unknown's avatar
unknown committed
10444 10445 10446 10447 10448
SHOW_VAR ndb_status_variables_export[]= {
  {"Ndb",                      (char*) &ndb_status_variables,   SHOW_ARRAY},
  {NullS, NullS, SHOW_LONG}
};

unknown's avatar
unknown committed
10449
struct st_mysql_storage_engine ndbcluster_storage_engine=
10450
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
unknown's avatar
unknown committed
10451 10452 10453 10454

mysql_declare_plugin(ndbcluster)
{
  MYSQL_STORAGE_ENGINE_PLUGIN,
unknown's avatar
unknown committed
10455
  &ndbcluster_storage_engine,
10456
  ndbcluster_hton_name,
unknown's avatar
unknown committed
10457
  "MySQL AB",
unknown's avatar
unknown committed
10458
  "Clustered, fault-tolerant tables",
10459
  PLUGIN_LICENSE_GPL,
unknown's avatar
unknown committed
10460
  ndbcluster_init, /* Plugin Init */
unknown's avatar
unknown committed
10461 10462
  NULL, /* Plugin Deinit */
  0x0100 /* 1.0 */,
10463 10464 10465
  ndb_status_variables_export,/* status variables                */
  NULL,                       /* system variables                */
  NULL                        /* config options                  */
unknown's avatar
unknown committed
10466 10467 10468 10469
}
mysql_declare_plugin_end;

#endif