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

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

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

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

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

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

#include "mysql_priv.h"

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

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

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

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

47 48 49 50
// options from from mysqld.cc
extern my_bool opt_ndb_optimized_node_selection;
extern const char *opt_ndbcluster_connectstring;

51 52 53 54 55 56
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
57
// Default value for parallelism
58
static const int parallelism= 0;
unknown's avatar
unknown committed
59

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

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

72 73 74
static const char ndbcluster_hton_name[]= "ndbcluster";
static const char ndbcluster_hton_comment[]= "Clustered, fault-tolerant tables";

75
handlerton ndbcluster_hton = {
unknown's avatar
unknown committed
76
  MYSQL_HANDLERTON_INTERFACE_VERSION,
unknown's avatar
unknown committed
77
  "ndbcluster",
78
  SHOW_OPTION_YES,
79
  "Clustered, fault-tolerant tables", 
80 81
  DB_TYPE_NDBCLUSTER,
  ndbcluster_init,
unknown's avatar
unknown committed
82
  ~(uint)0, /* slot */
83 84
  /* below are initialized by name in ndbcluster_init() */
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
85 86
};

unknown's avatar
unknown committed
87
static handler *ndbcluster_create_handler(TABLE_SHARE *table)
88 89 90 91
{
  return new ha_ndbcluster(table);
}

unknown's avatar
unknown committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
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);

}

109
#define NDB_FAILED_AUTO_INCREMENT ~(Uint64)0
110
#define NDB_AUTO_INCREMENT_RETRIES 10
unknown's avatar
unknown committed
111 112

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

115 116
#define ERR_RETURN(err)                  \
{                                        \
117
  const NdbError& tmp= err;              \
118
  ERR_PRINT(tmp);                        \
119
  DBUG_RETURN(ndb_to_mysql_error(&tmp)); \
unknown's avatar
unknown committed
120 121
}

122 123 124 125 126 127 128 129
#define ERR_BREAK(err, code)             \
{                                        \
  const NdbError& tmp= err;              \
  ERR_PRINT(tmp);                        \
  code= ndb_to_mysql_error(&tmp);        \
  break;                                 \
}

unknown's avatar
unknown committed
130
static int ndbcluster_inited= 0;
unknown's avatar
unknown committed
131
int ndbcluster_util_inited= 0;
unknown's avatar
unknown committed
132

133
static Ndb* g_ndb= NULL;
unknown's avatar
unknown committed
134 135
Ndb_cluster_connection* g_ndb_cluster_connection= NULL;
unsigned char g_node_id_map[max_ndb_nodes];
136

unknown's avatar
unknown committed
137 138 139 140
// Handler synchronization
pthread_mutex_t ndbcluster_mutex;

// Table lock handling
unknown's avatar
unknown committed
141
HASH ndbcluster_open_tables;
unknown's avatar
unknown committed
142 143 144

static byte *ndbcluster_get_key(NDB_SHARE *share,uint *length,
                                my_bool not_used __attribute__((unused)));
unknown's avatar
unknown committed
145 146 147
#ifdef HAVE_NDB_BINLOG
static int rename_share(NDB_SHARE *share, const char *new_key);
#endif
unknown's avatar
unknown committed
148
static void ndb_set_fragmentation(NDBTAB &tab, TABLE *table, uint pk_len);
unknown's avatar
unknown committed
149

150
static int ndb_get_table_statistics(Ndb*, const NDBTAB *, 
151
                                    struct Ndb_statistics *);
152

unknown's avatar
unknown committed
153

unknown's avatar
Merge  
unknown committed
154
// Util thread variables
unknown's avatar
unknown committed
155
pthread_t ndb_util_thread;
unknown's avatar
Merge  
unknown committed
156 157
pthread_mutex_t LOCK_ndb_util_thread;
pthread_cond_t COND_ndb_util_thread;
158
pthread_handler_t ndb_util_thread_func(void *arg);
unknown's avatar
Merge  
unknown committed
159
ulong ndb_cache_check_time;
unknown's avatar
unknown committed
160

161 162 163 164
/*
  Dummy buffer to read zero pack_length fields
  which are mapped to 1 char
*/
unknown's avatar
unknown committed
165
static uint32 dummy_buf;
166

167 168 169 170 171 172 173 174 175 176 177
/*
  Stats that can be retrieved from ndb
*/

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

178 179 180 181 182 183
/* Status variables shown with 'show status like 'Ndb%' */

static long ndb_cluster_node_id= 0;
static const char * ndb_connected_host= 0;
static long ndb_connected_port= 0;
static long ndb_number_of_replicas= 0;
unknown's avatar
unknown committed
184
long ndb_number_of_storage_nodes= 0;
185 186 187 188 189 190 191 192 193 194 195

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

unknown's avatar
unknown committed
196
SHOW_VAR ndb_status_variables[]= {
197 198 199 200 201 202 203 204
  {"cluster_node_id",        (char*) &ndb_cluster_node_id,         SHOW_LONG},
  {"connected_host",         (char*) &ndb_connected_host,      SHOW_CHAR_PTR},
  {"connected_port",         (char*) &ndb_connected_port,          SHOW_LONG},
//  {"number_of_replicas",     (char*) &ndb_number_of_replicas,      SHOW_LONG},
  {"number_of_storage_nodes",(char*) &ndb_number_of_storage_nodes, SHOW_LONG},
  {NullS, NullS, SHOW_LONG}
};

unknown's avatar
unknown committed
205 206 207 208
/*
  Error handling functions
*/

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

unknown's avatar
unknown committed
211
static int ndb_to_mysql_error(const NdbError *ndberr)
unknown's avatar
unknown committed
212
{
unknown's avatar
unknown committed
213 214
  /* read the mysql mapped error code */
  int error= ndberr->mysql_code;
215

unknown's avatar
unknown committed
216 217 218 219 220 221 222 223 224 225 226 227 228
  switch (error)
  {
    /* errors for which we do not add warnings, just return mapped error code
    */
  case HA_ERR_NO_SUCH_TABLE:
  case HA_ERR_KEY_NOT_FOUND:
  case HA_ERR_FOUND_DUPP_KEY:
    return error;

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

unknown's avatar
unknown committed
230 231 232 233
    /* Mapping exists, go with the mapped code */
  default:
    break;
  }
unknown's avatar
unknown committed
234

unknown's avatar
unknown committed
235 236 237 238 239 240
  /*
    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)
241
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
unknown's avatar
unknown committed
242 243 244 245 246 247 248
			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
249 250
}

unknown's avatar
unknown committed
251 252 253 254 255 256 257 258 259 260 261 262
int execute_no_commit_ignore_no_key(ha_ndbcluster *h, NdbTransaction *trans)
{
  int res= trans->execute(NdbTransaction::NoCommit,
                          NdbTransaction::AO_IgnoreError,
                          h->m_force_send);
  if (res == 0)
    return 0;

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

unknown's avatar
unknown committed
264 265
  return 0;
}
unknown's avatar
unknown committed
266 267

inline
268
int execute_no_commit(ha_ndbcluster *h, NdbTransaction *trans)
unknown's avatar
unknown committed
269
{
unknown's avatar
unknown committed
270
#ifdef NOT_USED
271
  int m_batch_execute= 0;
unknown's avatar
unknown committed
272
  if (m_batch_execute)
unknown's avatar
unknown committed
273
    return 0;
unknown's avatar
unknown committed
274
#endif
unknown's avatar
unknown committed
275 276 277 278 279
  return h->m_ignore_no_key ?
    execute_no_commit_ignore_no_key(h,trans) :
    trans->execute(NdbTransaction::NoCommit,
		   NdbTransaction::AbortOnError,
		   h->m_force_send);
unknown's avatar
unknown committed
280 281 282
}

inline
283
int execute_commit(ha_ndbcluster *h, NdbTransaction *trans)
unknown's avatar
unknown committed
284
{
unknown's avatar
unknown committed
285
#ifdef NOT_USED
286
  int m_batch_execute= 0;
unknown's avatar
unknown committed
287
  if (m_batch_execute)
unknown's avatar
unknown committed
288
    return 0;
unknown's avatar
unknown committed
289
#endif
290
  return trans->execute(NdbTransaction::Commit,
291 292
                        NdbTransaction::AbortOnError,
                        h->m_force_send);
293 294 295
}

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

inline
309
int execute_no_commit_ie(ha_ndbcluster *h, NdbTransaction *trans)
unknown's avatar
unknown committed
310
{
unknown's avatar
unknown committed
311
#ifdef NOT_USED
312
  int m_batch_execute= 0;
unknown's avatar
unknown committed
313
  if (m_batch_execute)
unknown's avatar
unknown committed
314
    return 0;
unknown's avatar
unknown committed
315
#endif
316
  return trans->execute(NdbTransaction::NoCommit,
317 318
                        NdbTransaction::AO_IgnoreError,
                        h->m_force_send);
unknown's avatar
unknown committed
319 320
}

321 322 323
/*
  Place holder for ha_ndbcluster thread specific data
*/
324 325 326 327 328 329 330 331
static
byte *thd_ndb_share_get_key(THD_NDB_SHARE *thd_ndb_share, uint *length,
                            my_bool not_used __attribute__((unused)))
{
  *length= sizeof(thd_ndb_share->key);
  return (byte*) thd_ndb_share->key;
}

332 333
Thd_ndb::Thd_ndb()
{
334
  ndb= new Ndb(g_ndb_cluster_connection, "");
335 336
  lock_count= 0;
  count= 0;
337 338
  all= NULL;
  stmt= NULL;
339
  error= 0;
unknown's avatar
unknown committed
340
  options= 0;
341
  (void) hash_init(&open_tables, &my_charset_bin, 5, 0, 0,
342
                   (hash_get_key)thd_ndb_share_get_key, 0, 0);
343 344 345 346
}

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

void
Thd_ndb::init_open_tables()
{
372 373
  count= 0;
  error= 0;
374 375 376 377 378 379 380
  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
381
  HASH_SEARCH_STATE state;
382
  THD_NDB_SHARE *thd_ndb_share=
unknown's avatar
unknown committed
383 384 385
    (THD_NDB_SHARE*)hash_first(&open_tables, (byte *)key, sizeof(key), &state);
  while (thd_ndb_share && thd_ndb_share->key != key)
    thd_ndb_share= (THD_NDB_SHARE*)hash_next(&open_tables, (byte *)key, sizeof(key), &state);
386 387 388 389 390
  if (thd_ndb_share == 0)
  {
    thd_ndb_share= (THD_NDB_SHARE *) alloc_root(&thd->transaction.mem_root,
                                                sizeof(THD_NDB_SHARE));
    thd_ndb_share->key= key;
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 395
    my_hash_insert(&open_tables, (byte *)thd_ndb_share);
  }
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
  DBUG_PRINT("exit", ("thd_ndb_share: 0x%x  key: 0x%x", thd_ndb_share, key));
403
  DBUG_RETURN(thd_ndb_share);
404 405
}

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

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

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

426 427
void ha_ndbcluster::records_update()
{
428 429
  if (m_ha_not_exact_count)
    return;
430
  DBUG_ENTER("ha_ndbcluster::records_update");
431
  struct Ndb_local_table_statistics *info= m_table_info;
432
  DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
433 434
                      ((const NDBTAB *)m_table)->getTableId(),
                      info->no_uncommitted_rows_count));
435
  //  if (info->records == ~(ha_rows)0)
436
  {
437
    Ndb *ndb= get_ndb();
438
    ndb->setDatabaseName(m_dbname);
439
    struct Ndb_statistics stat;
440
    if (ndb_get_table_statistics(ndb, m_table, &stat) == 0){
441 442 443
      mean_rec_length= stat.row_size;
      data_file_length= stat.fragment_memory;
      info->records= stat.row_count;
444 445
    }
  }
446 447
  {
    THD *thd= current_thd;
448
    if (get_thd_ndb(thd)->error)
449 450
      info->no_uncommitted_rows_count= 0;
  }
451 452 453 454
  records= info->records+ info->no_uncommitted_rows_count;
  DBUG_VOID_RETURN;
}

455 456
void ha_ndbcluster::no_uncommitted_rows_execute_failure()
{
457 458
  if (m_ha_not_exact_count)
    return;
459
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_execute_failure");
460
  get_thd_ndb(current_thd)->error= 1;
461 462 463
  DBUG_VOID_RETURN;
}

464 465
void ha_ndbcluster::no_uncommitted_rows_update(int c)
{
466 467
  if (m_ha_not_exact_count)
    return;
468
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_update");
469
  struct Ndb_local_table_statistics *info= m_table_info;
470 471
  info->no_uncommitted_rows_count+= c;
  DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
472 473
                      ((const NDBTAB *)m_table)->getTableId(),
                      info->no_uncommitted_rows_count));
474 475 476 477 478
  DBUG_VOID_RETURN;
}

void ha_ndbcluster::no_uncommitted_rows_reset(THD *thd)
{
479 480
  if (m_ha_not_exact_count)
    return;
481
  DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_reset");
482 483 484
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  thd_ndb->count++;
  thd_ndb->error= 0;
485 486 487
  DBUG_VOID_RETURN;
}

488
int ha_ndbcluster::ndb_err(NdbTransaction *trans)
unknown's avatar
unknown committed
489
{
490
  int res;
491
  NdbError err= trans->getNdbError();
unknown's avatar
unknown committed
492 493 494 495 496
  DBUG_ENTER("ndb_err");
  
  ERR_PRINT(err);
  switch (err.classification) {
  case NdbError::SchemaError:
497
  {
498 499
    // TODO perhaps we need to do more here, invalidate also in the cache
    m_table->setStatusInvalid();
500 501 502 503 504 505
    /* Close other open handlers not used by any thread */
    TABLE_LIST table_list;
    bzero((char*) &table_list,sizeof(table_list));
    table_list.db= m_dbname;
    table_list.alias= table_list.table_name= m_tabname;
    close_cached_tables(current_thd, 0, &table_list);
unknown's avatar
unknown committed
506
    break;
507
  }
unknown's avatar
unknown committed
508 509 510
  default:
    break;
  }
511 512
  res= ndb_to_mysql_error(&err);
  DBUG_PRINT("info", ("transformed ndbcluster error %d to mysql error %d", 
513
                      err.code, res));
514
  if (res == HA_ERR_FOUND_DUPP_KEY)
515 516
  {
    if (m_rows_to_insert == 1)
unknown's avatar
unknown committed
517
      m_dupkey= table_share->primary_key;
518
    else
unknown's avatar
unknown committed
519 520
    {
      /* We are batching inserts, offending key is not available */
521
      m_dupkey= (uint) -1;
unknown's avatar
unknown committed
522
    }
523
  }
524
  DBUG_RETURN(res);
unknown's avatar
unknown committed
525 526 527
}


528
/*
529
  Override the default get_error_message in order to add the 
530 531 532
  error message of NDB 
 */

533
bool ha_ndbcluster::get_error_message(int error, 
534
                                      String *buf)
535
{
536
  DBUG_ENTER("ha_ndbcluster::get_error_message");
537
  DBUG_PRINT("enter", ("error: %d", error));
538

539
  Ndb *ndb= get_ndb();
540
  if (!ndb)
unknown's avatar
unknown committed
541
    DBUG_RETURN(FALSE);
542

543
  const NdbError err= ndb->getNdbError(error);
544 545 546 547
  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);
548 549 550
}


unknown's avatar
unknown committed
551
#ifndef DBUG_OFF
unknown's avatar
unknown committed
552 553 554 555
/*
  Check if type is supported by NDB.
*/

unknown's avatar
unknown committed
556
static bool ndb_supported_type(enum_field_types type)
unknown's avatar
unknown committed
557 558
{
  switch (type) {
unknown's avatar
unknown committed
559 560 561 562 563 564 565
  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:
566 567
  case MYSQL_TYPE_DECIMAL:    
  case MYSQL_TYPE_NEWDECIMAL:
unknown's avatar
unknown committed
568 569 570 571 572 573 574 575
  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
576
  case MYSQL_TYPE_VARCHAR:
unknown's avatar
unknown committed
577 578 579 580 581 582
  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:         
583
  case MYSQL_TYPE_BIT:
584
  case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
585
    return TRUE;
unknown's avatar
unknown committed
586
  case MYSQL_TYPE_NULL:   
unknown's avatar
unknown committed
587
    break;
unknown's avatar
unknown committed
588
  }
unknown's avatar
unknown committed
589
  return FALSE;
unknown's avatar
unknown committed
590
}
unknown's avatar
unknown committed
591
#endif /* !DBUG_OFF */
unknown's avatar
unknown committed
592 593


unknown's avatar
unknown committed
594 595 596 597 598
/*
  Instruct NDB to set the value of the hidden primary key
*/

bool ha_ndbcluster::set_hidden_key(NdbOperation *ndb_op,
599
                                   uint fieldnr, const byte *field_ptr)
unknown's avatar
unknown committed
600 601
{
  DBUG_ENTER("set_hidden_key");
unknown's avatar
unknown committed
602
  DBUG_RETURN(ndb_op->equal(fieldnr, (char*)field_ptr) != 0);
unknown's avatar
unknown committed
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
}


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

int ha_ndbcluster::set_ndb_key(NdbOperation *ndb_op, Field *field,
                               uint fieldnr, const byte *field_ptr)
{
  uint32 pack_len= field->pack_length();
  DBUG_ENTER("set_ndb_key");
  DBUG_PRINT("enter", ("%d: %s, ndb_type: %u, len=%d", 
                       fieldnr, field->field_name, field->type(),
                       pack_len));
  DBUG_DUMP("key", (char*)field_ptr, pack_len);
  
unknown's avatar
unknown committed
620 621 622 623
  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
624 625 626 627 628 629 630 631
}


/*
 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
632 633
                                 uint fieldnr, int row_offset,
                                 bool *set_blob_value)
unknown's avatar
unknown committed
634
{
unknown's avatar
unknown committed
635 636
  const byte* field_ptr= field->ptr + row_offset;
  uint32 pack_len= field->pack_length();
unknown's avatar
unknown committed
637
  DBUG_ENTER("set_ndb_value");
unknown's avatar
unknown committed
638
  DBUG_PRINT("enter", ("%d: %s  type: %u  len=%d  is_null=%s", 
unknown's avatar
unknown committed
639
                       fieldnr, field->field_name, field->type(), 
unknown's avatar
unknown committed
640
                       pack_len, field->is_null(row_offset) ? "Y" : "N"));
unknown's avatar
unknown committed
641
  DBUG_DUMP("value", (char*) field_ptr, pack_len);
unknown's avatar
unknown committed
642

unknown's avatar
unknown committed
643
  DBUG_ASSERT(ndb_supported_type(field->type()));
unknown's avatar
unknown committed
644
  {
645
    // ndb currently does not support size 0
unknown's avatar
unknown committed
646
    uint32 empty_field;
647 648
    if (pack_len == 0)
    {
unknown's avatar
unknown committed
649 650
      pack_len= sizeof(empty_field);
      field_ptr= (byte *)&empty_field;
unknown's avatar
unknown committed
651
      if (field->is_null(row_offset))
652
        empty_field= 0;
unknown's avatar
unknown committed
653
      else
654
        empty_field= 1;
655
    }
unknown's avatar
unknown committed
656 657
    if (! (field->flags & BLOB_FLAG))
    {
658 659
      if (field->type() != MYSQL_TYPE_BIT)
      {
unknown's avatar
unknown committed
660 661 662
        if (field->is_null(row_offset))
        {
          DBUG_PRINT("info", ("field is NULL"));
663
          // Set value to NULL
unknown's avatar
unknown committed
664
          DBUG_RETURN((ndb_op->setValue(fieldnr, (char*)NULL) != 0));
unknown's avatar
unknown committed
665
	}
666
        // Common implementation for most field types
unknown's avatar
unknown committed
667
        DBUG_RETURN(ndb_op->setValue(fieldnr, (char*)field_ptr) != 0);
668 669 670
      }
      else // if (field->type() == MYSQL_TYPE_BIT)
      {
671
        longlong bits= field->val_int();
672
 
673 674
        // Round up bit field length to nearest word boundry
        pack_len= ((pack_len + 3) >> 2) << 2;
675
        DBUG_ASSERT(pack_len <= 8);
unknown's avatar
unknown committed
676
        if (field->is_null(row_offset))
677
          // Set value to NULL
unknown's avatar
unknown committed
678
          DBUG_RETURN((ndb_op->setValue(fieldnr, (char*)NULL) != 0));
679
        DBUG_PRINT("info", ("bit field"));
680
        DBUG_DUMP("value", (char*)&bits, pack_len);
681
#ifdef WORDS_BIGENDIAN
682 683
        if (pack_len < 5)
        {
unknown's avatar
unknown committed
684
          DBUG_RETURN(ndb_op->setValue(fieldnr, ((char*)&bits)+4) != 0);
685
        }
686
#endif
unknown's avatar
unknown committed
687
        DBUG_RETURN(ndb_op->setValue(fieldnr, (char*)&bits) != 0);
688
      }
unknown's avatar
unknown committed
689 690
    }
    // Blob type
691
    NdbBlob *ndb_blob= ndb_op->getBlobHandle(fieldnr);
unknown's avatar
unknown committed
692 693
    if (ndb_blob != NULL)
    {
unknown's avatar
unknown committed
694
      if (field->is_null(row_offset))
unknown's avatar
unknown committed
695 696 697 698 699 700 701 702 703
        DBUG_RETURN(ndb_blob->setNull() != 0);

      Field_blob *field_blob= (Field_blob*)field;

      // Get length and pointer to data
      uint32 blob_len= field_blob->get_length(field_ptr);
      char* blob_ptr= NULL;
      field_blob->get_ptr(&blob_ptr);

704 705 706
      // Looks like NULL ptr signals length 0 blob
      if (blob_ptr == NULL) {
        DBUG_ASSERT(blob_len == 0);
707
        blob_ptr= (char*)"";
708
      }
unknown's avatar
unknown committed
709

unknown's avatar
unknown committed
710 711
      DBUG_PRINT("value", ("set blob ptr=%p len=%u",
                           blob_ptr, blob_len));
unknown's avatar
unknown committed
712 713
      DBUG_DUMP("value", (char*)blob_ptr, min(blob_len, 26));

714
      if (set_blob_value)
715
        *set_blob_value= TRUE;
unknown's avatar
unknown committed
716 717 718 719
      // No callback needed to write value
      DBUG_RETURN(ndb_blob->setValue(blob_ptr, blob_len) != 0);
    }
    DBUG_RETURN(1);
unknown's avatar
unknown committed
720
  }
unknown's avatar
unknown committed
721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
}


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

unknown's avatar
unknown committed
738
int g_get_ndb_blobs_value(NdbBlob *ndb_blob, void *arg)
unknown's avatar
unknown committed
739
{
unknown's avatar
unknown committed
740
  DBUG_ENTER("g_get_ndb_blobs_value");
unknown's avatar
unknown committed
741 742 743
  if (ndb_blob->blobsNextBlob() != NULL)
    DBUG_RETURN(0);
  ha_ndbcluster *ha= (ha_ndbcluster *)arg;
744 745 746 747
  int ret= get_ndb_blobs_value(ha->table, ha->m_value,
                               ha->m_blobs_buffer, ha->m_blobs_buffer_size,
                               0);
  DBUG_RETURN(ret);
unknown's avatar
unknown committed
748 749
}

750 751 752 753 754 755 756 757
/*
  This routine is shared by injector.  There is no common blobs buffer
  so the buffer and length are passed by reference.  Injector also
  passes a record pointer diff.
 */
int get_ndb_blobs_value(TABLE* table, NdbValue* value_array,
                        byte*& buffer, uint& buffer_size,
                        my_ptrdiff_t ptrdiff)
unknown's avatar
unknown committed
758 759 760 761 762 763 764 765
{
  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;
766
    for (uint i= 0; i < table->s->fields; i++)
unknown's avatar
unknown committed
767 768
    {
      Field *field= table->field[i];
769
      NdbValue value= value_array[i];
770 771 772
      if (! (field->flags & BLOB_FLAG))
        continue;
      if (value.blob == NULL)
unknown's avatar
unknown committed
773
      {
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
        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
791
        {
792 793 794 795 796 797 798 799 800 801 802
          char *buf= buffer + offset;
          uint32 len= 0xffffffff;  // Max uint32
          if (ndb_blob->readData(buf, len) != 0)
            ERR_RETURN(ndb_blob->getNdbError());
          DBUG_PRINT("info", ("[%u] offset=%u buf=%p len=%u [ptrdiff=%d]",
                              i, offset, buf, len, (int)ptrdiff));
          DBUG_ASSERT(len == len64);
          // Ugly hack assumes only ptr needs to be changed
          field_blob->ptr+= ptrdiff;
          field_blob->set_ptr(len, buf);
          field_blob->ptr-= ptrdiff;
unknown's avatar
unknown committed
803
        }
804 805 806 807 808 809 810 811 812 813 814
        offset+= size;
      }
      else if (loop == 1) // undefined or null
      {
        // have to set length even in this case
        char *buf= buffer + offset; // or maybe NULL
        uint32 len= 0;
        field_blob->ptr+= ptrdiff;
        field_blob->set_ptr(len, buf);
        field_blob->ptr-= ptrdiff;
        DBUG_PRINT("info", ("[%u] isNull=%d", i, isNull));
unknown's avatar
unknown committed
815 816
      }
    }
817
    if (loop == 0 && offset > buffer_size)
unknown's avatar
unknown committed
818
    {
819 820 821 822 823
      my_free(buffer, MYF(MY_ALLOW_ZERO_PTR));
      buffer_size= 0;
      DBUG_PRINT("info", ("allocate blobs buffer size %u", offset));
      buffer= my_malloc(offset, MYF(MY_WME));
      if (buffer == NULL)
unknown's avatar
unknown committed
824
        DBUG_RETURN(-1);
825
      buffer_size= offset;
unknown's avatar
unknown committed
826
    }
unknown's avatar
unknown committed
827
  }
unknown's avatar
unknown committed
828
  DBUG_RETURN(0);
unknown's avatar
unknown committed
829 830 831 832 833
}


/*
  Instruct NDB to fetch one field
unknown's avatar
unknown committed
834 835
  - 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
836 837
*/

unknown's avatar
unknown committed
838
int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field,
unknown's avatar
unknown committed
839
                                 uint fieldnr, byte* buf)
unknown's avatar
unknown committed
840 841
{
  DBUG_ENTER("get_ndb_value");
unknown's avatar
unknown committed
842 843 844 845 846
  DBUG_PRINT("enter", ("fieldnr: %d flags: %o", fieldnr,
                       (int)(field != NULL ? field->flags : 0)));

  if (field != NULL)
  {
unknown's avatar
unknown committed
847 848
      DBUG_ASSERT(buf);
      DBUG_ASSERT(ndb_supported_type(field->type()));
unknown's avatar
unknown committed
849 850
      DBUG_ASSERT(field->ptr != NULL);
      if (! (field->flags & BLOB_FLAG))
851
      { 
852 853
        if (field->type() != MYSQL_TYPE_BIT)
        {
854 855 856 857 858 859 860 861
          byte *field_buf;
          if (field->pack_length() != 0)
            field_buf= buf + (field->ptr - table->record[0]);
          else
            field_buf= (byte *)&dummy_buf;
          m_value[fieldnr].rec= ndb_op->getValue(fieldnr, 
                                                 field_buf);
        }
862 863 864 865
        else // if (field->type() == MYSQL_TYPE_BIT)
        {
          m_value[fieldnr].rec= ndb_op->getValue(fieldnr);
        }
unknown's avatar
unknown committed
866 867 868 869 870 871 872 873 874 875
        DBUG_RETURN(m_value[fieldnr].rec == NULL);
      }

      // Blob type
      NdbBlob *ndb_blob= ndb_op->getBlobHandle(fieldnr);
      m_value[fieldnr].blob= ndb_blob;
      if (ndb_blob != NULL)
      {
        // Set callback
        void *arg= (void *)this;
unknown's avatar
unknown committed
876
        DBUG_RETURN(ndb_blob->setActiveHook(g_get_ndb_blobs_value, arg) != 0);
unknown's avatar
unknown committed
877 878 879 880 881
      }
      DBUG_RETURN(1);
  }

  // Used for hidden key only
882
  m_value[fieldnr].rec= ndb_op->getValue(fieldnr, m_ref);
unknown's avatar
unknown committed
883 884 885
  DBUG_RETURN(m_value[fieldnr].rec == NULL);
}

886 887 888 889 890 891
/*
  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");
892 893
  DBUG_RETURN(ndb_op->getValue(NdbDictionary::Column::FRAGMENT, 
                               (char *)&m_part_id) == NULL);
894
}
unknown's avatar
unknown committed
895 896 897 898

/*
  Check if any set or get of blob value in current query.
*/
899
bool ha_ndbcluster::uses_blob_value()
unknown's avatar
unknown committed
900
{
unknown's avatar
unknown committed
901
  if (table_share->blob_fields == 0)
unknown's avatar
unknown committed
902
    return FALSE;
unknown's avatar
unknown committed
903
  {
unknown's avatar
unknown committed
904
    uint no_fields= table_share->fields;
unknown's avatar
unknown committed
905 906 907 908
    int i;
    // They always put blobs at the end..
    for (i= no_fields - 1; i >= 0; i--)
    {
909 910
      if ((m_write_op && ha_get_bit_in_write_set(i+1)) ||
          (!m_write_op && ha_get_bit_in_read_set(i+1)))
unknown's avatar
unknown committed
911
      {
unknown's avatar
unknown committed
912
        return TRUE;
unknown's avatar
unknown committed
913 914 915
      }
    }
  }
unknown's avatar
unknown committed
916
  return FALSE;
unknown's avatar
unknown committed
917 918 919 920 921 922 923 924 925
}


/*
  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
926 927 928 929

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

932 933
int cmp_frm(const NDBTAB *ndbtab, const void *pack_data,
            uint pack_length)
unknown's avatar
unknown committed
934 935 936 937 938 939 940 941 942 943 944
{
  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
945 946
int ha_ndbcluster::get_metadata(const char *path)
{
947 948
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
unknown's avatar
unknown committed
949 950 951 952 953
  const NDBTAB *tab;
  int error;
  DBUG_ENTER("get_metadata");
  DBUG_PRINT("enter", ("m_tabname: %s, path: %s", m_tabname, path));

954 955
  DBUG_ASSERT(m_table == NULL);
  DBUG_ASSERT(m_table_info == NULL);
956

957 958 959 960 961 962 963 964 965 966 967 968 969 970
  const void *data, *pack_data;
  uint length, pack_length;

  /*
    Compare FrmData in NDB with frm file from disk.
  */
  error= 0;
  if (readfrm(path, &data, &length) ||
      packfrm(data, length, &pack_data, &pack_length))
  {
    my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
    my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR));
    DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
971
    
972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988
  Ndb_table_guard ndbtab_g(dict, m_tabname);
  if (!(tab= ndbtab_g.get_table()))
    ERR_RETURN(dict->getNdbError());

  if (get_ndb_share_state(m_share) != NSS_ALTERED 
      && cmp_frm(tab, pack_data, pack_length))
  {
    DBUG_PRINT("error", 
               ("metadata, pack_length: %d  getFrmLength: %d  memcmp: %d",
                pack_length, tab->getFrmLength(),
                memcmp(pack_data, tab->getFrmData(), pack_length)));
    DBUG_DUMP("pack_data", (char*)pack_data, pack_length);
    DBUG_DUMP("frm", (char*)tab->getFrmData(), tab->getFrmLength());
    error= HA_ERR_TABLE_DEF_CHANGED;
  }
  my_free((char*)data, MYF(0));
  my_free((char*)pack_data, MYF(0));
989

unknown's avatar
unknown committed
990
  if (error)
991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
    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);
1004
}
unknown's avatar
unknown committed
1005

1006
static int fix_unique_index_attr_order(NDB_INDEX_DATA &data,
1007 1008
                                       const NDBINDEX *index,
                                       KEY *key_info)
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027
{
  DBUG_ENTER("fix_unique_index_attr_order");
  unsigned sz= index->getNoOfIndexColumns();

  if (data.unique_index_attrid_map)
    my_free((char*)data.unique_index_attrid_map, MYF(0));
  data.unique_index_attrid_map= (unsigned char*)my_malloc(sz,MYF(MY_WME));

  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
  DBUG_ASSERT(key_info->key_parts == sz);
  for (unsigned i= 0; key_part != end; key_part++, i++) 
  {
    const char *field_name= key_part->field->field_name;
#ifndef DBUG_OFF
   data.unique_index_attrid_map[i]= 255;
#endif
    for (unsigned j= 0; j < sz; j++)
    {
1028
      const NDBCOL *c= index->getColumn(j);
unknown's avatar
unknown committed
1029
      if (strcmp(field_name, c->getName()) == 0)
1030
      {
1031 1032
        data.unique_index_attrid_map[i]= j;
        break;
1033 1034 1035 1036 1037 1038
      }
    }
    DBUG_ASSERT(data.unique_index_attrid_map[i] != 255);
  }
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1039

1040 1041 1042 1043 1044 1045
/*
  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)
1046
{
1047
  uint i;
unknown's avatar
unknown committed
1048
  int error= 0;
1049
  const char *index_name;
unknown's avatar
unknown committed
1050
  KEY* key_info= tab->key_info;
1051
  const char **key_name= tab->s->keynames.type_names;
1052
  NDBDICT *dict= ndb->getDictionary();
1053
  DBUG_ENTER("ha_ndbcluster::create_indexes");
1054
  
1055
  for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
1056
  {
unknown's avatar
unknown committed
1057
    index_name= *key_name;
1058
    NDB_INDEX_TYPE idx_type= get_index_type_from_table(i);
1059 1060
    error= create_index(index_name, key_info, idx_type, i);
    if (error)
1061
    {
1062 1063
      DBUG_PRINT("error", ("Failed to create index %u", i));
      break;
1064
    }
1065 1066 1067 1068 1069
  }

  DBUG_RETURN(error);
}

unknown's avatar
ndb:  
unknown committed
1070
static void ndb_init_index(NDB_INDEX_DATA &data)
1071
{
unknown's avatar
ndb:  
unknown committed
1072 1073 1074 1075 1076 1077 1078 1079 1080
  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;
1081 1082
}

unknown's avatar
ndb:  
unknown committed
1083
static void ndb_clear_index(NDB_INDEX_DATA &data)
1084
{
unknown's avatar
ndb:  
unknown committed
1085 1086 1087 1088 1089 1090 1091 1092 1093
  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);
1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
}

/*
  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;
1106 1107
  DBUG_ENTER("ha_ndbcluster::add_index_handle");
  DBUG_PRINT("enter", ("table %s", m_tabname));
1108 1109 1110 1111

  if (idx_type != PRIMARY_KEY_INDEX && idx_type != UNIQUE_INDEX)
  {
    DBUG_PRINT("info", ("Get handle to index %s", index_name));
1112 1113 1114
    const NDBINDEX *index;
    do
    {
1115
      index= dict->getIndexGlobal(index_name, *m_table);
1116 1117 1118 1119 1120 1121 1122 1123
      if (!index)
        ERR_RETURN(dict->getNdbError());
      DBUG_PRINT("info", ("index: 0x%x  id: %d  version: %d.%d  status: %d",
                          index,
                          index->getObjectId(),
                          index->getObjectVersion() & 0xFFFFFF,
                          index->getObjectVersion() >> 24,
                          index->getObjectStatus()));
1124 1125
      DBUG_ASSERT(index->getObjectStatus() ==
                  NdbDictionary::Object::Retrieved);
1126 1127
      break;
    } while (1);
1128
    m_index[index_no].index= index;
1129 1130 1131 1132 1133
    // 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
1134
    {
1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152
      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";
1153
    m_has_unique_index= TRUE;
1154 1155
    strxnmov(unique_index_name, FN_LEN, index_name, unique_suffix, NullS);
    DBUG_PRINT("info", ("Get handle to unique_index %s", unique_index_name));
1156 1157 1158
    const NDBINDEX *index;
    do
    {
1159
      index= dict->getIndexGlobal(unique_index_name, *m_table);
1160 1161 1162 1163 1164 1165 1166 1167
      if (!index)
        ERR_RETURN(dict->getNdbError());
      DBUG_PRINT("info", ("index: 0x%x  id: %d  version: %d.%d  status: %d",
                          index,
                          index->getObjectId(),
                          index->getObjectVersion() & 0xFFFFFF,
                          index->getObjectVersion() >> 24,
                          index->getObjectStatus()));
1168 1169
      DBUG_ASSERT(index->getObjectStatus() ==
                  NdbDictionary::Object::Retrieved);
1170 1171
      break;
    } while (1);
1172
    m_index[index_no].unique_index= index;
1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183
    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
*/
1184
int ha_ndbcluster::open_indexes(Ndb *ndb, TABLE *tab, bool ignore_error)
1185 1186 1187 1188 1189 1190 1191 1192 1193
{
  uint i;
  int error= 0;
  THD *thd=current_thd;
  NDBDICT *dict= ndb->getDictionary();
  const char *index_name;
  KEY* key_info= tab->key_info;
  const char **key_name= tab->s->keynames.type_names;
  DBUG_ENTER("ha_ndbcluster::open_indexes");
1194
  m_has_unique_index= FALSE;
1195 1196 1197
  for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
  {
    if ((error= add_index_handle(thd, dict, key_info, *key_name, i)))
1198 1199 1200 1201
      if (ignore_error)
        m_index[i].index= m_index[i].unique_index= NULL;
      else
        break;
1202
  }
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223

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

1224 1225 1226 1227 1228 1229 1230
  DBUG_RETURN(error);
}

/*
  Renumber indexes in index list by shifting out
  indexes that are to be dropped
 */
1231
void ha_ndbcluster::renumber_indexes(Ndb *ndb, TABLE *tab)
1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252
{
  uint i;
  const char *index_name;
  KEY* key_info= tab->key_info;
  const char **key_name= tab->s->keynames.type_names;
  NDBDICT *dict= ndb->getDictionary();
  DBUG_ENTER("ha_ndbcluster::renumber_indexes");
  
  for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
  {
    index_name= *key_name;
    NDB_INDEX_TYPE idx_type= get_index_type_from_table(i);
    m_index[i].type= idx_type;
    if (m_index[i].status == TO_BE_DROPPED) 
    {
      DBUG_PRINT("info", ("Shifting index %s(%i) out of the list", 
                          index_name, i));
      NDB_INDEX_DATA tmp;
      uint j= i + 1;
      // Shift index out of list
      while(j != MAX_KEY && m_index[j].status != UNDEFINED)
unknown's avatar
unknown committed
1253
      {
1254 1255 1256 1257
        tmp=  m_index[j - 1];
        m_index[j - 1]= m_index[j];
        m_index[j]= tmp;
        j++;
unknown's avatar
unknown committed
1258 1259
      }
    }
1260 1261
  }

1262
  DBUG_VOID_RETURN;
1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276
}

/*
  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
1277
  for (i= 0; i < tab->s->keys; i++, key_info++)
1278 1279 1280 1281
  {
    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)
1282
    {
1283 1284
      const NdbDictionary::Index *index= m_index[i].index;
      const NdbDictionary::Index *unique_index= m_index[i].unique_index;
1285 1286
      
      if (index)
1287
      {
1288 1289 1290
        index_name= index->getName();
        DBUG_PRINT("info", ("Dropping index %u: %s", i, index_name));  
        // Drop ordered index from ndb
1291 1292 1293 1294 1295 1296
        error= dict->dropIndexGlobal(*index);
        if (!error)
        {
          dict->removeIndexGlobal(*index, 1);
          m_index[i].index= NULL;
        }
1297 1298
      }
      if (!error && unique_index)
1299
      {
unknown's avatar
unknown committed
1300 1301
        index_name= unique_index->getName();
        DBUG_PRINT("info", ("Dropping unique index %u: %s", i, index_name));
1302
        // Drop unique index from ndb
1303 1304 1305 1306 1307 1308
        error= dict->dropIndexGlobal(*unique_index);
        if (!error)
        {
          dict->removeIndexGlobal(*unique_index, 1);
          m_index[i].unique_index= NULL;
        }
1309
      }
1310 1311
      if (error)
        DBUG_RETURN(error);
unknown's avatar
ndb:  
unknown committed
1312
      ndb_clear_index(m_index[i]);
1313
      continue;
1314
    }
1315
  }
unknown's avatar
unknown committed
1316 1317
  
  DBUG_RETURN(error);
1318 1319
}

unknown's avatar
unknown committed
1320 1321 1322 1323
/*
  Decode the type of an index from information 
  provided in table object
*/
1324
NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_table(uint inx) const
unknown's avatar
unknown committed
1325
{
1326 1327
  return get_index_type_from_key(inx, table_share->key_info,
                                 inx == table_share->primary_key);
1328 1329 1330
}

NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_key(uint inx,
1331 1332
                                                      KEY *key_info,
                                                      bool primary) const
1333 1334
{
  bool is_hash_index=  (key_info[inx].algorithm == 
unknown's avatar
unknown committed
1335
                        HA_KEY_ALG_HASH);
1336
  if (primary)
1337
    return is_hash_index ? PRIMARY_KEY_INDEX : PRIMARY_KEY_ORDERED_INDEX;
1338 1339
  
  return ((key_info[inx].flags & HA_NOSAME) ? 
1340 1341
          (is_hash_index ? UNIQUE_INDEX : UNIQUE_ORDERED_INDEX) :
          ORDERED_INDEX);
unknown's avatar
unknown committed
1342
} 
1343

1344 1345 1346 1347 1348
int ha_ndbcluster::check_index_fields_not_null(uint inx)
{
  KEY* key_info= table->key_info + inx;
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
1349
  DBUG_ENTER("ha_ndbcluster::check_index_fields_not_null");
1350 1351 1352 1353 1354 1355
  
  for (; key_part != end; key_part++) 
    {
      Field* field= key_part->field;
      if (field->maybe_null())
      {
1356 1357 1358
        my_printf_error(ER_NULL_COLUMN_IN_INDEX,ER(ER_NULL_COLUMN_IN_INDEX),
                        MYF(0),field->field_name);
        DBUG_RETURN(ER_NULL_COLUMN_IN_INDEX);
1359 1360 1361 1362 1363
      }
    }
  
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1364

1365
void ha_ndbcluster::release_metadata(THD *thd, Ndb *ndb)
unknown's avatar
unknown committed
1366
{
1367
  uint i;
1368

unknown's avatar
unknown committed
1369 1370 1371
  DBUG_ENTER("release_metadata");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));

1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385
  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
1386
  m_table_info= NULL;
unknown's avatar
unknown committed
1387

1388
  // Release index list 
1389 1390
  for (i= 0; i < MAX_KEY; i++)
  {
1391 1392 1393 1394 1395 1396 1397 1398 1399 1400
    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
1401
    ndb_clear_index(m_index[i]);
1402 1403
  }

1404
  m_table= NULL;
unknown's avatar
unknown committed
1405 1406 1407
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
1408
int ha_ndbcluster::get_ndb_lock_type(enum thr_lock_type type)
1409
{
1410
  if (type >= TL_WRITE_ALLOW_WRITE)
unknown's avatar
unknown committed
1411
    return NdbOperation::LM_Exclusive;
1412
  else if (uses_blob_value())
1413
    return NdbOperation::LM_Read;
unknown's avatar
unknown committed
1414
  else
unknown's avatar
unknown committed
1415
    return NdbOperation::LM_CommittedRead;
1416 1417
}

unknown's avatar
unknown committed
1418 1419 1420 1421 1422 1423
static const ulong index_type_flags[]=
{
  /* UNDEFINED_INDEX */
  0,                         

  /* PRIMARY_KEY_INDEX */
1424
  HA_ONLY_WHOLE_INDEX, 
1425 1426

  /* PRIMARY_KEY_ORDERED_INDEX */
1427
  /* 
unknown's avatar
unknown committed
1428
     Enable HA_KEYREAD_ONLY when "sorted" indexes are supported, 
1429 1430 1431
     thus ORDERD BY clauses can be optimized by reading directly 
     through the index.
  */
unknown's avatar
unknown committed
1432
  // HA_KEYREAD_ONLY | 
unknown's avatar
unknown committed
1433
  HA_READ_NEXT |
1434
  HA_READ_PREV |
unknown's avatar
unknown committed
1435 1436
  HA_READ_RANGE |
  HA_READ_ORDER,
unknown's avatar
unknown committed
1437 1438

  /* UNIQUE_INDEX */
1439
  HA_ONLY_WHOLE_INDEX,
unknown's avatar
unknown committed
1440

1441
  /* UNIQUE_ORDERED_INDEX */
unknown's avatar
unknown committed
1442
  HA_READ_NEXT |
1443
  HA_READ_PREV |
unknown's avatar
unknown committed
1444 1445
  HA_READ_RANGE |
  HA_READ_ORDER,
1446

unknown's avatar
unknown committed
1447
  /* ORDERED_INDEX */
unknown's avatar
unknown committed
1448
  HA_READ_NEXT |
1449
  HA_READ_PREV |
unknown's avatar
unknown committed
1450 1451
  HA_READ_RANGE |
  HA_READ_ORDER
unknown's avatar
unknown committed
1452 1453 1454 1455 1456 1457 1458
};

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);
1459
  return m_index[idx_no].type;
unknown's avatar
unknown committed
1460 1461 1462 1463 1464 1465 1466 1467 1468 1469
}


/*
  Get the flags for an index

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

1470 1471
inline ulong ha_ndbcluster::index_flags(uint idx_no, uint part,
                                        bool all_parts) const 
unknown's avatar
unknown committed
1472
{ 
1473
  DBUG_ENTER("ha_ndbcluster::index_flags");
unknown's avatar
unknown committed
1474
  DBUG_PRINT("enter", ("idx_no: %u", idx_no));
unknown's avatar
unknown committed
1475
  DBUG_ASSERT(get_index_type_from_table(idx_no) < index_flags_size);
1476 1477
  DBUG_RETURN(index_type_flags[get_index_type_from_table(idx_no)] | 
              HA_KEY_SCAN_NOT_ROR);
unknown's avatar
unknown committed
1478 1479
}

unknown's avatar
unknown committed
1480 1481
static void shrink_varchar(Field* field, const byte* & ptr, char* buf)
{
1482
  if (field->type() == MYSQL_TYPE_VARCHAR && ptr != NULL) {
unknown's avatar
unknown committed
1483
    Field_varstring* f= (Field_varstring*)field;
unknown's avatar
unknown committed
1484
    if (f->length_bytes == 1) {
unknown's avatar
unknown committed
1485 1486 1487 1488 1489
      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
1490
        DBUG_ASSERT(FALSE);
unknown's avatar
unknown committed
1491 1492 1493 1494 1495 1496 1497
        buf[0]= 255;
      }
      memmove(buf + 1, ptr + 2, pack_len - 1);
      ptr= buf;
    }
  }
}
unknown's avatar
unknown committed
1498 1499 1500

int ha_ndbcluster::set_primary_key(NdbOperation *op, const byte *key)
{
unknown's avatar
unknown committed
1501
  KEY* key_info= table->key_info + table_share->primary_key;
unknown's avatar
unknown committed
1502 1503 1504 1505 1506 1507 1508
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
  DBUG_ENTER("set_primary_key");

  for (; key_part != end; key_part++) 
  {
    Field* field= key_part->field;
unknown's avatar
unknown committed
1509 1510 1511
    const byte* ptr= key;
    char buf[256];
    shrink_varchar(field, ptr, buf);
unknown's avatar
unknown committed
1512
    if (set_ndb_key(op, field, 
1513
                    key_part->fieldnr-1, ptr))
unknown's avatar
unknown committed
1514
      ERR_RETURN(op->getNdbError());
unknown's avatar
unknown committed
1515
    key += key_part->store_length;
unknown's avatar
unknown committed
1516 1517 1518 1519 1520
  }
  DBUG_RETURN(0);
}


1521
int ha_ndbcluster::set_primary_key_from_record(NdbOperation *op, const byte *record)
1522
{
unknown's avatar
unknown committed
1523
  KEY* key_info= table->key_info + table_share->primary_key;
1524 1525
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
1526
  DBUG_ENTER("set_primary_key_from_record");
1527 1528 1529 1530 1531

  for (; key_part != end; key_part++) 
  {
    Field* field= key_part->field;
    if (set_ndb_key(op, field, 
1532
		    key_part->fieldnr-1, record+key_part->offset))
unknown's avatar
unknown committed
1533 1534 1535 1536 1537
      ERR_RETURN(op->getNdbError());
  }
  DBUG_RETURN(0);
}

1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556
int ha_ndbcluster::set_index_key_from_record(NdbOperation *op, 
                                             const byte *record, uint keyno)
{
  KEY* key_info= table->key_info + keyno;
  KEY_PART_INFO* key_part= key_info->key_part;
  KEY_PART_INFO* end= key_part+key_info->key_parts;
  uint i;
  DBUG_ENTER("set_index_key_from_record");
                                                                                
  for (i= 0; key_part != end; key_part++, i++)
  {
    Field* field= key_part->field;
    if (set_ndb_key(op, field, m_index[keyno].unique_index_attrid_map[i],
                    record+key_part->offset))
      ERR_RETURN(m_active_trans->getNdbError());
  }
  DBUG_RETURN(0);
}

1557 1558
int 
ha_ndbcluster::set_index_key(NdbOperation *op, 
1559 1560
                             const KEY *key_info, 
                             const byte * key_ptr)
1561
{
1562
  DBUG_ENTER("ha_ndbcluster::set_index_key");
1563 1564 1565 1566 1567 1568
  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
1569 1570 1571 1572
    Field* field= key_part->field;
    const byte* ptr= key_part->null_bit ? key_ptr + 1 : key_ptr;
    char buf[256];
    shrink_varchar(field, ptr, buf);
unknown's avatar
Merge  
unknown committed
1573
    if (set_ndb_key(op, field, m_index[active_index].unique_index_attrid_map[i], ptr))
1574 1575 1576 1577 1578
      ERR_RETURN(m_active_trans->getNdbError());
    key_ptr+= key_part->store_length;
  }
  DBUG_RETURN(0);
}
unknown's avatar
unknown committed
1579

unknown's avatar
unknown committed
1580 1581 1582 1583 1584 1585 1586
inline 
int ha_ndbcluster::define_read_attrs(byte* buf, NdbOperation* op)
{
  uint i;
  DBUG_ENTER("define_read_attrs");  

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

unknown's avatar
unknown committed
1619 1620 1621 1622
/*
  Read one record from NDB using primary key
*/

1623 1624
int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf,
                           uint32 part_id)
unknown's avatar
unknown committed
1625
{
unknown's avatar
unknown committed
1626
  uint no_fields= table_share->fields;
unknown's avatar
unknown committed
1627 1628
  NdbConnection *trans= m_active_trans;
  NdbOperation *op;
1629

1630 1631 1632 1633
  int res;
  DBUG_ENTER("pk_read");
  DBUG_PRINT("enter", ("key_len: %u", key_len));
  DBUG_DUMP("key", (char*)key, key_len);
1634
  m_write_op= FALSE;
unknown's avatar
unknown committed
1635

1636 1637
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
unknown's avatar
unknown committed
1638
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || 
1639
      op->readTuple(lm) != 0)
1640
    ERR_RETURN(trans->getNdbError());
1641
  
unknown's avatar
unknown committed
1642
  if (table_share->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
1643 1644 1645 1646 1647
  {
    // This table has no primary key, use "hidden" primary key
    DBUG_PRINT("info", ("Using hidden key"));
    DBUG_DUMP("key", (char*)key, 8);    
    if (set_hidden_key(op, no_fields, key))
1648
      ERR_RETURN(trans->getNdbError());
1649
    
unknown's avatar
unknown committed
1650
    // Read key at the same time, for future reference
unknown's avatar
unknown committed
1651
    if (get_ndb_value(op, NULL, no_fields, NULL))
1652
      ERR_RETURN(trans->getNdbError());
unknown's avatar
unknown committed
1653 1654 1655 1656 1657 1658 1659
  } 
  else 
  {
    if ((res= set_primary_key(op, key)))
      return res;
  }
  
unknown's avatar
unknown committed
1660
  if ((res= define_read_attrs(buf, op)))
1661
    DBUG_RETURN(res);
1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673

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

unknown's avatar
unknown committed
1674
  if (execute_no_commit_ie(this,trans) != 0) 
unknown's avatar
unknown committed
1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685
  {
    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);
}

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

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

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

1706 1707
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
unknown's avatar
unknown committed
1708
  if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || 
1709
      op->readTuple(lm) != 0)
1710
    ERR_RETURN(trans->getNdbError());
1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721
  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());
  }
1722 1723 1724 1725

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

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

1764
/*
1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824
 * Check that all operations between first and last all
 * have gotten the errcode
 * If checking for HA_ERR_KEY_NOT_FOUND then update m_dupkey
 * for all succeeding operations
 */
bool ha_ndbcluster::check_all_operations_for_error(NdbTransaction *trans,
                                                   const NdbOperation *first,
                                                   const NdbOperation *last,
                                                   uint errcode)
{
  const NdbOperation *op= first;
  DBUG_ENTER("ha_ndbcluster::check_all_operations_for_error");

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


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

1827
int ha_ndbcluster::peek_indexed_rows(const byte *record)
1828
{
1829
  NdbTransaction *trans= m_active_trans;
1830
  NdbOperation *op;
1831 1832
  const NdbOperation *first, *last;
  uint i;
1833
  int res;
1834
  DBUG_ENTER("peek_indexed_rows");
unknown's avatar
unknown committed
1835

1836 1837 1838
  NdbOperation::LockMode lm= NdbOperation::LM_Read;
  first= NULL;
  if (table->s->primary_key != MAX_KEY)
1839
  {
1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851
    /*
     * 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)
1852
    {
1853 1854 1855 1856 1857 1858 1859 1860 1861
      uint32 part_id;
      int error;
      longlong func_value;
      if ((error= m_part_info->get_partition_id(m_part_info, &part_id,
                                                &func_value)))
      {
        DBUG_RETURN(error);
      }
      op->setPartitionId(part_id);
1862 1863
    }
  }
1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875
  /*
   * Fetch any rows with colliding unique indexes
   */
  KEY* key_info;
  KEY_PART_INFO *key_part, *end;
  for (i= 0, key_info= table->key_info; i < table->s->keys; i++, key_info++)
  {
    if (i != table->s->primary_key &&
        key_info->flags & HA_NOSAME)
    {
      // A unique index is defined on table
      NdbIndexOperation *iop;
1876
      const NDBINDEX *unique_index = m_index[i].unique_index;
1877 1878
      key_part= key_info->key_part;
      end= key_part + key_info->key_parts;
1879
      if (!(iop= trans->getNdbIndexOperation(unique_index, m_table)) ||
1880 1881
          iop->readTuple(lm) != 0)
        ERR_RETURN(trans->getNdbError());
1882

1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899
      if (!first)
        first= iop;
      if ((res= set_index_key_from_record(iop, record, i)))
        ERR_RETURN(trans->getNdbError());
    }
  }
  last= trans->getLastDefinedOperation();
  if (first)
    res= execute_no_commit_ie(this,trans);
  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
1900 1901 1902 1903
  {
    table->status= STATUS_NOT_FOUND;
    DBUG_RETURN(ndb_err(trans));
  } 
1904 1905 1906 1907
  else
  {
    DBUG_PRINT("info", ("m_dupkey %d", m_dupkey));
  }
1908 1909
  DBUG_RETURN(0);
}
1910

1911

unknown's avatar
unknown committed
1912 1913 1914 1915 1916
/*
  Read one record from NDB using unique secondary index
*/

int ha_ndbcluster::unique_index_read(const byte *key,
1917
                                     uint key_len, byte *buf)
unknown's avatar
unknown committed
1918
{
1919
  int res;
1920
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
1921
  NdbIndexOperation *op;
1922
  DBUG_ENTER("ha_ndbcluster::unique_index_read");
unknown's avatar
unknown committed
1923 1924 1925
  DBUG_PRINT("enter", ("key_len: %u, index: %u", key_len, active_index));
  DBUG_DUMP("key", (char*)key, key_len);
  
1926 1927
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
1928 1929
  if (!(op= trans->getNdbIndexOperation(m_index[active_index].unique_index, 
                                        m_table)) ||
1930
      op->readTuple(lm) != 0)
unknown's avatar
unknown committed
1931 1932 1933
    ERR_RETURN(trans->getNdbError());
  
  // Set secondary index key(s)
unknown's avatar
unknown committed
1934
  if ((res= set_index_key(op, table->key_info + active_index, key)))
1935 1936
    DBUG_RETURN(res);
  
unknown's avatar
unknown committed
1937
  if ((res= define_read_attrs(buf, op)))
1938
    DBUG_RETURN(res);
unknown's avatar
unknown committed
1939

unknown's avatar
unknown committed
1940
  if (execute_no_commit_ie(this,trans) != 0) 
unknown's avatar
unknown committed
1941 1942 1943 1944 1945 1946 1947 1948 1949 1950
  {
    table->status= STATUS_NOT_FOUND;
    DBUG_RETURN(ndb_err(trans));
  }
  // The value have now been fetched from NDB
  unpack_record(buf);
  table->status= 0;
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
1951
inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor)
1952 1953
{
  DBUG_ENTER("fetch_next");
1954
  int check;
1955
  NdbTransaction *trans= m_active_trans;
1956
  
1957
  bool contact_ndb= m_lock.type < TL_WRITE_ALLOW_WRITE;
1958 1959
  do {
    DBUG_PRINT("info", ("Call nextResult, contact_ndb: %d", contact_ndb));
unknown's avatar
unknown committed
1960 1961 1962
    /*
      We can only handle one tuple with blobs at a time.
    */
1963
    if (m_ops_pending && m_blobs_pending)
unknown's avatar
unknown committed
1964
    {
unknown's avatar
unknown committed
1965
      if (execute_no_commit(this,trans) != 0)
1966
        DBUG_RETURN(ndb_err(trans));
1967 1968
      m_ops_pending= 0;
      m_blobs_pending= FALSE;
unknown's avatar
unknown committed
1969
    }
1970 1971
    
    if ((check= cursor->nextResult(contact_ndb, m_force_send)) == 0)
1972 1973 1974 1975 1976 1977 1978
    {
      DBUG_RETURN(0);
    } 
    else if (check == 1 || check == 2)
    {
      // 1: No more records
      // 2: No more cached records
1979
      
1980
      /*
1981 1982 1983
        Before fetching more rows and releasing lock(s),
        all pending update or delete operations should 
        be sent to NDB
1984
      */
1985 1986
      DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending));    
      if (m_ops_pending)
1987
      {
1988 1989 1990 1991 1992 1993 1994 1995 1996
        if (m_transaction_on)
        {
          if (execute_no_commit(this,trans) != 0)
            DBUG_RETURN(-1);
        }
        else
        {
          if  (execute_commit(this,trans) != 0)
            DBUG_RETURN(-1);
unknown's avatar
unknown committed
1997
          if (trans->restart() != 0)
1998 1999 2000 2001 2002 2003
          {
            DBUG_ASSERT(0);
            DBUG_RETURN(-1);
          }
        }
        m_ops_pending= 0;
2004
      }
2005 2006
      contact_ndb= (check == 2);
    }
unknown's avatar
unknown committed
2007 2008 2009 2010
    else
    {
      DBUG_RETURN(-1);
    }
2011
  } while (check == 2);
unknown's avatar
unknown committed
2012

2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023
  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
2024

2025 2026 2027 2028 2029 2030 2031
*/

inline int ha_ndbcluster::next_result(byte *buf)
{  
  int res;
  DBUG_ENTER("next_result");
    
2032 2033 2034
  if (!m_active_cursor)
    DBUG_RETURN(HA_ERR_END_OF_FILE);
  
unknown's avatar
unknown committed
2035
  if ((res= fetch_next(m_active_cursor)) == 0)
2036 2037 2038 2039 2040 2041 2042
  {
    DBUG_PRINT("info", ("One more record found"));    
    
    unpack_record(buf);
    table->status= 0;
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
2043
  else if (res == 1)
2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054
  {
    // 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
2055 2056
}

2057
/*
2058
  Set bounds for ordered index scan.
2059 2060
*/

unknown's avatar
unknown committed
2061
int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
2062 2063
                              uint inx,
                              bool rir,
2064 2065
                              const key_range *keys[2],
                              uint range_no)
2066
{
2067
  const KEY *const key_info= table->key_info + inx;
2068 2069 2070
  const uint key_parts= key_info->key_parts;
  uint key_tot_len[2];
  uint tot_len;
2071
  uint i, j;
2072 2073

  DBUG_ENTER("set_bounds");
2074
  DBUG_PRINT("info", ("key_parts=%d", key_parts));
2075

2076
  for (j= 0; j <= 1; j++)
2077
  {
2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090
    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;
    }
2091 2092
  }
  tot_len= 0;
unknown's avatar
unknown committed
2093

2094 2095 2096 2097
  for (i= 0; i < key_parts; i++)
  {
    KEY_PART_INFO *key_part= &key_info->key_part[i];
    Field *field= key_part->field;
2098
#ifndef DBUG_OFF
2099
    uint part_len= key_part->length;
2100
#endif
2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114
    uint part_store_len= key_part->store_length;
    // Info about each key part
    struct part_st {
      bool part_last;
      const key_range *key;
      const byte *part_ptr;
      bool part_null;
      int bound_type;
      const char* bound_ptr;
    };
    struct part_st part[2];

    for (j= 0; j <= 1; j++)
    {
2115
      struct part_st &p= part[j];
2116 2117 2118 2119 2120 2121 2122
      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
2123
        p.part_null= key_part->null_bit && *p.part_ptr;
2124
        p.bound_ptr= (const char *)
unknown's avatar
unknown committed
2125
          p.part_null ? 0 : key_part->null_bit ? p.part_ptr + 1 : p.part_ptr;
2126 2127 2128 2129 2130 2131

        if (j == 0)
        {
          switch (p.key->flag)
          {
            case HA_READ_KEY_EXACT:
2132 2133 2134 2135
              if (! rir)
                p.bound_type= NdbIndexScanOperation::BoundEQ;
              else // differs for records_in_range
                p.bound_type= NdbIndexScanOperation::BoundLE;
2136
              break;
2137
            // ascending
2138 2139 2140 2141 2142 2143 2144 2145 2146
            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;
2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159
            // 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;
2160 2161 2162 2163 2164 2165 2166
            default:
              break;
          }
        }
        if (j == 1) {
          switch (p.key->flag)
          {
2167
            // ascending
2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178
            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;
2179
            // descending strangely sets no end key
2180 2181
          }
        }
2182

2183 2184 2185
        if (p.bound_type == -1)
        {
          DBUG_PRINT("error", ("key %d unknown flag %d", j, p.key->flag));
unknown's avatar
unknown committed
2186
          DBUG_ASSERT(FALSE);
2187
          // Stop setting bounds but continue with what we have
2188
          op->end_of_bound(range_no);
2189 2190 2191 2192
          DBUG_RETURN(0);
        }
      }
    }
2193

2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210
    // 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;
    }
2211

2212 2213
    for (j= 0; j <= 1; j++)
    {
2214
      struct part_st &p= part[j];
2215 2216 2217 2218 2219 2220 2221 2222 2223
      // Set bound if not done with this key
      if (p.key != NULL)
      {
        DBUG_PRINT("info", ("key %d:%d offset=%d length=%d last=%d bound=%d",
                            j, i, tot_len, part_len, p.part_last, p.bound_type));
        DBUG_DUMP("info", (const char*)p.part_ptr, part_store_len);

        // Set bound if not cancelled via type -1
        if (p.bound_type != -1)
2224
        {
unknown's avatar
unknown committed
2225 2226 2227
          const char* ptr= p.bound_ptr;
          char buf[256];
          shrink_varchar(field, ptr, buf);
unknown's avatar
Merge  
unknown committed
2228
          if (op->setBound(i, p.bound_type, ptr))
2229
            ERR_RETURN(op->getNdbError());
2230
        }
2231 2232 2233 2234
      }
    }

    tot_len+= part_store_len;
2235
  }
2236
  op->end_of_bound(range_no);
2237 2238 2239
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
2240
/*
2241
  Start ordered index scan in NDB
unknown's avatar
unknown committed
2242 2243
*/

2244
int ha_ndbcluster::ordered_index_scan(const key_range *start_key,
2245
                                      const key_range *end_key,
2246 2247
                                      bool sorted, bool descending,
                                      byte* buf, part_id_range *part_spec)
unknown's avatar
unknown committed
2248
{  
2249
  int res;
unknown's avatar
unknown committed
2250
  bool restart;
2251
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
2252
  NdbIndexScanOperation *op;
2253

2254 2255 2256
  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
2257
  DBUG_PRINT("enter", ("Starting new ordered scan on %s", m_tabname));
2258
  m_write_op= FALSE;
unknown's avatar
unknown committed
2259

2260 2261
  // Check that sorted seems to be initialised
  DBUG_ASSERT(sorted == 0 || sorted == 1);
unknown's avatar
unknown committed
2262
  
2263
  if (m_active_cursor == 0)
unknown's avatar
unknown committed
2264
  {
unknown's avatar
unknown committed
2265
    restart= FALSE;
unknown's avatar
unknown committed
2266 2267
    NdbOperation::LockMode lm=
      (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
2268 2269
    if (!(op= trans->getNdbIndexScanOperation(m_index[active_index].index, 
                                              m_table)) ||
2270
        op->readTuples(lm, 0, parallelism, sorted, descending))
unknown's avatar
unknown committed
2271
      ERR_RETURN(trans->getNdbError());
2272 2273 2274
    if (m_use_partition_function && part_spec != NULL &&
        part_spec->start_part == part_spec->end_part)
      op->setPartitionId(part_spec->start_part);
2275
    m_active_cursor= op;
unknown's avatar
unknown committed
2276
  } else {
unknown's avatar
unknown committed
2277
    restart= TRUE;
2278
    op= (NdbIndexScanOperation*)m_active_cursor;
unknown's avatar
unknown committed
2279
    
2280 2281 2282
    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
2283 2284
    DBUG_ASSERT(op->getSorted() == sorted);
    DBUG_ASSERT(op->getLockMode() == 
2285
                (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type));
unknown's avatar
unknown committed
2286
    if (op->reset_bounds(m_force_send))
unknown's avatar
unknown committed
2287 2288
      DBUG_RETURN(ndb_err(m_active_trans));
  }
2289
  
2290
  {
2291
    const key_range *keys[2]= { start_key, end_key };
2292
    res= set_bounds(op, active_index, false, keys);
2293 2294
    if (res)
      DBUG_RETURN(res);
2295
  }
2296

2297
  if (!restart)
2298
  {
2299 2300 2301
    if (generate_scan_filter(m_cond_stack, op))
      DBUG_RETURN(ndb_err(trans));

2302
    if ((res= define_read_attrs(buf, op)))
2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313
    {
      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
2314
  }
2315 2316 2317 2318 2319 2320

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

/*
2323
  Start full table scan in NDB
unknown's avatar
unknown committed
2324 2325 2326 2327
 */

int ha_ndbcluster::full_table_scan(byte *buf)
{
2328
  int res;
unknown's avatar
unknown committed
2329
  NdbScanOperation *op;
2330
  NdbTransaction *trans= m_active_trans;
2331
  part_id_range part_spec;
unknown's avatar
unknown committed
2332 2333 2334

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

2337 2338
  NdbOperation::LockMode lm=
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
2339
  if (!(op=trans->getNdbScanOperation(m_table)) ||
2340
      op->readTuples(lm, 0, parallelism))
unknown's avatar
unknown committed
2341
    ERR_RETURN(trans->getNdbError());
2342
  m_active_cursor= op;
2343 2344 2345 2346

  if (m_use_partition_function)
  {
    part_spec.start_part= 0;
2347
    part_spec.end_part= m_part_info->get_tot_partitions() - 1;
2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369
    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);
    }
2370 2371 2372 2373 2374 2375
    // 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());
2376 2377
  }

2378 2379
  if (generate_scan_filter(m_cond_stack, op))
    DBUG_RETURN(ndb_err(trans));
unknown's avatar
unknown committed
2380
  if ((res= define_read_attrs(buf, op)))
2381 2382 2383 2384 2385 2386
    DBUG_RETURN(res);

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

unknown's avatar
unknown committed
2389 2390 2391 2392 2393
/*
  Insert one record into NDB
*/
int ha_ndbcluster::write_row(byte *record)
{
unknown's avatar
unknown committed
2394
  bool has_auto_increment;
unknown's avatar
unknown committed
2395
  uint i;
2396
  NdbTransaction *trans= m_active_trans;
unknown's avatar
unknown committed
2397 2398
  NdbOperation *op;
  int res;
unknown's avatar
unknown committed
2399
  THD *thd= current_thd;
2400 2401
  longlong func_value= 0;
  DBUG_ENTER("ha_ndbcluster::write_row");
2402

2403
  m_write_op= TRUE;
2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425
  has_auto_increment= (table->next_number_field && record == table->record[0]);
  if (table_share->primary_key != MAX_KEY)
  {
    /*
     * Increase any auto_incremented primary key
     */
    if (has_auto_increment) 
    {
      THD *thd= table->in_use;

      m_skip_auto_increment= FALSE;
      update_auto_increment();
      /* Ensure that handler is always called for auto_increment values */
      thd->next_insert_id= 0;
      m_skip_auto_increment= !auto_increment_column_changed;
    }
  }

  /*
   * If IGNORE the ignore constraint violations on primary and unique keys
   */
  if (!m_use_write && m_ignore_dup_key)
2426
  {
2427
    int peek_res= peek_indexed_rows(record);
2428 2429 2430 2431 2432 2433 2434
    
    if (!peek_res) 
    {
      DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
    }
    if (peek_res != HA_ERR_KEY_NOT_FOUND)
      DBUG_RETURN(peek_res);
2435
  }
2436

unknown's avatar
unknown committed
2437
  statistic_increment(thd->status_var.ha_write_count, &LOCK_status);
unknown's avatar
unknown committed
2438 2439
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
    table->timestamp_field->set_time();
unknown's avatar
unknown committed
2440

2441
  if (!(op= trans->getNdbOperation(m_table)))
unknown's avatar
unknown committed
2442 2443 2444 2445 2446 2447
    ERR_RETURN(trans->getNdbError());

  res= (m_use_write) ? op->writeTuple() :op->insertTuple(); 
  if (res != 0)
    ERR_RETURN(trans->getNdbError());  
 
2448 2449 2450 2451
  if (m_use_partition_function)
  {
    uint32 part_id;
    int error;
2452 2453
    if ((error= m_part_info->get_partition_id(m_part_info, &part_id,
                                              &func_value)))
2454 2455 2456 2457 2458 2459
    {
      DBUG_RETURN(error);
    }
    op->setPartitionId(part_id);
  }

unknown's avatar
unknown committed
2460
  if (table_share->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
2461 2462
  {
    // Table has hidden primary key
2463
    Ndb *ndb= get_ndb();
2464 2465 2466
    Uint64 auto_value= NDB_FAILED_AUTO_INCREMENT;
    uint retries= NDB_AUTO_INCREMENT_RETRIES;
    do {
2467
      auto_value= ndb->getAutoIncrementValue(m_table);
2468 2469 2470
    } while (auto_value == NDB_FAILED_AUTO_INCREMENT && 
             --retries &&
             ndb->getNdbError().status == NdbError::TemporaryError);
2471 2472
    if (auto_value == NDB_FAILED_AUTO_INCREMENT)
      ERR_RETURN(ndb->getNdbError());
unknown's avatar
unknown committed
2473
    if (set_hidden_key(op, table_share->fields, (const byte*)&auto_value))
unknown's avatar
unknown committed
2474 2475 2476 2477 2478
      ERR_RETURN(op->getNdbError());
  } 
  else 
  {
    int res;
2479

2480
    if ((res= set_primary_key_from_record(op, record)))
2481
      return res;  
unknown's avatar
unknown committed
2482 2483 2484
  }

  // Set non-key attribute(s)
unknown's avatar
unknown committed
2485
  bool set_blob_value= FALSE;
unknown's avatar
unknown committed
2486
  for (i= 0; i < table_share->fields; i++) 
unknown's avatar
unknown committed
2487 2488 2489
  {
    Field *field= table->field[i];
    if (!(field->flags & PRI_KEY_FLAG) &&
unknown's avatar
unknown committed
2490 2491
	(ha_get_bit_in_write_set(i + 1) || !m_use_write) &&
        set_ndb_value(op, field, i, record-table->record[0], &set_blob_value))
2492
    {
2493
      m_skip_auto_increment= TRUE;
unknown's avatar
unknown committed
2494
      ERR_RETURN(op->getNdbError());
2495
    }
unknown's avatar
unknown committed
2496 2497
  }

2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513
  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);
  }

2514 2515
  m_rows_changed++;

unknown's avatar
unknown committed
2516 2517 2518 2519 2520 2521 2522
  /*
    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!
  */
2523
  m_rows_inserted++;
2524
  no_uncommitted_rows_update(1);
2525
  m_bulk_insert_not_flushed= TRUE;
2526
  if ((m_rows_to_insert == (ha_rows) 1) || 
2527
      ((m_rows_inserted % m_bulk_insert_rows) == 0) ||
2528
      m_primary_key_update ||
2529
      set_blob_value)
2530 2531 2532
  {
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
2533 2534
                        "rows_inserted:%d, bulk_insert_rows: %d", 
                        (int)m_rows_inserted, (int)m_bulk_insert_rows));
2535

2536
    m_bulk_insert_not_flushed= FALSE;
2537
    if (m_transaction_on)
2538
    {
unknown's avatar
unknown committed
2539
      if (execute_no_commit(this,trans) != 0)
2540
      {
2541 2542 2543
        m_skip_auto_increment= TRUE;
        no_uncommitted_rows_execute_failure();
        DBUG_RETURN(ndb_err(trans));
2544
      }
2545 2546
    }
    else
2547
    {
unknown's avatar
unknown committed
2548
      if (execute_commit(this,trans) != 0)
2549
      {
2550 2551 2552
        m_skip_auto_increment= TRUE;
        no_uncommitted_rows_execute_failure();
        DBUG_RETURN(ndb_err(trans));
2553
      }
unknown's avatar
unknown committed
2554
      if (trans->restart() != 0)
2555
      {
2556 2557
        DBUG_ASSERT(0);
        DBUG_RETURN(-1);
2558
      }
2559
    }
2560
  }
2561
  if ((has_auto_increment) && (m_skip_auto_increment))
unknown's avatar
unknown committed
2562
  {
2563
    Ndb *ndb= get_ndb();
2564
    Uint64 next_val= (Uint64) table->next_number_field->val_int() + 1;
unknown's avatar
unknown committed
2565
    DBUG_PRINT("info", 
2566
               ("Trying to set next auto increment value to %lu",
2567
                (ulong) next_val));
2568
    if (ndb->setAutoIncrementValue(m_table, next_val, TRUE))
unknown's avatar
unknown committed
2569
      DBUG_PRINT("info", 
2570
                 ("Setting next auto increment value to %u", next_val));  
2571
  }
2572
  m_skip_auto_increment= TRUE;
2573

unknown's avatar
unknown committed
2574 2575 2576 2577 2578 2579 2580
  DBUG_RETURN(0);
}


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

int ha_ndbcluster::key_cmp(uint keynr, const byte * old_row,
2581
                           const byte * new_row)
unknown's avatar
unknown committed
2582 2583 2584 2585 2586 2587 2588 2589 2590
{
  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) !=
2591 2592
          (new_row[key_part->null_offset] & key_part->null_bit))
        return 1;
unknown's avatar
unknown committed
2593
    }
2594
    if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
unknown's avatar
unknown committed
2595 2596 2597
    {

      if (key_part->field->cmp_binary((char*) (old_row + key_part->offset),
2598 2599 2600
                                      (char*) (new_row + key_part->offset),
                                      (ulong) key_part->length))
        return 1;
unknown's avatar
unknown committed
2601 2602 2603 2604
    }
    else
    {
      if (memcmp(old_row+key_part->offset, new_row+key_part->offset,
2605 2606
                 key_part->length))
        return 1;
unknown's avatar
unknown committed
2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618
    }
  }
  return 0;
}

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

int ha_ndbcluster::update_row(const byte *old_data, byte *new_data)
{
  THD *thd= current_thd;
2619
  NdbTransaction *trans= m_active_trans;
2620
  NdbScanOperation* cursor= m_active_cursor;
unknown's avatar
unknown committed
2621 2622
  NdbOperation *op;
  uint i;
2623 2624
  uint32 old_part_id= 0, new_part_id= 0;
  int error;
2625
  longlong func_value;
unknown's avatar
unknown committed
2626
  DBUG_ENTER("update_row");
2627
  m_write_op= TRUE;
unknown's avatar
unknown committed
2628
  
unknown's avatar
unknown committed
2629
  statistic_increment(thd->status_var.ha_update_count, &LOCK_status);
unknown's avatar
unknown committed
2630
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
2631
  {
unknown's avatar
unknown committed
2632
    table->timestamp_field->set_time();
2633
    ha_set_bit_in_write_set(table->timestamp_field->fieldnr);
2634
  }
unknown's avatar
unknown committed
2635

2636 2637
  if (m_use_partition_function &&
      (error= get_parts_for_update(old_data, new_data, table->record[0],
2638 2639
                                   m_part_info, &old_part_id, &new_part_id,
                                   &func_value)))
2640 2641 2642 2643
  {
    DBUG_RETURN(error);
  }

2644 2645 2646 2647 2648 2649
  /*
   * Check for update of primary key or partition change
   * for special handling
   */  
  if (((table_share->primary_key != MAX_KEY) &&
       key_cmp(table_share->primary_key, old_data, new_data)) ||
2650
      (old_part_id != new_part_id))
2651
  {
2652
    int read_res, insert_res, delete_res, undo_res;
2653

2654 2655
    DBUG_PRINT("info", ("primary key update or partition change, "
                        "doing read+delete+insert"));
2656
    // Get all old fields, since we optimize away fields not in query
2657
    read_res= complemented_read(old_data, new_data, old_part_id);
2658 2659
    if (read_res)
    {
2660
      DBUG_PRINT("info", ("read failed"));
2661 2662
      DBUG_RETURN(read_res);
    }
2663
    // Delete old row
2664
    m_primary_key_update= TRUE;
2665
    delete_res= delete_row(old_data);
2666
    m_primary_key_update= FALSE;
2667 2668 2669
    if (delete_res)
    {
      DBUG_PRINT("info", ("delete failed"));
2670
      DBUG_RETURN(delete_res);
2671
    }     
2672 2673
    // Insert new row
    DBUG_PRINT("info", ("delete succeded"));
2674
    m_primary_key_update= TRUE;
2675
    insert_res= write_row(new_data);
2676
    m_primary_key_update= FALSE;
2677 2678 2679 2680 2681
    if (insert_res)
    {
      DBUG_PRINT("info", ("insert failed"));
      if (trans->commitStatus() == NdbConnection::Started)
      {
2682
        // Undo delete_row(old_data)
2683
        m_primary_key_update= TRUE;
2684 2685 2686 2687 2688 2689
        undo_res= write_row((byte *)old_data);
        if (undo_res)
          push_warning(current_thd, 
                       MYSQL_ERROR::WARN_LEVEL_WARN, 
                       undo_res, 
                       "NDB failed undoing delete at primary key update");
2690 2691 2692 2693 2694
        m_primary_key_update= FALSE;
      }
      DBUG_RETURN(insert_res);
    }
    DBUG_PRINT("info", ("delete+insert succeeded"));
2695
    DBUG_RETURN(0);
2696
  }
2697

2698
  if (cursor)
unknown's avatar
unknown committed
2699
  {
2700 2701 2702 2703 2704 2705 2706 2707
    /*
      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"));
2708
    if (!(op= cursor->updateCurrentTuple()))
2709
      ERR_RETURN(trans->getNdbError());
2710
    m_ops_pending++;
2711
    if (uses_blob_value())
2712
      m_blobs_pending= TRUE;
2713 2714
    if (m_use_partition_function)
      cursor->setPartitionId(new_part_id);
2715 2716 2717
  }
  else
  {  
2718
    if (!(op= trans->getNdbOperation(m_table)) ||
2719
        op->updateTuple() != 0)
2720 2721
      ERR_RETURN(trans->getNdbError());  
    
2722 2723
    if (m_use_partition_function)
      op->setPartitionId(new_part_id);
unknown's avatar
unknown committed
2724
    if (table_share->primary_key == MAX_KEY) 
2725 2726 2727 2728 2729
    {
      // 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 
2730 2731
      // read into m_ref
      DBUG_DUMP("key", m_ref, NDB_HIDDEN_PRIMARY_KEY_LENGTH);
2732
      
unknown's avatar
unknown committed
2733
      if (set_hidden_key(op, table->s->fields, m_ref))
2734
        ERR_RETURN(op->getNdbError());
2735 2736 2737 2738
    } 
    else 
    {
      int res;
2739
      if ((res= set_primary_key_from_record(op, old_data)))
2740
        DBUG_RETURN(res);
2741
    }
unknown's avatar
unknown committed
2742 2743
  }

2744 2745
  m_rows_changed++;

unknown's avatar
unknown committed
2746
  // Set non-key attribute(s)
unknown's avatar
unknown committed
2747
  for (i= 0; i < table_share->fields; i++) 
unknown's avatar
unknown committed
2748 2749
  {
    Field *field= table->field[i];
2750
    if (ha_get_bit_in_write_set(i+1) &&
unknown's avatar
unknown committed
2751
        (!(field->flags & PRI_KEY_FLAG)) &&
unknown's avatar
unknown committed
2752
        set_ndb_value(op, field, i, new_data - table->record[0]))
unknown's avatar
unknown committed
2753 2754
      ERR_RETURN(op->getNdbError());
  }
2755

2756 2757 2758 2759 2760 2761 2762 2763 2764 2765
  if (m_use_partition_function)
  {
    if (func_value >= INT_MAX32)
      func_value= INT_MAX32;
    uint32 part_func_value= (uint32)func_value;
    uint no_fields= table_share->fields;
    if (table_share->primary_key == MAX_KEY)
      no_fields++;
    op->setValue(no_fields, part_func_value);
  }
unknown's avatar
unknown committed
2766
  // Execute update operation
unknown's avatar
unknown committed
2767
  if (!cursor && execute_no_commit(this,trans) != 0) {
2768
    no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
2769
    DBUG_RETURN(ndb_err(trans));
2770
  }
unknown's avatar
unknown committed
2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781
  
  DBUG_RETURN(0);
}


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

int ha_ndbcluster::delete_row(const byte *record)
{
unknown's avatar
unknown committed
2782
  THD *thd= current_thd;
2783
  NdbTransaction *trans= m_active_trans;
2784
  NdbScanOperation* cursor= m_active_cursor;
unknown's avatar
unknown committed
2785
  NdbOperation *op;
2786 2787
  uint32 part_id;
  int error;
unknown's avatar
unknown committed
2788
  DBUG_ENTER("delete_row");
2789
  m_write_op= TRUE;
unknown's avatar
unknown committed
2790

unknown's avatar
unknown committed
2791
  statistic_increment(thd->status_var.ha_delete_count,&LOCK_status);
unknown's avatar
unknown committed
2792
  m_rows_changed++;
unknown's avatar
unknown committed
2793

2794 2795 2796 2797 2798 2799 2800
  if (m_use_partition_function &&
      (error= get_part_for_delete(record, table->record[0], m_part_info,
                                  &part_id)))
  {
    DBUG_RETURN(error);
  }

2801
  if (cursor)
unknown's avatar
unknown committed
2802
  {
2803
    /*
2804
      We are scanning records and want to delete the record
2805
      that was just found, call deleteTuple on the cursor 
2806
      to take over the lock to a new delete operation
2807 2808 2809 2810
      And thus setting the primary key of the record from 
      the active record in cursor
    */
    DBUG_PRINT("info", ("Calling deleteTuple on cursor"));
2811
    if (cursor->deleteCurrentTuple() != 0)
2812
      ERR_RETURN(trans->getNdbError());     
2813
    m_ops_pending++;
unknown's avatar
unknown committed
2814

2815 2816 2817
    if (m_use_partition_function)
      cursor->setPartitionId(part_id);

2818 2819
    no_uncommitted_rows_update(-1);

2820 2821 2822
    if (!m_primary_key_update)
      // If deleting from cursor, NoCommit will be handled in next_result
      DBUG_RETURN(0);
2823 2824
  }
  else
unknown's avatar
unknown committed
2825
  {
2826
    
2827
    if (!(op=trans->getNdbOperation(m_table)) || 
2828
        op->deleteTuple() != 0)
2829 2830
      ERR_RETURN(trans->getNdbError());
    
2831 2832 2833
    if (m_use_partition_function)
      op->setPartitionId(part_id);

2834 2835
    no_uncommitted_rows_update(-1);
    
unknown's avatar
unknown committed
2836
    if (table_share->primary_key == MAX_KEY) 
2837 2838 2839 2840
    {
      // This table has no primary key, use "hidden" primary key
      DBUG_PRINT("info", ("Using hidden key"));
      
unknown's avatar
unknown committed
2841
      if (set_hidden_key(op, table->s->fields, m_ref))
2842
        ERR_RETURN(op->getNdbError());
2843 2844 2845 2846
    } 
    else 
    {
      int res;
2847 2848
      if ((res= set_primary_key_from_record(op, record)))
        return res;  
2849
    }
unknown's avatar
unknown committed
2850
  }
2851

unknown's avatar
unknown committed
2852
  // Execute delete operation
unknown's avatar
unknown committed
2853
  if (execute_no_commit(this,trans) != 0) {
2854
    no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
2855
    DBUG_RETURN(ndb_err(trans));
2856
  }
unknown's avatar
unknown committed
2857 2858
  DBUG_RETURN(0);
}
2859
  
unknown's avatar
unknown committed
2860 2861 2862 2863 2864
/*
  Unpack a record read from NDB 

  SYNOPSIS
    unpack_record()
2865
    buf                 Buffer to store read row
unknown's avatar
unknown committed
2866 2867 2868 2869 2870 2871 2872 2873

  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
2874 2875
void ndb_unpack_record(TABLE *table, NdbValue *value,
                       MY_BITMAP *defined, byte *buf)
unknown's avatar
unknown committed
2876
{
unknown's avatar
unknown committed
2877
  Field **p_field= table->field, *field= *p_field;
2878
  my_ptrdiff_t row_offset= buf - table->record[0];
unknown's avatar
unknown committed
2879
  DBUG_ENTER("ndb_unpack_record");
2880

unknown's avatar
unknown committed
2881
  // Set null flag(s)
2882
  bzero(buf, table->s->null_bytes);
unknown's avatar
unknown committed
2883 2884
  for ( ; field;
       p_field++, value++, field= *p_field)
unknown's avatar
unknown committed
2885
  {
unknown's avatar
unknown committed
2886 2887
    if ((*value).ptr)
    {
unknown's avatar
unknown committed
2888
      if (!(field->flags & BLOB_FLAG))
unknown's avatar
unknown committed
2889
      {
unknown's avatar
unknown committed
2890 2891
        int is_null= (*value).rec->isNULL();
        if (is_null)
2892
        {
unknown's avatar
unknown committed
2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908
          if (is_null > 0)
          {
            DBUG_PRINT("info",("[%u] NULL",
                               (*value).rec->getColumn()->getColumnNo()));
            field->set_null(row_offset);
          }
          else
          {
            DBUG_PRINT("info",("[%u] UNDEFINED",
                               (*value).rec->getColumn()->getColumnNo()));
            bitmap_clear_bit(defined,
                             (*value).rec->getColumn()->getColumnNo());
          }
        }
        else if (field->type() == MYSQL_TYPE_BIT)
        {
2909 2910 2911 2912 2913 2914 2915 2916
          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
2917
          if (field->pack_length() < 5)
2918 2919
          {
            DBUG_PRINT("info", ("bit field H'%.8X", 
2920
                                (*value).rec->u_32_value()));
2921 2922
            field_bit->Field_bit::store((longlong) (*value).rec->u_32_value(),
                                        FALSE);
2923 2924 2925 2926
          }
          else
          {
            DBUG_PRINT("info", ("bit field H'%.8X%.8X",
unknown's avatar
unknown committed
2927 2928
                                *(Uint32*) (*value).rec->aRef(),
                                *((Uint32*) (*value).rec->aRef()+1)));
2929 2930
            field_bit->Field_bit::store((longlong) (*value).rec->u_64_value(), 
                                        TRUE);
2931
          }
2932 2933 2934 2935 2936
          /*
            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
2937 2938 2939 2940 2941 2942 2943 2944 2945
          DBUG_PRINT("info",("[%u] SET",
                             (*value).rec->getColumn()->getColumnNo()));
          DBUG_DUMP("info", (const char*) field->ptr, field->field_length);
        }
        else
        {
          DBUG_PRINT("info",("[%u] SET",
                             (*value).rec->getColumn()->getColumnNo()));
          DBUG_DUMP("info", (const char*) field->ptr, field->field_length);
2946
        }
unknown's avatar
unknown committed
2947 2948 2949
      }
      else
      {
unknown's avatar
unknown committed
2950
        NdbBlob *ndb_blob= (*value).blob;
unknown's avatar
unknown committed
2951
        uint col_no = ndb_blob->getColumn()->getColumnNo();
2952 2953
        int isNull;
        ndb_blob->getDefined(isNull);
unknown's avatar
unknown committed
2954
        if (isNull == 1)
2955
        {
unknown's avatar
unknown committed
2956
          DBUG_PRINT("info",("[%u] NULL", col_no));
unknown's avatar
unknown committed
2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973
          field->set_null(row_offset);
        }
        else if (isNull == -1)
        {
          DBUG_PRINT("info",("[%u] UNDEFINED", col_no));
          bitmap_clear_bit(defined, col_no);
        }
        else
        {
#ifndef DBUG_OFF
          // pointer vas set in get_ndb_blobs_value
          Field_blob *field_blob= (Field_blob*)field;
          char* ptr;
          field_blob->get_ptr(&ptr, row_offset);
          uint32 len= field_blob->get_length(row_offset);
          DBUG_PRINT("info",("[%u] SET ptr=%p len=%u", col_no, ptr, len));
#endif
2974
        }
unknown's avatar
unknown committed
2975 2976
      }
    }
unknown's avatar
unknown committed
2977
  }
unknown's avatar
unknown committed
2978 2979 2980 2981 2982 2983
  DBUG_VOID_RETURN;
}

void ha_ndbcluster::unpack_record(byte *buf)
{
  ndb_unpack_record(table, m_value, 0, buf);
unknown's avatar
unknown committed
2984 2985
#ifndef DBUG_OFF
  // Read and print all values that was fetched
unknown's avatar
unknown committed
2986
  if (table_share->primary_key == MAX_KEY)
unknown's avatar
unknown committed
2987 2988
  {
    // Table with hidden primary key
unknown's avatar
unknown committed
2989
    int hidden_no= table_share->fields;
2990
    const NDBTAB *tab= m_table;
unknown's avatar
unknown committed
2991
    const NDBCOL *hidden_col= tab->getColumn(hidden_no);
2992
    const NdbRecAttr* rec= m_value[hidden_no].rec;
unknown's avatar
unknown committed
2993
    DBUG_ASSERT(rec);
unknown's avatar
unknown committed
2994
    DBUG_PRINT("hidden", ("%d: %s \"%llu\"", hidden_no,
unknown's avatar
unknown committed
2995
                          hidden_col->getName(), rec->u_64_value()));
unknown's avatar
unknown committed
2996 2997
  }
  //DBUG_EXECUTE("value", print_results(););
unknown's avatar
unknown committed
2998 2999 3000 3001 3002
#endif
}

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

    DBUG_EXECUTE("value", print_results(););
unknown's avatar
unknown committed
3006 3007 3008 3009 3010 3011 3012
 */

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

#ifndef DBUG_OFF
unknown's avatar
Merge  
unknown committed
3013

3014
  char buf_type[MAX_FIELD_WIDTH], buf_val[MAX_FIELD_WIDTH];
unknown's avatar
Merge  
unknown committed
3015
  String type(buf_type, sizeof(buf_type), &my_charset_bin);
3016
  String val(buf_val, sizeof(buf_val), &my_charset_bin);
unknown's avatar
unknown committed
3017
  for (uint f= 0; f < table_share->fields; f++)
unknown's avatar
unknown committed
3018
  {
unknown's avatar
Merge  
unknown committed
3019
    /* Use DBUG_PRINT since DBUG_FILE cannot be filtered out */
3020
    char buf[2000];
unknown's avatar
unknown committed
3021
    Field *field;
3022
    void* ptr;
unknown's avatar
unknown committed
3023
    NdbValue value;
unknown's avatar
unknown committed
3024

3025
    buf[0]= 0;
unknown's avatar
Merge  
unknown committed
3026
    field= table->field[f];
unknown's avatar
unknown committed
3027
    if (!(value= m_value[f]).ptr)
unknown's avatar
unknown committed
3028
    {
unknown's avatar
unknown committed
3029
      strmov(buf, "not read");
3030
      goto print_value;
unknown's avatar
unknown committed
3031
    }
3032

3033
    ptr= field->ptr;
unknown's avatar
unknown committed
3034 3035

    if (! (field->flags & BLOB_FLAG))
unknown's avatar
unknown committed
3036
    {
unknown's avatar
unknown committed
3037 3038
      if (value.rec->isNULL())
      {
unknown's avatar
unknown committed
3039
        strmov(buf, "NULL");
3040
        goto print_value;
unknown's avatar
unknown committed
3041
      }
3042 3043 3044 3045 3046
      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
3047 3048 3049
    }
    else
    {
3050
      NdbBlob *ndb_blob= value.blob;
unknown's avatar
unknown committed
3051
      bool isNull= TRUE;
unknown's avatar
unknown committed
3052
      ndb_blob->getNull(isNull);
unknown's avatar
unknown committed
3053 3054
      if (isNull)
        strmov(buf, "NULL");
unknown's avatar
unknown committed
3055
    }
unknown's avatar
Merge  
unknown committed
3056

3057
print_value:
unknown's avatar
Merge  
unknown committed
3058
    DBUG_PRINT("value", ("%u,%s: %s", f, field->field_name, buf));
unknown's avatar
unknown committed
3059 3060 3061 3062 3063 3064
  }
#endif
  DBUG_VOID_RETURN;
}


3065
int ha_ndbcluster::index_init(uint index, bool sorted)
unknown's avatar
unknown committed
3066
{
3067
  DBUG_ENTER("ha_ndbcluster::index_init");
3068 3069 3070 3071
  DBUG_PRINT("enter", ("index: %u  sorted: %d", index, sorted));
  active_index= index;
  m_sorted= sorted;
  DBUG_RETURN(0);
unknown's avatar
unknown committed
3072 3073 3074 3075 3076
}


int ha_ndbcluster::index_end()
{
3077
  DBUG_ENTER("ha_ndbcluster::index_end");
3078
  DBUG_RETURN(close_scan());
unknown's avatar
unknown committed
3079 3080
}

3081 3082 3083 3084 3085 3086 3087 3088
/**
 * Check if key contains null
 */
static
int
check_null_in_key(const KEY* key_info, const byte *key, uint key_len)
{
  KEY_PART_INFO *curr_part, *end_part;
3089
  const byte* end_ptr= key + key_len;
3090 3091 3092 3093 3094 3095
  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
3096
    if (curr_part->null_bit && *key)
3097 3098 3099 3100 3101 3102
      return 1;

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

int ha_ndbcluster::index_read(byte *buf,
3105 3106
                              const byte *key, uint key_len, 
                              enum ha_rkey_function find_flag)
unknown's avatar
unknown committed
3107
{
3108 3109
  key_range start_key;
  bool descending= FALSE;
3110
  DBUG_ENTER("ha_ndbcluster::index_read");
unknown's avatar
unknown committed
3111 3112 3113
  DBUG_PRINT("enter", ("active_index: %u, key_len: %u, find_flag: %d", 
                       active_index, key_len, find_flag));

3114 3115 3116
  start_key.key= key;
  start_key.length= key_len;
  start_key.flag= find_flag;
3117
  descending= FALSE;
3118 3119 3120 3121 3122 3123 3124 3125 3126 3127
  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;
  }
3128 3129
  DBUG_RETURN(read_range_first_to_buf(&start_key, 0, descending,
                                      m_sorted, buf));
unknown's avatar
unknown committed
3130 3131 3132 3133
}


int ha_ndbcluster::index_read_idx(byte *buf, uint index_no, 
3134 3135
                              const byte *key, uint key_len, 
                              enum ha_rkey_function find_flag)
unknown's avatar
unknown committed
3136
{
unknown's avatar
unknown committed
3137
  statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status);
3138
  DBUG_ENTER("ha_ndbcluster::index_read_idx");
unknown's avatar
unknown committed
3139
  DBUG_PRINT("enter", ("index_no: %u, key_len: %u", index_no, key_len));  
unknown's avatar
unknown committed
3140
  close_scan();
3141
  index_init(index_no, 0);  
unknown's avatar
unknown committed
3142 3143 3144 3145 3146 3147
  DBUG_RETURN(index_read(buf, key, key_len, find_flag));
}


int ha_ndbcluster::index_next(byte *buf)
{
3148
  DBUG_ENTER("ha_ndbcluster::index_next");
unknown's avatar
unknown committed
3149
  statistic_increment(current_thd->status_var.ha_read_next_count,
3150
                      &LOCK_status);
3151
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
3152 3153 3154 3155 3156
}


int ha_ndbcluster::index_prev(byte *buf)
{
3157
  DBUG_ENTER("ha_ndbcluster::index_prev");
unknown's avatar
unknown committed
3158
  statistic_increment(current_thd->status_var.ha_read_prev_count,
3159
                      &LOCK_status);
3160
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
3161 3162 3163 3164 3165
}


int ha_ndbcluster::index_first(byte *buf)
{
3166
  DBUG_ENTER("ha_ndbcluster::index_first");
unknown's avatar
unknown committed
3167
  statistic_increment(current_thd->status_var.ha_read_first_count,
3168
                      &LOCK_status);
unknown's avatar
unknown committed
3169 3170 3171
  // Start the ordered index scan and fetch the first row

  // Only HA_READ_ORDER indexes get called by index_first
3172
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, FALSE, buf, NULL));
unknown's avatar
unknown committed
3173 3174 3175 3176 3177
}


int ha_ndbcluster::index_last(byte *buf)
{
3178
  DBUG_ENTER("ha_ndbcluster::index_last");
3179
  statistic_increment(current_thd->status_var.ha_read_last_count,&LOCK_status);
3180
  DBUG_RETURN(ordered_index_scan(0, 0, TRUE, TRUE, buf, NULL));
unknown's avatar
unknown committed
3181 3182
}

3183 3184 3185 3186 3187
int ha_ndbcluster::index_read_last(byte * buf, const byte * key, uint key_len)
{
  DBUG_ENTER("ha_ndbcluster::index_read_last");
  DBUG_RETURN(index_read(buf, key, key_len, HA_READ_PREFIX_LAST));
}
unknown's avatar
unknown committed
3188

3189
int ha_ndbcluster::read_range_first_to_buf(const key_range *start_key,
3190
                                           const key_range *end_key,
3191
                                           bool desc, bool sorted,
3192
                                           byte* buf)
3193
{
3194 3195 3196 3197
  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; 
3198
  DBUG_ENTER("ha_ndbcluster::read_range_first_to_buf");
3199
  DBUG_PRINT("info", ("desc: %d, sorted: %d", desc, sorted));
3200

3201 3202 3203
  if (m_use_partition_function)
  {
    get_partition_set(table, buf, active_index, start_key, &part_spec);
3204 3205 3206 3207 3208 3209 3210 3211
    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.
    */
3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225
    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;
    }
  }
3226

3227 3228
  m_write_op= FALSE;
  switch (type){
3229
  case PRIMARY_KEY_ORDERED_INDEX:
3230
  case PRIMARY_KEY_INDEX:
3231
    if (start_key && 
3232 3233
        start_key->length == key_info->key_length &&
        start_key->flag == HA_READ_KEY_EXACT)
3234
    {
unknown's avatar
unknown committed
3235
      if (m_active_cursor && (error= close_scan()))
3236
        DBUG_RETURN(error);
3237 3238
      DBUG_RETURN(pk_read(start_key->key, start_key->length, buf,
                          part_spec.start_part));
3239
    }
3240
    break;
3241
  case UNIQUE_ORDERED_INDEX:
3242
  case UNIQUE_INDEX:
3243
    if (start_key && start_key->length == key_info->key_length &&
3244 3245
        start_key->flag == HA_READ_KEY_EXACT && 
        !check_null_in_key(key_info, start_key->key, start_key->length))
3246
    {
unknown's avatar
unknown committed
3247
      if (m_active_cursor && (error= close_scan()))
3248
        DBUG_RETURN(error);
3249
      DBUG_RETURN(unique_index_read(start_key->key, start_key->length, buf));
3250
    }
3251 3252 3253 3254
    break;
  default:
    break;
  }
3255
  // Start the ordered index scan and fetch the first row
3256 3257
  DBUG_RETURN(ordered_index_scan(start_key, end_key, sorted, desc, buf,
                                 &part_spec));
3258 3259
}

unknown's avatar
unknown committed
3260
int ha_ndbcluster::read_range_first(const key_range *start_key,
3261 3262
                                    const key_range *end_key,
                                    bool eq_r, bool sorted)
unknown's avatar
unknown committed
3263 3264 3265
{
  byte* buf= table->record[0];
  DBUG_ENTER("ha_ndbcluster::read_range_first");
3266 3267
  DBUG_RETURN(read_range_first_to_buf(start_key, end_key, FALSE,
                                      sorted, buf));
unknown's avatar
unknown committed
3268 3269
}

3270
int ha_ndbcluster::read_range_next()
3271 3272 3273 3274 3275 3276
{
  DBUG_ENTER("ha_ndbcluster::read_range_next");
  DBUG_RETURN(next_result(table->record[0]));
}


unknown's avatar
unknown committed
3277 3278
int ha_ndbcluster::rnd_init(bool scan)
{
3279
  NdbScanOperation *cursor= m_active_cursor;
unknown's avatar
unknown committed
3280 3281
  DBUG_ENTER("rnd_init");
  DBUG_PRINT("enter", ("scan: %d", scan));
3282
  // Check if scan is to be restarted
unknown's avatar
unknown committed
3283 3284 3285 3286
  if (cursor)
  {
    if (!scan)
      DBUG_RETURN(1);
unknown's avatar
unknown committed
3287
    if (cursor->restart(m_force_send) != 0)
3288 3289 3290 3291
    {
      DBUG_ASSERT(0);
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
3292
  }
unknown's avatar
unknown committed
3293
  index_init(table_share->primary_key, 0);
unknown's avatar
unknown committed
3294 3295 3296
  DBUG_RETURN(0);
}

3297 3298
int ha_ndbcluster::close_scan()
{
3299
  NdbTransaction *trans= m_active_trans;
3300 3301
  DBUG_ENTER("close_scan");

unknown's avatar
unknown committed
3302 3303
  m_multi_cursor= 0;
  if (!m_active_cursor && !m_multi_cursor)
3304 3305
    DBUG_RETURN(1);

unknown's avatar
unknown committed
3306
  NdbScanOperation *cursor= m_active_cursor ? m_active_cursor : m_multi_cursor;
unknown's avatar
unknown committed
3307
  
3308
  if (m_ops_pending)
unknown's avatar
unknown committed
3309 3310 3311 3312 3313
  {
    /*
      Take over any pending transactions to the 
      deleteing/updating transaction before closing the scan    
    */
3314
    DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending));    
unknown's avatar
unknown committed
3315
    if (execute_no_commit(this,trans) != 0) {
3316
      no_uncommitted_rows_execute_failure();
unknown's avatar
unknown committed
3317
      DBUG_RETURN(ndb_err(trans));
3318
    }
3319
    m_ops_pending= 0;
unknown's avatar
unknown committed
3320 3321
  }
  
unknown's avatar
unknown committed
3322
  cursor->close(m_force_send, TRUE);
unknown's avatar
unknown committed
3323
  m_active_cursor= m_multi_cursor= NULL;
unknown's avatar
unknown committed
3324
  DBUG_RETURN(0);
3325
}
unknown's avatar
unknown committed
3326 3327 3328 3329

int ha_ndbcluster::rnd_end()
{
  DBUG_ENTER("rnd_end");
3330
  DBUG_RETURN(close_scan());
unknown's avatar
unknown committed
3331 3332 3333 3334 3335 3336
}


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

unknown's avatar
unknown committed
3340
  if (!m_active_cursor)
3341 3342
    DBUG_RETURN(full_table_scan(buf));
  DBUG_RETURN(next_result(buf));
unknown's avatar
unknown committed
3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355
}


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

int ha_ndbcluster::rnd_pos(byte *buf, byte *pos)
{
  DBUG_ENTER("rnd_pos");
unknown's avatar
unknown committed
3356
  statistic_increment(current_thd->status_var.ha_read_rnd_count,
3357
                      &LOCK_status);
unknown's avatar
unknown committed
3358 3359
  // The primary key for the record is stored in pos
  // Perform a pk_read using primary key "index"
3360 3361
  {
    part_id_range part_spec;
3362
    uint key_length= ref_length;
3363 3364
    if (m_use_partition_function)
    {
3365 3366 3367 3368 3369 3370
      if (table_share->primary_key == MAX_KEY)
      {
        /*
          The partition id has been fetched from ndb
          and has been stored directly after the hidden key
        */
unknown's avatar
unknown committed
3371
        DBUG_DUMP("key+part", (char *)pos, key_length);
3372
        key_length= ref_length - sizeof(m_part_id);
3373
        part_spec.start_part= part_spec.end_part= *(uint32 *)(pos + key_length);
3374 3375 3376 3377
      }
      else
      {
        key_range key_spec;
3378
        KEY *key_info= table->key_info + table_share->primary_key;
3379 3380 3381 3382 3383 3384 3385 3386
        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));
3387
    }
unknown's avatar
unknown committed
3388
    DBUG_DUMP("key", (char *)pos, key_length);
3389
    DBUG_RETURN(pk_read(pos, key_length, buf, part_spec.start_part));
3390
  }
unknown's avatar
unknown committed
3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405
}


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

void ha_ndbcluster::position(const byte *record)
{
  KEY *key_info;
  KEY_PART_INFO *key_part;
  KEY_PART_INFO *end;
  byte *buff;
3406 3407
  uint key_length;

unknown's avatar
unknown committed
3408 3409
  DBUG_ENTER("position");

unknown's avatar
unknown committed
3410
  if (table_share->primary_key != MAX_KEY) 
unknown's avatar
unknown committed
3411
  {
3412
    key_length= ref_length;
unknown's avatar
unknown committed
3413
    key_info= table->key_info + table_share->primary_key;
unknown's avatar
unknown committed
3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428
    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;
      }
3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448

      size_t len = key_part->length;
      const byte * ptr = record + key_part->offset;
      Field *field = key_part->field;
      if ((field->type() ==  MYSQL_TYPE_VARCHAR) &&
	  ((Field_varstring*)field)->length_bytes == 1)
      {
	/** 
	 * Keys always use 2 bytes length
	 */
	buff[0] = ptr[0];
	buff[1] = 0;
	memcpy(buff+2, ptr + 1, len);	
	len += 2;
      }
      else
      {
	memcpy(buff, ptr, len);
      }
      buff += len;
unknown's avatar
unknown committed
3449 3450 3451 3452 3453 3454
    }
  } 
  else 
  {
    // No primary key, get hidden key
    DBUG_PRINT("info", ("Getting hidden key"));
3455 3456 3457
    // If table has user defined partition save the partition id as well
    if(m_use_partition_function)
    {
unknown's avatar
unknown committed
3458
      DBUG_PRINT("info", ("Saving partition id %u", m_part_id));
3459 3460 3461
      key_length= ref_length - sizeof(m_part_id);
      memcpy(ref+key_length, (void *)&m_part_id, sizeof(m_part_id));
    }
3462 3463
    else
      key_length= ref_length;
3464
#ifndef DBUG_OFF
3465
    int hidden_no= table->s->fields;
3466
    const NDBTAB *tab= m_table;  
unknown's avatar
unknown committed
3467 3468 3469
    const NDBCOL *hidden_col= tab->getColumn(hidden_no);
    DBUG_ASSERT(hidden_col->getPrimaryKey() && 
                hidden_col->getAutoIncrement() &&
3470
                key_length == NDB_HIDDEN_PRIMARY_KEY_LENGTH);
3471
#endif
3472
    memcpy(ref, m_ref, key_length);
unknown's avatar
unknown committed
3473
  }
unknown's avatar
unknown committed
3474 3475 3476 3477
#ifndef DBUG_OFF
  if (table_share->primary_key == MAX_KEY && m_use_partition_function) 
    DBUG_DUMP("key+part", (char*)ref, key_length+sizeof(m_part_id));
#endif
3478
  DBUG_DUMP("ref", (char*)ref, key_length);
unknown's avatar
unknown committed
3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494
  DBUG_VOID_RETURN;
}


void ha_ndbcluster::info(uint flag)
{
  DBUG_ENTER("info");
  DBUG_PRINT("enter", ("flag: %d", flag));
  
  if (flag & HA_STATUS_POS)
    DBUG_PRINT("info", ("HA_STATUS_POS"));
  if (flag & HA_STATUS_NO_LOCK)
    DBUG_PRINT("info", ("HA_STATUS_NO_LOCK"));
  if (flag & HA_STATUS_TIME)
    DBUG_PRINT("info", ("HA_STATUS_TIME"));
  if (flag & HA_STATUS_VARIABLE)
3495
  {
unknown's avatar
unknown committed
3496
    DBUG_PRINT("info", ("HA_STATUS_VARIABLE"));
3497 3498
    if (m_table_info)
    {
3499
      if (m_ha_not_exact_count)
3500
        records= 100;
3501
      else
3502
        records_update();
3503 3504 3505
    }
    else
    {
3506 3507 3508
      if ((my_errno= check_ndb_connection()))
        DBUG_VOID_RETURN;
      Ndb *ndb= get_ndb();
3509
      ndb->setDatabaseName(m_dbname);
3510 3511
      struct Ndb_statistics stat;
      if (current_thd->variables.ndb_use_exact_count &&
3512
          ndb_get_table_statistics(ndb, m_table, &stat) == 0)
3513
      {
3514 3515 3516
        mean_rec_length= stat.row_size;
        data_file_length= stat.fragment_memory;
        records= stat.row_count;
3517 3518 3519
      }
      else
      {
3520 3521
        mean_rec_length= 0;
        records= 100;
3522
      }
3523
    }
3524
  }
unknown's avatar
unknown committed
3525 3526 3527 3528 3529
  if (flag & HA_STATUS_CONST)
  {
    DBUG_PRINT("info", ("HA_STATUS_CONST"));
    set_rec_per_key();
  }
unknown's avatar
unknown committed
3530
  if (flag & HA_STATUS_ERRKEY)
3531
  {
unknown's avatar
unknown committed
3532
    DBUG_PRINT("info", ("HA_STATUS_ERRKEY"));
3533
    errkey= m_dupkey;
3534
  }
unknown's avatar
unknown committed
3535
  if (flag & HA_STATUS_AUTO)
3536
  {
unknown's avatar
unknown committed
3537
    DBUG_PRINT("info", ("HA_STATUS_AUTO"));
3538 3539 3540 3541 3542
    if (m_table)
    {
      Ndb *ndb= get_ndb();
      
      auto_increment_value= 
3543
        ndb->readAutoIncrementValue(m_table);
3544 3545
    }
  }
unknown's avatar
unknown committed
3546 3547 3548
  DBUG_VOID_RETURN;
}

3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562

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
3563 3564 3565 3566 3567 3568
int ha_ndbcluster::extra(enum ha_extra_function operation)
{
  DBUG_ENTER("extra");
  switch (operation) {
  case HA_EXTRA_RESET:                 /* Reset database to after open */
    DBUG_PRINT("info", ("HA_EXTRA_RESET"));
3569 3570
    DBUG_PRINT("info", ("Clearing condition stack"));
    cond_clear();
3571 3572 3573 3574 3575
    /*
     * Regular partition pruning will set the bitmap appropriately.
     * Some queries like ALTER TABLE doesn't use partition pruning and
     * thus the 'used_partitions' bitmap needs to be initialized
     */
unknown's avatar
unknown committed
3576
    if (m_part_info)
3577
      bitmap_set_all(&m_part_info->used_partitions);
unknown's avatar
unknown committed
3578 3579 3580
    break;
  case HA_EXTRA_IGNORE_DUP_KEY:       /* Dup keys don't rollback everything*/
    DBUG_PRINT("info", ("HA_EXTRA_IGNORE_DUP_KEY"));
3581
    if (current_thd->lex->sql_command == SQLCOM_REPLACE && !m_has_unique_index)
3582 3583 3584 3585 3586
    {
      DBUG_PRINT("info", ("Turning ON use of write instead of insert"));
      m_use_write= TRUE;
    } else 
    {
3587 3588
      DBUG_PRINT("info", ("Ignoring duplicate key"));
      m_ignore_dup_key= TRUE;
3589
    }
unknown's avatar
unknown committed
3590 3591 3592 3593
    break;
  case HA_EXTRA_NO_IGNORE_DUP_KEY:
    DBUG_PRINT("info", ("HA_EXTRA_NO_IGNORE_DUP_KEY"));
    DBUG_PRINT("info", ("Turning OFF use of write instead of insert"));
unknown's avatar
unknown committed
3594
    m_use_write= FALSE;
3595
    m_ignore_dup_key= FALSE;
unknown's avatar
unknown committed
3596
    break;
unknown's avatar
unknown committed
3597 3598 3599 3600 3601 3602 3603 3604 3605 3606
  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;
3607 3608
  default:
    break;
unknown's avatar
unknown committed
3609 3610 3611 3612 3613
  }
  
  DBUG_RETURN(0);
}

3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626
/* 
   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;
3627
  const NDBTAB *tab= m_table;    
3628 3629

  DBUG_ENTER("start_bulk_insert");
unknown's avatar
unknown committed
3630
  DBUG_PRINT("enter", ("rows: %d", (int)rows));
3631
  
3632 3633
  m_rows_inserted= (ha_rows) 0;
  if (rows == (ha_rows) 0)
unknown's avatar
unknown committed
3634
  {
3635 3636
    /* We don't know how many will be inserted, guess */
    m_rows_to_insert= m_autoincrement_prefetch;
unknown's avatar
unknown committed
3637
  }
3638 3639
  else
    m_rows_to_insert= rows; 
3640 3641 3642 3643 3644 3645 3646 3647

  /* 
    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.   
  */
3648
  const int bytesperbatch= 8192;
3649
  bytes= 12 + tab->getRowSizeInBytes() + 4 * tab->getNoOfColumns();
3650
  batch= bytesperbatch/bytes;
3651 3652
  batch= batch == 0 ? 1 : batch;
  DBUG_PRINT("info", ("batch: %d, bytes: %d", batch, bytes));
3653
  m_bulk_insert_rows= batch;
3654 3655 3656 3657 3658 3659 3660 3661 3662

  DBUG_VOID_RETURN;
}

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

3665
  DBUG_ENTER("end_bulk_insert");
3666
  // Check if last inserts need to be flushed
3667
  if (m_bulk_insert_not_flushed)
3668
  {
3669
    NdbTransaction *trans= m_active_trans;
3670 3671 3672
    // Send rows to NDB
    DBUG_PRINT("info", ("Sending inserts to NDB, "\
                        "rows_inserted:%d, bulk_insert_rows: %d", 
3673
                        (int) m_rows_inserted, (int) m_bulk_insert_rows)); 
3674
    m_bulk_insert_not_flushed= FALSE;
3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694
    if (m_transaction_on)
    {
      if (execute_no_commit(this, trans) != 0)
      {
        no_uncommitted_rows_execute_failure();
        my_errno= error= ndb_err(trans);
      }
    }
    else
    {
      if (execute_commit(this, trans) != 0)
      {
        no_uncommitted_rows_execute_failure();
        my_errno= error= ndb_err(trans);
      }
      else
      {
        int res= trans->restart();
        DBUG_ASSERT(res == 0);
      }
3695
    }
3696 3697
  }

3698 3699
  m_rows_inserted= (ha_rows) 0;
  m_rows_to_insert= (ha_rows) 1;
3700
  DBUG_RETURN(error);
3701 3702
}

unknown's avatar
unknown committed
3703 3704 3705 3706

int ha_ndbcluster::extra_opt(enum ha_extra_function operation, ulong cache_size)
{
  DBUG_ENTER("extra_opt");
unknown's avatar
unknown committed
3707
  DBUG_PRINT("enter", ("cache_size: %lu", cache_size));
unknown's avatar
unknown committed
3708 3709 3710
  DBUG_RETURN(extra(operation));
}

unknown's avatar
unknown committed
3711 3712 3713 3714
static const char *ha_ndbcluster_exts[] = {
 ha_ndb_ext,
 NullS
};
unknown's avatar
unknown committed
3715

3716
const char** ha_ndbcluster::bas_ext() const
unknown's avatar
unknown committed
3717 3718 3719
{
  return ha_ndbcluster_exts;
}
unknown's avatar
unknown committed
3720 3721 3722 3723 3724 3725 3726 3727 3728

/*
  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
3729 3730 3731
  DBUG_ENTER("ha_ndbcluster::scan_time()");
  double res= rows2double(records*1000);
  DBUG_PRINT("exit", ("table: %s value: %f", 
3732
                      m_tabname, res));
unknown's avatar
unknown committed
3733
  DBUG_RETURN(res);
unknown's avatar
unknown committed
3734 3735
}

unknown's avatar
unknown committed
3736 3737 3738 3739 3740 3741 3742
/*
  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
3743 3744 3745 3746 3747 3748 3749 3750

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
3751

unknown's avatar
unknown committed
3752 3753 3754
    /* If we are not doing a LOCK TABLE, then allow multiple
       writers */
    
3755 3756 3757
    /* Since NDB does not currently have table locks
       this is treated as a ordinary lock */

3758
    if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
unknown's avatar
unknown committed
3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773
         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;
3774 3775

  DBUG_PRINT("exit", ("lock_type: %d", lock_type));
unknown's avatar
unknown committed
3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797
  
  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
3798
  for the statement, this will be stored in thd_ndb.stmt.
unknown's avatar
unknown committed
3799
  If not, we have to start a master transaction if there doesn't exist
3800
  one from before, this will be stored in thd_ndb.all
unknown's avatar
unknown committed
3801 3802 3803
 
  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  
3804
  If we are locking the table then:
3805
  - save the NdbDictionary::Table for easy access
3806 3807
  - save reference to table statistics
  - refresh list of the indexes for the table if needed (if altered)
unknown's avatar
unknown committed
3808 3809 3810 3811 3812
 */

int ha_ndbcluster::external_lock(THD *thd, int lock_type)
{
  int error=0;
3813
  NdbTransaction* trans= NULL;
unknown's avatar
unknown committed
3814 3815 3816 3817 3818 3819

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

3823
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
3824
  Ndb *ndb= thd_ndb->ndb;
3825

unknown's avatar
unknown committed
3826 3827 3828
  DBUG_PRINT("enter", ("this: %x  thd: %lx  thd_ndb: %lx  "
                       "thd_ndb->lock_count: %d",
                       this, thd, thd_ndb, thd_ndb->lock_count));
3829

unknown's avatar
unknown committed
3830 3831
  if (lock_type != F_UNLCK)
  {
3832
    DBUG_PRINT("info", ("lock_type != F_UNLCK"));
3833 3834 3835 3836
    if (!thd->transaction.on)
      m_transaction_on= FALSE;
    else
      m_transaction_on= thd->variables.ndb_use_transactions;
3837
    if (!thd_ndb->lock_count++)
unknown's avatar
unknown committed
3838 3839
    {
      PRINT_OPTION_FLAGS(thd);
3840
      if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) 
unknown's avatar
unknown committed
3841 3842
      {
        // Autocommit transaction
3843
        DBUG_ASSERT(!thd_ndb->stmt);
unknown's avatar
unknown committed
3844 3845
        DBUG_PRINT("trans",("Starting transaction stmt"));      

3846
        trans= ndb->startTransaction();
unknown's avatar
unknown committed
3847
        if (trans == NULL)
3848
          ERR_RETURN(ndb->getNdbError());
3849
        thd_ndb->init_open_tables();
3850
        thd_ndb->stmt= trans;
3851
        trans_register_ha(thd, FALSE, &ndbcluster_hton);
unknown's avatar
unknown committed
3852 3853 3854
      } 
      else 
      { 
3855
        if (!thd_ndb->all)
3856
        {
unknown's avatar
unknown committed
3857 3858 3859 3860
          // Not autocommit transaction
          // A "master" transaction ha not been started yet
          DBUG_PRINT("trans",("starting transaction, all"));
          
3861
          trans= ndb->startTransaction();
unknown's avatar
unknown committed
3862
          if (trans == NULL)
3863
            ERR_RETURN(ndb->getNdbError());
3864
          thd_ndb->init_open_tables();
3865
          thd_ndb->all= trans; 
3866
          trans_register_ha(thd, TRUE, &ndbcluster_hton);
unknown's avatar
unknown committed
3867 3868 3869 3870 3871 3872 3873 3874

          /*
            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))
3875
          {
unknown's avatar
unknown committed
3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894
            //lockThisTable();
            DBUG_PRINT("info", ("Locking the table..." ));
          }

        }
      }
    }
    /*
      This is the place to make sure this handler instance
      has a started transaction.
     
      The transaction is started by the first handler on which 
      MySQL Server calls external lock
     
      Other handlers in the same stmt or transaction should use 
      the same NDB transaction. This is done by setting up the m_active_trans
      pointer to point to the NDB transaction. 
     */

3895 3896 3897
    // 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;
3898 3899
    m_autoincrement_prefetch= 
      (ha_rows) thd->variables.ndb_autoincrement_prefetch_sz;
3900

3901
    m_active_trans= thd_ndb->all ? thd_ndb->all : thd_ndb->stmt;
unknown's avatar
unknown committed
3902
    DBUG_ASSERT(m_active_trans);
3903
    // Start of transaction
3904 3905
    m_rows_changed= 0;
    m_ops_pending= 0;
3906 3907

    // TODO remove double pointers...
3908 3909
    m_thd_ndb_share= thd_ndb->get_open_table(thd, m_table);
    m_table_info= &m_thd_ndb_share->stat;
3910 3911
  }
  else
unknown's avatar
unknown committed
3912
  {
3913
    DBUG_PRINT("info", ("lock_type == F_UNLCK"));
3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931

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

3932
    if (!--thd_ndb->lock_count)
unknown's avatar
unknown committed
3933 3934 3935 3936
    {
      DBUG_PRINT("trans", ("Last external_lock"));
      PRINT_OPTION_FLAGS(thd);

3937
      if (thd_ndb->stmt)
unknown's avatar
unknown committed
3938 3939 3940 3941 3942 3943 3944
      {
        /*
          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"));
3945
        ndb->closeTransaction(m_active_trans);
3946
        thd_ndb->stmt= NULL;
unknown's avatar
unknown committed
3947 3948
      }
    }
unknown's avatar
unknown committed
3949
    m_table_info= NULL;
3950

3951 3952 3953 3954 3955 3956 3957 3958 3959
    /*
      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;    

3960 3961
    if (m_active_cursor)
      DBUG_PRINT("warning", ("m_active_cursor != NULL"));
3962 3963
    m_active_cursor= NULL;

unknown's avatar
unknown committed
3964 3965 3966 3967
    if (m_multi_cursor)
      DBUG_PRINT("warning", ("m_multi_cursor != NULL"));
    m_multi_cursor= NULL;
    
3968
    if (m_blobs_pending)
3969
      DBUG_PRINT("warning", ("blobs_pending != 0"));
3970
    m_blobs_pending= 0;
3971
    
3972
    if (m_ops_pending)
3973
      DBUG_PRINT("warning", ("ops_pending != 0L"));
3974
    m_ops_pending= 0;
unknown's avatar
unknown committed
3975 3976 3977 3978 3979
  }
  DBUG_RETURN(error);
}

/*
3980 3981 3982 3983 3984
  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
3985 3986
*/

unknown's avatar
unknown committed
3987
int ha_ndbcluster::start_stmt(THD *thd, thr_lock_type lock_type)
unknown's avatar
unknown committed
3988 3989 3990 3991 3992
{
  int error=0;
  DBUG_ENTER("start_stmt");
  PRINT_OPTION_FLAGS(thd);

3993
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
3994
  NdbTransaction *trans= (thd_ndb->stmt)?thd_ndb->stmt:thd_ndb->all;
unknown's avatar
unknown committed
3995
  if (!trans){
3996
    Ndb *ndb= thd_ndb->ndb;
unknown's avatar
unknown committed
3997
    DBUG_PRINT("trans",("Starting transaction stmt"));  
3998
    trans= ndb->startTransaction();
unknown's avatar
unknown committed
3999
    if (trans == NULL)
4000
      ERR_RETURN(ndb->getNdbError());
4001
    no_uncommitted_rows_reset(thd);
4002 4003
    thd_ndb->stmt= trans;
    trans_register_ha(thd, FALSE, &ndbcluster_hton);
unknown's avatar
unknown committed
4004 4005
  }
  m_active_trans= trans;
4006

4007
  // Start of statement
4008
  m_ops_pending= 0;    
unknown's avatar
unknown committed
4009 4010 4011 4012 4013 4014
  
  DBUG_RETURN(error);
}


/*
4015
  Commit a transaction started in NDB
unknown's avatar
unknown committed
4016 4017
 */

unknown's avatar
unknown committed
4018
static int ndbcluster_commit(THD *thd, bool all)
unknown's avatar
unknown committed
4019 4020
{
  int res= 0;
4021 4022 4023
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  Ndb *ndb= thd_ndb->ndb;
  NdbTransaction *trans= all ? thd_ndb->all : thd_ndb->stmt;
unknown's avatar
unknown committed
4024 4025 4026

  DBUG_ENTER("ndbcluster_commit");
  DBUG_PRINT("transaction",("%s",
4027
                            trans == thd_ndb->stmt ?
unknown's avatar
unknown committed
4028 4029 4030
                            "stmt" : "all"));
  DBUG_ASSERT(ndb && trans);

4031
  if (execute_commit(thd,trans) != 0)
unknown's avatar
unknown committed
4032 4033
  {
    const NdbError err= trans->getNdbError();
4034
    const NdbOperation *error_op= trans->getNdbErrorOperation();
4035
    ERR_PRINT(err);
unknown's avatar
unknown committed
4036
    res= ndb_to_mysql_error(&err);
4037
    if (res != -1)
4038
      ndbcluster_print_error(res, error_op);
unknown's avatar
unknown committed
4039
  }
4040
  ndb->closeTransaction(trans);
4041

unknown's avatar
unknown committed
4042
  if (all)
4043 4044 4045
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;
4046 4047 4048 4049 4050 4051 4052

  /* 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
4053 4054
    DBUG_PRINT("info", ("Invalidate commit_count for %s, share->commit_count: %d ",
			share->key, share->commit_count));
4055 4056 4057 4058 4059 4060
    share->commit_count= 0;
    share->commit_count_lock++;
    pthread_mutex_unlock(&share->mutex);
  }
  thd_ndb->changed_tables.empty();

unknown's avatar
unknown committed
4061 4062 4063 4064 4065 4066 4067 4068
  DBUG_RETURN(res);
}


/*
  Rollback a transaction started in NDB
 */

unknown's avatar
unknown committed
4069
static int ndbcluster_rollback(THD *thd, bool all)
unknown's avatar
unknown committed
4070 4071
{
  int res= 0;
4072 4073 4074
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
  Ndb *ndb= thd_ndb->ndb;
  NdbTransaction *trans= all ? thd_ndb->all : thd_ndb->stmt;
unknown's avatar
unknown committed
4075 4076 4077

  DBUG_ENTER("ndbcluster_rollback");
  DBUG_PRINT("transaction",("%s",
4078
                            trans == thd_ndb->stmt ? 
unknown's avatar
unknown committed
4079 4080 4081
                            "stmt" : "all"));
  DBUG_ASSERT(ndb && trans);

4082
  if (trans->execute(NdbTransaction::Rollback) != 0)
unknown's avatar
unknown committed
4083 4084
  {
    const NdbError err= trans->getNdbError();
4085
    const NdbOperation *error_op= trans->getNdbErrorOperation();
unknown's avatar
unknown committed
4086 4087
    ERR_PRINT(err);     
    res= ndb_to_mysql_error(&err);
4088 4089
    if (res != -1) 
      ndbcluster_print_error(res, error_op);
unknown's avatar
unknown committed
4090 4091
  }
  ndb->closeTransaction(trans);
4092

unknown's avatar
unknown committed
4093
  if (all)
4094 4095 4096 4097
    thd_ndb->all= NULL;
  else
    thd_ndb->stmt= NULL;

4098 4099 4100
  /* Clear list of tables changed by transaction */
  thd_ndb->changed_tables.empty();

4101
  DBUG_RETURN(res);
unknown's avatar
unknown committed
4102 4103 4104 4105
}


/*
unknown's avatar
unknown committed
4106 4107 4108
  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
4109 4110 4111

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

unknown's avatar
unknown committed
4114 4115 4116
static int create_ndb_column(NDBCOL &col,
                             Field *field,
                             HA_CREATE_INFO *info)
unknown's avatar
unknown committed
4117
{
unknown's avatar
unknown committed
4118
  // Set name
unknown's avatar
unknown committed
4119
  col.setName(field->field_name);
unknown's avatar
unknown committed
4120 4121
  // Get char set
  CHARSET_INFO *cs= field->charset();
unknown's avatar
unknown committed
4122 4123 4124 4125
  // Set type and sizes
  const enum enum_field_types mysql_type= field->real_type();
  switch (mysql_type) {
  // Numeric types
unknown's avatar
unknown committed
4126
  case MYSQL_TYPE_TINY:        
unknown's avatar
unknown committed
4127 4128 4129 4130 4131 4132
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Tinyunsigned);
    else
      col.setType(NDBCOL::Tinyint);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
4133
  case MYSQL_TYPE_SHORT:
unknown's avatar
unknown committed
4134 4135 4136 4137 4138 4139
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Smallunsigned);
    else
      col.setType(NDBCOL::Smallint);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
4140
  case MYSQL_TYPE_LONG:
unknown's avatar
unknown committed
4141 4142 4143 4144 4145 4146
    if (field->flags & UNSIGNED_FLAG)
      col.setType(NDBCOL::Unsigned);
    else
      col.setType(NDBCOL::Int);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
4147
  case MYSQL_TYPE_INT24:       
unknown's avatar
unknown committed
4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159
    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
4160 4161
    break;
  case MYSQL_TYPE_FLOAT:
unknown's avatar
unknown committed
4162 4163 4164
    col.setType(NDBCOL::Float);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
4165
  case MYSQL_TYPE_DOUBLE:
unknown's avatar
unknown committed
4166 4167 4168
    col.setType(NDBCOL::Double);
    col.setLength(1);
    break;
4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188
  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;
4189 4190 4191
  case MYSQL_TYPE_NEWDECIMAL:    
    {
      Field_new_decimal *f= (Field_new_decimal*)field;
unknown's avatar
unknown committed
4192
      uint precision= f->precision;
4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206
      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
4207 4208 4209 4210 4211
  // Date types
  case MYSQL_TYPE_DATETIME:    
    col.setType(NDBCOL::Datetime);
    col.setLength(1);
    break;
4212 4213 4214 4215
  case MYSQL_TYPE_DATE: // ?
    col.setType(NDBCOL::Char);
    col.setLength(field->pack_length());
    break;
unknown's avatar
unknown committed
4216
  case MYSQL_TYPE_NEWDATE:
unknown's avatar
unknown committed
4217 4218 4219
    col.setType(NDBCOL::Date);
    col.setLength(1);
    break;
unknown's avatar
unknown committed
4220
  case MYSQL_TYPE_TIME:        
unknown's avatar
unknown committed
4221 4222 4223
    col.setType(NDBCOL::Time);
    col.setLength(1);
    break;
4224 4225 4226 4227 4228 4229 4230
  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
4231 4232 4233
    break;
  // Char types
  case MYSQL_TYPE_STRING:      
4234
    if (field->pack_length() == 0)
4235 4236 4237 4238
    {
      col.setType(NDBCOL::Bit);
      col.setLength(1);
    }
unknown's avatar
unknown committed
4239
    else if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4240
    {
unknown's avatar
unknown committed
4241
      col.setType(NDBCOL::Binary);
unknown's avatar
unknown committed
4242
      col.setLength(field->pack_length());
unknown's avatar
unknown committed
4243
    }
4244
    else
unknown's avatar
unknown committed
4245 4246 4247
    {
      col.setType(NDBCOL::Char);
      col.setCharset(cs);
4248
      col.setLength(field->pack_length());
unknown's avatar
unknown committed
4249
    }
unknown's avatar
unknown committed
4250
    break;
unknown's avatar
unknown committed
4251 4252 4253 4254 4255 4256
  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
4257
        if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4258 4259 4260 4261 4262 4263 4264 4265
          col.setType(NDBCOL::Varbinary);
        else {
          col.setType(NDBCOL::Varchar);
          col.setCharset(cs);
        }
      }
      else if (f->length_bytes == 2)
      {
unknown's avatar
unknown committed
4266
        if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277
          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
4278
    }
unknown's avatar
unknown committed
4279 4280 4281 4282
    break;
  // Blob types (all come in as MYSQL_TYPE_BLOB)
  mysql_type_tiny_blob:
  case MYSQL_TYPE_TINY_BLOB:
unknown's avatar
unknown committed
4283
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4284
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
4285
    else {
unknown's avatar
unknown committed
4286
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
4287 4288
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
4289 4290 4291 4292 4293
    col.setInlineSize(256);
    // No parts
    col.setPartSize(0);
    col.setStripeSize(0);
    break;
4294
  //mysql_type_blob:
4295
  case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
4296
  case MYSQL_TYPE_BLOB:    
unknown's avatar
unknown committed
4297
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4298
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
4299
    else {
unknown's avatar
unknown committed
4300
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
4301 4302
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318
    // Use "<=" even if "<" is the exact condition
    if (field->max_length() <= (1 << 8))
      goto mysql_type_tiny_blob;
    else if (field->max_length() <= (1 << 16))
    {
      col.setInlineSize(256);
      col.setPartSize(2000);
      col.setStripeSize(16);
    }
    else if (field->max_length() <= (1 << 24))
      goto mysql_type_medium_blob;
    else
      goto mysql_type_long_blob;
    break;
  mysql_type_medium_blob:
  case MYSQL_TYPE_MEDIUM_BLOB:   
unknown's avatar
unknown committed
4319
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4320
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
4321
    else {
unknown's avatar
unknown committed
4322
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
4323 4324
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
4325 4326 4327 4328 4329 4330
    col.setInlineSize(256);
    col.setPartSize(4000);
    col.setStripeSize(8);
    break;
  mysql_type_long_blob:
  case MYSQL_TYPE_LONG_BLOB:  
unknown's avatar
unknown committed
4331
    if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin)
unknown's avatar
unknown committed
4332
      col.setType(NDBCOL::Blob);
unknown's avatar
unknown committed
4333
    else {
unknown's avatar
unknown committed
4334
      col.setType(NDBCOL::Text);
unknown's avatar
unknown committed
4335 4336
      col.setCharset(cs);
    }
unknown's avatar
unknown committed
4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349
    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
4350 4351
  case MYSQL_TYPE_BIT:
  {
unknown's avatar
unknown committed
4352
    int no_of_bits= field->field_length;
4353 4354 4355 4356 4357 4358 4359
    col.setType(NDBCOL::Bit);
    if (!no_of_bits)
      col.setLength(1);
      else
        col.setLength(no_of_bits);
    break;
  }
unknown's avatar
unknown committed
4360 4361 4362 4363 4364
  case MYSQL_TYPE_NULL:        
    goto mysql_type_unsupported;
  mysql_type_unsupported:
  default:
    return HA_ERR_UNSUPPORTED;
unknown's avatar
unknown committed
4365
  }
unknown's avatar
unknown committed
4366 4367 4368 4369 4370 4371 4372 4373
  // Set nullable and pk
  col.setNullable(field->maybe_null());
  col.setPrimaryKey(field->flags & PRI_KEY_FLAG);
  // Set autoincrement
  if (field->flags & AUTO_INCREMENT_FLAG) 
  {
    col.setAutoIncrement(TRUE);
    ulonglong value= info->auto_increment_value ?
4374
      info->auto_increment_value : (ulonglong) 1;
unknown's avatar
unknown committed
4375 4376
    DBUG_PRINT("info", ("Autoincrement key, initial: %llu", value));
    col.setAutoIncrementInitialValue(value);
unknown's avatar
unknown committed
4377
  }
unknown's avatar
unknown committed
4378
  else
unknown's avatar
unknown committed
4379
    col.setAutoIncrement(FALSE);
unknown's avatar
unknown committed
4380
  return 0;
unknown's avatar
unknown committed
4381 4382
}

unknown's avatar
unknown committed
4383 4384 4385 4386
/*
  Create a table in NDB Cluster
*/

unknown's avatar
unknown committed
4387
int ha_ndbcluster::create(const char *name, 
4388 4389
                          TABLE *form, 
                          HA_CREATE_INFO *info)
unknown's avatar
unknown committed
4390 4391 4392
{
  NDBTAB tab;
  NDBCOL col;
unknown's avatar
unknown committed
4393
  uint pack_length, length, i, pk_length= 0;
unknown's avatar
unknown committed
4394
  const void *data, *pack_data;
4395
  bool create_from_engine= (info->table_options & HA_OPTION_CREATE_FROM_ENGINE);
4396

unknown's avatar
unknown committed
4397
  DBUG_ENTER("ha_ndbcluster::create");
unknown's avatar
unknown committed
4398
  DBUG_PRINT("enter", ("name: %s", name));
unknown's avatar
unknown committed
4399

4400 4401 4402
  DBUG_ASSERT(*fn_rext((char*)name) == 0);
  set_dbname(name);
  set_tabname(name);
4403

unknown's avatar
unknown committed
4404
  table= form;
4405 4406 4407
  if (create_from_engine)
  {
    /*
unknown's avatar
unknown committed
4408
      Table already exists in NDB and frm file has been created by 
4409 4410 4411
      caller.
      Do Ndb specific stuff, such as create a .ndb file
    */
unknown's avatar
unknown committed
4412
    if ((my_errno= write_ndb_file(name)))
unknown's avatar
unknown committed
4413
      DBUG_RETURN(my_errno);
unknown's avatar
unknown committed
4414
#ifdef HAVE_NDB_BINLOG
4415
    ndbcluster_create_binlog_setup(get_ndb(), name, strlen(name),
4416
                                   m_dbname, m_tabname, FALSE);
unknown's avatar
unknown committed
4417
#endif /* HAVE_NDB_BINLOG */
4418 4419
    DBUG_RETURN(my_errno);
  }
unknown's avatar
unknown committed
4420

4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435
#ifdef HAVE_NDB_BINLOG
  /*
    Don't allow table creation unless
    schema distribution table is setup
    ( unless it is a creation of the schema dist table itself )
  */
  if (!schema_share &&
      !(strcmp(m_dbname, NDB_REP_DB) == 0 &&
        strcmp(m_tabname, NDB_SCHEMA_TABLE) == 0))
  {
    DBUG_PRINT("info", ("Schema distribution table not setup"));
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
  }
#endif /* HAVE_NDB_BINLOG */

unknown's avatar
unknown committed
4436 4437 4438 4439 4440 4441 4442 4443
  DBUG_PRINT("table", ("name: %s", m_tabname));  
  tab.setName(m_tabname);
  tab.setLogging(!(info->options & HA_LEX_CREATE_TMP_TABLE));    
   
  // Save frm data for this table
  if (readfrm(name, &data, &length))
    DBUG_RETURN(1);
  if (packfrm(data, length, &pack_data, &pack_length))
4444 4445
  {
    my_free((char*)data, MYF(0));
unknown's avatar
unknown committed
4446
    DBUG_RETURN(2);
4447 4448
  }

unknown's avatar
unknown committed
4449
  DBUG_PRINT("info", ("setFrm data=%lx  len=%d", pack_data, pack_length));
unknown's avatar
unknown committed
4450 4451 4452 4453
  tab.setFrm(pack_data, pack_length);      
  my_free((char*)data, MYF(0));
  my_free((char*)pack_data, MYF(0));
  
4454
  for (i= 0; i < form->s->fields; i++) 
unknown's avatar
unknown committed
4455 4456 4457 4458
  {
    Field *field= form->field[i];
    DBUG_PRINT("info", ("name: %s, type: %u, pack_length: %d", 
                        field->field_name, field->real_type(),
4459
                        field->pack_length()));
4460
    if ((my_errno= create_ndb_column(col, field, info)))
unknown's avatar
unknown committed
4461
      DBUG_RETURN(my_errno);
unknown's avatar
unknown committed
4462 4463
 
    if (info->store_on_disk || getenv("NDB_DEFAULT_DISK"))
unknown's avatar
unknown committed
4464 4465 4466 4467
      col.setStorageType(NdbDictionary::Column::StorageTypeDisk);
    else
      col.setStorageType(NdbDictionary::Column::StorageTypeMemory);

unknown's avatar
unknown committed
4468
    tab.addColumn(col);
unknown's avatar
unknown committed
4469
    if (col.getPrimaryKey())
unknown's avatar
unknown committed
4470
      pk_length += (field->pack_length() + 3) / 4;
unknown's avatar
unknown committed
4471
  }
unknown's avatar
unknown committed
4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487

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

  if (info->store_on_disk)
    if (info->tablespace)
      tab.setTablespace(info->tablespace);
    else
      tab.setTablespace("DEFAULT-TS");
unknown's avatar
unknown committed
4488
  // No primary key, create shadow key as 64 bit, auto increment  
4489
  if (form->s->primary_key == MAX_KEY) 
unknown's avatar
unknown committed
4490 4491 4492 4493 4494
  {
    DBUG_PRINT("info", ("Generating shadow key"));
    col.setName("$PK");
    col.setType(NdbDictionary::Column::Bigunsigned);
    col.setLength(1);
unknown's avatar
unknown committed
4495
    col.setNullable(FALSE);
unknown's avatar
unknown committed
4496 4497 4498
    col.setPrimaryKey(TRUE);
    col.setAutoIncrement(TRUE);
    tab.addColumn(col);
unknown's avatar
unknown committed
4499 4500
    pk_length += 2;
  }
4501
 
unknown's avatar
unknown committed
4502
  // Make sure that blob tables don't have to big part size
4503
  for (i= 0; i < form->s->fields; i++) 
unknown's avatar
unknown committed
4504 4505 4506 4507 4508 4509 4510
  {
    /**
     * 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()) {
4511
    case MYSQL_TYPE_GEOMETRY:
unknown's avatar
unknown committed
4512 4513 4514 4515
    case MYSQL_TYPE_BLOB:    
    case MYSQL_TYPE_MEDIUM_BLOB:   
    case MYSQL_TYPE_LONG_BLOB: 
    {
4516 4517
      NdbDictionary::Column * col= tab.getColumn(i);
      int size= pk_length + (col->getPartSize()+3)/4 + 7;
unknown's avatar
unknown committed
4518
      if (size > NDB_MAX_TUPLE_SIZE_IN_WORDS && 
4519
         (pk_length+7) < NDB_MAX_TUPLE_SIZE_IN_WORDS)
unknown's avatar
unknown committed
4520
      {
4521 4522
        size= NDB_MAX_TUPLE_SIZE_IN_WORDS - pk_length - 7;
        col->setPartSize(4*size);
unknown's avatar
unknown committed
4523 4524 4525 4526 4527 4528 4529 4530 4531 4532
      }
      /**
       * 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
4533
  }
unknown's avatar
Merge  
unknown committed
4534

4535
  // Check partition info
unknown's avatar
unknown committed
4536
  partition_info *part_info= form->part_info;
unknown's avatar
unknown committed
4537
  if ((my_errno= set_up_partition_info(part_info, form, (void*)&tab)))
4538
  {
unknown's avatar
unknown committed
4539
    DBUG_RETURN(my_errno);
4540 4541
  }

4542
  if ((my_errno= check_ndb_connection()))
unknown's avatar
unknown committed
4543 4544 4545
    DBUG_RETURN(my_errno);
  
  // Create the table in NDB     
4546 4547
  Ndb *ndb= get_ndb();
  NDBDICT *dict= ndb->getDictionary();
4548
  if (dict->createTable(tab) != 0) 
unknown's avatar
unknown committed
4549 4550 4551 4552 4553 4554
  {
    const NdbError err= dict->getNdbError();
    ERR_PRINT(err);
    my_errno= ndb_to_mysql_error(&err);
    DBUG_RETURN(my_errno);
  }
4555 4556 4557 4558 4559 4560 4561 4562

  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...
  DBUG_ASSERT(m_table != 0);

unknown's avatar
unknown committed
4563 4564
  DBUG_PRINT("info", ("Table %s/%s created successfully", 
                      m_dbname, m_tabname));
4565

unknown's avatar
unknown committed
4566
  // Create secondary indexes
4567
  my_errno= create_indexes(ndb, form);
4568

4569
  if (!my_errno)
unknown's avatar
unknown committed
4570
    my_errno= write_ndb_file(name);
4571 4572 4573 4574 4575 4576
  else
  {
    /*
      Failed to create an index,
      drop the table (and all it's indexes)
    */
4577 4578 4579 4580
    if (dict->dropTableGlobal(*m_table) == 0)
    {
      m_table = 0;
    }
4581
  }
4582

unknown's avatar
unknown committed
4583 4584 4585 4586 4587 4588 4589 4590 4591
#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...
    */
    {
4592
      uint length= (uint) strlen(name);
unknown's avatar
unknown committed
4593
      if ((share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
4594
                                           (byte*) name, length)))
unknown's avatar
unknown committed
4595 4596 4597 4598 4599
        handle_trailing_share(share);
    }
    /*
      get a new share
    */
4600 4601

    if (!(share= get_share(name, form, true, true)))
unknown's avatar
unknown committed
4602
    {
4603
      sql_print_error("NDB: allocating table share for %s failed", name);
unknown's avatar
unknown committed
4604 4605 4606 4607 4608 4609 4610 4611
      /* my_errno is set */
    }
    pthread_mutex_unlock(&ndbcluster_mutex);

    while (!IS_TMP_PREFIX(m_tabname))
    {
      String event_name(INJECTOR_EVENT_LEN);
      ndb_rep_event_name(&event_name,m_dbname,m_tabname);
4612 4613 4614 4615 4616 4617
      int do_event_op= ndb_binlog_running;

      if (!schema_share &&
          strcmp(share->db, NDB_REP_DB) == 0 &&
          strcmp(share->table_name, NDB_SCHEMA_TABLE) == 0)
        do_event_op= 1;
unknown's avatar
unknown committed
4618 4619 4620 4621 4622

      /*
        Always create an event for the table, as other mysql servers
        expect it to be there.
      */
4623
      if (!ndbcluster_create_event(ndb, m_table, event_name.c_ptr(), share,
unknown's avatar
unknown committed
4624
                                   share && do_event_op /* push warning */))
unknown's avatar
unknown committed
4625
      {
unknown's avatar
unknown committed
4626 4627 4628 4629
        if (ndb_extra_logging)
          sql_print_information("NDB Binlog: CREATE TABLE Event: %s",
                                event_name.c_ptr());
        if (share && do_event_op &&
4630
            ndbcluster_create_event_ops(share, m_table, event_name.c_ptr()))
unknown's avatar
unknown committed
4631 4632 4633 4634 4635
        {
          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
4636
      }
unknown's avatar
unknown committed
4637 4638 4639 4640
      /*
        warning has been issued if ndbcluster_create_event failed
        and (share && do_event_op)
      */
4641
      if (share && !do_event_op)
4642
        share->flags|= NSF_NO_BINLOG;
unknown's avatar
unknown committed
4643 4644 4645
      ndbcluster_log_schema_op(current_thd, share,
                               current_thd->query, current_thd->query_length,
                               share->db, share->table_name,
4646 4647
                               m_table->getObjectId(),
                               m_table->getObjectVersion(),
unknown's avatar
unknown committed
4648 4649 4650 4651 4652 4653
                               SOT_CREATE_TABLE);
      break;
    }
  }
#endif /* HAVE_NDB_BINLOG */

4654
  m_table= 0;
unknown's avatar
unknown committed
4655 4656 4657
  DBUG_RETURN(my_errno);
}

4658 4659
int ha_ndbcluster::create_handler_files(const char *file,
                                        const char *old_name,
4660 4661
                                        int action_flag,
                                        HA_CREATE_INFO *info) 
unknown's avatar
unknown committed
4662
{ 
4663
  char path[FN_REFLEN];
unknown's avatar
unknown committed
4664 4665 4666 4667 4668 4669 4670 4671 4672
  const char *name;
  Ndb* ndb;
  const NDBTAB *tab;
  const void *data, *pack_data;
  uint length, pack_length;
  int error= 0;

  DBUG_ENTER("create_handler_files");

4673
  if (action_flag != CHF_INDEX_FLAG)
4674 4675 4676
  {
    DBUG_RETURN(FALSE);
  }
4677
  DBUG_PRINT("enter", ("file: %s", file));
unknown's avatar
unknown committed
4678 4679 4680 4681
  if (!(ndb= get_ndb()))
    DBUG_RETURN(HA_ERR_NO_CONNECTION);

  NDBDICT *dict= ndb->getDictionary();
4682
  if (!info->frm_only)
unknown's avatar
unknown committed
4683
    DBUG_RETURN(0); // Must be a create, ignore since frm is saved in create
4684 4685 4686 4687

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

4688 4689
  set_dbname(file);
  set_tabname(file);
4690
  Ndb_table_guard ndbtab_g(dict, m_tabname);
4691
  DBUG_PRINT("info", ("m_dbname: %s, m_tabname: %s", m_dbname, m_tabname));
4692
  if (!(tab= ndbtab_g.get_table()))
4693 4694
    DBUG_RETURN(0); // Unkown table, must be temporary table

4695
  DBUG_ASSERT(get_ndb_share_state(m_share) == NSS_ALTERED);
4696
  if (readfrm(file, &data, &length) ||
unknown's avatar
unknown committed
4697 4698 4699 4700 4701
      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));
4702
    error= 1;
unknown's avatar
unknown committed
4703
  }
4704 4705
  else
  {
unknown's avatar
unknown committed
4706 4707
    DBUG_PRINT("info", ("Table %s has changed, altering frm in ndb", 
                        m_tabname));
4708 4709 4710 4711 4712 4713
    NdbDictionary::Table new_tab= *tab;
    new_tab.setFrm(pack_data, pack_length);
    if (dict->alterTableGlobal(*tab, new_tab))
    {
      error= ndb_to_mysql_error(&dict->getNdbError());
    }
4714 4715
    my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
    my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR));
unknown's avatar
unknown committed
4716
  }
4717
  
4718
  set_ndb_share_state(m_share, NSS_INITIAL);
4719
  free_share(&m_share); // Decrease ref_count
unknown's avatar
unknown committed
4720 4721 4722 4723

  DBUG_RETURN(error);
}

4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764
int ha_ndbcluster::create_index(const char *name, KEY *key_info, 
                                NDB_INDEX_TYPE idx_type, uint idx_no)
{
  int error= 0;
  char unique_name[FN_LEN];
  static const char* unique_suffix= "$unique";
  DBUG_ENTER("ha_ndbcluster::create_ordered_index");
  DBUG_PRINT("info", ("Creating index %u: %s", idx_no, name));  

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

4766
int ha_ndbcluster::create_ordered_index(const char *name, 
4767
                                        KEY *key_info)
4768
{
4769
  DBUG_ENTER("ha_ndbcluster::create_ordered_index");
4770
  DBUG_RETURN(create_ndb_index(name, key_info, FALSE));
4771 4772 4773
}

int ha_ndbcluster::create_unique_index(const char *name, 
4774
                                       KEY *key_info)
4775 4776
{

4777
  DBUG_ENTER("ha_ndbcluster::create_unique_index");
4778
  DBUG_RETURN(create_ndb_index(name, key_info, TRUE));
4779 4780 4781
}


unknown's avatar
unknown committed
4782 4783 4784 4785
/*
  Create an index in NDB Cluster
 */

4786 4787 4788
int ha_ndbcluster::create_ndb_index(const char *name, 
                                     KEY *key_info,
                                     bool unique)
4789
{
4790 4791
  Ndb *ndb= get_ndb();
  NdbDictionary::Dictionary *dict= ndb->getDictionary();
unknown's avatar
unknown committed
4792 4793 4794
  KEY_PART_INFO *key_part= key_info->key_part;
  KEY_PART_INFO *end= key_part + key_info->key_parts;
  
4795
  DBUG_ENTER("ha_ndbcluster::create_index");
unknown's avatar
unknown committed
4796
  DBUG_PRINT("enter", ("name: %s ", name));
4797

unknown's avatar
unknown committed
4798
  NdbDictionary::Index ndb_index(name);
4799
  if (unique)
unknown's avatar
unknown committed
4800 4801 4802 4803 4804
    ndb_index.setType(NdbDictionary::Index::UniqueHashIndex);
  else 
  {
    ndb_index.setType(NdbDictionary::Index::OrderedIndex);
    // TODO Only temporary ordered indexes supported
unknown's avatar
unknown committed
4805
    ndb_index.setLogging(FALSE); 
unknown's avatar
unknown committed
4806 4807 4808 4809 4810 4811 4812
  }
  ndb_index.setTable(m_tabname);

  for (; key_part != end; key_part++) 
  {
    Field *field= key_part->field;
    DBUG_PRINT("info", ("attr: %s", field->field_name));
unknown's avatar
unknown committed
4813
    ndb_index.addColumnName(field->field_name);
unknown's avatar
unknown committed
4814 4815
  }
  
4816
  if (dict->createIndex(ndb_index, *m_table))
unknown's avatar
unknown committed
4817 4818 4819 4820 4821 4822 4823
    ERR_RETURN(dict->getNdbError());

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

4824 4825 4826 4827 4828 4829 4830 4831 4832
/*
 Prepare for an on-line alter table
*/ 
void ha_ndbcluster::prepare_for_alter()
{
  ndbcluster_get_share(m_share); // Increase ref_count
  set_ndb_share_state(m_share, NSS_ALTERED);
}

4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844
/*
  Add an index on-line to a table
*/
int ha_ndbcluster::add_index(TABLE *table_arg, 
                             KEY *key_info, uint num_of_keys)
{
  DBUG_ENTER("ha_ndbcluster::add_index");
  DBUG_PRINT("info", ("ha_ndbcluster::add_index to table %s", 
                      table_arg->s->table_name));
  int error= 0;
  uint idx;

4845
  DBUG_ASSERT(m_share->state == NSS_ALTERED);
4846 4847 4848 4849 4850
  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;
4851
    NDB_INDEX_TYPE idx_type= get_index_type_from_key(idx, key, false);
4852 4853 4854 4855 4856 4857 4858 4859 4860
    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;
  }
4861
  if (error)
4862
  {
4863 4864
    set_ndb_share_state(m_share, NSS_INITIAL);
    free_share(&m_share); // Decrease ref_count
4865
  }
4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876
  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");
4877
  DBUG_ASSERT(m_share->state == NSS_ALTERED);
4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888
  // 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;
4889 4890
  renumber_indexes(ndb, table_arg);
  DBUG_RETURN(0);
4891 4892 4893 4894 4895 4896 4897
}
 
/*
  Really drop all indexes marked for deletion
*/
int ha_ndbcluster::final_drop_index(TABLE *table_arg)
{
4898
  int error;
4899 4900 4901 4902 4903 4904
  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;
4905 4906 4907 4908 4909 4910
  if((error= drop_indexes(ndb, table_arg)))
  {
    m_share->state= NSS_INITIAL;
    free_share(&m_share); // Decrease ref_count
  }
  DBUG_RETURN(error);
4911 4912
}

unknown's avatar
unknown committed
4913 4914 4915 4916 4917 4918
/*
  Rename a table in NDB Cluster
*/

int ha_ndbcluster::rename_table(const char *from, const char *to)
{
4919
  NDBDICT *dict;
4920
  char old_dbname[FN_HEADLEN];
unknown's avatar
unknown committed
4921
  char new_tabname[FN_HEADLEN];
4922 4923
  const NDBTAB *orig_tab;
  int result;
unknown's avatar
unknown committed
4924 4925

  DBUG_ENTER("ha_ndbcluster::rename_table");
4926
  DBUG_PRINT("info", ("Renaming %s to %s", from, to));
4927
  set_dbname(from, old_dbname);
unknown's avatar
unknown committed
4928 4929 4930
  set_tabname(from);
  set_tabname(to, new_tabname);

4931 4932 4933
  if (check_ndb_connection())
    DBUG_RETURN(my_errno= HA_ERR_NO_CONNECTION);

unknown's avatar
unknown committed
4934
  Ndb *ndb= get_ndb();
4935
  ndb->setDatabaseName(old_dbname);
unknown's avatar
unknown committed
4936
  dict= ndb->getDictionary();
4937 4938
  Ndb_table_guard ndbtab_g(dict, m_tabname);
  if (!(orig_tab= ndbtab_g.get_table()))
4939
    ERR_RETURN(dict->getNdbError());
4940

unknown's avatar
unknown committed
4941
#ifdef HAVE_NDB_BINLOG
4942 4943 4944
  int ndb_table_id= orig_tab->getObjectId();
  int ndb_table_version= orig_tab->getObjectVersion();

4945 4946
  NDB_SHARE *share= get_share(from, 0, false);
  if (share)
unknown's avatar
unknown committed
4947 4948 4949 4950 4951
  {
    int r= rename_share(share, to);
    DBUG_ASSERT(r == 0);
  }
#endif
4952 4953
  // Change current database to that of target table
  set_dbname(to);
unknown's avatar
unknown committed
4954
  ndb->setDatabaseName(m_dbname);
unknown's avatar
unknown committed
4955

4956 4957 4958
  NdbDictionary::Table new_tab= *orig_tab;
  new_tab.setName(new_tabname);
  if (dict->alterTableGlobal(*orig_tab, new_tab) != 0)
unknown's avatar
unknown committed
4959
  {
4960
    NdbError ndb_error= dict->getNdbError();
unknown's avatar
unknown committed
4961 4962 4963 4964 4965 4966 4967 4968
#ifdef HAVE_NDB_BINLOG
    if (share)
    {
      int r= rename_share(share, from);
      DBUG_ASSERT(r == 0);
      free_share(&share);
    }
#endif
4969
    ERR_RETURN(ndb_error);
unknown's avatar
unknown committed
4970 4971 4972 4973
  }
  
  // Rename .ndb file
  if ((result= handler::rename_table(from, to)))
4974
  {
unknown's avatar
unknown committed
4975
    // ToDo in 4.1 should rollback alter table...
unknown's avatar
unknown committed
4976 4977 4978 4979
#ifdef HAVE_NDB_BINLOG
    if (share)
      free_share(&share);
#endif
unknown's avatar
unknown committed
4980
    DBUG_RETURN(result);
4981
  }
4982

unknown's avatar
unknown committed
4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993
#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);
4994 4995
    ndbcluster_handle_drop_table(ndb, event_name.c_ptr(), share,
                                 "rename table");
unknown's avatar
unknown committed
4996 4997 4998 4999 5000 5001 5002
  }

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

unknown's avatar
unknown committed
5006 5007
    if (!ndbcluster_create_event(ndb, ndbtab, event_name.c_ptr(), share,
                                 share && ndb_binlog_running /* push warning */))
unknown's avatar
unknown committed
5008 5009 5010 5011
    {
      if (ndb_extra_logging)
        sql_print_information("NDB Binlog: RENAME Event: %s",
                              event_name.c_ptr());
unknown's avatar
unknown committed
5012 5013
      if (share && ndb_binlog_running &&
          ndbcluster_create_event_ops(share, ndbtab, event_name.c_ptr()))
unknown's avatar
unknown committed
5014
      {
unknown's avatar
unknown committed
5015 5016 5017
        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
5018 5019
      }
    }
unknown's avatar
unknown committed
5020 5021 5022 5023
    /*
      warning has been issued if ndbcluster_create_event failed
      and (share && ndb_binlog_running)
    */
5024
    if (!is_old_table_tmpfile)
unknown's avatar
unknown committed
5025 5026
      ndbcluster_log_schema_op(current_thd, share,
                               current_thd->query, current_thd->query_length,
5027 5028
                               old_dbname, m_tabname,
                               ndb_table_id, ndb_table_version,
5029
                               SOT_RENAME_TABLE,
5030
                               m_dbname, new_tabname);
unknown's avatar
unknown committed
5031 5032 5033 5034 5035
  }
  if (share)
    free_share(&share);
#endif

unknown's avatar
unknown committed
5036 5037 5038 5039 5040
  DBUG_RETURN(result);
}


/*
5041 5042
  Delete table from NDB Cluster

unknown's avatar
unknown committed
5043 5044
 */

unknown's avatar
unknown committed
5045 5046 5047 5048 5049 5050 5051 5052 5053 5054
/* static version which does not need a handler */

int
ha_ndbcluster::delete_table(ha_ndbcluster *h, Ndb *ndb,
                            const char *path,
                            const char *db,
                            const char *table_name)
{
  DBUG_ENTER("ha_ndbcluster::ndbcluster_delete_table");
  NDBDICT *dict= ndb->getDictionary();
unknown's avatar
unknown committed
5055
#ifdef HAVE_NDB_BINLOG
5056 5057
  int ndb_table_id= 0;
  int ndb_table_version= 0;
5058 5059 5060 5061 5062 5063 5064 5065 5066
  /*
    Don't allow drop table unless
    schema distribution table is setup
  */
  if (!schema_share)
  {
    DBUG_PRINT("info", ("Schema distribution table not setup"));
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
  }
unknown's avatar
unknown committed
5067 5068
  NDB_SHARE *share= get_share(path, 0, false);
#endif
unknown's avatar
unknown committed
5069 5070 5071 5072

  /* Drop the table from NDB */
  
  int res;
5073
  if (h && h->m_table)
unknown's avatar
unknown committed
5074
  {
5075 5076 5077 5078 5079 5080 5081 5082 5083 5084
    if (dict->dropTableGlobal(*h->m_table))
      res= ndb_to_mysql_error(&dict->getNdbError());
#ifdef HAVE_NDB_BINLOG
    if (res == 0)
    {
      ndb_table_id= h->m_table->getObjectId();
      ndb_table_version= h->m_table->getObjectVersion();
    }
#endif
    h->release_metadata(current_thd, ndb);
unknown's avatar
unknown committed
5085 5086 5087 5088
  }
  else
  {
    ndb->setDatabaseName(db);
5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111
    while (1)
    {
      Ndb_table_guard ndbtab_g(dict, table_name);
      if (ndbtab_g.get_table())
      {
        if (dict->dropTableGlobal(*ndbtab_g.get_table()) == 0)
        {
#ifdef HAVE_NDB_BINLOG
          ndb_table_id= ndbtab_g.get_table()->getObjectId();
          ndb_table_version= ndbtab_g.get_table()->getObjectVersion();
#endif
          res= 0;
        }
        else if (dict->getNdbError().code == NDB_INVALID_SCHEMA_OBJECT)
        {
          ndbtab_g.invalidate();
          continue;
        }
      }
      else
        res= ndb_to_mysql_error(&dict->getNdbError());
      break;
    }
unknown's avatar
unknown committed
5112 5113 5114 5115
  }

  if (res)
  {
unknown's avatar
unknown committed
5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133
#ifdef HAVE_NDB_BINLOG
    /* the drop table failed for some reason, drop the share anyways */
    if (share)
    {
      pthread_mutex_lock(&ndbcluster_mutex);
      if (share->state != NSS_DROPPED)
      {
        /*
          The share kept by the server has not been freed, free it
        */
        share->state= NSS_DROPPED;
        free_share(&share, TRUE);
      }
      /* free the share taken above */
      free_share(&share, TRUE);
      pthread_mutex_unlock(&ndbcluster_mutex);
    }
#endif
unknown's avatar
unknown committed
5134 5135 5136
    DBUG_RETURN(res);
  }

unknown's avatar
unknown committed
5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152
#ifdef HAVE_NDB_BINLOG
  /* stop the logging of the dropped table, and cleanup */

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

  if (!IS_TMP_PREFIX(table_name) && share)
  {
    ndbcluster_log_schema_op(current_thd, share,
                             current_thd->query, current_thd->query_length,
                             share->db, share->table_name,
5153
                             ndb_table_id, ndb_table_version,
unknown's avatar
unknown committed
5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165
                             SOT_DROP_TABLE);
  }
  else if (table_dropped && share && share->op) /* ndbcluster_log_schema_op
                                                   will do a force GCP */
    dict->forceGCPWait();

  if (!IS_TMP_PREFIX(table_name))
  {
    String event_name(INJECTOR_EVENT_LEN);
    ndb_rep_event_name(&event_name, path + sizeof(share_prefix) - 1, 0);
    ndbcluster_handle_drop_table(ndb,
                                 table_dropped ? event_name.c_ptr() : 0,
5166
                                 share, "delete table");
unknown's avatar
unknown committed
5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184
  }

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

unknown's avatar
unknown committed
5188 5189
int ha_ndbcluster::delete_table(const char *name)
{
5190
  DBUG_ENTER("ha_ndbcluster::delete_table");
unknown's avatar
unknown committed
5191 5192 5193
  DBUG_PRINT("enter", ("name: %s", name));
  set_dbname(name);
  set_tabname(name);
5194

5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206
#ifdef HAVE_NDB_BINLOG
  /*
    Don't allow drop table unless
    schema distribution table is setup
  */
  if (!schema_share)
  {
    DBUG_PRINT("info", ("Schema distribution table not setup"));
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
  }
#endif

unknown's avatar
unknown committed
5207 5208
  if (check_ndb_connection())
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
5209 5210

  /* Call ancestor function to delete .ndb file */
5211
  handler::delete_table(name);
unknown's avatar
unknown committed
5212 5213

  DBUG_RETURN(delete_table(this, get_ndb(),name, m_dbname, m_tabname));
unknown's avatar
unknown committed
5214 5215 5216
}


5217
ulonglong ha_ndbcluster::get_auto_increment()
5218
{  
5219 5220
  int cache_size;
  Uint64 auto_value;
unknown's avatar
unknown committed
5221 5222
  DBUG_ENTER("get_auto_increment");
  DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));
5223
  Ndb *ndb= get_ndb();
5224
   
5225
  if (m_rows_inserted > m_rows_to_insert)
unknown's avatar
unknown committed
5226
  {
5227 5228
    /* We guessed too low */
    m_rows_to_insert+= m_autoincrement_prefetch;
unknown's avatar
unknown committed
5229
  }
unknown's avatar
unknown committed
5230
  cache_size= 
unknown's avatar
unknown committed
5231 5232 5233 5234
    (int) ((m_rows_to_insert - m_rows_inserted < m_autoincrement_prefetch) ?
           m_rows_to_insert - m_rows_inserted :
           ((m_rows_to_insert > m_autoincrement_prefetch) ?
            m_rows_to_insert : m_autoincrement_prefetch));
unknown's avatar
unknown committed
5235
  auto_value= NDB_FAILED_AUTO_INCREMENT;
5236 5237 5238 5239
  uint retries= NDB_AUTO_INCREMENT_RETRIES;
  do {
    auto_value=
      (m_skip_auto_increment) ? 
5240 5241
      ndb->readAutoIncrementValue(m_table)
      : ndb->getAutoIncrementValue(m_table, cache_size);
5242 5243 5244
  } while (auto_value == NDB_FAILED_AUTO_INCREMENT && 
           --retries &&
           ndb->getNdbError().status == NdbError::TemporaryError);
5245
  if (auto_value == NDB_FAILED_AUTO_INCREMENT)
5246 5247 5248 5249 5250 5251
  {
    const NdbError err= ndb->getNdbError();
    sql_print_error("Error %lu in ::get_auto_increment(): %s",
                    (ulong) err.code, err.message);
    DBUG_RETURN(~(ulonglong) 0);
  }
unknown's avatar
unknown committed
5252
  DBUG_RETURN((longlong)auto_value);
unknown's avatar
unknown committed
5253 5254 5255 5256 5257 5258 5259
}


/*
  Constructor for the NDB Cluster table handler 
 */

unknown's avatar
unknown committed
5260 5261 5262 5263 5264 5265 5266
#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
5267 5268
                HA_CAN_BIT_FIELD | \
                HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
unknown's avatar
unknown committed
5269

unknown's avatar
unknown committed
5270
ha_ndbcluster::ha_ndbcluster(TABLE_SHARE *table_arg):
5271
  handler(&ndbcluster_hton, table_arg),
unknown's avatar
unknown committed
5272 5273 5274
  m_active_trans(NULL),
  m_active_cursor(NULL),
  m_table(NULL),
5275
  m_table_info(NULL),
unknown's avatar
unknown committed
5276
  m_table_flags(HA_NDBCLUSTER_TABLE_FLAGS),
5277
  m_share(0),
5278 5279 5280
  m_part_info(NULL),
  m_use_partition_function(FALSE),
  m_sorted(FALSE),
unknown's avatar
unknown committed
5281
  m_use_write(FALSE),
5282
  m_ignore_dup_key(FALSE),
5283
  m_has_unique_index(FALSE),
5284
  m_primary_key_update(FALSE),
unknown's avatar
unknown committed
5285
  m_ignore_no_key(FALSE),
5286 5287 5288
  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
5289
  m_rows_changed((ha_rows) 0),
5290 5291 5292 5293 5294 5295
  m_bulk_insert_not_flushed(FALSE),
  m_ops_pending(0),
  m_skip_auto_increment(TRUE),
  m_blobs_pending(0),
  m_blobs_buffer(0),
  m_blobs_buffer_size(0),
5296 5297 5298
  m_dupkey((uint) -1),
  m_ha_not_exact_count(FALSE),
  m_force_send(TRUE),
5299
  m_autoincrement_prefetch((ha_rows) 32),
unknown's avatar
unknown committed
5300
  m_transaction_on(TRUE),
unknown's avatar
unknown committed
5301 5302
  m_cond_stack(NULL),
  m_multi_cursor(NULL)
5303
{
5304
  int i;
5305
 
unknown's avatar
unknown committed
5306 5307 5308 5309 5310
  DBUG_ENTER("ha_ndbcluster");

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

5311
  records= ~(ha_rows)0; // uninitialized
unknown's avatar
unknown committed
5312 5313
  block_size= 1024;

unknown's avatar
ndb:  
unknown committed
5314 5315
  for (i= 0; i < MAX_KEY; i++)
    ndb_init_index(m_index[i]);
5316

unknown's avatar
unknown committed
5317 5318 5319 5320
  DBUG_VOID_RETURN;
}


5321 5322 5323 5324 5325 5326 5327 5328 5329 5330
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
5331 5332 5333 5334 5335 5336
/*
  Destructor for NDB Cluster table handler
 */

ha_ndbcluster::~ha_ndbcluster() 
{
5337 5338
  THD *thd= current_thd;
  Ndb *ndb= thd ? check_ndb_in_thd(thd) : g_ndb;
unknown's avatar
unknown committed
5339 5340
  DBUG_ENTER("~ha_ndbcluster");

5341
  if (m_share)
unknown's avatar
unknown committed
5342 5343 5344
  {
    free_share(&m_share);
  }
5345
  release_metadata(thd, ndb);
5346 5347
  my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
  m_blobs_buffer= 0;
unknown's avatar
unknown committed
5348 5349

  // Check for open cursor/transaction
5350 5351
  if (m_active_cursor) {
  }
unknown's avatar
unknown committed
5352
  DBUG_ASSERT(m_active_cursor == NULL);
5353 5354
  if (m_active_trans) {
  }
unknown's avatar
unknown committed
5355 5356
  DBUG_ASSERT(m_active_trans == NULL);

5357 5358 5359 5360
  // Discard the condition stack
  DBUG_PRINT("info", ("Clearing condition stack"));
  cond_clear();

unknown's avatar
unknown committed
5361 5362 5363 5364
  DBUG_VOID_RETURN;
}


unknown's avatar
Merge  
unknown committed
5365

unknown's avatar
unknown committed
5366 5367 5368 5369
/*
  Open a table for further use
  - fetch metadata for this table from NDB
  - check that table exists
unknown's avatar
unknown committed
5370 5371 5372 5373

  RETURN
    0    ok
    < 0  Table has changed
unknown's avatar
unknown committed
5374 5375 5376 5377
*/

int ha_ndbcluster::open(const char *name, int mode, uint test_if_locked)
{
unknown's avatar
unknown committed
5378
  int res;
unknown's avatar
unknown committed
5379
  KEY *key;
unknown's avatar
unknown committed
5380 5381 5382
  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
5383
  
unknown's avatar
unknown committed
5384 5385 5386 5387
  /*
    Setup ref_length to make room for the whole 
    primary key to be written in the ref variable
  */
unknown's avatar
unknown committed
5388
  
unknown's avatar
unknown committed
5389
  if (table_share->primary_key != MAX_KEY) 
unknown's avatar
unknown committed
5390
  {
unknown's avatar
unknown committed
5391
    key= table->key_info+table_share->primary_key;
unknown's avatar
unknown committed
5392 5393
    ref_length= key->key_length;
  }
5394 5395 5396 5397 5398 5399 5400 5401 5402 5403
  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
5404
  // Init table lock structure 
unknown's avatar
unknown committed
5405
  if (!(m_share=get_share(name, table)))
unknown's avatar
unknown committed
5406 5407 5408 5409 5410 5411
    DBUG_RETURN(1);
  thr_lock_data_init(&m_share->lock,&m_lock,(void*) 0);
  
  set_dbname(name);
  set_tabname(name);
  
5412
  if (check_ndb_connection()) {
unknown's avatar
unknown committed
5413 5414
    free_share(&m_share);
    m_share= 0;
unknown's avatar
unknown committed
5415
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
5416
  }
5417
  
unknown's avatar
unknown committed
5418 5419 5420
  res= get_metadata(name);
  if (!res)
    info(HA_STATUS_VARIABLE | HA_STATUS_CONST);
unknown's avatar
unknown committed
5421

5422 5423 5424 5425 5426
#ifdef HAVE_NDB_BINLOG
  if (!ndb_binlog_tables_inited && ndb_binlog_running)
    table->db_stat|= HA_READ_ONLY;
#endif

unknown's avatar
unknown committed
5427
  DBUG_RETURN(res);
unknown's avatar
unknown committed
5428 5429
}

unknown's avatar
unknown committed
5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443
/*
  Set partition info

  SYNOPSIS
    set_part_info()
    part_info

  RETURN VALUE
    NONE

  DESCRIPTION
    Set up partition info when handler object created
*/

5444 5445 5446 5447 5448
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 &&
5449
        !m_part_info->is_sub_partitioned()))
5450 5451
    m_use_partition_function= TRUE;
}
unknown's avatar
unknown committed
5452 5453 5454 5455 5456 5457 5458 5459

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

int ha_ndbcluster::close(void)
{
5460 5461 5462
  DBUG_ENTER("close");
  THD *thd= current_thd;
  Ndb *ndb= thd ? check_ndb_in_thd(thd) : g_ndb;
unknown's avatar
unknown committed
5463 5464
  free_share(&m_share);
  m_share= 0;
5465
  release_metadata(thd, ndb);
unknown's avatar
unknown committed
5466 5467 5468 5469
  DBUG_RETURN(0);
}


5470
Thd_ndb* ha_ndbcluster::seize_thd_ndb()
unknown's avatar
unknown committed
5471
{
5472 5473
  Thd_ndb *thd_ndb;
  DBUG_ENTER("seize_thd_ndb");
unknown's avatar
unknown committed
5474

5475 5476
  thd_ndb= new Thd_ndb();
  if (thd_ndb->ndb->init(max_transactions) != 0)
unknown's avatar
unknown committed
5477
  {
5478
    ERR_PRINT(thd_ndb->ndb->getNdbError());
unknown's avatar
unknown committed
5479 5480 5481 5482 5483 5484
    /*
      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 
    */
5485 5486
    delete thd_ndb;
    thd_ndb= NULL;
unknown's avatar
unknown committed
5487
  }
5488
  DBUG_RETURN(thd_ndb);
unknown's avatar
unknown committed
5489 5490 5491
}


5492
void ha_ndbcluster::release_thd_ndb(Thd_ndb* thd_ndb)
unknown's avatar
unknown committed
5493
{
5494 5495
  DBUG_ENTER("release_thd_ndb");
  delete thd_ndb;
unknown's avatar
unknown committed
5496 5497 5498 5499 5500
  DBUG_VOID_RETURN;
}


/*
unknown's avatar
unknown committed
5501
  If this thread already has a Thd_ndb object allocated
unknown's avatar
unknown committed
5502
  in current THD, reuse it. Otherwise
unknown's avatar
unknown committed
5503
  seize a Thd_ndb object, assign it to current THD and use it.
unknown's avatar
unknown committed
5504 5505 5506
 
*/

5507
Ndb* check_ndb_in_thd(THD* thd)
unknown's avatar
unknown committed
5508
{
5509
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
5510
  if (!thd_ndb)
unknown's avatar
unknown committed
5511
  {
unknown's avatar
unknown committed
5512
    if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
unknown's avatar
Merge  
unknown committed
5513
      return NULL;
5514
    set_thd_ndb(thd, thd_ndb);
unknown's avatar
unknown committed
5515
  }
unknown's avatar
Merge  
unknown committed
5516
  return thd_ndb->ndb;
5517 5518
}

unknown's avatar
unknown committed
5519

5520

5521
int ha_ndbcluster::check_ndb_connection(THD* thd)
unknown's avatar
unknown committed
5522
{
5523
  Ndb *ndb;
unknown's avatar
unknown committed
5524 5525
  DBUG_ENTER("check_ndb_connection");
  
5526
  if (!(ndb= check_ndb_in_thd(thd)))
5527
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
5528
  ndb->setDatabaseName(m_dbname);
unknown's avatar
unknown committed
5529 5530 5531
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
5532

unknown's avatar
unknown committed
5533
static int ndbcluster_close_connection(THD *thd)
unknown's avatar
unknown committed
5534
{
5535
  Thd_ndb *thd_ndb= get_thd_ndb(thd);
unknown's avatar
unknown committed
5536
  DBUG_ENTER("ndbcluster_close_connection");
5537 5538
  if (thd_ndb)
  {
5539
    ha_ndbcluster::release_thd_ndb(thd_ndb);
5540
    set_thd_ndb(thd, NULL); // not strictly required but does not hurt either
5541
  }
5542
  DBUG_RETURN(0);
unknown's avatar
unknown committed
5543 5544 5545 5546 5547 5548 5549
}


/*
  Try to discover one table from NDB
 */

5550
int ndbcluster_discover(THD* thd, const char *db, const char *name,
5551
                        const void** frmblob, uint* frmlen)
unknown's avatar
unknown committed
5552
{
5553 5554
  int error= 0;
  NdbError ndb_error;
unknown's avatar
unknown committed
5555 5556
  uint len;
  const void* data;
5557
  Ndb* ndb;
5558
  char key[FN_REFLEN];
unknown's avatar
unknown committed
5559
  DBUG_ENTER("ndbcluster_discover");
5560
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name)); 
unknown's avatar
unknown committed
5561

5562 5563 5564 5565
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(HA_ERR_NO_CONNECTION);  
  ndb->setDatabaseName(db);
  NDBDICT* dict= ndb->getDictionary();
5566
  build_table_filename(key, sizeof(key), db, name, "");
5567
  NDB_SHARE *share= get_share(key, 0, false);
5568
  if (share && get_ndb_share_state(share) == NSS_ALTERED)
unknown's avatar
unknown committed
5569
  {
5570 5571 5572 5573
    // Frm has been altered on disk, but not yet written to ndb
    if (readfrm(key, &data, &len))
    {
      DBUG_PRINT("error", ("Could not read frm"));
5574 5575
      error= 1;
      goto err;
5576
    }
unknown's avatar
unknown committed
5577
  }
5578
  else
5579
  {
5580 5581 5582 5583
    Ndb_table_guard ndbtab_g(dict, name);
    const NDBTAB *tab= ndbtab_g.get_table();
    if (!tab)
    {
5584 5585
      const NdbError err= dict->getNdbError();
      if (err.code == 709 || err.code == 723)
5586 5587 5588 5589
        error= -1;
      else
        ndb_error= err;
      goto err;
5590 5591 5592 5593 5594 5595 5596
    }
    DBUG_PRINT("info", ("Found table %s", tab->getName()));
    
    len= tab->getFrmLength();  
    if (len == 0 || tab->getFrmData() == NULL)
    {
      DBUG_PRINT("error", ("No frm data found."));
5597 5598
      error= 1;
      goto err;
5599 5600 5601 5602 5603
    }
    
    if (unpackfrm(&data, &len, tab->getFrmData()))
    {
      DBUG_PRINT("error", ("Could not unpack table"));
5604 5605
      error= 1;
      goto err;
5606
    }
5607
  }
unknown's avatar
unknown committed
5608 5609 5610 5611

  *frmlen= len;
  *frmblob= data;
  
5612 5613 5614
  if (share)
    free_share(&share);

unknown's avatar
unknown committed
5615
  DBUG_RETURN(0);
5616 5617 5618 5619 5620 5621 5622 5623
err:
  if (share)
    free_share(&share);
  if (ndb_error.code)
  {
    ERR_RETURN(ndb_error);
  }
  DBUG_RETURN(error);
unknown's avatar
unknown committed
5624 5625 5626
}

/*
5627
  Check if a table exists in NDB
5628

5629
 */
unknown's avatar
unknown committed
5630

5631 5632
int ndbcluster_table_exists_in_engine(THD* thd, const char *db,
                                      const char *name)
5633 5634
{
  Ndb* ndb;
5635
  DBUG_ENTER("ndbcluster_table_exists_in_engine");
5636
  DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
5637 5638

  if (!(ndb= check_ndb_in_thd(thd)))
5639
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
5640 5641

  NDBDICT* dict= ndb->getDictionary();
5642 5643 5644 5645
  NdbDictionary::Dictionary::List list;
  if (dict->listObjects(list, NdbDictionary::Object::UserTable) != 0)
    ERR_RETURN(dict->getNdbError());
  for (int i= 0 ; i < list.count ; i++)
5646
  {
5647 5648 5649 5650 5651 5652 5653 5654
    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;
    // table found
    DBUG_PRINT("info", ("Found table"));
    DBUG_RETURN(1);
5655
  }
5656
  DBUG_RETURN(0);
5657 5658
}

unknown's avatar
unknown committed
5659 5660


unknown's avatar
unknown committed
5661
extern "C" byte* tables_get_key(const char *entry, uint *length,
5662
                                my_bool not_used __attribute__((unused)))
5663 5664 5665 5666 5667 5668
{
  *length= strlen(entry);
  return (byte*) entry;
}


5669 5670
/*
  Drop a database in NDB Cluster
5671 5672
  NOTE add a dummy void function, since stupid handlerton is returning void instead of int...
*/
5673

5674
int ndbcluster_drop_database_impl(const char *path)
5675 5676 5677 5678 5679 5680 5681 5682 5683
{
  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;
5684
  int ret= 0;
5685 5686 5687 5688
  ha_ndbcluster::set_dbname(path, (char *)&dbname);
  DBUG_PRINT("enter", ("db: %s", dbname));
  
  if (!(ndb= check_ndb_in_thd(thd)))
5689
    DBUG_RETURN(-1);
5690 5691 5692 5693 5694
  
  // List tables in NDB
  NDBDICT *dict= ndb->getDictionary();
  if (dict->listObjects(list, 
                        NdbDictionary::Object::UserTable) != 0)
5695
    DBUG_RETURN(-1);
5696 5697
  for (i= 0 ; i < list.count ; i++)
  {
unknown's avatar
unknown committed
5698 5699
    NdbDictionary::Dictionary::List::Element& elmt= list.elements[i];
    DBUG_PRINT("info", ("Found %s/%s in NDB", elmt.database, elmt.name));     
5700 5701
    
    // Add only tables that belongs to db
unknown's avatar
unknown committed
5702
    if (my_strcasecmp(system_charset_info, elmt.database, dbname))
5703
      continue;
unknown's avatar
unknown committed
5704 5705
    DBUG_PRINT("info", ("%s must be dropped", elmt.name));     
    drop_list.push_back(thd->strdup(elmt.name));
5706 5707
  }
  // Drop any tables belonging to database
unknown's avatar
unknown committed
5708
  char full_path[FN_REFLEN];
5709 5710 5711
  char *tmp= full_path +
    build_table_filename(full_path, sizeof(full_path), dbname, "", "");

5712 5713 5714
  ndb->setDatabaseName(dbname);
  List_iterator_fast<char> it(drop_list);
  while ((tabname=it++))
5715
  {
5716
    tablename_to_filename(tabname, tmp, FN_REFLEN - (tmp - full_path)-1);
unknown's avatar
unknown committed
5717
    if (ha_ndbcluster::delete_table(0, ndb, full_path, dbname, tabname))
5718 5719
    {
      const NdbError err= dict->getNdbError();
unknown's avatar
unknown committed
5720
      if (err.code != 709 && err.code != 723)
5721 5722
      {
        ERR_PRINT(err);
5723
        ret= ndb_to_mysql_error(&err);
5724
      }
5725 5726 5727
    }
  }
  DBUG_RETURN(ret);      
5728 5729
}

unknown's avatar
unknown committed
5730
static void ndbcluster_drop_database(char *path)
5731
{
5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744
  DBUG_ENTER("ndbcluster_drop_database");
#ifdef HAVE_NDB_BINLOG
  /*
    Don't allow drop database unless
    schema distribution table is setup
  */
  if (!schema_share)
  {
    DBUG_PRINT("info", ("Schema distribution table not setup"));
    DBUG_VOID_RETURN;
    //DBUG_RETURN(HA_ERR_NO_CONNECTION);
  }
#endif
5745
  ndbcluster_drop_database_impl(path);
unknown's avatar
unknown committed
5746 5747 5748 5749 5750 5751 5752
#ifdef HAVE_NDB_BINLOG
  char db[FN_REFLEN];
  ha_ndbcluster::set_dbname(path, db);
  ndbcluster_log_schema_op(current_thd, 0,
                           current_thd->query, current_thd->query_length,
                           db, "", 0, 0, SOT_DROP_DB);
#endif
5753
  DBUG_VOID_RETURN;
5754
}
unknown's avatar
unknown committed
5755 5756 5757
/*
  find all tables in ndb and discover those needed
*/
5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769
int ndb_create_table_from_engine(THD *thd, const char *db,
                                 const char *table_name)
{
  LEX *old_lex= thd->lex, newlex;
  thd->lex= &newlex;
  newlex.current_select= NULL;
  lex_start(thd, (const uchar*) "", 0);
  int res= ha_create_table_from_engine(thd, db, table_name);
  thd->lex= old_lex;
  return res;
}

unknown's avatar
unknown committed
5770
int ndbcluster_find_all_files(THD *thd)
unknown's avatar
unknown committed
5771 5772 5773 5774 5775 5776 5777 5778 5779 5780
{
  DBUG_ENTER("ndbcluster_find_all_files");
  Ndb* ndb;
  char key[FN_REFLEN];

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

  NDBDICT *dict= ndb->getDictionary();

5781
  int unhandled, retries= 5, skipped;
unknown's avatar
unknown committed
5782 5783
  do
  {
unknown's avatar
ndb -  
unknown committed
5784
    NdbDictionary::Dictionary::List list;
unknown's avatar
unknown committed
5785 5786 5787
    if (dict->listObjects(list, NdbDictionary::Object::UserTable) != 0)
      ERR_RETURN(dict->getNdbError());
    unhandled= 0;
5788 5789
    skipped= 0;
    retries--;
unknown's avatar
unknown committed
5790 5791 5792
    for (uint i= 0 ; i < list.count ; i++)
    {
      NDBDICT::List::Element& elmt= list.elements[i];
unknown's avatar
unknown committed
5793
      if (IS_TMP_PREFIX(elmt.name) || IS_NDB_BLOB_PREFIX(elmt.name))
unknown's avatar
unknown committed
5794 5795 5796 5797
      {
        DBUG_PRINT("info", ("Skipping %s.%s in NDB", elmt.database, elmt.name));
        continue;
      }
unknown's avatar
unknown committed
5798
      DBUG_PRINT("info", ("Found %s.%s in NDB", elmt.database, elmt.name));
5799 5800 5801
      if (elmt.state != NDBOBJ::StateOnline &&
          elmt.state != NDBOBJ::StateBackup &&
          elmt.state != NDBOBJ::StateBuilding)
unknown's avatar
unknown committed
5802 5803 5804
      {
        sql_print_information("NDB: skipping setup table %s.%s, in state %d",
                              elmt.database, elmt.name, elmt.state);
5805
        skipped++;
unknown's avatar
unknown committed
5806 5807 5808 5809
        continue;
      }

      ndb->setDatabaseName(elmt.database);
5810 5811 5812
      Ndb_table_guard ndbtab_g(dict, elmt.name);
      const NDBTAB *ndbtab= ndbtab_g.get_table();
      if (!ndbtab)
unknown's avatar
unknown committed
5813
      {
5814
        if (retries == 0)
unknown's avatar
unknown committed
5815 5816 5817 5818
          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
5819 5820 5821 5822 5823 5824 5825
        unhandled++;
        continue;
      }

      if (ndbtab->getFrmLength() == 0)
        continue;
    
5826
      /* check if database exists */
5827 5828
      char *end= key +
        build_table_filename(key, sizeof(key), elmt.database, "", "");
5829 5830 5831 5832 5833
      if (my_access(key, F_OK))
      {
        /* no such database defined, skip table */
        continue;
      }
5834 5835 5836
      /* finalize construction of path */
      end+= tablename_to_filename(elmt.name, end,
                                  sizeof(key)-(end-key));
unknown's avatar
unknown committed
5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848
      const void *data= 0, *pack_data= 0;
      uint length, pack_length;
      int discover= 0;
      if (readfrm(key, &data, &length) ||
          packfrm(data, length, &pack_data, &pack_length))
      {
        discover= 1;
        sql_print_information("NDB: missing frm for %s.%s, discovering...",
                              elmt.database, elmt.name);
      }
      else if (cmp_frm(ndbtab, pack_data, pack_length))
      {
unknown's avatar
unknown committed
5849
        NDB_SHARE *share= get_share(key, 0, false);
5850
        if (!share || get_ndb_share_state(share) != NSS_ALTERED)
unknown's avatar
unknown committed
5851 5852 5853 5854 5855
        {
          discover= 1;
          sql_print_information("NDB: mismatch in frm for %s.%s, discovering...",
                                elmt.database, elmt.name);
        }
unknown's avatar
unknown committed
5856 5857
        if (share)
          free_share(&share);
unknown's avatar
unknown committed
5858 5859 5860 5861
      }
      my_free((char*) data, MYF(MY_ALLOW_ZERO_PTR));
      my_free((char*) pack_data, MYF(MY_ALLOW_ZERO_PTR));

5862
      pthread_mutex_lock(&LOCK_open);
unknown's avatar
unknown committed
5863 5864 5865
      if (discover)
      {
        /* ToDo 4.1 database needs to be created if missing */
5866
        if (ndb_create_table_from_engine(thd, elmt.database, elmt.name))
unknown's avatar
unknown committed
5867 5868 5869 5870
        {
          /* ToDo 4.1 handle error */
        }
      }
unknown's avatar
unknown committed
5871
#ifdef HAVE_NDB_BINLOG
5872
      else
unknown's avatar
unknown committed
5873 5874
      {
        /* set up replication for this table */
5875 5876 5877
        ndbcluster_create_binlog_setup(ndb, key, end-key,
                                       elmt.database, elmt.name,
                                       TRUE);
unknown's avatar
unknown committed
5878 5879
      }
#endif
5880
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
5881 5882
    }
  }
5883
  while (unhandled && retries);
unknown's avatar
unknown committed
5884

5885
  DBUG_RETURN(-(skipped + unhandled));
unknown's avatar
unknown committed
5886
}
5887

5888
int ndbcluster_find_files(THD *thd,const char *db,const char *path,
5889
                          const char *wild, bool dir, List<char> *files)
unknown's avatar
unknown committed
5890
{
5891 5892 5893
  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
5894
  uint i;
5895
  Ndb* ndb;
5896
  char name[FN_REFLEN];
unknown's avatar
unknown committed
5897
  HASH ndb_tables, ok_tables;
unknown's avatar
unknown committed
5898
  NDBDICT::List list;
5899 5900 5901 5902

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

5903
  if (dir)
unknown's avatar
unknown committed
5904
    DBUG_RETURN(0); // Discover of databases not yet supported
5905

unknown's avatar
unknown committed
5906
  // List tables in NDB
5907
  NDBDICT *dict= ndb->getDictionary();
unknown's avatar
unknown committed
5908
  if (dict->listObjects(list, 
5909
                        NdbDictionary::Object::UserTable) != 0)
unknown's avatar
unknown committed
5910
    ERR_RETURN(dict->getNdbError());
5911

unknown's avatar
unknown committed
5912
  if (hash_init(&ndb_tables, system_charset_info,list.count,0,0,
5913
                (hash_get_key)tables_get_key,0,0))
unknown's avatar
unknown committed
5914 5915 5916 5917 5918 5919
  {
    DBUG_PRINT("error", ("Failed to init HASH ndb_tables"));
    DBUG_RETURN(-1);
  }

  if (hash_init(&ok_tables, system_charset_info,32,0,0,
5920
                (hash_get_key)tables_get_key,0,0))
unknown's avatar
unknown committed
5921 5922 5923 5924 5925 5926
  {
    DBUG_PRINT("error", ("Failed to init HASH ok_tables"));
    hash_free(&ndb_tables);
    DBUG_RETURN(-1);
  }  

unknown's avatar
unknown committed
5927 5928
  for (i= 0 ; i < list.count ; i++)
  {
unknown's avatar
unknown committed
5929
    NDBDICT::List::Element& elmt= list.elements[i];
unknown's avatar
unknown committed
5930
    if (IS_TMP_PREFIX(elmt.name) || IS_NDB_BLOB_PREFIX(elmt.name))
unknown's avatar
unknown committed
5931 5932 5933 5934
    {
      DBUG_PRINT("info", ("Skipping %s.%s in NDB", elmt.database, elmt.name));
      continue;
    }
unknown's avatar
unknown committed
5935
    DBUG_PRINT("info", ("Found %s/%s in NDB", elmt.database, elmt.name));
unknown's avatar
unknown committed
5936

5937
    // Add only tables that belongs to db
unknown's avatar
unknown committed
5938
    if (my_strcasecmp(system_charset_info, elmt.database, db))
5939
      continue;
unknown's avatar
unknown committed
5940

unknown's avatar
unknown committed
5941
    // Apply wildcard to list of tables in NDB
5942
    if (wild)
5943
    {
5944 5945
      if (lower_case_table_names)
      {
unknown's avatar
unknown committed
5946
        if (wild_case_compare(files_charset_info, elmt.name, wild))
5947
          continue;
5948
      }
unknown's avatar
unknown committed
5949
      else if (wild_compare(elmt.name,wild,0))
5950
        continue;
5951
    }
unknown's avatar
unknown committed
5952 5953
    DBUG_PRINT("info", ("Inserting %s into ndb_tables hash", elmt.name));     
    my_hash_insert(&ndb_tables, (byte*)thd->strdup(elmt.name));
unknown's avatar
unknown committed
5954 5955
  }

unknown's avatar
unknown committed
5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970
  char *file_name;
  List_iterator<char> it(*files);
  List<char> delete_list;
  while ((file_name=it++))
  {
    DBUG_PRINT("info", ("%s", file_name));     
    if (hash_search(&ndb_tables, file_name, strlen(file_name)))
    {
      DBUG_PRINT("info", ("%s existed in NDB _and_ on disk ", file_name));
      // File existed in NDB and as frm file, put in ok_tables list
      my_hash_insert(&ok_tables, (byte*)file_name);
      continue;
    }
    
    // File is not in NDB, check for .ndb file with this name
5971
    build_table_filename(name, sizeof(name), db, file_name, ha_ndb_ext);
unknown's avatar
unknown committed
5972
    DBUG_PRINT("info", ("Check access for %s", name));
5973
    if (my_access(name, F_OK))
unknown's avatar
unknown committed
5974 5975 5976 5977
    {
      DBUG_PRINT("info", ("%s did not exist on disk", name));     
      // .ndb file did not exist on disk, another table type
      continue;
5978
    }
5979

unknown's avatar
unknown committed
5980 5981 5982
    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.
5983
    if (ndbcluster_table_exists_in_engine(thd, db, file_name) == 0)    
unknown's avatar
unknown committed
5984 5985 5986 5987 5988 5989 5990
    {
      DBUG_PRINT("info", ("NDB says %s does not exists", file_name));     
      it.remove();
      // Put in list of tables to remove from disk
      delete_list.push_back(thd->strdup(file_name));
    }
  }
5991

unknown's avatar
unknown committed
5992 5993 5994
#ifdef HAVE_NDB_BINLOG
  /* setup logging to binlog for all discovered tables */
  {
5995 5996
    char *end, *end1= name +
      build_table_filename(name, sizeof(name), db, "", "");
unknown's avatar
unknown committed
5997 5998 5999
    for (i= 0; i < ok_tables.records; i++)
    {
      file_name= (char*)hash_element(&ok_tables, i);
6000 6001
      end= end1 +
        tablename_to_filename(file_name, end1, sizeof(name) - (end1 - name));
6002 6003 6004 6005
      pthread_mutex_lock(&LOCK_open);
      ndbcluster_create_binlog_setup(ndb, name, end-name,
                                     db, file_name, TRUE);
      pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
6006 6007 6008 6009
    }
  }
#endif

unknown's avatar
unknown committed
6010 6011 6012 6013
  // 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++)
6014
  {
unknown's avatar
unknown committed
6015 6016
    file_name= hash_element(&ndb_tables, i);
    if (!hash_search(&ok_tables, file_name, strlen(file_name)))
6017
    {
6018 6019
      build_table_filename(name, sizeof(name), db, file_name, reg_ext);
      if (my_access(name, F_OK))
unknown's avatar
unknown committed
6020 6021 6022 6023 6024 6025
      {
        DBUG_PRINT("info", ("%s must be discovered", file_name));
        // File is in list of ndb tables and not in ok_tables
        // This table need to be created
        create_list.push_back(thd->strdup(file_name));
      }
unknown's avatar
unknown committed
6026 6027
    }
  }
6028

unknown's avatar
unknown committed
6029 6030
  // Lock mutex before deleting and creating frm files
  pthread_mutex_lock(&LOCK_open);
6031

unknown's avatar
unknown committed
6032 6033 6034 6035 6036
  if (!global_read_lock)
  {
    // Delete old files
    List_iterator_fast<char> it3(delete_list);
    while ((file_name=it3++))
6037 6038
    {
      DBUG_PRINT("info", ("Remove table %s/%s", db, file_name));
unknown's avatar
unknown committed
6039 6040 6041 6042
      // Delete the table and all related files
      TABLE_LIST table_list;
      bzero((char*) &table_list,sizeof(table_list));
      table_list.db= (char*) db;
6043
      table_list.alias= table_list.table_name= (char*)file_name;
6044
      (void)mysql_rm_table_part2(thd, &table_list,
unknown's avatar
Merge  
unknown committed
6045 6046 6047 6048
                                                                 /* if_exists */ FALSE,
                                                                 /* drop_temporary */ FALSE,
                                                                 /* drop_view */ FALSE,
                                                                 /* dont_log_query*/ TRUE);
6049 6050
      /* Clear error message that is returned when table is deleted */
      thd->clear_error();
6051 6052 6053
    }
  }

unknown's avatar
unknown committed
6054 6055 6056 6057
  // Create new files
  List_iterator_fast<char> it2(create_list);
  while ((file_name=it2++))
  {  
6058
    DBUG_PRINT("info", ("Table %s need discovery", file_name));
6059
    if (ndb_create_table_from_engine(thd, db, file_name) == 0)
6060
      files->push_back(thd->strdup(file_name)); 
unknown's avatar
unknown committed
6061 6062
  }

unknown's avatar
unknown committed
6063
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
6064 6065
  
  hash_free(&ok_tables);
6066
  hash_free(&ndb_tables);
6067
  } // extra bracket to avoid gcc 2.95.3 warning
6068
  DBUG_RETURN(0);    
unknown's avatar
unknown committed
6069 6070 6071 6072 6073 6074 6075 6076
}


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

6077 6078 6079 6080
/* Call back after cluster connect */
static int connect_callback()
{
  update_status_variables(g_ndb_cluster_connection);
unknown's avatar
unknown committed
6081 6082 6083 6084 6085 6086 6087

  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
6088
  pthread_cond_signal(&COND_ndb_util_thread);
6089 6090 6091
  return 0;
}

6092
extern int ndb_dictionary_is_mysqld;
unknown's avatar
unknown committed
6093
static bool ndbcluster_init()
unknown's avatar
unknown committed
6094
{
unknown's avatar
unknown committed
6095
  int res;
unknown's avatar
unknown committed
6096
  DBUG_ENTER("ndbcluster_init");
6097

6098
  ndb_dictionary_is_mysqld= 1;
6099 6100 6101
  if (have_ndbcluster != SHOW_OPTION_YES)
    goto ndbcluster_init_error;

unknown's avatar
unknown committed
6102 6103 6104 6105 6106 6107 6108 6109 6110 6111
  {
    handlerton &h= ndbcluster_hton;
    h.close_connection= ndbcluster_close_connection;
    h.commit=           ndbcluster_commit;
    h.rollback=         ndbcluster_rollback;
    h.create=           ndbcluster_create_handler; /* Create a new handler */
    h.drop_database=    ndbcluster_drop_database;  /* Drop a database */
    h.panic=            ndbcluster_end;            /* Panic call */
    h.show_status=      ndbcluster_show_status;    /* Show status */
    h.alter_tablespace= ndbcluster_alter_tablespace;    /* Show status */
unknown's avatar
unknown committed
6112 6113
    h.partition_flags=  ndbcluster_partition_flags; /* Partition flags */
    h.alter_table_flags=ndbcluster_alter_table_flags; /* Alter table flags */
6114
    h.fill_files_table= ndbcluster_fill_files_table;
unknown's avatar
unknown committed
6115 6116 6117
#ifdef HAVE_NDB_BINLOG
    ndbcluster_binlog_init_handlerton();
#endif
6118
    h.flags=            HTON_TEMPORARY_NOT_SUPPORTED;
unknown's avatar
unknown committed
6119 6120
  }

6121
  // Set connectstring if specified
6122 6123
  if (opt_ndbcluster_connectstring != 0)
    DBUG_PRINT("connectstring", ("%s", opt_ndbcluster_connectstring));     
6124
  if ((g_ndb_cluster_connection=
6125
       new Ndb_cluster_connection(opt_ndbcluster_connectstring)) == 0)
6126
  {
6127
    DBUG_PRINT("error",("Ndb_cluster_connection(%s)",
6128
                        opt_ndbcluster_connectstring));
unknown's avatar
unknown committed
6129
    goto ndbcluster_init_error;
6130
  }
unknown's avatar
ndb:  
unknown committed
6131 6132 6133 6134 6135
  {
    char buf[128];
    my_snprintf(buf, sizeof(buf), "mysqld --server-id=%d", server_id);
    g_ndb_cluster_connection->set_name(buf);
  }
6136 6137 6138
  g_ndb_cluster_connection->set_optimized_node_selection
    (opt_ndb_optimized_node_selection);

unknown's avatar
unknown committed
6139
  // Create a Ndb object to open the connection  to NDB
6140 6141 6142 6143 6144
  if ( (g_ndb= new Ndb(g_ndb_cluster_connection, "sys")) == 0 )
  {
    DBUG_PRINT("error", ("failed to create global ndb object"));
    goto ndbcluster_init_error;
  }
unknown's avatar
unknown committed
6145 6146 6147
  if (g_ndb->init() != 0)
  {
    ERR_PRINT (g_ndb->getNdbError());
unknown's avatar
unknown committed
6148
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
6149
  }
unknown's avatar
unknown committed
6150

unknown's avatar
unknown committed
6151
  if ((res= g_ndb_cluster_connection->connect(0,0,0)) == 0)
unknown's avatar
unknown committed
6152
  {
6153
    connect_callback();
unknown's avatar
unknown committed
6154
    DBUG_PRINT("info",("NDBCLUSTER storage engine at %s on port %d",
6155 6156
                       g_ndb_cluster_connection->get_connected_host(),
                       g_ndb_cluster_connection->get_connected_port()));
6157
    g_ndb_cluster_connection->wait_until_ready(10,3);
unknown's avatar
unknown committed
6158
  } 
unknown's avatar
unknown committed
6159
  else if (res == 1)
unknown's avatar
unknown committed
6160
  {
6161
    if (g_ndb_cluster_connection->start_connect_thread(connect_callback)) 
6162
    {
unknown's avatar
unknown committed
6163
      DBUG_PRINT("error", ("g_ndb_cluster_connection->start_connect_thread()"));
unknown's avatar
unknown committed
6164 6165
      goto ndbcluster_init_error;
    }
6166
#ifndef DBUG_OFF
unknown's avatar
unknown committed
6167 6168
    {
      char buf[1024];
6169
      DBUG_PRINT("info",
6170 6171 6172 6173
                 ("NDBCLUSTER storage engine not started, "
                  "will connect using %s",
                  g_ndb_cluster_connection->
                  get_connectstring(buf,sizeof(buf))));
unknown's avatar
unknown committed
6174
    }
6175
#endif
unknown's avatar
unknown committed
6176
  }
unknown's avatar
unknown committed
6177
  else
unknown's avatar
unknown committed
6178 6179 6180
  {
    DBUG_ASSERT(res == -1);
    DBUG_PRINT("error", ("permanent error"));
unknown's avatar
unknown committed
6181
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
6182
  }
unknown's avatar
unknown committed
6183
  
unknown's avatar
unknown committed
6184 6185 6186
  (void) hash_init(&ndbcluster_open_tables,system_charset_info,32,0,0,
                   (hash_get_key) ndbcluster_get_key,0,0);
  pthread_mutex_init(&ndbcluster_mutex,MY_MUTEX_INIT_FAST);
unknown's avatar
unknown committed
6187 6188
#ifdef HAVE_NDB_BINLOG
  /* start the ndb injector thread */
6189 6190
  if (ndbcluster_binlog_start())
    goto ndbcluster_init_error;
unknown's avatar
unknown committed
6191 6192
#endif /* HAVE_NDB_BINLOG */
  
unknown's avatar
Merge  
unknown committed
6193 6194
  pthread_mutex_init(&LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST);
  pthread_cond_init(&COND_ndb_util_thread, NULL);
6195

unknown's avatar
Merge  
unknown committed
6196 6197 6198 6199 6200 6201

  // 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"));
6202 6203 6204 6205
    hash_free(&ndbcluster_open_tables);
    pthread_mutex_destroy(&ndbcluster_mutex);
    pthread_mutex_destroy(&LOCK_ndb_util_thread);
    pthread_cond_destroy(&COND_ndb_util_thread);
unknown's avatar
Merge  
unknown committed
6206 6207 6208
    goto ndbcluster_init_error;
  }
  
unknown's avatar
unknown committed
6209
  ndbcluster_inited= 1;
6210
  DBUG_RETURN(FALSE);
unknown's avatar
Merge  
unknown committed
6211

6212
ndbcluster_init_error:
unknown's avatar
unknown committed
6213
  if (g_ndb)
6214 6215 6216 6217 6218
    delete g_ndb;
  g_ndb= NULL;
  if (g_ndb_cluster_connection)
    delete g_ndb_cluster_connection;
  g_ndb_cluster_connection= NULL;
6219 6220
  have_ndbcluster= SHOW_OPTION_DISABLED;	// If we couldn't use handler
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
6221 6222
}

unknown's avatar
unknown committed
6223
static int ndbcluster_end(ha_panic_function type)
unknown's avatar
unknown committed
6224 6225
{
  DBUG_ENTER("ndbcluster_end");
unknown's avatar
Merge  
unknown committed
6226

6227 6228 6229
  if (!ndbcluster_inited)
    DBUG_RETURN(0);

6230 6231 6232
#ifdef HAVE_NDB_BINLOG
  {
    pthread_mutex_lock(&ndbcluster_mutex);
6233
    while (ndbcluster_open_tables.records)
6234 6235
    {
      NDB_SHARE *share=
6236
        (NDB_SHARE*) hash_element(&ndbcluster_open_tables, 0);
6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247
#ifndef DBUG_OFF
      fprintf(stderr, "NDB: table share %s with use_count %d not freed\n",
              share->key, share->use_count);
#endif
      real_free_share(&share);
    }
    pthread_mutex_unlock(&ndbcluster_mutex);
  }
#endif
  hash_free(&ndbcluster_open_tables);

unknown's avatar
unknown committed
6248
  if (g_ndb)
6249 6250
  {
#ifndef DBUG_OFF
unknown's avatar
unknown committed
6251 6252
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
6253 6254 6255 6256 6257 6258 6259 6260 6261 6262
    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
6263
    delete g_ndb;
unknown's avatar
unknown committed
6264
    g_ndb= NULL;
6265
  }
unknown's avatar
unknown committed
6266
  delete g_ndb_cluster_connection;
6267
  g_ndb_cluster_connection= NULL;
6268

unknown's avatar
unknown committed
6269
  pthread_mutex_destroy(&ndbcluster_mutex);
unknown's avatar
Merge  
unknown committed
6270 6271
  pthread_mutex_destroy(&LOCK_ndb_util_thread);
  pthread_cond_destroy(&COND_ndb_util_thread);
unknown's avatar
unknown committed
6272 6273 6274 6275
  ndbcluster_inited= 0;
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
6276 6277 6278 6279 6280 6281
void ha_ndbcluster::print_error(int error, myf errflag)
{
  DBUG_ENTER("ha_ndbcluster::print_error");
  DBUG_PRINT("enter", ("error = %d", error));

  if (error == HA_ERR_NO_PARTITION_FOUND)
6282 6283
  {
    char buf[100];
unknown's avatar
unknown committed
6284
    my_error(ER_NO_PARTITION_FOR_GIVEN_VALUE, MYF(0),
6285
             m_part_info->part_expr->null_value ? "NULL" :
6286 6287
             llstr(m_part_info->part_expr->val_int(), buf));
  }
unknown's avatar
unknown committed
6288 6289 6290 6291 6292 6293
  else
    handler::print_error(error, errflag);
  DBUG_VOID_RETURN;
}


6294 6295 6296 6297 6298
/*
  Static error print function called from
  static handler method ndbcluster_commit
  and ndbcluster_rollback
*/
6299 6300

void ndbcluster_print_error(int error, const NdbOperation *error_op)
6301
{
6302
  DBUG_ENTER("ndbcluster_print_error");
unknown's avatar
unknown committed
6303
  TABLE_SHARE share;
6304
  const char *tab_name= (error_op) ? error_op->getTableName() : "";
unknown's avatar
unknown committed
6305 6306 6307 6308 6309
  share.db.str= (char*) "";
  share.db.length= 0;
  share.table_name.str= (char *) tab_name;
  share.table_name.length= strlen(tab_name);
  ha_ndbcluster error_handler(&share);
6310
  error_handler.print_error(error, MYF(0));
unknown's avatar
unknown committed
6311
  DBUG_VOID_RETURN;
6312
}
unknown's avatar
unknown committed
6313

6314 6315 6316
/**
 * Set a given location from full pathname to database name
 *
unknown's avatar
unknown committed
6317
 */
6318
void ha_ndbcluster::set_dbname(const char *path_name, char *dbname)
unknown's avatar
unknown committed
6319
{
unknown's avatar
unknown committed
6320 6321 6322 6323
  char *end, *ptr, *tmp_name;
  char tmp_buff[FN_REFLEN];
 
  tmp_name= tmp_buff;
unknown's avatar
unknown committed
6324
  /* Scan name from the end */
6325 6326 6327 6328 6329 6330
  ptr= strend(path_name)-1;
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  ptr--;
  end= ptr;
unknown's avatar
unknown committed
6331 6332 6333 6334
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
  uint name_len= end - ptr;
unknown's avatar
unknown committed
6335 6336
  memcpy(tmp_name, ptr + 1, name_len);
  tmp_name[name_len]= '\0';
unknown's avatar
unknown committed
6337 6338
#ifdef __WIN__
  /* Put to lower case */
6339
  
unknown's avatar
unknown committed
6340
  ptr= tmp_name;
unknown's avatar
unknown committed
6341 6342
  
  while (*ptr != '\0') {
6343
    *ptr= tolower(*ptr);
unknown's avatar
unknown committed
6344 6345 6346
    ptr++;
  }
#endif
unknown's avatar
unknown committed
6347
  filename_to_tablename(tmp_name, dbname, FN_REFLEN);
unknown's avatar
unknown committed
6348 6349
}

6350 6351 6352 6353 6354 6355 6356 6357 6358
/*
  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
6359 6360 6361 6362 6363 6364 6365
/**
 * 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
6366 6367 6368 6369
  char *end, *ptr, *tmp_name;
  char tmp_buff[FN_REFLEN];

  tmp_name= tmp_buff;
unknown's avatar
unknown committed
6370
  /* Scan name from the end */
6371 6372
  end= strend(path_name)-1;
  ptr= end;
unknown's avatar
unknown committed
6373 6374 6375
  while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
    ptr--;
  }
6376
  uint name_len= end - ptr;
unknown's avatar
unknown committed
6377 6378
  memcpy(tmp_name, ptr + 1, end - ptr);
  tmp_name[name_len]= '\0';
unknown's avatar
unknown committed
6379 6380
#ifdef __WIN__
  /* Put to lower case */
unknown's avatar
unknown committed
6381
  ptr= tmp_name;
unknown's avatar
unknown committed
6382 6383 6384 6385 6386 6387
  
  while (*ptr != '\0') {
    *ptr= tolower(*ptr);
    ptr++;
  }
#endif
unknown's avatar
unknown committed
6388
  filename_to_tablename(tmp_name, tabname, FN_REFLEN);
unknown's avatar
unknown committed
6389 6390 6391
}

/*
6392
  Set m_tabname from full pathname to table file 
unknown's avatar
unknown committed
6393 6394
 */

6395
void ha_ndbcluster::set_tabname(const char *path_name)
unknown's avatar
unknown committed
6396
{
6397
  set_tabname(path_name, m_tabname);
unknown's avatar
unknown committed
6398 6399 6400 6401
}


ha_rows 
unknown's avatar
unknown committed
6402 6403 6404 6405
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
6406
  uint key_length= key_info->key_length;
6407
  NDB_INDEX_TYPE idx_type= get_index_type(inx);  
unknown's avatar
unknown committed
6408 6409

  DBUG_ENTER("records_in_range");
6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422
  // 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);
  
6423 6424 6425 6426 6427 6428
  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];
6429
    const NDBINDEX* index= d.index;
6430 6431 6432 6433 6434 6435 6436 6437 6438 6439
    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;
6440
      Ndb_local_table_statistics *info= m_table_info;
6441 6442 6443 6444 6445 6446 6447 6448
      if (info->records != ~(ha_rows)0 && info->records != 0)
      {
        table_rows = info->records;
        DBUG_PRINT("info", ("use info->records: %llu", table_rows));
      }
      else
      {
        Ndb_statistics stat;
6449
        if ((res=ndb_get_table_statistics(ndb, m_table, &stat)) != 0)
6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462
          break;
        table_rows=stat.row_count;
        DBUG_PRINT("info", ("use db row_count: %llu", table_rows));
        if (table_rows == 0) {
          // Problem if autocommit=0
#ifdef ndb_get_table_statistics_uses_active_trans
          rows=0;
          break;
#endif
        }
      }

      // Define scan op for the range
6463 6464
      if ((trans=m_active_trans) == NULL || 
	  trans->commitStatus() != NdbTransaction::Started)
6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500
      {
        DBUG_PRINT("info", ("no active trans"));
        if (! (trans=ndb->startTransaction()))
          ERR_BREAK(ndb->getNdbError(), res);
      }
      if (! (op=trans->getNdbIndexScanOperation(index, (NDBTAB*)m_table)))
        ERR_BREAK(trans->getNdbError(), res);
      if ((op->readTuples(NdbOperation::LM_CommittedRead)) == -1)
        ERR_BREAK(op->getNdbError(), res);
      const key_range *keys[2]={ min_key, max_key };
      if ((res=set_bounds(op, inx, true, keys)) != 0)
        break;

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

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

6501
  DBUG_RETURN(10); /* Good guess when you don't know anything */
unknown's avatar
unknown committed
6502 6503
}

6504 6505 6506 6507 6508 6509 6510 6511 6512
ulong ha_ndbcluster::table_flags(void) const
{
  if (m_ha_not_exact_count)
    return m_table_flags | HA_NOT_EXACT_COUNT;
  else
    return m_table_flags;
}
const char * ha_ndbcluster::table_type() const 
{
6513
  return("NDBCLUSTER");
6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530
}
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
6531 6532 6533 6534
uint ha_ndbcluster::max_supported_key_part_length() const
{
  return NDB_MAX_KEY_SIZE;
}
6535 6536 6537 6538 6539 6540 6541 6542 6543 6544
bool ha_ndbcluster::low_byte_first() const
{ 
#ifdef WORDS_BIGENDIAN
  return FALSE;
#else
  return TRUE;
#endif
}
bool ha_ndbcluster::has_transactions()
{
6545
  return TRUE;
6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559
}
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
6560

6561 6562
uint8 ha_ndbcluster::table_cache_type()
{
unknown's avatar
Merge  
unknown committed
6563 6564 6565 6566 6567 6568
  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,
6569
                         Uint64 *commit_count)
unknown's avatar
Merge  
unknown committed
6570
{
6571 6572
  char name[FN_REFLEN];
  NDB_SHARE *share;
unknown's avatar
unknown committed
6573 6574
  DBUG_ENTER("ndb_get_commitcount");

6575
  build_table_filename(name, sizeof(name), dbname, tabname, "");
6576 6577 6578 6579 6580 6581 6582
  DBUG_PRINT("enter", ("name: %s", name));
  pthread_mutex_lock(&ndbcluster_mutex);
  if (!(share=(NDB_SHARE*) hash_search(&ndbcluster_open_tables,
                                       (byte*) name,
                                       strlen(name))))
  {
    pthread_mutex_unlock(&ndbcluster_mutex);
unknown's avatar
unknown committed
6583
    DBUG_PRINT("info", ("Table %s not found in ndbcluster_open_tables", name));
6584 6585 6586 6587 6588 6589
    DBUG_RETURN(1);
  }
  share->use_count++;
  pthread_mutex_unlock(&ndbcluster_mutex);

  pthread_mutex_lock(&share->mutex);
unknown's avatar
Merge  
unknown committed
6590 6591
  if (ndb_cache_check_time > 0)
  {
6592
    if (share->commit_count != 0)
unknown's avatar
Merge  
unknown committed
6593
    {
6594 6595 6596 6597
      *commit_count= share->commit_count;
      DBUG_PRINT("info", ("Getting commit_count: %llu from share",
                          share->commit_count));
      pthread_mutex_unlock(&share->mutex);
unknown's avatar
unknown committed
6598
      free_share(&share);
6599
      DBUG_RETURN(0);
unknown's avatar
Merge  
unknown committed
6600 6601
    }
  }
6602
  DBUG_PRINT("info", ("Get commit_count from NDB"));
unknown's avatar
Merge  
unknown committed
6603 6604 6605 6606
  Ndb *ndb;
  if (!(ndb= check_ndb_in_thd(thd)))
    DBUG_RETURN(1);
  ndb->setDatabaseName(dbname);
6607 6608
  uint lock= share->commit_count_lock;
  pthread_mutex_unlock(&share->mutex);
unknown's avatar
Merge  
unknown committed
6609 6610

  struct Ndb_statistics stat;
6611
  {
6612 6613 6614 6615 6616 6617 6618
    Ndb_table_guard ndbtab_g(ndb->getDictionary(), tabname);
    if (ndbtab_g.get_table() == 0
        || ndb_get_table_statistics(ndb, ndbtab_g.get_table(), &stat))
    {
      free_share(&share);
      DBUG_RETURN(1);
    }
6619 6620 6621
  }

  pthread_mutex_lock(&share->mutex);
unknown's avatar
unknown committed
6622
  if (share->commit_count_lock == lock)
6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633
  {
    DBUG_PRINT("info", ("Setting commit_count to %llu", stat.commit_count));
    share->commit_count= stat.commit_count;
    *commit_count= stat.commit_count;
  }
  else
  {
    DBUG_PRINT("info", ("Discarding commit_count, comit_count_lock changed"));
    *commit_count= 0;
  }
  pthread_mutex_unlock(&share->mutex);
unknown's avatar
unknown committed
6634
  free_share(&share);
unknown's avatar
Merge  
unknown committed
6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670
  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,
6671 6672
                                   char *full_name, uint full_name_len,
                                   ulonglong *engine_data)
unknown's avatar
Merge  
unknown committed
6673 6674 6675 6676 6677 6678 6679 6680
{
  DBUG_ENTER("ndbcluster_cache_retrieval_allowed");

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

6681 6682
  DBUG_PRINT("enter", ("dbname: %s, tabname: %s, is_autocommit: %d",
                       dbname, tabname, is_autocommit));
unknown's avatar
Merge  
unknown committed
6683 6684

  if (!is_autocommit)
6685 6686
  {
    DBUG_PRINT("exit", ("No, don't use cache in transaction"));
unknown's avatar
Merge  
unknown committed
6687
    DBUG_RETURN(FALSE);
6688
  }
unknown's avatar
Merge  
unknown committed
6689 6690 6691

  if (ndb_get_commitcount(thd, dbname, tabname, &commit_count))
  {
6692 6693
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, could not retrieve commit_count"));
unknown's avatar
Merge  
unknown committed
6694 6695
    DBUG_RETURN(FALSE);
  }
6696
  DBUG_PRINT("info", ("*engine_data: %llu, commit_count: %llu",
6697
                      *engine_data, commit_count));
6698
  if (commit_count == 0)
unknown's avatar
Merge  
unknown committed
6699
  {
6700 6701
    *engine_data= 0; /* invalidate */
    DBUG_PRINT("exit", ("No, local commit has been performed"));
unknown's avatar
Merge  
unknown committed
6702 6703
    DBUG_RETURN(FALSE);
  }
6704 6705 6706 6707 6708 6709
  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
6710

6711
  DBUG_PRINT("exit", ("OK to use cache, engine_data: %llu", *engine_data));
unknown's avatar
Merge  
unknown committed
6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739
  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,
6740 6741 6742
                                          char *full_name, uint full_name_len,
                                          qc_engine_callback *engine_callback,
                                          ulonglong *engine_data)
unknown's avatar
Merge  
unknown committed
6743 6744 6745 6746
{
  DBUG_ENTER("ha_ndbcluster::register_query_cache_table");

  bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
6747 6748 6749 6750

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

unknown's avatar
Merge  
unknown committed
6751
  if (!is_autocommit)
6752
  {
unknown's avatar
unknown committed
6753
    DBUG_PRINT("exit", ("Can't register table during transaction"));
unknown's avatar
Merge  
unknown committed
6754
    DBUG_RETURN(FALSE);
6755
  }
unknown's avatar
Merge  
unknown committed
6756 6757 6758 6759 6760

  Uint64 commit_count;
  if (ndb_get_commitcount(thd, m_dbname, m_tabname, &commit_count))
  {
    *engine_data= 0;
unknown's avatar
unknown committed
6761
    DBUG_PRINT("exit", ("Error, could not get commitcount"));
unknown's avatar
Merge  
unknown committed
6762 6763 6764 6765
    DBUG_RETURN(FALSE);
  }
  *engine_data= commit_count;
  *engine_callback= ndbcluster_cache_retrieval_allowed;
6766 6767
  DBUG_PRINT("exit", ("commit_count: %llu", commit_count));
  DBUG_RETURN(commit_count > 0);
6768
}
unknown's avatar
unknown committed
6769

unknown's avatar
Merge  
unknown committed
6770

unknown's avatar
unknown committed
6771
/*
unknown's avatar
Merge  
unknown committed
6772
  Handling the shared NDB_SHARE structure that is needed to
unknown's avatar
unknown committed
6773 6774 6775 6776 6777 6778
  provide table locking.
  It's also used for sharing data with other NDB handlers
  in the same MySQL Server. There is currently not much
  data we want to or can share.
 */

unknown's avatar
unknown committed
6779
static byte *ndbcluster_get_key(NDB_SHARE *share,uint *length,
6780
                                my_bool not_used __attribute__((unused)))
unknown's avatar
unknown committed
6781
{
unknown's avatar
unknown committed
6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799
  *length= share->key_length;
  return (byte*) share->key;
}

#ifndef DBUG_OFF
static void dbug_print_open_tables()
{
  DBUG_ENTER("dbug_print_open_tables");
  for (uint i= 0; i < ndbcluster_open_tables.records; i++)
  {
    NDB_SHARE *share= (NDB_SHARE*) hash_element(&ndbcluster_open_tables, i);
    DBUG_PRINT("share",
               ("[%d] 0x%lx key: %s  key_length: %d",
                i, share, share->key, share->key_length));
    DBUG_PRINT("share",
               ("db.tablename: %s.%s  use_count: %d  commit_count: %d",
                share->db, share->table_name,
                share->use_count, share->commit_count));
unknown's avatar
unknown committed
6800 6801 6802 6803 6804 6805
#ifdef HAVE_NDB_BINLOG
    if (share->table)
      DBUG_PRINT("share",
                 ("table->s->db.table_name: %s.%s",
                  share->table->s->db.str, share->table->s->table_name.str));
#endif
unknown's avatar
unknown committed
6806 6807
  }
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
6808
}
unknown's avatar
unknown committed
6809 6810 6811
#else
#define dbug_print_open_tables()
#endif
unknown's avatar
unknown committed
6812

unknown's avatar
unknown committed
6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831
#ifdef HAVE_NDB_BINLOG
/*
  For some reason a share is still around, try to salvage the situation
  by closing all cached tables. If the share still exists, there is an
  error somewhere but only report this to the error log.  Keep this
  "trailing share" but rename it since there are still references to it
  to avoid segmentation faults.  There is a risk that the memory for
  this trailing share leaks.
  
  Must be called with previous pthread_mutex_lock(&ndbcluster_mutex)
*/
int handle_trailing_share(NDB_SHARE *share)
{
  static ulong trailing_share_id= 0;
  DBUG_ENTER("handle_trailing_share");

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

6832 6833 6834 6835 6836
  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;
  close_cached_tables(current_thd, 0, &table_list, TRUE);
unknown's avatar
unknown committed
6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866

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

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

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

6867 6868 6869 6870 6871
  /*
    Ndb share has not been released as it should
  */
  DBUG_ASSERT(FALSE);

unknown's avatar
unknown committed
6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960
  /*
    This is probably an error.  We can however save the situation
    at the cost of a possible mem leak, by "renaming" the share
    - First remove from hash
  */
  hash_delete(&ndbcluster_open_tables, (byte*) share);

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

  DBUG_RETURN(0);
}

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

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

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

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

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

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

  DBUG_PRINT("rename_share",
             ("0x%lx key: %s  key_length: %d",
              share, share->key, share->key_length));
  DBUG_PRINT("rename_share",
             ("db.tablename: %s.%s  use_count: %d  commit_count: %d",
              share->db, share->table_name,
              share->use_count, share->commit_count));
6961
  if (share->table)
unknown's avatar
unknown committed
6962
  {
6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973
    DBUG_PRINT("rename_share",
               ("table->s->db.table_name: %s.%s",
                share->table->s->db.str, share->table->s->table_name.str));

    if (share->op == 0)
    {
      share->table->s->db.str= share->db;
      share->table->s->db.length= strlen(share->db);
      share->table->s->table_name.str= share->table_name;
      share->table->s->table_name.length= strlen(share->table_name);
    }
unknown's avatar
unknown committed
6974 6975 6976 6977 6978 6979 6980 6981 6982 6983
  }
  /* 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
6984 6985 6986 6987
/*
  Increase refcount on existing share.
  Always returns share and cannot fail.
*/
unknown's avatar
unknown committed
6988
NDB_SHARE *ndbcluster_get_share(NDB_SHARE *share)
unknown's avatar
unknown committed
6989 6990
{
  pthread_mutex_lock(&ndbcluster_mutex);
unknown's avatar
unknown committed
6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005
  share->use_count++;

  dbug_print_open_tables();

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

unknown's avatar
unknown committed
7006

unknown's avatar
unknown committed
7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020
/*
  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
7021

unknown's avatar
unknown committed
7022 7023 7024
NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table,
                                bool create_if_not_exists,
                                bool have_lock)
unknown's avatar
unknown committed
7025
{
unknown's avatar
unknown committed
7026
  THD *thd= current_thd;
unknown's avatar
unknown committed
7027
  NDB_SHARE *share;
unknown's avatar
unknown committed
7028 7029 7030 7031
  uint length= (uint) strlen(key);
  DBUG_ENTER("ndbcluster_get_share");
  DBUG_PRINT("enter", ("key: '%s'", key));

unknown's avatar
unknown committed
7032 7033 7034 7035 7036
  if (!have_lock)
    pthread_mutex_lock(&ndbcluster_mutex);
  if (!(share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
                                        (byte*) key,
                                        length)))
unknown's avatar
unknown committed
7037
  {
unknown's avatar
unknown committed
7038 7039 7040 7041 7042
    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
7043
      DBUG_RETURN(0);
unknown's avatar
unknown committed
7044 7045
    }
    if ((share= (NDB_SHARE*) my_malloc(sizeof(*share),
unknown's avatar
unknown committed
7046 7047
                                       MYF(MY_WME | MY_ZEROFILL))))
    {
unknown's avatar
unknown committed
7048 7049 7050 7051 7052
      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
7053
      share->state= NSS_INITIAL;
unknown's avatar
unknown committed
7054 7055 7056 7057
      /* enough space for key, db, and table_name */
      share->key= alloc_root(*root_ptr, 2 * (length + 1));
      share->key_length= length;
      strmov(share->key, key);
unknown's avatar
unknown committed
7058 7059
      if (my_hash_insert(&ndbcluster_open_tables, (byte*) share))
      {
unknown's avatar
unknown committed
7060 7061 7062 7063 7064
        free_root(&share->mem_root, MYF(0));
        my_free((gptr) share, 0);
        *root_ptr= old_root;
        if (!have_lock)
          pthread_mutex_unlock(&ndbcluster_mutex);
unknown's avatar
unknown committed
7065
        DBUG_RETURN(0);
unknown's avatar
unknown committed
7066 7067
      }
      thr_lock_init(&share->lock);
unknown's avatar
unknown committed
7068
      pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST);
unknown's avatar
Merge  
unknown committed
7069
      share->commit_count= 0;
7070
      share->commit_count_lock= 0;
unknown's avatar
unknown committed
7071 7072 7073 7074
      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
7075 7076 7077
#ifdef HAVE_NDB_BINLOG
      ndbcluster_binlog_init_share(share, table);
#endif
unknown's avatar
unknown committed
7078
      *root_ptr= old_root;
7079 7080 7081
    }
    else
    {
unknown's avatar
unknown committed
7082 7083 7084 7085
      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
7086
      DBUG_RETURN(0);
unknown's avatar
unknown committed
7087 7088 7089
    }
  }
  share->use_count++;
7090

unknown's avatar
unknown committed
7091 7092
  dbug_print_open_tables();

unknown's avatar
unknown committed
7093
  DBUG_PRINT("info",
unknown's avatar
unknown committed
7094 7095
             ("0x%lx key: %s  key_length: %d  key: %s",
              share, share->key, share->key_length, key));
unknown's avatar
unknown committed
7096
  DBUG_PRINT("info",
unknown's avatar
unknown committed
7097 7098 7099 7100 7101
             ("db.tablename: %s.%s  use_count: %d  commit_count: %d",
              share->db, share->table_name,
              share->use_count, share->commit_count));
  if (!have_lock)
    pthread_mutex_unlock(&ndbcluster_mutex);
unknown's avatar
unknown committed
7102
  DBUG_RETURN(share);
unknown's avatar
unknown committed
7103 7104
}

unknown's avatar
unknown committed
7105

unknown's avatar
unknown committed
7106
void ndbcluster_real_free_share(NDB_SHARE **share)
unknown's avatar
unknown committed
7107
{
unknown's avatar
unknown committed
7108
  DBUG_ENTER("ndbcluster_real_free_share");
unknown's avatar
unknown committed
7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120
  DBUG_PRINT("real_free_share",
             ("0x%lx key: %s  key_length: %d",
              (*share), (*share)->key, (*share)->key_length));
  DBUG_PRINT("real_free_share",
             ("db.tablename: %s.%s  use_count: %d  commit_count: %d",
              (*share)->db, (*share)->table_name,
              (*share)->use_count, (*share)->commit_count));

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

unknown's avatar
unknown committed
7121 7122 7123
#ifdef HAVE_NDB_BINLOG
  if ((*share)->table)
  {
7124
    // (*share)->table->mem_root is freed by closefrm
unknown's avatar
unknown committed
7125
    closefrm((*share)->table, 0);
7126 7127
    // (*share)->table_share->mem_root is freed by free_table_share
    free_table_share((*share)->table_share);
unknown's avatar
unknown committed
7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139
#ifndef DBUG_OFF
    bzero((gptr)(*share)->table_share, sizeof(*(*share)->table_share));
    bzero((gptr)(*share)->table, sizeof(*(*share)->table));
#endif
    my_free((gptr) (*share)->table_share, MYF(0));
    my_free((gptr) (*share)->table, MYF(0));
#ifndef DBUG_OFF
    (*share)->table_share= 0;
    (*share)->table= 0;
#endif
  }
#endif
unknown's avatar
unknown committed
7140
  free_root(&(*share)->mem_root, MYF(0));
unknown's avatar
unknown committed
7141 7142 7143 7144
  my_free((gptr) *share, MYF(0));
  *share= 0;

  dbug_print_open_tables();
unknown's avatar
unknown committed
7145
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
7146 7147 7148 7149 7150
}

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

unknown's avatar
unknown committed
7152 7153
  have_lock == TRUE, pthread_mutex_lock(&ndbcluster_mutex) already taken
*/
unknown's avatar
unknown committed
7154
void ndbcluster_free_share(NDB_SHARE **share, bool have_lock)
unknown's avatar
unknown committed
7155
{
unknown's avatar
unknown committed
7156 7157 7158 7159 7160
  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
7161
  {
unknown's avatar
unknown committed
7162
    real_free_share(share);
unknown's avatar
unknown committed
7163
  }
unknown's avatar
unknown committed
7164 7165 7166 7167 7168 7169 7170 7171 7172 7173 7174 7175 7176
  else
  {
    dbug_print_open_tables();
    DBUG_PRINT("free_share",
               ("0x%lx key: %s  key_length: %d",
                *share, (*share)->key, (*share)->key_length));
    DBUG_PRINT("free_share",
               ("db.tablename: %s.%s  use_count: %d  commit_count: %d",
                (*share)->db, (*share)->table_name,
                (*share)->use_count, (*share)->commit_count));
  }
  if (!have_lock)
    pthread_mutex_unlock(&ndbcluster_mutex);
unknown's avatar
unknown committed
7177 7178 7179
}


unknown's avatar
unknown committed
7180 7181
static 
int
7182
ndb_get_table_statistics(Ndb* ndb, const NDBTAB *ndbtab,
7183
                         struct Ndb_statistics * ndbstat)
unknown's avatar
unknown committed
7184 7185
{
  DBUG_ENTER("ndb_get_table_statistics");
7186
  DBUG_PRINT("enter", ("table: %s", ndbtab->getName()));
7187 7188 7189 7190
  NdbTransaction* pTrans;
  int retries= 10;
  int retry_sleep= 30 * 1000; /* 30 milliseconds */

7191 7192
  DBUG_ASSERT(ndbtab != 0);

7193
  do
unknown's avatar
unknown committed
7194
  {
7195
    pTrans= ndb->startTransaction();
unknown's avatar
unknown committed
7196
    if (pTrans == NULL)
7197 7198 7199 7200 7201 7202 7203
    {
      if (ndb->getNdbError().status == NdbError::TemporaryError &&
          retries--)
      {
        my_sleep(retry_sleep);
        continue;
      }
7204
      ERR_RETURN(ndb->getNdbError());
7205 7206
    }

7207
    NdbScanOperation* pOp= pTrans->getNdbScanOperation(ndbtab);
unknown's avatar
unknown committed
7208 7209 7210
    if (pOp == NULL)
      break;
    
7211
    if (pOp->readTuples(NdbOperation::LM_CommittedRead))
unknown's avatar
unknown committed
7212 7213 7214 7215 7216 7217
      break;
    
    int check= pOp->interpret_exit_last_row();
    if (check == -1)
      break;
    
unknown's avatar
unknown committed
7218 7219
    Uint64 rows, commits, mem;
    Uint32 size;
unknown's avatar
unknown committed
7220 7221
    pOp->getValue(NdbDictionary::Column::ROW_COUNT, (char*)&rows);
    pOp->getValue(NdbDictionary::Column::COMMIT_COUNT, (char*)&commits);
7222 7223
    pOp->getValue(NdbDictionary::Column::ROW_SIZE, (char*)&size);
    pOp->getValue(NdbDictionary::Column::FRAGMENT_MEMORY, (char*)&mem);
unknown's avatar
unknown committed
7224
    
7225
    check= pTrans->execute(NdbTransaction::NoCommit,
7226 7227
                           NdbTransaction::AbortOnError,
                           TRUE);
unknown's avatar
unknown committed
7228
    if (check == -1)
7229 7230 7231 7232 7233 7234 7235 7236 7237
    {
      if (pTrans->getNdbError().status == NdbError::TemporaryError &&
          retries--)
      {
        ndb->closeTransaction(pTrans);
        pTrans= 0;
        my_sleep(retry_sleep);
        continue;
      }
unknown's avatar
unknown committed
7238
      break;
7239 7240
    }

7241
    Uint32 count= 0;
unknown's avatar
unknown committed
7242 7243
    Uint64 sum_rows= 0;
    Uint64 sum_commits= 0;
7244 7245
    Uint64 sum_row_size= 0;
    Uint64 sum_mem= 0;
unknown's avatar
unknown committed
7246
    while ((check= pOp->nextResult(TRUE, TRUE)) == 0)
unknown's avatar
unknown committed
7247 7248 7249
    {
      sum_rows+= rows;
      sum_commits+= commits;
7250
      if (sum_row_size < size)
7251
        sum_row_size= size;
7252
      sum_mem+= mem;
7253
      count++;
unknown's avatar
unknown committed
7254 7255 7256 7257 7258
    }
    
    if (check == -1)
      break;

7259
    pOp->close(TRUE);
unknown's avatar
unknown committed
7260

unknown's avatar
unknown committed
7261
    ndb->closeTransaction(pTrans);
7262 7263 7264 7265 7266 7267

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

7268 7269 7270 7271 7272
    DBUG_PRINT("exit", ("records: %llu commits: %llu "
                        "row_size: %llu mem: %llu count: %u",
			sum_rows, sum_commits, sum_row_size,
                        sum_mem, count));

unknown's avatar
unknown committed
7273
    DBUG_RETURN(0);
7274
  } while(1);
unknown's avatar
unknown committed
7275

unknown's avatar
unknown committed
7276 7277
  if (pTrans)
    ndb->closeTransaction(pTrans);
unknown's avatar
unknown committed
7278 7279 7280 7281
  DBUG_PRINT("exit", ("failed"));
  DBUG_RETURN(-1);
}

7282 7283 7284 7285 7286
/*
  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
7287
int ha_ndbcluster::write_ndb_file(const char *name)
7288 7289 7290 7291 7292 7293
{
  File file;
  bool error=1;
  char path[FN_REFLEN];
  
  DBUG_ENTER("write_ndb_file");
unknown's avatar
unknown committed
7294
  DBUG_PRINT("enter", ("name: %s", name));
7295

unknown's avatar
unknown committed
7296
  (void)strxnmov(path, FN_REFLEN-1, 
unknown's avatar
unknown committed
7297
                 mysql_data_home,"/",name,ha_ndb_ext,NullS);
7298 7299 7300 7301 7302 7303 7304 7305 7306 7307

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

7308
int
7309
ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
7310 7311 7312 7313
                                      KEY_MULTI_RANGE *ranges, 
                                      uint range_count,
                                      bool sorted, 
                                      HANDLER_BUFFER *buffer)
7314 7315
{
  DBUG_ENTER("ha_ndbcluster::read_multi_range_first");
7316
  m_write_op= FALSE;
unknown's avatar
unknown committed
7317
  
7318 7319
  int res;
  KEY* key_info= table->key_info + active_index;
7320
  NDB_INDEX_TYPE index_type= get_index_type(active_index);
unknown's avatar
unknown committed
7321
  ulong reclength= table_share->reclength;
7322 7323
  NdbOperation* op;

7324
  if (uses_blob_value())
unknown's avatar
unknown committed
7325 7326 7327 7328
  {
    /**
     * blobs can't be batched currently
     */
unknown's avatar
unknown committed
7329
    m_disable_multi_read= TRUE;
unknown's avatar
unknown committed
7330
    DBUG_RETURN(handler::read_multi_range_first(found_range_p, 
7331 7332 7333 7334
                                                ranges, 
                                                range_count,
                                                sorted, 
                                                buffer));
unknown's avatar
unknown committed
7335 7336
  }

unknown's avatar
unknown committed
7337
  m_disable_multi_read= FALSE;
7338 7339 7340 7341

  /**
   * Copy arguments into member variables
   */
7342 7343 7344
  m_multi_ranges= ranges;
  multi_range_curr= ranges;
  multi_range_end= ranges+range_count;
7345 7346 7347
  multi_range_sorted= sorted;
  multi_range_buffer= buffer;

7348 7349 7350 7351 7352 7353 7354 7355 7356 7357 7358
  /**
   * 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
7359 7360
   */   

unknown's avatar
unknown committed
7361
  /**
7362 7363
   * Variables for loop
   */
unknown's avatar
unknown committed
7364 7365
  byte *curr= (byte*)buffer->buffer;
  byte *end_of_buffer= (byte*)buffer->buffer_end;
7366 7367
  NdbOperation::LockMode lm= 
    (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
7368 7369 7370
  const NDBTAB *tab= m_table;
  const NDBINDEX *unique_idx= m_index[active_index].unique_index;
  const NDBINDEX *idx= m_index[active_index].index; 
7371 7372
  const NdbOperation* lastOp= m_active_trans->getLastDefinedOperation();
  NdbIndexScanOperation* scanOp= 0;
unknown's avatar
unknown committed
7373 7374
  for (; multi_range_curr<multi_range_end && curr+reclength <= end_of_buffer; 
       multi_range_curr++)
7375
  {
7376 7377 7378 7379 7380 7381
    part_id_range part_spec;
    if (m_use_partition_function)
    {
      get_partition_set(table, curr, active_index,
                        &multi_range_curr->start_key,
                        &part_spec);
7382 7383 7384 7385 7386 7387
      DBUG_PRINT("info", ("part_spec.start_part = %u, part_spec.end_part = %u",
                          part_spec.start_part, part_spec.end_part));
      /*
        If partition pruning has found no partition in set
        we can skip this scan
      */
7388 7389 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399
      if (part_spec.start_part > part_spec.end_part)
      {
        /*
          We can skip this partition since the key won't fit into any
          partition
        */
        curr += reclength;
        multi_range_curr->range_flag |= SKIP_RANGE;
        continue;
      }
    }
    switch(index_type){
unknown's avatar
unknown committed
7400 7401
    case PRIMARY_KEY_ORDERED_INDEX:
      if (!(multi_range_curr->start_key.length == key_info->key_length &&
7402 7403 7404
          multi_range_curr->start_key.flag == HA_READ_KEY_EXACT))
        goto range;
      // else fall through
7405
    case PRIMARY_KEY_INDEX:
7406
    {
7407
      multi_range_curr->range_flag |= UNIQUE_RANGE;
7408
      if ((op= m_active_trans->getNdbOperation(tab)) && 
7409 7410 7411
          !op->readTuple(lm) && 
          !set_primary_key(op, multi_range_curr->start_key.key) &&
          !define_read_attrs(curr, op) &&
7412 7413 7414
          (op->setAbortOption(AO_IgnoreError), TRUE) &&
          (!m_use_partition_function ||
           (op->setPartitionId(part_spec.start_part), true)))
7415
        curr += reclength;
7416
      else
7417
        ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
7418
      break;
7419 7420
    }
    break;
unknown's avatar
unknown committed
7421 7422
    case UNIQUE_ORDERED_INDEX:
      if (!(multi_range_curr->start_key.length == key_info->key_length &&
7423 7424 7425 7426 7427
          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
7428
    case UNIQUE_INDEX:
7429
    {
7430
      multi_range_curr->range_flag |= UNIQUE_RANGE;
7431
      if ((op= m_active_trans->getNdbIndexOperation(unique_idx, tab)) && 
7432 7433 7434
          !op->readTuple(lm) && 
          !set_index_key(op, key_info, multi_range_curr->start_key.key) &&
          !define_read_attrs(curr, op) &&
unknown's avatar
unknown committed
7435
          (op->setAbortOption(AO_IgnoreError), TRUE))
7436
        curr += reclength;
7437
      else
7438
        ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
7439 7440
      break;
    }
7441
    case ORDERED_INDEX: {
7442
  range:
7443
      multi_range_curr->range_flag &= ~(uint)UNIQUE_RANGE;
7444 7445
      if (scanOp == 0)
      {
7446 7447 7448 7449 7450 7451
        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
7452
          if (scanOp->reset_bounds(m_force_send))
7453 7454 7455 7456 7457
            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
7458
                 &&!scanOp->readTuples(lm, 0, parallelism, sorted, FALSE, TRUE)
7459 7460 7461 7462 7463 7464 7465 7466 7467 7468 7469
                 &&!generate_scan_filter(m_cond_stack, scanOp)
                 &&!define_read_attrs(end_of_buffer-reclength, scanOp))
        {
          m_multi_cursor= scanOp;
          m_multi_range_cursor_result_ptr= end_of_buffer-reclength;
        }
        else
        {
          ERR_RETURN(scanOp ? scanOp->getNdbError() : 
                     m_active_trans->getNdbError());
        }
7470
      }
7471

7472
      const key_range *keys[2]= { &multi_range_curr->start_key, 
7473
                                  &multi_range_curr->end_key };
7474 7475
      if ((res= set_bounds(scanOp, active_index, false, keys,
                           multi_range_curr-ranges)))
7476
        DBUG_RETURN(res);
7477
      break;
7478
    }
unknown's avatar
unknown committed
7479
    case UNDEFINED_INDEX:
unknown's avatar
unknown committed
7480 7481 7482 7483
      DBUG_ASSERT(FALSE);
      DBUG_RETURN(1);
      break;
    }
7484 7485
  }
  
7486
  if (multi_range_curr != multi_range_end)
7487
  {
7488 7489 7490 7491 7492 7493
    /**
     * 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
     */
7494
    buffer->end_of_used_area= (byte*)buffer->buffer_end;
7495 7496 7497 7498 7499 7500 7501 7502 7503 7504 7505
  }
  else
  {
    buffer->end_of_used_area= curr;
  }
  
  /**
   * Set first operation in multi range
   */
  m_current_multi_operation= 
    lastOp ? lastOp->next() : m_active_trans->getFirstDefinedOperation();
7506
  if (!(res= execute_no_commit_ie(this, m_active_trans)))
7507
  {
7508 7509
    m_multi_range_defined= multi_range_curr;
    multi_range_curr= ranges;
7510 7511
    m_multi_range_result_ptr= (byte*)buffer->buffer;
    DBUG_RETURN(read_multi_range_next(found_range_p));
7512 7513 7514 7515
  }
  ERR_RETURN(m_active_trans->getNdbError());
}

unknown's avatar
unknown committed
7516
#if 0
7517
#define DBUG_MULTI_RANGE(x) DBUG_PRINT("info", ("read_multi_range_next: case %d\n", x));
unknown's avatar
unknown committed
7518 7519 7520 7521
#else
#define DBUG_MULTI_RANGE(x)
#endif

7522
int
7523
ha_ndbcluster::read_multi_range_next(KEY_MULTI_RANGE ** multi_range_found_p)
7524 7525
{
  DBUG_ENTER("ha_ndbcluster::read_multi_range_next");
7526
  if (m_disable_multi_read)
7527
  {
7528
    DBUG_MULTI_RANGE(11);
7529
    DBUG_RETURN(handler::read_multi_range_next(multi_range_found_p));
7530
  }
7531
  
7532
  int res;
7533
  int range_no;
unknown's avatar
unknown committed
7534
  ulong reclength= table_share->reclength;
7535
  const NdbOperation* op= m_current_multi_operation;
unknown's avatar
unknown committed
7536
  for (;multi_range_curr < m_multi_range_defined; multi_range_curr++)
7537
  {
7538 7539 7540
    DBUG_MULTI_RANGE(12);
    if (multi_range_curr->range_flag & SKIP_RANGE)
      continue;
7541
    if (multi_range_curr->range_flag & UNIQUE_RANGE)
7542
    {
7543
      if (op->getNdbError().code == 0)
7544 7545
      {
        DBUG_MULTI_RANGE(13);
7546
        goto found_next;
7547
      }
7548 7549 7550
      
      op= m_active_trans->getNextCompletedOperation(op);
      m_multi_range_result_ptr += reclength;
7551
      continue;
7552
    } 
7553
    else if (m_multi_cursor && !multi_range_sorted)
7554
    {
unknown's avatar
unknown committed
7555 7556
      DBUG_MULTI_RANGE(1);
      if ((res= fetch_next(m_multi_cursor)) == 0)
7557
      {
7558 7559 7560
        DBUG_MULTI_RANGE(2);
        range_no= m_multi_cursor->get_range_no();
        goto found;
7561 7562 7563
      } 
      else
      {
7564
        DBUG_MULTI_RANGE(14);
7565
        goto close_scan;
7566 7567
      }
    }
unknown's avatar
unknown committed
7568
    else if (m_multi_cursor && multi_range_sorted)
7569
    {
unknown's avatar
unknown committed
7570 7571
      if (m_active_cursor && (res= fetch_next(m_multi_cursor)))
      {
7572 7573
        DBUG_MULTI_RANGE(3);
        goto close_scan;
unknown's avatar
unknown committed
7574
      }
7575
      
7576
      range_no= m_multi_cursor->get_range_no();
7577
      uint current_range_no= multi_range_curr - m_multi_ranges;
unknown's avatar
unknown committed
7578
      if ((uint) range_no == current_range_no)
7579
      {
7580
        DBUG_MULTI_RANGE(4);
7581
        // return current row
7582
        goto found;
7583
      }
7584
      else if (range_no > (int)current_range_no)
7585
      {
7586 7587 7588 7589
        DBUG_MULTI_RANGE(5);
        // wait with current row
        m_active_cursor= 0;
        continue;
7590 7591 7592
      }
      else 
      {
7593 7594 7595
        DBUG_MULTI_RANGE(6);
        // First fetch from cursor
        DBUG_ASSERT(range_no == -1);
unknown's avatar
unknown committed
7596
        if ((res= m_multi_cursor->nextResult(true)))
7597
        {
7598
          DBUG_MULTI_RANGE(15);
7599 7600 7601 7602
          goto close_scan;
        }
        multi_range_curr--; // Will be increased in for-loop
        continue;
7603
      }
7604
    }
unknown's avatar
unknown committed
7605
    else /** m_multi_cursor == 0 */
7606
    {
unknown's avatar
unknown committed
7607
      DBUG_MULTI_RANGE(7);
7608 7609 7610 7611
      /**
       * Corresponds to range 5 in example in read_multi_range_first
       */
      (void)1;
7612
      continue;
7613
    }
7614
    
unknown's avatar
unknown committed
7615
    DBUG_ASSERT(FALSE); // Should only get here via goto's
7616 7617 7618
close_scan:
    if (res == 1)
    {
unknown's avatar
unknown committed
7619
      m_multi_cursor->close(FALSE, TRUE);
7620
      m_active_cursor= m_multi_cursor= 0;
unknown's avatar
unknown committed
7621
      DBUG_MULTI_RANGE(8);
7622 7623 7624 7625
      continue;
    } 
    else 
    {
7626
      DBUG_MULTI_RANGE(9);
7627 7628 7629
      DBUG_RETURN(ndb_err(m_active_trans));
    }
  }
7630
  
7631
  if (multi_range_curr == multi_range_end)
7632 7633
  {
    DBUG_MULTI_RANGE(16);
7634
    DBUG_RETURN(HA_ERR_END_OF_FILE);
7635
  }
7636
  
7637 7638 7639 7640
  /**
   * Read remaining ranges
   */
  DBUG_RETURN(read_multi_range_first(multi_range_found_p, 
7641 7642 7643 7644
                                     multi_range_curr,
                                     multi_range_end - multi_range_curr, 
                                     multi_range_sorted,
                                     multi_range_buffer));
7645 7646
  
found:
7647 7648 7649
  /**
   * Found a record belonging to a scan
   */
7650
  m_active_cursor= m_multi_cursor;
7651
  * multi_range_found_p= m_multi_ranges + range_no;
7652 7653
  memcpy(table->record[0], m_multi_range_cursor_result_ptr, reclength);
  setup_recattr(m_active_cursor->getFirstRecAttr());
7654 7655 7656
  unpack_record(table->record[0]);
  table->status= 0;     
  DBUG_RETURN(0);
7657
  
7658
found_next:
7659 7660 7661 7662
  /**
   * Found a record belonging to a pk/index op,
   *   copy result and move to next to prepare for next call
   */
7663
  * multi_range_found_p= multi_range_curr;
7664
  memcpy(table->record[0], m_multi_range_result_ptr, reclength);
7665
  setup_recattr(op->getFirstRecAttr());
7666
  unpack_record(table->record[0]);
7667 7668
  table->status= 0;
  
7669
  multi_range_curr++;
7670
  m_current_multi_operation= m_active_trans->getNextCompletedOperation(op);
7671 7672
  m_multi_range_result_ptr += reclength;
  DBUG_RETURN(0);
7673 7674
}

7675 7676 7677 7678 7679 7680 7681 7682
int
ha_ndbcluster::setup_recattr(const NdbRecAttr* curr)
{
  DBUG_ENTER("setup_recattr");

  Field **field, **end;
  NdbValue *value= m_value;
  
unknown's avatar
unknown committed
7683
  end= table->field + table_share->fields;
7684 7685 7686 7687 7688 7689
  
  for (field= table->field; field < end; field++, value++)
  {
    if ((* value).ptr)
    {
      DBUG_ASSERT(curr != 0);
unknown's avatar
unknown committed
7690 7691 7692
      NdbValue* val= m_value + curr->getColumn()->getColumnNo();
      DBUG_ASSERT(val->ptr);
      val->rec= curr;
7693
      curr= curr->next();
7694 7695 7696
    }
  }
  
unknown's avatar
unknown committed
7697
  DBUG_RETURN(0);
7698 7699
}

unknown's avatar
Merge  
unknown committed
7700 7701
char*
ha_ndbcluster::update_table_comment(
7702 7703
                                /* out: table comment + additional */
        const char*     comment)/* in:  table comment defined by user */
unknown's avatar
Merge  
unknown committed
7704 7705
{
  uint length= strlen(comment);
unknown's avatar
unknown committed
7706
  if (length > 64000 - 3)
unknown's avatar
Merge  
unknown committed
7707 7708 7709 7710 7711 7712 7713 7714 7715 7716 7717 7718
  {
    return((char*)comment); /* string too long */
  }

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

  ndb->setDatabaseName(m_dbname);
  NDBDICT* dict= ndb->getDictionary();
7719 7720
  const NDBTAB* tab= m_table;
  DBUG_ASSERT(tab != NULL);
unknown's avatar
Merge  
unknown committed
7721 7722 7723 7724 7725 7726 7727 7728 7729

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

unknown's avatar
unknown committed
7730 7731 7732
  my_snprintf(str,fmt_len_plus_extra,fmt,comment,
              length > 0 ? " ":"",
              tab->getReplicaCount());
unknown's avatar
Merge  
unknown committed
7733 7734 7735 7736 7737
  return str;
}


// Utility thread main loop
7738
pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
unknown's avatar
Merge  
unknown committed
7739 7740
{
  THD *thd; /* needs to be first for thread_stack */
7741
  Ndb* ndb;
unknown's avatar
Merge  
unknown committed
7742
  struct timespec abstime;
unknown's avatar
unknown committed
7743
  List<NDB_SHARE> util_open_tables;
unknown's avatar
Merge  
unknown committed
7744 7745 7746 7747 7748 7749 7750

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

  thd= new THD; /* note that contructor of THD uses DBUG_ */
  THD_CHECK_SENTRY(thd);
7751
  ndb= new Ndb(g_ndb_cluster_connection, "");
unknown's avatar
Merge  
unknown committed
7752 7753 7754 7755 7756

  pthread_detach_this_thread();
  ndb_util_thread= pthread_self();

  thd->thread_stack= (char*)&thd; /* remember where our stack is */
unknown's avatar
unknown committed
7757
  if (thd->store_globals() || (ndb->init() != 0))
unknown's avatar
Merge  
unknown committed
7758 7759 7760
  {
    thd->cleanup();
    delete thd;
7761
    delete ndb;
unknown's avatar
Merge  
unknown committed
7762 7763
    DBUG_RETURN(NULL);
  }
unknown's avatar
unknown committed
7764 7765 7766 7767 7768 7769 7770 7771 7772 7773 7774 7775 7776 7777 7778 7779 7780
  thd->init_for_queries();
  thd->version=refresh_version;
  thd->set_time();
  thd->main_security_ctx.host_or_ip= "";
  thd->client_capabilities = 0;
  my_net_init(&thd->net, 0);
  thd->main_security_ctx.master_access= ~0;
  thd->main_security_ctx.priv_user = 0;

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

7781 7782
  ndbcluster_util_inited= 1;

unknown's avatar
unknown committed
7783 7784 7785 7786
  /*
    Wait for cluster to start
  */
  pthread_mutex_lock(&LOCK_ndb_util_thread);
unknown's avatar
unknown committed
7787
  while (!ndb_cluster_node_id && (ndbcluster_hton.slot != ~(uint)0))
unknown's avatar
unknown committed
7788 7789 7790 7791 7792 7793 7794 7795 7796 7797 7798 7799 7800 7801
  {
    /* ndb not connected yet */
    set_timespec(abstime, 1);
    pthread_cond_timedwait(&COND_ndb_util_thread,
                           &LOCK_ndb_util_thread,
                           &abstime);
    if (abort_loop)
    {
      pthread_mutex_unlock(&LOCK_ndb_util_thread);
      goto ndb_util_thread_end;
    }
  }
  pthread_mutex_unlock(&LOCK_ndb_util_thread);

unknown's avatar
unknown committed
7802 7803 7804 7805 7806 7807 7808 7809 7810 7811 7812 7813
  {
    Thd_ndb *thd_ndb;
    if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
    {
      sql_print_error("Could not allocate Thd_ndb object");
      goto ndb_util_thread_end;
    }
    set_thd_ndb(thd, thd_ndb);
    thd_ndb->options|= TNO_NO_LOG_SCHEMA_OP;
  }

#ifdef HAVE_NDB_BINLOG
7814 7815
  if (ndb_extra_logging && ndb_binlog_running)
    sql_print_information("NDB Binlog: Ndb tables initially read only.");
unknown's avatar
unknown committed
7816 7817 7818
  /* create tables needed by the replication */
  ndbcluster_setup_binlog_table_shares(thd);
#else
unknown's avatar
unknown committed
7819 7820 7821 7822
  /*
    Get all table definitions from the storage node
  */
  ndbcluster_find_all_files(thd);
unknown's avatar
unknown committed
7823
#endif
unknown's avatar
unknown committed
7824

7825
  set_timespec(abstime, 0);
unknown's avatar
unknown committed
7826
  for (;!abort_loop;)
unknown's avatar
Merge  
unknown committed
7827 7828
  {
    pthread_mutex_lock(&LOCK_ndb_util_thread);
unknown's avatar
unknown committed
7829 7830 7831
    pthread_cond_timedwait(&COND_ndb_util_thread,
                           &LOCK_ndb_util_thread,
                           &abstime);
unknown's avatar
Merge  
unknown committed
7832
    pthread_mutex_unlock(&LOCK_ndb_util_thread);
unknown's avatar
unknown committed
7833
#ifdef NDB_EXTRA_DEBUG_UTIL_THREAD
unknown's avatar
Merge  
unknown committed
7834 7835
    DBUG_PRINT("ndb_util_thread", ("Started, ndb_cache_check_time: %d",
                                   ndb_cache_check_time));
unknown's avatar
unknown committed
7836
#endif
unknown's avatar
Merge  
unknown committed
7837 7838 7839
    if (abort_loop)
      break; /* Shutting down server */

unknown's avatar
unknown committed
7840 7841 7842 7843 7844
#ifdef HAVE_NDB_BINLOG
    /*
      Check that the apply_status_share and schema_share has been created.
      If not try to create it
    */
7845
    if (!ndb_binlog_tables_inited)
unknown's avatar
unknown committed
7846 7847 7848
      ndbcluster_setup_binlog_table_shares(thd);
#endif

unknown's avatar
Merge  
unknown committed
7849 7850
    if (ndb_cache_check_time == 0)
    {
7851 7852
      /* Wake up in 1 second to check if value has changed */
      set_timespec(abstime, 1);
unknown's avatar
Merge  
unknown committed
7853 7854 7855 7856 7857 7858 7859 7860 7861
      continue;
    }

    /* Lock mutex and fill list with pointers to all open tables */
    NDB_SHARE *share;
    pthread_mutex_lock(&ndbcluster_mutex);
    for (uint i= 0; i < ndbcluster_open_tables.records; i++)
    {
      share= (NDB_SHARE *)hash_element(&ndbcluster_open_tables, i);
unknown's avatar
unknown committed
7862 7863 7864 7865 7866 7867
#ifdef HAVE_NDB_BINLOG
      if ((share->use_count - (int) (share->op != 0) - (int) (share->op != 0))
          <= 0)
        continue; // injector thread is the only user, skip statistics
      share->util_lock= current_thd; // Mark that util thread has lock
#endif /* HAVE_NDB_BINLOG */
unknown's avatar
Merge  
unknown committed
7868 7869 7870 7871 7872 7873 7874 7875 7876 7877 7878 7879
      share->use_count++; /* Make sure the table can't be closed */
      DBUG_PRINT("ndb_util_thread",
                 ("Found open table[%d]: %s, use_count: %d",
                  i, share->table_name, share->use_count));

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

    /* Iterate through the  open files list */
    List_iterator_fast<NDB_SHARE> it(util_open_tables);
7880
    while ((share= it++))
unknown's avatar
Merge  
unknown committed
7881
    {
unknown's avatar
unknown committed
7882 7883 7884 7885 7886 7887 7888 7889 7890 7891 7892
#ifdef HAVE_NDB_BINLOG
      if ((share->use_count - (int) (share->op != 0) - (int) (share->op != 0))
          <= 1)
      {
        /*
          Util thread and injector thread is the only user, skip statistics
	*/
        free_share(&share);
        continue;
      }
#endif /* HAVE_NDB_BINLOG */
unknown's avatar
Merge  
unknown committed
7893
      DBUG_PRINT("ndb_util_thread",
7894
                 ("Fetching commit count for: %s",
unknown's avatar
unknown committed
7895
                  share->key));
unknown's avatar
Merge  
unknown committed
7896 7897

      /* Contact NDB to get commit count for table */
unknown's avatar
unknown committed
7898
      ndb->setDatabaseName(share->db);
7899 7900 7901 7902 7903 7904 7905
      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
7906
      {
7907 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917 7918 7919 7920 7921
        Ndb_table_guard ndbtab_g(ndb->getDictionary(), share->table_name);
        if (ndbtab_g.get_table() &&
            ndb_get_table_statistics(ndb, ndbtab_g.get_table(), &stat) == 0)
        {
          DBUG_PRINT("ndb_util_thread",
                     ("Table: %s, commit_count: %llu, rows: %llu",
                      share->key, stat.commit_count, stat.row_count));
        }
        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
7922
      }
7923 7924 7925 7926 7927 7928

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

unknown's avatar
Merge  
unknown committed
7929
      /* Decrease the use count and possibly free share */
unknown's avatar
unknown committed
7930
      free_share(&share);
unknown's avatar
Merge  
unknown committed
7931 7932 7933 7934 7935
    }

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

7936 7937 7938 7939 7940 7941 7942 7943 7944
    /* 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
7945
    if (msecs >= 1000){
7946 7947 7948 7949 7950 7951 7952 7953 7954 7955
      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
7956
  }
unknown's avatar
unknown committed
7957
ndb_util_thread_end:
unknown's avatar
unknown committed
7958
  sql_print_information("Stopping Cluster Utility thread");
unknown's avatar
unknown committed
7959
  net_end(&thd->net);
unknown's avatar
Merge  
unknown committed
7960 7961
  thd->cleanup();
  delete thd;
7962
  delete ndb;
unknown's avatar
Merge  
unknown committed
7963 7964 7965 7966 7967 7968
  DBUG_PRINT("exit", ("ndb_util_thread"));
  my_thread_end();
  pthread_exit(0);
  DBUG_RETURN(NULL);
}

7969 7970 7971
/*
  Condition pushdown
*/
7972 7973 7974 7975 7976 7977 7978 7979 7980 7981 7982 7983 7984 7985 7986 7987 7988
/*
  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)
*/
7989 7990 7991 7992 7993
const 
COND* 
ha_ndbcluster::cond_push(const COND *cond) 
{ 
  DBUG_ENTER("cond_push");
7994 7995 7996
  Ndb_cond_stack *ndb_cond = new Ndb_cond_stack();
  DBUG_EXECUTE("where",print_where((COND *)cond, m_tabname););
  if (m_cond_stack)
unknown's avatar
unknown committed
7997
    ndb_cond->next= m_cond_stack;
7998 7999 8000 8001 8002 8003
  else
    ndb_cond->next= NULL;
  m_cond_stack= ndb_cond;
  
  if (serialize_cond(cond, ndb_cond))
  {
unknown's avatar
unknown committed
8004
    DBUG_RETURN(NULL);
8005 8006 8007 8008
  }
  else
  {
    cond_pop();
unknown's avatar
unknown committed
8009
  }
8010 8011 8012
  DBUG_RETURN(cond); 
}

8013 8014 8015
/*
  Pop the top condition from the condition stack of the handler instance.
*/
8016 8017 8018 8019 8020 8021 8022 8023 8024
void 
ha_ndbcluster::cond_pop() 
{ 
  Ndb_cond_stack *ndb_cond_stack= m_cond_stack;  
  if (ndb_cond_stack)
  {
    m_cond_stack= ndb_cond_stack->next;
    delete ndb_cond_stack;
  }
unknown's avatar
unknown committed
8025
}
8026

8027 8028 8029
/*
  Clear the condition stack
*/
8030 8031 8032 8033 8034 8035 8036 8037 8038 8039
void
ha_ndbcluster::cond_clear()
{
  DBUG_ENTER("cond_clear");
  while (m_cond_stack)
    cond_pop();

  DBUG_VOID_RETURN;
}

8040 8041 8042 8043 8044 8045
/*
  Serialize the item tree into a linked list represented by Ndb_cond
  for fast generation of NbdScanFilter. Adds information such as
  position of fields that is not directly available in the Item tree.
  Also checks if condition is supported.
*/
8046 8047 8048 8049 8050
void ndb_serialize_cond(const Item *item, void *arg)
{
  Ndb_cond_traverse_context *context= (Ndb_cond_traverse_context *) arg;
  DBUG_ENTER("ndb_serialize_cond");  

unknown's avatar
unknown committed
8051 8052 8053 8054 8055
  // Check if we are skipping arguments to a function to be evaluated
  if (context->skip)
  {
    DBUG_PRINT("info", ("Skiping argument %d", context->skip));
    context->skip--;
unknown's avatar
unknown committed
8056 8057 8058
    switch (item->type()) {
    case Item::FUNC_ITEM:
    {
unknown's avatar
unknown committed
8059 8060 8061 8062
      Item_func *func_item= (Item_func *) item;
      context->skip+= func_item->argument_count();
      break;
    }
unknown's avatar
unknown committed
8063 8064 8065 8066 8067
    case Item::INT_ITEM:
    case Item::REAL_ITEM:
    case Item::STRING_ITEM:
    case Item::VARBIN_ITEM:
    case Item::DECIMAL_ITEM:
unknown's avatar
unknown committed
8068 8069
      break;
    default:
8070
      context->supported= FALSE;
unknown's avatar
unknown committed
8071 8072
      break;
    }
8073
    
unknown's avatar
unknown committed
8074 8075 8076
    DBUG_VOID_RETURN;
  }
  
8077
  if (context->supported)
8078
  {
8079 8080 8081 8082 8083 8084
    Ndb_rewrite_context *rewrite_context= context->rewrite_stack;
    const Item_func *func_item;
    // Check if we are rewriting some unsupported function call
    if (rewrite_context &&
        (func_item= rewrite_context->func_item) &&
        rewrite_context->count++ == 0)
unknown's avatar
unknown committed
8085
    {
unknown's avatar
unknown committed
8086 8087
      switch (func_item->functype()) {
      case Item_func::BETWEEN:
8088
        /*
8089 8090 8091 8092 8093 8094 8095
          Rewrite 
          <field>|<const> BETWEEN <const1>|<field1> AND <const2>|<field2>
          to <field>|<const> > <const1>|<field1> AND 
          <field>|<const> < <const2>|<field2>
          or actually in prefix format
          BEGIN(AND) GT(<field>|<const>, <const1>|<field1>), 
          LT(<field>|<const>, <const2>|<field2>), END()
8096
        */
unknown's avatar
unknown committed
8097 8098
      case Item_func::IN_FUNC:
      {
8099 8100 8101 8102 8103 8104 8105 8106 8107 8108 8109 8110 8111 8112 8113 8114
        /*
          Rewrite <field>|<const> IN(<const1>|<field1>, <const2>|<field2>,..)
          to <field>|<const> = <const1>|<field1> OR 
          <field> = <const2>|<field2> ...
          or actually in prefix format
          BEGIN(OR) EQ(<field>|<const>, <const1><field1>), 
          EQ(<field>|<const>, <const2>|<field2>), ... END()
          Each part of the disjunction is added for each call
          to ndb_serialize_cond and end of rewrite statement 
          is wrapped in end of ndb_serialize_cond
        */
        if (context->expecting(item->type()))
        {
          // This is the <field>|<const> item, save it in the rewrite context
          rewrite_context->left_hand_item= item;
          if (item->type() == Item::FUNC_ITEM)
8115
          {
8116 8117 8118
            Item_func *func_item= (Item_func *) item;
            if (func_item->functype() == Item_func::UNKNOWN_FUNC &&
                func_item->const_item())
8119
            {
8120 8121 8122
              // Skip any arguments since we will evaluate function instead
              DBUG_PRINT("info", ("Skip until end of arguments marker"));
              context->skip= func_item->argument_count();
8123 8124 8125
            }
            else
            {
8126 8127 8128 8129
              DBUG_PRINT("info", ("Found unsupported functional expression in BETWEEN|IN"));
              context->supported= FALSE;
              DBUG_VOID_RETURN;
              
8130 8131 8132
            }
          }
        }
8133 8134
        else
        {
8135 8136 8137
          // Non-supported BETWEEN|IN expression
          DBUG_PRINT("info", ("Found unexpected item of type %u in BETWEEN|IN",
                              item->type()));
8138
          context->supported= FALSE;
8139
          DBUG_VOID_RETURN;
8140
        }
8141 8142 8143 8144 8145 8146 8147 8148 8149 8150 8151 8152 8153 8154 8155 8156 8157 8158 8159 8160 8161 8162
        break;
      }
      default:
        context->supported= FALSE;
        break;
      }
      DBUG_VOID_RETURN;
    }
    else
    {
      Ndb_cond_stack *ndb_stack= context->stack_ptr;
      Ndb_cond *prev_cond= context->cond_ptr;
      Ndb_cond *curr_cond= context->cond_ptr= new Ndb_cond();
      if (!ndb_stack->ndb_cond)
        ndb_stack->ndb_cond= curr_cond;
      curr_cond->prev= prev_cond;
      if (prev_cond) prev_cond->next= curr_cond;
    // Check if we are rewriting some unsupported function call
      if (context->rewrite_stack)
      {
        Ndb_rewrite_context *rewrite_context= context->rewrite_stack;
        const Item_func *func_item= rewrite_context->func_item;
unknown's avatar
unknown committed
8163 8164 8165 8166 8167 8168 8169 8170 8171 8172 8173 8174
        switch (func_item->functype()) {
        case Item_func::BETWEEN:
        {
          /*
            Rewrite 
            <field>|<const> BETWEEN <const1>|<field1> AND <const2>|<field2>
            to <field>|<const> > <const1>|<field1> AND 
            <field>|<const> < <const2>|<field2>
            or actually in prefix format
            BEGIN(AND) GT(<field>|<const>, <const1>|<field1>), 
            LT(<field>|<const>, <const2>|<field2>), END()
          */
8175 8176 8177 8178 8179 8180 8181 8182 8183 8184 8185 8186 8187 8188 8189 8190 8191 8192 8193
          if (rewrite_context->count == 2)
          {
            // Lower limit of BETWEEN
            DBUG_PRINT("info", ("GE_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(Item_func::GE_FUNC, 2);
          }
          else if (rewrite_context->count == 3)
          {
            // Upper limit of BETWEEN
            DBUG_PRINT("info", ("LE_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(Item_func::LE_FUNC, 2);
          }
          else
          {
            // Illegal BETWEEN expression
            DBUG_PRINT("info", ("Illegal BETWEEN expression"));
            context->supported= FALSE;
            DBUG_VOID_RETURN;
          }
8194 8195
          break;
        }
unknown's avatar
unknown committed
8196 8197
        case Item_func::IN_FUNC:
        {
8198 8199 8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210
          /*
            Rewrite <field>|<const> IN(<const1>|<field1>, <const2>|<field2>,..)
            to <field>|<const> = <const1>|<field1> OR 
            <field> = <const2>|<field2> ...
            or actually in prefix format
            BEGIN(OR) EQ(<field>|<const>, <const1><field1>), 
            EQ(<field>|<const>, <const2>|<field2>), ... END()
            Each part of the disjunction is added for each call
            to ndb_serialize_cond and end of rewrite statement 
            is wrapped in end of ndb_serialize_cond
          */
          DBUG_PRINT("info", ("EQ_FUNC"));      
          curr_cond->ndb_item= new Ndb_item(Item_func::EQ_FUNC, 2);
8211 8212
          break;
        }
8213 8214
        default:
          context->supported= FALSE;
8215
        }
8216 8217 8218 8219 8220 8221 8222 8223 8224 8225 8226 8227 8228 8229 8230 8231 8232 8233 8234 8235 8236 8237 8238 8239 8240 8241 8242 8243 8244 8245
        // Handle left hand <field>|<const>
        context->rewrite_stack= NULL; // Disable rewrite mode
        context->expect_only(Item::FIELD_ITEM);
        context->expect_field_result(STRING_RESULT);
        context->expect_field_result(REAL_RESULT);
        context->expect_field_result(INT_RESULT);
        context->expect_field_result(DECIMAL_RESULT);
        context->expect(Item::INT_ITEM);
        context->expect(Item::STRING_ITEM);
        context->expect(Item::VARBIN_ITEM);
        context->expect(Item::FUNC_ITEM);
        ndb_serialize_cond(rewrite_context->left_hand_item, arg);
        context->skip= 0; // Any FUNC_ITEM expression has already been parsed
        context->rewrite_stack= rewrite_context; // Enable rewrite mode
        if (!context->supported)
          DBUG_VOID_RETURN;

        prev_cond= context->cond_ptr;
        curr_cond= context->cond_ptr= new Ndb_cond();
        prev_cond->next= curr_cond;
      }
      
      // Check for end of AND/OR expression
      if (!item)
      {
        // End marker for condition group
        DBUG_PRINT("info", ("End of condition group"));
        curr_cond->ndb_item= new Ndb_item(NDB_END_COND);
      }
      else
unknown's avatar
unknown committed
8246 8247 8248 8249
      {
        switch (item->type()) {
        case Item::FIELD_ITEM:
        {
8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261 8262 8263 8264 8265 8266 8267 8268
          Item_field *field_item= (Item_field *) item;
          Field *field= field_item->field;
          enum_field_types type= field->type();
          /*
            Check that the field is part of the table of the handler
            instance and that we expect a field with of this result type.
          */
          if (context->table == field->table)
          {       
            const NDBTAB *tab= (const NDBTAB *) context->ndb_table;
            DBUG_PRINT("info", ("FIELD_ITEM"));
            DBUG_PRINT("info", ("table %s", tab->getName()));
            DBUG_PRINT("info", ("column %s", field->field_name));
            DBUG_PRINT("info", ("result type %d", field->result_type()));
            
            // Check that we are expecting a field and with the correct
            // result type
            if (context->expecting(Item::FIELD_ITEM) &&
                (context->expecting_field_result(field->result_type()) ||
unknown's avatar
unknown committed
8269
                 // Date and year can be written as string or int
8270 8271 8272 8273
                 ((type == MYSQL_TYPE_TIME ||
                   type == MYSQL_TYPE_DATE || 
                   type == MYSQL_TYPE_YEAR ||
                   type == MYSQL_TYPE_DATETIME)
unknown's avatar
unknown committed
8274 8275 8276
                  ? (context->expecting_field_result(STRING_RESULT) ||
                     context->expecting_field_result(INT_RESULT))
                  : true)) &&
8277
                // Bit fields no yet supported in scan filter
8278 8279 8280
                type != MYSQL_TYPE_BIT &&
                // No BLOB support in scan filter
                type != MYSQL_TYPE_TINY_BLOB &&
8281 8282
                type != MYSQL_TYPE_MEDIUM_BLOB &&
                type != MYSQL_TYPE_LONG_BLOB &&
8283
                type != MYSQL_TYPE_BLOB)
8284 8285 8286 8287 8288 8289 8290
            {
              const NDBCOL *col= tab->getColumn(field->field_name);
              DBUG_ASSERT(col);
              curr_cond->ndb_item= new Ndb_item(field, col->getColumnNo());
              context->dont_expect(Item::FIELD_ITEM);
              context->expect_no_field_result();
              if (context->expect_mask)
8291
              {
8292 8293 8294 8295 8296 8297 8298 8299 8300 8301
                // We have not seen second argument yet
                if (type == MYSQL_TYPE_TIME ||
                    type == MYSQL_TYPE_DATE || 
                    type == MYSQL_TYPE_YEAR ||
                    type == MYSQL_TYPE_DATETIME)
                {
                  context->expect_only(Item::STRING_ITEM);
                  context->expect(Item::INT_ITEM);
                }
                else
unknown's avatar
unknown committed
8302 8303
                  switch (field->result_type()) {
                  case STRING_RESULT:
8304 8305 8306 8307 8308
                    // Expect char string or binary string
                    context->expect_only(Item::STRING_ITEM);
                    context->expect(Item::VARBIN_ITEM);
                    context->expect_collation(field_item->collation.collation);
                    break;
unknown's avatar
unknown committed
8309
                  case REAL_RESULT:
8310 8311
                    context->expect_only(Item::REAL_ITEM);
                    context->expect(Item::DECIMAL_ITEM);
8312
                    context->expect(Item::INT_ITEM);
8313
                    break;
unknown's avatar
unknown committed
8314
                  case INT_RESULT:
8315 8316 8317
                    context->expect_only(Item::INT_ITEM);
                    context->expect(Item::VARBIN_ITEM);
                    break;
unknown's avatar
unknown committed
8318
                  case DECIMAL_RESULT:
8319 8320
                    context->expect_only(Item::DECIMAL_ITEM);
                    context->expect(Item::REAL_ITEM);
8321
                    context->expect(Item::INT_ITEM);
8322 8323 8324 8325
                    break;
                  default:
                    break;
                  }    
8326 8327
              }
              else
8328 8329 8330 8331
              {
                // Expect another logical expression
                context->expect_only(Item::FUNC_ITEM);
                context->expect(Item::COND_ITEM);
8332 8333 8334 8335 8336 8337 8338
                // Check that field and string constant collations are the same
                if ((field->result_type() == STRING_RESULT) &&
                    !context->expecting_collation(item->collation.collation)
                    && type != MYSQL_TYPE_TIME
                    && type != MYSQL_TYPE_DATE
                    && type != MYSQL_TYPE_YEAR
                    && type != MYSQL_TYPE_DATETIME)
8339
                {
unknown's avatar
unknown committed
8340
                  DBUG_PRINT("info", ("Found non-matching collation %s",  
8341 8342
                                      item->collation.collation->name)); 
                  context->supported= FALSE;                
8343 8344
                }
              }
8345 8346
              break;
            }
8347 8348
            else
            {
unknown's avatar
unknown committed
8349 8350
              DBUG_PRINT("info", ("Was not expecting field of type %u(%u)",
                                  field->result_type(), type));
8351
              context->supported= FALSE;
8352
            }
8353
          }
8354
          else
8355
          {
unknown's avatar
unknown committed
8356 8357 8358
            DBUG_PRINT("info", ("Was not expecting field from table %s (%s)",
                                context->table->s->table_name.str, 
                                field->table->s->table_name.str));
8359
            context->supported= FALSE;
8360
          }
8361 8362
          break;
        }
unknown's avatar
unknown committed
8363 8364
        case Item::FUNC_ITEM:
        {
8365 8366 8367 8368 8369 8370
          Item_func *func_item= (Item_func *) item;
          // Check that we expect a function or functional expression here
          if (context->expecting(Item::FUNC_ITEM) || 
              func_item->functype() == Item_func::UNKNOWN_FUNC)
            context->expect_nothing();
          else
8371
          {
8372 8373 8374
            // Did not expect function here
            context->supported= FALSE;
            break;
8375
          }
8376
          
unknown's avatar
unknown committed
8377 8378 8379
          switch (func_item->functype()) {
          case Item_func::EQ_FUNC:
          {
8380 8381 8382 8383 8384 8385 8386 8387 8388 8389 8390 8391 8392 8393
            DBUG_PRINT("info", ("EQ_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(), 
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::REAL_ITEM);
            context->expect(Item::DECIMAL_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
8394
          }
unknown's avatar
unknown committed
8395 8396
          case Item_func::NE_FUNC:
          {
8397 8398 8399 8400 8401 8402 8403 8404 8405 8406 8407 8408 8409 8410
            DBUG_PRINT("info", ("NE_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::REAL_ITEM);
            context->expect(Item::DECIMAL_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
8411
          }
unknown's avatar
unknown committed
8412 8413
          case Item_func::LT_FUNC:
          {
8414 8415 8416 8417 8418 8419 8420 8421 8422 8423 8424 8425 8426 8427 8428
            DBUG_PRINT("info", ("LT_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::REAL_ITEM);
            context->expect(Item::DECIMAL_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
8429 8430
          case Item_func::LE_FUNC:
          {
8431 8432 8433 8434 8435 8436 8437 8438 8439 8440 8441 8442 8443 8444 8445
            DBUG_PRINT("info", ("LE_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::REAL_ITEM);
            context->expect(Item::DECIMAL_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
8446 8447
          case Item_func::GE_FUNC:
          {
8448 8449 8450 8451 8452 8453 8454 8455 8456 8457 8458 8459 8460 8461 8462
            DBUG_PRINT("info", ("GE_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::REAL_ITEM);
            context->expect(Item::DECIMAL_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
8463 8464
          case Item_func::GT_FUNC:
          {
8465 8466 8467 8468 8469 8470 8471 8472 8473 8474 8475 8476 8477 8478 8479
            DBUG_PRINT("info", ("GT_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::REAL_ITEM);
            context->expect(Item::DECIMAL_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
8480 8481
          case Item_func::LIKE_FUNC:
          {
8482 8483 8484 8485 8486 8487 8488 8489 8490
            DBUG_PRINT("info", ("LIKE_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::STRING_ITEM);
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect(Item::FUNC_ITEM);
            break;
          }
unknown's avatar
unknown committed
8491 8492
          case Item_func::ISNULL_FUNC:
          {
8493 8494 8495 8496 8497 8498 8499 8500 8501 8502
            DBUG_PRINT("info", ("ISNULL_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);      
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
8503 8504
          case Item_func::ISNOTNULL_FUNC:
          {
8505 8506 8507 8508 8509 8510 8511 8512 8513 8514
            DBUG_PRINT("info", ("ISNOTNULL_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);     
            context->expect(Item::FIELD_ITEM);
            context->expect_field_result(STRING_RESULT);
            context->expect_field_result(REAL_RESULT);
            context->expect_field_result(INT_RESULT);
            context->expect_field_result(DECIMAL_RESULT);
            break;
          }
unknown's avatar
unknown committed
8515 8516
          case Item_func::NOT_FUNC:
          {
8517 8518 8519 8520
            DBUG_PRINT("info", ("NOT_FUNC"));      
            curr_cond->ndb_item= new Ndb_item(func_item->functype(),
                                              func_item);     
            context->expect(Item::FUNC_ITEM);
8521
            context->expect(Item::COND_ITEM);
8522
            break;
8523
          }
unknown's avatar
unknown committed
8524 8525
          case Item_func::BETWEEN:
          {
8526
            DBUG_PRINT("info", ("BETWEEN, rewriting using AND"));
8527
            Item_func_between *between_func= (Item_func_between *) func_item;
8528 8529 8530 8531
            Ndb_rewrite_context *rewrite_context= 
              new Ndb_rewrite_context(func_item);
            rewrite_context->next= context->rewrite_stack;
            context->rewrite_stack= rewrite_context;
8532 8533 8534 8535 8536 8537 8538 8539 8540
            if (between_func->negated)
            {
              DBUG_PRINT("info", ("NOT_FUNC"));
              curr_cond->ndb_item= new Ndb_item(Item_func::NOT_FUNC, 1);
              prev_cond= curr_cond;
              curr_cond= context->cond_ptr= new Ndb_cond();
              curr_cond->prev= prev_cond;
              prev_cond->next= curr_cond;
            }
8541
            DBUG_PRINT("info", ("COND_AND_FUNC"));
8542 8543 8544
            curr_cond->ndb_item= 
              new Ndb_item(Item_func::COND_AND_FUNC, 
                           func_item->argument_count() - 1);
8545
            context->expect_only(Item::FIELD_ITEM);
8546 8547 8548 8549 8550
            context->expect(Item::INT_ITEM);
            context->expect(Item::STRING_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FUNC_ITEM);
            break;
8551
          }
unknown's avatar
unknown committed
8552 8553
          case Item_func::IN_FUNC:
          {
8554
            DBUG_PRINT("info", ("IN_FUNC, rewriting using OR"));
8555
            Item_func_in *in_func= (Item_func_in *) func_item;
8556 8557 8558 8559
            Ndb_rewrite_context *rewrite_context= 
              new Ndb_rewrite_context(func_item);
            rewrite_context->next= context->rewrite_stack;
            context->rewrite_stack= rewrite_context;
8560 8561 8562 8563 8564 8565 8566 8567 8568
            if (in_func->negated)
            {
              DBUG_PRINT("info", ("NOT_FUNC"));
              curr_cond->ndb_item= new Ndb_item(Item_func::NOT_FUNC, 1);
              prev_cond= curr_cond;
              curr_cond= context->cond_ptr= new Ndb_cond();
              curr_cond->prev= prev_cond;
              prev_cond->next= curr_cond;
            }
8569 8570 8571 8572 8573 8574 8575 8576 8577
            DBUG_PRINT("info", ("COND_OR_FUNC"));
            curr_cond->ndb_item= new Ndb_item(Item_func::COND_OR_FUNC, 
                                              func_item->argument_count() - 1);
            context->expect_only(Item::FIELD_ITEM);
            context->expect(Item::INT_ITEM);
            context->expect(Item::STRING_ITEM);
            context->expect(Item::VARBIN_ITEM);
            context->expect(Item::FUNC_ITEM);
            break;
8578
          }
unknown's avatar
unknown committed
8579 8580
          case Item_func::UNKNOWN_FUNC:
          {
8581 8582 8583 8584
            DBUG_PRINT("info", ("UNKNOWN_FUNC %s", 
                                func_item->const_item()?"const":""));  
            DBUG_PRINT("info", ("result type %d", func_item->result_type()));
            if (func_item->const_item())
unknown's avatar
unknown committed
8585 8586 8587 8588
            {
              switch (func_item->result_type()) {
              case STRING_RESULT:
              {
8589 8590 8591 8592 8593 8594 8595 8596 8597 8598 8599 8600 8601 8602 8603 8604 8605 8606 8607 8608 8609 8610 8611 8612 8613 8614 8615 8616
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::STRING_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item); 
                if (context->expect_field_result_mask)
                {
                  // We have not seen the field argument yet
                  context->expect_only(Item::FIELD_ITEM);
                  context->expect_only_field_result(STRING_RESULT);
                  context->expect_collation(func_item->collation.collation);
                }
                else
                {
                  // Expect another logical expression
                  context->expect_only(Item::FUNC_ITEM);
                  context->expect(Item::COND_ITEM);
                  // Check that string result have correct collation
                  if (!context->expecting_collation(item->collation.collation))
                  {
                    DBUG_PRINT("info", ("Found non-matching collation %s",  
                                        item->collation.collation->name));
                    context->supported= FALSE;
                  }
                }
                // Skip any arguments since we will evaluate function instead
                DBUG_PRINT("info", ("Skip until end of arguments marker"));
                context->skip= func_item->argument_count();
                break;
              }
unknown's avatar
unknown committed
8617 8618
              case REAL_RESULT:
              {
8619 8620 8621 8622 8623 8624 8625 8626 8627 8628 8629 8630 8631 8632 8633 8634 8635 8636 8637 8638 8639
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::REAL_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
                if (context->expect_field_result_mask) 
                {
                  // We have not seen the field argument yet
                  context->expect_only(Item::FIELD_ITEM);
                  context->expect_only_field_result(REAL_RESULT);
                }
                else
                {
                  // Expect another logical expression
                  context->expect_only(Item::FUNC_ITEM);
                  context->expect(Item::COND_ITEM);
                }
                
                // Skip any arguments since we will evaluate function instead
                DBUG_PRINT("info", ("Skip until end of arguments marker"));
                context->skip= func_item->argument_count();
                break;
              }
unknown's avatar
unknown committed
8640 8641
              case INT_RESULT:
              {
8642 8643 8644 8645 8646 8647 8648 8649 8650 8651 8652 8653 8654 8655 8656 8657 8658 8659 8660 8661 8662
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::INT_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
                if (context->expect_field_result_mask) 
                {
                  // We have not seen the field argument yet
                  context->expect_only(Item::FIELD_ITEM);
                  context->expect_only_field_result(INT_RESULT);
                }
                else
                {
                  // Expect another logical expression
                  context->expect_only(Item::FUNC_ITEM);
                  context->expect(Item::COND_ITEM);
                }
                
                // Skip any arguments since we will evaluate function instead
                DBUG_PRINT("info", ("Skip until end of arguments marker"));
                context->skip= func_item->argument_count();
                break;
              }
unknown's avatar
unknown committed
8663 8664
              case DECIMAL_RESULT:
              {
8665 8666 8667 8668 8669 8670 8671 8672 8673 8674 8675 8676 8677 8678 8679 8680 8681 8682 8683 8684 8685 8686 8687
                NDB_ITEM_QUALIFICATION q;
                q.value_type= Item::DECIMAL_ITEM;
                curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
                if (context->expect_field_result_mask) 
                {
                  // We have not seen the field argument yet
                  context->expect_only(Item::FIELD_ITEM);
                  context->expect_only_field_result(DECIMAL_RESULT);
                }
                else
                {
                  // Expect another logical expression
                  context->expect_only(Item::FUNC_ITEM);
                  context->expect(Item::COND_ITEM);
                }
                // Skip any arguments since we will evaluate function instead
                DBUG_PRINT("info", ("Skip until end of arguments marker"));
                context->skip= func_item->argument_count();
                break;
              }
              default:
                break;
              }
unknown's avatar
unknown committed
8688
            }
8689 8690 8691 8692 8693
            else
              // Function does not return constant expression
              context->supported= FALSE;
            break;
          }
unknown's avatar
unknown committed
8694 8695
          default:
          {
8696 8697 8698
            DBUG_PRINT("info", ("Found func_item of type %d", 
                                func_item->functype()));
            context->supported= FALSE;
8699
          }
8700 8701
          }
          break;
8702
        }
unknown's avatar
unknown committed
8703
        case Item::STRING_ITEM:
8704 8705 8706
          DBUG_PRINT("info", ("STRING_ITEM")); 
          if (context->expecting(Item::STRING_ITEM)) 
          {
8707
#ifndef DBUG_OFF
8708 8709 8710 8711 8712 8713
            char buff[256];
            String str(buff,(uint32) sizeof(buff), system_charset_info);
            str.length(0);
            Item_string *string_item= (Item_string *) item;
            DBUG_PRINT("info", ("value \"%s\"", 
                                string_item->val_str(&str)->ptr()));
8714
#endif
8715 8716 8717 8718 8719 8720 8721 8722 8723 8724 8725 8726 8727 8728 8729 8730 8731 8732 8733 8734 8735 8736 8737 8738 8739 8740 8741
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::STRING_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);      
            if (context->expect_field_result_mask)
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(STRING_RESULT);
              context->expect_collation(item->collation.collation);
            }
            else 
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
              // Check that we are comparing with a field with same collation
              if (!context->expecting_collation(item->collation.collation))
              {
                DBUG_PRINT("info", ("Found non-matching collation %s",  
                                    item->collation.collation->name));
                context->supported= FALSE;
              }
            }
          }
          else
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
8742
        case Item::INT_ITEM:
8743 8744
          DBUG_PRINT("info", ("INT_ITEM"));
          if (context->expecting(Item::INT_ITEM)) 
8745
          {
8746 8747 8748 8749 8750 8751 8752 8753 8754 8755
            Item_int *int_item= (Item_int *) item;      
            DBUG_PRINT("info", ("value %d", int_item->value));
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::INT_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
            if (context->expect_field_result_mask) 
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(INT_RESULT);
8756 8757
              context->expect_field_result(REAL_RESULT);
              context->expect_field_result(DECIMAL_RESULT);
8758 8759 8760 8761 8762 8763 8764
            }
            else
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
            }
8765 8766
          }
          else
8767 8768
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
8769
        case Item::REAL_ITEM:
8770 8771
          DBUG_PRINT("info", ("REAL_ITEM %s"));
          if (context->expecting(Item::REAL_ITEM)) 
8772
          {
8773 8774 8775 8776 8777 8778 8779 8780 8781 8782 8783 8784 8785 8786 8787 8788 8789
            Item_float *float_item= (Item_float *) item;      
            DBUG_PRINT("info", ("value %f", float_item->value));
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::REAL_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
            if (context->expect_field_result_mask) 
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(REAL_RESULT);
            }
            else
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
            }
8790
          }
8791 8792 8793
          else
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
8794
        case Item::VARBIN_ITEM:
8795 8796
          DBUG_PRINT("info", ("VARBIN_ITEM"));
          if (context->expecting(Item::VARBIN_ITEM)) 
8797
          {
8798 8799 8800 8801 8802 8803 8804 8805 8806 8807 8808 8809 8810 8811 8812
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::VARBIN_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);      
            if (context->expect_field_result_mask)
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(STRING_RESULT);
            }
            else
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
            }
8813 8814
          }
          else
8815 8816
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
8817
        case Item::DECIMAL_ITEM:
8818 8819
          DBUG_PRINT("info", ("DECIMAL_ITEM %s"));
          if (context->expecting(Item::DECIMAL_ITEM)) 
8820
          {
8821 8822 8823 8824 8825 8826 8827 8828 8829 8830 8831 8832 8833 8834 8835 8836 8837 8838
            Item_decimal *decimal_item= (Item_decimal *) item;      
            DBUG_PRINT("info", ("value %f", decimal_item->val_real()));
            NDB_ITEM_QUALIFICATION q;
            q.value_type= Item::DECIMAL_ITEM;
            curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
            if (context->expect_field_result_mask) 
            {
              // We have not seen the field argument yet
              context->expect_only(Item::FIELD_ITEM);
              context->expect_only_field_result(REAL_RESULT);
              context->expect_field_result(DECIMAL_RESULT);
            }
            else
            {
              // Expect another logical expression
              context->expect_only(Item::FUNC_ITEM);
              context->expect(Item::COND_ITEM);
            }
8839
          }
8840 8841 8842
          else
            context->supported= FALSE;
          break;
unknown's avatar
unknown committed
8843 8844
        case Item::COND_ITEM:
        {
8845 8846 8847
          Item_cond *cond_item= (Item_cond *) item;
          
          if (context->expecting(Item::COND_ITEM))
unknown's avatar
unknown committed
8848 8849 8850
          {
            switch (cond_item->functype()) {
            case Item_func::COND_AND_FUNC:
8851 8852 8853 8854
              DBUG_PRINT("info", ("COND_AND_FUNC"));
              curr_cond->ndb_item= new Ndb_item(cond_item->functype(),
                                                cond_item);      
              break;
unknown's avatar
unknown committed
8855
            case Item_func::COND_OR_FUNC:
8856 8857 8858 8859 8860 8861 8862 8863 8864
              DBUG_PRINT("info", ("COND_OR_FUNC"));
              curr_cond->ndb_item= new Ndb_item(cond_item->functype(),
                                                cond_item);      
              break;
            default:
              DBUG_PRINT("info", ("COND_ITEM %d", cond_item->functype()));
              context->supported= FALSE;
              break;
            }
unknown's avatar
unknown committed
8865
          }
8866
          else
unknown's avatar
unknown committed
8867 8868
          {
            /* Did not expect condition */
8869
            context->supported= FALSE;          
unknown's avatar
unknown committed
8870
          }
8871
          break;
8872
        }
unknown's avatar
unknown committed
8873 8874
        default:
        {
8875
          DBUG_PRINT("info", ("Found item of type %d", item->type()));
8876
          context->supported= FALSE;
8877 8878
        }
        }
unknown's avatar
unknown committed
8879
      }
8880 8881 8882 8883 8884 8885 8886 8887 8888 8889
      if (context->supported && context->rewrite_stack)
      {
        Ndb_rewrite_context *rewrite_context= context->rewrite_stack;
        if (rewrite_context->count == 
            rewrite_context->func_item->argument_count())
        {
          // Rewrite is done, wrap an END() at the en
          DBUG_PRINT("info", ("End of condition group"));
          prev_cond= curr_cond;
          curr_cond= context->cond_ptr= new Ndb_cond();
8890
          curr_cond->prev= prev_cond;
8891 8892 8893
          prev_cond->next= curr_cond;
          curr_cond->ndb_item= new Ndb_item(NDB_END_COND);
          // Pop rewrite stack
unknown's avatar
unknown committed
8894 8895 8896
          context->rewrite_stack=  rewrite_context->next;
          rewrite_context->next= NULL;
          delete(rewrite_context);
8897
        }
8898
      }
8899
    }
8900
  }
8901
 
8902 8903 8904 8905 8906 8907 8908 8909
  DBUG_VOID_RETURN;
}

bool
ha_ndbcluster::serialize_cond(const COND *cond, Ndb_cond_stack *ndb_cond)
{
  DBUG_ENTER("serialize_cond");
  Item *item= (Item *) cond;
8910
  Ndb_cond_traverse_context context(table, (void *)m_table, ndb_cond);
8911 8912 8913
  // Expect a logical expression
  context.expect(Item::FUNC_ITEM);
  context.expect(Item::COND_ITEM);
8914
  item->traverse_cond(&ndb_serialize_cond, (void *) &context, Item::PREFIX);
8915
  DBUG_PRINT("info", ("The pushed condition is %ssupported", (context.supported)?"":"not "));
8916

8917
  DBUG_RETURN(context.supported);
8918 8919
}

8920 8921
int
ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, 
8922 8923
                                           NdbScanFilter *filter,
                                           bool negated)
8924 8925
{
  DBUG_ENTER("build_scan_filter_predicate");  
unknown's avatar
unknown committed
8926 8927 8928
  switch (cond->ndb_item->type) {
  case NDB_FUNCTION:
  {
8929 8930 8931
    if (!cond->next)
      break;
    Ndb_item *a= cond->next->ndb_item;
8932
    Ndb_item *b, *field, *value= NULL;
8933 8934
    LINT_INIT(field);

unknown's avatar
unknown committed
8935 8936
    switch (cond->ndb_item->argument_count()) {
    case 1:
8937 8938 8939
      field= 
        (a->type == NDB_FIELD)? a : NULL;
      break;
unknown's avatar
unknown committed
8940
    case 2:
8941
      if (!cond->next->next)
8942
        break;
8943 8944
      b= cond->next->next->ndb_item;
      value= 
8945 8946 8947
        (a->type == NDB_VALUE)? a
        : (b->type == NDB_VALUE)? b
        : NULL;
8948
      field= 
8949 8950 8951
        (a->type == NDB_FIELD)? a
        : (b->type == NDB_FIELD)? b
        : NULL;
8952
      break;
8953
    default:
8954 8955
      field= NULL; //Keep compiler happy
      DBUG_ASSERT(0);
8956 8957
      break;
    }
unknown's avatar
unknown committed
8958 8959 8960
    switch ((negated) ? 
            Ndb_item::negate(cond->ndb_item->qualification.function_type)
            : cond->ndb_item->qualification.function_type) {
8961
    case NDB_EQ_FUNC:
8962
    {
8963
      if (!value || !field) break;
unknown's avatar
unknown committed
8964 8965
      // Save value in right format for the field type
      value->save_in_field(field);
8966
      DBUG_PRINT("info", ("Generating EQ filter"));
8967
      if (filter->cmp(NdbScanFilter::COND_EQ, 
8968 8969 8970 8971
                      field->get_field_no(),
                      field->get_val(),
                      field->pack_length()) == -1)
        DBUG_RETURN(1);
8972 8973
      cond= cond->next->next->next;
      DBUG_RETURN(0);
8974
    }
8975
    case NDB_NE_FUNC:
unknown's avatar
unknown committed
8976
    {
8977
      if (!value || !field) break;
unknown's avatar
unknown committed
8978 8979
      // Save value in right format for the field type
      value->save_in_field(field);
8980
      DBUG_PRINT("info", ("Generating NE filter"));
8981
      if (filter->cmp(NdbScanFilter::COND_NE, 
8982 8983 8984 8985
                      field->get_field_no(),
                      field->get_val(),
                      field->pack_length()) == -1)
        DBUG_RETURN(1);
8986 8987
      cond= cond->next->next->next;
      DBUG_RETURN(0);
8988
    }
8989
    case NDB_LT_FUNC:
unknown's avatar
unknown committed
8990
    {
8991
      if (!value || !field) break;
unknown's avatar
unknown committed
8992 8993
      // Save value in right format for the field type
      value->save_in_field(field);
8994
      if (a == field)
8995
      {
8996 8997 8998 8999 9000 9001
        DBUG_PRINT("info", ("Generating LT filter")); 
        if (filter->cmp(NdbScanFilter::COND_LT, 
                        field->get_field_no(),
                        field->get_val(),
                        field->pack_length()) == -1)
          DBUG_RETURN(1);
9002
      }
9003
      else
9004
      {
9005 9006 9007 9008 9009 9010
        DBUG_PRINT("info", ("Generating GT filter")); 
        if (filter->cmp(NdbScanFilter::COND_GT, 
                        field->get_field_no(),
                        field->get_val(),
                        field->pack_length()) == -1)
          DBUG_RETURN(1);
9011
      }
9012 9013
      cond= cond->next->next->next;
      DBUG_RETURN(0);
9014
    }
9015
    case NDB_LE_FUNC:
unknown's avatar
unknown committed
9016
    {
9017
      if (!value || !field) break;
unknown's avatar
unknown committed
9018 9019
      // Save value in right format for the field type
      value->save_in_field(field);
9020
      if (a == field)
9021
      {
9022 9023 9024 9025 9026 9027
        DBUG_PRINT("info", ("Generating LE filter")); 
        if (filter->cmp(NdbScanFilter::COND_LE, 
                        field->get_field_no(),
                        field->get_val(),
                        field->pack_length()) == -1)
          DBUG_RETURN(1);       
9028
      }
9029
      else
9030
      {
9031 9032 9033 9034 9035 9036
        DBUG_PRINT("info", ("Generating GE filter")); 
        if (filter->cmp(NdbScanFilter::COND_GE, 
                        field->get_field_no(),
                        field->get_val(),
                        field->pack_length()) == -1)
          DBUG_RETURN(1);
9037
      }
9038 9039
      cond= cond->next->next->next;
      DBUG_RETURN(0);
9040
    }
9041
    case NDB_GE_FUNC:
unknown's avatar
unknown committed
9042
    {
9043
      if (!value || !field) break;
unknown's avatar
unknown committed
9044 9045
      // Save value in right format for the field type
      value->save_in_field(field);
9046
      if (a == field)
9047
      {
9048 9049 9050 9051 9052 9053
        DBUG_PRINT("info", ("Generating GE filter")); 
        if (filter->cmp(NdbScanFilter::COND_GE, 
                        field->get_field_no(),
                        field->get_val(),
                        field->pack_length()) == -1)
          DBUG_RETURN(1);
9054
      }
9055
      else
9056
      {
9057 9058 9059 9060 9061 9062
        DBUG_PRINT("info", ("Generating LE filter")); 
        if (filter->cmp(NdbScanFilter::COND_LE, 
                        field->get_field_no(),
                        field->get_val(),
                        field->pack_length()) == -1)
          DBUG_RETURN(1);
9063
      }
9064 9065
      cond= cond->next->next->next;
      DBUG_RETURN(0);
9066
    }
9067
    case NDB_GT_FUNC:
unknown's avatar
unknown committed
9068
    {
9069
      if (!value || !field) break;
unknown's avatar
unknown committed
9070 9071
      // Save value in right format for the field type
      value->save_in_field(field);
9072
      if (a == field)
9073
      {
9074 9075 9076 9077 9078 9079
        DBUG_PRINT("info", ("Generating GT filter"));
        if (filter->cmp(NdbScanFilter::COND_GT, 
                        field->get_field_no(),
                        field->get_val(),
                        field->pack_length()) == -1)
          DBUG_RETURN(1);
9080
      }
9081
      else
9082
      {
9083 9084 9085 9086 9087 9088
        DBUG_PRINT("info", ("Generating LT filter"));
        if (filter->cmp(NdbScanFilter::COND_LT, 
                        field->get_field_no(),
                        field->get_val(),
                        field->pack_length()) == -1)
          DBUG_RETURN(1);
9089
      }
9090 9091
      cond= cond->next->next->next;
      DBUG_RETURN(0);
9092
    }
9093
    case NDB_LIKE_FUNC:
unknown's avatar
unknown committed
9094
    {
9095
      if (!value || !field) break;
9096 9097 9098
      if ((value->qualification.value_type != Item::STRING_ITEM) &&
          (value->qualification.value_type != Item::VARBIN_ITEM))
          break;
unknown's avatar
unknown committed
9099 9100 9101
      // Save value in right format for the field type
      value->save_in_field(field);
      DBUG_PRINT("info", ("Generating LIKE filter: like(%d,%s,%d)", 
9102 9103 9104 9105
                          field->get_field_no(), value->get_val(), 
                          value->pack_length()));
      if (filter->cmp(NdbScanFilter::COND_LIKE, 
                      field->get_field_no(),
9106 9107
                      value->get_val(),
                      value->pack_length()) == -1)
9108
        DBUG_RETURN(1);
9109 9110
      cond= cond->next->next->next;
      DBUG_RETURN(0);
9111
    }
9112
    case NDB_NOTLIKE_FUNC:
unknown's avatar
unknown committed
9113
    {
9114
      if (!value || !field) break;
9115 9116
      if ((value->qualification.value_type != Item::STRING_ITEM) &&
          (value->qualification.value_type != Item::VARBIN_ITEM))
9117
          break;
unknown's avatar
unknown committed
9118 9119 9120
      // Save value in right format for the field type
      value->save_in_field(field);
      DBUG_PRINT("info", ("Generating NOTLIKE filter: notlike(%d,%s,%d)", 
9121 9122 9123 9124
                          field->get_field_no(), value->get_val(), 
                          value->pack_length()));
      if (filter->cmp(NdbScanFilter::COND_NOT_LIKE, 
                      field->get_field_no(),
9125 9126
                      value->get_val(),
                      value->pack_length()) == -1)
9127
        DBUG_RETURN(1);
9128 9129
      cond= cond->next->next->next;
      DBUG_RETURN(0);
9130
    }
9131
    case NDB_ISNULL_FUNC:
9132 9133 9134 9135 9136
      if (!field)
        break;
      DBUG_PRINT("info", ("Generating ISNULL filter"));
      if (filter->isnull(field->get_field_no()) == -1)
        DBUG_RETURN(1);
9137 9138
      cond= cond->next->next;
      DBUG_RETURN(0);
9139
    case NDB_ISNOTNULL_FUNC:
unknown's avatar
unknown committed
9140
    {
9141 9142 9143 9144 9145
      if (!field)
        break;
      DBUG_PRINT("info", ("Generating ISNOTNULL filter"));
      if (filter->isnotnull(field->get_field_no()) == -1)
        DBUG_RETURN(1);         
9146 9147
      cond= cond->next->next;
      DBUG_RETURN(0);
9148 9149 9150 9151 9152 9153 9154 9155 9156 9157
    }
    default:
      break;
    }
    break;
  }
  default:
    break;
  }
  DBUG_PRINT("info", ("Found illegal condition"));
9158
  DBUG_RETURN(1);
9159 9160
}

9161
int
9162
ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter)
9163
{
9164
  uint level=0;
unknown's avatar
unknown committed
9165
  bool negated= FALSE;
9166
  DBUG_ENTER("build_scan_filter_group");
unknown's avatar
unknown committed
9167

9168 9169
  do
  {
unknown's avatar
unknown committed
9170 9171 9172 9173 9174 9175
    if (!cond)
      DBUG_RETURN(1);
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
    {
      switch (cond->ndb_item->qualification.function_type) {
9176
      case NDB_COND_AND_FUNC:
unknown's avatar
unknown committed
9177
      {
9178 9179 9180 9181 9182
        level++;
        DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NAND":"AND",
                            level));
        if ((negated) ? filter->begin(NdbScanFilter::NAND)
            : filter->begin(NdbScanFilter::AND) == -1)
9183
          DBUG_RETURN(1);
unknown's avatar
unknown committed
9184
        negated= FALSE;
9185 9186 9187
        cond= cond->next;
        break;
      }
9188
      case NDB_COND_OR_FUNC:
unknown's avatar
unknown committed
9189
      {
9190 9191 9192 9193 9194 9195
        level++;
        DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NOR":"OR",
                            level));
        if ((negated) ? filter->begin(NdbScanFilter::NOR)
            : filter->begin(NdbScanFilter::OR) == -1)
          DBUG_RETURN(1);
unknown's avatar
unknown committed
9196
        negated= FALSE;
9197 9198 9199
        cond= cond->next;
        break;
      }
9200
      case NDB_NOT_FUNC:
unknown's avatar
unknown committed
9201
      {
9202
        DBUG_PRINT("info", ("Generating negated query"));
9203
        cond= cond->next;
unknown's avatar
unknown committed
9204
        negated= TRUE;
9205 9206 9207 9208
        break;
      }
      default:
        if (build_scan_filter_predicate(cond, filter, negated))
9209
          DBUG_RETURN(1);
unknown's avatar
unknown committed
9210
        negated= FALSE;
9211 9212 9213
        break;
      }
      break;
unknown's avatar
unknown committed
9214 9215
    }
    case NDB_END_COND:
9216 9217
      DBUG_PRINT("info", ("End of group %u", level));
      level--;
9218 9219
      if (cond) cond= cond->next;
      if (filter->end() == -1)
9220
        DBUG_RETURN(1);
9221 9222 9223
      if (!negated)
        break;
      // else fall through (NOT END is an illegal condition)
unknown's avatar
unknown committed
9224 9225
    default:
    {
9226
      DBUG_PRINT("info", ("Illegal scan filter"));
9227
    }
9228
    }
9229
  }  while (level > 0 || negated);
9230
  
9231
  DBUG_RETURN(0);
9232 9233
}

9234 9235
int
ha_ndbcluster::build_scan_filter(Ndb_cond * &cond, NdbScanFilter *filter)
9236 9237 9238 9239
{
  bool simple_cond= TRUE;
  DBUG_ENTER("build_scan_filter");  

unknown's avatar
unknown committed
9240 9241 9242
    switch (cond->ndb_item->type) {
    case NDB_FUNCTION:
      switch (cond->ndb_item->qualification.function_type) {
9243 9244
      case NDB_COND_AND_FUNC:
      case NDB_COND_OR_FUNC:
9245 9246 9247 9248 9249 9250 9251 9252 9253
        simple_cond= FALSE;
        break;
      default:
        break;
      }
      break;
    default:
      break;
    }
9254 9255 9256 9257 9258 9259
  if (simple_cond && filter->begin() == -1)
    DBUG_RETURN(1);
  if (build_scan_filter_group(cond, filter))
    DBUG_RETURN(1);
  if (simple_cond && filter->end() == -1)
    DBUG_RETURN(1);
9260

9261
  DBUG_RETURN(0);
9262 9263
}

9264
int
9265
ha_ndbcluster::generate_scan_filter(Ndb_cond_stack *ndb_cond_stack,
9266
                                    NdbScanOperation *op)
9267 9268 9269 9270
{
  DBUG_ENTER("generate_scan_filter");
  if (ndb_cond_stack)
  {
9271
    DBUG_PRINT("info", ("Generating scan filter"));
9272 9273 9274 9275 9276
    NdbScanFilter filter(op);
    bool multiple_cond= FALSE;
    // Wrap an AND group around multiple conditions
    if (ndb_cond_stack->next) {
      multiple_cond= TRUE;
9277
      if (filter.begin() == -1)
9278
        DBUG_RETURN(1); 
9279 9280
    }
    for (Ndb_cond_stack *stack= ndb_cond_stack; 
9281 9282
         (stack); 
         stack= stack->next)
9283
      {
9284
        Ndb_cond *cond= stack->ndb_cond;
9285

9286 9287 9288 9289 9290
        if (build_scan_filter(cond, &filter))
        {
          DBUG_PRINT("info", ("build_scan_filter failed"));
          DBUG_RETURN(1);
        }
9291
      }
9292 9293
    if (multiple_cond && filter.end() == -1)
      DBUG_RETURN(1);
9294 9295 9296 9297 9298 9299
  }
  else
  {  
    DBUG_PRINT("info", ("Empty stack"));
  }

9300
  DBUG_RETURN(0);
9301 9302
}

9303 9304 9305
/*
  get table space info for SHOW CREATE TABLE
*/
9306
char* ha_ndbcluster::get_tablespace_name(THD *thd)
9307
{
9308
  Ndb *ndb= check_ndb_in_thd(thd);
9309
  NDBDICT *ndbdict= ndb->getDictionary();
9310 9311
  NdbError ndberr;
  Uint32 id;
9312
  ndb->setDatabaseName(m_dbname);
9313 9314
  const NDBTAB *ndbtab= m_table;
  DBUG_ASSERT(ndbtab != NULL);
9315 9316
  if (!ndbtab->getTablespace(&id))
  {
9317
    return 0;
9318 9319 9320 9321
  }
  {
    NdbDictionary::Tablespace ts= ndbdict->getTablespace(id);
    ndberr= ndbdict->getNdbError();
9322
    if(ndberr.classification != NdbError::NoError)
9323 9324 9325 9326 9327
      goto err;
    return (my_strdup(ts.getName(), MYF(0)));
  }
err:
  if (ndberr.status == NdbError::TemporaryError)
9328
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
9329 9330 9331
			ER_GET_TEMPORARY_ERRMSG, ER(ER_GET_TEMPORARY_ERRMSG),
			ndberr.code, ndberr.message, "NDB");
  else
9332
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
9333 9334
			ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
			ndberr.code, ndberr.message, "NDB");
9335 9336 9337
  return 0;
}

unknown's avatar
unknown committed
9338 9339 9340
/*
  Implements the SHOW NDB STATUS command.
*/
9341 9342 9343
bool
ndbcluster_show_status(THD* thd, stat_print_fn *stat_print,
                       enum ha_stat_type stat_type)
9344
{
9345
  char buf[IO_SIZE];
unknown's avatar
unknown committed
9346
  uint buflen;
9347 9348 9349 9350
  DBUG_ENTER("ndbcluster_show_status");
  
  if (have_ndbcluster != SHOW_OPTION_YES) 
  {
9351 9352 9353 9354 9355
    DBUG_RETURN(FALSE);
  }
  if (stat_type != HA_ENGINE_STATUS)
  {
    DBUG_RETURN(FALSE);
9356
  }
unknown's avatar
unknown committed
9357 9358 9359 9360 9361 9362 9363 9364 9365 9366 9367 9368 9369 9370 9371 9372 9373

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

unknown's avatar
unknown committed
9374
  if (get_thd_ndb(thd) && get_thd_ndb(thd)->ndb)
9375
  {
unknown's avatar
unknown committed
9376
    Ndb* ndb= (get_thd_ndb(thd))->ndb;
unknown's avatar
unknown committed
9377 9378
    Ndb::Free_list_usage tmp;
    tmp.m_name= 0;
9379 9380
    while (ndb->get_free_list_usage(&tmp))
    {
unknown's avatar
unknown committed
9381
      buflen=
unknown's avatar
unknown committed
9382
        my_snprintf(buf, sizeof(buf),
9383 9384
                  "created=%u, free=%u, sizeof=%u",
                  tmp.m_created, tmp.m_free, tmp.m_sizeof);
unknown's avatar
unknown committed
9385 9386
      if (stat_print(thd, ndbcluster_hton.name, strlen(ndbcluster_hton.name),
                     tmp.m_name, strlen(tmp.m_name), buf, buflen))
9387
        DBUG_RETURN(TRUE);
9388 9389
    }
  }
unknown's avatar
unknown committed
9390 9391 9392 9393
#ifdef HAVE_NDB_BINLOG
  ndbcluster_show_status_binlog(thd, stat_print, stat_type);
#endif

9394 9395
  DBUG_RETURN(FALSE);
}
9396

unknown's avatar
unknown committed
9397

9398 9399 9400 9401 9402 9403 9404 9405 9406 9407 9408 9409 9410 9411 9412 9413 9414 9415 9416 9417 9418 9419 9420 9421 9422 9423 9424 9425 9426 9427 9428 9429 9430 9431 9432 9433 9434 9435 9436 9437 9438 9439 9440 9441 9442 9443
/*
  Create a table in NDB Cluster
 */
static uint get_no_fragments(ulonglong max_rows)
{
#if MYSQL_VERSION_ID >= 50000
  uint acc_row_size= 25 + /*safety margin*/ 2;
#else
  uint acc_row_size= pk_length*4;
  /* add acc overhead */
  if (pk_length <= 8)  /* main page will set the limit */
    acc_row_size+= 25 + /*safety margin*/ 2;
  else                /* overflow page will set the limit */
    acc_row_size+= 4 + /*safety margin*/ 4;
#endif
  ulonglong acc_fragment_size= 512*1024*1024;
#if MYSQL_VERSION_ID >= 50100
  return (max_rows*acc_row_size)/acc_fragment_size+1;
#else
  return ((max_rows*acc_row_size)/acc_fragment_size+1
	  +1/*correct rounding*/)/2;
#endif
}


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

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

int ha_ndbcluster::get_default_no_partitions(ulonglong max_rows)
{
  uint reported_frags;
  uint no_fragments= get_no_fragments(max_rows);
  uint no_nodes= g_ndb_cluster_connection->no_db_nodes();
unknown's avatar
unknown committed
9444 9445 9446 9447 9448 9449
  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");
  }
9450 9451 9452 9453
  return (int)reported_frags;
}


unknown's avatar
unknown committed
9454 9455 9456 9457 9458 9459 9460 9461 9462 9463 9464 9465 9466 9467 9468 9469 9470 9471 9472 9473 9474 9475 9476 9477 9478 9479 9480 9481 9482 9483 9484 9485 9486 9487 9488 9489 9490 9491 9492 9493 9494 9495 9496 9497 9498 9499 9500 9501 9502 9503 9504 9505
/*
  Set-up auto-partitioning for NDB Cluster

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

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

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


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

  if (!range_data)
  {
    mem_alloc_error(part_info->no_parts*sizeof(int32));
    DBUG_RETURN(1);
  }
  for (i= 0; i < part_info->no_parts; i++)
  {
    longlong range_val= part_info->range_int_array[i];
9506
    if (range_val < INT_MIN32 || range_val >= INT_MAX32)
unknown's avatar
unknown committed
9507
    {
9508 9509 9510 9511 9512 9513 9514 9515
      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
9516 9517 9518 9519 9520 9521 9522 9523 9524 9525 9526 9527 9528 9529 9530 9531 9532 9533 9534 9535 9536 9537 9538 9539 9540 9541 9542 9543 9544 9545 9546 9547 9548 9549 9550 9551 9552 9553 9554 9555 9556 9557 9558
    }
    range_data[i]= (int32)range_val;
  }
  tab->setRangeListData(range_data, sizeof(int32)*part_info->no_parts);
error:
  my_free((char*)range_data, MYF(0));
  DBUG_RETURN(error);
}

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

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

9559 9560 9561 9562 9563 9564 9565 9566 9567 9568 9569 9570 9571 9572 9573 9574 9575
/*
  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
9576 9577 9578
  uint16 frag_data[MAX_PARTITIONS];
  char *ts_names[MAX_PARTITIONS];
  ulong ts_index= 0, fd_index= 0, i, j;
9579 9580 9581
  NDBTAB *tab= (NDBTAB*)tab_par;
  NDBTAB::FragmentType ftype= NDBTAB::UserDefined;
  partition_element *part_elem;
unknown's avatar
unknown committed
9582 9583 9584 9585 9586 9587
  bool first= TRUE;
  uint ts_id, ts_version, part_count= 0, tot_ts_name_len;
  List_iterator<partition_element> part_it(part_info->partitions);
  int error;
  char *name_ptr;
  DBUG_ENTER("ha_ndbcluster::set_up_partition_info");
9588 9589 9590 9591 9592 9593 9594 9595 9596 9597 9598 9599 9600 9601 9602 9603 9604 9605

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

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

    for (i= 0; i < part_info->part_field_list.elements; i++)
    {
      NDBCOL *col= tab->getColumn(fields[i]->fieldnr - 1);
      DBUG_PRINT("info",("setting dist key on %s", col->getName()));
      col->setPartitionKey(TRUE);
    }
  }
9606
  else 
9607
  {
9608 9609 9610 9611 9612 9613 9614 9615 9616 9617 9618 9619 9620 9621 9622 9623 9624
    /*
      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)
9625
    {
9626 9627 9628 9629
      if ((error= set_range_data((void*)tab, part_info)))
      {
        DBUG_RETURN(error);
      }
9630
    }
9631
    else if (part_info->part_type == LIST_PARTITION)
9632
    {
9633 9634 9635 9636
      if ((error= set_list_data((void*)tab, part_info)))
      {
        DBUG_RETURN(error);
      }
9637 9638 9639
    }
  }
  tab->setFragmentType(ftype);
unknown's avatar
unknown committed
9640 9641 9642
  i= 0;
  tot_ts_name_len= 0;
  do
9643
  {
unknown's avatar
unknown committed
9644 9645
    uint ng;
    part_elem= part_it++;
9646
    if (!part_info->is_sub_partitioned())
9647
    {
unknown's avatar
unknown committed
9648 9649 9650 9651 9652
      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;
9653
    }
unknown's avatar
unknown committed
9654 9655 9656 9657 9658 9659 9660 9661 9662 9663 9664 9665 9666 9667 9668 9669 9670
    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);
9671
  tab->setLinearFlag(part_info->linear_hash_ind);
unknown's avatar
unknown committed
9672 9673 9674 9675 9676
  tab->setMaxRows(table->s->max_rows);
  tab->setTablespaceNames(ts_names, fd_index*sizeof(char*));
  tab->setFragmentCount(fd_index);
  tab->setFragmentData(&frag_data, fd_index*2);
  DBUG_RETURN(0);
9677
}
9678

unknown's avatar
unknown committed
9679

unknown's avatar
unknown committed
9680 9681 9682
bool ha_ndbcluster::check_if_incompatible_data(HA_CREATE_INFO *info,
					       uint table_changes)
{
9683 9684 9685
  DBUG_ENTER("ha_ndbcluster::check_if_incompatible_data");
  uint i;
  const NDBTAB *tab= (const NDBTAB *) m_table;
unknown's avatar
unknown committed
9686

9687 9688 9689 9690 9691 9692 9693 9694 9695 9696 9697
  for (i= 0; i < table->s->fields; i++) 
  {
    Field *field= table->field[i];
    const NDBCOL *col= tab->getColumn(field->field_name);
    if (field->add_index &&
        col->getStorageType() == NdbDictionary::Column::StorageTypeDisk)
    {
      DBUG_PRINT("info", ("add/drop index not supported for disk stored column"));
      DBUG_RETURN(COMPATIBLE_DATA_NO);
    }
  }
unknown's avatar
unknown committed
9698
  if (table_changes != IS_EQUAL_YES)
9699
    DBUG_RETURN(COMPATIBLE_DATA_NO);
unknown's avatar
unknown committed
9700 9701 9702 9703
  
  /* Check that auto_increment value was not changed */
  if ((info->used_fields & HA_CREATE_USED_AUTO) &&
      info->auto_increment_value != 0)
9704
    DBUG_RETURN(COMPATIBLE_DATA_NO);
unknown's avatar
unknown committed
9705 9706 9707 9708
  
  /* Check that row format didn't change */
  if ((info->used_fields & HA_CREATE_USED_AUTO) &&
      get_row_type() != info->row_type)
9709
    DBUG_RETURN(COMPATIBLE_DATA_NO);
unknown's avatar
unknown committed
9710

9711
  DBUG_RETURN(COMPATIBLE_DATA_YES);
unknown's avatar
unknown committed
9712 9713
}

unknown's avatar
unknown committed
9714 9715 9716 9717 9718 9719 9720 9721 9722 9723 9724 9725 9726 9727 9728 9729 9730 9731 9732 9733 9734 9735 9736 9737 9738 9739 9740 9741 9742 9743 9744 9745 9746 9747 9748 9749 9750 9751 9752 9753
bool set_up_tablespace(st_alter_tablespace *info,
                       NdbDictionary::Tablespace *ndb_ts)
{
  ndb_ts->setName(info->tablespace_name);
  ndb_ts->setExtentSize(info->extent_size);
  ndb_ts->setDefaultLogfileGroup(info->logfile_group_name);
  return false;
}

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

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

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

unknown's avatar
unknown committed
9754
int ndbcluster_alter_tablespace(THD* thd, st_alter_tablespace *info)
unknown's avatar
unknown committed
9755 9756
{
  DBUG_ENTER("ha_ndbcluster::alter_tablespace");
unknown's avatar
unknown committed
9757

9758
  int is_tablespace= 0;
unknown's avatar
unknown committed
9759 9760
  Ndb *ndb= check_ndb_in_thd(thd);
  if (ndb == NULL)
unknown's avatar
unknown committed
9761
  {
unknown's avatar
unknown committed
9762
    DBUG_RETURN(HA_ERR_NO_CONNECTION);
unknown's avatar
unknown committed
9763
  }
unknown's avatar
unknown committed
9764 9765 9766 9767
  
  NDBDICT *dict = ndb->getDictionary();
  int error;
  const char * errmsg;
9768
  LINT_INIT(errmsg);
unknown's avatar
unknown committed
9769

unknown's avatar
unknown committed
9770 9771 9772
  switch (info->ts_cmd_type){
  case (CREATE_TABLESPACE):
  {
9773
    error= ER_CREATE_FILEGROUP_FAILED;
unknown's avatar
unknown committed
9774
    
unknown's avatar
unknown committed
9775 9776 9777 9778 9779 9780 9781 9782 9783 9784
    NdbDictionary::Tablespace ndb_ts;
    NdbDictionary::Datafile ndb_df;
    if (set_up_tablespace(info, &ndb_ts))
    {
      DBUG_RETURN(1);
    }
    if (set_up_datafile(info, &ndb_df))
    {
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
9785 9786
    errmsg= "TABLESPACE";
    if (dict->createTablespace(ndb_ts))
unknown's avatar
unknown committed
9787 9788
    {
      DBUG_PRINT("error", ("createTablespace returned %d", error));
unknown's avatar
unknown committed
9789
      goto ndberror;
unknown's avatar
unknown committed
9790 9791
    }
    DBUG_PRINT("info", ("Successfully created Tablespace"));
unknown's avatar
unknown committed
9792 9793
    errmsg= "DATAFILE";
    if (dict->createDatafile(ndb_df))
unknown's avatar
unknown committed
9794 9795
    {
      DBUG_PRINT("error", ("createDatafile returned %d", error));
unknown's avatar
unknown committed
9796
      goto ndberror;
unknown's avatar
unknown committed
9797
    }
9798
    is_tablespace= 1;
unknown's avatar
unknown committed
9799 9800 9801 9802
    break;
  }
  case (ALTER_TABLESPACE):
  {
9803
    error= ER_ALTER_FILEGROUP_FAILED;
unknown's avatar
unknown committed
9804 9805 9806 9807 9808 9809 9810
    if (info->ts_alter_tablespace_type == ALTER_TABLESPACE_ADD_FILE)
    {
      NdbDictionary::Datafile ndb_df;
      if (set_up_datafile(info, &ndb_df))
      {
	DBUG_RETURN(1);
      }
unknown's avatar
unknown committed
9811 9812
      errmsg= " CREATE DATAFILE";
      if (dict->createDatafile(ndb_df))
unknown's avatar
unknown committed
9813
      {
unknown's avatar
unknown committed
9814
	goto ndberror;
unknown's avatar
unknown committed
9815 9816 9817 9818 9819 9820 9821 9822
      }
    }
    else if(info->ts_alter_tablespace_type == ALTER_TABLESPACE_DROP_FILE)
    {
      NdbDictionary::Datafile df = dict->getDatafile(0, 
						     info->data_file_name);
      if (strcmp(df.getPath(), info->data_file_name) == 0)
      {
unknown's avatar
unknown committed
9823 9824
	errmsg= " DROP DATAFILE";
	if (dict->dropDatafile(df))
unknown's avatar
unknown committed
9825
	{
unknown's avatar
unknown committed
9826
	  goto ndberror;
unknown's avatar
unknown committed
9827 9828 9829 9830 9831
	}
      }
      else
      {
	DBUG_PRINT("error", ("No such datafile"));
9832
	my_error(ER_ALTER_FILEGROUP_FAILED, MYF(0), " NO SUCH FILE");
unknown's avatar
unknown committed
9833 9834 9835 9836 9837 9838 9839 9840 9841
	DBUG_RETURN(1);
      }
    }
    else
    {
      DBUG_PRINT("error", ("Unsupported alter tablespace: %d", 
			   info->ts_alter_tablespace_type));
      DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
    }
9842
    is_tablespace= 1;
unknown's avatar
unknown committed
9843 9844 9845 9846
    break;
  }
  case (CREATE_LOGFILE_GROUP):
  {
9847
    error= ER_CREATE_FILEGROUP_FAILED;
unknown's avatar
unknown committed
9848 9849 9850 9851 9852 9853 9854 9855 9856 9857 9858 9859 9860
    NdbDictionary::LogfileGroup ndb_lg;
    NdbDictionary::Undofile ndb_uf;
    if (info->undo_file_name == NULL)
    {
      /*
	REDO files in LOGFILE GROUP not supported yet
      */
      DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
    }
    if (set_up_logfile_group(info, &ndb_lg))
    {
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
9861 9862
    errmsg= "LOGFILE GROUP";
    if (dict->createLogfileGroup(ndb_lg))
unknown's avatar
unknown committed
9863
    {
unknown's avatar
unknown committed
9864
      goto ndberror;
unknown's avatar
unknown committed
9865 9866 9867 9868 9869 9870
    }
    DBUG_PRINT("info", ("Successfully created Logfile Group"));
    if (set_up_undofile(info, &ndb_uf))
    {
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
9871 9872
    errmsg= "UNDOFILE";
    if (dict->createUndofile(ndb_uf))
unknown's avatar
unknown committed
9873
    {
unknown's avatar
unknown committed
9874
      goto ndberror;
unknown's avatar
unknown committed
9875 9876 9877 9878 9879
    }
    break;
  }
  case (ALTER_LOGFILE_GROUP):
  {
9880
    error= ER_ALTER_FILEGROUP_FAILED;
unknown's avatar
unknown committed
9881 9882 9883 9884 9885 9886 9887 9888 9889 9890 9891 9892
    if (info->undo_file_name == NULL)
    {
      /*
	REDO files in LOGFILE GROUP not supported yet
      */
      DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
    }
    NdbDictionary::Undofile ndb_uf;
    if (set_up_undofile(info, &ndb_uf))
    {
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
9893 9894
    errmsg= "CREATE UNDOFILE";
    if (dict->createUndofile(ndb_uf))
unknown's avatar
unknown committed
9895
    {
unknown's avatar
unknown committed
9896
      goto ndberror;
unknown's avatar
unknown committed
9897 9898 9899 9900 9901
    }
    break;
  }
  case (DROP_TABLESPACE):
  {
9902
    error= ER_DROP_FILEGROUP_FAILED;
unknown's avatar
unknown committed
9903 9904
    errmsg= "TABLESPACE";
    if (dict->dropTablespace(dict->getTablespace(info->tablespace_name)))
unknown's avatar
unknown committed
9905
    {
unknown's avatar
unknown committed
9906
      goto ndberror;
unknown's avatar
unknown committed
9907
    }
9908
    is_tablespace= 1;
unknown's avatar
unknown committed
9909 9910 9911 9912
    break;
  }
  case (DROP_LOGFILE_GROUP):
  {
9913
    error= ER_DROP_FILEGROUP_FAILED;
unknown's avatar
unknown committed
9914 9915
    errmsg= "LOGFILE GROUP";
    if (dict->dropLogfileGroup(dict->getLogfileGroup(info->logfile_group_name)))
unknown's avatar
unknown committed
9916
    {
unknown's avatar
unknown committed
9917
      goto ndberror;
unknown's avatar
unknown committed
9918 9919 9920 9921 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933
    }
    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);
  }
  }
9934
#ifdef HAVE_NDB_BINLOG
9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946
  if (is_tablespace)
    ndbcluster_log_schema_op(thd, 0,
                             thd->query, thd->query_length,
                             "", info->tablespace_name,
                             0, 0,
                             SOT_TABLESPACE);
  else
    ndbcluster_log_schema_op(thd, 0,
                             thd->query, thd->query_length,
                             "", info->logfile_group_name,
                             0, 0,
                             SOT_LOGFILE_GROUP);
9947
#endif
unknown's avatar
unknown committed
9948
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
9949 9950 9951 9952 9953 9954 9955 9956

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

unknown's avatar
unknown committed
9959 9960 9961 9962 9963 9964 9965 9966 9967 9968 9969 9970 9971 9972 9973 9974 9975 9976 9977

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

  set_dbname(name);
  set_tabname(name);
  do
  {
    if (check_ndb_connection())
    {
      err= HA_ERR_NO_CONNECTION;
      break;
    }
    ndb= get_ndb();
9978
    ndb->setDatabaseName(m_dbname);
9979 9980
    Ndb_table_guard ndbtab_g(dict= ndb->getDictionary(), m_tabname);
    if (!ndbtab_g.get_table())
unknown's avatar
unknown committed
9981
      ERR_BREAK(dict->getNdbError(), err);
9982
    *no_parts= ndbtab_g.get_table()->getFragmentCount();
unknown's avatar
unknown committed
9983 9984 9985 9986 9987 9988 9989 9990
    DBUG_RETURN(FALSE);
  } while (1);

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

9991 9992 9993
static int ndbcluster_fill_files_table(THD *thd, TABLE_LIST *tables, COND *cond)
{
  TABLE* table= tables->table;
9994
  Ndb *ndb= check_ndb_in_thd(thd);
9995 9996
  NdbDictionary::Dictionary* dict= ndb->getDictionary();
  NdbDictionary::Dictionary::List dflist;
9997
  NdbError ndberr;
9998 9999 10000
  unsigned i;

  DBUG_ENTER("ndbcluster_fill_files_table");
10001

10002 10003
  dict->listObjects(dflist, NdbDictionary::Object::Datafile);
  ndberr= dict->getNdbError();
10004 10005
  if (ndberr.classification != NdbError::NoError)
    ERR_RETURN(ndberr);
10006

10007
  for (i= 0; i < dflist.count; i++)
10008 10009 10010 10011 10012 10013 10014
  {
    NdbDictionary::Dictionary::List::Element& elt = dflist.elements[i];
    Ndb_cluster_connection_node_iter iter;
    unsigned id;

    g_ndb_cluster_connection->init_get_next_node(iter);

10015
    while ((id= g_ndb_cluster_connection->get_next_node(iter)))
10016 10017
    {
      NdbDictionary::Datafile df= dict->getDatafile(id, elt.name);
10018
      ndberr= dict->getNdbError();
10019 10020 10021 10022 10023 10024
      if(ndberr.classification != NdbError::NoError)
      {
        if (ndberr.classification == NdbError::SchemaError)
          continue;
        ERR_RETURN(ndberr);
      }
10025 10026
      NdbDictionary::Tablespace ts= dict->getTablespace(df.getTablespace());
      ndberr= dict->getNdbError();
10027 10028 10029 10030 10031 10032
      if (ndberr.classification != NdbError::NoError)
      {
        if (ndberr.classification == NdbError::SchemaError)
          continue;
        ERR_RETURN(ndberr);
      }
10033 10034

      int c= 0;
10035
      table->field[c++]->set_null(); // FILE_ID
10036 10037
      table->field[c++]->store(elt.name, strlen(elt.name),
                               system_charset_info);
10038
      table->field[c++]->store("DATAFILE",8,system_charset_info);
10039
      table->field[c++]->store(df.getTablespace(), strlen(df.getTablespace()),
10040 10041 10042 10043 10044 10045 10046 10047 10048 10049 10050 10051 10052 10053 10054 10055 10056 10057 10058 10059 10060 10061 10062 10063 10064 10065 10066 10067 10068 10069 10070 10071 10072
                               system_charset_info);
      table->field[c++]->set_null(); // TABLE_CATALOG
      table->field[c++]->set_null(); // TABLE_SCHEMA
      table->field[c++]->set_null(); // TABLE_NAME

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

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

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

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

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

10073
      table->field[c++]->store("FIXED", 5, system_charset_info); // ROW_FORMAT
10074 10075 10076 10077 10078 10079 10080 10081 10082 10083 10084 10085

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

10086
      table->field[c++]->store("NORMAL", 6, system_charset_info);
10087 10088

      char extra[30];
10089 10090
      int len= my_snprintf(extra, sizeof(extra), "CLUSTER_NODE=%u", id);
      table->field[c]->store(extra, len, system_charset_info);
10091 10092 10093 10094
      schema_table_store_record(thd, table);
    }
  }

unknown's avatar
ndb -  
unknown committed
10095 10096
  NdbDictionary::Dictionary::List uflist;
  dict->listObjects(uflist, NdbDictionary::Object::Undofile);
10097
  ndberr= dict->getNdbError();
10098 10099
  if (ndberr.classification != NdbError::NoError)
    ERR_RETURN(ndberr);
10100

unknown's avatar
ndb -  
unknown committed
10101
  for (i= 0; i < uflist.count; i++)
10102
  {
unknown's avatar
ndb -  
unknown committed
10103
    NdbDictionary::Dictionary::List::Element& elt= uflist.elements[i];
10104 10105 10106 10107 10108
    Ndb_cluster_connection_node_iter iter;
    unsigned id;

    g_ndb_cluster_connection->init_get_next_node(iter);

10109
    while ((id= g_ndb_cluster_connection->get_next_node(iter)))
10110 10111
    {
      NdbDictionary::Undofile uf= dict->getUndofile(id, elt.name);
10112
      ndberr= dict->getNdbError();
10113 10114 10115 10116 10117 10118
      if (ndberr.classification != NdbError::NoError)
      {
        if (ndberr.classification == NdbError::SchemaError)
          continue;
        ERR_RETURN(ndberr);
      }
10119 10120 10121
      NdbDictionary::LogfileGroup lfg=
        dict->getLogfileGroup(uf.getLogfileGroup());
      ndberr= dict->getNdbError();
10122 10123 10124 10125 10126 10127
      if (ndberr.classification != NdbError::NoError)
      {
        if (ndberr.classification == NdbError::SchemaError)
          continue;
        ERR_RETURN(ndberr);
      }
10128 10129

      int c= 0;
10130
      table->field[c++]->set_null(); // FILE_ID
10131 10132 10133
      table->field[c++]->store(elt.name, strlen(elt.name),
                               system_charset_info);
      table->field[c++]->store("UNDO LOG", 8, system_charset_info);
10134 10135 10136 10137 10138 10139 10140 10141 10142 10143 10144 10145 10146 10147 10148 10149 10150 10151 10152 10153 10154 10155 10156 10157 10158 10159 10160 10161 10162 10163 10164 10165 10166 10167 10168 10169 10170 10171 10172 10173 10174 10175 10176 10177 10178 10179
      table->field[c++]->set_null(); // TABLESPACE NAME
      table->field[c++]->set_null(); // TABLE_CATALOG
      table->field[c++]->set_null(); // TABLE_SCHEMA
      table->field[c++]->set_null(); // TABLE_NAME

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

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

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

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

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

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

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

10180
      table->field[c++]->store("NORMAL", 6, system_charset_info);
10181 10182

      char extra[30];
unknown's avatar
unknown committed
10183
      int len= my_snprintf(extra,sizeof(extra),"CLUSTER_NODE=%u",id);
10184
      table->field[c]->store(extra, len, system_charset_info);
10185 10186 10187
      schema_table_store_record(thd, table);
    }
  }
10188
  DBUG_RETURN(0);
10189
}
unknown's avatar
unknown committed
10190 10191 10192 10193 10194 10195


mysql_declare_plugin(ndbcluster)
{
  MYSQL_STORAGE_ENGINE_PLUGIN,
  &ndbcluster_hton,
10196
  ndbcluster_hton_name,
unknown's avatar
unknown committed
10197
  "MySQL AB",
10198
  ndbcluster_hton_comment,
unknown's avatar
unknown committed
10199 10200 10201
  NULL, /* Plugin Init */
  NULL, /* Plugin Deinit */
  0x0100 /* 1.0 */,
unknown's avatar
unknown committed
10202
  0
unknown's avatar
unknown committed
10203 10204 10205 10206
}
mysql_declare_plugin_end;

#endif